Skip to content

Commit

Permalink
#5220: Towards fixing models losing their scale: preserve any modifie…
Browse files Browse the repository at this point in the history
…d scale at the point right before the scene is passed to the map writers.

In regular save events no models will have a modified scale anymore since the rescaled models will have been written to disk already, but in the case of auto-saves or (prefab) export no models will have been processed.
  • Loading branch information
codereader committed Apr 19, 2020
1 parent 9ebf550 commit 2ea5990
Show file tree
Hide file tree
Showing 9 changed files with 99 additions and 2 deletions.
5 changes: 5 additions & 0 deletions include/imodel.h
Expand Up @@ -7,6 +7,8 @@
#include "imodelsurface.h"
#include <vector>

#include "math/Vector3.h"

/* Forward decls */
class AABB;
class ModelSkin;
Expand Down Expand Up @@ -110,6 +112,9 @@ class ModelNode
// Returns true if this model's scale has been modified
// and needs to be written to file
virtual bool hasModifiedScale() = 0;

// Returns the current scale of this model
virtual Vector3 getModelScale() = 0;
};
typedef std::shared_ptr<ModelNode> ModelNodePtr;

Expand Down
5 changes: 5 additions & 0 deletions radiant/md5model/MD5ModelNode.cpp
Expand Up @@ -45,6 +45,11 @@ bool MD5ModelNode::hasModifiedScale()
return false; // not supported
}

Vector3 MD5ModelNode::getModelScale()
{
return Vector3(1, 1, 1); // not supported
}

void MD5ModelNode::lightsChanged()
{
_lightList->setDirty();
Expand Down
1 change: 1 addition & 0 deletions radiant/md5model/MD5ModelNode.h
Expand Up @@ -35,6 +35,7 @@ class MD5ModelNode :
const model::IModel& getIModel() const override;
model::IModel& getIModel() override;
bool hasModifiedScale() override;
Vector3 getModelScale() override;

void lightsChanged();

Expand Down
5 changes: 5 additions & 0 deletions radiant/model/NullModelNode.cpp
Expand Up @@ -49,6 +49,11 @@ bool NullModelNode::hasModifiedScale()
return false;
}

Vector3 NullModelNode::getModelScale()
{
return Vector3(1,1,1);
}

void NullModelNode::testSelect(Selector& selector, SelectionTest& test) {
_nullModel->testSelect(selector, test, localToWorld());
}
Expand Down
1 change: 1 addition & 0 deletions radiant/model/NullModelNode.h
Expand Up @@ -33,6 +33,7 @@ class NullModelNode :
const IModel& getIModel() const override;
IModel& getIModel() override;
bool hasModifiedScale() override;
Vector3 getModelScale() override;

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

Expand Down
74 changes: 74 additions & 0 deletions radiant/model/ScaledModelExporter.cpp
Expand Up @@ -8,30 +8,104 @@
#include "igame.h"
#include "ientity.h"
#include "iscenegraph.h"
#include "imapresource.h"
#include "os/fs.h"
#include "os/path.h"
#include "registry/registry.h"
#include <fmt/format.h>
#include "string/case_conv.h"
#include "string/convert.h"
#include <regex>

#include "ModelExporter.h"

namespace map
{

namespace
{
const std::string MODELSCALE_KEY = "editor_modelScale";
}

void ScaledModelExporter::initialise()
{
_mapEventConn = GlobalMapModule().signal_mapEvent().connect(
sigc::mem_fun(*this, &ScaledModelExporter::onMapEvent)
);

// #5220: To cover having the scale of resized models preserved in
// auto-saves and prefabs, we subscribe to the exporting events
// and check for any models that still have a modified scale on it.
// That scale value is then written to the hosting entity's spawnargs.
GlobalMapResourceManager().signal_onResourceExporting().connect(
sigc::mem_fun(this, &ScaledModelExporter::onResourceExporting)
);
GlobalMapResourceManager().signal_onResourceExported().connect(
sigc::mem_fun(this, &ScaledModelExporter::onResourceExported)
);
}

void ScaledModelExporter::shutdown()
{
_mapEventConn.disconnect();
}

namespace
{

inline void forEachScaledModel(const scene::IMapRootNodePtr& root,
const std::function<void(Entity&, model::ModelNode&)>& func)
{
root->foreachNode([&](const scene::INodePtr& node)
{
if (Node_isEntity(node))
{
// Find any model nodes below that one
node->foreachNode([&](const scene::INodePtr& child)
{
model::ModelNodePtr model = Node_getModel(child);

if (model && model->hasModifiedScale())
{
// Found a model with modified scale
func(*Node_getEntity(node), *model);
}

return true;
});
}

return true;
});
}

}

void ScaledModelExporter::onResourceExporting(const scene::IMapRootNodePtr& root)
{
// Traverse the exported scene and check for any models that are still scaled, to
// persist that value in the exported scene.
// In "regular" map saves, all models already have been processed here at this point,
// and their scale is reset, so in this case the following traversal does nothing.
forEachScaledModel(root, [](Entity& entity, model::ModelNode& model)
{
// Persist the modified scale by placing a special editor spawnarg
entity.setKeyValue(MODELSCALE_KEY, string::to_string(model.getModelScale()));
});
}

void ScaledModelExporter::onResourceExported(const scene::IMapRootNodePtr& root)
{
// In this post-export event, we remove any scale spawnargs added earlier
forEachScaledModel(root, [](Entity& entity, model::ModelNode& model)
{
if (!entity.getKeyValue(MODELSCALE_KEY).empty())
{
entity.setKeyValue(MODELSCALE_KEY, "");
}
});
}

void ScaledModelExporter::onMapEvent(IMap::MapEvent ev)
{
if (ev == IMap::MapSaving)
Expand Down
4 changes: 2 additions & 2 deletions radiant/model/ScaledModelExporter.h
Expand Up @@ -33,8 +33,8 @@ class ScaledModelExporter
std::string generateUniqueModelFilename(const fs::path& outputPath,
const fs::path& modelPath, const std::string& outputExtension);

void exportModel(const model::IModelExporterPtr& exporter,
const fs::path& modelOutputPath, const std::string& modelFilename);
void onResourceExporting(const scene::IMapRootNodePtr& root);
void onResourceExported(const scene::IMapRootNodePtr& root);
};

}
5 changes: 5 additions & 0 deletions radiant/modelfile/PicoModelNode.cpp
Expand Up @@ -57,6 +57,11 @@ bool PicoModelNode::hasModifiedScale()
return _picoModel->getScale() != Vector3(1, 1, 1);
}

Vector3 PicoModelNode::getModelScale()
{
return _picoModel->getScale();
}

const AABB& PicoModelNode::localAABB() const {
return _picoModel->localAABB();
}
Expand Down
1 change: 1 addition & 0 deletions radiant/modelfile/PicoModelNode.h
Expand Up @@ -52,6 +52,7 @@ class PicoModelNode :
const IModel& getIModel() const override;
IModel& getIModel() override;
bool hasModifiedScale() override;
Vector3 getModelScale() override;

// SkinnedModel implementation
// Skin changed notify
Expand Down

0 comments on commit 2ea5990

Please sign in to comment.