-
个人简介
#include "Camera.h"
Camera::Camera(float fov, float aspectRatio, float nearPlane, float farPlane) : position(0.0f, 0.0f, 3.0f), front(0.0f, 0.0f, -1.0f), up(0.0f, 1.0f, 0.0f), right(1.0f, 0.0f, 0.0f), worldUp(0.0f, 1.0f, 0.0f), yaw(-90.0f), pitch(0.0f), fov(fov), aspectRatio(aspectRatio), nearPlane(nearPlane), farPlane(farPlane) {
updateVectors(); updateMatrices();}
void Camera::update() { updateVectors(); updateMatrices(); }
void Camera::setPosition(const glm::vec3& position) { this->position = position; }
void Camera::setRotation(float yaw, float pitch) { this->yaw = yaw; this->pitch = pitch;
// 限制pitch角度,防止过度旋转 if (pitch > 89.0f) pitch = 89.0f; if (pitch < -89.0f) pitch = -89.0f; updateVectors();}
void Camera::move(const glm::vec3& offset) { position += offset; }
void Camera::rotate(float yawOffset, float pitchOffset) { yaw += yawOffset; pitch += pitchOffset;
// 限制pitch角度,防止过度旋转 if (pitch > 89.0f) pitch = 89.0f; if (pitch < -89.0f) pitch = -89.0f; updateVectors();}
void Camera::updateVectors() { // 计算新的front向量 glm::vec3 newFront; newFront.x = cos(glm::radians(yaw)) * cos(glm::radians(pitch)); newFront.y = sin(glm::radians(pitch)); newFront.z = sin(glm::radians(yaw)) * cos(glm::radians(pitch)); front = glm::normalize(newFront);
// 重新计算right和up向量 right = glm::normalize(glm::cross(front, worldUp)); up = glm::normalize(glm::cross(right, front));}
void Camera::updateMatrices() { // 计算视图矩阵 viewMatrix = glm::lookAt(position, position + front, up);
// 计算投影矩阵 projectionMatrix = glm::perspective(glm::radians(fov), aspectRatio, nearPlane, farPlane);} #pragma once #include <glm/glm.hpp> #include <glm/gtc/matrix_transform.hpp>
class Camera { public: Camera(float fov, float aspectRatio, float nearPlane, float farPlane);
void update(); void setPosition(const glm::vec3& position); void setRotation(float yaw, float pitch); void move(const glm::vec3& offset); void rotate(float yawOffset, float pitchOffset); const glm::mat4& getViewMatrix() const { return viewMatrix; } const glm::mat4& getProjectionMatrix() const { return projectionMatrix; } const glm::vec3& getPosition() const { return position; } const glm::vec3& getFront() const { return front; } const glm::vec3& getUp() const { return up; } const glm::vec3& getRight() const { return right; }private: glm::vec3 position; glm::vec3 front; glm::vec3 up; glm::vec3 right; glm::vec3 worldUp;
float yaw; float pitch; float fov; float aspectRatio; float nearPlane; float farPlane; glm::mat4 viewMatrix; glm::mat4 projectionMatrix; void updateVectors(); void updateMatrices();}; #include "Input.h" #include
Input::Input(GLFWwindow* window) : window(window), mouseX(0.0), mouseY(0.0), previousMouseX(0.0), previousMouseY(0.0), mouseDeltaX(0.0), mouseDeltaY(0.0), mouseLocked(false) {
// 设置回调函数 glfwSetKeyCallback(window, keyCallback); glfwSetMouseButtonCallback(window, mouseButtonCallback); // 存储Input实例到窗口用户指针 glfwSetWindowUserPointer(window, this); // 获取初始鼠标位置 glfwGetCursorPos(window, &mouseX, &mouseY); previousMouseX = mouseX; previousMouseY = mouseY;}
void Input::update() { // 保存上一帧的按键状态 previousKeys = currentKeys; previousMouseButtons = currentMouseButtons;
// 获取当前鼠标位置 previousMouseX = mouseX; previousMouseY = mouseY; glfwGetCursorPos(window, &mouseX, &mouseY); // 计算鼠标移动增量 mouseDeltaX = mouseX - previousMouseX; mouseDeltaY = mouseY - previousMouseY;}
bool Input::isKeyPressed(int key) const { auto it = currentKeys.find(key); auto prevIt = previousKeys.find(key);
return (it != currentKeys.end() && it->second) && (prevIt == previousKeys.end() || !prevIt->second);}
bool Input::isKeyDown(int key) const { auto it = currentKeys.find(key); return it != currentKeys.end() && it->second; }
bool Input::isKeyReleased(int key) const { auto it = currentKeys.find(key); auto prevIt = previousKeys.find(key);
return (it == currentKeys.end() || !it->second) && (prevIt != previousKeys.end() && prevIt->second);}
bool Input::isMouseButtonPressed(int button) const { auto it = currentMouseButtons.find(button); auto prevIt = previousMouseButtons.find(button);
return (it != currentMouseButtons.end() && it->second) && (prevIt == previousMouseButtons.end() || !prevIt->second);}
bool Input::isMouseButtonDown(int button) const { auto it = currentMouseButtons.find(button); return it != currentMouseButtons.end() && it->second; }
bool Input::isMouseButtonReleased(int button) const { auto it = currentMouseButtons.find(button); auto prevIt = previousMouseButtons.find(button);
return (it == currentMouseButtons.end() || !it->second) && (prevIt != previousMouseButtons.end() && prevIt->second);}
void Input::setMouseLocked(bool locked) { mouseLocked = locked; if (locked) { glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED); } else { glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_NORMAL); } }
void Input::keyCallback(GLFWwindow* window, int key, int scancode, int action, int mods) { Input* input = static_cast<Input*>(glfwGetWindowUserPointer(window)); if (input) { if (action == GLFW_PRESS) { input->currentKeys[key] = true; } else if (action == GLFW_RELEASE) { input->currentKeys[key] = false; } } }
void Input::mouseButtonCallback(GLFWwindow* window, int button, int action, int mods) { Input* input = static_cast<Input*>(glfwGetWindowUserPointer(window)); if (input) { if (action == GLFW_PRESS) { input->currentMouseButtons[button] = true; } else if (action == GLFW_RELEASE) { input->currentMouseButtons[button] = false; } } } #pragma once #include <GLFW/glfw3.h> #include <unordered_map>
class Input { public: Input(GLFWwindow* window);
void update(); bool isKeyPressed(int key) const; bool isKeyDown(int key) const; bool isKeyReleased(int key) const; bool isMouseButtonPressed(int button) const; bool isMouseButtonDown(int button) const; bool isMouseButtonReleased(int button) const; double getMouseX() const { return mouseX; } double getMouseY() const { return mouseY; } double getMouseDeltaX() const { return mouseDeltaX; } double getMouseDeltaY() const { return mouseDeltaY; } void setMouseLocked(bool locked); bool isMouseLocked() const { return mouseLocked; }private: GLFWwindow* window;
std::unordered_map<int, bool> currentKeys; std::unordered_map<int, bool> previousKeys; std::unordered_map<int, bool> currentMouseButtons; std::unordered_map<int, bool> previousMouseButtons; double mouseX; double mouseY; double previousMouseX; double previousMouseY; double mouseDeltaX; double mouseDeltaY; bool mouseLocked; static void keyCallback(GLFWwindow* window, int key, int scancode, int action, int mods); static void mouseButtonCallback(GLFWwindow* window, int button, int action, int mods);}; #include "Window.h" #include
Window::Window(const std::string& title, int width, int height) : title(title), width(width), height(height), window(nullptr) { init(); }
Window::~Window() { cleanup(); }
void Window::init() { // 初始化GLFW if (!glfwInit()) { std::cerr << "Failed to initialize GLFW" << std::endl; exit(-1); }
// 设置OpenGL版本 glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); // 创建窗口 window = glfwCreateWindow(width, height, title.c_str(), nullptr, nullptr); if (!window) { std::cerr << "Failed to create GLFW window" << std::endl; glfwTerminate(); exit(-1); } // 设置当前上下文 glfwMakeContextCurrent(window); // 初始化GLEW if (glewInit() != GLEW_OK) { std::cerr << "Failed to initialize GLEW" << std::endl; exit(-1); } // 设置视口 glViewport(0, 0, width, height); // 设置窗口大小回调 glfwSetWindowSizeCallback(window, [](GLFWwindow* window, int width, int height) { glViewport(0, 0, width, height); });}
void Window::cleanup() { glfwDestroyWindow(window); glfwTerminate(); }
bool Window::shouldClose() const { return glfwWindowShouldClose(window); }
void Window::update() { glfwSwapBuffers(window); glfwPollEvents(); }
void Window::setTitle(const std::string& title) { this->title = title; glfwSetWindowTitle(window, title.c_str()); } #pragma once #include #include <GLFW/glfw3.h>
class Window { public: Window(const std::string& title, int width, int height); ~Window();
bool shouldClose() const; void update(); void setTitle(const std::string& title); int getWidth() const { return width; } int getHeight() const { return height; } GLFWwindow* getHandle() { return window; }private: GLFWwindow* window; std::string title; int width; int height;
void init(); void cleanup();}; #include "Physics.h" #include
Physics::Physics() : gravity(0.0f, -9.81f, 0.0f), airResistance(0.05f), groundFriction(0.1f) { }
Physics::~Physics() { }
void Physics::applyGravity(glm::vec3& velocity, float deltaTime) const { velocity += gravity * deltaTime; }
bool Physics::checkCollision(const AABB& aabb, const Block& block) const { // 如果方块是空气或透明的,不进行碰撞检测 if (block.getType() == BlockType::AIR || block.isTransparent()) { return false; }
// 创建方块的碰撞箱 AABB blockAABB = createBlockAABB(glm::vec3(0.0f), block.getType()); // 检查碰撞 return aabb.intersects(blockAABB);}
bool Physics::resolveCollision(AABB& aabb, glm::vec3& velocity, const Block& block, float deltaTime) { // 如果方块是空气或透明的,不进行碰撞检测 if (block.getType() == BlockType::AIR || block.isTransparent()) { return false; }
// 创建方块的碰撞箱 AABB blockAABB = createBlockAABB(glm::vec3(0.0f), block.getType()); // 检查是否碰撞 if (!aabb.intersects(blockAABB)) { return false; } // 计算碰撞法线 glm::vec3 aabbCenter = aabb.getCenter(); glm::vec3 blockCenter = blockAABB.getCenter(); glm::vec3 delta = aabbCenter - blockCenter; // 找出最小的重叠轴 float overlapX = (aabb.getSize().x + blockAABB.getSize().x) * 0.5f - std::abs(delta.x); float overlapY = (aabb.getSize().y + blockAABB.getSize().y) * 0.5f - std::abs(delta.y); float overlapZ = (aabb.getSize().z + blockAABB.getSize().z) * 0.5f - std::abs(delta.z); if (overlapX < 0 || overlapY < 0 || overlapZ < 0) { return false; } // 找出最小的重叠 if (overlapX < overlapY && overlapX < overlapZ) { // X轴碰撞 if (delta.x > 0) { aabb.min.x = blockAABB.max.x; } else { aabb.max.x = blockAABB.min.x; } velocity.x = 0.0f; } else if (overlapY < overlapX && overlapY < overlapZ) { // Y轴碰撞 if (delta.y > 0) { aabb.min.y = blockAABB.max.y; } else { aabb.max.y = blockAABB.min.y; } velocity.y = 0.0f; } else { // Z轴碰撞 if (delta.z > 0) { aabb.min.z = blockAABB.max.z; } else { aabb.max.z = blockAABB.min.z; } velocity.z = 0.0f; } return true;}
bool Physics::checkBlockCollision(const AABB& aabb, const glm::vec3& position) const { // 创建方块的碰撞箱 AABB blockAABB = createBlockAABB(position);
// 检查碰撞 return aabb.intersects(blockAABB);}
AABB Physics::createBlockAABB(const glm::vec3& position) const { return AABB(position, position + glm::vec3(1.0f)); }
AABB Physics::createBlockAABB(const glm::vec3& position, BlockType type) const { switch (type) { case BlockType::WATER: // 水方块的碰撞箱略小 return AABB(position + glm::vec3(0.0f, 0.0f, 0.0f), position + glm::vec3(1.0f, 0.9f, 1.0f)); case BlockType::LEAVES: // 树叶的碰撞箱也略小 return AABB(position + glm::vec3(0.05f, 0.05f, 0.05f), position + glm::vec3(0.95f, 0.95f, 0.95f)); default: // 普通方块的碰撞箱 return AABB(position, position + glm::vec3(1.0f)); } }
bool Physics::checkFaceCollision(const AABB& aabb, const AABB& blockAABB, Block::Face face) const { // 根据面的方向检查碰撞 switch (face) { case Block::FRONT: return aabb.max.z >= blockAABB.min.z && aabb.min.z < blockAABB.min.z; case Block::BACK: return aabb.min.z <= blockAABB.max.z && aabb.max.z > blockAABB.max.z; case Block::LEFT: return aabb.min.x <= blockAABB.max.x && aabb.max.x > blockAABB.max.x; case Block::RIGHT: return aabb.max.x >= blockAABB.min.x && aabb.min.x < blockAABB.min.x; case Block::TOP: return aabb.max.y >= blockAABB.min.y && aabb.min.y < blockAABB.min.y; case Block::BOTTOM: return aabb.min.y <= blockAABB.max.y && aabb.max.y > blockAABB.max.y; default: return false; } }
bool Physics::resolveFaceCollision(AABB& aabb, glm::vec3& velocity, const AABB& blockAABB, Block::Face face, float deltaTime) { // 根据面的方向解决碰撞 switch (face) { case Block::FRONT: aabb.max.z = blockAABB.min.z; velocity.z = 0.0f; return true; case Block::BACK: aabb.min.z = blockAABB.max.z; velocity.z = 0.0f; return true; case Block::LEFT: aabb.min.x = blockAABB.max.x; velocity.x = 0.0f; return true; case Block::RIGHT: aabb.max.x = blockAABB.min.x; velocity.x = 0.0f; return true; case Block::TOP: aabb.max.y = blockAABB.min.y; velocity.y = 0.0f; return true; case Block::BOTTOM: aabb.min.y = blockAABB.max.y; velocity.y = 0.0f; return true; default: return false; } } #pragma once #include <glm/glm.hpp> #include "../world/Block.h"
// 轴对齐边界框 struct AABB { glm::vec3 min; glm::vec3 max;
AABB() : min(0.0f), max(0.0f) {} AABB(const glm::vec3& min, const glm::vec3& max) : min(min), max(max) {} // 检查点是否在AABB内 bool contains(const glm::vec3& point) const { return point.x >= min.x && point.x <= max.x && point.y >= min.y && point.y <= max.y && point.z >= min.z && point.z <= max.z; } // 检查两个AABB是否相交 bool intersects(const AABB& other) const { return min.x < other.max.x && max.x > other.min.x && min.y < other.max.y && max.y > other.min.y && min.z < other.max.z && max.z > other.min.z; } // 获取AABB的中心点 glm::vec3 getCenter() const { return (min + max) * 0.5f; } // 获取AABB的大小 glm::vec3 getSize() const { return max - min; }};
class Physics { public: Physics(); ~Physics();
// 设置重力 void setGravity(const glm::vec3& gravity) { this->gravity = gravity; } const glm::vec3& getGravity() const { return gravity; } // 应用重力 void applyGravity(glm::vec3& velocity, float deltaTime) const; // 碰撞检测 bool checkCollision(const AABB& aabb, const Block& block) const; // 解决碰撞 bool resolveCollision(AABB& aabb, glm::vec3& velocity, const Block& block, float deltaTime); // 方块碰撞检测 bool checkBlockCollision(const AABB& aabb, const glm::vec3& position) const; // 从方块位置创建碰撞箱 AABB createBlockAABB(const glm::vec3& position) const; // 从方块位置和类型创建碰撞箱 AABB createBlockAABB(const glm::vec3& position, BlockType type) const;private: glm::vec3 gravity; float airResistance; float groundFriction;
// 检查AABB与方块面的碰撞 bool checkFaceCollision(const AABB& aabb, const AABB& blockAABB, Block::Face face) const; // 解决AABB与方块面的碰撞 bool resolveFaceCollision(AABB& aabb, glm::vec3& velocity, const AABB& blockAABB, Block::Face face, float deltaTime);}; #pragma once #include <glm/glm.hpp> #include "../core/Camera.h" #include "../core/Input.h" #include "../world/World.h" #include "Physics.h"
class Player { public: Player(Camera* camera, World* world, Input* input); ~Player();
void update(float deltaTime); void handleInput(float deltaTime); // 获取玩家位置 const glm::vec3& getPosition() const { return position; } void setPosition(const glm::vec3& position); // 获取玩家视角 float getYaw() const { return yaw; } float getPitch() const { return pitch; } void setRotation(float yaw, float pitch); // 玩家状态 bool isFlying() const { return flying; } void setFlying(bool flying) { this->flying = flying; } bool isSwimming() const { return swimming; } bool isOnGround() const { return onGround; } // 方块交互 void breakBlock(); void placeBlock(BlockType type); // 获取当前选中的方块 bool getSelectedBlock(glm::vec3& blockPosition, Block::Face& face) const;private: Camera* camera; World* world; Input* input; Physics physics;
glm::vec3 position; glm::vec3 velocity; float yaw; float pitch; float walkSpeed; float sprintSpeed; float flySpeed; float jumpStrength; bool flying; bool swimming; bool onGround; bool sprinting; // 玩家碰撞箱 AABB boundingBox; // 更新摄像机 void updateCamera(); // 处理移动 void handleMovement(float deltaTime); // 处理跳跃 void handleJump(); // 检测玩家状态 void updatePlayerState(); // 更新碰撞箱 void updateBoundingBox(); // 射线检测最大距离 static const float REACH_DISTANCE;}; #include "Renderer.h" #include #include <glm/glm.hpp> #include <glm/gtc/matrix_transform.hpp>
Renderer::Renderer() : debugMode(false) { initShaders(); initBuffers(); initTextures(); initSkybox(); }
Renderer::~Renderer() { // 清理VAO和VBO glDeleteVertexArrays(1, &chunkVAO); glDeleteBuffers(1, &chunkVBO); glDeleteBuffers(1, &chunkEBO);
glDeleteVertexArrays(1, &skyboxVAO); glDeleteBuffers(1, &skyboxVBO);}
void Renderer::beginFrame() { // 清除颜色和深度缓冲 glClearColor(0.529f, 0.808f, 0.922f, 1.0f); // 天空蓝 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// 启用深度测试 glEnable(GL_DEPTH_TEST); glEnable(GL_CULL_FACE);}
void Renderer::endFrame() { // 禁用深度测试和背面剔除 glDisable(GL_DEPTH_TEST); glDisable(GL_CULL_FACE); }
void Renderer::renderWorld(const World* world, const Camera* camera) { // 渲染天空盒 renderSkybox(camera);
// 渲染所有加载的区块 const auto& chunks = world->getLoadedChunks(); for (const auto& pair : chunks) { const auto& chunk = pair.second; renderChunk(chunk.get()); } // 使用区块着色器 chunkShader->use(); // 设置视图和投影矩阵 chunkShader->setUniform("view", camera->getViewMatrix()); chunkShader->setUniform("projection", camera->getProjectionMatrix()); // 设置光照 chunkShader->setUniform("lightPos", glm::vec3(100.0f, 200.0f, 100.0f)); chunkShader->setUniform("lightColor", glm::vec3(1.0f, 1.0f, 0.9f)); // 渲染所有加载的区块 for (const auto& pair : chunks) { const auto& chunk = pair.second; renderChunk(chunk.get()); } chunkShader->unuse();}
void Renderer::renderChunk(const Chunk* chunk) { if (!chunk) return;
// 检查区块是否有顶点数据 if (chunk->getVertexCount() == 0) return; // 绑定VAO glBindVertexArray(chunkVAO); // 更新VBO数据 glBindBuffer(GL_ARRAY_BUFFER, chunkVBO); glBufferData(GL_ARRAY_BUFFER, chunk->getVertices().size() * sizeof(float), chunk->getVertices().data(), GL_DYNAMIC_DRAW); // 更新EBO数据 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, chunkEBO); glBufferData(GL_ELEMENT_ARRAY_BUFFER, chunk->getIndices().size() * sizeof(unsigned int), chunk->getIndices().data(), GL_DYNAMIC_DRAW); // 绑定纹理 Texture* texture = textureManager->getTexture("blocks"); if (texture) { texture->bind(0); chunkShader->setUniform("textureAtlas", 0); } // 绘制区块 glDrawElements(GL_TRIANGLES, chunk->getIndices().size(), GL_UNSIGNED_INT, 0); // 解绑 glBindVertexArray(0); glBindBuffer(GL_ARRAY_BUFFER, 0); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);}
void Renderer::initShaders() { // 创建区块着色器 chunkShader = std::make_unique("resources/shaders/chunk.vert", "resources/shaders/chunk.frag");
// 创建天空盒着色器 skyboxShader = std::make_unique<Shader>("resources/shaders/skybox.vert", "resources/shaders/skybox.frag");}
void Renderer::initBuffers() { // 创建区块VAO和VBO glGenVertexArrays(1, &chunkVAO); glGenBuffers(1, &chunkVBO); glGenBuffers(1, &chunkEBO);
// 绑定VAO glBindVertexArray(chunkVAO); // 配置VBO glBindBuffer(GL_ARRAY_BUFFER, chunkVBO); // 配置顶点属性 // 位置 (x, y, z) glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)0); glEnableVertexAttribArray(0); // 纹理坐标 (u, v) glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)(3 * sizeof(float))); glEnableVertexAttribArray(1); // 解绑 glBindVertexArray(0); glBindBuffer(GL_ARRAY_BUFFER, 0);}
void Renderer::initTextures() { // 创建纹理管理器 textureManager = std::make_unique();
// 加载默认纹理 textureManager->loadTexture("blocks", "resources/textures/blocks.png"); textureManager->loadTexture("skybox", "resources/textures/skybox.png");}
void Renderer::initSkybox() { // 天空盒顶点数据 float skyboxVertices[] = { // 后面 -1.0f, 1.0f, -1.0f, -1.0f, -1.0f, -1.0f, 1.0f, -1.0f, -1.0f, 1.0f, -1.0f, -1.0f, 1.0f, 1.0f, -1.0f, -1.0f, 1.0f, -1.0f, // 前面 -1.0f, -1.0f, 1.0f, -1.0f, -1.0f, -1.0f, -1.0f, 1.0f, -1.0f, -1.0f, 1.0f, -1.0f, -1.0f, 1.0f, 1.0f, -1.0f, -1.0f, 1.0f, // 右面 1.0f, -1.0f, -1.0f, 1.0f, -1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, -1.0f, 1.0f, -1.0f, -1.0f, // 左面 -1.0f, -1.0f, 1.0f, -1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, -1.0f, 1.0f, -1.0f, -1.0f, 1.0f, // 顶面 -1.0f, 1.0f, -1.0f, 1.0f, 1.0f, -1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, -1.0f, 1.0f, 1.0f, -1.0f, 1.0f, -1.0f, // 底面 -1.0f, -1.0f, -1.0f, -1.0f, -1.0f, 1.0f, 1.0f, -1.0f, -1.0f, 1.0f, -1.0f, -1.0f, -1.0f, -1.0f, 1.0f, 1.0f, -1.0f, 1.0f };
// 创建天空盒VAO和VBO glGenVertexArrays(1, &skyboxVAO); glGenBuffers(1, &skyboxVBO); // 绑定VAO glBindVertexArray(skyboxVAO); // 配置VBO glBindBuffer(GL_ARRAY_BUFFER, skyboxVBO); glBufferData(GL_ARRAY_BUFFER, sizeof(skyboxVertices), skyboxVertices, GL_STATIC_DRAW); // 配置顶点属性 glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0); glEnableVertexAttribArray(0); // 解绑 glBindVertexArray(0); glBindBuffer(GL_ARRAY_BUFFER, 0);}
void Renderer::renderSkybox(const Camera* camera) { // 禁用深度写入 glDepthMask(GL_FALSE);
// 使用天空盒着色器 skyboxShader->use(); // 设置视图矩阵(移除平移部分) glm::mat4 view = glm::mat4(glm::mat3(camera->getViewMatrix())); skyboxShader->setUniform("view", view); // 设置投影矩阵 skyboxShader->setUniform("projection", camera->getProjectionMatrix()); // 绑定天空盒纹理 Texture* skyboxTexture = textureManager->getTexture("skybox"); if (skyboxTexture) { skyboxTexture->bind(0); skyboxShader->setUniform("skybox", 0); } // 绑定VAO并绘制 glBindVertexArray(skyboxVAO); glDrawArrays(GL_TRIANGLES, 0, 36); glBindVertexArray(0); // 启用深度写入 glDepthMask(GL_TRUE); skyboxShader->unuse();}
void Renderer::renderBlockOutline(const glm::vec3& position) { // TODO: 实现方块选择框渲染 } #pragma once #include #include <glad/glad.h> #include "../world/World.h" #include "../core/Camera.h" #include "Shader.h" #include "TextureManager.h"
class Renderer { public: Renderer(); ~Renderer();
void beginFrame(); void endFrame(); void renderWorld(const World* world, const Camera* camera); void renderChunk(const Chunk* chunk); // 设置调试模式 void setDebugMode(bool debug) { debugMode = debug; }private: // 着色器 std::unique_ptr chunkShader; std::unique_ptr skyboxShader;
// 纹理管理器 std::unique_ptr<TextureManager> textureManager; // 天空盒VAO unsigned int skyboxVAO; unsigned int skyboxVBO; // 方块VAO unsigned int chunkVAO; unsigned int chunkVBO; unsigned int chunkEBO; // 调试模式 bool debugMode; // 初始化 void initShaders(); void initBuffers(); void initTextures(); void initSkybox(); // 渲染天空盒 void renderSkybox(const Camera* camera); // 渲染方块选择框 void renderBlockOutline(const glm::vec3& position);}; #include "Shader.h" #include #include #include #include <glad/glad.h>
Shader::Shader(const std::string& vertexPath, const std::string& fragmentPath) { // 编译顶点着色器 unsigned int vertexShader = compileShader(vertexPath, GL_VERTEX_SHADER); if (vertexShader == 0) { std::cerr << "Failed to compile vertex shader" << std::endl; return; }
// 编译片段着色器 unsigned int fragmentShader = compileShader(fragmentPath, GL_FRAGMENT_SHADER); if (fragmentShader == 0) { std::cerr << "Failed to compile fragment shader" << std::endl; glDeleteShader(vertexShader); return; } // 创建着色器程序 programID = glCreateProgram(); glAttachShader(programID, vertexShader); glAttachShader(programID, fragmentShader); glLinkProgram(programID); // 检查链接错误 int success; char infoLog[512]; glGetProgramiv(programID, GL_LINK_STATUS, &success); if (!success) { glGetProgramInfoLog(programID, 512, NULL, infoLog); std::cerr << "Failed to link shader program: " << infoLog << std::endl; glDeleteShader(vertexShader); glDeleteShader(fragmentShader); programID = 0; return; } // 删除已链接的着色器 glDeleteShader(vertexShader); glDeleteShader(fragmentShader);}
Shader::~Shader() { if (programID != 0) { glDeleteProgram(programID); } }
void Shader::use() const { if (programID != 0) { glUseProgram(programID); } }
void Shader::unuse() const { glUseProgram(0); }
void Shader::setUniform(const std::string& name, float value) const { int location = getUniformLocation(name); if (location != -1) { glUniform1f(location, value); } }
void Shader::setUniform(const std::string& name, int value) const { int location = getUniformLocation(name); if (location != -1) { glUniform1i(location, value); } }
void Shader::setUniform(const std::string& name, bool value) const { int location = getUniformLocation(name); if (location != -1) { glUniform1i(location, value ? 1 : 0); } }
void Shader::setUniform(const std::string& name, const glm::vec2& value) const { int location = getUniformLocation(name); if (location != -1) { glUniform2fv(location, 1, &value[0]); } }
void Shader::setUniform(const std::string& name, const glm::vec3& value) const { int location = getUniformLocation(name); if (location != -1) { glUniform3fv(location, 1, &value[0]); } }
void Shader::setUniform(const std::string& name, const glm::vec4& value) const { int location = getUniformLocation(name); if (location != -1) { glUniform4fv(location, 1, &value[0]); } }
void Shader::setUniform(const std::string& name, const glm::mat2& value) const { int location = getUniformLocation(name); if (location != -1) { glUniformMatrix2fv(location, 1, GL_FALSE, &value[0][0]); } }
void Shader::setUniform(const std::string& name, const glm::mat3& value) const { int location = getUniformLocation(name); if (location != -1) { glUniformMatrix3fv(location, 1, GL_FALSE, &value[0][0]); } }
void Shader::setUniform(const std::string& name, const glm::mat4& value) const { int location = getUniformLocation(name); if (location != -1) { glUniformMatrix4fv(location, 1, GL_FALSE, &value[0][0]); } }
unsigned int Shader::compileShader(const std::string& path, unsigned int type) const { // 读取着色器文件 std::string shaderCode = readShaderFile(path); if (shaderCode.empty()) { return 0; }
// 创建着色器 unsigned int shader = glCreateShader(type); const char* codePtr = shaderCode.c_str(); glShaderSource(shader, 1, &codePtr, NULL); glCompileShader(shader); // 检查编译错误 int success; char infoLog[512]; glGetShaderiv(shader, GL_COMPILE_STATUS, &success); if (!success) { glGetShaderInfoLog(shader, 512, NULL, infoLog); std::cerr << "Failed to compile shader " << path << ": " << infoLog << std::endl; glDeleteShader(shader); return 0; } return shader;}
int Shader::getUniformLocation(const std::string& name) const { // 检查缓存 auto it = uniformLocations.find(name); if (it != uniformLocations.end()) { return it->second; }
// 获取位置并缓存 int location = glGetUniformLocation(programID, name.c_str()); if (location == -1) { std::cerr << "Warning: Uniform '" << name << "' not found in shader" << std::endl; } uniformLocations[name] = location; return location;}
std::string Shader::readShaderFile(const std::string& path) const { std::ifstream file; std::stringstream stream;
// 打开文件 file.open(path); if (!file.is_open()) { std::cerr << "Failed to open shader file: " << path << std::endl; return ""; } // 读取文件内容 stream << file.rdbuf(); file.close(); return stream.str();}
#pragma once #include #include <unordered_map> #include <glm/glm.hpp>
class Shader { public: Shader(const std::string& vertexPath, const std::string& fragmentPath); ~Shader();
void use() const; void unuse() const; // 设置 uniform 变量 void setUniform(const std::string& name, float value) const; void setUniform(const std::string& name, int value) const; void setUniform(const std::string& name, bool value) const; void setUniform(const std::string& name, const glm::vec2& value) const; void setUniform(const std::string& name, const glm::vec3& value) const; void setUniform(const std::string& name, const glm::vec4& value) const; void setUniform(const std::string& name, const glm::mat2& value) const; void setUniform(const std::string& name, const glm::mat3& value) const; void setUniform(const std::string& name, const glm::mat4& value) const; unsigned int getProgramID() const { return programID; }private: unsigned int programID; std::unordered_map<std::string, int> uniformLocations;
// 编译着色器 unsigned int compileShader(const std::string& path, unsigned int type) const; // 获取 uniform 位置 int getUniformLocation(const std::string& name) const; // 读取着色器文件 std::string readShaderFile(const std::string& path) const;}; #include "Texture.h" #include #include <SOIL2/SOIL2.h>
Texture::Texture(const std::string& path) : id(0), width(0), height(0), channels(0) { loadTexture(path); }
Texture::~Texture() { if (id != 0) { glDeleteTextures(1, &id); } }
void Texture::bind(unsigned int slot) const { glActiveTexture(GL_TEXTURE0 + slot); glBindTexture(GL_TEXTURE_2D, id); }
void Texture::unbind() const { glBindTexture(GL_TEXTURE_2D, 0); }
void Texture::loadTexture(const std::string& path) { // 生成纹理ID glGenTextures(1, &id); glBindTexture(GL_TEXTURE_2D, id);
// 设置纹理参数 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); // 加载纹理图像 unsigned char* data = SOIL_load_image(path.c_str(), &width, &height, &channels, SOIL_LOAD_RGBA); if (data) { glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data); glGenerateMipmap(GL_TEXTURE_2D); } else { std::cerr << "Failed to load texture: " << path << std::endl; std::cerr << "SOIL error: " << SOIL_last_result() << std::endl; } // 释放图像数据 SOIL_free_image_data(data); glBindTexture(GL_TEXTURE_2D, 0);}
TextureManager::TextureManager() : defaultTexturePath("resources/textures/") { }
TextureManager::~TextureManager() { cleanup(); }
Texture* TextureManager::getTexture(const std::string& name) { auto it = textures.find(name); if (it != textures.end()) { return it->second; }
// 尝试加载纹理 std::string path = defaultTexturePath + name + ".png"; Texture* texture = new Texture(path); textures[name] = texture; return texture;}
void TextureManager::loadTexture(const std::string& name, const std::string& path) { // 检查是否已存在 auto it = textures.find(name); if (it != textures.end()) { delete it->second; textures.erase(it); }
// 加载新纹理 Texture* texture = new Texture(path); textures[name] = texture;}
void TextureManager::cleanup() { for (auto& pair : textures) { delete pair.second; } textures.clear(); } #pragma once #include #include <unordered_map> #include <glad/glad.h>
class Texture { public: Texture(const std::string& path); ~Texture();
void bind(unsigned int slot = 0) const; void unbind() const; unsigned int getID() const { return id; } int getWidth() const { return width; } int getHeight() const { return height; }private: unsigned int id; int width; int height; int channels;
void loadTexture(const std::string& path);};
class TextureManager { public: TextureManager(); ~TextureManager();
// 获取或加载纹理 Texture* getTexture(const std::string& name); // 加载纹理 void loadTexture(const std::string& name, const std::string& path); // 清理所有纹理 void cleanup();private: std::unordered_map<std::string, Texture*> textures;
// 默认纹理路径 std::string defaultTexturePath;}; #include "Block.h"
Block::Block(BlockType type) : type(type) { }
Block::~Block() { }
bool Block::isSolid() const { return isSolidBlock(type); }
bool Block::isTransparent() const { return isTransparentBlock(type); }
std::string Block::getTextureName(int face) const { return getTextureForBlock(type, face); }
bool Block::isSolidBlock(BlockType type) const { switch (type) { case BlockType::AIR: case BlockType::WATER: return false; case BlockType::GRASS: case BlockType::DIRT: case BlockType::STONE: case BlockType::SAND: case BlockType::WOOD: case BlockType::LEAVES: return true; default: return false; } }
bool Block::isTransparentBlock(BlockType type) const { switch (type) { case BlockType::AIR: case BlockType::WATER: case BlockType::LEAVES: return true; case BlockType::GRASS: case BlockType::DIRT: case BlockType::STONE: case BlockType::SAND: case BlockType::WOOD: return false; default: return false; } }
std::string Block::getTextureForBlock(BlockType type, int face) const { switch (type) { case BlockType::GRASS: if (face == TOP) return "grass_top"; if (face == BOTTOM) return "dirt"; return "grass_side"; case BlockType::DIRT: return "dirt"; case BlockType::STONE: return "stone"; case BlockType::SAND: return "sand"; case BlockType::WATER: return "water"; case BlockType::WOOD: if (face == TOP || face == BOTTOM) return "wood_top"; return "wood_side"; case BlockType::LEAVES: return "leaves"; default: return "missing"; } } #pragma once #include <glm/glm.hpp> #include
enum class BlockType { AIR, GRASS, DIRT, STONE, SAND, WATER, WOOD, LEAVES };
class Block { public: Block(BlockType type = BlockType::AIR); ~Block();
BlockType getType() const { return type; } void setType(BlockType type) { this->type = type; } bool isSolid() const; bool isTransparent() const; std::string getTextureName(int face) const; static const int FACE_COUNT = 6; enum Face { FRONT, BACK, LEFT, RIGHT, TOP, BOTTOM };private: BlockType type;
bool isSolidBlock(BlockType type) const; bool isTransparentBlock(BlockType type) const; std::string getTextureForBlock(BlockType type, int face) const;}; #include "Chunk.h" #include
Chunk::Chunk(int chunkX, int chunkZ) : chunkX(chunkX), chunkZ(chunkZ), dirty(true) { initBlocks(); }
Chunk::~Chunk() { }
void Chunk::initBlocks() { blocks.resize(CHUNK_SIZE_X); for (int x = 0; x < CHUNK_SIZE_X; x++) { blocks[x].resize(CHUNK_SIZE_Y); for (int y = 0; y < CHUNK_SIZE_Y; y++) { blocks[x][y].resize(CHUNK_SIZE_Z, Block(BlockType::AIR)); } } }
Block& Chunk::getBlock(int x, int y, int z) { if (!isBlockLoaded(x, y, z)) { static Block airBlock(BlockType::AIR); return airBlock; } return blocks[x][y][z]; }
const Block& Chunk::getBlock(int x, int y, int z) const { if (!isBlockLoaded(x, y, z)) { static Block airBlock(BlockType::AIR); return airBlock; } return blocks[x][y][z]; }
void Chunk::setBlock(int x, int y, int z, BlockType type) { if (!isBlockLoaded(x, y, z)) return;
blocks[x][y][z].setType(type); dirty = true;}
bool Chunk::isBlockLoaded(int x, int y, int z) const { return x >= 0 && x < CHUNK_SIZE_X && y >= 0 && y < CHUNK_SIZE_Y && z >= 0 && z < CHUNK_SIZE_Z; }
void Chunk::generateMesh() { if (!dirty) return;
vertices.clear(); indices.clear(); // 遍历所有方块 for (int x = 0; x < CHUNK_SIZE_X; x++) { for (int y = 0; y < CHUNK_SIZE_Y; y++) { for (int z = 0; z < CHUNK_SIZE_Z; z++) { const Block& block = getBlock(x, y, z); // 跳过空气方块 if (block.getType() == BlockType::AIR) continue; // 检查每个面是否需要渲染 for (int face = 0; face < Block::FACE_COUNT; face++) { if (shouldRenderFace(x, y, z, static_cast<Block::Face>(face))) { addFaceToMesh(x, y, z, static_cast<Block::Face>(face)); } } } } } dirty = false;}
bool Chunk::shouldRenderFace(int x, int y, int z, Block::Face face) const { // 根据面的方向计算相邻方块的坐标 int neighborX = x; int neighborY = y; int neighborZ = z;
switch (face) { case Block::FRONT: neighborZ++; break; case Block::BACK: neighborZ--; break; case Block::LEFT: neighborX--; break; case Block::RIGHT: neighborX++; break; case Block::TOP: neighborY++; break; case Block::BOTTOM: neighborY--; break; } // 检查相邻方块是否在区块内 if (!isBlockLoaded(neighborX, neighborY, neighborZ)) { // 如果相邻方块不在区块内,暂时假设它是空气方块 return true; } const Block& neighborBlock = getBlock(neighborX, neighborY, neighborZ); // 如果相邻方块是透明的,则渲染当前面 return !neighborBlock.isSolid() || neighborBlock.isTransparent();}
void Chunk::addFaceToMesh(int x, int y, int z, Block::Face face) { const Block& block = getBlock(x, y, z);
// 顶点位置偏移 float offsetX = x + chunkX * CHUNK_SIZE_X; float offsetY = y; float offsetZ = z + chunkZ * CHUNK_SIZE_Z; // 每个面的4个顶点 float faceVertices[4][5] = { // x, y, z, u, v {0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, {1.0f, 0.0f, 0.0f, 1.0f, 0.0f}, {1.0f, 1.0f, 0.0f, 1.0f, 1.0f}, {0.0f, 1.0f, 0.0f, 0.0f, 1.0f} }; // 根据面的方向调整顶点位置和纹理坐标 switch (face) { case Block::FRONT: // 已经是默认方向 break; case Block::BACK: // 翻转Z轴 for (int i = 0; i < 4; i++) { faceVertices[i][2] = 1.0f - faceVertices[i][2]; faceVertices[i][3] = 1.0f - faceVertices[i][3]; } break; case Block::LEFT: // 旋转到X轴负方向 for (int i = 0; i < 4; i++) { float temp = faceVertices[i][0]; faceVertices[i][0] = faceVertices[i][2]; faceVertices[i][2] = 1.0f - temp; } break; case Block::RIGHT: // 旋转到X轴正方向 for (int i = 0; i < 4; i++) { float temp = faceVertices[i][0]; faceVertices[i][0] = 1.0f - faceVertices[i][2]; faceVertices[i][2] = temp; faceVertices[i][3] = 1.0f - faceVertices[i][3]; } break; case Block::TOP: // 旋转到Y轴正方向 for (int i = 0; i < 4; i++) { float temp = faceVertices[i][1]; faceVertices[i][1] = 1.0f - faceVertices[i][2]; faceVertices[i][2] = temp; faceVertices[i][4] = 1.0f - faceVertices[i][4]; } break; case Block::BOTTOM: // 旋转到Y轴负方向 for (int i = 0; i < 4; i++) { float temp = faceVertices[i][1]; faceVertices[i][1] = faceVertices[i][2]; faceVertices[i][2] = 1.0f - temp; faceVertices[i][3] = 1.0f - faceVertices[i][3]; faceVertices[i][4] = 1.0f - faceVertices[i][4]; } break; } // 添加顶点到顶点数组 unsigned int baseIndex = vertices.size() / 5; for (int i = 0; i < 4; i++) { vertices.push_back(faceVertices[i][0] + offsetX); vertices.push_back(faceVertices[i][1] + offsetY); vertices.push_back(faceVertices[i][2] + offsetZ); vertices.push_back(faceVertices[i][3]); vertices.push_back(faceVertices[i][4]); } // 添加索引到索引数组(两个三角形组成一个面) indices.push_back(baseIndex + 0); indices.push_back(baseIndex + 1); indices.push_back(baseIndex + 2); indices.push_back(baseIndex + 0); indices.push_back(baseIndex + 2); indices.push_back(baseIndex + 3);} #pragma once #include #include #include <glm/glm.hpp> #include "Block.h"
class Chunk { public: static const int CHUNK_SIZE_X = 16; static const int CHUNK_SIZE_Y = 256; static const int CHUNK_SIZE_Z = 16;
Chunk(int chunkX, int chunkZ); ~Chunk(); Block& getBlock(int x, int y, int z); const Block& getBlock(int x, int y, int z) const; void setBlock(int x, int y, int z, BlockType type); bool isBlockLoaded(int x, int y, int z) const; int getChunkX() const { return chunkX; } int getChunkZ() const { return chunkZ; } bool isDirty() const { return dirty; } void setDirty(bool dirty) { this->dirty = dirty; } // 生成区块网格数据 void generateMesh(); // 获取渲染数据 const std::vector<float>& getVertices() const { return vertices; } const std::vector<unsigned int>& getIndices() const { return indices; } int getVertexCount() const { return vertices.size() / 5; } // 5 components per vertex (x, y, z, u, v)private: int chunkX; int chunkZ; std::vector<std::vector<std::vector>> blocks;
bool dirty; // 渲染数据 std::vector<float> vertices; std::vector<unsigned int> indices; void initBlocks(); bool shouldRenderFace(int x, int y, int z, Block::Face face) const; void addFaceToMesh(int x, int y, int z, Block::Face face);}; #pragma once #include #include #include <glm/glm.hpp> #include "Block.h"
class Chunk { public: static const int CHUNK_SIZE_X = 16; static const int CHUNK_SIZE_Y = 256; static const int CHUNK_SIZE_Z = 16;
Chunk(int chunkX, int chunkZ); ~Chunk(); Block& getBlock(int x, int y, int z); const Block& getBlock(int x, int y, int z) const; void setBlock(int x, int y, int z, BlockType type); bool isBlockLoaded(int x, int y, int z) const; int getChunkX() const { return chunkX; } int getChunkZ() const { return chunkZ; } bool isDirty() const { return dirty; } void setDirty(bool dirty) { this->dirty = dirty; } // 生成区块网格数据 void generateMesh(); // 获取渲染数据 const std::vector<float>& getVertices() const { return vertices; } const std::vector<unsigned int>& getIndices() const { return indices; } int getVertexCount() const { return vertices.size() / 5; } // 5 components per vertex (x, y, z, u, v)private: int chunkX; int chunkZ; std::vector<std::vector<std::vector>> blocks;
bool dirty; // 渲染数据 std::vector<float> vertices; std::vector<unsigned int> indices; void initBlocks(); bool shouldRenderFace(int x, int y, int z, Block::Face face) const; void addFaceToMesh(int x, int y, int z, Block::Face face);}; #pragma once #include <unordered_map> #include #include #include <glm/glm.hpp> #include "Chunk.h" #include "WorldGenerator.h" #include "Block.h"
// 区块位置哈希函数 struct ChunkPositionHash { std::size_t operator()(const std::pair<int, int>& k) const { return std::hash()(k.first) ^ std::hash()(k.second); } };
class World { public: World(); ~World();
// 获取指定位置的方块 Block& getBlock(int x, int y, int z); const Block& getBlock(int x, int y, int z) const; // 设置指定位置的方块 void setBlock(int x, int y, int z, BlockType type); // 获取或创建区块 std::shared_ptr<Chunk> getChunk(int chunkX, int chunkZ); // 加载区块 void loadChunk(int chunkX, int chunkZ); // 卸载区块 void unloadChunk(int chunkX, int chunkZ); // 更新世界(加载/卸载区块) void update(const glm::vec3& playerPosition, int renderDistance); // 获取所有加载的区块 const std::unordered_map<std::pair<int, int>, std::shared_ptr<Chunk>, ChunkPositionHash>& getLoadedChunks() const; // 射线检测(用于方块选择) bool raycast(const glm::vec3& start, const glm::vec3& direction, float maxDistance, glm::vec3& hitPosition, Block::Face& hitFace); // 设置世界生成器参数 void setSeed(int seed) { generator.setSeed(seed); }private: // 加载的区块 std::unordered_map<std::pair<int, int>, std::shared_ptr, ChunkPositionHash> loadedChunks;
// 世界生成器 WorldGenerator generator; // 线程安全锁 std::mutex worldMutex; // 计算区块坐标 std::pair<int, int> getChunkPosition(int x, int z) const; // 计算区块内坐标 void getBlockPositionInChunk(int x, int z, int& blockX, int& blockZ) const;}; #include "WorldGenerator.h" #include #include
WorldGenerator::WorldGenerator() : seed(12345), amplitude(40.0f), frequency(0.01f) { initNoiseModules(); }
WorldGenerator::~WorldGenerator() { }
void WorldGenerator::initNoiseModules() { // 设置噪声模块参数 perlin.SetSeed(seed); perlin.SetFrequency(frequency); perlin.SetOctaveCount(6); perlin.SetPersistence(0.5f);
ridgedNoise.SetSeed(seed + 1); ridgedNoise.SetFrequency(frequency * 0.5f); ridgedNoise.SetOctaveCount(4); detailNoise.SetSeed(seed + 2); detailNoise.SetFrequency(frequency * 2.0f); detailNoise.SetOctaveCount(3); detailNoise.SetPersistence(0.7f); biomeNoise.SetSeed(seed + 3); biomeNoise.SetFrequency(frequency * 0.25f); biomeNoise.SetOctaveCount(2); // 组合噪声模块 baseTerrain.SetSourceModule(0, perlin); baseTerrain.SetSourceModule(1, ridgedNoise); terrainScale.SetSourceModule(0, baseTerrain); terrainScale.SetScale(amplitude); terrainScale.SetBias(64.0f); // 基础高度}
void WorldGenerator::setSeed(int seed) { this->seed = seed; initNoiseModules(); }
int WorldGenerator::getHeight(int x, int z) const { // 获取噪声值 double noiseValue = terrainScale.GetValue(x, 0, z);
// 添加细节噪声 double detailValue = detailNoise.GetValue(x, 0, z) * 5.0; // 计算最终高度 int height = static_cast<int>(noiseValue + detailValue); // 限制高度范围 if (height < 10) height = 10; if (height > 128) height = 128; return height;}
int WorldGenerator::getBiomeType(int x, int z) const { double biomeValue = biomeNoise.GetValue(x, 0, z);
// 根据噪声值确定生物群系 if (biomeValue < -0.3) { return 0; // 沙漠 } else if (biomeValue < 0.0) { return 1; // 平原 } else if (biomeValue < 0.3) { return 2; // 森林 } else { return 3; // 山地 }}
void WorldGenerator::generateBiome(int x, int z, int height, Chunk* chunk) { int biomeType = getBiomeType(x, z);
switch (biomeType) { case 0: // 沙漠 // 沙子表面 for (int y = 0; y < height; y++) { if (y == height - 1) { chunk->setBlock(x, y, z, BlockType::SAND); } else if (y > height - 4) { chunk->setBlock(x, y, z, BlockType::SAND); } else { chunk->setBlock(x, y, z, BlockType::STONE); } } break; case 1: // 平原 // 草方块表面 for (int y = 0; y < height; y++) { if (y == height - 1) { chunk->setBlock(x, y, z, BlockType::GRASS); } else if (y > height - 4) { chunk->setBlock(x, y, z, BlockType::DIRT); } else { chunk->setBlock(x, y, z, BlockType::STONE); } } break; case 2: // 森林 // 草方块表面,偶尔生成树木 for (int y = 0; y < height; y++) { if (y == height - 1) { chunk->setBlock(x, y, z, BlockType::GRASS); // 随机生成树木 if (rand() % 50 == 0) { generateTree(x, y + 1, z, chunk); } } else if (y > height - 4) { chunk->setBlock(x, y, z, BlockType::DIRT); } else { chunk->setBlock(x, y, z, BlockType::STONE); } } break; case 3: // 山地 // 更多石头,更高的地形 for (int y = 0; y < height; y++) { if (y == height - 1) { // 山顶可能是草或石头 if (rand() % 3 == 0) { chunk->setBlock(x, y, z, BlockType::GRASS); } else { chunk->setBlock(x, y, z, BlockType::STONE); } } else if (y > height - 3) { chunk->setBlock(x, y, z, BlockType::DIRT); } else { chunk->setBlock(x, y, z, BlockType::STONE); } } break; } // 添加一些地下洞穴 if (rand() % 100 == 0 && height > 20) { generateCave(x, z, height / 2, chunk); }}
void WorldGenerator::generateTree(int x, int y, int z, Chunk* chunk) { int trunkHeight = 4 + rand() % 3; int leafRadius = 3;
// 生成树干 for (int dy = 0; dy < trunkHeight; dy++) { if (y + dy < 256) { chunk->setBlock(x, y + dy, z, BlockType::WOOD); } } // 生成树叶 for (int dx = -leafRadius; dx <= leafRadius; dx++) { for (int dy = -leafRadius; dy <= leafRadius; dy++) { for (int dz = -leafRadius; dz <= leafRadius; dz++) { int distance = dx * dx + dy * dy + dz * dz; if (distance <= leafRadius * leafRadius) { int leafX = x + dx; int leafY = y + trunkHeight + dy; int leafZ = z + dz; // 检查是否在区块范围内 if (leafX >= 0 && leafX < 16 && leafY >= 0 && leafY < 256 && leafZ >= 0 && leafZ < 16) { // 只在空地生成树叶 if (chunk->getBlock(leafX, leafY, leafZ).getType() == BlockType::AIR) { chunk->setBlock(leafX, leafY, leafZ, BlockType::LEAVES); } } } } } }}
void WorldGenerator::generateCave(int x, int z, int startY, Chunk* chunk) { int caveLength = 20 + rand() % 30; int caveRadius = 2 + rand() % 2;
float dirX = (rand() % 100 - 50) / 100.0f; float dirZ = (rand() % 100 - 50) / 100.0f; float dirY = (rand() % 100 - 50) / 100.0f; // 归一化方向向量 float length = sqrt(dirX * dirX + dirY * dirY + dirZ * dirZ); dirX /= length; dirY /= length; dirZ /= length; float currentX = x + 0.5f; float currentY = startY + 0.5f; float currentZ = z + 0.5f; for (int i = 0; i < caveLength; i++) { // 挖洞 for (int dx = -caveRadius; dx <= caveRadius; dx++) { for (int dy = -caveRadius; dy <= caveRadius; dy++) { for (int dz = -caveRadius; dz <= caveRadius; dz++) { float distance = sqrt(dx * dx + dy * dy + dz * dz); if (distance <= caveRadius) { int caveX = static_cast<int>(currentX + dx); int caveY = static_cast<int>(currentY + dy); int caveZ = static_cast<int>(currentZ + dz); if (caveX >= 0 && caveX < 16 && caveY >= 0 && caveY < 256 && caveZ >= 0 && caveZ < 16) { chunk->setBlock(caveX, caveY, caveZ, BlockType::AIR); } } } } } // 移动到下一个位置 currentX += dirX; currentY += dirY; currentZ += dirZ; // 随机改变方向 dirX += (rand() % 100 - 50) / 500.0f; dirY += (rand() % 100 - 50) / 500.0f; dirZ += (rand() % 100 - 50) / 500.0f; // 重新归一化方向向量 length = sqrt(dirX * dirX + dirY * dirY + dirZ * dirZ); dirX /= length; dirY /= length; dirZ /= length; }}
void WorldGenerator::generateChunk(int chunkX, int chunkZ, Chunk* chunk) { // 设置随机种子 srand(seed + chunkX * 1000 + chunkZ);
// 生成区块内的每个方块 for (int x = 0; x < 16; x++) { for (int z = 0; z < 16; z++) { // 计算世界坐标 int worldX = chunkX * 16 + x; int worldZ = chunkZ * 16 + z; // 获取该位置的高度 int height = getHeight(worldX, worldZ); // 根据生物群系生成地形 generateBiome(x, z, height, chunk); // 在水面以下填充水 if (height < 63) { for (int y = height; y < 63; y++) { chunk->setBlock(x, y, z, BlockType::WATER); } } } }} #pragma once #include #include <noise/noise.h> #include "Block.h"
class WorldGenerator { public: WorldGenerator(); ~WorldGenerator();
// 生成区块地形 void generateChunk(int chunkX, int chunkZ, std::vector<std::vector<std::vector<Block>>>& blocks); // 获取指定位置的高度 int getHeight(int x, int z) const; // 设置生成器参数 void setSeed(int seed); void setAmplitude(float amplitude) { this->amplitude = amplitude; } void setFrequency(float frequency) { this->frequency = frequency; }private: int seed; float amplitude; float frequency;
// Perlin噪声模块 noise::module::Perlin perlin; noise::module::RidgedMulti ridgedNoise; noise::module::Perlin detailNoise; // 噪声组合模块 noise::module::Add baseTerrain; noise::module::ScaleBias terrainScale; // 生物群系相关 noise::module::Perlin biomeNoise; // 初始化噪声模块 void initNoiseModules(); // 获取生物群系类型 int getBiomeType(int x, int z) const; // 根据生物群系生成地形 void generateBiome(int x, int z, int height, std::vector<std::vector<std::vector<Block>>>& blocks);}; #include #include #include "core/Window.h" #include "core/Camera.h" #include "core/Input.h" #include "world/World.h" #include "rendering/Renderer.h" #include "player/Player.h"
int main() { // 创建窗口 std::unique_ptr window = std::make_unique("Minecraft Clone", 1280, 720);
// 创建摄像机 std::unique_ptr<Camera> camera = std::make_unique<Camera>(90.0f, 1280.0f / 720.0f, 0.1f, 1000.0f); // 创建输入处理器 std::unique_ptr<Input> input = std::make_unique<Input>(window.get()); // 创建渲染器 std::unique_ptr<Renderer> renderer = std::make_unique<Renderer>(); // 创建世界 std::unique_ptr<World> world = std::make_unique<World>(); // 创建玩家 std::unique_ptr<Player> player = std::make_unique<Player>(camera.get(), world.get(), input.get()); // 游戏循环 float lastFrameTime = 0.0f; while (!window->shouldClose()) { // 计算deltaTime float currentTime = glfwGetTime(); float deltaTime = currentTime - lastFrameTime; lastFrameTime = currentTime; // 处理输入 input->update(); player->handleInput(deltaTime); // 更新玩家 player->update(deltaTime); // 更新摄像机 camera->update(); // 渲染 renderer->beginFrame(); renderer->renderWorld(world.get(), camera.get()); renderer->endFrame(); // 窗口更新 window->update(); } return 0;}
-
通过的题目
-
最近活动
-
最近编写的题解
This person is lazy and didn't write any solutions.
题目标签
- 初级班期中考
- 5
- 字符串
- 3
- 模拟
- 3
- 计算几何
- 2
- 排序
- 2
- 数学
- 2
- 数组
- 1
- for循环
- 1
- while循环
- 1
- 结构体
- 1
- 其他
- 1
- 搜索
- 1
- 枚举
- 1
- 数据结构
- 1
- 队列
- 1
- 初级期中考
- 1