diff --git a/src/8.guest/2021/3.tessellation/terrain_cpu_src/8.3.cpuheight.fs b/src/8.guest/2021/3.tessellation/terrain_cpu_src/8.3.cpuheight.fs new file mode 100644 index 0000000..b25e251 --- /dev/null +++ b/src/8.guest/2021/3.tessellation/terrain_cpu_src/8.3.cpuheight.fs @@ -0,0 +1,11 @@ +#version 330 core + +out vec4 FragColor; + +in float Height; + +void main() +{ + float h = (Height + 16)/32.0f; // shift and scale the height in to a grayscale value + FragColor = vec4(h, h, h, 1.0); +} \ No newline at end of file diff --git a/src/8.guest/2021/3.tessellation/terrain_cpu_src/8.3.cpuheight.vs b/src/8.guest/2021/3.tessellation/terrain_cpu_src/8.3.cpuheight.vs new file mode 100644 index 0000000..3bb34eb --- /dev/null +++ b/src/8.guest/2021/3.tessellation/terrain_cpu_src/8.3.cpuheight.vs @@ -0,0 +1,16 @@ +#version 330 core +layout (location = 0) in vec3 aPos; + +out float Height; +out vec3 Position; + +uniform mat4 model; +uniform mat4 view; +uniform mat4 projection; + +void main() +{ + Height = aPos.y; + Position = (view * model * vec4(aPos, 1.0)).xyz; + gl_Position = projection * view * model * vec4(aPos, 1.0); +} \ No newline at end of file diff --git a/src/8.guest/2021/3.tessellation/terrain_cpu_src/main.cpp b/src/8.guest/2021/3.tessellation/terrain_cpu_src/main.cpp new file mode 100644 index 0000000..f7f592b --- /dev/null +++ b/src/8.guest/2021/3.tessellation/terrain_cpu_src/main.cpp @@ -0,0 +1,296 @@ +#include +#include + +#define STB_IMAGE_IMPLEMENTATION +#include "stb_image.h" + +#include +#include + +#include +#include + +#include +#include + +void framebuffer_size_callback(GLFWwindow* window, int width, int height); +void key_callback(GLFWwindow* window, int key, int scancode, int action, int modifiers); +void mouse_callback(GLFWwindow* window, double xpos, double ypos); +void scroll_callback(GLFWwindow* window, double xoffset, double yoffset); +void processInput(GLFWwindow *window); + +// settings +const unsigned int SCR_WIDTH = 800; +const unsigned int SCR_HEIGHT = 600; +int useWireframe = 0; +int displayGrayscale = 0; + +// camera - give pretty starting point +Camera camera(glm::vec3(67.0f, 627.5f, 169.9f), + glm::vec3(0.0f, 1.0f, 0.0f), + -128.1f, -42.4f); +float lastX = SCR_WIDTH / 2.0f; +float lastY = SCR_HEIGHT / 2.0f; +bool firstMouse = true; + +// timing +float deltaTime = 0.0f; +float lastFrame = 0.0f; + +int main() +{ + // glfw: initialize and configure + // ------------------------------ + glfwInit(); + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); + glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); + +#ifdef __APPLE__ + glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); +#endif + + // glfw window creation + // -------------------- + GLFWwindow* window = glfwCreateWindow(SCR_WIDTH, SCR_HEIGHT, "LearnOpenGL: Terrain CPU", NULL, NULL); + if (window == NULL) + { + std::cout << "Failed to create GLFW window" << std::endl; + glfwTerminate(); + return -1; + } + glfwMakeContextCurrent(window); + glfwSetFramebufferSizeCallback(window, framebuffer_size_callback); + glfwSetKeyCallback(window, key_callback); + glfwSetCursorPosCallback(window, mouse_callback); + glfwSetScrollCallback(window, scroll_callback); + + // tell GLFW to capture our mouse + glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED); + + // glad: load all OpenGL function pointers + // --------------------------------------- + if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)) + { + std::cout << "Failed to initialize GLAD" << std::endl; + return -1; + } + + // configure global opengl state + // ----------------------------- + glEnable(GL_DEPTH_TEST); + + // build and compile our shader program + // ------------------------------------ + Shader heightMapShader("8.3.cpuheight.vs","8.3.cpuheight.fs"); + + // load and create a texture + // ------------------------- + // load image, create texture and generate mipmaps + // The FileSystem::getPath(...) is part of the GitHub repository so we can find files on any IDE/platform; replace it with your own image path. + stbi_set_flip_vertically_on_load(true); + int width, height, nrChannels; + unsigned char *data = stbi_load("resources/heightmaps/iceland_heightmap.png", &width, &height, &nrChannels, 0); + if (data) + { + std::cout << "Loaded heightmap of size " << height << " x " << width << std::endl; + } + else + { + std::cout << "Failed to load texture" << std::endl; + } + + + // set up vertex data (and buffer(s)) and configure vertex attributes + // ------------------------------------------------------------------ + std::vector vertices; + float yScale = 64.0f / 256.0f, yShift = 16.0f; + int rez = 1; + unsigned bytePerPixel = nrChannels; + for(int i = 0; i < height; i++) + { + 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 + } + } + std::cout << "Loaded " << vertices.size() / 3 << " vertices" << std::endl; + stbi_image_free(data); + + std::vector indices; + 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)); + } + } + } + std::cout << "Loaded " << indices.size() << " indices" << std::endl; + + const int numStrips = (height-1)/rez; + const int numTrisPerStrip = (width/rez)*2-2; + std::cout << "Created lattice of " << numStrips << " strips with " << numTrisPerStrip << " triangles each" << std::endl; + std::cout << "Created " << numStrips * numTrisPerStrip << " triangles total" << std::endl; + + // first, configure the cube's VAO (and terrainVBO + terrainIBO) + unsigned int terrainVAO, terrainVBO, terrainIBO; + glGenVertexArrays(1, &terrainVAO); + glBindVertexArray(terrainVAO); + + glGenBuffers(1, &terrainVBO); + glBindBuffer(GL_ARRAY_BUFFER, terrainVBO); + glBufferData(GL_ARRAY_BUFFER, vertices.size() * sizeof(float), &vertices[0], GL_STATIC_DRAW); + + // position attribute + glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0); + glEnableVertexAttribArray(0); + + glGenBuffers(1, &terrainIBO); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, terrainIBO); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices.size() * sizeof(unsigned), &indices[0], GL_STATIC_DRAW); + + // render loop + // ----------- + while (!glfwWindowShouldClose(window)) + { + // per-frame time logic + // -------------------- + float currentFrame = glfwGetTime(); + deltaTime = currentFrame - lastFrame; + lastFrame = currentFrame; + +// std::cout << deltaTime << "ms (" << 1.0f / deltaTime << " FPS)" << std::endl; + + // input + // ----- + processInput(window); + + // render + // ------ + glClearColor(0.1f, 0.1f, 0.1f, 1.0f); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + // be sure to activate shader when setting uniforms/drawing objects + heightMapShader.use(); + + // view/projection transformations + glm::mat4 projection = glm::perspective(glm::radians(camera.Zoom), (float)SCR_WIDTH / (float)SCR_HEIGHT, 0.1f, 100000.0f); + glm::mat4 view = camera.GetViewMatrix(); + heightMapShader.setMat4("projection", projection); + heightMapShader.setMat4("view", view); + + // world transformation + glm::mat4 model = glm::mat4(1.0f); + heightMapShader.setMat4("model", model); + + // render the cube + glBindVertexArray(terrainVAO); +// glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); + for(unsigned strip = 0; strip < numStrips; strip++) + { + 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 + } + + // glfw: swap buffers and poll IO events (keys pressed/released, mouse moved etc.) + // ------------------------------------------------------------------------------- + glfwSwapBuffers(window); + glfwPollEvents(); + } + + // optional: de-allocate all resources once they've outlived their purpose: + // ------------------------------------------------------------------------ + glDeleteVertexArrays(1, &terrainVAO); + glDeleteBuffers(1, &terrainVBO); + glDeleteBuffers(1, &terrainIBO); + + // glfw: terminate, clearing all previously allocated GLFW resources. + // ------------------------------------------------------------------ + glfwTerminate(); + return 0; +} + +// process all input: query GLFW whether relevant keys are pressed/released this frame and react accordingly +// --------------------------------------------------------------------------------------------------------- +void processInput(GLFWwindow *window) +{ + if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS) + glfwSetWindowShouldClose(window, true); + + if (glfwGetKey(window, GLFW_KEY_W) == GLFW_PRESS) + camera.ProcessKeyboard(FORWARD, deltaTime); + if (glfwGetKey(window, GLFW_KEY_S) == GLFW_PRESS) + camera.ProcessKeyboard(BACKWARD, deltaTime); + if (glfwGetKey(window, GLFW_KEY_A) == GLFW_PRESS) + camera.ProcessKeyboard(LEFT, deltaTime); + if (glfwGetKey(window, GLFW_KEY_D) == GLFW_PRESS) + camera.ProcessKeyboard(RIGHT, deltaTime); +} + +// glfw: whenever the window size changed (by OS or user resize) this callback function executes +// --------------------------------------------------------------------------------------------- +void framebuffer_size_callback(GLFWwindow* window, int width, int height) +{ + // make sure the viewport matches the new window dimensions; note that width and + // height will be significantly larger than specified on retina displays. + glViewport(0, 0, width, height); +} + +// glfw: whenever a key event occurs, this callback is called +// --------------------------------------------------------------------------------------------- +void key_callback(GLFWwindow* window, int key, int scancode, int action, int modifiers) +{ + if(action == GLFW_PRESS) + { + switch(key) + { + case GLFW_KEY_SPACE: + useWireframe = 1 - useWireframe; + break; + case GLFW_KEY_G: + displayGrayscale = 1 - displayGrayscale; + break; + default: + break; + } + } +} + + +// glfw: whenever the mouse moves, this callback is called +// ------------------------------------------------------- +void mouse_callback(GLFWwindow* window, double xpos, double ypos) +{ + if (firstMouse) + { + lastX = xpos; + lastY = ypos; + firstMouse = false; + } + + float xoffset = xpos - lastX; + float yoffset = lastY - ypos; // reversed since y-coordinates go from bottom to top + + lastX = xpos; + lastY = ypos; + + camera.ProcessMouseMovement(xoffset, yoffset); +} + +// glfw: whenever the mouse scroll wheel scrolls, this callback is called +// ---------------------------------------------------------------------- +void scroll_callback(GLFWwindow* window, double xoffset, double yoffset) +{ + camera.ProcessMouseScroll(yoffset); +} \ No newline at end of file diff --git a/src/8.guest/2021/3.tessellation/terrain_cpu_src/resources/heightmaps/iceland_heightmap.png b/src/8.guest/2021/3.tessellation/terrain_cpu_src/resources/heightmaps/iceland_heightmap.png new file mode 100644 index 0000000..606f175 Binary files /dev/null and b/src/8.guest/2021/3.tessellation/terrain_cpu_src/resources/heightmaps/iceland_heightmap.png differ diff --git a/src/8.guest/2021/3.tessellation/terrain_cpu_src/resources/heightmaps/river2_heightmap.png b/src/8.guest/2021/3.tessellation/terrain_cpu_src/resources/heightmaps/river2_heightmap.png new file mode 100644 index 0000000..ffb211f Binary files /dev/null and b/src/8.guest/2021/3.tessellation/terrain_cpu_src/resources/heightmaps/river2_heightmap.png differ diff --git a/src/8.guest/2021/3.tessellation/terrain_cpu_src/resources/heightmaps/river_heightmap.png b/src/8.guest/2021/3.tessellation/terrain_cpu_src/resources/heightmaps/river_heightmap.png new file mode 100644 index 0000000..753fecd Binary files /dev/null and b/src/8.guest/2021/3.tessellation/terrain_cpu_src/resources/heightmaps/river_heightmap.png differ diff --git a/src/8.guest/2021/3.tessellation/terrain_gpu_dist/8.3.gpuheight.fs b/src/8.guest/2021/3.tessellation/terrain_gpu_dist/8.3.gpuheight.fs new file mode 100644 index 0000000..b3707da --- /dev/null +++ b/src/8.guest/2021/3.tessellation/terrain_gpu_dist/8.3.gpuheight.fs @@ -0,0 +1,11 @@ +#version 410 core + +in float Height; + +out vec4 FragColor; + +void main() +{ + float h = (Height + 16)/64.0f; + FragColor = vec4(h, h, h, 1.0); +} \ No newline at end of file diff --git a/src/8.guest/2021/3.tessellation/terrain_gpu_dist/8.3.gpuheight.tcs b/src/8.guest/2021/3.tessellation/terrain_gpu_dist/8.3.gpuheight.tcs new file mode 100644 index 0000000..57f4a7d --- /dev/null +++ b/src/8.guest/2021/3.tessellation/terrain_gpu_dist/8.3.gpuheight.tcs @@ -0,0 +1,47 @@ +#version 410 core + +layout(vertices=4) out; + +uniform mat4 model; +uniform mat4 view; + +in vec2 TexCoord[]; +out vec2 TextureCoord[]; + +void main() +{ + gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position; + TextureCoord[gl_InvocationID] = TexCoord[gl_InvocationID]; + + if(gl_InvocationID == 0) + { + const int MIN_TESS_LEVEL = 4; + const int MAX_TESS_LEVEL = 64; + const float MIN_DISTANCE = 20; + const float MAX_DISTANCE = 800; + + vec4 eyeSpacePos00 = view * model * gl_in[0].gl_Position; + vec4 eyeSpacePos01 = view * model * gl_in[1].gl_Position; + vec4 eyeSpacePos10 = view * model * gl_in[2].gl_Position; + vec4 eyeSpacePos11 = view * model * gl_in[3].gl_Position; + + // "distance" from camera scaled between 0 and 1 + float distance00 = clamp( (abs(eyeSpacePos00.z) - MIN_DISTANCE) / (MAX_DISTANCE-MIN_DISTANCE), 0.0, 1.0 ); + float distance01 = clamp( (abs(eyeSpacePos01.z) - MIN_DISTANCE) / (MAX_DISTANCE-MIN_DISTANCE), 0.0, 1.0 ); + float distance10 = clamp( (abs(eyeSpacePos10.z) - MIN_DISTANCE) / (MAX_DISTANCE-MIN_DISTANCE), 0.0, 1.0 ); + float distance11 = clamp( (abs(eyeSpacePos11.z) - MIN_DISTANCE) / (MAX_DISTANCE-MIN_DISTANCE), 0.0, 1.0 ); + + float tessLevel0 = mix( MAX_TESS_LEVEL, MIN_TESS_LEVEL, min(distance10, distance00) ); + float tessLevel1 = mix( MAX_TESS_LEVEL, MIN_TESS_LEVEL, min(distance00, distance01) ); + float tessLevel2 = mix( MAX_TESS_LEVEL, MIN_TESS_LEVEL, min(distance01, distance11) ); + float tessLevel3 = mix( MAX_TESS_LEVEL, MIN_TESS_LEVEL, min(distance11, distance10) ); + + gl_TessLevelOuter[0] = tessLevel0; + gl_TessLevelOuter[1] = tessLevel1; + gl_TessLevelOuter[2] = tessLevel2; + gl_TessLevelOuter[3] = tessLevel3; + + gl_TessLevelInner[0] = max(tessLevel1, tessLevel3); + gl_TessLevelInner[1] = max(tessLevel0, tessLevel2); + } +} \ No newline at end of file diff --git a/src/8.guest/2021/3.tessellation/terrain_gpu_dist/8.3.gpuheight.tes b/src/8.guest/2021/3.tessellation/terrain_gpu_dist/8.3.gpuheight.tes new file mode 100644 index 0000000..ab0acab --- /dev/null +++ b/src/8.guest/2021/3.tessellation/terrain_gpu_dist/8.3.gpuheight.tes @@ -0,0 +1,43 @@ +#version 410 core +layout(quads, fractional_odd_spacing, ccw) in; + +uniform sampler2D heightMap; +uniform mat4 model; +uniform mat4 view; +uniform mat4 projection; + +in vec2 TextureCoord[]; + +out float Height; + +void main() +{ + float u = gl_TessCoord.x; + float v = gl_TessCoord.y; + + vec2 t00 = TextureCoord[0]; + vec2 t01 = TextureCoord[1]; + vec2 t10 = TextureCoord[2]; + vec2 t11 = TextureCoord[3]; + + vec2 t0 = (t01 - t00) * u + t00; + vec2 t1 = (t11 - t10) * u + t10; + vec2 texCoord = (t1 - t0) * v + t0; + + Height = texture(heightMap, texCoord).y * 64.0 - 16.0; + + vec4 p00 = gl_in[0].gl_Position; + vec4 p01 = gl_in[1].gl_Position; + vec4 p10 = gl_in[2].gl_Position; + vec4 p11 = gl_in[3].gl_Position; + + vec4 uVec = p01 - p00; + vec4 vVec = p10 - p00; + vec4 normal = normalize( vec4(cross(vVec.xyz, uVec.xyz), 0) ); + + vec4 p0 = (p01 - p00) * u + p00; + vec4 p1 = (p11 - p10) * u + p10; + vec4 p = (p1 - p0) * v + p0 + normal * Height; + + gl_Position = projection * view * model * p; +} \ No newline at end of file diff --git a/src/8.guest/2021/3.tessellation/terrain_gpu_dist/8.3.gpuheight.vs b/src/8.guest/2021/3.tessellation/terrain_gpu_dist/8.3.gpuheight.vs new file mode 100644 index 0000000..fd67c4e --- /dev/null +++ b/src/8.guest/2021/3.tessellation/terrain_gpu_dist/8.3.gpuheight.vs @@ -0,0 +1,11 @@ +#version 410 core +layout (location = 0) in vec3 aPos; +layout (location = 1) in vec2 aTex; + +out vec2 TexCoord; + +void main() +{ + gl_Position = vec4(aPos, 1.0); + TexCoord = aTex; +} \ No newline at end of file diff --git a/src/8.guest/2021/3.tessellation/terrain_gpu_dist/main.cpp b/src/8.guest/2021/3.tessellation/terrain_gpu_dist/main.cpp new file mode 100644 index 0000000..e2c0d72 --- /dev/null +++ b/src/8.guest/2021/3.tessellation/terrain_gpu_dist/main.cpp @@ -0,0 +1,294 @@ +#include +#include + +#define STB_IMAGE_IMPLEMENTATION +#include "stb_image.h" + +#include +#include + +#include +#include + +#include +#include + +void framebuffer_size_callback(GLFWwindow* window, int width, int height); +void key_callback(GLFWwindow* window, int key, int scancode, int action, int modifiers); +void mouse_callback(GLFWwindow* window, double xpos, double ypos); +void scroll_callback(GLFWwindow* window, double xoffset, double yoffset); +void processInput(GLFWwindow *window); + +// settings +const unsigned int SCR_WIDTH = 800; +const unsigned int SCR_HEIGHT = 600; +const unsigned int NUM_PATCH_PTS = 4; + +// camera - give pretty starting point +Camera camera(glm::vec3(67.0f, 627.5f, 169.9f), + glm::vec3(0.0f, 1.0f, 0.0f), + -128.1f, -42.4f); +float lastX = SCR_WIDTH / 2.0f; +float lastY = SCR_HEIGHT / 2.0f; +bool firstMouse = true; + +// timing +float deltaTime = 0.0f; +float lastFrame = 0.0f; + +int main() +{ + // glfw: initialize and configure + // ------------------------------ + glfwInit(); + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4); + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 1); + glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); + +#ifdef __APPLE__ + glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); +#endif + + // glfw window creation + // -------------------- + GLFWwindow* window = glfwCreateWindow(SCR_WIDTH, SCR_HEIGHT, "LearnOpenGL: Terrain GPU", NULL, NULL); + if (window == NULL) + { + std::cout << "Failed to create GLFW window" << std::endl; + glfwTerminate(); + return -1; + } + glfwMakeContextCurrent(window); + glfwSetFramebufferSizeCallback(window, framebuffer_size_callback); + glfwSetKeyCallback(window, key_callback); + glfwSetCursorPosCallback(window, mouse_callback); + glfwSetScrollCallback(window, scroll_callback); + + // tell GLFW to capture our mouse + glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED); + + // glad: load all OpenGL function pointers + // --------------------------------------- + if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)) + { + std::cout << "Failed to initialize GLAD" << std::endl; + return -1; + } + + GLint maxTessLevel; + glGetIntegerv(GL_MAX_TESS_GEN_LEVEL, &maxTessLevel); + std::cout << "Max available tess level: " << maxTessLevel << std::endl; + + // configure global opengl state + // ----------------------------- + glEnable(GL_DEPTH_TEST); + + // build and compile our shader program + // ------------------------------------ + Shader tessHeightMapShader("8.3.gpuheight.vs","8.3.gpuheight.fs", nullptr, // if wishing to render as is + "8.3.gpuheight.tcs", "8.3.gpuheight.tes"); + + // load and create a texture + // ------------------------- + unsigned int texture; + glGenTextures(1, &texture); + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, texture); // all upcoming GL_TEXTURE_2D operations now have effect on this texture object + // set the texture wrapping parameters + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); // set texture wrapping to GL_REPEAT (default wrapping method) + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + // set texture filtering parameters + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + // load image, create texture and generate mipmaps + int width, height, nrChannels; + // The FileSystem::getPath(...) is part of the GitHub repository so we can find files on any IDE/platform; replace it with your own image path. + unsigned char *data = stbi_load("resources/heightmaps/iceland_heightmap.png", &width, &height, &nrChannels, 0); + if (data) + { + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data); + glGenerateMipmap(GL_TEXTURE_2D); + + tessHeightMapShader.setInt("heightMap", 0); + std::cout << "Loaded heightmap of size " << height << " x " << width << std::endl; + } + else + { + std::cout << "Failed to load texture" << std::endl; + } + stbi_image_free(data); + + // set up vertex data (and buffer(s)) and configure vertex attributes + // ------------------------------------------------------------------ + std::vector vertices; + + unsigned rez = 20; + for(unsigned i = 0; i <= rez-1; i++) + { + for(unsigned j = 0; j <= rez-1; j++) + { + vertices.push_back(-width/2.0f + width*i/(float)rez); // v.x + vertices.push_back(0.0f); // v.y + vertices.push_back(-height/2.0f + height*j/(float)rez); // v.z + vertices.push_back(i / (float)rez); // u + vertices.push_back(j / (float)rez); // v + + vertices.push_back(-width/2.0f + width*(i+1)/(float)rez); // v.x + vertices.push_back(0.0f); // v.y + vertices.push_back(-height/2.0f + height*j/(float)rez); // v.z + vertices.push_back((i+1) / (float)rez); // u + vertices.push_back(j / (float)rez); // v + + vertices.push_back(-width/2.0f + width*i/(float)rez); // v.x + vertices.push_back(0.0f); // v.y + vertices.push_back(-height/2.0f + height*(j+1)/(float)rez); // v.z + vertices.push_back(i / (float)rez); // u + vertices.push_back((j+1) / (float)rez); // v + + vertices.push_back(-width/2.0f + width*(i+1)/(float)rez); // v.x + vertices.push_back(0.0f); // v.y + vertices.push_back(-height/2.0f + height*(j+1)/(float)rez); // v.z + vertices.push_back((i+1) / (float)rez); // u + vertices.push_back((j+1) / (float)rez); // v + } + } + std::cout << "Loaded " << rez*rez << " patches of 4 control points each" << std::endl; + std::cout << "Processing " << rez*rez*4 << " vertices in vertex shader" << std::endl; + + // first, configure the cube's VAO (and terrainVBO) + unsigned int terrainVAO, terrainVBO; + glGenVertexArrays(1, &terrainVAO); + glBindVertexArray(terrainVAO); + + glGenBuffers(1, &terrainVBO); + glBindBuffer(GL_ARRAY_BUFFER, terrainVBO); + glBufferData(GL_ARRAY_BUFFER, sizeof(float) * vertices.size(), &vertices[0], GL_STATIC_DRAW); + + // position attribute + glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)0); + glEnableVertexAttribArray(0); + // texCoord attribute + glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)(sizeof(float) * 3)); + glEnableVertexAttribArray(1); + + glPatchParameteri(GL_PATCH_VERTICES, NUM_PATCH_PTS); + + // render loop + // ----------- + while (!glfwWindowShouldClose(window)) + { + // per-frame time logic + // -------------------- + float currentFrame = glfwGetTime(); + deltaTime = currentFrame - lastFrame; + lastFrame = currentFrame; + + // input + // ----- + processInput(window); + + // render + // ------ + glClearColor(0.1f, 0.1f, 0.1f, 1.0f); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + // be sure to activate shader when setting uniforms/drawing objects + tessHeightMapShader.use(); + + // view/projection transformations + glm::mat4 projection = glm::perspective(glm::radians(camera.Zoom), (float)SCR_WIDTH / (float)SCR_HEIGHT, 0.1f, 100000.0f); + glm::mat4 view = camera.GetViewMatrix(); + tessHeightMapShader.setMat4("projection", projection); + tessHeightMapShader.setMat4("view", view); + + // world transformation + glm::mat4 model = glm::mat4(1.0f); + tessHeightMapShader.setMat4("model", model); + + // render the terrain + glBindVertexArray(terrainVAO); + glDrawArrays(GL_PATCHES, 0, NUM_PATCH_PTS*rez*rez); + + // glfw: swap buffers and poll IO events (keys pressed/released, mouse moved etc.) + // ------------------------------------------------------------------------------- + glfwSwapBuffers(window); + glfwPollEvents(); + } + + // optional: de-allocate all resources once they've outlived their purpose: + // ------------------------------------------------------------------------ + glDeleteVertexArrays(1, &terrainVAO); + glDeleteBuffers(1, &terrainVBO); + + // glfw: terminate, clearing all previously allocated GLFW resources. + // ------------------------------------------------------------------ + glfwTerminate(); + return 0; +} + +// process all input: query GLFW whether relevant keys are pressed/released this frame and react accordingly +// --------------------------------------------------------------------------------------------------------- +void processInput(GLFWwindow *window) +{ + if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS) + glfwSetWindowShouldClose(window, true); + + if (glfwGetKey(window, GLFW_KEY_W) == GLFW_PRESS) + camera.ProcessKeyboard(FORWARD, deltaTime); + if (glfwGetKey(window, GLFW_KEY_S) == GLFW_PRESS) + camera.ProcessKeyboard(BACKWARD, deltaTime); + if (glfwGetKey(window, GLFW_KEY_A) == GLFW_PRESS) + camera.ProcessKeyboard(LEFT, deltaTime); + if (glfwGetKey(window, GLFW_KEY_D) == GLFW_PRESS) + camera.ProcessKeyboard(RIGHT, deltaTime); +} + +// glfw: whenever the window size changed (by OS or user resize) this callback function executes +// --------------------------------------------------------------------------------------------- +void framebuffer_size_callback(GLFWwindow* window, int width, int height) +{ + // make sure the viewport matches the new window dimensions; note that width and + // height will be significantly larger than specified on retina displays. + glViewport(0, 0, width, height); +} + +// glfw: whenever a key event occurs, this callback is called +// --------------------------------------------------------------------------------------------- +void key_callback(GLFWwindow* window, int key, int scancode, int action, int modifiers) +{ + if(action == GLFW_PRESS) + { + switch(key) + { + default: + break; + } + } +} + +// glfw: whenever the mouse moves, this callback is called +// ------------------------------------------------------- +void mouse_callback(GLFWwindow* window, double xpos, double ypos) +{ + if (firstMouse) + { + lastX = xpos; + lastY = ypos; + firstMouse = false; + } + + float xoffset = xpos - lastX; + float yoffset = lastY - ypos; // reversed since y-coordinates go from bottom to top + + lastX = xpos; + lastY = ypos; + + camera.ProcessMouseMovement(xoffset, yoffset); +} + +// glfw: whenever the mouse scroll wheel scrolls, this callback is called +// ---------------------------------------------------------------------- +void scroll_callback(GLFWwindow* window, double xoffset, double yoffset) +{ + camera.ProcessMouseScroll(yoffset); +} \ No newline at end of file diff --git a/src/8.guest/2021/3.tessellation/terrain_gpu_dist/resources/heightmaps/iceland_heightmap.png b/src/8.guest/2021/3.tessellation/terrain_gpu_dist/resources/heightmaps/iceland_heightmap.png new file mode 100644 index 0000000..606f175 Binary files /dev/null and b/src/8.guest/2021/3.tessellation/terrain_gpu_dist/resources/heightmaps/iceland_heightmap.png differ diff --git a/src/8.guest/2021/3.tessellation/terrain_gpu_dist/resources/heightmaps/river2_heightmap.png b/src/8.guest/2021/3.tessellation/terrain_gpu_dist/resources/heightmaps/river2_heightmap.png new file mode 100644 index 0000000..ffb211f Binary files /dev/null and b/src/8.guest/2021/3.tessellation/terrain_gpu_dist/resources/heightmaps/river2_heightmap.png differ diff --git a/src/8.guest/2021/3.tessellation/terrain_gpu_dist/resources/heightmaps/river_heightmap.png b/src/8.guest/2021/3.tessellation/terrain_gpu_dist/resources/heightmaps/river_heightmap.png new file mode 100644 index 0000000..753fecd Binary files /dev/null and b/src/8.guest/2021/3.tessellation/terrain_gpu_dist/resources/heightmaps/river_heightmap.png differ