[英]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 来存储加载/卸载的块,但我不知道如何使用它以及它是否有效?
有人愿意帮助我吗?? :)
我会做以下假设:
现在,让我们看看我们的可能性是什么:
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.