Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Split ShaderFileLoader out for testing
ShaderFileLoader is now in its own header, with a template parameter specifying
the ShaderLibrary class which can be mocked in tests. ShaderFileLoader can now
be instantiated in unit tests but does not yet do anything interesting.
  • Loading branch information
Matthew Mott committed Mar 7, 2019
1 parent 80b8e91 commit 68e6598
Show file tree
Hide file tree
Showing 3 changed files with 139 additions and 115 deletions.
117 changes: 2 additions & 115 deletions radiant/shaders/Doom3ShaderSystem.cpp
@@ -1,4 +1,5 @@
#include "Doom3ShaderSystem.h"
#include "ShaderFileLoader.h"

#include "i18n.h"
#include "iradiant.h"
Expand Down Expand Up @@ -75,120 +76,6 @@ void Doom3ShaderSystem::destroy()
// the CShader destructors.
}

namespace
{

// VFS functor class which loads material (mtr) files.
class ShaderFileLoader
{
// The base path for the shaders (e.g. "materials/")
std::string _basePath;

ShaderLibrary& _library;

// List of shader definition files to parse
std::vector<vfs::FileInfo> _files;

private:

// Parse a shader file with the given contents and filename
void parseShaderFile(std::istream& inStr, const vfs::FileInfo& fileInfo)
{
// Parse the file with a blocktokeniser, the actual block contents
// will be parsed separately.
parser::BasicDefBlockTokeniser<std::istream> tokeniser(inStr);

while (tokeniser.hasMoreBlocks())
{
// Get the next block
parser::BlockTokeniser::Block block = tokeniser.nextBlock();

// Skip tables
if (block.name.substr(0, 5) == "table")
{
std::string tableName = block.name.substr(6);

if (tableName.empty())
{
rError() << "[shaders] " << fileInfo.name << ": Missing table name." << std::endl;
continue;
}

TableDefinitionPtr table(new TableDefinition(tableName, block.contents));

if (!_library.addTableDefinition(table))
{
rError() << "[shaders] " << fileInfo.name
<< ": table " << tableName << " already defined." << std::endl;
}

continue;
}
else if (block.name.substr(0, 5) == "skin ")
{
continue; // skip skin definition
}
else if (block.name.substr(0, 9) == "particle ")
{
continue; // skip particle definition
}

string::replace_all(block.name, "\\", "/"); // use forward slashes

ShaderTemplatePtr shaderTemplate(new ShaderTemplate(block.name, block.contents));

// Construct the ShaderDefinition wrapper class
ShaderDefinition def(shaderTemplate, fileInfo.name);

// Insert into the definitions map, if not already present
if (!_library.addDefinition(block.name, def))
{
rError() << "[shaders] " << fileInfo.name
<< ": shader " << block.name << " already defined." << std::endl;
}
}
}

public:
// Constructor. Set the basepath to prepend onto shader filenames.
ShaderFileLoader(const std::string& path, ShaderLibrary& library)
: _basePath(path), _library(library)
{
_files.reserve(200);
}

void addFile(const vfs::FileInfo& fileInfo)
{
// Construct the full VFS path
vfs::FileInfo fileWithBasePath = fileInfo;
fileWithBasePath.name = _basePath + fileInfo.name;
_files.push_back(fileWithBasePath);
}

void parseFiles()
{
for (std::size_t i = 0; i < _files.size(); ++i)
{
const vfs::FileInfo& fileInfo = _files[i];

// Open the file
ArchiveTextFilePtr file = GlobalFileSystem().openTextFile(fileInfo.name);

if (file != nullptr)
{
std::istream is(&(file->getInputStream()));
parseShaderFile(is, fileInfo);
}
else
{
throw std::runtime_error("Unable to read shaderfile: " + fileInfo.name);
}
}
}
};

} // namespace

ShaderLibraryPtr Doom3ShaderSystem::loadMaterialFiles()
{
// Get the shaders path and extension from the XML game file
Expand All @@ -212,7 +99,7 @@ ShaderLibraryPtr Doom3ShaderSystem::loadMaterialFiles()
ShaderLibraryPtr library = std::make_shared<ShaderLibrary>();

// Load each file from the global filesystem
ShaderFileLoader loader(sPath, *library);
ShaderFileLoader<ShaderLibrary> loader(sPath, *library);
{
ScopedDebugTimer timer("ShaderFiles parsed: ");
GlobalFileSystem().forEachFile(
Expand Down
124 changes: 124 additions & 0 deletions radiant/shaders/ShaderFileLoader.h
@@ -0,0 +1,124 @@
#pragma once

#include "iarchive.h"

#include "TableDefinition.h"
#include "ShaderTemplate.h"
#include "ShaderDefinition.h"

#include "parser/DefBlockTokeniser.h"
#include "string/replace.h"

namespace shaders
{

// VFS functor class which loads material (mtr) files.
template<typename ShaderLibrary_T> class ShaderFileLoader
{
// The base path for the shaders (e.g. "materials/")
std::string _basePath;

ShaderLibrary_T& _library;

// List of shader definition files to parse
std::vector<vfs::FileInfo> _files;

private:

// Parse a shader file with the given contents and filename
void parseShaderFile(std::istream& inStr, const vfs::FileInfo& fileInfo)
{
// Parse the file with a blocktokeniser, the actual block contents
// will be parsed separately.
parser::BasicDefBlockTokeniser<std::istream> tokeniser(inStr);

while (tokeniser.hasMoreBlocks())
{
// Get the next block
parser::BlockTokeniser::Block block = tokeniser.nextBlock();

// Skip tables
if (block.name.substr(0, 5) == "table")
{
std::string tableName = block.name.substr(6);

if (tableName.empty())
{
rError() << "[shaders] " << fileInfo.name << ": Missing table name." << std::endl;
continue;
}

TableDefinitionPtr table(new TableDefinition(tableName, block.contents));

if (!_library.addTableDefinition(table))
{
rError() << "[shaders] " << fileInfo.name
<< ": table " << tableName << " already defined." << std::endl;
}

continue;
}
else if (block.name.substr(0, 5) == "skin ")
{
continue; // skip skin definition
}
else if (block.name.substr(0, 9) == "particle ")
{
continue; // skip particle definition
}

string::replace_all(block.name, "\\", "/"); // use forward slashes

ShaderTemplatePtr shaderTemplate(new ShaderTemplate(block.name, block.contents));

// Construct the ShaderDefinition wrapper class
ShaderDefinition def(shaderTemplate, fileInfo.name);

// Insert into the definitions map, if not already present
if (!_library.addDefinition(block.name, def))
{
rError() << "[shaders] " << fileInfo.name
<< ": shader " << block.name << " already defined." << std::endl;
}
}
}

public:
// Constructor. Set the basepath to prepend onto shader filenames.
ShaderFileLoader(const std::string& path, ShaderLibrary_T& library)
: _basePath(path), _library(library)
{
_files.reserve(200);
}

void addFile(const vfs::FileInfo& fileInfo)
{
// Construct the full VFS path
vfs::FileInfo fileWithBasePath = fileInfo;
fileWithBasePath.name = _basePath + fileInfo.name;
_files.push_back(fileWithBasePath);
}

void parseFiles()
{
for (std::size_t i = 0; i < _files.size(); ++i)
{
const vfs::FileInfo& fileInfo = _files[i];

// Open the file
ArchiveTextFilePtr file = GlobalFileSystem().openTextFile(fileInfo.name);

if (file != nullptr)
{
std::istream is(&(file->getInputStream()));
parseShaderFile(is, fileInfo);
}
else
{
throw std::runtime_error("Unable to read shaderfile: " + fileInfo.name);
}
}
}
};

}
13 changes: 13 additions & 0 deletions radiant/test/vfsTest.cpp
Expand Up @@ -2,6 +2,7 @@
#include <boost/test/included/unit_test.hpp>

#include "radiant/vfs/Doom3FileSystem.h"
#include "radiant/shaders/ShaderFileLoader.h"

struct VFSFixture
{
Expand Down Expand Up @@ -98,3 +99,15 @@ BOOST_FIXTURE_TEST_CASE(handleAssetsLst, VFSFixture)
// returned as an actual file to the calling code.
BOOST_TEST(fileVis.count("assets.lst") == 0);
}

using namespace shaders;

struct MockShaderLibrary
{
};

BOOST_FIXTURE_TEST_CASE(loaderShaderFiles, VFSFixture)
{
MockShaderLibrary library;
shaders::ShaderFileLoader<MockShaderLibrary> loader("materials", library);
}

0 comments on commit 68e6598

Please sign in to comment.