11 Commits

195 changed files with 88105 additions and 539 deletions

View File

@@ -1,2 +1,5 @@
# TSE
packages needed for building under linux:
build-essential llvm cmake ninja-build clang mesa-common-dev gdb

388
TSE_Base/include/picosha2.h Normal file
View File

@@ -0,0 +1,388 @@
/*
The MIT License (MIT)
Copyright (C) 2017 okdshin
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
#ifndef PICOSHA2_H
#define PICOSHA2_H
// picosha2:20140213
#ifndef PICOSHA2_BUFFER_SIZE_FOR_INPUT_ITERATOR
#define PICOSHA2_BUFFER_SIZE_FOR_INPUT_ITERATOR \
1048576 //=1024*1024: default is 1MB memory
#endif
#include <algorithm>
#include <cassert>
#include <iterator>
#include <sstream>
#include <vector>
#include <fstream>
namespace picosha2 {
typedef unsigned long word_t;
typedef unsigned char byte_t;
static const size_t k_digest_size = 32;
namespace detail {
inline byte_t mask_8bit(byte_t x) { return x & 0xff; }
inline word_t mask_32bit(word_t x) { return x & 0xffffffff; }
const word_t add_constant[64] = {
0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1,
0x923f82a4, 0xab1c5ed5, 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3,
0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, 0xe49b69c1, 0xefbe4786,
0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147,
0x06ca6351, 0x14292967, 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13,
0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, 0xa2bfe8a1, 0xa81a664b,
0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a,
0x5b9cca4f, 0x682e6ff3, 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208,
0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2};
const word_t initial_message_digest[8] = {0x6a09e667, 0xbb67ae85, 0x3c6ef372,
0xa54ff53a, 0x510e527f, 0x9b05688c,
0x1f83d9ab, 0x5be0cd19};
inline word_t ch(word_t x, word_t y, word_t z) { return (x & y) ^ ((~x) & z); }
inline word_t maj(word_t x, word_t y, word_t z) {
return (x & y) ^ (x & z) ^ (y & z);
}
inline word_t rotr(word_t x, std::size_t n) {
assert(n < 32);
return mask_32bit((x >> n) | (x << (32 - n)));
}
inline word_t bsig0(word_t x) { return rotr(x, 2) ^ rotr(x, 13) ^ rotr(x, 22); }
inline word_t bsig1(word_t x) { return rotr(x, 6) ^ rotr(x, 11) ^ rotr(x, 25); }
inline word_t shr(word_t x, std::size_t n) {
assert(n < 32);
return x >> n;
}
inline word_t ssig0(word_t x) { return rotr(x, 7) ^ rotr(x, 18) ^ shr(x, 3); }
inline word_t ssig1(word_t x) { return rotr(x, 17) ^ rotr(x, 19) ^ shr(x, 10); }
template <typename RaIter1, typename RaIter2>
void hash256_block(RaIter1 message_digest, RaIter2 first, RaIter2 last) {
assert(first + 64 == last);
static_cast<void>(last); // for avoiding unused-variable warning
word_t w[64];
std::fill(w, w + 64, word_t(0));
for (std::size_t i = 0; i < 16; ++i) {
w[i] = (static_cast<word_t>(mask_8bit(*(first + i * 4))) << 24) |
(static_cast<word_t>(mask_8bit(*(first + i * 4 + 1))) << 16) |
(static_cast<word_t>(mask_8bit(*(first + i * 4 + 2))) << 8) |
(static_cast<word_t>(mask_8bit(*(first + i * 4 + 3))));
}
for (std::size_t i = 16; i < 64; ++i) {
w[i] = mask_32bit(ssig1(w[i - 2]) + w[i - 7] + ssig0(w[i - 15]) +
w[i - 16]);
}
word_t a = *message_digest;
word_t b = *(message_digest + 1);
word_t c = *(message_digest + 2);
word_t d = *(message_digest + 3);
word_t e = *(message_digest + 4);
word_t f = *(message_digest + 5);
word_t g = *(message_digest + 6);
word_t h = *(message_digest + 7);
for (std::size_t i = 0; i < 64; ++i) {
word_t temp1 = h + bsig1(e) + ch(e, f, g) + add_constant[i] + w[i];
word_t temp2 = bsig0(a) + maj(a, b, c);
h = g;
g = f;
f = e;
e = mask_32bit(d + temp1);
d = c;
c = b;
b = a;
a = mask_32bit(temp1 + temp2);
}
*message_digest += a;
*(message_digest + 1) += b;
*(message_digest + 2) += c;
*(message_digest + 3) += d;
*(message_digest + 4) += e;
*(message_digest + 5) += f;
*(message_digest + 6) += g;
*(message_digest + 7) += h;
for (std::size_t i = 0; i < 8; ++i) {
*(message_digest + i) = mask_32bit(*(message_digest + i));
}
}
} // namespace detail
template <typename InIter>
void output_hex(InIter first, InIter last, std::ostream& os) {
os.setf(std::ios::hex, std::ios::basefield);
while (first != last) {
os.width(2);
os.fill('0');
os << static_cast<unsigned int>(*first);
++first;
}
os.setf(std::ios::dec, std::ios::basefield);
}
template <typename InIter>
void bytes_to_hex_string(InIter first, InIter last, std::string& hex_str) {
std::ostringstream oss;
output_hex(first, last, oss);
hex_str.assign(oss.str());
}
template <typename InContainer>
void bytes_to_hex_string(const InContainer& bytes, std::string& hex_str) {
bytes_to_hex_string(bytes.begin(), bytes.end(), hex_str);
}
template <typename InIter>
std::string bytes_to_hex_string(InIter first, InIter last) {
std::string hex_str;
bytes_to_hex_string(first, last, hex_str);
return hex_str;
}
template <typename InContainer>
std::string bytes_to_hex_string(const InContainer& bytes) {
std::string hex_str;
bytes_to_hex_string(bytes, hex_str);
return hex_str;
}
class hash256_one_by_one {
public:
hash256_one_by_one() { init(); }
void init() {
buffer_.clear();
std::fill(data_length_digits_, data_length_digits_ + 4, word_t(0));
std::copy(detail::initial_message_digest,
detail::initial_message_digest + 8, h_);
}
template <typename RaIter>
void process(RaIter first, RaIter last) {
add_to_data_length(static_cast<word_t>(std::distance(first, last)));
std::copy(first, last, std::back_inserter(buffer_));
std::size_t i = 0;
for (; i + 64 <= buffer_.size(); i += 64) {
detail::hash256_block(h_, buffer_.begin() + i,
buffer_.begin() + i + 64);
}
buffer_.erase(buffer_.begin(), buffer_.begin() + i);
}
void finish() {
byte_t temp[64];
std::fill(temp, temp + 64, byte_t(0));
std::size_t remains = buffer_.size();
std::copy(buffer_.begin(), buffer_.end(), temp);
assert(remains < 64);
// This branch is not executed actually (`remains` is always lower than 64),
// but needed to avoid g++ false-positive warning.
// See https://github.com/okdshin/PicoSHA2/issues/25
// vvvvvvvvvvvvvvvv
if(remains >= 64) {
remains = 63;
}
// ^^^^^^^^^^^^^^^^
temp[remains] = 0x80;
if (remains > 55) {
std::fill(temp + remains + 1, temp + 64, byte_t(0));
detail::hash256_block(h_, temp, temp + 64);
std::fill(temp, temp + 64 - 4, byte_t(0));
} else {
std::fill(temp + remains + 1, temp + 64 - 4, byte_t(0));
}
write_data_bit_length(&(temp[56]));
detail::hash256_block(h_, temp, temp + 64);
}
template <typename OutIter>
void get_hash_bytes(OutIter first, OutIter last) const {
for (const word_t* iter = h_; iter != h_ + 8; ++iter) {
for (std::size_t i = 0; i < 4 && first != last; ++i) {
*(first++) = detail::mask_8bit(
static_cast<byte_t>((*iter >> (24 - 8 * i))));
}
}
}
private:
void add_to_data_length(word_t n) {
word_t carry = 0;
data_length_digits_[0] += n;
for (std::size_t i = 0; i < 4; ++i) {
data_length_digits_[i] += carry;
if (data_length_digits_[i] >= 65536u) {
carry = data_length_digits_[i] >> 16;
data_length_digits_[i] &= 65535u;
} else {
break;
}
}
}
void write_data_bit_length(byte_t* begin) {
word_t data_bit_length_digits[4];
std::copy(data_length_digits_, data_length_digits_ + 4,
data_bit_length_digits);
// convert byte length to bit length (multiply 8 or shift 3 times left)
word_t carry = 0;
for (std::size_t i = 0; i < 4; ++i) {
word_t before_val = data_bit_length_digits[i];
data_bit_length_digits[i] <<= 3;
data_bit_length_digits[i] |= carry;
data_bit_length_digits[i] &= 65535u;
carry = (before_val >> (16 - 3)) & 65535u;
}
// write data_bit_length
for (int i = 3; i >= 0; --i) {
(*begin++) = static_cast<byte_t>(data_bit_length_digits[i] >> 8);
(*begin++) = static_cast<byte_t>(data_bit_length_digits[i]);
}
}
std::vector<byte_t> buffer_;
word_t data_length_digits_[4]; // as 64bit integer (16bit x 4 integer)
word_t h_[8];
};
inline void get_hash_hex_string(const hash256_one_by_one& hasher,
std::string& hex_str) {
byte_t hash[k_digest_size];
hasher.get_hash_bytes(hash, hash + k_digest_size);
return bytes_to_hex_string(hash, hash + k_digest_size, hex_str);
}
inline std::string get_hash_hex_string(const hash256_one_by_one& hasher) {
std::string hex_str;
get_hash_hex_string(hasher, hex_str);
return hex_str;
}
namespace impl {
template <typename RaIter, typename OutIter>
void hash256_impl(RaIter first, RaIter last, OutIter first2, OutIter last2, int,
std::random_access_iterator_tag) {
hash256_one_by_one hasher;
// hasher.init();
hasher.process(first, last);
hasher.finish();
hasher.get_hash_bytes(first2, last2);
}
template <typename InputIter, typename OutIter>
void hash256_impl(InputIter first, InputIter last, OutIter first2,
OutIter last2, int buffer_size, std::input_iterator_tag) {
std::vector<byte_t> buffer(buffer_size);
hash256_one_by_one hasher;
// hasher.init();
while (first != last) {
int size = buffer_size;
for (int i = 0; i != buffer_size; ++i, ++first) {
if (first == last) {
size = i;
break;
}
buffer[i] = *first;
}
hasher.process(buffer.begin(), buffer.begin() + size);
}
hasher.finish();
hasher.get_hash_bytes(first2, last2);
}
}
template <typename InIter, typename OutIter>
void hash256(InIter first, InIter last, OutIter first2, OutIter last2,
int buffer_size = PICOSHA2_BUFFER_SIZE_FOR_INPUT_ITERATOR) {
picosha2::impl::hash256_impl(
first, last, first2, last2, buffer_size,
typename std::iterator_traits<InIter>::iterator_category());
}
template <typename InIter, typename OutContainer>
void hash256(InIter first, InIter last, OutContainer& dst) {
hash256(first, last, dst.begin(), dst.end());
}
template <typename InContainer, typename OutIter>
void hash256(const InContainer& src, OutIter first, OutIter last) {
hash256(src.begin(), src.end(), first, last);
}
template <typename InContainer, typename OutContainer>
void hash256(const InContainer& src, OutContainer& dst) {
hash256(src.begin(), src.end(), dst.begin(), dst.end());
}
template <typename InIter>
void hash256_hex_string(InIter first, InIter last, std::string& hex_str) {
byte_t hashed[k_digest_size];
hash256(first, last, hashed, hashed + k_digest_size);
std::ostringstream oss;
output_hex(hashed, hashed + k_digest_size, oss);
hex_str.assign(oss.str());
}
template <typename InIter>
std::string hash256_hex_string(InIter first, InIter last) {
std::string hex_str;
hash256_hex_string(first, last, hex_str);
return hex_str;
}
inline void hash256_hex_string(const std::string& src, std::string& hex_str) {
hash256_hex_string(src.begin(), src.end(), hex_str);
}
template <typename InContainer>
void hash256_hex_string(const InContainer& src, std::string& hex_str) {
hash256_hex_string(src.begin(), src.end(), hex_str);
}
template <typename InContainer>
std::string hash256_hex_string(const InContainer& src) {
return hash256_hex_string(src.begin(), src.end());
}
template<typename OutIter>void hash256(std::ifstream& f, OutIter first, OutIter last){
hash256(std::istreambuf_iterator<char>(f), std::istreambuf_iterator<char>(), first,last);
}
}// namespace picosha2
#endif // PICOSHA2_H

View File

@@ -1,6 +1,7 @@
#include "Camera.hpp"
#include "elements/Transformable.hpp"
#include "interfaces/IRenderer.hpp"
#include "interfaces/IWindow.hpp"
#include "uuid.h"
TSE::Camera* TSE::Camera::mainCamera = nullptr;
@@ -101,8 +102,12 @@ void TSE::Camera::SetRenderTarget(IRenderTarget *target)
if(target != nullptr)
target->AddResizeNotifiable(this);
rt = target;
if(lastRtSize != rt->GetRawIResizableSize())
{
lastRtSize = rt->GetRawIResizableSize();
RecalculateProjMatrix();
}
}
TSE::IRenderTarget *TSE::Camera::GetRenderTarget()
{
@@ -208,6 +213,9 @@ void TSE::Camera::Unbind()
}
void TSE::Camera::UpdateRenderTarget()
{
if (dynamic_cast<IWindow*>(rt) == nullptr)
{
rt->Update();
}
}

View File

@@ -14,3 +14,8 @@ TSE::Mesh *TSE::MeshContainer::GetMesh() const
{
return mesh;
}
TSE::Mesh **TSE::MeshContainer::GetMeshRef()
{
return &mesh;
}

View File

@@ -3,7 +3,7 @@
#define MESH_CONTAINER typeid(TSE::MeshContainer).name()
#include "Types.hpp"
#include "Mesh.hpp"
#include "elements/Mesh.hpp"
#include "elements/BehaviourScript.hpp"
namespace TSE
@@ -19,6 +19,7 @@ namespace TSE
void SetMesh(Mesh* mesh);
Mesh* GetMesh() const;
Mesh** GetMeshRef();
inline const char* GetName() override
{

View File

@@ -1,6 +1,8 @@
#include "OrdererSpriteSet.hpp"
#include <algorithm>
#include <tuple>
#include <cmath>
#include "Debug.hpp"
TSE::OrdererSpriteSetChunk::OrdererSpriteSetChunk(int _chunksize, const Vector2 &_pos, SortingOrder _order)
{
@@ -144,6 +146,8 @@ void TSE::OrdererSpriteSet::SetSprite(Vector2 p, Vector2 Spriteindex, float heig
{
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;
@@ -175,7 +179,9 @@ const std::vector<TSE::Vector2> *TSE::OrdererSpriteSet::GetChunkPositionsInOrder
case TopLeft:
std::sort(orderedChunks.begin(), orderedChunks.end(), [](const Vector2& a, const Vector2& b)
{
return std::tie(a.y, a.x) > std::tie(b.y, b.x);
if (a.y != b.y)
return a.y > b.y;
return a.x < b.x;
});
break;
case TopRight:
@@ -189,7 +195,9 @@ const std::vector<TSE::Vector2> *TSE::OrdererSpriteSet::GetChunkPositionsInOrder
case BottomLeft:
std::sort(orderedChunks.begin(), orderedChunks.end(), [](const Vector2& a, const Vector2& b)
{
return std::tie(a.y, a.x) < std::tie(b.y, b.x);
if (a.y != b.y)
return a.y < b.y;
return a.x < b.x;
});
break;
case BottomRight:
@@ -230,7 +238,7 @@ void TSE::OrdererSpriteSet::DirtyAll()
TSE::Vector2 TSE::OrdererSpriteSet::LocalToChunkPos(const Vector2 &v)
{
Vector2 p = Vector2((int)v.x % chunkSize, (int)v.y % chunkSize);
Vector2 p = Vector2(std::fmod(v.x, chunkSize), std::fmod(v.y, chunkSize));
if(p.x < 0) p.x += chunkSize;
if(p.y < 0) p.y += chunkSize;
return p;

View File

@@ -50,7 +50,7 @@ namespace TSE
Rect bounds = Rect(0,0,0,0);
public:
int chunkSize = 16;
float chunkSize = 16;
SortingOrder order = TopRight;
TileSet* set;
std::unordered_map<Vector2, OrdererSpriteSetChunk> chunks;

View File

@@ -5,7 +5,7 @@
#include "elements/BehaviourScript.hpp"
#include "Rect.hpp"
#include "Vector2.hpp"
#include "Mesh.hpp"
#include "elements/Mesh.hpp"
namespace TSE
{

View File

@@ -22,6 +22,11 @@ namespace TSE
return material;
}
Material **Renderable::GetMaterialRef()
{
return &material;
}
Mesh* Renderable::GetMeshContainer() const {
if(baseObject->HasBehaviourScript(MESH_CONTAINER))
return dynamic_cast<MeshContainer*>(baseObject->GetBehaviourScript(MESH_CONTAINER))->GetMesh();

View File

@@ -5,7 +5,7 @@
#include "elements/Material.hpp"
#include "interfaces/IRenderable.hpp"
#include "elements/BehaviourScript.hpp"
#include "Mesh.hpp"
#include "elements/Mesh.hpp"
namespace TSE
{
@@ -28,6 +28,7 @@ namespace TSE
}
void SetMaterial(Material* material);
Material* GetMaterial();
Material** GetMaterialRef();
private:
Mesh* GetMeshContainer() const;

View File

@@ -0,0 +1,185 @@
#include "AssetLibrary.hpp"
#include "Material.hpp"
#include "Mesh.hpp"
#include "Texture.hpp"
#include <iostream>
#include <filesystem>
#include <algorithm>
#include "Debug.hpp"
std::unordered_map<uuids::uuid, std::tuple<std::any, TSE::string, uuids::uuid>> TSE::AssetLibrary::assets;
std::unordered_map<TSE::string, uuids::uuid> TSE::AssetLibrary::alreadyLoadedAssets;
TSE::string TSE::AssetLibrary::currentAssetPath;
void TSE::AssetLibrary::LoadDefaultAssets()
{
Mesh* quad = new Mesh(Mesh::GetQuadMesh());
quad->id = TSE_ID_MESH_QUAD;
quad->IsVirtualAsset = true;
Mesh* circle = new Mesh(Mesh::GetCircleMesh());
circle->id = TSE_ID_MESH_CIRCLE;
circle->IsVirtualAsset = true;
SetValue(TSE_ID_MESH_QUAD, quad);
SetValue(TSE_ID_MESH_CIRCLE, circle);
}
void TSE::AssetLibrary::RemoveValue(const uuids::uuid &key, IAsset *asset)
{
if(HasValue(key))
{
if(!asset->IsVirtualAsset)
{
alreadyLoadedAssets.erase(asset->rawPath);
}
assets.erase(key);
}
}
bool TSE::AssetLibrary::HasValue(const uuids::uuid &key)
{
return assets.find(key) != assets.end();
}
int TSE::AssetLibrary::GetValueCount()
{
return assets.size();
}
std::tuple<std::any, TSE::string, uuids::uuid> &TSE::AssetLibrary::GetValueAt(int j)
{
auto it = assets.begin();
for (int i = 0; i < j; i++)
{
it++;
}
return it->second;
}
void TSE::AssetLibrary::SaveAllAssets()
{
int count = GetValueCount();
for(int i = 0; i < count; i++)
{
std::tuple<std::any, string, uuids::uuid> tupel = GetValueAt(i);
auto[ptr, type, name] = tupel;
if (type == typeid(Material*).name())
{
Material* value = GetValue<Material*>(name);
if(!value->IsVirtualAsset)
value->SaveAsset();
}
if (type == typeid(Texture*).name())
{
Texture* value = GetValue<Texture*>(name);
if(!value->IsVirtualAsset)
value->SaveAsset();
}
if (type == typeid(Mesh*).name())
{
Mesh* value = GetValue<Mesh*>(name);
if(!value->IsVirtualAsset)
value->SaveAsset();
}
}
}
std::string ToLower(std::string str)
{
std::transform(str.begin(), str.end(), str.begin(),
[](unsigned char c)
{
return static_cast<char>(std::tolower(c));
});
return str;
}
void TSE::AssetLibrary::LoadAllAssets(string &path)
{
LoadDefaultAssets();
namespace fs = std::filesystem;
currentAssetPath = string(path);
if (!fs::exists(currentAssetPath) || !fs::is_directory(currentAssetPath))
{
TSE_ERROR("Invalid Asset Path: " + currentAssetPath);
return;
}
for (const auto& entry : fs::recursive_directory_iterator(currentAssetPath))
{
if (!entry.is_regular_file())
continue;
string ext = ToLower(entry.path().extension().string());
string path = entry.path().string();
if(alreadyLoadedAssets.find(path) != alreadyLoadedAssets.end()) continue;
if (ext == ".material")
{
Material* mat = new Material();
mat->LoadAsset(path);
SetValue(mat->id, mat);
alreadyLoadedAssets[path] = mat->id;
}
else if (ext == ".png" || ext == ".jpeg" || ext == ".jpg")
{
Texture* tex = new Texture(0,0);
tex->LoadAsset(path);
SetValue(tex->id, tex);
alreadyLoadedAssets[path] = tex->id;
}
else if (ext == ".obj")
{
Mesh* mesh = new Mesh();
mesh->LoadAsset(path);
SetValue(mesh->id, mesh);
alreadyLoadedAssets[path] = mesh->id;
}
}
}
void TSE::AssetLibrary::RescanAssets()
{
int count = GetValueCount();
for(int i = 0; i < count; i++)
{
std::tuple<std::any, string, uuids::uuid> tupel = GetValueAt(i);
auto[ptr, type, name] = tupel;
if (type == typeid(Material*).name())
{
Material* value = GetValue<Material*>(name);
if(!value->IsVirtualAsset)
value->Rescan();
}
if (type == typeid(Texture*).name())
{
Texture* value = GetValue<Texture*>(name);
if(!value->IsVirtualAsset)
value->Rescan();
}
if (type == typeid(Mesh*).name())
{
Mesh* value = GetValue<Mesh*>(name);
if(!value->IsVirtualAsset)
value->Rescan();
}
}
}
TSE::string TSE::AssetLibrary::GetCurrentAssetPath()
{
return currentAssetPath;
}
template void TSE::AssetLibrary::SetValue<TSE::Material>(const uuids::uuid&, TSE::Material*);
template void TSE::AssetLibrary::SetValue<TSE::Texture>(const uuids::uuid&, TSE::Texture*);
template void TSE::AssetLibrary::SetValue<TSE::Mesh>(const uuids::uuid&, TSE::Mesh*);
template TSE::Texture* TSE::AssetLibrary::GetValue<TSE::Texture*>(const uuids::uuid&);
template TSE::Material* TSE::AssetLibrary::GetValue<TSE::Material*>(const uuids::uuid&);
template TSE::Mesh* TSE::AssetLibrary::GetValue<TSE::Mesh*>(const uuids::uuid&);

View File

@@ -0,0 +1,56 @@
#pragma once
#include "interfaces/IAsset.hpp"
#include <unordered_map>
#include <any>
#include "uuid.h"
namespace TSE
{
const uuids::uuid TSE_ID_MESH_QUAD = uuids::uuid::from_string("00000000-0000-0000-1000-000000000000").value();
const uuids::uuid TSE_ID_MESH_CIRCLE = uuids::uuid::from_string("00000000-0000-0000-1001-000000000000").value();
class AssetLibrary
{
private:
static std::unordered_map<uuids::uuid, std::tuple<std::any, string, uuids::uuid>> assets;
static std::unordered_map<string, uuids::uuid> alreadyLoadedAssets;
static string currentAssetPath;
static void LoadDefaultAssets();
public:
template<typename T>
static T GetValue(const uuids::uuid& key);
template<typename T>
static void SetValue(const uuids::uuid& key, T* value);
static void RemoveValue(const uuids::uuid& key, IAsset* asset);
static bool HasValue(const uuids::uuid& key);
static int GetValueCount();
static std::tuple<std::any, string, uuids::uuid>& GetValueAt(int i);
static void SaveAllAssets();
static void LoadAllAssets(string& path);
static void RescanAssets();
static string GetCurrentAssetPath();
};
template<typename T>
T AssetLibrary::GetValue(const uuids::uuid& key) {
auto it = assets.find(key);
if (it == assets.end()) {
throw std::runtime_error("AssetLibrary::GetValue - key '" + uuids::to_string(key) + "' not found");
}
auto [a,b,c] = it->second;
return std::any_cast<T>(a);
}
template<typename T>
void AssetLibrary::SetValue(const uuids::uuid& key, T* value) {
assets[key] = std::make_tuple(value, typeid(T).name(), key);
}
}

View File

@@ -9,6 +9,11 @@
#include "Vector4.hpp"
#include "Color.hpp"
#include "Matrix4x4.hpp"
#include "PathHelper.hpp"
#include "utils/JsonExports.hpp"
#include "Debug.hpp"
#include "ShaderRegistry.hpp"
#include "AssetLibrary.hpp"
namespace TSE
{
@@ -18,7 +23,9 @@ namespace TSE
id = GenerateRandomUUID();
}
Material::Material(const string& name, IShader* shader, uuids::uuid id)
: name(name), shader(shader), id(id) {}
: name(name), shader(shader){
this->id = id;
}
Material::Material(const string &name, IShader *shader)
: name(name), shader(shader)
@@ -59,6 +66,266 @@ namespace TSE
return id;
}
bool Material::LoadAsset(string &path)
{
rawPath = string(path);
string metaPath = PathToMetaPath(rawPath);
using json = nlohmann::ordered_json;
std::ifstream rawStream;
OpenFileReading(rawStream, rawPath);
json material = json::parse(rawStream);
if(material["type"] != "material")
{
rawStream.close();
TSE_ERROR("Asset Loading Error (Material): not a material file = " + ((string)material["type"]));
return false;
}
if(material["version"] == 1)
{
name = material["name"];
shader = ShaderRegistry::GetShader(material["shader"]);
if (material.contains("properties"))
for (auto& [name, prop] : material["properties"].items())
{
std::string type = prop["type"];
auto& value = prop["value"];
if (type == "Int")
{
int v = value.get<int>();
SetValue(name, v);
}
else if (type == "Float")
{
float v = value.get<float>();
SetValue(name, v);
}
else if (type == "Vector2")
{
Vector2 v = ImportVector2(value);
SetValue(name, v);
}
else if (type == "Vector3")
{
Vector3 v = ImportVector3(value);
SetValue(name, v);
}
else if (type == "Vector4")
{
Vector4 v = ImportVector4(value);
SetValue(name, v);
}
else if (type == "Color")
{
Color v = ImportColor(value);
SetValue(name, v);
}
else if (type == "String")
{
string v = value.get<string>();
SetValue(name, v);
}
else if (type == "Uint")
{
uint v = value.get<uint>();
SetValue(name, v);
}
}
rawStream.close();
Hash();
rawStream.close();
if(FileExists(metaPath))
{
std::ifstream metaStream;
OpenFileReading(metaStream, metaPath);
json meta = json::parse(metaStream);
if(meta["type"] != "metadata")
{
metaStream.close();
TSE_WARNING("Rebuilding corrupted meta file (Material): " + metaPath);
SaveMeta();
return true;
}
if(meta["version"] == 1)
{
if(meta["assetType"] == "material")
{
id = uuids::uuid::from_string((string)meta["id"]).value_or(id);
//dependencies;
metaStream.close();
SaveMeta();
return true;
}
else
{
metaStream.close();
TSE_WARNING("Meta file incompatible with asset type. Please delete for auto regeneration or fix maualy (Material): " + metaPath);
return true;
}
}
else
{
metaStream.close();
TSE_WARNING("Meta file incompatible. Please delete for auto regeneration or fix maualy (Material): " + metaPath);
return true;
}
}
else
{
SaveMeta();
return true;
}
}
else
{
rawStream.close();
TSE_ERROR("Asset Loading Error (Material): incompatible asset version = " + std::to_string((int)material["version"]));
return false;
}
}
void Material::SaveAsset()
{
if(IsVirtualAsset) return;
using json = nlohmann::ordered_json;
json material = GenerateMaterialJson();
std::ofstream rawStream;
CreateFileWriting(rawStream, rawPath);
rawStream << material.dump(4) << std::endl;
rawStream.close();
Hash();
SaveMeta();
}
nlohmann::ordered_json Material::GenerateMaterialJson()
{
using json = nlohmann::ordered_json;
json material;
material["type"] = "material";
material["version"] = 1;
material["name"] = name;
material["shader"] = shader->GetName();
int count = GetValueCount();
for(int i = 0; i < count; i++)
{
std::tuple<std::any, std::string, std::string> tupel = GetValueAt(i);
auto[ptr, type, name] = tupel;
if (type == typeid(int).name())
{
int value = GetValue<int>(name);
material["properties"][name]["type"] = "Int";
material["properties"][name]["value"] = value;
}
else if (type == typeid(float).name())
{
float value = GetValue<float>(name);
material["properties"][name]["type"] = "Float";
material["properties"][name]["value"] = value;
}
else if (type == typeid(Vector2).name())
{
Vector2 value = GetValue<Vector2>(name);
material["properties"][name]["type"] = "Vector2";
material["properties"][name]["value"] = ExportVector2(value);
}
else if (type == typeid(Vector3).name())
{
Vector3 value = GetValue<Vector3>(name);
material["properties"][name]["type"] = "Vector3";
material["properties"][name]["value"] = ExportVector3(value);
}
else if (type == typeid(Vector4).name())
{
Vector4 value = GetValue<Vector4>(name);
material["properties"][name]["type"] = "Vector4";
material["properties"][name]["value"] = ExportVector4(value);
}
else if (type == typeid(Matrix4x4).name())
{
Matrix4x4 value = GetValue<Matrix4x4>(name);
//TODO: need to implement;
}
else if (type == typeid(Color).name())
{
Color value = GetValue<Color>(name);
material["properties"][name]["type"] = "Color";
material["properties"][name]["value"] = ExportColor(value);
}
else if (type == typeid(std::string).name())
{
std::string value = GetValue<std::string>(name);
material["properties"][name]["type"] = "String";
material["properties"][name]["value"] = value;
}
else if (type == typeid(Texture*).name())
{
Texture* value = GetValue<Texture*>(name);
//todo: implement
}
if (type == typeid(uint).name())
{
int value = GetValue<uint>(name);
material["properties"][name]["type"] = "Uint";
material["properties"][name]["value"] = value;
}
}
return material;
}
void Material::Hash()
{
using json = nlohmann::ordered_json;
json material = GenerateMaterialJson();
hash = sha256(material.dump(-1));
}
string Material::assetType()
{
return "material";
}
void Material::UnloadAsset()
{
AssetLibrary::RemoveValue(id, static_cast<IAsset*>(this));
delete(this);
}
void Material::SaveMeta()
{
string metaPath = PathToMetaPath(rawPath);
using json = nlohmann::ordered_json;
json meta;
meta["type"] = "metadata";
meta["assetType"] = "material";
meta["version"] = 1;
meta["source"] = rawPath; //todo: clamp to assets root
meta["id"] = uuids::to_string(id);
meta["hash"] = hash;
for (int i = 0; i < dependecies.size(); i++)
{
meta["dependencies"][i]["id"] = uuids::to_string(dependecies[i]->id);
meta["dependencies"][i]["path"] = dependecies[i]->rawPath; //todo: clamp to assets root
}
std::ofstream metaStream;
CreateFileWriting(metaStream, metaPath);
metaStream << meta.dump(4) << std::endl;
metaStream.close();
}
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&);

View File

@@ -6,14 +6,15 @@
#include <any>
#include <string>
#include <tuple>
#include "interfaces/IAsset.hpp"
#include "json.hpp"
namespace TSE
{
struct Material
struct Material : public IAsset
{
private:
string name;
uuids::uuid id;
IShader* shader;
std::unordered_map<string, std::tuple<std::any, string, string>> values;
@@ -40,6 +41,15 @@ namespace TSE
IShader* GetShader() const;
void SetShader(IShader* shader);
uuids::uuid GetID() const;
bool LoadAsset(string& path) override;
void SaveAsset() override;
nlohmann::ordered_json GenerateMaterialJson();
void Hash() override;
string assetType() override;
void UnloadAsset() override;
protected:
void SaveMeta() override;
};
template<typename T>

View File

@@ -0,0 +1,564 @@
#include "Mesh.hpp"
#include "MathF.hpp"
#include <cmath>
#include "json.hpp"
#include "PathHelper.hpp"
#include "IdGenerator.hpp"
#include "Debug.hpp"
#include "elements/AssetLibrary.hpp"
#include <algorithm>
#include <filesystem>
#include <iomanip>
#include <limits>
#include <sstream>
#include <unordered_map>
namespace
{
struct ObjFaceVertex
{
int vertexIndex = -1;
int uvIndex = -1;
bool operator==(const ObjFaceVertex& other) const
{
return vertexIndex == other.vertexIndex && uvIndex == other.uvIndex;
}
};
struct ObjFaceVertexHash
{
size_t operator()(const ObjFaceVertex& value) const
{
size_t h1 = std::hash<int>{}(value.vertexIndex);
size_t h2 = std::hash<int>{}(value.uvIndex);
return h1 ^ (h2 + 0x9e3779b9 + (h1 << 6) + (h1 >> 2));
}
};
struct ObjMeshData
{
TSE::string name;
std::vector<TSE::Vector3> vertecies;
std::vector<TSE::ushort> indecies;
std::vector<TSE::Vector2> uvs;
std::unordered_map<ObjFaceVertex, TSE::ushort, ObjFaceVertexHash> vertexLookup;
};
int ResolveObjIndex(int index, size_t count)
{
if(index > 0)
{
return index - 1;
}
if(index < 0)
{
return static_cast<int>(count) + index;
}
return -1;
}
ObjFaceVertex ParseFaceVertex(const std::string& token, size_t vertexCount, size_t uvCount)
{
ObjFaceVertex result;
std::stringstream stream(token);
std::string part;
if(std::getline(stream, part, '/') && !part.empty())
{
result.vertexIndex = ResolveObjIndex(std::stoi(part), vertexCount);
}
if(std::getline(stream, part, '/') && !part.empty())
{
result.uvIndex = ResolveObjIndex(std::stoi(part), uvCount);
}
return result;
}
bool AddObjFaceVertex(
ObjMeshData& mesh,
const ObjFaceVertex& faceVertex,
const std::vector<TSE::Vector3>& sourceVertices,
const std::vector<TSE::Vector2>& sourceUvs)
{
if(faceVertex.vertexIndex < 0 || faceVertex.vertexIndex >= static_cast<int>(sourceVertices.size()))
{
return false;
}
auto existing = mesh.vertexLookup.find(faceVertex);
if(existing != mesh.vertexLookup.end())
{
mesh.indecies.push_back(existing->second);
return true;
}
if(mesh.vertecies.size() > std::numeric_limits<TSE::ushort>::max())
{
TSE_WARNING("OBJ mesh has more vertices than ushort indices can address: " + mesh.name);
return false;
}
TSE::ushort newIndex = static_cast<TSE::ushort>(mesh.vertecies.size());
mesh.vertecies.push_back(sourceVertices[faceVertex.vertexIndex]);
if(faceVertex.uvIndex >= 0 && faceVertex.uvIndex < static_cast<int>(sourceUvs.size()))
{
mesh.uvs.push_back(sourceUvs[faceVertex.uvIndex]);
}
else
{
mesh.uvs.emplace_back(0.0f, 0.0f);
}
mesh.vertexLookup[faceVertex] = newIndex;
mesh.indecies.push_back(newIndex);
return true;
}
TSE::string MeshGeometryHash(
const std::vector<TSE::Vector3>& vertecies,
const std::vector<TSE::ushort>& indecies,
const std::vector<TSE::Vector2>& uvs)
{
std::ostringstream stream;
stream << std::setprecision(std::numeric_limits<float>::max_digits10);
stream << "v:" << vertecies.size() << ";";
for(const auto& vertex : vertecies)
{
stream << vertex.x << "," << vertex.y << "," << vertex.z << ";";
}
stream << "i:" << indecies.size() << ";";
for(TSE::ushort index : indecies)
{
stream << index << ";";
}
stream << "uv:" << uvs.size() << ";";
for(const auto& uv : uvs)
{
stream << uv.x << "," << uv.y << ";";
}
return picosha2::hash256_hex_string(stream.str());
}
TSE::string MeshGeometryHash(const TSE::Mesh& mesh)
{
return MeshGeometryHash(mesh.vertecies, mesh.indecies, mesh.uvs);
}
}
TSE::Mesh::Mesh()
{
id = GenerateRandomUUID();
name = "";
}
TSE::Mesh::Mesh(string _name, const std::vector<Vector3> &_vertecies, const std::vector<ushort> &_indecies, const std::vector<Vector2> &_uvs)
{
id = GenerateRandomUUID();
name = _name;
vertecies = std::move(_vertecies);
indecies = std::move(_indecies);
uvs = std::move(_uvs);
}
size_t TSE::Mesh::IndeciesCount() const
{
return indecies.size();
}
size_t TSE::Mesh::VerteciesCount() const
{
return vertecies.size();
}
TSE::Mesh TSE::Mesh::GetCircleMesh(ushort segments)
{
std::vector<Vector3> verts;
std::vector<ushort> indices;
std::vector<Vector2> uvs;
verts.emplace_back(0.0f, 0.0f, 0.0f);
uvs.emplace_back(0.5f, 0.5f);
float angleStep = 2.0f * TSE_PI / segments;
for (int i = 0; i <= segments; ++i) {
float angle = i * angleStep;
float x = std::cos(angle) * 0.5f;
float y = std::sin(angle) * 0.5f;
verts.emplace_back(x, y, 0);
uvs.emplace_back(x + 0.5f, y + 0.5f);
if (i > 0) {
indices.push_back(0);
indices.push_back(i);
indices.push_back(i + 1);
}
}
return Mesh("Circle", verts, indices, uvs);
}
TSE::Mesh TSE::Mesh::GetQuadMesh()
{
std::vector<Vector3> verts = {
Vector3(-0.5f, -0.5f, 0),
Vector3( 0.5f, -0.5f, 0),
Vector3( 0.5f, 0.5f, 0),
Vector3(-0.5f, 0.5f, 0)
};
std::vector<Vector2> uvs = {
Vector2(0.0f, 0.0f),
Vector2(1.0f, 0.0f),
Vector2(1.0f, 1.0f),
Vector2(0.0f, 1.0f)
};
std::vector<unsigned short> indices = {
0, 1, 2,
2, 3, 0
};
return Mesh("Quad", verts, indices, uvs);
}
void TSE::Mesh::LoadObj(const string &path)
{
std::ifstream objStream;
OpenFileReading(objStream, path);
if(!objStream.is_open())
{
TSE_WARNING("Could not open OBJ file: " + path);
return;
}
namespace fs = std::filesystem;
string fallbackName = fs::path(path).stem().string();
std::vector<Vector3> sourceVertices;
std::vector<Vector2> sourceUvs;
std::vector<ObjMeshData> objects;
ObjMeshData* currentObject = nullptr;
auto ensureObject = [&]() -> ObjMeshData&
{
if(currentObject == nullptr)
{
objects.emplace_back();
objects.back().name = fallbackName;
currentObject = &objects.back();
}
return *currentObject;
};
std::string line;
while(std::getline(objStream, line))
{
std::stringstream lineStream(line);
std::string command;
lineStream >> command;
if(command == "v")
{
float x = 0.0f;
float y = 0.0f;
float z = 0.0f;
lineStream >> x >> y >> z;
sourceVertices.emplace_back(x, y, z);
}
else if(command == "vt")
{
float u = 0.0f;
float v = 0.0f;
lineStream >> u >> v;
sourceUvs.emplace_back(u, v);
}
else if(command == "o")
{
std::string objectName;
std::getline(lineStream >> std::ws, objectName);
objects.emplace_back();
objects.back().name = objectName.empty() ? fallbackName : objectName;
currentObject = &objects.back();
}
else if(command == "f")
{
ObjMeshData& mesh = ensureObject();
std::vector<ObjFaceVertex> face;
std::string token;
while(lineStream >> token)
{
face.push_back(ParseFaceVertex(token, sourceVertices.size(), sourceUvs.size()));
}
if(face.size() < 3)
{
continue;
}
bool hasInvalidVertex = std::any_of(face.begin(), face.end(), [&sourceVertices](const ObjFaceVertex& faceVertex)
{
return faceVertex.vertexIndex < 0 || faceVertex.vertexIndex >= static_cast<int>(sourceVertices.size());
});
if(hasInvalidVertex)
{
TSE_WARNING("Skipping invalid OBJ face in: " + path);
continue;
}
for(size_t i = 1; i + 1 < face.size(); i++)
{
AddObjFaceVertex(mesh, face[0], sourceVertices, sourceUvs);
AddObjFaceVertex(mesh, face[i], sourceVertices, sourceUvs);
AddObjFaceVertex(mesh, face[i + 1], sourceVertices, sourceUvs);
}
}
}
objects.erase(
std::remove_if(objects.begin(), objects.end(), [](const ObjMeshData& object)
{
return object.vertecies.empty() || object.indecies.empty();
}),
objects.end());
if(objects.empty())
{
name = fallbackName;
return;
}
if(objects.size() == 1)
{
name = objects[0].name;
vertecies = std::move(objects[0].vertecies);
indecies = std::move(objects[0].indecies);
uvs = std::move(objects[0].uvs);
return;
}
name = fallbackName;
for(auto& object : objects)
{
if(vertecies.size() + object.vertecies.size() > std::numeric_limits<ushort>::max())
{
TSE_WARNING("Combined OBJ mesh has more vertices than ushort indices can address: " + path);
continue;
}
ushort indexOffset = static_cast<ushort>(vertecies.size());
vertecies.insert(vertecies.end(), object.vertecies.begin(), object.vertecies.end());
uvs.insert(uvs.end(), object.uvs.begin(), object.uvs.end());
for(ushort index : object.indecies)
{
indecies.push_back(static_cast<ushort>(index + indexOffset));
}
Mesh* submesh = new Mesh(object.name, object.vertecies, object.indecies, object.uvs);
submesh->rawPath = path + "#" + object.name;
submesh->hash = MeshGeometryHash(*submesh);
submesh->IsVirtualAsset = true;
derivedAssets.push_back(submesh);
AssetLibrary::SetValue(submesh->id, submesh);
}
}
bool TSE::Mesh::LoadAsset(string &path)
{
namespace fs = std::filesystem;
for(auto submesh : derivedAssets)
{
AssetLibrary::RemoveValue(submesh->id, submesh);
delete(submesh);
}
derivedAssets.clear();
if(vertecies.size() != 0)
{
vertecies.clear();
}
if(indecies.size() != 0)
{
indecies.clear();
}
if(uvs.size() != 0)
{
uvs.clear();
}
rawPath = string(path);
name = rawPath;
string extension = fs::directory_entry(rawPath).path().extension().string();
if(extension == ".obj")
{
LoadObj(rawPath);
}
string metaPath = PathToMetaPath(rawPath);
using json = nlohmann::ordered_json;
Hash();
for(auto derivedAsset : derivedAssets)
{
Mesh* submesh = dynamic_cast<Mesh*>(derivedAsset);
if(submesh != nullptr)
{
submesh->hash = MeshGeometryHash(*submesh);
}
}
if(FileExists(metaPath))
{
std::ifstream metaStream;
OpenFileReading(metaStream, metaPath);
json meta = json::parse(metaStream);
if(meta["type"] != "metadata")
{
metaStream.close();
TSE_WARNING("Rebuilding corrupted meta file (Texture): " + metaPath);
SaveMeta();
return true;
}
if(meta["version"] == 1)
{
if(meta["assetType"] == "mesh")
{
id = uuids::uuid::from_string((string)meta["id"]).value_or(id);
std::unordered_map<string, string> derivedAssetIds;
if(meta.contains("derivedAssets") && meta["derivedAssets"].is_array())
{
for(const auto& derivedAssetMeta : meta["derivedAssets"])
{
if(derivedAssetMeta.contains("hash") && derivedAssetMeta.contains("id"))
{
derivedAssetIds[(string)derivedAssetMeta["hash"]] = (string)derivedAssetMeta["id"];
}
}
}
for(auto derivedAsset : derivedAssets)
{
Mesh* submesh = dynamic_cast<Mesh*>(derivedAsset);
if(submesh == nullptr)
{
continue;
}
submesh->hash = MeshGeometryHash(*submesh);
auto idIt = derivedAssetIds.find(submesh->hash);
if(idIt == derivedAssetIds.end())
{
continue;
}
uuids::uuid oldId = submesh->id;
submesh->id = uuids::uuid::from_string(idIt->second).value_or(submesh->id);
if(oldId != submesh->id)
{
AssetLibrary::RemoveValue(oldId, submesh);
AssetLibrary::SetValue(submesh->id, submesh);
}
}
metaStream.close();
SaveMeta();
return true;
}
else
{
metaStream.close();
TSE_WARNING("Meta file incompatible with asset type. Please delete for auto regeneration or fix maualy (Mesh): " + metaPath);
return true;
}
}
else
{
metaStream.close();
TSE_WARNING("Meta file incompatible. Please delete for auto regeneration or fix maualy (Mesh): " + metaPath);
return true;
}
}
else
{
SaveMeta();
return true;
}
}
void TSE::Mesh::SaveAsset()
{
if(IsVirtualAsset) return;
Hash();
SaveMeta();
}
void TSE::Mesh::Hash()
{
std::ifstream ifs(rawPath, std::ios::binary);
std::vector<unsigned char> s(picosha2::k_digest_size);
picosha2::hash256(ifs, s.begin(), s.end());
hash = picosha2::hash256_hex_string(s);
ifs.close();
}
TSE::string TSE::Mesh::assetType()
{
return "mesh";
}
void TSE::Mesh::UnloadAsset()
{
for(auto submesh : derivedAssets)
{
AssetLibrary::RemoveValue(submesh->id, submesh);
delete(submesh);
}
AssetLibrary::RemoveValue(id, static_cast<IAsset*>(this));
delete(this);
}
void TSE::Mesh::SaveMeta()
{
string metaPath = PathToMetaPath(rawPath);
using json = nlohmann::ordered_json;
json meta;
meta["type"] = "metadata";
meta["assetType"] = "mesh";
meta["version"] = 1;
meta["source"] = rawPath; //todo: clamp to assets root
meta["id"] = uuids::to_string(id);
meta["hash"] = hash;
meta["derivedAssets"] = json::array();
for(auto derivedAsset : derivedAssets)
{
Mesh* submesh = dynamic_cast<Mesh*>(derivedAsset);
if(submesh == nullptr)
{
continue;
}
submesh->hash = MeshGeometryHash(*submesh);
json derivedAssetMeta;
derivedAssetMeta["name"] = submesh->name;
derivedAssetMeta["id"] = uuids::to_string(submesh->id);
derivedAssetMeta["hash"] = submesh->hash;
meta["derivedAssets"].push_back(derivedAssetMeta);
}
std::ofstream metaStream;
CreateFileWriting(metaStream, metaPath);
metaStream << meta.dump(4) << std::endl;
metaStream.close();
}

View File

@@ -4,10 +4,11 @@
#include "Types.hpp"
#include "Vector3.hpp"
#include "Vector2.hpp"
#include "interfaces/IAsset.hpp"
namespace TSE
{
class Mesh
class Mesh : public IAsset
{
public:
string name;
@@ -41,5 +42,16 @@ namespace TSE
/// @brief gives you a basic unit quad with (-0.5, -0.5) -> (0.5, 0.5)
/// @return the resulting mesh
static Mesh GetQuadMesh();
void LoadObj(const string& path);
bool LoadAsset(string& path) override;
void SaveAsset() override;
void Hash() override;
string assetType() override;
void UnloadAsset() override;
protected:
void SaveMeta() override;
};
} // namespace TSE

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

@@ -2,10 +2,59 @@
#include "BehaviourScripts/Camera.hpp"
#include <algorithm>
#include "Debug.hpp"
#include "RenderPipeline.hpp"
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;
for(auto l : layers)
{
@@ -37,6 +86,7 @@ void TSE::Scene::Render(IRenderer &rnd, const IWindow &wnd)
}
}
}
}
void TSE::Scene::DoneRender()
{

View File

@@ -12,9 +12,9 @@ namespace TSE
inline static std::unordered_map<string, IShader*> registeredShaders = {};
public:
inline static void SetShader(const string& name, IShader* shader)
inline static void SetShader(IShader* shader)
{
registeredShaders[name] = shader;
registeredShaders[shader->GetName()] = shader;
};
inline static IShader* GetShader(const string& name)
{

View File

@@ -3,8 +3,19 @@
#include "Color.hpp"
#include "ErrorTextureData.hpp"
#include <filesystem>
#include "json.hpp"
#include "PathHelper.hpp"
#include "utils/JsonExports.hpp"
#include "IdGenerator.hpp"
#include "AssetLibrary.hpp"
TSE::Texture::Texture(const string &path)
{
id = GenerateRandomUUID();
LoadImageFromFile(path);
}
bool TSE::Texture::LoadImageFromFile(const string &path)
{
FREE_IMAGE_FORMAT fif = FREE_IMAGE_FORMAT::FIF_UNKNOWN;
bmp = nullptr;
@@ -19,7 +30,7 @@ TSE::Texture::Texture(const string &path)
TSE_ERROR(msg);
Bpp = 24;
makeError(*this);
return;
return false;
}
fif = FreeImage_GetFileType(name.c_str(), 0);
@@ -30,12 +41,12 @@ TSE::Texture::Texture(const string &path)
TSE_ERROR("Failed to load image. Unsupported Format.");
Bpp = 24;
makeError(*this);
return;
return false;
}
if(FreeImage_FIFSupportsReading(fif))
{
bmp = FreeImage_Load(fif, path.c_str());
if(FreeImage_GetBPP(bmp) != 32)
bmp = FreeImage_Load(fif, name.c_str());
if(bmp != nullptr && FreeImage_GetBPP(bmp) != 32)
{
auto tmpBmp = FreeImage_ConvertTo32Bits(bmp);
FreeImage_Unload(bmp);
@@ -47,7 +58,7 @@ TSE::Texture::Texture(const string &path)
TSE_ERROR("Failed to load image. Bitmap was nullptr.");
Bpp = 24;
makeError(*this);
return;
return false;
}
Bpp = FreeImage_GetBPP(bmp);
imagePtr = FreeImage_GetBits(bmp);
@@ -65,10 +76,12 @@ TSE::Texture::Texture(const string &path)
Size = Vector2(FreeImage_GetWidth(bmp), FreeImage_GetHeight(bmp));
regist();
return true;
}
TSE::Texture::Texture(const int &width, const int &height, int bpp)
{
id = GenerateRandomUUID();
switch (bpp)
{
case 32:
@@ -88,6 +101,7 @@ TSE::Texture::Texture(const int &width, const int &height, int bpp)
TSE::Texture::Texture(const Vector2 &size, int bpp)
{
id = GenerateRandomUUID();
switch (bpp)
{
case 32:
@@ -120,6 +134,11 @@ TSE::Texture::~Texture()
}
}
TSE::string TSE::Texture::name() const
{
return settings.name;
}
TSE::uint TSE::Texture::bpp() const
{
return Bpp;
@@ -461,3 +480,154 @@ void TSE::Texture::PlatformDestroy()
{
helper->PlatromDestroy(this);
}
bool TSE::Texture::LoadAsset(string &path)
{
if(bmp != nullptr)
{
FreeImage_Unload(bmp);
bmp = nullptr;
}
if(TextureID != 0)
{
PlatformDestroy();
TextureID = 0;
}
imagePtr = nullptr;
LoadImageFromFile(path);
rawPath = string(path);
UpdateImportSettings(rawPath, 0, {1,1});
string metaPath = PathToMetaPath(rawPath);
using json = nlohmann::ordered_json;
Hash();
if(FileExists(metaPath))
{
std::ifstream metaStream;
OpenFileReading(metaStream, metaPath);
json meta = json::parse(metaStream);
if(meta["type"] != "metadata")
{
metaStream.close();
TSE_WARNING("Rebuilding corrupted meta file (Texture): " + metaPath);
SaveMeta();
return true;
}
if(meta["version"] == 1)
{
if(meta["assetType"] == "texture")
{
id = uuids::uuid::from_string((string)meta["id"]).value_or(id);
//dependencies;
settings.importMode = meta["importSettings"]["mode"];
settings.importCount = ImportVector2(meta["importSettings"]["count"]);
settings.bpp = meta["importSettings"]["bpp"];
settings.chanels = meta["importSettings"]["chanels"];
settings.name = meta["importSettings"]["name"];
Bpp = settings.bpp;
chanels = settings.chanels;
//create sub assets eg. sprites;
metaStream.close();
SaveMeta();
return true;
}
else
{
metaStream.close();
TSE_WARNING("Meta file incompatible with asset type. Please delete for auto regeneration or fix maualy (Texture): " + metaPath);
return true;
}
}
else
{
metaStream.close();
TSE_WARNING("Meta file incompatible. Please delete for auto regeneration or fix maualy (Texture): " + metaPath);
return true;
}
}
else
{
SaveMeta();
return true;
}
}
void TSE::Texture::SaveAsset()
{
if(IsVirtualAsset) return;
Hash();
SaveMeta();
}
void TSE::Texture::Hash()
{
std::ifstream ifs(rawPath, std::ios::binary);
std::vector<unsigned char> s(picosha2::k_digest_size);
picosha2::hash256(ifs, s.begin(), s.end());
hash = picosha2::hash256_hex_string(s);
ifs.close();
}
void TSE::Texture::UpdateImportSettings(string name, byte mode, Vector2 count)
{
settings.importMode = mode;
settings.importCount = count;
settings.name = name;
settings.bpp = Bpp;
settings.chanels = chanels;
}
TSE::string TSE::Texture::assetType()
{
return "texture";
}
void TSE::Texture::UnloadAsset()
{
for(auto submesh : derivedAssets)
{
AssetLibrary::RemoveValue(submesh->id, submesh);
delete(submesh);
}
AssetLibrary::RemoveValue(id, static_cast<IAsset*>(this));
delete(this);
}
void TSE::Texture::SaveMeta()
{
string metaPath = PathToMetaPath(rawPath);
using json = nlohmann::ordered_json;
json meta;
meta["type"] = "metadata";
meta["assetType"] = "texture";
meta["version"] = 1;
meta["source"] = rawPath; //todo: clamp to assets root
meta["id"] = uuids::to_string(id);
meta["hash"] = hash;
meta["importSettings"]["mode"] = settings.importMode;
meta["importSettings"]["count"] = ExportVector2(settings.importCount);
meta["importSettings"]["bpp"] = settings.bpp;
meta["importSettings"]["chanels"] = settings.chanels;
meta["importSettings"]["name"] = settings.name;
for (int i = 0; i < dependecies.size(); i++)
{
meta["dependencies"][i]["id"] = uuids::to_string(dependecies[i]->id);
meta["dependencies"][i]["path"] = dependecies[i]->rawPath; //todo: clamp to assets root
}
std::ofstream metaStream;
CreateFileWriting(metaStream, metaPath);
metaStream << meta.dump(4) << std::endl;
metaStream.close();
}

View File

@@ -5,15 +5,31 @@
#include "Types.hpp"
#include "Vector2.hpp"
#include "interfaces/ITextureHelper.hpp"
#include "interfaces/IAsset.hpp"
#define FREEIMAGE_LIB
#include "FI/FreeImage.h"
namespace TSE
{
class Texture : public ITexture
struct TextureImportSettings
{
//0 = rawTexture;
//1 = sprite;
//2 = spriteset; ==> need to set import count to sprite counts
//3 = ninetile;
byte importMode = 0;
Vector2 importCount = {1,1};
//bpp and chanels are set from import, but can be changed for custum effects, or memory layouts
uint bpp;
byte chanels;
string name = "Unnamed";
};
class Texture : public ITexture, public IAsset
{
protected:
TextureImportSettings settings;
uint TextureID = 0;
Vector2 Size;
uint Bpp;
@@ -22,14 +38,13 @@ namespace TSE
byte* imagePtr = nullptr;
public:
string name = "Unnamed";
inline static ITextureHelper* helper = nullptr;
Texture(const string& path);
Texture(const int& width, const int& height, int bpp = 32);
Texture(const Vector2& size, int bpp = 32);
~Texture();
string name() const;
uint bpp() const;
Vector2 size() const override;
float Width() const override;
@@ -60,5 +75,18 @@ namespace TSE
void Apply();
void regist();
void PlatformDestroy();
bool LoadAsset(string& path) override;
void SaveAsset() override;
void Hash() override;
TextureImportSettings& GetImportSettings() { return settings; }
const TextureImportSettings& GetImportSettings() const { return settings; }
void UpdateImportSettings(string name, byte mode, Vector2 count);
string assetType() override;
void UnloadAsset() override;
protected:
bool LoadImageFromFile(const string& path);
void SaveMeta() override;
};
} // namespace TSE

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
{
None = 0x0000,
ShiftMod = 0x0001,
ControlMod = 0x0002,
AltMod = 0x0004,

View File

@@ -0,0 +1,47 @@
#pragma once
#include "Types.hpp"
#include <filesystem>
#include "picosha2.h"
#include "uuid.h"
namespace TSE
{
class IAsset
{
public:
uuids::uuid id;
string rawPath;
string hash;
std::vector<IAsset*> dependecies;
std::vector<IAsset*> derivedAssets;
bool IsVirtualAsset = false;
virtual ~IAsset() = default;
virtual bool LoadAsset(string& path) = 0;
virtual void SaveAsset() = 0;
virtual void Hash() = 0;
virtual string assetType() = 0;
virtual void UnloadAsset() = 0;
inline void Rescan()
{
string oldHash = string(hash);
Hash();
if(oldHash != hash)
{
LoadAsset(rawPath);
}
};
protected:
virtual void SaveMeta() = 0;
inline string PathToMetaPath(string path) const
{
return path + ".meta";
};
inline std::string sha256(const std::string& str) {
return picosha2::hash256_hex_string(str);
};
};
}

View File

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

View File

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

View File

@@ -10,6 +10,9 @@ namespace TSE
{
class IShader
{
protected:
string name = "UnnamedShader";
public:
virtual void Bind() const = 0;
@@ -24,6 +27,8 @@ namespace TSE
virtual void SetUniform(const char* name, const Vector3* value) = 0;
virtual void SetUniform(const char* name, const Vector4* value) = 0;
inline string GetName() {return name;};
virtual ~IShader() = default;
};
} // namespace TSE

View File

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

View File

@@ -3,6 +3,7 @@
namespace TSE
{
class Texture;
class VolumeTexture3D;
class ITextureHelper
{
@@ -12,5 +13,11 @@ namespace TSE
virtual void Apply(Texture* tex) = 0;
virtual void Regist(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

View File

@@ -3,77 +3,60 @@
nlohmann::json TSE::ExportColor(Color &color)
{
using json = nlohmann::json;
json exp;
exp["r"] = color.r;
exp["g"] = color.g;
exp["b"] = color.b;
exp["a"] = color.a;
return exp;
return json::array({color.r, color.g, color.b, color.a});
}
nlohmann::json TSE::ExportVector2(Vector2 &vec2)
{
using json = nlohmann::json;
json exp;
exp["x"] = vec2.x;
exp["y"] = vec2.y;
return exp;
return json::array({vec2.x, vec2.y});
}
nlohmann::json TSE::ExportVector3(Vector3 &vec3)
{
using json = nlohmann::json;
json exp;
exp["x"] = vec3.x;
exp["y"] = vec3.y;
exp["z"] = vec3.z;
return exp;
return json::array({vec3.x, vec3.y, vec3.z});
}
nlohmann::json TSE::ExportVector4(Vector4 &vec4)
{
using json = nlohmann::json;
json exp;
exp["x"] = vec4.x;
exp["y"] = vec4.y;
exp["z"] = vec4.z;
exp["w"] = vec4.w;
return exp;
return json::array({vec4.x, vec4.y, vec4.z, vec4.w});
}
TSE::Color TSE::ImportColor(nlohmann::json json)
{
Color imp;
imp.r = json["r"];
imp.g = json["g"];
imp.b = json["b"];
imp.a = json["a"];
imp.r = json[0];
imp.g = json[1];
imp.b = json[2];
imp.a = json[3];
return imp;
}
TSE::Vector2 TSE::ImportVector2(nlohmann::json json)
{
Vector2 imp;
imp.x = json["x"];
imp.y = json["y"];
imp.x = json[0];
imp.y = json[1];
return imp;
}
TSE::Vector3 TSE::ImportVector3(nlohmann::json json)
{
Vector3 imp;
imp.x = json["x"];
imp.y = json["y"];
imp.z = json["z"];
imp.x = json[0];
imp.y = json[1];
imp.z = json[2];
return imp;
}
TSE::Vector4 TSE::ImportVector4(nlohmann::json json)
{
Vector4 imp;
imp.x = json["x"];
imp.y = json["y"];
imp.z = json["z"];
imp.w = json["w"];
imp.x = json[0];
imp.y = json[1];
imp.z = json[2];
imp.w = json[3];
return imp;
}

View File

@@ -3,19 +3,24 @@
#include "BehaviourScriptRegistry.hpp"
#include "BehaviourScripts/AudioListener.hpp"
#include "BehaviourScripts/AudioSource.hpp"
#include "BehaviourScripts/TileMap.hpp"
#include "BehaviourScripts/OrdererSpriteSet.hpp"
#include "BehaviourScripts/PhysicsObject.hpp"
#include "BehaviourScripts/basicEditorCamera.hpp"
TSE::EDITOR::EditorSubsystem::EditorSubsystem() : sv(nullptr), editorLayer("")
TSE::EDITOR::EditorSubsystem::EditorSubsystem(ConsolView* cvp) : sv(nullptr), editorLayer("")
{
rt = IRenderTexture::factory->CreateTextureHeap({100,100}, 5);
sv = SceneView(rt);
cv = cvp;
controller.AddGuiElement("Scene", &sv);
controller.AddGuiElement("Consol", &cv);
controller.AddGuiElement("Consol", cv);
controller.AddGuiElement("Hirearchie", &hv);
controller.AddGuiElement("Properties", &pv);
controller.AddGuiElement("Debug", &dv);
controller.AddGuiElement("Camera", &camv);
controller.AddGuiElement("Assets", &av);
BehaviourScriptRegistry::RegisterBehaviourScript("Image", []() -> BehaviourScript* {return new Image();});
BehaviourScriptRegistry::RegisterBehaviourScript("Image Animation", []() -> BehaviourScript* {return new ImageAnimation();});
@@ -26,6 +31,9 @@ TSE::EDITOR::EditorSubsystem::EditorSubsystem() : sv(nullptr), editorLayer("")
BehaviourScriptRegistry::RegisterBehaviourScript("Camera", []() -> BehaviourScript* {return new Camera();});
BehaviourScriptRegistry::RegisterBehaviourScript("Audio Listener", []() -> BehaviourScript* {return new AudioListener();});
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

View File

@@ -7,6 +7,7 @@
#include "UI/windows/HirearchieView.hpp"
#include "UI/windows/PropertiesView.hpp"
#include "UI/windows/SceneView.hpp"
#include "UI/windows/AssetsView.hpp"
#include "interfaces/IRenderTexture.hpp"
namespace TSE::EDITOR
@@ -15,15 +16,16 @@ namespace TSE::EDITOR
{
public:
ViewportController controller;
ConsolView cv;
ConsolView* cv = nullptr;
DebugView dv;
HirearchieView hv = HirearchieView(nullptr);
PropertiesView pv;
CameraView camv;
TSE::IRenderTexture* rt;
SceneView sv;
AssetsView av;
Layer editorLayer;
EditorSubsystem();
EditorSubsystem(ConsolView* cvp);
};
} // namespace TSE::EDITOR

View File

@@ -1,12 +1,167 @@
#include "ElementDrawer.hpp"
#include "BehaviourScriptRegistry.hpp"
#include "elements/ShaderRegistry.hpp"
#include "elements/AssetLibrary.hpp"
#include "BehaviourScripts/Camera.hpp"
#include "windows/HirearchieView.hpp"
#include "windows/PropertiesView.hpp"
#include <algorithm>
#include <any>
#include <cctype>
namespace TSE::EDITOR
{
namespace
{
char assetPickerSearchBuffer[128] = "";
std::string ToLowerCopy(std::string value)
{
std::transform(value.begin(), value.end(), value.begin(), [](unsigned char c)
{
return static_cast<char>(std::tolower(c));
});
return value;
}
template<typename T>
std::vector<T*> GetAssetsOfType()
{
std::vector<T*> result;
int count = AssetLibrary::GetValueCount();
for(int i = 0; i < count; i++)
{
auto [ptr, type, id] = AssetLibrary::GetValueAt(i);
if(type != typeid(T).name() && type != typeid(T*).name())
{
continue;
}
try
{
result.push_back(std::any_cast<T*>(ptr));
}
catch(const std::bad_any_cast&)
{
}
}
return result;
}
template<typename T, typename NameGetter>
T* DrawAssetPickerPopup(const char* popupId, const char* searchHint, const char* emptyText, NameGetter getName)
{
T* selectedAsset = nullptr;
ImGui::SetNextWindowSize({360.0f, 420.0f}, ImGuiCond_Appearing);
if(ImGui::BeginPopup(popupId))
{
ImGui::PushItemWidth(-1.0f);
ImGui::InputTextWithHint("##AssetPickerSearch", searchHint, assetPickerSearchBuffer, IM_ARRAYSIZE(assetPickerSearchBuffer));
ImGui::PopItemWidth();
ImGui::Separator();
std::string filterText = ToLowerCopy(assetPickerSearchBuffer);
std::vector<T*> assets = GetAssetsOfType<T>();
if(ImGui::BeginChild("##AssetPickerList", {0.0f, 0.0f}, ImGuiChildFlags_None, ImGuiWindowFlags_AlwaysVerticalScrollbar))
{
int shownCount = 0;
for(T* asset : assets)
{
if(asset == nullptr)
{
continue;
}
std::string assetName = getName(asset);
if(!filterText.empty() && ToLowerCopy(assetName).find(filterText) == std::string::npos)
{
continue;
}
ImGui::PushID(asset);
if(ImGui::Selectable(assetName.c_str()))
{
selectedAsset = asset;
ImGui::CloseCurrentPopup();
}
ImGui::PopID();
shownCount++;
}
if(shownCount == 0)
{
ImGui::TextDisabled("%s", emptyText);
}
}
ImGui::EndChild();
ImGui::EndPopup();
}
return selectedAsset;
}
template<typename T>
bool BeginAssetField(T** element, const char* popupId)
{
if(element == nullptr)
{
ImGui::TextDisabled("No Asset Field");
return false;
}
ImGui::PushID(element);
float buttonWidth = ImGui::GetFrameHeight();
float fieldHeight = std::max(36.0f, ImGui::GetFrameHeightWithSpacing() + ImGui::GetStyle().WindowPadding.y * 2.0f);
ImGui::BeginChild("##AssetField", {0.0f, fieldHeight}, ImGuiChildFlags_Borders, ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse);
if(ImGui::Button("+##SelectAsset", {buttonWidth, 0.0f}))
{
assetPickerSearchBuffer[0] = '\0';
ImGui::OpenPopup(popupId);
}
ImGui::SameLine();
ImGui::BeginChild("##AssetFieldValue", {0.0f, 0.0f}, ImGuiChildFlags_None, ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse);
return true;
}
template<typename T>
void EndAssetField()
{
ImGui::EndChild();
}
template<typename T>
void OpenAssetPropertiesOnDoubleClick(T* asset)
{
if(asset == nullptr)
{
return;
}
if(ImGui::IsItemHovered() && ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left))
{
PropertiesView::SetInspectorElement(InspectableType::IAsset, static_cast<IAsset*>(asset));
}
}
template<typename T, typename NameGetter>
void AssetSelector(T** element, const char* popupId, const char* searchHint, const char* emptyText, NameGetter getName)
{
T* selectedAsset = DrawAssetPickerPopup<T>(popupId, searchHint, emptyText, getName);
if(selectedAsset != nullptr)
{
*element = selectedAsset;
}
ImGui::EndChild();
ImGui::PopID();
}
}
#pragma region helper
bool InputText(const char* label, std::string& str, size_t bufferSize = 256) {
std::vector<char> buffer(bufferSize);
@@ -208,6 +363,10 @@ namespace TSE::EDITOR
{
Draw((TileMap*)element, debug);
}
else if (name == "Orderer Sprite Set")
{
Draw((OrdererSpriteSet*)element, debug);
}
else
{
element->CustomDraw(debug);
@@ -218,15 +377,7 @@ namespace TSE::EDITOR
void ElementDrawer::Draw(Renderable *element, const bool &debug)
{
ImGui::SeparatorText("Material");
int height = 100;
if(element->GetMaterial() == nullptr)
{
height = 35;
}
ImVec2 size(0, height);
ImGui::BeginChild("MaterialViewer", size, ImGuiChildFlags_Borders);
Draw(element->GetMaterial(), debug);
ImGui::EndChild();
DrawMaterialField(element->GetMaterialRef());
}
void ElementDrawer::Draw(MeshContainer *element, const bool &debug)
{
@@ -236,7 +387,7 @@ namespace TSE::EDITOR
height = 35;
}
ImVec2 size(0, height);
Draw(element->GetMesh(), debug, "Mesh", true);
DrawMeshField(element->GetMeshRef());
}
void ElementDrawer::Draw(Image *element, const bool &debug)
{
@@ -440,7 +591,7 @@ namespace TSE::EDITOR
Texture* value = element->GetValue<Texture*>(name);
Draw(value, debug, name , true);
}
if (type == typeid(uint).name())
else if (type == typeid(uint).name())
{
int value = element->GetValue<uint>(name);
if(ImGui::InputInt(name.c_str(), &value))
@@ -477,11 +628,6 @@ namespace TSE::EDITOR
}
void ElementDrawer::Draw(Mesh *element, const bool &debug, const std::string &label, const bool small)
{
if(element == nullptr)
{
ImGui::Text("No Mesh Set");
return;
}
if(small) DrawMeshCompact(element, debug, label);
else DrawMeshNormal(element, debug, label);
}
@@ -915,6 +1061,48 @@ namespace TSE::EDITOR
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::DragFloat("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::Draw(IAsset *element, const bool &debug)
{
if (element->assetType() == "material")
{
Draw((Material*)element, debug);
}
else if (element->assetType() == "texture")
{
Draw((Texture*)element, debug);
}
else if (element->assetType() == "mesh")
{
Draw((Mesh*)element, debug);
}
element->SaveAsset();
}
void ElementDrawer::DrawAudioClipCompact(AudioClip *element, const bool &debug, const std::string &label)
{
float item_spacing = ImGui::GetStyle().ItemSpacing.x;
@@ -1006,6 +1194,21 @@ namespace TSE::EDITOR
}
void ElementDrawer::DrawMeshCompact(Mesh *element, const bool &debug, const std::string &label)
{
if(element == nullptr)
{
ImGui::Text("No Mesh Set");
return;
}
ImGui::Text(element->name.c_str());
}
void ElementDrawer::DrawMeshNormal(Mesh *element, const bool &debug, const std::string &label)
{
if(element == nullptr)
{
ImGui::Text("No Mesh Set");
return;
}
float item_spacing = ImGui::GetStyle().ItemSpacing.x;
ImVec2 label_size = ImGui::CalcTextSize(label.c_str());
@@ -1032,54 +1235,6 @@ namespace TSE::EDITOR
cursorCurrent.y += 2;
ImGui::SetCursorPos(cursorCurrent);
ImGui::TextUnformatted(label.c_str());
}
void ElementDrawer::DrawMeshNormal(Mesh *element, const bool &debug, const std::string &label)
{
ImGui::Text(("Name: " + element->name).c_str());
if(debug)
{
//ImGui::TextDisabled(("ID: " + to_string(element->id)).c_str());
}
ImGui::Separator();
ImGui::Text(("Vectex Count: " + std::to_string(element->vertecies.size())).c_str());
ImGui::Text(("Index Count: " + std::to_string(element->indecies.size())).c_str());
ImGui::Text(("UV Count: " + std::to_string(element->uvs.size())).c_str());
ImGui::Indent(20.0f);
if(ImGui::CollapsingHeader("Vertecies"))
{
ImGui::PushID("Verts");
ImGui::BeginDisabled();
for (int i = 0; i < element->vertecies.size(); i++)
{
ImGui::InputFloat3(std::to_string(i).c_str(), &element->vertecies[i].x);
}
ImGui::EndDisabled();
ImGui::PopID();
}
if(ImGui::CollapsingHeader("Indecies"))
{
ImGui::PushID("Inds");
ImGui::BeginDisabled();
for (int i = 0; i < element->indecies.size(); i++)
{
int val = element->indecies[i];
ImGui::InputInt(std::to_string(i).c_str(), &val);
}
ImGui::EndDisabled();
ImGui::PopID();
}
if(ImGui::CollapsingHeader("UVs"))
{
ImGui::PushID("Uvs");
ImGui::BeginDisabled();
for (int i = 0; i < element->uvs.size(); i++)
{
ImGui::InputFloat2(std::to_string(i).c_str(), &element->uvs[i].x);
}
ImGui::EndDisabled();
ImGui::PopID();
}
ImGui::Unindent(20.0f);
}
void ElementDrawer::DrawSpriteCompact(Sprite *element, const bool &debug, const std::string &label)
@@ -1155,7 +1310,7 @@ namespace TSE::EDITOR
ImGui::Image(element->GetTextureId(), texSize, {0,1}, {1,0});
ImGui::SameLine();
ImGui::SetCursorPos(ImVec2(field_size.y + 1,(60-label_size.y) / 2));
ImGui::TextUnformatted(element->name.c_str());
ImGui::TextUnformatted(element->name().c_str());
ImGui::EndChild();
@@ -1170,7 +1325,7 @@ namespace TSE::EDITOR
}
void ElementDrawer::DrawTextureNormal(Texture *element, const bool &debug, const std::string& label)
{
ImGui::TextUnformatted(element->name.c_str());
ImGui::TextUnformatted(element->name().c_str());
if(debug)
{
ImGui::Separator();
@@ -1187,6 +1342,98 @@ namespace TSE::EDITOR
ImVec2 texSize (available_width, (available_width) * ymultiplyer);
ImGui::Image(element->GetTextureId(), texSize, {0,1}, {1,0});
ImGui::Separator();
ImGui::SeparatorText("Import Settings");
TextureImportSettings& settings = element->GetImportSettings();
bool importSettingsChanged = false;
int importMode = static_cast<int>(settings.importMode);
const char* importModes[] = { "Raw Texture", "Sprite", "Sprite Set", "Nine Tile" };
if (ImGui::Combo("Import Mode", &importMode, importModes, IM_ARRAYSIZE(importModes)))
{
settings.importMode = static_cast<byte>(std::clamp(importMode, 0, IM_ARRAYSIZE(importModes) - 1));
importSettingsChanged = true;
}
int importCount[2] = {
static_cast<int>(settings.importCount.x),
static_cast<int>(settings.importCount.y)
};
if (ImGui::InputInt2("Import Count", importCount))
{
settings.importCount.x = static_cast<float>(std::max(1, importCount[0]));
settings.importCount.y = static_cast<float>(std::max(1, importCount[1]));
importSettingsChanged = true;
}
int bpp = static_cast<int>(settings.bpp);
if (ImGui::InputInt("BPP", &bpp))
{
settings.bpp = static_cast<uint>(std::max(0, bpp));
importSettingsChanged = true;
}
int chanels = static_cast<int>(settings.chanels);
if (ImGui::InputInt("Chanels", &chanels))
{
settings.chanels = static_cast<byte>(std::clamp(chanels, 0, 255));
importSettingsChanged = true;
}
if (InputText("Import Name", settings.name))
{
importSettingsChanged = true;
}
if (importSettingsChanged)
{
element->SaveAsset();
}
}
void ElementDrawer::DrawMaterialCompact(Material *element, const bool &debug, const std::string &label)
{
if(element == nullptr)
{
ImGui::Text("No Mesh Set");
return;
}
ImGui::Text(element->GetName().c_str());
}
void ElementDrawer::DrawMeshField(Mesh **element)
{
if(!BeginAssetField<Mesh>(element, "MeshAssetPicker"))
{
return;
}
DrawMeshCompact(*element, false, "");
OpenAssetPropertiesOnDoubleClick(*element);
EndAssetField<Mesh>();
AssetSelector<Mesh>(element, "MeshAssetPicker", "Search Mesh", "No meshes found", [](Mesh* mesh)
{
return mesh->name.empty() ? std::string("Unnamed Mesh") : mesh->name;
});
}
void ElementDrawer::DrawMaterialField(Material **element)
{
if(!BeginAssetField<Material>(element, "MaterialAssetPicker"))
{
return;
}
DrawMaterialCompact(*element, false, "");
OpenAssetPropertiesOnDoubleClick(*element);
EndAssetField<Material>();
AssetSelector<Material>(element, "MaterialAssetPicker", "Search Material", "No materials found", [](Material* mat)
{
return mat->GetName().empty() ? std::string("Unnamed Material") : mat->GetName();
});
}
} // namespace TSE::EDITOR

View File

@@ -14,6 +14,7 @@
#include "BehaviourScripts/AudioListener.hpp"
#include "BehaviourScripts/AudioSource.hpp"
#include "BehaviourScripts/TileMap.hpp"
#include "BehaviourScripts/OrdererSpriteSet.hpp"
namespace TSE::EDITOR
{
@@ -22,7 +23,8 @@ namespace TSE::EDITOR
None,
Transformable,
Scene,
Layer
Layer,
IAsset
};
struct Inspectable
@@ -53,6 +55,9 @@ namespace TSE::EDITOR
case InspectableType::Layer:
Draw(static_cast<Layer*>(element.ptr), debug);
break;
case InspectableType::IAsset:
Draw(static_cast<IAsset*>(element.ptr), debug);
break;
default:
TSE_WARNING("Draw not implemented for this type.");
break;
@@ -81,6 +86,8 @@ namespace TSE::EDITOR
static void Draw(Camera* element, const bool& debug);
static void Draw(ParticleSystem* element, const bool& debug);
static void Draw(TileMap* element, const bool& debug);
static void Draw(OrdererSpriteSet* element, const bool& debug);
static void Draw(IAsset* element, const bool& debug);
static void DrawAudioClipCompact(AudioClip* element, const bool& debug, const std::string& label);
static void DrawAudioClipNormal(AudioClip* element, const bool& debug, const std::string& label);
@@ -92,5 +99,9 @@ namespace TSE::EDITOR
static void DrawSpriteNormal(Sprite* element, const bool& debug, const std::string& label);
static void DrawTextureCompact(Texture* element, const bool& debug, const std::string& label);
static void DrawTextureNormal(Texture* element, const bool& debug, const std::string& label);
static void DrawMaterialCompact(Material* element, const bool& debug, const std::string& label);
static void DrawMeshField(Mesh** element);
static void DrawMaterialField(Material** element);
};
} // namespace TSE::EDITOR

View File

@@ -0,0 +1,751 @@
#include "AssetsView.hpp"
#include "elements/AssetLibrary.hpp"
#include "PathHelper.hpp"
#include "json.hpp"
#include "uuid.h"
#include "elements/Material.hpp"
#include "elements/Texture.hpp"
#include "PropertiesView.hpp"
#include <algorithm>
#include <cstdio>
#include <filesystem>
#include <vector>
namespace
{
std::vector<std::filesystem::directory_entry> GetDirectories(const std::filesystem::path& folderPath)
{
std::vector<std::filesystem::directory_entry> directories;
std::error_code errorCode;
for(const auto& entry : std::filesystem::directory_iterator(folderPath, errorCode))
{
std::error_code entryErrorCode;
if(entry.is_directory(entryErrorCode))
directories.push_back(entry);
}
std::sort(directories.begin(), directories.end(), [](const auto& a, const auto& b)
{
return a.path().filename().string() < b.path().filename().string();
});
return directories;
}
std::vector<std::filesystem::directory_entry> GetFiles(const std::filesystem::path& folderPath)
{
std::vector<std::filesystem::directory_entry> files;
std::error_code errorCode;
for(const auto& entry : std::filesystem::directory_iterator(folderPath, errorCode))
{
std::error_code entryErrorCode;
if(entry.is_regular_file(entryErrorCode) && entry.path().extension() != ".meta")
files.push_back(entry);
}
std::sort(files.begin(), files.end(), [](const auto& a, const auto& b)
{
return a.path().filename().string() < b.path().filename().string();
});
return files;
}
bool IsPathInFolder(const std::filesystem::path& path, const std::filesystem::path& folderPath)
{
if(path.empty() || folderPath.empty())
return false;
const std::filesystem::path relativePath = path.lexically_relative(folderPath);
if(relativePath.empty() || relativePath == ".")
return true;
const auto firstPart = relativePath.begin();
return firstPart != relativePath.end() && firstPart->string() != "..";
}
bool IsSameOrChildPath(const std::filesystem::path& path, const std::filesystem::path& possibleParentPath)
{
return IsPathInFolder(path.lexically_normal(), possibleParentPath.lexically_normal());
}
std::string FitTextToWidth(const std::string& text, float maxWidth)
{
if(ImGui::CalcTextSize(text.c_str()).x <= maxWidth)
return text;
std::string result = text;
while(result.size() > 3)
{
result.pop_back();
const std::string truncated = result + "...";
if(ImGui::CalcTextSize(truncated.c_str()).x <= maxWidth)
return truncated;
}
return "...";
}
}
TSE::EDITOR::AssetsView::AssetsView() : GuiWindow("Assets", ImGuiWindowFlags_NoScrollWithMouse | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoScrollbar)
{
}
void TSE::EDITOR::AssetsView::Define()
{
const float splitterWidth = 1.0f;
const float minPanelWidth = 100.0f;
const ImVec2 availableRegion = ImGui::GetContentRegionAvail();
if(availableRegion.x <= splitterWidth)
return;
if(leftPanelWidth < 0.0f)
leftPanelWidth = availableRegion.x / 3.0f;
float maxLeftPanelWidth = availableRegion.x - splitterWidth - minPanelWidth;
if(maxLeftPanelWidth < minPanelWidth)
maxLeftPanelWidth = (availableRegion.x - splitterWidth) * 0.5f;
if(leftPanelWidth < minPanelWidth)
leftPanelWidth = minPanelWidth;
if(leftPanelWidth > maxLeftPanelWidth)
leftPanelWidth = maxLeftPanelWidth;
if(leftPanelWidth < 1.0f)
leftPanelWidth = 1.0f;
auto clampLeftPanelWidth = [&]()
{
if(leftPanelWidth < minPanelWidth)
leftPanelWidth = minPanelWidth;
if(leftPanelWidth > maxLeftPanelWidth)
leftPanelWidth = maxLeftPanelWidth;
if(leftPanelWidth < 1.0f)
leftPanelWidth = 1.0f;
};
ImGuiWindowFlags childFlags = ImGuiWindowFlags_None;
if(ImGui::BeginChild("##AssetsTreeView", {leftPanelWidth, 0.0f}, ImGuiChildFlags_Borders, childFlags))
{
DefineTreeView();
}
ImGui::EndChild();
ImGui::SameLine(0.0f, 0.0f);
ImGui::InvisibleButton("##AssetsSplitter", {splitterWidth, availableRegion.y});
if(ImGui::IsItemActive())
{
leftPanelWidth += ImGui::GetIO().MouseDelta.x;
clampLeftPanelWidth();
}
if(ImGui::IsItemHovered() || ImGui::IsItemActive())
ImGui::SetMouseCursor(ImGuiMouseCursor_ResizeEW);
ImU32 splitterColor = ImGui::GetColorU32(ImGuiCol_Separator);
if(ImGui::IsItemHovered())
splitterColor = ImGui::GetColorU32(ImGuiCol_SeparatorHovered);
if(ImGui::IsItemActive())
splitterColor = ImGui::GetColorU32(ImGuiCol_SeparatorActive);
ImGui::GetWindowDrawList()->AddRectFilled(ImGui::GetItemRectMin(), ImGui::GetItemRectMax(), splitterColor);
ImGui::SameLine(0.0f, 0.0f);
if(ImGui::BeginChild("##AssetsItemView", {0.0f, 0.0f}, ImGuiChildFlags_Borders, childFlags))
{
DefineItemView();
}
ImGui::EndChild();
}
void TSE::EDITOR::AssetsView::DefineTreeView()
{
const bool assetsOpen = ImGui::CollapsingHeader("Assets", ImGuiTreeNodeFlags_DefaultOpen);
const std::filesystem::path assetPath = TSE::AssetLibrary::GetCurrentAssetPath();
std::error_code errorCode;
if(assetPath.empty() || !std::filesystem::exists(assetPath, errorCode) || !std::filesystem::is_directory(assetPath, errorCode))
{
DefineFolderNamePopup();
return;
}
AcceptAssetDrop(assetPath);
if(assetsOpen)
{
DisplayDirectoryTree(assetPath);
ImVec2 remainingSpace = ImGui::GetContentRegionAvail();
if(remainingSpace.y > 0.0f)
{
if(remainingSpace.x < 1.0f)
remainingSpace.x = 1.0f;
ImGui::InvisibleButton("##AssetsRootDropArea", remainingSpace);
AcceptAssetDrop(assetPath);
if(ImGui::BeginPopupContextItem("##AssetsRootContext"))
{
DefineFolderBackgroundContextMenu(assetPath);
ImGui::EndPopup();
}
}
}
DefineFolderNamePopup();
}
void TSE::EDITOR::AssetsView::DefineItemView()
{
DefineItemViewToolbar();
const std::filesystem::path browserPath = GetCurrentBrowserPath();
std::error_code errorCode;
if(browserPath.empty() || !std::filesystem::exists(browserPath, errorCode) || !std::filesystem::is_directory(browserPath, errorCode))
return;
if(ImGui::BeginChild("##AssetsItemScroll", {0.0f, 0.0f}, ImGuiChildFlags_None, ImGuiWindowFlags_AlwaysVerticalScrollbar))
{
if(itemViewMode == ItemViewMode::List)
DefineItemListView(browserPath);
else
DefineItemTileView(browserPath);
ImVec2 remainingSpace = ImGui::GetContentRegionAvail();
if(remainingSpace.y > 0.0f)
{
if(remainingSpace.x < 1.0f)
remainingSpace.x = 1.0f;
ImGui::InvisibleButton("##AssetsItemRootDropArea", remainingSpace);
AcceptAssetDrop(browserPath);
if(ImGui::BeginPopupContextItem("##AssetsItemRootContext"))
{
DefineFolderBackgroundContextMenu(browserPath);
ImGui::EndPopup();
}
}
if(ImGui::BeginPopupContextWindow("##AssetsItemBackgroundContext", ImGuiPopupFlags_MouseButtonRight | ImGuiPopupFlags_NoOpenOverItems))
{
DefineFolderBackgroundContextMenu(browserPath);
ImGui::EndPopup();
}
}
ImGui::EndChild();
}
void TSE::EDITOR::AssetsView::DefineItemViewToolbar()
{
const float buttonWidth = 64.0f;
const float totalWidth = buttonWidth * 2.0f + ImGui::GetStyle().ItemSpacing.x;
const float startX = ImGui::GetContentRegionAvail().x - totalWidth;
if(startX > 0.0f)
ImGui::SetCursorPosX(ImGui::GetCursorPosX() + startX);
if(ImGui::Selectable("List", itemViewMode == ItemViewMode::List, 0, {buttonWidth, 0.0f}))
itemViewMode = ItemViewMode::List;
ImGui::SameLine();
if(ImGui::Selectable("Tiles", itemViewMode == ItemViewMode::Tiles, 0, {buttonWidth, 0.0f}))
itemViewMode = ItemViewMode::Tiles;
}
void TSE::EDITOR::AssetsView::DefineItemListView(const std::filesystem::path& folderPath)
{
for(const auto& folderEntry : GetDirectories(folderPath))
DefineFolderListItem(folderEntry);
for(const auto& fileEntry : GetFiles(folderPath))
DefineFileListItem(fileEntry);
}
void TSE::EDITOR::AssetsView::DefineItemTileView(const std::filesystem::path& folderPath)
{
const float tileSize = 96.0f;
const float spacing = ImGui::GetStyle().ItemSpacing.x;
const float availableWidth = ImGui::GetContentRegionAvail().x;
int columns = static_cast<int>(availableWidth / (tileSize + spacing));
if(columns < 1)
columns = 1;
int index = 0;
auto nextTile = [&]()
{
index++;
if(index % columns != 0)
ImGui::SameLine();
};
for(const auto& folderEntry : GetDirectories(folderPath))
{
DefineFolderTileItem(folderEntry);
nextTile();
}
for(const auto& fileEntry : GetFiles(folderPath))
{
DefineFileTileItem(fileEntry);
nextTile();
}
}
void TSE::EDITOR::AssetsView::DefineFolderListItem(const std::filesystem::directory_entry& folderEntry)
{
const std::filesystem::path folderPath = folderEntry.path();
ImGui::PushID(folderPath.string().c_str());
ImGui::TextUnformatted("[D]");
ImGui::SameLine();
const bool selected = currentSelectedPath == folderPath;
ImGui::Selectable(folderPath.filename().string().c_str(), selected, 0, {ImGui::GetContentRegionAvail().x, 0.0f});
if(ImGui::IsItemHovered() && ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left))
currentSelectedPath = folderPath;
if(ImGui::BeginDragDropSource())
{
const std::string payloadPath = folderPath.string();
ImGui::SetDragDropPayload("ASSET_FOLDER_PATH", payloadPath.c_str(), payloadPath.size() + 1);
ImGui::Text("%s", folderPath.filename().string().c_str());
ImGui::EndDragDropSource();
}
AcceptAssetDrop(folderPath);
if(ImGui::BeginPopupContextItem())
{
DefineFolderContextMenu(folderPath);
ImGui::EndPopup();
}
ImGui::PopID();
}
void TSE::EDITOR::AssetsView::DefineFileListItem(const std::filesystem::directory_entry& fileEntry)
{
const std::filesystem::path filePath = fileEntry.path();
ImGui::PushID(filePath.string().c_str());
ImGui::TextUnformatted("[F]");
ImGui::SameLine();
ImGui::Selectable(filePath.filename().string().c_str(), false, 0, {ImGui::GetContentRegionAvail().x, 0.0f});
if(ImGui::IsItemClicked(ImGuiMouseButton_Left))
OpenFileProperties(filePath.string());
if(ImGui::BeginDragDropSource())
{
const std::string payloadPath = filePath.string();
ImGui::SetDragDropPayload("ASSET_FILE_PATH", payloadPath.c_str(), payloadPath.size() + 1);
ImGui::Text("%s", filePath.filename().string().c_str());
ImGui::EndDragDropSource();
}
if(ImGui::BeginPopupContextItem())
{
DefineFileContextMenu(filePath);
ImGui::EndPopup();
}
ImGui::PopID();
}
void TSE::EDITOR::AssetsView::DefineFolderTileItem(const std::filesystem::directory_entry& folderEntry)
{
const std::filesystem::path folderPath = folderEntry.path();
const float tileSize = 96.0f;
ImGui::PushID(folderPath.string().c_str());
const bool selected = currentSelectedPath == folderPath;
ImGui::Selectable("##FolderTile", selected, 0, {tileSize, tileSize});
if(ImGui::IsItemHovered() && ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left))
currentSelectedPath = folderPath;
ImVec2 itemMin = ImGui::GetItemRectMin();
ImVec2 itemMax = ImGui::GetItemRectMax();
ImDrawList* drawList = ImGui::GetWindowDrawList();
const std::string icon = "[D]";
const std::string name = FitTextToWidth(folderPath.filename().string(), tileSize - 12.0f);
const ImVec2 iconSize = ImGui::CalcTextSize(icon.c_str());
const ImVec2 nameSize = ImGui::CalcTextSize(name.c_str());
drawList->AddText({itemMin.x + (tileSize - iconSize.x) * 0.5f, itemMin.y + 22.0f}, ImGui::GetColorU32(ImGuiCol_Text), icon.c_str());
drawList->AddText({itemMin.x + (tileSize - nameSize.x) * 0.5f, itemMax.y - 28.0f}, ImGui::GetColorU32(ImGuiCol_Text), name.c_str());
if(ImGui::BeginDragDropSource())
{
const std::string payloadPath = folderPath.string();
ImGui::SetDragDropPayload("ASSET_FOLDER_PATH", payloadPath.c_str(), payloadPath.size() + 1);
ImGui::Text("%s", folderPath.filename().string().c_str());
ImGui::EndDragDropSource();
}
AcceptAssetDrop(folderPath);
if(ImGui::BeginPopupContextItem())
{
DefineFolderContextMenu(folderPath);
ImGui::EndPopup();
}
ImGui::PopID();
}
void TSE::EDITOR::AssetsView::DefineFileTileItem(const std::filesystem::directory_entry& fileEntry)
{
const std::filesystem::path filePath = fileEntry.path();
const float tileSize = 96.0f;
ImGui::PushID(filePath.string().c_str());
ImGui::Selectable("##FileTile", false, 0, {tileSize, tileSize});
if(ImGui::IsItemClicked(ImGuiMouseButton_Left))
OpenFileProperties(filePath.string());
ImVec2 itemMin = ImGui::GetItemRectMin();
ImVec2 itemMax = ImGui::GetItemRectMax();
ImDrawList* drawList = ImGui::GetWindowDrawList();
const std::string icon = "[F]";
const std::string name = FitTextToWidth(filePath.filename().string(), tileSize - 12.0f);
const ImVec2 iconSize = ImGui::CalcTextSize(icon.c_str());
const ImVec2 nameSize = ImGui::CalcTextSize(name.c_str());
drawList->AddText({itemMin.x + (tileSize - iconSize.x) * 0.5f, itemMin.y + 22.0f}, ImGui::GetColorU32(ImGuiCol_Text), icon.c_str());
drawList->AddText({itemMin.x + (tileSize - nameSize.x) * 0.5f, itemMax.y - 28.0f}, ImGui::GetColorU32(ImGuiCol_Text), name.c_str());
if(ImGui::BeginDragDropSource())
{
const std::string payloadPath = filePath.string();
ImGui::SetDragDropPayload("ASSET_FILE_PATH", payloadPath.c_str(), payloadPath.size() + 1);
ImGui::Text("%s", filePath.filename().string().c_str());
ImGui::EndDragDropSource();
}
if(ImGui::BeginPopupContextItem())
{
DefineFileContextMenu(filePath);
ImGui::EndPopup();
}
ImGui::PopID();
}
void TSE::EDITOR::AssetsView::DisplayDirectoryTree(const std::filesystem::path& folderPath)
{
std::vector<std::filesystem::directory_entry> childDirectories = GetDirectories(folderPath);
for(const auto& childDirectory : childDirectories)
{
const std::filesystem::path childPath = childDirectory.path();
const std::vector<std::filesystem::directory_entry> grandChildDirectories = GetDirectories(childPath);
ImGui::PushID(childPath.string().c_str());
ImGuiTreeNodeFlags flags = ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_OpenOnDoubleClick | ImGuiTreeNodeFlags_SpanFullWidth;
if(grandChildDirectories.empty())
flags |= ImGuiTreeNodeFlags_Leaf | ImGuiTreeNodeFlags_NoTreePushOnOpen;
if(currentSelectedPath == childPath)
flags |= ImGuiTreeNodeFlags_Selected;
const bool open = ImGui::TreeNodeEx(childPath.filename().string().c_str(), flags);
if(ImGui::IsItemClicked(ImGuiMouseButton_Left) && !ImGui::IsItemToggledOpen())
currentSelectedPath = childPath;
if(ImGui::BeginDragDropSource())
{
const std::string payloadPath = childPath.string();
ImGui::SetDragDropPayload("ASSET_FOLDER_PATH", payloadPath.c_str(), payloadPath.size() + 1);
ImGui::Text("%s", childPath.filename().string().c_str());
ImGui::EndDragDropSource();
}
AcceptAssetDrop(childPath);
bool folderDeleted = false;
if(ImGui::BeginPopupContextItem())
{
folderDeleted = DefineFolderContextMenu(childPath);
ImGui::EndPopup();
}
if(open && !grandChildDirectories.empty())
{
if(!folderDeleted)
DisplayDirectoryTree(childPath);
ImGui::TreePop();
}
ImGui::PopID();
}
}
bool TSE::EDITOR::AssetsView::DefineFolderContextMenu(const std::filesystem::path& folderPath)
{
if(ImGui::MenuItem("Rename"))
RequestFolderNamePopup(FolderPopupPurpose::Rename, folderPath);
bool folderDeleted = false;
if(ImGui::MenuItem("Delete"))
{
DeleteFolder(folderPath);
folderDeleted = true;
}
if(ImGui::MenuItem("New Folder"))
RequestFolderNamePopup(FolderPopupPurpose::NewFolder, folderPath);
return folderDeleted;
}
void TSE::EDITOR::AssetsView::DefineFolderBackgroundContextMenu(const std::filesystem::path& folderPath)
{
if(ImGui::MenuItem("New Folder"))
RequestFolderNamePopup(FolderPopupPurpose::NewFolder, folderPath);
}
bool TSE::EDITOR::AssetsView::DefineFileContextMenu(const std::filesystem::path& filePath)
{
if(ImGui::MenuItem("Rename"))
RequestFolderNamePopup(FolderPopupPurpose::Rename, filePath);
bool fileDeleted = false;
if(ImGui::MenuItem("Delete"))
{
DeleteFile(filePath);
fileDeleted = true;
}
return fileDeleted;
}
void TSE::EDITOR::AssetsView::AcceptAssetDrop(const std::filesystem::path& targetFolderPath)
{
if(ImGui::BeginDragDropTarget())
{
const ImGuiPayload* folderPayload = ImGui::AcceptDragDropPayload("ASSET_FOLDER_PATH");
if(folderPayload != nullptr && folderPayload->Data != nullptr)
{
const char* payloadPath = static_cast<const char*>(folderPayload->Data);
MoveFolder(std::filesystem::path(payloadPath), targetFolderPath);
}
const ImGuiPayload* filePayload = ImGui::AcceptDragDropPayload("ASSET_FILE_PATH");
if(filePayload != nullptr && filePayload->Data != nullptr)
{
const char* payloadPath = static_cast<const char*>(filePayload->Data);
MoveFile(std::filesystem::path(payloadPath), targetFolderPath);
}
ImGui::EndDragDropTarget();
}
}
void TSE::EDITOR::AssetsView::MoveFolder(const std::filesystem::path& sourceFolderPath, const std::filesystem::path& targetFolderPath)
{
const std::filesystem::path sourcePath = sourceFolderPath.lexically_normal();
const std::filesystem::path targetPath = targetFolderPath.lexically_normal();
if(sourcePath.empty() || targetPath.empty())
return;
if(sourcePath.parent_path() == targetPath)
return;
if(IsSameOrChildPath(targetPath, sourcePath))
return;
const std::filesystem::path newFolderPath = targetPath / sourcePath.filename();
std::error_code errorCode;
if(std::filesystem::exists(newFolderPath, errorCode))
return;
std::filesystem::rename(sourcePath, newFolderPath, errorCode);
if(errorCode)
return;
if(currentSelectedPath == sourcePath)
currentSelectedPath = newFolderPath;
else if(IsSameOrChildPath(currentSelectedPath, sourcePath))
currentSelectedPath = newFolderPath / currentSelectedPath.lexically_relative(sourcePath);
TSE::AssetLibrary::RescanAssets();
}
void TSE::EDITOR::AssetsView::MoveFile(const std::filesystem::path& sourceFilePath, const std::filesystem::path& targetFolderPath)
{
const std::filesystem::path sourcePath = sourceFilePath.lexically_normal();
const std::filesystem::path targetPath = targetFolderPath.lexically_normal();
if(sourcePath.empty() || targetPath.empty())
return;
if(sourcePath.parent_path() == targetPath)
return;
const std::filesystem::path newFilePath = targetPath / sourcePath.filename();
std::error_code errorCode;
if(std::filesystem::exists(newFilePath, errorCode))
return;
std::filesystem::rename(sourcePath, newFilePath, errorCode);
if(!errorCode)
TSE::AssetLibrary::RescanAssets();
}
void TSE::EDITOR::AssetsView::RequestFolderNamePopup(FolderPopupPurpose purpose, const std::filesystem::path& folderPath)
{
folderPopupPurpose = purpose;
pendingFolderPath = folderPath;
openFolderNamePopup = true;
focusFolderNameInput = true;
const std::string defaultName = purpose == FolderPopupPurpose::Rename ? folderPath.filename().string() : "";
std::snprintf(folderNameBuffer, sizeof(folderNameBuffer), "%s", defaultName.c_str());
}
void TSE::EDITOR::AssetsView::DefineFolderNamePopup()
{
if(openFolderNamePopup)
{
openFolderNamePopup = false;
ImGui::OpenPopup("Folder Name");
}
if(ImGui::BeginPopupModal("Folder Name", nullptr, ImGuiWindowFlags_AlwaysAutoResize))
{
ImGui::Text("Name:");
if(focusFolderNameInput)
{
ImGui::SetKeyboardFocusHere();
focusFolderNameInput = false;
}
ImGui::InputText("##FolderName", folderNameBuffer, sizeof(folderNameBuffer));
const bool inputValid = folderNameBuffer[0] != '\0';
if(!inputValid)
ImGui::TextColored(ImVec4(1, 0, 0, 1), "Not Valid.");
if(!inputValid)
ImGui::PushStyleVar(ImGuiStyleVar_Alpha, ImGui::GetStyle().Alpha * 0.5f);
if(ImGui::Button("OK", ImVec2(120, 0)) && inputValid)
{
ConfirmFolderNamePopup();
ImGui::CloseCurrentPopup();
}
if(!inputValid)
ImGui::PopStyleVar();
ImGui::SameLine();
if(ImGui::Button("Cancel", ImVec2(120, 0)))
{
folderPopupPurpose = FolderPopupPurpose::None;
ImGui::CloseCurrentPopup();
}
ImGui::EndPopup();
}
}
void TSE::EDITOR::AssetsView::ConfirmFolderNamePopup()
{
const std::string folderName = folderNameBuffer;
if(folderName.empty())
return;
std::error_code errorCode;
if(folderPopupPurpose == FolderPopupPurpose::Rename)
{
const std::filesystem::path newFolderPath = pendingFolderPath.parent_path() / folderName;
if(newFolderPath != pendingFolderPath && !std::filesystem::exists(newFolderPath, errorCode))
{
const bool selectedPathIsInRenamedFolder = IsSameOrChildPath(currentSelectedPath, pendingFolderPath);
const std::filesystem::path selectedRelativePath = selectedPathIsInRenamedFolder ? currentSelectedPath.lexically_relative(pendingFolderPath) : std::filesystem::path();
std::filesystem::rename(pendingFolderPath, newFolderPath, errorCode);
if(!errorCode)
{
if(selectedPathIsInRenamedFolder)
currentSelectedPath = newFolderPath / selectedRelativePath;
TSE::AssetLibrary::RescanAssets();
}
}
}
else if(folderPopupPurpose == FolderPopupPurpose::NewFolder)
{
const std::filesystem::path newFolderPath = pendingFolderPath / folderName;
if(!std::filesystem::exists(newFolderPath, errorCode))
{
std::filesystem::create_directory(newFolderPath, errorCode);
if(!errorCode)
{
currentSelectedPath = newFolderPath;
TSE::AssetLibrary::RescanAssets();
}
}
}
folderPopupPurpose = FolderPopupPurpose::None;
}
void TSE::EDITOR::AssetsView::DeleteFolder(const std::filesystem::path& folderPath)
{
std::error_code errorCode;
std::filesystem::remove_all(folderPath, errorCode);
if(!errorCode && IsPathInFolder(currentSelectedPath, folderPath))
currentSelectedPath.clear();
if(!errorCode)
TSE::AssetLibrary::RescanAssets();
}
void TSE::EDITOR::AssetsView::DeleteFile(const std::filesystem::path& filePath)
{
std::error_code errorCode;
std::filesystem::remove(filePath, errorCode);
if(!errorCode)
TSE::AssetLibrary::RescanAssets();
}
std::filesystem::path TSE::EDITOR::AssetsView::GetCurrentBrowserPath() const
{
std::error_code errorCode;
if(!currentSelectedPath.empty() && std::filesystem::exists(currentSelectedPath, errorCode) && std::filesystem::is_directory(currentSelectedPath, errorCode))
return currentSelectedPath;
return TSE::AssetLibrary::GetCurrentAssetPath();
}
void TSE::EDITOR::AssetsView::OpenFileProperties(string path)
{
string metapath = path + ".meta";
if(FileExists(metapath))
{
using json = nlohmann::ordered_json;
std::ifstream metaStream;
OpenFileReading(metaStream, metapath);
json meta = json::parse(metaStream);
uuids::uuid id = uuids::uuid::from_string((string)meta["id"]).value_or(id);
if(!AssetLibrary::HasValue(id)) return;
IAsset* assetPtr = nullptr;
if(meta["assetType"] == "material")
{
assetPtr = static_cast<IAsset*>(AssetLibrary::GetValue<Material*>(id));
}
else if(meta["assetType"] == "texture")
{
assetPtr = static_cast<IAsset*>(AssetLibrary::GetValue<Texture*>(id));
}
if(assetPtr != nullptr)
{
PropertiesView::SetInspectorElement(InspectableType::IAsset, assetPtr);
}
}
}

View File

@@ -0,0 +1,62 @@
#pragma once
#include "UI/base/GuiWindow.h"
#include <filesystem>
namespace TSE::EDITOR
{
class AssetsView : public GuiWindow
{
private:
enum class FolderPopupPurpose
{
None,
Rename,
NewFolder
};
enum class ItemViewMode
{
List,
Tiles
};
float leftPanelWidth = -1.0f;
ItemViewMode itemViewMode = ItemViewMode::List;
std::filesystem::path currentSelectedPath;
std::filesystem::path pendingFolderPath;
FolderPopupPurpose folderPopupPurpose = FolderPopupPurpose::None;
bool openFolderNamePopup = false;
bool focusFolderNameInput = false;
char folderNameBuffer[256] = "";
void DefineTreeView();
void DefineItemView();
void DefineItemViewToolbar();
void DefineItemListView(const std::filesystem::path& folderPath);
void DefineItemTileView(const std::filesystem::path& folderPath);
void DefineFolderListItem(const std::filesystem::directory_entry& folderEntry);
void DefineFileListItem(const std::filesystem::directory_entry& fileEntry);
void DefineFolderTileItem(const std::filesystem::directory_entry& folderEntry);
void DefineFileTileItem(const std::filesystem::directory_entry& fileEntry);
void DisplayDirectoryTree(const std::filesystem::path& folderPath);
bool DefineFolderContextMenu(const std::filesystem::path& folderPath);
void DefineFolderBackgroundContextMenu(const std::filesystem::path& folderPath);
bool DefineFileContextMenu(const std::filesystem::path& filePath);
void AcceptAssetDrop(const std::filesystem::path& targetFolderPath);
void MoveFolder(const std::filesystem::path& sourceFolderPath, const std::filesystem::path& targetFolderPath);
void MoveFile(const std::filesystem::path& sourceFilePath, const std::filesystem::path& targetFolderPath);
void RequestFolderNamePopup(FolderPopupPurpose purpose, const std::filesystem::path& folderPath);
void DefineFolderNamePopup();
void ConfirmFolderNamePopup();
void DeleteFolder(const std::filesystem::path& folderPath);
void DeleteFile(const std::filesystem::path& filePath);
std::filesystem::path GetCurrentBrowserPath() const;
void OpenFileProperties(string path);
public:
AssetsView();
void Define() override;
};
} // namespace TSE::EDITOR

View File

@@ -7,7 +7,6 @@
#include "enums/WindowType.hpp"
#include "Color.hpp"
namespace TSE::GLFW
{
class WindowGlfw : public IWindow

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

View File

@@ -1,78 +0,0 @@
#include "Mesh.hpp"
#include "MathF.hpp"
#include <cmath>
TSE::Mesh::Mesh()
{
name = "";
}
TSE::Mesh::Mesh(string _name, const std::vector<Vector3> &_vertecies, const std::vector<ushort> &_indecies, const std::vector<Vector2> &_uvs)
{
name = _name;
vertecies = std::move(_vertecies);
indecies = std::move(_indecies);
uvs = std::move(_uvs);
}
size_t TSE::Mesh::IndeciesCount() const
{
return indecies.size();
}
size_t TSE::Mesh::VerteciesCount() const
{
return vertecies.size();
}
TSE::Mesh TSE::Mesh::GetCircleMesh(ushort segments)
{
std::vector<Vector3> verts;
std::vector<ushort> indices;
std::vector<Vector2> uvs;
verts.emplace_back(0.0f, 0.0f, 0.0f);
uvs.emplace_back(0.5f, 0.5f);
float angleStep = 2.0f * TSE_PI / segments;
for (int i = 0; i <= segments; ++i) {
float angle = i * angleStep;
float x = std::cos(angle) * 0.5f;
float y = std::sin(angle) * 0.5f;
verts.emplace_back(x, y, 0);
uvs.emplace_back(x + 0.5f, y + 0.5f);
if (i > 0) {
indices.push_back(0);
indices.push_back(i);
indices.push_back(i + 1);
}
}
return Mesh("Circle", verts, indices, uvs);
}
TSE::Mesh TSE::Mesh::GetQuadMesh()
{
std::vector<Vector3> verts = {
Vector3(-0.5f, -0.5f, 0),
Vector3( 0.5f, -0.5f, 0),
Vector3( 0.5f, 0.5f, 0),
Vector3(-0.5f, 0.5f, 0)
};
std::vector<Vector2> uvs = {
Vector2(0.0f, 0.0f),
Vector2(1.0f, 0.0f),
Vector2(1.0f, 1.0f),
Vector2(0.0f, 1.0f)
};
std::vector<unsigned short> indices = {
0, 1, 2,
2, 3, 0
};
return Mesh("Quad", verts, indices, uvs);
}

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/gl.h"
namespace TSE::GLFW
namespace TSE::OpenGL
{
class CameraHelperOpenGL : public ICameraHelper
{
@@ -14,4 +14,4 @@ namespace TSE::GLFW
glViewport(0, 0, width, height);
};
};
} // namespace TSE::GLFW
} // namespace TSE::OpenGL

View File

@@ -6,13 +6,13 @@
#define RENDERER_MAX_SPRITES 20000
#define RENDERER_MAX_INDECIES 60000
TSE::GLFW::DefaultRendererOpenGL::DefaultRendererOpenGL(Shader &shader)
TSE::OpenGL::DefaultRendererOpenGL::DefaultRendererOpenGL(Shader &shader)
{
iboData = new ushort[RENDERER_MAX_INDECIES];
InitShader(shader, true);
}
TSE::GLFW::DefaultRendererOpenGL::~DefaultRendererOpenGL()
TSE::OpenGL::DefaultRendererOpenGL::~DefaultRendererOpenGL()
{
if(vao != 0)
glDeleteVertexArrays(1, &vao);
@@ -20,7 +20,7 @@ TSE::GLFW::DefaultRendererOpenGL::~DefaultRendererOpenGL()
delete ibo;
}
void TSE::GLFW::DefaultRendererOpenGL::InitShader(Shader &s, bool init)
void TSE::OpenGL::DefaultRendererOpenGL::InitShader(Shader &s, bool init)
{
if(!init) End();
if(vao != 0)
@@ -51,13 +51,13 @@ void TSE::GLFW::DefaultRendererOpenGL::InitShader(Shader &s, bool init)
if(!init) Begin();
}
void TSE::GLFW::DefaultRendererOpenGL::End()
void TSE::OpenGL::DefaultRendererOpenGL::End()
{
glUnmapBuffer(GL_ARRAY_BUFFER);
vbo.Unbind();
}
void TSE::GLFW::DefaultRendererOpenGL::Flush()
void TSE::OpenGL::DefaultRendererOpenGL::Flush()
{
lastShader->Flush();
@@ -91,18 +91,18 @@ void TSE::GLFW::DefaultRendererOpenGL::Flush()
iboOffset = 0;
}
void TSE::GLFW::DefaultRendererOpenGL::Begin()
void TSE::OpenGL::DefaultRendererOpenGL::Begin()
{
vbo.Bind();
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);
}
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);
if(lastShader == nullptr) InitShader(*s, true);
@@ -137,14 +137,14 @@ void TSE::GLFW::DefaultRendererOpenGL::Submit(const Transformable &trans, IShade
lastShader->Submit(trans, bufferPointer, stack, Redraw, *this);
}
void TSE::GLFW::DefaultRendererOpenGL::Redraw(IRenderer &rnd)
void TSE::OpenGL::DefaultRendererOpenGL::Redraw(IRenderer &rnd)
{
rnd.End();
rnd.Flush();
rnd.Begin();
}
bool TSE::GLFW::DefaultRendererOpenGL::CreateIbo()
bool TSE::OpenGL::DefaultRendererOpenGL::CreateIbo()
{
if(indexCount == 0) return true;
if(ibo != nullptr)

View File

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

View File

@@ -1,24 +1,36 @@
#include "GL/gl3w.h"
#include "GL/gl.h"
#include "OpenGLRenderingBackend.hpp"
#include "GLFW/glfw3.h"
#include "WindowGlfw.hpp"
#include "Debug.hpp"
#include "imgui/imgui.h"
#include "extern/imgui_impl_glfw.h"
#include "extern/imgui_impl_opengl3.h"
#include "PathHelper.hpp"
#include "elements/Texture.hpp"
#include "interfaces/ITexture.hpp"
#include "TextureHelperOpenGL.hpp"
#include "interfaces/IRenderer.hpp"
#include "BehaviourScripts/Camera.hpp"
#include "RenderTextureCreatorOpenGL.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){ }
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;
vsync = _vsync;
@@ -26,33 +38,50 @@ TSE::GLFW::OpenGLRenderingBackend::OpenGLRenderingBackend(Color _backgroundColor
useseImGui = _useseImGui;
}
TSE::GLFW::OpenGLRenderingBackend::~OpenGLRenderingBackend()
TSE::OpenGL::OpenGLRenderingBackend::~OpenGLRenderingBackend()
{
if(useseImGui)
{
ImGui_ImplOpenGL3_Shutdown();
#if defined(TSE_GLFW)
ImGui_ImplGlfw_Shutdown();
#elif defined(TSE_SDL3)
ImGui_ImplSDL3_Shutdown();
#endif
ImGui::DestroyContext();
}
}
void TSE::GLFW::OpenGLRenderingBackend::InitPreWindow()
void TSE::OpenGL::OpenGLRenderingBackend::InitPreWindow()
{
IRenderTexture::factory = new RenderTextureCreatorOpenGL();
Texture::helper = new TextureHelperOpenGL();
ITexture::helper = new TextureHelperOpenGL();
Camera::helper = new CameraHelperOpenGL();
#if defined(TSE_GLFW)
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, TSE_OPENGL_VERSION_MAJOR);
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_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);
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())
{
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.");
return false;
}
#if defined(TSE_GLFW)
if(vsync) glfwSwapInterval(1);
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("GLSL:" + std::string((const char*)glGetString(GL_SHADING_LANGUAGE_VERSION)));
@@ -88,7 +125,7 @@ bool TSE::GLFW::OpenGLRenderingBackend::InitPostWindow()
std::string imguiIniPath;
bool TSE::GLFW::OpenGLRenderingBackend::InitEnd()
bool TSE::OpenGL::OpenGLRenderingBackend::InitEnd()
{
if(useseImGui)
{
@@ -102,8 +139,15 @@ bool TSE::GLFW::OpenGLRenderingBackend::InitEnd()
io.IniFilename = imguiIniPath.c_str();
ImGui::StyleColorsDark();
#if defined(TSE_GLFW)
WindowGlfw* wnd = static_cast<WindowGlfw*>(window);
wnd->useImGui = 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");
Debug::Log("ImGui:" + std::string(ImGui::GetVersion()));
@@ -111,12 +155,12 @@ bool TSE::GLFW::OpenGLRenderingBackend::InitEnd()
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);
}
void TSE::GLFW::OpenGLRenderingBackend::onUpdate() const
void TSE::OpenGL::OpenGLRenderingBackend::onUpdate() const
{
int error = glGetError();
if(error != GL_NO_ERROR)
@@ -124,6 +168,12 @@ void TSE::GLFW::OpenGLRenderingBackend::onUpdate() const
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)
{
ImGui::Render();
@@ -132,19 +182,30 @@ void TSE::GLFW::OpenGLRenderingBackend::onUpdate() const
if(ImGui::GetIO().ConfigFlags & ImGuiConfigFlags_ViewportsEnable)
{
#if defined(TSE_GLFW)
GLFWwindow* backup_current_context = glfwGetCurrentContext();
#elif defined(TSE_SDL3)
SDL_GLContext backup_current_context = SDL_GL_GetCurrentContext();
#endif
ImGui::UpdatePlatformWindows();
ImGui::RenderPlatformWindowsDefault();
#if defined(TSE_GLFW)
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);
#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++)
{
@@ -158,12 +219,16 @@ void TSE::GLFW::OpenGLRenderingBackend::onClear() const
if(useseImGui)
{
ImGui_ImplOpenGL3_NewFrame();
#if defined(TSE_GLFW)
ImGui_ImplGlfw_NewFrame();
#elif defined(TSE_SDL3)
ImGui_ImplSDL3_NewFrame();
#endif
ImGui::NewFrame();
}
}
void TSE::GLFW::OpenGLRenderingBackend::onClearDepthBuffer() const
void TSE::OpenGL::OpenGLRenderingBackend::onClearDepthBuffer() const
{
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_MINOR 3
namespace TSE::GLFW
namespace TSE::OpenGL
{
class OpenGLRenderingBackend : public IRenderingBackend
{
@@ -25,4 +25,4 @@ namespace TSE::GLFW
void onClear() 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/IRenderTexture.hpp"
namespace TSE::GLFW
namespace TSE::OpenGL
{
class RenderTexture : public IRenderTexture
{
@@ -28,4 +28,4 @@ namespace TSE::GLFW
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 "RenderTexture.hpp"
namespace TSE::GLFW
namespace TSE::OpenGL
{
class RenderTextureCreatorOpenGL : public IRenderTextureCreator
{
@@ -13,4 +13,4 @@ namespace TSE::GLFW
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_RGBA;
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 "elements/Texture.hpp"
#include "elements/VolumeTexture3D.hpp"
namespace TSE::GLFW
namespace TSE::OpenGL
{
class TextureHelperOpenGL : public ITextureHelper
{
@@ -13,5 +14,11 @@ namespace TSE::GLFW
void Apply(Texture* tex) override;
void Regist(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 "Debug.hpp"
TSE::GLFW::FrameBuffer::FrameBuffer(const Vector2 &size, uint textureCount)
TSE::OpenGL::FrameBuffer::FrameBuffer(const Vector2 &size, uint textureCount)
{
textureOutputCount = textureCount;
width = size.x;
@@ -14,17 +14,17 @@ TSE::GLFW::FrameBuffer::FrameBuffer(const Vector2 &size, uint textureCount)
Initialize();
}
void TSE::GLFW::FrameBuffer::Bind()
void TSE::OpenGL::FrameBuffer::Bind()
{
glBindFramebuffer(GL_FRAMEBUFFER, bufferID);
}
void TSE::GLFW::FrameBuffer::Unbind()
void TSE::OpenGL::FrameBuffer::Unbind()
{
glBindFramebuffer(GL_FRAMEBUFFER, 0);
}
TSE::GLFW::FrameBuffer::~FrameBuffer()
TSE::OpenGL::FrameBuffer::~FrameBuffer()
{
glDeleteFramebuffers(1,&bufferID);
for (int i = 0; i < textureOutputCount; i++)
@@ -34,14 +34,14 @@ TSE::GLFW::FrameBuffer::~FrameBuffer()
glDeleteRenderbuffers(1, &depthRboID);
}
void TSE::GLFW::FrameBuffer::Resize(Vector2 size)
void TSE::OpenGL::FrameBuffer::Resize(Vector2 size)
{
width = size.x;
height = size.y;
shouldResize = true;
}
void TSE::GLFW::FrameBuffer::Update()
void TSE::OpenGL::FrameBuffer::Update()
{
if (!shouldResize) return;
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];
}
TSE::Vector2 TSE::GLFW::FrameBuffer::GetSize() const
TSE::Vector2 TSE::OpenGL::FrameBuffer::GetSize() const
{
return {width, height};
}
void TSE::GLFW::FrameBuffer::Initialize()
void TSE::OpenGL::FrameBuffer::Initialize()
{
glGenFramebuffers(1, &bufferID);
Bind();
@@ -85,7 +85,7 @@ void TSE::GLFW::FrameBuffer::Initialize()
Unbind();
}
void TSE::GLFW::FrameBuffer::LoadFBTexture()
void TSE::OpenGL::FrameBuffer::LoadFBTexture()
{
for (int i = 0; i < textureOutputCount; i++)
{
@@ -104,7 +104,7 @@ void TSE::GLFW::FrameBuffer::LoadFBTexture()
}
}
void TSE::GLFW::FrameBuffer::CreateFBTexture()
void TSE::OpenGL::FrameBuffer::CreateFBTexture()
{
glViewport(0,0, width, height);
//resize

View File

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

View File

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

View File

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

View File

@@ -1,35 +1,35 @@
#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);
}
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);
}
void TSE::GLFW::VertexBuffer::Bind()
void TSE::OpenGL::VertexBuffer::Bind()
{
glBindBuffer(GL_ARRAY_BUFFER, bufferID);
boundBuffer = bufferID;
}
void TSE::GLFW::VertexBuffer::Unbind()
void TSE::OpenGL::VertexBuffer::Unbind()
{
glBindBuffer(GL_ARRAY_BUFFER, 0);
boundBuffer = 0;
}
bool TSE::GLFW::VertexBuffer::IsBound()
bool TSE::OpenGL::VertexBuffer::IsBound()
{
return boundBuffer == bufferID;
}
TSE::GLFW::VertexBuffer::~VertexBuffer()
TSE::OpenGL::VertexBuffer::~VertexBuffer()
{
if(bufferID != 0)
{

View File

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

View File

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

View File

@@ -3,63 +3,63 @@
#include "Shader.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);
}
void TSE::GLFW::Shader::Unbind() const
void TSE::OpenGL::Shader::Unbind() const
{
Disable(true);
}
void TSE::GLFW::Shader::Enable(bool notify) const
void TSE::OpenGL::Shader::Enable(bool notify) const
{
activeProgramID = programID;
glUseProgram(programID);
if(notify) OnEnable();
}
void TSE::GLFW::Shader::Disable(bool notify) const
void TSE::OpenGL::Shader::Disable(bool notify) const
{
activeProgramID = 0;
glUseProgram(0);
if(notify) OnDisable();
}
void TSE::GLFW::Shader::Flush()
void TSE::OpenGL::Shader::Flush()
{
OnFlush();
}
void TSE::GLFW::Shader::DrawCall(int indexCount)
void TSE::OpenGL::Shader::DrawCall(int indexCount)
{
OnDrawCall(indexCount);
}
void TSE::GLFW::Shader::PostDraw()
void TSE::OpenGL::Shader::PostDraw()
{
OnPostDraw();
}
void TSE::GLFW::Shader::Submit(const Transformable &t, float *&target, TransformationStack &stack, void (*restartDrawcall)(IRenderer &), IRenderer &rnd)
void TSE::OpenGL::Shader::Submit(const Transformable &t, float *&target, TransformationStack &stack, void (*restartDrawcall)(IRenderer &), IRenderer &rnd)
{
OnSubmit(t, target, stack, restartDrawcall, rnd);
}
bool TSE::GLFW::Shader::IsEnabled() const
bool TSE::OpenGL::Shader::IsEnabled() const
{
return programID == activeProgramID;
}
int TSE::GLFW::Shader::packageSize()
int TSE::OpenGL::Shader::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();
@@ -85,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);
}
int TSE::GLFW::Shader::GetUniformLocation(const char *name)
int TSE::OpenGL::Shader::GetUniformLocation(const char *name)
{
auto it = uniformLocations.find(name);
if (it != uniformLocations.end()) return it->second;
@@ -100,44 +100,44 @@ int TSE::GLFW::Shader::GetUniformLocation(const char *name)
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);
}
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);
}
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];
value->ToArrayColumnMajor(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);
}
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);
}
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);
}
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);
}
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);
}

View File

@@ -9,7 +9,7 @@
#include "TransformationStack.hpp"
#include "interfaces/IRenderer.hpp"
namespace TSE::GLFW
namespace TSE::OpenGL
{
class Shader : public IShader
{
@@ -54,4 +54,4 @@ namespace TSE::GLFW
void SetUniform(const char* name, const Vector3* 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 "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);
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);
}
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;
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;
}
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;
OpenFileReading(stream, path);

View File

@@ -3,7 +3,7 @@
#include "Types.hpp"
#include <memory>
namespace TSE::GLFW
namespace TSE::OpenGL
{
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> LoadFromPath(const std::string& path, int shaderType);
};
} // namespace TSE
} // namespace OpenGL

View File

@@ -5,7 +5,7 @@
#include "basicOrderedSpriteSetShaderGLSL.hpp"
using namespace TSE;
using namespace TSE::GLFW;
using namespace TSE::OpenGL;
#define SHADER_MESH_INDEX 0
#define SHADER_POS_INDEX 1
@@ -17,21 +17,21 @@ using namespace TSE::GLFW;
#define SHADER_PACKAGE_SIZE sizeof(float) * (3 + 1 + 1 + 1 + 2)
TSE::GLFW::BasicOrderedSpriteSetShader* BasicOrderedSpriteSetShader::instance = nullptr;
TSE::OpenGL::BasicOrderedSpriteSetShader* BasicOrderedSpriteSetShader::instance = nullptr;
TSE::GLFW::BasicOrderedSpriteSetShader *TSE::GLFW::BasicOrderedSpriteSetShader::Instance()
TSE::OpenGL::BasicOrderedSpriteSetShader *TSE::OpenGL::BasicOrderedSpriteSetShader::Instance()
{
return instance;
}
void TSE::GLFW::BasicOrderedSpriteSetShader::Destroy()
void TSE::OpenGL::BasicOrderedSpriteSetShader::Destroy()
{
if(instance != nullptr)
delete instance;
instance = nullptr;
}
void TSE::GLFW::BasicOrderedSpriteSetShader::Init(float width, float height)
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));
@@ -44,18 +44,19 @@ void TSE::GLFW::BasicOrderedSpriteSetShader::Init(float width, float height)
instance->Disable();
}
TSE::GLFW::BasicOrderedSpriteSetShader::BasicOrderedSpriteSetShader(std::vector<std::unique_ptr<ShaderPart>> &&parts) : Shader(parts)
TSE::OpenGL::BasicOrderedSpriteSetShader::BasicOrderedSpriteSetShader(std::vector<std::unique_ptr<ShaderPart>> &&parts) : Shader(parts)
{
name = "Basic Ordered Sprite Set Shader";
PackageSize = SHADER_PACKAGE_SIZE;
}
TSE::GLFW::BasicOrderedSpriteSetShader::~BasicOrderedSpriteSetShader()
TSE::OpenGL::BasicOrderedSpriteSetShader::~BasicOrderedSpriteSetShader()
{
if (meshVBO) glDeleteBuffers(1, &meshVBO);
if (meshIBO) glDeleteBuffers(1, &meshIBO);
}
void TSE::GLFW::BasicOrderedSpriteSetShader::SetMesh(const void *verts, int vertCount, int stride, int floatCountPerVertex, int posOffsetBytes, GLenum primitive, const void *indices, int indexCount, GLenum indexType)
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);
@@ -96,7 +97,7 @@ void TSE::GLFW::BasicOrderedSpriteSetShader::SetMesh(const void *verts, int vert
glBindVertexArray(prevVAO);
}
void TSE::GLFW::BasicOrderedSpriteSetShader::OnEnable() const
void TSE::OpenGL::BasicOrderedSpriteSetShader::OnEnable() const
{
if (!meshReady)
{
@@ -138,7 +139,7 @@ void TSE::GLFW::BasicOrderedSpriteSetShader::OnEnable() const
glVertexAttribDivisor(SHADER_SCALE_INDEX, 1);
}
void TSE::GLFW::BasicOrderedSpriteSetShader::OnDisable() const
void TSE::OpenGL::BasicOrderedSpriteSetShader::OnDisable() const
{
glDisableVertexAttribArray(SHADER_MESH_INDEX);
glDisableVertexAttribArray(SHADER_POS_INDEX);
@@ -148,14 +149,14 @@ void TSE::GLFW::BasicOrderedSpriteSetShader::OnDisable() const
glDisableVertexAttribArray(SHADER_SCALE_INDEX);
}
void TSE::GLFW::BasicOrderedSpriteSetShader::OnFlush()
void TSE::OpenGL::BasicOrderedSpriteSetShader::OnFlush()
{
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, TextureID);
glDisable(GL_BLEND);
}
void TSE::GLFW::BasicOrderedSpriteSetShader::OnDrawCall(int indexCount)
void TSE::OpenGL::BasicOrderedSpriteSetShader::OnDrawCall(int indexCount)
{
if (instanceCount <= 0) return;
SetUniform("spriteCount", &SpriteCount);
@@ -177,13 +178,13 @@ void TSE::GLFW::BasicOrderedSpriteSetShader::OnDrawCall(int indexCount)
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, (GLuint)prevElementBuffer);
}
void TSE::GLFW::BasicOrderedSpriteSetShader::OnPostDraw()
void TSE::OpenGL::BasicOrderedSpriteSetShader::OnPostDraw()
{
glEnable(GL_BLEND);
instanceCount = 0;
}
void TSE::GLFW::BasicOrderedSpriteSetShader::OnSubmit(const Transformable &t, float *&target, TransformationStack &stack, void (*restartDrawcall)(IRenderer &), IRenderer &rnd)
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;

View File

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

View File

@@ -12,21 +12,21 @@
#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;
}
void TSE::GLFW::BasicParticleShader::Destroy()
void TSE::OpenGL::BasicParticleShader::Destroy()
{
if(instance != nullptr)
delete instance;
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;
parts.push_back(ShaderPart::LoadFromString(vertPart, GL_VERTEX_SHADER));
@@ -34,18 +34,19 @@ void TSE::GLFW::BasicParticleShader::Init(float width, float height)
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)
{
name = "Basic Unlit Particle Shader";
PackageSize = SHADER_PACKAGE_SIZE;
}
TSE::GLFW::BasicParticleShader::~BasicParticleShader()
TSE::OpenGL::BasicParticleShader::~BasicParticleShader()
{
if (meshVBO) glDeleteBuffers(1, &meshVBO);
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;
glGetIntegerv(GL_VERTEX_ARRAY_BINDING, &prevVAO);
@@ -86,7 +87,7 @@ void TSE::GLFW::BasicParticleShader::SetMesh(const void *verts, int vertCount, i
glBindVertexArray(prevVAO);
}
void TSE::GLFW::BasicParticleShader::OnEnable() const
void TSE::OpenGL::BasicParticleShader::OnEnable() const
{
if (!meshReady)
{
@@ -128,7 +129,7 @@ void TSE::GLFW::BasicParticleShader::OnEnable() const
glVertexAttribDivisor(SHADER_COLOR_INDEX, 1);
}
void TSE::GLFW::BasicParticleShader::OnDisable() const
void TSE::OpenGL::BasicParticleShader::OnDisable() const
{
glDisableVertexAttribArray(SHADER_MESH_INDEX);
glDisableVertexAttribArray(SHADER_POS_INDEX);
@@ -137,11 +138,11 @@ void TSE::GLFW::BasicParticleShader::OnDisable() const
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;
@@ -163,7 +164,7 @@ void TSE::GLFW::BasicParticleShader::OnDrawCall(int indexCount)
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));
if (!r) return;

View File

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

View File

@@ -11,21 +11,21 @@
#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;
}
void TSE::GLFW::BasicShader::Destroy()
void TSE::OpenGL::BasicShader::Destroy()
{
if(instance != nullptr)
delete instance;
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;
parts.push_back(ShaderPart::LoadFromString(vert, GL_VERTEX_SHADER));
@@ -33,12 +33,13 @@ void TSE::GLFW::BasicShader::Init(float width, float height)
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)
{
name = "Basic Unlit Shader";
PackageSize = SHADER_PACKAGE_SIZE;
}
void TSE::GLFW::BasicShader::OnEnable() const
void TSE::OpenGL::BasicShader::OnEnable() const
{
glEnableVertexAttribArray(SHADER_VERTEX_INDEX);
glVertexAttribPointer(SHADER_VERTEX_INDEX, 3, GL_FLOAT, false, SHADER_PACKAGE_SIZE, (void*)0);
@@ -46,22 +47,22 @@ void TSE::GLFW::BasicShader::OnEnable() const
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_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);
}
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));
if (!r) return;

View File

@@ -2,7 +2,7 @@
#include "Shader.hpp"
namespace TSE::GLFW
namespace TSE::OpenGL
{
class BasicShader : public Shader
{
@@ -22,4 +22,4 @@ namespace TSE::GLFW
void OnDrawCall(int indexCount) 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))
TSE::GLFW::BasicTextureShader* TSE::GLFW::BasicTextureShader::instance = nullptr;
std::map<TSE::uint, float> TSE::GLFW::BasicTextureShader::textureSlots;
TSE::OpenGL::BasicTextureShader* TSE::OpenGL::BasicTextureShader::instance = nullptr;
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;
}
void TSE::GLFW::BasicTextureShader::Destroy()
void TSE::OpenGL::BasicTextureShader::Destroy()
{
if(instance != nullptr)
delete instance;
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;
parts.push_back(ShaderPart::LoadFromString(vertT, GL_VERTEX_SHADER));
@@ -41,12 +41,13 @@ void TSE::GLFW::BasicTextureShader::Init(float width, float height)
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)
{
name = "Basic Unlit Texture Shader";
PackageSize = SHADER_PACKAGE_SIZE;
}
void TSE::GLFW::BasicTextureShader::OnEnable() const
void TSE::OpenGL::BasicTextureShader::OnEnable() const
{
glEnableVertexAttribArray(SHADER_VERTEX_INDEX);
glVertexAttribPointer(SHADER_VERTEX_INDEX, 3, GL_FLOAT, false, SHADER_PACKAGE_SIZE, (void*)0);
@@ -58,7 +59,7 @@ void TSE::GLFW::BasicTextureShader::OnEnable() const
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_COLOR_INDEX);
@@ -66,7 +67,7 @@ void TSE::GLFW::BasicTextureShader::OnDisable() const
glDisableVertexAttribArray(SHADER_TID_INDEX);
}
void TSE::GLFW::BasicTextureShader::OnFlush()
void TSE::OpenGL::BasicTextureShader::OnFlush()
{
auto it = textureSlots.begin();
for (int i = 0; i < textureSlots.size(); i++, it++)
@@ -76,12 +77,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);
}
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));
if (!r) return;

View File

@@ -4,7 +4,7 @@
#include "Types.hpp"
#include <map>
namespace TSE::GLFW
namespace TSE::OpenGL
{
class BasicTextureShader : public Shader
{
@@ -25,4 +25,4 @@ namespace TSE::GLFW
void OnDrawCall(int indexCount) 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)
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;
}
void TSE::GLFW::BasicTileMapShader::Destroy()
void TSE::OpenGL::BasicTileMapShader::Destroy()
{
if(instance != nullptr)
delete instance;
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;
parts.push_back(ShaderPart::LoadFromString(vertTile, GL_VERTEX_SHADER));
@@ -37,18 +37,19 @@ void TSE::GLFW::BasicTileMapShader::Init(float width, float height)
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)
{
name = "Basic Unlit TileMap Shader";
PackageSize = SHADER_PACKAGE_SIZE;
}
TSE::GLFW::BasicTileMapShader::~BasicTileMapShader()
TSE::OpenGL::BasicTileMapShader::~BasicTileMapShader()
{
if (meshVBO) glDeleteBuffers(1, &meshVBO);
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;
glGetIntegerv(GL_VERTEX_ARRAY_BINDING, &prevVAO);
@@ -89,7 +90,7 @@ void TSE::GLFW::BasicTileMapShader::SetMesh(const void *verts, int vertCount, in
glBindVertexArray(prevVAO);
}
void TSE::GLFW::BasicTileMapShader::OnEnable() const
void TSE::OpenGL::BasicTileMapShader::OnEnable() const
{
if (!meshReady)
{
@@ -121,20 +122,20 @@ void TSE::GLFW::BasicTileMapShader::OnEnable() const
glVertexAttribDivisor(SHADER_SPRITE_INDEX, 1);
}
void TSE::GLFW::BasicTileMapShader::OnDisable() const
void TSE::OpenGL::BasicTileMapShader::OnDisable() const
{
glDisableVertexAttribArray(SHADER_MESH_INDEX);
glDisableVertexAttribArray(SHADER_POS_INDEX);
glDisableVertexAttribArray(SHADER_SPRITE_INDEX);
}
void TSE::GLFW::BasicTileMapShader::OnFlush()
void TSE::OpenGL::BasicTileMapShader::OnFlush()
{
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, TextureID);
}
void TSE::GLFW::BasicTileMapShader::OnDrawCall(int indexCount)
void TSE::OpenGL::BasicTileMapShader::OnDrawCall(int indexCount)
{
if (instanceCount <= 0) return;
SetUniform("spriteCount", &SpriteCount);
@@ -158,7 +159,7 @@ void TSE::GLFW::BasicTileMapShader::OnDrawCall(int indexCount)
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));
if (!r) return;

View File

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

View File

@@ -7,7 +7,7 @@
#include "basicOrderedSpriteSetShader.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);
BasicTextureShader::Init(width, height);
@@ -15,15 +15,15 @@ void TSE::GLFW::LoadBasicShaders(float width, float height)
BasicParticleShader::Init(width, height);
BasicTileMapShader::Init(width, height);
BasicOrderedSpriteSetShader::Init(width, height);
ShaderRegistry::SetShader("Basic Unlit Shader", BasicShader::Instance());
ShaderRegistry::SetShader("Basic Unlit Texture Shader", BasicTextureShader::Instance());
ShaderRegistry::SetShader("Basic Unlit Dithering Shader", DitheringShader::Instance());
ShaderRegistry::SetShader("Basic Unlit Particle Shader", BasicParticleShader::Instance());
ShaderRegistry::SetShader("Basic Unlit TileMap Shader", BasicTileMapShader::Instance());
ShaderRegistry::SetShader("Basic Ordered Sprite Set Shader", BasicOrderedSpriteSetShader::Instance());
ShaderRegistry::SetShader(BasicShader::Instance());
ShaderRegistry::SetShader(BasicTextureShader::Instance());
ShaderRegistry::SetShader(DitheringShader::Instance());
ShaderRegistry::SetShader(BasicParticleShader::Instance());
ShaderRegistry::SetShader(BasicTileMapShader::Instance());
ShaderRegistry::SetShader(BasicOrderedSpriteSetShader::Instance());
}
void TSE::GLFW::UnLoadBasicShaders()
void TSE::OpenGL::UnLoadBasicShaders()
{
ShaderRegistry::RemoveShader("Basic Unlit Shader");
ShaderRegistry::RemoveShader("Basic Unlit Texture Shader");

View File

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

View File

@@ -10,21 +10,21 @@
#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;
}
void TSE::GLFW::DitheringShader::Destroy()
void TSE::OpenGL::DitheringShader::Destroy()
{
if(instance != nullptr)
delete instance;
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;
parts.push_back(ShaderPart::LoadFromString(vertD, GL_VERTEX_SHADER));
@@ -32,12 +32,13 @@ void TSE::GLFW::DitheringShader::Init(float width, float height)
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)
{
name = "Basic Unlit Dithering Shader";
PackageSize = SHADER_PACKAGE_SIZE;
}
void TSE::GLFW::DitheringShader::OnEnable() const
void TSE::OpenGL::DitheringShader::OnEnable() const
{
glEnableVertexAttribArray(SHADER_VERTEX_INDEX);
glVertexAttribPointer(SHADER_VERTEX_INDEX, 3, GL_FLOAT, false, SHADER_PACKAGE_SIZE, (void*)0);
@@ -45,22 +46,22 @@ void TSE::GLFW::DitheringShader::OnEnable() const
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_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);
}
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));
if (!r) return;

View File

@@ -2,7 +2,7 @@
#include "Shader.hpp"
namespace TSE::GLFW
namespace TSE::OpenGL
{
class DitheringShader : public Shader
{
@@ -22,4 +22,4 @@ namespace TSE::GLFW
void OnDrawCall(int indexCount) 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)
#project name
project(TSE_GlfwOpenGlImpl)
project(TSE_Sdl3Impl)
#cpp settings
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_Core/src)
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
if(Lib)
add_library(TSE_GlfwOpenGlImpl SHARED ${CPP_SOURCE_TSE})
add_library(TSE_Sdl3Impl SHARED ${CPP_SOURCE_TSE})
else()
add_library(TSE_GlfwOpenGlImpl STATIC ${CPP_SOURCE_TSE})
add_library(TSE_Sdl3Impl STATIC ${CPP_SOURCE_TSE})
endif()
#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_ */

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