[英]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:我会做以下假设:
Now, let see what are our possibilities:现在,让我们看看我们的可能性是什么:
std:: container ![]() |
Insertion![]() |
Access![]() |
Erase![]() |
Find![]() |
Persistent![]() Iterators ![]() |
---|---|---|---|---|---|
vector / ![]() vector /string |
Back: O(1) or O(n)![]() Other: O(n) ![]() |
O(1) ![]() |
Back: O(1)![]() Other: O(n) ![]() |
Sorted: O(log n)![]() Other: O(n) ![]() |
No![]() |
deque |
Back/Front: O(1)![]() Other: O(n) ![]() |
O(1) ![]() |
Back/Front: O(1)![]() Other: O(n) ![]() |
Sorted: O(log n)![]() Other: O(n) ![]() |
Pointers only![]() |
list / ![]() list /forward_list |
Back/Front: O(1)![]() With iterator: O(1) ![]() Index: O(n) ![]() |
Back/Front: O(1)![]() With iterator: O(1) ![]() Index: O(n) ![]() |
Back/Front: O(1)![]() With iterator: O(1) ![]() Index: O(n) ![]() |
O(n)![]() |
Yes![]() |
set / map ![]() set / map |
O(log n) ![]() |
- ![]() |
O(log n) ![]() |
O(log n) ![]() |
Yes![]() |
unordered_set / ![]() unordered_set /unordered_map |
O(1) or O(n) ![]() |
O(1) or O(n) ![]() |
O(1) or O(n) ![]() |
O(1) or O(n) ![]() |
Pointers only![]() |
priority_queue |
O(log n) ![]() |
O(1) ![]() |
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.