diff --git a/code/nel/include/nel/pipeline/project_config.h b/code/nel/include/nel/pipeline/project_config.h
new file mode 100644
index 000000000..11210d5e8
--- /dev/null
+++ b/code/nel/include/nel/pipeline/project_config.h
@@ -0,0 +1,79 @@
+// NeL - MMORPG Framework
+// Copyright (C) 2015 Winch Gate Property Limited
+// Author: Jan Boon
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as
+// published by the Free Software Foundation, either version 3 of the
+// License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with this program. If not, see .
+
+#ifndef NLPIPELINE_PROJECT_CONFIG_H
+#define NLPIPELINE_PROJECT_CONFIG_H
+#include
+
+namespace NLMISC {
+ class CConfigFile;
+}
+
+#ifdef NL_OS_WINDOWS
+#include
+typedef NLMISC::CSString TPathString;
+#else
+typedef std::string TPathString;
+#endif
+
+namespace NLPIPELINE {
+
+/// Asset project configuration. Used to configure lookup directories for tools and buildsite specific setup. Do not use for pipeline build settings
+class CProjectConfig
+{
+public:
+ enum Flags
+ {
+ DatabaseTextureSearchPaths = 0x0001,
+ DatabaseMaterialSearchPaths = 0x0002,
+ RuntimeTextureSearchPaths = 0x0100,
+ RuntimeShapeSearchPaths = 0x0200,
+ };
+
+public:
+ ~CProjectConfig();
+
+ /// Searches for the configuration for the specified asset path by recursively going through all parent directories looking for 'nel.cfg', matches it to a project cfg if partial is not set, initializes and applies the configuration.
+ static bool init(const std::string &asset, Flags flags, bool partial = false);
+ /// Undo init
+ static void release();
+
+private:
+ static void cleanup();
+ static void searchDirectories(const char *var);
+
+ static CProjectConfig s_Instance;
+
+ static uint32 s_AssetConfigModification;
+ static uint32 s_ProjectConfigModification;
+
+ static TPathString s_AssetConfigPath;
+ static TPathString s_ProjectConfigPath;
+
+ static std::string s_ProjectName;
+ static CProjectConfig::Flags CProjectConfig::s_InitFlags;
+
+ static std::vector s_ConfigPaths;
+ static std::vector s_ConfigFiles;
+
+};
+
+} /* namespace NLPIPELINE */
+
+#endif /* #ifndef NLPIPELINE_PROJECT_CONFIG_H */
+
+/* end of file */
diff --git a/code/nel/src/pipeline/project_config.cpp b/code/nel/src/pipeline/project_config.cpp
new file mode 100644
index 000000000..995f92fd4
--- /dev/null
+++ b/code/nel/src/pipeline/project_config.cpp
@@ -0,0 +1,238 @@
+// NeL - MMORPG Framework
+// Copyright (C) 2015 Winch Gate Property Limited
+// Author: Jan Boon
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as
+// published by the Free Software Foundation, either version 3 of the
+// License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with this program. If not, see .
+
+#include
+#include "nel/pipeline/project_config.h"
+
+#ifdef NL_OS_WINDOWS
+# include
+#else
+# include
+#endif
+
+#include
+
+#include
+#include
+#include
+
+using namespace std;
+using namespace NLMISC;
+
+namespace NLPIPELINE {
+
+TPathString CProjectConfig::s_AssetConfigPath;
+TPathString CProjectConfig::s_ProjectConfigPath;
+std::vector CProjectConfig::s_ConfigFiles;
+std::vector CProjectConfig::s_ConfigPaths;
+CProjectConfig CProjectConfig::s_Instance;
+uint32 CProjectConfig::s_AssetConfigModification;
+uint32 CProjectConfig::s_ProjectConfigModification;
+CProjectConfig::Flags CProjectConfig::s_InitFlags = (CProjectConfig::Flags)0;
+std::string CProjectConfig::s_ProjectName;
+
+static std::set s_SearchPaths;
+
+void CProjectConfig::cleanup()
+{
+ for (std::vector::iterator it(s_ConfigFiles.begin()), end(s_ConfigFiles.end()); it != end; ++it)
+ delete *it;
+ s_ConfigFiles.clear();
+}
+
+CProjectConfig::~CProjectConfig()
+{
+ cleanup();
+}
+
+bool CProjectConfig::init(const std::string &asset, Flags flags, bool partial)
+{
+ TPathString rootPath = NLMISC::CPath::standardizePath(asset, false);
+ TPathString configPath = rootPath + "/nel.cfg";
+ while (!CFile::fileExists(configPath))
+ {
+ int sep = CFile::getLastSeparator(rootPath);
+ if (sep == string::npos)
+ return false;
+
+ rootPath = rootPath.substr(0, sep);
+ if (rootPath.empty())
+ return false;
+
+ configPath = rootPath + "/nel.cfg";
+ }
+
+ rootPath += "/";
+ uint32 configFileModification = CFile::getFileModificationDate(configPath);
+ bool assetConfigSame = configPath == s_AssetConfigPath && s_AssetConfigModification == configFileModification && s_InitFlags == flags;
+
+ std::vector configRootPaths;
+ TPathString projectConfigPath;
+ uint32 projectConfigModification;
+ std::string projectName;
+ if (partial)
+ {
+ if (assetConfigSame && s_ProjectConfigPath.empty())
+ return true; // Do not reload
+ }
+ else
+ {
+ if (assetConfigSame && !s_ProjectConfigPath.empty() && CFile::fileExists(s_ProjectConfigPath))
+ {
+ projectConfigModification = CFile::getFileModificationDate(s_ProjectConfigPath);
+
+ if (s_ProjectConfigModification == projectConfigModification)
+ return true; // Do not reload
+ }
+
+ // Search for project and load up all root paths
+ std::vector files;
+ CPath::getPathContent(CPath::getApplicationDirectory("NeL", true) + "/projects", false, false, true, files);
+ for (std::vector::iterator it(files.begin()), end(files.end()); it != end; ++it)
+ {
+ const std::string& file = *it;
+ if (file.length() >= 4 && (file.compare(file.length() - 4, 4, ".cfg") == 0))
+ {
+ CConfigFile project;
+ project.load(file);
+ CConfigFile::CVar &directories = project.getVar("Directories");
+ bool isProject = false;
+ for (uint i = 0; i < directories.size(); ++i)
+ {
+ if (rootPath == CPath::standardizePath(directories.asString(i), true))
+ {
+ isProject = true;
+ break;
+ }
+ }
+ if (isProject)
+ {
+ projectConfigModification = CFile::getFileModificationDate(file);
+ projectConfigPath = file;
+
+ for (uint i = 0; i < directories.size(); ++i)
+ {
+ std::string dir = CPath::standardizePath(directories.asString(i), true);
+ std::string cfgPath = dir + "nel.cfg";
+ if (CFile::fileExists(cfgPath))
+ configRootPaths.push_back(dir);
+ }
+
+ projectName = project.getVar("ProjectName").asString();
+
+ break;
+ }
+ }
+ }
+ }
+
+ if (projectConfigPath.empty())
+ {
+ projectName = "NeL Project";
+ configRootPaths.push_back(rootPath);
+ projectConfigModification = 0;
+ }
+
+ nldebug("Initializing project config '%s'", projectConfigPath.empty() ? configPath.c_str() : projectConfigPath.c_str());
+ release();
+
+ s_InitFlags = flags;
+ s_AssetConfigPath = configPath;
+ s_AssetConfigModification = configFileModification;
+ s_ProjectConfigPath = projectConfigPath;
+ s_ProjectConfigModification = projectConfigModification;
+ s_ProjectName = projectName;
+ s_ConfigPaths = configRootPaths;
+
+ std::map configFiles;
+ for (std::vector::iterator it(configRootPaths.begin()), end(configRootPaths.end()); it != end; ++it)
+ {
+ const std::string &dir = *it;
+ const std::string &cfgPath = *it + "nel.cfg";
+ CConfigFile *cfgFile = new CConfigFile();
+ cfgFile->load(cfgPath);
+ std::string identifier = cfgFile->getVar("Identifier").asString();
+ if (configFiles.find(identifier) != configFiles.end()) // Identifier already exists
+ {
+ if (dir == rootPath)
+ {
+ // Replace config that was already added, asset root gets priority
+ std::vector::iterator old = std::find(s_ConfigFiles.begin(), s_ConfigFiles.end(), configFiles[identifier]);
+ uint idx = old - s_ConfigFiles.begin();
+ s_ConfigFiles.erase(old);
+ s_ConfigPaths.erase(s_ConfigPaths.begin() + idx);
+ }
+ else
+ {
+ // Skip, first listed config gets priority
+ s_ConfigPaths.erase(s_ConfigPaths.begin() + s_ConfigFiles.size());
+ continue;
+ }
+ }
+#ifdef NL_OS_WINDOWS
+ SetEnvironmentVariableA(identifier.c_str(), dir.c_str());
+#else
+ setenv(identifier.c_str(), dir.c_str(), 1);
+#endif
+ configFiles[identifier] = cfgFile;
+ s_ConfigFiles.push_back(cfgFile);
+ }
+
+ nlassert(s_ConfigFiles.size() == s_ConfigPaths.size());
+
+ if (flags & DatabaseTextureSearchPaths)
+ {
+ searchDirectories("DatabaseTextureSearchPaths");
+ }
+
+ return true;
+}
+
+void CProjectConfig::searchDirectories(const char *var)
+{
+ for (uint i = 0; i < s_ConfigFiles.size(); ++i)
+ {
+ CConfigFile *cfg = s_ConfigFiles[i];
+ const TPathString &dir = s_ConfigPaths[i];
+ CConfigFile::CVar *paths = cfg->getVarPtr(var);
+ if (paths)
+ {
+ for (uint i = 0; i < paths->size(); i++)
+ {
+ TPathString path = paths->asString(i);
+ if (!CPath::isAbsolutePath(path)) path = dir + path;
+ path = CPath::standardizePath(path);
+ if (s_SearchPaths.find(path) == s_SearchPaths.end())
+ {
+ CPath::addSearchPath(path);
+ s_SearchPaths.insert(path);
+ }
+ }
+ }
+ }
+}
+
+void CProjectConfig::release()
+{
+ s_SearchPaths.clear();
+ CPath::clearMap();
+ cleanup();
+}
+
+} /* namespace NLPIPELINE */
+
+/* end of file */
diff --git a/code/nel/tools/3d/mesh_utils/mesh_utils.cpp b/code/nel/tools/3d/mesh_utils/mesh_utils.cpp
index 0b7025d0e..8765e3960 100644
--- a/code/nel/tools/3d/mesh_utils/mesh_utils.cpp
+++ b/code/nel/tools/3d/mesh_utils/mesh_utils.cpp
@@ -20,7 +20,7 @@
#include
#include
-#include
+#include
#include
#include
#include
@@ -292,14 +292,14 @@ int exportScene(const CMeshUtilsSettings &settings)
context.ToolLogger.writeDepend(NLPIPELINE::BUILD, "*", NLMISC::CPath::standardizePath(context.Settings.SourceFilePath, false).c_str()); // Base input file
// Apply database configuration
- if (!NLPIPELINE::CDatabaseConfig::init(settings.SourceFilePath))
+ if (!NLPIPELINE::CProjectConfig::init(settings.SourceFilePath,
+ NLPIPELINE::CProjectConfig::DatabaseTextureSearchPaths,
+ true))
{
tlerror(context.ToolLogger, context.Settings.SourceFilePath.c_str(), "Unable to find database.cfg in input path or any of its parents.");
- return EXIT_FAILURE;
+ // return EXIT_FAILURE; We can continue but the output will not be guaranteed...
}
- NLPIPELINE::CDatabaseConfig::initTextureSearchDirectories();
-
Assimp::Importer importer;
const aiScene *scene = importer.ReadFile(settings.SourceFilePath, 0
| aiProcess_Triangulate