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