Este projeto consiste em construir um programa para ler uma descrição de objeto em 3 dimensões de um arquivo em disco e apresentar na tela a visualização desse objeto, considerando uma projeção em perspectiva.
Os requisitos do projeto são:
A seguir serão apresentados mais detalhes sobre cada um desses requisitos.
O formato de dados Wavefront OBJ é considerado um “formato universal” para a representação de objetos em 3 dimensões, sendo reconhecido pela maioria dos softwares de modelagem/visualização em 3D.
Nesse tipo de arquivo, os dados do objeto são representados em formato ASCII. Os dados com representação mais frequente são os vértices e as faces:
Um exemplo simples de objeto 3D no formato OBJ:
# OBJ - Wavefront object file # Esta linha contém um comentário # definição dos vértices (coordenadas x y z) # números podem ser inteiros ou reais v 16 32 16 # definição do vértice v1 v 16 32 -16 # definição do vértice v2 v 16 0 16 # ... v 16 0 -16 v -16 32 16 v -16 32 -16 v -16 0 16 v -16 0 -16 # definição das faces f 1 3 4 2 # face f1: v1 -> v3 -> v4 -> v2 -> v1 f 6 8 7 5 f 2 6 5 1 f 3 7 8 4 f 1 5 7 3 f 4 8 6 2
Além dos vértices e faces, o formato OBJ também permite definir linhas, pontos, normais, texturas e outros elementos importantes em um modelo 3D.
Mais exemplos de objetos 3D estão disponíveis a seguir:
Esses arquivos podem ser facilmente visualizados no Linux através do programa g3dviewer
, ou neste visualizador online (ou neste).
A técnica de projeção 3D consiste em transformar uma representação 3D em 2D, ou seja, com todos os seus pontos em um mesmo plano, possibilitando então sua visualização na tela do computador.
Existem vários tipos de projeção, como a projeção ortográfica e a projeção em perspectiva. Na projeção em perspectiva, a posição do observador é considerada nos cálculos da transformação 3D-2D. Uma boa explicação da projeção em perspectiva pode ser encontrada neste site.
Os cálculos necessários à projeção podem ser complexos e demorados, mas algumas simplificações podem ser realizadas. Este projeto usa uma simplificação chamada perspectiva fraca (weak perspective), descrita a seguir:
Considerando que as coordenadas da câmera são [xc yc zc] e que a câmera está olhando para a origem [0 0 0], a conversão das coordenadas 3D de cada vértice v = [xv yv zv] em sua projeção 2D p = [xp yp] no plano z = 0 pode ser calculada desta forma:
Assim é obtida uma coleção de pontos [xp yp] que representa os vértices do objeto projetados no plano z=0.
Para que os pontos obtidos na etapa anterior possam ser plotados na tela, eles devem primeiro ser ajustados para o intervalo [(0,0) … (width, height)], onde width e height são as dimensões da janela de visualização, em pixels.
Esta etapa transforma o o conjunto de pontos projetados [xp yp] em um conjunto de pontos de desenho [xd yd], que podem ser usados para plotar as arestas dos objetos na tela.
Passo 1: calcular mínimos, máximos, centros e diferenças das coordenadas em X e Y
Passo 2: calcular fator de escala para o desenho na tela
Passo 3: centrar pontos da projeção em [0 0]
Passo 4: ajustar escala dos pontos da projeção para a tela
Passo 5: ajustar pontos do desenho em relação ao centro da tela
Com isso é obtido um conjunto de pontos no intervalo [(0, 0) .. (width, height)] que pode ser usado para plotar na janela gráfica as arestas que definem o objeto 3D.
Para apresentar a visualização de um objeto 3D, o programa deve:
strtok
.Formas de chamada do executável (ambas devem ser implementadas):
# usando argc/argv wireframe arquivo.obj # usando stdin wireframe < arquivo.obj # ou cat arquivo.obj | wireframe
A implementação deve atender os seguintes requisitos:
struct
para representar os elementos do modelo (vértices, faces, arestas)malloc
/free
)wireframe.c
: arquivo principaldatatypes.h
: tipos de dados usados no programaobjread.c/h
: funções de leitura do arquivo OBJperspect.c/h
: funções de cálculo de perspectivagraphics.c/h
: funções de apresentação gráficaMakefile
all
(default), clean
e purge
-Wall
Bônus (+ 20/100 pontos cada):
O que deve ser entregue ao professor:
.c
e .h
Makefile
As estruturas mais adequadas para este projeto são vetores de elementos (de pontos 3D, de pontos 2D, de retas, etc). Como o número de elementos não é fixo, esses vetores devem ser alocados dinamicamente. Entretanto, o número de elementos só é conhecido ao final da leitura do arquivo OBJ, por isso não é possível alocar cada vetor com seu tamanho final desde o início do programa.
A estratégia recomendada para esse problema é fazer uma alocação inicial e aumentá-la conforme a necessidade (usando a chamada realloc
). Como a realocação de memória é uma operação demorada, sugere-se realocar os vetores em “blocos” de centenas ou milhares de elementos de cada vez.
Existe uma infinidade de bibliotecas gráficas; entre as mais populares está a biblioteca SDL (Simple DirectMedia Layer). Além de gráficos, essa biblioteca permite a produção de sons e o gerenciamento de dispositivos de entrada/saída (mouse, teclado, joystick) em várias plataformas.
Documentação e tutoriais sobre SDL:
Instalação da biblioteca SDL 2 em Linux (Ubuntu, Mint, Debian):
sudo apt-get install libsdl2-dev
Arquivos de cabeçalho necessários:
#include <SDL2/SDL.h>
Flags de compilação:
LDLIBS = -lSDL2
A biblioteca gráfica Allegro permite a manipulação de gráficos simples e áudio, sendo bem adaptada para a construção de jogos 2D. É uma biblioteca mais simples e limitada que SDL, mas boa para projetos menores.
Algumas de suas características:
Instalação da biblioteca Allegro 5 em Linux (Ubuntu, Mint, Debian):
sudo apt-get install liballegro5-dev
Arquivos de cabeçalho necessários (mínimo, depende dos módulos usados):
#include <allegro5/allegro.h>
Flags de compilação (idem):
LDLIBS = -lallegro