map-dynamics.cpp

Go to the documentation of this file.
00001 /*
00002  * map-dynamics.cpp
00003  *
00004  * Copyright (C) 2008,2009  Thomas A. Vaughan
00005  * All rights reserved.
00006  *
00007  *
00008  * Redistribution and use in source and binary forms, with or without
00009  * modification, are permitted provided that the following conditions are met:
00010  *     * Redistributions of source code must retain the above copyright
00011  *       notice, this list of conditions and the following disclaimer.
00012  *     * Redistributions in binary form must reproduce the above copyright
00013  *       notice, this list of conditions and the following disclaimer in the
00014  *       documentation and/or other materials provided with the distribution.
00015  *     * Neither the name of the <organization> nor the
00016  *       names of its contributors may be used to endorse or promote products
00017  *       derived from this software without specific prior written permission.
00018  *
00019  * THIS SOFTWARE IS PROVIDED BY THOMAS A. VAUGHAN ''AS IS'' AND ANY
00020  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
00021  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
00022  * DISCLAIMED. IN NO EVENT SHALL THOMAS A. VAUGHAN BE LIABLE FOR ANY
00023  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
00024  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
00025  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
00026  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
00027  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
00028  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
00029  *
00030  *
00031  * Implementation of objects that can evolve map dynamics.
00032  * See map-dynamics.h
00033  */
00034 
00035 // includes --------------------------------------------------------------------
00036 #include "map-dynamics.h"               // always include our own header first!
00037 
00038 #include "aesop-physics/aesop-physics.h"
00039 #include "datahash/datahash_util.h"
00040 #include "perf/perf.h"
00041 #include "physics-loader/physics-loader.h"
00042 
00043 
00044 namespace aesop {
00045 
00046 
00047 // interface destructor implementation
00048 ObjectContext::~ObjectContext(void) throw() { }
00049 MapDynamics::~MapDynamics(void) throw() { }
00050 
00051 ////////////////////////////////////////////////////////////////////////////////
00052 //
00053 //      static helper methods
00054 //
00055 ////////////////////////////////////////////////////////////////////////////////
00056 
00057 
00058 struct obj_context_t {
00059         obj_context_t(void) throw() { this->clear(); }
00060         void clear(void) throw() {
00061                         instance = NULL;
00062                         dyn = NULL;
00063                         context = NULL;
00064                 }
00065 
00066         smart_ptr<ObjectContext>        context;
00067         smart_ptr<Instance>             instance;
00068         MapDynamics *                   dyn;
00069 };
00070 
00071 
00072 
00073 static obj_context_t *
00074 getContext
00075 (
00076 IN smart_ptr<PhysicsObject>& obj
00077 )
00078 {
00079         ASSERT(obj, "null");
00080         return (obj_context_t *) obj->getUserPointer();
00081 }
00082 
00083 
00084 
00085 ////////////////////////////////////////////////////////////////////////////////
00086 //
00087 //      Dynamic  --  object that implements the MapDynamics interface
00088 //
00089 ////////////////////////////////////////////////////////////////////////////////
00090 
00091 class Dynamic : public MapDynamics {
00092 public:
00093         ~Dynamic(void) throw();
00094 
00095         // public class methods ------------------------------------------------
00096         void initialize(IN smart_ptr<Map>& map,
00097                                 IN const Datahash * params);
00098 
00099         // aesop::MapDynamics class interface methods -------------------------
00100         smart_ptr<Map> getMap(void) { return m_map; }
00101         smart_ptr<PhysicsWorld> getPhysics(void) { return m_physics; }
00102         smart_ptr<PhysicsObject> addInstance(IN smart_ptr<Instance>& instance);
00103         void removeObject(IN smart_ptr<PhysicsObject>& obj);
00104         void tickMap(IN float seconds);
00105         void iterateInstancesInZone(IN Zone * zone,
00106                                 IN zone_obj_iteration_fn fn,
00107                                 IN void * context);
00108 
00109 private:
00110         // private typedefs ----------------------------------------------------
00111         typedef std::vector<smart_ptr<PhysicsObject> > obj_vec_t;
00112 
00113         struct zone_context_t {
00114                 smart_ptr<PhysicsShape>         shape;
00115                 smart_ptr<PhysicsObject>        ghost;
00116                 obj_vec_t                       staticObjects;
00117         };
00118 
00119         struct iter_context_t {
00120                 iter_context_t(void) throw() { this->clear(); }
00121                 void clear(void) throw() {
00122                                 fn = NULL;
00123                                 context = NULL;
00124                         }
00125 
00126                 // data fields
00127                 zone_obj_iteration_fn           fn;
00128                 void *                          context;
00129         };
00130 
00131         struct static_context_t {
00132                 static_context_t(void) throw() { this->clear(); }
00133                 void clear(void) throw() {
00134                                 pThis = NULL;
00135                                 zone = NULL;
00136                         }
00137 
00138                 // data fields
00139                 Dynamic *               pThis;
00140                 Zone *                  zone;
00141         };
00142 
00143         // private helper methods ----------------------------------------------
00144         smart_ptr<PhysicsObject> addInstanceInternal(
00145                                 IN smart_ptr<Instance>& instance,
00146                                 IN Zone * zone,
00147                                 IN bool isStatic);
00148         void addGhostObjectForZone(IN Zone * zone);
00149         static void addZoneInstances(IN Zone * zone,
00150                                 IN void * context);
00151         static void addStaticInstance(IN smart_ptr<Instance>& instance,
00152                                 IN void * context);
00153         static void destroyZoneContext(IN Zone * zone,
00154                                 IN void * context);
00155         static void objectCallback(IN smart_ptr<PhysicsObject>& obj,
00156                                 IN void * context);
00157 
00158         // private member data -------------------------------------------------
00159         std::string                     m_timerName;
00160         smart_ptr<Map>                  m_map;          // aesop map
00161         smart_ptr<PhysicsWorld>         m_physics;      // physics engine
00162 };
00163 
00164 
00165 
00166 Dynamic::~Dynamic
00167 (
00168 void
00169 )
00170 throw()
00171 {
00172         if (m_map) {
00173                 // we have a map: iterate and delete any zone context
00174                 m_map->iterateZones(destroyZoneContext, this);
00175         }
00176 }
00177 
00178 
00179 
00180 ////////////////////////////////////////////////////////////////////////////////
00181 //
00182 //      Dynamic  --  public class methods
00183 //
00184 ////////////////////////////////////////////////////////////////////////////////
00185 
00186 void
00187 Dynamic::initialize
00188 (
00189 IN smart_ptr<Map>& map,
00190 IN const Datahash * params
00191 )
00192 {
00193         ASSERT(map, "null");
00194         ASSERT(params, "null");
00195 
00196         // remember map!
00197         m_map = map;
00198 
00199         // create timer name
00200         m_timerName = "MapDynamics::tickMap(";
00201         m_timerName += m_map->getId();
00202         m_timerName += ")";
00203 
00204         // create physics engine
00205         m_physics = PhysicsWorld::create(params);
00206         ASSERT(m_physics, "failed to create physics engine for map");
00207 
00208         // walk through all static objects and add them to the world
00209         m_map->iterateZones(addZoneInstances, this);
00210 }
00211 
00212 
00213 
00214 ////////////////////////////////////////////////////////////////////////////////
00215 //
00216 //      Dynamic -- MapDynamics class interface methods 
00217 //
00218 ////////////////////////////////////////////////////////////////////////////////
00219 
00220 smart_ptr<PhysicsObject>
00221 Dynamic::addInstance
00222 (
00223 IN smart_ptr<Instance>& instance
00224 )
00225 {
00226         ASSERT(instance, "null");
00227 
00228         // use our internal helper method
00229         // Anthing coming through this interface is assumed to NOT be static!
00230         return this->addInstanceInternal(instance, NULL, false);
00231 }
00232 
00233 
00234 
00235 void
00236 Dynamic::removeObject
00237 (
00238 IN smart_ptr<PhysicsObject>& obj
00239 )
00240 {
00241         ASSERT(obj, "null");
00242 
00243         m_physics->removeObject(obj);
00244 
00245         obj_context_t * oc = (obj_context_t *) obj->getUserPointer();
00246         if (oc) {
00247                 obj->setUserPointer(NULL);
00248                 delete oc;
00249         }
00250 }
00251 
00252 
00253 
00254 void
00255 Dynamic::tickMap
00256 (
00257 IN float seconds
00258 )
00259 {
00260         perf::Timer timer(m_timerName.c_str());
00261         ASSERT(seconds > 0.0, "Bad seconds: %f", seconds);
00262 
00263 //      DPRINTF("%s", timer_name.c_str());
00264 
00265         // tick map!
00266         m_physics->tick(seconds);
00267 }
00268 
00269 
00270 
00271 void
00272 Dynamic::iterateInstancesInZone
00273 (
00274 IN Zone * zone,
00275 IN zone_obj_iteration_fn fn,
00276 IN void * context
00277 )
00278 {
00279         ASSERT(zone, "null");
00280         ASSERT(fn, "null");
00281         // ASSERT(context) -- we don't care!
00282 
00283         // first, iterate over all static objects (if any)
00284         zone_context_t * zc = (zone_context_t *) zone->getUserPointer();
00285         ASSERT(zc, "null zone context");
00286 
00287         for (obj_vec_t::iterator i = zc->staticObjects.begin();
00288              i != zc->staticObjects.end(); ++i) {
00289                 smart_ptr<PhysicsObject>& obj = *i;
00290                 ASSERT(obj, "null static object?");
00291                 smart_ptr<Instance> inst = getInstanceFromPhysicsObject(obj);
00292                 if (inst) {
00293                         // inst->dump("(map-dynamics) Iterating");
00294                         fn(inst, obj, context);
00295                 } else {
00296                         DPRINTF("WARNING: static object with  no instance?");
00297                 }
00298         }
00299 
00300         // is this a virtual leaf zone?  In that case, there are no dynamic
00301         //   objects ever.
00302         if (Zone::eType_VirtualLeaf == zone->getType()) {
00303                 return;         // nothing else to iterate
00304         }
00305 
00306         // okay, this zone may contain dynamic objects!  Make sure context is
00307         //      correctly set up.
00308         ASSERT(zc->shape, "null");
00309         ASSERT(zc->ghost, "null");
00310 
00311         // quickly add ghost bounding box to world
00312         m_physics->addObject(zc->ghost);
00313 
00314         // create our own iteration context
00315         iter_context_t ic;
00316         ic.fn = fn;
00317         ic.context = context;
00318 
00319 //      DPRINTF("Getting overlapping objects for zone '%s'", zone->getId());
00320 
00321         // see what all is overlapping with the ghost object
00322         zc->ghost->iterateOverlappingObjects(objectCallback, &ic);
00323 
00324         // now remove
00325         m_physics->removeObject(zc->ghost);
00326 }
00327 
00328 
00329 
00330 ////////////////////////////////////////////////////////////////////////////////
00331 //
00332 //      Dynamic -- private helper methods
00333 //
00334 ////////////////////////////////////////////////////////////////////////////////
00335 
00336 smart_ptr<PhysicsObject>
00337 Dynamic::addInstanceInternal
00338 (
00339 IN smart_ptr<Instance>& instance,
00340 IN Zone * zone,                         ///< can be null!
00341 IN bool isStatic                        ///< static object in map?
00342 )
00343 {
00344         perf::Timer timer("MapDynamics::addInstanceInternal");
00345 
00346         ASSERT(instance, "null");
00347         // ASSERT(zone) -- can be null!
00348         ASSERT(zone || !isStatic, "zone is required for static objects");
00349 
00350         DPRINTF("addInstanceInternal...");
00351 
00352         // Given the instance, create a physics object and add it to the world
00353         //   with the specified placement.
00354         physics_meta_t meta;
00355         if (!getPhysicsMetaFromInstance(instance, meta)) {
00356                 DPRINTF("No physics meta?");
00357                 return NULL;
00358         }
00359         ASSERT(meta.shape, "should always have a shape!");
00360 
00361         // adjust if static!
00362         if (isStatic) {
00363                 // make sure static flag is set
00364                 meta.objectFlags |= PhysicsObject::eFlag_Static;
00365 
00366                 // must be zero (infinite) mass!
00367                 meta.mass = 0.0;
00368         }
00369 
00370         // create an object
00371         smart_ptr<PhysicsObject> obj =
00372             createObject(meta, instance->getPlacement());
00373         ASSERT(obj, "null");
00374 
00375         // put our own data in the new object's user pointer field
00376         smart_ptr<obj_context_t> oc = new obj_context_t;
00377         ASSERT(oc, "out of memory");
00378         oc->instance = instance;
00379         oc->dyn = this;
00380         obj->setUserPointer(oc);
00381         oc.disown();
00382 
00383         // add to world
00384         ASSERT(m_physics, "null");
00385         m_physics->addObject(obj);
00386 
00387         DPRINTF("Successfully added object to world!");
00388         obj->dump("Just added to world");
00389 
00390         // add to zone
00391         if (isStatic) {
00392                 ASSERT(zone, "zone is required for static objects");
00393 
00394                 zone_context_t * zc = (zone_context_t *) zone->getUserPointer();
00395                 ASSERT(zc, "null zone context");
00396 
00397                 zc->staticObjects.push_back(obj);
00398         }
00399 
00400         // all done!
00401         return obj;
00402 }
00403 
00404 
00405 void
00406 Dynamic::addGhostObjectForZone
00407 (
00408 IN Zone * zone
00409 )
00410 {
00411         ASSERT(zone, "null");
00412         ASSERT(m_physics, "null");
00413 
00414         // create context for zone
00415         smart_ptr<zone_context_t> szc = new zone_context_t;
00416         ASSERT(szc, "out of memory");
00417 
00418         zone->setUserPointer((void *) (zone_context_t *) szc);
00419         szc.disown();   // zone owns this now
00420 
00421         // short-circuit if a virtual leaf zone!  no bounding box...
00422         if (Zone::eType_VirtualLeaf == zone->getType()) {
00423                 return;         // yup, ignore this one...
00424         }
00425 
00426         // not a virtual leaf zone!  Get zone pointer so we can create ghost
00427         zone_context_t * zc = (zone_context_t *) zone->getUserPointer();
00428         ASSERT(zc, "just created this!");
00429 
00430         // get the bounding box for this zone
00431         rect3d_t r;
00432         zone->getBoundingRect(r);
00433 
00434         DPRINTF("Adding ghost object for zone '%s'", zone->getId());
00435         r.dump("  bounding box");
00436 
00437         // create shape
00438         point3d_t dimensions(r.x1 - r.x0, r.y1 - r.y0, r.z1 - r.z0);
00439         zc->shape = createBoxShape(dimensions);
00440         ASSERT(zc->shape, "null");
00441 
00442         // create meta
00443         physics_meta_t meta;
00444         meta.shape = zc->shape;
00445         meta.mass = 0;
00446         meta.objectFlags =
00447             PhysicsObject::eFlag_Ghost | PhysicsObject::eFlag_NoCollisions;
00448 
00449         // create placement data
00450         placement_t p;
00451         p.position = point3d_t(r.x1 + r.x0, r.y1 + r.y0, r.z1 + r.z0);
00452         p.position = 0.5 * p.position;
00453 
00454         // create physics object
00455         zc->ghost = createObject(meta, p);
00456         ASSERT(zc->ghost, "null");
00457 
00458         smart_ptr<obj_context_t> oc = new obj_context_t;
00459         ASSERT(oc, "out of memory");
00460         oc->dyn = this;
00461         zc->ghost->setUserPointer(oc);
00462         oc.disown();    // ghost object owns this now
00463 
00464 //      zc->ghost->dump("  zone ghost");
00465 //      ASSERT(false, "stop");
00466 
00467         // add to world
00468 //      m_physics->addObject(zc->ghost);
00469 }
00470 
00471 
00472 
00473 void
00474 Dynamic::addZoneInstances
00475 (
00476 IN Zone * zone,
00477 IN void * context
00478 )
00479 {
00480         ASSERT(zone, "null");
00481         ASSERT(context, "should be this pointer");
00482         Dynamic * pThis = (Dynamic *) context;
00483 
00484         // create a ghost object for efficient collision detection
00485         pThis->addGhostObjectForZone(zone);
00486 
00487         // iterate over all static objects in this zone (if any)
00488         LeafZone * lz = dynamic_cast<LeafZone *>(zone);
00489         if (!lz) {
00490                 return;         // not a leaf zone!
00491         }
00492 
00493         static_context_t sc;
00494         sc.pThis = pThis;
00495         sc.zone = zone;
00496 
00497         lz->iterateStaticInstances(addStaticInstance, &sc);
00498 }
00499 
00500 
00501 
00502 void
00503 Dynamic::addStaticInstance
00504 (
00505 IN smart_ptr<Instance>& instance,
00506 IN void * context
00507 )
00508 {
00509         ASSERT(instance, "null");
00510         static_context_t * psc = (static_context_t *) context;
00511         ASSERT(psc, "null context");
00512         ASSERT(psc->pThis, "null");
00513         ASSERT(psc->zone, "null");
00514 
00515         DPRINTF("Adding static instance!");
00516         DPRINTF("  zone: %s", psc->zone->getId());
00517         DPRINTF("  object type: %s", instance->getTypeId());
00518 
00519         // add to world!
00520         psc->pThis->addInstanceInternal(instance, psc->zone, true);
00521 }
00522 
00523 
00524 
00525 void
00526 Dynamic::destroyZoneContext
00527 (
00528 IN Zone * zone,
00529 IN void * context
00530 )
00531 {
00532         ASSERT(zone, "null");
00533         Dynamic * pThis = (Dynamic *) context;
00534         ASSERT(pThis, "null");
00535 
00536         // first, walk all instances in zone and delete that data...
00537         // TODO: clean up objects!
00538 
00539         // next, clean up per-zone data
00540         void * user = zone->getUserPointer();
00541         if (user) {
00542                 zone->setUserPointer(NULL);
00543                 zone_context_t * zc = (zone_context_t *) user;
00544 //              if (zc->ghost && pThis->m_physics) {
00545 //                      pThis->m_physics->removeObject(zc->ghost);
00546 //              }
00547                 delete zc;
00548         }
00549 }
00550 
00551 
00552 
00553 void
00554 Dynamic::objectCallback
00555 (
00556 IN smart_ptr<PhysicsObject>& obj,
00557 IN void * context
00558 )
00559 {
00560         ASSERT(obj, "null");
00561         iter_context_t * pic = (iter_context_t *) context;
00562         ASSERT(pic, "null context");
00563 
00564         // ignore static objects
00565         if (PhysicsObject::eFlag_Static & obj->getFlags()) {
00566                 return;         // yup, ignore
00567         }
00568 
00569         // get the object context
00570         obj_context_t * oc = getContext(obj);
00571         if (!oc || !oc->instance) {
00572                 return;         // ghosts or other uninteresting objects
00573         }
00574 
00575         // update placement
00576         placement_t& p = oc->instance->getPlacement();
00577         p.position = obj->getPosition();
00578         p.rotation = obj->getOrientation();
00579 
00580 //      DPRINTF("Found an object!  type='%s'", (const char *) instance.typeId);
00581 //      instance.placement.position.dump("  position");
00582 
00583         // okay, go ahead and call the real callback
00584         pic->fn(oc->instance, obj, pic->context);
00585 }
00586 
00587 
00588 
00589 ////////////////////////////////////////////////////////////////////////////////
00590 //
00591 //      public API
00592 //
00593 ////////////////////////////////////////////////////////////////////////////////
00594 
00595 smart_ptr<MapDynamics>
00596 MapDynamics::create
00597 (
00598 IN smart_ptr<Map>& map,
00599 IN const Datahash * params
00600 )
00601 {
00602         ASSERT(map, "null");
00603         ASSERT(params, "null");
00604 
00605         smart_ptr<Dynamic> local = new Dynamic;
00606         ASSERT(local, "out of memory");
00607 
00608         local->initialize(map, params);
00609 
00610         return local;
00611 }
00612 
00613 
00614 
00615 smart_ptr<Instance>
00616 getInstanceFromPhysicsObject
00617 (
00618 IN smart_ptr<PhysicsObject>& obj
00619 )
00620 {
00621         ASSERT(obj, "null");
00622 
00623         obj_context_t * oc = getContext(obj);
00624         if (!oc) {
00625                 return NULL;
00626         }
00627         return oc->instance;
00628 }
00629 
00630 
00631 
00632 MapDynamics *
00633 getMapDynamicsFromPhysicsObject
00634 (
00635 IN smart_ptr<PhysicsObject>& obj
00636 )
00637 {
00638         ASSERT(obj, "null");
00639 
00640         obj_context_t * oc = getContext(obj);
00641         if (!oc)
00642                 return NULL;
00643 
00644         return oc->dyn;
00645 }
00646 
00647 
00648 
00649 };      // aesop namespace
00650