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  * Called by the parent class after all collisions have been detected, this
54  * manages the various collisions and moves around the nodes as necessary.
55  *
56  * The return value is normally true, but it may be false to indicate the
57  * CollisionTraverser should disable this handler from being called in the
58  * future.
59  */
60 bool CollisionHandlerPusher::
61 handle_entries() {
62  bool okflag = true;
63 
64  FromEntries::const_iterator fi;
65  for (fi = _from_entries.begin(); fi != _from_entries.end(); ++fi) {
66  const NodePath &from_node_path = (*fi).first;
67  const Entries &entries = (*fi).second;
68 
69  Colliders::iterator ci;
70  ci = _colliders.find(from_node_path);
71  if (ci == _colliders.end()) {
72  // Hmm, someone added a CollisionNode to a traverser and gave it this
73  // CollisionHandler pointer--but they didn't tell us about the node.
74  collide_cat.error()
75  << "CollisionHandlerPusher doesn't know about "
76  << from_node_path << ", disabling.\n";
77  okflag = false;
78  } else {
79  ColliderDef &def = (*ci).second;
80  {
81  // How to apply multiple shoves from different solids onto the same
82  // collider? One's first intuition is to vector sum all the shoves.
83  // However, this causes problems when two parallel walls shove on the
84  // collider, because we end up with a double shove. We hack around
85  // this by testing if two shove vectors share nearly the same
86  // direction, and if so, we keep only the longer of the two.
87 
88  typedef epvector<ShoveData> Shoves;
89  Shoves shoves;
90 
91  Entries::const_iterator ei;
92  for (ei = entries.begin(); ei != entries.end(); ++ei) {
93  CollisionEntry *entry = (*ei);
94  nassertr(entry != nullptr, false);
95  nassertr(from_node_path == entry->get_from_node_path(), false);
96 
97  LPoint3 surface_point;
98  LVector3 normal;
99  LPoint3 interior_point;
100 
101  if (!entry->get_all(def._target, surface_point, normal, interior_point)) {
102 #ifndef NDEBUG
103  if (collide_cat.is_debug()) {
104  collide_cat.debug()
105  << "Cannot shove on " << from_node_path << " for collision into "
106  << entry->get_into_node_path() << "; no normal/depth information.\n";
107  }
108 #endif
109  } else {
110  // Shove it just enough to clear the volume.
111  if (!surface_point.almost_equal(interior_point)) {
112  if (_horizontal) {
113  normal[2] = 0.0f;
114  }
115  // Just to be on the safe size, we normalize the normal vector,
116  // even though it really ought to be unit-length already (unless
117  // we just forced it horizontal, above).
118  normal.normalize();
119 
120  ShoveData sd;
121  sd._vector = normal;
122  sd._length = (surface_point - interior_point).length();
123  sd._valid = true;
124  sd._entry = entry;
125 
126  #ifndef NDEBUG
127  if (collide_cat.is_debug()) {
128  collide_cat.debug()
129  << "Shove on " << from_node_path << " from "
130  << entry->get_into_node_path() << ": " << sd._vector
131  << " times " << sd._length << "\n";
132  }
133  #endif
134 
135  shoves.push_back(sd);
136  }
137  }
138  }
139 
140  if (!shoves.empty()) {
141  // Now we look for two shoves that are largely in the same
142  // direction, so we can combine them into a single shove of the same
143  // magnitude; we also check for two shoves at 90 degrees, so we can
144  // detect whether we are hitting an inner or an outer corner.
145 
146  Shoves::iterator si;
147  for (si = shoves.begin(); si != shoves.end(); ++si) {
148  ShoveData &sd = (*si);
149  Shoves::iterator sj;
150  for (sj = shoves.begin(); sj != si; ++sj) {
151  ShoveData &sd2 = (*sj);
152  if (sd2._valid) {
153  PN_stdfloat d = sd._vector.dot(sd2._vector);
154  if (collide_cat.is_debug()) {
155  collide_cat.debug()
156  << "Considering dot product " << d << "\n";
157  }
158 
159  if (d > 0.9) {
160  // These two shoves are largely in the same direction; save
161  // the larger of the two.
162  if (sd2._length < sd._length) {
163  sd2._valid = false;
164  } else {
165  sd._valid = false;
166  }
167  } else {
168  // These two shoves are not in the same direction. If they
169  // are both from polygons that are a child of the same node,
170  // try to determine the shape of the corner (convex or
171  // concave).
172  const CollisionSolid *s1 = sd._entry->get_into();
173  const CollisionSolid *s2 = sd2._entry->get_into();
174  if (s1 != nullptr &&
175  s2 != nullptr &&
176  s1->is_exact_type(CollisionPolygon::get_class_type()) &&
177  s2->is_exact_type(CollisionPolygon::get_class_type()) &&
178  sd._entry->get_into_node_path() ==
179  sd2._entry->get_into_node_path()) {
180  const CollisionPolygon *p1 = DCAST(CollisionPolygon, s1);
181  const CollisionPolygon *p2 = DCAST(CollisionPolygon, s2);
182  if (p1->dist_to_plane(p2->get_collision_origin()) < 0 &&
183  p2->dist_to_plane(p1->get_collision_origin()) < 0) {
184  // Each polygon is behind the other one. That means we
185  // have a convex corner, and therefore we should discard
186  // one of the shoves (or the user will get stuck coming
187  // at a convex corner).
188  if (collide_cat.is_debug()) {
189  collide_cat.debug()
190  << "Discarding shove from convex corner.\n";
191  }
192 
193  // This time, unlike the case of two parallel walls
194  // above, we discard the larger of the two shoves, not
195  // the smaller. This is because as we slide off the
196  // convex corner, the wall we are sliding away from will
197  // get a bigger and bigger shove--and we need to keep
198  // ignoring the same wall as we slide.
199  if (sd2._length < sd._length) {
200  sd._valid = false;
201  } else {
202  sd2._valid = false;
203  }
204  }
205  }
206  }
207  }
208  }
209  }
210 
211  // Now we can determine the net shove.
212  LVector3 net_shove(0.0f, 0.0f, 0.0f);
213  LVector3 force_normal(0.0f, 0.0f, 0.0f);
214  for (si = shoves.begin(); si != shoves.end(); ++si) {
215  const ShoveData &sd = (*si);
216  if (sd._valid) {
217  net_shove += sd._vector * sd._length;
218  force_normal += sd._vector;
219  }
220  }
221 
222  #ifndef NDEBUG
223  if (collide_cat.is_debug()) {
224  collide_cat.debug()
225  << "Net shove on " << from_node_path << " is: "
226  << net_shove << "\n";
227  }
228  #endif
229 
230  // This is the part where the node actually gets moved:
231  CPT(TransformState) trans = def._target.get_transform();
232  LVecBase3 pos = trans->get_pos();
233  pos += net_shove * trans->get_mat();
234  def._target.set_transform(trans->set_pos(pos));
235  def.updated_transform();
236 
237  // We call this to allow derived classes to do other fix-ups as they
238  // see fit:
239  apply_net_shove(def, net_shove, force_normal);
240  apply_linear_force(def, force_normal);
241  }
242  }
243  }
244  }
245 
246  return okflag;
247 }
248 
249 /**
250  * This is an optional hook for derived classes to do some work with the
251  * ColliderDef and the force vector.
252  */
253 void CollisionHandlerPusher::
254 apply_net_shove(ColliderDef &def, const LVector3 &net_shove,
255  const LVector3 &force_normal) {
256 }
257 
258 /**
259  * This is an optional hook for derived classes to do some work with the
260  * ColliderDef and the force vector.
261  */
262 void CollisionHandlerPusher::
263 apply_linear_force(ColliderDef &def, const LVector3 &force_normal) {
264 }
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...
get_from_node_path
Returns the NodePath that represents the CollisionNode that contains the CollisionSolid that triggere...
NodePath find(const std::string &path) const
Searches for a node below the referenced node that matches the indicated string.
Definition: nodePath.cxx:314
bool is_exact_type(TypeHandle handle) const
Returns true if the current object is the indicated type exactly.
Definition: typedObject.I:38
Indicates a coordinate-system transform on vertices.
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,...
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
get_into_node_path
Returns the NodePath that represents the specific CollisionNode or GeomNode instance that was collide...
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
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.
TypeHandle is the identifier used to differentiate C++ class types.
Definition: typeHandle.h:81
NodePath is the fundamental system for disambiguating instances, and also provides a higher-level int...
Definition: nodePath.h:161