ORTS

gfxclient/src/GfxAnimesh.C

Go to the documentation of this file.
00001 // $Id: GfxAnimesh.C 7357 2008-08-07 02:38:57Z furtak $
00002 
00003 // This is an ORTS file (c) Keith Yerex, licensed under the GPL
00004 
00005 #include "GfxGlobal.H"
00006 #include "GameStateModule.H"
00007 #include "GfxAnimesh.H"
00008 #include "GfxModule.H"
00009 #include "CycleCounter.H"
00010 #include "GameObj.H"
00011 #include "Image.H"
00012 #include "Game.H"
00013 #include "MD2.H"
00014 #include "MD3.H"
00015 #include "Profiler.H"
00016 
00017 #include "GfxEmitter.H"
00018 
00019 #ifndef GCC
00020 static sint4 round(real8 x) { return (sint4)floor(x+0.5); }
00021 #endif
00022 
00023 using namespace std;
00024 
00025 //===================================================================
00026 
00027 map<string, Model::TexPair> Model::textures;
00028 Vector<Vec4<real4> > Model::colors;
00029 
00030 map<string, Model*> GfxAnimesh::name2mdl;
00031 map<string, CustomShaderProgram*> GfxAnimesh::name2shader; // modelfile -> shader
00032 CustomShaderProgram* GfxAnimesh::default_shader = 0;
00033 real4 GfxAnimesh::gamma = 1.0; // gamma correct all model textures by this value
00034 sint4 GfxAnimesh::mode = 0;
00035 sint4 GfxAnimesh::texmode = 2;
00036 sint4 GfxAnimesh::texlevel = 0;
00037 GLuint GfxAnimesh::default_shadow_tex = 0;
00038 
00039 bool Model::clear_fast = false;
00040 bool Model::verbose_load = false;
00041 
00042 //===================================================================
00043 
00044 GfxAnimesh::GfxAnimesh(GfxModule &g, const std::string &modelfile, const std::string &texfile,
00045                        sint4 team, real4 scale, real4 dy)
00046 {
00047   set_gfxm(g);
00048   shadow_tex = default_shadow_tex;
00049   shadow_on = 0;
00050   shadow_sc = 0.0;
00051 
00052   mdl = 0;
00053   shader = 0;
00054 
00055   load(modelfile, texfile, team, scale, dy);
00056 }
00057 
00058 //===================================================================
00059 
00060 void GfxAnimesh::load(const string &modelfile, const string &texfile, sint4 team_,
00061                       real4 scale_, real4 dy)
00062 {
00063   PROFILE("GfxAnimesh::load");
00064 
00065   team = team_;
00066   map<string, Model*>::const_iterator it;
00067 
00068   if ((it = name2mdl.find(modelfile)) != name2mdl.end()) mdl = it->second;
00069   else {
00070 
00071     string ext;
00072     size_t p = modelfile.rfind(".");
00073     if (p != string::npos) {
00074       ext = modelfile.substr(p+1);
00075     }
00076 
00077     if (ext == "md3") {
00078       switch(mode) {
00079       case 1:  mdl = new MD3_Model; break;
00080         //case 2:  mdl = new MD3_Model_VB; break;
00081       default: mdl = new MD3_Model_DL; break;
00082       }
00083       
00084     } else if (ext == "md2"){
00085       switch(mode) {
00086       case 1:  mdl = new MD2_Model; break;
00087       case 2:  mdl = new MD2_Model_VB; break;
00088       default: mdl = new MD2_Model_DL; break;
00089       }
00090     } else {
00091       ERR2("unknown extension:", modelfile.c_str());
00092     }
00093     mdl->load(modelfile, texfile, 100.0, dy, gamma);
00094     name2mdl[modelfile] = mdl;
00095 
00096     CustomShaderProgram *csp = new CustomShaderProgram;
00097     if (csp->load(modelfile + ".custom")) {
00098       name2shader[modelfile] = csp;
00099     } else {
00100       ShaderProgram *sp = new ShaderProgram();
00101       sp->vertex_shader(modelfile + ".vert");
00102       sp->fragment_shader(modelfile + ".frag");
00103       if (sp->id()) {
00104         csp->load(sp);
00105         name2shader[modelfile] = csp;
00106       } else {
00107         delete sp;
00108         delete csp;
00109       }
00110     }
00111   }
00112 
00113   {
00114     FIND (name2shader, it2, modelfile);
00115     if (it2 != name2shader.end()) shader = it2->second;
00116   }
00117 
00118   centroid = mdl->centroid;
00119   radius = mdl->radius;
00120   set_scale(scale_ * 0.01);
00121 
00122   idle.frame = 0;
00123   idle.first = 0;
00124   idle.loop_first = 0;
00125   idle.last = 0;
00126   idle.fps = 0;
00127   idle.mode = FrameSet::REPEAT;
00128   idle.f1 = 0;
00129   idle.f2 = 0;
00130   idle.blend = 0.0;
00131 
00132   current.frame = 0;
00133   current.first = 0;
00134   current.loop_first = 0;
00135   current.last = 0;
00136   current.fps = 0;
00137   current.mode = FrameSet::STOP;
00138   current.f1 = 0;
00139   current.f2 = 0;
00140   current.blend = 0.0;
00141 }
00142 
00143 //===================================================================
00144 
00145 GfxAnimesh::~GfxAnimesh()
00146 {
00147 }
00148 
00149 //===================================================================
00150 
00151 void GfxAnimesh::update(real4 dt)
00152 {
00153   PROFILE("GfxAnimesh::update");
00154 
00155   if (!current.update(dt)) current = idle;
00156 
00157   assert(mdl);
00158   if (current.f1 >= (sint4)mdl->bbox.size()) return;
00159 
00160   aabb_o = pose.center + mdl->bbox[current.f1].min * pose.scale;
00161   aabb_d = (mdl->bbox[current.f1].max - mdl->bbox[current.f1].min) * pose.scale; 
00162 
00163   FORALL (sub_objects, it) (*it)->update(dt);
00164   FORALL (emitters, it) (*it)->update(dt);
00165 
00166   FORALL (sub_objects_tag, it) (*it).obj->update(dt);
00167   FORALL (emitters_tag, it) (*it).obj->update(dt);
00168 }
00169 
00170 //===================================================================
00171 
00172 void GfxAnimesh::beginDraw()
00173 {
00174   glEnable(GL_BLEND);
00175   glDisable(GL_LIGHTING);
00176   glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
00177   
00178   if (glBlendEquationEXT) {
00179     glBlendEquationEXT(GL_FUNC_ADD_EXT);
00180   } else {
00181     glBlendEquation(GL_FUNC_ADD);   
00182   }
00183   
00184   glCullFace(GL_FRONT);
00185 
00186   glActiveTextureARB(GL_TEXTURE0);
00187   glEnable(GL_TEXTURE_2D);
00188   glColor4f(1, 1, 1, 1);
00189 }
00190 
00191 //-------------------------------------------------------------------
00192 
00193 void GfxAnimesh::endDraw()
00194 {
00195   glDisable(GL_BLEND);
00196   glActiveTextureARB(GL_TEXTURE0);
00197   glDisable(GL_TEXTURE_2D);
00198 
00199   glCullFace(GL_BACK);
00200 
00201   glDisable(GL_LIGHTING);
00202 }
00203 
00204 //===================================================================
00205 
00206 void GfxAnimesh::draw()
00207 {
00208   PROFILE("GfxAnimesh::draw");
00209 
00210   glerror("GfxAnimesh::draw 0");
00211   glLoadName(my_id << GfxObject::SUB_ID_LOG);
00212   
00213   glPushMatrix();
00214   apply_pose();
00215 
00216   CustomShaderProgram *prog;
00217   if (shader) {
00218     prog = shader;
00219   } else {
00220     prog = default_shader;
00221   }
00222 
00223   if (prog) {
00224 
00225     glActiveTextureARB(GL_TEXTURE2);
00226     glBindTexture(GL_TEXTURE_2D, mdl->tex->team_tex);
00227     glActiveTextureARB(GL_TEXTURE0);
00228     glBindTexture(GL_TEXTURE_2D, mdl->tex->base_tex);
00229     glColor4fv(mdl->get_color(team).ptr());
00230 
00231     FORT (i, prog->num_passes()) {
00232       ShaderProgram *sp = prog->get_pass(i).prog;
00233       if (!sp || !sp->begin()) continue;
00234       prog->get_pass(i).setup();
00235 
00236       GLint tex = glGetUniformLocation(sp->id(), "tex");
00237       //      cout << "tex = " << tex << endl;
00238       if (tex >= 0) glUniform1i(tex, 0);
00239       
00240       GLint shadow = glGetUniformLocation(sp->id(), "shadow");
00241       if (shadow >= 0) glUniform1i(shadow, 1);
00242       
00243       GLint team = glGetUniformLocation(sp->id(), "team");
00244       if (team >= 0) glUniform1i(team, 2);
00245       
00246       GLint ntex = glGetUniformLocation(sp->id(), "ntex");
00247       if (ntex >= 0) glUniform1i(ntex, 3);
00248 
00249       mdl->draw(current.f1);
00250       
00251       sp->end();
00252     }
00253 
00254   } else {
00255     
00256     if (current.first == current.last) {
00257       mdl->draw(current.first, team);
00258     } else {
00259       mdl->draw_interp(current.f1, current.f2, current.blend, team);
00260     }
00261 
00262   }
00263 
00264   FORALL (sub_objects, it) (*it)->draw();
00265 
00266   FORALL (sub_objects_tag, it) {
00267     GfxObject *o = (*it).obj;
00268     sint4 tag_num = (*it).tag_num;
00269     Mat4<real4> &mat = mdl->tag[tag_num][current.f1].axis;
00270     o->draw_at(mat);
00271   }
00272 
00273   glPopMatrix();
00274   glerror("GfxAnimesh::draw 1");
00275 }
00276 
00277 //===================================================================
00278 
00279 void GfxAnimesh::draw_emitters()
00280 {
00281   PROFILE("GfxAnimesh::draw_emitters");
00282 
00283   glerror("GfxObject::draw_emitters 0");
00284 
00285   glPushMatrix();
00286   apply_pose();
00287 
00288   FORALL (emitters, i) (*i)->draw_emitters();
00289 
00290   //  glScalef(pose.scale, pose.scale, pose.scale);
00291 
00292   FORALL (sub_objects, i) (*i)->draw_emitters();
00293 
00294   FORALL (sub_objects_tag, it) {
00295     GfxObject *o = (*it).obj;
00296     sint4 tag_num = (*it).tag_num;
00297     Mat4<real4> &mat = mdl->tag[tag_num][current.f1].axis;
00298     o->draw_emitters_at(mat);
00299   }
00300 
00301   FORALL (emitters_tag, it) {
00302     GfxObject *o = (*it).obj;
00303     sint4 tag_num = (*it).tag_num;
00304     Mat4<real4> &mat = mdl->tag[tag_num][current.f1].axis;
00305     o->draw_emitters_at(mat);
00306   }
00307 
00308   glPopMatrix();
00309   glerror("GfxObject::draw_emitters 1");
00310 }
00311 
00312 //===================================================================
00313 
00314 void GfxAnimesh::pick()
00315 {
00316   glerror("GfxAnimesh::pick 0");
00317   glCullFace(GL_FRONT);
00318   glLoadName(my_id << GfxObject::SUB_ID_LOG);
00319   
00320   glPushMatrix();
00321   apply_pose();
00322 
00323   mdl->select(current.f1);
00324 
00325   FORALL (sub_objects, it) (*it)->pick();
00326 
00327   FORALL (sub_objects_tag, it) {
00328     GfxObject *o = (*it).obj;
00329     sint4 tag_num = (*it).tag_num;
00330     Mat4<real4> &mat = mdl->tag[tag_num][current.f1].axis;
00331 
00332     glPushMatrix();
00333     glMultMatrixf(mat.ptr());
00334     o->pick();
00335     glPopMatrix();
00336   }
00337 
00338   glPopMatrix();
00339   glCullFace(GL_BACK);
00340   glerror("GfxAnimesh::pick 1");
00341 }
00342 
00343 //===================================================================
00344 
00345 void GfxAnimesh::draw_shadow()
00346 {
00347   PROFILE("GfxAnimesh::draw_shadow");
00348 
00349   if (!shadow_on) return;
00350   glerror("GfxAnimesh::drawshadow 0");
00351   glPushMatrix();
00352   apply_pose();
00353 
00354   if (shadow_tex > 0) {
00355 
00356     glColor4f(1, 1, 1, 1);
00357     glActiveTextureARB(GL_TEXTURE0);
00358     glEnable(GL_TEXTURE_2D);
00359     glBindTexture(GL_TEXTURE_2D, shadow_tex);
00360     mdl->draw_shadow(current.f1, shadow_sc);
00361 
00362   } else {
00363 
00364     glColor4f(0, 0, 0, 0.5);
00365     glActiveTextureARB(GL_TEXTURE1);
00366     glDisable(GL_TEXTURE_2D);
00367     glActiveTextureARB(GL_TEXTURE0);
00368     glDisable(GL_TEXTURE_2D);
00369 
00370     mdl->draw(current.f1);
00371   }
00372 
00373   FORALL (sub_objects, it) (*it)->draw_shadow();
00374 
00375   FORALL (sub_objects_tag, it) {
00376     GfxObject *o = (*it).obj;
00377     sint4 tag_num = (*it).tag_num;
00378     Mat4<real4> &mat = mdl->tag[tag_num][current.f1].axis;
00379 
00380     glPushMatrix();
00381     glMultMatrixf(mat.ptr());
00382     o->draw_shadow();
00383     glPopMatrix();
00384   }
00385 
00386 
00387   glPopMatrix();
00388   glerror("GfxAnimesh::drawshadow");
00389 }
00390 
00391 //===================================================================
00392 
00393 void GfxAnimesh::draw_bbox()
00394 {
00395   glDisable(GL_TEXTURE_2D);
00396   glDisable(GL_CULL_FACE);
00397   glDisable(GL_BLEND);
00398   
00399   real4 x0 = aabb_o.x;
00400   real4 x1 = aabb_o.x + aabb_d.x;
00401   real4 y0 = aabb_o.y;
00402   real4 y1 = aabb_o.y + aabb_d.y;
00403   real4 z0 = aabb_o.z;
00404   real4 z1 = aabb_o.z + aabb_d.z;
00405   
00406   glColor4f(1,1,1,1);
00407   glBegin(GL_LINES);
00408   glVertex3f(x0, y0, z0);
00409   glVertex3f(x0, y0, z1);
00410   glVertex3f(x0, y1, z0);
00411   glVertex3f(x0, y1, z1);
00412   glVertex3f(x1, y0, z0);
00413   glVertex3f(x1, y0, z1);
00414   glVertex3f(x1, y1, z0);
00415   glVertex3f(x1, y1, z1);
00416   
00417   glVertex3f(x0, y0, z0);
00418   glVertex3f(x0, y1, z0);
00419   glVertex3f(x0, y0, z1);
00420   glVertex3f(x0, y1, z1);
00421   glVertex3f(x1, y0, z0);
00422   glVertex3f(x1, y1, z0);
00423   glVertex3f(x1, y0, z1);
00424   glVertex3f(x1, y1, z1);
00425   
00426   glVertex3f(x0, y0, z0);
00427   glVertex3f(x1, y0, z0);
00428   glVertex3f(x0, y0, z1);
00429   glVertex3f(x1, y0, z1);
00430   glVertex3f(x0, y1, z0);
00431   glVertex3f(x1, y1, z0);
00432   glVertex3f(x0, y1, z1);
00433   glVertex3f(x1, y1, z1);
00434   
00435   glEnd();
00436 }
00437 
00438 //===================================================================
00439 
00440 void GfxAnimesh::play(const string &name, real4 fps, sint2 mode, sint2 looping)
00441 {
00442   pair<sint2,sint2> p = mdl->name2frames[name];
00443   play(p.first, p.second, fps, mode, looping);
00444 }
00445 
00446 //-------------------------------------------------------------------
00447 
00448 void GfxAnimesh::play(sint2 start, sint2 end, real4 fps, sint2 mode, sint2 looping)
00449 {
00450   current.fps = fps;
00451   current.mode = mode;
00452   current.first = start;
00453   if (looping >= 0 && looping < (end-start+1))
00454     current.loop_first = end - looping + 1;
00455   else
00456     current.loop_first = start;
00457   current.last = end;
00458   current.is_custom = false;
00459   if (fps >= 0) {
00460     current.frame = static_cast<real4>(current.first);
00461     current.f1 = current.f2 = current.first;
00462   } else {
00463     current.frame = static_cast<real4>(current.last) + 0.99;
00464     current.f1 = current.f2 = current.last;
00465   }
00466 }
00467 
00468 //-------------------------------------------------------------------
00469 
00470 void GfxAnimesh::play_custom(const Vector<sint4> &f, real4 fps, sint2 mode)
00471 {
00472   current.fps = fps;
00473   current.mode = mode;
00474   current.set_custom(f);
00475 }
00476 
00477 //===================================================================
00478 
00479 void GfxAnimesh::set_idle(const string &name, real4 fps, sint2 mode, sint2 looping)
00480 {
00481   pair<sint2,sint2> p = mdl->name2frames[name];
00482   
00483   idle.fps = fps;
00484   idle.mode = mode;
00485   idle.first = p.first;
00486   idle.last = p.second;
00487   if (looping >= 0 && looping < (idle.last - idle.first + 1))
00488     idle.loop_first = idle.last - looping + 1;
00489   else
00490     idle.loop_first = idle.first;
00491   idle.is_custom = false;
00492   if (fps >= 0) {
00493     idle.frame = static_cast<real4>(idle.first);
00494     idle.f1 = idle.f2 = idle.first;
00495   } else {
00496     idle.frame = static_cast<real4>(idle.last) + 0.99;
00497     idle.f1 = idle.f2 = idle.last;
00498   }
00499 }
00500 
00501 //===================================================================
00502 
00503 void GfxAnimesh::set_frame(const string &name, sint2 frame)
00504 {
00505   pair<sint2,sint2> p = mdl->name2frames[name];
00506 
00507   current.mode = FrameSet::HALT;
00508   current.fps = 0;
00509   current.f1 = frame + p.first;
00510   current.f2 = frame + p.first;
00511   current.blend = 0.0;
00512   current.frame = static_cast<real4>(frame + p.first);
00513   current.first = p.first;
00514   current.last  = p.second;
00515 }
00516 
00517 //===================================================================
00518 
00519 void GfxAnimesh::attachGfxObject_tag(GfxObject *go, const string &tag)
00520 {
00521   if (!mdl) {
00522     cout << "no model" << endl;
00523     return;
00524   }
00525 
00526   sint4 tag_id = -1;
00527   FORALL (mdl->tag_name, i) {
00528     if (*i == tag) {
00529       tag_id = i-mdl->tag_name.begin();
00530       break;
00531     }
00532   }
00533   if (tag_id == -1) {
00534     cout << "no such tag" << endl;
00535     return;
00536   }
00537 
00538   GfxTag gt;
00539   gt.obj = go;
00540   gt.tag_num = tag_id;
00541 
00542   sub_objects_tag.push_back(gt);
00543 }
00544 
00545 //-------------------------------------------------------------------
00546 
00547 void GfxAnimesh::attachEmitter_tag(GfxObject *go, const string &tag)
00548 {
00549   if (!mdl) return;
00550 
00551   sint4 tag_id = -1;
00552   FORALL (mdl->tag_name, i) {
00553     if (*i == tag) {
00554       tag_id = i - mdl->tag_name.begin();
00555       break;
00556     }
00557   }
00558   if (tag_id == -1) return;
00559 
00560   GfxTag gt;
00561   gt.obj = go;
00562   gt.tag_num = tag_id;
00563 
00564   emitters_tag.push_back(gt);
00565 }
00566 
00567 //===================================================================
00568 
00569 Model::~Model()
00570 {
00571   delete [] all_tags;
00572 }
00573 
00574 //-------------------------------------------------------------------
00575 
00576 sint4 Model::load_textures(const string &filename, real4 gamma)
00577 {
00578   PROFILE("Model::load_textures");
00579 
00580   TexPair &tp = textures[filename];
00581   tex = &tp;
00582   if (tp.base_tex) return 1; // already loaded
00583 
00584   // Mesh texture
00585   string fileTex = filename + ".tga";
00586   Image imgTex(fileTex.c_str(), gamma);
00587 
00588   // Team colour texture
00589   string fileTeam = filename + "_color.tga";
00590   Image imgTeam(fileTeam.c_str(), gamma);
00591   uint1 *color_data = imgTeam.pixels();
00592   
00593   GLuint type;
00594   
00595   switch (imgTex.bpp()) {
00596   case 1:  type = GL_LUMINANCE; break;
00597   case 3:  type = GL_RGB;       break;
00598   case 4:  type = GL_RGBA;      break;
00599   default: ERR("bpp not handled");
00600   }
00601   
00602   //----------------------------------------
00603 tp.base_tex=imgTex.createTexture(static_cast<Image::FILTER>(GfxAnimesh::get_texmode()),
00604                  GfxAnimesh::get_texlevel());
00605   
00606   if (imgTeam.bpp() == 1) { // no alpha for the team color map
00607     Image aTeam;
00608     aTeam.empty(imgTeam.width(), imgTeam.height(), 2); // with alpha
00609     uint1 *adata = aTeam.pixels();
00610     
00611     FORU (i, imgTeam.width() * imgTeam.height()) {
00612       uint1 l = color_data[i];
00613       adata[i*2] = l;
00614       adata[i*2+1] = l ? 255 : 0;
00615     }
00616     
00617 tp.team_tex=aTeam.createTexture(static_cast<Image::FILTER>(GfxAnimesh::get_texmode()),
00618                 GfxAnimesh::get_texlevel());
00619     
00620   } else if (imgTeam.bpp() == 2) {
00621 tp.team_tex=imgTeam.createTexture(static_cast<Image::FILTER>(GfxAnimesh::get_texmode()),
00622                   GfxAnimesh::get_texlevel());
00623   } else {
00624     ERR("invalid color map bpp");
00625   }
00626   //----------------------------------------
00627 
00628   return 1;
00629 }
00630 
00631 //===================================================================
00632 
00633 void FrameSet::set_custom(const Vector<sint4> &v)
00634 {
00635   frame = 0;
00636   first = 0;
00637   loop_first = 0;
00638   last = v.size() - 1;
00639   
00640   is_custom = true;
00641   custom.clear();
00642   FORALL (v, i) custom.push_back(*i);
00643   custom.push_back(custom[0]);
00644   
00645   f1 = custom[0];
00646   f2 = custom[1];
00647   blend = 0.0;
00648 }
00649 
00650 //-------------------------------------------------------------------
00651 
00652 bool FrameSet::update(real4 dt)
00653 {
00654   frame += dt * fps;
00655 
00656   if (frame >= last+1.0 || frame < first) {
00657       
00658     switch (mode) {
00659       
00660     case FrameSet::STOP:
00661       return false;
00662       
00663     case FrameSet::REPEAT:
00664       first = loop_first;
00665       if (first == last) frame = first;
00666       else {
00667         if (fps > 0)
00668           do {
00669             frame -= (last-first+1);
00670           } while (frame >= last+1.0);
00671         else
00672           do {
00673             frame += (last-first+1);
00674           } while (frame < first);
00675       }
00676       break;
00677       
00678     case FrameSet::MIRROR:
00679       fps *= -1;
00680       frame += dt*fps;
00681       break;
00682       
00683     case FrameSet::HALT:
00684       if (fps > 0) {
00685         frame = last;
00686       } else if (fps < 0) {
00687         frame = first;
00688       }
00689       fps = 0;
00690       break;
00691     }
00692   }
00693 
00694   if (fps == 0) {
00695 
00696     f1 = f2 = static_cast<sint2>(frame);
00697     blend = 0;
00698 
00699   } else if (fps > 0) {
00700 
00701     sint4 f = static_cast<sint4>(floor(frame));
00702     f1 = f;
00703     f2 = f+1;
00704     if (f2 > last) {
00705       if (mode == FrameSet::REPEAT) {
00706         f2 = first;
00707       } else {
00708         f2 = last;
00709       }
00710     }
00711     blend = frame - f;
00712 
00713   } else {
00714 
00715     //    sint4 f = (sint4)ceil(frame);
00716     sint4 f = static_cast<sint4>(floor(frame));
00717     f1 = f;
00718     f2 = f-1;
00719     if (f2 < first) {
00720       if (mode == FrameSet::REPEAT) {
00721         f2 = last;
00722       } else {
00723         f2 = first;
00724       }
00725     }
00726     blend = f - frame;
00727 
00728   }
00729 
00730   if (is_custom) {
00731     f1 = custom[f1];
00732     f2 = custom[f2];
00733   }
00734 
00735   return true;
00736 }
00737 
00738 //===================================================================


Generated on Fri May 18 2012 03:02:39 for ORTS by Doxygen1.7.3