md3-model.cpp

Go to the documentation of this file.
00001 /*
00002  * md3-model.cpp
00003  *
00004  * Copyright (C) 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  * Entrypoint to md3-model library
00031  */
00032 
00033 // includes --------------------------------------------------------------------
00034 #include "md3-model.h"          // always include our own header first
00035 
00036 #include "animation-state/animation-state.h"
00037 #include "perf/perf.h"
00038 #include "Md3Player.h"
00039 
00040 
00041 namespace md3 {
00042 
00043 
00044 
00045 struct anim_entry_t {
00046         Md3PlayerAnimType       index1;
00047         Md3PlayerAnimType       index2;
00048 };
00049 
00050 
00051 // maps from animation verb --> animation information
00052 typedef std::map<std::string, anim_entry_t> animation_map_t;
00053 
00054 
00055 
00056 ////////////////////////////////////////////////////////////////////////////////
00057 //
00058 //      static helper methods
00059 //
00060 ////////////////////////////////////////////////////////////////////////////////
00061 
00062 static void
00063 addAnimation
00064 (
00065 IN animation_map_t * map,
00066 IN const char * verb,
00067 IN Md3PlayerAnimType index1,
00068 IN Md3PlayerAnimType index2
00069 )
00070 {
00071         ASSERT(map, "null");
00072         ASSERT(verb, "null");
00073 
00074         anim_entry_t ae;
00075         ae.index1 = index1;
00076         ae.index2 = index2;
00077 
00078         map->operator[](verb) = ae;
00079 }
00080 
00081 
00082 
00083 static animation_map_t *
00084 getAnimationMap
00085 (
00086 void
00087 )
00088 {
00089         static smart_ptr<animation_map_t> s_map;
00090         if (!s_map) {
00091                 // need to initialize!
00092                 smart_ptr<animation_map_t> map = new animation_map_t;
00093                 ASSERT(map, "null");
00094 
00095                 // update
00096                 addAnimation(map, "idle",       kLegsIdle,      kTorsoStand);
00097                 addAnimation(map, "walk",       kLegsWalk,      kTorsoStand2);
00098                 addAnimation(map, "run",        kLegsRun,       kTorsoStand2);
00099 
00100                 // fully constructed
00101                 s_map = map;
00102         }
00103 
00104         return s_map;
00105 }
00106 
00107 
00108 
00109 ////////////////////////////////////////////////////////////////////////////////
00110 //
00111 //      PlayerTask - glut::Task object so it is possible to load Md3 models
00112 //      on a separate thread, but delegate to the glut thread for any openGL
00113 //      calls.  At the moment, *all* md3 model loading is delegated to the
00114 //      open GL thread.  This is because we don't want to crack open the
00115 //      original md3 model code.  That's probably a fine tradeoff for now.
00116 //
00117 ////////////////////////////////////////////////////////////////////////////////
00118 
00119 class PlayerTask : public glut::Task {
00120 public:
00121         PlayerTask(IN const char * dir) {
00122                         m_dir = dir;
00123                 }
00124         ~PlayerTask(void) throw() { }
00125 
00126         // public class methods ------------------------------------------------
00127         smart_ptr<Md3Player> getPlayer(void) { return m_player; }
00128 
00129         // glut::Task class interface methods ----------------------------------
00130         void doTask(void) {
00131                         m_player = new Md3Player(m_dir.c_str());
00132                         ASSERT(m_player, "null");
00133 
00134                         m_player->setAnimation(kTorsoStand);
00135                         m_player->setAnimation(kLegsIdle);
00136                 }
00137 
00138 private:
00139         std::string             m_dir;
00140         smart_ptr<Md3Player>    m_player;
00141 };
00142 
00143 
00144 
00145 ////////////////////////////////////////////////////////////////////////////////
00146 //
00147 //      Player object -- wraps the Md3Player object with a glut::Renderable
00148 //      interface.
00149 //
00150 ////////////////////////////////////////////////////////////////////////////////
00151 
00152 class Player : public glut::Renderable {
00153 public:
00154         ~Player(void) throw() { }
00155 
00156         // public class methods ------------------------------------------------
00157         void initialize(IN const char * model_path);
00158 
00159         // glut::Renderable class interface methods ----------------------------
00160         void render(IN const glut::render_context_t& rc,
00161                                 IN glut::RenderQueue * rq);
00162         rect3d_t getBoundingBox(void) const throw() { m_boundingBox.dump("MD3 DEBUG"); return m_boundingBox; }
00163         bool isAnimateable(void) const throw();
00164         void getAnimationState(OUT std::string& state);
00165         bool setAnimationState(IN const char * state);
00166         bool setAnimation(IN const char * verb);
00167 
00168 private:
00169         // private typedefs ----------------------------------------------------
00170 
00171         // private member functions --------------------------------------------
00172         void addState(IO glut::AnimationState& as,
00173                                 IN eAnimationArea area);
00174         void setState(IN const glut::anim_channel_state_t& acs,
00175                                 IN eAnimationArea area);
00176 
00177         // private member data -------------------------------------------------
00178         smart_ptr<Md3Player>            m_md3Player;
00179         rect3d_t                        m_boundingBox;
00180 };
00181 
00182 
00183 
00184 void
00185 Player::initialize
00186 (
00187 IN const char * model_path
00188 )
00189 {
00190         ASSERT(model_path, "null");
00191 
00192         // wrap as a glut task
00193         PlayerTask task(model_path);
00194 
00195         // block until OpenGL thread handles this task
00196         glut::requestTask(&task);
00197 
00198         // okay, got it
00199         m_md3Player = task.getPlayer();
00200         ASSERT(m_md3Player, "failed to load player?");
00201 
00202         // now determine bounding box
00203         // TODO: need a way to do this!
00204         // For now, give it a generous window
00205         m_boundingBox.clear();
00206         m_boundingBox.inflate(1.5);     // basically a 3m cube
00207 }
00208 
00209 
00210 
00211 void
00212 Player::render
00213 (
00214 IN const glut::render_context_t& rc,
00215 IN glut::RenderQueue * rq
00216 )
00217 {
00218         ASSERT(rq, "null");
00219 
00220         glMatrixMode(GL_TEXTURE);
00221         glPushMatrix();
00222 
00223         // TODO: pull this out of here!  Just for testing...
00224         m_md3Player->animate(0.01);
00225 
00226         glEnable(GL_TEXTURE_2D);
00227         m_md3Player->draw();
00228         glDisable(GL_TEXTURE_2D);
00229 
00230         glMatrixMode(GL_TEXTURE);
00231         glPopMatrix();
00232 }
00233 
00234 
00235 
00236 bool
00237 Player::isAnimateable
00238 (
00239 void
00240 )
00241 const
00242 throw()
00243 {
00244         return true;
00245 }
00246 
00247 
00248 
00249 void
00250 Player::getAnimationState
00251 (
00252 OUT std::string& state
00253 )
00254 {
00255         glut::AnimationState as;
00256 
00257         addState(as, eAnimationArea_Upper);
00258         addState(as, eAnimationArea_Lower);
00259         as.serialize(state);
00260 
00261         //DPRINTF("State: '%s'", state.c_str());
00262 }
00263 
00264 
00265 
00266 bool
00267 Player::setAnimationState
00268 (
00269 IN const char * state
00270 )
00271 {
00272         ASSERT(state, "null");
00273 
00274         glut::AnimationState as;
00275         if (!as.deserialize(state)) {
00276                 DPRINTF("Unable to deserialize animation state?");
00277                 DPRINTF("  state='%s'", state);
00278                 return false;
00279         }
00280 
00281         // okay, update animation values!
00282         if (2 != as.size()) {
00283                 DPRINTF("Only support 2-channel animation state");
00284                 return false;
00285         }
00286 
00287         setState(as[0], eAnimationArea_Upper);
00288         setState(as[1], eAnimationArea_Lower);
00289 
00290         return true;
00291 }
00292 
00293 
00294 
00295 bool
00296 Player::setAnimation
00297 (
00298 IN const char * verb
00299 )
00300 {
00301         ASSERT(verb, "null");
00302         ASSERT(m_md3Player, "null");
00303 
00304         animation_map_t * map = getAnimationMap();
00305         ASSERT(map, "failed to acquire animation map?");
00306 
00307         animation_map_t::const_iterator i = map->find(verb);
00308         if (map->end() == i) {
00309                 DPRINTF("No such verb for md3 models: '%s'", verb);
00310                 return false;
00311         }
00312         //DPRINTF("Setting animation verb: '%s'", verb);
00313 
00314         const anim_entry_t& ae = i->second;
00315         m_md3Player->setAnimation(ae.index1);
00316         m_md3Player->setAnimation(ae.index2);
00317 
00318         return true;
00319 }
00320 
00321 
00322 
00323 ////////////////////////////////////////////////////////////////////////////////
00324 //
00325 //      Player -- private helper methods
00326 //
00327 ////////////////////////////////////////////////////////////////////////////////
00328 
00329 void
00330 Player::addState
00331 (
00332 IO glut::AnimationState& as,
00333 IN eAnimationArea area
00334 )
00335 {
00336         ASSERT(m_md3Player, "null");
00337 
00338         // get state from model
00339         Md3AnimState_t mas;
00340         m_md3Player->getAnimationState(area, mas);
00341 
00342         // map to general animation state
00343         glut::anim_channel_state_t acs;
00344         acs.animIndex = (int16_t) mas.animationIndex;
00345         acs.msElapsed = (int32_t) ((mas.time * 1000) + 0.5);
00346 
00347         // add
00348         as.push_back(acs);
00349 }
00350 
00351 
00352 
00353 void
00354 Player::setState
00355 (
00356 IN const glut::anim_channel_state_t& acs,
00357 IN eAnimationArea area
00358 )
00359 {
00360         ASSERT(m_md3Player, "null");
00361 
00362         Md3AnimState_t mas;
00363 
00364         mas.animationIndex = acs.animIndex;
00365         mas.time = 0.001 * acs.msElapsed;
00366 
00367         m_md3Player->setAnimationState(area, mas);
00368 }
00369 
00370 
00371 
00372 ////////////////////////////////////////////////////////////////////////////////
00373 //
00374 //      public API
00375 //
00376 ////////////////////////////////////////////////////////////////////////////////
00377 
00378 smart_ptr<glut::Renderable>
00379 loadMd3Player
00380 (
00381 IN const char * model_path
00382 )
00383 {
00384         perf::Timer timer("loadMd3Model");
00385         ASSERT(model_path, "null");
00386 
00387         smart_ptr<Player> local = new Player;
00388         ASSERT(local, "null");
00389 
00390         local->initialize(model_path);
00391 
00392         return local;
00393 }
00394 
00395 
00396 
00397 };      // md3 namespace
00398