This commit is contained in:
Laureηt 2023-06-22 20:30:57 +02:00
commit 17a86dcec5
Signed by: Laurent
SSH key fingerprint: SHA256:kZEpW8cMJ54PDeCvOhzreNr4FSh6R13CMGH/POoO8DI
62 changed files with 385695 additions and 0 deletions

1
TP1-2/.gitignore vendored Normal file
View file

@ -0,0 +1 @@
build/

73
TP1-2/.vscode/settings.json vendored Normal file
View file

@ -0,0 +1,73 @@
{
"files.associations": {
"*.html": "html",
"*.toml": "toml",
"cmath": "cpp",
"iostream": "cpp",
"cctype": "cpp",
"clocale": "cpp",
"cstdarg": "cpp",
"cstddef": "cpp",
"cstdio": "cpp",
"cstdlib": "cpp",
"cstring": "cpp",
"ctime": "cpp",
"cwchar": "cpp",
"cwctype": "cpp",
"array": "cpp",
"atomic": "cpp",
"hash_map": "cpp",
"bit": "cpp",
"*.tcc": "cpp",
"cfenv": "cpp",
"chrono": "cpp",
"compare": "cpp",
"complex": "cpp",
"concepts": "cpp",
"condition_variable": "cpp",
"cstdint": "cpp",
"deque": "cpp",
"list": "cpp",
"map": "cpp",
"set": "cpp",
"string": "cpp",
"unordered_map": "cpp",
"unordered_set": "cpp",
"vector": "cpp",
"exception": "cpp",
"algorithm": "cpp",
"functional": "cpp",
"iterator": "cpp",
"memory": "cpp",
"memory_resource": "cpp",
"numeric": "cpp",
"optional": "cpp",
"random": "cpp",
"ratio": "cpp",
"string_view": "cpp",
"system_error": "cpp",
"tuple": "cpp",
"type_traits": "cpp",
"utility": "cpp",
"fstream": "cpp",
"initializer_list": "cpp",
"iomanip": "cpp",
"iosfwd": "cpp",
"istream": "cpp",
"limits": "cpp",
"mutex": "cpp",
"new": "cpp",
"numbers": "cpp",
"ostream": "cpp",
"semaphore": "cpp",
"sstream": "cpp",
"stdexcept": "cpp",
"stop_token": "cpp",
"streambuf": "cpp",
"thread": "cpp",
"cinttypes": "cpp",
"typeinfo": "cpp",
"valarray": "cpp",
"variant": "cpp"
}
}

43
TP1-2/CMakeLists.txt Executable file
View file

@ -0,0 +1,43 @@
cmake_minimum_required(VERSION 3.10)
project(tp1 VERSION 2021.0.0 LANGUAGES C CXX)
set(CMAKE_CXX_STANDARD 11)
# set(OpenGL_GL_PREFERENCE "GLVND")
find_package(OpenGL REQUIRED)
message(STATUS "OPENGL_gl_LIBRARY: ${OPENGL_gl_LIBRARY}")
if(MSVC)
set(GLUT_ROOT_PATH "${CMAKE_SOURCE_DIR}/freeglut")
message(STATUS "GLUT_ROOT_PATH: ${GLUT_ROOT_PATH}")
endif()
find_package(GLUT REQUIRED)
message(STATUS "GLUT_FOUND: ${GLUT_FOUND}")
message(STATUS "GLUT_INCLUDE_DIR: ${GLUT_INCLUDE_DIR}")
message(STATUS "GLUT_LIBRARIES: ${GLUT_LIBRARIES}")
if(APPLE)
add_definitions(-Wno-deprecated-declarations)
endif()
add_executable(helloteapot helloteapot.cpp)
target_include_directories(helloteapot PUBLIC ${OPENGL_INCLUDE_DIR} ${GLUT_INCLUDE_DIR})
#target_link_libraries(helloteapot PUBLIC ${OPENGL_opengl_LIBRARY} ${OPENGL_glu_LIBRARY} ${GLUT_LIBRARIES})
target_link_libraries(helloteapot PUBLIC OpenGL::GL OpenGL::GLU ${GLUT_LIBRARIES})
# Uncomment the following lines (remove the #) to add the helloteapot2.cpp to the build system
# add_executable(helloteapot2 helloteapot2.cpp)
# target_include_directories(helloteapot2 PUBLIC ${OPENGL_INCLUDE_DIR} ${GLUT_INCLUDE_DIR})
# # target_link_libraries(helloteapot2 PUBLIC ${OPENGL_opengl_LIBRARY} ${OPENGL_glu_LIBRARY} ${GLUT_LIBRARIES})
# target_link_libraries(helloteapot2 PUBLIC OpenGL::GL OpenGL::GLU ${GLUT_LIBRARIES})
add_executable(moreteapots moreteapots.cpp)
target_include_directories(moreteapots PUBLIC ${OPENGL_INCLUDE_DIR} ${GLUT_INCLUDE_DIR})
#target_link_libraries(moreteapots PUBLIC ${OPENGL_opengl_LIBRARY} ${OPENGL_glu_LIBRARY} ${GLUT_LIBRARIES})
target_link_libraries(moreteapots PUBLIC OpenGL::GL OpenGL::GLU ${GLUT_LIBRARIES})
# Uncomment the following lines (remove the #) to add the navigator to the build system (after having added the navigator.cpp)
add_executable(navigator navigator.cpp)
target_include_directories(navigator PUBLIC ${OPENGL_INCLUDE_DIR} ${GLUT_INCLUDE_DIR})
# target_link_libraries(navigator PUBLIC ${OPENGL_opengl_LIBRARY} ${OPENGL_glu_LIBRARY} ${GLUT_LIBRARIES})
target_link_libraries(navigator PUBLIC OpenGL::GL OpenGL::GLU ${GLUT_LIBRARIES})

183
TP1-2/README.md Executable file
View file

@ -0,0 +1,183 @@
# Introduction to OpenGL
- Building
* [Windows](#windows)
* [Linux](#linux)
* [MacOs](#macos)
- [Adding new files](#Adding-new-files-to-the-build-systems)
---
## Windows
### Prerequisites
* download and install the latest version of CMake
* download here: https://github.com/Kitware/CMake/releases/download/v3.17.1/cmake-3.17.1-win64-x64.msi
* !!! When installing make sure that the checkbox "ne pas ajouter cmake au PATH" is NOT checked
* if you don't have it already, download and install MS Visual Studio Community Edition (free for students): https://visualstudio.microsoft.com/downloads/
* install instructions here: https://docs.microsoft.com/en-us/cpp/build/vscpp-step-0-installation?view=vs-2019
* !!! install the "Desktop development with C++"
* If you have VS already installed, you can go in **Tools** --> **Get Tools and Features...** to install "Desktop development with C++" if it is missing.
### Create the Visual Studio Solution.
This step enables you to create the project file to load inside VS:
* unzip the code inside a folder. *Avoid to place the code in folders with spaces and accented characters*.
* open a Terminal and go to the directory containing the code.
* execute:
* `md build`
* `cd build`
* `cmake -G "Visual Studio 16 2019" -A x64 -DCMAKE_BUILD_TYPE:STRING=Release ..`
* `dir`
> if you had a different version of VS installed (not the latest) you may need to adapt the string `Visual Studio 16 2019` to your version: e.g. Visual Studio 15 2017, Visual Studio 14 2015, Visual Studio 12 2013
* if everything went well you should find a file named `tp1.sln` inside the directory.
### Compile, build, execute
* open `tp1.sln` inside VS either by double clicking on it or opening from inside VS
* build the solution (**Build Solution** from the **Build menu**)
* from the tp directory copy `freeglut\bin\x64\freeglut.dll` in `build\Release`
* execute the code:
* Select the project you want to run (e.g. `helloteapot`), right click on it and select **Set as Startup Project**
* On the menu bar, choose **Debug** --> **Start without debugging**.)
(see https://docs.microsoft.com/en-us/cpp/build/vscpp-step-2-build?view=vs-2019 for how to build, execute, etc)
### Editing the code
Edit the code according to the assignments that are given, rebuild the solution and execute.
> !!! You need to run the cmake line only **once**
> !!! You need to copy the dll file only **once**.
---
## Linux
### Prerequisites
In order to develop with OpenGL some system packages are required (unless you are using the N7 machines):
```
sudo apt-get install libglu1-mesa-dev freeglut3-dev build-essential mesa-common-dev libxi-dev libxmu-dev automake
```
To build this code we use the CMake build system. You can install CMake from the system package manager but you need a recent version >= 3.10. Check the version that is provided by your linux distribution and if it is suitable usually you need to
```
sudo apt-get install cmake
```
otherwise you can install the binaries from here: https://github.com/Kitware/CMake/releases/download/v3.17.1/cmake-3.17.1-Linux-x86_64.sh
To install:
```
wget https://github.com/Kitware/CMake/releases/download/v3.17.1/cmake-3.17.1-Linux-x86_64.sh
chmod +x cmake-3.17.1-Linux-x86_64.sh
sudo cmake-3.17.1-Linux-x86_64.sh --prefix=/usr/local/ --skip-license
```
### Build
To compile and build the code you do
```
mkdir build && cd build
cmake ..
make <name_file_without_cpp>
```
Also,
```
make all
```
builds everything, and
```
make clean
```
cleans everything.
Execute the code:
```
./helloteapot
```
### Editing the code
Edit the code as required and then
```
make <name_file_without_cpp>
```
> !!! the cmake line has to be run only **once**
---
## macOS
### Prerequisites
In order to develop with OpenGL check if
```
ls /System/Library/Frameworks/
```
contains OpenGL and GLUT frameworks.
If not you need to install XCode from the `Mac App Store`, see here for more details https://developer.apple.com/support/xcode/
Install the latest version of CMake by downloading https://github.com/Kitware/CMake/releases/download/v3.17.1/cmake-3.17.1-Darwin-x86_64.dmg
### Build
Same as Linux.
### Editing the code
Same as Linux.
---
## Adding new files to the build systems
* Create the new file (helloteapot2.cpp, navigator.cpp)
* [Windows only] close VS
* Edit `CMakeLists.txt` and uncomment (remove the `#` at the beginning) the lines relevant to the file
* in the terminal, from the `build` directory run `cmake ..`
* [Windows only] reload the solution file, now a new `helloteapot2` or `navigator` target should appear. Build/execute as usual
* [other os] build and execute as usual, i.e. `make navigator`, `./navigator` ...

30
TP1-2/appveyor.yml Executable file
View file

@ -0,0 +1,30 @@
version: '1.0.{build}'
image: Visual Studio 2019
platform:
- x64
configuration:
- Release
#install:
# - vcpkg upgrade --no-dry-run
# - vcpkg install
# opengl
# --triplet %PLATFORM%-windows
before_build:
- md build
- cd build
# - cmake -G "Visual Studio 16 2019" -A x64 -T v140,host=x64 -DCMAKE_BUILD_TYPE=%configuration% -DCMAKE_TOOLCHAIN_FILE=c:/tools/vcpkg/scripts/buildsystems/vcpkg.cmake ..
- cmake -G "Visual Studio 16 2019" -A x64 -T v140,host=x64 -DCMAKE_BUILD_TYPE=%configuration% ..
- ls -l
build:
verbosity: detailed
project: $(APPVEYOR_BUILD_FOLDER)\build\tp1.sln
parallel: true
cache:
# - c:\tools\vcpkg\installed\

136
TP1-2/helloteapot.cpp Executable file
View file

@ -0,0 +1,136 @@
#include <stdlib.h>
// for mac osx
#ifdef __APPLE__
#include <OpenGL/gl.h>
#include <OpenGL/glu.h>
#include <GLUT/glut.h>
#else
// only for windows
#ifdef _WIN32
#include <windows.h>
#endif
// for windows and linux
#include <GL/gl.h>
#include <GL/glu.h>
#include <GL/freeglut.h>
#include <math.h>
#endif
bool solidMode = false;
double angle = 0;
// function called everytime the windows is refreshed
void display()
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// define the projection transformation
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(60, 1, 1, 10);
// define the viewing transformation
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
gluLookAt(3 * cos(angle), 0, 3 * sin(angle), 0.0, 0.0, 0.0, 0.0, 1.0, 0.0);
// clear window
glClear(GL_COLOR_BUFFER_BIT);
// draw scene
// glutWireTeapot(.5);
// glutSolidTeacup(0.5);
if (solidMode)
{
glutSolidTorus(0.25, 0.5, 100, 100);
}
else
{
glutWireTorus(0.25, 0.5, 100, 100);
}
// flush drawing routines to the window
glFlush();
angle += 0.001;
glutPostRedisplay();
}
// Function called everytime a key is pressed
void key(unsigned char key, int x, int y)
{
switch (key)
{
case 'r':
angle += 0.1;
break;
case 'w':
solidMode = !solidMode;
break;
// the 'esc' key
case 27:
// the 'q' key
case 'q':
exit(EXIT_SUCCESS);
break;
default:
break;
}
glutPostRedisplay();
}
// Function called every time the main window is resized
void reshape(int width, int height)
{
// define the viewport transformation;
glViewport(0, 0, width, height);
}
// Main routine
int main(int argc, char *argv[])
{
// initialize GLUT, using any commandline parameters passed to the
// program
glutInit(&argc, argv);
// setup the size, position, and display mode for new windows
glutInitWindowSize(500, 500);
glutInitWindowPosition(0, 0);
glutInitDisplayMode(GLUT_RGB);
// create and set up a window
glutCreateWindow("Hello, teapot!");
// Set up the callback functions:
// for display
glutDisplayFunc(display);
// for the keyboard
glutKeyboardFunc(key);
// for reshaping
glutReshapeFunc(reshape);
// lights from tp3
GLfloat light_ambient[] = {0.0, 0.0, 0.0, 1.0}; // the ambient component
GLfloat light_diffuse[] = {1.0, 1.0, 1.0, 1.0}; // the diffuse component
GLfloat light_specular[] = {1.0, 1.0, 1.0, 1.0}; // the specular component
GLfloat light_position[] = {1.0, 1.0, 1.0, 0.0}; // the light position
// set the components to the first light
glLightfv(GL_LIGHT0, GL_AMBIENT, light_ambient);
glLightfv(GL_LIGHT0, GL_DIFFUSE, light_diffuse);
glLightfv(GL_LIGHT0, GL_SPECULAR, light_specular);
glLightfv(GL_LIGHT0, GL_POSITION, light_position);
// activate lighting effects
glEnable(GL_LIGHTING);
// turn on the first light
glEnable(GL_LIGHT0);
glutInitDisplayMode(GLUT_RGB | GLUT_DEPTH);
glEnable(GL_DEPTH_TEST);
glEnable(GL_CULL_FACE);
// tell GLUT to wait for events
glutMainLoop();
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

BIN
TP1-2/imgs/sphere_wire.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

BIN
TP1-2/imgs/teapot_45.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

BIN
TP1-2/imgs/teapot_90.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

BIN
TP1-2/imgs/teapot_solid.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.3 KiB

BIN
TP1-2/imgs/teapot_wire.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.2 KiB

126
TP1-2/moreteapots.cpp Executable file
View file

@ -0,0 +1,126 @@
#include <cstdlib>
// for mac osx
#ifdef __APPLE__
#include <OpenGL/gl.h>
#include <OpenGL/glu.h>
#include <GLUT/glut.h>
#else
// only for windows
#ifdef _WIN32
#include <windows.h>
#endif
// for windows and linux
#include <GL/gl.h>
#include <GL/glu.h>
#include <GL/freeglut.h>
#endif
#include <iostream>
using namespace std;
void display()
{
glLoadIdentity();
/* clear window */
glClear(GL_COLOR_BUFFER_BIT);
// set current matrix as GL_MODELVIEW
glMatrixMode(GL_MODELVIEW);
// draw scene
// add a copy of the curr. matrix to the stack
glPushMatrix();
glPushMatrix();
// translate by -3 on the z
glTranslatef(0, 0, -3);
// set drawing color to red
glColor3f(1.0f, 0.0f, 0.0f);
// middle red teapot
glutWireTeapot(1);
// translate by 2 on the y
glTranslatef(0, 2, 0);
// set drawing color to green
glColor3f(0.0f, 1.0f, 0.0f);
// rotate 90 deg around x
glRotatef(90, 1.0f, 0.0f, 0.0f);
// top green teapot
glutWireTeapot(1);
// pop the current matrix
glPopMatrix();
// translate -2 on y and -1 on z
glTranslatef(0, -2, -1);
// set drawing color to blue
glColor3f(0.0f, 0.0f, 1.0f);
// bottom blue teapot
glutWireTeapot(1);
// pop the current matrix
glPopMatrix();
/* flush drawing routines to the window */
glFlush();
}
// Function called everytime a key is pressed
void key(unsigned char key, int x, int y)
{
switch (key)
{
case 27:
case 'q':
exit(EXIT_SUCCESS);
break;
default:
break;
}
glutPostRedisplay();
}
void reshape(int width, int height)
{
/* define the viewport transformation */
glViewport(0, 0, width, height);
}
int main(int argc, char *argv[])
{
/* initialize GLUT, using any commandline parameters passed to the
program */
glutInit(&argc, argv);
/* setup the size, position, and display mode for new windows */
glutInitWindowSize(500, 500);
glutInitWindowPosition(0, 0);
glutInitDisplayMode(GLUT_RGB);
/* create and set up a window */
glutCreateWindow("hello, teapot!");
// Set up the callback functions
// for display
glutDisplayFunc(display);
// for the keyboard
glutKeyboardFunc(key);
// for reshaping
glutReshapeFunc(reshape);
/* define the projection transformation */
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(60, 1, 1, 10);
/* define the viewing transformation */
glMatrixMode(GL_MODELVIEW);
gluLookAt(0.0, 0.0, 5.0, 0.0, 0.0, -1.0, 0.0, 1.0, 0.0);
/* tell GLUT to wait for events */
glutMainLoop();
}

176
TP1-2/navigator.cpp Executable file
View file

@ -0,0 +1,176 @@
#include <cstdlib>
// for mac osx
#ifdef __APPLE__
#include <OpenGL/gl.h>
#include <OpenGL/glu.h>
#include <GLUT/glut.h>
#else
// only for windows
#ifdef _WIN32
#include <windows.h>
#endif
// for windows and linux
#include <GL/gl.h>
#include <GL/glu.h>
#include <GL/freeglut.h>
#include <math.h>
#endif
#include <iostream>
using namespace std;
#define SPEED 0.1 // OpenGL unit
#define ANG_SPEED 0.01 // degrees
double x_cam = 0.0;
double y_cam = 0.0;
double z_cam = 5.0;
double angleX_cam = 0.0;
double angleY_cam = -M_PI / 2;
void display()
{
/* define the projection transformation */
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(60, 1, 1, 10);
/* define the viewing transformation */
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
gluLookAt(
x_cam, y_cam, z_cam,
x_cam + cos(angleY_cam), y_cam + sin(angleX_cam), z_cam + sin(angleY_cam) * cos(angleX_cam),
0, cos(angleX_cam), 0);
/* clear window */
glClear(GL_COLOR_BUFFER_BIT);
// set current matrix as GL_MODELVIEW
glMatrixMode(GL_MODELVIEW);
// draw scene
// add a copy of the curr. matrix to the stack
glPushMatrix();
glPushMatrix();
// translate by -3 on the z
glTranslatef(0, 0, -3);
// set drawing color to red
glColor3f(1.0f, 0.0f, 0.0f);
// middle red teapot
glutWireTeapot(1);
// translate by 2 on the y
glTranslatef(0, 2, 0);
// set drawing color to blue
glColor3f(0.0f, 1.0f, 0.0f);
// rotate 90 deg around x
glRotatef(90, 1.0f, 0.0f, 0.0f);
// top green teapot
glutWireTeapot(1);
// pop the current matrix
glPopMatrix();
// translate -2 on y and -1 on z
glTranslatef(0, -2, -1);
// set drawing color to blue
glColor3f(0.0f, 0.0f, 1.0f);
// bottom blue teapot
glutWireTeapot(1);
// pop the current matrix
glPopMatrix();
/* flush drawing routines to the window */
glFlush();
}
// Function called everytime a key is pressed
void key(unsigned char key, int x, int y)
{
switch (key)
{
case 'z':
z_cam -= SPEED;
break;
case 's':
z_cam += SPEED;
break;
case 'q':
x_cam -= SPEED;
break;
case 'd':
x_cam += SPEED;
break;
case 'a':
y_cam -= SPEED;
break;
case 'e':
y_cam += SPEED;
break;
case 27:
exit(EXIT_SUCCESS);
break;
default:
break;
}
glutPostRedisplay();
}
void specialkey(int key, int x, int y)
{
switch (key)
{
case GLUT_KEY_LEFT:
angleY_cam -= ANG_SPEED;
break;
case GLUT_KEY_RIGHT:
angleY_cam += ANG_SPEED;
break;
case GLUT_KEY_DOWN:
angleX_cam -= ANG_SPEED;
break;
case GLUT_KEY_UP:
angleX_cam += ANG_SPEED;
break;
default:
break;
}
glutPostRedisplay();
}
void reshape(int width, int height)
{
/* define the viewport transformation */
glViewport(0, 0, width, height);
}
int main(int argc, char *argv[])
{
/* initialize GLUT, using any commandline parameters passed to the
program */
glutInit(&argc, argv);
/* setup the size, position, and display mode for new windows */
glutInitWindowSize(500, 500);
glutInitWindowPosition(0, 0);
glutInitDisplayMode(GLUT_RGB);
/* create and set up a window */
glutCreateWindow("hello, navigator!");
// Set up the callback functions
// for display
glutDisplayFunc(display);
// for the keyboard
glutKeyboardFunc(key);
// for special keys
glutSpecialFunc(specialkey);
// for reshaping
glutReshapeFunc(reshape);
/* tell GLUT to wait for events */
glutMainLoop();
}

124
TP1-2/readme.html Executable file
View file

@ -0,0 +1,124 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta http-equiv="Content-Style-Type" content="text/css" />
<meta name="generator" content="pandoc" />
<title></title>
<style type="text/css">code{white-space: pre;}</style>
</head>
<body>
<h1 id="introduction-to-opengl">Introduction to OpenGL</h1>
<ul>
<li>Building</li>
<li><a href="#windows">Windows</a></li>
<li><a href="#linux">Linux</a></li>
<li><a href="#macos">MacOs</a></li>
<li><a href="#adding-navigator.cpp">Navigator</a></li>
</ul>
<hr />
<h2 id="windows">Windows</h2>
<h3 id="prerequisites">Prerequisites</h3>
<ul>
<li><p>download and install the latest version of CMake</p></li>
<li><p>download here: https://github.com/Kitware/CMake/releases/download/v3.17.1/cmake-3.17.1-win64-x64.msi</p></li>
<li><p>!!! When installing make sure that the checkbox &quot;ne pas ajouter cmake au PATH&quot; is NOT checked</p></li>
<li><p>if you don't have it already, download and install MS Visual Studio Community Edition (free for students): https://visualstudio.microsoft.com/downloads/</p>
<ul>
<li><p>install instructions here: https://docs.microsoft.com/en-us/cpp/build/vscpp-step-0-installation?view=vs-2019</p></li>
<li><p>!!! install the &quot;Desktop development with C++&quot;</p></li>
<li><p>If you have VS already installed, you can go in <strong>Tools</strong> --&gt; <strong>Get Tools and Features...</strong> to install &quot;Desktop development with C++&quot; if it is missing.</p></li>
</ul></li>
</ul>
<h3 id="create-the-visual-studio-solution.">Create the Visual Studio Solution.</h3>
<p>This step enables you to create the project file to load inside VS:</p>
<ul>
<li><p>unzip the code inside a folder. <em>Avoid to place the code in folders with spaces and accented characters</em>.</p></li>
<li><p>open a Terminal and go to the directory containing the code.</p></li>
<li><p>execute:</p></li>
<li><p><code>md build</code></p></li>
<li><p><code>cd build</code></p></li>
<li><p><code>cmake -G &quot;Visual Studio 16 2019&quot; -A x64 -DCMAKE_BUILD_TYPE:STRING=Release ..</code></p></li>
<li><p><code>dir</code></p></li>
</ul>
<blockquote>
<p>if you had a different version of VS installed (not the latest) you may need to adapt the string <code>Visual Studio 16 2019</code> to your version: e.g. Visual Studio 15 2017, Visual Studio 14 2015, Visual Studio 12 2013</p>
</blockquote>
<ul>
<li>if everything went well you should find a file named <code>tp1.sln</code> inside the directory.</li>
</ul>
<h3 id="compile-build-execute">Compile, build, execute</h3>
<ul>
<li><p>open <code>tp1.sln</code> inside VS either by double clicking on it or opening from inside VS</p></li>
<li><p>build the solution (<strong>Build Solution</strong> from the <strong>Build menu</strong>)</p></li>
<li><p>from the tp directory copy <code>freeglut\bin\x64\freeglut.dll</code> in <code>build\Release</code></p></li>
<li><p>execute the code:</p></li>
<li><p>Select the project you want to run (e.g. <code>helloteapot</code>), right click on it and select <strong>Set as Startup Project</strong></p></li>
<li><p>On the menu bar, choose <strong>Debug</strong> --&gt; <strong>Start without debugging</strong>.)</p></li>
</ul>
<p>(see https://docs.microsoft.com/en-us/cpp/build/vscpp-step-2-build?view=vs-2019 for how to build, execute, etc)</p>
<h3 id="editing-the-code">Editing the code</h3>
<p>Edit the code according to the assignments that are given, rebuild the solution and execute.</p>
<blockquote>
<p>!!! You need to run the cmake line only <strong>once</strong></p>
</blockquote>
<blockquote>
<p>!!! You need to copy the dll file only <strong>once</strong>.</p>
</blockquote>
<hr />
<h2 id="linux">Linux</h2>
<h3 id="prerequisites-1">Prerequisites</h3>
<p>In order to develop with OpenGL some system packages are required (unless you are using the N7 machines):</p>
<pre><code>sudo apt-get install libglu1-mesa-dev freeglut3-dev build-essential mesa-common-dev libxi-dev libxmu-dev automake</code></pre>
<p>To build this code we use the CMake build system. You can install CMake from the system package manager but you need a recent version &gt;= 3.10. Check the version that is provided by your linux distribution and if it is suitable usually you need to</p>
<pre><code>```
sudo apt-get install cmake
```
otherwise you can install the binaries from here: https://github.com/Kitware/CMake/releases/download/v3.17.1/cmake-3.17.1-Linux-x86_64.sh
To install:
```
wget https://github.com/Kitware/CMake/releases/download/v3.17.1/cmake-3.17.1-Linux-x86_64.sh
chmod +x cmake-3.17.1-Linux-x86_64.sh
sudo cmake-3.17.1-Linux-x86_64.sh --prefix=/usr/local/ --skip-license
```</code></pre>
<h3 id="build">Build</h3>
<p>To compile and build the code you do</p>
<p><code>mkdir build &amp;&amp; cd build cmake .. make &lt;name_file_without_cpp&gt;</code></p>
<p>Also,</p>
<pre><code>make all</code></pre>
<p>builds everything, and</p>
<pre><code>make clean</code></pre>
<p>cleans everything.</p>
<p>Execute the code:</p>
<pre><code>./helloteapot</code></pre>
<h3 id="editing-the-code-1">Editing the code</h3>
<p>Edit the code as required and then</p>
<pre><code>make &lt;name_file_without_cpp&gt;</code></pre>
<blockquote>
<p>!!! the cmake line has to be run only <strong>once</strong></p>
</blockquote>
<hr />
<h2 id="macos">macOS</h2>
<h3 id="prerequisites-2">Prerequisites</h3>
<p>In order to develop with OpenGL check if</p>
<pre><code>ls /System/Library/Frameworks/</code></pre>
<p>contains OpenGL and GLUT frameworks. If not you need to install Xcode from the <code>Mac App Store</code>, see here for more details https://developer.apple.com/support/xcode/</p>
<p>If you want to use CMake, follow the instructions for linux to install the latest version</p>
<h3 id="build-1">Build</h3>
<p>Same as Linux.</p>
<h3 id="editing-the-code-2">Editing the code</h3>
<p>Same as Linux.</p>
<hr />
<h2 id="adding-new-files-to-the-build-systems">Adding new files to the build systems</h2>
<ul>
<li><p>Create the new file (helloteapot2.cpp, navigator.cpp)</p></li>
<li><p>[Windows only] close VS</p></li>
<li><p>Edit <code>CMakeLists.txt</code> and uncomment (remove the <code>#</code> at the beginning) the lines relevant to the file</p></li>
<li><p>in the terminal, from the <code>build</code> directory run <code>cmake ..</code></p></li>
<li><p>[Windows only] reload the solution file, now a new <code>helloteapot2</code> or <code>navigator</code> target should appear. Build/execute as usual</p></li>
<li><p>[other os] build and execute as usual, i.e. <code>make navigator</code>, <code>./navigator</code> ...</p></li>
</ul>
</body>
</html>

137
TP1-2/reponses.md Normal file
View file

@ -0,0 +1,137 @@
# Replace glutWireTeapot with glutSolidTeapot.
What do you see ?
What do you think it is still missing in order to see a more realistic rendering of the teapot?
> On voit un teapot "plein"/solid, il manque simplement un peu de lumière/shading, pour que le rendu soit plus réaliste.
<table>
<tr>
<td>
<figcaption>glutWireTeapot(.5)</figcaption>
<img src="imgs/teapot_wire.png"/>
</td>
<td>
<figcaption>glutSolidTeapot(.5)</figcaption>
<img src="imgs/teapot_solid.png"/>
</td>
</tr>
</table>
# Replace the teapot with a sphere using glutWireSphere.
The parameter slices is the number of meridians around the z-axis to draw, and stacks the parallels along the z-axis.
Try with different values for each of them and compare the results
<table>
<tr>
<td>
<figcaption>glutWireSphere(0.7, 20, 20)</figcaption>
<img src="imgs/sphere_wire.png"/>
</td>
<td>
<figcaption>glutWireSphere(0.7, 100, 100)</figcaption>
<img src="imgs/sphere_morewire.png"/>
</td>
</tr>
</table>
# Try to manually resize the window. What happens?
How can we preserve the aspect ratio of our scene?
Change the call to glViewport so that every time the windows is resized, the viewport is always a square centred both vertically and horizontally inside the window, and its size is the maximum that can fit the window (i.e. its size is the minimum between the width and the height of the window).
> L'aspect ratio de notre fenêtre change, mais l'aspect ratio de notre scène n'étant pas fixe, on observe que le rendu devient difforme.
> En utilisant le callback glviewport, on peut ainsi fixer/conserver l'aspect ratio en toute circonstance.
# Add a new case for the letter w, so that every time it is pressed we can switch from a wireframe teapot to a solid teapot and viceversa.
(Hint: you can define and use a global bool variable...)
> cf helloteapot.cpp
# Try to increase and decrease the value of the parameter fovy of gluPerspective.
What happens and why?
> fovy correspond à la focale de la caméra selon l'axe y de l'image.
> On observe alors que les objets sont comprimés ou étalées, si l'on diminue ou augmente fovy.
# Try to increase the value of the parameter zNear of gluPerspective.
What happens for values, e.g., greater than 2.1?
> zNear permet de définir le début du volume de clipping.
> Ainsi si l'on défini zNear supérieur à 2.1 dans la scène de l'exercice, le volume de clipping se situe derrière le teapot et donc on ne le voit plus.
# What happens if we invert the up vector, i.e. we set it to (0,-1,0)?
> L'image est à l'envers.
<table>
<tr>
<td>
<figcaption>gluLookAt(0.0, 0.0, 2.0, 0.0, 0.0, 0.0, 0.0, -1.0, 0.0)</figcaption>
<img src="imgs/teapot_upsidedown.png"/>
</td>
</tr>
</table>
# Set the camera so that we can see the teapot as in Figure 9 (a-b)?
How do you change the “up-vector”?
> On défini le up vector : (1, 1, -1)
<table>
<tr>
<td>
<figcaption>gluLookAt(0.0, 0.0, -2.0, 0.0, 0.0, 0.0, 1.0, 1.0, -1.0)</figcaption>
<img src="imgs/teapot_45.png"/>
</td>
<td>
<figcaption>gluLookAt(0.0, 2.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0);</figcaption>
<img src="imgs/teapot_90.png"/>
</td>
</tr>
</table>
# What happens if the second glPopMatrix in display isnt there?
Try resizing the window or partially covering it, to force a repaint.
Why does this occur? (Consider what happens to the modelview stack.)
> On push deux matrices au début de la fonction. On doit alors pop deux matrices avant de finir la fonction. Si on enlève le deuxième glPopMatrix, alors les transformations suivantes seront conservées lors de la prochaine itération:
```cpp
// translate -2 on y and -1 on z
glTranslatef(0, -2, -1);
// set drawing color to blue
glColor3f(0.0f, 0.0f, 1.0f);
// bottom blue teapot
glutWireTeapot(1);
```
Ainsi lorsque l'on redimensionne la fenêtre, on doit redessiner les teapots, et par accumulation des glTranslatef, ceux-ci disparaissent.
# What happens if the gluLookAt(0.0,0.0,5.0,0.0,0.0,0.0,0.0,1.0,0.0) line in main is moved after the second glPushMatrix in display?
Why does this occur? (Consider what happens to the modelview stack.)
> On observe que le teapot bleu n'est plus affiché. On suppose que cela se produit car le gluLookAt n'est appliqué que sur les deux premiers teapots (le rouge et le vert). Après le premier pop, l'information du gluLookAt, n'est pas transmise au tracé du teapot bleu.
<table>
<tr>
<td>
<img src="imgs/blue_teapot_gone.png"/>
</td>
</tr>
</table>
# What happens instead if the glLoadIdentity line in main is moved to just before the “draw scene” comment in display, and why?
(Consider what happens to the modelview stack.)
> En utilisant glLoadIdentity en début d'itéation, on "reset" la camera, cela inclut gluLookAt et gluPerspective. On obtient une image très zoommée.
<table>
<tr>
<td>
<img src="imgs/teapot_identity.png"/>
</td>
</tr>
</table>

33
TP3/CMakeLists.txt Normal file
View file

@ -0,0 +1,33 @@
cmake_minimum_required(VERSION 3.10)
project(tp2 VERSION 2021.0.0 LANGUAGES C CXX)
set(CMAKE_CXX_STANDARD 11)
#set(OpenGL_GL_PREFERENCE "GLVND")
find_package(OpenGL REQUIRED)
message(STATUS "OPENGL_gl_LIBRARY: ${OPENGL_gl_LIBRARY}")
if(MSVC)
set(GLUT_ROOT_PATH "${CMAKE_SOURCE_DIR}/freeglut")
message(STATUS "GLUT_ROOT_PATH: ${GLUT_ROOT_PATH}")
endif()
find_package(GLUT REQUIRED)
message(STATUS "GLUT_FOUND: ${GLUT_FOUND}")
message(STATUS "GLUT_INCLUDE_DIR: ${GLUT_INCLUDE_DIR}")
message(STATUS "GLUT_LIBRARIES: ${GLUT_LIBRARIES}")
if(APPLE)
add_definitions(-Wno-deprecated-declarations)
endif()
add_executable(robot robot.c)
#target_include_directories(robot PUBLIC ${OPENGL_INCLUDE_DIR} ${GLUT_INCLUDE_DIR})
#target_link_libraries(robot PUBLIC ${OPENGL_opengl_LIBRARY} ${OPENGL_glu_LIBRARY} ${GLUT_LIBRARIES})
target_link_libraries(robot OpenGL::GL OpenGL::GLU GLUT::GLUT)
if (CMAKE_CXX_COMPILER_ID MATCHES "Clang|GNU")
target_compile_options(robot PRIVATE -Wall -Wextra -pedantic -Wno-comment)
endif()
if(MSVC)
target_compile_definitions(robot PUBLIC -DNOMINMAX)
endif()

170
TP3/README.md Normal file
View file

@ -0,0 +1,170 @@
# Introduction to OpenGL
- Building
* [Windows](#windows)
* [Linux](#linux)
* [MacOs](#macos)
---
## Windows
### Prerequisites
Not necessary if you already did the previous tp, but to recall:
* download and install the latest version of CMake
* download here: https://github.com/Kitware/CMake/releases/download/v3.17.1/cmake-3.17.1-win64-x64.msi
* !!! When installing make sure that the checkbox "ne pas ajouter cmake au PATH" is NOT checked
* if you don't have it already, download and install MS Visual Studio Community Edition (free for students): https://visualstudio.microsoft.com/downloads/
* install instructions here: https://docs.microsoft.com/en-us/cpp/build/vscpp-step-0-installation?view=vs-2019
* !!! install the "Desktop development with C++"
* If you have VS already installed, you can go in **Tools** --> **Get Tools and Features...** to install "Desktop development with C++" if it is missing.
### Create the Visual Studio Solution.
This step enables you to create the project file to load inside VS:
* unzip the code inside a folder. *Avoid to place the code in folders with spaces and accented characters*.
* open a Terminal and go to the directory containing the code.
* execute:
* `md build`
* `cd build`
* `cmake -G "Visual Studio 16 2019" -A x64 -DCMAKE_BUILD_TYPE:STRING=Release ..`
* `dir`
> if you had a different version of VS installed (not the latest) you may need to adapt the string `Visual Studio 16 2019` to your version: e.g. Visual Studio 15 2017, Visual Studio 14 2015, Visual Studio 12 2013
* if everything went well you should find a file named `tp2.sln` inside the directory.
### Compile, build, execute
* open `tp1.sln` inside VS either by double clicking on it or opening from inside VS
* build the solution (**Build Solution** from the **Build menu**)
* from the tp directory copy `freeglut\bin\x64\freeglut.dll` in `build\Release`
* execute the code:
* Select the project you want to run (e.g. `robot`), right click on it and select **Set as Startup Project**
* On the menu bar, choose **Debug** --> **Start without debugging**.)
(see https://docs.microsoft.com/en-us/cpp/build/vscpp-step-2-build?view=vs-2019 for how to build, execute, etc)
### Editing the code
Edit the code according to the assignments that are given, rebuild the solution and execute.
> !!! You need to run the cmake line only **once**
> !!! You need to copy the dll file only **once**.
---
## Linux
### Prerequisites
**Not necessary if you already did the previous tp.**
In order to develop with OpenGL some system packages are required (unless you are using the N7 machines):
```
sudo apt-get install libglu1-mesa-dev freeglut3-dev build-essential mesa-common-dev libxi-dev libxmu-dev automake
```
To build this code we use the CMake build system. You can install CMake from the system package manager but you need a recent version >= 3.10. Check the version that is provided by your linux distribution and if it is suitable usually you need to
```
sudo apt-get install cmake
```
otherwise you can install the binaries from here: https://github.com/Kitware/CMake/releases/download/v3.17.1/cmake-3.17.1-Linux-x86_64.sh
To install:
```
wget https://github.com/Kitware/CMake/releases/download/v3.17.1/cmake-3.17.1-Linux-x86_64.sh
chmod +x cmake-3.17.1-Linux-x86_64.sh
sudo cmake-3.17.1-Linux-x86_64.sh --prefix=/usr/local/ --skip-license
```
### Build
To compile and build the code you do
```
mkdir build && cd build
cmake ..
make robot
```
Also,
```
make all
```
builds everything, and
```
make clean
```
cleans everything.
Execute the code:
```
./robot
```
### Editing the code
Edit the code as required and then
```
make robot
```
> !!! the cmake line has to be run only **once**
---
## macOS
### Prerequisites
In order to develop with OpenGL check if
```
ls /System/Library/Frameworks/
```
contains OpenGL and GLUT frameworks.
If not you need to install XCode from the `Mac App Store`, see here for more details https://developer.apple.com/support/xcode/
Install the latest version of CMake by downloading https://github.com/Kitware/CMake/releases/download/v3.17.1/cmake-3.17.1-Darwin-x86_64.dmg
### Build
Same as Linux.
### Editing the code
Same as Linux.

31
TP3/appveyor.yml Normal file
View file

@ -0,0 +1,31 @@
version: '2021.0.0.{build}'
image: Visual Studio 2019
platform:
- x64
configuration:
- Release
- Debug
#install:
# - vcpkg upgrade --no-dry-run
# - vcpkg install
# opengl
# --triplet %PLATFORM%-windows
before_build:
- md build
- cd build
# - cmake -G "Visual Studio 16 2019" -A x64 -T v140,host=x64 -DCMAKE_BUILD_TYPE=%configuration% -DCMAKE_TOOLCHAIN_FILE=c:/tools/vcpkg/scripts/buildsystems/vcpkg.cmake ..
- cmake -G "Visual Studio 16 2019" -A x64 -T v140,host=x64 -DCMAKE_BUILD_TYPE=%configuration% ..
- ls -l
build:
verbosity: detailed
project: $(APPVEYOR_BUILD_FOLDER)\build\tp2.sln
parallel: true
cache:
# - c:\tools\vcpkg\installed\

114
TP3/readme.html Normal file
View file

@ -0,0 +1,114 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta http-equiv="Content-Style-Type" content="text/css" />
<meta name="generator" content="pandoc" />
<title></title>
<style type="text/css">code{white-space: pre;}</style>
</head>
<body>
<h1 id="introduction-to-opengl">Introduction to OpenGL</h1>
<ul>
<li>Building</li>
<li><a href="#windows">Windows</a></li>
<li><a href="#linux">Linux</a></li>
<li><a href="#macos">MacOs</a></li>
</ul>
<hr />
<h2 id="windows">Windows</h2>
<h3 id="prerequisites">Prerequisites</h3>
<p>Not necessary if you already did the previous tp, but to recall: * download and install the latest version of CMake</p>
<ul>
<li><p>download here: https://github.com/Kitware/CMake/releases/download/v3.17.1/cmake-3.17.1-win64-x64.msi</p></li>
<li><p>!!! When installing make sure that the checkbox &quot;ne pas ajouter cmake au PATH&quot; is NOT checked</p></li>
<li><p>if you don't have it already, download and install MS Visual Studio Community Edition (free for students): https://visualstudio.microsoft.com/downloads/</p>
<ul>
<li><p>install instructions here: https://docs.microsoft.com/en-us/cpp/build/vscpp-step-0-installation?view=vs-2019</p></li>
<li><p>!!! install the &quot;Desktop development with C++&quot;</p></li>
<li><p>If you have VS already installed, you can go in <strong>Tools</strong> --&gt; <strong>Get Tools and Features...</strong> to install &quot;Desktop development with C++&quot; if it is missing.</p></li>
</ul></li>
</ul>
<h3 id="create-the-visual-studio-solution.">Create the Visual Studio Solution.</h3>
<p>This step enables you to create the project file to load inside VS:</p>
<ul>
<li><p>unzip the code inside a folder. <em>Avoid to place the code in folders with spaces and accented characters</em>.</p></li>
<li><p>open a Terminal and go to the directory containing the code.</p></li>
<li><p>execute:</p></li>
<li><p><code>md build</code></p></li>
<li><p><code>cd build</code></p></li>
<li><p><code>cmake -G &quot;Visual Studio 16 2019&quot; -A x64 -DCMAKE_BUILD_TYPE:STRING=Release ..</code></p></li>
<li><p><code>dir</code></p></li>
</ul>
<blockquote>
<p>if you had a different version of VS installed (not the latest) you may need to adapt the string <code>Visual Studio 16 2019</code> to your version: e.g. Visual Studio 15 2017, Visual Studio 14 2015, Visual Studio 12 2013</p>
</blockquote>
<ul>
<li>if everything went well you should find a file named <code>tp2.sln</code> inside the directory.</li>
</ul>
<h3 id="compile-build-execute">Compile, build, execute</h3>
<ul>
<li><p>open <code>tp1.sln</code> inside VS either by double clicking on it or opening from inside VS</p></li>
<li><p>build the solution (<strong>Build Solution</strong> from the <strong>Build menu</strong>)</p></li>
<li><p>from the tp directory copy <code>freeglut\bin\x64\freeglut.dll</code> in <code>build\Release</code></p></li>
<li><p>execute the code:</p></li>
<li><p>Select the project you want to run (e.g. <code>robot</code>), right click on it and select <strong>Set as Startup Project</strong></p></li>
<li><p>On the menu bar, choose <strong>Debug</strong> --&gt; <strong>Start without debugging</strong>.)</p></li>
</ul>
<p>(see https://docs.microsoft.com/en-us/cpp/build/vscpp-step-2-build?view=vs-2019 for how to build, execute, etc)</p>
<h3 id="editing-the-code">Editing the code</h3>
<p>Edit the code according to the assignments that are given, rebuild the solution and execute.</p>
<blockquote>
<p>!!! You need to run the cmake line only <strong>once</strong></p>
</blockquote>
<blockquote>
<p>!!! You need to copy the dll file only <strong>once</strong>.</p>
</blockquote>
<hr />
<h2 id="linux">Linux</h2>
<h3 id="prerequisites-1">Prerequisites</h3>
<p><strong>Not necessary if you already did the previous tp.</strong></p>
<p>In order to develop with OpenGL some system packages are required (unless you are using the N7 machines):</p>
<pre><code>sudo apt-get install libglu1-mesa-dev freeglut3-dev build-essential mesa-common-dev libxi-dev libxmu-dev automake</code></pre>
<p>To build this code we use the CMake build system. You can install CMake from the system package manager but you need a recent version &gt;= 3.10. Check the version that is provided by your linux distribution and if it is suitable usually you need to</p>
<pre><code>```
sudo apt-get install cmake
```
otherwise you can install the binaries from here: https://github.com/Kitware/CMake/releases/download/v3.17.1/cmake-3.17.1-Linux-x86_64.sh
To install:
```
wget https://github.com/Kitware/CMake/releases/download/v3.17.1/cmake-3.17.1-Linux-x86_64.sh
chmod +x cmake-3.17.1-Linux-x86_64.sh
sudo cmake-3.17.1-Linux-x86_64.sh --prefix=/usr/local/ --skip-license
```</code></pre>
<h3 id="build">Build</h3>
<p>To compile and build the code you do</p>
<p><code>mkdir build &amp;&amp; cd build cmake .. make robot</code></p>
<p>Also,</p>
<pre><code>make all</code></pre>
<p>builds everything, and</p>
<pre><code>make clean</code></pre>
<p>cleans everything.</p>
<p>Execute the code:</p>
<pre><code>./robot</code></pre>
<h3 id="editing-the-code-1">Editing the code</h3>
<p>Edit the code as required and then</p>
<pre><code>make robot</code></pre>
<blockquote>
<p>!!! the cmake line has to be run only <strong>once</strong></p>
</blockquote>
<hr />
<h2 id="macos">macOS</h2>
<h3 id="prerequisites-2">Prerequisites</h3>
<p>In order to develop with OpenGL check if</p>
<pre><code>ls /System/Library/Frameworks/</code></pre>
<p>contains OpenGL and GLUT frameworks. If not you need to install XCode from the <code>Mac App Store</code>, see here for more details https://developer.apple.com/support/xcode/</p>
<p>Install the latest version of CMake by downloading https://github.com/Kitware/CMake/releases/download/v3.17.1/cmake-3.17.1-Darwin-x86_64.dmg</p>
<h3 id="build-1">Build</h3>
<p>Same as Linux.</p>
<h3 id="editing-the-code-2">Editing the code</h3>
<p>Same as Linux.</p>
</body>
</html>

355
TP3/robot.c Normal file
View file

@ -0,0 +1,355 @@
#include <stdlib.h>
#include <stdio.h>
// for mac osx
#ifdef __APPLE__
#include <OpenGL/gl.h>
#include <OpenGL/glu.h>
#include <GLUT/glut.h>
#else
// only for windows
#ifdef _WIN32
#include <windows.h>
#endif
// for windows and linux
#include <GL/gl.h>
#include <GL/glu.h>
#include <GL/freeglut.h>
#endif
#include <math.h>
// Global variables to animate the robotic arm
int Angle1 = 45;
int Angle2 = 45;
// Global variables to rotate the arm as a whole
int RobotAngleX = 0;
int RobotAngleY = -20;
float pincer = 0.5;
//*************************************************************************
// Function that draws a reference system
//*************************************************************************
void DrawReferenceSystem()
{
//**********************************
// set the line width to 3.0
//**********************************
glLineWidth(3);
//**********************************
// Draw three lines along the x, y, z axis to represent the reference system
// Use red for the x-axis, green for the y-axis and blue for the z-axis
//**********************************
glBegin(GL_LINES);
glColor3f(1, 0, 0);
glVertex3f(0, 0, 0);
glVertex3f(1, 0, 0);
glColor3f(0, 1, 0);
glVertex3f(0, 0, 0);
glVertex3f(0, 1, 0);
glColor3f(0, 0, 1);
glVertex3f(0, 0, 0);
glVertex3f(0, 0, 1);
glEnd();
//**********************************
// reset the drawing color to white
//**********************************
glColor3f(1, 1, 1);
//**********************************
// reset the line width to 1.0
//**********************************
glLineWidth(1);
}
//*************************************************************************
// Function that draws a single joint of the robotic arm
//*************************************************************************
void DrawJoint()
{
// first draw the reference system
DrawReferenceSystem();
// Draw the joint as a parallelepiped (a cube scaled on the y-axis)
// the bottom face of the cube must be on the xz plane
//**********************************
// draw the parallelepiped
//**********************************
glPushMatrix();
glTranslatef(0, 1, 0);
glScalef(1, 2, 1);
glutWireCube(1);
glPopMatrix();
}
// Function that draws the robot as three parallelepipeds
void DrawRobot()
{
//**********************************
// It's better to work on a local reference system...
//**********************************
glPushMatrix();
// draw the first joint
DrawJoint();
// Draw the other joints
// every joint must be placed on top of the previous one
// and rotated according to the relevant Angle
//**********************************
// the second joint
//**********************************
glTranslatef(0, 2, 0);
glRotatef(Angle1, 0, 0, 1);
DrawJoint();
//**********************************
// the third joint
//**********************************
glTranslatef(0, 2, 0);
glRotatef(Angle2, 0, 0, 1);
DrawJoint();
//**********************************
// the pincer's base
//**********************************
glTranslatef(0, 2, 0);
DrawReferenceSystem();
glPushMatrix();
glTranslatef(0, 0.125, 0);
glScalef(1, 0.25, 0.25);
glutWireCube(1);
glPopMatrix();
glTranslatef(0, 0.25, 0);
DrawReferenceSystem();
// pincer left
glPushMatrix();
glTranslatef(pincer, 0.5, 0);
glScalef(0.25, 1, 0.25);
glutWireCube(1);
glPopMatrix();
// picer right
glPushMatrix();
glTranslatef(-pincer, 0.5, 0);
glScalef(0.25, 1, 0.25);
glutWireCube(1);
glPopMatrix();
//**********************************
// "Release" the copy of the current MODELVIEW matrix
//**********************************
glPopMatrix();
}
//*************************************************************************
// display callback
//*************************************************************************
void display(void)
{
// clear the window
glClear(GL_COLOR_BUFFER_BIT);
// working with the GL_MODELVIEW Matrix
glMatrixMode(GL_MODELVIEW);
//**********************************
// we work on a copy of the current MODELVIEW matrix, hence we need to...
//**********************************
glPushMatrix();
//**********************************
// Rotate the robot around the x-axis and y-axis according to the relevant angles
//**********************************
glRotatef(RobotAngleX, 1, 0, 0);
glRotatef(RobotAngleY, 0, 1, 0);
// draw the robot
DrawRobot();
//**********************************
// not anymore on a local reference system
//**********************************
glPopMatrix();
// flush drawing routines to the window
glutSwapBuffers();
}
//*************************************************************************
// Special keys callback
//*************************************************************************
void arrows(int key, int x, int y)
{
//**********************************
// Manage the update of RobotAngleX and RobotAngleY with the arrow keys
//**********************************
switch (key)
{
case GLUT_KEY_LEFT:
RobotAngleY += 5;
break;
case GLUT_KEY_RIGHT:
RobotAngleY -= 5;
break;
case GLUT_KEY_UP:
RobotAngleX += 5;
break;
case GLUT_KEY_DOWN:
RobotAngleX -= 5;
break;
default:
break;
}
glutPostRedisplay();
}
//*************************************************************************
// Keyboard callback
//*************************************************************************
void keyboard(unsigned char key, int x, int y)
{
switch (key)
{
case 'q':
case 27:
exit(0);
break;
//**********************************
// Manage the update of Angle1 with the key 'a' and 'z'
//**********************************
case 'a':
Angle1 += 5;
break;
case 'z':
Angle1 -= 5;
break;
//**********************************
// Manage the update of Angle2 with the key 'e' and 'r'
//**********************************
case 'e':
Angle2 += 5;
break;
case 'r':
Angle2 -= 5;
break;
//**********************************
// Manage the pincer
//**********************************
case 'o':
pincer += 0.01;
break;
case 'l':
pincer -= 0.01;
break;
default:
break;
}
if (pincer >= 0.5)
{
pincer = 0.5;
}
else if (pincer <= 0.125)
{
pincer = 0.125;
}
glutPostRedisplay();
}
void init(void)
{
glClearColor(0.0, 0.0, 0.0, 0.0);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(65.0, 1.0, 1.0, 100.0);
glShadeModel(GL_FLAT);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
// Place the camera
gluLookAt(-6, 5, -6, 0, 0, 2, 0, 1, 0);
}
//*************************************************************************
// Function called every time the main window is resized
//*************************************************************************
void reshape(int width, int height)
{
// define the viewport transformation;
glViewport(0, 0, width, height);
if (width < height)
glViewport(0, (height - width) / 2, width, width);
else
glViewport((width - height) / 2, 0, height, height);
}
//*************************************************************************
// Prints out how to use the keyboard
//*************************************************************************
void usage()
{
printf("\n*******\n");
printf("Arrows key: rotate the whole robot\n");
printf("[a][z] : move the second joint of the arm\n");
printf("[e][r] : move the third joint of the arm\n");
printf("[esc] : terminate\n");
printf("*******\n");
}
//////////////////////////////////////////////////////////////
int main(int argc, char **argv)
{
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB);
glutInitWindowSize(500, 500);
glutInitWindowPosition(100, 100);
glutCreateWindow(argv[0]);
init();
glutDisplayFunc(display);
glutReshapeFunc(reshape);
//**********************************
// Register the keyboard function
//**********************************
glutKeyboardFunc(keyboard);
//**********************************
// Register the special key function
//**********************************
glutSpecialFunc(arrows);
// just print the help
usage();
glutMainLoop();
return EXIT_SUCCESS;
}
//////////////////////////////////////////////////////////////

33
TP4/CMakeLists.txt Normal file
View file

@ -0,0 +1,33 @@
cmake_minimum_required(VERSION 3.10)
project(tp3 VERSION 2021.0.0 LANGUAGES C CXX)
set(CMAKE_CXX_STANDARD 11)
set(OpenGL_GL_PREFERENCE "GLVND")
find_package(OpenGL REQUIRED)
message(STATUS "OPENGL_gl_LIBRARY: ${OPENGL_gl_LIBRARY}")
if(MSVC)
set(GLUT_ROOT_PATH "${CMAKE_SOURCE_DIR}/freeglut")
message(STATUS "GLUT_ROOT_PATH: ${GLUT_ROOT_PATH}")
endif()
find_package(GLUT REQUIRED)
message(STATUS "GLUT_FOUND: ${GLUT_FOUND}")
message(STATUS "GLUT_INCLUDE_DIR: ${GLUT_INCLUDE_DIR}")
message(STATUS "GLUT_LIBRARIES: ${GLUT_LIBRARIES}")
if(APPLE)
add_definitions(-Wno-deprecated-declarations)
endif()
add_executable(lumiere lumiere.c)
#target_include_directories(lumiere PUBLIC ${OPENGL_INCLUDE_DIR} ${GLUT_INCLUDE_DIR})
#target_link_libraries(lumiere PUBLIC ${OPENGL_opengl_LIBRARY} ${OPENGL_glu_LIBRARY} ${GLUT_LIBRARIES})
target_link_libraries(lumiere OpenGL::GL OpenGL::GLU GLUT::GLUT)
if (CMAKE_CXX_COMPILER_ID MATCHES "Clang|GNU")
target_compile_options(lumiere PRIVATE -Wall -Wextra -pedantic -Wno-comment)
endif()
if(MSVC)
target_compile_definitions(lumiere PUBLIC -DNOMINMAX)
endif()

170
TP4/README.md Normal file
View file

@ -0,0 +1,170 @@
# Introduction to OpenGL
- Building
* [Windows](#windows)
* [Linux](#linux)
* [MacOs](#macos)
---
## Windows
### Prerequisites
Not necessary if you already did the previous tp, but to recall:
* download and install the latest version of CMake
* download here: https://github.com/Kitware/CMake/releases/download/v3.17.1/cmake-3.17.1-win64-x64.msi
* !!! When installing make sure that the checkbox "ne pas ajouter cmake au PATH" is NOT checked
* if you don't have it already, download and install MS Visual Studio Community Edition (free for students): https://visualstudio.microsoft.com/downloads/
* install instructions here: https://docs.microsoft.com/en-us/cpp/build/vscpp-step-0-installation?view=vs-2019
* !!! install the "Desktop development with C++"
* If you have VS already installed, you can go in **Tools** --> **Get Tools and Features...** to install "Desktop development with C++" if it is missing.
### Create the Visual Studio Solution.
This step enables you to create the project file to load inside VS:
* unzip the code inside a folder. *Avoid to place the code in folders with spaces and accented characters*.
* open a Terminal and go to the directory containing the code.
* execute:
* `md build`
* `cd build`
* `cmake -G "Visual Studio 16 2019" -A x64 -DCMAKE_BUILD_TYPE:STRING=Release ..`
* `dir`
> if you had a different version of VS installed (not the latest) you may need to adapt the string `Visual Studio 16 2019` to your version: e.g. Visual Studio 15 2017, Visual Studio 14 2015, Visual Studio 12 2013
* if everything went well you should find a file named `tp3.sln` inside the directory.
### Compile, build, execute
* open `tp3.sln` inside VS either by double clicking on it or opening from inside VS
* build the solution (**Build Solution** from the **Build menu**)
* from the tp directory copy `freeglut\bin\x64\freeglut.dll` in `build\Release`
* execute the code:
* Select the project you want to run (e.g. `lumiere`), right click on it and select **Set as Startup Project**
* On the menu bar, choose **Debug** --> **Start without debugging**.)
(see https://docs.microsoft.com/en-us/cpp/build/vscpp-step-2-build?view=vs-2019 for how to build, execute, etc)
### Editing the code
Edit the code according to the assignments that are given, rebuild the solution and execute.
> !!! You need to run the cmake line only **once**
> !!! You need to copy the dll file only **once**.
---
## Linux
### Prerequisites
**Not necessary if you already did the previous tp.**
In order to develop with OpenGL some system packages are required (unless you are using the N7 machines):
```
sudo apt-get install libglu1-mesa-dev freeglut3-dev build-essential mesa-common-dev libxi-dev libxmu-dev automake
```
To build this code we use the CMake build system. You can install CMake from the system package manager but you need a recent version >= 3.10. Check the version that is provided by your linux distribution and if it is suitable usually you need to
```
sudo apt-get install cmake
```
otherwise you can install the binaries from here: https://github.com/Kitware/CMake/releases/download/v3.17.1/cmake-3.17.1-Linux-x86_64.sh
To install:
```
wget https://github.com/Kitware/CMake/releases/download/v3.17.1/cmake-3.17.1-Linux-x86_64.sh
chmod +x cmake-3.17.1-Linux-x86_64.sh
sudo cmake-3.17.1-Linux-x86_64.sh --prefix=/usr/local/ --skip-license
```
### Build
To compile and build the code you do
```
mkdir build && cd build
cmake ..
make lumiere
```
Also,
```
make all
```
builds everything, and
```
make clean
```
cleans everything.
Execute the code:
```
./lumiere
```
### Editing the code
Edit the code as required and then
```
make lumiere
```
> !!! the cmake line has to be run only **once**
---
## macOS
### Prerequisites
In order to develop with OpenGL check if
```
ls /System/Library/Frameworks/
```
contains OpenGL and GLUT frameworks.
If not you need to install XCode from the `Mac App Store`, see here for more details https://developer.apple.com/support/xcode/
Install the latest version of CMake by downloading https://github.com/Kitware/CMake/releases/download/v3.17.1/cmake-3.17.1-Darwin-x86_64.dmg
### Build
Same as Linux.
### Editing the code
Same as Linux.

31
TP4/appveyor.yml Normal file
View file

@ -0,0 +1,31 @@
version: '1.0.{build}'
image: Visual Studio 2019
platform:
- x64
configuration:
- Release
- Debug
#install:
# - vcpkg upgrade --no-dry-run
# - vcpkg install
# opengl
# --triplet %PLATFORM%-windows
before_build:
- md build
- cd build
# - cmake -G "Visual Studio 16 2019" -A x64 -T v140,host=x64 -DCMAKE_BUILD_TYPE=%configuration% -DCMAKE_TOOLCHAIN_FILE=c:/tools/vcpkg/scripts/buildsystems/vcpkg.cmake ..
- cmake -G "Visual Studio 16 2019" -A x64 -T v140,host=x64 -DCMAKE_BUILD_TYPE=%configuration% ..
- ls -l
build:
verbosity: detailed
project: $(APPVEYOR_BUILD_FOLDER)\build\tp3.sln
parallel: true
cache:
# - c:\tools\vcpkg\installed\

383
TP4/lumiere.c Normal file
View file

@ -0,0 +1,383 @@
/*
* lumiere.c
*
* OpenGL light
*/
#include <stdlib.h>
#include <stdio.h>
// for mac osx
#ifdef __APPLE__
#include <OpenGL/gl.h>
#include <OpenGL/glu.h>
#include <GLUT/glut.h>
#else
// only for windows
#ifdef _WIN32
#include <windows.h>
#endif
// for windows and linux
#include <GL/gl.h>
#include <GL/glu.h>
#include <GL/freeglut.h>
#endif
int angle_x = 45, angle_y = -45;
float distance = 8.f;
float shininess = 25.f;
int directional = 0;
int infinite_view = 0;
// the room: it's a cube with inverted normals, ie pointing inwards
void glRoom(GLfloat size)
{
GLfloat v = size / 2;
glBegin(GL_QUADS);
glNormal3f(0, 0, 1);
glVertex3f(-v, -v, -v);
glVertex3f(v, -v, -v);
glVertex3f(v, v, -v);
glVertex3f(-v, v, -v);
glNormal3f(0, 0, -1);
glVertex3f(v, -v, v);
glVertex3f(-v, -v, v);
glVertex3f(-v, v, v);
glVertex3f(v, v, v);
glNormal3f(-1, 0, 0);
glVertex3f(v, -v, -v);
glVertex3f(v, -v, v);
glVertex3f(v, v, v);
glVertex3f(v, v, -v);
glNormal3f(1, 0, 0);
glVertex3f(-v, -v, v);
glVertex3f(-v, -v, -v);
glVertex3f(-v, v, -v);
glVertex3f(-v, v, v);
glNormal3f(0, -1, 0);
glVertex3f(-v, v, -v);
glVertex3f(v, v, -v);
glVertex3f(v, v, v);
glVertex3f(-v, v, v);
glNormal3f(0, 1, 0);
glVertex3f(-v, -v, -v);
glVertex3f(-v, -v, v);
glVertex3f(v, -v, v);
glVertex3f(v, -v, -v);
glEnd();
}
// place the camera, make the scene turn around the scene origin
void place_camera()
{
glTranslatef(0.f, 0.f, -distance);
glRotatef(angle_x, 1.f, 0.f, 0.f);
glRotatef(angle_y, 0.f, 1.f, 0.f);
}
// place the light in x,y,z
void place_light(GLfloat x, GLfloat y, GLfloat z)
{
//**********************************
// set the light components: ambient (0.2 grey),
// diffuse and specular (both white)
//**********************************
GLfloat light_ambient[] = {0.2, 0.2, 0.2, 1.0}; // the ambient component
// GLfloat light_diffuse[] = {1.0, 1.0, 1.0, 1.0}; // the diffuse component
// GLfloat light_specular[] = {1.0, 1.0, 1.0, 1.0}; // the specular component
glLightfv(GL_LIGHT0, GL_AMBIENT, light_ambient);
// glLightfv(GL_LIGHT0, GL_DIFFUSE, light_diffuse);
// glLightfv(GL_LIGHT0, GL_SPECULAR, light_specular);
//**********************************
// set the light position (directional or positional)
//**********************************
GLfloat light_position[] = {x, y, z, directional}; // the light position
glLightfv(GL_LIGHT0, GL_POSITION, light_position);
//**********************************
// draw a yellow point to visually represent the light
//**********************************
glDisable(GL_LIGHT0);
glDisable(GL_LIGHTING);
glPushMatrix();
glColor3f(1, 1, 0);
glTranslatef(x, y, z);
glutSolidSphere(0.1, 100, 100); // a yellow sphere
glPopMatrix();
glEnable(GL_LIGHT0);
glEnable(GL_LIGHTING);
//**********************************
// turn the light and the lighting on
//**********************************
glEnable(GL_LIGHT0); // turn light on
glEnable(GL_LIGHTING); // turn lighting on
}
// define a material in terms of its components
void define_material(GLfloat ar, GLfloat ag, GLfloat ab, // ambient
GLfloat dr, GLfloat dg, GLfloat db, // diffuse
GLfloat sr, GLfloat sg, GLfloat sb, // specular
GLfloat sh // shininess
)
{
GLfloat mat_ambient[4];
GLfloat mat_diffuse[4];
GLfloat mat_specular[4];
//**********************************
// set the ambient property
//**********************************
mat_ambient[0] = ar;
mat_ambient[1] = ag;
mat_ambient[2] = ab;
mat_ambient[3] = 1.f;
glMaterialfv(GL_FRONT, GL_AMBIENT, mat_ambient);
//**********************************
// set the diffuse property
//**********************************
mat_diffuse[0] = dr;
mat_diffuse[1] = dg;
mat_diffuse[2] = db;
mat_diffuse[3] = 1.f;
glMaterialfv(GL_FRONT, GL_DIFFUSE, mat_diffuse);
//**********************************
// set the specular property
//**********************************
mat_specular[0] = sr;
mat_specular[1] = sg;
mat_specular[2] = sb;
mat_specular[3] = 1.f;
glMaterialfv(GL_FRONT, GL_SPECULAR, mat_specular);
//**********************************
// set the shininess property
//**********************************
glMaterialf(GL_FRONT, GL_SHININESS, sh);
}
// draw the room
void place_background()
{
glPushMatrix();
glScalef(15.f, 15.f, 15.f);
glRoom(1.f);
glPopMatrix();
}
/*
* OpenGL Initialization
*/
void init()
{
glClearColor(0.f, 0.f, 0.f, 0.f);
//**********************************
// activate the Gouraud shading instead of the flat one
//**********************************
glShadeModel(GL_FLAT);
//**********************************
// enable face culling
//**********************************
glEnable(GL_CULL_FACE);
//**********************************
// enable the depth test
//**********************************
glutInitDisplayMode(GLUT_RGB | GLUT_DEPTH);
glEnable(GL_DEPTH_TEST);
//**********************************
// set the ambient light with glLightModelfv to a 50% grey
//**********************************
GLfloat light_ambient[] = {0.5, 0.5, 0.5, 1.0};
glLightModelfv(GL_LIGHT_MODEL_AMBIENT, light_ambient);
glEnable(GL_NORMALIZE);
}
void display()
{
glLightModeli(GL_LIGHT_MODEL_LOCAL_VIEWER, infinite_view);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
// place the camera
place_camera();
//**********************************
// place the light in the scene using place_light
//**********************************
place_light(7.4, 4, -7.4);
//**********************************
// define the material for the room (instead of color)
//**********************************
// glColor3f(1.f, 1.f, 1.f);
define_material(1, 1, 1,
0.5, 0.5, 0.5,
0, 0, 0,
0);
place_background();
// the 2 objects
// red shining sphere
glPushMatrix();
glTranslatef(-2.f, 0.f, 0.f);
//**********************************
// define the material for the sphere (instead of color)
//**********************************
// glColor3f(1.f, 0.f, 0.f);
define_material(0.2, 0, 0,
0.8, 0, 0,
0.8, 0.8, 0.8,
shininess);
glutSolidSphere(1.0, 240, 120);
glPopMatrix();
// green cube
glPushMatrix();
glTranslatef(2.f, 0.f, 0.f);
//**********************************
// define the material for the cube (instead of color)
//**********************************
// glColor3f(0.f, 1.f, 0.f);
define_material(0, 0.8, 0,
0, 0.5, 0,
0, 0, 0,
0);
glutSolidCube(2.0);
glPopMatrix();
glutSwapBuffers();
}
/*
* @brief Callback for window size change
* @param[in] w new width of the window
* @param[in] h new height of the window
*/
void reshape(int w, int h)
{
glViewport(0, 0, (GLsizei)w, (GLsizei)h);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(60, (GLfloat)w / (GLfloat)h, 0.1, 50);
}
/*
* Callback for special keys
*/
#define DELTA_ANGLE_X 5
#define DELTA_ANGLE_Y 5
#define DELTA_DISTANCE 0.3f
#define DISTANCE_MIN 0.0
void special(int key, int x, int y)
{
switch (key)
{
case GLUT_KEY_UP:
angle_x = (angle_x + DELTA_ANGLE_X) % 360;
break;
case GLUT_KEY_DOWN:
angle_x = (angle_x - DELTA_ANGLE_X) % 360;
break;
case GLUT_KEY_LEFT:
angle_y = (angle_y + DELTA_ANGLE_Y) % 360;
break;
case GLUT_KEY_RIGHT:
angle_y = (angle_y - DELTA_ANGLE_Y) % 360;
break;
case GLUT_KEY_PAGE_DOWN:
distance += DELTA_DISTANCE;
break;
case GLUT_KEY_PAGE_UP:
distance -= (distance > DISTANCE_MIN) ? DELTA_DISTANCE : 0.f;
break;
default:
break;
}
glutPostRedisplay();
}
void keyboard(unsigned char key, int x, int y)
{
switch (key)
{
//**********************************
// directional light, use global variable directional
//**********************************
case 'd':
directional = (directional + 1) % 2;
break;
//**********************************
// infinite_view
//**********************************
case 'f':
infinite_view = (infinite_view + 1) % 2;
break;
//**********************************
// Shininess: 's' to decrement, 'S' to increment
//**********************************
case 'S':
shininess += 1;
break;
case 's':
shininess -= 1;
break;
case 'q':
case 27: // [ESC]
exit(0);
break;
default:
break;
}
glutPostRedisplay();
}
int main(int argc, char **argv)
{
glutInit(&argc, argv);
// enable the double buffer and the depth buffer
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
// main window
glutInitWindowSize(640, 480);
glutInitWindowPosition(50, 50);
glutCreateWindow("Testing OpenGL light");
// callbacks
glutDisplayFunc(display);
glutReshapeFunc(reshape);
glutKeyboardFunc(keyboard);
glutSpecialFunc(special);
init();
glutMainLoop();
exit(0);
}

12
TP5/.editorconfig Normal file
View file

@ -0,0 +1,12 @@
# EditorConfig is awesome: https://EditorConfig.org
# top-most EditorConfig file
root = true
[*]
indent_style = space
indent_size = 4
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = false
insert_final_newline = false

2
TP5/.gitignore vendored Normal file
View file

@ -0,0 +1,2 @@
build
*DS_Store

7
TP5/.vscode/settings.json vendored Normal file
View file

@ -0,0 +1,7 @@
{
"files.associations": {
"*.html": "html",
"*.toml": "toml",
"iosfwd": "cpp"
}
}

185
TP5/BUILD.md Executable file
View file

@ -0,0 +1,185 @@
# OpenGL OBJ Visualizer
## Building instructions
Required tools:
* C/C++ 11 compiler (gcc or visual studio or clang)
* cmake
### Dependencies
The project depends on:
- OpenGL
- freeglut (included for ease in windows)
## Windows
### Prerequisites
* download and install the latest version of CMake
* download here: https://github.com/Kitware/CMake/releases/download/v3.17.1/cmake-3.17.1-win64-x64.msi
* !!! When installing make sure that the checkbox "ne pas ajouter cmake au PATH" is NOT checked
* if you don't have it already, download and install MS Visual Studio Community Edition (free for students): https://visualstudio.microsoft.com/downloads/
* install instructions here: https://docs.microsoft.com/en-us/cpp/build/vscpp-step-0-installation?view=vs-2019
* !!! install the "Desktop development with C++"
* If you have VS already installed, you can go in **Tools** --> **Get Tools and Features...** to install "Desktop development with C++" if it is missing.
### Create the Visual Studio Solution.
This step enables you to create the project file to load inside VS:
* unzip the code inside a folder. *Avoid to place the code in folders with spaces and accented characters*.
* open a Terminal and go to the directory containing the code.
* execute:
* `md build`
* `cd build`
* `cmake -G "Visual Studio 16 2019" -A x64 -DCMAKE_BUILD_TYPE:STRING=Release ..`
* `dir`
> if you had a different version of VS installed (not the latest) you may need to adapt the string `Visual Studio 16 2019` to your version: e.g. Visual Studio 15 2017, Visual Studio 14 2015, Visual Studio 12 2013
* if everything went well you should find a file named `tp4.sln` inside the directory.
### Compile, build, execute
* open `tp4.sln` inside VS either by double clicking on it or opening from inside VS
* build the solution (**Build Solution** from the **Build menu**)
* from the tp directory copy `freeglut\bin\x64\freeglut.dll` in `build\Release`
* execute the code:
* From VS:
* Select the project you want to run (e.g. `visualizer`), right click on it and select **Set as Startup Project**
* From **Debug > visualizer Properties... > Configuration Porpoerties > Command Arguments**, insert the full path of the obj file that you want to test
(In French: **Déboguer > Propriétés de visualizer > Débogage > Arguments de la commande**)
* On the menu bar, choose **Debug** --> **Start without debugging**.)
(see https://docs.microsoft.com/en-us/cpp/build/vscpp-step-2-build?view=vs-2019 for how to build, execute, etc)
* From the command line:
* Open a Terminal and go to `build/Release`
* launch `visualizer ../../data/models/cube.obj` (the argument being the model to load)
### Editing the code
Edit the code according to the assignments that are given, rebuild the solution and execute.
> !!! You need to run the cmake line only **once**
> !!! You need to copy the dll file only **once**.
---
## Linux
### Prerequisites
In order to develop with OpenGL some system packages are required (unless you are using the N7 machines):
```
sudo apt-get install libglu1-mesa-dev freeglut3-dev build-essential mesa-common-dev libxi-dev libxmu-dev automake
```
To build this code we use the CMake build system. You can install CMake from the system package manager but you need a recent version >= 3.10. Check the version that is provided by your linux distribution and if it is suitable usually you need to
```
sudo apt-get install cmake
```
otherwise you can install the binaries from here: https://github.com/Kitware/CMake/releases/download/v3.17.1/cmake-3.17.1-Linux-x86_64.sh
To install:
```
wget https://github.com/Kitware/CMake/releases/download/v3.17.1/cmake-3.17.1-Linux-x86_64.sh
chmod +x cmake-3.17.1-Linux-x86_64.sh
sudo cmake-3.17.1-Linux-x86_64.sh --prefix=/usr/local/ --skip-license
```
### Build
To compile and build the code you do
```
mkdir build && cd build
cmake ..
make visualizer
```
Also,
```
make all
```
builds everything, and
```
make clean
```
cleans everything.
Execute the code:
```
./visualizer ../data/models/cube.obj
```
### Editing the code
Edit the code as required and then
```
make
```
> !!! the cmake line has to be run only **once**
---
## macOS
### Prerequisites
In order to develop with OpenGL check if
```
ls /System/Library/Frameworks/
```
contains OpenGL and GLUT frameworks.
If not you need to install XCode from the `Mac App Store`, see here for more details https://developer.apple.com/support/xcode/
Install the latest version of CMake by downloading https://github.com/Kitware/CMake/releases/download/v3.17.1/cmake-3.17.1-Darwin-x86_64.dmg
### Build
Same as Linux.
### Editing the code
Same as Linux.

82
TP5/CMakeLists.txt Executable file
View file

@ -0,0 +1,82 @@
cmake_minimum_required(VERSION 3.1)
project( tp4 LANGUAGES CXX VERSION 2021.0.0)
option(BUILD_TESTS "Build tests" OFF)
#########################################################
#
# EXTERNAL LIBRARIES
#
#########################################################
#########################################################
# FIND OPENGL
#########################################################
set(OpenGL_GL_PREFERENCE "GLVND")
find_package(OpenGL REQUIRED)
message(STATUS "OPENGL_gl_LIBRARY: ${OPENGL_gl_LIBRARY}")
#########################################################
# FIND GLUT
#########################################################
if(MSVC)
set(GLUT_ROOT_PATH "${CMAKE_SOURCE_DIR}/freeglut")
message(STATUS "GLUT_ROOT_PATH: ${GLUT_ROOT_PATH}")
endif()
find_package(GLUT REQUIRED)
message(STATUS "GLUT_FOUND: ${GLUT_FOUND}")
message(STATUS "GLUT_INCLUDE_DIR: ${GLUT_INCLUDE_DIR}")
message(STATUS "GLUT_LIBRARIES: ${GLUT_LIBRARIES}")
if(CMAKE_SYSTEM_NAME STREQUAL Linux)
#########################################################
# FIND Threads, not used but necessary for linkning on linux
# funny story, even if it is not used it is needed by other
# dependencies which do not propagate their dependencies (?)
#########################################################
find_package(Threads REQUIRED)
# this force all libs to be include even if not directly used
set(CMAKE_EXE_LINKER_FLAGS "-Wl,--no-as-needed")
endif()
#########################################################
# SET COMPILATION FLAGS FOR C++11
#########################################################
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
add_executable( visualizer main.cpp ObjModel.cpp core.cpp)
#target_include_directories(visualizer PUBLIC ${OPENGL_INCLUDE_DIR} ${GLUT_INCLUDE_DIR})
#target_link_libraries( visualizer ${OPENGL_opengl_LIBRARY} ${OPENGL_glu_LIBRARY} ${GLUT_LIBRARIES} )
target_link_libraries( visualizer OpenGL::GL OpenGL::GLU GLUT::GLUT )
if(CMAKE_SYSTEM_NAME STREQUAL Linux)
target_link_libraries( visualizer ${CMAKE_THREAD_LIBS_INIT} )
endif()
if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang")
target_compile_options(visualizer PRIVATE -Wno-deprecated-declarations)
endif()
if (CMAKE_CXX_COMPILER_ID MATCHES "Clang|GNU")
target_compile_options(visualizer PRIVATE -Wall -Wextra -pedantic -Wno-comment)
endif()
if(MSVC)
target_compile_definitions(visualizer PUBLIC -DNOMINMAX)
endif()
if(BUILD_TESTS)
add_executable( testEdge testEdge.cpp core.cpp)
target_include_directories(testEdge PUBLIC ${OPENGL_INCLUDE_DIR} ${GLUT_INCLUDE_DIR})
target_link_libraries( testEdge ${OPENGL_opengl_LIBRARY} ${OPENGL_glu_LIBRARY} ${GLUT_LIBRARIES} )
add_executable( testEdgeList testEdgeList.cpp core.cpp)
target_include_directories(testEdgeList PUBLIC ${OPENGL_INCLUDE_DIR} ${GLUT_INCLUDE_DIR})
target_link_libraries( testEdgeList ${OPENGL_opengl_LIBRARY} ${OPENGL_glu_LIBRARY} ${GLUT_LIBRARIES} )
if(CMAKE_SYSTEM_NAME STREQUAL Linux)
target_link_libraries( testEdge ${CMAKE_THREAD_LIBS_INIT} )
target_link_libraries( testEdgeList ${CMAKE_THREAD_LIBS_INIT} )
endif()
enable_testing()
add_test( testEdgeList bin/testEdgeList 1000 )
endif()

362
TP5/LICENSE Executable file
View file

@ -0,0 +1,362 @@
Mozilla Public License, version 2.0
1. Definitions
1.1. "Contributor"
means each individual or legal entity that creates, contributes to the
creation of, or owns Covered Software.
1.2. "Contributor Version"
means the combination of the Contributions of others (if any) used by a
Contributor and that particular Contributor's Contribution.
1.3. "Contribution"
means Covered Software of a particular Contributor.
1.4. "Covered Software"
means Source Code Form to which the initial Contributor has attached the
notice in Exhibit A, the Executable Form of such Source Code Form, and
Modifications of such Source Code Form, in each case including portions
thereof.
1.5. "Incompatible With Secondary Licenses"
means
a. that the initial Contributor has attached the notice described in
Exhibit B to the Covered Software; or
b. that the Covered Software was made available under the terms of
version 1.1 or earlier of the License, but not also under the terms of
a Secondary License.
1.6. "Executable Form"
means any form of the work other than Source Code Form.
1.7. "Larger Work"
means a work that combines Covered Software with other material, in a
separate file or files, that is not Covered Software.
1.8. "License"
means this document.
1.9. "Licensable"
means having the right to grant, to the maximum extent possible, whether
at the time of the initial grant or subsequently, any and all of the
rights conveyed by this License.
1.10. "Modifications"
means any of the following:
a. any file in Source Code Form that results from an addition to,
deletion from, or modification of the contents of Covered Software; or
b. any new file in Source Code Form that contains any Covered Software.
1.11. "Patent Claims" of a Contributor
means any patent claim(s), including without limitation, method,
process, and apparatus claims, in any patent Licensable by such
Contributor that would be infringed, but for the grant of the License,
by the making, using, selling, offering for sale, having made, import,
or transfer of either its Contributions or its Contributor Version.
1.12. "Secondary License"
means either the GNU General Public License, Version 2.0, the GNU Lesser
General Public License, Version 2.1, the GNU Affero General Public
License, Version 3.0, or any later versions of those licenses.
1.13. "Source Code Form"
means the form of the work preferred for making modifications.
1.14. "You" (or "Your")
means an individual or a legal entity exercising rights under this
License. For legal entities, "You" includes any entity that controls, is
controlled by, or is under common control with You. For purposes of this
definition, "control" means (a) the power, direct or indirect, to cause
the direction or management of such entity, whether by contract or
otherwise, or (b) ownership of more than fifty percent (50%) of the
outstanding shares or beneficial ownership of such entity.
2. License Grants and Conditions
2.1. Grants
Each Contributor hereby grants You a world-wide, royalty-free,
non-exclusive license:
a. under intellectual property rights (other than patent or trademark)
Licensable by such Contributor to use, reproduce, make available,
modify, display, perform, distribute, and otherwise exploit its
Contributions, either on an unmodified basis, with Modifications, or
as part of a Larger Work; and
b. under Patent Claims of such Contributor to make, use, sell, offer for
sale, have made, import, and otherwise transfer either its
Contributions or its Contributor Version.
2.2. Effective Date
The licenses granted in Section 2.1 with respect to any Contribution
become effective for each Contribution on the date the Contributor first
distributes such Contribution.
2.3. Limitations on Grant Scope
The licenses granted in this Section 2 are the only rights granted under
this License. No additional rights or licenses will be implied from the
distribution or licensing of Covered Software under this License.
Notwithstanding Section 2.1(b) above, no patent license is granted by a
Contributor:
a. for any code that a Contributor has removed from Covered Software; or
b. for infringements caused by: (i) Your and any other third party's
modifications of Covered Software, or (ii) the combination of its
Contributions with other software (except as part of its Contributor
Version); or
c. under Patent Claims infringed by Covered Software in the absence of
its Contributions.
This License does not grant any rights in the trademarks, service marks,
or logos of any Contributor (except as may be necessary to comply with
the notice requirements in Section 3.4).
2.4. Subsequent Licenses
No Contributor makes additional grants as a result of Your choice to
distribute the Covered Software under a subsequent version of this
License (see Section 10.2) or under the terms of a Secondary License (if
permitted under the terms of Section 3.3).
2.5. Representation
Each Contributor represents that the Contributor believes its
Contributions are its original creation(s) or it has sufficient rights to
grant the rights to its Contributions conveyed by this License.
2.6. Fair Use
This License is not intended to limit any rights You have under
applicable copyright doctrines of fair use, fair dealing, or other
equivalents.
2.7. Conditions
Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted in
Section 2.1.
3. Responsibilities
3.1. Distribution of Source Form
All distribution of Covered Software in Source Code Form, including any
Modifications that You create or to which You contribute, must be under
the terms of this License. You must inform recipients that the Source
Code Form of the Covered Software is governed by the terms of this
License, and how they can obtain a copy of this License. You may not
attempt to alter or restrict the recipients' rights in the Source Code
Form.
3.2. Distribution of Executable Form
If You distribute Covered Software in Executable Form then:
a. such Covered Software must also be made available in Source Code Form,
as described in Section 3.1, and You must inform recipients of the
Executable Form how they can obtain a copy of such Source Code Form by
reasonable means in a timely manner, at a charge no more than the cost
of distribution to the recipient; and
b. You may distribute such Executable Form under the terms of this
License, or sublicense it under different terms, provided that the
license for the Executable Form does not attempt to limit or alter the
recipients' rights in the Source Code Form under this License.
3.3. Distribution of a Larger Work
You may create and distribute a Larger Work under terms of Your choice,
provided that You also comply with the requirements of this License for
the Covered Software. If the Larger Work is a combination of Covered
Software with a work governed by one or more Secondary Licenses, and the
Covered Software is not Incompatible With Secondary Licenses, this
License permits You to additionally distribute such Covered Software
under the terms of such Secondary License(s), so that the recipient of
the Larger Work may, at their option, further distribute the Covered
Software under the terms of either this License or such Secondary
License(s).
3.4. Notices
You may not remove or alter the substance of any license notices
(including copyright notices, patent notices, disclaimers of warranty, or
limitations of liability) contained within the Source Code Form of the
Covered Software, except that You may alter any license notices to the
extent required to remedy known factual inaccuracies.
3.5. Application of Additional Terms
You may choose to offer, and to charge a fee for, warranty, support,
indemnity or liability obligations to one or more recipients of Covered
Software. However, You may do so only on Your own behalf, and not on
behalf of any Contributor. You must make it absolutely clear that any
such warranty, support, indemnity, or liability obligation is offered by
You alone, and You hereby agree to indemnify every Contributor for any
liability incurred by such Contributor as a result of warranty, support,
indemnity or liability terms You offer. You may include additional
disclaimers of warranty and limitations of liability specific to any
jurisdiction.
4. Inability to Comply Due to Statute or Regulation
If it is impossible for You to comply with any of the terms of this License
with respect to some or all of the Covered Software due to statute,
judicial order, or regulation then You must: (a) comply with the terms of
this License to the maximum extent possible; and (b) describe the
limitations and the code they affect. Such description must be placed in a
text file included with all distributions of the Covered Software under
this License. Except to the extent prohibited by statute or regulation,
such description must be sufficiently detailed for a recipient of ordinary
skill to be able to understand it.
5. Termination
5.1. The rights granted under this License will terminate automatically if You
fail to comply with any of its terms. However, if You become compliant,
then the rights granted under this License from a particular Contributor
are reinstated (a) provisionally, unless and until such Contributor
explicitly and finally terminates Your grants, and (b) on an ongoing
basis, if such Contributor fails to notify You of the non-compliance by
some reasonable means prior to 60 days after You have come back into
compliance. Moreover, Your grants from a particular Contributor are
reinstated on an ongoing basis if such Contributor notifies You of the
non-compliance by some reasonable means, this is the first time You have
received notice of non-compliance with this License from such
Contributor, and You become compliant prior to 30 days after Your receipt
of the notice.
5.2. If You initiate litigation against any entity by asserting a patent
infringement claim (excluding declaratory judgment actions,
counter-claims, and cross-claims) alleging that a Contributor Version
directly or indirectly infringes any patent, then the rights granted to
You by any and all Contributors for the Covered Software under Section
2.1 of this License shall terminate.
5.3. In the event of termination under Sections 5.1 or 5.2 above, all end user
license agreements (excluding distributors and resellers) which have been
validly granted by You or Your distributors under this License prior to
termination shall survive termination.
6. Disclaimer of Warranty
Covered Software is provided under this License on an "as is" basis,
without warranty of any kind, either expressed, implied, or statutory,
including, without limitation, warranties that the Covered Software is free
of defects, merchantable, fit for a particular purpose or non-infringing.
The entire risk as to the quality and performance of the Covered Software
is with You. Should any Covered Software prove defective in any respect,
You (not any Contributor) assume the cost of any necessary servicing,
repair, or correction. This disclaimer of warranty constitutes an essential
part of this License. No use of any Covered Software is authorized under
this License except under this disclaimer.
7. Limitation of Liability
Under no circumstances and under no legal theory, whether tort (including
negligence), contract, or otherwise, shall any Contributor, or anyone who
distributes Covered Software as permitted above, be liable to You for any
direct, indirect, special, incidental, or consequential damages of any
character including, without limitation, damages for lost profits, loss of
goodwill, work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses, even if such party shall have been
informed of the possibility of such damages. This limitation of liability
shall not apply to liability for death or personal injury resulting from
such party's negligence to the extent applicable law prohibits such
limitation. Some jurisdictions do not allow the exclusion or limitation of
incidental or consequential damages, so this exclusion and limitation may
not apply to You.
8. Litigation
Any litigation relating to this License may be brought only in the courts
of a jurisdiction where the defendant maintains its principal place of
business and such litigation shall be governed by laws of that
jurisdiction, without reference to its conflict-of-law provisions. Nothing
in this Section shall prevent a party's ability to bring cross-claims or
counter-claims.
9. Miscellaneous
This License represents the complete agreement concerning the subject
matter hereof. If any provision of this License is held to be
unenforceable, such provision shall be reformed only to the extent
necessary to make it enforceable. Any law or regulation which provides that
the language of a contract shall be construed against the drafter shall not
be used to construe this License against a Contributor.
10. Versions of the License
10.1. New Versions
Mozilla Foundation is the license steward. Except as provided in Section
10.3, no one other than the license steward has the right to modify or
publish new versions of this License. Each version will be given a
distinguishing version number.
10.2. Effect of New Versions
You may distribute the Covered Software under the terms of the version
of the License under which You originally received the Covered Software,
or under the terms of any subsequent version published by the license
steward.
10.3. Modified Versions
If you create software not governed by this License, and you want to
create a new license for such software, you may create and use a
modified version of this License if you rename the license and remove
any references to the name of the license steward (except to note that
such modified license differs from this License).
10.4. Distributing Source Code Form that is Incompatible With Secondary
Licenses If You choose to distribute Source Code Form that is
Incompatible With Secondary Licenses under the terms of this version of
the License, the notice described in Exhibit B of this License must be
attached.
Exhibit A - Source Code Form License Notice
This Source Code Form is subject to the
terms of the Mozilla Public License, v.
2.0. If a copy of the MPL was not
distributed with this file, You can
obtain one at
http://mozilla.org/MPL/2.0/.
If it is not possible or desirable to put the notice in a particular file,
then You may include the notice in a location (such as a LICENSE file in a
relevant directory) where a recipient would be likely to look for such a
notice.
You may add additional accurate notices of copyright ownership.
Exhibit B - "Incompatible With Secondary Licenses" Notice
This Source Code Form is "Incompatible
With Secondary Licenses", as defined by
the Mozilla Public License, v. 2.0.

19
TP5/Makefile Executable file
View file

@ -0,0 +1,19 @@
CC = g++
CXXFLAGS = -Wall -O3 -std=c++11 -Wno-comment
UNAME_S := $(shell uname -s)
ifeq ($(UNAME_S),Darwin)
LIBS := -framework OpenGL -lglut -lm
CXXFLAGS += -Wno-deprecated
else
LIBS = -Wl,--no-as-needed -lpthread -lGL -lglut -lm -lGLU -pthread
endif
all: visualizer
# solutions
visualizer: main.o ObjModel.o core.o
$(CC) $(CXXFLAGS) -o $@ $^ $(LIBS)
clean:
rm -f *.o visualizer

563
TP5/ObjModel.cpp Executable file
View file

@ -0,0 +1,563 @@
/**
* @file ObjModel.cpp
* @author Simone Gasparini <simone.gasparini@enseeiht.fr>
* @version 1.0
*
* @section LICENSE
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* @section DESCRIPTION
*
* Simple Class to load and draw 3D objects from OBJ files
* Using triangles and normals as static object. No texture mapping.
* OBJ files must be triangulated!!!
*
*/
#include "ObjModel.hpp"
#include <iostream>
#include <fstream>
#include <cstdio>
#include <cstring>
#include <cassert>
//#include <stdlib.h>
#include <sstream>
#include <string>
#include <vector>
#include <cmath>
using namespace std;
/**
* Load the OBJ data from file
* @param filename The name of the OBJ file
* @return 0 if everything went well
*/
int ObjModel::load(char *filename)
{
string line;
ifstream objFile(filename);
// ifstream objFile("../data/models/cube.obj");
// ifstream objFile("../data/models/cow-nonormals.obj");
// ifstream objFile("../data/models/triang.obj");
// If obj file is open, continue
if (objFile.is_open())
{
// Start reading file data
while (!objFile.eof())
{
// Get a line from file
getline(objFile, line);
// If the first character is a simple 'v'...
// PRINTVAR(line);
if ((line.c_str()[0] == 'v') && (line.c_str()[1] == ' ')) // to drop all the vn and vn lines
{
// PRINTVAR(line);
// Read 3 floats from the line: X Y Z and store them in the corresponding place in _vertices
point3d p;
sscanf(line.c_str(), "v %f %f %f ", &p.x, &p.y, &p.z);
//**************************************************
// add the new point to the list of the vertices
// and its normal to the list of normals: for the time
// being it is a [0, 0 ,0] normal.
//**************************************************
_vertices.push_back(p);
_normals.push_back(vec3d(0, 0, 0));
// update the bounding box, if it is the first vertex simply
// set the bb to it
if (_vertices.size() == 1)
{
_bb.set(p);
}
else
{
// otherwise add the point
_bb.add(p);
}
}
// If the first character is a 'f'...
if (line.c_str()[0] == 'f')
{
face t;
const auto ok = parseFaceString(line, t);
if (!ok)
throw std::runtime_error("Error while reading line: " + line);
//**************************************************
// correct the indices: OBJ starts counting from 1, in C the arrays starts at 0...
//**************************************************
t.v1--;
t.v2--;
t.v3--;
//**************************************************
// add it to the mesh
//**************************************************
_mesh.push_back(t);
//*********************************************************************
// Compute the normal of the face
//*********************************************************************
vec3d normal;
computeNormal(_vertices[t.v1], _vertices[t.v2], _vertices[t.v3], normal);
//*********************************************************************
// Sum the normal of the face to each vertex normal
//*********************************************************************
_normals[t.v1] += normal * angleAtVertex(_vertices[t.v1], _vertices[t.v2], _vertices[t.v3]);
_normals[t.v2] += normal * angleAtVertex(_vertices[t.v2], _vertices[t.v1], _vertices[t.v3]);
_normals[t.v3] += normal * angleAtVertex(_vertices[t.v3], _vertices[t.v1], _vertices[t.v2]);
}
}
cerr << "Found :\n\tNumber of triangles (_indices) " << _mesh.size() << "\n\tNumber of Vertices: " << _vertices.size() << "\n\tNumber of Normals: " << _normals.size() << endl;
// PRINTVAR(_mesh);
// PRINTVAR(_vertices);
// PRINTVAR(_normals);
//*********************************************************************
// normalize the normals of each vertex
//*********************************************************************
for (vec3d n : _normals)
{
n.normalize();
}
// PRINTVAR(_normals);
// Close OBJ file
objFile.close();
}
else
{
cout << "Unable to open file";
}
cout << "Object loaded with " << _vertices.size() << " vertices and " << _mesh.size() << " faces" << endl;
cout << "Bounding box : pmax=" << _bb.pmax << " pmin=" << _bb.pmin << endl;
return 0;
}
/**
* Draw the wireframe of the model
*
* @param vertices The list of vertices
* @param mesh The mesh as a list of faces, each face is a tripleIndex of vertex indices
* @param params The rendering parameters
*/
void ObjModel::drawWireframe(const std::vector<point3d> &vertices, const std::vector<face> &mesh, const RenderingParameters &params) const
{
//**************************************************
// we first need to disable the lighting in order to
// draw colored segments
//**************************************************
glDisable(GL_LIGHTING);
// if we are displaying the object with colored faces
if (params.solid)
{
// use black ticker lines
glColor3f(0, 0, 0);
glLineWidth(2);
}
else
{
// otherwise use white thinner lines for wireframe only
glColor3f(.8, .8, .8);
glLineWidth(.21);
}
//**************************************************
// for each face of the mesh...
//**************************************************
for (auto &face : mesh)
{
//**************************************************
// draw the contour of the face as a GL_LINE_LOOP
//**************************************************
glBegin(GL_LINE_LOOP);
glVertex3fv((float *)&vertices[face.v1]);
glVertex3fv((float *)&vertices[face.v2]);
glVertex3fv((float *)&vertices[face.v3]);
glEnd();
}
//**************************************************
// re-enable the lighting
//**************************************************
glEnable(GL_LIGHTING);
}
/**
* Calculate the normal of a triangular face defined by three points
*
* @param[in] v1 the first vertex
* @param[in] v2 the second vertex
* @param[in] v3 the third vertex
* @param[out] norm the normal
*/
void ObjModel::computeNormal(const point3d &v1, const point3d &v2, const point3d &v3, vec3d &norm) const
{
//**************************************************
// compute the cross product between two edges of the triangular face
//**************************************************
norm = (v2 - v1).cross(v3 - v1);
//**************************************************
// remember to normalize the result
//**************************************************
norm.normalize();
}
/**
* Draw the faces using the computed normal of each face
*
* @param vertices The list of vertices
* @param mesh The list of face, each face containing the indices of the vertices
* @param params The rendering parameters
*/
void ObjModel::drawFlatFaces(const std::vector<point3d> &vertices, const std::vector<face> &mesh, const RenderingParameters &params) const
{
// shading model to use
if (params.smooth)
{
glShadeModel(GL_SMOOTH);
}
else
{
glShadeModel(GL_FLAT);
}
//**************************************************
// for each face
//**************************************************
for (auto &face : mesh)
{
//**************************************************
// Compute the normal to the face and then draw the
// faces as GL_TRIANGLES assigning the proper normal
//**************************************************
vec3d normal;
computeNormal(vertices[face.v1], vertices[face.v2], vertices[face.v3], normal);
glNormal3fv((float *)&normal);
glBegin(GL_TRIANGLES);
glVertex3fv((float *)&vertices[face.v1]);
glVertex3fv((float *)&vertices[face.v2]);
glVertex3fv((float *)&vertices[face.v3]);
glEnd();
}
}
/**
* Draw the model using the vertex indices
*
* @param vertices The vertices
* @param indices The list of the faces, each face containing the 3 indices of the vertices
* @param vertexNormals The list of normals associated to each vertex
* @param params The rendering parameters
*/
void ObjModel::drawSmoothFaces(const std::vector<point3d> &vertices,
const std::vector<face> &mesh,
std::vector<vec3d> &vertexNormals,
const RenderingParameters &params) const
{
if (params.smooth)
{
glShadeModel(GL_SMOOTH);
}
else
{
glShadeModel(GL_FLAT);
}
//****************************************
// Enable vertex arrays
//****************************************
glEnableClientState(GL_VERTEX_ARRAY);
//****************************************
// Enable normal arrays
//****************************************
glEnableClientState(GL_NORMAL_ARRAY);
//****************************************
// Normal pointer to normal array
//****************************************
glNormalPointer(GL_FLOAT, 0, &vertexNormals[0]);
//****************************************
// Vertex pointer to Vertex array
//****************************************
glVertexPointer(3, GL_FLOAT, 0, &vertices[0]);
//****************************************
// Draw the faces
//****************************************
glDrawElements(GL_TRIANGLES, 3 * mesh.size(), GL_UNSIGNED_INT, &mesh[0]);
//****************************************
// Disable vertex arrays
//****************************************
glDisableClientState(GL_VERTEX_ARRAY);
//****************************************
// Disable normal arrays
//****************************************
glDisableClientState(GL_NORMAL_ARRAY);
}
/**
* Compute the subdivision of the input mesh by applying one step of the Loop algorithm
*
* @param[in] origVert The list of the input vertices
* @param[in] origMesh The input mesh (the vertex indices for each face/triangle)
* @param[out] destVert The list of the new vertices for the subdivided mesh
* @param[out] destMesh The new subdivided mesh (the vertex indices for each face/triangle)
* @param[out] destNorm The new list of normals for each new vertex of the subdivided mesh
*/
void ObjModel::loopSubdivision(const std::vector<point3d> &origVert, //!< the original vertices
const std::vector<face> &origMesh, //!< the original mesh
std::vector<point3d> &destVert, //!< the new vertices
std::vector<face> &destMesh, //!< the new mesh
std::vector<vec3d> &destNorm) const //!< the new normals
{
// copy the original vertices in destVert
destVert = origVert;
// start fresh with the new mesh
destMesh.clear();
// PRINTVAR(destVert);
// PRINTVAR(origVert);
// create a list of the new vertices creates with the reference to the edge
EdgeList newVertices;
//*********************************************************************
// for each face
//*********************************************************************
for (auto &f : origMesh)
{
//*********************************************************************
// get the indices of the triangle vertices
//*********************************************************************
uint v1 = f.v1;
uint v2 = f.v2;
uint v3 = f.v3;
//*********************************************************************
// for each edge get the index of the vertex of the midpoint using getNewVertex
//*********************************************************************
const uint a = getNewVertex(edge{v1, v2}, destVert, origMesh, newVertices);
const uint b = getNewVertex(edge{v2, v3}, destVert, origMesh, newVertices);
const uint c = getNewVertex(edge{v3, v1}, destVert, origMesh, newVertices);
//*********************************************************************
// create the four new triangles
// BE CAREFUL WITH THE VERTEX ORDER!!
// v2
// /\
// / \
// / \
// a ---- b
// / \ /\
// / \ / \
// / \ / \
// v1 ---- c ---- v3
//
// the original triangle was v1-v2-v3, use the same clock-wise order for the other
// hence v1-a-c, a-b-c and so on
//*********************************************************************
destMesh.push_back(face{v1, a, c});
destMesh.push_back(face{a, b, c});
destMesh.push_back(face{c, b, v3});
destMesh.push_back(face{a, v2, b});
}
//*********************************************************************
// Update each "old" vertex using the Loop coefficients. A smart way to do
// so is to think in terms of faces than the single vertex: for each face
// we update each of the 3 vertices using the Loop formula wrt the other 2 and
// sum it to a temporary copy tmp of the vertices (which is initialized to
// [0 0 0] at the beginning). We also keep a record of the occurrence of each vertex.
// At then end, to get the final vertices we just need to divide each vertex
// in tmp by its occurrence
//*********************************************************************
// A list containing the occurrence of each vertex
vector<size_t> occurrences(origVert.size(), 0.0f);
// A list of the same size as origVert with all the elements initialized to [0 0 0]
vector<point3d> tmp(origVert.size());
//*********************************************************************
// for each face
//*********************************************************************
for (auto &face : origMesh)
{
//*********************************************************************
// consider each of the 3 vertices:
// 1) increment its occurrence
// 2) apply Loop update with the other 2 vertices of the face
// BE CAREFUL WITH THE COEFFICIENT OF THE OTHER 2 VERTICES!... consider
// how many times each vertex is summed in the general case...
//*********************************************************************
// 1) increment occurrences
occurrences[face.v1] += 2;
occurrences[face.v2] += 2;
occurrences[face.v3] += 2;
// 2) apply Loop update
tmp[face.v1] += (destVert[face.v2] + destVert[face.v3]);
tmp[face.v2] += (destVert[face.v1] + destVert[face.v3]);
tmp[face.v3] += (destVert[face.v1] + destVert[face.v2]);
}
//*********************************************************************
// To obtain the new vertices, divide each vertex by its occurrence value
//*********************************************************************
for (int i = 0; i < tmp.size(); i++)
{
assert(occurrences[i] != 0);
destVert[i] = 5.0f / 8.0f * destVert[i] + 3.0f / 8.0f / occurrences[i] * tmp[i];
}
// PRINTVAR(destVert);
// redo the normals, reset and create a list of normals of the same size as
// the vertices, each normal set to [0 0 0]
destNorm.clear();
destNorm = vector<vec3d>(destVert.size());
//*********************************************************************
// Recompute the normals for each face
//*********************************************************************
for (auto &f : destMesh)
{
//*********************************************************************
// Calculate the normal of the triangles, it will be the same for each vertex
//*********************************************************************
vec3d n;
computeNormal(destVert[f.v1], destVert[f.v2], destVert[f.v3], n);
//*********************************************************************
// Sum the normal of the face to each vertex normal using the angleAtVertex as weight
//*********************************************************************
destNorm[f.v1] += n * angleAtVertex(destVert[f.v1], destVert[f.v2], destVert[f.v3]);
destNorm[f.v2] += n * angleAtVertex(destVert[f.v2], destVert[f.v1], destVert[f.v3]);
destNorm[f.v3] += n * angleAtVertex(destVert[f.v3], destVert[f.v2], destVert[f.v1]);
}
//*********************************************************************
// normalize the normals of each vertex
//*********************************************************************
for (vec3d n : destNorm)
{
n.normalize();
}
}
/**
* For a given edge it returns the index of the new vertex created on its middle point.
* If such vertex already exists it just returns the its index; if it does not exist
* it creates it in vertList along it's normal and return the index
*
* @param[in] e the edge
* @param[in,out] vertList the list of vertices
* @param[in] mesh the list of triangles
* @param[in,out] newVertList The list of the new vertices added so far
* @return the index of the new vertex or the one that has been already created for that edge
* @see EdgeList
*/
idxtype ObjModel::getNewVertex(const edge &e,
std::vector<point3d> &vertList,
const std::vector<face> &mesh,
EdgeList &newVertList) const
{
// PRINTVAR(e);
// PRINTVAR(newVertList);
//*********************************************************************
// if the egde is NOT contained in the new vertex list (see EdgeList.contains() method)
//*********************************************************************
if (!newVertList.contains(e))
{
//*********************************************************************
// generate new index (vertex.size)
//*********************************************************************
int newIndex = vertList.size();
//*********************************************************************
// add the edge and index to the newVertList
//*********************************************************************
newVertList.add(e, newIndex);
// generate new vertex
point3d nvert; //!< this will contain the new vertex
idxtype oppV1; //!< the index of the first "opposite" vertex
idxtype oppV2; //!< the index of the second "opposite" vertex (if it exists)
//*********************************************************************
// check if it is a boundary edge, ie check if there is another triangle
// sharing this edge and if so get the index of its "opposite" vertex
//*********************************************************************
if (!isBoundaryEdge(e, mesh, oppV1, oppV2))
{
// if it is not a boundary edge create the new vertex
//*********************************************************************
// the new vertex is the linear combination of the two extrema of
// the edge V1 and V2 and the two opposite vertices oppV1 and oppV2
// Using the loop coefficient the new vertex is
// nvert = 3/8 (V1+V2) + 1/8(oppV1 + oppV2)
//
// REMEMBER THAT IN THE CODE OPPV1 AND OPPV2 ARE INDICES, NOT VERTICES!!!
//*********************************************************************
nvert = 3.0f / 8.0f * (vertList[e.first] + vertList[e.second]) + 1.0f / 8.0f * (vertList[oppV1] + vertList[oppV2]);
}
else
{
//*********************************************************************
// otherwise it is a boundary edge then the vertex is the linear combination of the
// two extrema
//*********************************************************************
nvert = 0.5f * vertList[e.first] + 0.5f * vertList[e.second];
}
//*********************************************************************
// append the new vertex to the list of vertices
//*********************************************************************
vertList.push_back(nvert);
//*********************************************************************
// return the index of the new vertex
//*********************************************************************
return newIndex;
}
else
// else we don't need to do anything, just return the associated index of the
// already existing vertex
{
//*********************************************************************
// get and return the index of the vertex
//*********************************************************************
return newVertList.getIndex(e);
}
// this is just to avoid compilation errors at the beginning
// execution should normally never reach here
// the return instructions go inside each branch of the if - else above
std::cerr << "WARNING: the subdivision may not be implemented correctly" << std::endl;
return 0;
}
#include "ObjModel.cxx"

413
TP5/ObjModel.cxx Executable file
View file

@ -0,0 +1,413 @@
/**
* @file ObjModel.cxx
* @author Simone Gasparini <simone.gasparini@enseeiht.fr>
* @version 1.0
*
* @section LICENSE
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* @section DESCRIPTION
*
* Simple Class to load and draw 3D objects from OBJ files
* Using triangles and normals as static object. No texture mapping.
* OBJ files must be triangulated!!!
*
*/
#include <algorithm>
/**
* Computes the angle at vertex baseV formed by the edges connecting it with the
* vertices v1 and v2 respectively, ie the baseV-v1 and baseV-v2 edges
*
* @brief Computes the angle at vertex
* @param baseV the vertex at which to compute the angle
* @param v1 the other vertex of the first edge baseV-v1
* @param v2 the other vertex of the second edge baseV-v2
* @return the angle in radiants
*/
float ObjModel::angleAtVertex( const point3d& baseV, const point3d& v1, const point3d& v2 ) const
{
vec3d e1 = baseV - v1;
vec3d e2 = baseV - v2;
//safe acos...
if ( fabs( (e1).dot( e2 ) / (e1.norm( ) * e2.norm( )) ) >= 1.0f )
{
cerr << "warning: using safe acos" << endl;
return (acos( 1.0f ));
}
else
{
return ( acos( (e1).dot( e2 ) / (e1.norm( ) * e2.norm( )) ));
}
}
/**
* Render the model according to the provided parameters
* @param params The rendering parameters
*/
void ObjModel::render( const RenderingParameters &params )
{
// if we need to draw the original model
if ( !params.subdivision )
{
// draw it
draw( _vertices, _mesh, _normals, params );
// draw the normals
if ( params.normals )
{
drawNormals( _vertices, _normals );
}
}
else
{
PRINTVAR(params.subdivLevel);
PRINTVAR(_currentSubdivLevel);
// before drawing check the current level of subdivision and the required one
if ( ( _currentSubdivLevel == 0 ) || ( _currentSubdivLevel != params.subdivLevel ) )
{
// if they are different apply the missing steps: either restart from the beginning
// if the required level is less than the current one or apply the missing
// steps starting from the current one
vector<point3d> tmpVert; //!< a temporary list of vertices used in the iterations
vector<face> tmpMesh; //!< a temporary mesh used in the iterations
if(( _currentSubdivLevel == 0 ) || ( _currentSubdivLevel > params.subdivLevel ) )
{
// start from the beginning
_currentSubdivLevel = 0;
tmpVert = _vertices;
tmpMesh = _mesh;
}
else
{
// start from the current level
tmpVert = _subVert;
tmpMesh = _subMesh;
}
// apply the proper subdivision iterations
for( ; _currentSubdivLevel < params.subdivLevel; ++_currentSubdivLevel)
{
cerr << "[Loop subdivision] iteration " << _currentSubdivLevel << endl;
loopSubdivision( tmpVert, tmpMesh, _subVert, _subMesh, _subNorm );
// swap unless it's the last iteration
if( _currentSubdivLevel < ( params.subdivLevel - 1) )
{
tmpVert = _subVert;
tmpMesh = _subMesh;
}
}
}
draw( _subVert, _subMesh, _subNorm, params );
if ( params.normals )
{
drawNormals( _subVert, _subNorm );
}
}
}
/**
* Draw the model
*
* @param vertices list of vertices
* @param indices list of faces
* @param vertexNormals list of normals
* @param params Rendering parameters
*/
void ObjModel::draw( const std::vector<point3d> &vertices, const std::vector<face> &indices, std::vector<vec3d> &vertexNormals, const RenderingParameters &params ) const
{
if ( params.solid )
{
drawSolid( vertices, indices, vertexNormals, params );
}
if ( params.wireframe )
{
drawWireframe( vertices, indices, params );
}
}
void ObjModel::drawSolid( const vector<point3d> &vertices, const vector<face> &indices, vector<vec3d> &vertexNormals, const RenderingParameters &params ) const
{
if ( params.useIndexRendering )
{
drawSmoothFaces( vertices, indices, vertexNormals, params );
}
else
{
drawFlatFaces( vertices, indices, params );
}
}
/**
* Draw the normals at each vertex
* @param vertices The list of vertices
* @param vertexNormals The list of associated normals
*/
void ObjModel::drawNormals( const std::vector<point3d> &vertices, std::vector<vec3d> &vertexNormals ) const
{
glDisable( GL_LIGHTING );
glColor3f( 0.8, 0, 0 );
glLineWidth( 2 );
for(const auto &v : vertices)
{
glBegin( GL_LINES );
vec3d newP = v + 0.1 * v;
glVertex3fv( (float*) &v );
glVertex3f( newP.x, newP.y, newP.z );
glEnd( );
}
glEnable( GL_LIGHTING );
}
/**
* It scales the model to unitary size by translating it to the origin and
* scaling it to fit in a unit cube around the origin.
*
* @return the scale factor used to transform the model
*/
float ObjModel::unitizeModel( )
{
if ( !_vertices.empty( ) && !_mesh.empty( ) )
{
//****************************************
// calculate model width, height, and
// depth using the bounding box
//****************************************
const float w = fabs( _bb.pmax.x - _bb.pmin.x );
const float h = fabs( _bb.pmax.y - _bb.pmin.y );
const float d = fabs( _bb.pmax.z - _bb.pmin.z );
cout << "size: w: " << w << " h " << h << " d " << d << endl;
//****************************************
// calculate center of the bounding box of the model
//****************************************
const point3d c = (_bb.pmax + _bb.pmin) * 0.5;
//****************************************
// calculate the unitizing scale factor as the
// maximum of the 3 dimensions
//****************************************
const float scale = 2.0 / std::max( std::max( w, h ), d );
cout << "scale: " << scale << " cx " << c.x << " cy " << c.y << " cz " << c.z << endl;
// translate each vertex wrt to the center and then apply the scaling to the coordinate
for(auto& v : _vertices)
{
//****************************************
// translate the vertex
//****************************************
v.translate( -c.x, -c.y, -c.z );
//****************************************
// apply the scaling
//****************************************
v.scale( scale );
}
//****************************************
// update the bounding box, ie translate and scale the 6 coordinates
//****************************************
_bb.pmax = (_bb.pmax - c) * scale;
_bb.pmin = (_bb.pmin - c) * scale;
cout << "New bounding box : pmax=" << _bb.pmax << " pmin=" << _bb.pmin << endl;
return scale;
}
return 0;
}
void ObjModel::release( ) { }
/**
* It parses a line of the OBJ file containing a face and it return the result.
* NB: it only recover the indices, it discard normal and texture indices
*
* @param toParse the string to parse in the OBJ format for a face (f v/vt/vn v/vt/vn v/vt/vn) and its variants
* @param out the 3 indices for the face
* @return true if the parse was successful
*/
bool ObjModel::parseFaceString( const string &toParse, face &out ) const
{
// PRINTVAR( toParse );
if ( toParse.c_str( )[0] == 'f' )
{
idxtype a;
// now check the different formats: %d, %d//%d, %d/%d, %d/%d/%d
if ( strstr( toParse.c_str( ), "//" ) )
{
// v//n
return ( sscanf( toParse.c_str( ), "f %u//%u %u//%u %u//%u", &(out.v1), &a, &(out.v2), &a, &(out.v3), &a ) == 6);
}
else if ( sscanf( toParse.c_str( ), "f %u/%u/%u", &a, &a, &a ) == 3 )
{
// v/t/n
return ( sscanf( toParse.c_str( ), "f %u/%u/%u %u/%u/%u %u/%u/%u", &(out.v1), &a, &a, &(out.v2), &a, &a, &(out.v3), &a, &a ) == 9);
}
else if ( sscanf( toParse.c_str( ), "f %u/%u", &a, &a ) == 2 )
{
// v/t .
return ( sscanf( toParse.c_str( ), "f %u/%u %u/%u %u/%u", &(out.v1), &a, &(out.v2), &a, &(out.v3), &a ) == 6);
}
else
{
// v
// sscanf( toParse.c_str( ), "f %u %u %u", &(out.v1), &(out.v2), &(out.v3) );
// PRINTVAR( out );
return ( sscanf( toParse.c_str( ), "f %u %u %u", &(out.v1), &(out.v2), &(out.v3) ) == 3);
}
}
else
{
return false;
}
}
//*****************************************************************************
//* DEPRECATED FUNCTIONS
//*****************************************************************************
// to be deprecated
void ObjModel::flatDraw( ) const
{
glShadeModel( GL_SMOOTH );
// for each triangle draw the vertices and the normals
for(const auto &face : _mesh)
{
glBegin( GL_TRIANGLES );
//compute the normal of the triangle
vec3d n;
computeNormal( _vertices[face.v1], _vertices[(int) face.v2], _vertices[(int) face.v3], n );
glNormal3fv( (float*) &n );
glVertex3fv( (float*) &_vertices[face.v1] );
glVertex3fv( (float*) &_vertices[face.v2] );
glVertex3fv( (float*) &_vertices[face.v3] );
glEnd( );
}
}
// to be deprecated
void ObjModel::drawWireframe( ) const
{
drawWireframe( _vertices, _mesh, RenderingParameters( ) );
}
// to be deprecated
void ObjModel::indexDraw( ) const
{
glShadeModel( GL_SMOOTH );
//****************************************
// Enable vertex arrays
//****************************************
glEnableClientState( GL_VERTEX_ARRAY );
//****************************************
// Enable normal arrays
//****************************************
glEnableClientState( GL_NORMAL_ARRAY );
//****************************************
// Vertex Pointer to triangle array
//****************************************
glEnableClientState( GL_VERTEX_ARRAY );
//****************************************
// Normal pointer to normal array
//****************************************
glNormalPointer( GL_FLOAT, 0, (float*) &_normals[0] );
//****************************************
// Index pointer to normal array
//****************************************
glVertexPointer( COORD_PER_VERTEX, GL_FLOAT, 0, (float*) &_vertices[0] );
//****************************************
// Draw the triangles
//****************************************
glDrawElements( GL_TRIANGLES, _mesh.size( ) * VERTICES_PER_TRIANGLE, GL_UNSIGNED_INT, (idxtype*) & _mesh[0] );
//****************************************
// Disable vertex arrays
//****************************************
glDisableClientState( GL_VERTEX_ARRAY ); // disable vertex arrays
//****************************************
// Disable normal arrays
//****************************************
glDisableClientState( GL_NORMAL_ARRAY );
}
// to be deprecated
void ObjModel::drawSubdivision( )
{
if ( _subMesh.empty( ) || _subNorm.empty( ) || _subVert.empty( ) )
{
loopSubdivision( _vertices, _mesh, _subVert, _subMesh, _subNorm );
}
glShadeModel( GL_SMOOTH );
glEnableClientState( GL_NORMAL_ARRAY );
glEnableClientState( GL_VERTEX_ARRAY );
glNormalPointer( GL_FLOAT, 0, (float*) &_subNorm[0] );
glVertexPointer( COORD_PER_VERTEX, GL_FLOAT, 0, (float*) &_subVert[0] );
glDrawElements( GL_TRIANGLES, _subMesh.size( ) * VERTICES_PER_TRIANGLE, GL_UNSIGNED_SHORT, (idxtype*) & _subMesh[0] );
glDisableClientState( GL_VERTEX_ARRAY ); // disable vertex arrays
glDisableClientState( GL_NORMAL_ARRAY );
drawWireframe( _subVert, _subMesh, RenderingParameters( ) );
}
// to be removed
void ObjModel::applyLoop( const face &t, const std::vector<point3d> &origVert, std::vector<size_t> &valence, std::vector<point3d> &destVert ) const
{
// 5/8 V + 3/8 sum(V_i))
// in this case since we are summing each face the other vertices are counted
// twice, so we use 3/16 instead of 3/8
valence[t.v1]++;
destVert[t.v1] += (0.625f * origVert[t.v1] + 0.1875f * origVert[t.v2] + 0.1875f * origVert[t.v3]);
// PRINTVAR(valence[t.v1]);
valence[t.v2]++;
destVert[t.v2] += (0.625f * origVert[t.v2] + 0.1875f * origVert[t.v1] + 0.1875f * origVert[t.v3]);
valence[t.v3]++;
destVert[t.v3] += (0.625f * origVert[t.v3] + 0.1875f * origVert[t.v2] + 0.1875f * origVert[t.v1]);
}

260
TP5/ObjModel.hpp Executable file
View file

@ -0,0 +1,260 @@
/**
* @file ObjModel.hpp
* @author Simone Gasparini <simone.gasparini@enseeiht.fr>
* @version 1.0
*
* @section LICENSE
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* @section DESCRIPTION
*
* Simple Class to load and draw 3D objects from OBJ files
* Using triangles and normals as static object. No texture mapping.
* OBJ files must be triangulated!!!
*
*/
#pragma once
#include "core.hpp"
#include <vector>
#include <ostream>
#include <cmath>
#define VERTICES_PER_TRIANGLE 3
#define COORD_PER_VERTEX 3
#define TOTAL_FLOATS_IN_TRIANGLE (VERTICES_PER_TRIANGLE*COORD_PER_VERTEX)
/**
* A structure that model the bounding box
*/
typedef struct BoundingBox
{
point3d pmax; //!<the maximum point of the bounding box
point3d pmin; //!<the minimum point of the bounding box
BoundingBox() :
pmax(point3d()),
pmin(point3d()) { }
BoundingBox(const point3d &p) :
pmax(p),
pmin(p) { }
/**
* Add a point to the bounding box. Its coordinates are taken into account
* and the limits of the bounding box updated accordingly
* @param p the point to add
*/
void add(const point3d &p)
{
pmax.max(p);
pmin.min(p);
}
/**
* Set the bounding box to the given point
* @param p the point
*/
void set(const point3d &p)
{
pmax = p;
pmin = p;
}
} BoundingBox;
struct RenderingParameters
{
bool wireframe{true}; //!< wireframe on/off
bool solid{true}; //!< draw the mesh on/off
bool useIndexRendering{false}; //!< use opengl drawElements on/off
bool subdivision{false}; //!< subdivision on/off
bool smooth{false}; //!< GL_SMOOTH on/off
bool normals{false}; //!< show normals on/off
unsigned short subdivLevel{1}; //!< number of subdivision level
RenderingParameters() = default;
};
/**
* The class containing and managing the 3D model
*/
class ObjModel
{
private:
std::vector<face> _mesh; //!< Stores the vertex indices for the triangles
std::vector<point3d> _vertices; //!< Stores the vertices
std::vector<vec3d> _normals; //!< Stores the normals for the triangles
// Subdivision
std::vector<face> _subMesh; //!< Stores the vertex indices for the triangles
std::vector<point3d> _subVert; //!< Stores the vertices
std::vector<vec3d> _subNorm; //!< Stores the normals for the triangles
BoundingBox _bb; //!< the current bounding box of the model
unsigned short _currentSubdivLevel{}; //!< the current subdivision level
public:
ObjModel() = default;
/**
* Calculate the normal of a triangular face defined by three points
*
* @param[in] v1 the first vertex
* @param[in] v2 the second vertex
* @param[in] cv3 the third vertex
* @param[out] norm the normal
*/
void computeNormal(const point3d& v1, const point3d& v2, const point3d& v3, vec3d &norm) const;
/**
* Computes the angle at vertex baseV formed by the edges connecting it with the
* vertices v1 and v2 respectively, ie the baseV-v1 and baseV-v2 edges
*
* @brief Computes the angle at vertex
* @param[in] baseV the vertex at which to compute the angle
* @param[in] v1 the other vertex of the first edge baseV-v1
* @param[in] v2 the other vertex of the second edge baseV-v2
* @return the angle in radiants
*/
float angleAtVertex(const point3d& v1, const point3d& v2, const point3d& v3) const;
/**
* Load the OBJ data from file
* @param[in] filename The name of the OBJ file
* @return 0 if everything went well
*/
int load(char *filename);
/**
* Render the model according to the provided parameters
* @param params The rendering parameters
*/
void render(const RenderingParameters &params = RenderingParameters());
/**
* Release the model
*/
void release();
/**
* It scales the model to unitary size by translating it to the origin and
* scaling it to fit in a unit cube around the origin.
*
* @return the scale factor used to transform the model
*/
float unitizeModel();
private:
/**
* Draw the model
*
* @param[in] vertices list of vertices
* @param[in] indices list of faces
* @param[in] vertexNormals list of normals
* @param[in] params Rendering parameters
*/
void draw(const std::vector<point3d> &vertices, const std::vector<face> &indices, std::vector<vec3d> &vertexNormals, const RenderingParameters &params) const;
void drawSolid(const std::vector<point3d> &vertices, const std::vector<face> &indices, std::vector<vec3d> &vertexNormals, const RenderingParameters &params) const;
/**
* Draw the wireframe of the model
*
* @param vertices The list of vertices
* @param mesh The mesh as a list of faces, each face is a tripleIndex of vertex indices
* @param params The rendering parameters
*/
void drawWireframe(const std::vector<point3d> &vertices, const std::vector<face> &indices, const RenderingParameters &params) const;
/**
* Draw the model using the vertex indices and using a single normal for each vertex
*
* @param vertices The vertices
* @param indices The list of the faces, each face containing the 3 indices of the vertices
* @param vertexNormals The list of normals associated to each vertex
* @param params The rendering parameters
*/
void drawSmoothFaces(const std::vector<point3d> &vertices, const std::vector<face> &indices, std::vector<vec3d> &vertexNormals, const RenderingParameters &params) const;
/**
* Draw the faces using the computed normal of each face
*
* @param vertices The list of vertices
* @param mesh The list of face, each face containing the indices of the vertices
* @param params The rendering parameters
*/
void drawFlatFaces(const std::vector<point3d> &vertices, const std::vector<face> &indices, const RenderingParameters &params) const;
/**
* Draw the normals at each vertex
* @param vertices The list of vertices
* @param vertexNormals The list of associated normals
*/
void drawNormals(const std::vector<point3d> &vertices, std::vector<vec3d> &vertexNormals) const;
/////////////////////////////
// DEPRECATED METHODS
DEPRECATED(void drawSubdivision());
DEPRECATED(void indexDraw() const);
DEPRECATED(void flatDraw() const);
DEPRECATED(void drawWireframe() const);
private:
/**
* Compute the subdivision of the input mesh by applying one step of the Loop algorithm
*
* @param[in] origVert The list of the input vertices
* @param[in] origMesh The input mesh (the vertex indices for each face/triangle)
* @param[out] destVert The list of the new vertices for the subdivided mesh
* @param[out] destMesh The new subdivided mesh (the vertex indices for each face/triangle)
* @param[out] destNorm The new list of normals for each new vertex of the subdivided mesh
*/
void loopSubdivision(const std::vector<point3d> &origVert, const std::vector<face> &origMesh, std::vector<point3d> &destVert, std::vector<face> &destMesh, std::vector<vec3d> &destNorm) const;
/**
* For a given edge it returns the index of the new vertex created on its middle point. If such vertex already exists it just returns the
* its index; if it does not exist it creates it in vertList along it's normal and return the index
* @brief ObjModel::getNewVertex
* @param e the edge
* @param currFace the current triangle containing the edge e
* @param vertList the list of vertices
* @param indices the list of triangles
* @param normList the list of normals associated to the vertices
* @param newVertList The list of the new vertices added so far
* @return the index of the new vertex
* @see EdgeList
*/
idxtype getNewVertex(const edge &e, std::vector<point3d> &vertList, const std::vector<face> &mesh, EdgeList &newVertList) const;
/**
* It parses a line of the OBJ file containing a face and it return the result.
* NB: it only recover the indices, it discard normal and texture indices
*
* @param toParse the string to parse in the OBJ format for a face (f v/vt/vn v/vt/vn v/vt/vn) and its variants
* @param out the 3 indices for the face
* @return true if the parse was successful
*/
bool parseFaceString(const std::string &toParse, face &out) const;
DEPRECATED(void applyLoop(const face &t, const std::vector<point3d> &orig, std::vector<size_t> &valence, std::vector<point3d> &dest) const);
};

40
TP5/README.md Executable file
View file

@ -0,0 +1,40 @@
# OpenGL OBJ Visualizer
## Introduction
The fifth TP is about creating a visualizer for models loaded from [Wavefront OBJ format](https://en.wikipedia.org/wiki/Wavefront_.obj_file) files.
It also lets you implement a simple subdivision model of the mesh using Loop's algorithm.
![alt text](data/img/screeshot.png "Application visualizer")
The application lets you navigate the scene and change the different rendering algorithms.
The keys are:
* `s` - use index rendering
* `w` - draw wireframe
* `s` - enable/disable subdivision
* `1`-`4` - with subdivision enabled, level of subdivision
* `d` - enable/disable solid rendering
* `a` - enable/disable smooth rendering
* `arrow keys` - rotate around the object
* `pg down/up` - zoom out/in
The folder [data/models](data/models) contains some 3D models to play with.
## Building
See [BUILD](BUILD.md) text file
## License
See [LICENSE](LICENSE) text file
## Authors
Simone Gasparini
## Contact
Simone Gasparini simone.gasparini@irit.fr

31
TP5/appveyor.yml Executable file
View file

@ -0,0 +1,31 @@
version: '2021.0.0.{build}'
image: Visual Studio 2019
platform:
- x64
configuration:
- Release
- Debug
#install:
# - vcpkg upgrade --no-dry-run
# - vcpkg install
# opengl
# --triplet %PLATFORM%-windows
before_build:
- md build
- cd build
# - cmake -G "Visual Studio 16 2019" -A x64 -T v140,host=x64 -DCMAKE_BUILD_TYPE=%configuration% -DCMAKE_TOOLCHAIN_FILE=c:/tools/vcpkg/scripts/buildsystems/vcpkg.cmake ..
- cmake -G "Visual Studio 16 2019" -A x64 -T v140,host=x64 -DCMAKE_BUILD_TYPE=%configuration% ..
- ls -l
build:
verbosity: detailed
project: $(APPVEYOR_BUILD_FOLDER)\build\tp4.sln
parallel: true
cache:
# - c:\tools\vcpkg\installed\

27
TP5/changelog.md Executable file
View file

@ -0,0 +1,27 @@
# Changelog
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [Unreleased]
### Added
- support for windows
- embed freeglut for windows
- ci on appveyor
### Changed
- flat structure of the code
- modernize the code, rely only on c++11
- raised cmake version
## [0.6.0] - 2017-10-05
First stable version with subdivision
### Added
- Subdivision
## [0.5.0] - 2014-10-19
First version of the visualizer

443
TP5/core.cpp Executable file
View file

@ -0,0 +1,443 @@
/**
* @file core.cpp
* @author Simone Gasparini <simone.gasparini@enseeiht.fr>
* @version 1.0
*
* @section LICENSE
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* @section DESCRIPTION
*
* The core module providing some helper classes for manipulating mesh data
*
*/
#include "core.hpp"
#include <cmath>
#include <cassert>
#include <algorithm>
#include <limits>
void v3f::normalize()
{
const float n = norm();
if (n > (100.f * std::numeric_limits<float>::epsilon()) )
{
x /= n;
y /= n;
z /= n;
}
}
float v3f::dot(const v3f &v) const
{
return (x*v.x + y*v.y + z*v.z);
}
float v3f::norm() const
{
return std::sqrt(x*x + y*y + z*z);
}
/**
* Translate the vector
* @param x the delta x of the translation
* @param y the delta y of the translation
* @param z the delta z of the translation
*/
void v3f::translate( const float &x, const float &y, const float &z )
{
this->x += x;
this->y += y;
this->z += z;
}
void v3f::translate( const v3f &t )
{
x += t.x;
y += t.y;
z += t.z;
}
void v3f::scale( const v3f &t )
{
x *= t.x;
y *= t.y;
z *= t.z;
}
void v3f::scale( const float &x, const float &y, const float &z )
{
scale(v3f(x,y,z));
}
void v3f::scale( const float &a )
{
scale(v3f(a,a,a));
}
void v3f::min( const v3f& a )
{
x = std::min( x, a.x );
y = std::min( y, a.y );
z = std::min( z, a.z );
}
void v3f::max( const v3f& a )
{
x = std::max( x, a.x );
y = std::max( y, a.y );
z = std::max( z, a.z );
}
/**
* Return the minimum value among the 3 elements
* @return the minimum value
*/
float v3f::min() const
{
return std::min( std::min( x, y ), z );
}
/**
* Return the maximum value among the 3 elements
* @return the maximum value
*/
float v3f::max() const
{
return std::max( std::max( x, y ), z );
}
v3f v3f::cross(const v3f& v) const
{
return {y*v.z - z*v.y,
z*v.x - x*v.z,
x*v.y - y*v.x};
}
v3f v3f::cross(const float v[3]) const
{
return {y*v[2] - z*v[1],
z*v[0] - x*v[2],
x*v[1] - y*v[0]};
}
// OPERATOR OVERLOADINGS
v3f v3f::operator +(const v3f& a) const
{
return {x + a.x, y + a.y, z + a.z};
}
v3f& v3f::operator +=(const v3f& a)
{
x += a.x;
y += a.y;
z += a.z;
return *this;
}
v3f v3f::operator +(const float a[3]) const
{
return {x + a[0], y + a[1], z + a[2]};
}
v3f& v3f::operator +=(const float a[3])
{
x += a[0];
y += a[1];
z += a[2];
return *this;
}
v3f v3f::operator +(const float &a) const
{
return {x + a, y + a, z + a};
}
v3f& v3f::operator +=(const float &a)
{
x += a;
y += a;
z += a;
return *this;
}
// subtraction
v3f v3f::operator -(const v3f& a) const
{
return {x - a.x, y - a.y, z - a.z};
}
v3f& v3f::operator -=(const v3f& a)
{
x -= a.x;
y -= a.y;
z -= a.z;
return *this;
}
v3f v3f::operator -(const float a[3]) const
{
return {x - a[0], y - a[1], z - a[2]};
}
v3f& v3f::operator -=(const float a[3])
{
x -= a[0];
y -= a[1];
z -= a[2];
return *this;
}
v3f v3f::operator -(const float &a) const
{
return {x - a, y - a, z - a};
}
v3f& v3f::operator -=(const float& a)
{
x -= a;
y -= a;
z -= a;
return *this;
}
// element-wise product
v3f v3f::operator *(const v3f& a) const
{
return {x * a.x, y * a.y, z * a.z};
}
v3f& v3f::operator *=(const v3f& a)
{
x *= a.x;
y *= a.y;
z *= a.z;
return *this;
}
v3f v3f::operator *(const float a[3]) const
{
return {x * a[0], y * a[1], z * a[2]};
}
v3f& v3f::operator *=(const float a[3])
{
x *= a[0];
y *= a[1];
z *= a[2];
return *this;
}
v3f v3f::operator *(const float &a) const
{
return {x * a, y * a, z * a};
}
v3f& v3f::operator *=(const float &a)
{
x *= a;
y *= a;
z *= a;
return *this;
}
// element-wise ratio
v3f v3f::operator /(const v3f& a) const
{
return {x / a.x, y / a.y, z / a.z};
}
v3f& v3f::operator /=(const v3f& a)
{
x /= a.x;
y /= a.y;
z /= a.z;
return *this;
}
v3f v3f::operator /(const float a[3]) const
{
return {x / a[0], y / a[1], z / a[2]};
}
v3f& v3f::operator /=(const float a[3])
{
x /= a[0];
y /= a[1];
z /= a[2];
return *this;
}
v3f v3f::operator /(const float &a) const
{
return {x / a, y / a, z / a};
}
v3f& v3f::operator /=(const float &a)
{
x /= a;
y /= a;
z /= a;
return *this;
}
//**************************** tindex ***********************//
/**
* Return true if the edge e is contained in the triplet of indices. If it is
* contained it also return the opposite vertex (ie the index of the vertex not
* belonging to the edge)
*
* @param[in] e the edge to check
* @param[out] oppositeVertex If the triplet contain the edge, this will be the (index of the) opposite
* vertex wrt the edge in the triangle
* @return true if the edge is contained (the order of the indices does not matter)
*/
bool face::containsEdge(const edge &e, idxtype &oppositeVertex) const
{
if( edge(v1, v2) == e )
{
oppositeVertex = v3;
return true;
}
else if ( edge(v2, v3) == e )
{
oppositeVertex = v1;
return true;
}
else if ( edge(v3, v1) == e )
{
oppositeVertex = v2;
return true;
}
else
{
return false;
}
}
// ********** SUM
face face::operator +(const face& a) const
{
return {v1 + a.v1, v2 + a.v2, v3 + a.v3};
}
face& face::operator +=(const face& a)
{
v1 += a.v1;
v2 += a.v2;
v3 += a.v3;
return *this;
}
face face::operator +(const idxtype &a) const
{
return {v1 + a, v2 + a, v3 + a};
}
face& face::operator +=(const idxtype &a)
{
v1 += a;
v2 += a;
v3 += a;
return *this;
}
// ********** DIFFERENCE
face face::operator -(const face& a) const
{
return {v1 - a.v1, v2 - a.v2, v3 - a.v3};
}
face& face::operator -=(const face& a)
{
v1 -= a.v1;
v2 -= a.v2;
v3 -= a.v3;
return *this;
}
face face::operator -(const idxtype &a) const
{
return {v1 - a, v2 - a, v3 - a};
}
face& face::operator -=(const idxtype &a)
{
v1 -= a;
v2 -= a;
v3 -= a;
return *this;
}
// ********** MULTIPLICATION
face face::operator *(const face& a) const
{
return {v1 * a.v1, v2 * a.v2, v3 * a.v3};
}
face& face::operator *=(const face& a)
{
v1 *= a.v1;
v2 *= a.v2;
v3 *= a.v3;
return *this;
}
face face::operator *(const idxtype &a) const
{
return {v1 * a, v2 * a, v3 * a};
}
face& face::operator *=(const idxtype &a)
{
v1 *= a;
v2 *= a;
v3 *= a;
return *this;
}
bool face::operator==( const face& r) const
{
return ( (v1 == r.v1) && (v2 == r.v2) && (v3 == r.v3));
}
/**
* Two index triplets are different if... they are not equal
*/
bool face::operator!=( const face& r ) const
{
return ( !(*this == r));
}
/**
* It checks if the edge e is a boundary edge in the list of triangle. It also
* return the indices of the two opposite vertices of the edge or only one of
* them if it is a boundary edge
*
* @param[in] e the edge to check
* @param[in] mesh the list of triangles
* @param[out] oppVert1 the index of the first opposite vertices (the only one if the edge is a boundary edge)
* @param[out] oppVert2 the index of the second opposite vertices (only if the edge is not a boundary edge)
* @return true if the edge is a boundary edge
*/
bool isBoundaryEdge(const edge &e, const std::vector<face> &mesh, idxtype &oppVert1, idxtype &oppVert2 )
{
bool foundFirst = false;
bool foundSecond = false;
for(size_t i = 0; (i < mesh.size()) && ( !(foundFirst && foundSecond)); ++i )
{
if(!foundFirst)
{
foundFirst = mesh[i].containsEdge(e, oppVert1);
}
else
{
foundSecond = mesh[i].containsEdge(e, oppVert2);
}
}
// the edge should be in the list, so at least one should be found
assert(foundFirst);
return(foundFirst && (!foundSecond));
}

568
TP5/core.hpp Executable file
View file

@ -0,0 +1,568 @@
/**
* @file core.hpp
* @author Simone Gasparini <simone.gasparini@enseeiht.fr>
* @version 1.0
*
* @section LICENSE
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* @section DESCRIPTION
*
* The core module providing some helper classes for manipulating mesh data
*
*/
#pragma once
// for mac osx
#ifdef __APPLE__
#include <OpenGL/gl.h>
#else
// only for windows
#ifdef _WIN32
#include <windows.h>
#endif
// for windows and linux
#include <GL/gl.h>
#endif
#ifdef __GNUC__
#define DEPRECATED(func) func __attribute__ ((deprecated))
#elif defined(_MSC_VER)
#define DEPRECATED(func) __declspec(deprecated) func
#else
#pragma message("WARNING: You need to implement DEPRECATED for this compiler")
#define DEPRECATED(func) func
#endif //__attribute__ ((deprecated))
#include <vector>
#include <iostream>
#include <string>
#include <unordered_map>
#include <functional>
#define DEBUGGING 1
#if DEBUGGING
#define PRINTVAR( a ) std::cout << #a << " = " << (a) << std::endl << std::endl;
#else
#define PRINTVAR( a )
#endif
/**
* Renaming, the type of an index is a unsigned int
*/
using idxtype = GLuint;
/**
* An edge is defined as a pair of indices of the vertices
*/
using edge = std::pair<idxtype, idxtype>;
/**
* Function to print the values of an edge
*
* @param os the string to fill
* @param p the edge to print
* @return the string with the printed value of the edge
*/
inline std::ostream& operator<<( std::ostream& os, const edge& e )
{
return os << "[" << e.first << "," << e.second << "]";
}
/**
* It checks whether two edges are equals: two edges are equal if their indices
* are the same, no matter the order
*
* @param[in] a The first edge
* @param[in] b The second edge
* @return true if the edges are equal
*/
inline bool operator==( const edge& a, const edge& b )
{
return ( ( ( a.first == b.first ) && ( a.second == b.second ) ) ||
( ( a.first == b.second ) && ( a.second == b.first ) ) );
}
/**
* Return the sum of vertex indices of an edge
* @param[in] e the edge
* @return the sum of the indices
*/
inline idxtype sum( const edge &e )
{
return (e.first + e.second );
}
/**
* return the min index of the two vertices
* @param[in] e the edge
* @return the min index
*/
inline idxtype min( const edge &e )
{
return (( e.first > e.second ) ? ( e.second ) : ( e.first ) );
}
/**
* Structure used to compare two edges
*/
struct edgeEquivalent
{
bool operator( ) ( const edge &a, const edge &b ) const
{
// return !( ( (a.first == b.first) && (a.second == b.second) ) ||
// ( (a.first == b.second) && (a.second == b.first) ) );
// two edges are equal either their corresponding elements are equal or
// they are inverted
return ( ( ( a.first == b.first ) && ( a.second == b.second ) ) ||
( ( a.first == b.second ) && ( a.second == b.first ) ) );
}
};
// to be used with unordered
struct edgeHash
{
size_t operator( ) ( const edge &a ) const
{
std::hash<std::string> fun;
return (fun( ( a.first > a.second ) ? ( "v" + std::to_string( a.second ) + "-" + std::to_string( a.first ) ) :
( "v" + std::to_string( a.first ) + "-" + std::to_string( a.second ) ) ) );
}
};
/**
* An edge list is a map of edges (the keys) and a index of the vertex
*/
using edge2vertex = std::unordered_map< edge, idxtype, edgeHash, edgeEquivalent >;
inline std::ostream& operator<<( std::ostream& os, const edge2vertex & l )
{
os << std::endl;
for (const auto &it : l)
os << "\t" << it.first << "\t" << it.second << std::endl;
return os;
}
/**
* A helper class containing the indices of the new vertices added with the subdivision
* coupled with the edge that has generated them. More specifically, it is a list in which
* each entry has a key (ie an identifier) and a value: the key is the edge that
* generate the vertex, the value is the index of the new vertex.
* The key (ie the edge) is unique, ie an edge cannot generate more than one vertex. The
* edge is a pair of vertex indices, two edges are the same if they contain the
* same pair of vertices, no matter their order, ie
* edge(v1, v2) == edge(v2, v1)
*
* @see edge
*/
class EdgeList
{
public:
EdgeList( ) = default;
/**
* Add the edge and the index of the new vertex generated on it
* @param[in] e the edge
* @param[in] idx the index of the new vertex generated on the edge
*/
void add( const edge &e, const idxtype &idx )
{
list[e] = idx;
}
/**
* Return true if the edge is in the map
* @param[in] e the edge to search for
* @return true if the edge is in the map
*/
bool contains( const edge &e ) const
{
return (list.find( e ) != list.end( ) );
}
/**
* Get the vertex index associated to the edge
* @param e the edge
* @return the index
*/
idxtype getIndex( const edge &e )
{
return (list[e] );
}
friend std::ostream& operator<<( std::ostream& os, const EdgeList& l );
private:
edge2vertex list;
};
inline std::ostream& operator<<( std::ostream& os, const EdgeList& l )
{
return (os << l.list );
}
/**************************************************************************/
/**
* A generic vector of three elements
*/
struct v3f
{
float x; //!< the first component
float y; //!< the second component
float z; //!< the third component
/**
* Generic constructor
* @param x the first element
* @param y the second element
* @param z the third element
*/
v3f( float x, float y, float z ) : x( x ), y( y ), z( z ) { }
/**
* Default constructor, everything is initialized to 0
*/
v3f( ) : x( 0 ), y( 0 ), z( 0 ) { }
/**
* Constructor from an array of three elements
* @param[in] a the array from which to copy the elements
*/
explicit v3f( const float a[3] ) : x( a[0] ), y( a[1] ), z( a[2] ) { }
/**
* Normalize the vector (ie divide by the norm)
*/
void normalize( );
/**
* Return the dot product
* @param[in] v the other vector
* @return the dot product
*/
float dot( const v3f &v ) const;
/**
* Return the norm of the vector
* @return the norm
*/
float norm( ) const;
/**
* Translate the vector
* @param[in] x the delta x of the translation
* @param[in] y the delta y of the translation
* @param[in] z the delta z of the translation
*/
void translate( const float &x, const float &y, const float &z );
/**
* Translate the vector
* @param[in] t the translation
*/
void translate( const v3f &t );
/**
* Scale each element of the vector by the corresponding value
* @param[in] t a vector containing a factor scale to apply to each element
*/
void scale( const v3f &t );
/**
* Scale each element of the vector by the corresponding value
* @param x the scale value on x
* @param y the scale value on y
* @param z the scale value on z
*/
void scale( const float &x, const float& y, const float &z );
/**
* Scale each element of the vector by the same value
* @param a The scalar value to apply to each element
*/
void scale( const float &a );
/**
* Set each element of the current vector to the minimum value wrt another vector
* @param a the other vector
*/
void min( const v3f& a );
/**
* Return the minimum value among the 3 elements
* @return the minimum value
*/
float min( ) const;
/**
* Set each element of the current vector to the maximum value wrt another vector
* @param a the other vector
*/
void max( const v3f& a );
/**
* Return the maximum value among the 3 elements
* @return the maximum value
*/
float max( ) const;
/**
* Return the cross product of two vectors
* @param v the other vector
* @return the cross product
*/
v3f cross( const v3f& v ) const;
/**
* Return the cross product of two vectors
* @param v the other array
* @return the cross product
*/
v3f cross( const float v[3] ) const;
// element-wise addition
v3f operator +( const v3f& a ) const;
v3f& operator +=( const v3f& a );
v3f operator +( const float a[3] ) const;
v3f& operator +=( const float a[3] );
v3f operator +( const float &a ) const;
v3f& operator +=( const float &a );
// element-wise subtraction
v3f operator -( const v3f& a ) const;
v3f& operator -=( const v3f& a );
v3f operator -( const float a[3] ) const;
v3f& operator -=( const float a[3] );
v3f operator -( const float &a ) const;
v3f& operator -=( const float &a );
// element-wise product
v3f operator *( const v3f& a ) const;
v3f& operator *=( const v3f& a );
v3f operator *( const float a[3] ) const;
v3f& operator *=( const float a[3] );
v3f operator *( const float &a ) const;
v3f& operator *=( const float &a );
// element-wise ratio
v3f operator /( const v3f& a ) const;
v3f& operator /=( const v3f& a );
v3f operator /( const float a[3] ) const;
v3f& operator /=( const float a[3] );
v3f operator /( const float &a ) const;
v3f& operator /=( const float &a );
};
/**
* Some definitions
*/
using point3d = struct v3f ;
using vec3d = struct v3f;
/**
* Print the elements of a vector
* @param os the string to fill with the vector values
* @param p the vector
* @return the string with the values
*/
inline std::ostream& operator<<( std::ostream& os, const v3f& p )
{
return os << "[" << p.x << "," << p.y << "," << p.z << "]";
}
/**
* Print the elements of a vector of v3f elements
* @param os the string to fill with the vector elements
* @param p the vector
* @return the string with the vector elements
*/
inline std::ostream& operator<<( std::ostream& os, const std::vector<v3f>& p )
{
os << std::endl;
for (const auto& v : p)
os << "\t" << v << std::endl;
return os;
}
// REFLEXIVE OPERATORS FOR V3F
inline v3f operator +( const float &a, const v3f& p )
{
return (p + a );
}
inline v3f operator +( const float a[3], const v3f& p )
{
return (p + a );
}
inline v3f operator -( const float &a, const v3f& p )
{
return (p - a );
}
inline v3f operator -( const float a[3], const v3f& p )
{
return (p - a );
}
inline v3f operator *( const float a[3], const v3f& p )
{
return (p * a );
}
inline v3f operator *( const float &a, const v3f& p )
{
return (p * a );
}
inline v3f operator /( const float a[3], const v3f& p )
{
return (p / a );
}
inline v3f operator /( const float &a, const v3f& p )
{
return (p / a );
}
/**************************************************************************/
/**
* The face as a triplet of indices
*/
struct face
{
idxtype v1; //!< the first index
idxtype v2; //!< the second index
idxtype v3; //!< the third index
/**
* Default constructor, everything set to 0
*/
face( ) : v1( 0 ), v2( 0 ), v3( 0 ) { }
/**
* Constructor from indices
* @param v1 the first index
* @param v2 the second index
* @param v3 the third index
*/
face( idxtype v1, idxtype v2, idxtype v3 ) : v1( v1 ), v2( v2 ), v3( v3 ) { }
/**
* Return true if the edge e is contained in the triplet of indices. If it is
* contained it also return the opposite vertex (ie the index of the vertex not
* belonging to the edge)
*
* @param[in] e the edge to check
* @param[out] oppositeVertex If the triplet contain the edge, this will be the (index of the) opposite
* vertex wrt the edge in the triangle
* @return true if the edge is contained (the order of the indices does not matter)
*/
bool containsEdge( const edge &e, idxtype &oppositeVertex ) const;
face operator +( const face& a ) const;
face& operator +=( const face& a );
face operator +( const idxtype &a ) const;
face& operator +=( const idxtype &a );
face operator -( const face& a ) const;
face& operator -=( const face& a );
face operator -( const idxtype &a ) const;
face& operator -=( const idxtype &a );
face operator *( const face& a ) const;
face& operator *=( const face& a );
face operator *( const idxtype &a ) const;
face& operator *=( const idxtype &a );
/**
* Two index triplets are equal if their corresponding elements are equal
*/
bool operator==( const face& rhs ) const;
/**
* Two index triplets are different if... they are not equal
*/
bool operator!=( const face& rhs ) const;
};
/**
* Print the face values on a string
* @param os the string to fill with the values
* @param p the face to print
* @return the string filled with the values
*/
inline std::ostream& operator<<( std::ostream& os, const face& p )
{
return os << "[" << p.v1 << "," << p.v2 << "," << p.v3 << "]";
}
/**
* Print a vector of faces on a string
* @param[in,out] os the string to fill with the faces
* @param[in] p the vector of faces to print
* @return the string filled with the faces
*/
inline std::ostream& operator<<( std::ostream& os, const std::vector<face>& p )
{
os << std::endl;
for (auto v : p)
os << "\t" << v << std::endl;
return os;
}
/**
* It checks if the edge e is a boundary edge in the list of triangle. It also
* return the indices of the two opposite vertices of the edge or only one of
* them if it is a boundary edge
*
* @param[in] e the edge to check
* @param[in] triangleList the list of triangles
* @param[out] oppVert1 the index of the first opposite vertices (the only one if the edge is a boundary edge)
* @param[out] oppVert2 the index of the second opposite vertices (only if the edge is not a boundary edge)
* @return true if the edge is not a boundary edge
*/
bool isBoundaryEdge( const edge &e, const std::vector<face> &triangleList, idxtype &oppVert1, idxtype &oppVert2 );

BIN
TP5/data/img/screeshot.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

File diff suppressed because it is too large Load diff

10388
TP5/data/models/cow-nonormals.obj Executable file

File diff suppressed because it is too large Load diff

26
TP5/data/models/cube (copy).obj Executable file
View file

@ -0,0 +1,26 @@
# cube.obj
#
g cube
v 0.0 0.0 0.0 1.0 1.0 0.0
v 0.0 0.0 1.0 0.0 1.0 0.5
v 0.0 1.0 0.0 0.5 0.5 0
v 0.0 1.0 1.0 0.75 0.15 0.66
v 1.0 0.0 0.0 1.0 0.0 0.0
v 1.0 0.0 1.0 0.0 1.0 0.0
v 1.0 1.0 0.0 0.75 0.15 0.3
v 1.0 1.0 1.0 0.3 0.8 0.1
f 1 7 5
f 1 3 7
f 1 4 3
f 1 2 4
f 3 8 7
f 3 4 8
f 5 7 8
f 5 8 6
f 1 5 6
f 1 6 2
f 2 6 8
f 2 8 4

26
TP5/data/models/cube.obj Executable file
View file

@ -0,0 +1,26 @@
# cube.obj
#
g cube
v 0.0 0.0 0.0
v 0.0 0.0 1.0
v 0.0 1.0 0.0
v 0.0 1.0 1.0
v 1.0 0.0 0.0
v 1.0 0.0 1.0
v 1.0 1.0 0.0
v 1.0 1.0 1.0
f 1 7 5
f 1 3 7
f 1 4 3
f 1 2 4
f 3 8 7
f 3 4 8
f 5 7 8
f 5 8 6
f 1 5 6
f 1 6 2
f 2 6 8
f 2 8 4

37
TP5/data/models/icosahedron.obj Executable file
View file

@ -0,0 +1,37 @@
# OBJ file created by ply_to_obj.c
#
g Object001
v 0 -0.525731 0.850651
v 0.850651 0 0.525731
v 0.850651 0 -0.525731
v -0.850651 0 -0.525731
v -0.850651 0 0.525731
v -0.525731 0.850651 0
v 0.525731 0.850651 0
v 0.525731 -0.850651 0
v -0.525731 -0.850651 0
v 0 -0.525731 -0.850651
v 0 0.525731 -0.850651
v 0 0.525731 0.850651
f 2 3 7
f 2 8 3
f 4 5 6
f 5 4 9
f 7 6 12
f 6 7 11
f 10 11 3
f 11 10 4
f 8 9 10
f 9 8 1
f 12 1 2
f 1 12 5
f 7 3 11
f 2 7 12
f 4 6 11
f 6 5 12
f 3 8 10
f 8 2 1
f 4 10 9
f 5 9 1

12
TP5/data/models/square.obj Executable file
View file

@ -0,0 +1,12 @@
# cube.obj
#
g cube
v 0.0 0.0 0.0
v 0.0 0.0 1.0
v 0.0 1.0 0.0
v 0.0 1.0 1.0
f 1 4 3
f 1 2 4

19
TP5/data/models/sujet.obj Executable file
View file

@ -0,0 +1,19 @@
# sujet.obj
#
g sujet
v 0.0 0.0 0.0
v 1.0 0.0 0.0
v 0.7 1.0 0.0
v -0.7 1.0 0.0
v -1.0 0.0 0.0
v -0.7 -1.0 0.0
v 0.7 -1.0 0.0
f 1 3 4
f 1 2 3
f 1 7 2
f 1 6 7
f 1 5 6
f 1 4 5

324549
TP5/data/models/surprise.obj Executable file

File diff suppressed because it is too large Load diff

1480
TP5/data/models/suzanne.obj Executable file

File diff suppressed because it is too large Load diff

9965
TP5/data/models/teapot.obj Executable file

File diff suppressed because it is too large Load diff

9577
TP5/data/models/teapotmani.obj Executable file

File diff suppressed because it is too large Load diff

9571
TP5/data/models/teapotmani.ply Executable file

File diff suppressed because it is too large Load diff

4790
TP5/data/models/teddy.obj Executable file

File diff suppressed because it is too large Load diff

16
TP5/data/models/triang.obj Executable file
View file

@ -0,0 +1,16 @@
# triang.obj
#
g triang
v 0.0 0.0 0.0
v 0.0 0.0 1.0
v 0.0 1.0 0.0
v 0.0 1.0 1.0
v 0.0 1.0 -1.0
v 0.0 -1.0 1.0
f 2 1 3
f 2 3 4
f 1 5 3
f 1 2 6

414
TP5/main.cpp Executable file
View file

@ -0,0 +1,414 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "ObjModel.hpp"
// for mac osx
#ifdef __APPLE__
#include <OpenGL/gl.h>
#include <OpenGL/glu.h>
#include <GLUT/glut.h>
#else
// only for windows
#ifdef _WIN32
#include <windows.h>
#endif
// for windows and linux
#include <GL/gl.h>
#include <GL/glu.h>
#include <GL/freeglut.h>
#endif
#include <iostream>
#include <fstream>
#include <utility>
#include <vector>
#include <cstdio>
#include <cstring>
#include <cassert>
#define KEY_ESCAPE 27
#define DELTA_ANGLE_X 5
#define DELTA_ANGLE_Y 5
#define DELTA_DISTANCE 0.3
#define DISTANCE_MIN 0.0
using namespace std;
// Window
struct glutWindow
{
int width{1024};
int height{760};
string title;
float field_of_view_angle{45};
float z_near{0.25f};
float z_far{500.0f};
glutWindow() = default;
explicit glutWindow(string &winName) : title(winName) {}
};
//************************************
// global variable containing the OBJ model
//************************************
ObjModel obj;
int angle_y = 0;
int angle_x = 0;
float camDistance = 5;
RenderingParameters params;
glutWindow win;
// Function called every time the main window is resized
void reshape(int width, int height);
void DrawAxis(float scale);
// initialize the opengl
void initialize();
// define a material in terms of its components
void define_material(GLfloat ar, GLfloat ag, GLfloat ab, // ambient
GLfloat dr, GLfloat dg, GLfloat db, // diffuse
GLfloat sr, GLfloat sg, GLfloat sb, // specular
GLfloat sh // shininess
)
{
GLfloat mat_ambient[4];
GLfloat mat_diffuse[4];
GLfloat mat_specular[4];
mat_ambient[0] = ar;
mat_ambient[1] = ag;
mat_ambient[2] = ab;
mat_ambient[3] = 1.0;
glMaterialfv(GL_FRONT, GL_AMBIENT, mat_ambient);
mat_diffuse[0] = dr;
mat_diffuse[1] = dg;
mat_diffuse[2] = db;
mat_diffuse[3] = 1.0;
glMaterialfv(GL_FRONT, GL_DIFFUSE, mat_diffuse);
mat_specular[0] = sr;
mat_specular[1] = sg;
mat_specular[2] = sb;
mat_specular[3] = 1.0;
glMaterialfv(GL_FRONT, GL_SPECULAR, mat_specular);
glMaterialf(GL_FRONT, GL_SHININESS, sh);
}
// place the light in x,y,z
void place_light(GLfloat x, GLfloat y, GLfloat z)
{
GLfloat light_position[4];
GLfloat light_ambient[] = {0.2, 0.2, 0.2, 1.0};
GLfloat light_diffuse[] = {1.0, 1.0, 1.0, 1.0};
GLfloat light_specular[] = {1.0, 1.0, 1.0, 1.0};
light_position[0] = x;
light_position[1] = y;
light_position[2] = z;
light_position[3] = 1.0;
glLightfv(GL_LIGHT0, GL_AMBIENT, light_ambient);
glLightfv(GL_LIGHT0, GL_DIFFUSE, light_diffuse);
glLightfv(GL_LIGHT0, GL_SPECULAR, light_specular);
glLightfv(GL_LIGHT0, GL_POSITION, light_position);
glEnable(GL_LIGHT0);
glEnable(GL_LIGHTING);
}
void display()
{
glClearColor(0.5, .5, .75, 1.);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glPushMatrix();
place_light(0, 0, 140);
glTranslatef(0, 0, -camDistance);
glRotatef(angle_x, 1, 0, 0);
glRotatef(angle_y, 0, 1, 0);
// angle_y += 1;
DrawAxis(1.0f);
define_material(
0.2, 0.2, 0.2,
0.8, 0.8, 0.8,
1.0, 0.8, 0.8,
100);
//***********************************************
// draw the model
//***********************************************
obj.render(params);
glPopMatrix();
glutSwapBuffers();
}
void printKeyboardHelp()
{
std::cout << "keys:"
<< "\t s - use index rendering\n"
<< "\t w - draw wireframe\n"
<< "\t h - enable/disable subdivision\n"
<< "\t 1-4 - with subdivision enabled, level of subdivision\n"
<< "\t d - enable/disable solid rendering\n"
<< "\t a - enable/disable smooth rendering\n"
<< "\t n - enable/disable normals rendering\n"
<< "\t arrow keys - rotate around the object\n"
<< "\t pg down/up - zoom out/in\n"
<< std::endl;
}
void keyboard(unsigned char key, int, int)
{
switch (key)
{
case KEY_ESCAPE:
exit(0);
case 's':
params.useIndexRendering = !params.useIndexRendering;
PRINTVAR(params.useIndexRendering);
break;
case 'w':
params.wireframe = !params.wireframe;
PRINTVAR(params.wireframe);
break;
case 'h':
params.subdivision = !params.subdivision;
PRINTVAR(params.subdivision);
break;
case 'd':
params.solid = !params.solid;
PRINTVAR(params.solid);
break;
case 'a':
params.smooth = !params.smooth;
PRINTVAR(params.smooth);
break;
case 'n':
params.normals = !params.normals;
PRINTVAR(params.normals);
break;
case '1':
case '2':
case '3':
case '4':
params.subdivLevel = (key - '0');
PRINTVAR(params.subdivLevel);
break;
default:
break;
}
glutPostRedisplay();
printKeyboardHelp();
}
void arrows(int key, int, int)
{
switch (key)
{
case GLUT_KEY_UP:
angle_x = (angle_x + DELTA_ANGLE_X) % 360;
break;
case GLUT_KEY_DOWN:
angle_x = (angle_x - DELTA_ANGLE_X) % 360;
break;
case GLUT_KEY_LEFT:
angle_y = (angle_y + DELTA_ANGLE_Y) % 360;
break;
case GLUT_KEY_RIGHT:
angle_y = (angle_y - DELTA_ANGLE_Y) % 360;
break;
case GLUT_KEY_PAGE_DOWN:
camDistance += DELTA_DISTANCE;
break;
case GLUT_KEY_PAGE_UP:
camDistance -= (camDistance > DISTANCE_MIN) ? DELTA_DISTANCE : 0.0;
break;
default:
break;
}
glutPostRedisplay();
}
int main(int argc, char **argv)
{
if (argc == 1)
{
std::cout << "No obj file to load, displaying an emply scene with the reference system" << std::endl;
std::cout << "Usage:\n\t" + std::string(argv[0]) + " <obj file>" << std::endl;
}
// set window values
win.width = 1024;
win.height = 760;
win.title = string("OpenGL/GLUT OBJ Loader.");
win.field_of_view_angle = 45;
win.z_near = 0.25f;
win.z_far = 500.0f;
// initialize and run program
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH);
glutInitWindowSize(win.width, win.height);
glutCreateWindow(win.title.c_str());
glutDisplayFunc(display);
glutReshapeFunc(reshape);
// glutIdleFunc( display ); // register Idle Function
glutKeyboardFunc(keyboard);
glutSpecialFunc(arrows);
initialize();
//***********************************************
// Load the obj model from file
//***********************************************
obj.load(argv[1]);
//***********************************************
// Make it unitary
//***********************************************
obj.unitizeModel();
printKeyboardHelp();
glutMainLoop();
return EXIT_SUCCESS;
}
// Function called every time the main window is resized
void reshape(int width, int height)
{
// define the viewport transformation;
glViewport(0, 0, width, height);
GLfloat aspect = (GLfloat)win.width / win.height;
if (aspect > 1.0f) // w>h
{
// if (width < height)
// {
// glViewport(0,(height-width/aspect)/2, width, width/aspect);
// cout << "a" << endl;
// }
// else if ( width/height > aspect )
// {
// glViewport((width-height*aspect)/2,0,height*aspect,height);
// cout << "b" << endl;
// }
// else
if (width / height < aspect)
{
glViewport(0, (height - width / aspect) / 2, width, width / aspect);
// cout << "c " << (height-width/aspect)/2 << endl;
}
else
{
glViewport((width - height * aspect) / 2, 0, height * aspect, height);
// cout << "d " << (width-height*aspect)/2 << endl;
}
}
else // h>h
{
// if (height < width)
// {
// glViewport((width-height*aspect)/2,0, height*aspect, height);
// cout << "d "<< endl;
// }
// else if ( width/height > aspect )
// {
// glViewport((width-height*aspect)/2,0,height*aspect,height);
// cout << "e" << endl;
// }
// else
{
glViewport((width - height * aspect) / 2, 0, height * aspect, height);
cout << "d " << (width - height * aspect) / 2 << endl;
}
}
}
void DrawAxis(float scale)
{
glPushMatrix();
glDisable(GL_LIGHTING);
glScalef(scale, scale, scale);
glBegin(GL_LINES);
glColor3f(1.0, 0.0, 0.0);
/* X axis */
glVertex3f(0.0, 0.0, 0.0);
glVertex3f(1.0, 0.0, 0.0);
/* Letter X */
glVertex3f(.8f, 0.05f, 0.0); // "backslash""
glVertex3f(1.0, 0.25f, 0.0);
glVertex3f(0.8f, .25f, 0.0); // "slash"
glVertex3f(1.0, 0.05f, 0.0);
/* Y axis */
glColor3f(0.0, 1.0, 0.0);
glVertex3f(0.0, 0.0, 0.0);
glVertex3f(0.0, 1.0, 0.0);
// z-axis
glColor3f(0.0, 0.0, 1.0);
glVertex3f(0.0, 0.0, 0.0);
glVertex3f(0.0, 0.0, 1.0);
/* Letter Z */
glVertex3f(0.0, 0.05f, 0.8); // bottom horizontal leg
glVertex3f(0.0, 0.05f, 1.0);
glVertex3f(0.0, 0.05f, 1.0); // slash
glVertex3f(0.0, 0.25f, 0.8);
glVertex3f(0.0, 0.25f, 0.8); // upper horizontal leg
glVertex3f(0.0, 0.25f, 1.0);
glEnd();
glEnable(GL_LIGHTING);
glColor3f(1.0, 1.0, 1.0);
glPopMatrix();
}
void initialize()
{
glMatrixMode(GL_MODELVIEW);
glShadeModel(GL_SMOOTH);
glEnable(GL_CULL_FACE);
glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_LEQUAL);
glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
glEnable(GL_NORMALIZE);
glMatrixMode(GL_PROJECTION);
glViewport(0, 0, win.width, win.height);
GLfloat aspect = (GLfloat)win.width / win.height;
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(win.field_of_view_angle, aspect, win.z_near, win.z_far);
glMatrixMode(GL_MODELVIEW);
}

563
TP5/message.txt Normal file
View file

@ -0,0 +1,563 @@
/**
* @file ObjModel.cpp
* @author Simone Gasparini <simone.gasparini@enseeiht.fr>
* @version 1.0
*
* @section LICENSE
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* @section DESCRIPTION
*
* Simple Class to load and draw 3D objects from OBJ files
* Using triangles and normals as static object. No texture mapping.
* OBJ files must be triangulated!!!
*
*/
#include "ObjModel.hpp"
#include <iostream>
#include <fstream>
#include <cstdio>
#include <cstring>
#include <cassert>
//#include <stdlib.h>
#include <sstream>
#include <string>
#include <vector>
#include <cmath>
using namespace std;
/**
* Load the OBJ data from file
* @param filename The name of the OBJ file
* @return 0 if everything went well
*/
int ObjModel::load(char *filename)
{
string line;
ifstream objFile(filename);
// If obj file is open, continue
if (objFile.is_open())
{
// Start reading file data
while (!objFile.eof())
{
// Get a line from file
getline(objFile, line);
// If the first character is a simple 'v'...
// PRINTVAR( line );
if ((line.c_str()[0] == 'v') && (line.c_str()[1] == ' ')) // to drop all the vn and vn lines
{
// PRINTVAR( line );
// Read 3 floats from the line: X Y Z and store them in the corresponding place in _vertices
point3d p;
sscanf(line.c_str(), "v %f %f %f ", &p.x, &p.y, &p.z);
//**************************************************
// add the new point to the list of the vertices
// and its normal to the list of normals: for the time
// being it is a [0, 0 ,0] normal.
//**************************************************
_vertices.push_back(p);
_normals.push_back((vec3d){0.0f, 0.0f, 0.0f});
// update the bounding box, if it is the first vertex simply
// set the bb to it
if (_vertices.size() == 1)
{
_bb.set(p);
}
else
{
// otherwise add the point
_bb.add(p);
}
}
// If the first character is a 'f'...
if (line.c_str()[0] == 'f')
{
face t;
const auto ok = parseFaceString(line, t);
if (!ok)
throw std::runtime_error("Error while reading line: " + line);
//**************************************************
// correct the indices: OBJ starts counting from 1, in C the arrays starts at 0...
//**************************************************
sscanf(line.c_str(), "v %d %d %d", &t.v1, &t.v2, &t.v3);
t.v1--;
t.v2--;
t.v3--;
//**************************************************
// add it to the mesh
//**************************************************
_mesh.push_back(t);
//*********************************************************************
// Compute the normal of the face
//*********************************************************************
vec3d n;
computeNormal(_vertices[t.v1], _vertices[t.v2], _vertices[t.v3], n);
//*********************************************************************
// Sum the normal of the face to each vertex normal
//*********************************************************************
_normals[t.v1] += n * angleAtVertex(_vertices[t.v1], _vertices[t.v2], _vertices[t.v3]);
_normals[t.v2] += n * angleAtVertex(_vertices[t.v2], _vertices[t.v1], _vertices[t.v3]);
_normals[t.v3] += n * angleAtVertex(_vertices[t.v3], _vertices[t.v2], _vertices[t.v1]);
}
}
cerr << "Found :\n\tNumber of triangles (_indices) " << _mesh.size() << "\n\tNumber of Vertices: " << _vertices.size() << "\n\tNumber of Normals: " << _normals.size() << endl;
// PRINTVAR( _mesh );
// PRINTVAR( _vertices );
// PRINTVAR( _normals );
//*********************************************************************
// normalize the normals of each vertex
//*********************************************************************
for (vec3d n : _normals)
{
n.normalize();
}
// PRINTVAR( _normals );
// Close OBJ file
objFile.close();
}
else
{
cout << "Unable to open file";
}
cout << "Object loaded with " << _vertices.size() << " vertices and " << _mesh.size() << " faces" << endl;
cout << "Bounding box : pmax=" << _bb.pmax << " pmin=" << _bb.pmin << endl;
return 0;
}
/**
* Draw the wireframe of the model
*
* @param vertices The list of vertices
* @param mesh The mesh as a list of faces, each face is a tripleIndex of vertex indices
* @param params The rendering parameters
*/
void ObjModel::drawWireframe(const std::vector<point3d> &vertices, const std::vector<face> &mesh, const RenderingParameters &params) const
{
//**************************************************
// we first need to disable the lighting in order to
// draw colored segments
//**************************************************
glDisable(GL_LIGHTING);
// if we are displaying the object with colored faces
if (params.solid)
{
// use black ticker lines
glColor3f(0, 0, 0);
glLineWidth(2);
}
else
{
// otherwise use white thinner lines for wireframe only
glColor3f(.8, .8, .8);
glLineWidth(.21);
}
//**************************************************
// for each face of the mesh...
//**************************************************
for (auto &f : mesh)
{
//**************************************************
// draw the contour of the face as a GL_LINE_LOOP
//**************************************************
glBegin(GL_LINE_LOOP);
glVertex3f(vertices[f.v1].x, vertices[f.v1].y, vertices[f.v1].z);
glVertex3f(vertices[f.v2].x, vertices[f.v2].y, vertices[f.v2].z);
glVertex3f(vertices[f.v3].x, vertices[f.v3].y, vertices[f.v3].z);
glEnd();
}
//**************************************************
// re-enable the lighting
//**************************************************
glEnable(GL_LIGHTING);
}
/**
* Calculate the normal of a triangular face defined by three points
*
* @param[in] v1 the first vertex
* @param[in] v2 the second vertex
* @param[in] v3 the third vertex
* @param[out] norm the normal
*/
void ObjModel::computeNormal(const point3d &v1, const point3d &v2, const point3d &v3, vec3d &norm) const
{
//**************************************************
// compute the cross product between two edges of the triangular face
//**************************************************
norm = (v2 - v1).cross(v3 - v1);
//**************************************************
// remember to normalize the result
//**************************************************
norm.normalize();
}
/**
* Draw the faces using the computed normal of each face
*
* @param vertices The list of vertices
* @param mesh The list of face, each face containing the indices of the vertices
* @param params The rendering parameters
*/
void ObjModel::drawFlatFaces(const std::vector<point3d> &vertices, const std::vector<face> &mesh, const RenderingParameters &params) const
{
// shading model to use
if (params.smooth)
{
glShadeModel(GL_SMOOTH);
}
else
{
glShadeModel(GL_FLAT);
}
//**************************************************
// for each face
//**************************************************
std::vector<face> subList(&mesh[params.slice], &mesh[params.slice + 1]);
for (auto &f : mesh)
{
//**************************************************
// Compute the normal to the face and then draw the
// faces as GL_TRIANGLES assigning the proper normal
//**************************************************
vec3d normal;
computeNormal(vertices[f.v1], vertices[f.v2], vertices[f.v3], normal);
glNormal3f(normal.x, normal.y, normal.z);
glBegin(GL_TRIANGLES);
glVertex3f(vertices[f.v1].x, vertices[f.v1].y, vertices[f.v1].z);
glVertex3f(vertices[f.v2].x, vertices[f.v2].y, vertices[f.v2].z);
glVertex3f(vertices[f.v3].x, vertices[f.v3].y, vertices[f.v3].z);
glEnd();
}
}
/**
* Draw the model using the vertex indices
*
* @param vertices The vertices
* @param indices The list of the faces, each face containing the 3 indices of the vertices
* @param vertexNormals The list of normals associated to each vertex
* @param params The rendering parameters
*/
void ObjModel::drawSmoothFaces(const std::vector<point3d> &vertices,
const std::vector<face> &mesh,
std::vector<vec3d> &vertexNormals,
const RenderingParameters &params) const
{
if (params.smooth)
{
glShadeModel(GL_SMOOTH);
}
else
{
glShadeModel(GL_FLAT);
}
//****************************************
// Enable vertex arrays
//****************************************
glEnable(GL_VERTEX_ARRAY);
//****************************************
// Enable normal arrays
//****************************************
glEnable(GL_NORMAL_ARRAY);
//****************************************
// Normal pointer to normal array
//****************************************
glNormalPointer(GL_FLOAT, 0, &vertexNormals[0]);
//****************************************
// Vertex pointer to Vertex array
//****************************************
glVertexPointer(3, GL_FLOAT, 0, &vertices[0]);
//****************************************
// Draw the faces
//****************************************
glDrawElements(GL_TRIANGLES, 3 * mesh.size(), GL_UNSIGNED_INT, &mesh[0]);
//****************************************
// Disable vertex arrays
//****************************************
glDisable(GL_VERTEX_ARRAY);
//****************************************
// Disable normal arrays
//****************************************
glDisable(GL_NORMAL_ARRAY);
}
/**
* Compute the subdivision of the input mesh by applying one step of the Loop algorithm
*
* @param[in] origVert The list of the input vertices
* @param[in] origMesh The input mesh (the vertex indices for each face/triangle)
* @param[out] destVert The list of the new vertices for the subdivided mesh
* @param[out] destMesh The new subdivided mesh (the vertex indices for each face/triangle)
* @param[out] destNorm The new list of normals for each new vertex of the subdivided mesh
*/
void ObjModel::loopSubdivision(const std::vector<point3d> &origVert, //!< the original vertices
const std::vector<face> &origMesh, //!< the original mesh
std::vector<point3d> &destVert, //!< the new vertices
std::vector<face> &destMesh, //!< the new mesh
std::vector<vec3d> &destNorm) const //!< the new normals
{
// copy the original vertices in destVert
destVert = origVert;
// start fresh with the new mesh
destMesh.clear();
// PRINTVAR(destVert);
// PRINTVAR(origVert);
// create a list of the new vertices creates with the reference to the edge
EdgeList newVertices;
//*********************************************************************
// for each face
//*********************************************************************
for (auto &f : origMesh)
{
//*********************************************************************
// get the indices of the triangle vertices
//*********************************************************************
int v1 = f.v1;
int v2 = f.v2;
int v3 = f.v3;
//*********************************************************************
// for each edge get the index of the vertex of the midpoint using getNewVertex
//*********************************************************************
const int a = getNewVertex(edge{v1, v2}, destVert, origMesh, newVertices);
const int b = getNewVertex(edge{v2, v3}, destVert, origMesh, newVertices);
const int c = getNewVertex(edge{v3, v1}, destVert, origMesh, newVertices);
//*********************************************************************
// create the four new triangles
// BE CAREFUL WITH THE VERTEX ORDER!!
// v2
// /\
// / \
// / \
// a ---- b
// / \ /\
// / \ / \
// / \ / \
// v1 ---- c ---- v3
//
// the original triangle was v1-v2-v3, use the same clock-wise order for the other
// hence v1-a-c, a-b-c and so on
//*********************************************************************
destMesh.push_back(face{v1, a, c});
destMesh.push_back(face{a, v2, b});
destMesh.push_back(face{c, b, v3});
destMesh.push_back(face{a, b, c});
}
//*********************************************************************
// Update each "old" vertex using the Loop coefficients. A smart way to do
// so is to think in terms of faces than the single vertex: for each face
// we update each of the 3 vertices using the Loop formula wrt the other 2 and
// sum it to a temporary copy tmp of the vertices (which is initialized to
// [0 0 0] at the beginning). We also keep a record of the occurrence of each vertex.
// At then end, to get the final vertices we just need to divide each vertex
// in tmp by its occurrence
//********************************************************************
// A list containing the occurrence of each vertex
vector<size_t> occurrences(origVert.size(), 0);
// A list of the same size as origVert with all the elements initialized to [0 0 0]
vector<point3d> tmp(origVert.size());
//*********************************************************************
// for each face
//*********************************************************************
for (auto &f : origMesh)
{
//*********************************************************************
// consider each of the 3 vertices:
// 1) increment its occurrence
// 2) apply Loop update with the other 2 vertices of the face
// BE CAREFUL WITH THE COEFFICIENT OF THE OTHER 2 VERTICES!... consider
// how many times each vertex is summed in the general case...
//*********************************************************************
// 1) increment occurrences
occurrences[f.v1] += 2;
occurrences[f.v2] += 2;
occurrences[f.v3] += 2;
// 2) apply Loop update
tmp[f.v1] += (destVert[f.v2] + destVert[f.v3]);
tmp[f.v2] += (destVert[f.v1] + destVert[f.v3]);
tmp[f.v3] += (destVert[f.v1] + destVert[f.v2]);
}
//*********************************************************************
// To obtain the new vertices, divide each vertex by its occurrence value
//*********************************************************************
for (int i = 0; i < tmp.size(); i++)
{
assert(occurrences[i] != 0);
destVert[i] = 5.0f / 8.0f * destVert[i] + 3.0f / 8.0f / occurrences[i] * tmp[i];
}
// PRINTVAR(destVert);
// redo the normals, reset and create a list of normals of the same size as
// the vertices, each normal set to [0 0 0]
destNorm.clear();
destNorm = vector<vec3d>(destVert.size());
//*********************************************************************
// Recompute the normals for each face
//*********************************************************************
for (auto &f : destMesh)
{
//*********************************************************************
// Calculate the normal of the triangles, it will be the same for each vertex
//*********************************************************************
vec3d n;
computeNormal(destVert[f.v1], destVert[f.v2], destVert[f.v3], n);
//*********************************************************************
// Sum the normal of the face to each vertex normal using the angleAtVertex as weight
//*********************************************************************
destNorm[f.v1] += n * angleAtVertex(destVert[f.v1], destVert[f.v2], destVert[f.v3]);
destNorm[f.v2] += n * angleAtVertex(destVert[f.v2], destVert[f.v1], destVert[f.v3]);
destNorm[f.v3] += n * angleAtVertex(destVert[f.v3], destVert[f.v2], destVert[f.v1]);
}
//*********************************************************************
// normalize the normals of each vertex
//*********************************************************************
for (vec3d n : destNorm)
{
n.normalize();
}
}
/**
* For a given edge it returns the index of the new vertex created on its middle point.
* If such vertex already exists it just returns the its index; if it does not exist
* it creates it in vertList along it's normal and return the index
*
* @param[in] e the edge
* @param[in,out] vertList the list of vertices
* @param[in] mesh the list of triangles
* @param[in,out] newVertList The list of the new vertices added so far
* @return the index of the new vertex or the one that has been already created for that edge
* @see EdgeList
*/
idxtype ObjModel::getNewVertex(const edge &e,
std::vector<point3d> &vertList,
const std::vector<face> &mesh,
EdgeList &newVertList) const
{
// PRINTVAR(e);
// PRINTVAR(newVertList);
//*********************************************************************
// if the egde is NOT contained in the new vertex list (see EdgeList.contains() method)
//*********************************************************************
if (!newVertList.contains(e))
{
//*********************************************************************
// generate new index (vertex.size)
//*********************************************************************
int newIndex = vertList.size();
//*********************************************************************
// add the edge and index to the newVertList
//*********************************************************************
newVertList.add(e, newIndex);
// generate new vertex
point3d nvert; //!< this will contain the new vertex
idxtype oppV1; //!< the index of the first "opposite" vertex
idxtype oppV2; //!< the index of the second "opposite" vertex (if it exists)
//*********************************************************************
// check if it is a boundary edge, ie check if there is another triangle
// sharing this edge and if so get the index of its "opposite" vertex
//*********************************************************************
if (!isBoundaryEdge(e, mesh, oppV1, oppV2))
{
// if it is not a boundary edge create the new vertex
//*********************************************************************
// the new vertex is the linear combination of the two extrema of
// the edge V1 and V2 and the two opposite vertices oppV1 and oppV2
// Using the loop coefficient the new vertex is
// nvert = 3/8 (V1+V2) + 1/8(oppV1 + oppV2)
//
// REMEMBER THAT IN THE CODE OPPV1 AND OPPV2 ARE INDICES, NOT VERTICES!!!
//*********************************************************************
nvert = 3.0f / 8.0f * (vertList[e.first] + vertList[e.second]) + 1.0f / 8.0f * (vertList[oppV1] + vertList[oppV2]);
}
else
{
//*********************************************************************
// otherwise it is a boundary edge then the vertex is the linear combination of the
// two extrema
//*********************************************************************
nvert = 0.5f * vertList[e.first] + 0.5f * vertList[e.second];
}
//*********************************************************************
// append the new vertex to the list of vertices
//*********************************************************************
vertList.push_back(nvert);
//*********************************************************************
// return the index of the new vertex
//*********************************************************************
return newIndex;
}
else
// else we don't need to do anything, just return the associated index of the
// already existing vertex
{
//*********************************************************************
// get and return the index of the vertex
//*********************************************************************
return newVertList.getIndex(e);
}
// this is just to avoid compilation errors at the beginning
// execution should normally never reach here
// the return instructions go inside each branch of the if - else above
std::cerr << "WARNING: the subdivision may not be implemented correctly" << std::endl;
return 0;
}
#include "ObjModel.cxx"

82
TP5/testEdge.cpp Executable file
View file

@ -0,0 +1,82 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "core.hpp"
#include "ObjModel.hpp"
#include <sstream>
#include <cstdio>
#include <unordered_map>
std::unordered_map< int, int > map;
int main(int argc, char **argv)
{
edge2vertex mylist;
mylist.insert(std::pair<edge,GLushort>(edge(6,5),6));
mylist.insert(std::pair<edge,GLushort>(edge(1,2),1));
mylist.insert(std::pair<edge,GLushort>(edge(2,1),4));
mylist.insert(std::pair<edge,GLushort>(edge(1,4),5));
mylist.insert(std::pair<edge,GLushort>(edge(3,5),6));
// mylist[edge(3,2)] = 10;
PRINTVAR(mylist);
PRINTVAR(mylist.size());
bool res = (mylist.find(edge(5,3))) != mylist.end();
PRINTVAR(res);
PRINTVAR(((mylist.find(edge(4,1))) != mylist.end()));
PRINTVAR(((mylist.find(edge(3,2))) != mylist.end()));
PRINTVAR(((mylist.find(edge(1,2))) != mylist.end()));
PRINTVAR(((mylist.find(edge(2,1))) != mylist.end()));
PRINTVAR(mylist[edge(4,1)]);
PRINTVAR(mylist[edge(2,1)]);
PRINTVAR(mylist[edge(3,5)]);
PRINTVAR(((mylist.find(edge(5,3))) != mylist.end()));
PRINTVAR(mylist[edge(6,5)]);
PRINTVAR(((mylist.find(edge(5,6))) != mylist.end()));
mylist[edge(0,1)]=100;
PRINTVAR(mylist);
std::string line("f 34//23 11//65 123//98");
idxtype a;
face out;
std::string b;
// std::stringstream parser (line);
// parser >> b
// >> out.v1 >> b >> a
// >> out.v2 >> b >> a
// >> out.v3 >> b >> a;
// PRINTVAR(parser.fail());
// std::cout << out << a << b << std::endl;
PRINTVAR(line);
PRINTVAR( (sscanf(line.c_str(), "f %u//%u %u//%u %u//%u", &(out.v1), &a, &(out.v2), &a, &(out.v3), &a) == 6) );
std::cout << out << a << b << std::endl;
PRINTVAR(v3f(1,2,4)*10.f);
PRINTVAR(10.f*v3f(1,2,4));
PRINTVAR((edge(3,5)==edge(3,5)));
PRINTVAR((edge(3,5)==edge(5,3)));
PRINTVAR((edge(1,2)==edge(3,5)));
idxtype idx;
PRINTVAR((face(1,2,3).containsEdge( edge(3,5), idx )));
PRINTVAR( idx );
PRINTVAR((face(1,2,3).containsEdge( edge(2,3), idx )));
PRINTVAR( idx );
PRINTVAR((face(1,2,3).containsEdge( edge(3,2), idx )));
PRINTVAR( idx );
PRINTVAR((face(1,2,3).containsEdge( edge(0,2), idx )));
PRINTVAR( idx );
PRINTVAR((face(1,2,3).containsEdge( edge(3,1), idx )));
PRINTVAR( idx );
}

94
TP5/testEdgeList.cpp Executable file
View file

@ -0,0 +1,94 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/*
* File: testEdgeList.cpp
* Author: Simone Gasparini
*
* Created on October 25, 2014, 1:26 PM
*/
#include "core.hpp"
#include "ObjModel.hpp"
#include <sstream>
#include <cstdio>
#include <cstdlib>
#include <cassert>
using namespace std;
/*
*
*/
int main( int argc, char** argv )
{
EdgeList list;
int numTrial;
if(argc == 2)
{
numTrial = atoi(argv[1]);
}
else
{
numTrial = 1000;
}
idxtype maxIdx = numTrial/5;
vector<edge> listEdges;
vector<idxtype> listIdx;
// fill up the list with random edges
cout << "Filling up data..." << endl;
for(size_t i =0; i < numTrial; ++i)
{
edge e(rand() % maxIdx, rand() % maxIdx);
listEdges.push_back(e);
listIdx.push_back(i);
list.add(e, i);
}
// test
cout << "Testing..." << endl;
for(size_t i =0; i < numTrial; ++i)
{
edge e = listEdges[i];
// test it contains the edges we inserted
assert(list.contains( e ));
// reflexive test (inverted indices))
assert(list.contains( edge(e.second, e.first)));
idxtype res = list.getIndex( e );
// if the index is different
if( res != listIdx[i] )
{
// then there must be another edge with the same indices in the reversed order
bool found = false;
for( size_t j= 0; (j < numTrial) && (!found); ++j )
{
// avoid to check for the current edge
if(j != i )
{
found = (listEdges[j] == e);
if(found)
{
PRINTVAR(e);
PRINTVAR(listEdges[j]);
PRINTVAR(i);
PRINTVAR(j)
}
}
}
assert(found);
}
}
cout << "Test passed!" << endl;
}