commit 8d2af1dbdaab14ca954cf6b998f1fd4599b55091 Author: DawidPietrykowski Date: Tue Aug 10 22:52:19 2021 +0200 first commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..dcb442d --- /dev/null +++ b/.gitignore @@ -0,0 +1,14 @@ +.vscode/.browse.c_cpp.db* +.vscode/c_cpp_properties.json +.vscode/launch.json +.vscode/ipch +.vscode +lib +include +test +packages +.vs +bin +obj +build +makeP.bat \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..d53ab61 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,137 @@ +cmake_minimum_required(VERSION 3.10) + +# So library linking is more sane. +cmake_policy(SET CMP0003 NEW) + +# So syntax problems are errors. +cmake_policy(SET CMP0010 NEW) + +# Input directories must have CMakeLists.txt +cmake_policy(SET CMP0014 NEW) + +# Compile definitions. +cmake_policy(SET CMP0043 NEW) + +# Use ROOT variables in find_package. +cmake_policy(SET CMP0074 NEW) + +# Convert relative paths to absolute in target_sources() +cmake_policy(SET CMP0076 NEW) + +# Copy files from source directory to destination directory, substituting any +# variables. Create destination directory if it does not exist. + +macro(configure_files srcDir destDir) + message(STATUS "Configuring directory ${destDir}") + make_directory(${destDir}) + + file(GLOB templateFiles RELATIVE ${srcDir} ${srcDir}/*) + foreach(templateFile ${templateFiles}) + set(srcTemplatePath ${srcDir}/${templateFile}) + if(NOT IS_DIRECTORY ${srcTemplatePath}) + message(STATUS "Configuring file ${templateFile}") + configure_file( + ${srcTemplatePath} + ${destDir}/${templateFile} + @ONLY) + endif(NOT IS_DIRECTORY ${srcTemplatePath}) + endforeach(templateFile) +endmacro(configure_files) + +# Initialize project +project(ComputeEngine) + +set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) +set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) +set(default_build_type "Release") + +# Add binary +add_library(${PROJECT_NAME} STATIC) + +# Find GLFW + +set(GLFW_BUILD_DOCS OFF CACHE BOOL "" FORCE) +set(GLFW_BUILD_TESTS OFF CACHE BOOL "" FORCE) +set(GLFW_BUILD_EXAMPLES OFF CACHE BOOL "" FORCE) + +find_path(GLFW_INCLUDE_DIR GLFW/glfw3.h +HINTS +${CMAKE_CURRENT_SOURCE_DIR}/include +${CMAKE_CURRENT_SOURCE_DIR}/lib +${GLFW_ROOT} +${GLFW_ROOT}/include) + +if(GLFW_INCLUDE_DIR) +message(STATUS "GLFW_INCLUDE_DIR: ${GLFW_INCLUDE_DIR}") +else() +message(FATAL_ERROR "glfw3.h not found") +endif() + +find_path(GLFW_LIB_DIR glfw3.lib +HINTS +${CMAKE_CURRENT_SOURCE_DIR}/include +${CMAKE_CURRENT_SOURCE_DIR}/lib +${GLFW_ROOT}/lib +${GLFW_ROOT}/lib-vc2019 +${GLFW_ROOT}/lib-vc2017 +${GLFW_ROOT}/lib-vc2015) + +if(GLFW_LIB_DIR) +message(STATUS "GLFW_LIB_DIR: ${GLFW_LIB_DIR}") +else() +message(FATAL_ERROR "glfw3.lib not found") +endif() + +# Find GLEW +find_package(GLEW REQUIRED) + +if(GLEW_FOUND) +set(GLEW_INCLUDE_DIR ${GLEW_INCLUDE_DIRS}) +get_filename_component(GLEW_LIBRARIES ${GLEW_LIBRARIES} DIRECTORY) +message(STATUS "GLEW_INCLUDE_DIR: ${GLEW_INCLUDE_DIR}") +message(STATUS "GLEW_LIBRARIES: ${GLEW_LIBRARIES}") +else() +message(FATAL_ERROR "GLEW not found") +endif() + +# Find OpenGL +if(NOT WIN32) + find_package(OpenGL REQUIRED) +endif() + +# Find stb_image +find_path(stb_image_INCLUDE_DIR stb_image.h +HINTS +${CMAKE_CURRENT_SOURCE_DIR}/include +${CMAKE_CURRENT_SOURCE_DIR}/include/stb +${CMAKE_CURRENT_SOURCE_DIR}/lib +${stb_image_ROOT}) + +if(stb_image_INCLUDE_DIR) +message(STATUS "stb_image_INCLUDE_DIR: ${stb_image_INCLUDE_DIR}") +else() +message(FATAL_ERROR "stb_image not found") +endif() + +# Add source +add_subdirectory(src) +include_directories(${CMAKE_CURRENT_SOURCE_DIR}/src +${GLFW_INCLUDE_DIR} +${OPENGL_INCLUDE_DIR} +${stb_image_INCLUDE_DIR} +${GLEW_INCLUDE_DIR}) +target_link_directories(${PROJECT_NAME} PUBLIC ${GLFW_LIB_DIR} ${GLEW_LIBRARIES}) +target_link_libraries(${PROJECT_NAME} "glfw3.lib" "opengl32.lib" "glew32s.lib") + +# Header files +file(COPY src/ComputeEngine.h DESTINATION ${CMAKE_BINARY_DIR}/include) +file(COPY src/Timer.h DESTINATION ${CMAKE_BINARY_DIR}/include) +file(COPY src/Texture.h DESTINATION ${CMAKE_BINARY_DIR}/include) +file(COPY src/UBO.h DESTINATION ${CMAKE_BINARY_DIR}/include) +file(COPY src/SSBO.h DESTINATION ${CMAKE_BINARY_DIR}/include) +file(COPY src/Shader.h DESTINATION ${CMAKE_BINARY_DIR}/include) +file(COPY src/Program.h DESTINATION ${CMAKE_BINARY_DIR}/include) +file(COPY src/GLUtils.h DESTINATION ${CMAKE_BINARY_DIR}/include) +#file(COPY src/Scene.h DESTINATION ${CMAKE_BINARY_DIR}/include) + diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..d6adc5a --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2021 Dawid Pietrykowski + +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. \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..9e21481 --- /dev/null +++ b/README.md @@ -0,0 +1,79 @@ +# GPU Ray Tracer +GPU Ray Tracing application based on my OpenGL Compute Engine. +

+ +

+ +## Dependencies + +* OpenGL +* GLFW +* GLEW +* [stb_image](https://github.com/nothings/stb) +* [OpenGL Compute Engine](https://github.com/DawidPietrykowski/ComputeEngine) + +## Build + +Specify these root directories for dependencies in [make.bat](make.bat) script: +* GLFW_ROOT +* GLEW_ROOT +* stb_image_ROOT +* ComputeEngine_ROOT + +Run [make.bat](make.bat) script. + +## Config files + +Each scene/configuration is described in a config file. + +Example scenes are located in [/configs](configs) directory. + +All available parameters are specified in [config_template.txt](config_template.txt). + +## Usage + +Pass chosen config file to the generated executable as an argument in command line. + +If no argument given, *config.txt* is picked. + +## Features + +* Wavefront (.obj) file import +* PNG, JPG, HDR texture import +* Object can be an imported triangle mesh, sphere or infinite plane +* Each scene is saved in a separate file +* Specular and diffuse reflections +* Physics based refractions +* Camera movement +* Screenshot saving + +## Example images +Mesh | Refraction +:-------------------------:|:-------------------------: +![](screenshots/fox2.png) | ![](screenshots/refraction1.png) +Multiple meshes 1 | Multiple meshes 2 +![](screenshots/objects1.png) | ![](screenshots/objects4.jpg) +Solar system 1 | Solar system 2 +![](screenshots/solar_system1.png) | ![](screenshots/solar_system2.png) + +## Keys +Camera + +* UP - SPACE +* DOWN - CTRL +* LEFT - A +* RIGHT - D +* FORWARD - W +* BACKWARD - S + +Window + +* Screenshot - L +* Disable input to window - P +* Close application - ESC + +## License and copyright + +© Dawid Pietrykowski + +Licensed under the [MIT LICENSE](LICENSE) \ No newline at end of file diff --git a/make.bat b/make.bat new file mode 100644 index 0000000..9a87c24 --- /dev/null +++ b/make.bat @@ -0,0 +1,8 @@ +@echo off + +set GLFW_ROOT= +set GLEW_ROOT= +set stb_image_ROOT= + +cmake -B build -S . -DGLFW_ROOT=%GLFW_ROOT% -DGLEW_ROOT=%GLEW_ROOT% -Dstb_image_ROOT=%stb_image_ROOT% +cmake --build build --config Release \ No newline at end of file diff --git a/remake.bat b/remake.bat new file mode 100644 index 0000000..5b690da --- /dev/null +++ b/remake.bat @@ -0,0 +1,2 @@ +rmdir /s /q build +make.bat \ No newline at end of file diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt new file mode 100644 index 0000000..0d4c97f --- /dev/null +++ b/src/CMakeLists.txt @@ -0,0 +1,21 @@ +target_sources(${PROJECT_NAME} +PUBLIC +GLUtils.cpp +GLUtils.h +ComputeEngine.cpp +ComputeEngine.h +Program.cpp +Program.h +#Scene.cpp +#Scene.h +Shader.cpp +Shader.h +SSBO.cpp +SSBO.h +UBO.cpp +UBO.h +Texture.cpp +Texture.h +Timer.cpp +Timer.h +) \ No newline at end of file diff --git a/src/ComputeEngine.cpp b/src/ComputeEngine.cpp new file mode 100644 index 0000000..ea5ab1c --- /dev/null +++ b/src/ComputeEngine.cpp @@ -0,0 +1,379 @@ +#include + +#include "ComputeEngine.h" + +#define STBI_MSC_SECURE_CRT +#define STB_IMAGE_WRITE_IMPLEMENTATION +#pragma warning(disable : 4996) +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "Texture.h" +#include "Program.h" +#include "Shader.h" +#include "Timer.h" + +ComputeEngine* m_instance; + +ComputeEngine::~ComputeEngine() { + m_TextureDisplay.~Program(); + m_instance = nullptr; + glfwTerminate(); +} + +ComputeEngine::ComputeEngine(int w, int h, std::string name, bool focus, bool tonemapping) +{ + m_instance = this; + + m_width = w; + m_height = h; + + m_window = InitWindow(m_width, m_height, name, focus); + LOG("GLFW initiated"); + + GLCall(glEnable(GL_DEBUG_OUTPUT)); + glewExperimental = GL_FALSE; + + glfwGetCursorPos(m_window, &m_xpos, &m_ypos); + + Shader vertexShadrer(GL_VERTEX_SHADER); + Shader fragmentShader(GL_FRAGMENT_SHADER, tonemapping); + + m_TextureDisplay = InitQuad(vertexShadrer, fragmentShader); + LOG("Texture display shader created"); + + for(int i = 0; i < 348; i++) + m_keys[i] = GLFW_RELEASE; +} + +GLFWwindow* ComputeEngine::InitWindow(int width, int height, std::string name, bool focus) { + + GLFWwindow* window; + + if (!glfwInit()) + LOG("Failed to initialize glfw"); + + window = glfwCreateWindow(width, height, name.c_str(), NULL, NULL); + if (!window) + { + glfwTerminate(); + LOG("Failed to initialize window"); + } + + glfwMakeContextCurrent(window); + glfwSetKeyCallback(window, this->KeyCallback); + glfwSetMouseButtonCallback(window, this->MouseButtonCallback); + glfwSetFramebufferSizeCallback(window, this->FramebufferSizeCallback); + if (focus) { + glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED); + } + glfwSetWindowSizeLimits(window, 200, 200, m_fullscreen_width, m_fullscreen_height); + + m_input = focus; + + GLenum err = glewInit(); + if (GLEW_OK != err) + { + /* Problem: glewInit failed, something is seriously wrong. */ + LOG("Error: " << glewGetErrorString(err)); + } + + LOG("Using GL " << glGetString(GL_VERSION)); + LOG("Using GLEW " << glewGetString(GLEW_VERSION)); + + GLFWmonitor* primary = glfwGetPrimaryMonitor(); + const GLFWvidmode* mode = glfwGetVideoMode(primary); + m_fullscreen_width = mode->width; + m_fullscreen_height = mode->height; + + return window; +} + +Program ComputeEngine::InitQuad(Shader& vertShader, Shader& fragShader) { + + unsigned int buffer; + GLCall(glGenBuffers(1, &buffer)); + GLCall(glBindBuffer(GL_ARRAY_BUFFER, buffer)); + float quad_vao[] = { + -1.0f, -1.0f, 0.0f, 0.0f, // bottom left corner + -1.0f, 1.0f, 0.0f, 1.0f, // top left corner + 1.0f, -1.0f, 1.0f, 0.0f, // bottom right corner + 1.0f, 1.0f, 1.0f, 1.0f // top right corner + }; + GLCall(glBufferData(GL_ARRAY_BUFFER, sizeof(float) * 16, quad_vao, GL_STATIC_DRAW)); + GLuint vao = 0; + GLCall(glGenVertexArrays(1, &vao)); + GLCall(glBindVertexArray(vao)); + GLCall(glBindBuffer(GL_ARRAY_BUFFER, buffer)); + GLCall(glEnableVertexAttribArray(0)); + GLCall(glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(float) * 4, (void*)0)); + GLCall(glEnableVertexAttribArray(1)); + GLCall(glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(float) * 4, (void*)(sizeof(float) * 2))); + + Program quad_program(vertShader, fragShader); + return quad_program; +} + +void GLAPIENTRY MessageCallback(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar* message, const void* userParam) +{ + std::string sev = ""; + if (severity == GL_DEBUG_SEVERITY_LOW) + sev = "low"; + else if (severity == GL_DEBUG_SEVERITY_HIGH) + sev = "high"; + else if (severity == GL_DEBUG_SEVERITY_NOTIFICATION) + return; + else if (severity == GL_DEBUG_SEVERITY_MEDIUM) + sev = "medium"; + + fprintf(stderr, "GL CALLBACK: %s type = %x, severity = %s, message = %s\n", + (type == GL_DEBUG_TYPE_ERROR ? "** GL ERROR **" : ""), + type, sev.c_str(), message); + +} + +void ComputeEngine::FramebufferSizeCallback(GLFWwindow* window, int w, int h) { + if(w != 0 && h != 0) + (*m_instance).UpdateSize(w, h); +} + +void ComputeEngine::KeyCallback(GLFWwindow* window, int key, int scancode, int action, int mods) { + (*m_instance).m_keys[key] = action; +} + +void ComputeEngine::MouseButtonCallback(GLFWwindow* window, int button, int action, int mods) +{ + (*m_instance).m_mouse_buttons[button] = action; +} + +void ComputeEngine::PollEvents() { + ResetKeys(); + + glfwPollEvents(); + + for (int i = 0; i < 348; i++) { + m_keys[i] = (m_keys[i] == GLFW_PRESS && m_prev_keys[i] == GLFW_PRESS) ? GLFW_REPEAT : m_keys[i]; + } + + for (int i = 0; i < 8; i++) { + m_mouse_buttons[i] = (m_mouse_buttons[i] == GLFW_PRESS && m_prev_mouse_buttons[i] == GLFW_PRESS) ? GLFW_REPEAT : m_mouse_buttons[i]; + } + + double x_p, y_p; + glfwGetCursorPos(m_window, &x_p, &y_p); + + m_dx = (float)(m_xpos - x_p) * m_input; + m_dy = (float)(m_ypos - y_p) * m_input; + m_xpos = x_p; + m_ypos = y_p; +} + +void ComputeEngine::ResetKeys() { + memcpy(m_prev_keys, m_keys, sizeof(int) * 348); + memcpy(m_prev_mouse_buttons, m_mouse_buttons, sizeof(int) * 8); +} + +void ComputeEngine::SetFullscreen(bool fullscreen) { + m_fullscreen = fullscreen; + + if (!m_fullscreen) { + m_width = m_windowed_width; + m_height = m_windowed_height; + + glfwSetWindowMonitor(m_window, NULL, 200, 200, m_width, m_height, GLFW_DONT_CARE); + + glViewport(0, 0, m_width, m_height); + } + else { + m_windowed_width = m_width; + m_windowed_height = m_height; + + m_width = m_fullscreen_width; + m_height = m_fullscreen_height; + + glfwSetWindowMonitor(m_window, NULL, 0, 0, m_width, m_height, GLFW_DONT_CARE); + + glViewport(0, 0, m_width, m_height); + + m_dx = 0; + m_dy = 0; + } +} + +void ComputeEngine::SwitchFullScreen() { + SetFullscreen(!IsFullscreen()); +} + +void ComputeEngine::GetWindowSize(int& width, int& height) { + width = m_width; + height = m_height; +} + +bool ComputeEngine::IsFullscreen() { + return m_fullscreen; +} + +void ComputeEngine::SetInput(bool mode) { + m_input = mode; + if(!mode) + glfwSetInputMode(m_window, GLFW_CURSOR, GLFW_CURSOR_NORMAL); + else + glfwSetInputMode(m_window, GLFW_CURSOR, GLFW_CURSOR_DISABLED); +} + +void ComputeEngine::UpdateSize(int width, int height) { + m_width = width; + m_height = height; + + glViewport(0, 0, m_width, m_height); +} + +bool ComputeEngine::ShouldClose() { + return glfwWindowShouldClose(m_window); +} + +void ComputeEngine::CloseWindow() { + glfwSetWindowShouldClose(m_window, GLFW_TRUE); +} + +void ComputeEngine::SwitchInput() { + SetInput(!m_input); +} + +void ComputeEngine::DrawTexture(Texture &texture){ + m_TextureDisplay.Use(); + + GLCall(glMemoryBarrier(GL_ALL_BARRIER_BITS)); + + + GLCall(glActiveTexture(GL_TEXTURE0)); + GLuint boundTexture = 0; + GLCall(glGetIntegerv(GL_TEXTURE_BINDING_2D, (GLint*)&boundTexture)); + GLCall(glBindTexture(GL_TEXTURE_2D, texture.GetId())); + + GLCall(glDrawArrays(GL_TRIANGLE_STRIP, 0, 4)); + + GLCall(glBindTexture(GL_TEXTURE_2D, boundTexture)); + GLCall(glfwSwapBuffers(m_window)); +} + +void ComputeEngine::UpdateFrametime() { + double ms = m_timer.GetMilis(); + m_frametime = m_framecount > 0 ? ms : 1; + m_time_sum += m_frametime; + + m_framecount++; +} + +void ComputeEngine::SaveScreen(std::string name) { + bool found = true; + int i = 0; + while (found) { + i++; + std::string full_name_png = "screenshots\\" + name + std::to_string(i) + ".png"; + std::string full_name_jpg = "screenshots\\" + name + std::to_string(i) + ".jpg"; + found = FileExists(full_name_png) || FileExists(full_name_jpg); + } + + const char* path = "screenshots"; + CreateDirectoryA(path, NULL); + + name = name + std::to_string(i) + ".png"; + + int img_size = 4 * m_width * m_height; + + unsigned char* pixels = new unsigned char[img_size]; + + LOG("Saving screenshot: " + name); + + GLCall(glReadBuffer(GL_FRONT)); + GLCall(glReadPixels(0, 0, m_width, m_height, GL_RGBA, GL_UNSIGNED_BYTE, pixels)); + + stbi__flip_vertically_on_write = true; + + stbi_write_png(("screenshots\\" + name).c_str(), m_width, m_height, 4, pixels, m_width * 4); + + delete[] pixels; +}; + +void ComputeEngine::Finish() { + GLCall(glFinish()); +} + +bool ComputeEngine::IsKeyPressed(int key) { + int state = m_keys[key]; + return state == GLFW_PRESS || state == GLFW_REPEAT; +} + +bool ComputeEngine::IsKeyClicked(int key) { + return m_keys[key] == GLFW_PRESS; +} + +bool ComputeEngine::IsMouseButtonPressed(int button) { + int state = m_mouse_buttons[button]; + return state == GLFW_PRESS || state == GLFW_REPEAT; +} + +bool ComputeEngine::IsMouseButtonClicked(int button) { + return m_prev_mouse_buttons[button] == GLFW_PRESS; +} + +void ComputeEngine::GetMousePos(double& xpos, double& ypos) { + xpos = m_xpos; + ypos = m_ypos; +} + +void ComputeEngine::GetMouseDelta(double& dx, double& dy) { + dx = m_dx; + dy = m_dy; +} + +bool ComputeEngine::GetInput() { + return m_input; +} + +double ComputeEngine::GetFrametime() { + return m_frametime; +} + +int ComputeEngine::GetFramecount() { + return m_framecount; +} + +double ComputeEngine::GetAverageFrametime() { + return m_time_sum / m_framecount; +} + +int ComputeEngine::GetWidth() { + return m_width; +} + +int ComputeEngine::GetHeight() { + return m_height; +} + +float ComputeEngine::GetRand() { + return (float)((double)rand() / (RAND_MAX)); +} + +bool ComputeEngine::FileExists(const std::string& name) { + struct stat buffer; + return (stat(name.c_str(), &buffer) == 0); +} + +GLFWwindow* ComputeEngine::GetWindow() { + return m_window; +} \ No newline at end of file diff --git a/src/ComputeEngine.h b/src/ComputeEngine.h new file mode 100644 index 0000000..cba15aa --- /dev/null +++ b/src/ComputeEngine.h @@ -0,0 +1,89 @@ +#pragma once +#define GLEW_STATIC + +#include "UBO.h" +#include "GLUtils.h" +#include "SSBO.h" +#include "Texture.h" +#include "Program.h" +#include "Scene.h" +#include "Shader.h" +#include "Timer.h" + +class ComputeEngine { +public: + ComputeEngine(int w, int h, std::string name, bool focus = true, bool tonemapping = false); + ~ComputeEngine(); + + GLFWwindow* InitWindow(int width, int height, std::string name, bool focus); + + void UpdateFrametime(); + void CloseWindow(); + void SwitchInput(); + void SetInput(bool mode); + void PollEvents(); + void GetMousePos(double& xpos, double& ypos); + void GetMouseDelta(double& dx, double& dy); + void SaveScreen(std::string name); + void ResetKeys(); + void SetFullscreen(bool fullscreen); + void SwitchFullScreen(); + void GetWindowSize(int& width, int& height); + void DrawTexture(Texture& texture); + void Finish(); + + bool IsFullscreen(); + bool GetInput(); + bool ShouldClose(); + bool IsKeyPressed(int key); + bool IsKeyClicked(int key); + bool IsMouseButtonPressed(int button); + bool IsMouseButtonClicked(int button); + bool FileExists(const std::string& name); + + double GetFrametime(); + double GetAverageFrametime(); + + int GetWidth(); + int GetHeight(); + int GetFramecount(); + + float GetRand(); + + GLFWwindow* GetWindow(); + +private: + Program m_TextureDisplay; + Timer m_timer; + GLFWwindow* m_window; + + + Program InitQuad(Shader& vertShader, Shader& fragShader); + + double m_frametime = 0; + double m_time_sum = 0; + + int m_keys[348]; + int m_prev_keys[348]; + int m_mouse_buttons[8]; + int m_prev_mouse_buttons[8]; + + bool m_fullscreen = false; + + bool m_input = false; + + int m_framecount = 0; + int m_width, m_height; + int m_windowed_width, m_windowed_height; + + double m_xpos = 0, m_ypos = 0; + float m_dx = 0, m_dy = 0; + + int m_fullscreen_width = 1920; + int m_fullscreen_height = 1080; + + static void FramebufferSizeCallback(GLFWwindow* window, int width, int height); + static void KeyCallback(GLFWwindow* window, int key, int scancode, int action, int mods); + static void MouseButtonCallback(GLFWwindow* window, int button, int action, int mods); + void UpdateSize(int w, int h); +}; diff --git a/src/GLUtils.cpp b/src/GLUtils.cpp new file mode 100644 index 0000000..fc4f7ba --- /dev/null +++ b/src/GLUtils.cpp @@ -0,0 +1,75 @@ +#pragma once +#include "GLUtils.h" + +#include +#include + +#include +#include + +using std::cout; +using std::endl; + +bool glErrorCheck() { + bool no_err = true; + + GLenum error = glGetError(); + while (error) { + std::string err_str; + switch (error) { + case GL_NO_ERROR: + err_str = "GL_NO_ERROR"; + break; + case GL_INVALID_ENUM: + err_str = "GL_INVALID_ENUM"; + break; + case GL_INVALID_VALUE: + err_str = "GL_INVALID_VALUE"; + break; + case GL_INVALID_OPERATION: + err_str = "GL_INVALID_OPERATION"; + break; + case GL_INVALID_FRAMEBUFFER_OPERATION: + err_str = "GL_INVALID_FRAMEBUFFER_OPERATION"; + break; + case GL_OUT_OF_MEMORY: + err_str = "GL_OUT_OF_MEMORY"; + break; + default: + err_str = "GL_UNKNOWN_ERROR"; + break; + } + + cout << err_str << endl; + + error = glGetError(); + + no_err = false; + return no_err; + } + + return no_err; +} + +void glClearError() { + while (glGetError() != GL_NO_ERROR); +} + +void PrintSizeLimits() { + GLint size; + GLint size2; + + glGetIntegerv(GL_MAX_TEXTURE_BUFFER_SIZE, &size); + std::cout << "GL_MAX_TEXTURE_BUFFER_SIZE is " << ((float)size * 32.0f / 1073741824.0f) << " GB" << endl; + glGetIntegerv(GL_MAX_SHADER_STORAGE_BLOCK_SIZE, &size); + std::cout << "GL_MAX_SHADER_STORAGE_BLOCK_SIZE is " << ((float)size / 1073741824.0f) << " GB" << endl; + glGetIntegerv(GL_MAX_UNIFORM_BLOCK_SIZE, &size); + std::cout << "GL_MAX_UNIFORM_BLOCK_SIZE is " << ((float)size / 1024.0f) << " KB" << endl; + glGetIntegerv(GL_MAX_TEXTURE_SIZE, &size); + std::cout << "GL_MAX_TEXTURE_SIZE is " << ((float)size / 1024.0f) << " MB" << endl; + glGetIntegerv(GL_MAX_IMAGE_UNITS, &size); + std::cout << "GL_MAX_IMAGE_UNITS is " << size << endl; + glGetIntegeri_v(GL_MAX_COMPUTE_WORK_GROUP_SIZE, 0, &size); + glGetIntegeri_v(GL_MAX_COMPUTE_WORK_GROUP_SIZE, 1, &size2); + std::cout << "GL_MAX_COMPUTE_WORK_GROUP_SIZE is " << size << ", " << size2 << endl; +} \ No newline at end of file diff --git a/src/GLUtils.h b/src/GLUtils.h new file mode 100644 index 0000000..778d19d --- /dev/null +++ b/src/GLUtils.h @@ -0,0 +1,29 @@ +#pragma once + +#include +#include +#include + + +#define __FILENAME__ (strrchr(__FILE__, '\\') ? strrchr(__FILE__, '\\') + 1 : __FILE__) + +#define ASSERT(x) if(!(x)){ std::cout << "[ERROR] " << #x << '\n';\ +__debugbreak();} + +#define GLCall(x) \ + glClearError();\ + x;\ + if(!(glErrorCheck())){ std::cout << "[ERROR] " << #x << '\n';\ + __debugbreak();} + +#if 1 +#define LOG(msg) \ + std::cout << '[' << __TIME__ << "] Renderer: " << msg << std::endl +#else +#define LOG(msg) \ + std::cout << __FILENAME__ << "(" << __LINE__ << "): " << msg << std::endl +#endif + +void PrintSizeLimits(); +bool glErrorCheck(); +void glClearError(); \ No newline at end of file diff --git a/src/Program.cpp b/src/Program.cpp new file mode 100644 index 0000000..40af97e --- /dev/null +++ b/src/Program.cpp @@ -0,0 +1,428 @@ +#include "Program.h" + +#include +#include + +#include "GLUtils.h" +#include "Texture.h" +#include "Shader.h" + +Program::Program() : + m_id(0) +{ +} + +Program::Program(unsigned int _id) : + m_id(_id) +{ +} + +Program::Program(Shader& vertShader, Shader& fragShader) { + unsigned int program; + GLCall(program = glCreateProgram()); + + GLCall(glAttachShader(program, vertShader.GetId())); + GLCall(glAttachShader(program, fragShader.GetId())); + GLCall(glLinkProgram(program)); + GLCall(glValidateProgram(program)); + + m_id = program; +} + +Program::~Program() { + Delete(); +} + + +Program::Program(Program&& other) : + m_id(other.m_id) +{ + other.m_id = 0; +} + +Program& Program::operator=(Program&& other) +{ + if (this != &other) + { + Delete(); + std::swap(m_id, other.m_id); + } + return *this; +} + +Program::Program(Shader& computeShader) { + unsigned int program; + GLCall(program = glCreateProgram()); + + GLCall(glAttachShader(program, computeShader.GetId())); + GLCall(glLinkProgram(program)); + GLCall(glValidateProgram(program)); + + m_id = program; +} + +void Program::Delete() { + if (m_id != 0) { + GLCall(glDeleteProgram(m_id)); + } + m_id = 0; +} + +void Program::DispatchCompute(int width, int height) { + Use(); + + int up_w = int(ceil((float)width / 8.0f)); + int up_h = int(ceil((float)height / 8.0f)); + GLCall(glDispatchCompute(up_w, up_h, 1)); +} + +void Program::DispatchCompute(int x) { + Use(); + + GLCall(glDispatchCompute(x, 1, 1)); +} + +void Program::Use() { + GLCall(glUseProgram(m_id)); +} + +void Program::BindTexture(Texture& texture, int slot) { + Use(); + + int location; + GLCall(location = glGetUniformLocation(m_id, texture.GetName().c_str())); + + ASSERT(location != -1); + + Use(); + GLCall(glUniform1i(location, slot)); + texture.Bind(slot); +} +void Program::BindTexture(Texture* texture, int slot) { + Use(); + + int location; + GLCall(location = glGetUniformLocation(m_id, texture->GetName().c_str())); + + ASSERT(location != -1); + + Use(); + GLCall(glUniform1i(location, slot)); + texture->Bind(slot); +} + +void Program::BindTexture(Texture& texture, int slot, std::string name) { + Use(); + + int location; + GLCall(location = glGetUniformLocation(m_id, name.c_str())); + + ASSERT(location != -1); + + GLCall(glUniform1i(location, slot)); + texture.Bind(slot); +} + +void Program::BindTextureImage(Texture& texture, int slot) { + Use(); + + int location; + GLCall(location = glGetUniformLocation(m_id, texture.GetName().c_str())); + + ASSERT(location != -1); + + GLCall(glUniform1i(location, slot)); + texture.BindImage(slot); +} + +unsigned int Program::GetId() { + return m_id; +} + +void Program::SetUniform1f(std::string name, float u) { + Use(); + + int location; + + GLCall(location = glGetUniformLocation(m_id, name.c_str())); + + ASSERT(location != -1); + + GLCall(glUniform1f(location, u)); +} + +void Program::SetUniform2f(std::string name, float u1, float u2) { + Use(); + + int location; + + GLCall(location = glGetUniformLocation(m_id, name.c_str())); + + ASSERT(location != -1); + + GLCall(glUniform2f(location, u1, u2)); +} + +void Program::SetUniform3f(std::string name, float u1, float u2, float u3) { + Use(); + + int location; + + GLCall(location = glGetUniformLocation(m_id, name.c_str())); + + ASSERT(location != -1); + + GLCall(glUniform3f(location, u1, u2, u3)); +} + +void Program::SetUniform4f(std::string name, float u1, float u2, float u3, float u4) { + Use(); + + int location; + + GLCall(location = glGetUniformLocation(m_id, name.c_str())); + + ASSERT(location != -1); + + GLCall(glUniform4f(location, u1, u2, u3, u4)); +} + +void Program::SetUniform1i(std::string name, int u) { + Use(); + + int location; + + GLCall(location = glGetUniformLocation(m_id, name.c_str())); + + ASSERT(location != -1); + + GLCall(glUniform1i(location, u)); +} + +void Program::SetUniform2i(std::string name, int u1, int u2) { + Use(); + + int location; + + GLCall(location = glGetUniformLocation(m_id, name.c_str())); + + ASSERT(location != -1); + + GLCall(glUniform2i(location, u1, u2)); +} + +void Program::SetUniform3i(std::string name, int u1, int u2, int u3) { + Use(); + + int location; + + GLCall(location = glGetUniformLocation(m_id, name.c_str())); + + ASSERT(location != -1); + + GLCall(glUniform3i(location, u1, u2, u3)); +} + +void Program::SetUniform4i(std::string name, int u1, int u2, int u3, int u4) { + Use(); + + int location; + + GLCall(location = glGetUniformLocation(m_id, name.c_str())); + + ASSERT(location != -1); + + GLCall(glUniform4i(location, u1, u2, u3, u4)); +} + +void Program::SetUniform1ui(std::string name, unsigned int u) { + Use(); + + int location; + + GLCall(location = glGetUniformLocation(m_id, name.c_str())); + + ASSERT(location != -1); + + GLCall(glUniform1ui(location, u)); +} + +void Program::SetUniform2ui(std::string name, unsigned int u1, unsigned int u2) { + Use(); + + int location; + + GLCall(location = glGetUniformLocation(m_id, name.c_str())); + + ASSERT(location != -1); + + GLCall(glUniform2ui(location, u1, u2)); +} + +void Program::SetUniform3ui(std::string name, unsigned int u1, unsigned int u2, unsigned int u3) { + Use(); + + int location; + + GLCall(location = glGetUniformLocation(m_id, name.c_str())); + + ASSERT(location != -1); + + GLCall(glUniform3ui(location, u1, u2, u3)); +} + +void Program::SetUniform4ui(std::string name, unsigned int u1, unsigned int u2, unsigned int u3, unsigned int u4) { + Use(); + + int location; + + GLCall(location = glGetUniformLocation(m_id, name.c_str())); + + ASSERT(location != -1); + + GLCall(glUniform4ui(location, u1, u2, u3, u4)); +} + +void Program::SetUniform1fv(std::string name, int count, float* u) { + Use(); + + int location; + + GLCall(location = glGetUniformLocation(m_id, name.c_str())); + + ASSERT(location != -1); + + GLCall(glUniform1fv(location, count, u)); +} + +void Program::SetUniform2fv(std::string name, int count, float* u) { + Use(); + + int location; + + GLCall(location = glGetUniformLocation(m_id, name.c_str())); + + ASSERT(location != -1); + + GLCall(glUniform2fv(location, count, u)); +} + +void Program::SetUniform3fv(std::string name, int count, float* u) { + Use(); + + int location; + + GLCall(location = glGetUniformLocation(m_id, name.c_str())); + + ASSERT(location != -1); + + GLCall(glUniform3fv(location, count, u)); +} + +void Program::SetUniform4fv(std::string name, int count, float* u) { + Use(); + + int location; + + GLCall(location = glGetUniformLocation(m_id, name.c_str())); + + ASSERT(location != -1); + + GLCall(glUniform4fv(location, count, u)); +} + +void Program::SetUniform1iv(std::string name, int count, int* u) { + Use(); + + int location; + + GLCall(location = glGetUniformLocation(m_id, name.c_str())); + + ASSERT(location != -1); + + GLCall(glUniform1iv(location, count, u)); +} + +void Program::SetUniform2iv(std::string name, int count, int* u) { + Use(); + + int location; + + GLCall(location = glGetUniformLocation(m_id, name.c_str())); + + ASSERT(location != -1); + + GLCall(glUniform2iv(location, count, u)); +} + +void Program::SetUniform3iv(std::string name, int count, int* u) { + Use(); + + int location; + + GLCall(location = glGetUniformLocation(m_id, name.c_str())); + + ASSERT(location != -1); + + GLCall(glUniform3iv(location, count, u)); +} + +void Program::SetUniform4iv(std::string name, int count, int* u) { + Use(); + + int location; + + GLCall(location = glGetUniformLocation(m_id, name.c_str())); + + ASSERT(location != -1); + + GLCall(glUniform4iv(location, count, u)); +} + +void Program::SetUniform1uiv(std::string name, int count, unsigned int* u) { + Use(); + + int location; + + GLCall(location = glGetUniformLocation(m_id, name.c_str())); + + ASSERT(location != -1); + + GLCall(glUniform1uiv(location, count, u)); +} + +void Program::SetUniform2uiv(std::string name, int count, unsigned int* u) { + Use(); + + int location; + + GLCall(location = glGetUniformLocation(m_id, name.c_str())); + + ASSERT(location != -1); + + GLCall(glUniform2uiv(location, count, u)); +} + +void Program::SetUniform3uiv(std::string name, int count, unsigned int* u) { + Use(); + + int location; + + GLCall(location = glGetUniformLocation(m_id, name.c_str())); + + ASSERT(location != -1); + + GLCall(glUniform3uiv(location, count, u)); +} + +void Program::SetUniform4uiv(std::string name, int count, unsigned int* u) { + Use(); + + int location; + + GLCall(location = glGetUniformLocation(m_id, name.c_str())); + + ASSERT(location != -1); + + GLCall(glUniform4uiv(location, count, u)); +} \ No newline at end of file diff --git a/src/Program.h b/src/Program.h new file mode 100644 index 0000000..91db932 --- /dev/null +++ b/src/Program.h @@ -0,0 +1,61 @@ +#pragma once +#include "Shader.h" +#include "Texture.h" + +class Program { +private: + unsigned int m_id = 0; + +public: + Program(); + ~Program(); + Program(unsigned int id); + Program(Shader& vertShader, Shader& fragShader); + Program(Shader& computeShader); + + Program& operator=(Program&& other); + Program(Program&& other); + Program(const Program&) = delete; + Program& operator=(const Program&) = delete; + + void Delete(); + + void Use(); + void DispatchCompute(int width, int height); + void DispatchCompute(int x); + void BindTexture(Texture& texture, int slot); + void BindTexture(Texture* texture, int slot); + void BindTexture(Texture& texture, int slot, std::string name); + void BindTextureImage(Texture& texture, int slot); + unsigned int GetId(); + + void SetUniform1f(std::string name, float u); + void SetUniform2f(std::string name, float u1, float u2); + void SetUniform3f(std::string name, float u1, float u2, float u3); + void SetUniform4f(std::string name, float u1, float u2, float u3, float u4); + + void SetUniform1i(std::string name, int u); + void SetUniform2i(std::string name, int u1, int u2); + void SetUniform3i(std::string name, int u1, int u2, int u3); + void SetUniform4i(std::string name, int u1, int u2, int u3, int u4); + + void SetUniform1ui(std::string name, unsigned int u); + void SetUniform2ui(std::string name, unsigned int u1, unsigned int u2); + void SetUniform3ui(std::string name, unsigned int u1, unsigned int u2, unsigned int u3); + void SetUniform4ui(std::string name, unsigned int u1, unsigned int u2, unsigned int u3, unsigned int u4); + + void SetUniform1fv(std::string name, int count, float* u); + void SetUniform2fv(std::string name, int count, float* u); + void SetUniform3fv(std::string name, int count, float* u); + void SetUniform4fv(std::string name, int count, float* u); + + void SetUniform1iv(std::string name, int count, int* u); + void SetUniform2iv(std::string name, int count, int* u); + void SetUniform3iv(std::string name, int count, int* u); + void SetUniform4iv(std::string name, int count, int* u); + + void SetUniform1uiv(std::string name, int count, unsigned int* u); + void SetUniform2uiv(std::string name, int count, unsigned int* u); + void SetUniform3uiv(std::string name, int count, unsigned int* u); + void SetUniform4uiv(std::string name, int count, unsigned int* u); +}; \ No newline at end of file diff --git a/src/SSBO.cpp b/src/SSBO.cpp new file mode 100644 index 0000000..1e9a3a2 --- /dev/null +++ b/src/SSBO.cpp @@ -0,0 +1,58 @@ +#include "SSBO.h" +#include "GLUtils.h" + +#include +#include + +SSBO::SSBO(int bind) : + m_id(0), m_binding(bind), m_last_size(0) +{ + GLCall(glGenBuffers(1, &m_id)); +} + +SSBO::SSBO() : + m_id(0), m_binding(0), m_last_size(0) +{ +} + +SSBO::SSBO(unsigned int i, int bind) : + m_id(i), m_binding(bind), m_last_size(0) +{} + +SSBO::~SSBO() { + Delete(); +} + +int SSBO::GetId() +{ + return m_id; +} + +SSBO::SSBO(SSBO&& other) : + m_id(other.m_id), m_binding(other.m_binding), m_last_size(other.m_last_size) +{ + other.m_id = 0; +} + +SSBO& SSBO::operator=(SSBO&& other) +{ + if (this != &other) + { + Delete(); + std::swap(m_id, other.m_id); + m_binding = other.m_binding; + m_last_size = other.m_last_size; + } + return *this; +} + +void SSBO::Delete() { + if (m_id != 0) { + GLCall(glDeleteBuffers(1, &m_id)); + } + m_id = 0; +} + +void SSBO::Bind() { + GLCall(glBindBuffer(GL_SHADER_STORAGE_BUFFER, m_id)); +} \ No newline at end of file diff --git a/src/SSBO.h b/src/SSBO.h new file mode 100644 index 0000000..967181a --- /dev/null +++ b/src/SSBO.h @@ -0,0 +1,76 @@ +#pragma once +#include +#include "GLUtils.h" + +class SSBO { +private: + unsigned int m_id; + int m_binding; + int m_last_size; +public: + + SSBO(int bind); + SSBO(); + SSBO(unsigned int i, int bind); + SSBO& operator=(SSBO&& other); + SSBO(SSBO&& other); + SSBO(const SSBO&) = delete; + SSBO& operator=(const SSBO&) = delete; + ~SSBO(); + int GetId(); + void Delete(); + void Bind(); + + template + void Set(T shader_data) { + int size = sizeof(shader_data); + + m_last_size = size; + + Bind(); + GLCall(glBufferData(GL_SHADER_STORAGE_BUFFER, size, size > 0 ? &shader_data : nullptr, GL_DYNAMIC_DRAW)); + + GLCall(glBindBufferBase(GL_SHADER_STORAGE_BUFFER, m_binding, m_id)); + } + template + void Set(std::vector* shader_data) { + int size = sizeof(T) * (int)shader_data->size(); + + m_last_size = size; + + Bind(); + GLCall(glBufferData(GL_SHADER_STORAGE_BUFFER, size, size > 0 ? shader_data->data() : nullptr, GL_DYNAMIC_DRAW)); + + GLCall(glBindBufferBase(GL_SHADER_STORAGE_BUFFER, m_binding, m_id)); + } + + + template + void Update(T shader_data) { + int size = sizeof(shader_data); + + Bind(); + if (size == m_last_size) { + GLCall(glBufferSubData(GL_SHADER_STORAGE_BUFFER, 0, size, &shader_data)); + } + else { + m_last_size = size; + GLCall(glBufferData(GL_SHADER_STORAGE_BUFFER, size, &shader_data, GL_DYNAMIC_DRAW)); + GLCall(glBindBufferBase(GL_SHADER_STORAGE_BUFFER, m_binding, m_id)); + } + } + template + void Update(std::vector* shader_data) { + int size = sizeof(T) * shader_data->size(); + + Bind(); + if (size == m_last_size) { + GLCall(glBufferSubData(GL_SHADER_STORAGE_BUFFER, 0, size, size > 0 ? shader_data->data() : nullptr)); + } + else { + m_last_size = size; + GLCall(glBufferData(GL_SHADER_STORAGE_BUFFER, size, size > 0 ? shader_data->data() : nullptr, GL_DYNAMIC_DRAW)); + GLCall(glBindBufferBase(GL_SHADER_STORAGE_BUFFER, m_binding, m_id)); + } + } +}; \ No newline at end of file diff --git a/src/Scene.cpp b/src/Scene.cpp new file mode 100644 index 0000000..0c739e5 --- /dev/null +++ b/src/Scene.cpp @@ -0,0 +1,311 @@ +#include "Scene.h" + +#include +#include +#include +#include +#include + +void IndexArray::AddVertex(int x, int y, int z) { + m_array.push_back(x - 1); + m_array.push_back(y - 1); + m_array.push_back(z - 1); + m_array.push_back(0); + m_array.push_back(0); + m_array.push_back(0); +} + +void IndexArray::AddVertex(int x, int y, int z, int tx, int ty, int tz) { + m_array.push_back(x - 1); + m_array.push_back(y - 1); + m_array.push_back(z - 1); + m_array.push_back(tx - 1); + m_array.push_back(ty - 1); + m_array.push_back(tz - 1); +} + +int IndexArray::GetSize() { + return (int)m_array.size(); +} + +int IndexArray::GetByteSize() { + return (int)m_array.size() * (int)sizeof(int); +} + +std::vector* IndexArray::GetPointer() { + return &m_array; +} + +std::vector IndexArray::GetVector() { + return m_array; +} + +void IndexArray::MoveIndices(int offset) { + int size = (int)m_array.size(); + const int block_size = 6; + for (int i = 0; i < size / block_size; i++) { + m_array[block_size *i] += offset; + m_array[block_size *i + 1] += offset; + m_array[block_size *i + 2] += offset; + } +} + +void IndexArray::MoveTextureIndices(int offset){ + int size = (int)m_array.size(); + const int block_size = 6; + for (int i = 0; i < size / block_size; i++) { + m_array[block_size * i + 3] += offset; + m_array[block_size * i + 4] += offset; + m_array[block_size * i + 5] += offset; + } +} + +void VertexArray::AddVertex(float x, float y, float z) { + m_array.push_back(x); + m_array.push_back(y); + m_array.push_back(z); +} + +int VertexArray::GetSize() { + return (int)m_array.size(); +} + +int VertexArray::GetByteSize() { + return (int)m_array.size() * (int)sizeof(float); +} + +std::vector* VertexArray::GetPointer() { + return &m_array; +} + +std::vector VertexArray::GetVector() { + return m_array; +} + +void TextureVertexArray::AddVertex(float u, float v) { + m_array.push_back(u); + m_array.push_back(v); +} + +int TextureVertexArray::GetSize() { + return (int)m_array.size(); +} + +int TextureVertexArray::GetByteSize() { + return (int)m_array.size() * (int)sizeof(float); +} + +std::vector* TextureVertexArray::GetPointer() { + return &m_array; +} + +std::vector TextureVertexArray::GetVector() { + return m_array; +} + +void SceneVertices::AddModel(Model3D* model) { + int size = model->GetIndexArrayPtr()->GetSize() / 6; + m_offsets.push_back(size + m_index_count); + + m_models.push_back(model); +} + +void SceneVertices::FillBuffers() { + int size = (int)m_models.size(); + for (int i = 0; i < size; i++) { + m_models[i]->GetIndexArrayPtr()->MoveIndices(m_vertex_count); + m_models[i]->GetIndexArrayPtr()->MoveTextureIndices(m_texture_vertex_count); + m_models[i]->SetIndexOffset(m_index_count); + + m_vertex_buffer.insert(m_vertex_buffer.end(), + m_models[i]->GetVertexArrayPtr()->GetPointer()->begin(), + m_models[i]->GetVertexArrayPtr()->GetPointer()->end()); + + m_texture_buffer.insert(m_texture_buffer.end(), + m_models[i]->GetTextureArrayPtr()->GetPointer()->begin(), + m_models[i]->GetTextureArrayPtr()->GetPointer()->end()); + + m_index_buffer.insert(m_index_buffer.end(), + m_models[i]->GetIndexArrayPtr()->GetPointer()->begin(), + m_models[i]->GetIndexArrayPtr()->GetPointer()->end()); + + m_vertex_count += m_models[i]->GetVertexArrayPtr()->GetSize() / 3; + m_texture_vertex_count += m_models[i]->GetTextureArrayPtr()->GetSize() / 2; + m_index_count += m_models[i]->GetIndexArrayPtr()->GetSize() / 6; + } +} + +std::vector* SceneVertices::GetVertexBuffer() { + return &m_vertex_buffer; +} + +std::vector* SceneVertices::GetTextureBuffer() { + return &m_texture_buffer; +} + +std::vector* SceneVertices::GetIndexBuffer() { + return &m_index_buffer; +} + +int SceneVertices::GetFaceCount() { + return m_index_count; +} + +int SceneVertices::GetTextureVertsCount() { + return m_texture_vertex_count; +} + +int SceneVertices::GetVertexCount() { + return m_vertex_count; +} + +void Model3D::Load(std::string path) { + std::fstream file(path); + + std::string line; + std::stringstream str; + + std::string prefix; + + int vertex_id = 0; + + float x, y, z; + while (std::getline(file, line)) { + str.clear(); + str.str(line); + str >> prefix; + if (prefix == "v") { + str >> x >> y >> z; + + if (m_vertex_arr.GetSize() == 0) + m_lowest_point = y; + else + m_lowest_point = std::min(m_lowest_point, y); + + m_vertex_arr.AddVertex(x, y, z); + } + else if (prefix == "vt") { + str >> x >> y; + + m_texture_arr.AddVertex(x, y); + } + else if (prefix == "f") { + int f; + int v1 = 0; + int v2 = 0; + int v3 = 0; + int v4 = 0; + + int vt1 = 0; + int vt2 = 0; + int vt3 = 0; + int vt4 = 0; + + int type_counter = 0; + int id_counter = 0; + + while (str >> f) { + + + if (type_counter == 0) { + if (id_counter == 0) { + v1 = f; + } + else if (id_counter == 1) { + v2 = f; + } + else if (id_counter == 2) { + v3 = f; + } + else if (id_counter == 3) { + v4 = f; + } + } + else if (type_counter == 1) { + if (id_counter == 0) { + vt1 = f; + } + else if (id_counter == 1) { + vt2 = f; + } + else if (id_counter == 2) { + vt3 = f; + } + else if (id_counter == 3) { + vt4 = f; + } + } + + while (str.peek() == '/') { + type_counter++; + type_counter %= 3; + str.ignore(1, '/'); + } + while (str.peek() == ' ') { + id_counter++; + type_counter = 0; + str.ignore(1, ' '); + } + } + + if (id_counter == 2) + m_index_arr.AddVertex(v1, v2, v3, vt1, vt2, vt3); + else { + m_index_arr.AddVertex(v1, v2, v3, vt1, vt2, vt3); + m_index_arr.AddVertex(v1, v3, v4, vt1, vt3, vt4); + } + } + } + // shift vertices to level the vertices to ground + const int block_size = 3; + for (int i = 0; i < m_vertex_arr.GetSize()/ block_size; i++) + (*m_vertex_arr.GetPointer())[block_size * i + 1] -= (m_lowest_point - 0.001f); +} + +void Model3D::SetId(int id) { + m_id = id; +} + +void Model3D::SetIndexOffset(int offset) { + m_index_offset = offset; +} + +int Model3D::GetIndexOffset() { + return m_index_offset; +} + +int Model3D::GetFaceCount() { + return m_index_arr.GetSize() / 6; +} + +int Model3D::GetId() { + return m_id; +} + +float Model3D::GetLowestPoint() { + return m_lowest_point; +} + +VertexArray Model3D::GetVertexArray() { + return m_vertex_arr; +} + +IndexArray Model3D::GetIndexArray() { + return m_index_arr; +} + +TextureVertexArray Model3D::GetTextureArray() { + return m_texture_arr; +} + +VertexArray* Model3D::GetVertexArrayPtr() { + return &m_vertex_arr; +} + +IndexArray* Model3D::GetIndexArrayPtr() { + return &m_index_arr; +} + +TextureVertexArray* Model3D::GetTextureArrayPtr() { + return &m_texture_arr; +} diff --git a/src/Scene.h b/src/Scene.h new file mode 100644 index 0000000..dd2984e --- /dev/null +++ b/src/Scene.h @@ -0,0 +1,93 @@ +#pragma once +#include +#include +#include +#include +#include +#include + +#include "Texture.h" + +class IndexArray { +public: + void AddVertex(int x, int y, int z, int tx, int ty, int tz); + void AddVertex(int x, int y, int z); + int GetSize(); + int GetByteSize(); + std::vector* GetPointer(); + std::vector GetVector(); + void MoveIndices(int offset); + void MoveTextureIndices(int offset); +private: + std::vector m_array; +}; + +class VertexArray { +public: + void AddVertex(float x, float y, float z); + int GetSize(); + int GetByteSize(); + std::vector* GetPointer(); + std::vector GetVector(); +private: + std::vector m_array; +}; + +class TextureVertexArray { +public: + void AddVertex(float u, float v); + int GetSize(); + int GetByteSize(); + std::vector* GetPointer(); + std::vector GetVector(); +private: + std::vector m_array; +}; + +class Model3D { +public: + void Load(std::string path); + void SetId(int id); + void SetIndexOffset(int offset); + int GetIndexOffset(); + int GetFaceCount(); + float GetLowestPoint(); + int GetId(); + VertexArray GetVertexArray(); + IndexArray GetIndexArray(); + TextureVertexArray GetTextureArray(); + VertexArray* GetVertexArrayPtr(); + IndexArray* GetIndexArrayPtr(); + TextureVertexArray* GetTextureArrayPtr(); +private: + VertexArray m_vertex_arr; + IndexArray m_index_arr; + TextureVertexArray m_texture_arr; + + int m_id = -1; + int m_index_offset = 0; + + float m_lowest_point = 0; +}; + +class SceneVertices { +public: + void AddModel(Model3D* model); + void FillBuffers(); + std::vector* GetVertexBuffer(); + std::vector* GetTextureBuffer(); + std::vector* GetIndexBuffer(); + int GetFaceCount(); + int GetTextureVertsCount(); + int GetVertexCount(); +private: + std::vector m_models; + int m_index_count = 0; + int m_vertex_count = 0; + int m_texture_vertex_count = 0; + std::vector m_offsets; + + std::vector m_vertex_buffer; + std::vector m_texture_buffer; + std::vector m_index_buffer; +}; \ No newline at end of file diff --git a/src/Shader.cpp b/src/Shader.cpp new file mode 100644 index 0000000..e50b96e --- /dev/null +++ b/src/Shader.cpp @@ -0,0 +1,169 @@ +#include "Shader.h" +#include "GLUtils.h" + +#include +#include +#include +#include +#include + +std::string vert_source = +"#version 330 core\n" +"layout(location = 0) in vec4 position;\n" +"layout(location = 1) in vec2 aTexCoord;\n" +"out vec2 TexCoord;\n" +"void main(){\n" +"gl_Position = position;\n" +"TexCoord = aTexCoord;\n" +"}"; + +std::string frag_source_tonemapped = +"#version 330 core\n" +"layout(location = 0) out vec4 color;\n" +"in vec2 TexCoord;\n" +"uniform sampler2D img_output;\n" +"void main(){\n" +"const float gamma = 2.2;\n" +"vec3 hdrColor = texture(img_output, TexCoord).xyz;\n" +"vec3 mapped = vec3(1.0) - exp(-hdrColor * 1.0);\n" +"mapped = pow(mapped, vec3(1.0 / gamma));\n" +"color = vec4(mapped, 1.0);\n" +"}"; + +std::string frag_source = +"#version 330 core\n" +"layout(location = 0) out vec4 color;\n" +"in vec2 TexCoord;\n" +"uniform sampler2D img_output;\n" +"void main(){\n" +"color = texture(img_output, TexCoord);\n" +"}"; + +Shader::Shader(std::string path, unsigned int type) { + std::string source; + + LoadShader(path, source); + + m_id = CompileShader(source, type); +} + +Shader::Shader(std::string path, unsigned int type, bool tonemapping) +{ + std::string source; + + LoadShader(path, source); + + m_id = CompileShader(source, type); +} + +Shader::Shader(std::string path) { + std::string source; + + LoadShader(path, source); + + m_id = CompileShader(source, GL_COMPUTE_SHADER); +} + +//Shader::Shader(unsigned int type) { +// switch (type) { +// case GL_VERTEX_SHADER: +// m_id = CompileShader(vert_source, type); +// break; +// case GL_FRAGMENT_SHADER: +// m_id = CompileShader(frag_source, type); +// break; +// default: +// __debugbreak(); +// break; +// } +//} +Shader::Shader(unsigned int type, bool tonemapped) { + switch (type) { + case GL_VERTEX_SHADER: + m_id = CompileShader(vert_source, type); + break; + case GL_FRAGMENT_SHADER: + if(!tonemapped) + m_id = CompileShader(frag_source, type); + else + m_id = CompileShader(frag_source_tonemapped, type); + break; + default: + __debugbreak(); + break; + } +} + +Shader::~Shader() { + Delete(); +} + +Shader::Shader(Shader&& other) : + m_id(other.m_id) +{ + other.m_id = 0; +} + +Shader& Shader::operator=(Shader&& other) +{ + if (this != &other) + { + Delete(); + std::swap(m_id, other.m_id); + } + return *this; +} + +void Shader::Delete() { + if (m_id != 0) { + GLCall(glDeleteShader(m_id)); + } + m_id = 0; +} + +int Shader::CompileShader(std::string _source, unsigned int type){ + unsigned int id = glCreateShader(type); + const char* src = _source.c_str(); + GLCall(glShaderSource(id, 1, &src, nullptr)); + m_source = _source; + GLCall(glCompileShader(id)); + + int result; + GLCall(glGetShaderiv(id, GL_COMPILE_STATUS, &result)); + if (result == GL_FALSE) { + m_compilation_success = false; + int length; + GLCall(glGetShaderiv(id, GL_INFO_LOG_LENGTH, &length)); + char* message = (char*)alloca(length * sizeof(char)); + GLCall(glGetShaderInfoLog(id, length, &length, message)); + if (type == GL_COMPUTE_SHADER) { + LOG("Compilation failed of compute shader"); + LOG(message); + } + GLCall(glDeleteShader(id)); + return 0; + } + else { + m_compilation_success = true; + if (type == GL_COMPUTE_SHADER) + LOG("Compilation successfull of compute shader"); + } + + return id; +} + +void Shader::LoadShader(std::string name, std::string& Shader){ + std::ifstream v(name); + std::stringstream buffer; + buffer << v.rdbuf(); + Shader = buffer.str(); +} + +int Shader::GetId() { + return m_id; +} + +bool Shader::Compiled() +{ + return m_compilation_success; +} diff --git a/src/Shader.h b/src/Shader.h new file mode 100644 index 0000000..6f24d4d --- /dev/null +++ b/src/Shader.h @@ -0,0 +1,28 @@ +#pragma once +#define _USE_MATH_DEFINES +#include +#include + +class Shader { +public: + Shader(std::string path); + Shader(std::string path, unsigned int type); + Shader(std::string path, unsigned int type, bool tonemapping); + Shader(unsigned int type, bool tonemapped = false); + ~Shader(); + + Shader& operator=(Shader&& other); + Shader(Shader&& other); + Shader(const Shader&) = delete; + Shader& operator=(const Shader&) = delete; + + void Delete(); + int CompileShader(std::string _source, unsigned int type); + void LoadShader(std::string name, std::string& Shader); + int GetId(); + bool Compiled(); +private: + bool m_compilation_success = false; + unsigned int m_id = 0; + std::string m_source; +}; \ No newline at end of file diff --git a/src/Texture.cpp b/src/Texture.cpp new file mode 100644 index 0000000..1323487 --- /dev/null +++ b/src/Texture.cpp @@ -0,0 +1,146 @@ +#include "Texture.h" +#include "GLUtils.h" +#include +#include + +#define STB_IMAGE_IMPLEMENTATION +#include + + +Texture::Texture(std::string name, std::string path) : + m_width(0), m_height(0), m_name(name), m_image(false), + m_bpp(0), m_id(0), m_buffer(nullptr) +{ + GLCall(glGenTextures(1, &m_id)); + + GLCall(glBindTexture(GL_TEXTURE_2D, m_id)); + GLCall(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT)); + GLCall(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT)); + GLCall(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)); + GLCall(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)); + + Load(path); +} + +Texture::Texture(std::string name, int width, int height) : + m_width(width), m_height(height), m_name(name), m_image(true), + m_bpp(0), m_id(0), m_buffer(nullptr) +{ + GLCall(glGenTextures(1, &m_id)); + + GLCall(glBindTexture(GL_TEXTURE_2D, m_id)); + GLCall(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT)); + GLCall(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT)); + GLCall(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)); + GLCall(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)); + + GLCall(glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA32F, m_width, m_height, 0, GL_RGBA, GL_FLOAT, NULL)); +} + +void Texture::Resize(int width, int height) { + m_width = width; + m_height = height; + + + GLuint boundTexture = 0; + GLCall(glGetIntegerv(GL_TEXTURE_BINDING_2D, (GLint*)&boundTexture)); + Bind(); + + GLCall(glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA32F, m_width, m_height, 0, GL_RGBA, GL_FLOAT, NULL)); + + GLCall(glBindTexture(GL_TEXTURE_2D, boundTexture)); +} + +void Texture::Load(std::string path) { + stbi_set_flip_vertically_on_load(1); + std::string extension = path.substr(path.length() - 3, 3); + if (extension != "hdr") { + m_buffer = stbi_load(path.c_str(), &m_width, &m_height, &m_bpp, 4); + + GLCall(glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA32F, m_width, m_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, m_buffer)); + } + else { + m_buffer = (unsigned char*)stbi_loadf(path.c_str(), &m_width, &m_height, &m_bpp, 4); + + GLCall(glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA32F, m_width, m_height, 0, GL_RGBA, GL_FLOAT, m_buffer)); + } + + if (m_buffer) + delete[] m_buffer; +} + +void Texture::Bind(unsigned int slot) { + GLCall(glActiveTexture(GL_TEXTURE0 + slot)); + GLCall(glBindTexture(GL_TEXTURE_2D, m_id)); +} + +void Texture::BindImage(unsigned int slot) { + GLCall(glBindImageTexture(slot, m_id, 0, GL_FALSE, 0, GL_READ_WRITE, GL_RGBA32F)); +} + +bool Texture::IsImg() { + return m_image; +} + +std::string Texture::GetName() +{ + return m_name; +} + +Texture::Texture() : + m_width(0), m_height(0), m_name(""), m_image(false), + m_bpp(0), m_id(0), m_buffer(nullptr) {} + +Texture::~Texture() { + Delete(); +} + +Texture::Texture(Texture&& other) : + m_width(other.m_width), m_height(other.m_height), m_name(other.m_name), m_image(other.m_image), + m_bpp(other.m_bpp), m_id(other.m_id), m_buffer(other.m_buffer) +{ + other.m_id = 0; +} + +Texture& Texture::operator=(Texture&& other) +{ + if (this != &other) + { + Delete(); + std::swap(m_id, other.m_id); + std::swap(m_width, other.m_width); + std::swap(m_height, other.m_height); + std::swap(m_name, other.m_name); + std::swap(m_image, other.m_image); + std::swap(m_bpp, other.m_bpp); + std::swap(m_buffer, other.m_buffer); + } + return *this; +} + +void Texture::Delete() { + if (m_id != 0) { + Unbind(); + GLCall(glDeleteTextures(1, &m_id)); + } +} + +void Texture::Bind() { + GLCall(glBindTexture(GL_TEXTURE_2D, m_id)); +} + +void Texture::Unbind() { + GLCall(glBindTexture(GL_TEXTURE_2D, 0)); +} + +unsigned int Texture::GetId() { + return m_id; +} + +int Texture::GetWidth() { + return m_width; +} + +int Texture::GetHeight() { + return m_height; +} \ No newline at end of file diff --git a/src/Texture.h b/src/Texture.h new file mode 100644 index 0000000..a309401 --- /dev/null +++ b/src/Texture.h @@ -0,0 +1,39 @@ +#pragma once +#define _USE_MATH_DEFINES +#include +#include +#include +#include + +class Texture { +private: + unsigned int m_id; + std::string m_name; + bool m_image; + unsigned char* m_buffer; + int m_width; + int m_height; + int m_bpp; + +public: + Texture(std::string name, std::string path); + Texture(std::string name, int width, int height); + void Resize(int width, int height); + Texture(); + ~Texture(); + Texture& operator=(Texture&& other); + Texture(Texture&& other); + Texture(const Texture&) = delete; + Texture& operator=(const Texture&) = delete; + void Load(std::string path); + void Bind(unsigned int slot); + void BindImage(unsigned int slot); + void Bind(); + void Unbind(); + void Delete(); + unsigned int GetId(); + int GetWidth(); + int GetHeight(); + bool IsImg(); + std::string GetName(); +}; \ No newline at end of file diff --git a/src/Timer.cpp b/src/Timer.cpp new file mode 100644 index 0000000..c0e07af --- /dev/null +++ b/src/Timer.cpp @@ -0,0 +1,37 @@ +#include "Timer.h" + +Timer::Timer() { + m_start = std::chrono::high_resolution_clock::now(); + m_s = std::chrono::microseconds(0); + m_e = std::chrono::microseconds(0); + m_duration = std::chrono::microseconds(0); +} +void Timer::Start() { + m_start = std::chrono::high_resolution_clock::now(); +} +void Timer::Stop() { + m_end = std::chrono::high_resolution_clock::now(); +} +double Timer::TimeSinceLast() { + m_end = std::chrono::high_resolution_clock::now(); + m_s = std::chrono::time_point_cast(m_start).time_since_epoch(); + m_e = std::chrono::time_point_cast(m_end).time_since_epoch(); + m_start = std::chrono::high_resolution_clock::now(); + m_duration = m_e - m_s; + return (double)m_duration.count(); +} +double Timer::GetMicro() { + return Timer::TimeSinceLast(); +} +double Timer::GetMilis() { + return Timer::TimeSinceLast() * 0.001; +} +double Timer::GetSec() { + return Timer::TimeSinceLast() * 0.000001; +} +double Timer::TimeAfterStop() { + m_s = std::chrono::time_point_cast(m_start).time_since_epoch(); + m_e = std::chrono::time_point_cast(m_end).time_since_epoch(); + m_duration = m_e - m_s; + return m_duration.count() * 0.001; +} \ No newline at end of file diff --git a/src/Timer.h b/src/Timer.h new file mode 100644 index 0000000..d2eb6e4 --- /dev/null +++ b/src/Timer.h @@ -0,0 +1,24 @@ +#pragma once +#define _USE_MATH_DEFINES +#include +#include +#include +#include +#include + +class Timer { +public: + Timer(); + + void Stop(); + void Start(); + double TimeSinceLast(); + double TimeAfterStop(); + double GetMicro(); + double GetMilis(); + double GetSec(); +private: + std::chrono::time_point m_start, m_end; + std::chrono::microseconds m_s, m_e; + std::chrono::microseconds m_duration; +}; \ No newline at end of file diff --git a/src/UBO.cpp b/src/UBO.cpp new file mode 100644 index 0000000..79e3751 --- /dev/null +++ b/src/UBO.cpp @@ -0,0 +1,56 @@ +#include "UBO.h" +#include "GLUtils.h" + +#include +#include + +UBO::UBO(int bind) : + m_id(0), m_binding(bind), m_last_size(0) +{ + GLCall(glGenBuffers(1, &m_id)); +} + +UBO::UBO() : + m_id(0), m_binding(0), m_last_size(0) +{ +} + +UBO::~UBO() { + Delete(); +} + +int UBO::GetId() +{ + return m_id; +} + +UBO::UBO(UBO&& other) : + m_id(other.m_id), m_binding(other.m_binding), m_last_size(other.m_last_size) +{ + other.m_id = 0; +} + +UBO& UBO::operator=(UBO&& other) +{ + if (this != &other) + { + Delete(); + std::swap(m_id,other.m_id); + m_binding = other.m_binding; + m_last_size = other.m_last_size; + } + return *this; +} + +void UBO::Delete() { + if (m_id != 0) { + GLCall(glDeleteBuffers(1, &m_id)); + } + m_id = 0; +} + +void UBO::Bind() { + GLCall(glBindBuffer(GL_UNIFORM_BUFFER, m_id)); +} + + diff --git a/src/UBO.h b/src/UBO.h new file mode 100644 index 0000000..5e96e64 --- /dev/null +++ b/src/UBO.h @@ -0,0 +1,76 @@ +#pragma once +#include + +#include "GLUtils.h" + +class UBO { +private: + unsigned int m_id = 0; + int m_binding = 0; + size_t m_last_size = 0; +public: + + UBO(int bind); + UBO(); + UBO& operator=(UBO&& other); + UBO(UBO&& other); + UBO(const UBO&) = delete; + UBO& operator=(const UBO&) = delete; + ~UBO(); + int GetId(); + void Delete(); + void Bind(); + + template + void Set(T* shader_data) { + size_t size = sizeof(*shader_data); + + m_last_size = size; + + Bind(); + GLCall(glBufferData(GL_UNIFORM_BUFFER, size, size > 0 ? shader_data : nullptr, GL_DYNAMIC_DRAW)); + + GLCall(glBindBufferBase(GL_UNIFORM_BUFFER, m_binding, m_id)); + } + template + void Set(std::vector* shader_data) { + size_t size = sizeof(T) * shader_data->size(); + + m_last_size = size; + + Bind(); + GLCall(glBufferData(GL_UNIFORM_BUFFER, size, size > 0 ? shader_data->data() : nullptr, GL_DYNAMIC_DRAW)); + + GLCall(glBindBufferBase(GL_UNIFORM_BUFFER, m_binding, m_id)); + } + + + template + void Update(T* shader_data) { + size_t size = sizeof(*shader_data); + + Bind(); + if (size == m_last_size) { + GLCall(glBufferSubData(GL_UNIFORM_BUFFER, 0, size, shader_data)); + } + else { + m_last_size = size; + GLCall(glBufferData(GL_UNIFORM_BUFFER, size, shader_data, GL_DYNAMIC_DRAW)); + GLCall(glBindBufferBase(GL_UNIFORM_BUFFER, m_binding, m_id)); + } + } + template + void Update(std::vector* shader_data) { + size_t size = sizeof(T) * shader_data->size(); + + Bind(); + if (size == m_last_size) { + GLCall(glBufferSubData(GL_UNIFORM_BUFFER, 0, size, size > 0 ? shader_data->data() : nullptr)); + } + else { + m_last_size = size; + GLCall(glBufferData(GL_UNIFORM_BUFFER, size, size > 0 ? shader_data->data() : nullptr, GL_DYNAMIC_DRAW)); + GLCall(glBindBufferBase(GL_UNIFORM_BUFFER, m_binding, m_id)); + } + } +}; \ No newline at end of file