174 lines
6.7 KiB
C++
174 lines
6.7 KiB
C++
#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, VoxelKind kind) {
|
|
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);
|
|
chunk->set(local_position, kind);
|
|
|
|
chunk->regenerateMesh();
|
|
}
|
|
|
|
Chunk* World::get_or_create_chunk(glm::ivec3 chunk_position) {
|
|
|
|
// Fill in new chunk with a flat plane of voxels at y=0
|
|
auto [it, inserted] = this->chunks.try_emplace(chunk_position);
|
|
Chunk& chunk = it->second;
|
|
if (inserted) {
|
|
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++) {
|
|
glm::ivec3 offset = glm::ivec3(x, y, z);
|
|
glm::ivec3 world_voxel_position = chunk_position * CHUNK_SIZE + offset;
|
|
|
|
VoxelKind kind;
|
|
if (world_voxel_position.y == 0) {
|
|
kind = VoxelKind::Dirt;
|
|
} else if (world_voxel_position.y < 0) {
|
|
kind = VoxelKind::Stone;
|
|
} else {
|
|
kind = VoxelKind::Air;
|
|
}
|
|
|
|
chunk.set(offset, kind);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return &chunk;
|
|
}
|
|
|
|
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;
|
|
|
|
// Adjust start position to account for voxels being centered at integer + 0.5
|
|
// (voxel at pos (0,0,0) occupies space from (-0.5,-0.5,-0.5) to (0.5,0.5,0.5))
|
|
glm::vec3 adjusted_start = start + glm::vec3(0.5f, 0.5f, 0.5f);
|
|
|
|
glm::ivec3 pos = glm::ivec3(std::floor(adjusted_start.x), std::floor(adjusted_start.y), std::floor(adjusted_start.z));
|
|
// std::cout << "[RAYCAST] Starting voxel: (" << pos.x << ", " << pos.y << ", " << pos.z << ")" << std::endl;
|
|
|
|
// (+1, -1, or 0 for each axis)
|
|
glm::ivec3 step_dir = glm::ivec3(
|
|
direction.x > 0 ? 1 : (direction.x < 0 ? -1 : 0),
|
|
direction.y > 0 ? 1 : (direction.y < 0 ? -1 : 0),
|
|
direction.z > 0 ? 1 : (direction.z < 0 ? -1 : 0)
|
|
);
|
|
// std::cout << "[RAYCAST] Step direction: (" << step_dir.x << ", " << step_dir.y << ", " << step_dir.z << ")" << std::endl;
|
|
|
|
// Delta: how far along the ray we move for a full voxel step in each axis
|
|
glm::vec3 delta = glm::vec3(
|
|
step_dir.x != 0 ? std::abs(1.0f / direction.x) : FLT_MAX,
|
|
step_dir.y != 0 ? std::abs(1.0f / direction.y) : FLT_MAX,
|
|
step_dir.z != 0 ? std::abs(1.0f / direction.z) : FLT_MAX
|
|
);
|
|
|
|
// Calculate t_max: distance along ray to next voxel boundary for each axis
|
|
glm::vec3 t_max;
|
|
glm::vec3 fract = adjusted_start - glm::vec3(pos);
|
|
|
|
// For each axis, calculate distance to the next boundary
|
|
if (step_dir.x > 0)
|
|
t_max.x = (1.0f - fract.x) * delta.x;
|
|
else if (step_dir.x < 0)
|
|
t_max.x = fract.x * delta.x;
|
|
else
|
|
t_max.x = FLT_MAX;
|
|
|
|
if (step_dir.y > 0)
|
|
t_max.y = (1.0f - fract.y) * delta.y;
|
|
else if (step_dir.y < 0)
|
|
t_max.y = fract.y * delta.y;
|
|
else
|
|
t_max.y = FLT_MAX;
|
|
|
|
if (step_dir.z > 0)
|
|
t_max.z = (1.0f - fract.z) * delta.z;
|
|
else if (step_dir.z < 0)
|
|
t_max.z = fract.z * delta.z;
|
|
else
|
|
t_max.z = FLT_MAX;
|
|
|
|
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) != VoxelKind::Air) {
|
|
// 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;
|
|
}
|
|
|
|
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);
|
|
|
|
return chunk->get(position);
|
|
}
|