702 lines
34 KiB
C++
702 lines
34 KiB
C++
#include "ProjectManager.h"
|
|
#include "Rendering.h"
|
|
#include "ModelLoader.h"
|
|
|
|
// Project implementation
|
|
Project::Project(const std::string& projectName, const fs::path& basePath)
|
|
: name(projectName) {
|
|
projectPath = basePath / projectName;
|
|
scenesPath = projectPath / "Scenes";
|
|
assetsPath = projectPath / "Assets";
|
|
scriptsPath = projectPath / "Scripts";
|
|
scriptsConfigPath = projectPath / "Scripts.modu";
|
|
}
|
|
|
|
bool Project::create() {
|
|
try {
|
|
fs::create_directories(projectPath);
|
|
fs::create_directories(scenesPath);
|
|
fs::create_directories(assetsPath);
|
|
fs::create_directories(assetsPath / "Textures");
|
|
fs::create_directories(assetsPath / "Models");
|
|
fs::create_directories(assetsPath / "Shaders");
|
|
fs::create_directories(scriptsPath);
|
|
fs::create_directories(projectPath / "Cache" / "ScriptBin");
|
|
|
|
saveProjectFile();
|
|
|
|
// Initialize a default scripting build file
|
|
fs::path engineRoot = fs::current_path();
|
|
std::ofstream scriptCfg(scriptsConfigPath);
|
|
scriptCfg << "# Scripts.modu\n";
|
|
scriptCfg << "cppStandard=c++20\n";
|
|
scriptCfg << "scriptsDir=Scripts\n";
|
|
scriptCfg << "outDir=Cache/ScriptBin\n";
|
|
scriptCfg << "includeDir=" << (engineRoot / "src").string() << "\n";
|
|
scriptCfg << "includeDir=" << (engineRoot / "include").string() << "\n";
|
|
scriptCfg << "includeDir=" << (engineRoot / "src/ThirdParty").string() << "\n";
|
|
scriptCfg << "includeDir=" << (engineRoot / "src/ThirdParty/glm").string() << "\n";
|
|
scriptCfg << "define=MODU_SCRIPTING=1\n";
|
|
scriptCfg << "define=MODU_PROJECT_NAME=\"" << name << "\"\n";
|
|
scriptCfg << "linux.linkLib=pthread\n";
|
|
scriptCfg << "linux.linkLib=dl\n";
|
|
scriptCfg << "win.linkLib=User32.lib\n";
|
|
scriptCfg << "win.linkLib=Advapi32.lib\n";
|
|
scriptCfg.close();
|
|
|
|
currentSceneName = "Main";
|
|
isLoaded = true;
|
|
return true;
|
|
} catch (const std::exception& e) {
|
|
std::cerr << "Failed to create project: " << e.what() << std::endl;
|
|
return false;
|
|
}
|
|
}
|
|
|
|
bool Project::load(const fs::path& projectFilePath) {
|
|
try {
|
|
projectPath = projectFilePath.parent_path();
|
|
scenesPath = projectPath / "Scenes";
|
|
assetsPath = projectPath / "Assets";
|
|
scriptsPath = projectPath / "Scripts";
|
|
scriptsConfigPath = projectPath / "Scripts.modu";
|
|
|
|
std::ifstream file(projectFilePath);
|
|
if (!file.is_open()) return false;
|
|
|
|
std::string line;
|
|
while (std::getline(file, line)) {
|
|
if (line.find("name=") == 0) {
|
|
name = line.substr(5);
|
|
} else if (line.find("lastScene=") == 0) {
|
|
currentSceneName = line.substr(10);
|
|
}
|
|
}
|
|
file.close();
|
|
|
|
if (currentSceneName.empty()) {
|
|
currentSceneName = "Main";
|
|
}
|
|
|
|
isLoaded = true;
|
|
return true;
|
|
} catch (const std::exception& e) {
|
|
std::cerr << "Failed to load project: " << e.what() << std::endl;
|
|
return false;
|
|
}
|
|
}
|
|
|
|
void Project::saveProjectFile() const {
|
|
std::ofstream file(projectPath / "project.modu");
|
|
file << "name=" << name << "\n";
|
|
file << "lastScene=" << currentSceneName << "\n";
|
|
file.close();
|
|
}
|
|
|
|
std::vector<std::string> Project::getSceneList() const {
|
|
std::vector<std::string> scenes;
|
|
try {
|
|
for (const auto& entry : fs::directory_iterator(scenesPath)) {
|
|
if (entry.path().extension() == ".scene") {
|
|
scenes.push_back(entry.path().stem().string());
|
|
}
|
|
}
|
|
} catch (...) {}
|
|
return scenes;
|
|
}
|
|
|
|
fs::path Project::getSceneFilePath(const std::string& sceneName) const {
|
|
return scenesPath / (sceneName + ".scene");
|
|
}
|
|
|
|
// ProjectManager implementation
|
|
ProjectManager::ProjectManager() {
|
|
#ifdef _WIN32
|
|
const char* appdata = std::getenv("APPDATA");
|
|
if (appdata) {
|
|
appDataPath = fs::path(appdata) / ".Modularity";
|
|
} else {
|
|
appDataPath = fs::current_path() / "AppData";
|
|
}
|
|
#else
|
|
const char* home = std::getenv("HOME");
|
|
if (home) {
|
|
appDataPath = fs::path(home) / ".Modularity";
|
|
} else {
|
|
appDataPath = fs::current_path() / ".Modularity";
|
|
}
|
|
#endif
|
|
|
|
fs::create_directories(appDataPath);
|
|
loadRecentProjects();
|
|
|
|
std::string defaultPath = (fs::current_path() / "Projects").string();
|
|
strncpy(newProjectLocation, defaultPath.c_str(), sizeof(newProjectLocation) - 1);
|
|
}
|
|
|
|
void ProjectManager::loadRecentProjects() {
|
|
recentProjects.clear();
|
|
fs::path recentFile = appDataPath / "recent_projects.txt";
|
|
|
|
std::cerr << "[DEBUG] Loading recent projects from: " << recentFile << std::endl;
|
|
|
|
if (!fs::exists(recentFile)) {
|
|
std::cerr << "[DEBUG] Recent projects file does not exist" << std::endl;
|
|
return;
|
|
}
|
|
|
|
std::ifstream file(recentFile);
|
|
std::string line;
|
|
int lineNum = 0;
|
|
while (std::getline(file, line)) {
|
|
lineNum++;
|
|
if (line.empty()) continue;
|
|
|
|
line.erase(0, line.find_first_not_of(" \t\r\n"));
|
|
line.erase(line.find_last_not_of(" \t\r\n") + 1);
|
|
if (line.empty()) continue;
|
|
|
|
RecentProject rp;
|
|
size_t pos1 = line.find('|');
|
|
size_t pos2 = line.find('|', pos1 + 1);
|
|
|
|
if (pos1 != std::string::npos && pos2 != std::string::npos) {
|
|
rp.name = line.substr(0, pos1);
|
|
rp.path = line.substr(pos1 + 1, pos2 - pos1 - 1);
|
|
rp.lastOpened = line.substr(pos2 + 1);
|
|
|
|
rp.path.erase(0, rp.path.find_first_not_of(" \t\r\n"));
|
|
rp.path.erase(rp.path.find_last_not_of(" \t\r\n") + 1);
|
|
|
|
std::cerr << "[DEBUG] Line " << lineNum << ": name='" << rp.name
|
|
<< "' path='" << rp.path << "' exists=" << fs::exists(rp.path) << std::endl;
|
|
|
|
if (fs::exists(rp.path)) {
|
|
recentProjects.push_back(rp);
|
|
} else {
|
|
std::cerr << "[DEBUG] Project path does not exist, skipping: " << rp.path << std::endl;
|
|
}
|
|
} else {
|
|
std::cerr << "[DEBUG] Line " << lineNum << " malformed: " << line << std::endl;
|
|
}
|
|
}
|
|
file.close();
|
|
|
|
std::cerr << "[DEBUG] Loaded " << recentProjects.size() << " recent projects" << std::endl;
|
|
}
|
|
|
|
void ProjectManager::saveRecentProjects() {
|
|
fs::path recentFile = appDataPath / "recent_projects.txt";
|
|
std::cerr << "[DEBUG] Saving recent projects to: " << recentFile << std::endl;
|
|
|
|
std::ofstream file(recentFile);
|
|
|
|
for (const auto& rp : recentProjects) {
|
|
std::string absolutePath = rp.path;
|
|
try {
|
|
if (fs::exists(rp.path)) {
|
|
absolutePath = fs::canonical(rp.path).string();
|
|
}
|
|
} catch (...) {
|
|
// Keep original path if canonical fails
|
|
}
|
|
file << rp.name << "|" << absolutePath << "|" << rp.lastOpened << "\n";
|
|
std::cerr << "[DEBUG] Saved: " << rp.name << " -> " << absolutePath << std::endl;
|
|
}
|
|
file.close();
|
|
}
|
|
|
|
void ProjectManager::addToRecentProjects(const std::string& name, const std::string& path) {
|
|
std::string absolutePath = path;
|
|
try {
|
|
if (fs::exists(path)) {
|
|
absolutePath = fs::canonical(path).string();
|
|
} else {
|
|
// For new projects, the file might not exist yet - use absolute()
|
|
absolutePath = fs::absolute(path).string();
|
|
}
|
|
} catch (...) {
|
|
// Keep original path if conversion fails
|
|
}
|
|
|
|
std::cerr << "[DEBUG] Adding to recent: " << name << " -> " << absolutePath << std::endl;
|
|
|
|
recentProjects.erase(
|
|
std::remove_if(recentProjects.begin(), recentProjects.end(),
|
|
[&absolutePath](const RecentProject& rp) { return rp.path == absolutePath; }),
|
|
recentProjects.end()
|
|
);
|
|
|
|
std::time_t now = std::time(nullptr);
|
|
char timeStr[64];
|
|
std::strftime(timeStr, sizeof(timeStr), "%Y-%m-%d %H:%M", std::localtime(&now));
|
|
|
|
RecentProject rp;
|
|
rp.name = name;
|
|
rp.path = absolutePath; // Use absolute path
|
|
rp.lastOpened = timeStr;
|
|
recentProjects.insert(recentProjects.begin(), rp);
|
|
|
|
saveRecentProjects();
|
|
}
|
|
|
|
bool ProjectManager::loadProject(const std::string& path) {
|
|
if (currentProject.load(path)) {
|
|
addToRecentProjects(currentProject.name, path);
|
|
return true;
|
|
}
|
|
errorMessage = "Failed to load project file";
|
|
return false;
|
|
}
|
|
|
|
// SceneSerializer implementation
|
|
bool SceneSerializer::saveScene(const fs::path& filePath,
|
|
const std::vector<SceneObject>& objects,
|
|
int nextId) {
|
|
try {
|
|
std::ofstream file(filePath);
|
|
if (!file.is_open()) return false;
|
|
|
|
file << "# Scene File\n";
|
|
file << "version=7\n";
|
|
file << "nextId=" << nextId << "\n";
|
|
file << "objectCount=" << objects.size() << "\n";
|
|
file << "\n";
|
|
|
|
for (const auto& obj : objects) {
|
|
file << "[Object]\n";
|
|
file << "id=" << obj.id << "\n";
|
|
file << "name=" << obj.name << "\n";
|
|
file << "type=" << static_cast<int>(obj.type) << "\n";
|
|
file << "parentId=" << obj.parentId << "\n";
|
|
file << "position=" << obj.position.x << "," << obj.position.y << "," << obj.position.z << "\n";
|
|
file << "rotation=" << obj.rotation.x << "," << obj.rotation.y << "," << obj.rotation.z << "\n";
|
|
file << "scale=" << obj.scale.x << "," << obj.scale.y << "," << obj.scale.z << "\n";
|
|
file << "hasRigidbody=" << (obj.hasRigidbody ? 1 : 0) << "\n";
|
|
if (obj.hasRigidbody) {
|
|
file << "rbEnabled=" << (obj.rigidbody.enabled ? 1 : 0) << "\n";
|
|
file << "rbMass=" << obj.rigidbody.mass << "\n";
|
|
file << "rbUseGravity=" << (obj.rigidbody.useGravity ? 1 : 0) << "\n";
|
|
file << "rbKinematic=" << (obj.rigidbody.isKinematic ? 1 : 0) << "\n";
|
|
file << "rbLinearDamping=" << obj.rigidbody.linearDamping << "\n";
|
|
file << "rbAngularDamping=" << obj.rigidbody.angularDamping << "\n";
|
|
}
|
|
file << "hasCollider=" << (obj.hasCollider ? 1 : 0) << "\n";
|
|
if (obj.hasCollider) {
|
|
file << "colliderEnabled=" << (obj.collider.enabled ? 1 : 0) << "\n";
|
|
file << "colliderType=" << static_cast<int>(obj.collider.type) << "\n";
|
|
file << "colliderBox=" << obj.collider.boxSize.x << "," << obj.collider.boxSize.y << "," << obj.collider.boxSize.z << "\n";
|
|
file << "colliderConvex=" << (obj.collider.convex ? 1 : 0) << "\n";
|
|
}
|
|
file << "hasPlayerController=" << (obj.hasPlayerController ? 1 : 0) << "\n";
|
|
if (obj.hasPlayerController) {
|
|
file << "pcEnabled=" << (obj.playerController.enabled ? 1 : 0) << "\n";
|
|
file << "pcMoveSpeed=" << obj.playerController.moveSpeed << "\n";
|
|
file << "pcLookSensitivity=" << obj.playerController.lookSensitivity << "\n";
|
|
file << "pcHeight=" << obj.playerController.height << "\n";
|
|
file << "pcRadius=" << obj.playerController.radius << "\n";
|
|
file << "pcJumpStrength=" << obj.playerController.jumpStrength << "\n";
|
|
}
|
|
file << "materialColor=" << obj.material.color.r << "," << obj.material.color.g << "," << obj.material.color.b << "\n";
|
|
file << "materialAmbient=" << obj.material.ambientStrength << "\n";
|
|
file << "materialSpecular=" << obj.material.specularStrength << "\n";
|
|
file << "materialShininess=" << obj.material.shininess << "\n";
|
|
file << "materialTextureMix=" << obj.material.textureMix << "\n";
|
|
file << "materialPath=" << obj.materialPath << "\n";
|
|
file << "albedoTex=" << obj.albedoTexturePath << "\n";
|
|
file << "overlayTex=" << obj.overlayTexturePath << "\n";
|
|
file << "normalMap=" << obj.normalMapPath << "\n";
|
|
file << "vertexShader=" << obj.vertexShaderPath << "\n";
|
|
file << "fragmentShader=" << obj.fragmentShaderPath << "\n";
|
|
file << "useOverlay=" << (obj.useOverlay ? 1 : 0) << "\n";
|
|
file << "additionalMaterialCount=" << obj.additionalMaterialPaths.size() << "\n";
|
|
for (size_t mi = 0; mi < obj.additionalMaterialPaths.size(); ++mi) {
|
|
file << "additionalMaterial" << mi << "=" << obj.additionalMaterialPaths[mi] << "\n";
|
|
}
|
|
file << "scripts=" << obj.scripts.size() << "\n";
|
|
for (size_t si = 0; si < obj.scripts.size(); ++si) {
|
|
const auto& sc = obj.scripts[si];
|
|
file << "script" << si << "_path=" << sc.path << "\n";
|
|
file << "script" << si << "_settings=" << sc.settings.size() << "\n";
|
|
for (size_t k = 0; k < sc.settings.size(); ++k) {
|
|
file << "script" << si << "_setting" << k << "=" << sc.settings[k].key << ":" << sc.settings[k].value << "\n";
|
|
}
|
|
}
|
|
file << "lightColor=" << obj.light.color.r << "," << obj.light.color.g << "," << obj.light.color.b << "\n";
|
|
file << "lightIntensity=" << obj.light.intensity << "\n";
|
|
file << "lightRange=" << obj.light.range << "\n";
|
|
file << "lightInner=" << obj.light.innerAngle << "\n";
|
|
file << "lightOuter=" << obj.light.outerAngle << "\n";
|
|
file << "lightSize=" << obj.light.size.x << "," << obj.light.size.y << "\n";
|
|
file << "lightEnabled=" << (obj.light.enabled ? 1 : 0) << "\n";
|
|
file << "cameraType=" << static_cast<int>(obj.camera.type) << "\n";
|
|
file << "cameraFov=" << obj.camera.fov << "\n";
|
|
file << "cameraNear=" << obj.camera.nearClip << "\n";
|
|
file << "cameraFar=" << obj.camera.farClip << "\n";
|
|
if (obj.type == ObjectType::PostFXNode) {
|
|
file << "postEnabled=" << (obj.postFx.enabled ? 1 : 0) << "\n";
|
|
file << "postBloomEnabled=" << (obj.postFx.bloomEnabled ? 1 : 0) << "\n";
|
|
file << "postBloomThreshold=" << obj.postFx.bloomThreshold << "\n";
|
|
file << "postBloomIntensity=" << obj.postFx.bloomIntensity << "\n";
|
|
file << "postBloomRadius=" << obj.postFx.bloomRadius << "\n";
|
|
file << "postColorAdjustEnabled=" << (obj.postFx.colorAdjustEnabled ? 1 : 0) << "\n";
|
|
file << "postExposure=" << obj.postFx.exposure << "\n";
|
|
file << "postContrast=" << obj.postFx.contrast << "\n";
|
|
file << "postSaturation=" << obj.postFx.saturation << "\n";
|
|
file << "postColorFilter=" << obj.postFx.colorFilter.r << "," << obj.postFx.colorFilter.g << "," << obj.postFx.colorFilter.b << "\n";
|
|
file << "postMotionBlurEnabled=" << (obj.postFx.motionBlurEnabled ? 1 : 0) << "\n";
|
|
file << "postMotionBlurStrength=" << obj.postFx.motionBlurStrength << "\n";
|
|
file << "postVignetteEnabled=" << (obj.postFx.vignetteEnabled ? 1 : 0) << "\n";
|
|
file << "postVignetteIntensity=" << obj.postFx.vignetteIntensity << "\n";
|
|
file << "postVignetteSmoothness=" << obj.postFx.vignetteSmoothness << "\n";
|
|
file << "postChromaticEnabled=" << (obj.postFx.chromaticAberrationEnabled ? 1 : 0) << "\n";
|
|
file << "postChromaticAmount=" << obj.postFx.chromaticAmount << "\n";
|
|
file << "postAOEnabled=" << (obj.postFx.ambientOcclusionEnabled ? 1 : 0) << "\n";
|
|
file << "postAORadius=" << obj.postFx.aoRadius << "\n";
|
|
file << "postAOStrength=" << obj.postFx.aoStrength << "\n";
|
|
}
|
|
|
|
file << "scriptCount=" << obj.scripts.size() << "\n";
|
|
for (size_t s = 0; s < obj.scripts.size(); ++s) {
|
|
const auto& sc = obj.scripts[s];
|
|
file << "script" << s << "_path=" << sc.path << "\n";
|
|
file << "script" << s << "_settingCount=" << sc.settings.size() << "\n";
|
|
for (size_t si = 0; si < sc.settings.size(); ++si) {
|
|
file << "script" << s << "_setting" << si << "=" << sc.settings[si].key << ":" << sc.settings[si].value << "\n";
|
|
}
|
|
}
|
|
|
|
if ((obj.type == ObjectType::OBJMesh || obj.type == ObjectType::Model) && !obj.meshPath.empty()) {
|
|
file << "meshPath=" << obj.meshPath << "\n";
|
|
}
|
|
|
|
file << "children=";
|
|
for (size_t i = 0; i < obj.childIds.size(); i++) {
|
|
if (i > 0) file << ",";
|
|
file << obj.childIds[i];
|
|
}
|
|
file << "\n\n";
|
|
}
|
|
|
|
file.close();
|
|
return true;
|
|
} catch (const std::exception& e) {
|
|
std::cerr << "Failed to save scene: " << e.what() << std::endl;
|
|
return false;
|
|
}
|
|
}
|
|
|
|
bool SceneSerializer::loadScene(const fs::path& filePath,
|
|
std::vector<SceneObject>& objects,
|
|
int& nextId) {
|
|
try {
|
|
std::ifstream file(filePath);
|
|
if (!file.is_open()) return false;
|
|
|
|
objects.clear();
|
|
std::string line;
|
|
SceneObject* currentObj = nullptr;
|
|
|
|
while (std::getline(file, line)) {
|
|
size_t first = line.find_first_not_of(" \t\r\n");
|
|
if (first == std::string::npos) {
|
|
continue;
|
|
}
|
|
line.erase(0, first);
|
|
size_t last = line.find_last_not_of(" \t\r\n");
|
|
if (last != std::string::npos) {
|
|
line.erase(last + 1);
|
|
} else {
|
|
continue;
|
|
}
|
|
|
|
if (line.empty() || line[0] == '#') continue;
|
|
|
|
if (line == "[Object]") {
|
|
objects.push_back(SceneObject("", ObjectType::Cube, 0));
|
|
currentObj = &objects.back();
|
|
continue;
|
|
}
|
|
|
|
size_t eqPos = line.find('=');
|
|
if (eqPos == std::string::npos) continue;
|
|
|
|
std::string key = line.substr(0, eqPos);
|
|
std::string value = line.substr(eqPos + 1);
|
|
|
|
if (key == "nextId") {
|
|
nextId = std::stoi(value);
|
|
} else if (currentObj) {
|
|
if (key == "id") {
|
|
currentObj->id = std::stoi(value);
|
|
} else if (key == "name") {
|
|
currentObj->name = value;
|
|
} else if (key == "type") {
|
|
currentObj->type = static_cast<ObjectType>(std::stoi(value));
|
|
if (currentObj->type == ObjectType::DirectionalLight) currentObj->light.type = LightType::Directional;
|
|
else if (currentObj->type == ObjectType::PointLight) currentObj->light.type = LightType::Point;
|
|
else if (currentObj->type == ObjectType::SpotLight) currentObj->light.type = LightType::Spot;
|
|
else if (currentObj->type == ObjectType::AreaLight) currentObj->light.type = LightType::Area;
|
|
else if (currentObj->type == ObjectType::Camera) {
|
|
currentObj->camera.type = SceneCameraType::Scene;
|
|
}
|
|
} else if (key == "parentId") {
|
|
currentObj->parentId = std::stoi(value);
|
|
} else if (key == "position") {
|
|
sscanf(value.c_str(), "%f,%f,%f",
|
|
¤tObj->position.x,
|
|
¤tObj->position.y,
|
|
¤tObj->position.z);
|
|
} else if (key == "rotation") {
|
|
sscanf(value.c_str(), "%f,%f,%f",
|
|
¤tObj->rotation.x,
|
|
¤tObj->rotation.y,
|
|
¤tObj->rotation.z);
|
|
currentObj->rotation = NormalizeEulerDegrees(currentObj->rotation);
|
|
} else if (key == "scale") {
|
|
sscanf(value.c_str(), "%f,%f,%f",
|
|
¤tObj->scale.x,
|
|
¤tObj->scale.y,
|
|
¤tObj->scale.z);
|
|
} else if (key == "hasRigidbody") {
|
|
currentObj->hasRigidbody = std::stoi(value) != 0;
|
|
} else if (key == "rbEnabled") {
|
|
currentObj->rigidbody.enabled = std::stoi(value) != 0;
|
|
} else if (key == "rbMass") {
|
|
currentObj->rigidbody.mass = std::stof(value);
|
|
} else if (key == "rbUseGravity") {
|
|
currentObj->rigidbody.useGravity = std::stoi(value) != 0;
|
|
} else if (key == "rbKinematic") {
|
|
currentObj->rigidbody.isKinematic = std::stoi(value) != 0;
|
|
} else if (key == "rbLinearDamping") {
|
|
currentObj->rigidbody.linearDamping = std::stof(value);
|
|
} else if (key == "rbAngularDamping") {
|
|
currentObj->rigidbody.angularDamping = std::stof(value);
|
|
} else if (key == "hasCollider") {
|
|
currentObj->hasCollider = std::stoi(value) != 0;
|
|
} else if (key == "colliderEnabled") {
|
|
currentObj->collider.enabled = std::stoi(value) != 0;
|
|
} else if (key == "colliderType") {
|
|
currentObj->collider.type = static_cast<ColliderType>(std::stoi(value));
|
|
} else if (key == "colliderBox") {
|
|
sscanf(value.c_str(), "%f,%f,%f",
|
|
¤tObj->collider.boxSize.x,
|
|
¤tObj->collider.boxSize.y,
|
|
¤tObj->collider.boxSize.z);
|
|
} else if (key == "colliderConvex") {
|
|
currentObj->collider.convex = std::stoi(value) != 0;
|
|
} else if (key == "hasPlayerController") {
|
|
currentObj->hasPlayerController = std::stoi(value) != 0;
|
|
} else if (key == "pcEnabled") {
|
|
currentObj->playerController.enabled = std::stoi(value) != 0;
|
|
} else if (key == "pcMoveSpeed") {
|
|
currentObj->playerController.moveSpeed = std::stof(value);
|
|
} else if (key == "pcLookSensitivity") {
|
|
currentObj->playerController.lookSensitivity = std::stof(value);
|
|
} else if (key == "pcHeight") {
|
|
currentObj->playerController.height = std::stof(value);
|
|
} else if (key == "pcRadius") {
|
|
currentObj->playerController.radius = std::stof(value);
|
|
} else if (key == "pcJumpStrength") {
|
|
currentObj->playerController.jumpStrength = std::stof(value);
|
|
} else if (key == "materialColor") {
|
|
sscanf(value.c_str(), "%f,%f,%f",
|
|
¤tObj->material.color.r,
|
|
¤tObj->material.color.g,
|
|
¤tObj->material.color.b);
|
|
} else if (key == "materialAmbient") {
|
|
currentObj->material.ambientStrength = std::stof(value);
|
|
} else if (key == "materialSpecular") {
|
|
currentObj->material.specularStrength = std::stof(value);
|
|
} else if (key == "materialShininess") {
|
|
currentObj->material.shininess = std::stof(value);
|
|
} else if (key == "materialTextureMix") {
|
|
currentObj->material.textureMix = std::stof(value);
|
|
} else if (key == "materialPath") {
|
|
currentObj->materialPath = value;
|
|
} else if (key == "albedoTex") {
|
|
currentObj->albedoTexturePath = value;
|
|
} else if (key == "overlayTex") {
|
|
currentObj->overlayTexturePath = value;
|
|
} else if (key == "normalMap") {
|
|
currentObj->normalMapPath = value;
|
|
} else if (key == "vertexShader") {
|
|
currentObj->vertexShaderPath = value;
|
|
} else if (key == "fragmentShader") {
|
|
currentObj->fragmentShaderPath = value;
|
|
} else if (key == "useOverlay") {
|
|
currentObj->useOverlay = (std::stoi(value) != 0);
|
|
} else if (key == "additionalMaterialCount") {
|
|
int count = std::stoi(value);
|
|
currentObj->additionalMaterialPaths.resize(std::max(0, count));
|
|
} else if (key.rfind("additionalMaterial", 0) == 0) {
|
|
int idx = std::stoi(key.substr(18)); // length of "additionalMaterial"
|
|
if (idx >= 0 && idx < (int)currentObj->additionalMaterialPaths.size()) {
|
|
currentObj->additionalMaterialPaths[idx] = value;
|
|
}
|
|
} else if (key == "scripts") {
|
|
int count = std::stoi(value);
|
|
currentObj->scripts.resize(std::max(0, count));
|
|
} else if (key.rfind("script", 0) == 0) {
|
|
size_t underscore = key.find('_');
|
|
if (underscore != std::string::npos && underscore > 6) {
|
|
int idx = std::stoi(key.substr(6, underscore - 6));
|
|
if (idx >= 0 && idx < (int)currentObj->scripts.size()) {
|
|
std::string sub = key.substr(underscore + 1);
|
|
ScriptComponent& sc = currentObj->scripts[idx];
|
|
if (sub == "path") {
|
|
sc.path = value;
|
|
} else if (sub == "settings" || sub == "settingCount") {
|
|
int cnt = std::stoi(value);
|
|
sc.settings.resize(std::max(0, cnt));
|
|
} else if (sub.rfind("setting", 0) == 0) {
|
|
std::string idxStr = sub.substr(7);
|
|
if (!idxStr.empty() && std::all_of(idxStr.begin(), idxStr.end(), ::isdigit)) {
|
|
int sIdx = std::stoi(idxStr);
|
|
if (sIdx >= 0 && sIdx < (int)sc.settings.size()) {
|
|
size_t sep = value.find(':');
|
|
if (sep != std::string::npos) {
|
|
sc.settings[sIdx].key = value.substr(0, sep);
|
|
sc.settings[sIdx].value = value.substr(sep + 1);
|
|
} else {
|
|
sc.settings[sIdx].value = value;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} else if (key == "lightColor") {
|
|
sscanf(value.c_str(), "%f,%f,%f",
|
|
¤tObj->light.color.r,
|
|
¤tObj->light.color.g,
|
|
¤tObj->light.color.b);
|
|
} else if (key == "lightIntensity") {
|
|
currentObj->light.intensity = std::stof(value);
|
|
} else if (key == "lightRange") {
|
|
currentObj->light.range = std::stof(value);
|
|
} else if (key == "lightInner") {
|
|
currentObj->light.innerAngle = std::stof(value);
|
|
} else if (key == "lightOuter") {
|
|
currentObj->light.outerAngle = std::stof(value);
|
|
} else if (key == "lightSize") {
|
|
sscanf(value.c_str(), "%f,%f",
|
|
¤tObj->light.size.x,
|
|
¤tObj->light.size.y);
|
|
} else if (key == "lightEnabled") {
|
|
currentObj->light.enabled = (std::stoi(value) != 0);
|
|
} else if (key == "cameraType") {
|
|
currentObj->camera.type = static_cast<SceneCameraType>(std::stoi(value));
|
|
} else if (key == "cameraFov") {
|
|
currentObj->camera.fov = std::stof(value);
|
|
} else if (key == "cameraNear") {
|
|
currentObj->camera.nearClip = std::stof(value);
|
|
} else if (key == "cameraFar") {
|
|
currentObj->camera.farClip = std::stof(value);
|
|
} else if (key == "postEnabled") {
|
|
currentObj->postFx.enabled = (std::stoi(value) != 0);
|
|
} else if (key == "postBloomEnabled") {
|
|
currentObj->postFx.bloomEnabled = (std::stoi(value) != 0);
|
|
} else if (key == "postBloomThreshold") {
|
|
currentObj->postFx.bloomThreshold = std::stof(value);
|
|
} else if (key == "postBloomIntensity") {
|
|
currentObj->postFx.bloomIntensity = std::stof(value);
|
|
} else if (key == "postBloomRadius") {
|
|
currentObj->postFx.bloomRadius = std::stof(value);
|
|
} else if (key == "postColorAdjustEnabled") {
|
|
currentObj->postFx.colorAdjustEnabled = (std::stoi(value) != 0);
|
|
} else if (key == "postExposure") {
|
|
currentObj->postFx.exposure = std::stof(value);
|
|
} else if (key == "postContrast") {
|
|
currentObj->postFx.contrast = std::stof(value);
|
|
} else if (key == "postSaturation") {
|
|
currentObj->postFx.saturation = std::stof(value);
|
|
} else if (key == "postColorFilter") {
|
|
sscanf(value.c_str(), "%f,%f,%f",
|
|
¤tObj->postFx.colorFilter.r,
|
|
¤tObj->postFx.colorFilter.g,
|
|
¤tObj->postFx.colorFilter.b);
|
|
} else if (key == "postMotionBlurEnabled") {
|
|
currentObj->postFx.motionBlurEnabled = (std::stoi(value) != 0);
|
|
} else if (key == "postMotionBlurStrength") {
|
|
currentObj->postFx.motionBlurStrength = std::stof(value);
|
|
} else if (key == "postVignetteEnabled") {
|
|
currentObj->postFx.vignetteEnabled = (std::stoi(value) != 0);
|
|
} else if (key == "postVignetteIntensity") {
|
|
currentObj->postFx.vignetteIntensity = std::stof(value);
|
|
} else if (key == "postVignetteSmoothness") {
|
|
currentObj->postFx.vignetteSmoothness = std::stof(value);
|
|
} else if (key == "postChromaticEnabled") {
|
|
currentObj->postFx.chromaticAberrationEnabled = (std::stoi(value) != 0);
|
|
} else if (key == "postChromaticAmount") {
|
|
currentObj->postFx.chromaticAmount = std::stof(value);
|
|
} else if (key == "postAOEnabled") {
|
|
currentObj->postFx.ambientOcclusionEnabled = (std::stoi(value) != 0);
|
|
} else if (key == "postAORadius") {
|
|
currentObj->postFx.aoRadius = std::stof(value);
|
|
} else if (key == "postAOStrength") {
|
|
currentObj->postFx.aoStrength = std::stof(value);
|
|
} else if (key == "scriptCount") {
|
|
int count = std::stoi(value);
|
|
currentObj->scripts.resize(std::max(0, count));
|
|
} else if (key.rfind("script", 0) == 0) {
|
|
size_t underscore = key.find('_');
|
|
if (underscore != std::string::npos && underscore > 6) {
|
|
int idx = std::stoi(key.substr(6, underscore - 6));
|
|
if (idx >= 0 && idx < (int)currentObj->scripts.size()) {
|
|
std::string subKey = key.substr(underscore + 1);
|
|
ScriptComponent& sc = currentObj->scripts[idx];
|
|
if (subKey == "path") {
|
|
sc.path = value;
|
|
} else if (subKey == "settingCount") {
|
|
int cnt = std::stoi(value);
|
|
sc.settings.resize(std::max(0, cnt));
|
|
} else if (subKey.rfind("setting", 0) == 0) {
|
|
int sIdx = std::stoi(subKey.substr(7));
|
|
if (sIdx >= 0 && sIdx < (int)sc.settings.size()) {
|
|
size_t sep = value.find(':');
|
|
if (sep != std::string::npos) {
|
|
sc.settings[sIdx].key = value.substr(0, sep);
|
|
sc.settings[sIdx].value = value.substr(sep + 1);
|
|
} else {
|
|
sc.settings[sIdx].key.clear();
|
|
sc.settings[sIdx].value = value;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} else if (key == "meshPath") {
|
|
currentObj->meshPath = value;
|
|
if (!value.empty() && currentObj->type == ObjectType::OBJMesh) {
|
|
std::string err;
|
|
currentObj->meshId = g_objLoader.loadOBJ(value, err);
|
|
} else if (!value.empty() && currentObj->type == ObjectType::Model) {
|
|
ModelLoadResult result = getModelLoader().loadModel(value);
|
|
if (result.success) {
|
|
currentObj->meshId = result.meshIndex;
|
|
} else {
|
|
std::cerr << "Failed to load model from scene: " << result.errorMessage << std::endl;
|
|
currentObj->meshId = -1;
|
|
}
|
|
}
|
|
} else if (key == "children" && !value.empty()) {
|
|
std::stringstream ss(value);
|
|
std::string item;
|
|
while (std::getline(ss, item, ',')) {
|
|
if (!item.empty()) {
|
|
currentObj->childIds.push_back(std::stoi(item));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
file.close();
|
|
return true;
|
|
} catch (const std::exception& e) {
|
|
std::cerr << "Failed to load scene: " << e.what() << std::endl;
|
|
return false;
|
|
}
|
|
}
|