block->m_node_timers.remove(p_rel);
}
-bool Map::isOccluded(v3s16 pos_origin, v3s16 pos_blockcenter, float step,
- float stepfac, float offset, float end_offset, u32 needed_count)
+bool Map::isOccluded(const v3s16 &pos_camera, const v3s16 &pos_target,
+ const core::aabbox3d<s16> &block_bounds, float step, float stepfac, float offset,
+ u32 needed_count)
{
- float distance = BS * pos_origin.getDistanceFrom(pos_blockcenter);
- v3f direction = intToFloat(pos_blockcenter - pos_origin, BS);
- direction.normalize();
- v3f pos_origin_f = intToFloat(pos_origin, BS);
+ // Worst-case safety distance to keep to the target position
+ // Anything smaller than the mapblock diagonal could result in in self-occlusion
+ // Diagonal = sqrt(1*1 + 1*1 + 1*1)
+ const static float BLOCK_DIAGONAL = BS * MAP_BLOCKSIZE * 1.732f;
+ v3f direction = intToFloat(pos_target - pos_camera, BS);
+ float distance = direction.getLength();
+
+ // Disable occlusion culling for near mapblocks in underground
+ if (distance < BLOCK_DIAGONAL)
+ return false;
+
+ // Normalize direction vector
+ if (distance > 0.0f)
+ direction /= distance;
+
+ v3f pos_origin_f = intToFloat(pos_camera, BS);
u32 count = 0;
- for (; offset < distance + end_offset; offset += step){
- v3f pf = pos_origin_f + direction * offset;
- v3s16 pos_node = floatToInt(pf, BS);
- MapNode node = getNode(pos_node);
- if (m_nodedef->get(node).drawtype == NDT_NORMAL) {
- // not transparent, see ContentFeature::updateTextures
+ bool is_valid_position;
+
+ for (; offset < distance; offset += step) {
+ v3f pos_node_f = pos_origin_f + direction * offset;
+ v3s16 pos_node = floatToInt(pos_node_f, BS);
+
+ if (offset > distance - BLOCK_DIAGONAL) {
+ // Do accurate position checks:
+ // Check whether the node is inside the current mapblock
+ if (block_bounds.isPointInside(pos_node))
+ return false;
+ }
+
+ MapNode node = getNode(pos_node, &is_valid_position);
+
+ if (is_valid_position &&
+ !m_nodedef->get(node).light_propagates) {
+ // Cannot see through light-blocking nodes --> occluded
count++;
if (count >= needed_count)
return true;
bool Map::isBlockOccluded(MapBlock *block, v3s16 cam_pos_nodes)
{
+ // Check occlusion for center and all 8 corners of the mapblock
+ // Overshoot a little for less flickering
static const s16 bs2 = MAP_BLOCKSIZE / 2 + 1;
- static const v3s16 dir8[8] = {
+ static const v3s16 dir9[9] = {
+ v3s16( 0, 0, 0),
v3s16( 1, 1, 1) * bs2,
v3s16( 1, 1, -1) * bs2,
v3s16( 1, -1, 1) * bs2,
v3s16(-1, -1, -1) * bs2,
};
- v3s16 pos_blockcenter = block->getPos() * MAP_BLOCKSIZE;
- pos_blockcenter += v3s16(MAP_BLOCKSIZE) / 2;
+ // Minimal and maximal positions in the mapblock
+ core::aabbox3d<s16> block_bounds = block->getBox();
- float step = BS * 1;
+ v3s16 pos_blockcenter = block_bounds.MinEdge + (MAP_BLOCKSIZE / 2);
+
+ // Starting step size, value between 1m and sqrt(3)m
+ float step = BS * 1.2f;
// Multiply step by each iteraction by 'stepfac' to reduce checks in distance
- float stepfac = 1.1;
+ float stepfac = 1.05f;
- float start_offset = BS * 1;
- // The occlusion search of 'isOccluded()' must stop short of the target
- // point by distance 'end_offset' (end offset) to not enter the target mapblock.
- // For the 8 mapblock corners 'end_offset' must therefore be the maximum diagonal
- // of a mapblock, because we must consider all view angles.
- // sqrt(1^2 + 1^2 + 1^2) = 1.732
- float end_offset = -BS * MAP_BLOCKSIZE * 1.7321;
+ float start_offset = BS * 1.0f;
// to reduce the likelihood of falsely occluded blocks
// require at least two solid blocks
// this is a HACK, we should think of a more precise algorithm
u32 needed_count = 2;
- // For the central point of the mapblock 'end_offset' can be halved
- if (!isOccluded(cam_pos_nodes, pos_blockcenter,
- step, stepfac, start_offset, end_offset / 2.0f, needed_count))
- return false;
-
- for (const v3s16 &dir : dir8) {
- if (!isOccluded(cam_pos_nodes, pos_blockcenter + dir,
- step, stepfac, start_offset, end_offset, needed_count))
+ for (const v3s16 &dir : dir9) {
+ if (!isOccluded(cam_pos_nodes, pos_blockcenter + dir, block_bounds,
+ step, stepfac, start_offset, needed_count))
return false;
}
return true;