00001 // Filename: collisionHandlerPusher.cxx 00002 // Created by: drose (16Mar02) 00003 // 00004 //////////////////////////////////////////////////////////////////// 00005 // 00006 // PANDA 3D SOFTWARE 00007 // Copyright (c) 2001, Disney Enterprises, Inc. All rights reserved 00008 // 00009 // All use of this software is subject to the terms of the Panda 3d 00010 // Software license. You should have received a copy of this license 00011 // along with this source code; you will also find a current copy of 00012 // the license at http://www.panda3d.org/license.txt . 00013 // 00014 // To contact the maintainers of this program write to 00015 // panda3d@yahoogroups.com . 00016 // 00017 //////////////////////////////////////////////////////////////////// 00018 00019 #include "collisionHandlerPusher.h" 00020 #include "collisionNode.h" 00021 #include "collisionEntry.h" 00022 #include "collisionPolygon.h" 00023 #include "config_collide.h" 00024 #include "dcast.h" 00025 00026 TypeHandle CollisionHandlerPusher::_type_handle; 00027 00028 /////////////////////////////////////////////////////////////////// 00029 // Class : ShoveData 00030 // Description : The ShoveData class is used within 00031 // CollisionHandlerPusher::handle_entries(), to track 00032 // multiple shoves onto a given collider. It's not 00033 // exported outside this file. 00034 //////////////////////////////////////////////////////////////////// 00035 class ShoveData { 00036 public: 00037 LVector3f _vector; 00038 float _length; 00039 bool _valid; 00040 CollisionEntry *_entry; 00041 }; 00042 00043 //////////////////////////////////////////////////////////////////// 00044 // Function: CollisionHandlerPusher::Constructor 00045 // Access: Public 00046 // Description: 00047 //////////////////////////////////////////////////////////////////// 00048 CollisionHandlerPusher:: 00049 CollisionHandlerPusher() { 00050 _horizontal = true; 00051 } 00052 00053 //////////////////////////////////////////////////////////////////// 00054 // Function: CollisionHandlerPusher::Destructor 00055 // Access: Public, Virtual 00056 // Description: 00057 //////////////////////////////////////////////////////////////////// 00058 CollisionHandlerPusher:: 00059 ~CollisionHandlerPusher() { 00060 } 00061 00062 //////////////////////////////////////////////////////////////////// 00063 // Function: CollisionHandlerPusher::handle_entries 00064 // Access: Protected, Virtual 00065 // Description: Called by the parent class after all collisions have 00066 // been detected, this manages the various collisions 00067 // and moves around the nodes as necessary. 00068 // 00069 // The return value is normally true, but it may be 00070 // false to indicate the CollisionTraverser should 00071 // disable this handler from being called in the future. 00072 //////////////////////////////////////////////////////////////////// 00073 bool CollisionHandlerPusher:: 00074 handle_entries() { 00075 bool okflag = true; 00076 00077 FromEntries::const_iterator fi; 00078 for (fi = _from_entries.begin(); fi != _from_entries.end(); ++fi) { 00079 CollisionNode *from_node = (*fi).first; 00080 nassertr(from_node != (CollisionNode *)NULL, false); 00081 const Entries &entries = (*fi).second; 00082 00083 Colliders::iterator ci; 00084 ci = _colliders.find(from_node); 00085 if (ci == _colliders.end()) { 00086 // Hmm, someone added a CollisionNode to a traverser and gave 00087 // it this CollisionHandler pointer--but they didn't tell us 00088 // about the node. 00089 collide_cat.error() 00090 << "CollisionHandlerPusher doesn't know about " 00091 << *from_node << ", disabling.\n"; 00092 okflag = false; 00093 00094 } else { 00095 ColliderDef &def = (*ci).second; 00096 if (!def.is_valid()) { 00097 collide_cat.error() 00098 << "Removing invalid collider " << *from_node << " from " 00099 << get_type() << "\n"; 00100 _colliders.erase(ci); 00101 00102 } else { 00103 // How to apply multiple shoves from different solids onto the 00104 // same collider? One's first intuition is to vector sum all 00105 // the shoves. However, this causes problems when two parallel 00106 // walls shove on the collider, because we end up with a double 00107 // shove. We hack around this by testing if two shove vectors 00108 // share nearly the same direction, and if so, we keep only the 00109 // longer of the two. 00110 00111 typedef pvector<ShoveData> Shoves; 00112 Shoves shoves; 00113 00114 Entries::const_iterator ei; 00115 for (ei = entries.begin(); ei != entries.end(); ++ei) { 00116 CollisionEntry *entry = (*ei); 00117 nassertr(entry != (CollisionEntry *)NULL, false); 00118 nassertr(from_node == entry->get_from_node(), false); 00119 00120 if (!entry->has_from_surface_normal() || 00121 !entry->has_from_depth()) { 00122 #ifndef NDEBUG 00123 if (collide_cat.is_debug()) { 00124 collide_cat.debug() 00125 << "Cannot shove on " << *from_node << " for collision into " 00126 << *entry->get_into_node() << "; no normal/depth information.\n"; 00127 } 00128 #endif 00129 00130 } else { 00131 // Shove it just enough to clear the volume. 00132 if (entry->get_from_depth() != 0.0f) { 00133 LVector3f normal = entry->get_from_surface_normal(); 00134 if (_horizontal) { 00135 normal[2] = 0.0f; 00136 } 00137 // Just to be on the safe size, we normalize the normal 00138 // vector, even though it really ought to be unit-length 00139 // already (unless we just forced it horizontal, above). 00140 normal.normalize(); 00141 00142 ShoveData sd; 00143 sd._vector = normal; 00144 sd._length = entry->get_from_depth(); 00145 sd._valid = true; 00146 sd._entry = entry; 00147 00148 #ifndef NDEBUG 00149 if (collide_cat.is_debug()) { 00150 collide_cat.debug() 00151 << "Shove on " << *from_node << " from " 00152 << *entry->get_into_node() << ": " << sd._vector 00153 << " times " << sd._length << "\n"; 00154 } 00155 #endif 00156 00157 shoves.push_back(sd); 00158 } 00159 } 00160 } 00161 00162 if (!shoves.empty()) { 00163 // Now we look for two shoves that are largely in the same 00164 // direction, so we can combine them into a single shove of 00165 // the same magnitude; we also check for two shoves at 90 00166 // degrees, so we can detect whether we are hitting an inner 00167 // or an outer corner. 00168 00169 Shoves::iterator si; 00170 for (si = shoves.begin(); si != shoves.end(); ++si) { 00171 ShoveData &sd = (*si); 00172 Shoves::iterator sj; 00173 for (sj = shoves.begin(); sj != si; ++sj) { 00174 ShoveData &sd2 = (*sj); 00175 if (sd2._valid) { 00176 00177 float d = sd._vector.dot(sd2._vector); 00178 if (collide_cat.is_debug()) { 00179 collide_cat.debug() 00180 << "Considering dot product " << d << "\n"; 00181 } 00182 00183 if (d > 0.9) { 00184 // These two shoves are largely in the same direction; 00185 // save the larger of the two. 00186 if (sd2._length < sd._length) { 00187 sd2._valid = false; 00188 } else { 00189 sd._valid = false; 00190 } 00191 00192 } else { 00193 // These two shoves are not in the same direction. 00194 // If they are both from polygons that are a child 00195 // of the same node, try to determine the shape of 00196 // the corner (convex or concave). 00197 const CollisionSolid *s1 = sd._entry->get_into(); 00198 const CollisionSolid *s2 = sd2._entry->get_into(); 00199 if (s1 != (CollisionSolid *)NULL && 00200 s2 != (CollisionSolid *)NULL && 00201 s1->is_exact_type(CollisionPolygon::get_class_type()) && 00202 s2->is_exact_type(CollisionPolygon::get_class_type()) && 00203 sd._entry->get_into_node_path() == 00204 sd2._entry->get_into_node_path()) { 00205 const CollisionPolygon *p1 = DCAST(CollisionPolygon, s1); 00206 const CollisionPolygon *p2 = DCAST(CollisionPolygon, s2); 00207 if (p1->dist_to_plane(p2->get_collision_origin()) < 0 && 00208 p2->dist_to_plane(p1->get_collision_origin()) < 0) { 00209 // Each polygon is behind the other one. That 00210 // means we have a convex corner, and therefore 00211 // we should discard one of the shoves (or the 00212 // user will get stuck coming at a convex 00213 // corner). 00214 if (collide_cat.is_debug()) { 00215 collide_cat.debug() 00216 << "Discarding shove from convex corner.\n"; 00217 } 00218 00219 // This time, unlike the case of two parallel 00220 // walls above, we discard the larger of the two 00221 // shoves, not the smaller. This is because as 00222 // we slide off the convex corner, the wall we 00223 // are sliding away from will get a bigger and 00224 // bigger shove--and we need to keep ignoring 00225 // the same wall as we slide. 00226 if (sd2._length < sd._length) { 00227 sd._valid = false; 00228 } else { 00229 sd2._valid = false; 00230 } 00231 } 00232 } 00233 } 00234 } 00235 } 00236 } 00237 00238 // Now we can determine the net shove. 00239 LVector3f net_shove(0.0f, 0.0f, 0.0f); 00240 for (si = shoves.begin(); si != shoves.end(); ++si) { 00241 const ShoveData &sd = (*si); 00242 if (sd._valid) { 00243 net_shove += sd._vector * sd._length; 00244 } 00245 } 00246 00247 #ifndef NDEBUG 00248 if (collide_cat.is_debug()) { 00249 collide_cat.debug() 00250 << "Net shove on " << *from_node << " is: " 00251 << net_shove << "\n"; 00252 } 00253 #endif 00254 00255 LMatrix4f mat; 00256 def.get_mat(mat); 00257 def.set_mat(LMatrix4f::translate_mat(net_shove) * mat); 00258 } 00259 } 00260 } 00261 } 00262 00263 return okflag; 00264 }