TDME2  1.9.200
ModelTools.cpp
Go to the documentation of this file.
2 
3 #include <array>
4 #include <memory>
5 #include <string>
6 #include <unordered_map>
7 #include <unordered_set>
8 #include <vector>
9 
10 #include <tdme/tdme.h>
11 #include <tdme/engine/Texture.h>
15 #include <tdme/engine/model/Face.h>
21 #include <tdme/engine/model/Node.h>
27 #include <tdme/engine/Transform.h>
28 #include <tdme/math/Matrix4x4.h>
29 #include <tdme/math/Vector2.h>
30 #include <tdme/math/Vector3.h>
32 #include <tdme/utilities/Console.h>
35 
36 using std::array;
37 using std::make_unique;
38 using std::string;
39 using std::to_string;
40 using std::unique_ptr;
41 using std::unordered_map;
42 using std::unordered_set;
43 using std::vector;
44 
70 
71 ModelTools::VertexOrder ModelTools::determineVertexOrder(const vector<Vector3>& vertices)
72 {
73  auto edgeSum = 0;
74  for (auto i = 0; i < vertices.size(); i++) {
75  const auto& currentVertex = vertices[i];
76  const auto& nextVertex = vertices[(i + 1) % vertices.size()];
77  edgeSum += (nextVertex[0] - currentVertex[0]) * (nextVertex[1] - currentVertex[1]) * (nextVertex[2] - currentVertex[2]);
78  }
79  if (edgeSum >= 0) {
80  return VERTEXORDER_CLOCKWISE;
81  } else {
83  }
84 }
85 
87 {
89 }
90 
91 void ModelTools::prepareForIndexedRendering(const unordered_map<string, Node*>& nodes)
92 {
93  // we need to prepare the node for indexed rendering
94  for (const auto& [nodeId, node]: nodes) {
95  const auto& nodeVertices = node->getVertices();
96  const auto& nodeNormals = node->getNormals();
97  const auto& nodeTextureCoordinates = node->getTextureCoordinates();
98  const auto& nodeTangents = node->getTangents();
99  const auto& nodeBitangents = node->getBitangents();
100  const auto& nodeOrigins = node->getOrigins();
101  vector<int32_t> vertexMapping;
102  vector<Vector3> indexedVertices;
103  vector<Vector3> indexedNormals;
104  vector<Vector2> indexedTextureCoordinates;
105  vector<Vector3> indexedTangents;
106  vector<Vector3> indexedBitangents;
107  vector<Vector3> indexedOrigins;
108  // construct indexed vertex data suitable for indexed rendering
109  auto preparedIndices = 0;
110  auto newFacesEntities = node->getFacesEntities();
111  for (auto& newFacesEntity: newFacesEntities) {
112  auto newFaces = newFacesEntity.getFaces();
113  for (auto& face: newFaces) {
114  auto faceVertexIndices = face.getVertexIndices();
115  auto faceNormalIndices = face.getNormalIndices();
116  auto faceTextureIndices = face.getTextureCoordinateIndices();
117  auto faceTangentIndices = face.getTangentIndices();
118  auto faceBitangentIndices = face.getBitangentIndices();
119  array<int32_t, 3> indexedFaceVertexIndices;
120  for (int16_t idx = 0; idx < 3; idx++) {
121  auto nodeVertexIndex = faceVertexIndices[idx];
122  auto nodeNormalIndex = faceNormalIndices[idx];
123  auto nodeTextureCoordinateIndex = faceTextureIndices[idx];
124  auto nodeTangentIndex = faceTangentIndices[idx];
125  auto nodeBitangentIndex = faceBitangentIndices[idx];
126  auto vertex = &nodeVertices[nodeVertexIndex];
127  auto normal = &nodeNormals[nodeNormalIndex];
128  auto textureCoordinate = nodeTextureCoordinates.size() > 0?&nodeTextureCoordinates[nodeTextureCoordinateIndex]:static_cast<Vector2*>(nullptr);
129  auto tangent = nodeTangents.size() > 0 ? &nodeTangents[nodeTangentIndex] : static_cast<Vector3*>(nullptr);
130  auto bitangent = nodeBitangents.size() > 0 ? &nodeBitangents[nodeBitangentIndex] : static_cast<Vector3*>(nullptr);
131  auto origin = nodeOrigins.size() > 0 ? &nodeOrigins[nodeVertexIndex] : static_cast<Vector3*>(nullptr);
132  auto newIndex = preparedIndices;
133  for (auto i = 0; i < preparedIndices; i++)
134  if (indexedVertices[i].equals(*vertex) &&
135  indexedNormals[i].equals(*normal) &&
136  (textureCoordinate == nullptr || indexedTextureCoordinates[i].equals(*textureCoordinate)) &&
137  (tangent == nullptr || indexedTangents[i].equals(*tangent)) &&
138  (bitangent == nullptr || indexedBitangents[i].equals(*bitangent))) {
139  newIndex = i;
140  break;
141  }
142  if (newIndex == preparedIndices) {
143  vertexMapping.push_back(nodeVertexIndex);
144  indexedVertices.push_back(*vertex);
145  indexedNormals.push_back(*normal);
146  if (textureCoordinate != nullptr) indexedTextureCoordinates.push_back(*textureCoordinate);
147  if (tangent != nullptr) indexedTangents.push_back(*tangent);
148  if (bitangent != nullptr) indexedBitangents.push_back(*bitangent);
149  if (origin != nullptr) indexedOrigins.push_back(*origin);
150  preparedIndices++;
151  }
152  indexedFaceVertexIndices[idx] = newIndex;
153  }
154  face.setIndexedRenderingIndices(indexedFaceVertexIndices);
155  }
156  newFacesEntity.setFaces(newFaces);
157  }
158  node->setFacesEntities(newFacesEntities);
159  // remap skinning
160  auto skinning = node->getSkinning();
161  if (skinning != nullptr) {
162  prepareForIndexedRendering(skinning, vertexMapping, preparedIndices);
163  }
164  node->setVertices(indexedVertices);
165  node->setNormals(indexedNormals);
166  if (nodeTextureCoordinates.size() > 0) {
167  node->setTextureCoordinates(indexedTextureCoordinates);
168  }
169  node->setTangents(indexedTangents);
170  node->setBitangents(indexedBitangents);
171  if (nodeOrigins.size() > 0) {
172  node->setOrigins(indexedOrigins);
173  }
174  // process sub nodes
175  prepareForIndexedRendering(node->getSubNodes());
176  }
177 }
178 
179 void ModelTools::prepareForIndexedRendering(Skinning* skinning, const vector<int32_t>& vertexMapping, int32_t vertices)
180 {
181  const auto& originalVerticesJointsWeights = skinning->getVerticesJointsWeights();
182  vector<vector<JointWeight>> verticesJointsWeights;
183  verticesJointsWeights.resize(vertices);
184  for (auto i = 0; i < vertices; i++) {
185  auto vertexOriginalMappedToIdx = vertexMapping[i];
186  verticesJointsWeights[i].resize(originalVerticesJointsWeights[vertexOriginalMappedToIdx].size());
187  for (auto j = 0; j < verticesJointsWeights[i].size(); j++) {
188  verticesJointsWeights[i][j] = originalVerticesJointsWeights[vertexOriginalMappedToIdx][j];
189  }
190  }
191  skinning->setVerticesJointsWeights(verticesJointsWeights);
192 }
193 
195 {
196  // determine joints and mark them as joints
197  auto nodes = model->getNodes();
198  for (const auto& [nodeId, node]: nodes) {
199  auto skinning = node->getSkinning();
200  // do we have a skinning
201  if (skinning != nullptr) {
202  // yep
203  for (const auto& joint : skinning->getJoints()) {
204  auto jointNodeIt = nodes.find(joint.getNodeId());
205  if (jointNodeIt != nodes.end()) {
206  setJoint(jointNodeIt->second);
207  }
208  }
209  }
210  }
211 }
212 
214 {
215  root->setJoint(true);
216  for (const auto& [subNodeId, subNode]: root->getSubNodes()) {
217  setJoint(subNode);
218  }
219 }
220 
222 {
223  // fix animation length
224  auto defaultAnimation = model->getAnimationSetup(Model::ANIMATIONSETUP_DEFAULT);
225  if (defaultAnimation != nullptr) {
226  for (const auto& [subNodeId, subNode]: model->getSubNodes()) {
227  fixAnimationLength(subNode, defaultAnimation->getFrames());
228  }
229  }
230 }
231 
232 void ModelTools::fixAnimationLength(Node* root, int32_t frames)
233 {
234  auto animation = root->getAnimation();
235  if (animation != nullptr) {
236  vector<Matrix4x4> newTransformMatrices;
237  auto oldTransformMatrices = root->getAnimation()->getTransformMatrices();
238  auto animation = make_unique<Animation>();
239  newTransformMatrices.resize(frames);
240  for (auto i = 0; i < frames; i++) {
241  if (i < oldTransformMatrices.size()) {
242  newTransformMatrices[i] = oldTransformMatrices[i];
243  } else {
244  newTransformMatrices[i].identity();
245  }
246  }
247  animation->setTransformMatrices(newTransformMatrices);
248  root->setAnimation(animation.release());
249  }
250  for (const auto& [subNodeId, subNode]: root->getSubNodes()) {
251  fixAnimationLength(subNode, frames);
252  }
253 }
254 
256  return model->getAnimationSetup(Model::ANIMATIONSETUP_DEFAULT) != nullptr;
257 }
258 
259 void ModelTools::createDefaultAnimation(Model* model, int32_t frames)
260 {
261  // add default model animation setup if not yet done
262  auto defaultAnimation = model->getAnimationSetup(Model::ANIMATIONSETUP_DEFAULT);
263  if (defaultAnimation == nullptr) {
264  model->addAnimationSetup(Model::ANIMATIONSETUP_DEFAULT, 0, frames - 1, true);
265  } else {
266  // check default animation setup
267  if (defaultAnimation->getStartFrame() != 0 || defaultAnimation->getEndFrame() != frames - 1) {
268  Console::println(string("Warning: default animation mismatch"));
269  }
270  if (frames - 1 > defaultAnimation->getEndFrame()) {
271  Console::println(string("Warning: default animation mismatch, will be fixed"));
272  model->addAnimationSetup(Model::ANIMATIONSETUP_DEFAULT, 0, frames - 1, true);
273  }
274  }
275 }
276 
277 Material* ModelTools::cloneMaterial(const Material* material, const string& id) {
278  auto clonedMaterial = make_unique<Material>(id.empty()?material->getId():id);
279  auto specularMaterialProperties = material->getSpecularMaterialProperties();
280  if (specularMaterialProperties != nullptr) {
281  auto clonedSpecularMaterialProperties = make_unique<SpecularMaterialProperties>();
282  clonedSpecularMaterialProperties->setAmbientColor(specularMaterialProperties->getAmbientColor());
283  clonedSpecularMaterialProperties->setDiffuseColor(specularMaterialProperties->getDiffuseColor());
284  clonedSpecularMaterialProperties->setEmissionColor(specularMaterialProperties->getEmissionColor());
285  clonedSpecularMaterialProperties->setSpecularColor(specularMaterialProperties->getSpecularColor());
286  clonedSpecularMaterialProperties->setShininess(specularMaterialProperties->getShininess());
287  clonedSpecularMaterialProperties->setTextureAtlasSize(specularMaterialProperties->getTextureAtlasSize());
288  auto clonedSpecularMaterialPropertiesDiffuseTexture = specularMaterialProperties->getDiffuseTexture();
289  if (clonedSpecularMaterialPropertiesDiffuseTexture != nullptr) {
290  clonedSpecularMaterialPropertiesDiffuseTexture->acquireReference();
291  clonedSpecularMaterialProperties->setDiffuseTexture(clonedSpecularMaterialPropertiesDiffuseTexture);
292  clonedSpecularMaterialProperties->setDiffuseTexturePathName(specularMaterialProperties->getDiffuseTexturePathName());
293  clonedSpecularMaterialProperties->setDiffuseTextureFileName(specularMaterialProperties->getDiffuseTextureFileName());
294  }
295  clonedSpecularMaterialProperties->setDiffuseTextureTransparency(specularMaterialProperties->hasDiffuseTextureTransparency());
296  clonedSpecularMaterialProperties->setDiffuseTextureMaskedTransparency(specularMaterialProperties->hasDiffuseTextureMaskedTransparency());
297  clonedSpecularMaterialProperties->setDiffuseTextureMaskedTransparencyThreshold(specularMaterialProperties->getDiffuseTextureMaskedTransparencyThreshold());
298  auto clonedSpecularMaterialPropertiesSpecularTexture = specularMaterialProperties->getSpecularTexture();
299  if (clonedSpecularMaterialPropertiesSpecularTexture != nullptr) {
300  clonedSpecularMaterialPropertiesSpecularTexture->acquireReference();
301  clonedSpecularMaterialProperties->setSpecularTexture(clonedSpecularMaterialPropertiesSpecularTexture);
302  clonedSpecularMaterialProperties->setSpecularTexturePathName(specularMaterialProperties->getSpecularTexturePathName());
303  clonedSpecularMaterialProperties->setSpecularTextureFileName(specularMaterialProperties->getSpecularTextureFileName());
304  }
305  auto clonedSpecularMaterialPropertiesNormalTexture = specularMaterialProperties->getNormalTexture();
306  if (clonedSpecularMaterialPropertiesNormalTexture != nullptr) {
307  clonedSpecularMaterialPropertiesNormalTexture->acquireReference();
308  clonedSpecularMaterialProperties->setNormalTexture(clonedSpecularMaterialPropertiesNormalTexture);
309  clonedSpecularMaterialProperties->setNormalTexturePathName(specularMaterialProperties->getNormalTexturePathName());
310  clonedSpecularMaterialProperties->setNormalTextureFileName(specularMaterialProperties->getNormalTextureFileName());
311  }
312  clonedMaterial->setSpecularMaterialProperties(clonedSpecularMaterialProperties.release());
313  }
314  return clonedMaterial.release();
315 }
316 
317 void ModelTools::cloneNode(Node* sourceNode, Model* targetModel, Node* targetParentNode, bool cloneMesh) {
318  auto clonedNode = make_unique<Node>(targetModel, targetParentNode, sourceNode->getId(), sourceNode->getName());
319  clonedNode->setTransformMatrix(sourceNode->getTransformMatrix());
320  clonedNode->setJoint(sourceNode->isJoint());
321  if (cloneMesh == true) {
322  clonedNode->setVertices(sourceNode->getVertices());
323  clonedNode->setNormals(sourceNode->getNormals());
324  clonedNode->setTextureCoordinates(sourceNode->getTextureCoordinates());
325  clonedNode->setTangents(sourceNode->getTangents());
326  clonedNode->setBitangents(sourceNode->getBitangents());
327  clonedNode->setOrigins(sourceNode->getOrigins());
328  auto facesEntities = clonedNode->getFacesEntities();
329  for (const auto& facesEntity: sourceNode->getFacesEntities()) {
330  if (facesEntity.getMaterial() == nullptr) continue;
331  Material* material = nullptr;
332  auto materialIt = targetModel->getMaterials().find(facesEntity.getMaterial()->getId());
333  if (materialIt == targetModel->getMaterials().end()) {
334  auto newMaterial = unique_ptr<Material>(cloneMaterial(facesEntity.getMaterial()));
335  targetModel->getMaterials()[material->getId()] = newMaterial.get();
336  material = newMaterial.release();
337  } else {
338  material = materialIt->second;
339  }
340  auto clonedFacesEntity = FacesEntity(clonedNode.get(), facesEntity.getId());
341  clonedFacesEntity.setMaterial(material);
342  clonedFacesEntity.setFaces(facesEntity.getFaces());
343  facesEntities.push_back(clonedFacesEntity);
344  }
345  clonedNode->setFacesEntities(facesEntities);
346  }
347  if (sourceNode->getAnimation() != nullptr) {
348  auto clonedAnimation = make_unique<Animation>();
349  clonedAnimation->setTransformMatrices(sourceNode->getAnimation()->getTransformMatrices());
350  clonedNode->setAnimation(clonedAnimation.release());
351  }
352  targetModel->getNodes()[clonedNode->getId()] = clonedNode.get();
353  if (targetParentNode == nullptr) {
354  targetModel->getSubNodes()[clonedNode->getId()] = clonedNode.get();
355  } else {
356  targetParentNode->getSubNodes()[clonedNode->getId()] = clonedNode.get();
357  }
358  auto newTargetParentNode = clonedNode.release();
359  for (const auto& [sourceSubNodeId, sourceSubNode]: sourceNode->getSubNodes()) {
360  cloneNode(sourceSubNode, targetModel, newTargetParentNode, cloneMesh);
361  }
362 }
363 
364 void ModelTools::partitionNode(Node* sourceNode, unordered_map<string, Model*>& modelsByPartition, unordered_map<string, Vector3>& modelsPosition, const Matrix4x4& parentTransformMatrix) {
365  // TODO: performance: faces handling is very suboptimal currently, however this is only executed in SceneEditor if doing partitioning
366  Vector3 faceCenter;
367 
368  Matrix4x4 transformMatrix;
369  transformMatrix.set(sourceNode->getTransformMatrix());
370  transformMatrix.multiply(parentTransformMatrix);
371 
372  Vector3 vertex0;
373  Vector3 vertex1;
374  Vector3 vertex2;
375  Vector3 normal0;
376  Vector3 normal1;
377  Vector3 normal2;
378  Vector2 textureCoordinate0;
379  Vector2 textureCoordinate1;
380  Vector2 textureCoordinate2;
381  Vector3 tangent0;
382  Vector3 tangent1;
383  Vector3 tangent2;
384  Vector3 bitangent0;
385  Vector3 bitangent1;
386  Vector3 bitangent2;
387 
388  Vector3 vertex0Transformed;
389  Vector3 vertex1Transformed;
390  Vector3 vertex2Transformed;
391 
392  auto sourceNodeId = sourceNode->getModel()->getId();
393  auto sourceNodeName = sourceNode->getModel()->getName();
394 
395  // TODO: maybe check if id and node do have real file endings like .tm, .dae, .fbx or something
396  if (StringTools::lastIndexOf(sourceNodeId, '.') != -1) {
397  sourceNodeId = StringTools::substring(sourceNodeId, 0, StringTools::lastIndexOf(sourceNodeId, '.') - 1);
398  }
399  if (StringTools::lastIndexOf(sourceNodeName, '.') != -1) {
400  sourceNodeName = StringTools::substring(sourceNodeName, 0, StringTools::lastIndexOf(sourceNodeName, '.') - 1);
401  }
402 
403  //
404  unordered_map<string, Node*> partitionModelNodes;
405 
406  // partition model node vertices and such
407  unordered_map<string, vector<Vector3>> partitionModelNodesVertices;
408  unordered_map<string, vector<Vector3>> partitionModelNodesNormals;
409  unordered_map<string, vector<Vector2>> partitionModelNodesTextureCoordinates;
410  unordered_map<string, vector<Vector3>> partitionModelNodesTangents;
411  unordered_map<string, vector<Vector3>> partitionModelNodesBitangents;
412  unordered_map<string, vector<FacesEntity>> partitionModelNodesFacesEntities;
413 
414  for (const auto& facesEntity: sourceNode->getFacesEntities()) {
415  bool haveTextureCoordinates = facesEntity.isTextureCoordinatesAvailable();
416  bool haveTangentsBitangents = facesEntity.isTangentBitangentAvailable();
417  for (const auto& face: facesEntity.getFaces()) {
418  // get face vertices and such
419  const auto& vertexIndices = face.getVertexIndices();
420  const auto& normalIndices = face.getNormalIndices();
421  const auto& textureCoordinatesIndices = face.getTextureCoordinateIndices();
422  const auto& tangentIndices = face.getTangentIndices();
423  const auto& bitangentIndices = face.getBitangentIndices();
424  vertex0.set(sourceNode->getVertices()[vertexIndices[0]]);
425  vertex1.set(sourceNode->getVertices()[vertexIndices[1]]);
426  vertex2.set(sourceNode->getVertices()[vertexIndices[2]]);
427  normal0.set(sourceNode->getNormals()[normalIndices[0]]);
428  normal1.set(sourceNode->getNormals()[normalIndices[1]]);
429  normal2.set(sourceNode->getNormals()[normalIndices[2]]);
430  if (haveTextureCoordinates == true) {
431  textureCoordinate0.set(sourceNode->getTextureCoordinates()[textureCoordinatesIndices[0]]);
432  textureCoordinate1.set(sourceNode->getTextureCoordinates()[textureCoordinatesIndices[1]]);
433  textureCoordinate2.set(sourceNode->getTextureCoordinates()[textureCoordinatesIndices[2]]);
434  }
435  if (haveTangentsBitangents == true) {
436  tangent0.set(sourceNode->getTangents()[tangentIndices[0]]);
437  tangent1.set(sourceNode->getTangents()[tangentIndices[1]]);
438  tangent2.set(sourceNode->getTangents()[tangentIndices[2]]);
439  bitangent0.set(sourceNode->getBitangents()[bitangentIndices[0]]);
440  bitangent1.set(sourceNode->getBitangents()[bitangentIndices[1]]);
441  bitangent2.set(sourceNode->getBitangents()[bitangentIndices[2]]);
442  }
443 
444  // find out partition by transforming vertices into world coordinates
445  vertex0Transformed = transformMatrix.multiply(vertex0);
446  vertex1Transformed = transformMatrix.multiply(vertex1);
447  vertex2Transformed = transformMatrix.multiply(vertex2);
448  faceCenter.set(vertex0Transformed);
449  faceCenter.add(vertex1Transformed);
450  faceCenter.add(vertex2Transformed);
451  faceCenter.scale(1.0f / 3.0f);
452  auto minX = Math::min(Math::min(vertex0Transformed.getX(), vertex1Transformed.getX()), vertex2Transformed.getX());
453  auto minY = Math::min(Math::min(vertex0Transformed.getY(), vertex1Transformed.getY()), vertex2Transformed.getY());
454  auto minZ = Math::min(Math::min(vertex0Transformed.getZ(), vertex1Transformed.getZ()), vertex2Transformed.getZ());
455  auto partitionX = (int)(minX / 64.0f);
456  auto partitionY = (int)(minY / 64.0f);
457  auto partitionZ = (int)(minZ / 64.0f);
458 
459  // key
460  string partitionModelKey =
461  to_string(partitionX) + "," +
462  to_string(partitionY) + "," +
463  to_string(partitionZ);
464 
465  // get model
466  auto partitionModel = modelsByPartition[partitionModelKey];
467  if (partitionModel == nullptr) {
468  auto newPartitionModel = make_unique<Model>(
469  sourceNodeId + "." + partitionModelKey,
470  sourceNodeName + "." + partitionModelKey,
471  sourceNode->getModel()->getUpVector(),
472  sourceNode->getModel()->getRotationOrder(),
473  nullptr
474  );
475  modelsByPartition[partitionModelKey] = newPartitionModel.get();
476  modelsPosition[partitionModelKey].set(partitionX * 64.0f, partitionY * 64.0f, partitionZ * 64.0f);
477  partitionModel = newPartitionModel.release();
478  }
479 
480  // get node
481  auto partitionModelNode = partitionModelNodes[partitionModelKey];
482  partitionModelNode = partitionModel->getNodeById(sourceNode->getId());
483  if (partitionModelNode == nullptr) {
484  auto newPartitionModelNode = make_unique<Node>(
485  partitionModel,
486  sourceNode->getParentNode() == nullptr?nullptr:partitionModel->getNodeById(sourceNode->getParentNode()->getId()),
487  sourceNode->getId(),
488  sourceNode->getName()
489  );
490  newPartitionModelNode->setTransformMatrix(sourceNode->getTransformMatrix());
491  if (sourceNode->getParentNode() == nullptr) {
492  partitionModel->getSubNodes()[newPartitionModelNode->getId()] = newPartitionModelNode.get();
493  } else {
494  newPartitionModelNode->getParentNode()->getSubNodes()[newPartitionModelNode->getId()] = newPartitionModelNode.get();
495  }
496  partitionModel->getNodes()[newPartitionModelNode->getId()] = newPartitionModelNode.get();
497  partitionModelNodes[partitionModelKey] = newPartitionModelNode.get();
498  partitionModelNode = newPartitionModelNode.release();
499  }
500 
501  // get faces entity
502  FacesEntity* partitionModelNodeFacesEntity = nullptr;
503  for (auto& partitionModelNodeFacesEntityExisting: partitionModelNodesFacesEntities[partitionModelKey]) {
504  if (partitionModelNodeFacesEntityExisting.getId() == facesEntity.getId()) {
505  partitionModelNodeFacesEntity = &partitionModelNodeFacesEntityExisting;
506  }
507  }
508  if (partitionModelNodeFacesEntity == nullptr) {
509  auto newFacesEntity = FacesEntity(
510  partitionModelNode,
511  facesEntity.getId()
512  );
513  partitionModelNodesFacesEntities[partitionModelKey].push_back(newFacesEntity);
514  partitionModelNodeFacesEntity = &newFacesEntity;
515  auto partitionModelNodeFacesEntityMaterial = partitionModel->getMaterials()[facesEntity.getMaterial()->getId()];
516  if (partitionModelNodeFacesEntityMaterial == nullptr) {
517  partitionModelNodeFacesEntityMaterial = cloneMaterial(facesEntity.getMaterial());
518  partitionModel->getMaterials()[facesEntity.getMaterial()->getId()] = partitionModelNodeFacesEntityMaterial;
519  }
520  partitionModelNodeFacesEntity->setMaterial(partitionModelNodeFacesEntityMaterial);
521  }
522 
523  auto faces = partitionModelNodeFacesEntity->getFaces();
524 
525  // add vertices and such
526  auto verticesIdx = partitionModelNodesVertices[partitionModelKey].size();
527  partitionModelNodesVertices[partitionModelKey].push_back(vertex0);
528  partitionModelNodesVertices[partitionModelKey].push_back(vertex1);
529  partitionModelNodesVertices[partitionModelKey].push_back(vertex2);
530  partitionModelNodesNormals[partitionModelKey].push_back(normal0);
531  partitionModelNodesNormals[partitionModelKey].push_back(normal1);
532  partitionModelNodesNormals[partitionModelKey].push_back(normal2);
533  if (haveTextureCoordinates == true) {
534  partitionModelNodesTextureCoordinates[partitionModelKey].push_back(textureCoordinate0);
535  partitionModelNodesTextureCoordinates[partitionModelKey].push_back(textureCoordinate1);
536  partitionModelNodesTextureCoordinates[partitionModelKey].push_back(textureCoordinate2);
537  }
538  if (haveTangentsBitangents == true) {
539  partitionModelNodesTangents[partitionModelKey].push_back(tangent0);
540  partitionModelNodesTangents[partitionModelKey].push_back(tangent1);
541  partitionModelNodesTangents[partitionModelKey].push_back(tangent2);
542  partitionModelNodesBitangents[partitionModelKey].push_back(bitangent0);
543  partitionModelNodesBitangents[partitionModelKey].push_back(bitangent1);
544  partitionModelNodesBitangents[partitionModelKey].push_back(bitangent2);
545  }
546  Face newFace =
547  Face(
548  partitionModelNode,
549  verticesIdx + 0,
550  verticesIdx + 1,
551  verticesIdx + 2,
552  verticesIdx + 0,
553  verticesIdx + 1,
554  verticesIdx + 2
555  );
556  if (haveTextureCoordinates == true) {
558  verticesIdx + 0,
559  verticesIdx + 1,
560  verticesIdx + 2
561  );
562  }
563  if (haveTangentsBitangents == true) {
564  newFace.setTangentIndices(
565  verticesIdx + 0,
566  verticesIdx + 1,
567  verticesIdx + 2
568  );
569  newFace.setBitangentIndices(
570  verticesIdx + 0,
571  verticesIdx + 1,
572  verticesIdx + 2
573  );
574  }
575  faces.push_back(newFace);
576  partitionModelNodeFacesEntity->setFaces(faces);
577  }
578  }
579 
580  // set vertices and such
581  for (const auto& [partitionModelKey, model]: modelsByPartition) {
582  if (partitionModelNodes[partitionModelKey] == nullptr) continue;
583  partitionModelNodes[partitionModelKey]->setVertices(partitionModelNodesVertices[partitionModelKey]);
584  partitionModelNodes[partitionModelKey]->setNormals(partitionModelNodesNormals[partitionModelKey]);
585  partitionModelNodes[partitionModelKey]->setTextureCoordinates(partitionModelNodesTextureCoordinates[partitionModelKey]);
586  partitionModelNodes[partitionModelKey]->setTangents(partitionModelNodesTangents[partitionModelKey]);
587  partitionModelNodes[partitionModelKey]->setBitangents(partitionModelNodesBitangents[partitionModelKey]);
588  partitionModelNodes[partitionModelKey]->setFacesEntities(partitionModelNodesFacesEntities[partitionModelKey]);
589  }
590 
591  // partition sub nodes
592  for (const auto& [sourceNodeId, sourceNode]: sourceNode->getSubNodes()) {
593  partitionNode(sourceNode, modelsByPartition, modelsPosition, transformMatrix);
594  }
595 }
596 
597 void ModelTools::partition(Model* model, const Transform& transform, unordered_map<string, Model*>& modelsByPartition, unordered_map<string, Vector3>& modelsPosition) {
598  Matrix4x4 transformMatrix;
599  transformMatrix.set(model->getImportTransformMatrix());
600  transformMatrix.multiply(transform.getTransformMatrix());
601  for (const auto& [subNodeId, subNode]: model->getSubNodes()) {
602  partitionNode(subNode, modelsByPartition, modelsPosition, transformMatrix);
603  }
604  for (const auto& [partitionKey, partitionModel]: modelsByPartition) {
605  partitionModel->setImportTransformMatrix(model->getImportTransformMatrix());
606  ModelTools::createDefaultAnimation(partitionModel, 0);
607  ModelTools::setupJoints(partitionModel);
608  ModelTools::fixAnimationLength(partitionModel);
610  }
611 }
612 
614  // TODO: a.drewke
615  /*
616  for (auto& facesEntity: node->getFacesEntities()) {
617  facesEntity.getFaces().shrink_to_fit();
618  }
619 
620  node->getFacesEntities().shrink_to_fit();
621  node->getVertices().shrink_to_fit();
622  node->getNormals().shrink_to_fit();
623  node->getTextureCoordinates().shrink_to_fit();
624  node->getTangents().shrink_to_fit();
625  node->getBitangents().shrink_to_fit();
626 
627  // do child nodes
628  for (const auto& [subNodeId, subNode]: node->getSubNodes()) {
629  shrinkToFit(subNode);
630  }
631  */
632 }
633 
635  for (const auto& [subNodeId, subNode]: model->getSubNodes()) {
636  shrinkToFit(subNode);
637  }
638 }
639 
640 float ModelTools::computeNormals(Node* node, ProgressCallback* progressCallback, float incrementPerFace, float progress) {
641  node->setNormals(vector<Vector3>());
642  array<Vector3, 3> vertices;
643  Vector3 normal;
644  auto facesEntityProcessed = 0;
645  vector<Vector3> normals;
646  auto facesEntities = node->getFacesEntities();
647  for (auto& facesEntity: facesEntities) {
648  auto faces = facesEntity.getFaces();
649  for (auto& face: faces) {
650  for (auto i = 0; i < vertices.size(); i++) {
651  vertices[i] = node->getVertices()[face.getVertexIndices()[i]];
652  }
653  normal = computeNormal(vertices);
654  face.setNormalIndices(normals.size(), normals.size() + 1, normals.size() + 2);
655  normals.push_back(normal);
656  normals.push_back(normal);
657  normals.push_back(normal);
658  if (progressCallback != nullptr) {
659  progress+= incrementPerFace / 2.0f;
660  if (facesEntityProcessed == 0 || facesEntityProcessed % 1000 == 0) progressCallback->progress(progress);
661  facesEntityProcessed++;
662  }
663  }
664  facesEntity.setFaces(faces);
665  }
666  node->setFacesEntities(facesEntities);
667  facesEntityProcessed = 0;
668  for (const auto& facesEntity: node->getFacesEntities()) {
669  for (const auto& face: facesEntity.getFaces()) {
670  for (auto i = 0; i < vertices.size(); i++) {
671  if (interpolateNormal(node, node->getVertices()[face.getVertexIndices()[i]], normals, normal) == true) {
672  normals[face.getNormalIndices()[i]].set(normal);
673  }
674  }
675  if (progressCallback != nullptr) {
676  progress+= incrementPerFace / 2.0f;
677  if (facesEntityProcessed == 0 || facesEntityProcessed % 1000 == 0) progressCallback->progress(progress);
678  facesEntityProcessed++;
679  }
680  }
681  }
682  node->setNormals(normals);
683  for (const auto& [subNodeId, subNode]: node->getSubNodes()) {
684  progress = computeNormals(subNode, progressCallback, incrementPerFace, progress);
685  }
686  return progress;
687 }
688 
689 void ModelTools::computeNormals(Model* model, ProgressCallback* progressCallback) {
690  auto progessCallbackPtr = unique_ptr<ProgressCallback>(progressCallback);
691  auto faceCount = 0;
692  for (const auto& [subNodeId, subNode]: model->getSubNodes()) {
693  faceCount+= determineFaceCount(subNode);
694  }
695  for (const auto& [subNodeId, subNode]: model->getSubNodes()) {
696  computeNormals(subNode, progessCallbackPtr.get(), 1.0f / static_cast<float>(faceCount), 0.0f);
697  }
699  if (progessCallbackPtr != nullptr) {
700  progessCallbackPtr->progress(1.0f);
701  }
702 }
703 
705  auto faceCount = 0;
706  faceCount+= node->getFaceCount();
707  for (const auto& [subNodeId, subNode]: node->getSubNodes()) {
708  faceCount+= determineFaceCount(subNode);
709  }
710  return faceCount;
711 }
712 
713 void ModelTools::prepareForShader(Model* model, const string& shader) {
714  if (shader == "foliage" || shader == "pbr-foliage" || shader == "tree" || shader == "pbr-tree") {
715  model->clearAnimationSetups();
716  for (const auto& [subNodeId, subNode]: model->getSubNodes()) prepareForFoliageTreeShader(subNode, model->getImportTransformMatrix(), shader);
717  model->setImportTransformMatrix(Matrix4x4().identity());
718  model->setUpVector(UpVector::Y_UP);
719  createDefaultAnimation(model, 0);
720  }
721 }
722 
723 void ModelTools::prepareForDefaultShader(Node* node, const Matrix4x4& parentTransformaMatrix) {
724  auto transformMatrix = node->getTransformMatrix().clone().multiply(parentTransformaMatrix);
725  // do not continue on non mesh datas
726  if (node->isEmpty() == true || node->isJoint() == true) {
727  node->setTransformMatrix(transformMatrix);
728  //
729  for (const auto& [subNodeId, subNode]: node->getSubNodes()) {
730  prepareForDefaultShader(subNode, transformMatrix);
731  }
732  //
733  return;
734  }
735  // apply transformations matrix to vertices, ...
736  {
737  auto vertices = node->getVertices();
738  auto vertexIdx = 0;
739  for (auto& vertex: vertices) {
740  vertex = transformMatrix.multiply(vertex);
741  }
742  node->setVertices(vertices);
743  }
744  {
745  auto normals = node->getNormals();
746  for (auto& normal: normals) {
747  normal = transformMatrix.multiplyNoTranslation(normal);
748  normal.normalize();
749  }
750  node->setNormals(normals);
751  }
752  {
753  auto tangents = node->getTangents();
754  for (auto& tangent: tangents) {
755  tangent = transformMatrix.multiplyNoTranslation(tangent);
756  tangent.normalize();
757  }
758  node->setTangents(tangents);
759  }
760  {
761  auto bitangents = node->getBitangents();
762  for (auto& bitangent: bitangents) {
763  bitangent = transformMatrix.multiplyNoTranslation(bitangent);
764  bitangent.normalize();
765  }
766  node->setBitangents(bitangents);
767  }
768  node->setTransformMatrix(Matrix4x4().identity());
769  // check if we need to change front face
770  RightHandedMatrix4x4 rightHandedMatrix;
771  if (rightHandedMatrix.isRightHanded(transformMatrix) == true) changeFrontFace(node, false);
772  //
773  for (const auto& [subNodeId, subNode]: node->getSubNodes()) {
774  prepareForDefaultShader(subNode, transformMatrix);
775  }
776 }
777 
778 void ModelTools::prepareForFoliageTreeShader(Node* node, const Matrix4x4& parentTransformMatrix, const string& shader) {
779  //
780  node->setAnimation(nullptr);
781  //
782  auto transformMatrix = node->getTransformMatrix().clone().multiply(parentTransformMatrix);
783  // do not continue on non mesh datas
784  if (node->isEmpty() == true || node->isJoint() == true) {
785  node->setTransformMatrix(transformMatrix);
786  //
787  for (const auto& [subNodeId, subNode]: node->getSubNodes()) {
788  prepareForFoliageTreeShader(subNode, transformMatrix, shader);
789  }
790  //
791  return;
792  }
793  //
794  vector<Vector3> objectOrigins;
795  objectOrigins.resize(node->getVertices().size());
796  {
797  auto vertices = node->getVertices();
798  auto vertexIdx = 0;
799  for (auto& vertex: vertices) {
800  vertex = transformMatrix.multiply(vertex);
801  if (shader == "tree" || shader == "pbr-tree") objectOrigins[vertexIdx].set(0.0f, vertex.getY(), 0.0f);
802  vertexIdx++;
803  }
804  node->setVertices(vertices);
805  }
806  {
807  auto normals = node->getNormals();
808  for (auto& normal: normals) {
809  normal = transformMatrix.multiplyNoTranslation(normal);
810  normal.normalize();
811  }
812  node->setNormals(normals);
813  }
814  {
815  auto tangents = node->getTangents();
816  for (auto& tangent: tangents) {
817  tangent = transformMatrix.multiplyNoTranslation(tangent);
818  tangent.normalize();
819  }
820  node->setTangents(tangents);
821  }
822  {
823  auto bitangents = node->getBitangents();
824  for (auto& bitangent: bitangents) {
825  bitangent = transformMatrix.multiplyNoTranslation(bitangent);
826  bitangent.normalize();
827  }
828  node->setBitangents(bitangents);
829  }
830  node->setTransformMatrix(Matrix4x4().identity());
831  node->setOrigins(objectOrigins);
832  // check if we need to change front face
833  RightHandedMatrix4x4 rightHandedMatrix;
834  if (rightHandedMatrix.isRightHanded(transformMatrix) == true) changeFrontFace(node, false);
835  //
836  for (const auto& [subNodeId, subNode]: node->getSubNodes()) {
837  prepareForFoliageTreeShader(subNode, transformMatrix, shader);
838  }
839 }
840 
841 void ModelTools::checkForOptimization(Node* node, unordered_map<string, int>& materialUseCount, const vector<string>& excludeDiffuseTextureFileNamePatterns) {
842  // skip on joints as they do not have textures to display and no vertex data
843  if (node->isJoint() == true) return;
844 
845  // track material usage
846  for (const auto& facesEntity: node->getFacesEntities()) {
847  if (facesEntity.getMaterial() == nullptr) continue;
848  bool excludeDiffuseTexture = false;
849  for (const auto& excludeDiffuseTextureFileNamePattern: excludeDiffuseTextureFileNamePatterns) {
850  if (StringTools::startsWith(facesEntity.getMaterial()->getSpecularMaterialProperties()->getDiffuseTextureFileName(), excludeDiffuseTextureFileNamePattern) == true) {
851  excludeDiffuseTexture = true;
852  break;
853  }
854  }
855  if (excludeDiffuseTexture == true) {
856  continue;
857  }
858  materialUseCount[facesEntity.getMaterial()->getId()]++;
859  }
860 
861  // do not transform skinning vertices and such
862  if (node->getSkinning() != nullptr) return;
863 
864  //
865  for (const auto& [subNodeId, subNode]: node->getSubNodes()) {
866  checkForOptimization(subNode, materialUseCount, excludeDiffuseTextureFileNamePatterns);
867  }
868 }
869 
870 void ModelTools::prepareForOptimization(Node* node, const Matrix4x4& parentTransformMatrix) {
871  // skip on joints as they do not have textures to display and no vertex data
872  if (node->isJoint() == true) return;
873 
874  // do not transform skinning vertices and such
875  if (node->getSkinning() != nullptr) return;
876 
877  // static node, apply node transform matrix
878  auto transformMatrix = node->getTransformMatrix().clone().multiply(parentTransformMatrix);
879  {
880  auto vertices = node->getVertices();
881  for (auto& vertex: vertices) {
882  vertex = transformMatrix.multiply(vertex);
883  }
884  node->setVertices(vertices);
885  }
886  {
887  auto normals = node->getNormals();
888  for (auto& normal: normals) {
889  normal = transformMatrix.multiplyNoTranslation(normal);
890  normal.normalize();
891  }
892  node->setNormals(normals);
893  }
894  {
895  auto tangents = node->getTangents();
896  for (auto& tangent: tangents) {
897  tangent = transformMatrix.multiplyNoTranslation(tangent);
898  tangent.normalize();
899  }
900  node->setTangents(tangents);
901  }
902  {
903  auto bitangents = node->getBitangents();
904  for (auto& bitangent: bitangents) {
905  bitangent = transformMatrix.multiplyNoTranslation(bitangent);
906  bitangent.normalize();
907  }
908  node->setBitangents(bitangents);
909  }
910 
911  // we do not set node transform matrix as we need it later
912  // node->setTransformMatrix(Matrix4x4().identity());
913 
914  //
915  for (const auto& [subNodeId, subNode]: node->getSubNodes()) {
916  prepareForOptimization(subNode, transformMatrix);
917  }
918 }
919 
920 void ModelTools::optimizeNode(Node* sourceNode, Model* targetModel, int diffuseTextureAtlasSize, const unordered_map<string, int>& diffuseTextureAtlasIndices, const vector<string>& excludeDiffuseTextureFileNamePatterns) {
921  if (sourceNode->getFacesEntities().size() > 0) {
922  unordered_set<int> processedTextureCoordinates;
923  const auto& sourceVertices = sourceNode->getVertices();
924  const auto& sourceNormals = sourceNode->getNormals();
925  const auto& sourceTangents = sourceNode->getTangents();
926  const auto& sourceBitangents = sourceNode->getBitangents();
927  const auto& sourceTextureCoordinates = sourceNode->getTextureCoordinates();
928  const auto& sourceOrigins = sourceNode->getOrigins();
929  auto targetNode = targetModel->getNodes()["tdme.node.optimized"];
930  auto targetVertices = targetNode->getVertices();
931  auto targetNormals = targetNode->getNormals();
932  auto targetTangents = targetNode->getTangents();
933  auto targetBitangents = targetNode->getBitangents();
934  auto targetTextureCoordinates = targetNode->getTextureCoordinates();
935  auto targetOrigins = targetNode->getOrigins();
936  auto targetOffset = targetVertices.size();
937  for (const auto& v: sourceVertices) targetVertices.push_back(v);
938  for (const auto& v: sourceNormals) targetNormals.push_back(v);
939  for (const auto& v: sourceTangents) targetTangents.push_back(v);
940  for (const auto& v: sourceBitangents) targetBitangents.push_back(v);
941  for (const auto& v: sourceTextureCoordinates) targetTextureCoordinates.push_back(v);
942  for (const auto& v: sourceOrigins) targetOrigins.push_back(v);
943  targetNode->setVertices(targetVertices);
944  targetNode->setNormals(targetNormals);
945  targetNode->setTangents(targetTangents);
946  targetNode->setBitangents(targetBitangents);
947  targetNode->setOrigins(targetOrigins);
948  vector<FacesEntity> targetFacesEntitiesKeptMaterials;
949  for (auto& targetFacesEntity: targetNode->getFacesEntities()) {
950  if (StringTools::startsWith(targetFacesEntity.getId(), "tdme.facesentity.kept.") == false) continue;
951  targetFacesEntitiesKeptMaterials.push_back(targetFacesEntity);
952  }
953  FacesEntity* tmpFacesEntity = nullptr;
954  auto targetFaces = (tmpFacesEntity = targetNode->getFacesEntity("tdme.facesentity.optimized")) != nullptr?tmpFacesEntity->getFaces():vector<Face>();
955  auto targetFacesMaskedTransparency = (tmpFacesEntity = targetNode->getFacesEntity("tdme.facesentity.optimized.maskedtransparency")) != nullptr?tmpFacesEntity->getFaces():vector<Face>();
956  auto targetFacesTransparency = (tmpFacesEntity = targetNode->getFacesEntity("tdme.facesentity.optimized.transparency")) != nullptr?tmpFacesEntity->getFaces():vector<Face>();
957  for (const auto& sourceFacesEntity: sourceNode->getFacesEntities()) {
958  auto material = sourceFacesEntity.getMaterial();
959 
960  //
961  string keptMaterialId;
962  for (const auto& excludeDiffuseTextureFileNamePattern: excludeDiffuseTextureFileNamePatterns) {
963  if (StringTools::startsWith(sourceFacesEntity.getMaterial()->getSpecularMaterialProperties()->getDiffuseTextureFileName(), excludeDiffuseTextureFileNamePattern) == true) {
964  keptMaterialId = sourceFacesEntity.getMaterial()->getId();
965  break;
966  }
967  }
968  auto targetFacesKeptMaterial = (tmpFacesEntity = targetNode->getFacesEntity("tdme.facesentity.kept." + keptMaterialId)) != nullptr?tmpFacesEntity->getFaces():vector<Face>();
969 
970  auto diffuseTextureAtlasIndexIt = diffuseTextureAtlasIndices.find(material->getId());
971  auto diffuseTextureAtlasIndex = -1;
972  if (diffuseTextureAtlasIndexIt != diffuseTextureAtlasIndices.end()) {
973  diffuseTextureAtlasIndex = diffuseTextureAtlasIndices.find(material->getId())->second;
974  }
975  auto textureXOffset = 0.0f;
976  auto textureYOffset = 0.0f;
977  auto textureXScale = 1.0f;
978  auto textureYScale = 1.0f;
979  if (diffuseTextureAtlasIndex != -1) {
980  textureXOffset = diffuseTextureAtlasSize == 0?0.0f:static_cast<float>(diffuseTextureAtlasIndex % diffuseTextureAtlasSize) * 1000.0f + 500.0f;
981  textureYOffset = diffuseTextureAtlasSize == 0?0.0f:static_cast<float>(diffuseTextureAtlasIndex / diffuseTextureAtlasSize) * 1000.0f + 500.0f;
982  textureXScale = diffuseTextureAtlasSize == 0?1.0f:1.0f;
983  textureYScale = diffuseTextureAtlasSize == 0?1.0f:1.0f;
984  }
985  for (const auto& face: sourceFacesEntity.getFaces()) {
986  auto sourceVertexIndices = face.getVertexIndices();
987  auto sourceNormalIndices = face.getNormalIndices();
988  auto sourceTangentIndices = face.getTangentIndices();
989  auto sourceBitangentIndices = face.getBitangentIndices();
990  auto sourceTextureCoordinateIndices = face.getTextureCoordinateIndices();
991  // TODO: could happen that in one node are two faces entities with different textures that reference the same texture coordinate, in this case the following breaks the texture coordinates
992  for (auto i = 0; i < sourceTextureCoordinateIndices.size(); i++) {
993  if (sourceTextureCoordinateIndices[i] != -1 &&
994  sourceTextureCoordinates.size() > 0 &&
995  processedTextureCoordinates.find(sourceTextureCoordinateIndices[i]) == processedTextureCoordinates.end()) {
996  auto textureCoordinateArray = sourceTextureCoordinates[sourceTextureCoordinateIndices[i]].getArray();
997  textureCoordinateArray[0]*= textureXScale;
998  textureCoordinateArray[0]+= textureXOffset;
999  textureCoordinateArray[1]*= textureYScale;
1000  textureCoordinateArray[1]+= textureYOffset;
1001  targetTextureCoordinates[sourceTextureCoordinateIndices[i] + targetOffset] = Vector2(textureCoordinateArray);
1002  processedTextureCoordinates.insert(sourceTextureCoordinateIndices[i]);
1003  }
1004  }
1005  if (keptMaterialId.empty() == false) {
1006  targetFacesKeptMaterial.push_back(
1007  Face(
1008  targetNode,
1009  sourceVertexIndices[0] + targetOffset,
1010  sourceVertexIndices[1] + targetOffset,
1011  sourceVertexIndices[2] + targetOffset,
1012  sourceNormalIndices[0] + targetOffset,
1013  sourceNormalIndices[1] + targetOffset,
1014  sourceNormalIndices[2] + targetOffset,
1015  sourceTextureCoordinateIndices[0] + targetOffset,
1016  sourceTextureCoordinateIndices[1] + targetOffset,
1017  sourceTextureCoordinateIndices[2] + targetOffset
1018  )
1019  );
1020  } else
1021  if (material->getSpecularMaterialProperties()->hasDiffuseTextureTransparency() == true) {
1022  if (material->getSpecularMaterialProperties()->hasDiffuseTextureMaskedTransparency() == true) {
1023  targetFacesMaskedTransparency.push_back(
1024  Face(
1025  targetNode,
1026  sourceVertexIndices[0] + targetOffset,
1027  sourceVertexIndices[1] + targetOffset,
1028  sourceVertexIndices[2] + targetOffset,
1029  sourceNormalIndices[0] + targetOffset,
1030  sourceNormalIndices[1] + targetOffset,
1031  sourceNormalIndices[2] + targetOffset,
1032  sourceTextureCoordinateIndices[0] + targetOffset,
1033  sourceTextureCoordinateIndices[1] + targetOffset,
1034  sourceTextureCoordinateIndices[2] + targetOffset
1035  )
1036  );
1037  } else {
1038  targetFacesTransparency.push_back(
1039  Face(
1040  targetNode,
1041  sourceVertexIndices[0] + targetOffset,
1042  sourceVertexIndices[1] + targetOffset,
1043  sourceVertexIndices[2] + targetOffset,
1044  sourceNormalIndices[0] + targetOffset,
1045  sourceNormalIndices[1] + targetOffset,
1046  sourceNormalIndices[2] + targetOffset,
1047  sourceTextureCoordinateIndices[0] + targetOffset,
1048  sourceTextureCoordinateIndices[1] + targetOffset,
1049  sourceTextureCoordinateIndices[2] + targetOffset
1050  )
1051  );
1052  }
1053  } else {
1054  targetFaces.push_back(
1055  Face(
1056  targetNode,
1057  sourceVertexIndices[0] + targetOffset,
1058  sourceVertexIndices[1] + targetOffset,
1059  sourceVertexIndices[2] + targetOffset,
1060  sourceNormalIndices[0] + targetOffset,
1061  sourceNormalIndices[1] + targetOffset,
1062  sourceNormalIndices[2] + targetOffset,
1063  sourceTextureCoordinateIndices[0] + targetOffset,
1064  sourceTextureCoordinateIndices[1] + targetOffset,
1065  sourceTextureCoordinateIndices[2] + targetOffset
1066  )
1067  );
1068  }
1069  }
1070  if (targetFacesKeptMaterial.size() > 0) {
1071  FacesEntity* facesEntity = nullptr;
1072  for (auto& targetFacesEntityKeptMaterial: targetFacesEntitiesKeptMaterials) {
1073  if (targetFacesEntityKeptMaterial.getId() == "tdme.facesentity.kept." + keptMaterialId) {
1074  facesEntity = &targetFacesEntityKeptMaterial;
1075  break;
1076  }
1077  }
1078  if (facesEntity == nullptr) {
1079  targetFacesEntitiesKeptMaterials.push_back(FacesEntity(targetNode, "tdme.facesentity.kept." + keptMaterialId));
1080  facesEntity = &targetFacesEntitiesKeptMaterials[targetFacesEntitiesKeptMaterials.size() - 1];
1081  }
1082  facesEntity->setFaces(targetFacesKeptMaterial);
1083  facesEntity->setMaterial(targetModel->getMaterials()[keptMaterialId]);
1084  }
1085  }
1086  targetNode->setTextureCoordinates(targetTextureCoordinates);
1087  vector<FacesEntity> targetFacesEntities;
1088  if (targetFaces.size() > 0) {
1089  targetFacesEntities.push_back(FacesEntity(targetNode, "tdme.facesentity.optimized"));
1090  targetFacesEntities[targetFacesEntities.size() - 1].setFaces(targetFaces);
1091  }
1092  if (targetFacesMaskedTransparency.size() > 0) {
1093  targetFacesEntities.push_back(FacesEntity(targetNode, "tdme.facesentity.optimized.maskedtransparency"));
1094  targetFacesEntities[targetFacesEntities.size() - 1].setFaces(targetFacesMaskedTransparency);
1095  }
1096  if (targetFacesTransparency.size() > 0) {
1097  targetFacesEntities.push_back(FacesEntity(targetNode, "tdme.facesentity.optimized.transparency"));
1098  targetFacesEntities[targetFacesEntities.size() - 1].setFaces(targetFacesTransparency);
1099  }
1100  for (const auto& targetFacesEntityKeptMaterial: targetFacesEntitiesKeptMaterials) {
1101  targetFacesEntities.push_back(targetFacesEntityKeptMaterial);
1102  }
1103  targetNode->setFacesEntities(targetFacesEntities);
1104  }
1105  for (const auto& [subNodeId, subNode]: sourceNode->getSubNodes()) {
1106  optimizeNode(subNode, targetModel, diffuseTextureAtlasSize, diffuseTextureAtlasIndices, excludeDiffuseTextureFileNamePatterns);
1107  }
1108 }
1109 
1111  return model->getNodes()["tdme.node.optimized"] != nullptr;
1112 }
1113 
1114 Model* ModelTools::optimizeModel(Model* model, const string& texturePathName, const vector<string>& excludeDiffuseTextureFileNamePatterns) {
1115  auto modelPtr = unique_ptr<Model>(model);
1116  // exit early if modelPtr has been optimized already
1117  if (isOptimizedModel(modelPtr.get()) == true) return modelPtr.release();
1118 
1119  // TODO: 2 mats could have the same texture
1120  // prepare for optimizations
1121  unordered_map<string, int> materialUseCount;
1122  for (const auto& [subNodeId, subNode]: modelPtr->getSubNodes()) {
1124  subNode,
1125  materialUseCount,
1126  excludeDiffuseTextureFileNamePatterns
1127  );
1128  }
1129 
1130  // create diffuse atlas texture
1131  SimpleTextureAtlas diffuseAtlas(modelPtr->getName() + ".diffuse.atlas");
1132 
1133  // check materials and diffuse textures
1134  auto diffuseTextureCount = 0;
1135  unordered_map<string, int> diffuseTextureAtlasIndices;
1136  unordered_map<string, Material*> atlasMaterials;
1137  for (const auto& [materialName, materialCount]: materialUseCount) {
1138  auto material = modelPtr->getMaterials().find(materialName)->second;
1139  auto diffuseTexture = material->getSpecularMaterialProperties()->getDiffuseTexture();
1140  if (diffuseTexture != nullptr) {
1141  diffuseTextureAtlasIndices[material->getId()] = diffuseAtlas.addTexture(diffuseTexture);
1142  diffuseTextureCount++;
1143  atlasMaterials[material->getId()] = material;
1144  }
1145  }
1146 
1147  // do we need to optimize?
1148  if (diffuseTextureCount < 2) return modelPtr.release();
1149 
1150  // prepare for optimizations
1151  for (const auto& [subNodeId, subNode]: modelPtr->getSubNodes()) {
1153  subNode,
1154  Matrix4x4().identity()
1155  );
1156  }
1157 
1158  // update diffuse atlas texture
1159  diffuseAtlas.update();
1160 
1161  // create modelPtr with optimizations applied
1162  auto optimizedModelPtr = make_unique<Model>(modelPtr->getId() + ".optimized", modelPtr->getName() + ".optimized", modelPtr->getUpVector(), modelPtr->getRotationOrder(), make_unique<BoundingBox>(modelPtr->getBoundingBox()).release(), modelPtr->getAuthoringTool());
1163  optimizedModelPtr->setImportTransformMatrix(modelPtr->getImportTransformMatrix());
1164  optimizedModelPtr->setEmbedSpecularTextures(true);
1165  optimizedModelPtr->setEmbedPBRTextures(true);
1166  auto optimizedNode = make_unique<Node>(optimizedModelPtr.get(), nullptr, "tdme.node.optimized", "tdme.node.optimized");
1167  optimizedModelPtr->getNodes()["tdme.node.optimized"] = optimizedNode.get();
1168  optimizedModelPtr->getSubNodes()["tdme.node.optimized"] = optimizedNode.get();
1169  optimizedNode.release();
1170 
1171  // clone materials with diffuse textures that we like to keep
1172  for (const auto& [materialId, material]: modelPtr->getMaterials()) {
1173  bool keepDiffuseTexture = false;
1174  for (const auto& excludeDiffuseTextureFileNamePattern: excludeDiffuseTextureFileNamePatterns) {
1175  if (StringTools::startsWith(material->getSpecularMaterialProperties()->getDiffuseTextureFileName(), excludeDiffuseTextureFileNamePattern) == true) {
1176  keepDiffuseTexture = true;
1177  break;
1178  }
1179  }
1180  if (keepDiffuseTexture == false) continue;
1181  optimizedModelPtr->getMaterials()[material->getId()] = cloneMaterial(material);
1182  }
1183 
1184  // create optimized material
1185  auto optimizedMaterial = make_unique<Material>("tdme.material.optimized");
1186  {
1187  diffuseAtlas.getAtlasTexture()->acquireReference();
1188  optimizedMaterial->getSpecularMaterialProperties()->setDiffuseTexture(diffuseAtlas.getAtlasTexture());
1189  optimizedMaterial->getSpecularMaterialProperties()->setTextureAtlasSize(diffuseAtlas.getAtlasTexture()->getAtlasSize());
1190  optimizedMaterial->getSpecularMaterialProperties()->setDiffuseTextureTransparency(false);
1191  optimizedMaterial->getSpecularMaterialProperties()->setDiffuseTextureMaskedTransparency(false);
1192  Vector4 optimizedMaterialEmission(0.0f, 0.0f, 0.0f, 0.0f);
1193  Vector4 optimizedMaterialAmbient(0.0f, 0.0f, 0.0f, 0.0f);
1194  Vector4 optimizedMaterialDiffuse(0.0f, 0.0f, 0.0f, 0.0f);
1195  Vector4 optimizedMaterialSpecular(0.0f, 0.0f, 0.0f, 0.0f);
1196  float optimizedMaterialShininess = 0.0f;
1197  for (const auto& [atlasMaterialsId, material]: atlasMaterials) {
1198  optimizedMaterialEmission+= Vector4(material->getSpecularMaterialProperties()->getEmissionColor().getArray());
1199  optimizedMaterialAmbient+= Vector4(material->getSpecularMaterialProperties()->getAmbientColor().getArray());
1200  optimizedMaterialDiffuse+= Vector4(material->getSpecularMaterialProperties()->getDiffuseColor().getArray());
1201  optimizedMaterialSpecular+= Vector4(material->getSpecularMaterialProperties()->getSpecularColor().getArray());
1202  optimizedMaterialShininess+= material->getSpecularMaterialProperties()->getShininess();
1203  }
1204  optimizedMaterialEmission/= static_cast<float>(atlasMaterials.size());
1205  optimizedMaterialAmbient/= static_cast<float>(atlasMaterials.size());
1206  optimizedMaterialDiffuse/= static_cast<float>(atlasMaterials.size());
1207  optimizedMaterialSpecular/= static_cast<float>(atlasMaterials.size());
1208  optimizedMaterialShininess/= static_cast<float>(atlasMaterials.size());
1209  optimizedMaterial->getSpecularMaterialProperties()->setEmissionColor(Color4(optimizedMaterialEmission.getArray()));
1210  optimizedMaterial->getSpecularMaterialProperties()->setAmbientColor(Color4(optimizedMaterialAmbient.getArray()));
1211  optimizedMaterial->getSpecularMaterialProperties()->setDiffuseColor(Color4(optimizedMaterialDiffuse.getArray()));
1212  optimizedMaterial->getSpecularMaterialProperties()->setSpecularColor(Color4(optimizedMaterialSpecular.getArray()));
1213  optimizedMaterial->getSpecularMaterialProperties()->setShininess(optimizedMaterialShininess);
1214  }
1215 
1216  // also have a material with masked transparency
1217  auto optimizedMaterialMaskedTransparency = unique_ptr<Material>(cloneMaterial(optimizedMaterial.get(), "tdme.material.optimized.maskedtransparency"));
1218  optimizedMaterialMaskedTransparency->getSpecularMaterialProperties()->setDiffuseTextureTransparency(true);
1219  optimizedMaterialMaskedTransparency->getSpecularMaterialProperties()->setDiffuseTextureMaskedTransparency(true);
1220 
1221  // also have a material with transparency
1222  auto optimizedMaterialTransparency = unique_ptr<Material>(cloneMaterial(optimizedMaterial.get(), "tdme.material.optimized.transparency"));
1223  optimizedMaterialTransparency->getSpecularMaterialProperties()->setDiffuseTextureTransparency(true);
1224 
1225  // now optimize into our optimized modelPtr
1226  for (const auto& [subNodeId, subNode]: modelPtr->getSubNodes()) {
1227  if ((modelPtr->hasSkinning() == true && subNode->getSkinning() != nullptr) ||
1228  (modelPtr->hasSkinning() == false && subNode->isJoint() == false)) {
1229  optimizeNode(subNode, optimizedModelPtr.get(), diffuseAtlas.getAtlasTexture()->getAtlasSize(), diffuseTextureAtlasIndices, excludeDiffuseTextureFileNamePatterns);
1230  if (modelPtr->hasSkinning() == true) {
1231  auto skinning = subNode->getSkinning();
1232  auto optimizedSkinning = make_unique<Skinning>();
1233  optimizedSkinning->setWeights(skinning->getWeights());
1234  optimizedSkinning->setJoints(skinning->getJoints());
1235  optimizedSkinning->setVerticesJointsWeights(skinning->getVerticesJointsWeights());
1236  optimizedModelPtr->getNodes()["tdme.node.optimized"]->setSkinning(optimizedSkinning.release());
1237  }
1238  }
1239  cloneNode(subNode, optimizedModelPtr.get(), nullptr, false);
1240  }
1241 
1242  // set up materials
1243  {
1244  auto optimizedFacesEntity = optimizedModelPtr->getNodes()["tdme.node.optimized"]->getFacesEntity("tdme.facesentity.optimized");
1245  if (optimizedFacesEntity != nullptr) {
1246  optimizedModelPtr->getMaterials()[optimizedMaterial->getId()] = optimizedMaterial.get();
1247  optimizedFacesEntity->setMaterial(optimizedMaterial.release());
1248  }
1249  }
1250  {
1251  auto optimizedFacesEntityMaskedTransparency = optimizedModelPtr->getNodes()["tdme.node.optimized"]->getFacesEntity("tdme.facesentity.optimized.maskedtransparency");
1252  if (optimizedFacesEntityMaskedTransparency != nullptr) {
1253  optimizedModelPtr->getMaterials()[optimizedMaterialMaskedTransparency->getId()] = optimizedMaterialMaskedTransparency.get();
1254  optimizedFacesEntityMaskedTransparency->setMaterial(optimizedMaterialMaskedTransparency.release());
1255  }
1256  }
1257  {
1258  auto optimizedFacesEntityTransparency = optimizedModelPtr->getNodes()["tdme.node.optimized"]->getFacesEntity("tdme.facesentity.optimized.transparency");
1259  if (optimizedFacesEntityTransparency != nullptr) {
1260  optimizedModelPtr->getMaterials()[optimizedMaterialTransparency->getId()] = optimizedMaterialTransparency.get();
1261  optimizedFacesEntityTransparency->setMaterial(optimizedMaterialTransparency.release());
1262  }
1263  }
1264 
1265  // copy animation set up
1266  for (const auto& [animationSetupId, animationSetup]: modelPtr->getAnimationSetups()) {
1267  if (animationSetup->getOverlayFromNodeId().empty() == false) {
1268  optimizedModelPtr->addOverlayAnimationSetup(
1269  animationSetup->getId(),
1270  animationSetup->getOverlayFromNodeId(),
1271  animationSetup->getStartFrame(),
1272  animationSetup->getEndFrame(),
1273  animationSetup->isLoop(),
1274  animationSetup->getSpeed()
1275  );
1276  } else {
1277  optimizedModelPtr->addAnimationSetup(
1278  animationSetup->getId(),
1279  animationSetup->getStartFrame(),
1280  animationSetup->getEndFrame(),
1281  animationSetup->isLoop(),
1282  animationSetup->getSpeed()
1283  );
1284  }
1285  }
1286 
1287  // done
1288  return optimizedModelPtr.release();
1289 }
1290 
1292 {
1293  // without texture coordinates we cant compute tangents and bitangents
1294  if (node->getTextureCoordinates().empty() == true) {
1295  Console::println("ModelTools::computeTangentsAndBitangents(): " + node->getId() + ": No texture coordinates");
1296  return;
1297  }
1298  // see: https://www.opengl-tutorial.org/intermediate-tutorials/tutorial-13-normal-mapping/
1299  // what we need
1300  vector<Vector3> tangents;
1301  vector<Vector3> bitangents;
1302  // temporary variables
1303  Vector2 uv0;
1304  Vector2 uv1;
1305  Vector2 uv2;
1306  Vector3 deltaPos1;
1307  Vector3 deltaPos2;
1308  Vector2 deltaUV1;
1309  Vector2 deltaUV2;
1310  // create it
1311  auto facesEntities = node->getFacesEntities();
1312  const auto& vertices = node->getVertices();
1313  const auto& normals = node->getNormals();
1314  auto textureCoordinates = node->getTextureCoordinates();
1315  for (auto& faceEntity: facesEntities) {
1316  auto faces = faceEntity.getFaces();
1317  for (auto& face: faces) {
1318  // shortcuts for vertices
1319  auto verticesIndexes = face.getVertexIndices();
1320  auto v0 = vertices[verticesIndexes[0]];
1321  auto v1 = vertices[verticesIndexes[1]];
1322  auto v2 = vertices[verticesIndexes[2]];
1323  // shortcuts for UVs
1324  auto textureCoordinatesIndexes = face.getTextureCoordinateIndices();
1325  uv0.set(textureCoordinates[textureCoordinatesIndexes[0]].getArray());
1326  uv0.setY(1.0f - uv0.getY());
1327  uv1.set(textureCoordinates[textureCoordinatesIndexes[1]].getArray());
1328  uv1.setY(1.0f - uv1.getY());
1329  uv2.set(textureCoordinates[textureCoordinatesIndexes[2]].getArray());
1330  uv2.setY(1.0f - uv2.getY());
1331  // edges of the triangle : position delta
1332  deltaPos1.set(v1).sub(v0);
1333  deltaPos2.set(v2).sub(v0);
1334  // UV delta
1335  deltaUV1.set(uv1).sub(uv0);
1336  deltaUV2.set(uv2).sub(uv0);
1337  // compute tangent and bitangent
1338  auto r = 1.0f / (deltaUV1.getX() * deltaUV2.getY() - deltaUV1.getY() * deltaUV2.getX());
1339  auto tangent = deltaPos1.clone().scale(deltaUV2.getY()).sub(deltaPos2.clone().scale(deltaUV1.getY())).scale(r);
1340  auto bitangent = deltaPos2.clone().scale(deltaUV1.getX()).sub(deltaPos1.clone().scale(deltaUV2.getX())).scale(r);
1341  // set up tangent face indices
1342  face.setTangentIndices(tangents.size() + 0, tangents.size() + 1, tangents.size() + 2);
1343  // set up bitangent face indices
1344  face.setBitangentIndices(bitangents.size() + 0, bitangents.size() + 1, bitangents.size() + 2);
1345  // add to group tangents, bitangents
1346  tangents.push_back(tangent);
1347  tangents.push_back(tangent);
1348  tangents.push_back(tangent);
1349  bitangents.push_back(bitangent);
1350  bitangents.push_back(bitangent);
1351  bitangents.push_back(bitangent);
1352  }
1353  faceEntity.setFaces(faces);
1354  }
1355  node->setFacesEntities(facesEntities);
1356 
1357  // set up tangents and bitangents if we have any
1358  if (tangents.size() > 0 && bitangents.size() > 0) {
1359  // going further
1360  auto facesEntities = node->getFacesEntities();
1361  for (auto& faceEntity: facesEntities) {
1362  auto faces = faceEntity.getFaces();
1363  for (auto& face: faces) {
1364  for (auto i = 0; i < 3; i++) {
1365  auto normal = normals[face.getNormalIndices()[i]];
1366  auto& tangent = tangents[face.getTangentIndices()[i]];
1367  auto& bitangent = bitangents[face.getBitangentIndices()[i]];
1368  tangent.sub(normal.clone().scale(Vector3::computeDotProduct(normal, tangent))).normalize();
1369  if (Vector3::computeDotProduct(Vector3::computeCrossProduct(normal, tangent), bitangent) < 0.0f) {
1370  tangent.scale(-1.0f);
1371  }
1372  bitangent.normalize();
1373  }
1374  }
1375  faceEntity.setFaces(faces);
1376  }
1377  node->setFacesEntities(facesEntities);
1378  //
1379  node->setTangents(tangents);
1380  node->setBitangents(bitangents);
1381  }
1382 }
1383 
1384 void ModelTools::changeFrontFace(Node* node, bool applyToSubNodes) {
1385  auto facesEntities = node->getFacesEntities();
1386  for (auto& facesEntity: facesEntities) {
1387  auto faces = facesEntity.getFaces();
1388  for (auto& face: faces) face.changeFrontFace();
1389  facesEntity.setFaces(faces);
1390  }
1391  node->setFacesEntities(facesEntities);
1392  if (applyToSubNodes == true) {
1393  for (const auto& [subNodeId, subNode]: node->getSubNodes()) {
1394  changeFrontFace(subNode, true);
1395  }
1396  }
1397 }
1398 
1400  for (const auto& [subNodeId, subNode]: model->getSubNodes()) {
1401  changeFrontFace(subNode, true);
1402  }
1403 }
Color 4 definition class.
Definition: Color4.h:18
Texture entity.
Definition: Texture.h:24
uint16_t getAtlasSize() const
Definition: Texture.h:369
Transform which contain scale, rotations and translation.
Definition: Transform.h:29
const Matrix4x4 & getTransformMatrix() const
Definition: Transform.h:169
const vector< Matrix4x4 > & getTransformMatrices() const
Returns transform matrices.
Definition: Animation.h:43
Represents a model face, consisting of vertex, normal, tangent and bitangent vectors,...
Definition: Face.h:18
void setTextureCoordinateIndices(int32_t vt0, int32_t vt1, int32_t vt2)
Set up optional texture coordinate indices.
Definition: Face.cpp:45
void setTangentIndices(int32_t ti0, int32_t ti1, int32_t ti2)
Set tangent indices.
Definition: Face.cpp:52
void setBitangentIndices(int32_t bi0, int32_t bi1, int32_t bi2)
Set bitangent indices.
Definition: Face.cpp:59
Node faces entity A node can have multiple entities containing faces and a applied material.
Definition: FacesEntity.h:23
const vector< Face > & getFaces() const
Definition: FacesEntity.h:81
void setMaterial(Material *material)
Set up the entity's material.
Definition: FacesEntity.h:67
void setFaces(const vector< Face > &faces)
Set up entity's faces.
Definition: FacesEntity.cpp:43
Joint / Bone.
Definition: Joint.h:19
Represents a material.
Definition: Material.h:23
const SpecularMaterialProperties * getSpecularMaterialProperties() const
Definition: Material.h:64
const string & getId() const
Definition: Material.h:57
Representation of a 3D model.
Definition: Model.h:35
unordered_map< string, Node * > & getSubNodes()
Returns object's sub nodes.
Definition: Model.h:220
unordered_map< string, Material * > & getMaterials()
Returns all object materials.
Definition: Model.h:185
void setImportTransformMatrix(const Matrix4x4 &importTransformMatrix)
Set import transform matrix.
Definition: Model.h:348
void clearAnimationSetups()
Clear animation setups.
Definition: Model.cpp:143
RotationOrder * getRotationOrder()
Definition: Model.h:157
AnimationSetup * getAnimationSetup(const string &id)
Definition: Model.h:274
const Matrix4x4 & getImportTransformMatrix()
Definition: Model.h:340
void setUpVector(UpVector *upVector)
Set up vector.
Definition: Model.h:150
unordered_map< string, Node * > & getNodes()
Returns all object's nodes.
Definition: Model.h:199
const string & getName()
Definition: Model.h:135
AnimationSetup * addAnimationSetup(const string &id, int32_t startFrame, int32_t endFrame, bool loop, float speed=1.0f)
Adds an base animation setup.
Definition: Model.cpp:101
const string & getId()
Definition: Model.h:128
UpVector * getUpVector()
Definition: Model.h:142
Model node.
Definition: Node.h:32
const vector< Vector3 > & getBitangents() const
Definition: Node.h:216
int32_t getFaceCount() const
Definition: Node.cpp:105
unordered_map< string, Node * > & getSubNodes()
Definition: Node.h:293
void setAnimation(Animation *animation)
Sets animation object.
Definition: Node.cpp:95
void setOrigins(const vector< Vector3 > &origins)
Set origins.
Definition: Node.cpp:130
void setVertices(const vector< Vector3 > &vertices)
Set vertices.
Definition: Node.cpp:48
const vector< Vector3 > & getTangents() const
Definition: Node.h:203
void setFacesEntities(const vector< FacesEntity > &facesEntities)
Set up faces entities.
Definition: Node.cpp:121
bool isJoint() const
Definition: Node.h:110
Model * getModel()
Definition: Node.h:74
const vector< Vector3 > & getOrigins() const
Definition: Node.h:280
void setTransformMatrix(const Matrix4x4 &transformMatrix)
Definition: Node.h:132
const string & getName()
Definition: Node.h:96
Animation * getAnimation()
Definition: Node.h:229
void setJoint(bool isJoint)
Sets up if this node is a joint or not.
Definition: Node.h:118
const vector< Vector3 > & getVertices() const
Definition: Node.h:155
const vector< Vector3 > & getNormals() const
Definition: Node.h:177
const vector< Vector2 > & getTextureCoordinates() const
Definition: Node.h:190
bool isEmpty() const
Definition: Node.h:103
Skinning * getSkinning()
Definition: Node.h:242
void setNormals(const vector< Vector3 > &normals)
Set normals.
Definition: Node.cpp:58
const string & getId()
Returns id.
Definition: Node.h:89
void setBitangents(const vector< Vector3 > &bitangents)
Set bitangents.
Definition: Node.cpp:86
Node * getParentNode()
Definition: Node.h:81
const vector< FacesEntity > & getFacesEntities() const
Definition: Node.h:260
void setTangents(const vector< Vector3 > &tangents)
Set tangents.
Definition: Node.cpp:77
const Matrix4x4 & getTransformMatrix() const
Definition: Node.h:125
Skinning definition for nodes.
Definition: Skinning.h:25
const vector< vector< JointWeight > > & getVerticesJointsWeights()
Definition: Skinning.h:70
void setVerticesJointsWeights(const vector< vector< JointWeight >> &verticesJointsWeights)
Sets up vertices joints weights.
Definition: Skinning.cpp:42
Represents specular material properties.
Model up vector.
Definition: UpVector.h:20
Axis aligned bounding box used for frustum, this is not directly connectable with physics engine.
Definition: BoundingBox.h:26
Scene entity definition.
Definition: SceneEntity.h:23
Matrix4x4 class representing matrix4x4 mathematical structure and operations for 3d space.
Definition: Matrix4x4.h:23
Matrix4x4 clone() const
Clones this matrix.
Definition: Matrix4x4.h:619
Vector3 multiply(const Vector3 &vector3) const
Multiplies this matrix with vector3.
Definition: Matrix4x4.h:225
Matrix4x4 & set(float r0c0, float r0c1, float r0c2, float r0c3, float r1c0, float r1c1, float r1c2, float r1c3, float r2c0, float r2c1, float r2c2, float r2c3, float r3c0, float r3c1, float r3c2, float r3c3)
Sets this matrix by its components.
Definition: Matrix4x4.h:108
Vector2 class representing vector2 mathematical structure and operations with x, y components.
Definition: Vector2.h:20
float getY() const
Definition: Vector2.h:111
Vector2 & sub(float scalar)
Subtracts a scalar.
Definition: Vector2.h:152
float getX() const
Definition: Vector2.h:94
Vector2 & setY(float y)
Sets y component.
Definition: Vector2.h:120
Vector2 & set(float x, float y)
Sets this vector2 by its components.
Definition: Vector2.h:65
Vector3 class representing vector3 mathematical structure and operations with x, y,...
Definition: Vector3.h:20
float getY() const
Definition: Vector3.h:117
float getX() const
Definition: Vector3.h:100
float getZ() const
Definition: Vector3.h:134
Vector3 & add(float scalar)
Adds a scalar.
Definition: Vector3.h:153
Vector3 clone() const
Clones this vector3.
Definition: Vector3.h:374
Vector3 & sub(float scalar)
Subtracts a scalar.
Definition: Vector3.h:177
Vector3 & scale(float scalar)
Scales by scalar.
Definition: Vector3.h:201
Vector3 & set(float x, float y, float z)
Sets this vector3 by its components.
Definition: Vector3.h:70
Vector4 class representing vector4 mathematical structure and operations with x, y,...
Definition: Vector4.h:22
const array< float, 4 > & getArray() const
Definition: Vector4.h:357
Byte buffer class.
Definition: ByteBuffer.h:27
Console class.
Definition: Console.h:29
static void println()
Print new line to console.
Definition: Console.cpp:92
Simple class to determine if a transform matrix is right handed.
Definition: ModelTools.h:49
bool isRightHanded(Matrix4x4 &matrix)
Check if matrix is negative.
Definition: ModelTools.h:67
Model tools functions class.
Definition: ModelTools.h:42
static bool interpolateNormal(Node *node, const Vector3 &vertex, const vector< Vector3 > &normals, Vector3 &normal)
Find all faces that include vertex and compute the avarage normal.
Definition: ModelTools.h:241
static void shrinkToFit(Node *node)
Shrink to fit node.
Definition: ModelTools.cpp:613
static void computeTangentsAndBitangents(Node *node)
Compute tangents and bitangents for given node.
static void checkForOptimization(Node *node, unordered_map< string, int > &materialUseCount, const vector< string > &excludeDiffuseTextureFileNamePatterns)
Check for optimization.
Definition: ModelTools.cpp:841
static void createDefaultAnimation(Model *model, int32_t frames)
Create default animation.
Definition: ModelTools.cpp:259
static bool hasDefaultAnimation(Model *model)
Check default animation.
Definition: ModelTools.cpp:255
static array< Vector3, 3 > computeNormals(const array< Vector3, 3 > &vertices)
Computes face normals for given face vertices these normals will not be smooth.
Definition: ModelTools.h:115
static void prepareForFoliageTreeShader(Node *node, const Matrix4x4 &parentTransformMatrix, const string &shader)
Prepare node for foliage shader.
Definition: ModelTools.cpp:778
static void prepareForIndexedRendering(Model *model)
Prepare for indexed rendering.
Definition: ModelTools.cpp:86
static void partition(Model *model, const Transform &transform, unordered_map< string, Model * > &modelsByPartition, unordered_map< string, Vector3 > &modelsPosition)
Partition model.
Definition: ModelTools.cpp:597
static void prepareForOptimization(Node *node, const Matrix4x4 &parentTransformMatrix)
Prepare for optimization.
Definition: ModelTools.cpp:870
static void setupJoints(Model *model)
Set up joints for skinning nodes.
Definition: ModelTools.cpp:194
static void prepareForShader(Model *model, const string &shader=string())
Prepare model for specific shader.
Definition: ModelTools.cpp:713
static void changeFrontFace(Node *node, bool applyToSubNodes)
Change front face from counter clock wise to clock wise or clock wise to counter clock wise.
static Model * optimizeModel(Model *model, const string &texturePathName=string(), const vector< string > &excludeDiffuseTextureFileNamePatterns=vector< string >())
Optimizes model in terms of material / node reduction.
static void partitionNode(Node *sourceNode, unordered_map< string, Model * > &modelsByPartition, unordered_map< string, Vector3 > &modelsPosition, const Matrix4x4 &parentTransformMatrix)
Partition sub nodes.
Definition: ModelTools.cpp:364
static int determineFaceCount(Node *node)
Compute face count.
Definition: ModelTools.cpp:704
static void optimizeNode(Node *sourceNode, Model *targetModel, int diffuseTextureAtlasSize, const unordered_map< string, int > &diffuseTextureAtlasIndices, const vector< string > &excludeDiffuseTextureFileNamePatterns)
Prepare for optimization.
Definition: ModelTools.cpp:920
static Material * cloneMaterial(const Material *material, const string &id=string())
Clone material.
Definition: ModelTools.cpp:277
static bool isOptimizedModel(Model *model)
static void fixAnimationLength(Model *model)
Fix animation length.
Definition: ModelTools.cpp:221
static void setJoint(Node *root)
Sets up a node as joint taking all subnodes into account.
Definition: ModelTools.cpp:213
static void prepareForDefaultShader(Node *node, const Matrix4x4 &parentTransformMatrix)
Prepare node for default shader.
Definition: ModelTools.cpp:723
static void cloneNode(Node *sourceNode, Model *targetModel, Node *targetParentNode=nullptr, bool cloneMesh=true)
Create model from source sub nodes into target sub nodes.
Definition: ModelTools.cpp:317
static Vector3 computeNormal(const array< Vector3, 3 > &vertices)
Computes face normal for given face vertices.
Definition: ModelTools.h:92
virtual void acquireReference()
Acquires a reference, incrementing the counter.
Definition: Reference.h:31
int addTexture(Texture *texture)
Add texture.
void update()
Update texture atlas.
String tools class.
Definition: StringTools.h:22
static int32_t lastIndexOf(const string &src, char what, int beginIndex=-1)
Finds last index of given character.
Definition: StringTools.h:139
static const bool startsWith(const string &src, const string &prefix)
Checks if string starts with prefix.
Definition: StringTools.h:30
static const string substring(const string &src, int32_t beginIndex)
Returns substring of given string from begin index.
Definition: StringTools.h:160
virtual void progress(float value)=0
Perform action.