Optimize raycast with large Heightmap shape data
Port raycast accelerator from Bullet's btHeightfieldTerrainShape.
This commit is contained in:
parent
b277c88280
commit
8d56354f7c
|
@ -1676,6 +1676,17 @@ struct _HeightmapSegmentCullParams {
|
||||||
FaceShape3DSW *face = nullptr;
|
FaceShape3DSW *face = nullptr;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct _HeightmapGridCullState {
|
||||||
|
real_t length = 0.0;
|
||||||
|
real_t length_flat = 0.0;
|
||||||
|
|
||||||
|
real_t dist = 0.0;
|
||||||
|
real_t prev_dist = 0.0;
|
||||||
|
|
||||||
|
int x = 0;
|
||||||
|
int z = 0;
|
||||||
|
};
|
||||||
|
|
||||||
_FORCE_INLINE_ bool _heightmap_face_cull_segment(_HeightmapSegmentCullParams &p_params) {
|
_FORCE_INLINE_ bool _heightmap_face_cull_segment(_HeightmapSegmentCullParams &p_params) {
|
||||||
Vector3 res;
|
Vector3 res;
|
||||||
Vector3 normal;
|
Vector3 normal;
|
||||||
|
@ -1688,11 +1699,11 @@ _FORCE_INLINE_ bool _heightmap_face_cull_segment(_HeightmapSegmentCullParams &p_
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
_FORCE_INLINE_ bool _heightmap_cell_cull_segment(_HeightmapSegmentCullParams &p_params, int p_x, int p_z) {
|
_FORCE_INLINE_ bool _heightmap_cell_cull_segment(_HeightmapSegmentCullParams &p_params, const _HeightmapGridCullState &p_state) {
|
||||||
// First triangle.
|
// First triangle.
|
||||||
p_params.heightmap->_get_point(p_x, p_z, p_params.face->vertex[0]);
|
p_params.heightmap->_get_point(p_state.x, p_state.z, p_params.face->vertex[0]);
|
||||||
p_params.heightmap->_get_point(p_x + 1, p_z, p_params.face->vertex[1]);
|
p_params.heightmap->_get_point(p_state.x + 1, p_state.z, p_params.face->vertex[1]);
|
||||||
p_params.heightmap->_get_point(p_x, p_z + 1, p_params.face->vertex[2]);
|
p_params.heightmap->_get_point(p_state.x, p_state.z + 1, p_params.face->vertex[2]);
|
||||||
p_params.face->normal = Plane(p_params.face->vertex[0], p_params.face->vertex[1], p_params.face->vertex[2]).normal;
|
p_params.face->normal = Plane(p_params.face->vertex[0], p_params.face->vertex[1], p_params.face->vertex[2]).normal;
|
||||||
if (_heightmap_face_cull_segment(p_params)) {
|
if (_heightmap_face_cull_segment(p_params)) {
|
||||||
return true;
|
return true;
|
||||||
|
@ -1700,7 +1711,7 @@ _FORCE_INLINE_ bool _heightmap_cell_cull_segment(_HeightmapSegmentCullParams &p_
|
||||||
|
|
||||||
// Second triangle.
|
// Second triangle.
|
||||||
p_params.face->vertex[0] = p_params.face->vertex[1];
|
p_params.face->vertex[0] = p_params.face->vertex[1];
|
||||||
p_params.heightmap->_get_point(p_x + 1, p_z + 1, p_params.face->vertex[1]);
|
p_params.heightmap->_get_point(p_state.x + 1, p_state.z + 1, p_params.face->vertex[1]);
|
||||||
p_params.face->normal = Plane(p_params.face->vertex[0], p_params.face->vertex[1], p_params.face->vertex[2]).normal;
|
p_params.face->normal = Plane(p_params.face->vertex[0], p_params.face->vertex[1], p_params.face->vertex[2]).normal;
|
||||||
if (_heightmap_face_cull_segment(p_params)) {
|
if (_heightmap_face_cull_segment(p_params)) {
|
||||||
return true;
|
return true;
|
||||||
|
@ -1709,6 +1720,182 @@ _FORCE_INLINE_ bool _heightmap_cell_cull_segment(_HeightmapSegmentCullParams &p_
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_FORCE_INLINE_ bool _heightmap_chunk_cull_segment(_HeightmapSegmentCullParams &p_params, const _HeightmapGridCullState &p_state) {
|
||||||
|
const HeightMapShape3DSW::Range &chunk = p_params.heightmap->_get_bounds_chunk(p_state.x, p_state.z);
|
||||||
|
|
||||||
|
Vector3 enter_pos;
|
||||||
|
Vector3 exit_pos;
|
||||||
|
|
||||||
|
if (p_state.length_flat > CMP_EPSILON) {
|
||||||
|
real_t flat_to_3d = p_state.length / p_state.length_flat;
|
||||||
|
real_t enter_param = p_state.prev_dist * flat_to_3d;
|
||||||
|
real_t exit_param = p_state.dist * flat_to_3d;
|
||||||
|
enter_pos = p_params.from + p_params.dir * enter_param;
|
||||||
|
exit_pos = p_params.from + p_params.dir * exit_param;
|
||||||
|
} else {
|
||||||
|
// Consider the ray vertical.
|
||||||
|
// (though we shouldn't reach this often because there is an early check up-front)
|
||||||
|
enter_pos = p_params.from;
|
||||||
|
exit_pos = p_params.to;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Transform positions to heightmap space.
|
||||||
|
enter_pos *= HeightMapShape3DSW::BOUNDS_CHUNK_SIZE;
|
||||||
|
exit_pos *= HeightMapShape3DSW::BOUNDS_CHUNK_SIZE;
|
||||||
|
|
||||||
|
// We did enter the flat projection of the AABB,
|
||||||
|
// but we have to check if we intersect it on the vertical axis.
|
||||||
|
if ((enter_pos.y > chunk.max) && (exit_pos.y > chunk.max)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if ((enter_pos.y < chunk.min) && (exit_pos.y < chunk.min)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return p_params.heightmap->_intersect_grid_segment(_heightmap_cell_cull_segment, enter_pos, exit_pos, p_params.heightmap->width, p_params.heightmap->depth, p_params.heightmap->local_origin, p_params.result, p_params.normal);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename ProcessFunction>
|
||||||
|
bool HeightMapShape3DSW::_intersect_grid_segment(ProcessFunction &p_process, const Vector3 &p_begin, const Vector3 &p_end, int p_width, int p_depth, const Vector3 &offset, Vector3 &r_point, Vector3 &r_normal) const {
|
||||||
|
Vector3 delta = (p_end - p_begin);
|
||||||
|
real_t length = delta.length();
|
||||||
|
|
||||||
|
if (length < CMP_EPSILON) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Vector3 local_begin = p_begin + offset;
|
||||||
|
|
||||||
|
FaceShape3DSW face;
|
||||||
|
face.backface_collision = false;
|
||||||
|
|
||||||
|
_HeightmapSegmentCullParams params;
|
||||||
|
params.from = p_begin;
|
||||||
|
params.to = p_end;
|
||||||
|
params.dir = delta / length;
|
||||||
|
params.heightmap = this;
|
||||||
|
params.face = &face;
|
||||||
|
|
||||||
|
_HeightmapGridCullState state;
|
||||||
|
|
||||||
|
// Perform grid query from projected ray.
|
||||||
|
Vector2 ray_dir_flat(delta.x, delta.z);
|
||||||
|
state.length = length;
|
||||||
|
state.length_flat = ray_dir_flat.length();
|
||||||
|
|
||||||
|
if (state.length_flat < CMP_EPSILON) {
|
||||||
|
ray_dir_flat = Vector2();
|
||||||
|
} else {
|
||||||
|
ray_dir_flat /= state.length_flat;
|
||||||
|
}
|
||||||
|
|
||||||
|
const int x_step = (ray_dir_flat.x > CMP_EPSILON) ? 1 : ((ray_dir_flat.x < -CMP_EPSILON) ? -1 : 0);
|
||||||
|
const int z_step = (ray_dir_flat.y > CMP_EPSILON) ? 1 : ((ray_dir_flat.y < -CMP_EPSILON) ? -1 : 0);
|
||||||
|
|
||||||
|
const real_t infinite = 1e20;
|
||||||
|
const real_t delta_x = (x_step != 0) ? 1.f / Math::abs(ray_dir_flat.x) : infinite;
|
||||||
|
const real_t delta_z = (z_step != 0) ? 1.f / Math::abs(ray_dir_flat.y) : infinite;
|
||||||
|
|
||||||
|
real_t cross_x; // At which value of `param` we will cross a x-axis lane?
|
||||||
|
real_t cross_z; // At which value of `param` we will cross a z-axis lane?
|
||||||
|
|
||||||
|
// X initialization.
|
||||||
|
if (x_step != 0) {
|
||||||
|
if (x_step == 1) {
|
||||||
|
cross_x = (Math::ceil(local_begin.x) - local_begin.x) * delta_x;
|
||||||
|
} else {
|
||||||
|
cross_x = (local_begin.x - Math::floor(local_begin.x)) * delta_x;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
cross_x = infinite; // Will never cross on X.
|
||||||
|
}
|
||||||
|
|
||||||
|
// Z initialization.
|
||||||
|
if (z_step != 0) {
|
||||||
|
if (z_step == 1) {
|
||||||
|
cross_z = (Math::ceil(local_begin.z) - local_begin.z) * delta_z;
|
||||||
|
} else {
|
||||||
|
cross_z = (local_begin.z - Math::floor(local_begin.z)) * delta_z;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
cross_z = infinite; // Will never cross on Z.
|
||||||
|
}
|
||||||
|
|
||||||
|
int x = Math::floor(local_begin.x);
|
||||||
|
int z = Math::floor(local_begin.z);
|
||||||
|
|
||||||
|
// Workaround cases where the ray starts at an integer position.
|
||||||
|
if (Math::is_zero_approx(cross_x)) {
|
||||||
|
cross_x += delta_x;
|
||||||
|
// If going backwards, we should ignore the position we would get by the above flooring,
|
||||||
|
// because the ray is not heading in that direction.
|
||||||
|
if (x_step == -1) {
|
||||||
|
x -= 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Math::is_zero_approx(cross_z)) {
|
||||||
|
cross_z += delta_z;
|
||||||
|
if (z_step == -1) {
|
||||||
|
z -= 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start inside the grid.
|
||||||
|
int x_start = MAX(MIN(x, p_width - 2), 0);
|
||||||
|
int z_start = MAX(MIN(z, p_depth - 2), 0);
|
||||||
|
|
||||||
|
// Adjust initial cross values.
|
||||||
|
cross_x += delta_x * x_step * (x_start - x);
|
||||||
|
cross_z += delta_z * z_step * (z_start - z);
|
||||||
|
|
||||||
|
x = x_start;
|
||||||
|
z = z_start;
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
state.prev_dist = state.dist;
|
||||||
|
state.x = x;
|
||||||
|
state.z = z;
|
||||||
|
|
||||||
|
if (cross_x < cross_z) {
|
||||||
|
// X lane.
|
||||||
|
x += x_step;
|
||||||
|
// Assign before advancing the param,
|
||||||
|
// to be in sync with the initialization step.
|
||||||
|
state.dist = cross_x;
|
||||||
|
cross_x += delta_x;
|
||||||
|
} else {
|
||||||
|
// Z lane.
|
||||||
|
z += z_step;
|
||||||
|
state.dist = cross_z;
|
||||||
|
cross_z += delta_z;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (state.dist > state.length_flat) {
|
||||||
|
state.dist = state.length_flat;
|
||||||
|
if (p_process(params, state)) {
|
||||||
|
r_point = params.result;
|
||||||
|
r_normal = params.normal;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (p_process(params, state)) {
|
||||||
|
r_point = params.result;
|
||||||
|
r_normal = params.normal;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stop when outside the grid.
|
||||||
|
if ((x < 0) || (z < 0) || (x >= p_width - 1) || (z >= p_depth - 1)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
bool HeightMapShape3DSW::intersect_segment(const Vector3 &p_begin, const Vector3 &p_end, Vector3 &r_point, Vector3 &r_normal) const {
|
bool HeightMapShape3DSW::intersect_segment(const Vector3 &p_begin, const Vector3 &p_end, Vector3 &r_point, Vector3 &r_normal) const {
|
||||||
if (heights.is_empty()) {
|
if (heights.is_empty()) {
|
||||||
return false;
|
return false;
|
||||||
|
@ -1717,142 +1904,49 @@ bool HeightMapShape3DSW::intersect_segment(const Vector3 &p_begin, const Vector3
|
||||||
Vector3 local_begin = p_begin + local_origin;
|
Vector3 local_begin = p_begin + local_origin;
|
||||||
Vector3 local_end = p_end + local_origin;
|
Vector3 local_end = p_end + local_origin;
|
||||||
|
|
||||||
FaceShape3DSW face;
|
|
||||||
face.backface_collision = false;
|
|
||||||
|
|
||||||
_HeightmapSegmentCullParams params;
|
|
||||||
params.from = p_begin;
|
|
||||||
params.to = p_end;
|
|
||||||
params.dir = (p_end - p_begin).normalized();
|
|
||||||
params.heightmap = this;
|
|
||||||
params.face = &face;
|
|
||||||
|
|
||||||
// Quantize the ray begin/end.
|
// Quantize the ray begin/end.
|
||||||
int begin_x = floor(local_begin.x);
|
int begin_x = Math::floor(local_begin.x);
|
||||||
int begin_z = floor(local_begin.z);
|
int begin_z = Math::floor(local_begin.z);
|
||||||
int end_x = floor(local_end.x);
|
int end_x = Math::floor(local_end.x);
|
||||||
int end_z = floor(local_end.z);
|
int end_z = Math::floor(local_end.z);
|
||||||
|
|
||||||
if ((begin_x == end_x) && (begin_z == end_z)) {
|
if ((begin_x == end_x) && (begin_z == end_z)) {
|
||||||
// Simple case for rays that don't traverse the grid horizontally.
|
// Simple case for rays that don't traverse the grid horizontally.
|
||||||
// Just perform a test on the given cell.
|
// Just perform a test on the given cell.
|
||||||
int x = CLAMP(begin_x, 0, width - 2);
|
FaceShape3DSW face;
|
||||||
int z = CLAMP(begin_z, 0, depth - 2);
|
face.backface_collision = false;
|
||||||
if (_heightmap_cell_cull_segment(params, x, z)) {
|
|
||||||
|
_HeightmapSegmentCullParams params;
|
||||||
|
params.from = p_begin;
|
||||||
|
params.to = p_end;
|
||||||
|
params.dir = (p_end - p_begin).normalized();
|
||||||
|
|
||||||
|
params.heightmap = this;
|
||||||
|
params.face = &face;
|
||||||
|
|
||||||
|
_HeightmapGridCullState state;
|
||||||
|
state.x = MAX(MIN(begin_x, width - 2), 0);
|
||||||
|
state.z = MAX(MIN(begin_z, depth - 2), 0);
|
||||||
|
if (_heightmap_cell_cull_segment(params, state)) {
|
||||||
r_point = params.result;
|
r_point = params.result;
|
||||||
r_normal = params.normal;
|
r_normal = params.normal;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
} else if (bounds_grid.is_empty()) {
|
||||||
|
// Process all cells intersecting the flat projection of the ray.
|
||||||
|
return _intersect_grid_segment(_heightmap_cell_cull_segment, p_begin, p_end, width, depth, local_origin, r_point, r_normal);
|
||||||
} else {
|
} else {
|
||||||
// Perform grid query from projected ray.
|
Vector3 ray_diff = (p_end - p_begin);
|
||||||
Vector2 ray_dir_proj(local_end.x - local_begin.x, local_end.z - local_begin.z);
|
real_t length_flat_sqr = ray_diff.x * ray_diff.x + ray_diff.z * ray_diff.z;
|
||||||
real_t ray_dist_proj = ray_dir_proj.length();
|
if (length_flat_sqr < BOUNDS_CHUNK_SIZE * BOUNDS_CHUNK_SIZE) {
|
||||||
|
// Don't use chunks, the ray is too short in the plane.
|
||||||
if (ray_dist_proj < CMP_EPSILON) {
|
return _intersect_grid_segment(_heightmap_cell_cull_segment, p_begin, p_end, width, depth, local_origin, r_point, r_normal);
|
||||||
ray_dir_proj = Vector2();
|
|
||||||
} else {
|
} else {
|
||||||
ray_dir_proj /= ray_dist_proj;
|
// The ray is long, run raycast on a higher-level grid.
|
||||||
}
|
Vector3 bounds_from = p_begin / BOUNDS_CHUNK_SIZE;
|
||||||
|
Vector3 bounds_to = p_end / BOUNDS_CHUNK_SIZE;
|
||||||
const int x_step = (ray_dir_proj.x > CMP_EPSILON) ? 1 : ((ray_dir_proj.x < -CMP_EPSILON) ? -1 : 0);
|
Vector3 bounds_offset = local_origin / BOUNDS_CHUNK_SIZE;
|
||||||
const int z_step = (ray_dir_proj.y > CMP_EPSILON) ? 1 : ((ray_dir_proj.y < -CMP_EPSILON) ? -1 : 0);
|
return _intersect_grid_segment(_heightmap_chunk_cull_segment, bounds_from, bounds_to, bounds_grid_width, bounds_grid_depth, bounds_offset, r_point, r_normal);
|
||||||
|
|
||||||
const real_t infinite = 1e20;
|
|
||||||
const real_t delta_x = (x_step != 0) ? 1.f / Math::abs(ray_dir_proj.x) : infinite;
|
|
||||||
const real_t delta_z = (z_step != 0) ? 1.f / Math::abs(ray_dir_proj.y) : infinite;
|
|
||||||
|
|
||||||
real_t cross_x; // At which value of `param` we will cross a x-axis lane?
|
|
||||||
real_t cross_z; // At which value of `param` we will cross a z-axis lane?
|
|
||||||
|
|
||||||
// X initialization.
|
|
||||||
if (x_step != 0) {
|
|
||||||
if (x_step == 1) {
|
|
||||||
cross_x = (ceil(local_begin.x) - local_begin.x) * delta_x;
|
|
||||||
} else {
|
|
||||||
cross_x = (local_begin.x - floor(local_begin.x)) * delta_x;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
cross_x = infinite; // Will never cross on X.
|
|
||||||
}
|
|
||||||
|
|
||||||
// Z initialization.
|
|
||||||
if (z_step != 0) {
|
|
||||||
if (z_step == 1) {
|
|
||||||
cross_z = (ceil(local_begin.z) - local_begin.z) * delta_z;
|
|
||||||
} else {
|
|
||||||
cross_z = (local_begin.z - floor(local_begin.z)) * delta_z;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
cross_z = infinite; // Will never cross on Z.
|
|
||||||
}
|
|
||||||
|
|
||||||
int x = floor(local_begin.x);
|
|
||||||
int z = floor(local_begin.z);
|
|
||||||
|
|
||||||
// Workaround cases where the ray starts at an integer position.
|
|
||||||
if (Math::is_zero_approx(cross_x)) {
|
|
||||||
cross_x += delta_x;
|
|
||||||
// If going backwards, we should ignore the position we would get by the above flooring,
|
|
||||||
// because the ray is not heading in that direction.
|
|
||||||
if (x_step == -1) {
|
|
||||||
x -= 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Math::is_zero_approx(cross_z)) {
|
|
||||||
cross_z += delta_z;
|
|
||||||
if (z_step == -1) {
|
|
||||||
z -= 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Start inside the grid.
|
|
||||||
int x_start = CLAMP(x, 0, width - 2);
|
|
||||||
int z_start = CLAMP(z, 0, depth - 2);
|
|
||||||
|
|
||||||
// Adjust initial cross values.
|
|
||||||
cross_x += delta_x * x_step * (x_start - x);
|
|
||||||
cross_z += delta_z * z_step * (z_start - z);
|
|
||||||
|
|
||||||
x = x_start;
|
|
||||||
z = z_start;
|
|
||||||
|
|
||||||
if (_heightmap_cell_cull_segment(params, x, z)) {
|
|
||||||
r_point = params.result;
|
|
||||||
r_normal = params.normal;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
real_t dist = 0.0;
|
|
||||||
while (true) {
|
|
||||||
if (cross_x < cross_z) {
|
|
||||||
// X lane.
|
|
||||||
x += x_step;
|
|
||||||
// Assign before advancing the param,
|
|
||||||
// to be in sync with the initialization step.
|
|
||||||
dist = cross_x;
|
|
||||||
cross_x += delta_x;
|
|
||||||
} else {
|
|
||||||
// Z lane.
|
|
||||||
z += z_step;
|
|
||||||
dist = cross_z;
|
|
||||||
cross_z += delta_z;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Stop when outside the grid.
|
|
||||||
if ((x < 0) || (z < 0) || (x >= width - 1) || (z >= depth - 1)) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_heightmap_cell_cull_segment(params, x, z)) {
|
|
||||||
r_point = params.result;
|
|
||||||
r_normal = params.normal;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (dist > ray_dist_proj) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1917,7 +2011,7 @@ void HeightMapShape3DSW::cull(const AABB &p_local_aabb, QueryCallback p_callback
|
||||||
_get_point(x, z, face.vertex[0]);
|
_get_point(x, z, face.vertex[0]);
|
||||||
_get_point(x + 1, z, face.vertex[1]);
|
_get_point(x + 1, z, face.vertex[1]);
|
||||||
_get_point(x, z + 1, face.vertex[2]);
|
_get_point(x, z + 1, face.vertex[2]);
|
||||||
face.normal = Plane(face.vertex[0], face.vertex[2], face.vertex[1]).normal;
|
face.normal = Plane(face.vertex[0], face.vertex[1], face.vertex[2]).normal;
|
||||||
if (p_callback(p_userdata, &face)) {
|
if (p_callback(p_userdata, &face)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -1925,7 +2019,7 @@ void HeightMapShape3DSW::cull(const AABB &p_local_aabb, QueryCallback p_callback
|
||||||
// Second triangle.
|
// Second triangle.
|
||||||
face.vertex[0] = face.vertex[1];
|
face.vertex[0] = face.vertex[1];
|
||||||
_get_point(x + 1, z + 1, face.vertex[1]);
|
_get_point(x + 1, z + 1, face.vertex[1]);
|
||||||
face.normal = Plane(face.vertex[0], face.vertex[2], face.vertex[1]).normal;
|
face.normal = Plane(face.vertex[0], face.vertex[1], face.vertex[2]).normal;
|
||||||
if (p_callback(p_userdata, &face)) {
|
if (p_callback(p_userdata, &face)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -1943,6 +2037,75 @@ Vector3 HeightMapShape3DSW::get_moment_of_inertia(real_t p_mass) const {
|
||||||
(p_mass / 3.0) * (extents.x * extents.x + extents.y * extents.y));
|
(p_mass / 3.0) * (extents.x * extents.x + extents.y * extents.y));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void HeightMapShape3DSW::_build_accelerator() {
|
||||||
|
bounds_grid.clear();
|
||||||
|
|
||||||
|
bounds_grid_width = width / BOUNDS_CHUNK_SIZE;
|
||||||
|
bounds_grid_depth = depth / BOUNDS_CHUNK_SIZE;
|
||||||
|
|
||||||
|
if (width % BOUNDS_CHUNK_SIZE > 0) {
|
||||||
|
++bounds_grid_width; // In case terrain size isn't dividable by chunk size.
|
||||||
|
}
|
||||||
|
|
||||||
|
if (depth % BOUNDS_CHUNK_SIZE > 0) {
|
||||||
|
++bounds_grid_depth;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t bound_grid_size = (uint32_t)(bounds_grid_width * bounds_grid_depth);
|
||||||
|
|
||||||
|
if (bound_grid_size < 2) {
|
||||||
|
// Grid is empty or just one chunk.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
bounds_grid.resize(bound_grid_size);
|
||||||
|
|
||||||
|
// Compute min and max height for all chunks.
|
||||||
|
for (int cz = 0; cz < bounds_grid_depth; ++cz) {
|
||||||
|
int z0 = cz * BOUNDS_CHUNK_SIZE;
|
||||||
|
|
||||||
|
for (int cx = 0; cx < bounds_grid_width; ++cx) {
|
||||||
|
int x0 = cx * BOUNDS_CHUNK_SIZE;
|
||||||
|
|
||||||
|
Range r;
|
||||||
|
|
||||||
|
r.min = _get_height(x0, z0);
|
||||||
|
r.max = r.min;
|
||||||
|
|
||||||
|
// Compute min and max height for this chunk.
|
||||||
|
// We have to include one extra cell to account for neighbors.
|
||||||
|
// Here is why:
|
||||||
|
// Say we have a flat terrain, and a plateau that fits a chunk perfectly.
|
||||||
|
//
|
||||||
|
// Left Right
|
||||||
|
// 0---0---0---1---1---1
|
||||||
|
// | | | | | |
|
||||||
|
// 0---0---0---1---1---1
|
||||||
|
// | | | | | |
|
||||||
|
// 0---0---0---1---1---1
|
||||||
|
// x
|
||||||
|
//
|
||||||
|
// If the AABB for the Left chunk did not share vertices with the Right,
|
||||||
|
// then we would fail collision tests at x due to a gap.
|
||||||
|
//
|
||||||
|
int z_max = MIN(z0 + BOUNDS_CHUNK_SIZE + 1, depth);
|
||||||
|
int x_max = MIN(x0 + BOUNDS_CHUNK_SIZE + 1, width);
|
||||||
|
for (int z = z0; z < z_max; ++z) {
|
||||||
|
for (int x = x0; x < x_max; ++x) {
|
||||||
|
real_t height = _get_height(x, z);
|
||||||
|
if (height < r.min) {
|
||||||
|
r.min = height;
|
||||||
|
} else if (height > r.max) {
|
||||||
|
r.max = height;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bounds_grid[cx + cz * bounds_grid_width] = r;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void HeightMapShape3DSW::_setup(const Vector<real_t> &p_heights, int p_width, int p_depth, real_t p_min_height, real_t p_max_height) {
|
void HeightMapShape3DSW::_setup(const Vector<real_t> &p_heights, int p_width, int p_depth, real_t p_min_height, real_t p_max_height) {
|
||||||
heights = p_heights;
|
heights = p_heights;
|
||||||
width = p_width;
|
width = p_width;
|
||||||
|
@ -1959,6 +2122,8 @@ void HeightMapShape3DSW::_setup(const Vector<real_t> &p_heights, int p_width, in
|
||||||
|
|
||||||
aabb.position -= local_origin;
|
aabb.position -= local_origin;
|
||||||
|
|
||||||
|
_build_accelerator();
|
||||||
|
|
||||||
configure(aabb);
|
configure(aabb);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2017,7 +2182,7 @@ void HeightMapShape3DSW::set_data(const Variant &p_data) {
|
||||||
} else {
|
} else {
|
||||||
int heights_size = heights.size();
|
int heights_size = heights.size();
|
||||||
for (int i = 0; i < heights_size; ++i) {
|
for (int i = 0; i < heights_size; ++i) {
|
||||||
float h = heights[i];
|
real_t h = heights[i];
|
||||||
if (h < min_height) {
|
if (h < min_height) {
|
||||||
min_height = h;
|
min_height = h;
|
||||||
} else if (h > max_height) {
|
} else if (h > max_height) {
|
||||||
|
|
|
@ -32,6 +32,7 @@
|
||||||
#define SHAPE_SW_H
|
#define SHAPE_SW_H
|
||||||
|
|
||||||
#include "core/math/geometry_3d.h"
|
#include "core/math/geometry_3d.h"
|
||||||
|
#include "core/templates/local_vector.h"
|
||||||
#include "servers/physics_server_3d.h"
|
#include "servers/physics_server_3d.h"
|
||||||
|
|
||||||
class Shape3DSW;
|
class Shape3DSW;
|
||||||
|
@ -385,6 +386,21 @@ struct HeightMapShape3DSW : public ConcaveShape3DSW {
|
||||||
int depth = 0;
|
int depth = 0;
|
||||||
Vector3 local_origin;
|
Vector3 local_origin;
|
||||||
|
|
||||||
|
// Accelerator.
|
||||||
|
struct Range {
|
||||||
|
real_t min = 0.0;
|
||||||
|
real_t max = 0.0;
|
||||||
|
};
|
||||||
|
LocalVector<Range> bounds_grid;
|
||||||
|
int bounds_grid_width = 0;
|
||||||
|
int bounds_grid_depth = 0;
|
||||||
|
|
||||||
|
static const int BOUNDS_CHUNK_SIZE = 16;
|
||||||
|
|
||||||
|
_FORCE_INLINE_ const Range &_get_bounds_chunk(int p_x, int p_z) const {
|
||||||
|
return bounds_grid[(p_z * bounds_grid_width) + p_x];
|
||||||
|
}
|
||||||
|
|
||||||
_FORCE_INLINE_ real_t _get_height(int p_x, int p_z) const {
|
_FORCE_INLINE_ real_t _get_height(int p_x, int p_z) const {
|
||||||
return heights[(p_z * width) + p_x];
|
return heights[(p_z * width) + p_x];
|
||||||
}
|
}
|
||||||
|
@ -397,6 +413,11 @@ struct HeightMapShape3DSW : public ConcaveShape3DSW {
|
||||||
|
|
||||||
void _get_cell(const Vector3 &p_point, int &r_x, int &r_y, int &r_z) const;
|
void _get_cell(const Vector3 &p_point, int &r_x, int &r_y, int &r_z) const;
|
||||||
|
|
||||||
|
void _build_accelerator();
|
||||||
|
|
||||||
|
template <typename ProcessFunction>
|
||||||
|
bool _intersect_grid_segment(ProcessFunction &p_process, const Vector3 &p_begin, const Vector3 &p_end, int p_width, int p_depth, const Vector3 &offset, Vector3 &r_point, Vector3 &r_normal) const;
|
||||||
|
|
||||||
void _setup(const Vector<real_t> &p_heights, int p_width, int p_depth, real_t p_min_height, real_t p_max_height);
|
void _setup(const Vector<real_t> &p_heights, int p_width, int p_depth, real_t p_min_height, real_t p_max_height);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
Loading…
Reference in New Issue