From dc48754e89708bdc2bf8a5c56ce6bf63d588335e Mon Sep 17 00:00:00 2001 From: Luca Conte Date: Fri, 24 May 2024 10:50:06 +0200 Subject: [PATCH] =?UTF-8?q?Grundlage=20aus=20den=20=C3=9Cbungen?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Main.c | 5 - src/Makefile | 34 +++ src/fragmentShader.glsl | 58 +++++ src/main.c | 462 ++++++++++++++++++++++++++++++++++++++++ src/matrixMath.c | 314 +++++++++++++++++++++++++++ src/matrixMath.h | 106 +++++++++ src/test.c | 130 +++++++++++ src/transformation.c | 79 +++++++ src/transformation.h | 11 + src/vertexShader.glsl | 36 ++++ src/wavefrontobj.c | 242 +++++++++++++++++++++ src/wavefrontobj.h | 30 +++ 12 files changed, 1502 insertions(+), 5 deletions(-) delete mode 100644 src/Main.c create mode 100644 src/Makefile create mode 100644 src/fragmentShader.glsl create mode 100644 src/main.c create mode 100644 src/matrixMath.c create mode 100644 src/matrixMath.h create mode 100644 src/test.c create mode 100644 src/transformation.c create mode 100644 src/transformation.h create mode 100644 src/vertexShader.glsl create mode 100644 src/wavefrontobj.c create mode 100644 src/wavefrontobj.h diff --git a/src/Main.c b/src/Main.c deleted file mode 100644 index bbed82d..0000000 --- a/src/Main.c +++ /dev/null @@ -1,5 +0,0 @@ -int main(int argc, char const *argv[]) -{ - /* code */ - return 0; -} diff --git a/src/Makefile b/src/Makefile new file mode 100644 index 0000000..1ea898c --- /dev/null +++ b/src/Makefile @@ -0,0 +1,34 @@ +GLEW_LIBS = $(shell pkgconf glew --libs) +GLFW_LIBS = $(shell pkgconf glfw3 --libs) +OTHER_LIBS = -lm + +ALL_LIBS = $(GLEW_LIBS) $(GLFW_LIBS) $(OTHER_LIBS) + +OBJ = matrixMath.o transformation.o wavefrontobj.o +SHADERS = fragmentShader.c vertexShader.c + +cg1.out: test.out main.o $(OBJ) $(SHADERS) + ./test.out + gcc -o $@ main.o $(OBJ) $(ALL_LIBS) + +test.out: test.o $(OBJ) + gcc -o $@ test.o $(OBJ) $(ALL_LIBS) + +%Shader.c: %Shader.glsl + xxd -i $? > $@ + +main.o: $(SHADERS) matrixMath.h transformation.h wavefrontobj.h + +test.o: matrixMath.h transformation.h wavefrontobj.h + +%.o: %.c + gcc -c $< + +run: cg1.out + ./cg1.out + +test: test.out + ./test.out + +clean: + rm $(SHADERS) main.o test.o $(OBJ) cg1.out test.out \ No newline at end of file diff --git a/src/fragmentShader.glsl b/src/fragmentShader.glsl new file mode 100644 index 0000000..79e05d1 --- /dev/null +++ b/src/fragmentShader.glsl @@ -0,0 +1,58 @@ +#version 330 core + +in vec3 normal; +in vec3 fragmentPosition; +in vec2 textureCoordinate; + +flat in mat3 TBN; + + +uniform float shininess; + +uniform vec4 ambientLight; +uniform vec3 lightPosition; +uniform vec4 lightColor; + +uniform sampler2D day; +uniform sampler2D night; +uniform sampler2D clouds; +uniform sampler2D ocean; +uniform sampler2D normalMap; + + +float emissionStrength = 0.0; + + +void main() { + vec4 color = vec4(texture(day, textureCoordinate).rgb, 1.0); + + float nightBrightness = texture(night, textureCoordinate).r; + vec4 nightColor = vec4(nightBrightness, nightBrightness * 0.7, nightBrightness * 0.5, 1.0); + float shininessMultiplier = texture(ocean, textureCoordinate).r; + + vec4 cloudColor = texture(clouds, textureCoordinate).rgba; + + vec3 norm = normalize(normal); + + vec3 lightDir = normalize(lightPosition - fragmentPosition); + vec3 eyeDir = (-normalize(fragmentPosition)); + + float diff = max(dot(norm, lightDir), 0.0); + + vec3 halfway = (lightDir + eyeDir) / length(lightDir + eyeDir); + float specular = pow(max(dot(halfway, norm), 0.0), shininess) * shininessMultiplier; + + + gl_FragColor = + // EMISSION + color * emissionStrength + + + // // AMBIENT + ambientLight * color + + + // DIFFUSION + mix(nightColor * (vec4(1,1,1,1) - cloudColor) + (cloudColor * ambientLight), color + cloudColor, diff) + + + // SPECULAR + specular * lightColor * color; +} \ No newline at end of file diff --git a/src/main.c b/src/main.c new file mode 100644 index 0000000..f7a00c2 --- /dev/null +++ b/src/main.c @@ -0,0 +1,462 @@ +#include + +#include +#include + +#include "vertexShader.c" +#include "fragmentShader.c" + +#include "matrixMath.h" +#include "transformation.h" +#include "wavefrontobj.h" + +#define STB_IMAGE_IMPLEMENTATION +#include "../lib/stb_image.h" + +#include +#include +#include +#include + +GLuint program; +GLuint vao; + +#define NUM_TEXTURES 5 + +#define DAY 0 +#define NIGHT 1 +#define CLOUDS 2 +#define OCEAN 3 +#define NORMAL 4 + +int flipFlag = 1; + +GLuint textures[NUM_TEXTURES]; +char* textureFiles[NUM_TEXTURES] = { + "../texture/earth/day.png", + "../texture/earth/night.png", + "../texture/earth/clouds.png", + "../texture/earth/ocean_mask.png", + "../texture/earth/normal.png" +}; + +int numFaces = 0; + +bool exitRequested = false; + +GLFWwindow* window; + +GLfloat aspectRatio = 1.0f; + +double timeBetweenUpdates = 0.2f; + +double timeSinceUpdate = 0.0f; +int framesSinceUpdate = 0; + +GLfloat step = 0.0f; +const GLfloat pi = 3.14159f; + +vec3 cameraPosition = {0.0f, 3.0f, 5.5f}; + +char* defaultModel = "../obj/earth.obj"; +char* model; + +// input handler for camera movement +void handleInputs(double deltaTime) { + if (glfwGetKey(window, GLFW_KEY_S) == GLFW_PRESS) { + cameraPosition.z += deltaTime * 10; + } + if (glfwGetKey(window, GLFW_KEY_W) == GLFW_PRESS) { + cameraPosition.z -= deltaTime * 10; + } + if (glfwGetKey(window, GLFW_KEY_SPACE) == GLFW_PRESS) { + cameraPosition.y += deltaTime * 10; + } + if (glfwGetKey(window, GLFW_KEY_LEFT_SHIFT) == GLFW_PRESS) { + cameraPosition.y -= deltaTime * 10; + } +} + +// input handler to quit with ESC +void keyboardHandler(GLFWwindow* window, int key, int scancode, int action, int mods) { + if (action == GLFW_PRESS) { + if (key == GLFW_KEY_ESCAPE) { + exitRequested = true; + } + } +} + +void loadTexture(char* textureFile, GLuint* texture) { + int width, height, nrChannels; + unsigned char* image = stbi_load(textureFile, &width, &height, &nrChannels, 0); + + // default: 3 channels, RGB + + GLenum channelFormats[] = { + 0, + GL_RED, + GL_RG, + GL_RGB, + GL_RGBA + }; + GLenum format = channelFormats[nrChannels]; + + glGenTextures(1, texture); + glBindTexture(GL_TEXTURE_2D, *texture); + + printf("%s - %d\n", textureFile, nrChannels); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, format, GL_UNSIGNED_BYTE, image); + // load texture using previously determined format ----- ^^^^^^ + + glGenerateMipmap(GL_TEXTURE_2D); + glBindTexture(GL_TEXTURE_2D, 0); + + stbi_image_free(image); +} + +void init(void) { + // create and compile vertex shader + const GLchar *vertexTextConst = vertexShader_glsl; + + GLuint vertexShader = glCreateShader(GL_VERTEX_SHADER); + glShaderSource(vertexShader, 1, &vertexTextConst, &vertexShader_glsl_len); + glCompileShader(vertexShader); + + + GLint status; + glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &status); + + if (!status) { + printf("Error compiling vertex shader: "); + GLchar infoLog[1024]; + glGetShaderInfoLog(vertexShader, 1024, NULL, infoLog); + printf("%s",infoLog); + exit(1); + } + + vertexTextConst = NULL; + + + + // create and compile fragment shader + + const GLchar *fragmentTextConst = fragmentShader_glsl; + + GLuint fragmentShader = glCreateShader(GL_FRAGMENT_SHADER); + glShaderSource(fragmentShader, 1, &fragmentTextConst, &fragmentShader_glsl_len); + glCompileShader(fragmentShader); + + glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &status); + + if (!status) { + printf("Error compiling fragment shader: "); + GLchar infoLog[1024]; + glGetShaderInfoLog(fragmentShader, 1024, NULL, infoLog); + printf("%s",infoLog); + exit(1); + } + + // create and link shader program + program = glCreateProgram(); + glAttachShader(program, vertexShader); + glAttachShader(program, fragmentShader); + glLinkProgram(program); + + glGetProgramiv(program, GL_LINK_STATUS, &status); + + if (!status) { + printf("Error linking program: "); + GLchar infoLog[1024]; + glGetProgramInfoLog(program, 1024, NULL, infoLog); + printf("%s",infoLog); + exit(1); + } + glValidateProgram(program); + + + glGetProgramiv(program, GL_VALIDATE_STATUS, &status); + + if (!status) { + printf("Error validating program: "); + GLchar infoLog[1024]; + glGetProgramInfoLog(program, 1024, NULL, infoLog); + printf("%s",infoLog); + exit(1); + } + + + // --------------- READ MODEL FILE + ParsedObjFile teapot = readObjFile(model); + numFaces = teapot.length; + + // write faces to buffer + GLuint triangleVertexBufferObject; + glGenBuffers(1, &triangleVertexBufferObject); + glBindBuffer(GL_ARRAY_BUFFER, triangleVertexBufferObject); + glBufferData(GL_ARRAY_BUFFER, teapot.length * sizeof(face), teapot.faces, GL_STATIC_DRAW); + glBindBuffer(GL_ARRAY_BUFFER, 0); + + + stbi_set_flip_vertically_on_load(flipFlag); + // -------------- READ TEXTURE FILES + for (int i = 0; i < NUM_TEXTURES; i++) { + loadTexture(textureFiles[i], &textures[i]); + } + + + // create vertex array object + glGenVertexArrays(1, &vao); + glBindVertexArray(vao); + glBindBuffer(GL_ARRAY_BUFFER, triangleVertexBufferObject); + + // vertex positions + glVertexAttribPointer( + 0, + 3, + GL_FLOAT, + GL_FALSE, + sizeof(vertex), + 0 + ); + glEnableVertexAttribArray(0); + + // vertex normals + glVertexAttribPointer( + 1, + 3, + GL_FLOAT, + GL_FALSE, + sizeof(vertex), + (void*) offsetof(vertex, normal) + ); + glEnableVertexAttribArray(1); + + // vertex texture coordinates + glVertexAttribPointer( + 2, + 2, + GL_FLOAT, + GL_FALSE, + sizeof(vertex), + (void*) offsetof(vertex, texture) + ); + glEnableVertexAttribArray(2); + + // face tangents + glVertexAttribPointer( + 3, + 3, + GL_FLOAT, + GL_FALSE, + sizeof(vertex), + (void*) offsetof(vertex, tangent) + ); + glEnableVertexAttribArray(3); + + glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindVertexArray(0); + + + // ENABLE BACKFACE CULLING + glFrontFace(GL_CCW); + glEnable(GL_CULL_FACE); + + // ENABLE DEPTH BUFFER + glEnable(GL_DEPTH_TEST); + + glClearColor(0.1f, 0.1f, 0.1f, 1.0f); +} + +void updateStats() { + printf("\rFPS: %.1f", framesSinceUpdate / timeSinceUpdate); + printf(" - Camera Position: [%f, %f, %f]", cameraPosition.x, cameraPosition.y, cameraPosition.z); + fflush(stdout); +} + +void draw(void) { + + // FPS Counter + framesSinceUpdate++; + double deltaTime = glfwGetTime(); + timeSinceUpdate += deltaTime; + glfwSetTime(0.0f); + + if (timeSinceUpdate >= timeBetweenUpdates) { + updateStats(); + timeSinceUpdate = 0.0f; + framesSinceUpdate = 0; + } + + // camera movement + handleInputs(deltaTime); + + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + glUseProgram(program); + glBindVertexArray(vao); + + // step for rotations + // counts up to 1.0 and then resets back to 0.0 forever + step += deltaTime / 15; + if (step > 1.0f) step -= 1.0f; + if (step < 0.0f) step += 1.0f; + + // step multiplied by pi * 2 for use in rotation and trig functions + GLfloat stepi = step * pi * 2; + + + // ------------- MODEL TRANSFORMATION --------------------- + // SCALE -> ROTATE -> TRANSLATE + + mat4 modelTransformation; + identity(&modelTransformation); + + rotateY(&modelTransformation, &modelTransformation, stepi * 2); + rotateZ(&modelTransformation, &modelTransformation, -23.5f / 180 * pi); + + // ------------- VIEWING TRANSFORMATION ------------------- + vec3 origin = {0.0f, 0.0f, 0.0f}; + vec3 up = {0.0f, 1.0f, 0.0f}; + + mat4 viewingTransformation; + lookAt(&viewingTransformation, &cameraPosition, &origin, &up); + + + + // -------------- PROJECTION TRANSFORMATION ---------------- + mat4 projectionTransformation; + GLfloat near = 0.1f; + GLfloat far = 20.0f; + perspectiveProjection(&projectionTransformation, near, far); + + + + // -------------- NORMALISATION TRANSFORMATION ------------- + mat4 normalisationTransformation; + GLfloat fovy = pi / 2; + normalisedDeviceCoordinatesFov(&normalisationTransformation, fovy, aspectRatio, near, far); + + + mat4 modelView; + identity(&modelView); + multiply(&modelView, &modelTransformation, &modelView); + multiply(&modelView, &viewingTransformation, &modelView); + + mat4 projection; + identity(&projection); + multiply(&projection, &projectionTransformation, &projection); + multiply(&projection, &normalisationTransformation, &projection); + + // calculate matrix for normals + mat3 normalModelView; + mat3From4(&normalModelView, &modelView); + mat3Inverse(&normalModelView, &normalModelView); + mat3Transpose(&normalModelView, &normalModelView); + + // send transformation matrix to shader + glUniformMatrix4fv(glGetUniformLocation(program, "modelView"), 1, GL_FALSE, (GLfloat*)&modelView); + glUniformMatrix3fv(glGetUniformLocation(program, "normalModelView"), 1, GL_FALSE, (GLfloat*)&normalModelView); + glUniformMatrix4fv(glGetUniformLocation(program, "projection"), 1, GL_FALSE, (GLfloat*)&projection); + + + vec4 lightPosition = {cos(stepi) * 1000.0f, 0.0f, sin(stepi) * 1000.0f, 1.0f}; + multiplyAny((GLfloat *)&lightPosition, (GLfloat *)&viewingTransformation, (GLfloat *)&lightPosition, 4, 4, 1); + + glUniform3f(glGetUniformLocation(program, "lightPosition"), lightPosition.x, lightPosition.y, lightPosition.z); + + + // SET MATERIAL DATA + glUniform1f(glGetUniformLocation(program, "shininess"), 60.0f * 4.0f); + + // SET LIGHT DATA + glUniform4f(glGetUniformLocation(program, "lightColor"), 1.0f, 1.0f, 1.0f, 1.0f); + glUniform4f(glGetUniformLocation(program, "ambientLight"), 0.05f, 0.05f, 0.05f, 1.0f); + + + // BIND TEXTURES + GLuint textureLocation; + textureLocation = glGetUniformLocation(program, "day"); + glUniform1i(textureLocation, 0); + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, textures[DAY]); + + textureLocation = glGetUniformLocation(program, "night"); + glUniform1i(textureLocation, 1); + glActiveTexture(GL_TEXTURE1); + glBindTexture(GL_TEXTURE_2D, textures[NIGHT]); + + textureLocation = glGetUniformLocation(program, "clouds"); + glUniform1i(textureLocation, 2); + glActiveTexture(GL_TEXTURE2); + glBindTexture(GL_TEXTURE_2D, textures[CLOUDS]); + + textureLocation = glGetUniformLocation(program, "ocean"); + glUniform1i(textureLocation, 3); + glActiveTexture(GL_TEXTURE3); + glBindTexture(GL_TEXTURE_2D, textures[OCEAN]); + + textureLocation = glGetUniformLocation(program, "normalMap"); + glUniform1i(textureLocation, 4); + glActiveTexture(GL_TEXTURE4); + glBindTexture(GL_TEXTURE_2D, textures[NORMAL]); + + + // draw!!1 + glDrawArrays(GL_TRIANGLES, 0, numFaces * 3); +} + +// change viewport size and adjust aspect ratio when changing window size +void framebuffer_size_callback(GLFWwindow *window, int width, int height) { + glViewport(0, 0, width, height); + aspectRatio = (float)width / height; +} + +int main(int argc, char **argv) { + + if (argc >= 2) { + model = argv[1]; + } else { + model = defaultModel; + } + // initialise window + glfwInit(); + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); + glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); + + window = glfwCreateWindow(700, 700, "Computergrafik 1", NULL, NULL); + + if (!window) { + printf("Failed to create window\n"); + glfwTerminate(); + return -1; + } + + + glfwSetFramebufferSizeCallback(window, framebuffer_size_callback); + glfwMakeContextCurrent(window); + + // disable framerate cap + glfwSwapInterval(0); + + // register keyboard event handler + glfwSetKeyCallback(window, keyboardHandler); + + // initialise glew + glewInit(); + + printf("OpenGL version supported by this platform (%s):\n", glGetString(GL_VERSION)); + + init(); + + // exit when window should close or exit is requested (ESC) + while (!glfwWindowShouldClose(window) && !exitRequested) { + draw(); + + glfwSwapBuffers(window); + glfwPollEvents(); + } + + glfwTerminate(); + + return 0; +} \ No newline at end of file diff --git a/src/matrixMath.c b/src/matrixMath.c new file mode 100644 index 0000000..f9acc51 --- /dev/null +++ b/src/matrixMath.c @@ -0,0 +1,314 @@ +#include +#include +#include +#include +#include + +#include "matrixMath.h" + +// MATRICES IN COLUMN MAJOR + +void vec3Zero(vec3* out) { + for (int i = 0; i < 3; i++) { + ((GLfloat*)out)[i] = 0; + } +} + +void vec3Add(vec3* out, vec3* a, vec3* b) { + for (int i = 0; i < 3; i++) { + ((GLfloat*)out)[i] = ((GLfloat*)a)[i] + ((GLfloat*)b)[i]; + } +} + +void vec3Multiply(vec3* out, vec3* a, GLfloat x) { + for (int i = 0; i < 3; i++) { + ((GLfloat*)out)[i] = ((GLfloat*)a)[i] * x; + } +} + +void vec3Subtract(vec3* out, vec3* a, vec3* b) { + vec3 minusB; + vec3Multiply(&minusB, b, -1); + vec3Add(out, a, &minusB); +} + +void vec3Cross(vec3* out, vec3* a, vec3* b) { + vec3 result; + result.x = a->y * b->z - a->z * b->y; + result.y = a->z * b->x - a->x * b->z; + result.z = a->x * b->y - a->y * b->x; + memcpy(out, &result, sizeof(vec3)); +} + +GLfloat vec3Length(vec3* a) { + return (GLfloat)sqrt(a->x * a->x + a->y * a->y + a->z * a->z); +} + +GLfloat vec3Dot(vec3* a, vec3* b) { + return a->x * b->x + a->y * b->y + a->z * b->z; +} + +void vec3Normalise(vec3* out, vec3* a) { + vec3Multiply(out, a, 1 / vec3Length(a)); +} +// CREATE 4x4 IDENTITY MATRIX +void identity(mat4* out) { + for (int i = 0; i < 16; i++) { + ((GLfloat*)out)[i] = (i % 4 == i / 4); + } +} + +// CREATE 4x4 TRANSLATION MATRIX +void translation(mat4* out, vec3* v) { + identity(out); + out->m03 = v->x; + out->m13 = v->y; + out->m23 = v->z; +} + +// CREATE 4x4 SCALING MATRIX +void scaling(mat4* out, vec3* v) { + identity(out); + out->m00 = v->x; + out->m11 = v->y; + out->m22 = v->z; +} + +// CREATE 4x4 ROTATION MATRIX AROUND Z AXIS +/* cos a -sin a 0 0 + * sin a cos a 0 0 + * 0 0 1 0 + * 0 0 0 1 +*/ +void rotationZ(mat4* out, GLfloat angle) { + identity(out); + out->m00 = cos(angle); + out->m10 = sin(angle); + out->m01 = -sin(angle); + out->m11 = cos(angle); +} + +// CREATE 4x4 ROTATION MATRIX AROUND Y AXIS +/* cos a 0 sin a 0 + * 0 1 0 0 + * -sin a 0 cos a 0 + * 0 0 0 1 +*/ +void rotationY(mat4* out, GLfloat angle) { + identity(out); + out->m00 = cos(angle); + out->m20 = -sin(angle); + out->m02 = sin(angle); + out->m22 = cos(angle); +} + +// CREATE 4x4 ROTATION MATRIX AROUND Y AXIS +void rotationX(mat4* out, GLfloat angle) { + identity(out); + out->m11 = cos(angle); + out->m21 = sin(angle); + out->m12 = -sin(angle); + out->m22 = cos(angle); +} + +// MULTIPLY ANY TWO MATRICES +void multiplyAny(GLfloat* out, GLfloat* A, GLfloat* B, int wA, int hA, int wB) { + int sizeOut = hA * wB; + GLfloat* result = (GLfloat*) malloc(sizeOut * sizeof(GLfloat)); + for (int i = 0; i < sizeOut; i++) { + result[i] = 0; + for (int j = 0; j < wA; j++) { + result[i] += A[j * hA + i % hA] * B[j + i / hA * wB]; + } + } + memcpy(out, result, sizeOut * sizeof(GLfloat)); + free(result); + result = NULL; +} + +// MULTIPLY TWO 4x4 MATRICES +void multiply(mat4* out, mat4* A, mat4* B) { + multiplyAny((GLfloat*)out, (GLfloat*)A, (GLfloat*)B, 4, 4, 4); +} + +// MULTIPLY in WITH TRANSLATION MATRIX OF v +void translate(mat4* out, mat4* in, vec3* v) { + mat4 translationMatrix; + translation(&translationMatrix, v); + multiply(out, &translationMatrix, in); +} + +// MULTIPLY in WITH SCALING MATRIX OF v +void scale(mat4* out, mat4* in, vec3* v) { + mat4 scalingMatrix; + scaling(&scalingMatrix, v); + multiply(out, &scalingMatrix, in); +} + +// MULTIPLY in WITH ROTATION MATRIX OF angle AROUND Z AXIS +void rotateZ(mat4* out, mat4* in, GLfloat angle) { + mat4 rotationMatrix; + rotationZ(&rotationMatrix, angle); + multiply(out, &rotationMatrix, in); +} +// MULTIPLY in WITH ROTATION MATRIX OF angle AROUND Y AXIS +void rotateY(mat4* out, mat4* in, GLfloat angle) { + mat4 rotationMatrix; + rotationY(&rotationMatrix, angle); + multiply(out, &rotationMatrix, in); +} +// MULTIPLY in WITH ROTATION MATRIX OF angle AROUND X AXIS +void rotateX(mat4* out, mat4* in, GLfloat angle) { + mat4 rotationMatrix; + rotationX(&rotationMatrix, angle); + multiply(out, &rotationMatrix, in); +} + +// TRANSPOSE MATRIX OF ANY SIZE +void transposeAny(GLfloat* out, GLfloat* in, int w, int h) { + int size = w * h; + GLfloat* result = (GLfloat*) malloc(size * sizeof(GLfloat)); + + for (int i = 0; i < size; i++) { + result[i] = in[(i % w) * h + i / w]; + } + + memcpy(out, result, size * sizeof(GLfloat)); + free(result); + result = NULL; +} + + +// TRANSPOSE 4x4 MATRIX +void transpose(mat4* out, mat4* in) { + transposeAny((GLfloat*)out, (GLfloat*)in, 4, 4); +} + +// PRINT MATRIX OF ANY SIZE +void printAny(GLfloat* M, int w, int h) { + GLfloat* transposed = (GLfloat*) malloc(w * h * sizeof(GLfloat)); + transposeAny(transposed, M, w, h); + + for (int i = 0; i < h; i++) { + for (int j = 0; j < w; j++) { + printf("%.4f ", transposed[i * w + j]); + } + printf("\n"); + } + free(transposed); + transposed = NULL; +} + +void vec3Print(vec3* a) { + printAny((GLfloat*)a, 1, 3); +} + +void mat4Print(mat4* m) { + printAny((GLfloat*)m, 4, 4); +} + +void mat3Print(mat3* m) { + printAny((GLfloat*)m, 3, 3); +} + +// TURN mat4 INTO mat3 BY CUTTING LAST COLUMN AND ROW +void mat3From4(mat3* out, mat4* in) { + memcpy(&out->m00, &in->m00, sizeof(vec3)); + memcpy(&out->m01, &in->m01, sizeof(vec3)); + memcpy(&out->m02, &in->m02, sizeof(vec3)); +} + +// TRANSPOSE 3x3 MATRIX +void mat3Transpose(mat3* out, mat3* in) { + transposeAny((GLfloat*)out, (GLfloat*)in, 3, 3); +} + +/** + * a - m00 b - m01 c - m02 + * d - m10 e - m11 f - m12 + * g - m20 h - m21 i - m22 + */ +// GET THE MINOR MATRIX OF A 3x3 MATRIX +void mat3Minor(mat3* out, mat3* in) { + mat3 result; + + result.m00 = in->m11 * in->m22 - in->m21 * in->m12; + result.m01 = in->m10 * in->m22 - in->m20 * in->m12; + result.m02 = in->m10 * in->m21 - in->m20 * in->m11; + + result.m10 = in->m01 * in->m22 - in->m21 * in->m02; + result.m11 = in->m00 * in->m22 - in->m20 * in->m02; + result.m12 = in->m00 * in->m21 - in->m20 * in->m01; + + result.m20 = in->m01 * in->m12 - in->m11 * in->m02; + result.m21 = in->m00 * in->m12 - in->m10 * in->m02; + result.m22 = in->m00 * in->m11 - in->m10 * in->m01; + + memcpy(out, &result, sizeof(mat3)); +} + +// GET THE COFACTOR MATRIX OF A 3x3 MATRIX +void mat3Cofactor(mat3* out, mat3* in) { + mat3 result; + mat3Minor(out, in); + + out->m01 *= -1; + out->m10 *= -1; + out->m21 *= -1; + out->m12 *= -1; +} + +// GET ADJOING MATRRIX OF 3x3 MATRIX +void mat3Adjoint(mat3* out, mat3* in) { + mat3Cofactor(out, in); + mat3Transpose(out, out); +} + +// MULTIPLY A 3x3 MATRIX WITH A SCALAR x (componentwise) +void mat3MultiplyScalar(mat3* out, mat3* in, GLfloat x) { + for (int i = 0; i < 9; i++) { + ((GLfloat*)out)[i] = ((GLfloat*)in)[i] * x; + } +} + +// CALCULATE DETERMINANT OF 3x3 MATRIX +GLfloat mat3Determinant(mat3* M) { + return + M->m00 * M->m11 * M->m22 + + M->m01 * M->m12 * M->m20 + + M->m02 * M->m10 * M->m21 + + - M->m20 * M->m11 * M->m02 + - M->m21 * M->m12 * M->m00 + - M->m22 * M->m10 * M->m01 + ; +} + +// GET INVERSE OF 3x3 MATRIX +void mat3Inverse(mat3* out, mat3* in) { + mat3 result; + mat3Adjoint(&result, in); + mat3MultiplyScalar(&result, &result, 1 / mat3Determinant(in)); + + memcpy(out, &result, sizeof(mat3)); +} + +// GET THE SUM OF DIFFERENCES BETWEEON TWO MATRICES +GLfloat sumDiffAny(GLfloat* A, GLfloat* B, int w, int h) { + GLfloat result = 0; + for (int i = 0; i < w * h; i++) { + result += fabs(A[i] - B[i]); + } + return result; +} + +// GET THE SUM OF DIFFERENCES BETWEEN TWO 3x3 MATRICES +GLfloat mat3SumDiff(mat3* A, mat3* B) { + return sumDiffAny((GLfloat*)A, (GLfloat*)B, 3, 3); +} + +// COMPONENTWISE SUBTRACT vec2 b FROM vec2 a +void vec2Subtract(vec2* out, vec2* a, vec2* b) { + out->x = a->x - b->x; + out->y = a->y - b->y; +} \ No newline at end of file diff --git a/src/matrixMath.h b/src/matrixMath.h new file mode 100644 index 0000000..6b32ac2 --- /dev/null +++ b/src/matrixMath.h @@ -0,0 +1,106 @@ +#ifndef MATRIX_MATH +#define MATRIX_MATH + +#include + +typedef struct { + GLfloat x; + GLfloat y; + GLfloat z; +} vec3; + +typedef struct { + GLfloat x; + GLfloat y; + GLfloat z; + GLfloat w; +} vec4; + +typedef struct { + GLfloat x; + GLfloat y; +} vec2; + +typedef struct { + GLfloat m00; + GLfloat m10; + GLfloat m20; + GLfloat m30; + + GLfloat m01; + GLfloat m11; + GLfloat m21; + GLfloat m31; + + GLfloat m02; + GLfloat m12; + GLfloat m22; + GLfloat m32; + + GLfloat m03; + GLfloat m13; + GLfloat m23; + GLfloat m33; +} mat4; + +typedef struct { + GLfloat m00; + GLfloat m10; + GLfloat m20; + + GLfloat m01; + GLfloat m11; + GLfloat m21; + + GLfloat m02; + GLfloat m12; + GLfloat m22; +} mat3; + +extern void vec3Zero(vec3* out); +extern void vec3Add(vec3* out, vec3* a, vec3* b); +extern void vec3Multiply(vec3* out, vec3* a, GLfloat x); +extern void vec3Subtract(vec3* out, vec3* a, vec3* b); +extern void vec3Cross(vec3* out, vec3* a, vec3* b); +extern void vec3Normalise(vec3* out, vec3* a); +extern GLfloat vec3Length(vec3* a); +extern GLfloat vec3Dot(vec3* a, vec3* b); + +extern void identity(mat4* out); +extern void translation(mat4* out, vec3* v); +extern void scaling(mat4* out, vec3* v); +extern void rotationZ(mat4* out, GLfloat angle); +extern void rotationY(mat4* out, GLfloat angle); +extern void rotationX(mat4* out, GLfloat angle); + +extern void multiplyAny(GLfloat* out, GLfloat* A, GLfloat* B, int wA, int hA, int wB); +extern void multiply(mat4* out, mat4* A, mat4* B); + +extern void translate(mat4* out, mat4* in, vec3* v); +extern void scale(mat4* out, mat4* in, vec3* v); +extern void rotateZ(mat4* out, mat4* in, GLfloat angle); +extern void rotateY(mat4* out, mat4* in, GLfloat angle); +extern void rotateX(mat4* out, mat4* in, GLfloat angle); + +extern void transposeAny(GLfloat* out, GLfloat* in, int w, int h); +extern void transpose(mat4* out, mat4* in); + +extern void printAny(GLfloat* M, int w, int h); +extern void vec3Print(vec3* a); +extern void mat4Print(mat4* m); +extern void mat3Print(mat3* m); + +extern void mat3From4(mat3* out, mat4* in); +extern void mat3MultiplyScalar(mat3* out, mat3* in, GLfloat x); +extern GLfloat mat3Determinant(mat3* m); +extern void mat3Transpose(mat3* out, mat3* in); +extern void mat3Minor(mat3* out, mat3* in); +extern void mat3Cofactor(mat3* out, mat3* in); +extern void mat3Adjoint(mat3* out, mat3* in); +extern void mat3Inverse(mat3* out, mat3* in); + +extern GLfloat sumDiffAny(GLfloat* A, GLfloat* B, int w, int h); +extern GLfloat mat3SumDiff(mat3* A, mat3* B); + +extern void vec2Subtract(vec2* out, vec2* a, vec2* b); +#endif \ No newline at end of file diff --git a/src/test.c b/src/test.c new file mode 100644 index 0000000..b317647 --- /dev/null +++ b/src/test.c @@ -0,0 +1,130 @@ +#include +#include +#include +#include + +#include "matrixMath.h" + + +#define KRED "\x1B[31m" +#define KGRN "\x1B[32m" +#define KNRM "\x1B[0m" + +#define EPSILON 0.001f + +int exitCode = 0; + +void printTest(char* name, bool result) { + if (result) { + printf("%s PASSED%s - %s\n", KGRN, KNRM, name); + } else { + printf("%s!!! FAILED%s - %s\n", KRED, KNRM, name); + exitCode = 1; + } +} + +void testSumDiff() { + mat3 A = {1, 0, 0, -1, 0, -1, 1, -1, 9}; + mat3 B = {0, 1, -1, 0, 0, 1, 1, -1, 8}; + GLfloat target = 7; + + GLfloat value = mat3SumDiff(&A, &B); + + bool result = fabs(value - target) < EPSILON; + + if (!result) { + printf("\nA:\n"); + mat3Print(&A); + printf("\nB:\n"); + mat3Print(&B); + printf("target: %f\n", target); + printf("value: %f\n", value); + } + + printTest("mat3SumDiff", result); +} + +void testMat3Minor() { + mat3 M = {2, 0, 1, -1, 5, -1, 3, 2, -2}; + mat3 target = {-8, 5, -17, -2, -7, 4, -5, -1, 10}; + + mat3Minor(&M, &M); + + printTest("mat3Minor", mat3SumDiff(&M, &target) < EPSILON); +} + +void testMat3Cofactor() { + mat3 M = {2, 0, 1, -1, 5, -1, 3, 2, -2}; + mat3 target = {-8, -5, -17, 2, -7, -4, -5, 1, 10}; + + mat3Cofactor(&M, &M); + + printTest("mat3Cofactor", mat3SumDiff(&M, &target) < EPSILON); +} + +void testMat3Adjoint() { + mat3 M = {1, 2, -1, 2, 1, 2, -1, 2, 1}; + mat3 target = {-3, -4, 5, -4, 0, -4, 5, -4, -3}; + + + mat3Adjoint(&M, &M); + + printTest("mat3Adjoint", mat3SumDiff(&M, &target) < EPSILON); +} + +void testMat3MultiplyScalar() { + mat3 M = {1, 2, 3, 4, 0, -1.5f, -2.5f, -3.5f, -4.5f}; + GLfloat x = 0.9f; + mat3 target = {0.9f, 1.8f, 2.7f, 3.6f, 0, -1.35f, -2.25f, -3.15f, -4.05f}; + + mat3MultiplyScalar(&M, &M, x); + + printTest("mat3MultiplyScalar", mat3SumDiff(&M, &target) < EPSILON); +} + +void testMat3Determinant() { + mat3 M = {1, -3, 2, 3, -1, 3, 2, -3, 1}; + GLfloat target = -15; + + printTest("mat3Determinant", fabs(mat3Determinant(&M) - target) < EPSILON); +} + +void testMat3Inverse() { + mat3 M = {1, 2, -1, 2, 1, 2, -1, 2, 1}; + mat3 target = {0.1875f, 0.25f, -0.3125f, 0.25f, 0, 0.25f, -0.3125f, 0.25f, 0.1875f}; + + mat3Inverse(&M, &M); + + bool result = mat3SumDiff(&M, &target) < EPSILON; + + if (!result) { + printf("\nM:\n"); + mat3Print(&M); + printf("\ntarget:\n"); + mat3Print(&target); + } + + printTest("mat3Inverse", result); +} + +void testMat3Transpose() { + mat3 M = {1, 2, 3, 4, 5, 6, 7, 8, 9}; + mat3 target = {1, 4, 7, 2, 5, 8, 3, 6, 9}; + + mat3Transpose(&M, &M); + + printTest("mat3Transpose", mat3SumDiff(&M, &target) < EPSILON); +} + +int main(void) { + testSumDiff(); + testMat3Minor(); + testMat3Cofactor(); + testMat3Adjoint(); + testMat3MultiplyScalar(); + testMat3Determinant(); + testMat3Inverse(); + testMat3Transpose(); + + return exitCode; +} \ No newline at end of file diff --git a/src/transformation.c b/src/transformation.c new file mode 100644 index 0000000..b914ab6 --- /dev/null +++ b/src/transformation.c @@ -0,0 +1,79 @@ +#include +#include +#include +#include +#include + +#include "matrixMath.h" +#include "transformation.h" + +void lookAt(mat4* out, vec3* eye, vec3* look, vec3* up) { + + + vec3 n; + vec3Subtract(&n, eye, look); + + vec3 u; + vec3Cross(&u, up, &n); + + vec3 v; + vec3Cross(&v, &n, &u); + + + vec3Normalise(&n, &n); + vec3Normalise(&u, &u); + vec3Normalise(&v, &v); + + + mat4 Mr; + identity(&Mr); + + memcpy(&Mr.m00, &u, sizeof(vec3)); + memcpy(&Mr.m01, &v, sizeof(vec3)); + memcpy(&Mr.m02, &n, sizeof(vec3)); + transpose(&Mr, &Mr); + + + vec3 t; + vec3Multiply(&u, &u, -1); + vec3Multiply(&v, &v, -1); + vec3Multiply(&n, &n, -1); + + + + t.x = vec3Dot(&u, eye); + t.y = vec3Dot(&v, eye); + t.z = vec3Dot(&n, eye); + + + memcpy(&Mr.m03, &t, sizeof(vec3)); + + memcpy(out, &Mr, sizeof(mat4)); +} + +void perspectiveProjection(mat4* out, GLfloat near, GLfloat far) { + identity(out); + + out->m22 = 1 + (far / near); + out->m32 = - 1.0f / near; + out->m23 = far; + out->m33 = 0; +} + +void normalisedDeviceCoordinates(mat4* out, GLfloat r, GLfloat l, GLfloat t, GLfloat b, GLfloat n, GLfloat f) { + identity(out); + + out->m00 = 2 / (r - l); + out->m11 = 2 / (t - b); + out->m22 = -2 / (f - n); + + out->m03 = - (r + l) / (r - l); + out->m13 = - (t + b) / (t - b); + out->m23 = - (f + n) / (f - n); +} + +void normalisedDeviceCoordinatesFov(mat4* out, GLfloat fovy, GLfloat aspectRatio, GLfloat n, GLfloat f) { + GLfloat t = tan(fovy / 2) * n; + GLfloat r = t * aspectRatio; + normalisedDeviceCoordinates(out, r, -r, t, -t, n, f); +} \ No newline at end of file diff --git a/src/transformation.h b/src/transformation.h new file mode 100644 index 0000000..9bbc03d --- /dev/null +++ b/src/transformation.h @@ -0,0 +1,11 @@ +#ifndef TRANSFORMATION_H +#define TRANSFORMATION_H + +#include + +extern void lookAt(mat4* out, vec3* eye, vec3* look, vec3* up); +extern void perspectiveProjection(mat4* out, GLfloat near, GLfloat far); +extern void normalisedDeviceCoordinates(mat4* out, GLfloat r, GLfloat l, GLfloat t, GLfloat b, GLfloat n, GLfloat f); +extern void normalisedDeviceCoordinatesFov(mat4* out, GLfloat fovy, GLfloat aspectRatio, GLfloat n, GLfloat f); + +#endif \ No newline at end of file diff --git a/src/vertexShader.glsl b/src/vertexShader.glsl new file mode 100644 index 0000000..551573f --- /dev/null +++ b/src/vertexShader.glsl @@ -0,0 +1,36 @@ +#version 330 core +layout (location = 0) in vec3 aPosition; +layout (location = 1) in vec3 aNormal; +layout (location = 2) in vec2 aTextureCoordinate; +layout (location = 3) in vec3 aTangent; + +uniform mat4 modelView; +uniform mat3 normalModelView; +uniform mat4 projection; + + +out vec3 normal; +out vec3 fragmentPosition; +out vec2 textureCoordinate; +flat out mat3 TBN; +void main() { + textureCoordinate = aTextureCoordinate; + + vec3 tangent = normalize(normalModelView * aTangent); + normal = normalize(normalModelView * aNormal); + + vec3 bitangent = normalize(cross(normal, tangent)); + + TBN = transpose(mat3( + tangent, + bitangent, + normal + )); + + + vec4 modelViewPos = modelView * vec4(aPosition, 1.0); + + gl_Position = projection * modelViewPos; + + fragmentPosition = vec3(modelViewPos); +} \ No newline at end of file diff --git a/src/wavefrontobj.c b/src/wavefrontobj.c new file mode 100644 index 0000000..ae40078 --- /dev/null +++ b/src/wavefrontobj.c @@ -0,0 +1,242 @@ +#include +#include +#include +#include + +#include "wavefrontobj.h" + +#define OBJ_LINE_BUFFER_SIZE 256 + +/** + * + * ADJUSTMENT NEEDED FOR + * - Face Definitions other than vertex/texture/normal + * - Vertex positions including w + * - Any faces using vertices yet to be defined + * (File is read top to bottom. A face using a vertex + * defined underneath it in the file will not work) + * + */ + +void storeFace( + face* f, + vec3* v1, vec2* vt1, vec3* vn1, + vec3* v2, vec2* vt2, vec3* vn2, + vec3* v3, vec2* vt3, vec3* vn3 + ) { + + memcpy(&f->v1.position, v1, sizeof(vec3)); + memcpy(&f->v2.position, v2, sizeof(vec3)); + memcpy(&f->v3.position, v3, sizeof(vec3)); + + memcpy(&f->v1.normal, vn1, sizeof(vec3)); + memcpy(&f->v2.normal, vn2, sizeof(vec3)); + memcpy(&f->v3.normal, vn3, sizeof(vec3)); + + memcpy(&f->v1.texture, vt1, sizeof(vec2)); + memcpy(&f->v2.texture, vt2, sizeof(vec2)); + memcpy(&f->v3.texture, vt3, sizeof(vec2)); +} + +void storeTB(face* f, + vec3* v1, vec2* vt1, + vec3* v2, vec2* vt2, + vec3* v3, vec2* vt3 + ) { + // https://www.opengl-tutorial.org/intermediate-tutorials/tutorial-13-normal-mapping/ + vec3 deltaPos1; + vec3 deltaPos2; + + vec3Subtract(&deltaPos1, v2, v1); + vec3Subtract(&deltaPos1, v3, v1); + + vec2 deltaTex1; + vec2 deltaTex2; + + vec2Subtract(&deltaTex1, vt2, vt1); + vec2Subtract(&deltaTex2, vt3, vt1); + + GLfloat r = 1.0f / (deltaTex1.x * deltaTex2.y - deltaTex1.y * deltaTex2.x); + + vec3Multiply(&deltaPos1, &deltaPos1, deltaTex2.y); + vec3Multiply(&deltaPos2, &deltaPos2, deltaTex1.y); + + vec3 tangent; + vec3Subtract(&tangent, &deltaPos1, &deltaPos2); + vec3Multiply(&tangent, &tangent, r); + + memcpy(&f->v1.tangent, &tangent, sizeof(vec3)); + memcpy(&f->v2.tangent, &tangent, sizeof(vec3)); + memcpy(&f->v3.tangent, &tangent, sizeof(vec3)); +} + +ParsedObjFile readObjFile(char* path) { + ParsedObjFile parsedFile; + + FILE* fp = fopen(path, "r"); + + if (fp == NULL) { + fprintf(stderr, "File could not be opened: %s", path); + parsedFile.faces = NULL; + parsedFile.length = 0; + } + + uint numVertices = 0; + uint numVertexNormals = 0; + uint numFaces = 0; + uint numTextureCoords = 0; + + char buf[OBJ_LINE_BUFFER_SIZE]; + + while (fgets(buf, OBJ_LINE_BUFFER_SIZE, fp)) { + if (buf[0] == 'v') { + if (buf[1] == ' ') { + numVertices++; + } else if (buf[1] == 't') { + numTextureCoords++; + } else if (buf[1] == 'n') { + numVertexNormals++; + } + } + if (buf[0] == 'f') { + int numSpaces = 0; + for (int i = 0; i < strlen(buf); i++) { + if (buf[i] == ' ') { + numSpaces++; + } + } + numFaces += numSpaces - 2; + } + } + + // printf("Vertices: %d\nFaces: %d\nNormals:%d\nTextures:%d\n", numVertices, numFaces, numVertexNormals, numTextureCoords); + + vec3* vertices = (vec3*) malloc(sizeof(vec3) * numVertices); + vec3* normals = (vec3*) malloc(sizeof(vec3) * numVertexNormals); + + vec2* textures = (vec2*) malloc(sizeof(vec2) * numTextureCoords); + + face* faces = (face*) malloc(sizeof(face) * numFaces); + + parsedFile.faces = faces; + parsedFile.length = numFaces; + + rewind(fp); + + uint curVertex = 0; + uint curNormal = 0; + uint curFace = 0; + uint curTexture = 0; + + while (fgets(buf, OBJ_LINE_BUFFER_SIZE, fp)) { + if (buf[0] == 'v') { + if (buf[1] == ' ') { + + sscanf(buf, + "v %f %f %f", + &vertices[curVertex].x, + &vertices[curVertex].y, + &vertices[curVertex].z + ); + curVertex++; + + } else if (buf[1] == 't') { + + int readValues = sscanf(buf, + "vt %f %f", + &textures[curTexture].x, + &textures[curTexture].y + ); + + if (readValues != 2) { + textures[curTexture].y = 0; + } + + curTexture++; + + + } else if (buf[1] == 'n') { + + sscanf(buf, + "vn %f %f %f", + &normals[curNormal].x, + &normals[curNormal].y, + &normals[curNormal].z + ); + curNormal++; + + } + } + + if (buf[0] == 'f') { + int v1, v2, v3; + int vt1, vt2, vt3; + int vn1, vn2, vn3; + + sscanf(buf, + "f %d/%d/%d %d/%d/%d %d/%d/%d", + &v1, &vt1, &vn1, + &v2, &vt2, &vn2, + &v3, &vt3, &vn3 + ); + + storeFace(&faces[curFace], + &vertices[v1 - 1], &textures[vt1 - 1], &normals[vn1 - 1], + &vertices[v2 - 1], &textures[vt2 - 1], &normals[vn2 - 1], + &vertices[v3 - 1], &textures[vt3 - 1], &normals[vn3 - 1] + ); + + storeTB(&faces[curFace], + &vertices[v1 - 1], &textures[vt1 - 1], + &vertices[v2 - 1], &textures[vt2 - 1], + &vertices[v3 - 1], &textures[vt3 - 1] + ); + + curFace++; + + int numSpaces = 0; + for (int i = 0; i < strlen(buf); i++) { + if (buf[i] == ' ') { + numSpaces++; + } + } + + if (numSpaces == 4) { + storeTB(&faces[curFace], + &vertices[v1 - 1], &textures[vt1 - 1], + &vertices[v2 - 1], &textures[vt2 - 1], + &vertices[v3 - 1], &textures[vt3 - 1] + ); + + sscanf(buf, + "f %d/%d/%d %*d/%*d/%*d %d/%d/%d %d/%d/%d", + &v1, &vt1, &vn1, + &v2, &vt2, &vn2, + &v3, &vt3, &vn3 + ); + + storeFace(&faces[curFace], + &vertices[v1 - 1], &textures[vt1 - 1], &normals[vn1 - 1], + &vertices[v2 - 1], &textures[vt2 - 1], &normals[vn2 - 1], + &vertices[v3 - 1], &textures[vt3 - 1], &normals[vn3 - 1] + ); + + curFace++; + } + + + // TODO: textures + } + + } + + free(vertices); + free(normals); + fclose(fp); + + return parsedFile; +} + +void clearParsedFile(ParsedObjFile file) { + free(file.faces); +} \ No newline at end of file diff --git a/src/wavefrontobj.h b/src/wavefrontobj.h new file mode 100644 index 0000000..950c4d1 --- /dev/null +++ b/src/wavefrontobj.h @@ -0,0 +1,30 @@ +#ifndef WAVEFRONTOBJ_H +#define WAVEFRONTOBJ_H + +#include +#include "matrixMath.h" + +typedef struct { + vec3 position; + vec3 normal; + vec2 texture; + vec3 tangent; +} vertex; + +typedef struct { + vertex v1; + vertex v2; + vertex v3; +} face; + +typedef struct { + face* faces; + GLuint length; +} ParsedObjFile; + + +extern ParsedObjFile readObjFile(char* path); +extern void clearParsedFile(ParsedObjFile file); + + +#endif \ No newline at end of file