Skip to content

Commit

Permalink
#5532: Adjust model loader implementation to load from absolute paths…
Browse files Browse the repository at this point in the history
…, not just from the VFS
  • Loading branch information
codereader committed Mar 12, 2021
1 parent 9c3d313 commit 0aa27c4
Show file tree
Hide file tree
Showing 8 changed files with 99 additions and 74 deletions.
6 changes: 3 additions & 3 deletions include/imodel.h
Expand Up @@ -165,7 +165,7 @@ class IModelExporter

/**
* Importer interface for models. An importer must be able
* to load a model (node) from the VFS.
* to load a model (node) from the VFS and from an absolute path.
* The importer instance shouldn't maintain an internal state,
* such that the same instance can be used to load several models,
* from different client code.
Expand All @@ -186,10 +186,10 @@ class IModelImporter
virtual scene::INodePtr loadModel(const std::string& modelName) = 0;

/**
* Load a model from the VFS, and return the IModel subclass for it.
* Load a model from the given (maybe be VFS or absolute), and return the IModel subclass for it.
*
* @returns: the IModelPtr containing the renderable model or
* NULL if the model loader could not load the file.
* an empty IModelPtr if the model loader could not load the file.
*/
virtual model::IModelPtr loadModelFromPath(const std::string& path) = 0;
};
Expand Down
13 changes: 8 additions & 5 deletions include/imodelcache.h
Expand Up @@ -16,23 +16,26 @@ class IModelCache :
public:
/**
* greebo: This method returns the readily fabricated scene::Node
* containing the suitable model node. The modelPath is analysed
* and the correct ModelLoader is invoked to create the node.
* containing the suitable model node. The modelPath is analysed
* and the correct ModelLoader is invoked to create the node.
*
* @returns: a valid scene::INodePtr, which is never NULL. If the model
* could not be loaded, the fallback "NullModel" is returned.
* could not be loaded, the fallback "NullModel" is returned.
*/
virtual scene::INodePtr getModelNode(const std::string& modelPath) = 0;

/**
* greebo: Get the IModel object for the given VFS path. The request is cached,
* so calling this with the same path twice will return the same
* IModelPtr to save memory.
* so calling this with the same path twice will return the same
* IModelPtr to save memory.
*
* This method is primarily used by the ModelLoaders to acquire their model data.
*/
virtual IModelPtr getModel(const std::string& modelPath) = 0;

// Loads a model from the static resources in DarkRadiant's runtime data/resources folder
virtual scene::INodePtr getModelNodeForStaticResource(const std::string& resourcePath) = 0;

// This reloads all models in the map
virtual void refreshModels(bool blockScreenUpdates = true) = 0;

Expand Down
35 changes: 28 additions & 7 deletions radiantcore/model/ModelCache.cpp
Expand Up @@ -52,7 +52,7 @@ scene::INodePtr ModelCache::getModelNode(const std::string& modelPath)
IModelImporterPtr modelLoader = GlobalModelFormatManager().getImporter(type);

// Try to construct a model node using the suitable loader
scene::INodePtr node = modelLoader->loadModel(actualModelPath);
auto node = modelLoader->loadModel(actualModelPath);

if (node)
{
Expand Down Expand Up @@ -97,15 +97,13 @@ scene::INodePtr ModelCache::getModelNode(const std::string& modelPath)
}

// The model load failed, let's return a NullModel
IModelImporterPtr nullModelLoader = GlobalModelFormatManager().getImporter("");

return nullModelLoader->loadModel(actualModelPath);
return loadNullModel(actualModelPath);
}

IModelPtr ModelCache::getModel(const std::string& modelPath)
{
// Try to lookup the existing model
ModelMap::iterator found = _modelMap.find(modelPath);
auto found = _modelMap.find(modelPath);

if (_enabled && found != _modelMap.end())
{
Expand All @@ -115,7 +113,7 @@ IModelPtr ModelCache::getModel(const std::string& modelPath)
// The model is not cached or the cache is disabled, load afresh

// Get the extension of this model
std::string type = modelPath.substr(modelPath.rfind(".") + 1);
std::string type = os::getExtension(modelPath);

// Find a suitable model loader
IModelImporterPtr modelLoader = GlobalModelFormatManager().getImporter(type);
Expand All @@ -125,12 +123,35 @@ IModelPtr ModelCache::getModel(const std::string& modelPath)
if (model)
{
// Model successfully loaded, insert a reference into the map
_modelMap.insert(ModelMap::value_type(modelPath, model));
_modelMap.emplace(modelPath, model);
}

return model;
}

scene::INodePtr ModelCache::getModelNodeForStaticResource(const std::string& resourcePath)
{
// Get the extension of this model
auto extension = os::getExtension(resourcePath);

// Find a suitable model loader
auto modelLoader = GlobalModelFormatManager().getImporter(extension);

auto fullPath = module::GlobalModuleRegistry().getApplicationContext().getRuntimeDataPath();
fullPath + "resources/" + resourcePath;

auto node = modelLoader->loadModel(resourcePath);

return node ? node : loadNullModel(resourcePath);
}

scene::INodePtr ModelCache::loadNullModel(const std::string& modelPath)
{
auto nullModelLoader = GlobalModelFormatManager().getImporter("");

return nullModelLoader->loadModel(modelPath);
}

void ModelCache::removeModel(const std::string& modelPath)
{
// greebo: Disable the modelcache. During map::clear(), the nodes
Expand Down
4 changes: 4 additions & 0 deletions radiantcore/model/ModelCache.h
Expand Up @@ -30,6 +30,8 @@ class ModelCache :
// greebo: For documentation, see the abstract base class.
IModelPtr getModel(const std::string& modelPath) override;

scene::INodePtr getModelNodeForStaticResource(const std::string& resourcePath) override;

// Clear methods
void removeModel(const std::string& modelPath) override;
void clear() override;
Expand All @@ -47,6 +49,8 @@ class ModelCache :
void shutdownModule() override;

private:
scene::INodePtr loadNullModel(const std::string& modelPath);

// Command targets
void refreshModelsCmd(const cmd::ArgumentList& args);
void refreshSelectedModelsCmd(const cmd::ArgumentList& args);
Expand Down
99 changes: 47 additions & 52 deletions radiantcore/model/md5/MD5ModelLoader.cpp
Expand Up @@ -34,79 +34,74 @@ const std::string& MD5ModelLoader::getExtension() const
scene::INodePtr MD5ModelLoader::loadModel(const std::string& modelName)
{
// Initialise the paths, this is all needed for realisation
std::string path = rootPath(modelName);
std::string name = os::getRelativePath(modelName, path);
auto path = rootPath(modelName);
auto name = os::getRelativePath(modelName, path);

// greebo: Path is empty for models in PK4 files, don't check for that

// Try to load the model from the given VFS path
model::IModelPtr model = GlobalModelCache().getModel(name);

if (model == NULL)
if (!model)
{
rError() << "MD5ModelLoader: Could not load model << " << modelName << std::endl;
return scene::INodePtr();
}

// The cached model should be an MD5Model, otherwise we're in the wrong movie
MD5ModelPtr md5Model = std::dynamic_pointer_cast<MD5Model>(model);
auto md5Model = std::dynamic_pointer_cast<MD5Model>(model);

if (md5Model != NULL)
if (md5Model)
{
// Load was successful, construct a modelnode using this resource
return MD5ModelNodePtr(new MD5ModelNode(md5Model));
return std::make_shared<MD5ModelNode>(md5Model);
}
else
{
rError() << "MD5ModelLoader: Cached model is not an MD5Model?" << std::endl;
}


rError() << "MD5ModelLoader: Cached model is not an MD5Model?" << std::endl;
return scene::INodePtr();
}

model::IModelPtr MD5ModelLoader::loadModelFromPath(const std::string& name)
model::IModelPtr MD5ModelLoader::loadModelFromPath(const std::string& path)
{
// Open an ArchiveFile to load
ArchiveFilePtr file = GlobalFileSystem().openFile(name);

if (file != NULL)
{
// Construct a new MD5Model container
MD5ModelPtr model(new MD5Model);

// Store the VFS path in this model
model->setModelPath(name);
// Set the filename this model was loaded from
model->setFilename(os::getFilename(file->getName()));

// greebo: Get the Inputstream from the given file
stream::BinaryToTextInputStream<InputStream> inputStream(file->getInputStream());

// Construct a Tokeniser object and start reading the file
try
{
std::istream is(&inputStream);
parser::BasicDefTokeniser<std::istream> tokeniser(is);

// Invoke the parser routine (might throw)
model->parseFromTokens(tokeniser);
}
catch (parser::ParseException& e)
{
rError() << "[md5model] Parse failure. Exception was:" << std::endl
<< e.what() << std::endl;
// Return an empty model on error
return model::IModelPtr();
}

// Load was successful, return the model
return model;
}
else
{
rError() << "Failed to load model " << name << std::endl;
return model::IModelPtr(); // delete the model
}
auto file = path_is_absolute(path.c_str()) ?
GlobalFileSystem().openFileInAbsolutePath(path) :
GlobalFileSystem().openFile(path);

if (!file)
{
rError() << "Failed to load model " << path << std::endl;
return model::IModelPtr(); // delete the model
}

// Construct a new MD5Model container
auto model = std::make_shared<MD5Model>();

// Store the path in this model
model->setModelPath(path);
// Set the filename this model was loaded from
model->setFilename(os::getFilename(file->getName()));

// greebo: Get the Inputstream from the given file
stream::BinaryToTextInputStream<InputStream> inputStream(file->getInputStream());

// Construct a Tokeniser object and start reading the file
try
{
std::istream is(&inputStream);
parser::BasicDefTokeniser<std::istream> tokeniser(is);

// Invoke the parser routine (might throw)
model->parseFromTokens(tokeniser);

// Load was successful, return the model
return model;
}
catch (parser::ParseException& e)
{
rError() << "[md5model] Parse failure. Exception was: " << e.what() << std::endl;
return model::IModelPtr();
}
}

} // namespace md5
4 changes: 2 additions & 2 deletions radiantcore/model/md5/MD5ModelLoader.h
@@ -1,6 +1,7 @@
#pragma once

#include "imodel.h"
#include "iarchive.h"
#include "MD5Model.h"

class ArchiveFile;
Expand All @@ -21,6 +22,5 @@ class MD5ModelLoader :
// Documentation: See imodel.h
model::IModelPtr loadModelFromPath(const std::string& name) override;
};
typedef std::shared_ptr<MD5ModelLoader> MD5ModelLoaderPtr;

} // namespace md5
} // namespace
10 changes: 6 additions & 4 deletions radiantcore/model/picomodel/PicoModelLoader.cpp
Expand Up @@ -74,14 +74,16 @@ scene::INodePtr PicoModelLoader::loadModel(const std::string& modelName)
}

// Load the given model from the VFS path
IModelPtr PicoModelLoader::loadModelFromPath(const std::string& name)
IModelPtr PicoModelLoader::loadModelFromPath(const std::string& path)
{
// Open an ArchiveFile to load
ArchiveFilePtr file = GlobalFileSystem().openFile(name);
auto file = path_is_absolute(path.c_str()) ?
GlobalFileSystem().openFileInAbsolutePath(path) :
GlobalFileSystem().openFile(path);

if (!file)
{
rError() << "Failed to load model " << name << std::endl;
rError() << "Failed to load model " << path << std::endl;
return IModelPtr();
}

Expand Down Expand Up @@ -111,7 +113,7 @@ IModelPtr PicoModelLoader::loadModelFromPath(const std::string& name)

// Set the filename
modelObj->setFilename(os::getFilename(file->getName()));
modelObj->setModelPath(name);
modelObj->setModelPath(path);

PicoFreeModel(model);

Expand Down
2 changes: 1 addition & 1 deletion radiantcore/model/picomodel/PicoModelLoader.h
Expand Up @@ -24,7 +24,7 @@ class PicoModelLoader :
// Returns a new ModelNode for the given model name
scene::INodePtr loadModel(const std::string& modelName) override;

// Load the given model from the VFS path
// Load the given model from the path, VFS or absolute
IModelPtr loadModelFromPath(const std::string& name) override;
};
typedef std::shared_ptr<PicoModelLoader> PicoModelLoaderPtr;
Expand Down

0 comments on commit 0aa27c4

Please sign in to comment.