Panda3D
 All Classes Functions Variables Enumerations
collisionHandlerEvent.cxx
1 // Filename: collisionHandlerEvent.cxx
2 // Created by: drose (16Mar02)
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 
16 #include "collisionHandlerEvent.h"
17 #include "config_collide.h"
18 
19 #include "eventParameter.h"
20 #include "throw_event.h"
21 
22 
23 TypeHandle CollisionHandlerEvent::_type_handle;
24 
25 ////////////////////////////////////////////////////////////////////
26 // Function: CollisionHandlerEvent::Constructor
27 // Access: Public
28 // Description: The default CollisionHandlerEvent will throw no
29 // events. Its pattern strings must first be set via a
30 // call to add_in_pattern() and/or add_out_pattern().
31 ////////////////////////////////////////////////////////////////////
34 }
35 
36 ////////////////////////////////////////////////////////////////////
37 // Function: CollisionHandlerEvent::begin_group
38 // Access: Public, Virtual
39 // Description: Will be called by the CollisionTraverser before a new
40 // traversal is begun. It instructs the handler to
41 // reset itself in preparation for a number of
42 // CollisionEntries to be sent.
43 ////////////////////////////////////////////////////////////////////
46  if (collide_cat.is_spam()) {
47  collide_cat.spam()
48  << "begin_group.\n";
49  }
50  _last_colliding.swap(_current_colliding);
51  _current_colliding.clear();
52 }
53 
54 ////////////////////////////////////////////////////////////////////
55 // Function: CollisionHandlerEvent::add_entry
56 // Access: Public, Virtual
57 // Description: Called between a begin_group() .. end_group()
58 // sequence for each collision that is detected.
59 ////////////////////////////////////////////////////////////////////
62  nassertv(entry != (CollisionEntry *)NULL);
63 
64  // Record this particular entry for later. This will keep track of
65  // all the unique pairs of node/node intersections.
66  bool inserted = _current_colliding.insert(entry).second;
67 
68  if (collide_cat.is_spam()) {
69  collide_cat.spam()
70  << "Detected collision from " << entry->get_from_node_path()
71  << " to " << entry->get_into_node_path()
72  << ", inserted = " << inserted << "\n";
73  }
74 }
75 
76 ////////////////////////////////////////////////////////////////////
77 // Function: CollisionHandlerEvent::end_group
78 // Access: Public, Virtual
79 // Description: Called by the CollisionTraverser at the completion of
80 // all collision detections for this traversal. It
81 // should do whatever finalization is required for the
82 // handler.
83 ////////////////////////////////////////////////////////////////////
86  // Now compare the list of entries we collected this frame with
87  // those we kept from the last time. Each new entry represents a
88  // new 'in' event; each missing entry represents a new 'out' event.
89 
90  if (collide_cat.is_spam()) {
91  collide_cat.spam()
92  << "end_group.\n"
93  << "current_colliding has " << _current_colliding.size()
94  << " entries, last_colliding has " << _last_colliding.size()
95  << "\n";
96  }
97 
98  Colliding::iterator ca, cb;
99 
100  ca = _current_colliding.begin();
101  cb = _last_colliding.begin();
102 
103  SortEntries order;
104  while (ca != _current_colliding.end() && cb != _last_colliding.end()) {
105  if (order(*ca, *cb)) {
106  // Here's an element in a but not in b. That's a newly entered
107  // intersection.
108  throw_event_for(_in_patterns, *ca);
109  ++ca;
110  } else if (order(*cb, *ca)) {
111  // Here's an element in b but not in a. That's a newly exited
112  // intersection.
113  throw_event_for(_out_patterns, *cb);
114  ++cb;
115  } else {
116  // This element is in both b and a. It hasn't changed.
117  throw_event_for(_again_patterns, *cb);
118  ++ca;
119  ++cb;
120  }
121  }
122 
123  while (ca != _current_colliding.end()) {
124  // Here's an element in a but not in b. That's a newly entered
125  // intersection.
126  throw_event_for(_in_patterns, *ca);
127  ++ca;
128  }
129 
130  while (cb != _last_colliding.end()) {
131  // Here's an element in b but not in a. That's a newly exited
132  // intersection.
133  throw_event_for(_out_patterns, *cb);
134  ++cb;
135  }
136 
137  return true;
138 }
139 
140 ////////////////////////////////////////////////////////////////////
141 // Function: CollisionHandlerEvent::clear
142 // Access: Public
143 // Description: Empties the list of elements that all colliders are
144 // known to be colliding with. No "out" events will be
145 // thrown; if the same collision is detected next frame,
146 // a new "in" event will be thrown for each collision.
147 //
148 // This can be called each frame to defeat the
149 // persistent "in" event mechanism, which prevents the
150 // same "in" event from being thrown repeatedly.
151 // However, also see add_again_pattern(), which can be
152 // used to set the event that is thrown when a collision
153 // is detected for two or more consecutive frames.
154 ////////////////////////////////////////////////////////////////////
156 clear() {
157  _last_colliding.clear();
158  _current_colliding.clear();
159 }
160 
161 ////////////////////////////////////////////////////////////////////
162 // Function: CollisionHandlerEvent::flush
163 // Access: Public
164 // Description: Same as clear() except "out" events are thrown.
165 ////////////////////////////////////////////////////////////////////
167 flush() {
168  begin_group();
169  end_group();
170 }
171 
172 ////////////////////////////////////////////////////////////////////
173 // Function: CollisionHandlerEvent::throw_event_for
174 // Access: Private
175 // Description: Throws whatever events are suggested by the list of
176 // patterns.
177 ////////////////////////////////////////////////////////////////////
178 void CollisionHandlerEvent::
179 throw_event_for(const vector_string &patterns, CollisionEntry *entry) {
180  vector_string::const_iterator pi;
181  for (pi = patterns.begin(); pi != patterns.end(); ++pi) {
182  throw_event_pattern(*pi, entry);
183  }
184 }
185 
186 ////////////////////////////////////////////////////////////////////
187 // Function: CollisionHandlerEvent::throw_event_pattern
188 // Access: Private
189 // Description: Throws an event matching the indicated pattern.
190 ////////////////////////////////////////////////////////////////////
191 void CollisionHandlerEvent::
192 throw_event_pattern(const string &pattern, CollisionEntry *entry) {
193  if (pattern.empty()) {
194  return;
195  }
196 
197  string event;
198  for (size_t p = 0; p < pattern.size(); ++p) {
199  if (pattern[p] == '%') {
200  string key;
201  if (p + 1 < pattern.size() && pattern[p + 1] == '(') {
202  // Extract out the key--the name up until the closing paren.
203  size_t close = pattern.find(')', p + 2);
204  if (close != string::npos) {
205  key = pattern.substr(p + 2, close - (p + 2));
206  p = close;
207  }
208  }
209 
210  // Get out the command--the two characters following the percent
211  // sign (or the key).
212  string cmd = pattern.substr(p + 1, 2);
213  p += 2;
214  if (cmd == "fn") {
215  event += entry->get_from_node()->get_name();
216 
217  } else if (cmd == "in") {
218  if (entry->has_into()) {
219  event += entry->get_into_node()->get_name();
220  }
221 
222  } else if (cmd == "fs") {
223  event +=
224  (!entry->get_from()->is_tangible() ? 'i' : 't');
225 
226  } else if (cmd == "is") {
227  event +=
228  (entry->has_into() && !entry->get_into()->is_tangible() ? 'i' : 't');
229 
230  } else if (cmd == "ig") {
231  event +=
232  (entry->has_into() ? 'c' : 'g');
233 
234  } else if (cmd == "fh") {
235  if (!entry->get_from_node_path().has_net_tag(key)) {
236  return;
237  }
238 
239  } else if (cmd == "fx") {
240  if (entry->get_from_node_path().has_net_tag(key)) {
241  return;
242  }
243 
244  } else if (cmd == "ih") {
245  if (!(entry->has_into() && entry->get_into_node_path().has_net_tag(key))) {
246  return;
247  }
248 
249  } else if (cmd == "ix") {
250  if (entry->has_into() && entry->get_into_node_path().has_net_tag(key)) {
251  return;
252  }
253 
254  } else if (cmd == "ft") {
255  event += entry->get_from_node_path().get_net_tag(key);
256 
257  } else if (cmd == "it") {
258  if (entry->has_into()) {
259  event += entry->get_into_node_path().get_net_tag(key);
260  }
261 
262  } else {
263  collide_cat.error()
264  << "Invalid symbol in event_pattern: %" << cmd << "\n";
265  }
266  } else {
267  event += pattern[p];
268  }
269  }
270 
271  if (!event.empty()) {
272  throw_event(event, EventParameter(entry));
273  }
274 }
virtual bool end_group()
Called by the CollisionTraverser at the completion of all collision detections for this traversal...
CollisionNode * get_from_node() const
Returns the node that contains the CollisionSolid that triggered this collision.
string get_net_tag(const string &key) const
Returns the tag value that has been defined on this node, or the nearest ancestor node...
Definition: nodePath.I:2533
An optional parameter associated with an event.
void flush()
Same as clear() except &quot;out&quot; events are thrown.
virtual void begin_group()
Will be called by the CollisionTraverser before a new traversal is begun.
const CollisionSolid * get_from() const
Returns the CollisionSolid pointer for the particular solid that triggered this collision.
void clear()
Empties the list of elements that all colliders are known to be colliding with.
bool has_net_tag(const string &key) const
Returns true if the indicated tag value has been defined on this node or on any ancestor node...
Definition: nodePath.I:2545
Defines a single collision event.
const CollisionSolid * get_into() const
Returns the CollisionSolid pointer for the particular solid was collided into.
PandaNode * get_into_node() const
Returns the node that contains the CollisionSolid that was collided into.
bool is_tangible() const
Returns whether the solid is considered &#39;tangible&#39; or not.
CollisionHandlerEvent()
The default CollisionHandlerEvent will throw no events.
NodePath get_into_node_path() const
Returns the NodePath that represents the specific CollisionNode or GeomNode instance that was collide...
NodePath get_from_node_path() const
Returns the NodePath that represents the CollisionNode that contains the CollisionSolid that triggere...
TypeHandle is the identifier used to differentiate C++ class types.
Definition: typeHandle.h:85
virtual void add_entry(CollisionEntry *entry)
Called between a begin_group() .
bool has_into() const
Returns true if the &quot;into&quot; solid is, in fact, a CollisionSolid, and its pointer is known (in which ca...
static int size()
Returns 3: the number of components of a LVecBase3.
Definition: lvecBase3.h:452