diff --git a/AppFiles/build/freetype6.dll b/AppFiles/build/freetype6.dll deleted file mode 100644 index e35edc6..0000000 Binary files a/AppFiles/build/freetype6.dll and /dev/null differ diff --git a/AppFiles/build/glew32.dll b/AppFiles/build/glew32.dll deleted file mode 100644 index 40b71cf..0000000 Binary files a/AppFiles/build/glew32.dll and /dev/null differ diff --git a/AppFiles/build/libengine.dll b/AppFiles/build/libengine.dll deleted file mode 100644 index 32b32ed..0000000 Binary files a/AppFiles/build/libengine.dll and /dev/null differ diff --git a/AppFiles/build/libgcc_s_dw2-1.dll b/AppFiles/build/libgcc_s_dw2-1.dll deleted file mode 100644 index a98ce46..0000000 Binary files a/AppFiles/build/libgcc_s_dw2-1.dll and /dev/null differ diff --git a/AppFiles/build/libstb_image.dll b/AppFiles/build/libstb_image.dll deleted file mode 100644 index f3dcb65..0000000 Binary files a/AppFiles/build/libstb_image.dll and /dev/null differ diff --git a/AppFiles/build/libstdc++-6.dll b/AppFiles/build/libstdc++-6.dll deleted file mode 100644 index d2f0815..0000000 Binary files a/AppFiles/build/libstdc++-6.dll and /dev/null differ diff --git a/AppFiles/build/terrain.exe b/AppFiles/build/terrain.exe deleted file mode 100644 index 42452c1..0000000 Binary files a/AppFiles/build/terrain.exe and /dev/null differ diff --git a/AppFiles/build/zlib1.dll b/AppFiles/build/zlib1.dll deleted file mode 100644 index bb11610..0000000 Binary files a/AppFiles/build/zlib1.dll and /dev/null differ diff --git a/src/engine/ENGINE PLACE HERE.txt b/src/engine/ENGINE PLACE HERE.txt deleted file mode 100644 index e69de29..0000000 diff --git a/src/engine/Engine.cpp b/src/engine/Engine.cpp new file mode 100644 index 0000000..0efe616 --- /dev/null +++ b/src/engine/Engine.cpp @@ -0,0 +1,65 @@ +/* Copyright (c) 2020 by Stan Fortoński */ + +#include "Engine.hpp" + +namespace Engine +{ + void Engine::initGLFW() const + { + glfwInit(); + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, Config::get().getMajorVersion()); + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, Config::get().getMinorVersion()); + glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); + + #ifdef __APPLE__ + glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); + #endif + } + + void Engine::initGLEW() const + { + glewExperimental = GL_TRUE; + if (glewInit() != GLEW_OK) + throw std::runtime_error("Can\'t init GLEW"); + } + + void Engine::initDefaultOptionsGL() const + { + if (Config::get().getSamples() > 0) + glEnable(GL_MULTISAMPLE); + glEnable(GL_DEPTH_TEST); + glDepthFunc(GL_LESS); + glEnable(GL_CULL_FACE); + glEnable(GL_BLEND); + glEnable(GL_CLIP_DISTANCE0); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + } + + void Engine::initDeltaTime() + { + float currentTime = glfwGetTime(); + deltaTime = currentTime - lastTime; + lastTime = currentTime; + } + + void Engine::calcFPS() + { + float currentTime = glfwGetTime(); + if ((currentTime - lastTimeFromShow) > 1) + { + actualFPS = std::to_string(frames)+" FPS"; + frames = 0; + lastTimeFromShow = currentTime; + } + else ++frames; + } + + #if DEBUG_ENGINE == 1 + void Engine::showErrors() + { + GLenum err; + while ((err = glGetError()) != GL_NO_ERROR) + std::cout<<"GL error code: "< +#include +#include +#include "support/Singleton.hpp" +#include "Config.hpp" + +namespace Engine +{ + class Engine: public Singleton + { + friend class Singleton; + float lastTimeFromShow = 0; + unsigned frames = 0; + float lastTime = 0; + float deltaTime = 0; + std::string actualFPS; + + Engine(){;} + + public: + void initGLFW() const; + void initGLEW() const; + void initDefaultOptionsGL() const; + void initDeltaTime(); + void calcFPS(); + + #if DEBUG_ENGINE == 1 + void showErrors(); + #endif + + float getDeltaTime() const{return deltaTime;} + std::string getFPS() const{return actualFPS;} + }; +} +#endif diff --git a/src/engine/base/Program.cpp b/src/engine/base/Program.cpp new file mode 100644 index 0000000..0bc7af6 --- /dev/null +++ b/src/engine/base/Program.cpp @@ -0,0 +1,158 @@ +/* Copyright (c) 2019 by Stan Fortoński */ + +#include "Program.hpp" + +namespace Engine +{ + Program::Program() + { + amount = new unsigned; + *amount = 1; + } + + Program::Program(const Shader & vertex, const Shader & fragment) + { + programId = glCreateProgram(); + glAttachShader(programId, vertex.getId()); + glAttachShader(programId, fragment.getId()); + link(); + vertex.clear(); + fragment.clear(); + amount = new unsigned; + *amount = 1; + } + + Program::Program(const std::string & vertFileName, const std::string & fragFileName) + { + programId = glCreateProgram(); + Shader vertex = Shader::createVertexShader(vertFileName); + Shader fragment = Shader::createFragmentShader(fragFileName); + glAttachShader(programId, vertex.getId()); + glAttachShader(programId, fragment.getId()); + link(); + vertex.clear(); + fragment.clear(); + amount = new unsigned; + *amount = 1; + } + + Program::Program(const Program & program) + { + swap(program); + } + + Program & Program::operator=(const Program & program) + { + clear(); + swap(program); + return *this; + } + + void Program::swap(const Program & program) + { + programId = program.programId; + amount = program.amount; + *amount = *amount + 1; + } + + void Program::clear() + { + *amount = *amount - 1; + if (*amount == 0) + { + delete amount; + if (programId != 0) + glDeleteProgram(programId); + } + } + + Program::~Program() + { + clear(); + } + + std::string Program::getLinkMessageErrorAndClear() const + { + int length; + glGetProgramiv(programId, GL_INFO_LOG_LENGTH, &length); + char* message = new char[length]; + + glGetProgramInfoLog(programId, length, &length, message); + glDeleteProgram(programId); + + std::string finalMess = message; + delete [] message; + return finalMess; + } + + void Program::create() + { + if (programId != 0) + throw std::runtime_error("Can't create exists program"); + programId = glCreateProgram(); + } + + void Program::attachShader(const Shader & shader) const + { + if (programId == 0) + throw std::runtime_error("Can't attach shader to empty program"); + glAttachShader(programId, shader.getId()); + shader.clear(); + } + + void Program::link() const + { + if (programId == 0) + throw std::runtime_error("Can't link empty program"); + glLinkProgram(programId); + int success; + glGetProgramiv(programId, GL_LINK_STATUS, &success); + if (!success) + throw std::runtime_error(getLinkMessageErrorAndClear()); + } + + unsigned Program::getUniformId(const char * name) const + { + return glGetUniformLocation(programId, name); + } + + void Program::setInt(const char * name, int i) const + { + glUniform1i(getUniformId(name), i); + } + + void Program::setFloat(const char * name, float f) const + { + glUniform1f(getUniformId(name), f); + } + + void Program::setVec2(const char * name, const glm::vec2 & vec) const + { + glUniform2fv(getUniformId(name), 1, glm::value_ptr(vec)); + } + + void Program::setVec3(const char * name, const glm::vec3 & vec) const + { + glUniform3fv(getUniformId(name), 1, glm::value_ptr(vec)); + } + + void Program::setVec4(const char * name, const glm::vec4 & vec) const + { + glUniform4fv(getUniformId(name), 1, glm::value_ptr(vec)); + } + + void Program::setMat2(const char * name, const glm::mat2 & mat) const + { + glUniformMatrix2fv(getUniformId(name), 1, GL_FALSE, glm::value_ptr(mat)); + } + + void Program::setMat3(const char * name, const glm::mat3 & mat) const + { + glUniformMatrix3fv(getUniformId(name), 1, GL_FALSE, glm::value_ptr(mat)); + } + + void Program::setMat4(const char * name, const glm::mat4 & mat) const + { + glUniformMatrix4fv(getUniformId(name), 1, GL_FALSE, glm::value_ptr(mat)); + } +} diff --git a/src/engine/base/Program.hpp b/src/engine/base/Program.hpp new file mode 100644 index 0000000..03172f5 --- /dev/null +++ b/src/engine/base/Program.hpp @@ -0,0 +1,49 @@ +/* Copyright (c) 2019 by Stan Fortoński */ + +#ifndef PROGRAM_HPP +#define PROGRAM_HPP 1 +#include "Shader.hpp" +#include +#include +#include + +namespace Engine +{ + class Program + { + unsigned programId = 0; + unsigned * amount; + + std::string getLinkMessageErrorAndClear() const; + unsigned getUniformId(const char * name) const; + + void swap(const Program & program); + void clear(); + + public: + Program(); + Program(const Program & program); + Program & operator=(const Program & program); + Program(const Shader & vertex, const Shader & fragment); + Program(const std::string & vertFileName, const std::string & fragFileName); + ~Program(); + + void create(); + void link() const; + void attachShader(const Shader & shader) const; + + void use() const {glUseProgram(programId);} + + void setInt(const char * name, int i) const; + void setFloat(const char * name, float f) const; + void setVec2(const char * name, const glm::vec2 & vec) const; + void setVec3(const char * name, const glm::vec3 & vec) const; + void setVec4(const char * name, const glm::vec4 & vec) const; + void setMat2(const char * name, const glm::mat2 & mat) const; + void setMat3(const char * name, const glm::mat3 & mat) const; + void setMat4(const char * name, const glm::mat4 & mat) const; + + unsigned getId() const {return programId;} + }; +} +#endif diff --git a/src/engine/base/Shader.cpp b/src/engine/base/Shader.cpp new file mode 100644 index 0000000..513b5ac --- /dev/null +++ b/src/engine/base/Shader.cpp @@ -0,0 +1,48 @@ +/* Copyright (c) 2019 by Stan Fortoński */ + +#include "Shader.hpp" + +namespace Engine +{ + Shader::Shader(const std::string & fileName, GLenum t): type(t) + { + std::string source = getSource(fileName); + const char* data = source.c_str(); + shaderId = glCreateShader(type); + glShaderSource(shaderId, 1, &data, nullptr); + glCompileShader(shaderId); + + int success; + glGetShaderiv(shaderId, GL_COMPILE_STATUS, &success); + if (!success) + throw std::runtime_error(getCompileMessageErrorAndClear()); + } + + std::string Shader::getSource(const std::string & fileName) const + { + std::ifstream file(fileName, std::ios::binary); + if (!file.is_open()) + throw std::runtime_error("Can\'t open shader file: "+fileName+"."); + + std::stringstream stream; + stream< +#include +#include +#include +#include + +namespace Engine +{ + class Shader + { + unsigned shaderId; + GLenum type; + + std::string getSource(const std::string & fileName) const; + std::string getCompileMessageErrorAndClear() const; + + Shader(const std::string & fileName, GLenum t); + public: + static Shader createVertexShader(const std::string & fileName) + { + return Shader(fileName, GL_VERTEX_SHADER); + } + + static Shader createFragmentShader(const std::string & fileName) + { + return Shader(fileName, GL_FRAGMENT_SHADER); + } + + static Shader createGeometryShader(const std::string & fileName) + { + return Shader(fileName, GL_GEOMETRY_SHADER); + } + + static Shader createTessalationControlShader(const std::string & fileName) + { + return Shader(fileName, GL_TESS_CONTROL_SHADER); + } + + static Shader createTessalationEvaluationShader(const std::string & fileName) + { + return Shader(fileName, GL_TESS_EVALUATION_SHADER); + } + + static Shader createComputeShader(const std::string & fileName) + { + return Shader(fileName, GL_COMPUTE_SHADER); + } + + void clear() const{glDeleteShader(shaderId);} + + unsigned getId() const {return shaderId;} + GLenum getType() const {return type;} + }; +} +#endif diff --git a/src/engine/base/ShadersManager.hpp b/src/engine/base/ShadersManager.hpp new file mode 100644 index 0000000..c19645b --- /dev/null +++ b/src/engine/base/ShadersManager.hpp @@ -0,0 +1,55 @@ +/* Copyright (c) 2020 by Stan Fortoński */ + +#ifndef SHADERS_MANAGER_HPP +#define SHADERS_MANAGER_HPP 1 +#include "Program.hpp" + +namespace Engine +{ + class ShadersManager + { + Program object; + Program anim; + Program depth; + Program depthAnim; + Program depthTerrain; + Program hdr; + Program blur; + Program particle; + Program skybox; + Program terrain; + Program font; + Program postprocess; + Program water; + + public: + void setObjectProgram(const Program & prog){object = prog;} + void setAnimProgram(const Program & prog){anim = prog;} + void setDepthProgram(const Program & prog){depth = prog;} + void setAnimDepthProgram(const Program & prog){depthAnim = prog;} + void setTerrainDepthProgram(const Program & prog){depthTerrain = prog;} + void setHDRProgram(const Program & prog){hdr = prog;} + void setPostProcessProgram(const Program & prog){postprocess = prog;} + void setBlurProgram(const Program & prog){blur = prog;} + void setParticleProgram(const Program & prog){particle = prog;} + void setSkyboxProgram(const Program & prog){skybox = prog;} + void setTerrainProgram(const Program & prog){terrain = prog;} + void setFontProgram(const Program & prog){font = prog;} + void setWaterProgram(const Program & prog){water = prog;} + + Program & getObjectProgram(){return object;} + Program & getAnimProgram(){return anim;} + Program & getDepthProgram(){return depth;} + Program & getAnimDepthProgram(){return depthAnim;} + Program & getTerrainDepthProgram(){return depthTerrain;} + Program & getHDRProgram(){return hdr;} + Program & getPostProcessProgram(){return postprocess;} + Program & getBlurProgram(){return blur;} + Program & getParticleProgram(){return particle;} + Program & getSkyboxProgram(){return skybox;} + Program & getTerrainProgram(){return terrain;} + Program & getFontProgram(){return font;} + Program & getWaterProgram(){return water;} + }; +} +#endif diff --git a/src/engine/base/Texture.cpp b/src/engine/base/Texture.cpp new file mode 100644 index 0000000..1ad68d4 --- /dev/null +++ b/src/engine/base/Texture.cpp @@ -0,0 +1,97 @@ +/* Copyright (c) 2020 by Stan Fortoński */ + +#include "Texture.hpp" + +namespace Engine +{ + Texture::Texture() + { + init(); + } + + Texture::Texture(const std::string & path, GLenum type) + { + init(); + load(path, type); + } + + Texture::Texture(const std::vector & paths) + { + init(); + load(paths); + } + + void Texture::init() + { + amount = new unsigned; + *amount = 1; + } + + Texture::Texture(const Texture & tex) + { + swap(tex); + } + + Texture & Texture::operator=(const Texture & tex) + { + clear(); + swap(tex); + return *this; + } + + void Texture::swap(const Texture & tex) + { + id = tex.id; + amount = tex.amount; + *amount = *amount + 1; + } + + void Texture::clear() + { + *amount = *amount - 1; + if (*amount == 0) + { + delete amount; + remove(); + } + } + + void Texture::remove() + { + glDeleteTextures(1, &id); + id = 0; + } + + Texture::~Texture() + { + clear(); + } + + void Texture::load(const std::string & path, GLenum type) + { + create(); + TextureLoader::loadTexture(id, path, type); + } + + void Texture::load(const std::vector & paths) + { + create(); + TextureLoader::loadCubeMapTexture(id, paths); + } + + void Texture::create() + { + if (isCreated()) + throw std::runtime_error("Error: Cannot create existing Texture"); + + glGenTextures(1, &id); + } + + void Texture::bind(GLenum type) const + { + if (isNotCreated()) + throw std::runtime_error("Error: Cannot bind not existing Texture"); + + glBindTexture(type, id); + } +}; diff --git a/src/engine/base/Texture.hpp b/src/engine/base/Texture.hpp new file mode 100644 index 0000000..d205733 --- /dev/null +++ b/src/engine/base/Texture.hpp @@ -0,0 +1,43 @@ +/* Copyright (c) 2020 by Stan Fortoński */ + +#ifndef TEXTURE_HPP +#define TEXTURE_HPP 1 +#include "../support/TextureLoader.hpp" + +namespace Engine +{ + class Texture + { + unsigned id = 0; + unsigned * amount; + + void init(); + void swap(const Texture & tex); + void clear(); + + public: + Texture(); + Texture(const std::string & path, GLenum type); + Texture(const std::vector & paths); + Texture(const Texture & tex); + Texture & operator=(const Texture & tex); + virtual ~Texture(); + + void create(); + void remove(); + + void bind(GLenum type) const; + + void load(const std::string & path, GLenum type); + void load(const std::vector & paths); + + unsigned getId() const{return id;} + + static void active(const unsigned & n = 0){glActiveTexture(GL_TEXTURE0 + n);} + static void unbind(GLenum type){glBindTexture(type, 0);} + + bool isNotCreated() const{return id == 0;} + bool isCreated() const{return id != 0;} + }; +} +#endif diff --git a/src/engine/buffer/CubeDepthBuffer.cpp b/src/engine/buffer/CubeDepthBuffer.cpp new file mode 100644 index 0000000..04e95c2 --- /dev/null +++ b/src/engine/buffer/CubeDepthBuffer.cpp @@ -0,0 +1,30 @@ +/* Copyright (c) 2020 by Stan Fortoński */ + +#include "CubeDepthBuffer.hpp" + +namespace Engine +{ + void CubeDepthBuffer::create(const unsigned & width, const unsigned & height) + { + if (isNotCreated()) + { + Texture::create(); + bind(GL_TEXTURE_CUBE_MAP); + for (unsigned i = 0; i < 6; ++i) + glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, GL_DEPTH_COMPONENT32, width, height, 0, GL_DEPTH_COMPONENT, GL_FLOAT, nullptr); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_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); + Texture::unbind(GL_TEXTURE_CUBE_MAP); + } + } + + void CubeDepthBuffer::attach() const + { + glFramebufferTexture(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, getId(), 0); + glDrawBuffer(GL_NONE); + glReadBuffer(GL_NONE); + } +} diff --git a/src/engine/buffer/CubeDepthBuffer.hpp b/src/engine/buffer/CubeDepthBuffer.hpp new file mode 100644 index 0000000..f4d5a50 --- /dev/null +++ b/src/engine/buffer/CubeDepthBuffer.hpp @@ -0,0 +1,16 @@ +/* Copyright (c) 2020 by Stan Fortoński */ + +#ifndef CUBE_DEPTH_BUFFER_HPP +#define CUBE_DEPTH_BUFFER_HPP 1 +#include "DepthBuffer.hpp" + +namespace Engine +{ + class CubeDepthBuffer: public DepthBuffer + { + public: + virtual void create(const unsigned & width, const unsigned & height); + virtual void attach() const; + }; +} +#endif diff --git a/src/engine/buffer/DepthBuffer.cpp b/src/engine/buffer/DepthBuffer.cpp new file mode 100644 index 0000000..174505d --- /dev/null +++ b/src/engine/buffer/DepthBuffer.cpp @@ -0,0 +1,29 @@ +/* Copyright (c) 2020 by Stan Fortoński */ + +#include "DepthBuffer.hpp" + +namespace Engine +{ + void DepthBuffer::create(const unsigned & width, const unsigned & height) + { + static float borderColor[] = {1.0f, 1.0f, 1.0f, 1.0f}; + + if (isNotCreated()) + { + Texture::create(); + bind(GL_TEXTURE_2D); + glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT32, width, height, 0, GL_DEPTH_COMPONENT, GL_FLOAT, nullptr); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER); + glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, borderColor); + Texture::unbind(GL_TEXTURE_2D); + } + } + + void DepthBuffer::attach() const + { + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, getId(), 0); + } +} diff --git a/src/engine/buffer/DepthBuffer.hpp b/src/engine/buffer/DepthBuffer.hpp new file mode 100644 index 0000000..1b1120f --- /dev/null +++ b/src/engine/buffer/DepthBuffer.hpp @@ -0,0 +1,18 @@ +/* Copyright (c) 2020 by Stan Fortoński */ + +#ifndef DEPTH_BUFFER_HPP +#define DEPTH_BUFFER_HPP 1 +#include +#include "../base/Texture.hpp" + +namespace Engine +{ + class DepthBuffer: public Texture + { + public: + void create(){;} + virtual void create(const unsigned & width, const unsigned & height); + virtual void attach() const; + }; +} +#endif diff --git a/src/engine/buffer/FrameBuffer.cpp b/src/engine/buffer/FrameBuffer.cpp new file mode 100644 index 0000000..2e0362f --- /dev/null +++ b/src/engine/buffer/FrameBuffer.cpp @@ -0,0 +1,112 @@ +/* Copyright (c) 2020 by Stan Fortoński */ + +#include "FrameBuffer.hpp" + +namespace Engine +{ + FrameBuffer::FrameBuffer() + { + amount = new unsigned; + *amount = 1; + } + + FrameBuffer::FrameBuffer(const FrameBuffer & fb) + { + swap(fb); + } + + FrameBuffer & FrameBuffer::operator=(const FrameBuffer & fb) + { + clear(); + swap(fb); + return *this; + } + + void FrameBuffer::swap(const FrameBuffer & fb) + { + FBO = fb.FBO; + colorTextures = fb.colorTextures; + renderBuffer = fb.renderBuffer; + depthBuffer = fb.depthBuffer; + amount = fb.amount; + *amount = *amount + 1; + } + + FrameBuffer::~FrameBuffer() + { + clear(); + } + + void FrameBuffer::clear() + { + *amount = *amount - 1; + if (*amount == 0) + { + delete amount; + removeBuffers(); + } + } + + void FrameBuffer::removeBuffers() + { + colorTextures.clear(); + if (renderBuffer != nullptr) + renderBuffer->remove(); + if (depthBuffer != nullptr) + depthBuffer->remove(); + glDeleteFramebuffers(1, &FBO); + FBO = 0; + } + + void FrameBuffer::generate(const unsigned & width, const unsigned & height, const unsigned & amount) + { + if (FBO != 0) + throw std::runtime_error("Framebuffer was generated previously"); + + glGenFramebuffers(1, &FBO); + glBindFramebuffer(GL_FRAMEBUFFER, FBO); + + if (amount > 0) + { + colorTextures.resize(amount); + std::fill(colorTextures.begin(), colorTextures.end(), Texture()); + makeColorTextureBuffer(width, height); + } + + if (depthBuffer != nullptr) + { + depthBuffer->create(width, height); + depthBuffer->attach(); + } + + if (renderBuffer != nullptr) + { + renderBuffer->create(width, height); + renderBuffer->attach(); + } + + if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) + throw std::runtime_error("Can't generate Framebuffer"); + + glBindFramebuffer(GL_FRAMEBUFFER, 0); + } + + void FrameBuffer::makeColorTextureBuffer(const unsigned & width, const unsigned & height) + { + const unsigned ARRSIZE = colorTextures.size(); + GLenum attachments[ARRSIZE]; + for (unsigned i = 0; i < ARRSIZE; ++i) + { + attachments[i] = GL_COLOR_ATTACHMENT0 + i; + + colorTextures[i].create(); + colorTextures[i].bind(GL_TEXTURE_2D); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB16F, width, height, 0, GL_RGBA, GL_FLOAT, nullptr); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glFramebufferTexture2D(GL_FRAMEBUFFER, attachments[i], GL_TEXTURE_2D, colorTextures[i].getId(), 0); + } + glDrawBuffers(ARRSIZE, attachments); + Texture::unbind(GL_TEXTURE_2D); + } +} diff --git a/src/engine/buffer/FrameBuffer.hpp b/src/engine/buffer/FrameBuffer.hpp new file mode 100644 index 0000000..ff86080 --- /dev/null +++ b/src/engine/buffer/FrameBuffer.hpp @@ -0,0 +1,50 @@ +/* Copyright (c) 2020 by Stan Fortoński */ + +#ifndef FRAME_BUFFER_HPP +#define FRAME_BUFFER_HPP 1 +#include +#include +#include +#include +#include "RenderBuffer.hpp" +#include "DepthBuffer.hpp" +#include "../base/Texture.hpp" + +namespace Engine +{ + class FrameBuffer + { + void clear(); + void swap(const FrameBuffer & fb); + + protected: + unsigned FBO = 0; + std::vector colorTextures; + RenderBuffer * renderBuffer = nullptr; + DepthBuffer * depthBuffer = nullptr; + unsigned * amount; + + virtual void makeColorTextureBuffer(const unsigned & width, const unsigned & height); + + public: + FrameBuffer(); + FrameBuffer(const FrameBuffer & fb); + FrameBuffer & operator=(const FrameBuffer & fb); + virtual ~FrameBuffer(); + + virtual void generate(const unsigned & width, const unsigned & height, const unsigned & amount = 1); + void removeBuffers(); + + void setRenderBuffer(RenderBuffer & buffer){renderBuffer = &buffer;} + void setDepthBuffer(DepthBuffer & buffer){depthBuffer = &buffer;} + + unsigned getFBO() const{return FBO;} + const std::vector & getTextures() const{return colorTextures;} + + void bind() const{glBindFramebuffer(GL_FRAMEBUFFER, FBO);} + void bind(GLenum type) const{glBindFramebuffer(type, FBO);} + static void unbind(GLenum type){glBindFramebuffer(type, 0);} + static void unbind(){glBindFramebuffer(GL_FRAMEBUFFER, 0);} + }; +} +#endif diff --git a/src/engine/buffer/MultisampledFrameBuffer.cpp b/src/engine/buffer/MultisampledFrameBuffer.cpp new file mode 100644 index 0000000..32531e8 --- /dev/null +++ b/src/engine/buffer/MultisampledFrameBuffer.cpp @@ -0,0 +1,23 @@ +/* Copyright (c) 2020 by Stan Fortoński */ + +#include "MultisampledFrameBuffer.hpp" + +namespace Engine +{ + void MultisampledFrameBuffer::makeColorTextureBuffer(const unsigned & width, const unsigned & height) + { + const unsigned ARRSIZE = colorTextures.size(); + GLenum attachments[ARRSIZE]; + + for (unsigned i = 0; i < ARRSIZE; ++i) + { + attachments[i] = GL_COLOR_ATTACHMENT0 + i; + colorTextures[i].create(); + colorTextures[i].bind(GL_TEXTURE_2D_MULTISAMPLE); + glTexImage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, Config::get().getSamples(), GL_RGB16F, width, height, GL_TRUE); + glFramebufferTexture2D(GL_FRAMEBUFFER, attachments[i], GL_TEXTURE_2D_MULTISAMPLE, colorTextures[i].getId(), 0); + } + glDrawBuffers(ARRSIZE, attachments); + Texture::unbind(GL_TEXTURE_2D_MULTISAMPLE); + } +} diff --git a/src/engine/buffer/MultisampledFrameBuffer.hpp b/src/engine/buffer/MultisampledFrameBuffer.hpp new file mode 100644 index 0000000..af7d838 --- /dev/null +++ b/src/engine/buffer/MultisampledFrameBuffer.hpp @@ -0,0 +1,16 @@ +/* Copyright (c) 2020 by Stan Fortoński */ + +#ifndef MULTISAMPLED_FRAME_BUFFER_HPP +#define MULTISAMPLED_FRAME_BUFFER_HPP 1 +#include "FrameBuffer.hpp" +#include "../Config.hpp" + +namespace Engine +{ + class MultisampledFrameBuffer: public FrameBuffer + { + protected: + virtual void makeColorTextureBuffer(const unsigned & width, const unsigned & height); + }; +} +#endif diff --git a/src/engine/buffer/MultisampledRenderBuffer.cpp b/src/engine/buffer/MultisampledRenderBuffer.cpp new file mode 100644 index 0000000..0aa8ed5 --- /dev/null +++ b/src/engine/buffer/MultisampledRenderBuffer.cpp @@ -0,0 +1,14 @@ +/* Copyright (c) 2020 by Stan Fortoński */ + +#include "MultisampledRenderBuffer.hpp" + +namespace Engine +{ + void MultisampledRenderBuffer::create(const unsigned & width, const unsigned & height) + { + glGenRenderbuffers(1, &RBO); + bind(); + glRenderbufferStorageMultisample(GL_RENDERBUFFER, Config::get().getSamples(), GL_DEPTH24_STENCIL8, width, height); + unbind(); + } +} diff --git a/src/engine/buffer/MultisampledRenderBuffer.hpp b/src/engine/buffer/MultisampledRenderBuffer.hpp new file mode 100644 index 0000000..c7be615 --- /dev/null +++ b/src/engine/buffer/MultisampledRenderBuffer.hpp @@ -0,0 +1,16 @@ +/* Copyright (c) 2020 by Stan Fortoński */ + +#ifndef MULTISAMPLED_RENDER_BUFFER_HPP +#define MULTISAMPLED_RENDER_BUFFER_HPP 1 +#include "RenderBuffer.hpp" +#include "../Config.hpp" + +namespace Engine +{ + class MultisampledRenderBuffer: public RenderBuffer + { + public: + virtual void create(const unsigned & width, const unsigned & height); + }; +} +#endif diff --git a/src/engine/buffer/RenderBuffer.cpp b/src/engine/buffer/RenderBuffer.cpp new file mode 100644 index 0000000..0f3732f --- /dev/null +++ b/src/engine/buffer/RenderBuffer.cpp @@ -0,0 +1,68 @@ +/* Copyright (c) 2020 by Stan Fortoński */ + +#include "RenderBuffer.hpp" + +namespace Engine +{ + void RenderBuffer::create(const unsigned & width, const unsigned & height) + { + if (!isCreated()) + { + glGenRenderbuffers(1, &RBO); + bind(); + glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, width, height); + unbind(); + } + } + + void RenderBuffer::attach() const + { + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, RBO); + } + + RenderBuffer::RenderBuffer() + { + amount = new unsigned; + *amount = 1; + } + + RenderBuffer::RenderBuffer(const RenderBuffer & fb) + { + swap(fb); + } + + RenderBuffer & RenderBuffer::operator=(const RenderBuffer & fb) + { + clear(); + swap(fb); + return *this; + } + + void RenderBuffer::swap(const RenderBuffer & fb) + { + RBO = fb.RBO; + amount = fb.amount; + *amount = *amount + 1; + } + + RenderBuffer::~RenderBuffer() + { + clear(); + } + + void RenderBuffer::clear() + { + *amount = *amount - 1; + if (*amount == 0) + { + delete amount; + remove(); + } + } + + void RenderBuffer::remove() + { + glDeleteRenderbuffers(1, &RBO); + RBO = 0; + } +} diff --git a/src/engine/buffer/RenderBuffer.hpp b/src/engine/buffer/RenderBuffer.hpp new file mode 100644 index 0000000..2c61bab --- /dev/null +++ b/src/engine/buffer/RenderBuffer.hpp @@ -0,0 +1,35 @@ +/* Copyright (c) 2020 by Stan Fortoński */ + +#ifndef RENDER_BUFFER_HPP +#define RENDER_BUFFER_HPP 1 +#include + +namespace Engine +{ + class RenderBuffer + { + void clear(); + void swap(const RenderBuffer & fb); + + protected: + unsigned RBO = 0; + unsigned * amount; + + public: + RenderBuffer(); + RenderBuffer(const RenderBuffer & fb); + RenderBuffer & operator=(const RenderBuffer & fb); + virtual ~RenderBuffer(); + + virtual void create(const unsigned & width, const unsigned & height); + virtual void remove(); + virtual void attach() const; + + unsigned getId() const{return RBO;} + bool isCreated() const{return RBO != 0;} + + void bind() const{glBindRenderbuffer(GL_RENDERBUFFER, RBO);} + static void unbind(){glBindFramebuffer(GL_RENDERBUFFER, 0);} + }; +} +#endif diff --git a/src/engine/buffer/WaterBuffers.cpp b/src/engine/buffer/WaterBuffers.cpp new file mode 100644 index 0000000..4918e25 --- /dev/null +++ b/src/engine/buffer/WaterBuffers.cpp @@ -0,0 +1,43 @@ +/* Copyright (c) 2020 by Stan Fortoński */ +/* Based on: https://www.youtube.com/watch?v=21UsMuFTN0k */ + +#include "WaterBuffers.hpp" + +namespace Engine +{ + void WaterBuffers::init() + { + reflectionFrame.setRenderBuffer(reflectRenderBuffer); + reflectionFrame.generate(window.getWidth(), window.getHeight()); + + refractionFrame.setDepthBuffer(refractDepth); + refractionFrame.generate(window.getWidth(), window.getHeight()); + } + + WaterBuffers::WaterBuffers(Window & win): window(win) + { + init(); + } + + void WaterBuffers::updateBuffers() + { + removeBuffers(); + init(); + } + + void WaterBuffers::removeBuffers() + { + reflectionFrame.removeBuffers(); + refractionFrame.removeBuffers(); + } + + void WaterBuffers::bindReflectBuffer() const + { + reflectionFrame.bind(); + } + + void WaterBuffers::bindRefractBuffer() const + { + refractionFrame.bind(); + } +} diff --git a/src/engine/buffer/WaterBuffers.hpp b/src/engine/buffer/WaterBuffers.hpp new file mode 100644 index 0000000..df5bd09 --- /dev/null +++ b/src/engine/buffer/WaterBuffers.hpp @@ -0,0 +1,37 @@ +/* Copyright (c) 2020 by Stan Fortoński */ + +#ifndef WATER_BUFFERS_HPP +#define WATER_BUFFERS_HPP 1 +#include "../buffer/FrameBuffer.hpp" +#include "../buffer/RenderBuffer.hpp" +#include "../window/Window.hpp" +#include "../Config.hpp" + +namespace Engine +{ + class WaterBuffers + { + Window & window; + FrameBuffer reflectionFrame; + FrameBuffer refractionFrame; + RenderBuffer reflectRenderBuffer; + RenderBuffer refractRenderBuffer; + DepthBuffer refractDepth; + + void init(); + + public: + WaterBuffers(Window & win); + virtual ~WaterBuffers(){;} + + void bindReflectBuffer() const; + void bindRefractBuffer() const; + void updateBuffers(); + void removeBuffers(); + + unsigned getReflectTextureId(){return reflectionFrame.getTextures()[0].getId();} + unsigned getRefractTextureId(){return refractionFrame.getTextures()[0].getId();} + unsigned getRefractDepthTextureId(){return refractDepth.getId();} + }; +} +#endif diff --git a/src/engine/camera/BaseCamera.cpp b/src/engine/camera/BaseCamera.cpp new file mode 100644 index 0000000..5b0b5ea --- /dev/null +++ b/src/engine/camera/BaseCamera.cpp @@ -0,0 +1,48 @@ +/* Copyright (c) 2020 by Stan Fortoński */ + +#include "BaseCamera.hpp" + +namespace Engine +{ + void BaseCamera::moveRight() + { + position += right * (speed * Engine::get().getDeltaTime()); + } + + void BaseCamera::moveLeft() + { + position -= right * (speed * Engine::get().getDeltaTime()); + } + + void BaseCamera::moveTop() + { + position += direction * (speed * Engine::get().getDeltaTime()); + } + + void BaseCamera::moveBottom() + { + position -= direction * (speed * Engine::get().getDeltaTime()); + } + + void BaseCamera::rotate(const float & offsetX, const float & offsetY) + { + yaw += offsetX * sensitivity; + pitch += offsetY * sensitivity; + + if (pitch > 89) pitch = 89; + else if (pitch < -89) pitch = -89; + + updateVectors(); + } + + void BaseCamera::updateVectors() + { + glm::vec3 front; + front.x = cos(glm::radians(yaw)) * cos(glm::radians(pitch)); + front.y = sin(glm::radians(pitch)); + front.z = sin(glm::radians(yaw)) * cos(glm::radians(pitch)); + direction = glm::normalize(front); + right = glm::normalize(glm::cross(direction, worldUp)); + up = glm::normalize(glm::cross(right, direction)); + } +} diff --git a/src/engine/camera/BaseCamera.hpp b/src/engine/camera/BaseCamera.hpp new file mode 100644 index 0000000..15c907e --- /dev/null +++ b/src/engine/camera/BaseCamera.hpp @@ -0,0 +1,71 @@ +/* Copyright (c) 2020 by Stan Fortoński */ + +#ifndef BASE_CAMERA_HPP +#define BASE_CAMERA_HPP 1 +#include "InterfaceCamera.hpp" + +namespace Engine +{ + class BaseCamera: public InterfaceCamera + { + glm::vec3 position; + glm::vec3 direction; + glm::vec3 up = glm::vec3(0.0f, 1.0f, 0.0f); + glm::vec3 worldUp = glm::vec3(0.0f, 1.0f, 0.0f); + glm::vec3 right; + float pitch; + float yaw; + float speed; + float sensitivity; + float fov; + float far; + float near; + + protected: + Window & window; + + public: + BaseCamera(Window & win, + const glm::vec3 & pPosition = Config::get().getCameraPosition(), + const glm::vec3 & pDirection = Config::get().getCameraDirection(), + const float & pPitch = Config::get().getCameraPitch(), + const float & pYaw = Config::get().getCameraYaw(), + const float & pSpeed = Config::get().getCameraSpeed(), + const float & pSensitivity = Config::get().getCameraSensitivity(), + const float & pFov = Config::get().getCameraFov(), + const float & pNear = Config::get().getCameraNear(), + const float & pFar = Config::get().getCameraFar() + ): window(win), position(pPosition), direction(pDirection), + pitch(pPitch), yaw(pYaw), speed(pSpeed), sensitivity(pSensitivity), + fov(pFov), near(pNear), far(pFar){updateVectors();} + + virtual void updateVectors(); + virtual void moveRight(); + virtual void moveLeft(); + virtual void moveTop(); + virtual void moveBottom(); + virtual void rotate(const float & offsetX, const float & offsetY); + virtual glm::mat4 getViewMatrix() const{return glm::lookAt(position, position + direction, up);} + virtual glm::mat4 getProjectionMatrix() const = 0; + + virtual void setPosition(const glm::vec3 & vec){position = vec;} + virtual void setDirection(const glm::vec3 & vec){direction = vec;} + virtual void setPitch(const float & val){pitch = val;} + virtual void setYaw(const float & val){yaw = val;} + virtual void setSpeed(const float & val){speed = val;} + virtual void setFov(const float & val){fov = val;} + virtual void setFar(const float & val){far = val;} + virtual void setNear(const float & val){near = val;} + virtual void setSensitivity(const float & val){sensitivity = val;} + + virtual glm::vec3 getPosition() const{return position;} + virtual glm::vec3 getDirection() const{return direction;} + virtual float getPitch() const{return pitch;} + virtual float getYaw() const{return yaw;} + virtual float getSpeed() const{return speed;} + virtual float getFov() const{return fov;} + virtual float getNear() const{return near;} + virtual float getFar() const{return far;} + }; +} +#endif diff --git a/src/engine/camera/InterfaceCamera.hpp b/src/engine/camera/InterfaceCamera.hpp new file mode 100644 index 0000000..5239814 --- /dev/null +++ b/src/engine/camera/InterfaceCamera.hpp @@ -0,0 +1,46 @@ +/* Copyright (c) 2020 by Stan Fortoński */ + +#ifndef INTERFACE_CAMERA_HPP +#define INTERFACE_CAMERA_HPP 1 +#include +#include +#include "../window/Window.hpp" + +namespace Engine +{ + class InterfaceCamera + { + public: + virtual ~InterfaceCamera(){;} + + virtual void moveRight() = 0; + virtual void moveLeft() = 0; + virtual void moveTop() = 0; + virtual void moveBottom() = 0; + virtual void rotate(const float & offsetX, const float & offsetY) = 0; + virtual void updateVectors() = 0; + + virtual glm::mat4 getViewMatrix() const = 0; + virtual glm::mat4 getProjectionMatrix() const = 0; + virtual glm::mat4 getViewProjectionMatrix() const{return getProjectionMatrix() * getViewMatrix();} + virtual glm::vec3 getPosition() const = 0; + virtual glm::vec3 getDirection() const = 0; + virtual float getPitch() const = 0; + virtual float getYaw() const = 0; + virtual float getSpeed() const = 0; + virtual float getFov() const = 0; + virtual float getNear() const = 0; + virtual float getFar() const = 0; + + virtual void setPosition(const glm::vec3 & vec) = 0; + virtual void setDirection(const glm::vec3 & vec) = 0; + virtual void setPitch(const float & val) = 0; + virtual void setYaw(const float & val) = 0; + virtual void setSpeed(const float & val) = 0; + virtual void setFov(const float & val) = 0; + virtual void setFar(const float & val) = 0; + virtual void setNear(const float & val) = 0; + virtual void setSensitivity(const float & val) = 0; + }; +} +#endif diff --git a/src/engine/camera/OrthogonalCamera.hpp b/src/engine/camera/OrthogonalCamera.hpp new file mode 100644 index 0000000..ebca685 --- /dev/null +++ b/src/engine/camera/OrthogonalCamera.hpp @@ -0,0 +1,17 @@ +/* Copyright (c) 2020 by Stan Fortoński */ + +#ifndef ORTHOGONAL_CAMERA_HPP +#define ORTHOGONAL_CAMERA_HPP 1 +#include "BaseCamera.hpp" + +namespace Engine +{ + class OrthogonalCamera: public BaseCamera + { + public: + using BaseCamera::BaseCamera; + + virtual glm::mat4 getProjectionMatrix() const{return glm::ortho(-1.0f, 1.0f, -1.0f, 1.0f, getNear(), getFar());} + }; +} +#endif diff --git a/src/engine/camera/PerspectiveCamera.hpp b/src/engine/camera/PerspectiveCamera.hpp new file mode 100644 index 0000000..8481e4d --- /dev/null +++ b/src/engine/camera/PerspectiveCamera.hpp @@ -0,0 +1,17 @@ +/* Copyright (c) 2020 by Stan Fortoński */ + +#ifndef PERSPECTIVE_CAMERA_HPP +#define PERSPECTIVE_CAMERA_HPP 1 +#include "BaseCamera.hpp" + +namespace Engine +{ + class PerspectiveCamera: public BaseCamera + { + public: + using BaseCamera::BaseCamera; + + virtual glm::mat4 getProjectionMatrix() const{return glm::perspective(glm::radians(getFov()), (float)window.getWidth()/(float)window.getHeight(), getNear(), getFar());} + }; +} +#endif diff --git a/src/engine/config.hpp b/src/engine/config.hpp new file mode 100644 index 0000000..221fe83 --- /dev/null +++ b/src/engine/config.hpp @@ -0,0 +1,60 @@ +/* Copyright (c) 2020 by Stan Fortoński */ + +#ifndef CONFIG_HPP +#define CONFIG_HPP 1 +#define DEBUG_ENGINE 0 +#include "support/Singleton.hpp" +#include +#include + +namespace Engine +{ + class Config: public Singleton + { + friend class Singleton; + + std::string title = "3D Engine - OpenGL 4.1"; + unsigned majorVersion = 4; + unsigned minorVersion = 1; + unsigned windowWidth = 1920; + unsigned windowHeight = 1080; + unsigned shadowSize = 2048; + unsigned samples = 8; + float cameraPitch = 0; + float cameraYaw = -90; + float cameraSpeed = 10; + float cameraSensitivity = 0.1; + float cameraFov = 45; + float cameraFar = 1000; + float cameraNear = 0.01; + float shadowFar = 500; + float anisotropy = 8; + glm::vec3 cameraPosition = glm::vec3(0.0f, 0.0f, 0.0f); + glm::vec3 cameraDirection = glm::vec3(0.0f, 0.0f, -1.0f); + glm::vec3 gravity = glm::vec3(0.0f, -9.81f, 0.0f); + + Config(){;} + + public: + std::string getTitle() const{return title;} + unsigned getWindowWidth() const{return windowWidth;} + unsigned getWindowHeight() const{return windowHeight;} + unsigned getMajorVersion() const{return majorVersion;} + unsigned getMinorVersion() const{return minorVersion;} + unsigned getShadowSize() const{return shadowSize;} + unsigned getSamples() const{return samples;} + float getCameraPitch() const{return cameraPitch;} + float getCameraYaw() const{return cameraYaw;} + float getCameraSpeed() const{return cameraSpeed;} + float getCameraSensitivity() const{return cameraSensitivity;} + float getCameraFov() const{return cameraFov;} + float getCameraFar() const{return cameraFar;} + float getCameraNear() const{return cameraNear;} + glm::vec3 getCameraPosition() const{return cameraPosition;} + glm::vec3 getCameraDirection() const{return cameraDirection;} + float getShadowFar() const{return shadowFar;} + float getAnisotropy() const{return anisotropy;} + glm::vec3 getGravity() const{return gravity;} + }; +} +#endif diff --git a/src/engine/effect/Bloom.cpp b/src/engine/effect/Bloom.cpp new file mode 100644 index 0000000..71cc6da --- /dev/null +++ b/src/engine/effect/Bloom.cpp @@ -0,0 +1,47 @@ +/* Copyright (c) 2020 by Stan Fortoński */ +/* Multisampled HDR effect, Ping Pong Method */ +/* Based on: https://learnopengl.com/Advanced-Lighting/Bloom */ + +#include "Bloom.hpp" + +namespace Engine +{ + Bloom::Bloom(Window & win): window(win) + { + unsigned width = window.getWidth(); + unsigned height = window.getHeight(); + pingPongBuffer[0].generate(width, height); + pingPongBuffer[1].generate(width, height); + } + + void Bloom::updateBuffers() + { + unsigned width = window.getWidth(); + unsigned height = window.getHeight(); + + pingPongBuffer[0].removeBuffers(); + pingPongBuffer[1].removeBuffers(); + pingPongBuffer[0].generate(width, height); + pingPongBuffer[1].generate(width, height); + } + + void Bloom::blurTexture(Program & blurProgram, const Texture & texture) + { + horizontal = true; + bool first_iteration = true; + blurProgram.use(); + blurProgram.setInt("image", 0); + for (unsigned i = 0; i < BLUR_AMOUNT; ++i) + { + pingPongBuffer[horizontal].bind(); + blurProgram.setInt("isHorizontal", horizontal); + Texture::active(0); + glBindTexture(GL_TEXTURE_2D, first_iteration ? texture.getId() : pingPongBuffer[!horizontal].getTextures()[0].getId()); + quad.render(blurProgram); + horizontal = !horizontal; + if (first_iteration) + first_iteration = false; + } + glBindFramebuffer(GL_FRAMEBUFFER, 0); + } +} diff --git a/src/engine/effect/Bloom.hpp b/src/engine/effect/Bloom.hpp new file mode 100644 index 0000000..9534692 --- /dev/null +++ b/src/engine/effect/Bloom.hpp @@ -0,0 +1,29 @@ +/* Copyright (c) 2020 by Stan Fortoński */ + +#ifndef BLOOM_HPP +#define BLOOM_HPP 1 +#include "../base/Program.hpp" +#include "../window/Window.hpp" +#include "../buffer/FrameBuffer.hpp" +#include "../renderable/shape/Plane.hpp" + +namespace Engine +{ + class Bloom + { + const unsigned BLUR_AMOUNT = 8; + Window & window; + FrameBuffer pingPongBuffer[2]; + Plane quad; + bool horizontal; + + public: + Bloom(Window & win); + virtual ~Bloom(){;} + + void updateBuffers(); + void blurTexture(Program & blurProgram, const Texture & texture); + const Texture & getResultTexture() const{return pingPongBuffer[!horizontal].getTextures()[0];} + }; +} +#endif diff --git a/src/engine/effect/ParticlesGenerator.hpp b/src/engine/effect/ParticlesGenerator.hpp new file mode 100644 index 0000000..d5b9857 --- /dev/null +++ b/src/engine/effect/ParticlesGenerator.hpp @@ -0,0 +1,149 @@ +/* Copyright (c) 2020 by Stan Fortoński */ + +#ifndef PARTICLES_HPP +#define PARTICLES_HPP 1 +#include +#include +#include +#include +#include "../Engine.hpp" +#include "../Window/window.hpp" +#include "../renderable/Transformationable.hpp" +#include "../camera/InterfaceCamera.hpp" + +namespace Engine +{ + class StandardParticle + { + public: + glm::vec3 position = glm::vec3(0.0f); + glm::vec4 color = glm::vec4(1.0); + }; + + template + class ParticlesGenerator: public Transformationable + { + InterfaceCamera * camera; + Program & particleProgram; + std::vector particles; + std::vector positions; + std::vector colors; + unsigned VBOparticlesPos; + unsigned VBOparticlesColor; + unsigned amount; + P particleModel; + + void respawnParticles(); + void generateBuffers(); + void updateBuffers(); + + protected: + virtual void respawnParticle(T & particle) = 0; + virtual bool updateParticle(T & particle, const float & deltaTime) = 0; + + public: + ParticlesGenerator(InterfaceCamera * cam, Program & program, const unsigned & particlesAmount); + virtual ~ParticlesGenerator(){;} + + void update(); + void render(); + virtual void render(Program & program); + + virtual unsigned getVAO() const{return particleModel.getVAO();} + }; + + template + ParticlesGenerator::ParticlesGenerator(InterfaceCamera * cam, Program & program, const unsigned & particlesAmount): camera(cam), particleProgram(program) + { + colors.resize(particlesAmount); + positions.resize(particlesAmount); + particles.resize(particlesAmount); + std::fill(particles.begin(), particles.end(), T()); + generateBuffers(); + } + + template + void ParticlesGenerator::render() + { + glDisable(GL_CULL_FACE); + glBlendFunc(GL_SRC_ALPHA, GL_ONE); + + particleProgram.use(); + particleProgram.setMat4("viewProject", camera->getViewProjectionMatrix()); + particleProgram.setMat4("model", getTransformMatrix()); + particleModel.render(particleProgram, amount); + + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glEnable(GL_CULL_FACE); + } + + template + void ParticlesGenerator::render(Program & program) + { + update(); + render(); + program.use(); + } + + template + void ParticlesGenerator::update() + { + amount = 0; + for (unsigned i = 0; i < particles.size(); ++i) + respawnParticle(particles[i]); + + float dt = Engine::get().getDeltaTime(); + for (unsigned i = 0; i < particles.size(); ++i) + { + T & p = particles[i]; + if (updateParticle(p, dt)) + { + positions[i] = p.position; + colors[i] = p.color; + ++amount; + } + } + updateBuffers(); + } + + template + void ParticlesGenerator::generateBuffers() + { + glBindVertexArray(getVAO()); + glGenBuffers(1, &VBOparticlesPos); + glBindBuffer(GL_ARRAY_BUFFER, VBOparticlesPos); + glBufferData(GL_ARRAY_BUFFER, particles.size() * sizeof(glm::vec3), 0, GL_STREAM_DRAW); + + glEnableVertexAttribArray(2); + glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, 0, nullptr); + + glGenBuffers(1, &VBOparticlesColor); + glBindBuffer(GL_ARRAY_BUFFER, VBOparticlesColor); + glBufferData(GL_ARRAY_BUFFER, particles.size() * sizeof(glm::vec4), 0, GL_STREAM_DRAW); + + glEnableVertexAttribArray(3); + glVertexAttribPointer(3, 4, GL_FLOAT, GL_FALSE, 0, nullptr); + + glVertexAttribDivisor(0, 0); + glVertexAttribDivisor(1, 0); + glVertexAttribDivisor(2, 1); + glVertexAttribDivisor(3, 1); + + glBindVertexArray(0); + } + + template + void ParticlesGenerator::updateBuffers() + { + glBindVertexArray(getVAO()); + glBindBuffer(GL_ARRAY_BUFFER, VBOparticlesPos); + glBufferData(GL_ARRAY_BUFFER, particles.size() * sizeof(glm::vec3), 0, GL_STREAM_DRAW); + glBufferSubData(GL_ARRAY_BUFFER, 0, amount * sizeof(glm::vec3), &positions[0]); + + glBindBuffer(GL_ARRAY_BUFFER, VBOparticlesColor); + glBufferData(GL_ARRAY_BUFFER, particles.size() * sizeof(glm::vec4), 0, GL_STREAM_DRAW); + glBufferSubData(GL_ARRAY_BUFFER, 0, amount * sizeof(glm::vec4), &colors[0]); + glBindVertexArray(0); + } +} +#endif diff --git a/src/engine/effect/PostProcessing.cpp b/src/engine/effect/PostProcessing.cpp new file mode 100644 index 0000000..fe5fd12 --- /dev/null +++ b/src/engine/effect/PostProcessing.cpp @@ -0,0 +1,84 @@ +/* Copyright (c) 2020 by Stan Fortoński */ + +#include "PostProcessing.hpp" + +namespace Engine +{ + PostProcessing::PostProcessing(Window & win, const unsigned & attach): window(win), attachments(attach) + { + unsigned width = window.getWidth(); + unsigned height = window.getHeight(); + + buffer.setRenderBuffer(renderBuffer); + buffer.generate(width, height, attachments); + + multisampledBuffer.setRenderBuffer(mulitsampledRenderBuffer); + multisampledBuffer.generate(width, height, attachments); + } + + void PostProcessing::startProcessing() const + { + multisampledBuffer.bind(); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + glClearColor(0, 0, 0, 1); + } + + void PostProcessing::endProcessing() const + { + unsigned width = window.getWidth(); + unsigned height = window.getHeight(); + + multisampledBuffer.bind(GL_READ_FRAMEBUFFER); + buffer.bind(GL_DRAW_FRAMEBUFFER); + for (unsigned i = 0; i < attachments; ++i) + { + glReadBuffer(GL_COLOR_ATTACHMENT0 + i); + glDrawBuffer(GL_COLOR_ATTACHMENT0 + i); + glBlitFramebuffer(0, 0, width, height, 0, 0, width, height, GL_COLOR_BUFFER_BIT, GL_NEAREST); + } + FrameBuffer::unbind(GL_READ_FRAMEBUFFER); + FrameBuffer::unbind(GL_DRAW_FRAMEBUFFER); + glBindFramebuffer(GL_FRAMEBUFFER, 0); + } + + void PostProcessing::updateBuffers() + { + unsigned width = window.getWidth(); + unsigned height = window.getHeight(); + + buffer.removeBuffers(); + buffer.generate(width, height, attachments); + + multisampledBuffer.removeBuffers(); + multisampledBuffer.generate(width, height, attachments); + } + + void PostProcessing::renderToQuad(Program & program, const std::vector & textures, const glm::mat4 & model) + { + std::vector texturesIds; + for (unsigned i = 0; i < textures.size(); ++i) + texturesIds.push_back(textures[i].getId()); + renderToQuad(program, texturesIds, model); + } + + void PostProcessing::renderToQuad(Program & program, const std::vector & textures, const glm::mat4 & model) + { + static Plane quad; + glDisable(GL_DEPTH_TEST); + glDisable(GL_BLEND); + + program.use(); + program.setMat4("model", model); + for (unsigned i = 0; i < textures.size(); ++i) + { + std::string name = std::string("screenTexture"+std::to_string(i)).c_str(); + program.setInt(name.c_str(), i); + Texture::active(i); + glBindTexture(GL_TEXTURE_2D, textures[i]); + } + quad.render(program); + + glEnable(GL_DEPTH_TEST); + glEnable(GL_BLEND); + } +} diff --git a/src/engine/effect/PostProcessing.hpp b/src/engine/effect/PostProcessing.hpp new file mode 100644 index 0000000..989ab70 --- /dev/null +++ b/src/engine/effect/PostProcessing.hpp @@ -0,0 +1,36 @@ +/* Copyright (c) 2020 by Stan Fortoński */ + +#ifndef POST_PROCESSING_HPP +#define POST_PROCESSING_HPP 1 +#include "../base/Program.hpp" +#include "../window/Window.hpp" +#include "../buffer/FrameBuffer.hpp" +#include "../buffer/MultisampledFrameBuffer.hpp" +#include "../buffer/MultisampledRenderBuffer.hpp" +#include "../renderable/shape/Plane.hpp" + +namespace Engine +{ + class PostProcessing + { + Window & window; + FrameBuffer buffer; + RenderBuffer renderBuffer; + MultisampledRenderBuffer mulitsampledRenderBuffer; + MultisampledFrameBuffer multisampledBuffer; + unsigned attachments; + + public: + PostProcessing(Window & win, const unsigned & attach = 1); + virtual ~PostProcessing(){;} + + void startProcessing() const; + void endProcessing() const; + void updateBuffers(); + const std::vector & getResultTextures() const{return buffer.getTextures();} + + static void renderToQuad(Program & program, const std::vector & textures, const glm::mat4 & model = glm::mat4(1.0f)); + static void renderToQuad(Program & program, const std::vector & textures, const glm::mat4 & model = glm::mat4(1.0f)); + }; +} +#endif diff --git a/src/engine/effect/Shadow.cpp b/src/engine/effect/Shadow.cpp new file mode 100644 index 0000000..6bfc562 --- /dev/null +++ b/src/engine/effect/Shadow.cpp @@ -0,0 +1,62 @@ +/* Copyright (c) 2020 by Stan Fortoński */ + +#include "Shadow.hpp" + +namespace Engine +{ + Shadow::Shadow() + { + depth.setDepthBuffer(depthBuffer); + depth.generate(Config::get().getShadowSize(), Config::get().getShadowSize(), 0); + } + + void Shadow::updateBuffer() + { + depth.removeBuffers(); + depth.generate(Config::get().getShadowSize(), Config::get().getShadowSize(), 0); + } + + void Shadow::initCastShadow(const Light & light, const std::vector & programs) + { + for (unsigned i = 0; i < programs.size(); ++i) + { + programs[i]->use(); + programs[i]->setFloat("farPlane", Config::get().getShadowFar()); + programs[i]->setVec3("lightPos", light.getPosition()); + } + + const ShadowTransforms & shadowTransforms = light.getShadowTransforms(); + for (unsigned i = 0; i < shadowTransforms.size(); ++i) + { + const char * name = std::string("shadowMatrices["+std::to_string(i)+"]").c_str(); + for (unsigned j = 0; j < programs.size(); ++j) + { + programs[j]->use(); + programs[j]->setMat4(name, shadowTransforms[i]); + } + } + } + + void Shadow::startCastShadow(const Light & light, const std::vector & programs) + { + initCastShadow(light, programs); + glViewport(0, 0, Config::get().getShadowSize(), Config::get().getShadowSize()); + depth.bind(); + glClear(GL_DEPTH_BUFFER_BIT); + } + + void Shadow::endCastShadow(const Light & light, const std::vector & programs) + { + glBindFramebuffer(GL_FRAMEBUFFER, 0); + + const char * name = std::string("shadowMaps["+std::to_string(light.getIndex())+"]").c_str(); + for (unsigned i = 0; i < programs.size(); ++i) + { + programs[i]->use(); + programs[i]->setInt(name, 9 + light.getIndex()); + programs[i]->setFloat("farPlane", Config::get().getShadowFar()); + Texture::active(9 + light.getIndex()); + depthBuffer.bind(GL_TEXTURE_CUBE_MAP); + } + } +} diff --git a/src/engine/effect/Shadow.hpp b/src/engine/effect/Shadow.hpp new file mode 100644 index 0000000..fa66c5f --- /dev/null +++ b/src/engine/effect/Shadow.hpp @@ -0,0 +1,31 @@ +/* Copyright (c) 2020 by Stan Fortoński */ + +#ifndef SHADOW_HPP +#define SHADOW_HPP 1 +#include +#include +#include "../buffer/FrameBuffer.hpp" +#include "../buffer/CubeDepthBuffer.hpp" +#include "../Config.hpp" +#include "../renderable/light/light.hpp" + +namespace Engine +{ + class Shadow + { + FrameBuffer depth; + CubeDepthBuffer depthBuffer; + + void initCastShadow(const Light & light, const std::vector & programs); + + public: + Shadow(); + virtual ~Shadow(){;} + + void startCastShadow(const Light & light, const std::vector & programs); + void endCastShadow(const Light & light, const std::vector & programs); + void updateBuffer(); + ShadowTransforms generateShadowTransforms(); + }; +} +#endif diff --git a/src/engine/include.hpp b/src/engine/include.hpp new file mode 100644 index 0000000..6195d8d --- /dev/null +++ b/src/engine/include.hpp @@ -0,0 +1,53 @@ +/* Copyright (c) 2020 by Stan Fortoński */ + +#include "Config.hpp" +#include "Engine.hpp" +#include "window/Window.hpp" +#include "base/Shader.hpp" +#include "base/Program.hpp" +#include "base/ShadersManager.hpp" +#include "base/Texture.hpp" +#include "support/Singleton.hpp" +#include "support/TextureLoader.hpp" +#include "support/Font.hpp" +#include "camera/InterfaceCamera.hpp" +#include "camera/BaseCamera.hpp" +#include "camera/OrthogonalCamera.hpp" +#include "camera/PerspectiveCamera.hpp" +#include "buffer/RenderBuffer.hpp" +#include "buffer/DepthBuffer.hpp" +#include "buffer/MultisampledRenderBuffer.hpp" +#include "buffer/FrameBuffer.hpp" +#include "buffer/MultisampledFrameBuffer.hpp" +#include "buffer/WaterBuffers.hpp" +#include "effect/PostProcessing.hpp" +#include "effect/Bloom.hpp" +#include "effect/Shadow.hpp" +#include "effect/ParticlesGenerator.hpp" +#include "renderable/Renderable.hpp" +#include "renderable/Transformationable.hpp" +#include "renderable/shape/Shape.hpp" +#include "renderable/shape/Plane.hpp" +#include "renderable/shape/Cube.hpp" +#include "renderable/model/Mesh.hpp" +#include "renderable/model/MeshBone.hpp" +#include "renderable/model/Model.hpp" +#include "renderable/model/ModelAnim.hpp" +#include "renderable/light/Light.hpp" +#include "renderable/Material.hpp" +#include "renderable/skybox/Skybox.hpp" +#include "renderable/terrain/Terrain.hpp" +#include "renderable/decorator/MaterialDecorator.hpp" +#include "renderable/decorator/TransformDecorator.hpp" +#include "renderable/font/FontRenderer.hpp" +#include "renderable/water/Water.hpp" +#include "scene/Scene.hpp" +#include "texture-generator/TextureGenerator.hpp" +#include "texture-generator/TextureGenerator2D.hpp" +#include "texture-generator/TextureGenerator3D.hpp" +#include "texture-generator/method/Random.hpp" +#include "texture-generator/method/SmoothNoise2D.hpp" +#include "texture-generator/method/SmoothNoise3D.hpp" +#include "texture-generator/method/PerlinNoise2D.hpp" +#include "texture-generator/method/PerlinNoise3D.hpp" +#include "texture-generator/ConverterToNormalMap.hpp" diff --git a/src/engine/renderable/Material.cpp b/src/engine/renderable/Material.cpp new file mode 100644 index 0000000..9b9d9bd --- /dev/null +++ b/src/engine/renderable/Material.cpp @@ -0,0 +1,13 @@ +/* Copyright (c) 2020 by Stan Fortoński */ + +#include "Material.hpp" + +void Engine::Material::render(Program & program) +{ + program.setVec3("mat.diffuse", diffuse); + program.setVec3("mat.ambient", ambient); + program.setVec3("mat.specular", specular); + program.setFloat("mat.shininess", shininess); + program.setInt("mat.isSupportTex", isSupportTex); + program.setFloat("mat.transparency", transparency); +} diff --git a/src/engine/renderable/Material.hpp b/src/engine/renderable/Material.hpp new file mode 100644 index 0000000..7eaa9bf --- /dev/null +++ b/src/engine/renderable/Material.hpp @@ -0,0 +1,48 @@ +/* Copyright (c) 2020 by Stan Fortoński */ + +#ifndef MATERIAL_HPP +#define MATERIAL_HPP 1 +#include "Renderable.hpp" + +namespace Engine +{ + class Material: public Renderable + { + private: + glm::vec3 diffuse; + glm::vec3 specular; + glm::vec3 ambient; + float shininess; + int isSupportTex; + float transparency; + + public: + Material(const glm::vec3 & pAmbient = glm::vec3(0.0f), + const glm::vec3 & pDiffuse = glm::vec3(0.0f), + const glm::vec3 & pSpecular = glm::vec3(0.0f), + const float & pShininess = 2, + const float & opacity = 1, + const int & supportTex = 0): ambient(pAmbient), diffuse(pDiffuse), + specular(pSpecular), shininess(pShininess), + transparency(opacity), isSupportTex(supportTex){;} + + glm::vec3 getAmbient() const{return ambient;} + glm::vec3 getDiffuse() const{return diffuse;} + glm::vec3 getSpecular() const{return specular;} + float getShininess() const{return shininess;} + int getTransparency() const{return transparency;} + + void setAmbient(const glm::vec3 & vec){ambient = vec;} + void setDiffuse(const glm::vec3 & vec){diffuse = vec;} + void setSpecular(const glm::vec3 & vec){specular = vec;} + void setShininess(const float & value){shininess = value;} + void setTransparency(const float & value){transparency = value;} + + bool isSupportingTex() const{return isSupportTex == 1;} + void setSupportTex(){isSupportTex = 1;} + void unsetSupportTex(){isSupportTex = 0;} + + virtual void render(Program & program); + }; +} +#endif diff --git a/src/engine/renderable/Renderable.hpp b/src/engine/renderable/Renderable.hpp new file mode 100644 index 0000000..29d54bb --- /dev/null +++ b/src/engine/renderable/Renderable.hpp @@ -0,0 +1,17 @@ +/* Copyright (c) 2020 by Stan Fortoński */ + +#ifndef RENDERABLE_HPP +#define RENDERABLE_HPP 1 +#include "../base/Program.hpp" + +namespace Engine +{ + class Renderable + { + public: + virtual void render(Program & program) = 0; + virtual unsigned getVAO() const{return 0;} + virtual ~Renderable(){;} + }; +} +#endif diff --git a/src/engine/renderable/Transformationable.hpp b/src/engine/renderable/Transformationable.hpp new file mode 100644 index 0000000..2f3a5bc --- /dev/null +++ b/src/engine/renderable/Transformationable.hpp @@ -0,0 +1,46 @@ +/* Copyright (c) 2020 by Stan Fortoński */ + +#ifndef TRANSFORMATIONABLE_HPP +#define TRANSFORMATIONABLE_HPP 1 +#include +#include +#include "Renderable.hpp" + +namespace Engine +{ + class Transformationable: public Renderable + { + glm::vec3 vPosition = glm::vec3(0.0f); + glm::vec3 vScale = glm::vec3(1.0f); + glm::mat4 mRotation = glm::mat4(1.0f); + + public: + virtual void render(Program & program) + { + program.setMat4("model", getTransformMatrix()); + } + virtual ~Transformationable(){;} + + glm::mat4 getTransformMatrix() const + { + glm::mat4 matrix = glm::mat4(1.0f); + matrix = glm::translate(matrix, vPosition); + matrix = glm::scale(matrix, vScale); + matrix = matrix * mRotation; + return matrix; + } + + void rotate(const float & angle, const glm::vec3 & vec){mRotation = glm::rotate(mRotation, glm::radians(angle), vec);} + void rotateX(const float & angle){rotate(angle, glm::vec3(1.0f, 0.0f, 0.0f));} + void rotateY(const float & angle){rotate(angle, glm::vec3(0.0f, 1.0f, 0.0f));} + void rotateZ(const float & angle){rotate(angle, glm::vec3(0.0f, 0.0f, 1.0f));} + void translate(const glm::vec3 & translate){vPosition += translate;} + void scale(const glm::vec3 & vec){vScale = vec;} + + void setPosition(const glm::vec3 & vec){vPosition = vec;} + glm::vec3 getPosition() const{return vPosition;} + glm::vec3 getScale() const{return vScale;} + glm::mat4 getRotation() const{return mRotation;} + }; +} +#endif diff --git a/src/engine/renderable/decorator/MaterialDecorator.hpp b/src/engine/renderable/decorator/MaterialDecorator.hpp new file mode 100644 index 0000000..b847ef6 --- /dev/null +++ b/src/engine/renderable/decorator/MaterialDecorator.hpp @@ -0,0 +1,29 @@ +/* Copyright (c) 2020 by Stan Fortoński */ + +#ifndef MATERIAL_DECORATOR +#define MATERIAL_DECORATOR 1 +#include "../material.hpp" + +namespace Engine +{ + class MaterialDecorator: public Renderable + { + Renderable & renderer; + Material material; + + public: + MaterialDecorator(Renderable & object): renderer(object){;} + MaterialDecorator(Renderable & object, const Material & mat): renderer(object), material(mat){;} + + void setMaterial(const Material & mat){material = mat;} + const Material & getMaterial(){return material;} + Renderable & getReference(){return renderer;} + + virtual void render(Program & program) + { + material.render(program); + renderer.render(program); + } + }; +} +#endif diff --git a/src/engine/renderable/decorator/TransformDecorator.hpp b/src/engine/renderable/decorator/TransformDecorator.hpp new file mode 100644 index 0000000..6b65578 --- /dev/null +++ b/src/engine/renderable/decorator/TransformDecorator.hpp @@ -0,0 +1,25 @@ +/* Copyright (c) 2020 by Stan Fortoński */ + +#ifndef TRANSFORM_DECORATOR +#define TRANSFORM_DECORATOR 1 +#include "../Transformationable.hpp" + +namespace Engine +{ + class TransformDecorator: public Transformationable + { + Renderable & renderer; + + public: + TransformDecorator(Renderable & object): renderer(object){;} + + Renderable & getReference(){return renderer;} + + virtual void render(Program & program) + { + Transformationable::render(program); + renderer.render(program); + } + }; +} +#endif diff --git a/src/engine/renderable/font/FontRenderer.cpp b/src/engine/renderable/font/FontRenderer.cpp new file mode 100644 index 0000000..f75bbcf --- /dev/null +++ b/src/engine/renderable/font/FontRenderer.cpp @@ -0,0 +1,63 @@ +/* Copyright (c) 2020 by Stan Fortoński */ +/* Free Type library for text rendering */ +/* Based on: https://learnopengl.com/In-Practice/2D-Game/Render-text */ + +#include "FontRenderer.hpp" + +namespace Engine +{ + FontRenderer::FontRenderer(Window & win, const Font & f): window(win), font(f) + { + glGenVertexArrays(1 , &VAO); + glGenBuffers(1 , &VBO); + glBindVertexArray(VAO); + glBindBuffer(GL_ARRAY_BUFFER, VBO); + glBufferData(GL_ARRAY_BUFFER, sizeof(float) * 6 * 4, 0, GL_DYNAMIC_DRAW); + glEnableVertexAttribArray(0); + glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 4 * sizeof(float), 0); + glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindVertexArray(0); + } + + void FontRenderer::render(Program & program) + { + program.use(); + program.setVec3("textColor", color); + program.setInt("text", 0); + program.setMat4("projection", getProjectionMatrix()); + Texture::active(0); + + glBindVertexArray(VAO); + + std::map & characters = font.getCharacters(); + for (auto it = text.cbegin(); it != text.cend(); ++it) + { + Font::Character ch = characters[*it]; + float xpos = posX + ch.bearing.x * scale; + float ypos = posY - (ch.size.y - ch.bearing.y) * scale; + + float width = ch.size.x * scale; + float height = ch.size.y * scale; + + float vertices[6][4] = { + {xpos, ypos + height, 0.0, 0.0}, + {xpos, ypos, 0.0, 1.0}, + {xpos + width, ypos, 1.0, 1.0}, + + {xpos, ypos + height, 0.0, 0.0}, + {xpos + width, ypos, 1.0, 1.0}, + {xpos + width, ypos + height, 1.0, 0.0} + }; + + ch.texture.bind(GL_TEXTURE_2D); + glBindBuffer(GL_ARRAY_BUFFER, VBO); + glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(vertices), vertices); + glBindBuffer(GL_ARRAY_BUFFER, 0); + + glDrawArrays(GL_TRIANGLES, 0, 6); + posX += (ch.advance >> 6) * scale; + } + glBindVertexArray(0); + Texture::unbind(GL_TEXTURE_2D); + } +}; diff --git a/src/engine/renderable/font/FontRenderer.hpp b/src/engine/renderable/font/FontRenderer.hpp new file mode 100644 index 0000000..e30a7cc --- /dev/null +++ b/src/engine/renderable/font/FontRenderer.hpp @@ -0,0 +1,41 @@ +/* Copyright (c) 2020 by Stan Fortoński */ + +#ifndef FONT_RENDERER_HPP +#define FONT_RENDERER_HPP 1 +#include "../shape/Shape.hpp" +#include "../../support/Font.hpp" +#include "../../window/Window.hpp" + +namespace Engine +{ + class FontRenderer: public Shape + { + Window & window; + Font font; + glm::vec3 color = glm::vec3(0.0f); + float posX = 0; + float posY = 0; + float scale = 1; + std::string text; + + glm::mat4 getProjectionMatrix() const{return glm::ortho(0.0f, (float)window.getWidth(), 0.0f, (float)window.getHeight());} + + public: + FontRenderer(Window & win, const Font & f); + + virtual void render(Program & program); + + void setColor(const glm::vec3 & c){color = c;} + glm::vec3 getColor() const{return color;} + + void setPosition(const float & x, const float & y){posX = x; posY = y;} + glm::vec2 getPosition() const{return glm::vec2(posX, posY);} + + void setScale(const float & s){scale = s;} + float getScale() const{return scale;} + + void setText(const std::string & textLine){text = textLine;} + std::string getText() const{return text;} + }; +} +#endif diff --git a/src/engine/renderable/light/Light.cpp b/src/engine/renderable/light/Light.cpp new file mode 100644 index 0000000..1ff26bc --- /dev/null +++ b/src/engine/renderable/light/Light.cpp @@ -0,0 +1,33 @@ +/* Copyright (c) 2020 by Stan Fortoński */ + +#include "Light.hpp" + +namespace Engine +{ + unsigned Light::amount = 0; + + void Light::generateShadowTransforms() + { + glm::mat4 projection = glm::perspective(glm::radians(90.0f), 1.0f, 0.1f, Config::get().getShadowFar()); + shadowTransforms.clear(); + shadowTransforms.push_back(projection * glm::lookAt(position, position + glm::vec3(1.0f, 0.0f, 0.0f), glm::vec3(0.0f, -1.0f, 0.0f))); + shadowTransforms.push_back(projection * glm::lookAt(position, position + glm::vec3(-1.0f, 0.0f, 0.0f), glm::vec3(0.0f, -1.0f, 0.0f))); + shadowTransforms.push_back(projection * glm::lookAt(position, position + glm::vec3(0.0f, 1.0f, 0.0f), glm::vec3(0.0f, 0.0f, 1.0f))); + shadowTransforms.push_back(projection * glm::lookAt(position, position + glm::vec3(0.0f, -1.0f, 0.0f), glm::vec3(0.0f, 0.0f, -1.0f))); + shadowTransforms.push_back(projection * glm::lookAt(position, position + glm::vec3(0.0f, 0.0f, 1.0f), glm::vec3(0.0f, -1.0f, 0.0f))); + shadowTransforms.push_back(projection * glm::lookAt(position, position + glm::vec3(0.0f, 0.0f, -1.0f), glm::vec3(0.0f, -1.0f, 0.0f))); + } + + void Light::render(Program & program) + { + using std::string; + string prefix = "lights["+std::to_string(getIndex())+"]"; + program.setVec3(string(prefix+".position").c_str(), position); + program.setVec3(string(prefix+".ambient").c_str(), ambient); + program.setVec3(string(prefix+".diffuse").c_str(), diffuse); + program.setVec3(string(prefix+".specular").c_str(), specular); + program.setFloat(string(prefix+".constant").c_str(), constant); + program.setFloat(string(prefix+".linear").c_str(), linear); + program.setFloat(string(prefix+".quadratic").c_str(), quadratic); + } +} diff --git a/src/engine/renderable/light/Light.hpp b/src/engine/renderable/light/Light.hpp new file mode 100644 index 0000000..70eb8a9 --- /dev/null +++ b/src/engine/renderable/light/Light.hpp @@ -0,0 +1,66 @@ +/* Copyright (c) 2020 by Stan Fortoński */ + +#ifndef LIGHT_HPP +#define LIGHT_HPP 1 +#include +#include +#include +#include "../Renderable.hpp" +#include "../../config.hpp" + +namespace Engine +{ + typedef std::vector ShadowTransforms; + + class Light: public Renderable + { + unsigned index; + glm::vec3 position; + glm::vec3 ambient; + glm::vec3 diffuse; + glm::vec3 specular; + float constant; + float linear; + float quadratic; + ShadowTransforms shadowTransforms; + + void generateShadowTransforms(); + + public: + static unsigned amount; + Light( + const glm::vec3 & pPosition = glm::vec3(0.0f), + const glm::vec3 & pAmbient = glm::vec3(0.0f), + const glm::vec3 & pDiffuse = glm::vec3(0.0f), + const glm::vec3 & pSpecular = glm::vec3(0.0f), + const float & pConstant = 1.0f, + const float & pLinear = 0.0f, + const float & pQuadratic = 0.0f + ): position(pPosition), ambient(pAmbient), + diffuse(pDiffuse), specular(pSpecular), + constant(pConstant), linear(pLinear), + quadratic(pQuadratic){index = amount++; generateShadowTransforms();} + + virtual void render(Program & program); + + void setPosition(const glm::vec3 & pos){position = pos; generateShadowTransforms();} + void setAmbient(const glm::vec3 & color){ambient = color;} + void setDiffuse(const glm::vec3 & color){diffuse = color;} + void setSpecular(const glm::vec3 & color){specular = color;} + void setConstant(const float & value){constant = value;} + void setLinear(const float & value){linear = value;} + void setQuadratic(const float & value){quadratic = value;} + + glm::vec3 getPosition() const{return position;} + glm::vec3 getAmbient() const{return ambient;} + glm::vec3 getDiffuse() const{return diffuse;} + glm::vec3 getSpecular() const{return specular;} + float getConstant() const{return constant;} + float getLinear() const{return linear;} + float getQuadratic() const{return quadratic;} + unsigned getIndex() const{return index;} + + const ShadowTransforms & getShadowTransforms() const{return shadowTransforms;} + }; +} +#endif diff --git a/src/engine/renderable/model/Mesh.cpp b/src/engine/renderable/model/Mesh.cpp new file mode 100644 index 0000000..ce46f04 --- /dev/null +++ b/src/engine/renderable/model/Mesh.cpp @@ -0,0 +1,100 @@ +/* Copyright (c) 2020 by Stan Fortoński */ + +#include "Mesh.hpp" + +namespace Engine +{ + Mesh::Mesh(const std::vector & verts, const std::vector & inds, const std::vector & texs, const Material & mat): vertices(verts), indices(inds), textures(texs), material(mat) + { + initMesh(); + } + + Mesh::Mesh(const Mesh & mesh): Shape(mesh) + { + swap(mesh); + } + + Mesh & Mesh::operator=(const Mesh & mesh) + { + Shape::operator=((const Shape &)mesh); + clear(); + swap(mesh); + return *this; + } + + void Mesh::swap(const Mesh & mesh) + { + EBO = mesh.EBO; + vertices = mesh.vertices; + indices = mesh.indices; + textures = mesh.textures; + material = mesh.material; + } + + void Mesh::clear() + { + if (willBeClear()) + glDeleteBuffers(1, &EBO); + } + + Mesh::~Mesh() + { + clear(); + } + + void Mesh::initMesh() + { + unsigned size = sizeof(MeshVertex); + + glGenVertexArrays(1, &VAO); + glGenBuffers(1, &VBO); + glGenBuffers(1, &EBO); + + glBindVertexArray(VAO); + + glBindBuffer(GL_ARRAY_BUFFER, VBO); + glBufferData(GL_ARRAY_BUFFER, vertices.size() * size, &vertices[0], GL_STATIC_DRAW); + + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices.size() * sizeof(unsigned), &indices[0], GL_STATIC_DRAW); + + glEnableVertexAttribArray(0); + glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, size, nullptr); + + glEnableVertexAttribArray(1); + glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, size, (void*)offsetof(MeshVertex, texCoords)); + + glEnableVertexAttribArray(2); + glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, size, (void*)offsetof(MeshVertex, normal)); + + glBindVertexArray(0); + } + + void Mesh::render(Program & program) + { + unsigned diffuseNum = 0, specularNum = 0; + for (unsigned i = 0; i < textures.size(); ++i) + { + Texture::active(i); + + std::string number; + std::string type = textures[i].type; + if (type == "texture_diffuse") + number = std::to_string(diffuseNum++); + else if (type == "texture_specular") + number = std::to_string(specularNum++); + + program.setInt(std::string(type+"["+number+"]").c_str(), i); + textures[i].texture.bind(GL_TEXTURE_2D); + } + + program.setInt("diffuseTexturesAmount", diffuseNum); + program.setInt("specularTexturesAmount", specularNum); + material.render(program); + + glBindVertexArray(VAO); + glDrawElements(GL_TRIANGLES, indices.size(), GL_UNSIGNED_INT, 0); + glBindVertexArray(0); + Texture::unbind(GL_TEXTURE_2D); + } +} diff --git a/src/engine/renderable/model/Mesh.hpp b/src/engine/renderable/model/Mesh.hpp new file mode 100644 index 0000000..e7f86ee --- /dev/null +++ b/src/engine/renderable/model/Mesh.hpp @@ -0,0 +1,64 @@ +/* Copyright (c) 2020 by Stan Fortoński */ + +#ifndef MESH_HPP +#define MESH_HPP 1 +#define _HAS_ITERATOR_DEBUGGING 0 +#define _SECURE_SCL 0 +#include +#include +#include +#include +#include +#include +#include +#include +#include "../Material.hpp" +#include "../shape/Shape.hpp" +#include "../../base/Texture.hpp" + +namespace Engine +{ + class Mesh: public Shape + { + public: + struct MeshTexture + { + Texture texture; + std::string type; + std::string path; + }; + + struct MeshVertex + { + glm::vec3 position; + glm::vec2 texCoords; + glm::vec3 normal; + }; + + private: + unsigned EBO; + std::vector vertices; + std::vector indices; + std::vector textures; + Material material; + + void swap(const Mesh & mesh); + void clear(); + void initMesh(); + + public: + Mesh(const std::vector & verts, const std::vector & inds, const std::vector & texs, const Material & mat); + Mesh(const Mesh & mesh); + Mesh & operator=(const Mesh & mesh); + virtual ~Mesh(); + + virtual void render(Program & program); + + void setMaterial(const Material & mat){material = mat;} + const Material & getMaterial() const{return material;} + const std::vector & getVertices() const{return vertices;} + const std::vector & getIndices() const{return indices;} + const std::vector & getTextures() const{return textures;} + }; +} +#endif diff --git a/src/engine/renderable/model/MeshBone.cpp b/src/engine/renderable/model/MeshBone.cpp new file mode 100644 index 0000000..dafe797 --- /dev/null +++ b/src/engine/renderable/model/MeshBone.cpp @@ -0,0 +1,75 @@ +/* Copyright (c) 2020 by Stan Fortoński */ + +#include "MeshBone.hpp" + +namespace Engine +{ + void MeshBone::BoneData::addBoneData(const unsigned & boneId, const float & weight) + { + for (unsigned i = 0 ; i < NUM_BONES_PER_VEREX; ++i) + { + if (weights[i] == 0.0) + { + ids[i] = boneId; + weights[i] = weight; + return; + } + } + } + + MeshBone::MeshBone(const std::vector & verts, const std::vector & inds, const std::vector & texs, const Material & mat, const std::vector & bones): Mesh(verts, inds, texs, mat), bonesData(bones) + { + initBones(); + } + + MeshBone::MeshBone(const MeshBone & mesh): Mesh(mesh) + { + swap(mesh); + } + + MeshBone & MeshBone::operator=(const MeshBone & mesh) + { + Mesh::operator=((const Mesh &) mesh); + clear(); + swap(mesh); + return *this; + } + + void MeshBone::swap(const MeshBone & mesh) + { + bonesData = mesh.bonesData; + VBObones = mesh.VBObones; + } + + void MeshBone::clear() + { + if (willBeClear()) + glDeleteBuffers(1, &VBObones); + } + + MeshBone::~MeshBone() + { + clear(); + } + + void MeshBone::initBones() + { + glGenBuffers(1, &VBObones); + + glBindBuffer(GL_ARRAY_BUFFER, VBObones); + glBufferData(GL_ARRAY_BUFFER, bonesData.size() * sizeof(BoneData), &bonesData[0], GL_STATIC_DRAW); + glBindBuffer(GL_ARRAY_BUFFER, 0); + + glBindVertexArray(VAO); + + glBindBuffer(GL_ARRAY_BUFFER, VBObones); + glEnableVertexAttribArray(3); + glVertexAttribIPointer(3, 4, GL_INT, sizeof(BoneData), nullptr); + + glEnableVertexAttribArray(4); + glVertexAttribPointer(4, 4, GL_FLOAT, GL_FALSE, sizeof(BoneData), (void*)offsetof(BoneData, weights)); + glBindBuffer(GL_ARRAY_BUFFER, 0); + + glBindVertexArray(0); + } +} diff --git a/src/engine/renderable/model/MeshBone.hpp b/src/engine/renderable/model/MeshBone.hpp new file mode 100644 index 0000000..f7f191d --- /dev/null +++ b/src/engine/renderable/model/MeshBone.hpp @@ -0,0 +1,38 @@ +/* Copyright (c) 2020 by Stan Fortoński */ + +#ifndef MESH_BONE_HPP +#define MESH_BONE_HPP 1 +#define NUM_BONES_PER_VEREX 4 +#include "Mesh.hpp" + +namespace Engine +{ + class MeshBone: public Mesh + { + public: + struct BoneData + { + unsigned ids[NUM_BONES_PER_VEREX]; + float weights[NUM_BONES_PER_VEREX]; + + void addBoneData(const unsigned & boneId, const float & weight); + }; + + private: + unsigned VBObones; + std::vector bonesData; + + void swap(const MeshBone & mesh); + void clear(); + void initBones(); + + public: + MeshBone(const std::vector & verts, const std::vector & inds, const std::vector & texs, const Material & mat, const std::vector & bones); + MeshBone(const MeshBone & mesh); + MeshBone & operator=(const MeshBone & mesh); + virtual ~MeshBone(); + + const std::vector & getVerticesBoneData() const{return bonesData;} + }; +} +#endif diff --git a/src/engine/renderable/model/Model.cpp b/src/engine/renderable/model/Model.cpp new file mode 100644 index 0000000..3976861 --- /dev/null +++ b/src/engine/renderable/model/Model.cpp @@ -0,0 +1,231 @@ +/* Copyright (c) 2020 by Stan Fortoński */ +/* Assimp library for loading 3D models */ +/* Based on: https://learnopengl.com/Model-Loading/Assimp */ + +#include "Model.hpp" + +namespace Engine +{ + Model::Model() + { + amount = new unsigned; + *amount = 1; + } + + Model::Model(const std::string & path) + { + load(path); + amount = new unsigned; + *amount = 1; + } + + Model::Model(const Model & model) + { + swap(model); + } + + Model & Model::operator=(const Model & model) + { + clear(); + swap(model); + return *this; + } + + void Model::swap(const Model & model) + { + meshes = model.meshes; + texturesLoaded = model.texturesLoaded; + directory = model.directory; + importer = model.importer; + scene = model.scene; + amount = model.amount; + *amount = *amount + 1; + } + + void Model::clear() + { + *amount = *amount - 1; + if (*amount == 0) + { + importer.FreeScene(); + delete amount; + for (unsigned i = 0; i < meshes.size(); ++i) + delete meshes[i]; + } + } + + Model::~Model() + { + clear(); + } + + void Model::load(const std::string & path) + { + directory = path.substr(0, path.find_last_of('/')); + scene = importer.ReadFile(path, aiProcessPreset_TargetRealtime_Quality); + + if (!scene || scene->mFlags & AI_SCENE_FLAGS_INCOMPLETE || !scene->mRootNode) + { + std::string message = "ASSIMP Error: "; + message += importer.GetErrorString(); + throw std::runtime_error(message+"\n"); + } + processNode(scene->mRootNode); + } + + void Model::render(Program & program) + { + Transformationable::render(program); + for (unsigned i = 0; i < meshes.size(); ++i) + meshes[i]->render(program); + } + + void Model::processNode(const aiNode* node) + { + for (unsigned i = 0; i < node->mNumMeshes; ++i) + { + aiMesh* aMesh = scene->mMeshes[node->mMeshes[i]]; + meshes.push_back(processMesh(aMesh)); + } + + for (unsigned i = 0; i < node->mNumChildren; ++i) + processNode(node->mChildren[i]); + } + + Mesh * Model::processMesh(const aiMesh* aMesh) + { + std::vector vertices = getVertices(aMesh); + std::vector indices = getIndices(aMesh); + std::vector textures = getTextures(aMesh); + Material material = getMaterial(aMesh, textures.size()); + return new Mesh(vertices, indices, textures, material); + } + + std::vector Model::getVertices(const aiMesh* aMesh) + { + std::vector vertices; + vertices.reserve(aMesh->mNumVertices); + for (unsigned i = 0; i < aMesh->mNumVertices; ++i) + { + Mesh::MeshVertex vertex; + vertex.position = glm::vec3(aMesh->mVertices[i].x, aMesh->mVertices[i].y, aMesh->mVertices[i].z); + vertex.normal = glm::vec3(aMesh->mNormals[i].x, aMesh->mNormals[i].y, aMesh->mNormals[i].z); + + if (aMesh->mTextureCoords[0]) + vertex.texCoords = glm::vec2(aMesh->mTextureCoords[0][i].x, aMesh->mTextureCoords[0][i].y); + else vertex.texCoords = glm::vec2(0.0f, 0.0f); + + vertices.push_back(vertex); + } + return vertices; + } + + std::vector Model::getIndices(const aiMesh* aMesh) + { + std::vector indices; + for (unsigned i = 0; i < aMesh->mNumFaces; ++i) + { + aiFace face = aMesh->mFaces[i]; + for (unsigned j = 0; j < face.mNumIndices; ++j) + indices.push_back(face.mIndices[j]); + } + return indices; + } + + std::vector Model::getTextures(const aiMesh* aMesh) + { + std::vector textures; + if (aMesh->mMaterialIndex >= 0) + { + aiMaterial * mat = scene->mMaterials[aMesh->mMaterialIndex]; + + std::vector diffuseMaps = loadMaterialTextures(mat, aiTextureType_DIFFUSE, "texture_diffuse"); + textures.insert(textures.end(), diffuseMaps.begin(), diffuseMaps.end()); + + std::vector specularMaps = loadMaterialTextures(mat, aiTextureType_SPECULAR, "texture_specular"); + textures.insert(textures.end(), specularMaps.begin(), specularMaps.end()); + } + return textures; + } + + Material Model::getMaterial(const aiMesh* aMesh, const unsigned & texCount) + { + Material material; + if (aMesh->mMaterialIndex >= 0) + { + aiMaterial * mat = scene->mMaterials[aMesh->mMaterialIndex]; + if (texCount > 0) + material.setSupportTex(); + else material = loadMaterial(mat); + } + return material; + } + + Material Model::loadMaterial(const aiMaterial* mat) + { + Material material; + aiColor3D color(0.0f, 0.0f, 0.0f); + + if (mat->Get(AI_MATKEY_COLOR_DIFFUSE, color) == AI_SUCCESS) + material.setDiffuse(glm::vec3(color.r, color.g, color.b)); + + if (mat->Get(AI_MATKEY_COLOR_AMBIENT, color) == AI_SUCCESS) + material.setAmbient(glm::vec3(color.r, color.g, color.b)); + + if (mat->Get(AI_MATKEY_COLOR_SPECULAR, color) == AI_SUCCESS) + material.setSpecular(glm::vec3(color.r, color.g, color.b)); + + float shininess; + mat->Get(AI_MATKEY_SHININESS, shininess); + material.setShininess(shininess); + + float transparency; + if (mat->Get(AI_MATKEY_OPACITY, transparency) == AI_SUCCESS) + material.setTransparency(transparency); + + material.unsetSupportTex(); + return material; + } + + std::vector Model::loadMaterialTextures(const aiMaterial* mat, const aiTextureType & type, const std::string & typeName) + { + std::vector textures; + std::string fileName; + for (unsigned i = 0; i < mat->GetTextureCount(type); ++i) + { + aiString str; + mat->GetTexture(type, i, &str); + + bool skip = false; + for (unsigned int j = 0; j < texturesLoaded.size(); ++j) + { + if (std::strcmp(texturesLoaded[j].path.data(), str.C_Str()) == 0) + { + textures.push_back(texturesLoaded[j]); + skip = true; + break; + } + } + + if (!skip) + { + fileName = std::string(str.C_Str()); + fileName = directory+'/'+fileName; + + Mesh::MeshTexture texture; + texture.texture.load(fileName, GL_TEXTURE_2D); + texture.type = typeName; + texture.path = str.C_Str(); + textures.push_back(texture); + texturesLoaded.push_back(texture); + } + } + return textures; + } + + void Model::changeWholeMaterial(const Material & mat) + { + for (unsigned i = 0; i < meshes.size(); ++i) + meshes[i]->setMaterial(mat); + } +} diff --git a/src/engine/renderable/model/Model.hpp b/src/engine/renderable/model/Model.hpp new file mode 100644 index 0000000..7792893 --- /dev/null +++ b/src/engine/renderable/model/Model.hpp @@ -0,0 +1,52 @@ +/* Copyright (c) 2020 by Stan Fortoński */ + +#ifndef MODEL_HPP +#define MODEL_HPP 1 +#include "Mesh.hpp" +#include "../../support/TextureLoader.hpp" +#include "../Transformationable.hpp" + +namespace Engine +{ + class Model: public Transformationable + { + std::vector meshes; + std::vector texturesLoaded; + std::string directory; + unsigned * amount; + + void swap(const Model & model); + void clear(); + + protected: + Assimp::Importer importer; + const aiScene * scene = nullptr; + + private: + void processNode(const aiNode* node); + Material loadMaterial(const aiMaterial* mat); + std::vector loadMaterialTextures(const aiMaterial* mat, const aiTextureType & type, const std::string & typeName); + + protected: + virtual Mesh * processMesh(const aiMesh* aMesh); + std::vector getVertices(const aiMesh* aMesh); + std::vector getIndices(const aiMesh* aMesh); + std::vector getTextures(const aiMesh* aMesh); + Material getMaterial(const aiMesh* aMesh, const unsigned & texCount); + + public: + Model(); + Model(const std::string & path); + Model(const Model & model); + Model & operator=(const Model & model); + virtual ~Model(); + + virtual void render(Program & program); + virtual void load(const std::string & path); + + void changeWholeMaterial(const Material & mat); + + const std::vector & getMeshes(){return meshes;} + }; +} +#endif diff --git a/src/engine/renderable/model/ModelAnim.cpp b/src/engine/renderable/model/ModelAnim.cpp new file mode 100644 index 0000000..148d76f --- /dev/null +++ b/src/engine/renderable/model/ModelAnim.cpp @@ -0,0 +1,278 @@ +/* Copyright (c) 2020 by Stan Fortoński */ +/* Based on: Human Skeleton System Animation Stephanie Cheng */ + +#include "ModelAnim.hpp" +#include +#include "../../Engine.hpp" + +namespace Engine +{ + ModelAnim::ModelAnim(const std::string & path) + { + load(path); + } + + void ModelAnim::load(const std::string & path) + { + Model::load(path); + + if (!scene->HasAnimations()) + throw std::runtime_error("Model does not contain animations"); + + if (scene->mAnimations[0]->mTicksPerSecond != 0.0) + ticksPerSecond = scene->mAnimations[0]->mTicksPerSecond; + else ticksPerSecond = 25.0f; + duration = scene->mAnimations[0]->mDuration; + + globalInverseTransformation = scene->mRootNode->mTransformation; + globalInverseTransformation.Inverse(); + } + + void ModelAnim::initRender(Program & program) + { + if (boneLocations.size() == 0) + { + boneLocations.resize(scene->mAnimations[0]->mNumChannels); + for (unsigned i = 0; i < boneLocations.size(); ++i) + { + std::string name = "bones[" + std::to_string(i) + "]"; + boneLocations[i] = glGetUniformLocation(program.getId(), name.c_str()); + } + } + } + + void ModelAnim::render(Program & program) + { + initRender(program); + update(); + Model::render(program); + } + + void ModelAnim::play(const float & st, const float & et) + { + if (st < 0) + startTick = 0; + else startTick = st; + + if (et > duration-1) + endTick = duration-1; + else endTick = et; + + if (endTick >= startTick) + setForward(); + else setBackward(); + + currentTime = tickToTimeInSec(startTick); + currentTick = startTick; + } + + void ModelAnim::update() + { + if (loop && isAnimationEnd()) + play(startTick, endTick); + + if (isAnimationWork()) + { + std::vector transforms; + boneTransform(currentTime, transforms); + + for (unsigned i = 0; i < boneLocations.size(); i++) + glUniformMatrix4fv(boneLocations[i], 1, GL_TRUE, (const float*)&transforms[i]); + + currentTime += Engine::get().getDeltaTime() * speed * way; + } + } + + float ModelAnim::timeInSecToTick(const float & timeInSec) const + { + double timeInTicks = timeInSec * ticksPerSecond; + return fmod(timeInTicks, duration); + } + + float ModelAnim::tickToTimeInSec(const float & timeInTicks) const + { + return timeInTicks / ticksPerSecond; + } + + bool ModelAnim::isAnimationEnd() const + { + if (way == 1) + return currentTick >= endTick; + return currentTick <= endTick; + } + + bool ModelAnim::isAnimationWork() const + { + if (way == 1) + return currentTick < endTick; + return currentTick > endTick; + } + + MeshBone * ModelAnim::processMesh(const aiMesh* aMesh) + { + std::vector vertices = getVertices(aMesh); + std::vector indices = getIndices(aMesh); + std::vector textures = getTextures(aMesh); + Material material = getMaterial(aMesh, textures.size()); + std::vector bonesData = getBones(aMesh); + return new MeshBone(vertices, indices, textures, material, bonesData); + } + + std::vector ModelAnim::getBones(const aiMesh* aMesh) + { + std::vector bonesData; + bonesData.resize(aMesh->mNumVertices); + for (unsigned i = 0; i < aMesh->mNumBones; ++i) + { + unsigned boneIndex = 0; + std::string boneName(aMesh->mBones[i]->mName.data); + + if (boneMapping.find(boneName) == boneMapping.end()) + { + boneIndex = numBones; + ++numBones; + BoneMatrix info; + boneMatrices.push_back(info); + boneMatrices[boneIndex].boneOffset = aMesh->mBones[i]->mOffsetMatrix; + boneMapping[boneName] = boneIndex; + } + else boneIndex = boneMapping[boneName]; + + for (unsigned j = 0; j < aMesh->mBones[i]->mNumWeights; ++j) + { + unsigned vertexId = aMesh->mBones[i]->mWeights[j].mVertexId; + float weight = aMesh->mBones[i]->mWeights[j].mWeight; + bonesData[vertexId].addBoneData(boneIndex, weight); + } + } + return bonesData; + } + + void ModelAnim::boneTransform(const float & timeInSeconds, std::vector & transforms) + { + aiMatrix4x4 identity; + currentTick = timeInSecToTick(timeInSeconds); + readNodeHeirarchy(currentTick, scene->mRootNode, identity); + transforms.resize(numBones); + for (unsigned i = 0; i < numBones; ++i) + transforms[i] = boneMatrices[i].finalTransformation; + } + + void ModelAnim::readNodeHeirarchy(const float & animationTime, const aiNode* aNode, const aiMatrix4x4 parentTransform) + { + std::string nodeName(aNode->mName.data); + const aiAnimation * aAnimation = scene->mAnimations[0]; + aiMatrix4x4 nodeTransformation = aNode->mTransformation; + + const aiNodeAnim * aNodeAnim = findNodeAnim(aAnimation, nodeName); + if (aNodeAnim) + { + aiVector3D aScaling = calcInterpolatedScaling(animationTime, aNodeAnim); + aiMatrix4x4 scalingMat; + aiMatrix4x4::Scaling(aScaling, scalingMat); + + aiVector3D aTranslation = calcInterpolatedTranslation(animationTime, aNodeAnim); + aiMatrix4x4 translationMat; + aiMatrix4x4::Translation(aTranslation, translationMat); + + aiQuaternion aRotation = calcInterpolatedRotation(animationTime, aNodeAnim); + aiMatrix4x4 rotateMat = aiMatrix4x4(aRotation.GetMatrix()); + + nodeTransformation = translationMat * rotateMat * scalingMat; + } + + aiMatrix4x4 globalTransformation = parentTransform * nodeTransformation; + if (boneMapping.find(nodeName) != boneMapping.end()) + { + unsigned boneIndex = boneMapping[nodeName]; + boneMatrices[boneIndex].finalTransformation = globalInverseTransformation * globalTransformation * boneMatrices[boneIndex].boneOffset; + } + + for (unsigned i = 0; i < aNode->mNumChildren; ++i) + readNodeHeirarchy(animationTime, aNode->mChildren[i], globalTransformation); + } + + const aiNodeAnim* ModelAnim::findNodeAnim(const aiAnimation* aAnimation, const std::string & nodeName) + { + for (unsigned i = 0; i < aAnimation->mNumChannels; ++i) + { + const aiNodeAnim* aNodeAnim = aAnimation->mChannels[i]; + if (std::string(aNodeAnim->mNodeName.data) == nodeName) + return aNodeAnim; + } + return nullptr; + } + + aiVector3D ModelAnim::calcInterpolatedTranslation(const float & animationTime, const aiNodeAnim* aNodeAnim) + { + if (aNodeAnim->mNumPositionKeys == 1) + return aNodeAnim->mPositionKeys[0].mValue; + + unsigned translateIndex = findTranslation(animationTime, aNodeAnim); + unsigned nextIndex = translateIndex + 1; + float deltaTime = (float)(aNodeAnim->mPositionKeys[nextIndex].mTime - aNodeAnim->mPositionKeys[translateIndex].mTime); + float factor = (animationTime - (float)aNodeAnim->mPositionKeys[translateIndex].mTime) / deltaTime; + const aiVector3D & START = aNodeAnim->mPositionKeys[translateIndex].mValue; + const aiVector3D & END = aNodeAnim->mPositionKeys[nextIndex].mValue; + aiVector3D delta = END - START; + return START + factor * delta; + } + + aiQuaternion ModelAnim::calcInterpolatedRotation(const float & animationTime, const aiNodeAnim* aNodeAnim) + { + if (aNodeAnim->mNumRotationKeys == 1) + return aNodeAnim->mRotationKeys[0].mValue; + + unsigned rotationIndex = findRotation(animationTime, aNodeAnim); + unsigned nextIndex = rotationIndex + 1; + float deltaTime = (float)(aNodeAnim->mRotationKeys[nextIndex].mTime - aNodeAnim->mRotationKeys[rotationIndex].mTime); + float factor = (animationTime - (float)aNodeAnim->mRotationKeys[rotationIndex].mTime) / deltaTime; + const aiQuaternion & quatStartRotation = aNodeAnim->mRotationKeys[rotationIndex].mValue; + const aiQuaternion & quatEndRotation = aNodeAnim->mRotationKeys[nextIndex].mValue; + aiQuaternion quatResult; + aiQuaternion::Interpolate(quatResult, quatStartRotation, quatEndRotation, factor); + return quatResult.Normalize(); + } + + aiVector3D ModelAnim::calcInterpolatedScaling(const float & animationTime, const aiNodeAnim* aNodeAnim) + { + if (aNodeAnim->mNumScalingKeys == 1) + return aNodeAnim->mScalingKeys[0].mValue; + + unsigned scalingIndex = findScaling(animationTime, aNodeAnim); + unsigned nextIndex = scalingIndex + 1; + float deltaTime = (float)(aNodeAnim->mScalingKeys[nextIndex].mTime - aNodeAnim->mScalingKeys[scalingIndex].mTime); + float factor = (animationTime - (float)aNodeAnim->mScalingKeys[scalingIndex].mTime) / deltaTime; + const aiVector3D & START = aNodeAnim->mScalingKeys[scalingIndex].mValue; + const aiVector3D & END = aNodeAnim->mScalingKeys[nextIndex].mValue; + aiVector3D delta = END - START; + return START + factor * delta; + } + + unsigned ModelAnim::findTranslation(const float & animationTime, const aiNodeAnim* aNodeAnim) + { + for (unsigned i = 0; i < aNodeAnim->mNumPositionKeys-1; ++i) + { + if (animationTime < (float)aNodeAnim->mPositionKeys[i+1].mTime) + return i; + } + } + + unsigned ModelAnim::findRotation(const float & animationTime, const aiNodeAnim* aNodeAnim) + { + for (unsigned i = 0; i < aNodeAnim->mNumRotationKeys-1; ++i) + { + if (animationTime < (float)aNodeAnim->mRotationKeys[i+1].mTime) + return i; + } + } + + unsigned ModelAnim::findScaling(const float & animationTime, const aiNodeAnim* aNodeAnim) + { + for (unsigned i = 0; i < aNodeAnim->mNumScalingKeys-1; ++i) + { + if (animationTime < (float)aNodeAnim->mScalingKeys[i+1].mTime) + return i; + } + } +} diff --git a/src/engine/renderable/model/ModelAnim.hpp b/src/engine/renderable/model/ModelAnim.hpp new file mode 100644 index 0000000..0816972 --- /dev/null +++ b/src/engine/renderable/model/ModelAnim.hpp @@ -0,0 +1,84 @@ +/* Copyright (c) 2020 by Stan Fortoński */ + +#ifndef MODEL_ANIM_HPP +#define MODEL_ANIM_HPP 1 +#include +#include +#include "MeshBone.hpp" +#include "Model.hpp" + +namespace Engine +{ + class ModelAnim: public Model + { + static const unsigned MAX_BONES = 100; + + struct BoneMatrix + { + aiMatrix4x4 boneOffset; + aiMatrix4x4 finalTransformation; + }; + + std::map boneMapping; + std::vector boneMatrices; + aiMatrix4x4 globalInverseTransformation; + std::vector boneLocations; + unsigned numBones = 0; + float ticksPerSecond = 0; + float startTick = 0; + float endTick = 0; + float currentTime = 0; + float currentTick = 0; + float duration = 0; + float speed = 1; + int way = 1; + bool loop = false; + + virtual MeshBone * processMesh(const aiMesh* aMesh); + std::vector getBones(const aiMesh* aMesh); + + unsigned findTranslation(const float & animationTime, const aiNodeAnim* aNodeAnim); + unsigned findRotation(const float & animationTime, const aiNodeAnim* aNodeAnim); + unsigned findScaling(const float & animationTime, const aiNodeAnim* aNodeAnim); + + aiVector3D calcInterpolatedTranslation(const float & animationTime, const aiNodeAnim* aNodeAnim); + aiQuaternion calcInterpolatedRotation(const float & animationTime, const aiNodeAnim* aNodeAnim); + aiVector3D calcInterpolatedScaling(const float & animationTime, const aiNodeAnim* pNodeAnim); + + void readNodeHeirarchy(const float & animationTime, const aiNode* aNode, const aiMatrix4x4 parentTransform); + void boneTransform(const float & timeInSeconds, std::vector & transforms); + const aiNodeAnim* findNodeAnim(const aiAnimation* aAnimation, const std::string & nodeName); + + void initRender(Program & program); + void setForward(){way = 1;} + void setBackward(){way = -1;} + + public: + ModelAnim(){;} + ModelAnim(const std::string & path); + + virtual void render(Program & program); + virtual void load(const std::string & path); + + void play(const float & st, const float & et); + void update(); + + void setLoop(bool l){loop = l;} + void setSpeed(const float & s){speed = s;} + float getDuration() const{return duration;} + float getAnimationDuration() const{return abs(endTick - startTick);} + float getCurrentTick() const{return currentTick;} + float getStartTick() const{return startTick;} + float getEndTick() const{return endTick;} + float getSpeed() const{return speed;} + bool isLoop() const{return loop;} + bool isAnimationEnd() const; + bool isAnimationWork() const; + bool isForward(){return way == 1;} + bool isBackward(){return way == -1;} + + float timeInSecToTick(const float & timeInSec) const; + float tickToTimeInSec(const float & timeInTicks) const; + }; +} +#endif diff --git a/src/engine/renderable/shape/Cube.cpp b/src/engine/renderable/shape/Cube.cpp new file mode 100644 index 0000000..036c3c1 --- /dev/null +++ b/src/engine/renderable/shape/Cube.cpp @@ -0,0 +1,86 @@ +/* Copyright (c) 2020 by Stan Fortoński */ + +#include "Cube.hpp" + +namespace Engine +{ + Cube::Cube() + { + float cubeVertices[] = { + -1.0, -1.0, -1.0, 0.0, 0.0, 0.0, 0.0, -1.0, + 1.0, 1.0, -1.0, 1.0, 1.0, 0.0, 0.0, -1.0, + 1.0, -1.0, -1.0, 1.0, 0.0, 0.0, 0.0, -1.0, + 1.0, 1.0, -1.0, 1.0, 1.0, 0.0, 0.0, -1.0, + -1.0, -1.0, -1.0, 0.0, 0.0, 0.0, 0.0, -1.0, + -1.0, 1.0, -1.0, 0.0, 1.0, 0.0, 0.0, -1.0, + + -1.0, -1.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, + 1.0, -1.0, 1.0, 1.0, 0.0, 0.0, 0.0, 1.0, + 1.0, 1.0, 1.0, 1.0, 1.0, 0.0, 0.0, 1.0, + 1.0, 1.0, 1.0, 1.0, 1.0, 0.0, 0.0, 1.0, + -1.0, 1.0, 1.0, 0.0, 1.0, 0.0, 0.0, 1.0, + -1.0, -1.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, + + -1.0, 1.0, 1.0, 1.0, 0.0, -1.0, 0.0, 0.0, + -1.0, 1.0, -1.0, 1.0, 1.0, -1.0, 0.0, 0.0, + -1.0, -1.0, -1.0, 0.0, 1.0, -1.0, 0.0, 0.0, + -1.0, -1.0, -1.0, 0.0, 1.0, -1.0, 0.0, 0.0, + -1.0, -1.0, 1.0, 0.0, 0.0, -1.0, 0.0, 0.0, + -1.0, 1.0, 1.0, 1.0, 0.0, -1.0, 0.0, 0.0, + + 1.0, 1.0, 1.0, 1.0, 0.0, 1.0, 0.0, 0.0, + 1.0, -1.0, -1.0, 0.0, 1.0, 1.0, 0.0, 0.0, + 1.0, 1.0, -1.0, 1.0, 1.0, 1.0, 0.0, 0.0, + 1.0, -1.0, -1.0, 0.0, 1.0, 1.0, 0.0, 0.0, + 1.0, 1.0, 1.0, 1.0, 0.0, 1.0, 0.0, 0.0, + 1.0, -1.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, + + -1.0, -1.0, -1.0, 0.0, 1.0, 0.0, -1.0, 0.0, + 1.0, -1.0, -1.0, 1.0, 1.0, 0.0, -1.0, 0.0, + 1.0, -1.0, 1.0, 1.0, 0.0, 0.0, -1.0, 0.0, + 1.0, -1.0, 1.0, 1.0, 0.0, 0.0, -1.0, 0.0, + -1.0, -1.0, 1.0, 0.0, 0.0, 0.0, -1.0, 0.0, + -1.0, -1.0, -1.0, 0.0, 1.0, 0.0, -1.0, 0.0, + + -1.0, 1.0, -1.0, 0.0, 1.0, 0.0, 1.0, 0.0, + 1.0, 1.0, 1.0, 1.0, 0.0, 0.0, 1.0, 0.0, + 1.0, 1.0, -1.0, 1.0, 1.0, 0.0, 1.0, 0.0, + 1.0, 1.0, 1.0, 1.0, 0.0, 0.0, 1.0, 0.0, + -1.0, 1.0, -1.0, 0.0, 1.0, 0.0, 1.0, 0.0, + -1.0, 1.0, 1.0, 0.0, 0.0, 0.0, 1.0, 0.0 + }; + unsigned size = 8 * sizeof(float); + + glGenVertexArrays(1, &VAO); + glGenBuffers(1, &VBO); + glBindVertexArray(VAO); + + glBindBuffer(GL_ARRAY_BUFFER, VBO); + glBufferData(GL_ARRAY_BUFFER, sizeof(cubeVertices), cubeVertices, GL_STATIC_DRAW); + + glEnableVertexAttribArray(0); + glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, size, nullptr); + + glEnableVertexAttribArray(1); + glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, size, (void *)(3 * sizeof(float))); + + glEnableVertexAttribArray(2); + glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, size, (void *)(6 * sizeof(float))); + + glBindVertexArray(0); + } + + void Cube::render(Program & program) + { + glBindVertexArray(VAO); + glDrawArrays(GL_TRIANGLES, 0, 36); + glBindVertexArray(0); + } + + void Cube::render(Program & program, const unsigned & amount) + { + glBindVertexArray(VAO); + glDrawArraysInstanced(GL_TRIANGLES, 0, 36, amount); + glBindVertexArray(0); + } +} diff --git a/src/engine/renderable/shape/Cube.hpp b/src/engine/renderable/shape/Cube.hpp new file mode 100644 index 0000000..d818490 --- /dev/null +++ b/src/engine/renderable/shape/Cube.hpp @@ -0,0 +1,17 @@ +/* Copyright (c) 2020 by Stan Fortoński */ + +#ifndef CUBE_HPP +#define CUBE_HPP 1 +#include "Shape.hpp" + +namespace Engine +{ + class Cube: public Shape + { + public: + Cube(); + virtual void render(Program & program); + virtual void render(Program & program, const unsigned & amount); + }; +} +#endif diff --git a/src/engine/renderable/shape/Plane.cpp b/src/engine/renderable/shape/Plane.cpp new file mode 100644 index 0000000..01f7b15 --- /dev/null +++ b/src/engine/renderable/shape/Plane.cpp @@ -0,0 +1,42 @@ +/* Copyright (c) 2020 by Stan Fortoński */ + +#include "Plane.hpp" + +namespace Engine +{ + Plane::Plane() + { + static float planeVertices[] = { + -1.0, 1.0, 0.0, 0.0, 1.0, + -1.0, -1.0, 0.0, 0.0, 0.0, + 1.0, 1.0, 0.0, 1.0, 1.0, + 1.0, -1.0, 0.0, 1.0, 0.0 + }; + unsigned size = 5 * sizeof(float); + + glGenVertexArrays(1, &VAO); + glGenBuffers(1, &VBO); + glBindVertexArray(VAO); + glBindBuffer(GL_ARRAY_BUFFER, VBO); + glBufferData(GL_ARRAY_BUFFER, sizeof(planeVertices), planeVertices, GL_STATIC_DRAW); + glEnableVertexAttribArray(0); + glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, size, nullptr); + glEnableVertexAttribArray(1); + glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, size, (void *)(3 * sizeof(float))); + glBindVertexArray(0); + } + + void Plane::render(Program & program) + { + glBindVertexArray(VAO); + glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); + glBindVertexArray(0); + } + + void Plane::render(Program & program, const unsigned & amount) + { + glBindVertexArray(VAO); + glDrawArraysInstanced(GL_TRIANGLE_STRIP, 0, 4, amount); + glBindVertexArray(0); + } +} diff --git a/src/engine/renderable/shape/Plane.hpp b/src/engine/renderable/shape/Plane.hpp new file mode 100644 index 0000000..9a164ca --- /dev/null +++ b/src/engine/renderable/shape/Plane.hpp @@ -0,0 +1,17 @@ +/* Copyright (c) 2020 by Stan Fortoński */ + +#ifndef PLANE_HPP +#define PLANE_HPP 1 +#include "Shape.hpp" + +namespace Engine +{ + class Plane: public Shape + { + public: + Plane(); + virtual void render(Program & program); + virtual void render(Program & program, const unsigned & amount); + }; +} +#endif diff --git a/src/engine/renderable/shape/Shape.cpp b/src/engine/renderable/shape/Shape.cpp new file mode 100644 index 0000000..d0eaf2f --- /dev/null +++ b/src/engine/renderable/shape/Shape.cpp @@ -0,0 +1,48 @@ +/* Copyright (c) 2020 by Stan Fortoński */ + +#include "Shape.hpp" + +namespace Engine +{ + Shape::Shape() + { + amount = new unsigned; + *amount = 1; + } + + Shape::Shape(const Shape & shape) + { + swap(shape); + } + + Shape & Shape::operator=(const Shape & shape) + { + clear(); + swap(shape); + return *this; + } + + void Shape::swap(const Shape & shape) + { + VAO = shape.VAO; + VBO = shape.VBO; + amount = shape.amount; + *amount = *amount + 1; + } + + void Shape::clear() + { + *amount = *amount - 1; + if (*amount == 0) + { + delete amount; + glDeleteVertexArrays(1, &VAO); + glDeleteBuffers(1, &VBO); + } + } + + Shape::~Shape() + { + clear(); + } +} diff --git a/src/engine/renderable/shape/Shape.hpp b/src/engine/renderable/shape/Shape.hpp new file mode 100644 index 0000000..024f4d6 --- /dev/null +++ b/src/engine/renderable/shape/Shape.hpp @@ -0,0 +1,30 @@ +/* Copyright (c) 2020 by Stan Fortoński */ + +#ifndef SHAPE_HPP +#define SHAPE_HPP 1 +#include "../Renderable.hpp" + +namespace Engine +{ + class Shape: public Renderable + { + void swap(const Shape & shape); + void clear(); + + protected: + unsigned * amount; + unsigned VAO = 0; + unsigned VBO = 0; + + bool willBeClear() const{return (*amount - 1) == 0;} + + public: + Shape(); + Shape(const Shape & shape); + Shape & operator=(const Shape & shape); + virtual ~Shape(); + + virtual unsigned getVAO() const{return VAO;} + }; +} +#endif diff --git a/src/engine/renderable/skybox/Skybox.cpp b/src/engine/renderable/skybox/Skybox.cpp new file mode 100644 index 0000000..7766690 --- /dev/null +++ b/src/engine/renderable/skybox/Skybox.cpp @@ -0,0 +1,76 @@ +/* Copyright (c) 2020 by Stan Fortoński */ + +#include "Skybox.hpp" + +namespace Engine +{ + void Skybox::init() + { + static float skybox[] = { + -1.0f, 1.0f, -1.0f, + -1.0f, -1.0f, -1.0f, + 1.0f, -1.0f, -1.0f, + 1.0f, -1.0f, -1.0f, + 1.0f, 1.0f, -1.0f, + -1.0f, 1.0f, -1.0f, + + -1.0f, -1.0f, 1.0f, + -1.0f, -1.0f, -1.0f, + -1.0f, 1.0f, -1.0f, + -1.0f, 1.0f, -1.0f, + -1.0f, 1.0f, 1.0f, + -1.0f, -1.0f, 1.0f, + + 1.0f, -1.0f, -1.0f, + 1.0f, -1.0f, 1.0f, + 1.0f, 1.0f, 1.0f, + 1.0f, 1.0f, 1.0f, + 1.0f, 1.0f, -1.0f, + 1.0f, -1.0f, -1.0f, + + -1.0f, -1.0f, 1.0f, + -1.0f, 1.0f, 1.0f, + 1.0f, 1.0f, 1.0f, + 1.0f, 1.0f, 1.0f, + 1.0f, -1.0f, 1.0f, + -1.0f, -1.0f, 1.0f, + + -1.0f, 1.0f, -1.0f, + 1.0f, 1.0f, -1.0f, + 1.0f, 1.0f, 1.0f, + 1.0f, 1.0f, 1.0f, + -1.0f, 1.0f, 1.0f, + -1.0f, 1.0f, -1.0f, + + -1.0f, -1.0f, -1.0f, + -1.0f, -1.0f, 1.0f, + 1.0f, -1.0f, -1.0f, + 1.0f, -1.0f, -1.0f, + -1.0f, -1.0f, 1.0f, + 1.0f, -1.0f, 1.0f + }; + glGenVertexArrays(1, &VAO); + glBindVertexArray(VAO); + + glGenBuffers(1, &VBO); + glBindBuffer(GL_ARRAY_BUFFER, VBO); + glBufferData(GL_ARRAY_BUFFER, sizeof(skybox), &skybox, GL_STATIC_DRAW); + + glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), nullptr); + glEnableVertexAttribArray(0); + } + + void Skybox::render(Program & program) + { + glDepthFunc(GL_LEQUAL); + program.setInt("skybox", 0); + Texture::active(0); + texture.bind(GL_TEXTURE_CUBE_MAP); + + glBindVertexArray(VAO); + glDrawArrays(GL_TRIANGLES, 0, 36); + glDepthFunc(GL_LESS); + glBindVertexArray(0); + Texture::unbind(GL_TEXTURE_CUBE_MAP); + } +} diff --git a/src/engine/renderable/skybox/Skybox.hpp b/src/engine/renderable/skybox/Skybox.hpp new file mode 100644 index 0000000..c45122b --- /dev/null +++ b/src/engine/renderable/skybox/Skybox.hpp @@ -0,0 +1,24 @@ +/* Copyright (c) 2020 by Stan Fortoński */ + +#ifndef SKYBOX_HPP +#define SKYBOX_HPP 1 +#include "../../base/Texture.hpp" +#include "../shape/Cube.hpp" + +namespace Engine +{ + class Skybox: public Shape + { + Texture texture; + void init(); + + public: + Skybox(){init();} + Skybox(const Texture & tex):texture(tex){init();} + + virtual void render(Program & program); + + Texture & getTexture(){return texture;} + }; +} +#endif diff --git a/src/engine/renderable/terrain/Terrain.cpp b/src/engine/renderable/terrain/Terrain.cpp new file mode 100644 index 0000000..227303e --- /dev/null +++ b/src/engine/renderable/terrain/Terrain.cpp @@ -0,0 +1,84 @@ +/* Copyright (c) 2020 by Stan Fortoński */ + +#include "Terrain.hpp" + +namespace Engine +{ + Terrain::Terrain() + { + glGenVertexArrays(1, &VAO); + amount = new unsigned; + *amount = 1; + } + + Terrain::Terrain(const Terrain & terrain) + { + swap(terrain); + } + + Terrain & Terrain::operator=(const Terrain & terrain) + { + swap(terrain); + return *this; + } + + void Terrain::swap(const Terrain & terrain) + { + textureStone = terrain.textureStone; + textureGrass = terrain.textureGrass; + textureHeight = terrain.textureHeight; + textureNormal = terrain.textureNormal; + depth = terrain.depth; + maxTessLevel = terrain.maxTessLevel; + VAO = terrain.VAO; + amount = terrain.amount; + *amount = *amount + 1; + } + + Terrain::~Terrain() + { + clear(); + } + + void Terrain::clear() + { + *amount = *amount - 1; + if (*amount == 0) + { + delete amount; + glDeleteVertexArrays(1, &VAO); + } + } + + void Terrain::render(Program & program) + { + glDisable(GL_CULL_FACE); + + Transformationable::render(program); + program.setFloat("depth", depth); + program.setInt("maxTessLevel", maxTessLevel); + + Texture::active(0); + program.setInt("heightMap", 0); + textureHeight.bind(GL_TEXTURE_2D); + + Texture::active(1); + program.setInt("normalMap", 1); + textureNormal.bind(GL_TEXTURE_2D); + + Texture::active(2); + program.setInt("stoneTex", 2); + textureStone.bind(GL_TEXTURE_2D); + + Texture::active(3); + program.setInt("grassTex", 3); + textureGrass.bind(GL_TEXTURE_2D); + + glBindVertexArray(VAO); + glPatchParameteri(GL_PATCH_VERTICES, 4); + glDrawArraysInstanced(GL_PATCHES, 0, 4, 64*64); + glBindVertexArray(0); + Texture::unbind(GL_TEXTURE_2D); + glEnable(GL_CULL_FACE); + } +} diff --git a/src/engine/renderable/terrain/Terrain.hpp b/src/engine/renderable/terrain/Terrain.hpp new file mode 100644 index 0000000..bdda336 --- /dev/null +++ b/src/engine/renderable/terrain/Terrain.hpp @@ -0,0 +1,45 @@ +/* Copyright (c) 2020 by Stan Fortoński */ + +#ifndef TERRAIN_HPP +#define TERRAIN_HPP 1 +#include "../../base/Texture.hpp" +#include "../Transformationable.hpp" + +namespace Engine +{ + class Terrain: public Transformationable + { + Texture textureGrass; + Texture textureStone; + Texture textureHeight; + Texture textureNormal; + float depth = 0.05; + unsigned maxTessLevel = 8; + unsigned VAO = 0; + unsigned * amount; + + void swap(const Terrain & terrain); + void clear(); + + public: + Terrain(); + Terrain(const Terrain & terrain); + Terrain & operator=(const Terrain & terrain); + virtual ~Terrain(); + + virtual void render(Program & program); + virtual unsigned getVAO() const{return VAO;} + + Texture & getHeightMap(){return textureHeight;} + Texture & getNormalMap(){return textureNormal;} + Texture & getStoneTexture(){return textureStone;} + Texture & getGrassTexture(){return textureGrass;} + + void setDepth(const float & d){depth = d;} + float getDepth() const{return depth;} + + void setTessLevel(const unsigned & tl){maxTessLevel = tl;} + unsigned getTessLevel() const{return maxTessLevel;} + }; +} +#endif diff --git a/src/engine/renderable/water/Water.cpp b/src/engine/renderable/water/Water.cpp new file mode 100644 index 0000000..685da06 --- /dev/null +++ b/src/engine/renderable/water/Water.cpp @@ -0,0 +1,63 @@ +/* Copyright (c) 2020 by Stan Fortoński */ + +#include "Water.hpp" + +namespace Engine +{ + void Water::render(Program & program) + { + offset += 0.03 * Engine::Engine::get().getDeltaTime(); + if (offset >= 100) + offset = 0; + program.setFloat("waterOffset", offset); + + Texture::active(0); + program.setInt("refractionTex", 0); + glBindTexture(GL_TEXTURE_2D, buffers.getRefractTextureId()); + + Texture::active(1); + program.setInt("reflectionTex", 1); + glBindTexture(GL_TEXTURE_2D, buffers.getReflectTextureId()); + + Texture::active(2); + program.setInt("dudvMap", 2); + dudvMap.bind(GL_TEXTURE_2D); + + Texture::active(3); + program.setInt("normalMap", 3); + normalMap.bind(GL_TEXTURE_2D); + + Texture::active(4); + program.setInt("depthMap", 4); + glBindTexture(GL_TEXTURE_2D, buffers.getRefractDepthTextureId()); + + program.setFloat("nearPlane", Config::get().getCameraNear()); + program.setFloat("farPlane", Config::get().getCameraFar()); + Transformationable::render(program); + plane.render(program); + Texture::unbind(GL_TEXTURE_2D); + } + + void Water::renderReflectAndRefract(Scene * scene) + { + glm::vec3 defaultCamPos = camera->getPosition(); + float defaultCamPitch = camera->getPitch(); + + float distance = 2 * (defaultCamPos.y - getPosition().y); + camera->setPosition(glm::vec3(defaultCamPos.x, defaultCamPos.y - distance, defaultCamPos.z)); + camera->setPitch(-defaultCamPitch); + camera->updateVectors(); + + buffers.bindReflectBuffer(); + scene->render(glm::vec4(0, 1, 0, -getPosition().y+0.1)); + FrameBuffer::unbind(); + + camera->setPosition(defaultCamPos); + camera->setPitch(defaultCamPitch); + camera->updateVectors(); + + buffers.bindRefractBuffer(); + scene->render(glm::vec4(0, -1, 0, getPosition().y)); + FrameBuffer::unbind(); + } +} diff --git a/src/engine/renderable/water/Water.hpp b/src/engine/renderable/water/Water.hpp new file mode 100644 index 0000000..835a205 --- /dev/null +++ b/src/engine/renderable/water/Water.hpp @@ -0,0 +1,35 @@ +/* Copyright (c) 2020 by Stan Fortoński */ +/* Based on: https://www.youtube.com/watch?v=GADTasvDOX4 */ + +#ifndef WATER_HPP +#define WATER_HPP 1 +#include "../shape/Plane.hpp" +#include "../Transformationable.hpp" +#include "../../buffer/WaterBuffers.hpp" +#include "../../Config.hpp" +#include "../../scene/Scene.hpp" +#include "../../Camera/InterfaceCamera.hpp" + +namespace Engine +{ + class Water: public Transformationable + { + Plane plane; + Texture dudvMap; + Texture normalMap; + float offset = 0; + InterfaceCamera * camera = nullptr; + + public: + WaterBuffers buffers; + Water(Window & win, InterfaceCamera * cam): camera(cam), buffers(win){rotateX(-90);} + + virtual void render(Program & program); + void renderReflectAndRefract(Scene * scene); + virtual unsigned getVAO() const{return plane.getVAO();} + + Texture & getDudvMap(){return dudvMap;} + Texture & getNormalMap(){return normalMap;} + }; +} +#endif diff --git a/src/engine/scene/Scene.cpp b/src/engine/scene/Scene.cpp new file mode 100644 index 0000000..8597152 --- /dev/null +++ b/src/engine/scene/Scene.cpp @@ -0,0 +1,177 @@ +/* Copyright (c) 2020 by Stan Fortoński */ + +#include "Scene.hpp" + +namespace Engine +{ + void Scene::initPrograms(const glm::vec4 & clipPlane) + { + initProgram(manager.getObjectProgram(), clipPlane); + initProgram(manager.getAnimProgram(), clipPlane); + initProgram(manager.getTerrainProgram(), clipPlane); + initProgram(manager.getWaterProgram(), clipPlane); + initProgram(manager.getTerrainDepthProgram(), clipPlane); + } + + void Scene::initProgram(Program & program, const glm::vec4 & clipPlane) + { + if (program.getId() != 0) + { + program.use(); + program.setMat4("viewProject", camera->getViewProjectionMatrix()); + program.setVec3("viewPos", camera->getPosition()); + program.setInt("lightsAmount", Light::amount); + program.setInt("allowShadows", allowShadows ? 1 : 0); + program.setVec4("clipPlane", clipPlane); + } + } + + void Scene::renderShadows() + { + if (allowShadows) + { + std::vector programs; + if (manager.getObjectProgram().getId() != 0) + programs.push_back(&manager.getObjectProgram()); + if (manager.getAnimProgram().getId() != 0) + programs.push_back(&manager.getAnimProgram()); + if (manager.getTerrainProgram().getId() != 0) + programs.push_back(&manager.getTerrainProgram()); + + std::vector depthPrograms; + if (manager.getDepthProgram().getId() != 0) + depthPrograms.push_back(&manager.getDepthProgram()); + if (manager.getAnimDepthProgram().getId() != 0) + depthPrograms.push_back(&manager.getAnimDepthProgram()); + if (manager.getTerrainDepthProgram().getId() != 0) + depthPrograms.push_back(&manager.getTerrainDepthProgram()); + + for (unsigned i = 0; i < lights.size(); ++i) + { + shadows[i].startCastShadow(*lights[i], depthPrograms); + renderObjects(manager.getDepthProgram()); + renderAnimations(manager.getAnimDepthProgram()); + renderTerrains(manager.getTerrainDepthProgram()); + shadows[i].endCastShadow(*lights[i], programs); + } + } + } + + void Scene::render(const glm::vec4 & clipPlane) + { + glViewport(0, 0, window.getWidth(), window.getHeight()); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + glClearColor(0, 0, 0, 1); + + initPrograms(clipPlane); + renderLights(); + renderTerrains(manager.getTerrainProgram()); + renderObjects(manager.getObjectProgram()); + renderAnimations(manager.getAnimProgram()); + renderWaters(manager.getWaterProgram()); + renderSkybox(); + } + + void Scene::renderObjects(Program & prog) + { + if (prog.getId() != 0) + { + prog.use(); + for (unsigned i = 0; i < objects.size(); ++i) + objects[i]->render(prog); + } + } + + void Scene::renderAnimations(Program & prog) + { + if (prog.getId() != 0) + { + prog.use(); + for (unsigned i = 0; i < animations.size(); ++i) + animations[i]->render(prog); + } + } + + void Scene::renderWaters(Program & prog) + { + if (prog.getId() != 0) + { + prog.use(); + for (unsigned i = 0; i < waters.size(); ++i) + waters[i]->render(prog); + } + } + + void Scene::renderTerrains(Program & prog) + { + if (prog.getId() != 0) + { + prog.use(); + for (unsigned i = 0; i < terrains.size(); ++i) + terrains[i]->render(prog); + } + } + + void Scene::renderSkybox() + { + Program & skyboxProgram = manager.getSkyboxProgram(); + if (skybox != nullptr && skyboxProgram.getId() != 0) + { + skyboxProgram.use(); + glm::mat4 vp = camera->getProjectionMatrix() * glm::mat4(glm::mat3(camera->getViewMatrix())); + skyboxProgram.setMat4("viewProject", vp); + skybox->render(skyboxProgram); + } + } + + void Scene::renderLights() + { + Program & objProgram = manager.getObjectProgram(); + Program & animProgram = manager.getAnimProgram(); + Program & worldProgram = manager.getTerrainProgram(); + Program & waterProgram = manager.getWaterProgram(); + bool isWorldProgram = worldProgram.getId() != 0; + bool isObjectProgram = objProgram.getId() != 0; + bool isAnimProgram = animProgram.getId() != 0; + bool isWaterProgram = waterProgram.getId() != 0; + + for (unsigned i = 0; i < lights.size(); ++i) + { + if (isObjectProgram) + { + objProgram.use(); + lights[i]->render(objProgram); + } + + if (isAnimProgram) + { + animProgram.use(); + lights[i]->render(animProgram); + } + + if (isWaterProgram) + { + waterProgram.use(); + lights[i]->render(waterProgram); + } + + if (isWorldProgram) + { + worldProgram.use(); + lights[i]->render(worldProgram); + } + } + } + + void Scene::addLight(Light & obj) + { + shadows.push_back(Shadow()); + lights.push_back(&obj); + } + + void Scene::removeLight(const unsigned & n) + { + shadows.erase(shadows.begin()+n); + lights.erase(lights.begin()+n); + } +} diff --git a/src/engine/scene/Scene.hpp b/src/engine/scene/Scene.hpp new file mode 100644 index 0000000..119a57d --- /dev/null +++ b/src/engine/scene/Scene.hpp @@ -0,0 +1,71 @@ +/* Copyright (c) 2020 by Stan Fortoński */ + +#ifndef SCENE_HPP +#define SCENE_HPP 1 +#include +#include "../renderable/Skybox/Skybox.hpp" +#include "../renderable/terrain/Terrain.hpp" +#include "../camera/InterfaceCamera.hpp" +#include "../window/Window.hpp" +#include "../renderable/light/Light.hpp" +#include "../effect/Shadow.hpp" +#include "../base/ShadersManager.hpp" + +namespace Engine +{ + class Scene + { + Window & window; + ShadersManager & manager; + InterfaceCamera * camera = nullptr; + Skybox * skybox = nullptr; + std::vector terrains; + std::vector objects; + std::vector animations; + std::vector waters; + std::vector lights; + std::vector shadows; + bool allowShadows = true; + + void initProgram(Program & program, const glm::vec4 & clipPlane); + void initPrograms(const glm::vec4 & clipPlane); + + void renderTerrains(Program & prog); + void renderObjects(Program & prog); + void renderAnimations(Program & prog); + void renderWaters(Program & prog); + void renderLights(); + void renderSkybox(); + + public: + Scene(Window & win, InterfaceCamera * cam, ShadersManager & shadersmanager): window(win), camera(cam), manager(shadersmanager){;} + + void renderShadows(); + virtual void render(const glm::vec4 & clipPlane = glm::vec4(0, 1, 0, 10000)); + + void addSkybox(Skybox & obj){skybox = &obj;} + void addLight(Light & obj); + void removeLight(const unsigned & n); + + void addTerrain(Renderable & obj){terrains.push_back(&obj);} + void addObject(Renderable & obj){objects.push_back(&obj);} + void addAnimation(Renderable & obj){animations.push_back(&obj);} + void addWater(Renderable & obj){waters.push_back(&obj);} + + void removeTerrain(const unsigned & n){terrains.erase(terrains.begin()+n);} + void removeObject(const unsigned & n){objects.erase(objects.begin()+n);} + void removeAnimation(const unsigned & n){animations.erase(animations.begin()+n);} + void removeWater(const unsigned & n){waters.erase(waters.begin()+n);} + + Skybox & getSkybox(){return *skybox;} + std::vector & getTerrains(){return terrains;} + std::vector & getObjects(){return objects;} + std::vector & getAnimations(){return animations;} + std::vector & getWaters(){return waters;} + std::vector & getLights(){return lights;} + + void setShadows(bool val){allowShadows = val;} + bool isAllowShadows() const{return allowShadows;} + }; +} +#endif diff --git a/src/engine/support/Font.cpp b/src/engine/support/Font.cpp new file mode 100644 index 0000000..653d81b --- /dev/null +++ b/src/engine/support/Font.cpp @@ -0,0 +1,59 @@ +/* Copyright (c) 2020 by Stan Fortoński */ +/* Based on: https://learnopengl.com/In-Practice/2D-Game/Render-text */ + +#include "Font.hpp" + +namespace Engine +{ + Font::Font(const std::string & fontPath) + { + load(fontPath); + } + + void Font::load(const std::string & fontPath) + { + characters.clear(); + font = fontPath; + + FT_Library freeType; + if (FT_Init_FreeType(&freeType)) + throw std::runtime_error("Error FreeType: Could not init FreeType Library"); + + FT_Face face; + if (FT_New_Face(freeType, fontPath.c_str(), 0, &face)) + throw std::runtime_error("Error FreeType: Failed to load font"); + + FT_Set_Pixel_Sizes(face, 0, 48); + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + + for (unsigned char c = 0; c < 128; ++c) + { + if (FT_Load_Char(face, c, FT_LOAD_RENDER)) + { + std::cout<<"Error FreeType: Failed to load Glyph"<glyph->bitmap.width, face->glyph->bitmap.rows, 0, GL_RED, GL_UNSIGNED_BYTE, face->glyph->bitmap.buffer); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + Character character = { + texture, + glm::ivec2(face->glyph->bitmap.width, face->glyph->bitmap.rows), + glm::ivec2(face->glyph->bitmap_left, face->glyph->bitmap_top), + (unsigned)face->glyph->advance.x + }; + characters.insert(std::pair(c, character)); + } + glPixelStorei(GL_UNPACK_ALIGNMENT, 4); + FT_Done_Face(face); + FT_Done_FreeType(freeType); + Texture::unbind(GL_TEXTURE_2D); + } +}; diff --git a/src/engine/support/Font.hpp b/src/engine/support/Font.hpp new file mode 100644 index 0000000..e37e8df --- /dev/null +++ b/src/engine/support/Font.hpp @@ -0,0 +1,39 @@ +/* Copyright (c) 2020 by Stan Fortoński */ + +#ifndef FONT_HPP +#define FONT_HPP 1 +#include +#include FT_FREETYPE_H +#include +#include +#include "../base/Texture.hpp" + +namespace Engine +{ + class Font + { + public: + struct Character + { + Texture texture; + glm::ivec2 size; + glm::ivec2 bearing; + unsigned advance; + }; + + private: + std::string font; + std::map characters; + +public: + Font(){;} + Font(const std::string & fontPath); + + void load(const std::string & fontPath); + + std::string getFontPath() const{return font;} + std::map & getCharacters(){return characters;} + }; +} + +#endif diff --git a/src/engine/support/Singleton.hpp b/src/engine/support/Singleton.hpp new file mode 100644 index 0000000..773347e --- /dev/null +++ b/src/engine/support/Singleton.hpp @@ -0,0 +1,29 @@ +/* Copyright (c) 2020 by Stan Fortoński */ + +#ifndef SINGLETON_HPP +#define SINGLETON_HPP 1 +#include + +namespace Engine +{ + template + class Singleton + { + protected: + Singleton(){} + virtual ~Singleton(){;} + + public: + Singleton(const Singleton &) = delete; + Singleton & operator=(const Singleton &) = delete; + Singleton(Singleton &&) = delete; + Singleton & operator=(Singleton &&) = delete; + + static T & get() + { + static T instance; + return instance; + }; + }; +} +#endif diff --git a/src/engine/support/TextureLoader.cpp b/src/engine/support/TextureLoader.cpp new file mode 100644 index 0000000..cb420fc --- /dev/null +++ b/src/engine/support/TextureLoader.cpp @@ -0,0 +1,76 @@ +/* Copyright (c) 2020 by Stan Fortoński */ + +#include "TextureLoader.hpp" + +namespace Engine +{ + std::string TextureLoader::getError(const std::string & fileName) + { + return "Failed to load texture from file: "+fileName+"."; + } + + void TextureLoader::loadTexture(unsigned & textureId, const std::string & fileName, GLenum type) + { + int width, height, nrChannels; + unsigned char* data = stbi_load(fileName.c_str(), &width, &height, &nrChannels, 0); + if (!data) + { + stbi_image_free(data); + throw std::runtime_error(getError(fileName)); + } + + GLenum format = getFormat(nrChannels); + glBindTexture(type, textureId); + glTexImage2D(type, 0, format, width, height, 0, format, GL_UNSIGNED_BYTE, data); + stbi_image_free(data); + + glGenerateMipmap(type); + glTexParameteri(type, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTexParameteri(type, GL_TEXTURE_WRAP_T, GL_REPEAT); + glTexParameteri(type, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); + glTexParameteri(type, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(type, GL_TEXTURE_MAX_ANISOTROPY_EXT, Config::get().getAnisotropy()); + } + + void TextureLoader::loadTexture2D(unsigned & textureId, const std::string & fileName) + { + loadTexture(textureId, fileName, GL_TEXTURE_2D); + } + + void TextureLoader::loadCubeMapTexture(unsigned & textureId, const std::vector & fileNames) + { + glBindTexture(GL_TEXTURE_CUBE_MAP, textureId); + + int width, height, nrChannels; + for (unsigned i = 0; i < fileNames.size(); ++i) + { + unsigned char* data = stbi_load(fileNames[i].c_str(), &width, &height, &nrChannels, 0); + if (!data) + { + stbi_image_free(data); + throw std::runtime_error(getError(fileNames[i])); + } + + GLenum format = getFormat(nrChannels); + glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, format, width, height, 0, format, GL_UNSIGNED_BYTE, data); + stbi_image_free(data); + } + 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); + 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_MAX_ANISOTROPY_EXT, Config::get().getAnisotropy()); + } + + GLenum TextureLoader::getFormat(const unsigned & nrChannels) + { + if (nrChannels == 1) + return GL_RED; + else if (nrChannels == 3) + return GL_RGB; + else if (nrChannels == 4) + return GL_RGBA; + else throw std::runtime_error("Can't detected the format of the image from the number of channels"); + } +} diff --git a/src/engine/support/TextureLoader.hpp b/src/engine/support/TextureLoader.hpp new file mode 100644 index 0000000..f672c07 --- /dev/null +++ b/src/engine/support/TextureLoader.hpp @@ -0,0 +1,26 @@ +/* Copyright (c) 2020 by Stan Fortoński */ + +#ifndef TEXTURE_LOADER_HPP +#define TEXTURE_LOADER_HPP 1 +#include +#include +#include +#include +#include +#include +#include "../Config.hpp" + +namespace Engine +{ + class TextureLoader + { + static std::string getError(const std::string & fileName); + static GLenum getFormat(const unsigned & nrChannels); + + public: + static void loadTexture(unsigned & textureId, const std::string & fileName, GLenum type); + static void loadTexture2D(unsigned & textureId, const std::string & fileName); + static void loadCubeMapTexture(unsigned & textureId, const std::vector & fileNames); + }; +} +#endif diff --git a/src/engine/texture-generator/ConverterToNormalMap.cpp b/src/engine/texture-generator/ConverterToNormalMap.cpp new file mode 100644 index 0000000..464caef --- /dev/null +++ b/src/engine/texture-generator/ConverterToNormalMap.cpp @@ -0,0 +1,85 @@ +/* Copyright (c) 2020 by Stan Fortoński */ +/* Sobel operator for generating height map to normal map */ +/* Based on: https://stackoverflow.com/questions/2368728/can-normal-maps-be-generated-from-a-texture/2368794#2368794 */ + +#include "ConverterToNormalMap.hpp" + +namespace Engine +{ + GLubyte ConverterToNormalMap::toRGB(const float & pixel) + { + return (GLubyte)((pixel + 1.0f) * (255.0f / 2.0f)); + } + + float ConverterToNormalMap::getHeight(const std::vector & data, const unsigned & width, const unsigned & height, int row, int column) + { + return (data[(clamp(row, width)*width + clamp(column, height))*4]) / 255.0f; + } + + int ConverterToNormalMap::clamp(const int & value, const int & max) + { + if (value > max) + return max; + else if (value < 0) + return 0; + return value; + } + + glm::vec3 ConverterToNormalMap::calcNormal(const std::vector & data, const unsigned & width, const unsigned & height, const int & row, const int & column) + { + const float strength = 2.0f; + + float topLeft = getHeight(data, width, height, row-1, column-1); + float top = getHeight(data, width, height, row-1, column); + float topRight = getHeight(data, width, height, row-1, column+1); + float right = getHeight(data, width, height, row, column+1); + float bottomRight = getHeight(data, width, height, row+1, column+1); + float bottom = getHeight(data, width, height, row+1, column); + float bottomLeft = getHeight(data, width, height, row+1, column-1); + float left = getHeight(data, width, height, row, column-1); + + float sobelX = (topRight + 2.0f * right + bottomRight) - (topLeft + 2.0f * left + bottomLeft); + float sobelY = (bottomLeft + 2.0f * bottom + bottomRight) - (topLeft + 2.0f * top + topRight); + float sobelZ = 1.0f / strength; + + glm::vec3 normal(sobelX, sobelY, sobelZ); + return glm::normalize(normal); + } + + void ConverterToNormalMap::prepareData(GLubyte * inputData, const std::vector & data, const unsigned & width, const unsigned & height) + { + for (unsigned w = 0; w < width; ++w) + { + for (unsigned h = 0; h < height; ++h) + { + glm::vec3 normal = calcNormal(data, width, height, w, h); + + unsigned index = (w*width+h)*4; + inputData[index] = toRGB(normal.x); + inputData[index+1] = toRGB(normal.y); + inputData[index+2] = toRGB(normal.z); + inputData[index+3] = (GLubyte)255; + } + } + } + + void ConverterToNormalMap::convert(const std::vector & data, Texture & textureDestination, const unsigned & width, const unsigned & height) + { + if (textureDestination.isNotCreated()) + { + textureDestination.create(); + textureDestination.bind(GL_TEXTURE_2D); + glTexStorage2D(GL_TEXTURE_2D, 1, GL_RGBA8, width, height); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, Config::get().getAnisotropy()); + } + + GLubyte * inputData = new GLubyte[width*height*4]; + prepareData(inputData, data, width, height); + textureDestination.bind(GL_TEXTURE_2D); + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, inputData); + Texture::unbind(GL_TEXTURE_2D); + delete [] inputData; + } +} diff --git a/src/engine/texture-generator/ConverterToNormalMap.hpp b/src/engine/texture-generator/ConverterToNormalMap.hpp new file mode 100644 index 0000000..52b2f0c --- /dev/null +++ b/src/engine/texture-generator/ConverterToNormalMap.hpp @@ -0,0 +1,21 @@ +/* Copyright (c) 2020 by Stan Fortoński */ + +#ifndef CONVERTER_TO_NORMAL_MAP_HPP +#define CONVERTER_TO_NORMAL_MAP_HPP 1 +#include "../base/Texture.hpp" + +namespace Engine +{ + class ConverterToNormalMap + { + GLubyte toRGB(const float & pixel); + int clamp(const int & value, const int & max); + void prepareData(GLubyte * inputData, const std::vector & data, const unsigned & width, const unsigned & height); + glm::vec3 calcNormal(const std::vector & data, const unsigned & width, const unsigned & height, const int & row, const int & column); + float getHeight(const std::vector & data, const unsigned & width, const unsigned & height, int row, int column); + + public: + void convert(const std::vector & data, Texture & textureDestination, const unsigned & width, const unsigned & height); + }; +} +#endif diff --git a/src/engine/texture-generator/TextureGenerator.hpp b/src/engine/texture-generator/TextureGenerator.hpp new file mode 100644 index 0000000..9f45679 --- /dev/null +++ b/src/engine/texture-generator/TextureGenerator.hpp @@ -0,0 +1,27 @@ +/* Copyright (c) 2020 by Stan Fortoński */ + +#ifndef TEXTURE_GENERATOR_HPP +#define TEXTURE_GENERATOR_HPP 1 +#include "../base/Texture.hpp" + +namespace Engine +{ + class TextureGenerateMethod + { + public: + virtual void fillData(std::vector & data, const unsigned & width, const unsigned & height, const unsigned & depth) const = 0; + }; + + class TextureGenerator + { + protected: + std::vector data; + + public: + virtual void generate(Texture & texture, const TextureGenerateMethod & method, const unsigned & width, const unsigned & height, const unsigned & depth){;} + virtual ~TextureGenerator(){;} + + const std::vector & getTextureData() const{return data;} + }; +} +#endif diff --git a/src/engine/texture-generator/TextureGenerator2D.cpp b/src/engine/texture-generator/TextureGenerator2D.cpp new file mode 100644 index 0000000..79aaf75 --- /dev/null +++ b/src/engine/texture-generator/TextureGenerator2D.cpp @@ -0,0 +1,33 @@ +/* Copyright (c) 2020 by Stan Fortoński */ + +#include "TextureGenerator2D.hpp" + +namespace Engine +{ + void TextureGenerator2D::generate(Texture & texture, const TextureGenerateMethod & method, const unsigned & width, const unsigned & height, const unsigned & depth) + { + const unsigned SIZE = width * height * 4; + data.clear(); + data.resize(SIZE); + method.fillData(data, width, height, depth); + + if (texture.isNotCreated()) + { + texture.create(); + texture.bind(GL_TEXTURE_2D); + glTexStorage2D(GL_TEXTURE_2D, 1, GL_RGBA8, width, height); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, Config::get().getAnisotropy()); + } + + GLubyte * inputData = new GLubyte[SIZE]; + for (unsigned i = 0; i < SIZE; ++i) + inputData[i] = data[i]; + + texture.bind(GL_TEXTURE_2D); + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, inputData); + Texture::unbind(GL_TEXTURE_2D); + delete [] inputData; + } +} diff --git a/src/engine/texture-generator/TextureGenerator2D.hpp b/src/engine/texture-generator/TextureGenerator2D.hpp new file mode 100644 index 0000000..ecb2aee --- /dev/null +++ b/src/engine/texture-generator/TextureGenerator2D.hpp @@ -0,0 +1,15 @@ +/* Copyright (c) 2020 by Stan Fortoński */ + +#ifndef TEXTURE_GENERATOR_2D_HPP +#define TEXTURE_GENERATOR_2D_HPP 1 +#include "TextureGenerator.hpp" + +namespace Engine +{ + class TextureGenerator2D: public TextureGenerator + { + public: + virtual void generate(Texture & texture, const TextureGenerateMethod & method, const unsigned & width, const unsigned & height, const unsigned & depth = 1); + }; +} +#endif diff --git a/src/engine/texture-generator/TextureGenerator3D.cpp b/src/engine/texture-generator/TextureGenerator3D.cpp new file mode 100644 index 0000000..6c21312 --- /dev/null +++ b/src/engine/texture-generator/TextureGenerator3D.cpp @@ -0,0 +1,33 @@ +/* Copyright (c) 2020 by Stan Fortoński */ + +#include "TextureGenerator3D.hpp" + +namespace Engine +{ + void TextureGenerator3D::generate(Texture & texture, const TextureGenerateMethod & method, const unsigned & width, const unsigned & height, const unsigned & depth) + { + const unsigned SIZE = width * height * depth * 4; + data.clear(); + data.resize(SIZE); + method.fillData(data, width, height, depth); + + if (texture.isNotCreated()) + { + texture.create(); + texture.bind(GL_TEXTURE_3D); + glTexStorage3D(GL_TEXTURE_3D, 1, GL_RGBA8, width, height, depth); + glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAX_ANISOTROPY_EXT, Config::get().getAnisotropy()); + } + + GLubyte * inputData = new GLubyte[SIZE]; + for (unsigned i = 0; i < SIZE; ++i) + inputData[i] = data[i]; + + texture.bind(GL_TEXTURE_3D); + glTexSubImage3D(GL_TEXTURE_3D, 0, 0, 0, 0, width, height, depth, GL_RGBA, GL_UNSIGNED_BYTE, inputData); + Texture::unbind(GL_TEXTURE_3D); + delete [] inputData; + } +} diff --git a/src/engine/texture-generator/TextureGenerator3D.hpp b/src/engine/texture-generator/TextureGenerator3D.hpp new file mode 100644 index 0000000..9301fc8 --- /dev/null +++ b/src/engine/texture-generator/TextureGenerator3D.hpp @@ -0,0 +1,15 @@ +/* Copyright (c) 2020 by Stan Fortoński */ + +#ifndef TEXTURE_GENERATOR_3D_HPP +#define TEXTURE_GENERATOR_3D_HPP 1 +#include "TextureGenerator.hpp" + +namespace Engine +{ + class TextureGenerator3D: public TextureGenerator + { + public: + virtual void generate(Texture & texture, const TextureGenerateMethod & method, const unsigned & width, const unsigned & height, const unsigned & depth); + }; +} +#endif diff --git a/src/engine/texture-generator/method/PerlinNoise2D.cpp b/src/engine/texture-generator/method/PerlinNoise2D.cpp new file mode 100644 index 0000000..fc04d32 --- /dev/null +++ b/src/engine/texture-generator/method/PerlinNoise2D.cpp @@ -0,0 +1,52 @@ +/* Copyright (c) 2020 by Stan Fortoński */ +/* Perlin noise from GLM library to generate terrains */ +/* Based on: OpenGL 4 ShadingLanguage Cookbook Second Edition David Wolff */ + +#include "PerlinNoise2D.hpp" + +namespace Engine +{ + PerlinNoise2D::PerlinNoise2D(const double & pFrequency, const double & pAmplitude, const double & pPersistance, const unsigned & pOctaves, const double & pMulitplier, const unsigned & pOffsetX, const unsigned & pOffsetY) + : frequency(pFrequency), amplitude(pAmplitude), persistence(pPersistance), octaves(pOctaves), multiplier(pMulitplier), offsetX(pOffsetX), offsetY(pOffsetY){;} + + double PerlinNoise2D::octavePerlin(const double & x, const double & y) const + { + double total = 0; + double freq = frequency; + double amp = amplitude; + double maxValue = 0; + + for (unsigned i = 0; i < octaves; ++i) + { + glm::vec2 p((x * freq + offsetX) * multiplier, (y * freq + offsetY) * multiplier); + + total += ((glm::perlin(p) + 1.0)/2.0) * amp; + maxValue += amp; + amp *= persistence; + freq *= 2; + } + return total/maxValue; + } + + void PerlinNoise2D::fillData(std::vector & data, const unsigned & width, const unsigned & height, const unsigned & depth) const + { + double xFactor = 1.0 / (width - 1); + double yFactor = 1.0 / (height - 1); + + for (unsigned w = 0; w < width; ++w) + { + for (unsigned h = 0; h < height; ++h) + { + double x = xFactor * w; + double y = yFactor * h; + double perlin = octavePerlin(x, y); + GLubyte result = (GLubyte)(perlin * 255); + unsigned index = (w * width + h)*4; + data[index] = result; + data[index+1] = result; + data[index+2] = result; + data[index+3] = result; + } + } + } +} diff --git a/src/engine/texture-generator/method/PerlinNoise2D.hpp b/src/engine/texture-generator/method/PerlinNoise2D.hpp new file mode 100644 index 0000000..187204d --- /dev/null +++ b/src/engine/texture-generator/method/PerlinNoise2D.hpp @@ -0,0 +1,27 @@ +/* Copyright (c) 2020 by Stan Fortoński */ + +#ifndef PERLIN_NOISE_2D_HPP +#define PERLIN_NOISE_2D_HPP 1 +#include +#include "../TextureGenerator.hpp" + +namespace Engine +{ + class PerlinNoise2D: public TextureGenerateMethod + { + double frequency; + double amplitude; + double persistence; + double multiplier; + unsigned offsetX; + unsigned offsetY; + unsigned octaves; + + double octavePerlin(const double & x, const double & y) const; + + public: + PerlinNoise2D(const double & pFrequency = 1, const double & pAmplitude = 1, const double & pPersistance = 1, const unsigned & pOctaves = 4, const double & pMulitplier = 1, const unsigned & pOffsetX = 0, const unsigned & pOffsetY = 0); + virtual void fillData(std::vector & data, const unsigned & width, const unsigned & height, const unsigned & depth) const; + }; +} +#endif diff --git a/src/engine/texture-generator/method/PerlinNoise3D.cpp b/src/engine/texture-generator/method/PerlinNoise3D.cpp new file mode 100644 index 0000000..9dd470b --- /dev/null +++ b/src/engine/texture-generator/method/PerlinNoise3D.cpp @@ -0,0 +1,57 @@ +/* Copyright (c) 2020 by Stan Fortoński */ +/* Perlin noise from GLM library to generate 3d textures */ +/* Based on: OpenGL 4 ShadingLanguage Cookbook Second Edition David Wolff */ + +#include "PerlinNoise3D.hpp" + +namespace Engine +{ + PerlinNoise3D::PerlinNoise3D(const double & pFrequency, const double & pAmplitude, const double & pPersistance, const unsigned & pOctaves, const double & pMulitplier, const unsigned & pOffsetX, const unsigned & pOffsetY, const unsigned & pOffsetZ) + : frequency(pFrequency), amplitude(pAmplitude), persistence(pPersistance), octaves(pOctaves), multiplier(pMulitplier), offsetX(pOffsetX), offsetY(pOffsetY), offsetZ(pOffsetZ){;} + + double PerlinNoise3D::octavePerlin(const double & x, const double & y, const double & z) const + { + double total = 0; + double freq = frequency; + double amp = amplitude; + double maxValue = 0; + + for (unsigned i = 0; i < octaves; ++i) + { + glm::vec3 p((x * freq + offsetX) * multiplier, (y * freq + offsetY) * multiplier, (z * freq + offsetZ) * multiplier); + + total += ((glm::simplex(p) + 1.0)/2.0) * amp; + maxValue += amp; + amp *= persistence; + freq *= 2; + } + return total/maxValue; + } + + void PerlinNoise3D::fillData(std::vector & data, const unsigned & width, const unsigned & height, const unsigned & depth) const + { + double xFactor = 1.0 / (width - 1); + double yFactor = 1.0 / (height - 1); + double zFactor = 1.0 / (depth - 1); + + for (unsigned w = 0; w < width; ++w) + { + for (unsigned h = 0; h < height; ++h) + { + for (unsigned d = 0; d < depth; ++d) + { + double x = xFactor * w; + double y = yFactor * h; + double z = zFactor * d; + double perlin = octavePerlin(x, y, z); + GLubyte result = (GLubyte)(perlin * 255); + unsigned index = w*width*height*4 + h*height*4 + d*4; + data[index] = result; + data[index+1] = result; + data[index+2] = result; + data[index+3] = result; + } + } + } + } +} diff --git a/src/engine/texture-generator/method/PerlinNoise3D.hpp b/src/engine/texture-generator/method/PerlinNoise3D.hpp new file mode 100644 index 0000000..95cbf0d --- /dev/null +++ b/src/engine/texture-generator/method/PerlinNoise3D.hpp @@ -0,0 +1,28 @@ +/* Copyright (c) 2020 by Stan Fortoński */ + +#ifndef PERLIN_NOISE_3D_HPP +#define PERLIN_NOISE_3D_HPP 1 +#include +#include "../TextureGenerator.hpp" + +namespace Engine +{ + class PerlinNoise3D: public TextureGenerateMethod + { + double frequency; + double amplitude; + double persistence; + double multiplier; + unsigned offsetX; + unsigned offsetY; + unsigned offsetZ; + unsigned octaves; + + double octavePerlin(const double & x, const double & y, const double & z) const; + + public: + PerlinNoise3D(const double & pFrequency = 1, const double & pAmplitude = 1, const double & pPersistance = 1, const unsigned & pOctaves = 4, const double & pMulitplier = 1, const unsigned & pOffsetX = 0, const unsigned & pOffsetY = 0, const unsigned & pOffsetZ = 0); + virtual void fillData(std::vector & data, const unsigned & width, const unsigned & height, const unsigned & depth) const; + }; +} +#endif diff --git a/src/engine/texture-generator/method/Random.cpp b/src/engine/texture-generator/method/Random.cpp new file mode 100644 index 0000000..a779ac0 --- /dev/null +++ b/src/engine/texture-generator/method/Random.cpp @@ -0,0 +1,13 @@ +/* Copyright (c) 2020 by Stan Fortoński */ + +#include "Random.hpp" + +namespace Engine +{ + void Random::fillData(std::vector & data, const unsigned & width, const unsigned & height, const unsigned & depth) const + { + const unsigned SIZE = width * height * depth * 4; + for (unsigned i = 0; i < SIZE; ++i) + data[i] = (GLubyte) (rand()%max)+min; + } +} diff --git a/src/engine/texture-generator/method/Random.hpp b/src/engine/texture-generator/method/Random.hpp new file mode 100644 index 0000000..27b2b40 --- /dev/null +++ b/src/engine/texture-generator/method/Random.hpp @@ -0,0 +1,19 @@ +/* Copyright (c) 2020 by Stan Fortoński */ + +#ifndef RANDOM_HPP +#define RANDOM_HPP 1 +#include "../TextureGenerator.hpp" + +namespace Engine +{ + class Random: public TextureGenerateMethod + { + unsigned min; + unsigned max; + + public: + Random(const unsigned & pMin, const unsigned & pMax):min(pMin), max(pMax){;} + virtual void fillData(std::vector & data, const unsigned & width, const unsigned & height, const unsigned & depth) const; + }; +} +#endif diff --git a/src/engine/texture-generator/method/SmoothNoise2D.cpp b/src/engine/texture-generator/method/SmoothNoise2D.cpp new file mode 100644 index 0000000..2974299 --- /dev/null +++ b/src/engine/texture-generator/method/SmoothNoise2D.cpp @@ -0,0 +1,59 @@ +/* Copyright (c) 2020 by Stan Fortoński */ +/* Based on: Computer Graphics Programming in OpenGL with C++ V. Scott Gordon | John Clevenger */ + +#include "SmoothNoise2D.hpp" + +namespace Engine +{ + double SmoothNoise2D::smooth(const NoiseData2D & pattern, const unsigned & width, const unsigned & height, double x1, double y1) const + { + double fractX = x1 - (int)x1; + double fractY = y1 - (int)y1; + int x2 = ((int)x1 + width + 1)%width; + int y2 = ((int)y1 + height + 1)%height; + + double value = 0.0; + value += (1-fractX) * (1-fractY) * pattern[(int)x1][(int)y1]; + value += (1-fractX) * fractY * pattern[(int)x1][(int)y2]; + value += fractX * (1-fractY) * pattern[(int)x2][(int)y1]; + value += fractX * fractY * pattern[(int)x2][(int)y2]; + return value; + }; + + double SmoothNoise2D::turbulence(const NoiseData2D & pattern, const unsigned & width, const unsigned & height, double x, double y) const + { + double sum = 0.0, actZoom = zoom; + while (actZoom >= 1.0) + { + sum = sum + smooth(pattern, width, height, x/actZoom, y/actZoom) * actZoom; + actZoom = actZoom/2.0; + } + sum = 128 * sum / zoom; + return sum; + } + + void SmoothNoise2D::fillData(std::vector & data, const unsigned & width, const unsigned & height, const unsigned & depth) const + { + NoiseData2D pattern; + pattern.resize(width, std::vector(height)); + for (unsigned w = 0; w < width; ++w) + { + for (unsigned h = 0; h < height; ++h) + pattern[w][h] = (double)rand()/(RAND_MAX + 1.0); + } + + for (unsigned w = 0; w < width; ++w) + { + for (unsigned h = 0; h < height; ++h) + { + unsigned index = w*width*4 + h*4; + GLubyte value = (GLubyte) turbulence(pattern, width, height, w, h); + + data[index] = value; + data[index+1] = value; + data[index+2] = value; + data[index+3] = (GLubyte) 255; + } + } + } +} diff --git a/src/engine/texture-generator/method/SmoothNoise2D.hpp b/src/engine/texture-generator/method/SmoothNoise2D.hpp new file mode 100644 index 0000000..67f3e8d --- /dev/null +++ b/src/engine/texture-generator/method/SmoothNoise2D.hpp @@ -0,0 +1,24 @@ +/* Copyright (c) 2020 by Stan Fortoński */ + +#ifndef SMOOTH_NOISE_2D_HPP +#define SMOOTH_NOISE_2D_HPP 1 +#include "../TextureGenerator.hpp" +#include +#include + +namespace Engine +{ + typedef std::vector > NoiseData2D; + class SmoothNoise2D: public TextureGenerateMethod + { + unsigned zoom; + + double turbulence(const NoiseData2D & pattern, const unsigned & width, const unsigned & height, double x, double y) const; + double smooth(const NoiseData2D & pattern, const unsigned & width, const unsigned & height, double x1, double y1) const; + + public: + SmoothNoise2D(const unsigned & zoomLevel = 1): zoom(zoomLevel){if (zoom == 0) zoom = 1;} + virtual void fillData(std::vector & data, const unsigned & width, const unsigned & height, const unsigned & depth) const; + }; +} +#endif diff --git a/src/engine/texture-generator/method/SmoothNoise3D.cpp b/src/engine/texture-generator/method/SmoothNoise3D.cpp new file mode 100644 index 0000000..959f99c --- /dev/null +++ b/src/engine/texture-generator/method/SmoothNoise3D.cpp @@ -0,0 +1,74 @@ +/* Copyright (c) 2020 by Stan Fortoński */ +/* Based on: Computer Graphics Programming in OpenGL with C++ V. Scott Gordon | John Clevenger */ + +#include "SmoothNoise3D.hpp" + +namespace Engine +{ + double SmoothNoise3D::smooth(const NoiseData3D & pattern, const unsigned & width, const unsigned & height, const unsigned & depth, double x1, double y1, double z1) const + { + double fractX = x1 - (int)x1; + double fractY = y1 - (int)y1; + double fractZ = z1 - (int)z1; + int x2 = ((int)x1 + width + 1)%width; + int y2 = ((int)y1 + height + 1)%height; + int z2 = ((int)z1 + depth + 1)%depth; + + double value = 0.0; + value += (1-fractX) * (1-fractY) * (1-fractZ) * pattern[(int)x1][(int)y1][(int)z1]; + value += (1-fractX) * fractY * (1-fractZ) * pattern[(int)x1][(int)y2][(int)z1]; + value += fractX * (1-fractY) * (1-fractZ) * pattern[(int)x2][(int)y1][(int)z1]; + value += fractX * fractY * (1-fractZ) * pattern[(int)x2][(int)y2][(int)z1]; + + value += (1-fractX) * (1-fractY) * fractZ * pattern[(int)x1][(int)y1][(int)z2]; + value += (1-fractX) * fractY * fractZ * pattern[(int)x1][(int)y2][(int)z2]; + value += fractX * (1-fractY) * fractZ * pattern[(int)x2][(int)y1][(int)z2]; + value += fractX * fractY * fractZ * pattern[(int)x2][(int)y2][(int)z2]; + + return value; + }; + + double SmoothNoise3D::turbulence(const NoiseData3D & pattern, const unsigned & width, const unsigned & height, const unsigned & depth, double x, double y, double z) const + { + double sum = 0.0, actZoom = zoom; + while (actZoom >= 1.0) + { + sum = sum + smooth(pattern, width, height, depth, x/actZoom, y/actZoom, z/actZoom) * actZoom; + actZoom = actZoom/2.0; + } + sum = 128 * sum / zoom; + return sum; + } + + void SmoothNoise3D::fillData(std::vector & data, const unsigned & width, const unsigned & height, const unsigned & depth) const + { + NoiseData3D pattern; + for (unsigned w = 0; w < width; ++w) + { + pattern.push_back(std::vector >()); + for (unsigned h = 0; h < height; ++h) + { + pattern[w].push_back(std::vector()); + for (unsigned d = 0; d < depth; ++d) + pattern[w][h].push_back((double)rand()/(RAND_MAX + 1.0)); + } + } + + for (unsigned w = 0; w < width; ++w) + { + for (unsigned h = 0; h < height; ++h) + { + for (unsigned d = 0; d < depth; ++d) + { + unsigned index = w*width*height*4 + h*height*4 + d*4; + GLubyte value = (GLubyte) turbulence(pattern, width, height, depth, w, h, d); + + data[index] = value; + data[index+1] = value; + data[index+2] = value; + data[index+3] = (GLubyte) 255; + } + } + } + } +} diff --git a/src/engine/texture-generator/method/SmoothNoise3D.hpp b/src/engine/texture-generator/method/SmoothNoise3D.hpp new file mode 100644 index 0000000..b19d7f0 --- /dev/null +++ b/src/engine/texture-generator/method/SmoothNoise3D.hpp @@ -0,0 +1,24 @@ +/* Copyright (c) 2020 by Stan Fortoński */ + +#ifndef SMOOTH_NOISE_3D_HPP +#define SMOOTH_NOISE_3D_HPP 1 +#include "../TextureGenerator.hpp" +#include +#include + +namespace Engine +{ + typedef std::vector > > NoiseData3D; + class SmoothNoise3D: public TextureGenerateMethod + { + unsigned zoom; + + double turbulence(const NoiseData3D & pattern, const unsigned & width, const unsigned & height, const unsigned & depth, double x, double y, double z) const; + double smooth(const NoiseData3D & pattern, const unsigned & width, const unsigned & height, const unsigned & depth, double x1, double y1, double z1) const; + + public: + SmoothNoise3D(const unsigned & zoomLevel = 1): zoom(zoomLevel){if (zoom == 0) zoom = 1;} + virtual void fillData(std::vector & data, const unsigned & width, const unsigned & height, const unsigned & depth) const; + }; +} +#endif diff --git a/src/engine/window/Window.cpp b/src/engine/window/Window.cpp new file mode 100644 index 0000000..9280b24 --- /dev/null +++ b/src/engine/window/Window.cpp @@ -0,0 +1,69 @@ +/* Copyright (c) 2020 by Stan Fortoński */ +/* GLFW library for input/output */ + +#include "Window.hpp" + +namespace Engine +{ + void Window::create(bool fullscreen) + { + if (isCreate()) + return; + + Engine::get().initGLFW(); + + float samples = Config::get().getSamples(); + if (samples > 0) + glfwWindowHint(GLFW_SAMPLES, samples); + + if (fullscreen) + frame = glfwCreateWindow(width, height, title.c_str(), glfwGetPrimaryMonitor(), nullptr); + else frame = glfwCreateWindow(width, height, title.c_str(), nullptr, nullptr); + if (!frame) + throw std::runtime_error("Can\'t create GLFW window."); + glfwMakeContextCurrent(frame); + + Engine::get().initGLEW(); + Engine::get().initDefaultOptionsGL(); + + glViewport(0, 0, width, height); + } + + void Window::startRender() + { + if (loopCallback == nullptr) + loopCallback = []()->void{}; + + while (!glfwWindowShouldClose(frame)) + { + Engine::get().initDeltaTime(); + inputCallback(frame); + loopCallback(); + + #if DEBUG_ENGINE == 1 + Engine::get().showErrors(); + #endif + + glfwPollEvents(); + glfwSwapBuffers(frame); + } + } + + void Window::stopRender() + { + glfwSetWindowShouldClose(frame, true); + } + + void Window::setSize(const unsigned & w, const unsigned & h) + { + width = w; + height = h; + glViewport(0, 0, w, h); + } + + void Window::setTitle(const std::string & t) + { + title = t; + glfwSetWindowTitle(frame, title.c_str()); + } +} diff --git a/src/engine/window/Window.hpp b/src/engine/window/Window.hpp new file mode 100644 index 0000000..18609ba --- /dev/null +++ b/src/engine/window/Window.hpp @@ -0,0 +1,50 @@ +/* Copyright (c) 2020 by Stan Fortoński */ + +#ifndef WINDOW_HPP +#define WINDOW_HPP 1 +#include +#include "../Engine.hpp" + +namespace Engine +{ + class Window + { + GLFWwindow * frame = nullptr; + unsigned width; + unsigned height; + std::string title; + + void (*loopCallback)() = nullptr; + void (*inputCallback)(GLFWwindow *) = nullptr; + + public: + Window(const unsigned & pWidth = Config::get().getWindowWidth(), + const unsigned & pHeight = Config::get().getWindowHeight(), + const std::string & pTitle = Config::get().getTitle()): + width(pWidth), height(pHeight), title(pTitle){;} + + virtual ~Window(){glfwTerminate();} + + void create(bool fullscreen = false); + void setRenderMethod(void (*func)()){loopCallback = func;} + void startRender(); + void stopRender(); + void exit() const{glfwTerminate();} + + void setTitle(const std::string & t); + void setSize(const unsigned & w, const unsigned & h); + void setMode(const int & type, const int & value){glfwWindowHint(type, value);} + void setInputMode(const int & type, const int & value){glfwSetInputMode(frame, type, value);} + void setEventResize(void (*func)(GLFWwindow *, int, int)){glfwSetFramebufferSizeCallback(frame, func);} + void setEventKeyPress(void (*func)(GLFWwindow *, int, int, int, int)){glfwSetKeyCallback(frame, func);} + void setEventInput(void (*func)(GLFWwindow *)){inputCallback = func;} + void setEventMouseMove(void (*func)(GLFWwindow *, double, double)){glfwSetCursorPosCallback(frame, func);} + void setEventScroll(void (*func)(GLFWwindow *, double, double)){glfwSetScrollCallback(frame, func);} + + unsigned getWidth() const{return width;} + unsigned getHeight() const{return height;} + std::string getTitle() const{return title;} + bool isCreate() const{return frame != nullptr;} + }; +} +#endif diff --git a/src/stb_image.cpp b/src/stb_image.cpp new file mode 100644 index 0000000..badb3ef --- /dev/null +++ b/src/stb_image.cpp @@ -0,0 +1,2 @@ +#define STB_IMAGE_IMPLEMENTATION +#include "stb_image.h" \ No newline at end of file