Panda3D
collisionVisualizer.cxx
1 // Filename: collisionVisualizer.cxx
2 // Created by: drose (16Apr03)
3 //
4 ////////////////////////////////////////////////////////////////////
5 //
6 // PANDA 3D SOFTWARE
7 // Copyright (c) Carnegie Mellon University. All rights reserved.
8 //
9 // All use of this software is subject to the terms of the revised BSD
10 // license. You should have received a copy of this license along
11 // with this source code in a file named "LICENSE."
12 //
13 ////////////////////////////////////////////////////////////////////
14 
15 #include "collisionVisualizer.h"
16 #include "collisionEntry.h"
17 #include "cullTraverser.h"
18 #include "cullTraverserData.h"
19 #include "cullableObject.h"
20 #include "cullHandler.h"
21 #include "renderState.h"
22 #include "renderModeAttrib.h"
23 #include "geomVertexData.h"
24 #include "geomVertexFormat.h"
25 #include "geomVertexArrayFormat.h"
26 #include "geom.h"
27 #include "geomPoints.h"
28 #include "geomLines.h"
29 #include "omniBoundingVolume.h"
30 #include "depthOffsetAttrib.h"
31 #include "colorScaleAttrib.h"
32 #include "transparencyAttrib.h"
33 #include "clipPlaneAttrib.h"
34 #include "geomVertexWriter.h"
35 
36 
37 #ifdef DO_COLLISION_RECORDING
38 
39 TypeHandle CollisionVisualizer::_type_handle;
40 
41 ////////////////////////////////////////////////////////////////////
42 // Function: CollisionVisualizer::Constructor
43 // Access: Published
44 // Description:
45 ////////////////////////////////////////////////////////////////////
46 CollisionVisualizer::
47 CollisionVisualizer(const string &name) : PandaNode(name) {
48  set_cull_callback();
49 
50  // We always want to render the CollisionVisualizer node itself
51  // (even if it doesn't appear to have any geometry within it).
52  set_internal_bounds(new OmniBoundingVolume());
53  _point_scale = 1.0f;
54  _normal_scale = 1.0f;
55 }
56 
57 ////////////////////////////////////////////////////////////////////
58 // Function: CollisionVisualizer::Destructor
59 // Access: Published, Virtual
60 // Description:
61 ////////////////////////////////////////////////////////////////////
62 CollisionVisualizer::
63 ~CollisionVisualizer() {
64 }
65 
66 ////////////////////////////////////////////////////////////////////
67 // Function: CollisionVisualizer::clear
68 // Access: Published
69 // Description: Removes all the visualization data from a previous
70 // traversal and resets the visualizer to empty.
71 ////////////////////////////////////////////////////////////////////
72 void CollisionVisualizer::
73 clear() {
74  _data.clear();
75 }
76 
77 ////////////////////////////////////////////////////////////////////
78 // Function: CollisionVisualizer::make_copy
79 // Access: Public, Virtual
80 // Description: Returns a newly-allocated Node that is a shallow copy
81 // of this one. It will be a different Node pointer,
82 // but its internal data may or may not be shared with
83 // that of the original Node.
84 ////////////////////////////////////////////////////////////////////
85 PandaNode *CollisionVisualizer::
86 make_copy() const {
87  return new CollisionVisualizer(*this);
88 }
89 
90 ////////////////////////////////////////////////////////////////////
91 // Function: CollisionVisualizer::cull_callback
92 // Access: Public, Virtual
93 // Description: This function will be called during the cull
94 // traversal to perform any additional operations that
95 // should be performed at cull time. This may include
96 // additional manipulation of render state or additional
97 // visible/invisible decisions, or any other arbitrary
98 // operation.
99 //
100 // Note that this function will *not* be called unless
101 // set_cull_callback() is called in the constructor of
102 // the derived class. It is necessary to call
103 // set_cull_callback() to indicated that we require
104 // cull_callback() to be called.
105 //
106 // By the time this function is called, the node has
107 // already passed the bounding-volume test for the
108 // viewing frustum, and the node's transform and state
109 // have already been applied to the indicated
110 // CullTraverserData object.
111 //
112 // The return value is true if this node should be
113 // visible, or false if it should be culled.
114 ////////////////////////////////////////////////////////////////////
115 bool CollisionVisualizer::
116 cull_callback(CullTraverser *trav, CullTraverserData &data) {
117  // Now we go through and actually draw our visualized collision solids.
118 
119  Data::const_iterator di;
120  for (di = _data.begin(); di != _data.end(); ++di) {
121  const TransformState *net_transform = (*di).first;
122  const VizInfo &viz_info = (*di).second;
123 
124  CullTraverserData xform_data(data);
125 
126  // We don't want to inherit the transform from above! We ignore
127  // whatever transforms were above the CollisionVisualizer node; it
128  // always renders its objects according to their appropriate net
129  // transform.
130  xform_data._net_transform = TransformState::make_identity();
131  xform_data._view_frustum = trav->get_view_frustum();
132  xform_data.apply_transform_and_state(trav, net_transform,
133  RenderState::make_empty(),
134  RenderEffects::make_empty(),
135  ClipPlaneAttrib::make());
136 
137  // Draw all the collision solids.
138  Solids::const_iterator si;
139  for (si = viz_info._solids.begin(); si != viz_info._solids.end(); ++si) {
140  // Note that we don't preserve the clip plane attribute from the
141  // collision solid. We always draw the whole polygon (or
142  // whatever) in the CollisionVisualizer. This is a deliberate
143  // decision; clipping the polygons may obscure many collision
144  // tests that are being made.
145  const CollisionSolid *solid = (*si).first;
146  const SolidInfo &solid_info = (*si).second;
147  bool was_detected = (solid_info._detected_count > 0);
148  PT(PandaNode) node = solid->get_viz(trav, xform_data, !was_detected);
149  if (node != (PandaNode *)NULL) {
150  CullTraverserData next_data(xform_data, node);
151 
152  // We don't want to inherit the render state from above for
153  // these guys.
154  next_data._state = get_viz_state();
155  trav->traverse(next_data);
156  }
157  }
158 
159  // Now draw all of the detected points.
160  if (!viz_info._points.empty()) {
161  CPT(RenderState) empty_state = RenderState::make_empty();
162  CPT(RenderState) point_state = RenderState::make(RenderModeAttrib::make(RenderModeAttrib::M_unchanged, 1.0f, false));
163 
164  PT(GeomVertexArrayFormat) point_array_format =
165  new GeomVertexArrayFormat(InternalName::get_vertex(), 3,
166  Geom::NT_stdfloat, Geom::C_point,
167  InternalName::get_color(), 1,
168  Geom::NT_packed_dabc, Geom::C_color,
169  InternalName::get_size(), 1,
170  Geom::NT_stdfloat, Geom::C_other);
171  CPT(GeomVertexFormat) point_format =
172  GeomVertexFormat::register_format(point_array_format);
173 
174  Points::const_iterator pi;
175  for (pi = viz_info._points.begin(); pi != viz_info._points.end(); ++pi) {
176  const CollisionPoint &point = (*pi);
177 
178  // Draw a small red point at the surface point, and a smaller
179  // white point at the interior point.
180  {
181  PT(GeomVertexData) point_vdata =
182  new GeomVertexData("viz", point_format, Geom::UH_stream);
183 
184  PT(GeomPoints) points = new GeomPoints(Geom::UH_stream);
185 
186  GeomVertexWriter vertex(point_vdata, InternalName::get_vertex());
187  GeomVertexWriter color(point_vdata, InternalName::get_color());
188  GeomVertexWriter size(point_vdata, InternalName::get_size());
189 
190  vertex.add_data3(point._surface_point);
191  color.add_data4(1.0f, 0.0f, 0.0f, 1.0f);
192  size.add_data1f(16.0f * _point_scale);
193  points->add_next_vertices(1);
194  points->close_primitive();
195 
196  if (point._interior_point != point._surface_point) {
197  vertex.add_data3(point._interior_point);
198  color.add_data4(1.0f, 1.0f, 1.0f, 1.0f);
199  size.add_data1f(8.0f * _point_scale);
200  points->add_next_vertices(1);
201  points->close_primitive();
202  }
203 
204  PT(Geom) geom = new Geom(point_vdata);
205  geom->add_primitive(points);
206 
207  CullableObject *object =
208  new CullableObject(geom, point_state,
209  xform_data.get_internal_transform(trav));
210 
211  trav->get_cull_handler()->record_object(object, trav);
212  }
213 
214  // Draw the normal vector at the surface point.
215  if (!point._surface_normal.almost_equal(LVector3::zero())) {
216  PT(GeomVertexData) line_vdata =
217  new GeomVertexData("viz", GeomVertexFormat::get_v3cp(),
218  Geom::UH_stream);
219 
220  PT(GeomLines) lines = new GeomLines(Geom::UH_stream);
221 
222  GeomVertexWriter vertex(line_vdata, InternalName::get_vertex());
223  GeomVertexWriter color(line_vdata, InternalName::get_color());
224 
225  vertex.add_data3(point._surface_point);
226  vertex.add_data3(point._surface_point +
227  point._surface_normal * _normal_scale);
228  color.add_data4(1.0f, 0.0f, 0.0f, 1.0f);
229  color.add_data4(1.0f, 1.0f, 1.0f, 1.0f);
230  lines->add_next_vertices(2);
231  lines->close_primitive();
232 
233  PT(Geom) geom = new Geom(line_vdata);
234  geom->add_primitive(lines);
235 
236  CullableObject *object =
237  new CullableObject(geom, empty_state,
238  xform_data.get_internal_transform(trav));
239 
240  trav->get_cull_handler()->record_object(object, trav);
241  }
242  }
243  }
244  }
245 
246  // Now carry on to render our child nodes.
247  return true;
248 }
249 
250 ////////////////////////////////////////////////////////////////////
251 // Function: CollisionVisualizer::is_renderable
252 // Access: Public, Virtual
253 // Description: Returns true if there is some value to visiting this
254 // particular node during the cull traversal for any
255 // camera, false otherwise. This will be used to
256 // optimize the result of get_net_draw_show_mask(), so
257 // that any subtrees that contain only nodes for which
258 // is_renderable() is false need not be visited.
259 ////////////////////////////////////////////////////////////////////
260 bool CollisionVisualizer::
261 is_renderable() const {
262  return true;
263 }
264 
265 
266 ////////////////////////////////////////////////////////////////////
267 // Function: CollisionVisualizer::output
268 // Access: Public, Virtual
269 // Description: Writes a brief description of the node to the
270 // indicated output stream. This is invoked by the <<
271 // operator. It may be overridden in derived classes to
272 // include some information relevant to the class.
273 ////////////////////////////////////////////////////////////////////
274 void CollisionVisualizer::
275 output(ostream &out) const {
276  PandaNode::output(out);
277  out << " ";
278  CollisionRecorder::output(out);
279 }
280 
281 ////////////////////////////////////////////////////////////////////
282 // Function: CollisionVisualizer::begin_traversal
283 // Access: Public, Virtual
284 // Description: This method is called at the beginning of a
285 // CollisionTraverser::traverse() call. It is provided
286 // as a hook for the derived class to reset its state as
287 // appropriate.
288 ////////////////////////////////////////////////////////////////////
289 void CollisionVisualizer::
290 begin_traversal() {
291  CollisionRecorder::begin_traversal();
292  _data.clear();
293 }
294 
295 ////////////////////////////////////////////////////////////////////
296 // Function: CollisionVisualizer::collision_tested
297 // Access: Public, Virtual
298 // Description: This method is called when a pair of collision solids
299 // have passed all bounding-volume tests and have been
300 // tested for a collision. The detected value is set
301 // true if a collision was detected, false otherwise.
302 ////////////////////////////////////////////////////////////////////
303 void CollisionVisualizer::
304 collision_tested(const CollisionEntry &entry, bool detected) {
305  CollisionRecorder::collision_tested(entry, detected);
306 
307  NodePath node_path = entry.get_into_node_path();
308  CPT(TransformState) net_transform = node_path.get_net_transform();
309  const CollisionSolid *solid = entry.get_into();
310  nassertv(solid != (CollisionSolid *)NULL);
311 
312  VizInfo &viz_info = _data[net_transform];
313  if (detected) {
314  viz_info._solids[solid]._detected_count++;
315 
316  if (entry.has_surface_point()) {
317  CollisionPoint p;
318  entry.get_all(entry.get_into_node_path(),
319  p._surface_point, p._surface_normal, p._interior_point);
320  viz_info._points.push_back(p);
321  }
322 
323  } else {
324  viz_info._solids[solid]._missed_count++;
325  }
326 }
327 
328 
329 ////////////////////////////////////////////////////////////////////
330 // Function: CollisionVisualizer::get_viz_state
331 // Access: Private
332 // Description: Returns a RenderState suitable for rendering the
333 // collision solids with which a collision was detected.
334 ////////////////////////////////////////////////////////////////////
335 CPT(RenderState) CollisionVisualizer::
336 get_viz_state() {
337  // Once someone asks for this pointer, we hold its reference count
338  // and never free it.
339  static CPT(RenderState) state = (const RenderState *)NULL;
340  if (state == (const RenderState *)NULL) {
341  state = RenderState::make
342  (DepthOffsetAttrib::make());
343  }
344 
345  return state;
346 }
347 
348 #endif // DO_COLLISION_RECORDING
bool has_surface_point() const
Returns true if the surface point has been specified, false otherwise.
bool get_all(const NodePath &space, LPoint3 &surface_point, LVector3 &surface_normal, LPoint3 &interior_point) const
Simultaneously transforms the surface point, surface normal, and interior point of the collision into...
A basic node of the scene graph or data graph.
Definition: pandaNode.h:72
This object provides a high-level interface for quickly writing a sequence of numeric values from a v...
CullHandler * get_cull_handler() const
Returns the object that will receive the culled Geoms.
NodePath get_into_node_path() const
Returns the NodePath that represents the specific CollisionNode or GeomNode instance that was collide...
Defines a series of disconnected points.
Definition: geomPoints.h:25
The abstract base class for all things that can collide with other things in the world, and all the things they can collide with (except geometry).
static const LVector3f & zero()
Returns a zero-length vector.
Definition: lvector3.h:270
This collects together the pieces of data that are accumulated for each node while walking the scene ...
void traverse(const NodePath &root)
Begins the traversal from the indicated node.
const CollisionSolid * get_into() const
Returns the CollisionSolid pointer for the particular solid was collided into.
The smallest atom of cull.
virtual void record_object(CullableObject *object, const CullTraverser *traverser)
This callback function is intended to be overridden by a derived class.
Definition: cullHandler.cxx:52
Defines a single collision event.
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:58
This represents a unique collection of RenderAttrib objects that correspond to a particular renderabl...
Definition: renderState.h:53
Defines a series of disconnected line segments.
Definition: geomLines.h:25
GeometricBoundingVolume * get_view_frustum() const
Returns the bounding volume that corresponds to the view frustum, or NULL if the view frustum is not ...
This is a special kind of GeometricBoundingVolume that fills all of space.
TypeHandle is the identifier used to differentiate C++ class types.
Definition: typeHandle.h:85
NodePath is the fundamental system for disambiguating instances, and also provides a higher-level int...
Definition: nodePath.h:165
This object performs a depth-first traversal of the scene graph, with optional view-frustum culling...
Definition: cullTraverser.h:48