diff --git a/.gitignore b/.gitignore index 7081f79..96d3490 100644 --- a/.gitignore +++ b/.gitignore @@ -363,3 +363,5 @@ MigrationBackup/ FodyWeavers.xsd /FinalProject/temp/shaders /Models +/Terrains +/terrain diff --git a/FinalProject/FinalProject.vcxproj b/FinalProject/FinalProject.vcxproj index fa35487..85ae864 100644 --- a/FinalProject/FinalProject.vcxproj +++ b/FinalProject/FinalProject.vcxproj @@ -129,6 +129,7 @@ + @@ -163,6 +164,7 @@ + diff --git a/FinalProject/FinalProject.vcxproj.filters b/FinalProject/FinalProject.vcxproj.filters index 9409877..2b3fe40 100644 --- a/FinalProject/FinalProject.vcxproj.filters +++ b/FinalProject/FinalProject.vcxproj.filters @@ -180,6 +180,9 @@ Source Files\Qt Widgets\Pages\Scene Editor\Object Setter + + Source Files\OpenGL Abstractions + @@ -233,6 +236,9 @@ Header Files\OpenGL Abstractions + + Header Files\OpenGL Abstractions + diff --git a/FinalProject/sceneviewer.cpp b/FinalProject/sceneviewer.cpp index dc915eb..050c021 100644 --- a/FinalProject/sceneviewer.cpp +++ b/FinalProject/sceneviewer.cpp @@ -40,6 +40,8 @@ SceneViewer::SceneViewer(QWidget* parent) extractShaderResource("fragmentshader.glsl"); extractShaderResource("skyboxvertexshader.glsl"); extractShaderResource("skyboxfragmentshader.glsl"); + extractShaderResource("terrainvertexshader.glsl"); + extractShaderResource("terrainfragmentshader.glsl"); extractShaderResource("boundfragmentshader.glsl"); extractShaderResource("boundvertexshader.glsl"); } @@ -132,8 +134,19 @@ void SceneViewer::initializeGL() { skyVertexShader.dispose(); skyFragmentShader.dispose(); + _terrainShader.ensureInitialized(); + Logger::info("Terrain Shader initialized"); + + VertexShader terrainVertexShader("./temp/shaders/terrainvertexshader.glsl"); + FragmentShader terrainFragmentShader("./temp/shaders/terrainfragmentshader.glsl"); + _terrainShader.attachShader(terrainVertexShader); + _terrainShader.attachShader(terrainFragmentShader); + terrainVertexShader.dispose(); + terrainFragmentShader.dispose(); + // Test Code Start _sky = new SkyBox("E:\\Repositories\\CollegeProjects\\CGAssignments\\FinalProject\\SkyBoxes"); + _terrain = new Terrain("E:\\Repositories\\CollegeProjects\\CGAssignments\\FinalProject\\Terrains"); _dirLight = new DirLight(); @@ -161,11 +174,35 @@ void SceneViewer::paintGL() { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - _shaderProgram.bind(); - // Set view and projection matrices glm::mat4 view = _camera.viewMatrix(); glm::mat4 projection = _camera.projectionMatrix((float)width() / (float)height()); + + // Sky Box Render --------------------------------------------------- + if (_sky != nullptr) { + _skyShader.bind(); + _skyShader.setUniform("view", glm::mat4(glm::mat3(view))); + _skyShader.setUniform("projection", projection); + _sky->render(); + _skyShader.unbind(); + } + // ------------------------------------------------------------------ + + // Terrain Render --------------------------------------------------- + if (_terrain != nullptr) { + _terrainShader.bind(); + _terrainShader.setUniform("view", view); + _terrainShader.setUniform("projection", projection); + _terrainShader.setUniform("model", _terrain->modelMatrix()); + _terrainShader.setUniform("texture1", 2); + _terrain->render(); + _terrainShader.unbind(); + } + // ------------------------------------------------------------------ + + // Renderable Render ------------------------------------------------ + _shaderProgram.bind(); + _shaderProgram.setUniform("view", view); _shaderProgram.setUniform("projection", projection); _shaderProgram.setUniform("viewPos", _camera.position()); @@ -173,6 +210,7 @@ void SceneViewer::paintGL() { int pointLights = 0; int spotLights = 0; + // Update lights for (auto object : _objects) { if (object->hasLight()) { ScopedLight light = object->transformedLight(); @@ -194,6 +232,7 @@ void SceneViewer::paintGL() { _shaderProgram.setUniform("dirlightnr", _dirLight != nullptr ? 1 : 0); + // Render objects for (auto object : _objects) { if (object == _pressedObject) { _shaderProgram.setUniform("selColor", glm::vec3(0.22f)); @@ -211,7 +250,9 @@ void SceneViewer::paintGL() { } _shaderProgram.unbind(); + // ------------------------------------------------------------------ + // Bound box render ------------------------------------------------- if (_selectedObject != nullptr && !_hideBound) { _boundShader.bind(); _boundShader.setUniform("view", view); @@ -226,12 +267,7 @@ void SceneViewer::paintGL() { _hoveredObject->boundary().render(); _boundShader.unbind(); } - - _skyShader.bind(); - _skyShader.setUniform("view", glm::mat4(glm::mat3(view))); - _skyShader.setUniform("projection", projection); - _sky->render(); - _skyShader.unbind(); + // ------------------------------------------------------------------ } void SceneViewer::mousePressEvent(QMouseEvent* event) { diff --git a/FinalProject/sceneviewer.h b/FinalProject/sceneviewer.h index 71585e1..225c696 100644 --- a/FinalProject/sceneviewer.h +++ b/FinalProject/sceneviewer.h @@ -14,6 +14,7 @@ #include "utils.h" #include "logger.h" #include "skybox.h" +#include "terrain.h" class SceneViewer : public QOpenGLWidget, protected QOpenGLFunctions { @@ -25,11 +26,15 @@ private: std::vector _objects; // Dir light DirLight* _dirLight = nullptr; + // Sky Box + SkyBox* _sky; + // Terrain + Terrain* _terrain; // Shader program for objects ShaderProgram _shaderProgram = ShaderProgram::empty(); ShaderProgram _boundShader = ShaderProgram::empty(); ShaderProgram _skyShader = ShaderProgram::empty(); - SkyBox* _sky; + ShaderProgram _terrainShader = ShaderProgram::empty(); // Main camera Camera _camera; float _cameraMovementSpeed = 0.02f; diff --git a/FinalProject/terrain.cpp b/FinalProject/terrain.cpp new file mode 100644 index 0000000..e990744 --- /dev/null +++ b/FinalProject/terrain.cpp @@ -0,0 +1,193 @@ +#include "terrain.h" +#include "utils.h" +#include "vertex.h" +#include +#include +#include +#include + +Terrain::Terrain(std::string path){ + // Convert '\ ' to '/' for Windows + std::replace(path.begin(), path.end(), '\\', '/'); + + stbi_set_flip_vertically_on_load(true); + + unsigned char* data = stbi_load((path+"/heightmap.png").c_str(), &width, &height, &nrChannels, 0); + + + int rez = 1; + unsigned bytePerPixel = nrChannels; + for (int i = 0; i < height; i++) + { + std::vector temp; + for (int j = 0; j < width; j++) + { + unsigned char* pixelOffset = data + (j + width * i) * bytePerPixel; + unsigned char y = pixelOffset[0]; + + // vertex + vertices.push_back(-height / 2.0f + height * i / (float)height); // vx + vertices.push_back((int)y * yScale - yShift); // vy + vertices.push_back(-width / 2.0f + width * j / (float)width); // vz + vertices.push_back((float)i); + vertices.push_back((float)j); + temp.push_back((int)y * yScale - yShift); + } + Point.push_back(temp); + } + stbi_image_free(data); + + for (unsigned i = 0; i < height - 1; i += rez) + { + for (unsigned j = 0; j < width; j += rez) + { + for (unsigned k = 0; k < 2; k++) + { + indices.push_back(j + width * (i + k * rez)); + } + } + } + + numStrips = (height - 1) / rez; + numTrisPerStrip = (width / rez) * 2 - 2; + + OPENGL_EXTRA_FUNCTIONS->glGenVertexArrays(1, &terrainVAO); + OPENGL_EXTRA_FUNCTIONS->glBindVertexArray(terrainVAO); + + OPENGL_EXTRA_FUNCTIONS->glGenBuffers(1, &terrainVBO); + OPENGL_EXTRA_FUNCTIONS->glBindBuffer(GL_ARRAY_BUFFER, terrainVBO); + OPENGL_EXTRA_FUNCTIONS->glBufferData(GL_ARRAY_BUFFER, vertices.size() * sizeof(float), &vertices[0], GL_STATIC_DRAW); + + // position attribute + OPENGL_EXTRA_FUNCTIONS->glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)0); + OPENGL_EXTRA_FUNCTIONS->glEnableVertexAttribArray(0); + // texCoord attribute + OPENGL_EXTRA_FUNCTIONS->glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)(sizeof(float) * 3)); + OPENGL_EXTRA_FUNCTIONS->glEnableVertexAttribArray(1); + + OPENGL_EXTRA_FUNCTIONS->glGenBuffers(1, &terrainIBO); + OPENGL_EXTRA_FUNCTIONS->glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, terrainIBO); + OPENGL_EXTRA_FUNCTIONS->glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices.size() * sizeof(unsigned), &indices[0], GL_STATIC_DRAW); + + + //textureID = loadTexture2(texName, GL_REPEAT, GL_REPEAT, GL_LINEAR_MIPMAP_LINEAR, GL_LINEAR); + tex = loadTexture(path + "/grass.jpg"); + +} + +unsigned int Terrain::loadTexture(std::string path) { + unsigned int textureID; + OPENGL_FUNCTIONS->glGenTextures(1, &textureID); + OPENGL_FUNCTIONS->glBindTexture(GL_TEXTURE_2D, textureID); + + + + stbi_set_flip_vertically_on_load(true); + + int width, height, nrChannels; + + unsigned char* data = stbi_load(path.c_str(), &width, &height, &nrChannels, 0); + if (data) { + GLenum format; + if (nrChannels == 1) { + format = GL_RED; + } + else if (nrChannels == 3) { + format = GL_RGB; + } + else if (nrChannels == 4) { + format = GL_RGBA; + } + else { + return 0; + } + + OPENGL_FUNCTIONS->glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, format, GL_UNSIGNED_BYTE, data); + OPENGL_FUNCTIONS->glGenerateMipmap(GL_TEXTURE_2D); + } + + stbi_image_free(data); + + return textureID; +} + +void Terrain::render() { + // Set the texture wrapping parameters + OPENGL_FUNCTIONS->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); // Set texture wrapping to GL_REPEAT (usually basic wrapping method) + OPENGL_FUNCTIONS->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + // Set texture filtering parameters + OPENGL_FUNCTIONS->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + OPENGL_FUNCTIONS->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + OPENGL_EXTRA_FUNCTIONS->glActiveTexture(GL_TEXTURE2); + OPENGL_EXTRA_FUNCTIONS->glBindTexture(GL_TEXTURE_2D, tex); + + OPENGL_EXTRA_FUNCTIONS->glBindVertexArray(terrainVAO); + for (unsigned strip = 0; strip < numStrips; strip++) + { + OPENGL_EXTRA_FUNCTIONS->glDrawElements(GL_TRIANGLE_STRIP, // primitive type + numTrisPerStrip + 2, // number of indices to render + GL_UNSIGNED_INT, // index data type + (void*)(sizeof(unsigned) * (numTrisPerStrip + 2) * strip)); // offset to starting index + } +} + +float Terrain::GetHeight(float px, float pz) { + float fx = px + height / 2; + float fz = pz + width / 2; + + int x = ((int)fx) % height; + int z = ((int)fz) % width; + int gx = (x + 1) % height; + int gz = (z + 1) % width; + + float ans = (x - fx) * (Point[gx][gz] - Point[x][z]) + Point[x][z]; + + return ans; +} + +glm::vec3 Terrain::GetNormal(glm::vec3 pos) { + float fx = pos.x; + float fz = pos.z; + + glm::vec3 point1(fx - 1, GetHeight(fx - 1, fz - 1), fz - 1); + glm::vec3 point2(fx + 1, GetHeight(fx + 1, fz + 1), fz + 1); + + glm::vec3 l1 = pos - point1; + glm::vec3 l2 = point2 - point1; + glm::vec3 ans = glm::normalize(glm::cross(l1, l2)); + return ans; +} + +void Terrain::hitPoint(glm::vec3 orig, glm::vec3 dir) { + // A good ray step is half of the blockScale + glm::vec3 rayStep = dir * (float)width * 0.25f; + glm::vec3 rayStartPosition = orig; + + // Linear search - Loop until find a point inside and outside the terrain Vector3 + glm::vec3 lastRayPosition = orig; + orig += rayStep; + float map_height = GetHeight(orig.x,orig.z); + while (orig.y > map_height) + { + lastRayPosition = orig; + orig += rayStep; + map_height = GetHeight(orig.x, orig.z); + } + + glm::vec3 startPosition = lastRayPosition; + glm::vec3 endPosition = orig; + + // Binary search with 32 steps. Try to find the exact collision point + for (int i = 0; i < 32; i++) + { + // Binary search pass + glm::vec3 middlePoint = (startPosition + endPosition) * 0.5f; + if (middlePoint.y < height) + endPosition = middlePoint; + else + startPosition = middlePoint; + } + glm::vec3 position = (startPosition + endPosition) * 0.5f; + glm::vec3 normal = GetNormal(position); +} \ No newline at end of file diff --git a/FinalProject/terrain.h b/FinalProject/terrain.h new file mode 100644 index 0000000..89b104a --- /dev/null +++ b/FinalProject/terrain.h @@ -0,0 +1,29 @@ +#pragma once + +#include "utils.h" +#include "vertex.h" + +#include +#include + +class Terrain { +private: + std::vector vertices; + std::vector indices; + unsigned int terrainVAO, terrainVBO, terrainIBO; + std::vector> Point; + float GetHeight(float px, float pz); + glm::vec3 GetNormal(glm::vec3 pos); + int width, height, nrChannels; + int numStrips, numTrisPerStrip; + int NUM_PATCH_PTS = 4; + unsigned rez = 20; + float yScale = 64.0f / 256.0f, yShift = 30.0f; +public: + unsigned int tex; + Terrain(std::string path); + void render(); + unsigned int loadTexture(std::string path); + void hitPoint(glm::vec3 orig, glm::vec3 dir); + glm::mat4 modelMatrix() const { return glm::mat4(1.0f); } +}; \ No newline at end of file diff --git a/FinalProject/terrainfragmentshader.glsl b/FinalProject/terrainfragmentshader.glsl index 02255ad..7b16c4f 100644 --- a/FinalProject/terrainfragmentshader.glsl +++ b/FinalProject/terrainfragmentshader.glsl @@ -1,7 +1,6 @@ -#version 330 core +#version 430 core out vec4 FragColor; -in vec3 ourColor; in vec2 TexCoord; // texture sampler diff --git a/FinalProject/terrainvertexshader.glsl b/FinalProject/terrainvertexshader.glsl index e19fa8b..a71b063 100644 --- a/FinalProject/terrainvertexshader.glsl +++ b/FinalProject/terrainvertexshader.glsl @@ -1,9 +1,8 @@ -#version 330 core +#version 430 core + layout (location = 0) in vec3 aPos; -layout (location = 1) in vec3 aColor; -layout (location = 2) in vec2 aTexCoord; - -out vec3 ourColor; +layout (location = 1) in vec2 aTex; + out vec2 TexCoord; uniform mat4 model; @@ -13,10 +12,5 @@ uniform mat4 projection; void main() { gl_Position = projection * view * model * vec4(aPos, 1.0); - ourColor = aColor; - TexCoord = vec2(aTexCoord.x, aTexCoord.y); + TexCoord = vec2(aTex.x, aTex.y); } - - - -