├── .clang-format ├── .gitattributes ├── .gitignore ├── .gitmodules ├── CMakeLists.txt ├── FrameBuffer.cpp ├── FrameBuffer.h ├── GameTime.cpp ├── GameTime.h ├── InputState.h ├── Loader.cpp ├── Loader.h ├── Model.cpp ├── Model.h ├── README.md ├── ShadowMap.cpp ├── ShadowMap.h ├── Window.cpp ├── Window.h ├── constants.h ├── entities ├── Camera.cpp ├── Camera.h ├── Entity.cpp ├── Entity.h ├── Light.h ├── Player.cpp ├── Player.h ├── Terrain.cpp └── Terrain.h ├── extern ├── CMakeLists.txt ├── glad │ ├── CMakeLists.txt │ ├── include │ │ ├── KHR │ │ │ └── khrplatform.h │ │ └── glad │ │ │ └── glad.h │ └── src │ │ └── glad.c ├── stb_image │ ├── CMakeLists.txt │ ├── stb_image.cpp │ └── stb_image.h └── tiny_obj_loader │ ├── CMakeLists.txt │ ├── tiny_obj_loader.cpp │ └── tiny_obj_loader.h ├── glm_ext.h ├── main.cpp ├── particles ├── Particle.cpp ├── Particle.h ├── ParticleManager.cpp ├── ParticleManager.h ├── ParticleRenderer.cpp ├── ParticleRenderer.h ├── ParticleShader.cpp ├── ParticleShader.h ├── ParticleSystem.cpp ├── ParticleSystem.h ├── particle.frag └── particle.vert ├── renderers ├── EntityRenderer.cpp ├── EntityRenderer.h ├── RenderManager.cpp ├── RenderManager.h ├── SkyboxRenderer.cpp ├── SkyboxRenderer.h ├── TerrainRenderer.cpp └── TerrainRenderer.h ├── res ├── Barrel │ ├── Barrel.png │ ├── Barrel02.mtl │ └── Barrel02.obj ├── car │ ├── brakedis.jpg │ ├── car-n.mtl │ ├── car-n.obj │ ├── gen-trd.jpg │ ├── p206-bk.jpg │ ├── p206-fnt.jpg │ ├── p206-sd.jpg │ ├── p206-sd2.jpg │ ├── p206-tp.jpg │ ├── p206-wh2.jpg │ ├── rwheel.jpg │ └── spokes.jpg ├── cone │ ├── cone2.png │ ├── cone2_obj.mtl │ └── cone2_obj.obj ├── dust_single.png ├── fence │ ├── fence.mtl │ ├── fence.obj │ └── model │ │ └── fenceMaterial.png ├── pine │ ├── NewBark.jpg │ ├── PineNeedles.png │ ├── PineTransp.mtl │ └── PineTransp.obj ├── readme.txt ├── sky │ ├── sky_back.tga │ ├── sky_bottom.tga │ ├── sky_front.tga │ ├── sky_left.tga │ ├── sky_right.tga │ ├── sky_top.png │ └── sky_top.tga ├── stump │ ├── StumpBark02.png │ ├── StumpEnd02.png │ ├── TreeStump03.mtl │ └── TreeStump03.obj ├── terrain │ ├── blendMap.png │ ├── dirt.png │ ├── grass.jpg │ ├── heightmap.png │ ├── mud.jpg │ └── road.jpg ├── tree │ ├── PineTree03.mtl │ ├── PineTree03.obj │ ├── TreeBark.png │ └── leaves01.png └── water │ └── dudv.png ├── shaders ├── EntityShader.cpp ├── EntityShader.h ├── ShaderProgram.cpp ├── ShaderProgram.h ├── SkyboxShader.cpp ├── SkyboxShader.h ├── TerrainShader.cpp ├── TerrainShader.h ├── entity.frag ├── entity.vert ├── skybox.frag ├── skybox.vert ├── terrain.frag └── terrain.vert └── water ├── WaterRenderer.cpp ├── WaterRenderer.h ├── WaterShader.cpp ├── WaterShader.h ├── water.frag └── water.vert /.clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | Language: Cpp 3 | AlignAfterOpenBracket: DontAlign 4 | AlignConsecutiveAssignments: false 5 | AlignConsecutiveDeclarations: false 6 | AlignEscapedNewlines: Left 7 | AlignOperands: false 8 | AlignTrailingComments: true 9 | AllowAllParametersOfDeclarationOnNextLine: true 10 | AllowShortBlocksOnASingleLine: false 11 | AllowShortCaseLabelsOnASingleLine: false 12 | AllowShortFunctionsOnASingleLine: false 13 | AllowShortIfStatementsOnASingleLine: false 14 | AllowShortLoopsOnASingleLine: false 15 | AlwaysBreakAfterDefinitionReturnType: None 16 | AlwaysBreakAfterReturnType: None 17 | AlwaysBreakBeforeMultilineStrings: true 18 | AlwaysBreakTemplateDeclarations: true 19 | BinPackArguments: true 20 | BinPackParameters: true 21 | BraceWrapping: 22 | AfterClass: false 23 | AfterControlStatement: false 24 | AfterEnum: false 25 | AfterFunction: false 26 | AfterNamespace: false 27 | AfterObjCDeclaration: false 28 | AfterStruct: false 29 | AfterUnion: false 30 | BeforeCatch: false 31 | BeforeElse: false 32 | IndentBraces: false 33 | BreakBeforeBinaryOperators: None 34 | BreakBeforeBraces: Attach 35 | BreakBeforeTernaryOperators: true 36 | BreakConstructorInitializers: BeforeComma 37 | ColumnLimit: 120 38 | CommentPragmas: '^ IWYU pragma:' 39 | ConstructorInitializerAllOnOneLineOrOnePerLine: true 40 | ConstructorInitializerIndentWidth: 4 41 | ContinuationIndentWidth: 4 42 | Cpp11BracedListStyle: true 43 | DerivePointerAlignment: true 44 | DisableFormat: false 45 | ExperimentalAutoDetectBinPacking: false 46 | ForEachMacros: [ foreach, Q_FOREACH, BOOST_FOREACH ] 47 | IncludeCategories: 48 | - Regex: '^<.*\.h>' 49 | Priority: 1 50 | - Regex: '^<.*' 51 | Priority: 2 52 | - Regex: '.*' 53 | Priority: 3 54 | IndentCaseLabels: true 55 | IndentWidth: 4 56 | IndentWrappedFunctionNames: false 57 | KeepEmptyLinesAtTheStartOfBlocks: false 58 | MacroBlockBegin: '' 59 | MacroBlockEnd: '' 60 | MaxEmptyLinesToKeep: 1 61 | NamespaceIndentation: None 62 | ObjCBlockIndentWidth: 2 63 | ObjCSpaceAfterProperty: false 64 | ObjCSpaceBeforeProtocolList: false 65 | PenaltyBreakBeforeFirstCallParameter: 1 66 | PenaltyBreakComment: 300 67 | PenaltyBreakFirstLessLess: 120 68 | PenaltyBreakString: 1000 69 | PenaltyExcessCharacter: 1000000 70 | PenaltyReturnTypeOnItsOwnLine: 200 71 | PointerAlignment: Left 72 | ReflowComments: true 73 | SortIncludes: false 74 | SpaceAfterCStyleCast: false 75 | SpaceBeforeAssignmentOperators: true 76 | SpaceBeforeParens: ControlStatements 77 | SpaceInEmptyParentheses: false 78 | SpacesBeforeTrailingComments: 2 79 | SpacesInAngles: false 80 | SpacesInContainerLiterals: true 81 | SpacesInCStyleCastParentheses: false 82 | SpacesInParentheses: false 83 | SpacesInSquareBrackets: false 84 | Standard: Auto 85 | TabWidth: 8 86 | UseTab: Never 87 | ... 88 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | *.png filter=lfs diff=lfs merge=lfs -text 2 | *.jpg filter=lfs diff=lfs merge=lfs -text 3 | *.tga filter=lfs diff=lfs merge=lfs -text 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files 2 | *.slo 3 | *.lo 4 | *.o 5 | 6 | # Precompiled Headers 7 | *.gch 8 | *.pch 9 | 10 | # Compiled Dynamic libraries 11 | *.so 12 | *.dylib 13 | *.dll 14 | 15 | # Fortran module files 16 | *.mod 17 | *.smod 18 | 19 | # Compiled Static libraries 20 | *.lai 21 | *.la 22 | *.a 23 | *.lib 24 | 25 | # Executables 26 | *.exe 27 | *.out 28 | *.app 29 | driver 30 | 31 | # Build output directory 32 | build 33 | 34 | .idea 35 | cmake-build-debug -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "extern/fmt"] 2 | path = extern/fmt 3 | url = https://github.com/fmtlib/fmt.git 4 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.15) 2 | 3 | # Removes /W3 from default MSVC options so there isn't a warning when building. 4 | cmake_policy(SET CMP0092 NEW) 5 | project(opengl-car-game LANGUAGES C CXX) 6 | set(CMAKE_CXX_STANDARD 17) 7 | set(CMAKE_CXX_STANDARD_REQUIRED ON) 8 | set(CMAKE_CXX_EXTENSIONS OFF) 9 | 10 | add_subdirectory(extern EXCLUDE_FROM_ALL) 11 | 12 | set(all_headers 13 | constants.h 14 | entities/Camera.h 15 | entities/Entity.h 16 | entities/Light.h 17 | entities/Player.h 18 | entities/Terrain.h 19 | FrameBuffer.h 20 | GameTime.h 21 | glm_ext.h 22 | InputState.h 23 | Loader.h 24 | Model.h 25 | particles/Particle.h 26 | particles/ParticleManager.h 27 | particles/ParticleRenderer.h 28 | particles/ParticleShader.h 29 | particles/ParticleSystem.h 30 | renderers/EntityRenderer.h 31 | renderers/RenderManager.h 32 | renderers/SkyboxRenderer.h 33 | renderers/TerrainRenderer.h 34 | shaders/EntityShader.h 35 | shaders/ShaderProgram.h 36 | shaders/SkyboxShader.h 37 | shaders/TerrainShader.h 38 | ShadowMap.h 39 | water/WaterRenderer.h 40 | water/WaterShader.h 41 | Window.h 42 | ) 43 | 44 | set(all_sources 45 | entities/Camera.cpp 46 | entities/Entity.cpp 47 | entities/Player.cpp 48 | entities/Terrain.cpp 49 | FrameBuffer.cpp 50 | GameTime.cpp 51 | Loader.cpp 52 | main.cpp 53 | Model.cpp 54 | particles/Particle.cpp 55 | particles/ParticleManager.cpp 56 | particles/ParticleRenderer.cpp 57 | particles/ParticleShader.cpp 58 | particles/ParticleSystem.cpp 59 | renderers/EntityRenderer.cpp 60 | renderers/RenderManager.cpp 61 | renderers/SkyboxRenderer.cpp 62 | renderers/TerrainRenderer.cpp 63 | shaders/EntityShader.cpp 64 | shaders/ShaderProgram.cpp 65 | shaders/SkyboxShader.cpp 66 | shaders/TerrainShader.cpp 67 | ShadowMap.cpp 68 | water/WaterRenderer.cpp 69 | water/WaterShader.cpp 70 | Window.cpp 71 | ) 72 | 73 | find_package(OpenGL REQUIRED) 74 | find_package(glfw3 REQUIRED) 75 | find_package(glm REQUIRED) 76 | 77 | add_executable(opengl-car-game ${all_sources} ${all_headers}) 78 | target_link_libraries(opengl-car-game PRIVATE glad::glad OpenGL::GL glfw fmt::fmt stb_image tiny_obj_loader ${CMAKE_DL_LIBS}) 79 | target_include_directories(opengl-car-game PUBLIC ${CMAKE_CURRENT_LIST_DIR}) 80 | 81 | # Vcpkg exports the target, but the default find_package does not. This is a 82 | # quick hack since the dependencies on Windows are usually with vcpkg and usually 83 | # apt (or some other system package manager) on Ubuntu 84 | if(MSVC) 85 | target_link_libraries(opengl-car-game PRIVATE glm) 86 | else() 87 | target_include_directories(opengl-car-game PRIVATE ${GLM_INCLUDE_DIRS}) 88 | endif() 89 | 90 | # Turn on more warnings 91 | if(MSVC) 92 | target_compile_options(opengl-car-game PRIVATE /W4) 93 | else(MSVC) 94 | target_compile_options(opengl-car-game PRIVATE -Wall -Wextra -Wpedantic) 95 | endif(MSVC) 96 | 97 | set_target_properties(opengl-car-game PROPERTIES VS_GLOBAL_RunCodeAnalysis "false") 98 | set_target_properties(opengl-car-game PROPERTIES VS_GLOBAL_EnableClangTidyCodeAnalysis "true") 99 | set_target_properties(opengl-car-game PROPERTIES VS_GLOBAL_ClangTidyChecks "-*,clang-analyzer-*,-clang-analyzer-cplusplus*,modernize-*,performance-*,readability-*,-modernize-use-trailing-return-type,-readability-uppercase-literal-suffix") 100 | 101 | # Copy assets 102 | add_custom_command(TARGET opengl-car-game POST_BUILD 103 | COMMAND ${CMAKE_COMMAND} -E copy_directory 104 | ${CMAKE_CURRENT_LIST_DIR}/res/ 105 | ${PROJECT_BINARY_DIR}/res/ 106 | COMMENT "Copy resources to build tree") 107 | 108 | function(target_add_shader target out_dir) 109 | foreach(source_shader_file ${ARGN}) 110 | get_filename_component(shader_file_name ${source_shader_file} NAME) 111 | set(out_shader_file "${out_dir}${shader_file_name}") 112 | file(MAKE_DIRECTORY ${out_dir}) 113 | file(TOUCH ${out_shader_file}) 114 | file(CREATE_LINK ${source_shader_file} ${out_shader_file}) 115 | endforeach() 116 | endfunction() 117 | 118 | # Copy shaders 119 | target_add_shader(opengl-car-game ${PROJECT_BINARY_DIR}/water/ 120 | ${CMAKE_CURRENT_LIST_DIR}/water/water.frag 121 | ${CMAKE_CURRENT_LIST_DIR}/water/water.vert 122 | ) 123 | 124 | target_add_shader(opengl-car-game ${PROJECT_BINARY_DIR}/particles/ 125 | ${CMAKE_CURRENT_LIST_DIR}/particles/particle.frag 126 | ${CMAKE_CURRENT_LIST_DIR}/particles/particle.vert 127 | ) 128 | 129 | target_add_shader(opengl-car-game ${PROJECT_BINARY_DIR}/shaders/ 130 | ${CMAKE_CURRENT_LIST_DIR}/shaders/entity.frag 131 | ${CMAKE_CURRENT_LIST_DIR}/shaders/entity.vert 132 | ${CMAKE_CURRENT_LIST_DIR}/shaders/skybox.frag 133 | ${CMAKE_CURRENT_LIST_DIR}/shaders/skybox.vert 134 | ${CMAKE_CURRENT_LIST_DIR}/shaders/terrain.frag 135 | ${CMAKE_CURRENT_LIST_DIR}/shaders/terrain.vert 136 | ) 137 | -------------------------------------------------------------------------------- /FrameBuffer.cpp: -------------------------------------------------------------------------------- 1 | #include "FrameBuffer.h" 2 | 3 | FrameBuffer::FrameBuffer(uint32_t width, uint32_t height) 4 | : depthTexture(0), depthBuffer(0), colourTexture(0), width(width), height(height) { 5 | glGenFramebuffers(1, &framebufferID); 6 | } 7 | 8 | void FrameBuffer::addColourTexture() { 9 | bind(); 10 | glGenTextures(1, &colourTexture); 11 | 12 | glBindTexture(GL_TEXTURE_2D, colourTexture); 13 | glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, 0); 14 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); 15 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); 16 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); 17 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); 18 | glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, colourTexture, 0); 19 | 20 | // Isn't necessary at the moment but I feel like it should be 21 | // GLenum DrawBuffers[1] = {GL_COLOR_ATTACHMENT0}; 22 | // glDrawBuffers(1, DrawBuffers); // "1" is the size of DrawBuffers 23 | 24 | unbind(); 25 | } 26 | 27 | void FrameBuffer::addDepthTexture() { 28 | bind(); 29 | glGenTextures(1, &depthTexture); 30 | 31 | glBindTexture(GL_TEXTURE_2D, depthTexture); 32 | glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT32, width, height, 0, GL_DEPTH_COMPONENT, GL_FLOAT, 0); 33 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); 34 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); 35 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); 36 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); 37 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_REF_TO_TEXTURE); 38 | glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, depthTexture, 0); 39 | 40 | unbind(); 41 | } 42 | 43 | void FrameBuffer::addDepthBuffer() { 44 | bind(); 45 | glGenRenderbuffers(1, &depthBuffer); 46 | glBindRenderbuffer(GL_RENDERBUFFER, depthBuffer); 47 | glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, width, height); 48 | glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, depthBuffer); 49 | unbind(); 50 | } 51 | 52 | bool FrameBuffer::isOkay() { 53 | bind(); 54 | bool result = true; 55 | if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { 56 | result = false; 57 | } 58 | unbind(); 59 | return result; 60 | } 61 | 62 | GLuint FrameBuffer::getColourTexture() { 63 | return colourTexture; 64 | } 65 | 66 | GLuint FrameBuffer::getDepthTexture() { 67 | return depthTexture; 68 | } 69 | 70 | GLuint FrameBuffer::getDepthBuffer() { 71 | return depthBuffer; 72 | } 73 | 74 | int FrameBuffer::getWidth() { 75 | return width; 76 | } 77 | 78 | int FrameBuffer::getHeight() { 79 | return height; 80 | } 81 | 82 | void FrameBuffer::bind() { 83 | glBindFramebuffer(GL_FRAMEBUFFER, framebufferID); 84 | glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); 85 | glViewport(0, 0, width, height); 86 | } 87 | 88 | void FrameBuffer::unbind() { 89 | glBindFramebuffer(GL_FRAMEBUFFER, 0); 90 | } 91 | -------------------------------------------------------------------------------- /FrameBuffer.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | class FrameBuffer { 7 | private: 8 | GLuint framebufferID; 9 | 10 | GLuint depthTexture; 11 | GLuint depthBuffer; 12 | GLuint colourTexture; 13 | 14 | GLuint width; 15 | GLuint height; 16 | 17 | public: 18 | FrameBuffer(uint32_t width, uint32_t height); 19 | void addColourTexture(); 20 | void addDepthTexture(); 21 | void addDepthBuffer(); 22 | bool isOkay(); 23 | 24 | GLuint getColourTexture(); 25 | GLuint getDepthTexture(); 26 | GLuint getDepthBuffer(); 27 | 28 | int getWidth(); 29 | int getHeight(); 30 | 31 | virtual void bind(); 32 | virtual void unbind(); 33 | }; 34 | -------------------------------------------------------------------------------- /GameTime.cpp: -------------------------------------------------------------------------------- 1 | #include "GameTime.h" 2 | 3 | #include 4 | 5 | // Initialise singleton 6 | GameTime* GameTime::gameTime = nullptr; 7 | 8 | GameTime::GameTime() { 9 | initialTime = glfwGetTime(); 10 | lastTime = -1; 11 | currentTime = -1; 12 | } 13 | 14 | GameTime* GameTime::getGameTime() { 15 | if (gameTime == nullptr) { 16 | gameTime = new GameTime(); 17 | } 18 | 19 | return gameTime; 20 | } 21 | 22 | void GameTime::update() { 23 | lastTime = currentTime; 24 | currentTime = glfwGetTime(); 25 | } 26 | 27 | float GameTime::getDt() const { 28 | return static_cast(currentTime - lastTime); 29 | } 30 | 31 | float GameTime::getFPS() const { 32 | return static_cast(1.f / getDt()); 33 | } 34 | -------------------------------------------------------------------------------- /GameTime.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | class GameTime { 4 | private: 5 | static GameTime* gameTime; 6 | GameTime(); 7 | 8 | double initialTime; 9 | double lastTime; 10 | double currentTime; 11 | 12 | public: 13 | static GameTime* getGameTime(); 14 | 15 | void update(); // Should be called once per frame 16 | [[nodiscard]] float getDt() const; 17 | [[nodiscard]] float getFPS() const; 18 | }; 19 | -------------------------------------------------------------------------------- /InputState.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define _USE_MATH_DEFINES 4 | 5 | struct InputState { 6 | InputState() 7 | : lMousePressed(false) 8 | , rMousePressed(false) 9 | , prevX(0) 10 | , prevY(0) 11 | , deltaX(0) 12 | , deltaY(0) 13 | , scrollDeltaX(0) 14 | , scrollDeltaY(0) { 15 | } 16 | 17 | // Is the mouse button currently being held down? 18 | bool lMousePressed; 19 | bool rMousePressed; 20 | 21 | // Last known position of the cursor 22 | float prevX; 23 | float prevY; 24 | 25 | // Accumulated change in cursor position. 26 | float deltaX; 27 | float deltaY; 28 | 29 | float scrollDeltaX; 30 | float scrollDeltaY; 31 | 32 | // Update cursor variables based on new position x,y 33 | void update(float x, float y) { 34 | float xDiff = x - prevX; 35 | float yDiff = y - prevY; 36 | deltaX += xDiff; 37 | deltaY += yDiff; 38 | prevX = x; 39 | prevY = y; 40 | }; 41 | 42 | void updateScroll(float x, float y) { 43 | scrollDeltaX += x; 44 | scrollDeltaY += y; 45 | }; 46 | 47 | // Read off the accumulated motion and reset it 48 | void readDeltaAndReset(float* x, float* y) { 49 | *x = deltaX; 50 | *y = deltaY; 51 | deltaX = 0; 52 | deltaY = 0; 53 | }; 54 | }; 55 | -------------------------------------------------------------------------------- /Loader.cpp: -------------------------------------------------------------------------------- 1 | #include "Loader.h" 2 | 3 | #include 4 | 5 | #define VALS_PER_VERT 3 6 | #define VALS_PER_NORMAL 3 7 | #define VALS_PER_TEX 2 8 | 9 | Image Image::loadFromFile(std::string_view filePath) { 10 | Image image; 11 | const std::string definitelyNullTerminatedFilePath(filePath); 12 | image.data = stbi_load(definitelyNullTerminatedFilePath.c_str(), // char* filepath 13 | &image.width, // The address to store the width of the image 14 | &image.height, // The address to store the height of the image 15 | &image.channels, // Number of channels in the image 16 | 0 // Force number of channels if > 0 17 | ); 18 | return image; 19 | } 20 | 21 | Image::Image() : data(nullptr), width(-1), height(-1), channels(-1) { 22 | } 23 | 24 | Image::~Image() { 25 | stbi_image_free(data); 26 | } 27 | 28 | Image::Image(unsigned char* data, int width, int height, int channels) 29 | : data(data), width(width), height(height), channels(channels) { 30 | } 31 | 32 | glm::vec3 Image::getPixel(int x, int y) const { 33 | if (x < 0 || x >= width || y < 0 || y >= height) { 34 | return glm::vec3(-1.0f, -1.0f, 35 | -1.0f); // Returns a vector of -1 to indicate the image does not contain a pixel at that location. 36 | } 37 | int offset = 38 | ((width * y) + x) * channels; // Determine the position in data to start reading. Each pixel is 1 byte. 39 | return glm::vec3((float)data[offset] / 255, (float)data[offset + 1] / 255, (float)data[offset + 2] / 255); 40 | } 41 | 42 | // Initialise Loader singleton 43 | Loader* Loader::loader = nullptr; 44 | 45 | Loader* Loader::getLoader() { 46 | if (loader == nullptr) { 47 | loader = new Loader(); 48 | } 49 | 50 | return loader; 51 | } 52 | 53 | glm::vec3 getVertex(const std::vector& vertices, int index) { 54 | int pos = index * 3; 55 | return glm::vec3(vertices[pos], vertices[pos + 1], vertices[pos + 2]); 56 | } 57 | 58 | std::vector Loader::generateNormals( 59 | const std::vector& vertices, const std::vector& indices) { 60 | std::vector resultNormals(vertices.size(), 0.0f); 61 | 62 | // Iterate over each triangle as defined in indices 63 | for (size_t i = 0; i < indices.size(); i += 3) { 64 | glm::vec3 vert1 = getVertex(vertices, indices[i]); 65 | glm::vec3 vert2 = getVertex(vertices, indices[i + 1]); 66 | glm::vec3 vert3 = getVertex(vertices, indices[i + 2]); 67 | 68 | glm::vec3 v1 = vert2 - vert1; 69 | glm::vec3 v2 = vert3 - vert1; 70 | 71 | glm::vec3 faceNormal = glm::cross(v1, v2); 72 | faceNormal = glm::normalize(faceNormal); 73 | 74 | // Iterate over each vertex in that triangle 75 | for (size_t j = i; j < i + 3; j++) { 76 | glm::vec3 vertNormal(resultNormals[indices[j] * 3], resultNormals[(indices[j] * 3) + 1], 77 | resultNormals[(indices[j] * 3) + 2]); 78 | 79 | // Add the current faces normal to the vertexes normal 80 | vertNormal = glm::normalize(vertNormal + faceNormal); 81 | 82 | resultNormals[indices[j] * 3] = vertNormal.x; 83 | resultNormals[(indices[j] * 3) + 1] = vertNormal.y; 84 | resultNormals[(indices[j] * 3) + 2] = vertNormal.z; 85 | } 86 | } 87 | 88 | return resultNormals; 89 | } 90 | 91 | // http://stackoverflow.com/questions/12774207/fastest-way-to-check-if-a-file-exist-using-standard-c-c11-c 92 | inline bool Loader::fileExists(const std::string& name) { 93 | struct stat buffer{}; 94 | return (stat(name.c_str(), &buffer) == 0); 95 | } 96 | 97 | Model Loader::loadModel(const std::string& model_file) { 98 | // Declare containers for object values 99 | std::vector shapes; 100 | std::vector materials; 101 | 102 | // If the object file is in a different directory, the material file path must be specified. 103 | // Assumes material file is in the same directory as obj 104 | std::string model_file_path; 105 | if (model_file.find('/') != std::string::npos) { 106 | model_file_path = model_file.substr(0, model_file.find_last_of("\\/") + 1); 107 | } 108 | 109 | // Load object 110 | std::string err; 111 | bool succeeded = tinyobj::LoadObj(shapes, materials, err, model_file.c_str(), model_file_path.c_str()); 112 | if (!err.empty()) { 113 | std::cerr << err << std::endl; 114 | } 115 | if (!succeeded) { 116 | exit(1); 117 | } 118 | 119 | return loadModel(shapes, materials, model_file_path); 120 | } 121 | 122 | Model Loader::loadModel(const std::vector& shapes, const std::vector& materials, 123 | const std::string& materialpath) { 124 | Model model; 125 | for (const auto& shape : shapes) { 126 | ModelComponent component = loadModelComponent(shape, materials, materialpath); 127 | model.addRange(shape.mesh.positions); 128 | model.addModelComponent(component); 129 | } 130 | return model; 131 | } 132 | 133 | ModelComponent Loader::loadModelComponent( 134 | const tinyobj::shape_t& shape, const std::vector& materials, const std::string& materialpath) { 135 | GLuint vao = loadVAO(shape); 136 | size_t numIndices = shape.mesh.indices.size(); 137 | 138 | // TODO - revisit this. Likely a result of the file not loading on windows requiring this, meaning no textures can 139 | // load. 140 | tinyobj::material_t material; 141 | initMaterial(material); 142 | if (!shape.mesh.material_ids.empty() && shape.mesh.material_ids[0] != -1) { 143 | material = materials[shape.mesh.material_ids[0]]; // Loads the first material 144 | } 145 | GLuint textureID = loadTexture(materialpath + material.diffuse_texname); 146 | 147 | return ModelComponent(vao, numIndices, textureID, material); 148 | } 149 | 150 | ModelComponent Loader::loadModelComponent( 151 | const std::vector& vertices, const std::vector& indices, const std::vector& texCoords) { 152 | GLuint vao = loadVAO(vertices, indices, texCoords); 153 | size_t numIndices = indices.size(); 154 | GLuint textureID = loadDefaultTexture(); 155 | 156 | return ModelComponent(vao, numIndices, textureID); 157 | } 158 | 159 | ModelComponent Loader::loadModelComponent(const std::vector& vertices, const std::vector& indices, 160 | const std::vector& texCoords, const std::vector& normals) { 161 | GLuint vao = loadVAO(vertices, indices, texCoords, normals); 162 | size_t numIndices = indices.size(); 163 | GLuint textureID = loadDefaultTexture(); 164 | 165 | return ModelComponent(vao, numIndices, textureID); 166 | } 167 | 168 | ModelComponent Loader::loadModelComponent(const std::vector& vertices, const std::vector& indices, 169 | const std::vector& texCoords, const std::string& texturepath) { 170 | GLuint vao = loadVAO(vertices, indices, texCoords); 171 | size_t numIndices = indices.size(); 172 | GLuint textureID = loadTexture(texturepath); 173 | 174 | return ModelComponent(vao, numIndices, textureID); 175 | } 176 | 177 | ModelComponent Loader::loadModelComponent(const std::vector& vertices, const std::vector& indices, 178 | const std::vector& texCoords, const std::vector& normals, const std::string& texturepath) { 179 | GLuint vao = loadVAO(vertices, indices, texCoords, normals); 180 | size_t numIndices = indices.size(); 181 | GLuint textureID = loadTexture(texturepath); 182 | 183 | return ModelComponent(vao, numIndices, textureID); 184 | } 185 | 186 | GLuint Loader::loadVAO( 187 | const std::vector& vertices, const std::vector& indices, const std::vector& texCoords) { 188 | GLuint vaoHandle; 189 | glGenVertexArrays(1, &vaoHandle); 190 | glBindVertexArray(vaoHandle); 191 | 192 | unsigned int buffer[3]; 193 | glGenBuffers(3, buffer); 194 | 195 | setupBuffer(buffer[0], vertices, 0, VALS_PER_VERT); 196 | setupBuffer(buffer[1], texCoords, 1, VALS_PER_TEX); 197 | setupIndicesBuffer(buffer[2], indices); 198 | 199 | glBindVertexArray(0); 200 | glBindBuffer(GL_ARRAY_BUFFER, 0); 201 | return vaoHandle; 202 | } 203 | 204 | GLuint Loader::loadVAO(const std::vector& vertices, const std::vector& indices) { 205 | GLuint vaoHandle; 206 | glGenVertexArrays(1, &vaoHandle); 207 | glBindVertexArray(vaoHandle); 208 | 209 | unsigned int buffer[2]; 210 | glGenBuffers(2, buffer); 211 | 212 | setupBuffer(buffer[0], vertices, 0, VALS_PER_VERT); 213 | setupIndicesBuffer(buffer[1], indices); 214 | 215 | glBindVertexArray(0); 216 | glBindBuffer(GL_ARRAY_BUFFER, 0); 217 | return vaoHandle; 218 | } 219 | 220 | GLuint Loader::loadVAO(const std::vector& vertices, const std::vector& indices, 221 | const std::vector& texCoords, const std::vector& normals) { 222 | GLuint vaoHandle; 223 | glGenVertexArrays(1, &vaoHandle); 224 | glBindVertexArray(vaoHandle); 225 | 226 | unsigned int buffer[4]; 227 | glGenBuffers(4, buffer); 228 | 229 | setupBuffer(buffer[0], vertices, 0, VALS_PER_VERT); 230 | setupBuffer(buffer[1], normals, 1, VALS_PER_NORMAL); 231 | setupBuffer(buffer[2], texCoords, 2, VALS_PER_TEX); 232 | setupIndicesBuffer(buffer[3], indices); 233 | 234 | glBindVertexArray(0); 235 | glBindBuffer(GL_ARRAY_BUFFER, 0); 236 | return vaoHandle; 237 | } 238 | 239 | GLuint Loader::setupBuffer( 240 | unsigned int buffer, const std::vector& values, int attributeIndex, int dataDimension) { 241 | glBindBuffer(GL_ARRAY_BUFFER, buffer); 242 | glBufferData(GL_ARRAY_BUFFER, sizeof(float) * values.size(), &values[0], GL_STATIC_DRAW); 243 | glVertexAttribPointer(attributeIndex, dataDimension, GL_FLOAT, GL_FALSE, 0, 0); 244 | 245 | return buffer; 246 | } 247 | 248 | GLuint Loader::setupIndicesBuffer(unsigned int buffer, const std::vector& values) { 249 | glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffer); 250 | glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(unsigned int) * values.size(), &values[0], GL_STATIC_DRAW); 251 | return buffer; 252 | } 253 | 254 | GLuint Loader::loadVAO(const tinyobj::shape_t& shape) { 255 | // If texcoords is null, set it to some dummy values 256 | if (shape.mesh.texcoords.empty()) { 257 | auto shapeCopy = shape; 258 | std::vector texVec; 259 | for (size_t i = 0; i < shapeCopy.mesh.positions.size(); i += 3) { 260 | texVec.push_back(0.0f); 261 | texVec.push_back(0.0f); 262 | } 263 | shapeCopy.mesh.texcoords = texVec; 264 | // Call this function again with a shape that has textcoords. 265 | return loadVAO(shapeCopy); 266 | } 267 | 268 | return loadVAO(shape.mesh.positions, shape.mesh.indices, shape.mesh.texcoords, shape.mesh.normals); 269 | } 270 | 271 | GLuint Loader::loadCubemapTexture(const std::vector& filenames) { 272 | if (filenames.size() != 6) { 273 | std::cerr << "[Loader][Error] Cubemap requires 6 texture files." << std::endl; 274 | exit(1); 275 | } 276 | 277 | GLuint textureID; 278 | glActiveTexture(GL_TEXTURE0); 279 | glGenTextures(1, &textureID); 280 | glBindTexture(GL_TEXTURE_CUBE_MAP, textureID); 281 | 282 | for (size_t i = 0; i < filenames.size(); i++) { 283 | std::cout << "[Loader] loading: " << filenames[i] << std::endl; 284 | if (!fileExists(filenames[i])) { 285 | std::cerr << "[Loader][Error] Skybox texture file " << i << " doesnt exist." << std::endl; 286 | } 287 | 288 | Image image = Image::loadFromFile(filenames[i]); 289 | 290 | GLenum format = GL_RGB; 291 | if (image.channels == 4) { 292 | format = GL_RGBA; 293 | } 294 | 295 | // The first cast works because the enums are defined sequentially. 296 | glTexImage2D(static_cast(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i), 0, GL_RGB, image.width, image.height, 0, 297 | format, GL_UNSIGNED_BYTE, image.data); 298 | glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR); 299 | glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR); 300 | glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); 301 | glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); 302 | glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); 303 | } 304 | 305 | return textureID; 306 | } 307 | 308 | GLuint Loader::loadTexture(const std::string& filepath) { 309 | if (loadedTextures.count(filepath)) { 310 | std::cout << "[Loader] '" << filepath << "' already loaded, using cached texture." << std::endl; 311 | return loadedTextures[filepath]; 312 | } 313 | 314 | std::cout << "[Loader] loading: " << filepath << std::endl; 315 | if (!fileExists(filepath)) { 316 | std::cerr << "[Loader] File doesnt exist, loading default texture." << std::endl; 317 | return loadDefaultTexture(); 318 | } 319 | 320 | // Load an image from file as texture 321 | const Image image = Image::loadFromFile(filepath); 322 | 323 | GLuint textureID = loadTextureData(image.data, image.width, image.height, image.channels, GL_TEXTURE0); 324 | 325 | // Save texture to cache 326 | loadedTextures[filepath] = textureID; 327 | 328 | return textureID; 329 | } 330 | 331 | GLuint Loader::loadTextureData(GLubyte* data, int x, int y, int n, GLenum textureUnit) { 332 | GLuint textureID; 333 | 334 | glActiveTexture(textureUnit); 335 | glGenTextures(1, &textureID); 336 | glBindTexture(GL_TEXTURE_2D, textureID); 337 | GLenum format = GL_RGB; 338 | 339 | // If there are four channels include alpha 340 | if (n == 4) { 341 | format = GL_RGBA; 342 | } 343 | 344 | glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, x, y, 0, format, GL_UNSIGNED_BYTE, data); 345 | 346 | // Set texture parameters 347 | glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); 348 | glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); 349 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); 350 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR_MIPMAP_LINEAR); 351 | glGenerateMipmap(GL_TEXTURE_2D); 352 | 353 | return textureID; 354 | } 355 | 356 | GLuint Loader::loadDefaultTexture() { 357 | if (loadedTextures.count("DEFAULT_TEXTURE")) { 358 | std::cout << "[Loader] 'DEFAULT_TEXTURE' already loaded, using cached texture." << std::endl; 359 | return loadedTextures["DEFAULT_TEXTURE"]; 360 | } 361 | 362 | const int SIZE = 64; 363 | GLubyte myimage[SIZE][SIZE][3]; 364 | 365 | // Create checkerboard image as the default texture 366 | for (size_t i = 0; i < SIZE; i++) { 367 | for (size_t j = 0; j < SIZE; j++) { 368 | GLubyte c; 369 | c = (((i & 0x8) == 0) ^ ((j & 0x8) == 0)) * 255; 370 | myimage[i][j][0] = c; 371 | myimage[i][j][1] = c; 372 | myimage[i][j][2] = c; 373 | } 374 | } 375 | 376 | GLuint textureID = loadTextureData(&myimage[0][0][0], SIZE, SIZE, 3, GL_TEXTURE0); 377 | loadedTextures["DEFAULT_TEXTURE"] = textureID; 378 | 379 | return textureID; 380 | } 381 | -------------------------------------------------------------------------------- /Loader.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Model.h" 4 | #include 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | struct Image { 15 | static Image loadFromFile(std::string_view filePath); 16 | 17 | Image(); 18 | ~Image(); 19 | Image(const Image& other) = delete; 20 | 21 | Image(Image&& other) noexcept 22 | : data(other.data), width(other.width), height(other.height), channels(other.channels) { 23 | other.data = nullptr; 24 | } 25 | 26 | Image& operator=(const Image& other) = delete; 27 | 28 | Image& operator=(Image&& other) noexcept { 29 | if (this == &other) 30 | return *this; 31 | data = other.data; 32 | width = other.width; 33 | height = other.height; 34 | channels = other.channels; 35 | other.data = nullptr; 36 | return *this; 37 | } 38 | 39 | unsigned char* data; 40 | int width; 41 | int height; 42 | int channels; 43 | 44 | [[nodiscard]] glm::vec3 getPixel(int x, int y) const; 45 | 46 | private: 47 | 48 | Image(unsigned char* data, int width, int height, int channels); 49 | }; 50 | 51 | class Loader { 52 | private: 53 | static Loader* loader; 54 | Loader() = default; 55 | 56 | // Stores the file/id mapping for each loaded texture to use for caching. 57 | std::map loadedTextures; 58 | static GLuint loadTextureData(GLubyte* data, int x, int y, int n, GLenum textureUnit); 59 | static GLuint setupBuffer(unsigned int buffer, const std::vector& values, int attributeIndex, int dataDimension); 60 | static GLuint setupIndicesBuffer(unsigned int buffer, const std::vector& values); 61 | 62 | public: 63 | static Loader* getLoader(); 64 | 65 | static std::vector generateNormals(const std::vector& vertices, const std::vector& indices); 66 | 67 | static bool fileExists(const std::string& name); 68 | Model loadModel(const std::string& filepath); 69 | Model loadModel(const std::vector& shapes, const std::vector& materials, 70 | const std::string& materialpath); 71 | ModelComponent loadModelComponent( 72 | const tinyobj::shape_t&, const std::vector& materials, const std::string& materialpath); 73 | ModelComponent loadModelComponent(const std::vector& vertices, const std::vector& indices, 74 | const std::vector& texCoords); 75 | ModelComponent loadModelComponent(const std::vector& vertices, const std::vector& indices, 76 | const std::vector& texCoords, const std::vector& normals); 77 | ModelComponent loadModelComponent(const std::vector& vertices, const std::vector& indices, 78 | const std::vector& texCoords, const std::string& texturepath); 79 | ModelComponent loadModelComponent(const std::vector& vertices, const std::vector& indices, 80 | const std::vector& texCoords, const std::vector& normals, const std::string& texturepath); 81 | 82 | static GLuint loadVAO(const std::vector& vertices, const std::vector& indices); 83 | static GLuint loadVAO(const std::vector& vertices, const std::vector& indices, 84 | const std::vector& texCoords); 85 | static GLuint loadVAO(const std::vector& vertices, const std::vector& indices, 86 | const std::vector& texCoords, const std::vector& normals); 87 | GLuint loadVAO(const tinyobj::shape_t&); 88 | 89 | static GLuint loadCubemapTexture(const std::vector& filenames); 90 | GLuint loadTexture(const std::string& filepath); 91 | GLuint loadDefaultTexture(); 92 | }; 93 | -------------------------------------------------------------------------------- /Model.cpp: -------------------------------------------------------------------------------- 1 | #include "Model.h" 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | // Taken from tiny_obj_loader.h as its hidden in a different namespace in the implementation. 8 | void initMaterial(tinyobj::material_t& material) { 9 | material.name = ""; 10 | material.ambient_texname = ""; 11 | material.diffuse_texname = ""; 12 | material.specular_texname = ""; 13 | material.specular_highlight_texname = ""; 14 | material.bump_texname = ""; 15 | material.displacement_texname = ""; 16 | material.alpha_texname = ""; 17 | for (int i = 0; i < 3; i++) { 18 | material.ambient[i] = 0.f; 19 | material.diffuse[i] = 0.f; 20 | material.specular[i] = 0.f; 21 | material.transmittance[i] = 0.f; 22 | material.emission[i] = 0.f; 23 | } 24 | material.illum = 0; 25 | material.dissolve = 1.f; 26 | material.shininess = 1.f; 27 | material.ior = 1.f; 28 | material.unknown_parameter.clear(); 29 | } 30 | 31 | ModelComponent::ModelComponent(GLuint vaoID, size_t indexCount, GLuint textureID, const tinyobj::material_t& material) { 32 | this->vaoID = vaoID; 33 | this->indexCount = indexCount; 34 | this->textureID = textureID; 35 | this->material = material; 36 | } 37 | ModelComponent::ModelComponent(GLuint vaoID, size_t indexCount, GLuint textureID) { 38 | this->vaoID = vaoID; 39 | this->indexCount = indexCount; 40 | this->textureID = textureID; 41 | this->material.specular[0] = 1.0f; 42 | this->material.specular[1] = 0.0f; 43 | this->material.specular[2] = 1.0f; 44 | initMaterial(this->material); 45 | } 46 | ModelComponent::ModelComponent() { 47 | this->vaoID = 0; 48 | this->indexCount = 0; 49 | this->textureID = 0; 50 | initMaterial(this->material); 51 | } 52 | 53 | size_t ModelComponent::getIndexCount() const { 54 | return indexCount; 55 | } 56 | 57 | GLuint ModelComponent::getVaoID() const { 58 | return vaoID; 59 | } 60 | 61 | GLuint ModelComponent::getTextureID() const { 62 | return textureID; 63 | } 64 | 65 | tinyobj::material_t ModelComponent::getMaterial() const { 66 | return material; 67 | } 68 | 69 | Model::Model(const std::vector& components) { 70 | this->components = components; 71 | for (int i = 0; i < 3; ++i) { 72 | maxRanges.push_back(FLT_MAX); 73 | maxRanges.push_back(-FLT_MAX); 74 | } 75 | } 76 | 77 | // Adds the vertices into the range stored for this model. 78 | void Model::addRange(const std::vector& vertices) { 79 | for (int dim = 0; dim < 3; ++dim) { 80 | for (size_t j = dim; j < vertices.size(); j += 3) { 81 | maxRanges[2 * dim] = std::min(vertices[j], maxRanges[2 * dim]); 82 | maxRanges[2 * dim + 1] = std::max(vertices[j], maxRanges[2 * dim + 1]); 83 | } 84 | } 85 | } 86 | 87 | // [xMin, xMax, yMin, yMax, zMin, zMax] 88 | void Model::setRange( 89 | const std::tuple& /*minXYZ*/, const std::tuple& /*maxXYZ*/) { 90 | throw std::runtime_error("Not implemented"); 91 | } 92 | 93 | std::pair Model::getRangeInDim(int dim) const { 94 | return std::make_pair(maxRanges[2 * dim], maxRanges[2 * dim + 1]); 95 | } 96 | 97 | Model::Model() { 98 | for (int i = 0; i < 3; ++i) { 99 | maxRanges.push_back(FLT_MAX); 100 | maxRanges.push_back(-FLT_MAX); 101 | } 102 | } 103 | 104 | void Model::addModelComponent(const ModelComponent& component) { 105 | components.push_back(component); 106 | } 107 | 108 | const std::vector& Model::getModelComponents() const { 109 | return components; 110 | } 111 | -------------------------------------------------------------------------------- /Model.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include 7 | 8 | void initMaterial(tinyobj::material_t& material); 9 | 10 | // Represents a single mesh/shape/vao 11 | class ModelComponent { 12 | private: 13 | GLuint vaoID; 14 | size_t indexCount; 15 | tinyobj::material_t material; 16 | 17 | public: 18 | GLuint textureID; 19 | ModelComponent(GLuint, size_t, GLuint, const tinyobj::material_t&); 20 | ModelComponent(GLuint, size_t, GLuint); 21 | ModelComponent(); 22 | 23 | size_t getIndexCount() const; 24 | GLuint getVaoID() const; 25 | GLuint getTextureID() const; 26 | tinyobj::material_t getMaterial() const; 27 | }; 28 | 29 | // Represents a grouping of meshes/shapes/vaos/ModelComponents to form a larger object. 30 | class Model { 31 | private: 32 | std::vector components; 33 | // Essentially forms a bounding box for the model. Format: [xMin, xMax, yMin, yMax, zMin, zMax] 34 | std::vector maxRanges; 35 | 36 | public: 37 | Model(const std::vector&); 38 | Model(); 39 | void addModelComponent(const ModelComponent&); 40 | const std::vector& getModelComponents() const; 41 | 42 | void addRange(const std::vector& vertices); 43 | void setRange(const std::tuple& minXYZ, const std::tuple& maxXYZ); 44 | std::pair getRangeInDim(int dim) const; 45 | }; 46 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # opengl-car-game 2 | 3 | ![](https://i.imgur.com/o3EVk2U.png) 4 | 5 | Simple car game written in C++, using OpenGL 3.3. 6 | 7 | Notable features include: 8 | - Shadow mapping 9 | - Water with dynamic reflection and refraction 10 | - Dynamic particle system 11 | 12 | ## Dependencies 13 | 14 | ##### Ubuntu/Debian 15 | ``` 16 | sudo apt-get install libglfw3-dev libglm-dev 17 | ``` 18 | 19 | ##### Vcpkg on Windows 20 | ``` 21 | vcpkg install --triplet x64-windows glfw3 glm 22 | ``` 23 | 24 | ## Build 25 | 26 | If dependencies are visible to CMake by default, can build like so: 27 | ``` 28 | git lfs pull 29 | mkdir build 30 | cd build 31 | cmake .. 32 | make 33 | ``` 34 | 35 | Otherwise, you'll need to point CMake to the Vcpkg toolchain file to find the dependencies and specify the triplet. For example on Windows: 36 | 37 | ``` 38 | git lfs pull 39 | cmake .. -DCMAKE_TOOLCHAIN_FILE="[vcpkg root]\scripts\buildsystems\vcpkg.cmake" -DVCPKG_TARGET_TRIPLET=x64-windows 40 | .\opengl-car-game.sln 41 | ``` 42 | 43 | ## Usage 44 | 45 | ``` 46 | ./opengl-car-game basic|physics 47 | ``` 48 | __basic__ - No intertia, simple turning 49 | 50 | __physics__ - uses formula for more realistic driving experience. 51 | -------------------------------------------------------------------------------- /ShadowMap.cpp: -------------------------------------------------------------------------------- 1 | #include "ShadowMap.h" 2 | 3 | #include "glm_ext.h" 4 | 5 | ShadowMap::ShadowMap(Player* player, Light* light, GLuint textureSize) 6 | : FrameBuffer(textureSize, textureSize), player(player), textureSize(textureSize) { 7 | projection = glm::ortho(-30, 30, -30, 30, -50, 150); 8 | lightDir = glm::vec3(light->position.x, light->position.y, light->position.z); 9 | 10 | addDepthTexture(); 11 | if (!isOkay()) { 12 | std::cerr << "[FrameBuffer] FrameBuffer is not okay." << std::endl; 13 | exit(1); 14 | } 15 | } 16 | 17 | GLuint ShadowMap::getTextureID() { 18 | return getDepthTexture(); 19 | } 20 | 21 | GLuint ShadowMap::getTextureSize() { 22 | return textureSize; 23 | } 24 | 25 | void ShadowMap::bind() { 26 | FrameBuffer::bind(); 27 | // Set the view matrix to be looking from sun's position to the player. 28 | view = glm::lookAt(player->getPosition() + lightDir, player->getPosition(), glm::vec3(0, 1, 0)); 29 | } 30 | 31 | glm::mat4 ShadowMap::getView() { 32 | return view; 33 | } 34 | 35 | glm::mat4 ShadowMap::getProjection() { 36 | return projection; 37 | } 38 | -------------------------------------------------------------------------------- /ShadowMap.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "entities/Light.h" 4 | #include "entities/Player.h" 5 | #include "FrameBuffer.h" 6 | 7 | #include 8 | #include 9 | 10 | class ShadowMap : public FrameBuffer { 11 | private: 12 | Player* player; 13 | 14 | const GLuint textureSize; 15 | 16 | glm::vec3 lightDir; 17 | glm::mat4 projection; 18 | glm::mat4 view; 19 | 20 | public: 21 | ShadowMap(Player* player, Light* light, GLuint textureSize = 2048); 22 | 23 | GLuint getTextureID(); 24 | GLuint getTextureSize(); 25 | glm::mat4 getView(); 26 | glm::mat4 getProjection(); 27 | 28 | // Override default bind of FrameBuffer to update state. 29 | // Unbind is unchanged 30 | void bind() override; 31 | }; 32 | -------------------------------------------------------------------------------- /Window.cpp: -------------------------------------------------------------------------------- 1 | #include "Window.h" 2 | 3 | #include 4 | #include 5 | 6 | void error_callback(int /*error*/, const char* description) { 7 | fputs(description, stderr); 8 | } 9 | 10 | Window::Window() { 11 | glfwSetErrorCallback(error_callback); 12 | if (!glfwInit()) { 13 | std::exit(1); 14 | } 15 | 16 | // Specify that we want OpenGL 3.3 17 | glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); 18 | glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); 19 | glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); 20 | glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); 21 | 22 | // Create the window and OpenGL context 23 | m_window = glfwCreateWindow(win_x, win_y, "OpenGL Car Game", nullptr, nullptr); 24 | if (m_window == nullptr) { 25 | fmt::print(stderr, "Failed to create window"); 26 | glfwTerminate(); 27 | exit(1); 28 | } 29 | 30 | glfwSetWindowUserPointer(m_window, this); 31 | 32 | // Set callbacks key press and mouse press. 33 | glfwSetKeyCallback(m_window, key_callback); 34 | glfwSetCursorPosCallback(m_window, mouse_pos_callback); 35 | glfwSetMouseButtonCallback(m_window, mouse_button_callback); 36 | glfwSetFramebufferSizeCallback(m_window, reshape_callback); 37 | glfwSetScrollCallback(m_window, scroll_callback); 38 | 39 | glfwMakeContextCurrent(m_window); 40 | glfwSwapInterval(1); 41 | 42 | if (!gladLoadGL()) { 43 | fmt::print(stderr, "Failed to load OpenGL"); 44 | glfwTerminate(); 45 | exit(1); 46 | } 47 | glfwGetFramebufferSize(m_window, &win_x, &win_y); 48 | 49 | // Sets the (background) colour for each time the frame-buffer 50 | // (colour buffer) is cleared 51 | glClearColor(0.2f, 0.2f, 0.2f, 1.0f); 52 | glEnable(GL_DEPTH_TEST); 53 | glEnable(GL_CULL_FACE); 54 | glEnable(GL_BLEND); 55 | glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); 56 | } 57 | 58 | void Window::key_callback(GLFWwindow* window, int key, int scancode, int action, int mods) { 59 | auto* this_window = (Window*)glfwGetWindowUserPointer(window); 60 | this_window->m_key_callback(window, key, scancode, action, mods); 61 | } 62 | 63 | void Window::mouse_pos_callback(GLFWwindow* window, double x, double y) { 64 | auto* this_window = (Window*)glfwGetWindowUserPointer(window); 65 | this_window->m_mouse_pos_callback(window, x, y); 66 | } 67 | 68 | void Window::scroll_callback(GLFWwindow* window, double xoffset, double yoffset) { 69 | auto* this_window = (Window*)glfwGetWindowUserPointer(window); 70 | this_window->m_scroll_callback(window, xoffset, yoffset); 71 | } 72 | 73 | void Window::mouse_button_callback(GLFWwindow* window, int button, int action, int mods) { 74 | auto* this_window = (Window*)glfwGetWindowUserPointer(window); 75 | this_window->m_mouse_button_callback(window, button, action, mods); 76 | } 77 | 78 | void Window::reshape_callback(GLFWwindow* window, int x, int y) { 79 | auto* this_window = (Window*)glfwGetWindowUserPointer(window); 80 | this_window->win_x = x; 81 | this_window->win_y = y; 82 | this_window->m_reshape_callback(window, x, y); 83 | } 84 | 85 | GLFWwindow* Window::get_window() const { 86 | return m_window; 87 | } 88 | -------------------------------------------------------------------------------- /Window.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include 8 | 9 | struct GLFWwindow; 10 | 11 | struct Window { 12 | Window(); 13 | 14 | [[nodiscard]] int get_width() const { 15 | return win_x; 16 | } 17 | 18 | [[nodiscard]] int get_height() const { 19 | return win_y; 20 | } 21 | 22 | [[nodiscard]] GLFWwindow* get_window() const; 23 | 24 | template 25 | void set_key_callback(Lambda&& callback) { 26 | m_key_callback = std::forward(callback); 27 | } 28 | 29 | template 30 | void set_mouse_position_callback(Lambda&& callback) { 31 | m_mouse_pos_callback = std::forward(callback); 32 | } 33 | 34 | template 35 | void set_mouse_scroll_callback(Lambda&& callback) { 36 | m_scroll_callback = std::forward(callback); 37 | } 38 | 39 | template 40 | void set_mouse_button_callback(Lambda&& callback) { 41 | m_mouse_button_callback = std::forward(callback); 42 | } 43 | 44 | template 45 | void set_window_reshape_callback(Lambda&& callback) { 46 | m_reshape_callback = std::forward(callback); 47 | } 48 | 49 | private: 50 | static void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods); 51 | static void mouse_pos_callback(GLFWwindow* window, double x, double y); 52 | static void scroll_callback(GLFWwindow* window, double xoffset, double yoffset); 53 | static void mouse_button_callback(GLFWwindow* window, int button, int action, int mods); 54 | static void reshape_callback(GLFWwindow* window, int x, int y); 55 | 56 | std::function m_key_callback; 57 | std::function m_mouse_pos_callback; 58 | std::function m_scroll_callback; 59 | std::function m_mouse_button_callback; 60 | std::function m_reshape_callback; 61 | 62 | int win_x = 640; 63 | int win_y = 480; 64 | GLFWwindow* m_window; 65 | }; 66 | -------------------------------------------------------------------------------- /constants.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace constants { 4 | inline constexpr float PI = 3.14159265358979323846f; 5 | } 6 | -------------------------------------------------------------------------------- /entities/Camera.cpp: -------------------------------------------------------------------------------- 1 | #include "Camera.h" 2 | 3 | #include 4 | #include 5 | #include "../constants.h" 6 | #include "../GameTime.h" 7 | 8 | #include 9 | 10 | #define DEG2RAD(x) ((x)*constants::PI / 180.0) 11 | #define RAD2DEG(x) ((x)*180.0 / constants::PI) 12 | 13 | const glm::mat4& Camera::getViewMtx() const { 14 | return m_view_matrix; 15 | } 16 | 17 | const glm::vec3& Camera::getPosition() const { 18 | return m_position; 19 | } 20 | void Camera::setPosition(const glm::vec3& pos) { 21 | m_position = pos; 22 | } 23 | 24 | void Camera::look(const glm::vec3& at) { 25 | glm::vec3 up(0.0f, 1.0f, 0.0f); 26 | m_view_matrix = glm::lookAt(m_position, at, up); 27 | } 28 | 29 | void Camera::look(const glm::vec3& from, const glm::vec3& at) { 30 | glm::vec3 up(0.0f, 1.0f, 0.0f); 31 | m_view_matrix = glm::lookAt(from, at, up); 32 | } 33 | 34 | glm::mat4 Camera::getInverted(float pivotPoint) { 35 | glm::vec3 playerPos = m_focal_point; 36 | 37 | float offset = fabs(m_position.y - playerPos.y); 38 | glm::vec3 camPos = m_position - glm::vec3(0, 2 * offset, 0); 39 | 40 | camPos -= glm::vec3(0, playerPos.y - pivotPoint, 0); 41 | playerPos -= glm::vec3(0, playerPos.y - pivotPoint, 0); 42 | 43 | glm::vec3 up(0.0f, 1.0f, 0.0f); 44 | return glm::lookAt(camPos, playerPos, up); 45 | } 46 | 47 | PlayerCamera::PlayerCamera(Player* player) { 48 | m_player = player; 49 | m_distance = 5.0f; 50 | m_pitch = constants::PI / 8.f; 51 | m_angle_around = 0.0f; 52 | } 53 | 54 | void PlayerCamera::update(InputState& input) { 55 | m_distance -= input.scrollDeltaY; 56 | m_distance = std::clamp(m_distance, 2.5f, 20.0f); 57 | 58 | if (input.lMousePressed) { 59 | m_pitch -= input.deltaY / 50; 60 | m_pitch = std::clamp(m_pitch, 0.1f, constants::PI / 2 - 0.0001f); 61 | 62 | m_angle_around -= input.deltaX / 50.0f; 63 | 64 | if (m_angle_around > 2 * constants::PI) { 65 | m_angle_around -= 2 * constants::PI; 66 | } 67 | if (m_angle_around < -2 * constants::PI) { 68 | m_angle_around += 2 * constants::PI; 69 | } 70 | } 71 | input.scrollDeltaY = 0.0; 72 | input.deltaY = 0; 73 | input.deltaX = 0; 74 | 75 | float change = RESET_SPEED * GameTime::getGameTime()->getDt(); 76 | 77 | if (m_player->getThrottle() > 0.1f) { 78 | if (fabs(m_angle_around) < change) { 79 | m_angle_around = 0.0f; 80 | } else if (m_angle_around > 0) { 81 | m_angle_around -= change; 82 | } else if (m_angle_around < 0) { 83 | m_angle_around += change; 84 | } 85 | } 86 | 87 | float angle = m_angle_around + m_player->getRotationY(); 88 | 89 | float hDist = m_distance * glm::cos(m_pitch); 90 | float vDist = m_distance * glm::sin(m_pitch); 91 | float offsetX = hDist * glm::sin(angle); 92 | float offsetZ = hDist * glm::cos(angle); 93 | 94 | m_focal_point = m_player->getPosition(); 95 | m_position = glm::vec3(-offsetX, vDist, -offsetZ) + m_focal_point; 96 | 97 | look(m_focal_point); 98 | } 99 | -------------------------------------------------------------------------------- /entities/Camera.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "../constants.h" 4 | 5 | #include "../InputState.h" 6 | #include "Player.h" 7 | 8 | #include 9 | 10 | class Camera { 11 | protected: 12 | glm::vec3 m_position{0.f}; 13 | glm::vec3 m_focal_point{0.f}; 14 | glm::mat4 m_view_matrix{1.f}; 15 | 16 | public: 17 | Camera() = default; 18 | 19 | [[nodiscard]] const glm::mat4& getViewMtx() const; 20 | [[nodiscard]] const glm::vec3& getPosition() const; 21 | void setPosition(const glm::vec3& position); 22 | [[nodiscard]] glm::mat4 getInverted(float pivotPoint); 23 | 24 | virtual void update(InputState& input) = 0; 25 | 26 | void look(const glm::vec3& at); 27 | void look(const glm::vec3& from, const glm::vec3& at); 28 | }; 29 | 30 | class PlayerCamera : public Camera { 31 | private: 32 | Player* m_player; 33 | float m_distance; 34 | float m_pitch; 35 | float m_angle_around; 36 | 37 | static constexpr float RESET_SPEED = constants::PI / 2.f; 38 | 39 | public: 40 | PlayerCamera(Player* player); 41 | void update(InputState& input) override; 42 | }; 43 | -------------------------------------------------------------------------------- /entities/Entity.cpp: -------------------------------------------------------------------------------- 1 | #include "Entity.h" 2 | 3 | #include "glm_ext.h" 4 | 5 | using namespace std; 6 | 7 | // Constructor accepts a model defining vertex, colour and index data for this entity. 8 | Entity::Entity(const Model* model) { 9 | this->m_model = model; 10 | 11 | m_position = glm::vec3(0.0f, 0.0f, 0.0f); 12 | m_scale = glm::vec3(1.0f, 1.0f, 1.0f); 13 | m_x_rot = 0.0f; 14 | m_y_rot = 0.0f; 15 | m_z_rot = 0.0f; 16 | } 17 | 18 | Entity::Entity() { 19 | this->m_model = nullptr; 20 | 21 | m_position = glm::vec3(0.0f, 0.0f, 0.0f); 22 | m_scale = glm::vec3(1.0f, 1.0f, 1.0f); 23 | m_x_rot = 0.0f; 24 | m_y_rot = 0.0f; 25 | m_z_rot = 0.0f; 26 | } 27 | 28 | bool Entity::update() { 29 | return false; 30 | } 31 | 32 | const Model* Entity::getModel() const { 33 | return m_model; 34 | } 35 | 36 | glm::mat4 Entity::calculateModelMatrix() const { 37 | glm::mat4 rotation = calculateRotationMatrix(m_x_rot, m_y_rot, m_z_rot); 38 | return calculateModelMatrix(m_position, rotation, m_scale); 39 | } 40 | 41 | // Getters and setters for entity state values. 42 | const glm::vec3& Entity::getPosition() const { 43 | return m_position; 44 | } 45 | 46 | const glm::vec3& Entity::getScale() const { 47 | return m_scale; 48 | } 49 | 50 | float Entity::getRotationX() const { 51 | return m_x_rot; 52 | } 53 | 54 | float Entity::getRotationY() const { 55 | return m_y_rot; 56 | } 57 | 58 | float Entity::getRotationZ() const { 59 | return m_z_rot; 60 | } 61 | 62 | glm::vec3 Entity::calculateDirectionVector() const { 63 | return glm::normalize(glm::vec3(glm::sin(m_y_rot), glm::sin(m_x_rot), glm::cos(m_y_rot))); 64 | } 65 | 66 | void Entity::setPosition(const glm::vec3& inputPosition) { 67 | this->m_position = inputPosition; 68 | } 69 | 70 | void Entity::placeBottomEdge(float surfaceY) { 71 | if (m_model != nullptr) { 72 | m_position.y = surfaceY - m_model->getRangeInDim(1).first * m_scale.y; 73 | } 74 | } 75 | 76 | void Entity::setScale(const glm::vec3& scale) { 77 | this->m_scale = scale; 78 | } 79 | 80 | void Entity::setRotationX(float rot) { 81 | m_x_rot = rot; 82 | } 83 | 84 | void Entity::setRotationY(float rot) { 85 | m_y_rot = rot; 86 | } 87 | 88 | void Entity::setRotationZ(float rot) { 89 | m_z_rot = rot; 90 | } 91 | // Set the value of rotation or position relatively (Takes into account current value) 92 | void Entity::rotateX(float rot) { 93 | m_x_rot += rot; 94 | } 95 | 96 | void Entity::rotateY(float rot) { 97 | m_y_rot += rot; 98 | } 99 | 100 | void Entity::rotateZ(float rot) { 101 | m_z_rot += rot; 102 | } 103 | 104 | void Entity::move(const glm::vec3& movement) { 105 | m_position = m_position + movement; 106 | } 107 | 108 | glm::mat4 Entity::calculateModelMatrix( 109 | const glm::vec3& position, const glm::mat4& rotationMat, const glm::vec3& scale) { 110 | glm::mat4 modelMatrix(1.0f); 111 | 112 | // scale, rotate and translate 113 | modelMatrix = glm::translate(modelMatrix, position); 114 | modelMatrix = modelMatrix * rotationMat; 115 | modelMatrix = glm::scale(modelMatrix, scale); 116 | 117 | return modelMatrix; 118 | } 119 | 120 | glm::mat4 Entity::calculateRotationMatrix(float xRot, float yRot, float zRot) { 121 | glm::mat4 rotation(1.0f); 122 | 123 | rotation = glm::rotate(rotation, yRot, glm::vec3(0.0f, 1.0f, 0.0f)); 124 | rotation = glm::rotate(rotation, xRot, glm::vec3(1.0f, 0.0f, 0.0f)); 125 | rotation = glm::rotate(rotation, zRot, glm::vec3(0.0f, 0.0f, 1.0f)); 126 | 127 | return rotation; 128 | } 129 | -------------------------------------------------------------------------------- /entities/Entity.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define _USE_MATH_DEFINES 4 | 5 | #include "../Model.h" 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | class Entity { 14 | protected: 15 | const Model* m_model; 16 | 17 | glm::vec3 m_position; 18 | glm::vec3 m_scale; 19 | float m_x_rot; 20 | float m_y_rot; 21 | float m_z_rot; 22 | 23 | public: 24 | Entity(const Model* model); 25 | Entity(); 26 | 27 | virtual ~Entity() = default; 28 | 29 | virtual bool update(); 30 | 31 | [[nodiscard]] const Model* getModel() const; 32 | [[nodiscard]] glm::mat4 calculateModelMatrix() const; 33 | [[nodiscard]] const glm::vec3& getPosition() const; 34 | [[nodiscard]] const glm::vec3& getScale() const; 35 | [[nodiscard]] float getRotationX() const; 36 | [[nodiscard]] float getRotationY() const; 37 | [[nodiscard]] float getRotationZ() const; 38 | 39 | [[nodiscard]] glm::vec3 calculateDirectionVector() const; 40 | 41 | // Can be overridden in inheriting class if behaviour requires it. 42 | // Set the value absolutely of position, scale, or rotation. 43 | virtual void setPosition(const glm::vec3&); 44 | virtual void placeBottomEdge(float surfaceY); 45 | virtual void setScale(const glm::vec3&); 46 | virtual void setRotationX(float); 47 | virtual void setRotationY(float); 48 | virtual void setRotationZ(float); 49 | 50 | // Set the value of rotation or position relatively (Takes into account current value) 51 | virtual void rotateX(float); 52 | virtual void rotateY(float); 53 | virtual void rotateZ(float); 54 | virtual void move(const glm::vec3&); 55 | 56 | // Generates the transformation to be applied to the mesh with the given parameters. 57 | static glm::mat4 calculateModelMatrix(const glm::vec3& position, const glm::mat4& rotation, const glm::vec3& scale); 58 | static glm::mat4 calculateRotationMatrix(float xRot, float yRot, float zRot); 59 | }; 60 | -------------------------------------------------------------------------------- /entities/Light.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "../constants.h" 4 | #include 5 | 6 | // Lights are a simple struct holding all of the relevant information. 7 | struct Light { 8 | // Position of the camera in world coordinates. w value of 0 indicates directional light, 1 indicates 9 | // positional/point light 10 | glm::vec4 position = glm::vec4(0.0f); 11 | // Colour intensities of each light component 12 | glm::vec3 specular = glm::vec3(0.0f); 13 | glm::vec3 diffuse = glm::vec3(0.0f); 14 | glm::vec3 ambient = glm::vec3(0.0f); 15 | // Radius used for attenuation 16 | float radius = 0.f; 17 | // Angle and direction of cone for spotlights 18 | float coneAngle = constants::PI; 19 | glm::vec3 coneDirection = glm::vec3(0.0f); 20 | }; 21 | -------------------------------------------------------------------------------- /entities/Player.cpp: -------------------------------------------------------------------------------- 1 | #include "Player.h" 2 | 3 | #include "../constants.h" 4 | #include "../GameTime.h" 5 | #include "Terrain.h" 6 | 7 | #include 8 | #include 9 | 10 | using namespace std; 11 | using namespace glm; 12 | 13 | /* 14 | Car physics calculation has been modified from 15 | #1 https://github.com/spacejack/carphysics2d 16 | which is an adaptation of 17 | http://www.asawicki.info/Mirror/Car%20Physics%20for%20Games/Car%20Physics%20for%20Games.html 18 | I didnt make many changes since it is a complete equation, but I did add code to stop when there is no throttle. 19 | */ 20 | 21 | // Static variables used for car physics 22 | constexpr float gravity = 9.81f; // m/s^2 23 | constexpr float mass = 2000.0f; // kg 24 | constexpr float inertiaScale = 1.0f; // Multiply by mass for inertia 25 | constexpr float halfWidth = 0.8f; // Centre to side of chassis (metres) 26 | constexpr float cgToFront = 1.5f; // Centre of gravity to front of chassis (metres) 27 | constexpr float cgToRear = 1.5f; // Centre of gravity to rear of chassis 28 | constexpr float cgToFrontAxle = 1.25f; // Centre gravity to front axle 29 | constexpr float cgToRearAxle = 1.25f; // Centre gravity to rear axle 30 | constexpr float cgHeight = 0.55f; // Centre gravity height 31 | constexpr float wheelRadius = 0.3f; // Includes tire (also represents height of axle) 32 | constexpr float wheelWidth = 0.2f; // Used for render only 33 | constexpr float tireGrip = 3.0f; // How much grip tires have 34 | constexpr float lockGrip = 0.8f; // % of grip available when wheel is locked 35 | constexpr float engineForce = 8000.0f; 36 | constexpr float brakeForce = 12000.0f; 37 | constexpr float eBrakeForce = brakeForce / 2.5f; 38 | constexpr float weightTransfer = 0.2f; // How much weight is transferred during acceleration/braking 39 | constexpr float maxSteer = 0.6f; // Maximum steering angle in radians 40 | constexpr float cornerStiffnessFront = 5.0f; 41 | constexpr float cornerStiffnessRear = 5.2f; 42 | constexpr float airResist = 3.0f; // air resistance (* vel) 43 | constexpr float rollResist = 5.0f; 44 | constexpr float inertia = mass * inertiaScale; // will be = mass 45 | constexpr float wheelBase = cgToFrontAxle + cgToRearAxle; // set from axle to CG lengths 46 | constexpr float axleWeightRatioFront = cgToRearAxle / wheelBase; // % car weight on the front axle 47 | constexpr float axleWeightRatioRear = cgToFrontAxle / wheelBase; // % car weight on the rear axle 48 | 49 | Player::Player(Model* model, Terrain* terrain, bool basic_controls) : Entity(model) { 50 | this->terrain = terrain; 51 | this->absVel = 0.0f; 52 | this->yawRate = 0.0f; 53 | this->steerAngle = 0.0f; 54 | this->steerChange = 0.0f; 55 | this->throttle_input = 0.0f; 56 | this->brake_input = 0.0f; 57 | this->ebrake_input = 0.0f; 58 | this->basic_controls = basic_controls; 59 | this->velocity = {0.f, 0.f}; 60 | this->velocity_c = {0.f, 0.f}; 61 | this->accel = {0.f, 0.f}; 62 | this->accel_c = {0.f, 0.f}; 63 | if (basic_controls) { 64 | ROTATION_SPEED = constants::PI; 65 | } else { 66 | ROTATION_SPEED = 0.6f; 67 | } 68 | } 69 | 70 | template 71 | int sgn(T val) { 72 | return (T(0) < val) - (val < T(0)); 73 | } 74 | 75 | float Player::getThrottle() const { 76 | return throttle_input; 77 | } 78 | 79 | float Player::getBrake() const { 80 | return brake_input; 81 | } 82 | 83 | float Player::getSteer() const { 84 | return steerAngle; 85 | } 86 | 87 | bool Player::update() { 88 | steerAngle = smoothSteering(steerChange); 89 | float dt = GameTime::getGameTime()->getDt(); 90 | float dx = 0.0f; 91 | float dz = 0.0f; 92 | 93 | if (basic_controls) { 94 | rotateY(steerAngle * dt); 95 | 96 | if (m_y_rot > (float)constants::PI * 2) { 97 | m_y_rot -= constants::PI * 2; 98 | } else if (m_y_rot < (float)-constants::PI * 2) { 99 | m_y_rot += constants::PI * 2; 100 | } 101 | 102 | float distance = (throttle_input - brake_input) * MOVE_SPEED * dt; 103 | 104 | dx = distance * glm::sin(m_y_rot); 105 | dz = distance * glm::cos(m_y_rot); 106 | } else { 107 | /* Following is code from #1 and adapted to this program */ 108 | // Pre-calc heading vector 109 | float sn = sin(m_y_rot); 110 | float cs = cos(m_y_rot); 111 | 112 | // Get velocity in local car coordinates 113 | velocity_c.y = cs * velocity.y + sn * velocity.x; 114 | velocity_c.x = cs * velocity.x - sn * velocity.y; 115 | 116 | // Weight on axles based on centre of gravity and weight shift due to forward/reverse acceleration 117 | float axleWeightFront = 118 | mass * (axleWeightRatioFront * gravity - weightTransfer * accel_c.y * cgHeight / wheelBase); 119 | float axleWeightRear = 120 | mass * (axleWeightRatioRear * gravity + weightTransfer * accel_c.y * cgHeight / wheelBase); 121 | 122 | // Resulting velocity of the wheels as result of the yaw rate of the car body. 123 | // v = yawrate * r where r is distance from axle to CG and yawRate (angular velocity) in rad/s. 124 | float yawSpeedFront = cgToFrontAxle * yawRate; 125 | float yawSpeedRear = -cgToRearAxle * yawRate; 126 | 127 | // Calculate slip angles for front and rear wheels (a.k.a. alpha) 128 | float slipAngleFront = atan2(velocity_c.x + yawSpeedFront, abs(velocity_c.y)) - sgn(velocity_c.y) * steerAngle; 129 | float slipAngleRear = atan2(velocity_c.x + yawSpeedRear, abs(velocity_c.y)); 130 | 131 | float tireGripFront = tireGrip; 132 | float tireGripRear = tireGrip * (1.f - ebrake_input * (1.f - lockGrip)); // reduce rear grip when ebrake is on 133 | 134 | float frictionForceFront_cy = 135 | std::clamp(-cornerStiffnessFront * slipAngleFront, -tireGripFront, tireGripFront) * axleWeightFront; 136 | float frictionForceRear_cy = 137 | std::clamp(-cornerStiffnessRear * slipAngleRear, -tireGripRear, tireGripRear) * axleWeightRear; 138 | 139 | // Get amount of brake/throttle from our inputs 140 | float brake = std::min(brake_input * brakeForce + ebrake_input * eBrakeForce, brakeForce); 141 | float throttle = throttle_input * engineForce; 142 | 143 | // Resulting force in local car coordinates. 144 | // is implemented as a RWD car only. 145 | float tractionForce_cx = throttle - brake * sgn(velocity_c.y); 146 | float tractionForce_cy = 0; 147 | 148 | float dragForce_cx = -rollResist * velocity_c.y - airResist * velocity_c.y * abs(velocity_c.y); 149 | float dragForce_cy = -rollResist * velocity_c.x - airResist * velocity_c.x * abs(velocity_c.x); 150 | 151 | // total force in car coordinates 152 | float totalForce_cx = dragForce_cx + tractionForce_cx; 153 | float totalForce_cy = 154 | dragForce_cy + tractionForce_cy + cos(steerAngle) * frictionForceFront_cy + frictionForceRear_cy; 155 | 156 | // acceleration along car axes 157 | accel_c.y = totalForce_cx / mass; // forward/reverse accel 158 | accel_c.x = totalForce_cy / mass; // sideways accel 159 | 160 | // acceleration in world coordinates 161 | accel.y = cs * accel_c.y - sn * accel_c.x; 162 | accel.x = sn * accel_c.y + cs * accel_c.x; 163 | 164 | // update velocity 165 | velocity.y += accel.y * dt; 166 | velocity.x += accel.x * dt; 167 | 168 | absVel = length(velocity); 169 | 170 | // Slow the car down when no throttle, the overall equation doesn't seem to do this very well. 171 | if (throttle < 0.5f) { 172 | velocity = velocity - (velocity * 0.5f * dt); 173 | } 174 | 175 | // calculate rotational forces 176 | float angularTorque = 177 | (frictionForceFront_cy + tractionForce_cy) * cgToFrontAxle - frictionForceRear_cy * cgToRearAxle; 178 | 179 | float angularAccel = angularTorque / inertia; 180 | 181 | // Sim gets unstable at very slow speeds, so just stop the car 182 | if (abs(absVel) < 2.0f && throttle < 0.5f) { 183 | velocity.y = 0.0f; 184 | velocity.x = 0.0f; 185 | absVel = 0.0f; 186 | yawRate = 0.0f; 187 | } else { 188 | yawRate += angularAccel * dt; 189 | m_y_rot += yawRate * dt; 190 | 191 | dx = velocity.x * dt; 192 | dz = velocity.y * dt; 193 | } 194 | /* End code from #1 */ 195 | } 196 | 197 | // Wrap rotation around once it reaches 2*pi 198 | if (m_y_rot > (float)constants::PI * 2) { 199 | m_y_rot -= constants::PI * 2; 200 | } else if (m_y_rot < (float)-constants::PI * 2) { 201 | m_y_rot += constants::PI * 2; 202 | } 203 | 204 | // Assumes constant scale 205 | float player_length = (abs(m_model->getRangeInDim(2).second - m_model->getRangeInDim(2).first)) / 2.0f * m_scale.x; 206 | float player_length_x = player_length * glm::sin(m_y_rot); 207 | float player_length_z = player_length * glm::cos(m_y_rot); 208 | 209 | // Currently, acceleration and velocity are maintained on collision with edge. 210 | // TODO zero velocity and acceleration on FIRST collision in incident, so as to allow escaping the wall 211 | if (terrain->isOnTerrain(m_position.x + dx + player_length_x, m_position.z + dz + player_length_z)) { 212 | move(glm::vec3(dx, 0, dz)); 213 | placeBottomEdge(terrain->getHeight(getPosition().x, getPosition().z)); 214 | setRotationX(terrain->getAngleX(getPosition().x, getPosition().z, getRotationY())); 215 | setRotationZ(terrain->getAngleZ(getPosition().x, getPosition().z, getRotationY())); 216 | return true; 217 | } 218 | return false; 219 | } 220 | 221 | float Player::smoothSteering(float inputAngle) { 222 | float smoothedAngle = 0; 223 | float dt = GameTime::getGameTime()->getDt(); 224 | float CHANGE_MODIFIER = 12.0f; 225 | 226 | if (abs(inputAngle) > 0.001) { 227 | smoothedAngle = 228 | std::clamp((float)(steerAngle + inputAngle * dt * CHANGE_MODIFIER), -ROTATION_SPEED, ROTATION_SPEED); 229 | } else { 230 | // No steer input - move toward centre (0) 231 | if (steerAngle > 0) { 232 | smoothedAngle = std::max(steerAngle - dt * CHANGE_MODIFIER, 0.0f); 233 | } else if (steerAngle < 0) { 234 | smoothedAngle = std::min(steerAngle + dt * CHANGE_MODIFIER, 0.0f); 235 | } 236 | } 237 | 238 | return smoothedAngle; 239 | } 240 | 241 | void Player::handleKeyboardEvents(GLFWwindow* /*window*/, int key, int /*scancode*/, int action, int /*mods*/) { 242 | if (action == GLFW_PRESS) { 243 | if (key == GLFW_KEY_W || key == GLFW_KEY_UP) { 244 | throttle_input = 1.0f; 245 | } 246 | if (key == GLFW_KEY_S || key == GLFW_KEY_DOWN) { 247 | brake_input = 1.0f; 248 | } 249 | if (key == GLFW_KEY_SPACE) { 250 | ebrake_input = 1.0f; 251 | } 252 | if (key == GLFW_KEY_A || key == GLFW_KEY_LEFT) { 253 | steerChange = ROTATION_SPEED; 254 | } else if (key == GLFW_KEY_D || key == GLFW_KEY_RIGHT) { 255 | steerChange = -ROTATION_SPEED; 256 | } 257 | } 258 | 259 | if (action == GLFW_RELEASE) { 260 | if (key == GLFW_KEY_W || key == GLFW_KEY_UP) { 261 | throttle_input = 0.0f; 262 | } 263 | if (key == GLFW_KEY_S || key == GLFW_KEY_DOWN) { 264 | brake_input = 0.0f; 265 | } 266 | if (key == GLFW_KEY_SPACE) { 267 | ebrake_input = 0.0f; 268 | } 269 | 270 | if ((key == GLFW_KEY_A || key == GLFW_KEY_LEFT) && steerAngle > 0.0f) { 271 | steerChange = 0.0f; 272 | } 273 | 274 | if ((key == GLFW_KEY_D || key == GLFW_KEY_RIGHT) && steerAngle < 0.0f) { 275 | steerChange = 0.0f; 276 | } 277 | } 278 | } 279 | -------------------------------------------------------------------------------- /entities/Player.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Entity.h" 4 | #include "Terrain.h" 5 | #include "../Model.h" 6 | 7 | #include 8 | #include 9 | 10 | class Player : public Entity { 11 | private: 12 | const float MOVE_SPEED = 10.0f; 13 | float ROTATION_SPEED; 14 | 15 | // Extra parameters if physics mode is being used 16 | glm::vec2 velocity; 17 | glm::vec2 velocity_c; 18 | glm::vec2 accel; 19 | glm::vec2 accel_c; 20 | 21 | float yawRate; 22 | 23 | // Input parameters 24 | float steerAngle; 25 | float steerChange; 26 | float throttle_input; 27 | float brake_input; 28 | float ebrake_input; 29 | 30 | Terrain* terrain; 31 | 32 | // True to use basic controls, false to use physics model 33 | bool basic_controls; 34 | 35 | float smoothSteering(float); 36 | 37 | public: 38 | Player(Model* model, Terrain* terrain, bool basic_controls); 39 | bool update() override; 40 | float getThrottle() const; 41 | float getBrake() const; 42 | float getSteer() const; 43 | float absVel; 44 | 45 | void handleKeyboardEvents(GLFWwindow* window, int key, int scancode, int action, int mods); 46 | }; 47 | -------------------------------------------------------------------------------- /entities/Terrain.cpp: -------------------------------------------------------------------------------- 1 | #include "Terrain.h" 2 | 3 | #include "../constants.h" 4 | #include "Entity.h" 5 | 6 | using namespace std; 7 | 8 | const float Terrain::TERRAIN_SIZE = 301.43f; // Set so that the fences fit better 9 | const float Terrain::TERRAIN_MAX_HEIGHT = 10.0f; 10 | 11 | // Constructor accepts a model defining vertex, colour and index data for this Terrain. 12 | Terrain::Terrain(Model* model, const vector& textures, Image heightMap) 13 | : Entity(model), textures(textures), heightMap(std::move(heightMap)) { 14 | } 15 | 16 | Terrain* Terrain::loadTerrain(const std::vector& images, const std::string& heightMapFile) { 17 | Image heightMap = Image::loadFromFile(heightMapFile); 18 | Model* model = Terrain::generateTerrainModel(heightMap); 19 | std::vector textures; 20 | textures.reserve(images.size()); 21 | for (const auto& image : images) { 22 | textures.push_back(Loader::getLoader()->loadTexture(image)); 23 | } 24 | 25 | return new Terrain(model, textures, std::move(heightMap)); 26 | } 27 | 28 | Model* Terrain::generateTerrainModel(const Image& heightMap) { 29 | std::vector vertices; 30 | std::vector textureCoords; 31 | std::vector indices; 32 | const int TERRAIN_NUM_VERTS = heightMap.width; 33 | 34 | for (int z_off = 0; z_off < TERRAIN_NUM_VERTS; z_off++) { 35 | for (int x_off = 0; x_off < TERRAIN_NUM_VERTS; x_off++) { 36 | glm::vec3 colour = heightMap.getPixel(x_off, z_off); 37 | float height = (colour.r + colour.g + colour.b) / 3 * TERRAIN_MAX_HEIGHT; 38 | 39 | vertices.push_back((float)x_off / ((float)TERRAIN_NUM_VERTS - 1) * TERRAIN_SIZE); 40 | vertices.push_back(height); 41 | vertices.push_back((float)z_off / ((float)TERRAIN_NUM_VERTS - 1) * TERRAIN_SIZE); 42 | textureCoords.push_back((float)x_off / ((float)TERRAIN_NUM_VERTS - 1)); 43 | textureCoords.push_back((float)z_off / ((float)TERRAIN_NUM_VERTS - 1)); 44 | } 45 | } 46 | 47 | for (int z_off = 0; z_off < TERRAIN_NUM_VERTS - 1; z_off++) { 48 | for (int x_off = 0; x_off < TERRAIN_NUM_VERTS - 1; x_off++) { 49 | int topLeft = (z_off * TERRAIN_NUM_VERTS) + x_off; 50 | int topRight = topLeft + 1; 51 | int bottomLeft = ((z_off + 1) * TERRAIN_NUM_VERTS) + x_off; 52 | int bottomRight = bottomLeft + 1; 53 | 54 | indices.push_back(topLeft); 55 | indices.push_back(bottomLeft); 56 | indices.push_back(topRight); 57 | indices.push_back(topRight); 58 | indices.push_back(bottomLeft); 59 | indices.push_back(bottomRight); 60 | } 61 | } 62 | 63 | std::vector normals = Loader::getLoader()->generateNormals(vertices, indices); 64 | 65 | GLuint vao = Loader::getLoader()->loadVAO(vertices, indices, textureCoords, normals); 66 | GLuint tex = Loader::getLoader()->loadDefaultTexture(); 67 | 68 | ModelComponent component(vao, indices.size(), tex); 69 | auto* model = new Model(); 70 | model->addRange(vertices); 71 | model->addModelComponent(component); 72 | 73 | return model; 74 | } 75 | 76 | bool Terrain::isOnTerrain(float x, float z) const { 77 | int x_int = convertCoordinate(x); 78 | int z_int = convertCoordinate(z); 79 | 80 | // Bring in boundaries slightly from absolute edge and do check. 81 | if (x_int < 4 || x_int >= heightMap.width - 4 || z_int < 4 || z_int >= heightMap.width - 4) { 82 | return false; 83 | } 84 | 85 | return getHeight(x, z) >= 0.0f; 86 | } 87 | 88 | GLuint Terrain::getVaoID() const { 89 | return m_model->getModelComponents().at(0).getVaoID(); 90 | } 91 | 92 | size_t Terrain::getIndexCount() const { 93 | return m_model->getModelComponents().at(0).getIndexCount(); 94 | } 95 | 96 | GLuint Terrain::getTextureID(int i) const { 97 | return textures[i]; 98 | } 99 | float Terrain::getHeight(float x, float z) const { 100 | int x_int = convertCoordinate(x); 101 | int z_int = convertCoordinate(z); 102 | glm::vec3 pixel = heightMap.getPixel(x_int, z_int); 103 | 104 | return (pixel.r + pixel.g + pixel.b) / 3 * TERRAIN_MAX_HEIGHT; 105 | } 106 | 107 | // Assumes already translated coordinates 108 | float Terrain::getHeight(int x_int, int z_int) const { 109 | glm::vec3 pixel = heightMap.getPixel(x_int, z_int); 110 | 111 | return (pixel.r + pixel.g + pixel.b) / 3 * TERRAIN_MAX_HEIGHT; 112 | } 113 | 114 | int Terrain::convertCoordinate(float coord) const { 115 | return int((heightMap.width / 2) + coord * heightMap.width / TERRAIN_SIZE); 116 | } 117 | 118 | glm::vec3 Terrain::getPositionFromPixel(int x, int y) const { 119 | float x_flt = ((float)(x - heightMap.width / 2)) / heightMap.width * TERRAIN_SIZE; 120 | float z_flt = ((float)(y - heightMap.height / 2)) / heightMap.height * TERRAIN_SIZE; 121 | 122 | return glm::vec3(x_flt, 0.0f, z_flt); 123 | } 124 | 125 | float Terrain::getAngleX(float x, float y, float rotation) const { 126 | return getAngle(x, y, rotation, 0.0f); 127 | } 128 | 129 | float Terrain::getAngleZ(float x, float y, float rotation) const { 130 | return getAngle(x, y, rotation, constants::PI / 2); 131 | } 132 | 133 | // Returns the angle that should be applied for the car given its direction and heading to conform to terrain. 134 | float Terrain::getAngle(float x, float z, float rotation, float offset) const { 135 | int x_cur = convertCoordinate(x); 136 | int z_cur = convertCoordinate(z); 137 | 138 | int x_nxt = 0; 139 | int z_nxt = 0; 140 | rotation += constants::PI / 8; // apply small rotation to offset 141 | rotation -= offset; // Allows to test both front and next to car 142 | if (rotation < 0) 143 | rotation += constants::PI * 2.f; 144 | if (rotation > 0 && rotation <= constants::PI / 4.f) { // North 145 | z_nxt = -1; 146 | } else if (rotation > constants::PI / 4.f && rotation <= constants::PI / 2) { // North west 147 | x_nxt = -1; 148 | z_nxt = -1; 149 | } else if (rotation > constants::PI / 2.f && rotation <= 3.f * constants::PI / 4.f) { // West 150 | x_nxt = -1; 151 | } else if (rotation > 3.f * constants::PI / 4.f && rotation <= constants::PI) { // South west 152 | x_nxt = -1; 153 | z_nxt = +1; 154 | } else if (rotation > constants::PI && rotation <= 5.f * constants::PI / 4.f) { // South 155 | z_nxt = +1; 156 | } else if (rotation > 5.f * constants::PI / 4.f && rotation <= 3.f * constants::PI / 2.f) { // South east 157 | x_nxt = +1; 158 | z_nxt = +1; 159 | } else if (rotation > 3.f * constants::PI / 2.f && rotation <= 7.f * constants::PI / 4.f) { // East 160 | x_nxt = +1; 161 | } else if (rotation > 7.f * constants::PI / 4.f && rotation <= 2.f * constants::PI) { // North East 162 | x_nxt = +1; 163 | z_nxt = -1; 164 | } 165 | 166 | float h_cur = getHeight(x_cur, z_cur); 167 | // Looks 5 units ahead for a next height 168 | float h_nxt = getHeight(x_cur + x_nxt * 5, z_cur + z_nxt * 5); 169 | 170 | // However, if that is negative (meaning off map), it tightens until either 0 or a good reading is received 171 | const int LOOK_AHEAD = 5; 172 | for (int i = LOOK_AHEAD; i > 0; --i) { 173 | if (h_nxt > 0.0) { 174 | break; 175 | } 176 | h_nxt = getHeight(x_cur + x_nxt * i, z_cur + z_nxt * i); 177 | } 178 | 179 | return glm::atan((h_nxt - h_cur) / ((float)LOOK_AHEAD * TERRAIN_SIZE / heightMap.width)); 180 | } 181 | -------------------------------------------------------------------------------- /entities/Terrain.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "../Model.h" 4 | #include "Entity.h" 5 | #include "../Loader.h" 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | class Terrain : public Entity { 13 | protected: 14 | std::vector textures; 15 | Image heightMap; 16 | 17 | public: 18 | static const float TERRAIN_SIZE; 19 | static const float TERRAIN_MAX_HEIGHT; 20 | 21 | Terrain(Model* model, const std::vector &textures, Image heightMap); 22 | static Terrain* loadTerrain(const std::vector& images, const std::string& heightMapFile); 23 | static Model* generateTerrainModel(const Image& heightMap); 24 | 25 | GLuint getVaoID() const; 26 | size_t getIndexCount() const; 27 | GLuint getTextureID(int) const; 28 | bool isOnTerrain(float x, float z) const; 29 | float getHeight(float x, float z) const; 30 | float getHeight(int x, int z) const; 31 | int convertCoordinate(float coord) const; 32 | 33 | glm::vec3 getPositionFromPixel(int x, int y) const; 34 | 35 | float getAngleX(float x, float y, float rotation) const; 36 | float getAngleZ(float x, float y, float rotation) const; 37 | float getAngle(float x, float y, float rotation, float offset) const; 38 | }; 39 | -------------------------------------------------------------------------------- /extern/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_subdirectory(fmt) 2 | add_subdirectory(stb_image) 3 | add_subdirectory(tiny_obj_loader) 4 | add_subdirectory(glad) -------------------------------------------------------------------------------- /extern/glad/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_library(glad include/glad/glad.h src/glad.c) 2 | target_include_directories(glad PUBLIC include/) 3 | add_library(glad::glad ALIAS glad) 4 | -------------------------------------------------------------------------------- /extern/glad/include/KHR/khrplatform.h: -------------------------------------------------------------------------------- 1 | #ifndef __khrplatform_h_ 2 | #define __khrplatform_h_ 3 | 4 | /* 5 | ** Copyright (c) 2008-2018 The Khronos Group Inc. 6 | ** 7 | ** Permission is hereby granted, free of charge, to any person obtaining a 8 | ** copy of this software and/or associated documentation files (the 9 | ** "Materials"), to deal in the Materials without restriction, including 10 | ** without limitation the rights to use, copy, modify, merge, publish, 11 | ** distribute, sublicense, and/or sell copies of the Materials, and to 12 | ** permit persons to whom the Materials are furnished to do so, subject to 13 | ** the following conditions: 14 | ** 15 | ** The above copyright notice and this permission notice shall be included 16 | ** in all copies or substantial portions of the Materials. 17 | ** 18 | ** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 19 | ** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 20 | ** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 21 | ** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 22 | ** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 23 | ** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 24 | ** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. 25 | */ 26 | 27 | /* Khronos platform-specific types and definitions. 28 | * 29 | * The master copy of khrplatform.h is maintained in the Khronos EGL 30 | * Registry repository at https://github.com/KhronosGroup/EGL-Registry 31 | * The last semantic modification to khrplatform.h was at commit ID: 32 | * 67a3e0864c2d75ea5287b9f3d2eb74a745936692 33 | * 34 | * Adopters may modify this file to suit their platform. Adopters are 35 | * encouraged to submit platform specific modifications to the Khronos 36 | * group so that they can be included in future versions of this file. 37 | * Please submit changes by filing pull requests or issues on 38 | * the EGL Registry repository linked above. 39 | * 40 | * 41 | * See the Implementer's Guidelines for information about where this file 42 | * should be located on your system and for more details of its use: 43 | * http://www.khronos.org/registry/implementers_guide.pdf 44 | * 45 | * This file should be included as 46 | * #include 47 | * by Khronos client API header files that use its types and defines. 48 | * 49 | * The types in khrplatform.h should only be used to define API-specific types. 50 | * 51 | * Types defined in khrplatform.h: 52 | * khronos_int8_t signed 8 bit 53 | * khronos_uint8_t unsigned 8 bit 54 | * khronos_int16_t signed 16 bit 55 | * khronos_uint16_t unsigned 16 bit 56 | * khronos_int32_t signed 32 bit 57 | * khronos_uint32_t unsigned 32 bit 58 | * khronos_int64_t signed 64 bit 59 | * khronos_uint64_t unsigned 64 bit 60 | * khronos_intptr_t signed same number of bits as a pointer 61 | * khronos_uintptr_t unsigned same number of bits as a pointer 62 | * khronos_ssize_t signed size 63 | * khronos_usize_t unsigned size 64 | * khronos_float_t signed 32 bit floating point 65 | * khronos_time_ns_t unsigned 64 bit time in nanoseconds 66 | * khronos_utime_nanoseconds_t unsigned time interval or absolute time in 67 | * nanoseconds 68 | * khronos_stime_nanoseconds_t signed time interval in nanoseconds 69 | * khronos_boolean_enum_t enumerated boolean type. This should 70 | * only be used as a base type when a client API's boolean type is 71 | * an enum. Client APIs which use an integer or other type for 72 | * booleans cannot use this as the base type for their boolean. 73 | * 74 | * Tokens defined in khrplatform.h: 75 | * 76 | * KHRONOS_FALSE, KHRONOS_TRUE Enumerated boolean false/true values. 77 | * 78 | * KHRONOS_SUPPORT_INT64 is 1 if 64 bit integers are supported; otherwise 0. 79 | * KHRONOS_SUPPORT_FLOAT is 1 if floats are supported; otherwise 0. 80 | * 81 | * Calling convention macros defined in this file: 82 | * KHRONOS_APICALL 83 | * KHRONOS_APIENTRY 84 | * KHRONOS_APIATTRIBUTES 85 | * 86 | * These may be used in function prototypes as: 87 | * 88 | * KHRONOS_APICALL void KHRONOS_APIENTRY funcname( 89 | * int arg1, 90 | * int arg2) KHRONOS_APIATTRIBUTES; 91 | */ 92 | 93 | #if defined(__SCITECH_SNAP__) && !defined(KHRONOS_STATIC) 94 | # define KHRONOS_STATIC 1 95 | #endif 96 | 97 | /*------------------------------------------------------------------------- 98 | * Definition of KHRONOS_APICALL 99 | *------------------------------------------------------------------------- 100 | * This precedes the return type of the function in the function prototype. 101 | */ 102 | #if defined(KHRONOS_STATIC) 103 | /* If the preprocessor constant KHRONOS_STATIC is defined, make the 104 | * header compatible with static linking. */ 105 | # define KHRONOS_APICALL 106 | #elif defined(_WIN32) 107 | # define KHRONOS_APICALL __declspec(dllimport) 108 | #elif defined (__SYMBIAN32__) 109 | # define KHRONOS_APICALL IMPORT_C 110 | #elif defined(__ANDROID__) 111 | # define KHRONOS_APICALL __attribute__((visibility("default"))) 112 | #else 113 | # define KHRONOS_APICALL 114 | #endif 115 | 116 | /*------------------------------------------------------------------------- 117 | * Definition of KHRONOS_APIENTRY 118 | *------------------------------------------------------------------------- 119 | * This follows the return type of the function and precedes the function 120 | * name in the function prototype. 121 | */ 122 | #if defined(_WIN32) && !defined(_WIN32_WCE) && !defined(KHRONOS_STATIC) 123 | /* Win32 but not WinCE */ 124 | # define KHRONOS_APIENTRY __stdcall 125 | #else 126 | # define KHRONOS_APIENTRY 127 | #endif 128 | 129 | /*------------------------------------------------------------------------- 130 | * Definition of KHRONOS_APIATTRIBUTES 131 | *------------------------------------------------------------------------- 132 | * This follows the closing parenthesis of the function prototype arguments. 133 | */ 134 | #if defined (__ARMCC_2__) 135 | #define KHRONOS_APIATTRIBUTES __softfp 136 | #else 137 | #define KHRONOS_APIATTRIBUTES 138 | #endif 139 | 140 | /*------------------------------------------------------------------------- 141 | * basic type definitions 142 | *-----------------------------------------------------------------------*/ 143 | #if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || defined(__GNUC__) || defined(__SCO__) || defined(__USLC__) 144 | 145 | 146 | /* 147 | * Using 148 | */ 149 | #include 150 | typedef int32_t khronos_int32_t; 151 | typedef uint32_t khronos_uint32_t; 152 | typedef int64_t khronos_int64_t; 153 | typedef uint64_t khronos_uint64_t; 154 | #define KHRONOS_SUPPORT_INT64 1 155 | #define KHRONOS_SUPPORT_FLOAT 1 156 | 157 | #elif defined(__VMS ) || defined(__sgi) 158 | 159 | /* 160 | * Using 161 | */ 162 | #include 163 | typedef int32_t khronos_int32_t; 164 | typedef uint32_t khronos_uint32_t; 165 | typedef int64_t khronos_int64_t; 166 | typedef uint64_t khronos_uint64_t; 167 | #define KHRONOS_SUPPORT_INT64 1 168 | #define KHRONOS_SUPPORT_FLOAT 1 169 | 170 | #elif defined(_WIN32) && !defined(__SCITECH_SNAP__) 171 | 172 | /* 173 | * Win32 174 | */ 175 | typedef __int32 khronos_int32_t; 176 | typedef unsigned __int32 khronos_uint32_t; 177 | typedef __int64 khronos_int64_t; 178 | typedef unsigned __int64 khronos_uint64_t; 179 | #define KHRONOS_SUPPORT_INT64 1 180 | #define KHRONOS_SUPPORT_FLOAT 1 181 | 182 | #elif defined(__sun__) || defined(__digital__) 183 | 184 | /* 185 | * Sun or Digital 186 | */ 187 | typedef int khronos_int32_t; 188 | typedef unsigned int khronos_uint32_t; 189 | #if defined(__arch64__) || defined(_LP64) 190 | typedef long int khronos_int64_t; 191 | typedef unsigned long int khronos_uint64_t; 192 | #else 193 | typedef long long int khronos_int64_t; 194 | typedef unsigned long long int khronos_uint64_t; 195 | #endif /* __arch64__ */ 196 | #define KHRONOS_SUPPORT_INT64 1 197 | #define KHRONOS_SUPPORT_FLOAT 1 198 | 199 | #elif 0 200 | 201 | /* 202 | * Hypothetical platform with no float or int64 support 203 | */ 204 | typedef int khronos_int32_t; 205 | typedef unsigned int khronos_uint32_t; 206 | #define KHRONOS_SUPPORT_INT64 0 207 | #define KHRONOS_SUPPORT_FLOAT 0 208 | 209 | #else 210 | 211 | /* 212 | * Generic fallback 213 | */ 214 | #include 215 | typedef int32_t khronos_int32_t; 216 | typedef uint32_t khronos_uint32_t; 217 | typedef int64_t khronos_int64_t; 218 | typedef uint64_t khronos_uint64_t; 219 | #define KHRONOS_SUPPORT_INT64 1 220 | #define KHRONOS_SUPPORT_FLOAT 1 221 | 222 | #endif 223 | 224 | 225 | /* 226 | * Types that are (so far) the same on all platforms 227 | */ 228 | typedef signed char khronos_int8_t; 229 | typedef unsigned char khronos_uint8_t; 230 | typedef signed short int khronos_int16_t; 231 | typedef unsigned short int khronos_uint16_t; 232 | 233 | /* 234 | * Types that differ between LLP64 and LP64 architectures - in LLP64, 235 | * pointers are 64 bits, but 'long' is still 32 bits. Win64 appears 236 | * to be the only LLP64 architecture in current use. 237 | */ 238 | #ifdef _WIN64 239 | typedef signed long long int khronos_intptr_t; 240 | typedef unsigned long long int khronos_uintptr_t; 241 | typedef signed long long int khronos_ssize_t; 242 | typedef unsigned long long int khronos_usize_t; 243 | #else 244 | typedef signed long int khronos_intptr_t; 245 | typedef unsigned long int khronos_uintptr_t; 246 | typedef signed long int khronos_ssize_t; 247 | typedef unsigned long int khronos_usize_t; 248 | #endif 249 | 250 | #if KHRONOS_SUPPORT_FLOAT 251 | /* 252 | * Float type 253 | */ 254 | typedef float khronos_float_t; 255 | #endif 256 | 257 | #if KHRONOS_SUPPORT_INT64 258 | /* Time types 259 | * 260 | * These types can be used to represent a time interval in nanoseconds or 261 | * an absolute Unadjusted System Time. Unadjusted System Time is the number 262 | * of nanoseconds since some arbitrary system event (e.g. since the last 263 | * time the system booted). The Unadjusted System Time is an unsigned 264 | * 64 bit value that wraps back to 0 every 584 years. Time intervals 265 | * may be either signed or unsigned. 266 | */ 267 | typedef khronos_uint64_t khronos_utime_nanoseconds_t; 268 | typedef khronos_int64_t khronos_stime_nanoseconds_t; 269 | #endif 270 | 271 | /* 272 | * Dummy value used to pad enum types to 32 bits. 273 | */ 274 | #ifndef KHRONOS_MAX_ENUM 275 | #define KHRONOS_MAX_ENUM 0x7FFFFFFF 276 | #endif 277 | 278 | /* 279 | * Enumerated boolean type 280 | * 281 | * Values other than zero should be considered to be true. Therefore 282 | * comparisons should not be made against KHRONOS_TRUE. 283 | */ 284 | typedef enum { 285 | KHRONOS_FALSE = 0, 286 | KHRONOS_TRUE = 1, 287 | KHRONOS_BOOLEAN_ENUM_FORCE_SIZE = KHRONOS_MAX_ENUM 288 | } khronos_boolean_enum_t; 289 | 290 | #endif /* __khrplatform_h_ */ 291 | -------------------------------------------------------------------------------- /extern/stb_image/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_library(stb_image OBJECT) 2 | target_sources(stb_image PRIVATE stb_image.cpp stb_image.h) 3 | target_include_directories(stb_image PRIVATE ${CMAKE_CURRENT_LIST_DIR} SYSTEM INTERFACE ${CMAKE_CURRENT_LIST_DIR}) 4 | 5 | if(MSVC) 6 | target_compile_options(stb_image PRIVATE /w) 7 | else(MSVC) 8 | target_compile_options(stb_image PRIVATE -w) 9 | endif(MSVC) -------------------------------------------------------------------------------- /extern/stb_image/stb_image.cpp: -------------------------------------------------------------------------------- 1 | #define STB_IMAGE_IMPLEMENTATION 2 | #include "stb_image.h" -------------------------------------------------------------------------------- /extern/tiny_obj_loader/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_library(tiny_obj_loader OBJECT) 2 | target_sources(tiny_obj_loader PRIVATE tiny_obj_loader.cpp tiny_obj_loader.h) 3 | target_include_directories(tiny_obj_loader PRIVATE ${CMAKE_CURRENT_LIST_DIR} SYSTEM INTERFACE ${CMAKE_CURRENT_LIST_DIR}) 4 | 5 | if(MSVC) 6 | target_compile_options(tiny_obj_loader PRIVATE /w) 7 | else(MSVC) 8 | target_compile_options(tiny_obj_loader PRIVATE -w) 9 | endif(MSVC) -------------------------------------------------------------------------------- /extern/tiny_obj_loader/tiny_obj_loader.cpp: -------------------------------------------------------------------------------- 1 | #define TINYOBJLOADER_IMPLEMENTATION 2 | #include "tiny_obj_loader.h" -------------------------------------------------------------------------------- /glm_ext.h: -------------------------------------------------------------------------------- 1 | #ifdef _WIN32 2 | #pragma warning(push, 0) 3 | #endif 4 | #include 5 | #ifdef _WIN32 6 | #pragma warning(pop) 7 | #endif 8 | -------------------------------------------------------------------------------- /main.cpp: -------------------------------------------------------------------------------- 1 | #define GLFW_INCLUDE_NONE 2 | #define _USE_MATH_DEFINES 3 | 4 | #include "constants.h" 5 | #include "renderers/RenderManager.h" 6 | #include "GameTime.h" 7 | 8 | #include "particles/ParticleManager.h" 9 | #include "particles/ParticleSystem.h" 10 | #include "Window.h" 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include "glm_ext.h" 19 | #define GLM_ENABLE_EXPERIMENTAL 20 | #include 21 | 22 | using namespace glm; 23 | 24 | constexpr float SKYBOX_SIZE = 200.0f; 25 | 26 | auto setProjection(int inputWinX, int inputWinY) { 27 | // float aspect = (float) winX / winY; 28 | // FOV angle is in radians 29 | return glm::perspective(constants::PI / 4.0, double(inputWinX) / double(inputWinY), 1.0, 800.0); 30 | } 31 | 32 | int main(int argc, char** argv) { 33 | InputState input; 34 | glm::mat4 projection; 35 | Player* player; 36 | 37 | if (argc != 2) { 38 | std::cerr << "USAGE: " << argv[0] << " basic|physics" << std::endl; 39 | exit(1); 40 | } 41 | 42 | // Check if desired controls are basic or physics 43 | bool basic_controls = strcmp(argv[1], "basic") == 0; 44 | 45 | if (basic_controls) { 46 | std::cout << "Controls: \n\tw - forward\n\ts - backwards\n\ta/d - turn left/right" << std::endl; 47 | } else { 48 | std::cout << "Controls: \n\tw - throttle\n\ts - brake\n\ta/d - steer left/right\n\tspace - handbrake" << std::endl; 49 | } 50 | 51 | Window window; 52 | window.set_key_callback([&](GLFWwindow* window, int key, int scancode, int action, int mods) { 53 | // Terminate program if escape is pressed 54 | if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS) { 55 | glfwSetWindowShouldClose(window, GLFW_TRUE); 56 | } 57 | player->handleKeyboardEvents(window, key, scancode, action, mods); 58 | }); 59 | 60 | window.set_mouse_position_callback([&](GLFWwindow* /*window*/, double x, double y) { 61 | input.update((float)x, (float)y); 62 | }); 63 | 64 | window.set_mouse_scroll_callback([&](GLFWwindow* /*window*/, double xoffset, double yoffset) { 65 | input.updateScroll((float)xoffset, (float)yoffset); 66 | }); 67 | 68 | window.set_mouse_button_callback([&](GLFWwindow* /*window*/, int button, int action, int /*mods*/) { 69 | if (button == GLFW_MOUSE_BUTTON_RIGHT && action == GLFW_PRESS) { 70 | input.rMousePressed = true; 71 | } else if (button == GLFW_MOUSE_BUTTON_RIGHT && action == GLFW_RELEASE) { 72 | input.rMousePressed = false; 73 | } else if (button == GLFW_MOUSE_BUTTON_LEFT && action == GLFW_PRESS) { 74 | input.lMousePressed = true; 75 | } else if (button == GLFW_MOUSE_BUTTON_LEFT && action == GLFW_RELEASE) { 76 | input.lMousePressed = false; 77 | } 78 | }); 79 | 80 | window.set_window_reshape_callback([&](GLFWwindow* /*window*/, int x, int y) { 81 | projection = setProjection(x, y); 82 | glViewport(0, 0, x, y); 83 | }); 84 | 85 | srand(static_cast(time(nullptr))); 86 | // clang-format off 87 | // Define skybox textures 88 | // The current skybox is unfortunately quite low res and has compression issues. 89 | // However, I wanted a skybox that had no terrain AND a visible sun which was surprisingly hard to find. 90 | std::vector skyboxTextures = { 91 | "res/sky/sky_right.tga", 92 | "res/sky/sky_left.tga", 93 | "res/sky/sky_top.tga", 94 | "res/sky/sky_bottom.tga", 95 | "res/sky/sky_back.tga", 96 | "res/sky/sky_front.tga" 97 | }; 98 | // clang-format on 99 | // Load all of the requires models from disk. 100 | Model barrelModel = Loader::getLoader()->loadModel("res/Barrel/Barrel02.obj"); 101 | Model playerModel = Loader::getLoader()->loadModel("res/car/car-n.obj"); 102 | Model fenceModel = Loader::getLoader()->loadModel("res/fence/fence.obj"); 103 | Model coneModel = Loader::getLoader()->loadModel("res/cone/cone2_obj.obj"); 104 | Model treeModel = Loader::getLoader()->loadModel("res/tree/PineTree03.obj"); 105 | Model stumpModel = Loader::getLoader()->loadModel("res/stump/TreeStump03.obj"); 106 | 107 | // Vector to hold all of the world entities. 108 | std::vector entities; 109 | // Vector to hold lights 110 | std::vector lights; 111 | 112 | // Create the skybox with the textures defined. 113 | SkyboxRenderer skybox(skyboxTextures, SKYBOX_SIZE); 114 | RenderManager manager; 115 | 116 | // Create Terrain using blend map, height map and all of the remaining texture components. 117 | std::vector terrainImages = {"res/terrain/blendMap.png", "res/terrain/grass.jpg", "res/terrain/road.jpg", 118 | "res/terrain/dirt.png", "res/terrain/mud.jpg"}; 119 | Terrain* terrain = Terrain::loadTerrain(terrainImages, "res/terrain/heightmap.png"); 120 | // Moves the terrain model to be centered about the origin. 121 | terrain->setPosition(vec3(-Terrain::TERRAIN_SIZE / 2, 0.0f, -Terrain::TERRAIN_SIZE / 2)); 122 | 123 | // Load dust texture and create particle system. 124 | GLuint dust_texture = Loader::getLoader()->loadTexture("./res/dust_single.png"); 125 | ParticleSystem particleSystem(30.0f, 3.0f, 0.5f, 0.5f, dust_texture); 126 | 127 | // Create the player object, scaling for the model, and setting its position in the world to somewhere interesting. 128 | player = new Player(&playerModel, terrain, basic_controls); 129 | player->setScale(vec3(0.4f, 0.4f, 0.4f)); 130 | player->setPosition(terrain->getPositionFromPixel(555, 751)); 131 | player->setRotationY((float)5.f * constants::PI / 8.f); 132 | entities.push_back(player); 133 | 134 | // Initialisation of camera, projection matrix 135 | projection = setProjection(window.get_width(), window.get_height()); 136 | Camera* cam = new PlayerCamera(player); 137 | 138 | // Create light sources 139 | auto* sun = new Light(); 140 | sun->position = 141 | vec4(-1.25 * SKYBOX_SIZE / 10, 2.5 * SKYBOX_SIZE / 10, 3 * SKYBOX_SIZE / 10, 0.0f); // w = 0 - directional 142 | sun->specular = vec3(1.0f, 1.0f, 1.0f); 143 | sun->diffuse = vec3(0.7f, 0.7f, 0.7f); 144 | sun->ambient = vec3(0.1f, 0.1f, 0.1f); 145 | lights.push_back(sun); 146 | 147 | auto* headlight = new Light(); 148 | headlight->position = vec4(2.0f, 8.0f, 0.0f, 1.0f); 149 | headlight->specular = vec3(0.8f, 0.8f, 0.4f); 150 | headlight->diffuse = vec3(0.8f, 0.8f, 0.4f); 151 | headlight->coneDirection = vec3(0.0f, -1.0f, 0.0f); 152 | headlight->coneAngle = constants::PI / 4.f; 153 | headlight->radius = 10.0f; 154 | lights.push_back(headlight); 155 | 156 | // Adds entities to random positions on the map 157 | const size_t RAND_ENTITIES = 500; 158 | for (size_t i = 0; i < RAND_ENTITIES; i += 2) { 159 | int selection = rand() % 2; 160 | Entity* ent; 161 | switch (selection) { 162 | case 0: 163 | ent = new Entity(&treeModel); 164 | break; 165 | case 1: 166 | ent = new Entity(&stumpModel); 167 | ent->setScale(glm::vec3(0.5, 0.5, 0.5)); 168 | break; 169 | default: 170 | throw std::runtime_error("Unreachable statement"); 171 | } 172 | 173 | ent->setPosition(terrain->getPositionFromPixel(rand() % 1024, rand() % 1024)); 174 | entities.push_back(ent); 175 | } 176 | 177 | // Set of pre calculated cone positions on corners of the track 178 | // clang-format off 179 | std::vector conePositions = { 180 | 263, 262, 226, 250, 209, 273, 181 | 213, 299, 342, 717, 329, 734, 182 | 326, 751, 354, 755, 372, 754, 183 | 750, 400, 765, 396, 748, 381, 184 | 828, 480, 842, 476, 854, 478, 185 | 852, 500, 852, 521, 842, 547, 186 | 772, 402 187 | }; 188 | // clang-format on 189 | // Creates cones from the positions and adds them. 190 | for (size_t i = 0; i < conePositions.size(); i += 2) { 191 | auto* cone = new Entity(&coneModel); 192 | cone->setPosition(terrain->getPositionFromPixel(conePositions[i], conePositions[i + 1])); 193 | cone->setScale(vec3(0.01f, 0.01f, 0.01f)); // The cone model was MASSIVE 194 | entities.push_back(cone); 195 | } 196 | 197 | // Add the bordering fences to the map. 198 | float fenceSize = fenceModel.getRangeInDim(0).second - fenceModel.getRangeInDim(0).first; 199 | for (float x = -Terrain::TERRAIN_SIZE / 2; x < Terrain::TERRAIN_SIZE / 2; x += fenceSize) { 200 | auto* fence = new Entity(&fenceModel); 201 | fence->setPosition(vec3(x, 0.0f, Terrain::TERRAIN_SIZE / 2 - 1.0f)); 202 | entities.push_back(fence); 203 | 204 | fence = new Entity(&fenceModel); 205 | fence->setPosition(vec3(x, 0.0f, -Terrain::TERRAIN_SIZE / 2 + 1.0f)); 206 | entities.push_back(fence); 207 | } 208 | 209 | for (float z = -Terrain::TERRAIN_SIZE / 2; z < Terrain::TERRAIN_SIZE / 2; z += fenceSize) { 210 | auto* fence = new Entity(&fenceModel); 211 | fence->setPosition(vec3(Terrain::TERRAIN_SIZE / 2 - 1.0f, 0.0f, z)); 212 | fence->rotateY(-constants::PI / 2); 213 | entities.push_back(fence); 214 | 215 | fence = new Entity(&fenceModel); 216 | fence->setPosition(vec3(-Terrain::TERRAIN_SIZE / 2 + 1.0f, 0.0f, z)); 217 | fence->rotateY(-constants::PI / 2); 218 | entities.push_back(fence); 219 | } 220 | 221 | // Goes through each entity and aligns its bottom edge with the terrain at that position. 222 | for (auto& entity : entities) { 223 | entity->placeBottomEdge(terrain->getHeight(entity->getPosition().x, entity->getPosition().z)); 224 | } 225 | 226 | // Create the large lake 227 | auto* water = new Entity(); 228 | water->setScale(vec3(100.0f, 1.0f, 50.0f)); 229 | water->setPosition(terrain->getPositionFromPixel(650, 826)); 230 | water->setPosition(glm::vec3(water->getPosition().x, 0.4f, water->getPosition().z)); 231 | 232 | // Create the object for handling rendering to texture for shadows. 233 | ShadowMap shadowMap(player, lights[0], 4096); 234 | 235 | // Main logic/render loop. 236 | while (!glfwWindowShouldClose(window.get_window())) { 237 | GameTime::getGameTime()->update(); 238 | cam->update(input); 239 | 240 | // Render entire scene 241 | manager.render(entities, lights, terrain, water, skybox, shadowMap, cam, projection, window.get_width(), 242 | window.get_height()); 243 | 244 | // Updates all particles and entities. 245 | ParticleManager::getParticleManager()->update(); 246 | for (auto* entity : entities) { 247 | entity->update(); 248 | } 249 | 250 | // Generate dust particles at the players positions if the car is going past enough or moving 251 | if (player->absVel > 5.0f || player->getThrottle() > 0.1f || (basic_controls && player->getBrake() > 0.1f)) { 252 | particleSystem.generateParticles(player->getPosition() - player->calculateDirectionVector()); 253 | } 254 | 255 | // Update the position of the car headlights 256 | headlight->position = vec4(player->getPosition() + vec3(0.0f, 0.1f, 0.0f), 1.0f); 257 | headlight->coneDirection = player->calculateDirectionVector(); 258 | 259 | glFlush(); 260 | 261 | glfwSwapBuffers(window.get_window()); 262 | glfwPollEvents(); 263 | } 264 | 265 | // Cleanup program, delete all the dynamic entities. 266 | delete player; 267 | delete water; 268 | for (auto* entity : entities) { 269 | delete entity; 270 | } 271 | 272 | glfwDestroyWindow(window.get_window()); 273 | glfwTerminate(); 274 | 275 | return 0; 276 | } 277 | -------------------------------------------------------------------------------- /particles/Particle.cpp: -------------------------------------------------------------------------------- 1 | #include "Particle.h" 2 | 3 | #include "../GameTime.h" 4 | 5 | #define GRAVITY_ACCELERATION -9.81f 6 | 7 | Particle::Particle( 8 | const glm::vec3& position, const glm::vec3& velocity, float gravityEffect, float lifeDuration, GLuint texture) { 9 | this->m_position = position; 10 | this->velocity = velocity; 11 | this->gravityEffect = gravityEffect; 12 | this->lifeDuration = lifeDuration; 13 | this->elapsedTime = 0.0f; 14 | this->texid = texture; 15 | } 16 | 17 | // Returns true if the particle is still alive after this update. 18 | bool Particle::update() { 19 | float dt = GameTime::getGameTime()->getDt(); 20 | velocity.y += GRAVITY_ACCELERATION * gravityEffect * dt; 21 | m_position = m_position + (velocity * dt); 22 | elapsedTime += dt; 23 | return elapsedTime < lifeDuration; 24 | } 25 | 26 | GLuint Particle::getTextureID() const { 27 | return texid; 28 | } 29 | -------------------------------------------------------------------------------- /particles/Particle.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "../entities/Entity.h" 4 | 5 | #include 6 | #include 7 | 8 | class Particle : public Entity { 9 | protected: 10 | glm::vec3 velocity; 11 | float gravityEffect; 12 | float lifeDuration; 13 | float elapsedTime; 14 | GLuint texid; 15 | 16 | public: 17 | Particle( 18 | const glm::vec3& position, const glm::vec3& velocity, float gravityEffect, float lifeDuration, GLuint texture); 19 | bool update() override; 20 | GLuint getTextureID() const; 21 | }; 22 | -------------------------------------------------------------------------------- /particles/ParticleManager.cpp: -------------------------------------------------------------------------------- 1 | #include "ParticleManager.h" 2 | 3 | // Initialise singleton 4 | ParticleManager* ParticleManager::particleManager = nullptr; 5 | 6 | ParticleManager* ParticleManager::getParticleManager() { 7 | if (particleManager == nullptr) { 8 | particleManager = new ParticleManager(); 9 | } 10 | 11 | return particleManager; 12 | } 13 | 14 | void ParticleManager::update() { 15 | // Update and kill particles 16 | for (auto particles_it = particles.begin(); particles_it != particles.end(); particles_it++) { 17 | if (!(*particles_it)->update()) { 18 | delete *particles_it; 19 | // TODO make particles a linked list so this erase is less costly. 20 | particles_it = particles.erase(particles_it); 21 | 22 | // If we just erased the last item in the vector we need to break out instead of iterating to the next 23 | // element. 24 | if (particles_it == particles.end()) { 25 | break; 26 | } 27 | } 28 | } 29 | } 30 | 31 | void ParticleManager::addParticle(Particle* particle) { 32 | particles.push_back(particle); 33 | } 34 | 35 | void ParticleManager::render(const glm::mat4& view, const glm::mat4& proj) { 36 | renderer.render(particles, view, proj); 37 | } 38 | -------------------------------------------------------------------------------- /particles/ParticleManager.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Particle.h" 4 | #include "ParticleRenderer.h" 5 | #include 6 | #include 7 | 8 | class ParticleManager { 9 | private: 10 | static ParticleManager* particleManager; 11 | ParticleManager() = default; 12 | 13 | ParticleRenderer renderer; 14 | std::vector particles; 15 | 16 | public: 17 | static ParticleManager* getParticleManager(); 18 | 19 | // Should be called once per frame 20 | void update(); 21 | 22 | void addParticle(Particle*); 23 | void render(const glm::mat4& view, const glm::mat4& proj); 24 | }; 25 | -------------------------------------------------------------------------------- /particles/ParticleRenderer.cpp: -------------------------------------------------------------------------------- 1 | #include "ParticleRenderer.h" 2 | 3 | #include "../Loader.h" 4 | 5 | #include 6 | 7 | ParticleRenderer::ParticleRenderer() { 8 | // clang-format off 9 | // Create a square mesh encompassing the viewport 10 | std::vector vertices = { 11 | -0.5f, 0.5f, 0.0f, 12 | -0.5f, -0.5f, 0.0f, 13 | 0.5f, -0.5f, 0.0f, 14 | 0.5f, 0.5f, 0.0f, 15 | }; 16 | 17 | std::vector texcoords = { 18 | 0.0f, 1.0f, 19 | 0.0f, 0.0f, 20 | 1.0f, 0.0f, 21 | 1.0f, 1.0f, 22 | }; 23 | 24 | std::vector indices = { 25 | 0, 1, 2, 26 | 2, 3, 0 27 | }; 28 | // clang-format on 29 | quad = Loader::getLoader()->loadModelComponent(vertices, indices, texcoords); 30 | } 31 | 32 | void ParticleRenderer::render(std::vector particles, glm::mat4 view, glm::mat4 proj) { 33 | shader.enable(); 34 | 35 | glActiveTexture(GL_TEXTURE0); 36 | 37 | glBindVertexArray(quad.getVaoID()); 38 | glEnable(GL_BLEND); 39 | glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); 40 | glDepthMask(GL_FALSE); 41 | 42 | shader.loadCamera(view); 43 | shader.loadProjection(proj); 44 | 45 | glEnableVertexAttribArray(0); 46 | glEnableVertexAttribArray(1); 47 | 48 | for (const auto& particle : particles) { 49 | shader.loadParticle(particle, view); 50 | glBindTexture(GL_TEXTURE_2D, particle->getTextureID()); 51 | glDrawElements(GL_TRIANGLES, static_cast(quad.getIndexCount()), GL_UNSIGNED_INT, (void*)0); 52 | } 53 | 54 | glDepthMask(GL_TRUE); 55 | glDisable(GL_BLEND); 56 | glDisableVertexAttribArray(0); 57 | glDisableVertexAttribArray(1); 58 | glBindVertexArray(0); 59 | shader.disable(); 60 | } -------------------------------------------------------------------------------- /particles/ParticleRenderer.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "ParticleShader.h" 4 | #include "../Model.h" 5 | 6 | #include 7 | #include 8 | 9 | class ParticleRenderer { 10 | private: 11 | ParticleShader shader; 12 | ModelComponent quad; 13 | 14 | public: 15 | ParticleRenderer(); 16 | 17 | void render(std::vector particles, glm::mat4 view, glm::mat4 proj); 18 | }; 19 | -------------------------------------------------------------------------------- /particles/ParticleShader.cpp: -------------------------------------------------------------------------------- 1 | #include "ParticleShader.h" 2 | 3 | ParticleShader::ParticleShader() : ShaderProgram(PARTICLE_VERTEX_SHADER, PARTICLE_FRAGMENT_SHADER) { 4 | bindUniformLocations(); 5 | } 6 | 7 | void ParticleShader::bindUniformLocations() { 8 | location_projection = glGetUniformLocation(shaderID, "projection"); 9 | location_model = glGetUniformLocation(shaderID, "model"); 10 | location_view = glGetUniformLocation(shaderID, "view"); 11 | } 12 | 13 | void ParticleShader::loadCamera(const glm::mat4& view) { 14 | loadUniformValue(location_view, view); 15 | } 16 | 17 | void ParticleShader::loadParticle(const Particle *particle, const glm::mat4& viewmtx) { 18 | auto modelmtx = particle->calculateModelMatrix(); 19 | 20 | // Apply transpose of view matrix to make sure the particle always faces the camera. 21 | // TODO - test if glm::transpose can be used here 22 | modelmtx[0][0] = viewmtx[0][0]; 23 | modelmtx[0][1] = viewmtx[1][0]; 24 | modelmtx[0][2] = viewmtx[2][0]; 25 | modelmtx[1][0] = viewmtx[0][1]; 26 | modelmtx[1][1] = viewmtx[1][1]; 27 | modelmtx[1][2] = viewmtx[2][1]; 28 | modelmtx[2][0] = viewmtx[0][2]; 29 | modelmtx[2][1] = viewmtx[1][2]; 30 | modelmtx[2][2] = viewmtx[2][2]; 31 | 32 | loadUniformValue(location_model, modelmtx); 33 | } 34 | 35 | void ParticleShader::loadProjection(const glm::mat4& proj) { 36 | loadUniformValue(location_projection, proj); 37 | } 38 | -------------------------------------------------------------------------------- /particles/ParticleShader.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "../entities/Camera.h" 4 | 5 | #include "../shaders/ShaderProgram.h" 6 | #include "Particle.h" 7 | 8 | #include 9 | #include 10 | 11 | #include 12 | #include 13 | 14 | const std::string PARTICLE_VERTEX_SHADER = "particles/particle.vert"; 15 | const std::string PARTICLE_FRAGMENT_SHADER = "particles/particle.frag"; 16 | 17 | class ParticleShader : public ShaderProgram { 18 | private: 19 | GLuint location_projection; 20 | GLuint location_model; 21 | GLuint location_view; 22 | 23 | public: 24 | ParticleShader(); 25 | 26 | void bindUniformLocations(); 27 | void loadCamera(const glm::mat4& view); 28 | void loadParticle(const Particle *particle, const glm::mat4& viewmtx); 29 | void loadProjection(const glm::mat4& proj); 30 | }; 31 | -------------------------------------------------------------------------------- /particles/ParticleSystem.cpp: -------------------------------------------------------------------------------- 1 | #include "ParticleSystem.h" 2 | 3 | #include "Particle.h" 4 | #include "ParticleManager.h" 5 | #include "../GameTime.h" 6 | 7 | ParticleSystem::ParticleSystem( 8 | float particlesPerSecond, float particleSpeed, float gravityFactor, float lifeDuration, GLuint textureid) { 9 | this->particlesPerSecond = particlesPerSecond; 10 | this->particleSpeed = particleSpeed; 11 | this->gravityFactor = gravityFactor; 12 | this->lifeDuration = lifeDuration; 13 | this->textureid = textureid; 14 | this->renderQueue = 0.0f; 15 | } 16 | 17 | void ParticleSystem::generateParticles(glm::vec3 emissionPoint) { 18 | renderQueue += particlesPerSecond * GameTime::getGameTime()->getDt(); 19 | while (renderQueue > 1.0f) { 20 | glm::vec3 velo((rand() % 100) / 50.f - 1.0f, 1.0f, (rand() % 100) / 50.f - 1.0f); 21 | velo = glm::normalize(velo); 22 | velo *= particleSpeed; 23 | auto* p = new Particle(emissionPoint, velo, gravityFactor, lifeDuration, textureid); 24 | float scaleVal = (rand() % 100) / 100.f; 25 | p->setScale(glm::vec3(scaleVal, scaleVal, scaleVal)); 26 | 27 | ParticleManager::getParticleManager()->addParticle(p); 28 | renderQueue -= 1.0f; 29 | } 30 | } -------------------------------------------------------------------------------- /particles/ParticleSystem.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "ParticleManager.h" 4 | 5 | #include 6 | #include 7 | 8 | class ParticleSystem { 9 | private: 10 | float particlesPerSecond; 11 | float particleSpeed; 12 | float gravityFactor; 13 | float lifeDuration; 14 | GLuint textureid; 15 | float renderQueue; 16 | 17 | public: 18 | ParticleSystem( 19 | float particlesPerSecond, float particleSpeed, float gravityFactor, float lifeDuration, GLuint textureid); 20 | 21 | void generateParticles(glm::vec3 emissionPoint); 22 | }; 23 | -------------------------------------------------------------------------------- /particles/particle.frag: -------------------------------------------------------------------------------- 1 | #version 330 2 | 3 | in vec2 st; 4 | 5 | uniform sampler2D texMap; 6 | 7 | out vec4 out_colour; 8 | 9 | void main(void){ 10 | out_colour = texture(texMap, st); 11 | } -------------------------------------------------------------------------------- /particles/particle.vert: -------------------------------------------------------------------------------- 1 | #version 330 2 | 3 | layout (location = 0) in vec3 position; 4 | layout (location = 1) in vec2 tex_coord; 5 | 6 | uniform mat4 projection; 7 | uniform mat4 view; 8 | uniform mat4 model; 9 | 10 | out vec2 st; 11 | 12 | void main(void){ 13 | gl_Position = projection * view * model * vec4(position, 1.0); 14 | st = vec2(tex_coord.x, 1.0 - tex_coord.y); 15 | } -------------------------------------------------------------------------------- /renderers/EntityRenderer.cpp: -------------------------------------------------------------------------------- 1 | #include "EntityRenderer.h" 2 | 3 | void EntityRenderer::render(const std::vector& entities, const std::vector& lights, 4 | const glm::mat4& view, const glm::mat4& proj, GLuint reflectionTexture, const glm::vec4& clipPlane) { 5 | m_shader.enable(); 6 | m_shader.loadProjection(proj); 7 | m_shader.loadLights(lights); 8 | m_shader.loadView(view); 9 | m_shader.loadClipPlane(clipPlane); 10 | 11 | glActiveTexture(GL_TEXTURE1); 12 | glBindTexture(GL_TEXTURE_CUBE_MAP, reflectionTexture); 13 | 14 | for (const auto& entity : entities) { 15 | m_shader.loadEntity(entity); 16 | if (entity->getModel() != nullptr) { 17 | renderModel(entity->getModel()); 18 | } 19 | } 20 | 21 | m_shader.disable(); 22 | } 23 | 24 | void EntityRenderer::render(const std::vector& entities, const std::vector& lights, 25 | const glm::mat4& view, const glm::mat4& proj, GLuint reflectionTexture, const glm::mat4& depthView, 26 | const glm::mat4& depthProj, GLuint shadowMap, const glm::vec4& clipPlane) { 27 | m_shader.enable(); 28 | m_shader.loadProjection(proj); 29 | m_shader.loadLights(lights); 30 | m_shader.loadView(view); 31 | m_shader.loadClipPlane(clipPlane); 32 | 33 | glm::mat4 biasMatrix(0.5, 0.0, 0.0, 0.0, 0.0, 0.5, 0.0, 0.0, 0.0, 0.0, 0.5, 0.0, 0.5, 0.5, 0.5, 1.0); 34 | 35 | glm::mat4 depthBiasPV = biasMatrix * depthProj * depthView; 36 | m_shader.loadDepth(depthBiasPV); 37 | 38 | glActiveTexture(GL_TEXTURE1); 39 | glBindTexture(GL_TEXTURE_CUBE_MAP, reflectionTexture); 40 | glActiveTexture(GL_TEXTURE2); 41 | glBindTexture(GL_TEXTURE_2D, shadowMap); 42 | 43 | for (const auto& entity : entities) { 44 | m_shader.loadEntity(entity); 45 | if (entity->getModel() != nullptr) { 46 | renderModel(entity->getModel()); 47 | } 48 | } 49 | 50 | m_shader.disable(); 51 | } 52 | 53 | void EntityRenderer::renderModel(const Model* model) { 54 | for (const auto& component : model->getModelComponents()) { 55 | m_shader.loadModelComponent(component); 56 | 57 | glActiveTexture(GL_TEXTURE0); 58 | glBindTexture(GL_TEXTURE_2D, component.getTextureID()); 59 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); 60 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); 61 | 62 | glBindVertexArray(component.getVaoID()); 63 | 64 | glEnableVertexAttribArray(0); 65 | glEnableVertexAttribArray(1); 66 | glEnableVertexAttribArray(2); 67 | 68 | glDrawElements(GL_TRIANGLES, static_cast(component.getIndexCount()), GL_UNSIGNED_INT, (void*)0); 69 | 70 | glDisableVertexAttribArray(0); 71 | glDisableVertexAttribArray(1); 72 | glDisableVertexAttribArray(2); 73 | glBindVertexArray(0); 74 | } 75 | } -------------------------------------------------------------------------------- /renderers/EntityRenderer.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define _USE_MATH_DEFINES 4 | 5 | #include "../shaders/EntityShader.h" 6 | #include "../entities/Light.h" 7 | #include "../entities/Camera.h" 8 | #include "../Model.h" 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | #include 17 | #include "glm_ext.h" 18 | #include 19 | 20 | class EntityRenderer { 21 | private: 22 | EntityShader m_shader; 23 | 24 | public: 25 | void render(const std::vector& entities, const std::vector& lights, const glm::mat4& view, 26 | const glm::mat4& proj, GLuint reflectionTexture, const glm::vec4& clipPlane); 27 | void render(const std::vector& entities, const std::vector& lights, const glm::mat4& view, 28 | const glm::mat4& proj, GLuint reflectionTexture, const glm::mat4& depthView, const glm::mat4& depthProj, 29 | GLuint shadowMap, const glm::vec4& clipPlane); 30 | void renderModel(const Model* model); 31 | }; 32 | -------------------------------------------------------------------------------- /renderers/RenderManager.cpp: -------------------------------------------------------------------------------- 1 | #include "RenderManager.h" 2 | 3 | #include 4 | #include "../particles/Particle.h" 5 | #include "../particles/ParticleManager.h" 6 | #include "../particles/ParticleSystem.h" 7 | 8 | const float WATER_PLANE_HEIGHT = 0.398918f; 9 | 10 | RenderManager::RenderManager() : reflectionBuffer(640, 320), refractionBuffer(1280, 720) { 11 | reflectionBuffer.addColourTexture(); 12 | reflectionBuffer.addDepthBuffer(); 13 | 14 | refractionBuffer.addColourTexture(); 15 | refractionBuffer.addDepthTexture(); 16 | } 17 | 18 | void RenderManager::render(const std::vector& entities, const std::vector& lights, Terrain* terrain, 19 | Entity* water, SkyboxRenderer& skybox, ShadowMap& shadowMap, Camera* cam, const glm::mat4& projection, 20 | int winX, int winY) { 21 | // SHADOW PASS 22 | glDisable(GL_CLIP_DISTANCE0); 23 | shadowMap.bind(); 24 | renderer.render(entities, lights, shadowMap.getView(), shadowMap.getProjection(), skybox.getSkyboxTexture(), 25 | glm::vec4(0, 1, 0, 10000)); 26 | shadowMap.unbind(); 27 | 28 | // REFRACTION PASS 29 | glEnable(GL_CLIP_DISTANCE0); 30 | refractionBuffer.bind(); 31 | terrainRenderer.render(terrain, lights, cam->getViewMtx(), projection, shadowMap.getView(), 32 | shadowMap.getProjection(), shadowMap.getTextureID(), glm::vec4(0, -1, 0, water->getPosition().y)); 33 | skybox.render(cam->getViewMtx(), projection); 34 | renderer.render(entities, lights, cam->getViewMtx(), projection, skybox.getSkyboxTexture(), shadowMap.getView(), 35 | shadowMap.getProjection(), shadowMap.getTextureID(), glm::vec4(0, -1, 0, water->getPosition().y)); 36 | ParticleManager::getParticleManager()->render(cam->getViewMtx(), projection); 37 | refractionBuffer.unbind(); 38 | 39 | // REFLECTION PASS 40 | glEnable(GL_CLIP_DISTANCE0); 41 | glm::mat4 invView = cam->getInverted(WATER_PLANE_HEIGHT); 42 | reflectionBuffer.bind(); 43 | terrainRenderer.render(terrain, lights, invView, projection, shadowMap.getView(), shadowMap.getProjection(), 44 | shadowMap.getTextureID(), glm::vec4(0, 1, 0, -water->getPosition().y)); 45 | skybox.render(invView, projection); 46 | renderer.render(entities, lights, invView, projection, skybox.getSkyboxTexture(), shadowMap.getView(), 47 | shadowMap.getProjection(), shadowMap.getTextureID(), glm::vec4(0, 1, 0, -water->getPosition().y)); 48 | ParticleManager::getParticleManager()->render(invView, projection); 49 | reflectionBuffer.unbind(); 50 | 51 | // NORMAL PASS 52 | glDisable(GL_CLIP_DISTANCE0); 53 | glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT); 54 | glViewport(0, 0, winX, winY); 55 | 56 | terrainRenderer.render(terrain, lights, cam->getViewMtx(), projection, shadowMap.getView(), 57 | shadowMap.getProjection(), shadowMap.getTextureID(), glm::vec4(0, 1, 0, 10000)); 58 | skybox.render(cam->getViewMtx(), projection); 59 | renderer.render(entities, lights, cam->getViewMtx(), projection, skybox.getSkyboxTexture(), shadowMap.getView(), 60 | shadowMap.getProjection(), shadowMap.getTextureID(), glm::vec4(0, 1, 0, 10000)); 61 | ParticleManager::getParticleManager()->render(cam->getViewMtx(), projection); 62 | waterRenderer.render(water, cam->getViewMtx(), projection, refractionBuffer.getColourTexture(), 63 | reflectionBuffer.getColourTexture(), cam->getPosition(), lights[0]); 64 | } 65 | -------------------------------------------------------------------------------- /renderers/RenderManager.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "EntityRenderer.h" 4 | #include "TerrainRenderer.h" 5 | #include "SkyboxRenderer.h" 6 | #include "../water/WaterRenderer.h" 7 | #include "../entities/Entity.h" 8 | #include "../entities/Light.h" 9 | #include "../entities/Camera.h" 10 | #include "../entities/Terrain.h" 11 | #include "../FrameBuffer.h" 12 | #include "../ShadowMap.h" 13 | 14 | #include 15 | #include 16 | 17 | class RenderManager { 18 | private: 19 | FrameBuffer reflectionBuffer; 20 | FrameBuffer refractionBuffer; 21 | 22 | EntityRenderer renderer; 23 | TerrainRenderer terrainRenderer; 24 | WaterRenderer waterRenderer; 25 | 26 | public: 27 | RenderManager(); 28 | 29 | // Renders the entire scene, including 4 passes (shadow, refraction, reflection, total) 30 | // Lots of parameters, some of these could be maybe moved to the constructor as they do not change. 31 | // Not sure what a better way to manage all of these parameters is 32 | void render(const std::vector& entities, const std::vector& lights, Terrain* terrain, 33 | Entity* water, SkyboxRenderer& skybox, ShadowMap& shadowMap, Camera* cam, const glm::mat4& projection, 34 | int winX, int winY); 35 | }; 36 | -------------------------------------------------------------------------------- /renderers/SkyboxRenderer.cpp: -------------------------------------------------------------------------------- 1 | #include "SkyboxRenderer.h" 2 | #include "../Loader.h" 3 | 4 | SkyboxRenderer::SkyboxRenderer(const std::vector& images, float SIZE) { 5 | // clang-format off 6 | std::vector vertices = { 7 | -SIZE, -SIZE, SIZE, 8 | SIZE, -SIZE, SIZE, 9 | SIZE, SIZE, SIZE, 10 | -SIZE, SIZE, SIZE, 11 | -SIZE, -SIZE, -SIZE, 12 | SIZE, -SIZE, -SIZE, 13 | SIZE, SIZE, -SIZE, 14 | -SIZE, SIZE, -SIZE 15 | }; 16 | 17 | std::vector indices = { 18 | 0,1,2, 2,3,0, 19 | 1,5,6, 6,2,1, 20 | 5,4,7, 7,6,5, 21 | 4,0,3, 3,7,4, 22 | 3,2,6, 6,7,3, 23 | 4,5,1, 1,0,4 24 | }; 25 | // clang-format on 26 | vao = Loader::getLoader()->loadVAO(vertices, indices); 27 | indexCount = indices.size(); 28 | texture = Loader::getLoader()->loadCubemapTexture(images); 29 | } 30 | 31 | void SkyboxRenderer::render(const glm::mat4& view, const glm::mat4& projection) { 32 | glDisable(GL_CULL_FACE); 33 | shader.enable(); 34 | glActiveTexture(GL_TEXTURE0); 35 | glBindTexture(GL_TEXTURE_CUBE_MAP, texture); 36 | glBindVertexArray(vao); 37 | glEnableVertexAttribArray(0); 38 | 39 | shader.loadMatrices(view, projection); 40 | 41 | glDrawElements(GL_TRIANGLES, static_cast(indexCount), GL_UNSIGNED_INT, (void*)0); 42 | 43 | glDisableVertexAttribArray(0); 44 | glBindVertexArray(0); 45 | shader.disable(); 46 | glEnable(GL_CULL_FACE); 47 | } 48 | 49 | GLuint SkyboxRenderer::getSkyboxTexture() const { 50 | return texture; 51 | } -------------------------------------------------------------------------------- /renderers/SkyboxRenderer.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "../shaders/SkyboxShader.h" 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | class SkyboxRenderer { 10 | private: 11 | SkyboxShader shader; 12 | GLuint vao; 13 | GLuint texture; 14 | size_t indexCount; 15 | 16 | public: 17 | /* 18 | Images should be in order: 19 | PosXFilename, 20 | NegXFilename, 21 | PosYFilename, 22 | NegYFilename, 23 | PosZFilename, 24 | NegZFilename 25 | */ 26 | SkyboxRenderer(const std::vector& images, float SIZE); 27 | GLuint getSkyboxTexture() const; 28 | void render(const glm::mat4& view, const glm::mat4&); 29 | }; 30 | -------------------------------------------------------------------------------- /renderers/TerrainRenderer.cpp: -------------------------------------------------------------------------------- 1 | #include "TerrainRenderer.h" 2 | 3 | void TerrainRenderer::render(const Terrain* terrain, const std::vector& lights, const glm::mat4& view, 4 | const glm::mat4& proj, const glm::vec4& clipPlane) { 5 | shader.enable(); 6 | shader.loadProjection(proj); 7 | shader.loadLights(lights); 8 | shader.loadView(view); 9 | 10 | shader.loadTerrain(terrain); 11 | shader.loadClipPlane(clipPlane); 12 | 13 | glActiveTexture(GL_TEXTURE0); 14 | glBindTexture(GL_TEXTURE_2D, terrain->getTextureID(0)); 15 | glActiveTexture(GL_TEXTURE1); 16 | glBindTexture(GL_TEXTURE_2D, terrain->getTextureID(1)); 17 | glActiveTexture(GL_TEXTURE2); 18 | glBindTexture(GL_TEXTURE_2D, terrain->getTextureID(2)); 19 | glActiveTexture(GL_TEXTURE3); 20 | glBindTexture(GL_TEXTURE_2D, terrain->getTextureID(3)); 21 | glActiveTexture(GL_TEXTURE4); 22 | glBindTexture(GL_TEXTURE_2D, terrain->getTextureID(4)); 23 | 24 | glBindVertexArray(terrain->getVaoID()); 25 | 26 | glEnableVertexAttribArray(0); 27 | glEnableVertexAttribArray(1); 28 | glEnableVertexAttribArray(2); 29 | 30 | glDrawElements(GL_TRIANGLES, static_cast(terrain->getIndexCount()), GL_UNSIGNED_INT, (void*)0); 31 | 32 | glDisableVertexAttribArray(0); 33 | glDisableVertexAttribArray(1); 34 | glDisableVertexAttribArray(2); 35 | glBindVertexArray(0); 36 | 37 | shader.disable(); 38 | } 39 | 40 | void TerrainRenderer::render(const Terrain* terrain, const std::vector& lights, const glm::mat4& view, 41 | const glm::mat4& proj, const glm::mat4& depthView, const glm::mat4& depthProj, GLuint shadowMap, 42 | const glm::vec4& clipPlane) { 43 | shader.enable(); 44 | shader.loadProjection(proj); 45 | shader.loadLights(lights); 46 | shader.loadView(view); 47 | 48 | shader.loadTerrain(terrain); 49 | shader.loadClipPlane(clipPlane); 50 | // clang-format off 51 | glm::mat4 biasMatrix( 52 | 0.5, 0.0, 0.0, 0.0, 53 | 0.0, 0.5, 0.0, 0.0, 54 | 0.0, 0.0, 0.5, 0.0, 55 | 0.5, 0.5, 0.5, 1.0 56 | ); 57 | // clang-format on 58 | glm::mat4 depthBiasPV = biasMatrix * depthProj * depthView; 59 | shader.loadDepth(depthBiasPV); 60 | 61 | glActiveTexture(GL_TEXTURE0); 62 | glBindTexture(GL_TEXTURE_2D, terrain->getTextureID(0)); 63 | glActiveTexture(GL_TEXTURE1); 64 | glBindTexture(GL_TEXTURE_2D, terrain->getTextureID(1)); 65 | glActiveTexture(GL_TEXTURE2); 66 | glBindTexture(GL_TEXTURE_2D, terrain->getTextureID(2)); 67 | glActiveTexture(GL_TEXTURE3); 68 | glBindTexture(GL_TEXTURE_2D, terrain->getTextureID(3)); 69 | glActiveTexture(GL_TEXTURE4); 70 | glBindTexture(GL_TEXTURE_2D, terrain->getTextureID(4)); 71 | glActiveTexture(GL_TEXTURE5); 72 | glBindTexture(GL_TEXTURE_2D, shadowMap); 73 | 74 | glBindVertexArray(terrain->getVaoID()); 75 | 76 | glEnableVertexAttribArray(0); 77 | glEnableVertexAttribArray(1); 78 | glEnableVertexAttribArray(2); 79 | 80 | glDrawElements(GL_TRIANGLES, static_cast(terrain->getIndexCount()), GL_UNSIGNED_INT, (void*)0); 81 | 82 | glDisableVertexAttribArray(0); 83 | glDisableVertexAttribArray(1); 84 | glDisableVertexAttribArray(2); 85 | glBindVertexArray(0); 86 | 87 | shader.disable(); 88 | } 89 | -------------------------------------------------------------------------------- /renderers/TerrainRenderer.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "../shaders/TerrainShader.h" 4 | #include "../entities/Light.h" 5 | #include "../entities/Camera.h" 6 | #include "../Model.h" 7 | 8 | #include 9 | 10 | #include 11 | #include 12 | 13 | class TerrainRenderer { 14 | private: 15 | TerrainShader shader; 16 | 17 | public: 18 | TerrainRenderer() = default; 19 | 20 | void render(const Terrain* terrain, const std::vector& lights, const glm::mat4& view, const glm::mat4& proj, 21 | const glm::vec4& clipPlane); 22 | void render(const Terrain* terrain, const std::vector& lights, const glm::mat4& view, const glm::mat4& proj, 23 | const glm::mat4& depthView, const glm::mat4& depthProj, GLuint shadowMap, const glm::vec4& clipPlane); 24 | }; 25 | -------------------------------------------------------------------------------- /res/Barrel/Barrel.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:cdb63347e2aba4af68af0d5f53b0103a32d1b4dc54802a457225afd204a22986 3 | size 63118 4 | -------------------------------------------------------------------------------- /res/Barrel/Barrel02.mtl: -------------------------------------------------------------------------------- 1 | # Exported from Wings 3D 1.0.1 2 | newmtl Barrel 3 | Ns 100.0 4 | d 1.0 5 | illum 2 6 | Kd 1.0 1.0 1.0 7 | Ka 0.2 0.2 0.2 8 | Ks 0.2 0.2 0.2 9 | Ke 0.0 0.0 0.0 10 | map_Kd Barrel.png 11 | 12 | -------------------------------------------------------------------------------- /res/car/brakedis.jpg: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:452fa89020c0c1bc28a589d69a1d38d1a25c95925f42f4314f47a84be1bc754a 3 | size 4623 4 | -------------------------------------------------------------------------------- /res/car/car-n.mtl: -------------------------------------------------------------------------------- 1 | # Blender3D MTL File: groundplane.blend 2 | # Material Count: 10 3 | newmtl p206-tp.bmp_p206-tp.j_p206-tp.jpg 4 | Ns 90.196078 5 | Ka 0.000000 0.000000 0.000000 6 | Kd 0.409600 0.409600 0.409600 7 | Ks 1.000000 1.000000 1.000000 8 | Ni 1.000000 9 | d 1.000000 10 | illum 2 11 | map_Kd p206-tp.jpg 12 | 13 | 14 | newmtl rwheel.bmp_rwheel.jpg_rwheel.jpg 15 | Ns 90.196078 16 | Ka 0.000000 0.000000 0.000000 17 | Kd 0.409600 0.409600 0.409600 18 | Ks 1.000000 1.000000 1.000000 19 | Ni 1.000000 20 | d 1.000000 21 | illum 2 22 | map_Kd rwheel.jpg 23 | 24 | 25 | newmtl p206-sd2.bmp_p206-sd2_p206-sd2.jpg 26 | Ns 90.196078 27 | Ka 0.000000 0.000000 0.000000 28 | Kd 0.409600 0.409600 0.409600 29 | Ks 1.000000 1.000000 1.000000 30 | Ni 1.000000 31 | d 1.000000 32 | illum 2 33 | map_Kd p206-sd2.jpg 34 | 35 | 36 | newmtl brakedisc.bmp_brakedi_brakedis.jpg 37 | Ns 90.196078 38 | Ka 0.000000 0.000000 0.000000 39 | Kd 0.409600 0.409600 0.409600 40 | Ks 1.000000 1.000000 1.000000 41 | Ni 1.000000 42 | d 1.000000 43 | illum 2 44 | map_Kd brakedis.jpg 45 | 46 | 47 | newmtl p206-sd.bmp_p206-sd.j_p206-sd.jpg 48 | Ns 90.196078 49 | Ka 0.000000 0.000000 0.000000 50 | Kd 0.409600 0.409600 0.409600 51 | Ks 1.000000 1.000000 1.000000 52 | Ni 1.000000 53 | d 1.000000 54 | illum 2 55 | map_Kd p206-sd.jpg 56 | 57 | 58 | newmtl gen-trd.bmp_gen-trd.j_gen-trd.jpg 59 | Ns 90.196078 60 | Ka 0.000000 0.000000 0.000000 61 | Kd 0.409600 0.409600 0.409600 62 | Ks 1.000000 1.000000 1.000000 63 | Ni 1.000000 64 | d 1.000000 65 | illum 2 66 | map_Kd gen-trd.jpg 67 | 68 | 69 | newmtl p206-wh2.bmp_p206-wh2_p206-wh2.jpg 70 | Ns 90.196078 71 | Ka 0.000000 0.000000 0.000000 72 | Kd 0.409600 0.409600 0.409600 73 | Ks 1.000000 1.000000 1.000000 74 | Ni 1.000000 75 | d 1.000000 76 | illum 2 77 | map_Kd p206-wh2.jpg 78 | 79 | 80 | newmtl spokes.bmp_spokes.jpg_spokes.jpg 81 | Ns 90.196078 82 | Ka 0.000000 0.000000 0.000000 83 | Kd 0.409600 0.409600 0.409600 84 | Ks 1.000000 1.000000 1.000000 85 | Ni 1.000000 86 | d 1.000000 87 | illum 2 88 | map_Kd spokes.jpg 89 | 90 | 91 | newmtl p206-bk.bmp_p206-bk.j_p206-bk.jpg 92 | Ns 90.196078 93 | Ka 0.000000 0.000000 0.000000 94 | Kd 0.409600 0.409600 0.409600 95 | Ks 1.000000 1.000000 1.000000 96 | Ni 1.000000 97 | d 1.000000 98 | illum 2 99 | map_Kd p206-bk.jpg 100 | 101 | 102 | newmtl p206-fnt.bmp_p206-fnt_p206-fnt.jpg 103 | Ns 90.196078 104 | Ka 0.000000 0.000000 0.000000 105 | Kd 0.409600 0.409600 0.409600 106 | Ks 1.000000 1.000000 1.000000 107 | Ni 1.000000 108 | d 1.000000 109 | illum 2 110 | map_Kd p206-fnt.jpg 111 | 112 | 113 | -------------------------------------------------------------------------------- /res/car/gen-trd.jpg: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:a01077f85849cdfc217fd6794624a33a1d939bbc18587c30f5d99d48a6f6f3dd 3 | size 3514 4 | -------------------------------------------------------------------------------- /res/car/p206-bk.jpg: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:a3daaa2ce5cc97c0dacfd102b9ac7fee7c6219f68add9e9c4bcb5f06b85bf88f 3 | size 9347 4 | -------------------------------------------------------------------------------- /res/car/p206-fnt.jpg: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:c924965a31500b91a2298e9edcc1723e9d7f5a07953355f7970885b41fee2af2 3 | size 13578 4 | -------------------------------------------------------------------------------- /res/car/p206-sd.jpg: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:62f270996f176b75311654d4130ac10f4c415c90db3b669a6669604c14fb054e 3 | size 25997 4 | -------------------------------------------------------------------------------- /res/car/p206-sd2.jpg: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:17b95566229acf2f7985c0eaf494bc964cd551fb06dcf84af4f32bb7a9c64891 3 | size 26668 4 | -------------------------------------------------------------------------------- /res/car/p206-tp.jpg: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:72cba74cc59fec3aef97cf2d90b19a4a69be07d1f302c2aeb0c334cf0de31903 3 | size 11501 4 | -------------------------------------------------------------------------------- /res/car/p206-wh2.jpg: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:f0a4cf4a5ee3fdfe72f1eb6fc86c67184d6072998584b0f1fb23ff9a2cebe6e4 3 | size 10836 4 | -------------------------------------------------------------------------------- /res/car/rwheel.jpg: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:a6d4ba8a4a423028675b255dc3bcc483be987e226b14f3987c6afd6bdd9fb066 3 | size 7878 4 | -------------------------------------------------------------------------------- /res/car/spokes.jpg: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:f4666522fbbd4db78e2d459dd84e6517dfbe88bbf5e91ba54559afed7b52286f 3 | size 2300 4 | -------------------------------------------------------------------------------- /res/cone/cone2.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:38a43442e45faca90cd9b7709580779581b6762f9367d1909f85b2771d2c3d0e 3 | size 1312508 4 | -------------------------------------------------------------------------------- /res/cone/cone2_obj.mtl: -------------------------------------------------------------------------------- 1 | # 3ds Max Wavefront OBJ Exporter v0.97b - (c)2007 guruware 2 | # File Created: 03.09.2013 19:59:44 3 | 4 | newmtl cone2 5 | Ns 34.0000 6 | Ni 1.5000 7 | d 1.0000 8 | Tr 0.0000 9 | Tf 1.0000 1.0000 1.0000 10 | illum 2 11 | Ka 0.5880 0.5880 0.5880 12 | Kd 0.5880 0.5880 0.5880 13 | Ks 0.1980 0.1980 0.1980 14 | Ke 0.0000 0.0000 0.0000 15 | map_Ka cone2.png 16 | map_Kd cone2.png 17 | map_bump cone2.png 18 | bump cone2.png 19 | -------------------------------------------------------------------------------- /res/dust_single.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:0f954ddc40de11449c3421b1278e827a1bccfc70463bbe2628d6ea61c350143b 3 | size 8452 4 | -------------------------------------------------------------------------------- /res/fence/fence.mtl: -------------------------------------------------------------------------------- 1 | # Blender MTL File: 'None' 2 | # Material Count: 1 3 | 4 | newmtl Material2 5 | Ns 96.078431 6 | Ka 1.000000 1.000000 1.000000 7 | Kd 0.640000 0.640000 0.640000 8 | Ks 0.500000 0.500000 0.500000 9 | Ke 0.000000 0.000000 0.000000 10 | Ni -1.000000 11 | d 0.000000 12 | illum 2 13 | map_Kd model/fenceMaterial.png 14 | map_d model/fenceMaterial.png 15 | -------------------------------------------------------------------------------- /res/fence/fence.obj: -------------------------------------------------------------------------------- 1 | # Blender v2.77 (sub 0) OBJ File: '' 2 | # www.blender.org 3 | mtllib fence.mtl 4 | o instance_0_ID4 5 | v 0.000000 1.200170 0.000000 6 | v 9.436001 0.000000 0.000000 7 | v 0.000000 0.000000 0.000000 8 | v 9.436001 1.200170 0.000000 9 | v 9.436001 1.200170 0.000000 10 | v 0.000000 1.200170 0.000000 11 | v 9.436001 0.000000 0.000000 12 | v 0.000000 0.000000 0.000000 13 | v 0.000000 0.000000 0.000000 14 | v 0.000000 1.200170 0.000000 15 | v 9.436001 1.200170 0.000000 16 | v 9.436001 0.000000 0.000000 17 | vt 0.000000 0.984391 18 | vt -2.768808 0.000000 19 | vt 0.000000 0.000000 20 | vt -2.768808 0.984391 21 | vt -2.768808 0.984391 22 | vt 0.000000 0.984391 23 | vt -2.768808 0.000000 24 | vt 0.000000 0.000000 25 | vn 0.000000 0.000000 -1.000000 26 | vn 0.000000 0.000000 1.000000 27 | usemtl Material2 28 | s off 29 | f 1/1/1 2/2/1 3/3/1 30 | f 2/2/1 1/1/1 4/4/1 31 | f 5/5/2 6/6/2 7/7/2 32 | f 8/8/2 7/7/2 6/6/2 33 | -------------------------------------------------------------------------------- /res/fence/model/fenceMaterial.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:acbf5b0f6b128f7d9fbd7fff3e3f63939f8e713391615c4c3106bac1360b6bfb 3 | size 65001 4 | -------------------------------------------------------------------------------- /res/pine/NewBark.jpg: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:21e1c7b18f9b44fdf7bedc0c739f92b36f7270d027a3f50e3d2bd114de0f61d6 3 | size 97113 4 | -------------------------------------------------------------------------------- /res/pine/PineNeedles.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:c1d8609a2a860b87d5abb6b6ad9fd42ea368a9b8dee7641249e681d6025ea619 3 | size 48516 4 | -------------------------------------------------------------------------------- /res/pine/PineTransp.mtl: -------------------------------------------------------------------------------- 1 | # Exported from Wings 3D 1.0.1 2 | newmtl PineNeedles 3 | Ns 100.0 4 | d 1.0 5 | illum 2 6 | Kd 0.239216 0.329412 0.219608 7 | Ka 0.2 0.2 0.2 8 | Ks 1.0 1.0 1.0 9 | Ke 0.0 0.0 0.0 10 | map_Kd PineNeedles.png 11 | 12 | newmtl PineTrunk 13 | Ns 100.0 14 | d 1.0 15 | illum 2 16 | Kd 0.34902 0.25098 0.2 17 | Ka 0.2 0.2 0.2 18 | Ks 1.0 1.0 1.0 19 | Ke 0.0 0.0 0.0 20 | map_Kd NewBark.jpg 21 | 22 | -------------------------------------------------------------------------------- /res/readme.txt: -------------------------------------------------------------------------------- 1 | All assets in the res folder except 'dust_single.png' are not mine and property of the respective creators. 2 | -------------------------------------------------------------------------------- /res/sky/sky_back.tga: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:7b7e2d6738fa5cc3563748da0ae4106a5aada969434bd46f43fbcfd688567218 3 | size 316829 4 | -------------------------------------------------------------------------------- /res/sky/sky_bottom.tga: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:221050d42cc967c8a805a8c00a2bc65c16e7cb33becdf0eeebb57599e9c84e65 3 | size 10779 4 | -------------------------------------------------------------------------------- /res/sky/sky_front.tga: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:f57c792df05712e41be34ec2964d3adf8d005cb6d75f23c8c6212c4b4d841add 3 | size 239385 4 | -------------------------------------------------------------------------------- /res/sky/sky_left.tga: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:7a676cd405b861865caa729226835446736607a95cf18c540a0735d1f53975d9 3 | size 324226 4 | -------------------------------------------------------------------------------- /res/sky/sky_right.tga: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:c27c1336b2da4745b5b72b6a106214597eb3f8ca6103ee01e722415056a6b9a4 3 | size 279775 4 | -------------------------------------------------------------------------------- /res/sky/sky_top.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:fa430c195db237acdce67c8bbc83a0e893f9cb3dd4224af055986b93df5897de 3 | size 142593 4 | -------------------------------------------------------------------------------- /res/sky/sky_top.tga: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:2130da79b6d0aa31845d9ea7da53746dd9c4c8c8018a69a29e25f5ff4328bf64 3 | size 384298 4 | -------------------------------------------------------------------------------- /res/stump/StumpBark02.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:8d9aea69bdcd3bd393c528285a28f624a2e23d864249e2315064661107833675 3 | size 63088 4 | -------------------------------------------------------------------------------- /res/stump/StumpEnd02.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:d18f636849a066da38ed904be8d34e299e429d929b3a2bb02e277d3f3b8cc2a2 3 | size 38584 4 | -------------------------------------------------------------------------------- /res/stump/TreeStump03.mtl: -------------------------------------------------------------------------------- 1 | # Exported from Wings 3D 1.0.1 2 | newmtl Bark 3 | Ns 100.0 4 | d 1.0 5 | illum 2 6 | Kd 0.435294 0.254902 0.133333 7 | Ka 0.2 0.2 0.2 8 | Ks 1.0 1.0 1.0 9 | Ke 0.0 0.0 0.0 10 | map_Kd StumpBark02.png 11 | 12 | newmtl CutStump 13 | Ns 100.0 14 | d 1.0 15 | illum 2 16 | Kd 1.0 0.870588 0.568627 17 | Ka 0.2 0.2 0.2 18 | Ks 1.0 1.0 1.0 19 | Ke 0.0 0.0 0.0 20 | map_Kd StumpEnd02.png 21 | 22 | -------------------------------------------------------------------------------- /res/stump/TreeStump03.obj: -------------------------------------------------------------------------------- 1 | # Exported from Wings 3D 1.0.1 2 | mtllib TreeStump03.mtl 3 | o Mesh 4 | #37 vertices, 60 faces 5 | v 0.82225800 -1.0132800e-8 -1.7509200e-2 6 | v 0.60906200 -1.0132800e-8 -0.65090300 7 | v -0.53644200 -1.0132800e-8 -0.89891900 8 | v -0.39261800 -1.0132800e-8 0.79965100 9 | v 0.55342200 -1.0132800e-8 0.73193400 10 | v 0.64672600 0.36029000 -3.4694500e-17 11 | v 0.35474900 0.31779000 0.52213000 12 | v -0.25316900 0.31779000 0.58508700 13 | v -0.41538500 0.36029000 -0.66910500 14 | v 0.46945700 0.36029000 -0.55824400 15 | v 0.61777200 0.68449100 -1.3521200e-2 16 | v 0.40626800 0.69208200 -0.55311200 17 | v 0.29760800 0.69044200 0.50402600 18 | v -0.28230400 0.67347200 0.51661600 19 | v -0.36422300 0.68350800 -0.62535800 20 | v -1.18630000 -1.0132800e-8 2.6898700e-2 21 | v -0.74885900 0.34329000 -2.9713200e-2 22 | v -0.59193100 0.68246300 -9.8736000e-3 23 | v 6.2654300e-2 -1.0132800e-8 0.82832200 24 | v 2.5580900e-2 0.31779000 0.63621900 25 | v 2.5174900e-2 0.68247000 0.58994100 26 | v -0.75612800 -1.0132800e-8 0.41764500 27 | v -0.52964700 0.33214200 0.30408400 28 | v -0.47146100 0.67838400 0.27083500 29 | v -0.77161800 -1.0132800e-8 -0.42010900 30 | v -0.55680300 0.35209800 -0.32244800 31 | v -0.48364600 0.68296000 -0.28158600 32 | v -5.8711000e-2 -1.0132800e-8 -0.95889700 33 | v 8.4976700e-5 0.36029000 -0.73203900 34 | v -9.3569700e-4 0.68799600 -0.70754300 35 | v 0.92896800 -1.0132800e-8 -0.49126400 36 | v 0.69249100 0.28029000 -0.35675800 37 | v 0.57150600 0.68830500 -0.31462900 38 | v 0.75575700 -1.0132800e-8 0.40088200 39 | v 0.55688900 0.33848000 0.30794800 40 | v 0.51565400 0.68750400 0.28854300 41 | v 0.0000000e+0 0.87000000 0.0000000e+0 42 | vt -1.11016000 0.0000000e+0 43 | vt -1.10888000 0.54389500 44 | vt -1.09668000 0.98694500 45 | vt -0.95053700 0.0000000e+0 46 | vt -0.92962700 0.52789400 47 | vt -0.91537000 0.99574800 48 | vt -0.75402700 0.0000000e+0 49 | vt -0.69884700 0.50729500 50 | vt -0.69702800 1.00635000 51 | vt -0.52799600 0.0000000e+0 52 | vt -0.49297300 0.52407700 53 | vt -0.46028100 1.00415000 54 | vt -0.25051500 1.00220000 55 | vt -0.24986300 0.54389500 56 | vt -0.22263000 0.0000000e+0 57 | vt -0.16103600 0.54389500 58 | vt -0.15896500 0.99658600 59 | vt -0.14909800 0.0000000e+0 60 | vt -6.7397100e-2 0.54389500 61 | vt -6.5052100e-2 0.99082300 62 | vt -6.4856400e-2 0.0000000e+0 63 | vt 2.8950900e-2 0.99811700 64 | vt 4.8575800e-2 0.54389500 65 | vt 6.1437300e-2 0.0000000e+0 66 | vt 8.0000000e-2 0.44600800 67 | vt 0.12204400 1.00534000 68 | vt 0.12951400 0.68896700 69 | vt 0.15076700 0.54389500 70 | vt 0.16612000 0.26273900 71 | vt 0.17708600 0.0000000e+0 72 | vt 0.24823500 0.84000000 73 | vt 0.31595300 0.13000000 74 | vt 0.34420200 0.98008300 75 | vt 0.36728800 0.54389500 76 | vt 0.37638300 0.0000000e+0 77 | vt 0.47561200 0.89288600 78 | vt 0.48931900 0.45238900 79 | vt 0.50013000 9.5845600e-2 80 | vt 0.57783600 0.0000000e+0 81 | vt 0.58819300 0.95234400 82 | vt 0.59696800 0.54389500 83 | vt 0.66533700 0.13102500 84 | vt 0.69316000 0.82673600 85 | vt 0.78409600 0.0000000e+0 86 | vt 0.78930800 0.54389500 87 | vt 0.79748900 0.97049100 88 | vt 0.82598600 0.25474500 89 | vt 0.84615500 0.68363200 90 | vt 0.90200000 0.46422900 91 | vt 0.98726500 0.98694500 92 | vt 0.99483600 0.0000000e+0 93 | vt 0.99612100 0.54389500 94 | vn 0.88903753 0.42484281 0.17064834 95 | vn 0.40181936 0.37100576 -0.83719527 96 | vn -0.48011414 0.58069738 -0.65748077 97 | vn -0.32527238 0.62302646 0.71136201 98 | vn 0.41796027 0.66787128 0.61583859 99 | vn 0.92931868 0.34192895 0.13946825 100 | vn 0.49546259 0.37215141 0.78486951 101 | vn -0.43798866 0.37807061 0.81561544 102 | vn -0.57073381 0.40697170 -0.71318788 103 | vn 0.51745523 0.32991899 -0.78955275 104 | vn 0.76582413 0.63940480 6.8373251e-2 105 | vn 0.45691757 0.67222273 -0.58253149 106 | vn 0.38810953 0.64777088 0.65556379 107 | vn -0.42230329 0.64337320 0.63853807 108 | vn -0.47760445 0.71248364 -0.51406327 109 | vn -0.61719027 0.78681108 2.1206884e-3 110 | vn -0.83981648 0.54279744 8.9000275e-3 111 | vn -0.65158026 0.75839075 1.6930333e-2 112 | vn 5.7039037e-2 0.55927211 0.82701950 113 | vn 7.0861208e-2 0.33185639 0.94066467 114 | vn 3.4917924e-2 0.47605312 0.87872303 115 | vn -0.57028514 0.62832902 0.52912900 116 | vn -0.70417626 0.40170674 0.58546349 117 | vn -0.70656566 0.51018396 0.49038483 118 | vn -0.71076244 0.56655137 -0.41693680 119 | vn -0.81291549 0.40692971 -0.41662527 120 | vn -0.76276729 0.55634501 -0.32964570 121 | vn 0.12036065 0.50066177 -0.85723457 122 | vn 9.9869846e-2 0.30640005 -0.94664937 123 | vn 7.0571186e-2 0.47293487 -0.87826666 124 | vn 0.74121984 0.60063085 -0.29972610 125 | vn 0.81058898 0.51373045 -0.28111657 126 | vn 0.78812008 0.54005967 -0.29530036 127 | vn 0.75088985 0.55126263 0.36369485 128 | vn 0.80249779 0.34417711 0.48738014 129 | vn 0.75466408 0.47407313 0.45358217 130 | vn -1.3726488e-2 0.99938350 3.2314247e-2 131 | g Mesh_Bark 132 | usemtl Bark 133 | f 1/21/1 31/24/31 6/19/6 134 | f 2/30/2 28/35/28 10/28/10 135 | f 3/39/3 25/44/25 9/41/9 136 | f 4/7/4 19/10/19 8/8/8 137 | f 5/15/5 34/18/34 7/14/7 138 | f 6/19/6 31/24/31 32/23/32 139 | f 6/19/6 33/22/33 11/20/11 140 | f 6/19/6 34/18/34 1/21/1 141 | f 6/19/6 36/17/36 35/16/35 142 | f 7/14/7 19/10/19 5/15/5 143 | f 7/14/7 21/12/21 20/11/20 144 | f 7/14/7 34/18/34 35/16/35 145 | f 7/14/7 36/17/36 13/13/13 146 | f 8/8/8 19/10/19 20/11/20 147 | f 8/8/8 21/12/21 14/9/14 148 | f 8/8/8 22/4/22 4/7/4 149 | f 8/8/8 24/6/24 23/5/23 150 | f 9/41/9 25/44/25 26/45/26 151 | f 9/41/9 27/46/27 15/40/15 152 | f 9/41/9 28/35/28 3/39/3 153 | f 9/41/9 30/33/30 29/34/29 154 | f 10/28/10 28/35/28 29/34/29 155 | f 10/28/10 30/33/30 12/26/12 156 | f 10/28/10 31/24/31 2/30/2 157 | f 10/28/10 33/22/33 32/23/32 158 | f 11/20/11 36/17/36 6/19/6 159 | f 12/26/12 33/22/33 10/28/10 160 | f 13/13/13 21/12/21 7/14/7 161 | f 14/9/14 24/6/24 8/8/8 162 | f 15/40/15 30/33/30 9/41/9 163 | f 16/1/16 22/4/22 17/2/17 164 | f 17/2/17 22/4/22 23/5/23 165 | f 17/2/17 24/6/24 18/3/18 166 | f 17/52/17 25/44/25 16/51/16 167 | f 17/52/17 27/46/27 26/45/26 168 | f 18/50/18 27/46/27 17/52/17 169 | f 20/11/20 19/10/19 7/14/7 170 | f 20/11/20 21/12/21 8/8/8 171 | f 23/5/23 22/4/22 8/8/8 172 | f 23/5/23 24/6/24 17/2/17 173 | f 26/45/26 25/44/25 17/52/17 174 | f 26/45/26 27/46/27 9/41/9 175 | f 29/34/29 28/35/28 9/41/9 176 | f 29/34/29 30/33/30 10/28/10 177 | f 32/23/32 31/24/31 10/28/10 178 | f 32/23/32 33/22/33 6/19/6 179 | f 35/16/35 34/18/34 6/19/6 180 | f 35/16/35 36/17/36 7/14/7 181 | g Mesh_CutStump 182 | usemtl CutStump 183 | f 11/49/11 37/37/37 36/47/36 184 | f 12/43/12 37/37/37 33/48/33 185 | f 13/42/13 37/37/37 21/38/21 186 | f 14/32/14 37/37/37 24/29/24 187 | f 15/31/15 37/37/37 30/36/30 188 | f 18/25/18 37/37/37 27/27/27 189 | f 21/38/21 37/37/37 14/32/14 190 | f 24/29/24 37/37/37 18/25/18 191 | f 27/27/27 37/37/37 15/31/15 192 | f 30/36/30 37/37/37 12/43/12 193 | f 33/48/33 37/37/37 11/49/11 194 | f 36/47/36 37/37/37 13/42/13 195 | -------------------------------------------------------------------------------- /res/terrain/blendMap.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:820d094dda879d3ac23f29b9a493bc058cac684d7d6322b2f60adaf8533db6b3 3 | size 361987 4 | -------------------------------------------------------------------------------- /res/terrain/dirt.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:e81e09a99e71035b7584763acc5fb6de5276d4f1455a15dc1cfe30a245557d8a 3 | size 61091 4 | -------------------------------------------------------------------------------- /res/terrain/grass.jpg: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:acb5034915c960698f994f819dd90400f389483d7611af2cd32892d0e0f683f4 3 | size 309402 4 | -------------------------------------------------------------------------------- /res/terrain/heightmap.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:63ec6588e0c0bc0d358a7f7e6ad9bf21aab7ad84ea0e557ca5db4f48030b808f 3 | size 187774 4 | -------------------------------------------------------------------------------- /res/terrain/mud.jpg: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:5a80d3f12b428ef3dc2abc8e6dfe2df5d0fdcc541cd3202ddfcaffc3a8590350 3 | size 2068170 4 | -------------------------------------------------------------------------------- /res/terrain/road.jpg: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:baf900d5b4861fc5bda5a5ff5ad3c0c515ae542cfe8e89940e633341d953dcbb 3 | size 1235198 4 | -------------------------------------------------------------------------------- /res/tree/PineTree03.mtl: -------------------------------------------------------------------------------- 1 | # Exported from Wings 3D 1.0.1 2 | newmtl Bark 3 | Ns 100.0 4 | d 1.0 5 | illum 2 6 | Kd 0.435294 0.254902 0.133333 7 | Ka 1.0 1.0 1.0 8 | Ks 0 0 0 9 | Ke 0.0 0.0 0.0 10 | map_Kd TreeBark.png 11 | 12 | newmtl Leaves 13 | Ns 100.0 14 | d 1.0 15 | illum 2 16 | Kd 0.129412 0.439216 0.145098 17 | Ka 1.0 1.0 1.0 18 | Ks 0 0 0 19 | Ke 0.0 0.0 0.0 20 | map_Kd leaves01.png 21 | 22 | -------------------------------------------------------------------------------- /res/tree/PineTree03.obj: -------------------------------------------------------------------------------- 1 | # Exported from Wings 3D 1.0.1 2 | mtllib PineTree03.mtl 3 | o Mesh 4 | #127 vertices, 159 faces 5 | v 0.39360000 -1.1920900e-8 0.0000000e+0 6 | v 0.29280000 -1.1920900e-8 -0.32486800 7 | v -0.22880000 -1.1920900e-8 -0.42086800 8 | v -0.18080000 -1.1920900e-8 0.38886800 9 | v 0.27680000 -1.1920900e-8 0.32486800 10 | v 0.31957500 0.26000000 0.0000000e+0 11 | v 0.22218700 0.26000000 0.27676000 12 | v -9.7387400e-2 0.26000000 0.31836000 13 | v -0.15978700 0.26000000 -0.27676000 14 | v 0.18058700 0.26000000 -0.21436000 15 | v 0.19830200 1.33852000 1.7380600e-2 16 | v 7.3535900e-2 1.33454000 0.17725700 17 | v -0.15376000 1.34107000 0.15332200 18 | v -8.2496100e-2 1.33199000 -0.12557600 19 | v 9.7274000e-2 1.34359000 -0.16369300 20 | v 9.2622200e-2 2.65000000 -2.7986400e-2 21 | v 4.6311100e-2 2.65000000 5.2226800e-2 22 | v -4.6311100e-2 2.65000000 8.0213200e-2 23 | v -4.6311100e-2 2.65000000 -8.0213200e-2 24 | v 1.8324700e-2 2.65000000 -0.10820000 25 | v 0.23764000 0.57154300 -5.5927500e-3 26 | v 0.15628100 0.58284700 -0.18453400 27 | v 0.11448200 0.58040500 0.20847900 28 | v -0.10859500 0.55513300 0.21368700 29 | v -0.14010700 0.57007900 -0.21441700 30 | v 0.13648000 1.95407000 -1.1483200e-2 31 | v 3.6614400e-2 1.95658000 -0.14065900 32 | v 9.5924200e-2 1.94154000 0.13155400 33 | v -6.6666000e-2 1.95265000 9.9795400e-2 34 | v -6.3686000e-2 1.95642000 -0.12385600 35 | v -0.47360000 -1.1920900e-8 3.2000000e-2 36 | v -0.36117500 0.26000000 2.0800000e-2 37 | v -0.22770000 0.56852300 1.2461100e-2 38 | v -0.21938500 1.32292000 1.6410700e-2 39 | v -0.12375400 1.95215000 2.5780300e-2 40 | v -9.2622200e-2 2.65000000 0.0000000e+0 41 | v -0.61555900 1.27200000 0.58085500 42 | v -0.27814100 2.06473000 0.26008800 43 | v 0.47125900 1.27200000 0.78430700 44 | v 0.20899400 2.09059000 0.34570000 45 | v 0.80185200 1.27200000 -0.27081200 46 | v 0.35695800 2.06828000 -0.11950900 47 | v -0.17015300 1.24186000 -0.84070300 48 | v -7.3078000e-2 2.11465000 -0.37601800 49 | v -0.29334000 2.11087000 -0.24278200 50 | v -0.63758800 1.33228000 -0.55983900 51 | v -0.87490700 1.24186000 5.1410700e-2 52 | v -0.36739400 2.08636000 8.8337200e-3 53 | v -4.5433500e-2 2.06851000 0.38610700 54 | v -0.12009800 1.30214000 0.84792100 55 | v 0.18453100 2.08381000 -0.32544600 56 | v 0.42304800 1.33228000 -0.70762200 57 | v 0.38397900 2.06399000 0.13144200 58 | v 0.75720500 1.30214000 0.26864000 59 | v 0.21831600 0.77199800 0.99806800 60 | v 0.10485000 1.48473000 0.48995800 61 | v 1.10454000 0.77199800 0.0000000e+0 62 | v 0.53153000 1.51059000 -1.4397600e-3 63 | v 0.21831600 0.77199800 -0.99806800 64 | v 0.10711500 1.48828000 -0.48358400 65 | v -0.97568600 0.74185800 -0.34662500 66 | v -0.47361600 1.53465000 -0.17239900 67 | v -0.47261400 1.53087000 0.16631500 68 | v -0.97568600 0.83227800 0.31166300 69 | v -0.49075700 0.74185800 0.93725300 70 | v -0.23901300 1.50636000 0.42035300 71 | v 0.40468200 1.48851000 0.31290000 72 | v 0.80269800 0.80213800 0.65144300 73 | v -0.24200200 1.50381000 -0.42867200 74 | v -0.46917500 0.83227800 -0.87768600 75 | v 0.40846300 1.48399000 -0.34399600 76 | v 0.74874300 0.80213800 -0.61648000 77 | v -0.24823400 2.88080000 0.20826800 78 | v 0.19004300 2.88080000 0.28860700 79 | v 0.32336000 2.88080000 -0.12803700 80 | v -0.25711800 2.91998000 -0.24216700 81 | v -0.35282000 2.86121000 -7.9790300e-4 82 | v -4.8431300e-2 2.90039000 0.31372600 83 | v 0.17060100 2.91998000 -0.30052300 84 | v 0.30535500 2.90039000 8.4981000e-2 85 | v -0.14740700 3.12000000 0.11893600 86 | v -0.20460200 3.12000000 -6.9259200e-3 87 | v 0.11435700 3.12000000 0.16906900 88 | v 0.19077100 3.12000000 4.8616600e-2 89 | v 0.19175800 3.12000000 -8.0221400e-2 90 | v 0.10637100 3.12000000 -0.19345300 91 | v -4.0800200e-2 3.12000000 -0.21640000 92 | v -0.16491000 3.12000000 -0.15728900 93 | v -6.8616900e-2 2.86121000 -0.35307400 94 | v -2.8265300e-2 3.12000000 0.18697800 95 | v -1.5999100e-2 3.66408000 4.5872100e-3 96 | v -0.33884000 2.37200000 0.29973600 97 | v -0.11515000 3.08473000 9.3876500e-2 98 | v 0.25940900 2.37200000 0.41172800 99 | v 8.6523600e-2 3.11059000 0.12932000 100 | v 0.44138600 2.37200000 -0.16907100 101 | v 0.14778100 3.08828000 -6.3276700e-2 102 | v -9.3662100e-2 2.34186000 -0.48277200 103 | v -3.0254300e-2 3.13465000 -0.16947200 104 | v -0.12144300 3.13087000 -0.11431200 105 | v -0.35096600 2.43228000 -0.32816800 106 | v -0.48160000 2.34186000 8.2994900e-3 107 | v -0.15210100 3.10636000 -1.0142800e-2 108 | v -1.8809500e-2 3.08851000 0.14604800 109 | v -6.6108800e-2 2.40214000 0.44674500 110 | v 7.6395700e-2 3.10381000 -0.14853500 111 | v 0.23287100 2.43228000 -0.40951700 112 | v 0.15896700 3.08399000 4.0617000e-2 113 | v 0.41681000 2.40214000 0.12787500 114 | v 0.13099000 1.87200000 0.57884100 115 | v 4.5295300e-2 2.72473000 0.19726200 116 | v 0.66272200 1.87200000 -2.0000000e-2 117 | v 0.22962100 2.75059000 -1.5022000e-2 118 | v 0.13099000 1.87200000 -0.61884100 119 | v 4.6273800e-2 2.72828000 -0.22330800 120 | v -0.58541100 1.84186000 -0.22797500 121 | v -0.20460200 2.77465000 -8.8876600e-2 122 | v -0.20416900 2.77087000 5.7448200e-2 123 | v -0.58541100 1.93228000 0.16699800 124 | v -0.29445400 1.84186000 0.54235200 125 | v -0.10325400 2.74636000 0.16719200 126 | v 0.17482300 2.72851000 0.12077300 127 | v 0.48161900 1.90214000 0.37086600 128 | v -0.10454500 2.74381000 -0.19958600 129 | v -0.28150500 1.93228000 -0.54661200 130 | v 0.17645600 2.72399000 -0.16300600 131 | v 0.44924600 1.90214000 -0.38988800 132 | vt -3.0219300e-3 1.00000000 133 | vt -2.4528200e-3 0.0000000e+0 134 | vt -1.8433100e-3 9.1228100e-2 135 | vt -1.5512500e-3 0.53435800 136 | vt 0.0000000e+0 2.8750500e-2 137 | vt 3.6447200e-3 0.75514000 138 | vt 3.9503300e-3 0.26965700 139 | vt 8.4417200e-3 2.5394700e-2 140 | vt 8.9620700e-3 0.51976100 141 | vt 1.1131000e-2 0.50189300 142 | vt 1.3869100e-2 0.98703100 143 | vt 1.4899600e-2 1.9497200e-2 144 | vt 1.7208800e-2 0.97158100 145 | vt 2.0074400e-2 0.46321100 146 | vt 2.1811900e-2 0.48104300 147 | vt 2.1847300e-2 9.8026600e-3 148 | vt 2.3095100e-2 0.48968100 149 | vt 2.4382200e-2 0.98469000 150 | vt 2.6192500e-2 0.98780600 151 | vt 0.16673300 0.0000000e+0 152 | vt 0.19294700 8.1228100e-2 153 | vt 0.19381100 0.27495900 154 | vt 0.19584600 0.52072700 155 | vt 0.23397700 0.75531400 156 | vt 0.23957500 1.00000000 157 | vt 0.39354000 1.00000000 158 | vt 0.39847400 0.76141700 159 | vt 0.40453800 0.54843700 160 | vt 0.40593100 0.27382600 161 | vt 0.40624100 9.1228100e-2 162 | vt 0.41917800 0.0000000e+0 163 | vt 0.45398200 0.97516000 164 | vt 0.45434500 1.00000000 165 | vt 0.47152800 0.97374500 166 | vt 0.47375300 0.97553000 167 | vt 0.47785000 0.53983200 168 | vt 0.47963300 1.4086100e-2 169 | vt 0.48158600 0.49327700 170 | vt 0.48284600 0.50547500 171 | vt 0.48592800 0.75581300 172 | vt 0.48801600 0.49606300 173 | vt 0.49065800 0.44217900 174 | vt 0.49166300 0.97002400 175 | vt 0.49173400 8.5548500e-3 176 | vt 0.49245500 8.8089300e-3 177 | vt 0.49292300 9.1228100e-2 178 | vt 0.49403700 0.27071700 179 | vt 0.49413000 0.0000000e+0 180 | vt 0.50466300 0.98594200 181 | vt 0.52181300 2.0310700e-2 182 | vt 0.52688300 0.50084700 183 | vt 0.56448100 0.0000000e+0 184 | vt 0.57023000 1.00000000 185 | vt 0.58291900 0.27468300 186 | vt 0.59656400 9.1228100e-2 187 | vt 0.60081700 0.54161000 188 | vt 0.60906700 0.0000000e+0 189 | vt 0.61229300 0.75669600 190 | vt 0.78888200 0.53753900 191 | vt 0.79559200 1.00000000 192 | vt 0.79944700 0.0000000e+0 193 | vt 0.80436700 0.26020300 194 | vt 0.80853600 9.1228100e-2 195 | vt 0.81440500 0.74664000 196 | vt 0.97570800 0.97424800 197 | vt 0.97691400 3.7914200e-2 198 | vt 0.97830800 0.97341600 199 | vt 0.98047500 0.53115900 200 | vt 0.98336600 0.97671200 201 | vt 0.98375200 0.98503500 202 | vt 0.98806900 0.45047500 203 | vt 0.98845700 6.9065200e-3 204 | vt 0.98988200 0.43322700 205 | vt 0.99010600 0.50617500 206 | vt 0.99169100 0.97473700 207 | vt 0.99364500 0.75514000 208 | vt 0.99395000 0.26965700 209 | vt 0.99697800 1.00000000 210 | vt 0.99754700 0.0000000e+0 211 | vt 0.99815700 9.1228100e-2 212 | vt 0.99844900 0.53435800 213 | vt 0.99852500 7.4559500e-3 214 | vt 1.00000000 2.0217700e-2 215 | vt 1.00170000 4.7255100e-2 216 | vn 0.94316581 0.33008171 -3.8526908e-2 217 | vn 0.56488765 0.51711025 -0.64303883 218 | vn -0.36095522 0.49569716 -0.78993395 219 | vn -0.32610427 0.32645006 0.88717888 220 | vn 0.60544734 0.26597107 0.75012859 221 | vn 0.95824713 0.26134940 -0.11601260 222 | vn 0.56950786 0.30435341 0.76356388 223 | vn -0.32981872 0.32202583 0.88742266 224 | vn -0.35776334 0.37260089 -0.85625579 225 | vn 0.56132558 0.30869640 -0.76786726 226 | vn 0.99408223 7.6007824e-2 7.7610061e-2 227 | vn 0.45201443 7.1083048e-2 0.88917386 228 | vn -0.57588999 9.1056240e-2 0.81244044 229 | vn -0.48692761 7.7391122e-2 -0.87000696 230 | vn 0.44565269 7.5479138e-2 -0.89201826 231 | vn 0.99190713 5.9855125e-2 -0.11197146 232 | vn 0.57637617 8.3729448e-2 0.81288369 233 | vn -0.36755142 3.6823259e-2 0.92927391 234 | vn -0.66407473 6.3511832e-2 -0.74496376 235 | vn 0.28832487 5.6576361e-2 -0.95585976 236 | vn 0.98613801 0.16575520 7.5524439e-3 237 | vn 0.58099293 9.2773430e-2 -0.80860393 238 | vn 0.49159614 0.19925366 0.84772119 239 | vn -0.46964810 0.17401506 0.86553418 240 | vn -0.47077867 0.16616730 -0.86646170 241 | vn 0.99167152 7.3763625e-2 -0.10557708 242 | vn 0.36381656 6.1988795e-2 -0.92940567 243 | vn 0.52296030 8.7984642e-2 0.84780377 244 | vn -0.50232670 9.5231615e-2 0.85941773 245 | vn -0.60107190 5.3470014e-2 -0.79740425 246 | vn -0.91296438 0.39853993 8.7532696e-2 247 | vn -0.91790559 0.38915253 7.7521859e-2 248 | vn -0.97394674 0.20925717 8.7402387e-2 249 | vn -0.99612357 8.5404306e-2 -2.1069607e-2 250 | vn -0.98556818 9.8189308e-2 0.13789207 251 | vn -0.99630840 5.1370846e-2 6.8779383e-2 252 | vn -0.70443043 0.50540998 0.49833576 253 | vn -0.57780530 0.50861913 0.63831624 254 | vn 0.47141534 0.52908706 0.70557385 255 | vn 0.45259422 0.52008616 0.72434029 256 | vn 0.82251752 0.50810620 -0.25552498 257 | vn 0.82154127 0.49080174 -0.29014409 258 | vn -0.13426421 0.47729467 -0.86842554 259 | vn -0.12853469 0.50418796 -0.85397502 260 | vn -0.69738364 0.51240092 -0.50110015 261 | vn -0.67578450 0.51178857 -0.53045996 262 | vn -0.85448961 0.51637180 5.6636314e-2 263 | vn -0.86191656 0.50348014 6.0063244e-2 264 | vn -0.16937796 0.52072787 0.83675181 265 | vn -0.16769921 0.51806542 0.83874024 266 | vn 0.47329806 0.51401052 -0.71538950 267 | vn 0.45432161 0.50704224 -0.73246163 268 | vn 0.85593387 0.46136576 0.23349270 269 | vn 0.84477257 0.47688719 0.24277129 270 | vn 5.8949360e-2 0.58934661 0.80572672 271 | vn 0.23214160 0.59268840 0.77125271 272 | vn 0.78954831 0.61246719 -3.8696356e-2 273 | vn 0.79846857 0.60183123 -1.5719738e-2 274 | vn 0.19206843 0.59201318 -0.78270691 275 | vn 0.16510420 0.57303520 -0.80272739 276 | vn -0.76918018 0.55624656 -0.31456575 277 | vn -0.74894530 0.58617856 -0.30899132 278 | vn -0.73688391 0.59673806 0.31765673 279 | vn -0.75096959 0.59530760 0.28575084 280 | vn -0.36517345 0.60096142 0.71098081 281 | vn -0.36853295 0.58736020 0.72054942 282 | vn 0.58803050 0.60549919 0.53627498 283 | vn 0.59064365 0.60232686 0.53697526 284 | vn -0.34648998 0.59958404 -0.72141782 285 | vn -0.37033826 0.59094142 -0.71668529 286 | vn 0.60963573 0.53718293 -0.58290546 287 | vn 0.60802306 0.55655155 -0.56617871 288 | vn -0.58226947 0.49010449 0.64866004 289 | vn 0.46692081 0.51115718 0.72159774 290 | vn 0.82370456 0.50411220 -0.25957983 291 | vn -0.63691097 0.53084699 -0.55905804 292 | vn -0.86388717 0.49828335 7.3570785e-2 293 | vn -0.16491465 0.50434188 0.84760983 294 | vn 0.41507344 0.52936567 -0.73992299 295 | vn 0.83860864 0.48015389 0.25726987 296 | vn -0.70927545 0.41732177 0.56812927 297 | vn -0.90732855 0.41312854 7.7972554e-2 298 | vn 0.48112307 0.43559540 0.76077411 299 | vn 0.85786191 0.44520234 0.25664725 300 | vn 0.85101197 0.44400804 -0.28042019 301 | vn 0.44419462 0.47453279 -0.75994064 302 | vn -0.13030134 0.42667949 -0.89496714 303 | vn -0.67386713 0.47188424 -0.56853176 304 | vn -0.12725567 0.48714687 -0.86399880 305 | vn -0.17491529 0.43917218 0.88121078 306 | vn 2.3144409e-2 0.99897611 3.8872365e-2 307 | vn -0.74849837 0.38975270 0.53651004 308 | vn -0.61228480 0.39357938 0.68571320 309 | vn 0.50582787 0.40884165 0.75959639 310 | vn 0.48160395 0.41143821 0.77380633 311 | vn 0.87710697 0.39901445 -0.26734028 312 | vn 0.87201111 0.38794229 -0.29849189 313 | vn -0.14063635 0.37358416 -0.91687311 314 | vn -0.12495649 0.40410668 -0.90613667 315 | vn -0.75339859 0.40589985 -0.51733536 316 | vn -0.72457679 0.40617778 -0.55678370 317 | vn -0.91552058 0.39626009 6.9282174e-2 318 | vn -0.91724142 0.38913027 8.5122314e-2 319 | vn -0.20160824 0.40466136 0.89196597 320 | vn -0.18962414 0.40188438 0.89584130 321 | vn 0.53497368 0.41143841 -0.73791706 322 | vn 0.49787648 0.40622808 -0.76622305 323 | vn 0.90013230 0.36984238 0.23017050 324 | vn 0.89253663 0.37619560 0.24870712 325 | vn 7.1889095e-2 0.41438490 0.90725802 326 | vn 0.26688548 0.41833277 0.86819919 327 | vn 0.89607125 0.44195423 -4.1626659e-2 328 | vn 0.89810713 0.43955330 -1.4017285e-2 329 | vn 0.21600967 0.42697568 -0.87808405 330 | vn 0.18558031 0.41386875 -0.89121973 331 | vn -0.84929491 0.39837786 -0.34640038 332 | vn -0.83525114 0.42391995 -0.35021052 333 | vn -0.82812989 0.42433258 0.36625504 334 | vn -0.84421190 0.42490454 0.32674515 335 | vn -0.41245995 0.42203983 0.80731603 336 | vn -0.40553399 0.41224517 0.81584073 337 | vn 0.65872621 0.43116123 0.61658719 338 | vn 0.66454631 0.42896502 0.61185555 339 | vn -0.37127055 0.43234488 -0.82172750 340 | vn -0.40441494 0.42744907 -0.80853933 341 | vn 0.65833353 0.39534593 -0.64054552 342 | vn 0.66696812 0.40355138 -0.62633841 343 | g Mesh_Bark 344 | usemtl Bark 345 | f 1/48/1 7/30/7 5/31/5 346 | f 1/48/1 10/55/10 6/46/6 347 | f 2/57/2 10/55/10 1/48/1 348 | f 3/61/3 10/55/10 2/57/2 349 | f 3/61/3 32/80/32 9/63/9 350 | f 4/20/4 7/30/7 8/21/8 351 | f 4/20/4 32/3/32 31/2/31 352 | f 5/31/5 7/30/7 4/20/4 353 | f 6/46/6 7/30/7 1/48/1 354 | f 6/46/6 21/47/21 7/30/7 355 | f 7/30/7 21/47/21 23/29/23 356 | f 7/30/7 24/22/24 8/21/8 357 | f 8/21/8 32/3/32 4/20/4 358 | f 9/63/9 10/55/10 3/61/3 359 | f 9/63/9 25/62/25 10/55/10 360 | f 9/63/9 32/80/32 25/62/25 361 | f 10/55/10 21/47/21 6/46/6 362 | f 10/55/10 25/62/25 22/54/22 363 | f 11/36/11 21/47/21 15/56/15 364 | f 11/36/11 26/40/26 12/28/12 365 | f 12/28/12 21/47/21 11/36/11 366 | f 12/28/12 24/22/24 23/29/23 367 | f 12/28/12 26/40/26 28/27/28 368 | f 12/28/12 29/24/29 13/23/13 369 | f 13/23/13 24/22/24 12/28/12 370 | f 13/23/13 34/4/34 24/22/24 371 | f 14/59/14 30/64/30 15/56/15 372 | f 14/59/14 34/81/34 30/64/30 373 | f 15/56/15 21/47/21 22/54/22 374 | f 15/56/15 25/62/25 14/59/14 375 | f 15/56/15 26/40/26 11/36/11 376 | f 15/56/15 30/64/30 27/58/27 377 | f 16/33/16 26/40/26 20/53/20 378 | f 17/26/17 26/40/26 16/33/16 379 | f 17/26/17 29/24/29 28/27/28 380 | f 18/25/18 29/24/29 17/26/17 381 | f 18/25/18 36/1/36 29/24/29 382 | f 20/53/20 26/40/26 27/58/27 383 | f 20/53/20 30/64/30 19/60/19 384 | f 22/54/22 21/47/21 10/55/10 385 | f 22/54/22 25/62/25 15/56/15 386 | f 23/29/23 21/47/21 12/28/12 387 | f 23/29/23 24/22/24 7/30/7 388 | f 24/22/24 32/3/32 8/21/8 389 | f 24/22/24 34/4/34 33/7/33 390 | f 25/62/25 32/80/32 33/77/33 391 | f 25/62/25 34/81/34 14/59/14 392 | f 27/58/27 26/40/26 15/56/15 393 | f 27/58/27 30/64/30 20/53/20 394 | f 28/27/28 26/40/26 17/26/17 395 | f 28/27/28 29/24/29 12/28/12 396 | f 29/24/29 34/4/34 13/23/13 397 | f 29/24/29 36/1/36 35/6/35 398 | f 30/64/30 34/81/34 35/76/35 399 | f 30/64/30 36/78/36 19/60/19 400 | f 31/79/31 32/80/32 3/61/3 401 | f 33/7/33 32/3/32 24/22/24 402 | f 33/77/33 34/81/34 25/62/25 403 | f 35/6/35 34/4/34 29/24/29 404 | f 35/76/35 36/78/36 30/64/30 405 | g Mesh_Leaves 406 | usemtl Leaves 407 | f 37/52/37 48/13/48 47/5/47 408 | f 37/52/37 50/83/50 38/43/38 409 | f 38/43/38 48/13/48 37/52/37 410 | f 38/43/38 50/83/50 49/69/49 411 | f 39/50/39 54/84/54 40/32/40 412 | f 40/32/40 50/16/50 39/50/39 413 | f 40/32/40 54/84/54 53/67/53 414 | f 41/45/41 52/82/52 42/35/42 415 | f 42/35/42 52/82/52 51/65/51 416 | f 42/35/42 54/8/54 41/45/41 417 | f 43/44/43 46/72/46 44/34/44 418 | f 44/34/44 46/72/46 45/75/45 419 | f 44/34/44 52/12/52 43/44/43 420 | f 46/37/46 48/70/48 45/49/45 421 | f 47/66/47 48/70/48 46/37/46 422 | f 49/18/49 50/16/50 40/32/40 423 | f 51/19/51 52/12/52 44/34/44 424 | f 53/11/53 54/8/54 42/35/42 425 | f 55/52/55 66/13/66 65/5/65 426 | f 55/52/55 68/83/68 56/43/56 427 | f 56/43/56 66/13/66 55/52/55 428 | f 56/43/56 68/83/68 67/69/67 429 | f 57/50/57 72/84/72 58/32/58 430 | f 58/32/58 68/16/68 57/50/57 431 | f 58/32/58 72/84/72 71/67/71 432 | f 59/45/59 70/82/70 60/35/60 433 | f 60/35/60 70/82/70 69/65/69 434 | f 60/35/60 72/8/72 59/45/59 435 | f 61/44/61 64/72/64 62/34/62 436 | f 62/34/62 64/72/64 63/75/63 437 | f 62/34/62 70/12/70 61/44/61 438 | f 64/37/64 66/70/66 63/49/63 439 | f 65/66/65 66/70/66 64/37/64 440 | f 67/18/67 68/16/68 58/32/58 441 | f 69/19/69 70/12/70 62/34/62 442 | f 71/11/71 72/8/72 60/35/60 443 | f 73/52/73 81/51/81 77/5/77 444 | f 73/52/73 90/15/90 81/51/81 445 | f 74/50/74 84/74/84 83/41/83 446 | f 74/50/74 90/17/90 78/16/78 447 | f 75/45/75 84/10/84 80/8/80 448 | f 75/45/75 86/71/86 85/39/85 449 | f 77/5/77 81/51/81 82/9/82 450 | f 77/66/77 88/42/88 76/37/76 451 | f 78/16/78 90/15/90 73/52/73 452 | f 79/82/79 86/71/86 75/45/75 453 | f 79/12/79 89/44/89 86/14/86 454 | f 80/84/80 84/74/84 74/50/74 455 | f 81/51/81 91/13/91 82/9/82 456 | f 82/68/82 88/42/88 77/66/77 457 | f 82/68/82 91/49/91 88/42/88 458 | f 83/41/83 90/17/90 74/50/74 459 | f 83/41/83 91/18/91 90/17/90 460 | f 84/74/84 91/32/91 83/41/83 461 | f 85/39/85 84/10/84 75/45/75 462 | f 85/39/85 91/11/91 84/10/84 463 | f 86/14/86 89/44/89 87/38/87 464 | f 86/71/86 91/35/91 85/39/85 465 | f 87/38/87 89/44/89 88/73/88 466 | f 87/38/87 91/19/91 86/14/86 467 | f 88/73/88 89/44/89 76/72/76 468 | f 88/73/88 91/34/91 87/38/87 469 | f 90/15/90 91/43/91 81/51/81 470 | f 92/52/92 103/13/103 102/5/102 471 | f 92/52/92 105/83/105 93/43/93 472 | f 93/43/93 103/13/103 92/52/92 473 | f 93/43/93 105/83/105 104/69/104 474 | f 94/50/94 109/84/109 95/32/95 475 | f 95/32/95 105/16/105 94/50/94 476 | f 95/32/95 109/84/109 108/67/108 477 | f 96/45/96 107/82/107 97/35/97 478 | f 97/35/97 107/82/107 106/65/106 479 | f 97/35/97 109/8/109 96/45/96 480 | f 98/44/98 101/72/101 99/34/99 481 | f 99/34/99 101/72/101 100/75/100 482 | f 99/34/99 107/12/107 98/44/98 483 | f 101/37/101 103/70/103 100/49/100 484 | f 102/66/102 103/70/103 101/37/101 485 | f 104/18/104 105/16/105 95/32/95 486 | f 106/19/106 107/12/107 99/34/99 487 | f 108/11/108 109/8/109 97/35/97 488 | f 110/52/110 121/13/121 120/5/120 489 | f 110/52/110 123/83/123 111/43/111 490 | f 111/43/111 121/13/121 110/52/110 491 | f 111/43/111 123/83/123 122/69/122 492 | f 112/50/112 127/84/127 113/32/113 493 | f 113/32/113 123/16/123 112/50/112 494 | f 113/32/113 127/84/127 126/67/126 495 | f 114/45/114 125/82/125 115/35/115 496 | f 115/35/115 125/82/125 124/65/124 497 | f 115/35/115 127/8/127 114/45/114 498 | f 116/44/116 119/72/119 117/34/117 499 | f 117/34/117 119/72/119 118/75/118 500 | f 117/34/117 125/12/125 116/44/116 501 | f 119/37/119 121/70/121 118/49/118 502 | f 120/66/120 121/70/121 119/37/119 503 | f 122/18/122 123/16/123 113/32/113 504 | f 124/19/124 125/12/125 117/34/117 505 | f 126/11/126 127/8/127 115/35/115 506 | -------------------------------------------------------------------------------- /res/tree/TreeBark.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:5ae6c4e4fb3db4a79efb79c0b1c6eafac3c2fed99036a94a98e9656cd4e5cdb8 3 | size 113154 4 | -------------------------------------------------------------------------------- /res/tree/leaves01.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:dbb6bdcbcf5e1dbde104afe93174a6a78a2fa669f05abed0f516a61fe8d34b98 3 | size 45811 4 | -------------------------------------------------------------------------------- /res/water/dudv.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:9a999fbd3a4a55d1cdffbb88553b5128f9966c396ff767e628bc85eedd6737c9 3 | size 239263 4 | -------------------------------------------------------------------------------- /shaders/EntityShader.cpp: -------------------------------------------------------------------------------- 1 | #include "EntityShader.h" 2 | 3 | EntityShader::EntityShader() : ShaderProgram(ENTITY_VERTEX_SHADER, ENTITY_FRAGMENT_SHADER) { 4 | bindUniformLocations(); 5 | } 6 | 7 | void EntityShader::bindUniformLocations() { 8 | // If named attributes are used the shader MUST be linked again after they are setup! 9 | // For now go back to using location based attributes 10 | // glBindAttribLocation(shaderID, 0, "a_vertex"); 11 | // glBindAttribLocation(shaderID, 1, "a_normal"); 12 | // glBindAttribLocation(shaderID, 2, "a_tex_coord"); 13 | // glLinkProgram(this->shaderID); 14 | 15 | location_texMap = glGetUniformLocation(shaderID, "texMap"); 16 | location_cubeMap = glGetUniformLocation(shaderID, "cubeMap"); 17 | location_shadowMap = glGetUniformLocation(shaderID, "shadowMap"); 18 | location_clip_plane = glGetUniformLocation(shaderID, "clip_plane"); 19 | 20 | location_projection = glGetUniformLocation(shaderID, "projection"); 21 | location_model = glGetUniformLocation(shaderID, "model"); 22 | location_view = glGetUniformLocation(shaderID, "view"); 23 | location_inv_view = glGetUniformLocation(shaderID, "inv_view"); 24 | 25 | location_shininess = glGetUniformLocation(shaderID, "shininess"); 26 | location_emission = glGetUniformLocation(shaderID, "emission"); 27 | location_num_lights = glGetUniformLocation(shaderID, "num_lights"); 28 | location_mtl_ambient = glGetUniformLocation(shaderID, "mtl_ambient"); 29 | location_mtl_diffuse = glGetUniformLocation(shaderID, "mtl_diffuse"); 30 | location_mtl_specular = glGetUniformLocation(shaderID, "mtl_specular"); 31 | 32 | location_depth_pv = glGetUniformLocation(shaderID, "depth_pv"); 33 | location_render_shadows = glGetUniformLocation(shaderID, "render_shadows"); 34 | } 35 | 36 | void EntityShader::loadLights(const std::vector& lights) { 37 | loadUniformValue(location_num_lights, static_cast(lights.size())); 38 | for (size_t i = 0; i < lights.size(); i++) { 39 | loadLight(lights[i], i); 40 | } 41 | } 42 | 43 | void EntityShader::loadLight(Light* light, size_t i) { 44 | loadLightUniform("position", i, light->position); 45 | loadLightUniform("specular", i, light->specular); 46 | loadLightUniform("diffuse", i, light->diffuse); 47 | loadLightUniform("ambient", i, light->ambient); 48 | loadLightUniform("radius", i, light->radius); 49 | loadLightUniform("coneAngle", i, light->coneAngle); 50 | loadLightUniform("coneDirection", i, light->coneDirection); 51 | } 52 | 53 | void EntityShader::loadView(const glm::mat4& view) { 54 | loadUniformValue(location_view, view); 55 | loadUniformValue(location_inv_view, glm::inverse(view)); 56 | } 57 | 58 | void EntityShader::loadEntity(Entity* entity) { 59 | loadUniformValue(location_texMap, 0); 60 | loadUniformValue(location_cubeMap, 1); 61 | glm::mat4 model = entity->calculateModelMatrix(); 62 | 63 | loadUniformValue(location_model, model); 64 | } 65 | 66 | void EntityShader::loadModelComponent(const ModelComponent& component) { 67 | loadUniformValue(location_mtl_ambient, component.getMaterial().ambient, 3); 68 | loadUniformValue(location_mtl_diffuse, component.getMaterial().diffuse, 3); 69 | loadUniformValue(location_mtl_specular, component.getMaterial().specular, 3); 70 | loadUniformValue(location_emission, component.getMaterial().emission, 3); 71 | loadUniformValue(location_shininess, component.getMaterial().shininess); 72 | } 73 | 74 | void EntityShader::loadProjection(const glm::mat4& proj) { 75 | loadUniformValue(location_projection, proj); 76 | loadUniformValue(location_render_shadows, 0); 77 | } 78 | void EntityShader::loadClipPlane(const glm::vec4& clip) { 79 | loadUniformValue(location_clip_plane, clip); 80 | } 81 | 82 | void EntityShader::loadDepth(const glm::mat4& pv) { 83 | loadUniformValue(location_shadowMap, 2); 84 | loadUniformValue(location_depth_pv, pv); 85 | loadUniformValue(location_render_shadows, 1); 86 | } -------------------------------------------------------------------------------- /shaders/EntityShader.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define _USE_MATH_DEFINES 4 | 5 | #include "../entities/Entity.h" 6 | #include "../entities/Light.h" 7 | #include "../Model.h" 8 | #include "ShaderProgram.h" 9 | 10 | #include 11 | #include 12 | #include 13 | 14 | #include 15 | #include 16 | 17 | const std::string ENTITY_VERTEX_SHADER = "shaders/entity.vert"; 18 | const std::string ENTITY_FRAGMENT_SHADER = "shaders/entity.frag"; 19 | 20 | class EntityShader : public ShaderProgram { 21 | private: 22 | GLuint location_texMap; 23 | GLuint location_cubeMap; 24 | GLuint location_shadowMap; 25 | GLuint location_clip_plane; 26 | 27 | GLuint location_projection; 28 | GLuint location_model; 29 | GLuint location_view; 30 | GLuint location_inv_view; 31 | 32 | GLuint location_num_lights; 33 | GLuint location_shininess; 34 | GLuint location_emission; 35 | 36 | GLuint location_mtl_ambient; 37 | GLuint location_mtl_diffuse; 38 | GLuint location_mtl_specular; 39 | 40 | GLuint location_depth_pv; 41 | GLuint location_render_shadows; 42 | 43 | public: 44 | EntityShader(); 45 | 46 | void bindUniformLocations(); 47 | 48 | void loadLights(const std::vector& lights); 49 | void loadLight(Light* light, size_t i); 50 | void loadView(const glm::mat4& view); 51 | void loadEntity(Entity* entity); 52 | void loadModelComponent(const ModelComponent& component); 53 | void loadProjection(const glm::mat4& proj); 54 | void loadDepth(const glm::mat4& pv); 55 | void loadClipPlane(const glm::vec4& clip); 56 | }; 57 | -------------------------------------------------------------------------------- /shaders/ShaderProgram.cpp: -------------------------------------------------------------------------------- 1 | #include "ShaderProgram.h" 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include "glm_ext.h" 8 | 9 | ShaderProgram::ShaderProgram(const std::string& vertexShader, const std::string& fragmentShader) { 10 | this->shaderID = loadShaders(vertexShader, fragmentShader); 11 | } 12 | 13 | ShaderProgram::ShaderProgram(int shaderID) { 14 | this->shaderID = shaderID; 15 | } 16 | 17 | void ShaderProgram::enable() { 18 | glUseProgram(shaderID); 19 | } 20 | 21 | void ShaderProgram::disable() { 22 | glUseProgram(0); 23 | } 24 | 25 | GLuint ShaderProgram::getShaderID() { 26 | return shaderID; 27 | } 28 | 29 | void ShaderProgram::loadUniformValue(GLuint uniformLocation, int value) { 30 | glUniform1i(uniformLocation, value); 31 | } 32 | 33 | void ShaderProgram::loadUniformValue(GLuint uniformLocation, float value) { 34 | glUniform1f(uniformLocation, value); 35 | } 36 | 37 | void ShaderProgram::loadUniformValue(GLuint uniformLocation, glm::vec2 value) { 38 | glUniform2fv(uniformLocation, 1, &value.x); 39 | } 40 | 41 | void ShaderProgram::loadUniformValue(GLuint uniformLocation, glm::vec3 value) { 42 | glUniform3fv(uniformLocation, 1, &value.x); 43 | } 44 | 45 | void ShaderProgram::loadUniformValue(GLuint uniformLocation, glm::vec4 value) { 46 | glUniform4fv(uniformLocation, 1, &value.x); 47 | } 48 | 49 | void ShaderProgram::loadUniformValue(GLuint uniformLocation, glm::mat2 value) { 50 | glUniformMatrix2fv(uniformLocation, 1, false, glm::value_ptr(value)); 51 | } 52 | 53 | void ShaderProgram::loadUniformValue(GLuint uniformLocation, glm::mat3 value) { 54 | glUniformMatrix3fv(uniformLocation, 1, false, glm::value_ptr(value)); 55 | } 56 | 57 | void ShaderProgram::loadUniformValue(GLuint uniformLocation, glm::mat4 value) { 58 | glUniformMatrix4fv(uniformLocation, 1, false, glm::value_ptr(value)); 59 | } 60 | 61 | void ShaderProgram::loadUniformValue(GLuint uniformLocation, float* value, int count) { 62 | switch (count) { 63 | case 1: 64 | glUniform1f(uniformLocation, *value); 65 | break; 66 | case 2: 67 | glUniform2fv(uniformLocation, 1, value); 68 | break; 69 | case 3: 70 | glUniform3fv(uniformLocation, 1, value); 71 | break; 72 | case 4: 73 | glUniform4fv(uniformLocation, 1, value); 74 | break; 75 | default: 76 | std::cerr << "Cant load uniform with " << count << " dimensions." << std::endl; 77 | } 78 | } 79 | 80 | int ShaderProgram::compileShader(const std::string& shader_path, GLuint shader_id) { 81 | // Read shader code from file 82 | std::string shader_code; 83 | std::ifstream shader_file_stream(shader_path, std::ios::in); 84 | if (shader_file_stream.is_open()) { 85 | std::string line; 86 | while (getline(shader_file_stream, line)) { 87 | shader_code += "\n" + line; 88 | } 89 | shader_file_stream.close(); 90 | } else { 91 | std::cerr << "Cannot open " << shader_path << ". Are you in the right directory?" << std::endl; 92 | return 0; 93 | } 94 | 95 | // Compile Shader 96 | char const* SourcePointer = shader_code.c_str(); 97 | glShaderSource(shader_id, 1, &SourcePointer, nullptr); 98 | glCompileShader(shader_id); 99 | 100 | // Check Shader 101 | GLint result = GL_FALSE; 102 | int info_log_length; 103 | 104 | glGetShaderiv(shader_id, GL_COMPILE_STATUS, &result); 105 | glGetShaderiv(shader_id, GL_INFO_LOG_LENGTH, &info_log_length); 106 | printf("compiled shader %d %d\n", result, info_log_length); 107 | if (info_log_length > 1) { 108 | std::vector shader_error_message(info_log_length + 1); 109 | glGetShaderInfoLog(shader_id, info_log_length, nullptr, shader_error_message.data()); 110 | 111 | std::cerr << shader_error_message.data() << std::endl; 112 | return 0; 113 | } 114 | return 1; 115 | } 116 | 117 | GLuint ShaderProgram::loadShaders(const std::string& vertex_file_path, const std::string& fragment_file_path) { 118 | // Create the shaders 119 | GLuint vertex_shader_id = glCreateShader(GL_VERTEX_SHADER); 120 | GLuint fragment_shader_id = glCreateShader(GL_FRAGMENT_SHADER); 121 | 122 | // Compile both shaders. Exit if compile errors. 123 | if (!compileShader(vertex_file_path, vertex_shader_id) || !compileShader(fragment_file_path, fragment_shader_id)) { 124 | return 0; 125 | } 126 | 127 | // Link the program 128 | GLuint program_id = glCreateProgram(); 129 | glAttachShader(program_id, vertex_shader_id); 130 | glAttachShader(program_id, vertex_shader_id); 131 | glAttachShader(program_id, fragment_shader_id); 132 | glLinkProgram(program_id); 133 | 134 | // Check the program 135 | GLint result = GL_FALSE; 136 | int info_log_length; 137 | 138 | glGetProgramiv(program_id, GL_LINK_STATUS, &result); 139 | glGetProgramiv(program_id, GL_INFO_LOG_LENGTH, &info_log_length); 140 | if (info_log_length > 0) { 141 | std::vector program_error_message(info_log_length + 1); 142 | glGetProgramInfoLog(program_id, info_log_length, nullptr, &program_error_message[0]); 143 | std::cerr << &program_error_message[0] << std::endl; 144 | } 145 | 146 | glDeleteShader(vertex_shader_id); 147 | glDeleteShader(fragment_shader_id); 148 | 149 | return program_id; 150 | } 151 | -------------------------------------------------------------------------------- /shaders/ShaderProgram.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include 7 | #include 8 | 9 | // Abstract shader program class, holds all uniforms, 10 | class ShaderProgram { 11 | protected: 12 | GLuint shaderID; 13 | 14 | private: 15 | int compileShader(const std::string& ShaderPath, GLuint ShaderID); 16 | GLuint loadShaders(const std::string& vertex_file_path, const std::string& fragment_file_path); 17 | 18 | public: 19 | ShaderProgram(const std::string&, const std::string&); 20 | ShaderProgram(int); 21 | virtual void enable(); 22 | virtual void disable(); 23 | 24 | template 25 | void loadLightUniform(const std::string& property, size_t index, const T& value); 26 | 27 | // Uniform loading helpers 28 | void loadUniformValue(GLuint uniformLocation, int value); 29 | void loadUniformValue(GLuint uniformLocation, float value); 30 | void loadUniformValue(GLuint uniformLocation, glm::vec2 value); 31 | void loadUniformValue(GLuint uniformLocation, glm::vec3 value); 32 | void loadUniformValue(GLuint uniformLocation, glm::vec4 value); 33 | void loadUniformValue(GLuint uniformLocation, glm::mat2 value); 34 | void loadUniformValue(GLuint uniformLocation, glm::mat3 value); 35 | void loadUniformValue(GLuint uniformLocation, glm::mat4 value); 36 | void loadUniformValue(GLuint uniformLocation, float* value, int count); 37 | 38 | GLuint getShaderID(); 39 | }; 40 | 41 | // Should generalise this to take 'lights' as a parameter 42 | template 43 | void ShaderProgram::loadLightUniform(const std::string& property, size_t index, const T& value) { 44 | // Lights are passed as an array of structs. However these are essentially bound and send individually. 45 | // They have special uniform name syntax though. ie uniform_name[i].property -> lights[0].position 46 | std::ostringstream ss; 47 | ss << "lights[" << index << "]." << property; 48 | std::string uniformName = ss.str(); 49 | 50 | GLuint uniform_location = glGetUniformLocation(shaderID, uniformName.c_str()); 51 | loadUniformValue(uniform_location, value); 52 | } 53 | -------------------------------------------------------------------------------- /shaders/SkyboxShader.cpp: -------------------------------------------------------------------------------- 1 | #include "SkyboxShader.h" 2 | 3 | SkyboxShader::SkyboxShader() : ShaderProgram(SKYBOX_VERTEX_SHADER, SKYBOX_FRAGMENT_SHADER) { 4 | bindUniformLocations(); 5 | } 6 | 7 | void SkyboxShader::bindUniformLocations() { 8 | location_projection = glGetUniformLocation(shaderID, "projection"); 9 | location_view = glGetUniformLocation(shaderID, "view"); 10 | } 11 | 12 | void SkyboxShader::loadMatrices(const glm::mat4& camera, const glm::mat4& projection) { 13 | auto camera_copy = camera; 14 | // Remove translation of camera matrix, just keep rotation 15 | camera_copy[3][0] = 0.0f; 16 | camera_copy[3][1] = 0.0f; 17 | camera_copy[3][2] = 0.0f; 18 | 19 | loadUniformValue(location_view, camera_copy); 20 | loadUniformValue(location_projection, projection); 21 | } -------------------------------------------------------------------------------- /shaders/SkyboxShader.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "ShaderProgram.h" 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | const std::string SKYBOX_VERTEX_SHADER = "shaders/skybox.vert"; 10 | const std::string SKYBOX_FRAGMENT_SHADER = "shaders/skybox.frag"; 11 | 12 | class SkyboxShader : public ShaderProgram { 13 | private: 14 | GLuint location_projection; 15 | GLuint location_view; 16 | 17 | public: 18 | SkyboxShader(); 19 | 20 | void bindUniformLocations(); 21 | void loadMatrices(const glm::mat4& camera, const glm::mat4& projection); 22 | }; 23 | -------------------------------------------------------------------------------- /shaders/TerrainShader.cpp: -------------------------------------------------------------------------------- 1 | #include "TerrainShader.h" 2 | 3 | TerrainShader::TerrainShader() : ShaderProgram(TERRAIN_VERTEX_SHADER, TERRAIN_FRAGMENT_SHADER) { 4 | bindUniformLocations(); 5 | } 6 | 7 | void TerrainShader::bindUniformLocations() { 8 | location_blendMap = glGetUniformLocation(shaderID, "blendMap"); 9 | location_backMap = glGetUniformLocation(shaderID, "backMap"); 10 | location_rMap = glGetUniformLocation(shaderID, "rMap"); 11 | location_gMap = glGetUniformLocation(shaderID, "gMap"); 12 | location_bMap = glGetUniformLocation(shaderID, "bMap"); 13 | 14 | location_clip_plane = glGetUniformLocation(shaderID, "clip_plane"); 15 | 16 | location_projection = glGetUniformLocation(shaderID, "projection"); 17 | location_model = glGetUniformLocation(shaderID, "model"); 18 | location_view = glGetUniformLocation(shaderID, "view"); 19 | location_depth_pv = glGetUniformLocation(shaderID, "depth_pv"); 20 | location_shadowMap = glGetUniformLocation(shaderID, "shadowMap"); 21 | 22 | location_shininess = glGetUniformLocation(shaderID, "shininess"); 23 | location_num_lights = glGetUniformLocation(shaderID, "num_lights"); 24 | } 25 | 26 | void TerrainShader::loadLights(const std::vector& lights) { 27 | loadUniformValue(location_num_lights, int(lights.size())); 28 | for (size_t i = 0; i < lights.size(); i++) { 29 | loadLight(lights[i], i); 30 | } 31 | } 32 | 33 | void TerrainShader::loadLight(Light* light, size_t i) { 34 | loadLightUniform("position", i, light->position); 35 | loadLightUniform("specular", i, light->specular); 36 | loadLightUniform("diffuse", i, light->diffuse); 37 | loadLightUniform("ambient", i, light->ambient); 38 | loadLightUniform("radius", i, light->radius); 39 | loadLightUniform("coneAngle", i, light->coneAngle); 40 | loadLightUniform("coneDirection", i, light->coneDirection); 41 | } 42 | 43 | void TerrainShader::loadView(const glm::mat4& view) { 44 | loadUniformValue(location_view, view); 45 | } 46 | 47 | void TerrainShader::loadDepth(const glm::mat4& pv) { 48 | loadUniformValue(location_depth_pv, pv); 49 | loadUniformValue(location_shadowMap, 5); 50 | } 51 | 52 | void TerrainShader::loadTerrain(const Terrain* terrain) { 53 | loadUniformValue(location_blendMap, 0); 54 | loadUniformValue(location_backMap, 1); 55 | loadUniformValue(location_rMap, 2); 56 | loadUniformValue(location_gMap, 3); 57 | loadUniformValue(location_bMap, 4); 58 | 59 | glm::mat4 model = terrain->calculateModelMatrix(); 60 | loadUniformValue(location_model, model); 61 | } 62 | 63 | void TerrainShader::loadClipPlane(const glm::vec4& clip) { 64 | loadUniformValue(location_clip_plane, clip); 65 | } 66 | 67 | void TerrainShader::loadProjection(const glm::mat4& proj) { 68 | loadUniformValue(location_projection, proj); 69 | } -------------------------------------------------------------------------------- /shaders/TerrainShader.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "../entities/Terrain.h" 4 | #include "../entities/Light.h" 5 | #include "ShaderProgram.h" 6 | 7 | #include 8 | #include 9 | 10 | #include 11 | #include 12 | 13 | const std::string TERRAIN_VERTEX_SHADER = "shaders/terrain.vert"; 14 | const std::string TERRAIN_FRAGMENT_SHADER = "shaders/terrain.frag"; 15 | 16 | class TerrainShader : public ShaderProgram { 17 | private: 18 | GLuint location_blendMap; 19 | GLuint location_backMap; 20 | GLuint location_rMap; 21 | GLuint location_gMap; 22 | GLuint location_bMap; 23 | GLuint location_shadowMap; 24 | 25 | GLuint location_clip_plane; 26 | 27 | GLuint location_projection; 28 | GLuint location_model; 29 | GLuint location_view; 30 | 31 | GLuint location_depth_pv; 32 | 33 | GLuint location_num_lights; 34 | GLuint location_shininess; 35 | 36 | public: 37 | TerrainShader(); 38 | 39 | void bindUniformLocations(); 40 | 41 | void loadTerrain(const Terrain* terrain); 42 | 43 | void loadLights(const std::vector& lights); 44 | void loadLight(Light* light, size_t i); 45 | void loadView(const glm::mat4& view); 46 | void loadDepth(const glm::mat4& pv); 47 | void loadClipPlane(const glm::vec4& clip); 48 | void loadProjection(const glm::mat4& proj); 49 | }; 50 | -------------------------------------------------------------------------------- /shaders/entity.frag: -------------------------------------------------------------------------------- 1 | 2 | // Calculates Phong colour at each fragment. 3 | // Ambient, diffuse and specular terms. 4 | // Uses interpolated position and normal values passed from vertex shader. 5 | 6 | #version 330 7 | 8 | in vec4 vertex; 9 | in vec3 normal; 10 | in vec2 st; 11 | in vec4 shadowCoord; 12 | 13 | layout(location = 0) out vec4 fragColour; 14 | 15 | uniform sampler2D texMap; 16 | uniform samplerCube cubeMap; 17 | uniform sampler2DShadow shadowMap; 18 | 19 | uniform mat4 projection; 20 | uniform mat4 model; 21 | uniform mat4 view; 22 | uniform mat4 inv_view; 23 | 24 | // Light parameters 25 | #define MAX_LIGHTS 10 26 | uniform int num_lights; 27 | uniform struct Light { 28 | vec4 position; 29 | vec3 diffuse; 30 | vec3 specular; 31 | vec3 ambient; 32 | float radius; 33 | float coneAngle; 34 | vec3 coneDirection; 35 | } lights[MAX_LIGHTS]; 36 | 37 | uniform vec3 mtl_ambient; // Ambient surface colour 38 | uniform vec3 mtl_diffuse; // Diffuse surface colour 39 | uniform vec3 mtl_specular; // Specular surface colour 40 | uniform vec3 emission; // Specular surface colour 41 | 42 | uniform float shininess; 43 | uniform int render_shadows; 44 | 45 | uniform bool use_mtl = false; 46 | uniform float fog_density = 0.02; 47 | 48 | vec2 poissonDisk[16] = vec2[]( 49 | vec2( -0.94201624, -0.39906216 ), 50 | vec2( 0.94558609, -0.76890725 ), 51 | vec2( -0.094184101, -0.92938870 ), 52 | vec2( 0.34495938, 0.29387760 ), 53 | vec2( -0.91588581, 0.45771432 ), 54 | vec2( -0.81544232, -0.87912464 ), 55 | vec2( -0.38277543, 0.27676845 ), 56 | vec2( 0.97484398, 0.75648379 ), 57 | vec2( 0.44323325, -0.97511554 ), 58 | vec2( 0.53742981, -0.47373420 ), 59 | vec2( -0.26496911, -0.41893023 ), 60 | vec2( 0.79197514, 0.19090188 ), 61 | vec2( -0.24188840, 0.99706507 ), 62 | vec2( -0.81409955, 0.91437590 ), 63 | vec2( 0.19984126, 0.78641367 ), 64 | vec2( 0.14383161, -0.14100790 ) 65 | ); 66 | 67 | 68 | // Returns a random number based on a vec3 and an int. 69 | float random(vec3 seed, int i){ 70 | vec4 seed4 = vec4(seed,i); 71 | float dot_product = dot(seed4, vec4(12.9898,78.233,45.164,94.673)); 72 | return fract(sin(dot_product) * 43758.5453); 73 | } 74 | 75 | 76 | // Multiple lights code from 77 | // http://www.tomdalling.com/blog/modern-opengl/08-even-more-lighting-directional-lights-spotlights-multiple-lights 78 | vec3 ApplyLight(Light light, vec3 surfaceColor, vec3 normal, vec4 vertex_world, vec4 vertex_view) { 79 | vec3 light_surface_dir; 80 | float attenuation = 1.0; 81 | 82 | if(light.position.w == 0.0) { 83 | //directional light 84 | light_surface_dir = normalize(light.position.xyz); 85 | attenuation = 1.0; //no attenuation for directional lights 86 | } else { 87 | //point light 88 | light_surface_dir = normalize(vec3(light.position - vertex_world)); 89 | float distanceToLight = length(vec3(light.position - vertex_world)); 90 | attenuation = clamp(1.0 - distanceToLight*distanceToLight/(light.radius*light.radius), 0.0, 1.0); 91 | 92 | //cone restrictions (affects attenuation) 93 | float lightToSurfaceAngle = acos(dot(-light_surface_dir, normalize(light.coneDirection))); 94 | if(lightToSurfaceAngle > light.coneAngle){ 95 | attenuation = 0.0; 96 | } 97 | } 98 | 99 | //ambient 100 | vec3 ambient = surfaceColor.rgb * light.ambient; 101 | 102 | //diffuse 103 | float sDotN = max(0.0, dot(normal, light_surface_dir)); 104 | vec3 diffuse = sDotN * surfaceColor.rgb * light.diffuse; 105 | 106 | // Convert world space variables to view space 107 | vec3 normal_view = normalize(mat3(view) *normal); 108 | vec3 view_dir = normalize(-vertex_view.xyz); 109 | vec3 light_surface_view_dir = normalize(vec3(view * light.position - vertex_view)); 110 | 111 | // Specular 112 | vec3 halfDir = normalize(light_surface_view_dir + view_dir); 113 | float specAngle = max(dot(halfDir, normal_view), 0.0); 114 | 115 | vec3 reflection = reflect(view_dir, normal_view); 116 | vec3 r_world = vec3(inv_view * vec4(reflection, 0.0)); 117 | vec3 specular = mtl_diffuse * texture(cubeMap, -r_world).rgb * light.specular * pow(specAngle, shininess); 118 | 119 | return emission + ambient + attenuation*(diffuse + specular); 120 | } 121 | 122 | vec3 applyFog( in vec3 rgb, // original color of the pixel 123 | in float distance ) // camera to point distance 124 | { 125 | float fogAmount = 1.0 - exp( -distance*fog_density); 126 | vec3 fogColor = vec3(0.5,0.6,0.7); 127 | return mix(rgb, fogColor, fogAmount); 128 | } 129 | 130 | void main(void) { 131 | vec4 vertex_view = view * vertex; 132 | 133 | vec3 lit_colour = vec3(0); 134 | float texture_alpha = texture(texMap, st).a; 135 | // Do not render or blend clear pixels 136 | if(texture_alpha < 0.1){ 137 | discard; 138 | } 139 | 140 | // 8 pass poisson sampled shadows 141 | float visibility = 1.0; 142 | if(render_shadows == 1){ 143 | int num_passes = 8; 144 | for (int i=0;i<8;i++){ 145 | vec3 current = vec3(shadowCoord.xy + poissonDisk[i]/750.0, shadowCoord.z); 146 | if ( texture(shadowMap, current) < 0.5 ){ 147 | visibility -= 0.5/num_passes; 148 | } 149 | } 150 | } 151 | 152 | for(int i = 0; i < num_lights; ++i){ 153 | lit_colour += ApplyLight(lights[i], vec3(texture(texMap, st)), normal, vertex, vertex_view); 154 | } 155 | 156 | lit_colour = lit_colour * visibility; 157 | lit_colour = applyFog(lit_colour,-vertex_view.z); 158 | fragColour = vec4(lit_colour, texture_alpha); 159 | } -------------------------------------------------------------------------------- /shaders/entity.vert: -------------------------------------------------------------------------------- 1 | 2 | // Per-fragment Phong lighting. 3 | // The vertex shader converts vertex position and normal in eye space. 4 | // Passes these to the fragment shader for per-fragment Phong calculation. 5 | 6 | #version 330 7 | 8 | uniform mat4 model; 9 | uniform mat4 view; 10 | uniform mat4 projection; 11 | 12 | uniform mat4 depth_pv; 13 | uniform vec4 clip_plane; 14 | 15 | // using named parameters for a test as defined in EntityShader.cpp 16 | layout (location = 0) in vec3 a_vertex; 17 | layout (location = 1) in vec3 a_normal; 18 | layout (location = 2) in vec2 a_tex_coord; 19 | 20 | out vec4 vertex; // vertex position in world space 21 | out vec3 normal; // the world space normal 22 | out vec4 shadowCoord; // shadow vertex position in world space 23 | out vec2 st; 24 | 25 | void main(void) { 26 | vertex = model * vec4(a_vertex, 1.0); 27 | normal = normalize(mat3(model) * a_normal); // not using inverse-transpose but still seems to work 28 | st = vec2(a_tex_coord.x, 1.0 - a_tex_coord.y); 29 | 30 | gl_ClipDistance[0] = dot(vertex, clip_plane); 31 | gl_Position = projection * view * vertex; 32 | shadowCoord = depth_pv * vertex; 33 | } 34 | -------------------------------------------------------------------------------- /shaders/skybox.frag: -------------------------------------------------------------------------------- 1 | #version 330 2 | 3 | in vec3 st; 4 | 5 | out vec4 fragColour; 6 | 7 | uniform samplerCube cubeMap; 8 | const float lowerLimit = -10.0; 9 | const float upperLimit = 45.0; 10 | 11 | void main(void) { 12 | vec3 fogColor = vec3(0.5,0.6,0.7); 13 | float factor = (st.y - lowerLimit) / (upperLimit - lowerLimit); 14 | factor = clamp(factor, 0.0, 1.0); 15 | fragColour = mix(vec4(fogColor, 1.0f), texture(cubeMap, st), factor); 16 | } -------------------------------------------------------------------------------- /shaders/skybox.vert: -------------------------------------------------------------------------------- 1 | #version 330 2 | 3 | layout (location = 0) in vec3 a_vertex; 4 | 5 | out vec3 st; 6 | out float blend; 7 | 8 | uniform mat4 projection; 9 | uniform mat4 view; 10 | 11 | void main(void) { 12 | gl_Position = projection * view * vec4(a_vertex, 1.0f); 13 | st = a_vertex; 14 | } 15 | -------------------------------------------------------------------------------- /shaders/terrain.frag: -------------------------------------------------------------------------------- 1 | // 2 | // Calculates Phong colour at each fragment. 3 | // Ambient, diffuse and specular terms. 4 | // Uses interpolated position and normal values passed from vertex shader. 5 | 6 | #version 330 7 | 8 | in vec4 vertex; 9 | in vec3 normal; 10 | in vec2 st; 11 | in vec4 shadowCoord; 12 | 13 | layout(location = 0) out vec4 fragColour; 14 | 15 | uniform sampler2D blendMap; 16 | uniform sampler2D backMap; 17 | uniform sampler2D rMap; 18 | uniform sampler2D gMap; 19 | uniform sampler2D bMap; 20 | uniform sampler2DShadow shadowMap; 21 | 22 | uniform mat4 projection; 23 | uniform mat4 view; 24 | uniform mat4 model; 25 | 26 | 27 | 28 | 29 | #define MAX_LIGHTS 10 30 | uniform int num_lights; 31 | uniform struct Light { 32 | vec4 position; 33 | vec3 diffuse; 34 | vec3 specular; 35 | vec3 ambient; 36 | float radius; 37 | float coneAngle; 38 | vec3 coneDirection; 39 | } lights[MAX_LIGHTS]; 40 | 41 | uniform float shininess = 32; 42 | uniform float fog_density = 0.02; 43 | 44 | vec2 poissonDisk[16] = vec2[]( 45 | vec2(-0.94201624, -0.39906216), 46 | vec2(0.94558609, -0.76890725), 47 | vec2(-0.094184101, -0.92938870), 48 | vec2(0.34495938, 0.29387760), 49 | vec2(-0.91588581, 0.45771432), 50 | vec2(-0.81544232, -0.87912464), 51 | vec2(-0.38277543, 0.27676845), 52 | vec2(0.97484398, 0.75648379), 53 | vec2(0.44323325, -0.97511554), 54 | vec2(0.53742981, -0.47373420), 55 | vec2(-0.26496911, -0.41893023), 56 | vec2(0.79197514, 0.19090188), 57 | vec2(-0.24188840, 0.99706507), 58 | vec2(-0.81409955, 0.91437590), 59 | vec2(0.19984126, 0.78641367), 60 | vec2(0.14383161, -0.1410079 ) 61 | ); 62 | 63 | // Returns a random number based on a vec3 and an int. 64 | float random(vec3 seed, int i){ 65 | vec4 seed4 = vec4(seed,i); 66 | float dot_product = dot(seed4, vec4(12.9898,78.233,45.164,94.673)); 67 | return fract(sin(dot_product) * 43758.5453); 68 | } 69 | 70 | // Multiple lights code from 71 | // http://www.tomdalling.com/blog/modern-opengl/08-even-more-lighting-directional-lights-spotlights-multiple-lights 72 | vec3 applyLight(Light light, vec3 surfaceColor, vec3 normal, vec3 surface_pos) { 73 | vec3 light_dir; 74 | float attenuation = 1.0; 75 | 76 | if(light.position.w == 0.0) { 77 | //directional light 78 | light_dir = normalize(light.position.xyz); 79 | attenuation = 1.0; //no attenuation for directional lights 80 | } else { 81 | //point light 82 | light_dir = normalize(light.position.xyz - surface_pos); 83 | float distanceToLight = length(light.position.xyz - surface_pos); 84 | attenuation = clamp(1.0 - distanceToLight*distanceToLight/(light.radius*light.radius), 0.0, 1.0); 85 | 86 | //cone restrictions (affects attenuation) 87 | float lightToSurfaceAngle = acos(dot(-light_dir, normalize(light.coneDirection))); 88 | if(lightToSurfaceAngle > light.coneAngle){ 89 | attenuation = 0.0; 90 | } 91 | } 92 | 93 | //ambient 94 | vec3 ambient = surfaceColor.rgb * light.ambient; 95 | 96 | //diffuse 97 | float sDotN = max(0.0, dot(normal, light_dir)); 98 | vec3 diffuse = sDotN * surfaceColor.rgb * light.diffuse; 99 | 100 | // Specular is ignored for terrain. 101 | // Extension: Use a specular map for this 102 | 103 | return ambient + attenuation*(diffuse); 104 | } 105 | 106 | vec3 applyFog(vec3 rgb, float cam_point_dist) { 107 | float fogAmount = 1.0 - exp( -cam_point_dist*fog_density); 108 | vec3 fogColor = vec3(0.5,0.6,0.7); 109 | return mix(rgb, fogColor, fogAmount); 110 | } 111 | 112 | void main(void) { 113 | vec4 blendColour = texture(blendMap, st); 114 | float backgroundAmount = 1.0 - (blendColour.r + blendColour.g + blendColour.b); 115 | vec2 st_tiled = st * 64.0f; 116 | vec4 backComponent = texture(backMap, st_tiled) * backgroundAmount; 117 | vec4 rComponent = texture(rMap, st_tiled) * blendColour.r; 118 | vec4 gComponent = texture(gMap, st_tiled) * blendColour.g; 119 | vec4 bComponent = texture(bMap, st_tiled) * blendColour.b; 120 | 121 | vec4 mixedColour = backComponent + rComponent + gComponent + bComponent; 122 | vec4 cameraSpaceVert = view * vertex; 123 | 124 | // 8 pass poisson sampled shadows 125 | float visibility = 1.0; 126 | int num_passes = 8; 127 | for (int i=0;i<8;i++){ 128 | // int index = int(16.0*random(floor(vertex.xyz*1000.0), i))%16; 129 | vec3 current = vec3(shadowCoord.xy + poissonDisk[i]/2000.0, shadowCoord.z); 130 | if ( texture(shadowMap, current) < 0.5 ){ 131 | visibility -= 0.5/num_passes; 132 | } 133 | } 134 | 135 | vec3 lit_colour = vec3(0); 136 | for(int i = 0; i < num_lights; ++i){ 137 | lit_colour += applyLight(lights[i], mixedColour.rgb, normal, vertex.xyz); 138 | } 139 | lit_colour = lit_colour * visibility; 140 | lit_colour = applyFog(lit_colour,-cameraSpaceVert.z); 141 | 142 | fragColour = vec4(lit_colour, 1.0); 143 | } -------------------------------------------------------------------------------- /shaders/terrain.vert: -------------------------------------------------------------------------------- 1 | 2 | // Per-fragment Phong lighting. 3 | // The vertex shader converts vertex position and normal in eye space. 4 | // Passes these to the fragment shader for per-fragment Phong calculation. 5 | 6 | #version 330 7 | 8 | uniform mat4 model; 9 | uniform mat4 view; 10 | uniform mat4 projection; 11 | 12 | uniform mat4 depth_pv; 13 | uniform vec4 clip_plane; 14 | 15 | layout (location = 0) in vec3 a_vertex; 16 | layout (location = 1) in vec3 a_normal; 17 | layout (location = 2) in vec2 a_tex_coord; 18 | 19 | out vec4 vertex; // vertex position in world space 20 | out vec4 shadowCoord; // shadow vertex position in world space 21 | out vec3 normal; // the world space normal 22 | out vec2 st; 23 | 24 | void main(void) { 25 | vertex = model * vec4(a_vertex, 1.0); 26 | normal = normalize(mat3(model) * a_normal); // not using inverse-transpose but still seems to work 27 | st = vec2(a_tex_coord.x, 1.0 - a_tex_coord.y); 28 | 29 | gl_ClipDistance[0] = dot(vertex, clip_plane); 30 | gl_Position = projection * view * vertex; 31 | shadowCoord = depth_pv * model * vec4(a_vertex,1.0); 32 | } 33 | -------------------------------------------------------------------------------- /water/WaterRenderer.cpp: -------------------------------------------------------------------------------- 1 | #include "WaterRenderer.h" 2 | 3 | #include "../Loader.h" 4 | #include "../GameTime.h" 5 | 6 | WaterRenderer::WaterRenderer() { 7 | // clang-format off 8 | std::vector vertices = { 9 | -1.0f, 0.0f, 1.0f, 10 | -1.0f, 0.0f, -1.0f, 11 | 1.0f, 0.0f, -1.0f, 12 | 1.0f, 0.0f, 1.0f, 13 | }; 14 | 15 | std::vector indices = { 16 | 0, 3, 2, 17 | 2, 1, 0 18 | }; 19 | // clang-format on 20 | indexCount = indices.size(); 21 | vao = Loader::getLoader()->loadVAO(vertices, indices); 22 | movement = 0.0f; 23 | 24 | dudvMap = Loader::getLoader()->loadTexture("res/water/dudv.png"); 25 | } 26 | 27 | // Only works for one set of water, update to reflect 28 | void WaterRenderer::render(const Entity* water, const glm::mat4& view, const glm::mat4& projection, GLuint refract, 29 | GLuint reflect, const glm::vec3& cameraPosition, const Light* light) { 30 | shader.enable(); 31 | glDisable(GL_CULL_FACE); 32 | 33 | shader.loadProjection(projection); 34 | shader.loadView(view); 35 | shader.loadTextures(); 36 | shader.loadMovement(movement); 37 | shader.loadCameraPosition(cameraPosition); 38 | shader.loadLight(light); 39 | 40 | movement += WATER_MOVE_SPEED * GameTime::getGameTime()->getDt(); 41 | if (movement > 1.0f) { 42 | movement -= 1.0f; 43 | } 44 | 45 | glActiveTexture(GL_TEXTURE0); 46 | glBindTexture(GL_TEXTURE_2D, refract); 47 | glActiveTexture(GL_TEXTURE1); 48 | glBindTexture(GL_TEXTURE_2D, reflect); 49 | glActiveTexture(GL_TEXTURE2); 50 | glBindTexture(GL_TEXTURE_2D, dudvMap); 51 | 52 | shader.loadModel(water->calculateModelMatrix()); 53 | glBindVertexArray(vao); 54 | glEnableVertexAttribArray(0); 55 | 56 | glDrawElements(GL_TRIANGLES, static_cast(indexCount), GL_UNSIGNED_INT, (void*)0); 57 | 58 | glDisableVertexAttribArray(0); 59 | glBindVertexArray(0); 60 | 61 | glEnable(GL_CULL_FACE); 62 | shader.disable(); 63 | } -------------------------------------------------------------------------------- /water/WaterRenderer.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "WaterShader.h" 4 | #include "../entities/Entity.h" 5 | 6 | #include 7 | #include 8 | 9 | #include 10 | #include 11 | 12 | /* 13 | https://www.youtube.com/playlist?list=PLRIWtICgwaX23jiqVByUs0bqhnalNTNZh 14 | 15 | This tutorial was used to implement water. 16 | Therefore, there are similarities to the code presented there, however I did try and implement it with my own spin and 17 | differences. But especially in the shaders it isn't THAT different, so I am referencing because it is the work of 18 | another, but it only makes up a small bit of the overall project. 19 | */ 20 | 21 | const float WATER_MOVE_SPEED = 0.04f; 22 | 23 | class WaterRenderer { 24 | private: 25 | WaterShader shader; 26 | GLuint vao; 27 | size_t indexCount; 28 | 29 | GLuint dudvMap; 30 | 31 | float movement; 32 | 33 | public: 34 | WaterRenderer(); 35 | 36 | void render(const Entity* water, const glm::mat4& view, const glm::mat4& projection, GLuint refract, GLuint reflect, 37 | const glm::vec3& cameraPosition, const Light* light); 38 | }; 39 | -------------------------------------------------------------------------------- /water/WaterShader.cpp: -------------------------------------------------------------------------------- 1 | #include "WaterShader.h" 2 | 3 | WaterShader::WaterShader() : ShaderProgram(WATER_VERTEX_SHADER, WATER_FRAGMENT_SHADER) { 4 | bindUniformLocations(); 5 | } 6 | 7 | void WaterShader::bindUniformLocations() { 8 | location_projection = glGetUniformLocation(shaderID, "projection"); 9 | location_view = glGetUniformLocation(shaderID, "view"); 10 | location_model = glGetUniformLocation(shaderID, "model"); 11 | 12 | location_refractionTexture = glGetUniformLocation(shaderID, "refractionTexture"); 13 | location_reflectionTexture = glGetUniformLocation(shaderID, "reflectionTexture"); 14 | 15 | location_dudvMap = glGetUniformLocation(shaderID, "dudvMap"); 16 | location_movement = glGetUniformLocation(shaderID, "movement"); 17 | location_camera_position = glGetUniformLocation(shaderID, "camera_position"); 18 | location_lightColour = glGetUniformLocation(shaderID, "lightColour"); 19 | location_lightPosition = glGetUniformLocation(shaderID, "lightPosition"); 20 | } 21 | 22 | void WaterShader::loadProjection(const glm::mat4& proj) { 23 | loadUniformValue(location_projection, proj); 24 | } 25 | 26 | void WaterShader::loadView(const glm::mat4& view) { 27 | loadUniformValue(location_view, view); 28 | } 29 | 30 | void WaterShader::loadModel(const glm::mat4& model) { 31 | loadUniformValue(location_model, model); 32 | } 33 | 34 | void WaterShader::loadMovement(float movement) { 35 | loadUniformValue(location_movement, movement); 36 | } 37 | 38 | void WaterShader::loadCameraPosition(const glm::vec3& position) { 39 | loadUniformValue(location_camera_position, position); 40 | } 41 | 42 | void WaterShader::loadLight(const Light* light) { 43 | loadUniformValue(location_lightColour, light->specular); 44 | loadUniformValue(location_lightPosition, glm::vec3(light->position)); 45 | } 46 | 47 | void WaterShader::loadTextures() { 48 | loadUniformValue(location_refractionTexture, 0); 49 | loadUniformValue(location_reflectionTexture, 1); 50 | loadUniformValue(location_dudvMap, 2); 51 | } -------------------------------------------------------------------------------- /water/WaterShader.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "../entities/Light.h" 4 | #include "../shaders/ShaderProgram.h" 5 | 6 | #include 7 | 8 | #include 9 | #include 10 | 11 | const std::string WATER_VERTEX_SHADER = "water/water.vert"; 12 | const std::string WATER_FRAGMENT_SHADER = "water/water.frag"; 13 | 14 | class WaterShader : public ShaderProgram { 15 | private: 16 | GLuint location_projection; 17 | GLuint location_view; 18 | GLuint location_model; 19 | 20 | GLuint location_refractionTexture; 21 | GLuint location_reflectionTexture; 22 | GLuint location_dudvMap; 23 | GLuint location_movement; 24 | GLuint location_camera_position; 25 | 26 | GLuint location_lightColour; 27 | GLuint location_lightPosition; 28 | 29 | public: 30 | WaterShader(); 31 | 32 | void bindUniformLocations(); 33 | 34 | void loadProjection(const glm::mat4& projection); 35 | void loadView(const glm::mat4& view); 36 | void loadModel(const glm::mat4& model); 37 | void loadMovement(float movement); 38 | void loadLight(const Light* light); 39 | void loadCameraPosition(const glm::vec3& position); 40 | void loadTextures(); 41 | }; 42 | -------------------------------------------------------------------------------- /water/water.frag: -------------------------------------------------------------------------------- 1 | #version 330 2 | 3 | in vec4 clipSpace; 4 | in vec2 texCoords; 5 | in vec3 vertCamDir; 6 | in vec4 vertex_view; 7 | 8 | out vec4 fragColour; 9 | 10 | uniform sampler2D refractionTexture; 11 | uniform sampler2D reflectionTexture; 12 | uniform sampler2D dudvMap; 13 | uniform sampler2D normalMap; 14 | 15 | uniform vec3 lightColour; 16 | 17 | uniform float movement; 18 | 19 | uniform float fog_density = 0.02; 20 | const float strength = 0.02; 21 | 22 | const float shininess = 30.0; 23 | const float reflectance = 0.3; 24 | 25 | const vec3 normal = vec3(0.0, 1.0, 0.0); 26 | 27 | vec3 applyFog(vec3 rgb, float distance ){ 28 | float fogAmount = 1.0 - exp( -distance*fog_density); 29 | vec3 fogColor = vec3(0.5,0.6,0.7); 30 | return mix(rgb, fogColor, fogAmount); 31 | } 32 | 33 | void main(void) { 34 | 35 | vec2 ndc = (clipSpace.xy/clipSpace.w)/2.0 + 0.5; 36 | 37 | vec2 distortedTexCoords = texture(dudvMap, vec2(texCoords.x + movement, texCoords.y)).rg*0.1; 38 | distortedTexCoords = texCoords + vec2(distortedTexCoords.x, distortedTexCoords.y+movement); 39 | vec2 totalDistort = (texture(dudvMap, distortedTexCoords).rg * 2.0 - 1.0) * strength; 40 | 41 | vec2 reflectCoord = vec2(ndc.x, -ndc.y) + totalDistort; 42 | reflectCoord.x = clamp(reflectCoord.x, 0.001, 0.999); 43 | reflectCoord.y = clamp(reflectCoord.y, -0.999, -0.001); 44 | vec2 refractCoord = clamp(vec2(ndc.x, ndc.y) + totalDistort, 0.001, 0.999); 45 | 46 | float fresnelFactor = dot(normal, normalize(vertCamDir)); 47 | fresnelFactor = clamp(fresnelFactor, 0.2, 0.8); 48 | 49 | vec4 refractionColour = texture(refractionTexture, refractCoord); 50 | vec4 reflectionColour = texture(reflectionTexture, reflectCoord); 51 | 52 | fragColour = mix(reflectionColour, refractionColour, fresnelFactor); 53 | fragColour = mix(fragColour, vec4(0.0, 0.3, 0.4, 1.0), 0.2); 54 | fragColour = vec4(applyFog(fragColour.rgb, -vertex_view.z), 1.0); 55 | } -------------------------------------------------------------------------------- /water/water.vert: -------------------------------------------------------------------------------- 1 | #version 330 2 | 3 | layout (location = 0) in vec3 a_vertex; 4 | 5 | out vec4 clipSpace; 6 | out vec2 texCoords; 7 | out vec3 vertCamDir; 8 | out vec4 vertex_view; 9 | 10 | uniform mat4 projection; 11 | uniform mat4 view; 12 | uniform mat4 model; 13 | uniform vec3 camera_position; 14 | 15 | uniform vec3 lightPosition; 16 | 17 | const float tileFactor = 5.0; 18 | 19 | void main(void) { 20 | vec4 worldPos = model * vec4(a_vertex, 1.0); 21 | vertex_view = view * worldPos; 22 | clipSpace = projection * vertex_view; 23 | gl_Position = clipSpace; 24 | texCoords = vec2((a_vertex.x/2.0 + 0.5)*2, a_vertex.z/2.0 + 0.5) * tileFactor; 25 | vertCamDir = camera_position - worldPos.xyz; 26 | } --------------------------------------------------------------------------------