diff --git a/libs/ThreadedDefLoader.h b/libs/ThreadedDefLoader.h index 4934a3a8e9..03a4736720 100644 --- a/libs/ThreadedDefLoader.h +++ b/libs/ThreadedDefLoader.h @@ -2,6 +2,9 @@ #include #include +#include +#include +#include "ifilesystem.h" namespace util { @@ -16,10 +19,18 @@ namespace util * * Client code (even from multiple threads) can retrieve (and wait for) the result * by calling the get() method. + * + * The loader will invoke the functor passed to the constructor for each encountered + * def file, in the same order as the idTech4/TDM engine (alphabetically). */ template class ThreadedDefLoader { +private: + std::string _baseDir; + std::string _extension; + std::size_t _depth; + typedef std::function LoadFunction; LoadFunction _loadFunc; @@ -32,11 +43,19 @@ class ThreadedDefLoader bool _loadingStarted; public: - ThreadedDefLoader(const LoadFunction& loadFunc) : - ThreadedDefLoader(loadFunc, std::function()) + ThreadedDefLoader(const std::string& baseDir, const std::string& extension, const LoadFunction& loadFunc) : + ThreadedDefLoader(baseDir, extension, 0, loadFunc) + {} + + ThreadedDefLoader(const std::string& baseDir, const std::string& extension, std::size_t depth, const LoadFunction& loadFunc) : + ThreadedDefLoader(baseDir, extension, depth, loadFunc, std::function()) {} - ThreadedDefLoader(const LoadFunction& loadFunc, const std::function& finishedFunc) : + ThreadedDefLoader(const std::string& baseDir, const std::string& extension, std::size_t depth, + const LoadFunction& loadFunc, const std::function& finishedFunc) : + _baseDir(baseDir), + _extension(extension), + _depth(depth), _loadFunc(loadFunc), _finishedFunc(finishedFunc), _loadingStarted(false) @@ -101,6 +120,33 @@ class ThreadedDefLoader } } +protected: + void loadFiles(const vfs::VirtualFileSystem::VisitorFunc& visitor) + { + loadFiles(GlobalFileSystem(), visitor); + } + + void loadFiles(vfs::VirtualFileSystem& vfs, const vfs::VirtualFileSystem::VisitorFunc& visitor) + { + // Accumulate all the files and sort them before calling the visitor + std::vector _incomingFiles; + _incomingFiles.reserve(200); + + vfs.forEachFile(_baseDir, _extension, [&](const vfs::FileInfo& info) + { + _incomingFiles.push_back(info); + }, _depth); + + // Sort the files by name + std::sort(_incomingFiles.begin(), _incomingFiles.end(), [](const vfs::FileInfo& a, const vfs::FileInfo& b) + { + return a.name < b.name; + }); + + // Dispatch the sorted list to the visitor + std::for_each(_incomingFiles.begin(), _incomingFiles.end(), visitor); + } + private: struct FinishFunctionCaller { diff --git a/plugins/dm.gui/gui/GuiManager.cpp b/plugins/dm.gui/gui/GuiManager.cpp index 2d2ad838f5..09f793bf0b 100644 --- a/plugins/dm.gui/gui/GuiManager.cpp +++ b/plugins/dm.gui/gui/GuiManager.cpp @@ -11,7 +11,7 @@ namespace gui { GuiManager::GuiManager() : - _guiLoader(std::bind(&GuiManager::findGuis, this)) + _guiLoader(GUI_DIR, GUI_EXT, std::bind(&GuiManager::findGuis, this)) {} void GuiManager::registerGui(const std::string& guiPath) diff --git a/plugins/sound/SoundManager.cpp b/plugins/sound/SoundManager.cpp index f95c422ade..9ba025bdce 100644 --- a/plugins/sound/SoundManager.cpp +++ b/plugins/sound/SoundManager.cpp @@ -63,7 +63,7 @@ ArchiveFilePtr openSoundFile(const std::string& fileName) // Constructor SoundManager::SoundManager() : - _defLoader(std::bind(&SoundManager::loadShadersFromFilesystem, this)), + _defLoader(SOUND_FOLDER, "sndshd", 99, std::bind(&SoundManager::loadShadersFromFilesystem, this)), _emptyShader(new SoundShader("", "", vfs::FileInfo("sounds/", "_autogenerated_by_darkradiant_.sndshd", vfs::Visibility::HIDDEN), "")) {} diff --git a/radiantcore/CMakeLists.txt b/radiantcore/CMakeLists.txt index 0ecac891de..923fdbaa32 100644 --- a/radiantcore/CMakeLists.txt +++ b/radiantcore/CMakeLists.txt @@ -194,6 +194,7 @@ add_library(radiantcore MODULE modulesystem/ModuleLoader.cpp modulesystem/ModuleRegistry.cpp particles/ParticleDef.cpp + particles/ParticleLoader.cpp particles/ParticleNode.cpp particles/ParticleParameter.cpp particles/ParticlesManager.cpp diff --git a/radiantcore/eclass/EClassManager.cpp b/radiantcore/eclass/EClassManager.cpp index 1d2abd4094..b27f1e145c 100644 --- a/radiantcore/eclass/EClassManager.cpp +++ b/radiantcore/eclass/EClassManager.cpp @@ -24,7 +24,7 @@ namespace eclass { // Constructor EClassManager::EClassManager() : _realised(false), - _defLoader(std::bind(&EClassManager::loadDefAndResolveInheritance, this), + _defLoader("def/", "def", 1, std::bind(&EClassManager::loadDefAndResolveInheritance, this), std::bind(&EClassManager::onDefLoadingCompleted, this)), _curParseStamp(0) {} diff --git a/radiantcore/fonts/FontLoader.cpp b/radiantcore/fonts/FontLoader.cpp index fa4acdd55f..d026a9a7c6 100644 --- a/radiantcore/fonts/FontLoader.cpp +++ b/radiantcore/fonts/FontLoader.cpp @@ -11,10 +11,10 @@ namespace fonts { -void FontLoader::operator()(const vfs::FileInfo& fileInfo) +void FontLoader::loadFont(const vfs::FileInfo& fileInfo) { // Construct the full VFS path - std::string fullPath = os::standardPath(_basePath + fileInfo.name); + std::string fullPath = fileInfo.fullPath(); std::regex expr("^/?(.*)/.*_(\\d{2})\\.dat$", std::regex::icase); std::smatch matches; @@ -45,7 +45,7 @@ void FontLoader::operator()(const vfs::FileInfo& fileInfo) if (resolution != NumResolutions) { // Create the font (if not done yet), acquire the info structure - FontInfoPtr font = _manager.findOrCreateFontInfo(fontname); + auto font = _manager.findOrCreateFontInfo(fontname); // Load the DAT file and create the glyph info font->glyphSets[resolution] = GlyphSet::createFromDatFile( @@ -59,4 +59,11 @@ void FontLoader::operator()(const vfs::FileInfo& fileInfo) } } +void FontLoader::loadFonts() +{ + loadFiles(std::bind(&FontLoader::loadFont, this, std::placeholders::_1)); + + rMessage() << _manager.getNumFonts() << " fonts registered." << std::endl; +} + } // namespace fonts diff --git a/radiantcore/fonts/FontLoader.h b/radiantcore/fonts/FontLoader.h index 28fdff1d47..4d16b2b0a2 100644 --- a/radiantcore/fonts/FontLoader.h +++ b/radiantcore/fonts/FontLoader.h @@ -1,29 +1,35 @@ +#pragma once + #include "ifonts.h" #include "ifilesystem.h" +#include "ThreadedDefLoader.h" namespace fonts { class FontManager; -class FontLoader +class FontLoader : + public util::ThreadedDefLoader { private: - // The base path for the shaders (e.g. "materials/") - std::string _basePath; - // The manager for registering the fonts FontManager& _manager; + std::size_t _numFonts; + public: // Constructor. Set the base path of the search. - FontLoader(const std::string& path, FontManager& manager) : - _basePath(path), - _manager(manager) + FontLoader(const std::string& path, const std::string& extension, FontManager& manager) : + ThreadedDefLoader(path, extension, 2, std::bind(&FontLoader::loadFonts, this)), + _manager(manager), + _numFonts(0) {} - void operator()(const vfs::FileInfo& fileInfo); +private: + void loadFonts(); + void loadFont(const vfs::FileInfo& fileInfo); }; } // namespace fonts diff --git a/radiantcore/fonts/FontManager.cpp b/radiantcore/fonts/FontManager.cpp index 5118cb1c42..3966d570bd 100644 --- a/radiantcore/fonts/FontManager.cpp +++ b/radiantcore/fonts/FontManager.cpp @@ -27,7 +27,6 @@ in game descriptor"; } FontManager::FontManager() : - _loader(std::bind(&FontManager::loadFonts, this)), _curLanguage("english") {} @@ -39,15 +38,13 @@ const std::string& FontManager::getName() const const StringSet& FontManager::getDependencies() const { - static StringSet _dependencies; - - if (_dependencies.empty()) - { - _dependencies.insert(MODULE_VIRTUALFILESYSTEM); - _dependencies.insert(MODULE_XMLREGISTRY); - _dependencies.insert(MODULE_GAMEMANAGER); - _dependencies.insert(MODULE_SHADERSYSTEM); - } + static StringSet _dependencies + { + MODULE_VIRTUALFILESYSTEM, + MODULE_XMLREGISTRY, + MODULE_GAMEMANAGER, + MODULE_SHADERSYSTEM, + }; return _dependencies; } @@ -56,13 +53,15 @@ void FontManager::initialiseModule(const IApplicationContext& ctx) { rMessage() << getName() << "::initialiseModule called" << std::endl; + _loader = std::make_unique(getFontPath(), getFontExtension(), *this); + // Find installed fonts in a new thread - _loader.start(); + _loader->start(); } void FontManager::shutdownModule() { - _loader.reset(); + _loader->reset(); _fonts.clear(); } @@ -73,45 +72,45 @@ const std::string& FontManager::getCurLanguage() void FontManager::ensureFontsLoaded() { - _loader.ensureFinished(); + _loader->ensureFinished(); } -void FontManager::loadFonts() +std::size_t FontManager::getNumFonts() { - _fonts.clear(); - - xml::NodeList nlBasePath = GlobalGameManager().currentGame()->getLocalXPath("/filesystem/fonts/basepath"); - - if (nlBasePath.empty()) - { - throw xml::MissingXMLNodeException(MISSING_BASEPATH_NODE); - } + return _fonts.size(); +} - xml::NodeList nlExt = GlobalGameManager().currentGame()->getLocalXPath("/filesystem/fonts/extension"); +std::string FontManager::getFontPath() +{ + auto nlBasePath = GlobalGameManager().currentGame()->getLocalXPath("/filesystem/fonts/basepath"); - if (nlExt.empty()) - { - throw xml::MissingXMLNodeException(MISSING_EXTENSION_NODE); - } + if (nlBasePath.empty()) + { + throw xml::MissingXMLNodeException(MISSING_BASEPATH_NODE); + } - // TODO: Get the language from the registry - _curLanguage = "english"; + // Load the DAT files from the VFS + return os::standardPathWithSlash(nlBasePath[0].getContent()) + _curLanguage + "/"; +} - // Load the DAT files from the VFS - std::string path = os::standardPathWithSlash(nlBasePath[0].getContent()) + _curLanguage + "/"; - std::string extension = nlExt[0].getContent(); +std::string FontManager::getFontExtension() +{ + auto nlExt = GlobalGameManager().currentGame()->getLocalXPath("/filesystem/fonts/extension"); - // Instantiate a visitor to traverse the VFS - FontLoader loader(path, *this); - GlobalFileSystem().forEachFile(path, extension, loader, 2); + if (nlExt.empty()) + { + throw xml::MissingXMLNodeException(MISSING_EXTENSION_NODE); + } - rMessage() << _fonts.size() << " fonts registered." << std::endl; + return nlExt[0].getContent(); } void FontManager::reloadFonts() { - _loader.reset(); - _loader.start(); + _fonts.clear(); + + _loader->reset(); + _loader->start(); } IFontInfoPtr FontManager::findFontInfo(const std::string& name) diff --git a/radiantcore/fonts/FontManager.h b/radiantcore/fonts/FontManager.h index 9b5d4b0f2d..05a207afc1 100644 --- a/radiantcore/fonts/FontManager.h +++ b/radiantcore/fonts/FontManager.h @@ -7,7 +7,7 @@ #include #include "FontInfo.h" -#include "ThreadedDefLoader.h" +#include "FontLoader.h" namespace fonts { @@ -16,12 +16,12 @@ class FontManager : public IFontManager { private: + std::string _curLanguage; + typedef std::map FontMap; FontMap _fonts; - util::ThreadedDefLoader _loader; - - std::string _curLanguage; + std::unique_ptr _loader; public: FontManager(); @@ -43,11 +43,15 @@ class FontManager : // Returns the current language (e.g. "english") const std::string& getCurLanguage(); + std::size_t getNumFonts(); + private: void ensureFontsLoaded(); - void loadFonts(); void reloadFonts(); + + std::string getFontPath(); + std::string getFontExtension(); }; typedef std::shared_ptr FontManagerPtr; diff --git a/radiantcore/particles/ParticleLoader.cpp b/radiantcore/particles/ParticleLoader.cpp new file mode 100644 index 0000000000..d5276351ca --- /dev/null +++ b/radiantcore/particles/ParticleLoader.cpp @@ -0,0 +1,93 @@ +#include "ParticleLoader.h" + +#include "ifilesystem.h" +#include "itextstream.h" +#include "debugging/ScopedDebugTimer.h" + +namespace particles +{ + +void ParticleLoader::parseParticleDef(parser::DefTokeniser& tok, const std::string& filename) +{ + // Standard DEF, starts with "particle {" + auto declName = tok.nextToken(); + + // Check for a valid particle declaration, some .prt files contain materials + if (declName != "particle") + { + // No particle, skip name plus whole block + tok.skipTokens(1); + tok.assertNextToken("{"); + + for (std::size_t level = 1; level > 0;) + { + std::string token = tok.nextToken(); + + if (token == "}") + { + level--; + } + else if (token == "{") + { + level++; + } + } + + return; + } + + // Valid particle declaration, go ahead parsing the name + auto name = tok.nextToken(); + tok.assertNextToken("{"); + + // Find the particle def (use the non-blocking, internal lookup) + auto def = _findOrInsert(name); + + def->setFilename(filename); + + // Let the particle construct itself from the token stream + def->parseFromTokens(tok); +} + +void ParticleLoader::parseStream(std::istream& contents, const std::string& filename) +{ + // Usual ritual, get a parser::DefTokeniser and start tokenising the DEFs + parser::BasicDefTokeniser tok(contents); + + while (tok.hasMoreTokens()) + { + parseParticleDef(tok, filename); + } +} + +void ParticleLoader::load() +{ + ScopedDebugTimer timer("Particle definitions parsed: "); + + loadFiles([&](const vfs::FileInfo& fileInfo) + { + // Attempt to open the file in text mode + auto file = GlobalFileSystem().openTextFile(PARTICLES_DIR + fileInfo.name); + + if (file) + { + // File is open, so parse the tokens + try + { + std::istream is(&(file->getInputStream())); + parseStream(is, fileInfo.name); + } + catch (parser::ParseException& e) + { + rError() << "[particles] Failed to parse " << fileInfo.name + << ": " << e.what() << std::endl; + } + } + else + { + rError() << "[particles] Unable to open " << fileInfo.name << std::endl; + } + }); +} + +} diff --git a/radiantcore/particles/ParticleLoader.h b/radiantcore/particles/ParticleLoader.h new file mode 100644 index 0000000000..940e8c0e90 --- /dev/null +++ b/radiantcore/particles/ParticleLoader.h @@ -0,0 +1,37 @@ +#pragma once + +#include +#include +#include "ParticleDef.h" +#include "ThreadedDefLoader.h" +#include "parser/DefTokeniser.h" + +namespace particles +{ + +class ParticleLoader : + public util::ThreadedDefLoader +{ +private: + std::function _findOrInsert; + std::function _onFinished; + +public: + ParticleLoader(const std::function& findOrInsert, + const std::function& onFinished) : + ThreadedDefLoader(PARTICLES_DIR, PARTICLES_EXT, 1, std::bind(&ParticleLoader::load, this), onFinished), + _findOrInsert(findOrInsert) + {} + +private: + void load(); + + /** + * Accept a stream containing particle definitions to parse and add to the list. + */ + void parseStream(std::istream& contents, const std::string& filename); + + void parseParticleDef(parser::DefTokeniser& tok, const std::string& filename); +}; + +} diff --git a/radiantcore/particles/ParticlesManager.cpp b/radiantcore/particles/ParticlesManager.cpp index ab6262d5bb..e86311bacf 100644 --- a/radiantcore/particles/ParticlesManager.cpp +++ b/radiantcore/particles/ParticlesManager.cpp @@ -43,7 +43,8 @@ namespace } ParticlesManager::ParticlesManager() : - _defLoader(std::bind(&ParticlesManager::reloadParticleDefs, this)) + _defLoader(std::bind(&ParticlesManager::findOrInsertParticleDefInternal, this, std::placeholders::_1), + std::bind(&ParticlesManager::onParticlesLoaded, this)) {} sigc::signal ParticlesManager::signal_particlesReloaded() const @@ -153,61 +154,6 @@ void ParticlesManager::ensureDefsLoaded() _defLoader.ensureFinished(); } -// Parse particle defs from string -void ParticlesManager::parseStream(std::istream& contents, const std::string& filename) -{ - // Usual ritual, get a parser::DefTokeniser and start tokenising the DEFs - parser::BasicDefTokeniser tok(contents); - - while (tok.hasMoreTokens()) - { - parseParticleDef(tok, filename); - } -} - -// Parse a single particle def -void ParticlesManager::parseParticleDef(parser::DefTokeniser& tok, const std::string& filename) -{ - // Standard DEF, starts with "particle {" - std::string declName = tok.nextToken(); - - // Check for a valid particle declaration, some .prt files contain materials - if (declName != "particle") - { - // No particle, skip name plus whole block - tok.skipTokens(1); - tok.assertNextToken("{"); - - for (std::size_t level = 1; level > 0;) - { - std::string token = tok.nextToken(); - - if (token == "}") - { - level--; - } - else if (token == "{") - { - level++; - } - } - - return; - } - - // Valid particle declaration, go ahead parsing the name - std::string name = tok.nextToken(); - tok.assertNextToken("{"); - - // Find the particle def (use the non-blocking, internal lookup) - ParticleDefPtr pdef = findOrInsertParticleDefInternal(name); - - pdef->setFilename(filename); - - // Let the particle construct itself from the token stream - pdef->parseFromTokens(tok); -} - const std::string& ParticlesManager::getName() const { static std::string _name(MODULE_PARTICLESMANAGER); @@ -245,40 +191,17 @@ void ParticlesManager::initialiseModule(const IApplicationContext& ctx) void ParticlesManager::reloadParticleDefs() { - ScopedDebugTimer timer("Particle definitions parsed: "); - - GlobalFileSystem().forEachFile( - PARTICLES_DIR, PARTICLES_EXT, - [&](const vfs::FileInfo& fileInfo) - { - // Attempt to open the file in text mode - ArchiveTextFilePtr file = GlobalFileSystem().openTextFile(PARTICLES_DIR + fileInfo.name); - - if (file != NULL) - { - // File is open, so parse the tokens - try - { - std::istream is(&(file->getInputStream())); - parseStream(is, fileInfo.name); - } - catch (parser::ParseException& e) - { - rError() << "[particles] Failed to parse " << fileInfo.name - << ": " << e.what() << std::endl; - } - } - else - { - rError() << "[particles] Unable to open " << fileInfo.name << std::endl; - } - }, - 1 // depth == 1: don't search subdirectories - ); + _particleDefs.clear(); + _defLoader.reset(); + _defLoader.start(); +} + +void ParticlesManager::onParticlesLoaded() +{ rMessage() << "Found " << _particleDefs.size() << " particle definitions." << std::endl; - // Notify observers about this event + // Notify observers about this event _particlesReloadedSignal.emit(); } diff --git a/radiantcore/particles/ParticlesManager.h b/radiantcore/particles/ParticlesManager.h index 0319d67d86..bc7d513faf 100644 --- a/radiantcore/particles/ParticlesManager.h +++ b/radiantcore/particles/ParticlesManager.h @@ -3,7 +3,7 @@ #include "ParticleDef.h" #include "StageDef.h" -#include "ThreadedDefLoader.h" +#include "ParticleLoader.h" #include "iparticles.h" #include "parser/DefTokeniser.h" @@ -20,7 +20,7 @@ class ParticlesManager : ParticleDefMap _particleDefs; - util::ThreadedDefLoader _defLoader; + ParticleLoader _defLoader; // Reloaded signal sigc::signal _particlesReloadedSignal; @@ -65,17 +65,7 @@ class ParticlesManager : // that it's done loading before accessing any defs. void ensureDefsLoaded(); - /** - * Accept a stream containing particle definitions to parse and add to the - * list. - */ - void parseStream(std::istream& s, const std::string& filename); - - // Recursive-descent parse functions - void parseParticleDef(parser::DefTokeniser& tok, const std::string& filename); - - static void stripParticleDefFromStream(std::istream& input, std::ostream& output, const std::string& particleName); + void onParticlesLoaded(); }; -typedef std::shared_ptr ParticlesManagerPtr; } diff --git a/radiantcore/shaders/Doom3ShaderSystem.cpp b/radiantcore/shaders/Doom3ShaderSystem.cpp index 4e426f7457..d50de13c08 100644 --- a/radiantcore/shaders/Doom3ShaderSystem.cpp +++ b/radiantcore/shaders/Doom3ShaderSystem.cpp @@ -49,15 +49,14 @@ namespace namespace shaders { -// Constructor Doom3ShaderSystem::Doom3ShaderSystem() : - _defLoader(std::bind(&Doom3ShaderSystem::loadMaterialFiles, this)), _enableActiveUpdates(true), _realised(false) {} void Doom3ShaderSystem::construct() { + _defLoader = std::make_unique(GlobalFileSystem()); _library = std::make_shared(); _textureManager = std::make_shared(); @@ -80,34 +79,12 @@ void Doom3ShaderSystem::destroy() // the CShader destructors. } -ShaderLibraryPtr Doom3ShaderSystem::loadMaterialFiles() -{ - // Get the shaders path and extension from the XML game file - auto materialsFolder = getMaterialsFolderName(); - auto extension = getMaterialFileExtension(); - - // Load the shader files from the VFS - ShaderLibraryPtr library = std::make_shared(); - - // Load each file from the global filesystem - { - ScopedDebugTimer timer("ShaderFiles parsed: "); - ShaderFileLoader loader(GlobalFileSystem(), *library, - materialsFolder, extension); - loader.parseFiles(); - } - - rMessage() << library->getNumDefinitions() << " shader definitions found." << std::endl; - - return library; -} - void Doom3ShaderSystem::realise() { if (!_realised) { // Start loading defs - _defLoader.start(); + _defLoader->start(); _signalDefsLoaded.emit(); _realised = true; @@ -129,7 +106,7 @@ void Doom3ShaderSystem::ensureDefsLoaded() // To avoid assigning the pointer everytime, check if the library is empty if (_library->getNumDefinitions() == 0) { - _library = _defLoader.get(); + _library = _defLoader->get(); } } @@ -145,7 +122,7 @@ void Doom3ShaderSystem::onFileSystemShutdown() void Doom3ShaderSystem::freeShaders() { _library->clear(); - _defLoader.reset(); + _defLoader->reset(); _textureManager->checkBindings(); activeShadersChangedNotify(); } diff --git a/radiantcore/shaders/Doom3ShaderSystem.h b/radiantcore/shaders/Doom3ShaderSystem.h index 6de726af41..89e80cc94c 100644 --- a/radiantcore/shaders/Doom3ShaderSystem.h +++ b/radiantcore/shaders/Doom3ShaderSystem.h @@ -9,9 +9,9 @@ #include #include "ShaderLibrary.h" +#include "ShaderFileLoader.h" #include "TableDefinition.h" #include "textures/GLTextureManager.h" -#include "ThreadedDefLoader.h" namespace shaders { @@ -29,7 +29,7 @@ class Doom3ShaderSystem : ShaderLibraryPtr _library; // The ShaderFileLoader will provide a new ShaderLibrary once complete - util::ThreadedDefLoader _defLoader; + std::unique_ptr _defLoader; // The manager that handles the texture caching. GLTextureManagerPtr _textureManager; @@ -148,10 +148,6 @@ class Doom3ShaderSystem : // Unloads all the existing shaders and calls activeShadersChangedNotify() void freeShaders(); - /** Load the shader definitions from the MTR files - * (doesn't load any textures yet). */ - ShaderLibraryPtr loadMaterialFiles(); - void testShaderExpressionParsing(); std::string ensureNonConflictingName(const std::string& name); diff --git a/radiantcore/shaders/ShaderFileLoader.h b/radiantcore/shaders/ShaderFileLoader.h index 546568851a..18c2bde27a 100644 --- a/radiantcore/shaders/ShaderFileLoader.h +++ b/radiantcore/shaders/ShaderFileLoader.h @@ -8,28 +8,28 @@ #include "TableDefinition.h" #include "ShaderTemplate.h" #include "ShaderDefinition.h" +#include "ShaderLibrary.h" #include "parser/DefBlockTokeniser.h" +#include "materials/ParseLib.h" +#include "ThreadedDefLoader.h" #include "string/replace.h" #include "string/predicate.h" +#include "debugging/ScopedDebugTimer.h" namespace shaders { // VFS functor class which loads material (mtr) files. -template class ShaderFileLoader +class ShaderFileLoader : + public util::ThreadedDefLoader { // The VFS module to provide shader files vfs::VirtualFileSystem& _vfs; - ShaderLibrary_T& _library; - - // List of shader definition files to parse - std::vector _files; - private: - bool parseTable(const parser::BlockTokeniser::Block& block, const vfs::FileInfo& fileInfo) + bool parseTable(const parser::BlockTokeniser::Block& block, const vfs::FileInfo& fileInfo, ShaderLibrary& library) { if (block.name.length() <= 5 || !string::starts_with(block.name, "table")) { @@ -47,7 +47,7 @@ template class ShaderFileLoader auto table = std::make_shared(tableName, block.contents); - if (!_library.addTableDefinition(table)) + if (!library.addTableDefinition(table)) { rError() << "[shaders] " << fileInfo.name << ": table " << tableName << " already defined." << std::endl; } @@ -59,7 +59,7 @@ template class ShaderFileLoader } // Parse a shader file with the given contents and filename - void parseShaderFile(std::istream& inStr, const vfs::FileInfo& fileInfo) + void parseShaderFile(std::istream& inStr, const vfs::FileInfo& fileInfo, ShaderLibrary& library) { // Parse the file with a blocktokeniser, the actual block contents // will be parsed separately. @@ -71,7 +71,7 @@ template class ShaderFileLoader parser::BlockTokeniser::Block block = tokeniser.nextBlock(); // Try to parse tables - if (parseTable(block, fileInfo)) + if (parseTable(block, fileInfo, library)) { continue; // table successfully parsed } @@ -94,7 +94,7 @@ template class ShaderFileLoader ShaderDefinition def(shaderTemplate, fileInfo); // Insert into the definitions map, if not already present - if (!_library.addDefinition(block.name, def)) + if (!library.addDefinition(block.name, def)) { rError() << "[shaders] " << fileInfo.name << ": shader " << block.name << " already defined." << std::endl; } @@ -104,38 +104,38 @@ template class ShaderFileLoader public: /// Construct and initialise the ShaderFileLoader - ShaderFileLoader(vfs::VirtualFileSystem& fs, ShaderLibrary_T& library, - const std::string& basedir, - const std::string& extension = "mtr") - : _vfs(fs), _library(library) - { - _files.reserve(200); - - // Walk the VFS and populate our files list - _vfs.forEachFile( - basedir, extension, - [this](const vfs::FileInfo& fi) { _files.push_back(fi); }, - 0 - ); - } + ShaderFileLoader(vfs::VirtualFileSystem& fs) : + util::ThreadedDefLoader(getMaterialsFolderName(), getMaterialFileExtension(), + std::bind(&ShaderFileLoader::loadMaterialFiles, this)), + _vfs(fs) + {} - void parseFiles() +private: + ShaderLibraryPtr loadMaterialFiles() { - for (const vfs::FileInfo& fileInfo: _files) - { - // Open the file - auto file = _vfs.openTextFile(fileInfo.fullPath()); + // Load the shader files from the VFS into a fresh library + auto library = std::make_shared(); - if (file) + { + ScopedDebugTimer timer("ShaderFiles parsed: "); + loadFiles(_vfs, [&](const vfs::FileInfo& fileInfo) { + // Open the file + auto file = _vfs.openTextFile(fileInfo.fullPath()); + + if (!file) + { + throw std::runtime_error("Unable to read shaderfile: " + fileInfo.name); + } + std::istream is(&(file->getInputStream())); - parseShaderFile(is, fileInfo); - } - else - { - throw std::runtime_error("Unable to read shaderfile: " + fileInfo.name); - } + parseShaderFile(is, fileInfo, *library); + }); } + + rMessage() << library->getNumDefinitions() << " shader definitions found." << std::endl; + + return library; } }; diff --git a/radiantcore/skins/Doom3SkinCache.cpp b/radiantcore/skins/Doom3SkinCache.cpp index cf446d1d68..e5c2376b41 100644 --- a/radiantcore/skins/Doom3SkinCache.cpp +++ b/radiantcore/skins/Doom3SkinCache.cpp @@ -17,7 +17,7 @@ namespace } Doom3SkinCache::Doom3SkinCache() : - _defLoader(std::bind(&Doom3SkinCache::loadSkinFiles, this)), + _defLoader(SKINS_FOLDER, "skin", 1, std::bind(&Doom3SkinCache::loadSkinFiles, this)), _nullSkin("") {} diff --git a/tools/msvc/DarkRadiantCore.vcxproj b/tools/msvc/DarkRadiantCore.vcxproj index bea27523d5..5b2c291e2e 100644 --- a/tools/msvc/DarkRadiantCore.vcxproj +++ b/tools/msvc/DarkRadiantCore.vcxproj @@ -592,6 +592,7 @@ + @@ -948,6 +949,7 @@ + diff --git a/tools/msvc/DarkRadiantCore.vcxproj.filters b/tools/msvc/DarkRadiantCore.vcxproj.filters index 5f85a59922..f2e3ec806b 100644 --- a/tools/msvc/DarkRadiantCore.vcxproj.filters +++ b/tools/msvc/DarkRadiantCore.vcxproj.filters @@ -1141,6 +1141,9 @@ src\rendersystem\backend + + src\particles + @@ -2343,5 +2346,8 @@ src\rendersystem\backend + + src\particles + \ No newline at end of file