#include #include #include #include "vertexShader.c" #include "skyboxVertexShader.c" #include "fragmentShader.c" #include "skyboxFragmentShader.c" #include "sceneGraph.c" #include "matrixMath.h" #include "transformation.h" #include "wavefrontobj.h" #include "sceneGraph.h" #define STB_IMAGE_IMPLEMENTATION #include "../lib/stb_image.h" #include #include #include #include GLuint program; GLuint vao; // skybox data GLuint skyboxProgram; GLuint skyboxVAO, skyboxVBO; GLuint skyboxTexture; #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; int skyboxNumFaces = 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/monkey.obj"; char* skyboxModel = "../obj/skybox.obj"; char* model; // Define a global scene graph root node SceneNode* rootNode; // 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_A) == GLFW_PRESS) { cameraPosition.x += deltaTime * 10; } if (glfwGetKey(window, GLFW_KEY_D) == GLFW_PRESS) { cameraPosition.x -= 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; } } } // Define the function to render a node (e.g., rendering an object) void renderNode(SceneNode* node) { glUniformMatrix4fv(glGetUniformLocation(program, "modelView"), 1, GL_FALSE, (GLfloat*)&node->worldTransformation); glDrawArrays(GL_TRIANGLES, 0, numFaces * 3); } 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 loadSkyboxTexture() { glGenTextures(1, &skyboxTexture); glBindTexture(GL_TEXTURE_CUBE_MAP, skyboxTexture); int width, height, nrChannels; unsigned char *data; const char* faces[6] = { "../texture/Cubemaps/Test_map.png", "../texture/Cubemaps/Test_map.png", "../texture/Cubemaps/Test_map.png", "../texture/Cubemaps/Test_map.png", "../texture/Cubemaps/Test_map.png", "../texture/Cubemaps/Test_map.png" }; for (unsigned int i = 0; i < 6; i++) { data = stbi_load(faces[i], &width, &height, &nrChannels, 0); if (data) { glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, GL_RGB, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data); stbi_image_free(data); } else { printf("Failed to load texture %s\n", faces[i]); stbi_image_free(data); } } glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); } void initSkybox() { // create and compile skybox vertex shader const GLchar *vertexTextConst = skyboxVertexShader_glsl; GLuint skyboxVertexShader = glCreateShader(GL_VERTEX_SHADER); glShaderSource(skyboxVertexShader, 1, &vertexTextConst, &skyboxVertexShader_glsl_len); glCompileShader(skyboxVertexShader); GLint status; glGetShaderiv(skyboxVertexShader, GL_COMPILE_STATUS, &status); if (!status) { printf("Error compiling skybox vertex shader: "); GLchar infoLog[1024]; glGetShaderInfoLog(skyboxVertexShader, 1024, NULL, infoLog); printf("%s",infoLog); exit(1); } vertexTextConst = NULL; // create and compile skybox fragment shader const GLchar *fragmentTextConst = skyboxFragmentShader_glsl; GLuint skyboxFragmentShader = glCreateShader(GL_FRAGMENT_SHADER); glShaderSource(skyboxFragmentShader, 1, &fragmentTextConst, &skyboxFragmentShader_glsl_len); glCompileShader(skyboxFragmentShader); glGetShaderiv(skyboxFragmentShader, GL_COMPILE_STATUS, &status); if (!status) { printf("Error compiling skybox fragment shader: "); GLchar infoLog[1024]; glGetShaderInfoLog(skyboxFragmentShader, 1024, NULL, infoLog); printf("%s",infoLog); exit(1); } // create and link skybox shader program skyboxProgram = glCreateProgram(); glAttachShader(skyboxProgram, skyboxVertexShader); glAttachShader(skyboxProgram, skyboxFragmentShader); glLinkProgram(skyboxProgram); glGetProgramiv(skyboxProgram, GL_LINK_STATUS, &status); if (!status) { printf("Error linking skybox program: "); GLchar infoLog[1024]; glGetProgramInfoLog(skyboxProgram, 1024, NULL, infoLog); printf("%s",infoLog); exit(1); } glValidateProgram(skyboxProgram); glGetProgramiv(skyboxProgram, GL_VALIDATE_STATUS, &status); if (!status) { printf("Error validating skybox program: "); GLchar infoLog[1024]; glGetProgramInfoLog(skyboxProgram, 1024, NULL, infoLog); printf("%s",infoLog); exit(1); } ParsedObjFile skyboxObj = readObjFile(skyboxModel); skyboxNumFaces = skyboxObj.length; printf("skybox faces: %d\n", skyboxNumFaces); // write faces to buffer GLuint triangleVertexBufferObject; glGenBuffers(1, &triangleVertexBufferObject); glBindBuffer(GL_ARRAY_BUFFER, triangleVertexBufferObject); glBufferData(GL_ARRAY_BUFFER, skyboxObj.length * sizeof(face), skyboxObj.faces, GL_STATIC_DRAW); glBindBuffer(GL_ARRAY_BUFFER, 0); // create vertex array object glGenVertexArrays(1, &skyboxVAO); glBindVertexArray(skyboxVAO); 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); loadSkyboxTexture(); } void renderSkybox(mat4* viewMatrix, mat4* projectionMatrix) { glUseProgram(skyboxProgram); glDepthFunc(GL_LEQUAL); glDepthMask(GL_FALSE); glDisable(GL_CULL_FACE); mat4 view = *viewMatrix; view.m30 = 0.0f; view.m31 = 0.0f; view.m32 = 0.0f; glUniformMatrix4fv(glGetUniformLocation(skyboxProgram, "view"), 1, GL_FALSE, (GLfloat*)&view); glUniformMatrix4fv(glGetUniformLocation(skyboxProgram, "projection"), 1, GL_FALSE, (GLfloat*)projectionMatrix); glBindVertexArray(skyboxVAO); glUniform1i(glGetUniformLocation(skyboxProgram, "skybox"), 10); glActiveTexture(GL_TEXTURE10); glBindTexture(GL_TEXTURE_CUBE_MAP, skyboxTexture); glDrawArrays(GL_TRIANGLES, 0, skyboxNumFaces * 3); glBindVertexArray(0); glDepthFunc(GL_LESS); } 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); // Create the root scene node rootNode = createSceneNode(); // Create a child node (e.g., for the model) SceneNode* modelNode = createSceneNode(); modelNode->render = renderNode; addChild(rootNode, modelNode); // Set transformations for the modelNode (example) rotateY(&modelNode->transformation, &modelNode->transformation, pi / 4); // 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); // initialize skybox initSkybox(); } 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); // Update and render the scene graph mat4 identityMatrix; identity(&identityMatrix); updateSceneNode(rootNode, &identityMatrix); renderSceneNode(rootNode); // 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); // draw skybox renderSkybox(&viewingTransformation, &projection); } // 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(); } freeSceneNode(rootNode); glfwTerminate(); return 0; }