www.pudn.com > fi_treegen10.zip > Tree.cpp


//--------------------------------------------------------------------------- 
// Tree.cpp 
// by Alex ( alex@FusionIndustries.com ) 
// Created May 14, 2001 
// 
// This code may be freely used in any project, as long as credit 
// or greetz are given. Please also let me know if you use this 
// code, find it useful, or even if you don't like it. 
//--------------------------------------------------------------------------- 
#include "Tree.h" 
 
//--------------------------------------------------------------------------- 
Tree::Tree() 
{ 
  initialized = 0; 
  texId = 0; 
  maxRecursionLevel = 8; 
  branchesPerLevel  = 2; 
  currPrim = PLANE; 
  quadric = gluNewQuadric(); 
  useLeaves( false ); 
 
  line[0] = 0; 
  line[1] = 1.0f; 
  line[2] = 0.0f; 
 
  currLOD = 0.5f; 
 
  spread = 45; 
  twist  = 0; 
 
  treeBaseType    = 0; 
  currColorScheme = false; 
 
  useDisplayList = false; 
  displayListId = 1;       // these are only used if the display list code is enabled 
  treeGeomValid = false; 
} 
 
//--------------------------------------------------------------------------- 
void Tree::init() 
{ 
  if( initialized == 0 ) 
  { 
    texId = 0; 
    maxRecursionLevel = 10; 
 
    texId = pngBind( "treeTex.png", PNG_NOMIPMAP, PNG_SOLID, NULL, 
                     GL_REPEAT, GL_NEAREST, GL_NEAREST ); 
    if( texId == 0) 
    { 
      printf( "Couldn't find tree texture (treeTex.png)!\n" ); 
      printf( "Make sure it is in the same directory as the executable and run again.\n" ); 
      printf( "Quitting\n" ); 
      exit(0); 
    } 
 
    initialized = 1; 
  } 
} 
 
//--------------------------------------------------------------------------- 
void Tree::shutdown() 
{ 
  if( initialized ) 
  { 
    if( glIsList(displayListId) == GL_TRUE ) 
    { 
      glDeleteLists( displayListId, 1 ); 
    } 
    glDeleteTextures( 1, &texId ); 
    texId = 0; 
    initialized = 0; 
  } 
} 
 
//--------------------------------------------------------------------------- 
// Wrapper for the recursive draw function. 
// Putting the display list building code here makes things easy, because 
// we don't have to worry about what level of recursion we're on to decide 
// whether to start or stop building the display list 
//--------------------------------------------------------------------------- 
// Tradeoffs with the display list: when enabled, it takes longer to see 
// any changes that you make to the tree, but rotating and moving the 
// tree around goes much faster. When not using display lists, changes are 
// shown instantly, but it can take longer to display. 
void Tree::draw( unsigned int currLevel ) 
{ 
  if( glIsEnabled(GL_TEXTURE_2D) ) 
  { 
    glBindTexture( GL_TEXTURE_2D, texId ); 
  } 
 
  // HACK ALERT! 
  // Turn off depth testing when we're using planes and only have 2 branches 
  // This is mainly used as a demo mode to show how it works 
  if( currPrim == PLANE && branchesPerLevel == 2) 
  { 
    glDisable( GL_DEPTH_TEST ); 
  } 
  else 
  { 
    glEnable( GL_DEPTH_TEST ); 
  } 
 
  // Draw method is selected here 
  if( !useDisplayList ) 
  { 
    drawRecTree(currLevel); 
  } 
  else if( treeGeomValid && useDisplayList) // just display the compiled tree 
  { 
    if( glIsList(displayListId) == GL_TRUE ) 
    { 
      //printf( "Drawing compiled tree...\n" ); 
      glCallList( displayListId ); 
    } 
    else 
    { 
      printf( "Tree::draw() error: invalid list (%d)\n", displayListId ); 
    } 
  } 
  else // build a new tree 
  { 
    printf( "Building a new tree..." ); 
    glDeleteLists( displayListId, 1 ); // delete our only list 
    glNewList( displayListId, GL_COMPILE_AND_EXECUTE ); 
 
    drawRecTree(currLevel); 
 
    glEndList(); 
    printf( "done\n" ); 
    treeGeomValid = true; 
  } 
} 
 
//--------------------------------------------------------------------------- 
// Func: void drawRecTree( unsigned int currLevel ) 
// Desc: Recursively draws a textured tree. Recurses currLevel levels deep 
// Parm: currLevel: the bigger the number, the more times it will recurse. 
//                  It stops when currLevel == 0 
//--------------------------------------------------------------------------- 
void Tree::drawRecTree( unsigned int currLevel ) 
{ 
  if( currLevel > 0 ) 
  { 
    // scale->0 as we recurse deeper and deeper 
    float scale = (float)currLevel / (float)maxRecursionLevel; 
 
    // we can do this because 0 <= scale <= 1 
    if( currColorScheme ) 
    { 
      glColor3f(1 * scale, 1-scale, 0); // red to green 
    } 
    else 
    { 
      glColor3f(0.7f*scale, 0.4f+0.2f*scale,0.4f*scale); // tree colors 
    } 
 
    switch( currPrim ) 
    { 
      case LINE: 
        drawLinePrim( scale ); 
        break; 
 
      case PLANE: 
        drawPlanePrim( scale ); 
        break; 
 
      case CYLINDER: 
        { 
        // the second scale becomes the first scale in the next iter 
        // and the pieces fit very snugly 
        float scaleNext = (currLevel-1) / (float)maxRecursionLevel; 
        drawCylinderPrim( scale, scaleNext ); 
        } 
        break; 
 
      case SPHERE: 
        drawSpherePrim( scale ); 
        break; 
 
      default: 
        printf( "Error: unknown shape. Defaulting to plane\n" ); 
        currPrim = PLANE; 
        break; 
    } 
 
    if( drawLeaves ) 
    { 
      if( currLevel <= 2 ) 
      { 
        glPushMatrix(); 
        glTranslatef( 0, scale, 0 ); 
        glRotatef( (GLfloat)(rand()%60), 0, 0, 1 ); // makes the leaves "flutter in the wind" 
        glRotatef( (GLfloat)(rand()%60), 0, 1, 0 ); // we should really be saving these vals 
        glRotatef( (GLfloat)(rand()%60), 1, 0, 0 ); // or finding a seed that will generate the 
        drawPlanePrim( 0.5f * scale );              // same params each time the tree is moved 
        glPopMatrix(); 
      } 
    } 
 
    glPushMatrix(); 
    { 
      float dTheta = 360.0f / (float)branchesPerLevel; 
      float rot = 24.0f; 
      // We're currently in the default position, 
      // which is parallel to the y-axis (straight up) 
      // The following transforms are relative to any previous transforms; 
      // they are not absolute transforms. 
      // Ahhh... the beauty of the transformation matrix stack. 
      for( unsigned int i = 0; i < branchesPerLevel; i++ ) 
      { 
        glPushMatrix(); 
          if( treeBaseType == 0 ) // Base: all branches per level start at the same point 
          { 
            // move to the end of the primitive 
            glTranslatef( scale * line[0], scale * line[1], 0 ); 
          } 
          else if( treeBaseType > 0 ) // Base: each branch spirals up 
          { 
            // move to the end of the primitive 
            glTranslatef( scale * line[0], scale * line[1] * float(i+1) / float(branchesPerLevel), 0 ); 
          } 
 
          glRotatef( (GLfloat)i*dTheta, 0, 1, 0 ); // distributes the branches around the previous branch 
          glRotatef( (GLfloat)twist,    0, 1, 0 ); // how much more to rotate each branch 
          glRotatef( (GLfloat)spread,   0, 0, 1 ); // how much to rotate branch away from the y-axis/up 
 
          drawRecTree( currLevel - 1 ); // recursively draw the rest of the branches that are connected to this one 
        glPopMatrix(); 
      } 
    } 
    glPopMatrix(); 
  } 
} 
 
//--------------------------------------------------------------------------- 
// Desc: draws a line 
// Parm: float scale: typically ranges between 0 and 1 but can be anything 
//--------------------------------------------------------------------------- 
void Tree::drawLinePrim( float scale ) 
{ 
  glBegin( GL_LINES ); 
  { 
    glVertex3f( 0, 0, 0 ); 
    glVertex3f( scale * line[0], scale * line[1], 0 ); 
  } 
  glEnd(); 
} 
 
//--------------------------------------------------------------------------- 
// Parm: float scale: typically ranges between 0 and 1 but can be anything 
//--------------------------------------------------------------------------- 
void Tree::drawPlanePrim( float scale ) 
{ 
  glBegin( GL_QUADS ); 
  { 
    // draw a quad slightly longer than the line-segment 
    float width  = scale * 0.8f; 
    float height = scale * (line[1] * 1.5f); 
 
    glTexCoord2f( 0.0f, 1.0f );   // upper-left 
    glVertex2f  ( -width/2, 0.0f ); 
 
    glTexCoord2f( 0.0f, 0.0f );   // lower-left 
    glVertex2f  ( -width/2, height ); 
 
    glTexCoord2f( 1.0f,  0.0f );  // lower-right 
    glVertex2f  ( width/2, height ); 
 
    glTexCoord2f( 1.0f, 1.0f );   // upper-right 
    glVertex2f  ( width/2, 0.0f ); 
  } 
  glEnd(); 
} 
 
//--------------------------------------------------------------------------- 
// Parm: float scaleBase, scaleTop: typically ranges between 0 and 1 
//                                  but can be anything 
//--------------------------------------------------------------------------- 
void Tree::drawCylinderPrim( float scaleBase, float scaleTop ) 
{ 
  glPushMatrix(); 
    glRotatef( -90.0f, 1,0,0); 
    gluCylinder( quadric,         // bottom radius, top radius, height, slices, stacks 
                 0.25f * scaleBase, 
                 0.25f * scaleTop, 
                 scaleBase * line[1], 
                 int(currLOD*16), 
                 int(currLOD*4) ); 
  glPopMatrix(); 
} 
 
//--------------------------------------------------------------------------- 
// Parm: float scale: typically ranges between 0 and 1 but can be anything 
//--------------------------------------------------------------------------- 
void Tree::drawSpherePrim( float scale ) 
{ 
  glPushMatrix(); 
    glRotatef( -90.0f, 1, 0, 0 ); 
    gluSphere( quadric, scale*0.6f, int(currLOD*20), int(currLOD*16) ); 
  glPopMatrix(); 
} 
 
//-- E O F ------------------------------------------------------------------