O'Reilly logo

iPhone 3D Programming by Philip Rideout

Stay ahead with the world's most comprehensive technology and business learning platform.

With Safari, you learn the way you learn best. Get unlimited access to videos, live online training, learning paths, books, tutorials, and more.

Start Free Trial

No credit card required

Filling the Wireframe with Triangles

In this section we’ll walk through the steps required to render parametric surfaces with triangles rather than lines. First we need to enhance the ISurface interface to support the generation of indices for triangles rather than lines. Open Interfaces.hpp, and make the changes shown in bold in Example 4-3.

Example 4-3. Enhanced ISurface interface

struct ISurface {
    virtual int GetVertexCount() const = 0;
    virtual int GetLineIndexCount() const = 0;
    virtual int GetTriangleIndexCount() const = 0;
    virtual void GenerateVertices(vector<float>& vertices) const = 0;
    virtual void GenerateLineIndices(vector<unsigned short>& indices) const = 0;
    virtual void
      GenerateTriangleIndices(vector<unsigned short>& indices) const = 0;
    virtual ~ISurface() {}

You’ll also need to open ParametricSurface.hpp and make the complementary changes to the class declaration of ParametricSurface shown in Example 4-4.

Example 4-4. Enhanced ParametricSurface interface

class ParametricSurface : public ISurface {
    int GetVertexCount() const;
    int GetLineIndexCount() const;
    int GetTriangleIndexCount() const;
    void GenerateVertices(vector<float>& vertices) const;
    void GenerateLineIndices(vector<unsigned short>& indices) const;
    void GenerateTriangleIndices(vector<unsigned short>& indices) const;

Next open ParametericSurface.cpp, and add the implementation of GetTriangleIndexCount and GenerateTriangleIndices per Example 4-5.

Example 4-5. ParametricSurface::GenerateTriangleIndices

int ParametricSurface::GetTriangleIndexCount() const
    return 6 * m_slices.x * m_slices.y;

ParametricSurface::GenerateTriangleIndices(vector<unsigned short>& indices) const
    vector<unsigned short>::iterator index = indices.begin();
    for (int j = 0, vertex = 0; j < m_slices.y; j++) {
        for (int i = 0; i < m_slices.x; i++) {
            int next = (i + 1) % m_divisions.x;
            *index++ = vertex + i;
            *index++ = vertex + next;
            *index++ = vertex + i + m_divisions.x;
            *index++ = vertex + next;
            *index++ = vertex + next + m_divisions.x;
            *index++ = vertex + i + m_divisions.x;
        vertex += m_divisions.x;

Example 4-5 is computing indices for two triangles, as shown in Figure 4-3.

Generating triangle indices for a parametric surface

Figure 4-3. Generating triangle indices for a parametric surface

Now we need to modify the rendering engine so that it calls these new methods when generating VBOs, as in Example 4-6. The modified lines are shown in bold. Make these changes to both RenderingEngine.ES1.cpp and RenderingEngine.ES2.cpp.

Example 4-6. RenderingEngine Modifications for triangles

void RenderingEngine::Initialize(const vector<ISurface*>& surfaces)
    vector<ISurface*>::const_iterator surface;
    for (surface = surfaces.begin(); surface != surfaces.end(); ++surface) {
        // Create the VBO for the vertices.
        vector<float> vertices;
        GLuint vertexBuffer;
        glGenBuffers(1, &vertexBuffer);
        glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer);
                     vertices.size() * sizeof(vertices[0]),
        // Create a new VBO for the indices if needed.
        int indexCount = (*surface)->GetTriangleIndexCount();
        GLuint indexBuffer;
        if (!m_drawables.empty() && indexCount == m_drawables[0].IndexCount) {
            indexBuffer = m_drawables[0].IndexBuffer;
        } else {
            vector<GLushort> indices(indexCount);
            glGenBuffers(1, &indexBuffer);
            glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuffer);
                         indexCount * sizeof(GLushort),
        Drawable drawable = { vertexBuffer, indexBuffer, indexCount};



void RenderingEngine::Render(const vector<Visual>& visuals) const
    glClearColor(0.5, 0.5f, 0.5f, 1);
    vector<Visual>::const_iterator visual = visuals.begin();
    for (int visualIndex = 0; 
         visual != visuals.end(); 
         ++visual, ++visualIndex) 

        // Draw the surface.
        int stride = sizeof(vec3);
        const Drawable& drawable = m_drawables[visualIndex];
        glBindBuffer(GL_ARRAY_BUFFER, drawable.VertexBuffer);
        glVertexPointer(3, GL_FLOAT, stride, 0);
        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, drawable.IndexBuffer);
        glDrawElements(GL_TRIANGLES, drawable.IndexCount, GL_UNSIGNED_SHORT, 0);

Getting back to the sample app, at this point the wireframe viewer has officially become ModelViewer; feel free to build it and try it. You may be disappointed—the result is horribly boring, as shown in Figure 4-4. Lighting to the rescue!

ModelViewer without lighting

Figure 4-4. ModelViewer without lighting

With Safari, you learn the way you learn best. Get unlimited access to videos, live online training, learning paths, books, interactive tutorials, and more.

Start Free Trial

No credit card required