Escuela Técnica Superior de Ingeniería

 

Grado en Ingeniería Informática

Animación por Ordenador

Curso 2025/2026

 

Práctica 5b

Creación de una escena

 

Objetivos

 

Modificar el modelo 3D para considerar una escena formada por varios objetos. Se añade una cámara que permite navegar por la escena y generar la vista desde diferentes posicionies.

 

 

La clase GEGround

 

Para representar la escena se ha creado una nueva figura que representa el suelo sobre el que se situarán los diferentes objetos. Esta nueva figura, definida en la clase GEGround, representa un rectángulo de lados 2·l1 y 2·l2.

#pragma once

#include "GEFigure.h"

class GEGround : public GEFigure
{
public:
  GEGround(float l1, float l2);
};

El código de la clase describe simplemente dos triángulos que forman el rectángulo.

#include "GEGround.h"

GEGround::GEGround(float l1, float l2)
{
  vertices = {
    {{ l1, 0.0f, l2 }},
    {{ l1, 0.0f, -l2 } },
    {{ -l1, 0.0f, -l2 }},
    {{ -l1, 0.0f, l2 }}
  };

  indices = { 
    0, 1, 2,
    0, 2, 3
  };
}

 

 

Modificaciones de la clase GEFigure

 

El posicionamiento de los vértices de las figuras requieren tres transformaciones: Model-View-Projection. La transformación Model supone modificar la posición de los vértices desde el sistema de referencia local, propio de cada figura, al sistema de referencia del modelo, propio de la escena. Esto quiere decir que la matriz de la transformación Model depende de cada figura. Para incorporar esta característica se ha modificado la clase GEFigure para añadir el campo location que contendrá la matriz de la transformación Model. El método update() recibirá ahora las matrices View y Projection y deberá calcular el producto de las tres matrices para asignar la variable uniforme MVP. Se han añadido además varios métodos dedicados a modificar la matriz de la transformación Model. El método resetLocation() inicializa el campo location a la matriz unidad. El método setLocation() asigna directamente el valor de esta matriz. El método translate() añade una transformación de traslación a la figura. El método rotate() añade una transformación de rotación a la figura.

#pragma once

#include "GEGraphicsContext.h"
#include "GERenderingContext.h"
#include "GEVertex.h"
#include "GETransform.h"
#include "GEVertexBuffer.h"
#include "GEIndexBuffer.h"
#include "GEUniformBuffer.h"
#include "GEDescriptorSet.h"
#include <glm/glm.hpp>
#include <vector>

//
// CLASE: GEFigure
//
// DESCRIPCIÓN: Clase que describe una figura formada por una malla de vértices
//
class GEFigure
{
protected:
  std::vector<GEVertex> vertices;
  std::vector<uint16_t> indices;
  glm::mat4 location;

public:
  void initialize(GEGraphicsContext* gc, GERenderingContext* rc);
  void destroy(GEGraphicsContext* gc);
  void addCommands(VkCommandBuffer commandBuffer,
                   VkPipelineLayout pipelineLayout, int index);
  void update(GEGraphicsContext* gc, uint32_t index, 
                                     glm::mat4 view, glm::mat4 projection);
  void resetLocation();
  void setLocation(glm::mat4 m);
  void translate(glm::vec3 t);
  void rotate(float angle, glm::vec3 axis);

private:
  GEVertexBuffer* vbo;
  GEIndexBuffer* ibo;
  GEUniformBuffer* ubo;
  GEDescriptorSet* dset;
};

El código de los métodos modificados es el siguiente:

//
// FUNCIÓN: GEFigure::initialize(GEGraphicsContext* gc, GERenderingContext* rc)
//
// PROPÓSITO: Crea la figura
//
void GEFigure::initialize(GEGraphicsContext* gc, GERenderingContext* rc)
{
  size_t vertexSize = sizeof(GEVertex) * vertices.size();
  vbo = new GEVertexBuffer(gc, vertexSize, vertices.data());

  size_t indexSize = sizeof(indices[0]) * indices.size();
  ibo = new GEIndexBuffer(gc, indexSize, indices.data());

  size_t transformBufferSize = sizeof(GETransform);
  ubo = new GEUniformBuffer(gc, rc->imageCount, transformBufferSize);

  std::vector<GEUniformBuffer*> ubos(1);
  ubos[0] = ubo;

  dset = new GEDescriptorSet(gc, rc, ubos);

  location = glm::mat4(1.0f);
}

//
// FUNCIÓN: GEFigure::update(...)
//
// PROPÓSITO: Actualiza las variables uniformes sobre una imagen del swapchain
//
void GEFigure::update(GEGraphicsContext* gc, uint32_t index, glm::mat4 view, 
                                                             glm::mat4 projection)
{
  GETransform transform;
  transform.MVP = projection * view * location;
  ubo->update(gc, index, sizeof(GETransform), &transform);
}

//
// FUNCIÓN: CAFigure::resetLocation()
//
// PROPÓSITO: Resetea la matriz de localización (Model).
//
void CAFigure::resetLocation()
{
  location = glm::mat4(1.0f);
}

//
// FUNCIÓN: GEFigure::resetLocation()
//
// PROPÓSITO: Resetea la matriz de localización (Model).
//
void GEFigure::resetLocation()
{
  location = glm::mat4(1.0f);
}

//
// FUNCIÓN: GEFigure::setLocation()
//
// PROPÓSITO: Resetea la matriz de localización (Model).
//
void GEFigure::setLocation(glm::mat4 m)
{
  location = glm::mat4(m);
}

//
// FUNCIÓN: GEFigure::translate(glm::vec3 t)
//
// PROPÓSITO: Añade un desplazamiento la matriz de localización (Model).
//
void GEFigure::translate(glm::vec3 t)
{
  location = glm::translate(location, t);
}

//
// FUNCIÓN: GEFigure::rotate(float angle, glm::vec3 axis)
//
// PROPÓSITO: Añade una rotación la matriz de localización (Model).
//
void GEFigure::rotate(float angle, glm::vec3 axis)
{
  location = glm::rotate(location, glm::radians(angle), axis);
}

 

 

Descripción de la cámara (clase GECamera)

 

Para poder desplazarse a través de la escena es necesario establecer la posición y orientación del observador y realizar una transformación del sistema de coordenadas entre el sistema de coordenadas del modelo y el sistema de coordenadas del observador.

Sistemas de Coordenadas

La posición del observador, en coordenadas del modelo, viene dada por el vector pos. Los ejes de coordenadas del observador, en coordenadas del modelo, vienen dados por los vectores right, up y dir. Para transformar las coordenadas del modelo en coordenadas del observador el primer paso es realizar una traslación de (-pos). De esta forma, el origen de coordenadas del modelo pasa a estar situado en la posición (-pos) y el origen de coordenadas del observador pasa a estar situado en la posición (0,0,0).

Traslación

Para completar la transformación del sistema de coordenadas es necesario rotar los ejes. El resultado de esta rotación es que el vector right debe convertirse en el vector unitario del eje X en el sistema de coordenadas del observador, es decir, en el vector (1,0,0); el vector up debe transformarse en el vector unitario sobre el eje Y, es decir, en el vector (0,1,0); y el vector dir debe convertirse en el vector unitario sobre el eje Z, es decir, el vector (0,0,1). A continuación se muestra el contenido de la matriz de rotación.

Rotación

Esta matriz de transformación se puede generar utilizando la función glm::lookAt(eye,center,up) incluida en la biblioteca GLM. El primer argumento es la posición del observador. El segundo argumento es la posición de un punto situado sobre el eje dir. El tercer argumento de esta función es el vector up.

Para controlar las propiedades del observador vamos a definir una nueva clase que denominamos GECamera. Esta clase nos permitirá almacenar la posición y orientación del observador y moverlo libremente a través de la escena. La descripción de esta clase se encuentra en el fichero GECamera.h. Los miembros de la clase, descritos en su zona privada, almacenan los valores de los vectores pos, right, up y dir. También se incluyen los campos moveStep y turnStep que contienen la magnitud de los desplazamientos y giros a realizar en cada paso de la animación. Los campos cosAngle y sinAngle almacenan el seno y coseno de turnStep para que no sea necesario recalcularlos constantemente. Los campos se completan con un conjunto de flags que indican si se encuentra pulsada una tecla que activa un tipo de movimiento de la cámara (turnLeftPressed, ...). La interfaz pública de la clase GECamera consta del constructor, setters y getters parta los campos, métodos setMove..() y setTurn...() para asignar los flags de los movimientos y giros de la cámara, métodos move...() y turn...() para realizar los movimientos y giros de la cámara, el método update() para realizar los movimientos y giros en función de los flags activos y el método getViewMatrix() que devuelve la matriz de transformación entre el sistema de coordenadas del modelo y el sistema de coordenadas del observador.

#pragma once

#include <glm/glm.hpp>

class GECamera {
public:
  GECamera();
  glm::mat4 getViewMatrix();

  void setPosition(glm::vec3 pos);
  void setDirection(glm::vec3 dir, glm::vec3 up);
  void setMoveStep(float step);
  void setTurnStep(float step);

  glm::vec3 getPosition();
  glm::vec3 getDirection();
  glm::vec3 getUpDirection();
  float getMoveStep();
  float getTurnStep();

  void update();

  void setTurnLeft(bool flag);
  void setTurnRight(bool flag);
  void setTurnUp(bool flag);
  void setTurnDown(bool flag);
  void setTurnCW(bool flag);
  void setTurnCCW(bool flag);
  void setMoveLeft(bool flag);
  void setMoveRight(bool flag);
  void setMoveUp(bool flag);
  void setMoveDown(bool flag);

private:
  glm::vec3 Pos;
  glm::vec3 Dir;
  glm::vec3 Up;
  glm::vec3 Right;

  float moveStep;
  float turnStep;
  float cosAngle;
  float sinAngle;

  bool turnLeftPressed;
  bool turnRightPressed;
  bool turnUpPressed;
  bool turnDownPressed;
  bool turnCWPressed;
  bool turnCCWPressed;
  bool moveLeftPressed;
  bool moveRightPressed;
  bool moveUpPressed;
  bool moveDownPressed;

  void turnRight();
  void turnLeft();
  void turnUp();
  void turnDown();
  void turnCW();
  void turnCCW();

  void moveLeft();
  void moveRight();
  void moveUp();
  void moveDown();
  void moveFront();
  void moveBack();
};

El desarrollo de la clase GECamera se encuentra en el archivo GECamera.cpp, que se muestra a continuación. El constructor de la clase inicializa la posición y orientación del observador de manera que coincide con el sistema de coordenadas del modelo. El método getViewMatrix()  desarrolla la transformación de los sistemas de coordenadas. Para ello construye y apica la matriz de rotación a partir de los valores de right, up y dir y la traslación a partir de los valores de pos. Los métodos get...() se limitan a obtener los valores de los campos internos de la clase. Con respecto a los métodos set...() es importante señalar que la orientación se fija en un único método que solo asigna los valores de dir y up. Se asume que estos vectores son unitarios y perpendiculares entre sí. El vector right se calcula como el producto vectorial entre up y dir. Los métodos move...() generan traslaciones de módulo moveStep sobre cada uno de los ejes del sistema de coordenadas del observador. Esto supone modificar el vector pos añadiendo un desplazamiento de moveStep por el vector unitario de la traslación. Por ejemplo, moveRight() genera un desplazamiento en la dirección del eje X (vector right) y moveFront() genera un desplazamiento hacia adelante (dirección -Z, es decir, -dir). Los métodos turn...() realizan giros de magnitud turnStep sobre cada uno de los ejes del sistema de coordenadas. Para evitar que por problemas de redondeo se pierda la ortogonalidad de los vectores right, up y dir, los giros se calculan sobre un único vector y el otro vector afectado se recalcula mediante un producto vectorial. Los métodos turnLeft() y turnRight() realizan giros respecto al eje Y. Los métodos turnUp() y turnDown() realizan giros respecto al eje X. Los métodos turnCW() y turnCCW() realizan giros respecto al eje Z. Los métodos setMove...() y setTurn...() asignan los flags de movimiento y se utilizarán como respuesta a los eventos de teclado. El método update() realiza las modificaciones en la posición y orientación de la cámara en función de los flags activos.

#include "GECamera.h"

#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>

//
// FUNCIÓN: GECamera::GECamera()
//
// PROPÓSITO: Construye una cámara
//
// COMENTARIOS: 
// La posición inicial es (0,0,0).
// La orientación incial es el sistema de coordenadas del modelo
// El tamaño del paso inicial es 0.1
// El támaño del giro inicial es 1.0 grados
//
GECamera::GECamera()
{
  Pos = glm::vec3(0.0f, 0.0f, 0.0f);
  Dir = glm::vec3(0.0f, 0.0f, 1.0f);
  Up = glm::vec3(0.0f, 1.0f, 0.0f);
  Right = glm::vec3(1.0f, 0.0f, 0.0f);

  moveStep = 0.1f;
  turnStep = 1.0f;
  cosAngle = (float)cos(glm::radians(turnStep));
  sinAngle = (float)sin(glm::radians(turnStep));

  turnLeftPressed = false;
  turnRightPressed = false;
  turnUpPressed = false;
  turnDownPressed = false;
  turnCWPressed = false;
  turnCCWPressed = false;
  moveLeftPressed = false;
  moveRightPressed = false;
  moveUpPressed = false;
  moveDownPressed = false;
}

//
// FUNCIÓN: GECamera::getViewMatrix()
//
// PROPÓSITO: Obtiene la matriz View para situar la cámara.
//
glm::mat4 GECamera::getViewMatrix()
{
  return glm::lookAt(Pos, Pos - Dir, Up);
}

//
// FUNCIÓN: GECamera::setPosition(glm::vec3 pos)
//
// PROPÓSITO: Asigna la posición de la cámara con respecto al sistema de coordenadas
//            del modelo.
//
void GECamera::setPosition(glm::vec3 pos)
{
  Pos = glm::vec3(pos);
}

//
// FUNCIÓN: GECamera::setDirection(glm::vec3 dir, glm::vec3 up)
//
// PROPÓSITO: Asigna la orientación de la cámara.
//
void GECamera::setDirection(glm::vec3 dir, glm::vec3 up)
{
  Dir = glm::vec3(dir);
  Up = glm::vec3(up);
  Right = glm::cross(Up, Dir);
}

//
// FUNCIÓN: GECamera::setMoveStep(float step)
//
// PROPÓSITO: Asigna el avance en cada paso.
//
void GECamera::setMoveStep(float step)
{
  moveStep = step;
}

//
// FUNCIÓN: GECamera::setTurnStep(float step)
//
// PROPÓSITO: Asigna el ángulo de giro en cada paso.
//
void GECamera::setTurnStep(float step)
{
  turnStep = step;
  cosAngle = (float)cos(glm::radians(turnStep));
  sinAngle = (float)sin(glm::radians(turnStep));
}

//
// FUNCIÓN: GECamera::getPosition()
//
// PROPÓSITO: Obtiene la posición de la cámara.
//
glm::vec3 GECamera::getPosition()
{
  return Pos;
}

//
// FUNCIÓN: GECamera::getDirection()
//
// PROPÓSITO: Obtiene la orientación de la cámara (eje Z).
//
glm::vec3 GECamera::getDirection()
{
  return Dir;
}

//
// FUNCIÓN: GECamera::getUpDirection()
//
// PROPÓSITO: Obtiene la orientación cenital de la cámara (eje Y).
//
glm::vec3 GECamera::getUpDirection()
{
  return Up;
}

//
// FUNCIÓN: GECamera::getMoveStep()
//
// PROPÓSITO: Obtiene el avance en cada paso.
//
float GECamera::getMoveStep()
{
  return moveStep;
}

//
// FUNCIÓN: GECamera::getTurnStep()
//
// PROPÓSITO: Obtiene el ángulo de giro en cada paso.
//
float GECamera::getTurnStep()
{
  return turnStep;
}

//
// FUNCIÓN: GECamera::update()
//
// PROPÓSITO: Actualiza la posición y orientación de la cámara 
//
void GECamera::update()
{
  if (turnLeftPressed && !turnRightPressed) turnLeft();
  if (!turnLeftPressed && turnRightPressed) turnRight();
  if (turnUpPressed && !turnDownPressed) turnUp();
  if (!turnUpPressed && turnDownPressed) turnDown();
  if (turnCWPressed && !turnCCWPressed) turnCW();
  if (!turnCWPressed && turnCCWPressed) turnCCW();
  if (moveLeftPressed && !moveRightPressed) moveLeft();
  if (!moveLeftPressed && moveRightPressed) moveRight();
  if (moveUpPressed && !moveDownPressed) moveUp();
  if (!moveUpPressed && moveDownPressed) moveDown();

  moveFront();
}

//
// FUNCIÓN: GECamera::moveFront()
//
// PROPÓSITO: Mueve el observador un paso (moveStep) en la dirección -Dir 
//
void GECamera::moveFront()
{
  Pos -= moveStep * Dir;
}

//
// FUNCIÓN: GECamera::moveBack()
//
// PROPÓSITO: Mueve el observador un paso (moveStep) hacia atrás en la dirección Dir 
//
void GECamera::moveBack()
{
  Pos += moveStep * Dir;
}

//
// FUNCIÓN: GECamera::moveLeft()
//
// PROPÓSITO: Mueve el observador un paso (moveStep) hacia la izquierda. 
//
void GECamera::moveLeft()
{
  Pos -= 0.1f * Right;
}

//
// FUNCIÓN: GECamera::moveRight()
//
// PROPÓSITO: Mueve el observador un paso (moveStep) hacia la derecha. 
//
void GECamera::moveRight()
{
  Pos += 0.1f * Right;
}

//
// FUNCIÓN: GECamera::moveUp()
//
// PROPÓSITO: Mueve el observador un paso (moveStep) hacia arriba. 
//
void GECamera::moveUp()
{
  Pos += 0.1f * Up;
}

//
// FUNCIÓN: GECamera::moveDown()
//
// PROPÓSITO: Mueve el observador un paso (moveStep) hacia abajo. 
//
void GECamera::moveDown()
{
  Pos -= 0.1f * Up;
}

//
// FUNCIÓN: GECamera::turnRight()
//
// PROPÓSITO: Rota el observador un paso (angleStep) hacia su derecha.
//
void GECamera::turnRight()
{
  Dir.x = cosAngle * Dir.x - sinAngle * Right.x;
  Dir.y = cosAngle * Dir.y - sinAngle * Right.y;
  Dir.z = cosAngle * Dir.z - sinAngle * Right.z;

  // Right = Up x Dir
  Right = glm::cross(Up, Dir);
}

//
// FUNCIÓN: CACamera::turnLeft()
//
// PROPÓSITO: Rota el observador un paso (angleStep) hacia su izquierda.
//
void GECamera::turnLeft()
{
  Dir.x = cosAngle * Dir.x + sinAngle * Right.x;
  Dir.y = cosAngle * Dir.y + sinAngle * Right.y;
  Dir.z = cosAngle * Dir.z + sinAngle * Right.z;

  // Right = Up x Dir
  Right = glm::cross(Up, Dir);
}

//
// FUNCIÓN: GECamera::turnUp()
//
// PROPÓSITO: Rota el observador un paso (angleStep) hacia arriba.
//
void GECamera::turnUp()
{
  Dir.x = cosAngle * Dir.x - sinAngle * Up.x;
  Dir.y = cosAngle * Dir.y - sinAngle * Up.y;
  Dir.z = cosAngle * Dir.z - sinAngle * Up.z;

  // Up = Dir x Right
  Up = glm::cross(Dir, Right);
}

//
// FUNCIÓN: GECamera::turnDown()
//
// PROPÓSITO: Rota el observador un paso (angleStep) hacia abajo.
//
void GECamera::turnDown()
{
  Dir.x = cosAngle * Dir.x + sinAngle * Up.x;
  Dir.y = cosAngle * Dir.y + sinAngle * Up.y; 
  Dir.z = cosAngle * Dir.z + sinAngle * Up.z;

  // Up = Dir x Right
  Up = glm::cross(Dir, Right);
}

//
// FUNCIÓN: GECamera::turnCW()
//
// PROPÓSITO: Rota el observador un paso (angleStep) en sentido del reloj.
//
void GECamera::turnCW()
{
  Up.x = cosAngle * Up.x + sinAngle * Right.x;
  Up.y = cosAngle * Up.y + sinAngle * Right.y;
  Up.z = cosAngle * Up.z + sinAngle * Right.z;

  // Right = Up x Dir
  Right = glm::cross(Up, Dir);
}

//
// FUNCIÓN: GECamera::turnCCW()
//
// PROPÓSITO: Rota el observador un paso (angleStep) en sentido contrario al reloj.
//
void GECamera::turnCCW()
{
  Up.x = cosAngle * Up.x - sinAngle * Right.x;
  Up.y = cosAngle * Up.y - sinAngle * Right.y;
  Up.z = cosAngle * Up.z - sinAngle * Right.z;

  // Right = Up x Dir
  Right = glm::cross(Up, Dir);
}

//
// FUNCIÓN: GECamera::setTurnLeft(bool flag)
//
// PROPÓSITO: Activa o desactiva el giro a la izquierda
//
void GECamera::setTurnLeft(bool flag)
{
  turnLeftPressed = flag;
}

//
// FUNCIÓN: GECamera::setTurnRight(bool flag)
//
// PROPÓSITO: Activa o desactiva el giro a la derecha
//
void GECamera::setTurnRight(bool flag)
{
  turnRightPressed = flag;
}

//
// FUNCIÓN: GECamera::setTurnUp(bool flag)
//
// PROPÓSITO: Activa o desactiva el giro hacia arriba
//
void GECamera::setTurnUp(bool flag)
{
  turnUpPressed = flag;
}

//
// FUNCIÓN: GECamera::setTurnDown(bool flag)
//
// PROPÓSITO: Activa o desactiva el giro hacia abajo
//
void GECamera::setTurnDown(bool flag)
{
  turnDownPressed = flag;
}

//
// FUNCIÓN: GECamera::setTurnCW(bool flag)
//
// PROPÓSITO: Activa o desactiva el giro horario
//
void GECamera::setTurnCW(bool flag)
{
  turnCWPressed = flag;
}

//
// FUNCIÓN: GECamera::setTurnCCW(bool flag)
//
// PROPÓSITO: Activa o desactiva el giro antihorario
//
void GECamera::setTurnCCW(bool flag)
{
  turnCCWPressed = flag;
}

//
// FUNCIÓN: GECamera::setMoveLeft(bool flag)
//
// PROPÓSITO: Activa o desactiva el desplazamiento a la izquierda
//
void GECamera::setMoveLeft(bool flag)
{
  moveLeftPressed = flag;
}

//
// FUNCIÓN: GECamera::setMoveRight(bool flag)
//
// PROPÓSITO: Activa o desactiva el desplazamiento a la derecha
//
void GECamera::setMoveRight(bool flag)
{
  moveRightPressed = flag;
}

//
// FUNCIÓN: GECamera::setMoveUp(bool flag)
//
// PROPÓSITO: Activa o desactiva el desplazamiento hacia arriba
//
void GECamera::setMoveUp(bool flag)
{
  moveUpPressed = flag;
}

//
// FUNCIÓN: GECamera::setMoveDown(bool flag)
//
// PROPÓSITO: Activa o desactiva el desplazamiento hacia abajo
//
void GECamera::setMoveDown(bool flag)
{
  moveDownPressed = flag;
}

 

 

Modificaciones de la clase GEScene

 

La clase GEScene se va a modificar para contener la cámara y el conjunto de figuras. En este caso no se va a mutar entre las figuras sino que se va a modelar una escena formada por 7 figuras: el suelo, un cubo, un cono, un toro, un cilindro, un icosahedro y una esfera. La cámara permitirá desplazarse por el espacio, generando la imagen desde diferentes posiciones y orientaciones.

El constructor de la clase es el encargado de crear las figuras y posicionarlas. Los métodos  destroyBuffers(), addCommands() y update() se limitan a llamar a los métodos correspondientes de todas las figuras que forman la escena.

#pragma once

#include "GEGraphicsContext.h"
#include "GEDrawingContext.h"
#include "GECommandContext.h"
#include "GERenderingContext.h"

#include "GEFigure.h"
#include "GECamera.h"
#include <vulkan/vulkan.h>
#include <glm/glm.hpp>
#include <vector>

//
// CLASE: GEScene
//
// DESCRIPCIÓN: Clase que describe una escena
//
class GEScene
{
private:
  GERenderingContext* rc;
  std::vector<GEFigure*> figures;
  GECamera* camera;
  glm::mat4 projection;

public:
  GEScene(GEGraphicsContext* gc, GEDrawingContext* dc, GECommandContext* cc);
  void destroy(GEGraphicsContext* gc);
  void recreate(GEGraphicsContext* gc, GEDrawingContext* dc, GECommandContext* cc);
  void update(GEGraphicsContext* gc, uint32_t index);
  void key_action(int key, bool pressed);
  void aspect_ratio(double aspect);

private:
  void fillCommandBuffers(std::vector<VkCommandBuffer> commandBuffers);
  GEPipelineConfig* createPipelineConfig(VkExtent2D extent);
};

A continuación se muestra el código de la clase.

#include "GEScene.h"

#include "GECube.h"
#include "GEPyramid.h"
#include "GECone.h"
#include "GECylinder.h"
#include "GESphere.h"
#include "GEDisk.h"
#include "GETorus.h"
#include "GEIcosahedron.h"
#include "GEGround.h"
#include "GETransform.h"
#include <windows.h>
#include "resource.h"
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>

//
// FUNCIÓN: GEScene::GEScene(GEGraphicsContext* gc, GEDrawingContext* dc)
//
// PROPÓSITO: Crea la escena
//
GEScene::GEScene(GEGraphicsContext* gc, GEDrawingContext* dc, GECommandContext* cc)
{
  VkExtent2D extent = dc->getExtent();
  double aspect = (double)extent.width / (double)extent.height;
  aspect_ratio(aspect);

  GEPipelineConfig* config = createPipelineConfig(dc->getExtent());
  rc = new GERenderingContext(gc, dc, config);

  this->camera = new GECamera();
  this->camera->setPosition(glm::vec3(0.0f, 10.0f, 300.0f));
  this->camera->setMoveStep(0.0f);

  GEFigure* ground = new GEGround(50.0f, 50.0f);
  ground->initialize(gc, rc);

  GEFigure* fig1 = new GECone(5, 20, 5.0f, 5.0f);
  fig1->initialize(gc, rc);
  fig1->translate(glm::vec3(25.0f, 5.0f, 25.0f));
  fig1->rotate(-90.0f, glm::vec3(1.0f, 0.0f, 0.0f));

  GEFigure* fig2 = new GECube(5.0f);
  fig2->initialize(gc, rc);
  fig2->translate(glm::vec3(-25.0f, 5.0f, 25.0f));
  fig2->rotate(-90.0f, glm::vec3(1.0f, 0.0f, 0.0f));

  GEFigure* fig3 = new GECylinder(20, 20, 5.0f, 5.0f);
  fig3->initialize(gc, rc);
  fig3->translate(glm::vec3(25.0f, 5.0f, 0.0f));
  fig3->rotate(90.0f, glm::vec3(1.0f, 0.0f, 0.0f));

  GEFigure* fig4 = new GETorus(20, 40, 3.0f, 5.0f);
  fig4->initialize(gc, rc);
  fig4->translate(glm::vec3(-25.0f, 8.0f, 0.0f));

  GEFigure* fig5 = new GESphere(20, 40, 8.0f);
  fig5->initialize(gc, rc);
  fig5->translate(glm::vec3(25.0f, 8.0f, -25.0f));

  GEFigure* fig6 = new GEIcosahedron(5.0f);
  fig6->initialize(gc, rc);
  fig6->translate(glm::vec3(-25.0f, 8.0f, -25.0f));

  figures.resize(7);
  figures[0] = ground;
  figures[1] = fig1;
  figures[2] = fig2;
  figures[3] = fig3;
  figures[4] = fig4;
  figures[5] = fig5;
  figures[6] = fig6;

  fillCommandBuffers(cc->commandBuffers);
}

//
// FUNCIÓN: GEScene::destroy(GEGraphicsContext* gc)
//
// PROPÓSITO: Reconstruye los componentes gráficos de la escena
//
void GEScene::destroy(GEGraphicsContext* gc)
{
  rc->destroy(gc);
  delete rc;

  for (int i = 0; i < figures.size(); i++)
  {
    figures[i]->destroy(gc); 
    delete figures[i];
  }
}

//
// FUNCIÓN: GEScene::recreate(GEGraphicsContext* gc, GEDrawingContext* dc)
//
// PROPÓSITO: Reconstruye los componentes gráficos de la escena
//
void GEScene::recreate(GEGraphicsContext* gc, GEDrawingContext* dc, GECommandContext* cc)
{
  rc->destroy(gc);
  GEPipelineConfig* config = createPipelineConfig(dc->getExtent());
  this->rc = new GERenderingContext(gc, dc, config);
  fillCommandBuffers(cc->commandBuffers);
}

//
// FUNCIÓN: GEScene::update(GEGraphicsContext* gc, uint32_t index)
//
// PROPÓSITO: Actualiza la información para generar la imagen 
//
void GEScene::update(GEGraphicsContext* gc, uint32_t index)
{
  camera->update();
  glm::mat4 view = camera->getViewMatrix();

  for (int i = 0; i < figures.size(); i++)
  {
    figures[i]->update(gc, index, view, projection);
  }
}

//
// FUNCIÓN: GEScene::key_action(int key, bool pressed)
//
// PROPÓSITO: Respuesta a acciones de teclado
//
void GEScene::key_action(int key, bool pressed)
{
  switch (key)
  {
  case GLFW_KEY_UP:
    camera->setTurnDown(pressed);
    break;
  case GLFW_KEY_DOWN:
    camera->setTurnUp(pressed);
    break;
  case GLFW_KEY_LEFT:
    camera->setTurnCCW(pressed);
    break;
  case GLFW_KEY_RIGHT:
    camera->setTurnCW(pressed);
    break;
  case GLFW_KEY_S:
    camera->setMoveStep(0.0f);
    break;
  case GLFW_KEY_KP_ADD:
  case GLFW_KEY_1:
    camera->setMoveStep(camera->getMoveStep() + 0.1f);
    break;
  case GLFW_KEY_KP_SUBTRACT:
  case GLFW_KEY_2:
    camera->setMoveStep(camera->getMoveStep() - 0.1f);
    break;
  case GLFW_KEY_Q:
    camera->setMoveUp(pressed);
    break;
  case GLFW_KEY_A:
    camera->setMoveDown(pressed);
    break;
  case GLFW_KEY_O:
    camera->setMoveLeft(pressed);
    break;
  case GLFW_KEY_P:
    camera->setMoveRight(pressed);
    break;
  case GLFW_KEY_K:
    camera->setTurnLeft(pressed);
    break;
  case GLFW_KEY_L:
    camera->setTurnRight(pressed);
    break;
  }
}

//
// FUNCIÓN: GEScene::aspect_ratio(double)
//
// PROPÓSITO: Modifica la relación anchura/altura del modelo
//
void GEScene::aspect_ratio(double aspect)
{
  constexpr double fov = glm::radians(30.0f);
  double sin_fov = sin(fov);
  double cos_fov = cos(fov);
  float wHeight = (float)(sin_fov * 0.2 / cos_fov);
  float wWidth = (float)(wHeight * aspect);

  projection = glm::perspective((float)fov, (float)aspect, 0.2f, 400.0f);
  projection[1][1] *= -1.0f;
}

//
// FUNCIÓN: CAScene::fillCommandBuffers(std::vector<VkCommandBuffer> commandBuffers)
//
// PROPÓSITO: Añade los comandos de renderizado al command buffer
//
void GEScene::fillCommandBuffers(std::vector<VkCommandBuffer> commandBuffers)
{
  rc->startFillingCommandBuffers(commandBuffers);
  for (int i = 0; i < commandBuffers.size(); i++)
  {
    for (int j = 0; j < figures.size(); j++)
    {
      figures[j]->addCommands(commandBuffers[i], rc->pipelineLayout, i);
    }
  }
  rc->endFillingCommandBuffers(commandBuffers);
}

//
// FUNCIÓN: GEScene::createPipelineConfig()
//
// PROPÓSITO: Obtiene la configuración del pipeline de renderizado
//
GEPipelineConfig* GEScene::createPipelineConfig(VkExtent2D extent)
{
  GEPipelineConfig* config = new GEPipelineConfig();
  config->vertex_shader = IDR_HTML1;
  config->fragment_shader = IDR_HTML2;

  config->attrStride = sizeof(GEVertex);
  config->attrOffsets.resize(1);
  config->attrOffsets[0] = offsetof(GEVertex, pos);
  config->attrFormats.resize(1);
  config->attrFormats[0] = VK_FORMAT_R32G32B32_SFLOAT;

  config->descriptorTypes.resize(1);
  config->descriptorTypes[0] = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
  config->descriptorStages.resize(1);
  config->descriptorStages[1] = VK_SHADER_STAGE_VERTEX_BIT;

  config->depthTestEnable = VK_TRUE;
  config->cullMode = VK_CULL_MODE_BACK_BIT;
  config->extent = extent;

  return config;
}

 

 

Aspecto final

 

El aspecto final de la aplicación muestra la escena y permite navegar por medio del teclado.

Captura