c++ - 在大小为 n 的 3D 数组中有效检查相邻体素

标签 c++ arrays access-violation voxels

我的体素系统使用在运行时为每个 block 动态分配的平面 3D 数组,但是每个 block 生成数百万个立方体是不可行的,因此我需要优化。

我打算实现的第一个优化当然是不为被遮挡的体素生成网格数据,这在纸面上是个好主意,但我不知道该怎么做。

我所有的尝试都以难以调试内存分配问题而告终,因此我不得不放弃并询问更多知识渊博的人,因为我不知所措。

我目前的化身是这样的

int8_t x = 0, y= 0, z = 0;
const int MAX = CHUNKSIZE-1;
const int MIN = 0;


int8_t sPosX = (x - 1 < MIN) ? x : x-1;
int8_t sPosY = (y - 1 < MIN) ? y : y-1;
int8_t sPosZ = (z - 1 < MIN) ? z : z-1;

int8_t ePosX = (x + 1 > MAX) ? x : x+1;
int8_t ePosY = (y + 1 > MAX) ? y : y+1;
int8_t ePosZ = (z + 1 > MAX) ? z : z+1;
int8_t a=sPosX, b=sPosY, c=sPosZ;
int8_t add = 0;


BlockType BT = BT_grass;
scene::SMesh* mesh = new scene::SMesh();
for(x = 0; x <= MAX; x++)
{
    for(y = 0; y <= MAX; y++)
    {
        for(z = 0; z <= MAX; z++)
        {
            cm = b_blocks[x][y][z].material;
            //b_blocks[x][y][z].setFlags(0xFE, BT);
            if( !b_blocks[x][x][z].isActive() )
            {
                continue;
            }
            else
            {
                if(sPosX == MIN)
                {
                    createCube(x,y,z,c,mesh,cm);
                }

                else
                {
                    if(a<=ePosX) 
                    {
                        if(b<=ePosY) 
                        {
                            if(c<=ePosZ) 
                            {
                                printf("x %d, y %d, z %d\n", x, y, z);
                                if(!b_blocks[x][y][z].isActive())
                                {
                                    add = 1;
                                }
                            }
                        }
                    }
                    if(add == 1)
                    {
                        createCube(x,y,z,c,mesh,cm);
                        add = 0;
                    }
                }
            }
        }
    }
}

if(sPosX == MIN) 是我实现的一个 hack,目的是在生成 block 时不出现段错误(否则它会在生成 block [CHUNKSIZE][CHUNKSIZE][CHUNKSIZE] 时出现内存访问冲突的段错误,这不是很好好的。 然而,这种 hack 无意中确保生成了所有多维数据集,而且同样没有吸引力。

这里的简明问题如下: 我的逻辑的哪一部分被打破了? (大概是全部)以及如何以一种不会导致越界错误的快速方式正确检查相邻 block ? (我尝试为每个最后的角落案例手动编码异常,但事实证明这是不可维护的,而且速度慢了几个数量级并且容易出现段错误)

最佳答案

我会使用类似的东西:

class BlockChunk final
{
public:
    static constexpr int sizeXShift = 4, sizeYshift = 8, sizeZshift = 4;
    static constexpr int sizeX = 1 << sizeXShift; // 2 ** sizeXShift
    static constexpr int sizeY = 1 << sizeYShift; 
    static constexpr int sizeZ = 1 << sizeZShift;
    static constexpr int sizeXRelativeMask = sizeX - 1; // mask to get position mod sizeX (faster than % because negative inputs to % return negative answers which need more adjusting whereas masking always returns the positive answer)
    static constexpr int sizeYRelativeMask = sizeY - 1;
    static constexpr int sizeZRelativeMask = sizeZ - 1;
    static constexpr int sizeXChunkBaseMask = ~sizeXRelativeMask; // mask to get position - relativePosition (aka chunk base position)
    static constexpr int sizeYChunkBaseMask = ~sizeYRelativeMask;
    static constexpr int sizeZChunkBaseMask = ~sizeZRelativeMask;
private:
    Block blocks[sizeX][sizeY][sizeZ];
public:
    const PositionI basePosition;
    BlockChunk(PositionI basePosition)
        : basePosition(basePosition)
    {
    }
    Block &at(PositionI relative)
    {
        assert(relative.x >= 0 && relative.x < sizeX);
        assert(relative.y >= 0 && relative.y < sizeY);
        assert(relative.z >= 0 && relative.z < sizeZ); // these asserts are important for finding out-of-bounds bugs
        return blocks[relative.x][relative.y][relative.z];
    }
    static PositionI getRelativePosition(PositionI p)
    {
        p.x &= sizeXRelativeMask;
        p.y &= sizeYRelativeMask;
        p.z &= sizeZRelativeMask;
        return p;
    }
    static PositionI getChunkBasePosition(PositionI p)
    {
        p.x &= sizeXChunkBaseMask;
        p.y &= sizeYChunkBaseMask;
        p.z &= sizeZChunkBaseMask;
        return p;
    }
};

class BlockIterator;

class BlockWorldBase
{
    friend class BlockIterator;
private:
    std::unordered_map<PositionI, std::shared_ptr<BlockChunk>> chunks;
    BlockChunk *getOrMakeChunk(PositionI chunkBasePosition)
    {
        std::shared_ptr<BlockChunk> &chunk = chunks[chunkBasePosition];
        if(chunk == nullptr)
            chunk = std::make_shared<BlockChunk>(chunkBasePosition);
        return chunk.get();
    }
};

class BlockWorld;

class BlockIterator final
{
    friend class BlockWorld;
private:
    BlockChunk *chunk;
    BlockWorldBase *world;
    PositionI chunkBasePosition, relativePosition;
    void updateChunk()
    {
        chunk = world->getOrMakeChunk(chunkBasePosition);
    }
    BlockIterator(BlockWorldBase *world, PositionI position)
        : chunk(), 
        world(world),
        chunkBasePosition(BlockChunk::getChunkBasePosition(position)),
        relativePosition(BlockChunk::getRelativePosition(position))
    {
        updateChunk();
    }
public:
    PositionI getPosition() const
    {
        return relativePosition + chunkBasePosition;
    }
    Block &get()
    {
        return chunk->at(relativePosition);
    }
    BlockIterator &operator +=(PositionI deltaPosition) // move to point to a new block
    {
        PositionI newRelativePosition = relativePosition + deltaPosition;
        if(BlockChunk::getRelativePosition(newRelativePosition) != newRelativePosition) // if the new position is outside of this chunk
        {
            relativePosition = BlockChunk::getRelativePosition(newRelativePosition);
            chunkBasePosition += BlockChunk::getChunkBasePosition(newRelativePosition);
            updateChunk();
        }
        else
        {
            relativePosition = newRelativePosition;
        }
    }
    friend BlockIterator operator +(PositionI p, BlockIterator bi)
    {
        bi += p;
        return bi;
    }
    friend BlockIterator operator +(BlockIterator bi, PositionI p)
    {
        bi += p;
        return bi;
    }
};

class BlockWorld final : public BlockWorldBase
{
public:
    BlockIterator getIterator(PositionI p)
    {
        return BlockIterator(this, p);
    }
};

如果您保留断言并通过 BlockIterator 访问,您永远不应该出现段错误

void drawBlock(Renderer &renderer, BlockIterator bi)
{
    BlockIterator nxBlockIterator = bi + PositionI(-1, 0, 0);
    BlockIterator pxBlockIterator = bi + PositionI(1, 0, 0);
    BlockIterator nyBlockIterator = bi + PositionI(0, -1, 0);
    BlockIterator pyBlockIterator = bi + PositionI(0, 1, 0);
    BlockIterator nzBlockIterator = bi + PositionI(0, 0, -1);
    BlockIterator pzBlockIterator = bi + PositionI(0, 0, 1);
    if(nxBlockIterator.get().isPXFaceBlocked())
        bi.get().renderNXFace(renderer, bi);
    if(pxBlockIterator.get().isNXFaceBlocked())
        bi.get().renderPXFace(renderer, bi);
    if(nyBlockIterator.get().isPYFaceBlocked())
        bi.get().renderNYFace(renderer, bi);
    if(pyBlockIterator.get().isNYFaceBlocked())
        bi.get().renderPYFace(renderer, bi);
    if(nzBlockIterator.get().isPZFaceBlocked())
        bi.get().renderNZFace(renderer, bi);
    if(pzBlockIterator.get().isNZFaceBlocked())
        bi.get().renderPZFace(renderer, bi);
    bi.get().renderCenter(renderer, bi);
}

关于c++ - 在大小为 n 的 3D 数组中有效检查相邻体素,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30220398/

相关文章:

c++ - C++代码的静态链接

c++ - std::set 中索引处的元素?

iOS Swift 过滤器和生成模型需要太多时间

javascript - Javascript 中两个数组的连接

执行指针时出现 C++ 访问冲突

c - 使用 fopen_s 打开文件流时出现未处理的异常访问冲突

c++ - 导致在 32 位代码中使用 64 位变量会导致性能下降

javascript - indexOf 不是函数,无法在数组中找到元素的索引

c# - 尝试读取或写入 protected 内存。这通常表明其他内存已损坏

c++ - 使用 CUDA 进行矩阵操作