added first selectable, and modyfiable assets to system, Mesh, Material, and Texture. Texture ist WIP, because no subassets are created jet, well technicaly everything is WIP bit nevermind XD
This commit is contained in:
@@ -14,3 +14,8 @@ TSE::Mesh *TSE::MeshContainer::GetMesh() const
|
||||
{
|
||||
return mesh;
|
||||
}
|
||||
|
||||
TSE::Mesh **TSE::MeshContainer::GetMeshRef()
|
||||
{
|
||||
return &mesh;
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
#define MESH_CONTAINER typeid(TSE::MeshContainer).name()
|
||||
|
||||
#include "Types.hpp"
|
||||
#include "Mesh.hpp"
|
||||
#include "elements/Mesh.hpp"
|
||||
#include "elements/BehaviourScript.hpp"
|
||||
|
||||
namespace TSE
|
||||
@@ -19,6 +19,7 @@ namespace TSE
|
||||
|
||||
void SetMesh(Mesh* mesh);
|
||||
Mesh* GetMesh() const;
|
||||
Mesh** GetMeshRef();
|
||||
|
||||
inline const char* GetName() override
|
||||
{
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
#include "elements/BehaviourScript.hpp"
|
||||
#include "Rect.hpp"
|
||||
#include "Vector2.hpp"
|
||||
#include "Mesh.hpp"
|
||||
#include "elements/Mesh.hpp"
|
||||
|
||||
namespace TSE
|
||||
{
|
||||
|
||||
@@ -22,6 +22,11 @@ namespace TSE
|
||||
return material;
|
||||
}
|
||||
|
||||
Material **Renderable::GetMaterialRef()
|
||||
{
|
||||
return &material;
|
||||
}
|
||||
|
||||
Mesh* Renderable::GetMeshContainer() const {
|
||||
if(baseObject->HasBehaviourScript(MESH_CONTAINER))
|
||||
return dynamic_cast<MeshContainer*>(baseObject->GetBehaviourScript(MESH_CONTAINER))->GetMesh();
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
#include "elements/Material.hpp"
|
||||
#include "interfaces/IRenderable.hpp"
|
||||
#include "elements/BehaviourScript.hpp"
|
||||
#include "Mesh.hpp"
|
||||
#include "elements/Mesh.hpp"
|
||||
|
||||
namespace TSE
|
||||
{
|
||||
@@ -28,6 +28,7 @@ namespace TSE
|
||||
}
|
||||
void SetMaterial(Material* material);
|
||||
Material* GetMaterial();
|
||||
Material** GetMaterialRef();
|
||||
|
||||
private:
|
||||
Mesh* GetMeshContainer() const;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#include "AssetLibrary.hpp"
|
||||
#include "Material.hpp"
|
||||
#include "Mesh.hpp"
|
||||
#include "Texture.hpp"
|
||||
#include <iostream>
|
||||
#include <filesystem>
|
||||
@@ -10,6 +11,32 @@ std::unordered_map<uuids::uuid, std::tuple<std::any, TSE::string, uuids::uuid>>
|
||||
std::unordered_map<TSE::string, uuids::uuid> TSE::AssetLibrary::alreadyLoadedAssets;
|
||||
TSE::string TSE::AssetLibrary::currentAssetPath;
|
||||
|
||||
void TSE::AssetLibrary::LoadDefaultAssets()
|
||||
{
|
||||
Mesh* quad = new Mesh(Mesh::GetQuadMesh());
|
||||
quad->id = TSE_ID_MESH_QUAD;
|
||||
quad->IsVirtualAsset = true;
|
||||
|
||||
Mesh* circle = new Mesh(Mesh::GetCircleMesh());
|
||||
circle->id = TSE_ID_MESH_CIRCLE;
|
||||
circle->IsVirtualAsset = true;
|
||||
|
||||
SetValue(TSE_ID_MESH_QUAD, quad);
|
||||
SetValue(TSE_ID_MESH_CIRCLE, circle);
|
||||
}
|
||||
|
||||
void TSE::AssetLibrary::RemoveValue(const uuids::uuid &key, IAsset *asset)
|
||||
{
|
||||
if(HasValue(key))
|
||||
{
|
||||
if(!asset->IsVirtualAsset)
|
||||
{
|
||||
alreadyLoadedAssets.erase(asset->rawPath);
|
||||
}
|
||||
assets.erase(key);
|
||||
}
|
||||
}
|
||||
|
||||
bool TSE::AssetLibrary::HasValue(const uuids::uuid &key)
|
||||
{
|
||||
return assets.find(key) != assets.end();
|
||||
@@ -49,6 +76,12 @@ void TSE::AssetLibrary::SaveAllAssets()
|
||||
if(!value->IsVirtualAsset)
|
||||
value->SaveAsset();
|
||||
}
|
||||
if (type == typeid(Mesh*).name())
|
||||
{
|
||||
Mesh* value = GetValue<Mesh*>(name);
|
||||
if(!value->IsVirtualAsset)
|
||||
value->SaveAsset();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -65,6 +98,7 @@ std::string ToLower(std::string str)
|
||||
|
||||
void TSE::AssetLibrary::LoadAllAssets(string &path)
|
||||
{
|
||||
LoadDefaultAssets();
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
currentAssetPath = string(path);
|
||||
@@ -97,6 +131,13 @@ void TSE::AssetLibrary::LoadAllAssets(string &path)
|
||||
SetValue(tex->id, tex);
|
||||
alreadyLoadedAssets[path] = tex->id;
|
||||
}
|
||||
else if (ext == ".obj")
|
||||
{
|
||||
Mesh* mesh = new Mesh();
|
||||
mesh->LoadAsset(path);
|
||||
SetValue(mesh->id, mesh);
|
||||
alreadyLoadedAssets[path] = mesh->id;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -120,8 +161,25 @@ void TSE::AssetLibrary::RescanAssets()
|
||||
if(!value->IsVirtualAsset)
|
||||
value->Rescan();
|
||||
}
|
||||
if (type == typeid(Mesh*).name())
|
||||
{
|
||||
Mesh* value = GetValue<Mesh*>(name);
|
||||
if(!value->IsVirtualAsset)
|
||||
value->Rescan();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template void TSE::AssetLibrary::SetValue<TSE::Material>(const uuids::uuid&, const TSE::Material*);
|
||||
template void TSE::AssetLibrary::SetValue<TSE::Texture>(const uuids::uuid&, const TSE::Texture*);
|
||||
TSE::string TSE::AssetLibrary::GetCurrentAssetPath()
|
||||
{
|
||||
return currentAssetPath;
|
||||
}
|
||||
|
||||
template void TSE::AssetLibrary::SetValue<TSE::Material>(const uuids::uuid&, TSE::Material*);
|
||||
template void TSE::AssetLibrary::SetValue<TSE::Texture>(const uuids::uuid&, TSE::Texture*);
|
||||
template void TSE::AssetLibrary::SetValue<TSE::Mesh>(const uuids::uuid&, TSE::Mesh*);
|
||||
|
||||
|
||||
template TSE::Texture* TSE::AssetLibrary::GetValue<TSE::Texture*>(const uuids::uuid&);
|
||||
template TSE::Material* TSE::AssetLibrary::GetValue<TSE::Material*>(const uuids::uuid&);
|
||||
template TSE::Mesh* TSE::AssetLibrary::GetValue<TSE::Mesh*>(const uuids::uuid&);
|
||||
|
||||
@@ -6,20 +6,27 @@
|
||||
|
||||
namespace TSE
|
||||
{
|
||||
const uuids::uuid TSE_ID_MESH_QUAD = uuids::uuid::from_string("00000000-0000-0000-1000-000000000000").value();
|
||||
const uuids::uuid TSE_ID_MESH_CIRCLE = uuids::uuid::from_string("00000000-0000-0000-1001-000000000000").value();
|
||||
|
||||
class AssetLibrary
|
||||
{
|
||||
private:
|
||||
static std::unordered_map<uuids::uuid, std::tuple<std::any, string, uuids::uuid>> assets;
|
||||
static std::unordered_map<string, uuids::uuid> alreadyLoadedAssets;
|
||||
|
||||
|
||||
static string currentAssetPath;
|
||||
|
||||
static void LoadDefaultAssets();
|
||||
|
||||
public:
|
||||
template<typename T>
|
||||
static T GetValue(const uuids::uuid& key);
|
||||
|
||||
template<typename T>
|
||||
static void SetValue(const uuids::uuid& key, const T* value);
|
||||
static void SetValue(const uuids::uuid& key, T* value);
|
||||
static void RemoveValue(const uuids::uuid& key, IAsset* asset);
|
||||
|
||||
static bool HasValue(const uuids::uuid& key);
|
||||
static int GetValueCount();
|
||||
@@ -28,6 +35,8 @@ namespace TSE
|
||||
static void SaveAllAssets();
|
||||
static void LoadAllAssets(string& path);
|
||||
static void RescanAssets();
|
||||
|
||||
static string GetCurrentAssetPath();
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
@@ -41,7 +50,7 @@ namespace TSE
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void AssetLibrary::SetValue(const uuids::uuid& key, const T* value) {
|
||||
void AssetLibrary::SetValue(const uuids::uuid& key, T* value) {
|
||||
assets[key] = std::make_tuple(value, typeid(T).name(), key);
|
||||
}
|
||||
}
|
||||
@@ -13,6 +13,7 @@
|
||||
#include "utils/JsonExports.hpp"
|
||||
#include "Debug.hpp"
|
||||
#include "ShaderRegistry.hpp"
|
||||
#include "AssetLibrary.hpp"
|
||||
|
||||
namespace TSE
|
||||
{
|
||||
@@ -191,6 +192,7 @@ namespace TSE
|
||||
|
||||
void Material::SaveAsset()
|
||||
{
|
||||
if(IsVirtualAsset) return;
|
||||
using json = nlohmann::ordered_json;
|
||||
|
||||
json material = GenerateMaterialJson();
|
||||
@@ -289,6 +291,17 @@ namespace TSE
|
||||
hash = sha256(material.dump(-1));
|
||||
}
|
||||
|
||||
string Material::assetType()
|
||||
{
|
||||
return "material";
|
||||
}
|
||||
|
||||
void Material::UnloadAsset()
|
||||
{
|
||||
AssetLibrary::RemoveValue(id, static_cast<IAsset*>(this));
|
||||
delete(this);
|
||||
}
|
||||
|
||||
void Material::SaveMeta()
|
||||
{
|
||||
string metaPath = PathToMetaPath(rawPath);
|
||||
|
||||
@@ -46,6 +46,8 @@ namespace TSE
|
||||
void SaveAsset() override;
|
||||
nlohmann::ordered_json GenerateMaterialJson();
|
||||
void Hash() override;
|
||||
string assetType() override;
|
||||
void UnloadAsset() override;
|
||||
protected:
|
||||
void SaveMeta() override;
|
||||
};
|
||||
|
||||
564
TSE_Core/src/elements/Mesh.cpp
Normal file
564
TSE_Core/src/elements/Mesh.cpp
Normal file
@@ -0,0 +1,564 @@
|
||||
#include "Mesh.hpp"
|
||||
#include "MathF.hpp"
|
||||
#include <cmath>
|
||||
#include "json.hpp"
|
||||
#include "PathHelper.hpp"
|
||||
#include "IdGenerator.hpp"
|
||||
#include "Debug.hpp"
|
||||
#include "elements/AssetLibrary.hpp"
|
||||
#include <algorithm>
|
||||
#include <filesystem>
|
||||
#include <iomanip>
|
||||
#include <limits>
|
||||
#include <sstream>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace
|
||||
{
|
||||
struct ObjFaceVertex
|
||||
{
|
||||
int vertexIndex = -1;
|
||||
int uvIndex = -1;
|
||||
|
||||
bool operator==(const ObjFaceVertex& other) const
|
||||
{
|
||||
return vertexIndex == other.vertexIndex && uvIndex == other.uvIndex;
|
||||
}
|
||||
};
|
||||
|
||||
struct ObjFaceVertexHash
|
||||
{
|
||||
size_t operator()(const ObjFaceVertex& value) const
|
||||
{
|
||||
size_t h1 = std::hash<int>{}(value.vertexIndex);
|
||||
size_t h2 = std::hash<int>{}(value.uvIndex);
|
||||
return h1 ^ (h2 + 0x9e3779b9 + (h1 << 6) + (h1 >> 2));
|
||||
}
|
||||
};
|
||||
|
||||
struct ObjMeshData
|
||||
{
|
||||
TSE::string name;
|
||||
std::vector<TSE::Vector3> vertecies;
|
||||
std::vector<TSE::ushort> indecies;
|
||||
std::vector<TSE::Vector2> uvs;
|
||||
std::unordered_map<ObjFaceVertex, TSE::ushort, ObjFaceVertexHash> vertexLookup;
|
||||
};
|
||||
|
||||
int ResolveObjIndex(int index, size_t count)
|
||||
{
|
||||
if(index > 0)
|
||||
{
|
||||
return index - 1;
|
||||
}
|
||||
if(index < 0)
|
||||
{
|
||||
return static_cast<int>(count) + index;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
ObjFaceVertex ParseFaceVertex(const std::string& token, size_t vertexCount, size_t uvCount)
|
||||
{
|
||||
ObjFaceVertex result;
|
||||
std::stringstream stream(token);
|
||||
std::string part;
|
||||
|
||||
if(std::getline(stream, part, '/') && !part.empty())
|
||||
{
|
||||
result.vertexIndex = ResolveObjIndex(std::stoi(part), vertexCount);
|
||||
}
|
||||
|
||||
if(std::getline(stream, part, '/') && !part.empty())
|
||||
{
|
||||
result.uvIndex = ResolveObjIndex(std::stoi(part), uvCount);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool AddObjFaceVertex(
|
||||
ObjMeshData& mesh,
|
||||
const ObjFaceVertex& faceVertex,
|
||||
const std::vector<TSE::Vector3>& sourceVertices,
|
||||
const std::vector<TSE::Vector2>& sourceUvs)
|
||||
{
|
||||
if(faceVertex.vertexIndex < 0 || faceVertex.vertexIndex >= static_cast<int>(sourceVertices.size()))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
auto existing = mesh.vertexLookup.find(faceVertex);
|
||||
if(existing != mesh.vertexLookup.end())
|
||||
{
|
||||
mesh.indecies.push_back(existing->second);
|
||||
return true;
|
||||
}
|
||||
|
||||
if(mesh.vertecies.size() > std::numeric_limits<TSE::ushort>::max())
|
||||
{
|
||||
TSE_WARNING("OBJ mesh has more vertices than ushort indices can address: " + mesh.name);
|
||||
return false;
|
||||
}
|
||||
|
||||
TSE::ushort newIndex = static_cast<TSE::ushort>(mesh.vertecies.size());
|
||||
mesh.vertecies.push_back(sourceVertices[faceVertex.vertexIndex]);
|
||||
|
||||
if(faceVertex.uvIndex >= 0 && faceVertex.uvIndex < static_cast<int>(sourceUvs.size()))
|
||||
{
|
||||
mesh.uvs.push_back(sourceUvs[faceVertex.uvIndex]);
|
||||
}
|
||||
else
|
||||
{
|
||||
mesh.uvs.emplace_back(0.0f, 0.0f);
|
||||
}
|
||||
|
||||
mesh.vertexLookup[faceVertex] = newIndex;
|
||||
mesh.indecies.push_back(newIndex);
|
||||
return true;
|
||||
}
|
||||
|
||||
TSE::string MeshGeometryHash(
|
||||
const std::vector<TSE::Vector3>& vertecies,
|
||||
const std::vector<TSE::ushort>& indecies,
|
||||
const std::vector<TSE::Vector2>& uvs)
|
||||
{
|
||||
std::ostringstream stream;
|
||||
stream << std::setprecision(std::numeric_limits<float>::max_digits10);
|
||||
|
||||
stream << "v:" << vertecies.size() << ";";
|
||||
for(const auto& vertex : vertecies)
|
||||
{
|
||||
stream << vertex.x << "," << vertex.y << "," << vertex.z << ";";
|
||||
}
|
||||
|
||||
stream << "i:" << indecies.size() << ";";
|
||||
for(TSE::ushort index : indecies)
|
||||
{
|
||||
stream << index << ";";
|
||||
}
|
||||
|
||||
stream << "uv:" << uvs.size() << ";";
|
||||
for(const auto& uv : uvs)
|
||||
{
|
||||
stream << uv.x << "," << uv.y << ";";
|
||||
}
|
||||
|
||||
return picosha2::hash256_hex_string(stream.str());
|
||||
}
|
||||
|
||||
TSE::string MeshGeometryHash(const TSE::Mesh& mesh)
|
||||
{
|
||||
return MeshGeometryHash(mesh.vertecies, mesh.indecies, mesh.uvs);
|
||||
}
|
||||
}
|
||||
|
||||
TSE::Mesh::Mesh()
|
||||
{
|
||||
id = GenerateRandomUUID();
|
||||
name = "";
|
||||
}
|
||||
|
||||
TSE::Mesh::Mesh(string _name, const std::vector<Vector3> &_vertecies, const std::vector<ushort> &_indecies, const std::vector<Vector2> &_uvs)
|
||||
{
|
||||
id = GenerateRandomUUID();
|
||||
name = _name;
|
||||
vertecies = std::move(_vertecies);
|
||||
indecies = std::move(_indecies);
|
||||
uvs = std::move(_uvs);
|
||||
}
|
||||
|
||||
size_t TSE::Mesh::IndeciesCount() const
|
||||
{
|
||||
return indecies.size();
|
||||
}
|
||||
|
||||
size_t TSE::Mesh::VerteciesCount() const
|
||||
{
|
||||
return vertecies.size();
|
||||
}
|
||||
|
||||
TSE::Mesh TSE::Mesh::GetCircleMesh(ushort segments)
|
||||
{
|
||||
std::vector<Vector3> verts;
|
||||
std::vector<ushort> indices;
|
||||
std::vector<Vector2> uvs;
|
||||
|
||||
verts.emplace_back(0.0f, 0.0f, 0.0f);
|
||||
uvs.emplace_back(0.5f, 0.5f);
|
||||
|
||||
float angleStep = 2.0f * TSE_PI / segments;
|
||||
|
||||
for (int i = 0; i <= segments; ++i) {
|
||||
float angle = i * angleStep;
|
||||
float x = std::cos(angle) * 0.5f;
|
||||
float y = std::sin(angle) * 0.5f;
|
||||
verts.emplace_back(x, y, 0);
|
||||
uvs.emplace_back(x + 0.5f, y + 0.5f);
|
||||
|
||||
if (i > 0) {
|
||||
indices.push_back(0);
|
||||
indices.push_back(i);
|
||||
indices.push_back(i + 1);
|
||||
}
|
||||
}
|
||||
|
||||
return Mesh("Circle", verts, indices, uvs);
|
||||
}
|
||||
|
||||
TSE::Mesh TSE::Mesh::GetQuadMesh()
|
||||
{
|
||||
std::vector<Vector3> verts = {
|
||||
Vector3(-0.5f, -0.5f, 0),
|
||||
Vector3( 0.5f, -0.5f, 0),
|
||||
Vector3( 0.5f, 0.5f, 0),
|
||||
Vector3(-0.5f, 0.5f, 0)
|
||||
};
|
||||
|
||||
std::vector<Vector2> uvs = {
|
||||
Vector2(0.0f, 0.0f),
|
||||
Vector2(1.0f, 0.0f),
|
||||
Vector2(1.0f, 1.0f),
|
||||
Vector2(0.0f, 1.0f)
|
||||
};
|
||||
|
||||
std::vector<unsigned short> indices = {
|
||||
0, 1, 2,
|
||||
2, 3, 0
|
||||
};
|
||||
|
||||
return Mesh("Quad", verts, indices, uvs);
|
||||
}
|
||||
|
||||
void TSE::Mesh::LoadObj(const string &path)
|
||||
{
|
||||
|
||||
std::ifstream objStream;
|
||||
OpenFileReading(objStream, path);
|
||||
if(!objStream.is_open())
|
||||
{
|
||||
TSE_WARNING("Could not open OBJ file: " + path);
|
||||
return;
|
||||
}
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
string fallbackName = fs::path(path).stem().string();
|
||||
std::vector<Vector3> sourceVertices;
|
||||
std::vector<Vector2> sourceUvs;
|
||||
std::vector<ObjMeshData> objects;
|
||||
ObjMeshData* currentObject = nullptr;
|
||||
|
||||
auto ensureObject = [&]() -> ObjMeshData&
|
||||
{
|
||||
if(currentObject == nullptr)
|
||||
{
|
||||
objects.emplace_back();
|
||||
objects.back().name = fallbackName;
|
||||
currentObject = &objects.back();
|
||||
}
|
||||
return *currentObject;
|
||||
};
|
||||
|
||||
std::string line;
|
||||
while(std::getline(objStream, line))
|
||||
{
|
||||
std::stringstream lineStream(line);
|
||||
std::string command;
|
||||
lineStream >> command;
|
||||
|
||||
if(command == "v")
|
||||
{
|
||||
float x = 0.0f;
|
||||
float y = 0.0f;
|
||||
float z = 0.0f;
|
||||
lineStream >> x >> y >> z;
|
||||
sourceVertices.emplace_back(x, y, z);
|
||||
}
|
||||
else if(command == "vt")
|
||||
{
|
||||
float u = 0.0f;
|
||||
float v = 0.0f;
|
||||
lineStream >> u >> v;
|
||||
sourceUvs.emplace_back(u, v);
|
||||
}
|
||||
else if(command == "o")
|
||||
{
|
||||
std::string objectName;
|
||||
std::getline(lineStream >> std::ws, objectName);
|
||||
objects.emplace_back();
|
||||
objects.back().name = objectName.empty() ? fallbackName : objectName;
|
||||
currentObject = &objects.back();
|
||||
}
|
||||
else if(command == "f")
|
||||
{
|
||||
ObjMeshData& mesh = ensureObject();
|
||||
std::vector<ObjFaceVertex> face;
|
||||
std::string token;
|
||||
while(lineStream >> token)
|
||||
{
|
||||
face.push_back(ParseFaceVertex(token, sourceVertices.size(), sourceUvs.size()));
|
||||
}
|
||||
|
||||
if(face.size() < 3)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
bool hasInvalidVertex = std::any_of(face.begin(), face.end(), [&sourceVertices](const ObjFaceVertex& faceVertex)
|
||||
{
|
||||
return faceVertex.vertexIndex < 0 || faceVertex.vertexIndex >= static_cast<int>(sourceVertices.size());
|
||||
});
|
||||
if(hasInvalidVertex)
|
||||
{
|
||||
TSE_WARNING("Skipping invalid OBJ face in: " + path);
|
||||
continue;
|
||||
}
|
||||
|
||||
for(size_t i = 1; i + 1 < face.size(); i++)
|
||||
{
|
||||
AddObjFaceVertex(mesh, face[0], sourceVertices, sourceUvs);
|
||||
AddObjFaceVertex(mesh, face[i], sourceVertices, sourceUvs);
|
||||
AddObjFaceVertex(mesh, face[i + 1], sourceVertices, sourceUvs);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
objects.erase(
|
||||
std::remove_if(objects.begin(), objects.end(), [](const ObjMeshData& object)
|
||||
{
|
||||
return object.vertecies.empty() || object.indecies.empty();
|
||||
}),
|
||||
objects.end());
|
||||
|
||||
if(objects.empty())
|
||||
{
|
||||
name = fallbackName;
|
||||
return;
|
||||
}
|
||||
|
||||
if(objects.size() == 1)
|
||||
{
|
||||
name = objects[0].name;
|
||||
vertecies = std::move(objects[0].vertecies);
|
||||
indecies = std::move(objects[0].indecies);
|
||||
uvs = std::move(objects[0].uvs);
|
||||
return;
|
||||
}
|
||||
|
||||
name = fallbackName;
|
||||
for(auto& object : objects)
|
||||
{
|
||||
if(vertecies.size() + object.vertecies.size() > std::numeric_limits<ushort>::max())
|
||||
{
|
||||
TSE_WARNING("Combined OBJ mesh has more vertices than ushort indices can address: " + path);
|
||||
continue;
|
||||
}
|
||||
|
||||
ushort indexOffset = static_cast<ushort>(vertecies.size());
|
||||
vertecies.insert(vertecies.end(), object.vertecies.begin(), object.vertecies.end());
|
||||
uvs.insert(uvs.end(), object.uvs.begin(), object.uvs.end());
|
||||
for(ushort index : object.indecies)
|
||||
{
|
||||
indecies.push_back(static_cast<ushort>(index + indexOffset));
|
||||
}
|
||||
|
||||
Mesh* submesh = new Mesh(object.name, object.vertecies, object.indecies, object.uvs);
|
||||
submesh->rawPath = path + "#" + object.name;
|
||||
submesh->hash = MeshGeometryHash(*submesh);
|
||||
submesh->IsVirtualAsset = true;
|
||||
derivedAssets.push_back(submesh);
|
||||
AssetLibrary::SetValue(submesh->id, submesh);
|
||||
}
|
||||
}
|
||||
|
||||
bool TSE::Mesh::LoadAsset(string &path)
|
||||
{
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
for(auto submesh : derivedAssets)
|
||||
{
|
||||
AssetLibrary::RemoveValue(submesh->id, submesh);
|
||||
delete(submesh);
|
||||
}
|
||||
derivedAssets.clear();
|
||||
|
||||
if(vertecies.size() != 0)
|
||||
{
|
||||
vertecies.clear();
|
||||
}
|
||||
if(indecies.size() != 0)
|
||||
{
|
||||
indecies.clear();
|
||||
}
|
||||
if(uvs.size() != 0)
|
||||
{
|
||||
uvs.clear();
|
||||
}
|
||||
rawPath = string(path);
|
||||
name = rawPath;
|
||||
string extension = fs::directory_entry(rawPath).path().extension().string();
|
||||
if(extension == ".obj")
|
||||
{
|
||||
LoadObj(rawPath);
|
||||
}
|
||||
|
||||
string metaPath = PathToMetaPath(rawPath);
|
||||
using json = nlohmann::ordered_json;
|
||||
|
||||
Hash();
|
||||
for(auto derivedAsset : derivedAssets)
|
||||
{
|
||||
Mesh* submesh = dynamic_cast<Mesh*>(derivedAsset);
|
||||
if(submesh != nullptr)
|
||||
{
|
||||
submesh->hash = MeshGeometryHash(*submesh);
|
||||
}
|
||||
}
|
||||
|
||||
if(FileExists(metaPath))
|
||||
{
|
||||
std::ifstream metaStream;
|
||||
OpenFileReading(metaStream, metaPath);
|
||||
json meta = json::parse(metaStream);
|
||||
if(meta["type"] != "metadata")
|
||||
{
|
||||
metaStream.close();
|
||||
TSE_WARNING("Rebuilding corrupted meta file (Texture): " + metaPath);
|
||||
SaveMeta();
|
||||
return true;
|
||||
}
|
||||
if(meta["version"] == 1)
|
||||
{
|
||||
if(meta["assetType"] == "mesh")
|
||||
{
|
||||
id = uuids::uuid::from_string((string)meta["id"]).value_or(id);
|
||||
|
||||
std::unordered_map<string, string> derivedAssetIds;
|
||||
if(meta.contains("derivedAssets") && meta["derivedAssets"].is_array())
|
||||
{
|
||||
for(const auto& derivedAssetMeta : meta["derivedAssets"])
|
||||
{
|
||||
if(derivedAssetMeta.contains("hash") && derivedAssetMeta.contains("id"))
|
||||
{
|
||||
derivedAssetIds[(string)derivedAssetMeta["hash"]] = (string)derivedAssetMeta["id"];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for(auto derivedAsset : derivedAssets)
|
||||
{
|
||||
Mesh* submesh = dynamic_cast<Mesh*>(derivedAsset);
|
||||
if(submesh == nullptr)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
submesh->hash = MeshGeometryHash(*submesh);
|
||||
auto idIt = derivedAssetIds.find(submesh->hash);
|
||||
if(idIt == derivedAssetIds.end())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
uuids::uuid oldId = submesh->id;
|
||||
submesh->id = uuids::uuid::from_string(idIt->second).value_or(submesh->id);
|
||||
if(oldId != submesh->id)
|
||||
{
|
||||
AssetLibrary::RemoveValue(oldId, submesh);
|
||||
AssetLibrary::SetValue(submesh->id, submesh);
|
||||
}
|
||||
}
|
||||
|
||||
metaStream.close();
|
||||
SaveMeta();
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
metaStream.close();
|
||||
TSE_WARNING("Meta file incompatible with asset type. Please delete for auto regeneration or fix maualy (Mesh): " + metaPath);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
metaStream.close();
|
||||
TSE_WARNING("Meta file incompatible. Please delete for auto regeneration or fix maualy (Mesh): " + metaPath);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
SaveMeta();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
void TSE::Mesh::SaveAsset()
|
||||
{
|
||||
if(IsVirtualAsset) return;
|
||||
Hash();
|
||||
SaveMeta();
|
||||
}
|
||||
|
||||
void TSE::Mesh::Hash()
|
||||
{
|
||||
std::ifstream ifs(rawPath, std::ios::binary);
|
||||
std::vector<unsigned char> s(picosha2::k_digest_size);
|
||||
picosha2::hash256(ifs, s.begin(), s.end());
|
||||
hash = picosha2::hash256_hex_string(s);
|
||||
ifs.close();
|
||||
}
|
||||
|
||||
TSE::string TSE::Mesh::assetType()
|
||||
{
|
||||
return "mesh";
|
||||
}
|
||||
|
||||
void TSE::Mesh::UnloadAsset()
|
||||
{
|
||||
for(auto submesh : derivedAssets)
|
||||
{
|
||||
AssetLibrary::RemoveValue(submesh->id, submesh);
|
||||
delete(submesh);
|
||||
}
|
||||
AssetLibrary::RemoveValue(id, static_cast<IAsset*>(this));
|
||||
delete(this);
|
||||
}
|
||||
|
||||
void TSE::Mesh::SaveMeta()
|
||||
{
|
||||
string metaPath = PathToMetaPath(rawPath);
|
||||
using json = nlohmann::ordered_json;
|
||||
|
||||
json meta;
|
||||
meta["type"] = "metadata";
|
||||
meta["assetType"] = "mesh";
|
||||
meta["version"] = 1;
|
||||
meta["source"] = rawPath; //todo: clamp to assets root
|
||||
meta["id"] = uuids::to_string(id);
|
||||
meta["hash"] = hash;
|
||||
meta["derivedAssets"] = json::array();
|
||||
|
||||
for(auto derivedAsset : derivedAssets)
|
||||
{
|
||||
Mesh* submesh = dynamic_cast<Mesh*>(derivedAsset);
|
||||
if(submesh == nullptr)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
submesh->hash = MeshGeometryHash(*submesh);
|
||||
|
||||
json derivedAssetMeta;
|
||||
derivedAssetMeta["name"] = submesh->name;
|
||||
derivedAssetMeta["id"] = uuids::to_string(submesh->id);
|
||||
derivedAssetMeta["hash"] = submesh->hash;
|
||||
meta["derivedAssets"].push_back(derivedAssetMeta);
|
||||
}
|
||||
|
||||
std::ofstream metaStream;
|
||||
CreateFileWriting(metaStream, metaPath);
|
||||
metaStream << meta.dump(4) << std::endl;
|
||||
metaStream.close();
|
||||
}
|
||||
@@ -4,10 +4,11 @@
|
||||
#include "Types.hpp"
|
||||
#include "Vector3.hpp"
|
||||
#include "Vector2.hpp"
|
||||
#include "interfaces/IAsset.hpp"
|
||||
|
||||
namespace TSE
|
||||
{
|
||||
class Mesh
|
||||
class Mesh : public IAsset
|
||||
{
|
||||
public:
|
||||
string name;
|
||||
@@ -41,5 +42,16 @@ namespace TSE
|
||||
/// @brief gives you a basic unit quad with (-0.5, -0.5) -> (0.5, 0.5)
|
||||
/// @return the resulting mesh
|
||||
static Mesh GetQuadMesh();
|
||||
|
||||
void LoadObj(const string& path);
|
||||
|
||||
bool LoadAsset(string& path) override;
|
||||
void SaveAsset() override;
|
||||
void Hash() override;
|
||||
string assetType() override;
|
||||
void UnloadAsset() override;
|
||||
|
||||
protected:
|
||||
void SaveMeta() override;
|
||||
};
|
||||
} // namespace TSE
|
||||
@@ -7,10 +7,16 @@
|
||||
#include "PathHelper.hpp"
|
||||
#include "utils/JsonExports.hpp"
|
||||
#include "IdGenerator.hpp"
|
||||
#include "AssetLibrary.hpp"
|
||||
|
||||
TSE::Texture::Texture(const string &path)
|
||||
{
|
||||
id = GenerateRandomUUID();
|
||||
LoadImageFromFile(path);
|
||||
}
|
||||
|
||||
bool TSE::Texture::LoadImageFromFile(const string &path)
|
||||
{
|
||||
FREE_IMAGE_FORMAT fif = FREE_IMAGE_FORMAT::FIF_UNKNOWN;
|
||||
bmp = nullptr;
|
||||
imagePtr = nullptr;
|
||||
@@ -24,7 +30,7 @@ TSE::Texture::Texture(const string &path)
|
||||
TSE_ERROR(msg);
|
||||
Bpp = 24;
|
||||
makeError(*this);
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
fif = FreeImage_GetFileType(name.c_str(), 0);
|
||||
@@ -35,12 +41,12 @@ TSE::Texture::Texture(const string &path)
|
||||
TSE_ERROR("Failed to load image. Unsupported Format.");
|
||||
Bpp = 24;
|
||||
makeError(*this);
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
if(FreeImage_FIFSupportsReading(fif))
|
||||
{
|
||||
bmp = FreeImage_Load(fif, path.c_str());
|
||||
if(FreeImage_GetBPP(bmp) != 32)
|
||||
bmp = FreeImage_Load(fif, name.c_str());
|
||||
if(bmp != nullptr && FreeImage_GetBPP(bmp) != 32)
|
||||
{
|
||||
auto tmpBmp = FreeImage_ConvertTo32Bits(bmp);
|
||||
FreeImage_Unload(bmp);
|
||||
@@ -52,7 +58,7 @@ TSE::Texture::Texture(const string &path)
|
||||
TSE_ERROR("Failed to load image. Bitmap was nullptr.");
|
||||
Bpp = 24;
|
||||
makeError(*this);
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
Bpp = FreeImage_GetBPP(bmp);
|
||||
imagePtr = FreeImage_GetBits(bmp);
|
||||
@@ -70,6 +76,7 @@ TSE::Texture::Texture(const string &path)
|
||||
|
||||
Size = Vector2(FreeImage_GetWidth(bmp), FreeImage_GetHeight(bmp));
|
||||
regist();
|
||||
return true;
|
||||
}
|
||||
|
||||
TSE::Texture::Texture(const int &width, const int &height, int bpp)
|
||||
@@ -476,7 +483,19 @@ void TSE::Texture::PlatformDestroy()
|
||||
|
||||
bool TSE::Texture::LoadAsset(string &path)
|
||||
{
|
||||
*this = Texture(path);
|
||||
if(bmp != nullptr)
|
||||
{
|
||||
FreeImage_Unload(bmp);
|
||||
bmp = nullptr;
|
||||
}
|
||||
if(TextureID != 0)
|
||||
{
|
||||
PlatformDestroy();
|
||||
TextureID = 0;
|
||||
}
|
||||
imagePtr = nullptr;
|
||||
LoadImageFromFile(path);
|
||||
|
||||
rawPath = string(path);
|
||||
UpdateImportSettings(rawPath, 0, {1,1});
|
||||
string metaPath = PathToMetaPath(rawPath);
|
||||
@@ -542,6 +561,7 @@ bool TSE::Texture::LoadAsset(string &path)
|
||||
|
||||
void TSE::Texture::SaveAsset()
|
||||
{
|
||||
if(IsVirtualAsset) return;
|
||||
Hash();
|
||||
SaveMeta();
|
||||
}
|
||||
@@ -564,6 +584,22 @@ void TSE::Texture::UpdateImportSettings(string name, byte mode, Vector2 count)
|
||||
settings.chanels = chanels;
|
||||
}
|
||||
|
||||
TSE::string TSE::Texture::assetType()
|
||||
{
|
||||
return "texture";
|
||||
}
|
||||
|
||||
void TSE::Texture::UnloadAsset()
|
||||
{
|
||||
for(auto submesh : derivedAssets)
|
||||
{
|
||||
AssetLibrary::RemoveValue(submesh->id, submesh);
|
||||
delete(submesh);
|
||||
}
|
||||
AssetLibrary::RemoveValue(id, static_cast<IAsset*>(this));
|
||||
delete(this);
|
||||
}
|
||||
|
||||
void TSE::Texture::SaveMeta()
|
||||
{
|
||||
string metaPath = PathToMetaPath(rawPath);
|
||||
|
||||
@@ -79,9 +79,14 @@ namespace TSE
|
||||
bool LoadAsset(string& path) override;
|
||||
void SaveAsset() override;
|
||||
void Hash() override;
|
||||
TextureImportSettings& GetImportSettings() { return settings; }
|
||||
const TextureImportSettings& GetImportSettings() const { return settings; }
|
||||
void UpdateImportSettings(string name, byte mode, Vector2 count);
|
||||
string assetType() override;
|
||||
void UnloadAsset() override;
|
||||
|
||||
protected:
|
||||
bool LoadImageFromFile(const string& path);
|
||||
void SaveMeta() override;
|
||||
};
|
||||
} // namespace TSE
|
||||
|
||||
@@ -14,12 +14,15 @@ namespace TSE
|
||||
string rawPath;
|
||||
string hash;
|
||||
std::vector<IAsset*> dependecies;
|
||||
std::vector<IAsset*> derivedAssets;
|
||||
bool IsVirtualAsset = false;
|
||||
virtual ~IAsset() = default;
|
||||
|
||||
virtual bool LoadAsset(string& path) = 0;
|
||||
virtual void SaveAsset() = 0;
|
||||
virtual void Hash() = 0;
|
||||
virtual string assetType() = 0;
|
||||
virtual void UnloadAsset() = 0;
|
||||
inline void Rescan()
|
||||
{
|
||||
string oldHash = string(hash);
|
||||
|
||||
@@ -20,6 +20,7 @@ TSE::EDITOR::EditorSubsystem::EditorSubsystem(ConsolView* cvp) : sv(nullptr), ed
|
||||
controller.AddGuiElement("Properties", &pv);
|
||||
controller.AddGuiElement("Debug", &dv);
|
||||
controller.AddGuiElement("Camera", &camv);
|
||||
controller.AddGuiElement("Assets", &av);
|
||||
|
||||
BehaviourScriptRegistry::RegisterBehaviourScript("Image", []() -> BehaviourScript* {return new Image();});
|
||||
BehaviourScriptRegistry::RegisterBehaviourScript("Image Animation", []() -> BehaviourScript* {return new ImageAnimation();});
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include "UI/windows/HirearchieView.hpp"
|
||||
#include "UI/windows/PropertiesView.hpp"
|
||||
#include "UI/windows/SceneView.hpp"
|
||||
#include "UI/windows/AssetsView.hpp"
|
||||
#include "interfaces/IRenderTexture.hpp"
|
||||
|
||||
namespace TSE::EDITOR
|
||||
@@ -22,6 +23,7 @@ namespace TSE::EDITOR
|
||||
CameraView camv;
|
||||
TSE::IRenderTexture* rt;
|
||||
SceneView sv;
|
||||
AssetsView av;
|
||||
Layer editorLayer;
|
||||
|
||||
EditorSubsystem(ConsolView* cvp);
|
||||
|
||||
@@ -1,12 +1,167 @@
|
||||
#include "ElementDrawer.hpp"
|
||||
#include "BehaviourScriptRegistry.hpp"
|
||||
#include "elements/ShaderRegistry.hpp"
|
||||
#include "elements/AssetLibrary.hpp"
|
||||
#include "BehaviourScripts/Camera.hpp"
|
||||
#include "windows/HirearchieView.hpp"
|
||||
#include "windows/PropertiesView.hpp"
|
||||
#include <algorithm>
|
||||
#include <any>
|
||||
#include <cctype>
|
||||
|
||||
namespace TSE::EDITOR
|
||||
{
|
||||
namespace
|
||||
{
|
||||
char assetPickerSearchBuffer[128] = "";
|
||||
|
||||
std::string ToLowerCopy(std::string value)
|
||||
{
|
||||
std::transform(value.begin(), value.end(), value.begin(), [](unsigned char c)
|
||||
{
|
||||
return static_cast<char>(std::tolower(c));
|
||||
});
|
||||
return value;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
std::vector<T*> GetAssetsOfType()
|
||||
{
|
||||
std::vector<T*> result;
|
||||
int count = AssetLibrary::GetValueCount();
|
||||
for(int i = 0; i < count; i++)
|
||||
{
|
||||
auto [ptr, type, id] = AssetLibrary::GetValueAt(i);
|
||||
if(type != typeid(T).name() && type != typeid(T*).name())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
result.push_back(std::any_cast<T*>(ptr));
|
||||
}
|
||||
catch(const std::bad_any_cast&)
|
||||
{
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
template<typename T, typename NameGetter>
|
||||
T* DrawAssetPickerPopup(const char* popupId, const char* searchHint, const char* emptyText, NameGetter getName)
|
||||
{
|
||||
T* selectedAsset = nullptr;
|
||||
|
||||
ImGui::SetNextWindowSize({360.0f, 420.0f}, ImGuiCond_Appearing);
|
||||
if(ImGui::BeginPopup(popupId))
|
||||
{
|
||||
ImGui::PushItemWidth(-1.0f);
|
||||
ImGui::InputTextWithHint("##AssetPickerSearch", searchHint, assetPickerSearchBuffer, IM_ARRAYSIZE(assetPickerSearchBuffer));
|
||||
ImGui::PopItemWidth();
|
||||
ImGui::Separator();
|
||||
|
||||
std::string filterText = ToLowerCopy(assetPickerSearchBuffer);
|
||||
std::vector<T*> assets = GetAssetsOfType<T>();
|
||||
|
||||
if(ImGui::BeginChild("##AssetPickerList", {0.0f, 0.0f}, ImGuiChildFlags_None, ImGuiWindowFlags_AlwaysVerticalScrollbar))
|
||||
{
|
||||
int shownCount = 0;
|
||||
for(T* asset : assets)
|
||||
{
|
||||
if(asset == nullptr)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
std::string assetName = getName(asset);
|
||||
if(!filterText.empty() && ToLowerCopy(assetName).find(filterText) == std::string::npos)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
ImGui::PushID(asset);
|
||||
if(ImGui::Selectable(assetName.c_str()))
|
||||
{
|
||||
selectedAsset = asset;
|
||||
ImGui::CloseCurrentPopup();
|
||||
}
|
||||
ImGui::PopID();
|
||||
shownCount++;
|
||||
}
|
||||
|
||||
if(shownCount == 0)
|
||||
{
|
||||
ImGui::TextDisabled("%s", emptyText);
|
||||
}
|
||||
}
|
||||
ImGui::EndChild();
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
|
||||
return selectedAsset;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
bool BeginAssetField(T** element, const char* popupId)
|
||||
{
|
||||
if(element == nullptr)
|
||||
{
|
||||
ImGui::TextDisabled("No Asset Field");
|
||||
return false;
|
||||
}
|
||||
|
||||
ImGui::PushID(element);
|
||||
|
||||
float buttonWidth = ImGui::GetFrameHeight();
|
||||
float fieldHeight = std::max(36.0f, ImGui::GetFrameHeightWithSpacing() + ImGui::GetStyle().WindowPadding.y * 2.0f);
|
||||
|
||||
ImGui::BeginChild("##AssetField", {0.0f, fieldHeight}, ImGuiChildFlags_Borders, ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse);
|
||||
if(ImGui::Button("+##SelectAsset", {buttonWidth, 0.0f}))
|
||||
{
|
||||
assetPickerSearchBuffer[0] = '\0';
|
||||
ImGui::OpenPopup(popupId);
|
||||
}
|
||||
|
||||
ImGui::SameLine();
|
||||
ImGui::BeginChild("##AssetFieldValue", {0.0f, 0.0f}, ImGuiChildFlags_None, ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse);
|
||||
return true;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void EndAssetField()
|
||||
{
|
||||
ImGui::EndChild();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void OpenAssetPropertiesOnDoubleClick(T* asset)
|
||||
{
|
||||
if(asset == nullptr)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if(ImGui::IsItemHovered() && ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left))
|
||||
{
|
||||
PropertiesView::SetInspectorElement(InspectableType::IAsset, static_cast<IAsset*>(asset));
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T, typename NameGetter>
|
||||
void AssetSelector(T** element, const char* popupId, const char* searchHint, const char* emptyText, NameGetter getName)
|
||||
{
|
||||
T* selectedAsset = DrawAssetPickerPopup<T>(popupId, searchHint, emptyText, getName);
|
||||
if(selectedAsset != nullptr)
|
||||
{
|
||||
*element = selectedAsset;
|
||||
}
|
||||
|
||||
ImGui::EndChild();
|
||||
ImGui::PopID();
|
||||
}
|
||||
}
|
||||
|
||||
#pragma region helper
|
||||
bool InputText(const char* label, std::string& str, size_t bufferSize = 256) {
|
||||
std::vector<char> buffer(bufferSize);
|
||||
@@ -222,15 +377,7 @@ namespace TSE::EDITOR
|
||||
void ElementDrawer::Draw(Renderable *element, const bool &debug)
|
||||
{
|
||||
ImGui::SeparatorText("Material");
|
||||
int height = 100;
|
||||
if(element->GetMaterial() == nullptr)
|
||||
{
|
||||
height = 35;
|
||||
}
|
||||
ImVec2 size(0, height);
|
||||
ImGui::BeginChild("MaterialViewer", size, ImGuiChildFlags_Borders);
|
||||
Draw(element->GetMaterial(), debug);
|
||||
ImGui::EndChild();
|
||||
DrawMaterialField(element->GetMaterialRef());
|
||||
}
|
||||
void ElementDrawer::Draw(MeshContainer *element, const bool &debug)
|
||||
{
|
||||
@@ -240,7 +387,7 @@ namespace TSE::EDITOR
|
||||
height = 35;
|
||||
}
|
||||
ImVec2 size(0, height);
|
||||
Draw(element->GetMesh(), debug, "Mesh", true);
|
||||
DrawMeshField(element->GetMeshRef());
|
||||
}
|
||||
void ElementDrawer::Draw(Image *element, const bool &debug)
|
||||
{
|
||||
@@ -444,7 +591,7 @@ namespace TSE::EDITOR
|
||||
Texture* value = element->GetValue<Texture*>(name);
|
||||
Draw(value, debug, name , true);
|
||||
}
|
||||
if (type == typeid(uint).name())
|
||||
else if (type == typeid(uint).name())
|
||||
{
|
||||
int value = element->GetValue<uint>(name);
|
||||
if(ImGui::InputInt(name.c_str(), &value))
|
||||
@@ -481,11 +628,6 @@ namespace TSE::EDITOR
|
||||
}
|
||||
void ElementDrawer::Draw(Mesh *element, const bool &debug, const std::string &label, const bool small)
|
||||
{
|
||||
if(element == nullptr)
|
||||
{
|
||||
ImGui::Text("No Mesh Set");
|
||||
return;
|
||||
}
|
||||
if(small) DrawMeshCompact(element, debug, label);
|
||||
else DrawMeshNormal(element, debug, label);
|
||||
}
|
||||
@@ -943,6 +1085,24 @@ namespace TSE::EDITOR
|
||||
ImGui::TextDisabled(("Chunk Count: " + std::to_string(element->GetChunkCount())).c_str());
|
||||
}
|
||||
}
|
||||
void ElementDrawer::Draw(IAsset *element, const bool &debug)
|
||||
{
|
||||
if (element->assetType() == "material")
|
||||
{
|
||||
Draw((Material*)element, debug);
|
||||
}
|
||||
else if (element->assetType() == "texture")
|
||||
{
|
||||
Draw((Texture*)element, debug);
|
||||
}
|
||||
else if (element->assetType() == "mesh")
|
||||
{
|
||||
Draw((Mesh*)element, debug);
|
||||
}
|
||||
|
||||
element->SaveAsset();
|
||||
}
|
||||
|
||||
void ElementDrawer::DrawAudioClipCompact(AudioClip *element, const bool &debug, const std::string &label)
|
||||
{
|
||||
float item_spacing = ImGui::GetStyle().ItemSpacing.x;
|
||||
@@ -1034,6 +1194,21 @@ namespace TSE::EDITOR
|
||||
}
|
||||
void ElementDrawer::DrawMeshCompact(Mesh *element, const bool &debug, const std::string &label)
|
||||
{
|
||||
|
||||
if(element == nullptr)
|
||||
{
|
||||
ImGui::Text("No Mesh Set");
|
||||
return;
|
||||
}
|
||||
ImGui::Text(element->name.c_str());
|
||||
}
|
||||
void ElementDrawer::DrawMeshNormal(Mesh *element, const bool &debug, const std::string &label)
|
||||
{
|
||||
if(element == nullptr)
|
||||
{
|
||||
ImGui::Text("No Mesh Set");
|
||||
return;
|
||||
}
|
||||
float item_spacing = ImGui::GetStyle().ItemSpacing.x;
|
||||
ImVec2 label_size = ImGui::CalcTextSize(label.c_str());
|
||||
|
||||
@@ -1060,54 +1235,6 @@ namespace TSE::EDITOR
|
||||
cursorCurrent.y += 2;
|
||||
ImGui::SetCursorPos(cursorCurrent);
|
||||
ImGui::TextUnformatted(label.c_str());
|
||||
}
|
||||
void ElementDrawer::DrawMeshNormal(Mesh *element, const bool &debug, const std::string &label)
|
||||
{
|
||||
ImGui::Text(("Name: " + element->name).c_str());
|
||||
if(debug)
|
||||
{
|
||||
//ImGui::TextDisabled(("ID: " + to_string(element->id)).c_str());
|
||||
}
|
||||
ImGui::Separator();
|
||||
ImGui::Text(("Vectex Count: " + std::to_string(element->vertecies.size())).c_str());
|
||||
ImGui::Text(("Index Count: " + std::to_string(element->indecies.size())).c_str());
|
||||
ImGui::Text(("UV Count: " + std::to_string(element->uvs.size())).c_str());
|
||||
ImGui::Indent(20.0f);
|
||||
if(ImGui::CollapsingHeader("Vertecies"))
|
||||
{
|
||||
ImGui::PushID("Verts");
|
||||
ImGui::BeginDisabled();
|
||||
for (int i = 0; i < element->vertecies.size(); i++)
|
||||
{
|
||||
ImGui::InputFloat3(std::to_string(i).c_str(), &element->vertecies[i].x);
|
||||
}
|
||||
ImGui::EndDisabled();
|
||||
ImGui::PopID();
|
||||
}
|
||||
if(ImGui::CollapsingHeader("Indecies"))
|
||||
{
|
||||
ImGui::PushID("Inds");
|
||||
ImGui::BeginDisabled();
|
||||
for (int i = 0; i < element->indecies.size(); i++)
|
||||
{
|
||||
int val = element->indecies[i];
|
||||
ImGui::InputInt(std::to_string(i).c_str(), &val);
|
||||
}
|
||||
ImGui::EndDisabled();
|
||||
ImGui::PopID();
|
||||
}
|
||||
if(ImGui::CollapsingHeader("UVs"))
|
||||
{
|
||||
ImGui::PushID("Uvs");
|
||||
ImGui::BeginDisabled();
|
||||
for (int i = 0; i < element->uvs.size(); i++)
|
||||
{
|
||||
ImGui::InputFloat2(std::to_string(i).c_str(), &element->uvs[i].x);
|
||||
}
|
||||
ImGui::EndDisabled();
|
||||
ImGui::PopID();
|
||||
}
|
||||
ImGui::Unindent(20.0f);
|
||||
|
||||
}
|
||||
void ElementDrawer::DrawSpriteCompact(Sprite *element, const bool &debug, const std::string &label)
|
||||
@@ -1215,6 +1342,98 @@ namespace TSE::EDITOR
|
||||
|
||||
ImVec2 texSize (available_width, (available_width) * ymultiplyer);
|
||||
ImGui::Image(element->GetTextureId(), texSize, {0,1}, {1,0});
|
||||
ImGui::Separator();
|
||||
ImGui::SeparatorText("Import Settings");
|
||||
|
||||
TextureImportSettings& settings = element->GetImportSettings();
|
||||
bool importSettingsChanged = false;
|
||||
|
||||
int importMode = static_cast<int>(settings.importMode);
|
||||
const char* importModes[] = { "Raw Texture", "Sprite", "Sprite Set", "Nine Tile" };
|
||||
if (ImGui::Combo("Import Mode", &importMode, importModes, IM_ARRAYSIZE(importModes)))
|
||||
{
|
||||
settings.importMode = static_cast<byte>(std::clamp(importMode, 0, IM_ARRAYSIZE(importModes) - 1));
|
||||
importSettingsChanged = true;
|
||||
}
|
||||
|
||||
int importCount[2] = {
|
||||
static_cast<int>(settings.importCount.x),
|
||||
static_cast<int>(settings.importCount.y)
|
||||
};
|
||||
if (ImGui::InputInt2("Import Count", importCount))
|
||||
{
|
||||
settings.importCount.x = static_cast<float>(std::max(1, importCount[0]));
|
||||
settings.importCount.y = static_cast<float>(std::max(1, importCount[1]));
|
||||
importSettingsChanged = true;
|
||||
}
|
||||
|
||||
int bpp = static_cast<int>(settings.bpp);
|
||||
if (ImGui::InputInt("BPP", &bpp))
|
||||
{
|
||||
settings.bpp = static_cast<uint>(std::max(0, bpp));
|
||||
importSettingsChanged = true;
|
||||
}
|
||||
|
||||
int chanels = static_cast<int>(settings.chanels);
|
||||
if (ImGui::InputInt("Chanels", &chanels))
|
||||
{
|
||||
settings.chanels = static_cast<byte>(std::clamp(chanels, 0, 255));
|
||||
importSettingsChanged = true;
|
||||
}
|
||||
|
||||
if (InputText("Import Name", settings.name))
|
||||
{
|
||||
importSettingsChanged = true;
|
||||
}
|
||||
|
||||
if (importSettingsChanged)
|
||||
{
|
||||
element->SaveAsset();
|
||||
}
|
||||
}
|
||||
|
||||
void ElementDrawer::DrawMaterialCompact(Material *element, const bool &debug, const std::string &label)
|
||||
{
|
||||
if(element == nullptr)
|
||||
{
|
||||
ImGui::Text("No Mesh Set");
|
||||
return;
|
||||
}
|
||||
ImGui::Text(element->GetName().c_str());
|
||||
}
|
||||
|
||||
void ElementDrawer::DrawMeshField(Mesh **element)
|
||||
{
|
||||
if(!BeginAssetField<Mesh>(element, "MeshAssetPicker"))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
DrawMeshCompact(*element, false, "");
|
||||
OpenAssetPropertiesOnDoubleClick(*element);
|
||||
EndAssetField<Mesh>();
|
||||
|
||||
AssetSelector<Mesh>(element, "MeshAssetPicker", "Search Mesh", "No meshes found", [](Mesh* mesh)
|
||||
{
|
||||
return mesh->name.empty() ? std::string("Unnamed Mesh") : mesh->name;
|
||||
});
|
||||
}
|
||||
|
||||
void ElementDrawer::DrawMaterialField(Material **element)
|
||||
{
|
||||
if(!BeginAssetField<Material>(element, "MaterialAssetPicker"))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
DrawMaterialCompact(*element, false, "");
|
||||
OpenAssetPropertiesOnDoubleClick(*element);
|
||||
EndAssetField<Material>();
|
||||
|
||||
AssetSelector<Material>(element, "MaterialAssetPicker", "Search Material", "No materials found", [](Material* mat)
|
||||
{
|
||||
return mat->GetName().empty() ? std::string("Unnamed Material") : mat->GetName();
|
||||
});
|
||||
}
|
||||
|
||||
} // namespace TSE::EDITOR
|
||||
|
||||
@@ -23,7 +23,8 @@ namespace TSE::EDITOR
|
||||
None,
|
||||
Transformable,
|
||||
Scene,
|
||||
Layer
|
||||
Layer,
|
||||
IAsset
|
||||
};
|
||||
|
||||
struct Inspectable
|
||||
@@ -54,6 +55,9 @@ namespace TSE::EDITOR
|
||||
case InspectableType::Layer:
|
||||
Draw(static_cast<Layer*>(element.ptr), debug);
|
||||
break;
|
||||
case InspectableType::IAsset:
|
||||
Draw(static_cast<IAsset*>(element.ptr), debug);
|
||||
break;
|
||||
default:
|
||||
TSE_WARNING("Draw not implemented for this type.");
|
||||
break;
|
||||
@@ -83,6 +87,7 @@ namespace TSE::EDITOR
|
||||
static void Draw(ParticleSystem* element, const bool& debug);
|
||||
static void Draw(TileMap* element, const bool& debug);
|
||||
static void Draw(OrdererSpriteSet* element, const bool& debug);
|
||||
static void Draw(IAsset* element, const bool& debug);
|
||||
|
||||
static void DrawAudioClipCompact(AudioClip* element, const bool& debug, const std::string& label);
|
||||
static void DrawAudioClipNormal(AudioClip* element, const bool& debug, const std::string& label);
|
||||
@@ -94,5 +99,9 @@ namespace TSE::EDITOR
|
||||
static void DrawSpriteNormal(Sprite* element, const bool& debug, const std::string& label);
|
||||
static void DrawTextureCompact(Texture* element, const bool& debug, const std::string& label);
|
||||
static void DrawTextureNormal(Texture* element, const bool& debug, const std::string& label);
|
||||
static void DrawMaterialCompact(Material* element, const bool& debug, const std::string& label);
|
||||
|
||||
static void DrawMeshField(Mesh** element);
|
||||
static void DrawMaterialField(Material** element);
|
||||
};
|
||||
} // namespace TSE::EDITOR
|
||||
|
||||
751
TSE_Editor/src/UI/windows/AssetsView.cpp
Normal file
751
TSE_Editor/src/UI/windows/AssetsView.cpp
Normal file
@@ -0,0 +1,751 @@
|
||||
#include "AssetsView.hpp"
|
||||
#include "elements/AssetLibrary.hpp"
|
||||
#include "PathHelper.hpp"
|
||||
#include "json.hpp"
|
||||
#include "uuid.h"
|
||||
#include "elements/Material.hpp"
|
||||
#include "elements/Texture.hpp"
|
||||
#include "PropertiesView.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdio>
|
||||
#include <filesystem>
|
||||
#include <vector>
|
||||
|
||||
namespace
|
||||
{
|
||||
std::vector<std::filesystem::directory_entry> GetDirectories(const std::filesystem::path& folderPath)
|
||||
{
|
||||
std::vector<std::filesystem::directory_entry> directories;
|
||||
std::error_code errorCode;
|
||||
|
||||
for(const auto& entry : std::filesystem::directory_iterator(folderPath, errorCode))
|
||||
{
|
||||
std::error_code entryErrorCode;
|
||||
if(entry.is_directory(entryErrorCode))
|
||||
directories.push_back(entry);
|
||||
}
|
||||
|
||||
std::sort(directories.begin(), directories.end(), [](const auto& a, const auto& b)
|
||||
{
|
||||
return a.path().filename().string() < b.path().filename().string();
|
||||
});
|
||||
|
||||
return directories;
|
||||
}
|
||||
|
||||
std::vector<std::filesystem::directory_entry> GetFiles(const std::filesystem::path& folderPath)
|
||||
{
|
||||
std::vector<std::filesystem::directory_entry> files;
|
||||
std::error_code errorCode;
|
||||
|
||||
for(const auto& entry : std::filesystem::directory_iterator(folderPath, errorCode))
|
||||
{
|
||||
std::error_code entryErrorCode;
|
||||
if(entry.is_regular_file(entryErrorCode) && entry.path().extension() != ".meta")
|
||||
files.push_back(entry);
|
||||
}
|
||||
|
||||
std::sort(files.begin(), files.end(), [](const auto& a, const auto& b)
|
||||
{
|
||||
return a.path().filename().string() < b.path().filename().string();
|
||||
});
|
||||
|
||||
return files;
|
||||
}
|
||||
|
||||
bool IsPathInFolder(const std::filesystem::path& path, const std::filesystem::path& folderPath)
|
||||
{
|
||||
if(path.empty() || folderPath.empty())
|
||||
return false;
|
||||
|
||||
const std::filesystem::path relativePath = path.lexically_relative(folderPath);
|
||||
if(relativePath.empty() || relativePath == ".")
|
||||
return true;
|
||||
|
||||
const auto firstPart = relativePath.begin();
|
||||
return firstPart != relativePath.end() && firstPart->string() != "..";
|
||||
}
|
||||
|
||||
bool IsSameOrChildPath(const std::filesystem::path& path, const std::filesystem::path& possibleParentPath)
|
||||
{
|
||||
return IsPathInFolder(path.lexically_normal(), possibleParentPath.lexically_normal());
|
||||
}
|
||||
|
||||
std::string FitTextToWidth(const std::string& text, float maxWidth)
|
||||
{
|
||||
if(ImGui::CalcTextSize(text.c_str()).x <= maxWidth)
|
||||
return text;
|
||||
|
||||
std::string result = text;
|
||||
while(result.size() > 3)
|
||||
{
|
||||
result.pop_back();
|
||||
const std::string truncated = result + "...";
|
||||
if(ImGui::CalcTextSize(truncated.c_str()).x <= maxWidth)
|
||||
return truncated;
|
||||
}
|
||||
|
||||
return "...";
|
||||
}
|
||||
}
|
||||
|
||||
TSE::EDITOR::AssetsView::AssetsView() : GuiWindow("Assets", ImGuiWindowFlags_NoScrollWithMouse | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoScrollbar)
|
||||
{
|
||||
}
|
||||
|
||||
void TSE::EDITOR::AssetsView::Define()
|
||||
{
|
||||
const float splitterWidth = 1.0f;
|
||||
const float minPanelWidth = 100.0f;
|
||||
const ImVec2 availableRegion = ImGui::GetContentRegionAvail();
|
||||
|
||||
if(availableRegion.x <= splitterWidth)
|
||||
return;
|
||||
|
||||
if(leftPanelWidth < 0.0f)
|
||||
leftPanelWidth = availableRegion.x / 3.0f;
|
||||
|
||||
float maxLeftPanelWidth = availableRegion.x - splitterWidth - minPanelWidth;
|
||||
if(maxLeftPanelWidth < minPanelWidth)
|
||||
maxLeftPanelWidth = (availableRegion.x - splitterWidth) * 0.5f;
|
||||
|
||||
if(leftPanelWidth < minPanelWidth)
|
||||
leftPanelWidth = minPanelWidth;
|
||||
if(leftPanelWidth > maxLeftPanelWidth)
|
||||
leftPanelWidth = maxLeftPanelWidth;
|
||||
|
||||
if(leftPanelWidth < 1.0f)
|
||||
leftPanelWidth = 1.0f;
|
||||
|
||||
auto clampLeftPanelWidth = [&]()
|
||||
{
|
||||
if(leftPanelWidth < minPanelWidth)
|
||||
leftPanelWidth = minPanelWidth;
|
||||
if(leftPanelWidth > maxLeftPanelWidth)
|
||||
leftPanelWidth = maxLeftPanelWidth;
|
||||
if(leftPanelWidth < 1.0f)
|
||||
leftPanelWidth = 1.0f;
|
||||
};
|
||||
|
||||
ImGuiWindowFlags childFlags = ImGuiWindowFlags_None;
|
||||
|
||||
if(ImGui::BeginChild("##AssetsTreeView", {leftPanelWidth, 0.0f}, ImGuiChildFlags_Borders, childFlags))
|
||||
{
|
||||
DefineTreeView();
|
||||
}
|
||||
ImGui::EndChild();
|
||||
|
||||
ImGui::SameLine(0.0f, 0.0f);
|
||||
|
||||
ImGui::InvisibleButton("##AssetsSplitter", {splitterWidth, availableRegion.y});
|
||||
if(ImGui::IsItemActive())
|
||||
{
|
||||
leftPanelWidth += ImGui::GetIO().MouseDelta.x;
|
||||
clampLeftPanelWidth();
|
||||
}
|
||||
|
||||
if(ImGui::IsItemHovered() || ImGui::IsItemActive())
|
||||
ImGui::SetMouseCursor(ImGuiMouseCursor_ResizeEW);
|
||||
|
||||
ImU32 splitterColor = ImGui::GetColorU32(ImGuiCol_Separator);
|
||||
if(ImGui::IsItemHovered())
|
||||
splitterColor = ImGui::GetColorU32(ImGuiCol_SeparatorHovered);
|
||||
if(ImGui::IsItemActive())
|
||||
splitterColor = ImGui::GetColorU32(ImGuiCol_SeparatorActive);
|
||||
|
||||
ImGui::GetWindowDrawList()->AddRectFilled(ImGui::GetItemRectMin(), ImGui::GetItemRectMax(), splitterColor);
|
||||
|
||||
ImGui::SameLine(0.0f, 0.0f);
|
||||
|
||||
if(ImGui::BeginChild("##AssetsItemView", {0.0f, 0.0f}, ImGuiChildFlags_Borders, childFlags))
|
||||
{
|
||||
DefineItemView();
|
||||
}
|
||||
ImGui::EndChild();
|
||||
}
|
||||
|
||||
void TSE::EDITOR::AssetsView::DefineTreeView()
|
||||
{
|
||||
const bool assetsOpen = ImGui::CollapsingHeader("Assets", ImGuiTreeNodeFlags_DefaultOpen);
|
||||
const std::filesystem::path assetPath = TSE::AssetLibrary::GetCurrentAssetPath();
|
||||
std::error_code errorCode;
|
||||
|
||||
if(assetPath.empty() || !std::filesystem::exists(assetPath, errorCode) || !std::filesystem::is_directory(assetPath, errorCode))
|
||||
{
|
||||
DefineFolderNamePopup();
|
||||
return;
|
||||
}
|
||||
|
||||
AcceptAssetDrop(assetPath);
|
||||
|
||||
if(assetsOpen)
|
||||
{
|
||||
DisplayDirectoryTree(assetPath);
|
||||
|
||||
ImVec2 remainingSpace = ImGui::GetContentRegionAvail();
|
||||
if(remainingSpace.y > 0.0f)
|
||||
{
|
||||
if(remainingSpace.x < 1.0f)
|
||||
remainingSpace.x = 1.0f;
|
||||
|
||||
ImGui::InvisibleButton("##AssetsRootDropArea", remainingSpace);
|
||||
AcceptAssetDrop(assetPath);
|
||||
if(ImGui::BeginPopupContextItem("##AssetsRootContext"))
|
||||
{
|
||||
DefineFolderBackgroundContextMenu(assetPath);
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DefineFolderNamePopup();
|
||||
}
|
||||
|
||||
void TSE::EDITOR::AssetsView::DefineItemView()
|
||||
{
|
||||
DefineItemViewToolbar();
|
||||
|
||||
const std::filesystem::path browserPath = GetCurrentBrowserPath();
|
||||
std::error_code errorCode;
|
||||
if(browserPath.empty() || !std::filesystem::exists(browserPath, errorCode) || !std::filesystem::is_directory(browserPath, errorCode))
|
||||
return;
|
||||
|
||||
if(ImGui::BeginChild("##AssetsItemScroll", {0.0f, 0.0f}, ImGuiChildFlags_None, ImGuiWindowFlags_AlwaysVerticalScrollbar))
|
||||
{
|
||||
if(itemViewMode == ItemViewMode::List)
|
||||
DefineItemListView(browserPath);
|
||||
else
|
||||
DefineItemTileView(browserPath);
|
||||
|
||||
ImVec2 remainingSpace = ImGui::GetContentRegionAvail();
|
||||
if(remainingSpace.y > 0.0f)
|
||||
{
|
||||
if(remainingSpace.x < 1.0f)
|
||||
remainingSpace.x = 1.0f;
|
||||
|
||||
ImGui::InvisibleButton("##AssetsItemRootDropArea", remainingSpace);
|
||||
AcceptAssetDrop(browserPath);
|
||||
if(ImGui::BeginPopupContextItem("##AssetsItemRootContext"))
|
||||
{
|
||||
DefineFolderBackgroundContextMenu(browserPath);
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
}
|
||||
|
||||
if(ImGui::BeginPopupContextWindow("##AssetsItemBackgroundContext", ImGuiPopupFlags_MouseButtonRight | ImGuiPopupFlags_NoOpenOverItems))
|
||||
{
|
||||
DefineFolderBackgroundContextMenu(browserPath);
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
}
|
||||
ImGui::EndChild();
|
||||
}
|
||||
|
||||
void TSE::EDITOR::AssetsView::DefineItemViewToolbar()
|
||||
{
|
||||
const float buttonWidth = 64.0f;
|
||||
const float totalWidth = buttonWidth * 2.0f + ImGui::GetStyle().ItemSpacing.x;
|
||||
const float startX = ImGui::GetContentRegionAvail().x - totalWidth;
|
||||
if(startX > 0.0f)
|
||||
ImGui::SetCursorPosX(ImGui::GetCursorPosX() + startX);
|
||||
|
||||
if(ImGui::Selectable("List", itemViewMode == ItemViewMode::List, 0, {buttonWidth, 0.0f}))
|
||||
itemViewMode = ItemViewMode::List;
|
||||
ImGui::SameLine();
|
||||
if(ImGui::Selectable("Tiles", itemViewMode == ItemViewMode::Tiles, 0, {buttonWidth, 0.0f}))
|
||||
itemViewMode = ItemViewMode::Tiles;
|
||||
}
|
||||
|
||||
void TSE::EDITOR::AssetsView::DefineItemListView(const std::filesystem::path& folderPath)
|
||||
{
|
||||
for(const auto& folderEntry : GetDirectories(folderPath))
|
||||
DefineFolderListItem(folderEntry);
|
||||
|
||||
for(const auto& fileEntry : GetFiles(folderPath))
|
||||
DefineFileListItem(fileEntry);
|
||||
}
|
||||
|
||||
void TSE::EDITOR::AssetsView::DefineItemTileView(const std::filesystem::path& folderPath)
|
||||
{
|
||||
const float tileSize = 96.0f;
|
||||
const float spacing = ImGui::GetStyle().ItemSpacing.x;
|
||||
const float availableWidth = ImGui::GetContentRegionAvail().x;
|
||||
int columns = static_cast<int>(availableWidth / (tileSize + spacing));
|
||||
if(columns < 1)
|
||||
columns = 1;
|
||||
|
||||
int index = 0;
|
||||
auto nextTile = [&]()
|
||||
{
|
||||
index++;
|
||||
if(index % columns != 0)
|
||||
ImGui::SameLine();
|
||||
};
|
||||
|
||||
for(const auto& folderEntry : GetDirectories(folderPath))
|
||||
{
|
||||
DefineFolderTileItem(folderEntry);
|
||||
nextTile();
|
||||
}
|
||||
|
||||
for(const auto& fileEntry : GetFiles(folderPath))
|
||||
{
|
||||
DefineFileTileItem(fileEntry);
|
||||
nextTile();
|
||||
}
|
||||
}
|
||||
|
||||
void TSE::EDITOR::AssetsView::DefineFolderListItem(const std::filesystem::directory_entry& folderEntry)
|
||||
{
|
||||
const std::filesystem::path folderPath = folderEntry.path();
|
||||
ImGui::PushID(folderPath.string().c_str());
|
||||
|
||||
ImGui::TextUnformatted("[D]");
|
||||
ImGui::SameLine();
|
||||
|
||||
const bool selected = currentSelectedPath == folderPath;
|
||||
ImGui::Selectable(folderPath.filename().string().c_str(), selected, 0, {ImGui::GetContentRegionAvail().x, 0.0f});
|
||||
if(ImGui::IsItemHovered() && ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left))
|
||||
currentSelectedPath = folderPath;
|
||||
|
||||
if(ImGui::BeginDragDropSource())
|
||||
{
|
||||
const std::string payloadPath = folderPath.string();
|
||||
ImGui::SetDragDropPayload("ASSET_FOLDER_PATH", payloadPath.c_str(), payloadPath.size() + 1);
|
||||
ImGui::Text("%s", folderPath.filename().string().c_str());
|
||||
ImGui::EndDragDropSource();
|
||||
}
|
||||
|
||||
AcceptAssetDrop(folderPath);
|
||||
|
||||
if(ImGui::BeginPopupContextItem())
|
||||
{
|
||||
DefineFolderContextMenu(folderPath);
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
|
||||
ImGui::PopID();
|
||||
}
|
||||
|
||||
void TSE::EDITOR::AssetsView::DefineFileListItem(const std::filesystem::directory_entry& fileEntry)
|
||||
{
|
||||
const std::filesystem::path filePath = fileEntry.path();
|
||||
ImGui::PushID(filePath.string().c_str());
|
||||
|
||||
ImGui::TextUnformatted("[F]");
|
||||
ImGui::SameLine();
|
||||
|
||||
ImGui::Selectable(filePath.filename().string().c_str(), false, 0, {ImGui::GetContentRegionAvail().x, 0.0f});
|
||||
if(ImGui::IsItemClicked(ImGuiMouseButton_Left))
|
||||
OpenFileProperties(filePath.string());
|
||||
|
||||
if(ImGui::BeginDragDropSource())
|
||||
{
|
||||
const std::string payloadPath = filePath.string();
|
||||
ImGui::SetDragDropPayload("ASSET_FILE_PATH", payloadPath.c_str(), payloadPath.size() + 1);
|
||||
ImGui::Text("%s", filePath.filename().string().c_str());
|
||||
ImGui::EndDragDropSource();
|
||||
}
|
||||
|
||||
if(ImGui::BeginPopupContextItem())
|
||||
{
|
||||
DefineFileContextMenu(filePath);
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
|
||||
ImGui::PopID();
|
||||
}
|
||||
|
||||
void TSE::EDITOR::AssetsView::DefineFolderTileItem(const std::filesystem::directory_entry& folderEntry)
|
||||
{
|
||||
const std::filesystem::path folderPath = folderEntry.path();
|
||||
const float tileSize = 96.0f;
|
||||
ImGui::PushID(folderPath.string().c_str());
|
||||
|
||||
const bool selected = currentSelectedPath == folderPath;
|
||||
ImGui::Selectable("##FolderTile", selected, 0, {tileSize, tileSize});
|
||||
if(ImGui::IsItemHovered() && ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left))
|
||||
currentSelectedPath = folderPath;
|
||||
|
||||
ImVec2 itemMin = ImGui::GetItemRectMin();
|
||||
ImVec2 itemMax = ImGui::GetItemRectMax();
|
||||
ImDrawList* drawList = ImGui::GetWindowDrawList();
|
||||
const std::string icon = "[D]";
|
||||
const std::string name = FitTextToWidth(folderPath.filename().string(), tileSize - 12.0f);
|
||||
const ImVec2 iconSize = ImGui::CalcTextSize(icon.c_str());
|
||||
const ImVec2 nameSize = ImGui::CalcTextSize(name.c_str());
|
||||
drawList->AddText({itemMin.x + (tileSize - iconSize.x) * 0.5f, itemMin.y + 22.0f}, ImGui::GetColorU32(ImGuiCol_Text), icon.c_str());
|
||||
drawList->AddText({itemMin.x + (tileSize - nameSize.x) * 0.5f, itemMax.y - 28.0f}, ImGui::GetColorU32(ImGuiCol_Text), name.c_str());
|
||||
|
||||
if(ImGui::BeginDragDropSource())
|
||||
{
|
||||
const std::string payloadPath = folderPath.string();
|
||||
ImGui::SetDragDropPayload("ASSET_FOLDER_PATH", payloadPath.c_str(), payloadPath.size() + 1);
|
||||
ImGui::Text("%s", folderPath.filename().string().c_str());
|
||||
ImGui::EndDragDropSource();
|
||||
}
|
||||
|
||||
AcceptAssetDrop(folderPath);
|
||||
|
||||
if(ImGui::BeginPopupContextItem())
|
||||
{
|
||||
DefineFolderContextMenu(folderPath);
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
|
||||
ImGui::PopID();
|
||||
}
|
||||
|
||||
void TSE::EDITOR::AssetsView::DefineFileTileItem(const std::filesystem::directory_entry& fileEntry)
|
||||
{
|
||||
const std::filesystem::path filePath = fileEntry.path();
|
||||
const float tileSize = 96.0f;
|
||||
ImGui::PushID(filePath.string().c_str());
|
||||
|
||||
ImGui::Selectable("##FileTile", false, 0, {tileSize, tileSize});
|
||||
if(ImGui::IsItemClicked(ImGuiMouseButton_Left))
|
||||
OpenFileProperties(filePath.string());
|
||||
|
||||
ImVec2 itemMin = ImGui::GetItemRectMin();
|
||||
ImVec2 itemMax = ImGui::GetItemRectMax();
|
||||
ImDrawList* drawList = ImGui::GetWindowDrawList();
|
||||
const std::string icon = "[F]";
|
||||
const std::string name = FitTextToWidth(filePath.filename().string(), tileSize - 12.0f);
|
||||
const ImVec2 iconSize = ImGui::CalcTextSize(icon.c_str());
|
||||
const ImVec2 nameSize = ImGui::CalcTextSize(name.c_str());
|
||||
drawList->AddText({itemMin.x + (tileSize - iconSize.x) * 0.5f, itemMin.y + 22.0f}, ImGui::GetColorU32(ImGuiCol_Text), icon.c_str());
|
||||
drawList->AddText({itemMin.x + (tileSize - nameSize.x) * 0.5f, itemMax.y - 28.0f}, ImGui::GetColorU32(ImGuiCol_Text), name.c_str());
|
||||
|
||||
if(ImGui::BeginDragDropSource())
|
||||
{
|
||||
const std::string payloadPath = filePath.string();
|
||||
ImGui::SetDragDropPayload("ASSET_FILE_PATH", payloadPath.c_str(), payloadPath.size() + 1);
|
||||
ImGui::Text("%s", filePath.filename().string().c_str());
|
||||
ImGui::EndDragDropSource();
|
||||
}
|
||||
|
||||
if(ImGui::BeginPopupContextItem())
|
||||
{
|
||||
DefineFileContextMenu(filePath);
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
|
||||
ImGui::PopID();
|
||||
}
|
||||
|
||||
void TSE::EDITOR::AssetsView::DisplayDirectoryTree(const std::filesystem::path& folderPath)
|
||||
{
|
||||
std::vector<std::filesystem::directory_entry> childDirectories = GetDirectories(folderPath);
|
||||
for(const auto& childDirectory : childDirectories)
|
||||
{
|
||||
const std::filesystem::path childPath = childDirectory.path();
|
||||
const std::vector<std::filesystem::directory_entry> grandChildDirectories = GetDirectories(childPath);
|
||||
|
||||
ImGui::PushID(childPath.string().c_str());
|
||||
|
||||
ImGuiTreeNodeFlags flags = ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_OpenOnDoubleClick | ImGuiTreeNodeFlags_SpanFullWidth;
|
||||
if(grandChildDirectories.empty())
|
||||
flags |= ImGuiTreeNodeFlags_Leaf | ImGuiTreeNodeFlags_NoTreePushOnOpen;
|
||||
if(currentSelectedPath == childPath)
|
||||
flags |= ImGuiTreeNodeFlags_Selected;
|
||||
|
||||
const bool open = ImGui::TreeNodeEx(childPath.filename().string().c_str(), flags);
|
||||
if(ImGui::IsItemClicked(ImGuiMouseButton_Left) && !ImGui::IsItemToggledOpen())
|
||||
currentSelectedPath = childPath;
|
||||
|
||||
if(ImGui::BeginDragDropSource())
|
||||
{
|
||||
const std::string payloadPath = childPath.string();
|
||||
ImGui::SetDragDropPayload("ASSET_FOLDER_PATH", payloadPath.c_str(), payloadPath.size() + 1);
|
||||
ImGui::Text("%s", childPath.filename().string().c_str());
|
||||
ImGui::EndDragDropSource();
|
||||
}
|
||||
|
||||
AcceptAssetDrop(childPath);
|
||||
|
||||
bool folderDeleted = false;
|
||||
if(ImGui::BeginPopupContextItem())
|
||||
{
|
||||
folderDeleted = DefineFolderContextMenu(childPath);
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
|
||||
if(open && !grandChildDirectories.empty())
|
||||
{
|
||||
if(!folderDeleted)
|
||||
DisplayDirectoryTree(childPath);
|
||||
ImGui::TreePop();
|
||||
}
|
||||
|
||||
ImGui::PopID();
|
||||
}
|
||||
}
|
||||
|
||||
bool TSE::EDITOR::AssetsView::DefineFolderContextMenu(const std::filesystem::path& folderPath)
|
||||
{
|
||||
if(ImGui::MenuItem("Rename"))
|
||||
RequestFolderNamePopup(FolderPopupPurpose::Rename, folderPath);
|
||||
|
||||
bool folderDeleted = false;
|
||||
if(ImGui::MenuItem("Delete"))
|
||||
{
|
||||
DeleteFolder(folderPath);
|
||||
folderDeleted = true;
|
||||
}
|
||||
|
||||
if(ImGui::MenuItem("New Folder"))
|
||||
RequestFolderNamePopup(FolderPopupPurpose::NewFolder, folderPath);
|
||||
|
||||
return folderDeleted;
|
||||
}
|
||||
|
||||
void TSE::EDITOR::AssetsView::DefineFolderBackgroundContextMenu(const std::filesystem::path& folderPath)
|
||||
{
|
||||
if(ImGui::MenuItem("New Folder"))
|
||||
RequestFolderNamePopup(FolderPopupPurpose::NewFolder, folderPath);
|
||||
}
|
||||
|
||||
bool TSE::EDITOR::AssetsView::DefineFileContextMenu(const std::filesystem::path& filePath)
|
||||
{
|
||||
if(ImGui::MenuItem("Rename"))
|
||||
RequestFolderNamePopup(FolderPopupPurpose::Rename, filePath);
|
||||
|
||||
bool fileDeleted = false;
|
||||
if(ImGui::MenuItem("Delete"))
|
||||
{
|
||||
DeleteFile(filePath);
|
||||
fileDeleted = true;
|
||||
}
|
||||
|
||||
return fileDeleted;
|
||||
}
|
||||
|
||||
void TSE::EDITOR::AssetsView::AcceptAssetDrop(const std::filesystem::path& targetFolderPath)
|
||||
{
|
||||
if(ImGui::BeginDragDropTarget())
|
||||
{
|
||||
const ImGuiPayload* folderPayload = ImGui::AcceptDragDropPayload("ASSET_FOLDER_PATH");
|
||||
if(folderPayload != nullptr && folderPayload->Data != nullptr)
|
||||
{
|
||||
const char* payloadPath = static_cast<const char*>(folderPayload->Data);
|
||||
MoveFolder(std::filesystem::path(payloadPath), targetFolderPath);
|
||||
}
|
||||
|
||||
const ImGuiPayload* filePayload = ImGui::AcceptDragDropPayload("ASSET_FILE_PATH");
|
||||
if(filePayload != nullptr && filePayload->Data != nullptr)
|
||||
{
|
||||
const char* payloadPath = static_cast<const char*>(filePayload->Data);
|
||||
MoveFile(std::filesystem::path(payloadPath), targetFolderPath);
|
||||
}
|
||||
|
||||
ImGui::EndDragDropTarget();
|
||||
}
|
||||
}
|
||||
|
||||
void TSE::EDITOR::AssetsView::MoveFolder(const std::filesystem::path& sourceFolderPath, const std::filesystem::path& targetFolderPath)
|
||||
{
|
||||
const std::filesystem::path sourcePath = sourceFolderPath.lexically_normal();
|
||||
const std::filesystem::path targetPath = targetFolderPath.lexically_normal();
|
||||
|
||||
if(sourcePath.empty() || targetPath.empty())
|
||||
return;
|
||||
if(sourcePath.parent_path() == targetPath)
|
||||
return;
|
||||
if(IsSameOrChildPath(targetPath, sourcePath))
|
||||
return;
|
||||
|
||||
const std::filesystem::path newFolderPath = targetPath / sourcePath.filename();
|
||||
std::error_code errorCode;
|
||||
if(std::filesystem::exists(newFolderPath, errorCode))
|
||||
return;
|
||||
|
||||
std::filesystem::rename(sourcePath, newFolderPath, errorCode);
|
||||
if(errorCode)
|
||||
return;
|
||||
|
||||
if(currentSelectedPath == sourcePath)
|
||||
currentSelectedPath = newFolderPath;
|
||||
else if(IsSameOrChildPath(currentSelectedPath, sourcePath))
|
||||
currentSelectedPath = newFolderPath / currentSelectedPath.lexically_relative(sourcePath);
|
||||
|
||||
TSE::AssetLibrary::RescanAssets();
|
||||
}
|
||||
|
||||
void TSE::EDITOR::AssetsView::MoveFile(const std::filesystem::path& sourceFilePath, const std::filesystem::path& targetFolderPath)
|
||||
{
|
||||
const std::filesystem::path sourcePath = sourceFilePath.lexically_normal();
|
||||
const std::filesystem::path targetPath = targetFolderPath.lexically_normal();
|
||||
|
||||
if(sourcePath.empty() || targetPath.empty())
|
||||
return;
|
||||
if(sourcePath.parent_path() == targetPath)
|
||||
return;
|
||||
|
||||
const std::filesystem::path newFilePath = targetPath / sourcePath.filename();
|
||||
std::error_code errorCode;
|
||||
if(std::filesystem::exists(newFilePath, errorCode))
|
||||
return;
|
||||
|
||||
std::filesystem::rename(sourcePath, newFilePath, errorCode);
|
||||
if(!errorCode)
|
||||
TSE::AssetLibrary::RescanAssets();
|
||||
}
|
||||
|
||||
void TSE::EDITOR::AssetsView::RequestFolderNamePopup(FolderPopupPurpose purpose, const std::filesystem::path& folderPath)
|
||||
{
|
||||
folderPopupPurpose = purpose;
|
||||
pendingFolderPath = folderPath;
|
||||
openFolderNamePopup = true;
|
||||
focusFolderNameInput = true;
|
||||
|
||||
const std::string defaultName = purpose == FolderPopupPurpose::Rename ? folderPath.filename().string() : "";
|
||||
std::snprintf(folderNameBuffer, sizeof(folderNameBuffer), "%s", defaultName.c_str());
|
||||
}
|
||||
|
||||
void TSE::EDITOR::AssetsView::DefineFolderNamePopup()
|
||||
{
|
||||
if(openFolderNamePopup)
|
||||
{
|
||||
openFolderNamePopup = false;
|
||||
ImGui::OpenPopup("Folder Name");
|
||||
}
|
||||
|
||||
if(ImGui::BeginPopupModal("Folder Name", nullptr, ImGuiWindowFlags_AlwaysAutoResize))
|
||||
{
|
||||
ImGui::Text("Name:");
|
||||
if(focusFolderNameInput)
|
||||
{
|
||||
ImGui::SetKeyboardFocusHere();
|
||||
focusFolderNameInput = false;
|
||||
}
|
||||
ImGui::InputText("##FolderName", folderNameBuffer, sizeof(folderNameBuffer));
|
||||
|
||||
const bool inputValid = folderNameBuffer[0] != '\0';
|
||||
if(!inputValid)
|
||||
ImGui::TextColored(ImVec4(1, 0, 0, 1), "Not Valid.");
|
||||
|
||||
if(!inputValid)
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_Alpha, ImGui::GetStyle().Alpha * 0.5f);
|
||||
|
||||
if(ImGui::Button("OK", ImVec2(120, 0)) && inputValid)
|
||||
{
|
||||
ConfirmFolderNamePopup();
|
||||
ImGui::CloseCurrentPopup();
|
||||
}
|
||||
|
||||
if(!inputValid)
|
||||
ImGui::PopStyleVar();
|
||||
|
||||
ImGui::SameLine();
|
||||
if(ImGui::Button("Cancel", ImVec2(120, 0)))
|
||||
{
|
||||
folderPopupPurpose = FolderPopupPurpose::None;
|
||||
ImGui::CloseCurrentPopup();
|
||||
}
|
||||
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
}
|
||||
|
||||
void TSE::EDITOR::AssetsView::ConfirmFolderNamePopup()
|
||||
{
|
||||
const std::string folderName = folderNameBuffer;
|
||||
if(folderName.empty())
|
||||
return;
|
||||
|
||||
std::error_code errorCode;
|
||||
if(folderPopupPurpose == FolderPopupPurpose::Rename)
|
||||
{
|
||||
const std::filesystem::path newFolderPath = pendingFolderPath.parent_path() / folderName;
|
||||
if(newFolderPath != pendingFolderPath && !std::filesystem::exists(newFolderPath, errorCode))
|
||||
{
|
||||
const bool selectedPathIsInRenamedFolder = IsSameOrChildPath(currentSelectedPath, pendingFolderPath);
|
||||
const std::filesystem::path selectedRelativePath = selectedPathIsInRenamedFolder ? currentSelectedPath.lexically_relative(pendingFolderPath) : std::filesystem::path();
|
||||
|
||||
std::filesystem::rename(pendingFolderPath, newFolderPath, errorCode);
|
||||
if(!errorCode)
|
||||
{
|
||||
if(selectedPathIsInRenamedFolder)
|
||||
currentSelectedPath = newFolderPath / selectedRelativePath;
|
||||
TSE::AssetLibrary::RescanAssets();
|
||||
}
|
||||
}
|
||||
}
|
||||
else if(folderPopupPurpose == FolderPopupPurpose::NewFolder)
|
||||
{
|
||||
const std::filesystem::path newFolderPath = pendingFolderPath / folderName;
|
||||
if(!std::filesystem::exists(newFolderPath, errorCode))
|
||||
{
|
||||
std::filesystem::create_directory(newFolderPath, errorCode);
|
||||
if(!errorCode)
|
||||
{
|
||||
currentSelectedPath = newFolderPath;
|
||||
TSE::AssetLibrary::RescanAssets();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
folderPopupPurpose = FolderPopupPurpose::None;
|
||||
}
|
||||
|
||||
void TSE::EDITOR::AssetsView::DeleteFolder(const std::filesystem::path& folderPath)
|
||||
{
|
||||
std::error_code errorCode;
|
||||
std::filesystem::remove_all(folderPath, errorCode);
|
||||
|
||||
if(!errorCode && IsPathInFolder(currentSelectedPath, folderPath))
|
||||
currentSelectedPath.clear();
|
||||
if(!errorCode)
|
||||
TSE::AssetLibrary::RescanAssets();
|
||||
}
|
||||
|
||||
void TSE::EDITOR::AssetsView::DeleteFile(const std::filesystem::path& filePath)
|
||||
{
|
||||
std::error_code errorCode;
|
||||
std::filesystem::remove(filePath, errorCode);
|
||||
|
||||
if(!errorCode)
|
||||
TSE::AssetLibrary::RescanAssets();
|
||||
}
|
||||
|
||||
std::filesystem::path TSE::EDITOR::AssetsView::GetCurrentBrowserPath() const
|
||||
{
|
||||
std::error_code errorCode;
|
||||
if(!currentSelectedPath.empty() && std::filesystem::exists(currentSelectedPath, errorCode) && std::filesystem::is_directory(currentSelectedPath, errorCode))
|
||||
return currentSelectedPath;
|
||||
|
||||
return TSE::AssetLibrary::GetCurrentAssetPath();
|
||||
}
|
||||
|
||||
void TSE::EDITOR::AssetsView::OpenFileProperties(string path)
|
||||
{
|
||||
string metapath = path + ".meta";
|
||||
if(FileExists(metapath))
|
||||
{
|
||||
using json = nlohmann::ordered_json;
|
||||
std::ifstream metaStream;
|
||||
OpenFileReading(metaStream, metapath);
|
||||
json meta = json::parse(metaStream);
|
||||
|
||||
uuids::uuid id = uuids::uuid::from_string((string)meta["id"]).value_or(id);
|
||||
|
||||
if(!AssetLibrary::HasValue(id)) return;
|
||||
|
||||
IAsset* assetPtr = nullptr;
|
||||
|
||||
if(meta["assetType"] == "material")
|
||||
{
|
||||
assetPtr = static_cast<IAsset*>(AssetLibrary::GetValue<Material*>(id));
|
||||
}
|
||||
else if(meta["assetType"] == "texture")
|
||||
{
|
||||
assetPtr = static_cast<IAsset*>(AssetLibrary::GetValue<Texture*>(id));
|
||||
}
|
||||
|
||||
if(assetPtr != nullptr)
|
||||
{
|
||||
PropertiesView::SetInspectorElement(InspectableType::IAsset, assetPtr);
|
||||
}
|
||||
}
|
||||
}
|
||||
62
TSE_Editor/src/UI/windows/AssetsView.hpp
Normal file
62
TSE_Editor/src/UI/windows/AssetsView.hpp
Normal file
@@ -0,0 +1,62 @@
|
||||
#pragma once
|
||||
|
||||
#include "UI/base/GuiWindow.h"
|
||||
|
||||
#include <filesystem>
|
||||
|
||||
namespace TSE::EDITOR
|
||||
{
|
||||
class AssetsView : public GuiWindow
|
||||
{
|
||||
private:
|
||||
enum class FolderPopupPurpose
|
||||
{
|
||||
None,
|
||||
Rename,
|
||||
NewFolder
|
||||
};
|
||||
|
||||
enum class ItemViewMode
|
||||
{
|
||||
List,
|
||||
Tiles
|
||||
};
|
||||
|
||||
float leftPanelWidth = -1.0f;
|
||||
ItemViewMode itemViewMode = ItemViewMode::List;
|
||||
std::filesystem::path currentSelectedPath;
|
||||
std::filesystem::path pendingFolderPath;
|
||||
FolderPopupPurpose folderPopupPurpose = FolderPopupPurpose::None;
|
||||
bool openFolderNamePopup = false;
|
||||
bool focusFolderNameInput = false;
|
||||
char folderNameBuffer[256] = "";
|
||||
|
||||
void DefineTreeView();
|
||||
void DefineItemView();
|
||||
void DefineItemViewToolbar();
|
||||
void DefineItemListView(const std::filesystem::path& folderPath);
|
||||
void DefineItemTileView(const std::filesystem::path& folderPath);
|
||||
void DefineFolderListItem(const std::filesystem::directory_entry& folderEntry);
|
||||
void DefineFileListItem(const std::filesystem::directory_entry& fileEntry);
|
||||
void DefineFolderTileItem(const std::filesystem::directory_entry& folderEntry);
|
||||
void DefineFileTileItem(const std::filesystem::directory_entry& fileEntry);
|
||||
void DisplayDirectoryTree(const std::filesystem::path& folderPath);
|
||||
bool DefineFolderContextMenu(const std::filesystem::path& folderPath);
|
||||
void DefineFolderBackgroundContextMenu(const std::filesystem::path& folderPath);
|
||||
bool DefineFileContextMenu(const std::filesystem::path& filePath);
|
||||
void AcceptAssetDrop(const std::filesystem::path& targetFolderPath);
|
||||
void MoveFolder(const std::filesystem::path& sourceFolderPath, const std::filesystem::path& targetFolderPath);
|
||||
void MoveFile(const std::filesystem::path& sourceFilePath, const std::filesystem::path& targetFolderPath);
|
||||
void RequestFolderNamePopup(FolderPopupPurpose purpose, const std::filesystem::path& folderPath);
|
||||
void DefineFolderNamePopup();
|
||||
void ConfirmFolderNamePopup();
|
||||
void DeleteFolder(const std::filesystem::path& folderPath);
|
||||
void DeleteFile(const std::filesystem::path& filePath);
|
||||
std::filesystem::path GetCurrentBrowserPath() const;
|
||||
void OpenFileProperties(string path);
|
||||
|
||||
public:
|
||||
AssetsView();
|
||||
void Define() override;
|
||||
};
|
||||
} // namespace TSE::EDITOR
|
||||
@@ -1,78 +0,0 @@
|
||||
#include "Mesh.hpp"
|
||||
#include "MathF.hpp"
|
||||
#include <cmath>
|
||||
|
||||
TSE::Mesh::Mesh()
|
||||
{
|
||||
name = "";
|
||||
}
|
||||
|
||||
TSE::Mesh::Mesh(string _name, const std::vector<Vector3> &_vertecies, const std::vector<ushort> &_indecies, const std::vector<Vector2> &_uvs)
|
||||
{
|
||||
name = _name;
|
||||
vertecies = std::move(_vertecies);
|
||||
indecies = std::move(_indecies);
|
||||
uvs = std::move(_uvs);
|
||||
}
|
||||
|
||||
size_t TSE::Mesh::IndeciesCount() const
|
||||
{
|
||||
return indecies.size();
|
||||
}
|
||||
|
||||
size_t TSE::Mesh::VerteciesCount() const
|
||||
{
|
||||
return vertecies.size();
|
||||
}
|
||||
|
||||
TSE::Mesh TSE::Mesh::GetCircleMesh(ushort segments)
|
||||
{
|
||||
std::vector<Vector3> verts;
|
||||
std::vector<ushort> indices;
|
||||
std::vector<Vector2> uvs;
|
||||
|
||||
verts.emplace_back(0.0f, 0.0f, 0.0f);
|
||||
uvs.emplace_back(0.5f, 0.5f);
|
||||
|
||||
float angleStep = 2.0f * TSE_PI / segments;
|
||||
|
||||
for (int i = 0; i <= segments; ++i) {
|
||||
float angle = i * angleStep;
|
||||
float x = std::cos(angle) * 0.5f;
|
||||
float y = std::sin(angle) * 0.5f;
|
||||
verts.emplace_back(x, y, 0);
|
||||
uvs.emplace_back(x + 0.5f, y + 0.5f);
|
||||
|
||||
if (i > 0) {
|
||||
indices.push_back(0);
|
||||
indices.push_back(i);
|
||||
indices.push_back(i + 1);
|
||||
}
|
||||
}
|
||||
|
||||
return Mesh("Circle", verts, indices, uvs);
|
||||
}
|
||||
|
||||
TSE::Mesh TSE::Mesh::GetQuadMesh()
|
||||
{
|
||||
std::vector<Vector3> verts = {
|
||||
Vector3(-0.5f, -0.5f, 0),
|
||||
Vector3( 0.5f, -0.5f, 0),
|
||||
Vector3( 0.5f, 0.5f, 0),
|
||||
Vector3(-0.5f, 0.5f, 0)
|
||||
};
|
||||
|
||||
std::vector<Vector2> uvs = {
|
||||
Vector2(0.0f, 0.0f),
|
||||
Vector2(1.0f, 0.0f),
|
||||
Vector2(1.0f, 1.0f),
|
||||
Vector2(0.0f, 1.0f)
|
||||
};
|
||||
|
||||
std::vector<unsigned short> indices = {
|
||||
0, 1, 2,
|
||||
2, 3, 0
|
||||
};
|
||||
|
||||
return Mesh("Quad", verts, indices, uvs);
|
||||
}
|
||||
Reference in New Issue
Block a user