Please disable AdBlock. CAN is an ad-supported site that takes hundreds of hours and thousands of dollars to sustain. Read More.

OUT NOW:
HOLO 1

Emerging trajectories in art, science, and technology.

226 pages of conversation, research, opinion, analysis. Step into artists' studios and workshops to discover the faces, personalities, and processes behind important work. Learn more!

HOLO is brought to you by the people behind CreativeApplications.Net

Guide to Meshes In Cinder [Cinder, Tutorials]

Introduction

Written by Joshua Noble, with images by Robert Hodgin

This article is going to cover two things in great detail: vertices and meshes and how they are handled in Cinder. There are a few different names for things that may be new to you in this tutorial which I’ll lay out for you right at the beginning: vertices, mesh, TriMesh, VboMesh, and VBOs. If any of those are already familiar to you and you simply want implementation details on how they work, feel free to skip ahead or around, I won’t be offended. For those of you who want more background, we’ll proceed in an orderly fashion.
So, let’s begin at the beginning: the vertex. In Cinder we use the OpenGL coordinate system which is a right-handed system. Initially the world is oriented at the upper left hand corner of your screen, so the x Axis points right, y Axis points downward, z Axis points into the screen (away from the user). A vertex is a point in geometrical shape, or, a vertex is a point in 3D space that has x, y, and z properties that determine where it is in relation to the 0, 0, 0 point (you can think of this as the center) of the world. For instance, a vertex with the values 100, 100, 100, describes a location 100 pixels to the right on the x axis, down on the y axis, and back on the z axis from the origin of the world in which it is located. In your Cinder application, unless you change it, the world is oriented at the upper left hand corner of your screen. All shapes are the result of the connections between vertices. So, a line is made up of the connections between two vertices, a pyramid is a construction made of the connections between five vertices, a cube is made up of the connections between eight vertices.

What do these vertices look like in pre? Something like this:

gl::vertex( Vec3f(300, 100, 0) ); // creates an OpenGL vertex from a Vec3f

Those vertices are passed to your graphics card and your graphics card fill in the spaces in between them in a processing usually called “the rendering pipeline”. The rendering pipeline goes more or less like this:
1. Say how you’re going to connect all the points.
2. Make some points.
3. Say that you’re done making points.
Saying it in pre, and more specficially in Cinder pre, looks like this:

glBegin(GL_QUAD_STRIP); // start drawing
gl::vertex(Vec3f(20, 20, 0)); // repeated a lot of times with different positions
glEnd(); // stop drawing

You may be thinking: “I’ll just make eight vertices and voila: a cube.” Not so quick. There’s a hitch and that hitch is that the OpenGL renderer has different ways of connecting the vertices that you pass to it and none are as efficient as to only need eight vertices to create a cube. You’ve probably seen a version of the following image somewhere before:

Creating eight vertices and expecting the GL_QUAD_STRIP to connect them is going to lead to something looking like this:

Why is that you ask? Well, first off because we used the GL_QUAD_STRIP but it’s also because there isn’t an easy way to tell your graphics card that you want to connect all those points into a cube. Generally you have to create your points to fit the drawing mode that you’ve selected because of what’s called “winding”. A vertex gets connected to another vertex in the order that the mode does it’s winding and this means that you might need multiple vertices in a given location to create the shape you want. The cube, for example, requires eighteen vertices, not the eight that you would expect. If you note the order of vertices in the GL chart above you’ll see that all of them use their vertices slightly differently (in particular you should make note of the GL_TRIANGLE_STRIP example). Drawing a shape requires that you keep track of which drawing mode is being used and which order your vertices are declared in. If you’re thinking: “it would be nice if there were an abstraction layer for this” you’re thinking right. Enter the mesh, which is really just an abstraction of the vertex and drawing mode that we started with but which has the added bonus of managing the draw order for you. That may seem insignificant at first, but it provides some real benefits when working with complex geometry.

TriMesh

The TriMesh represents a series of vertices connected with the same connection algorithm as the GL_TRIANGLE_STRIP. It’s a convienent way to keep track of multiple complex objects, draw and scale them easily, and manage which vertices create which faces of a model. Let’s get the simplest example out the way first, drawing a square consisting of two triangles. This requires, as you’d imagine, four vertices that represent the vertices of each triangle.

// Position and Color for the four corners
mesh.appendVertex( Vec3f( 10, 10, 0 ) );
mesh.appendColorRGB( Color( 1, 0, 0 ) );
mesh.appendVertex( Vec3f( 10, 300, 0 ) );
mesh.appendColorRGB( Color( 0, 1, 0 ) );
mesh.appendVertex( Vec3f( 300, 300, 0 ) );
mesh.appendColorRGB( Color( 0, 0, 1 ) );
mesh.appendVertex( Vec3f( 300, 10, 0 ) );
mesh.appendColorRGB( Color( 0, 0, 0 ) );

// Indices for each of the four corners
int vIdx0 = mesh.getNumVertices() - 4;
int vIdx1 = mesh.getNumVertices() - 3;
int vIdx2 = mesh.getNumVertices() - 2;
int vIdx3 = mesh.getNumVertices() - 1;

// Two triangles to create our square
mesh.appendTriangle( vIdx0, vIdx1, vIdx2 );
mesh.appendTriangle( vIdx0, vIdx2, vIdx3 );

So far, so good. Now, let’s extend that a little further and build something in 3D. A simple cube should suffice to demonstrate how the mesh connects all the vertices that it contains. At first glance, the next block of pre (140 lines) might seem daunting, but all we are doing is repeating the previous pre block 6 times, once per side. Since each side is comprised of 2 triangles, we draw a total of 12 triangles.

// Create the points of our cube
Vec3f v0(-100,-100,-100);
Vec3f v1( 100,-100,-100);
Vec3f v2( 100, 100,-100);
Vec3f v3(-100, 100,-100);
Vec3f v4(-100,-100, 100);
Vec3f v5( 100,-100, 100);
Vec3f v6( 100, 100, 100);
Vec3f v7(-100, 100, 100);

// Create the colors for each vertex
Color c0( 0, 0, 0 );
Color c1( 1, 0, 0 );
Color c2( 1, 1, 0 );
Color c3( 0, 1, 0 );
Color c4( 0, 0, 1 );
Color c5( 1, 0, 1 );
Color c6( 1, 1, 1 );
Color c7( 0, 1, 1 );

Vec3f faces[6][4] = { /* Vertices for the 6 faces of a cube. */
{v0, v1, v2, v3}, {v3, v2, v6, v7}, {v7, v6, v5, v4},
{v4, v5, v1, v0}, {v5, v6, v2, v1}, {v7, v4, v0, v3} };

Color colors[6][4] = { /* colors for each vertex of the cube. */
{c0, c1, c2, c3}, {c3, c2, c6, c7}, {c7, c6, c5, c4},
{c4, c5, c1, c0}, {c5, c6, c2, c1}, {c7, c4, c0, c3} };

for (int i = 0; i < 6; i++)
{

mesh.appendVertex(faces[i][0]);
mesh.appendColorRGB(colors[i][0]);
mesh.appendVertex(faces[i][1]);
mesh.appendColorRGB(colors[i][1]);
mesh.appendVertex(faces[i][2]);
mesh.appendColorRGB(colors[i][2]);
mesh.appendVertex(faces[i][3]);
mesh.appendColorRGB(colors[i][3]);

int numberVertices = mesh.getNumVertices();

mesh.appendTriangle( numberVertices - 4, numberVertices - 3, numberVertices - 2 );
mesh.appendTriangle( numberVertices - 4, numberVertices - 2, numberVertices - 1 );

}

Accessing the vertices in the TriMesh is a little different than you might imagine it at first, because you can’t directly access the vertices of the mesh and then alter them. Instead, you make a copy of the vertices using getVertices(), which returns a Vector of the vertices contained by the TriMesh as Vec3f objects and then modifying the values in that vector. To update the vertices in the mesh itself you then need to clear the mesh and append all the vertices again. The same goes for any modifications to the RGB color array as well. Look at the following:

void TriMeshSampleApp::update()
{
if( mMesh.getNumVertices() == 0 ) return;

// store all the mesh information

std::vector col = mMesh.getColorsRGB();
std::vector vec = mMesh.getVertices();

int i, j;
i = mMesh.getNumVertices();
j = 0;

mMesh.clear();

// something to add a little movement
float inc = sin( getElapsedSeconds() );

while(j < i)
{
// alter the positions array to get a little dynamism
vec.at(j).x -= inc;
vec.at(j+1).x += inc;
vec.at(j+2).x += inc*2.0f;
vec.at(j+3).x -= inc*2.0f;

// now replace it in the mesh
mMesh.appendVertex( vec.at(j));
mMesh.appendColorRGB( col.at(j) );
mMesh.appendVertex( vec.at(j+1));
mMesh.appendColorRGB( col.at(j+1) );
mMesh.appendVertex( vec.at(j+2));
mMesh.appendColorRGB( col.at(j+2) );
mMesh.appendVertex( vec.at(j+3));
mMesh.appendColorRGB( col.at(j+3) );

int vIdx0 = mMesh.getNumVertices() - 4;
int vIdx1 = mMesh.getNumVertices() - 3;
int vIdx2 = mMesh.getNumVertices() - 2;
int vIdx3 = mMesh.getNumVertices() - 1;

mMesh.appendTriangle( vIdx0, vIdx1, vIdx2 );
mMesh.appendTriangle( vIdx0, vIdx2, vIdx3 );

// go to the next triangle pair
j+=4;
}
}

The result?

Want to use a gl::Texture to texture the cube? Not so difficult. First, you need to set the texture co-ordinates of each vertex in the TriMesh. This requires that you use texture coordinates, not space coordinates or pixel co-ordinates. So, you may think your image is 500 pixels high and 500 pixels wide, but texture co-ordinates say that your texture is 1×1 with what you think of as upper-left corner (0, 0), still being (0, 0), but the bottom-right corner being (1,1). All the values between the 0, 0 and 1,1 points are scalar, meaning that 0.5 will be the midpoint of the image. So if you assign the texture co-ordinate 0.5, 0.5 to a vertex, the middle of the image will appear at that point. The picture below will hopefully help clarify this:

So, now, how to put our Trimesh and gl::Texture together? When creating the vertices using the appendVertex() and appendColorRGB() methods, use the appendTexCoord() method to create a texture co-ordinate. This indicates the location in the texture that you want to appear at that location in space:

mesh.appendVertex(v0);
mesh.appendColorRGB( Color(1, 0, 0) );
mesh.appendTexCoord(Vec2f(0, 0));
mesh.appendVertex(v1);
mesh.appendColorRGB( Color(0, 0, 1) );
mesh.appendTexCoord(Vec2f(1, 0));
mesh.appendVertex(v2);
mesh.appendColorRGB( Color(1, 0, 0) );
mesh.appendTexCoord(Vec2f(1, 1));
mesh.appendVertex(v3);
mesh.appendColorRGB( Color(0, 1, 0) );
mesh.appendTexCoord(Vec2f(0, 1));

Now, to apply a gl::Texture to this TriMesh, call gl::Texture bind(), draw your TriMesh, and then call gl::Texture unbind() and you’re done.

tex.enableAndBind();
gl::draw(mesh);
tex.unbind();

You can even animate the positions of the texture coordinates by copying, modifying, and then re-appending them in the same way that the vertices and RGB coordinates were updated. But there’s more to the TriMesh than just a nice way to store all the vertices that you create because, let’s face it, lining up vertices is a drag and there are better and more fun ways to make vertices, for instance, using a 3d modeling program. To that end, you can import and export OBJ files. So, in my favorite 3d modeling program (the opensource and free one) Blender, I’ll create a classic platonic solid, the isodecahedron, and then export it as an .obj file:

Now I can import it using an instance of the ObjLoader class like so:

ObjLoader loader( loadResource( SOLID ) );
loader.load( *mesh );

and draw it to the screen by passing it to gl::draw():

gl::draw(mesh);

Here it is with some random colors applied to it:

You can even manipulate it and export it using the TriMesh::writeObject() method:

mesh.write( writeFileStream(meshFileName, true); );

Now, if you thought that was all there was to working with meshes in Cinder you would be wrong. As I mentioned earlier, the way that geometry is drawn on the graphics card is by sending points and telling the card how to connect those points. This might remind you a little of how the Textures work in OpenGL: load some data up there, draw it by referring to it using its TextureId. So then, naturally you might wonder, what if we just stored the points on the card? Introducing the Vertex Buffer Object, aka VBO:

VBO

A VBO is a way of storing all of the data of vertex data on the graphics card. You’ve perhaps heard of Vertex Arrays and Display Lists and the VBO is similiar to both of these, but with a few advantages that we’ll go over very quickly. Vertex Arrays just let you store all the vertex data in an array on the client side, that is, on the CPU side and then send it to the graphics card when you’re ready to draw it. The downside of that is that you’re still storing the data on the client side and sending it over to the graphics card. So, instead of making all of our vertex data in what’s called “immediate mode”, which means between a glBegin() and glEnd() pair, you can just store vertex data in an arrays and you can draw geometric primitives by dereferencing the array elements with array indices. The Display List is a similar technique, using an array to store the created geometry, with the crucial difference that a Display List lives solely on the graphics card. This means that once you’ve created the vertex data for geometry, you can send it the graphics card and draw it simply by referencing the id of the stored data. The downside is that display lists can’t be modified. Once they’ve been sent to the card, you need to load them from the card, modify them, and then resend them to the card to see your changes applied. Since one of the conveniences of moving things to the graphics card is reducing the amount of traffic between the graphics card and the rest of your system. The VBO operates quite similarly to the Display List, with the advantage of allowing you to modify the geometry data on the graphics card without downloading all of it at once.

Now, since we’re focusing on Cinder, we’ll be focusing on the VboMesh class that Cinder uses to wrap the core OpenGL functionality. When you create a VboMesh, much like in the TriMesh, you need to decide whether the mesh is going to contain color information, texture coordinate information, but, there is another element to the VboMesh: whether the data is dynamic. Thius is particularly important because it determines whether you’re able to manipulate vertices in the mesh after creating it and it matters because using dynamic data means that the VboMesh will generate two copies of the data: one on the graphics card and one in the memory of your application. So, if you don’t need to update the data after it’s been loaded then you you don’t need to do anything special and if you do want to create dynamic data, you’ll need to create a gl::VboMesh::Layout object and pass it to the VboMesh. So, for instance, if you want static positions and static colors you would create a Layout object with those properties set on it, declare a VboMesh and then assign it to the result of a call to VboMesh(). This is because the VboMesh, like so many other things in Cinder, is a shared pointer, which means that it has to be created before you can use it, you can return it from a method without problems, and also that you don’t have to worry about deallocating the VboMesh once you’ve created it.
Here’s one of the constructors for the VboMesh:

VboMesh( size_t numVertices, size_t numIndices, Layout layout, GLenum primitiveType );

so that would be used like this:

gl::VboMesh mesh;
gl::VboMesh::Layout layout;
layout.setStaticPositions();
layout.setStaticColorsRGBA();
mesh = gl::VboMesh( 24, 20, layout, GL_QUADS );

Now, the numVertices and numIndices need a little explanantion. You’ll recall from the TriMesh that the number of indices and the number of vertices aren’t directly correlated. You’ll almost always need to create more indices than vertices because there are more indices than discrete locations required for most geometry, but that depends on what kind of geometry you’re creating. This brings us to one of the more confusing elements of working with VboMesh::Layout, which is the idea of indices and dynamic indices in particular. The indices set the location of an Vec3f in the order of drawing elements. Remember how in the first example before introducing the TriMesh, you saw how the order that the vertices were created in affected how they were connected the other vertices? Well, the index for a vertex in the VboMesh works much the same way, indicating where the vertex is in relation to the other vertices and how it should be connected to them. In the graphic below the ways to draw a square using GL_QUADS is shown at the top and left and using GL_TRIANGLE_STRIP at the bottom and right.

Cinder always requires that you create the indices for a mesh before you create the positions for that mesh, because conceptually a vertice without a index can’t be properly integrated into the mesh and vice versa. The two modes of storing positions means that the process for working with VboMeshes that dynamic positions versus those that have static positions is slightly different. We’ll do the static first:

gl::VboMesh::Layout layout;
layout.setStaticIndices();
layout.setDynamicColorsRGBA();
layout.setStaticPositions();

int vertCount = 24;
int quadCount = 6;
mesh = gl::VboMesh(vertCount, quadCount * 4, layout, GL_QUADS);

When you’re using static positions you can simply assign positions using a vector of Vec3f objects and calling VboMesh::bufferPositions(). So, our VboMesh creation pre looks like this, note the indices being assigned before the positions:

vector indices;
int i=0;
while(i < 24){
indices.push_back(i);
i++;
}

mesh.bufferIndices(indices);

positions.push_back(Vec3f(100, 200, 1));
positions.push_back(Vec3f( 200, 200, 1));
positions.push_back(Vec3f( 200, 100, 1));
positions.push_back(Vec3f(100, 100, 1));

positions.push_back(Vec3f( 200, 200, 1));
positions.push_back(Vec3f( 200, 200, 100));
positions.push_back(Vec3f( 200, 100, 100));
positions.push_back(Vec3f( 200, 100, 1));

positions.push_back(Vec3f( 200, 200, 100));
positions.push_back(Vec3f(100, 200, 100));
positions.push_back(Vec3f(100, 100, 100));
positions.push_back(Vec3f( 200, 100, 100));

positions.push_back(Vec3f(100, 200, 100));
positions.push_back(Vec3f(100, 200, 1));
positions.push_back(Vec3f(100, 100, 1));
positions.push_back(Vec3f(100, 100, 100));

positions.push_back(Vec3f(100, 200, 100));
positions.push_back(Vec3f( 200, 200, 100));
positions.push_back(Vec3f( 200, 200, 1));
positions.push_back(Vec3f(100, 200, 1));

positions.push_back(Vec3f(100, 100, 100));
positions.push_back(Vec3f( 200, 100, 100));
positions.push_back(Vec3f( 200, 100, 1));
positions.push_back(Vec3f(100, 100, 1));

// now we can buffer positions
mesh.bufferPositions(positions);

This creates the same old boring looking white cube. Until we update the colors. This requires retrieving the color values from the mesh and using an iterator to examine the values, which is a familiar pattern in Cinder. The VertexIter object is created to iterate over all the values in the mesh, you assign to the result of VboMesh::mapVertexBuffer() like so:

void VboTutorial::update()
{
float g = sin(getElapsedSeconds());
float b = cos(getElapsedSeconds());
gl::VboMesh::VertexIter iter = mesh.mapVertexBuffer();
for( int x = 0; x < 24; ++x ) {
//positions.at(x) *= 1.001;
iter.setColorRGBA( ColorA( 1 - (g+b/3), g, b, 0.5) );
++iter;
}
}

Dynamic positions are handled in the same way as dynamic colors. To create the dynamic positions for the simple cube in the VboMesh, setting up the VboMesh is going to look familiar, assigning the indices via a vector of uints, but the actual updating of the positions is going to look quite different because you can’t pass a position values into the VboMesh by using VboMesh::bufferPositions(). Instead, you create an VertexIter to iterate over and modify the positions:

void VboTutorialApp::setup()
{
gl::VboMesh::Layout layout;
layout.setStaticIndices();
layout.setDynamicColorsRGBA();
layout.setDynamicPositions();

int vertCount = 24;
int quadCount = 6;
mesh = gl::VboMesh(vertCount, quadCount * 4, layout, GL_QUADS);
vector indices;
int i=0;
while(i < 24){
indices.push_back(i);
i++;
}

mesh.bufferIndices(indices);
}

Note that creating the VboMesh only buffers the indices and not any positions. Creating position data is done via a VertexIter, for instance, in the update() of the application:

void VboTutorialApp::update()
{
gl::VboMesh::VertexIter iter = mesh.mapVertexBuffer();
for( int idx = 0; idx < numberOfVertices; ++idx ) {
iter.setPosition( Vec3f( xVal, yVal, zVal) );
++iter;
}
}

The positions are automatically generated to be unit vector values, stored both on the graphics card and in the application memory, When you modify the VertexIter you mark a certain range of data as needing to be uploaded to the graphics card. For those interested in the underlying functionality, this uses the stored local copy of the vertex and index information to pass to glBufferDataARB(). The values are only uploaded to the card when index, color, or position data is modified saving time and space on any draw operation when changes haven’t been made. This is an important nuance because the idea of keeping records both on the graphics card and in the application memory is used throughout the VboMesh, color data, texture positions, and even indices. If you create dynamic texture positions using either setDynamicTexCoords2d() if you’re using 2d coordinates or setDynamicTexCoords3d() if you’re using 3d coordinates, you can reposition a texture on a mesh dynamically. There are some key differences between 2d textures and 3d textures that you can read up more on here . To move a texture around the x axis of the mesh, create a VertexIter and then modify the texture using setTexCoord3d2() or setTexCoord2d2() depending on the texture type that you’re using. For instance, shifting a 2d texture around:

// dynmaically generate our new positions based on a simple sine wave for mesh
gl::VboMesh::VertexIter iter2 = mVboMesh2.mapVertexBuffer();
for( int x = 0; x < VERTICES_X; ++x ) {
for( int z = 0; z < VERTICES_Z; ++z ) {
float height = sin( z / (float)VERTICES_Z * zFreq + x / (float)VERTICES_X * xFreq + offset ) / 5.0f;
iter2.setPosition( Vec3f( x / (float)VERTICES_X, height, z / (float)VERTICES_Z ) + Vec3f( 0, 0.5, 0 ) );
iter.setTexCoord2d2( Vec2f( x / (float)VERTICES_X, z / (float)VERTICES_Z ));
++iter2;
}
}

You can also copy the values from one VboMesh to another, retrieving values from a mesh using the VboMesh::getIndexVbo() to get the indices from a mesh and VboMesh::getStaticVbo() to get the vertex data from the mesh. The VboMesh can be constructed like so assuming that there’s another VboMesh called otherMesh:

mesh = gl::VboMesh( numberVertices, numberQuads * 4, otherMesh.getLayout(), GL_QUADS, &otherMesh.getIndexVbo(), NULL, &otherMesh.getDynamicVbo());

You may be noticing the last two parameters, which are pointers to either the static or dynamic VBO data from the source VBO.

VboMesh( size_t numVertices, size_t numIndices, Layout layout, GLenum primitiveType, Vbo *indexBuffer, Vbo *staticBuffer, Vbo *dynamicBuffer );

Which you use will depend on how the source has been created, using the dynamic properties from a source VBO requires that the source has dynamic positionsVBO with dynamic properties. Using the static values from a buffer means copying the values over so that they can be manipulated independently. This means that the target mesh will have the same properties as the source mesh until they are altered. Using the dynamic properties of a mesh means that the target mesh is using the same data as the source and changes to the source will be reflected in the target mesh. This makes it easy to reflect changes across meshes, for instance color effects or textures, but makes your meshes dependent on the source mesh.

For the last trick of interoperation between the mesh types in Cinder, a VboMesh can load a mesh from a TriMesh instance, which means that you can easily import and export an .obj file to a VBO with ease, for instance:

ObjLoader loader( loadResource( OBJ_FILE_RESOURCE ));
loader.load(&trimesh);
gl::VboMesh mesh = gl::VboMesh(trimesh);

And that gets you from a modeling tool to a VBO quite painlessly.

As a final demonstration of how much is built into the VBO and mesh handling in Cinder you can easily create normals and set the normals for a VBO using the VertexIter object and its setNormal() method. A normal is simply a vector that’s perpendicular to the face of a triangle or quad in a mesh or other piece of OpenGL geometry and they’re quite important if you plan to use lighting of any kind when rendering a TriMesh or VBO to the screen. The normal essentially tells OpenGL how any light is to be reflected off of a particular face. This allows you to create shading, depth, and other real world lighting effects. It’s also a little out of the scope of this guide, so I’ll point you to a chapter from OpenGL Programming aka “The Red Book” Using the VertexIter allows you set the normal position for each vertex in a VBO (or in a TriMesh for that matter) by iterating through the vertices of the VBO and setting each normal with an appropriate value. The algorithm here was taken from the http://www.opengl.org/wiki/Calculating_a_Surface_Normal OpenGL wiki which explains the algorithm in a little bit greater detail, as well as providing an alternative algorithm.

Vec3f v0, v1, v2;

// dynmaically generate our new positions based on a simple sine wave
gl::VboMesh::VertexIter iter = mVboMesh.mapVertexBuffer();
for( int x = 0; x < VERTICES_X; ++x ) {
for( int z = 0; z < VERTICES_Z ; ++z ) {

float height = sin( z / (float)VERTICES_Z * zFreq + x / (float)VERTICES_X * xFreq + offset ) / 2.0f;
if( currentVert == 0) {
v0.set( x / (float)VERTICES_X, height, z / (float)VERTICES_Z );
++currentVert;
} else if ( currentVert == 1) {
v1.set( x / (float)VERTICES_X, height, z / (float)VERTICES_Z );
++currentVert;
} else {

//v3.set( x / (float)VERTICES_X, height, z / (float)VERTICES_Z );
v2.set( x / (float)VERTICES_X, height, z / (float)VERTICES_Z );

Vec3f uu = v2 - v0;
Vec3f vv = v1 - v0;
Vec3f n = uu.cross(vv).normalized();

iter.setPosition(v0);
iter.setNormal(n);
++iter;
iter.setPosition(v1);
iter.setNormal(n);
++iter;
iter.setPosition(v2);
iter.setNormal(n);
++iter;
currentVert = 0;

}
}
}

So hopefully you’re feeling a little more comfortable not only with creating and manipulating meshes in Cinder, but also with underestanding what some of the underlying mechanics of a mesh and OpenGL geometry are as well. This guide has barely scratched the surface, but it should be enough to get you started. For more information you can check the excellent references at songho or to the reference for the TriMesh and VBO mesh. Have fun, and make something beautiful.

Joshua Noble is a writer, designer, and programmer based in Portland, Oregon and New York City. He’s the author of, most recently, Programming Interactivity and the forthcoming book Research for Living.

Posted on: 21/03/2011

Posted in: Cinder, Tutorials

Post tags:

    • http://twitter.com/comkee Steffen Fiedler

      Great introduction – thanks for sharing!

    • http://twitter.com/I33N Benjamin Petit

      Really nice introduction and clean tutorial!
      Beware of “&” in your code and “songho” reference wich links to your local path.
      Ben

    • Tapete

      That’s exactly what I needed! Thanks.

    • http://twitter.com/peterpaulrubens Stephan Thiel

      great work!

    • Jamie655321

      Good tutorial, thanks!
      Do you know if there’s a similar class for OpenFrameworks?

    • http://www.creativeapplications.net Filip

      Mike Tucker (http://www.mike-tucker.com) posted a repo on GitHub if you are interested in the source https://github.com/miketucker/Cinder—Guide-to-Meshes

    • http://twitter.com/ForeverScape Vance Feldman

      Very useful for my new adventurs in c++, but that shape is an icosahedron, not an isodecahedron. Maybe you’re thinking of the Icosidodecahedron, which has triangles and pentagons. 

    • Ventoline

      could just develop a little on the line “Vec3f n = uu.cross(vv).normalized();” I am not sure what cross refers to..Great tutorial!

    • Niall Henn

      This tutorial is a little old, but I’m just starting with Cinder/xcode, (coming from processing) and I’m a bit unsure if I’m putting things in the right place.
      Can anyone tell me how to define either the ‘mesh’ or ‘mMesh’ objects men 

    • Dazzid

      Love this tutorials and also amazed by Cinder, hope to see more of those  

    • http://www.lab101.be Kris Meeusen

      @fce57afaaab1e42e2432fc0fe1af8648:disqus it’s called ofMesh in openFrameworks

    • pritam

      Hi …great tutorial..thanx for sharing.
      I am working on image warping. using opengls for iphone.
      i created the grid of vertices and texture but when i am changing the position of some vertices i got the result shown in image…would i need to change vertices and texture coordinates or only vertices..

    • http://twitter.com/camb416 camb416

      TriMesh mesh;
      TriMesh mMesh;

    • http://twitter.com/camb416 camb416

      Thanks for the tutorial… I’ve been trying and failing to learn this stuff for a while. a small fix I found:

      error: Cannot refer to class template ‘vector’ without a template argument list

      fix:
      std::vector col = mMesh.getColorsRGB();
      std::vector vec = mMesh.getVertices();

      should read:

      std::vector col = mMesh.getColorsRGB();
      std::vector vec = mMesh.getVertices();