Allow server side occlusion culling.
authorLars Hofhansl <larsh@apache.org>
Tue, 28 Feb 2017 07:06:15 +0000 (23:06 -0800)
committerAuke Kok <sofar+github@foo-projects.org>
Sun, 12 Mar 2017 02:11:19 +0000 (18:11 -0800)
builtin/settingtypes.txt
minetest.conf.example
src/clientiface.cpp
src/clientmap.cpp
src/defaultsettings.cpp
src/map.cpp
src/map.h

index 4e800e25b6c01f10eb4617e0cb32236eb996e875..ffd872c20be2dcb6c29082972b5a70e384cb0579 100644 (file)
@@ -864,6 +864,12 @@ liquid_update (Liquid update tick) float 1.0
 #    Stated in mapblocks (16 nodes)
 block_send_optimize_distance (block send optimize distance) int 4 2
 
+#    If enabled the server will perform map block occlusion culling based on
+#    on the eye position of the player. This can reduce the number of blocks
+#    sent to the client 50-80%. The client will not longer receive most invisible
+#    so that the utility of noclip mode is reduced.
+server_side_occlusion_culling (Server side occlusion culling) bool false
+
 [*Mapgen]
 
 #    Name of map generator to be used when creating a new world.
index d53a00fd90b0cf4eafb339636fc0e9d705040d92..08d00d62a882bf93ab28f273b4ec65cd1cf58be8 100644 (file)
 #    type: int min: 2
 # block_send_optimize_distance = 4
 
+#    If enabled the server will perform map block occlusion culling based on
+#    on the eye position of the player. This can reduce the number of blocks
+#    sent to the client 50-80%. The client will not longer receive most invisible
+#    so that the utility of noclip mode is reduced.
+server_side_occlusion_culling = false
+
 ## Mapgen
 
 #    Name of map generator to be used when creating a new world.
index 0eb68c9c12295061b044926f12f9971611d2d892..76a34c392ae224bcffd6acc79054f9901eb0864c 100644 (file)
@@ -197,6 +197,9 @@ void RemoteClient::GetNextBlocks (
        s32 nearest_sent_d = -1;
        //bool queue_is_full = false;
 
+       const v3s16 cam_pos_nodes = floatToInt(camera_pos, BS);
+       const bool occ_cull = g_settings->getBool("server_side_occlusion_culling");
+
        s16 d;
        for(d = d_start; d <= d_max; d++) {
                /*
@@ -298,6 +301,11 @@ void RemoteClient::GetNextBlocks (
                                        if(block->getDayNightDiff() == false)
                                                continue;
                                }
+
+                               if (occ_cull && !block_is_invalid &&
+                                               env->getMap().isBlockOccluded(block, cam_pos_nodes)) {
+                                       continue;
+                               }
                        }
 
                        /*
index fb70a97e92f04ef58ab7845c498d4d396ce2dd9a..4cae03bf26d27f814332089b8a332a457cfa8361 100644 (file)
@@ -109,35 +109,6 @@ void ClientMap::OnRegisterSceneNode()
        ISceneNode::OnRegisterSceneNode();
 }
 
-static bool isOccluded(Map *map, v3s16 p0, v3s16 p1, float step, float stepfac,
-               float start_off, float end_off, u32 needed_count, INodeDefManager *nodemgr)
-{
-       float d0 = (float)BS * p0.getDistanceFrom(p1);
-       v3s16 u0 = p1 - p0;
-       v3f uf = v3f(u0.X, u0.Y, u0.Z) * BS;
-       uf.normalize();
-       v3f p0f = v3f(p0.X, p0.Y, p0.Z) * BS;
-       u32 count = 0;
-       for(float s=start_off; s<d0+end_off; s+=step){
-               v3f pf = p0f + uf * s;
-               v3s16 p = floatToInt(pf, BS);
-               MapNode n = map->getNodeNoEx(p);
-               bool is_transparent = false;
-               const ContentFeatures &f = nodemgr->get(n);
-               if(f.solidness == 0)
-                       is_transparent = (f.visual_solidness != 2);
-               else
-                       is_transparent = (f.solidness != 2);
-               if(!is_transparent){
-                       count++;
-                       if(count >= needed_count)
-                               return true;
-               }
-               step *= stepfac;
-       }
-       return false;
-}
-
 void ClientMap::getBlocksInViewRange(v3s16 cam_pos_nodes,
                v3s16 *p_blocks_min, v3s16 *p_blocks_max)
 {
@@ -273,43 +244,7 @@ void ClientMap::updateDrawList(video::IVideoDriver* driver)
                        /*
                                Occlusion culling
                        */
-                       v3s16 cpn = block->getPos() * MAP_BLOCKSIZE;
-                       cpn += v3s16(MAP_BLOCKSIZE / 2, MAP_BLOCKSIZE / 2, MAP_BLOCKSIZE / 2);
-                       float step = BS * 1;
-                       float stepfac = 1.1;
-                       float startoff = BS * 1;
-                       // The occlusion search of 'isOccluded()' must stop short of the target
-                       // point by distance 'endoff' (end offset) to not enter the target mapblock.
-                       // For the 8 mapblock corners 'endoff' 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 endoff = -BS * MAP_BLOCKSIZE * 1.732050807569;
-                       v3s16 spn = cam_pos_nodes;
-                       s16 bs2 = MAP_BLOCKSIZE / 2 + 1;
-                       // 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;
-                       if (occlusion_culling_enabled &&
-                                       // For the central point of the mapblock 'endoff' can be halved
-                                       isOccluded(this, spn, cpn,
-                                               step, stepfac, startoff, endoff / 2.0f, needed_count, m_nodedef) &&
-                                       isOccluded(this, spn, cpn + v3s16(bs2,bs2,bs2),
-                                               step, stepfac, startoff, endoff, needed_count, m_nodedef) &&
-                                       isOccluded(this, spn, cpn + v3s16(bs2,bs2,-bs2),
-                                               step, stepfac, startoff, endoff, needed_count, m_nodedef) &&
-                                       isOccluded(this, spn, cpn + v3s16(bs2,-bs2,bs2),
-                                               step, stepfac, startoff, endoff, needed_count, m_nodedef) &&
-                                       isOccluded(this, spn, cpn + v3s16(bs2,-bs2,-bs2),
-                                               step, stepfac, startoff, endoff, needed_count, m_nodedef) &&
-                                       isOccluded(this, spn, cpn + v3s16(-bs2,bs2,bs2),
-                                               step, stepfac, startoff, endoff, needed_count, m_nodedef) &&
-                                       isOccluded(this, spn, cpn + v3s16(-bs2,bs2,-bs2),
-                                               step, stepfac, startoff, endoff, needed_count, m_nodedef) &&
-                                       isOccluded(this, spn, cpn + v3s16(-bs2,-bs2,bs2),
-                                               step, stepfac, startoff, endoff, needed_count, m_nodedef) &&
-                                       isOccluded(this, spn, cpn + v3s16(-bs2,-bs2,-bs2),
-                                               step, stepfac, startoff, endoff, needed_count, m_nodedef)) {
+                       if (occlusion_culling_enabled && isBlockOccluded(block, cam_pos_nodes)) {
                                blocks_occlusion_culled++;
                                continue;
                        }
index e9ee1135fb676850f4a97ee96835b6460fd18a1a..483c9cff6773b05d2e9491b6d20470d303a62f5e 100644 (file)
@@ -282,6 +282,7 @@ void set_default_settings(Settings *settings)
        // This causes frametime jitter on client side, or does it?
        settings->setDefault("max_block_send_distance", "9");
        settings->setDefault("block_send_optimize_distance", "4");
+       settings->setDefault("server_side_occlusion_culling", "false");
        settings->setDefault("max_clearobjects_extra_loaded_blocks", "4096");
        settings->setDefault("time_speed", "72");
        settings->setDefault("server_unload_unused_data_timeout", "29");
index a1502befa5ff0a7aa161b062dae82c340ce01d2d..43a49dc2f6ddb068d930ccb61d15070c6477ebe8 100644 (file)
@@ -1157,6 +1157,72 @@ void Map::removeNodeTimer(v3s16 p)
        block->m_node_timers.remove(p_rel);
 }
 
+bool Map::isOccluded(v3s16 p0, v3s16 p1, float step, float stepfac,
+               float start_off, float end_off, u32 needed_count)
+{
+       float d0 = (float)BS * p0.getDistanceFrom(p1);
+       v3s16 u0 = p1 - p0;
+       v3f uf = v3f(u0.X, u0.Y, u0.Z) * BS;
+       uf.normalize();
+       v3f p0f = v3f(p0.X, p0.Y, p0.Z) * BS;
+       u32 count = 0;
+       for(float s=start_off; s<d0+end_off; s+=step){
+               v3f pf = p0f + uf * s;
+               v3s16 p = floatToInt(pf, BS);
+               MapNode n = getNodeNoEx(p);
+               const ContentFeatures &f = m_nodedef->get(n);
+               if(f.drawtype == NDT_NORMAL){
+                       // not transparent, see ContentFeature::updateTextures
+                       count++;
+                       if(count >= needed_count)
+                               return true;
+               }
+               step *= stepfac;
+       }
+       return false;
+}
+
+bool Map::isBlockOccluded(MapBlock *block, v3s16 cam_pos_nodes) {
+       v3s16 cpn = block->getPos() * MAP_BLOCKSIZE;
+       cpn += v3s16(MAP_BLOCKSIZE / 2, MAP_BLOCKSIZE / 2, MAP_BLOCKSIZE / 2);
+       float step = BS * 1;
+       float stepfac = 1.1;
+       float startoff = BS * 1;
+       // The occlusion search of 'isOccluded()' must stop short of the target
+       // point by distance 'endoff' (end offset) to not enter the target mapblock.
+       // For the 8 mapblock corners 'endoff' 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 endoff = -BS * MAP_BLOCKSIZE * 1.732050807569;
+       v3s16 spn = cam_pos_nodes;
+       s16 bs2 = MAP_BLOCKSIZE / 2 + 1;
+       // 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;
+
+       return (
+               // For the central point of the mapblock 'endoff' can be halved
+               isOccluded(spn, cpn,
+                       step, stepfac, startoff, endoff / 2.0f, needed_count) &&
+               isOccluded(spn, cpn + v3s16(bs2,bs2,bs2),
+                       step, stepfac, startoff, endoff, needed_count) &&
+               isOccluded(spn, cpn + v3s16(bs2,bs2,-bs2),
+                       step, stepfac, startoff, endoff, needed_count) &&
+               isOccluded(spn, cpn + v3s16(bs2,-bs2,bs2),
+                       step, stepfac, startoff, endoff, needed_count) &&
+               isOccluded(spn, cpn + v3s16(bs2,-bs2,-bs2),
+                       step, stepfac, startoff, endoff, needed_count) &&
+               isOccluded(spn, cpn + v3s16(-bs2,bs2,bs2),
+                       step, stepfac, startoff, endoff, needed_count) &&
+               isOccluded(spn, cpn + v3s16(-bs2,bs2,-bs2),
+                       step, stepfac, startoff, endoff, needed_count) &&
+               isOccluded(spn, cpn + v3s16(-bs2,-bs2,bs2),
+                       step, stepfac, startoff, endoff, needed_count) &&
+               isOccluded(spn, cpn + v3s16(-bs2,-bs2,-bs2),
+                       step, stepfac, startoff, endoff, needed_count));
+}
+
 /*
        ServerMap
 */
index c4181a49f48791dcda8b18e0d012fcf079e37af7..aeb05c70435da06d2ae85ba0a7beef7e13f44ccc 100644 (file)
--- a/src/map.h
+++ b/src/map.h
@@ -314,6 +314,7 @@ public:
        void transforming_liquid_add(v3s16 p);
        s32 transforming_liquid_size();
 
+       bool isBlockOccluded(MapBlock *block, v3s16 cam_pos_nodes);
 protected:
        friend class LuaVoxelManip;
 
@@ -335,6 +336,9 @@ protected:
        // This stores the properties of the nodes on the map.
        INodeDefManager *m_nodedef;
 
+       bool isOccluded(v3s16 p0, v3s16 p1, float step, float stepfac,
+                       float start_off, float end_off, u32 needed_count);
+
 private:
        f32 m_transforming_liquid_loop_count_multiplier;
        u32 m_unprocessed_count;