I have spent quite some time going through the Road model, and thought I
would post the results for everyone else.

The road file contains two types of lines, waypoint and track. The
format is as follows:
waypoint [display distance] [waypoint id] [X Position] [Y Position] [Z
Position]

track [display distance] [waypoint id] [X Position] [Y Position] [Z
Position] [No of Profile Points] [Profile Height 1] [Profile Height
2] ...

The "waypoints" create a road sign inside the simulation, which displays
the [waypoint id] and the [display distance]. The "tracks" add a new
point in the road path; a spline is used to connect sequential track
points. In the track data line, the [display distance] and [waypoint id]
have no effect.

Additionally, inside the world file, the parameter <roadSpacing> is
somewhat misleading. The first value is the number of intermediate
spline points that will be added between sequential track points. Note
that the total number of points (~= track Points * roadSpacing) must
equal or exceed 10,000 for the the native Gazebo road to work properly.
The second value is the lateral distance in meters between sequential
profile points.


Once I adjusted the <roadSpacing> value correctly, the road would
render. However, for short roads, the hard-coded 10,000 spline points
was causing serious CPU issues.  Also, when large changes in the Z
Position were present, the road seemed to narrow considerably and
occasionally invert.

To fix the above issues, as well as some data redundancy in the road
file, I have created a gazebo plugin called (imaginatively) "Road2". The
code is attached, or available at
http://swilliams.homelinux.net/websvn/listing.php?repname=Gazebo+Road
+Plugin&path=%2Ftrunk%2Fsrc%2F&rev=0&sc=0

Instructions on how to compile a plugin are available on the main Gazebo
site.  Additionally, I had to copy the Image.hh file from the Gazebo
source to /usr/local/include/gazebo and include the path
path /usr/include/libxml2 in the compile statement. Notes on how to use
the plugin are written the comments of Road2.cc


Hopefully some of this will help someone,
Stephen Williams
/*
 *  Gazebo - Outdoor Multi-Robot Simulator
 *  Copyright (C) 2003  
 *     Nate Koenig & Andrew Howard
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 */
/* Desc: Model for a road
 * Author: Andrew Howard
 * Date: 23 Oct 2004
 * CVS: $Id: Road.cc,v 1.17 2005/05/06 17:09:30 natepak Exp $
 */
/* Desc: Road2 geometry
 * Modified By: Stephen Williams
 * Date: 3 July 2007
 * CVS: ???
 * Notes: Modification of the original RoadGeom body.  Changes the syntax of the road file, the model parameters, the tangent calculation, and the way the spline is done
 */

/*

Generates a road surface using a Catmull-Rom spline based on points from a descriptor file. 

@par libgazebo interfaces

This model has no libgazebo interfaces.

@par Player drivers

No Player drivers are available.

@par Attributes

Road models can be instantiated using the <tt><model:Road2></tt> tag.
The following attributes are supported.

@htmlinclude default_attr_include.html

- hardness (float tuple)
  - Set spring-damper parameters for contact joints
  - Default: dInfinity 0

- friction (float tuple)
  - Set friction coefficients
  - Default: 1 1

- color (float tuple)
  - RGB color.
  - Default: 0 0 0

- textureFile (string, filename)
  - Texture image file.
  - Default: empty

- textureSize (float tuple, meters)
  - Size of the texture image when projected onto the ground plane (m).
  - Default: 2.0 2.0

- profileSpacing (float, meters)
  - Lateral distance between sequential points for the profile defined in the roadFile.
  - Default: 0.10

- profileIntermediatePoints (int)
  - The number of spline interpolated points to be added between sequential profile points
  - Default: 1

- trackIntermediatePoints (int)
  - The number of spline interpolated points to be added between sequential track points
  - Default: 10

- roadFile (string, filename)
  - Road data filename. A road file is a plain text file with a specific format.  
  - Default: empty
  - The first word of each line specifies the type of data on the line. Subsequent information is type specific. The following types are supported
  	- waypoint (draws a road sign in the simulation)
  	  X Position
  	  Y Position
  	  Z Position
  	  Waypoint ID
  	  Distance to Display (meters)
  	  Example: waypoint 10.0 12.0 0.10 1 5.50 
  	- trackpoint (adds a point to the raod path)
  	  X position
  	  Y Position
  	  Z Position
  	  Example: trackpoint 10.0 12.0 0.05
    - profile (specifies the track profile by listing a sequences of heights/Z Positions.  The lateral distance between profile points is controlled by profileSpacing.)
      Z Position 1
      Z Position 2
           .
           .
           .
           
@par Bodies

The following bodies are created by this model.

@htmlinclude default_body_include.html

@par Example

@verbatim
<model:Road2>
  <plugin>road2.so</plugin>
  <xyz>0 0 0</xyz>
</model:Road2>
@endverbatim

@par Authors

Andrew Howard

@par Modified By

Stephen Williams

*/
/// @}

#if HAVE_CONFIG_H
#include <config.h>
#endif

#include <assert.h>
#include <errno.h>
#include <string.h>
#include <stdio.h>

#include <gazebo.h>
#include <gazebo/Error.hh>
#include <gazebo/World.hh>
#include <gazebo/WorldFile.hh>
#include <gazebo/ModelFactory.hh>
#include <gazebo/Body.hh>

#include "Road2Geom.hh"
#include "Road2.hh"


//////////////////////////////////////////////////////////////////////////////
// Register this model
GZ_REGISTER_PLUGIN("Road2", Road2);


//////////////////////////////////////////////////////////////////////////////
// Constructor
Road2::Road2( World *world )
    : Model( world )
{
  this->body = NULL;
  this->roadGeom = NULL;
  return;
}


//////////////////////////////////////////////////////////////////////////////
// Destructor
Road2::~Road2()
{
  if (this->body)
    delete this->body;
  if (this->roadGeom)
    delete this->roadGeom;
  
  this->body = NULL;
  this->roadGeom = NULL;
  return;
}


//////////////////////////////////////////////////////////////////////////////
// Load the model
int Road2::Load(WorldFile *file, WorldFileNode *node)
{
  // Create the ODE objects
  if (this->LoadODE(file, node) != 0)
    return -1;

  // Load the road description
  if (this->LoadRoad(file, node) != 0)
    return -1;

  return 0;
}


//////////////////////////////////////////////////////////////////////////////
// Load ODE objects
int Road2::LoadODE(WorldFile *file, WorldFileNode *node)
{
  const char *textureFile;

  this->body = new Body(this->world, NULL, true);
  this->AddBody(this->body, true);
  
  this->roadGeom = new Road2Geom(this->body, this->modelSpaceId);
  
  // Surface hardness
  this->roadGeom->SetHardness(node->GetTupleDouble("surfaceHardness", 0, dInfinity),
                              node->GetTupleDouble("surfaceHardness", 1, 0));

  // Surface friction
  this->roadGeom->SetFriction(node->GetTupleDouble("surfaceFriction", 0, 1.0),
                              node->GetTupleDouble("surfaceFriction", 1, 1.0));

  // Color
  this->roadGeom->SetColor(node->GetColor("color", GzColor(0.7, 0.7, 0)));

  // Apply a texture
  textureFile = node->SearchFilename("textureFile", GAZEBO_TEXTURES_PATH, NULL);
  if (textureFile)
  {
    PRINT_MSG1(1, "loading texture file [%s]", textureFile);
    if (this->roadGeom->SetTexture2DFile(textureFile) != 0)
    {
      PRINT_ERR("unable to load texture file");
      return -1;
    }
  }
  this->roadGeom->SetTexture2DSize(node->GetPosition("textureSize", GzVectorSet(2.0, 2.0, 0.0)));

  // Set Road construction parameters
  this->roadGeom->SetTrackIntermediatePoints(node->GetInt("trackIntermediatePoints", 10));
  this->roadGeom->SetProfileIntermediatePoints(node->GetInt("profileIntermediatePoints", 1));
  this->roadGeom->SetProfileSpacing(node->GetDouble("profileSpacing", 0.10));
  
  
  dGeomSetCategoryBits((dGeomID) this->modelSpaceId, GZ_FIXED_COLLIDE);
  dGeomSetCollideBits((dGeomID) this->modelSpaceId, ~GZ_FIXED_COLLIDE);

  return 0;
}


//////////////////////////////////////////////////////////////////////////////
// Load the road description
int Road2::LoadRoad(WorldFile *file, WorldFileNode *node)
{
  const char *filename;
  FILE *rfile;
  char line[1024];
  char *tokens[1024];
  int lineCount, tokenCount;
  int i, wp;
  double x, y, z;
  GzVector p;
  char *type;
  char text[128];
  double dist;
  
  // Get the road file
  filename = node->SearchFilename("roadFile", NULL, NULL);
  if (!filename)
  {
    PRINT_ERR("no road file specified");
    return -1;
  }
  
  // Open file
  rfile = fopen(filename, "r");
  if (rfile == NULL)
  {
    PRINT_ERR2("unable to open [%s] : %s", filename, strerror(errno));
    return -1;
  }
  
  lineCount = 0;
  
  // Parse the file
  while (true)
  {      
    if (fgets(line, sizeof(line), rfile) == NULL)
      break;
    lineCount++;

    // Break line into tokens
    tokenCount = 0;
    tokens[tokenCount] = strtok(line, " ");
    while (tokens[tokenCount])
    {
      tokenCount++;
      assert(tokenCount < (int) (sizeof(tokens) / sizeof(tokens[0])));
      tokens[tokenCount] = strtok(NULL, " ");
    }

//    if (tokenCount < 5)
//    {
//      PRINT_ERR1("syntax error in road file, line [%d]", lineCount);
//      break;
//    }

    // Get type from the first token, then load the rest of the line accordingly
    type = tokens[0];
   
    // If this is a waypoint entry
    if (strcmp(type, "waypoint") == 0)
    {
      // Get data from tokens
      x = 		atof(tokens[1]);
      y =	 	atof(tokens[2]);
      z = 		atof(tokens[3]);
      wp = 		atoi(tokens[4]);
      dist =	atof(tokens[5]);

      // Compute position in global cs
      p = GzVectorSet(x, y, z);
      p = GzVectorSub(p, this->world->utmOffset);           

      snprintf(text, sizeof(text), "WP %d -- %8.1f m", wp, dist);
      
      this->roadGeom->AddSign(p, text);
    }
    
    // If this is a track entry
    else if (strcmp(type, "trackpoint") == 0)
    {
      // Get data from tokens
      x = 		atof(tokens[1]);
      y =	 	atof(tokens[2]);
      z = 		atof(tokens[3]);

   	  // Compute track point in global cs
      p = GzVectorSet(x, y, z);
      p = GzVectorSub(p, this->world->utmOffset);
      this->roadGeom->AddTrackPoint(p);
    }

    // If this is a profile entry
    else if (strcmp(type, "profile") == 0)
    {
      // Extract profile points
      for (i = 1 ; i < tokenCount ; i++)
      {
        z = atof(tokens[i]);
        this->roadGeom->AddProfilePoint( z );
      }
    }        
  }

  // See if we got to the end
  if (!feof(rfile))
  {
    fclose(rfile);
    return -1;
  }
  
  fclose(rfile);
  this->roadGeom->InitMesh();

  return 0;
}


//////////////////////////////////////////////////////////////////////////////
// Initialize the model
int Road2::Init(WorldFile *file, WorldFileNode *node)
{
  //int i;
  //Model *model;

  /* TODO
  this->trackModel = NULL;
  
  for (i = 0; i < this->world->GetNumModels(); i++)
  {
    model = this->world->GetModels()[i];
    if (strcmp(model->GetId(), "car1") == 0) // HACK
      this->trackModel = model;
  }

  assert(this->trackModel);
  
  this->trackPose = this->trackModel->GetPose();
  */
  
  this->roadGeom->UpdateMesh(this->trackPose.pos);

  return 0;
}



//////////////////////////////////////////////////////////////////////////////
// Finalize the model
int Road2::Fini()
{
  return 0;
}


//////////////////////////////////////////////////////////////////////////////
// Update model
void Road2::Update(double step)
{
  /* TODO
  GzPose pose;
  GzVector d;

  if (!this->trackModel)
    return;

  pose = this->trackModel->GetPose();
  d = GzVectorSub(pose.pos, this->trackPose.pos);

  if (fabs(d.x) > 1.0 || fabs(d.y) > 1.0) // HACK
  {
    //this->roadGeom->UpdateMesh(pose.pos);
    this->trackPose = pose;
  }
  */
  
  return;
}
/*
 *  Gazebo - Outdoor Multi-Robot Simulator
 *  Copyright (C) 2003  
 *     Nate Koenig & Andrew Howard
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 */
/* Desc: Model for a road
 * Author: Andrew Howard
 * Date: 23 Oct 2004
 * CVS: $Id: Road.hh,v 1.3 2004/11/17 03:53:43 natepak Exp $
 */
/* Desc: Road2 geometry
 * Modified By: Stephen Williams
 * Date: 3 July 2007
 * CVS: ???
 * Notes: Modification of the original RoadGeom body.  Changes the syntax of the road file, the model parameters, the tangent calculation, and the way the spline is done
 */

#ifndef ROAD2_HH
#define ROAD2_HH

// Gazebo includes
#include <gazebo/Model.hh>
#include <gazebo/Body.hh>

// Local Includes
#include "Road2Geom.hh"

// Forward declarations
//class Road2Geom;

// Definitions
#define GAZEBO_TEXTURES_PATH "./"

/// @brief A linear road model
class Road2 : public Model
{
  /// @brief Construct
  public: Road2(World *world);

  /// @brief Destructor
  public: virtual ~Road2();

  /// @brief Load the model
  public: virtual int Load(WorldFile *file, WorldFileNode *node);

  /// @brief Load ODE stuff
  private: int LoadODE(WorldFile *file, WorldFileNode *node);

  /// @brief Load road description
  private: int LoadRoad(WorldFile *file, WorldFileNode *node);

  /// @brief Initialize the model
  public: virtual int Init(WorldFile *file, WorldFileNode *node);

  /// @brief Finalize the model
  public: virtual int Fini();

  /// @brief Update the model state
  public: virtual void Update(double step);
  
  // Components
  private: Body *body;
  private: Road2Geom *roadGeom;

  /// Model we are tracking
  private: Model *trackModel;

  /// Current track pose
  private: GzPose trackPose;
};

#endif
/*
 *  Gazebo - Outdoor Multi-Robot Simulator
 *  Copyright (C) 2003  
 *     Nate Koenig & Andrew Howard
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 */
/* Desc: Road Geometry
 * Author: Andrew Howard
 * Date: 23 Oct 2004
 * CVS: $Id: Road.hh,v 1.3 2004/11/17 03:53:43 natepak Exp $
 */
/* Desc: Road2 geometry
 * Modified By: Stephen Williams
 * Date: 3 July 2007
 * CVS: ???
 * Notes: Modification of the original RoadGeom body.  Changes the syntax of the road file, the way the spline is done, and the scaling parameters
 * Road File Details:
 */


#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

//#ifdef HAVE_TRIMESH

#include <assert.h>
#include <GL/glu.h>
#include <GL/glut.h>
#include <gazebo/Image.hh>
#include <gazebo/Error.hh>  // allows message printing

#include "Road2Geom.hh"


//////////////////////////////////////////////////////////////////////////////
// Constructor
Road2Geom::Road2Geom( Body *body, dSpaceID spaceId )
    : Geom( spaceId )
{

  this->trackIntermediatePoints = 10;
  this->trackPointCount = 0;
  this->trackPointMaxCount = 0;
  this->trackPoints = NULL;
  this->track = NULL;

  this->profileSpacing = 1.0;
  this->profileIntermediatePoints = 10;
  this->profilePointCount = 0;
  this->profilePointMaxCount = 0;
  this->profilePoints = NULL;
  this->profile = NULL;

  this->signCount = 0;
  this->signMaxCount = 0;
  this->signs = NULL;

  this->vertexCount = 0;
  this->vertexMaxCount = 0;
  this->vertices = NULL;
  this->normals = NULL;

  this->indexCount = 0;
  this->indexMaxCount = 0;
  this->indices = NULL;

  this->patchSize = 10.0;

  // Create an object for managing trimesh data
  this->tridata = dGeomTriMeshDataCreate();

  // Construct the trimesh data
  dGeomTriMeshDataBuildSimple(this->tridata,
                              (dReal*) this->vertices, this->vertexCount,
                              this->indices, this->indexCount);
  
  // Create and ODE trimesh geom
  this->trimesh = dCreateTriMesh(spaceId, this->tridata, NULL, NULL, NULL);

  // Initialize underlying geom
  this->SetGeom(body, this->trimesh, NULL, false);
  
  return;
}


//////////////////////////////////////////////////////////////////////////////
// Destructor
Road2Geom::~Road2Geom()
{
  int i;
  
  // Clean up
  dGeomDestroy(this->trimesh);
  dGeomTriMeshDataDestroy(this->tridata);

  free(this->indices);
  free(this->vertices);
  free(this->trackPoints);
  free(this->track);
  free(this->profilePoints);
  free(this->profile);

  for (i = 0; i < this->signCount; i++)
    free(this->signs[i].text);
  free(this->signs);  

  return;
}


//////////////////////////////////////////////////////////////////////////////
// Add a point to the track
void Road2Geom::AddTrackPoint(GzVector pos)
{
  size_t size;
  
  if (this->trackPointCount >= this->trackPointMaxCount)
  {
    this->trackPointMaxCount += 128;
    size = sizeof(this->trackPoints[0]) * this->trackPointMaxCount;
    this->trackPoints = (GzVector*) realloc(this->trackPoints, size);
  }
  this->trackPoints[trackPointCount++] = pos;

  return;
}


//////////////////////////////////////////////////////////////////////////////
// Add a point to the profile
void Road2Geom::AddProfilePoint(double pz)
{
  size_t size;
  
  if (this->profilePointCount >= this->profilePointMaxCount)
  {
    this->profilePointMaxCount += 8;
    size = sizeof(this->profilePoints[0]) * this->profilePointMaxCount;
    this->profilePoints = (GzVector*) realloc(this->profilePoints, size);
  }
  this->profilePoints[profilePointCount].x = this->profilePointCount * this->profileSpacing;
  this->profilePoints[profilePointCount].y = 0;
  this->profilePoints[profilePointCount++].z = pz;

  return;
}


//////////////////////////////////////////////////////////////////////////////
// Add a sign.
void Road2Geom::AddSign(GzVector pos, const char *text)
{
  size_t size;
  SignData *sign;
  
  if (this->signCount >= this->signMaxCount)
  {
    this->signMaxCount += 254;
    size = sizeof(this->signs[0]) * this->signMaxCount;
    this->signs = (SignData*) realloc(this->signs, size);
  }  

  sign = this->signs + this->signCount++;
  sign->pos = pos;
  sign->text = strdup(text);

  return;
}


//////////////////////////////////////////////////////////////////////////////
// Initialize the mesh (this is now done after the road file is loaded)
void Road2Geom::InitMesh()
{
  int i, j;
  int n0, n1, n2, n3;
  double t;
  dReal* vertex;
  size_t size;
  

  // Calculate the total number (specified plus intermediate spline) of profile points, and the number of track points
  int totalTrackPoints = (this->trackPointCount - 1) * this->trackIntermediatePoints + 1;
  int totalProfilePoints = (this->profilePointCount - 1) * this->profileIntermediatePoints + 1;
  
  // Allocate vertex storage
  this->vertexCount = 0;
  this->vertexMaxCount = totalTrackPoints * totalProfilePoints;
  this->vertices = (dVector3*) realloc(this->vertices,
                                       sizeof(this->vertices[0]) * this->vertexMaxCount);
  this->normals = (GzVector*) realloc(this->normals,
                                      sizeof(this->normals[0]) * this->vertexMaxCount);

  // Allocate memory for the profile
  size = sizeof(this->profile[0]) * totalProfilePoints;
  this->profile = (GzVector*) realloc(this->profile, size);

  // Calculate and store the profile spline (this only needs to be done once)
  for( i = 0 ; i < this->profilePointCount-1 ; i++ )
  {
    for( j = 0 ; j <= this->profileIntermediatePoints ; j++ )
    {
      // Spline control point indexes
      n0 = i - 1;
      n1 = i + 0;
      n2 = i + 1;
      n3 = i + 2;

      // Handle boundary conditions for spline fit
      n0 = Min(Max(0, n0), this->profilePointCount - 1);
      n1 = Min(Max(0, n1), this->profilePointCount - 1);
      n2 = Min(Max(0, n2), this->profilePointCount - 1);
      n3 = Min(Max(0, n3), this->profilePointCount - 1);

      // Error Checking / Debugging
      assert(n0 >= 0 && n0 < this->profilePointCount);
      assert(n1 >= 0 && n1 < this->profilePointCount);
      assert(n2 >= 0 && n2 < this->profilePointCount);
      assert(n3 >= 0 && n3 < this->profilePointCount);

      // Calculate the percentage between user defined points
      t = (double)j / ( (double)this->profileIntermediatePoints + 1 );
      
      // Use a spline to interpolate the profile altitude
      this->profile[i*this->profileIntermediatePoints + j] = this->SplineCR(t, this->profilePoints[n0], this->profilePoints[n1], this->profilePoints[n2], this->profilePoints[n3] );
      
      // Translate the profile to have a center at 0
      this->profile[i*this->profileIntermediatePoints + j].x -= ( ((double)totalProfilePoints - 1) * this->profileSpacing )/2.0;
    }
  }
  // Add the last point
  this->profile[totalProfilePoints - 1] = this->profilePoints[this->profilePointCount - 1];
  this->profile[totalProfilePoints - 1].x -= ( ((double)totalProfilePoints - 1) * this->profileSpacing )/2.0;


  // Allocate memory for the track
  size = sizeof(this->track[0]) * totalTrackPoints;
  this->track = (GzVector*) realloc(this->track, size);

  // Calculate and store the track spline (this only needs to be done once)
  for( i = 0 ; i < this->trackPointCount-1 ; i++ )
  {
    for( j = 0 ; j <= this->trackIntermediatePoints ; j++ )
    {
      // Spline control point indexes
      n0 = i - 1;
      n1 = i + 0;
      n2 = i + 1;
      n3 = i + 2;

      // Handle boundary conditions for spline fit
      n0 = Min(Max(0, n0), this->trackPointCount - 1);
      n1 = Min(Max(0, n1), this->trackPointCount - 1);
      n2 = Min(Max(0, n2), this->trackPointCount - 1);
      n3 = Min(Max(0, n3), this->trackPointCount - 1);

      // Error Checking / Debugging
      assert(n0 >= 0 && n0 < this->trackPointCount);
      assert(n1 >= 0 && n1 < this->trackPointCount);
      assert(n2 >= 0 && n2 < this->trackPointCount);
      assert(n3 >= 0 && n3 < this->trackPointCount);

      // Calculate the percentage between user defined points
      t = (double)j / ( (double)this->trackIntermediatePoints + 1 );

      // Use a spline to interpolate between track points
      this->track[i*this->trackIntermediatePoints + j] = this->SplineCR(t, this->trackPoints[n0], this->trackPoints[n1], this->trackPoints[n2], this->trackPoints[n3] );
    }
  }
  // Add the last point
  this->track[totalTrackPoints - 1] = this->trackPoints[this->trackPointCount - 1];

  
  // Initialize vertices
  for (i = 0 ; i < totalTrackPoints ; i++)
  {
    for (j = 0 ; j < totalProfilePoints ; j++)
    {
      vertex = this->vertices[this->vertexCount++];
      vertex[0] = i;
      vertex[1] = j;
      vertex[2] = 0.05;
    }
  }

  // Allocate index storage
  this->indexCount = 0;
  this->indexMaxCount = totalTrackPoints * totalProfilePoints * 6;
  this->indices = (int*) realloc(this->indices,
                                 sizeof(this->indices[0]) * this->indexMaxCount);

  // Construct triangle strips
  for (i = 0; i < totalTrackPoints - 1; i++)
  {
    for (j = 0; j < totalProfilePoints - 1; j++)
    { 
      assert(this->indexCount + 6 <= this->indexMaxCount);
      this->indices[this->indexCount++] = (i + 0) * totalProfilePoints + j;
      this->indices[this->indexCount++] = (i + 1) * totalProfilePoints + j;
      this->indices[this->indexCount++] = (i + 0) * totalProfilePoints + j + 1;     
      this->indices[this->indexCount++] = (i + 0) * totalProfilePoints + j + 1;
      this->indices[this->indexCount++] = (i + 1) * totalProfilePoints + j;
      this->indices[this->indexCount++] = (i + 1) * totalProfilePoints + j + 1;
    }
  }


  return;
}


//////////////////////////////////////////////////////////////////////////////
// Regenerate the mesh
void Road2Geom::UpdateMesh(GzVector pos)
{
  int i, j;
  int n0, n1, n2;
  GzVector tangent;
  dReal* vertex;
  
  
  // Calculate total number of track and profile points
  int totalTrackPoints = (this->trackPointCount - 1) * this->trackIntermediatePoints + 1;
  int totalProfilePoints = (this->profilePointCount - 1) * this->profileIntermediatePoints + 1;

  // Rotate the the profile to be perpendicular to the track tangent
  for( i = 0 ; i < totalTrackPoints ; i++ )
  {
    // Tangent point indexes
    // Handle boundary conditions for the central difference
    if( i <= 0 )
    {
      n0 = 0;
      n1 = 1;
      n2 = 2;
    }
    else if( i >= totalTrackPoints-1 )
    {
        n0 = totalTrackPoints-3;
        n1 = totalTrackPoints-2;
        n2 = totalTrackPoints-1;
    }
    else
    {
    	n0 = i - 1;
    	n1 = i - 0;
    	n2 = i + 1;
    }
    
    // Error Checking / Debugging
    assert(n0 >= 0 && n0 < totalTrackPoints);
    assert(n1 >= 0 && n1 < totalTrackPoints);
    assert(n2 >= 0 && n2 < totalTrackPoints);

    // Use a Central Differencing scheme to compute the tangent
    tangent = this->SplineCDtan(this->track[n0], this->track[n1], this->track[n2]);

	// Update Vertex points
    // Rotate the profile about the center to be perpendicular to the tangent, then translate it to the track point
    for( j = 0 ; j < totalProfilePoints ; j++ )
    {
      // move to the correct vertex
      vertex = this->vertices[i * totalProfilePoints + j];

      // Calculate the vertex position by rotating the profile point to be perpendicular to the tangent line, and translating it to the track point position
      vertex[0] = this->track[i].x + profile[j].x*tangent.y;
      vertex[1] = this->track[i].y + profile[j].x*-tangent.x;
      vertex[2] = this->track[i].z + profile[j].z;
	}
  }
  
  // Compute normal vectors
  this->UpdateNormals();

  // Construct the trimesh data
  dGeomTriMeshDataBuildSimple(this->tridata,
                              (dReal*) this->vertices, this->vertexCount,
                              this->indices, this->indexCount);
  dGeomTriMeshSetData(this->trimesh, this->tridata);

  return;
}


//////////////////////////////////////////////////////////////////////////////
// Compute normal vectors on vertices
void Road2Geom::UpdateNormals()
{
  int i, a, b, c;
  dVector3 *v;
  GzVector v0, v1, v2, n;

  v = this->vertices;

  // Reset the normals
  for (i = 0; i < this->vertexCount; i++)
    this->normals[i] = GzVectorZero();

  // Consider each triangle
  for (i = 0; i < this->indexCount; i += 3)
  {
    a = this->indices[i + 0];
    b = this->indices[i + 1];
    c = this->indices[i + 2];

    // Compute the normal
    v0 = GzVectorSet(v[a][0], v[a][1], v[a][2]);
    v1 = GzVectorSet(v[b][0], v[b][1], v[b][2]);
    v2 = GzVectorSet(v[c][0], v[c][1], v[c][2]);
    n = GzVectorCross(GzVectorSub(v1, v0), GzVectorSub(v2, v1));
    n = GzVectorUnit(n);

    // Accumate normals across triangles
    // We assume normals are being normalized by OpenGL
    this->normals[a] = GzVectorAdd(this->normals[a], n);
    this->normals[b] = GzVectorAdd(this->normals[b], n);
    this->normals[c] = GzVectorAdd(this->normals[c], n);
  }

  return;
}


//////////////////////////////////////////////////////////////////////////////
// Compute a Catmull-Rom spline
GzVector Road2Geom::SplineCR(double t, GzVector p0, GzVector p1,
                            GzVector p2, GzVector p3)
{
  GzVector q;

  q.x = p1.x +
    (-0.5*p0.x + 0.5*p2.x) * t +
    (1*p0.x - 2.5*p1.x + 2*p2.x - 0.5*p3.x) * t*t +
    (-0.5*p0.x + 1.5*p1.x - 1.5*p2.x + 0.5*p3.x) * t*t*t;

  q.y = p1.y +
    (-0.5*p0.y + 0.5*p2.y) * t +
    (1*p0.y - 2.5*p1.y + 2*p2.y - 0.5*p3.y) * t*t +
    (-0.5*p0.y + 1.5*p1.y - 1.5*p2.y + 0.5*p3.y) * t*t*t;

  q.z = p1.z +
    (-0.5*p0.z + 0.5*p2.z) * t +
    (1*p0.z - 2.5*p1.z + 2*p2.z - 0.5*p3.z) * t*t +
    (-0.5*p0.z + 1.5*p1.z - 1.5*p2.z + 0.5*p3.z) * t*t*t;

  return q;
}


//////////////////////////////////////////////////////////////////////////////
// Compute a 3-Point Central Difference tangent (note: p0 -- point immediately before the point in question, p1 -- point in question, p2 -- point immediately after point in question
GzVector Road2Geom::SplineCDtan(GzVector p0, GzVector p1, GzVector p2)
{
  GzVector q;
  double dx1, dx2, dy1, dy2;

  dx1 = p1.x - p0.x;
  dy1 = p1.y - p0.y;
  dx2 = p2.x - p1.x;
  dy2 = p2.y - p1.y;

  q.x = (dx2*dx2)*p0.x + (dx1*dx1 - dx2*dx2)*p1.x - (dx1*dx1)*p2.x;
  q.y = (dy2*dy2)*p0.y + (dy1*dy1 - dy2*dy2)*p1.y - (dy1*dy1)*p2.y;
  q.z = 0;  // ignore elevation changes
  q = GzVectorUnit(q);
  
  return q;
}


//////////////////////////////////////////////////////////////////////////////
// Default render routine
void Road2Geom::Render(RenderOptions *opt)
{
  int i, j;
  int ni, nj, mi, mj;
  GLuint listId;
  RenderOptions listOpt;
  
  // Recover stored display list for this camera
  this->GetList(opt->cameraIndex, &listId, &listOpt);

  // See if the current display list is dirty
  this->dirty |= (listId == 0);
  this->dirty |= (opt->displayMaterials != listOpt.displayMaterials);
  this->dirty |= (opt->displayTextures != listOpt.displayTextures);

  // Compute offset based on old camera pose
  mi = (int) floor(listOpt.cameraPose.pos.x / this->patchSize + 0.5);
  mj = (int) floor(listOpt.cameraPose.pos.y / this->patchSize + 0.5);

  // Compute offset based on new camera pose
  ni = (int) floor(opt->cameraPose.pos.x / this->patchSize + 0.5);
  nj = (int) floor(opt->cameraPose.pos.y / this->patchSize + 0.5);

  // If the offset has changed, we need to redraw
  this->dirty |= (ni != mi);
  this->dirty |= (nj != mj);

  // Generate the display list
  if (this->dirty)
  {
    if (listId == 0)
      listId = glGenLists(1);    
    glNewList(listId, GL_COMPILE);

    // Set material properties
    if (opt->displayMaterials)
    {
      glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, this->colorAmbient);
      glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, this->colorDiffuse);
      glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, this->colorSpecular);
      glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, this->shininess);
    }

    if (opt->displayTextures && this->textureImage)
    {
      // Set up textures
      glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
      glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
      glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
      glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
      glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);

      // Build the mipmaps
      // Assume image data is *not* aligned
      glPixelStorei( GL_UNPACK_ALIGNMENT, 1);
      gluBuild2DMipmaps(GL_TEXTURE_2D, this->textureImage->components,
                        this->textureImage->width, this->textureImage->height,
                        this->textureImage->format, this->textureImage->type,
                        this->textureImage->data);
      
      glEnable(GL_TEXTURE_2D);
    }

    dReal *v;
    GzVector n;
    double tx, ty;

    // Texture scale; each texture image is scaled such that it maps
    // to a road patch of size textureSize
    tx = 0.5 / this->textureSize.x;
    ty = 0.5 / this->textureSize.y;
    
    // Calculate total number of track and profile points
    int totalTrackPoints = (this->trackPointCount - 1) * this->trackIntermediatePoints + 1;
    int totalProfilePoints = (this->profilePointCount - 1) * this->profileIntermediatePoints + 1;

    // Construct triangle strips (skipping the first two and last two points, as these cause problems)
    for (i = 2 ; i < totalTrackPoints-2 ; i++)
    {
      // Test vertex to see if it is near the current camera position
      v = this->vertices[totalProfilePoints * i + totalProfilePoints / 2];
      if (fabs(v[0] - opt->cameraPose.pos.x) > opt->farClip)
        continue;
      if (fabs(v[1] - opt->cameraPose.pos.y) > opt->farClip)
        continue;

      glBegin(GL_TRIANGLE_STRIP);
      
      // Construct triangle strips
      for (j = 0 ; j < totalProfilePoints ; j++)
      {
        glTexCoord2f(i * tx, j * tx);        
        n = this->normals[totalProfilePoints * (i + 0) + j];
        v = this->vertices[totalProfilePoints * (i + 0) + j];
        glNormal3f(n.x, n.y, n.z);
        glVertex3f(v[0], v[1], v[2]);

        glTexCoord2f((i + 1) * tx, j * tx);
        n = this->normals[totalProfilePoints * (i + 1) + j];
        v = this->vertices[totalProfilePoints * (i + 1) + j];        
        glNormal3f(n.x, n.y, n.z);
        glVertex3f(v[0], v[1], v[2]);
      }

      glEnd();
    }

    if (opt->displayTextures && this->textureImage)
      glDisable(GL_TEXTURE_2D);

    // Display signs
    this->RenderSigns(opt, listOpt);
    
    // Store list options
    this->SetList(opt->cameraIndex, listId, *opt);      
    glEndList();
  }
  
  // Call the display list
  if (listId)
    glCallList(listId);
  
  return;
}


//////////////////////////////////////////////////////////////////////////////
// Render signs
void Road2Geom::RenderSigns(RenderOptions *opt, RenderOptions listOpt)
{
  int i, j;
  double size;
  GzVector p;
  SignData *sign;

  // Set material properties
  if (opt->displayMaterials)
  {
    glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, GzColor(0, 0, 0));
    glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, GzColor(0, 0, 0));
    glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, GzColor(0, 0, 0));
  }

  for (i = 0; i < this->signCount; i++)
  {
    sign = this->signs + i;
    p = sign->pos;
        
    // Test sign to see if it is near the current camera position
    if (fabs(p.x - opt->cameraPose.pos.x) > opt->farClip)
      continue;
    if (fabs(p.y - opt->cameraPose.pos.y) > opt->farClip)
      continue;

    // Set sign hight
    size = 0.80;
    
    // Print the sign
    glPushMatrix();
    glTranslatef(p.x, p.y, p.z + 1.0); // HACK
    glRotatef(90, 1, 0, 0);
    glScalef(size / 150, size / 150, size / 150);
    for (j = 0; j < (int) strlen(sign->text); j++)
      glutStrokeCharacter(GLUT_STROKE_MONO_ROMAN, sign->text[j]);
    glPopMatrix();

    // Print a little stand
    glBegin(GL_LINES);
    glVertex3f(p.x, p.y, p.z - size / 2);
    glVertex3f(p.x, p.y, 0.0);
    glEnd();
  }

  return;
}

//#endif

/*
 *  Gazebo - Outdoor Multi-Robot Simulator
 *  Copyright (C) 2003  
 *     Nate Koenig & Andrew Howard
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 */
/* Desc: Road Geometry
 * Author: Andrew Howard
 * Date: 23 Oct 2004
 * CVS: $Id: Road.hh,v 1.3 2004/11/17 03:53:43 natepak Exp $
 */
/* Desc: Road2 geometry
 * Modified By: Stephen Williams
 * Date: 3 July 2007
 * CVS: ???
 * Notes: Modification of the original RoadGeom body.  Changes the syntax of the road file, the model parameters, the tangent calculation, and the way the spline is done
 */

#ifndef ROAD2GEOM_HH
#define ROAD2GEOM_HH

#include <gazebo/Geom.hh>


class Road2Geom : public Geom
{
  /// @brief Constructor
  public: Road2Geom(Body *body, dSpaceID spaceId);
  
  /// @brief Destructor
  public: virtual ~Road2Geom();

  /// @brief Set the number of intermediate spline points to use between defined track points
  public: void SetTrackIntermediatePoints(int points) {this->trackIntermediatePoints = points;}

  /// @brief Set the number of intermediate spline points to use between defined profile points
  public: void SetProfileIntermediatePoints(int points) {this->profileIntermediatePoints = points;}

  /// @brief Set the nominal spacing between profile points (m)
  public: void SetProfileSpacing(double spacing) {this->profileSpacing = spacing;}

  /// @brief Add a point to the track
  /// @param pos Point position (relative to road).
  public: void AddTrackPoint(GzVector pos);

  /// @brief Add a point to the profile
  /// @param pz Height above or below the nominal road level.
  public: void AddProfilePoint(double pz);

  /// @brief Add a sign.
  /// @param pos Sign position (relative to road).
  /// @param text Sign text (the value will be copied).
  public: void AddSign(GzVector pos, const char *text);

  /// @brief Initialize the mesh
  public: void InitMesh();

  /// @brief Update the mesh
  public: void UpdateMesh(GzVector pos);

  /// @brief Compute normal vectors on vertices
  private: void UpdateNormals();

  /// @brief Compute a Catmull-Rom spline
  private: GzVector SplineCR(double t, GzVector p0, GzVector p1,
                             GzVector p2, GzVector p3);

  /// @brief Compute a 3-point Central Difference spline tangent
  private: GzVector SplineCDtan(GzVector p0, GzVector p1, GzVector p2);

  /// @brief Render the geom (GL)
  public: virtual void Render(RenderOptions *opt);

  /// @brief Render signs
  private: void RenderSigns(RenderOptions *opt, RenderOptions listOpt);

  /// Data manager for trimesh
  private: dTriMeshDataID tridata;

  /// ODE trimesh geom
  private: dGeomID trimesh;

  
  /// Track data
  private: int trackIntermediatePoints;
  private: int trackPointCount, trackPointMaxCount;
  private: GzVector* trackPoints;
  private: GzVector* track;

  // Profile Data
  private: double profileSpacing;
  private: int profileIntermediatePoints;
  private: int profilePointCount, profilePointMaxCount;
  private: GzVector* profilePoints;
  private: GzVector* profile;
  
  /// Description for a single sign
  private: struct SignData
  {
    /// Position
    GzVector pos;

    /// Text
    char *text;
  };

  /// Sign data
  private: int signCount, signMaxCount;
  private: SignData *signs;

  /// Vertex data
  private: int vertexCount, vertexMaxCount;
  private: dVector3 *vertices;
  private: GzVector *normals;

  /// Triangle data
  private: int indexCount, indexMaxCount;
  private: int *indices;

  /// Maximum patch size; road will be re-rendered when the camera is
  /// outside the current patch.
  private: double patchSize;
};

#endif
-------------------------------------------------------------------------
This SF.net email is sponsored by DB2 Express
Download DB2 Express C - the FREE version of DB2 express and take
control of your XML. No limits. Just data. Click to get it now.
http://sourceforge.net/powerbar/db2/
_______________________________________________
Playerstage-gazebo mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/playerstage-gazebo

Reply via email to