/
ScaledModelExporter.cpp
191 lines (147 loc) · 4.75 KB
/
ScaledModelExporter.cpp
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
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
#include "ScaledModelExporter.h"
#include <map>
#include <fstream>
#include "i18n.h"
#include "iundo.h"
#include "itextstream.h"
#include "igame.h"
#include "ientity.h"
#include "iscenegraph.h"
#include "os/fs.h"
#include "os/path.h"
#include "registry/registry.h"
#include <fmt/format.h>
#include "string/case_conv.h"
#include <regex>
#include <climits>
#include "ModelExporter.h"
namespace map
{
void ScaledModelExporter::initialise()
{
_mapEventConn = GlobalMapModule().signal_mapEvent().connect(
sigc::mem_fun(*this, &ScaledModelExporter::onMapEvent)
);
}
void ScaledModelExporter::shutdown()
{
_mapEventConn.disconnect();
}
void ScaledModelExporter::onMapEvent(IMap::MapEvent ev)
{
if (ev == IMap::MapSaving)
{
saveScaledModels();
}
}
void ScaledModelExporter::saveScaledModels()
{
std::map<scene::INodePtr, model::ModelNodePtr> nodesToProcess;
// Find any models with modified scale
GlobalSceneGraph().foreachNode([&](const scene::INodePtr& node)
{
if (Node_isEntity(node))
{
// Find any model nodes below that one
node->foreachNode([&](const scene::INodePtr& child)
{
model::ModelNodePtr candidate = Node_getModel(child);
if (candidate && candidate->hasModifiedScale())
{
nodesToProcess.insert(std::make_pair(node, candidate));
}
return true;
});
}
return true;
});
// Do we have any models with modified scale?
if (!nodesToProcess.empty())
{
UndoableCommand scaleModels("saveScaledModels");
for (auto& pair : nodesToProcess)
{
saveScaledModel(pair.first, pair.second);
}
}
}
void ScaledModelExporter::saveScaledModel(const scene::INodePtr& entityNode, const model::ModelNodePtr& modelNode)
{
// Request the default format from the preferences
std::string outputExtension = registry::getValue<std::string>(RKEY_DEFAULT_MODEL_EXPORT_FORMAT);
string::to_lower(outputExtension);
rMessage() << "Model format used for export: " << outputExtension <<
" (this can be changed in the preferences)" << std::endl;
// Save the scaled model in the configured format
model::IModelExporterPtr exporter = GlobalModelFormatManager().getExporter(outputExtension);
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, Matrix4::getIdentity());
}
// Get the current model file name
Entity* entity = Node_getEntity(entityNode);
fs::path targetPath = getWritableGamePath();
fs::path modelPath = "models/map_specific/scaled";
// Ensure the output path exists
targetPath /= modelPath;
fs::create_directories(targetPath);
fs::path modelKeyValue = entity->getKeyValue("model");
rMessage() << "Exporting scaled model for entity " << entity->getKeyValue("name") <<
": " << modelKeyValue.string() << std::endl;
// Generate a new model name, à la "haystack_scaled3.ase"
std::string modelFilename = generateUniqueModelFilename(targetPath, modelKeyValue, outputExtension);
// assemble the new model spawnarg
modelPath /= modelFilename;
// Export to temporary file and rename afterwards
try
{
exporter->exportToPath(targetPath.string(), modelFilename);
std::string newModelKey = os::standardPath(modelPath.string());
entity->setKeyValue("model", newModelKey);
rMessage() << "Done exporting scaled model, new model key is " << newModelKey << std::endl;
}
catch (std::runtime_error& ex)
{
rError() << "Failed to export scaled model: " << ex.what() << std::endl;
}
}
std::string ScaledModelExporter::generateUniqueModelFilename(
const fs::path& outputPath, const fs::path& modelPath, const std::string& outputExtension)
{
std::string modelFilename = modelPath.filename().string();
// Remove any previously existing "_scaledN" suffix
std::regex expr("_scaled\\d+\\.");
modelFilename = std::regex_replace(modelFilename, expr, ".");
std::string filenameNoExt = os::replaceExtension(modelFilename, "");
int i = 0;
while (++i < INT_MAX)
{
std::string generatedFilename = fmt::format("{0}_scaled{1:d}.{2}", filenameNoExt, i, outputExtension);
fs::path targetFile = outputPath / generatedFilename;
if (!fs::exists(targetFile))
{
return generatedFilename; // break the loop
}
}
throw new std::runtime_error("Could not generate a unique model filename.");
}
fs::path ScaledModelExporter::getWritableGamePath()
{
fs::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;
}
return targetPath;
}
}