added some really bad raycasting
This commit is contained in:
parent
46be697e2f
commit
ae5b2a0185
BIN
.attachments/raycasting_test.png
Normal file
BIN
.attachments/raycasting_test.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 44 KiB |
6
Notes.md
Normal file
6
Notes.md
Normal file
@ -0,0 +1,6 @@
|
||||
|
||||
These are just some notes on the blogpost I was following. While it was great, I thought the author might want to know where some areas were lacking (i would).
|
||||
|
||||
- in the definition of raycast_voxel `step` is never defined, nor explained. I'm pretty sure it's just supposed to be `step_dir` though.
|
||||
- `get_voxel` returns the type `Voxel?` but `Chunk.voxels` doesn't hold a type named `Voxel` only a HashMap<ivec3, VoxelKind>
|
||||
|
||||
@ -29,3 +29,9 @@ Chunks now generate around the player in a square based on a render distance. Th
|
||||
Different voxel types are now supported in the data structures and passed to the shaders.
|
||||
|
||||

|
||||
|
||||
## 0.6.0 : Raycasting Placing - 11/13/25
|
||||
|
||||
When placing a block, the program now raycasts to place where the camera is looking.
|
||||
|
||||

|
||||
|
||||
14
src/Camera.h
14
src/Camera.h
@ -4,7 +4,7 @@
|
||||
#include <glm/glm.hpp>
|
||||
#include <glm/gtc/matrix_transform.hpp>
|
||||
|
||||
const float CAMERA_HEIGHT = 1.7f;
|
||||
const float CAMERA_HEIGHT = 1.8f;
|
||||
const float GRAVITY = 18.6f; // Should be in m/s^2 but it doesn't really work out with 9.8 so we double it
|
||||
|
||||
class Camera
|
||||
@ -62,11 +62,12 @@ class Camera
|
||||
|
||||
glm::mat4 getView()
|
||||
{
|
||||
glm::mat4 v = glm::lookAt(this->position,
|
||||
this->target + this->position,
|
||||
this->worldUp);
|
||||
glm::vec3 real_camera_pos = this->position;
|
||||
real_camera_pos.y += CAMERA_HEIGHT;
|
||||
|
||||
v = glm::translate(v, glm::vec3(0.0f, -CAMERA_HEIGHT, 0.0f));
|
||||
glm::mat4 v = glm::lookAt(real_camera_pos,
|
||||
real_camera_pos + this->target,
|
||||
this->worldUp);
|
||||
|
||||
return v;
|
||||
}
|
||||
@ -92,6 +93,9 @@ class Camera
|
||||
|
||||
}
|
||||
|
||||
glm::vec3 getEyePosition() const {
|
||||
return position + glm::vec3(0.0f, CAMERA_HEIGHT, 0.0f);
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@ -46,9 +46,19 @@ void addFace(glm::ivec3 pos, const float *faceVertices, float voxelKind, std::ve
|
||||
unsigned int vIndex = vbo.size() / VERTEX_SIZE;
|
||||
for (int i = 0; i < 4; i++) {
|
||||
const float* v = &faceVertices[i * 6]; // Face data is still 6 floats per vertex
|
||||
vbo.push_back(v[0] + pos.x);
|
||||
vbo.push_back(v[1] + pos.y);
|
||||
vbo.push_back(v[2] + pos.z);
|
||||
float world_x = v[0] + pos.x;
|
||||
float world_y = v[1] + pos.y;
|
||||
float world_z = v[2] + pos.z;
|
||||
|
||||
// Debug: Print first vertex of first face
|
||||
if (vbo.empty() && i == 0) {
|
||||
std::cout << "[MESH] First vertex - Local pos: (" << pos.x << ", " << pos.y << ", " << pos.z << ")" << std::endl;
|
||||
std::cout << "[MESH] First vertex - World: (" << world_x << ", " << world_y << ", " << world_z << ")" << std::endl;
|
||||
}
|
||||
|
||||
vbo.push_back(world_x);
|
||||
vbo.push_back(world_y);
|
||||
vbo.push_back(world_z);
|
||||
vbo.push_back(v[3]);
|
||||
vbo.push_back(v[4]);
|
||||
vbo.push_back(v[5]);
|
||||
@ -117,5 +127,10 @@ void Chunk::generateMesh() {
|
||||
}
|
||||
|
||||
void Chunk::regenerateMesh() {
|
||||
std::cout << "[REGENERATE] Regenerating mesh with " << voxels.size() << " voxels" << std::endl;
|
||||
if (!voxels.empty()) {
|
||||
auto first = voxels.begin();
|
||||
std::cout << "[REGENERATE] First voxel at local: (" << first->first.x << ", " << first->first.y << ", " << first->first.z << ")" << std::endl;
|
||||
}
|
||||
this->generateMesh();
|
||||
}
|
||||
|
||||
119
src/World.cpp
119
src/World.cpp
@ -1,10 +1,34 @@
|
||||
#include "World.h"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
// Helper function to handle negative modulo correctly
|
||||
glm::ivec3 positive_modulo(glm::ivec3 value, int divisor) {
|
||||
glm::ivec3 result;
|
||||
result.x = ((value.x % divisor) + divisor) % divisor;
|
||||
result.y = ((value.y % divisor) + divisor) % divisor;
|
||||
result.z = ((value.z % divisor) + divisor) % divisor;
|
||||
return result;
|
||||
}
|
||||
|
||||
// Helper function to correctly floor divide for chunk positions
|
||||
glm::ivec3 floor_divide(glm::ivec3 value, int divisor) {
|
||||
glm::ivec3 result;
|
||||
result.x = (int)std::floor((float)value.x / divisor);
|
||||
result.y = (int)std::floor((float)value.y / divisor);
|
||||
result.z = (int)std::floor((float)value.z / divisor);
|
||||
return result;
|
||||
}
|
||||
|
||||
World::World() {}
|
||||
|
||||
void World::set_voxel(glm::ivec3 position, std::optional<VoxelKind> kind) {
|
||||
glm::ivec3 chunk_position = position / CHUNK_SIZE;
|
||||
glm::ivec3 local_position = position % CHUNK_SIZE;
|
||||
glm::ivec3 chunk_position = floor_divide(position, CHUNK_SIZE);
|
||||
glm::ivec3 local_position = positive_modulo(position, CHUNK_SIZE);
|
||||
|
||||
std::cout << "[SET_VOXEL] World pos: (" << position.x << ", " << position.y << ", " << position.z << ")" << std::endl;
|
||||
std::cout << "[SET_VOXEL] Chunk pos: (" << chunk_position.x << ", " << chunk_position.y << ", " << chunk_position.z << ")" << std::endl;
|
||||
std::cout << "[SET_VOXEL] Local pos: (" << local_position.x << ", " << local_position.y << ", " << local_position.z << ")" << std::endl;
|
||||
|
||||
Chunk * chunk = get_or_create_chunk(chunk_position);
|
||||
if (!kind.has_value()) {
|
||||
@ -22,11 +46,11 @@ Chunk* World::get_or_create_chunk(glm::ivec3 chunk_position) {
|
||||
// Fill in new chunk with a flat plane of voxels at y=0
|
||||
std::unordered_map<glm::ivec3, VoxelKind> voxels;
|
||||
if (chunk_position.y == 0) {
|
||||
for (int z = 0; z <= CHUNK_SIZE; z++) {
|
||||
for (int y = 0; y <= CHUNK_SIZE; y++) {
|
||||
for (int x = 0; x <= CHUNK_SIZE; x++) {
|
||||
for (int z = 0; z < CHUNK_SIZE; z++) {
|
||||
for (int y = 0; y < CHUNK_SIZE; y++) {
|
||||
for (int x = 0; x < CHUNK_SIZE; x++) {
|
||||
glm::ivec3 offset = glm::ivec3(x, y, z);
|
||||
glm::ivec3 world_voxel_position = chunk_position * 32 + offset;
|
||||
glm::ivec3 world_voxel_position = chunk_position * CHUNK_SIZE + offset;
|
||||
|
||||
if (world_voxel_position.y == 0) {
|
||||
voxels[offset] = VoxelKind::Dirt;
|
||||
@ -42,3 +66,86 @@ Chunk* World::get_or_create_chunk(glm::ivec3 chunk_position) {
|
||||
}
|
||||
return &this->chunks[chunk_position];
|
||||
}
|
||||
|
||||
std::optional<std::pair<glm::ivec3, glm::ivec3>> World::raycast_voxel(glm::vec3 start, glm::vec3 direction, float max_dist) {
|
||||
// Normalize direction to ensure consistent behavior
|
||||
direction = glm::normalize(direction);
|
||||
|
||||
std::cout << "[RAYCAST] Start: (" << start.x << ", " << start.y << ", " << start.z << ")" << std::endl;
|
||||
std::cout << "[RAYCAST] Direction: (" << direction.x << ", " << direction.y << ", " << direction.z << ")" << std::endl;
|
||||
|
||||
glm::ivec3 pos = glm::ivec3(std::floor(start.x), std::floor(start.y), std::floor(start.z));
|
||||
std::cout << "[RAYCAST] Starting voxel: (" << pos.x << ", " << pos.y << ", " << pos.z << ")" << std::endl;
|
||||
|
||||
// (+1 or -1 for each axis)
|
||||
glm::ivec3 step_dir = glm::sign(direction);
|
||||
std::cout << "[RAYCAST] Step direction: (" << step_dir.x << ", " << step_dir.y << ", " << step_dir.z << ")" << std::endl;
|
||||
|
||||
// How far to step in each axis
|
||||
const float epsilon = 0.0001f;
|
||||
glm::vec3 safe_dir = glm::vec3(
|
||||
abs(direction.x) < epsilon ? epsilon : direction.x,
|
||||
abs(direction.y) < epsilon ? epsilon : direction.y,
|
||||
abs(direction.z) < epsilon ? epsilon : direction.z
|
||||
);
|
||||
|
||||
glm::vec3 delta = glm::abs(1.0f / safe_dir);
|
||||
|
||||
glm::vec3 fract = start - glm::vec3(pos);
|
||||
glm::vec3 t_max = glm::vec3(
|
||||
(step_dir.x > 0 ? (1.0f - fract.x) : fract.x) * delta.x,
|
||||
(step_dir.y > 0 ? (1.0f - fract.y) : fract.y) * delta.y,
|
||||
(step_dir.z > 0 ? (1.0f - fract.z) : fract.z) * delta.z
|
||||
);
|
||||
|
||||
float dist = 0.0f;
|
||||
glm::ivec3 last_move = glm::ivec3(0, 0, 0);
|
||||
|
||||
int iterations = 0;
|
||||
const int max_iterations = 100; // Safety limit
|
||||
|
||||
while (dist < max_dist && iterations < max_iterations) {
|
||||
iterations++;
|
||||
|
||||
std::cout << "[RAYCAST] Iteration " << iterations << " at voxel (" << pos.x << ", " << pos.y << ", " << pos.z << ")" << std::endl;
|
||||
|
||||
if (this->get_voxel(pos).has_value()) {
|
||||
std::cout << "[RAYCAST] Hit voxel at (" << pos.x << ", " << pos.y << ", " << pos.z << ") after " << iterations << " iterations" << std::endl;
|
||||
std::cout << "[RAYCAST] Returning normal: (" << -last_move.x << ", " << -last_move.y << ", " << -last_move.z << ")" << std::endl;
|
||||
return std::make_pair(pos, -last_move);
|
||||
}
|
||||
|
||||
// step in the axis with the smallest t_max - that's the next voxel boundary
|
||||
if (t_max.x < t_max.y && t_max.x < t_max.z) {
|
||||
pos.x += step_dir.x;
|
||||
last_move = glm::ivec3(step_dir.x, 0, 0);
|
||||
dist = t_max.x;
|
||||
t_max.x += delta.x;
|
||||
} else if (t_max.y < t_max.z) {
|
||||
pos.y += step_dir.y;
|
||||
last_move = glm::ivec3(0, step_dir.y, 0);
|
||||
dist = t_max.y;
|
||||
t_max.y += delta.y;
|
||||
} else {
|
||||
pos.z += step_dir.z;
|
||||
last_move = glm::ivec3(0, 0, step_dir.z);
|
||||
dist = t_max.z;
|
||||
t_max.z += delta.z;
|
||||
}
|
||||
}
|
||||
|
||||
std::cout << "[RAYCAST] No voxel hit after " << iterations << " iterations, dist=" << dist << std::endl;
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::optional<VoxelKind> World::get_voxel(glm::ivec3 position) {
|
||||
glm::ivec3 chunk_position = floor_divide(position, CHUNK_SIZE);
|
||||
glm::ivec3 local_position = positive_modulo(position, CHUNK_SIZE);
|
||||
|
||||
Chunk* chunk = this->get_or_create_chunk(chunk_position);
|
||||
auto it = chunk->voxels.find(local_position);
|
||||
if (it != chunk->voxels.end()) {
|
||||
return it->second;
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
@ -17,6 +17,10 @@ class World
|
||||
|
||||
void set_voxel(glm::ivec3 position, std::optional<VoxelKind> kind);
|
||||
|
||||
std::optional<std::pair<glm::ivec3, glm::ivec3>> raycast_voxel(glm::vec3 start, glm::vec3 direction, float max_dist);
|
||||
|
||||
std::optional<VoxelKind> get_voxel(glm::ivec3 position);
|
||||
|
||||
Chunk* get_or_create_chunk(glm::ivec3 chunk_position);
|
||||
};
|
||||
|
||||
|
||||
129
src/main.cpp
129
src/main.cpp
@ -33,7 +33,7 @@ const int VIEW_DISTANCE = 4; // In Chunks
|
||||
bool W_pressed, S_pressed, A_pressed, D_pressed = false;
|
||||
|
||||
glm::mat4 projection = glm::mat4(1.0f);
|
||||
Camera camera = Camera(CAMERA_SPEED, glm::vec3(0.0f, 1.0f, 3.0f));
|
||||
Camera camera = Camera(CAMERA_SPEED, glm::vec3(0.0f, 0.0f, 0.0f));
|
||||
|
||||
// Callback function for when the window is resized
|
||||
// glfw: whenever the window size changed (by OS or user resize) this callback function executes
|
||||
@ -86,10 +86,6 @@ void mouse_callback(GLFWwindow * window, double xpos, double ypos)
|
||||
direction.y = -sin(glm::radians(pitch));
|
||||
direction.z = sin(glm::radians(yaw)) * cos(glm::radians(pitch));
|
||||
camera.target = glm::normalize(direction);
|
||||
|
||||
if(glfwGetMouseButton(window, GLFW_MOUSE_BUTTON_2) == GLFW_PRESS) {
|
||||
world.set_voxel(glm::ivec3(camera.position), VoxelKind::Stone);
|
||||
}
|
||||
}
|
||||
|
||||
// process all input: query GLFW whether relevant keys are pressed/released this frame and react accordingly
|
||||
@ -176,6 +172,57 @@ void processInput(GLFWwindow *window)
|
||||
camera.speed = CAMERA_SPEED * 7.5;
|
||||
if(glfwGetKey(window, GLFW_KEY_LEFT_SHIFT) == GLFW_RELEASE)
|
||||
camera.speed = CAMERA_SPEED * 20;
|
||||
|
||||
static bool left_mouse_pressed = false;
|
||||
static bool right_mouse_pressed = false;
|
||||
|
||||
// Left click - place block
|
||||
if (glfwGetMouseButton(window, GLFW_MOUSE_BUTTON_LEFT) == GLFW_PRESS && !left_mouse_pressed) {
|
||||
left_mouse_pressed = true;
|
||||
std::cout << "\n=== LEFT CLICK (PLACE) ===" << std::endl;
|
||||
std::cout << "Camera eye pos: (" << camera.getEyePosition().x << ", " << camera.getEyePosition().y << ", " << camera.getEyePosition().z << ")" << std::endl;
|
||||
std::cout << "Camera target: (" << camera.target.x << ", " << camera.target.y << ", " << camera.target.z << ")" << std::endl;
|
||||
|
||||
std::optional<std::pair<glm::ivec3, glm::ivec3>> raycast_result =
|
||||
world.raycast_voxel(camera.getEyePosition(), camera.target, 3);
|
||||
|
||||
if (raycast_result.has_value()) {
|
||||
auto [target_block, normal] = raycast_result.value();
|
||||
std::cout << "Hit block at: (" << target_block.x << ", " << target_block.y << ", " << target_block.z << ")" << std::endl;
|
||||
std::cout << "Normal: (" << normal.x << ", " << normal.y << ", " << normal.z << ")" << std::endl;
|
||||
glm::ivec3 place_pos = target_block + normal;
|
||||
std::cout << "Placing at: (" << place_pos.x << ", " << place_pos.y << ", " << place_pos.z << ")" << std::endl;
|
||||
world.set_voxel(place_pos, VoxelKind::Stone);
|
||||
} else {
|
||||
std::cout << "No block hit within range" << std::endl;
|
||||
}
|
||||
}
|
||||
if (glfwGetMouseButton(window, GLFW_MOUSE_BUTTON_LEFT) == GLFW_RELEASE) {
|
||||
left_mouse_pressed = false;
|
||||
}
|
||||
|
||||
// Right click - remove block
|
||||
if (glfwGetMouseButton(window, GLFW_MOUSE_BUTTON_RIGHT) == GLFW_PRESS && !right_mouse_pressed) {
|
||||
right_mouse_pressed = true;
|
||||
std::cout << "\n=== RIGHT CLICK (REMOVE) ===" << std::endl;
|
||||
std::cout << "Camera eye pos: (" << camera.getEyePosition().x << ", " << camera.getEyePosition().y << ", " << camera.getEyePosition().z << ")" << std::endl;
|
||||
std::cout << "Camera target: (" << camera.target.x << ", " << camera.target.y << ", " << camera.target.z << ")" << std::endl;
|
||||
|
||||
std::optional<std::pair<glm::ivec3, glm::ivec3>> raycast_result =
|
||||
world.raycast_voxel(camera.getEyePosition(), camera.target, 3);
|
||||
|
||||
if (raycast_result.has_value()) {
|
||||
auto [target_block, normal] = raycast_result.value();
|
||||
std::cout << "Hit block at: (" << target_block.x << ", " << target_block.y << ", " << target_block.z << ")" << std::endl;
|
||||
std::cout << "Removing block at: (" << target_block.x << ", " << target_block.y << ", " << target_block.z << ")" << std::endl;
|
||||
world.set_voxel(target_block, std::nullopt);
|
||||
} else {
|
||||
std::cout << "No block hit within range" << std::endl;
|
||||
}
|
||||
}
|
||||
if (glfwGetMouseButton(window, GLFW_MOUSE_BUTTON_RIGHT) == GLFW_RELEASE) {
|
||||
right_mouse_pressed = false;
|
||||
}
|
||||
}
|
||||
|
||||
int main() {
|
||||
@ -189,7 +236,7 @@ int main() {
|
||||
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
|
||||
|
||||
// 2. --- Create a Window ---
|
||||
GLFWwindow* window = glfwCreateWindow(SCR_WIDTH, SCR_HEIGHT, "Voxel Engine 0.5.0", NULL, NULL);
|
||||
GLFWwindow* window = glfwCreateWindow(SCR_WIDTH, SCR_HEIGHT, "Voxel Engine 0.6.0", NULL, NULL);
|
||||
if (window == NULL) {
|
||||
std::cerr << "Failed to create GLFW window" << std::endl;
|
||||
glfwTerminate();
|
||||
@ -227,6 +274,30 @@ int main() {
|
||||
// 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
|
||||
|
||||
// Setup crosshair
|
||||
unsigned int crosshairVAO, crosshairVBO;
|
||||
glGenVertexArrays(1, &crosshairVAO);
|
||||
glGenBuffers(1, &crosshairVBO);
|
||||
|
||||
glBindVertexArray(crosshairVAO);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, crosshairVBO);
|
||||
|
||||
// Crosshair vertices in NDC (Normalized Device Coordinates: -1 to 1)
|
||||
float crosshairSize = 0.02f; // Size in NDC
|
||||
float crosshairVertices[] = {
|
||||
// Horizontal line
|
||||
-crosshairSize, 0.0f, 0.0f,
|
||||
crosshairSize, 0.0f, 0.0f,
|
||||
// Vertical line
|
||||
0.0f, -crosshairSize, 0.0f,
|
||||
0.0f, crosshairSize, 0.0f
|
||||
};
|
||||
|
||||
glBufferData(GL_ARRAY_BUFFER, sizeof(crosshairVertices), crosshairVertices, GL_STATIC_DRAW);
|
||||
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
|
||||
glEnableVertexAttribArray(0);
|
||||
glBindVertexArray(0);
|
||||
|
||||
glm::mat4 view;
|
||||
double start_time = glfwGetTime();
|
||||
double move_time = glfwGetTime();
|
||||
@ -246,15 +317,19 @@ int main() {
|
||||
|
||||
shader.use();
|
||||
|
||||
if (!debug_printed) {
|
||||
std::cout << "\n=== RENDER DEBUG ===" << std::endl;
|
||||
std::cout << "Camera pos: (" << camera.position.x << ", " << camera.position.y << ", " << camera.position.z << ")" << std::endl;
|
||||
std::cout << "Camera target: (" << camera.target.x << ", " << camera.target.y << ", " << camera.target.z << ")" << std::endl;
|
||||
// std::cout << "Number of chunks to draw: " << world.chunks.size() << std::endl;
|
||||
}
|
||||
// if (!debug_printed) {
|
||||
// std::cout << "\n=== RENDER DEBUG ===" << std::endl;
|
||||
// std::cout << "Camera pos: (" << camera.position.x << ", " << camera.position.y << ", " << camera.position.z << ")" << std::endl;
|
||||
// std::cout << "Camera target: (" << camera.target.x << ", " << camera.target.y << ", " << camera.target.z << ")" << std::endl;
|
||||
// // std::cout << "Number of chunks to draw: " << world.chunks.size() << std::endl;
|
||||
// }
|
||||
|
||||
// Generate chunks around the camera
|
||||
glm::ivec3 camera_chunk = glm::ivec3(camera.position) / CHUNK_SIZE;
|
||||
glm::ivec3 camera_chunk = glm::ivec3(
|
||||
std::floor(camera.position.x / CHUNK_SIZE),
|
||||
std::floor(camera.position.y / CHUNK_SIZE),
|
||||
std::floor(camera.position.z / CHUNK_SIZE)
|
||||
);
|
||||
|
||||
// ---- PASS 1: Draw solid grey cubes ----
|
||||
glEnable(GL_POLYGON_OFFSET_FILL);
|
||||
@ -276,16 +351,26 @@ int main() {
|
||||
Chunk* chunk = world.get_or_create_chunk(chunk_pos);
|
||||
|
||||
Mesh* chunk_mesh = chunk->getMesh();
|
||||
if (chunk_mesh)
|
||||
if (chunk_mesh) {
|
||||
// Debug: Log when rendering chunks with your placed block
|
||||
static bool logged_placed_block = false;
|
||||
if (!logged_placed_block && chunk->voxels.count(glm::ivec3(0, 1, 31)) > 0) {
|
||||
std::cout << "\n[RENDER] ===== FOUND PLACED BLOCK =====" << std::endl;
|
||||
std::cout << "[RENDER] Drawing chunk at pos (" << chunk_pos.x << ", " << chunk_pos.y << ", " << chunk_pos.z << ")" << std::endl;
|
||||
std::cout << "[RENDER] Translation: (" << (chunk_pos.x * CHUNK_SIZE) << ", " << (chunk_pos.y * CHUNK_SIZE) << ", " << (chunk_pos.z * CHUNK_SIZE) << ")" << std::endl;
|
||||
std::cout << "[RENDER] Final world position should be: (" << (chunk_pos.x * CHUNK_SIZE + 0) << ", " << (chunk_pos.y * CHUNK_SIZE + 1) << ", " << (chunk_pos.z * CHUNK_SIZE + 31) << ")" << std::endl;
|
||||
logged_placed_block = true;
|
||||
}
|
||||
chunk_mesh->draw();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!debug_printed) {
|
||||
debug_printed = true;
|
||||
}
|
||||
|
||||
// if (!debug_printed) {
|
||||
// debug_printed = true;
|
||||
// }
|
||||
|
||||
glDisable(GL_POLYGON_OFFSET_FILL);
|
||||
|
||||
// ---- PASS 2: Draw wireframe on top ----
|
||||
@ -315,6 +400,16 @@ int main() {
|
||||
// --- Reset polygon mode for next frame (good practice) ---
|
||||
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
|
||||
|
||||
// ---- Draw Crosshair ----
|
||||
glDisable(GL_DEPTH_TEST); // Draw on top of everything
|
||||
shader.use();
|
||||
shader.setMat4("u_mvp", glm::mat4(1.0f)); // Identity matrix (NDC coordinates)
|
||||
shader.setVec3("u_Color", glm::vec3(1.0f, 1.0f, 1.0f)); // White
|
||||
glBindVertexArray(crosshairVAO);
|
||||
glDrawArrays(GL_LINES, 0, 4); // 4 vertices = 2 lines
|
||||
glBindVertexArray(0);
|
||||
glEnable(GL_DEPTH_TEST);
|
||||
|
||||
double end_time = glfwGetTime();
|
||||
float time_since_last_move = end_time - move_time;
|
||||
if (time_since_last_move >= 0.017)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user