Panda3D
 All Classes Functions Variables Enumerations
collisionHandlerGravity.cxx
1 // Filename: CollisionHandlerGravity.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 "collisionHandlerGravity.h"
16 #include "collisionNode.h"
17 #include "collisionEntry.h"
18 #include "config_collide.h"
19 #include "collisionPlane.h"
20 #include "clockObject.h"
21 
22 TypeHandle CollisionHandlerGravity::_type_handle;
23 
24 ////////////////////////////////////////////////////////////////////
25 // Function: CollisionHandlerGravity::Constructor
26 // Access: Public
27 // Description:
28 ////////////////////////////////////////////////////////////////////
29 CollisionHandlerGravity::
30 CollisionHandlerGravity() {
31  _offset = 0.0f;
32  _reach = 1.0f;
33  _airborne_height = 0.0f;
34  _impact_velocity = 0.0f;
35  _gravity = 32.174;
36  _current_velocity = 0.0f;
37  _max_velocity = 400.0f;
38  _contact_normal = LVector3::zero();
39  _legacy_mode = false;
40 }
41 
42 ////////////////////////////////////////////////////////////////////
43 // Function: CollisionHandlerGravity::Destructor
44 // Access: Public, Virtual
45 // Description:
46 ////////////////////////////////////////////////////////////////////
47 CollisionHandlerGravity::
48 ~CollisionHandlerGravity() {
49 }
50 
51 ////////////////////////////////////////////////////////////////////
52 // Function: CollisionHandlerGravity::set_highest_collision
53 // Access: Protected
54 // Description:
55 //
56 //
57 //
58 //
59 //
60 //
61 ////////////////////////////////////////////////////////////////////
62 #define OLD_COLLISION_HANDLER_GRAVITY 0
63 #if OLD_COLLISION_HANDLER_GRAVITY
64 PN_stdfloat CollisionHandlerGravity::
65 set_highest_collision(const NodePath &target_node_path, const NodePath &from_node_path, const Entries &entries) {
66  // Get the maximum height for all collisions with this node.
67  bool got_max = false;
68  PN_stdfloat max_height = 0.0f;
69  CollisionEntry *highest = NULL;
70 
71  Entries::const_iterator ei;
72  for (ei = entries.begin(); ei != entries.end(); ++ei) {
73  CollisionEntry *entry = (*ei);
74  nassertr(entry != (CollisionEntry *)NULL, 0.0f);
75  nassertr(from_node_path == entry->get_from_node_path(), 0.0f);
76 
77  if (entry->has_surface_point()) {
78  LPoint3 point = entry->get_surface_point(target_node_path);
79  if (collide_cat.is_debug()) {
80  collide_cat.debug()
81  << "Intersection point detected at " << point << "\n";
82  }
83 
84  PN_stdfloat height = point[2];
85  if (!got_max || height > max_height) {
86  got_max = true;
87  max_height = height;
88  highest = entry;
89  }
90  }
91  }
92  //#*#_has_contact = got_max;
93 
94  #if 0
95  cout<<"\ncolliding with:\n";
96  for (Colliding::const_iterator i = _current_colliding.begin(); i != _current_colliding.end(); ++i) {
97  (**i).write(cout, 2);
98  }
99  cout<<"\nhighest:\n";
100  highest->write(cout, 2);
101  cout<<endl;
102  #endif
103 
104  if (_legacy_mode) {
105  // We only collide with things we are impacting with.
106  // Remove the collisions:
107  _current_colliding.clear();
108  // Add only the one that we're impacting with:
109  add_entry(highest);
110  }
111 
112  return max_height;
113 }
114 #else
115 PN_stdfloat CollisionHandlerGravity::
116 set_highest_collision(const NodePath &target_node_path, const NodePath &from_node_path, const Entries &entries) {
117  // Get the maximum height for all collisions with this node.
118  // This is really the distance to-the-ground, so it will
119  // be negative when the avatar is above the ground.
120  // Larger values (less negative) are higher elevation (assuming
121  // the avatar is right-side-up (or the ray is plumb)).
122  bool got_max = false;
123  bool got_min = false;
124  PN_stdfloat max_height = 0.0f;
125  PN_stdfloat min_height = 0.0f;
126  CollisionEntry *highest = NULL;
127  CollisionEntry *lowest = NULL;
128 
129  pvector<PT(CollisionEntry)> valid_entries;
130 
131  Entries::const_iterator ei;
132  for (ei = entries.begin(); ei != entries.end(); ++ei) {
133  CollisionEntry *entry = (*ei);
134  nassertr(entry != (CollisionEntry *)NULL, 0.0f);
135  nassertr(from_node_path == entry->get_from_node_path(), 0.0f);
136 
137  if (entry->has_surface_point()) {
138  LPoint3 point = entry->get_surface_point(target_node_path);
139  if (collide_cat.is_debug()) {
140  collide_cat.debug()
141  << "Intersection point detected at " << point << "\n";
142  }
143  PN_stdfloat height = point[2];
144  if(height < _offset + _reach) {
145  valid_entries.push_back(entry);
146  if (!got_max || height > max_height) {
147  got_max = true;
148  max_height = height;
149  highest = entry;
150  }
151  }
152  if (!got_min || height < min_height) {
153  got_min = true;
154  min_height = height;
155  lowest = entry;
156  }
157  }
158  }
159  if (!got_max && got_min) {
160  // We've fallen through the world, but we're also under some walkable
161  // geometry.
162  // Move us up to the lowest surface:
163  got_max = true;
164  max_height = min_height;
165  highest = lowest;
166  valid_entries.push_back(lowest);
167  }
168  //#*#_has_contact = got_max;
169 
170  #if 0
171  cout<<"\ncolliding with:\n";
172  for (Colliding::const_iterator i = _current_colliding.begin(); i != _current_colliding.end(); ++i) {
173  (**i).write(cout, 2);
174  }
175  cout<<"\nhighest:\n";
176  highest->write(cout, 2);
177  cout<<endl;
178  #endif
179 
180  // We only collide with things we are impacting with.
181  // Remove the collisions:
182  _current_colliding.clear();
183  if (_legacy_mode) {
184  // Add only the one that we're impacting with:
185  add_entry(highest);
186  } else {
187  // Add all of them.
189  for (vi = valid_entries.begin(); vi != valid_entries.end(); ++vi) {
190  add_entry(*vi);
191  }
192  }
193 
194 
195  // Set the contact normal so that other code can make use of the
196  // surface slope:
197  if (highest->get_into()->is_of_type(CollisionPlane::get_class_type())) {
198  // This is asking: what is the normal of the plane that the avatar
199  // is colliding with relative to the avatar. A positive y valye means
200  // the avatar is facing downhill and a negative y value means the
201  // avatar is facing uphill.
202  //_contact_normal = DCAST(CollisionPlane, highest->get_into())->get_normal() * from_node_path.get_mat(highest->get_into_node_path());
203  //_contact_normal = DCAST(CollisionPlane, highest->get_into())->get_normal();
204  // This is asking: what is the normal of the avatar that the avatar
205  // is colliding with relative to the plane.
206  CPT(TransformState) transform = highest->get_into_node_path().get_transform(from_node_path);
207  _contact_normal = DCAST(CollisionPlane, highest->get_into())->get_normal() * transform->get_mat();
208  } else {
209  _contact_normal = highest->get_surface_normal(from_node_path);
210  }
211 
212  return max_height;
213 }
214 #endif
215 
216 ////////////////////////////////////////////////////////////////////
217 // Function: CollisionHandlerGravity::handle_entries
218 // Access: Protected, Virtual
219 // Description: Called by the parent class after all collisions have
220 // been detected, this manages the various collisions
221 // and moves around the nodes as necessary.
222 //
223 // The return value is normally true, but it may be
224 // false to indicate the CollisionTraverser should
225 // disable this handler from being called in the future.
226 ////////////////////////////////////////////////////////////////////
227 bool CollisionHandlerGravity::
228 handle_entries() {
229  bool okflag = true;
230 
231  FromEntries::const_iterator fi;
232  for (fi = _from_entries.begin(); fi != _from_entries.end(); ++fi) {
233  const NodePath &from_node_path = (*fi).first;
234  const Entries &entries = (*fi).second;
235 
236  Colliders::iterator ci;
237  ci = _colliders.find(from_node_path);
238  if (ci == _colliders.end()) {
239  // Hmm, someone added a CollisionNode to a traverser and gave
240  // it this CollisionHandler pointer--but they didn't tell us
241  // about the node.
242  collide_cat.error()
243  << get_type() << " doesn't know about "
244  << from_node_path << ", disabling.\n";
245  okflag = false;
246  } else {
247  ColliderDef &def = (*ci).second;
248  PN_stdfloat max_height = set_highest_collision(def._target, from_node_path, entries);
249 
250  // Now set our height accordingly.
251  #if OLD_COLLISION_HANDLER_GRAVITY
252  PN_stdfloat adjust = max_height + _offset;
253  #else
254  PN_stdfloat adjust = max_height + _offset;
255  #endif
256  if (_current_velocity > 0.0f || !IS_THRESHOLD_ZERO(adjust, 0.001)) {
257  if (collide_cat.is_debug()) {
258  collide_cat.debug()
259  << "Adjusting height by " << adjust << "\n";
260  }
261 
262  if (_current_velocity > 0.0f || adjust) {
263  // ...we have a vertical thrust,
264  // ...or the node is above the floor, so it is airborne.
265  PN_stdfloat dt = ClockObject::get_global_clock()->get_dt();
266  // Fyi, the sign of _gravity is reversed. I think it makes the get_*() set_*()
267  // more intuitive to do it this way.
268  PN_stdfloat gravity_adjust = _current_velocity * dt + 0.5 * -_gravity * dt * dt;
269  if (adjust > 0.0f) {
270  // ...the node is under the floor, so it has landed.
271  // Keep the adjust to bring us up to the ground and
272  // then add the gravity_adjust to get us airborne:
273  adjust += max((PN_stdfloat)0.0, gravity_adjust);
274  } else {
275  // ...the node is above the floor, so it is airborne.
276  adjust = max(adjust, gravity_adjust);
277  }
278  _current_velocity -= _gravity * dt;
279  // Record the airborne height in case someone else needs it:
280  _airborne_height = -(max_height + _offset) + adjust;
281  assert(_airborne_height >= -0.001f);
282  }
283 
284  if (_airborne_height < 0.001f && _current_velocity < 0.001f) {
285  // ...the node is under the floor, so it has landed.
286  _impact_velocity = _current_velocity;
287  // These values are used by is_on_ground().
288  _current_velocity = _airborne_height = 0.0f;
289  } else if (_legacy_mode) {
290  // ...we're airborne.
291  _current_colliding.clear();
292  }
293 
294  CPT(TransformState) trans = def._target.get_transform();
295  LVecBase3 pos = trans->get_pos();
296  pos[2] += adjust;
297  def._target.set_transform(trans->set_pos(pos));
298  def.updated_transform();
299 
300  apply_linear_force(def, LVector3(0.0f, 0.0f, adjust));
301  } else {
302  // _impact_velocity = _current_velocity;
303  _current_velocity = _airborne_height = 0.0f;
304  if (collide_cat.is_spam()) {
305  collide_cat.spam()
306  << "Leaving height unchanged.\n";
307  }
308  }
309  }
310  }
311 
312  return okflag;
313 }
314 
315 ////////////////////////////////////////////////////////////////////
316 // Function: CollisionHandlerGravity::apply_linear_force
317 // Access: Protected, Virtual
318 // Description:
319 ////////////////////////////////////////////////////////////////////
320 void CollisionHandlerGravity::
321 apply_linear_force(ColliderDef &def, const LVector3 &force) {
322 }
static ClockObject * get_global_clock()
Returns a pointer to the global ClockObject.
Definition: clockObject.I:271
This is the base class for all three-component vectors and points.
Definition: lvecBase3.h:105
bool is_of_type(TypeHandle handle) const
Returns true if the current object is or derives from the indicated type.
Definition: typedObject.I:63
static const LVector3f & zero()
Returns a zero-length vector.
Definition: lvector3.h:269
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
This is our own Panda specialization on the default STL vector.
Definition: pvector.h:39
double get_dt(Thread *current_thread=Thread::get_current_thread()) const
Returns the elapsed time for the previous frame: the number of seconds elapsed between the last two c...
Definition: clockObject.I:141
LPoint3 get_surface_point(const NodePath &space) const
Returns the point, on the surface of the &quot;into&quot; object, at which a collision is detected.
Defines a single collision event.
const CollisionSolid * get_into() const
Returns the CollisionSolid pointer for the particular solid was collided into.
bool has_surface_point() const
Returns true if the surface point has been specified, false otherwise.
virtual void add_entry(CollisionEntry *entry)
Called between a begin_group() .
LVector3 get_surface_normal(const NodePath &space) const
Returns the surface normal of the &quot;into&quot; object at the point at which a collision is detected...
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
NodePath is the fundamental system for disambiguating instances, and also provides a higher-level int...
Definition: nodePath.h:165