Compare commits

10 Commits

193 changed files with 86876 additions and 680 deletions

View File

@@ -1,9 +1,21 @@
#include "IdGenerator.hpp" #include "IdGenerator.hpp"
std::mt19937 rnd = std::mt19937(); bool init = false;
std::mt19937 rnd;
auto gen = uuids::uuid_random_generator(rnd); auto gen = uuids::uuid_random_generator(rnd);
uuids::uuid TSE::GenerateRandomUUID() uuids::uuid TSE::GenerateRandomUUID()
{ {
if(!init)
{
InitRandomIDs();
init = true;
}
return gen(); return gen();
} }
void TSE::InitRandomIDs()
{
rnd = std::mt19937();
gen = uuids::uuid_random_generator(rnd);
}

View File

@@ -5,4 +5,5 @@
namespace TSE namespace TSE
{ {
uuids::uuid GenerateRandomUUID(); uuids::uuid GenerateRandomUUID();
void InitRandomIDs();
} // namespace TSE } // namespace TSE

View File

@@ -1,6 +1,7 @@
#include "Camera.hpp" #include "Camera.hpp"
#include "elements/Transformable.hpp" #include "elements/Transformable.hpp"
#include "interfaces/IRenderer.hpp" #include "interfaces/IRenderer.hpp"
#include "uuid.h"
TSE::Camera* TSE::Camera::mainCamera = nullptr; TSE::Camera* TSE::Camera::mainCamera = nullptr;
TSE::ICameraHelper* TSE::Camera::helper = nullptr; TSE::ICameraHelper* TSE::Camera::helper = nullptr;
@@ -30,6 +31,11 @@ float TSE::Camera::GetFov() const
return fov; return fov;
} }
const TSE::Vector2 &TSE::Camera::GetRenderTargetSize() const
{
return lastRtSize;
}
TSE::Vector3 TSE::Camera::SceenPositionToGamePosition(Vector2 screenPos) TSE::Vector3 TSE::Camera::SceenPositionToGamePosition(Vector2 screenPos)
{ {
float x = 2.0f * screenPos.x / lastRtSize.x -1.0f; float x = 2.0f * screenPos.x / lastRtSize.x -1.0f;
@@ -95,8 +101,12 @@ void TSE::Camera::SetRenderTarget(IRenderTarget *target)
if(target != nullptr) if(target != nullptr)
target->AddResizeNotifiable(this); target->AddResizeNotifiable(this);
rt = target; rt = target;
if(lastRtSize != rt->GetRawIResizableSize())
{
lastRtSize = rt->GetRawIResizableSize();
RecalculateProjMatrix(); RecalculateProjMatrix();
} }
}
TSE::IRenderTarget *TSE::Camera::GetRenderTarget() TSE::IRenderTarget *TSE::Camera::GetRenderTarget()
{ {
@@ -176,14 +186,36 @@ TSE::Matrix4x4 BuildView_Zplus_RH(const TSE::Matrix4x4& world)
void TSE::Camera::PreDraw(IShader *shader) void TSE::Camera::PreDraw(IShader *shader)
{ {
rt->Bind(); rt->Bind();
shader->SetUniform("prMatrix", projectionMatrix); // shader->SetUniform("prMatrix", projectionMatrix);
auto worlmatrix = baseObject->GetGlobalMatrix(); // auto worlmatrix = baseObject->GetGlobalMatrix();
viewMatrix = BuildView_Zplus_RH(worlmatrix); // viewMatrix = BuildView_Zplus_RH(worlmatrix);
shader->SetUniform("camMatrix", &viewMatrix); // shader->SetUniform("camMatrix", &viewMatrix);
helper->OnRenderTargetChanged(lastRtSize.x, lastRtSize.y); // helper->OnRenderTargetChanged(lastRtSize.x, lastRtSize.y);
Vector3 pos = baseObject->GetGlobalPosition();
Vector3 right = baseObject->LocalToGlobalPosition(Vector3::right) - pos;
Vector3 up = baseObject->LocalToGlobalPosition(Vector3::up) - pos;
Vector3 forward = baseObject->LocalToGlobalPosition(Vector3::forward) - pos;
forward.Normalize();
shader->SetUniform("CamPos", &pos);
shader->SetUniform("CamRight", &right);
shader->SetUniform("CamUp", &up);
shader->SetUniform("CamForward", &forward);
float x = lastRtSize.x / RenderScale;
float y = lastRtSize.y / RenderScale;
float mx = -x;
float my = -y;
shader->SetUniform("OrthoLeft", mx);
shader->SetUniform("OrthoRight", x);
shader->SetUniform("OrthoBottom", my);
shader->SetUniform("OrthoTop", y);
shader->SetUniform("NearPlane", nearClippingPlane);
shader->SetUniform("FarPlane", farClippingPlane);
} }
void TSE::Camera::PostDraw() void TSE::Camera::PostDraw()

View File

@@ -8,6 +8,7 @@
#include "Vector3.hpp" #include "Vector3.hpp"
#include "interfaces/IRenderTarget.hpp" #include "interfaces/IRenderTarget.hpp"
#include "elements/BehaviourScript.hpp" #include "elements/BehaviourScript.hpp"
#include "uuid.h"
namespace TSE namespace TSE
{ {
@@ -41,6 +42,7 @@ namespace TSE
Vector2 lastRtSize = {0, 0}; Vector2 lastRtSize = {0, 0};
public: public:
std::vector<uuids::uuid> layersNotToRender;
static ICameraHelper* helper; static ICameraHelper* helper;
static Camera* mainCamera; static Camera* mainCamera;
@@ -50,6 +52,7 @@ namespace TSE
float GetNearClippingPlane() const; float GetNearClippingPlane() const;
float GetFarClippingPlane() const; float GetFarClippingPlane() const;
float GetFov() const; float GetFov() const;
const Vector2& GetRenderTargetSize() const;
// Setter // Setter
Vector3 SceenPositionToGamePosition(Vector2 screenPos); Vector3 SceenPositionToGamePosition(Vector2 screenPos);

View File

@@ -0,0 +1,252 @@
#include "OrdererSpriteSet.hpp"
#include <algorithm>
#include <tuple>
#include "Debug.hpp"
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;
// Vector2 p2 = Vector2((int)p.x % chunkSize, (int)p.y % chunkSize);
// Vector2 chunkIndex = p - p2;
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)
{
if (a.y != b.y)
return a.y > b.y;
return a.x < 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)
{
if (a.y != b.y)
return a.y < b.y;
return a.x < 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;
string poses = "[";
for(auto pos : orderedChunks)
{
poses += pos.ToString() + ",";
}
poses.erase(poses.end() - 1);
poses += "]";
TSE_LOG("orderedPositions: " + poses);
}
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

@@ -7,9 +7,14 @@ TSE::TileMapChunk::TileMapChunk(int _chunksize, const Vector2 &_pos, SortingOrde
order = _order; order = _order;
} }
void TSE::TileMapChunk::SetTile(const Vector2& p, const Vector2& Spriteindex, TileSet* set) void TSE::TileMapChunk::SetTile(const Vector2& p, const Vector2& Spriteindex, const Vector2& Normalindex, TileSet* set)
{ {
sprites[p] = set->GetSpriteIdAt(Spriteindex.x, Spriteindex.y); int normalid = -1;
if(Normalindex != Vector2(-1,-1))
normalid = set->GetSpriteIdAt(Normalindex.x, Normalindex.y);
sprites[p] = {set->GetSpriteIdAt(Spriteindex.x, Spriteindex.y), normalid};
dirtyPositions = true;
dirtySpriteIds = true;
} }
void TSE::TileMapChunk::RemoveTile(Vector2 p) void TSE::TileMapChunk::RemoveTile(Vector2 p)
@@ -20,10 +25,15 @@ void TSE::TileMapChunk::RemoveTile(Vector2 p)
void TSE::TileMapChunk::SetOrdering(SortingOrder _order) void TSE::TileMapChunk::SetOrdering(SortingOrder _order)
{ {
order = _order; order = _order;
dirtyPositions = true;
dirtySpriteIds = true;
} }
void TSE::TileMapChunk::GetOrderedPositions(Vector2 *array) const std::vector<TSE::Vector2>* TSE::TileMapChunk::GetOrderedPositions()
{ {
if(dirtyPositions)
{
orderedPositions.clear();
switch (order) switch (order)
{ {
case TopLeft: case TopLeft:
@@ -35,7 +45,7 @@ void TSE::TileMapChunk::GetOrderedPositions(Vector2 *array)
Vector2 p(x,y); Vector2 p(x,y);
auto v = sprites.find(p); auto v = sprites.find(p);
if(v != sprites.end()) if(v != sprites.end())
*array++ = 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; break;
@@ -48,7 +58,7 @@ void TSE::TileMapChunk::GetOrderedPositions(Vector2 *array)
Vector2 p(x,y); Vector2 p(x,y);
auto v = sprites.find(p); auto v = sprites.find(p);
if(v != sprites.end()) if(v != sprites.end())
*array++ = 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; break;
@@ -61,7 +71,7 @@ void TSE::TileMapChunk::GetOrderedPositions(Vector2 *array)
Vector2 p(x,y); Vector2 p(x,y);
auto v = sprites.find(p); auto v = sprites.find(p);
if(v != sprites.end()) if(v != sprites.end())
*array++ = 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; break;
@@ -74,15 +84,21 @@ void TSE::TileMapChunk::GetOrderedPositions(Vector2 *array)
Vector2 p(x,y); Vector2 p(x,y);
auto v = sprites.find(p); auto v = sprites.find(p);
if(v != sprites.end()) if(v != sprites.end())
*array++ = 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; break;
} }
dirtyPositions = false;
}
return &orderedPositions;
} }
void TSE::TileMapChunk::GetOrderedSpriteIds(int *array) const std::vector<TSE::Vector2i>* TSE::TileMapChunk::GetOrderedSpriteIds()
{ {
if(dirtySpriteIds)
{
orderedSpriteIDs.clear();
switch (order) switch (order)
{ {
case TopLeft: case TopLeft:
@@ -93,7 +109,7 @@ void TSE::TileMapChunk::GetOrderedSpriteIds(int *array)
Vector2 p(x,y); Vector2 p(x,y);
auto v = sprites.find(p); auto v = sprites.find(p);
if(v != sprites.end()) if(v != sprites.end())
*array++ = v->second; orderedSpriteIDs.push_back(v->second);
} }
} }
break; break;
@@ -105,7 +121,7 @@ void TSE::TileMapChunk::GetOrderedSpriteIds(int *array)
Vector2 p(x,y); Vector2 p(x,y);
auto v = sprites.find(p); auto v = sprites.find(p);
if(v != sprites.end()) if(v != sprites.end())
*array++ = v->second; orderedSpriteIDs.push_back(v->second);
} }
} }
break; break;
@@ -117,7 +133,7 @@ void TSE::TileMapChunk::GetOrderedSpriteIds(int *array)
Vector2 p(x,y); Vector2 p(x,y);
auto v = sprites.find(p); auto v = sprites.find(p);
if(v != sprites.end()) if(v != sprites.end())
*array++ = v->second; orderedSpriteIDs.push_back(v->second);
} }
} }
break; break;
@@ -129,11 +145,14 @@ void TSE::TileMapChunk::GetOrderedSpriteIds(int *array)
Vector2 p(x,y); Vector2 p(x,y);
auto v = sprites.find(p); auto v = sprites.find(p);
if(v != sprites.end()) if(v != sprites.end())
*array++ = v->second; orderedSpriteIDs.push_back(v->second);
} }
} }
break; break;
} }
dirtySpriteIds = false;
}
return &orderedSpriteIDs;
} }
int TSE::TileMapChunk::GetChunksize() int TSE::TileMapChunk::GetChunksize()
@@ -154,17 +173,18 @@ void TSE::TileMap::RemoveTile(Vector2 p)
chunks[chunkIndex].RemoveTile(chunkInnerPos); chunks[chunkIndex].RemoveTile(chunkInnerPos);
} }
void TSE::TileMap::SetTile(Vector2 p, Vector2 Spriteindex) void TSE::TileMap::SetTile(Vector2 p, Vector2 Spriteindex, Vector2 Normalindex)
{ {
Vector2 chunkInnerPos = LocalToChunkPos(p); Vector2 chunkInnerPos = LocalToChunkPos(p);
Vector2 chunkIndex = p - chunkInnerPos; Vector2 chunkIndex = p - chunkInnerPos;
if(!chunks.contains(chunkIndex)) if(!chunks.contains(chunkIndex))
{ {
dirty = true;
chunks[chunkIndex] = TileMapChunk(chunkSize, chunkIndex, order); chunks[chunkIndex] = TileMapChunk(chunkSize, chunkIndex, order);
chunks[chunkIndex].nextLine = nextLine; chunks[chunkIndex].nextLine = nextLine;
CheckBounds(chunkIndex); CheckBounds(chunkIndex);
} }
chunks[chunkIndex].SetTile(chunkInnerPos, Spriteindex, set); chunks[chunkIndex].SetTile(chunkInnerPos, Spriteindex, Normalindex, set);
} }
TSE::TileMapChunk* TSE::TileMap::GetChunk(const Vector2 &pos) TSE::TileMapChunk* TSE::TileMap::GetChunk(const Vector2 &pos)
@@ -175,9 +195,11 @@ TSE::TileMapChunk* TSE::TileMap::GetChunk(const Vector2 &pos)
return &chunks[pos]; return &chunks[pos];
} }
void TSE::TileMap::GetChunkPositionsInOrder(Vector2 *arr) const std::vector<TSE::Vector2>* TSE::TileMap::GetChunkPositionsInOrder()
{ {
if(dirty)
{
orderedChunks.clear();
switch (order) switch (order)
{ {
case TopLeft: case TopLeft:
@@ -188,7 +210,7 @@ void TSE::TileMap::GetChunkPositionsInOrder(Vector2 *arr)
Vector2 p(x,y); Vector2 p(x,y);
auto v = chunks.find(p); auto v = chunks.find(p);
if(v != chunks.end()) if(v != chunks.end())
*arr++ = v->first; orderedChunks.push_back(v->first);
} }
} }
break; break;
@@ -200,7 +222,7 @@ void TSE::TileMap::GetChunkPositionsInOrder(Vector2 *arr)
Vector2 p(x,y); Vector2 p(x,y);
auto v = chunks.find(p); auto v = chunks.find(p);
if(v != chunks.end()) if(v != chunks.end())
*arr++ = v->first; orderedChunks.push_back(v->first);
} }
} }
break; break;
@@ -212,7 +234,7 @@ void TSE::TileMap::GetChunkPositionsInOrder(Vector2 *arr)
Vector2 p(x,y); Vector2 p(x,y);
auto v = chunks.find(p); auto v = chunks.find(p);
if(v != chunks.end()) if(v != chunks.end())
*arr++ = v->first; orderedChunks.push_back(v->first);
} }
} }
break; break;
@@ -224,11 +246,14 @@ void TSE::TileMap::GetChunkPositionsInOrder(Vector2 *arr)
Vector2 p(x,y); Vector2 p(x,y);
auto v = chunks.find(p); auto v = chunks.find(p);
if(v != chunks.end()) if(v != chunks.end())
*arr++ = v->first; orderedChunks.push_back(v->first);
} }
} }
break; break;
} }
dirty = false;
}
return &orderedChunks;
} }
int TSE::TileMap::GetChunkCount() int TSE::TileMap::GetChunkCount()
@@ -248,6 +273,7 @@ void TSE::TileMap::SetNextLineOffset(const Vector2 &offset)
{ {
chunk.nextLine = offset; chunk.nextLine = offset;
} }
DirtyAll();
} }
TSE::Vector2 TSE::TileMap::GetNextLineOffset() TSE::Vector2 TSE::TileMap::GetNextLineOffset()
@@ -255,6 +281,16 @@ TSE::Vector2 TSE::TileMap::GetNextLineOffset()
return nextLine; 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) void TSE::TileMap::CheckBounds(Vector2 pos)
{ {
if(pos.x > bounds.p2.x) if(pos.x > bounds.p2.x)
@@ -282,10 +318,10 @@ TSE::Vector2 TSE::TileMap::ChunkToLocalPos(const Vector2 &v, const TileMapChunk
TSE::Vector2 TSE::TileMap::RealPosToTileMapPos(const Vector2 &v) 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) 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

@@ -5,35 +5,33 @@
#include <unordered_map> #include <unordered_map>
#include "elements/BehaviourScript.hpp" #include "elements/BehaviourScript.hpp"
#include "Vector2.hpp" #include "Vector2.hpp"
#include "Vector2i.hpp"
#include "elements/Sprite.hpp" #include "elements/Sprite.hpp"
#include "elements/TileSet.hpp" #include "elements/TileSet.hpp"
#include "enums/SortingOrder.hpp"
namespace TSE namespace TSE
{ {
enum SortingOrder
{
TopRight,
TopLeft,
BottomRight,
BottomLeft,
};
struct TileMapChunk struct TileMapChunk
{ {
private: private:
std::vector<Vector2> orderedPositions;
std::vector<Vector2i> orderedSpriteIDs;
SortingOrder order; SortingOrder order;
int chunksize; int chunksize;
std::unordered_map<Vector2, int> sprites; std::unordered_map<Vector2, Vector2i> sprites;
public: public:
bool dirtyPositions = true;
bool dirtySpriteIds = true;
Vector2 nextLine; Vector2 nextLine;
Vector2 pos; Vector2 pos;
TileMapChunk(int _chunksize = 16, const Vector2& _pos = {0,0}, SortingOrder _order = TopRight); TileMapChunk(int _chunksize = 16, const Vector2& _pos = {0,0}, SortingOrder _order = TopRight);
void SetTile(const Vector2& p, const Vector2& Spriteindex, TileSet* set); void SetTile(const Vector2& p, const Vector2& Spriteindex, const Vector2& Normalindex, TileSet* set);
void RemoveTile(Vector2 p); void RemoveTile(Vector2 p);
void SetOrdering(SortingOrder _order); void SetOrdering(SortingOrder _order);
void GetOrderedPositions(Vector2* array); const std::vector<Vector2>* GetOrderedPositions();
void GetOrderedSpriteIds(int* array); const std::vector<Vector2i>* GetOrderedSpriteIds();
int GetChunksize(); int GetChunksize();
int GetSpriteCount(); int GetSpriteCount();
@@ -42,19 +40,21 @@ namespace TSE
class TileMap : public BehaviourScript class TileMap : public BehaviourScript
{ {
private: private:
bool dirty = true;
std::vector<Vector2> orderedChunks;
Rect bounds = Rect(0,0,0,0); Rect bounds = Rect(0,0,0,0);
Vector2 nextLine = Vector2(-0.5f, 1.25f); Vector2 nextLine = Vector2(0.5f, 0.5f);
public: public:
int chunkSize = 16; int chunkSize = 16;
SortingOrder order = TopRight; SortingOrder order = BottomRight;
Vector2 SpriteScale = Vector2(1,1); Vector2 SpriteScale = Vector2(1,1);
TileSet* set; TileSet* set;
std::unordered_map<Vector2, TileMapChunk> chunks; std::unordered_map<Vector2, TileMapChunk> chunks;
void RemoveTile(Vector2 p); void RemoveTile(Vector2 p);
void SetTile(Vector2 p, Vector2 Spriteindex); void SetTile(Vector2 p, Vector2 Spriteindex, Vector2 Normalindex = {-1,-1});
TileMapChunk* GetChunk(const Vector2& pos); TileMapChunk* GetChunk(const Vector2& pos);
void GetChunkPositionsInOrder(Vector2* arr); const std::vector<Vector2>* GetChunkPositionsInOrder();
int GetChunkCount(); int GetChunkCount();
TileSet* GetTileSet(); TileSet* GetTileSet();
const Rect& GetBounds() const { return bounds; } const Rect& GetBounds() const { return bounds; }
@@ -62,6 +62,7 @@ namespace TSE
Vector2 GetNextLineOffset(); Vector2 GetNextLineOffset();
Vector2 RealPosToTileMapPos(const Vector2& v); Vector2 RealPosToTileMapPos(const Vector2& v);
Vector2 TileMapToRealPos(const Vector2& v); Vector2 TileMapToRealPos(const Vector2& v);
void DirtyAll();
inline const char* GetName() override inline const char* GetName() override
{ {

View File

@@ -1,6 +1,7 @@
#include "Layer.hpp" #include "Layer.hpp"
#include "BehaviourScripts/Renderable.hpp" #include "BehaviourScripts/Renderable.hpp"
#include "elements/BehaviourScript.hpp" #include "elements/BehaviourScript.hpp"
#include "IdGenerator.hpp"
void HandleObject(TSE::Transformable* trans, TSE::TransformationStack& stack, TSE::IRenderer& rnd) void HandleObject(TSE::Transformable* trans, TSE::TransformationStack& stack, TSE::IRenderer& rnd)
{ {
@@ -24,6 +25,7 @@ void HandleObject(TSE::Transformable* trans, TSE::TransformationStack& stack, TS
TSE::Layer::Layer(const string &n) TSE::Layer::Layer(const string &n)
{ {
ID = GenerateRandomUUID();
name = n; name = n;
} }
@@ -94,3 +96,13 @@ void TSE::Layer::Update()
trans->Update(); trans->Update();
} }
} }
void TSE::Layer::SetNonVisual(bool v)
{
nonVisual = v;
}
bool TSE::Layer::IsVisual()
{
return !nonVisual;
}

View File

@@ -4,14 +4,16 @@
#include "Types.hpp" #include "Types.hpp"
#include "interfaces/IRenderer.hpp" #include "interfaces/IRenderer.hpp"
#include <vector> #include <vector>
#include "interfaces/IIdentifyable.hpp"
namespace TSE namespace TSE
{ {
class Layer class Layer : public IIdentifyable
{ {
private: private:
string name; string name;
std::vector<Transformable*> objectsToRender; std::vector<Transformable*> objectsToRender;
bool nonVisual = false;
public: public:
Layer(const string& name); Layer(const string& name);
@@ -27,5 +29,7 @@ namespace TSE
void SetName(const string& name); void SetName(const string& name);
std::vector<Transformable*>& GetAllObjects(); std::vector<Transformable*>& GetAllObjects();
void Update(); void Update();
void SetNonVisual(bool v);
bool IsVisual();
}; };
} // namespace TSE } // namespace TSE

View File

@@ -60,6 +60,7 @@ namespace TSE
} }
template void Material::SetValue<int>(const string&, const int&); template void Material::SetValue<int>(const string&, const int&);
template void Material::SetValue<uint>(const string&, const uint&);
template void Material::SetValue<float>(const string&, const float&); template void Material::SetValue<float>(const string&, const float&);
template void Material::SetValue<Vector2>(const string&, const Vector2&); template void Material::SetValue<Vector2>(const string&, const Vector2&);
template void Material::SetValue<Vector3>(const string&, const Vector3&); template void Material::SetValue<Vector3>(const string&, const Vector3&);
@@ -70,6 +71,7 @@ namespace TSE
template void Material::SetValue<ITexture>(const string&, const ITexture*); template void Material::SetValue<ITexture>(const string&, const ITexture*);
template int Material::GetValue<int>(const string&) const; template int Material::GetValue<int>(const string&) const;
template uint Material::GetValue<uint>(const string&) const;
template float Material::GetValue<float>(const string&) const; template float Material::GetValue<float>(const string&) const;
template Vector2 Material::GetValue<Vector2>(const string&) const; template Vector2 Material::GetValue<Vector2>(const string&) const;
template Vector3 Material::GetValue<Vector3>(const string&) const; template Vector3 Material::GetValue<Vector3>(const string&) const;

View File

@@ -0,0 +1,40 @@
#include "RenderPipeline.hpp"
void TSE::RenderPipeline::AddRenderStep(const RenderStep &step)
{
steps.push_back(step);
}
void TSE::RenderPipeline::AddRenderStepAt(const RenderStep &step, int n)
{
steps.insert(steps.begin() + n, step);
}
void TSE::RenderPipeline::SetRenderStepAt(const RenderStep &step, int n)
{
steps[n] = step;
}
void TSE::RenderPipeline::RemoveRenderStepAt(int n)
{
auto i = steps.begin() += n;
steps.erase(i);
}
int TSE::RenderPipeline::GetRenderStepCount()
{
return steps.size();
}
const TSE::RenderStep &TSE::RenderPipeline::GetRenderStepAt(int n)
{
return steps[n];
}
TSE::RenderPipeline* TSE::RenderPipeline::CreateEmpty()
{
RenderPipeline* res = new RenderPipeline();
RenderStep step;
res->AddRenderStep(step);
return res;
}

View File

@@ -0,0 +1,23 @@
#pragma once
#include "RenderStep.hpp"
namespace TSE
{
class RenderPipeline
{
private:
std::vector<RenderStep> steps;
public:
void AddRenderStep(const RenderStep& step);
void AddRenderStepAt(const RenderStep& step, int n);
void SetRenderStepAt(const RenderStep& step, int n);
void RemoveRenderStepAt(int n);
int GetRenderStepCount();
const RenderStep& GetRenderStepAt(int n);
static RenderPipeline* CreateEmpty();
};
} // namespace TSE

View File

@@ -0,0 +1,18 @@
#pragma once
#include "Layer.hpp"
#include <vector>
#include "interfaces/IRenderTarget.hpp"
#include "Types.hpp"
namespace TSE
{
struct RenderStep
{
public:
std::vector<Layer*> layersToRender;
IRenderTarget* target;
bool EditorCamera = true;
};
} // namespace TSE

View File

@@ -1,10 +1,74 @@
#include "Scene.hpp" #include "Scene.hpp"
#include "BehaviourScripts/Camera.hpp"
#include <algorithm>
#include "Debug.hpp"
#include "RenderPipeline.hpp"
void TSE::Scene::Render(IRenderer &rnd, const IWindow &wnd) void TSE::Scene::Render(IRenderer &rnd, const IWindow &wnd)
{
RenderPipeline* pipeline = IRenderer::pipeline;
auto camerasBackup = std::vector<Camera*>(IRenderer::camerasToRenderWith);
if(pipeline != nullptr)
{
for(int i = 0; i < pipeline->GetRenderStepCount(); i++)
{
IRenderer::camerasToRenderWith = std::vector<Camera*>(camerasBackup);
const RenderStep& step = pipeline->GetRenderStepAt(i);
if(!step.EditorCamera)
{
for(int i = 0; i < IRenderer::camerasToRenderWith.size(); i++)
{
if(IRenderer::camerasToRenderWith[i]->baseObject->name == ".EditorCamera")
IRenderer::camerasToRenderWith.erase(IRenderer::camerasToRenderWith.begin() + i);
}
}
for(int i = 0; i < IRenderer::camerasToRenderWith.size(); i++)
{
if(IRenderer::camerasToRenderWith[i]->baseObject->name != ".EditorCamera")
IRenderer::camerasToRenderWith[i]->SetRenderTarget(step.target);
}
int counter = 1;
for(auto l : step.layersToRender)
{
if(!l->IsVisual()) continue;
l->Render(rnd);
if(counter++ != layers.size())
{
rnd.End(); //OPTIMIZE:
//takes up 13,97% of function, but only needed, if there is more then one layer
//possible optimizations:
// -remove layers
// -make layer calculations, in shader
// -make an offset, that is calculated on cpu, and then commit everything at once
// now it is better because it is only done once per frame if only one shader is used, or textures are full, or more then one layers are used
rnd.Flush();
rnd.Begin();
wnd.ClearDepthBuffer();
}
}
}
}
else
{ {
int counter = 1; int counter = 1;
for(auto l : layers) for(auto l : layers)
{ {
IRenderer::camerasToRenderWith.clear();
if(!l.second->IsVisual()) continue;
for(auto camera : camerasBackup)
{
auto it = std::find(camera->layersNotToRender.begin(), camera->layersNotToRender.end(), l.second->GetID());
if(it == camera->layersNotToRender.end())
{
IRenderer::camerasToRenderWith.push_back(camera);
}
}
l.second->Render(rnd); l.second->Render(rnd);
if(counter++ != layers.size()) if(counter++ != layers.size())
{ {
@@ -22,6 +86,7 @@ void TSE::Scene::Render(IRenderer &rnd, const IWindow &wnd)
} }
} }
} }
}
void TSE::Scene::DoneRender() void TSE::Scene::DoneRender()
{ {

View File

@@ -23,7 +23,6 @@ namespace TSE
public: public:
string name = "Unnamed"; string name = "Unnamed";
inline static ITextureHelper* helper = nullptr;
Texture(const string& path); Texture(const string& path);
Texture(const int& width, const int& height, int bpp = 32); Texture(const int& width, const int& height, int bpp = 32);

View File

@@ -0,0 +1,429 @@
#include "VolumeTexture3D.hpp"
#include "Debug.hpp"
#include <algorithm>
#include <cctype>
#include <filesystem>
#include <vector>
TSE::VolumeTexture3D::VolumeTexture3D(const string &path)
{
namespace fs = std::filesystem;
Size = Vector3(0, 0, 0);
Bpp = 0;
std::error_code ec;
const fs::path folderPath = fs::absolute(path, ec);
if(ec || !fs::exists(folderPath, ec))
{
TSE_WARNING("Can't create VolumeTexture3D because the given path is inaccessible: \n" + path);
return;
}
if(ec || !fs::is_directory(folderPath, ec))
{
TSE_WARNING("Can't create VolumeTexture3D because the given path is not a folder: \n" + folderPath.string());
return;
}
std::vector<fs::path> pngFiles;
for(fs::directory_iterator it(folderPath, ec); !ec && it != fs::directory_iterator(); it.increment(ec))
{
if(!it->is_regular_file(ec))
continue;
const fs::path &filePath = it->path();
auto extension = filePath.extension().string();
std::transform(extension.begin(), extension.end(), extension.begin(), [](unsigned char c) { return static_cast<char>(std::tolower(c)); });
if(extension == ".png")
pngFiles.push_back(filePath);
}
if(ec)
{
TSE_WARNING("Can't read VolumeTexture3D folder: \n" + folderPath.string());
return;
}
if(pngFiles.empty())
{
TSE_WARNING("Can't create VolumeTexture3D because the folder contains no png files: \n" + folderPath.string());
return;
}
std::sort(pngFiles.begin(), pngFiles.end());
std::vector<FIBITMAP*> loadedBitmaps;
loadedBitmaps.reserve(pngFiles.size());
uint width = 0;
uint height = 0;
for(const fs::path &filePath : pngFiles)
{
FREE_IMAGE_FORMAT fif = FreeImage_GetFileType(filePath.string().c_str(), 0);
if(fif == FREE_IMAGE_FORMAT::FIF_UNKNOWN)
fif = FreeImage_GetFIFFromFilename(filePath.string().c_str());
if(fif == FREE_IMAGE_FORMAT::FIF_UNKNOWN || !FreeImage_FIFSupportsReading(fif))
{
TSE_WARNING("Failed to load png for VolumeTexture3D: \n" + filePath.string());
for(FIBITMAP* loadedBmp : loadedBitmaps)
FreeImage_Unload(loadedBmp);
return;
}
FIBITMAP* currentBmp = FreeImage_Load(fif, filePath.string().c_str());
if(currentBmp == nullptr)
{
TSE_WARNING("Failed to load png for VolumeTexture3D: \n" + filePath.string());
for(FIBITMAP* loadedBmp : loadedBitmaps)
FreeImage_Unload(loadedBmp);
return;
}
if(FreeImage_GetBPP(currentBmp) != 32)
{
FIBITMAP* convertedBmp = FreeImage_ConvertTo32Bits(currentBmp);
FreeImage_Unload(currentBmp);
currentBmp = convertedBmp;
}
if(currentBmp == nullptr)
{
TSE_WARNING("Failed to convert png for VolumeTexture3D: \n" + filePath.string());
for(FIBITMAP* loadedBmp : loadedBitmaps)
FreeImage_Unload(loadedBmp);
return;
}
const uint currentWidth = FreeImage_GetWidth(currentBmp);
const uint currentHeight = FreeImage_GetHeight(currentBmp);
if(loadedBitmaps.empty())
{
width = currentWidth;
height = currentHeight;
}
else if(currentWidth != width || currentHeight != height)
{
TSE_WARNING("Can't create VolumeTexture3D because all pngs must have the same dimensions: \n" + filePath.string());
FreeImage_Unload(currentBmp);
for(FIBITMAP* loadedBmp : loadedBitmaps)
FreeImage_Unload(loadedBmp);
return;
}
loadedBitmaps.push_back(currentBmp);
}
Bpp = 32;
chanels = 4;
Size = Vector3(width, height, static_cast<float>(loadedBitmaps.size()));
bmp = new FIBITMAP*[loadedBitmaps.size()];
imagePtr = new byte*[loadedBitmaps.size()];
for(size_t i = 0; i < loadedBitmaps.size(); ++i)
{
bmp[i] = loadedBitmaps[i];
imagePtr[i] = FreeImage_GetBits(bmp[i]);
}
regist();
}
TSE::VolumeTexture3D::VolumeTexture3D(const int &width, const int &height, const int &depth, int bpp)
{
switch (bpp)
{
case 32:
chanels = 4;
break;
case 24:
chanels = 3;
case 16:
chanels = 1;
case 8:
chanels = 4;
}
Bpp = bpp;
Size = Vector3(width, height, depth);
bmp = new FIBITMAP*[depth];
imagePtr = new byte*[depth];
for(int i = 0; i < Depth(); i++)
{
bmp[i] = FreeImage_Allocate(width, height, bpp);
imagePtr[i] = FreeImage_GetBits(bmp[i]);
}
regist();
}
TSE::VolumeTexture3D::VolumeTexture3D(const Vector3 &size, int bpp)
: VolumeTexture3D(size.x, size.y, size.z, bpp) { }
TSE::VolumeTexture3D::~VolumeTexture3D()
{
if(bmp != nullptr)
{
for(int i = 0; i < Depth(); i++)
{
if(bmp[i] != nullptr)
{
FreeImage_Unload(bmp[i]);
bmp[i] = nullptr;
}
}
delete [] bmp;
delete [] imagePtr;
}
if(TextureID != 0)
{
PlatformDestroy();
TextureID = 0;
}
}
TSE::uint TSE::VolumeTexture3D::bpp() const
{
return Bpp;
}
TSE::Vector2 TSE::VolumeTexture3D::size() const
{
return Vector2(Size.x, Size.y);
}
float TSE::VolumeTexture3D::Width() const
{
return Size.x;
}
float TSE::VolumeTexture3D::Height() const
{
return Size.y;
}
float TSE::VolumeTexture3D::Depth() const
{
return Size.z;
}
TSE::byte TSE::VolumeTexture3D::Chanels() const
{
return chanels;
}
TSE::byte *TSE::VolumeTexture3D::GetImagePtr(const int depth) const
{
return imagePtr[depth];
}
void TSE::VolumeTexture3D::SetPixel(const Vector3 &pos, const Color &c)
{
SetPixel(pos.x, pos.y, pos.z, c);
}
void TSE::VolumeTexture3D::GetPixel(const Vector3 &pos, Color &c) const
{
GetPixel(pos.x, pos.y, pos.z, c);
}
void TSE::VolumeTexture3D::SetPixelNoApply(const Vector3 &pos, const Color &c)
{
SetPixelNoApply(pos.x, pos.y, pos.z, c);
}
void TSE::VolumeTexture3D::SetChanels(const byte &ch)
{
chanels = ch;
}
TSE::uint TSE::VolumeTexture3D::GetTextureId() const
{
return TextureID;
}
void TSE::VolumeTexture3D::SetTextureId(uint id)
{
TextureID = id;
}
void TSE::VolumeTexture3D::SetPixel(const int &x, const int &y, const int &z, const Color &c)
{
SetPixelNoApply(x,y,z,c);
Apply();
}
void TSE::VolumeTexture3D::GetPixel(const int &x, const int &y, const int &z, Color &c) const
{
if(x >= Width() || x < 0 || y >= Height() || y < 0|| z >= Depth() || z < 0)
{
TSE_WARNING("trying to access pixel outside of texture.\n pixel: (" + std::to_string(x) + ";" + std::to_string(y) + ";" + std::to_string(z) + ")\nTexture size: (" + std::to_string(Width()) + ";" + std::to_string(Height()) + ";" + std::to_string(Depth()) );
return;
}
byte* pixel = getPixelPointer(x,y,z);
byte b = *pixel++;
byte g = *pixel++;
byte r = *pixel++;
byte a = *pixel++;
if(bpp() == 8)
c = Color(r,r,r);
else if(bpp() == 24)
c = Color(r,g,b,a);
else if(bpp() == 32)
c = Color(r,g,b);
}
void TSE::VolumeTexture3D::Fill(const Color &c)
{
for (int x = 0; x < Width(); x++)
{
for (int y = 0; y < Height(); y++)
{
for (int z = 0; z < Depth(); z++)
{
SetPixelNoApply(x,y,z,c);
}
}
}
Apply();
}
void TSE::VolumeTexture3D::SetPixelNoApply(const int &x, const int &y, const int &z, const Color &c)
{
if(x >= Width() || x < 0 || y >= Height() || y < 0 || z >= Depth() || z < 0)
{
TSE_WARNING("trying to access pixel outside of texture.\n pixel: (" + std::to_string(x) + ";" + std::to_string(y) + ";" + std::to_string(z) + ")\nTexture size: (" + std::to_string(Width()) + ";" + std::to_string(Height()) + ";" + std::to_string(Depth()) );
return;
}
byte* pixel = getPixelPointer(x,y,z);
if(chanels == 4 && bpp() == 8)
{
byte r2bit = static_cast<byte>(c.r * 3.0f);
byte g2bit = static_cast<byte>(c.g * 3.0f);
byte b2bit = static_cast<byte>(c.b * 3.0f);
byte a2bit = static_cast<byte>(c.a * 3.0f);
byte result = (r2bit << 6) | (g2bit << 4) | (b2bit << 2) | a2bit;
*pixel++ = result;
return;
}
*pixel++ = c.B();
if(bpp() > 8)
{
*pixel++ = c.G();
*pixel++ = c.R();
if(bpp() > 24)
*pixel++ = c.A();
}
}
void TSE::VolumeTexture3D::AddPixelNoApply(const int &x, const int &y, const int &z, const Color &c)
{
if(x >= Width() || x < 0 ||y >= Height() || y < 0 || z >= Depth() || z < 0)
{
TSE_WARNING("trying to access pixel outside of texture.\n pixel: (" + std::to_string(x) + ";" + std::to_string(y) + ";" + std::to_string(z) + ")\nTexture size: (" + std::to_string(Width()) + ";" + std::to_string(Height()) + ";" + std::to_string(Depth()) );
return;
}
byte* pixel = getPixelPointer(x,y,z);
if(chanels == 4 && bpp() == 8) //TODO add propper adding, becouse currently is only setting
{
byte r2bit = static_cast<byte>(c.r * 3.0f);
byte g2bit = static_cast<byte>(c.g * 3.0f);
byte b2bit = static_cast<byte>(c.b * 3.0f);
byte a2bit = static_cast<byte>(c.a * 3.0f);
byte result = (r2bit << 6) | (g2bit << 4) | (b2bit << 2) | a2bit;
*pixel++ = result;
return;
}
Color bc(pixel[2], pixel[1], pixel[0], pixel[3]);
bc = c + bc;
*pixel++ = bc.B();
if(bpp() > 8)
{
*pixel++ = bc.G();
*pixel++ = bc.R();
if(bpp() > 24)
*pixel++ = bc.A();
}
}
void TSE::VolumeTexture3D::Recreate(const int &x, const int &y, const int &z, const int &bpp, const int &chanels)
{
if(bmp != nullptr)
{
for(int i = 0; i < Depth(); i++)
{
if(bmp[i] != nullptr)
{
FreeImage_Unload(bmp[i]);
bmp[i] = nullptr;
}
}
delete [] bmp;
delete [] imagePtr;
}
if(TextureID != 0)
{
PlatformDestroy();
TextureID = 0;
}
this->chanels = chanels;
Bpp = bpp;
Size = Vector3(x, y, z);
bmp = new FIBITMAP*[z];
imagePtr = new byte*[z];
for(int i = 0; i < Depth(); i++)
{
bmp[i] = FreeImage_Allocate(x, y, bpp);
imagePtr[i] = FreeImage_GetBits(bmp[i]);
}
regist();
}
void TSE::VolumeTexture3D::SavePNG(const std::string &path) const
{
//TODO: implement save all images as PNG in the given path, if the given path is inaccaseable, or not a folder, ust TSE_WARNING with an apropriate message
}
TSE::byte *TSE::VolumeTexture3D::getPixelPointer(const int &x, const int &y, const int &z) const
{
int alphaoffset = y * 2;
if(bpp() > 24)
alphaoffset = 0;
int offset = ((y * Width() + x) * (bpp() / 8) + alphaoffset);
return imagePtr[z] + offset;
}
void TSE::VolumeTexture3D::bind() const
{
helper->Bind3D(this);
}
void TSE::VolumeTexture3D::unbind() const
{
helper->UnBind3D(this);
}
void TSE::VolumeTexture3D::Apply()
{
helper->Apply3D(this);
}
void TSE::VolumeTexture3D::regist()
{
helper->Regist3D(this);
}
void TSE::VolumeTexture3D::PlatformDestroy()
{
helper->PlatromDestroy3D(this);
}

View File

@@ -0,0 +1,61 @@
#pragma once
#include "interfaces/ITexture.hpp"
#include "Types.hpp"
#include "Vector3.hpp"
#include "Color.hpp"
#include "interfaces/ITextureHelper.hpp"
#define FREEIMAGE_LIB
#include "FI/FreeImage.h"
namespace TSE
{
class VolumeTexture3D : public ITexture
{
protected:
uint TextureID = 0;
Vector3 Size;
uint Bpp;
byte chanels = 0;
FIBITMAP** bmp = nullptr;
byte** imagePtr = nullptr;
public:
string name = "Unnamed";
VolumeTexture3D(const string& path);
VolumeTexture3D(const int& width, const int& height, const int& depth, int bpp = 32);
VolumeTexture3D(const Vector3& size, int bpp = 32);
~VolumeTexture3D();
uint bpp() const;
Vector2 size() const override;
float Width() const override;
float Height() const override;
float Depth() const;
byte Chanels() const;
byte* GetImagePtr(const int depth) const;
void SetPixel(const Vector3& pos, const Color& c);
void GetPixel(const Vector3& pos, Color& c) const;
void SetPixelNoApply(const Vector3& pos, const Color& c);
void SetChanels(const byte& ch);
uint GetTextureId() const override;
void SetTextureId(uint id);
void SetPixel(const int& x, const int& y, const int& z, const Color& c);
void GetPixel(const int& x, const int& y, const int& z, Color& c) const;
void Fill(const Color& c);
void SetPixelNoApply(const int& x, const int& y, const int& z, const Color& c);
void AddPixelNoApply(const int& x, const int& y, const int& z, const Color& c);
void Recreate(const int& x, const int& y, const int& z, const int& bpp, const int& chanels);
void SavePNG(const std::string& path) const;
byte* getPixelPointer(const int& x, const int& y, const int& z) const;
void bind() const;
void unbind() const;
void Apply();
void regist();
void PlatformDestroy();
};
} // namespace TSE

View File

@@ -19,6 +19,7 @@ namespace TSE
enum Modifier enum Modifier
{ {
None = 0x0000,
ShiftMod = 0x0001, ShiftMod = 0x0001,
ControlMod = 0x0002, ControlMod = 0x0002,
AltMod = 0x0004, AltMod = 0x0004,

View File

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

View File

@@ -0,0 +1,13 @@
#pragma once
#include "uuid.h"
namespace TSE
{
class IIdentifyable
{
protected:
uuids::uuid ID;
public:
inline const uuids::uuid& GetID() { return ID;};
};
} // namespace TSE

View File

@@ -0,0 +1,35 @@
#pragma once
#include "IObserver.hpp"
#include <vector>
#include <algorithm>
namespace TSE
{
class IObservable
{
private:
std::vector<IObserver*> objectsToNotify;
public:
inline void Observe(IObserver* observer)
{
objectsToNotify.push_back(observer);
};
inline void StopObserve(IObserver* observer)
{
auto it = std::find(objectsToNotify.begin(), objectsToNotify.end(), observer);
if(it != objectsToNotify.end())
objectsToNotify.erase(it);
};
protected:
inline void TriggerObserver(void* data)
{
for (auto &&observer : objectsToNotify)
{
observer->OnObserved(data);
}
};
};
} // namespace TSE

View File

@@ -0,0 +1,10 @@
#pragma once
namespace TSE
{
class IObserver
{
public:
virtual void OnObserved(void* data) = 0;
};
} // namespace TSE

View File

@@ -7,11 +7,13 @@
namespace TSE namespace TSE
{ {
class Camera; class Camera;
class RenderPipeline;
class IRenderer class IRenderer
{ {
public: public:
inline static TSE::RenderPipeline* pipeline = nullptr;
static std::vector<Camera*> camerasToRenderWith; static std::vector<Camera*> camerasToRenderWith;
virtual void End() = 0; virtual void End() = 0;

View File

@@ -2,6 +2,7 @@
#include <list> #include <list>
#include "IResizeNotifiable.hpp" #include "IResizeNotifiable.hpp"
#include "Vector2.hpp"
namespace TSE namespace TSE
{ {
@@ -15,5 +16,9 @@ namespace TSE
public: public:
void AddResizeNotifiable(IResizeNotifiable* obj); void AddResizeNotifiable(IResizeNotifiable* obj);
void RemoveResizeNotifiable(IResizeNotifiable* obj); void RemoveResizeNotifiable(IResizeNotifiable* obj);
inline Vector2 GetRawIResizableSize()
{
return Vector2(width, height);
};
}; };
} // namespace TSE } // namespace TSE

View File

@@ -1,12 +1,15 @@
#pragma once #pragma once
#include "Vector2.hpp" #include "Vector2.hpp"
#include "interfaces/ITextureHelper.hpp"
namespace TSE namespace TSE
{ {
class ITexture class ITexture
{ {
public: public:
inline static ITextureHelper* helper = nullptr;
virtual ~ITexture() = default; virtual ~ITexture() = default;
virtual Vector2 size() const = 0; virtual Vector2 size() const = 0;
virtual float Width() const = 0; virtual float Width() const = 0;

View File

@@ -3,6 +3,7 @@
namespace TSE namespace TSE
{ {
class Texture; class Texture;
class VolumeTexture3D;
class ITextureHelper class ITextureHelper
{ {
@@ -12,5 +13,11 @@ namespace TSE
virtual void Apply(Texture* tex) = 0; virtual void Apply(Texture* tex) = 0;
virtual void Regist(Texture* tex) = 0; virtual void Regist(Texture* tex) = 0;
virtual void PlatromDestroy(Texture* tex) = 0; virtual void PlatromDestroy(Texture* tex) = 0;
virtual void Bind3D(const VolumeTexture3D* tex) = 0;
virtual void UnBind3D(const VolumeTexture3D* tex) = 0;
virtual void Apply3D(VolumeTexture3D* tex) = 0;
virtual void Regist3D(VolumeTexture3D* tex) = 0;
virtual void PlatromDestroy3D(VolumeTexture3D* tex) = 0;
}; };
} // namespace TSE } // namespace TSE

View File

@@ -5,6 +5,7 @@
#include "version.h" #include "version.h"
#include "IInputManager.hpp" #include "IInputManager.hpp"
#include "elements/AudioEngine.hpp" #include "elements/AudioEngine.hpp"
#include "IdGenerator.hpp"
#define FREEIMAGE_LIB #define FREEIMAGE_LIB
#include "FI/FreeImage.h" #include "FI/FreeImage.h"

View File

@@ -18,6 +18,7 @@ namespace TSE
virtual void Clear() const = 0; virtual void Clear() const = 0;
virtual void ClearDepthBuffer() const = 0; virtual void ClearDepthBuffer() const = 0;
virtual bool ShouldClose() const = 0; virtual bool ShouldClose() const = 0;
virtual void DoneSetup() = 0;
bool BaseInit() const; bool BaseInit() const;
void BaseUpdate() const; void BaseUpdate() const;

View File

@@ -3,11 +3,14 @@
#include "BehaviourScriptRegistry.hpp" #include "BehaviourScriptRegistry.hpp"
#include "BehaviourScripts/AudioListener.hpp" #include "BehaviourScripts/AudioListener.hpp"
#include "BehaviourScripts/AudioSource.hpp" #include "BehaviourScripts/AudioSource.hpp"
#include "BehaviourScripts/TileMap.hpp"
#include "BehaviourScripts/OrdererSpriteSet.hpp"
#include "BehaviourScripts/PhysicsObject.hpp"
#include "BehaviourScripts/basicEditorCamera.hpp" #include "BehaviourScripts/basicEditorCamera.hpp"
TSE::EDITOR::EditorSubsystem::EditorSubsystem() : sv(nullptr), editorLayer("") TSE::EDITOR::EditorSubsystem::EditorSubsystem() : sv(nullptr), editorLayer("")
{ {
rt = IRenderTexture::factory->CreateTextureHeap({100,100}, 2); rt = IRenderTexture::factory->CreateTextureHeap({100,100}, 5);
sv = SceneView(rt); sv = SceneView(rt);
controller.AddGuiElement("Scene", &sv); controller.AddGuiElement("Scene", &sv);
@@ -26,6 +29,9 @@ TSE::EDITOR::EditorSubsystem::EditorSubsystem() : sv(nullptr), editorLayer("")
BehaviourScriptRegistry::RegisterBehaviourScript("Camera", []() -> BehaviourScript* {return new Camera();}); BehaviourScriptRegistry::RegisterBehaviourScript("Camera", []() -> BehaviourScript* {return new Camera();});
BehaviourScriptRegistry::RegisterBehaviourScript("Audio Listener", []() -> BehaviourScript* {return new AudioListener();}); BehaviourScriptRegistry::RegisterBehaviourScript("Audio Listener", []() -> BehaviourScript* {return new AudioListener();});
BehaviourScriptRegistry::RegisterBehaviourScript("Audio Source", []() -> BehaviourScript* {return new AudioSource();}); BehaviourScriptRegistry::RegisterBehaviourScript("Audio Source", []() -> BehaviourScript* {return new AudioSource();});
BehaviourScriptRegistry::RegisterBehaviourScript("Tile Map", []() -> BehaviourScript* {return new TileMap();});
BehaviourScriptRegistry::RegisterBehaviourScript("Orderer Sprite Set", []() -> BehaviourScript* {return new OrdererSpriteSet();});
BehaviourScriptRegistry::RegisterBehaviourScript("Physics Object", []() -> BehaviourScript* { return new PhysicsObject(BodyType::Dynamic, ColliderShape::Box, 1.0f, 0.3f, Vector3(1, 1, 0)); });
#pragma region camerastuff #pragma region camerastuff
@@ -40,6 +46,7 @@ TSE::EDITOR::EditorSubsystem::EditorSubsystem() : sv(nullptr), editorLayer("")
editorLayer = Layer(".editor"); editorLayer = Layer(".editor");
editorLayer.AddTransformable(editorCamera); editorLayer.AddTransformable(editorCamera);
editorLayer.SetNonVisual(true);
#pragma endregion #pragma endregion
} }

View File

@@ -2,6 +2,7 @@
#include "BehaviourScriptRegistry.hpp" #include "BehaviourScriptRegistry.hpp"
#include "elements/ShaderRegistry.hpp" #include "elements/ShaderRegistry.hpp"
#include "BehaviourScripts/Camera.hpp" #include "BehaviourScripts/Camera.hpp"
#include "windows/HirearchieView.hpp"
#include <algorithm> #include <algorithm>
namespace TSE::EDITOR namespace TSE::EDITOR
@@ -207,6 +208,10 @@ namespace TSE::EDITOR
{ {
Draw((TileMap*)element, debug); Draw((TileMap*)element, debug);
} }
else if (name == "Orderer Sprite Set")
{
Draw((OrdererSpriteSet*)element, debug);
}
else else
{ {
element->CustomDraw(debug); element->CustomDraw(debug);
@@ -439,6 +444,14 @@ namespace TSE::EDITOR
Texture* value = element->GetValue<Texture*>(name); Texture* value = element->GetValue<Texture*>(name);
Draw(value, debug, name , true); Draw(value, debug, name , true);
} }
if (type == typeid(uint).name())
{
int value = element->GetValue<uint>(name);
if(ImGui::InputInt(name.c_str(), &value))
{
element->SetValue(name, value);
}
}
else else
{ {
ImGui::TextDisabled(("Not Implemented: " + type).c_str()); ImGui::TextDisabled(("Not Implemented: " + type).c_str());
@@ -604,6 +617,56 @@ namespace TSE::EDITOR
if (ImGui::DragFloat("Field of View (deg)", &fov, 0.1f)) if (ImGui::DragFloat("Field of View (deg)", &fov, 0.1f))
element->SetFov(fov); element->SetFov(fov);
if (!isPerspective) ImGui::EndDisabled(); 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) void ElementDrawer::Draw(ParticleSystem *element, const bool &debug)
{ {
@@ -828,6 +891,7 @@ namespace TSE::EDITOR
{ {
chunk.SetOrdering(element->order); chunk.SetOrdering(element->order);
} }
element->DirtyAll();
} }
ImGui::BeginDisabled(); ImGui::BeginDisabled();
@@ -855,6 +919,30 @@ namespace TSE::EDITOR
ImGui::TextDisabled(("Chunk Count: " + std::to_string(element->GetChunkCount())).c_str()); ImGui::TextDisabled(("Chunk Count: " + std::to_string(element->GetChunkCount())).c_str());
} }
} }
void ElementDrawer::Draw(OrdererSpriteSet *element, const bool &debug)
{
int orderIndex = static_cast<int>(element->order);
const char* orderItems[] = { "TopRight", "TopLeft", "BottomRight", "BottomLeft" };
if (ImGui::Combo("Order", &orderIndex, orderItems, IM_ARRAYSIZE(orderItems)))
{
element->order = static_cast<SortingOrder>(orderIndex);
for (auto& [_, chunk] : element->chunks)
{
chunk.SetOrdering(element->order);
}
element->DirtyAll();
}
ImGui::BeginDisabled();
ImGui::DragInt("Chunk Size", &element->chunkSize, 1.0f);
ImGui::EndDisabled();
if (debug)
{
ImGui::Separator();
ImGui::TextDisabled(("Chunk Count: " + std::to_string(element->GetChunkCount())).c_str());
}
}
void ElementDrawer::DrawAudioClipCompact(AudioClip *element, const bool &debug, const std::string &label) void ElementDrawer::DrawAudioClipCompact(AudioClip *element, const bool &debug, const std::string &label)
{ {
float item_spacing = ImGui::GetStyle().ItemSpacing.x; float item_spacing = ImGui::GetStyle().ItemSpacing.x;

View File

@@ -14,6 +14,7 @@
#include "BehaviourScripts/AudioListener.hpp" #include "BehaviourScripts/AudioListener.hpp"
#include "BehaviourScripts/AudioSource.hpp" #include "BehaviourScripts/AudioSource.hpp"
#include "BehaviourScripts/TileMap.hpp" #include "BehaviourScripts/TileMap.hpp"
#include "BehaviourScripts/OrdererSpriteSet.hpp"
namespace TSE::EDITOR namespace TSE::EDITOR
{ {
@@ -81,6 +82,7 @@ namespace TSE::EDITOR
static void Draw(Camera* element, const bool& debug); static void Draw(Camera* element, const bool& debug);
static void Draw(ParticleSystem* element, const bool& debug); static void Draw(ParticleSystem* element, const bool& debug);
static void Draw(TileMap* element, const bool& debug); static void Draw(TileMap* element, const bool& debug);
static void Draw(OrdererSpriteSet* element, const bool& debug);
static void DrawAudioClipCompact(AudioClip* element, const bool& debug, const std::string& label); static void DrawAudioClipCompact(AudioClip* element, const bool& debug, const std::string& label);
static void DrawAudioClipNormal(AudioClip* element, const bool& debug, const std::string& label); static void DrawAudioClipNormal(AudioClip* element, const bool& debug, const std::string& label);

View File

@@ -17,7 +17,6 @@ namespace TSE::EDITOR
RenamingTransformable = 3, RenamingTransformable = 3,
RenamingScene = 4 RenamingScene = 4
}; };
Scene* currentScene;
uuids::uuid selected = uuids::uuid(); uuids::uuid selected = uuids::uuid();
bool openPopUpNamingLayer = false; bool openPopUpNamingLayer = false;
@@ -29,6 +28,7 @@ namespace TSE::EDITOR
Layer* tmpHolder2 = nullptr; Layer* tmpHolder2 = nullptr;
public: public:
inline static Scene* currentScene = nullptr;
HirearchieView(Scene* s); HirearchieView(Scene* s);
void SetScene(Scene* s); void SetScene(Scene* s);
void Define() override; void Define() override;

View File

@@ -149,3 +149,13 @@ bool TSE::GLFW::WindowGlfw::ShouldClose() const
{ {
return glfwWindowShouldClose(window); return glfwWindowShouldClose(window);
} }
void TSE::GLFW::WindowGlfw::DoneSetup()
{
renderingBackend->onResize(width, height);
for (auto const& i : objectsToResize)
{
i->OnResize(width, height, this);
}
}

View File

@@ -7,7 +7,6 @@
#include "enums/WindowType.hpp" #include "enums/WindowType.hpp"
#include "Color.hpp" #include "Color.hpp"
namespace TSE::GLFW namespace TSE::GLFW
{ {
class WindowGlfw : public IWindow class WindowGlfw : public IWindow
@@ -36,5 +35,6 @@ namespace TSE::GLFW
bool ShouldClose() const override; bool ShouldClose() const override;
inline void Bind() override { }; inline void Bind() override { };
inline void Unbind() override { }; inline void Unbind() override { };
void DoneSetup() override;
}; };
} // namespace TSE::GLFW } // namespace TSE::GLFW

View File

@@ -0,0 +1,3 @@
#pragma once
#define TSE_GLFW

View File

@@ -1,59 +0,0 @@
#include "RenderTexture.hpp"
TSE::GLFW::RenderTexture::RenderTexture(Vector2 v, uint textureCount) : buffer(v, textureCount)
{
buffer.AddResizeNotifiable(this);
}
TSE::Vector2 TSE::GLFW::RenderTexture::size() const
{
return buffer.GetSize();
}
void TSE::GLFW::RenderTexture::SetSize(Vector2 v)
{
buffer.Resize(v);
}
float TSE::GLFW::RenderTexture::Width() const
{
return buffer.GetSize().x;
}
float TSE::GLFW::RenderTexture::Height() const
{
return buffer.GetSize().y;
}
TSE::uint TSE::GLFW::RenderTexture::GetTextureId() const
{
return buffer.GetTextureId();
}
TSE::uint TSE::GLFW::RenderTexture::GetTextureId(uint id) const
{
return buffer.GetTextureId(id);
}
void TSE::GLFW::RenderTexture::Update()
{
buffer.Update();
}
void TSE::GLFW::RenderTexture::Bind()
{
buffer.Bind();
}
void TSE::GLFW::RenderTexture::Unbind()
{
buffer.Unbind();
}
void TSE::GLFW::RenderTexture::OnResize(float width, float height, IResizable *wnd)
{
for (auto const& i : objectsToResize)
{
i->OnResize(width, height, this);
}
}

View File

@@ -1,61 +0,0 @@
#include "GL/gl3w.h"
#include "GL/gl.h"
#include "TextureHelperOpenGL.hpp"
void TSE::GLFW::TextureHelperOpenGL::Bind(const Texture *tex)
{
glBindTexture(GL_TEXTURE_2D, tex->GetTextureId());
}
void TSE::GLFW::TextureHelperOpenGL::UnBind(const Texture *tex)
{
glBindTexture(GL_TEXTURE_2D, 0);
}
void TSE::GLFW::TextureHelperOpenGL::Apply(Texture *tex)
{
glBindTexture(GL_TEXTURE_2D, tex->GetTextureId());
if(tex->Chanels() == 1)
{
if (tex->bpp() == 8)
glTexImage2D(GL_TEXTURE_2D, 0,GL_RGBA, tex->Width(), tex->Height(), 0, GL_RED, GL_UNSIGNED_BYTE, tex->GetImagePtr());
}
if(tex->Chanels() == 3)
{
if(tex->bpp() == 24)
glTexImage2D(GL_TEXTURE_2D, 0,GL_RGBA, tex->Width(), tex->Height(), 0, GL_BGR, GL_UNSIGNED_BYTE, tex->GetImagePtr());
}
else if(tex->Chanels() == 4)
{
if(tex->bpp() == 32)
glTexImage2D(GL_TEXTURE_2D, 0,GL_RGBA, tex->Width(), tex->Height(), 0, GL_BGRA, GL_UNSIGNED_BYTE, tex->GetImagePtr());
if (tex->bpp() == 8) //need to decode it with bitwise operations in shader
glTexImage2D(GL_TEXTURE_2D, 0,GL_RGBA, tex->Width(), tex->Height(), 0, GL_RED, GL_UNSIGNED_BYTE, tex->GetImagePtr());
}
glGenerateMipmap(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, 0);
}
void TSE::GLFW::TextureHelperOpenGL::Regist(Texture *tex)
{
uint TextureID;
glGenTextures(1, &TextureID);
glBindTexture(GL_TEXTURE_2D, TextureID);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
tex->SetTextureId(TextureID);
tex->Apply();
}
void TSE::GLFW::TextureHelperOpenGL::PlatromDestroy(Texture *tex)
{
uint id = tex->GetTextureId();
glDeleteTextures(1, &id);
}

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

138
TSE_Math/src/Vector2i.cpp Normal file
View File

@@ -0,0 +1,138 @@
#include "Vector2i.hpp"
#include "Vector2.hpp"
#include "Vector3.hpp"
#include "Vector4.hpp"
#include <cmath>
TSE::Vector2i::Vector2i() { }
TSE::Vector2i::Vector2i(int _x, int _y)
{
x = _x;
y = _y;
}
TSE::Vector2i::Vector2i(const Vector2i &other)
{
x = other.x;
y = other.y;
}
TSE::Vector2i::Vector2i(const Vector2 &other)
{
x = other.x;
y = other.y;
}
TSE::Vector2i::Vector2i(const Vector3 &other)
{
x = other.x;
y = other.y;
}
TSE::Vector2i::Vector2i(const Vector4 &other)
{
x = other.x;
y = other.y;
}
bool TSE::Vector2i::IsValid() const
{
return !std::isnan(x) && !std::isnan(y);
}
TSE::string TSE::Vector2i::ToString() const
{
return "(" + std::to_string(x) + "|" + std::to_string(y) + ")";
}
TSE::Vector2 TSE::Vector2i::ToVector2() const
{
return Vector2();
}
TSE::Vector3 TSE::Vector2i::ToVector3() const
{
return Vector3(x,y,0);
}
TSE::Vector4 TSE::Vector2i::ToVector4() const
{
return Vector4(x,y,0,0);
}
TSE::Vector2i TSE::Vector2i::operator+(const Vector2i &other) const
{
return Vector2i(x + other.x, y + other.y);
}
TSE::Vector2i TSE::Vector2i::operator+=(const Vector2i &other)
{
*this = *this + other;
return *this;
}
TSE::Vector2i TSE::Vector2i::operator-(const Vector2i &other) const
{
return Vector2i(x - other.x, y - other.y);
}
TSE::Vector2i TSE::Vector2i::operator-=(const Vector2i &other)
{
*this = *this - other;
return *this;
}
TSE::Vector2i TSE::Vector2i::operator*(const Vector2i &other) const
{
return Vector2i(x * other.x, y * other.y);
}
TSE::Vector2i TSE::Vector2i::operator*=(const Vector2i &other)
{
*this = *this * other;
return *this;
}
TSE::Vector2i TSE::Vector2i::operator/(const Vector2i &other) const
{
return Vector2i(x / other.x, y / other.y);
}
TSE::Vector2i TSE::Vector2i::operator/=(const Vector2i &other)
{
*this = *this / other;
return *this;
}
TSE::Vector2i TSE::Vector2i::operator*(const float other) const
{
return Vector2i(x * other, y * other);
}
TSE::Vector2i TSE::Vector2i::operator*=(const float other)
{
*this = *this * other;
return *this;
}
TSE::Vector2i TSE::Vector2i::operator/(const float other) const
{
return Vector2i(x / other, y / other);
}
TSE::Vector2i TSE::Vector2i::operator/=(const float other)
{
*this = *this / other;
return *this;
}
bool TSE::Vector2i::operator==(const Vector2i &other) const
{
return x == other.x && y == other.y;
}
bool TSE::Vector2i::operator!=(const Vector2i &other) const
{
return !(*this == other);
}

85
TSE_Math/src/Vector2i.hpp Normal file
View File

@@ -0,0 +1,85 @@
#pragma once
#include "Types.hpp"
#include "MathF.hpp"
#include <functional>
namespace TSE
{
class Vector2;
class Vector3;
class Vector4;
class Vector2i
{
public:
#pragma region members
int x = 0, y = 0;
#pragma endregion members
#pragma region consts
static const Vector2i left;
static const Vector2i right;
static const Vector2i up;
static const Vector2i down;
static const Vector2i one;
static const Vector2i zero;
#pragma endregion
#pragma region ctor
/// @brief enpty constructor defined as (0,0)
Vector2i();
/// @brief constructs a Vector2i with custom values
/// @param _x the x component
/// @param _y the y component
Vector2i(int _x, int _y);
/// @brief copy constructor
/// @param other the Vector2i to be copied from
Vector2i(const Vector2i& other);
/// @brief copy constructor
/// @param other the Vector2i to be copied from
Vector2i(const Vector2& other);
/// @brief converter constructor. it converts a Vector3 to a Vector2i without encointing for the z component
/// @param other the Vector3 to convert
Vector2i(const Vector3& other);
/// @brief converter constructor. it converts a Vector4 to a Vector2i without encointing for the z, and w component
/// @param other the Vector4 to convert
Vector2i(const Vector4& other);
#pragma endregion ctor
#pragma region methods
/// @brief checks if the individual components have valid values aka are not nan
/// @return are the values valid
bool IsValid() const;
/// @brief gives you the Vector2i as a string representation. mostly for debugging
/// @return the Vector2i in a format like (x|y)
string ToString() const;
/// @brief creates a Vector3 with the same values as the Vector2i, the y component gets the value 0
/// @return the Vector2i as a Vector3
Vector2 ToVector2() const;
/// @brief creates a Vector3 with the same values as the Vector2i, the y component gets the value 0
/// @return the Vector2i as a Vector3
Vector3 ToVector3() const;
/// @brief creates a Vector4 with the same values as the Vector2i, the y and w components get the value 0
/// @return the Vector2i as a Vector4
Vector4 ToVector4() const;
#pragma region operators
Vector2i operator+(const Vector2i& other) const;
Vector2i operator+=(const Vector2i& other);
Vector2i operator-(const Vector2i& other) const;
Vector2i operator-=(const Vector2i& other);
Vector2i operator*(const Vector2i& other) const;
Vector2i operator*=(const Vector2i& other);
Vector2i operator/(const Vector2i& other) const;
Vector2i operator/=(const Vector2i& other);
Vector2i operator*(const float other) const;
Vector2i operator*=(const float other);
Vector2i operator/(const float other) const;
Vector2i operator/=(const float other);
bool operator==(const Vector2i& other) const;
bool operator!=(const Vector2i& other) const;
#pragma endregion operators
#pragma endregion methods
};
} // namespace TSE

View File

@@ -1,6 +1,9 @@
#pragma once #pragma once
#include "Types.hpp" #include "Types.hpp"
#include <functional>
#include <cstddef>
#include "MathF.hpp"
namespace TSE namespace TSE
{ {
@@ -145,3 +148,22 @@ namespace TSE
}; };
} // 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;
}
};
}

View File

@@ -0,0 +1,71 @@
#cmake version
cmake_minimum_required(VERSION 3.31)
#project name
project(TSE_OpenGlImpl)
#cpp settings
find_program(CLANG_C NAMES clang)
find_program(CLANG_CXX NAMES clang++)
if(CLANG_C AND CLANG_CXX)
message(STATUS "foung Clang, using as Compiler")
set(CMAKE_C_COMPILER ${CLANG_C} CACHE STRING "C Compiler" FORCE)
set(CMAKE_CXX_COMPILER ${CLANG_CXX} CACHE STRING "C++ Compiler" FORCE)
else()
message(STATUS "Clang not found, using Standard-Compiler")
endif()
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED True)
#project output settings
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${PROJECT_SOURCE_DIR}/lib")
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY_DEBUG "${PROJECT_SOURCE_DIR}/lib/Debug")
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY_RELEASE "${PROJECT_SOURCE_DIR}/lib/Release")
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${PROJECT_SOURCE_DIR}/lib")
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY_DEBUG "${PROJECT_SOURCE_DIR}/lib/Debug")
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY_RELEASE "${PROJECT_SOURCE_DIR}/lib/Release")
#source files
file(GLOB CPP_SOURCE_TSE
"${PROJECT_SOURCE_DIR}/src/*.cpp"
"${PROJECT_SOURCE_DIR}/src/*/*.cpp"
"${PROJECT_SOURCE_DIR}/src/*/*/*.cpp"
"${PROJECT_SOURCE_DIR}/src/*/*/*/*.cpp"
"${PROJECT_SOURCE_DIR}/src/*.c"
"${PROJECT_SOURCE_DIR}/src/*/*.c"
"${PROJECT_SOURCE_DIR}/src/*/*/*.c"
"${PROJECT_SOURCE_DIR}/src/*/*/*/*.c"
)
#includes
include_directories(${PROJECT_SOURCE_DIR}/src)
include_directories(${PROJECT_SOURCE_DIR}/include)
include_directories(${PROJECT_SOURCE_DIR}/../TSE_Base/src)
include_directories(${PROJECT_SOURCE_DIR}/../TSE_Base/include)
include_directories(${PROJECT_SOURCE_DIR}/../TSE_Math/src)
include_directories(${PROJECT_SOURCE_DIR}/../TSE_Core/src)
include_directories(${PROJECT_SOURCE_DIR}/../TSE_Core/include)
#project def
if(Lib)
add_library(TSE_OpenGlImpl SHARED ${CPP_SOURCE_TSE})
else()
add_library(TSE_OpenGlImpl STATIC ${CPP_SOURCE_TSE})
endif()
# Window backend specific includes for WindowManager.hpp and platform glue.
if(USE_GLFW)
target_include_directories(TSE_OpenGlImpl PRIVATE
${PROJECT_SOURCE_DIR}/../TSE_GlfwImpl/src
${PROJECT_SOURCE_DIR}/../TSE_GlfwImpl/include
)
elseif(USE_SDL3)
target_include_directories(TSE_OpenGlImpl PRIVATE
${PROJECT_SOURCE_DIR}/../TSE_Sdl3Impl/src
${PROJECT_SOURCE_DIR}/../TSE_Sdl3Impl/include
)
endif()
#flags
target_compile_options(TSE_OpenGlImpl PRIVATE -march=native)

View File

@@ -4,7 +4,7 @@
#include "GL/gl3w.h" #include "GL/gl3w.h"
#include "GL/gl.h" #include "GL/gl.h"
namespace TSE::GLFW namespace TSE::OpenGL
{ {
class CameraHelperOpenGL : public ICameraHelper class CameraHelperOpenGL : public ICameraHelper
{ {
@@ -14,4 +14,4 @@ namespace TSE::GLFW
glViewport(0, 0, width, height); glViewport(0, 0, width, height);
}; };
}; };
} // namespace TSE::GLFW } // namespace TSE::OpenGL

View File

@@ -1,17 +1,18 @@
#include "DefaultRendererOpenGL.hpp" #include "DefaultRendererOpenGL.hpp"
#include "BehaviourScripts/Camera.hpp" #include "BehaviourScripts/Camera.hpp"
#include "BehaviourScripts/Renderable.hpp" #include "BehaviourScripts/Renderable.hpp"
#include "Debug.hpp"
#define RENDERER_MAX_SPRITES 20000 #define RENDERER_MAX_SPRITES 20000
#define RENDERER_MAX_INDECIES 60000 #define RENDERER_MAX_INDECIES 60000
TSE::GLFW::DefaultRendererOpenGL::DefaultRendererOpenGL(Shader &shader) TSE::OpenGL::DefaultRendererOpenGL::DefaultRendererOpenGL(Shader &shader)
{ {
iboData = new ushort[RENDERER_MAX_INDECIES]; iboData = new ushort[RENDERER_MAX_INDECIES];
InitShader(shader, true); InitShader(shader, true);
} }
TSE::GLFW::DefaultRendererOpenGL::~DefaultRendererOpenGL() TSE::OpenGL::DefaultRendererOpenGL::~DefaultRendererOpenGL()
{ {
if(vao != 0) if(vao != 0)
glDeleteVertexArrays(1, &vao); glDeleteVertexArrays(1, &vao);
@@ -19,7 +20,7 @@ TSE::GLFW::DefaultRendererOpenGL::~DefaultRendererOpenGL()
delete ibo; delete ibo;
} }
void TSE::GLFW::DefaultRendererOpenGL::InitShader(Shader &s, bool init) void TSE::OpenGL::DefaultRendererOpenGL::InitShader(Shader &s, bool init)
{ {
if(!init) End(); if(!init) End();
if(vao != 0) if(vao != 0)
@@ -50,13 +51,13 @@ void TSE::GLFW::DefaultRendererOpenGL::InitShader(Shader &s, bool init)
if(!init) Begin(); if(!init) Begin();
} }
void TSE::GLFW::DefaultRendererOpenGL::End() void TSE::OpenGL::DefaultRendererOpenGL::End()
{ {
glUnmapBuffer(GL_ARRAY_BUFFER); glUnmapBuffer(GL_ARRAY_BUFFER);
vbo.Unbind(); vbo.Unbind();
} }
void TSE::GLFW::DefaultRendererOpenGL::Flush() void TSE::OpenGL::DefaultRendererOpenGL::Flush()
{ {
lastShader->Flush(); lastShader->Flush();
@@ -78,6 +79,8 @@ void TSE::GLFW::DefaultRendererOpenGL::Flush()
camerasToRenderWith[i]->PostDraw(); camerasToRenderWith[i]->PostDraw();
} }
lastShader->PostDraw();
if(ibobound) if(ibobound)
{ {
ibo->Unbind(); ibo->Unbind();
@@ -88,18 +91,18 @@ void TSE::GLFW::DefaultRendererOpenGL::Flush()
iboOffset = 0; iboOffset = 0;
} }
void TSE::GLFW::DefaultRendererOpenGL::Begin() void TSE::OpenGL::DefaultRendererOpenGL::Begin()
{ {
vbo.Bind(); vbo.Bind();
bufferPointer = (float*)glMapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY); bufferPointer = (float*)glMapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY);
} }
void TSE::GLFW::DefaultRendererOpenGL::Submit(const Transformable &trans, TransformationStack &stack) void TSE::OpenGL::DefaultRendererOpenGL::Submit(const Transformable &trans, TransformationStack &stack)
{ {
Submit(trans, (IShader*)lastShader, stack); Submit(trans, (IShader*)lastShader, stack);
} }
void TSE::GLFW::DefaultRendererOpenGL::Submit(const Transformable &trans, IShader *shader, TransformationStack &stack) void TSE::OpenGL::DefaultRendererOpenGL::Submit(const Transformable &trans, IShader *shader, TransformationStack &stack)
{ {
Shader* s = dynamic_cast<Shader*>(shader); Shader* s = dynamic_cast<Shader*>(shader);
if(lastShader == nullptr) InitShader(*s, true); if(lastShader == nullptr) InitShader(*s, true);
@@ -134,14 +137,14 @@ void TSE::GLFW::DefaultRendererOpenGL::Submit(const Transformable &trans, IShade
lastShader->Submit(trans, bufferPointer, stack, Redraw, *this); lastShader->Submit(trans, bufferPointer, stack, Redraw, *this);
} }
void TSE::GLFW::DefaultRendererOpenGL::Redraw(IRenderer &rnd) void TSE::OpenGL::DefaultRendererOpenGL::Redraw(IRenderer &rnd)
{ {
rnd.End(); rnd.End();
rnd.Flush(); rnd.Flush();
rnd.Begin(); rnd.Begin();
} }
bool TSE::GLFW::DefaultRendererOpenGL::CreateIbo() bool TSE::OpenGL::DefaultRendererOpenGL::CreateIbo()
{ {
if(indexCount == 0) return true; if(indexCount == 0) return true;
if(ibo != nullptr) if(ibo != nullptr)

View File

@@ -6,7 +6,7 @@
#include "interfaces/IRenderer.hpp" #include "interfaces/IRenderer.hpp"
#include <vector> #include <vector>
namespace TSE::GLFW namespace TSE::OpenGL
{ {
class DefaultRendererOpenGL : public IRenderer class DefaultRendererOpenGL : public IRenderer
{ {
@@ -35,4 +35,4 @@ namespace TSE::GLFW
static void Redraw(IRenderer& rnd); static void Redraw(IRenderer& rnd);
bool CreateIbo(); bool CreateIbo();
}; };
} // namespace TSE::GLFW } // namespace TSE::OpenGL

View File

@@ -1,24 +1,36 @@
#include "GL/gl3w.h" #include "GL/gl3w.h"
#include "GL/gl.h" #include "GL/gl.h"
#include "OpenGLRenderingBackend.hpp" #include "OpenGLRenderingBackend.hpp"
#include "GLFW/glfw3.h"
#include "WindowGlfw.hpp"
#include "Debug.hpp" #include "Debug.hpp"
#include "imgui/imgui.h" #include "imgui/imgui.h"
#include "extern/imgui_impl_glfw.h"
#include "extern/imgui_impl_opengl3.h" #include "extern/imgui_impl_opengl3.h"
#include "PathHelper.hpp" #include "PathHelper.hpp"
#include "elements/Texture.hpp" #include "interfaces/ITexture.hpp"
#include "TextureHelperOpenGL.hpp" #include "TextureHelperOpenGL.hpp"
#include "interfaces/IRenderer.hpp" #include "interfaces/IRenderer.hpp"
#include "BehaviourScripts/Camera.hpp" #include "BehaviourScripts/Camera.hpp"
#include "RenderTextureCreatorOpenGL.hpp" #include "RenderTextureCreatorOpenGL.hpp"
#include "CameraHelperOpenGL.hpp" #include "CameraHelperOpenGL.hpp"
TSE::GLFW::OpenGLRenderingBackend::OpenGLRenderingBackend(Color _backgroundColor, bool _vsync) #include "WindowManager.hpp"
#if defined(TSE_GLFW)
#include "GLFW/glfw3.h"
#include "WindowGlfw.hpp"
#include "extern/imgui_impl_glfw.h"
using namespace TSE::GLFW;
#elif defined(TSE_SDL3)
#include "SDL3/SDL.h"
#include "WindowSdl3.hpp"
#include "extern/imgui_impl_sdl3.h"
using namespace TSE::SDL3;
#endif
TSE::OpenGL::OpenGLRenderingBackend::OpenGLRenderingBackend(Color _backgroundColor, bool _vsync)
: OpenGLRenderingBackend(_backgroundColor, _vsync, 0, false){ } : OpenGLRenderingBackend(_backgroundColor, _vsync, 0, false){ }
TSE::GLFW::OpenGLRenderingBackend::OpenGLRenderingBackend(Color _backgroundColor, bool _vsync, int _samples, bool _useseImGui) TSE::OpenGL::OpenGLRenderingBackend::OpenGLRenderingBackend(Color _backgroundColor, bool _vsync, int _samples, bool _useseImGui)
{ {
backgroundColor = _backgroundColor; backgroundColor = _backgroundColor;
vsync = _vsync; vsync = _vsync;
@@ -26,33 +38,50 @@ TSE::GLFW::OpenGLRenderingBackend::OpenGLRenderingBackend(Color _backgroundColor
useseImGui = _useseImGui; useseImGui = _useseImGui;
} }
TSE::GLFW::OpenGLRenderingBackend::~OpenGLRenderingBackend() TSE::OpenGL::OpenGLRenderingBackend::~OpenGLRenderingBackend()
{ {
if(useseImGui) if(useseImGui)
{ {
ImGui_ImplOpenGL3_Shutdown(); ImGui_ImplOpenGL3_Shutdown();
#if defined(TSE_GLFW)
ImGui_ImplGlfw_Shutdown(); ImGui_ImplGlfw_Shutdown();
#elif defined(TSE_SDL3)
ImGui_ImplSDL3_Shutdown();
#endif
ImGui::DestroyContext(); ImGui::DestroyContext();
} }
} }
void TSE::GLFW::OpenGLRenderingBackend::InitPreWindow() void TSE::OpenGL::OpenGLRenderingBackend::InitPreWindow()
{ {
IRenderTexture::factory = new RenderTextureCreatorOpenGL(); IRenderTexture::factory = new RenderTextureCreatorOpenGL();
Texture::helper = new TextureHelperOpenGL(); ITexture::helper = new TextureHelperOpenGL();
Camera::helper = new CameraHelperOpenGL(); Camera::helper = new CameraHelperOpenGL();
#if defined(TSE_GLFW)
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, TSE_OPENGL_VERSION_MAJOR); glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, TSE_OPENGL_VERSION_MAJOR);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, TSE_OPENGL_VERSION_MINOR); glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, TSE_OPENGL_VERSION_MINOR);
glfwWindowHint(GLFW_OPENGL_DEBUG_CONTEXT, 1);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
glfwWindowHint(GLFW_SAMPLES, samples); glfwWindowHint(GLFW_SAMPLES, samples);
#elif defined(TSE_SDL3)
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, TSE_OPENGL_VERSION_MAJOR);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, TSE_OPENGL_VERSION_MINOR);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 1);
SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, samples);
#endif
} }
bool TSE::GLFW::OpenGLRenderingBackend::InitPostWindow() bool TSE::OpenGL::OpenGLRenderingBackend::InitPostWindow()
{ {
#if defined(TSE_GLFW)
WindowGlfw* wnd = static_cast<WindowGlfw*>(window); WindowGlfw* wnd = static_cast<WindowGlfw*>(window);
glfwMakeContextCurrent(wnd->window); glfwMakeContextCurrent(wnd->window);
#elif defined(TSE_SDL3)
WindowSdl3* wnd = static_cast<WindowSdl3*>(window);
wnd->context = SDL_GL_CreateContext(wnd->window);
SDL_GL_MakeCurrent(wnd->window, wnd->context);
#endif
if(gl3wInit()) if(gl3wInit())
{ {
Debug::Log("Failed to initialize gl3w."); Debug::Log("Failed to initialize gl3w.");
@@ -63,9 +92,17 @@ bool TSE::GLFW::OpenGLRenderingBackend::InitPostWindow()
Debug::Log("gl3w dose not support the selected version of OpenGL."); Debug::Log("gl3w dose not support the selected version of OpenGL.");
return false; return false;
} }
#if defined(TSE_GLFW)
if(vsync) glfwSwapInterval(1); if(vsync) glfwSwapInterval(1);
else glfwSwapInterval(0); else glfwSwapInterval(0);
#elif defined(TSE_SDL3)
if(vsync)
{
if(!SDL_GL_SetSwapInterval(-1))
SDL_GL_SetSwapInterval(1);
}
else SDL_GL_SetSwapInterval(0);
#endif
Debug::Log("OpenGL:" + std::string((const char*)glGetString(GL_VERSION))); Debug::Log("OpenGL:" + std::string((const char*)glGetString(GL_VERSION)));
Debug::Log("GLSL:" + std::string((const char*)glGetString(GL_SHADING_LANGUAGE_VERSION))); Debug::Log("GLSL:" + std::string((const char*)glGetString(GL_SHADING_LANGUAGE_VERSION)));
@@ -88,7 +125,7 @@ bool TSE::GLFW::OpenGLRenderingBackend::InitPostWindow()
std::string imguiIniPath; std::string imguiIniPath;
bool TSE::GLFW::OpenGLRenderingBackend::InitEnd() bool TSE::OpenGL::OpenGLRenderingBackend::InitEnd()
{ {
if(useseImGui) if(useseImGui)
{ {
@@ -102,8 +139,15 @@ bool TSE::GLFW::OpenGLRenderingBackend::InitEnd()
io.IniFilename = imguiIniPath.c_str(); io.IniFilename = imguiIniPath.c_str();
ImGui::StyleColorsDark(); ImGui::StyleColorsDark();
#if defined(TSE_GLFW)
WindowGlfw* wnd = static_cast<WindowGlfw*>(window); WindowGlfw* wnd = static_cast<WindowGlfw*>(window);
wnd->useImGui = true;
ImGui_ImplGlfw_InitForOpenGL(wnd->window, true); ImGui_ImplGlfw_InitForOpenGL(wnd->window, true);
#elif defined(TSE_SDL3)
WindowSdl3* wnd = static_cast<WindowSdl3*>(window);
wnd->useImGui = true;
ImGui_ImplSDL3_InitForOpenGL(wnd->window, wnd->context);
#endif
ImGui_ImplOpenGL3_Init("#version 130"); ImGui_ImplOpenGL3_Init("#version 130");
Debug::Log("ImGui:" + std::string(ImGui::GetVersion())); Debug::Log("ImGui:" + std::string(ImGui::GetVersion()));
@@ -111,12 +155,12 @@ bool TSE::GLFW::OpenGLRenderingBackend::InitEnd()
return true; return true;
} }
void TSE::GLFW::OpenGLRenderingBackend::onResize(int width, int height) void TSE::OpenGL::OpenGLRenderingBackend::onResize(int width, int height)
{ {
glViewport(0,0,width, height); glViewport(0,0,width, height);
} }
void TSE::GLFW::OpenGLRenderingBackend::onUpdate() const void TSE::OpenGL::OpenGLRenderingBackend::onUpdate() const
{ {
int error = glGetError(); int error = glGetError();
if(error != GL_NO_ERROR) if(error != GL_NO_ERROR)
@@ -124,6 +168,12 @@ void TSE::GLFW::OpenGLRenderingBackend::onUpdate() const
Debug::Log("OpenGL Error: " + std::to_string(error)); Debug::Log("OpenGL Error: " + std::to_string(error));
} }
#if defined(TSE_GLFW)
WindowGlfw* wnd = static_cast<WindowGlfw*>(window);
#elif defined(TSE_SDL3)
WindowSdl3* wnd = static_cast<WindowSdl3*>(window);
#endif
if(useseImGui) if(useseImGui)
{ {
ImGui::Render(); ImGui::Render();
@@ -132,19 +182,30 @@ void TSE::GLFW::OpenGLRenderingBackend::onUpdate() const
if(ImGui::GetIO().ConfigFlags & ImGuiConfigFlags_ViewportsEnable) if(ImGui::GetIO().ConfigFlags & ImGuiConfigFlags_ViewportsEnable)
{ {
#if defined(TSE_GLFW)
GLFWwindow* backup_current_context = glfwGetCurrentContext(); GLFWwindow* backup_current_context = glfwGetCurrentContext();
#elif defined(TSE_SDL3)
SDL_GLContext backup_current_context = SDL_GL_GetCurrentContext();
#endif
ImGui::UpdatePlatformWindows(); ImGui::UpdatePlatformWindows();
ImGui::RenderPlatformWindowsDefault(); ImGui::RenderPlatformWindowsDefault();
#if defined(TSE_GLFW)
glfwMakeContextCurrent(backup_current_context); glfwMakeContextCurrent(backup_current_context);
#elif defined(TSE_SDL3)
SDL_GL_MakeCurrent(wnd->window, backup_current_context);
#endif
} }
} }
WindowGlfw* wnd = static_cast<WindowGlfw*>(window); #if defined(TSE_GLFW)
glfwSwapBuffers(wnd->window); glfwSwapBuffers(wnd->window);
#elif defined(TSE_SDL3)
SDL_GL_SwapWindow(wnd->window);
#endif
} }
void TSE::GLFW::OpenGLRenderingBackend::onClear() const void TSE::OpenGL::OpenGLRenderingBackend::onClear() const
{ {
for (int i = 0; i < IRenderer::camerasToRenderWith.size(); i++) for (int i = 0; i < IRenderer::camerasToRenderWith.size(); i++)
{ {
@@ -158,12 +219,16 @@ void TSE::GLFW::OpenGLRenderingBackend::onClear() const
if(useseImGui) if(useseImGui)
{ {
ImGui_ImplOpenGL3_NewFrame(); ImGui_ImplOpenGL3_NewFrame();
#if defined(TSE_GLFW)
ImGui_ImplGlfw_NewFrame(); ImGui_ImplGlfw_NewFrame();
#elif defined(TSE_SDL3)
ImGui_ImplSDL3_NewFrame();
#endif
ImGui::NewFrame(); ImGui::NewFrame();
} }
} }
void TSE::GLFW::OpenGLRenderingBackend::onClearDepthBuffer() const void TSE::OpenGL::OpenGLRenderingBackend::onClearDepthBuffer() const
{ {
for (int i = 0; i < IRenderer::camerasToRenderWith.size(); i++) for (int i = 0; i < IRenderer::camerasToRenderWith.size(); i++)
{ {

View File

@@ -5,7 +5,7 @@
#define TSE_OPENGL_VERSION_MAJOR 3 #define TSE_OPENGL_VERSION_MAJOR 3
#define TSE_OPENGL_VERSION_MINOR 3 #define TSE_OPENGL_VERSION_MINOR 3
namespace TSE::GLFW namespace TSE::OpenGL
{ {
class OpenGLRenderingBackend : public IRenderingBackend class OpenGLRenderingBackend : public IRenderingBackend
{ {
@@ -25,4 +25,4 @@ namespace TSE::GLFW
void onClear() const override; void onClear() const override;
void onClearDepthBuffer() const override; void onClearDepthBuffer() const override;
}; };
} // namespace TSE } // namespace OpenGL

View File

@@ -0,0 +1,59 @@
#include "RenderTexture.hpp"
TSE::OpenGL::RenderTexture::RenderTexture(Vector2 v, uint textureCount) : buffer(v, textureCount)
{
buffer.AddResizeNotifiable(this);
}
TSE::Vector2 TSE::OpenGL::RenderTexture::size() const
{
return buffer.GetSize();
}
void TSE::OpenGL::RenderTexture::SetSize(Vector2 v)
{
buffer.Resize(v);
}
float TSE::OpenGL::RenderTexture::Width() const
{
return buffer.GetSize().x;
}
float TSE::OpenGL::RenderTexture::Height() const
{
return buffer.GetSize().y;
}
TSE::uint TSE::OpenGL::RenderTexture::GetTextureId() const
{
return buffer.GetTextureId();
}
TSE::uint TSE::OpenGL::RenderTexture::GetTextureId(uint id) const
{
return buffer.GetTextureId(id);
}
void TSE::OpenGL::RenderTexture::Update()
{
buffer.Update();
}
void TSE::OpenGL::RenderTexture::Bind()
{
buffer.Bind();
}
void TSE::OpenGL::RenderTexture::Unbind()
{
buffer.Unbind();
}
void TSE::OpenGL::RenderTexture::OnResize(float width, float height, IResizable *wnd)
{
for (auto const& i : objectsToResize)
{
i->OnResize(width, height, this);
}
}

View File

@@ -6,7 +6,7 @@
#include "interfaces/IResizeNotifiable.hpp" #include "interfaces/IResizeNotifiable.hpp"
#include "interfaces/IRenderTexture.hpp" #include "interfaces/IRenderTexture.hpp"
namespace TSE::GLFW namespace TSE::OpenGL
{ {
class RenderTexture : public IRenderTexture class RenderTexture : public IRenderTexture
{ {
@@ -28,4 +28,4 @@ namespace TSE::GLFW
void OnResize(float width, float height, IResizable* wnd) override; void OnResize(float width, float height, IResizable* wnd) override;
}; };
} // namespace TSE::GLFW } // namespace TSE::OpenGL

View File

@@ -3,7 +3,7 @@
#include "interfaces/IRenderTexture.hpp" #include "interfaces/IRenderTexture.hpp"
#include "RenderTexture.hpp" #include "RenderTexture.hpp"
namespace TSE::GLFW namespace TSE::OpenGL
{ {
class RenderTextureCreatorOpenGL : public IRenderTextureCreator class RenderTextureCreatorOpenGL : public IRenderTextureCreator
{ {
@@ -13,4 +13,4 @@ namespace TSE::GLFW
return new RenderTexture(v, textureCount); return new RenderTexture(v, textureCount);
}; };
}; };
} // namespace name } // namespace OpenGL

View File

@@ -0,0 +1,165 @@
#include "GL/gl3w.h"
#include "GL/gl.h"
#include "TextureHelperOpenGL.hpp"
void TSE::OpenGL::TextureHelperOpenGL::Bind(const Texture *tex)
{
glBindTexture(GL_TEXTURE_2D, tex->GetTextureId());
}
void TSE::OpenGL::TextureHelperOpenGL::UnBind(const Texture *tex)
{
glBindTexture(GL_TEXTURE_2D, 0);
}
void TSE::OpenGL::TextureHelperOpenGL::Apply(Texture *tex)
{
glBindTexture(GL_TEXTURE_2D, tex->GetTextureId());
if(tex->Chanels() == 1)
{
if (tex->bpp() == 8)
glTexImage2D(GL_TEXTURE_2D, 0,GL_R8, tex->Width(), tex->Height(), 0, GL_RED, GL_UNSIGNED_BYTE, tex->GetImagePtr());
if (tex->bpp() == 16)
glTexImage2D(GL_TEXTURE_2D, 0, GL_R16I, tex->Width(), tex->Height(), 0, GL_RED, GL_SHORT, tex->GetImagePtr());
}
if(tex->Chanels() == 3)
{
if(tex->bpp() == 24)
glTexImage2D(GL_TEXTURE_2D, 0,GL_RGB, tex->Width(), tex->Height(), 0, GL_BGR, GL_UNSIGNED_BYTE, tex->GetImagePtr());
}
else if(tex->Chanels() == 4)
{
if(tex->bpp() == 32)
glTexImage2D(GL_TEXTURE_2D, 0,GL_RGBA, tex->Width(), tex->Height(), 0, GL_BGRA, GL_UNSIGNED_BYTE, tex->GetImagePtr());
if (tex->bpp() == 8) //need to decode it with bitwise operations in shader
glTexImage2D(GL_TEXTURE_2D, 0,GL_RGBA2, tex->Width(), tex->Height(), 0, GL_RED, GL_UNSIGNED_BYTE, tex->GetImagePtr());
}
glGenerateMipmap(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, 0);
}
void TSE::OpenGL::TextureHelperOpenGL::Regist(Texture *tex)
{
uint TextureID;
glGenTextures(1, &TextureID);
glBindTexture(GL_TEXTURE_2D, TextureID);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
tex->SetTextureId(TextureID);
tex->Apply();
}
void TSE::OpenGL::TextureHelperOpenGL::PlatromDestroy(Texture *tex)
{
uint id = tex->GetTextureId();
glDeleteTextures(1, &id);
}
void TSE::OpenGL::TextureHelperOpenGL::Bind3D(const VolumeTexture3D *tex)
{
glBindTexture(GL_TEXTURE_3D, tex->GetTextureId());
}
void TSE::OpenGL::TextureHelperOpenGL::UnBind3D(const VolumeTexture3D *tex)
{
glBindTexture(GL_TEXTURE_3D, 0);
}
void TSE::OpenGL::TextureHelperOpenGL::Apply3D(VolumeTexture3D *tex)
{
glBindTexture(GL_TEXTURE_3D, tex->GetTextureId());
ushort internal,input,size;
if(tex->Chanels() == 1)
{
if (tex->bpp() == 8)
{
internal = GL_R8;
input = GL_RED;
size = GL_UNSIGNED_BYTE;
}
if (tex->bpp() == 16)
{
internal = GL_R16I;
input = GL_RED;
size = GL_SHORT;
}
}
if(tex->Chanels() == 3)
{
if(tex->bpp() == 24)
{
internal = GL_RGB;
input = GL_BGR;
size = GL_UNSIGNED_BYTE;
}
}
else if(tex->Chanels() == 4)
{
if(tex->bpp() == 32)
{
internal = GL_R16;
input = GL_BGRA;
size = GL_UNSIGNED_BYTE;
}
if (tex->bpp() == 8) //need to decode it with bitwise operations in shader
{
internal = GL_RGBA2;
input = GL_RED;
size = GL_UNSIGNED_BYTE;
}
}
glTexImage3D(GL_TEXTURE_3D, 0, internal, tex->Width(), tex->Height(), tex->Depth(), 0, input, size, nullptr);
for (int z = 0; z < tex->Depth(); ++z)
{
glTexSubImage3D(
GL_TEXTURE_3D,
0,
0,
0,
z,
tex->Width(),
tex->Height(),
1,
input,
size,
tex->GetImagePtr(z)
);
}
//glGenerateMipmap(GL_TEXTURE_3D);
glBindTexture(GL_TEXTURE_3D, 0);
}
void TSE::OpenGL::TextureHelperOpenGL::Regist3D(VolumeTexture3D *tex)
{
uint TextureID;
glGenTextures(1, &TextureID);
glBindTexture(GL_TEXTURE_3D, TextureID);
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
tex->SetTextureId(TextureID);
tex->Apply();
}
void TSE::OpenGL::TextureHelperOpenGL::PlatromDestroy3D(VolumeTexture3D *tex)
{
uint id = tex->GetTextureId();
glDeleteTextures(1, &id);
}

View File

@@ -2,8 +2,9 @@
#include "interfaces/ITextureHelper.hpp" #include "interfaces/ITextureHelper.hpp"
#include "elements/Texture.hpp" #include "elements/Texture.hpp"
#include "elements/VolumeTexture3D.hpp"
namespace TSE::GLFW namespace TSE::OpenGL
{ {
class TextureHelperOpenGL : public ITextureHelper class TextureHelperOpenGL : public ITextureHelper
{ {
@@ -13,5 +14,11 @@ namespace TSE::GLFW
void Apply(Texture* tex) override; void Apply(Texture* tex) override;
void Regist(Texture* tex) override; void Regist(Texture* tex) override;
void PlatromDestroy(Texture* tex) override; void PlatromDestroy(Texture* tex) override;
void Bind3D(const VolumeTexture3D* tex) override;
void UnBind3D(const VolumeTexture3D* tex) override;
void Apply3D(VolumeTexture3D* tex) override;
void Regist3D(VolumeTexture3D* tex) override;
void PlatromDestroy3D(VolumeTexture3D* tex) override;
}; };
} // namespace TSE::GLFW } // namespace TSE::OpenGL

View File

@@ -1,7 +1,7 @@
#include "FrameBuffer.hpp" #include "FrameBuffer.hpp"
#include "Debug.hpp" #include "Debug.hpp"
TSE::GLFW::FrameBuffer::FrameBuffer(const Vector2 &size, uint textureCount) TSE::OpenGL::FrameBuffer::FrameBuffer(const Vector2 &size, uint textureCount)
{ {
textureOutputCount = textureCount; textureOutputCount = textureCount;
width = size.x; width = size.x;
@@ -14,17 +14,17 @@ TSE::GLFW::FrameBuffer::FrameBuffer(const Vector2 &size, uint textureCount)
Initialize(); Initialize();
} }
void TSE::GLFW::FrameBuffer::Bind() void TSE::OpenGL::FrameBuffer::Bind()
{ {
glBindFramebuffer(GL_FRAMEBUFFER, bufferID); glBindFramebuffer(GL_FRAMEBUFFER, bufferID);
} }
void TSE::GLFW::FrameBuffer::Unbind() void TSE::OpenGL::FrameBuffer::Unbind()
{ {
glBindFramebuffer(GL_FRAMEBUFFER, 0); glBindFramebuffer(GL_FRAMEBUFFER, 0);
} }
TSE::GLFW::FrameBuffer::~FrameBuffer() TSE::OpenGL::FrameBuffer::~FrameBuffer()
{ {
glDeleteFramebuffers(1,&bufferID); glDeleteFramebuffers(1,&bufferID);
for (int i = 0; i < textureOutputCount; i++) for (int i = 0; i < textureOutputCount; i++)
@@ -34,14 +34,14 @@ TSE::GLFW::FrameBuffer::~FrameBuffer()
glDeleteRenderbuffers(1, &depthRboID); glDeleteRenderbuffers(1, &depthRboID);
} }
void TSE::GLFW::FrameBuffer::Resize(Vector2 size) void TSE::OpenGL::FrameBuffer::Resize(Vector2 size)
{ {
width = size.x; width = size.x;
height = size.y; height = size.y;
shouldResize = true; shouldResize = true;
} }
void TSE::GLFW::FrameBuffer::Update() void TSE::OpenGL::FrameBuffer::Update()
{ {
if (!shouldResize) return; if (!shouldResize) return;
shouldResize = false; shouldResize = false;
@@ -52,17 +52,17 @@ void TSE::GLFW::FrameBuffer::Update()
} }
} }
TSE::uint TSE::GLFW::FrameBuffer::GetTextureId(uint id) const TSE::uint TSE::OpenGL::FrameBuffer::GetTextureId(uint id) const
{ {
return textureIDs[id]; return textureIDs[id];
} }
TSE::Vector2 TSE::GLFW::FrameBuffer::GetSize() const TSE::Vector2 TSE::OpenGL::FrameBuffer::GetSize() const
{ {
return {width, height}; return {width, height};
} }
void TSE::GLFW::FrameBuffer::Initialize() void TSE::OpenGL::FrameBuffer::Initialize()
{ {
glGenFramebuffers(1, &bufferID); glGenFramebuffers(1, &bufferID);
Bind(); Bind();
@@ -85,7 +85,7 @@ void TSE::GLFW::FrameBuffer::Initialize()
Unbind(); Unbind();
} }
void TSE::GLFW::FrameBuffer::LoadFBTexture() void TSE::OpenGL::FrameBuffer::LoadFBTexture()
{ {
for (int i = 0; i < textureOutputCount; i++) for (int i = 0; i < textureOutputCount; i++)
{ {
@@ -96,13 +96,15 @@ void TSE::GLFW::FrameBuffer::LoadFBTexture()
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0);
glBindRenderbuffer(GL_RENDERBUFFER, depthRboID); glBindRenderbuffer(GL_RENDERBUFFER, depthRboID);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, width, height); glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, width, height);
} }
} }
void TSE::GLFW::FrameBuffer::CreateFBTexture() void TSE::OpenGL::FrameBuffer::CreateFBTexture()
{ {
glViewport(0,0, width, height); glViewport(0,0, width, height);
//resize //resize

View File

@@ -4,7 +4,7 @@
#include "interfaces/IResizable.hpp" #include "interfaces/IResizable.hpp"
#include "Vector2.hpp" #include "Vector2.hpp"
namespace TSE::GLFW namespace TSE::OpenGL
{ {
class FrameBuffer : public buffer, public IResizable class FrameBuffer : public buffer, public IResizable
{ {

View File

@@ -1,24 +1,24 @@
#include "IndexBuffer.hpp" #include "IndexBuffer.hpp"
TSE::GLFW::IndexBuffer::IndexBuffer(ushort *data, ushort count) TSE::OpenGL::IndexBuffer::IndexBuffer(ushort *data, ushort count)
{ {
glGenBuffers(1, &bufferID); glGenBuffers(1, &bufferID);
WriteData(data, count); WriteData(data, count);
} }
void TSE::GLFW::IndexBuffer::WriteData(ushort *data, ushort count) void TSE::OpenGL::IndexBuffer::WriteData(ushort *data, ushort count)
{ {
Bind(); Bind();
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(ushort) * count, data, GL_DYNAMIC_DRAW); glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(ushort) * count, data, GL_DYNAMIC_DRAW);
Unbind(); Unbind();
} }
void TSE::GLFW::IndexBuffer::Bind() void TSE::OpenGL::IndexBuffer::Bind()
{ {
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, bufferID); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, bufferID);
} }
void TSE::GLFW::IndexBuffer::Unbind() void TSE::OpenGL::IndexBuffer::Unbind()
{ {
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
} }

View File

@@ -3,7 +3,7 @@
#include "buffer.hpp" #include "buffer.hpp"
#include "Types.hpp" #include "Types.hpp"
namespace TSE::GLFW namespace TSE::OpenGL
{ {
class IndexBuffer : public buffer class IndexBuffer : public buffer
{ {

View File

@@ -1,35 +1,35 @@
#include "VertexBuffer.hpp" #include "VertexBuffer.hpp"
TSE::uint TSE::GLFW::VertexBuffer::boundBuffer = 0; TSE::uint TSE::OpenGL::VertexBuffer::boundBuffer = 0;
TSE::GLFW::VertexBuffer::VertexBuffer() TSE::OpenGL::VertexBuffer::VertexBuffer()
{ {
glGenBuffers(1, &bufferID); glGenBuffers(1, &bufferID);
} }
void TSE::GLFW::VertexBuffer::SetData(int size, void *buffer, GLenum usage) void TSE::OpenGL::VertexBuffer::SetData(int size, void *buffer, GLenum usage)
{ {
glBufferData(GL_ARRAY_BUFFER, size, buffer, usage); glBufferData(GL_ARRAY_BUFFER, size, buffer, usage);
} }
void TSE::GLFW::VertexBuffer::Bind() void TSE::OpenGL::VertexBuffer::Bind()
{ {
glBindBuffer(GL_ARRAY_BUFFER, bufferID); glBindBuffer(GL_ARRAY_BUFFER, bufferID);
boundBuffer = bufferID; boundBuffer = bufferID;
} }
void TSE::GLFW::VertexBuffer::Unbind() void TSE::OpenGL::VertexBuffer::Unbind()
{ {
glBindBuffer(GL_ARRAY_BUFFER, 0); glBindBuffer(GL_ARRAY_BUFFER, 0);
boundBuffer = 0; boundBuffer = 0;
} }
bool TSE::GLFW::VertexBuffer::IsBound() bool TSE::OpenGL::VertexBuffer::IsBound()
{ {
return boundBuffer == bufferID; return boundBuffer == bufferID;
} }
TSE::GLFW::VertexBuffer::~VertexBuffer() TSE::OpenGL::VertexBuffer::~VertexBuffer()
{ {
if(bufferID != 0) if(bufferID != 0)
{ {

View File

@@ -2,7 +2,7 @@
#include "buffer.hpp" #include "buffer.hpp"
namespace TSE::GLFW namespace TSE::OpenGL
{ {
class VertexBuffer : public buffer class VertexBuffer : public buffer
{ {

View File

@@ -4,7 +4,7 @@
#include "GL/gl.h" #include "GL/gl.h"
#include "Types.hpp" #include "Types.hpp"
namespace TSE::GLFW namespace TSE::OpenGL
{ {
class buffer class buffer
{ {

View File

@@ -3,58 +3,63 @@
#include "Shader.hpp" #include "Shader.hpp"
#include "Debug.hpp" #include "Debug.hpp"
TSE::uint TSE::GLFW::Shader::activeProgramID = 0; TSE::uint TSE::OpenGL::Shader::activeProgramID = 0;
void TSE::GLFW::Shader::Bind() const void TSE::OpenGL::Shader::Bind() const
{ {
Enable(true); Enable(true);
} }
void TSE::GLFW::Shader::Unbind() const void TSE::OpenGL::Shader::Unbind() const
{ {
Disable(true); Disable(true);
} }
void TSE::GLFW::Shader::Enable(bool notify) const void TSE::OpenGL::Shader::Enable(bool notify) const
{ {
activeProgramID = programID; activeProgramID = programID;
glUseProgram(programID); glUseProgram(programID);
if(notify) OnEnable(); if(notify) OnEnable();
} }
void TSE::GLFW::Shader::Disable(bool notify) const void TSE::OpenGL::Shader::Disable(bool notify) const
{ {
activeProgramID = 0; activeProgramID = 0;
glUseProgram(0); glUseProgram(0);
if(notify) OnDisable(); if(notify) OnDisable();
} }
void TSE::GLFW::Shader::Flush() void TSE::OpenGL::Shader::Flush()
{ {
OnFlush(); OnFlush();
} }
void TSE::GLFW::Shader::DrawCall(int indexCount) void TSE::OpenGL::Shader::DrawCall(int indexCount)
{ {
OnDrawCall(indexCount); OnDrawCall(indexCount);
} }
void TSE::GLFW::Shader::Submit(const Transformable &t, float *&target, TransformationStack &stack, void (*restartDrawcall)(IRenderer &), IRenderer &rnd) void TSE::OpenGL::Shader::PostDraw()
{
OnPostDraw();
}
void TSE::OpenGL::Shader::Submit(const Transformable &t, float *&target, TransformationStack &stack, void (*restartDrawcall)(IRenderer &), IRenderer &rnd)
{ {
OnSubmit(t, target, stack, restartDrawcall, rnd); OnSubmit(t, target, stack, restartDrawcall, rnd);
} }
bool TSE::GLFW::Shader::IsEnabled() const bool TSE::OpenGL::Shader::IsEnabled() const
{ {
return programID == activeProgramID; return programID == activeProgramID;
} }
int TSE::GLFW::Shader::packageSize() int TSE::OpenGL::Shader::packageSize()
{ {
return PackageSize; return PackageSize;
} }
TSE::GLFW::Shader::Shader(const std::vector<std::unique_ptr<ShaderPart>> &parts) TSE::OpenGL::Shader::Shader(const std::vector<std::unique_ptr<ShaderPart>> &parts)
{ {
programID = glCreateProgram(); programID = glCreateProgram();
@@ -80,12 +85,12 @@ TSE::GLFW::Shader::Shader(const std::vector<std::unique_ptr<ShaderPart>> &parts)
} }
} }
TSE::GLFW::Shader::~Shader() TSE::OpenGL::Shader::~Shader()
{ {
glDeleteProgram(programID); glDeleteProgram(programID);
} }
int TSE::GLFW::Shader::GetUniformLocation(const char *name) int TSE::OpenGL::Shader::GetUniformLocation(const char *name)
{ {
auto it = uniformLocations.find(name); auto it = uniformLocations.find(name);
if (it != uniformLocations.end()) return it->second; if (it != uniformLocations.end()) return it->second;
@@ -95,44 +100,44 @@ int TSE::GLFW::Shader::GetUniformLocation(const char *name)
return loc; return loc;
} }
void TSE::GLFW::Shader::SetUniform(const char *name, int value) void TSE::OpenGL::Shader::SetUniform(const char *name, int value)
{ {
glUniform1i(GetUniformLocation(name), value); glUniform1i(GetUniformLocation(name), value);
} }
void TSE::GLFW::Shader::SetUniform(const char *name, const int *value, const int count) void TSE::OpenGL::Shader::SetUniform(const char *name, const int *value, const int count)
{ {
glUniform1iv(GetUniformLocation(name), count, value); glUniform1iv(GetUniformLocation(name), count, value);
} }
void TSE::GLFW::Shader::SetUniform(const char *name, const Matrix4x4 *value) void TSE::OpenGL::Shader::SetUniform(const char *name, const Matrix4x4 *value)
{ {
float colmbMajor[16]; float colmbMajor[16];
value->ToArrayColumnMajor(colmbMajor); value->ToArrayColumnMajor(colmbMajor);
glUniformMatrix4fv(GetUniformLocation(name),1, false, colmbMajor); glUniformMatrix4fv(GetUniformLocation(name),1, false, colmbMajor);
} }
void TSE::GLFW::Shader::SetUniform(const char *name, float value) void TSE::OpenGL::Shader::SetUniform(const char *name, float value)
{ {
glUniform1f(GetUniformLocation(name), value); glUniform1f(GetUniformLocation(name), value);
} }
void TSE::GLFW::Shader::SetUniform(const char *name, const float *value, const int count) void TSE::OpenGL::Shader::SetUniform(const char *name, const float *value, const int count)
{ {
glUniform1fv(GetUniformLocation(name), count, value); glUniform1fv(GetUniformLocation(name), count, value);
} }
void TSE::GLFW::Shader::SetUniform(const char *name, const Vector2 *value) void TSE::OpenGL::Shader::SetUniform(const char *name, const Vector2 *value)
{ {
glUniform2f(GetUniformLocation(name), value->x, value->y); glUniform2f(GetUniformLocation(name), value->x, value->y);
} }
void TSE::GLFW::Shader::SetUniform(const char *name, const Vector3 *value) void TSE::OpenGL::Shader::SetUniform(const char *name, const Vector3 *value)
{ {
glUniform3f(GetUniformLocation(name), value->x, value->y, value->z); glUniform3f(GetUniformLocation(name), value->x, value->y, value->z);
} }
void TSE::GLFW::Shader::SetUniform(const char *name, const Vector4 *value) void TSE::OpenGL::Shader::SetUniform(const char *name, const Vector4 *value)
{ {
glUniform4f(GetUniformLocation(name), value->x, value->y, value->z, value->w); glUniform4f(GetUniformLocation(name), value->x, value->y, value->z, value->w);
} }

View File

@@ -9,7 +9,7 @@
#include "TransformationStack.hpp" #include "TransformationStack.hpp"
#include "interfaces/IRenderer.hpp" #include "interfaces/IRenderer.hpp"
namespace TSE::GLFW namespace TSE::OpenGL
{ {
class Shader : public IShader class Shader : public IShader
{ {
@@ -24,6 +24,7 @@ namespace TSE::GLFW
virtual void OnDisable() const {}; virtual void OnDisable() const {};
virtual void OnFlush() {}; virtual void OnFlush() {};
virtual void OnDrawCall(int indexCount) {}; virtual void OnDrawCall(int indexCount) {};
virtual void OnPostDraw() {};
virtual void OnSubmit(const Transformable& t, float*& target, TransformationStack& stack, void (*restartDrawcall)(IRenderer&), IRenderer& rnd) {}; virtual void OnSubmit(const Transformable& t, float*& target, TransformationStack& stack, void (*restartDrawcall)(IRenderer&), IRenderer& rnd) {};
public: public:
@@ -33,6 +34,7 @@ namespace TSE::GLFW
void Disable(bool notify = false) const; void Disable(bool notify = false) const;
void Flush(); void Flush();
void DrawCall(int indexCount); void DrawCall(int indexCount);
void PostDraw();
void Submit(const Transformable& t, float*& target, TransformationStack& stack, void (*restartDrawcall)(IRenderer&), IRenderer& rnd); void Submit(const Transformable& t, float*& target, TransformationStack& stack, void (*restartDrawcall)(IRenderer&), IRenderer& rnd);
bool IsEnabled() const; bool IsEnabled() const;
int packageSize(); int packageSize();
@@ -52,4 +54,4 @@ namespace TSE::GLFW
void SetUniform(const char* name, const Vector3* value) override; void SetUniform(const char* name, const Vector3* value) override;
void SetUniform(const char* name, const Vector4* value) override; void SetUniform(const char* name, const Vector4* value) override;
}; };
} // namespace TSE::GLFW } // namespace TSE::OpenGL

View File

@@ -6,7 +6,7 @@
#include <fstream> #include <fstream>
#include "PathHelper.hpp" #include "PathHelper.hpp"
void TSE::GLFW::ShaderPart::Init(const string &str, int shaderType) void TSE::OpenGL::ShaderPart::Init(const string &str, int shaderType)
{ {
shaderPartID = glCreateShader(shaderType); shaderPartID = glCreateShader(shaderType);
const char * cstr = str.c_str(); const char * cstr = str.c_str();
@@ -27,12 +27,12 @@ void TSE::GLFW::ShaderPart::Init(const string &str, int shaderType)
} }
} }
TSE::GLFW::ShaderPart::~ShaderPart() TSE::OpenGL::ShaderPart::~ShaderPart()
{ {
glDeleteShader(shaderPartID); glDeleteShader(shaderPartID);
} }
std::unique_ptr<TSE::GLFW::ShaderPart> TSE::GLFW::ShaderPart::LoadFromString(const std::string &str, int shaderType) std::unique_ptr<TSE::OpenGL::ShaderPart> TSE::OpenGL::ShaderPart::LoadFromString(const std::string &str, int shaderType)
{ {
if (str.length() == 0) throw; if (str.length() == 0) throw;
std::unique_ptr<ShaderPart> shader = std::make_unique<ShaderPart>(); std::unique_ptr<ShaderPart> shader = std::make_unique<ShaderPart>();
@@ -40,7 +40,7 @@ std::unique_ptr<TSE::GLFW::ShaderPart> TSE::GLFW::ShaderPart::LoadFromString(con
return shader; return shader;
} }
std::unique_ptr<TSE::GLFW::ShaderPart> TSE::GLFW::ShaderPart::LoadFromPath(const std::string &path, int shaderType) std::unique_ptr<TSE::OpenGL::ShaderPart> TSE::OpenGL::ShaderPart::LoadFromPath(const std::string &path, int shaderType)
{ {
std::ifstream stream; std::ifstream stream;
OpenFileReading(stream, path); OpenFileReading(stream, path);

View File

@@ -3,7 +3,7 @@
#include "Types.hpp" #include "Types.hpp"
#include <memory> #include <memory>
namespace TSE::GLFW namespace TSE::OpenGL
{ {
class ShaderPart class ShaderPart
{ {
@@ -20,4 +20,4 @@ namespace TSE::GLFW
static std::unique_ptr<ShaderPart> LoadFromString(const std::string& str, int shaderType); static std::unique_ptr<ShaderPart> LoadFromString(const std::string& str, int shaderType);
static std::unique_ptr<ShaderPart> LoadFromPath(const std::string& path, int shaderType); static std::unique_ptr<ShaderPart> LoadFromPath(const std::string& path, int shaderType);
}; };
} // namespace TSE } // namespace OpenGL

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::OpenGL;
#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::OpenGL::BasicOrderedSpriteSetShader* BasicOrderedSpriteSetShader::instance = nullptr;
TSE::OpenGL::BasicOrderedSpriteSetShader *TSE::OpenGL::BasicOrderedSpriteSetShader::Instance()
{
return instance;
}
void TSE::OpenGL::BasicOrderedSpriteSetShader::Destroy()
{
if(instance != nullptr)
delete instance;
instance = nullptr;
}
void TSE::OpenGL::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::OpenGL::BasicOrderedSpriteSetShader::BasicOrderedSpriteSetShader(std::vector<std::unique_ptr<ShaderPart>> &&parts) : Shader(parts)
{
PackageSize = SHADER_PACKAGE_SIZE;
}
TSE::OpenGL::BasicOrderedSpriteSetShader::~BasicOrderedSpriteSetShader()
{
if (meshVBO) glDeleteBuffers(1, &meshVBO);
if (meshIBO) glDeleteBuffers(1, &meshIBO);
}
void TSE::OpenGL::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::OpenGL::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::OpenGL::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::OpenGL::BasicOrderedSpriteSetShader::OnFlush()
{
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, TextureID);
glDisable(GL_BLEND);
}
void TSE::OpenGL::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::OpenGL::BasicOrderedSpriteSetShader::OnPostDraw()
{
glEnable(GL_BLEND);
instanceCount = 0;
}
void TSE::OpenGL::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::OpenGL
{
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

@@ -12,21 +12,21 @@
#define SHADER_PACKAGE_SIZE sizeof(float) * (3 + 1 + 1 + 4) #define SHADER_PACKAGE_SIZE sizeof(float) * (3 + 1 + 1 + 4)
TSE::GLFW::BasicParticleShader* TSE::GLFW::BasicParticleShader::instance = nullptr; TSE::OpenGL::BasicParticleShader* TSE::OpenGL::BasicParticleShader::instance = nullptr;
TSE::GLFW::BasicParticleShader *TSE::GLFW::BasicParticleShader::Instance() TSE::OpenGL::BasicParticleShader *TSE::OpenGL::BasicParticleShader::Instance()
{ {
return instance; return instance;
} }
void TSE::GLFW::BasicParticleShader::Destroy() void TSE::OpenGL::BasicParticleShader::Destroy()
{ {
if(instance != nullptr) if(instance != nullptr)
delete instance; delete instance;
instance = nullptr; instance = nullptr;
} }
void TSE::GLFW::BasicParticleShader::Init(float width, float height) void TSE::OpenGL::BasicParticleShader::Init(float width, float height)
{ {
std::vector<std::unique_ptr<ShaderPart>> parts; std::vector<std::unique_ptr<ShaderPart>> parts;
parts.push_back(ShaderPart::LoadFromString(vertPart, GL_VERTEX_SHADER)); parts.push_back(ShaderPart::LoadFromString(vertPart, GL_VERTEX_SHADER));
@@ -34,18 +34,18 @@ void TSE::GLFW::BasicParticleShader::Init(float width, float height)
instance = new BasicParticleShader(std::move(parts)); instance = new BasicParticleShader(std::move(parts));
} }
TSE::GLFW::BasicParticleShader::BasicParticleShader(std::vector<std::unique_ptr<ShaderPart>> &&parts) : Shader(parts) TSE::OpenGL::BasicParticleShader::BasicParticleShader(std::vector<std::unique_ptr<ShaderPart>> &&parts) : Shader(parts)
{ {
PackageSize = SHADER_PACKAGE_SIZE; PackageSize = SHADER_PACKAGE_SIZE;
} }
TSE::GLFW::BasicParticleShader::~BasicParticleShader() TSE::OpenGL::BasicParticleShader::~BasicParticleShader()
{ {
if (meshVBO) glDeleteBuffers(1, &meshVBO); if (meshVBO) glDeleteBuffers(1, &meshVBO);
if (meshIBO) glDeleteBuffers(1, &meshIBO); if (meshIBO) glDeleteBuffers(1, &meshIBO);
} }
void TSE::GLFW::BasicParticleShader::SetMesh(const void *verts, int vertCount, int stride, int floatCountPerVertex, int posOffsetBytes, GLenum primitive, const void *indices, int indexCount, GLenum indexType) void TSE::OpenGL::BasicParticleShader::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; GLint prevVAO = 0, prevArrayBuffer = 0, prevElementBuffer = 0;
glGetIntegerv(GL_VERTEX_ARRAY_BINDING, &prevVAO); glGetIntegerv(GL_VERTEX_ARRAY_BINDING, &prevVAO);
@@ -86,7 +86,7 @@ void TSE::GLFW::BasicParticleShader::SetMesh(const void *verts, int vertCount, i
glBindVertexArray(prevVAO); glBindVertexArray(prevVAO);
} }
void TSE::GLFW::BasicParticleShader::OnEnable() const void TSE::OpenGL::BasicParticleShader::OnEnable() const
{ {
if (!meshReady) if (!meshReady)
{ {
@@ -128,7 +128,7 @@ void TSE::GLFW::BasicParticleShader::OnEnable() const
glVertexAttribDivisor(SHADER_COLOR_INDEX, 1); glVertexAttribDivisor(SHADER_COLOR_INDEX, 1);
} }
void TSE::GLFW::BasicParticleShader::OnDisable() const void TSE::OpenGL::BasicParticleShader::OnDisable() const
{ {
glDisableVertexAttribArray(SHADER_MESH_INDEX); glDisableVertexAttribArray(SHADER_MESH_INDEX);
glDisableVertexAttribArray(SHADER_POS_INDEX); glDisableVertexAttribArray(SHADER_POS_INDEX);
@@ -137,11 +137,11 @@ void TSE::GLFW::BasicParticleShader::OnDisable() const
glDisableVertexAttribArray(SHADER_COLOR_INDEX); glDisableVertexAttribArray(SHADER_COLOR_INDEX);
} }
void TSE::GLFW::BasicParticleShader::OnFlush() void TSE::OpenGL::BasicParticleShader::OnFlush()
{ {
} }
void TSE::GLFW::BasicParticleShader::OnDrawCall(int indexCount) void TSE::OpenGL::BasicParticleShader::OnDrawCall(int indexCount)
{ {
if (instanceCount <= 0) return; if (instanceCount <= 0) return;
@@ -163,7 +163,7 @@ void TSE::GLFW::BasicParticleShader::OnDrawCall(int indexCount)
instanceCount = 0; instanceCount = 0;
} }
void TSE::GLFW::BasicParticleShader::OnSubmit(const Transformable &t, float *&target, TransformationStack &stack, void (*restartDrawcall)(IRenderer &), IRenderer &rnd) void TSE::OpenGL::BasicParticleShader::OnSubmit(const Transformable &t, float *&target, TransformationStack &stack, void (*restartDrawcall)(IRenderer &), IRenderer &rnd)
{ {
auto* r = dynamic_cast<Renderable*>(t.GetBehaviourScript(RENDERABLE)); auto* r = dynamic_cast<Renderable*>(t.GetBehaviourScript(RENDERABLE));
if (!r) return; if (!r) return;

View File

@@ -5,7 +5,7 @@
#include "Shader.hpp" #include "Shader.hpp"
#include "Types.hpp" #include "Types.hpp"
namespace TSE::GLFW namespace TSE::OpenGL
{ {
class BasicParticleShader : public Shader class BasicParticleShader : public Shader
{ {

View File

@@ -11,21 +11,21 @@
#define SHADER_PACKAGE_SIZE sizeof(float) * (3 + 4) #define SHADER_PACKAGE_SIZE sizeof(float) * (3 + 4)
TSE::GLFW::BasicShader* TSE::GLFW::BasicShader::instance = nullptr; TSE::OpenGL::BasicShader* TSE::OpenGL::BasicShader::instance = nullptr;
TSE::GLFW::BasicShader *TSE::GLFW::BasicShader::Instance() TSE::OpenGL::BasicShader *TSE::OpenGL::BasicShader::Instance()
{ {
return instance; return instance;
} }
void TSE::GLFW::BasicShader::Destroy() void TSE::OpenGL::BasicShader::Destroy()
{ {
if(instance != nullptr) if(instance != nullptr)
delete instance; delete instance;
instance = nullptr; instance = nullptr;
} }
void TSE::GLFW::BasicShader::Init(float width, float height) void TSE::OpenGL::BasicShader::Init(float width, float height)
{ {
std::vector<std::unique_ptr<ShaderPart>> parts; std::vector<std::unique_ptr<ShaderPart>> parts;
parts.push_back(ShaderPart::LoadFromString(vert, GL_VERTEX_SHADER)); parts.push_back(ShaderPart::LoadFromString(vert, GL_VERTEX_SHADER));
@@ -33,12 +33,12 @@ void TSE::GLFW::BasicShader::Init(float width, float height)
instance = new BasicShader(std::move(parts)); instance = new BasicShader(std::move(parts));
} }
TSE::GLFW::BasicShader::BasicShader(std::vector<std::unique_ptr<ShaderPart>> &&parts) : Shader(parts) TSE::OpenGL::BasicShader::BasicShader(std::vector<std::unique_ptr<ShaderPart>> &&parts) : Shader(parts)
{ {
PackageSize = SHADER_PACKAGE_SIZE; PackageSize = SHADER_PACKAGE_SIZE;
} }
void TSE::GLFW::BasicShader::OnEnable() const void TSE::OpenGL::BasicShader::OnEnable() const
{ {
glEnableVertexAttribArray(SHADER_VERTEX_INDEX); glEnableVertexAttribArray(SHADER_VERTEX_INDEX);
glVertexAttribPointer(SHADER_VERTEX_INDEX, 3, GL_FLOAT, false, SHADER_PACKAGE_SIZE, (void*)0); glVertexAttribPointer(SHADER_VERTEX_INDEX, 3, GL_FLOAT, false, SHADER_PACKAGE_SIZE, (void*)0);
@@ -46,22 +46,22 @@ void TSE::GLFW::BasicShader::OnEnable() const
glVertexAttribPointer(SHADER_COLOR_INDEX, 4, GL_FLOAT, false, SHADER_PACKAGE_SIZE, (void*)(sizeof(float) * 3)); glVertexAttribPointer(SHADER_COLOR_INDEX, 4, GL_FLOAT, false, SHADER_PACKAGE_SIZE, (void*)(sizeof(float) * 3));
} }
void TSE::GLFW::BasicShader::OnDisable() const void TSE::OpenGL::BasicShader::OnDisable() const
{ {
glDisableVertexAttribArray(SHADER_VERTEX_INDEX); glDisableVertexAttribArray(SHADER_VERTEX_INDEX);
glDisableVertexAttribArray(SHADER_COLOR_INDEX); glDisableVertexAttribArray(SHADER_COLOR_INDEX);
} }
void TSE::GLFW::BasicShader::OnFlush() void TSE::OpenGL::BasicShader::OnFlush()
{ {
} }
void TSE::GLFW::BasicShader::OnDrawCall(int indexCount) void TSE::OpenGL::BasicShader::OnDrawCall(int indexCount)
{ {
glDrawElements(GL_TRIANGLES, indexCount, GL_UNSIGNED_SHORT, NULL); glDrawElements(GL_TRIANGLES, indexCount, GL_UNSIGNED_SHORT, NULL);
} }
void TSE::GLFW::BasicShader::OnSubmit(const Transformable &t, float *&target, TransformationStack &stack, void (*restartDrawcall)(IRenderer &), IRenderer &rnd) void TSE::OpenGL::BasicShader::OnSubmit(const Transformable &t, float *&target, TransformationStack &stack, void (*restartDrawcall)(IRenderer &), IRenderer &rnd)
{ {
auto* r = dynamic_cast<Renderable*>(t.GetBehaviourScript(RENDERABLE)); auto* r = dynamic_cast<Renderable*>(t.GetBehaviourScript(RENDERABLE));
if (!r) return; if (!r) return;

View File

@@ -2,7 +2,7 @@
#include "Shader.hpp" #include "Shader.hpp"
namespace TSE::GLFW namespace TSE::OpenGL
{ {
class BasicShader : public Shader class BasicShader : public Shader
{ {
@@ -22,4 +22,4 @@ namespace TSE::GLFW
void OnDrawCall(int indexCount) override; void OnDrawCall(int indexCount) override;
void OnSubmit(const Transformable& t, float*& target, TransformationStack& stack, void (*restartDrawcall)(IRenderer&), IRenderer& rnd) override; void OnSubmit(const Transformable& t, float*& target, TransformationStack& stack, void (*restartDrawcall)(IRenderer&), IRenderer& rnd) override;
}; };
} // namespace TSE::GLFW } // namespace TSE::OpenGL

View File

@@ -13,22 +13,22 @@
#define SHADER_PACKAGE_SIZE (sizeof(float) * (3 + 4 + 2 + 1)) #define SHADER_PACKAGE_SIZE (sizeof(float) * (3 + 4 + 2 + 1))
TSE::GLFW::BasicTextureShader* TSE::GLFW::BasicTextureShader::instance = nullptr; TSE::OpenGL::BasicTextureShader* TSE::OpenGL::BasicTextureShader::instance = nullptr;
std::map<TSE::uint, float> TSE::GLFW::BasicTextureShader::textureSlots; std::map<TSE::uint, float> TSE::OpenGL::BasicTextureShader::textureSlots;
TSE::GLFW::BasicTextureShader *TSE::GLFW::BasicTextureShader::Instance() TSE::OpenGL::BasicTextureShader *TSE::OpenGL::BasicTextureShader::Instance()
{ {
return instance; return instance;
} }
void TSE::GLFW::BasicTextureShader::Destroy() void TSE::OpenGL::BasicTextureShader::Destroy()
{ {
if(instance != nullptr) if(instance != nullptr)
delete instance; delete instance;
instance = nullptr; instance = nullptr;
} }
void TSE::GLFW::BasicTextureShader::Init(float width, float height) void TSE::OpenGL::BasicTextureShader::Init(float width, float height)
{ {
std::vector<std::unique_ptr<ShaderPart>> parts; std::vector<std::unique_ptr<ShaderPart>> parts;
parts.push_back(ShaderPart::LoadFromString(vertT, GL_VERTEX_SHADER)); parts.push_back(ShaderPart::LoadFromString(vertT, GL_VERTEX_SHADER));
@@ -41,12 +41,12 @@ void TSE::GLFW::BasicTextureShader::Init(float width, float height)
instance->Disable(); instance->Disable();
} }
TSE::GLFW::BasicTextureShader::BasicTextureShader(std::vector<std::unique_ptr<ShaderPart>> &&parts) : Shader(parts) TSE::OpenGL::BasicTextureShader::BasicTextureShader(std::vector<std::unique_ptr<ShaderPart>> &&parts) : Shader(parts)
{ {
PackageSize = SHADER_PACKAGE_SIZE; PackageSize = SHADER_PACKAGE_SIZE;
} }
void TSE::GLFW::BasicTextureShader::OnEnable() const void TSE::OpenGL::BasicTextureShader::OnEnable() const
{ {
glEnableVertexAttribArray(SHADER_VERTEX_INDEX); glEnableVertexAttribArray(SHADER_VERTEX_INDEX);
glVertexAttribPointer(SHADER_VERTEX_INDEX, 3, GL_FLOAT, false, SHADER_PACKAGE_SIZE, (void*)0); glVertexAttribPointer(SHADER_VERTEX_INDEX, 3, GL_FLOAT, false, SHADER_PACKAGE_SIZE, (void*)0);
@@ -58,7 +58,7 @@ void TSE::GLFW::BasicTextureShader::OnEnable() const
glVertexAttribPointer(SHADER_TID_INDEX, 1, GL_FLOAT, false, SHADER_PACKAGE_SIZE, (void*)(sizeof(float) * 9)); glVertexAttribPointer(SHADER_TID_INDEX, 1, GL_FLOAT, false, SHADER_PACKAGE_SIZE, (void*)(sizeof(float) * 9));
} }
void TSE::GLFW::BasicTextureShader::OnDisable() const void TSE::OpenGL::BasicTextureShader::OnDisable() const
{ {
glDisableVertexAttribArray(SHADER_VERTEX_INDEX); glDisableVertexAttribArray(SHADER_VERTEX_INDEX);
glDisableVertexAttribArray(SHADER_COLOR_INDEX); glDisableVertexAttribArray(SHADER_COLOR_INDEX);
@@ -66,7 +66,7 @@ void TSE::GLFW::BasicTextureShader::OnDisable() const
glDisableVertexAttribArray(SHADER_TID_INDEX); glDisableVertexAttribArray(SHADER_TID_INDEX);
} }
void TSE::GLFW::BasicTextureShader::OnFlush() void TSE::OpenGL::BasicTextureShader::OnFlush()
{ {
auto it = textureSlots.begin(); auto it = textureSlots.begin();
for (int i = 0; i < textureSlots.size(); i++, it++) for (int i = 0; i < textureSlots.size(); i++, it++)
@@ -76,12 +76,12 @@ void TSE::GLFW::BasicTextureShader::OnFlush()
} }
} }
void TSE::GLFW::BasicTextureShader::OnDrawCall(int indexCount) void TSE::OpenGL::BasicTextureShader::OnDrawCall(int indexCount)
{ {
glDrawElements(GL_TRIANGLES, indexCount, GL_UNSIGNED_SHORT, NULL); glDrawElements(GL_TRIANGLES, indexCount, GL_UNSIGNED_SHORT, NULL);
} }
void TSE::GLFW::BasicTextureShader::OnSubmit(const Transformable &t, float *&target, TransformationStack &stack, void (*restartDrawcall)(IRenderer &), IRenderer &rnd) void TSE::OpenGL::BasicTextureShader::OnSubmit(const Transformable &t, float *&target, TransformationStack &stack, void (*restartDrawcall)(IRenderer &), IRenderer &rnd)
{ {
auto* r = dynamic_cast<Renderable*>(t.GetBehaviourScript(RENDERABLE)); auto* r = dynamic_cast<Renderable*>(t.GetBehaviourScript(RENDERABLE));
if (!r) return; if (!r) return;

View File

@@ -4,7 +4,7 @@
#include "Types.hpp" #include "Types.hpp"
#include <map> #include <map>
namespace TSE::GLFW namespace TSE::OpenGL
{ {
class BasicTextureShader : public Shader class BasicTextureShader : public Shader
{ {
@@ -25,4 +25,4 @@ namespace TSE::GLFW
void OnDrawCall(int indexCount) override; void OnDrawCall(int indexCount) override;
void OnSubmit(const Transformable& t, float*& target, TransformationStack& stack, void (*restartDrawcall)(IRenderer&), IRenderer& rnd) override; void OnSubmit(const Transformable& t, float*& target, TransformationStack& stack, void (*restartDrawcall)(IRenderer&), IRenderer& rnd) override;
}; };
} // namespace TSE::GLFW } // namespace TSE::OpenGL

View File

@@ -10,21 +10,21 @@
#define SHADER_PACKAGE_SIZE sizeof(float) * (3 + 1) #define SHADER_PACKAGE_SIZE sizeof(float) * (3 + 1)
TSE::GLFW::BasicTileMapShader* TSE::GLFW::BasicTileMapShader::instance = nullptr; TSE::OpenGL::BasicTileMapShader* TSE::OpenGL::BasicTileMapShader::instance = nullptr;
TSE::GLFW::BasicTileMapShader *TSE::GLFW::BasicTileMapShader::Instance() TSE::OpenGL::BasicTileMapShader *TSE::OpenGL::BasicTileMapShader::Instance()
{ {
return instance; return instance;
} }
void TSE::GLFW::BasicTileMapShader::Destroy() void TSE::OpenGL::BasicTileMapShader::Destroy()
{ {
if(instance != nullptr) if(instance != nullptr)
delete instance; delete instance;
instance = nullptr; instance = nullptr;
} }
void TSE::GLFW::BasicTileMapShader::Init(float width, float height) void TSE::OpenGL::BasicTileMapShader::Init(float width, float height)
{ {
std::vector<std::unique_ptr<ShaderPart>> parts; std::vector<std::unique_ptr<ShaderPart>> parts;
parts.push_back(ShaderPart::LoadFromString(vertTile, GL_VERTEX_SHADER)); parts.push_back(ShaderPart::LoadFromString(vertTile, GL_VERTEX_SHADER));
@@ -37,18 +37,18 @@ void TSE::GLFW::BasicTileMapShader::Init(float width, float height)
instance->Disable(); instance->Disable();
} }
TSE::GLFW::BasicTileMapShader::BasicTileMapShader(std::vector<std::unique_ptr<ShaderPart>> &&parts) : Shader(parts) TSE::OpenGL::BasicTileMapShader::BasicTileMapShader(std::vector<std::unique_ptr<ShaderPart>> &&parts) : Shader(parts)
{ {
PackageSize = SHADER_PACKAGE_SIZE; PackageSize = SHADER_PACKAGE_SIZE;
} }
TSE::GLFW::BasicTileMapShader::~BasicTileMapShader() TSE::OpenGL::BasicTileMapShader::~BasicTileMapShader()
{ {
if (meshVBO) glDeleteBuffers(1, &meshVBO); if (meshVBO) glDeleteBuffers(1, &meshVBO);
if (meshIBO) glDeleteBuffers(1, &meshIBO); if (meshIBO) glDeleteBuffers(1, &meshIBO);
} }
void TSE::GLFW::BasicTileMapShader::SetMesh(const void *verts, int vertCount, int stride, int floatCountPerVertex, int posOffsetBytes, GLenum primitive, const void *indices, int indexCount, GLenum indexType) void TSE::OpenGL::BasicTileMapShader::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; GLint prevVAO = 0, prevArrayBuffer = 0, prevElementBuffer = 0;
glGetIntegerv(GL_VERTEX_ARRAY_BINDING, &prevVAO); glGetIntegerv(GL_VERTEX_ARRAY_BINDING, &prevVAO);
@@ -89,7 +89,7 @@ void TSE::GLFW::BasicTileMapShader::SetMesh(const void *verts, int vertCount, in
glBindVertexArray(prevVAO); glBindVertexArray(prevVAO);
} }
void TSE::GLFW::BasicTileMapShader::OnEnable() const void TSE::OpenGL::BasicTileMapShader::OnEnable() const
{ {
if (!meshReady) if (!meshReady)
{ {
@@ -121,20 +121,20 @@ void TSE::GLFW::BasicTileMapShader::OnEnable() const
glVertexAttribDivisor(SHADER_SPRITE_INDEX, 1); glVertexAttribDivisor(SHADER_SPRITE_INDEX, 1);
} }
void TSE::GLFW::BasicTileMapShader::OnDisable() const void TSE::OpenGL::BasicTileMapShader::OnDisable() const
{ {
glDisableVertexAttribArray(SHADER_MESH_INDEX); glDisableVertexAttribArray(SHADER_MESH_INDEX);
glDisableVertexAttribArray(SHADER_POS_INDEX); glDisableVertexAttribArray(SHADER_POS_INDEX);
glDisableVertexAttribArray(SHADER_SPRITE_INDEX); glDisableVertexAttribArray(SHADER_SPRITE_INDEX);
} }
void TSE::GLFW::BasicTileMapShader::OnFlush() void TSE::OpenGL::BasicTileMapShader::OnFlush()
{ {
glActiveTexture(GL_TEXTURE0); glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, TextureID); glBindTexture(GL_TEXTURE_2D, TextureID);
} }
void TSE::GLFW::BasicTileMapShader::OnDrawCall(int indexCount) void TSE::OpenGL::BasicTileMapShader::OnDrawCall(int indexCount)
{ {
if (instanceCount <= 0) return; if (instanceCount <= 0) return;
SetUniform("spriteCount", &SpriteCount); SetUniform("spriteCount", &SpriteCount);
@@ -158,7 +158,7 @@ void TSE::GLFW::BasicTileMapShader::OnDrawCall(int indexCount)
instanceCount = 0; instanceCount = 0;
} }
void TSE::GLFW::BasicTileMapShader::OnSubmit(const Transformable &t, float *&target, TransformationStack &stack, void (*restartDrawcall)(IRenderer &), IRenderer &rnd) void TSE::OpenGL::BasicTileMapShader::OnSubmit(const Transformable &t, float *&target, TransformationStack &stack, void (*restartDrawcall)(IRenderer &), IRenderer &rnd)
{ {
auto* r = dynamic_cast<Renderable*>(t.GetBehaviourScript(RENDERABLE)); auto* r = dynamic_cast<Renderable*>(t.GetBehaviourScript(RENDERABLE));
if (!r) return; if (!r) return;
@@ -171,9 +171,7 @@ void TSE::GLFW::BasicTileMapShader::OnSubmit(const Transformable &t, float *&tar
SpriteCount = tileSet->GetCount(); SpriteCount = tileSet->GetCount();
SpriteScale = tm->SpriteScale; SpriteScale = tm->SpriteScale;
const int chunkcount = tm->GetChunkCount(); const std::vector<Vector2> orderedChunks = *tm->GetChunkPositionsInOrder();
Vector2 orderedChunks[chunkcount];
tm->GetChunkPositionsInOrder(orderedChunks);
Matrix4x4 matr = t.GetLocalMatrix(); Matrix4x4 matr = t.GetLocalMatrix();
@@ -183,10 +181,8 @@ void TSE::GLFW::BasicTileMapShader::OnSubmit(const Transformable &t, float *&tar
{ {
auto chunk = tm->GetChunk(chunkPos); auto chunk = tm->GetChunk(chunkPos);
const int spriteCount = chunk->GetSpriteCount(); const int spriteCount = chunk->GetSpriteCount();
Vector2 spritePositions[spriteCount]; const std::vector<Vector2> spritePositions = *chunk->GetOrderedPositions();
int spriteIds[spriteCount]; const std::vector<Vector2i> spriteIds = *chunk->GetOrderedSpriteIds();
chunk->GetOrderedPositions(spritePositions);
chunk->GetOrderedSpriteIds(spriteIds);
int chunkSize = chunk->GetChunksize(); int chunkSize = chunk->GetChunksize();
for (int i = 0; i < spriteCount; i++) for (int i = 0; i < spriteCount; i++)
@@ -198,7 +194,7 @@ void TSE::GLFW::BasicTileMapShader::OnSubmit(const Transformable &t, float *&tar
*target++ = pos.x; *target++ = pos.x;
*target++ = pos.y; *target++ = pos.y;
*target++ = pos.z; *target++ = pos.z;
*target++ = spriteIds[i]; *target++ = spriteIds[i].x;
++instanceCount; ++instanceCount;
stack.Pop(); stack.Pop();

View File

@@ -5,7 +5,7 @@
#include "Shader.hpp" #include "Shader.hpp"
#include "Types.hpp" #include "Types.hpp"
namespace TSE::GLFW namespace TSE::OpenGL
{ {
class BasicTileMapShader : public Shader class BasicTileMapShader : public Shader
{ {
@@ -41,4 +41,4 @@ namespace TSE::GLFW
void OnDrawCall(int indexCount) override; void OnDrawCall(int indexCount) override;
void OnSubmit(const Transformable& t, float*& target, TransformationStack& stack, void (*restartDrawcall)(IRenderer&), IRenderer& rnd) override; void OnSubmit(const Transformable& t, float*& target, TransformationStack& stack, void (*restartDrawcall)(IRenderer&), IRenderer& rnd) override;
}; };
} // namespace TSE::GLFW } // namespace TSE::OpenGL

View File

@@ -4,32 +4,37 @@
#include "ditheringShader.hpp" #include "ditheringShader.hpp"
#include "basicParticleShader.hpp" #include "basicParticleShader.hpp"
#include "basicTileMapShader.hpp" #include "basicTileMapShader.hpp"
#include "basicOrderedSpriteSetShader.hpp"
#include "elements/ShaderRegistry.hpp" #include "elements/ShaderRegistry.hpp"
void TSE::GLFW::LoadBasicShaders(float width, float height) void TSE::OpenGL::LoadBasicShaders(float width, float height)
{ {
BasicShader::Init(width, height); BasicShader::Init(width, height);
BasicTextureShader::Init(width, height); BasicTextureShader::Init(width, height);
DitheringShader::Init(width, height); DitheringShader::Init(width, height);
BasicParticleShader::Init(width, height); BasicParticleShader::Init(width, height);
BasicTileMapShader::Init(width, height); BasicTileMapShader::Init(width, height);
BasicOrderedSpriteSetShader::Init(width, height);
ShaderRegistry::SetShader("Basic Unlit Shader", BasicShader::Instance()); ShaderRegistry::SetShader("Basic Unlit Shader", BasicShader::Instance());
ShaderRegistry::SetShader("Basic Unlit Texture Shader", BasicTextureShader::Instance()); ShaderRegistry::SetShader("Basic Unlit Texture Shader", BasicTextureShader::Instance());
ShaderRegistry::SetShader("Basic Unlit Dithering Shader", DitheringShader::Instance()); ShaderRegistry::SetShader("Basic Unlit Dithering Shader", DitheringShader::Instance());
ShaderRegistry::SetShader("Basic Unlit Particle Shader", BasicParticleShader::Instance()); ShaderRegistry::SetShader("Basic Unlit Particle Shader", BasicParticleShader::Instance());
ShaderRegistry::SetShader("Basic Unlit TileMap Shader", BasicTileMapShader::Instance()); ShaderRegistry::SetShader("Basic Unlit TileMap Shader", BasicTileMapShader::Instance());
ShaderRegistry::SetShader("Basic Ordered Sprite Set Shader", BasicOrderedSpriteSetShader::Instance());
} }
void TSE::GLFW::UnLoadBasicShaders() void TSE::OpenGL::UnLoadBasicShaders()
{ {
ShaderRegistry::RemoveShader("Basic Unlit Shader"); ShaderRegistry::RemoveShader("Basic Unlit Shader");
ShaderRegistry::RemoveShader("Basic Unlit Texture Shader"); ShaderRegistry::RemoveShader("Basic Unlit Texture Shader");
ShaderRegistry::RemoveShader("Basic Unlit Dithering Shader"); ShaderRegistry::RemoveShader("Basic Unlit Dithering Shader");
ShaderRegistry::RemoveShader("Basic Unlit Particle Shader"); ShaderRegistry::RemoveShader("Basic Unlit Particle Shader");
ShaderRegistry::RemoveShader("Basic Unlit TileMap Shader"); ShaderRegistry::RemoveShader("Basic Unlit TileMap Shader");
ShaderRegistry::RemoveShader("Basic Ordered Sprite Set Shader");
BasicShader::Destroy(); BasicShader::Destroy();
BasicTextureShader::Destroy(); BasicTextureShader::Destroy();
DitheringShader::Destroy(); DitheringShader::Destroy();
BasicParticleShader::Destroy(); BasicParticleShader::Destroy();
BasicTileMapShader::Destroy(); BasicTileMapShader::Destroy();
BasicOrderedSpriteSetShader::Destroy();
} }

View File

@@ -1,6 +1,6 @@
#pragma once #pragma once
namespace TSE::GLFW namespace TSE::OpenGL
{ {
void LoadBasicShaders(float width, float height); void LoadBasicShaders(float width, float height);
void UnLoadBasicShaders(); void UnLoadBasicShaders();

View File

@@ -10,21 +10,21 @@
#define SHADER_PACKAGE_SIZE sizeof(float) * (3 + 4) #define SHADER_PACKAGE_SIZE sizeof(float) * (3 + 4)
TSE::GLFW::DitheringShader* TSE::GLFW::DitheringShader::instance = nullptr; TSE::OpenGL::DitheringShader* TSE::OpenGL::DitheringShader::instance = nullptr;
TSE::GLFW::DitheringShader *TSE::GLFW::DitheringShader::Instance() TSE::OpenGL::DitheringShader *TSE::OpenGL::DitheringShader::Instance()
{ {
return instance; return instance;
} }
void TSE::GLFW::DitheringShader::Destroy() void TSE::OpenGL::DitheringShader::Destroy()
{ {
if(instance != nullptr) if(instance != nullptr)
delete instance; delete instance;
instance = nullptr; instance = nullptr;
} }
void TSE::GLFW::DitheringShader::Init(float width, float height) void TSE::OpenGL::DitheringShader::Init(float width, float height)
{ {
std::vector<std::unique_ptr<ShaderPart>> parts; std::vector<std::unique_ptr<ShaderPart>> parts;
parts.push_back(ShaderPart::LoadFromString(vertD, GL_VERTEX_SHADER)); parts.push_back(ShaderPart::LoadFromString(vertD, GL_VERTEX_SHADER));
@@ -32,12 +32,12 @@ void TSE::GLFW::DitheringShader::Init(float width, float height)
instance = new DitheringShader(std::move(parts)); instance = new DitheringShader(std::move(parts));
} }
TSE::GLFW::DitheringShader::DitheringShader(std::vector<std::unique_ptr<ShaderPart>> &&parts) : Shader(parts) TSE::OpenGL::DitheringShader::DitheringShader(std::vector<std::unique_ptr<ShaderPart>> &&parts) : Shader(parts)
{ {
PackageSize = SHADER_PACKAGE_SIZE; PackageSize = SHADER_PACKAGE_SIZE;
} }
void TSE::GLFW::DitheringShader::OnEnable() const void TSE::OpenGL::DitheringShader::OnEnable() const
{ {
glEnableVertexAttribArray(SHADER_VERTEX_INDEX); glEnableVertexAttribArray(SHADER_VERTEX_INDEX);
glVertexAttribPointer(SHADER_VERTEX_INDEX, 3, GL_FLOAT, false, SHADER_PACKAGE_SIZE, (void*)0); glVertexAttribPointer(SHADER_VERTEX_INDEX, 3, GL_FLOAT, false, SHADER_PACKAGE_SIZE, (void*)0);
@@ -45,22 +45,22 @@ void TSE::GLFW::DitheringShader::OnEnable() const
glVertexAttribPointer(SHADER_COLOR_INDEX, 4, GL_FLOAT, false, SHADER_PACKAGE_SIZE, (void*)(sizeof(float) * 3)); glVertexAttribPointer(SHADER_COLOR_INDEX, 4, GL_FLOAT, false, SHADER_PACKAGE_SIZE, (void*)(sizeof(float) * 3));
} }
void TSE::GLFW::DitheringShader::OnDisable() const void TSE::OpenGL::DitheringShader::OnDisable() const
{ {
glDisableVertexAttribArray(SHADER_VERTEX_INDEX); glDisableVertexAttribArray(SHADER_VERTEX_INDEX);
glDisableVertexAttribArray(SHADER_COLOR_INDEX); glDisableVertexAttribArray(SHADER_COLOR_INDEX);
} }
void TSE::GLFW::DitheringShader::OnFlush() void TSE::OpenGL::DitheringShader::OnFlush()
{ {
} }
void TSE::GLFW::DitheringShader::OnDrawCall(int indexCount) void TSE::OpenGL::DitheringShader::OnDrawCall(int indexCount)
{ {
glDrawElements(GL_TRIANGLES, indexCount, GL_UNSIGNED_SHORT, NULL); glDrawElements(GL_TRIANGLES, indexCount, GL_UNSIGNED_SHORT, NULL);
} }
void TSE::GLFW::DitheringShader::OnSubmit(const Transformable &t, float *&target, TransformationStack &stack, void (*restartDrawcall)(IRenderer &), IRenderer &rnd) void TSE::OpenGL::DitheringShader::OnSubmit(const Transformable &t, float *&target, TransformationStack &stack, void (*restartDrawcall)(IRenderer &), IRenderer &rnd)
{ {
auto* r = dynamic_cast<Renderable*>(t.GetBehaviourScript(RENDERABLE)); auto* r = dynamic_cast<Renderable*>(t.GetBehaviourScript(RENDERABLE));
if (!r) return; if (!r) return;

View File

@@ -2,7 +2,7 @@
#include "Shader.hpp" #include "Shader.hpp"
namespace TSE::GLFW namespace TSE::OpenGL
{ {
class DitheringShader : public Shader class DitheringShader : public Shader
{ {
@@ -22,4 +22,4 @@ namespace TSE::GLFW
void OnDrawCall(int indexCount) override; void OnDrawCall(int indexCount) override;
void OnSubmit(const Transformable& t, float*& target, TransformationStack& stack, void (*restartDrawcall)(IRenderer&), IRenderer& rnd) override; void OnSubmit(const Transformable& t, float*& target, TransformationStack& stack, void (*restartDrawcall)(IRenderer&), IRenderer& rnd) override;
}; };
} // namespace TSE::GLFW } // namespace TSE::OpenGL

View File

@@ -2,7 +2,7 @@
cmake_minimum_required(VERSION 3.31) cmake_minimum_required(VERSION 3.31)
#project name #project name
project(TSE_GlfwOpenGlImpl) project(TSE_Sdl3Impl)
#cpp settings #cpp settings
find_program(CLANG_C NAMES clang) find_program(CLANG_C NAMES clang)
@@ -46,16 +46,14 @@ include_directories(${PROJECT_SOURCE_DIR}/../TSE_Base/include)
include_directories(${PROJECT_SOURCE_DIR}/../TSE_Math/src) include_directories(${PROJECT_SOURCE_DIR}/../TSE_Math/src)
include_directories(${PROJECT_SOURCE_DIR}/../TSE_Core/src) include_directories(${PROJECT_SOURCE_DIR}/../TSE_Core/src)
include_directories(${PROJECT_SOURCE_DIR}/../TSE_Core/include) include_directories(${PROJECT_SOURCE_DIR}/../TSE_Core/include)
include_directories(${PROJECT_SOURCE_DIR}/../TSE_GlfwImpl/src)
include_directories(${PROJECT_SOURCE_DIR}/../TSE_GlfwImpl/include)
#project def #project def
if(Lib) if(Lib)
add_library(TSE_GlfwOpenGlImpl SHARED ${CPP_SOURCE_TSE}) add_library(TSE_Sdl3Impl SHARED ${CPP_SOURCE_TSE})
else() else()
add_library(TSE_GlfwOpenGlImpl STATIC ${CPP_SOURCE_TSE}) add_library(TSE_Sdl3Impl STATIC ${CPP_SOURCE_TSE})
endif() endif()
#flags #flags
target_compile_options(TSE_GlfwOpenGlImpl PRIVATE -march=native) target_compile_options(TSE_Sdl3Impl PRIVATE -march=native)

Binary file not shown.

View File

@@ -0,0 +1,91 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2026 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
/**
* Main include header for the SDL library, version 3.4.2
*
* It is almost always best to include just this one header instead of
* picking out individual headers included here. There are exceptions to
* this rule--SDL_main.h is special and not included here--but usually
* letting SDL.h include the kitchen sink for you is the correct approach.
*/
#ifndef SDL_h_
#define SDL_h_
#include <SDL3/SDL_stdinc.h>
#include <SDL3/SDL_assert.h>
#include <SDL3/SDL_asyncio.h>
#include <SDL3/SDL_atomic.h>
#include <SDL3/SDL_audio.h>
#include <SDL3/SDL_bits.h>
#include <SDL3/SDL_blendmode.h>
#include <SDL3/SDL_camera.h>
#include <SDL3/SDL_clipboard.h>
#include <SDL3/SDL_cpuinfo.h>
#include <SDL3/SDL_dialog.h>
#include <SDL3/SDL_dlopennote.h>
#include <SDL3/SDL_endian.h>
#include <SDL3/SDL_error.h>
#include <SDL3/SDL_events.h>
#include <SDL3/SDL_filesystem.h>
#include <SDL3/SDL_gamepad.h>
#include <SDL3/SDL_gpu.h>
#include <SDL3/SDL_guid.h>
#include <SDL3/SDL_haptic.h>
#include <SDL3/SDL_hidapi.h>
#include <SDL3/SDL_hints.h>
#include <SDL3/SDL_init.h>
#include <SDL3/SDL_iostream.h>
#include <SDL3/SDL_joystick.h>
#include <SDL3/SDL_keyboard.h>
#include <SDL3/SDL_keycode.h>
#include <SDL3/SDL_loadso.h>
#include <SDL3/SDL_locale.h>
#include <SDL3/SDL_log.h>
#include <SDL3/SDL_messagebox.h>
#include <SDL3/SDL_metal.h>
#include <SDL3/SDL_misc.h>
#include <SDL3/SDL_mouse.h>
#include <SDL3/SDL_mutex.h>
#include <SDL3/SDL_pen.h>
#include <SDL3/SDL_pixels.h>
#include <SDL3/SDL_platform.h>
#include <SDL3/SDL_power.h>
#include <SDL3/SDL_process.h>
#include <SDL3/SDL_properties.h>
#include <SDL3/SDL_rect.h>
#include <SDL3/SDL_render.h>
#include <SDL3/SDL_scancode.h>
#include <SDL3/SDL_sensor.h>
#include <SDL3/SDL_storage.h>
#include <SDL3/SDL_surface.h>
#include <SDL3/SDL_system.h>
#include <SDL3/SDL_thread.h>
#include <SDL3/SDL_time.h>
#include <SDL3/SDL_timer.h>
#include <SDL3/SDL_tray.h>
#include <SDL3/SDL_touch.h>
#include <SDL3/SDL_version.h>
#include <SDL3/SDL_video.h>
#include <SDL3/SDL_oldnames.h>
#endif /* SDL_h_ */

View File

@@ -0,0 +1,697 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2026 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
/**
* # CategoryAssert
*
* A helpful assertion macro!
*
* SDL assertions operate like your usual `assert` macro, but with some added
* features:
*
* - It uses a trick with the `sizeof` operator, so disabled assertions
* vaporize out of the compiled code, but variables only referenced in the
* assertion won't trigger compiler warnings about being unused.
* - It is safe to use with a dangling-else: `if (x) SDL_assert(y); else
* do_something();`
* - It works the same everywhere, instead of counting on various platforms'
* compiler and C runtime to behave.
* - It provides multiple levels of assertion (SDL_assert, SDL_assert_release,
* SDL_assert_paranoid) instead of a single all-or-nothing option.
* - It offers a variety of responses when an assertion fails (retry, trigger
* the debugger, abort the program, ignore the failure once, ignore it for
* the rest of the program's run).
* - It tries to show the user a dialog by default, if possible, but the app
* can provide a callback to handle assertion failures however they like.
* - It lets failed assertions be retried. Perhaps you had a network failure
* and just want to retry the test after plugging your network cable back
* in? You can.
* - It lets the user ignore an assertion failure, if there's a harmless
* problem that one can continue past.
* - It lets the user mark an assertion as ignored for the rest of the
* program's run; if there's a harmless problem that keeps popping up.
* - It provides statistics and data on all failed assertions to the app.
* - It allows the default assertion handler to be controlled with environment
* variables, in case an automated script needs to control it.
* - It can be used as an aid to Clang's static analysis; it will treat SDL
* assertions as universally true (under the assumption that you are serious
* about the asserted claims and that your debug builds will detect when
* these claims were wrong). This can help the analyzer avoid false
* positives.
*
* To use it: compile a debug build and just sprinkle around tests to check
* your code!
*/
#ifndef SDL_assert_h_
#define SDL_assert_h_
#include <SDL3/SDL_stdinc.h>
#include <SDL3/SDL_begin_code.h>
/* Set up for C function definitions, even when using C++ */
#ifdef __cplusplus
extern "C" {
#endif
#ifdef SDL_WIKI_DOCUMENTATION_SECTION
/**
* The level of assertion aggressiveness.
*
* This value changes depending on compiler options and other preprocessor
* defines.
*
* It is currently one of the following values, but future SDL releases might
* add more:
*
* - 0: All SDL assertion macros are disabled.
* - 1: Release settings: SDL_assert disabled, SDL_assert_release enabled.
* - 2: Debug settings: SDL_assert and SDL_assert_release enabled.
* - 3: Paranoid settings: All SDL assertion macros enabled, including
* SDL_assert_paranoid.
*
* \since This macro is available since SDL 3.2.0.
*/
#define SDL_ASSERT_LEVEL SomeNumberBasedOnVariousFactors
#elif !defined(SDL_ASSERT_LEVEL)
#ifdef SDL_DEFAULT_ASSERT_LEVEL
#define SDL_ASSERT_LEVEL SDL_DEFAULT_ASSERT_LEVEL
#elif defined(_DEBUG) || defined(DEBUG) || \
(defined(__GNUC__) && !defined(__OPTIMIZE__))
#define SDL_ASSERT_LEVEL 2
#else
#define SDL_ASSERT_LEVEL 1
#endif
#endif
#ifdef SDL_WIKI_DOCUMENTATION_SECTION
/**
* Attempt to tell an attached debugger to pause.
*
* This allows an app to programmatically halt ("break") the debugger as if it
* had hit a breakpoint, allowing the developer to examine program state, etc.
*
* This is a macro--not a function--so that the debugger breaks on the source
* code line that used SDL_TriggerBreakpoint and not in some random guts of
* SDL. SDL_assert uses this macro for the same reason.
*
* If the program is not running under a debugger, SDL_TriggerBreakpoint will
* likely terminate the app, possibly without warning. If the current platform
* isn't supported, this macro is left undefined.
*
* \threadsafety It is safe to call this macro from any thread.
*
* \since This macro is available since SDL 3.2.0.
*/
#define SDL_TriggerBreakpoint() TriggerABreakpointInAPlatformSpecificManner
#elif defined(__MINGW32__) || (defined(_MSC_VER) && _MSC_VER >= 1310)
/* Don't include intrin.h here because it contains C++ code */
extern void __cdecl __debugbreak(void);
#define SDL_TriggerBreakpoint() __debugbreak()
#elif defined(_MSC_VER) && defined(_M_IX86)
#define SDL_TriggerBreakpoint() { _asm { int 0x03 } }
#elif SDL_HAS_BUILTIN(__builtin_debugtrap)
#define SDL_TriggerBreakpoint() __builtin_debugtrap()
#elif SDL_HAS_BUILTIN(__builtin_trap)
#define SDL_TriggerBreakpoint() __builtin_trap()
#elif (defined(__GNUC__) || defined(__clang__) || defined(__TINYC__) || defined(__slimcc__)) && (defined(__i386__) || defined(__x86_64__))
#define SDL_TriggerBreakpoint() __asm__ __volatile__ ( "int $3\n\t" )
#elif (defined(__GNUC__) || defined(__clang__)) && defined(__riscv)
#define SDL_TriggerBreakpoint() __asm__ __volatile__ ( "ebreak\n\t" )
#elif ( defined(SDL_PLATFORM_APPLE) && (defined(__arm64__) || defined(__aarch64__)) ) /* this might work on other ARM targets, but this is a known quantity... */
#define SDL_TriggerBreakpoint() __asm__ __volatile__ ( "brk #22\n\t" )
#elif defined(SDL_PLATFORM_APPLE) && defined(__arm__)
#define SDL_TriggerBreakpoint() __asm__ __volatile__ ( "bkpt #22\n\t" )
#elif defined(_WIN32) && ((defined(__GNUC__) || defined(__clang__)) && (defined(__arm64__) || defined(__aarch64__)) )
#define SDL_TriggerBreakpoint() __asm__ __volatile__ ( "brk #0xF000\n\t" )
#elif defined(__GNUC__) || defined(__clang__)
#define SDL_TriggerBreakpoint() __builtin_trap() /* older gcc may not support SDL_HAS_BUILTIN(__builtin_trap) above */
#elif defined(__386__) && defined(__WATCOMC__)
#define SDL_TriggerBreakpoint() { _asm { int 0x03 } }
#elif defined(HAVE_SIGNAL_H) && !defined(__WATCOMC__)
#include <signal.h>
#define SDL_TriggerBreakpoint() raise(SIGTRAP)
#else
/* SDL_TriggerBreakpoint is intentionally left undefined on unknown platforms. */
#endif
#ifdef SDL_WIKI_DOCUMENTATION_SECTION
/**
* A constant that contains the current function being compiled.
*
* If SDL can't figure how the compiler reports this, it will use "???".
*
* \since This macro is available since SDL 3.2.0.
*/
#define SDL_FUNCTION __FUNCTION__
#elif !defined(SDL_FUNCTION)
#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 supports __func__ as a standard. */
# define SDL_FUNCTION __func__
#elif ((defined(__GNUC__) && (__GNUC__ >= 2)) || defined(_MSC_VER) || defined (__WATCOMC__))
# define SDL_FUNCTION __FUNCTION__
#else
# define SDL_FUNCTION "???"
#endif
#endif
#ifdef SDL_WIKI_DOCUMENTATION_SECTION
/**
* A macro that reports the current file being compiled.
*
* This macro is only defined if it isn't already defined, so to override it
* (perhaps with something that doesn't provide path information at all, so
* build machine information doesn't leak into public binaries), apps can
* define this macro before including SDL.h or SDL_assert.h.
*
* \since This macro is available since SDL 3.2.0.
*/
#define SDL_FILE __FILE_NAME__
#elif !defined(SDL_FILE)
#ifdef __FILE_NAME__
#define SDL_FILE __FILE_NAME__
#else
#define SDL_FILE __FILE__
#endif
#endif
#ifdef SDL_WIKI_DOCUMENTATION_SECTION
/**
* A macro that reports the current file being compiled, for use in
* assertions.
*
* This macro is only defined if it isn't already defined, so to override it
* (perhaps with something that doesn't provide path information at all, so
* build machine information doesn't leak into public binaries), apps can
* define this macro before including SDL_assert.h. For example, defining this
* to `""` will make sure no source path information is included in asserts.
*
* \since This macro is available since SDL 3.4.0.
*/
#define SDL_ASSERT_FILE SDL_FILE
#elif !defined(SDL_ASSERT_FILE)
#define SDL_ASSERT_FILE SDL_FILE
#endif
/**
* A macro that reports the current line number of the file being compiled.
*
* \since This macro is available since SDL 3.2.0.
*/
#define SDL_LINE __LINE__
/*
sizeof (x) makes the compiler still parse the expression even without
assertions enabled, so the code is always checked at compile time, but
doesn't actually generate code for it, so there are no side effects or
expensive checks at run time, just the constant size of what x WOULD be,
which presumably gets optimized out as unused.
This also solves the problem of...
int somevalue = blah();
SDL_assert(somevalue == 1);
...which would cause compiles to complain that somevalue is unused if we
disable assertions.
*/
#ifdef SDL_WIKI_DOCUMENTATION_SECTION
/**
* A macro for wrapping code in `do {} while (0);` without compiler warnings.
*
* Visual Studio with really aggressive warnings enabled needs this to avoid
* compiler complaints.
*
* the `do {} while (0);` trick is useful for wrapping code in a macro that
* may or may not be a single statement, to avoid various C language
* accidents.
*
* To use:
*
* ```c
* do { SomethingOnce(); } while (SDL_NULL_WHILE_LOOP_CONDITION (0));
* ```
*
* \since This macro is available since SDL 3.2.0.
*/
#define SDL_NULL_WHILE_LOOP_CONDITION (0)
#elif defined(_MSC_VER) /* Avoid /W4 warnings. */
/* "while (0,0)" fools Microsoft's compiler's /W4 warning level into thinking
this condition isn't constant. And looks like an owl's face! */
#define SDL_NULL_WHILE_LOOP_CONDITION (0,0)
#else
#define SDL_NULL_WHILE_LOOP_CONDITION (0)
#endif
/**
* The macro used when an assertion is disabled.
*
* This isn't for direct use by apps, but this is the code that is inserted
* when an SDL_assert is disabled (perhaps in a release build).
*
* The code does nothing, but wraps `condition` in a sizeof operator, which
* generates no code and has no side effects, but avoid compiler warnings
* about unused variables.
*
* \param condition the condition to assert (but not actually run here).
*
* \since This macro is available since SDL 3.2.0.
*/
#define SDL_disabled_assert(condition) \
do { (void) sizeof ((condition)); } while (SDL_NULL_WHILE_LOOP_CONDITION)
/**
* Possible outcomes from a triggered assertion.
*
* When an enabled assertion triggers, it may call the assertion handler
* (possibly one provided by the app via SDL_SetAssertionHandler), which will
* return one of these values, possibly after asking the user.
*
* Then SDL will respond based on this outcome (loop around to retry the
* condition, try to break in a debugger, kill the program, or ignore the
* problem).
*
* \since This enum is available since SDL 3.2.0.
*/
typedef enum SDL_AssertState
{
SDL_ASSERTION_RETRY, /**< Retry the assert immediately. */
SDL_ASSERTION_BREAK, /**< Make the debugger trigger a breakpoint. */
SDL_ASSERTION_ABORT, /**< Terminate the program. */
SDL_ASSERTION_IGNORE, /**< Ignore the assert. */
SDL_ASSERTION_ALWAYS_IGNORE /**< Ignore the assert from now on. */
} SDL_AssertState;
/**
* Information about an assertion failure.
*
* This structure is filled in with information about a triggered assertion,
* used by the assertion handler, then added to the assertion report. This is
* returned as a linked list from SDL_GetAssertionReport().
*
* \since This struct is available since SDL 3.2.0.
*/
typedef struct SDL_AssertData
{
bool always_ignore; /**< true if app should always continue when assertion is triggered. */
unsigned int trigger_count; /**< Number of times this assertion has been triggered. */
const char *condition; /**< A string of this assert's test code. */
const char *filename; /**< The source file where this assert lives. */
int linenum; /**< The line in `filename` where this assert lives. */
const char *function; /**< The name of the function where this assert lives. */
const struct SDL_AssertData *next; /**< next item in the linked list. */
} SDL_AssertData;
/**
* Never call this directly.
*
* Use the SDL_assert macros instead.
*
* \param data assert data structure.
* \param func function name.
* \param file file name.
* \param line line number.
* \returns assert state.
*
* \threadsafety It is safe to call this function from any thread.
*
* \since This function is available since SDL 3.2.0.
*/
extern SDL_DECLSPEC SDL_AssertState SDLCALL SDL_ReportAssertion(SDL_AssertData *data,
const char *func,
const char *file, int line) SDL_ANALYZER_NORETURN;
#ifdef SDL_WIKI_DOCUMENTATION_SECTION
/**
* The macro used when an assertion triggers a breakpoint.
*
* This isn't for direct use by apps; use SDL_assert or SDL_TriggerBreakpoint
* instead.
*
* \since This macro is available since SDL 3.2.0.
*/
#define SDL_AssertBreakpoint() SDL_TriggerBreakpoint()
#elif !defined(SDL_AssertBreakpoint)
# if defined(ANDROID) && defined(assert)
/* Define this as empty in case assert() is defined as SDL_assert */
# define SDL_AssertBreakpoint()
# else
# define SDL_AssertBreakpoint() SDL_TriggerBreakpoint()
# endif
#endif /* !SDL_AssertBreakpoint */
/**
* The macro used when an assertion is enabled.
*
* This isn't for direct use by apps, but this is the code that is inserted
* when an SDL_assert is enabled.
*
* The `do {} while(0)` avoids dangling else problems:
*
* ```c
* if (x) SDL_assert(y); else blah();
* ```
*
* ... without the do/while, the "else" could attach to this macro's "if". We
* try to handle just the minimum we need here in a macro...the loop, the
* static vars, and break points. The heavy lifting is handled in
* SDL_ReportAssertion().
*
* \param condition the condition to assert.
*
* \since This macro is available since SDL 3.2.0.
*/
#define SDL_enabled_assert(condition) \
do { \
while ( !(condition) ) { \
static struct SDL_AssertData sdl_assert_data = { false, 0, #condition, NULL, 0, NULL, NULL }; \
const SDL_AssertState sdl_assert_state = SDL_ReportAssertion(&sdl_assert_data, SDL_FUNCTION, SDL_ASSERT_FILE, SDL_LINE); \
if (sdl_assert_state == SDL_ASSERTION_RETRY) { \
continue; /* go again. */ \
} else if (sdl_assert_state == SDL_ASSERTION_BREAK) { \
SDL_AssertBreakpoint(); \
} \
break; /* not retrying. */ \
} \
} while (SDL_NULL_WHILE_LOOP_CONDITION)
#ifdef SDL_WIKI_DOCUMENTATION_SECTION
/**
* An assertion test that is normally performed only in debug builds.
*
* This macro is enabled when the SDL_ASSERT_LEVEL is >= 2, otherwise it is
* disabled. This is meant to only do these tests in debug builds, so they can
* tend to be more expensive, and they are meant to bring everything to a halt
* when they fail, with the programmer there to assess the problem.
*
* In short: you can sprinkle these around liberally and assume they will
* evaporate out of the build when building for end-users.
*
* When assertions are disabled, this wraps `condition` in a `sizeof`
* operator, which means any function calls and side effects will not run, but
* the compiler will not complain about any otherwise-unused variables that
* are only referenced in the assertion.
*
* One can set the environment variable "SDL_ASSERT" to one of several strings
* ("abort", "break", "retry", "ignore", "always_ignore") to force a default
* behavior, which may be desirable for automation purposes. If your platform
* requires GUI interfaces to happen on the main thread but you're debugging
* an assertion in a background thread, it might be desirable to set this to
* "break" so that your debugger takes control as soon as assert is triggered,
* instead of risking a bad UI interaction (deadlock, etc) in the application.
*
* \param condition boolean value to test.
*
* \threadsafety It is safe to call this macro from any thread.
*
* \since This macro is available since SDL 3.2.0.
*/
#define SDL_assert(condition) if (assertion_enabled && (condition)) { trigger_assertion; }
/**
* An assertion test that is performed even in release builds.
*
* This macro is enabled when the SDL_ASSERT_LEVEL is >= 1, otherwise it is
* disabled. This is meant to be for tests that are cheap to make and
* extremely unlikely to fail; generally it is frowned upon to have an
* assertion failure in a release build, so these assertions generally need to
* be of more than life-and-death importance if there's a chance they might
* trigger. You should almost always consider handling these cases more
* gracefully than an assert allows.
*
* When assertions are disabled, this wraps `condition` in a `sizeof`
* operator, which means any function calls and side effects will not run, but
* the compiler will not complain about any otherwise-unused variables that
* are only referenced in the assertion.
*
* One can set the environment variable "SDL_ASSERT" to one of several strings
* ("abort", "break", "retry", "ignore", "always_ignore") to force a default
* behavior, which may be desirable for automation purposes. If your platform
* requires GUI interfaces to happen on the main thread but you're debugging
* an assertion in a background thread, it might be desirable to set this to
* "break" so that your debugger takes control as soon as assert is triggered,
* instead of risking a bad UI interaction (deadlock, etc) in the application.
* *
*
* \param condition boolean value to test.
*
* \threadsafety It is safe to call this macro from any thread.
*
* \since This macro is available since SDL 3.2.0.
*/
#define SDL_assert_release(condition) SDL_disabled_assert(condition)
/**
* An assertion test that is performed only when built with paranoid settings.
*
* This macro is enabled when the SDL_ASSERT_LEVEL is >= 3, otherwise it is
* disabled. This is a higher level than both release and debug, so these
* tests are meant to be expensive and only run when specifically looking for
* extremely unexpected failure cases in a special build.
*
* When assertions are disabled, this wraps `condition` in a `sizeof`
* operator, which means any function calls and side effects will not run, but
* the compiler will not complain about any otherwise-unused variables that
* are only referenced in the assertion.
*
* One can set the environment variable "SDL_ASSERT" to one of several strings
* ("abort", "break", "retry", "ignore", "always_ignore") to force a default
* behavior, which may be desirable for automation purposes. If your platform
* requires GUI interfaces to happen on the main thread but you're debugging
* an assertion in a background thread, it might be desirable to set this to
* "break" so that your debugger takes control as soon as assert is triggered,
* instead of risking a bad UI interaction (deadlock, etc) in the application.
*
* \param condition boolean value to test.
*
* \threadsafety It is safe to call this macro from any thread.
*
* \since This macro is available since SDL 3.2.0.
*/
#define SDL_assert_paranoid(condition) SDL_disabled_assert(condition)
/* Enable various levels of assertions. */
#elif SDL_ASSERT_LEVEL == 0 /* assertions disabled */
# define SDL_assert(condition) SDL_disabled_assert(condition)
# define SDL_assert_release(condition) SDL_disabled_assert(condition)
# define SDL_assert_paranoid(condition) SDL_disabled_assert(condition)
#elif SDL_ASSERT_LEVEL == 1 /* release settings. */
# define SDL_assert(condition) SDL_disabled_assert(condition)
# define SDL_assert_release(condition) SDL_enabled_assert(condition)
# define SDL_assert_paranoid(condition) SDL_disabled_assert(condition)
#elif SDL_ASSERT_LEVEL == 2 /* debug settings. */
# define SDL_assert(condition) SDL_enabled_assert(condition)
# define SDL_assert_release(condition) SDL_enabled_assert(condition)
# define SDL_assert_paranoid(condition) SDL_disabled_assert(condition)
#elif SDL_ASSERT_LEVEL == 3 /* paranoid settings. */
# define SDL_assert(condition) SDL_enabled_assert(condition)
# define SDL_assert_release(condition) SDL_enabled_assert(condition)
# define SDL_assert_paranoid(condition) SDL_enabled_assert(condition)
#else
# error Unknown assertion level.
#endif
/**
* An assertion test that is always performed.
*
* This macro is always enabled no matter what SDL_ASSERT_LEVEL is set to. You
* almost never want to use this, as it could trigger on an end-user's system,
* crashing your program.
*
* One can set the environment variable "SDL_ASSERT" to one of several strings
* ("abort", "break", "retry", "ignore", "always_ignore") to force a default
* behavior, which may be desirable for automation purposes. If your platform
* requires GUI interfaces to happen on the main thread but you're debugging
* an assertion in a background thread, it might be desirable to set this to
* "break" so that your debugger takes control as soon as assert is triggered,
* instead of risking a bad UI interaction (deadlock, etc) in the application.
*
* \param condition boolean value to test.
*
* \threadsafety It is safe to call this macro from any thread.
*
* \since This macro is available since SDL 3.2.0.
*/
#define SDL_assert_always(condition) SDL_enabled_assert(condition)
/**
* A callback that fires when an SDL assertion fails.
*
* \param data a pointer to the SDL_AssertData structure corresponding to the
* current assertion.
* \param userdata what was passed as `userdata` to SDL_SetAssertionHandler().
* \returns an SDL_AssertState value indicating how to handle the failure.
*
* \threadsafety This callback may be called from any thread that triggers an
* assert at any time.
*
* \since This datatype is available since SDL 3.2.0.
*/
typedef SDL_AssertState (SDLCALL *SDL_AssertionHandler)(
const SDL_AssertData *data, void *userdata);
/**
* Set an application-defined assertion handler.
*
* This function allows an application to show its own assertion UI and/or
* force the response to an assertion failure. If the application doesn't
* provide this, SDL will try to do the right thing, popping up a
* system-specific GUI dialog, and probably minimizing any fullscreen windows.
*
* This callback may fire from any thread, but it runs wrapped in a mutex, so
* it will only fire from one thread at a time.
*
* This callback is NOT reset to SDL's internal handler upon SDL_Quit()!
*
* \param handler the SDL_AssertionHandler function to call when an assertion
* fails or NULL for the default handler.
* \param userdata a pointer that is passed to `handler`.
*
* \threadsafety It is safe to call this function from any thread.
*
* \since This function is available since SDL 3.2.0.
*
* \sa SDL_GetAssertionHandler
*/
extern SDL_DECLSPEC void SDLCALL SDL_SetAssertionHandler(
SDL_AssertionHandler handler,
void *userdata);
/**
* Get the default assertion handler.
*
* This returns the function pointer that is called by default when an
* assertion is triggered. This is an internal function provided by SDL, that
* is used for assertions when SDL_SetAssertionHandler() hasn't been used to
* provide a different function.
*
* \returns the default SDL_AssertionHandler that is called when an assert
* triggers.
*
* \threadsafety It is safe to call this function from any thread.
*
* \since This function is available since SDL 3.2.0.
*
* \sa SDL_GetAssertionHandler
*/
extern SDL_DECLSPEC SDL_AssertionHandler SDLCALL SDL_GetDefaultAssertionHandler(void);
/**
* Get the current assertion handler.
*
* This returns the function pointer that is called when an assertion is
* triggered. This is either the value last passed to
* SDL_SetAssertionHandler(), or if no application-specified function is set,
* is equivalent to calling SDL_GetDefaultAssertionHandler().
*
* The parameter `puserdata` is a pointer to a void*, which will store the
* "userdata" pointer that was passed to SDL_SetAssertionHandler(). This value
* will always be NULL for the default handler. If you don't care about this
* data, it is safe to pass a NULL pointer to this function to ignore it.
*
* \param puserdata pointer which is filled with the "userdata" pointer that
* was passed to SDL_SetAssertionHandler().
* \returns the SDL_AssertionHandler that is called when an assert triggers.
*
* \threadsafety It is safe to call this function from any thread.
*
* \since This function is available since SDL 3.2.0.
*
* \sa SDL_SetAssertionHandler
*/
extern SDL_DECLSPEC SDL_AssertionHandler SDLCALL SDL_GetAssertionHandler(void **puserdata);
/**
* Get a list of all assertion failures.
*
* This function gets all assertions triggered since the last call to
* SDL_ResetAssertionReport(), or the start of the program.
*
* The proper way to examine this data looks something like this:
*
* ```c
* const SDL_AssertData *item = SDL_GetAssertionReport();
* while (item) {
* printf("'%s', %s (%s:%d), triggered %u times, always ignore: %s.\\n",
* item->condition, item->function, item->filename,
* item->linenum, item->trigger_count,
* item->always_ignore ? "yes" : "no");
* item = item->next;
* }
* ```
*
* \returns a list of all failed assertions or NULL if the list is empty. This
* memory should not be modified or freed by the application. This
* pointer remains valid until the next call to SDL_Quit() or
* SDL_ResetAssertionReport().
*
* \threadsafety This function is not thread safe. Other threads calling
* SDL_ResetAssertionReport() simultaneously, may render the
* returned pointer invalid.
*
* \since This function is available since SDL 3.2.0.
*
* \sa SDL_ResetAssertionReport
*/
extern SDL_DECLSPEC const SDL_AssertData * SDLCALL SDL_GetAssertionReport(void);
/**
* Clear the list of all assertion failures.
*
* This function will clear the list of all assertions triggered up to that
* point. Immediately following this call, SDL_GetAssertionReport will return
* no items. In addition, any previously-triggered assertions will be reset to
* a trigger_count of zero, and their always_ignore state will be false.
*
* \threadsafety This function is not thread safe. Other threads triggering an
* assertion, or simultaneously calling this function may cause
* memory leaks or crashes.
*
* \since This function is available since SDL 3.2.0.
*
* \sa SDL_GetAssertionReport
*/
extern SDL_DECLSPEC void SDLCALL SDL_ResetAssertionReport(void);
/* Ends C function definitions when using C++ */
#ifdef __cplusplus
}
#endif
#include <SDL3/SDL_close_code.h>
#endif /* SDL_assert_h_ */

View File

@@ -0,0 +1,550 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2026 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
/* WIKI CATEGORY: AsyncIO */
/**
* # CategoryAsyncIO
*
* SDL offers a way to perform I/O asynchronously. This allows an app to read
* or write files without waiting for data to actually transfer; the functions
* that request I/O never block while the request is fulfilled.
*
* Instead, the data moves in the background and the app can check for results
* at their leisure.
*
* This is more complicated than just reading and writing files in a
* synchronous way, but it can allow for more efficiency, and never having
* framerate drops as the hard drive catches up, etc.
*
* The general usage pattern for async I/O is:
*
* - Create one or more SDL_AsyncIOQueue objects.
* - Open files with SDL_AsyncIOFromFile.
* - Start I/O tasks to the files with SDL_ReadAsyncIO or SDL_WriteAsyncIO,
* putting those tasks into one of the queues.
* - Later on, use SDL_GetAsyncIOResult on a queue to see if any task is
* finished without blocking. Tasks might finish in any order with success
* or failure.
* - When all your tasks are done, close the file with SDL_CloseAsyncIO. This
* also generates a task, since it might flush data to disk!
*
* This all works, without blocking, in a single thread, but one can also wait
* on a queue in a background thread, sleeping until new results have arrived:
*
* - Call SDL_WaitAsyncIOResult from one or more threads to efficiently block
* until new tasks complete.
* - When shutting down, call SDL_SignalAsyncIOQueue to unblock any sleeping
* threads despite there being no new tasks completed.
*
* And, of course, to match the synchronous SDL_LoadFile, we offer
* SDL_LoadFileAsync as a convenience function. This will handle allocating a
* buffer, slurping in the file data, and null-terminating it; you still check
* for results later.
*
* Behind the scenes, SDL will use newer, efficient APIs on platforms that
* support them: Linux's io_uring and Windows 11's IoRing, for example. If
* those technologies aren't available, SDL will offload the work to a thread
* pool that will manage otherwise-synchronous loads without blocking the app.
*
* ## Best Practices
*
* Simple non-blocking I/O--for an app that just wants to pick up data
* whenever it's ready without losing framerate waiting on disks to spin--can
* use whatever pattern works well for the program. In this case, simply call
* SDL_ReadAsyncIO, or maybe SDL_LoadFileAsync, as needed. Once a frame, call
* SDL_GetAsyncIOResult to check for any completed tasks and deal with the
* data as it arrives.
*
* If two separate pieces of the same program need their own I/O, it is legal
* for each to create their own queue. This will prevent either piece from
* accidentally consuming the other's completed tasks. Each queue does require
* some amount of resources, but it is not an overwhelming cost. Do not make a
* queue for each task, however. It is better to put many tasks into a single
* queue. They will be reported in order of completion, not in the order they
* were submitted, so it doesn't generally matter what order tasks are
* started.
*
* One async I/O queue can be shared by multiple threads, or one thread can
* have more than one queue, but the most efficient way--if ruthless
* efficiency is the goal--is to have one queue per thread, with multiple
* threads working in parallel, and attempt to keep each queue loaded with
* tasks that are both started by and consumed by the same thread. On modern
* platforms that can use newer interfaces, this can keep data flowing as
* efficiently as possible all the way from storage hardware to the app, with
* no contention between threads for access to the same queue.
*
* Written data is not guaranteed to make it to physical media by the time a
* closing task is completed, unless SDL_CloseAsyncIO is called with its
* `flush` parameter set to true, which is to say that a successful result
* here can still result in lost data during an unfortunately-timed power
* outage if not flushed. However, flushing will take longer and may be
* unnecessary, depending on the app's needs.
*/
#ifndef SDL_asyncio_h_
#define SDL_asyncio_h_
#include <SDL3/SDL_stdinc.h>
#include <SDL3/SDL_begin_code.h>
/* Set up for C function definitions, even when using C++ */
#ifdef __cplusplus
extern "C" {
#endif
/**
* The asynchronous I/O operation structure.
*
* This operates as an opaque handle. One can then request read or write
* operations on it.
*
* \since This struct is available since SDL 3.2.0.
*
* \sa SDL_AsyncIOFromFile
*/
typedef struct SDL_AsyncIO SDL_AsyncIO;
/**
* Types of asynchronous I/O tasks.
*
* \since This enum is available since SDL 3.2.0.
*/
typedef enum SDL_AsyncIOTaskType
{
SDL_ASYNCIO_TASK_READ, /**< A read operation. */
SDL_ASYNCIO_TASK_WRITE, /**< A write operation. */
SDL_ASYNCIO_TASK_CLOSE /**< A close operation. */
} SDL_AsyncIOTaskType;
/**
* Possible outcomes of an asynchronous I/O task.
*
* \since This enum is available since SDL 3.2.0.
*/
typedef enum SDL_AsyncIOResult
{
SDL_ASYNCIO_COMPLETE, /**< request was completed without error */
SDL_ASYNCIO_FAILURE, /**< request failed for some reason; check SDL_GetError()! */
SDL_ASYNCIO_CANCELED /**< request was canceled before completing. */
} SDL_AsyncIOResult;
/**
* Information about a completed asynchronous I/O request.
*
* \since This struct is available since SDL 3.2.0.
*/
typedef struct SDL_AsyncIOOutcome
{
SDL_AsyncIO *asyncio; /**< what generated this task. This pointer will be invalid if it was closed! */
SDL_AsyncIOTaskType type; /**< What sort of task was this? Read, write, etc? */
SDL_AsyncIOResult result; /**< the result of the work (success, failure, cancellation). */
void *buffer; /**< buffer where data was read/written. */
Uint64 offset; /**< offset in the SDL_AsyncIO where data was read/written. */
Uint64 bytes_requested; /**< number of bytes the task was to read/write. */
Uint64 bytes_transferred; /**< actual number of bytes that were read/written. */
void *userdata; /**< pointer provided by the app when starting the task */
} SDL_AsyncIOOutcome;
/**
* A queue of completed asynchronous I/O tasks.
*
* When starting an asynchronous operation, you specify a queue for the new
* task. A queue can be asked later if any tasks in it have completed,
* allowing an app to manage multiple pending tasks in one place, in whatever
* order they complete.
*
* \since This struct is available since SDL 3.2.0.
*
* \sa SDL_CreateAsyncIOQueue
* \sa SDL_ReadAsyncIO
* \sa SDL_WriteAsyncIO
* \sa SDL_GetAsyncIOResult
* \sa SDL_WaitAsyncIOResult
*/
typedef struct SDL_AsyncIOQueue SDL_AsyncIOQueue;
/**
* Use this function to create a new SDL_AsyncIO object for reading from
* and/or writing to a named file.
*
* The `mode` string understands the following values:
*
* - "r": Open a file for reading only. It must exist.
* - "w": Open a file for writing only. It will create missing files or
* truncate existing ones.
* - "r+": Open a file for update both reading and writing. The file must
* exist.
* - "w+": Create an empty file for both reading and writing. If a file with
* the same name already exists its content is erased and the file is
* treated as a new empty file.
*
* There is no "b" mode, as there is only "binary" style I/O, and no "a" mode
* for appending, since you specify the position when starting a task.
*
* This function supports Unicode filenames, but they must be encoded in UTF-8
* format, regardless of the underlying operating system.
*
* This call is _not_ asynchronous; it will open the file before returning,
* under the assumption that doing so is generally a fast operation. Future
* reads and writes to the opened file will be async, however.
*
* \param file a UTF-8 string representing the filename to open.
* \param mode an ASCII string representing the mode to be used for opening
* the file.
* \returns a pointer to the SDL_AsyncIO structure that is created or NULL on
* failure; call SDL_GetError() for more information.
*
* \threadsafety It is safe to call this function from any thread.
*
* \since This function is available since SDL 3.2.0.
*
* \sa SDL_CloseAsyncIO
* \sa SDL_ReadAsyncIO
* \sa SDL_WriteAsyncIO
*/
extern SDL_DECLSPEC SDL_AsyncIO * SDLCALL SDL_AsyncIOFromFile(const char *file, const char *mode);
/**
* Use this function to get the size of the data stream in an SDL_AsyncIO.
*
* This call is _not_ asynchronous; it assumes that obtaining this info is a
* non-blocking operation in most reasonable cases.
*
* \param asyncio the SDL_AsyncIO to get the size of the data stream from.
* \returns the size of the data stream in the SDL_IOStream on success or a
* negative error code on failure; call SDL_GetError() for more
* information.
*
* \threadsafety It is safe to call this function from any thread.
*
* \since This function is available since SDL 3.2.0.
*/
extern SDL_DECLSPEC Sint64 SDLCALL SDL_GetAsyncIOSize(SDL_AsyncIO *asyncio);
/**
* Start an async read.
*
* This function reads up to `size` bytes from `offset` position in the data
* source to the area pointed at by `ptr`. This function may read less bytes
* than requested.
*
* This function returns as quickly as possible; it does not wait for the read
* to complete. On a successful return, this work will continue in the
* background. If the work begins, even failure is asynchronous: a failing
* return value from this function only means the work couldn't start at all.
*
* `ptr` must remain available until the work is done, and may be accessed by
* the system at any time until then. Do not allocate it on the stack, as this
* might take longer than the life of the calling function to complete!
*
* An SDL_AsyncIOQueue must be specified. The newly-created task will be added
* to it when it completes its work.
*
* \param asyncio a pointer to an SDL_AsyncIO structure.
* \param ptr a pointer to a buffer to read data into.
* \param offset the position to start reading in the data source.
* \param size the number of bytes to read from the data source.
* \param queue a queue to add the new SDL_AsyncIO to.
* \param userdata an app-defined pointer that will be provided with the task
* results.
* \returns true on success or false on failure; call SDL_GetError() for more
* information.
*
* \threadsafety It is safe to call this function from any thread.
*
* \since This function is available since SDL 3.2.0.
*
* \sa SDL_WriteAsyncIO
* \sa SDL_CreateAsyncIOQueue
*/
extern SDL_DECLSPEC bool SDLCALL SDL_ReadAsyncIO(SDL_AsyncIO *asyncio, void *ptr, Uint64 offset, Uint64 size, SDL_AsyncIOQueue *queue, void *userdata);
/**
* Start an async write.
*
* This function writes `size` bytes from `offset` position in the data source
* to the area pointed at by `ptr`.
*
* This function returns as quickly as possible; it does not wait for the
* write to complete. On a successful return, this work will continue in the
* background. If the work begins, even failure is asynchronous: a failing
* return value from this function only means the work couldn't start at all.
*
* `ptr` must remain available until the work is done, and may be accessed by
* the system at any time until then. Do not allocate it on the stack, as this
* might take longer than the life of the calling function to complete!
*
* An SDL_AsyncIOQueue must be specified. The newly-created task will be added
* to it when it completes its work.
*
* \param asyncio a pointer to an SDL_AsyncIO structure.
* \param ptr a pointer to a buffer to write data from.
* \param offset the position to start writing to the data source.
* \param size the number of bytes to write to the data source.
* \param queue a queue to add the new SDL_AsyncIO to.
* \param userdata an app-defined pointer that will be provided with the task
* results.
* \returns true on success or false on failure; call SDL_GetError() for more
* information.
*
* \threadsafety It is safe to call this function from any thread.
*
* \since This function is available since SDL 3.2.0.
*
* \sa SDL_ReadAsyncIO
* \sa SDL_CreateAsyncIOQueue
*/
extern SDL_DECLSPEC bool SDLCALL SDL_WriteAsyncIO(SDL_AsyncIO *asyncio, void *ptr, Uint64 offset, Uint64 size, SDL_AsyncIOQueue *queue, void *userdata);
/**
* Close and free any allocated resources for an async I/O object.
*
* Closing a file is _also_ an asynchronous task! If a write failure were to
* happen during the closing process, for example, the task results will
* report it as usual.
*
* Closing a file that has been written to does not guarantee the data has
* made it to physical media; it may remain in the operating system's file
* cache, for later writing to disk. This means that a successfully-closed
* file can be lost if the system crashes or loses power in this small window.
* To prevent this, call this function with the `flush` parameter set to true.
* This will make the operation take longer, and perhaps increase system load
* in general, but a successful result guarantees that the data has made it to
* physical storage. Don't use this for temporary files, caches, and
* unimportant data, and definitely use it for crucial irreplaceable files,
* like game saves.
*
* This function guarantees that the close will happen after any other pending
* tasks to `asyncio`, so it's safe to open a file, start several operations,
* close the file immediately, then check for all results later. This function
* will not block until the tasks have completed.
*
* Once this function returns true, `asyncio` is no longer valid, regardless
* of any future outcomes. Any completed tasks might still contain this
* pointer in their SDL_AsyncIOOutcome data, in case the app was using this
* value to track information, but it should not be used again.
*
* If this function returns false, the close wasn't started at all, and it's
* safe to attempt to close again later.
*
* An SDL_AsyncIOQueue must be specified. The newly-created task will be added
* to it when it completes its work.
*
* \param asyncio a pointer to an SDL_AsyncIO structure to close.
* \param flush true if data should sync to disk before the task completes.
* \param queue a queue to add the new SDL_AsyncIO to.
* \param userdata an app-defined pointer that will be provided with the task
* results.
* \returns true on success or false on failure; call SDL_GetError() for more
* information.
*
* \threadsafety It is safe to call this function from any thread, but two
* threads should not attempt to close the same object.
*
* \since This function is available since SDL 3.2.0.
*/
extern SDL_DECLSPEC bool SDLCALL SDL_CloseAsyncIO(SDL_AsyncIO *asyncio, bool flush, SDL_AsyncIOQueue *queue, void *userdata);
/**
* Create a task queue for tracking multiple I/O operations.
*
* Async I/O operations are assigned to a queue when started. The queue can be
* checked for completed tasks thereafter.
*
* \returns a new task queue object or NULL if there was an error; call
* SDL_GetError() for more information.
*
* \threadsafety It is safe to call this function from any thread.
*
* \since This function is available since SDL 3.2.0.
*
* \sa SDL_DestroyAsyncIOQueue
* \sa SDL_GetAsyncIOResult
* \sa SDL_WaitAsyncIOResult
*/
extern SDL_DECLSPEC SDL_AsyncIOQueue * SDLCALL SDL_CreateAsyncIOQueue(void);
/**
* Destroy a previously-created async I/O task queue.
*
* If there are still tasks pending for this queue, this call will block until
* those tasks are finished. All those tasks will be deallocated. Their
* results will be lost to the app.
*
* Any pending reads from SDL_LoadFileAsync() that are still in this queue
* will have their buffers deallocated by this function, to prevent a memory
* leak.
*
* Once this function is called, the queue is no longer valid and should not
* be used, including by other threads that might access it while destruction
* is blocking on pending tasks.
*
* Do not destroy a queue that still has threads waiting on it through
* SDL_WaitAsyncIOResult(). You can call SDL_SignalAsyncIOQueue() first to
* unblock those threads, and take measures (such as SDL_WaitThread()) to make
* sure they have finished their wait and won't wait on the queue again.
*
* \param queue the task queue to destroy.
*
* \threadsafety It is safe to call this function from any thread, so long as
* no other thread is waiting on the queue with
* SDL_WaitAsyncIOResult.
*
* \since This function is available since SDL 3.2.0.
*/
extern SDL_DECLSPEC void SDLCALL SDL_DestroyAsyncIOQueue(SDL_AsyncIOQueue *queue);
/**
* Query an async I/O task queue for completed tasks.
*
* If a task assigned to this queue has finished, this will return true and
* fill in `outcome` with the details of the task. If no task in the queue has
* finished, this function will return false. This function does not block.
*
* If a task has completed, this function will free its resources and the task
* pointer will no longer be valid. The task will be removed from the queue.
*
* It is safe for multiple threads to call this function on the same queue at
* once; a completed task will only go to one of the threads.
*
* \param queue the async I/O task queue to query.
* \param outcome details of a finished task will be written here. May not be
* NULL.
* \returns true if a task has completed, false otherwise.
*
* \threadsafety It is safe to call this function from any thread.
*
* \since This function is available since SDL 3.2.0.
*
* \sa SDL_WaitAsyncIOResult
*/
extern SDL_DECLSPEC bool SDLCALL SDL_GetAsyncIOResult(SDL_AsyncIOQueue *queue, SDL_AsyncIOOutcome *outcome);
/**
* Block until an async I/O task queue has a completed task.
*
* This function puts the calling thread to sleep until there a task assigned
* to the queue that has finished.
*
* If a task assigned to the queue has finished, this will return true and
* fill in `outcome` with the details of the task. If no task in the queue has
* finished, this function will return false.
*
* If a task has completed, this function will free its resources and the task
* pointer will no longer be valid. The task will be removed from the queue.
*
* It is safe for multiple threads to call this function on the same queue at
* once; a completed task will only go to one of the threads.
*
* Note that by the nature of various platforms, more than one waiting thread
* may wake to handle a single task, but only one will obtain it, so
* `timeoutMS` is a _maximum_ wait time, and this function may return false
* sooner.
*
* This function may return false if there was a system error, the OS
* inadvertently awoke multiple threads, or if SDL_SignalAsyncIOQueue() was
* called to wake up all waiting threads without a finished task.
*
* A timeout can be used to specify a maximum wait time, but rather than
* polling, it is possible to have a timeout of -1 to wait forever, and use
* SDL_SignalAsyncIOQueue() to wake up the waiting threads later.
*
* \param queue the async I/O task queue to wait on.
* \param outcome details of a finished task will be written here. May not be
* NULL.
* \param timeoutMS the maximum time to wait, in milliseconds, or -1 to wait
* indefinitely.
* \returns true if task has completed, false otherwise.
*
* \threadsafety It is safe to call this function from any thread.
*
* \since This function is available since SDL 3.2.0.
*
* \sa SDL_SignalAsyncIOQueue
*/
extern SDL_DECLSPEC bool SDLCALL SDL_WaitAsyncIOResult(SDL_AsyncIOQueue *queue, SDL_AsyncIOOutcome *outcome, Sint32 timeoutMS);
/**
* Wake up any threads that are blocking in SDL_WaitAsyncIOResult().
*
* This will unblock any threads that are sleeping in a call to
* SDL_WaitAsyncIOResult for the specified queue, and cause them to return
* from that function.
*
* This can be useful when destroying a queue to make sure nothing is touching
* it indefinitely. In this case, once this call completes, the caller should
* take measures to make sure any previously-blocked threads have returned
* from their wait and will not touch the queue again (perhaps by setting a
* flag to tell the threads to terminate and then using SDL_WaitThread() to
* make sure they've done so).
*
* \param queue the async I/O task queue to signal.
*
* \threadsafety It is safe to call this function from any thread.
*
* \since This function is available since SDL 3.2.0.
*
* \sa SDL_WaitAsyncIOResult
*/
extern SDL_DECLSPEC void SDLCALL SDL_SignalAsyncIOQueue(SDL_AsyncIOQueue *queue);
/**
* Load all the data from a file path, asynchronously.
*
* This function returns as quickly as possible; it does not wait for the read
* to complete. On a successful return, this work will continue in the
* background. If the work begins, even failure is asynchronous: a failing
* return value from this function only means the work couldn't start at all.
*
* The data is allocated with a zero byte at the end (null terminated) for
* convenience. This extra byte is not included in SDL_AsyncIOOutcome's
* bytes_transferred value.
*
* This function will allocate the buffer to contain the file. It must be
* deallocated by calling SDL_free() on SDL_AsyncIOOutcome's buffer field
* after completion.
*
* An SDL_AsyncIOQueue must be specified. The newly-created task will be added
* to it when it completes its work.
*
* \param file the path to read all available data from.
* \param queue a queue to add the new SDL_AsyncIO to.
* \param userdata an app-defined pointer that will be provided with the task
* results.
* \returns true on success or false on failure; call SDL_GetError() for more
* information.
*
* \threadsafety It is safe to call this function from any thread.
*
* \since This function is available since SDL 3.2.0.
*
* \sa SDL_LoadFile_IO
*/
extern SDL_DECLSPEC bool SDLCALL SDL_LoadFileAsync(const char *file, SDL_AsyncIOQueue *queue, void *userdata);
/* Ends C function definitions when using C++ */
#ifdef __cplusplus
}
#endif
#include <SDL3/SDL_close_code.h>
#endif /* SDL_asyncio_h_ */

View File

@@ -0,0 +1,682 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2026 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
/**
* # CategoryAtomic
*
* Atomic operations.
*
* IMPORTANT: If you are not an expert in concurrent lockless programming, you
* should not be using any functions in this file. You should be protecting
* your data structures with full mutexes instead.
*
* ***Seriously, here be dragons!***
*
* You can find out a little more about lockless programming and the subtle
* issues that can arise here:
* https://learn.microsoft.com/en-us/windows/win32/dxtecharts/lockless-programming
*
* There's also lots of good information here:
*
* - https://www.1024cores.net/home/lock-free-algorithms
* - https://preshing.com/
*
* These operations may or may not actually be implemented using processor
* specific atomic operations. When possible they are implemented as true
* processor specific atomic operations. When that is not possible the are
* implemented using locks that *do* use the available atomic operations.
*
* All of the atomic operations that modify memory are full memory barriers.
*/
#ifndef SDL_atomic_h_
#define SDL_atomic_h_
#include <SDL3/SDL_stdinc.h>
#include <SDL3/SDL_platform_defines.h>
#include <SDL3/SDL_begin_code.h>
/* Set up for C function definitions, even when using C++ */
#ifdef __cplusplus
extern "C" {
#endif
/**
* An atomic spinlock.
*
* The atomic locks are efficient spinlocks using CPU instructions, but are
* vulnerable to starvation and can spin forever if a thread holding a lock
* has been terminated. For this reason you should minimize the code executed
* inside an atomic lock and never do expensive things like API or system
* calls while holding them.
*
* They are also vulnerable to starvation if the thread holding the lock is
* lower priority than other threads and doesn't get scheduled. In general you
* should use mutexes instead, since they have better performance and
* contention behavior.
*
* The atomic locks are not safe to lock recursively.
*
* Porting Note: The spin lock functions and type are required and can not be
* emulated because they are used in the atomic emulation code.
*/
typedef int SDL_SpinLock;
/**
* Try to lock a spin lock by setting it to a non-zero value.
*
* ***Please note that spinlocks are dangerous if you don't know what you're
* doing. Please be careful using any sort of spinlock!***
*
* \param lock a pointer to a lock variable.
* \returns true if the lock succeeded, false if the lock is already held.
*
* \threadsafety It is safe to call this function from any thread.
*
* \since This function is available since SDL 3.2.0.
*
* \sa SDL_LockSpinlock
* \sa SDL_UnlockSpinlock
*/
extern SDL_DECLSPEC bool SDLCALL SDL_TryLockSpinlock(SDL_SpinLock *lock);
/**
* Lock a spin lock by setting it to a non-zero value.
*
* ***Please note that spinlocks are dangerous if you don't know what you're
* doing. Please be careful using any sort of spinlock!***
*
* \param lock a pointer to a lock variable.
*
* \threadsafety It is safe to call this function from any thread.
*
* \since This function is available since SDL 3.2.0.
*
* \sa SDL_TryLockSpinlock
* \sa SDL_UnlockSpinlock
*/
extern SDL_DECLSPEC void SDLCALL SDL_LockSpinlock(SDL_SpinLock *lock);
/**
* Unlock a spin lock by setting it to 0.
*
* Always returns immediately.
*
* ***Please note that spinlocks are dangerous if you don't know what you're
* doing. Please be careful using any sort of spinlock!***
*
* \param lock a pointer to a lock variable.
*
* \threadsafety It is safe to call this function from any thread.
*
* \since This function is available since SDL 3.2.0.
*
* \sa SDL_LockSpinlock
* \sa SDL_TryLockSpinlock
*/
extern SDL_DECLSPEC void SDLCALL SDL_UnlockSpinlock(SDL_SpinLock *lock);
#ifdef SDL_WIKI_DOCUMENTATION_SECTION
/**
* Mark a compiler barrier.
*
* A compiler barrier prevents the compiler from reordering reads and writes
* to globally visible variables across the call.
*
* This macro only prevents the compiler from reordering reads and writes, it
* does not prevent the CPU from reordering reads and writes. However, all of
* the atomic operations that modify memory are full memory barriers.
*
* \threadsafety Obviously this macro is safe to use from any thread at any
* time, but if you find yourself needing this, you are probably
* dealing with some very sensitive code; be careful!
*
* \since This macro is available since SDL 3.2.0.
*/
#define SDL_CompilerBarrier() DoCompilerSpecificReadWriteBarrier()
#elif defined(_MSC_VER) && (_MSC_VER > 1200) && !defined(__clang__)
void _ReadWriteBarrier(void);
#pragma intrinsic(_ReadWriteBarrier)
#define SDL_CompilerBarrier() _ReadWriteBarrier()
#elif (defined(__GNUC__) && !defined(SDL_PLATFORM_EMSCRIPTEN)) || (defined(__SUNPRO_C) && (__SUNPRO_C >= 0x5120))
/* This is correct for all CPUs when using GCC or Solaris Studio 12.1+. */
#define SDL_CompilerBarrier() __asm__ __volatile__ ("" : : : "memory")
#elif defined(__WATCOMC__)
extern __inline void SDL_CompilerBarrier(void);
#pragma aux SDL_CompilerBarrier = "" parm [] modify exact [];
#else
#define SDL_CompilerBarrier() \
{ SDL_SpinLock _tmp = 0; SDL_LockSpinlock(&_tmp); SDL_UnlockSpinlock(&_tmp); }
#endif
/**
* Insert a memory release barrier (function version).
*
* Please refer to SDL_MemoryBarrierRelease for details. This is a function
* version, which might be useful if you need to use this functionality from a
* scripting language, etc. Also, some of the macro versions call this
* function behind the scenes, where more heavy lifting can happen inside of
* SDL. Generally, though, an app written in C/C++/etc should use the macro
* version, as it will be more efficient.
*
* \threadsafety Obviously this function is safe to use from any thread at any
* time, but if you find yourself needing this, you are probably
* dealing with some very sensitive code; be careful!
*
* \since This function is available since SDL 3.2.0.
*
* \sa SDL_MemoryBarrierRelease
*/
extern SDL_DECLSPEC void SDLCALL SDL_MemoryBarrierReleaseFunction(void);
/**
* Insert a memory acquire barrier (function version).
*
* Please refer to SDL_MemoryBarrierRelease for details. This is a function
* version, which might be useful if you need to use this functionality from a
* scripting language, etc. Also, some of the macro versions call this
* function behind the scenes, where more heavy lifting can happen inside of
* SDL. Generally, though, an app written in C/C++/etc should use the macro
* version, as it will be more efficient.
*
* \threadsafety Obviously this function is safe to use from any thread at any
* time, but if you find yourself needing this, you are probably
* dealing with some very sensitive code; be careful!
*
* \since This function is available since SDL 3.2.0.
*
* \sa SDL_MemoryBarrierAcquire
*/
extern SDL_DECLSPEC void SDLCALL SDL_MemoryBarrierAcquireFunction(void);
#ifdef SDL_WIKI_DOCUMENTATION_SECTION
/**
* Insert a memory release barrier (macro version).
*
* Memory barriers are designed to prevent reads and writes from being
* reordered by the compiler and being seen out of order on multi-core CPUs.
*
* A typical pattern would be for thread A to write some data and a flag, and
* for thread B to read the flag and get the data. In this case you would
* insert a release barrier between writing the data and the flag,
* guaranteeing that the data write completes no later than the flag is
* written, and you would insert an acquire barrier between reading the flag
* and reading the data, to ensure that all the reads associated with the flag
* have completed.
*
* In this pattern you should always see a release barrier paired with an
* acquire barrier and you should gate the data reads/writes with a single
* flag variable.
*
* For more information on these semantics, take a look at the blog post:
* http://preshing.com/20120913/acquire-and-release-semantics
*
* This is the macro version of this functionality; if possible, SDL will use
* compiler intrinsics or inline assembly, but some platforms might need to
* call the function version of this, SDL_MemoryBarrierReleaseFunction to do
* the heavy lifting. Apps that can use the macro should favor it over the
* function.
*
* \threadsafety Obviously this macro is safe to use from any thread at any
* time, but if you find yourself needing this, you are probably
* dealing with some very sensitive code; be careful!
*
* \since This macro is available since SDL 3.2.0.
*
* \sa SDL_MemoryBarrierAcquire
* \sa SDL_MemoryBarrierReleaseFunction
*/
#define SDL_MemoryBarrierRelease() SDL_MemoryBarrierReleaseFunction()
/**
* Insert a memory acquire barrier (macro version).
*
* Please see SDL_MemoryBarrierRelease for the details on what memory barriers
* are and when to use them.
*
* This is the macro version of this functionality; if possible, SDL will use
* compiler intrinsics or inline assembly, but some platforms might need to
* call the function version of this, SDL_MemoryBarrierAcquireFunction, to do
* the heavy lifting. Apps that can use the macro should favor it over the
* function.
*
* \threadsafety Obviously this macro is safe to use from any thread at any
* time, but if you find yourself needing this, you are probably
* dealing with some very sensitive code; be careful!
*
* \since This macro is available since SDL 3.2.0.
*
* \sa SDL_MemoryBarrierRelease
* \sa SDL_MemoryBarrierAcquireFunction
*/
#define SDL_MemoryBarrierAcquire() SDL_MemoryBarrierAcquireFunction()
#elif defined(__GNUC__) && (defined(__powerpc__) || defined(__ppc__))
#define SDL_MemoryBarrierRelease() __asm__ __volatile__ ("lwsync" : : : "memory")
#define SDL_MemoryBarrierAcquire() __asm__ __volatile__ ("lwsync" : : : "memory")
#elif defined(__GNUC__) && defined(__aarch64__)
#define SDL_MemoryBarrierRelease() __asm__ __volatile__ ("dmb ish" : : : "memory")
#define SDL_MemoryBarrierAcquire() __asm__ __volatile__ ("dmb ish" : : : "memory")
#elif defined(__GNUC__) && defined(__arm__)
#if 0 /* defined(SDL_PLATFORM_LINUX) || defined(SDL_PLATFORM_ANDROID) */
/* Information from:
https://chromium.googlesource.com/chromium/chromium/+/trunk/base/atomicops_internals_arm_gcc.h#19
The Linux kernel provides a helper function which provides the right code for a memory barrier,
hard-coded at address 0xffff0fa0
*/
typedef void (*SDL_KernelMemoryBarrierFunc)();
#define SDL_MemoryBarrierRelease() ((SDL_KernelMemoryBarrierFunc)0xffff0fa0)()
#define SDL_MemoryBarrierAcquire() ((SDL_KernelMemoryBarrierFunc)0xffff0fa0)()
#else
#if defined(__ARM_ARCH_7__) || defined(__ARM_ARCH_7A__) || defined(__ARM_ARCH_7EM__) || defined(__ARM_ARCH_7R__) || defined(__ARM_ARCH_7M__) || defined(__ARM_ARCH_7S__) || defined(__ARM_ARCH_8A__)
#define SDL_MemoryBarrierRelease() __asm__ __volatile__ ("dmb ish" : : : "memory")
#define SDL_MemoryBarrierAcquire() __asm__ __volatile__ ("dmb ish" : : : "memory")
#elif defined(__ARM_ARCH_6__) || defined(__ARM_ARCH_6J__) || defined(__ARM_ARCH_6K__) || defined(__ARM_ARCH_6T2__) || defined(__ARM_ARCH_6Z__) || defined(__ARM_ARCH_6ZK__)
#ifdef __thumb__
/* The mcr instruction isn't available in thumb mode, use real functions */
#define SDL_MEMORY_BARRIER_USES_FUNCTION
#define SDL_MemoryBarrierRelease() SDL_MemoryBarrierReleaseFunction()
#define SDL_MemoryBarrierAcquire() SDL_MemoryBarrierAcquireFunction()
#else
#define SDL_MemoryBarrierRelease() __asm__ __volatile__ ("mcr p15, 0, %0, c7, c10, 5" : : "r"(0) : "memory")
#define SDL_MemoryBarrierAcquire() __asm__ __volatile__ ("mcr p15, 0, %0, c7, c10, 5" : : "r"(0) : "memory")
#endif /* __thumb__ */
#else
#define SDL_MemoryBarrierRelease() __asm__ __volatile__ ("" : : : "memory")
#define SDL_MemoryBarrierAcquire() __asm__ __volatile__ ("" : : : "memory")
#endif /* SDL_PLATFORM_LINUX || SDL_PLATFORM_ANDROID */
#endif /* __GNUC__ && __arm__ */
#else
#if (defined(__SUNPRO_C) && (__SUNPRO_C >= 0x5120))
/* This is correct for all CPUs on Solaris when using Solaris Studio 12.1+. */
#include <mbarrier.h>
#define SDL_MemoryBarrierRelease() __machine_rel_barrier()
#define SDL_MemoryBarrierAcquire() __machine_acq_barrier()
#else
/* This is correct for the x86 and x64 CPUs, and we'll expand this over time. */
#define SDL_MemoryBarrierRelease() SDL_CompilerBarrier()
#define SDL_MemoryBarrierAcquire() SDL_CompilerBarrier()
#endif
#endif
/* "REP NOP" is PAUSE, coded for tools that don't know it by that name. */
#ifdef SDL_WIKI_DOCUMENTATION_SECTION
/**
* A macro to insert a CPU-specific "pause" instruction into the program.
*
* This can be useful in busy-wait loops, as it serves as a hint to the CPU as
* to the program's intent; some CPUs can use this to do more efficient
* processing. On some platforms, this doesn't do anything, so using this
* macro might just be a harmless no-op.
*
* Note that if you are busy-waiting, there are often more-efficient
* approaches with other synchronization primitives: mutexes, semaphores,
* condition variables, etc.
*
* \threadsafety This macro is safe to use from any thread.
*
* \since This macro is available since SDL 3.2.0.
*/
#define SDL_CPUPauseInstruction() DoACPUPauseInACompilerAndArchitectureSpecificWay
#elif (defined(__GNUC__) || defined(__clang__)) && (defined(__i386__) || defined(__x86_64__))
#define SDL_CPUPauseInstruction() __asm__ __volatile__("pause\n") /* Some assemblers can't do REP NOP, so go with PAUSE. */
#elif (defined(__arm__) && defined(__ARM_ARCH) && __ARM_ARCH >= 7) || defined(__aarch64__)
#define SDL_CPUPauseInstruction() __asm__ __volatile__("yield" ::: "memory")
#elif (defined(__powerpc__) || defined(__powerpc64__))
#define SDL_CPUPauseInstruction() __asm__ __volatile__("or 27,27,27");
#elif (defined(__riscv) && __riscv_xlen == 64)
#define SDL_CPUPauseInstruction() __asm__ __volatile__(".insn i 0x0F, 0, x0, x0, 0x010");
#elif defined(_MSC_VER) && (defined(_M_IX86) || defined(_M_X64))
#define SDL_CPUPauseInstruction() _mm_pause() /* this is actually "rep nop" and not a SIMD instruction. No inline asm in MSVC x86-64! */
#elif defined(_MSC_VER) && (defined(_M_ARM) || defined(_M_ARM64))
#define SDL_CPUPauseInstruction() __yield()
#elif defined(__WATCOMC__) && defined(__386__)
extern __inline void SDL_CPUPauseInstruction(void);
#pragma aux SDL_CPUPauseInstruction = ".686p" ".xmm2" "pause"
#else
#define SDL_CPUPauseInstruction()
#endif
/**
* A type representing an atomic integer value.
*
* This can be used to manage a value that is synchronized across multiple
* CPUs without a race condition; when an app sets a value with
* SDL_SetAtomicInt all other threads, regardless of the CPU it is running on,
* will see that value when retrieved with SDL_GetAtomicInt, regardless of CPU
* caches, etc.
*
* This is also useful for atomic compare-and-swap operations: a thread can
* change the value as long as its current value matches expectations. When
* done in a loop, one can guarantee data consistency across threads without a
* lock (but the usual warnings apply: if you don't know what you're doing, or
* you don't do it carefully, you can confidently cause any number of
* disasters with this, so in most cases, you _should_ use a mutex instead of
* this!).
*
* This is a struct so people don't accidentally use numeric operations on it
* directly. You have to use SDL atomic functions.
*
* \since This struct is available since SDL 3.2.0.
*
* \sa SDL_CompareAndSwapAtomicInt
* \sa SDL_GetAtomicInt
* \sa SDL_SetAtomicInt
* \sa SDL_AddAtomicInt
*/
typedef struct SDL_AtomicInt { int value; } SDL_AtomicInt;
/**
* Set an atomic variable to a new value if it is currently an old value.
*
* ***Note: If you don't know what this function is for, you shouldn't use
* it!***
*
* \param a a pointer to an SDL_AtomicInt variable to be modified.
* \param oldval the old value.
* \param newval the new value.
* \returns true if the atomic variable was set, false otherwise.
*
* \threadsafety It is safe to call this function from any thread.
*
* \since This function is available since SDL 3.2.0.
*
* \sa SDL_GetAtomicInt
* \sa SDL_SetAtomicInt
*/
extern SDL_DECLSPEC bool SDLCALL SDL_CompareAndSwapAtomicInt(SDL_AtomicInt *a, int oldval, int newval);
/**
* Set an atomic variable to a value.
*
* This function also acts as a full memory barrier.
*
* ***Note: If you don't know what this function is for, you shouldn't use
* it!***
*
* \param a a pointer to an SDL_AtomicInt variable to be modified.
* \param v the desired value.
* \returns the previous value of the atomic variable.
*
* \threadsafety It is safe to call this function from any thread.
*
* \since This function is available since SDL 3.2.0.
*
* \sa SDL_GetAtomicInt
*/
extern SDL_DECLSPEC int SDLCALL SDL_SetAtomicInt(SDL_AtomicInt *a, int v);
/**
* Get the value of an atomic variable.
*
* ***Note: If you don't know what this function is for, you shouldn't use
* it!***
*
* \param a a pointer to an SDL_AtomicInt variable.
* \returns the current value of an atomic variable.
*
* \threadsafety It is safe to call this function from any thread.
*
* \since This function is available since SDL 3.2.0.
*
* \sa SDL_SetAtomicInt
*/
extern SDL_DECLSPEC int SDLCALL SDL_GetAtomicInt(SDL_AtomicInt *a);
/**
* Add to an atomic variable.
*
* This function also acts as a full memory barrier.
*
* ***Note: If you don't know what this function is for, you shouldn't use
* it!***
*
* \param a a pointer to an SDL_AtomicInt variable to be modified.
* \param v the desired value to add.
* \returns the previous value of the atomic variable.
*
* \threadsafety It is safe to call this function from any thread.
*
* \since This function is available since SDL 3.2.0.
*
* \sa SDL_AtomicDecRef
* \sa SDL_AtomicIncRef
*/
extern SDL_DECLSPEC int SDLCALL SDL_AddAtomicInt(SDL_AtomicInt *a, int v);
#ifndef SDL_AtomicIncRef
/**
* Increment an atomic variable used as a reference count.
*
* ***Note: If you don't know what this macro is for, you shouldn't use it!***
*
* \param a a pointer to an SDL_AtomicInt to increment.
* \returns the previous value of the atomic variable.
*
* \threadsafety It is safe to call this macro from any thread.
*
* \since This macro is available since SDL 3.2.0.
*
* \sa SDL_AtomicDecRef
*/
#define SDL_AtomicIncRef(a) SDL_AddAtomicInt(a, 1)
#endif
#ifndef SDL_AtomicDecRef
/**
* Decrement an atomic variable used as a reference count.
*
* ***Note: If you don't know what this macro is for, you shouldn't use it!***
*
* \param a a pointer to an SDL_AtomicInt to decrement.
* \returns true if the variable reached zero after decrementing, false
* otherwise.
*
* \threadsafety It is safe to call this macro from any thread.
*
* \since This macro is available since SDL 3.2.0.
*
* \sa SDL_AtomicIncRef
*/
#define SDL_AtomicDecRef(a) (SDL_AddAtomicInt(a, -1) == 1)
#endif
/**
* A type representing an atomic unsigned 32-bit value.
*
* This can be used to manage a value that is synchronized across multiple
* CPUs without a race condition; when an app sets a value with
* SDL_SetAtomicU32 all other threads, regardless of the CPU it is running on,
* will see that value when retrieved with SDL_GetAtomicU32, regardless of CPU
* caches, etc.
*
* This is also useful for atomic compare-and-swap operations: a thread can
* change the value as long as its current value matches expectations. When
* done in a loop, one can guarantee data consistency across threads without a
* lock (but the usual warnings apply: if you don't know what you're doing, or
* you don't do it carefully, you can confidently cause any number of
* disasters with this, so in most cases, you _should_ use a mutex instead of
* this!).
*
* This is a struct so people don't accidentally use numeric operations on it
* directly. You have to use SDL atomic functions.
*
* \since This struct is available since SDL 3.2.0.
*
* \sa SDL_CompareAndSwapAtomicU32
* \sa SDL_GetAtomicU32
* \sa SDL_SetAtomicU32
*/
typedef struct SDL_AtomicU32 { Uint32 value; } SDL_AtomicU32;
/**
* Set an atomic variable to a new value if it is currently an old value.
*
* ***Note: If you don't know what this function is for, you shouldn't use
* it!***
*
* \param a a pointer to an SDL_AtomicU32 variable to be modified.
* \param oldval the old value.
* \param newval the new value.
* \returns true if the atomic variable was set, false otherwise.
*
* \threadsafety It is safe to call this function from any thread.
*
* \since This function is available since SDL 3.2.0.
*
* \sa SDL_GetAtomicU32
* \sa SDL_SetAtomicU32
*/
extern SDL_DECLSPEC bool SDLCALL SDL_CompareAndSwapAtomicU32(SDL_AtomicU32 *a, Uint32 oldval, Uint32 newval);
/**
* Set an atomic variable to a value.
*
* This function also acts as a full memory barrier.
*
* ***Note: If you don't know what this function is for, you shouldn't use
* it!***
*
* \param a a pointer to an SDL_AtomicU32 variable to be modified.
* \param v the desired value.
* \returns the previous value of the atomic variable.
*
* \threadsafety It is safe to call this function from any thread.
*
* \since This function is available since SDL 3.2.0.
*
* \sa SDL_GetAtomicU32
*/
extern SDL_DECLSPEC Uint32 SDLCALL SDL_SetAtomicU32(SDL_AtomicU32 *a, Uint32 v);
/**
* Get the value of an atomic variable.
*
* ***Note: If you don't know what this function is for, you shouldn't use
* it!***
*
* \param a a pointer to an SDL_AtomicU32 variable.
* \returns the current value of an atomic variable.
*
* \threadsafety It is safe to call this function from any thread.
*
* \since This function is available since SDL 3.2.0.
*
* \sa SDL_SetAtomicU32
*/
extern SDL_DECLSPEC Uint32 SDLCALL SDL_GetAtomicU32(SDL_AtomicU32 *a);
/**
* Add to an atomic variable.
*
* This function also acts as a full memory barrier.
*
* ***Note: If you don't know what this function is for, you shouldn't use
* it!***
*
* \param a a pointer to an SDL_AtomicU32 variable to be modified.
* \param v the desired value to add or subtract.
* \returns the previous value of the atomic variable.
*
* \threadsafety It is safe to call this function from any thread.
*
* \since This function is available since SDL 3.4.0.
*/
extern SDL_DECLSPEC Uint32 SDLCALL SDL_AddAtomicU32(SDL_AtomicU32 *a, int v);
/**
* Set a pointer to a new value if it is currently an old value.
*
* ***Note: If you don't know what this function is for, you shouldn't use
* it!***
*
* \param a a pointer to a pointer.
* \param oldval the old pointer value.
* \param newval the new pointer value.
* \returns true if the pointer was set, false otherwise.
*
* \threadsafety It is safe to call this function from any thread.
*
* \since This function is available since SDL 3.2.0.
*
* \sa SDL_CompareAndSwapAtomicInt
* \sa SDL_GetAtomicPointer
* \sa SDL_SetAtomicPointer
*/
extern SDL_DECLSPEC bool SDLCALL SDL_CompareAndSwapAtomicPointer(void **a, void *oldval, void *newval);
/**
* Set a pointer to a value atomically.
*
* ***Note: If you don't know what this function is for, you shouldn't use
* it!***
*
* \param a a pointer to a pointer.
* \param v the desired pointer value.
* \returns the previous value of the pointer.
*
* \threadsafety It is safe to call this function from any thread.
*
* \since This function is available since SDL 3.2.0.
*
* \sa SDL_CompareAndSwapAtomicPointer
* \sa SDL_GetAtomicPointer
*/
extern SDL_DECLSPEC void * SDLCALL SDL_SetAtomicPointer(void **a, void *v);
/**
* Get the value of a pointer atomically.
*
* ***Note: If you don't know what this function is for, you shouldn't use
* it!***
*
* \param a a pointer to a pointer.
* \returns the current value of a pointer.
*
* \threadsafety It is safe to call this function from any thread.
*
* \since This function is available since SDL 3.2.0.
*
* \sa SDL_CompareAndSwapAtomicPointer
* \sa SDL_SetAtomicPointer
*/
extern SDL_DECLSPEC void * SDLCALL SDL_GetAtomicPointer(void **a);
/* Ends C function definitions when using C++ */
#ifdef __cplusplus
}
#endif
#include <SDL3/SDL_close_code.h>
#endif /* SDL_atomic_h_ */

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,553 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2026 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
/* WIKI CATEGORY: BeginCode */
/**
* # CategoryBeginCode
*
* `SDL_begin_code.h` sets things up for C dynamic library function
* definitions, static inlined functions, and structures aligned at 4-byte
* alignment. If you don't like ugly C preprocessor code, don't look at this
* file. :)
*
* SDL's headers use this; applications generally should not include this
* header directly.
*/
/* This shouldn't be nested -- included it around code only. */
#ifdef SDL_begin_code_h
#error Nested inclusion of SDL_begin_code.h
#endif
#define SDL_begin_code_h
#ifdef SDL_WIKI_DOCUMENTATION_SECTION
/**
* A macro to tag a symbol as deprecated.
*
* A function is marked deprecated by adding this macro to its declaration:
*
* ```c
* extern SDL_DEPRECATED int ThisFunctionWasABadIdea(void);
* ```
*
* Compilers with deprecation support can give a warning when a deprecated
* function is used. This symbol may be used in SDL's headers, but apps are
* welcome to use it for their own interfaces as well.
*
* SDL, on occasion, might deprecate a function for various reasons. However,
* SDL never removes symbols before major versions, so deprecated interfaces
* in SDL3 will remain available until SDL4, where it would be expected an app
* would have to take steps to migrate anyhow.
*
* On compilers without a deprecation mechanism, this is defined to nothing,
* and using a deprecated function will not generate a warning.
*
* \since This macro is available since SDL 3.2.0.
*/
#define SDL_DEPRECATED __attribute__((deprecated))
/**
* A macro to tag a symbol as a public API.
*
* SDL uses this macro for all its public functions. On some targets, it is
* used to signal to the compiler that this function needs to be exported from
* a shared library, but it might have other side effects.
*
* This symbol is used in SDL's headers, but apps and other libraries are
* welcome to use it for their own interfaces as well.
*
* \since This macro is available since SDL 3.2.0.
*/
#define SDL_DECLSPEC __attribute__ ((visibility("default")))
/**
* A macro to set a function's calling conventions.
*
* SDL uses this macro for all its public functions, and any callbacks it
* defines. This macro guarantees that calling conventions match between SDL
* and the app, even if the two were built with different compilers or
* optimization settings.
*
* When writing a callback function, it is very important for it to be
* correctly tagged with SDLCALL, as mismatched calling conventions can cause
* strange behaviors and can be difficult to diagnose. Plus, on many
* platforms, SDLCALL is defined to nothing, so compilers won't be able to
* warn that the tag is missing.
*
* This symbol is used in SDL's headers, but apps and other libraries are
* welcome to use it for their own interfaces as well.
*
* \since This macro is available since SDL 3.2.0.
*/
#define SDLCALL __cdecl
/**
* A macro to request a function be inlined.
*
* This is a hint to the compiler to inline a function. The compiler is free
* to ignore this request. On compilers without inline support, this is
* defined to nothing.
*
* \since This macro is available since SDL 3.2.0.
*/
#define SDL_INLINE __inline
/**
* A macro to demand a function be inlined.
*
* This is a command to the compiler to inline a function. SDL uses this macro
* in its public headers for a handful of simple functions. On compilers
* without forceinline support, this is defined to `static SDL_INLINE`, which
* is often good enough.
*
* This symbol is used in SDL's headers, but apps and other libraries are
* welcome to use it for their own interfaces as well.
*
* \since This macro is available since SDL 3.2.0.
*/
#define SDL_FORCE_INLINE __forceinline
/**
* A macro to tag a function as never-returning.
*
* This is a hint to the compiler that a function does not return. An example
* of a function like this is the C runtime's exit() function.
*
* This hint can lead to code optimizations, and help analyzers understand
* code flow better. On compilers without noreturn support, this is defined to
* nothing.
*
* This symbol is used in SDL's headers, but apps and other libraries are
* welcome to use it for their own interfaces as well.
*
* \since This macro is available since SDL 3.2.0.
*/
#define SDL_NORETURN __attribute__((noreturn))
/**
* A macro to tag a function as never-returning (for analysis purposes).
*
* This is almost identical to SDL_NORETURN, except functions marked with this
* _can_ actually return. The difference is that this isn't used for code
* generation, but rather static analyzers use this information to assume
* truths about program state and available code paths. Specifically, this tag
* is useful for writing an assertion mechanism. Indeed, SDL_assert uses this
* tag behind the scenes. Generally, apps that don't understand the specific
* use-case for this tag should avoid using it directly.
*
* On compilers without analyzer_noreturn support, this is defined to nothing.
*
* This symbol is used in SDL's headers, but apps and other libraries are
* welcome to use it for their own interfaces as well.
*
* \since This macro is available since SDL 3.2.0.
*/
#define SDL_ANALYZER_NORETURN __attribute__((analyzer_noreturn))
/**
* A macro to signal that a case statement without a `break` is intentional.
*
* C compilers have gotten more aggressive about warning when a switch's
* `case` block does not end with a `break` or other flow control statement,
* flowing into the next case's code, as this is a common accident that leads
* to strange bugs. But sometimes falling through to the next case is the
* correct and desired behavior. This symbol lets an app communicate this
* intention to the compiler, so it doesn't generate a warning.
*
* It is used like this:
*
* ```c
* switch (x) {
* case 1:
* DoSomethingOnlyForOne();
* SDL_FALLTHROUGH; // tell the compiler this was intentional.
* case 2:
* DoSomethingForOneAndTwo();
* break;
* }
* ```
*
* \since This macro is available since SDL 3.2.0.
*/
#define SDL_FALLTHROUGH [[fallthrough]]
/**
* A macro to tag a function's return value as critical.
*
* This is a hint to the compiler that a function's return value should not be
* ignored.
*
* If an NODISCARD function's return value is thrown away (the function is
* called as if it returns `void`), the compiler will issue a warning.
*
* While it's generally good practice to check return values for errors, often
* times legitimate programs do not for good reasons. Be careful about what
* functions are tagged as NODISCARD. It operates best when used on a function
* that's failure is surprising and catastrophic; a good example would be a
* program that checks the return values of all its file write function calls
* but not the call to close the file, which it assumes incorrectly never
* fails.
*
* Function callers that want to throw away a NODISCARD return value can call
* the function with a `(void)` cast, which informs the compiler the act is
* intentional.
*
* On compilers without nodiscard support, this is defined to nothing.
*
* \since This macro is available since SDL 3.2.0.
*/
#define SDL_NODISCARD [[nodiscard]]
/**
* A macro to tag a function as an allocator.
*
* This is a hint to the compiler that a function is an allocator, like
* malloc(), with certain rules. A description of how GCC treats this hint is
* here:
*
* https://gcc.gnu.org/onlinedocs/gcc/Common-Function-Attributes.html#index-malloc-function-attribute
*
* On compilers without allocator tag support, this is defined to nothing.
*
* Most apps don't need to, and should not, use this directly.
*
* \since This macro is available since SDL 3.2.0.
*/
#define SDL_MALLOC __declspec(allocator) __desclspec(restrict)
/**
* A macro to tag a function as returning a certain allocation.
*
* This is a hint to the compiler that a function allocates and returns a
* specific amount of memory based on one of its arguments. For example, the C
* runtime's malloc() function could use this macro with an argument of 1
* (first argument to malloc is the size of the allocation).
*
* On compilers without alloc_size support, this is defined to nothing.
*
* Most apps don't need to, and should not, use this directly.
*
* \since This macro is available since SDL 3.2.0.
*/
#define SDL_ALLOC_SIZE(p) __attribute__((alloc_size(p)))
/**
* A macro to tag a pointer variable, to help with pointer aliasing.
*
* A good explanation of the restrict keyword is here:
*
* https://en.wikipedia.org/wiki/Restrict
*
* On compilers without restrict support, this is defined to nothing.
*
* \since This macro is available since SDL 3.4.0.
*/
#define SDL_RESTRICT __restrict
/**
* Check if the compiler supports a given builtin functionality.
*
* This allows preprocessor checks for things that otherwise might fail to
* compile.
*
* Supported by virtually all clang versions and more-recent GCCs. Use this
* instead of checking the clang version if possible.
*
* On compilers without has_builtin support, this is defined to 0 (always
* false).
*
* \since This macro is available since SDL 3.2.0.
*/
#define SDL_HAS_BUILTIN(x) __has_builtin(x)
/**
* A macro to specify data alignment.
*
* This informs the compiler that a given datatype or variable must be aligned
* to a specific byte count.
*
* For example:
*
* ```c
* // make sure this is struct is aligned to 16 bytes for SIMD access.
* typedef struct {
* float x, y, z, w;
* } SDL_ALIGNED(16) MySIMDAlignedData;
*
* // make sure this one field in a struct is aligned to 16 bytes for SIMD access.
* typedef struct {
* SomeStuff stuff;
* float SDL_ALIGNED(16) position[4];
* SomeOtherStuff other_stuff;
* } MyStruct;
*
* // make sure this variable is aligned to 32 bytes.
* int SDL_ALIGNED(32) myval = 0;
* ```
*
* Alignment is only guaranteed for things the compiler places: local
* variables on the stack and global/static variables. To dynamically allocate
* something that respects this alignment, use SDL_aligned_alloc() or some
* other mechanism.
*
* On compilers without alignment support, this macro is defined to an invalid
* symbol, to make it clear that the current compiler is likely to generate
* incorrect code when it sees this macro.
*
* \param x the byte count to align to, so the data's address will be a
* multiple of this value.
*
* \since This macro is available since SDL 3.4.0.
*/
#define SDL_ALIGNED(x) __attribute__((aligned(x)))
/* end of wiki documentation section. */
#endif
/* `restrict` is from C99, but __restrict works with both Visual Studio and GCC. */
#ifndef SDL_RESTRICT
# if defined(restrict) || ((defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)))
# define SDL_RESTRICT restrict
# elif defined(_MSC_VER) || defined(__GNUC__) || defined(__clang__)
# define SDL_RESTRICT __restrict
# else
# define SDL_RESTRICT
# endif
#endif
#ifndef SDL_HAS_BUILTIN
#ifdef __has_builtin
#define SDL_HAS_BUILTIN(x) __has_builtin(x)
#else
#define SDL_HAS_BUILTIN(x) 0
#endif
#endif
#ifndef SDL_DEPRECATED
# if defined(__GNUC__) && (__GNUC__ >= 4) /* technically, this arrived in gcc 3.1, but oh well. */
# define SDL_DEPRECATED __attribute__((deprecated))
# elif defined(_MSC_VER)
# define SDL_DEPRECATED __declspec(deprecated)
# else
# define SDL_DEPRECATED
# endif
#endif
#ifndef SDL_UNUSED
# ifdef __GNUC__
# define SDL_UNUSED __attribute__((unused))
# else
# define SDL_UNUSED
# endif
#endif
/* Some compilers use a special export keyword */
#ifndef SDL_DECLSPEC
# if defined(SDL_PLATFORM_WINDOWS)
# ifdef DLL_EXPORT
# define SDL_DECLSPEC __declspec(dllexport)
# else
# define SDL_DECLSPEC
# endif
# else
# if defined(__GNUC__) && __GNUC__ >= 4
# define SDL_DECLSPEC __attribute__ ((visibility("default")))
# else
# define SDL_DECLSPEC
# endif
# endif
#endif
/* By default SDL uses the C calling convention */
#ifndef SDLCALL
#if defined(SDL_PLATFORM_WINDOWS) && !defined(__GNUC__)
#define SDLCALL __cdecl
#else
#define SDLCALL
#endif
#endif /* SDLCALL */
/* Force structure packing at 4 byte alignment.
This is necessary if the header is included in code which has structure
packing set to an alternate value, say for loading structures from disk.
The packing is reset to the previous value in SDL_close_code.h
*/
#if defined(_MSC_VER) || defined(__MWERKS__) || defined(__BORLANDC__)
#ifdef _MSC_VER
#pragma warning(disable: 4103)
#endif
#ifdef __clang__
#pragma clang diagnostic ignored "-Wpragma-pack"
#endif
#ifdef __BORLANDC__
#pragma nopackwarning
#endif
#ifdef _WIN64
/* Use 8-byte alignment on 64-bit architectures, so pointers are aligned */
#pragma pack(push,8)
#else
#pragma pack(push,4)
#endif
#endif /* Compiler needs structure packing set */
#ifndef SDL_INLINE
#ifdef __GNUC__
#define SDL_INLINE __inline__
#elif defined(_MSC_VER) || defined(__BORLANDC__) || \
defined(__DMC__) || defined(__SC__) || \
defined(__WATCOMC__) || defined(__LCC__) || \
defined(__DECC) || defined(__CC_ARM)
#define SDL_INLINE __inline
#ifndef __inline__
#define __inline__ __inline
#endif
#else
#define SDL_INLINE inline
#ifndef __inline__
#define __inline__ inline
#endif
#endif
#endif /* SDL_INLINE not defined */
#ifndef SDL_FORCE_INLINE
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
#define SDL_FORCE_INLINE __forceinline
#elif ( (defined(__GNUC__) && (__GNUC__ >= 4)) || defined(__clang__) )
#define SDL_FORCE_INLINE __attribute__((always_inline)) static __inline__
#else
#define SDL_FORCE_INLINE static SDL_INLINE
#endif
#endif /* SDL_FORCE_INLINE not defined */
#ifndef SDL_NORETURN
#if defined(__GNUC__)
#define SDL_NORETURN __attribute__((noreturn))
#elif defined(_MSC_VER)
#define SDL_NORETURN __declspec(noreturn)
#else
#define SDL_NORETURN
#endif
#endif /* SDL_NORETURN not defined */
#ifdef __clang__
#if __has_feature(attribute_analyzer_noreturn)
#define SDL_ANALYZER_NORETURN __attribute__((analyzer_noreturn))
#endif
#endif
#ifndef SDL_ANALYZER_NORETURN
#define SDL_ANALYZER_NORETURN
#endif
/* Apparently this is needed by several Windows compilers */
#ifndef __MACH__
#ifndef NULL
#ifdef __cplusplus
#define NULL 0
#else
#define NULL ((void *)0)
#endif
#endif /* NULL */
#endif /* ! macOS - breaks precompiled headers */
#ifndef SDL_FALLTHROUGH
#if (defined(__cplusplus) && __cplusplus >= 201703L) || \
(defined(__STDC_VERSION__) && __STDC_VERSION__ >= 202000L)
#define SDL_FALLTHROUGH [[fallthrough]]
#else
#if defined(__has_attribute) && !defined(__SUNPRO_C) && !defined(__SUNPRO_CC)
#define SDL_HAS_FALLTHROUGH __has_attribute(__fallthrough__)
#else
#define SDL_HAS_FALLTHROUGH 0
#endif /* __has_attribute */
#if SDL_HAS_FALLTHROUGH && \
((defined(__GNUC__) && __GNUC__ >= 7) || \
(defined(__clang_major__) && __clang_major__ >= 10))
#define SDL_FALLTHROUGH __attribute__((__fallthrough__))
#else
#define SDL_FALLTHROUGH do {} while (0) /* fallthrough */
#endif /* SDL_HAS_FALLTHROUGH */
#undef SDL_HAS_FALLTHROUGH
#endif /* C++17 or C2x */
#endif /* SDL_FALLTHROUGH not defined */
#ifndef SDL_NODISCARD
#if (defined(__cplusplus) && __cplusplus >= 201703L) || \
(defined(__STDC_VERSION__) && __STDC_VERSION__ >= 202311L)
#define SDL_NODISCARD [[nodiscard]]
#elif ( (defined(__GNUC__) && (__GNUC__ >= 4)) || defined(__clang__) )
#define SDL_NODISCARD __attribute__((warn_unused_result))
#elif defined(_MSC_VER) && (_MSC_VER >= 1700)
#define SDL_NODISCARD _Check_return_
#else
#define SDL_NODISCARD
#endif /* C++17 or C23 */
#endif /* SDL_NODISCARD not defined */
#ifndef SDL_MALLOC
#if defined(__GNUC__) && (__GNUC__ >= 3)
#define SDL_MALLOC __attribute__((malloc))
/** FIXME
#elif defined(_MSC_VER)
#define SDL_MALLOC __declspec(allocator) __desclspec(restrict)
**/
#else
#define SDL_MALLOC
#endif
#endif /* SDL_MALLOC not defined */
#ifndef SDL_ALLOC_SIZE
#if (defined(__clang__) && __clang_major__ >= 4) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3)))
#define SDL_ALLOC_SIZE(p) __attribute__((alloc_size(p)))
#elif defined(_MSC_VER)
#define SDL_ALLOC_SIZE(p)
#else
#define SDL_ALLOC_SIZE(p)
#endif
#endif /* SDL_ALLOC_SIZE not defined */
#ifndef SDL_ALLOC_SIZE2
#if (defined(__clang__) && __clang_major__ >= 4) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3)))
#define SDL_ALLOC_SIZE2(p1, p2) __attribute__((alloc_size(p1, p2)))
#elif defined(_MSC_VER)
#define SDL_ALLOC_SIZE2(p1, p2)
#else
#define SDL_ALLOC_SIZE2(p1, p2)
#endif
#endif /* SDL_ALLOC_SIZE2 not defined */
#ifndef SDL_ALIGNED
#if defined(__clang__) || defined(__GNUC__)
#define SDL_ALIGNED(x) __attribute__((aligned(x)))
#elif defined(_MSC_VER)
#define SDL_ALIGNED(x) __declspec(align(x))
#elif defined(__cplusplus) && (__cplusplus >= 201103L)
#define SDL_ALIGNED(x) alignas(x)
#elif defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L)
#define SDL_ALIGNED(x) _Alignas(x)
#else
#define SDL_ALIGNED(x) PLEASE_DEFINE_SDL_ALIGNED
#endif
#endif /* SDL_ALIGNED not defined */

Some files were not shown because too many files have changed in this diff Show More