Panda3D
collisionHandlerPusher.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 collisionHandlerPusher.cxx
10  * @author drose
11  * @date 2002-03-16
12  */
13 
14 #include "collisionHandlerPusher.h"
15 #include "collisionNode.h"
16 #include "collisionEntry.h"
17 #include "collisionPolygon.h"
18 #include "config_collide.h"
19 #include "dcast.h"
20 #include "epvector.h"
21 
22 TypeHandle CollisionHandlerPusher::_type_handle;
23 
24 /**
25  * The ShoveData class is used within
26  * CollisionHandlerPusher::handle_entries(), to track multiple shoves onto a
27  * given collider. It's not exported outside this file.
28  */
29 class ShoveData {
30 public:
31  LVector3 _vector;
32  PN_stdfloat _length;
33  bool _valid;
34  CollisionEntry *_entry;
35 };
36 
37 /**
38  *
39  */
40 CollisionHandlerPusher::
41 CollisionHandlerPusher() {
42  _horizontal = pushers_horizontal;
43 }
44 
45 /**
46  *
47  */
48 CollisionHandlerPusher::
49 ~CollisionHandlerPusher() {
50 }
51 
52 /**
53  * Serializes this object, to implement pickle support.
54  */
56 write_datagram(Datagram &dg) const {
58 
59  dg.add_bool(_horizontal);
60 }
61 
62 /**
63  * Restores the object state from the given datagram, previously obtained using
64  * __getstate__.
65  */
69 
70  _horizontal = scan.get_bool();
71 }
72 
73 /**
74  * Called by the parent class after all collisions have been detected, this
75  * manages the various collisions and moves around the nodes as necessary.
76  *
77  * The return value is normally true, but it may be false to indicate the
78  * CollisionTraverser should disable this handler from being called in the
79  * future.
80  */
81 bool CollisionHandlerPusher::
82 handle_entries() {
83  bool okflag = true;
84 
85  FromEntries::const_iterator fi;
86  for (fi = _from_entries.begin(); fi != _from_entries.end(); ++fi) {
87  const NodePath &from_node_path = (*fi).first;
88  const Entries &entries = (*fi).second;
89 
90  Colliders::iterator ci;
91  ci = _colliders.find(from_node_path);
92  if (ci == _colliders.end()) {
93  // Hmm, someone added a CollisionNode to a traverser and gave it this
94  // CollisionHandler pointer--but they didn't tell us about the node.
95  collide_cat.error()
96  << "CollisionHandlerPusher doesn't know about "
97  << from_node_path << ", disabling.\n";
98  okflag = false;
99  } else {
100  ColliderDef &def = (*ci).second;
101  {
102  // How to apply multiple shoves from different solids onto the same
103  // collider? One's first intuition is to vector sum all the shoves.
104  // However, this causes problems when two parallel walls shove on the
105  // collider, because we end up with a double shove. We hack around
106  // this by testing if two shove vectors share nearly the same
107  // direction, and if so, we keep only the longer of the two.
108 
109  typedef epvector<ShoveData> Shoves;
110  Shoves shoves;
111 
112  Entries::const_iterator ei;
113  for (ei = entries.begin(); ei != entries.end(); ++ei) {
114  CollisionEntry *entry = (*ei);
115  nassertr(entry != nullptr, false);
116  nassertr(from_node_path == entry->get_from_node_path(), false);
117 
118  LPoint3 surface_point;
119  LVector3 normal;
120  LPoint3 interior_point;
121 
122  if (!entry->get_all(def._target, surface_point, normal, interior_point)) {
123 #ifndef NDEBUG
124  if (collide_cat.is_debug()) {
125  collide_cat.debug()
126  << "Cannot shove on " << from_node_path << " for collision into "
127  << entry->get_into_node_path() << "; no normal/depth information.\n";
128  }
129 #endif
130  } else {
131  // Shove it just enough to clear the volume.
132  if (!surface_point.almost_equal(interior_point)) {
133  if (_horizontal) {
134  normal[2] = 0.0f;
135  }
136  // Just to be on the safe size, we normalize the normal vector,
137  // even though it really ought to be unit-length already (unless
138  // we just forced it horizontal, above).
139  normal.normalize();
140 
141  ShoveData sd;
142  sd._vector = normal;
143  sd._length = (surface_point - interior_point).length();
144  sd._valid = true;
145  sd._entry = entry;
146 
147  #ifndef NDEBUG
148  if (collide_cat.is_debug()) {
149  collide_cat.debug()
150  << "Shove on " << from_node_path << " from "
151  << entry->get_into_node_path() << ": " << sd._vector
152  << " times " << sd._length << "\n";
153  }
154  #endif
155 
156  shoves.push_back(sd);
157  }
158  }
159  }
160 
161  if (!shoves.empty()) {
162  // Now we look for two shoves that are largely in the same
163  // direction, so we can combine them into a single shove of the same
164  // magnitude; we also check for two shoves at 90 degrees, so we can
165  // detect whether we are hitting an inner or an outer corner.
166 
167  Shoves::iterator si;
168  for (si = shoves.begin(); si != shoves.end(); ++si) {
169  ShoveData &sd = (*si);
170  Shoves::iterator sj;
171  for (sj = shoves.begin(); sj != si; ++sj) {
172  ShoveData &sd2 = (*sj);
173  if (sd2._valid) {
174  PN_stdfloat d = sd._vector.dot(sd2._vector);
175  if (collide_cat.is_debug()) {
176  collide_cat.debug()
177  << "Considering dot product " << d << "\n";
178  }
179 
180  if (d > 0.9) {
181  // These two shoves are largely in the same direction; save
182  // the larger of the two.
183  if (sd2._length < sd._length) {
184  sd2._valid = false;
185  } else {
186  sd._valid = false;
187  }
188  } else {
189  // These two shoves are not in the same direction. If they
190  // are both from polygons that are a child of the same node,
191  // try to determine the shape of the corner (convex or
192  // concave).
193  const CollisionSolid *s1 = sd._entry->get_into();
194  const CollisionSolid *s2 = sd2._entry->get_into();
195  if (s1 != nullptr &&
196  s2 != nullptr &&
197  s1->is_of_type(CollisionPolygon::get_class_type()) &&
198  s2->is_of_type(CollisionPolygon::get_class_type()) &&
199  sd._entry->get_into_node_path() ==
200  sd2._entry->get_into_node_path()) {
201  const CollisionPolygon *p1 = DCAST(CollisionPolygon, s1);
202  const CollisionPolygon *p2 = DCAST(CollisionPolygon, s2);
203  if (p1->dist_to_plane(p2->get_collision_origin()) < 0 &&
204  p2->dist_to_plane(p1->get_collision_origin()) < 0) {
205  // Each polygon is behind the other one. That means we
206  // have a convex corner, and therefore we should discard
207  // one of the shoves (or the user will get stuck coming
208  // at a convex corner).
209  if (collide_cat.is_debug()) {
210  collide_cat.debug()
211  << "Discarding shove from convex corner.\n";
212  }
213 
214  // This time, unlike the case of two parallel walls
215  // above, we discard the larger of the two shoves, not
216  // the smaller. This is because as we slide off the
217  // convex corner, the wall we are sliding away from will
218  // get a bigger and bigger shove--and we need to keep
219  // ignoring the same wall as we slide.
220  if (sd2._length < sd._length) {
221  sd._valid = false;
222  } else {
223  sd2._valid = false;
224  }
225  }
226  }
227  }
228  }
229  }
230  }
231 
232  // Now we can determine the net shove.
233  LVector3 net_shove(0.0f, 0.0f, 0.0f);
234  LVector3 force_normal(0.0f, 0.0f, 0.0f);
235  for (si = shoves.begin(); si != shoves.end(); ++si) {
236  const ShoveData &sd = (*si);
237  if (sd._valid) {
238  net_shove += sd._vector * sd._length;
239  force_normal += sd._vector;
240  }
241  }
242 
243  #ifndef NDEBUG
244  if (collide_cat.is_debug()) {
245  collide_cat.debug()
246  << "Net shove on " << from_node_path << " is: "
247  << net_shove << "\n";
248  }
249  #endif
250 
251  // This is the part where the node actually gets moved:
252  CPT(TransformState) trans = def._target.get_transform();
253  LVecBase3 pos = trans->get_pos();
254  pos += net_shove * trans->get_mat();
255  def._target.set_transform(trans->set_pos(pos));
256  def.updated_transform();
257 
258  // We call this to allow derived classes to do other fix-ups as they
259  // see fit:
260  apply_net_shove(def, net_shove, force_normal);
261  apply_linear_force(def, force_normal);
262  }
263  }
264  }
265  }
266 
267  return okflag;
268 }
269 
270 /**
271  * This is an optional hook for derived classes to do some work with the
272  * ColliderDef and the force vector.
273  */
274 void CollisionHandlerPusher::
275 apply_net_shove(ColliderDef &def, const LVector3 &net_shove,
276  const LVector3 &force_normal) {
277 }
278 
279 /**
280  * This is an optional hook for derived classes to do some work with the
281  * ColliderDef and the force vector.
282  */
283 void CollisionHandlerPusher::
284 apply_linear_force(ColliderDef &def, const LVector3 &force_normal) {
285 }
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...
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...
void read_datagram(DatagramIterator &source)
Restores the object state from the given datagram, previously obtained using __getstate__.
void write_datagram(Datagram &destination) const
Serializes this object, to implement pickle support.
void write_datagram(Datagram &destination) const
Serializes this object, to implement pickle support.
void read_datagram(DatagramIterator &source)
Restores the object state from the given datagram, previously obtained using __getstate__.
virtual LPoint3 get_collision_origin() const
Returns the point in space deemed to be the "origin" of the solid for collision purposes.
The abstract base class for all things that can collide with other things in the world,...
A class to retrieve the individual data elements previously stored in a Datagram.
bool get_bool()
Extracts a boolean value.
An ordered list of data elements, formatted in memory for transmission over a socket or writing to a ...
Definition: datagram.h:38
void add_bool(bool value)
Adds a boolean value to the datagram.
Definition: datagram.I:34
NodePath is the fundamental system for disambiguating instances, and also provides a higher-level int...
Definition: nodePath.h:159
NodePath find(const std::string &path) const
Searches for a node below the referenced node that matches the indicated string.
Definition: nodePath.cxx:316
Indicates a coordinate-system transform on vertices.
TypeHandle is the identifier used to differentiate C++ class types.
Definition: typeHandle.h:81
bool is_of_type(TypeHandle handle) const
Returns true if the current object is or derives from the indicated type.
Definition: typedObject.I:28
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.