initial commit

Everything works correctly so far. Only the basics though. There's a light at the viewer (+z-axis) and the object gets loaded in the center. You can move with the keyboard ('wasd' and 'qe' do translation, 'ijkl' and 'ou' do rotations). Planning on changing that to use mouse input. Also hoping to add a way to move the lighting around.
master
Sean Hickey 2014-07-14 18:53:24 -04:00
commit a7b124996e
31 changed files with 376368 additions and 0 deletions

190
Makefile Normal file
View File

@ -0,0 +1,190 @@
#### PROJECT SETTINGS ####
# The name of the executable to be created
BIN_NAME := obj-loader
# Compiler used
CXX ?= g++
# Extension of source files used in the project
SRC_EXT = cpp
# Path to the source directory, relative to the makefile
SRC_PATH = src
# General compiler flags
COMPILE_FLAGS = -Wall -Wextra -g -O2
# Additional release-specific flags
RCOMPILE_FLAGS = -D NDEBUG
# Additional debug-specific flags
DCOMPILE_FLAGS = -D DEBUG
# Add additional include paths
INCLUDES = -I $(SRC_PATH)/ -IGL -IGLEW -IGLM -IGLFW
# General linker settings
LINK_FLAGS = -lGL -lGLEW -lglfw
# Additional release-specific linker settings
RLINK_FLAGS =
# Additional debug-specific linker settings
DLINK_FLAGS =
# Destination directory, like a jail or mounted system
DESTDIR = /
# Install path (bin/ is appended automatically)
INSTALL_PREFIX = usr/local
#### END PROJECT SETTINGS ####
# Generally should not need to edit below this line
# Shell used in this makefile
# bash is used for 'echo -en'
SHELL = /bin/bash
# Clear built-in rules
.SUFFIXES:
# Programs for installation
INSTALL = install
INSTALL_PROGRAM = $(INSTALL)
INSTALL_DATA = $(INSTALL) -m 644
# Verbose option, to output compile and link commands
export V := false
export CMD_PREFIX := @
ifeq ($(V),true)
CMD_PREFIX :=
endif
# Combine compiler and linker flags
release: export CXXFLAGS := $(CXXFLAGS) $(COMPILE_FLAGS) $(RCOMPILE_FLAGS)
release: export LDFLAGS := $(LDFLAGS) $(LINK_FLAGS) $(RLINK_FLAGS)
debug: export CXXFLAGS := $(CXXFLAGS) $(COMPILE_FLAGS) $(DCOMPILE_FLAGS)
debug: export LDFLAGS := $(LDFLAGS) $(LINK_FLAGS) $(DLINK_FLAGS)
# Build and output paths
release: export BUILD_PATH := build/release
release: export BIN_PATH := bin/release
debug: export BUILD_PATH := build/debug
debug: export BIN_PATH := bin/debug
install: export BIN_PATH := bin/release
# Find all source files in the source directory, sorted by most
# recently modified
SOURCES = $(shell find $(SRC_PATH)/ -name '*.$(SRC_EXT)' -printf '%T@\t%p\n' \
| sort -k 1nr | cut -f2-)
# fallback in case the above fails
rwildcard = $(foreach d, $(wildcard $1*), $(call rwildcard,$d/,$2) \
$(filter $(subst *,%,$2), $d))
ifeq ($(SOURCES),)
SOURCES := $(call rwildcard, $(SRC_PATH)/, *.$(SRC_EXT))
endif
# Set the object file names, with the source directory stripped
# from the path, and the build path prepended in its place
OBJECTS = $(SOURCES:$(SRC_PATH)/%.$(SRC_EXT)=$(BUILD_PATH)/%.o)
# Set the dependency files that will be used to add header dependencies
DEPS = $(OBJECTS:.o=.d)
# Macros for timing compilation
TIME_FILE = $(dir $@).$(notdir $@)_time
START_TIME = date '+%s' > $(TIME_FILE)
END_TIME = read st < $(TIME_FILE) ; \
$(RM) $(TIME_FILE) ; \
st=$$((`date '+%s'` - $$st - 86400)) ; \
echo `date -u -d @$$st '+%H:%M:%S'`
# Version macros
# Comment/remove this section to remove versioning
USE_VERSION := false
# If this isn't a git repo or the repo has no tags, git describe will return non-zero
ifeq ($(shell git describe > /dev/null 2>&1 ; echo $$?), 0)
USE_VERSION := true
VERSION := $(shell git describe --tags --long --dirty --always | \
sed 's/v\([0-9]*\)\.\([0-9]*\)\.\([0-9]*\)-\?.*-\([0-9]*\)-\(.*\)/\1 \2 \3 \4 \5/g')
VERSION_MAJOR := $(word 1, $(VERSION))
VERSION_MINOR := $(word 2, $(VERSION))
VERSION_PATCH := $(word 3, $(VERSION))
VERSION_REVISION := $(word 4, $(VERSION))
VERSION_HASH := $(word 5, $(VERSION))
VERSION_STRING := \
"$(VERSION_MAJOR).$(VERSION_MINOR).$(VERSION_PATCH).$(VERSION_REVISION)-$(VERSION_HASH)"
override CXXFLAGS := $(CXXFLAGS) \
-D VERSION_MAJOR=$(VERSION_MAJOR) \
-D VERSION_MINOR=$(VERSION_MINOR) \
-D VERSION_PATCH=$(VERSION_PATCH) \
-D VERSION_REVISION=$(VERSION_REVISION) \
-D VERSION_HASH=\"$(VERSION_HASH)\"
endif
# Standard, non-optimized release build
.PHONY: release
release: dirs
ifeq ($(USE_VERSION), true)
@echo "Beginning release build v$(VERSION_STRING)"
else
@echo "Beginning release build"
endif
@$(START_TIME)
@$(MAKE) all --no-print-directory
@echo -n "Total build time: "
@$(END_TIME)
# Debug build for gdb debugging
.PHONY: debug
debug: dirs
ifeq ($(USE_VERSION), true)
@echo "Beginning debug build v$(VERSION_STRING)"
else
@echo "Beginning debug build"
endif
@$(START_TIME)
@$(MAKE) all --no-print-directory
@echo -n "Total build time: "
@$(END_TIME)
# Create the directories used in the build
.PHONY: dirs
dirs:
@echo "Creating directories"
@mkdir -p $(dir $(OBJECTS))
@mkdir -p $(BIN_PATH)
# Installs to the set path
.PHONY: install
install:
@echo "Installing to $(DESTDIR)$(INSTALL_PREFIX)/bin"
@$(INSTALL_PROGRAM) $(BIN_PATH)/$(BIN_NAME) $(DESTDIR)$(INSTALL_PREFIX)/bin
# Uninstalls the program
.PHONY: uninstall
uninstall:
@echo "Removing $(DESTDIR)$(INSTALL_PREFIX)/bin/$(BIN_NAME)"
@$(RM) $(DESTDIR)$(INSTALL_PREFIX)/bin/$(BIN_NAME)
# Removes all build files
.PHONY: clean
clean:
@echo "Deleting $(BIN_NAME) symlink"
@$(RM) $(BIN_NAME)
@echo "Deleting directories"
@$(RM) -r build
@$(RM) -r bin
# Main rule, checks the executable and symlinks to the output
all: $(BIN_PATH)/$(BIN_NAME)
@echo "Making symlink: $(BIN_NAME) -> $<"
@$(RM) $(BIN_NAME)
@ln -s $(BIN_PATH)/$(BIN_NAME) $(BIN_NAME)
# Link the executable
$(BIN_PATH)/$(BIN_NAME): $(OBJECTS)
@echo "Linking: $@"
@$(START_TIME)
$(CMD_PREFIX)$(CXX) $(OBJECTS) $(LDFLAGS) -o $@
@echo -en "\t Link time: "
@$(END_TIME)
# Add dependency files, if they exist
-include $(DEPS)
# Source file rules
# After the first compilation they will be joined with the rules from the
# dependency files to provide header dependencies
$(BUILD_PATH)/%.o: $(SRC_PATH)/%.$(SRC_EXT)
@echo "Compiling: $< -> $@"
@$(START_TIME)
$(CMD_PREFIX)$(CXX) $(CXXFLAGS) $(INCLUDES) -MP -MMD -c $< -o $@
@echo -en "\t Compile time: "
@$(END_TIME)

24
UNLICENSE Normal file
View File

@ -0,0 +1,24 @@
This is free and unencumbered software released into the public domain.
Anyone is free to copy, modify, publish, use, compile, sell, or
distribute this software, either in source code form or as a compiled
binary, for any purpose, commercial or non-commercial, and by any
means.
In jurisdictions that recognize copyright laws, the author or authors
of this software dedicate any and all copyright interest in the
software to the public domain. We make this dedication for the benefit
of the public at large and to the detriment of our heirs and
successors. We intend this dedication to be an overt act of
relinquishment in perpetuity of all present and future rights to this
software under copyright law.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
For more information, please refer to <http://unlicense.org/>

10
objects/cow/cow-fixed.mtl Normal file
View File

@ -0,0 +1,10 @@
# Blender MTL File: 'None'
# Material Count: 1
newmtl None
Ns 0
Ka 0.000000 0.000000 0.000000
Kd 0.8 0.8 0.8
Ks 0.8 0.8 0.8
d 1
illum 2

14517
objects/cow/cow-fixed.obj Normal file

File diff suppressed because it is too large Load Diff

8707
objects/cow/cow.obj Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,10 @@
# Blender MTL File: 'None'
# Material Count: 1
newmtl None
Ns 0
Ka 0.000000 0.000000 0.000000
Kd 0.8 0.8 0.8
Ks 0.8 0.8 0.8
d 1
illum 2

187440
objects/dragon/dragon-fixed.obj Normal file

File diff suppressed because it is too large Load Diff

3
objects/sheep/link.txt Normal file
View File

@ -0,0 +1,3 @@
Here's where I got it from
http://www.blender-models.com/model-downloads/animals/mammals/id/sheep-comic-style/

BIN
objects/sheep/sheep.blend Normal file

Binary file not shown.

90
objects/sheep/sheep.mtl Normal file
View File

@ -0,0 +1,90 @@
# Blender MTL File: 'sheep.blend'
# Material Count: 9
newmtl Material.001
Ns 96.078431
Ka 0.000000 0.000000 0.000000
Kd 0.640000 0.640000 0.640000
Ks 0.500000 0.500000 0.500000
Ni 1.000000
d 1.000000
illum 2
map_Kd C:/Dokumente und Einstellungen/Robbi/Anwendungsdaten/Blender Foundation/Blender/.blender/Braun.JPG
newmtl Material.002
Ns 96.078431
Ka 0.000000 0.000000 0.000000
Kd 0.640000 0.640000 0.640000
Ks 0.500000 0.500000 0.500000
Ni 1.000000
d 1.000000
illum 2
map_Kd C:/Dokumente und Einstellungen/Robbi/Anwendungsdaten/Blender Foundation/Blender/.blender/Braun.JPG
newmtl Material.003
Ns 96.078431
Ka 0.000000 0.000000 0.000000
Kd 0.640000 0.640000 0.640000
Ks 0.500000 0.500000 0.500000
Ni 1.000000
d 1.000000
illum 2
map_Kd C:/Dokumente und Einstellungen/Robbi/Anwendungsdaten/Blender Foundation/Blender/.blender/wei\udcdf.bmp
newmtl Material.004
Ns 96.078431
Ka 0.000000 0.000000 0.000000
Kd 0.640000 0.640000 0.640000
Ks 0.500000 0.500000 0.500000
Ni 1.000000
d 1.000000
illum 2
map_Kd C:/Dokumente und Einstellungen/Robbi/Anwendungsdaten/Blender Foundation/Blender/.blender/wei\udcdf.bmp
newmtl Material.005
Ns 96.078431
Ka 0.000000 0.000000 0.000000
Kd 0.640000 0.640000 0.640000
Ks 0.500000 0.500000 0.500000
Ni 1.000000
d 1.000000
illum 2
map_Kd C:/Dokumente und Einstellungen/Robbi/Anwendungsdaten/Blender Foundation/Blender/.blender/schwarz.bmp
newmtl Material.006
Ns 96.078431
Ka 0.000000 0.000000 0.000000
Kd 0.640000 0.640000 0.640000
Ks 0.500000 0.500000 0.500000
Ni 1.000000
d 1.000000
illum 2
map_Kd C:/Dokumente und Einstellungen/Robbi/Anwendungsdaten/Blender Foundation/Blender/.blender/schwarz.bmp
newmtl Material.007
Ns 96.078431
Ka 0.000000 0.000000 0.000000
Kd 0.640000 0.640000 0.640000
Ks 0.500000 0.500000 0.500000
Ni 1.000000
d 1.000000
illum 2
map_Kd C:/Dokumente und Einstellungen/Robbi/Anwendungsdaten/Blender Foundation/Blender/.blender/schwarz.bmp
newmtl Material.008
Ns 96.078431
Ka 0.000000 0.000000 0.000000
Kd 0.640000 0.640000 0.640000
Ks 0.500000 0.500000 0.500000
Ni 1.000000
d 1.000000
illum 2
map_Kd C:/Dokumente und Einstellungen/Robbi/Anwendungsdaten/Blender Foundation/Blender/.blender/schwarz.bmp
newmtl None
Ns 0
Ka 0.000000 0.000000 0.000000
Kd 0.8 0.8 0.8
Ks 0.8 0.8 0.8
d 1
illum 2

162031
objects/sheep/sheep.obj Normal file

File diff suppressed because it is too large Load Diff

11
objects/sheep/sheep.scene Normal file
View File

@ -0,0 +1,11 @@
size 100 100
camera 0 0 10 0 0 0 0 1 0 30
point 10 10 10 1 1 1
ambient .1 .1 .1
diffuse .4 .4 .4
specular .8 .8 .8
shininess 16
object sheep.obj

Binary file not shown.

View File

@ -0,0 +1,10 @@
# Blender MTL File: 'suzanne.blend'
# Material Count: 1
newmtl None
Ns 0
Ka 0.000000 0.000000 0.000000
Kd 0.8 0.8 0.8
Ks 0.8 0.8 0.8
d 1
illum 2

1988
objects/suzanne/suzanne.obj Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2014 Michael Crawford
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

13
shaders/fragmentShader.c Normal file
View File

@ -0,0 +1,13 @@
/**
Simple fragment shader
*/
#version 150
in vec4 Color;
out vec4 outColor;
void main() {
outColor = Color;
}

102
shaders/vertexShader.c Normal file
View File

@ -0,0 +1,102 @@
/**
vertexShader - an OpenGL shader that implements Phong shading.
*/
#version 150
// vertex properties
in vec3 vPosition;
in vec3 vNormal;
in vec3 ambientColor;
in vec3 diffuseColor;
in vec3 specularColor;
in float shininess;
// light properties
uniform vec3 lightPosition;
uniform vec3 lightColor;
uniform vec3 lightDirection; // if directional
// viewer properties
uniform vec3 viewPosition;
uniform vec3 viewDirection;
// matrices
uniform mat4 model;
uniform mat4 view;
uniform mat4 proj;
// outputs the color for this vertex
out vec4 Color;
void main() {
/**
Calculate the position after matrix transformations
*/
mat4 modelView = view * model;
mat3 normalMatrix;
normalMatrix[0] = modelView[0].xyz;
normalMatrix[1] = modelView[1].xyz;
normalMatrix[2] = modelView[2].xyz;
// output the position to the special gl_Position variable
gl_Position = proj * modelView * vec4(vPosition, 1.0);
/**
Phong shading calculations for color
*/
vec3 tempColor = vec3(0,0,0);
vec3 d = lightDirection;
vec3 n = vNormal;
// point light needs to calculate direction
if (lightDirection == vec3(0.0,0.0,0.0)) {
d = normalize(vPosition - lightPosition);
}
d = vec3(0,0,1);
// ambient
tempColor += ambientColor;
// diffuse
vec3 tempDiffColor = diffuseColor * dot(d, n);
for (int i = 0; i < 3; i++) {
if (tempDiffColor[i] < 0.0) {
tempDiffColor[i] = 0.0;
}
}
tempColor += tempDiffColor;
// specular
vec3 r = d + 2*(dot(-1*d,n)) * n;
float re = dot(r,viewDirection);
if (re < 0) {
re = 0;
}
re = pow(re, shininess);
vec3 tempSpecColor = specularColor * re;
for (int i = 0; i < 3; i++) {
if (tempSpecColor[i] < 0.0) {
tempSpecColor[i] = 0.0;
}
}
tempColor += tempSpecColor;
// fix the color to fit constraints
for (int i = 0; i < 3; i++) {
if (tempColor[i] > 1) {
tempColor[i] = 1;
}
if (tempColor[i] < 0) {
tempColor[i] = 0;
}
}
// output color
Color = vec4(tempColor,1.0);
//Color = vec4(1.0,1.0,1.0,1.0);
}

67
src/Camera.cpp Normal file
View File

@ -0,0 +1,67 @@
/**
Camera - storage class for the viewer's camera
Contains position, viewing direction, and up direction.
*/
#include "Camera.h"
using namespace glm;
int Camera::init(vec3 pos, vec3 target, vec3 up) {
this->position = pos;
this->target = target;
this->up = up;
return 0;
}
Camera::Camera() {
// default to looking down negative z-axis
vec3 p (0.0f, 0.0f, 5.0f);
vec3 t (0.0f, 0.0f, 0.0f);
vec3 up (0.0f, 1.0f, 0.0f);
init(p, t, up);
}
Camera::Camera(vec3 pos, vec3 dir, vec3 up) {
init(pos, dir, up);
}
vec3 Camera::getPosition() const {
return this->position;
}
vec3 Camera::getTarget() const {
return this->target;
}
vec3 Camera::getUpDir() const {
return this->up;
}
vec3 Camera::getViewDir() const {
return normalize(this->target - this->position);
}
void Camera::translate(vec3 v) {
this->position += v;
this->target += v; // keep the view direction constant
}
// axis-angle rotation
void Camera::rotate(vec3 v) {
float angle = length(v);
vec3 w = normalize(v);
vec3 a = this->getViewDir();
// rodrigues formula to rotate
vec3 b =
a*cos(angle) + cross(w,a)*sin(angle) + w*dot(w,a)*(1-cos(angle));
vec3 view = this->target - this->position;
this->target = this->position + b*length(view);
a = this->getUpDir();
this->up =
a*cos(angle) + cross(w,a)*sin(angle) + w*dot(w,a)*(1-cos(angle));
}

35
src/Camera.h Normal file
View File

@ -0,0 +1,35 @@
/**
Camera - storage class for the viewer's camera
Contains position, viewing direction, and up direction.
*/
#ifndef CAMERA_H
#define CAMERA_H
#include <glm/glm.hpp>
using namespace glm;
class Camera {
public:
Camera();
Camera(vec3 pos, vec3 dir, vec3 up);
vec3 getPosition() const;
vec3 getTarget() const;
vec3 getViewDir() const;
vec3 getUpDir() const;
void translate(vec3 v);
void rotate(vec3 v);
private:
vec3 position;
vec3 target;
vec3 up;
int init(vec3 pos, vec3 dir, vec3 up);
};
#endif

414
src/Object.cpp Normal file
View File

@ -0,0 +1,414 @@
/**
Object - a storage class for a .obj file
*/
#include "Object.h"
using namespace std;
int Object::init(string filename, Shader* s) {
if (s == NULL) {
cerr << "Shader was null in Object::init(). Object not created.\n";
return 1;
}
this->parseObjectFile(filename);
glm::mat4 id (1.0f); // identity
this->transformation = id;
this->shader = s;
this->vao = 0;
this->elms = 16; // 3 pos, 3 norm, 3*3 color, 1 shine
// default to white/greyish
glm::vec3 white (1.0f,1.0f,1.0f);
this->ambient = white * 0.1f;
this->diffuse = white * 0.4f;
this->specular = white * 0.8f;
this->shininess = 16.0f;
openglInitialization();
return 0;
}
Object::Object() {
init(NULL, NULL);
}
Object::Object(string filename, Shader* s) {
init(filename, s);
}
/**
Loads a .obj file and parses it.
Note: I did not implement the entire .obj specification.
I only implemented vertices, vertex normals, and faces under the
assumption that each face is a triangle.
*/
void Object::parseObjectFile(string filename) {
if (this->vertices.size() > 0) {
cerr << "Object has already been loaded from file.\n"
<< "Loading again is undefined and not allowed.\n";
return;
}
ifstream file (filename.c_str());
if (!file) {
cerr << "Failed to open object file: " << filename << endl;
return;
}
string line;
while (getline(file,line)) {
stringstream ss (line);
if (line.size() == 0) {
continue;
}
string op;
ss >> op;
// vertices
if (op == "v") {
float x,y,z;
ss >> x >> y >> z;
glm::vec3 v (x,y,z);
this->vertices.push_back(v);
}
// vertex normals
if (op == "vn") {
float x,y,z;
ss >> x >> y >> z;
glm::vec3 v (x,y,z);
this->vertexNormals.push_back(v);
}
// parse a triangle face
if (op == "f") {
string s1,s2,s3;
// strings should be like
// "f 6//1 7//1 4//1"
// or "f 2 3 4"
ss >> s1 >> s2 >> s3;
int a,b,c;
int an,bn,cn;
a = parseOBJVertex(s1);
b = parseOBJVertex(s2);
c = parseOBJVertex(s3);
an = parseOBJVertexNormal(s1);
bn = parseOBJVertexNormal(s2);
cn = parseOBJVertexNormal(s3);
Triangle t;
if (an != -1 && bn != -1 && cn != -1) {
Triangle tr (a,b,c,an,bn,cn);
t = tr;
} else {
Triangle tr (a,b,c);
t = tr;
}
this->triangles.push_back(t);
}
// I didn't implement the entire .obj specification
// rest is garbled gooky shit.
}
}
void Object::glDraw() {
this->applyModelMatrix();
glBindVertexArray(this->vao);
glDrawElements(GL_TRIANGLES, this->triangles.size()*3,
GL_UNSIGNED_INT, 0);
}
void Object::setShader(Shader* shader) {
this->shader = shader;
}
void Object::openglInitialization() {
// initialize the vao, vbo, and ebo
GLuint vao;
glGenVertexArrays(1, &vao);
glBindVertexArray(vao);
this->vao = vao;
GLuint vbo;
glGenBuffers(1, &vbo);
GLuint ebo;
glGenBuffers(1, &ebo);
// create the vertex array
GLfloat vertexArray[this->vertices.size()*this->elms];
/*
The following for-loop got messy but was necessary because OpenGL
expects all vertex information (including the vertex normal) to be
in the vertex array (simply offset by however much we tell
it). This means we needed to map the vertex to its vertex normal
using the triangle faces (as they are the only things that have
that information). Because we were grabbing the vertices out of
the triangles, we needed new element indices as well, so that's
where q comes in.
*/
Triangle* t;
vector<uint> v, vn;
map<uint,uint> vertexMap; // mapping of vertex index to element index
uint q = 0; // element counter for current vertex
for (uint i = 0; i < this->triangles.size(); i++) {
t = &this->triangles[i];
v = t->getVertices();
vn = t->getVertexNormals();
for (uint k = 0; k < 3; k++) {
uint a = q*this->elms;
uint vi = v[k]-1;
uint vni = vn[k]-1;
if (vertexMap.find(vi) == vertexMap.end()) {
// add the vertex to the set and increment the counter
// the counter (q) is the element index
q++;
vertexMap.insert(pair<uint,uint>(vi,q));
// vertex position
glm::vec3 vert = this->vertices[vi];
for (uint j = 0; j < 3; j++) {
vertexArray[a+j] = vert[j];
}
a += 3;
// vertex normal
glm::vec3 vertn = this->vertexNormals[vni];
for (uint j = 0; j < 3; j++) {
vertexArray[a+j] = vertn[j];
}
a += 3;
// ambient color
for (uint j = 0; j < 3; j++) {
vertexArray[a+j] = this->ambient[j];
}
a += 3;
// diffuse color
for (uint j = 0; j < 3; j++) {
vertexArray[a+j] = this->diffuse[j];
}
a += 3;
// specular color
for (uint j = 0; j < 3; j++) {
vertexArray[a+j] = this->specular[j];
}
a += 3;
// specular exponent (shininess)
vertexArray[a] = this->shininess;
a += 1;
}
}
}
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertexArray),
vertexArray, GL_STATIC_DRAW);
/*
Here we finally create the element array that maps element indices
to vertices in the vertex array.
*/
uint elementArray[this->triangles.size()*3];
for (uint i = 0; i < this->triangles.size(); i++) {
uint a = i*3;
vector<uint> v = this->triangles[i].getVertices();
for (uint j = 0; j < 3; j++) {
elementArray[a+j] = vertexMap.find(v[j]-1)->second - 1;
}
}
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(elementArray),
elementArray, GL_STATIC_DRAW);
// setup the shaders
GLuint shaderProgram = this->shader->getProgram();
glUseProgram(shaderProgram);
// shader attributes
uint offset = 0;
GLint posAttrib = glGetAttribLocation(shaderProgram, "vPosition");
glEnableVertexAttribArray(posAttrib);
// (input, # of values, type, normalize, stride, offset)
glVertexAttribPointer(posAttrib, 3, GL_FLOAT, GL_FALSE,
this->elms*sizeof(float),
(void*)(offset*sizeof(float)));
offset += 3;
GLint normalAttrib = glGetAttribLocation(shaderProgram, "vNormal");
glEnableVertexAttribArray(normalAttrib);
glVertexAttribPointer(normalAttrib, 3, GL_FLOAT, GL_TRUE,
this->elms*sizeof(float),
(void*)(offset*sizeof(float)));
offset += 3;
GLint ambientAttrib = glGetAttribLocation(shaderProgram,
"ambientColor");
glEnableVertexAttribArray(ambientAttrib);
glVertexAttribPointer(ambientAttrib, 3, GL_FLOAT, GL_FALSE,
this->elms*sizeof(float),
(void*)(offset*sizeof(float)));
offset += 3;
GLint diffuseAttrib = glGetAttribLocation(shaderProgram,
"diffuseColor");
glEnableVertexAttribArray(diffuseAttrib);
glVertexAttribPointer(diffuseAttrib, 3, GL_FLOAT, GL_FALSE,
this->elms*sizeof(float),
(void*)(offset*sizeof(float)));
offset += 3;
GLint specularAttrib = glGetAttribLocation(shaderProgram,
"specularColor");
glEnableVertexAttribArray(specularAttrib);
glVertexAttribPointer(specularAttrib, 3, GL_FLOAT, GL_FALSE,
this->elms*sizeof(float),
(void*)(offset*sizeof(float)));
offset += 3;
GLint shininessAttrib = glGetAttribLocation(shaderProgram,
"shininess");
glEnableVertexAttribArray(shininessAttrib);
glVertexAttribPointer(shininessAttrib, 1, GL_FLOAT, GL_FALSE,
this->elms*sizeof(float),
(void*)(offset*sizeof(float)));
offset += 1;
}
GLuint Object::getVao() const {
return this->vao;
}
void Object::applyModelMatrix() const {
GLint uniModel = glGetUniformLocation(this->shader->getProgram(),
"model");
glUniformMatrix4fv(uniModel, 1, GL_FALSE,
glm::value_ptr(this->transformation));
}
void Object::setTransform(glm::mat4 t) {
this->transformation = t;
}
glm::mat4 Object::getTransform() const {
return this->transformation;
}
void Object::setAmbientColor(glm::vec3 c) {
this->ambient = c;
}
void Object::setDiffuseColor(glm::vec3 c) {
this->diffuse = c;
}
void Object::setSpecularColor(glm::vec3 c) {
this->specular = c;
}
void Object::setShininess(float s) {
this->shininess = s;
}
/**
This outputToObj function was part a school project on Bezier
Surfaces, so I kept it in here just because. Kinda useless for this
program though...
*/
void Object::outputToObj(string filename) const {
ofstream file;
file.open(filename.c_str());
file << "# " << filename
<< "\n\n";
file << "# vertices: should have " << this->vertices.size() << "\n";
// vertices
for (uint i = 0; i < this->vertices.size(); i++) {
glm::vec3 vert = this->vertices[i];
file << "v "
<< vert[0] << " " << vert[1] << " " << vert[2] << " " << "\n";
}
file << "\n\n\n";
file << "# vertex normals: " << this->vertexNormals.size() << "\n";
// vertex normals
for (uint i = 0; i < this->vertexNormals.size(); i++) {
glm::vec3 vert = this->vertexNormals[i];
file << "vn "
<< vert[0] << " " << vert[1] << " " << vert[2] << " " << "\n";
}
file << "\n\n\n";
file << "# triangle faces: should have " << this->triangles.size()
<< "\n";
// faces
for (uint i = 0; i < this->triangles.size(); i++) {
vector<uint> v = this->triangles[i].getVertices();
vector<uint> vn = this->triangles[i].getVertexNormals();
file << "f "
<< v[0] << "//" << vn[0] << " "
<< v[1] << "//" << vn[1] << " "
<< v[2] << "//" << vn[2] << "\n";
}
file << "\n\n# May the Llamas spit upon ye.";
}
/**
Private
*/
int Object::parseOBJVertex(string s) const {
string split = "/";
int slash = s.find(split);
if (!slash) {
return atoi(s.c_str());
}
string v = s.substr(0,slash);
return atoi(v.c_str());
}
int Object::parseOBJVertexNormal(string s) const {
string split = "//";
int slashes = s.find(split);
if (!slashes) {
return -1;
}
string n = s.substr(slashes + split.length());
return atoi(n.c_str());
}
glm::vec3 Object::surfaceNormal(Triangle t) const {
vector<uint> v = t.getVertices();
glm::vec3 a = this->vertices[v[0]];
glm::vec3 b = this->vertices[v[1]];
glm::vec3 c = this->vertices[v[2]];
glm::vec3 p = a - b;
glm::vec3 q = a - c;
return glm::cross(p,q);
}

87
src/Object.h Normal file
View File

@ -0,0 +1,87 @@
/**
Object - a storage class for a .obj file
Uses Triangle.h to keep track of faces.
Keeps a list of vertices and vertex normals here in the Object.
Shader attribute order in the Vertex Array:
(number of values in parentheses)
position 3
normal 3
ambient 3
diffuse 3
specular 3
shininess 1
*/
#ifndef OBJECT_H
#define OBJECT_H
#include <iostream>
#include <fstream>
#include <string>
#include <sstream>
#include <stdlib.h>
#include <vector>
#include <map>
#include <GL/glew.h>
#include <GLFW/glfw3.h>
#define GLM_FORCE_RADIANS
#include <glm/glm.hpp>
#include <glm/gtc/type_ptr.hpp>
#include "Triangle.h"
#include "Shader.h"
#include "utilities.h"
using namespace std;
class Object {
public:
Object();
Object(string filename, Shader* s);
void outputToObj(string filename) const;
void parseObjectFile(string filename);
void glDraw();
void setShader(Shader* shader);
void openglInitialization();
GLuint getVao() const;
glm::mat4 getTransform() const;
void setTransform(glm::mat4 t);
void applyModelMatrix() const;
void setAmbientColor(glm::vec3 c);
void setDiffuseColor(glm::vec3 c);
void setSpecularColor(glm::vec3 c);
void setShininess(float s);
private:
vector<Triangle> triangles;
vector<glm::vec3> vertices;
vector<glm::vec3> vertexNormals;
glm::mat4 transformation;
Shader* shader;
GLuint vao;
// shader elements
uint elms; // total number of elements per vertex
glm::vec3 ambient;
glm::vec3 diffuse;
glm::vec3 specular;
float shininess;
int init(string filename, Shader* s);
int parseOBJVertex(string s) const;
int parseOBJVertexNormal(string s) const;
glm::vec3 surfaceNormal(Triangle t) const;
};
#endif

103
src/Shader.cpp Normal file
View File

@ -0,0 +1,103 @@
/**
Shader - a storage class for the Vertex and Fragment Shader
*/
#include "Shader.h"
using namespace std;
int Shader::init(string vertFile, string fragFile) {
GLint successful = GL_TRUE;
string vertStr = this->loadSource(vertFile);
string fragStr = this->loadSource(fragFile);
const char* vertSource = vertStr.c_str();
const char* fragSource = fragStr.c_str();
// create the shaders
this->vert = glCreateShader(GL_VERTEX_SHADER);
this->frag = glCreateShader(GL_FRAGMENT_SHADER);
// give opengl the sources
glShaderSource(this->vert, 1, &vertSource, NULL);
glShaderSource(this->frag, 1, &fragSource, NULL);
// compile them
glCompileShader(this->vert);
glCompileShader(this->frag);
// check for errors
GLint status;
glGetShaderiv(this->vert, GL_COMPILE_STATUS, &status);
if (status != GL_TRUE) {
cerr << "Failed to compile the vertex shader\n\n";
char buffer[512];
glGetShaderInfoLog(this->vert, 512, NULL, buffer);
cerr << buffer << "\n\n";
successful = status;
}
status = GL_FALSE;
glGetShaderiv(this->frag, GL_COMPILE_STATUS, &status);
if (status != GL_TRUE) {
cerr << "Failed to compile the fragment shader\n\n";
char buffer[512];
glGetShaderInfoLog(this->frag, 512, NULL, buffer);
cerr << buffer << "\n\n";
successful = status;
}
GLuint shaderProgram = glCreateProgram();
this->program = shaderProgram;
glAttachShader(shaderProgram, this->vert);
glAttachShader(shaderProgram, this->frag);
// links up with the variable "outColor" in the shader source
glBindFragDataLocation(shaderProgram, 0, "outColor");
glLinkProgram(shaderProgram);
glUseProgram(shaderProgram);
return successful;
}
Shader::Shader(string vertFile, string fragFile) {
init(vertFile, fragFile);
}
Shader::Shader() {
this->vert = -1;
this->frag = -1;
this->program = -1;
}
GLuint Shader::getVert() const {
return this->vert;
}
GLuint Shader::getFrag() const {
return this->frag;
}
GLuint Shader::getProgram() const {
return this->program;
}
/**
Private
*/
string Shader::loadSource(string file) {
ifstream shaderFile (file.c_str());
string line;
stringstream ss;
if (shaderFile.is_open()) {
while (getline(shaderFile, line)) {
ss << line << "\n";
}
}
return ss.str();
}

32
src/Shader.h Normal file
View File

@ -0,0 +1,32 @@
/**
Shader - a storage class for an OpenGL shader
*/
#ifndef SHADER_H
#define SHADER_H
#include <GL/glew.h>
#include <GLFW/glfw3.h>
#include <iostream>
#include <string>
#include <fstream>
#include <sstream>
using namespace std;
class Shader {
public:
Shader();
Shader(string vertFile, string fragFile);
GLuint getVert() const;
GLuint getFrag() const;
GLuint getProgram() const;
private:
GLuint vert,frag,program;
int init(string vertFile, string fragFile);
string loadSource(string file);
};
#endif

View File

@ -0,0 +1,61 @@
/**
SimpleTriangleTest - a simple test case to make sure everything
is working correctly. Draws a rectangle using 2 triangles.
*/
#include "SimpleTriangleTest.h"
SimpleTriangleTest::SimpleTriangleTest() {
}
void SimpleTriangleTest::initialize(Shader* s) {
GLuint shaderProgram = s->getProgram();
GLuint vao;
glGenVertexArrays(1, &vao);
glBindVertexArray(vao);
GLuint vbo;
glGenBuffers(1, &vbo);
GLfloat vertices[] = {
-0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 0.0f, // Top-left
0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 0.0f, // Top-right
0.5f, -0.5f, 0.0f, 0.0f, 0.0f, 1.0f, // Bottom-right
-0.5f, -0.5f, 0.0f, 1.0f, 1.0f, 1.0f // Bottom-left
};
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices,
GL_STATIC_DRAW);
GLuint ebo;
glGenBuffers(1, &ebo);
GLuint elements[] = {
0, 1, 2,
2, 3, 0
};
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(elements),
elements, GL_STATIC_DRAW);
glUseProgram(shaderProgram);
GLint posAttrib = glGetAttribLocation(shaderProgram, "vPosition");
glEnableVertexAttribArray(posAttrib);
glVertexAttribPointer(posAttrib, 3, GL_FLOAT, GL_FALSE,
6*sizeof(GLfloat), 0);
GLint color = glGetAttribLocation(shaderProgram, "diffuseColor");
glEnableVertexAttribArray(color);
glVertexAttribPointer(color, 3, GL_FLOAT, GL_FALSE,
6*sizeof(GLfloat), (void*)(3*sizeof(GLfloat)));
}
void SimpleTriangleTest::glDraw() {
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
}

23
src/SimpleTriangleTest.h Normal file
View File

@ -0,0 +1,23 @@
/**
SimpleTriangleTest - a simple test case to make sure everything
is working correctly. Draws a rectangle using 2 triangles.
*/
#ifndef SIMPLETRIANGLETEST_H
#define SIMPLETRIANGLETEST_H
#include <GL/glew.h>
#include <GLFW/glfw3.h>
#include "Shader.h"
class SimpleTriangleTest {
public:
SimpleTriangleTest ();
void initialize(Shader* s);
void glDraw();
private:
};
#endif

60
src/Triangle.cpp Normal file
View File

@ -0,0 +1,60 @@
/**
Triangle - a storage class for a triangle face from a .obj file
*/
#include "Triangle.h"
int Triangle::init(uint a, uint b, uint c,
uint an, uint bn, uint cn) {
std::vector<uint> v, vn;
// vertex indices
v.push_back(a);
v.push_back(b);
v.push_back(c);
// vertex normal indices
vn.push_back(an);
vn.push_back(bn);
vn.push_back(cn);
this->vertices = v;
this->vertexNormals = vn;
this->elementWasSet = false;
return 0;
}
Triangle::Triangle() {
init(0,0,0,0,0,0);
}
Triangle::Triangle(uint a, uint b, uint c,
uint an, uint bn, uint cn) {
init(a,b,c,an,bn,cn);
}
Triangle::Triangle(uint a, uint b, uint c) {
init(a,b,c,0,0,0);
}
std::vector<uint> Triangle::getVertices() const {
return this->vertices;
}
std::vector<uint> Triangle::getVertexNormals() const {
return this->vertexNormals;
}
void Triangle::setElementBase(uint e) {
this->element = e;
this->elementWasSet = true;
}
uint Triangle::getElementBase() const {
return this->element;
}
bool Triangle::elementIsSet() const {
return this->elementWasSet;
}

38
src/Triangle.h Normal file
View File

@ -0,0 +1,38 @@
/**
Triangle - a storage class for a triangle
*/
#ifndef TRIANGLE_H
#define TRIANGLE_H
#include <vector>
typedef unsigned int uint;
class Triangle {
public:
Triangle();
Triangle(uint a, uint b, uint c,
uint an, uint bn, uint cn);
Triangle(uint a, uint b, uint c);
std::vector<uint> getVertices() const;
std::vector<uint> getVertexNormals() const;
void setElementBase(uint e);
uint getElementBase() const;
bool elementIsSet() const;
private:
std::vector<uint> vertices, vertexNormals;
// This is the element index for the first vertex.
// The other two are e+1 and e+2 respectively
uint element;
bool elementWasSet;
int init(uint a, uint b, uint c,
uint an, uint bn, uint cn);
};
#endif

238
src/main.cpp Normal file
View File

@ -0,0 +1,238 @@
#include <GL/glew.h>
#include <GLFW/glfw3.h>
#define GLM_FORCE_RADIANS
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include "Camera.h"
#include "Shader.h"
#include "Object.h"
#include "utilities.h"
using namespace std;
/**
Some global storage classes
*/
class Viewport {
public:
uint width, height;
float fov, near, far;
};
Viewport viewport;
class Globals {
public:
vector<Object> objects;
Camera camera;
float translate_step, rotate_step;
GLFWwindow* window;
Shader shader;
};
Globals globals;
/**
and the global initializations
*/
void initGlobals() {
// viewport
viewport.width = 800;
viewport.height = 600;
viewport.near = 0.1f;
viewport.far = 100;
viewport.fov = 45.0f;
// globals
globals.translate_step = 0.05f;
globals.rotate_step = 0.01f;
}
// pre-declaration
void updateViewMatrix();
void drawScene() {
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
updateViewMatrix();
for (uint i = 0; i < globals.objects.size(); i++) {
globals.objects[i].glDraw();
}
}
void updateViewMatrix() {
GLuint shaderProgram = globals.shader.getProgram();
glm::mat4 view = glm::lookAt
(globals.camera.getPosition(),
globals.camera.getTarget(), // the position to look at
globals.camera.getUpDir());
GLint uniView = glGetUniformLocation(shaderProgram, "view");
glUniformMatrix4fv(uniView, 1, GL_FALSE, glm::value_ptr(view));
}
void initializeMatrices() {
GLuint shaderProgram = globals.shader.getProgram();
glm::mat4 model;
// example
//model = glm::rotate(model, PI, glm::vec3(0.0f,0.0f,1.0f));
GLint uniModel = glGetUniformLocation(shaderProgram, "model");
// (var, # of matrices, transpose, matrix as an array)
glUniformMatrix4fv(uniModel, 1, GL_FALSE, glm::value_ptr(model));
glm::mat4 view = glm::lookAt
(globals.camera.getPosition(),
globals.camera.getTarget(), // the position to look at
globals.camera.getUpDir());
GLint uniView = glGetUniformLocation(shaderProgram, "view");
glUniformMatrix4fv(uniView, 1, GL_FALSE, glm::value_ptr(view));
glm::mat4 proj =
glm::perspective(viewport.fov,1.0f*viewport.width/viewport.height,
viewport.near, viewport.far);
GLint uniProj = glGetUniformLocation(shaderProgram, "proj");
glUniformMatrix4fv(uniProj, 1, GL_FALSE, glm::value_ptr(proj));
}
void moveCamera() {
float r = globals.rotate_step;
float t = globals.translate_step;
glm::vec3 up = globals.camera.getUpDir();
glm::vec3 forward = globals.camera.getViewDir();
glm::vec3 right = glm::cross(forward,up);
// rotation
if (glfwGetKey(globals.window, 'J') == GLFW_PRESS) {
globals.camera.rotate(r*up);
}
if (glfwGetKey(globals.window, 'L') == GLFW_PRESS) {
globals.camera.rotate(-r*up);
}
if (glfwGetKey(globals.window, 'I') == GLFW_PRESS) {
globals.camera.rotate(r*right);
}
if (glfwGetKey(globals.window, 'K') == GLFW_PRESS) {
globals.camera.rotate(-r*right);
}
if (glfwGetKey(globals.window, 'U') == GLFW_PRESS) {
globals.camera.rotate(-r*forward);
}
if (glfwGetKey(globals.window, 'O') == GLFW_PRESS) {
globals.camera.rotate(r*forward);
}
// translation
if (glfwGetKey(globals.window, 'A') == GLFW_PRESS) {
globals.camera.translate(-t*right);
}
if (glfwGetKey(globals.window, 'D') == GLFW_PRESS) {
globals.camera.translate(t*right);
}
if (glfwGetKey(globals.window, 'W') == GLFW_PRESS) {
globals.camera.translate(t*forward);
}
if (glfwGetKey(globals.window, 'S') == GLFW_PRESS) {
globals.camera.translate(-t*forward);
}
if (glfwGetKey(globals.window, 'Q') == GLFW_PRESS) {
globals.camera.translate(t*up);
}
if (glfwGetKey(globals.window, 'E') == GLFW_PRESS) {
globals.camera.translate(-t*up);
}
}
GLFWwindow* initializeGlfw() {
/**
Basic GLFW initialization for OpenGL 3.2+ only
with no backwards compatibility.
*/
glfwInit();
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
glfwWindowHint(GLFW_RESIZABLE, GL_FALSE);
// windowed
GLFWwindow* window =
glfwCreateWindow(viewport.width, viewport.height,
"Object Loader", NULL, NULL);
// fullscreen
/*GLFWwindow* window =
glfwCreateWindow(800, 600, "OpenGL",
glfwGetPrimaryMonitor(), NULL); */
glfwMakeContextCurrent(window);
return window;
}
int main(int argc, char* argv[]) {
// initialize the global settings
initGlobals();
// set up GLFW
GLFWwindow* window = initializeGlfw();
globals.window = window;
// GLEW setup
glewExperimental = GL_TRUE;
glewInit();
// everything currently uses the same shader
Shader shader ("shaders/vertexShader.c", "shaders/fragmentShader.c");
globals.shader = shader;
// parse args
int i;
for (i = 1; i < argc; i++) {
string s (argv[i]);
Object obj (s, &globals.shader);
globals.objects.push_back(obj);
}
i--;
cout << "Loaded " << i << " ";
if (i > 1) {
cout << "objects ";
} else {
cout << "object ";
}
cout << "successfully\n";
initializeMatrices();
glEnable(GL_DEPTH_TEST);
/**
the main, infinite loop
*/
while(!glfwWindowShouldClose(window)) {
glfwSwapBuffers(window);
glfwPollEvents();
if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS) {
glfwSetWindowShouldClose(window, GL_TRUE);
cout << "Quitting\n\n";
}
moveCamera();
drawScene();
}
glfwTerminate();
return 0;
}

25
src/utilities.cpp Normal file
View File

@ -0,0 +1,25 @@
/**
utilities - extra functions that didn't really belong
inside of a class or the main file.
*/
#include "utilities.h"
using namespace std;
void printGlmMat4(glm::mat4 m) {
for (uint i = 0; i < 4; i++) {
for (uint j = 0; j < 4; j++) {
cout << m[i][j] << " ";
}
cout << endl;
}
cout << endl;
}
void printGlmVec3(glm::vec3 v) {
for (uint i = 0; i < 3; i++) {
cout << v[i] << " ";
}
cout << endl;
}

18
src/utilities.h Normal file
View File

@ -0,0 +1,18 @@
/**
utilities - extra functions that didn't really belong
inside of a class or the main file.
*/
#ifndef UTILITIES_H
#define UTILITIES_H
#include <iostream>
#include <glm/glm.hpp>
using namespace std;
void printGlmMat4(glm::mat4 m);
void printGlmVec3(glm::vec3 v);
#endif