Format
This commit is contained in:
parent
0a2e37a04d
commit
2100a9c221
8 changed files with 791 additions and 638 deletions
1
assignment-2b/.gitignore
vendored
1
assignment-2b/.gitignore
vendored
|
@ -1,2 +1,3 @@
|
||||||
/build
|
/build
|
||||||
.DS_Store
|
.DS_Store
|
||||||
|
.cache
|
||||||
|
|
148
assignment-2b/CMakeLists.txt
Executable file → Normal file
148
assignment-2b/CMakeLists.txt
Executable file → Normal file
|
@ -1,71 +1,79 @@
|
||||||
# Set the minimum required version of cmake for this project
|
# Set the minimum required version of cmake for this project
|
||||||
cmake_minimum_required (VERSION 3.1)
|
cmake_minimum_required (VERSION 3.1)
|
||||||
|
|
||||||
# Create a project called 'HW2b'
|
|
||||||
project(HW2b)
|
|
||||||
|
|
||||||
# Find OpenGL, set link library names and include paths
|
|
||||||
find_package(OpenGL REQUIRED)
|
|
||||||
set(OPENGL_LIBRARIES ${OPENGL_gl_LIBRARY} ${OPENGL_glu_LIBRARY})
|
|
||||||
set(OPENGL_INCLUDE_DIRS ${OPENGL_INCLUDE_DIR})
|
|
||||||
include_directories(${OPENGL_INCLUDE_DIRS})
|
|
||||||
|
|
||||||
# Disable building some of the extra things GLFW has (examples, tests, docs)
|
|
||||||
set(GLFW_BUILD_EXAMPLES OFF CACHE BOOL " " FORCE)
|
|
||||||
set(GLFW_BUILD_TESTS OFF CACHE BOOL " " FORCE)
|
|
||||||
set(GLFW_BUILD_DOCS OFF CACHE BOOL " " FORCE)
|
|
||||||
|
|
||||||
add_definitions( -DMY_SRC_DIR="${CMAKE_CURRENT_SOURCE_DIR}/src/" )
|
|
||||||
add_definitions( -DMY_DATA_DIR="${CMAKE_CURRENT_SOURCE_DIR}/data/" )
|
|
||||||
|
|
||||||
# Run cmake on the CMakeLists.txt file found inside of the GLFW directory
|
|
||||||
add_subdirectory(ext/glfw)
|
|
||||||
|
|
||||||
# Make a list of all the source files
|
|
||||||
set(
|
|
||||||
SOURCES
|
|
||||||
src/main.cpp
|
|
||||||
ext/glad/src/glad.c
|
|
||||||
)
|
|
||||||
|
|
||||||
# Make a list of all the header files
|
|
||||||
set(
|
|
||||||
INCLUDES
|
|
||||||
src/shader.hpp
|
|
||||||
src/trimesh.hpp
|
|
||||||
)
|
|
||||||
|
|
||||||
# Make a list of all of the directories to look in when doing #include "whatever.h"
|
|
||||||
set(
|
|
||||||
INCLUDE_DIRS
|
|
||||||
ext/
|
|
||||||
ext/glfw/include
|
|
||||||
ext/glad/include
|
|
||||||
)
|
|
||||||
|
|
||||||
# Make a list of the libraries
|
# Create a project called 'HW2b'
|
||||||
set(
|
project(HW2b)
|
||||||
LIBS
|
|
||||||
glfw
|
# Generate the `compile_commands.json` file.
|
||||||
${OPENGL_LIBRARIES}
|
set(CMAKE_EXPORT_COMPILE_COMMANDS ON CACHE INTERNAL "")
|
||||||
)
|
|
||||||
|
if(CMAKE_EXPORT_COMPILE_COMMANDS)
|
||||||
# Define what we are trying to produce here (an executable),
|
set(CMAKE_CXX_STANDARD_INCLUDE_DIRECTORIES
|
||||||
# and what items are needed to create it (the header and source files)
|
${CMAKE_CXX_IMPLICIT_INCLUDE_DIRECTORIES})
|
||||||
add_executable(${PROJECT_NAME} ${SOURCES} ${INCLUDES})
|
endif()
|
||||||
|
|
||||||
# Tell cmake which directories to look in when you #include a file
|
# Find OpenGL, set link library names and include paths
|
||||||
# Equivalent to the "-I" option for g++
|
find_package(OpenGL REQUIRED)
|
||||||
include_directories(${INCLUDE_DIRS})
|
set(OPENGL_LIBRARIES ${OPENGL_gl_LIBRARY} ${OPENGL_glu_LIBRARY})
|
||||||
|
set(OPENGL_INCLUDE_DIRS ${OPENGL_INCLUDE_DIR})
|
||||||
# Tell cmake which libraries to link to
|
include_directories(${OPENGL_INCLUDE_DIRS})
|
||||||
# Equivalent to the "-l" option for g++
|
|
||||||
target_link_libraries(${PROJECT_NAME} PRIVATE ${LIBS})
|
# Disable building some of the extra things GLFW has (examples, tests, docs)
|
||||||
|
set(GLFW_BUILD_EXAMPLES OFF CACHE BOOL " " FORCE)
|
||||||
# For Visual Studio only
|
set(GLFW_BUILD_TESTS OFF CACHE BOOL " " FORCE)
|
||||||
if (MSVC)
|
set(GLFW_BUILD_DOCS OFF CACHE BOOL " " FORCE)
|
||||||
# Do a parallel compilation of this project
|
|
||||||
target_compile_options(${PROJECT_NAME} PRIVATE "/MP")
|
add_definitions( -DMY_SRC_DIR="${CMAKE_CURRENT_SOURCE_DIR}/src/" )
|
||||||
# Have this project be the default startup project (the one to build/run when hitting F5)
|
add_definitions( -DMY_DATA_DIR="${CMAKE_CURRENT_SOURCE_DIR}/data/" )
|
||||||
set_property(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT ${PROJECT_NAME})
|
|
||||||
endif()
|
# Run cmake on the CMakeLists.txt file found inside of the GLFW directory
|
||||||
|
add_subdirectory(ext/glfw)
|
||||||
|
|
||||||
|
# Make a list of all the source files
|
||||||
|
set(
|
||||||
|
SOURCES
|
||||||
|
src/main.cpp
|
||||||
|
ext/glad/src/glad.c
|
||||||
|
)
|
||||||
|
|
||||||
|
# Make a list of all the header files
|
||||||
|
set(
|
||||||
|
INCLUDES
|
||||||
|
src/shader.hpp
|
||||||
|
src/trimesh.hpp
|
||||||
|
)
|
||||||
|
|
||||||
|
# Make a list of all of the directories to look in when doing #include "whatever.h"
|
||||||
|
set(
|
||||||
|
INCLUDE_DIRS
|
||||||
|
ext/
|
||||||
|
ext/glfw/include
|
||||||
|
ext/glad/include
|
||||||
|
)
|
||||||
|
|
||||||
|
# Make a list of the libraries
|
||||||
|
set(
|
||||||
|
LIBS
|
||||||
|
glfw
|
||||||
|
${OPENGL_LIBRARIES}
|
||||||
|
)
|
||||||
|
|
||||||
|
# Define what we are trying to produce here (an executable),
|
||||||
|
# and what items are needed to create it (the header and source files)
|
||||||
|
add_executable(${PROJECT_NAME} ${SOURCES} ${INCLUDES})
|
||||||
|
|
||||||
|
# Tell cmake which directories to look in when you #include a file
|
||||||
|
# Equivalent to the "-I" option for g++
|
||||||
|
include_directories(${INCLUDE_DIRS})
|
||||||
|
|
||||||
|
# Tell cmake which libraries to link to
|
||||||
|
# Equivalent to the "-l" option for g++
|
||||||
|
target_link_libraries(${PROJECT_NAME} PRIVATE ${LIBS})
|
||||||
|
|
||||||
|
# For Visual Studio only
|
||||||
|
if (MSVC)
|
||||||
|
# Do a parallel compilation of this project
|
||||||
|
target_compile_options(${PROJECT_NAME} PRIVATE "/MP")
|
||||||
|
# Have this project be the default startup project (the one to build/run when hitting F5)
|
||||||
|
set_property(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT ${PROJECT_NAME})
|
||||||
|
endif()
|
||||||
|
|
1
assignment-2b/compile_commands.json
Symbolic link
1
assignment-2b/compile_commands.json
Symbolic link
|
@ -0,0 +1 @@
|
||||||
|
build/compile_commands.json
|
|
@ -1,12 +1,14 @@
|
||||||
// This template code was originally written by Matt Overby while a TA for CSci5607
|
// This template code was originally written by Matt Overby while a TA for
|
||||||
|
// CSci5607
|
||||||
|
|
||||||
|
#include "glad/glad.h"
|
||||||
|
|
||||||
// The loaders are included by glfw3 (glcorearb.h) if we are not using glew.
|
// The loaders are included by glfw3 (glcorearb.h) if we are not using glew.
|
||||||
#include "glad/glad.h"
|
|
||||||
#include "GLFW/glfw3.h"
|
#include "GLFW/glfw3.h"
|
||||||
|
|
||||||
// Includes
|
// Includes
|
||||||
#include "trimesh.hpp"
|
|
||||||
#include "shader.hpp"
|
#include "shader.hpp"
|
||||||
|
#include "trimesh.hpp"
|
||||||
#include <cstring> // memcpy
|
#include <cstring> // memcpy
|
||||||
|
|
||||||
// Constants
|
// Constants
|
||||||
|
@ -15,269 +17,319 @@
|
||||||
|
|
||||||
class Mat4x4 {
|
class Mat4x4 {
|
||||||
public:
|
public:
|
||||||
|
float m[16];
|
||||||
|
|
||||||
float m[16];
|
// clang-format off
|
||||||
|
Mat4x4(){ // Default: Identity
|
||||||
|
m[0] = 1.f; m[4] = 0.f; m[8] = 0.f; m[12] = 0.f;
|
||||||
|
m[1] = 0.f; m[5] = 1.f; m[9] = 0.f; m[13] = 0.f;
|
||||||
|
m[2] = 0.f; m[6] = 0.f; m[10] = 1.f; m[14] = 0.f;
|
||||||
|
m[3] = 0.f; m[7] = 0.f; m[11] = 0.f; m[15] = 1.f;
|
||||||
|
}
|
||||||
|
// clang-format on
|
||||||
|
|
||||||
Mat4x4(){ // Default: Identity
|
// clang-format off
|
||||||
m[0] = 1.f; m[4] = 0.f; m[8] = 0.f; m[12] = 0.f;
|
void make_identity(){
|
||||||
m[1] = 0.f; m[5] = 1.f; m[9] = 0.f; m[13] = 0.f;
|
m[0] = 1.f; m[4] = 0.f; m[8] = 0.f; m[12] = 0.f;
|
||||||
m[2] = 0.f; m[6] = 0.f; m[10] = 1.f; m[14] = 0.f;
|
m[1] = 0.f; m[5] = 1.f; m[9] = 0.f; m[13] = 0.f;
|
||||||
m[3] = 0.f; m[7] = 0.f; m[11] = 0.f; m[15] = 1.f;
|
m[2] = 0.f; m[6] = 0.f; m[10] = 1.f; m[14] = 0.f;
|
||||||
}
|
m[3] = 0.f; m[7] = 0.f; m[11] = 0.f; m[15] = 1.f;
|
||||||
|
}
|
||||||
|
// clang-format on
|
||||||
|
|
||||||
void make_identity(){
|
// clang-format off
|
||||||
m[0] = 1.f; m[4] = 0.f; m[8] = 0.f; m[12] = 0.f;
|
void print(){
|
||||||
m[1] = 0.f; m[5] = 1.f; m[9] = 0.f; m[13] = 0.f;
|
std::cout << m[0] << ' ' << m[4] << ' ' << m[8] << ' ' << m[12] << "\n";
|
||||||
m[2] = 0.f; m[6] = 0.f; m[10] = 1.f; m[14] = 0.f;
|
std::cout << m[1] << ' ' << m[5] << ' ' << m[9] << ' ' << m[13] << "\n";
|
||||||
m[3] = 0.f; m[7] = 0.f; m[11] = 0.f; m[15] = 1.f;
|
std::cout << m[2] << ' ' << m[6] << ' ' << m[10] << ' ' << m[14] << "\n";
|
||||||
}
|
std::cout << m[3] << ' ' << m[7] << ' ' << m[11] << ' ' << m[15] << "\n";
|
||||||
|
}
|
||||||
|
// clang-format on
|
||||||
|
|
||||||
void print(){
|
void make_scale(float x, float y, float z) {
|
||||||
std::cout << m[0] << ' ' << m[4] << ' ' << m[8] << ' ' << m[12] << "\n";
|
make_identity();
|
||||||
std::cout << m[1] << ' ' << m[5] << ' ' << m[9] << ' ' << m[13] << "\n";
|
m[0] = x;
|
||||||
std::cout << m[2] << ' ' << m[6] << ' ' << m[10] << ' ' << m[14] << "\n";
|
m[5] = y;
|
||||||
std::cout << m[3] << ' ' << m[7] << ' ' << m[11] << ' ' << m[15] << "\n";
|
m[10] = z;
|
||||||
}
|
}
|
||||||
|
|
||||||
void make_scale(float x, float y, float z){
|
|
||||||
make_identity();
|
|
||||||
m[0] = x; m[5] = y; m[10] = z;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static inline const Vec3f operator*(const Mat4x4 &m, const Vec3f &v){
|
static inline const Vec3f operator*(const Mat4x4 &m, const Vec3f &v) {
|
||||||
Vec3f r( m.m[0]*v[0]+m.m[4]*v[1]+m.m[8]*v[2],
|
Vec3f r(m.m[0] * v[0] + m.m[4] * v[1] + m.m[8] * v[2],
|
||||||
m.m[1]*v[0]+m.m[5]*v[1]+m.m[9]*v[2],
|
m.m[1] * v[0] + m.m[5] * v[1] + m.m[9] * v[2],
|
||||||
m.m[2]*v[0]+m.m[6]*v[1]+m.m[10]*v[2] );
|
m.m[2] * v[0] + m.m[6] * v[1] + m.m[10] * v[2]);
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// Global state variables
|
// Global state variables
|
||||||
//
|
//
|
||||||
namespace Globals {
|
namespace Globals {
|
||||||
double cursorX, cursorY; // cursor positions
|
double cursorX, cursorY; // cursor positions
|
||||||
float win_width, win_height; // window size
|
float win_width, win_height; // window size
|
||||||
float aspect;
|
float aspect;
|
||||||
GLuint verts_vbo[1], colors_vbo[1], normals_vbo[1], faces_ibo[1], tris_vao;
|
GLuint verts_vbo[1], colors_vbo[1], normals_vbo[1], faces_ibo[1], tris_vao;
|
||||||
TriMesh mesh;
|
TriMesh mesh;
|
||||||
|
|
||||||
// Model, view and projection matrices, initialized to the identity
|
|
||||||
Mat4x4 model; // not used in this assignment; included for completeness only
|
|
||||||
Mat4x4 view;
|
|
||||||
Mat4x4 projection;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
// Model, view and projection matrices, initialized to the identity
|
||||||
|
Mat4x4 model; // not used in this assignment; included for completeness only
|
||||||
|
Mat4x4 view;
|
||||||
|
Mat4x4 projection;
|
||||||
|
} // namespace Globals
|
||||||
|
|
||||||
//
|
//
|
||||||
// Callbacks
|
// Callbacks
|
||||||
//
|
//
|
||||||
static void error_callback(int error, const char* description){ fprintf(stderr, "Error: %s\n", description); }
|
static void error_callback(int error, const char *description) {
|
||||||
|
fprintf(stderr, "Error: %s\n", description);
|
||||||
static void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods){
|
|
||||||
// Close on escape or Q
|
|
||||||
if( action == GLFW_PRESS ){
|
|
||||||
switch ( key ) {
|
|
||||||
case GLFW_KEY_ESCAPE: glfwSetWindowShouldClose(window, GL_TRUE); break;
|
|
||||||
case GLFW_KEY_Q: glfwSetWindowShouldClose(window, GL_TRUE); break;
|
|
||||||
// ToDo: update the viewing transformation matrix according to key presses
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void framebuffer_size_callback(GLFWwindow* window, int width, int height){
|
static void key_callback(GLFWwindow *window, int key, int scancode, int action,
|
||||||
Globals::win_width = float(width);
|
int mods) {
|
||||||
Globals::win_height = float(height);
|
// Close on escape or Q
|
||||||
Globals::aspect = Globals::win_width/Globals::win_height;
|
if (action == GLFW_PRESS) {
|
||||||
|
switch (key) {
|
||||||
glViewport(0,0,width,height);
|
case GLFW_KEY_ESCAPE:
|
||||||
|
glfwSetWindowShouldClose(window, GL_TRUE);
|
||||||
// ToDo: update the perspective matrix as the window size changes
|
break;
|
||||||
|
case GLFW_KEY_Q:
|
||||||
|
glfwSetWindowShouldClose(window, GL_TRUE);
|
||||||
|
break;
|
||||||
|
// ToDo: update the viewing transformation matrix according to key presses
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void framebuffer_size_callback(GLFWwindow *window, int width,
|
||||||
|
int height) {
|
||||||
|
Globals::win_width = float(width);
|
||||||
|
Globals::win_height = float(height);
|
||||||
|
Globals::aspect = Globals::win_width / Globals::win_height;
|
||||||
|
|
||||||
|
glViewport(0, 0, width, height);
|
||||||
|
|
||||||
|
// ToDo: update the perspective matrix as the window size changes
|
||||||
|
}
|
||||||
|
|
||||||
// Function to set up geometry
|
// Function to set up geometry
|
||||||
void init_scene();
|
void init_scene();
|
||||||
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// Main
|
// Main
|
||||||
//
|
//
|
||||||
int main(int argc, char *argv[]){
|
int main(int argc, char *argv[]) {
|
||||||
|
|
||||||
// Load the mesh
|
// Load the mesh
|
||||||
std::stringstream obj_file; obj_file << MY_DATA_DIR << "sibenik/sibenik.obj";
|
std::stringstream obj_file;
|
||||||
if( !Globals::mesh.load_obj( obj_file.str() ) ){ return 0; }
|
obj_file << MY_DATA_DIR << "sibenik/sibenik.obj";
|
||||||
Globals::mesh.print_details();
|
if (!Globals::mesh.load_obj(obj_file.str())) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
Globals::mesh.print_details();
|
||||||
|
|
||||||
// Forcibly scale the mesh vertices so that the entire model fits within a (-1,1) volume: the code below is a temporary measure that is needed to enable the entire model to be visible in the template app, before the student has defined the proper viewing and projection matrices
|
// Forcibly scale the mesh vertices so that the entire model fits within a
|
||||||
// This code should eventually be replaced by the use of an appropriate projection matrix
|
// (-1,1) volume: the code below is a temporary measure that is needed to
|
||||||
// FYI: the model dimensions are: center = (0,0,0); height: 30.6; length: 40.3; width: 17.0
|
// enable the entire model to be visible in the template app, before the
|
||||||
// find the extremum of the vertex locations (this approach works because the model is known to be centered; a more complicated method would be required in the general case)
|
// student has defined the proper viewing and projection matrices This code
|
||||||
float min, max, scale;
|
// should eventually be replaced by the use of an appropriate projection
|
||||||
min = Globals::mesh.vertices[0][0]; max = Globals::mesh.vertices[0][0];
|
// matrix FYI: the model dimensions are: center = (0,0,0); height: 30.6;
|
||||||
for( int i=0; i<Globals::mesh.vertices.size(); ++i ){
|
// length: 40.3; width: 17.0
|
||||||
if (Globals::mesh.vertices[i][0] < min) min = Globals::mesh.vertices[i][0];
|
// find the extremum of the vertex locations (this approach works because the
|
||||||
else if (Globals::mesh.vertices[i][0] > max) max = Globals::mesh.vertices[i][0];
|
// model is known to be centered; a more complicated method would be required
|
||||||
if (Globals::mesh.vertices[i][1] < min) min = Globals::mesh.vertices[i][1];
|
// in the general case)
|
||||||
else if (Globals::mesh.vertices[i][1] > max) max = Globals::mesh.vertices[i][1];
|
float min, max, scale;
|
||||||
if (Globals::mesh.vertices[i][2] < min) min = Globals::mesh.vertices[i][2];
|
min = Globals::mesh.vertices[0][0];
|
||||||
else if (Globals::mesh.vertices[i][2] > max) max = Globals::mesh.vertices[i][2];
|
max = Globals::mesh.vertices[0][0];
|
||||||
}
|
for (int i = 0; i < Globals::mesh.vertices.size(); ++i) {
|
||||||
// work with positive numbers
|
if (Globals::mesh.vertices[i][0] < min)
|
||||||
if (min < 0) min = -min;
|
min = Globals::mesh.vertices[i][0];
|
||||||
// scale so that the component that is most different from 0 is mapped to 1 (or -1); all other values will then by definition fall between -1 and 1
|
else if (Globals::mesh.vertices[i][0] > max)
|
||||||
if (max > min) scale = 1/max; else scale = 1/min;
|
max = Globals::mesh.vertices[i][0];
|
||||||
|
if (Globals::mesh.vertices[i][1] < min)
|
||||||
// scale the model vertices by brute force
|
min = Globals::mesh.vertices[i][1];
|
||||||
Mat4x4 mscale; mscale.make_scale( scale, scale, scale );
|
else if (Globals::mesh.vertices[i][1] > max)
|
||||||
for( int i=0; i<Globals::mesh.vertices.size(); ++i ){
|
max = Globals::mesh.vertices[i][1];
|
||||||
Globals::mesh.vertices[i] = mscale*Globals::mesh.vertices[i];
|
if (Globals::mesh.vertices[i][2] < min)
|
||||||
}
|
min = Globals::mesh.vertices[i][2];
|
||||||
// The above can be removed once a proper projection matrix is defined
|
else if (Globals::mesh.vertices[i][2] > max)
|
||||||
|
max = Globals::mesh.vertices[i][2];
|
||||||
|
}
|
||||||
|
// work with positive numbers
|
||||||
|
if (min < 0)
|
||||||
|
min = -min;
|
||||||
|
// scale so that the component that is most different from 0 is mapped to 1
|
||||||
|
// (or -1); all other values will then by definition fall between -1 and 1
|
||||||
|
if (max > min)
|
||||||
|
scale = 1 / max;
|
||||||
|
else
|
||||||
|
scale = 1 / min;
|
||||||
|
|
||||||
// Set up the window variable
|
// scale the model vertices by brute force
|
||||||
GLFWwindow* window;
|
Mat4x4 mscale;
|
||||||
|
mscale.make_scale(scale, scale, scale);
|
||||||
// Define the error callback function
|
for (int i = 0; i < Globals::mesh.vertices.size(); ++i) {
|
||||||
glfwSetErrorCallback(&error_callback);
|
Globals::mesh.vertices[i] = mscale * Globals::mesh.vertices[i];
|
||||||
|
}
|
||||||
|
// The above can be removed once a proper projection matrix is defined
|
||||||
|
|
||||||
// Initialize glfw
|
// Set up the window variable
|
||||||
if( !glfwInit() ){ return EXIT_FAILURE; }
|
GLFWwindow *window;
|
||||||
|
|
||||||
// Ask for OpenGL 3.3
|
// Define the error callback function
|
||||||
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
|
glfwSetErrorCallback(&error_callback);
|
||||||
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
|
|
||||||
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
|
|
||||||
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
|
|
||||||
|
|
||||||
// Create the glfw window
|
// Initialize glfw
|
||||||
Globals::win_width = WIN_WIDTH;
|
if (!glfwInit()) {
|
||||||
Globals::win_height = WIN_HEIGHT;
|
return EXIT_FAILURE;
|
||||||
window = glfwCreateWindow(int(Globals::win_width), int(Globals::win_height), "HW2b", NULL, NULL);
|
}
|
||||||
if( !window ){ glfwTerminate(); return EXIT_FAILURE; }
|
|
||||||
|
|
||||||
// Define callbacks to handle user input and window resizing
|
// Ask for OpenGL 3.3
|
||||||
glfwSetKeyCallback(window, &key_callback);
|
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
|
||||||
glfwSetFramebufferSizeCallback(window, &framebuffer_size_callback);
|
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
|
||||||
|
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
|
||||||
|
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
|
||||||
|
|
||||||
// More setup stuff
|
// Create the glfw window
|
||||||
glfwMakeContextCurrent(window); // Make the window current
|
Globals::win_width = WIN_WIDTH;
|
||||||
glfwSwapInterval(1); // Set the swap interval
|
Globals::win_height = WIN_HEIGHT;
|
||||||
|
window = glfwCreateWindow(int(Globals::win_width), int(Globals::win_height),
|
||||||
|
"HW2b", NULL, NULL);
|
||||||
|
if (!window) {
|
||||||
|
glfwTerminate();
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
// make sure the openGL and GLFW code can be found
|
// Define callbacks to handle user input and window resizing
|
||||||
if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)) {
|
glfwSetKeyCallback(window, &key_callback);
|
||||||
std::cout << "Failed to gladLoadGLLoader" << std::endl;
|
glfwSetFramebufferSizeCallback(window, &framebuffer_size_callback);
|
||||||
glfwTerminate();
|
|
||||||
return EXIT_FAILURE;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Initialize the shaders
|
// More setup stuff
|
||||||
// MY_SRC_DIR was defined in CMakeLists.txt
|
glfwMakeContextCurrent(window); // Make the window current
|
||||||
// it specifies the full path to this project's src/ directory.
|
glfwSwapInterval(1); // Set the swap interval
|
||||||
mcl::Shader shader;
|
|
||||||
std::stringstream ss; ss << MY_SRC_DIR << "shader.";
|
|
||||||
shader.init_from_files( ss.str()+"vert", ss.str()+"frag" );
|
|
||||||
|
|
||||||
// Initialize the scene
|
// make sure the openGL and GLFW code can be found
|
||||||
init_scene();
|
if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)) {
|
||||||
framebuffer_size_callback(window, int(Globals::win_width), int(Globals::win_height));
|
std::cout << "Failed to gladLoadGLLoader" << std::endl;
|
||||||
|
glfwTerminate();
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
// Perform some OpenGL initializations
|
// Initialize the shaders
|
||||||
glEnable(GL_DEPTH_TEST); // turn hidden surfce removal on
|
// MY_SRC_DIR was defined in CMakeLists.txt
|
||||||
glClearColor(1.f,1.f,1.f,1.f); // set the background to white
|
// it specifies the full path to this project's src/ directory.
|
||||||
|
mcl::Shader shader;
|
||||||
|
std::stringstream ss;
|
||||||
|
ss << MY_SRC_DIR << "shader.";
|
||||||
|
shader.init_from_files(ss.str() + "vert", ss.str() + "frag");
|
||||||
|
|
||||||
// Enable the shader, this allows us to set uniforms and attributes
|
// Initialize the scene
|
||||||
shader.enable();
|
init_scene();
|
||||||
|
framebuffer_size_callback(window, int(Globals::win_width),
|
||||||
|
int(Globals::win_height));
|
||||||
|
|
||||||
// Bind buffers
|
// Perform some OpenGL initializations
|
||||||
glBindVertexArray(Globals::tris_vao);
|
glEnable(GL_DEPTH_TEST); // turn hidden surfce removal on
|
||||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, Globals::faces_ibo[0]);
|
glClearColor(1.f, 1.f, 1.f, 1.f); // set the background to white
|
||||||
|
|
||||||
// Game loop
|
|
||||||
while( !glfwWindowShouldClose(window) ){
|
|
||||||
|
|
||||||
// Clear the color and depth buffers
|
// Enable the shader, this allows us to set uniforms and attributes
|
||||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
shader.enable();
|
||||||
|
|
||||||
// Send updated info to the GPU
|
// Bind buffers
|
||||||
glUniformMatrix4fv( shader.uniform("model"), 1, GL_FALSE, Globals::model.m ); // model transformation (always the identity matrix in this assignment)
|
glBindVertexArray(Globals::tris_vao);
|
||||||
glUniformMatrix4fv( shader.uniform("view"), 1, GL_FALSE, Globals::view.m ); // viewing transformation
|
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, Globals::faces_ibo[0]);
|
||||||
glUniformMatrix4fv( shader.uniform("projection"), 1, GL_FALSE, Globals::projection.m ); // projection matrix
|
|
||||||
|
|
||||||
// Draw
|
// Game loop
|
||||||
glDrawElements(GL_TRIANGLES, Globals::mesh.faces.size()*3, GL_UNSIGNED_INT, 0);
|
while (!glfwWindowShouldClose(window)) {
|
||||||
|
|
||||||
// Finalize
|
// Clear the color and depth buffers
|
||||||
glfwSwapBuffers(window);
|
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||||
glfwPollEvents();
|
|
||||||
|
|
||||||
} // end game loop
|
// Send updated info to the GPU
|
||||||
|
glUniformMatrix4fv(shader.uniform("model"), 1, GL_FALSE,
|
||||||
|
Globals::model.m); // model transformation (always the
|
||||||
|
// identity matrix in this assignment)
|
||||||
|
glUniformMatrix4fv(shader.uniform("view"), 1, GL_FALSE,
|
||||||
|
Globals::view.m); // viewing transformation
|
||||||
|
glUniformMatrix4fv(shader.uniform("projection"), 1, GL_FALSE,
|
||||||
|
Globals::projection.m); // projection matrix
|
||||||
|
|
||||||
// Unbind
|
// Draw
|
||||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
|
glDrawElements(GL_TRIANGLES, Globals::mesh.faces.size() * 3,
|
||||||
glBindVertexArray(0);
|
GL_UNSIGNED_INT, 0);
|
||||||
|
|
||||||
// Disable the shader, we're done using it
|
// Finalize
|
||||||
shader.disable();
|
glfwSwapBuffers(window);
|
||||||
|
glfwPollEvents();
|
||||||
return EXIT_SUCCESS;
|
|
||||||
|
} // end game loop
|
||||||
|
|
||||||
|
// Unbind
|
||||||
|
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
|
||||||
|
glBindVertexArray(0);
|
||||||
|
|
||||||
|
// Disable the shader, we're done using it
|
||||||
|
shader.disable();
|
||||||
|
|
||||||
|
return EXIT_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void init_scene() {
|
||||||
|
|
||||||
void init_scene(){
|
using namespace Globals;
|
||||||
|
|
||||||
using namespace Globals;
|
// Create the buffer for vertices
|
||||||
|
glGenBuffers(1, verts_vbo);
|
||||||
|
glBindBuffer(GL_ARRAY_BUFFER, verts_vbo[0]);
|
||||||
|
glBufferData(GL_ARRAY_BUFFER, mesh.vertices.size() * sizeof(mesh.vertices[0]),
|
||||||
|
&mesh.vertices[0][0], GL_STATIC_DRAW);
|
||||||
|
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
||||||
|
|
||||||
// Create the buffer for vertices
|
// Create the buffer for colors
|
||||||
glGenBuffers(1, verts_vbo);
|
glGenBuffers(1, colors_vbo);
|
||||||
glBindBuffer(GL_ARRAY_BUFFER, verts_vbo[0]);
|
glBindBuffer(GL_ARRAY_BUFFER, colors_vbo[0]);
|
||||||
glBufferData(GL_ARRAY_BUFFER, mesh.vertices.size()*sizeof(mesh.vertices[0]), &mesh.vertices[0][0], GL_STATIC_DRAW);
|
glBufferData(GL_ARRAY_BUFFER, mesh.colors.size() * sizeof(mesh.colors[0]),
|
||||||
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
&mesh.colors[0][0], GL_STATIC_DRAW);
|
||||||
|
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
||||||
|
|
||||||
// Create the buffer for colors
|
// Create the buffer for normals
|
||||||
glGenBuffers(1, colors_vbo);
|
glGenBuffers(1, normals_vbo);
|
||||||
glBindBuffer(GL_ARRAY_BUFFER, colors_vbo[0]);
|
glBindBuffer(GL_ARRAY_BUFFER, normals_vbo[0]);
|
||||||
glBufferData(GL_ARRAY_BUFFER, mesh.colors.size()*sizeof(mesh.colors[0]), &mesh.colors[0][0], GL_STATIC_DRAW);
|
glBufferData(GL_ARRAY_BUFFER, mesh.normals.size() * sizeof(mesh.normals[0]),
|
||||||
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
&mesh.normals[0][0], GL_STATIC_DRAW);
|
||||||
|
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
||||||
|
|
||||||
// Create the buffer for normals
|
// Create the buffer for indices
|
||||||
glGenBuffers(1, normals_vbo);
|
glGenBuffers(1, faces_ibo);
|
||||||
glBindBuffer(GL_ARRAY_BUFFER, normals_vbo[0]);
|
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, faces_ibo[0]);
|
||||||
glBufferData(GL_ARRAY_BUFFER, mesh.normals.size()*sizeof(mesh.normals[0]), &mesh.normals[0][0], GL_STATIC_DRAW);
|
glBufferData(GL_ELEMENT_ARRAY_BUFFER,
|
||||||
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
mesh.faces.size() * sizeof(mesh.faces[0]), &mesh.faces[0][0],
|
||||||
|
GL_STATIC_DRAW);
|
||||||
|
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
|
||||||
|
|
||||||
// Create the buffer for indices
|
// Create the VAO
|
||||||
glGenBuffers(1, faces_ibo);
|
glGenVertexArrays(1, &tris_vao);
|
||||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, faces_ibo[0]);
|
glBindVertexArray(tris_vao);
|
||||||
glBufferData(GL_ELEMENT_ARRAY_BUFFER, mesh.faces.size()*sizeof(mesh.faces[0]), &mesh.faces[0][0], GL_STATIC_DRAW);
|
|
||||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
|
|
||||||
|
|
||||||
// Create the VAO
|
int vert_dim = 3;
|
||||||
glGenVertexArrays(1, &tris_vao);
|
|
||||||
glBindVertexArray(tris_vao);
|
|
||||||
|
|
||||||
int vert_dim = 3;
|
// location=0 is the vertex
|
||||||
|
glEnableVertexAttribArray(0);
|
||||||
|
glBindBuffer(GL_ARRAY_BUFFER, verts_vbo[0]);
|
||||||
|
glVertexAttribPointer(0, vert_dim, GL_FLOAT, GL_FALSE,
|
||||||
|
sizeof(mesh.vertices[0]), 0);
|
||||||
|
|
||||||
// location=0 is the vertex
|
// location=1 is the color
|
||||||
glEnableVertexAttribArray(0);
|
glEnableVertexAttribArray(1);
|
||||||
glBindBuffer(GL_ARRAY_BUFFER, verts_vbo[0]);
|
glBindBuffer(GL_ARRAY_BUFFER, colors_vbo[0]);
|
||||||
glVertexAttribPointer(0, vert_dim, GL_FLOAT, GL_FALSE, sizeof(mesh.vertices[0]), 0);
|
glVertexAttribPointer(1, vert_dim, GL_FLOAT, GL_FALSE, sizeof(mesh.colors[0]),
|
||||||
|
0);
|
||||||
|
|
||||||
// location=1 is the color
|
// location=2 is the normal
|
||||||
glEnableVertexAttribArray(1);
|
glEnableVertexAttribArray(2);
|
||||||
glBindBuffer(GL_ARRAY_BUFFER, colors_vbo[0]);
|
glBindBuffer(GL_ARRAY_BUFFER, normals_vbo[0]);
|
||||||
glVertexAttribPointer(1, vert_dim, GL_FLOAT, GL_FALSE, sizeof(mesh.colors[0]), 0);
|
glVertexAttribPointer(2, vert_dim, GL_FLOAT, GL_FALSE,
|
||||||
|
sizeof(mesh.normals[0]), 0);
|
||||||
// location=2 is the normal
|
|
||||||
glEnableVertexAttribArray(2);
|
|
||||||
glBindBuffer(GL_ARRAY_BUFFER, normals_vbo[0]);
|
|
||||||
glVertexAttribPointer(2, vert_dim, GL_FLOAT, GL_FALSE, sizeof(mesh.normals[0]), 0);
|
|
||||||
|
|
||||||
// Done setting data for the vao
|
|
||||||
glBindVertexArray(0);
|
|
||||||
|
|
||||||
|
// Done setting data for the vao
|
||||||
|
glBindVertexArray(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,31 +1,32 @@
|
||||||
#version 330 core
|
#version 330 core
|
||||||
|
|
||||||
layout (location=0) out vec4 out_fragcolor;
|
layout(location = 0) out vec4 out_fragcolor;
|
||||||
|
|
||||||
in vec3 position;
|
in vec3 position;
|
||||||
in vec3 color;
|
in vec3 color;
|
||||||
in vec3 normal;
|
in vec3 normal;
|
||||||
|
|
||||||
void main(){
|
void main() {
|
||||||
|
|
||||||
// hard code some material properties
|
|
||||||
float ka = 0.3f;
|
|
||||||
float kd = 0.6f;
|
|
||||||
|
|
||||||
// hard code a static point light source at (0,0,0)
|
|
||||||
// L is a unit vector from the fragment location towards this light
|
|
||||||
vec3 L = -1.f*normalize(vec3(position));
|
|
||||||
|
|
||||||
// normalize the input normal that was interpolated from the mesh vertices
|
// hard code some material properties
|
||||||
vec3 N = normalize(normal);
|
float ka = 0.3f;
|
||||||
|
float kd = 0.6f;
|
||||||
// compute a simple diffuse shading weight that is agnostic to the order in which the triangle vertices were specified
|
|
||||||
float N_dot_L = dot(N, L);
|
|
||||||
if ((N_dot_L) < 0.0) { N_dot_L *= -1.0; }
|
|
||||||
|
|
||||||
// use a simplified ambient+diffuse shading model to define the fragment color
|
|
||||||
vec3 result = ka * color + kd * color * N_dot_L;
|
|
||||||
out_fragcolor = vec4( result, 1.0 );
|
|
||||||
}
|
|
||||||
|
|
||||||
|
// hard code a static point light source at (0,0,0)
|
||||||
|
// L is a unit vector from the fragment location towards this light
|
||||||
|
vec3 L = -1.f * normalize(vec3(position));
|
||||||
|
|
||||||
|
// normalize the input normal that was interpolated from the mesh vertices
|
||||||
|
vec3 N = normalize(normal);
|
||||||
|
|
||||||
|
// compute a simple diffuse shading weight that is agnostic to the order in
|
||||||
|
// which the triangle vertices were specified
|
||||||
|
float N_dot_L = dot(N, L);
|
||||||
|
if ((N_dot_L) < 0.0) {
|
||||||
|
N_dot_L *= -1.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// use a simplified ambient+diffuse shading model to define the fragment color
|
||||||
|
vec3 result = ka * color + kd * color * N_dot_L;
|
||||||
|
out_fragcolor = vec4(result, 1.0);
|
||||||
|
}
|
||||||
|
|
|
@ -1,21 +1,26 @@
|
||||||
// Copyright 2016 University of Minnesota
|
// Copyright 2016 University of Minnesota
|
||||||
//
|
//
|
||||||
// SHADER Uses the BSD 2-Clause License (http://www.opensource.org/licenses/BSD-2-Clause)
|
// SHADER Uses the BSD 2-Clause License
|
||||||
// Redistribution and use in source and binary forms, with or without modification, are
|
// (http://www.opensource.org/licenses/BSD-2-Clause) Redistribution and use in
|
||||||
// permitted provided that the following conditions are met:
|
// source and binary forms, with or without modification, are permitted provided
|
||||||
// 1. Redistributions of source code must retain the above copyright notice, this list of
|
// that the following conditions are met:
|
||||||
|
// 1. Redistributions of source code must retain the above copyright notice,
|
||||||
|
// this list of
|
||||||
// conditions and the following disclaimer.
|
// conditions and the following disclaimer.
|
||||||
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
// 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||||
// of conditions and the following disclaimer in the documentation and/or other materials
|
// this list
|
||||||
// provided with the distribution.
|
// of conditions and the following disclaimer in the documentation and/or
|
||||||
// THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
// other materials provided with the distribution.
|
||||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
// THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
|
||||||
// ARE DISCLAIMED. IN NO EVENT SHALL THE UNIVERSITY OF MINNESOTA, DULUTH OR CONTRIBUTORS BE
|
// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||||
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
// FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||||
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
|
// UNIVERSITY OF MINNESOTA, DULUTH OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
|
||||||
// OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
|
// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
// IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
// OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||||
|
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
//
|
//
|
||||||
// Adapted from r3dux (http://r3dux.org).
|
// Adapted from r3dux (http://r3dux.org).
|
||||||
|
|
||||||
|
@ -47,154 +52,179 @@ namespace mcl {
|
||||||
|
|
||||||
class Shader {
|
class Shader {
|
||||||
public:
|
public:
|
||||||
Shader() : program_id(0) {}
|
Shader() : program_id(0) {}
|
||||||
|
|
||||||
~Shader(){ glDeleteProgram(program_id); }
|
~Shader() { glDeleteProgram(program_id); }
|
||||||
|
|
||||||
// Init the shader from files (must create OpenGL context first!)
|
// Init the shader from files (must create OpenGL context first!)
|
||||||
inline void init_from_files( std::string vertex_file, std::string frag_file );
|
inline void init_from_files(std::string vertex_file, std::string frag_file);
|
||||||
|
|
||||||
// Init the shader from strings (must create OpenGL context first!)
|
// Init the shader from strings (must create OpenGL context first!)
|
||||||
inline void init_from_strings( std::string vertex_source, std::string frag_source ){ init(vertex_source, frag_source); }
|
inline void init_from_strings(std::string vertex_source,
|
||||||
|
std::string frag_source) {
|
||||||
|
init(vertex_source, frag_source);
|
||||||
|
}
|
||||||
|
|
||||||
// Be sure to initialize the shader before enabling it
|
// Be sure to initialize the shader before enabling it
|
||||||
inline void enable();
|
inline void enable();
|
||||||
|
|
||||||
// Not really needed, but nice for readability
|
// Not really needed, but nice for readability
|
||||||
inline void disable(){ glUseProgram(0); }
|
inline void disable() { glUseProgram(0); }
|
||||||
|
|
||||||
// Returns the bound location of a named attribute
|
// Returns the bound location of a named attribute
|
||||||
inline GLuint attribute( const std::string name );
|
inline GLuint attribute(const std::string name);
|
||||||
|
|
||||||
|
// Returns the bound location of a named uniform
|
||||||
|
inline GLuint uniform(const std::string name);
|
||||||
|
|
||||||
// Returns the bound location of a named uniform
|
|
||||||
inline GLuint uniform( const std::string name );
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
GLuint program_id;
|
GLuint program_id;
|
||||||
GLuint vertex_id;
|
GLuint vertex_id;
|
||||||
GLuint fragment_id;
|
GLuint fragment_id;
|
||||||
|
|
||||||
std::unordered_map<std::string, GLuint> attributes;
|
std::unordered_map<std::string, GLuint> attributes;
|
||||||
std::unordered_map<std::string, GLuint> uniforms;
|
std::unordered_map<std::string, GLuint> uniforms;
|
||||||
|
|
||||||
// Initialize the shader, called by init_from_*
|
// Initialize the shader, called by init_from_*
|
||||||
inline void init( std::string vertex_source, std::string frag_source );
|
inline void init(std::string vertex_source, std::string frag_source);
|
||||||
|
|
||||||
// Compiles the shader, called by init
|
// Compiles the shader, called by init
|
||||||
inline GLuint compile( std::string shaderSource, GLenum type );
|
inline GLuint compile(std::string shaderSource, GLenum type);
|
||||||
|
|
||||||
}; // end of shader
|
}; // end of shader
|
||||||
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// Implementation
|
// Implementation
|
||||||
//
|
//
|
||||||
|
|
||||||
GLuint Shader::compile( std::string source, GLenum type ){
|
GLuint Shader::compile(std::string source, GLenum type) {
|
||||||
|
|
||||||
// Generate a shader id
|
// Generate a shader id
|
||||||
// Note: Shader id will be non-zero if successfully created.
|
// Note: Shader id will be non-zero if successfully created.
|
||||||
GLuint shaderId = glCreateShader(type);
|
GLuint shaderId = glCreateShader(type);
|
||||||
if( shaderId == 0 ){ throw std::runtime_error("\n**glCreateShader Error"); }
|
if (shaderId == 0) {
|
||||||
|
throw std::runtime_error("\n**glCreateShader Error");
|
||||||
|
}
|
||||||
|
|
||||||
// Attach the GLSL source code and compile the shader
|
// Attach the GLSL source code and compile the shader
|
||||||
const char *shaderchar = source.c_str();
|
const char *shaderchar = source.c_str();
|
||||||
glShaderSource(shaderId, 1, &shaderchar, NULL);
|
glShaderSource(shaderId, 1, &shaderchar, NULL);
|
||||||
glCompileShader(shaderId);
|
glCompileShader(shaderId);
|
||||||
|
|
||||||
// Check the compilation status and throw a runtime_error if shader compilation failed
|
// Check the compilation status and throw a runtime_error if shader
|
||||||
GLint shaderStatus;
|
// compilation failed
|
||||||
glGetShaderiv(shaderId, GL_COMPILE_STATUS, &shaderStatus);
|
GLint shaderStatus;
|
||||||
if( shaderStatus == GL_FALSE ){ throw std::runtime_error("\n**glCompileShader Error"); }
|
glGetShaderiv(shaderId, GL_COMPILE_STATUS, &shaderStatus);
|
||||||
|
if (shaderStatus == GL_FALSE) {
|
||||||
|
throw std::runtime_error("\n**glCompileShader Error");
|
||||||
|
}
|
||||||
|
|
||||||
return shaderId;
|
return shaderId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Shader::init(std::string vertex_source, std::string frag_source) {
|
||||||
|
|
||||||
void Shader::init(std::string vertex_source, std::string frag_source){
|
// Create the resource
|
||||||
|
program_id = glCreateProgram();
|
||||||
|
if (program_id == 0) {
|
||||||
|
throw std::runtime_error("\n**glCreateProgram Error");
|
||||||
|
}
|
||||||
|
glUseProgram(program_id);
|
||||||
|
|
||||||
// Create the resource
|
// Compile the shaders and return their id values
|
||||||
program_id = glCreateProgram();
|
vertex_id = compile(vertex_source, GL_VERTEX_SHADER);
|
||||||
if( program_id == 0 ){ throw std::runtime_error("\n**glCreateProgram Error"); }
|
fragment_id = compile(frag_source, GL_FRAGMENT_SHADER);
|
||||||
glUseProgram(program_id);
|
|
||||||
|
|
||||||
// Compile the shaders and return their id values
|
// Attach and link the shader program
|
||||||
vertex_id = compile(vertex_source, GL_VERTEX_SHADER);
|
glAttachShader(program_id, vertex_id);
|
||||||
fragment_id = compile(frag_source, GL_FRAGMENT_SHADER);
|
glAttachShader(program_id, fragment_id);
|
||||||
|
glLinkProgram(program_id);
|
||||||
|
|
||||||
// Attach and link the shader program
|
// Once the shader program has the shaders attached and linked, the shaders
|
||||||
glAttachShader(program_id, vertex_id);
|
// are no longer required. If the linking failed, then we're going to abort
|
||||||
glAttachShader(program_id, fragment_id);
|
// anyway so we still detach the shaders.
|
||||||
glLinkProgram(program_id);
|
glDetachShader(program_id, vertex_id);
|
||||||
|
glDetachShader(program_id, fragment_id);
|
||||||
|
|
||||||
// Once the shader program has the shaders attached and linked, the shaders are no longer required.
|
// Check the program link status and throw a runtime_error if program linkage
|
||||||
// If the linking failed, then we're going to abort anyway so we still detach the shaders.
|
// failed.
|
||||||
glDetachShader(program_id, vertex_id);
|
GLint programLinkSuccess = GL_FALSE;
|
||||||
glDetachShader(program_id, fragment_id);
|
glGetProgramiv(program_id, GL_LINK_STATUS, &programLinkSuccess);
|
||||||
|
if (programLinkSuccess != GL_TRUE) {
|
||||||
|
throw std::runtime_error("\n**Shader Error: Problem with link");
|
||||||
|
}
|
||||||
|
|
||||||
// Check the program link status and throw a runtime_error if program linkage failed.
|
// Check the validation status and throw a runtime_error if program validation
|
||||||
GLint programLinkSuccess = GL_FALSE;
|
// failed. Does NOT work with corearb headers???
|
||||||
glGetProgramiv(program_id, GL_LINK_STATUS, &programLinkSuccess);
|
// glValidateProgram(program_id);
|
||||||
if( programLinkSuccess != GL_TRUE ){ throw std::runtime_error("\n**Shader Error: Problem with link"); }
|
// GLint programValidatationStatus;
|
||||||
|
// glGetProgramiv(program_id, GL_VALIDATE_STATUS,
|
||||||
|
//&programValidatationStatus); if( programValidatationStatus != GL_TRUE ){
|
||||||
|
//throw std::runtime_error("\n**Shader Error: Problem with validation"); }
|
||||||
|
|
||||||
// Check the validation status and throw a runtime_error if program validation failed.
|
glUseProgram(0);
|
||||||
// Does NOT work with corearb headers???
|
|
||||||
// glValidateProgram(program_id);
|
|
||||||
// GLint programValidatationStatus;
|
|
||||||
// glGetProgramiv(program_id, GL_VALIDATE_STATUS, &programValidatationStatus);
|
|
||||||
// if( programValidatationStatus != GL_TRUE ){ throw std::runtime_error("\n**Shader Error: Problem with validation"); }
|
|
||||||
|
|
||||||
glUseProgram(0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Shader::init_from_files(std::string vertex_file, std::string frag_file) {
|
||||||
|
|
||||||
void Shader::init_from_files( std::string vertex_file, std::string frag_file ){
|
std::string vert_string, frag_string;
|
||||||
|
|
||||||
std::string vert_string, frag_string;
|
// Load the vertex shader
|
||||||
|
std::ifstream vert_in(vertex_file, std::ios::in | std::ios::binary);
|
||||||
|
if (vert_in) {
|
||||||
|
vert_string = (std::string((std::istreambuf_iterator<char>(vert_in)),
|
||||||
|
std::istreambuf_iterator<char>()));
|
||||||
|
} else {
|
||||||
|
throw std::runtime_error("\n**Shader Error: failed to load \"" +
|
||||||
|
vertex_file + "\"");
|
||||||
|
}
|
||||||
|
|
||||||
// Load the vertex shader
|
// Load the fragement shader
|
||||||
std::ifstream vert_in( vertex_file, std::ios::in | std::ios::binary );
|
std::ifstream frag_in(frag_file, std::ios::in | std::ios::binary);
|
||||||
if( vert_in ){ vert_string = (std::string((std::istreambuf_iterator<char>(vert_in)), std::istreambuf_iterator<char>())); }
|
if (frag_in) {
|
||||||
else{ throw std::runtime_error("\n**Shader Error: failed to load \""+vertex_file+"\"" ); }
|
frag_string = (std::string((std::istreambuf_iterator<char>(frag_in)),
|
||||||
|
std::istreambuf_iterator<char>()));
|
||||||
|
} else {
|
||||||
|
throw std::runtime_error("\n**Shader Error: failed to load \"" + frag_file +
|
||||||
|
"\"");
|
||||||
|
}
|
||||||
|
|
||||||
// Load the fragement shader
|
init(vert_string, frag_string);
|
||||||
std::ifstream frag_in( frag_file, std::ios::in | std::ios::binary );
|
|
||||||
if( frag_in ){ frag_string = (std::string((std::istreambuf_iterator<char>(frag_in)), std::istreambuf_iterator<char>())); }
|
|
||||||
else{ throw std::runtime_error("\n**Shader Error: failed to load \""+frag_file+"\"" ); }
|
|
||||||
|
|
||||||
init( vert_string, frag_string );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Shader::enable() {
|
||||||
void Shader::enable(){
|
if (program_id != 0) {
|
||||||
if( program_id!=0 ){ glUseProgram(program_id); }
|
glUseProgram(program_id);
|
||||||
else{ throw std::runtime_error("\n**Shader Error: Can't enable, not initialized"); }
|
} else {
|
||||||
|
throw std::runtime_error("\n**Shader Error: Can't enable, not initialized");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
GLuint Shader::attribute(const std::string name) {
|
||||||
|
|
||||||
GLuint Shader::attribute(const std::string name){
|
// Add the attribute to the map table if it doesn't already exist
|
||||||
|
if (attributes.count(name) == 0) {
|
||||||
// Add the attribute to the map table if it doesn't already exist
|
attributes[name] = glGetAttribLocation(program_id, name.c_str());
|
||||||
if( attributes.count(name)==0 ){
|
if (attributes[name] == -1) {
|
||||||
attributes[name] = glGetAttribLocation( program_id, name.c_str() );
|
throw std::runtime_error("\n**Shader Error: bad attribute (" + name +
|
||||||
if( attributes[name] == -1 ){ throw std::runtime_error("\n**Shader Error: bad attribute ("+name+")"); }
|
")");
|
||||||
}
|
}
|
||||||
return attributes[name];
|
}
|
||||||
|
return attributes[name];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
GLuint Shader::uniform(const std::string name) {
|
||||||
|
|
||||||
GLuint Shader::uniform(const std::string name){
|
// Add the uniform to the map table if it doesn't already exist
|
||||||
|
if (uniforms.count(name) == 0) {
|
||||||
// Add the uniform to the map table if it doesn't already exist
|
uniforms[name] = glGetUniformLocation(program_id, name.c_str());
|
||||||
if( uniforms.count(name)==0 ){
|
if (uniforms[name] == -1) {
|
||||||
uniforms[name] = glGetUniformLocation( program_id, name.c_str() );
|
throw std::runtime_error("\n**Shader Error: bad uniform (" + name + ")");
|
||||||
if( uniforms[name] == -1 ){ throw std::runtime_error("\n**Shader Error: bad uniform ("+name+")"); }
|
}
|
||||||
}
|
}
|
||||||
return uniforms[name];
|
return uniforms[name];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
} // end namespace mcl
|
} // end namespace mcl
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
#version 330 core
|
#version 330 core
|
||||||
|
|
||||||
layout(location=0) in vec4 in_position;
|
layout(location = 0) in vec4 in_position;
|
||||||
layout(location=1) in vec3 in_color;
|
layout(location = 1) in vec3 in_color;
|
||||||
layout(location=2) in vec3 in_normal;
|
layout(location = 2) in vec3 in_normal;
|
||||||
|
|
||||||
out vec3 position;
|
out vec3 position;
|
||||||
out vec3 color;
|
out vec3 color;
|
||||||
|
@ -12,16 +12,22 @@ uniform mat4 model;
|
||||||
uniform mat4 view;
|
uniform mat4 view;
|
||||||
uniform mat4 projection;
|
uniform mat4 projection;
|
||||||
|
|
||||||
void main()
|
void main() {
|
||||||
{
|
// pass the vertex color and normal information to the fragment shader
|
||||||
// pass the vertex color and normal information to the fragment shader (without any modification)
|
// (without any modification)
|
||||||
color = in_color;
|
color = in_color;
|
||||||
normal = in_normal;
|
normal = in_normal;
|
||||||
|
|
||||||
// determine what the vertex position will be after the model transformation and pass that information to the fragment shader, for use in the illumination calculations
|
// determine what the vertex position will be after the model transformation
|
||||||
// in our case the model transformation is the identity matrix so this isn't actually necessary, but it's included here for completeness. Note that the vectors needed for the lighting calculations must be computed using the vertex locations *without* perspective warp applied
|
// and pass that information to the fragment shader, for use in the
|
||||||
position = vec3(model * in_position);
|
// illumination calculations in our case the model transformation is the
|
||||||
|
// identity matrix so this isn't actually necessary, but it's included here
|
||||||
// apply the model, view, and projection transformations to the vertex position value that will be sent to the clipper, rasterizer, ...
|
// for completeness. Note that the vectors needed for the lighting
|
||||||
gl_Position = (projection * view * model * in_position);
|
// calculations must be computed using the vertex locations *without*
|
||||||
|
// perspective warp applied
|
||||||
|
position = vec3(model * in_position);
|
||||||
|
|
||||||
|
// apply the model, view, and projection transformations to the vertex
|
||||||
|
// position value that will be sent to the clipper, rasterizer, ...
|
||||||
|
gl_Position = (projection * view * model * in_position);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,31 +1,36 @@
|
||||||
// Copyright 2016 University of Minnesota
|
// Copyright 2016 University of Minnesota
|
||||||
//
|
//
|
||||||
// TRIMESH Uses the BSD 2-Clause License (http://www.opensource.org/licenses/BSD-2-Clause)
|
// TRIMESH Uses the BSD 2-Clause License
|
||||||
// Redistribution and use in source and binary forms, with or without modification, are
|
// (http://www.opensource.org/licenses/BSD-2-Clause) Redistribution and use in
|
||||||
// permitted provided that the following conditions are met:
|
// source and binary forms, with or without modification, are permitted provided
|
||||||
// 1. Redistributions of source code must retain the above copyright notice, this list of
|
// that the following conditions are met:
|
||||||
|
// 1. Redistributions of source code must retain the above copyright notice,
|
||||||
|
// this list of
|
||||||
// conditions and the following disclaimer.
|
// conditions and the following disclaimer.
|
||||||
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
// 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||||
// of conditions and the following disclaimer in the documentation and/or other materials
|
// this list
|
||||||
// provided with the distribution.
|
// of conditions and the following disclaimer in the documentation and/or
|
||||||
// THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
// other materials provided with the distribution.
|
||||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
// THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
|
||||||
// ARE DISCLAIMED. IN NO EVENT SHALL THE UNIVERSITY OF MINNESOTA, DULUTH OR CONTRIBUTORS BE
|
// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||||
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
// FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||||
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
|
// UNIVERSITY OF MINNESOTA, DULUTH OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
|
||||||
// OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
|
// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
// IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
// OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||||
|
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
#ifndef TRIMESH_HPP
|
#ifndef TRIMESH_HPP
|
||||||
#define TRIMESH_HPP 1
|
#define TRIMESH_HPP 1
|
||||||
|
|
||||||
#include <fstream>
|
|
||||||
#include <sstream>
|
|
||||||
#include <vector>
|
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
|
#include <fstream>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
#include <sstream>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
//
|
//
|
||||||
// Vector Class
|
// Vector Class
|
||||||
|
@ -33,261 +38,310 @@
|
||||||
//
|
//
|
||||||
template <size_t D, class T> class Vec {
|
template <size_t D, class T> class Vec {
|
||||||
public:
|
public:
|
||||||
// Constructors
|
// Constructors
|
||||||
Vec(){ data[0]=T(0); data[1]=T(0); data[2]=T(0); }
|
Vec() {
|
||||||
Vec(T x_, T y_, T z_){ data[0]=x_; data[1]=y_; data[2]=z_; }
|
data[0] = T(0);
|
||||||
|
data[1] = T(0);
|
||||||
|
data[2] = T(0);
|
||||||
|
}
|
||||||
|
Vec(T x_, T y_, T z_) {
|
||||||
|
data[0] = x_;
|
||||||
|
data[1] = y_;
|
||||||
|
data[2] = z_;
|
||||||
|
}
|
||||||
|
|
||||||
// Data
|
// Data
|
||||||
T data[3];
|
T data[3];
|
||||||
|
|
||||||
// Functions
|
// Functions
|
||||||
T operator[](int i) const { return data[i]; }
|
T operator[](int i) const { return data[i]; }
|
||||||
T& operator[](int i){ return data[i]; }
|
T &operator[](int i) { return data[i]; }
|
||||||
Vec<D,T> &operator += (const Vec<D,T> &x) {
|
Vec<D, T> &operator+=(const Vec<D, T> &x) {
|
||||||
for(size_t i=0; i<D; ++i){ data[i] += x[i]; }
|
for (size_t i = 0; i < D; ++i) {
|
||||||
return *this;
|
data[i] += x[i];
|
||||||
}
|
}
|
||||||
double len2() const { return this->dot(*this); } // squared length
|
return *this;
|
||||||
double len() const { return sqrt(len2()); } // length
|
}
|
||||||
T dot(const Vec<D,T> &v) const {
|
double len2() const { return this->dot(*this); } // squared length
|
||||||
T r(0);
|
double len() const { return sqrt(len2()); } // length
|
||||||
for(size_t i=0; i<D; ++i){ r += v[i] * data[i]; }
|
T dot(const Vec<D, T> &v) const {
|
||||||
return r;
|
T r(0);
|
||||||
}
|
for (size_t i = 0; i < D; ++i) {
|
||||||
Vec<3,T> cross(const Vec<3,T> &v) const {
|
r += v[i] * data[i];
|
||||||
assert(D == 3); // only defined for 3 dims
|
}
|
||||||
return Vec<3,T>(data[1]*v[2] - data[2]*v[1], data[2]*v[0] - data[0]*v[2], data[0]*v[1] - data[1]*v[0]);
|
return r;
|
||||||
}
|
}
|
||||||
void normalize() {
|
Vec<3, T> cross(const Vec<3, T> &v) const {
|
||||||
double l = len(); if( l<=0.0 ){ return; }
|
assert(D == 3); // only defined for 3 dims
|
||||||
for(size_t i=0; i<D; ++i){ data[i] = data[i] / l; }
|
return Vec<3, T>(data[1] * v[2] - data[2] * v[1],
|
||||||
}
|
data[2] * v[0] - data[0] * v[2],
|
||||||
|
data[0] * v[1] - data[1] * v[0]);
|
||||||
|
}
|
||||||
|
void normalize() {
|
||||||
|
double l = len();
|
||||||
|
if (l <= 0.0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (size_t i = 0; i < D; ++i) {
|
||||||
|
data[i] = data[i] / l;
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template <size_t D, class T> static inline const Vec<D,T> operator-(const Vec<D,T> &v1, const Vec<D,T> &v2){
|
template <size_t D, class T>
|
||||||
Vec<D,T> r;
|
static inline const Vec<D, T> operator-(const Vec<D, T> &v1,
|
||||||
for(size_t i=0; i<D; ++i){ r[i] = v1[i]-v2[i]; }
|
const Vec<D, T> &v2) {
|
||||||
return r;
|
Vec<D, T> r;
|
||||||
|
for (size_t i = 0; i < D; ++i) {
|
||||||
|
r[i] = v1[i] - v2[i];
|
||||||
|
}
|
||||||
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <size_t D, class T> static inline const Vec<D,T> operator*(const Vec<D,T> &v, const T &x){
|
template <size_t D, class T>
|
||||||
Vec<D,T> r;
|
static inline const Vec<D, T> operator*(const Vec<D, T> &v, const T &x) {
|
||||||
for (size_t i=0; i<D; ++i){ r[i] = v[i]*x; }
|
Vec<D, T> r;
|
||||||
return r;
|
for (size_t i = 0; i < D; ++i) {
|
||||||
|
r[i] = v[i] * x;
|
||||||
|
}
|
||||||
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
typedef Vec<3,float> Vec3f;
|
typedef Vec<3, float> Vec3f;
|
||||||
typedef Vec<3,int> Vec3i;
|
typedef Vec<3, int> Vec3i;
|
||||||
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// Triangle Mesh Class
|
// Triangle Mesh Class
|
||||||
//
|
//
|
||||||
class TriMesh {
|
class TriMesh {
|
||||||
public:
|
public:
|
||||||
std::vector<Vec3f> vertices;
|
std::vector<Vec3f> vertices;
|
||||||
std::vector<Vec3f> normals;
|
std::vector<Vec3f> normals;
|
||||||
std::vector<Vec3f> colors;
|
std::vector<Vec3f> colors;
|
||||||
std::vector<Vec3i> faces;
|
std::vector<Vec3i> faces;
|
||||||
|
|
||||||
// Compute normals if not loaded from obj
|
// Compute normals if not loaded from obj
|
||||||
// or if recompute is set to true.
|
// or if recompute is set to true.
|
||||||
void need_normals( bool recompute=false );
|
void need_normals(bool recompute = false);
|
||||||
|
|
||||||
// Sets a default vertex color if
|
// Sets a default vertex color if
|
||||||
// they haven't been set.
|
// they haven't been set.
|
||||||
void need_colors( Vec3f default_color = Vec3f(0.4,0.4,0.4) );
|
void need_colors(Vec3f default_color = Vec3f(0.4, 0.4, 0.4));
|
||||||
|
|
||||||
// Loads an OBJ file
|
// Loads an OBJ file
|
||||||
bool load_obj( std::string file );
|
bool load_obj(std::string file);
|
||||||
|
|
||||||
// Prints details about the mesh
|
// Prints details about the mesh
|
||||||
void print_details();
|
void print_details();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// Implementation
|
// Implementation
|
||||||
//
|
//
|
||||||
|
|
||||||
void TriMesh::print_details(){
|
void TriMesh::print_details() {
|
||||||
std::cout << "Vertices: " << vertices.size() << std::endl;
|
std::cout << "Vertices: " << vertices.size() << std::endl;
|
||||||
std::cout << "Normals: " << normals.size() << std::endl;
|
std::cout << "Normals: " << normals.size() << std::endl;
|
||||||
std::cout << "Colors: " << colors.size() << std::endl;
|
std::cout << "Colors: " << colors.size() << std::endl;
|
||||||
std::cout << "Faces: " << faces.size() << std::endl;
|
std::cout << "Faces: " << faces.size() << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TriMesh::need_normals(bool recompute) {
|
||||||
void TriMesh::need_normals( bool recompute ){
|
if (vertices.size() == normals.size() && !recompute) {
|
||||||
if( vertices.size() == normals.size() && !recompute ){ return; }
|
return;
|
||||||
if( normals.size() != vertices.size() ){ normals.resize( vertices.size() ); }
|
}
|
||||||
std::cout << "Computing TriMesh normals" << std::endl;
|
if (normals.size() != vertices.size()) {
|
||||||
const int nv = normals.size();
|
normals.resize(vertices.size());
|
||||||
for( int i = 0; i < nv; ++i ){ normals[i][0] = 0.f; normals[i][1] = 0.f; normals[i][2] = 0.f; }
|
}
|
||||||
int nf = faces.size();
|
std::cout << "Computing TriMesh normals" << std::endl;
|
||||||
for( int f = 0; f < nf; ++f ){
|
const int nv = normals.size();
|
||||||
Vec3i face = faces[f];
|
for (int i = 0; i < nv; ++i) {
|
||||||
const Vec3f &p0 = vertices[ face[0] ];
|
normals[i][0] = 0.f;
|
||||||
const Vec3f &p1 = vertices[ face[1] ];
|
normals[i][1] = 0.f;
|
||||||
const Vec3f &p2 = vertices[ face[2] ];
|
normals[i][2] = 0.f;
|
||||||
Vec3f a = p0-p1, b = p1-p2, c = p2-p0;
|
}
|
||||||
float l2a = a.len2(), l2b = b.len2(), l2c = c.len2();
|
int nf = faces.size();
|
||||||
if (!l2a || !l2b || !l2c){ continue; } // check for zeros or nans
|
for (int f = 0; f < nf; ++f) {
|
||||||
Vec3f facenormal = a.cross( b );
|
Vec3i face = faces[f];
|
||||||
normals[faces[f][0]] += facenormal * (1.0f / (l2a * l2c));
|
const Vec3f &p0 = vertices[face[0]];
|
||||||
normals[faces[f][1]] += facenormal * (1.0f / (l2b * l2a));
|
const Vec3f &p1 = vertices[face[1]];
|
||||||
normals[faces[f][2]] += facenormal * (1.0f / (l2c * l2b));
|
const Vec3f &p2 = vertices[face[2]];
|
||||||
}
|
Vec3f a = p0 - p1, b = p1 - p2, c = p2 - p0;
|
||||||
for (int i = 0; i < nv; i++){ normals[i].normalize(); }
|
float l2a = a.len2(), l2b = b.len2(), l2c = c.len2();
|
||||||
|
if (!l2a || !l2b || !l2c) {
|
||||||
|
continue;
|
||||||
|
} // check for zeros or nans
|
||||||
|
Vec3f facenormal = a.cross(b);
|
||||||
|
normals[faces[f][0]] += facenormal * (1.0f / (l2a * l2c));
|
||||||
|
normals[faces[f][1]] += facenormal * (1.0f / (l2b * l2a));
|
||||||
|
normals[faces[f][2]] += facenormal * (1.0f / (l2c * l2b));
|
||||||
|
}
|
||||||
|
for (int i = 0; i < nv; i++) {
|
||||||
|
normals[i].normalize();
|
||||||
|
}
|
||||||
} // end need normals
|
} // end need normals
|
||||||
|
|
||||||
void TriMesh::need_colors( Vec3f default_color ){
|
void TriMesh::need_colors(Vec3f default_color) {
|
||||||
if( vertices.size() == colors.size() ){ return; }
|
if (vertices.size() == colors.size()) {
|
||||||
else{ colors.resize( vertices.size(), default_color ); }
|
return;
|
||||||
|
} else {
|
||||||
|
colors.resize(vertices.size(), default_color);
|
||||||
|
}
|
||||||
} // end need colors
|
} // end need colors
|
||||||
|
|
||||||
// Function to split a string into multiple strings, seperated by delimeter
|
// Function to split a string into multiple strings, seperated by delimeter
|
||||||
static void split_str( char delim, const std::string &str, std::vector<std::string> *result ){
|
static void split_str(char delim, const std::string &str,
|
||||||
std::stringstream ss(str); std::string s;
|
std::vector<std::string> *result) {
|
||||||
while( std::getline(ss, s, delim) ){ result->push_back(s); }
|
std::stringstream ss(str);
|
||||||
|
std::string s;
|
||||||
|
while (std::getline(ss, s, delim)) {
|
||||||
|
result->push_back(s);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TriMesh::load_obj( std::string file ){
|
bool TriMesh::load_obj(std::string file) {
|
||||||
|
|
||||||
std::cout << "\nLoading " << file << std::endl;
|
std::cout << "\nLoading " << file << std::endl;
|
||||||
|
|
||||||
// README:
|
// README:
|
||||||
//
|
//
|
||||||
// The problem with standard obj files and opengl is that
|
// The problem with standard obj files and opengl is that
|
||||||
// there isn't a good way to make triangles with different indices
|
// there isn't a good way to make triangles with different indices
|
||||||
// for vertices/normals. At least, not any way that I'm aware of.
|
// for vertices/normals. At least, not any way that I'm aware of.
|
||||||
// So for now, we'll do the inefficient (but robust) way:
|
// So for now, we'll do the inefficient (but robust) way:
|
||||||
// redundant vertices/normals.
|
// redundant vertices/normals.
|
||||||
//
|
//
|
||||||
|
|
||||||
std::vector<Vec3f> temp_normals;
|
std::vector<Vec3f> temp_normals;
|
||||||
std::vector<Vec3f> temp_verts;
|
std::vector<Vec3f> temp_verts;
|
||||||
std::vector<Vec3f> temp_colors;
|
std::vector<Vec3f> temp_colors;
|
||||||
|
|
||||||
//
|
//
|
||||||
// First loop, make buffers
|
// First loop, make buffers
|
||||||
//
|
//
|
||||||
std::ifstream infile( file.c_str() );
|
std::ifstream infile(file.c_str());
|
||||||
if( infile.is_open() ){
|
if (infile.is_open()) {
|
||||||
|
|
||||||
std::string line;
|
std::string line;
|
||||||
while( std::getline( infile, line ) ){
|
while (std::getline(infile, line)) {
|
||||||
|
|
||||||
std::stringstream ss(line);
|
std::stringstream ss(line);
|
||||||
std::string tok; ss >> tok;
|
std::string tok;
|
||||||
|
ss >> tok;
|
||||||
|
|
||||||
// Vertex
|
// Vertex
|
||||||
if( tok == "v" ){
|
if (tok == "v") {
|
||||||
|
|
||||||
// First three location
|
// First three location
|
||||||
float x, y, z; ss >> x >> y >> z;
|
float x, y, z;
|
||||||
temp_verts.push_back( Vec3f(x,y,z) );
|
ss >> x >> y >> z;
|
||||||
|
temp_verts.push_back(Vec3f(x, y, z));
|
||||||
|
|
||||||
// Next three colors
|
// Next three colors
|
||||||
float cx, cy, cz;
|
float cx, cy, cz;
|
||||||
if( ss >> cx >> cy >> cz ){
|
if (ss >> cx >> cy >> cz) {
|
||||||
temp_colors.push_back( Vec3f(cx,cy,cz) );
|
temp_colors.push_back(Vec3f(cx, cy, cz));
|
||||||
} else {
|
} else {
|
||||||
temp_colors.push_back( Vec3f(0.3f,0.3f,0.3f) );
|
temp_colors.push_back(Vec3f(0.3f, 0.3f, 0.3f));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Normal
|
// Normal
|
||||||
if( tok == "vn" ){
|
if (tok == "vn") {
|
||||||
float x, y, z; ss >> x >> y >> z;
|
float x, y, z;
|
||||||
temp_normals.push_back( Vec3f(x,y,z) );
|
ss >> x >> y >> z;
|
||||||
}
|
temp_normals.push_back(Vec3f(x, y, z));
|
||||||
|
}
|
||||||
|
|
||||||
} // end loop lines
|
} // end loop lines
|
||||||
|
|
||||||
} // end load obj
|
} // end load obj
|
||||||
else { std::cerr << "\n**TriMesh::load_obj Error: Could not open file " << file << std::endl; return false; }
|
else {
|
||||||
|
std::cerr << "\n**TriMesh::load_obj Error: Could not open file " << file
|
||||||
|
<< std::endl;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// Second loop, make faces
|
// Second loop, make faces
|
||||||
//
|
//
|
||||||
std::ifstream infile2( file.c_str() );
|
std::ifstream infile2(file.c_str());
|
||||||
if( infile2.is_open() ){
|
if (infile2.is_open()) {
|
||||||
|
|
||||||
std::string line;
|
std::string line;
|
||||||
while( std::getline( infile2, line ) ){
|
while (std::getline(infile2, line)) {
|
||||||
|
|
||||||
std::stringstream ss(line);
|
std::stringstream ss(line);
|
||||||
std::string tok; ss >> tok;
|
std::string tok;
|
||||||
|
ss >> tok;
|
||||||
|
|
||||||
// Face
|
// Face
|
||||||
if( tok == "f" ){
|
if (tok == "f") {
|
||||||
|
|
||||||
Vec3i face;
|
Vec3i face;
|
||||||
// Get the three vertices
|
// Get the three vertices
|
||||||
for( size_t i=0; i<3; ++i ){
|
for (size_t i = 0; i < 3; ++i) {
|
||||||
|
|
||||||
std::string f_str; ss >> f_str;
|
std::string f_str;
|
||||||
std::vector<std::string> f_vals;
|
ss >> f_str;
|
||||||
split_str( '/', f_str, &f_vals );
|
std::vector<std::string> f_vals;
|
||||||
assert(f_vals.size()>0);
|
split_str('/', f_str, &f_vals);
|
||||||
|
assert(f_vals.size() > 0);
|
||||||
|
|
||||||
face[i] = vertices.size();
|
face[i] = vertices.size();
|
||||||
int v_idx = std::stoi(f_vals[0])-1;
|
int v_idx = std::stoi(f_vals[0]) - 1;
|
||||||
vertices.push_back( temp_verts[v_idx] );
|
vertices.push_back(temp_verts[v_idx]);
|
||||||
colors.push_back( temp_colors[v_idx] );
|
colors.push_back(temp_colors[v_idx]);
|
||||||
|
|
||||||
// Check for normal
|
// Check for normal
|
||||||
if( f_vals.size()>2 ){
|
if (f_vals.size() > 2) {
|
||||||
int n_idx = std::stoi(f_vals[2])-1;
|
int n_idx = std::stoi(f_vals[2]) - 1;
|
||||||
normals.push_back( temp_normals[n_idx] );
|
normals.push_back(temp_normals[n_idx]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
faces.push_back(face);
|
faces.push_back(face);
|
||||||
|
|
||||||
// If it's a quad, make another triangle
|
// If it's a quad, make another triangle
|
||||||
std::string last_vert="";
|
std::string last_vert = "";
|
||||||
if( ss >> last_vert ){
|
if (ss >> last_vert) {
|
||||||
Vec3i face2;
|
Vec3i face2;
|
||||||
face2[0] = face[0];
|
face2[0] = face[0];
|
||||||
face2[1] = face[2];
|
face2[1] = face[2];
|
||||||
|
|
||||||
std::vector<std::string> f_vals;
|
std::vector<std::string> f_vals;
|
||||||
split_str( '/', last_vert, &f_vals );
|
split_str('/', last_vert, &f_vals);
|
||||||
assert(f_vals.size()>0);
|
assert(f_vals.size() > 0);
|
||||||
|
|
||||||
int v_idx = std::stoi(f_vals[0])-1;
|
int v_idx = std::stoi(f_vals[0]) - 1;
|
||||||
vertices.push_back( temp_verts[v_idx] );
|
vertices.push_back(temp_verts[v_idx]);
|
||||||
colors.push_back( temp_colors[v_idx] );
|
colors.push_back(temp_colors[v_idx]);
|
||||||
face2[2] = vertices.size();
|
face2[2] = vertices.size();
|
||||||
|
|
||||||
// Check for normal
|
// Check for normal
|
||||||
if( f_vals.size()>2 ){
|
if (f_vals.size() > 2) {
|
||||||
int n_idx = std::stoi(f_vals[2])-1;
|
int n_idx = std::stoi(f_vals[2]) - 1;
|
||||||
normals.push_back( temp_normals[n_idx] );
|
normals.push_back(temp_normals[n_idx]);
|
||||||
}
|
}
|
||||||
|
|
||||||
faces.push_back(face2);
|
faces.push_back(face2);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // end parse face
|
} // end parse face
|
||||||
|
|
||||||
} // end loop lines
|
} // end loop lines
|
||||||
|
|
||||||
} // end load obj
|
} // end load obj
|
||||||
|
|
||||||
// Make sure we have normals
|
// Make sure we have normals
|
||||||
if( !normals.size() ){
|
if (!normals.size()) {
|
||||||
std::cout << "**Warning: normals not loaded so we'll compute them instead." << std::endl;
|
std::cout << "**Warning: normals not loaded so we'll compute them instead."
|
||||||
need_normals();
|
<< std::endl;
|
||||||
}
|
need_normals();
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
} // end load obj
|
} // end load obj
|
||||||
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue