Escuela Técnica Superior de Ingeniería

 

Grado en Ingeniería Informática

Realidad Virtual

Curso 2023/2024

 

Práctica 3

Dibujando en el espacio

 

Objetivos

 

En esta práctica se explica como desarrollar un modelo 3D formado por figuras geométricas básicas. Para ello se va a a crear un conjunto de clases que desarrollan estas figuras geométricas. El desarrollo de las clases requiere calcular la posición de los vértices de cada figura de manera que quede centrada en la escena. El programa gráfico añade una matriz de transformación de los vértices para poder incorporar rotaciones y traslaciones en los cuerpos.

 

 

Código de la práctica

 

 

Desarrollo del modelo

 

El punto de partida de esta práctica es el código generado en la práctica 2, que desarrolla un proyecto en VisualStudio 2019 en el que se construye una ventana sobre MS-Windows y se modifica su configuración para poder generar el contenido de la ventana por medio de las funciones de OpenGL.

Los ficheros main.cpp, CGApplication.cpp y CGApplication.h contienen el código encargado de configurar la ventana de MS-Windows y desarrollar la interfaz entre los eventos del sistema operativo y la parte gráfica de la aplicación (respuesta a eventos de teclado, a eventos de ratón, a cambios de tamaño, etc.). Estos ficheros no deben ser modificados.

Los ficheros CGModel.h y CGModel.cpp contienen el código encargado de desarrollar el modelo 3D. En la práctica anterior el modelo desarrollado era un único triángulo que se desplazaba por la ventana. El objetivo de esta práctica, por tanto, es modificar estos ficheros para generar algunas figuras geométricas y mostrarlas en la ventana principal de la aplicación.

A continuación se muestra el contenido del fichero CGModel.h. Este fichero contiene la definición de la clase CGModel. La interfaz pública de esta clase no se va a modificar, ya que se refiere a las funciones que se llaman desde el fichero CGApplication.cpp. Con respecto a la parte privada, la modificación de la clase CGModel consiste en incluir las referencias a 8 objetos de la clase CGFigure, así como los campos figure, xAngle e yAngle. La clase CGFigure está definida en el archivo CGFigure.h (añadido en los #include) y define un objeto que representa a una figura geométrica. En esta práctica vamos a presentar la definición de 8 figuras geométricas diferentes: pirámide, cubo, icosaedro, cono, cilindro, disco, esfera y toro. Los campos fig almacenarán un objeto de cada tipo. El campo figure indica cual de las 8 figuras se representará en cada momento. Para cambiar de figura se pulsará en la tecla 'F', por lo que debemos modificar el método key_pressed() para responder a esta tecla. Los campos xAngle e yAngle contendrán la orientación de la figura. Para orientar la figura realizaremos en primer lugar un giro de ángulo xAngle sobre el eje X y, a continuación, un giro de tamaño yAngle sobre el eje Y. Vamos a asociar las teclas de flecha modificaciones en estos ángulos. De esta manera podremos girar la figura mostrada por medio de los cursores y modificar la figura con la tecla 'F'. A partir de los valores de xAngle e yAngle se calculará la matriz de transformación que se le enviará al VertexShader. El campo projection permite almacenar una matriz de proyección que se utiliza para transformar el espacio de representación de los objetos en el clipping volume. El campo program contiene una referencia al programa gráfico a ejecutar.

#pragma once

#include <GL/glew.h>
#include "CGShaderProgram.h"
#include "CGFigure.h"

class CGModel
{
public:
  void initialize(int w, int h);
  void finalize();
  void render();
  void update();
  void key_pressed(int key);
  void mouse_button(int button, int action);
  void mouse_move(double xpos, double ypos);
  void resize(int w, int h);

private:
  CGShaderProgram* program;
  CGFigure* fig0;
  CGFigure* fig1;
  CGFigure* fig2;
  CGFigure* fig3;
  CGFigure* fig4;
  CGFigure* fig5;
  CGFigure* fig6;
  CGFigure* fig7;

  GLfloat xAngle;
  GLfloat yAngle;
  int figure;

  glm::mat4 projection;
};

Con respecto al archivo CGModel.cpp, para desarrollar esta práctica modificaremos las funciones initialize(), finalize(), render(), key_pressed() y resize(). En el caso de la función update(), en este caso no vamos a animar la escena por lo que simplemente eliminaremos su contenido. Las funciones mouse_move() y mouse_button() no tienen contenido.

La función initialize() comienza asignando los valores a los campos privados del modelo. Inicialmente la figura a mostrar será fig0 y los ángulos de orientación serán también 0. Para inicializar las figuras se hace una llamada a los constructores de cada una. Todas ellas son subclases de CGFigure y están declaradas en sus correspondientes archivos de cabecera. La inicialización incluye también una llamada a resize() para configurar el viewport y la matriz de proyección. Para terminar se incluyen algunas opciones de configuración de OpenGL. Entre otras cuestiones se indica que los polígonos serán mostrados en modo arista para que podamos apreciar bien las caras. (El modo GL_FILL muestra todas las caras con el mismo color y no permite apreciar la frontera entre dos caras). El código de la función es el siguiente:

void CGModel::initialize(int w, int h)
{
  // Crea el programa
  program = new CGShaderProgram(IDR_SHADER1, IDR_SHADER2, -1, -1, -1);
  //program = new CGShaderProgram("shaders/VertexShader.glsl", 
  //                              "shaders/FragmentShader.glsl", NULL, NULL, NULL);
  if (program->IsLinked() == GL_FALSE) return;
  program->Use();

  // Inicializa la posición de las figuras
  figure = 0;
  xAngle = 0.0f;
  yAngle = 0.0f;

  // Crea las figuras
  fig0 = new CGCube(25.0f);
  fig1 = new CGPiramid(25.0f);
  fig2 = new CGCone(5, 20, 25.0f, 25.0f);
  fig3 = new CGCylinder(20, 20, 25.0f, 25.0f);
  fig4 = new CGSphere(20, 20, 25.0f);
  fig5 = new CGDisk(5, 20, 12.5f, 25.0f);
  fig6 = new CGTorus(10, 20, 10.0f, 25.0f);
  fig7 = new CGIcosahedron(15.0f);

  // Asigna el viewport y el clipping volume
  resize(w, h);

  // Opciones de dibujo
  glEnable(GL_DEPTH_TEST);
  glEnable(GL_CULL_FACE);
  glFrontFace(GL_CCW);
  glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
}

La función finalize() se encarga de liberar los recursos utilizados por el modelo. En este caso la función debe liberar los objetos fig y el programa gráfico.

void CGModel::finalize()
{
  delete fig0;
  delete fig1;
  delete fig2;
  delete fig3;
  delete fig4;
  delete fig5;
  delete fig6;
  delete fig7;
  delete program;
}

La función resize() es la respuesta ante modificaciones del tamaño de la ventana. También se llama dentro de la función initialize() para fijar el tamaño inicial. En la práctica anterior esta función se limitaba a asignar el viewport de la imagen ya que las coordenadas del triángulo se expresaban directamente en el sistema de coordenadas homogéneo. Lo normal es que el modelo utilice su propio sistema de coordenadas y que se realice una transformación entre el volumen del modelo que se va a representar y el volumen clip. En este caso se va a realizar una transformación de tipo proyección en perspectiva. La función glm::frustum() permite generar la matriz de transformación de esta proyeccón de una manera sencilla. Para que el aspecto de la imagen no se deforme la proyección se calcula con la misma proporción de aspecto entre las coordenadas X e Y que existe en el tamaño de la ventana.

void CGModel::resize(int w, int h)
{
  double fov = glm::radians(15.0);
  double sin_fov = sin(fov);
  double cos_fov = cos(fov);
  if (h == 0) h = 1;
  GLfloat aspectRatio = (GLfloat)w / (GLfloat)h;
  GLfloat wHeight = (GLfloat)(sin_fov * 0.2 / cos_fov);
  GLfloat wWidth = wHeight * aspectRatio;

  glViewport(0, 0, w, h);
  projection = glm::frustum(-wWidth, wWidth, -wHeight, wHeight, 0.2f, 400.0f);
  // projection = glm::perspective((float)fov, (float)aspect, 0.2f, 400.0f);
}

La función render() es la encargada de dibujar la escena. Aunque el dibujo a desarrollar es bastante complejo, el contenido de esta función es muy simple porque los aspectos del dibujo de cada figura son responsabilidad del método Draw() de cada objeto. Por tanto, la función se limita a borrar la escena (usando el color blanco), seleccionar el rojo como color de las figuras, realizar la traslación y las rotaciones respecto a los ejes X e Y y lanzar el método Draw() de la figura seleccionada. Para realizar las rotaciones se utiliza el método glm::rotate(), que genera una matriz de rotación considerando el ángulo de rotación (en radianes) y el eje de rotación. Para realizar una traslación se utiliza el método glm::translate(). La matriz de transformación que se envía al VertexShader es el producto entre la matriz de proyección y la matriz de rotación y traslación.

void CGModel::render()
{
  // Limpia el framebuffer
  glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

  // Matriz de transformación
  glm::mat4 location = glm::mat4(1.0f);
  location = glm::translate(location, glm::vec3(0.0f, 0.0f, -180.0f));
  location = glm::rotate(location, glm::radians(yAngle), glm::vec3(0.0f,1.0f,0.0f));
  location = glm::rotate(location, glm::radians(xAngle), glm::vec3(1.0f,0.0f,0.0f));

  glm::mat4 transform = projection * location;

  switch (figure) {
  case 0: fig0->Draw(program, transform); break;
  case 1: fig1->Draw(program, transform); break;
  case 2: fig2->Draw(program, transform); break;
  case 3: fig3->Draw(program, transform); break;
  case 4: fig4->Draw(program, transform); break;
  case 5: fig5->Draw(program, transform); break;
  case 6: fig6->Draw(program, transform); break;
  case 7: fig7->Draw(program, transform); break;
  }
}

La función key_pressed() define la respuesta del modelo a los eventos de teclado. En este caso, al pulsar los cursores arriba y abajo incrementaremos o decrementaremos el ángulo xAngle. Cuando se pulse los cursores izquierda o derecha se modificará el valor del ángulo yAngle. Por último, al pulsar la tecla 'F' se modificará el campo figure lo que provocará que se dibuje una figura diferente.

void CGModel::key_pressed(int key)
{
  switch (key)
  {
  case GLFW_KEY_F:
    figure = (figure + 1) % 8;
    break;
  case GLFW_KEY_UP:
    xAngle += 5.0f;
    break;
  case GLFW_KEY_DOWN:
    xAngle -= 5.0f;
    break;
  case GLFW_KEY_LEFT:
    yAngle -= 5.0f;
    break;
  case GLFW_KEY_RIGHT:
    yAngle += 5.0f;
    break;
  }
}

 

 

Programa gráfico

 

El programa a ejecutar en la tarjeta gráfica está compuesto por el VertexShader y el FragmentShader, que se describen en los archivos "VertexShader.htm" y "FragmentShader.htm" respectivamente. Con respecto a la práctica anterior se ha modificado el VertexShader para considerar una variable uniforme que contiene una matriz 4x4. Esta matriz permite realizar una transformación sobre la posición de los vértices.

#version 400

in vec3 VertexPosition;

uniform mat4 Transform;

void main()
{
   gl_Position = Transform * vec4(VertexPosition, 1.0);
}

Por su parte, el FragmentShader es el mismo que el utilizado en la práctica anterior. Este FragmentShader se limita a dibujar de rojo los píxeles de cada fragmento.

#version 400

out vec4 FragColor;

void main()
{
   FragColor = vec4(1.0,0.0,0.0,1.0);
}  

 

 

Figuras geométricas

 

Todas las figuras geométricas que vamos a desarrollar en este proyecto van a ser subclases de una clase abstracta llamada CGFigure. Esta clase va a describir un objeto tridimensional formado por triángulos. Para construir los objetos se utilizará una lista de vértices (cuyas posiciones se van a almacenar en el campo vertices). Para evitar la duplicidad de vértices, los triángulos se van a referenciar por medio de una lista de índices que se almacenan en el campo indices. Los campos numVertices y numFaces almacenarán el número de vértices y de triángulos que forman el objeto. Para definir los objetos en el programa gráfico se utiliza un VertexArrayObject (cuyo identificador se almacena en el campo VAO) y dos VertexBufferObjects (cuyos identificadores se almacenan en el campo VBO). El contenido del fichero de cabecera de la clase CGFigure es el siguiente:

#pragma once

#include <GL/glew.h>
#include <glm/glm.hpp>
#include "CGShaderProgram.h"

#define VERTEX_DATA     0
#define INDEX_DATA      1

//
// CLASE: CGFigure
//
// DESCRIPCIÓN: Clase abstracta que representa un objeto descrito mediante
//              VAO para su renderizado mediante shaders
// 
class CGFigure {
protected:
  GLushort* indexes;  // Array of indexes 
  GLfloat* vertices;  // Array of vertices

  int numFaces;     // Number of faces
  int numVertices;  // Number of vertices
  GLuint VBO[2];
  GLuint VAO;

public:
  ~CGFigure();
  void InitBuffers();
  void Draw(CGShaderProgram* program, glm::mat4 transform);
};

Los métodos de la clase CGFigure, que utilizarán por herencia todas las figuras geométricas, son el destructor de la clase, el método InitBuffers() y el método Draw(). Las subclases de CGFigure deberán crear el contenido de los campos vertices e indices en su constructor. A partir de esos valores, el método InitBuffers() se encarga de crear el VertexArrayObject y los dos VertexBufferObjects, almacenar el contenido de los buffers tomándolo de los campos vertices e indices y definir los atributos del VertexArrayObject. El método Draw() es el encargado de lanzar el dibujo de la figura. Para ello asigna la matriz de transformación como variable uniforme, activa el VertexArrayObject y lanza el método glDrawElements() considerando el número de triángulos a dibujar. El destructor de la clase se encarga de liberar los buffers.

#include "CGFigure.h"
#include <GL/glew.h>
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>

CGFigure::~CGFigure()
{
  if (vertices != NULL) delete[] vertices;
  if (indexes != NULL) delete[] indexes;

  // Delete vertex buffer objects
  glDeleteBuffers(2, VBO);
  
  // Delete vertex array object
  glDeleteVertexArrays(1, &VAO);
}

void CGFigure::InitBuffers()
{
  // Create the Vertex Array Object
  glGenVertexArrays(1, &VAO);
  glBindVertexArray(VAO);

  // Create the Vertex Buffer Objects
  glGenBuffers(2, VBO);

  // Copy data to video memory
  // Vertex data
  int buffersize = sizeof(GLfloat) * numVertices * 3;
  glBindBuffer(GL_ARRAY_BUFFER, VBO[VERTEX_DATA]);
  glBufferData(GL_ARRAY_BUFFER, buffersize, vertices, GL_STATIC_DRAW);

  // Indexes
  buffersize = sizeof(GLushort) * numFaces * 3;
  glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, VBO[INDEX_DATA]);
  glBufferData(GL_ELEMENT_ARRAY_BUFFER, buffersize, indexes, GL_STATIC_DRAW);

  delete[] vertices;
  delete[] indexes;

  vertices = NULL;
  indexes = NULL;

  glEnableVertexAttribArray(0); // Vertex position
  glBindBuffer(GL_ARRAY_BUFFER, VBO[VERTEX_DATA]);
  glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);
}

void CGFigure::Draw(CGShaderProgram* program, glm::mat4 transform)
{
  glm::mat4 matrix = transform;
  program->SetUniformMatrix4("Transform", transform);

  glBindVertexArray(VAO);
  glDrawElements(GL_TRIANGLES, numFaces * 3, GL_UNSIGNED_SHORT, NULL);
}

 

 

Descripción de un cubo

 

Fichero de cabecera.

#pragma once

#include <GL/glew.h>
#include "CGFigure.h"

//
// CLASE: CGCube
//
// DESCRIPCIÓN: Representa un cubo de lado 2·s. 
// 
class CGCube : public CGFigure {
public:
  CGCube(GLfloat s);
};

Fichero de código.

#include "CGCube.h"
#include <GL/glew.h>
#include "CGFigure.h"

CGCube::CGCube(GLfloat s)
{
    numFaces = 12; // Number of faces
    numVertices = 24; // Number of vertices

    GLfloat p_vertices[24][3] = {
       { +s, +s, +s }, // A0 // Positive X
       { +s, -s, +s }, // D0 
       { +s, -s, -s }, // D1 
       { +s, +s, -s }, // A1 

       { -s, +s, +s }, // B0 // Positive Y
       { +s, +s, +s }, // A0 
       { +s, +s, -s }, // A1 
       { -s, +s, -s }, // B1 

       { -s, -s, +s }, // C0 // Negative X
       { -s, +s, +s }, // B0 
       { -s, +s, -s }, // B1 
       { -s, -s, -s }, // C1

       { +s, -s, +s }, // D0 // Negative Y
       { -s, -s, +s }, // C0 
       { -s, -s, -s }, // C1
       { +s, -s, -s }, // D1 

       { +s, +s, +s }, // A0 // Positive Z
       { -s, +s, +s }, // B0 
       { -s, -s, +s }, // C0 
       { +s, -s, +s }, // D0 

       { +s, +s, -s }, // A1 // Negative Z
       { +s, -s, -s }, // D1 
       { -s, -s, -s }, // C1 
       { -s, +s, -s } // B1 
    };

    GLushort p_indexes[12][3] = { // Array of indexes
       { 0, 1, 2 },
       { 0, 2, 3 },
       { 4, 5, 6 },
       { 4, 6, 7 },
       { 8, 9, 10 },
       { 8, 10, 11 },
       { 12, 13, 14 },
       { 12, 14, 15 },
       { 16, 17, 18 },
       { 16, 18, 19 },
       { 20, 21, 22 },
       { 20, 22, 23 }
    };

    vertices = new GLfloat[numVertices * 3];
    for (int i = 0; i < numVertices; i++) 
        for (int j = 0; j < 3; j++) 
            vertices[3 * i + j] = p_vertices[i][j];

    indexes = new GLushort[numFaces * 3];
    for (int i = 0; i < numFaces; i++)
        for (int j = 0; j < 3; j++) 
            indexes[3 * i + j] = p_indexes[i][j];
            
    InitBuffers();
 }

El aspecto de la figura es el siguiente.

Cubo

 

 

Descripción de una pirámide

 

Fichero de cabecera.

#pragma once

#include <GL/glew.h>
#include "CGFigure.h"

//
// CLASE: CGPiramid
//
// DESCRIPCIÓN: Representa una pirámide de base cuadrada de 
//              lado 2·s y altura 2·s.
// 
class CGPiramid : public CGFigure {
public:
  CGPiramid(GLfloat s);
};

Ficehro de código.

#include "CGPiramid.h"
#include <GL/glew.h>
#include <math.h>
#include "CGFigure.h"

CGPiramid::CGPiramid(GLfloat s)
{
   numFaces = 6; // Number of faces
   numVertices = 16; // Number of vertices

   GLfloat p_vertices[16][3] = {
      { 0.0f, 0.0f, s }, // E // Positive X
      { s, -s, -s }, // B 
      { s, s, -s }, // A

      { 0.0f, 0.0f, s }, // E // Positive Y
      { s, s, -s }, // A
      { -s, s, -s }, // D

      { 0.0f, 0.0f, s }, // E // Negative X
      { -s, s, -s }, // D
      { -s, -s, -s }, // C

      { 0.0f, 0.0f, s }, // E // Negative Y
      { -s, -s, -s }, // C
      { s, -s, -s }, // B 

      { s, s, -s }, // A // Negative Z
      { s, -s, -s }, // B 
      { -s, -s, -s }, // C
      { -s, s, -s }, // D
   };

   GLushort p_indexes[6][3] = { // Array of indexes
      { 0, 1, 2 },
      { 3, 4, 5 },
      { 6, 7, 8 },
      { 9, 10, 11 },
      { 12, 13, 14 },
      { 12, 14, 15 }
   };

   vertices = new GLfloat[numVertices * 3];
   for (int i = 0; i < numVertices; i++) 
      for (int j = 0; j < 3; j++) 
         vertices[3 * i + j] = p_vertices[i][j];
   indexes = new GLushort[numFaces * 3];
   for (int i = 0; i < numFaces; i++) 
      for (int j = 0; j < 3; j++) 
         indexes[3 * i + j] = p_indexes[i][j];
         
   InitBuffers();
}

El aspecto de la aplicación al representar esta figura es el siguiente:

Pirámide

 

 

Descripción de un cono

 

Fichero de cabecera.

#pragma once

#include <GL/glew.h>
#include "CGFigure.h"

//
// CLASE: CGCone
//
// DESCRIPCIÓN: Representa un cono de altura 'h', radio 'r', 
//              dividido en 'm' líneas y 'p' rodajas. 
//
class CGCone : public CGFigure {
public:
  CGCone(GLint p, GLint m, GLfloat h, GLfloat r);
};

Fichero de código.

#include "CGCone.h"
#include <GL/glew.h>
#include <math.h>
#include "CGFigure.h"

CGCone::CGCone(GLint p, GLint m, GLfloat h, GLfloat r)
{
   numFaces = 2 * p * m; // Number of faces
   numVertices = (p+1)*m+2; // Number of vertices
   vertices = new GLfloat[numVertices * 3];
   indexes = new GLushort[numFaces * 3];

   double module = sqrt(4 * h*h + r*r);
   double xyN = (GLfloat)(2 * h / module);
   double zN = (GLfloat)(r / module);

   int verticesIndex = 0;
   // Centro de la base
   vertices[0] = 0.0f; 
   vertices[1] = 0.0f; 
   vertices[2] = -h;
   verticesIndex += 3;

   // Vértices de la base
   for (int i = 0; i <m; i++)
   {
      GLfloat x = (GLfloat)cos(glm::radians(360.0 * i / m));
      GLfloat y = -(GLfloat)sin(glm::radians(360.0 * i / m));
      vertices[verticesIndex] = x*r;
      vertices[verticesIndex + 1] = y*r;
      vertices[verticesIndex + 2] = -h;
      verticesIndex += 3;
   }

   // Extremo del cono
   vertices[verticesIndex] = 0.0f;
   vertices[verticesIndex+1] = 0.0f;
   vertices[verticesIndex+2] = h;
   verticesIndex += 3;

   // Vértices de los lados
   for (int i = 1; i <= p; i++)
   {
      GLfloat xy = i*r / p;
      GLfloat z = h - 2 * i*h / p;
      for (int j = 0; j < m; j++)
      {
         GLfloat xN = (GLfloat)cos(glm::radians(360.0 * j / m));
         GLfloat yN = (GLfloat)sin(glm::radians(360.0 * j / m));
         GLfloat x = (GLfloat)(xN*xy);
         GLfloat y = (GLfloat)(yN*xy);
         vertices[verticesIndex] = x;
         vertices[verticesIndex + 1] = y;
         vertices[verticesIndex + 2] = z;
         verticesIndex += 3;
      }
   }

   int indicesIndex = 0;
   // Base
   for (int i = 0; i < m - 1; i++)
   {
      indexes[indicesIndex] = 0;
      indexes[indicesIndex + 1] = i + 1;
      indexes[indicesIndex + 2] = i + 2;
      indicesIndex += 3;
   }

   indexes[indicesIndex] = 0;
   indexes[indicesIndex + 1] = m;
   indexes[indicesIndex + 2] = 1;
   indicesIndex += 3;

   // Extremo
   for (int i = 0; i < m - 1; i++)
   {
      indexes[indicesIndex] = m + 1;
      indexes[indicesIndex + 1] = m + 2 + i;
      indexes[indicesIndex + 2] = m + 3 + i;
      indicesIndex += 3;
   }
   indexes[indicesIndex] = m + 1;
   indexes[indicesIndex + 1] = 2*m + 1;
   indexes[indicesIndex + 2] = m + 2;
   indicesIndex += 3;

   // Lados
   for (int j = 1; j < p; j++)
   {
      for (int i = 0; i < m - 1; i++)
      {
         indexes[indicesIndex] = j*m + 2 + i;
         indexes[indicesIndex + 1] = (j+1)*m + 2 + i;
         indexes[indicesIndex + 2] = (j+1)*m + 3 + i;
         indicesIndex += 3;

         indexes[indicesIndex] = j*m + 2 + i;
         indexes[indicesIndex + 1] = (j + 1)*m + 3 + i;
         indexes[indicesIndex + 2] = j*m + 3 + i;
         indicesIndex += 3;
      }

      indexes[indicesIndex] = (j+1)*m + 1;
      indexes[indicesIndex + 1] = (j + 2)*m + 1;
      indexes[indicesIndex + 2] = (j + 1)*m + 2;
      indicesIndex += 3;

      indexes[indicesIndex] = (j+1)*m + 1;
      indexes[indicesIndex + 1] = (j + 1)*m + 2;
      indexes[indicesIndex + 2] = j*m + 2;
      indicesIndex += 3;
   }
   
  InitBuffers();
}

A continuación se muestra una captura de la aplicación mostrando esta figura.

Cono

 

 

Descripción de un cilindro

 

Fichero de cabecera.

#pragma once

#include <GL/glew.h>
#include "CGFigure.h"

//
// CLASE: CGCylinder
//
// DESCRIPCIÓN: Representa un cilindro de radio 'r', longitud '2·l', 
//              dividido en 'p' capas y 'm' líneas.
//
class CGCylinder : public CGFigure {
public:
  CGCylinder(GLint p, GLint m, GLfloat r, GLfloat l);
};

Fichero de código.

#include "CGCylinder.h"
#include <GL/glew.h>
#include <math.h>
#include "CGFigure.h"

CGCylinder::CGCylinder(GLint p, GLint m, GLfloat r, GLfloat l)
{
   numFaces = 2 * m * (p + 1); // Number of faces
   numVertices = (m + 1)*(p + 3); // Number of vertices
   vertices = new GLfloat[numVertices * 3];
   indexes = new GLushort[numFaces * 3];

   int verticesIndex = 0;
   int indexesIndex = 0;

   /* northern polar cap*/
   vertices[0] = 0.0f;
   vertices[1] = 0.0f;
   vertices[2] = l;
   verticesIndex += 3;

   for (int j = 0; j < m; j++)
   {
      GLfloat mCos = (GLfloat)cos(glm::radians(360.0 * j / m));
      GLfloat mSin = (GLfloat)sin(glm::radians(360.0 * j / m));
      vertices[verticesIndex] = mCos * r;
      vertices[verticesIndex + 1] = mSin * r;
      vertices[verticesIndex + 2] = l;
      verticesIndex += 3;

      indexes[indexesIndex] = 0; // center
      indexes[indexesIndex + 1] = j + 1;
      indexes[indexesIndex + 2] = (j + 2>m ? 1 : j + 2);
      indexesIndex += 3;
   }

   /* southern polar cap*/
   vertices[verticesIndex] = 0.0f;
   vertices[verticesIndex + 1] = 0.0f;
   vertices[verticesIndex + 2] = -l;
   verticesIndex += 3;
  
   for (int j = 0; j < m; j++)
   {
      GLfloat mCos = (GLfloat)cos(glm::radians(360.0 * j / m));
      GLfloat mSin = (GLfloat)sin(glm::radians(360.0 * j / m));
      vertices[verticesIndex] = mCos * r;
      vertices[verticesIndex + 1] = -mSin * r;
      vertices[verticesIndex + 2] = -l;
      verticesIndex += 3;

      indexes[indexesIndex] = m + 1; // center
      indexes[indexesIndex + 1] = j + m + 2;
      indexes[indexesIndex + 2] = (j + 2>m ? m + 2 : j + m + 3);
      indexesIndex += 3;
   }


   /* body */
   for (int i = 0; i <= p; i++)
   {
      for (int j = 0; j <= m; j++)
      {
         GLfloat mCos = (GLfloat)cos(glm::radians(360.0 * j / m));
         GLfloat mSin = (GLfloat)sin(glm::radians(360.0 * j / m));
         vertices[verticesIndex] = mCos * r;
         vertices[verticesIndex + 1] = mSin * r;
         vertices[verticesIndex + 2] = l - 2 * l*i / p;
         verticesIndex += 3;
      }
   }

   int base = 2 * m + 2;
   for (int i = 0; i < p; i++)
   {
      for (int j = 0; j < m; j++)
      {
         indexes[indexesIndex] = base + (m + 1)*i + j;
         indexes[indexesIndex + 1] = base + (m + 1)*(i + 1) + j;
         indexes[indexesIndex + 2] = base + (m + 1)*(i + 1) + j + 1;
         indexesIndex += 3;

         indexes[indexesIndex] = base + (m + 1)*i + j;
         indexes[indexesIndex + 1] = base + (m + 1)*(i + 1) + j + 1;
         indexes[indexesIndex + 2] = base + (m + 1)*i + j + 1;
         indexesIndex += 3;
      }
   }

  InitBuffers();
}

A continuación se muestra una representación de esta figura.

Cilindro

 

 

Descripción de una esfera

 

Fichero de cabecera.

#pragma once

#include <GL/glew.h>
#include "CGFigure.h"

//
// CLASE: CGSphere
//
// DESCRIPCIÓN: Representa una esfera de radio 'r', dividida en 'p' 
//              capas (paralelos) y 'm' líneas (meridianos).
//
class CGSphere : public CGFigure {
public:
  CGSphere(GLint p, GLint m, GLfloat r);
};

Fichero de código.

#include "CGSphere.h"
#include <GL/glew.h>
#include <math.h>
#include "CGFigure.h"


CGSphere::CGSphere(GLint p, GLint m, GLfloat r)
{
   numFaces = 2 * m*(p - 1); // Number of faces
   numVertices = (m + 1)*(p + 1); // Number of vertices
   vertices = new GLfloat[numVertices * 3];
   indexes = new GLushort[numFaces * 3];

   int verticesIndex = 0;
   int indexesIndex = 0;
   /* northern polar cap*/
   for (int j = 0; j <= m; j++)
   {
      vertices[verticesIndex] = 0.0;
      vertices[verticesIndex + 1] = 0.0f;
      vertices[verticesIndex + 2] = r;
      verticesIndex += 3;
   }

   for (int i = 1; i < p; i++)
   {
      for (int j = 0; j <= m; j++)
      {
         GLfloat pCos = (GLfloat)cos(glm::radians(180.0 * i / p));
         GLfloat pSin = (GLfloat)sin(glm::radians(180.0 * i / p));
         GLfloat mCos = (GLfloat)cos(glm::radians(360.0 * j / m));
         GLfloat mSin = (GLfloat)sin(glm::radians(360.0 * j / m));
			
         vertices[verticesIndex] = pSin*mCos*r;
         vertices[verticesIndex + 1] = pSin*mSin*r;
         vertices[verticesIndex + 2] = pCos*r;
         verticesIndex += 3;
      }
   }

   /* southern polar cap*/
   for (int j = 0; j <= m; j++)
   {
      vertices[verticesIndex] = 0.0;
      vertices[verticesIndex + 1] = 0.0f;
      vertices[verticesIndex + 2] = -r;
      verticesIndex += 3;
   }

   /* northern polar cap*/
   for (int j = 0; j < m; j++)
   {
      indexes[indexesIndex] = j;
      indexes[indexesIndex + 1] = m + j + 1;
      indexes[indexesIndex + 2] = m + j + 2;
      indexesIndex += 3;
   }
   for (int i = 1; i < p - 1; i++)
   {
      for (int j = 0; j < m; j++)
      {
         indexes[indexesIndex] = i*(m + 1) + j;
         indexes[indexesIndex + 1] = (i + 1)*(m + 1) + j;
         indexes[indexesIndex + 2] = i*(m + 1) + j + 1;
         indexes[indexesIndex + 3] = (i + 1)*(m + 1) + j;
         indexes[indexesIndex + 4] = (i + 1)*(m + 1) + j + 1;
         indexes[indexesIndex + 5] = i*(m + 1) + j + 1;
         indexesIndex += 6;
      }
   }
   for (int j = 0; j < m; j++)
   {
      indexes[indexesIndex] = (p - 1)*(m + 1) + j;
      indexes[indexesIndex + 1] = p*(m + 1) + j;
      indexes[indexesIndex + 2] = (p - 1)*(m + 1) + j + 1;
      indexesIndex += 3;
   }

  InitBuffers();
}

La representación de la figura es la siguiente.

Esfera

 

 

Descripción de un disco

 

Fichero de cabecera.

#pragma once

#include <GL/glew.h>
#include "CGFigure.h"

//
// CLASE: CGDisk
//
// DESCRIPCIÓN: Representa un disco de radio interior 'r0' (puede ser cero), 
//              radio exterior 'r1', dividido en 'p' capas y 'm' sectores.
//
class CGDisk : public CGFigure {
public:
	CGDisk(GLint p, GLint m, GLfloat r0, GLfloat r1);
};

Fichero de código.

#include "CGDisk.h"
#include <GL/glew.h>
#include <math.h>
#include "CGFigure.h"
 
CGDisk::CGDisk(GLint p, GLint m, GLfloat r0, GLfloat r1)
{
   if (r0 == 0.0f)
   {
      numFaces = 2*(2 * m * p - m); // Number of faces
      numVertices = 2*(m* p + 1); // Number of vertices
      vertices = new GLfloat[numVertices * 3];
      indexes = new GLushort[numFaces * 3];

      int verticesIndex = 0;
      int indexesIndex = 0;
 
      vertices[0] = 0.0f;
      vertices[1] = 0.0f;
      vertices[2] = 0.0;
      verticesIndex += 3;

      for (int j = 0; j < m; j++)
      {
        GLfloat r = (GLfloat) (r1 / p);
        GLfloat mCos = (GLfloat)cos(glm::radians(360.0 * j / m));
        GLfloat mSin = (GLfloat)sin(glm::radians(360.0 * j / m));
        vertices[verticesIndex] = mCos * r;
        vertices[verticesIndex + 1] = mSin * r;
        vertices[verticesIndex + 2] = 0.0f;
        verticesIndex += 3;

        indexes[indexesIndex] = 0; // center
        indexes[indexesIndex + 1] = j + 1;
        indexes[indexesIndex + 2] = (j + 2>m ? 1 : j + 2);
        indexesIndex += 3;
      }

      for (int i = 2; i <= p; i++)
      {
        for (int j = 0; j < m; j++)
        {
          GLfloat r = (GLfloat) (r1*i / p);
          GLfloat mCos = (GLfloat)cos(glm::radians(360.0 * j / m));
          GLfloat mSin = (GLfloat)sin(glm::radians(360.0 * j / m));
          vertices[verticesIndex] = mCos * r;
          vertices[verticesIndex + 1] = mSin * r;
          vertices[verticesIndex + 2] = 0.0f;
          verticesIndex += 3;
        }
      }

      for (int i = 0; i < p - 1; i++)
      {
        for (int j = 0; j < m; j++)
        {
          indexes[indexesIndex] = m * i + j + 1;
          indexes[indexesIndex + 1] = m *(i + 1) + j + 1;
          indexes[indexesIndex + 2] = (j+1 == m ? m*(i+1) + 1 : m*(i+1) + j + 2);
          indexesIndex += 3;

          indexes[indexesIndex] = m *i + j + 1;
          indexes[indexesIndex + 1] = (j+1 == m ? m*(i+1) + 1 : m*(i+1) + j + 2);
          indexes[indexesIndex + 2] = (j+1 == m ? m*i + 1 : m*i + j + 2);
          indexesIndex += 3;
        }
      }

      int base = verticesIndex/3;

      vertices[base+0] = 0.0f;
      vertices[base+1] = 0.0f;
      vertices[base+2] = 0.0;
      verticesIndex += 3;

      for (int j = 0; j < m; j++)
      {
        GLfloat r = (GLfloat) (r1 / p);
        GLfloat mCos = (GLfloat)cos(glm::radians(360.0 * j / m));
        GLfloat mSin = (GLfloat)sin(glm::radians(360.0 * j / m));
        vertices[verticesIndex] = mCos * r;
        vertices[verticesIndex + 1] = mSin * r;
        vertices[verticesIndex + 2] = 0.0f;
        verticesIndex += 3;

        indexes[indexesIndex] = base + 0; // center
        indexes[indexesIndex + 1] = base+ (j + 2>m ? 1 : j + 2);
        indexes[indexesIndex + 2] = base + j + 1;
        indexesIndex += 3;
      }

      for (int i = 2; i <= p; i++)
      {
        for (int j = 0; j < m; j++)
        {
          GLfloat r = (GLfloat) (r1*i / p);
          GLfloat mCos = (GLfloat)cos(glm::radians(360.0 * j / m));
          GLfloat mSin = (GLfloat)sin(glm::radians(360.0 * j / m));
          vertices[verticesIndex] = mCos * r;
          vertices[verticesIndex + 1] = mSin * r;
          vertices[verticesIndex + 2] = 0.0f;
          verticesIndex += 3;
        }
      }

      for (int i = 0; i < p - 1; i++)
      {
        for (int j = 0; j < m; j++)
        {
          indexes[indexesIndex] = base + m * i + j + 1;
          indexes[indexesIndex + 1] = base + (j + 1 == m ? m*(i+1)+1 : m*(i+1)+j+2);
          indexes[indexesIndex + 2] = base + m *(i + 1) + j + 1;
          indexesIndex += 3;

          indexes[indexesIndex] = base + m *i + j + 1;
          indexes[indexesIndex + 1] = base + (j + 1 == m ? m*i + 1 : m*i + j + 2);
          indexes[indexesIndex + 2] = base + (j + 1 == m ? m*(i+1)+1 : m*(i+1)+j+2);
          indexesIndex += 3;
        }
      }
    }
    else
    {
      numFaces = 2*(2 * m * p); // Number of faces
      numVertices = 2*(m* (p + 1)); // Number of vertices
      vertices = new GLfloat[numVertices * 3];
      indexes = new GLushort[numFaces * 3];

      int verticesIndex = 0;
      int indexesIndex = 0;

      for (int i = 0; i <= p; i++)
      {
        GLfloat r = r0 + (r1 - r0)*i / p;
        for (int j = 0; j < m; j++)
        {
          GLfloat mCos = (GLfloat)cos(glm::radians(360.0 * j / m));
          GLfloat mSin = (GLfloat)sin(glm::radians(360.0 * j / m));
          vertices[verticesIndex] = mCos * r;
          vertices[verticesIndex + 1] = mSin * r;
          vertices[verticesIndex + 2] = 0.0f;
          verticesIndex += 3;
        }
      }

      for (int i = 0; i < p; i++)
      {
        for (int j = 0; j < m; j++)
        {
          indexes[indexesIndex] = m *i + j;
          indexes[indexesIndex + 1] = m *(i + 1) + j;
          indexes[indexesIndex + 2] = (j + 1 == m ? m *(i + 1) : m*(i + 1) + j + 1);
          indexesIndex += 3;

          indexes[indexesIndex] = m *i + j;
          indexes[indexesIndex + 1] = (j + 1 == m ? m *(i + 1) : m*(i + 1) + j + 1);
          indexes[indexesIndex + 2] = (j + 1 == m ? m *i : m *i + j + 1);
          indexesIndex += 3;
        }
      }

      int base = verticesIndex/3;

      for (int i = 0; i <= p; i++)
      {
        GLfloat r = r0 + (r1 - r0)*i / p;
        for (int j = 0; j < m; j++)
        {
          GLfloat mCos = (GLfloat)cos(glm::radians(360.0 * j / m));
          GLfloat mSin = (GLfloat)sin(glm::radians(360.0 * j / m));
          vertices[verticesIndex] = mCos * r;
          vertices[verticesIndex + 1] = mSin * r;
          vertices[verticesIndex + 2] = 0.0f;
          verticesIndex += 3;
        }
      }

      for (int i = 0; i < p; i++)
      {
        for (int j = 0; j < m; j++)
        {
          indexes[indexesIndex] = base + m *i + j;
          indexes[indexesIndex + 1] = base + (j+1 == m ? m *(i+1) : m*(i+1)+j+1);
          indexes[indexesIndex + 2] = base + m *(i + 1) + j;
          indexesIndex += 3;

          indexes[indexesIndex] = base + m *i + j;
          indexes[indexesIndex + 1] = base + (j+1 == m ? m *i : m *i + j + 1);
          indexes[indexesIndex + 2] = base + (j+1 == m ? m *(i+1) : m*(i+1)+j+1);
          indexesIndex += 3;
        }
      }
   }

  InitBuffers();
}

El aspecto de la figura es el siguiente.

Disco

 

 

Descripción de un toro

 

Fichero de cabecera.

#pragma once

#include <GL/glew.h>
#include "CGFigure.h"

//
// CLASE: CGTorus
//
// DESCRIPCIÓN: Representa un toro con radio interior 'r0', radio exterior 'r1', 
//              dividido en 'p' capas y 'm' sectores.
//
class CGTorus : public CGFigure {
public:
  CGTorus(GLint p, GLint m, GLfloat r0, GLfloat r1);
};

Fichero de código.

#include "CGTorus.h"
#include <GL/glew.h>
#include <math.h>
#include "CGFigure.h"

CGTorus::CGTorus(GLint p, GLint m, GLfloat r0, GLfloat r1)
{
  numFaces = 2 * m * p; // Number of faces
  numVertices = (m + 1)*(p + 1); // Number of vertices
  vertices = new GLfloat[numVertices * 3];
  indexes = new GLushort[numFaces * 3];

  int verticesIndex = 0;
  int indexesIndex = 0;

  for (int i = 0; i <= m; i++)
  {
    for (int j = 0; j <= p; j++)
    {
      GLfloat pCos = (GLfloat)cos(glm::radians(360.0 * j / p));
      GLfloat pSin = (GLfloat)sin(glm::radians(360.0 * j / p));
      GLfloat mCos = (GLfloat)cos(glm::radians(360.0 * i / m));
      GLfloat mSin = (GLfloat)sin(glm::radians(360.0 * i / m));
			
      vertices[verticesIndex] = (r1 + r0*pCos)*mCos;
      vertices[verticesIndex + 1] = (r1 + r0*pCos)*mSin;
      vertices[verticesIndex + 2] = r0*pSin;
      verticesIndex += 3;
    }
  }

  for (int i = 0; i < m; i++)
  {
    for (int j = 0; j < p; j++)
    {
      indexes[indexesIndex] = (p + 1)*i + j;
      indexes[indexesIndex + 1] = (p + 1)*(i + 1) + j;
      indexes[indexesIndex + 2] = (p + 1)*(i + 1) + j + 1;
      indexesIndex += 3;

      indexes[indexesIndex] = (p + 1)*i + j;
      indexes[indexesIndex + 1] = (p + 1)*(i + 1) + j + 1;
      indexes[indexesIndex + 2] = (p + 1)*i + j + 1;
      indexesIndex += 3;
    }
  }
  
  InitBuffers();
}

A continuación se muestra la imagen generada por esta clase.

Toro

 

 

Descripción de un icosaedro

 

Un icosaedro es un poliedro formado por 20 triángulos equiláteros, generados a partir de 12 vértices. Estos vértices forman tres rectángulos situados en planos perpendiculares entre sí. La proporción entre los lados de estos rectángulos se denomina razón áurea ( phi = (1+ sqrt(5))/2 ). Para definir esta figura se incluye la clase CGIcosahedron.

#pragma once

#include <GL/glew.h>
#include "CGFigure.h"

//
// CLASE: CGIcosahedron
//
// DESCRIPCIÓN: Representa un icosaedro con arista 'r'. 
//
class CGIcosahedron : public CGFigure {
public:
  CGIcosahedron(GLfloat r);
};

Fichero de código.

#include "CGIcosahedron.h"
#include <GL/glew.h>
#include <math.h>
#include "CGFigure.h"

CGIcosahedron::CGIcosahedron(GLfloat r)
{
  numFaces = 20; // Number of faces
  numVertices = 60; // Number of vertices
  vertices = new GLfloat[numVertices * 3];
  indexes = new GLushort[numFaces * 3];

  GLfloat phi = (GLfloat)((1 + sqrt(5.0)) / 2.0);

  GLfloat A0[] = { 0, r*phi, r };
  GLfloat A1[] = { 0, r*phi, -r };
  GLfloat A2[] = { 0, -r*phi, -r };
  GLfloat A3[] = { 0, -r*phi, r };
  GLfloat B0[] = { r*phi, r, 0 };
  GLfloat B1[] = { r*phi, -r, 0 };
  GLfloat B2[] = { -r*phi, -r, 0 };
  GLfloat B3[] = { -r*phi, r, 0 };
  GLfloat C0[] = { r, 0, r*phi };
  GLfloat C1[] = { r, 0, -r*phi };
  GLfloat C2[] = { -r, 0, -r*phi };
  GLfloat C3[] = { -r, 0, r*phi };

  int verticesIndex = 0;

  // face 1
  vertices[verticesIndex] = A0[0]; 
  vertices[verticesIndex + 1] = A0[1]; 
  vertices[verticesIndex + 2] = A0[2]; 
  verticesIndex += 3;
  vertices[verticesIndex] = A1[0]; 
  vertices[verticesIndex + 1] = A1[1]; 
  vertices[verticesIndex + 2] = A1[2]; 
  verticesIndex += 3;
  vertices[verticesIndex] = B3[0]; 
  vertices[verticesIndex + 1] = B3[1]; 
  vertices[verticesIndex + 2] = B3[2]; 
  verticesIndex += 3;

  // face 2
  vertices[verticesIndex] = A0[0]; 
  vertices[verticesIndex + 1] = A0[1]; 
  vertices[verticesIndex + 2] = A0[2]; 
  verticesIndex += 3;
  vertices[verticesIndex] = B3[0]; 
  vertices[verticesIndex + 1] = B3[1]; 
  vertices[verticesIndex + 2] = B3[2]; 
  verticesIndex += 3;
  vertices[verticesIndex] = C3[0]; 
  vertices[verticesIndex + 1] = C3[1]; 
  vertices[verticesIndex + 2] = C3[2]; 
  verticesIndex += 3;

  // face 3
  vertices[verticesIndex] = A0[0]; 
  vertices[verticesIndex + 1] = A0[1]; 
  vertices[verticesIndex + 2] = A0[2]; 
  verticesIndex += 3;
  vertices[verticesIndex] = C3[0]; 
  vertices[verticesIndex + 1] = C3[1]; 
  vertices[verticesIndex + 2] = C3[2]; 
  verticesIndex += 3;
  vertices[verticesIndex] = C0[0]; 
  vertices[verticesIndex + 1] = C0[1]; 
  vertices[verticesIndex + 2] = C0[2]; 
  verticesIndex += 3;

  // face 4
  vertices[verticesIndex] = A0[0]; 
  vertices[verticesIndex + 1] = A0[1]; 
  vertices[verticesIndex + 2] = A0[2]; 
  verticesIndex += 3;
  vertices[verticesIndex] = C0[0]; 
  vertices[verticesIndex + 1] = C0[1]; 
  vertices[verticesIndex + 2] = C0[2]; 
  verticesIndex += 3;
  vertices[verticesIndex] = B0[0]; 
  vertices[verticesIndex + 1] = B0[1]; 
  vertices[verticesIndex + 2] = B0[2]; 
  verticesIndex += 3;

  // face 5
  vertices[verticesIndex] = A0[0]; 
  vertices[verticesIndex + 1] = A0[1]; 
  vertices[verticesIndex + 2] = A0[2]; 
  verticesIndex += 3;
  vertices[verticesIndex] = B0[0]; 
  vertices[verticesIndex + 1] = B0[1]; 
  vertices[verticesIndex + 2] = B0[2]; 
  verticesIndex += 3;
  vertices[verticesIndex] = A1[0]; 
  vertices[verticesIndex + 1] = A1[1]; 
  vertices[verticesIndex + 2] = A1[2]; 
  verticesIndex += 3;

  // face 6
  vertices[verticesIndex] = B1[0]; 
  vertices[verticesIndex + 1] = B1[1]; 
  vertices[verticesIndex + 2] = B1[2]; 
  verticesIndex += 3;
  vertices[verticesIndex] = C1[0]; 
  vertices[verticesIndex + 1] = C1[1]; 
  vertices[verticesIndex + 2] = C1[2]; 
  verticesIndex += 3;
  vertices[verticesIndex] = B0[0]; 
  vertices[verticesIndex + 1] = B0[1]; 
  vertices[verticesIndex + 2] = B0[2]; 
  verticesIndex += 3;

  // face 7
  vertices[verticesIndex] = B1[0]; 
  vertices[verticesIndex + 1] = B1[1]; 
  vertices[verticesIndex + 2] = B1[2]; 
  verticesIndex += 3;
  vertices[verticesIndex] = B0[0]; 
  vertices[verticesIndex + 1] = B0[1]; 
  vertices[verticesIndex + 2] = B0[2]; 
  verticesIndex += 3;
  vertices[verticesIndex] = C0[0]; 
  vertices[verticesIndex + 1] = C0[1]; 
  vertices[verticesIndex + 2] = C0[2]; 
  verticesIndex += 3;

  // face 8
  vertices[verticesIndex] = B1[0]; 
  vertices[verticesIndex + 1] = B1[1]; 
  vertices[verticesIndex + 2] = B1[2]; 
  verticesIndex += 3;
  vertices[verticesIndex] = C0[0]; 
  vertices[verticesIndex + 1] = C0[1]; 
  vertices[verticesIndex + 2] = C0[2]; 
  verticesIndex += 3;
  vertices[verticesIndex] = A3[0]; 
  vertices[verticesIndex + 1] = A3[1]; 
  vertices[verticesIndex + 2] = A3[2]; 
  verticesIndex += 3;

  // face 9
  vertices[verticesIndex] = B1[0]; 
  vertices[verticesIndex + 1] = B1[1]; 
  vertices[verticesIndex + 2] = B1[2]; 
  verticesIndex += 3;
  vertices[verticesIndex] = A3[0]; 
  vertices[verticesIndex + 1] = A3[1]; 
  vertices[verticesIndex + 2] = A3[2]; 
  verticesIndex += 3;
  vertices[verticesIndex] = A2[0]; 
  vertices[verticesIndex + 1] = A2[1]; 
  vertices[verticesIndex + 2] = A2[2]; 
  verticesIndex += 3;

  // face 10
  vertices[verticesIndex] = B1[0]; 
  vertices[verticesIndex + 1] = B1[1]; 
  vertices[verticesIndex + 2] = B1[2]; 
  verticesIndex += 3;
  vertices[verticesIndex] = A2[0]; 
  vertices[verticesIndex + 1] = A2[1]; 
  vertices[verticesIndex + 2] = A2[2]; 
  verticesIndex += 3;
  vertices[verticesIndex] = C1[0]; 
  vertices[verticesIndex + 1] = C1[1]; 
  vertices[verticesIndex + 2] = C1[2]; 
  verticesIndex += 3;

  // face 11
  vertices[verticesIndex] = B0[0]; 
  vertices[verticesIndex + 1] = B0[1]; 
  vertices[verticesIndex + 2] = B0[2]; 
  verticesIndex += 3;
  vertices[verticesIndex] = C1[0]; 
  vertices[verticesIndex + 1] = C1[1]; 
  vertices[verticesIndex + 2] = C1[2]; 
  verticesIndex += 3;
  vertices[verticesIndex] = A1[0]; 
  vertices[verticesIndex + 1] = A1[1]; 
  vertices[verticesIndex + 2] = A1[2]; 
  verticesIndex += 3;

  // face 12
  vertices[verticesIndex] = C0[0]; 
  vertices[verticesIndex + 1] = C0[1]; 
  vertices[verticesIndex + 2] = C0[2]; 
  verticesIndex += 3;
  vertices[verticesIndex] = C3[0]; 
  vertices[verticesIndex + 1] = C3[1]; 
  vertices[verticesIndex + 2] = C3[2]; 
  verticesIndex += 3;
  vertices[verticesIndex] = A3[0]; 
  vertices[verticesIndex + 1] = A3[1]; 
  vertices[verticesIndex + 2] = A3[2]; 
  verticesIndex += 3;

  // face 13
  vertices[verticesIndex] = A1[0]; 
  vertices[verticesIndex + 1] = A1[1]; 
  vertices[verticesIndex + 2] = A1[2]; 
  verticesIndex += 3;
  vertices[verticesIndex] = C1[0]; 
  vertices[verticesIndex + 1] = C1[1]; 
  vertices[verticesIndex + 2] = C1[2]; 
  verticesIndex += 3;
  vertices[verticesIndex] = C2[0]; 
  vertices[verticesIndex + 1] = C2[1]; 
  vertices[verticesIndex + 2] = C2[2]; 
  verticesIndex += 3;

  // face 14
  vertices[verticesIndex] = A1[0]; 
  vertices[verticesIndex + 1] = A1[1]; 
  vertices[verticesIndex + 2] = A1[2]; 
  verticesIndex += 3;
  vertices[verticesIndex] = C2[0]; 
  vertices[verticesIndex + 1] = C2[1]; 
  vertices[verticesIndex + 2] = C2[2]; 
  verticesIndex += 3;
  vertices[verticesIndex] = B3[0]; 
  vertices[verticesIndex + 1] = B3[1]; 
  vertices[verticesIndex + 2] = B3[2]; 
  verticesIndex += 3;

  // face 15
  vertices[verticesIndex] = C3[0]; 
  vertices[verticesIndex + 1] = C3[1]; 
  vertices[verticesIndex + 2] = C3[2]; 
  verticesIndex += 3;
  vertices[verticesIndex] = B3[0]; 
  vertices[verticesIndex + 1] = B3[1]; 
  vertices[verticesIndex + 2] = B3[2]; 
  verticesIndex += 3;
  vertices[verticesIndex] = B2[0]; 
  vertices[verticesIndex + 1] = B2[1]; 
  vertices[verticesIndex + 2] = B2[2]; 
  verticesIndex += 3;

  // face 16
  vertices[verticesIndex] = C3[0]; 
  vertices[verticesIndex + 1] = C3[1]; 
  vertices[verticesIndex + 2] = C3[2]; 
  verticesIndex += 3;
  vertices[verticesIndex] = B2[0]; 
  vertices[verticesIndex + 1] = B2[1]; 
  vertices[verticesIndex + 2] = B2[2]; 
  verticesIndex += 3;
  vertices[verticesIndex] = A3[0]; 
  vertices[verticesIndex + 1] = A3[1]; 
  vertices[verticesIndex + 2] = A3[2]; 
  verticesIndex += 3;

  // face 17
  vertices[verticesIndex] = B3[0]; 
  vertices[verticesIndex + 1] = B3[1]; 
  vertices[verticesIndex + 2] = B3[2]; 
  verticesIndex += 3;
  vertices[verticesIndex] = C2[0]; 
  vertices[verticesIndex + 1] = C2[1]; 
  vertices[verticesIndex + 2] = C2[2]; 
  verticesIndex += 3;
  vertices[verticesIndex] = B2[0]; 
  vertices[verticesIndex + 1] = B2[1]; 
  vertices[verticesIndex + 2] = B2[2]; 
  verticesIndex += 3;

  // face 18
  vertices[verticesIndex] = C2[0]; 
  vertices[verticesIndex + 1] = C2[1]; 
  vertices[verticesIndex + 2] = C2[2]; 
  verticesIndex += 3;
  vertices[verticesIndex] = C1[0]; 
  vertices[verticesIndex + 1] = C1[1]; 
  vertices[verticesIndex + 2] = C1[2]; 
  verticesIndex += 3;
  vertices[verticesIndex] = A2[0]; 
  vertices[verticesIndex + 1] = A2[1]; 
  vertices[verticesIndex + 2] = A2[2]; 
  verticesIndex += 3;

  // face 19
  vertices[verticesIndex] = C2[0]; 
  vertices[verticesIndex + 1] = C2[1]; 
  vertices[verticesIndex + 2] = C2[2]; 
  verticesIndex += 3;
  vertices[verticesIndex] = A2[0]; 
  vertices[verticesIndex + 1] = A2[1]; 
  vertices[verticesIndex + 2] = A2[2]; 
  verticesIndex += 3;
  vertices[verticesIndex] = B2[0]; 
  vertices[verticesIndex + 1] = B2[1]; 
  vertices[verticesIndex + 2] = B2[2]; 
  verticesIndex += 3;

  // face 20
  vertices[verticesIndex] = A2[0]; 
  vertices[verticesIndex + 1] = A2[1]; 
  vertices[verticesIndex + 2] = A2[2]; 
  verticesIndex += 3;
  vertices[verticesIndex] = A3[0]; 
  vertices[verticesIndex + 1] = A3[1]; 
  vertices[verticesIndex + 2] = A3[2]; 
  verticesIndex += 3;
  vertices[verticesIndex] = B2[0]; 
  vertices[verticesIndex + 1] = B2[1]; 
  vertices[verticesIndex + 2] = B2[2]; 
  verticesIndex += 3;

  for (int i = 0; i < numVertices; i++) indexes[i] = i;

  InitBuffers();
}

La siguiente imagen muestra una captura de la aplicación dibujando el icosaedro.

Icosaedro