File: ModelScalePreserver.cpp

package info (click to toggle)
darkradiant 3.9.0-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 41,080 kB
  • sloc: cpp: 264,743; ansic: 10,659; python: 1,852; xml: 1,650; sh: 92; makefile: 21
file content (139 lines) | stat: -rw-r--r-- 3,887 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
#include "ModelScalePreserver.h"

#include "ientity.h"
#include "itransformable.h"
#include "imapresource.h"
#include "itextstream.h"
#include "string/convert.h"

namespace map
{

namespace
{
	const char* const MODELSCALE_KEY = "editor_modelScale";
}

ModelScalePreserver::ModelScalePreserver() :
	_modelScaleKey(MODELSCALE_KEY)
{
	// #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, &ModelScalePreserver::onResourceExporting)
	);
	GlobalMapResourceManager().signal_onResourceExported().connect(
		sigc::mem_fun(this, &ModelScalePreserver::onResourceExported)
	);

	// After map loading this class will try to reconstruct the scale
	GlobalMapModule().signal_mapEvent().connect(
		sigc::mem_fun(this, &ModelScalePreserver::onMapEvent)
	);
}

void ModelScalePreserver::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 ModelScalePreserver::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, [this](Entity& entity, model::ModelNode& model)
	{
		// Persist the modified scale by placing a special editor spawnarg
		entity.setKeyValue(_modelScaleKey, string::to_string(model.getModelScale()));
	});
}

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

void ModelScalePreserver::restoreModelScale(const scene::IMapRootNodePtr& root)
{
	root->foreachNode([this](const scene::INodePtr& node)
	{
		if (Node_isEntity(node))
		{
			Entity* entity = Node_getEntity(node);

			// Search for the editor_ key and apply the scale if found
			auto savedScale = entity->getKeyValue(_modelScaleKey);

			if (!savedScale.empty())
			{
				Vector3 scale = string::convert<Vector3>(savedScale);

				// Find any model nodes below that one
				node->foreachNode([&](const scene::INodePtr& child)
				{
					model::ModelNodePtr model = Node_getModel(child);
					ITransformablePtr transformable = scene::node_cast<ITransformable>(child);

					if (model && transformable)
					{
						rMessage() << "Restoring model scale on node " << child->name() << std::endl;

						transformable->setType(TRANSFORM_PRIMITIVE);
						transformable->setScale(scale);
						transformable->freezeTransform();
					}

					return true;
				});

				// Clear the spawnarg now that we've applied it
				entity->setKeyValue(_modelScaleKey, "");
			}
		}

		return true;
	});
}

void ModelScalePreserver::onMapEvent(IMap::MapEvent ev)
{
	if (ev == IMap::MapLoaded)
	{
		// After loading, restore the scale if it gets recovered
		restoreModelScale(GlobalMapModule().getRoot());
	}
}

}