Panda3D
collisionSolid.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 collisionSolid.cxx
10  * @author drose
11  * @date 2000-04-24
12  */
13 
14 #include "collisionSolid.h"
15 #include "config_collide.h"
16 #include "collisionSphere.h"
17 #include "collisionLine.h"
18 #include "collisionRay.h"
19 #include "collisionSegment.h"
20 #include "collisionCapsule.h"
21 #include "collisionParabola.h"
22 #include "collisionBox.h"
23 #include "collisionEntry.h"
24 #include "boundingSphere.h"
25 #include "datagram.h"
26 #include "datagramIterator.h"
27 #include "bamReader.h"
28 #include "bamWriter.h"
29 #include "indent.h"
30 #include "cullFaceAttrib.h"
31 #include "colorAttrib.h"
32 #include "renderModeAttrib.h"
33 #include "transparencyAttrib.h"
34 #include "geomNode.h"
35 
36 PStatCollector CollisionSolid::_volume_pcollector(
37  "Collision Volumes:CollisionSolid");
38 PStatCollector CollisionSolid::_test_pcollector(
39  "Collision Tests:CollisionSolid");
40 TypeHandle CollisionSolid::_type_handle;
41 
42 /**
43  *
44  */
45 CollisionSolid::
46 CollisionSolid() : _lock("CollisionSolid") {
47  _flags = F_viz_geom_stale | F_tangible | F_internal_bounds_stale;
48 }
49 
50 /**
51  *
52  */
53 CollisionSolid::
54 CollisionSolid(const CollisionSolid &copy) :
55  CopyOnWriteObject(copy),
56  _effective_normal(copy._effective_normal),
57  _internal_bounds(copy._internal_bounds),
58  _flags(copy._flags),
59  _lock("CollisionSolid")
60 {
61  _flags |= F_viz_geom_stale;
62 }
63 
64 /**
65  *
66  */
67 CollisionSolid::
68 ~CollisionSolid() {
69 }
70 
71 /**
72  * Required to implement CopyOnWriteObject.
73  */
74 PT(CopyOnWriteObject) CollisionSolid::
75 make_cow_copy() {
76  return make_copy();
77 }
78 
79 /**
80  * Returns the solid's bounding volume.
81  */
82 CPT(BoundingVolume) CollisionSolid::
83 get_bounds() const {
84  LightMutexHolder holder(_lock);
85  if (_flags & F_internal_bounds_stale) {
86  ((CollisionSolid *)this)->_internal_bounds = compute_internal_bounds();
87  ((CollisionSolid *)this)->_flags &= ~F_internal_bounds_stale;
88  }
89  return _internal_bounds;
90 }
91 
92 /**
93  * Returns the solid's bounding volume.
94  */
95 void CollisionSolid::
96 set_bounds(const BoundingVolume &bounding_volume) {
97  LightMutexHolder holder(_lock);
98  ((CollisionSolid *)this)->_internal_bounds = bounding_volume.make_copy();
99  ((CollisionSolid *)this)->_flags &= ~F_internal_bounds_stale;
100 }
101 
102 /**
103  * Tests for a collision between this object (which is also the "from" object
104  * in the entry) and the "into" object. If a collision is detected, returns a
105  * new CollisionEntry object that records the collision; otherwise, returns
106  * NULL.
107  */
108 PT(CollisionEntry) CollisionSolid::
109 test_intersection(const CollisionEntry &) const {
110  report_undefined_from_intersection(get_type());
111  return nullptr;
112 }
113 
114 /**
115  * Transforms the solid by the indicated matrix.
116  */
117 void CollisionSolid::
118 xform(const LMatrix4 &mat) {
119  LightMutexHolder holder(_lock);
120  if ((_flags & F_effective_normal) != 0) {
121  _effective_normal = _effective_normal * mat;
122  _effective_normal.normalize();
123  }
124 
125  _flags |= F_viz_geom_stale | F_internal_bounds_stale;
126 }
127 
128 /**
129  * Returns a GeomNode that may be rendered to visualize the CollisionSolid.
130  * This is used during the cull traversal to render the CollisionNodes that
131  * have been made visible.
132  */
133 PT(PandaNode) CollisionSolid::
134 get_viz(const CullTraverser *, const CullTraverserData &, bool bounds_only) const {
135  LightMutexHolder holder(_lock);
136  if ((_flags & F_viz_geom_stale) != 0) {
137  if (_viz_geom == nullptr) {
138  ((CollisionSolid *)this)->_viz_geom = new GeomNode("viz");
139  ((CollisionSolid *)this)->_bounds_viz_geom = new GeomNode("bounds_viz");
140  } else {
141  _viz_geom->remove_all_geoms();
142  _bounds_viz_geom->remove_all_geoms();
143  }
144  ((CollisionSolid *)this)->fill_viz_geom();
145  ((CollisionSolid *)this)->_flags &= ~F_viz_geom_stale;
146  }
147 
148  if (bounds_only) {
149  return _bounds_viz_geom;
150  } else {
151  return _viz_geom;
152  }
153 }
154 
155 /**
156  * Returns a PStatCollector that is used to count the number of bounding
157  * volume tests made against a solid of this type in a given frame.
158  */
159 PStatCollector &CollisionSolid::
160 get_volume_pcollector() {
161  return _volume_pcollector;
162 }
163 
164 /**
165  * Returns a PStatCollector that is used to count the number of intersection
166  * tests made against a solid of this type in a given frame.
167  */
170  return _test_pcollector;
171 }
172 
173 /**
174  *
175  */
176 void CollisionSolid::
177 output(std::ostream &out) const {
178  out << get_type();
179 }
180 
181 /**
182  *
183  */
184 void CollisionSolid::
185 write(std::ostream &out, int indent_level) const {
186  indent(out, indent_level) << (*this) << "\n";
187 }
188 
189 /**
190  *
191  */
192 PT(BoundingVolume) CollisionSolid::
193 compute_internal_bounds() const {
194  return new BoundingSphere;
195 }
196 
197 /**
198  * This is part of the double-dispatch implementation of test_intersection().
199  * It is called when the "from" object is a sphere.
200  */
201 PT(CollisionEntry) CollisionSolid::
202 test_intersection_from_sphere(const CollisionEntry &) const {
203  report_undefined_intersection_test(CollisionSphere::get_class_type(),
204  get_type());
205  return nullptr;
206 }
207 
208 /**
209  * This is part of the double-dispatch implementation of test_intersection().
210  * It is called when the "from" object is a line.
211  */
212 PT(CollisionEntry) CollisionSolid::
213 test_intersection_from_line(const CollisionEntry &) const {
214  report_undefined_intersection_test(CollisionLine::get_class_type(),
215  get_type());
216  return nullptr;
217 }
218 
219 /**
220  * This is part of the double-dispatch implementation of test_intersection().
221  * It is called when the "from" object is a ray.
222  */
223 PT(CollisionEntry) CollisionSolid::
224 test_intersection_from_ray(const CollisionEntry &) const {
225  report_undefined_intersection_test(CollisionRay::get_class_type(),
226  get_type());
227  return nullptr;
228 }
229 
230 /**
231  * This is part of the double-dispatch implementation of test_intersection().
232  * It is called when the "from" object is a segment.
233  */
234 PT(CollisionEntry) CollisionSolid::
235 test_intersection_from_segment(const CollisionEntry &) const {
236  report_undefined_intersection_test(CollisionSegment::get_class_type(),
237  get_type());
238  return nullptr;
239 }
240 
241 /**
242  * This is part of the double-dispatch implementation of test_intersection().
243  * It is called when the "from" object is a capsule.
244  */
245 PT(CollisionEntry) CollisionSolid::
246 test_intersection_from_capsule(const CollisionEntry &) const {
247  report_undefined_intersection_test(CollisionCapsule::get_class_type(),
248  get_type());
249  return nullptr;
250 }
251 
252 /**
253  * This is part of the double-dispatch implementation of test_intersection().
254  * It is called when the "from" object is a parabola.
255  */
256 PT(CollisionEntry) CollisionSolid::
257 test_intersection_from_parabola(const CollisionEntry &) const {
258  report_undefined_intersection_test(CollisionParabola::get_class_type(),
259  get_type());
260  return nullptr;
261 }
262 
263 /**
264  * This is part of the double-dispatch implementation of test_intersection().
265  * It is called when the "from" object is a box.
266  */
267 PT(CollisionEntry) CollisionSolid::
268 test_intersection_from_box(const CollisionEntry &) const {
269  report_undefined_intersection_test(CollisionBox::get_class_type(),
270  get_type());
271  return nullptr;
272 }
273 
274 
275 #ifndef NDEBUG
276 class CollisionSolidUndefinedPair {
277 public:
278  CollisionSolidUndefinedPair(TypeHandle a, TypeHandle b) :
279  _a(a), _b(b)
280  {}
281  bool operator < (const CollisionSolidUndefinedPair &other) const {
282  if (_a != other._a) {
283  return _a < other._a;
284  }
285  return _b < other._b;
286  }
287 
288  TypeHandle _a;
289  TypeHandle _b;
290 };
291 #endif // NDEBUG
292 
293 /**
294  * Outputs a message the first time an intersection test is attempted that
295  * isn't defined, and explains a bit about what it means.
296  */
297 void CollisionSolid::
298 report_undefined_intersection_test(TypeHandle from_type, TypeHandle into_type) {
299 #ifndef NDEBUG
300  typedef pset<CollisionSolidUndefinedPair> Reported;
301  static Reported reported;
302 
303  if (reported.insert(CollisionSolidUndefinedPair(from_type, into_type)).second) {
304  collide_cat.error()
305  << "Invalid attempt to detect collision from " << from_type << " into "
306  << into_type << "!\n\n"
307 
308  "This means that a " << from_type << " object attempted to test for an\n"
309  "intersection into a " << into_type << " object. This intersection\n"
310  "test has not yet been defined; it is possible the " << into_type << "\n"
311  "object is not intended to be collidable. Consider calling\n"
312  "set_into_collide_mask(0) on the " << into_type << " object, or\n"
313  "set_from_collide_mask(0) on the " << from_type << " object.\n\n";
314  }
315 #endif // NDEBUG
316 }
317 
318 /**
319  * Outputs a message the first time an intersection test is attempted that
320  * isn't defined, and explains a bit about what it means.
321  */
322 void CollisionSolid::
323 report_undefined_from_intersection(TypeHandle from_type) {
324 #ifndef NDEBUG
325  typedef pset<TypeHandle> Reported;
326  static Reported reported;
327 
328  if (reported.insert(from_type).second) {
329  collide_cat.error()
330  << "Invalid attempt to detect collision from " << from_type << "!\n\n"
331 
332  "This means that a " << from_type << " object was added to a\n"
333  "CollisionTraverser as if it were a colliding object. However,\n"
334  "no implementation for this kind of object has yet been defined\n"
335  "to collide with other objects.\n\n";
336  }
337 #endif // NDEBUG
338 }
339 
340 /**
341  * Function to write the important information in the particular object to a
342  * Datagram
343  */
344 void CollisionSolid::
346  // For now, we need only 8 bits of flags. If we need to expand this later,
347  // we will have to increase the bam version.
348  LightMutexHolder holder(_lock);
349  me.add_uint8(_flags);
350  if ((_flags & F_effective_normal) != 0) {
351  _effective_normal.write_datagram(me);
352  }
353 }
354 
355 /**
356  * Function that reads out of the datagram (or asks manager to read) all of
357  * the data that is needed to re-create this object and stores it in the
358  * appropiate place
359  */
360 void CollisionSolid::
361 fillin(DatagramIterator &scan, BamReader *manager) {
362  _flags = scan.get_uint8();
363  if ((_flags & F_effective_normal) != 0) {
364  _effective_normal.read_datagram(scan);
365  }
366 
367  // The viz is always stale after reading from a bam file. So is the
368  // bounding volume.
369  _flags |= F_viz_geom_stale | F_internal_bounds_stale;
370 }
371 
372 
373 /**
374  * Fills the _viz_geom GeomNode up with Geoms suitable for rendering this
375  * solid.
376  */
377 void CollisionSolid::
378 fill_viz_geom() {
379 }
380 
381 /**
382  * Returns a RenderState for rendering collision visualizations in solid.
383  * This automatically returns the appropriate state according to the setting
384  * of _tangible.
385  *
386  * Assumes the lock is already held.
387  */
388 CPT(RenderState) CollisionSolid::
389 get_solid_viz_state() {
390  // Once someone asks for this pointer, we hold its reference count and never
391  // free it.
392  static CPT(RenderState) base_state = nullptr;
393  if (base_state == nullptr) {
394  base_state = RenderState::make
395  (CullFaceAttrib::make(CullFaceAttrib::M_cull_clockwise),
396  RenderModeAttrib::make(RenderModeAttrib::M_filled),
397  TransparencyAttrib::make(TransparencyAttrib::M_alpha));
398  }
399 
400  if (!do_is_tangible()) {
401  static CPT(RenderState) intangible_state = nullptr;
402  if (intangible_state == nullptr) {
403  intangible_state = base_state->add_attrib
404  (ColorAttrib::make_flat(LColor(1.0f, 0.3, 0.5f, 0.5f)));
405  }
406  return intangible_state;
407 
408  } else if (do_has_effective_normal()) {
409  static CPT(RenderState) fakenormal_state = nullptr;
410  if (fakenormal_state == nullptr) {
411  fakenormal_state = base_state->add_attrib
412  (ColorAttrib::make_flat(LColor(0.5f, 0.5f, 1.0f, 0.5f)));
413  }
414  return fakenormal_state;
415 
416  } else {
417  static CPT(RenderState) tangible_state = nullptr;
418  if (tangible_state == nullptr) {
419  tangible_state = base_state->add_attrib
420  (ColorAttrib::make_flat(LColor(1.0f, 1.0f, 1.0f, 0.5f)));
421  }
422  return tangible_state;
423  }
424 }
425 
426 
427 /**
428  * Returns a RenderState for rendering collision visualizations in wireframe.
429  * This automatically returns the appropriate state according to the setting
430  * of _tangible.
431  *
432  * Assumes the lock is already held.
433  */
434 CPT(RenderState) CollisionSolid::
435 get_wireframe_viz_state() {
436  // Once someone asks for this pointer, we hold its reference count and never
437  // free it.
438  static CPT(RenderState) base_state = nullptr;
439  if (base_state == nullptr) {
440  base_state = RenderState::make
441  (CullFaceAttrib::make(CullFaceAttrib::M_cull_none),
442  RenderModeAttrib::make(RenderModeAttrib::M_wireframe),
443  TransparencyAttrib::make(TransparencyAttrib::M_none));
444  }
445 
446  if (!do_is_tangible()) {
447  static CPT(RenderState) intangible_state = nullptr;
448  if (intangible_state == nullptr) {
449  intangible_state = base_state->add_attrib
450  (ColorAttrib::make_flat(LColor(1.0f, 1.0f, 0.0f, 1.0f)));
451  }
452  return intangible_state;
453 
454  } else if (do_has_effective_normal()) {
455  static CPT(RenderState) fakenormal_state = nullptr;
456  if (fakenormal_state == nullptr) {
457  fakenormal_state = base_state->add_attrib
458  (ColorAttrib::make_flat(LColor(0.0f, 0.0f, 1.0f, 1.0f)));
459  }
460  return fakenormal_state;
461 
462  } else {
463  static CPT(RenderState) tangible_state = nullptr;
464  if (tangible_state == nullptr) {
465  tangible_state = base_state->add_attrib
466  (ColorAttrib::make_flat(LColor(0.0f, 0.0f, 1.0f, 1.0f)));
467  }
468  return tangible_state;
469  }
470 }
471 
472 
473 /**
474  * Returns a RenderState for rendering collision visualizations for things
475  * that are neither solid nor exactly wireframe, like rays and segments.
476  *
477  * Assumes the lock is already held.
478  */
479 CPT(RenderState) CollisionSolid::
480 get_other_viz_state() {
481  // Once someone asks for this pointer, we hold its reference count and never
482  // free it.
483  static CPT(RenderState) base_state = nullptr;
484  if (base_state == nullptr) {
485  base_state = RenderState::make
486  (CullFaceAttrib::make(CullFaceAttrib::M_cull_clockwise),
487  RenderModeAttrib::make(RenderModeAttrib::M_filled),
488  TransparencyAttrib::make(TransparencyAttrib::M_alpha));
489  }
490 
491  // We don't bother to make a distinction here between tangible and
492  // intangible.
493  return base_state;
494 }
495 
496 /**
497  * Returns a RenderState for rendering collision visualizations in solid.
498  * This automatically returns the appropriate state according to the setting
499  * of _tangible.
500  *
501  * Assumes the lock is already held.
502  */
503 CPT(RenderState) CollisionSolid::
504 get_solid_bounds_viz_state() {
505  // Once someone asks for this pointer, we hold its reference count and never
506  // free it.
507  static CPT(RenderState) base_state = nullptr;
508  if (base_state == nullptr) {
509  base_state = RenderState::make
510  (CullFaceAttrib::make(CullFaceAttrib::M_cull_clockwise),
511  RenderModeAttrib::make(RenderModeAttrib::M_filled),
512  TransparencyAttrib::make(TransparencyAttrib::M_alpha));
513  }
514 
515  if (!do_is_tangible()) {
516  static CPT(RenderState) intangible_state = nullptr;
517  if (intangible_state == nullptr) {
518  intangible_state = base_state->add_attrib
519  (ColorAttrib::make_flat(LColor(1.0f, 1.0f, 0.5f, 0.3)));
520  }
521  return intangible_state;
522 
523  } else if (do_has_effective_normal()) {
524  static CPT(RenderState) fakenormal_state = nullptr;
525  if (fakenormal_state == nullptr) {
526  fakenormal_state = base_state->add_attrib
527  (ColorAttrib::make_flat(LColor(0.5f, 0.5f, 1.0f, 0.3)));
528  }
529  return fakenormal_state;
530 
531  } else {
532  static CPT(RenderState) tangible_state = nullptr;
533  if (tangible_state == nullptr) {
534  tangible_state = base_state->add_attrib
535  (ColorAttrib::make_flat(LColor(1.0f, 1.0f, 0.5f, 0.3)));
536  }
537  return tangible_state;
538  }
539 }
540 
541 
542 /**
543  * Returns a RenderState for rendering collision visualizations in wireframe.
544  * This automatically returns the appropriate state according to the setting
545  * of _tangible.
546  *
547  * Assumes the lock is already held.
548  */
549 CPT(RenderState) CollisionSolid::
550 get_wireframe_bounds_viz_state() {
551  // Once someone asks for this pointer, we hold its reference count and never
552  // free it.
553  static CPT(RenderState) base_state = nullptr;
554  if (base_state == nullptr) {
555  base_state = RenderState::make
556  (CullFaceAttrib::make(CullFaceAttrib::M_cull_none),
557  RenderModeAttrib::make(RenderModeAttrib::M_wireframe),
558  TransparencyAttrib::make(TransparencyAttrib::M_none),
559  ColorAttrib::make_flat(LColor(1.0f, 0.0f, 0.0f, 1.0f)));
560  }
561 
562  return base_state;
563 }
564 
565 
566 /**
567  * Returns a RenderState for rendering collision visualizations for things
568  * that are neither solid nor exactly wireframe, like rays and segments.
569  *
570  * Assumes the lock is already held.
571  */
572 CPT(RenderState) CollisionSolid::
573 get_other_bounds_viz_state() {
574  // Once someone asks for this pointer, we hold its reference count and never
575  // free it.
576  static CPT(RenderState) base_state = nullptr;
577  if (base_state == nullptr) {
578  base_state = RenderState::make
579  (CullFaceAttrib::make(CullFaceAttrib::M_cull_clockwise),
580  RenderModeAttrib::make(RenderModeAttrib::M_filled),
581  TransparencyAttrib::make(TransparencyAttrib::M_alpha));
582  }
583 
584  // We don't bother to make a distinction here between tangible and
585  // intangible.
586  return base_state;
587 }
A basic node of the scene graph or data graph.
Definition: pandaNode.h:64
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
uint8_t get_uint8()
Extracts an unsigned 8-bit integer.
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
The abstract base class for all things that can collide with other things in the world,...
This defines a bounding sphere, consisting of a center and a radius.
This collects together the pieces of data that are accumulated for each node while walking the scene ...
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
This is the fundamental interface for writing binary objects to a Bam file, to be extracted later by ...
Definition: bamWriter.h:63
virtual void write_datagram(BamWriter *manager, Datagram &me)
Function to write the important information in the particular object to a Datagram.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
This is an abstract class for any volume in any sense which can be said to define the locality of ref...
A lightweight class that represents a single element that may be timed and/or counted via stats.
CPT(BoundingVolume) CollisionSolid
Returns the solid's bounding volume.
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
Similar to MutexHolder, but for a light mutex.
Defines a single collision event.
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.
This represents a unique collection of RenderAttrib objects that correspond to a particular renderabl...
Definition: renderState.h:47
This base class provides basic reference counting, but also can be used with a CopyOnWritePointer to ...
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PT(CopyOnWriteObject) CollisionSolid
Required to implement CopyOnWriteObject.
virtual PStatCollector & get_test_pcollector()
Returns a PStatCollector that is used to count the number of intersection tests made against a solid ...
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
void add_uint8(uint8_t value)
Adds an unsigned 8-bit integer to the datagram.
Definition: datagram.I:50
This is our own Panda specialization on the default STL set.
Definition: pset.h:49
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
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
An ordered list of data elements, formatted in memory for transmission over a socket or writing to a ...
Definition: datagram.h:38
This object performs a depth-first traversal of the scene graph, with optional view-frustum culling,...
Definition: cullTraverser.h:45
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
A node that holds Geom objects, renderable pieces of geometry.
Definition: geomNode.h:34
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.