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