small-projects/cpp-voxel-engine/main.cpp

165 lines
4.8 KiB
C++
Raw Normal View History

2025-04-06 02:03:23 +00:00
#include <iostream>
2025-04-06 03:14:06 +00:00
#include <cmath>
2025-04-06 04:20:54 +00:00
2025-04-06 02:09:28 +00:00
#include <GL/glew.h>
2025-04-06 02:03:23 +00:00
#include <GLFW/glfw3.h>
2025-04-06 04:20:54 +00:00
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
2025-04-06 02:03:23 +00:00
#include "imgui.h"
#include "imgui_impl_glfw.h"
#include "imgui_impl_opengl3.h"
2025-04-06 04:20:54 +00:00
2025-04-06 03:14:06 +00:00
#include "VoxelGame.h"
2025-04-06 04:20:54 +00:00
// camera globals
static glm::vec3 cameraPos = {32.0f, 32.0f, 80.0f};
static glm::vec3 cameraFront = {0.0f, 0.0f, -1.0f};
static glm::vec3 cameraUp = {0.0f, 1.0f, 0.0f};
static float yaw = -90.0f, pitch = 0.0f;
static bool firstMouse = true;
static float lastX, lastY;
2025-04-06 03:14:06 +00:00
2025-04-06 04:20:54 +00:00
static void mouse_callback(GLFWwindow* window, double xpos, double ypos) {
2025-04-06 03:14:06 +00:00
if (glfwGetInputMode(window, GLFW_CURSOR) == GLFW_CURSOR_NORMAL)
2025-04-06 04:20:54 +00:00
return; // do not rotate if cursor is free
2025-04-06 03:14:06 +00:00
if (firstMouse) {
2025-04-06 04:20:54 +00:00
lastX = float(xpos);
lastY = float(ypos);
2025-04-06 03:14:06 +00:00
firstMouse = false;
}
2025-04-06 04:20:54 +00:00
float dx = float(xpos) - lastX;
float dy = lastY - float(ypos);
lastX = float(xpos);
lastY = float(ypos);
const float sensitivity = 0.1f;
dx *= sensitivity;
dy *= sensitivity;
yaw += dx;
pitch += dy;
pitch = std::clamp(pitch, -89.0f, 89.0f);
float ry = glm::radians(yaw);
float rp = glm::radians(pitch);
cameraFront = glm::normalize(glm::vec3(
cos(ry)*cos(rp),
sin(rp),
sin(ry)*cos(rp)
));
2025-04-06 03:14:06 +00:00
}
2025-04-06 02:03:23 +00:00
2025-04-06 04:20:54 +00:00
static GLFWwindow* initWindow(int w, int h, const char* title) {
if (!glfwInit()) return nullptr;
2025-04-06 02:03:23 +00:00
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
2025-04-06 04:20:54 +00:00
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
#ifdef __APPLE__
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GLFW_TRUE);
#endif
GLFWwindow* win = glfwCreateWindow(w, h, title, nullptr, nullptr);
if (!win) { glfwTerminate(); return nullptr; }
glfwMakeContextCurrent(win);
2025-04-06 03:14:06 +00:00
glewExperimental = GL_TRUE;
if (glewInit() != GLEW_OK) {
2025-04-06 04:20:54 +00:00
glfwDestroyWindow(win);
glfwTerminate();
2025-04-06 03:14:06 +00:00
return nullptr;
}
2025-04-06 04:20:54 +00:00
glViewport(0, 0, w, h);
glfwSetCursorPosCallback(win, mouse_callback);
// start with cursor disabled
glfwSetInputMode(win, GLFW_CURSOR, GLFW_CURSOR_DISABLED);
lastX = w / 2.0f;
lastY = h / 2.0f;
return win;
2025-04-06 02:03:23 +00:00
}
int main() {
2025-04-06 04:20:54 +00:00
const int SCR_W = 1280, SCR_H = 720;
GLFWwindow* window = initWindow(SCR_W, SCR_H, "Voxel CSM");
if (!window) return -1;
// ImGui
2025-04-06 02:03:23 +00:00
IMGUI_CHECKVERSION();
ImGui::CreateContext();
ImGui_ImplGlfw_InitForOpenGL(window, true);
ImGui_ImplOpenGL3_Init("#version 330");
2025-04-06 04:20:54 +00:00
ImGui::StyleColorsDark();
// Our game
2025-04-06 02:03:23 +00:00
VoxelGame game;
2025-04-06 04:20:54 +00:00
if (!game.init(SCR_W, SCR_H)) {
std::cerr << "Failed to init VoxelGame\n";
2025-04-06 02:03:23 +00:00
return -1;
}
2025-04-06 04:20:54 +00:00
float lastFrame = 0.0f;
bool cursorEnabled = false;
bool f1Pressed = false;
2025-04-06 02:03:23 +00:00
while (!glfwWindowShouldClose(window)) {
2025-04-06 04:20:54 +00:00
float now = float(glfwGetTime());
float dt = now - lastFrame;
lastFrame = now;
2025-04-06 02:03:23 +00:00
glfwPollEvents();
2025-04-06 04:20:54 +00:00
// Toggle cursor with F1 (when ImGui not capturing)
ImGuiIO& io = ImGui::GetIO();
if (glfwGetKey(window, GLFW_KEY_F1) == GLFW_PRESS && !io.WantCaptureMouse) {
2025-04-06 03:14:06 +00:00
if (!f1Pressed) {
cursorEnabled = !cursorEnabled;
2025-04-06 04:20:54 +00:00
glfwSetInputMode(window, GLFW_CURSOR,
cursorEnabled ? GLFW_CURSOR_NORMAL : GLFW_CURSOR_DISABLED);
2025-04-06 03:14:06 +00:00
f1Pressed = true;
2025-04-06 04:20:54 +00:00
firstMouse = true; // reset for mouse callback
2025-04-06 03:14:06 +00:00
}
} else if (glfwGetKey(window, GLFW_KEY_F1) == GLFW_RELEASE) {
f1Pressed = false;
}
2025-04-06 04:20:54 +00:00
// WASD movement
float speed = 5.0f * dt;
if (glfwGetKey(window, GLFW_KEY_W) == GLFW_PRESS)
cameraPos += cameraFront * speed;
if (glfwGetKey(window, GLFW_KEY_S) == GLFW_PRESS)
cameraPos -= cameraFront * speed;
glm::vec3 right = glm::normalize(glm::cross(cameraFront, cameraUp));
if (glfwGetKey(window, GLFW_KEY_A) == GLFW_PRESS)
cameraPos -= right * speed;
if (glfwGetKey(window, GLFW_KEY_D) == GLFW_PRESS)
cameraPos += right * speed;
// Build view/proj
glm::mat4 view = glm::lookAt(cameraPos, cameraPos + cameraFront, cameraUp);
glm::mat4 proj = glm::perspective(glm::radians(45.0f),
float(SCR_W)/SCR_H, 0.1f, 200.0f);
// Update & render
game.update(dt, cameraPos);
game.render(view, proj);
// Debug UI
2025-04-06 02:03:23 +00:00
ImGui_ImplOpenGL3_NewFrame();
ImGui_ImplGlfw_NewFrame();
ImGui::NewFrame();
game.debugUI();
ImGui::Render();
ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
2025-04-06 04:20:54 +00:00
2025-04-06 02:03:23 +00:00
glfwSwapBuffers(window);
}
2025-04-06 04:20:54 +00:00
// Cleanup
2025-04-06 02:03:23 +00:00
ImGui_ImplOpenGL3_Shutdown();
ImGui_ImplGlfw_Shutdown();
ImGui::DestroyContext();
glfwDestroyWindow(window);
glfwTerminate();
return 0;
}