1084 lines
34 KiB
C++
1084 lines
34 KiB
C++
#include "AssetManager.h"
|
|
#include "ModelManager.h"
|
|
#include "ShaderManager.h"
|
|
#include "MaterialManager.h"
|
|
#include "TextureManager.h"
|
|
#include "PhysFSManager.h"
|
|
#include "log/log.h"
|
|
#include <algorithm>
|
|
#include <cctype>
|
|
#include <iomanip>
|
|
#include <sstream>
|
|
|
|
namespace {
|
|
std::string TrimAsset(const std::string& value) {
|
|
size_t start = 0;
|
|
while (start < value.size() && std::isspace(static_cast<unsigned char>(value[start]))) {
|
|
start++;
|
|
}
|
|
size_t end = value.size();
|
|
while (end > start && std::isspace(static_cast<unsigned char>(value[end - 1]))) {
|
|
end--;
|
|
}
|
|
return value.substr(start, end - start);
|
|
}
|
|
|
|
std::string StripCommentAsset(const std::string& line) {
|
|
bool inString = false;
|
|
for (size_t i = 0; i < line.size(); ++i) {
|
|
char c = line[i];
|
|
if (c == '"') {
|
|
inString = !inString;
|
|
}
|
|
if (!inString && c == '#') {
|
|
return line.substr(0, i);
|
|
}
|
|
}
|
|
return line;
|
|
}
|
|
|
|
bool ParseStringAsset(const std::string& value, std::string& out) {
|
|
std::string trimmed = TrimAsset(value);
|
|
if (trimmed.size() >= 2 && trimmed.front() == '"' && trimmed.back() == '"') {
|
|
out = trimmed.substr(1, trimmed.size() - 2);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool ParseFloatList(const std::string& value, std::vector<float>& out) {
|
|
std::string trimmed = TrimAsset(value);
|
|
if (trimmed.size() < 2 || trimmed.front() != '[' || trimmed.back() != ']') {
|
|
return false;
|
|
}
|
|
|
|
std::string inner = trimmed.substr(1, trimmed.size() - 2);
|
|
std::stringstream ss(inner);
|
|
std::string token;
|
|
out.clear();
|
|
|
|
while (std::getline(ss, token, ',')) {
|
|
std::string piece = TrimAsset(token);
|
|
if (piece.empty()) {
|
|
continue;
|
|
}
|
|
try {
|
|
out.push_back(std::stof(piece));
|
|
} catch (...) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return !out.empty();
|
|
}
|
|
|
|
std::string EscapeStringAsset(const std::string& value) {
|
|
std::string out;
|
|
out.reserve(value.size());
|
|
for (char c : value) {
|
|
if (c == '\\' || c == '"') {
|
|
out.push_back('\\');
|
|
}
|
|
out.push_back(c);
|
|
}
|
|
return out;
|
|
}
|
|
|
|
void WriteFloatList(std::ostream& out, const std::vector<float>& values) {
|
|
out << '[';
|
|
for (size_t i = 0; i < values.size(); ++i) {
|
|
if (i > 0) {
|
|
out << ", ";
|
|
}
|
|
out << values[i];
|
|
}
|
|
out << ']';
|
|
}
|
|
}
|
|
|
|
AssetManager::AssetManager() = default;
|
|
|
|
AssetManager::~AssetManager() = default;
|
|
|
|
void AssetManager::SetManagers(ModelManager* modelMgr, ShaderManager* shaderMgr,
|
|
MaterialManager* materialMgr, TextureManager* textureMgr)
|
|
{
|
|
modelManager = modelMgr;
|
|
shaderManager = shaderMgr;
|
|
materialManager = materialMgr;
|
|
textureManager = textureMgr;
|
|
}
|
|
|
|
bool AssetManager::RegisterTexture(const std::string& key, unsigned int id, const std::string& source)
|
|
{
|
|
if (key.empty() || id == 0) {
|
|
return false;
|
|
}
|
|
if (textureAssets.find(key) != textureAssets.end()) {
|
|
return textureAssets[key].id == id;
|
|
}
|
|
|
|
textureAssets[key] = AssetEntry{id, source};
|
|
textureIds[id] = key;
|
|
return true;
|
|
}
|
|
|
|
bool AssetManager::RegisterShader(const std::string& key, unsigned int id, const std::string& source)
|
|
{
|
|
if (key.empty() || id == 0) {
|
|
return false;
|
|
}
|
|
if (shaderAssets.find(key) != shaderAssets.end()) {
|
|
return shaderAssets[key].id == id;
|
|
}
|
|
|
|
shaderAssets[key] = AssetEntry{id, source};
|
|
shaderIds[id] = key;
|
|
return true;
|
|
}
|
|
|
|
bool AssetManager::RegisterModel(const std::string& key, unsigned int id, const std::string& source)
|
|
{
|
|
if (key.empty() || id == 0) {
|
|
return false;
|
|
}
|
|
if (modelAssets.find(key) != modelAssets.end()) {
|
|
return modelAssets[key].id == id;
|
|
}
|
|
|
|
modelAssets[key] = AssetEntry{id, source};
|
|
modelIds[id] = key;
|
|
return true;
|
|
}
|
|
|
|
bool AssetManager::RegisterMaterial(const std::string& key, unsigned int id, const std::string& source)
|
|
{
|
|
if (key.empty() || id == 0) {
|
|
return false;
|
|
}
|
|
if (materialAssets.find(key) != materialAssets.end()) {
|
|
return materialAssets[key].id == id;
|
|
}
|
|
|
|
materialAssets[key] = AssetEntry{id, source};
|
|
materialIds[id] = key;
|
|
return true;
|
|
}
|
|
|
|
unsigned int AssetManager::LoadTexture(const std::string& key, const std::string& path)
|
|
{
|
|
if (!textureManager) {
|
|
log_warn("AssetManager: texture manager not set");
|
|
return 0;
|
|
}
|
|
if (textureAssets.find(key) != textureAssets.end()) {
|
|
return textureAssets[key].id;
|
|
}
|
|
|
|
unsigned int id = textureManager->Load(path);
|
|
if (id != 0) {
|
|
RegisterTexture(key, id, path);
|
|
textureDefs[key] = TextureDefinition{path};
|
|
}
|
|
return id;
|
|
}
|
|
|
|
unsigned int AssetManager::LoadShader(const std::string& key, const std::string& vsPath, const std::string& fsPath)
|
|
{
|
|
if (!shaderManager) {
|
|
log_warn("AssetManager: shader manager not set");
|
|
return 0;
|
|
}
|
|
if (shaderAssets.find(key) != shaderAssets.end()) {
|
|
return shaderAssets[key].id;
|
|
}
|
|
|
|
unsigned int id = shaderManager->Load(vsPath, fsPath);
|
|
if (id != 0) {
|
|
RegisterShader(key, id, vsPath + "|" + fsPath);
|
|
shaderDefs[key] = ShaderDefinition{vsPath, fsPath};
|
|
}
|
|
return id;
|
|
}
|
|
|
|
unsigned int AssetManager::LoadModel(const std::string& key, const std::string& path)
|
|
{
|
|
if (!modelManager) {
|
|
log_warn("AssetManager: model manager not set");
|
|
return 0;
|
|
}
|
|
if (modelAssets.find(key) != modelAssets.end()) {
|
|
return modelAssets[key].id;
|
|
}
|
|
|
|
unsigned int id = modelManager->Load(path);
|
|
if (id != 0) {
|
|
RegisterModel(key, id, path);
|
|
ModelDefinition def;
|
|
def.type = ModelDefinition::Type::Path;
|
|
def.path = path;
|
|
modelDefs[key] = def;
|
|
}
|
|
return id;
|
|
}
|
|
|
|
unsigned int AssetManager::LoadCube(const std::string& key, float width, float height, float length)
|
|
{
|
|
if (!modelManager) {
|
|
log_warn("AssetManager: model manager not set");
|
|
return 0;
|
|
}
|
|
if (modelAssets.find(key) != modelAssets.end()) {
|
|
return modelAssets[key].id;
|
|
}
|
|
|
|
unsigned int id = modelManager->LoadCube(width, height, length);
|
|
if (id != 0) {
|
|
RegisterModel(key, id, "cube");
|
|
ModelDefinition def;
|
|
def.type = ModelDefinition::Type::Primitive;
|
|
def.primitive = "cube";
|
|
def.params = {width, height, length};
|
|
modelDefs[key] = def;
|
|
}
|
|
return id;
|
|
}
|
|
|
|
unsigned int AssetManager::LoadPoly(const std::string& key, int sides, float radius)
|
|
{
|
|
if (!modelManager) {
|
|
log_warn("AssetManager: model manager not set");
|
|
return 0;
|
|
}
|
|
if (modelAssets.find(key) != modelAssets.end()) {
|
|
return modelAssets[key].id;
|
|
}
|
|
|
|
Mesh mesh = GenMeshPoly(sides, radius);
|
|
unsigned int id = modelManager->LoadFromMesh(mesh);
|
|
if (id != 0) {
|
|
RegisterModel(key, id, "poly");
|
|
ModelDefinition def;
|
|
def.type = ModelDefinition::Type::Primitive;
|
|
def.primitive = "poly";
|
|
def.params = {static_cast<float>(sides), radius};
|
|
modelDefs[key] = def;
|
|
}
|
|
return id;
|
|
}
|
|
|
|
unsigned int AssetManager::LoadSphere(const std::string& key, float radius, int rings, int slices)
|
|
{
|
|
if (!modelManager) {
|
|
log_warn("AssetManager: model manager not set");
|
|
return 0;
|
|
}
|
|
if (modelAssets.find(key) != modelAssets.end()) {
|
|
return modelAssets[key].id;
|
|
}
|
|
|
|
unsigned int id = modelManager->LoadSphere(radius, rings, slices);
|
|
if (id != 0) {
|
|
RegisterModel(key, id, "sphere");
|
|
ModelDefinition def;
|
|
def.type = ModelDefinition::Type::Primitive;
|
|
def.primitive = "sphere";
|
|
def.params = {radius, static_cast<float>(rings), static_cast<float>(slices)};
|
|
modelDefs[key] = def;
|
|
}
|
|
return id;
|
|
}
|
|
|
|
unsigned int AssetManager::LoadHemiSphere(const std::string& key, float radius, int rings, int slices)
|
|
{
|
|
if (!modelManager) {
|
|
log_warn("AssetManager: model manager not set");
|
|
return 0;
|
|
}
|
|
if (modelAssets.find(key) != modelAssets.end()) {
|
|
return modelAssets[key].id;
|
|
}
|
|
|
|
Mesh mesh = GenMeshHemiSphere(radius, rings, slices);
|
|
unsigned int id = modelManager->LoadFromMesh(mesh);
|
|
if (id != 0) {
|
|
RegisterModel(key, id, "hemisphere");
|
|
ModelDefinition def;
|
|
def.type = ModelDefinition::Type::Primitive;
|
|
def.primitive = "hemisphere";
|
|
def.params = {radius, static_cast<float>(rings), static_cast<float>(slices)};
|
|
modelDefs[key] = def;
|
|
}
|
|
return id;
|
|
}
|
|
|
|
unsigned int AssetManager::LoadPlane(const std::string& key, float width, float length, int resX, int resZ)
|
|
{
|
|
if (!modelManager) {
|
|
log_warn("AssetManager: model manager not set");
|
|
return 0;
|
|
}
|
|
if (modelAssets.find(key) != modelAssets.end()) {
|
|
return modelAssets[key].id;
|
|
}
|
|
|
|
unsigned int id = modelManager->LoadPlane(width, length, resX, resZ);
|
|
if (id != 0) {
|
|
RegisterModel(key, id, "plane");
|
|
ModelDefinition def;
|
|
def.type = ModelDefinition::Type::Primitive;
|
|
def.primitive = "plane";
|
|
def.params = {width, length, static_cast<float>(resX), static_cast<float>(resZ)};
|
|
modelDefs[key] = def;
|
|
}
|
|
return id;
|
|
}
|
|
|
|
unsigned int AssetManager::LoadCylinder(const std::string& key, float radius, float height, int slices)
|
|
{
|
|
if (!modelManager) {
|
|
log_warn("AssetManager: model manager not set");
|
|
return 0;
|
|
}
|
|
if (modelAssets.find(key) != modelAssets.end()) {
|
|
return modelAssets[key].id;
|
|
}
|
|
|
|
Mesh mesh = GenMeshCylinder(radius, height, slices);
|
|
unsigned int id = modelManager->LoadFromMesh(mesh);
|
|
if (id != 0) {
|
|
RegisterModel(key, id, "cylinder");
|
|
ModelDefinition def;
|
|
def.type = ModelDefinition::Type::Primitive;
|
|
def.primitive = "cylinder";
|
|
def.params = {radius, height, static_cast<float>(slices)};
|
|
modelDefs[key] = def;
|
|
}
|
|
return id;
|
|
}
|
|
|
|
unsigned int AssetManager::LoadCone(const std::string& key, float radius, float height, int slices)
|
|
{
|
|
if (!modelManager) {
|
|
log_warn("AssetManager: model manager not set");
|
|
return 0;
|
|
}
|
|
if (modelAssets.find(key) != modelAssets.end()) {
|
|
return modelAssets[key].id;
|
|
}
|
|
|
|
Mesh mesh = GenMeshCone(radius, height, slices);
|
|
unsigned int id = modelManager->LoadFromMesh(mesh);
|
|
if (id != 0) {
|
|
RegisterModel(key, id, "cone");
|
|
ModelDefinition def;
|
|
def.type = ModelDefinition::Type::Primitive;
|
|
def.primitive = "cone";
|
|
def.params = {radius, height, static_cast<float>(slices)};
|
|
modelDefs[key] = def;
|
|
}
|
|
return id;
|
|
}
|
|
|
|
unsigned int AssetManager::LoadTorus(const std::string& key, float radius, float size, int radSeg, int sides)
|
|
{
|
|
if (!modelManager) {
|
|
log_warn("AssetManager: model manager not set");
|
|
return 0;
|
|
}
|
|
if (modelAssets.find(key) != modelAssets.end()) {
|
|
return modelAssets[key].id;
|
|
}
|
|
|
|
Mesh mesh = GenMeshTorus(radius, size, radSeg, sides);
|
|
unsigned int id = modelManager->LoadFromMesh(mesh);
|
|
if (id != 0) {
|
|
RegisterModel(key, id, "torus");
|
|
ModelDefinition def;
|
|
def.type = ModelDefinition::Type::Primitive;
|
|
def.primitive = "torus";
|
|
def.params = {radius, size, static_cast<float>(radSeg), static_cast<float>(sides)};
|
|
modelDefs[key] = def;
|
|
}
|
|
return id;
|
|
}
|
|
|
|
unsigned int AssetManager::LoadKnot(const std::string& key, float radius, float size, int radSeg, int sides)
|
|
{
|
|
if (!modelManager) {
|
|
log_warn("AssetManager: model manager not set");
|
|
return 0;
|
|
}
|
|
if (modelAssets.find(key) != modelAssets.end()) {
|
|
return modelAssets[key].id;
|
|
}
|
|
|
|
Mesh mesh = GenMeshKnot(radius, size, radSeg, sides);
|
|
unsigned int id = modelManager->LoadFromMesh(mesh);
|
|
if (id != 0) {
|
|
RegisterModel(key, id, "knot");
|
|
ModelDefinition def;
|
|
def.type = ModelDefinition::Type::Primitive;
|
|
def.primitive = "knot";
|
|
def.params = {radius, size, static_cast<float>(radSeg), static_cast<float>(sides)};
|
|
modelDefs[key] = def;
|
|
}
|
|
return id;
|
|
}
|
|
|
|
unsigned int AssetManager::LoadHeightmap(const std::string& key, const std::string& heightmapPath, float sizeX, float sizeY, float sizeZ)
|
|
{
|
|
if (!modelManager) {
|
|
log_warn("AssetManager: model manager not set");
|
|
return 0;
|
|
}
|
|
if (modelAssets.find(key) != modelAssets.end()) {
|
|
return modelAssets[key].id;
|
|
}
|
|
|
|
Image heightmap = LoadImage(heightmapPath.c_str());
|
|
if (!heightmap.data) {
|
|
log_warn("AssetManager: failed to load heightmap %s", heightmapPath.c_str());
|
|
return 0;
|
|
}
|
|
|
|
Mesh mesh = GenMeshHeightmap(heightmap, Vector3{sizeX, sizeY, sizeZ});
|
|
UnloadImage(heightmap);
|
|
|
|
unsigned int id = modelManager->LoadFromMesh(mesh);
|
|
if (id != 0) {
|
|
RegisterModel(key, id, "heightmap:" + heightmapPath);
|
|
ModelDefinition def;
|
|
def.type = ModelDefinition::Type::Heightmap;
|
|
def.path = heightmapPath;
|
|
def.params = {sizeX, sizeY, sizeZ};
|
|
modelDefs[key] = def;
|
|
}
|
|
return id;
|
|
}
|
|
|
|
unsigned int AssetManager::LoadCubicmap(const std::string& key, const std::string& cubicmapPath, float sizeX, float sizeY, float sizeZ)
|
|
{
|
|
if (!modelManager) {
|
|
log_warn("AssetManager: model manager not set");
|
|
return 0;
|
|
}
|
|
if (modelAssets.find(key) != modelAssets.end()) {
|
|
return modelAssets[key].id;
|
|
}
|
|
|
|
Image cubicmap = LoadImage(cubicmapPath.c_str());
|
|
if (!cubicmap.data) {
|
|
log_warn("AssetManager: failed to load cubicmap %s", cubicmapPath.c_str());
|
|
return 0;
|
|
}
|
|
|
|
Mesh mesh = GenMeshCubicmap(cubicmap, Vector3{sizeX, sizeY, sizeZ});
|
|
UnloadImage(cubicmap);
|
|
|
|
unsigned int id = modelManager->LoadFromMesh(mesh);
|
|
if (id != 0) {
|
|
RegisterModel(key, id, "cubicmap:" + cubicmapPath);
|
|
ModelDefinition def;
|
|
def.type = ModelDefinition::Type::Cubicmap;
|
|
def.path = cubicmapPath;
|
|
def.params = {sizeX, sizeY, sizeZ};
|
|
modelDefs[key] = def;
|
|
}
|
|
return id;
|
|
}
|
|
|
|
unsigned int AssetManager::LoadFromMesh(const std::string& key, Mesh mesh, const std::string& source)
|
|
{
|
|
if (!modelManager) {
|
|
log_warn("AssetManager: model manager not set");
|
|
return 0;
|
|
}
|
|
if (modelAssets.find(key) != modelAssets.end()) {
|
|
return modelAssets[key].id;
|
|
}
|
|
|
|
unsigned int id = modelManager->LoadFromMesh(mesh);
|
|
if (id != 0) {
|
|
RegisterModel(key, id, source);
|
|
ModelDefinition def;
|
|
def.type = ModelDefinition::Type::Mesh;
|
|
def.path = source;
|
|
modelDefs[key] = def;
|
|
}
|
|
return id;
|
|
}
|
|
|
|
unsigned int AssetManager::CreateMaterial(const std::string& key, unsigned int shaderId, Color albedo)
|
|
{
|
|
if (!materialManager) {
|
|
log_warn("AssetManager: material manager not set");
|
|
return 0;
|
|
}
|
|
if (materialAssets.find(key) != materialAssets.end()) {
|
|
return materialAssets[key].id;
|
|
}
|
|
|
|
unsigned int id = materialManager->Create(shaderId, albedo);
|
|
if (id != 0) {
|
|
RegisterMaterial(key, id, "shader_id=" + std::to_string(shaderId));
|
|
MaterialDefinition def;
|
|
def.albedo = albedo;
|
|
if (shaderId != 0) {
|
|
const std::string& shaderKey = GetShaderKey(shaderId);
|
|
if (!shaderKey.empty()) {
|
|
def.shaderKey = shaderKey;
|
|
def.hasShaderKey = true;
|
|
} else {
|
|
def.shaderId = shaderId;
|
|
def.hasShaderId = true;
|
|
}
|
|
}
|
|
materialDefs[key] = def;
|
|
}
|
|
return id;
|
|
}
|
|
|
|
bool AssetManager::ApplyMeshTangents(const std::string& key, int meshIndex)
|
|
{
|
|
if (!modelManager) {
|
|
log_warn("AssetManager: model manager not set");
|
|
return false;
|
|
}
|
|
|
|
unsigned int id = GetModelId(key);
|
|
if (id == 0) {
|
|
return false;
|
|
}
|
|
|
|
Model* model = modelManager->Get(id);
|
|
if (!model || model->meshCount <= 0) {
|
|
return false;
|
|
}
|
|
|
|
if (meshIndex < 0 || meshIndex >= model->meshCount) {
|
|
return false;
|
|
}
|
|
|
|
GenMeshTangents(&model->meshes[meshIndex]);
|
|
UploadMesh(&model->meshes[meshIndex], false);
|
|
return true;
|
|
}
|
|
|
|
bool AssetManager::UnloadTexture(const std::string& key)
|
|
{
|
|
if (!textureManager) {
|
|
log_warn("AssetManager: texture manager not set");
|
|
return false;
|
|
}
|
|
auto it = textureAssets.find(key);
|
|
if (it == textureAssets.end()) {
|
|
return false;
|
|
}
|
|
textureManager->Unload(it->second.id);
|
|
textureIds.erase(it->second.id);
|
|
textureAssets.erase(it);
|
|
textureDefs.erase(key);
|
|
return true;
|
|
}
|
|
|
|
bool AssetManager::UnloadShader(const std::string& key)
|
|
{
|
|
if (!shaderManager) {
|
|
log_warn("AssetManager: shader manager not set");
|
|
return false;
|
|
}
|
|
auto it = shaderAssets.find(key);
|
|
if (it == shaderAssets.end()) {
|
|
return false;
|
|
}
|
|
shaderManager->Unload(it->second.id);
|
|
shaderIds.erase(it->second.id);
|
|
shaderAssets.erase(it);
|
|
shaderDefs.erase(key);
|
|
return true;
|
|
}
|
|
|
|
bool AssetManager::UnloadModel(const std::string& key)
|
|
{
|
|
if (!modelManager) {
|
|
log_warn("AssetManager: model manager not set");
|
|
return false;
|
|
}
|
|
auto it = modelAssets.find(key);
|
|
if (it == modelAssets.end()) {
|
|
return false;
|
|
}
|
|
modelManager->Unload(it->second.id);
|
|
modelIds.erase(it->second.id);
|
|
modelAssets.erase(it);
|
|
modelDefs.erase(key);
|
|
return true;
|
|
}
|
|
|
|
bool AssetManager::UnloadMaterial(const std::string& key)
|
|
{
|
|
if (!materialManager) {
|
|
log_warn("AssetManager: material manager not set");
|
|
return false;
|
|
}
|
|
auto it = materialAssets.find(key);
|
|
if (it == materialAssets.end()) {
|
|
return false;
|
|
}
|
|
materialManager->Remove(it->second.id);
|
|
materialIds.erase(it->second.id);
|
|
materialAssets.erase(it);
|
|
materialDefs.erase(key);
|
|
return true;
|
|
}
|
|
|
|
unsigned int AssetManager::GetTextureId(const std::string& key) const
|
|
{
|
|
auto it = textureAssets.find(key);
|
|
return (it != textureAssets.end()) ? it->second.id : 0;
|
|
}
|
|
|
|
unsigned int AssetManager::GetShaderId(const std::string& key) const
|
|
{
|
|
auto it = shaderAssets.find(key);
|
|
return (it != shaderAssets.end()) ? it->second.id : 0;
|
|
}
|
|
|
|
unsigned int AssetManager::GetModelId(const std::string& key) const
|
|
{
|
|
auto it = modelAssets.find(key);
|
|
return (it != modelAssets.end()) ? it->second.id : 0;
|
|
}
|
|
|
|
unsigned int AssetManager::GetMaterialId(const std::string& key) const
|
|
{
|
|
auto it = materialAssets.find(key);
|
|
return (it != materialAssets.end()) ? it->second.id : 0;
|
|
}
|
|
|
|
const std::string& AssetManager::GetTextureKey(unsigned int id) const
|
|
{
|
|
return GetKeyForId(textureIds, id);
|
|
}
|
|
|
|
const std::string& AssetManager::GetShaderKey(unsigned int id) const
|
|
{
|
|
return GetKeyForId(shaderIds, id);
|
|
}
|
|
|
|
const std::string& AssetManager::GetModelKey(unsigned int id) const
|
|
{
|
|
return GetKeyForId(modelIds, id);
|
|
}
|
|
|
|
const std::string& AssetManager::GetMaterialKey(unsigned int id) const
|
|
{
|
|
return GetKeyForId(materialIds, id);
|
|
}
|
|
|
|
std::vector<std::string> AssetManager::ListTextureKeys() const
|
|
{
|
|
return ListKeys(textureAssets);
|
|
}
|
|
|
|
std::vector<std::string> AssetManager::ListShaderKeys() const
|
|
{
|
|
return ListKeys(shaderAssets);
|
|
}
|
|
|
|
std::vector<std::string> AssetManager::ListModelKeys() const
|
|
{
|
|
return ListKeys(modelAssets);
|
|
}
|
|
|
|
std::vector<std::string> AssetManager::ListMaterialKeys() const
|
|
{
|
|
return ListKeys(materialAssets);
|
|
}
|
|
|
|
std::string AssetManager::GetTextureSource(const std::string& key) const
|
|
{
|
|
auto it = textureAssets.find(key);
|
|
return (it != textureAssets.end()) ? it->second.source : std::string();
|
|
}
|
|
|
|
std::string AssetManager::GetShaderSource(const std::string& key) const
|
|
{
|
|
auto it = shaderAssets.find(key);
|
|
return (it != shaderAssets.end()) ? it->second.source : std::string();
|
|
}
|
|
|
|
std::string AssetManager::GetModelSource(const std::string& key) const
|
|
{
|
|
auto it = modelAssets.find(key);
|
|
return (it != modelAssets.end()) ? it->second.source : std::string();
|
|
}
|
|
|
|
std::string AssetManager::GetMaterialSource(const std::string& key) const
|
|
{
|
|
auto it = materialAssets.find(key);
|
|
return (it != materialAssets.end()) ? it->second.source : std::string();
|
|
}
|
|
|
|
void AssetManager::Clear()
|
|
{
|
|
textureAssets.clear();
|
|
shaderAssets.clear();
|
|
modelAssets.clear();
|
|
materialAssets.clear();
|
|
|
|
textureDefs.clear();
|
|
shaderDefs.clear();
|
|
modelDefs.clear();
|
|
materialDefs.clear();
|
|
|
|
textureIds.clear();
|
|
shaderIds.clear();
|
|
modelIds.clear();
|
|
materialIds.clear();
|
|
}
|
|
|
|
const std::string& AssetManager::GetKeyForId(const std::unordered_map<unsigned int, std::string>& map, unsigned int id) const
|
|
{
|
|
static const std::string empty;
|
|
auto it = map.find(id);
|
|
return (it != map.end()) ? it->second : empty;
|
|
}
|
|
|
|
std::vector<std::string> AssetManager::ListKeys(const std::unordered_map<std::string, AssetEntry>& map) const
|
|
{
|
|
std::vector<std::string> keys;
|
|
keys.reserve(map.size());
|
|
for (const auto& pair : map) {
|
|
keys.push_back(pair.first);
|
|
}
|
|
return keys;
|
|
}
|
|
|
|
bool AssetManager::SaveRegistry(const std::string& path) const
|
|
{
|
|
auto parentPos = path.find_last_of("/\\");
|
|
if (parentPos != std::string::npos) {
|
|
PhysFSManager::EnsureDir(path.substr(0, parentPos));
|
|
}
|
|
|
|
std::ostringstream file;
|
|
file << "# Simian asset registry\n";
|
|
file << "# Auto-generated - edits may be overwritten\n\n";
|
|
file << std::setprecision(6);
|
|
|
|
for (const auto& [key, def] : textureDefs) {
|
|
if (key.empty() || def.path.empty()) {
|
|
continue;
|
|
}
|
|
file << "[[texture]]\n";
|
|
file << "key = \"" << EscapeStringAsset(key) << "\"\n";
|
|
file << "path = \"" << EscapeStringAsset(def.path) << "\"\n\n";
|
|
}
|
|
|
|
for (const auto& [key, def] : shaderDefs) {
|
|
if (key.empty() || def.vsPath.empty() || def.fsPath.empty()) {
|
|
continue;
|
|
}
|
|
file << "[[shader]]\n";
|
|
file << "key = \"" << EscapeStringAsset(key) << "\"\n";
|
|
file << "vs = \"" << EscapeStringAsset(def.vsPath) << "\"\n";
|
|
file << "fs = \"" << EscapeStringAsset(def.fsPath) << "\"\n\n";
|
|
}
|
|
|
|
for (const auto& [key, def] : modelDefs) {
|
|
if (key.empty()) {
|
|
continue;
|
|
}
|
|
if (def.type == ModelDefinition::Type::Mesh) {
|
|
continue;
|
|
}
|
|
|
|
file << "[[model]]\n";
|
|
file << "key = \"" << EscapeStringAsset(key) << "\"\n";
|
|
switch (def.type) {
|
|
case ModelDefinition::Type::Path:
|
|
if (!def.path.empty()) {
|
|
file << "type = \"path\"\n";
|
|
file << "path = \"" << EscapeStringAsset(def.path) << "\"\n\n";
|
|
}
|
|
break;
|
|
case ModelDefinition::Type::Primitive:
|
|
if (!def.primitive.empty() && !def.params.empty()) {
|
|
file << "type = \"primitive\"\n";
|
|
file << "primitive = \"" << EscapeStringAsset(def.primitive) << "\"\n";
|
|
file << "params = ";
|
|
WriteFloatList(file, def.params);
|
|
file << "\n\n";
|
|
}
|
|
break;
|
|
case ModelDefinition::Type::Heightmap:
|
|
if (!def.path.empty() && def.params.size() >= 3) {
|
|
file << "type = \"heightmap\"\n";
|
|
file << "path = \"" << EscapeStringAsset(def.path) << "\"\n";
|
|
file << "size = ";
|
|
WriteFloatList(file, def.params);
|
|
file << "\n\n";
|
|
}
|
|
break;
|
|
case ModelDefinition::Type::Cubicmap:
|
|
if (!def.path.empty() && def.params.size() >= 3) {
|
|
file << "type = \"cubicmap\"\n";
|
|
file << "path = \"" << EscapeStringAsset(def.path) << "\"\n";
|
|
file << "size = ";
|
|
WriteFloatList(file, def.params);
|
|
file << "\n\n";
|
|
}
|
|
break;
|
|
case ModelDefinition::Type::Mesh:
|
|
break;
|
|
}
|
|
}
|
|
|
|
for (const auto& [key, def] : materialDefs) {
|
|
if (key.empty()) {
|
|
continue;
|
|
}
|
|
file << "[[material]]\n";
|
|
file << "key = \"" << EscapeStringAsset(key) << "\"\n";
|
|
if (def.hasShaderKey && !def.shaderKey.empty()) {
|
|
file << "shader_key = \"" << EscapeStringAsset(def.shaderKey) << "\"\n";
|
|
} else if (def.hasShaderId && def.shaderId > 0) {
|
|
file << "shader_id = " << def.shaderId << "\n";
|
|
}
|
|
std::vector<float> albedo = {
|
|
static_cast<float>(def.albedo.r),
|
|
static_cast<float>(def.albedo.g),
|
|
static_cast<float>(def.albedo.b),
|
|
static_cast<float>(def.albedo.a)
|
|
};
|
|
file << "albedo = ";
|
|
WriteFloatList(file, albedo);
|
|
file << "\n\n";
|
|
}
|
|
|
|
return PhysFSManager::WriteTextFile(path, file.str());
|
|
}
|
|
|
|
bool AssetManager::LoadRegistry(const std::string& path)
|
|
{
|
|
std::string contents;
|
|
if (!PhysFSManager::ReadTextFile(path, contents)) {
|
|
log_warn("Asset registry not found: %s", path.c_str());
|
|
return false;
|
|
}
|
|
|
|
enum class Section {
|
|
None,
|
|
Texture,
|
|
Shader,
|
|
Model,
|
|
Material
|
|
};
|
|
|
|
Section section = Section::None;
|
|
std::string entryKey;
|
|
std::unordered_map<std::string, std::string> fields;
|
|
|
|
auto flushEntry = [&]() {
|
|
if (section == Section::None || entryKey.empty()) {
|
|
entryKey.clear();
|
|
fields.clear();
|
|
return;
|
|
}
|
|
|
|
if (section == Section::Texture) {
|
|
auto it = fields.find("path");
|
|
if (it != fields.end()) {
|
|
std::string pathValue;
|
|
if (ParseStringAsset(it->second, pathValue)) {
|
|
textureDefs[entryKey] = TextureDefinition{pathValue};
|
|
if (GetTextureId(entryKey) == 0) {
|
|
LoadTexture(entryKey, pathValue);
|
|
}
|
|
}
|
|
}
|
|
} else if (section == Section::Shader) {
|
|
std::string vsPath;
|
|
std::string fsPath;
|
|
auto itVs = fields.find("vs");
|
|
auto itFs = fields.find("fs");
|
|
if (itVs != fields.end() && itFs != fields.end() &&
|
|
ParseStringAsset(itVs->second, vsPath) && ParseStringAsset(itFs->second, fsPath)) {
|
|
shaderDefs[entryKey] = ShaderDefinition{vsPath, fsPath};
|
|
if (GetShaderId(entryKey) == 0) {
|
|
LoadShader(entryKey, vsPath, fsPath);
|
|
}
|
|
}
|
|
} else if (section == Section::Model) {
|
|
std::string typeValue;
|
|
auto itType = fields.find("type");
|
|
if (itType != fields.end() && ParseStringAsset(itType->second, typeValue)) {
|
|
if (typeValue == "path") {
|
|
std::string modelPath;
|
|
auto itPath = fields.find("path");
|
|
if (itPath != fields.end() && ParseStringAsset(itPath->second, modelPath)) {
|
|
ModelDefinition def;
|
|
def.type = ModelDefinition::Type::Path;
|
|
def.path = modelPath;
|
|
modelDefs[entryKey] = def;
|
|
if (GetModelId(entryKey) == 0) {
|
|
LoadModel(entryKey, modelPath);
|
|
}
|
|
}
|
|
} else if (typeValue == "primitive") {
|
|
std::string primitive;
|
|
std::vector<float> params;
|
|
auto itPrim = fields.find("primitive");
|
|
auto itParams = fields.find("params");
|
|
if (itPrim != fields.end() && itParams != fields.end() &&
|
|
ParseStringAsset(itPrim->second, primitive) && ParseFloatList(itParams->second, params)) {
|
|
ModelDefinition def;
|
|
def.type = ModelDefinition::Type::Primitive;
|
|
def.primitive = primitive;
|
|
def.params = params;
|
|
modelDefs[entryKey] = def;
|
|
|
|
if (GetModelId(entryKey) == 0) {
|
|
if (primitive == "cube" && params.size() >= 3) {
|
|
LoadCube(entryKey, params[0], params[1], params[2]);
|
|
} else if (primitive == "poly" && params.size() >= 2) {
|
|
LoadPoly(entryKey, static_cast<int>(params[0]), params[1]);
|
|
} else if (primitive == "sphere" && params.size() >= 3) {
|
|
LoadSphere(entryKey, params[0], static_cast<int>(params[1]), static_cast<int>(params[2]));
|
|
} else if (primitive == "hemisphere" && params.size() >= 3) {
|
|
LoadHemiSphere(entryKey, params[0], static_cast<int>(params[1]), static_cast<int>(params[2]));
|
|
} else if (primitive == "plane" && params.size() >= 4) {
|
|
LoadPlane(entryKey, params[0], params[1], static_cast<int>(params[2]), static_cast<int>(params[3]));
|
|
} else if (primitive == "cylinder" && params.size() >= 3) {
|
|
LoadCylinder(entryKey, params[0], params[1], static_cast<int>(params[2]));
|
|
} else if (primitive == "cone" && params.size() >= 3) {
|
|
LoadCone(entryKey, params[0], params[1], static_cast<int>(params[2]));
|
|
} else if (primitive == "torus" && params.size() >= 4) {
|
|
LoadTorus(entryKey, params[0], params[1], static_cast<int>(params[2]), static_cast<int>(params[3]));
|
|
} else if (primitive == "knot" && params.size() >= 4) {
|
|
LoadKnot(entryKey, params[0], params[1], static_cast<int>(params[2]), static_cast<int>(params[3]));
|
|
}
|
|
}
|
|
}
|
|
} else if (typeValue == "heightmap" || typeValue == "cubicmap") {
|
|
std::string mapPath;
|
|
std::vector<float> sizeValues;
|
|
auto itPath = fields.find("path");
|
|
auto itSize = fields.find("size");
|
|
if (itPath != fields.end() && itSize != fields.end() &&
|
|
ParseStringAsset(itPath->second, mapPath) && ParseFloatList(itSize->second, sizeValues) && sizeValues.size() >= 3) {
|
|
ModelDefinition def;
|
|
def.type = (typeValue == "heightmap") ? ModelDefinition::Type::Heightmap : ModelDefinition::Type::Cubicmap;
|
|
def.path = mapPath;
|
|
def.params = sizeValues;
|
|
modelDefs[entryKey] = def;
|
|
if (GetModelId(entryKey) == 0) {
|
|
if (typeValue == "heightmap") {
|
|
LoadHeightmap(entryKey, mapPath, sizeValues[0], sizeValues[1], sizeValues[2]);
|
|
} else {
|
|
LoadCubicmap(entryKey, mapPath, sizeValues[0], sizeValues[1], sizeValues[2]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} else if (section == Section::Material) {
|
|
MaterialDefinition def;
|
|
std::vector<float> albedoVals;
|
|
|
|
auto itShaderKey = fields.find("shader_key");
|
|
if (itShaderKey != fields.end()) {
|
|
std::string shaderKey;
|
|
if (ParseStringAsset(itShaderKey->second, shaderKey)) {
|
|
def.shaderKey = shaderKey;
|
|
def.hasShaderKey = true;
|
|
}
|
|
}
|
|
|
|
auto itShaderId = fields.find("shader_id");
|
|
if (!def.hasShaderKey && itShaderId != fields.end()) {
|
|
try {
|
|
def.shaderId = static_cast<unsigned int>(std::stoul(TrimAsset(itShaderId->second), nullptr, 0));
|
|
def.hasShaderId = def.shaderId > 0;
|
|
} catch (...) {
|
|
}
|
|
}
|
|
|
|
auto itAlbedo = fields.find("albedo");
|
|
if (itAlbedo != fields.end() && ParseFloatList(itAlbedo->second, albedoVals) && albedoVals.size() >= 4) {
|
|
def.albedo = {
|
|
static_cast<unsigned char>(std::clamp(albedoVals[0], 0.0f, 255.0f)),
|
|
static_cast<unsigned char>(std::clamp(albedoVals[1], 0.0f, 255.0f)),
|
|
static_cast<unsigned char>(std::clamp(albedoVals[2], 0.0f, 255.0f)),
|
|
static_cast<unsigned char>(std::clamp(albedoVals[3], 0.0f, 255.0f))
|
|
};
|
|
}
|
|
|
|
materialDefs[entryKey] = def;
|
|
|
|
if (GetMaterialId(entryKey) == 0) {
|
|
unsigned int shaderId = 0;
|
|
if (def.hasShaderKey) {
|
|
shaderId = GetShaderId(def.shaderKey);
|
|
} else if (def.hasShaderId) {
|
|
shaderId = def.shaderId;
|
|
}
|
|
CreateMaterial(entryKey, shaderId, def.albedo);
|
|
materialDefs[entryKey] = def;
|
|
}
|
|
}
|
|
|
|
entryKey.clear();
|
|
fields.clear();
|
|
};
|
|
|
|
std::string line;
|
|
std::istringstream file(contents);
|
|
while (std::getline(file, line)) {
|
|
line = StripCommentAsset(line);
|
|
line = TrimAsset(line);
|
|
if (line.empty()) {
|
|
continue;
|
|
}
|
|
|
|
if (line == "[[texture]]") {
|
|
flushEntry();
|
|
section = Section::Texture;
|
|
continue;
|
|
}
|
|
if (line == "[[shader]]") {
|
|
flushEntry();
|
|
section = Section::Shader;
|
|
continue;
|
|
}
|
|
if (line == "[[model]]") {
|
|
flushEntry();
|
|
section = Section::Model;
|
|
continue;
|
|
}
|
|
if (line == "[[material]]") {
|
|
flushEntry();
|
|
section = Section::Material;
|
|
continue;
|
|
}
|
|
|
|
size_t eq = line.find('=');
|
|
if (eq == std::string::npos) {
|
|
continue;
|
|
}
|
|
|
|
std::string key = TrimAsset(line.substr(0, eq));
|
|
std::string value = TrimAsset(line.substr(eq + 1));
|
|
if (key == "key") {
|
|
ParseStringAsset(value, entryKey);
|
|
} else {
|
|
fields[key] = value;
|
|
}
|
|
}
|
|
|
|
flushEntry();
|
|
return true;
|
|
}
|