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
23using std::endl;
24
25TypeHandle BulletTriangleMesh::_type_handle;
26
27/**
28 *
29 */
30BulletTriangleMesh::
31BulletTriangleMesh()
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 */
48get_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 */
58get_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 */
69LVecBase3i BulletTriangleMesh::
70get_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 */
92get_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 */
104preallocate(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 */
125do_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 */
163add_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 */
177set_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 */
187PN_stdfloat BulletTriangleMesh::
188get_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 */
205add_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 */
319add_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 */
358void BulletTriangleMesh::
359output(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 */
368void BulletTriangleMesh::
369write(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 */
385unsigned int BulletTriangleMesh::
386find_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 */
416write_datagram(BamWriter *manager, Datagram &dg) {
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 */
459TypedWritable *BulletTriangleMesh::
460make_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 */
475void BulletTriangleMesh::
476fillin(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}
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
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
This is the fundamental interface for extracting binary objects from a Bam file, as generated by a Ba...
Definition: bamReader.h:110
static WritableFactory * get_factory()
Returns the global WritableFactory for generating TypedWritable objects.
Definition: bamReader.I:177
This is the fundamental interface for writing binary objects to a Bam file, to be extracted later by ...
Definition: bamWriter.h:63
void add_triangle(const LPoint3 &p0, const LPoint3 &p1, const LPoint3 &p2, bool remove_duplicate_vertices=false)
Adds a triangle with the indicated coordinates.
size_t do_get_num_triangles() const
Returns the number of triangles in this triangle mesh.
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.
virtual void write_datagram(BamWriter *manager, Datagram &dg)
Writes the contents of this object to the datagram for shipping out to a Bam file.
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.
static void register_with_read_factory()
Tells the BamReader how to create objects of type BulletTriangleMesh.
get_vertex
Returns the vertex at the given vertex index.
get_num_triangles
Returns the number of triangles in this triangle mesh.
get_welding_distance
Returns the value previously set with set_welding_distance(), or the value of 0 if none was set.
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...
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.
set_welding_distance
Sets the square of the distance at which vertices will be merged together when adding geometry with r...
get_triangle
Returns the vertex indices making up the given triangle index.
get_num_vertices
Returns the number of vertices in this triangle mesh.
A class to retrieve the individual data elements previously stored in a Datagram.
PN_stdfloat get_stdfloat()
Extracts either a 32-bit or a 64-bit floating-point number, according to Datagram::set_stdfloat_doubl...
vector_uchar extract_bytes(size_t size)
Extracts the indicated number of bytes in the datagram and returns them as a string.
bool get_bool()
Extracts a boolean value.
int32_t get_int32()
Extracts a signed 32-bit integer.
An ordered list of data elements, formatted in memory for transmission over a socket or writing to a ...
Definition: datagram.h:38
void add_int32(int32_t value)
Adds a signed 32-bit integer to the datagram.
Definition: datagram.I:67
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_bool(bool value)
Adds a boolean value to the datagram.
Definition: datagram.I:34
An instance of this class is passed to the Factory when requesting it to do its business and construc...
Definition: factoryParams.h:36
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
This is an abstract base class for a family of classes that represent the fundamental geometry primit...
Definition: geomPrimitive.h:56
This is the data for one array of a GeomVertexData structure.
This defines the actual numeric vertex data stored in a Geom, in the structure defined by a particula...
This object provides a high-level interface for quickly reading a sequence of numeric values from a v...
bool is_at_end() const
Returns true if the reader is currently at the end of the list of vertices, false otherwise.
int get_data1i()
Returns the data associated with the read row, expressed as a 1-component value, and advances the rea...
const LVecBase3 & get_data3()
Returns the data associated with the read row, expressed as a 3-component value, and advances the rea...
A container for geometry primitives.
Definition: geom.h:54
Similar to MutexHolder, but for a light mutex.
Indicates a coordinate-system transform on vertices.
get_mat
Returns the matrix that describes the transform.
bool is_identity() const
Returns true if the transform represents the identity matrix, false otherwise.
TypeHandle is the identifier used to differentiate C++ class types.
Definition: typeHandle.h:81
Base class for objects that can be written to and read from Bam files.
Definition: typedWritable.h:35
std::ostream & indent(std::ostream &out, int indent_level)
A handy function for doing text formatting.
Definition: dcindent.cxx:22
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.