Compare commits

...

2 Commits

Author SHA1 Message Date
JISAUAY
54a01e3a05 prepare code for a more dynamic world 2025-11-10 14:28:29 -06:00
JISAUAY
5f1bf88374 added camera control 2025-11-10 12:58:30 -06:00
4 changed files with 237 additions and 14 deletions

View File

@ -19,6 +19,7 @@ add_executable(VoxelEngine
src/Object3D.h src/Object3D.h
src/Object3D.cpp src/Object3D.cpp
src/Camera.h src/Camera.h
src/World.h
) )
# --- Configure GLAD (from your src/ and include/ folders) --- # --- Configure GLAD (from your src/ and include/ folders) ---

11
build.ps1 Normal file
View File

@ -0,0 +1,11 @@
cmake -S . -B build
cd build
cmake --build .
cd ..
cp vendor/lib-vc2022/glfw3.dll build/Debug
cp -r shaders/ build/Debug
cp -r objs/ build/Debug
cd build/Debug
.\VoxelEngine.exe

30
src/World.h Normal file
View File

@ -0,0 +1,30 @@
#ifndef WORLD_H
#define CAMERA_H
#include <unordered_set>
#include <glm/glm.hpp>
// Hash function for glm::ivec3 to use with unordered_set
namespace std {
template <>
struct hash<glm::ivec3> {
size_t operator()(const glm::ivec3& v) const {
// Combine hash values of x, y, z components
size_t h1 = hash<int>()(v.x);
size_t h2 = hash<int>()(v.y);
size_t h3 = hash<int>()(v.z);
// Use a simple hash combination algorithm
return h1 ^ (h2 << 1) ^ (h3 << 2);
}
};
}
class World
{
public:
std::unordered_set<glm::ivec3> voxels;
};
#endif WORLD_H

View File

@ -1,17 +1,25 @@
#include <iostream>
#include <unordered_set>
#include <glad/glad.h> // Must be included before GLFW #include <glad/glad.h> // Must be included before GLFW
#include <GLFW/glfw3.h> #include <GLFW/glfw3.h>
#include <iostream>
#include <glm/glm.hpp> #include <glm/glm.hpp>
#include "Shader.h" #include "Shader.h"
#include "Camera.h" #include "Camera.h"
#include "Object3D.h" #include "Object3D.h"
#include "World.h"
// Window dimensions // Window dimensions
const unsigned int SCR_WIDTH = 800; unsigned int SCR_WIDTH = 800 * 1.5;
const unsigned int SCR_HEIGHT = 600; unsigned int SCR_HEIGHT = 600 * 1.5;
// Jump
const float JUMP_COOLDOWN = 0.0f;
const float JUMP_POWER = 10.0f; // Initial Jump Speed
float last_jump = 0.0f;
// Camera
const float CAMERA_SPEED = 1.0f; const float CAMERA_SPEED = 1.0f;
const float MAX_CAMERA_SPEED = 2.0f; const float MAX_CAMERA_SPEED = 2.0f;
const float LOOK_SENSITIVITY = 0.1f; const float LOOK_SENSITIVITY = 0.1f;
@ -21,12 +29,162 @@ const float Z_NEAR = 0.1f;
bool WIREFRAME_MODE = true; bool WIREFRAME_MODE = true;
const float FOV = 90.0f; const float FOV = 90.0f;
bool W_pressed, S_pressed, A_pressed, D_pressed = false;
glm::mat4 projection = glm::mat4(1.0f); glm::mat4 projection = glm::mat4(1.0f);
Camera camera = Camera(CAMERA_SPEED, glm::vec3(0.0f, 0.0f, 3.0f)); Camera camera = Camera(CAMERA_SPEED, glm::vec3(0.0f, 0.0f, 3.0f));
// Callback function for when the window is resized // Callback function for when the window is resized
void framebuffer_size_callback(GLFWwindow* window, int width, int height) { // glfw: whenever the window size changed (by OS or user resize) this callback function executes
void framebuffer_size_callback(GLFWwindow* window, int width, int height)
{
// make sure the viewport matches the new window dimensions; note that width and
// height will be significantly larger than specified on retina displays.
glViewport(0, 0, width, height); glViewport(0, 0, width, height);
SCR_WIDTH = width;
SCR_HEIGHT = height;
// Perspective needs to be recalculated on window size change
projection = glm::perspective(glm::radians(FOV), (float)SCR_WIDTH / (float)SCR_HEIGHT, Z_NEAR, Z_FAR);
}
float yaw, pitch = 0;
bool first_mouse = true;
bool mouse_lock = true;
float last_x = 400, last_y = 300;
// Mouse Look
void mouse_callback(GLFWwindow * window, double xpos, double ypos)
{
float x_offset = xpos - last_x;
float y_offset = ypos - last_y;
last_x = xpos;
last_y = ypos;
if (first_mouse)
{
first_mouse = false;
return;
}
x_offset *= LOOK_SENSITIVITY;
y_offset *= LOOK_SENSITIVITY;
yaw += x_offset;
pitch += y_offset;
if (pitch > 89.0f)
pitch = 89.0f;
if (pitch < -89.0f)
pitch = -89.0f;
glm::vec3 direction;
direction.x = cos(glm::radians(yaw)) * cos(glm::radians(pitch));
direction.y = -sin(glm::radians(pitch));
direction.z = sin(glm::radians(yaw)) * cos(glm::radians(pitch));
camera.target = glm::normalize(direction);
}
// process all input: query GLFW whether relevant keys are pressed/released this frame and react accordingly
void processInput(GLFWwindow *window)
{
float current_time = glfwGetTime();
if(glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS) {
// if (mouse_lock) {
// glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_NORMAL);
// mouse_lock = false;
// } else {
// glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);
// mouse_lock = true;
// }
glfwSetWindowShouldClose(window, true);
}
// ---- Movement ----
// Forward / Backward
if(glfwGetKey(window, GLFW_KEY_W) == GLFW_PRESS)
{
camera.forward_velocity = camera.speed * glm::vec3(camera.target.x, 0.0f, camera.target.z);
W_pressed = true;
}
if(glfwGetKey(window, GLFW_KEY_S) == GLFW_PRESS)
{
camera.forward_velocity = -camera.speed * glm::vec3(camera.target.x, 0.0f, camera.target.z);
S_pressed = true;
}
if(glfwGetKey(window, GLFW_KEY_S) == GLFW_RELEASE && S_pressed)
{
camera.forward_velocity = glm::vec3(0.0f);
S_pressed = false;
}
if(glfwGetKey(window, GLFW_KEY_W) == GLFW_RELEASE && W_pressed)
{
camera.forward_velocity = glm::vec3(0.0f);
W_pressed = false;
}
// Horizontal
if(glfwGetKey(window, GLFW_KEY_A) == GLFW_PRESS)
{
camera.horizontal_velocity = -glm::normalize(glm::cross(glm::vec3(camera.target.x, 0.0f, camera.target.z), camera.worldUp)) * camera.speed;
A_pressed = true;
}
if(glfwGetKey(window, GLFW_KEY_D) == GLFW_PRESS)
{
camera.horizontal_velocity = glm::normalize(glm::cross(glm::vec3(camera.target.x, 0.0f, camera.target.z), camera.worldUp)) * camera.speed;
D_pressed = true;
}
if(glfwGetKey(window, GLFW_KEY_A) == GLFW_RELEASE && A_pressed)
{
camera.horizontal_velocity = glm::vec3(0.0f);
A_pressed = false;
}
if(glfwGetKey(window, GLFW_KEY_D) == GLFW_RELEASE && D_pressed)
{
camera.horizontal_velocity = glm::vec3(0.0f);
D_pressed = false;
}
// Jump
if(glfwGetKey(window, GLFW_KEY_SPACE) == GLFW_PRESS)
// camera.velocity -= glm::normalize(glm::cross(camera.target, glm::normalize(glm::cross(camera.target, camera.worldUp)))) * camera.speed * 0.5f;
{
if (current_time - last_jump > JUMP_COOLDOWN && camera.position.y == 0)
{
camera.vertical_velocity.y = JUMP_POWER;
last_jump = current_time;
}
}
// Sprint
if(glfwGetKey(window, GLFW_KEY_LEFT_SHIFT) == GLFW_PRESS)
camera.speed = CAMERA_SPEED * 7.5;
if(glfwGetKey(window, GLFW_KEY_LEFT_SHIFT) == GLFW_RELEASE)
camera.speed = CAMERA_SPEED * 20;
}
// Draw a cube at some x, y, z
void draw_cube(glm::ivec3 coords, Shader& shader, unsigned int VAO, size_t indexCount, const glm::mat4& view, const glm::mat4& projection) {
// Create model matrix for positioning the cube
glm::mat4 model = glm::mat4(1.0f);
model = glm::translate(model, glm::vec3(coords));
// Calculate MVP matrix
glm::mat4 mvp = projection * view * model;
// Set the uniform and draw
shader.use();
shader.setMat4("u_mvp", mvp);
glBindVertexArray(VAO);
glDrawElements(GL_TRIANGLES, indexCount, GL_UNSIGNED_INT, nullptr);
} }
int main() { int main() {
@ -61,8 +219,18 @@ int main() {
// 5. --- Set up Vertex Data and Buffers --- // 5. --- Set up Vertex Data and Buffers ---
// Load Cube OBJ // Load Cube OBJ
World world;
Object3D cube = Object3D("objs/cube.obj"); Object3D cube = Object3D("objs/cube.obj");
// Place cubes in the world
for (int z = 0; z <= 32; z++) {
for (int x = 0; x <= 32; x++) {
world.voxels.insert(glm::ivec3(x, -1, z));
}
}
world.voxels.insert(glm::ivec3(7, 0, 13));
unsigned int VBO, VAO, EBO; unsigned int VBO, VAO, EBO;
glGenVertexArrays(1, &VAO); // 1. Create Vertex Array Object (VAO) glGenVertexArrays(1, &VAO); // 1. Create Vertex Array Object (VAO)
glGenBuffers(1, &VBO); // 2. Create Vertex Buffer Object (VBO) glGenBuffers(1, &VBO); // 2. Create Vertex Buffer Object (VBO)
@ -95,26 +263,39 @@ int main() {
if (WIREFRAME_MODE) if (WIREFRAME_MODE)
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
// Setup mouse look
glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);
glfwSetCursorPosCallback(window, mouse_callback);
// Setup projection matrix // Setup projection matrix
projection = glm::perspective(glm::radians(FOV), (float)SCR_WIDTH / (float)SCR_HEIGHT, Z_NEAR, Z_FAR); // This doesn't need to be recalculated every frame projection = glm::perspective(glm::radians(FOV), (float)SCR_WIDTH / (float)SCR_HEIGHT, Z_NEAR, Z_FAR); // This doesn't need to be recalculated every frame
glm::mat4 view = camera.getView(); glm::mat4 view;
double start_time = glfwGetTime();
double move_time = glfwGetTime();
// 6. --- The Render Loop --- // 6. --- The Render Loop ---
while (!glfwWindowShouldClose(window)) { while (!glfwWindowShouldClose(window)) {
// Input (e.g., close window on ESC) // Input (e.g., close window on ESC)
// processInput(window); processInput(window);
if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
glfwSetWindowShouldClose(window, true);
glClearColor(0.0f, 0.0f, 0.0f, 1.0f); // Clear screen glClearColor(0.0f, 0.0f, 0.0f, 1.0f); // Clear screen
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // Clear Z-Buffer glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // Clear Z-Buffer
// Draw the triangle view = camera.getView();
shader.use();
shader.setMat4("u_mvp", projection * view); // Draw the World
glBindVertexArray(VAO); // Bind the VAO (our triangle's "recipe") for (glm::ivec3 voxel_pos : world.voxels) {
glDrawElements(GL_TRIANGLES, cube.EBO_buffer.size(), GL_UNSIGNED_INT, nullptr); // Draw it! draw_cube(voxel_pos, shader, VAO, cube.EBO_buffer.size(), view, projection);
}
double end_time = glfwGetTime();
float time_since_last_move = end_time - move_time;
if (time_since_last_move >= 0.017)
{
camera.move(time_since_last_move);
move_time = end_time;
}
// Swap buffers and poll for events // Swap buffers and poll for events
glfwSwapBuffers(window); glfwSwapBuffers(window);