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