Main Page   Class Hierarchy   Alphabetical List   Compound List   File List   Compound Members   File Members  

panda/src/collide/collisionHandlerPusher.cxx

Go to the documentation of this file.
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 }

Generated on Fri May 2 00:35:33 2003 for Panda by doxygen1.3