Cogs.Core
MergeCommand.cpp
1#include "MergeCommand.h"
2
3#include "Systems/Core/SceneSystem.h"
4#include "Systems/Core/MeshSystem.h"
5#include "Systems/Core/TransformSystem.h"
6#include "Systems/Core/RenderSystem.h"
7
8#include "Resources/MeshManager.h"
9
10#include "Foundation/Logging/Logger.h"
11
12#include <regex>
13
14namespace
15{
16 Cogs::Logging::Log logger = Cogs::Logging::getLogger("MergeCommand");
17}
18
19namespace Cogs::Core
20{
21 bool canMerge(SceneComponent * sc)
22 {
23 if (sc->children.empty()) return false;
24
25 for (auto & c : sc->children) {
26 auto childSc = c->getComponent<SceneComponent>();
27
28 if (!childSc->children.empty()) return false;
29
30 auto meshComponent = c->getComponent<MeshComponent>();
31
32 if (!meshComponent) return false;
33 }
34
35 return true;
36 }
37
38 template<typename Container>
39 void copyStream(Mesh * mesh, VertexDataType::EVertexDataType dataType, Container & container)
40 {
41 auto & stream = mesh->getStream(dataType);
42
43 auto pData = (const typename Container::value_type *)stream.data();
44
45 container.insert(container.end(), pData, pData + stream.numElements);
46 }
47
48 MeshHandle createMesh(Context * context, SceneComponent * sc)
49 {
50 auto mesh = context->meshManager->create();
51
52 std::vector<glm::vec3> positions;
53 std::vector<glm::vec3> normals;
54 std::vector<glm::vec2> texCoords;
55 std::vector<glm::vec3> tangents;
56
57 std::vector<uint32_t> indexes;
58
59 for (auto & c : sc->children) {
60 if (c->getName().size() && c->getName()[0] == '/') continue;
61
62 auto meshComponent = c->getComponent<MeshComponent>();
63
64 uint32_t offset = (uint32_t)positions.size();
65
66 auto childMesh = meshComponent->meshHandle.resolve();
67
68 copyStream(childMesh, VertexDataType::Positions, positions);
69 if (childMesh->hasStream(VertexDataType::Normals)) copyStream(childMesh, VertexDataType::Normals, normals);
70 if (childMesh->hasStream(VertexDataType::TexCoords0)) copyStream(childMesh, VertexDataType::TexCoords0, texCoords);
71 if (childMesh->hasStream(VertexDataType::Tangents)) copyStream(childMesh, VertexDataType::Tangents, tangents);
72
73 auto meshIndexes = childMesh->getIndexes();
74 for (auto i : meshIndexes) {
75 indexes.emplace_back(i + offset);
76 }
77 }
78
79 mesh->setPositions(positions);
80 if (!normals.empty()) mesh->setNormals(normals);
81 if (!texCoords.empty()) mesh->setTexCoords(texCoords);
82 if (!tangents.empty()) mesh->setTangents(tangents);
83 if (!indexes.empty()) mesh->setIndexes(indexes);
84
85 mesh->setMeshFlag(MeshFlags::ClockwiseWinding);
86 mesh->boundingBox = calculateBounds(mesh.resolve());
87
88 return mesh;
89 }
90
91 void traverse(Context * context, Entity * entity, const std::regex & matchRegex)
92 {
93 auto sc = entity->getComponent<SceneComponent>();
94
95 if (canMerge(sc)) {
96 auto mesh = createMesh(context, sc);
97 auto material = sc->children.front()->getComponent<MeshRenderComponent>()->material;
98
99 if (!mesh->getCount()) return;
100
101 auto oldChildren = sc->children;
102 sc->children.clear();
103
104 for (auto & oldChild : oldChildren) {
105
106 const std::string & name = oldChild->getName();
107 if (std::regex_match(name, matchRegex)) {
108 sc->children.emplace_back(oldChild);
109 }
110 }
111
112 const size_t numMerged = oldChildren.size() - sc->children.size();
113
114 LOG_TRACE(logger, "Merged %zd children up the hierarchy.", numMerged);
115
116 auto existingMc = entity->getComponent<MeshComponent>();
117
118 if (!existingMc) {
119 auto mc = context->meshSystem->createComponent();
120 auto mrc = context->renderSystem->createComponent();
121
122 entity->addComponent(mc);
123 entity->addComponent(mrc);
124
125 mc.resolveComponent<MeshComponent>()->meshHandle = mesh;
126 mrc.resolveComponent<MeshRenderComponent>()->material = material;
127 } else {
128 auto newChild = context->store->createChildEntity("MeshPart", entity, "MergedChild");
129
130 auto mc = newChild->getComponent<MeshComponent>();
131
132 mc->meshHandle = mesh;
133 mc->setChanged();
134
135 auto mrc = newChild->getComponent<MeshRenderComponent>();
136
137 mrc->material = material;
138 mrc->setChanged();
139 }
140 } else {
141 for (auto & child : sc->children) {
142 traverse(context, child.get(), matchRegex);
143 }
144 }
145 }
146}
147
149{
150 // Statoil special tags starts with '/' and has only one '/'.
151 const std::regex matchRegex = std::regex(getOption(options, "matchRegex", "^/[^/]*$").to_string(), std::regex_constants::ECMAScript);
152
153 for (auto & id : entityIds) {
154 auto entity = context->store->getEntityPtr(id);
155
156 traverse(context, entity, matchRegex);
157 }
158}
159
160void Cogs::Core::MergeCommand::undo()
161{
162
163}
164
165namespace Cogs::Core
166{
168 {
169 Mesh * mesh;
170 MeshComponent * meshComponent;
171 MeshRenderComponent * meshRenderComponent;
172 };
173
175 {
176 std::vector<MergeInstance> instances;
177 size_t size = 0;
178 uint32_t signature = 0;
179
180 size_t vertexCountThreshold = 100000;
181 };
182
183 uint32_t getMeshSignature(const Mesh * mesh)
184 {
185 uint32_t signature = 0;
186 for (size_t i = 0; i < 11; ++i) {
187 if (mesh->streamIndexes[i] != Mesh::NoStream) {
188 signature |= 1 << i;
189 }
190 }
191
192 for (size_t i = 11; i < 15; ++i) {
193 if (mesh->streamIndexes[i] != Mesh::NoStream) {
194 signature = (uint32_t)-1;
195 }
196 }
197
198 return signature;
199 }
200
201 void closeMergeState(Context * context, MergeMeshState & state)
202 {
203 if (state.instances.empty()) return;
204
205 if (state.instances.size() >= 2) {
206 auto mesh = context->meshManager->create();
207
208 std::vector<glm::vec3> positions;
209 std::vector<glm::vec3> normals;
210 std::vector<glm::vec2> texCoords;
211 std::vector<glm::vec3> tangents;
212
213 std::vector<uint32_t> indexes;
214
215 for (auto & instance : state.instances) {
216 instance.meshComponent->meshHandle = mesh;
217 instance.meshComponent->setChanged();
218
219 uint32_t offset = (uint32_t)positions.size();
220
221 auto childMesh = instance.mesh;
222
223 copyStream(childMesh, VertexDataType::Positions, positions);
224 if (childMesh->hasStream(VertexDataType::Normals)) copyStream(childMesh, VertexDataType::Normals, normals);
225 if (childMesh->hasStream(VertexDataType::TexCoords0)) copyStream(childMesh, VertexDataType::TexCoords0, texCoords);
226 if (childMesh->hasStream(VertexDataType::Tangents)) copyStream(childMesh, VertexDataType::Tangents, tangents);
227
228 auto meshIndexes = childMesh->getIndexes();
229 for (auto i : meshIndexes) {
230 indexes.emplace_back(i + offset);
231 }
232
233 instance.meshRenderComponent->startIndex = offset;
234 instance.meshRenderComponent->vertexCount = (uint32_t)meshIndexes.size();
235 auto localBounds = context->renderSystem->getLocalBounds(instance.meshRenderComponent);
236 context->renderSystem->setLocalBounds(instance.meshRenderComponent, localBounds);
237 instance.meshRenderComponent->setChanged();
238 }
239
240 mesh->setPositions(positions);
241 if (!normals.empty()) mesh->setNormals(normals);
242 if (!texCoords.empty()) mesh->setTexCoords(texCoords);
243 if (!tangents.empty()) mesh->setTangents(tangents);
244 if (!indexes.empty()) mesh->setIndexes(indexes);
245
247
248 LOG_TRACE(logger, "Merged %zd meshes with %zd verts.", state.instances.size(), state.size);
249 } else {
250 LOG_TRACE(logger, "Skipped %zd mesh(es) with %zd verts.", state.instances.size(), state.size);
251 }
252
253 state.instances = {};
254 state.size = 0;
255 }
256
257 void traverseMerge(Context * context, Entity * entity, MergeMeshState & state)
258 {
259 auto sc = entity->getComponent<SceneComponent>();
260 auto meshComponent = entity->getComponent<MeshComponent>();
261 auto meshRenderComponent = entity->getComponent<MeshRenderComponent>();
262
263 if (meshComponent) {
264 auto meshSignature = getMeshSignature(meshComponent->meshHandle.resolve());
265
266 if (meshSignature != state.signature) {
267 closeMergeState(context, state);
268 } else if (state.size > state.vertexCountThreshold) {
269 closeMergeState(context, state);
270 }
271
272 state.signature = meshSignature;
273
274 state.instances.emplace_back(MergeInstance
275 {
276 meshComponent->meshHandle.resolve(),
277 meshComponent,
278 meshRenderComponent
279 });
280
281 state.size += meshComponent->meshHandle->getCount();
282 }
283
284 for (auto & child : sc->children) {
285 traverseMerge(context, child.get(), state);
286 }
287 }
288
289 void traverseScale(Context * context, Entity * entity, MergeMeshState & state)
290 {
291 auto sc = entity->getComponent<SceneComponent>();
292 auto meshComponent = entity->getComponent<MeshComponent>();
293 auto meshRenderComponent = entity->getComponent<MeshRenderComponent>();
294
295 if (meshComponent) {
296 auto mesh = meshComponent->getMesh(context);
297 auto[pData, pSize, pStride] = mesh->getPositionStream();
298 assert(pStride == sizeof(glm::vec3));
299 auto positions = (glm::vec3 *)pData;
300
301 printf("Scaling mesh resource %u...\n", pSize);
302
303 for (size_t i = 0; i < pSize; ++i) {
304 positions[i] = positions[i] * 0.001f;
305 }
306
307 mesh->unmap(VertexDataType::Positions);
308 mesh->setChanged();
310 auto bounds = calculateBounds(mesh);
311 mesh->setBounds(bounds);
312
313 context->renderSystem->setLocalBounds(meshRenderComponent, bounds);
314 }
315
316 for (auto & child : sc->children) {
317 traverseScale(context, child.get(), state);
318 }
319 }
320}
321
323{
324 MergeMeshState state;
325
326 state.vertexCountThreshold = getOption(options, "vertexCountThreshold", state.vertexCountThreshold);
327
328 for (auto & id : entityIds) {
329 auto entity = context->store->getEntityPtr(id);
330
331 traverseMerge(context, entity, state);
332
333 if (state.instances.size()) closeMergeState(context, state);
334 }
335}
336
337void Cogs::Core::MergeMeshCommand::undo()
338{
339
340}
341
342
344{
345 MergeMeshState state;
346
347 state.vertexCountThreshold = getOption(options, "vertexCountThreshold", state.vertexCountThreshold);
348
349
350 for (auto & id : entityIds) {
351 printf("Scaling mesh entity...\n");
352
353 auto entity = context->store->getEntityPtr(id);
354
355 traverseScale(context, entity, state);
356
357 if (state.instances.size()) closeMergeState(context, state);
358 }
359}
360
361void Cogs::Core::ScaleMeshCommand::undo()
362{
363
364}
Container for components, providing composition of dynamic entities.
Definition: Entity.h:18
T * getComponent() const
Get a pointer to the first component implementing the given type in the entity.
Definition: Entity.h:35
void addComponent(ComponentHandle component)
Definition: Entity.cpp:18
Contains a handle to a Mesh resource to use when rendering using the MeshRenderComponent.
Definition: MeshComponent.h:15
Renders the contents of a MeshComponent using the given materials.
Log implementation class.
Definition: LogManager.h:139
Contains the Engine, Renderer, resource managers and other systems needed to run Cogs....
Cogs::Geometry::BoundingBox COGSCORE_DLL_API calculateBounds(Mesh *mesh)
Calculate a bounding box for the given mesh.
Definition: MeshHelper.cpp:283
StringView getOption(const std::vector< ParsedValue > &options, const StringView &key, const StringView &defaultValue="")
Find and get value of option in vector as a string. Return default if not found.
constexpr Log getLogger(const char(&name)[LEN]) noexcept
Definition: LogManager.h:180
std::vector< ParsedValue > options
Options passed to the command when running in batch mode.
Definition: EditorCommand.h:61
void apply() override
Run the command.
void apply() override
Run the command.
@ BoundingBoxSet
Custom bounding box set, no automatic calculation of bounds should be performed.
Definition: Mesh.h:67
@ ClockwiseWinding
The mesh uses clockwise winding order for it's triangles as opposed to the counter-clockwise default.
Definition: Mesh.h:63
Meshes contain streams of vertex data in addition to index data and options defining geometry used fo...
Definition: Mesh.h:265
void unmap(VertexDataType::EVertexDataType type)
Unmap the stream with the given vertex data type index.
Definition: Mesh.h:900
void setTangents(std::span< const glm::vec3 > tangents)
Set the tangent data of the Mesh.
Definition: Mesh.h:448
void setIndexes(std::span< const uint32_t > collection)
Set the index data to the collection given.
Definition: Mesh.h:596
void setTexCoords(std::span< const glm::vec2 > texCoords)
Set the texture coordinate data of the Mesh.
Definition: Mesh.h:504
StreamReference getPositionStream()
Get the data of the stream containing positions.
Definition: Mesh.cpp:136
void setBounds(Geometry::BoundingBox box)
Set custom bounds for the mesh.
Definition: Mesh.h:298
void setNormals(std::span< const glm::vec3 > normals)
Set the normal data of the Mesh.
Definition: Mesh.h:392
void unsetMeshFlag(MeshFlags::EMeshFlags flag)
Unset the given mesh flag.
Definition: Mesh.h:671
static constexpr int NoStream
Used to indicate no stream.
Definition: Mesh.h:308
void setMeshFlag(MeshFlags::EMeshFlags flag)
Set the given mesh flag.
Definition: Mesh.h:659
void setPositions(std::span< const glm::vec3 > positions)
Set the position data of the Mesh.
Definition: Mesh.h:315
void apply() override
Run the command.
EVertexDataType
Contains data types.
Definition: Mesh.h:26