繁体   English   中英

如何在不降低 c++ 性能的情况下实现无限体素块?

[英]How do I implement infinite voxel chunks without slowing down the performance in c++?

所以我目前正在处理一个大小为 16x256x16 的块,并为块类型创建一个 integer 网格。 但我的问题是如何实现无限体素块??? 顺便说一下,我使用 SFML 1.6。

这是我的代码:

Header (块.hpp):

#ifndef CHUNK_HPP
#define CHUNK_HPP

#pragma once
#include <SFML/Graphics.hpp>
#include "player.hpp"
#include "types.hpp"
#include "frustumcull.hpp"
#include "noise_generator.hpp"

const int horiz_chunksize = 16;
const int vert_chunksize = 256;

class Chunk {
public:

    Chunk();
    ~Chunk();

    int get(int x, int y, int z); // get block type in position
    void set(int x, int y, int z, int type); // set block type in position
    void render(Player &p, FrustumCull &cull, int renderDistance = 20); // render chunk
    int getTerrainHeight(int x, int y); // returns noise height on the given position

private:

    Perlin_Noise m_noise;
    void update(int x, int y, int z, int type); // update block faces
    
};

#endif // CHUNK_HPP

来源(块.cpp):

我把 m_blockgrid 放在 .cpp 中,因为如果我把它放在 header 中,它只会绘制一个立方体,并且在 Chunk::~Chunk() 中删除 [] chnk::m_blockgrid 也是必要的???

#include "chunk.hpp"
#include "block.hpp"
#include "maths.hpp"
#include <iostream>

namespace chnk {
    int m_blockgrid[horiz_chunksize][vert_chunksize][horiz_chunksize]; // block grid
    Block *m_block;
}

Chunk::Chunk() {
    chnk::m_block = new Block(); // initialize block class
    m_noise.setSeed(sf::Randomizer::Random(2736473, 8476864));

    for(int x = 0; x < horiz_chunksize; x++) {
        for(int z = 0; z < horiz_chunksize; z++) {
            int heightmap = 16;
            for(int y =-1; y < heightmap; y++) {
                if(y > heightmap-2) set(x, y, z, BlockType::GRASS);
                if(y < heightmap-1 && y > heightmap - 4) set(x, y, z, BlockType::DIRT);
                if(y < heightmap-3 && y > 0) set(x, y, z, BlockType::STONE);
                if(y == 0) set(x, y, z, BlockType::BEDROCK);
            }
        }
    }

}

Chunk::~Chunk() {
    delete[] chnk::m_blockgrid;
}

int Chunk::get(int x, int y, int z) {
    // check boundary
    if((x<0) || (x>=horiz_chunksize) ||
       (y<0) || (y>=vert_chunksize) ||
       (z<0) || (z>=horiz_chunksize)) return BlockType::AIR;
    return chnk::m_blockgrid[x][y][z];
}

void Chunk::set(int x, int y, int z, int type) {
    chnk::m_blockgrid[x][y][z] = type;
    m_update = true;
}

void Chunk::render(Player &p, FrustumCull &cull, int renderDistance) {
    int px = p.m_position.x / chnk::m_block->m_size;
    int py = (p.m_position.y + p.m_bottom) / chnk::m_block->m_size;
    int pz = p.m_position.z / chnk::m_block->m_size;
    float radius = sqrt(Maths::sqr(chnk::m_block->m_size) * 5);

    glEnable(GL_CULL_FACE); // hide back face
    glEnable(GL_DEPTH_TEST); // depth testing

    // render object(s)
    for(int x = 0; x < horiz_chunksize; x++) {
        for(int z = 0; z < horiz_chunksize; z++) {
            for(int y = 0; y < vert_chunksize; y++) {
                int type = get(x, y, z);
                if(!cull.sphereInFrustum(sf::Vector3f(chnk::m_block->m_size * x + chnk::m_block->m_size / 2, chnk::m_block->m_size * y + chnk::m_block->m_size / 2, chnk::m_block->m_size * z + chnk::m_block->m_size / 2), radius)) continue;
                update(x, y, z, type); // update for block texture & etc.
            }
        }
    }
}

void Chunk::update(int x, int y, int z, int type) {
    // only show face in outside not inside
    // I use get(x, y, z) to get block position at given grid
    if(BlockType::getSolidBlocks(type)) {
        if(BlockType::getSolidBlocks(get(x, y+1, z)) == 0 && get(x, y+1, z) != type) {
            chnk::m_block->setupBlock(x, y, z, Block::Top); // Top Face
        }
        if(BlockType::getSolidBlocks(get(x, y-1, z)) == 0 && get(x, y-1, z) != type) {
            chnk::m_block->setupBlock(x, y, z, Block::Bottom); // Bottom Face
        }
        if(BlockType::getSolidBlocks(get(x, y, z-1)) == 0 && get(x, y, z-1) != type) {
            chnk::m_block->setupBlock(x, y, z, Block::Front); // Front Face
        }
        if(BlockType::getSolidBlocks(get(x, y, z+1)) == 0 && get(x, y, z+1) != type) {
            chnk::m_block->setupBlock(x, y, z, Block::Back); // Back Face
        }
        if(BlockType::getSolidBlocks(get(x-1, y, z)) == 0 && get(x-1, y, z) != type) {
            chnk::m_block->setupBlock(x, y, z, Block::Left); // Left Face
        }
        if(BlockType::getSolidBlocks(get(x+1, y, z)) == 0 && get(x+1, y, z) != type) {
            chnk::m_block->setupBlock(x, y, z, Block::Right); // Right Face
        }
    }
}

int Chunk::getTerrainHeight(int x, int y) {
    return ( m_noise.getHeight(x, y) + 64 ); // total height of the given coordinates
}

有些人使用 unordered_map 来存储加载/卸载的块,但我不知道如何使用它以及它是否有效?

有人愿意帮助我吗?? :)

我会做以下假设:

  1. 优先访问给定的块,它们在一帧中被访问多次,因此访问需要为 O(k)
  2. 块的插入和删除需要尽可能快地完成,因为它们将在运行中生成,并在运行中丢弃。 不像访问那么重要。
  3. 大多数时候,memory 中的块数量大约是恒定的。

现在,让我们看看我们的可能性是什么:

std::
容器
插入 使用权 擦除 寻找 执着的
迭代器
vector /
string
返回:O(1) 或 O(n)
其他:O(n)
O(1) 返回:O(1)
其他:O(n)
排序:O(log n)
其他:O(n)
deque 后/前:O(1)
其他:O(n)
O(1) 后/前:O(1)
其他:O(n)
排序:O(log n)
其他:O(n)
仅指针
list /
forward_list
后/前:O(1)
使用迭代器:O(1)
索引:O(n)
后/前:O(1)
使用迭代器:O(1)
索引:O(n)
后/前:O(1)
使用迭代器:O(1)
索引:O(n)
在) 是的
set / map O(log n) - O(log n) O(log n) 是的
unordered_set /
unordered_map
O(1) 或 O(n) O(1) 或 O(n) O(1) 或 O(n) O(1) 或 O(n) 仅指针
priority_queue O(log n) O(1) O(log n) - -

std::unordered_map是最合适的结构,因为它允许常量访问,以及(大部分时间)常量插入/删除:

struct ChunkCoordinate
{
    int32_t x,
    int32_t y
};
class ChunkCoordinateHash{
public:
    size_t operator()(const ChunkCoordinate &val) const
    {
        static_assert(sizeof(size_t)==8);
        return (static_cast<size_t>(val.x)<<32ull) + (static_cast<size_t>(val.y)&0xffffffff);
    }
};

std::unordered_map<ChunkCoordinate, unique_ptr<Chunk>, ChunkCoordinateHash> m_chunks;

注意: unordered_map上的插入和擦除通常是O(k) (常数),除非缺少空间并且需要重新定位所有 map。 因为通常加载的块数是恒定的,所以您可以unordered_map::reserve足够数量的块,这样它就永远不会被重新定位。

注 2:我使用指向Chunk的指针来更快地进行任何重定位。

以下是一些使用示例: https://onlinegdb.com/FiGzEzHgD

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM