From 118f63b9103bc0c75b7499f4084ef10045f9b3a0 Mon Sep 17 00:00:00 2001 From: MrLetsplay2003 Date: Fri, 7 Oct 2022 15:48:50 +0200 Subject: [PATCH] initial commit --- .gitignore | 2 + CMakeLists.txt | 36 +++++++++ src/kekengine/cpp/errordialog.cpp | 80 ++++++++++++++++++++ src/kekengine/cpp/kekengine.cpp | 110 ++++++++++++++++++++++++++++ src/kekengine/cpp/object.cpp | 0 src/kekengine/cpp/objparser.cpp | 83 +++++++++++++++++++++ src/kekengine/cpp/resource.cpp | 17 +++++ src/kekengine/cpp/shader.cpp | 89 ++++++++++++++++++++++ src/kekengine/include/constants.h | 4 + src/kekengine/include/errordialog.h | 13 ++++ src/kekengine/include/kekengine.h | 9 +++ src/kekengine/include/mesh.h | 37 ++++++++++ src/kekengine/include/object.h | 1 + src/kekengine/include/objparser.h | 12 +++ src/kekengine/include/resource.h | 12 +++ src/kekengine/include/shader.h | 22 ++++++ src/kekengine/include/types.h | 28 +++++++ src/kekgame/cpp/kekgame.cpp | 7 ++ src/res/shader/mesh/fragment.glsl | 17 +++++ src/res/shader/mesh/vertex.glsl | 23 ++++++ 20 files changed, 602 insertions(+) create mode 100644 .gitignore create mode 100644 CMakeLists.txt create mode 100644 src/kekengine/cpp/errordialog.cpp create mode 100644 src/kekengine/cpp/kekengine.cpp create mode 100644 src/kekengine/cpp/object.cpp create mode 100644 src/kekengine/cpp/objparser.cpp create mode 100644 src/kekengine/cpp/resource.cpp create mode 100644 src/kekengine/cpp/shader.cpp create mode 100644 src/kekengine/include/constants.h create mode 100644 src/kekengine/include/errordialog.h create mode 100644 src/kekengine/include/kekengine.h create mode 100644 src/kekengine/include/mesh.h create mode 100644 src/kekengine/include/object.h create mode 100644 src/kekengine/include/objparser.h create mode 100644 src/kekengine/include/resource.h create mode 100644 src/kekengine/include/shader.h create mode 100644 src/kekengine/include/types.h create mode 100644 src/kekgame/cpp/kekgame.cpp create mode 100644 src/res/shader/mesh/fragment.glsl create mode 100644 src/res/shader/mesh/vertex.glsl diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..6f31401 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +build/ +.vscode/ diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..97257cb --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,36 @@ +cmake_minimum_required(VERSION 3.10) + +project(KekEngine VERSION 1.0) + +option(BUILD_SHARED_LIBS "Build as a shared library" ON) + +file(GLOB KEKENGINE_SOURCE_FILES "src/kekengine/cpp/*.cpp") +file(GLOB KEKGAME_SOURCE_FILES "src/kekgame/cpp/*.cpp") + +set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) +set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) + +add_compile_definitions(FT_CONFIG_OPTION_ERROR_STRINGS) # Freetype error strings + +add_compile_options(-Wall -g) + +# libkekengine + +add_library(kekengine ${KEKENGINE_SOURCE_FILES}) + +target_link_libraries(kekengine PUBLIC glfw GLEW GL) +target_include_directories(kekengine PRIVATE "src/kekengine/include") +target_compile_definitions(kekengine PUBLIC KEKENGINE_TARGET_LINUX) + +find_package(PkgConfig REQUIRED) +pkg_check_modules(GTK3 REQUIRED gtk+-3.0) +target_link_libraries(kekengine PUBLIC ${GTK3_LIBRARIES}) +target_include_directories(kekengine PRIVATE ${GTK3_INCLUDE_DIRS}) + +# kekgame + +add_executable(kekgame ${KEKGAME_SOURCE_FILES}) + +target_link_libraries(kekgame PUBLIC kekengine) +target_include_directories(kekgame PRIVATE "src/kekengine/include") \ No newline at end of file diff --git a/src/kekengine/cpp/errordialog.cpp b/src/kekengine/cpp/errordialog.cpp new file mode 100644 index 0000000..f8f497c --- /dev/null +++ b/src/kekengine/cpp/errordialog.cpp @@ -0,0 +1,80 @@ +#include "errordialog.h" + +#include + +namespace kek::ErrorDialog { + +#ifndef KEK_DISABLE_ERRORDIALOG + +#ifdef KEKENGINE_TARGET_WINDOWS + +#define WIN32_LEAN_AND_MEAN +#include + +void showInfo(std::string message) { + UINT flags = MB_TASKMODAL | MB_ICONINFORMATION | MB_OK; + MessageBox(nullptr, message.c_str(), "KekEngine Info", flags); +} + +void showWarning(std::string message) { + UINT flags = MB_TASKMODAL | MB_ICONWARNING | MB_OK; + MessageBox(nullptr, message.c_str(), "KekEngine Warning", flags); +} + +void showError(std::string message) { + UINT flags = MB_TASKMODAL | MB_ICONERROR | MB_OK; + MessageBox(nullptr, message.c_str(), "KekEngine Error", flags); +} + +#endif + +#ifdef KEKENGINE_TARGET_LINUX + +#include + +static void show(GtkMessageType type, GtkButtonsType buttons, const char *title, const char *message) { + if(!gtk_init_check(0, nullptr)) { + std::cout << "Failed init GTK for dialogs. Dialog message: " << message << std::endl; + return; + } + + GtkWidget *w = gtk_window_new(GTK_WINDOW_TOPLEVEL); + GtkWidget *d = gtk_message_dialog_new(GTK_WINDOW(w), GTK_DIALOG_MODAL, type, buttons, "%s", message); + gtk_window_set_title(GTK_WINDOW(w), title); + gtk_dialog_run(GTK_DIALOG(d)); + gtk_widget_destroy(w); + gtk_widget_destroy(d); + while(g_main_context_iteration(nullptr, false)); +} + +void showInfo(std::string message) { + show(GTK_MESSAGE_INFO, GTK_BUTTONS_OK, "KekEngine Info", message.c_str()); +} + +void showWarning(std::string message) { + show(GTK_MESSAGE_WARNING, GTK_BUTTONS_OK, "KekEngine Warning", message.c_str()); +} + +void showError(std::string message) { + show(GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, "KekEngine Error", message.c_str()); +} + +#endif + +#else // ifndef KEK_DISABLE_ERRORDIALOG + +void showInfo(std::string message) { + std::cout << "Info: " << message << std::endl; +} + +void showWarning(std::string message) { + std::cout << "Warn: " << message << std::endl; +} + +void showError(std::string message) { + std::cerr << "Error: " << message << std::endl; +} + +#endif + +} diff --git a/src/kekengine/cpp/kekengine.cpp b/src/kekengine/cpp/kekengine.cpp new file mode 100644 index 0000000..d1db87d --- /dev/null +++ b/src/kekengine/cpp/kekengine.cpp @@ -0,0 +1,110 @@ +#include "kekengine.h" +#include "errordialog.h" +#include "objparser.h" +#include "resource.h" + +#include +#include + +#include +#include + +namespace kek { + +static GLFWwindow *window; + +static int screenWidth = 800.0f; +static int screenHeight = 600.0f; + +static void framebufferSizeCallback(GLFWwindow *window, int w, int h) { + glViewport(0, 0, w, h); + screenWidth = w; + screenHeight = h; +} + +static void glDebugOutput(GLenum source, GLenum type, unsigned int id, GLenum severity, GLsizei length, const char *message, const void *userParam) { + //if(id == 0x20071) return; + if(severity == GL_DEBUG_SEVERITY_NOTIFICATION) return; + std::cout << "OpenGL Debug (" << id << "): " << message << std::endl; +} + +int init() { + // Init GLFW + if (glfwInit() != GL_TRUE) { + const char *errorMsg; + int code = glfwGetError(&errorMsg); + if(code != GLFW_NO_ERROR) { + ErrorDialog::showError("Failed to initialize GLFW: " + std::string(errorMsg) + std::string(" (") + std::to_string(code) + std::string(")")); + }else { + ErrorDialog::showError("Failed to initialize GLFW: Unknown error"); + } + + return KEK_ERROR; + } + + std::cout << "Initialized GLFW" << std::endl; + + // Create a rendering window with OpenGL 3.3 context + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4); + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2); + glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); + glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); + glfwWindowHint(GLFW_SAMPLES, 4); + glfwWindowHint(GLFW_MAXIMIZED, GL_TRUE); + glfwWindowHint(GLFW_OPENGL_DEBUG_CONTEXT, GL_TRUE); + + /*window = glfwCreateWindow(screenWidth, screenHeight, "MrLetsplay's weird OpenGL stuff", NULL, NULL); + if(window == NULL) { + const char *errorMsg; + int code = glfwGetError(&errorMsg); + if(code != GLFW_NO_ERROR) { + ErrorDialog::showError("Failed to create window: " + std::string(errorMsg) + " (" + std::to_string(code) + ")"); + }else { + ErrorDialog::showError("Failed to create window: Unknown error"); + } + + glfwTerminate(); + return KEK_ERROR; + } + + glfwMakeContextCurrent(window); + + std::cout << "Initialized window" << std::endl; + + // Init GLEW + glewExperimental = GL_TRUE; + if (glewInit() != GLEW_OK) { + ErrorDialog::showError("Failed to initialize GLEW"); + glfwTerminate(); + return KEK_ERROR; + } + + std::cout << "Initialized GLEW" << std::endl; + + int flags; + glGetIntegerv(GL_CONTEXT_FLAGS, &flags); + if (flags & GL_CONTEXT_FLAG_DEBUG_BIT) { + glEnable(GL_DEBUG_OUTPUT); + glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS); + glDebugMessageCallback(glDebugOutput, NULL); + glDebugMessageControl(GL_DONT_CARE, GL_DONT_CARE, GL_DONT_CARE, 0, nullptr, GL_TRUE); + } + + glViewport(0, 0, screenWidth, screenHeight); + + glfwSetFramebufferSizeCallback(window, framebufferSizeCallback);*/ + + //char *file = (char *) "# Blender 3.3.0\n# www.blender.org\nmtllib Cube.mtl\no Cube\nv 1.000000 1.000000 -1.000000\nv 1.000000 -1.000000 -1.000000\nv 1.000000 1.000000 1.000000\nv 1.000000 -1.000000 1.000000\nv -1.000000 1.000000 -1.000000\nv -1.000000 -1.000000 -1.000000\nv -1.000000 1.000000 1.000000\nv -1.000000 -1.000000 1.000000\nvn -0.0000 1.0000 -0.0000\nvn -0.0000 -0.0000 1.0000\nvn -1.0000 -0.0000 -0.0000\nvn -0.0000 -1.0000 -0.0000\nvn 1.0000 -0.0000 -0.0000\nvn -0.0000 -0.0000 -1.0000\nvt 0.625000 0.500000\nvt 0.375000 0.500000\nvt 0.625000 0.750000\nvt 0.375000 0.750000\nvt 0.875000 0.500000\nvt 0.625000 0.250000\nvt 0.125000 0.500000\nvt 0.375000 0.250000\nvt 0.875000 0.750000\nvt 0.625000 1.000000\nvt 0.625000 0.000000\nvt 0.375000 1.000000\nvt 0.375000 0.000000\nvt 0.125000 0.750000\ns 0\nusemtl Material\nf 1/1/1 5/5/1 7/9/1 3/3/1\nf 4/4/2 3/3/2 7/10/2 8/12/2\nf 8/13/3 7/11/3 5/6/3 6/8/3\nf 6/7/4 2/2/4 4/4/4 8/14/4\nf 2/2/5 1/1/5 3/3/5 4/4/5\nf 6/8/6 5/6/6 1/1/6 2/2/6"; + //MemoryBuffer *fileBuf = new MemoryBuffer(file, strlen(file)); + //Mesh *mesh = ObjParser::parse(fileBuf); + + MemoryBuffer *buf = kek::Resource::loadResource("../src/res/shader/mesh/fragment.glsl"); + if(buf) { + std::cout << buf << std::endl; + delete buf; + } + + return KEK_SUCCESS; +} + +} \ No newline at end of file diff --git a/src/kekengine/cpp/object.cpp b/src/kekengine/cpp/object.cpp new file mode 100644 index 0000000..e69de29 diff --git a/src/kekengine/cpp/objparser.cpp b/src/kekengine/cpp/objparser.cpp new file mode 100644 index 0000000..82f1ac1 --- /dev/null +++ b/src/kekengine/cpp/objparser.cpp @@ -0,0 +1,83 @@ +#include "objparser.h" + +#include +#include +#include +#include + +#include "types.h" + +namespace kek::ObjParser { + +static bool readFaceVertex(std::istringstream &stream, uint32_t *outVertex, uint32_t *outTexture, uint32_t *outNormal) { + std::string face; + stream >> face; + size_t first = face.find('/'); + if(first == std::string::npos) return false; + size_t second = face.find('/', first + 1); + if(second == std::string::npos) return false; + + *outVertex = std::stoi(face.substr(0, first)); + *outTexture = std::stoi(face.substr(first + 1, second)); + *outNormal = std::stoi(face.substr(second + 1, face.length())); + + return true; +} + +Mesh *parse(MemoryBuffer *buf) { + std::vector vertexPositions; + std::vector vertexNormals; + std::vector vertexTexCoords; + + std::vector vertices; + std::vector indices; + + std::istream stream(buf); + + std::string line; + while(std::getline(stream, line)) { + std::istringstream str(line); + std::string cmd; + str >> cmd; + + if(cmd == "#") continue; + + if(cmd == "v") { + float x, y, z; + str >> x >> y >> z; + vertexPositions.push_back(glm::vec3(x, y, z)); + }else if(cmd == "vn") { + float x, y, z; + str >> x >> y >> z; + vertexNormals.push_back(glm::normalize(glm::vec3(x, y, z))); + }else if(cmd == "vt") { + float u, v = 1.0f; + str >> u >> v; + vertexTexCoords.push_back(glm::vec2(u, v)); + }else if(cmd == "f") { + uint32_t v1, v1T, v1N; + readFaceVertex(str, &v1, &v1T, &v1N); + uint32_t v2, v2T, v2N; + readFaceVertex(str, &v2, &v2T, &v2N); + uint32_t v3, v3T, v3N; + readFaceVertex(str, &v3, &v3T, &v3N); + + vertices.push_back(Vertex(vertexPositions[v1], vertexNormals[v1N], vertexTexCoords[v1T])); + indices.push_back(indices.size()); + vertices.push_back(Vertex(vertexPositions[v2], vertexNormals[v2N], vertexTexCoords[v2T])); + indices.push_back(indices.size()); + vertices.push_back(Vertex(vertexPositions[v3], vertexNormals[v3N], vertexTexCoords[v3T])); + indices.push_back(indices.size()); + }else { + std::cout << "Ignoring unknown OBJ command: " << cmd << std::endl; + } + } + + for(glm::vec3 v : vertexNormals) { + std::cout << v.x << " " << v.y << " " << v.z << std::endl; + } + + return new Mesh(vertices, indices); +} + +} diff --git a/src/kekengine/cpp/resource.cpp b/src/kekengine/cpp/resource.cpp new file mode 100644 index 0000000..95767c3 --- /dev/null +++ b/src/kekengine/cpp/resource.cpp @@ -0,0 +1,17 @@ +#include "resource.h" + +namespace kek::Resource { + +MemoryBuffer *loadResource(std::string path) { + std::ifstream in = std::ifstream(path, std::ios::binary); + if(!in.good()) return nullptr; + in.ignore(std::numeric_limits::max()); + std::streamsize size = in.gcount(); + in.clear(); + in.seekg(0, std::ios_base::beg); + char *data = (char *) malloc(size); + in.read(data, size); + return new MemoryBuffer(data, size); +} + +} \ No newline at end of file diff --git a/src/kekengine/cpp/shader.cpp b/src/kekengine/cpp/shader.cpp new file mode 100644 index 0000000..77c4030 --- /dev/null +++ b/src/kekengine/cpp/shader.cpp @@ -0,0 +1,89 @@ +#include "shader.h" +#include "types.h" +#include "resource.h" + +#include + +namespace kek { + +static GLuint compileShader(GLenum type, std::string path) { + MemoryBuffer *buf = Resource::loadResource(path); + if(!buf) { + std::cout << "Couldn't load shader: " << path << std::endl; + throw std::exception(); + } + + std::ostringstream stream; + stream << buf; + std::string code = stream.str(); + + delete buf; + + const char *src = code.c_str(); + + GLint success; + + GLuint vertex = glCreateShader(type); + glShaderSource(vertex, 1, &src, NULL); + glCompileShader(vertex); + + glGetShaderiv(vertex, GL_COMPILE_STATUS, &success); + if(!success) { + char log[512]; + glGetShaderInfoLog(vertex, 512, NULL, log); + std::cout << "Failed to compile vertex shader \"" << path << "\":\n" << log << std::endl; + } + + return vertex; +} + +static GLuint compileProgram(unsigned int n, GLenum *types, std::string *paths) { + // Compile shaders + GLuint shaderIds[n]; + for(unsigned int i = 0; i < n; i++) { + shaderIds[i] = compileShader(types[i], paths[i]); + } + + GLuint id = glCreateProgram(); + + // Attach shaders + for(unsigned int i = 0; i < n; i++) { + glAttachShader(id, shaderIds[i]); + } + + glLinkProgram(id); + + GLint success; + glGetProgramiv(id, GL_LINK_STATUS, &success); + + if(!success) { + char log[512]; + glGetProgramInfoLog(id, 512, NULL, log); + std::cout << "Failed to link program:\n" << log << std::endl; + } + + // Delete shaders + for(unsigned int i = 0; i < n; i++) { + glDeleteShader(shaderIds[i]); + } + + return id; +} + +Shader::Shader(std::string vertexPath, std::string fragmentPath) { + +} + +Shader::Shader(std::string vertexPath, std::string geometryPath, std::string fragmentPath) { + +} + +Shader::~Shader() { + +} + +void Shader::use() { + +} + +} \ No newline at end of file diff --git a/src/kekengine/include/constants.h b/src/kekengine/include/constants.h new file mode 100644 index 0000000..0ac875c --- /dev/null +++ b/src/kekengine/include/constants.h @@ -0,0 +1,4 @@ +#pragma once + +#define KEK_SUCCESS 0 +#define KEK_ERROR 1 diff --git a/src/kekengine/include/errordialog.h b/src/kekengine/include/errordialog.h new file mode 100644 index 0000000..fbd118b --- /dev/null +++ b/src/kekengine/include/errordialog.h @@ -0,0 +1,13 @@ +#pragma once + +#include + +namespace kek::ErrorDialog { + +void showInfo(std::string message); + +void showWarning(std::string message); + +void showError(std::string message); + +} \ No newline at end of file diff --git a/src/kekengine/include/kekengine.h b/src/kekengine/include/kekengine.h new file mode 100644 index 0000000..882d475 --- /dev/null +++ b/src/kekengine/include/kekengine.h @@ -0,0 +1,9 @@ +#pragma once + +#include "constants.h" + +namespace kek { + +int init(); + +} diff --git a/src/kekengine/include/mesh.h b/src/kekengine/include/mesh.h new file mode 100644 index 0000000..3cc821c --- /dev/null +++ b/src/kekengine/include/mesh.h @@ -0,0 +1,37 @@ +#pragma once + +#include +#include +#include +#include + +namespace kek { + +struct Vertex { + glm::vec3 pos; + glm::vec3 normal; + glm::vec2 texCoords; + + Vertex(glm::vec3 pos, glm::vec3 normal, glm::vec2 texCoords) { + this->pos = pos; + this->normal = normal; + this->texCoords = texCoords; + } +}; + +class Mesh { + +public: + std::vector vertices; + std::vector indices; + + Mesh(std::vector vertices, std::vector indices) { + this->vertices = vertices; + this->indices = indices; + } + + ~Mesh(); + +}; + +} \ No newline at end of file diff --git a/src/kekengine/include/object.h b/src/kekengine/include/object.h new file mode 100644 index 0000000..6f70f09 --- /dev/null +++ b/src/kekengine/include/object.h @@ -0,0 +1 @@ +#pragma once diff --git a/src/kekengine/include/objparser.h b/src/kekengine/include/objparser.h new file mode 100644 index 0000000..ea69523 --- /dev/null +++ b/src/kekengine/include/objparser.h @@ -0,0 +1,12 @@ +#pragma once + +#include + +#include "types.h" +#include "mesh.h" + +namespace kek::ObjParser { + +Mesh *parse(MemoryBuffer *buf); + +} diff --git a/src/kekengine/include/resource.h b/src/kekengine/include/resource.h new file mode 100644 index 0000000..3b7847c --- /dev/null +++ b/src/kekengine/include/resource.h @@ -0,0 +1,12 @@ +#pragma once + +#include "types.h" + +#include +#include + +namespace kek::Resource { + +MemoryBuffer *loadResource(std::string path); + +} \ No newline at end of file diff --git a/src/kekengine/include/shader.h b/src/kekengine/include/shader.h new file mode 100644 index 0000000..3360bf9 --- /dev/null +++ b/src/kekengine/include/shader.h @@ -0,0 +1,22 @@ +#pragma once + +#include + +namespace kek { + +class Shader { + +public: + unsigned int id; + + Shader(std::string vertexPath, std::string fragmentPath); + + Shader(std::string vertexPath, std::string geometryPath, std::string fragmentPath); + + ~Shader(); + + void use(); + +}; + +} \ No newline at end of file diff --git a/src/kekengine/include/types.h b/src/kekengine/include/types.h new file mode 100644 index 0000000..fc1f620 --- /dev/null +++ b/src/kekengine/include/types.h @@ -0,0 +1,28 @@ +#pragma once + +#include +#include +#include +#include + +namespace kek { + +class MemoryBuffer: public std::streambuf { + +public: + char *buffer; + size_t length; + + MemoryBuffer(char *buffer, size_t length) { + this->buffer = buffer; + this->length = length; + this->setg(buffer, buffer, buffer + length); + } + + ~MemoryBuffer() { + free(buffer); + } + +}; + +} diff --git a/src/kekgame/cpp/kekgame.cpp b/src/kekgame/cpp/kekgame.cpp new file mode 100644 index 0000000..41b000f --- /dev/null +++ b/src/kekgame/cpp/kekgame.cpp @@ -0,0 +1,7 @@ +#include + +#include "kekengine.h" + +int main(int argc, char **argv) { + kek::init(); +} \ No newline at end of file diff --git a/src/res/shader/mesh/fragment.glsl b/src/res/shader/mesh/fragment.glsl new file mode 100644 index 0000000..bac761c --- /dev/null +++ b/src/res/shader/mesh/fragment.glsl @@ -0,0 +1,17 @@ +#version 330 core + +in VS_OUT { + vec2 textureCoordinate; + vec3 normal; + vec3 fragmentPosition; +} fs_in; + +uniform vec3 cameraPos; + +out vec4 color; + +void main() { + vec3 norm = normalize(fs_in.normal); + vec3 viewDir = normalize(cameraPos - fs_in.fragmentPosition); + color = vec4(1.0); +} \ No newline at end of file diff --git a/src/res/shader/mesh/vertex.glsl b/src/res/shader/mesh/vertex.glsl new file mode 100644 index 0000000..f510ff0 --- /dev/null +++ b/src/res/shader/mesh/vertex.glsl @@ -0,0 +1,23 @@ +#version 330 core + +layout (location = 0) in vec3 position; +layout (location = 1) in vec3 normal; +layout (location = 2) in vec2 texCoord; + +uniform mat4 model; +uniform mat4 view; +uniform mat4 projection; + +out VS_OUT { + vec3 fragmentPosition; + vec2 textureCoordinate; + vec3 normal; +} vs_out; + +void main() { + gl_Position = projection * view * model * vec4(position, 1.0); + + vs_out.fragmentPosition = vec3(model * vec4(position, 1.0)); + vs_out.textureCoordinate = texCoord; + vs_out.normal = mat3(transpose(inverse(model))) * normal; +} \ No newline at end of file