implemented basic shader expressions
This commit is contained in:
@@ -31,6 +31,13 @@ Asset::~Asset()
|
||||
{
|
||||
}
|
||||
|
||||
void Asset::updateByteSize()
|
||||
{
|
||||
ArchiveBuffer buffer;
|
||||
save(buffer);
|
||||
byteSize = buffer.size();
|
||||
}
|
||||
|
||||
std::string Asset::getFolderPath() const
|
||||
{
|
||||
return folderPath;
|
||||
|
||||
@@ -18,6 +18,8 @@ public:
|
||||
Asset(std::string_view folderPath, std::string_view name);
|
||||
virtual ~Asset();
|
||||
|
||||
void updateByteSize();
|
||||
|
||||
virtual void save(ArchiveBuffer& buffer) const = 0;
|
||||
virtual void load(ArchiveBuffer& buffer) = 0;
|
||||
|
||||
@@ -27,6 +29,8 @@ public:
|
||||
std::string getFolderPath() const;
|
||||
// returns the identifier with which it can be found from the asset registry
|
||||
std::string getAssetIdentifier() const;
|
||||
// returns the size of the assets data in bytes(excluding name and folder)
|
||||
uint64 getByteSize() const { return byteSize; }
|
||||
|
||||
constexpr Status getStatus()
|
||||
{
|
||||
@@ -41,7 +45,7 @@ protected:
|
||||
std::string name;
|
||||
std::string assetId;
|
||||
Status status;
|
||||
uint32 byteSize;
|
||||
uint64 byteSize;
|
||||
};
|
||||
DEFINE_REF(Asset)
|
||||
} // namespace Seele
|
||||
@@ -4,10 +4,6 @@
|
||||
#include "TextureAsset.h"
|
||||
#include "MaterialAsset.h"
|
||||
#include "MaterialInstanceAsset.h"
|
||||
#include "FontLoader.h"
|
||||
#include "TextureLoader.h"
|
||||
#include "MaterialLoader.h"
|
||||
#include "MeshLoader.h"
|
||||
#include "Graphics/Mesh.h"
|
||||
#include "Graphics/Graphics.h"
|
||||
#include "Window/WindowManager.h"
|
||||
@@ -29,46 +25,6 @@ void AssetRegistry::init(const std::string& rootFolder, Gfx::PGraphics graphics)
|
||||
get().initialize(rootFolder, graphics);
|
||||
}
|
||||
|
||||
void AssetRegistry::importMesh(MeshImportArgs args)
|
||||
{
|
||||
if (get().getOrCreateFolder(args.importPath)->meshes.contains(args.filePath.stem().string()))
|
||||
{
|
||||
// skip importing duplicates
|
||||
return;
|
||||
}
|
||||
get().meshLoader->importAsset(args);
|
||||
}
|
||||
|
||||
void AssetRegistry::importTexture(TextureImportArgs args)
|
||||
{
|
||||
if (get().getOrCreateFolder(args.importPath)->textures.contains(args.filePath.stem().string()))
|
||||
{
|
||||
// skip importing duplicates
|
||||
return;
|
||||
}
|
||||
get().textureLoader->importAsset(args);
|
||||
}
|
||||
|
||||
void AssetRegistry::importFont(FontImportArgs args)
|
||||
{
|
||||
if (get().getOrCreateFolder(args.importPath)->fonts.contains(args.filePath.stem().string()))
|
||||
{
|
||||
// skip importing duplicates
|
||||
return;
|
||||
}
|
||||
get().fontLoader->importAsset(args);
|
||||
}
|
||||
|
||||
void AssetRegistry::importMaterial(MaterialImportArgs args)
|
||||
{
|
||||
if (get().getOrCreateFolder(args.importPath)->materials.contains(args.filePath.stem().string()))
|
||||
{
|
||||
// skip importing duplicates
|
||||
return;
|
||||
}
|
||||
get().materialLoader->importAsset(args);
|
||||
}
|
||||
|
||||
PMeshAsset AssetRegistry::findMesh(const std::string &filePath)
|
||||
{
|
||||
AssetFolder* folder = get().assetRoot;
|
||||
@@ -150,15 +106,12 @@ void AssetRegistry::saveRegistry()
|
||||
get().saveRegistryInternal();
|
||||
}
|
||||
|
||||
|
||||
void AssetRegistry::initialize(const std::filesystem::path &_rootFolder, Gfx::PGraphics _graphics)
|
||||
{
|
||||
this->graphics = _graphics;
|
||||
this->rootFolder = _rootFolder;
|
||||
this->assetRoot = new AssetFolder("");
|
||||
this->fontLoader = new FontLoader(graphics);
|
||||
this->meshLoader = new MeshLoader(graphics);
|
||||
this->textureLoader = new TextureLoader(graphics);
|
||||
this->materialLoader = new MaterialLoader(graphics);
|
||||
loadRegistryInternal();
|
||||
}
|
||||
|
||||
@@ -168,7 +121,6 @@ void AssetRegistry::loadRegistryInternal()
|
||||
loadFolder(assetRoot);
|
||||
}
|
||||
|
||||
|
||||
void AssetRegistry::peekFolder(AssetFolder* folder)
|
||||
{
|
||||
for (const auto& entry : std::filesystem::directory_iterator(rootFolder / folder->folderPath))
|
||||
@@ -192,49 +144,10 @@ void AssetRegistry::peekFolder(AssetFolder* folder)
|
||||
|
||||
ArchiveBuffer buffer(graphics);
|
||||
buffer.readFromStream(stream);
|
||||
|
||||
// Read asset type
|
||||
uint64 identifier;
|
||||
Serialization::load(buffer, identifier);
|
||||
|
||||
// Read name
|
||||
std::string name;
|
||||
Serialization::load(buffer, name);
|
||||
|
||||
// Read folder
|
||||
std::string folderPath;
|
||||
Serialization::load(buffer, folderPath);
|
||||
|
||||
PAsset asset;
|
||||
switch (identifier)
|
||||
{
|
||||
case TextureAsset::IDENTIFIER:
|
||||
asset = new TextureAsset(folderPath, name);
|
||||
folder->textures[asset->getName()] = asset;
|
||||
break;
|
||||
case MeshAsset::IDENTIFIER:
|
||||
asset = new MeshAsset(folderPath, name);
|
||||
folder->meshes[asset->getName()] = asset;
|
||||
break;
|
||||
case MaterialAsset::IDENTIFIER:
|
||||
asset = new MaterialAsset(folderPath, name);
|
||||
folder->materials[asset->getName()] = asset;
|
||||
break;
|
||||
case MaterialInstanceAsset::IDENTIFIER:
|
||||
asset = new MaterialInstanceAsset(folderPath, name);
|
||||
// TODO
|
||||
break;
|
||||
case FontAsset::IDENTIFIER:
|
||||
asset = new FontAsset(folderPath, name);
|
||||
folder->fonts[asset->getName()] = asset;
|
||||
break;
|
||||
default:
|
||||
throw new std::logic_error("Unknown Identifier");
|
||||
}
|
||||
peekAsset(buffer);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void AssetRegistry::loadFolder(AssetFolder* folder)
|
||||
{
|
||||
for (const auto& entry : std::filesystem::directory_iterator(rootFolder / folder->folderPath))
|
||||
@@ -251,45 +164,101 @@ void AssetRegistry::loadFolder(AssetFolder* folder)
|
||||
|
||||
ArchiveBuffer buffer(graphics);
|
||||
buffer.readFromStream(stream);
|
||||
|
||||
// Read asset type
|
||||
uint64 identifier;
|
||||
Serialization::load(buffer, identifier);
|
||||
|
||||
// Read name
|
||||
std::string name;
|
||||
Serialization::load(buffer, name);
|
||||
|
||||
// Read folder
|
||||
std::string folderPath;
|
||||
Serialization::load(buffer, folderPath);
|
||||
|
||||
PAsset asset;
|
||||
switch (identifier)
|
||||
{
|
||||
case TextureAsset::IDENTIFIER:
|
||||
asset = folder->textures.at(name);
|
||||
break;
|
||||
case MeshAsset::IDENTIFIER:
|
||||
asset = folder->meshes.at(name);
|
||||
break;
|
||||
case MaterialAsset::IDENTIFIER:
|
||||
asset = folder->materials.at(name);
|
||||
break;
|
||||
case MaterialInstanceAsset::IDENTIFIER:
|
||||
//asset = new MaterialInstanceAsset(path);
|
||||
// TODO
|
||||
break;
|
||||
case FontAsset::IDENTIFIER:
|
||||
asset = folder->fonts.at(name);
|
||||
break;
|
||||
default:
|
||||
throw new std::logic_error("Unknown Identifier");
|
||||
}
|
||||
asset->load(buffer);
|
||||
loadAsset(buffer);
|
||||
}
|
||||
}
|
||||
|
||||
void AssetRegistry::peekAsset(ArchiveBuffer& buffer)
|
||||
{
|
||||
// Read asset type
|
||||
uint64 identifier;
|
||||
Serialization::load(buffer, identifier);
|
||||
|
||||
// Read name
|
||||
std::string name;
|
||||
Serialization::load(buffer, name);
|
||||
|
||||
// Read folder
|
||||
std::string folderPath;
|
||||
Serialization::load(buffer, folderPath);
|
||||
|
||||
uint64 assetSize;
|
||||
Serialization::load(buffer, assetSize);
|
||||
|
||||
AssetFolder* folder = getOrCreateFolder(folderPath);
|
||||
|
||||
PAsset asset;
|
||||
switch (identifier)
|
||||
{
|
||||
case TextureAsset::IDENTIFIER:
|
||||
asset = new TextureAsset(folderPath, name);
|
||||
folder->textures[asset->getName()] = asset;
|
||||
break;
|
||||
case MeshAsset::IDENTIFIER:
|
||||
asset = new MeshAsset(folderPath, name);
|
||||
folder->meshes[asset->getName()] = asset;
|
||||
break;
|
||||
case MaterialAsset::IDENTIFIER:
|
||||
asset = new MaterialAsset(folderPath, name);
|
||||
folder->materials[asset->getName()] = asset;
|
||||
break;
|
||||
case MaterialInstanceAsset::IDENTIFIER:
|
||||
asset = new MaterialInstanceAsset(folderPath, name);
|
||||
// TODO
|
||||
break;
|
||||
case FontAsset::IDENTIFIER:
|
||||
asset = new FontAsset(folderPath, name);
|
||||
folder->fonts[asset->getName()] = asset;
|
||||
break;
|
||||
default:
|
||||
throw new std::logic_error("Unknown Identifier");
|
||||
}
|
||||
}
|
||||
|
||||
void AssetRegistry::loadAsset(ArchiveBuffer& buffer)
|
||||
{
|
||||
// Read asset type
|
||||
uint64 identifier;
|
||||
Serialization::load(buffer, identifier);
|
||||
|
||||
// Read name
|
||||
std::string name;
|
||||
Serialization::load(buffer, name);
|
||||
|
||||
// Read folder
|
||||
std::string folderPath;
|
||||
Serialization::load(buffer, folderPath);
|
||||
|
||||
uint64 assetSize;
|
||||
Serialization::load(buffer, assetSize);
|
||||
|
||||
AssetFolder* folder = getOrCreateFolder(folderPath);
|
||||
|
||||
PAsset asset;
|
||||
switch (identifier)
|
||||
{
|
||||
case TextureAsset::IDENTIFIER:
|
||||
asset = folder->textures.at(name);
|
||||
break;
|
||||
case MeshAsset::IDENTIFIER:
|
||||
asset = folder->meshes.at(name);
|
||||
break;
|
||||
case MaterialAsset::IDENTIFIER:
|
||||
asset = folder->materials.at(name);
|
||||
break;
|
||||
case MaterialInstanceAsset::IDENTIFIER:
|
||||
//asset = new MaterialInstanceAsset(path);
|
||||
// TODO
|
||||
break;
|
||||
case FontAsset::IDENTIFIER:
|
||||
asset = folder->fonts.at(name);
|
||||
break;
|
||||
default:
|
||||
throw new std::logic_error("Unknown Identifier");
|
||||
}
|
||||
asset->load(buffer);
|
||||
}
|
||||
|
||||
void AssetRegistry::saveRegistryInternal()
|
||||
{
|
||||
saveFolder("", assetRoot);
|
||||
@@ -334,6 +303,9 @@ void AssetRegistry::saveAsset(PAsset asset, uint64 identifier, const std::filesy
|
||||
Serialization::save(assetBuffer, name);
|
||||
// write folder
|
||||
Serialization::save(assetBuffer, folderPath.string());
|
||||
// write the asset size
|
||||
asset->updateByteSize();
|
||||
Serialization::save(assetBuffer, asset->getByteSize());
|
||||
// write asset data
|
||||
asset->save(assetBuffer);
|
||||
assetBuffer.writeToStream(assetStream);
|
||||
@@ -420,3 +392,4 @@ AssetRegistry::AssetFolder::~AssetFolder()
|
||||
delete child;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -6,10 +6,6 @@
|
||||
|
||||
namespace Seele
|
||||
{
|
||||
DECLARE_REF(TextureLoader)
|
||||
DECLARE_REF(FontLoader)
|
||||
DECLARE_REF(MeshLoader)
|
||||
DECLARE_REF(MaterialLoader)
|
||||
DECLARE_REF(TextureAsset)
|
||||
DECLARE_REF(FontAsset)
|
||||
DECLARE_REF(MeshAsset)
|
||||
@@ -32,22 +28,14 @@ public:
|
||||
static std::ofstream createWriteStream(const std::string& relativePath, std::ios_base::openmode openmode = std::ios::out);
|
||||
static std::ifstream createReadStream(const std::string& relativePath, std::ios_base::openmode openmode = std::ios::in);
|
||||
|
||||
static void importMesh(struct MeshImportArgs args);
|
||||
static void importTexture(struct TextureImportArgs args);
|
||||
static void importFont(struct FontImportArgs args);
|
||||
static void importMaterial(struct MaterialImportArgs args);
|
||||
static void set(AssetRegistry registry);
|
||||
|
||||
static void loadRegistry();
|
||||
static void saveRegistry();
|
||||
|
||||
AssetRegistry();
|
||||
private:
|
||||
struct AssetFolder
|
||||
{
|
||||
std::string folderPath;
|
||||
Map<std::string, AssetFolder*> children;
|
||||
//Todo: Seele::Map doesn't really work with strings for some reason, so just use std::map for now
|
||||
Map<std::string, PTextureAsset> textures;
|
||||
Map<std::string, PFontAsset> fonts;
|
||||
Map<std::string, PMeshAsset> meshes;
|
||||
@@ -55,13 +43,17 @@ private:
|
||||
AssetFolder(std::string_view folderPath);
|
||||
~AssetFolder();
|
||||
};
|
||||
|
||||
AssetFolder* getOrCreateFolder(std::string foldername);
|
||||
AssetRegistry();
|
||||
private:
|
||||
static AssetRegistry& get();
|
||||
|
||||
void initialize(const std::filesystem::path& rootFolder, Gfx::PGraphics graphics);
|
||||
void loadRegistryInternal();
|
||||
void peekFolder(AssetFolder* folder);
|
||||
void loadFolder(AssetFolder* folder);
|
||||
void peekAsset(ArchiveBuffer& buffer);
|
||||
void loadAsset(ArchiveBuffer& buffer);
|
||||
void saveRegistryInternal();
|
||||
void saveFolder(const std::filesystem::path& folderPath, AssetFolder* folder);
|
||||
void saveAsset(PAsset asset, uint64 identifier, const std::filesystem::path& folderPath, std::string name);
|
||||
@@ -71,18 +63,14 @@ private:
|
||||
void registerFont(PFontAsset font);
|
||||
void registerMaterial(PMaterialAsset material);
|
||||
|
||||
AssetFolder* getOrCreateFolder(std::string foldername);
|
||||
|
||||
std::ofstream internalCreateWriteStream(const std::string& relativePath, std::ios_base::openmode openmode = std::ios::out);
|
||||
std::ifstream internalCreateReadStream(const std::string& relaitvePath, std::ios_base::openmode openmode = std::ios::in);
|
||||
|
||||
std::filesystem::path rootFolder;
|
||||
AssetFolder* assetRoot;
|
||||
Gfx::PGraphics graphics;
|
||||
UPTextureLoader textureLoader;
|
||||
UPFontLoader fontLoader;
|
||||
UPMeshLoader meshLoader;
|
||||
UPMaterialLoader materialLoader;
|
||||
ArchiveBuffer assetBuffer;
|
||||
bool release = false;
|
||||
friend class TextureLoader;
|
||||
friend class FontLoader;
|
||||
friend class MaterialLoader;
|
||||
|
||||
@@ -6,22 +6,14 @@ target_sources(Engine
|
||||
AssetRegistry.cpp
|
||||
FontAsset.h
|
||||
FontAsset.cpp
|
||||
FontLoader.h
|
||||
FontLoader.cpp
|
||||
MaterialAsset.h
|
||||
MaterialAsset.cpp
|
||||
MaterialInstanceAsset.h
|
||||
MaterialInstanceAsset.cpp
|
||||
MaterialLoader.h
|
||||
MaterialLoader.cpp
|
||||
MeshAsset.h
|
||||
MeshAsset.cpp
|
||||
MeshLoader.h
|
||||
MeshLoader.cpp
|
||||
TextureAsset.h
|
||||
TextureAsset.cpp
|
||||
TextureLoader.h
|
||||
TextureLoader.cpp)
|
||||
TextureAsset.cpp)
|
||||
|
||||
target_sources(Engine
|
||||
PUBLIC FILE_SET HEADERS
|
||||
@@ -29,11 +21,7 @@ target_sources(Engine
|
||||
Asset.h
|
||||
AssetRegistry.h
|
||||
FontAsset.h
|
||||
FontLoader.h
|
||||
MaterialAsset.h
|
||||
MaterialInstanceAsset.h
|
||||
MaterialLoader.h
|
||||
MeshAsset.h
|
||||
MeshLoader.h
|
||||
TextureAsset.h
|
||||
TextureLoader.h)
|
||||
TextureAsset.h)
|
||||
@@ -22,10 +22,9 @@ FontAsset::~FontAsset()
|
||||
void FontAsset::save(ArchiveBuffer& buffer) const
|
||||
{
|
||||
uint64 numGlyphs = glyphs.size();
|
||||
buffer.writeBytes(&numGlyphs, sizeof(uint64));
|
||||
Serialization::save(buffer, numGlyphs);
|
||||
for (auto& [index, glyph] : glyphs)
|
||||
{
|
||||
Serialization::save(buffer, index);
|
||||
|
||||
Array<uint8> textureData;
|
||||
ktxTexture2* kTexture;
|
||||
@@ -36,6 +35,7 @@ void FontAsset::save(ArchiveBuffer& buffer) const
|
||||
.baseDepth = glyph.texture->getSizeZ(),
|
||||
.numDimensions = glyph.texture->getSizeZ() > 1 ? 3u : glyph.texture->getSizeY() > 1 ? 2u : 1u,
|
||||
.numLevels = glyph.texture->getMipLevels(),
|
||||
.numLayers = glyph.texture->getSizeZ(),
|
||||
.numFaces = glyph.texture->getNumFaces(),
|
||||
.isArray = false,
|
||||
.generateMipmaps = false,
|
||||
@@ -64,9 +64,13 @@ void FontAsset::save(ArchiveBuffer& buffer) const
|
||||
ktx_size_t texSize;
|
||||
ktxTexture_WriteToMemory(ktxTexture(kTexture), &texData, &texSize);
|
||||
|
||||
buffer.writeBytes(texData, texSize);
|
||||
Array<uint8> rawData(texSize);
|
||||
std::memcpy(rawData.data(), texData, texSize);
|
||||
|
||||
free(texData);
|
||||
|
||||
Serialization::save(buffer, index);
|
||||
Serialization::save(buffer, rawData);
|
||||
Serialization::save(buffer, glyph.size);
|
||||
Serialization::save(buffer, glyph.bearing);
|
||||
Serialization::save(buffer, glyph.advance);
|
||||
@@ -77,7 +81,7 @@ void FontAsset::save(ArchiveBuffer& buffer) const
|
||||
void FontAsset::load(ArchiveBuffer& buffer)
|
||||
{
|
||||
uint64 numGlyphs = glyphs.size();
|
||||
buffer.readBytes(&numGlyphs, sizeof(uint64));
|
||||
Serialization::load(buffer, numGlyphs);
|
||||
for (uint64 x = 0; x < numGlyphs; ++x)
|
||||
{
|
||||
uint32 index;
|
||||
@@ -85,11 +89,8 @@ void FontAsset::load(ArchiveBuffer& buffer)
|
||||
|
||||
Glyph& glyph = glyphs[index];
|
||||
|
||||
uint64 texSize;
|
||||
buffer.readBytes(&texSize, sizeof(uint64));
|
||||
Array<uint8> rawTex;
|
||||
rawTex.resize(texSize);
|
||||
buffer.readBytes(rawTex.data(), rawTex.size());
|
||||
Serialization::load(buffer, rawTex);
|
||||
|
||||
ktxTexture2* kTexture;
|
||||
ktxTexture2_CreateFromMemory(rawTex.data(),
|
||||
|
||||
@@ -1,76 +0,0 @@
|
||||
#include "FontLoader.h"
|
||||
#include "Graphics/Graphics.h"
|
||||
#include "FontAsset.h"
|
||||
#include "AssetRegistry.h"
|
||||
#include "Graphics/GraphicsResources.h"
|
||||
#include <ft2build.h>
|
||||
#include FT_FREETYPE_H
|
||||
|
||||
using namespace Seele;
|
||||
|
||||
FontLoader::FontLoader(Gfx::PGraphics graphics)
|
||||
: graphics(graphics)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
FontLoader::~FontLoader()
|
||||
{
|
||||
}
|
||||
|
||||
void FontLoader::importAsset(FontImportArgs args)
|
||||
{
|
||||
std::filesystem::path assetPath = args.filePath.filename();
|
||||
assetPath.replace_extension("asset");
|
||||
PFontAsset asset = new FontAsset(args.importPath, assetPath.stem().string());
|
||||
asset->setStatus(Asset::Status::Loading);
|
||||
AssetRegistry::get().registerFont(asset);
|
||||
import(args, asset);
|
||||
}
|
||||
|
||||
// in case of the space character there is no bitmap
|
||||
// so we create a single pixel empty texture
|
||||
uint8 transparentPixel = 0;
|
||||
void FontLoader::import(FontImportArgs args, PFontAsset asset)
|
||||
{
|
||||
FT_Library ft;
|
||||
FT_Error error = FT_Init_FreeType(&ft);
|
||||
assert(!error);
|
||||
FT_Face face;
|
||||
error = FT_New_Face(ft, args.filePath.string().c_str(), 0, &face);
|
||||
assert(!error);
|
||||
FT_Set_Pixel_Sizes(face, 0, 48);
|
||||
for(uint32 c = 0; c < 256; ++c)
|
||||
{
|
||||
if(FT_Load_Char(face, c, FT_LOAD_RENDER))
|
||||
{
|
||||
std::cout << "error loading " << (char)c << std::endl;
|
||||
continue;
|
||||
}
|
||||
FontAsset::Glyph& glyph = asset->glyphs[c];
|
||||
glyph.size = IVector2(face->glyph->bitmap.width, face->glyph->bitmap.rows);
|
||||
glyph.bearing = IVector2(face->glyph->bitmap_left, face->glyph->bitmap_top);
|
||||
glyph.advance = face->glyph->advance.x;
|
||||
TextureCreateInfo imageData;
|
||||
imageData.format = Gfx::SE_FORMAT_R8_UINT;
|
||||
imageData.width = face->glyph->bitmap.width;
|
||||
imageData.height = face->glyph->bitmap.rows;
|
||||
imageData.resourceData.data = face->glyph->bitmap.buffer;
|
||||
imageData.resourceData.size = imageData.width * imageData.height;
|
||||
if(imageData.width == 0 || imageData.height == 0)
|
||||
{
|
||||
glyph.size.x = 1;
|
||||
glyph.size.y = 1;
|
||||
glyph.bearing.x = 0;
|
||||
glyph.bearing.y = 0;
|
||||
imageData.width = 1;
|
||||
imageData.height = 1;
|
||||
imageData.resourceData.size = sizeof(uint8);
|
||||
imageData.resourceData.data = &transparentPixel;
|
||||
}
|
||||
glyph.texture = graphics->createTexture2D(imageData);
|
||||
}
|
||||
FT_Done_Face(face);
|
||||
FT_Done_FreeType(ft);
|
||||
asset->setStatus(Asset::Status::Ready);
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
#pragma once
|
||||
#include "MinimalEngine.h"
|
||||
#include "Containers/List.h"
|
||||
#include <filesystem>
|
||||
|
||||
namespace Seele
|
||||
{
|
||||
DECLARE_REF(FontAsset)
|
||||
DECLARE_NAME_REF(Gfx, Graphics)
|
||||
struct FontImportArgs
|
||||
{
|
||||
std::filesystem::path filePath;
|
||||
std::string importPath;
|
||||
};
|
||||
class FontLoader
|
||||
{
|
||||
public:
|
||||
FontLoader(Gfx::PGraphics graphic);
|
||||
~FontLoader();
|
||||
void importAsset(FontImportArgs args);
|
||||
private:
|
||||
void import(FontImportArgs args, PFontAsset asset);
|
||||
Gfx::PGraphics graphics;
|
||||
};
|
||||
DEFINE_REF(FontLoader)
|
||||
} // namespace Seele
|
||||
@@ -1,149 +0,0 @@
|
||||
#include "MaterialLoader.h"
|
||||
#include "Graphics/Graphics.h"
|
||||
#include "MaterialAsset.h"
|
||||
#include "AssetRegistry.h"
|
||||
#include "Material/Material.h"
|
||||
#include "Material/BRDF.h"
|
||||
#include "Window/WindowManager.h"
|
||||
#include "Material/ShaderExpression.h"
|
||||
#include "TextureAsset.h"
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
using namespace Seele;
|
||||
using json = nlohmann::json;
|
||||
|
||||
MaterialLoader::MaterialLoader(Gfx::PGraphics graphics)
|
||||
: graphics(graphics)
|
||||
{
|
||||
importAsset(MaterialImportArgs{
|
||||
.filePath = std::filesystem::absolute("./shaders/Placeholder.asset"),
|
||||
.importPath = "",
|
||||
});
|
||||
}
|
||||
|
||||
MaterialLoader::~MaterialLoader()
|
||||
{
|
||||
}
|
||||
|
||||
void MaterialLoader::importAsset(MaterialImportArgs args)
|
||||
{
|
||||
std::filesystem::path assetPath = args.filePath.filename();
|
||||
assetPath.replace_extension("asset");
|
||||
PMaterialAsset asset = new MaterialAsset(args.importPath, assetPath.stem().string());
|
||||
asset->setStatus(Asset::Status::Loading);
|
||||
AssetRegistry::get().registerMaterial(asset);
|
||||
import(args, asset);
|
||||
}
|
||||
|
||||
void MaterialLoader::import(MaterialImportArgs args, PMaterialAsset asset)
|
||||
{
|
||||
auto stream = std::ifstream(args.filePath.c_str());
|
||||
json j;
|
||||
stream >> j;
|
||||
std::string materialName = j["name"].get<std::string>() + "Material";
|
||||
Gfx::PDescriptorLayout layout = graphics->createDescriptorLayout(materialName + "Layout");
|
||||
//Shader file needs to conform to the slang standard, which prohibits _
|
||||
materialName.erase(std::remove(materialName.begin(), materialName.end(), '_'), materialName.end());
|
||||
materialName.erase(std::remove(materialName.begin(), materialName.end(), '.'), materialName.end());
|
||||
std::ofstream codeStream("./shaders/generated/"+materialName+".slang");
|
||||
std::string profile = j["profile"].get<std::string>();
|
||||
|
||||
codeStream << "import Material;" << std::endl;
|
||||
codeStream << "import BRDF;" << std::endl;
|
||||
codeStream << "import MaterialParameter;" << std::endl << std::endl;
|
||||
|
||||
codeStream << "struct " << materialName << " : IMaterial {" << std::endl;
|
||||
uint32 uniformBufferOffset = 0;
|
||||
uint32 bindingCounter = 0; // Uniform buffers are always binding 0
|
||||
uint32 uniformBinding = -1;
|
||||
Array<PShaderParameter> parameters;
|
||||
for(auto param : j["params"].items())
|
||||
{
|
||||
std::string type = param.value()["type"].get<std::string>();
|
||||
auto defaultValue = param.value().find("default");
|
||||
// TODO: ALIGNMENT RULES
|
||||
if(type.compare("float") == 0)
|
||||
{
|
||||
PFloatParameter p = new FloatParameter(param.key(), uniformBufferOffset, 0);
|
||||
codeStream << "\tlayout(offset = " << uniformBufferOffset << ")";
|
||||
if(uniformBinding == -1)
|
||||
{
|
||||
layout->addDescriptorBinding(bindingCounter, Gfx::SE_DESCRIPTOR_TYPE_UNIFORM_BUFFER);
|
||||
uniformBinding = bindingCounter++;
|
||||
}
|
||||
uniformBufferOffset += 4;
|
||||
if(defaultValue != param.value().end())
|
||||
{
|
||||
p->data = std::stof(defaultValue.value().get<std::string>());
|
||||
}
|
||||
parameters.add(p);
|
||||
}
|
||||
// TODO: ALIGNMENT RULES
|
||||
else if(type.compare("float3") == 0)
|
||||
{
|
||||
PVectorParameter p = new VectorParameter(param.key(), uniformBufferOffset, 0);
|
||||
codeStream << "\tlayout(offset = " << uniformBufferOffset << ")";
|
||||
if(uniformBinding == -1)
|
||||
{
|
||||
layout->addDescriptorBinding(bindingCounter, Gfx::SE_DESCRIPTOR_TYPE_UNIFORM_BUFFER);
|
||||
uniformBinding = bindingCounter++;
|
||||
}
|
||||
uniformBufferOffset += 12;
|
||||
if(defaultValue != param.value().end())
|
||||
{
|
||||
p->data = parseVector(defaultValue.value().get<std::string>().c_str());
|
||||
}
|
||||
parameters.add(p);
|
||||
}
|
||||
else if(type.compare("Texture2D") == 0)
|
||||
{
|
||||
PTextureParameter p = new TextureParameter(param.key(), 0, bindingCounter);
|
||||
layout->addDescriptorBinding(bindingCounter++, Gfx::SE_DESCRIPTOR_TYPE_SAMPLED_IMAGE);
|
||||
if(defaultValue != param.value().end())
|
||||
{
|
||||
std::string defaultString = defaultValue.value().get<std::string>();
|
||||
p->data = AssetRegistry::findTexture(defaultString);
|
||||
}
|
||||
if(p->data == nullptr)
|
||||
{
|
||||
p->data = AssetRegistry::findTexture(""); // this will return placeholder texture
|
||||
}
|
||||
parameters.add(p);
|
||||
}
|
||||
else if(type.compare("SamplerState") == 0)
|
||||
{
|
||||
PSamplerParameter p = new SamplerParameter(param.key(), 0, bindingCounter);
|
||||
layout->addDescriptorBinding(bindingCounter++, Gfx::SE_DESCRIPTOR_TYPE_SAMPLER);
|
||||
p->data = graphics->createSamplerState({});
|
||||
parameters.add(p);
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cout << "Error unsupported parameter type" << std::endl;
|
||||
}
|
||||
codeStream << "\t" << type << " " << param.key() << ";\n";
|
||||
}
|
||||
uint32 uniformDataSize = uniformBufferOffset;
|
||||
|
||||
BRDF* brdf = BRDF::getBRDFByName(profile);
|
||||
brdf->generateMaterialCode(codeStream, j["code"]);
|
||||
codeStream << "};";
|
||||
codeStream.close();
|
||||
layout->create();
|
||||
asset->material = new Material(
|
||||
graphics,
|
||||
std::move(parameters),
|
||||
std::move(layout),
|
||||
uniformDataSize,
|
||||
uniformBinding,
|
||||
materialName
|
||||
);
|
||||
graphics->getShaderCompiler()->registerMaterial(asset->material);
|
||||
asset->setStatus(Asset::Status::Ready);
|
||||
////co_return;
|
||||
}
|
||||
|
||||
PMaterialAsset MaterialLoader::getPlaceHolderMaterial()
|
||||
{
|
||||
return placeholderMaterial;
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
#pragma once
|
||||
#include "MinimalEngine.h"
|
||||
#include "Containers/List.h"
|
||||
#include <filesystem>
|
||||
|
||||
namespace Seele
|
||||
{
|
||||
DECLARE_REF(MaterialAsset)
|
||||
DECLARE_NAME_REF(Gfx, Graphics)
|
||||
struct MaterialImportArgs
|
||||
{
|
||||
std::filesystem::path filePath;
|
||||
std::string importPath;
|
||||
};
|
||||
class MaterialLoader
|
||||
{
|
||||
public:
|
||||
MaterialLoader(Gfx::PGraphics graphic);
|
||||
~MaterialLoader();
|
||||
void importAsset(MaterialImportArgs args);
|
||||
PMaterialAsset getPlaceHolderMaterial();
|
||||
private:
|
||||
void import(MaterialImportArgs args, PMaterialAsset asset);
|
||||
Gfx::PGraphics graphics;
|
||||
PMaterialAsset placeholderMaterial;
|
||||
};
|
||||
DEFINE_REF(MaterialLoader)
|
||||
} // namespace Seele
|
||||
@@ -1,311 +0,0 @@
|
||||
#include "MeshLoader.h"
|
||||
#include "Graphics/GraphicsResources.h"
|
||||
#include "Graphics/Graphics.h"
|
||||
#include "MeshAsset.h"
|
||||
#include "Graphics/Mesh.h"
|
||||
#include "Graphics/StaticMeshVertexInput.h"
|
||||
#include "AssetRegistry.h"
|
||||
#include "MaterialAsset.h"
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <nlohmann/json.hpp>
|
||||
#include <stb_image_write.h>
|
||||
#include <assimp/config.h>
|
||||
#include <assimp/Importer.hpp>
|
||||
#include <assimp/scene.h>
|
||||
#include <assimp/postprocess.h>
|
||||
#include <assimp/material.h>
|
||||
#include <Asset/MaterialLoader.h>
|
||||
#include <Asset/TextureLoader.h>
|
||||
|
||||
using namespace Seele;
|
||||
|
||||
MeshLoader::MeshLoader(Gfx::PGraphics graphics)
|
||||
: graphics(graphics)
|
||||
{
|
||||
}
|
||||
|
||||
MeshLoader::~MeshLoader()
|
||||
{
|
||||
}
|
||||
|
||||
void MeshLoader::importAsset(MeshImportArgs args)
|
||||
{
|
||||
std::filesystem::path assetPath = args.filePath.filename();
|
||||
assetPath.replace_extension("asset");
|
||||
PMeshAsset asset = new MeshAsset(args.importPath, assetPath.stem().string());
|
||||
asset->setStatus(Asset::Status::Loading);
|
||||
AssetRegistry::get().registerMesh(asset);
|
||||
import(args, asset);
|
||||
}
|
||||
|
||||
void MeshLoader::loadMaterials(const aiScene* scene, const std::string& baseName, const std::filesystem::path& meshDirectory, const std::string& importPath, Array<PMaterialAsset>& globalMaterials)
|
||||
{
|
||||
using json = nlohmann::json;
|
||||
for(uint32 i = 0; i < scene->mNumMaterials; ++i)
|
||||
{
|
||||
aiMaterial* material = scene->mMaterials[i];
|
||||
json matCode;
|
||||
matCode["name"] = baseName + material->GetName().C_Str();
|
||||
matCode["profile"] = "BlinnPhong"; //TODO: other shading models
|
||||
aiString texPath;
|
||||
//TODO make samplers based on used textures
|
||||
matCode["params"]["textureSampler"] =
|
||||
{
|
||||
{"type", "SamplerState"}
|
||||
};
|
||||
if(material->GetTexture(aiTextureType_DIFFUSE, 0, &texPath) == AI_SUCCESS)
|
||||
{
|
||||
std::string texFilename = std::filesystem::path(texPath.C_Str()).replace_extension("asset").stem().string();
|
||||
matCode["params"]["diffuseTexture"] =
|
||||
{
|
||||
{"type", "Texture2D"},
|
||||
{"default", texFilename}
|
||||
};
|
||||
matCode["code"]["baseColor"] = "return diffuseTexture.Sample(textureSampler, input.texCoords[0]).xyz;";
|
||||
}
|
||||
else
|
||||
{
|
||||
matCode["code"]["baseColor"] = "return input.vertexColor.xyz;";
|
||||
}
|
||||
if(material->GetTexture(aiTextureType_SPECULAR, 0, &texPath) == AI_SUCCESS)
|
||||
{
|
||||
std::string texFilename = std::filesystem::path(texPath.C_Str()).replace_extension("asset").stem().string();
|
||||
matCode["params"]["specularTexture"] =
|
||||
{
|
||||
{"type", "Texture2D"},
|
||||
{"default", texFilename}
|
||||
};
|
||||
matCode["code"]["specular"] = "return specularTexture.Sample(textureSampler, input.texCoords[0]).x;";
|
||||
}
|
||||
if(material->GetTexture(aiTextureType_NORMALS, 0, &texPath) == AI_SUCCESS)
|
||||
{
|
||||
std::string texFilename = std::filesystem::path(texPath.C_Str()).replace_extension("asset").stem().string();
|
||||
matCode["params"]["normalTexture"] =
|
||||
{
|
||||
{"type", "Texture2D"},
|
||||
{"default", texFilename}
|
||||
};
|
||||
matCode["code"]["normal"] = "return normalTexture.Sample(textureSampler, input.texCoords[0]).xyz;";
|
||||
}
|
||||
std::string outMatFilename = matCode["name"].get<std::string>().append(".asset");
|
||||
std::ofstream outMatFile = std::ofstream(meshDirectory / outMatFilename);
|
||||
outMatFile << std::setw(4) << matCode;
|
||||
outMatFile.flush();
|
||||
outMatFile.close();
|
||||
|
||||
std::cout << "writing json to " << outMatFilename << std::endl;
|
||||
AssetRegistry::importMaterial(MaterialImportArgs{
|
||||
.filePath = meshDirectory / outMatFilename,
|
||||
.importPath = importPath,
|
||||
});
|
||||
globalMaterials[i] = AssetRegistry::findMaterial(matCode["name"].get<std::string>());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void findMeshRoots(aiNode *node, List<aiNode *> &meshNodes)
|
||||
{
|
||||
if (node->mNumMeshes > 0)
|
||||
{
|
||||
meshNodes.add(node);
|
||||
return;
|
||||
}
|
||||
for (uint32 i = 0; i < node->mNumChildren; ++i)
|
||||
{
|
||||
findMeshRoots(node->mChildren[i], meshNodes);
|
||||
}
|
||||
}
|
||||
VertexStreamComponent createVertexStream(uint32 size, aiVector3D* sourceData, Gfx::PGraphics graphics)
|
||||
{
|
||||
Array<Vector> buffer(size);
|
||||
for(uint32 i = 0; i < size; ++i)
|
||||
{
|
||||
buffer[i] = Vector(sourceData[i].x, sourceData[i].y, sourceData[i].z);
|
||||
}
|
||||
VertexBufferCreateInfo vbInfo;
|
||||
vbInfo.numVertices = size;
|
||||
vbInfo.vertexSize = sizeof(Vector);
|
||||
vbInfo.resourceData.data = (uint8 *)buffer.data();
|
||||
vbInfo.resourceData.owner = Gfx::QueueType::DEDICATED_TRANSFER;
|
||||
vbInfo.resourceData.size = sizeof(Vector) * buffer.size();
|
||||
Gfx::PVertexBuffer vertexBuffer = graphics->createVertexBuffer(vbInfo);
|
||||
vertexBuffer->transferOwnership(Gfx::QueueType::GRAPHICS);
|
||||
return VertexStreamComponent(vertexBuffer, 0, vbInfo.vertexSize, Gfx::SE_FORMAT_R32G32B32_SFLOAT);
|
||||
}
|
||||
VertexStreamComponent createVertexStream(uint32 size, aiVector2D* sourceData, Gfx::PGraphics graphics)
|
||||
{
|
||||
Array<Vector2> buffer(size);
|
||||
for(uint32 i = 0; i < size; ++i)
|
||||
{
|
||||
buffer[i] = Vector2(sourceData[i].x, sourceData[i].y);
|
||||
}
|
||||
VertexBufferCreateInfo vbInfo;
|
||||
vbInfo.numVertices = size;
|
||||
vbInfo.vertexSize = sizeof(Vector2);
|
||||
vbInfo.resourceData.data = (uint8 *)buffer.data();
|
||||
vbInfo.resourceData.owner = Gfx::QueueType::DEDICATED_TRANSFER;
|
||||
vbInfo.resourceData.size = sizeof(Vector2) * buffer.size();
|
||||
Gfx::PVertexBuffer vertexBuffer = graphics->createVertexBuffer(vbInfo);
|
||||
vertexBuffer->transferOwnership(Gfx::QueueType::GRAPHICS);
|
||||
return VertexStreamComponent(vertexBuffer, 0, vbInfo.vertexSize, Gfx::SE_FORMAT_R32G32_SFLOAT);
|
||||
}
|
||||
VertexStreamComponent createVertexStream(uint32 size, aiColor4D* sourceData, Gfx::PGraphics graphics)
|
||||
{
|
||||
Array<Vector4> buffer(size);
|
||||
for(uint32 i = 0; i < size; ++i)
|
||||
{
|
||||
buffer[i] = Vector4(sourceData[i].r, sourceData[i].g, sourceData[i].b, sourceData[i].a);
|
||||
}
|
||||
VertexBufferCreateInfo vbInfo;
|
||||
vbInfo.numVertices = size;
|
||||
vbInfo.vertexSize = sizeof(Vector4);
|
||||
vbInfo.resourceData.data = (uint8 *)buffer.data();
|
||||
vbInfo.resourceData.owner = Gfx::QueueType::DEDICATED_TRANSFER;
|
||||
vbInfo.resourceData.size = sizeof(Vector4) * buffer.size();
|
||||
Gfx::PVertexBuffer vertexBuffer = graphics->createVertexBuffer(vbInfo);
|
||||
vertexBuffer->transferOwnership(Gfx::QueueType::GRAPHICS);
|
||||
return VertexStreamComponent(vertexBuffer, 0, vbInfo.vertexSize, Gfx::SE_FORMAT_R32G32B32A32_SFLOAT);
|
||||
}
|
||||
void MeshLoader::loadGlobalMeshes(const aiScene* scene, const Array<PMaterialAsset>& materials, Array<PMesh>& globalMeshes, Component::Collider& collider)
|
||||
{
|
||||
for (uint32 meshIndex = 0; meshIndex < scene->mNumMeshes; ++meshIndex)
|
||||
{
|
||||
aiMesh *mesh = scene->mMeshes[meshIndex];
|
||||
collider.boundingbox.adjust(Vector(mesh->mAABB.mMin.x, mesh->mAABB.mMin.y, mesh->mAABB.mMin.z));
|
||||
collider.boundingbox.adjust(Vector(mesh->mAABB.mMax.x, mesh->mAABB.mMax.y, mesh->mAABB.mMax.z));
|
||||
|
||||
//! \todo duplicate from createVertexStream, clean up
|
||||
Array<Vector> vertices(mesh->mNumVertices);
|
||||
for(uint32 i = 0; i < mesh->mNumVertices; ++i)
|
||||
{
|
||||
vertices[i] = Vector(mesh->mVertices[i].x, mesh->mVertices[i].y, mesh->mVertices[i].z);
|
||||
}
|
||||
|
||||
PStaticMeshVertexInput vertexShaderInput = new StaticMeshVertexInput(std::string(mesh->mName.C_Str()));
|
||||
StaticMeshDataType data;
|
||||
data.positionStream = createVertexStream(mesh->mNumVertices, mesh->mVertices, graphics);
|
||||
|
||||
for(uint32 i = 0; i < MAX_TEXCOORDS; ++i)
|
||||
{
|
||||
if(mesh->HasTextureCoords(i))
|
||||
{
|
||||
data.textureCoordinates[i] = createVertexStream(mesh->mNumVertices, mesh->mTextureCoords[i], graphics);
|
||||
}
|
||||
}
|
||||
if(mesh->HasNormals())
|
||||
{
|
||||
data.tangentBasisComponents[0] = createVertexStream(mesh->mNumVertices, mesh->mNormals, graphics);
|
||||
}
|
||||
if(mesh->HasTangentsAndBitangents())
|
||||
{
|
||||
//TODO: use bitangent to calculate sign for 4th coordinate of tangentstream
|
||||
data.tangentBasisComponents[1] = createVertexStream(mesh->mNumVertices, mesh->mTangents, graphics);
|
||||
data.tangentBasisComponents[2] = createVertexStream(mesh->mNumVertices, mesh->mBitangents, graphics);
|
||||
}
|
||||
if(mesh->HasVertexColors(0))
|
||||
{
|
||||
data.colorComponent = createVertexStream(mesh->mNumVertices, mesh->mColors[0], graphics);
|
||||
}
|
||||
vertexShaderInput->setData(std::move(data));
|
||||
vertexShaderInput->init(graphics);
|
||||
|
||||
Array<uint32> indices(mesh->mNumFaces * 3);
|
||||
for (uint32 faceIndex = 0; faceIndex < mesh->mNumFaces; ++faceIndex)
|
||||
{
|
||||
indices[faceIndex * 3 + 0] = mesh->mFaces[faceIndex].mIndices[0];
|
||||
indices[faceIndex * 3 + 1] = mesh->mFaces[faceIndex].mIndices[1];
|
||||
indices[faceIndex * 3 + 2] = mesh->mFaces[faceIndex].mIndices[2];
|
||||
}
|
||||
|
||||
collider.physicsMesh.addCollider(vertices, indices, Matrix4(1.0f));
|
||||
|
||||
IndexBufferCreateInfo idxInfo;
|
||||
idxInfo.indexType = Gfx::SE_INDEX_TYPE_UINT32;
|
||||
idxInfo.resourceData.data = (uint8 *)indices.data();
|
||||
idxInfo.resourceData.owner = Gfx::QueueType::DEDICATED_TRANSFER;
|
||||
idxInfo.resourceData.size = sizeof(uint32) * indices.size();
|
||||
Gfx::PIndexBuffer indexBuffer = graphics->createIndexBuffer(idxInfo);
|
||||
indexBuffer->transferOwnership(Gfx::QueueType::GRAPHICS);
|
||||
|
||||
globalMeshes[meshIndex] = new Mesh(vertexShaderInput, indexBuffer);
|
||||
globalMeshes[meshIndex]->referencedMaterial = materials[mesh->mMaterialIndex];
|
||||
}
|
||||
}
|
||||
void MeshLoader::convertAssimpARGB(unsigned char* dst, aiTexel* src, uint32 numPixels)
|
||||
{
|
||||
for(uint32 i = 0; i < numPixels; ++i)
|
||||
{
|
||||
dst[i * 4 + 0] = src[i].r;
|
||||
dst[i * 4 + 1] = src[i].g;
|
||||
dst[i * 4 + 2] = src[i].b;
|
||||
dst[i * 4 + 3] = src[i].a;
|
||||
}
|
||||
}
|
||||
void MeshLoader::loadTextures(const aiScene* scene, const std::filesystem::path& meshDirectory, const std::string& importPath)
|
||||
{
|
||||
for (uint32 i = 0; i < scene->mNumTextures; ++i)
|
||||
{
|
||||
aiTexture* tex = scene->mTextures[i];
|
||||
auto texPath = std::filesystem::path(tex->mFilename.C_Str());
|
||||
auto texPngPath = meshDirectory;
|
||||
texPngPath.append(texPath.filename().string());
|
||||
if(tex->mHeight == 0)
|
||||
{
|
||||
// already compressed, just dump it to the disk
|
||||
std::ofstream file(texPngPath, std::ios::binary);
|
||||
file.write((const char*)tex->pcData, tex->mWidth);
|
||||
file.flush();
|
||||
}
|
||||
else
|
||||
{
|
||||
// recompress data so that the TextureLoader can read it
|
||||
unsigned char* texData = new unsigned char[tex->mWidth * tex->mHeight * 4];
|
||||
convertAssimpARGB(texData, tex->pcData, tex->mWidth * tex->mHeight);
|
||||
stbi_write_png(texPngPath.string().c_str(), tex->mWidth, tex->mHeight, 4, tex->pcData, tex->mWidth * 32);
|
||||
delete[] texData;
|
||||
}
|
||||
std::cout << "Loading model texture " << texPngPath.string() << std::endl;
|
||||
AssetRegistry::importTexture(TextureImportArgs {
|
||||
.filePath = texPath,
|
||||
.importPath = importPath,
|
||||
});
|
||||
}
|
||||
}
|
||||
void MeshLoader::import(MeshImportArgs args, PMeshAsset meshAsset)
|
||||
{
|
||||
std::cout << "Starting to import "<<args.filePath<< std::endl;
|
||||
meshAsset->setStatus(Asset::Status::Loading);
|
||||
Assimp::Importer importer;
|
||||
importer.ReadFile(args.filePath.string().c_str(), (uint32)(
|
||||
aiProcess_FlipUVs |
|
||||
aiProcess_Triangulate |
|
||||
aiProcess_SortByPType |
|
||||
aiProcess_GenBoundingBoxes |
|
||||
aiProcess_GenSmoothNormals |
|
||||
aiProcess_GenUVCoords |
|
||||
aiProcess_FindDegenerates));
|
||||
const aiScene *scene = importer.ApplyPostProcessing(aiProcess_CalcTangentSpace);
|
||||
|
||||
Array<PMaterialAsset> globalMaterials(scene->mNumMaterials);
|
||||
loadTextures(scene, args.filePath.parent_path(), args.importPath);
|
||||
loadMaterials(scene, args.filePath.stem().string(), args.filePath.parent_path(), args.importPath, globalMaterials);
|
||||
|
||||
Array<PMesh> globalMeshes(scene->mNumMeshes);
|
||||
Component::Collider collider;
|
||||
loadGlobalMeshes(scene, globalMaterials, globalMeshes, collider);
|
||||
|
||||
List<aiNode *> meshNodes;
|
||||
findMeshRoots(scene->mRootNode, meshNodes);
|
||||
|
||||
for (auto meshNode : meshNodes)
|
||||
{
|
||||
for(uint32 i = 0; i < meshNode->mNumMeshes; ++i)
|
||||
{
|
||||
meshAsset->addMesh(globalMeshes[meshNode->mMeshes[i]]);
|
||||
}
|
||||
}
|
||||
meshAsset->physicsMesh = std::move(collider);
|
||||
meshAsset->setStatus(Asset::Status::Ready);
|
||||
std::cout << "Finished loading " << args.filePath<< std::endl;
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
#pragma once
|
||||
#include "MinimalEngine.h"
|
||||
#include "Containers/List.h"
|
||||
#include "Component/Collider.h"
|
||||
#include <filesystem>
|
||||
|
||||
struct aiScene;
|
||||
struct aiTexel;
|
||||
namespace Seele
|
||||
{
|
||||
DECLARE_REF(Mesh)
|
||||
DECLARE_REF(MeshAsset)
|
||||
DECLARE_REF(MaterialAsset)
|
||||
DECLARE_NAME_REF(Gfx, Graphics)
|
||||
struct MeshImportArgs
|
||||
{
|
||||
std::filesystem::path filePath;
|
||||
std::string importPath;
|
||||
};
|
||||
class MeshLoader
|
||||
{
|
||||
public:
|
||||
MeshLoader(Gfx::PGraphics graphic);
|
||||
~MeshLoader();
|
||||
void importAsset(MeshImportArgs args);
|
||||
private:
|
||||
void loadMaterials(const aiScene* scene, const std::string& baseName, const std::filesystem::path& meshDirectory, const std::string& importPath, Array<PMaterialAsset>& globalMaterials);
|
||||
void loadTextures(const aiScene* scene, const std::filesystem::path& meshDirectory, const std::string& importPath);
|
||||
void loadGlobalMeshes(const aiScene* scene, const Array<PMaterialAsset>& materials, Array<PMesh>& globalMeshes, Component::Collider& collider);
|
||||
void convertAssimpARGB(unsigned char* dst, aiTexel* src, uint32 numPixels);
|
||||
|
||||
void import(MeshImportArgs args, PMeshAsset meshAsset);
|
||||
Gfx::PGraphics graphics;
|
||||
};
|
||||
DEFINE_REF(MeshLoader)
|
||||
} // namespace Seele
|
||||
@@ -25,7 +25,7 @@ TextureAsset::~TextureAsset()
|
||||
|
||||
void TextureAsset::save(ArchiveBuffer& buffer) const
|
||||
{
|
||||
Array<uint8> textureData;
|
||||
/*Array<uint8> textureData;
|
||||
ktxTexture2* kTexture;
|
||||
ktxTextureCreateInfo createInfo = {
|
||||
.vkFormat = (uint32_t)texture->getFormat(),
|
||||
@@ -66,25 +66,28 @@ void TextureAsset::save(ArchiveBuffer& buffer) const
|
||||
Array<uint8> rawData(texSize);
|
||||
std::memcpy(rawData.data(), texData, texSize);
|
||||
free(texData);
|
||||
|
||||
Serialization::save(buffer, rawData);
|
||||
*/
|
||||
Serialization::save(buffer, textureData);
|
||||
}
|
||||
|
||||
void TextureAsset::load(ArchiveBuffer& buffer)
|
||||
{
|
||||
setStatus(Status::Loading);
|
||||
|
||||
Array<uint8> rawData;
|
||||
Serialization::load(buffer, rawData);
|
||||
createFromMemory(std::move(rawData), buffer.getGraphics());
|
||||
}
|
||||
|
||||
void TextureAsset::createFromMemory(Array<uint8> memory, Gfx::PGraphics graphics)
|
||||
{
|
||||
textureData = std::move(memory);
|
||||
ktxTexture2* kTexture;
|
||||
std::cout << "Loading texture " << name << std::endl;
|
||||
KTX_CHECK(ktxTexture_CreateFromMemory(rawData.data(),
|
||||
rawData.size(),
|
||||
KTX_TEXTURE_CREATE_LOAD_IMAGE_DATA_BIT,
|
||||
(ktxTexture**) & kTexture));
|
||||
KTX_CHECK(ktxTexture_CreateFromMemory(textureData.data(),
|
||||
textureData.size(),
|
||||
KTX_TEXTURE_CREATE_LOAD_IMAGE_DATA_BIT,
|
||||
(ktxTexture**)&kTexture));
|
||||
|
||||
//ktxTexture2_TranscodeBasis(kTexture, KTX_TTF_BC7_RGBA, 0);
|
||||
ktxTexture2_TranscodeBasis(kTexture, KTX_TTF_BC7_RGBA, 0);
|
||||
|
||||
TextureCreateInfo createInfo = {
|
||||
.resourceData = {
|
||||
@@ -101,21 +104,18 @@ void TextureAsset::load(ArchiveBuffer& buffer)
|
||||
.format = Vulkan::cast((VkFormat)kTexture->vkFormat),
|
||||
.usage = Gfx::SE_IMAGE_USAGE_SAMPLED_BIT,
|
||||
};
|
||||
Gfx::PTexture tex;
|
||||
if (kTexture->isCubemap)
|
||||
{
|
||||
tex = buffer.getGraphics()->createTextureCube(createInfo);
|
||||
texture = graphics->createTextureCube(createInfo);
|
||||
}
|
||||
else if (kTexture->isArray)
|
||||
{
|
||||
tex = buffer.getGraphics()->createTexture3D(createInfo);
|
||||
texture = graphics->createTexture3D(createInfo);
|
||||
}
|
||||
else
|
||||
{
|
||||
tex = buffer.getGraphics()->createTexture2D(createInfo);
|
||||
texture = graphics->createTexture2D(createInfo);
|
||||
}
|
||||
tex->transferOwnership(Gfx::QueueType::GRAPHICS);
|
||||
setTexture(tex);
|
||||
texture->transferOwnership(Gfx::QueueType::GRAPHICS);
|
||||
ktxTexture_Destroy(ktxTexture(kTexture));
|
||||
setStatus(Asset::Status::Ready);
|
||||
}
|
||||
@@ -13,6 +13,7 @@ public:
|
||||
virtual ~TextureAsset();
|
||||
virtual void save(ArchiveBuffer& buffer) const override;
|
||||
virtual void load(ArchiveBuffer& buffer) override;
|
||||
void createFromMemory(Array<uint8> memory, Gfx::PGraphics graphics);
|
||||
void setTexture(Gfx::PTexture _texture)
|
||||
{
|
||||
texture = _texture;
|
||||
@@ -22,6 +23,7 @@ public:
|
||||
return texture;
|
||||
}
|
||||
private:
|
||||
Array<uint8> textureData;
|
||||
Gfx::PTexture texture;
|
||||
friend class TextureLoader;
|
||||
};
|
||||
|
||||
@@ -1,154 +0,0 @@
|
||||
#include "TextureLoader.h"
|
||||
#include "TextureAsset.h"
|
||||
#include "Graphics/Graphics.h"
|
||||
#include "AssetRegistry.h"
|
||||
#include "Graphics/Vulkan/VulkanGraphicsEnums.h"
|
||||
#define STB_IMAGE_IMPLEMENTATION
|
||||
#include <stb_image.h>
|
||||
#define STB_IMAGE_WRITE_IMPLEMENTATION
|
||||
#include <stb_image_write.h>
|
||||
#include "ktx.h"
|
||||
|
||||
using namespace Seele;
|
||||
|
||||
TextureLoader::TextureLoader(Gfx::PGraphics graphics)
|
||||
: graphics(graphics)
|
||||
{
|
||||
//(std::filesystem::absolute("./textures/placeholder.ktx"));
|
||||
//placeholderAsset->load(graphics);
|
||||
//AssetRegistry::get().assetRoot.textures[""] = placeholderAsset;
|
||||
placeholderAsset = new TextureAsset();
|
||||
import(TextureImportArgs{
|
||||
.filePath = std::filesystem::absolute("./textures/placeholder.png"),
|
||||
.importPath = "",
|
||||
}, placeholderAsset);
|
||||
AssetRegistry::get().assetRoot->textures[""] = placeholderAsset;
|
||||
}
|
||||
|
||||
TextureLoader::~TextureLoader()
|
||||
{
|
||||
}
|
||||
|
||||
void TextureLoader::importAsset(TextureImportArgs args)
|
||||
{
|
||||
std::filesystem::path assetPath = args.filePath.filename();
|
||||
assetPath.replace_extension("asset");
|
||||
PTextureAsset asset = new TextureAsset(args.importPath, assetPath.stem().string());
|
||||
asset->setStatus(Asset::Status::Loading);
|
||||
asset->setTexture(placeholderAsset->getTexture());
|
||||
AssetRegistry::get().registerTexture(asset);
|
||||
import(args, asset);
|
||||
}
|
||||
|
||||
PTextureAsset TextureLoader::getPlaceholderTexture()
|
||||
{
|
||||
return placeholderAsset;
|
||||
}
|
||||
|
||||
void TextureLoader::import(TextureImportArgs args, PTextureAsset textureAsset)
|
||||
{
|
||||
int totalWidth, totalHeight, n;
|
||||
unsigned char* data = stbi_load(args.filePath.string().c_str(), &totalWidth, &totalHeight, &n, 4);
|
||||
ktxTexture2* kTexture;
|
||||
ktxTextureCreateInfo createInfo;
|
||||
createInfo.vkFormat = VK_FORMAT_R8G8B8A8_UNORM;
|
||||
createInfo.baseDepth = 1;
|
||||
createInfo.numLevels = 1;
|
||||
createInfo.numLayers = 1;
|
||||
createInfo.isArray = false;
|
||||
createInfo.generateMipmaps = false;
|
||||
|
||||
if (args.type == TextureImportType::TEXTURE_CUBEMAP)
|
||||
{
|
||||
uint32 faceWidth = totalWidth / 4;
|
||||
uint32 faceHeight = totalHeight / 3;
|
||||
// Cube map
|
||||
createInfo.baseWidth = totalWidth / 4;
|
||||
createInfo.baseHeight = totalHeight / 3;
|
||||
createInfo.numFaces = 6;
|
||||
createInfo.numDimensions = 2;
|
||||
|
||||
ktxTexture2_Create(&createInfo,
|
||||
KTX_TEXTURE_CREATE_ALLOC_STORAGE,
|
||||
&kTexture);
|
||||
|
||||
auto loadCubeFace = [&kTexture, &faceWidth, &totalWidth, &data](int xPos, int yPos, int faceName)
|
||||
{
|
||||
std::vector<unsigned char> vec(faceWidth * faceWidth * 4);
|
||||
for (int y = 0; y < faceWidth; ++y)
|
||||
{
|
||||
for (int x = 0; x < faceWidth; ++x)
|
||||
{
|
||||
int imgX = x + (xPos * faceWidth);
|
||||
int imgY = y + (yPos * faceWidth);
|
||||
std::memcpy(&vec[(x + (faceWidth * y)) * 4], &data[(imgX + (totalWidth * imgY)) * 4], 4);
|
||||
}
|
||||
}
|
||||
ktxTexture_SetImageFromMemory(ktxTexture(kTexture),
|
||||
0, 0, faceName, vec.data(), vec.size());
|
||||
};
|
||||
loadCubeFace(2, 1, 0); // +X
|
||||
loadCubeFace(0, 1, 1); // -X
|
||||
loadCubeFace(1, 0, 2); // +Y
|
||||
loadCubeFace(1, 2, 3); // -Y
|
||||
loadCubeFace(1, 1, 4); // +Z
|
||||
loadCubeFace(3, 1, 5); // -Z
|
||||
}
|
||||
else
|
||||
{
|
||||
createInfo.baseWidth = totalWidth;
|
||||
createInfo.baseHeight = totalHeight;
|
||||
createInfo.numFaces = 1;
|
||||
createInfo.numDimensions = 1 + (totalHeight > 1);
|
||||
|
||||
ktxTexture2_Create(&createInfo,
|
||||
KTX_TEXTURE_CREATE_ALLOC_STORAGE,
|
||||
&kTexture);
|
||||
|
||||
ktxTexture_SetImageFromMemory(ktxTexture(kTexture),
|
||||
0, 0, 0, data, totalWidth * totalHeight * 4 * sizeof(unsigned char));
|
||||
}
|
||||
std::cout << "starting compression of " << textureAsset->getAssetIdentifier() << std::endl;
|
||||
ktxBasisParams params = {
|
||||
.structSize = sizeof(ktxBasisParams),
|
||||
.uastc = KTX_TRUE,
|
||||
.threadCount = std::thread::hardware_concurrency(),
|
||||
.qualityLevel = 0,
|
||||
};
|
||||
//ktx_error_code_e error = ktxTexture2_CompressBasisEx(kTexture, ¶ms);
|
||||
//assert(error == KTX_SUCCESS);
|
||||
|
||||
//error = ktxTexture2_TranscodeBasis(kTexture, KTX_TTF_BC7_RGBA, 0);
|
||||
//assert(error == KTX_SUCCESS);
|
||||
|
||||
stbi_image_free(data);
|
||||
|
||||
TextureCreateInfo texInfo = {
|
||||
.resourceData = {
|
||||
.size = ktxTexture_GetDataSize(ktxTexture(kTexture)),
|
||||
.data = ktxTexture_GetData(ktxTexture(kTexture)),
|
||||
},
|
||||
.width = createInfo.baseWidth,
|
||||
.height = createInfo.baseHeight,
|
||||
.depth = createInfo.baseDepth,
|
||||
.bArray = false,
|
||||
.arrayLayers = createInfo.numFaces,
|
||||
.mipLevels = 1,
|
||||
.samples = 1,
|
||||
.format = (Gfx::SeFormat)kTexture->vkFormat,
|
||||
.usage = args.usage,
|
||||
};
|
||||
|
||||
if (createInfo.numFaces == 1)
|
||||
{
|
||||
textureAsset->setTexture(graphics->createTexture2D(texInfo));
|
||||
}
|
||||
else
|
||||
{
|
||||
textureAsset->setTexture(graphics->createTextureCube(texInfo));
|
||||
}
|
||||
|
||||
ktxTexture_Destroy(ktxTexture(kTexture));
|
||||
textureAsset->setStatus(Asset::Status::Ready);
|
||||
////co_return;
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
#pragma once
|
||||
#include "MinimalEngine.h"
|
||||
#include "Containers/List.h"
|
||||
#include "Graphics/GraphicsEnums.h"
|
||||
#include <filesystem>
|
||||
|
||||
namespace Seele
|
||||
{
|
||||
DECLARE_REF(TextureAsset)
|
||||
DECLARE_NAME_REF(Gfx, Graphics)
|
||||
DECLARE_NAME_REF(Gfx, Texture2D)
|
||||
enum class TextureImportType
|
||||
{
|
||||
TEXTURE_2D,
|
||||
TEXTURE_CUBEMAP,
|
||||
};
|
||||
struct TextureImportArgs
|
||||
{
|
||||
std::filesystem::path filePath;
|
||||
std::string importPath;
|
||||
TextureImportType type = TextureImportType::TEXTURE_2D;
|
||||
Gfx::SeImageUsageFlagBits usage = Gfx::SE_IMAGE_USAGE_SAMPLED_BIT;
|
||||
};
|
||||
class TextureLoader
|
||||
{
|
||||
public:
|
||||
TextureLoader(Gfx::PGraphics graphic);
|
||||
~TextureLoader();
|
||||
void importAsset(TextureImportArgs args);
|
||||
PTextureAsset getPlaceholderTexture();
|
||||
private:
|
||||
void import(TextureImportArgs args, PTextureAsset asset);
|
||||
Gfx::PGraphics graphics;
|
||||
PTextureAsset placeholderAsset;
|
||||
};
|
||||
DEFINE_REF(TextureLoader)
|
||||
} // namespace Seele
|
||||
@@ -22,6 +22,7 @@ add_subdirectory(Graphics/)
|
||||
add_subdirectory(Material/)
|
||||
add_subdirectory(Math/)
|
||||
add_subdirectory(Physics/)
|
||||
add_subdirectory(Platform/)
|
||||
add_subdirectory(Serialization/)
|
||||
add_subdirectory(Scene/)
|
||||
add_subdirectory(System/)
|
||||
|
||||
@@ -18,6 +18,7 @@ target_sources(Engine
|
||||
Transform.h
|
||||
Transform.cpp)
|
||||
|
||||
|
||||
target_sources(Engine
|
||||
PUBLIC FILE_SET HEADERS
|
||||
FILES
|
||||
|
||||
@@ -5,7 +5,6 @@ target_sources(Engine
|
||||
Map.h
|
||||
Pair.h)
|
||||
|
||||
|
||||
target_sources(Engine
|
||||
PUBLIC FILE_SET HEADERS
|
||||
FILES
|
||||
|
||||
@@ -290,6 +290,18 @@ public:
|
||||
markIteratorsDirty();
|
||||
return getNode(root)->pair.value;
|
||||
}
|
||||
|
||||
constexpr const mapped_type& at(const key_type& key) const
|
||||
{
|
||||
for(const auto& node : nodeContainer)
|
||||
{
|
||||
if(equal(node.pair.key, key))
|
||||
{
|
||||
return node.pair.value;
|
||||
}
|
||||
}
|
||||
throw std::logic_error("Key not found");
|
||||
}
|
||||
constexpr iterator find(const key_type& key)
|
||||
{
|
||||
root = splay(root, key);
|
||||
@@ -621,7 +633,7 @@ private:
|
||||
return (!isValid(node->rightChild)) ? r : rotateLeft(r);
|
||||
}
|
||||
}
|
||||
bool equal(const key_type& a, const key_type& b)
|
||||
bool equal(const key_type& a, const key_type& b) const
|
||||
{
|
||||
return !comp(a, b) && !comp(b, a);
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ target_sources(Engine
|
||||
VertexShaderInput.cpp
|
||||
StaticMeshVertexInput.h
|
||||
StaticMeshVertexInput.cpp)
|
||||
|
||||
|
||||
target_sources(Engine
|
||||
PUBLIC FILE_SET HEADERS
|
||||
FILES
|
||||
|
||||
@@ -29,7 +29,7 @@ target_sources(Engine
|
||||
VulkanQueue.h
|
||||
VulkanQueue.cpp
|
||||
VulkanViewport.cpp)
|
||||
|
||||
|
||||
target_sources(Engine
|
||||
PUBLIC FILE_SET HEADERS
|
||||
FILES
|
||||
|
||||
@@ -235,7 +235,7 @@ void *ShaderBuffer::lockRegion(uint64 regionOffset, uint64 regionSize, bool bWri
|
||||
|
||||
graphics->getQueueCommands(owner)->submitCommands();
|
||||
vkQueueWaitIdle(graphics->getQueueCommands(owner)->getQueue()->getHandle());
|
||||
|
||||
stagingBuffer->getMappedPointer(); // this maps the memory if not mapped already
|
||||
stagingBuffer->flushMappedMemory();
|
||||
pending.stagingBuffer = stagingBuffer;
|
||||
|
||||
|
||||
@@ -1,111 +0,0 @@
|
||||
#include "BRDF.h"
|
||||
#include <sstream>
|
||||
#include <fstream>
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
using namespace Seele;
|
||||
using json = nlohmann::json;
|
||||
|
||||
List<BRDF*> BRDF::globalBRDFList;
|
||||
|
||||
BRDF::BRDF(const char* name)
|
||||
: name(name)
|
||||
{
|
||||
globalBRDFList.add(this);
|
||||
}
|
||||
|
||||
BRDF::~BRDF()
|
||||
{
|
||||
globalBRDFList.remove(globalBRDFList.find(this));
|
||||
}
|
||||
|
||||
|
||||
BRDF* BRDF::getBRDFByName(const std::string& name)
|
||||
{
|
||||
for(auto brdf : globalBRDFList)
|
||||
{
|
||||
if(name.compare(brdf->name) == 0)
|
||||
{
|
||||
return brdf;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
List<BRDF*> BRDF::getBRDFList()
|
||||
{
|
||||
return globalBRDFList;
|
||||
}
|
||||
|
||||
BlinnPhong::BlinnPhong(const char* name)
|
||||
: BRDF(name)
|
||||
{
|
||||
}
|
||||
|
||||
BlinnPhong::~BlinnPhong()
|
||||
{
|
||||
}
|
||||
|
||||
void BlinnPhong::generateMaterialCode(std::ofstream& codeStream, json codeJson)
|
||||
{
|
||||
std::stringstream accessorStream;
|
||||
|
||||
auto generateAccessor = [codeJson](std::stringstream& accessorStream, const std::string& key, const std::string& defaultVal)
|
||||
{
|
||||
if(codeJson.contains(key))
|
||||
{
|
||||
for(auto code : codeJson[key].items())
|
||||
{
|
||||
accessorStream << "\t\t" << code.value().get<std::string>() << ";" << std::endl;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
accessorStream << "\t\treturn " << defaultVal << ";\n";
|
||||
}
|
||||
|
||||
};
|
||||
accessorStream << "\tfloat3 getBaseColor(MaterialFragmentParameter input) {\n";
|
||||
generateAccessor(accessorStream, "baseColor", "float3(0.5f, 0.5f, 0.5f)");
|
||||
accessorStream << "\t}\n";
|
||||
|
||||
accessorStream << "\tfloat getMetallic(MaterialFragmentParameter input) {\n";
|
||||
generateAccessor(accessorStream, "metallic", "0.f");
|
||||
accessorStream << "\t}\n";
|
||||
|
||||
accessorStream << "\tfloat3 getNormal(MaterialFragmentParameter input) {\n";
|
||||
generateAccessor(accessorStream, "normal", "float3(0, 1, 0)");
|
||||
accessorStream << "\t}\n";
|
||||
|
||||
accessorStream << "\tfloat getSpecular(MaterialFragmentParameter input) {\n";
|
||||
generateAccessor(accessorStream, "specular", "0.f");
|
||||
accessorStream << "\t}\n";
|
||||
|
||||
accessorStream << "\tfloat getRoughness(MaterialFragmentParameter input) {\n";
|
||||
generateAccessor(accessorStream, "roughness", "0.f");
|
||||
accessorStream << "\t}\n";
|
||||
|
||||
accessorStream << "\tfloat getSheen(MaterialFragmentParameter input) {\n";
|
||||
generateAccessor(accessorStream, "sheen", "0.f");
|
||||
accessorStream << "\t}\n";
|
||||
|
||||
accessorStream << "\tfloat3 getWorldOffset() {\n";
|
||||
generateAccessor(accessorStream, "worldOffset", "0.f");
|
||||
accessorStream << "\t}\n";
|
||||
|
||||
codeStream << accessorStream.str();
|
||||
|
||||
codeStream << "\ttypedef " << name << " BRDF;" << std::endl;
|
||||
codeStream << "\t" << name << " prepare(MaterialFragmentParameter geometry) {" << std::endl;
|
||||
codeStream << "\t\t" << name << " result;" << std::endl;
|
||||
codeStream << "\t\tresult.baseColor = getBaseColor(geometry);" << std::endl;
|
||||
codeStream << "\t\tresult.metallic = getMetallic(geometry);" << std::endl;
|
||||
codeStream << "\t\tresult.normal = getNormal(geometry);" << std::endl;
|
||||
codeStream << "\t\tresult.specular = getSpecular(geometry);" << std::endl;
|
||||
codeStream << "\t\tresult.roughness = getRoughness(geometry);" << std::endl;
|
||||
codeStream << "\t\tresult.sheen = getSheen(geometry);" << std::endl;
|
||||
codeStream << "\t\treturn result;" << std::endl;
|
||||
codeStream << "\t}" << std::endl;
|
||||
}
|
||||
|
||||
IMPLEMENT_BRDF(BlinnPhong)
|
||||
@@ -1,39 +0,0 @@
|
||||
#pragma once
|
||||
#include "MinimalEngine.h"
|
||||
#include "Containers/List.h"
|
||||
#include <nlohmann/json_fwd.hpp>
|
||||
|
||||
namespace Seele
|
||||
{
|
||||
class BRDF
|
||||
{
|
||||
public:
|
||||
static BRDF* getBRDFByName(const std::string& name);
|
||||
static List<BRDF*> getBRDFList();
|
||||
|
||||
virtual void generateMaterialCode(std::ofstream& codeStream, nlohmann::json codeJson) = 0;
|
||||
protected:
|
||||
BRDF(const char* name);
|
||||
virtual ~BRDF();
|
||||
static List<BRDF*> globalBRDFList;
|
||||
const char* name;
|
||||
};
|
||||
|
||||
#define DECLARE_BRDF(inputClass) \
|
||||
public: \
|
||||
static inputClass staticType;
|
||||
|
||||
#define IMPLEMENT_BRDF(inputClass) \
|
||||
inputClass inputClass::staticType( \
|
||||
#inputClass);
|
||||
|
||||
class BlinnPhong : public BRDF
|
||||
{
|
||||
DECLARE_BRDF(BlinnPhong)
|
||||
public:
|
||||
virtual void generateMaterialCode(std::ofstream& codeStream, nlohmann::json codeJson);
|
||||
protected:
|
||||
BlinnPhong(const char* name);
|
||||
virtual ~BlinnPhong();
|
||||
};
|
||||
} // namespace Seele
|
||||
@@ -1,7 +1,5 @@
|
||||
target_sources(Engine
|
||||
PRIVATE
|
||||
BRDF.h
|
||||
BRDF.cpp
|
||||
Material.h
|
||||
Material.cpp
|
||||
MaterialInstance.h
|
||||
@@ -10,11 +8,10 @@ target_sources(Engine
|
||||
MaterialInterface.cpp
|
||||
ShaderExpression.h
|
||||
ShaderExpression.cpp)
|
||||
|
||||
|
||||
target_sources(Engine
|
||||
PUBLIC FILE_SET HEADERS
|
||||
FILES
|
||||
BRDF.h
|
||||
Material.h
|
||||
MaterialInstance.h
|
||||
MaterialInterface.h
|
||||
|
||||
@@ -9,10 +9,19 @@ using namespace Seele;
|
||||
Gfx::ShaderMap Material::shaderMap;
|
||||
std::mutex Material::shaderMapLock;
|
||||
|
||||
Material::Material(Gfx::PGraphics graphics, Array<PShaderParameter> parameter, Gfx::PDescriptorLayout layout, uint32 uniformDataSize, uint32 uniformBinding, std::string materialName)
|
||||
Material::Material(Gfx::PGraphics graphics,
|
||||
Array<PShaderParameter> parameter,
|
||||
Gfx::PDescriptorLayout layout,
|
||||
uint32 uniformDataSize,
|
||||
uint32 uniformBinding,
|
||||
std::string materialName,
|
||||
Array<PShaderExpression> expressions,
|
||||
MaterialNode brdf)
|
||||
: MaterialInterface(graphics, parameter, uniformDataSize, uniformBinding)
|
||||
, layout(layout)
|
||||
, materialName(materialName)
|
||||
, codeExpressions(expressions)
|
||||
, brdf(brdf)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -89,6 +98,34 @@ void Material::load(ArchiveBuffer& buffer)
|
||||
layout->create();
|
||||
}
|
||||
|
||||
void Material::compile()
|
||||
{
|
||||
std::ofstream codeStream("./shaders/generated/"+materialName+".slang");
|
||||
codeStream << "import Material;\n";
|
||||
codeStream << "import BRDF;\n";
|
||||
codeStream << "import MaterialParameter;\n";
|
||||
codeStream << "struct " << materialName << " : IMaterial {\n";
|
||||
for(const auto& parameter : parameters)
|
||||
{
|
||||
parameter->generateDeclaration(codeStream);
|
||||
}
|
||||
codeStream << "\ttypedef " << brdf.profile << " BRDF;\n";
|
||||
codeStream << "\t" << brdf.profile << " prepare(MaterialFragmentParameter input) {\n";
|
||||
codeStream << "\t\t" << brdf.profile << " result;\n";
|
||||
Map<int32, std::string> varState;
|
||||
for(const auto& expr : codeExpressions)
|
||||
{
|
||||
codeStream << expr->evaluate(varState);
|
||||
}
|
||||
for(const auto& [name, exp] : brdf.variables)
|
||||
{
|
||||
codeStream << "\t\tresult." << name << " = " << varState[exp->key] << ";";
|
||||
}
|
||||
codeStream << "\t\treturn result;\n";
|
||||
codeStream << "\t}\n";
|
||||
codeStream << "};\n";
|
||||
}
|
||||
|
||||
const Gfx::ShaderCollection* Material::getShaders(Gfx::RenderPassType renderPass, VertexInputType* vertexInput) const
|
||||
{
|
||||
Gfx::ShaderPermutation permutation;
|
||||
|
||||
@@ -8,7 +8,14 @@ class Material : public MaterialInterface
|
||||
{
|
||||
public:
|
||||
Material() {}
|
||||
Material(Gfx::PGraphics graphics, Array<PShaderParameter> parameter, Gfx::PDescriptorLayout layout, uint32 uniformDataSize, uint32 uniformBinding, std::string materialName);
|
||||
Material(Gfx::PGraphics graphics,
|
||||
Array<PShaderParameter> parameter,
|
||||
Gfx::PDescriptorLayout layout,
|
||||
uint32 uniformDataSize,
|
||||
uint32 uniformBinding,
|
||||
std::string materialName,
|
||||
Array<PShaderExpression> expressions,
|
||||
MaterialNode brdf);
|
||||
virtual ~Material();
|
||||
virtual Gfx::PDescriptorSet createDescriptorSet();
|
||||
virtual Gfx::PDescriptorLayout getDescriptorLayout() const { return layout; }
|
||||
@@ -17,6 +24,8 @@ public:
|
||||
virtual void save(ArchiveBuffer& buffer) const;
|
||||
virtual void load(ArchiveBuffer& buffer);
|
||||
|
||||
void compile();
|
||||
|
||||
virtual const Gfx::ShaderCollection* getShaders(Gfx::RenderPassType renderPass, VertexInputType* vertexInput) const;
|
||||
virtual Gfx::ShaderCollection& createShaders(Gfx::PGraphics graphics, Gfx::RenderPassType renderPass, VertexInputType* vertexInput);
|
||||
private:
|
||||
@@ -25,6 +34,8 @@ private:
|
||||
|
||||
Gfx::PDescriptorLayout layout;
|
||||
std::string materialName;
|
||||
Array<PShaderExpression> codeExpressions;
|
||||
MaterialNode brdf;
|
||||
// With draw-indirect, we batch vertex data into big vertex buffers
|
||||
// Gfx::PVertexDataManager vertexData;
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ public:
|
||||
virtual Gfx::ShaderCollection& createShaders(Gfx::PGraphics graphics, Gfx::RenderPassType renderPass, VertexInputType* vertexInput) = 0;
|
||||
protected:
|
||||
Gfx::PGraphics graphics;
|
||||
//For now its simply the collection of parameters, since there is no point for expressions
|
||||
std::string brdfName;
|
||||
Array<PShaderParameter> parameters;
|
||||
Gfx::PUniformBuffer uniformBuffer;
|
||||
uint32 uniformDataSize;
|
||||
|
||||
@@ -6,6 +6,16 @@
|
||||
|
||||
using namespace Seele;
|
||||
|
||||
void ShaderExpression::save(ArchiveBuffer& buffer) const
|
||||
{
|
||||
Serialization::save(buffer, key);
|
||||
}
|
||||
|
||||
void ShaderExpression::load(ArchiveBuffer& buffer)
|
||||
{
|
||||
Serialization::load(buffer, key);
|
||||
}
|
||||
|
||||
ShaderParameter::ShaderParameter(std::string name, uint32 byteOffset, uint32 binding)
|
||||
: name(name)
|
||||
, byteOffset(byteOffset)
|
||||
@@ -17,8 +27,15 @@ ShaderParameter::~ShaderParameter()
|
||||
{
|
||||
}
|
||||
|
||||
std::string ShaderParameter::evaluate(Map<int32, std::string>& varState) const
|
||||
{
|
||||
varState[key] = name;
|
||||
return "";
|
||||
}
|
||||
|
||||
void ShaderParameter::save(ArchiveBuffer& buffer) const
|
||||
{
|
||||
ShaderExpression::save(buffer);
|
||||
Serialization::save(buffer, name);
|
||||
Serialization::save(buffer, byteOffset);
|
||||
Serialization::save(buffer, binding);
|
||||
@@ -26,6 +43,7 @@ void ShaderParameter::save(ArchiveBuffer& buffer) const
|
||||
|
||||
void ShaderParameter::load(ArchiveBuffer& buffer)
|
||||
{
|
||||
ShaderExpression::load(buffer);
|
||||
Serialization::load(buffer, name);
|
||||
Serialization::load(buffer, byteOffset);
|
||||
Serialization::load(buffer, binding);
|
||||
@@ -35,6 +53,8 @@ FloatParameter::FloatParameter(std::string name, uint32 byteOffset, uint32 bindi
|
||||
: ShaderParameter(name, byteOffset, binding)
|
||||
, data(0)
|
||||
{
|
||||
output.name = name;
|
||||
output.type = ExpressionType::FLOAT;
|
||||
}
|
||||
|
||||
FloatParameter::~FloatParameter()
|
||||
@@ -58,22 +78,33 @@ void FloatParameter::updateDescriptorSet(Gfx::PDescriptorSet, uint8* dst)
|
||||
std::memcpy(dst + byteOffset, &data, sizeof(float));
|
||||
}
|
||||
|
||||
void FloatParameter::generateDeclaration(std::ofstream& stream) const
|
||||
{
|
||||
stream << "\tlayout(offset = " << byteOffset << ") float " << name << ";\n";
|
||||
}
|
||||
|
||||
VectorParameter::VectorParameter(std::string name, uint32 byteOffset, uint32 binding)
|
||||
: ShaderParameter(name, byteOffset, binding)
|
||||
, data()
|
||||
{
|
||||
output.name = name;
|
||||
output.type = ExpressionType::FLOAT3;
|
||||
}
|
||||
|
||||
VectorParameter::~VectorParameter()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
void VectorParameter::updateDescriptorSet(Gfx::PDescriptorSet, uint8* dst)
|
||||
{
|
||||
std::memcpy(dst + byteOffset, &data, sizeof(Vector));
|
||||
}
|
||||
|
||||
void VectorParameter::generateDeclaration(std::ofstream& stream) const
|
||||
{
|
||||
stream << "\tlayout(offset = " << byteOffset << ") float3 " << name << ";\n";
|
||||
}
|
||||
|
||||
void VectorParameter::save(ArchiveBuffer& buffer) const
|
||||
{
|
||||
ShaderParameter::save(buffer);
|
||||
@@ -88,7 +119,9 @@ void VectorParameter::load(ArchiveBuffer& buffer)
|
||||
|
||||
TextureParameter::TextureParameter(std::string name, uint32 byteOffset, uint32 binding)
|
||||
: ShaderParameter(name, byteOffset, binding)
|
||||
{
|
||||
{
|
||||
output.name = name;
|
||||
output.type = ExpressionType::TEXTURE;
|
||||
}
|
||||
|
||||
TextureParameter::~TextureParameter()
|
||||
@@ -100,6 +133,11 @@ void TextureParameter::updateDescriptorSet(Gfx::PDescriptorSet descriptorSet, ui
|
||||
descriptorSet->updateTexture(binding, data->getTexture());
|
||||
}
|
||||
|
||||
void TextureParameter::generateDeclaration(std::ofstream& stream) const
|
||||
{
|
||||
stream << "\tTexture2D " << name << ";\n";
|
||||
}
|
||||
|
||||
void TextureParameter::save(ArchiveBuffer& buffer) const
|
||||
{
|
||||
ShaderParameter::save(buffer);
|
||||
@@ -117,6 +155,8 @@ void TextureParameter::load(ArchiveBuffer& buffer)
|
||||
SamplerParameter::SamplerParameter(std::string name, uint32 byteOffset, uint32 binding)
|
||||
: ShaderParameter(name, byteOffset, binding)
|
||||
{
|
||||
output.name = name;
|
||||
output.type = ExpressionType::SAMPLER;
|
||||
}
|
||||
|
||||
SamplerParameter::~SamplerParameter()
|
||||
@@ -129,6 +169,10 @@ void SamplerParameter::updateDescriptorSet(Gfx::PDescriptorSet descriptorSet, ui
|
||||
descriptorSet->updateSampler(binding, data);
|
||||
}
|
||||
|
||||
void SamplerParameter::generateDeclaration(std::ofstream& stream) const
|
||||
{
|
||||
stream << "\tSamplerState " << name << ";\n";
|
||||
}
|
||||
|
||||
void SamplerParameter::save(ArchiveBuffer& buffer) const
|
||||
{
|
||||
@@ -144,6 +188,8 @@ void SamplerParameter::load(ArchiveBuffer& buffer)
|
||||
CombinedTextureParameter::CombinedTextureParameter(std::string name, uint32 byteOffset, uint32 binding)
|
||||
: ShaderParameter(name, byteOffset, binding)
|
||||
{
|
||||
output.name = name;
|
||||
output.type = ExpressionType::TEXTURE;
|
||||
}
|
||||
|
||||
CombinedTextureParameter::~CombinedTextureParameter()
|
||||
@@ -151,12 +197,16 @@ CombinedTextureParameter::~CombinedTextureParameter()
|
||||
|
||||
}
|
||||
|
||||
|
||||
void CombinedTextureParameter::updateDescriptorSet(Gfx::PDescriptorSet descriptorSet, uint8*)
|
||||
{
|
||||
descriptorSet->updateTexture(binding, data->getTexture(), sampler);
|
||||
}
|
||||
|
||||
void CombinedTextureParameter::generateDeclaration(std::ofstream& stream) const
|
||||
{
|
||||
stream << "\tTexture2D " << name << ";\n";
|
||||
}
|
||||
|
||||
void CombinedTextureParameter::save(ArchiveBuffer& buffer) const
|
||||
{
|
||||
ShaderParameter::save(buffer);
|
||||
@@ -172,6 +222,213 @@ void CombinedTextureParameter::load(ArchiveBuffer& buffer)
|
||||
sampler = buffer.getGraphics()->createSamplerState({});
|
||||
}
|
||||
|
||||
ConstantExpression::ConstantExpression()
|
||||
{
|
||||
}
|
||||
|
||||
ConstantExpression::ConstantExpression(std::string expr, ExpressionType type)
|
||||
: expr(expr)
|
||||
{
|
||||
output.name = "cexpr";
|
||||
output.type = type;
|
||||
}
|
||||
|
||||
ConstantExpression::~ConstantExpression()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
std::string ConstantExpression::evaluate(Map<int32, std::string>& varState) const
|
||||
{
|
||||
std::string varName = std::format("const_exp_{}", std::abs(key));
|
||||
varState[key] = varName;
|
||||
return std::format("let {} = {};\n", varName, expr);
|
||||
}
|
||||
|
||||
void ConstantExpression::save(ArchiveBuffer& buffer) const
|
||||
{
|
||||
ShaderExpression::save(buffer);
|
||||
Serialization::save(buffer, expr);
|
||||
}
|
||||
|
||||
void ConstantExpression::load(ArchiveBuffer& buffer)
|
||||
{
|
||||
ShaderExpression::load(buffer);
|
||||
Serialization::load(buffer, expr);
|
||||
}
|
||||
|
||||
AddExpression::AddExpression()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
AddExpression::~AddExpression()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
std::string AddExpression::evaluate(Map<int32, std::string>& varState) const
|
||||
{
|
||||
std::string varName = std::format("exp_{}", key);
|
||||
varState[key] = varName;
|
||||
return std::format("let {} = {} + {};\n", varName, varState[inputs.at("lhs").source], varState[inputs.at("rhs").source]);
|
||||
}
|
||||
|
||||
void AddExpression::save(ArchiveBuffer& buffer) const
|
||||
{
|
||||
ShaderExpression::save(buffer);
|
||||
}
|
||||
|
||||
void AddExpression::load(ArchiveBuffer& buffer)
|
||||
{
|
||||
ShaderExpression::load(buffer);
|
||||
}
|
||||
|
||||
std::string SubExpression::evaluate(Map<int32, std::string>& varState) const
|
||||
{
|
||||
std::string varName = std::format("exp_{}", key);
|
||||
varState[key] = varName;
|
||||
return std::format("let {} = {} - {};\n", varName, varState[inputs.at("lhs").source], varState[inputs.at("rhs").source]);
|
||||
}
|
||||
|
||||
void SubExpression::save(ArchiveBuffer& buffer) const
|
||||
{
|
||||
ShaderExpression::save(buffer);
|
||||
}
|
||||
|
||||
void SubExpression::load(ArchiveBuffer& buffer)
|
||||
{
|
||||
ShaderExpression::load(buffer);
|
||||
}
|
||||
|
||||
std::string MulExpression::evaluate(Map<int32, std::string>& varState) const
|
||||
{
|
||||
std::string varName = std::format("exp_{}", key);
|
||||
varState[key] = varName;
|
||||
return std::format("let {} = mul({}, {});\n", varName, varState[inputs.at("lhs").source], varState[inputs.at("rhs").source]);
|
||||
}
|
||||
|
||||
void MulExpression::save(ArchiveBuffer& buffer) const
|
||||
{
|
||||
ShaderExpression::save(buffer);
|
||||
}
|
||||
|
||||
void MulExpression::load(ArchiveBuffer& buffer)
|
||||
{
|
||||
ShaderExpression::load(buffer);
|
||||
}
|
||||
|
||||
std::string SwizzleExpression::evaluate(Map<int32, std::string>& varState) const
|
||||
{
|
||||
std::string varName = std::format("exp_{}", key);
|
||||
std::string swizzle = "";
|
||||
for(uint32 i = 0; i < 4; ++i)
|
||||
{
|
||||
if(comp[i] == -1)
|
||||
{
|
||||
break;
|
||||
}
|
||||
switch (comp[i])
|
||||
{
|
||||
case 0:
|
||||
swizzle += "x";
|
||||
break;
|
||||
case 1:
|
||||
swizzle += "y";
|
||||
break;
|
||||
case 2:
|
||||
swizzle += "z";
|
||||
break;
|
||||
case 3:
|
||||
swizzle += "w";
|
||||
default:
|
||||
throw std::logic_error("invalid component");
|
||||
}
|
||||
}
|
||||
varState[key] = varName;
|
||||
return std::format("let {} = {}.{};\n", varName, varState[inputs.at("target").source], swizzle);
|
||||
}
|
||||
|
||||
void SwizzleExpression::save(ArchiveBuffer& buffer) const
|
||||
{
|
||||
ShaderExpression::save(buffer);
|
||||
Serialization::save(buffer, comp);
|
||||
}
|
||||
|
||||
void SwizzleExpression::load(ArchiveBuffer& buffer)
|
||||
{
|
||||
ShaderExpression::load(buffer);
|
||||
Serialization::load(buffer, comp);
|
||||
}
|
||||
|
||||
std::string SampleExpression::evaluate(Map<int32, std::string>& varState) const
|
||||
{
|
||||
std::string varName = std::format("exp_{}", key);
|
||||
varState[key] = varName;
|
||||
return std::format("let {} = {}.Sample({}, {});\n", varName, varState[inputs.at("texture").source], varState[inputs.at("sampler").source], varState[inputs.at("coords").source]);
|
||||
}
|
||||
|
||||
void SampleExpression::save(ArchiveBuffer& buffer) const
|
||||
{
|
||||
ShaderExpression::save(buffer);
|
||||
}
|
||||
|
||||
void SampleExpression::load(ArchiveBuffer& buffer)
|
||||
{
|
||||
ShaderExpression::load(buffer);
|
||||
}
|
||||
|
||||
void Serialization::save(ArchiveBuffer& buffer, const PShaderExpression& parameter)
|
||||
{
|
||||
Serialization::save(buffer, parameter->getIdentifier());
|
||||
parameter->save(buffer);
|
||||
}
|
||||
|
||||
void Serialization::load(ArchiveBuffer& buffer, PShaderExpression& parameter)
|
||||
{
|
||||
uint64 identifier = 0;
|
||||
Serialization::load(buffer, identifier);
|
||||
switch (identifier)
|
||||
{
|
||||
case FloatParameter::IDENTIFIER:
|
||||
parameter = new FloatParameter();
|
||||
break;
|
||||
case VectorParameter::IDENTIFIER:
|
||||
parameter = new VectorParameter();
|
||||
break;
|
||||
case TextureParameter::IDENTIFIER:
|
||||
parameter = new TextureParameter();
|
||||
break;
|
||||
case SamplerParameter::IDENTIFIER:
|
||||
parameter = new SamplerParameter();
|
||||
break;
|
||||
case CombinedTextureParameter::IDENTIFIER:
|
||||
parameter = new CombinedTextureParameter();
|
||||
break;
|
||||
case ConstantExpression::IDENTIFIER:
|
||||
parameter = new ConstantExpression();
|
||||
break;
|
||||
case AddExpression::IDENTIFIER:
|
||||
parameter = new AddExpression();
|
||||
break;
|
||||
case SubExpression::IDENTIFIER:
|
||||
parameter = new SubExpression();
|
||||
break;
|
||||
case MulExpression::IDENTIFIER:
|
||||
parameter = new MulExpression();
|
||||
break;
|
||||
case SwizzleExpression::IDENTIFIER:
|
||||
parameter = new SwizzleExpression();
|
||||
break;
|
||||
case SampleExpression::IDENTIFIER:
|
||||
parameter = new SampleExpression();
|
||||
break;
|
||||
default:
|
||||
throw std::runtime_error("Unknown Identifier");
|
||||
}
|
||||
parameter->load(buffer);
|
||||
}
|
||||
|
||||
void Serialization::save(ArchiveBuffer& buffer, const PShaderParameter& parameter)
|
||||
{
|
||||
Serialization::save(buffer, parameter->getIdentifier());
|
||||
|
||||
@@ -1,21 +1,44 @@
|
||||
#pragma once
|
||||
#include "MinimalEngine.h"
|
||||
#include "Containers/Map.h"
|
||||
#include "Math/Math.h"
|
||||
#include "Asset/TextureAsset.h"
|
||||
|
||||
namespace Seele
|
||||
{
|
||||
enum class ExpressionType
|
||||
{
|
||||
UNKNOWN,
|
||||
FLOAT,
|
||||
FLOAT2,
|
||||
FLOAT3,
|
||||
FLOAT4,
|
||||
TEXTURE,
|
||||
SAMPLER,
|
||||
};
|
||||
struct ExpressionInput
|
||||
{
|
||||
|
||||
int source;
|
||||
ExpressionType type = ExpressionType::UNKNOWN;
|
||||
};
|
||||
struct ExpressionOutput
|
||||
{
|
||||
|
||||
std::string name;
|
||||
ExpressionType type = ExpressionType::UNKNOWN;
|
||||
};
|
||||
DECLARE_REF(ShaderExpression);
|
||||
struct ShaderExpression
|
||||
{
|
||||
Map<std::string, ExpressionInput> inputs;
|
||||
ExpressionOutput output;
|
||||
int32 key;
|
||||
virtual uint64 getIdentifier() const = 0;
|
||||
virtual std::string evaluate(Map<int32, std::string>& varState) const = 0;
|
||||
virtual void save(ArchiveBuffer& buffer) const;
|
||||
virtual void load(ArchiveBuffer& buffer);
|
||||
};
|
||||
DEFINE_REF(ShaderExpression)
|
||||
|
||||
DECLARE_NAME_REF(Gfx, DescriptorSet)
|
||||
struct ShaderParameter : public ShaderExpression
|
||||
{
|
||||
@@ -27,7 +50,9 @@ struct ShaderParameter : public ShaderExpression
|
||||
virtual ~ShaderParameter();
|
||||
// update a descriptorset, in case of a uniform buffer, copy the data to the dst + byteOffset
|
||||
virtual void updateDescriptorSet(Gfx::PDescriptorSet descriptorSet, uint8* dst) = 0;
|
||||
virtual void generateDeclaration(std::ofstream& stream) const = 0;
|
||||
virtual uint64 getIdentifier() const = 0;
|
||||
virtual std::string evaluate(Map<int32, std::string>& varState) const override;
|
||||
virtual void save(ArchiveBuffer& buffer) const;
|
||||
virtual void load(ArchiveBuffer& buffer);
|
||||
};
|
||||
@@ -40,6 +65,7 @@ struct FloatParameter : public ShaderParameter
|
||||
FloatParameter(std::string name, uint32 byteOffset, uint32 binding);
|
||||
virtual ~FloatParameter();
|
||||
virtual void updateDescriptorSet(Gfx::PDescriptorSet descriptorSet, uint8* dst) override;
|
||||
virtual void generateDeclaration(std::ofstream& stream) const override;
|
||||
virtual uint64 getIdentifier() const override { return IDENTIFIER; }
|
||||
virtual void save(ArchiveBuffer& buffer) const override;
|
||||
virtual void load(ArchiveBuffer& buffer) override;
|
||||
@@ -53,6 +79,7 @@ struct VectorParameter : public ShaderParameter
|
||||
VectorParameter(std::string name, uint32 byteOffset, uint32 binding);
|
||||
virtual ~VectorParameter();
|
||||
virtual void updateDescriptorSet(Gfx::PDescriptorSet descriptorSet, uint8* dst) override;
|
||||
virtual void generateDeclaration(std::ofstream& stream) const override;
|
||||
virtual uint64 getIdentifier() const override { return IDENTIFIER; }
|
||||
virtual void save(ArchiveBuffer& buffer) const override;
|
||||
virtual void load(ArchiveBuffer& buffer) override;
|
||||
@@ -66,6 +93,7 @@ struct TextureParameter : public ShaderParameter
|
||||
TextureParameter(std::string name, uint32 byteOffset, uint32 binding);
|
||||
virtual ~TextureParameter();
|
||||
virtual void updateDescriptorSet(Gfx::PDescriptorSet descriptorSet, uint8* dst) override;
|
||||
virtual void generateDeclaration(std::ofstream& stream) const override;
|
||||
virtual uint64 getIdentifier() const override { return IDENTIFIER; }
|
||||
virtual void save(ArchiveBuffer& buffer) const override;
|
||||
virtual void load(ArchiveBuffer& buffer) override;
|
||||
@@ -80,6 +108,7 @@ struct SamplerParameter : public ShaderParameter
|
||||
SamplerParameter(std::string name, uint32 byteOffset, uint32 binding);
|
||||
virtual ~SamplerParameter();
|
||||
virtual void updateDescriptorSet(Gfx::PDescriptorSet descriptorSet, uint8* dst) override;
|
||||
virtual void generateDeclaration(std::ofstream& stream) const override;
|
||||
virtual uint64 getIdentifier() const override { return IDENTIFIER; }
|
||||
virtual void save(ArchiveBuffer& buffer) const override;
|
||||
virtual void load(ArchiveBuffer& buffer) override;
|
||||
@@ -94,13 +123,99 @@ struct CombinedTextureParameter : public ShaderParameter
|
||||
CombinedTextureParameter(std::string name, uint32 byteOffset, uint32 binding);
|
||||
virtual ~CombinedTextureParameter();
|
||||
virtual void updateDescriptorSet(Gfx::PDescriptorSet descriptorSet, uint8* dst) override;
|
||||
virtual void generateDeclaration(std::ofstream& stream) const override;
|
||||
virtual uint64 getIdentifier() const override { return IDENTIFIER; }
|
||||
virtual void save(ArchiveBuffer& buffer) const override;
|
||||
virtual void load(ArchiveBuffer& buffer) override;
|
||||
};
|
||||
DEFINE_REF(CombinedTextureParameter)
|
||||
|
||||
|
||||
struct ConstantExpression : public ShaderExpression
|
||||
{
|
||||
static constexpr uint64 IDENTIFIER = 0x11;
|
||||
std::string expr;
|
||||
ConstantExpression();
|
||||
ConstantExpression(std::string expr, ExpressionType type);
|
||||
virtual ~ConstantExpression();
|
||||
virtual uint64 getIdentifier() const { return IDENTIFIER; }
|
||||
virtual std::string evaluate(Map<int32, std::string>& varState) const override;
|
||||
virtual void save(ArchiveBuffer& buffer) const override;
|
||||
virtual void load(ArchiveBuffer& buffer) override;
|
||||
};
|
||||
DEFINE_REF(ConstantExpression)
|
||||
struct AddExpression : public ShaderExpression
|
||||
{
|
||||
static constexpr uint64 IDENTIFIER = 0x12;
|
||||
AddExpression();
|
||||
virtual ~AddExpression();
|
||||
virtual uint64 getIdentifier() const { return IDENTIFIER; }
|
||||
virtual std::string evaluate(Map<int32, std::string>& varState) const override;
|
||||
virtual void save(ArchiveBuffer& buffer) const override;
|
||||
virtual void load(ArchiveBuffer& buffer) override;
|
||||
};
|
||||
DEFINE_REF(AddExpression)
|
||||
struct SubExpression : public ShaderExpression
|
||||
{
|
||||
static constexpr uint64 IDENTIFIER = 0x13;
|
||||
SubExpression() {}
|
||||
virtual ~SubExpression() {}
|
||||
virtual uint64 getIdentifier() const { return IDENTIFIER; }
|
||||
virtual std::string evaluate(Map<int32, std::string>& varState) const override;
|
||||
virtual void save(ArchiveBuffer& buffer) const override;
|
||||
virtual void load(ArchiveBuffer& buffer) override;
|
||||
};
|
||||
DEFINE_REF(SubExpression)
|
||||
struct MulExpression : public ShaderExpression
|
||||
{
|
||||
static constexpr uint64 IDENTIFIER = 0x14;
|
||||
MulExpression() {}
|
||||
virtual ~MulExpression() {}
|
||||
virtual uint64 getIdentifier() const { return IDENTIFIER; }
|
||||
virtual std::string evaluate(Map<int32, std::string>& varState) const override;
|
||||
virtual void save(ArchiveBuffer& buffer) const override;
|
||||
virtual void load(ArchiveBuffer& buffer) override;
|
||||
};
|
||||
DEFINE_REF(MulExpression)
|
||||
struct SwizzleExpression : public ShaderExpression
|
||||
{
|
||||
static constexpr uint64 IDENTIFIER = 0x15;
|
||||
StaticArray<int32, 4> comp = {-1, -1, -1, -1};
|
||||
SwizzleExpression() {}
|
||||
virtual ~SwizzleExpression() {}
|
||||
virtual uint64 getIdentifier() const { return IDENTIFIER; }
|
||||
virtual std::string evaluate(Map<int32, std::string>& varState) const override;
|
||||
virtual void save(ArchiveBuffer& buffer) const override;
|
||||
virtual void load(ArchiveBuffer& buffer) override;
|
||||
};
|
||||
DEFINE_REF(SwizzleExpression)
|
||||
struct SampleExpression : public ShaderExpression
|
||||
{
|
||||
static constexpr uint64 IDENTIFIER = 0x16;
|
||||
SampleExpression() {}
|
||||
virtual ~SampleExpression() {}
|
||||
virtual uint64 getIdentifier() const { return IDENTIFIER; }
|
||||
virtual std::string evaluate(Map<int32, std::string>& varState) const override;
|
||||
virtual void save(ArchiveBuffer& buffer) const override;
|
||||
virtual void load(ArchiveBuffer& buffer) override;
|
||||
};
|
||||
DEFINE_REF(SampleExpression)
|
||||
|
||||
struct MaterialNode
|
||||
{
|
||||
std::string profile;
|
||||
Map<std::string, PShaderExpression> variables;
|
||||
MaterialNode() {}
|
||||
virtual ~MaterialNode() {}
|
||||
virtual std::string evaluate() const { return ""; }
|
||||
void save(ArchiveBuffer& buffer) const;
|
||||
void load(ArchiveBuffer& buffer);
|
||||
};
|
||||
DEFINE_REF(MaterialNode)
|
||||
namespace Serialization
|
||||
{
|
||||
void save(ArchiveBuffer& buffer, const PShaderExpression& parameter);
|
||||
void load(ArchiveBuffer& buffer, PShaderExpression& parameter);
|
||||
void save(ArchiveBuffer& buffer, const PShaderParameter& parameter);
|
||||
void load(ArchiveBuffer& buffer, PShaderParameter& parameter);
|
||||
} // namespace Serialization
|
||||
|
||||
@@ -6,7 +6,7 @@ target_sources(Engine
|
||||
Transform.cpp
|
||||
Vector.h
|
||||
Vector.cpp)
|
||||
|
||||
|
||||
target_sources(Engine
|
||||
PUBLIC FILE_SET HEADERS
|
||||
FILES
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
add_subdirectory(Windows/)
|
||||
@@ -0,0 +1,10 @@
|
||||
target_sources(Engine
|
||||
PRIVATE
|
||||
GameInterface.h
|
||||
GameInterface.cpp)
|
||||
|
||||
|
||||
target_sources(Engine
|
||||
PUBLIC FILE_SET HEADERS
|
||||
FILES
|
||||
GameInterface.h)
|
||||
@@ -0,0 +1,31 @@
|
||||
#include "GameInterface.h"
|
||||
|
||||
using namespace Seele;
|
||||
|
||||
GameInterface::GameInterface(std::string dllPath)
|
||||
: dllPath(dllPath)
|
||||
{
|
||||
}
|
||||
|
||||
GameInterface::~GameInterface()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
Game* GameInterface::getGame()
|
||||
{
|
||||
return game;
|
||||
}
|
||||
|
||||
void GameInterface::reload(AssetRegistry* registry)
|
||||
{
|
||||
if(lib != NULL)
|
||||
{
|
||||
destroyInstance(game);
|
||||
FreeLibrary(lib);
|
||||
}
|
||||
lib = LoadLibraryA(dllPath.c_str());
|
||||
createInstance = (decltype(createInstance))GetProcAddress(lib, "createInstance");
|
||||
destroyInstance = (decltype(destroyInstance))GetProcAddress(lib, "destroyInstance");
|
||||
game = createInstance(registry);
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
#pragma once
|
||||
#include "Game.h"
|
||||
#include "Windows.h"
|
||||
|
||||
namespace Seele
|
||||
{
|
||||
class GameInterface
|
||||
{
|
||||
public:
|
||||
GameInterface(std::string dllPath);
|
||||
~GameInterface();
|
||||
Game* getGame();
|
||||
void reload(AssetRegistry* registry);
|
||||
private:
|
||||
HMODULE lib = NULL;
|
||||
std::string dllPath;
|
||||
Game* game;
|
||||
Game* (*createInstance)(AssetRegistry*) = nullptr;
|
||||
void (*destroyInstance)(Game*) = nullptr;
|
||||
};
|
||||
} // namespace Seele
|
||||
@@ -86,13 +86,14 @@ LightEnv Scene::getLightBuffer() const
|
||||
{
|
||||
LightEnv result;
|
||||
result.directionalLights[0].color = Vector4(0.4, 0.3, 0.5, 1.0);
|
||||
result.directionalLights[0].direction = Vector4(0.5, 0.5, 0, 0);
|
||||
result.directionalLights[0].intensity = Vector4(1.0, 0.9, 0.7, 0.5);\
|
||||
result.directionalLights[0].direction = Vector4(0.5, -0.5, 0, 0);
|
||||
result.directionalLights[0].intensity = Vector4(1.0, 0.9, 0.7, 0.5);
|
||||
result.numDirectionalLights = 1;
|
||||
result.numPointLights = 0;
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
Component::Skybox Scene::getSkybox()
|
||||
{
|
||||
return Seele::Component::Skybox {
|
||||
|
||||
@@ -44,11 +44,35 @@ void ArchiveBuffer::readFromStream(std::istream& stream)
|
||||
stream.read((char*)memory.data(), bufferLength);
|
||||
}
|
||||
|
||||
void ArchiveBuffer::seek(int64 s, SeekOp op)
|
||||
{
|
||||
int64 newPos = position;
|
||||
switch (op)
|
||||
{
|
||||
case SeekOp::BEGIN:
|
||||
newPos = s;
|
||||
break;
|
||||
case SeekOp::END:
|
||||
newPos = memory.size() + s;
|
||||
break;
|
||||
case SeekOp::CURRENT:
|
||||
newPos = position + s;
|
||||
break;
|
||||
}
|
||||
assert(newPos >= 0 && newPos < memory.size());
|
||||
newPos = position;
|
||||
}
|
||||
|
||||
bool ArchiveBuffer::eof() const
|
||||
{
|
||||
return position == memory.size();
|
||||
}
|
||||
|
||||
size_t Seele::ArchiveBuffer::size() const
|
||||
{
|
||||
return memory.size();
|
||||
}
|
||||
|
||||
void ArchiveBuffer::rewind()
|
||||
{
|
||||
position = 0;
|
||||
|
||||
@@ -16,7 +16,15 @@ public:
|
||||
void readBytes(void* dest, uint64 size);
|
||||
void writeToStream(std::ostream& stream);
|
||||
void readFromStream(std::istream& stream);
|
||||
enum class SeekOp
|
||||
{
|
||||
CURRENT,
|
||||
BEGIN,
|
||||
END,
|
||||
};
|
||||
void seek(int64 s, SeekOp op);
|
||||
bool eof() const;
|
||||
size_t size() const;
|
||||
void rewind();
|
||||
Gfx::PGraphics& getGraphics();
|
||||
private:
|
||||
@@ -105,7 +113,6 @@ namespace Serialization
|
||||
type.resize(length);
|
||||
buffer.readBytes(type.data(), sizeof(T) * type.size());
|
||||
}
|
||||
|
||||
template<std::floating_point T>
|
||||
static void save(ArchiveBuffer& buffer, const Array<T>& type)
|
||||
{
|
||||
@@ -121,6 +128,26 @@ namespace Serialization
|
||||
type.resize(length);
|
||||
buffer.readBytes(type.data(), sizeof(T) * type.size());
|
||||
}
|
||||
template<std::integral T, size_t N>
|
||||
static void save(ArchiveBuffer& buffer, const StaticArray<T, N>& type)
|
||||
{
|
||||
buffer.writeBytes(type.data(), sizeof(T) * N);
|
||||
}
|
||||
template<std::integral T, size_t N>
|
||||
static void load(ArchiveBuffer& buffer, StaticArray<T, N>& type)
|
||||
{
|
||||
buffer.readBytes(type.data(), sizeof(T) * N);
|
||||
}
|
||||
template<std::floating_point T, size_t N>
|
||||
static void save(ArchiveBuffer& buffer, const StaticArray<T, N>& type)
|
||||
{
|
||||
buffer.writeBytes(type.data(), sizeof(T) * N);
|
||||
}
|
||||
template<std::floating_point T, size_t N>
|
||||
static void load(ArchiveBuffer& buffer, StaticArray<T, N>& type)
|
||||
{
|
||||
buffer.readBytes(type.data(), sizeof(T) * N);
|
||||
}
|
||||
template<typename T>
|
||||
static void save(ArchiveBuffer& buffer, const Array<T>& type) requires (!std::is_integral_v<T> && !std::is_floating_point_v<T>)
|
||||
{
|
||||
|
||||
@@ -3,7 +3,6 @@ target_sources(Engine
|
||||
ArchiveBuffer.h
|
||||
ArchiveBuffer.cpp)
|
||||
|
||||
|
||||
target_sources(Engine
|
||||
PUBLIC FILE_SET HEADERS
|
||||
FILES
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
target_sources(Engine
|
||||
PRIVATE
|
||||
GameView.h
|
||||
GameView.cpp
|
||||
View.h
|
||||
View.cpp
|
||||
Window.cpp
|
||||
@@ -10,6 +12,7 @@ target_sources(Engine
|
||||
target_sources(Engine
|
||||
PUBLIC FILE_SET HEADERS
|
||||
FILES
|
||||
GameView.h
|
||||
View.h
|
||||
Window.h
|
||||
WindowManager.h)
|
||||
|
||||
@@ -0,0 +1,138 @@
|
||||
#include "GameView.h"
|
||||
#include "Graphics/Graphics.h"
|
||||
#include "Window/Window.h"
|
||||
#include "Component/KeyboardInput.h"
|
||||
#include "Actor/CameraActor.h"
|
||||
#include "Asset/AssetRegistry.h"
|
||||
|
||||
using namespace Seele;
|
||||
|
||||
AssetRegistry* instance = new AssetRegistry();
|
||||
|
||||
GameView::GameView(Gfx::PGraphics graphics, PWindow window, const ViewportCreateInfo &createInfo, std::string dllPath)
|
||||
: View(graphics, window, createInfo, "Game")
|
||||
, gameInterface(dllPath)
|
||||
, renderGraph(RenderGraphBuilder::build(
|
||||
DepthPrepass(graphics),
|
||||
LightCullingPass(graphics),
|
||||
BasePass(graphics),
|
||||
SkyboxRenderPass(graphics)
|
||||
))
|
||||
{
|
||||
scene = new Scene(graphics);
|
||||
gameInterface.reload(instance);
|
||||
|
||||
systemGraph = new SystemGraph();
|
||||
gameInterface.getGame()->setupScene(scene, systemGraph);
|
||||
renderGraph.updateViewport(viewport);
|
||||
}
|
||||
|
||||
GameView::~GameView()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void GameView::beginUpdate()
|
||||
{
|
||||
}
|
||||
|
||||
void GameView::update()
|
||||
{
|
||||
static auto startTime = std::chrono::high_resolution_clock::now();
|
||||
systemGraph->run(threadPool, updateTime);
|
||||
scene->update(updateTime);
|
||||
auto endTime = std::chrono::high_resolution_clock::now();
|
||||
std::chrono::duration<float> duration = (endTime - startTime);
|
||||
updateTime = duration.count();
|
||||
startTime = endTime;
|
||||
}
|
||||
|
||||
void GameView::commitUpdate()
|
||||
{
|
||||
depthPrepassData.staticDrawList = scene->getStaticMeshes();
|
||||
depthPrepassData.sceneDataBuffer = scene->getSceneDataBuffer();
|
||||
lightCullingData.lightEnv = scene->getLightBuffer();
|
||||
basePassData.staticDrawList = scene->getStaticMeshes();
|
||||
basePassData.sceneDataBuffer = scene->getSceneDataBuffer();
|
||||
skyboxData.skybox = scene->getSkybox();
|
||||
}
|
||||
|
||||
void GameView::prepareRender()
|
||||
{
|
||||
renderGraph.updatePassData(depthPrepassData, lightCullingData, basePassData, skyboxData);
|
||||
}
|
||||
|
||||
void GameView::render()
|
||||
{
|
||||
scene->view<Component::Camera>([&](Component::Camera& cam)
|
||||
{
|
||||
if(cam.mainCamera)
|
||||
{
|
||||
renderGraph.render(cam);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void GameView::keyCallback(KeyCode code, InputAction action, KeyModifier)
|
||||
{
|
||||
scene->view<Component::KeyboardInput>([=](Component::KeyboardInput& input)
|
||||
{
|
||||
//if(code == KeyCode::KEY_R && action == InputAction::PRESS)
|
||||
//{
|
||||
// auto cubeMesh = AssetRegistry::findMesh("cube");
|
||||
// PEntity cube2 = new Entity(scene);
|
||||
// Component::Transform& cube2Transform = cube2->attachComponent<Component::Transform>();
|
||||
// cube2Transform.setPosition(Vector(0, 20, 0));
|
||||
// cube2Transform.setRotation(Quaternion(Vector(0.f, 90.f, 90.f)));
|
||||
// cube2Transform.setScale(Vector(2.f, 2.f, 2.f));
|
||||
// cubeMesh->physicsMesh.type = Component::ColliderType::DYNAMIC;
|
||||
// cube2->attachComponent<Component::Collider>(cubeMesh->physicsMesh);
|
||||
// cube2->attachComponent<Component::StaticMesh>(cubeMesh);
|
||||
// Component::RigidBody& physics2 = cube2->attachComponent<Component::RigidBody>();
|
||||
// physics2.linearMomentum = Vector(0, -10, 0);
|
||||
// physics2.angularMomentum = Vector(0, 0, 0);
|
||||
//}
|
||||
//
|
||||
//if(code == KeyCode::KEY_B && action == InputAction::PRESS)
|
||||
//{
|
||||
// showDebug = !showDebug;
|
||||
// debugPassData.vertices.clear();
|
||||
//}
|
||||
|
||||
input.keys[code] = action != InputAction::RELEASE;
|
||||
});
|
||||
}
|
||||
|
||||
void GameView::mouseMoveCallback(double xPos, double yPos)
|
||||
{
|
||||
scene->view<Component::KeyboardInput>([=](Component::KeyboardInput& input)
|
||||
{
|
||||
input.mouseX = static_cast<float>(xPos);
|
||||
input.mouseY = static_cast<float>(yPos);
|
||||
});
|
||||
}
|
||||
|
||||
void GameView::mouseButtonCallback(MouseButton button, InputAction action, KeyModifier)
|
||||
{
|
||||
scene->view<Component::KeyboardInput>([=](Component::KeyboardInput& input)
|
||||
{
|
||||
if (button == MouseButton::MOUSE_BUTTON_1)
|
||||
{
|
||||
input.mouse1 = action != InputAction::RELEASE;
|
||||
}
|
||||
if (button == MouseButton::MOUSE_BUTTON_2)
|
||||
{
|
||||
input.mouse2 = action != InputAction::RELEASE;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void GameView::scrollCallback(double, double)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void GameView::fileCallback(int, const char**)
|
||||
{
|
||||
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
#pragma once
|
||||
#include "Window/View.h"
|
||||
#include "Scene/Scene.h"
|
||||
#include "Graphics/RenderPass/DepthPrepass.h"
|
||||
#include "Graphics/RenderPass/LightCullingPass.h"
|
||||
#include "Graphics/RenderPass/BasePass.h"
|
||||
#include "Graphics/RenderPass/SkyboxRenderPass.h"
|
||||
#include "Platform/Windows/GameInterface.h" // TODO
|
||||
|
||||
namespace Seele
|
||||
{
|
||||
class GameView : public View
|
||||
{
|
||||
public:
|
||||
GameView(Gfx::PGraphics graphics, PWindow window, const ViewportCreateInfo &createInfo, std::string dllPath);
|
||||
virtual ~GameView();
|
||||
virtual void beginUpdate() override;
|
||||
virtual void update() override;
|
||||
virtual void commitUpdate() override;
|
||||
|
||||
virtual void prepareRender() override;
|
||||
virtual void render() override;
|
||||
private:
|
||||
GameInterface gameInterface;
|
||||
RenderGraph<
|
||||
DepthPrepass,
|
||||
LightCullingPass,
|
||||
BasePass,
|
||||
SkyboxRenderPass
|
||||
> renderGraph;
|
||||
|
||||
DepthPrepassData depthPrepassData;
|
||||
LightCullingPassData lightCullingData;
|
||||
BasePassData basePassData;
|
||||
SkyboxPassData skyboxData;
|
||||
|
||||
PScene scene;
|
||||
PSystemGraph systemGraph;
|
||||
dp::thread_pool<> threadPool;
|
||||
float updateTime = 0;
|
||||
|
||||
virtual void keyCallback(Seele::KeyCode code, Seele::InputAction action, Seele::KeyModifier modifier) override;
|
||||
virtual void mouseMoveCallback(double xPos, double yPos) override;
|
||||
virtual void mouseButtonCallback(Seele::MouseButton button, Seele::InputAction action, Seele::KeyModifier modifier) override;
|
||||
virtual void scrollCallback(double xOffset, double yOffset) override;
|
||||
virtual void fileCallback(int count, const char** paths) override;
|
||||
};
|
||||
DEFINE_REF(GameView)
|
||||
} // namespace Seele
|
||||
Reference in New Issue
Block a user