/* * This file is part of mverse * Copyright (C) 2022 juanvalencia.xyz * mverse is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include #include #include #include #include #include #define STB_IMAGE_IMPLEMENTATION #include "../include/stb_image.h" #include "main.h" #include "linear.h" #include "shader.h" #include "obj.h" #include "camera.h" static void processCursorPosInput(GLFWwindow *window, struct Camera *camera); static void processKeyboardInput(GLFWwindow *window, struct Camera *camera, float deltatime); static void loadCLI(int argc, char *argv[], char **vertexPath, char **fragmentPath); static void initOpengl(void); static void initGlfw(void); static void userError(const char *msg, const char *detail); static void meshSetUp(Mesh *mesh); static void meshDraw(unsigned int shader, Mesh mesh); static void objSetUp(Obj obj); static void objDraw(unsigned int shader, Obj obj); static void usage(int status); static void glfw_key_callback(GLFWwindow *window, int key, int scancode, int action, int mods); static void glfw_size_callback(GLFWwindow *window, int width, int height); void processCursorPosInput(GLFWwindow *window, struct Camera *camera) { static int first_mouse = 1; double xpos, ypos; int width, height; static double lastX, lastY; float xoffset, yoffset; glfwGetCursorPos(window, &xpos, &ypos); glfwGetWindowSize(window, &width, &height); if (first_mouse) { first_mouse = 0; lastX = width / 2.0; lastY = height / 2.0; } xoffset = xpos - lastX; yoffset = ypos - lastY; lastX = xpos; lastY = ypos; cameraProcessMouseMovement(camera, xoffset, yoffset); } void processKeyboardInput(GLFWwindow *window, struct Camera *camera, float deltatime) { if (glfwGetKey(window, GLFW_KEY_W) == GLFW_PRESS) cameraProcessKeyboard(camera, CAMDIR_FORWARD, deltatime); if (glfwGetKey(window, GLFW_KEY_S) == GLFW_PRESS) cameraProcessKeyboard(camera, CAMDIR_BACKWARD, deltatime); if (glfwGetKey(window, GLFW_KEY_A) == GLFW_PRESS) cameraProcessKeyboard(camera, CAMDIR_LEFT, deltatime); if (glfwGetKey(window, GLFW_KEY_D) == GLFW_PRESS) cameraProcessKeyboard(camera, CAMDIR_RIGHT, deltatime); if (glfwGetKey(window, GLFW_KEY_K) == GLFW_PRESS) camera->movementSpeed += 0.2; if (glfwGetKey(window, GLFW_KEY_J) == GLFW_PRESS) camera->movementSpeed -= 0.2; } void glfw_key_callback(GLFWwindow *window, int key, int scancode, int action, int mods) { if (action != GLFW_PRESS) return; switch (key) { case GLFW_KEY_ESCAPE: case GLFW_KEY_Q: glfwSetWindowShouldClose(window, 1); break; case GLFW_KEY_1: glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); break; case GLFW_KEY_2: glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); break; case GLFW_KEY_3: glPolygonMode(GL_FRONT_AND_BACK, GL_POINT); break; default: return; } } void loadCLI(int argc, char *argv[], char **vertexPath, char **fragmentPath) { int opt; while ((opt = getopt(argc, argv, "hv:f:")) != -1) { switch (opt) { case 'h': usage(0); break; case 'v': *vertexPath = optarg; break; case 'f': *fragmentPath = optarg; break; default: usage(2); } } if (optind >= argc) userError("cli Error", "expected argument after options\n"); else if (!vertexPath) userError("environment Error", "MVERSE_VERTEX not defined"); else if (!fragmentPath) userError("environment Error", "MVERSE_FRAGMENT not defined"); } void initGlfw(void) { const char *glfwErrno; if (!glfwInit()) { glfwGetError(&glfwErrno); glfwTerminate(); userError("glfwInit() Error", glfwErrno); } } void initOpengl(void) { GLubyte glewErrno = glewInit(); if (glewErrno != GLEW_OK) { glfwTerminate(); userError("initGlfw() Error", (const char *)glewGetErrorString(glewErrno)); } } void userError(const char *msg, const char *detail) { fprintf(stderr, "%s: %s\n", msg, detail); exit(1); } void glfw_size_callback(GLFWwindow *window, int width, int height) { glViewport(0, 0, width, height); } void meshSetUp(Mesh *mesh) { glGenVertexArrays(1, &(mesh->VAO)); glGenBuffers(1, &(mesh->VBO)); glGenBuffers(1, &(mesh->EBO)); glBindVertexArray(mesh->VAO); glBindBuffer(GL_ARRAY_BUFFER, mesh->VBO); glBufferData(GL_ARRAY_BUFFER, mesh->vertexSize * sizeof(Vertex), mesh->vertices, GL_STATIC_DRAW); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mesh->EBO); glBufferData(GL_ELEMENT_ARRAY_BUFFER, mesh->indexSize * sizeof(int), mesh->indices, GL_STATIC_DRAW); glEnableVertexAttribArray(0); glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void *)0); glEnableVertexAttribArray(1); glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void *)offsetof(Vertex, normal)); glEnableVertexAttribArray(2); glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void *)offsetof(Vertex, texCoords)); glBindVertexArray(0); } void meshDraw(unsigned int shader, Mesh mesh) { glBindVertexArray(mesh.VAO); glDrawElements(GL_TRIANGLES, mesh.indexSize, GL_UNSIGNED_INT, 0); glBindVertexArray(0); } void objSetUp(Obj obj) { int i; for (i = 0; i < obj.size; i++) meshSetUp(obj.mesh + i); } void objDraw(unsigned int shader, Obj obj) { int i; for (i = 0; i < obj.size; i++) { shaderSetfv(shader, "mtl.ambient", obj.mesh[i].material.ka, glUniform3fv); shaderSetfv(shader, "mtl.diffuse", obj.mesh[i].material.kd, glUniform3fv); shaderSetfv(shader, "mtl.specular", obj.mesh[i].material.ks, glUniform3fv); meshDraw(shader, obj.mesh[i]); } } void usage(int exitStatus) { fprintf(stderr, "Usage: mverse [-h] [-v vertexshader] [-f fragmentshader] objfile\n"); exit(exitStatus); } int main(int argc, char *argv[]) { Obj obj; GLFWwindow *window; char *vertexFile, *fragmentFile; unsigned int shader; vertexFile = getenv("MVERSE_VERTEX"); fragmentFile = getenv("MVERSE_FRAGMENT"); loadCLI(argc, argv, &vertexFile, &fragmentFile); argv += optind; argc -= optind; obj = objCreate(argv[0]); // glfw Init initGlfw(); window = glfwCreateWindow(640, 480, "Mverse", NULL, NULL); if (!window) { glfwTerminate(); userError("glfwCreateWindow() Error", "Can't create window"); } // Window Setup glfwSetFramebufferSizeCallback(window, glfw_size_callback); glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_NORMAL); glfwSetKeyCallback(window, glfw_key_callback); //glfwSetCursorPosCallback(window, glfw); glfwMakeContextCurrent(window); initOpengl(); shader = shaderCreateProgram(vertexFile, fragmentFile); objSetUp(obj); struct Camera camera = { .position = {{0.0, 0.0, 10.0}}, .front = {{0.0, 0.0, 1.0}}, .up = {{0.0, 1.0, 0.0}}, .yaw = -90.0f, .pitch = 0.0f, .mouseSensivity = 0.1f, .movementSpeed = 2.0f }; Mat4 model, view, proj; Mat4 T, S, R; float t0, t, deltatime; int width, height; char title[1024]; t0 = 0; glEnable(GL_DEPTH_TEST); while (!glfwWindowShouldClose(window)) { glClearColor(0.2f, 0.2f, 0.2f, 0.0f); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // handle input glfwGetWindowSize(window, &width, &height); sprintf(title, "mverse: x: %f y: %f z: %f", camera.front.vector[0] + camera.position.vector[0], camera.front.vector[1] + camera.position.vector[1], camera.front.vector[2] + camera.position.vector[2]); glfwSetWindowTitle(window, title); t = (float)glfwGetTime(); deltatime = t - t0; t0 = t; processKeyboardInput(window, &camera, deltatime); processCursorPosInput(window, &camera); // update view = cameraGetViewMatrix(camera); proj = linearPerspective(35, (float)width / height, 0.1, 100); T = linearTranslate(0.0, 0.0, 0.0); R = linearRotate(0, 1.0, 0.0, 0.0); S = linearScale(scale, scale, scale); model = linearMat4Muln(3, T, R, S); // render glUseProgram(shader); shaderSetMatrixfv(shader, "model", model.matrix[0], glUniformMatrix4fv); shaderSetMatrixfv(shader, "proj", proj.matrix[0], glUniformMatrix4fv); shaderSetMatrixfv(shader, "view", view.matrix[0], glUniformMatrix4fv); shaderSetMatrixfv(shader, "rotNormals", R.matrix[0], glUniformMatrix4fv); shaderSetfv(shader, "viewPos", camera.position.vector, glUniform3fv); shaderSetfv(shader, "dirLight.direction", vec3(-0.2, -1.0, 0.3), glUniform3fv); shaderSetfv(shader, "dirLight.ambient", vec3(0.1, 0.1, 0.1), glUniform3fv); shaderSetfv(shader, "dirLight.diffuse", vec3(0.8, 0.8, 0.8), glUniform3fv); shaderSetfv(shader, "dirLight.specular", vec3(1.0, 1.0, 1.0), glUniform3fv); objDraw(shader, obj); glfwSwapBuffers(window); glfwPollEvents(); } glfwTerminate(); return 0; }