Optimize isBlockInSight (#17471)

* Reduce algebraic operations in isBlockInSight
* Pre-compute camera related constants
This commit is contained in:
lhofhansl
2026-10-06 17:15:59 -07:00
committed by GitHub
parent 25db88f717
commit 9a1b92d0d4
3 changed files with 82 additions and 37 deletions
+6 -1
View File
@@ -242,6 +242,11 @@ void RemoteClient::GetNextBlocks (
const v3s16 cam_pos_nodes = floatToInt(camera_pos, BS);
// Calculate FOV attributes here, once per call
// see numeric.h for details
const f32 target_cos_sq = getFovCosSq(camera_fov);
const f32 adjdist = getFovAdj(camera_fov);
s16 d;
for (d = d_start; d <= d_max; d++) {
/*
@@ -284,7 +289,7 @@ void RemoteClient::GetNextBlocks (
(0.1 is about 5 degrees)
*/
f32 dist;
if (!(isBlockInSight(p, camera_pos, camera_dir, camera_fov,
if (!(isBlockInSightEx(p, camera_pos, camera_dir, target_cos_sq, adjdist,
d_blocks_in_sight, &dist) ||
(playerspeed.getLength() > 1.0f * BS &&
isBlockInSight(p, camera_pos, playerspeeddir, 0.1f,
+41 -34
View File
@@ -86,57 +86,64 @@ u64 murmur_hash_64_ua(const void *key, size_t len, unsigned int seed)
return h;
}
bool isBlockInSight(v3s16 blockpos_b, v3f camera_pos, v3f camera_dir,
f32 camera_fov, f32 range, f32 *distance_ptr)
bool isBlockInSight(const v3s16 blockpos_b, v3f camera_pos, v3f camera_dir,
const f32 camera_fov, const f32 range, f32 *distance_ptr)
{
v3s16 blockpos_nodes = blockpos_b * MAP_BLOCKSIZE;
return isBlockInSightEx(blockpos_b, camera_pos, camera_dir, getFovCosSq(camera_fov), getFovAdj(camera_fov), range, distance_ptr);
}
// Block center position
v3f blockpos = v3f::from(blockpos_nodes + MAP_BLOCKSIZE / 2) * BS;
bool isBlockInSightEx(const v3s16 blockpos_b, v3f camera_pos, v3f camera_dir,
const f32 target_cos_sq, const f32 adjdist, f32 range, f32 *distance_ptr)
{
// Block center position relative to camera
const v3f blockpos_relative =
intToFloat(blockpos_b * MAP_BLOCKSIZE + v3s16(MAP_BLOCKSIZE / 2), BS) -
camera_pos;
// Block position relative to camera
v3f blockpos_relative = blockpos - camera_pos;
if (distance_ptr) {
*distance_ptr = 0.0f;
}
// Total distance
f32 d = std::max(0.0f, blockpos_relative.getLength() - BLOCK_MAX_RADIUS);
if (distance_ptr)
*distance_ptr = d;
// Square distance
const f32 len_sq = blockpos_relative.getLengthSQ();
// If block is far away, it's not in sight
if (d > range)
const f32 max_reach = range + BLOCK_MAX_RADIUS;
if (len_sq > max_reach * max_reach)
return false;
// If block is (nearly) touching the camera, don't
// bother validating further (that is, render it anyway)
if (d == 0)
if (len_sq <= BLOCK_MAX_RADIUS * BLOCK_MAX_RADIUS) {
// this means distance in blocks is 0
return true;
}
// Adjust camera position, for purposes of computing the angle,
// such that a block that has any portion visible with the
// current camera position will have the center visible at the
// adjusted position
f32 adjdist = BLOCK_MAX_RADIUS / cos((M_PI - camera_fov) / 2);
// Block position relative to adjusted camera
v3f blockpos_adj = blockpos - (camera_pos - camera_dir * adjdist);
const f32 dot = blockpos_relative.dotProduct(camera_dir);
// Distance in camera direction (+=front, -=back)
f32 dforward = blockpos_adj.dotProduct(camera_dir);
const f32 dforward = dot + adjdist;
// Cosine of the angle between the camera direction
// and the block direction (camera_dir is an unit vector)
f32 cosangle = dforward / blockpos_adj.getLength();
// If block is not in the field of view, skip it
// HOTFIX: use slightly increased angle (+10%) to fix too aggressive
// culling. Somebody has to find out what's wrong with the math here.
// Previous value: camera_fov / 2
if (cosangle < std::cos(camera_fov * 0.55f))
// If the block is behind the adjusted camera position, cull it instantly
if (dforward <= 0.0f)
return false;
// |A+B|^2 = |A|^2 + 2(A dot B) + |B|^2
const f32 len_adj_sq = len_sq + 2.0f * adjdist * dot + adjdist * adjdist;
// do angle check with squared values
// This is a transformation of the previous
// dForward/blockpos_adj.getLength() < target_cos
// This is safe because of the (dforward <= 0.0f) check above
// (and the fact that fov <= 160 degrees)
if (dforward * dforward < target_cos_sq * len_adj_sq)
return false;
// calculate the actual distance only if the block is visible
if (distance_ptr) {
*distance_ptr = std::max(0.0f, std::sqrt(len_sq) - BLOCK_MAX_RADIUS);
}
return true;
}
+35 -2
View File
@@ -313,14 +313,47 @@ u64 murmur_hash_64_ua(const void *key, size_t len, unsigned int seed);
* @param blockpos_b position of block in block coordinates
* @param camera_pos position of camera in nodes
* @param camera_dir an unit vector pointing to camera direction
* @param camera_fov in rad
* @param range viewing range
* @param distance_ptr return location for distance from the camera
*/
bool isBlockInSight(v3s16 blockpos_b, v3f camera_pos, v3f camera_dir,
f32 camera_fov, f32 range, f32 *distance_ptr=nullptr);
bool isBlockInSight(const v3s16 blockpos_b, v3f camera_pos, v3f camera_dir,
const f32 camera_fov, const f32 range, f32 *distance_ptr=nullptr);
/**
* @param blockpos_b position of block in block coordinates
* @param camera_pos position of camera in nodes
* @param camera_dir an unit vector pointing to camera direction
* @param target_cos_sq pre-computed adjusted cos^2(camera fov) (use getFovCosSq(...))
* @param adjdist pre-computed camera distance adjustment (use getFovAdj(...))
* @param range viewing range
* @param distance_ptr return location for distance from the camera
*/
bool isBlockInSightEx(const v3s16 blockpos_b, v3f camera_pos, v3f camera_dir,
const f32 target_cos_sq, const f32 adjdist, const f32 range, f32 *distance_ptr);
s16 adjustDist(s16 dist, float zoom_fov);
inline f32 getFovAdj(f32 camera_fov)
{
// Adjust camera position, for purposes of computing the angle,
// such that a block that has any portion visible with the
// current camera position will have the center visible at the
// adjusted position
return BLOCK_MAX_RADIUS / std::sin(camera_fov * 0.5f);
}
inline f32 getFovCosSq(f32 camera_fov)
{
// If block is not in the field of view, skip it
// HOTFIX: use slightly increased angle (+10%) to fix too aggressive
// culling. Somebody has to find out what's wrong with the math here.
// Previous value: camera_fov / 2
const f32 target_cos = std::cos(camera_fov * 0.55f);
return target_cos * target_cos;
}
/*
Returns nearest 32-bit integer for given floating point number.
<cmath> and <math.h> in VC++ don't provide round().