Panda3D
collisionHandlerEvent.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 collisionHandlerEvent.cxx
10  * @author drose
11  * @date 2002-03-16
12  */
13 
14 #include "collisionHandlerEvent.h"
15 #include "config_collide.h"
16 
17 #include "eventParameter.h"
18 #include "throw_event.h"
19 
20 using std::string;
21 
22 
23 TypeHandle CollisionHandlerEvent::_type_handle;
24 
25 /**
26  * The default CollisionHandlerEvent will throw no events. Its pattern
27  * strings must first be set via a call to add_in_pattern() and/or
28  * add_out_pattern().
29  */
32 }
33 
34 /**
35  * Will be called by the CollisionTraverser before a new traversal is begun.
36  * It instructs the handler to reset itself in preparation for a number of
37  * CollisionEntries to be sent.
38  */
40 begin_group() {
41  if (collide_cat.is_spam()) {
42  collide_cat.spam()
43  << "begin_group.\n";
44  }
45  _last_colliding.swap(_current_colliding);
46  _current_colliding.clear();
47 }
48 
49 /**
50  * Called between a begin_group() .. end_group() sequence for each collision
51  * that is detected.
52  */
54 add_entry(CollisionEntry *entry) {
55  nassertv(entry != nullptr);
56 
57  // Record this particular entry for later. This will keep track of all the
58  // unique pairs of nodenode intersections.
59  bool inserted = _current_colliding.insert(entry).second;
60 
61  if (collide_cat.is_spam()) {
62  collide_cat.spam()
63  << "Detected collision from " << entry->get_from_node_path()
64  << " to " << entry->get_into_node_path()
65  << ", inserted = " << inserted << "\n";
66  }
67 }
68 
69 /**
70  * Called by the CollisionTraverser at the completion of all collision
71  * detections for this traversal. It should do whatever finalization is
72  * required for the handler.
73  */
75 end_group() {
76  // Now compare the list of entries we collected this frame with those we
77  // kept from the last time. Each new entry represents a new 'in' event;
78  // each missing entry represents a new 'out' event.
79 
80  if (collide_cat.is_spam()) {
81  collide_cat.spam()
82  << "end_group.\n"
83  << "current_colliding has " << _current_colliding.size()
84  << " entries, last_colliding has " << _last_colliding.size()
85  << "\n";
86  }
87 
88  Colliding::iterator ca, cb;
89 
90  ca = _current_colliding.begin();
91  cb = _last_colliding.begin();
92 
93  SortEntries order;
94  while (ca != _current_colliding.end() && cb != _last_colliding.end()) {
95  if (order(*ca, *cb)) {
96  // Here's an element in a but not in b. That's a newly entered
97  // intersection.
98  throw_event_for(_in_patterns, *ca);
99  ++ca;
100  } else if (order(*cb, *ca)) {
101  // Here's an element in b but not in a. That's a newly exited
102  // intersection.
103  throw_event_for(_out_patterns, *cb);
104  ++cb;
105  } else {
106  // This element is in both b and a. It hasn't changed.
107  throw_event_for(_again_patterns, *cb);
108  ++ca;
109  ++cb;
110  }
111  }
112 
113  while (ca != _current_colliding.end()) {
114  // Here's an element in a but not in b. That's a newly entered
115  // intersection.
116  throw_event_for(_in_patterns, *ca);
117  ++ca;
118  }
119 
120  while (cb != _last_colliding.end()) {
121  // Here's an element in b but not in a. That's a newly exited
122  // intersection.
123  throw_event_for(_out_patterns, *cb);
124  ++cb;
125  }
126 
127  return true;
128 }
129 
130 /**
131  * Empties the list of elements that all colliders are known to be colliding
132  * with. No "out" events will be thrown; if the same collision is detected
133  * next frame, a new "in" event will be thrown for each collision.
134  *
135  * This can be called each frame to defeat the persistent "in" event
136  * mechanism, which prevents the same "in" event from being thrown repeatedly.
137  * However, also see add_again_pattern(), which can be used to set the event
138  * that is thrown when a collision is detected for two or more consecutive
139  * frames.
140  */
142 clear() {
143  _last_colliding.clear();
144  _current_colliding.clear();
145 }
146 
147 /**
148  * Same as clear() except "out" events are thrown.
149  */
151 flush() {
152  begin_group();
153  end_group();
154 }
155 
156 /**
157  * Serializes this object, to implement pickle support.
158  */
160 write_datagram(Datagram &dg) const {
161  dg.add_uint32(_in_patterns.size());
162  for (const std::string &pattern : _in_patterns) {
163  dg.add_string(pattern);
164  }
165 
166  dg.add_uint32(_again_patterns.size());
167  for (const std::string &pattern : _again_patterns) {
168  dg.add_string(pattern);
169  }
170 
171  dg.add_uint32(_out_patterns.size());
172  for (const std::string &pattern : _out_patterns) {
173  dg.add_string(pattern);
174  }
175 }
176 
177 /**
178  * Restores the object state from the given datagram, previously obtained using
179  * __getstate__.
180  */
183  _in_patterns.clear();
184  size_t num_in_patterns = scan.get_uint32();
185  for (size_t i = 0; i < num_in_patterns; ++i) {
186  add_in_pattern(scan.get_string());
187  }
188 
189  _again_patterns.clear();
190  size_t num_again_patterns = scan.get_uint32();
191  for (size_t i = 0; i < num_again_patterns; ++i) {
193  }
194 
195  _out_patterns.clear();
196  size_t num_out_patterns = scan.get_uint32();
197  for (size_t i = 0; i < num_out_patterns; ++i) {
198  add_out_pattern(scan.get_string());
199  }
200 }
201 
202 /**
203  * Throws whatever events are suggested by the list of patterns.
204  */
205 void CollisionHandlerEvent::
206 throw_event_for(const vector_string &patterns, CollisionEntry *entry) {
207  vector_string::const_iterator pi;
208  for (pi = patterns.begin(); pi != patterns.end(); ++pi) {
209  throw_event_pattern(*pi, entry);
210  }
211 }
212 
213 /**
214  * Throws an event matching the indicated pattern.
215  */
216 void CollisionHandlerEvent::
217 throw_event_pattern(const string &pattern, CollisionEntry *entry) {
218  if (pattern.empty()) {
219  return;
220  }
221 
222  string event;
223  for (size_t p = 0; p < pattern.size(); ++p) {
224  if (pattern[p] == '%') {
225  string key;
226  if (p + 1 < pattern.size() && pattern[p + 1] == '(') {
227  // Extract out the key--the name up until the closing paren.
228  size_t close = pattern.find(')', p + 2);
229  if (close != string::npos) {
230  key = pattern.substr(p + 2, close - (p + 2));
231  p = close;
232  }
233  }
234 
235  // Get out the command--the two characters following the percent sign
236  // (or the key).
237  string cmd = pattern.substr(p + 1, 2);
238  p += 2;
239  if (cmd == "fn") {
240  event += entry->get_from_node()->get_name();
241 
242  } else if (cmd == "in") {
243  if (entry->has_into()) {
244  event += entry->get_into_node()->get_name();
245  }
246 
247  } else if (cmd == "fs") {
248  event +=
249  (!entry->get_from()->is_tangible() ? 'i' : 't');
250 
251  } else if (cmd == "is") {
252  event +=
253  (entry->has_into() && !entry->get_into()->is_tangible() ? 'i' : 't');
254 
255  } else if (cmd == "ig") {
256  event +=
257  (entry->has_into() ? 'c' : 'g');
258 
259  } else if (cmd == "fh") {
260  if (!entry->get_from_node_path().has_net_tag(key)) {
261  return;
262  }
263 
264  } else if (cmd == "fx") {
265  if (entry->get_from_node_path().has_net_tag(key)) {
266  return;
267  }
268 
269  } else if (cmd == "ih") {
270  if (!(entry->has_into() && entry->get_into_node_path().has_net_tag(key))) {
271  return;
272  }
273 
274  } else if (cmd == "ix") {
275  if (entry->has_into() && entry->get_into_node_path().has_net_tag(key)) {
276  return;
277  }
278 
279  } else if (cmd == "ft") {
280  event += entry->get_from_node_path().get_net_tag(key);
281 
282  } else if (cmd == "it") {
283  if (entry->has_into()) {
284  event += entry->get_into_node_path().get_net_tag(key);
285  }
286 
287  } else {
288  collide_cat.error()
289  << "Invalid symbol in event_pattern: %" << cmd << "\n";
290  }
291  } else {
292  event += pattern[p];
293  }
294  }
295 
296  if (!event.empty()) {
297  throw_event(event, EventParameter(entry));
298  }
299 }
Defines a single collision event.
get_from_node
Returns the node that contains the CollisionSolid that triggered this collision.
get_into
Returns the CollisionSolid pointer for the particular solid was collided into.
bool has_into() const
Returns true if the "into" solid is, in fact, a CollisionSolid, and its pointer is known (in which ca...
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_into_node
Returns the node that contains the CollisionSolid that was collided into.
get_from
Returns the CollisionSolid pointer for the particular solid that triggered this collision.
void add_out_pattern(const std::string &out_pattern)
Adds the pattern string that indicates how the event names are generated when a collision between two...
void add_again_pattern(const std::string &again_pattern)
Adds the pattern string that indicates how the event names are generated when a collision between two...
virtual void begin_group()
Will be called by the CollisionTraverser before a new traversal is begun.
void add_in_pattern(const std::string &in_pattern)
Adds a pattern string to the list of events that will be generated in response to a collision.
void flush()
Same as clear() except "out" events are thrown.
CollisionHandlerEvent()
The default CollisionHandlerEvent will throw no events.
virtual void add_entry(CollisionEntry *entry)
Called between a begin_group() .
void read_datagram(DatagramIterator &source)
Restores the object state from the given datagram, previously obtained using __getstate__.
void clear()
Empties the list of elements that all colliders are known to be colliding with.
void write_datagram(Datagram &destination) const
Serializes this object, to implement pickle support.
virtual bool end_group()
Called by the CollisionTraverser at the completion of all collision detections for this traversal.
A class to retrieve the individual data elements previously stored in a Datagram.
uint32_t get_uint32()
Extracts an unsigned 32-bit integer.
std::string get_string()
Extracts a variable-length string.
An ordered list of data elements, formatted in memory for transmission over a socket or writing to a ...
Definition: datagram.h:38
void add_uint32(uint32_t value)
Adds an unsigned 32-bit integer to the datagram.
Definition: datagram.I:94
void add_string(const std::string &str)
Adds a variable-length string to the datagram.
Definition: datagram.I:219
An optional parameter associated with an event.
TypeHandle is the identifier used to differentiate C++ class types.
Definition: typeHandle.h:81
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.