Panda3D
bulletTriangleMesh.cxx
Go to the documentation of this file.
1 /**
2  * PANDA 3D SOFTWARE
3  * Copyright (c) Carnegie Mellon University. All rights reserved.
4  *
5  * All use of this software is subject to the terms of the revised BSD
6  * license. You should have received a copy of this license along
7  * with this source code in a file named "LICENSE."
8  *
9  * @file bulletTriangleMesh.cxx
10  * @author enn0x
11  * @date 2010-02-09
12  */
13 
14 #include "bulletTriangleMesh.h"
15 
16 #include "bulletWorld.h"
17 
18 #include "pvector.h"
19 #include "geomTriangles.h"
20 #include "geomVertexData.h"
21 #include "geomVertexReader.h"
22 
23 using std::endl;
24 
25 TypeHandle BulletTriangleMesh::_type_handle;
26 
27 /**
28  *
29  */
30 BulletTriangleMesh::
31 BulletTriangleMesh()
32  : _welding_distance(0) {
33  btIndexedMesh mesh;
34  mesh.m_numTriangles = 0;
35  mesh.m_numVertices = 0;
36  mesh.m_indexType = PHY_INTEGER;
37  mesh.m_triangleIndexBase = nullptr;
38  mesh.m_triangleIndexStride = 3 * sizeof(int);
39  mesh.m_vertexBase = nullptr;
40  mesh.m_vertexStride = sizeof(btVector3);
41  _mesh.addIndexedMesh(mesh);
42 }
43 
44 /**
45  * Returns the number of vertices in this triangle mesh.
46  */
47 size_t BulletTriangleMesh::
48 get_num_vertices() const {
49  LightMutexHolder holder(BulletWorld::get_global_lock());
50 
51  return _vertices.size();
52 }
53 
54 /**
55  * Returns the vertex at the given vertex index.
56  */
58 get_vertex(size_t index) const {
59  LightMutexHolder holder(BulletWorld::get_global_lock());
60 
61  nassertr(index < (size_t)_vertices.size(), LPoint3::zero());
62  const btVector3 &vertex = _vertices[index];
63  return LPoint3(vertex[0], vertex[1], vertex[2]);
64 }
65 
66 /**
67  * Returns the vertex indices making up the given triangle index.
68  */
69 LVecBase3i BulletTriangleMesh::
70 get_triangle(size_t index) const {
71  LightMutexHolder holder(BulletWorld::get_global_lock());
72 
73  index *= 3;
74  nassertr(index + 2 < (size_t)_indices.size(), LVecBase3i::zero());
75  return LVecBase3i(_indices[index], _indices[index + 1], _indices[index + 2]);
76 }
77 
78 /**
79  * Returns the number of triangles in this triangle mesh.
80  * Assumes the lock(bullet global lock) is held by the caller
81  */
84 
85  return _indices.size() / 3;
86 }
87 
88 /**
89  * Returns the number of triangles in this triangle mesh.
90  */
91 size_t BulletTriangleMesh::
92 get_num_triangles() const {
93  LightMutexHolder holder(BulletWorld::get_global_lock());
94 
95  return do_get_num_triangles();
96 }
97 
98 /**
99  * Used to reserve memory in anticipation of the given amount of vertices and
100  * indices being added to the triangle mesh. This is useful if you are about
101  * to call add_triangle() many times, to prevent unnecessary reallocations.
102  */
104 preallocate(int num_verts, int num_indices) {
105  LightMutexHolder holder(BulletWorld::get_global_lock());
106 
107  _vertices.reserve(num_verts);
108  _indices.reserve(num_indices);
109 
110  btIndexedMesh &mesh = _mesh.getIndexedMeshArray()[0];
111  mesh.m_vertexBase = (unsigned char*)&_vertices[0];
112  mesh.m_triangleIndexBase = (unsigned char *)&_indices[0];
113 }
114 
115 /**
116  * Adds a triangle with the indicated coordinates.
117  *
118  * If remove_duplicate_vertices is true, it will make sure that it does not
119  * add duplicate vertices if they already exist in the triangle mesh, within
120  * the tolerance specified by set_welding_distance(). This comes at a
121  * significant performance cost, especially for large meshes.
122  * Assumes the lock(bullet global lock) is held by the caller
123  */
125 do_add_triangle(const LPoint3 &p0, const LPoint3 &p1, const LPoint3 &p2, bool remove_duplicate_vertices) {
126 
127  nassertv(!p0.is_nan());
128  nassertv(!p1.is_nan());
129  nassertv(!p2.is_nan());
130 
131  btIndexedMesh &mesh = _mesh.getIndexedMeshArray()[0];
132  mesh.m_numTriangles++;
133 
134  if (!remove_duplicate_vertices) {
135  unsigned int index = _vertices.size();
136  _indices.push_back(index++);
137  _indices.push_back(index++);
138  _indices.push_back(index++);
139 
140  _vertices.push_back(LVecBase3_to_btVector3(p0));
141  _vertices.push_back(LVecBase3_to_btVector3(p1));
142  _vertices.push_back(LVecBase3_to_btVector3(p2));
143  mesh.m_numVertices += 3;
144  mesh.m_vertexBase = (unsigned char*)&_vertices[0];
145  } else {
146  _indices.push_back(find_or_add_vertex(p0));
147  _indices.push_back(find_or_add_vertex(p1));
148  _indices.push_back(find_or_add_vertex(p2));
149  }
150 
151  mesh.m_triangleIndexBase = (unsigned char *)&_indices[0];
152 }
153 
154 /**
155  * Adds a triangle with the indicated coordinates.
156  *
157  * If remove_duplicate_vertices is true, it will make sure that it does not
158  * add duplicate vertices if they already exist in the triangle mesh, within
159  * the tolerance specified by set_welding_distance(). This comes at a
160  * significant performance cost, especially for large meshes.
161  */
163 add_triangle(const LPoint3 &p0, const LPoint3 &p1, const LPoint3 &p2, bool remove_duplicate_vertices) {
164  LightMutexHolder holder(BulletWorld::get_global_lock());
165 
166  do_add_triangle(p0, p1, p2, remove_duplicate_vertices);
167 }
168 
169 /**
170  * Sets the square of the distance at which vertices will be merged
171  * together when adding geometry with remove_duplicate_vertices set to true.
172  *
173  * The default is 0, meaning vertices will only be merged if they have the
174  * exact same position.
175  */
177 set_welding_distance(PN_stdfloat distance) {
178  LightMutexHolder holder(BulletWorld::get_global_lock());
179 
180  _welding_distance = distance;
181 }
182 
183 /**
184  * Returns the value previously set with set_welding_distance(), or the
185  * value of 0 if none was set.
186  */
187 PN_stdfloat BulletTriangleMesh::
188 get_welding_distance() const {
189  LightMutexHolder holder(BulletWorld::get_global_lock());
190 
191  return _welding_distance;
192 }
193 
194 /**
195  * Adds the geometry from the indicated Geom from the triangle mesh. This is
196  * a one-time copy operation, and future updates to the Geom will not be
197  * reflected.
198  *
199  * If remove_duplicate_vertices is true, it will make sure that it does not
200  * add duplicate vertices if they already exist in the triangle mesh, within
201  * the tolerance specified by set_welding_distance(). This comes at a
202  * significant performance cost, especially for large meshes.
203  */
205 add_geom(const Geom *geom, bool remove_duplicate_vertices, const TransformState *ts) {
206  LightMutexHolder holder(BulletWorld::get_global_lock());
207 
208  nassertv(geom);
209  nassertv(ts);
210 
211  CPT(GeomVertexData) vdata = geom->get_vertex_data();
212  size_t num_vertices = vdata->get_num_rows();
213  GeomVertexReader reader = GeomVertexReader(vdata, InternalName::get_vertex());
214 
215  btIndexedMesh &mesh = _mesh.getIndexedMeshArray()[0];
216 
217  if (!remove_duplicate_vertices) {
218  // Fast path: directly copy the vertices and indices.
219  mesh.m_numVertices += num_vertices;
220  unsigned int index_offset = _vertices.size();
221  _vertices.reserve(_vertices.size() + num_vertices);
222 
223  if (ts->is_identity()) {
224  while (!reader.is_at_end()) {
225  _vertices.push_back(LVecBase3_to_btVector3(reader.get_data3()));
226  }
227  } else {
228  LMatrix4 m = ts->get_mat();
229  while (!reader.is_at_end()) {
230  _vertices.push_back(LVecBase3_to_btVector3(m.xform_point(reader.get_data3())));
231  }
232  }
233 
234  for (size_t k = 0; k < geom->get_num_primitives(); ++k) {
235  CPT(GeomPrimitive) prim = geom->get_primitive(k);
236  prim = prim->decompose();
237 
238  if (prim->is_of_type(GeomTriangles::get_class_type())) {
239  int num_vertices = prim->get_num_vertices();
240  _indices.reserve(_indices.size() + num_vertices);
241  mesh.m_numTriangles += num_vertices / 3;
242 
243  CPT(GeomVertexArrayData) vertices = prim->get_vertices();
244  if (vertices != nullptr) {
245  GeomVertexReader index(std::move(vertices), 0);
246  while (!index.is_at_end()) {
247  _indices.push_back(index_offset + index.get_data1i());
248  }
249  } else {
250  int index = index_offset + prim->get_first_vertex();
251  int end_index = index + num_vertices;
252  while (index < end_index) {
253  _indices.push_back(index++);
254  }
255  }
256  }
257  }
258  nassertv(mesh.m_numTriangles * 3 == _indices.size());
259 
260  } else {
261  // Collect points
262  pvector<LPoint3> points;
263  points.reserve(_vertices.size() + num_vertices);
264 
265  if (ts->is_identity()) {
266  while (!reader.is_at_end()) {
267  points.push_back(reader.get_data3());
268  }
269  } else {
270  LMatrix4 m = ts->get_mat();
271  while (!reader.is_at_end()) {
272  points.push_back(m.xform_point(reader.get_data3()));
273  }
274  }
275 
276  // Add triangles
277  for (size_t k = 0; k < geom->get_num_primitives(); ++k) {
278  CPT(GeomPrimitive) prim = geom->get_primitive(k);
279  prim = prim->decompose();
280 
281  if (prim->is_of_type(GeomTriangles::get_class_type())) {
282  int num_vertices = prim->get_num_vertices();
283  _indices.reserve(_indices.size() + num_vertices);
284  mesh.m_numTriangles += num_vertices / 3;
285 
286  CPT(GeomVertexArrayData) vertices = prim->get_vertices();
287  if (vertices != nullptr) {
288  GeomVertexReader index(std::move(vertices), 0);
289  while (!index.is_at_end()) {
290  _indices.push_back(find_or_add_vertex(points[index.get_data1i()]));
291  }
292  } else {
293  int index = prim->get_first_vertex();
294  int end_index = index + num_vertices;
295  while (index < end_index) {
296  _indices.push_back(find_or_add_vertex(points[index]));
297  }
298  }
299  }
300  }
301  nassertv(mesh.m_numTriangles * 3 == _indices.size());
302  }
303 
304  // Reset the pointers, since the vectors may have been reallocated.
305  mesh.m_vertexBase = (unsigned char*)&_vertices[0];
306  mesh.m_triangleIndexBase = (unsigned char *)&_indices[0];
307 }
308 
309 /**
310  * Adds triangle information from an array of points and indices referring to
311  * these points. This is more efficient than adding triangles one at a time.
312  *
313  * If remove_duplicate_vertices is true, it will make sure that it does not
314  * add duplicate vertices if they already exist in the triangle mesh, within
315  * the tolerance specified by set_welding_distance(). This comes at a
316  * significant performance cost, especially for large meshes.
317  */
319 add_array(const PTA_LVecBase3 &points, const PTA_int &indices, bool remove_duplicate_vertices) {
320  LightMutexHolder holder(BulletWorld::get_global_lock());
321 
322  btIndexedMesh &mesh = _mesh.getIndexedMeshArray()[0];
323 
324  _indices.reserve(_indices.size() + indices.size());
325 
326  if (!remove_duplicate_vertices) {
327  unsigned int index_offset = _vertices.size();
328  for (size_t i = 0; i < indices.size(); ++i) {
329  _indices.push_back(index_offset + indices[i]);
330  }
331 
332  _vertices.reserve(_vertices.size() + points.size());
333  for (size_t i = 0; i < points.size(); ++i) {
334  _vertices.push_back(LVecBase3_to_btVector3(points[i]));
335  }
336 
337  mesh.m_numVertices += points.size();
338 
339  } else {
340  // Add the points one by one.
341  _indices.reserve(_indices.size() + indices.size());
342  for (size_t i = 0; i < indices.size(); ++i) {
343  LVecBase3 p = points[indices[i]];
344  _indices.push_back(find_or_add_vertex(p));
345  }
346  }
347 
348  mesh.m_numTriangles += indices.size() / 3;
349 
350  // Reset the pointers, since the vectors may have been reallocated.
351  mesh.m_vertexBase = (unsigned char*)&_vertices[0];
352  mesh.m_triangleIndexBase = (unsigned char *)&_indices[0];
353 }
354 
355 /**
356  *
357  */
358 void BulletTriangleMesh::
359 output(std::ostream &out) const {
360  LightMutexHolder holder(BulletWorld::get_global_lock());
361 
362  out << get_type() << ", " << _indices.size() / 3 << " triangles";
363 }
364 
365 /**
366  *
367  */
368 void BulletTriangleMesh::
369 write(std::ostream &out, int indent_level) const {
370  indent(out, indent_level) << get_type() << ":" << endl;
371 
372  const IndexedMeshArray &array = _mesh.getIndexedMeshArray();
373  for (int i = 0; i < array.size(); ++i) {
374  indent(out, indent_level + 2) << "IndexedMesh " << i << ":" << endl;
375  const btIndexedMesh &mesh = array[0];
376  indent(out, indent_level + 4) << "num triangles:" << mesh.m_numTriangles << endl;
377  indent(out, indent_level + 4) << "num vertices:" << mesh.m_numVertices << endl;
378  }
379 }
380 
381 /**
382  * Finds the indicated vertex and returns its index. If it was not found,
383  * adds it as a new vertex and returns its index.
384  */
385 unsigned int BulletTriangleMesh::
386 find_or_add_vertex(const LVecBase3 &p) {
387  btVector3 vertex = LVecBase3_to_btVector3(p);
388 
389  for (int i = 0; i < _vertices.size(); ++i) {
390  if ((_vertices[i] - vertex).length2() <= _welding_distance) {
391  return i;
392  }
393  }
394 
395  _vertices.push_back(vertex);
396 
397  btIndexedMesh &mesh = _mesh.getIndexedMeshArray()[0];
398  mesh.m_numVertices++;
399  mesh.m_vertexBase = (unsigned char*)&_vertices[0];
400  return _vertices.size() - 1;
401 }
402 
403 /**
404  * Tells the BamReader how to create objects of type BulletTriangleMesh.
405  */
408  BamReader::get_factory()->register_factory(get_class_type(), make_from_bam);
409 }
410 
411 /**
412  * Writes the contents of this object to the datagram for shipping out to a
413  * Bam file.
414  */
418 
419  // In case we ever want to represent more than 1 indexed mesh.
420  dg.add_int32(1);
421 
422  btIndexedMesh &mesh = _mesh.getIndexedMeshArray()[0];
423  dg.add_int32(mesh.m_numVertices);
424  dg.add_int32(mesh.m_numTriangles);
425 
426  // In case we want to use this to distinguish 16-bit vs 32-bit indices.
427  dg.add_bool(true);
428 
429  // Add the vertices.
430  const unsigned char *vptr = mesh.m_vertexBase;
431  nassertv(vptr != nullptr || mesh.m_numVertices == 0);
432 
433  for (int i = 0; i < mesh.m_numVertices; ++i) {
434  const btVector3 &vertex = *((btVector3 *)vptr);
435  dg.add_stdfloat(vertex.getX());
436  dg.add_stdfloat(vertex.getY());
437  dg.add_stdfloat(vertex.getZ());
438  vptr += mesh.m_vertexStride;
439  }
440 
441  // Now add the triangle indices.
442  const unsigned char *iptr = mesh.m_triangleIndexBase;
443  nassertv(iptr != nullptr || mesh.m_numTriangles == 0);
444 
445  for (int i = 0; i < mesh.m_numTriangles; ++i) {
446  int *triangle = (int *)iptr;
447  dg.add_int32(triangle[0]);
448  dg.add_int32(triangle[1]);
449  dg.add_int32(triangle[2]);
450  iptr += mesh.m_triangleIndexStride;
451  }
452 }
453 
454 /**
455  * This function is called by the BamReader's factory when a new object of
456  * type BulletShape is encountered in the Bam file. It should create the
457  * BulletShape and extract its information from the file.
458  */
459 TypedWritable *BulletTriangleMesh::
460 make_from_bam(const FactoryParams &params) {
462  DatagramIterator scan;
463  BamReader *manager;
464 
465  parse_params(params, scan, manager);
466  param->fillin(scan, manager);
467 
468  return param;
469 }
470 
471 /**
472  * This internal function is called by make_from_bam to read in all of the
473  * relevant data from the BamFile for the new BulletTriangleMesh.
474  */
475 void BulletTriangleMesh::
476 fillin(DatagramIterator &scan, BamReader *manager) {
478 
479  nassertv(scan.get_int32() == 1);
480  int num_vertices = scan.get_int32();
481  int num_triangles = scan.get_int32();
482  nassertv(scan.get_bool() == true);
483 
484  btIndexedMesh &mesh = _mesh.getIndexedMeshArray()[0];
485  mesh.m_numVertices = num_vertices;
486  mesh.m_numTriangles = num_triangles;
487 
488  // Read and add the vertices.
489  _vertices.clear();
490  _vertices.reserve(num_vertices);
491  for (int i = 0; i < num_vertices; ++i) {
492  PN_stdfloat x = scan.get_stdfloat();
493  PN_stdfloat y = scan.get_stdfloat();
494  PN_stdfloat z = scan.get_stdfloat();
495  _vertices.push_back(btVector3(x, y, z));
496  }
497 
498  // Now read and add the indices.
499  size_t num_indices = (size_t)num_triangles * 3;
500  _indices.resize(num_indices);
501  scan.extract_bytes((unsigned char *)&_indices[0], num_indices * sizeof(int));
502 
503  // Reset the pointers, since the vectors may have been reallocated.
504  mesh.m_vertexBase = (unsigned char*)&_vertices[0];
505  mesh.m_triangleIndexBase = (unsigned char *)&_indices[0];
506 }
Indicates a coordinate-system transform on vertices.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
bool get_bool()
Extracts a boolean value.
PN_stdfloat get_stdfloat()
Extracts either a 32-bit or a 64-bit floating-point number, according to Datagram::set_stdfloat_doubl...
This is the fundamental interface for extracting binary objects from a Bam file, as generated by a Ba...
Definition: bamReader.h:110
virtual void write_datagram(BamWriter *manager, Datagram &dg)
Writes the contents of this object to the datagram for shipping out to a Bam file.
static void register_with_read_factory()
Tells the BamReader how to create objects of type BulletTriangleMesh.
void do_add_triangle(const LPoint3 &p0, const LPoint3 &p1, const LPoint3 &p2, bool remove_duplicate_vertices=false)
Adds a triangle with the indicated coordinates.
Base class for objects that can be written to and read from Bam files.
Definition: typedWritable.h:35
This is an abstract base class for a family of classes that represent the fundamental geometry primit...
Definition: geomPrimitive.h:56
get_vertex
Returns the vertex at the given vertex index.
This is the fundamental interface for writing binary objects to a Bam file, to be extracted later by ...
Definition: bamWriter.h:63
int32_t get_int32()
Extracts a signed 32-bit integer.
get_mat
Returns the matrix that describes the transform.
void add_stdfloat(PN_stdfloat value)
Adds either a 32-bit or a 64-bit floating-point number, according to set_stdfloat_double().
Definition: datagram.I:133
void add_geom(const Geom *geom, bool remove_duplicate_vertices=false, const TransformState *ts=TransformState::make_identity())
Adds the geometry from the indicated Geom from the triangle mesh.
vector_uchar extract_bytes(size_t size)
Extracts the indicated number of bytes in the datagram and returns them as a string.
void add_array(const PTA_LVecBase3 &points, const PTA_int &indices, bool remove_duplicate_vertices=false)
Adds triangle information from an array of points and indices referring to these points.
void parse_params(const FactoryParams &params, DatagramIterator &scan, BamReader *&manager)
Takes in a FactoryParams, passed from a WritableFactory into any TypedWritable's make function,...
Definition: bamReader.I:275
void add_bool(bool value)
Adds a boolean value to the datagram.
Definition: datagram.I:34
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
get_triangle
Returns the vertex indices making up the given triangle index.
std::ostream & indent(std::ostream &out, int indent_level)
A handy function for doing text formatting.
Definition: indent.cxx:20
Similar to MutexHolder, but for a light mutex.
This defines the actual numeric vertex data stored in a Geom, in the structure defined by a particula...
A container for geometry primitives.
Definition: geom.h:54
An instance of this class is passed to the Factory when requesting it to do its business and construc...
Definition: factoryParams.h:36
const LVecBase3 & get_data3()
Returns the data associated with the read row, expressed as a 3-component value, and advances the rea...
void register_factory(TypeHandle handle, CreateFunc *func, void *user_data=nullptr)
Registers a new kind of thing the Factory will be able to create.
Definition: factory.I:73
int get_data1i()
Returns the data associated with the read row, expressed as a 1-component value, and advances the rea...
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
void add_int32(int32_t value)
Adds a signed 32-bit integer to the datagram.
Definition: datagram.I:67
size_t do_get_num_triangles() const
Returns the number of triangles in this triangle mesh.
static WritableFactory * get_factory()
Returns the global WritableFactory for generating TypedWritable objects.
Definition: bamReader.I:177
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
void preallocate(int num_verts, int num_indices)
Used to reserve memory in anticipation of the given amount of vertices and indices being added to the...
This object provides a high-level interface for quickly reading a sequence of numeric values from a v...
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
set_welding_distance
Sets the square of the distance at which vertices will be merged together when adding geometry with r...
bool is_at_end() const
Returns true if the reader is currently at the end of the list of vertices, false otherwise.
void add_triangle(const LPoint3 &p0, const LPoint3 &p1, const LPoint3 &p2, bool remove_duplicate_vertices=false)
Adds a triangle with the indicated coordinates.
A class to retrieve the individual data elements previously stored in a Datagram.
TypeHandle is the identifier used to differentiate C++ class types.
Definition: typeHandle.h:81
An ordered list of data elements, formatted in memory for transmission over a socket or writing to a ...
Definition: datagram.h:38
get_welding_distance
Returns the value previously set with set_welding_distance(), or the value of 0 if none was set.
This is the data for one array of a GeomVertexData structure.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
bool is_identity() const
Returns true if the transform represents the identity matrix, false otherwise.