physics-loader.cpp

Go to the documentation of this file.
00001 /*
00002  * physics-loader.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  * Object that loads + stores physics collision shapes.  See physics-loader.h.
00032  */
00033 
00034 // includes --------------------------------------------------------------------
00035 #include "physics-loader.h"             // always list our own header first!
00036 
00037 #include <fstream>
00038 
00039 #include "common/wave_ex.h"
00040 #include "datahash/datahash_util.h"
00041 #include "datahash/datahash_text.h"
00042 #include "hfield/heightfield.h"
00043 #include "perf/perf.h"
00044 #include "threadsafe/smart_mutex.h"
00045 #include "trimesh/trimesh.h"
00046 #include "util/file.h"
00047 #include "util/parsing.h"
00048 #include "util/token_stream.h"
00049 
00050 
00051 namespace aesop {
00052 
00054 
00057 static const char * s_name              = "Physics Meta Loader";
00058 
00059 
00060 // TODO: use a more general approach to per-shape context?
00061 // Not sure if that's necessary yet...
00062 struct shape_context_t {
00063         smart_ptr<hfield::Heightfield>          hfield;
00064         smart_ptr<trimesh::Trimesh>             trimesh;
00065 };
00066 
00067 
00068 
00070 //
00071 //      PhysicsMeta -- used for component data
00072 //
00074 
00075 class PhysicsMeta : public ComponentData {
00076 public:
00077         ~PhysicsMeta(void) throw() {
00078                         if (m_meta.shape) {
00079                                 shape_context_t * ctx =
00080                                     (shape_context_t *) m_meta.shape->getContext();
00081                                 if (ctx) {
00082                                         delete ctx;
00083                                 }
00084                         }
00085                 }
00086 
00087         // aesop::ComponentData class interface methods ------------------------
00088         void dump(IN const char * txt) const throw() {
00089                         DPRINTF("%s: PhysicsMeta", txt);
00090                         m_meta.dump(txt);
00091                 }
00092 
00093         // data is public
00094         physics_meta_t          m_meta;
00095 
00096 private:
00097 };
00098 
00099 
00100 
00102 //
00103 //      static helper methods
00104 //
00106 
00107 static int
00108 checkFlag
00109 (
00110 IN const char * modelFlags,
00111 IN const char * name,
00112 IN int value
00113 )
00114 {
00115         ASSERT(modelFlags, "null");
00116         ASSERT(name, "null");
00117         ASSERT(value, "null");
00118 
00119         // TODO: how can the type override and *unset* flags?
00120         // At the moment, flags can only be cumulative...
00121         if (strstr(modelFlags, name))
00122                 return value;
00123         return 0;
00124 }
00125 
00126 
00127 #define TEST_FLAG(key, flag)                    \
00128         flags |= checkFlag(textFlags, key , PhysicsObject::eFlag_ ##flag );
00129 
00130 
00131 static void
00132 updateFlagsFromData
00133 (
00134 IO int& flags,
00135 IN const Datahash * data
00136 )
00137 {
00138         if (!data)
00139                 return;
00140 
00141         const char * textFlags = getOptionalString(data, "flags", "");
00142 
00143         TEST_FLAG("ghost",      Ghost);
00144         TEST_FLAG("events",     Events);
00145         TEST_FLAG("moveable",   Moveable);
00146 }
00147 
00148 
00149 
00150 static smart_ptr<PhysicsMeta>
00151 loadShape
00152 (
00153 IN const Datahash * physicsData,
00154 IN const Datahash * typeData,
00155 IN const Datahash * instData,
00156 IN const char * parentDir
00157 )
00158 {
00159         ASSERT(physicsData, "null");
00160         ASSERT(typeData, "null");
00161         // ASSERT(instData) -- can be null!
00162         ASSERT(parentDir, "null");
00163 
00164         // get manager
00165         smart_ptr<nstream::Manager> mgr =
00166             nstream::getFilesystemManager(parentDir);
00167         ASSERT_THROW(mgr, "failed to create fileystem manager");
00168 
00169         // get ready for return value
00170         smart_ptr<PhysicsMeta> pm = new PhysicsMeta;
00171         ASSERT(pm, "out of memory");
00172         physics_meta_t& meta = pm->m_meta;
00173         meta.clear();
00174 
00175         // mass? start with model, and allow type to override
00176         // TODO: allow instance to override!
00177         meta.mass = 0.0;                // assume infinite by default
00178         const char * val = getKeyWithOverrides("mass", instData, typeData,
00179             physicsData, eDatahash_Optional);
00180         if (val) {
00181                 meta.mass = atof(val);
00182         }
00183 
00184         // object flags 
00185         updateFlagsFromData(meta.objectFlags, physicsData);
00186         updateFlagsFromData(meta.objectFlags, typeData);
00187         updateFlagsFromData(meta.objectFlags, instData);
00188 
00189         // load shape
00190         const char * shape = getString(physicsData, "shape");
00191 
00192         // save parent directory in case it is needed...
00193 
00194         // determine body based on data
00195         // TODO: over time, this set of if-statements could become quite
00196         //   large as new shapes are supported.  Should be pulled out into
00197         //   a separate registry.
00198         if (!strcmp("box", shape)) {
00199                 const char * val = getKeyWithOverrides("dimensions",
00200                     instData, typeData, physicsData, eDatahash_Required);
00201                 point3d_t p;
00202                 parsePoint3d(val, p);
00203                 p.dump("box dimensions");
00204 
00205                 meta.shape = createBoxShape(p);
00206 
00207         } else if (!strcmp("capsule", shape)) {
00208                 // capsule!
00209                 float height = atof(getKeyWithOverrides("height",
00210                     instData, typeData, physicsData, eDatahash_Required));
00211                 float radius = atof(getKeyWithOverrides("radius",
00212                     instData, typeData, physicsData, eDatahash_Required));
00213                 meta.shape = createCapsuleShape(height, radius);
00214 
00215         } else if (!strcmp("trimesh", shape)) {
00216                 // triangle mesh
00217                 const char * tfile = getKeyWithOverrides("trimesh",
00218                     instData, typeData, physicsData, eDatahash_Required);
00219                 std::string path = parentDir;
00220                 path += "/";
00221                 path += tfile;
00222 
00223                 DPRINTF("Creating triangle mesh physics model from path: %s",
00224                     path.c_str());
00225 
00226                 smart_ptr<trimesh::Trimesh> trimesh =
00227                     trimesh::Trimesh::load(path.c_str());
00228                 ASSERT(trimesh, "failed to load trimesh");
00229                 trimesh->dump(path.c_str());
00230 
00231                 meta.shape = createTrimeshShape(trimesh);
00232                 ASSERT(meta.shape, "failed to create trimesh shape");
00233 
00234                 // stash trimesh state in shape
00235                 shape_context_t * ctx = new shape_context_t;
00236                 ASSERT(ctx, "out of memory");
00237                 ctx->trimesh = trimesh;
00238                 meta.shape->setContext(ctx);
00239 
00240         } else if (!strcmp("hfield", shape)) {
00241                 // it's a heightfield
00242                 const char * hfile = getKeyWithOverrides("hfield",
00243                     instData, typeData, physicsData, eDatahash_Required);
00244 
00245                 DPRINTF("Creating heightfield physics model from path: %s",
00246                     hfile);
00247 
00248                 smart_ptr<nstream::Stream> stream =
00249                     nstream::openNamedStream(mgr, hfile);
00250                 ASSERT(stream, "null");
00251 
00252                 smart_ptr<hfield::Heightfield> hfield =
00253                     hfield::Heightfield::create(stream);
00254                 ASSERT(hfield, "failed to load heightfield from %s", hfile);
00255                 hfield->dump("Loading for physics shape");
00256 
00257                 float y = hfield->getHeight(5, 0.5);
00258                 DPRINTF("Height at x=5, z=0.5: %f", y);
00259 
00260                 meta.shape = createHeightfieldShape(hfield);
00261                 ASSERT(meta.shape, "failed to create heightfield shape");
00262 
00263                 // stash the heightfield in the collision shape!
00264                 shape_context_t * ctx = new shape_context_t;
00265                 ASSERT(ctx, "out of memory");
00266                 ctx->hfield = hfield;   // take ownership
00267                 meta.shape->setContext(ctx);
00268         } else {
00269                 WAVE_EX(wex);
00270                 wex << "Unknown physics shape: " << shape;
00271         }
00272 
00273         // all done!
00274         ASSERT(meta.shape, "null");
00275         return pm;
00276 }
00277 
00278 
00279 
00281 //
00282 //      PhysicsRegistry - loads + retrieves 3d glut models
00283 //
00285 
00286 class PhysicsRegistry : public TypeComponentLoader {
00287 public:
00288         // constructor, destructor ---------------------------------------------
00289         PhysicsRegistry(void) throw() { }
00290         ~PhysicsRegistry(void) throw() { }
00291 
00292         // public class methods ------------------------------------------------
00293         void initialize(void) throw() { }
00294         bool getPhysicsMetaTS(IN smart_ptr<Instance>& instance,
00295                                 OUT physics_meta_t& meta);
00296 
00297         // aesop::TypeComponentLoader class interface methods -----------------
00298         const char * getComponentName(void) const throw() { return "physics"; }
00299         const char * getLoaderName(void) const throw() { return s_name; }
00300         bool isMyFormat(IN const Datahash * data,
00301                                 IN const char * path) const;
00302         smart_ptr<ComponentData> loadTS(IN smart_ptr<Instance>& instance,
00303                                 IN const Datahash * data,
00304                                 IN const char * path);
00305 
00306 private:
00307         // private typedefs ----------------------------------------------------
00308         typedef std::map<std::string, smart_ptr<Datahash> > data_map_t;
00309 
00310         // private helper methods ----------------------------------------------
00311 
00312         // private member data -------------------------------------------------
00313         smart_mutex             m_mutex;
00314         data_map_t              m_dataMap;
00315 };
00316 
00317 static smart_ptr<PhysicsRegistry> s_registry;
00318 
00319 
00320 
00321 bool
00322 PhysicsRegistry::isMyFormat
00323 (
00324 IN const Datahash * data,
00325 IN const char * path
00326 )
00327 const
00328 {
00329         ASSERT(data, "null");
00330         ASSERT(path, "null");
00331 
00332         DPRINTF("Checking '%s'", path);
00333 
00334         const char * physicsId = getString(data, "id");
00335 
00336         // has to end in .physics
00337         if (strcmp("physics", GetExtension(physicsId))) {
00338                 DPRINTF("Extension does not match!");
00339                 return false;
00340         }
00341 
00342         std::ifstream stream(path);
00343         if (!stream.good()) {
00344                 WAVE_EX(wex);
00345                 wex << "Failed to open file for reading: " << path;
00346         }
00347 
00348         std::string token;
00349         getNextToken(stream, token);
00350         DPRINTF("  token: %s", token.c_str());
00351         if ("physicsFormat" != token)
00352                 return false;
00353 
00354         getNextToken(stream, token);
00355         DPRINTF("  token: %s", token.c_str());
00356         if ("physics-shape-0.1" != token)
00357                 return false;
00358 
00359         // okay, has the correct extension and format specification!
00360         return true;
00361 }
00362 
00363 
00364 
00365 smart_ptr<ComponentData>
00366 PhysicsRegistry::loadTS
00367 (
00368 IN smart_ptr<Instance>& instance,
00369 IN const Datahash * typeData,
00370 IN const char * path
00371 )
00372 {
00373         ASSERT(instance, "null");
00374         ASSERT(typeData, "null");
00375         ASSERT(path, "null");
00376 
00377         DPRINTF("PHYSICS MODEL LOAD");
00378         const char * physicsId = getString(typeData, "id");
00379         DPRINTF("Asked to load physics component data for type: %s",
00380             instance->getTypeId());
00381         DPRINTF("  Uses physics model: %s", physicsId);
00382 
00383         // load or retrieve the data from disk
00384         smart_ptr<Datahash> physicsData;
00385         {
00386                 // touching our map -- lock this part!
00387                 mlock l(m_mutex);
00388                 data_map_t::iterator i = m_dataMap.find(path);
00389                 if (m_dataMap.end() != i) {
00390                         DPRINTF("  Already loaded physics data from disk!");
00391                         physicsData = i->second;
00392                 }
00393         }
00394 
00395         if (!physicsData) {
00396                 DPRINTF("Loading physics meta '%s' from file '%s'",
00397                     physicsId, path);
00398                 physicsData = readHashFromTextFile(path);
00399 
00400                 // touching map again: lock!
00401                 {
00402                         mlock l(m_mutex);
00403                         m_dataMap[path] = physicsData;  // cache this!
00404                 }
00405         }
00406         ASSERT(physicsData, "null");
00407 
00408         // get parent directory in case loader needs it...
00409         std::string parentDir;
00410         GetParentDirectory(path, parentDir);
00411 
00412         // get the physics instance data (if any) from the Instance
00413         smart_ptr<Datahash> instData = instance->getInstanceData("physics");
00414         // ASSERT(instData) -- can be null!     
00415 
00416         // okay, create component data
00417         smart_ptr<PhysicsMeta> pm;
00418         try {
00419                 pm = loadShape(physicsData, typeData, instData,
00420                     parentDir.c_str());
00421                 ASSERT(pm, "null");
00422 
00423         } catch (std::exception& e) {
00424                 DPRINTF("Error loading physics shape from file: %s", path);
00425                 DPRINTF("Exception: %s", e.what());
00426 
00427                 // TODO: remove this?  handy to find problem model files...
00428                 ASSERT(false, "Failed to load model!");
00429         }
00430         ASSERT(pm->m_meta.shape, "null");
00431         return pm;
00432 }
00433 
00434 
00435 
00437 //
00438 //      PhysicsRegistry -- private helper methods
00439 //
00441 
00442 
00443 
00445 //
00446 //      public API
00447 //
00449 
00450 smart_ptr<TypeComponentLoader>
00451 getPhysicsLoader
00452 (
00453 void
00454 )
00455 {
00456         if (!s_registry) {
00457                 s_registry = new PhysicsRegistry;
00458                 ASSERT(s_registry, "null");
00459                 s_registry->initialize();
00460         }
00461 
00462         return s_registry;
00463 }
00464 
00465 
00466 
00467 bool
00468 getPhysicsMetaFromInstance
00469 (
00470 IN smart_ptr<Instance>& instance,
00471 OUT physics_meta_t& meta
00472 )
00473 {
00474         ASSERT(instance, "null");
00475         meta.clear();
00476 
00477         DPRINTF("getPhysicsMetaFromInstance");
00478 
00479         smart_ptr<ComponentData> data = instance->getComponentData("physics");
00480         if (!data) {
00481                 DPRINTF("No physics data for instance?");
00482                 return false;
00483         }
00484 
00485         smart_ptr<PhysicsMeta> pm = data;
00486         ASSERT(pm, "failed to upcast?");
00487 
00488         meta = pm->m_meta;
00489         return true;
00490 }
00491 
00492 
00493 
00494 };              // aesop namespace
00495