mirror of
https://github.com/Relintai/godot_voxel.git
synced 2024-12-25 16:27:16 +01:00
Added simple voxel raycast
This commit is contained in:
parent
353b32c49a
commit
ef4d07ca03
141
voxel_raycast.cpp
Normal file
141
voxel_raycast.cpp
Normal file
@ -0,0 +1,141 @@
|
|||||||
|
#include "voxel_raycast.h"
|
||||||
|
#include <math_funcs.h>
|
||||||
|
|
||||||
|
const float g_infinite = 9999999;
|
||||||
|
|
||||||
|
bool voxel_raycast(
|
||||||
|
Vector3 ray_origin,
|
||||||
|
Vector3 ray_direction,
|
||||||
|
VoxelPredicate predicate,
|
||||||
|
void * predicate_context,
|
||||||
|
real_t max_distance,
|
||||||
|
Vector3i & out_hit_pos,
|
||||||
|
Vector3i & out_prev_pos
|
||||||
|
){
|
||||||
|
// Equation : p + v*t
|
||||||
|
// p : ray start position (ray.pos)
|
||||||
|
// v : ray orientation vector (ray.dir)
|
||||||
|
// t : parametric variable = a distance if v is normalized
|
||||||
|
|
||||||
|
// This raycasting technique is described here :
|
||||||
|
// http://www.cse.yorku.ca/~amana/research/grid.pdf
|
||||||
|
|
||||||
|
// Note : the grid is assumed to have 1-unit square cells.
|
||||||
|
|
||||||
|
ERR_FAIL_COND_V(predicate == 0, false);
|
||||||
|
ERR_FAIL_COND_V(ray_direction.is_normalized() == false, false); // Must be normalized
|
||||||
|
|
||||||
|
/* Initialisation */
|
||||||
|
|
||||||
|
// Voxel position
|
||||||
|
Vector3i hit_pos(
|
||||||
|
Math::floor(ray_origin.x),
|
||||||
|
Math::floor(ray_origin.y),
|
||||||
|
Math::floor(ray_origin.z)
|
||||||
|
);
|
||||||
|
Vector3i hit_prev_pos = hit_pos;
|
||||||
|
|
||||||
|
// Voxel step
|
||||||
|
const int xi_step = ray_direction.x > 0 ? 1 : ray_direction.x < 0 ? -1 : 0;
|
||||||
|
const int yi_step = ray_direction.y > 0 ? 1 : ray_direction.y < 0 ? -1 : 0;
|
||||||
|
const int zi_step = ray_direction.z > 0 ? 1 : ray_direction.z < 0 ? -1 : 0;
|
||||||
|
|
||||||
|
// Parametric voxel step
|
||||||
|
const real_t tdelta_x = xi_step != 0 ? 1.f / Math::abs(ray_direction.x) : g_infinite;
|
||||||
|
const real_t tdelta_y = yi_step != 0 ? 1.f / Math::abs(ray_direction.y) : g_infinite;
|
||||||
|
const real_t tdelta_z = zi_step != 0 ? 1.f / Math::abs(ray_direction.z) : g_infinite;
|
||||||
|
|
||||||
|
// Parametric grid-cross
|
||||||
|
real_t tcross_x; // At which value of T we will cross a vertical line?
|
||||||
|
real_t tcross_y; // At which value of T we will cross a horizontal line?
|
||||||
|
real_t tcross_z; // At which value of T we will cross a depth line?
|
||||||
|
|
||||||
|
// X initialization
|
||||||
|
if(xi_step != 0)
|
||||||
|
{
|
||||||
|
if(xi_step == 1)
|
||||||
|
tcross_x = (Math::ceil(ray_origin.x) - ray_origin.x) * tdelta_x;
|
||||||
|
else
|
||||||
|
tcross_x = (ray_origin.x - Math::floor(ray_origin.x)) * tdelta_x;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
tcross_x = g_infinite; // Will never cross on X
|
||||||
|
|
||||||
|
// Y initialization
|
||||||
|
if(yi_step != 0)
|
||||||
|
{
|
||||||
|
if(yi_step == 1)
|
||||||
|
tcross_y = (Math::ceil(ray_origin.y) - ray_origin.y) * tdelta_y;
|
||||||
|
else
|
||||||
|
tcross_y = (ray_origin.y - Math::floor(ray_origin.y)) * tdelta_y;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
tcross_y = g_infinite; // Will never cross on X
|
||||||
|
|
||||||
|
// Z initialization
|
||||||
|
if(zi_step != 0)
|
||||||
|
{
|
||||||
|
if(zi_step == 1)
|
||||||
|
tcross_z = (Math::ceil(ray_origin.z) - ray_origin.z) * tdelta_z;
|
||||||
|
else
|
||||||
|
tcross_z = (ray_origin.z - Math::floor(ray_origin.z)) * tdelta_z;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
tcross_z = g_infinite; // Will never cross on X
|
||||||
|
|
||||||
|
/* Iteration */
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
hit_prev_pos = hit_pos;
|
||||||
|
if(tcross_x < tcross_y)
|
||||||
|
{
|
||||||
|
if(tcross_x < tcross_z)
|
||||||
|
{
|
||||||
|
// X collision
|
||||||
|
//hit.prevPos.x = hit.pos.x;
|
||||||
|
hit_pos.x += xi_step;
|
||||||
|
if(tcross_x > max_distance)
|
||||||
|
return false;
|
||||||
|
tcross_x += tdelta_x;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Z collision (duplicate code)
|
||||||
|
//hit.prevPos.z = hit.pos.z;
|
||||||
|
hit_pos.z += zi_step;
|
||||||
|
if(tcross_z > max_distance)
|
||||||
|
return false;
|
||||||
|
tcross_z += tdelta_z;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if(tcross_y < tcross_z)
|
||||||
|
{
|
||||||
|
// Y collision
|
||||||
|
//hit.prevPos.y = hit.pos.y;
|
||||||
|
hit_pos.y += yi_step;
|
||||||
|
if(tcross_y > max_distance)
|
||||||
|
return false;
|
||||||
|
tcross_y += tdelta_y;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Z collision (duplicate code)
|
||||||
|
//hit.prevPos.z = hit.pos.z;
|
||||||
|
hit_pos.z += zi_step;
|
||||||
|
if(tcross_z > max_distance)
|
||||||
|
return false;
|
||||||
|
tcross_z += tdelta_z;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} while(!predicate(hit_pos, predicate_context));
|
||||||
|
|
||||||
|
out_hit_pos = hit_pos;
|
||||||
|
out_prev_pos = hit_prev_pos;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
18
voxel_raycast.h
Normal file
18
voxel_raycast.h
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
#include <vector3.h>
|
||||||
|
#include "vector3i.h"
|
||||||
|
|
||||||
|
// TODO Having a C++11 lambda would be nice...
|
||||||
|
// pos: voxel position
|
||||||
|
// context: arguments to carry (as a lamdbda capture)
|
||||||
|
typedef bool(*VoxelPredicate)(Vector3i pos, void * context);
|
||||||
|
|
||||||
|
bool voxel_raycast(
|
||||||
|
Vector3 ray_origin,
|
||||||
|
Vector3 ray_direction,
|
||||||
|
VoxelPredicate predicate,
|
||||||
|
void * predicate_context, // Handle that one with care
|
||||||
|
real_t max_distance,
|
||||||
|
Vector3i & out_hit_pos,
|
||||||
|
Vector3i & out_prev_pos
|
||||||
|
);
|
||||||
|
|
@ -1,6 +1,7 @@
|
|||||||
#include "voxel_terrain.h"
|
#include "voxel_terrain.h"
|
||||||
#include <scene/3d/mesh_instance.h>
|
#include <scene/3d/mesh_instance.h>
|
||||||
#include <os/os.h>
|
#include <os/os.h>
|
||||||
|
#include "voxel_raycast.h"
|
||||||
|
|
||||||
VoxelTerrain::VoxelTerrain(): Node(), _min_y(-4), _max_y(4), _generate_collisions(true) {
|
VoxelTerrain::VoxelTerrain(): Node(), _min_y(-4), _max_y(4), _generate_collisions(true) {
|
||||||
|
|
||||||
@ -24,6 +25,10 @@ Ref<VoxelProvider> VoxelTerrain::get_provider() {
|
|||||||
return _provider;
|
return _provider;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Ref<VoxelLibrary> VoxelTerrain::get_voxel_library() {
|
||||||
|
return _mesher->get_library();
|
||||||
|
}
|
||||||
|
|
||||||
void VoxelTerrain::set_generate_collisions(bool enabled) {
|
void VoxelTerrain::set_generate_collisions(bool enabled) {
|
||||||
_generate_collisions = enabled;
|
_generate_collisions = enabled;
|
||||||
}
|
}
|
||||||
@ -212,6 +217,46 @@ void VoxelTerrain::update_block_mesh(Vector3i block_pos) {
|
|||||||
// }
|
// }
|
||||||
//}
|
//}
|
||||||
|
|
||||||
|
static bool _raycast_binding_predicate(Vector3i pos, void *context) {
|
||||||
|
|
||||||
|
ERR_FAIL_COND_V(context == NULL, false);
|
||||||
|
VoxelTerrain * terrain = (VoxelTerrain*)context;
|
||||||
|
|
||||||
|
Ref<VoxelLibrary> lib_ref = terrain->get_voxel_library();
|
||||||
|
if(lib_ref.is_null())
|
||||||
|
return false;
|
||||||
|
const VoxelLibrary & lib = **lib_ref;
|
||||||
|
|
||||||
|
Ref<VoxelMap> map = terrain->get_map();
|
||||||
|
// TODO In the future we may want to query more channels
|
||||||
|
int v = map->get_voxel(pos, 0);
|
||||||
|
if(lib.has_voxel(v) == false)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
const Voxel & voxel = lib.get_voxel_const(v);
|
||||||
|
return !voxel.is_transparent();
|
||||||
|
}
|
||||||
|
|
||||||
|
Variant VoxelTerrain::_raycast_binding(Vector3 origin, Vector3 direction, real_t max_distance) {
|
||||||
|
|
||||||
|
// TODO Transform input if the terrain is rotated (in the future it can be made a Spatial node)
|
||||||
|
|
||||||
|
Vector3i hit_pos;
|
||||||
|
Vector3i prev_pos;
|
||||||
|
|
||||||
|
if(voxel_raycast(origin, direction, _raycast_binding_predicate, this, max_distance, hit_pos, prev_pos)) {
|
||||||
|
|
||||||
|
Dictionary hit = Dictionary();
|
||||||
|
hit["position"] = hit_pos.to_vec3();
|
||||||
|
hit["prev_position"] = prev_pos.to_vec3();
|
||||||
|
return hit;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return Variant(); // Null dictionary, no alloc
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void VoxelTerrain::_bind_methods() {
|
void VoxelTerrain::_bind_methods() {
|
||||||
|
|
||||||
ClassDB::bind_method(D_METHOD("set_provider", "provider:VoxelProvider"), &VoxelTerrain::set_provider);
|
ClassDB::bind_method(D_METHOD("set_provider", "provider:VoxelProvider"), &VoxelTerrain::set_provider);
|
||||||
@ -231,5 +276,7 @@ void VoxelTerrain::_bind_methods() {
|
|||||||
|
|
||||||
ClassDB::bind_method(D_METHOD("force_load_blocks", "center", "extents"), &VoxelTerrain::_force_load_blocks_binding);
|
ClassDB::bind_method(D_METHOD("force_load_blocks", "center", "extents"), &VoxelTerrain::_force_load_blocks_binding);
|
||||||
|
|
||||||
|
ClassDB::bind_method(D_METHOD("raycast:Dictionary", "origin", "direction", "max_distance"), &VoxelTerrain::_raycast_binding, DEFVAL(100));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -27,6 +27,7 @@ public:
|
|||||||
|
|
||||||
Ref<VoxelMesher> get_mesher() { return _mesher; }
|
Ref<VoxelMesher> get_mesher() { return _mesher; }
|
||||||
Ref<VoxelMap> get_map() { return _map; }
|
Ref<VoxelMap> get_map() { return _map; }
|
||||||
|
Ref<VoxelLibrary> get_voxel_library();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void _notification(int p_what);
|
void _notification(int p_what);
|
||||||
@ -44,6 +45,7 @@ protected:
|
|||||||
Vector3 _voxel_to_block_binding(Vector3 pos) { return Vector3i(VoxelMap::voxel_to_block(pos)).to_vec3(); }
|
Vector3 _voxel_to_block_binding(Vector3 pos) { return Vector3i(VoxelMap::voxel_to_block(pos)).to_vec3(); }
|
||||||
Vector3 _block_to_voxel_binding(Vector3 pos) { return Vector3i(VoxelMap::block_to_voxel(pos)).to_vec3(); }
|
Vector3 _block_to_voxel_binding(Vector3 pos) { return Vector3i(VoxelMap::block_to_voxel(pos)).to_vec3(); }
|
||||||
void _force_load_blocks_binding(Vector3 center, Vector3 extents) { force_load_blocks(center, extents); }
|
void _force_load_blocks_binding(Vector3 center, Vector3 extents) { force_load_blocks(center, extents); }
|
||||||
|
Variant _raycast_binding(Vector3 origin, Vector3 direction, real_t max_distance);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// Parameters
|
// Parameters
|
||||||
|
Loading…
Reference in New Issue
Block a user