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 79efa49..38e8f0d 100644 Binary files a/ScuffedMinecraft/assets/sprites/block_map.png and b/ScuffedMinecraft/assets/sprites/block_map.png differ diff --git a/ScuffedMinecraft/assets/sprites/block_map.psd b/ScuffedMinecraft/assets/sprites/block_map.psd index 378f4e4..da11a0e 100644 Binary files a/ScuffedMinecraft/assets/sprites/block_map.psd and b/ScuffedMinecraft/assets/sprites/block_map.psd differ diff --git a/ScuffedMinecraft/assets/sprites/crosshair.png b/ScuffedMinecraft/assets/sprites/crosshair.png new file mode 100644 index 0000000..41f5523 Binary files /dev/null and b/ScuffedMinecraft/assets/sprites/crosshair.png differ 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