TDME2  1.9.200
ConvexMesh.cpp
Go to the documentation of this file.
2 
3 #include <algorithm>
4 #include <array>
5 #include <memory>
6 #include <unordered_map>
7 #include <unordered_set>
8 #include <vector>
9 
10 #include <reactphysics3d/collision/shapes/ConvexMeshShape.h>
11 
13 
14 #include <tdme/tdme.h>
20 #include <tdme/engine/Transform.h>
21 #include <tdme/math/Math.h>
22 #include <tdme/math/Matrix4x4.h>
23 #include <tdme/math/Vector3.h>
25 #include <tdme/utilities/Console.h>
28 #include <tdme/utilities/Float.h>
32 
33 using std::array;
34 using std::find;
35 using std::make_unique;
36 using std::map;
37 using std::reverse;
38 using std::sort;
39 using std::unique;
40 using std::unique_ptr;
41 using std::unordered_map;
42 using std::unordered_set;
43 using std::vector;
44 
46 
54 using tdme::math::Math;
64 
65 ConvexMesh::ConvexMesh()
66 {
67 }
68 
69 void ConvexMesh::createConvexMesh(const vector<Vector3>& vertices, const vector<int>& facesVerticesCount, const vector<int>& indices, const Vector3& scale) {
70  // check if local translation is given
71  // determine center/position transformed
72  collisionShapeLocalTranslation.set(0.0f, 0.0f, 0.0f);
73  for (auto vertexIdx: indices) {
74  const auto& vertex = vertices[vertexIdx];
76  }
78 
79  // center
81 
82  // scale collision shape local translation
84 
85  // local transform
87 
88  // transform
89  collisionShapeTransform = reactphysics3d::Transform();
90 }
91 
93 {
94  vector<Triangle> triangles;
95  model->getTriangles(triangles);
96 
97  // determine coplanar faces of model
98  unordered_map<int, vector<const Triangle*>> trianglesCoplanar;
99  {
100  auto triangle1Idx = 0;
101  unordered_set<int> trianglesProcessed;
102  //
103  for (const auto& triangle1: triangles) {
104  if (trianglesProcessed.find(triangle1Idx) != trianglesProcessed.end()) {
105  triangle1Idx++;
106  continue;
107  }
108  // if not processed add outer triangle
109  trianglesCoplanar[triangle1Idx].push_back(&triangle1);
110  trianglesProcessed.insert(triangle1Idx);
111  //
112  auto triangle2Added = 0;
113  do {
114  auto triangle2Idx = 0;
115  triangle2Added = 0;
116  for (const auto& triangle2: triangles) {
117  // if alreaedy processed skip triangle2
118  if (trianglesProcessed.find(triangle2Idx) != trianglesProcessed.end()) {
119  triangle2Idx++;
120  continue;
121  }
122  // adjacent?
123  auto adjacent = areTrianglesAdjacent(triangle1, triangle2);
124  if (adjacent == false) {
125  for (auto triangle1CoplanarTriangle: trianglesCoplanar[triangle1Idx]) {
126  if (areTrianglesAdjacent(*triangle1CoplanarTriangle, triangle2) == true) {
127  adjacent = true;
128  break;
129  }
130  }
131  }
132  // same plane?
133  auto triangle2OnTriangle1Plane =
134  isVertexOnTrianglePlane(triangle1, triangle2.getVertices()[0]) &&
135  isVertexOnTrianglePlane(triangle1, triangle2.getVertices()[1]) &&
136  isVertexOnTrianglePlane(triangle1, triangle2.getVertices()[2]);
137  if (adjacent == true && triangle2OnTriangle1Plane == true) {
138  trianglesCoplanar[triangle1Idx].push_back(&triangle2);
139  // mark as processed
140  trianglesProcessed.insert(triangle2Idx);
141  // mark as added
142  triangle2Added++;
143  }
144  triangle2Idx++;
145  }
146  } while (triangle2Added > 0);
147  //
148  triangle1Idx++;
149  }
150  }
151 
152  // iterate triangles that are coplanar and build a polygon
153  for (const auto& [trianglesCoplanarIdx, trianglesCoplanarVector]: trianglesCoplanar) {
154  // collect polygon vertices
155  vector<Vector3> polygonVertices;
156 
157  // determine polygon vertices
158  for (auto triangle: trianglesCoplanarVector) {
159  for (const auto& triangleVertex: triangle->getVertices()) {
160  bool foundVertex = false;
161  for (const auto& polygonVertex: polygonVertices) {
162  if (polygonVertex.equals(triangleVertex, VERTEX_COMPARE_EPSILON) == true) {
163  foundVertex = true;
164  break;
165  }
166  }
167  if (foundVertex == false) {
168  polygonVertices.push_back(triangleVertex);
169  }
170  }
171  }
172 
173  // remove vertices that live on the line 2 other 2 vertices span
174  /*
175  {
176  vector<int> polygonVerticesToRemove;
177  Vector3 c;
178  for (auto i = 0; i < polygonVertices.size(); i++) {
179  for (auto j = 0; j < polygonVertices.size(); j++) {
180  if (i == j) continue;
181  for (auto k = 0; k < polygonVertices.size(); k++) {
182  if (i == k || j == k) continue;
183  LineSegment::computeClosestPointOnLineSegment(
184  polygonVertices[i],
185  polygonVertices[j],
186  polygonVertices[k],
187  c
188  );
189  if (polygonVertices[k].equals(c, VERTEX_COMPARE_EPSILON) == true) polygonVerticesToRemove.push_back(k);
190  }
191  }
192  }
193  sort(polygonVerticesToRemove.begin(), polygonVerticesToRemove.end());
194  polygonVerticesToRemove.erase(unique(polygonVerticesToRemove.begin(), polygonVerticesToRemove.end()), polygonVerticesToRemove.end());
195  auto polygonVerticesToRemoved = 0;
196  for (auto i: polygonVerticesToRemove) {
197  polygonVertices.erase(polygonVertices.begin() + i - polygonVerticesToRemoved);
198  polygonVerticesToRemoved++;
199  }
200  }
201  */
202 
203  // check if to skip as combined polygons could already have the current polygon
204  if (polygonVertices.size() > 2) {
205  auto skip = false;
206  auto idx = 0;
207  for (auto faceVertexCount: facesVerticesCount) {
208  unordered_set<int> foundIndices;
209  for (auto i = 0; i < faceVertexCount; i++) {
210  auto foundVertex = false;
211  for (const auto& polygonVertex: polygonVertices) {
212  if (polygonVertex.equals(vertices[indices[idx]], VERTEX_COMPARE_EPSILON) == true) {
213  foundIndices.insert(indices[idx]);
214  break;
215  }
216  }
217  idx++;
218  }
219  if (foundIndices.size() == polygonVertices.size()) {
220  skip = true;
221  break;
222  }
223  }
224  if (skip == true) {
225  continue;
226  }
227  }
228 
229  //
230  if (polygonVertices.size() < 3) continue;
231 
232  // determine polygon center, a point outside of mesh viewing the polygon
233  Vector3 polygonCenter;
234  for (const auto& polygonVertex: polygonVertices) {
235  polygonCenter.add(polygonVertex);
236  }
237  polygonCenter.scale(1.0f / polygonVertices.size());
238 
239  // plane normal
240  Vector3 triangle1Edge1;
241  Vector3 triangle1Edge2;
242  triangle1Edge1.set(trianglesCoplanarVector[0]->getVertices()[1]).sub(trianglesCoplanarVector[0]->getVertices()[0]).normalize();
243  triangle1Edge2.set(trianglesCoplanarVector[0]->getVertices()[2]).sub(trianglesCoplanarVector[0]->getVertices()[0]).normalize();
244  auto polygonNormal = Vector3::computeCrossProduct(triangle1Edge1, triangle1Edge2).normalize();
245 
246  // determine polygon vertices order
247  vector<int> polygonVerticesOrdered;
248  // add first vertex
249  polygonVerticesOrdered.push_back(0);
250 
251  // then check vertex order if it matches
252  // if it matches we have the next vertex
253  Vector3 distanceVector;
254  // as long as we have vertices left
255  while (polygonVerticesOrdered.size() != polygonVertices.size()) {
256  // find next vertex with most little
257  auto hitVertexAngle = 0.0f;
258  auto hitVertexIdx = -1;
259  for (int i = 0; i < polygonVertices.size(); i++) {
260  // check if already add to ordered vertices list
261  if (find(polygonVerticesOrdered.begin(), polygonVerticesOrdered.end(), i) != polygonVerticesOrdered.end()) continue;
262 
263  // otherwise check if angle is smaller
264  auto angleCurrent = Vector3::computeAngle(
265  polygonVertices[0].clone().sub(polygonCenter).normalize(),
266  polygonVertices[i].clone().sub(polygonCenter).normalize(),
267  polygonNormal
268  );
269  if (hitVertexIdx == -1 || angleCurrent < hitVertexAngle) {
270  hitVertexAngle = angleCurrent;
271  hitVertexIdx = i;
272  }
273  }
274  // yep
275  polygonVerticesOrdered.push_back(hitVertexIdx);
276  }
277 
278  /*
279  {
280  // vertex order
281  // see: https://stackoverflow.com/questions/14370636/sorting-a-list-of-3d-coplanar-points-to-be-clockwise-or-counterclockwise
282  auto& polygonVertexOrderedFirst = polygonVertices[polygonVerticesOrdered[0]];
283  auto& polygonVertexOrderedLast = polygonVertices[polygonVerticesOrdered[1]];
284  Vector3 ac;
285  Vector3 bc;
286  ac.set(polygonVertexOrderedFirst).sub(polygonCenter);
287  bc.set(polygonVertexOrderedLast).sub(polygonCenter);
288  auto acbcCross = Vector3::computeCrossProduct(ac, bc);
289  // counter clockwise???
290  if ((Vector3::computeDotProduct(polygonNormal, acbcCross) > 0.0f) == false) {
291  // yep, reverse
292  reverse(begin(polygonVerticesOrdered), end(polygonVerticesOrdered));
293  }
294  }
295  */
296 
297  // add face
298  facesVerticesCount.push_back(polygonVerticesOrdered.size());
299  for (auto polygonVerticesOrderedIdx: polygonVerticesOrdered) {
300  // polygon vertex
301  auto& polygonVertex = polygonVertices[polygonVerticesOrderedIdx];
302 
303  // check if to insert vertex
304  int vertexIdx = 0;
305  for (const auto& vertexExisting: vertices) {
306  if (vertexExisting.equals(polygonVertex, VERTEX_COMPARE_EPSILON) == true) {
307  break;
308  }
309  vertexIdx++;
310  }
311  if (vertexIdx == vertices.size()) {
312  vertices.push_back(polygonVertex);
313  }
314 
315  // add index
316  indices.push_back(vertexIdx);
317  }
318  }
319 
320  /*
321  static int wfObjIdx = 0;
322 
323  WFObjWriter wfObjWriter;
324  for (const auto& vertex: vertices) wfObjWriter.addVertex(vertex);
325  auto faceVertexIdx = 0;
326  for (auto& faceVertexCount: facesVerticesCount) {
327  vector<int> faceVertexIndices;
328  for (auto i = 0; i < faceVertexCount; i++) {
329  faceVertexIndices.push_back(indices[faceVertexIdx++]);
330  }
331  wfObjWriter.addFace(faceVertexIndices);
332  }
333  wfObjWriter.write(".", "wfobj." + to_string(wfObjIdx++) + ".obj");
334  */
335 
336  // create convex mesh
337  setScale(scale);
338 }
339 
340 ConvexMesh::ConvexMesh(const vector<Vector3>& vertices, const vector<int>& facesVerticesCount, const vector<int>& indices, const Vector3& scale) {
341  this->vertices = vertices;
342  this->facesVerticesCount = facesVerticesCount;
343  this->indices = indices;
344  setScale(scale);
345 }
346 
349 }
350 
351 void ConvexMesh::setScale(const Vector3& scale) {
352  // store new scale
353  this->scale.set(scale);
354  // recreate convex mesh
356  //
357  // generate vertices and indices buffers
358  verticesByteBuffer = unique_ptr<ByteBuffer>(ByteBuffer::allocate(vertices.size() * 3 * sizeof(float)));
359  indicesByteBuffer = unique_ptr<ByteBuffer>(ByteBuffer::allocate(indices.size() * sizeof(int)));
360  auto verticesBuffer = verticesByteBuffer->asFloatBuffer();
361  auto indicesBuffer = indicesByteBuffer->asIntBuffer();
362  Vector3 vertexTransformed;
363  for (const auto& vertex: vertices) {
364  vertexTransformed.set(vertex);
365  vertexTransformed.sub(center);
366  vertexTransformed.scale(scale);
367  verticesBuffer.put(vertexTransformed.getArray());
368  }
369  for (auto index: indices) {
370  indicesBuffer.put(index);
371  }
372  faces.clear();
373  int indexIdx = 0;
374  for (auto faceVerticesCount: facesVerticesCount) {
375  reactphysics3d::PolygonVertexArray::PolygonFace face;
376  face.nbVertices = faceVerticesCount;
377  face.indexBase = indexIdx;
378  faces.push_back(face);
379  indexIdx+= faceVerticesCount;
380  }
381 }
382 
384  if (collisionShape == nullptr) return;
385  this->world->physicsCommon.destroyConvexMeshShape(static_cast<reactphysics3d::ConvexMeshShape*>(collisionShape));
386  this->world->physicsCommon.destroyPolyhedronMesh(polyhedronMesh);
387  polygonVertexArray = nullptr;
388  verticesByteBuffer = nullptr;
389  indicesByteBuffer = nullptr;
390  collisionShape = nullptr;
391  polyhedronMesh = nullptr;
392  world = nullptr;
393 }
394 
396  if (this->world != nullptr && this->world != world) {
397  Console::println("ConvexMesh::createCollisionShape(): already attached to a world.");
398  }
399  this->world = world;
400 
401  //
402  try {
403  //
404  polygonVertexArray = make_unique<reactphysics3d::PolygonVertexArray>(
405  static_cast<uint32_t>(vertices.size()),
406  verticesByteBuffer->getBuffer(),
407  3 * sizeof(float),
408  indicesByteBuffer->getBuffer(),
409  sizeof(int),
410  static_cast<uint32_t>(faces.size()),
411  faces.data(),
412  reactphysics3d::PolygonVertexArray::VertexDataType::VERTEX_FLOAT_TYPE,
413  reactphysics3d::PolygonVertexArray::IndexDataType::INDEX_INTEGER_TYPE
414  );
415  polyhedronMesh = world->physicsCommon.createPolyhedronMesh(polygonVertexArray.get());
416  if (polyhedronMesh == nullptr) {
417  throw ExceptionBase("Invalid polyhedron mesh");
418  }
419  // create convex mesh shape
420  collisionShape = world->physicsCommon.createConvexMeshShape(polyhedronMesh);
421  } catch (Exception& exception) {
422  Console::println("ConvexMesh::createCollisionShape(): an error occurred: " + string(exception.what()));
423  if (collisionShape != nullptr) {
424  this->world->physicsCommon.destroyConvexMeshShape(static_cast<reactphysics3d::ConvexMeshShape*>(collisionShape));
425  collisionShape = nullptr;
426  }
427  if (polyhedronMesh != nullptr) {
428  this->world->physicsCommon.destroyPolyhedronMesh(polyhedronMesh);
429  polyhedronMesh = nullptr;
430  }
431  polygonVertexArray = nullptr;
432  this->world = nullptr;
433  }
434 }
435 
437 {
439 }
440 
441 const vector<Vector3>& ConvexMesh::getVertices() {
442  return vertices;
443 }
Transform which contain scale, rotations and translation.
Definition: Transform.h:29
Wavefront object model writer.
Definition: WFObjWriter.h:20
Dynamic physics world class.
Definition: World.h:38
reactphysics3d::PhysicsCommon physicsCommon
Definition: World.h:53
reactphysics3d::Transform collisionShapeLocalTransform
reactphysics3d::Transform collisionShapeTransform
reactphysics3d::CollisionShape * collisionShape
Convex mesh physics primitive.
Definition: ConvexMesh.h:37
unique_ptr< reactphysics3d::PolygonVertexArray > polygonVertexArray
Definition: ConvexMesh.h:46
static constexpr float VERTEX_COMPARE_EPSILON
Definition: ConvexMesh.h:39
reactphysics3d::PolyhedronMesh * polyhedronMesh
Definition: ConvexMesh.h:45
bool isVertexOnTrianglePlane(const Triangle &triangle, const Vector3 &vertex)
Checks if vertex lives on triangle plane.
Definition: ConvexMesh.h:69
void createConvexMesh(const vector< Vector3 > &vertices, const vector< int > &facesVerticesCount, const vector< int > &indices, const Vector3 &scale)
Create convex mesh Note: it also translates center into origin.
Definition: ConvexMesh.cpp:69
void destroyCollisionShape() override
Destroy collision shape.
Definition: ConvexMesh.cpp:383
void setScale(const Vector3 &scale) override
Set local scale.
Definition: ConvexMesh.cpp:351
const vector< Vector3 > & getVertices()
Definition: ConvexMesh.cpp:441
void createCollisionShape(World *world) override
Create collision shap.
Definition: ConvexMesh.cpp:395
BoundingVolume * clone() const override
Clones this bounding volume.
Definition: ConvexMesh.cpp:436
unique_ptr< ByteBuffer > verticesByteBuffer
Definition: ConvexMesh.h:47
vector< reactphysics3d::PolygonVertexArray::PolygonFace > faces
Definition: ConvexMesh.h:49
unique_ptr< ByteBuffer > indicesByteBuffer
Definition: ConvexMesh.h:48
bool areTrianglesAdjacent(const Triangle &triangle1, const Triangle &triangle2)
Checks if 2 triangles are adjacent.
Definition: ConvexMesh.h:90
Line segment helper functions.
Definition: LineSegment.h:16
Triangle entity, this is not directly connectable with physics engine.
Definition: Triangle.h:18
void getTriangles(vector< Triangle > &triangles, int nodeIdx=-1)
Retrieves list of triangles of all or given nodes.
Definition: ObjectBase.cpp:99
Standard math functions.
Definition: Math.h:19
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
const array< float, 3 > & getArray() const
Definition: Vector3.h:366
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
Vector3 & normalize()
Normalizes this vector3.
Definition: Vector3.h:239
Byte buffer class.
Definition: ByteBuffer.h:27
Console class.
Definition: Console.h:29
Exception base class.
Definition: ExceptionBase.h:19
Float buffer class.
Definition: FloatBuffer.h:18
Float class.
Definition: Float.h:27
Integer buffer class.
Definition: IntBuffer.h:14
Model tools functions class.
Definition: ModelTools.h:42
std::exception Exception
Exception base class.
Definition: Exception.h:18