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);
}
-
-
-
-