简体   繁体   English

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

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

So I'm currently working in a chunk with a size of 16x256x16 and create a integer grid for block types.所以我目前正在处理一个大小为 16x256x16 的块,并为块类型创建一个 integer 网格。 But my problem is how do I implement infinite voxel chunks???但我的问题是如何实现无限体素块??? I use SFML 1.6 by the way.顺便说一下,我使用 SFML 1.6。

Here is my code:这是我的代码:

Header (chunk.hpp): 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

Source (chunk.cpp):来源(块.cpp):

I put the m_blockgrid in.cpp because if I put it in the header it will only draw one cube and also does in Chunk::~Chunk() delete[] chnk::m_blockgrid necessary???我把 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
}

Some people uses unordered_map to store loaded/unloaded chunk but I dont know how to use it and if it does work??有些人使用 unordered_map 来存储加载/卸载的块,但我不知道如何使用它以及它是否有效?

Anyone would like to help me??有人愿意帮助我吗?? :) :)

I will make the following suppositions:我会做以下假设:

  1. Access to a given chunk is the priority, they are accessed several times a frame so access needs to be O(k)优先访问给定的块,它们在一帧中被访问多次,因此访问需要为 O(k)
  2. Insertion and deletion of chunks needs to be done as fast as possible, because they will be generated on the fly, and dropped on the fly.块的插入和删除需要尽可能快地完成,因为它们将在运行中生成,并在运行中丢弃。 Not as critical as the access.不像访问那么重要。
  3. Most of the time, the amount of chunks in memory is about constant.大多数时候,memory 中的块数量大约是恒定的。

Now, let see what are our possibilities:现在,让我们看看我们的可能性是什么:

std::
container容器
Insertion插入 Access使用权 Erase擦除 Find寻找 Persistent执着的
Iterators迭代器
vector / vector /
string
Back: O(1) or O(n)返回:O(1) 或 O(n)
Other: O(n)其他:O(n)
O(1) O(1) Back: O(1)返回:O(1)
Other: O(n)其他:O(n)
Sorted: O(log n)排序:O(log n)
Other: O(n)其他:O(n)
No
deque Back/Front: O(1)后/前:O(1)
Other: O(n)其他:O(n)
O(1) O(1) Back/Front: O(1)后/前:O(1)
Other: O(n)其他:O(n)
Sorted: O(log n)排序:O(log n)
Other: O(n)其他:O(n)
Pointers only仅指针
list / list /
forward_list
Back/Front: O(1)后/前:O(1)
With iterator: O(1)使用迭代器:O(1)
Index: O(n)索引:O(n)
Back/Front: O(1)后/前:O(1)
With iterator: O(1)使用迭代器:O(1)
Index: O(n)索引:O(n)
Back/Front: O(1)后/前:O(1)
With iterator: O(1)使用迭代器:O(1)
Index: O(n)索引:O(n)
O(n)在) Yes是的
set / map set / map O(log n) O(log n) - - O(log n) O(log n) O(log n) O(log n) Yes是的
unordered_set / unordered_set /
unordered_map
O(1) or O(n) O(1) 或 O(n) O(1) or O(n) O(1) 或 O(n) O(1) or O(n) O(1) 或 O(n) O(1) or O(n) O(1) 或 O(n) Pointers only仅指针
priority_queue O(log n) O(log n) O(1) O(1) O(log n) O(log n) - - - -

std::unordered_map is the most adequate structure for this because it allows constant access, and (most of the time) constant insertion/deletion: 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;

Note: Insertion and Erase on unordered_map is usually O(k) (constant), unless space is missing and all the map needs to be relocated.注意: unordered_map上的插入和擦除通常是O(k) (常数),除非缺少空间并且需要重新定位所有 map。 as you have usually a constant number of chunks loaded, you can unordered_map::reserve a sufficient amount of chunks so that it is never relocated.因为通常加载的块数是恒定的,所以您可以unordered_map::reserve足够数量的块,这样它就永远不会被重新定位。

Note2: I use pointers to Chunk to make any relocation faster.注 2:我使用指向Chunk的指针来更快地进行任何重定位。

Here is some example of usage: https://onlinegdb.com/FiGzEzHgD以下是一些使用示例: https://onlinegdb.com/FiGzEzHgD

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

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