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_of_type(CollisionPolygon::get_class_type()) &&
177  s2->is_of_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 }
CollisionEntry::get_from_node_path
get_from_node_path
Returns the NodePath that represents the CollisionNode that contains the CollisionSolid that triggere...
Definition: collisionEntry.h:101
CollisionEntry
Defines a single collision event.
Definition: collisionEntry.h:42
dcast.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
collisionHandlerPusher.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
CollisionPolygon::get_collision_origin
virtual LPoint3 get_collision_origin() const
Returns the point in space deemed to be the "origin" of the solid for collision purposes.
Definition: collisionPolygon.cxx:219
CollisionEntry::get_into_node_path
get_into_node_path
Returns the NodePath that represents the specific CollisionNode or GeomNode instance that was collide...
Definition: collisionEntry.h:102
collisionNode.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
TypeHandle
TypeHandle is the identifier used to differentiate C++ class types.
Definition: typeHandle.h:81
CollisionPolygon
Definition: collisionPolygon.h:29
TransformState
Indicates a coordinate-system transform on vertices.
Definition: transformState.h:54
NodePath::find
NodePath find(const std::string &path) const
Searches for a node below the referenced node that matches the indicated string.
Definition: nodePath.cxx:314
CollisionSolid
The abstract base class for all things that can collide with other things in the world,...
Definition: collisionSolid.h:45
collisionEntry.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
NodePath
NodePath is the fundamental system for disambiguating instances, and also provides a higher-level int...
Definition: nodePath.h:159
epvector.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
CollisionEntry::get_all
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...
Definition: collisionEntry.cxx:120
collisionPolygon.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
config_collide.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
TypedObject::is_of_type
bool is_of_type(TypeHandle handle) const
Returns true if the current object is or derives from the indicated type.
Definition: typedObject.I:28