|
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.
|
|
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.
|
|
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:
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|