Skip to content

Commit

Permalink
Hacking in a crude model save algorithm
Browse files Browse the repository at this point in the history
  • Loading branch information
codereader committed Jan 22, 2017
1 parent 5875b42 commit 4959149
Show file tree
Hide file tree
Showing 11 changed files with 186 additions and 6 deletions.
10 changes: 10 additions & 0 deletions include/imodel.h
Expand Up @@ -105,6 +105,10 @@ class ModelNode

// Returns the contained IModel
virtual IModel& getIModel() = 0;

// Returns true if this model's scale has been modified
// and needs to be written to file
virtual bool hasModifiedScale() = 0;
};
typedef std::shared_ptr<ModelNode> ModelNodePtr;

Expand All @@ -119,6 +123,12 @@ class IModelExporter

// Returns the uppercase file extension this exporter is suitable for
virtual const std::string& getExtension() const = 0;

// Adds the given Surface to the exporter's queue
virtual void addSurface(const IModelSurface& surface) = 0;

// Export the model file to the given stream
virtual void exportToStream(std::ostream& stream) = 0;
};
typedef std::shared_ptr<IModelExporter> IModelExporterPtr;

Expand Down
5 changes: 5 additions & 0 deletions plugins/md5model/MD5ModelNode.cpp
Expand Up @@ -40,6 +40,11 @@ model::IModel& MD5ModelNode::getIModel() {
return *_model;
}

bool MD5ModelNode::hasModifiedScale()
{
return false; // not supported
}

void MD5ModelNode::lightsChanged()
{
_lightList->setDirty();
Expand Down
5 changes: 3 additions & 2 deletions plugins/md5model/MD5ModelNode.h
Expand Up @@ -32,8 +32,9 @@ class MD5ModelNode :
virtual ~MD5ModelNode();

// ModelNode implementation
virtual const model::IModel& getIModel() const;
virtual model::IModel& getIModel();
const model::IModel& getIModel() const override;
model::IModel& getIModel() override;
bool hasModifiedScale() override;

void lightsChanged();

Expand Down
5 changes: 5 additions & 0 deletions plugins/model/PicoModelNode.cpp
Expand Up @@ -52,6 +52,11 @@ IModel& PicoModelNode::getIModel()
return *_picoModel;
}

bool PicoModelNode::hasModifiedScale()
{
return _picoModel->getScale() != Vector3(1, 1, 1);
}

const AABB& PicoModelNode::localAABB() const {
return _picoModel->localAABB();
}
Expand Down
5 changes: 3 additions & 2 deletions plugins/model/PicoModelNode.h
Expand Up @@ -48,8 +48,9 @@ class PicoModelNode :
virtual void onRemoveFromScene(scene::IMapRootNode& root) override;

// ModelNode implementation
virtual const IModel& getIModel() const;
virtual IModel& getIModel();
const IModel& getIModel() const override;
IModel& getIModel() override;
bool hasModifiedScale() override;

// SkinnedModel implementation
// Skin changed notify
Expand Down
5 changes: 5 additions & 0 deletions plugins/model/RenderablePicoModel.cpp
Expand Up @@ -410,4 +410,9 @@ void RenderablePicoModel::importState(const IUndoMementoPtr& state)
applyScaleToSurfaces();
}

const Vector3& RenderablePicoModel::getScale() const
{
return _scale;
}

} // namespace
3 changes: 3 additions & 0 deletions plugins/model/RenderablePicoModel.h
Expand Up @@ -235,6 +235,9 @@ class RenderablePicoModel :
// Undoable implementation
IUndoMementoPtr exportState() const override;
void importState(const IUndoMementoPtr& state) override;

// Returns the current base scale of this model
const Vector3& getScale() const;
};
typedef std::shared_ptr<RenderablePicoModel> RenderablePicoModelPtr;

Expand Down
141 changes: 141 additions & 0 deletions radiant/map/Map.cpp
Expand Up @@ -6,6 +6,7 @@
#include "iscenegraph.h"
#include "idialogmanager.h"
#include "ieventmanager.h"
#include "imodel.h"
#include "ifilesystem.h"
#include "ifiletypes.h"
#include "iselectiongroup.h"
Expand Down Expand Up @@ -439,6 +440,9 @@ bool Map::save(const MapFormatPtr& mapFormat)
// Disable screen updates for the scope of this function
ui::ScreenUpdateBlocker blocker(_("Processing..."), _("Saving Map"));

// write out scaled models
saveScaledModels();

// Store the camview position into worldspawn
saveCameraPosition();

Expand Down Expand Up @@ -1009,6 +1013,143 @@ void Map::shutdownModule()
GlobalSceneGraph().removeSceneObserver(this);
}

void Map::saveScaledModels()
{
// Find any models with modified scale
GlobalSceneGraph().foreachNode([&](const scene::INodePtr& node)
{
if (Node_isEntity(node))
{
// Find any model nodes below that one
model::ModelNodePtr childModel;

node->foreachNode([&](const scene::INodePtr& child)
{
model::ModelNodePtr candidate = Node_getModel(child);

if (candidate && candidate->hasModifiedScale())
{
childModel = candidate;
}

return true;
});

// Do we have a model with modified scale?
if (childModel)
{
saveScaledModel(node, childModel);
}

return false; // no further traversal
}

return true;
});
}

void Map::saveScaledModel(const scene::INodePtr& entityNode, const model::ModelNodePtr& modelNode)
{
std::string extension = "ase";

// Save the scaled model as ASE
model::IModelExporterPtr exporter = GlobalModelFormatManager().getExporter(extension);

if (!exporter)
{
rError() << "Cannot save out scaled models, no exporter found." << std::endl;
return;
}

// Push the geometry into the exporter
model::IModel& model = modelNode->getIModel();

for (int s = 0; s < model.getSurfaceCount(); ++s)
{
const model::IModelSurface& surface = model.getSurface(s);

exporter->addSurface(surface);
}

// Get the current model file name
Entity* entity = Node_getEntity(entityNode);

boost::filesystem::path targetPath = GlobalGameManager().getModPath();

if (targetPath.empty())
{
targetPath = GlobalGameManager().getUserEnginePath();

rMessage() << "No mod base path found, falling back to user engine path to save model file: " <<
targetPath.string() << std::endl;
}

targetPath /= "models";
targetPath /= "map_specific";
targetPath /= "scaled";

boost::filesystem::create_directories(targetPath);

boost::filesystem::path modelPath = entity->getKeyValue("model");

// Open a temporary file to write the model
int i = 100;

boost::filesystem::path filenameWithoutExt = boost::filesystem::change_extension(modelPath, "");

std::string generatedFilename = (filenameWithoutExt.string() + "_" + string::to_string(i) + "." + extension);
boost::filesystem::path targetFile = targetPath / generatedFilename;

while (boost::filesystem::exists(targetFile) && ++i < INT_MAX);
{
generatedFilename = (filenameWithoutExt.string() + "_" + string::to_string(i) + "." + extension);
targetFile = targetPath / generatedFilename;
}

boost::filesystem::path tempFile = targetPath / ("_" + targetPath.filename().string());

std::ofstream tempStream(tempFile.string().c_str());

if (!tempStream.is_open())
{
throw std::runtime_error(
(boost::format(_("Cannot open file for writing: %s")) % tempFile.string()).str());
}

exporter->exportToStream(tempStream);

tempStream.close();

if (boost::filesystem::exists(targetFile))
{
try
{
boost::filesystem::remove(targetFile);
}
catch (boost::filesystem::filesystem_error& e)
{
rError() << "Could not remove the file " << targetFile.string() << std::endl
<< e.what() << std::endl;

throw std::runtime_error(
(boost::format(_("Could not remove the file %s")) % targetFile.string()).str());
}
}

try
{
boost::filesystem::rename(tempFile, targetFile);
}
catch (boost::filesystem::filesystem_error& e)
{
rError() << "Could not rename the temporary file " << tempFile.string() << std::endl
<< e.what() << std::endl;

throw std::runtime_error(
(boost::format(_("Could not rename the temporary file %s")) % tempFile.string()).str());
}
}

// Creates the static module instance
module::StaticModule<Map> staticMapModule;

Expand Down
3 changes: 3 additions & 0 deletions radiant/map/Map.h
Expand Up @@ -232,6 +232,9 @@ class Map :

void loadMapResourceFromPath(const std::string& path);

void saveScaledModels();
void saveScaledModel(const scene::INodePtr& entity, const model::ModelNodePtr& childModel);

}; // class Map

} // namespace map
Expand Down
5 changes: 5 additions & 0 deletions radiant/model/NullModelNode.cpp
Expand Up @@ -44,6 +44,11 @@ IModel& NullModelNode::getIModel()
return *_nullModel;
}

bool NullModelNode::hasModifiedScale()
{
return false;
}

void NullModelNode::testSelect(Selector& selector, SelectionTest& test) {
_nullModel->testSelect(selector, test, localToWorld());
}
Expand Down
5 changes: 3 additions & 2 deletions radiant/model/NullModelNode.h
Expand Up @@ -30,8 +30,9 @@ class NullModelNode :
// Accessor to the singleton instance
static NullModelNodePtr InstancePtr();

virtual const IModel& getIModel() const;
virtual IModel& getIModel();
const IModel& getIModel() const override;
IModel& getIModel() override;
bool hasModifiedScale() override;

void testSelect(Selector& selector, SelectionTest& test);

Expand Down

0 comments on commit 4959149

Please sign in to comment.