Panda3D
Loading...
Searching...
No Matches
collisionPlane.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 collisionPlane.cxx
10 * @author drose
11 * @date 2000-04-25
12 */
13
14#include "collisionPlane.h"
15#include "collisionHandler.h"
16#include "collisionEntry.h"
17#include "collisionSphere.h"
18#include "collisionLine.h"
19#include "collisionRay.h"
20#include "collisionSegment.h"
21#include "collisionCapsule.h"
22#include "collisionParabola.h"
23#include "config_collide.h"
24#include "pointerToArray.h"
25#include "geomNode.h"
26#include "geom.h"
27#include "datagram.h"
28#include "datagramIterator.h"
29#include "bamReader.h"
30#include "bamWriter.h"
31#include "boundingPlane.h"
32#include "geom.h"
33#include "geomTrifans.h"
34#include "geomLinestrips.h"
35#include "geomVertexWriter.h"
36
37PStatCollector CollisionPlane::_volume_pcollector("Collision Volumes:CollisionPlane");
38PStatCollector CollisionPlane::_test_pcollector("Collision Tests:CollisionPlane");
39TypeHandle CollisionPlane::_type_handle;
40
41/**
42 *
43 */
44CollisionSolid *CollisionPlane::
45make_copy() {
46 return new CollisionPlane(*this);
47}
48
49/**
50 * Transforms the solid by the indicated matrix.
51 */
53xform(const LMatrix4 &mat) {
54 _plane = _plane * mat;
55 CollisionSolid::xform(mat);
56}
57
58/**
59 * Returns the point in space deemed to be the "origin" of the solid for
60 * collision purposes. The closest intersection point to this origin point is
61 * considered to be the most significant.
62 */
65 // No real sensible origin exists for a plane. We return 0, 0, 0, without
66 // even bothering to ensure that that point exists on the plane.
67 return LPoint3::origin();
68}
69
70/**
71 * Returns a PStatCollector that is used to count the number of bounding
72 * volume tests made against a solid of this type in a given frame.
73 */
76 return _volume_pcollector;
77}
78
79/**
80 * Returns a PStatCollector that is used to count the number of intersection
81 * tests made against a solid of this type in a given frame.
82 */
85 return _test_pcollector;
86}
87
88/**
89 *
90 */
91void CollisionPlane::
92output(std::ostream &out) const {
93 out << "cplane, (" << _plane << ")";
94}
95
96/**
97 *
98 */
99PT(BoundingVolume) CollisionPlane::
100compute_internal_bounds() const {
101 return new BoundingPlane(_plane);
102}
103
104/**
105 *
106 */
107PT(CollisionEntry) CollisionPlane::
108test_intersection_from_sphere(const CollisionEntry &entry) const {
109 const CollisionSphere *sphere;
110 DCAST_INTO_R(sphere, entry.get_from(), nullptr);
111
112 const LMatrix4 &wrt_mat = entry.get_wrt_mat();
113
114 LPoint3 from_center = sphere->get_center() * wrt_mat;
115 LVector3 from_radius_v =
116 LVector3(sphere->get_radius(), 0.0f, 0.0f) * wrt_mat;
117 PN_stdfloat from_radius = length(from_radius_v);
118
119 PN_stdfloat dist = dist_to_plane(from_center);
120 if (dist > from_radius) {
121 // No intersection.
122 return nullptr;
123 }
124
125 if (collide_cat.is_debug()) {
126 collide_cat.debug()
127 << "intersection detected from " << entry.get_from_node_path()
128 << " into " << entry.get_into_node_path() << "\n";
129 }
130 PT(CollisionEntry) new_entry = new CollisionEntry(entry);
131
132 LVector3 normal = (
134 ? get_effective_normal() : get_normal();
135
136 new_entry->set_surface_normal(normal);
137 new_entry->set_surface_point(from_center - get_normal() * dist);
138 new_entry->set_interior_point(from_center - get_normal() * from_radius);
139
140 return new_entry;
141}
142
143/**
144 *
145 */
146PT(CollisionEntry) CollisionPlane::
147test_intersection_from_line(const CollisionEntry &entry) const {
148 const CollisionLine *line;
149 DCAST_INTO_R(line, entry.get_from(), nullptr);
150
151 const LMatrix4 &wrt_mat = entry.get_wrt_mat();
152
153 LPoint3 from_origin = line->get_origin() * wrt_mat;
154 LVector3 from_direction = line->get_direction() * wrt_mat;
155
156 PN_stdfloat t;
157 if (!_plane.intersects_line(t, from_origin, from_direction)) {
158 // No intersection. The line is parallel to the plane.
159
160 if (_plane.dist_to_plane(from_origin) > 0.0f) {
161 // The line is entirely in front of the plane.
162 return nullptr;
163 }
164
165 // The line is entirely behind the plane.
166 t = 0.0f;
167 }
168
169 if (collide_cat.is_debug()) {
170 collide_cat.debug()
171 << "intersection detected from " << entry.get_from_node_path()
172 << " into " << entry.get_into_node_path() << "\n";
173 }
174 PT(CollisionEntry) new_entry = new CollisionEntry(entry);
175
176 LPoint3 into_intersection_point = from_origin + t * from_direction;
177
178 LVector3 normal =
180 ? get_effective_normal() : get_normal();
181
182 new_entry->set_surface_normal(normal);
183 new_entry->set_surface_point(into_intersection_point);
184
185 return new_entry;
186}
187
188/**
189 *
190 */
191PT(CollisionEntry) CollisionPlane::
192test_intersection_from_ray(const CollisionEntry &entry) const {
193 const CollisionRay *ray;
194 DCAST_INTO_R(ray, entry.get_from(), nullptr);
195
196 const LMatrix4 &wrt_mat = entry.get_wrt_mat();
197
198 LPoint3 from_origin = ray->get_origin() * wrt_mat;
199 LVector3 from_direction = ray->get_direction() * wrt_mat;
200
201 PN_stdfloat t;
202
203 if (_plane.dist_to_plane(from_origin) < 0.0f) {
204 // The origin of the ray is behind the plane, so we don't need to test
205 // further.
206 t = 0.0f;
207
208 } else {
209 if (!_plane.intersects_line(t, from_origin, from_direction)) {
210 // No intersection. The ray is parallel to the plane.
211 return nullptr;
212 }
213
214 if (t < 0.0f) {
215 // The intersection point is before the start of the ray, and so the ray
216 // is entirely in front of the plane.
217 return nullptr;
218 }
219 }
220
221 if (collide_cat.is_debug()) {
222 collide_cat.debug()
223 << "intersection detected from " << entry.get_from_node_path()
224 << " into " << entry.get_into_node_path() << "\n";
225 }
226 PT(CollisionEntry) new_entry = new CollisionEntry(entry);
227
228 LPoint3 into_intersection_point = from_origin + t * from_direction;
229
230 LVector3 normal =
232 ? get_effective_normal() : get_normal();
233
234 new_entry->set_surface_normal(normal);
235 new_entry->set_surface_point(into_intersection_point);
236
237 return new_entry;
238}
239
240/**
241 *
242 */
243PT(CollisionEntry) CollisionPlane::
244test_intersection_from_segment(const CollisionEntry &entry) const {
245 const CollisionSegment *segment;
246 DCAST_INTO_R(segment, entry.get_from(), nullptr);
247
248 const LMatrix4 &wrt_mat = entry.get_wrt_mat();
249
250 LPoint3 from_a = segment->get_point_a() * wrt_mat;
251 LPoint3 from_b = segment->get_point_b() * wrt_mat;
252
253 PN_stdfloat dist_a = _plane.dist_to_plane(from_a);
254 PN_stdfloat dist_b = _plane.dist_to_plane(from_b);
255
256 if (dist_a >= 0.0f && dist_b >= 0.0f) {
257 // Entirely in front of the plane means no intersection.
258 return nullptr;
259 }
260
261 if (collide_cat.is_debug()) {
262 collide_cat.debug()
263 << "intersection detected from " << entry.get_from_node_path()
264 << " into " << entry.get_into_node_path() << "\n";
265 }
266 PT(CollisionEntry) new_entry = new CollisionEntry(entry);
267
268 LVector3 normal = (has_effective_normal() && segment->get_respect_effective_normal()) ? get_effective_normal() : get_normal();
269 new_entry->set_surface_normal(normal);
270
271 PN_stdfloat t;
272 LVector3 from_direction = from_b - from_a;
273 if (_plane.intersects_line(t, from_a, from_direction)) {
274 // It intersects the plane.
275 if (t >= 0.0f && t <= 1.0f) {
276 // Within the segment! Yay, that means we have a surface point.
277 new_entry->set_surface_point(from_a + t * from_direction);
278 }
279 }
280
281 if (dist_a < dist_b) {
282 // Point A penetrates deeper.
283 new_entry->set_interior_point(from_a);
284
285 } else if (dist_b < dist_a) {
286 // No, point B does.
287 new_entry->set_interior_point(from_b);
288
289 } else {
290 // Let's be fair and choose the center of the segment.
291 new_entry->set_interior_point((from_a + from_b) * 0.5);
292 }
293
294 return new_entry;
295}
296
297/**
298 *
299 */
300PT(CollisionEntry) CollisionPlane::
301test_intersection_from_capsule(const CollisionEntry &entry) const {
302 const CollisionCapsule *capsule;
303 DCAST_INTO_R(capsule, entry.get_from(), nullptr);
304
305 const LMatrix4 &wrt_mat = entry.get_wrt_mat();
306
307 LPoint3 from_a = capsule->get_point_a() * wrt_mat;
308 LPoint3 from_b = capsule->get_point_b() * wrt_mat;
309 LVector3 from_radius_v =
310 LVector3(capsule->get_radius(), 0.0f, 0.0f) * wrt_mat;
311 PN_stdfloat from_radius = length(from_radius_v);
312
313 PN_stdfloat dist_a = _plane.dist_to_plane(from_a);
314 PN_stdfloat dist_b = _plane.dist_to_plane(from_b);
315
316 if (dist_a >= from_radius && dist_b >= from_radius) {
317 // Entirely in front of the plane means no intersection.
318 return nullptr;
319 }
320
321 if (collide_cat.is_debug()) {
322 collide_cat.debug()
323 << "intersection detected from " << entry.get_from_node_path()
324 << " into " << entry.get_into_node_path() << "\n";
325 }
326 PT(CollisionEntry) new_entry = new CollisionEntry(entry);
327
328 LVector3 normal = (has_effective_normal() && capsule->get_respect_effective_normal()) ? get_effective_normal() : get_normal();
329 new_entry->set_surface_normal(normal);
330
331 PN_stdfloat t;
332 LVector3 from_direction = from_b - from_a;
333 if (_plane.intersects_line(t, from_a, from_direction)) {
334 // It intersects the plane.
335 if (t >= 1.0f) {
336 new_entry->set_surface_point(from_b - get_normal() * dist_b);
337
338 } else if (t <= 0.0f) {
339 new_entry->set_surface_point(from_a - get_normal() * dist_a);
340
341 } else {
342 // Within the capsule! Yay, that means we have a surface point.
343 new_entry->set_surface_point(from_a + t * from_direction);
344 }
345 } else {
346 // If it's completely parallel, pretend it's colliding in the center of
347 // the capsule.
348 new_entry->set_surface_point(from_a + 0.5f * from_direction - get_normal() * dist_a);
349 }
350
351 if (IS_NEARLY_EQUAL(dist_a, dist_b)) {
352 // Let's be fair and choose the center of the capsule.
353 new_entry->set_interior_point(from_a + 0.5f * from_direction - get_normal() * from_radius);
354
355 } else if (dist_a < dist_b) {
356 // Point A penetrates deeper.
357 new_entry->set_interior_point(from_a - get_normal() * from_radius);
358
359 } else if (dist_b < dist_a) {
360 // No, point B does.
361 new_entry->set_interior_point(from_b - get_normal() * from_radius);
362 }
363
364 return new_entry;
365}
366
367/**
368 * This is part of the double-dispatch implementation of test_intersection().
369 * It is called when the "from" object is a parabola.
370 */
371PT(CollisionEntry) CollisionPlane::
372test_intersection_from_parabola(const CollisionEntry &entry) const {
373 const CollisionParabola *parabola;
374 DCAST_INTO_R(parabola, entry.get_from(), nullptr);
375
376 const LMatrix4 &wrt_mat = entry.get_wrt_mat();
377
378 // Convert the parabola into local coordinate space.
379 LParabola local_p(parabola->get_parabola());
380 local_p.xform(wrt_mat);
381
382 PN_stdfloat t;
383 if (_plane.dist_to_plane(local_p.calc_point(parabola->get_t1())) < 0.0f) {
384 // The first point in the parabola is behind the plane, so we don't need
385 // to test further.
386 t = parabola->get_t1();
387
388 } else {
389 PN_stdfloat t1, t2;
390 if (!get_plane().intersects_parabola(t1, t2, local_p)) {
391 // No intersection. The infinite parabola is entirely in front of the
392 // plane.
393 return nullptr;
394 }
395
396 if (t1 >= parabola->get_t1() && t1 <= parabola->get_t2()) {
397 if (t2 >= parabola->get_t1() && t2 <= parabola->get_t2()) {
398 // Both intersection points are within our segment of the parabola.
399 // Choose the first of the two.
400 t = std::min(t1, t2);
401 } else {
402 // Only t1 is within our segment.
403 t = t1;
404 }
405
406 } else if (t2 >= parabola->get_t1() && t2 <= parabola->get_t2()) {
407 // Only t2 is within our segment.
408 t = t2;
409
410 } else {
411 // Neither intersection point is within our segment.
412 return nullptr;
413 }
414 }
415
416 if (collide_cat.is_debug()) {
417 collide_cat.debug()
418 << "intersection detected from " << entry.get_from_node_path()
419 << " into " << entry.get_into_node_path() << "\n";
420 }
421 PT(CollisionEntry) new_entry = new CollisionEntry(entry);
422
423 LPoint3 into_intersection_point = local_p.calc_point(t);
424
425 LVector3 normal = (has_effective_normal() && parabola->get_respect_effective_normal()) ? get_effective_normal() : get_normal();
426
427 new_entry->set_surface_normal(normal);
428 new_entry->set_surface_point(into_intersection_point);
429
430 return new_entry;
431}
432
433/**
434 * This is part of the double-dispatch implementation of test_intersection().
435 * It is called when the "from" object is a box.
436 */
437PT(CollisionEntry) CollisionPlane::
438test_intersection_from_box(const CollisionEntry &entry) const {
439 const CollisionBox *box;
440 DCAST_INTO_R(box, entry.get_from(), nullptr);
441
442 const LMatrix4 &wrt_mat = entry.get_wrt_mat();
443
444 LPoint3 from_center = box->get_center() * wrt_mat;
445 LVector3 from_extents = box->get_dimensions() * 0.5f;
446 PN_stdfloat dist = _plane.dist_to_plane(from_center);
447
448 // Determine the basis vectors describing the box.
449 LVecBase3 box_x = wrt_mat.get_row3(0) * from_extents[0];
450 LVecBase3 box_y = wrt_mat.get_row3(1) * from_extents[1];
451 LVecBase3 box_z = wrt_mat.get_row3(2) * from_extents[2];
452
453 // Project the box onto the normal vector of the plane to determine whether
454 // there is a separating axis.
455 PN_stdfloat dx = box_x.dot(_plane.get_normal());
456 PN_stdfloat dy = box_y.dot(_plane.get_normal());
457 PN_stdfloat dz = box_z.dot(_plane.get_normal());
458 PN_stdfloat depth = dist - (cabs(dx) + cabs(dy) + cabs(dz));
459
460 if (depth > 0) {
461 // No collision.
462 return nullptr;
463 }
464
465 if (collide_cat.is_debug()) {
466 collide_cat.debug()
467 << "intersection detected from " << entry.get_from_node_path()
468 << " into " << entry.get_into_node_path() << "\n";
469 }
470 PT(CollisionEntry) new_entry = new CollisionEntry(entry);
471
472 LVector3 normal = (has_effective_normal() && box->get_respect_effective_normal()) ? get_effective_normal() : get_normal();
473 new_entry->set_surface_normal(normal);
474
475 // Determine which point on the cube will be the interior point. If the
476 // points are equally close, this chooses their center instead.
477 LPoint3 interior_point = from_center +
478 box_x * ((dx < 0) - (dx > 0)) +
479 box_y * ((dy < 0) - (dy > 0)) +
480 box_z * ((dz < 0) - (dz > 0));
481
482 // The surface point is the interior point projected onto the plane.
483 new_entry->set_surface_point(interior_point - get_normal() * depth);
484 new_entry->set_interior_point(interior_point);
485
486 return new_entry;
487}
488
489/**
490 * Fills the _viz_geom GeomNode up with Geoms suitable for rendering this
491 * solid.
492 */
493void CollisionPlane::
494fill_viz_geom() {
495 if (collide_cat.is_debug()) {
496 collide_cat.debug()
497 << "Recomputing viz for " << *this << "\n";
498 }
499
500 // Since we can't represent an infinite plane, we'll have to be satisfied
501 // with drawing a big polygon. Choose four points on the plane to be the
502 // corners of the polygon.
503
504 // We must choose four points fairly reasonably spread apart on the plane.
505 // We'll start with a center point and one corner point, and then use cross
506 // products to find the remaining three corners of a square.
507
508 // The center point will be on the axis with the largest coefficent. The
509 // first corner will be diagonal in the other two dimensions.
510
511 LPoint3 cp;
512 LVector3 p1, p2, p3, p4;
513
514 LVector3 normal = get_normal();
515 PN_stdfloat D = _plane[3];
516
517 if (fabs(normal[0]) > fabs(normal[1]) &&
518 fabs(normal[0]) > fabs(normal[2])) {
519 // X has the largest coefficient.
520 cp.set(-D / normal[0], 0.0f, 0.0f);
521 p1 = LPoint3(-(normal[1] + normal[2] + D)/normal[0], 1.0f, 1.0f) - cp;
522
523 } else if (fabs(normal[1]) > fabs(normal[2])) {
524 // Y has the largest coefficient.
525 cp.set(0.0f, -D / normal[1], 0.0f);
526 p1 = LPoint3(1.0f, -(normal[0] + normal[2] + D)/normal[1], 1.0f) - cp;
527
528 } else {
529 // Z has the largest coefficient.
530 cp.set(0.0f, 0.0f, -D / normal[2]);
531 p1 = LPoint3(1.0f, 1.0f, -(normal[0] + normal[1] + D)/normal[2]) - cp;
532 }
533
534 p1.normalize();
535 p2 = cross(normal, p1);
536 p3 = cross(normal, p2);
537 p4 = cross(normal, p3);
538
539 static const double plane_scale = 10.0;
540
541 PT(GeomVertexData) vdata = new GeomVertexData
542 ("collision", GeomVertexFormat::get_v3(),
543 Geom::UH_static);
544 GeomVertexWriter vertex(vdata, InternalName::get_vertex());
545
546 vertex.add_data3(cp + p1 * plane_scale);
547 vertex.add_data3(cp + p2 * plane_scale);
548 vertex.add_data3(cp + p3 * plane_scale);
549 vertex.add_data3(cp + p4 * plane_scale);
550
551 PT(GeomTrifans) body = new GeomTrifans(Geom::UH_static);
552 body->add_consecutive_vertices(0, 4);
553 body->close_primitive();
554
555 PT(GeomLinestrips) border = new GeomLinestrips(Geom::UH_static);
556 border->add_consecutive_vertices(0, 4);
557 border->add_vertex(0);
558 border->close_primitive();
559
560 PT(Geom) geom1 = new Geom(vdata);
561 geom1->add_primitive(body);
562
563 PT(Geom) geom2 = new Geom(vdata);
564 geom2->add_primitive(border);
565
566 _viz_geom->add_geom(geom1, get_solid_viz_state());
567 _viz_geom->add_geom(geom2, get_wireframe_viz_state());
568
569 _bounds_viz_geom->add_geom(geom1, get_solid_bounds_viz_state());
570 _bounds_viz_geom->add_geom(geom2, get_wireframe_bounds_viz_state());
571}
572
573/**
574 * Function to write the important information in the particular object to a
575 * Datagram
576 */
578write_datagram(BamWriter *manager, Datagram &me)
579{
581 _plane.write_datagram(me);
582}
583
584/**
585 * Function that reads out of the datagram (or asks manager to read) all of
586 * the data that is needed to re-create this object and stores it in the
587 * appropiate place
588 */
589void CollisionPlane::
590fillin(DatagramIterator& scan, BamReader* manager)
591{
592 CollisionSolid::fillin(scan, manager);
593 _plane.read_datagram(scan);
594}
595
596/**
597 * Factory method to generate a CollisionPlane object
598 */
601{
603 DatagramIterator scan;
604 BamReader *manager;
605
606 parse_params(params, scan, manager);
607 me->fillin(scan, manager);
608 return me;
609}
610
611/**
612 * Factory method to generate a CollisionPlane object
613 */
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
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
This funny bounding volume is an infinite plane that divides space into two regions: the part behind ...
This is an abstract class for any volume in any sense which can be said to define the locality of ref...
A cuboid collision volume or object.
This implements a solid consisting of a cylinder with hemispherical endcaps, also known as a capsule ...
Defines a single collision event.
get_from_node_path
Returns the NodePath that represents the CollisionNode that contains the CollisionSolid that triggere...
get_into_node_path
Returns the NodePath that represents the specific CollisionNode or GeomNode instance that was collide...
get_from
Returns the CollisionSolid pointer for the particular solid that triggered this collision.
An infinite line, similar to a CollisionRay, except that it extends in both directions.
This defines a parabolic arc, or subset of an arc, similar to the path of a projectile or falling obj...
get_t1
Returns the starting point on the parabola.
get_parabola
Returns the parabola specified by this solid.
virtual LPoint3 get_collision_origin() const
Returns the point in space deemed to be the "origin" of the solid for collision purposes.
virtual PStatCollector & get_volume_pcollector()
Returns a PStatCollector that is used to count the number of bounding volume tests made against a sol...
static void register_with_read_factory()
Factory method to generate a CollisionPlane object.
virtual void write_datagram(BamWriter *manager, Datagram &me)
Function to write the important information in the particular object to a Datagram.
static TypedWritable * make_CollisionPlane(const FactoryParams &params)
Factory method to generate a CollisionPlane object.
virtual PStatCollector & get_test_pcollector()
Returns a PStatCollector that is used to count the number of intersection tests made against a solid ...
virtual void xform(const LMatrix4 &mat)
Transforms the solid by the indicated matrix.
An infinite ray, with a specific origin and direction.
A finite line segment, with two specific endpoints but no thickness.
The abstract base class for all things that can collide with other things in the world,...
virtual void write_datagram(BamWriter *manager, Datagram &me)
Function to write the important information in the particular object to a Datagram.
bool has_effective_normal() const
Returns true if a special normal was set by set_effective_normal(), false otherwise.
const LVector3 & get_effective_normal() const
Returns the normal that was set by set_effective_normal().
get_respect_effective_normal
See set_respect_effective_normal().
A spherical collision volume or object.
A class to retrieve the individual data elements previously stored in a Datagram.
An ordered list of data elements, formatted in memory for transmission over a socket or writing to a ...
Definition datagram.h:38
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
Defines a series of line strips.
Defines a series of triangle fans.
Definition geomTrifans.h:23
This defines the actual numeric vertex data stored in a Geom, in the structure defined by a particula...
static const GeomVertexFormat * get_v3()
Returns a standard vertex format with just a 3-component vertex position.
This object provides a high-level interface for quickly writing a sequence of numeric values from a v...
A container for geometry primitives.
Definition geom.h:54
A lightweight class that represents a single element that may be timed and/or counted via stats.
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.
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.
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.
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.
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.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.