Using ofMesh

The ofMesh class is a powerful class that is used for representing, modifying, and rendering 3D objects. By default, it draws triangle meshes, but it can also be used for drawing curves and points.

The ofMesh class performs rendering of many thousands and even millions of triangles by one OpenGL call, at the highest possible speed. Even though using ofMesh will at first seem slightly more complicated than using ofTriangle(), it will give you more flexibility in creating and modifying 3D objects in return. So it is highly recommended that you use ofMesh for 3D in all cases, except the very beginning or for learning 3D. You can use ofMesh not only for 3D but for 2D graphics as well.

Tip

openFrameworks has one more class, named ofVBOMesh, that is used for working with meshes. The class name means "mesh based on Vertex Buffer Object (VBO)". This class is similar to ofMesh, but it renders significantly faster when the vertices of the mesh are not changing. See details of its usage and performance in comparison with ofMesh in openFrameworks example examples/gl/vboExample.

To draw a surface consisting of a number of triangles, follow these steps:

Note

This is example 07-3D/02-PyramidMesh. It is based on the emptyExample project in openFrameworks.

  1. Declare an object mesh of type ofMesh in the testApp class declaration:
    ofMesh mesh;
  2. Add the vertices of the surface triangles to the mesh using the mesh.addVertex( p ) function. Note that if a vertex belongs to several triangles, you should specify these vertices just once. This feature is very useful for changing the surface; you change the position of just one vertex, and all the triangles will be drawn correctly.

    Vertices are added to the end of a special array of vertices in the mesh and are later referenced by indices in this array. So the first vertex has the index 0, the second vertex has the index 1, and so on. For example, to draw a pyramid, we specify its four vertices as follows:

    //Pyramid's base vertices with indices 0, 1, 2
    mesh.addVertex( ofPoint( -200, -100, -50 ) );
    mesh.addVertex( ofPoint( 200, -100, -50 ) );
    mesh.addVertex( ofPoint( 0, 200, 0 ) );
    
    //Pyramid's top vertex with index 3
    mesh.addVertex( ofPoint( 0, 0, 50 ) );
  3. Add the triangles by specifying the indices of the vertices for each triangle using the mesh.addTriangle( index1, index2, index3 ) function. Be careful to order this in the clockwise direction for correct lighting. In our pyramid example, we specify just three of its four triangles, so that you can see the interior of the object.
    //Vertices with indices 3, 2, 0
    mesh.addTriangle( 3, 2, 0 );
    
    //Vertices with indices 3, 1, 2
    mesh.addTriangle( 3, 1, 2 ); 
    
    //Vertices with indices 3, 0, 1
    mesh.addTriangle( 3, 0, 1 );
  4. Draw a mesh in the testApp::draw() function using the mesh.draw() function. You may need coordinate system transformations for moving and rotating the object. For example, a rotating pyramid can be drawn with the following code in testApp::draw():
    ofEnableDepthTest();    //Enable z-buffering
    
    //Set a background
    ofBackgroundGradient( ofColor( 255 ), ofColor( 128 ) );
    
    ofPushMatrix();    //Store the coordinate system
    
    //Move coordinate center to screen's center
    ofTranslate( ofGetWidth()/2, ofGetHeight()/2, 0 );
    
    //Rotate the coordinate system
    float time = ofGetElapsedTimef(); //Get time in seconds
    float angle = time * 30;          //Rotate angle
    ofRotate( angle, 0, 1, 1 ); 
    
    ofSetColor( 0, 128, 0 );  //Set a dark green color
    mesh.draw();              //Draw the mesh
    
    ofPopMatrix();            //Restore the coordinate system

When you run this code, you will see the pyramid is uniformly colored a dark green color. It looks like some animated 2D polygon and it is hard to make out that this is really a 3D pyramid surface. To see the mesh as a 3D object, you need to enable lighting for the scene and add normals information to the mesh. Let's do it.

Enabling lighting and setting normals

Lighting is needed for different parts of the surface to have different shading, depending on their orientation to the viewer. Such shading makes the surfaces look much more interesting than if just rendered with a uniform color because it emphasizes the 3D curvature of the surfaces. openFrameworks has an ofLight class for controlling light sources.

Note

This is example 07-3D/03-PyramidLighting. This example is a good starting point for drawing smooth surfaces using the setNormals() function.

It is a continuation of example 07-3D/02-PyramidMesh.

To use one light source with default parameters, add the following line in the testApp class declaration:

ofLight light;

Add the following line in the testApp::setup() function to enable it:

light.enable();    //Enabling light source

For the light to interact with the mesh properly, you need to set up normal vectors for all the vertices using the mesh.addNormal( normal ) function. Each normal vector should have unit length and direction perpendicular to the surface in the vertex. Information about the normals gives openFrameworks information about the correct lighting of the surface. Across the chapter, we will use the setNormals() function for normals computing, which we will discuss.

Computing normals using the setNormals() function

To compute normals for a mesh consisting of triangles, you can use the following function:

//Universal function which sets normals for the triangle mesh
void setNormals( ofMesh &mesh ){

  //The number of the vertices
  int nV = mesh.getNumVertices();

  //The number of the triangles
  int nT = mesh.getNumIndices() / 3;

  vector<ofPoint> norm( nV ); //Array for the normals

  //Scan all the triangles. For each triangle add its
  //normal to norm's vectors of triangle's vertices
  for (int t=0; t<nT; t++) {
      //Get indices of the triangle t
      int i1 = mesh.getIndex( 3 * t );
      int i2 = mesh.getIndex( 3 * t + 1 );
      int i3 = mesh.getIndex( 3 * t + 2 );

      //Get vertices of the triangle
      const ofPoint &v1 = mesh.getVertex( i1 );
      const ofPoint &v2 = mesh.getVertex( i2 );
      const ofPoint &v3 = mesh.getVertex( i3 );

      //Compute the triangle's normal
      ofPoint dir = ( (v2 - v1).crossed( v3 - v1 ) ).normalized();

      //Accumulate it to norm array for i1, i2, i3
      norm[ i1 ] += dir;
      norm[ i2 ] += dir;
      norm[ i3 ] += dir;
  }

  //Normalize the normal's length
  for (int i=0; i<nV; i++) {
        norm[i].normalize();
  }

  //Set the normals to mesh
  mesh.clearNormals();
  mesh.addNormals( norm );
}

To use it in your project, insert this function at the end of the testApp.cpp file, and add its declaration in the testApp.h file (outside the testApp class):

//Universal function which sets normals for the triangle mesh
void setNormals( ofMesh &mesh );

Now you can call setNormals( mesh ) and the normals will be computed. You need to call the setNormals( mesh ) function after each modification of vertices of mesh for the normals to be up-to-date.

Note

Scaling using ofScale() while drawing affects not only the object's vertices but the normals vectors too, and it can make shading improper. So when using normals, just avoid scaling or recalculating the normals so that they have unit length even after the usage of ofScale().

With lighting and normals, the pyramid looks a little more like a 3D object, which changes its shade depending on its orientation:

Computing normals using the setNormals() function

Note that the lightness of all the surface triangles mainly depends on the orientation of the central ("top") vertex of the pyramid. The reason is that shading of each triangle is computed by interpolating the normals of its vertices, and in our case, the normal of the central vertex is perpendicular to the pyramid's base. Such an approach works well for drawing smooth surfaces; see the The oscillating plane example section. Although in our case of pyramid, it can look a little bit unnatural.

To obtain the most natural visualization of the pyramid with sharp edges, we need to draw triangles independently without formally creating any common vertices.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset