Panda3D
Loading...
Searching...
No Matches
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...
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...
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.
This is our own Panda specialization on the default STL vector.
Definition pvector.h:42
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
std::ostream & indent(std::ostream &out, int indent_level)
A handy function for doing text formatting.
Definition indent.cxx:20
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.