TDME2  1.9.200
ModelUtilitiesInternal.cpp
Go to the documentation of this file.
2 
3 #include <string>
4 #include <unordered_map>
5 
6 #include <tdme/tdme.h>
12 #include <tdme/engine/model/Node.h>
20 #include <tdme/engine/Timing.h>
21 #include <tdme/math/Matrix4x4.h>
22 #include <tdme/math/Vector3.h>
23 
24 using std::string;
25 using std::unordered_map;
26 
44 
45 BoundingBox* ModelUtilitiesInternal::createBoundingBox(Model* model, const unordered_map<string, Matrix4x4*> overriddenNodeTransformMatrices)
46 {
47  ObjectModelInternal objectModel(model);
48  objectModel.instanceAnimations[0]->overriddenTransformMatrices = overriddenNodeTransformMatrices;
49  auto boundingBox = ModelUtilitiesInternal::createBoundingBox(&objectModel);
50  if (boundingBox == nullptr) boundingBox = ModelUtilitiesInternal::createBoundingBoxNoMesh(&objectModel);
51  return boundingBox;
52 }
53 
55 {
56  auto model = objectModelInternal->getModel();
57  auto defaultAnimation = model->getAnimationSetup(Model::ANIMATIONSETUP_DEFAULT);
58  float minX = 0.0f, minY = 0.0f, minZ = 0.0f;
59  float maxX = 0.0f, maxY = 0.0f, maxZ = 0.0f;
60  auto firstVertex = true;
61  // create bounding box for whole animation at 60fps
62  AnimationState animationState;
63  animationState.setup = defaultAnimation;
64  animationState.lastAtTime = Timing::UNDEFINED;
65  animationState.currentAtTime = 0LL;
66  animationState.time = 0.0f;
67  animationState.finished = false;
68  for (auto t = 0.0f; t <= (defaultAnimation != nullptr ? static_cast<float>(defaultAnimation->getFrames()) : 0.0f) / model->getFPS(); t += 1.0f / model->getFPS()) {
69  // calculate transform matrices without world transform
70  auto parentTransformMatrix = objectModelInternal->getModel()->getImportTransformMatrix();
71  parentTransformMatrix.multiply(objectModelInternal->getTransformMatrix());
72  objectModelInternal->instanceAnimations[0]->computeNodesTransformMatrices(objectModelInternal->instanceAnimations[0]->nodeLists[0], parentTransformMatrix, &animationState);
73  ObjectNode::computeAnimation(0, objectModelInternal->objectNodes);
74  // parse through object nodes to determine min, max
75  for (auto objectNode : objectModelInternal->objectNodes) {
76  for (const auto& vertex : *objectNode->mesh->vertices) {
77  if (firstVertex == true) {
78  minX = vertex[0];
79  minY = vertex[1];
80  minZ = vertex[2];
81  maxX = vertex[0];
82  maxY = vertex[1];
83  maxZ = vertex[2];
84  firstVertex = false;
85  } else {
86  if (vertex[0] < minX) minX = vertex[0];
87  if (vertex[1] < minY) minY = vertex[1];
88  if (vertex[2] < minZ) minZ = vertex[2];
89  if (vertex[0] > maxX) maxX = vertex[0];
90  if (vertex[1] > maxY) maxY = vertex[1];
91  if (vertex[2] > maxZ) maxZ = vertex[2];
92  }
93  }
94  }
95  animationState.currentAtTime = static_cast<int64_t>((t * 1000.0f));
96  animationState.lastAtTime = static_cast<int64_t>((t * 1000.0f));
97  }
98  // skip on models without mesh
99  if (firstVertex == true) return nullptr;
100  // otherwise go with bounding box
101  return new BoundingBox(Vector3(minX, minY, minZ), Vector3(maxX, maxY, maxZ));
102 }
103 
105 {
106  auto model = objectModelInternal->getModel();
107  auto defaultAnimation = model->getAnimationSetup(Model::ANIMATIONSETUP_DEFAULT);
108  float minX = 0.0f, minY = 0.0f, minZ = 0.0f;
109  float maxX = 0.0f, maxY = 0.0f, maxZ = 0.0f;
110  auto firstVertex = true;
111  // create bounding box for whole animation at 60fps
112  AnimationState animationState;
113  animationState.setup = defaultAnimation;
114  animationState.lastAtTime = Timing::UNDEFINED;
115  animationState.currentAtTime = 0LL;
116  animationState.time = 0.0f;
117  animationState.finished = false;
118  Vector3 vertex;
119  for (auto t = 0.0f; t <= (defaultAnimation != nullptr ? static_cast<float>(defaultAnimation->getFrames()) : 0.0f) / model->getFPS(); t += 1.0f / model->getFPS()) {
120  // calculate transform matrices without world transform
121  auto parentTransformMatrix = objectModelInternal->getModel()->getImportTransformMatrix();
122  parentTransformMatrix.multiply(objectModelInternal->getTransformMatrix());
123  objectModelInternal->instanceAnimations[0]->computeNodesTransformMatrices(objectModelInternal->instanceAnimations[0]->nodeLists[0], parentTransformMatrix, &animationState);
124  for (const auto& [nodeId, node]: model->getNodes()) {
125  const auto& transformedNodeMatrix = objectModelInternal->getNodeTransformMatrix(node->getId());
126  vertex = transformedNodeMatrix.multiply(vertex.set(0.0f, 0.0f, 0.0f));
127  if (firstVertex == true) {
128  minX = vertex[0];
129  minY = vertex[1];
130  minZ = vertex[2];
131  maxX = vertex[0];
132  maxY = vertex[1];
133  maxZ = vertex[2];
134  firstVertex = false;
135  } else {
136  if (vertex[0] < minX) minX = vertex[0];
137  if (vertex[1] < minY) minY = vertex[1];
138  if (vertex[2] < minZ) minZ = vertex[2];
139  if (vertex[0] > maxX) maxX = vertex[0];
140  if (vertex[1] > maxY) maxY = vertex[1];
141  if (vertex[2] > maxZ) maxZ = vertex[2];
142  }
143  }
144  animationState.currentAtTime = static_cast<int64_t>((t * 1000.0f));
145  animationState.lastAtTime = static_cast<int64_t>((t * 1000.0f));
146  }
147  // skip on models without nodes
148  if (firstVertex == true) return nullptr;
149  // otherwise go with bounding box
150  return new BoundingBox(Vector3(minX, minY, minZ), Vector3(maxX, maxY, maxZ));
151 }
152 
154 {
155  invertNormals(model->getSubNodes());
156 }
157 
158 void ModelUtilitiesInternal::invertNormals(const unordered_map<string, Node*>& nodes)
159 {
160  for (const auto& [nodeId, node]: nodes) {
161  auto normals = node->getNormals();
162  for (auto& normal : normals) {
163  // invert
164  normal.scale(-1.0f);
165  }
166  node->setNormals(normals);
167  // process sub nodes
168  invertNormals(node->getSubNodes());
169  }
170 }
171 
173 {
174  ObjectModelInternal objectModelInternal(model);
175  computeModelStatistics(&objectModelInternal, modelStatistics);
176 }
177 
179 {
180  unordered_map<string, int32_t> materialCountById;
181  auto opaqueFaceCount = 0;
182  auto transparentFaceCount = 0;
183  for (auto objectNode : objectModelInternal->objectNodes) {
184  // check each faces entity
185  const auto& facesEntities = objectNode->node->getFacesEntities();
186  auto facesEntityIdxCount = facesEntities.size();
187  for (auto faceEntityIdx = 0; faceEntityIdx < facesEntityIdxCount; faceEntityIdx++) {
188  const auto& facesEntity = facesEntities[faceEntityIdx];
189  auto faces = facesEntity.getFaces().size();
190  // material
191  auto material = facesEntity.getMaterial();
192  // determine if transparent
193  auto transparentFacesEntity = false;
194  // via material
195  if (material != nullptr) {
196  auto specularMaterialProperties = material->getSpecularMaterialProperties();
197  if (specularMaterialProperties != nullptr &&
198  (specularMaterialProperties->hasColorTransparency() == true || specularMaterialProperties->hasTextureTransparency() == true))
199  transparentFacesEntity = true;
200 
201  }
202  // setup material usage
203  auto materialId = material == nullptr ? "tdme.material.none" : material->getId();
204  materialCountById[materialId]++;
205  // skip, if requested
206  if (transparentFacesEntity == true) {
207  // keep track of rendered faces
208  transparentFaceCount += faces;
209  // skip to next entity
210  continue;
211  }
212  opaqueFaceCount += faces;
213  }
214  }
215  // determine final material count
216  auto materialCount = materialCountById.size();
217  modelStatistics->opaqueFaceCount = opaqueFaceCount;
218  modelStatistics->transparentFaceCount = transparentFaceCount;
219  modelStatistics->materialCount = materialCount;
220 }
221 
223 {
224  ObjectModelInternal objectModel1(model1);
225  ObjectModelInternal objectModel2(model2);
226  return ModelUtilitiesInternal::equals(&objectModel1, &objectModel2);
227 }
228 
229 bool ModelUtilitiesInternal::equals(ObjectModelInternal* objectModel1Internal, ObjectModelInternal* objectModel2Internal)
230 {
231  // check number of object nodes
232  if (objectModel1Internal->objectNodes.size() != objectModel2Internal->objectNodes.size())
233  return false;
234 
235  for (auto i = 0; i < objectModel1Internal->objectNodes.size(); i++) {
236  auto objectNodeModel1 = objectModel1Internal->objectNodes[i];
237  auto objectNodeModel2 = objectModel2Internal->objectNodes[i];
238  auto node1 = objectModel1Internal->objectNodes[i]->node;
239  auto node2 = objectModel2Internal->objectNodes[i]->node;
240  const auto& facesEntitiesModel1 = objectNodeModel1->node->getFacesEntities();
241  const auto& facesEntitiesModel2 = objectNodeModel2->node->getFacesEntities();
242  // check transform matrix
243  if (objectNodeModel1->node->getTransformMatrix().equals(objectNodeModel2->node->getTransformMatrix()) == false)
244  return false;
245  // check vertices count
246  if (node1->getVertices().size() != node2->getVertices().size())
247  return false;
248  // check vertices
249  for (auto j = 0; j < node1->getVertices().size(); j++) {
250  if (node1->getVertices()[j].equals(node2->getVertices()[j]) == false)
251  return false;
252  }
253  // check normals count
254  if (node1->getNormals().size() != node2->getNormals().size())
255  return false;
256  // check normals
257  for (auto j = 0; j < node1->getNormals().size(); j++) {
258  if (node1->getNormals()[j].equals(node2->getNormals()[j]) == false)
259  return false;
260  }
261  // check number of faces entities
262  if (facesEntitiesModel1.size() != facesEntitiesModel2.size())
263  return false;
264  // check each faces entity
265  for (auto j = 0; j < facesEntitiesModel1.size(); j++) {
266  auto facesEntityModel1 = facesEntitiesModel1[j];
267  auto facesEntityModel2 = facesEntitiesModel2[j];
268  // check material
269  // TODO: check if it should be allowed to have NULL material
270  if (facesEntityModel1.getMaterial() == nullptr && facesEntityModel2.getMaterial() != nullptr)
271  return false;
272 
273  if (facesEntityModel1.getMaterial() != nullptr && facesEntityModel2.getMaterial() == nullptr)
274  return false;
275 
276  if (facesEntityModel1.getMaterial() != nullptr && facesEntityModel2.getMaterial() != nullptr &&
277  facesEntityModel1.getMaterial()->getId() != facesEntityModel2.getMaterial()->getId()) {
278  return false;
279  }
280  // check faces
281  const auto& facesModel1 = facesEntityModel1.getFaces();
282  const auto& facesModel2 = facesEntityModel2.getFaces();
283  // number of faces in faces entity
284  if (facesModel1.size() != facesModel2.size())
285  return false;
286  // face indices
287  for (auto k = 0; k < facesModel1.size(); k++) {
288  // vertex indices
289  auto vertexIndicesModel1 = facesModel1[k].getVertexIndices();
290  auto vertexIndicesModel2 = facesModel2[k].getVertexIndices();
291  if (vertexIndicesModel1[0] != vertexIndicesModel2[0] ||
292  vertexIndicesModel1[1] != vertexIndicesModel2[1] ||
293  vertexIndicesModel1[2] != vertexIndicesModel2[2]) {
294  return false;
295  }
296  // TODO: maybe other indices
297  }
298  // TODO: check vertices, normals and such
299  }
300  }
301  //
302  return true;
303 }
Timing class.
Definition: Timing.h:16
static constexpr int64_t UNDEFINED
Definition: Timing.h:20
Represents a model face, consisting of vertex, normal, tangent and bitangent vectors,...
Definition: Face.h:18
Node faces entity A node can have multiple entities containing faces and a applied material.
Definition: FacesEntity.h:23
Represents a material.
Definition: Material.h:23
Representation of a 3D model.
Definition: Model.h:35
unordered_map< string, Node * > & getSubNodes()
Returns object's sub nodes.
Definition: Model.h:220
AnimationSetup * getAnimationSetup(const string &id)
Definition: Model.h:274
const Matrix4x4 & getImportTransformMatrix()
Definition: Model.h:340
Model node.
Definition: Node.h:32
Represents specular material properties.
Axis aligned bounding box used for frustum, this is not directly connectable with physics engine.
Definition: BoundingBox.h:26
static void invertNormals(Model *model)
Invert normals of a model.
static void computeModelStatistics(Model *model, ModelStatistics *modelStatistics)
Compute model statistics.
static bool equals(Model *model1, Model *model2)
Compute if model 1 equals model 2.
static BoundingBox * createBoundingBox(Model *model, const unordered_map< string, Matrix4x4 * > overriddenNodeTransformMatrices=unordered_map< string, Matrix4x4 * >())
Creates a bounding box from given model.
static BoundingBox * createBoundingBoxNoMesh(ObjectModelInternal *objectModelInternal)
Creates a bounding box from given object model without mesh.
const Matrix4x4 getNodeTransformMatrix(const string &id)
Returns transform matrix for given node.
Definition: ObjectBase.h:269
vector< ObjectAnimation * > instanceAnimations
Definition: ObjectBase.h:55
const Matrix4x4 & getTransformMatrix() const
Definition: ObjectBase.h:293
Object model To be used in non engine context.
Object node mesh specifically for rendering.
Object node specifically for rendering.
Definition: ObjectNode.h:41
static void computeAnimation(int contextIdx, vector< ObjectNode * > &objectNodes)
Computes animation for given object nodes.
Definition: ObjectNode.cpp:185
Matrix4x4 class representing matrix4x4 mathematical structure and operations for 3d space.
Definition: Matrix4x4.h:23
Vector3 multiply(const Vector3 &vector3) const
Multiplies this matrix with vector3.
Definition: Matrix4x4.h:225
Vector3 class representing vector3 mathematical structure and operations with x, y,...
Definition: Vector3.h:20
Vector3 & set(float x, float y, float z)
Sets this vector3 by its components.
Definition: Vector3.h:70