From 41e3abe45e78e228dabef16c17184282ffec8a83 Mon Sep 17 00:00:00 2001 From: WSAL Evan Date: Fri, 1 Nov 2024 13:41:23 -0400 Subject: [PATCH] Episode 5 --- ScuffedMinecraft/ScuffedMinecraft.vcxproj | 13 +- .../ScuffedMinecraft.vcxproj.filters | 27 +- .../assets/shaders/block_outline_frag.glsl | 10 + .../assets/shaders/block_outline_vert.glsl | 12 + .../assets/shaders/crosshair_frag.glsl | 18 + .../assets/shaders/crosshair_vert.glsl | 14 + ScuffedMinecraft/assets/sprites/block_map.png | Bin 8941 -> 9354 bytes ScuffedMinecraft/assets/sprites/block_map.psd | Bin 132185 -> 119043 bytes ScuffedMinecraft/assets/sprites/crosshair.png | Bin 0 -> 246 bytes ScuffedMinecraft/src/Application.cpp | 331 +++++++++-- ScuffedMinecraft/src/Block.cpp | 8 +- ScuffedMinecraft/src/Block.h | 7 +- ScuffedMinecraft/src/Blocks.h | 37 +- ScuffedMinecraft/src/Camera.cpp | 4 +- ScuffedMinecraft/src/Camera.h | 1 + ScuffedMinecraft/src/Chunk.cpp | 287 +++++++--- ScuffedMinecraft/src/Chunk.h | 23 +- ScuffedMinecraft/src/ChunkData.cpp | 39 ++ ScuffedMinecraft/src/ChunkData.h | 20 + ScuffedMinecraft/src/ChunkPos.cpp | 18 + ScuffedMinecraft/src/ChunkPos.h | 13 + ScuffedMinecraft/src/ChunkPosHash.h | 15 + ScuffedMinecraft/src/Physics.cpp | 47 ++ ScuffedMinecraft/src/Physics.h | 30 + ScuffedMinecraft/src/Planet.cpp | 538 +++++++++++++----- ScuffedMinecraft/src/Planet.h | 36 +- ScuffedMinecraft/src/TupleHash.h | 53 -- ScuffedMinecraft/src/WorldGen.cpp | 55 +- ScuffedMinecraft/src/WorldGen.h | 5 +- 29 files changed, 1257 insertions(+), 404 deletions(-) create mode 100644 ScuffedMinecraft/assets/shaders/block_outline_frag.glsl create mode 100644 ScuffedMinecraft/assets/shaders/block_outline_vert.glsl create mode 100644 ScuffedMinecraft/assets/shaders/crosshair_frag.glsl create mode 100644 ScuffedMinecraft/assets/shaders/crosshair_vert.glsl create mode 100644 ScuffedMinecraft/assets/sprites/crosshair.png create mode 100644 ScuffedMinecraft/src/ChunkData.cpp create mode 100644 ScuffedMinecraft/src/ChunkData.h create mode 100644 ScuffedMinecraft/src/ChunkPos.cpp create mode 100644 ScuffedMinecraft/src/ChunkPos.h create mode 100644 ScuffedMinecraft/src/ChunkPosHash.h create mode 100644 ScuffedMinecraft/src/Physics.cpp create mode 100644 ScuffedMinecraft/src/Physics.h delete mode 100644 ScuffedMinecraft/src/TupleHash.h diff --git a/ScuffedMinecraft/ScuffedMinecraft.vcxproj b/ScuffedMinecraft/ScuffedMinecraft.vcxproj index 0a87fb3..0d702c2 100644 --- a/ScuffedMinecraft/ScuffedMinecraft.vcxproj +++ b/ScuffedMinecraft/ScuffedMinecraft.vcxproj @@ -157,11 +157,14 @@ + + + - + @@ -178,7 +181,10 @@ + + + @@ -198,6 +204,10 @@ + + + + @@ -207,6 +217,7 @@ + diff --git a/ScuffedMinecraft/ScuffedMinecraft.vcxproj.filters b/ScuffedMinecraft/ScuffedMinecraft.vcxproj.filters index 44ed770..45330a1 100644 --- a/ScuffedMinecraft/ScuffedMinecraft.vcxproj.filters +++ b/ScuffedMinecraft/ScuffedMinecraft.vcxproj.filters @@ -33,7 +33,7 @@ Source Files - + Source Files @@ -72,6 +72,15 @@ Source Files + + Source Files + + + Source Files + + + Source Files + @@ -134,6 +143,15 @@ Header Files + + Header Files + + + Header Files + + + Header Files + @@ -144,11 +162,18 @@ + + + + Resource Files + + Resource Files + diff --git a/ScuffedMinecraft/assets/shaders/block_outline_frag.glsl b/ScuffedMinecraft/assets/shaders/block_outline_frag.glsl new file mode 100644 index 0000000..98fc3a8 --- /dev/null +++ b/ScuffedMinecraft/assets/shaders/block_outline_frag.glsl @@ -0,0 +1,10 @@ +#version 330 core + +out vec4 FragColor; + +uniform float time; + +void main() +{ + FragColor = vec4(0.0, 0.0, 0.0, 1.0); +} \ No newline at end of file diff --git a/ScuffedMinecraft/assets/shaders/block_outline_vert.glsl b/ScuffedMinecraft/assets/shaders/block_outline_vert.glsl new file mode 100644 index 0000000..bdc6af2 --- /dev/null +++ b/ScuffedMinecraft/assets/shaders/block_outline_vert.glsl @@ -0,0 +1,12 @@ +#version 330 core + +layout (location = 0) in vec3 aPos; + +uniform vec3 model; +uniform mat4 view; +uniform mat4 projection; + +void main() +{ + gl_Position = projection * view * vec4(aPos + model, 1.0); +} \ No newline at end of file diff --git a/ScuffedMinecraft/assets/shaders/crosshair_frag.glsl b/ScuffedMinecraft/assets/shaders/crosshair_frag.glsl new file mode 100644 index 0000000..1e0de06 --- /dev/null +++ b/ScuffedMinecraft/assets/shaders/crosshair_frag.glsl @@ -0,0 +1,18 @@ +#version 330 core + +in vec2 TexCoord; + +out vec4 FragColor; + +uniform sampler2D tex; + +void main() +{ + vec4 texResult = texture(tex, TexCoord); + + if (texResult.a == 0) + discard; + + texResult.a = 0.8; + FragColor = texResult; +} \ No newline at end of file diff --git a/ScuffedMinecraft/assets/shaders/crosshair_vert.glsl b/ScuffedMinecraft/assets/shaders/crosshair_vert.glsl new file mode 100644 index 0000000..0f479d5 --- /dev/null +++ b/ScuffedMinecraft/assets/shaders/crosshair_vert.glsl @@ -0,0 +1,14 @@ +#version 330 core + +layout (location = 0) in vec2 inPos; +layout (location = 1) in vec2 inTexCoords; + +uniform mat4 projection; + +out vec2 TexCoord; + +void main() +{ + gl_Position = projection * vec4(inPos, 0.0, 1.0); + TexCoord = inTexCoords; +} \ No newline at end of file diff --git a/ScuffedMinecraft/assets/sprites/block_map.png b/ScuffedMinecraft/assets/sprites/block_map.png index 79efa496bf9579766c12096331c9eb13bf66b3b9..38e8f0d20c79289a943933c6b1387ff4c35c9067 100644 GIT binary patch literal 9354 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 literal 8941 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 diff --git a/ScuffedMinecraft/assets/sprites/crosshair.png b/ScuffedMinecraft/assets/sprites/crosshair.png new file mode 100644 index 0000000000000000000000000000000000000000..41f5523e69aa3598ec2f67765d1476812b6dfb05 GIT binary patch literal 246 zcmeAS@N?(olHy`uVBq!ia0vp^AT}2V8<6ZZI=>f4F%}28J29*~C-V}>nda%@7@`r} z+ONpPrYO+5RnoSpRzK-}hDnv7{)UShIfh(f2Qvh9n$FIj`Q^Rd8#{@VXSHX3XzV;` zx!p_CCENG^T5slKg)$sc$@>o8vkvsy+idx~bl!7e9>q0L+Kov)iG}AjvU4gHxFs_4 zJdm03!AtYx9AU=>hkk?Pp47E}{od}4*Ib&vVQX9cy=`x8OM3*(-~Fy*tbZ{j#617i k&1XOt@})+2ruq6ZXaU(AKnwzxf+vG0Pgg&ebxsLQ0EV?!f&c&j literal 0 HcmV?d00001 diff --git a/ScuffedMinecraft/src/Application.cpp b/ScuffedMinecraft/src/Application.cpp index 18f01d1..811ec4b 100644 --- a/ScuffedMinecraft/src/Application.cpp +++ b/ScuffedMinecraft/src/Application.cpp @@ -24,11 +24,13 @@ #include "Camera.h" #include "Planet.h" #include "Blocks.h" +#include "Physics.h" void framebufferSizeCallback(GLFWwindow* window, int width, int height); void processInput(GLFWwindow* window); void mouse_callback(GLFWwindow* window, double xpos, double ypos); void scroll_callback(GLFWwindow* window, double xoffset, double yoffset); +void mouse_button_callback(GLFWwindow* window, int button, int action, int mods); float deltaTime = 0.0f; float lastFrame = 0.0f; @@ -42,18 +44,22 @@ bool firstMouse = true; bool menuMode = false; bool escapeDown = false; +bool f1Down = false; +// Window settings float windowX = 1920; float windowY = 1080; +bool vsync = true; + +uint16_t selectedBlock = 1; + +bool uiEnabled = true; Camera camera; GLuint framebufferTexture; GLuint depthTexture; -// Window options -#define VSYNC 1 // 0 for off, 1 for on - float rectangleVertices[] = { // Coords // TexCoords @@ -66,39 +72,66 @@ float rectangleVertices[] = -1.0f, 1.0f, 0.0f, 1.0f }; -int main (int argc, char *argv[]) { +float outlineVertices[] = +{ + -.001f, -.001f, -.001f, 1.001f, -.001f, -.001f, + 1.001f, -.001f, -.001f, 1.001f, 1.001f, -.001f, + 1.001f, 1.001f, -.001f, -.001f, 1.001f, -.001f, + -.001f, 1.001f, -.001f, -.001f, -.001f, -.001f, + + -.001f, -.001f, -.001f, -.001f, -.001f, 1.001f, + -.001f, -.001f, 1.001f, -.001f, 1.001f, 1.001f, + -.001f, 1.001f, 1.001f, -.001f, 1.001f, -.001f, + + 1.001f, -.001f, -.001f, 1.001f, -.001f, 1.001f, + 1.001f, -.001f, 1.001f, 1.001f, 1.001f, 1.001f, + 1.001f, 1.001f, 1.001f, 1.001f, 1.001f, -.001f, + + -.001f, -.001f, 1.001f, 1.001f, -.001f, 1.001f, + -.001f, 1.001f, 1.001f, 1.001f, 1.001f, 1.001f, +}; + +float crosshairVertices[] = +{ + windowX / 2 - 13.5, windowY / 2 - 13.5, 0.0f, 0.0f, + windowX / 2 + 13.5, windowY / 2 - 13.5, 1.0f, 0.0f, + windowX / 2 + 13.5, windowY / 2 + 13.5, 1.0f, 1.0f, + + windowX / 2 - 13.5, windowY / 2 - 13.5, 0.0f, 0.0f, + windowX / 2 - 13.5, windowY / 2 + 13.5, 0.0f, 1.0f, + windowX / 2 + 13.5, windowY / 2 + 13.5, 1.0f, 1.0f, +}; + +int main(int argc, char *argv[]) +{ #ifdef LINUX - char* resolved_path = realpath(argv[0],NULL); - if (resolved_path == NULL) { - printf("%s: Please do not place binary in PATH\n", argv[0]); - exit(1); - } - size_t resolved_length = strlen(resolved_path); + char* resolved_path = realpath(argv[0], NULL); + if (resolved_path == NULL) { + printf("%s: Please do not place binary in PATH\n", argv[0]); + exit(1); + } + size_t resolved_length = strlen(resolved_path); + // remove executable from path + for (size_t i = resolved_length; i > 0; i--) { + if (resolved_path[i] == '/' && resolved_path[i + 1] != 0) { + resolved_path[i + 1] = 0; + resolved_length = i; + break; + } + } + char* assets_path = (char*)malloc(resolved_length + strlen("assets") + 2); + strcpy(assets_path, resolved_path); + strcpy(assets_path + resolved_length + 1, "assets"); + struct stat path_stat; + if (stat(assets_path, &path_stat) == -1 || !S_ISDIR(path_stat.st_mode)) { + printf("%s: Asset directory not found\n", argv[0]); + exit(1); + } + free(assets_path); - // remove executable from path - for (size_t i = resolved_length; i > 0; i--) { - if (resolved_path[i] == '/' && resolved_path[i+1] != 0) { - resolved_path[i+1] = 0; - resolved_length = i; - break; - } - } - - char* assets_path = (char *)malloc(resolved_length + strlen("assets") + 2); - strcpy(assets_path, resolved_path); - strcpy(assets_path + resolved_length + 1, "assets"); - struct stat path_stat; - if(stat(assets_path, &path_stat) == -1 || !S_ISDIR(path_stat.st_mode)) { - printf("%s: Asset directory not found\n", argv[0]); - exit(1); - } - - free(assets_path); - - chdir(resolved_path); - free(resolved_path); + chdir(resolved_path); + free(resolved_path); #endif - // Initialize GLFW glfwInit(); glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); @@ -114,7 +147,7 @@ int main (int argc, char *argv[]) { return -1; } glfwMakeContextCurrent(window); - glfwSwapInterval(VSYNC); + glfwSwapInterval(vsync ? 1 : 0); // Initialize GLAD if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)) @@ -128,6 +161,7 @@ int main (int argc, char *argv[]) { glfwSetFramebufferSizeCallback(window, framebufferSizeCallback); glfwSetCursorPosCallback(window, mouse_callback); glfwSetScrollCallback(window, scroll_callback); + glfwSetMouseButtonCallback(window, mouse_button_callback); glClearColor(0.6f, 0.8f, 1.0f, 1.0f); @@ -156,6 +190,10 @@ int main (int argc, char *argv[]) { Shader framebufferShader("assets/shaders/framebuffer_vert.glsl", "assets/shaders/framebuffer_frag.glsl"); + Shader outlineShader("assets/shaders/block_outline_vert.glsl", "assets/shaders/block_outline_frag.glsl"); + + Shader crosshairShader("assets/shaders/crosshair_vert.glsl", "assets/shaders/crosshair_frag.glsl"); + // Create post-processing framebuffer unsigned int FBO; glGenFramebuffers(1, &FBO); @@ -198,6 +236,26 @@ int main (int argc, char *argv[]) { glUniform1i(glGetUniformLocation(framebufferShader.ID, "screenTexture"), 0); glUniform1i(glGetUniformLocation(framebufferShader.ID, "depthTexture"), 1); + unsigned int outlineVAO, outlineVBO; + glGenVertexArrays(1, &outlineVAO); + glGenBuffers(1, &outlineVBO); + glBindVertexArray(outlineVAO); + glBindBuffer(GL_ARRAY_BUFFER, outlineVBO); + glBufferData(GL_ARRAY_BUFFER, sizeof(outlineVertices), &outlineVertices, GL_STATIC_DRAW); + glEnableVertexAttribArray(0); + glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0); + + unsigned int crosshairVAO, crosshairVBO; + glGenVertexArrays(1, &crosshairVAO); + glGenBuffers(1, &crosshairVBO); + glBindVertexArray(crosshairVAO); + glBindBuffer(GL_ARRAY_BUFFER, crosshairVBO); + glBufferData(GL_ARRAY_BUFFER, sizeof(crosshairVertices), &crosshairVertices, GL_STATIC_DRAW); + glEnableVertexAttribArray(0); + glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (void*)0); + glEnableVertexAttribArray(1); + glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (void*)(2 * sizeof(float))); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); // Create terrain texture @@ -227,6 +285,30 @@ int main (int argc, char *argv[]) { stbi_image_free(data); + // Create crosshair texture + unsigned int crosshairTexture; + glGenTextures(1, &crosshairTexture); + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, crosshairTexture); + + // Set texture parameters + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + + // Load Crosshair Texture + unsigned char* data2 = stbi_load("assets/sprites/crosshair.png", &width, &height, &nrChannels, 0); + if (data2) + { + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data2); + glGenerateMipmap(GL_TEXTURE_2D); + } + else + { + std::cout << "Failed to load texture\n"; + } + + stbi_image_free(data2); + // Create camera camera = Camera(glm::vec3(0.0f, 25.0f, 0.0f)); @@ -234,6 +316,8 @@ int main (int argc, char *argv[]) { Planet::planet = new Planet(&shader, &waterShader, &billboardShader); + glm::mat4 ortho = glm::ortho(0.0f, (float)windowX, (float)windowY, 0.0f, 0.0f, 10.0f); + // Initialize ImGui IMGUI_CHECKVERSION(); ImGui::CreateContext(); @@ -270,6 +354,8 @@ int main (int argc, char *argv[]) { waterShader.use(); waterShader.setFloat("time", currentFrame); + outlineShader.use(); + outlineShader.setFloat("time", currentFrame); // Input processInput(window); @@ -311,24 +397,60 @@ int main (int argc, char *argv[]) { projectionLoc = glGetUniformLocation(billboardShader.ID, "projection"); glUniformMatrix4fv(projectionLoc, 1, GL_FALSE, glm::value_ptr(projection)); - Planet::planet->Update(camera.Position.x, camera.Position.y, camera.Position.z); + outlineShader.use(); + viewLoc = glGetUniformLocation(outlineShader.ID, "view"); + glUniformMatrix4fv(viewLoc, 1, GL_FALSE, glm::value_ptr(view)); + projectionLoc = glGetUniformLocation(outlineShader.ID, "projection"); + glUniformMatrix4fv(projectionLoc, 1, GL_FALSE, glm::value_ptr(projection)); + + Planet::planet->Update(camera.Position); + + // -- Render block outline -- // + if (uiEnabled) + { + // Get block position + auto result = Physics::Raycast(camera.Position, camera.Front, 5); + if (result.hit) + { + outlineShader.use(); + + // Set outline view to position + unsigned int modelLoc = glGetUniformLocation(outlineShader.ID, "model"); + glUniform3f(modelLoc, result.blockX, result.blockY, result.blockZ); + + // Render + glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); + //glEnable(GL_COLOR_LOGIC_OP); + glLogicOp(GL_INVERT); + glDisable(GL_CULL_FACE); + glBindVertexArray(outlineVAO); + glLineWidth(2.0); + glDrawArrays(GL_LINES, 0, 24); + + glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); + glEnable(GL_CULL_FACE); + //glDisable(GL_COLOR_LOGIC_OP); + } + } framebufferShader.use(); + // -- Post Processing Stuff -- // + // Check if player is underwater int blockX = camera.Position.x < 0 ? camera.Position.x - 1 : camera.Position.x; int blockY = camera.Position.y < 0 ? camera.Position.y - 1 : camera.Position.y; int blockZ = camera.Position.z < 0 ? camera.Position.z - 1 : camera.Position.z; - int chunkX = blockX < 0 ? floorf(blockX / (float)Planet::chunkSize) : blockX / (int)Planet::chunkSize; - int chunkY = blockY < 0 ? floorf(blockY / (float)Planet::chunkSize) : blockY / (int)Planet::chunkSize; - int chunkZ = blockZ < 0 ? floorf(blockZ / (float)Planet::chunkSize) : blockZ / (int)Planet::chunkSize; + int chunkX = blockX < 0 ? floorf(blockX / (float)CHUNK_SIZE) : blockX / (int)CHUNK_SIZE; + int chunkY = blockY < 0 ? floorf(blockY / (float)CHUNK_SIZE) : blockY / (int)CHUNK_SIZE; + int chunkZ = blockZ < 0 ? floorf(blockZ / (float)CHUNK_SIZE) : blockZ / (int)CHUNK_SIZE; - int localBlockX = blockX - (chunkX * Planet::chunkSize); - int localBlockY = blockY - (chunkY * Planet::chunkSize); - int localBlockZ = blockZ - (chunkZ * Planet::chunkSize); + int localBlockX = blockX - (chunkX * CHUNK_SIZE); + int localBlockY = blockY - (chunkY * CHUNK_SIZE); + int localBlockZ = blockZ - (chunkZ * CHUNK_SIZE); - Chunk* chunk = Planet::planet->GetChunk(chunkX, chunkY, chunkZ); + Chunk* chunk = Planet::planet->GetChunk(ChunkPos(chunkX, chunkY, chunkZ)); if (chunk != nullptr) { unsigned int blockType = chunk->GetBlockAtPos( @@ -346,6 +468,7 @@ int main (int argc, char *argv[]) { } } + // Post Processing glBindFramebuffer(GL_FRAMEBUFFER, 0); glBindVertexArray(rectVAO); glDisable(GL_DEPTH_TEST); @@ -355,15 +478,50 @@ int main (int argc, char *argv[]) { glBindTexture(GL_TEXTURE_2D, depthTexture); glDrawArrays(GL_TRIANGLES, 0, 6); - // Draw ImGui UI - ImGui::Begin("Test"); - ImGui::Text("FPS: %f (Avg: %f, Min: %f, Max: %f)", fps, avgFps, lowestFps, highestFps); - ImGui::Text("MS: %f", deltaTime * 100.0f); - ImGui::Text("Chunks: %d (%d rendered)", Planet::planet->numChunks, Planet::planet->numChunksRendered); - ImGui::End(); + if (uiEnabled) + { + // -- Render Crosshair -- // - ImGui::Render(); - ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData()); + // Render + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, crosshairTexture); + + crosshairShader.use(); + glDisable(GL_CULL_FACE); + glDisable(GL_DEPTH_TEST); + glEnable(GL_BLEND); + glEnable(GL_COLOR_LOGIC_OP); + + unsigned int crosshairProjLoc = glGetUniformLocation(crosshairShader.ID, "projection"); + glUniformMatrix4fv(crosshairProjLoc, 1, GL_FALSE, glm::value_ptr(ortho)); + glBindVertexArray(crosshairVAO); + glDrawArrays(GL_TRIANGLES, 0, 6); + + glEnable(GL_CULL_FACE); + glEnable(GL_DEPTH_TEST); + glDisable(GL_BLEND); + glDisable(GL_COLOR_LOGIC_OP); + + // Draw ImGui UI + ImGui::Begin("Test"); + ImGui::Text("FPS: %d (Avg: %d, Min: %d, Max: %d)", (int)fps, (int)avgFps, (int)lowestFps, (int)highestFps); + ImGui::Text("MS: %f", deltaTime * 100.0f); + if (ImGui::Checkbox("VSYNC", &vsync)) + glfwSwapInterval(vsync ? 1 : 0); + ImGui::Text("Chunks: %d (%d rendered)", Planet::planet->numChunks, Planet::planet->numChunksRendered); + ImGui::Text("Position: x: %f, y: %f, z: %f", camera.Position.x, camera.Position.y, camera.Position.z); + ImGui::Text("Direction: x: %f, y: %f, z: %f", camera.Front.x, camera.Front.y, camera.Front.z); + ImGui::Text("Selected Block: %s", Blocks::blocks[selectedBlock].blockName); + if (ImGui::SliderInt("Render Distance", &Planet::planet->renderDistance, 0, 30)) + Planet::planet->ClearChunkQueue(); + if (ImGui::SliderInt("Render Height", &Planet::planet->renderHeight, 0, 10)) + Planet::planet->ClearChunkQueue(); + ImGui::Checkbox("Use absolute Y axis for camera vertical movement", &camera.absoluteVerticalMovement); + ImGui::End(); + + ImGui::Render(); + ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData()); + } // Check and call events and swap buffers glfwSwapBuffers(window); @@ -372,6 +530,8 @@ int main (int argc, char *argv[]) { //std::cout << camera.Position.x << ", " << camera.Position.y << ", " << camera.Position.z << '\n'; } + delete Planet::planet; + ImGui_ImplOpenGL3_Shutdown(); ImGui_ImplGlfw_Shutdown(); ImGui::DestroyContext(); @@ -388,7 +548,6 @@ void framebufferSizeCallback(GLFWwindow* window, int width, int height) // resize framebuffer texture glBindTexture(GL_TEXTURE_2D, framebufferTexture); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, windowX, windowY, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL); - // resize framebuffer depth texture glBindTexture(GL_TEXTURE_2D, depthTexture); glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, windowX, windowY, 0, GL_DEPTH_COMPONENT, GL_FLOAT, NULL); @@ -396,6 +555,7 @@ void framebufferSizeCallback(GLFWwindow* window, int width, int height) void processInput(GLFWwindow* window) { + // Pause if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS) { if (escapeDown) @@ -410,6 +570,18 @@ void processInput(GLFWwindow* window) else escapeDown = false; + // UI Toggle + if (glfwGetKey(window, GLFW_KEY_F1) == GLFW_PRESS) + { + if (f1Down) + return; + + f1Down = true; + uiEnabled = !uiEnabled; + } + else + f1Down = false; + if (glfwGetKey(window, GLFW_KEY_W) == GLFW_PRESS) { if (glfwGetKey(window, GLFW_KEY_SPACE) == GLFW_PRESS) @@ -429,6 +601,61 @@ void processInput(GLFWwindow* window) camera.ProcessKeyboard(DOWN, deltaTime); } +void mouse_button_callback(GLFWwindow* window, int button, int action, int mods) +{ + if (button == GLFW_MOUSE_BUTTON_LEFT && action == GLFW_PRESS) + { + auto result = Physics::Raycast(camera.Position, camera.Front, 5); + if (!result.hit) + return; + + result.chunk->UpdateBlock(result.localBlockX, result.localBlockY, result.localBlockZ, 0); + } + else if (button == GLFW_MOUSE_BUTTON_MIDDLE && action == GLFW_PRESS) + { + auto result = Physics::Raycast(camera.Position, camera.Front, 5); + if (!result.hit) + return; + + selectedBlock = result.chunk->GetBlockAtPos(result.localBlockX, result.localBlockY, result.localBlockZ); + } + else if (button == GLFW_MOUSE_BUTTON_RIGHT && action == GLFW_PRESS) + { + auto result = Physics::Raycast(camera.Position, camera.Front, 5); + if (!result.hit) + return; + + float distX = result.hitPos.x - (result.blockX + .5f); + float distY = result.hitPos.y - (result.blockY + .5f); + float distZ = result.hitPos.z - (result.blockZ + .5f); + + int blockX = result.blockX; + int blockY = result.blockY; + int blockZ = result.blockZ; + + // Choose face to place on + if (abs(distX) > abs(distY) && abs(distX) > abs(distZ)) + blockX += (distX > 0 ? 1 : -1); + else if (abs(distY) > abs(distX) && abs(distY) > abs(distZ)) + blockY += (distY > 0 ? 1 : -1); + else + blockZ += (distZ > 0 ? 1 : -1); + + int chunkX = blockX < 0 ? floorf(blockX / (float)CHUNK_SIZE) : blockX / (int)CHUNK_SIZE; + int chunkY = blockY < 0 ? floorf(blockY / (float)CHUNK_SIZE) : blockY / (int)CHUNK_SIZE; + int chunkZ = blockZ < 0 ? floorf(blockZ / (float)CHUNK_SIZE) : blockZ / (int)CHUNK_SIZE; + + int localBlockX = blockX - (chunkX * CHUNK_SIZE); + int localBlockY = blockY - (chunkY * CHUNK_SIZE); + int localBlockZ = blockZ - (chunkZ * CHUNK_SIZE); + + Chunk* chunk = Planet::planet->GetChunk(ChunkPos(chunkX, chunkY, chunkZ)); + uint16_t blockToReplace = chunk->GetBlockAtPos(localBlockX, localBlockY, localBlockZ); + if (chunk != nullptr && (blockToReplace == 0 || Blocks::blocks[blockToReplace].blockType == Block::LIQUID)) + chunk->UpdateBlock(localBlockX, localBlockY, localBlockZ, selectedBlock); + } +} + void mouse_callback(GLFWwindow* window, double xpos, double ypos) { if (menuMode) @@ -452,4 +679,4 @@ void mouse_callback(GLFWwindow* window, double xpos, double ypos) void scroll_callback(GLFWwindow* window, double xoffset, double yoffset) { camera.ProcessMouseScroll(yoffset); -} +} \ No newline at end of file diff --git a/ScuffedMinecraft/src/Block.cpp b/ScuffedMinecraft/src/Block.cpp index 7ab0153..c127080 100644 --- a/ScuffedMinecraft/src/Block.cpp +++ b/ScuffedMinecraft/src/Block.cpp @@ -1,7 +1,7 @@ #include "Block.h" -Block::Block(char minX, char minY, char maxX, char maxY, BLOCK_TYPE blockType) - : blockType(blockType) +Block::Block(char minX, char minY, char maxX, char maxY, BLOCK_TYPE blockType, std::string blockName) + : blockType(blockType), blockName(blockName) { topMinX = minX; topMinY = minY; @@ -21,8 +21,8 @@ Block::Block(char minX, char minY, char maxX, char maxY, BLOCK_TYPE blockType) Block::Block(char topMinX, char topMinY, char topMaxX, char topMaxY, char bottomMinX, char bottomMinY, char bottomMaxX, char bottomMaxY, - char sideMinX, char sideMinY, char sideMaxX, char sideMaxY, BLOCK_TYPE blockType) - : blockType(blockType) + char sideMinX, char sideMinY, char sideMaxX, char sideMaxY, BLOCK_TYPE blockType, std::string blockName) + : blockType(blockType), blockName(blockName) { this->topMinX = topMinX; this->topMinY = topMinY; diff --git a/ScuffedMinecraft/src/Block.h b/ScuffedMinecraft/src/Block.h index e828c4a..8df22f8 100644 --- a/ScuffedMinecraft/src/Block.h +++ b/ScuffedMinecraft/src/Block.h @@ -1,5 +1,7 @@ #pragma once +#include + struct Block { public: @@ -16,10 +18,11 @@ public: char bottomMinX, bottomMinY, bottomMaxX, bottomMaxY; char sideMinX, sideMinY, sideMaxX, sideMaxY; BLOCK_TYPE blockType; + std::string blockName; - Block(char minX, char minY, char maxX, char maxY, BLOCK_TYPE blockType); + Block(char minX, char minY, char maxX, char maxY, BLOCK_TYPE blockType, std::string blockName); Block(char topMinX, char topMinY, char topMaxX, char topMaxY, char bottomMinX, char bottomMinY, char bottomMaxX, char bottomMaxY, - char sideMinX, char sideMinY, char sideMaxX, char sideMaxY, BLOCK_TYPE blockType); + char sideMinX, char sideMinY, char sideMaxX, char sideMaxY, BLOCK_TYPE blockType, std::string blockName); }; \ No newline at end of file diff --git a/ScuffedMinecraft/src/Blocks.h b/ScuffedMinecraft/src/Blocks.h index ae0a4eb..e3a71c3 100644 --- a/ScuffedMinecraft/src/Blocks.h +++ b/ScuffedMinecraft/src/Blocks.h @@ -1,34 +1,36 @@ #pragma once #include +#include #include "Block.h" namespace Blocks { const std::vector blocks{ - Block(0, 0, 0, 0, Block::TRANSPARENT), // Air block - Block(0, 0, 1, 1, Block::SOLID), // Dirt block + Block(0, 0, 0, 0, Block::TRANSPARENT, "Air"), // Air block + Block(0, 0, 1, 1, Block::SOLID, "Dirt"), // Dirt block - Block(1, 1, 2, 2, // Grass block + Block(1, 1, 2, 2, // Grass block 0, 0, 1, 1, - 1, 0, 2, 1, Block::SOLID), + 1, 0, 2, 1, Block::SOLID, "Grass Block"), - Block(0, 1, 1, 2, Block::SOLID), // Stone block + Block(0, 1, 1, 2, Block::SOLID, "Stone"), // Stone block - Block(2, 1, 3, 2, // Log + Block(2, 1, 3, 2, // Log 2, 1, 3, 2, - 2, 0, 3, 1, Block::SOLID), + 2, 0, 3, 1, Block::SOLID, "Log"), - Block(0, 2, 1, 3, Block::LEAVES), // Leaves - Block(1, 2, 2, 3, Block::BILLBOARD), // Grass - Block(3, 0, 4, 1, Block::BILLBOARD), // Tall Grass Bottom - Block(3, 1, 4, 2, Block::BILLBOARD), // Tall Grass Top - Block(0, 3, 1, 4, Block::BILLBOARD), // Poppy - Block(2, 2, 3, 3, Block::BILLBOARD), // White Tulip - Block(3, 2, 4, 3, Block::BILLBOARD), // Pink Tulip - Block(1, 3, 2, 4, Block::BILLBOARD), // Orange Tulip - Block(0, 4, 1, 5, Block::LIQUID) // Water + Block(0, 2, 1, 3, Block::LEAVES, "Leaves"), // Leaves + Block(1, 2, 2, 3, Block::BILLBOARD, "Grass"), // Grass + Block(3, 0, 4, 1, Block::BILLBOARD, "Tall Grass Bot"), // Tall Grass Bottom + Block(3, 1, 4, 2, Block::BILLBOARD, "Tall Grass Top"), // Tall Grass Top + Block(0, 3, 1, 4, Block::BILLBOARD, "Poppy"), // Poppy + Block(2, 2, 3, 3, Block::BILLBOARD, "White Tulip"), // White Tulip + Block(3, 2, 4, 3, Block::BILLBOARD, "Pink Tulip"), // Pink Tulip + Block(1, 3, 2, 4, Block::BILLBOARD, "Orange Tulip"), // Orange Tulip + Block(0, 4, 1, 5, Block::LIQUID, "Water"), // Water + Block(4, 0, 5, 1, Block::SOLID, "Sand"), // Sand }; enum BLOCKS @@ -46,6 +48,7 @@ namespace Blocks WHITE_TULIP = 10, PINK_TULIP = 11, ORANGE_TULIP = 12, - WATER = 13 + WATER = 13, + SAND = 14, }; } \ No newline at end of file diff --git a/ScuffedMinecraft/src/Camera.cpp b/ScuffedMinecraft/src/Camera.cpp index 621906a..fd9e3b5 100644 --- a/ScuffedMinecraft/src/Camera.cpp +++ b/ScuffedMinecraft/src/Camera.cpp @@ -38,9 +38,9 @@ void Camera::ProcessKeyboard(Camera_Movement direction, float deltaTime) if (direction == RIGHT) Position += Right * velocity; if (direction == UP) - Position.y += 1 * velocity; + Position += (absoluteVerticalMovement ? glm::vec3(0, 1, 0) : Up) * velocity; if (direction == DOWN) - Position.y -= 1 * velocity; + Position -= (absoluteVerticalMovement ? glm::vec3(0, 1, 0) : Up) * velocity; if (direction == FORWARD_NO_Y) { glm::vec3 moveDir = Front; diff --git a/ScuffedMinecraft/src/Camera.h b/ScuffedMinecraft/src/Camera.h index 2fa7448..d07e5f3 100644 --- a/ScuffedMinecraft/src/Camera.h +++ b/ScuffedMinecraft/src/Camera.h @@ -36,6 +36,7 @@ public: float MovementSpeed; float MouseSensitivity; float Zoom; + bool absoluteVerticalMovement = true; Camera(glm::vec3 position = glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3 up = glm::vec3(0.0f, 1.0f, 0.0f), float yaw = YAW, float pitch = PITCH); diff --git a/ScuffedMinecraft/src/Chunk.cpp b/ScuffedMinecraft/src/Chunk.cpp index 4b7e95a..f40bfe5 100644 --- a/ScuffedMinecraft/src/Chunk.cpp +++ b/ScuffedMinecraft/src/Chunk.cpp @@ -10,14 +10,13 @@ #include "Blocks.h" #include "WorldGen.h" -Chunk::Chunk(unsigned int chunkSize, glm::vec3 chunkPos, Shader* shader, Shader* waterShader) - : chunkSize(chunkSize), chunkPos(chunkPos) +Chunk::Chunk(ChunkPos chunkPos, Shader* shader, Shader* waterShader) + : chunkPos(chunkPos) { - worldPos = glm::vec3(chunkPos.x * chunkSize, chunkPos.y * chunkSize, chunkPos.z * chunkSize); + worldPos = glm::vec3(chunkPos.x * (float)CHUNK_SIZE, chunkPos.y * (float)CHUNK_SIZE, chunkPos.z * (float)CHUNK_SIZE); ready = false; generated = false; - chunkThread = std::thread(&Chunk::GenerateChunk, this); } Chunk::~Chunk() @@ -38,47 +37,41 @@ Chunk::~Chunk() glDeleteVertexArrays(1, &billboardVAO); } -void Chunk::GenerateChunk() +void Chunk::GenerateChunkMesh() { - //std::cout << "Started thread: " << std::this_thread::get_id() << '\n'; - - WorldGen::GenerateChunkData(chunkPos.x, chunkPos.y, chunkPos.z, chunkSize, &chunkData); - std::vector northData, southData, eastData, westData, upData, downData; - - WorldGen::GenerateChunkData(chunkPos.x, chunkPos.y, chunkPos.z - 1, chunkSize, &northData); - WorldGen::GenerateChunkData(chunkPos.x, chunkPos.y, chunkPos.z + 1, chunkSize, &southData); - WorldGen::GenerateChunkData(chunkPos.x + 1, chunkPos.y, chunkPos.z, chunkSize, &eastData); - WorldGen::GenerateChunkData(chunkPos.x - 1, chunkPos.y, chunkPos.z, chunkSize, &westData); - WorldGen::GenerateChunkData(chunkPos.x, chunkPos.y + 1, chunkPos.z, chunkSize, &upData); - WorldGen::GenerateChunkData(chunkPos.x, chunkPos.y - 1, chunkPos.z, chunkSize, &downData); - - //std::cout << "Got chunk data in thread: " << std::this_thread::get_id() << '\n'; + mainVertices.clear(); + mainIndices.clear(); + waterVertices.clear(); + waterIndices.clear(); + billboardVertices.clear(); + billboardIndices.clear(); + numTrianglesMain = 0; + numTrianglesWater = 0; + numTrianglesBillboard = 0; unsigned int currentVertex = 0; unsigned int currentLiquidVertex = 0; unsigned int currentBillboardVertex = 0; - for (char x = 0; x < chunkSize; x++) + for (char x = 0; x < CHUNK_SIZE; x++) { - for (char z = 0; z < chunkSize; z++) + for (char z = 0; z < CHUNK_SIZE; z++) { - for (char y = 0; y < chunkSize; y++) + for (char y = 0; y < CHUNK_SIZE; y++) { - int index = x * chunkSize * chunkSize + z * chunkSize + y; - if (chunkData[index] == 0) + if (chunkData->GetBlock(x, y, z) == 0) continue; - const Block* block = &Blocks::blocks[chunkData[index]]; + const Block* block = &Blocks::blocks[chunkData->GetBlock(x, y, z)]; int topBlock; - if (y < chunkSize - 1) + if (y < CHUNK_SIZE - 1) { - int blockIndex = x * chunkSize * chunkSize + z * chunkSize + (y + 1); - topBlock = chunkData[blockIndex]; + topBlock = chunkData->GetBlock(x, y + 1, z); } else { - int blockIndex = x * chunkSize * chunkSize + z * chunkSize + 0; - topBlock = upData[blockIndex]; + int blockIndex = x * CHUNK_SIZE * CHUNK_SIZE + z * CHUNK_SIZE + 0; + topBlock = upData->GetBlock(x, 0, z); } const Block* topBlockType = &Blocks::blocks[topBlock]; @@ -119,13 +112,11 @@ void Chunk::GenerateChunk() int northBlock; if (z > 0) { - int northIndex = x * chunkSize * chunkSize + (z - 1) * chunkSize + y; - northBlock = chunkData[northIndex]; + northBlock = chunkData->GetBlock(x, y, z - 1); } else { - int northIndex = x * chunkSize * chunkSize + (chunkSize - 1) * chunkSize + y; - northBlock = northData[northIndex]; + northBlock = northData->GetBlock(x, y, CHUNK_SIZE - 1); } const Block* northBlockType = &Blocks::blocks[northBlock]; @@ -157,12 +148,12 @@ void Chunk::GenerateChunk() mainVertices.push_back(Vertex(x + 1, y + 1, z + 0, block->sideMinX, block->sideMaxY, 0)); mainVertices.push_back(Vertex(x + 0, y + 1, z + 0, block->sideMaxX, block->sideMaxY, 0)); - mianIndices.push_back(currentVertex + 0); - mianIndices.push_back(currentVertex + 3); - mianIndices.push_back(currentVertex + 1); - mianIndices.push_back(currentVertex + 0); - mianIndices.push_back(currentVertex + 2); - mianIndices.push_back(currentVertex + 3); + mainIndices.push_back(currentVertex + 0); + mainIndices.push_back(currentVertex + 3); + mainIndices.push_back(currentVertex + 1); + mainIndices.push_back(currentVertex + 0); + mainIndices.push_back(currentVertex + 2); + mainIndices.push_back(currentVertex + 3); currentVertex += 4; } } @@ -171,15 +162,13 @@ void Chunk::GenerateChunk() // South { int southBlock; - if (z < chunkSize - 1) + if (z < CHUNK_SIZE - 1) { - int southIndex = x * chunkSize * chunkSize + (z + 1) * chunkSize + y; - southBlock = chunkData[southIndex]; + southBlock = chunkData->GetBlock(x, y, z + 1); } else { - int southIndex = x * chunkSize * chunkSize + 0 * chunkSize + y; - southBlock = southData[southIndex]; + southBlock = southData->GetBlock(x, y, 0); } const Block* southBlockType = &Blocks::blocks[southBlock]; @@ -211,12 +200,12 @@ void Chunk::GenerateChunk() mainVertices.push_back(Vertex(x + 0, y + 1, z + 1, block->sideMinX, block->sideMaxY, 1)); mainVertices.push_back(Vertex(x + 1, y + 1, z + 1, block->sideMaxX, block->sideMaxY, 1)); - mianIndices.push_back(currentVertex + 0); - mianIndices.push_back(currentVertex + 3); - mianIndices.push_back(currentVertex + 1); - mianIndices.push_back(currentVertex + 0); - mianIndices.push_back(currentVertex + 2); - mianIndices.push_back(currentVertex + 3); + mainIndices.push_back(currentVertex + 0); + mainIndices.push_back(currentVertex + 3); + mainIndices.push_back(currentVertex + 1); + mainIndices.push_back(currentVertex + 0); + mainIndices.push_back(currentVertex + 2); + mainIndices.push_back(currentVertex + 3); currentVertex += 4; } } @@ -227,13 +216,11 @@ void Chunk::GenerateChunk() int westBlock; if (x > 0) { - int blockIndex = (x - 1) * chunkSize * chunkSize + z * chunkSize + y; - westBlock = chunkData[blockIndex]; + westBlock = chunkData->GetBlock(x - 1, y, z); } else { - int blockIndex = (chunkSize - 1) * chunkSize * chunkSize + z * chunkSize + y; - westBlock = westData[blockIndex]; + westBlock = westData->GetBlock(CHUNK_SIZE - 1, y, z); } const Block* westBlockType = &Blocks::blocks[westBlock]; @@ -265,12 +252,12 @@ void Chunk::GenerateChunk() mainVertices.push_back(Vertex(x + 0, y + 1, z + 0, block->sideMinX, block->sideMaxY, 2)); mainVertices.push_back(Vertex(x + 0, y + 1, z + 1, block->sideMaxX, block->sideMaxY, 2)); - mianIndices.push_back(currentVertex + 0); - mianIndices.push_back(currentVertex + 3); - mianIndices.push_back(currentVertex + 1); - mianIndices.push_back(currentVertex + 0); - mianIndices.push_back(currentVertex + 2); - mianIndices.push_back(currentVertex + 3); + mainIndices.push_back(currentVertex + 0); + mainIndices.push_back(currentVertex + 3); + mainIndices.push_back(currentVertex + 1); + mainIndices.push_back(currentVertex + 0); + mainIndices.push_back(currentVertex + 2); + mainIndices.push_back(currentVertex + 3); currentVertex += 4; } } @@ -279,15 +266,13 @@ void Chunk::GenerateChunk() // East { int eastBlock; - if (x < chunkSize - 1) + if (x < CHUNK_SIZE - 1) { - int blockIndex = (x + 1) * chunkSize * chunkSize + z * chunkSize + y; - eastBlock = chunkData[blockIndex]; + eastBlock = chunkData->GetBlock(x + 1, y, z); } else { - int blockIndex = 0 * chunkSize * chunkSize + z * chunkSize + y; - eastBlock = eastData[blockIndex]; + eastBlock = eastData->GetBlock(0, y, z); } const Block* eastBlockType = &Blocks::blocks[eastBlock]; @@ -319,12 +304,12 @@ void Chunk::GenerateChunk() mainVertices.push_back(Vertex(x + 1, y + 1, z + 1, block->sideMinX, block->sideMaxY, 3)); mainVertices.push_back(Vertex(x + 1, y + 1, z + 0, block->sideMaxX, block->sideMaxY, 3)); - mianIndices.push_back(currentVertex + 0); - mianIndices.push_back(currentVertex + 3); - mianIndices.push_back(currentVertex + 1); - mianIndices.push_back(currentVertex + 0); - mianIndices.push_back(currentVertex + 2); - mianIndices.push_back(currentVertex + 3); + mainIndices.push_back(currentVertex + 0); + mainIndices.push_back(currentVertex + 3); + mainIndices.push_back(currentVertex + 1); + mainIndices.push_back(currentVertex + 0); + mainIndices.push_back(currentVertex + 2); + mainIndices.push_back(currentVertex + 3); currentVertex += 4; } } @@ -335,13 +320,12 @@ void Chunk::GenerateChunk() int bottomBlock; if (y > 0) { - int blockIndex = x * chunkSize * chunkSize + z * chunkSize + (y - 1); - bottomBlock = chunkData[blockIndex]; + bottomBlock = chunkData->GetBlock(x, y - 1, z); } else { - int blockIndex = x * chunkSize * chunkSize + z * chunkSize + (chunkSize - 1); - bottomBlock = downData[blockIndex]; + //int blockIndex = x * chunkSize * chunkSize + z * chunkSize + (chunkSize - 1); + bottomBlock = downData->GetBlock(x, CHUNK_SIZE - 1, z); } const Block* bottomBlockType = &Blocks::blocks[bottomBlock]; @@ -373,12 +357,12 @@ void Chunk::GenerateChunk() mainVertices.push_back(Vertex(x + 1, y + 0, z + 0, block->bottomMinX, block->bottomMaxY, 4)); mainVertices.push_back(Vertex(x + 0, y + 0, z + 0, block->bottomMaxX, block->bottomMaxY, 4)); - mianIndices.push_back(currentVertex + 0); - mianIndices.push_back(currentVertex + 3); - mianIndices.push_back(currentVertex + 1); - mianIndices.push_back(currentVertex + 0); - mianIndices.push_back(currentVertex + 2); - mianIndices.push_back(currentVertex + 3); + mainIndices.push_back(currentVertex + 0); + mainIndices.push_back(currentVertex + 3); + mainIndices.push_back(currentVertex + 1); + mainIndices.push_back(currentVertex + 0); + mainIndices.push_back(currentVertex + 2); + mainIndices.push_back(currentVertex + 3); currentVertex += 4; } } @@ -427,12 +411,12 @@ void Chunk::GenerateChunk() mainVertices.push_back(Vertex(x + 0, y + 1, z + 0, block->topMinX, block->topMaxY, 5)); mainVertices.push_back(Vertex(x + 1, y + 1, z + 0, block->topMaxX, block->topMaxY, 5)); - mianIndices.push_back(currentVertex + 0); - mianIndices.push_back(currentVertex + 3); - mianIndices.push_back(currentVertex + 1); - mianIndices.push_back(currentVertex + 0); - mianIndices.push_back(currentVertex + 2); - mianIndices.push_back(currentVertex + 3); + mainIndices.push_back(currentVertex + 0); + mainIndices.push_back(currentVertex + 3); + mainIndices.push_back(currentVertex + 1); + mainIndices.push_back(currentVertex + 0); + mainIndices.push_back(currentVertex + 2); + mainIndices.push_back(currentVertex + 3); currentVertex += 4; } } @@ -455,7 +439,7 @@ void Chunk::Render(Shader* mainShader, Shader* billboardShader) if (generated) { // Solid - numTrianglesMain = mianIndices.size(); + numTrianglesMain = mainIndices.size(); glGenVertexArrays(1, &mainVAO); glGenBuffers(1, &mainVBO); @@ -467,7 +451,7 @@ void Chunk::Render(Shader* mainShader, Shader* billboardShader) glBufferData(GL_ARRAY_BUFFER, mainVertices.size() * sizeof(Vertex), mainVertices.data(), GL_STATIC_DRAW); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mainEBO); - glBufferData(GL_ELEMENT_ARRAY_BUFFER, mianIndices.size() * sizeof(unsigned int), mianIndices.data(), GL_STATIC_DRAW); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, mainIndices.size() * sizeof(unsigned int), mainIndices.data(), GL_STATIC_DRAW); glVertexAttribPointer(0, 3, GL_BYTE, GL_FALSE, sizeof(Vertex), (void*)offsetof(Vertex, posX)); glEnableVertexAttribArray(0); @@ -543,7 +527,7 @@ void Chunk::Render(Shader* mainShader, Shader* billboardShader) glDrawElements(GL_TRIANGLES, numTrianglesMain, GL_UNSIGNED_INT, 0); // Render billboard mesh - billboardShader->use(); + billboardShader->use(); modelLoc = glGetUniformLocation(billboardShader->ID, "model"); glUniformMatrix4fv(modelLoc, 1, GL_FALSE, glm::value_ptr(model)); @@ -572,11 +556,128 @@ void Chunk::RenderWater(Shader* shader) glDrawElements(GL_TRIANGLES, numTrianglesWater, GL_UNSIGNED_INT, 0); } -unsigned int Chunk::GetBlockAtPos(int x, int y, int z) +uint16_t Chunk::GetBlockAtPos(int x, int y, int z) { if (!ready) return 0; - int index = x * chunkSize * chunkSize + z * chunkSize + y; - return chunkData[index]; + return chunkData->GetBlock(x, y, z); +} + +void Chunk::UpdateBlock(int x, int y, int z, uint16_t newBlock) +{ + chunkData->SetBlock(x, y, z, newBlock); + + GenerateChunkMesh(); + + // Main + numTrianglesMain = mainIndices.size(); + + glBindVertexArray(mainVAO); + + glBindBuffer(GL_ARRAY_BUFFER, mainVBO); + glBufferData(GL_ARRAY_BUFFER, mainVertices.size() * sizeof(Vertex), mainVertices.data(), GL_STATIC_DRAW); + + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mainEBO); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, mainIndices.size() * sizeof(unsigned int), mainIndices.data(), GL_STATIC_DRAW); + + // Water + numTrianglesWater = waterIndices.size(); + + glBindVertexArray(waterVAO); + + glBindBuffer(GL_ARRAY_BUFFER, waterVBO); + glBufferData(GL_ARRAY_BUFFER, waterVertices.size() * sizeof(WaterVertex), waterVertices.data(), GL_STATIC_DRAW); + + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, waterEBO); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, waterIndices.size() * sizeof(unsigned int), waterIndices.data(), GL_STATIC_DRAW); + + // Billboard + numTrianglesBillboard = billboardIndices.size();; + + glBindVertexArray(billboardVAO); + + glBindBuffer(GL_ARRAY_BUFFER, billboardVBO); + glBufferData(GL_ARRAY_BUFFER, billboardVertices.size() * sizeof(BillboardVertex), billboardVertices.data(), GL_STATIC_DRAW); + + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, billboardEBO); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, billboardIndices.size() * sizeof(unsigned int), billboardIndices.data(), GL_STATIC_DRAW); + + + if (x == 0) + { + Chunk* westChunk = Planet::planet->GetChunk({ chunkPos.x - 1, chunkPos.y, chunkPos.z }); + if (westChunk != nullptr) + westChunk->UpdateChunk(); + } + else if (x == CHUNK_SIZE - 1) + { + Chunk* eastChunk = Planet::planet->GetChunk({ chunkPos.x + 1, chunkPos.y, chunkPos.z }); + if (eastChunk != nullptr) + eastChunk->UpdateChunk(); + } + + if (y == 0) + { + Chunk* downChunk = Planet::planet->GetChunk({ chunkPos.x, chunkPos.y - 1, chunkPos.z }); + if (downChunk != nullptr) + downChunk->UpdateChunk(); + } + else if (y == CHUNK_SIZE - 1) + { + Chunk* upChunk = Planet::planet->GetChunk({ chunkPos.x, chunkPos.y + 1, chunkPos.z }); + if (upChunk != nullptr) + upChunk->UpdateChunk(); + } + + if (z == 0) + { + Chunk* northChunk = Planet::planet->GetChunk({ chunkPos.x, chunkPos.y, chunkPos.z - 1 }); + if (northChunk != nullptr) + northChunk->UpdateChunk(); + } + else if (z == CHUNK_SIZE - 1) + { + Chunk* southChunk = Planet::planet->GetChunk({ chunkPos.x, chunkPos.y, chunkPos.z + 1 }); + if (southChunk != nullptr) + southChunk->UpdateChunk(); + } +} + +void Chunk::UpdateChunk() +{ + GenerateChunkMesh(); + + // Main + numTrianglesMain = mainIndices.size(); + + glBindVertexArray(mainVAO); + + glBindBuffer(GL_ARRAY_BUFFER, mainVBO); + glBufferData(GL_ARRAY_BUFFER, mainVertices.size() * sizeof(Vertex), mainVertices.data(), GL_STATIC_DRAW); + + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mainEBO); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, mainIndices.size() * sizeof(unsigned int), mainIndices.data(), GL_STATIC_DRAW); + + // Water + numTrianglesWater = waterIndices.size(); + + glBindVertexArray(waterVAO); + + glBindBuffer(GL_ARRAY_BUFFER, waterVBO); + glBufferData(GL_ARRAY_BUFFER, waterVertices.size() * sizeof(WaterVertex), waterVertices.data(), GL_STATIC_DRAW); + + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, waterEBO); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, waterIndices.size() * sizeof(unsigned int), waterIndices.data(), GL_STATIC_DRAW); + + // Billboard + numTrianglesBillboard = billboardIndices.size(); + + glBindVertexArray(billboardVAO); + + glBindBuffer(GL_ARRAY_BUFFER, billboardVBO); + glBufferData(GL_ARRAY_BUFFER, billboardVertices.size() * sizeof(BillboardVertex), billboardVertices.data(), GL_STATIC_DRAW); + + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, billboardEBO); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, billboardIndices.size() * sizeof(unsigned int), billboardIndices.data(), GL_STATIC_DRAW); } \ No newline at end of file diff --git a/ScuffedMinecraft/src/Chunk.h b/ScuffedMinecraft/src/Chunk.h index 1c380f2..08a4314 100644 --- a/ScuffedMinecraft/src/Chunk.h +++ b/ScuffedMinecraft/src/Chunk.h @@ -6,31 +6,40 @@ #include "Shader.h" #include "Vertex.h" +#include "ChunkPos.h" +#include "ChunkData.h" class Chunk { public: - Chunk(unsigned int chunkSize, glm::vec3 chunkPos, Shader* shader, Shader* waterShader); + Chunk(ChunkPos chunkPos, Shader* shader, Shader* waterShader); ~Chunk(); - void GenerateChunk(); + void GenerateChunkMesh(); void Render(Shader* mainShader, Shader* billboardShader); void RenderWater(Shader* shader); - unsigned int GetBlockAtPos(int x, int y, int z); + uint16_t GetBlockAtPos(int x, int y, int z); + void UpdateBlock(int x, int y, int z, uint16_t newBlock); + void UpdateChunk(); public: - std::vector chunkData; - glm::vec3 chunkPos; + ChunkData* chunkData; + ChunkData* northData; + ChunkData* southData; + ChunkData* upData; + ChunkData* downData; + ChunkData* eastData; + ChunkData* westData; + ChunkPos chunkPos; bool ready; bool generated; private: - unsigned int chunkSize; glm::vec3 worldPos; std::thread chunkThread; std::vector mainVertices; - std::vector mianIndices; + std::vector mainIndices; std::vector waterVertices; std::vector waterIndices; std::vector billboardVertices; diff --git a/ScuffedMinecraft/src/ChunkData.cpp b/ScuffedMinecraft/src/ChunkData.cpp new file mode 100644 index 0000000..30895e0 --- /dev/null +++ b/ScuffedMinecraft/src/ChunkData.cpp @@ -0,0 +1,39 @@ +#include "ChunkData.h" + +#include "Planet.h" + +ChunkData::ChunkData(uint16_t* data) + : data(data) +{ + +} + +ChunkData::~ChunkData() +{ + delete[] data; +} + +inline int ChunkData::GetIndex(int x, int y, int z) const +{ + return x * CHUNK_SIZE * CHUNK_SIZE + z * CHUNK_SIZE + y; +} + +inline int ChunkData::GetIndex(ChunkPos localBlockPos) const +{ + return localBlockPos.x * CHUNK_SIZE * CHUNK_SIZE + localBlockPos.z * CHUNK_SIZE + localBlockPos.y; +} + +uint16_t ChunkData::GetBlock(ChunkPos blockPos) +{ + return data[GetIndex(blockPos)]; +} + +uint16_t ChunkData::GetBlock(int x, int y, int z) +{ + return data[GetIndex(x, y, z)]; +} + +void ChunkData::SetBlock(int x, int y, int z, uint16_t block) +{ + data[GetIndex(x, y, z)] = block; +} \ No newline at end of file diff --git a/ScuffedMinecraft/src/ChunkData.h b/ScuffedMinecraft/src/ChunkData.h new file mode 100644 index 0000000..1dce469 --- /dev/null +++ b/ScuffedMinecraft/src/ChunkData.h @@ -0,0 +1,20 @@ +#pragma once + +#include + +#include "ChunkPos.h" + +struct ChunkData +{ + uint16_t* data; + + ChunkData(uint16_t* data); + ~ChunkData(); + + inline int GetIndex(int x, int y, int z) const; + inline int GetIndex(ChunkPos localBlockPos) const; + + uint16_t GetBlock(ChunkPos blockPos); + uint16_t GetBlock(int x, int y, int z); + void SetBlock(int x, int y, int z, uint16_t block); +}; \ No newline at end of file diff --git a/ScuffedMinecraft/src/ChunkPos.cpp b/ScuffedMinecraft/src/ChunkPos.cpp new file mode 100644 index 0000000..f2930e2 --- /dev/null +++ b/ScuffedMinecraft/src/ChunkPos.cpp @@ -0,0 +1,18 @@ +#include "ChunkPos.h" + +ChunkPos::ChunkPos() + : x(0), y(0), z(0) +{ + +} + +ChunkPos::ChunkPos(int x, int y, int z) + : x(x), y(y), z(z) +{ + +} + +bool ChunkPos::operator==(const ChunkPos& other) const +{ + return other.x == x && other.y == y && other.z == z; +} \ No newline at end of file diff --git a/ScuffedMinecraft/src/ChunkPos.h b/ScuffedMinecraft/src/ChunkPos.h new file mode 100644 index 0000000..3358463 --- /dev/null +++ b/ScuffedMinecraft/src/ChunkPos.h @@ -0,0 +1,13 @@ +#pragma once + +struct ChunkPos +{ + int x; + int y; + int z; + + ChunkPos(); + ChunkPos(int x, int y, int z); + + bool operator==(const ChunkPos& other) const; +}; \ No newline at end of file diff --git a/ScuffedMinecraft/src/ChunkPosHash.h b/ScuffedMinecraft/src/ChunkPosHash.h new file mode 100644 index 0000000..98a5852 --- /dev/null +++ b/ScuffedMinecraft/src/ChunkPosHash.h @@ -0,0 +1,15 @@ +#include "ChunkPos.h" +#include + +struct ChunkPosHash +{ + std::size_t operator()(const ChunkPos& key) const + { + std::size_t hx = std::hash()(key.x); + std::size_t hy = std::hash()(key.y); + std::size_t hz = std::hash()(key.z); + + // Combine the hashes using bitwise XOR and shifting + return hx ^ (hy << 1) ^ (hz << 2); + } +}; \ No newline at end of file diff --git a/ScuffedMinecraft/src/Physics.cpp b/ScuffedMinecraft/src/Physics.cpp new file mode 100644 index 0000000..6c020cd --- /dev/null +++ b/ScuffedMinecraft/src/Physics.cpp @@ -0,0 +1,47 @@ +#include "Physics.h" +#include +#include "Blocks.h" + +Physics::RaycastResult Physics::Raycast(const glm::vec3 startPos, const glm::vec3 direction, const float maxDistance) +{ + float currentDistance = 0; + + while (currentDistance < maxDistance) + { + currentDistance += Physics::RAY_STEP; + if (currentDistance > maxDistance) + currentDistance = maxDistance; + + // Get chunk + glm::vec3 resultPos = startPos + direction * currentDistance; + int chunkX = resultPos.x >= 0 ? resultPos.x / CHUNK_SIZE : resultPos.x / CHUNK_SIZE - 1; + int chunkY = resultPos.y >= 0 ? resultPos.y / CHUNK_SIZE : resultPos.y / CHUNK_SIZE - 1; + int chunkZ = resultPos.z >= 0 ? resultPos.z / CHUNK_SIZE : resultPos.z / CHUNK_SIZE - 1; + Chunk* chunk = Planet::planet->GetChunk(ChunkPos(chunkX, chunkY, chunkZ)); + if (chunk == nullptr) + continue; + + // Get block pos + int localBlockX = (int)floor(resultPos.x) % CHUNK_SIZE; + int localBlockZ = (int)floor(resultPos.z) % CHUNK_SIZE; + int localBlockY = (int)floor(resultPos.y) % CHUNK_SIZE; + + // Get block from chunk + unsigned int block = chunk->GetBlockAtPos(localBlockX, localBlockY, localBlockZ); + + // Get result pos + int blockX = resultPos.x >= 0 ? (int)resultPos.x : (int)resultPos.x - 1; + int blockY = resultPos.y >= 0 ? (int)resultPos.y : (int)resultPos.y - 1; + int blockZ = resultPos.z >= 0 ? (int)resultPos.z : (int)resultPos.z - 1; + + // Return true if it hit a block + if (block != 0 && Blocks::blocks[block].blockType != Block::LIQUID) + return { true, resultPos, chunk, + blockX, blockY, blockZ, + localBlockX, localBlockY, localBlockZ}; + } + + return { false, glm::vec3(0), nullptr, + 0, 0, 0, + 0, 0, 0}; +} \ No newline at end of file diff --git a/ScuffedMinecraft/src/Physics.h b/ScuffedMinecraft/src/Physics.h new file mode 100644 index 0000000..9c5e80c --- /dev/null +++ b/ScuffedMinecraft/src/Physics.h @@ -0,0 +1,30 @@ +#pragma once + +#include "glm/glm.hpp" +#include "Planet.h" + +namespace Physics +{ + struct RaycastResult + { + bool hit; + glm::vec3 hitPos; + Chunk* chunk; + int blockX; + int blockY; + int blockZ; + int localBlockX; + int localBlockY; + int localBlockZ; + + RaycastResult(bool hit, glm::vec3 hitPos, Chunk* chunk, int blockX, int blockY, int blockZ, int localBlockX, int localBlockY, int localBlockZ) + : hit(hit), hitPos(hitPos), chunk(chunk), blockX(blockX), blockY(blockY), blockZ(blockZ), localBlockX(localBlockX), localBlockY(localBlockY), localBlockZ(localBlockZ) + { + + } + }; + + RaycastResult Raycast(glm::vec3 startPos, glm::vec3 direction, float maxDistance); + + static constexpr float RAY_STEP = 0.01f; +} \ No newline at end of file diff --git a/ScuffedMinecraft/src/Planet.cpp b/ScuffedMinecraft/src/Planet.cpp index d251d90..1e40bd4 100644 --- a/ScuffedMinecraft/src/Planet.cpp +++ b/ScuffedMinecraft/src/Planet.cpp @@ -4,169 +4,69 @@ #include #include +#include "WorldGen.h" + Planet* Planet::planet = nullptr; -const unsigned int Planet::chunkSize = 32; +//static const unsigned int CHUNK_SIZE = 32; // Public Planet::Planet(Shader* solidShader, Shader* waterShader, Shader* billboardShader) : solidShader(solidShader), waterShader(waterShader), billboardShader(billboardShader) { - + chunkThread = std::thread(&Planet::ChunkThreadUpdate, this); } Planet::~Planet() { - + shouldEnd = true; + chunkThread.join(); } -std::vector Planet::GetChunkData(int chunkX, int chunkY, int chunkZ) +void Planet::Update(glm::vec3 cameraPos) { - std::tuple chunkTuple{ chunkX, chunkY, chunkZ }; - - if (chunks.find(chunkTuple) == chunks.end()) - { - return std::vector{}; - //return GenerateChunkData(chunkX, chunkY, chunkZ); - } - else - { - return std::vector{}; - //return chunks.at(chunkTuple).chunkData; - } -} - -void Planet::Update(float camX, float camY, float camZ) -{ - int camChunkX = camX < 0 ? floor(camX / chunkSize) : camX / chunkSize; - int camChunkY = camY < 0 ? floor(camY / chunkSize) : camY / chunkSize; - int camChunkZ = camZ < 0 ? floor(camZ / chunkSize) : camZ / chunkSize; - - // Check if camera moved to new chunk - if (camChunkX != lastCamX || camChunkY != lastCamY || camChunkZ != lastCamZ) - { - // Player moved chunks, start new chunk queue - lastCamX = camChunkX; - lastCamY = camChunkY; - lastCamZ = camChunkZ; - - // Current chunk - chunkQueue = {}; - if (chunks.find({ camChunkX, camChunkY, camChunkZ }) == chunks.end()) - chunkQueue.push({ camChunkX, camChunkY, camChunkZ }); - - for (int r = 0; r < renderDistance; r++) - { - // Add middle chunks - for (int y = 0; y <= renderHeight; y++) - { - chunkQueue.push({ camChunkX, camChunkY + y, camChunkZ + r }); - chunkQueue.push({ camChunkX + r, camChunkY + y, camChunkZ }); - chunkQueue.push({ camChunkX, camChunkY + y, camChunkZ - r }); - chunkQueue.push({ camChunkX - r, camChunkY + y, camChunkZ }); - - if (y > 0) - { - chunkQueue.push({ camChunkX, camChunkY - y, camChunkZ + r }); - chunkQueue.push({ camChunkX + r, camChunkY - y, camChunkZ }); - chunkQueue.push({ camChunkX, camChunkY - y, camChunkZ - r }); - chunkQueue.push({ camChunkX - r, camChunkY - y, camChunkZ }); - } - } - - // Add edges - for (int e = 1; e < r; e++) - { - for (int y = 0; y <= renderHeight; y++) - { - chunkQueue.push({ camChunkX + e, camChunkY + y, camChunkZ + r }); - chunkQueue.push({ camChunkX - e, camChunkY + y, camChunkZ + r }); - - chunkQueue.push({ camChunkX + r, camChunkY + y, camChunkZ + e }); - chunkQueue.push({ camChunkX + r, camChunkY + y, camChunkZ - e }); - - chunkQueue.push({ camChunkX + e, camChunkY + y, camChunkZ - r }); - chunkQueue.push({ camChunkX - e, camChunkY + y, camChunkZ - r }); - - chunkQueue.push({ camChunkX - r, camChunkY + y, camChunkZ + e }); - chunkQueue.push({ camChunkX - r, camChunkY + y, camChunkZ - e }); - - if (y > 0) - { - chunkQueue.push({ camChunkX + e, camChunkY - y, camChunkZ + r }); - chunkQueue.push({ camChunkX - e, camChunkY - y, camChunkZ + r }); - - chunkQueue.push({ camChunkX + r, camChunkY - y, camChunkZ + e }); - chunkQueue.push({ camChunkX + r, camChunkY - y, camChunkZ - e }); - - chunkQueue.push({ camChunkX + e, camChunkY - y, camChunkZ - r }); - chunkQueue.push({ camChunkX - e, camChunkY - y, camChunkZ - r }); - - chunkQueue.push({ camChunkX - r, camChunkY - y, camChunkZ + e }); - chunkQueue.push({ camChunkX - r, camChunkY - y, camChunkZ - e }); - } - } - } - - // Add corners - for (int y = 0; y <= renderHeight; y++) - { - chunkQueue.push({ camChunkX + r, camChunkY + y, camChunkZ + r }); - chunkQueue.push({ camChunkX + r, camChunkY + y, camChunkZ - r }); - chunkQueue.push({ camChunkX - r, camChunkY + y, camChunkZ + r }); - chunkQueue.push({ camChunkX - r, camChunkY + y, camChunkZ - r }); - - if (y > 0) - { - chunkQueue.push({ camChunkX + r, camChunkY - y, camChunkZ + r }); - chunkQueue.push({ camChunkX + r, camChunkY - y, camChunkZ - r }); - chunkQueue.push({ camChunkX - r, camChunkY - y, camChunkZ + r }); - chunkQueue.push({ camChunkX - r, camChunkY - y, camChunkZ - r }); - } - } - } - } - else if (chunksLoading == 0 && !chunkQueue.empty()) - { - // Queue is not empty. Process front item in queue - glm::vec3 next = chunkQueue.front(); - chunkQueue.pop(); - - std::tuple chunkTuple{ next.x, next.y, next.z }; - - if (chunks.find(chunkTuple) == chunks.end()) - { - chunks.try_emplace(chunkTuple, - chunkSize, next, solidShader, waterShader - ); - } - } + camChunkX = cameraPos.x < 0 ? floor(cameraPos.x / CHUNK_SIZE) : cameraPos.x / CHUNK_SIZE; + camChunkY = cameraPos.y < 0 ? floor(cameraPos.y / CHUNK_SIZE) : cameraPos.y / CHUNK_SIZE; + camChunkZ = cameraPos.z < 0 ? floor(cameraPos.z / CHUNK_SIZE) : cameraPos.z / CHUNK_SIZE; glDisable(GL_BLEND); chunksLoading = 0; numChunks = 0; numChunksRendered = 0; + chunkMutex.lock(); for (auto it = chunks.begin(); it != chunks.end(); ) { numChunks++; - if (!it->second.ready) + if (!(*it->second).ready) chunksLoading++; - int chunkX = it->second.chunkPos.x; - int chunkY = it->second.chunkPos.y; - int chunkZ = it->second.chunkPos.z; - if (it->second.ready && (abs(chunkX - camChunkX) > renderDistance || + int chunkX = (*it->second).chunkPos.x; + int chunkY = (*it->second).chunkPos.y; + int chunkZ = (*it->second).chunkPos.z; + if ((*it->second).ready && (abs(chunkX - camChunkX) > renderDistance || abs(chunkY - camChunkY) > renderDistance || abs(chunkZ - camChunkZ) > renderDistance)) { + // Out of range + // Add chunk data to delete queue + chunkDataDeleteQueue.push({ chunkX, chunkY, chunkZ }); + chunkDataDeleteQueue.push({ chunkX + 1, chunkY, chunkZ }); + chunkDataDeleteQueue.push({ chunkX - 1, chunkY, chunkZ }); + chunkDataDeleteQueue.push({ chunkX, chunkY + 1, chunkZ }); + chunkDataDeleteQueue.push({ chunkX, chunkY - 1, chunkZ }); + chunkDataDeleteQueue.push({ chunkX, chunkY, chunkZ + 1 }); + chunkDataDeleteQueue.push({ chunkX, chunkY, chunkZ - 1 }); + + // Delete chunk + delete it->second; it = chunks.erase(it); } else { numChunksRendered++; - it->second.Render(solidShader, billboardShader); + (*it->second).Render(solidShader, billboardShader); ++it; } } @@ -175,25 +75,387 @@ void Planet::Update(float camX, float camY, float camZ) waterShader->use(); for (auto it = chunks.begin(); it != chunks.end(); ) { - int chunkX = it->second.chunkPos.x; - int chunkY = it->second.chunkPos.y; - int chunkZ = it->second.chunkPos.z; + int chunkX = (*it->second).chunkPos.x; + int chunkY = (*it->second).chunkPos.y; + int chunkZ = (*it->second).chunkPos.z; - it->second.RenderWater(waterShader); + (*it->second).RenderWater(waterShader); ++it; } + + chunkMutex.unlock(); +} + +void Planet::ChunkThreadUpdate() +{ + while (!shouldEnd) + { + for (auto it = chunkData.begin(); it != chunkData.end(); ) + { + ChunkPos pos = it->first; + + if (chunks.find(pos) == chunks.end() && + chunks.find({ pos.x + 1, pos.y, pos.z }) == chunks.end() && + chunks.find({ pos.x - 1, pos.y, pos.z }) == chunks.end() && + chunks.find({ pos.x, pos.y + 1, pos.z }) == chunks.end() && + chunks.find({ pos.x, pos.y - 1, pos.z }) == chunks.end() && + chunks.find({ pos.x, pos.y, pos.z + 1 }) == chunks.end() && + chunks.find({ pos.x, pos.y, pos.z - 1 }) == chunks.end()) + { + delete chunkData.at(pos); + chunkData.erase(pos); + } + ++it; + } + + // Check if camera moved to new chunk + if (camChunkX != lastCamX || camChunkY != lastCamY || camChunkZ != lastCamZ) + { + // Player moved chunks, start new chunk queue + lastCamX = camChunkX; + lastCamY = camChunkY; + lastCamZ = camChunkZ; + + // Current chunk + chunkMutex.lock(); + chunkQueue = {}; + if (chunks.find({ camChunkX, camChunkY, camChunkZ }) == chunks.end()) + chunkQueue.push({ camChunkX, camChunkY, camChunkZ }); + + for (int r = 0; r < renderDistance; r++) + { + // Add middle chunks + for (int y = 0; y <= renderHeight; y++) + { + chunkQueue.push({ camChunkX, camChunkY + y, camChunkZ + r }); + chunkQueue.push({ camChunkX + r, camChunkY + y, camChunkZ }); + chunkQueue.push({ camChunkX, camChunkY + y, camChunkZ - r }); + chunkQueue.push({ camChunkX - r, camChunkY + y, camChunkZ }); + + if (y > 0) + { + chunkQueue.push({ camChunkX, camChunkY - y, camChunkZ + r }); + chunkQueue.push({ camChunkX + r, camChunkY - y, camChunkZ }); + chunkQueue.push({ camChunkX, camChunkY - y, camChunkZ - r }); + chunkQueue.push({ camChunkX - r, camChunkY - y, camChunkZ }); + } + } + + // Add edges + for (int e = 1; e < r; e++) + { + for (int y = 0; y <= renderHeight; y++) + { + chunkQueue.push({ camChunkX + e, camChunkY + y, camChunkZ + r }); + chunkQueue.push({ camChunkX - e, camChunkY + y, camChunkZ + r }); + + chunkQueue.push({ camChunkX + r, camChunkY + y, camChunkZ + e }); + chunkQueue.push({ camChunkX + r, camChunkY + y, camChunkZ - e }); + + chunkQueue.push({ camChunkX + e, camChunkY + y, camChunkZ - r }); + chunkQueue.push({ camChunkX - e, camChunkY + y, camChunkZ - r }); + + chunkQueue.push({ camChunkX - r, camChunkY + y, camChunkZ + e }); + chunkQueue.push({ camChunkX - r, camChunkY + y, camChunkZ - e }); + + if (y > 0) + { + chunkQueue.push({ camChunkX + e, camChunkY - y, camChunkZ + r }); + chunkQueue.push({ camChunkX - e, camChunkY - y, camChunkZ + r }); + + chunkQueue.push({ camChunkX + r, camChunkY - y, camChunkZ + e }); + chunkQueue.push({ camChunkX + r, camChunkY - y, camChunkZ - e }); + + chunkQueue.push({ camChunkX + e, camChunkY - y, camChunkZ - r }); + chunkQueue.push({ camChunkX - e, camChunkY - y, camChunkZ - r }); + + chunkQueue.push({ camChunkX - r, camChunkY - y, camChunkZ + e }); + chunkQueue.push({ camChunkX - r, camChunkY - y, camChunkZ - e }); + } + } + } + + // Add corners + for (int y = 0; y <= renderHeight; y++) + { + chunkQueue.push({ camChunkX + r, camChunkY + y, camChunkZ + r }); + chunkQueue.push({ camChunkX + r, camChunkY + y, camChunkZ - r }); + chunkQueue.push({ camChunkX - r, camChunkY + y, camChunkZ + r }); + chunkQueue.push({ camChunkX - r, camChunkY + y, camChunkZ - r }); + + if (y > 0) + { + chunkQueue.push({ camChunkX + r, camChunkY - y, camChunkZ + r }); + chunkQueue.push({ camChunkX + r, camChunkY - y, camChunkZ - r }); + chunkQueue.push({ camChunkX - r, camChunkY - y, camChunkZ + r }); + chunkQueue.push({ camChunkX - r, camChunkY - y, camChunkZ - r }); + } + } + } + + chunkMutex.unlock(); + } + else + { + chunkMutex.lock(); + if (!chunkDataQueue.empty()) + { + ChunkPos chunkPos = chunkDataQueue.front(); + + if (chunkData.find(chunkPos) != chunkData.end()) + { + chunkDataQueue.pop(); + chunkMutex.unlock(); + continue; + } + + chunkMutex.unlock(); + + uint16_t* d = new uint16_t[CHUNK_SIZE * CHUNK_SIZE * CHUNK_SIZE]; + ChunkData* data = new ChunkData(d); + + WorldGen::GenerateChunkData(chunkPos, d); + + chunkMutex.lock(); + chunkData[chunkPos] = data; + chunkDataQueue.pop(); + chunkMutex.unlock(); + } + else + { + if (!chunkQueue.empty()) + { + // Check if chunk exists + ChunkPos chunkPos = chunkQueue.front(); + if (chunks.find(chunkPos) != chunks.end()) + { + chunkQueue.pop(); + chunkMutex.unlock(); + continue; + } + + chunkMutex.unlock(); + + // Create chunk object + Chunk* chunk = new Chunk(chunkPos, solidShader, waterShader); + + // Set chunk data + { + chunkMutex.lock(); + if (chunkData.find(chunkPos) == chunkData.end()) + { + chunkMutex.unlock(); + uint16_t* d = new uint16_t[CHUNK_SIZE * CHUNK_SIZE * CHUNK_SIZE]; + + WorldGen::GenerateChunkData(chunkPos, d); + + ChunkData* data = new ChunkData(d); + + chunk->chunkData = data; + + chunkMutex.lock(); + chunkData[chunkPos] = data; + chunkMutex.unlock(); + } + else + { + chunk->chunkData = chunkData.at(chunkPos); + chunkMutex.unlock(); + } + } + + // Set top data + { + ChunkPos topPos(chunkPos.x, chunkPos.y + 1, chunkPos.z); + chunkMutex.lock(); + if (chunkData.find(topPos) == chunkData.end()) + { + chunkMutex.unlock(); + uint16_t* d = new uint16_t[CHUNK_SIZE * CHUNK_SIZE * CHUNK_SIZE]; + + WorldGen::GenerateChunkData(topPos, d); + + ChunkData* data = new ChunkData(d); + + chunk->upData = data; + + chunkMutex.lock(); + chunkData[topPos] = data; + chunkMutex.unlock(); + } + else + { + chunk->upData = chunkData.at(topPos); + chunkMutex.unlock(); + } + } + + // Set bottom data + { + ChunkPos bottomPos(chunkPos.x, chunkPos.y - 1, chunkPos.z); + chunkMutex.lock(); + if (chunkData.find(bottomPos) == chunkData.end()) + { + chunkMutex.unlock(); + uint16_t* d = new uint16_t[CHUNK_SIZE * CHUNK_SIZE * CHUNK_SIZE]; + + WorldGen::GenerateChunkData(bottomPos, d); + + ChunkData* data = new ChunkData(d); + + chunk->downData = data; + + chunkMutex.lock(); + chunkData[bottomPos] = data; + chunkMutex.unlock(); + } + else + { + chunk->downData = chunkData.at(bottomPos); + chunkMutex.unlock(); + } + } + + // Set north data + { + ChunkPos northPos(chunkPos.x, chunkPos.y, chunkPos.z - 1); + chunkMutex.lock(); + if (chunkData.find(northPos) == chunkData.end()) + { + chunkMutex.unlock(); + uint16_t* d = new uint16_t[CHUNK_SIZE * CHUNK_SIZE * CHUNK_SIZE]; + + WorldGen::GenerateChunkData(northPos, d); + + ChunkData* data = new ChunkData(d); + + chunk->northData = data; + + chunkMutex.lock(); + chunkData[northPos] = data; + chunkMutex.unlock(); + } + else + { + chunk->northData = chunkData.at(northPos); + chunkMutex.unlock(); + } + } + + // Set south data + { + ChunkPos southPos(chunkPos.x, chunkPos.y, chunkPos.z + 1); + chunkMutex.lock(); + if (chunkData.find(southPos) == chunkData.end()) + { + chunkMutex.unlock(); + uint16_t* d = new uint16_t[CHUNK_SIZE * CHUNK_SIZE * CHUNK_SIZE]; + + WorldGen::GenerateChunkData(southPos, d); + + ChunkData* data = new ChunkData(d); + + chunk->southData = data; + + chunkMutex.lock(); + chunkData[southPos] = data; + chunkMutex.unlock(); + } + else + { + chunk->southData = chunkData.at(southPos); + chunkMutex.unlock(); + } + } + + // Set east data + { + ChunkPos eastPos(chunkPos.x + 1, chunkPos.y, chunkPos.z); + chunkMutex.lock(); + if (chunkData.find(eastPos) == chunkData.end()) + { + chunkMutex.unlock(); + uint16_t* d = new uint16_t[CHUNK_SIZE * CHUNK_SIZE * CHUNK_SIZE]; + + WorldGen::GenerateChunkData(eastPos, d); + + ChunkData* data = new ChunkData(d); + + chunk->eastData = data; + + chunkMutex.lock(); + chunkData[eastPos] = data; + chunkMutex.unlock(); + } + else + { + chunk->eastData = chunkData.at(eastPos); + chunkMutex.unlock(); + } + } + + // Set west data + { + ChunkPos westPos(chunkPos.x - 1, chunkPos.y, chunkPos.z); + chunkMutex.lock(); + if (chunkData.find(westPos) == chunkData.end()) + { + chunkMutex.unlock(); + uint16_t* d = new uint16_t[CHUNK_SIZE * CHUNK_SIZE * CHUNK_SIZE]; + + WorldGen::GenerateChunkData(westPos, d); + + ChunkData* data = new ChunkData(d); + + chunk->westData = data; + + chunkMutex.lock(); + chunkData[westPos] = data; + chunkMutex.unlock(); + } + else + { + chunk->westData = chunkData.at(westPos); + chunkMutex.unlock(); + } + } + + // Generate chunk mesh + chunk->GenerateChunkMesh(); + + // Finish + chunkMutex.lock(); + chunks[chunkPos] = chunk; + chunkQueue.pop(); + chunkMutex.unlock(); + } + else + { + chunkMutex.unlock(); + + std::this_thread::sleep_for(std::chrono::milliseconds(1000)); + } + } + } + } } -Chunk* Planet::GetChunk(int chunkX, int chunkY, int chunkZ) +Chunk* Planet::GetChunk(ChunkPos chunkPos) { - std::tuple chunkTuple{ chunkX, chunkY, chunkZ }; - - if (chunks.find(chunkTuple) == chunks.end()) + chunkMutex.lock(); + if (chunks.find(chunkPos) == chunks.end()) { + chunkMutex.unlock(); return nullptr; } else { - return &chunks.at(chunkTuple); + chunkMutex.unlock(); + return chunks.at(chunkPos); } +} + +void Planet::ClearChunkQueue() +{ + lastCamX++; } \ No newline at end of file diff --git a/ScuffedMinecraft/src/Planet.h b/ScuffedMinecraft/src/Planet.h index 93daad5..7394e1b 100644 --- a/ScuffedMinecraft/src/Planet.h +++ b/ScuffedMinecraft/src/Planet.h @@ -5,9 +5,15 @@ #include #include #include +#include +#include +#include "ChunkPos.h" +#include "ChunkData.h" #include "Chunk.h" -#include "TupleHash.h" +#include "ChunkPosHash.h" + +constexpr unsigned int CHUNK_SIZE = 32; class Planet { @@ -16,26 +22,38 @@ public: Planet(Shader* solidShader, Shader* waterShader, Shader* billboardShader); ~Planet(); - std::vector GetChunkData(int chunkX, int chunkY, int chunkZ); - void Update(float camX, float camY, float camZ); + ChunkData* GetChunkData(ChunkPos chunkPos); + void Update(glm::vec3 cameraPos); - Chunk* GetChunk(int chunkX, int chunkY, int chunkZ); + Chunk* GetChunk(ChunkPos chunkPos); + void ClearChunkQueue(); + +private: + void ChunkThreadUpdate(); // Variables public: static Planet* planet; unsigned int numChunks = 0, numChunksRendered = 0; - static const unsigned int chunkSize; + int renderDistance = 10; + int renderHeight = 3; private: - std::unordered_map, Chunk> chunks; - std::queue chunkQueue; - int renderDistance = 3; - int renderHeight = 1; + std::unordered_map chunks; + std::unordered_map chunkData; + std::queue chunkQueue; + std::queue chunkDataQueue; + std::queue chunkDataDeleteQueue; unsigned int chunksLoading = 0; int lastCamX = -100, lastCamY = -100, lastCamZ = -100; + int camChunkX = -100, camChunkY = -100, camChunkZ = -100; Shader* solidShader; Shader* waterShader; Shader* billboardShader; + + std::thread chunkThread; + std::mutex chunkMutex; + + bool shouldEnd = false; }; \ No newline at end of file diff --git a/ScuffedMinecraft/src/TupleHash.h b/ScuffedMinecraft/src/TupleHash.h deleted file mode 100644 index 72fdc16..0000000 --- a/ScuffedMinecraft/src/TupleHash.h +++ /dev/null @@ -1,53 +0,0 @@ -#include -// function has to live in the std namespace -// so that it is picked up by argument-dependent name lookup (ADL). -namespace std { - namespace - { - - // Code from boost - // Reciprocal of the golden ratio helps spread entropy - // and handles duplicates. - // See Mike Seymour in magic-numbers-in-boosthash-combine: - // https://stackoverflow.com/questions/4948780 - - template - inline void hash_combine(std::size_t& seed, T const& v) - { - seed ^= hash()(v) + 0x9e3779b9 + (seed << 6) + (seed >> 2); - } - - // Recursive template code derived from Matthieu M. - template ::value - 1> - struct HashValueImpl - { - static void apply(size_t& seed, Tuple const& tuple) - { - HashValueImpl::apply(seed, tuple); - hash_combine(seed, get(tuple)); - } - }; - - template - struct HashValueImpl - { - static void apply(size_t& seed, Tuple const& tuple) - { - hash_combine(seed, get<0>(tuple)); - } - }; - } - - template - struct hash> - { - size_t - operator()(std::tuple const& tt) const - { - size_t seed = 0; - HashValueImpl >::apply(seed, tt); - return seed; - } - - }; -} \ No newline at end of file diff --git a/ScuffedMinecraft/src/WorldGen.cpp b/ScuffedMinecraft/src/WorldGen.cpp index 6d434a4..0702c55 100644 --- a/ScuffedMinecraft/src/WorldGen.cpp +++ b/ScuffedMinecraft/src/WorldGen.cpp @@ -5,9 +5,12 @@ #include #include "Blocks.h" +#include "Planet.h" -void WorldGen::GenerateChunkData(int chunkX, int chunkY, int chunkZ, int chunkSize, std::vector* chunkData) +void WorldGen::GenerateChunkData(ChunkPos chunkPos, uint16_t* chunkData) { + static int chunkSize = CHUNK_SIZE; + // Init noise static OSN::Noise<2> noise2D; static OSN::Noise<3> noise3D; @@ -15,7 +18,7 @@ void WorldGen::GenerateChunkData(int chunkX, int chunkY, int chunkZ, int chunkSi // Init noise settings static NoiseSettings surfaceSettings[]{ { 0.01f, 20.0f, 0 }, - { 0.05f, 3.0f, 0 } + { 0.05f, 3.0f, 0 } }; static int surfaceSettingsLength = sizeof(surfaceSettings) / sizeof(*surfaceSettings); @@ -284,14 +287,12 @@ void WorldGen::GenerateChunkData(int chunkX, int chunkY, int chunkZ, int chunkSi static int waterLevel = 20; - // Set vector size - chunkData->reserve(chunkSize * chunkSize * chunkSize); - // Account for chunk position - int startX = chunkX * chunkSize; - int startY = chunkY * chunkSize; - int startZ = chunkZ * chunkSize; + int startX = chunkPos.x * chunkSize; + int startY = chunkPos.y * chunkSize; + int startZ = chunkPos.z * chunkSize; + int currentIndex = 0; for (int x = 0; x < chunkSize; x++) { for (int z = 0; z < chunkSize; z++) @@ -334,12 +335,12 @@ void WorldGen::GenerateChunkData(int chunkX, int chunkY, int chunkZ, int chunkSi if (y + startY > noiseY) { if (y + startY <= waterLevel) - chunkData->push_back(Blocks::WATER); + chunkData[currentIndex] = Blocks::WATER; else - chunkData->push_back(Blocks::AIR); + chunkData[currentIndex] = Blocks::AIR; } else if (cave) - chunkData->push_back(Blocks::AIR); + chunkData[currentIndex] = Blocks::AIR; // Ground else { @@ -357,7 +358,7 @@ void WorldGen::GenerateChunkData(int chunkX, int chunkY, int chunkZ, int chunkSi if (noiseOre > oreSettings[i].chance) { - chunkData->push_back(oreSettings[i].block); + chunkData[currentIndex] = oreSettings[i].block; blockSet = true; break; } @@ -366,13 +367,21 @@ void WorldGen::GenerateChunkData(int chunkX, int chunkY, int chunkZ, int chunkSi if (!blockSet) { if (y + startY == noiseY) - chunkData->push_back(Blocks::GRASS_BLOCK); + if (noiseY > waterLevel + 1) + chunkData[currentIndex] = Blocks::GRASS_BLOCK; + else + chunkData[currentIndex] = Blocks::SAND; else if (y + startY > 10) - chunkData->push_back(Blocks::DIRT_BLOCK); + if (noiseY > waterLevel + 1) + chunkData[currentIndex] = Blocks::DIRT_BLOCK; + else + chunkData[currentIndex] = Blocks::SAND; else - chunkData->push_back(Blocks::STONE_BLOCK); + chunkData[currentIndex] = Blocks::STONE_BLOCK; } } + + currentIndex++; } } } @@ -393,11 +402,11 @@ void WorldGen::GenerateChunkData(int chunkX, int chunkY, int chunkZ, int chunkSi * surfaceSettings[s].amplitude; } - if (noiseY + surfaceFeatures[i].offsetY > startY + 32 || noiseY + surfaceFeatures[i].sizeY + surfaceFeatures[i].offsetY < startY) + if (noiseY + surfaceFeatures[i].offsetY > startY + chunkSize || noiseY + surfaceFeatures[i].sizeY + surfaceFeatures[i].offsetY < startY) continue; - // Check if it's in water - if (noiseY < waterLevel) + // Check if it's in water or on sand + if (noiseY < waterLevel + 2) continue; // Check if it's in a cave @@ -446,11 +455,11 @@ void WorldGen::GenerateChunkData(int chunkX, int chunkY, int chunkZ, int chunkSi int localZ = featureZ + fZ + surfaceFeatures[i].offsetZ - startZ; //std::cout << "FeatureZ: " << featureZ << ", fZ: " << fZ << ", startZ: " << startZ << ", localZ: " << localZ << '\n'; - if (localX >= 32 || localX < 0) + if (localX >= chunkSize || localX < 0) continue; - if (localY >= 32 || localY < 0) + if (localY >= chunkSize || localY < 0) continue; - if (localZ >= 32 || localZ < 0) + if (localZ >= chunkSize || localZ < 0) continue; int featureIndex = fY * surfaceFeatures[i].sizeX * surfaceFeatures[i].sizeZ + @@ -460,8 +469,8 @@ void WorldGen::GenerateChunkData(int chunkX, int chunkY, int chunkZ, int chunkSi int localIndex = localX * chunkSize * chunkSize + localZ * chunkSize + localY; //std::cout << "Local Index: " << localIndex << ", Max Index: " << chunkData->size() << '\n'; - if (surfaceFeatures[i].replaceBlock[featureIndex] || chunkData->at(localIndex) == 0) - chunkData->at(localIndex) = surfaceFeatures[i].blocks[featureIndex]; + if (surfaceFeatures[i].replaceBlock[featureIndex] || chunkData[localIndex] == 0) + chunkData[localIndex] = surfaceFeatures[i].blocks[featureIndex]; } } } diff --git a/ScuffedMinecraft/src/WorldGen.h b/ScuffedMinecraft/src/WorldGen.h index 5b09d1b..101ea91 100644 --- a/ScuffedMinecraft/src/WorldGen.h +++ b/ScuffedMinecraft/src/WorldGen.h @@ -3,9 +3,10 @@ #include #include "NoiseSettings.h" #include "SurfaceFeature.h" +#include "ChunkData.h" +#include "glm/glm.hpp" namespace WorldGen { - void GenerateChunkData(int chunkX, int chunkY, int chunkZ, int chunkSize, std::vector* chunkData); - + void GenerateChunkData(ChunkPos chunkPos, uint16_t* chunkData); } \ No newline at end of file