Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

Changeset 9869 in orxonox.OLD for trunk/src/lib/graphics/importer/md2


Ignore:
Timestamp:
Oct 3, 2006, 12:19:30 AM (18 years ago)
Author:
bensch
Message:

orxonox/trunk: merged the new_class_id branche back to the trunk.
merged with command:
svn merge https://svn.orxonox.net/orxonox/branches/new_class_id trunk -r9683:HEAD
no conflicts… puh..

Location:
trunk/src/lib/graphics/importer/md2
Files:
2 edited
2 copied

Legend:

Unmodified
Added
Removed
  • trunk/src/lib/graphics/importer/md2/md2Model.cc

    r9406 r9869  
    1919
    2020#include "debug.h"
    21 #include "util/loading/resource_manager.h"
    22 
    23 
    24 
     21#include "resource_md2.h"
     22
     23
     24ObjectListDefinition(MD2Model);
    2525
    2626//! the model anorms
    2727sVec3D MD2Model::anorms[NUM_VERTEX_NORMALS] = {
    28  #include "anorms.h"
    29 };
     28#include "anorms.h"
     29    };
    3030
    3131//! anormal dots, no idea of how this shall work, but it does
    3232float MD2Model::anormsDots[SHADEDOT_QUANT][256] = {
    33   #include "anormtab.h"
    34 };
     33#include "anormtab.h"
     34    };
    3535
    3636
     
    4242sAnim MD2Model::animationList[22] =
    4343  {
    44  // begin, end, fps, interruptable
     44    // begin, end, fps, interruptable
    4545    {   0,  39,  9, 1 },   //!< STAND
    4646    {  40,  45, 10, 1 },   //!< RUN
     
    7373 ********************************************************************************/
    7474
     75MD2Model::MD2Model()
     76  : data(new MD2Data())
     77{
     78  this->init();
     79}
     80
     81#include "resource_md2.h"
    7582/**
    7683  \brief simple constructor initializing all variables
    7784*/
    7885MD2Model::MD2Model(const std::string& modelFileName, const std::string& skinFileName, float scale)
    79 {
    80   this->setClassID(CL_MD2_MODEL, "MD2Model");
    81   /* this creates the data container via ressource manager */
    82   if (!modelFileName.empty())
    83     this->data = (MD2Data*)ResourceManager::getInstance()->load(modelFileName, MD2, RP_GAME, skinFileName, scale);
    84   if( unlikely(this->data == NULL))
    85     PRINTF(0)("The model was not found, MD2Model Loader finished abnormaly. Update the data-repos\n");
    86 
     86    : data(new MD2Data())
     87{
     88  this->init();
    8789  this->scaleFactor = scale;
    88   this->animationSpeed = 1.0f;
    89 
    90   shadeDots = MD2Model::anormsDots[0];
    91   /* set the animation stat mannualy */
    92   this->animationState.type = STAND;
    93   this->animationState.numPlays = 1;
    94   this->setAnimation(STAND);
    95 
    96   this->debug();
    97 
    98     //write the modelinfo information
     90
     91  this->data = ResourceMD2(modelFileName, skinFileName, scale).data;
     92  rebuildInfo();
     93
     94  //this->debug();
     95}
     96
     97MD2Model::MD2Model(const MD2Model& model)
     98  : data(model.data)
     99{
     100  this->init();
     101  this->rebuildInfo();
     102}
     103
     104MD2Model& MD2Model::operator=(const MD2Model& md2model)
     105{
     106  this->data = md2model.data;
     107  this->rebuildInfo();
     108  return *this;
     109}
     110
     111void MD2Model::rebuildInfo()
     112{
     113  //write the modelinfo information
    99114  this->pModelInfo.numVertices = this->data->numVertices;
    100115  this->pModelInfo.numTriangles = this->data->numTriangles;
     
    107122
    108123  // triangle conversion
     124  if (this->pModelInfo.pTriangles != NULL)
     125    delete[] this->pModelInfo.pTriangles;
    109126  this->pModelInfo.pTriangles = new sTriangleExt[this->data->numTriangles];
    110127  for( int i = 0; i < this->data->numTriangles; i++)
     
    118135    this->pModelInfo.pTriangles[i].indexToTexCoor[2] = this->data->pTriangles[i].indexToTexCoor[2];
    119136  }
     137}
     138
     139bool MD2Model::load(const std::string& modelFileName, const std::string& skinFileName, float scale)
     140{
     141  this->data = MD2Data::Pointer(new MD2Data(modelFileName, skinFileName, scale));
     142  this->rebuildInfo();
    120143}
    121144
     
    132155  this->pModelInfo.pTexCoor = NULL;
    133156  this->pModelInfo.pTriangles = NULL;
    134 
    135   ResourceManager::getInstance()->unload(this->data);
    136 }
    137 
     157}
     158
     159
     160void MD2Model::init()
     161{
     162  this->registerObject(this, MD2Model::_objectList);
     163  /* this creates the data container via ressource manager */
     164  if( unlikely(this->data.isNull()))
     165    PRINTF(0)("The model was not found, MD2Model Loader finished abnormaly. Update the data-repos\n");
     166
     167  this->scaleFactor = 1.0f;
     168  this->animationSpeed = 1.0f;
     169
     170  shadeDots = MD2Model::anormsDots[0];
     171  /* set the animation stat mannualy */
     172  this->animationState.type = STAND;
     173  this->animationState.numPlays = 1;
     174  this->setAnimation(STAND);
     175}
    138176
    139177/**
     
    153191
    154192  for( int i = 0; i < this->data->numVertices; ++i)
    155     {
    156       this->verticesList[i][0] = currVec[i][0] + this->animationState.interpolationState * (nextVec[i][0] - currVec[i][0]);
    157       this->verticesList[i][1] = currVec[i][1] + this->animationState.interpolationState * (nextVec[i][1] - currVec[i][1]);
    158       this->verticesList[i][2] = currVec[i][2] + this->animationState.interpolationState * (nextVec[i][2] - currVec[i][2]);
    159     }
     193  {
     194    this->verticesList[i][0] = currVec[i][0] + this->animationState.interpolationState * (nextVec[i][0] - currVec[i][0]);
     195    this->verticesList[i][1] = currVec[i][1] + this->animationState.interpolationState * (nextVec[i][1] - currVec[i][1]);
     196    this->verticesList[i][2] = currVec[i][2] + this->animationState.interpolationState * (nextVec[i][2] - currVec[i][2]);
     197  }
    160198}
    161199
     
    259297  /* draw the triangles */
    260298  while( int i = *(pCommands++)) /* strange looking while loop for maximum performance */
     299  {
     300    if( i < 0)
    261301    {
    262       if( i < 0)
    263         {
    264           glBegin(GL_TRIANGLE_FAN);
    265           i = -i;
    266         }
    267       else
    268         {
    269           glBegin(GL_TRIANGLE_STRIP);
    270         }
    271 
    272       for(; i > 0; i--, pCommands += 3) /* down counting for loop, next 3 gl commands */
    273         {
    274           glTexCoord2f( ((float *)pCommands)[0], ((float *)pCommands)[1] );
    275           glNormal3fv(anorms[this->data->pLightNormals[pCommands[2]]]);
    276           glVertex3fv(this->verticesList[pCommands[2]]);
    277         }
    278       glEnd();
    279 
     302      glBegin(GL_TRIANGLE_FAN);
     303      i = -i;
    280304    }
     305    else
     306    {
     307      glBegin(GL_TRIANGLE_STRIP);
     308    }
     309
     310    for(; i > 0; i--, pCommands += 3) /* down counting for loop, next 3 gl commands */
     311    {
     312      glTexCoord2f( ((float *)pCommands)[0], ((float *)pCommands)[1] );
     313      glNormal3fv(anorms[this->data->pLightNormals[pCommands[2]]]);
     314      glVertex3fv(this->verticesList[pCommands[2]]);
     315    }
     316    glEnd();
     317
     318  }
    281319  glDisable(GL_CULL_FACE);
    282320  glPopAttrib();
     
    289327  int* pCommands = this->data->pGLCommands;
    290328  /* some face culling stuff */
    291 //   glPushAttrib(GL_POLYGON_BIT);
    292 //   glFrontFace(GL_CW);
    293 //   glEnable(GL_CULL_FACE);
    294 //   glCullFace(GL_BACK);
    295 //
    296 //   this->processLighting();
    297 //   this->interpolate(/*this->verticesList*/);
     329  //   glPushAttrib(GL_POLYGON_BIT);
     330  //   glFrontFace(GL_CW);
     331  //   glEnable(GL_CULL_FACE);
     332  //   glCullFace(GL_BACK);
     333  //
     334  //   this->processLighting();
     335  //   this->interpolate(/*this->verticesList*/);
    298336  this->data->material.select();
    299337
     
    337375
    338376  if( this->animationState.localTime - this->animationState.lastTime > (1.0f / this->animationState.fps))
     377  {
     378    this->animationState.currentFrame = this->animationState.nextFrame;
     379    this->animationState.nextFrame++;
     380
     381    if( this->animationState.nextFrame > this->animationState.endFrame )
    339382    {
    340       this->animationState.currentFrame = this->animationState.nextFrame;
    341       this->animationState.nextFrame++;
    342 
    343       if( this->animationState.nextFrame > this->animationState.endFrame )
     383      if( this->animationState.animPlaybackMode == MD2_ANIM_LOOP)
    344384      {
    345         if( this->animationState.animPlaybackMode == MD2_ANIM_LOOP)
    346         {
    347           this->animationState.nextFrame = this->animationState.startFrame;
    348           this->animationState.numPlays++;
    349         }
    350         else
    351         {
    352           this->animationState.nextFrame = this->animationState.endFrame;
    353         }
     385        this->animationState.nextFrame = this->animationState.startFrame;
     386        this->animationState.numPlays++;
    354387      }
    355       this->animationState.lastTime = this->animationState.localTime;
     388      else
     389      {
     390        this->animationState.nextFrame = this->animationState.endFrame;
     391      }
    356392    }
    357 
    358 //     if( this->animationState.currentFrame > (this->data->numFrames - 1) )
    359 //       this->animationState.currentFrame = 0;
    360 
    361 //     if( (this->animationState.nextFrame > (this->data->numFrames - 1)) && this->animationState.animPlaybackMode == MD2_ANIM_LOOP)
    362 //     this->animationState.nextFrame = 0;
     393    this->animationState.lastTime = this->animationState.localTime;
     394  }
     395
     396  //     if( this->animationState.currentFrame > (this->data->numFrames - 1) )
     397  //       this->animationState.currentFrame = 0;
     398
     399  //     if( (this->animationState.nextFrame > (this->data->numFrames - 1)) && this->animationState.animPlaybackMode == MD2_ANIM_LOOP)
     400  //     this->animationState.nextFrame = 0;
    363401
    364402  this->animationState.interpolationState = this->animationState.fps *
    365     (this->animationState.localTime - this->animationState.lastTime);
     403      (this->animationState.localTime - this->animationState.lastTime);
    366404}
    367405
     
    390428  PRINT(0)("=  Number of Frames: \t%i\n", this->data->header->numFrames);
    391429  PRINT(0)("=  Height, Width:\t%i, %i\n", this->data->header->skinHeight, this->data->header->skinWidth);
    392   PRINT(0)("=  Pointer to the data object: %p\n", this->data);
     430  //PRINT(0)("=  Pointer to the data object: %p\n", this->data);
    393431  PRINT(0)("===================================================\n\n");
    394432}
     
    399437 ********************************************************************************/
    400438
     439MD2Data::MD2Data()
     440{
     441  this->init();
     442}
     443
    401444/**
    402445  \brief simple constructor
     
    404447MD2Data::MD2Data(const std::string& modelFileName, const std::string& skinFileName, float scale)
    405448{
    406   scale *= 0.1f;
    407 
     449  this->init();
     450  this->scaleFactor = scale * .1;
     451
     452  this->loadModel(modelFileName);
     453  this->loadSkin(skinFileName);
     454}
     455
     456void MD2Data::init()
     457{
     458  this->scaleFactor = 1.0;
    408459  this->pVertices = NULL;
    409460  this->pGLCommands = NULL;
    410461  this->pLightNormals = NULL;
    411462  this->pTexCoor = NULL;
     463  this->header = NULL;
    412464
    413465  this->numFrames = 0;
     
    415467  this->numGLCommands = 0;
    416468  this->numTexCoor = 0;
    417 
    418 //   this->scaleFactor = 1.0f;
    419   this->scaleFactor = scale;
    420 
    421   this->fileName = "";
    422   this->skinFileName = "";
    423   this->loadModel(modelFileName);
    424   this->loadSkin(skinFileName);
    425 }
    426 
     469}
    427470
    428471/**
     
    462505  pFile = fopen(fileName.c_str(), "rb");
    463506  if( unlikely(!pFile))
    464     {
    465       PRINTF(1)("Couldn't open the MD2 File for loading. Exiting.\n");
    466       return false;
    467     }
     507  {
     508    PRINTF(1)("Couldn't open the MD2 File for loading. Exiting.\n");
     509    return false;
     510  }
    468511  this->header = new MD2Header;
    469512  fread(this->header, 1, sizeof(MD2Header), pFile);
    470513  /* check for the header version: make sure its a md2 file :) */
    471514  if( unlikely(this->header->version != MD2_VERSION) && unlikely(this->header->ident != MD2_IDENT))
    472     {
    473       PRINTF(1)("Couldn't load file %s: invalid file format: stop loading\n", fileName.c_str());
    474       return false;
    475     }
     515  {
     516    PRINTF(1)("Couldn't load file %s: invalid file format: stop loading\n", fileName.c_str());
     517    return false;
     518  }
    476519
    477520  this->fileName =fileName;
     
    506549
    507550  for(int i = 0; i < this->numFrames; ++i)
     551  {
     552    frame = (sFrame*)(buffer + this->header->frameSize * i);
     553    pVertex = this->pVertices + this->numVertices  * i;
     554    pNormals = this->pLightNormals + this->numVertices * i;
     555
     556    for(int j = 0; j < this->numVertices; ++j)
    508557    {
    509       frame = (sFrame*)(buffer + this->header->frameSize * i);
    510       pVertex = this->pVertices + this->numVertices  * i;
    511       pNormals = this->pLightNormals + this->numVertices * i;
    512 
    513       for(int j = 0; j < this->numVertices; ++j)
    514         {
    515           /* SPEEDUP: *(pVerts + i + 0) = (*(frame->pVertices + i + 0)...  */
    516            pVertex[j][0] = ((frame->pVertices[j].v[0] * frame->scale[0] ) + frame->translate[0] )* this->scaleFactor;
    517            pVertex[j][1] = ((frame->pVertices[j].v[2] * frame->scale[2]) + frame->translate[2]) * this->scaleFactor;
    518            pVertex[j][2] = (-1.0 * (frame->pVertices[j].v[1] * frame->scale[1] + frame->translate[1])) * this->scaleFactor;
    519 
    520           //printf("vertex %i/%i: (%f, %f, %f)\n", j, this->numVertices, pVertex[j][0], pVertex[j][1], pVertex[j][2]);
    521 
    522           pNormals[j] = frame->pVertices[j].lightNormalIndex;
    523         }
     558      /* SPEEDUP: *(pVerts + i + 0) = (*(frame->pVertices + i + 0)...  */
     559      pVertex[j][0] = ((frame->pVertices[j].v[0] * frame->scale[0] ) + frame->translate[0] )* this->scaleFactor;
     560      pVertex[j][1] = ((frame->pVertices[j].v[2] * frame->scale[2]) + frame->translate[2]) * this->scaleFactor;
     561      pVertex[j][2] = (-1.0 * (frame->pVertices[j].v[1] * frame->scale[1] + frame->translate[1])) * this->scaleFactor;
     562
     563      //printf("vertex %i/%i: (%f, %f, %f)\n", j, this->numVertices, pVertex[j][0], pVertex[j][1], pVertex[j][2]);
     564
     565      pNormals[j] = frame->pVertices[j].lightNormalIndex;
    524566    }
    525     PRINTF(4)("Finished loading the md2 file\n");
     567  }
     568  PRINTF(4)("Finished loading the md2 file\n");
    526569
    527570  delete [] buffer;
     
    538581{
    539582  if( fileName.empty())
    540     {
    541       this->skinFileName = "";
    542       return false;
    543     }
     583  {
     584    this->skinFileName = "";
     585    return false;
     586  }
    544587
    545588  this->skinFileName = fileName;
  • trunk/src/lib/graphics/importer/md2/md2Model.h

    r9235 r9869  
    2020
    2121#include "base_object.h"
     22#include "count_pointer.h"
    2223
    2324#include "interactive_model.h"
     
    4546struct MD2Header
    4647{
    47    int ident;                           //!< This is used to identify the file
    48    int version;                         //!< The version number of the file (Must be 8)
    49 
    50    int skinWidth;                       //!< The skin width in pixels
    51    int skinHeight;                      //!< The skin height in pixels
    52    int frameSize;                       //!< The size in bytes the frames are
    53 
    54    int numSkins;                        //!< The number of skins associated with the model
    55    int numVertices;                     //!< The number of vertices (constant for each frame)
    56    int numTexCoords;                    //!< The number of texture coordinates
    57    int numTriangles;                    //!< The number of faces (polygons)
    58    int numGlCommands;                   //!< The number of gl commands
    59    int numFrames;                       //!< The number of animation frames
    60 
    61    int offsetSkins;                     //!< The offset in the file for the skin data
    62    int offsetTexCoords;                 //!< The offset in the file for the texture data
    63    int offsetTriangles;                 //!< The offset in the file for the face data
    64    int offsetFrames;                    //!< The offset in the file for the frames data
    65    int offsetGlCommands;                //!< The offset in the file for the gl commands data
    66    int offsetEnd;                       //!< The end of the file offset
     48  int ident;                           //!< This is used to identify the file
     49  int version;                         //!< The version number of the file (Must be 8)
     50
     51  int skinWidth;                       //!< The skin width in pixels
     52  int skinHeight;                      //!< The skin height in pixels
     53  int frameSize;                       //!< The size in bytes the frames are
     54
     55  int numSkins;                        //!< The number of skins associated with the model
     56  int numVertices;                     //!< The number of vertices (constant for each frame)
     57  int numTexCoords;                    //!< The number of texture coordinates
     58  int numTriangles;                    //!< The number of faces (polygons)
     59  int numGlCommands;                   //!< The number of gl commands
     60  int numFrames;                       //!< The number of animation frames
     61
     62  int offsetSkins;                     //!< The offset in the file for the skin data
     63  int offsetTexCoords;                 //!< The offset in the file for the texture data
     64  int offsetTriangles;                 //!< The offset in the file for the face data
     65  int offsetFrames;                    //!< The offset in the file for the frames data
     66  int offsetGlCommands;                //!< The offset in the file for the gl commands data
     67  int offsetEnd;                       //!< The end of the file offset
    6768};
    6869
     
    7071//! animation names enumeration
    7172typedef enum MD2animType
    72   {
    73     STAND,                       //0
    74     RUN,                         //1
    75     ATTACK,                      //2
    76     PAIN_A,                      //3
    77     PAIN_B,                      //4
    78     PAIN_C,          //5
    79     JUMP,            //6
    80     FLIP,            //7
    81     SALUTE,          //8
    82     FALLBACK,        //9
    83     WAVE,            //10
    84     POINT,           //11
    85     CROUCH_STAND,
    86     CROUCH_WALK,
    87     CROUCH_ATTACK,
    88     CROUCH_PAIN,
    89     CROUCH_DEATH,
    90     DEATH_FALLBACK,
    91     DEATH_FALLFORWARD,
    92     DEATH_FALLBACKSLOW,
    93     BOOM,
    94     WALK,
    95 
    96     MAX_ANIMATIONS
    97   };
     73{
     74  STAND,                       //0
     75  RUN,                         //1
     76  ATTACK,                      //2
     77  PAIN_A,                      //3
     78  PAIN_B,                      //4
     79  PAIN_C,          //5
     80  JUMP,            //6
     81  FLIP,            //7
     82  SALUTE,          //8
     83  FALLBACK,        //9
     84  WAVE,            //10
     85  POINT,           //11
     86  CROUCH_STAND,
     87  CROUCH_WALK,
     88  CROUCH_ATTACK,
     89  CROUCH_PAIN,
     90  CROUCH_DEATH,
     91  DEATH_FALLBACK,
     92  DEATH_FALLFORWARD,
     93  DEATH_FALLBACKSLOW,
     94  BOOM,
     95  WALK,
     96
     97  MAX_ANIMATIONS
     98};
    9899
    99100
     
    116117class MD2Data : public BaseObject
    117118{
    118  public:
    119    MD2Data(const std::string& modelFileName, const std::string& skinFileName, float scale = 1.0f);
     119public:
     120  typedef CountPointer<MD2Data>    Pointer;
     121public:
     122  MD2Data();
     123  MD2Data(const std::string& modelFileName, const std::string& skinFileName, float scale = 1.0f);
    120124  virtual ~MD2Data();
    121125
    122  private:
    123    bool loadModel(const std::string& fileName);
    124    bool loadSkin(const std::string& fileName = "");
    125 
    126  public:
     126private:
     127  void init();
     128  bool loadModel(const std::string& fileName);
     129  bool loadSkin(const std::string& fileName = "");
     130
     131public:
    127132  int                numFrames;             //!< number of frames
    128133  int                numVertices;           //!< number of vertices
     
    148153
    149154//! This is a MD2 Model class
    150 class MD2Model : public InteractiveModel {
    151 
    152 public:
     155class MD2Model : public InteractiveModel
     156{
     157  ObjectListDeclaration(MD2Model);
     158public:
     159  MD2Model();
    153160  MD2Model(const std::string& modelFileName, const std::string& skinFileName = "", float scale = 1.0f);
     161  MD2Model(const MD2Model& model);
    154162  virtual ~MD2Model();
     163
     164  MD2Model& operator=(const MD2Model& md2model);
     165
     166  bool load(const std::string& modelFileName, const std::string& skinFileName = "", float scale = 1.0f);
    155167
    156168  virtual void draw() const;
     
    160172  virtual void setAnimation(int firstFrame, int lastFrame, int fps, int bStoppable, int animPlayback);
    161173  /**  returns the current animation @returns animation type */
    162   inline int MD2Model::getAnimation() { return this->animationState.type; }
     174  inline int getAnimation() const { return this->animationState.type; }
    163175  virtual void setAnimationSpeed(float speed) { this->animationSpeed = speed; }
    164   virtual bool isAnimationFinished() { return (this->animationState.currentFrame == this->animationState.endFrame )?true:false; }
     176virtual bool isAnimationFinished() const { return (this->animationState.currentFrame == this->animationState.endFrame )?true:false; }
    165177  /**  scales the current model @param scaleFactor: the factor [0..1] to use for scaling */
    166178  void scaleModel(float scaleFactor) { this->scaleFactor = scaleFactor;}
    167179
     180
     181  void acquireData(const MD2Data::Pointer& data) { this->data = data; rebuildInfo(); };
     182  const MD2Data::Pointer& dataPointer() const { return this->data; };
    168183  virtual void tick(float dtS);
    169184  void debug();
     
    171186
    172187private:
     188  void init();
     189  void rebuildInfo();
     190
    173191  void animate(float time);
    174192  void processLighting();
     
    177195
    178196
    179  public:
     197public:
    180198  /* these variables are static, because they are all the same for every model */
    181   static sVec3D       anorms[NUM_VERTEX_NORMALS];       //!< the anormals
    182   static float        anormsDots[SHADEDOT_QUANT][256];  //!< the anormals dot products
    183   static sAnim        animationList[22];                //!< the anomation list
    184    //! again one of these strange id software parts
    185   float*              shadeDots;
    186 
    187   MD2Data*            data;                             //!< the md2 data pointer
    188 
    189  private:
    190   float               scaleFactor;                      //!< the scale factor (individual)
    191   float               animationSpeed;                   //!< the speed of the animation (factor for the time)
    192   sAnimState          animationState;                   //!< animation state of the model
    193   sVec3D              verticesList[MD2_MAX_VERTICES];   //!< place to temp sav the vert
     199  static sVec3D         anorms[NUM_VERTEX_NORMALS];       //!< the anormals
     200  static float          anormsDots[SHADEDOT_QUANT][256];  //!< the anormals dot products
     201  static sAnim          animationList[22];                //!< the anomation list
     202  //! again one of these strange id software parts
     203  float*                shadeDots;
     204
     205  CountPointer<MD2Data> data;                           //!< the md2 data pointer
     206
     207private:
     208  float                 scaleFactor;                      //!< the scale factor (individual)
     209  float                 animationSpeed;                   //!< the speed of the animation (factor for the time)
     210  sAnimState            animationState;                   //!< animation state of the model
     211  sVec3D                verticesList[MD2_MAX_VERTICES];   //!< place to temp sav the vert
    194212};
    195213
Note: See TracChangeset for help on using the changeset viewer.