From 9768278cf1d5359b94663072f5aa96d2483a9036 Mon Sep 17 00:00:00 2001 From: Alex Date: Sat, 29 Apr 2017 23:01:21 -0500 Subject: [PATCH] Completed A6 --- A6/README.txt | 8 + A6/resources/simple_frag.glsl | 8 - A6/resources/simple_vert.glsl | 11 - A6/resources/skin_frag.glsl | 8 - A6/resources/skin_vert.glsl | 15 - A6/shadow8t4.zip | Bin 0 -> 29628 bytes A6/shadow8t4/shadow8t4/CMakeLists.txt | 127 ++ A6/shadow8t4/shadow8t4/README.txt | 9 + A6/shadow8t4/shadow8t4/resources/frag.glsl | 37 + A6/shadow8t4/shadow8t4/resources/vert.glsl | 24 + A6/shadow8t4/shadow8t4/src/Camera.cpp | 68 + A6/shadow8t4/shadow8t4/src/Camera.h | 47 + A6/shadow8t4/shadow8t4/src/GLSL.cpp | 152 ++ A6/shadow8t4/shadow8t4/src/GLSL.h | 40 + A6/shadow8t4/shadow8t4/src/Material.cpp | 39 + A6/shadow8t4/shadow8t4/src/Material.h | 46 + A6/shadow8t4/shadow8t4/src/MatrixStack.cpp | 114 ++ A6/shadow8t4/shadow8t4/src/MatrixStack.h | 50 + A6/shadow8t4/shadow8t4/src/Program.cpp | 127 ++ A6/shadow8t4/shadow8t4/src/Program.h | 45 + A6/shadow8t4/shadow8t4/src/ShapeSkin.cpp | 316 +++ A6/shadow8t4/shadow8t4/src/ShapeSkin.h | 52 + A6/shadow8t4/shadow8t4/src/main.cpp | 291 +++ A6/shadow8t4/shadow8t4/src/tiny_obj_loader.h | 1922 ++++++++++++++++++ A6/src/Material.cpp | 39 + A6/src/Material.h | 46 + A6/src/ShapeSkin.cpp | 182 +- A6/src/ShapeSkin.h | 23 +- A6/src/main.cpp | 30 +- 29 files changed, 3821 insertions(+), 55 deletions(-) delete mode 100644 A6/resources/simple_frag.glsl delete mode 100644 A6/resources/simple_vert.glsl delete mode 100644 A6/resources/skin_frag.glsl delete mode 100644 A6/resources/skin_vert.glsl create mode 100644 A6/shadow8t4.zip create mode 100644 A6/shadow8t4/shadow8t4/CMakeLists.txt create mode 100644 A6/shadow8t4/shadow8t4/README.txt create mode 100644 A6/shadow8t4/shadow8t4/resources/frag.glsl create mode 100644 A6/shadow8t4/shadow8t4/resources/vert.glsl create mode 100644 A6/shadow8t4/shadow8t4/src/Camera.cpp create mode 100644 A6/shadow8t4/shadow8t4/src/Camera.h create mode 100644 A6/shadow8t4/shadow8t4/src/GLSL.cpp create mode 100644 A6/shadow8t4/shadow8t4/src/GLSL.h create mode 100644 A6/shadow8t4/shadow8t4/src/Material.cpp create mode 100644 A6/shadow8t4/shadow8t4/src/Material.h create mode 100644 A6/shadow8t4/shadow8t4/src/MatrixStack.cpp create mode 100644 A6/shadow8t4/shadow8t4/src/MatrixStack.h create mode 100644 A6/shadow8t4/shadow8t4/src/Program.cpp create mode 100644 A6/shadow8t4/shadow8t4/src/Program.h create mode 100644 A6/shadow8t4/shadow8t4/src/ShapeSkin.cpp create mode 100644 A6/shadow8t4/shadow8t4/src/ShapeSkin.h create mode 100644 A6/shadow8t4/shadow8t4/src/main.cpp create mode 100644 A6/shadow8t4/shadow8t4/src/tiny_obj_loader.h create mode 100644 A6/src/Material.cpp create mode 100644 A6/src/Material.h diff --git a/A6/README.txt b/A6/README.txt index 1bd43df..dc0589d 100644 --- a/A6/README.txt +++ b/A6/README.txt @@ -1 +1,9 @@ +Alexander Huddleston + This mesh was initially created using Cosmic Blobs software developed by Dassault Systemes SolidWorks Corp. + +Stage 2 + +None. + +Persona 5 is a really good game tbh. diff --git a/A6/resources/simple_frag.glsl b/A6/resources/simple_frag.glsl deleted file mode 100644 index c1b3b08..0000000 --- a/A6/resources/simple_frag.glsl +++ /dev/null @@ -1,8 +0,0 @@ -#version 120 - -varying vec3 vColor; - -void main() -{ - gl_FragColor = vec4(vColor, 1.0); -} diff --git a/A6/resources/simple_vert.glsl b/A6/resources/simple_vert.glsl deleted file mode 100644 index 6a5a916..0000000 --- a/A6/resources/simple_vert.glsl +++ /dev/null @@ -1,11 +0,0 @@ -#version 120 - -uniform mat4 P; -uniform mat4 MV; -varying vec3 vColor; - -void main() -{ - gl_Position = P * (MV * gl_Vertex); - vColor = gl_Color.rgb; -} diff --git a/A6/resources/skin_frag.glsl b/A6/resources/skin_frag.glsl deleted file mode 100644 index c1b3b08..0000000 --- a/A6/resources/skin_frag.glsl +++ /dev/null @@ -1,8 +0,0 @@ -#version 120 - -varying vec3 vColor; - -void main() -{ - gl_FragColor = vec4(vColor, 1.0); -} diff --git a/A6/resources/skin_vert.glsl b/A6/resources/skin_vert.glsl deleted file mode 100644 index 94a72cc..0000000 --- a/A6/resources/skin_vert.glsl +++ /dev/null @@ -1,15 +0,0 @@ -#version 120 - -attribute vec4 aPos; -attribute vec3 aNor; - -uniform mat4 P; -uniform mat4 MV; - -varying vec3 vColor; - -void main() -{ - gl_Position = P * (MV * aPos); - vColor = 0.5*aNor + 0.5; -} diff --git a/A6/shadow8t4.zip b/A6/shadow8t4.zip new file mode 100644 index 0000000000000000000000000000000000000000..c5f82b599dcb82ca6dea61942c1bd8b2e4ca9d57 GIT binary patch literal 29628 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 DAYDdl literal 0 HcmV?d00001 diff --git a/A6/shadow8t4/shadow8t4/CMakeLists.txt b/A6/shadow8t4/shadow8t4/CMakeLists.txt new file mode 100644 index 0000000..679d672 --- /dev/null +++ b/A6/shadow8t4/shadow8t4/CMakeLists.txt @@ -0,0 +1,127 @@ +CMAKE_MINIMUM_REQUIRED(VERSION 2.8) + +# Name of the project +PROJECT(A6) + +# FOR LAB MACHINES ONLY! +# DO NOT EDIT +SET(DEF_DIR_GLM "C:\\c++\\glm") +SET(DEF_DIR_GLFW "C:\\c++\\glfw-3.2.1") +SET(DEF_DIR_GLEW "C:\\c++\\glew-2.0.0") + +# Is this the solution? +# Override with `cmake -DSOL=ON ..` +OPTION(SOL "Solution" OFF) + +# Use glob to get the list of all source files. +# We don't really need to include header and resource files to build, but it's +# nice to have them also show up in IDEs. +IF(${SOL}) + FILE(GLOB_RECURSE SOURCES "src0/*.cpp") + FILE(GLOB_RECURSE HEADERS "src0/*.h") + FILE(GLOB_RECURSE GLSL "resources0/*.glsl") +ELSE() + FILE(GLOB_RECURSE SOURCES "src/*.cpp") + FILE(GLOB_RECURSE HEADERS "src/*.h") + FILE(GLOB_RECURSE GLSL "resources/*.glsl") +ENDIF() + +# Set the executable. +ADD_EXECUTABLE(${CMAKE_PROJECT_NAME} ${SOURCES} ${HEADERS} ${GLSL}) + +# Get the GLM environment variable. Since GLM is a header-only library, we +# just need to add it to the include directory. +SET(GLM_INCLUDE_DIR "$ENV{GLM_INCLUDE_DIR}") +IF(NOT GLM_INCLUDE_DIR) + # The environment variable was not set + SET(ERR_MSG "Please point the environment variable GLM_INCLUDE_DIR to the root directory of your GLM installation.") + IF(WIN32) + # On Windows, try the default location + MESSAGE(STATUS "Looking for GLM in ${DEF_DIR_GLM}") + IF(IS_DIRECTORY ${DEF_DIR_GLM}) + MESSAGE(STATUS "Found!") + SET(GLM_INCLUDE_DIR ${DEF_DIR_GLM}) + ELSE() + MESSAGE(FATAL_ERROR ${ERR_MSG}) + ENDIF() + ELSE() + MESSAGE(FATAL_ERROR ${ERR_MSG}) + ENDIF() +ENDIF() +INCLUDE_DIRECTORIES(${GLM_INCLUDE_DIR}) + +# Get the GLFW environment variable. There should be a CMakeLists.txt in the +# specified directory. +SET(GLFW_DIR "$ENV{GLFW_DIR}") +IF(NOT GLFW_DIR) + # The environment variable was not set + SET(ERR_MSG "Please point the environment variable GLFW_DIR to the root directory of your GLFW installation.") + IF(WIN32) + # On Windows, try the default location + MESSAGE(STATUS "Looking for GLFW in ${DEF_DIR_GLFW}") + IF(IS_DIRECTORY ${DEF_DIR_GLFW}) + MESSAGE(STATUS "Found!") + SET(GLFW_DIR ${DEF_DIR_GLFW}) + ELSE() + MESSAGE(FATAL_ERROR ${ERR_MSG}) + ENDIF() + ELSE() + MESSAGE(FATAL_ERROR ${ERR_MSG}) + ENDIF() +ENDIF() +OPTION(GLFW_BUILD_EXAMPLES "GLFW_BUILD_EXAMPLES" OFF) +OPTION(GLFW_BUILD_TESTS "GLFW_BUILD_TESTS" OFF) +OPTION(GLFW_BUILD_DOCS "GLFW_BUILD_DOCS" OFF) +IF(CMAKE_BUILD_TYPE MATCHES Release) + ADD_SUBDIRECTORY(${GLFW_DIR} ${GLFW_DIR}/release) +ELSE() + ADD_SUBDIRECTORY(${GLFW_DIR} ${GLFW_DIR}/debug) +ENDIF() +INCLUDE_DIRECTORIES(${GLFW_DIR}/include) +TARGET_LINK_LIBRARIES(${CMAKE_PROJECT_NAME} glfw ${GLFW_LIBRARIES}) + +# Get the GLEW environment variable. +SET(GLEW_DIR "$ENV{GLEW_DIR}") +IF(NOT GLEW_DIR) + # The environment variable was not set + SET(ERR_MSG "Please point the environment variable GLEW_DIR to the root directory of your GLEW installation.") + IF(WIN32) + # On Windows, try the default location + MESSAGE(STATUS "Looking for GLEW in ${DEF_DIR_GLEW}") + IF(IS_DIRECTORY ${DEF_DIR_GLEW}) + MESSAGE(STATUS "Found!") + SET(GLEW_DIR ${DEF_DIR_GLEW}) + ELSE() + MESSAGE(FATAL_ERROR ${ERR_MSG}) + ENDIF() + ELSE() + MESSAGE(FATAL_ERROR ${ERR_MSG}) + ENDIF() +ENDIF() +INCLUDE_DIRECTORIES(${GLEW_DIR}/include) +IF(WIN32) + # With prebuilt binaries + TARGET_LINK_LIBRARIES(${CMAKE_PROJECT_NAME} ${GLEW_DIR}/lib/Release/Win32/glew32s.lib) +ELSE() + TARGET_LINK_LIBRARIES(${CMAKE_PROJECT_NAME} ${GLEW_DIR}/lib/libGLEW.a) +ENDIF() + +# OS specific options and libraries +IF(WIN32) + # c++11 is enabled by default. + # -Wall produces way too many warnings. + # -pedantic is not supported. + # Disable warning 4996. + SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /wd4996") + TARGET_LINK_LIBRARIES(${CMAKE_PROJECT_NAME} opengl32.lib) +ELSE() + # Enable all pedantic warnings. + SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -Wall -pedantic") + IF(APPLE) + # Add required frameworks for GLFW. + TARGET_LINK_LIBRARIES(${CMAKE_PROJECT_NAME} "-framework OpenGL -framework Cocoa -framework IOKit -framework CoreVideo") + ELSE() + #Link the Linux OpenGL library + TARGET_LINK_LIBRARIES(${CMAKE_PROJECT_NAME} "GL") + ENDIF() +ENDIF() diff --git a/A6/shadow8t4/shadow8t4/README.txt b/A6/shadow8t4/shadow8t4/README.txt new file mode 100644 index 0000000..dc0589d --- /dev/null +++ b/A6/shadow8t4/shadow8t4/README.txt @@ -0,0 +1,9 @@ +Alexander Huddleston + +This mesh was initially created using Cosmic Blobs software developed by Dassault Systemes SolidWorks Corp. + +Stage 2 + +None. + +Persona 5 is a really good game tbh. diff --git a/A6/shadow8t4/shadow8t4/resources/frag.glsl b/A6/shadow8t4/shadow8t4/resources/frag.glsl new file mode 100644 index 0000000..ff05fa8 --- /dev/null +++ b/A6/shadow8t4/shadow8t4/resources/frag.glsl @@ -0,0 +1,37 @@ +#version 120 + +uniform mat4 MVL; +uniform vec3 lightPos1; +uniform vec3 lightPos2; +uniform vec3 ka; +uniform vec3 kd; +uniform vec3 ks; +uniform float s; +uniform float i1; +uniform float i2; + +varying vec3 color; // passed from the vertex shader +varying vec4 p; +varying vec4 n; + +void main() +{ + vec4 normal = normalize(n); + vec3 norm = vec3(normal.x, normal.y, normal.z); + + vec4 npos = normalize(p); + vec3 pos = vec3(npos.x, npos.y, npos.z); + + vec3 lightnorm = vec3(MVL[3].x, MVL[3].y, MVL[3].z); + + vec3 light = lightnorm - vec3(p.x, p.y, p.z); + vec3 lnorm = normalize(vec3(light.x,light.y,light.z)); + float temp = dot(lnorm, norm); + vec3 cd = kd*max(0, temp); + + vec3 h = normalize(lnorm - pos); + vec3 cs = ks*pow(max(0, dot(h, norm)), s); + + vec4 c = vec4(ka.r + cd.r + cs.r, ka.g + cd.g + cs.g, ka.b + cd.b + cs.b, 1.0); + gl_FragColor = c; +} diff --git a/A6/shadow8t4/shadow8t4/resources/vert.glsl b/A6/shadow8t4/shadow8t4/resources/vert.glsl new file mode 100644 index 0000000..ab647c8 --- /dev/null +++ b/A6/shadow8t4/shadow8t4/resources/vert.glsl @@ -0,0 +1,24 @@ +#version 120 + +uniform mat4 P; +uniform mat4 MV; +uniform mat4 MVL; +uniform vec3 lightPos1; +uniform vec3 lightPos2; +uniform float i1; +uniform float i2; + +attribute vec4 aPos; // in object space +attribute vec3 aNor; // in object space + +varying vec3 color; // Pass to fragment shader +varying vec4 p; +varying vec4 n; + +void main() +{ + gl_Position = P * MV * aPos; + p = MV * aPos; + n = MV * vec4(aNor, 0.0); + color = vec3(0.5, 0.5, 0.5); +} diff --git a/A6/shadow8t4/shadow8t4/src/Camera.cpp b/A6/shadow8t4/shadow8t4/src/Camera.cpp new file mode 100644 index 0000000..bf3b9c6 --- /dev/null +++ b/A6/shadow8t4/shadow8t4/src/Camera.cpp @@ -0,0 +1,68 @@ +#define _USE_MATH_DEFINES +#include +#include +#include +#include "Camera.h" +#include "MatrixStack.h" + +Camera::Camera() : + aspect(1.0f), + fovy((float)(45.0*M_PI/180.0)), + znear(0.1f), + zfar(1000.0f), + rotations(0.0, 0.0), + translations(0.0f, 0.0f, -5.0f), + rfactor(0.01f), + tfactor(0.001f), + sfactor(0.005f) +{ +} + +Camera::~Camera() +{ +} + +void Camera::mouseClicked(float x, float y, bool shift, bool ctrl, bool alt) +{ + mousePrev.x = x; + mousePrev.y = y; + if(shift) { + state = Camera::TRANSLATE; + } else if(ctrl) { + state = Camera::SCALE; + } else { + state = Camera::ROTATE; + } +} + +void Camera::mouseMoved(float x, float y) +{ + glm::vec2 mouseCurr(x, y); + glm::vec2 dv = mouseCurr - mousePrev; + switch(state) { + case Camera::ROTATE: + rotations += rfactor * dv; + break; + case Camera::TRANSLATE: + translations.x -= translations.z * tfactor * dv.x; + translations.y += translations.z * tfactor * dv.y; + break; + case Camera::SCALE: + translations.z *= (1.0f - sfactor * dv.y); + break; + } + mousePrev = mouseCurr; +} + +void Camera::applyProjectionMatrix(std::shared_ptr P) const +{ + // Modify provided MatrixStack + P->multMatrix(glm::perspective(fovy, aspect, znear, zfar)); +} + +void Camera::applyViewMatrix(std::shared_ptr MV) const +{ + MV->translate(translations); + MV->rotate(rotations.y, glm::vec3(1.0f, 0.0f, 0.0f)); + MV->rotate(rotations.x, glm::vec3(0.0f, 1.0f, 0.0f)); +} diff --git a/A6/shadow8t4/shadow8t4/src/Camera.h b/A6/shadow8t4/shadow8t4/src/Camera.h new file mode 100644 index 0000000..8501605 --- /dev/null +++ b/A6/shadow8t4/shadow8t4/src/Camera.h @@ -0,0 +1,47 @@ +#pragma once +#ifndef __Camera__ +#define __Camera__ + +#include + +#define GLM_FORCE_RADIANS +#include + +class MatrixStack; + +class Camera +{ +public: + enum { + ROTATE = 0, + TRANSLATE, + SCALE + }; + + Camera(); + virtual ~Camera(); + void setInitDistance(float z) { translations.z = -std::abs(z); } + void setAspect(float a) { aspect = a; }; + void setRotationFactor(float f) { rfactor = f; }; + void setTranslationFactor(float f) { tfactor = f; }; + void setScaleFactor(float f) { sfactor = f; }; + void mouseClicked(float x, float y, bool shift, bool ctrl, bool alt); + void mouseMoved(float x, float y); + void applyProjectionMatrix(std::shared_ptr P) const; + void applyViewMatrix(std::shared_ptr MV) const; + +private: + float aspect; + float fovy; + float znear; + float zfar; + glm::vec2 rotations; + glm::vec3 translations; + glm::vec2 mousePrev; + int state; + float rfactor; + float tfactor; + float sfactor; +}; + +#endif diff --git a/A6/shadow8t4/shadow8t4/src/GLSL.cpp b/A6/shadow8t4/shadow8t4/src/GLSL.cpp new file mode 100644 index 0000000..2969872 --- /dev/null +++ b/A6/shadow8t4/shadow8t4/src/GLSL.cpp @@ -0,0 +1,152 @@ +// +// Many useful helper functions for GLSL shaders - gleaned from various sources including orange book +// Created by zwood on 2/21/10. +// Modified by sueda 10/15/15. +// + +#include "GLSL.h" +#include +#include +#include +#include + +using namespace std; + +namespace GLSL { + +const char * errorString(GLenum err) +{ + switch(err) { + case GL_NO_ERROR: + return "No error"; + case GL_INVALID_ENUM: + return "Invalid enum"; + case GL_INVALID_VALUE: + return "Invalid value"; + case GL_INVALID_OPERATION: + return "Invalid operation"; + case GL_STACK_OVERFLOW: + return "Stack overflow"; + case GL_STACK_UNDERFLOW: + return "Stack underflow"; + case GL_OUT_OF_MEMORY: + return "Out of memory"; + default: + return "No error"; + } +} + +void checkVersion() +{ + int major, minor; + major = minor = 0; + const char *verstr = (const char *)glGetString(GL_VERSION); + + if((verstr == NULL) || (sscanf(verstr, "%d.%d", &major, &minor) != 2)) { + printf("Invalid GL_VERSION format %d.%d\n", major, minor); + } + if(major < 2) { + printf("This shader example will not work due to the installed Opengl version, which is %d.%d.\n", major, minor); + exit(0); + } +} + +void checkError(const char *str) +{ + GLenum glErr = glGetError(); + if(glErr != GL_NO_ERROR) { + if(str) { + printf("%s: ", str); + } + printf("GL_ERROR = %s.\n", errorString(glErr)); + assert(false); + } +} + +void printShaderInfoLog(GLuint shader) +{ + GLint infologLength = 0; + GLint charsWritten = 0; + GLchar *infoLog = 0; + + checkError(GET_FILE_LINE); + glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infologLength); + checkError(GET_FILE_LINE); + + if(infologLength > 0) { + infoLog = (GLchar *)malloc(infologLength); + if(infoLog == NULL) { + puts("ERROR: Could not allocate InfoLog buffer"); + exit(1); + } + glGetShaderInfoLog(shader, infologLength, &charsWritten, infoLog); + checkError(GET_FILE_LINE); + printf("Shader InfoLog:\n%s\n\n", infoLog); + free(infoLog); + } +} + +void printProgramInfoLog(GLuint program) +{ + GLint infologLength = 0; + GLint charsWritten = 0; + GLchar *infoLog = 0; + + checkError(GET_FILE_LINE); + glGetProgramiv(program, GL_INFO_LOG_LENGTH, &infologLength); + checkError(GET_FILE_LINE); + + if(infologLength > 0) { + infoLog = (GLchar *)malloc(infologLength); + if(infoLog == NULL) { + puts("ERROR: Could not allocate InfoLog buffer"); + exit(1); + } + glGetProgramInfoLog(program, infologLength, &charsWritten, infoLog); + checkError(GET_FILE_LINE); + printf("Program InfoLog:\n%s\n\n", infoLog); + free(infoLog); + } +} + +char *textFileRead(const char *fn) +{ + FILE *fp; + char *content = NULL; + int count = 0; + if(fn != NULL) { + fp = fopen(fn,"rt"); + if(fp != NULL) { + fseek(fp, 0, SEEK_END); + count = (int)ftell(fp); + rewind(fp); + if(count > 0) { + content = (char *)malloc(sizeof(char) * (count+1)); + count = (int)fread(content,sizeof(char),count,fp); + content[count] = '\0'; + } + fclose(fp); + } else { + printf("error loading %s\n", fn); + } + } + return content; +} + +int textFileWrite(const char *fn, const char *s) +{ + FILE *fp; + int status = 0; + if(fn != NULL) { + fp = fopen(fn,"w"); + if(fp != NULL) { + if(fwrite(s,sizeof(char),strlen(s),fp) == strlen(s)) { + status = 1; + } + fclose(fp); + } + } + return(status); +} + +} diff --git a/A6/shadow8t4/shadow8t4/src/GLSL.h b/A6/shadow8t4/shadow8t4/src/GLSL.h new file mode 100644 index 0000000..f945fdd --- /dev/null +++ b/A6/shadow8t4/shadow8t4/src/GLSL.h @@ -0,0 +1,40 @@ +// +// Many useful helper functions for GLSL shaders - gleaned from various sources including orange book +// Created by zwood on 2/21/10. +// Modified by sueda 10/15/15. +// + +#pragma once +#ifndef __GLSL__ +#define __GLSL__ + +#define GLEW_STATIC +#include + +/////////////////////////////////////////////////////////////////////////////// +// For printing out the current file and line number // +/////////////////////////////////////////////////////////////////////////////// +#include + +template +std::string NumberToString(T x) +{ + std::ostringstream ss; + ss << x; + return ss.str(); +} + +#define GET_FILE_LINE (std::string(__FILE__) + ":" + NumberToString(__LINE__)).c_str() +/////////////////////////////////////////////////////////////////////////////// + +namespace GLSL { + + void checkVersion(); + void checkError(const char *str = 0); + void printProgramInfoLog(GLuint program); + void printShaderInfoLog(GLuint shader); + int textFileWrite(const char *filename, const char *s); + char *textFileRead(const char *filename); +} + +#endif diff --git a/A6/shadow8t4/shadow8t4/src/Material.cpp b/A6/shadow8t4/shadow8t4/src/Material.cpp new file mode 100644 index 0000000..02317e3 --- /dev/null +++ b/A6/shadow8t4/shadow8t4/src/Material.cpp @@ -0,0 +1,39 @@ +#include "Material.h" + +using namespace std; + +Material::Material() +{ + this->ca = glm::vec3(0.3f,0.3f,0.3f); + this->cd = glm::vec3(0.3f,0.3f,0.3f); + this->cs = glm::vec3(1.0f,1.0f,1.0f); + this->shine = 0.0f; +} + +void Material::setMaterial(glm::vec3 a, glm::vec3 d, glm::vec3 s, float sh) +{ + this->ca = a; + this->cd = d; + this->cs = s; + this->shine = sh; +} + +glm::vec3 Material::getAmbient() const +{ + return this->ca; +} + +glm::vec3 Material::getDiffuse() const +{ + return this->cd; +} + +glm::vec3 Material::getSpecular() const +{ + return this->cs; +} + +float Material::getShiny() const +{ + return this->shine; +} diff --git a/A6/shadow8t4/shadow8t4/src/Material.h b/A6/shadow8t4/shadow8t4/src/Material.h new file mode 100644 index 0000000..71ee379 --- /dev/null +++ b/A6/shadow8t4/shadow8t4/src/Material.h @@ -0,0 +1,46 @@ +#pragma once +#ifndef _MATERIAL_H_ +#define _MATERIAL_H_ + +#include +#include +#include +#include +#include +#include + +#include "GLSL.h" +#include "Camera.h" +#include "MatrixStack.h" + +class Material +{ + private: + glm::vec3 ca; + glm::vec3 cd; + glm::vec3 cs; + float shine; + + public: + Material(); + Material(const Material &m) + { + ca = m.ca; + cd = m.cd; + cs = m.cs; + } + Material(glm::vec3 a, glm::vec3 d, glm::vec3 s, float sh) + { + ca = a; + cd = d; + cs = s; + shine = sh; + } + void setMaterial(glm::vec3 a, glm::vec3 d, glm::vec3 s, float sh); + glm::vec3 getAmbient() const; + glm::vec3 getDiffuse() const; + glm::vec3 getSpecular() const; + float getShiny() const; +}; + +#endif diff --git a/A6/shadow8t4/shadow8t4/src/MatrixStack.cpp b/A6/shadow8t4/shadow8t4/src/MatrixStack.cpp new file mode 100644 index 0000000..eaa6e6c --- /dev/null +++ b/A6/shadow8t4/shadow8t4/src/MatrixStack.cpp @@ -0,0 +1,114 @@ +#include "MatrixStack.h" + +#include +#include +#include + +#define GLM_FORCE_RADIANS +#include +#include + +using namespace std; + +MatrixStack::MatrixStack() +{ + mstack = make_shared< stack >(); + mstack->push(glm::mat4(1.0)); +} + +MatrixStack::~MatrixStack() +{ +} + +void MatrixStack::pushMatrix() +{ + const glm::mat4 &top = mstack->top(); + mstack->push(top); + assert(mstack->size() < 100); +} + +void MatrixStack::popMatrix() +{ + assert(!mstack->empty()); + mstack->pop(); + // There should always be one matrix left. + assert(!mstack->empty()); +} + +void MatrixStack::loadIdentity() +{ + glm::mat4 &top = mstack->top(); + top = glm::mat4(1.0); +} + +void MatrixStack::translate(const glm::vec3 &t) +{ + glm::mat4 &top = mstack->top(); + top *= glm::translate(t); +} + +void MatrixStack::translate(float x, float y, float z) +{ + translate(glm::vec3(x, y, z)); +} + +void MatrixStack::scale(const glm::vec3 &s) +{ + glm::mat4 &top = mstack->top(); + top *= glm::scale(s); +} + +void MatrixStack::scale(float x, float y, float z) +{ + scale(glm::vec3(x, y, z)); +} + +void MatrixStack::scale(float s) +{ + scale(glm::vec3(s, s, s)); +} + +void MatrixStack::rotate(float angle, const glm::vec3 &axis) +{ + glm::mat4 &top = mstack->top(); + top *= glm::rotate(angle, axis); +} + +void MatrixStack::rotate(float angle, float x, float y, float z) +{ + rotate(angle, glm::vec3(x, y, z)); +} + +void MatrixStack::multMatrix(const glm::mat4 &matrix) +{ + glm::mat4 &top = mstack->top(); + top *= matrix; +} + +const glm::mat4 &MatrixStack::topMatrix() const +{ + return mstack->top(); +} + +void MatrixStack::print(const glm::mat4 &mat, const char *name) +{ + if(name) { + printf("%s = [\n", name); + } + for(int i = 0; i < 4; ++i) { + for(int j = 0; j < 4; ++j) { + // mat[j] returns the jth column + printf("%- 5.2f ", mat[j][i]); + } + printf("\n"); + } + if(name) { + printf("];"); + } + printf("\n"); +} + +void MatrixStack::print(const char *name) const +{ + print(mstack->top(), name); +} diff --git a/A6/shadow8t4/shadow8t4/src/MatrixStack.h b/A6/shadow8t4/shadow8t4/src/MatrixStack.h new file mode 100644 index 0000000..66278ce --- /dev/null +++ b/A6/shadow8t4/shadow8t4/src/MatrixStack.h @@ -0,0 +1,50 @@ +#pragma once +#ifndef _MatrixStack_H_ +#define _MatrixStack_H_ + +#include +#include +#include + +class MatrixStack +{ +public: + MatrixStack(); + virtual ~MatrixStack(); + + // glPushMatrix(): Copies the current matrix and adds it to the top of the stack + void pushMatrix(); + // glPopMatrix(): Removes the top of the stack and sets the current matrix to be the matrix that is now on top + void popMatrix(); + + // glLoadIdentity(): Sets the top matrix to be the identity + void loadIdentity(); + // glMultMatrix(): Right multiplies the top matrix + void multMatrix(const glm::mat4 &matrix); + + // glTranslate(): Right multiplies the top matrix by a translation matrix + void translate(const glm::vec3 &trans); + void translate(float x, float y, float z); + // glScale(): Right multiplies the top matrix by a scaling matrix + void scale(const glm::vec3 &scale); + void scale(float x, float y, float z); + // glScale(): Right multiplies the top matrix by a scaling matrix + void scale(float size); + // glRotate(): Right multiplies the top matrix by a rotation matrix (angle in radians) + void rotate(float angle, const glm::vec3 &axis); + void rotate(float angle, float x, float y, float z); + + // glGet(GL_MODELVIEW_MATRIX): Gets the top matrix + const glm::mat4 &topMatrix() const; + + // Prints out the specified matrix + static void print(const glm::mat4 &mat, const char *name = 0); + // Prints out the top matrix + void print(const char *name = 0) const; + +private: + std::shared_ptr< std::stack > mstack; + +}; + +#endif diff --git a/A6/shadow8t4/shadow8t4/src/Program.cpp b/A6/shadow8t4/shadow8t4/src/Program.cpp new file mode 100644 index 0000000..4bf8af7 --- /dev/null +++ b/A6/shadow8t4/shadow8t4/src/Program.cpp @@ -0,0 +1,127 @@ +#include "Program.h" + +#include +#include + +#include "GLSL.h" + +using namespace std; + +Program::Program() : + name(""), + vShaderName(""), + fShaderName(""), + pid(0), + verbose(true) +{ + +} + +Program::~Program() +{ + +} + +void Program::setShaderNames(const string &v, const string &f) +{ + vShaderName = v; + fShaderName = f; +} + +bool Program::init() +{ + GLint rc; + + // Create shader handles + GLuint VS = glCreateShader(GL_VERTEX_SHADER); + GLuint FS = glCreateShader(GL_FRAGMENT_SHADER); + + // Read shader sources + const char *vshader = GLSL::textFileRead(vShaderName.c_str()); + const char *fshader = GLSL::textFileRead(fShaderName.c_str()); + glShaderSource(VS, 1, &vshader, NULL); + glShaderSource(FS, 1, &fshader, NULL); + + // Compile vertex shader + glCompileShader(VS); + glGetShaderiv(VS, GL_COMPILE_STATUS, &rc); + if(!rc) { + if(isVerbose()) { + GLSL::printShaderInfoLog(VS); + cout << "Error compiling vertex shader " << vShaderName << endl; + } + return false; + } + + // Compile fragment shader + glCompileShader(FS); + glGetShaderiv(FS, GL_COMPILE_STATUS, &rc); + if(!rc) { + if(isVerbose()) { + GLSL::printShaderInfoLog(FS); + cout << "Error compiling fragment shader " << fShaderName << endl; + } + return false; + } + + // Create the program and link + pid = glCreateProgram(); + glAttachShader(pid, VS); + glAttachShader(pid, FS); + glLinkProgram(pid); + glGetProgramiv(pid, GL_LINK_STATUS, &rc); + if(!rc) { + if(isVerbose()) { + GLSL::printProgramInfoLog(pid); + cout << "Error linking shaders " << vShaderName << " and " << fShaderName << endl; + } + return false; + } + + GLSL::checkError(GET_FILE_LINE); + return true; +} + +void Program::bind() +{ + glUseProgram(pid); +} + +void Program::unbind() +{ + glUseProgram(0); +} + +void Program::addAttribute(const string &name) +{ + attributes[name] = glGetAttribLocation(pid, name.c_str()); +} + +void Program::addUniform(const string &name) +{ + uniforms[name] = glGetUniformLocation(pid, name.c_str()); +} + +GLint Program::getAttribute(const string &name) const +{ + map::const_iterator attribute = attributes.find(name.c_str()); + if(attribute == attributes.end()) { + if(isVerbose()) { + cout << name << " is not an attribute variable" << endl; + } + return -1; + } + return attribute->second; +} + +GLint Program::getUniform(const string &name) const +{ + map::const_iterator uniform = uniforms.find(name.c_str()); + if(uniform == uniforms.end()) { + if(isVerbose()) { + cout << name << " is not a uniform variable" << endl; + } + return -1; + } + return uniform->second; +} diff --git a/A6/shadow8t4/shadow8t4/src/Program.h b/A6/shadow8t4/shadow8t4/src/Program.h new file mode 100644 index 0000000..74397fc --- /dev/null +++ b/A6/shadow8t4/shadow8t4/src/Program.h @@ -0,0 +1,45 @@ +#pragma once +#ifndef __Program__ +#define __Program__ + +#include +#include + +#define GLEW_STATIC +#include + +/** + * An OpenGL Program (vertex and fragment shaders) + */ +class Program +{ +public: + Program(); + virtual ~Program(); + + void setVerbose(bool v) { verbose = v; } + bool isVerbose() const { return verbose; } + + void setShaderNames(const std::string &v, const std::string &f); + virtual bool init(); + virtual void bind(); + virtual void unbind(); + + void addAttribute(const std::string &name); + void addUniform(const std::string &name); + GLint getAttribute(const std::string &name) const; + GLint getUniform(const std::string &name) const; + +protected: + std::string name; + std::string vShaderName; + std::string fShaderName; + +private: + GLuint pid; + std::map attributes; + std::map uniforms; + bool verbose; +}; + +#endif diff --git a/A6/shadow8t4/shadow8t4/src/ShapeSkin.cpp b/A6/shadow8t4/shadow8t4/src/ShapeSkin.cpp new file mode 100644 index 0000000..9ec9bb0 --- /dev/null +++ b/A6/shadow8t4/shadow8t4/src/ShapeSkin.cpp @@ -0,0 +1,316 @@ +#include +#include + +#define GLM_FORCE_RADIANS +#include +#include + +#define TINYOBJLOADER_IMPLEMENTATION +#include "tiny_obj_loader.h" + +#include "ShapeSkin.h" +#include "GLSL.h" +#include "Program.h" + +using namespace std; + +ShapeSkin::ShapeSkin() : + prog(NULL), + elemBufID(0), + posBufID(0), + norBufID(0), + texBufID(0) +{ +} + +ShapeSkin::~ShapeSkin() +{ +} + +void ShapeSkin::loadMesh(const string &meshName) +{ + // Load geometry + tinyobj::attrib_t attrib; + std::vector shapes; + std::vector materials; + string errStr; + bool rc = tinyobj::LoadObj(&attrib, &shapes, &materials, &errStr, meshName.c_str()); + if(!rc) { + cerr << errStr << endl; + } else { + posBuf = attrib.vertices; + norBuf = attrib.normals; + texBuf = attrib.texcoords; + assert(posBuf.size() == norBuf.size()); + // Loop over shapes + for(size_t s = 0; s < shapes.size(); s++) { + // Loop over faces (polygons) + const tinyobj::mesh_t &mesh = shapes[s].mesh; + size_t index_offset = 0; + for(size_t f = 0; f < mesh.num_face_vertices.size(); f++) { + size_t fv = mesh.num_face_vertices[f]; + // Loop over vertices in the face. + for(size_t v = 0; v < fv; v++) { + // access to vertex + tinyobj::index_t idx = mesh.indices[index_offset + v]; + elemBuf.push_back(idx.vertex_index); + } + index_offset += fv; + // per-face material (IGNORE) + shapes[s].mesh.material_ids[f]; + } + } + posBufInit = posBuf; + } +} + +void ShapeSkin::loadAttachment(const std::string &filename) +{ + unsigned int nverts, nbones; + ifstream in; + in.open(filename); + if(!in.good()) { + cout << "Cannot read " << filename << endl; + return; + } + string line; + getline(in, line); // comment + getline(in, line); // comment + getline(in, line); + stringstream ss0(line); + ss0 >> nverts; + ss0 >> nbones; + assert((unsigned int)nverts == posBuf.size()/3); + while(1) { + getline(in, line); + if(in.eof()) { + break; + } + // Parse line + stringstream ss(line); + float temp; + std::vector tempVec; + std::vector tempIndexes; + for(unsigned int i = 0; i < nbones; i++) + { + ss >> temp; + tempVec.push_back(temp); + if(temp > 0.0f) + { + tempIndexes.push_back((unsigned int) i); + } + } + indexes.push_back(tempIndexes); + boneInfluence.push_back(tempVec); + } + in.close(); +} + +void ShapeSkin::loadSkeleton(const std::string &filename) +{ + unsigned int nframes, nbones; + ifstream in; + in.open(filename); + if(!in.good()) { + cout << "Cannot read " << filename << endl; + return; + } + string line; + getline(in, line); // comment + getline(in, line); // comment + getline(in, line); // comment + getline(in, line); + stringstream ss0(line); + ss0 >> nframes; + ss0 >> nbones; + while(1) { + getline(in, line); + if(in.eof()) { + break; + } + // Parse line + stringstream ss(line); + vert temp; + + float tempx; + float tempy; + float tempz; + float tempw; + + std::vector tempVec; + tempVec.clear(); + for(unsigned int i = 0; i < nbones; i++) + { + tempVec.push_back(temp); + ss >> tempx; + ss >> tempy; + ss >> tempz; + ss >> tempw; + tempVec.at(tempVec.size() - 1).q = glm::quat(tempw, tempx, tempy, tempz); + + ss >> tempx; + ss >> tempy; + ss >> tempz; + tempVec.at(tempVec.size() - 1).p = glm::vec3(tempx, tempy, tempz); + } + vertInfo.push_back(tempVec); + } + in.close(); + + glm::mat4 E; + + std::vector tempMatVec; + + for(unsigned int j = 0; j < vertInfo.size(); j++) + { + for(unsigned int k = 0; k < nbones; k++) + { + E = glm::mat4_cast(vertInfo[j][k].q); + E[3] = glm::vec4(vertInfo[j][k].p, 1.0f); + if(j == 0) + { + tempMatVec.push_back(glm::inverse(E)); + } + else + tempMatVec.push_back(E); + } + transMats.push_back(tempMatVec); + tempMatVec.clear(); + } +} + +void ShapeSkin::init() +{ + // Send the position array to the GPU + glGenBuffers(1, &posBufID); + glBindBuffer(GL_ARRAY_BUFFER, posBufID); + glBufferData(GL_ARRAY_BUFFER, posBuf.size()*sizeof(float), &posBuf[0], GL_STATIC_DRAW); + + // Send the normal array to the GPU + glGenBuffers(1, &norBufID); + glBindBuffer(GL_ARRAY_BUFFER, norBufID); + glBufferData(GL_ARRAY_BUFFER, norBuf.size()*sizeof(float), &norBuf[0], GL_STATIC_DRAW); + + // No texture info + texBufID = 0; + + // Send the element array to the GPU + glGenBuffers(1, &elemBufID); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, elemBufID); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, elemBuf.size()*sizeof(unsigned int), &elemBuf[0], GL_STATIC_DRAW); + + // Unbind the arrays + glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + + assert(glGetError() == GL_NO_ERROR); +} + +void ShapeSkin::draw(float t, std::shared_ptr MV) const +{ + assert(prog); + + int h_pos = prog->getAttribute("aPos"); + glEnableVertexAttribArray(h_pos); + glBindBuffer(GL_ARRAY_BUFFER, posBufID); + glVertexAttribPointer(h_pos, 3, GL_FLOAT, GL_FALSE, 0, (const void *)0); + + int h_nor = prog->getAttribute("aNor"); + glEnableVertexAttribArray(h_nor); + glBindBuffer(GL_ARRAY_BUFFER, norBufID); + glVertexAttribPointer(h_nor, 3, GL_FLOAT, GL_FALSE, 0, (const void *)0); + + // Draw + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, elemBufID); + glDrawElements(GL_TRIANGLES, (int)elemBuf.size(), GL_UNSIGNED_INT, (const void *)0); + + glDisableVertexAttribArray(h_nor); + glDisableVertexAttribArray(h_pos); + glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + + float alpha = std::fmod(60.0f*t, (float)transMats.size() - 2); + float u = std::fmod(alpha, 1.0f); + int frame = std::ceil(alpha); + + for(unsigned int j = 0; j < transMats[frame].size(); j++) + { + glm::mat4 p0 = transMats[frame][j]; + glm::mat4 p1 = transMats[frame + 1][j]; + + + MV->pushMatrix(); + glm::mat4 temp((1 - u)*p0 + u*p1); + MV->multMatrix(glm::inverse(transMats[0][j]) + temp); + glm::vec3 pp = glm::vec3(0.0f, 0.0f, 0.0f); + + glUniformMatrix4fv(prog->getUniform("MV"), 1, GL_FALSE, glm::value_ptr(MV->topMatrix())); + // Draw frenet frame + glBegin(GL_LINE_STRIP); + glColor3f(1.0, 0.0, 0.0); + glVertex3f(pp.x, pp.y, pp.z); + glVertex3f(pp.x + 0.1f, pp.y + 0.0f, pp.z + 0.0f); + glColor3f(0.0, 1.0, 0.0); + glVertex3f(pp.x, pp.y, pp.z); + glVertex3f(pp.x + 0.0f, pp.y + 0.1f, pp.z + 0.0f); + glColor3f(0.0, 0.0, 1.0); + glVertex3f(pp.x, pp.y, pp.z); + glVertex3f(pp.x + 0.0f, pp.y + 0.0f, pp.z + 0.1f); + glEnd(); + MV->popMatrix(); + } + + // +} + +void ShapeSkin::skin(float t) +{ + float alpha = std::fmod(60.0f*t, (float)transMats.size() - 2); + float u = std::fmod(alpha, 1.0f); + int frame = std::ceil(alpha); + + std::vector tempInterpMats; + + for(unsigned int j = 0 ; j < boneInfluence[0].size(); j++) + { + glm::mat4 p0 = transMats[frame][j]; + glm::mat4 p1 = transMats[frame + 1][j]; + glm::mat4 temp((1 - u)*p0 + u*p1); + tempInterpMats.push_back(temp); + } + + + for(unsigned int i = 0; i < indexes.size(); i++) + { + glm::vec4 temp(posBufInit[i*3], posBufInit[i*3 + 1], posBufInit[i*3 + 2], 1.0f); + glm::vec4 tempOutput(0.0f, 0.0f, 0.0f, 0.0f); + for(unsigned int j = 0; j < indexes[i].size(); j++) + { + glm::mat4 tempMat = transMats[0][indexes[i][j]]; + glm::mat4 tempTransMat = tempInterpMats[indexes[i][j]]; + float w = boneInfluence[i][indexes[i][j]]; + + glm::vec4 temp1 = tempMat*temp; + glm::vec4 temp2 = tempTransMat*temp1; + glm::vec4 temp3 = w*temp2; + + tempOutput = tempOutput + temp3; + } + posBuf[i*3] = tempOutput.x; + posBuf[i*3 + 1] = tempOutput.y; + posBuf[i*3 + 2] = tempOutput.z; + } + glBindBuffer(GL_ARRAY_BUFFER, posBufID); + glBufferData(GL_ARRAY_BUFFER, posBuf.size()*sizeof(float), &posBuf[0], GL_STATIC_DRAW); +} + +void ShapeSkin::setMaterial(Material mat) +{ + m = mat; +} + +Material ShapeSkin::getMaterial() +{ + return m; +} + diff --git a/A6/shadow8t4/shadow8t4/src/ShapeSkin.h b/A6/shadow8t4/shadow8t4/src/ShapeSkin.h new file mode 100644 index 0000000..354ebea --- /dev/null +++ b/A6/shadow8t4/shadow8t4/src/ShapeSkin.h @@ -0,0 +1,52 @@ +#pragma once +#ifndef _SHAPESKIN_H_ +#define _SHAPESKIN_H_ + +#include "MatrixStack.h" +#include +#include +#include +#include "Material.h" + +class Program; + +struct vert +{ + glm::quat q; + glm::vec3 p; +}; + +class ShapeSkin +{ +public: + ShapeSkin(); + virtual ~ShapeSkin(); + void loadMesh(const std::string &meshName); + void loadAttachment(const std::string &filename); + void loadSkeleton(const std::string &filename); + void setProgram(std::shared_ptr p) { prog = p; } + void init(); + void draw(float t, std::shared_ptr MV) const; + void skin(float t); + void setMaterial(Material m); + Material getMaterial(); + +private: + std::shared_ptr prog; + std::vector elemBuf; + std::vector posBuf; + std::vector norBuf; + std::vector texBuf; + unsigned elemBufID; + unsigned posBufID; + unsigned norBufID; + unsigned texBufID; + Material m; + std::vector posBufInit; + std::vector > boneInfluence; + std::vector > vertInfo; + std::vector > transMats; + std::vector > indexes; +}; + +#endif diff --git a/A6/shadow8t4/shadow8t4/src/main.cpp b/A6/shadow8t4/shadow8t4/src/main.cpp new file mode 100644 index 0000000..edad0bf --- /dev/null +++ b/A6/shadow8t4/shadow8t4/src/main.cpp @@ -0,0 +1,291 @@ +#include +#include +#include +#include +#include + +#define GLEW_STATIC +#include +#include + +#define GLM_FORCE_RADIANS +#include +#include + +#include "GLSL.h" +#include "Program.h" +#include "Camera.h" +#include "MatrixStack.h" +#include "ShapeSkin.h" +#include "Material.h" + +using namespace std; + +GLFWwindow *window; // Main application window +string RESOURCE_DIR = ""; // Where the resources are loaded from +string MESH_FILE = ""; +string ATTACHMENT_FILE = ""; +string SKELETON_FILE = ""; +bool keyToggles[256] = {false}; + +shared_ptr camera = NULL; +shared_ptr shape = NULL; +shared_ptr progSimple = NULL; +shared_ptr progSkin = NULL; + +static void error_callback(int error, const char *description) +{ + cerr << description << endl; +} + +static void key_callback(GLFWwindow *window, int key, int scancode, int action, int mods) +{ + if(key == GLFW_KEY_ESCAPE && action == GLFW_PRESS) { + glfwSetWindowShouldClose(window, GL_TRUE); + } +} + +static void char_callback(GLFWwindow *window, unsigned int key) +{ + keyToggles[key] = !keyToggles[key]; + switch(key) { + case 'g': + break; + } +} + +static void cursor_position_callback(GLFWwindow* window, double xmouse, double ymouse) +{ + int state = glfwGetMouseButton(window, GLFW_MOUSE_BUTTON_LEFT); + if(state == GLFW_PRESS) { + camera->mouseMoved(xmouse, ymouse); + } +} + +void mouse_button_callback(GLFWwindow* window, int button, int action, int mods) +{ + // Get the current mouse position. + double xmouse, ymouse; + glfwGetCursorPos(window, &xmouse, &ymouse); + // Get current window size. + int width, height; + glfwGetWindowSize(window, &width, &height); + if(action == GLFW_PRESS) { + bool shift = mods & GLFW_MOD_SHIFT; + bool ctrl = mods & GLFW_MOD_CONTROL; + bool alt = mods & GLFW_MOD_ALT; + camera->mouseClicked(xmouse, ymouse, shift, ctrl, alt); + } +} + +void loadScene(const string &meshFile, const string &attachmentFile) +{ + keyToggles[(unsigned)'c'] = true; + + camera = make_shared(); + + // Single shape for all the animations. + shape = make_shared(); + shape->loadMesh(meshFile); + shape->loadAttachment(attachmentFile); + shape->loadSkeleton(SKELETON_FILE); + + // For drawing the grid, etc. + progSimple = make_shared(); + progSimple->setShaderNames(RESOURCE_DIR + "vert.glsl", RESOURCE_DIR + "frag.glsl"); + progSimple->setVerbose(true); + + // For skinned shape, CPU/GPU + progSkin = make_shared(); + progSkin->setShaderNames(RESOURCE_DIR + "vert.glsl", RESOURCE_DIR + "frag.glsl"); + progSkin->setVerbose(true); +} + +void init() +{ + // Non-OpenGL things + loadScene(MESH_FILE, ATTACHMENT_FILE); + + // Set background color + glClearColor(1.0f, 1.0f, 1.0f, 1.0f); + // Enable z-buffer test + glEnable(GL_DEPTH_TEST); + // Enable alpha blending + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + shape->init(); + Material m; + m.setMaterial(glm::vec3(0.1f, 0.1f, 0.3f), glm::vec3(0.2f, 0.2f, 0.5f), glm::vec3(0.2f, 0.2f, 0.6f), 50.0f); + shape->setMaterial(m); + + progSimple->init(); + progSimple->addUniform("P"); + progSimple->addUniform("MV"); + + progSkin->init(); + progSkin->addUniform("ka"); + progSkin->addUniform("kd"); + progSkin->addUniform("ks"); + progSkin->addUniform("s"); + progSkin->addAttribute("aPos"); + progSkin->addAttribute("aNor"); + progSkin->addUniform("P"); + progSkin->addUniform("MV"); + + // Initialize time. + glfwSetTime(0.0); + + GLSL::checkError(GET_FILE_LINE); +} + +void render() +{ + // Update time. + double t = glfwGetTime(); + + // Get current frame buffer size. + int width, height; + glfwGetFramebufferSize(window, &width, &height); + glViewport(0, 0, width, height); + + // Use the window size for camera. + glfwGetWindowSize(window, &width, &height); + camera->setAspect((float)width/(float)height); + + // Clear buffers + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + if(keyToggles[(unsigned)'c']) { + glEnable(GL_CULL_FACE); + } else { + glDisable(GL_CULL_FACE); + } + if(keyToggles[(unsigned)'z']) { + glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); + } else { + glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); + } + + auto P = make_shared(); + auto MV = make_shared(); + + // Apply camera transforms + P->pushMatrix(); + camera->applyProjectionMatrix(P); + MV->pushMatrix(); + camera->applyViewMatrix(MV); + + // Draw grid + progSimple->bind(); + glUniformMatrix4fv(progSimple->getUniform("P"), 1, GL_FALSE, glm::value_ptr(P->topMatrix())); + glUniformMatrix4fv(progSimple->getUniform("MV"), 1, GL_FALSE, glm::value_ptr(MV->topMatrix())); + float gridSizeHalf = 5.0f; + int gridNx = 11; + int gridNz = 11; + glLineWidth(1); + glColor3f(0.8f, 0.8f, 0.8f); + glBegin(GL_LINES); + for(int i = 0; i < gridNx; ++i) { + float alpha = i / (gridNx - 1.0f); + float x = (1.0f - alpha) * (-gridSizeHalf) + alpha * gridSizeHalf; + glVertex3f(x, 0, -gridSizeHalf); + glVertex3f(x, 0, gridSizeHalf); + } + for(int i = 0; i < gridNz; ++i) { + float alpha = i / (gridNz - 1.0f); + float z = (1.0f - alpha) * (-gridSizeHalf) + alpha * gridSizeHalf; + glVertex3f(-gridSizeHalf, 0, z); + glVertex3f( gridSizeHalf, 0, z); + } + glEnd(); + progSimple->unbind(); + + // Draw character + MV->pushMatrix(); + progSkin->bind(); + glUniformMatrix4fv(progSkin->getUniform("P"), 1, GL_FALSE, glm::value_ptr(P->topMatrix())); + glUniformMatrix4fv(progSkin->getUniform("MV"), 1, GL_FALSE, glm::value_ptr(MV->topMatrix())); + shape->setProgram(progSkin); + + glm::vec3 ambient = shape->getMaterial().getAmbient(); + glm::vec3 diffuse = shape->getMaterial().getDiffuse(); + glm::vec3 specular = shape->getMaterial().getSpecular(); + float shine = shape->getMaterial().getShiny(); + + glUniform3f(progSkin->getUniform("ka"), ambient.r, ambient.g, ambient.b); + glUniform3f(progSkin->getUniform("kd"), diffuse.r, diffuse.g, diffuse.b); + glUniform3f(progSkin->getUniform("ks"), specular.r, specular.g, specular.b); + glUniform1f(progSkin->getUniform("s"), shine); + + shape->skin((float) t); + shape->draw((float) t, MV); + progSkin->unbind(); + MV->popMatrix(); + + // Pop matrix stacks. + MV->popMatrix(); + P->popMatrix(); + + GLSL::checkError(GET_FILE_LINE); +} + +int main(int argc, char **argv) +{ + if(argc < 5) { + cout << "Usage: Assignment2 " << endl; + return 0; + } + RESOURCE_DIR = argv[1] + string("/"); + MESH_FILE = argv[2]; + ATTACHMENT_FILE = argv[3]; + SKELETON_FILE = argv[4]; + + // Set error callback. + glfwSetErrorCallback(error_callback); + // Initialize the library. + if(!glfwInit()) { + return -1; + } + // Create a windowed mode window and its OpenGL context. + window = glfwCreateWindow(640, 480, "Alexander Huddleston A6", NULL, NULL); + if(!window) { + glfwTerminate(); + return -1; + } + // Make the window's context current. + glfwMakeContextCurrent(window); + // Initialize GLEW. + glewExperimental = true; + if(glewInit() != GLEW_OK) { + cerr << "Failed to initialize GLEW" << endl; + return -1; + } + glGetError(); // A bug in glewInit() causes an error that we can safely ignore. + cout << "OpenGL version: " << glGetString(GL_VERSION) << endl; + cout << "GLSL version: " << glGetString(GL_SHADING_LANGUAGE_VERSION) << endl; + // Set vsync. + glfwSwapInterval(1); + // Set keyboard callback. + glfwSetKeyCallback(window, key_callback); + // Set char callback. + glfwSetCharCallback(window, char_callback); + // Set cursor position callback. + glfwSetCursorPosCallback(window, cursor_position_callback); + // Set mouse button callback. + glfwSetMouseButtonCallback(window, mouse_button_callback); + // Initialize scene. + init(); + // Loop until the user closes the window. + while(!glfwWindowShouldClose(window)) { + // Render scene. + render(); + // Swap front and back buffers. + glfwSwapBuffers(window); + // Poll for and process events. + glfwPollEvents(); + } + // Quit program. + glfwDestroyWindow(window); + glfwTerminate(); + return 0; +} diff --git a/A6/shadow8t4/shadow8t4/src/tiny_obj_loader.h b/A6/shadow8t4/shadow8t4/src/tiny_obj_loader.h new file mode 100644 index 0000000..b975601 --- /dev/null +++ b/A6/shadow8t4/shadow8t4/src/tiny_obj_loader.h @@ -0,0 +1,1922 @@ +/* +The MIT License (MIT) + +Copyright (c) 2012-2016 Syoyo Fujita and many contributors. + +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. +*/ + +// +// version 1.0.3 : Support parsing texture options(#85) +// version 1.0.2 : Improve parsing speed by about a factor of 2 for large +// files(#105) +// version 1.0.1 : Fixes a shape is lost if obj ends with a 'usemtl'(#104) +// version 1.0.0 : Change data structure. Change license from BSD to MIT. +// + +// +// Use this in *one* .cc +// #define TINYOBJLOADER_IMPLEMENTATION +// #include "tiny_obj_loader.h" +// + +#ifndef TINY_OBJ_LOADER_H_ +#define TINY_OBJ_LOADER_H_ + +#include +#include +#include + +namespace tinyobj { + +// https://en.wikipedia.org/wiki/Wavefront_.obj_file says ... +// +// -blendu on | off # set horizontal texture blending +// (default on) +// -blendv on | off # set vertical texture blending +// (default on) +// -boost float_value # boost mip-map sharpness +// -mm base_value gain_value # modify texture map values (default +// 0 1) +// # base_value = brightness, +// gain_value = contrast +// -o u [v [w]] # Origin offset (default +// 0 0 0) +// -s u [v [w]] # Scale (default +// 1 1 1) +// -t u [v [w]] # Turbulence (default +// 0 0 0) +// -texres resolution # texture resolution to create +// -clamp on | off # only render texels in the clamped +// 0-1 range (default off) +// # When unclamped, textures are +// repeated across a surface, +// # when clamped, only texels which +// fall within the 0-1 +// # range are rendered. +// -bm mult_value # bump multiplier (for bump maps +// only) +// +// -imfchan r | g | b | m | l | z # specifies which channel of the file +// is used to +// # create a scalar or bump texture. +// r:red, g:green, +// # b:blue, m:matte, l:luminance, +// z:z-depth.. +// # (the default for bump is 'l' and +// for decal is 'm') +// bump -imfchan r bumpmap.tga # says to use the red channel of +// bumpmap.tga as the bumpmap +// +// For reflection maps... +// +// -type sphere # specifies a sphere for a "refl" +// reflection map +// -type cube_top | cube_bottom | # when using a cube map, the texture +// file for each +// cube_front | cube_back | # side of the cube is specified +// separately +// cube_left | cube_right + +typedef enum { + TEXTURE_TYPE_NONE, // default + TEXTURE_TYPE_SPHERE, + TEXTURE_TYPE_CUBE_TOP, + TEXTURE_TYPE_CUBE_BOTTOM, + TEXTURE_TYPE_CUBE_FRONT, + TEXTURE_TYPE_CUBE_BACK, + TEXTURE_TYPE_CUBE_LEFT, + TEXTURE_TYPE_CUBE_RIGHT +} texture_type_t; + +typedef struct { + texture_type_t type; // -type (default TEXTURE_TYPE_NONE) + float sharpness; // -boost (default 1.0?) + float brightness; // base_value in -mm option (default 0) + float contrast; // gain_value in -mm option (default 1) + float origin_offset[3]; // -o u [v [w]] (default 0 0 0) + float scale[3]; // -s u [v [w]] (default 1 1 1) + float turbulence[3]; // -t u [v [w]] (default 0 0 0) + // int texture_resolution; // -texres resolution (default = ?) TODO + bool clamp; // -clamp (default false) + char imfchan; // -imfchan (the default for bump is 'l' and for decal is 'm') + bool blendu; // -blendu (default on) + bool blendv; // -blendv (default on) + float bump_multiplier; // -bm (for bump maps only, default 1.0) +} texture_option_t; + +typedef struct { + std::string name; + + float ambient[3]; + float diffuse[3]; + float specular[3]; + float transmittance[3]; + float emission[3]; + float shininess; + float ior; // index of refraction + float dissolve; // 1 == opaque; 0 == fully transparent + // illumination model (see http://www.fileformat.info/format/material/) + int illum; + + int dummy; // Suppress padding warning. + + std::string ambient_texname; // map_Ka + std::string diffuse_texname; // map_Kd + std::string specular_texname; // map_Ks + std::string specular_highlight_texname; // map_Ns + std::string bump_texname; // map_bump, bump + std::string displacement_texname; // disp + std::string alpha_texname; // map_d + + texture_option_t ambient_texopt; + texture_option_t diffuse_texopt; + texture_option_t specular_texopt; + texture_option_t specular_highlight_texopt; + texture_option_t bump_texopt; + texture_option_t displacement_texopt; + texture_option_t alpha_texopt; + + // PBR extension + // http://exocortex.com/blog/extending_wavefront_mtl_to_support_pbr + float roughness; // [0, 1] default 0 + float metallic; // [0, 1] default 0 + float sheen; // [0, 1] default 0 + float clearcoat_thickness; // [0, 1] default 0 + float clearcoat_roughness; // [0, 1] default 0 + float anisotropy; // aniso. [0, 1] default 0 + float anisotropy_rotation; // anisor. [0, 1] default 0 + float pad0; + float pad1; + std::string roughness_texname; // map_Pr + std::string metallic_texname; // map_Pm + std::string sheen_texname; // map_Ps + std::string emissive_texname; // map_Ke + std::string normal_texname; // norm. For normal mapping. + + texture_option_t roughness_texopt; + texture_option_t metallic_texopt; + texture_option_t sheen_texopt; + texture_option_t emissive_texopt; + texture_option_t normal_texopt; + + int pad2; + + std::map unknown_parameter; +} material_t; + +typedef struct { + std::string name; + + std::vector intValues; + std::vector floatValues; + std::vector stringValues; +} tag_t; + +// Index struct to support different indices for vtx/normal/texcoord. +// -1 means not used. +typedef struct { + int vertex_index; + int normal_index; + int texcoord_index; +} index_t; + +typedef struct { + std::vector indices; + std::vector num_face_vertices; // The number of vertices per + // face. 3 = polygon, 4 = quad, + // ... Up to 255. + std::vector material_ids; // per-face material ID + std::vector tags; // SubD tag +} mesh_t; + +typedef struct { + std::string name; + mesh_t mesh; +} shape_t; + +// Vertex attributes +typedef struct { + std::vector vertices; // 'v' + std::vector normals; // 'vn' + std::vector texcoords; // 'vt' +} attrib_t; + +typedef struct callback_t_ { + // W is optional and set to 1 if there is no `w` item in `v` line + void (*vertex_cb)(void *user_data, float x, float y, float z, float w); + void (*normal_cb)(void *user_data, float x, float y, float z); + + // y and z are optional and set to 0 if there is no `y` and/or `z` item(s) in + // `vt` line. + void (*texcoord_cb)(void *user_data, float x, float y, float z); + + // called per 'f' line. num_indices is the number of face indices(e.g. 3 for + // triangle, 4 for quad) + // 0 will be passed for undefined index in index_t members. + void (*index_cb)(void *user_data, index_t *indices, int num_indices); + // `name` material name, `material_id` = the array index of material_t[]. -1 + // if + // a material not found in .mtl + void (*usemtl_cb)(void *user_data, const char *name, int material_id); + // `materials` = parsed material data. + void (*mtllib_cb)(void *user_data, const material_t *materials, + int num_materials); + // There may be multiple group names + void (*group_cb)(void *user_data, const char **names, int num_names); + void (*object_cb)(void *user_data, const char *name); + + callback_t_() + : vertex_cb(NULL), + normal_cb(NULL), + texcoord_cb(NULL), + index_cb(NULL), + usemtl_cb(NULL), + mtllib_cb(NULL), + group_cb(NULL), + object_cb(NULL) {} +} callback_t; + +class MaterialReader { + public: + MaterialReader() {} + virtual ~MaterialReader(); + + virtual bool operator()(const std::string &matId, + std::vector *materials, + std::map *matMap, + std::string *err) = 0; +}; + +class MaterialFileReader : public MaterialReader { + public: + explicit MaterialFileReader(const std::string &mtl_basedir) + : m_mtlBaseDir(mtl_basedir) {} + virtual ~MaterialFileReader() {} + virtual bool operator()(const std::string &matId, + std::vector *materials, + std::map *matMap, std::string *err); + + private: + std::string m_mtlBaseDir; +}; + +class MaterialStreamReader : public MaterialReader { + public: + explicit MaterialStreamReader(std::istream &inStream) + : m_inStream(inStream) {} + virtual ~MaterialStreamReader() {} + virtual bool operator()(const std::string &matId, + std::vector *materials, + std::map *matMap, std::string *err); + + private: + std::istream &m_inStream; +}; + +/// Loads .obj from a file. +/// 'attrib', 'shapes' and 'materials' will be filled with parsed shape data +/// 'shapes' will be filled with parsed shape data +/// Returns true when loading .obj become success. +/// Returns warning and error message into `err` +/// 'mtl_basedir' is optional, and used for base directory for .mtl file. +/// In default(`NULL'), .mtl file is searched from an application's working directory. +/// 'triangulate' is optional, and used whether triangulate polygon face in .obj +/// or not. +bool LoadObj(attrib_t *attrib, std::vector *shapes, + std::vector *materials, std::string *err, + const char *filename, const char *mtl_basedir = NULL, + bool triangulate = true); + +/// Loads .obj from a file with custom user callback. +/// .mtl is loaded as usual and parsed material_t data will be passed to +/// `callback.mtllib_cb`. +/// Returns true when loading .obj/.mtl become success. +/// Returns warning and error message into `err` +/// See `examples/callback_api/` for how to use this function. +bool LoadObjWithCallback(std::istream &inStream, const callback_t &callback, + void *user_data = NULL, + MaterialReader *readMatFn = NULL, + std::string *err = NULL); + +/// Loads object from a std::istream, uses GetMtlIStreamFn to retrieve +/// std::istream for materials. +/// Returns true when loading .obj become success. +/// Returns warning and error message into `err` +bool LoadObj(attrib_t *attrib, std::vector *shapes, + std::vector *materials, std::string *err, + std::istream *inStream, MaterialReader *readMatFn = NULL, + bool triangulate = true); + +/// Loads materials into std::map +void LoadMtl(std::map *material_map, + std::vector *materials, std::istream *inStream); + +} // namespace tinyobj + +#ifdef TINYOBJLOADER_IMPLEMENTATION +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace tinyobj { + +MaterialReader::~MaterialReader() {} + +#define TINYOBJ_SSCANF_BUFFER_SIZE (4096) + +struct vertex_index { + int v_idx, vt_idx, vn_idx; + vertex_index() : v_idx(-1), vt_idx(-1), vn_idx(-1) {} + explicit vertex_index(int idx) : v_idx(idx), vt_idx(idx), vn_idx(idx) {} + vertex_index(int vidx, int vtidx, int vnidx) + : v_idx(vidx), vt_idx(vtidx), vn_idx(vnidx) {} +}; + +struct tag_sizes { + tag_sizes() : num_ints(0), num_floats(0), num_strings(0) {} + int num_ints; + int num_floats; + int num_strings; +}; + +struct obj_shape { + std::vector v; + std::vector vn; + std::vector vt; +}; + +// See +// http://stackoverflow.com/questions/6089231/getting-std-ifstream-to-handle-lf-cr-and-crlf +static std::istream &safeGetline(std::istream &is, std::string &t) { + t.clear(); + + // The characters in the stream are read one-by-one using a std::streambuf. + // That is faster than reading them one-by-one using the std::istream. + // Code that uses streambuf this way must be guarded by a sentry object. + // The sentry object performs various tasks, + // such as thread synchronization and updating the stream state. + + std::istream::sentry se(is, true); + std::streambuf *sb = is.rdbuf(); + + for (;;) { + int c = sb->sbumpc(); + switch (c) { + case '\n': + return is; + case '\r': + if (sb->sgetc() == '\n') sb->sbumpc(); + return is; + case EOF: + // Also handle the case when the last line has no line ending + if (t.empty()) is.setstate(std::ios::eofbit); + return is; + default: + t += static_cast(c); + } + } +} + +#define IS_SPACE(x) (((x) == ' ') || ((x) == '\t')) +#define IS_DIGIT(x) \ + (static_cast((x) - '0') < static_cast(10)) +#define IS_NEW_LINE(x) (((x) == '\r') || ((x) == '\n') || ((x) == '\0')) + +// Make index zero-base, and also support relative index. +static inline int fixIndex(int idx, int n) { + if (idx > 0) return idx - 1; + if (idx == 0) return 0; + return n + idx; // negative value = relative +} + +static inline std::string parseString(const char **token) { + std::string s; + (*token) += strspn((*token), " \t"); + size_t e = strcspn((*token), " \t\r"); + s = std::string((*token), &(*token)[e]); + (*token) += e; + return s; +} + +static inline int parseInt(const char **token) { + (*token) += strspn((*token), " \t"); + int i = atoi((*token)); + (*token) += strcspn((*token), " \t\r"); + return i; +} + +// Tries to parse a floating point number located at s. +// +// s_end should be a location in the string where reading should absolutely +// stop. For example at the end of the string, to prevent buffer overflows. +// +// Parses the following EBNF grammar: +// sign = "+" | "-" ; +// END = ? anything not in digit ? +// digit = "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9" ; +// integer = [sign] , digit , {digit} ; +// decimal = integer , ["." , integer] ; +// float = ( decimal , END ) | ( decimal , ("E" | "e") , integer , END ) ; +// +// Valid strings are for example: +// -0 +3.1417e+2 -0.0E-3 1.0324 -1.41 11e2 +// +// If the parsing is a success, result is set to the parsed value and true +// is returned. +// +// The function is greedy and will parse until any of the following happens: +// - a non-conforming character is encountered. +// - s_end is reached. +// +// The following situations triggers a failure: +// - s >= s_end. +// - parse failure. +// +static bool tryParseDouble(const char *s, const char *s_end, double *result) { + if (s >= s_end) { + return false; + } + + double mantissa = 0.0; + // This exponent is base 2 rather than 10. + // However the exponent we parse is supposed to be one of ten, + // thus we must take care to convert the exponent/and or the + // mantissa to a * 2^E, where a is the mantissa and E is the + // exponent. + // To get the final double we will use ldexp, it requires the + // exponent to be in base 2. + int exponent = 0; + + // NOTE: THESE MUST BE DECLARED HERE SINCE WE ARE NOT ALLOWED + // TO JUMP OVER DEFINITIONS. + char sign = '+'; + char exp_sign = '+'; + char const *curr = s; + + // How many characters were read in a loop. + int read = 0; + // Tells whether a loop terminated due to reaching s_end. + bool end_not_reached = false; + + /* + BEGIN PARSING. + */ + + // Find out what sign we've got. + if (*curr == '+' || *curr == '-') { + sign = *curr; + curr++; + } else if (IS_DIGIT(*curr)) { /* Pass through. */ + } else { + goto fail; + } + + // Read the integer part. + end_not_reached = (curr != s_end); + while (end_not_reached && IS_DIGIT(*curr)) { + mantissa *= 10; + mantissa += static_cast(*curr - 0x30); + curr++; + read++; + end_not_reached = (curr != s_end); + } + + // We must make sure we actually got something. + if (read == 0) goto fail; + // We allow numbers of form "#", "###" etc. + if (!end_not_reached) goto assemble; + + // Read the decimal part. + if (*curr == '.') { + curr++; + read = 1; + end_not_reached = (curr != s_end); + while (end_not_reached && IS_DIGIT(*curr)) { + static const double pow_lut[] = { + 1.0, 0.1, 0.01, 0.001, 0.0001, 0.00001, 0.000001, 0.0000001, + }; + const int lut_entries = sizeof pow_lut / sizeof pow_lut[0]; + + // NOTE: Don't use powf here, it will absolutely murder precision. + mantissa += static_cast(*curr - 0x30) * + (read < lut_entries ? pow_lut[read] : pow(10.0, -read)); + read++; + curr++; + end_not_reached = (curr != s_end); + } + } else if (*curr == 'e' || *curr == 'E') { + } else { + goto assemble; + } + + if (!end_not_reached) goto assemble; + + // Read the exponent part. + if (*curr == 'e' || *curr == 'E') { + curr++; + // Figure out if a sign is present and if it is. + end_not_reached = (curr != s_end); + if (end_not_reached && (*curr == '+' || *curr == '-')) { + exp_sign = *curr; + curr++; + } else if (IS_DIGIT(*curr)) { /* Pass through. */ + } else { + // Empty E is not allowed. + goto fail; + } + + read = 0; + end_not_reached = (curr != s_end); + while (end_not_reached && IS_DIGIT(*curr)) { + exponent *= 10; + exponent += static_cast(*curr - 0x30); + curr++; + read++; + end_not_reached = (curr != s_end); + } + exponent *= (exp_sign == '+' ? 1 : -1); + if (read == 0) goto fail; + } + +assemble: + *result = + (sign == '+' ? 1 : -1) * + (exponent ? ldexp(mantissa * pow(5.0, exponent), exponent) : mantissa); + return true; +fail: + return false; +} + +static inline float parseFloat(const char **token, double default_value = 0.0) { + (*token) += strspn((*token), " \t"); + const char *end = (*token) + strcspn((*token), " \t\r"); + double val = default_value; + tryParseDouble((*token), end, &val); + float f = static_cast(val); + (*token) = end; + return f; +} + +static inline void parseFloat2(float *x, float *y, const char **token, + const double default_x = 0.0, + const double default_y = 0.0) { + (*x) = parseFloat(token, default_x); + (*y) = parseFloat(token, default_y); +} + +static inline void parseFloat3(float *x, float *y, float *z, const char **token, + const double default_x = 0.0, + const double default_y = 0.0, + const double default_z = 0.0) { + (*x) = parseFloat(token, default_x); + (*y) = parseFloat(token, default_y); + (*z) = parseFloat(token, default_z); +} + +static inline void parseV(float *x, float *y, float *z, float *w, + const char **token, const double default_x = 0.0, + const double default_y = 0.0, + const double default_z = 0.0, + const double default_w = 1.0) { + (*x) = parseFloat(token, default_x); + (*y) = parseFloat(token, default_y); + (*z) = parseFloat(token, default_z); + (*w) = parseFloat(token, default_w); +} + +static inline bool parseOnOff(const char **token, bool default_value = true) { + (*token) += strspn((*token), " \t"); + const char *end = (*token) + strcspn((*token), " \t\r"); + + bool ret = default_value; + if ((0 == strncmp((*token), "on", 2))) { + ret = true; + } else if ((0 == strncmp((*token), "off", 3))) { + ret = false; + } + + (*token) = end; + return ret; +} + +static inline texture_type_t parseTextureType( + const char **token, texture_type_t default_value = TEXTURE_TYPE_NONE) { + (*token) += strspn((*token), " \t"); + const char *end = (*token) + strcspn((*token), " \t\r"); + texture_type_t ty = default_value; + + if ((0 == strncmp((*token), "cube_top", strlen("cube_top")))) { + ty = TEXTURE_TYPE_CUBE_TOP; + } else if ((0 == strncmp((*token), "cube_bottom", strlen("cube_bottom")))) { + ty = TEXTURE_TYPE_CUBE_BOTTOM; + } else if ((0 == strncmp((*token), "cube_left", strlen("cube_left")))) { + ty = TEXTURE_TYPE_CUBE_LEFT; + } else if ((0 == strncmp((*token), "cube_right", strlen("cube_right")))) { + ty = TEXTURE_TYPE_CUBE_RIGHT; + } else if ((0 == strncmp((*token), "cube_front", strlen("cube_front")))) { + ty = TEXTURE_TYPE_CUBE_FRONT; + } else if ((0 == strncmp((*token), "cube_back", strlen("cube_back")))) { + ty = TEXTURE_TYPE_CUBE_BACK; + } else if ((0 == strncmp((*token), "sphere", strlen("sphere")))) { + ty = TEXTURE_TYPE_SPHERE; + } + + (*token) = end; + return ty; +} + +static tag_sizes parseTagTriple(const char **token) { + tag_sizes ts; + + ts.num_ints = atoi((*token)); + (*token) += strcspn((*token), "/ \t\r"); + if ((*token)[0] != '/') { + return ts; + } + (*token)++; + + ts.num_floats = atoi((*token)); + (*token) += strcspn((*token), "/ \t\r"); + if ((*token)[0] != '/') { + return ts; + } + (*token)++; + + ts.num_strings = atoi((*token)); + (*token) += strcspn((*token), "/ \t\r") + 1; + + return ts; +} + +// Parse triples with index offsets: i, i/j/k, i//k, i/j +static vertex_index parseTriple(const char **token, int vsize, int vnsize, + int vtsize) { + vertex_index vi(-1); + + vi.v_idx = fixIndex(atoi((*token)), vsize); + (*token) += strcspn((*token), "/ \t\r"); + if ((*token)[0] != '/') { + return vi; + } + (*token)++; + + // i//k + if ((*token)[0] == '/') { + (*token)++; + vi.vn_idx = fixIndex(atoi((*token)), vnsize); + (*token) += strcspn((*token), "/ \t\r"); + return vi; + } + + // i/j/k or i/j + vi.vt_idx = fixIndex(atoi((*token)), vtsize); + (*token) += strcspn((*token), "/ \t\r"); + if ((*token)[0] != '/') { + return vi; + } + + // i/j/k + (*token)++; // skip '/' + vi.vn_idx = fixIndex(atoi((*token)), vnsize); + (*token) += strcspn((*token), "/ \t\r"); + return vi; +} + +// Parse raw triples: i, i/j/k, i//k, i/j +static vertex_index parseRawTriple(const char **token) { + vertex_index vi(static_cast(0)); // 0 is an invalid index in OBJ + + vi.v_idx = atoi((*token)); + (*token) += strcspn((*token), "/ \t\r"); + if ((*token)[0] != '/') { + return vi; + } + (*token)++; + + // i//k + if ((*token)[0] == '/') { + (*token)++; + vi.vn_idx = atoi((*token)); + (*token) += strcspn((*token), "/ \t\r"); + return vi; + } + + // i/j/k or i/j + vi.vt_idx = atoi((*token)); + (*token) += strcspn((*token), "/ \t\r"); + if ((*token)[0] != '/') { + return vi; + } + + // i/j/k + (*token)++; // skip '/' + vi.vn_idx = atoi((*token)); + (*token) += strcspn((*token), "/ \t\r"); + return vi; +} + +static bool ParseTextureNameAndOption(std::string *texname, + texture_option_t *texopt, + const char *linebuf, const bool is_bump) { + // @todo { write more robust lexer and parser. } + bool found_texname = false; + std::string texture_name; + + // Fill with default value for texopt. + if (is_bump) { + texopt->imfchan = 'l'; + } else { + texopt->imfchan = 'm'; + } + texopt->bump_multiplier = 1.0f; + texopt->clamp = false; + texopt->blendu = true; + texopt->blendv = true; + texopt->sharpness = 1.0f; + texopt->brightness = 0.0f; + texopt->contrast = 1.0f; + texopt->origin_offset[0] = 0.0f; + texopt->origin_offset[1] = 0.0f; + texopt->origin_offset[2] = 0.0f; + texopt->scale[0] = 1.0f; + texopt->scale[1] = 1.0f; + texopt->scale[2] = 1.0f; + texopt->turbulence[0] = 0.0f; + texopt->turbulence[1] = 0.0f; + texopt->turbulence[2] = 0.0f; + texopt->type = TEXTURE_TYPE_NONE; + + const char *token = linebuf; // Assume line ends with NULL + + while (!IS_NEW_LINE((*token))) { + if ((0 == strncmp(token, "-blendu", 7)) && IS_SPACE((token[7]))) { + token += 8; + texopt->blendu = parseOnOff(&token, /* default */ true); + } else if ((0 == strncmp(token, "-blendv", 7)) && IS_SPACE((token[7]))) { + token += 8; + texopt->blendv = parseOnOff(&token, /* default */ true); + } else if ((0 == strncmp(token, "-clamp", 6)) && IS_SPACE((token[6]))) { + token += 7; + texopt->clamp = parseOnOff(&token, /* default */ true); + } else if ((0 == strncmp(token, "-boost", 6)) && IS_SPACE((token[6]))) { + token += 7; + texopt->sharpness = parseFloat(&token, 1.0); + } else if ((0 == strncmp(token, "-bm", 3)) && IS_SPACE((token[3]))) { + token += 4; + texopt->bump_multiplier = parseFloat(&token, 1.0); + } else if ((0 == strncmp(token, "-o", 2)) && IS_SPACE((token[2]))) { + token += 3; + parseFloat3(&(texopt->origin_offset[0]), &(texopt->origin_offset[1]), + &(texopt->origin_offset[2]), &token); + } else if ((0 == strncmp(token, "-s", 2)) && IS_SPACE((token[2]))) { + token += 3; + parseFloat3(&(texopt->scale[0]), &(texopt->scale[1]), &(texopt->scale[2]), + &token, 1.0, 1.0, 1.0); + } else if ((0 == strncmp(token, "-t", 2)) && IS_SPACE((token[2]))) { + token += 3; + parseFloat3(&(texopt->turbulence[0]), &(texopt->turbulence[1]), + &(texopt->turbulence[2]), &token); + } else if ((0 == strncmp(token, "-type", 5)) && IS_SPACE((token[5]))) { + token += 5; + texopt->type = parseTextureType((&token), TEXTURE_TYPE_NONE); + } else if ((0 == strncmp(token, "-imfchan", 8)) && IS_SPACE((token[8]))) { + token += 9; + token += strspn(token, " \t"); + const char *end = token + strcspn(token, " \t\r"); + if ((end - token) == 1) { // Assume one char for -imfchan + texopt->imfchan = (*token); + } + token = end; + } else if ((0 == strncmp(token, "-mm", 3)) && IS_SPACE((token[3]))) { + token += 4; + parseFloat2(&(texopt->brightness), &(texopt->contrast), &token, 0.0, 1.0); + } else { + // Assume texture filename + token += strspn(token, " \t"); // skip space + size_t len = strcspn(token, " \t\r"); // untile next space + texture_name = std::string(token, token + len); + token += len; + + token += strspn(token, " \t"); // skip space + + found_texname = true; + } + } + + if (found_texname) { + (*texname) = texture_name; + return true; + } else { + return false; + } +} + +static void InitMaterial(material_t *material) { + material->name = ""; + material->ambient_texname = ""; + material->diffuse_texname = ""; + material->specular_texname = ""; + material->specular_highlight_texname = ""; + material->bump_texname = ""; + material->displacement_texname = ""; + material->alpha_texname = ""; + for (int i = 0; i < 3; i++) { + material->ambient[i] = 0.f; + material->diffuse[i] = 0.f; + material->specular[i] = 0.f; + material->transmittance[i] = 0.f; + material->emission[i] = 0.f; + } + material->illum = 0; + material->dissolve = 1.f; + material->shininess = 1.f; + material->ior = 1.f; + + material->roughness = 0.f; + material->metallic = 0.f; + material->sheen = 0.f; + material->clearcoat_thickness = 0.f; + material->clearcoat_roughness = 0.f; + material->anisotropy_rotation = 0.f; + material->anisotropy = 0.f; + material->roughness_texname = ""; + material->metallic_texname = ""; + material->sheen_texname = ""; + material->emissive_texname = ""; + material->normal_texname = ""; + + material->unknown_parameter.clear(); +} + +static bool exportFaceGroupToShape( + shape_t *shape, const std::vector > &faceGroup, + const std::vector &tags, const int material_id, + const std::string &name, bool triangulate) { + if (faceGroup.empty()) { + return false; + } + + // Flatten vertices and indices + for (size_t i = 0; i < faceGroup.size(); i++) { + const std::vector &face = faceGroup[i]; + + vertex_index i0 = face[0]; + vertex_index i1(-1); + vertex_index i2 = face[1]; + + size_t npolys = face.size(); + + if (triangulate) { + // Polygon -> triangle fan conversion + for (size_t k = 2; k < npolys; k++) { + i1 = i2; + i2 = face[k]; + + index_t idx0, idx1, idx2; + idx0.vertex_index = i0.v_idx; + idx0.normal_index = i0.vn_idx; + idx0.texcoord_index = i0.vt_idx; + idx1.vertex_index = i1.v_idx; + idx1.normal_index = i1.vn_idx; + idx1.texcoord_index = i1.vt_idx; + idx2.vertex_index = i2.v_idx; + idx2.normal_index = i2.vn_idx; + idx2.texcoord_index = i2.vt_idx; + + shape->mesh.indices.push_back(idx0); + shape->mesh.indices.push_back(idx1); + shape->mesh.indices.push_back(idx2); + + shape->mesh.num_face_vertices.push_back(3); + shape->mesh.material_ids.push_back(material_id); + } + } else { + for (size_t k = 0; k < npolys; k++) { + index_t idx; + idx.vertex_index = face[k].v_idx; + idx.normal_index = face[k].vn_idx; + idx.texcoord_index = face[k].vt_idx; + shape->mesh.indices.push_back(idx); + } + + shape->mesh.num_face_vertices.push_back( + static_cast(npolys)); + shape->mesh.material_ids.push_back(material_id); // per face + } + } + + shape->name = name; + shape->mesh.tags = tags; + + return true; +} + +void LoadMtl(std::map *material_map, + std::vector *materials, std::istream *inStream) { + // Create a default material anyway. + material_t material; + InitMaterial(&material); + + std::string linebuf; + while (inStream->peek() != -1) { + safeGetline(*inStream, linebuf); + + // Trim trailing whitespace. + if (linebuf.size() > 0) { + linebuf = linebuf.substr(0, linebuf.find_last_not_of(" \t") + 1); + } + + // Trim newline '\r\n' or '\n' + if (linebuf.size() > 0) { + if (linebuf[linebuf.size() - 1] == '\n') + linebuf.erase(linebuf.size() - 1); + } + if (linebuf.size() > 0) { + if (linebuf[linebuf.size() - 1] == '\r') + linebuf.erase(linebuf.size() - 1); + } + + // Skip if empty line. + if (linebuf.empty()) { + continue; + } + + // Skip leading space. + const char *token = linebuf.c_str(); + token += strspn(token, " \t"); + + assert(token); + if (token[0] == '\0') continue; // empty line + + if (token[0] == '#') continue; // comment line + + // new mtl + if ((0 == strncmp(token, "newmtl", 6)) && IS_SPACE((token[6]))) { + // flush previous material. + if (!material.name.empty()) { + material_map->insert(std::pair( + material.name, static_cast(materials->size()))); + materials->push_back(material); + } + + // initial temporary material + InitMaterial(&material); + + // set new mtl name + char namebuf[TINYOBJ_SSCANF_BUFFER_SIZE]; + token += 7; +#ifdef _MSC_VER + sscanf_s(token, "%s", namebuf, (unsigned)_countof(namebuf)); +#else + sscanf(token, "%s", namebuf); +#endif + material.name = namebuf; + continue; + } + + // ambient + if (token[0] == 'K' && token[1] == 'a' && IS_SPACE((token[2]))) { + token += 2; + float r, g, b; + parseFloat3(&r, &g, &b, &token); + material.ambient[0] = r; + material.ambient[1] = g; + material.ambient[2] = b; + continue; + } + + // diffuse + if (token[0] == 'K' && token[1] == 'd' && IS_SPACE((token[2]))) { + token += 2; + float r, g, b; + parseFloat3(&r, &g, &b, &token); + material.diffuse[0] = r; + material.diffuse[1] = g; + material.diffuse[2] = b; + continue; + } + + // specular + if (token[0] == 'K' && token[1] == 's' && IS_SPACE((token[2]))) { + token += 2; + float r, g, b; + parseFloat3(&r, &g, &b, &token); + material.specular[0] = r; + material.specular[1] = g; + material.specular[2] = b; + continue; + } + + // transmittance + if ((token[0] == 'K' && token[1] == 't' && IS_SPACE((token[2]))) || + (token[0] == 'T' && token[1] == 'f' && IS_SPACE((token[2])))) { + token += 2; + float r, g, b; + parseFloat3(&r, &g, &b, &token); + material.transmittance[0] = r; + material.transmittance[1] = g; + material.transmittance[2] = b; + continue; + } + + // ior(index of refraction) + if (token[0] == 'N' && token[1] == 'i' && IS_SPACE((token[2]))) { + token += 2; + material.ior = parseFloat(&token); + continue; + } + + // emission + if (token[0] == 'K' && token[1] == 'e' && IS_SPACE(token[2])) { + token += 2; + float r, g, b; + parseFloat3(&r, &g, &b, &token); + material.emission[0] = r; + material.emission[1] = g; + material.emission[2] = b; + continue; + } + + // shininess + if (token[0] == 'N' && token[1] == 's' && IS_SPACE(token[2])) { + token += 2; + material.shininess = parseFloat(&token); + continue; + } + + // illum model + if (0 == strncmp(token, "illum", 5) && IS_SPACE(token[5])) { + token += 6; + material.illum = parseInt(&token); + continue; + } + + // dissolve + if ((token[0] == 'd' && IS_SPACE(token[1]))) { + token += 1; + material.dissolve = parseFloat(&token); + continue; + } + if (token[0] == 'T' && token[1] == 'r' && IS_SPACE(token[2])) { + token += 2; + // Invert value of Tr(assume Tr is in range [0, 1]) + material.dissolve = 1.0f - parseFloat(&token); + continue; + } + + // PBR: roughness + if (token[0] == 'P' && token[1] == 'r' && IS_SPACE(token[2])) { + token += 2; + material.roughness = parseFloat(&token); + continue; + } + + // PBR: metallic + if (token[0] == 'P' && token[1] == 'm' && IS_SPACE(token[2])) { + token += 2; + material.metallic = parseFloat(&token); + continue; + } + + // PBR: sheen + if (token[0] == 'P' && token[1] == 's' && IS_SPACE(token[2])) { + token += 2; + material.sheen = parseFloat(&token); + continue; + } + + // PBR: clearcoat thickness + if (token[0] == 'P' && token[1] == 'c' && IS_SPACE(token[2])) { + token += 2; + material.clearcoat_thickness = parseFloat(&token); + continue; + } + + // PBR: clearcoat roughness + if ((0 == strncmp(token, "Pcr", 3)) && IS_SPACE(token[3])) { + token += 4; + material.clearcoat_roughness = parseFloat(&token); + continue; + } + + // PBR: anisotropy + if ((0 == strncmp(token, "aniso", 5)) && IS_SPACE(token[5])) { + token += 6; + material.anisotropy = parseFloat(&token); + continue; + } + + // PBR: anisotropy rotation + if ((0 == strncmp(token, "anisor", 6)) && IS_SPACE(token[6])) { + token += 7; + material.anisotropy_rotation = parseFloat(&token); + continue; + } + + // ambient texture + if ((0 == strncmp(token, "map_Ka", 6)) && IS_SPACE(token[6])) { + token += 7; + ParseTextureNameAndOption(&(material.ambient_texname), + &(material.ambient_texopt), token, + /* is_bump */ false); + continue; + } + + // diffuse texture + if ((0 == strncmp(token, "map_Kd", 6)) && IS_SPACE(token[6])) { + token += 7; + ParseTextureNameAndOption(&(material.diffuse_texname), + &(material.diffuse_texopt), token, + /* is_bump */ false); + continue; + } + + // specular texture + if ((0 == strncmp(token, "map_Ks", 6)) && IS_SPACE(token[6])) { + token += 7; + ParseTextureNameAndOption(&(material.specular_texname), + &(material.specular_texopt), token, + /* is_bump */ false); + continue; + } + + // specular highlight texture + if ((0 == strncmp(token, "map_Ns", 6)) && IS_SPACE(token[6])) { + token += 7; + ParseTextureNameAndOption(&(material.specular_highlight_texname), + &(material.specular_highlight_texopt), token, + /* is_bump */ false); + continue; + } + + // bump texture + if ((0 == strncmp(token, "map_bump", 8)) && IS_SPACE(token[8])) { + token += 9; + ParseTextureNameAndOption(&(material.bump_texname), + &(material.bump_texopt), token, + /* is_bump */ true); + continue; + } + + // bump texture + if ((0 == strncmp(token, "bump", 4)) && IS_SPACE(token[4])) { + token += 5; + ParseTextureNameAndOption(&(material.bump_texname), + &(material.bump_texopt), token, + /* is_bump */ true); + continue; + } + + // alpha texture + if ((0 == strncmp(token, "map_d", 5)) && IS_SPACE(token[5])) { + token += 6; + material.alpha_texname = token; + ParseTextureNameAndOption(&(material.alpha_texname), + &(material.alpha_texopt), token, + /* is_bump */ false); + continue; + } + + // displacement texture + if ((0 == strncmp(token, "disp", 4)) && IS_SPACE(token[4])) { + token += 5; + ParseTextureNameAndOption(&(material.displacement_texname), + &(material.displacement_texopt), token, + /* is_bump */ false); + continue; + } + + // PBR: roughness texture + if ((0 == strncmp(token, "map_Pr", 6)) && IS_SPACE(token[6])) { + token += 7; + ParseTextureNameAndOption(&(material.roughness_texname), + &(material.roughness_texopt), token, + /* is_bump */ false); + continue; + } + + // PBR: metallic texture + if ((0 == strncmp(token, "map_Pm", 6)) && IS_SPACE(token[6])) { + token += 7; + ParseTextureNameAndOption(&(material.metallic_texname), + &(material.metallic_texopt), token, + /* is_bump */ false); + continue; + } + + // PBR: sheen texture + if ((0 == strncmp(token, "map_Ps", 6)) && IS_SPACE(token[6])) { + token += 7; + ParseTextureNameAndOption(&(material.sheen_texname), + &(material.sheen_texopt), token, + /* is_bump */ false); + continue; + } + + // PBR: emissive texture + if ((0 == strncmp(token, "map_Ke", 6)) && IS_SPACE(token[6])) { + token += 7; + ParseTextureNameAndOption(&(material.emissive_texname), + &(material.emissive_texopt), token, + /* is_bump */ false); + continue; + } + + // PBR: normal map texture + if ((0 == strncmp(token, "norm", 4)) && IS_SPACE(token[4])) { + token += 5; + ParseTextureNameAndOption( + &(material.normal_texname), &(material.normal_texopt), token, + /* is_bump */ false); // @fixme { is_bump will be true? } + continue; + } + + // unknown parameter + const char *_space = strchr(token, ' '); + if (!_space) { + _space = strchr(token, '\t'); + } + if (_space) { + std::ptrdiff_t len = _space - token; + std::string key(token, static_cast(len)); + std::string value = _space + 1; + material.unknown_parameter.insert( + std::pair(key, value)); + } + } + // flush last material. + material_map->insert(std::pair( + material.name, static_cast(materials->size()))); + materials->push_back(material); +} + +bool MaterialFileReader::operator()(const std::string &matId, + std::vector *materials, + std::map *matMap, + std::string *err) { + std::string filepath; + + if (!m_mtlBaseDir.empty()) { + filepath = std::string(m_mtlBaseDir) + matId; + } else { + filepath = matId; + } + + std::ifstream matIStream(filepath.c_str()); + LoadMtl(matMap, materials, &matIStream); + if (!matIStream) { + std::stringstream ss; + ss << "WARN: Material file [ " << filepath + << " ] not found. Created a default material."; + if (err) { + (*err) += ss.str(); + } + } + return true; +} + +bool MaterialStreamReader::operator()(const std::string &matId, + std::vector *materials, + std::map *matMap, + std::string *err) { + (void)matId; + LoadMtl(matMap, materials, &m_inStream); + if (!m_inStream) { + std::stringstream ss; + ss << "WARN: Material stream in error state." + << " Created a default material."; + if (err) { + (*err) += ss.str(); + } + } + return true; +} + +bool LoadObj(attrib_t *attrib, std::vector *shapes, + std::vector *materials, std::string *err, + const char *filename, const char *mtl_basedir, + bool trianglulate) { + attrib->vertices.clear(); + attrib->normals.clear(); + attrib->texcoords.clear(); + shapes->clear(); + + std::stringstream errss; + + std::ifstream ifs(filename); + if (!ifs) { + errss << "Cannot open file [" << filename << "]" << std::endl; + if (err) { + (*err) = errss.str(); + } + return false; + } + + std::string baseDir; + if (mtl_basedir) { + baseDir = mtl_basedir; + } + MaterialFileReader matFileReader(baseDir); + + return LoadObj(attrib, shapes, materials, err, &ifs, &matFileReader, + trianglulate); +} + +bool LoadObj(attrib_t *attrib, std::vector *shapes, + std::vector *materials, std::string *err, + std::istream *inStream, MaterialReader *readMatFn /*= NULL*/, + bool triangulate) { + std::stringstream errss; + + std::vector v; + std::vector vn; + std::vector vt; + std::vector tags; + std::vector > faceGroup; + std::string name; + + // material + std::map material_map; + int material = -1; + + shape_t shape; + + std::string linebuf; + while (inStream->peek() != -1) { + safeGetline(*inStream, linebuf); + + // Trim newline '\r\n' or '\n' + if (linebuf.size() > 0) { + if (linebuf[linebuf.size() - 1] == '\n') + linebuf.erase(linebuf.size() - 1); + } + if (linebuf.size() > 0) { + if (linebuf[linebuf.size() - 1] == '\r') + linebuf.erase(linebuf.size() - 1); + } + + // Skip if empty line. + if (linebuf.empty()) { + continue; + } + + // Skip leading space. + const char *token = linebuf.c_str(); + token += strspn(token, " \t"); + + assert(token); + if (token[0] == '\0') continue; // empty line + + if (token[0] == '#') continue; // comment line + + // vertex + if (token[0] == 'v' && IS_SPACE((token[1]))) { + token += 2; + float x, y, z; + parseFloat3(&x, &y, &z, &token); + v.push_back(x); + v.push_back(y); + v.push_back(z); + continue; + } + + // normal + if (token[0] == 'v' && token[1] == 'n' && IS_SPACE((token[2]))) { + token += 3; + float x, y, z; + parseFloat3(&x, &y, &z, &token); + vn.push_back(x); + vn.push_back(y); + vn.push_back(z); + continue; + } + + // texcoord + if (token[0] == 'v' && token[1] == 't' && IS_SPACE((token[2]))) { + token += 3; + float x, y; + parseFloat2(&x, &y, &token); + vt.push_back(x); + vt.push_back(y); + continue; + } + + // face + if (token[0] == 'f' && IS_SPACE((token[1]))) { + token += 2; + token += strspn(token, " \t"); + + std::vector face; + face.reserve(3); + + while (!IS_NEW_LINE(token[0])) { + vertex_index vi = parseTriple(&token, static_cast(v.size() / 3), + static_cast(vn.size() / 3), + static_cast(vt.size() / 2)); + face.push_back(vi); + size_t n = strspn(token, " \t\r"); + token += n; + } + + // replace with emplace_back + std::move on C++11 + faceGroup.push_back(std::vector()); + faceGroup[faceGroup.size() - 1].swap(face); + + continue; + } + + // use mtl + if ((0 == strncmp(token, "usemtl", 6)) && IS_SPACE((token[6]))) { + char namebuf[TINYOBJ_SSCANF_BUFFER_SIZE]; + token += 7; +#ifdef _MSC_VER + sscanf_s(token, "%s", namebuf, (unsigned)_countof(namebuf)); +#else + sscanf(token, "%s", namebuf); +#endif + + int newMaterialId = -1; + if (material_map.find(namebuf) != material_map.end()) { + newMaterialId = material_map[namebuf]; + } else { + // { error!! material not found } + } + + if (newMaterialId != material) { + // Create per-face material. Thus we don't add `shape` to `shapes` at + // this time. + // just clear `faceGroup` after `exportFaceGroupToShape()` call. + exportFaceGroupToShape(&shape, faceGroup, tags, material, name, + triangulate); + faceGroup.clear(); + material = newMaterialId; + } + + continue; + } + + // load mtl + if ((0 == strncmp(token, "mtllib", 6)) && IS_SPACE((token[6]))) { + if (readMatFn) { + char namebuf[TINYOBJ_SSCANF_BUFFER_SIZE]; + token += 7; +#ifdef _MSC_VER + sscanf_s(token, "%s", namebuf, (unsigned)_countof(namebuf)); +#else + sscanf(token, "%s", namebuf); +#endif + + std::string err_mtl; + bool ok = (*readMatFn)(namebuf, materials, &material_map, &err_mtl); + if (err) { + (*err) += err_mtl; + } + + if (!ok) { + faceGroup.clear(); // for safety + return false; + } + } + + continue; + } + + // group name + if (token[0] == 'g' && IS_SPACE((token[1]))) { + // flush previous face group. + bool ret = exportFaceGroupToShape(&shape, faceGroup, tags, material, name, + triangulate); + if (ret) { + shapes->push_back(shape); + } + + shape = shape_t(); + + // material = -1; + faceGroup.clear(); + + std::vector names; + names.reserve(2); + + while (!IS_NEW_LINE(token[0])) { + std::string str = parseString(&token); + names.push_back(str); + token += strspn(token, " \t\r"); // skip tag + } + + assert(names.size() > 0); + + // names[0] must be 'g', so skip the 0th element. + if (names.size() > 1) { + name = names[1]; + } else { + name = ""; + } + + continue; + } + + // object name + if (token[0] == 'o' && IS_SPACE((token[1]))) { + // flush previous face group. + bool ret = exportFaceGroupToShape(&shape, faceGroup, tags, material, name, + triangulate); + if (ret) { + shapes->push_back(shape); + } + + // material = -1; + faceGroup.clear(); + shape = shape_t(); + + // @todo { multiple object name? } + char namebuf[TINYOBJ_SSCANF_BUFFER_SIZE]; + token += 2; +#ifdef _MSC_VER + sscanf_s(token, "%s", namebuf, (unsigned)_countof(namebuf)); +#else + sscanf(token, "%s", namebuf); +#endif + name = std::string(namebuf); + + continue; + } + + if (token[0] == 't' && IS_SPACE(token[1])) { + tag_t tag; + + char namebuf[4096]; + token += 2; +#ifdef _MSC_VER + sscanf_s(token, "%s", namebuf, (unsigned)_countof(namebuf)); +#else + sscanf(token, "%s", namebuf); +#endif + tag.name = std::string(namebuf); + + token += tag.name.size() + 1; + + tag_sizes ts = parseTagTriple(&token); + + tag.intValues.resize(static_cast(ts.num_ints)); + + for (size_t i = 0; i < static_cast(ts.num_ints); ++i) { + tag.intValues[i] = atoi(token); + token += strcspn(token, "/ \t\r") + 1; + } + + tag.floatValues.resize(static_cast(ts.num_floats)); + for (size_t i = 0; i < static_cast(ts.num_floats); ++i) { + tag.floatValues[i] = parseFloat(&token); + token += strcspn(token, "/ \t\r") + 1; + } + + tag.stringValues.resize(static_cast(ts.num_strings)); + for (size_t i = 0; i < static_cast(ts.num_strings); ++i) { + char stringValueBuffer[4096]; + +#ifdef _MSC_VER + sscanf_s(token, "%s", stringValueBuffer, + (unsigned)_countof(stringValueBuffer)); +#else + sscanf(token, "%s", stringValueBuffer); +#endif + tag.stringValues[i] = stringValueBuffer; + token += tag.stringValues[i].size() + 1; + } + + tags.push_back(tag); + } + + // Ignore unknown command. + } + + bool ret = exportFaceGroupToShape(&shape, faceGroup, tags, material, name, + triangulate); + // exportFaceGroupToShape return false when `usemtl` is called in the last + // line. + // we also add `shape` to `shapes` when `shape.mesh` has already some + // faces(indices) + if (ret || shape.mesh.indices.size()) { + shapes->push_back(shape); + } + faceGroup.clear(); // for safety + + if (err) { + (*err) += errss.str(); + } + + attrib->vertices.swap(v); + attrib->normals.swap(vn); + attrib->texcoords.swap(vt); + + return true; +} + +bool LoadObjWithCallback(std::istream &inStream, const callback_t &callback, + void *user_data /*= NULL*/, + MaterialReader *readMatFn /*= NULL*/, + std::string *err /*= NULL*/) { + std::stringstream errss; + + // material + std::map material_map; + int material_id = -1; // -1 = invalid + + std::vector indices; + std::vector materials; + std::vector names; + names.reserve(2); + std::string name; + std::vector names_out; + + std::string linebuf; + while (inStream.peek() != -1) { + safeGetline(inStream, linebuf); + + // Trim newline '\r\n' or '\n' + if (linebuf.size() > 0) { + if (linebuf[linebuf.size() - 1] == '\n') + linebuf.erase(linebuf.size() - 1); + } + if (linebuf.size() > 0) { + if (linebuf[linebuf.size() - 1] == '\r') + linebuf.erase(linebuf.size() - 1); + } + + // Skip if empty line. + if (linebuf.empty()) { + continue; + } + + // Skip leading space. + const char *token = linebuf.c_str(); + token += strspn(token, " \t"); + + assert(token); + if (token[0] == '\0') continue; // empty line + + if (token[0] == '#') continue; // comment line + + // vertex + if (token[0] == 'v' && IS_SPACE((token[1]))) { + token += 2; + float x, y, z, w; // w is optional. default = 1.0 + parseV(&x, &y, &z, &w, &token); + if (callback.vertex_cb) { + callback.vertex_cb(user_data, x, y, z, w); + } + continue; + } + + // normal + if (token[0] == 'v' && token[1] == 'n' && IS_SPACE((token[2]))) { + token += 3; + float x, y, z; + parseFloat3(&x, &y, &z, &token); + if (callback.normal_cb) { + callback.normal_cb(user_data, x, y, z); + } + continue; + } + + // texcoord + if (token[0] == 'v' && token[1] == 't' && IS_SPACE((token[2]))) { + token += 3; + float x, y, z; // y and z are optional. default = 0.0 + parseFloat3(&x, &y, &z, &token); + if (callback.texcoord_cb) { + callback.texcoord_cb(user_data, x, y, z); + } + continue; + } + + // face + if (token[0] == 'f' && IS_SPACE((token[1]))) { + token += 2; + token += strspn(token, " \t"); + + indices.clear(); + while (!IS_NEW_LINE(token[0])) { + vertex_index vi = parseRawTriple(&token); + + index_t idx; + idx.vertex_index = vi.v_idx; + idx.normal_index = vi.vn_idx; + idx.texcoord_index = vi.vt_idx; + + indices.push_back(idx); + size_t n = strspn(token, " \t\r"); + token += n; + } + + if (callback.index_cb && indices.size() > 0) { + callback.index_cb(user_data, &indices.at(0), + static_cast(indices.size())); + } + + continue; + } + + // use mtl + if ((0 == strncmp(token, "usemtl", 6)) && IS_SPACE((token[6]))) { + char namebuf[TINYOBJ_SSCANF_BUFFER_SIZE]; + token += 7; +#ifdef _MSC_VER + sscanf_s(token, "%s", namebuf, + static_cast(_countof(namebuf))); +#else + sscanf(token, "%s", namebuf); +#endif + + int newMaterialId = -1; + if (material_map.find(namebuf) != material_map.end()) { + newMaterialId = material_map[namebuf]; + } else { + // { error!! material not found } + } + + if (newMaterialId != material_id) { + material_id = newMaterialId; + } + + if (callback.usemtl_cb) { + callback.usemtl_cb(user_data, namebuf, material_id); + } + + continue; + } + + // load mtl + if ((0 == strncmp(token, "mtllib", 6)) && IS_SPACE((token[6]))) { + if (readMatFn) { + char namebuf[TINYOBJ_SSCANF_BUFFER_SIZE]; + token += 7; +#ifdef _MSC_VER + sscanf_s(token, "%s", namebuf, (unsigned)_countof(namebuf)); +#else + sscanf(token, "%s", namebuf); +#endif + + std::string err_mtl; + materials.clear(); + bool ok = (*readMatFn)(namebuf, &materials, &material_map, &err_mtl); + if (err) { + (*err) += err_mtl; + } + + if (!ok) { + return false; + } + + if (callback.mtllib_cb) { + callback.mtllib_cb(user_data, &materials.at(0), + static_cast(materials.size())); + } + } + + continue; + } + + // group name + if (token[0] == 'g' && IS_SPACE((token[1]))) { + names.clear(); + + while (!IS_NEW_LINE(token[0])) { + std::string str = parseString(&token); + names.push_back(str); + token += strspn(token, " \t\r"); // skip tag + } + + assert(names.size() > 0); + + // names[0] must be 'g', so skip the 0th element. + if (names.size() > 1) { + name = names[1]; + } else { + name.clear(); + } + + if (callback.group_cb) { + if (names.size() > 1) { + // create const char* array. + names_out.resize(names.size() - 1); + for (size_t j = 0; j < names_out.size(); j++) { + names_out[j] = names[j + 1].c_str(); + } + callback.group_cb(user_data, &names_out.at(0), + static_cast(names_out.size())); + + } else { + callback.group_cb(user_data, NULL, 0); + } + } + + continue; + } + + // object name + if (token[0] == 'o' && IS_SPACE((token[1]))) { + // @todo { multiple object name? } + char namebuf[TINYOBJ_SSCANF_BUFFER_SIZE]; + token += 2; +#ifdef _MSC_VER + sscanf_s(token, "%s", namebuf, (unsigned)_countof(namebuf)); +#else + sscanf(token, "%s", namebuf); +#endif + std::string object_name = std::string(namebuf); + + if (callback.object_cb) { + callback.object_cb(user_data, object_name.c_str()); + } + + continue; + } + +#if 0 // @todo + if (token[0] == 't' && IS_SPACE(token[1])) { + tag_t tag; + + char namebuf[4096]; + token += 2; +#ifdef _MSC_VER + sscanf_s(token, "%s", namebuf, (unsigned)_countof(namebuf)); +#else + sscanf(token, "%s", namebuf); +#endif + tag.name = std::string(namebuf); + + token += tag.name.size() + 1; + + tag_sizes ts = parseTagTriple(&token); + + tag.intValues.resize(static_cast(ts.num_ints)); + + for (size_t i = 0; i < static_cast(ts.num_ints); ++i) { + tag.intValues[i] = atoi(token); + token += strcspn(token, "/ \t\r") + 1; + } + + tag.floatValues.resize(static_cast(ts.num_floats)); + for (size_t i = 0; i < static_cast(ts.num_floats); ++i) { + tag.floatValues[i] = parseFloat(&token); + token += strcspn(token, "/ \t\r") + 1; + } + + tag.stringValues.resize(static_cast(ts.num_strings)); + for (size_t i = 0; i < static_cast(ts.num_strings); ++i) { + char stringValueBuffer[4096]; + +#ifdef _MSC_VER + sscanf_s(token, "%s", stringValueBuffer, + (unsigned)_countof(stringValueBuffer)); +#else + sscanf(token, "%s", stringValueBuffer); +#endif + tag.stringValues[i] = stringValueBuffer; + token += tag.stringValues[i].size() + 1; + } + + tags.push_back(tag); + } +#endif + + // Ignore unknown command. + } + + if (err) { + (*err) += errss.str(); + } + + return true; +} +} // namespace tinyobj + +#endif + +#endif // TINY_OBJ_LOADER_H_ diff --git a/A6/src/Material.cpp b/A6/src/Material.cpp new file mode 100644 index 0000000..02317e3 --- /dev/null +++ b/A6/src/Material.cpp @@ -0,0 +1,39 @@ +#include "Material.h" + +using namespace std; + +Material::Material() +{ + this->ca = glm::vec3(0.3f,0.3f,0.3f); + this->cd = glm::vec3(0.3f,0.3f,0.3f); + this->cs = glm::vec3(1.0f,1.0f,1.0f); + this->shine = 0.0f; +} + +void Material::setMaterial(glm::vec3 a, glm::vec3 d, glm::vec3 s, float sh) +{ + this->ca = a; + this->cd = d; + this->cs = s; + this->shine = sh; +} + +glm::vec3 Material::getAmbient() const +{ + return this->ca; +} + +glm::vec3 Material::getDiffuse() const +{ + return this->cd; +} + +glm::vec3 Material::getSpecular() const +{ + return this->cs; +} + +float Material::getShiny() const +{ + return this->shine; +} diff --git a/A6/src/Material.h b/A6/src/Material.h new file mode 100644 index 0000000..71ee379 --- /dev/null +++ b/A6/src/Material.h @@ -0,0 +1,46 @@ +#pragma once +#ifndef _MATERIAL_H_ +#define _MATERIAL_H_ + +#include +#include +#include +#include +#include +#include + +#include "GLSL.h" +#include "Camera.h" +#include "MatrixStack.h" + +class Material +{ + private: + glm::vec3 ca; + glm::vec3 cd; + glm::vec3 cs; + float shine; + + public: + Material(); + Material(const Material &m) + { + ca = m.ca; + cd = m.cd; + cs = m.cs; + } + Material(glm::vec3 a, glm::vec3 d, glm::vec3 s, float sh) + { + ca = a; + cd = d; + cs = s; + shine = sh; + } + void setMaterial(glm::vec3 a, glm::vec3 d, glm::vec3 s, float sh); + glm::vec3 getAmbient() const; + glm::vec3 getDiffuse() const; + glm::vec3 getSpecular() const; + float getShiny() const; +}; + +#endif diff --git a/A6/src/ShapeSkin.cpp b/A6/src/ShapeSkin.cpp index 11825d4..9ec9bb0 100644 --- a/A6/src/ShapeSkin.cpp +++ b/A6/src/ShapeSkin.cpp @@ -60,12 +60,13 @@ void ShapeSkin::loadMesh(const string &meshName) shapes[s].mesh.material_ids[f]; } } + posBufInit = posBuf; } } void ShapeSkin::loadAttachment(const std::string &filename) { - int nverts, nbones; + unsigned int nverts, nbones; ifstream in; in.open(filename); if(!in.good()) { @@ -79,7 +80,7 @@ void ShapeSkin::loadAttachment(const std::string &filename) stringstream ss0(line); ss0 >> nverts; ss0 >> nbones; - assert(nverts == posBuf.size()/3); + assert((unsigned int)nverts == posBuf.size()/3); while(1) { getline(in, line); if(in.eof()) { @@ -87,14 +88,97 @@ void ShapeSkin::loadAttachment(const std::string &filename) } // Parse line stringstream ss(line); - - // - // IMPLEMENT ME - // + float temp; + std::vector tempVec; + std::vector tempIndexes; + for(unsigned int i = 0; i < nbones; i++) + { + ss >> temp; + tempVec.push_back(temp); + if(temp > 0.0f) + { + tempIndexes.push_back((unsigned int) i); + } + } + indexes.push_back(tempIndexes); + boneInfluence.push_back(tempVec); } in.close(); } +void ShapeSkin::loadSkeleton(const std::string &filename) +{ + unsigned int nframes, nbones; + ifstream in; + in.open(filename); + if(!in.good()) { + cout << "Cannot read " << filename << endl; + return; + } + string line; + getline(in, line); // comment + getline(in, line); // comment + getline(in, line); // comment + getline(in, line); + stringstream ss0(line); + ss0 >> nframes; + ss0 >> nbones; + while(1) { + getline(in, line); + if(in.eof()) { + break; + } + // Parse line + stringstream ss(line); + vert temp; + + float tempx; + float tempy; + float tempz; + float tempw; + + std::vector tempVec; + tempVec.clear(); + for(unsigned int i = 0; i < nbones; i++) + { + tempVec.push_back(temp); + ss >> tempx; + ss >> tempy; + ss >> tempz; + ss >> tempw; + tempVec.at(tempVec.size() - 1).q = glm::quat(tempw, tempx, tempy, tempz); + + ss >> tempx; + ss >> tempy; + ss >> tempz; + tempVec.at(tempVec.size() - 1).p = glm::vec3(tempx, tempy, tempz); + } + vertInfo.push_back(tempVec); + } + in.close(); + + glm::mat4 E; + + std::vector tempMatVec; + + for(unsigned int j = 0; j < vertInfo.size(); j++) + { + for(unsigned int k = 0; k < nbones; k++) + { + E = glm::mat4_cast(vertInfo[j][k].q); + E[3] = glm::vec4(vertInfo[j][k].p, 1.0f); + if(j == 0) + { + tempMatVec.push_back(glm::inverse(E)); + } + else + tempMatVec.push_back(E); + } + transMats.push_back(tempMatVec); + tempMatVec.clear(); + } +} + void ShapeSkin::init() { // Send the position array to the GPU @@ -122,7 +206,7 @@ void ShapeSkin::init() assert(glGetError() == GL_NO_ERROR); } -void ShapeSkin::draw() const +void ShapeSkin::draw(float t, std::shared_ptr MV) const { assert(prog); @@ -144,5 +228,89 @@ void ShapeSkin::draw() const glDisableVertexAttribArray(h_pos); glBindBuffer(GL_ARRAY_BUFFER, 0); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + + float alpha = std::fmod(60.0f*t, (float)transMats.size() - 2); + float u = std::fmod(alpha, 1.0f); + int frame = std::ceil(alpha); + + for(unsigned int j = 0; j < transMats[frame].size(); j++) + { + glm::mat4 p0 = transMats[frame][j]; + glm::mat4 p1 = transMats[frame + 1][j]; + + + MV->pushMatrix(); + glm::mat4 temp((1 - u)*p0 + u*p1); + MV->multMatrix(glm::inverse(transMats[0][j]) + temp); + glm::vec3 pp = glm::vec3(0.0f, 0.0f, 0.0f); + + glUniformMatrix4fv(prog->getUniform("MV"), 1, GL_FALSE, glm::value_ptr(MV->topMatrix())); + // Draw frenet frame + glBegin(GL_LINE_STRIP); + glColor3f(1.0, 0.0, 0.0); + glVertex3f(pp.x, pp.y, pp.z); + glVertex3f(pp.x + 0.1f, pp.y + 0.0f, pp.z + 0.0f); + glColor3f(0.0, 1.0, 0.0); + glVertex3f(pp.x, pp.y, pp.z); + glVertex3f(pp.x + 0.0f, pp.y + 0.1f, pp.z + 0.0f); + glColor3f(0.0, 0.0, 1.0); + glVertex3f(pp.x, pp.y, pp.z); + glVertex3f(pp.x + 0.0f, pp.y + 0.0f, pp.z + 0.1f); + glEnd(); + MV->popMatrix(); + } + + // +} + +void ShapeSkin::skin(float t) +{ + float alpha = std::fmod(60.0f*t, (float)transMats.size() - 2); + float u = std::fmod(alpha, 1.0f); + int frame = std::ceil(alpha); + + std::vector tempInterpMats; + + for(unsigned int j = 0 ; j < boneInfluence[0].size(); j++) + { + glm::mat4 p0 = transMats[frame][j]; + glm::mat4 p1 = transMats[frame + 1][j]; + glm::mat4 temp((1 - u)*p0 + u*p1); + tempInterpMats.push_back(temp); + } + + + for(unsigned int i = 0; i < indexes.size(); i++) + { + glm::vec4 temp(posBufInit[i*3], posBufInit[i*3 + 1], posBufInit[i*3 + 2], 1.0f); + glm::vec4 tempOutput(0.0f, 0.0f, 0.0f, 0.0f); + for(unsigned int j = 0; j < indexes[i].size(); j++) + { + glm::mat4 tempMat = transMats[0][indexes[i][j]]; + glm::mat4 tempTransMat = tempInterpMats[indexes[i][j]]; + float w = boneInfluence[i][indexes[i][j]]; + + glm::vec4 temp1 = tempMat*temp; + glm::vec4 temp2 = tempTransMat*temp1; + glm::vec4 temp3 = w*temp2; + + tempOutput = tempOutput + temp3; + } + posBuf[i*3] = tempOutput.x; + posBuf[i*3 + 1] = tempOutput.y; + posBuf[i*3 + 2] = tempOutput.z; + } + glBindBuffer(GL_ARRAY_BUFFER, posBufID); + glBufferData(GL_ARRAY_BUFFER, posBuf.size()*sizeof(float), &posBuf[0], GL_STATIC_DRAW); +} + +void ShapeSkin::setMaterial(Material mat) +{ + m = mat; +} + +Material ShapeSkin::getMaterial() +{ + return m; } diff --git a/A6/src/ShapeSkin.h b/A6/src/ShapeSkin.h index 2159c6d..354ebea 100644 --- a/A6/src/ShapeSkin.h +++ b/A6/src/ShapeSkin.h @@ -2,11 +2,20 @@ #ifndef _SHAPESKIN_H_ #define _SHAPESKIN_H_ +#include "MatrixStack.h" #include +#include +#include +#include "Material.h" -class MatrixStack; class Program; +struct vert +{ + glm::quat q; + glm::vec3 p; +}; + class ShapeSkin { public: @@ -14,9 +23,13 @@ public: virtual ~ShapeSkin(); void loadMesh(const std::string &meshName); void loadAttachment(const std::string &filename); + void loadSkeleton(const std::string &filename); void setProgram(std::shared_ptr p) { prog = p; } void init(); - void draw() const; + void draw(float t, std::shared_ptr MV) const; + void skin(float t); + void setMaterial(Material m); + Material getMaterial(); private: std::shared_ptr prog; @@ -28,6 +41,12 @@ private: unsigned posBufID; unsigned norBufID; unsigned texBufID; + Material m; + std::vector posBufInit; + std::vector > boneInfluence; + std::vector > vertInfo; + std::vector > transMats; + std::vector > indexes; }; #endif diff --git a/A6/src/main.cpp b/A6/src/main.cpp index 4f61ce9..edad0bf 100644 --- a/A6/src/main.cpp +++ b/A6/src/main.cpp @@ -17,6 +17,7 @@ #include "Camera.h" #include "MatrixStack.h" #include "ShapeSkin.h" +#include "Material.h" using namespace std; @@ -87,6 +88,7 @@ void loadScene(const string &meshFile, const string &attachmentFile) shape = make_shared(); shape->loadMesh(meshFile); shape->loadAttachment(attachmentFile); + shape->loadSkeleton(SKELETON_FILE); // For drawing the grid, etc. progSimple = make_shared(); @@ -95,7 +97,7 @@ void loadScene(const string &meshFile, const string &attachmentFile) // For skinned shape, CPU/GPU progSkin = make_shared(); - progSkin->setShaderNames(RESOURCE_DIR + "skin_vert.glsl", RESOURCE_DIR + "skin_frag.glsl"); + progSkin->setShaderNames(RESOURCE_DIR + "vert.glsl", RESOURCE_DIR + "frag.glsl"); progSkin->setVerbose(true); } @@ -113,12 +115,20 @@ void init() glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); shape->init(); + Material m; + m.setMaterial(glm::vec3(0.1f, 0.1f, 0.3f), glm::vec3(0.2f, 0.2f, 0.5f), glm::vec3(0.2f, 0.2f, 0.6f), 50.0f); + shape->setMaterial(m); + progSimple->init(); progSimple->addUniform("P"); progSimple->addUniform("MV"); progSkin->init(); - progSkin->addAttribute("aPos"); + progSkin->addUniform("ka"); + progSkin->addUniform("kd"); + progSkin->addUniform("ks"); + progSkin->addUniform("s"); + progSkin->addAttribute("aPos"); progSkin->addAttribute("aNor"); progSkin->addUniform("P"); progSkin->addUniform("MV"); @@ -196,7 +206,19 @@ void render() glUniformMatrix4fv(progSkin->getUniform("P"), 1, GL_FALSE, glm::value_ptr(P->topMatrix())); glUniformMatrix4fv(progSkin->getUniform("MV"), 1, GL_FALSE, glm::value_ptr(MV->topMatrix())); shape->setProgram(progSkin); - shape->draw(); + + glm::vec3 ambient = shape->getMaterial().getAmbient(); + glm::vec3 diffuse = shape->getMaterial().getDiffuse(); + glm::vec3 specular = shape->getMaterial().getSpecular(); + float shine = shape->getMaterial().getShiny(); + + glUniform3f(progSkin->getUniform("ka"), ambient.r, ambient.g, ambient.b); + glUniform3f(progSkin->getUniform("kd"), diffuse.r, diffuse.g, diffuse.b); + glUniform3f(progSkin->getUniform("ks"), specular.r, specular.g, specular.b); + glUniform1f(progSkin->getUniform("s"), shine); + + shape->skin((float) t); + shape->draw((float) t, MV); progSkin->unbind(); MV->popMatrix(); @@ -225,7 +247,7 @@ int main(int argc, char **argv) return -1; } // Create a windowed mode window and its OpenGL context. - window = glfwCreateWindow(640, 480, "YOUR NAME", NULL, NULL); + window = glfwCreateWindow(640, 480, "Alexander Huddleston A6", NULL, NULL); if(!window) { glfwTerminate(); return -1;