大块加载和排序

时间:2014-09-13 12:51:40

标签: c++ algorithm voxel

我正在研究“我的世界”的克隆,我有2个大块加载问题。

首先:确定要加载的块。

我找到了一种丑陋的方式但对我来说很快

  1. 定义3d数组(数组)(大小:MAX_CHUNKS_X,MAX_CHUNKS_Y,MAX_CHUNKS_Z)
  2. 使用FALSE填充3d数组
  3. 从块列表中传递,检查视觉范围内是否有块
  4. 如果在set set array [chunk_x] [chunk_y] [chunk_z] = true;
  5. 通过列表后开始低音数组
  6. 对于所有数组[chunk_x] [chunk_y] [chunk_z] == false添加到chunk_x chunk_y中的LoadingList块chunk_z
  7. 另一种不那么丑陋但仍然很快的方法?

    代码:

         ChunksRenderList.clear();
        CChunk* Chunk = NULL;
    
        s32 RootChunk_X_Location = (floor(RenderCenter.x) / CHUNK_SIZE);
        s32 RootChunk_Y_Location = (floor(RenderCenter.y) / CHUNK_SIZE);
        s32 RootChunk_Z_Location = (floor(RenderCenter.z) / CHUNK_SIZE);
    
        if(RenderCenter.x < 0)
            RootChunk_X_Location--;
    
        if(RenderCenter.y < 0)
            RootChunk_Y_Location--;
    
        if(RenderCenter.z < 0)
            RootChunk_Z_Location--;
    
        core::vector3s RootChunkLocation(RootChunk_X_Location,RootChunk_Y_Location,RootChunk_Z_Location);
    
        u32 XZ_ArraySide = (RenderDistance_XZ*2)+1;
        u32 Y_ArraySide  = (RenderDistance_Y*2)+1;
        char array[XZ_ArraySide][Y_ArraySide][XZ_ArraySide];
    
        memset(array,0,(XZ_ArraySide*XZ_ArraySide*Y_ArraySide));
    
        for(auto it = Chunks.begin(); it != Chunks.end(); it++)
        {
            Chunk = (it->second);
    
            if(Chunk->Locked)
                continue;
    
            if(Chunk->KeepAliveCounter <= 0)
            {
                ChunksUnloadList.push_back(Chunk);
                continue;
            }
            else
            {
                Chunk->KeepAliveCounter -= WORLD_UPDATE_PERIOD;
                Chunk->DistanceToCamera = RenderCenter.distance_to(Chunk->ChunkAbsolutePosition);
            }
    
            if(Chunk->ChunkPosition.x >= (RootChunk_X_Location - (s32)RenderDistance_XZ) && Chunk->ChunkPosition.x <= (RootChunk_X_Location + (s32)RenderDistance_XZ))
                if(Chunk->ChunkPosition.y >= (RootChunk_Y_Location - (s32)RenderDistance_Y) && Chunk->ChunkPosition.y <= (RootChunk_Y_Location + (s32)RenderDistance_Y))
                    if(Chunk->ChunkPosition.z >= (RootChunk_Z_Location - (s32)RenderDistance_XZ) && Chunk->ChunkPosition.z <= (RootChunk_Z_Location + (s32)RenderDistance_XZ))
                    {
                        s32 PositionInMatrix_X = Chunk->ChunkPosition.x - (RootChunk_X_Location - (s32)RenderDistance_XZ);
                        s32 PositionInMatrix_Y = Chunk->ChunkPosition.y - (RootChunk_Y_Location - (s32)RenderDistance_Y);
                        s32 PositionInMatrix_Z = Chunk->ChunkPosition.z - (RootChunk_Z_Location - (s32)RenderDistance_XZ);
    
                        array[PositionInMatrix_X][PositionInMatrix_Y][PositionInMatrix_Z] = true;
    
                        Chunk->KeepAliveCounter = CHUNK_LIVE_TIME;
                    }
    
    
            if(not Chunk->NeightboarsUpdated)
            {
                ChunksNeightboarUpdateList.push_back(Chunk);
            }
    
            if(not Chunk->ChunkUpdated)
            {
                ChunksRebuildList.push_back(Chunk);
            }
            if(not Chunk->Locked and Chunk->VisibleBlocks > 0)
            {
                ChunksRenderList.push_back(Chunk);
            }
    
        }
    
        for(u32 y = 0; y < Y_ArraySide; y++)
            for(u32 x = 0; x < XZ_ArraySide; x++)
                for(u32 z = 0; z < XZ_ArraySide; z++)
                {
                    s32 ChunkPosition_X = (s32)x + (RootChunk_X_Location - (s32)RenderDistance_XZ);
                    s32 ChunkPosition_Y = (s32)y + (RootChunk_Y_Location - (s32)RenderDistance_Y);
                    s32 ChunkPosition_Z = (s32)z + (RootChunk_Z_Location - (s32)RenderDistance_XZ);
    
                    if(array[x][y][z] == 0)
                    {
    
       SPendingToLoad ToLoad;
                        ToLoad.Position.set(ChunkPosition_X,ChunkPosition_Y,ChunkPosition_Z);
                        ToLoad.DistanceToCamera = ToLoad.Position.distance_to_sqr(RootChunkLocation);
                        ChunksLoadList.push_back(ToLoad);
                    }
                }
    

    第二: 如何对ChunksLoadList进行排序才能在此图片中生效 https://www.dropbox.com/s/owjfaaekcj2m23w/58f2e4c8.png?dl=0 Red =离ChunksLoadList.begin()最近 Blue =最接近ChunksLoadList.begin()

    我尝试使用

        ChunksLoadList.sort([&RootChunkLocation](SPendingToLoad& i,SPendingToLoad& j)
        {
    
            return i.DistanceToCamera < j.DistanceToCamera;
        }
        );
    

    但它可以减缓大视野范围...... 如何重写代码以获得快速波浪加载效果?

    对不起我糟糕的英语,我希望你理解我......

1 个答案:

答案 0 :(得分:0)

让我们首先看一下距离排序问题,如果你的ChunksLoadList是std :: list而不是std :: vector或std :: array(C ++ 11)你已经失去了性能竞争! Bjarne Stroustrup: Why you should avoid Linked Lists密切关注图表!

如果你把它改成std :: vector后仍然太慢,你可以试试#34;我刚刚发明的这种方法(TM)&#34;!

最好的排序算法类似于

  

O(C + K * N log log N)fastest?

有一个可怕的C恒定准备时间,每个元素可怕的K和一个非常漂亮的N log log N

对于N - >;无穷大,这将是O(N log log N)

但是这个问题有一个更好的算法!
洪水填充后插入排序,洪水填充在O(N)中生成几乎排序的列表,插入排序从O(N)中的部分排序中获取完全有序列表,总共O(N)...

  

O(C + K * N)

有一个可怕的恒定准备时间,每个元素可怕但只有N次

variant of wikipedia

 Flood-fill (node, target-color, replacement-color): 

 If target-color is equal to replacement-color, return.
 Set Q to the empty queue. [must be std::vector or std::array or this will fail]
 Add camera node to the end of Q.
 While Q is not empty: 
     Set n equal to the *first* element of Q.
     Remove *first* element from Q.
     If the color of n is equal to target-color:
         Add n to the distance list as the next closed (this will be nearly correct)
         Set the color of n to replacement-color and mark "n" as processed.
         Add adjacent nodes to end of Q if they has not been processed yet. (x/y/z +1/-1)
 Return.

队列元素是x,y,z
使用std :: dequeue

距离列表也必须是随机访问包含,从大小开始(viewdistance * 2 + 1)^ 3完全分配,这可能很大。
如果视距100是201 ^ 3 = ~8000000体素,你真的想要这个吗?如果你需要一些信息,你必须有一些指针或索引,至少4个字节,这会在大多数系统上吹掉缓存。

作为洪水填充它不起作用,但作为距离的近似值 如果满足您的要求,您可以在这里停止 IF 你需要总排序然后在nearly sorted list O(N)上运行插入排序,但是你也需要计算相机距离。

潜在的进一步优化:

  • 不透明体素不会添加也是不透明的邻居。
  • 空气(完全透明)不会添加到相机列表中,但需要在那里填充,以防飞行岛存在。