#include "AssetManager.h" #include "ModelManager.h" #include "ShaderManager.h" #include "MaterialManager.h" #include "TextureManager.h" #include "PhysFSManager.h" #include "log/log.h" #include #include #include #include namespace { std::string TrimAsset(const std::string& value) { size_t start = 0; while (start < value.size() && std::isspace(static_cast(value[start]))) { start++; } size_t end = value.size(); while (end > start && std::isspace(static_cast(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& 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& 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(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(rings), static_cast(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(rings), static_cast(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(resX), static_cast(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(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(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(radSeg), static_cast(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(radSeg), static_cast(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 AssetManager::ListTextureKeys() const { return ListKeys(textureAssets); } std::vector AssetManager::ListShaderKeys() const { return ListKeys(shaderAssets); } std::vector AssetManager::ListModelKeys() const { return ListKeys(modelAssets); } std::vector 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& map, unsigned int id) const { static const std::string empty; auto it = map.find(id); return (it != map.end()) ? it->second : empty; } std::vector AssetManager::ListKeys(const std::unordered_map& map) const { std::vector 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 albedo = { static_cast(def.albedo.r), static_cast(def.albedo.g), static_cast(def.albedo.b), static_cast(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 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 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(params[0]), params[1]); } else if (primitive == "sphere" && params.size() >= 3) { LoadSphere(entryKey, params[0], static_cast(params[1]), static_cast(params[2])); } else if (primitive == "hemisphere" && params.size() >= 3) { LoadHemiSphere(entryKey, params[0], static_cast(params[1]), static_cast(params[2])); } else if (primitive == "plane" && params.size() >= 4) { LoadPlane(entryKey, params[0], params[1], static_cast(params[2]), static_cast(params[3])); } else if (primitive == "cylinder" && params.size() >= 3) { LoadCylinder(entryKey, params[0], params[1], static_cast(params[2])); } else if (primitive == "cone" && params.size() >= 3) { LoadCone(entryKey, params[0], params[1], static_cast(params[2])); } else if (primitive == "torus" && params.size() >= 4) { LoadTorus(entryKey, params[0], params[1], static_cast(params[2]), static_cast(params[3])); } else if (primitive == "knot" && params.size() >= 4) { LoadKnot(entryKey, params[0], params[1], static_cast(params[2]), static_cast(params[3])); } } } } else if (typeValue == "heightmap" || typeValue == "cubicmap") { std::string mapPath; std::vector 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 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(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(std::clamp(albedoVals[0], 0.0f, 255.0f)), static_cast(std::clamp(albedoVals[1], 0.0f, 255.0f)), static_cast(std::clamp(albedoVals[2], 0.0f, 255.0f)), static_cast(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; }