chunks rendering multithreading
This commit is contained in:
@@ -8,7 +8,6 @@
|
|||||||
#include "../window/Window.h"
|
#include "../window/Window.h"
|
||||||
#include "../window/Camera.h"
|
#include "../window/Camera.h"
|
||||||
#include "../content/Content.h"
|
#include "../content/Content.h"
|
||||||
#include "../graphics/ChunksRenderer.h"
|
|
||||||
#include "../graphics/Mesh.h"
|
#include "../graphics/Mesh.h"
|
||||||
#include "../graphics/Atlas.h"
|
#include "../graphics/Atlas.h"
|
||||||
#include "../graphics/Shader.h"
|
#include "../graphics/Shader.h"
|
||||||
@@ -33,6 +32,7 @@
|
|||||||
#include "../items/Inventory.h"
|
#include "../items/Inventory.h"
|
||||||
#include "LevelFrontend.h"
|
#include "LevelFrontend.h"
|
||||||
#include "graphics/Skybox.h"
|
#include "graphics/Skybox.h"
|
||||||
|
#include "graphics/ChunksRenderer.h"
|
||||||
|
|
||||||
using glm::vec3;
|
using glm::vec3;
|
||||||
using glm::vec4;
|
using glm::vec4;
|
||||||
@@ -74,7 +74,11 @@ bool WorldRenderer::drawChunk(size_t index,
|
|||||||
if (!chunk->isLighted()) {
|
if (!chunk->isLighted()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
auto mesh = renderer->getOrRender(chunk.get());
|
float distance = glm::distance(
|
||||||
|
camera->position,
|
||||||
|
glm::vec3((chunk->x + 0.5f) * CHUNK_W, camera->position.y, (chunk->z + 0.5f) * CHUNK_D)
|
||||||
|
);
|
||||||
|
auto mesh = renderer->getOrRender(chunk, distance < CHUNK_W*1.5f);
|
||||||
if (mesh == nullptr) {
|
if (mesh == nullptr) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -98,6 +102,7 @@ bool WorldRenderer::drawChunk(size_t index,
|
|||||||
void WorldRenderer::drawChunks(Chunks* chunks,
|
void WorldRenderer::drawChunks(Chunks* chunks,
|
||||||
Camera* camera,
|
Camera* camera,
|
||||||
Shader* shader) {
|
Shader* shader) {
|
||||||
|
renderer->update();
|
||||||
std::vector<size_t> indices;
|
std::vector<size_t> indices;
|
||||||
for (size_t i = 0; i < chunks->volume; i++){
|
for (size_t i = 0; i < chunks->volume; i++){
|
||||||
if (chunks->chunks[i] == nullptr)
|
if (chunks->chunks[i] == nullptr)
|
||||||
|
|||||||
@@ -2,16 +2,16 @@
|
|||||||
|
|
||||||
#include <glm/glm.hpp>
|
#include <glm/glm.hpp>
|
||||||
|
|
||||||
#include "Mesh.h"
|
#include "../../graphics/Mesh.h"
|
||||||
#include "UVRegion.h"
|
#include "../../graphics/UVRegion.h"
|
||||||
#include "../constants.h"
|
#include "../../constants.h"
|
||||||
#include "../content/Content.h"
|
#include "../../content/Content.h"
|
||||||
#include "../voxels/Block.h"
|
#include "../../voxels/Block.h"
|
||||||
#include "../voxels/Chunk.h"
|
#include "../../voxels/Chunk.h"
|
||||||
#include "../voxels/VoxelsVolume.h"
|
#include "../../voxels/VoxelsVolume.h"
|
||||||
#include "../voxels/ChunksStorage.h"
|
#include "../../voxels/ChunksStorage.h"
|
||||||
#include "../lighting/Lightmap.h"
|
#include "../../lighting/Lightmap.h"
|
||||||
#include "../frontend/ContentGfxCache.h"
|
#include "../../frontend/ContentGfxCache.h"
|
||||||
|
|
||||||
using glm::ivec3;
|
using glm::ivec3;
|
||||||
using glm::vec3;
|
using glm::vec3;
|
||||||
@@ -444,7 +444,7 @@ void BlocksRenderer::render(const voxel* voxels) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Mesh* BlocksRenderer::render(const Chunk* chunk, const ChunksStorage* chunks) {
|
void BlocksRenderer::build(const Chunk* chunk, const ChunksStorage* chunks) {
|
||||||
this->chunk = chunk;
|
this->chunk = chunk;
|
||||||
voxelsBuffer->setPosition(chunk->x * CHUNK_W - 1, 0, chunk->z * CHUNK_D - 1);
|
voxelsBuffer->setPosition(chunk->x * CHUNK_W - 1, 0, chunk->z * CHUNK_D - 1);
|
||||||
chunks->getVoxels(voxelsBuffer, settings.graphics.backlight);
|
chunks->getVoxels(voxelsBuffer, settings.graphics.backlight);
|
||||||
@@ -453,13 +453,20 @@ Mesh* BlocksRenderer::render(const Chunk* chunk, const ChunksStorage* chunks) {
|
|||||||
indexOffset = indexSize = 0;
|
indexOffset = indexSize = 0;
|
||||||
const voxel* voxels = chunk->voxels;
|
const voxel* voxels = chunk->voxels;
|
||||||
render(voxels);
|
render(voxels);
|
||||||
|
}
|
||||||
|
|
||||||
|
Mesh* BlocksRenderer::createMesh() {
|
||||||
const vattr attrs[]{ {3}, {2}, {1}, {0} };
|
const vattr attrs[]{ {3}, {2}, {1}, {0} };
|
||||||
size_t vcount = vertexOffset / BlocksRenderer::VERTEX_SIZE;
|
size_t vcount = vertexOffset / BlocksRenderer::VERTEX_SIZE;
|
||||||
Mesh* mesh = new Mesh(vertexBuffer, vcount, indexBuffer, indexSize, attrs);
|
Mesh* mesh = new Mesh(vertexBuffer, vcount, indexBuffer, indexSize, attrs);
|
||||||
return mesh;
|
return mesh;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Mesh* BlocksRenderer::render(const Chunk* chunk, const ChunksStorage* chunks) {
|
||||||
|
build(chunk, chunks);
|
||||||
|
return createMesh();
|
||||||
|
}
|
||||||
|
|
||||||
VoxelsVolume* BlocksRenderer::getVoxelsBuffer() const {
|
VoxelsVolume* BlocksRenderer::getVoxelsBuffer() const {
|
||||||
return voxelsBuffer;
|
return voxelsBuffer;
|
||||||
}
|
}
|
||||||
@@ -4,10 +4,10 @@
|
|||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <glm/glm.hpp>
|
#include <glm/glm.hpp>
|
||||||
#include "UVRegion.h"
|
#include "../../graphics/UVRegion.h"
|
||||||
#include "../typedefs.h"
|
#include "../../voxels/voxel.h"
|
||||||
#include "../voxels/voxel.h"
|
#include "../../typedefs.h"
|
||||||
#include "../settings.h"
|
#include "../../settings.h"
|
||||||
|
|
||||||
class Content;
|
class Content;
|
||||||
class Mesh;
|
class Mesh;
|
||||||
@@ -93,7 +93,9 @@ public:
|
|||||||
BlocksRenderer(size_t capacity, const Content* content, const ContentGfxCache* cache, const EngineSettings& settings);
|
BlocksRenderer(size_t capacity, const Content* content, const ContentGfxCache* cache, const EngineSettings& settings);
|
||||||
virtual ~BlocksRenderer();
|
virtual ~BlocksRenderer();
|
||||||
|
|
||||||
|
void build(const Chunk* chunk, const ChunksStorage* chunks);
|
||||||
Mesh* render(const Chunk* chunk, const ChunksStorage* chunks);
|
Mesh* render(const Chunk* chunk, const ChunksStorage* chunks);
|
||||||
|
Mesh* createMesh();
|
||||||
VoxelsVolume* getVoxelsBuffer() const;
|
VoxelsVolume* getVoxelsBuffer() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -0,0 +1,150 @@
|
|||||||
|
#include "ChunksRenderer.h"
|
||||||
|
|
||||||
|
#include "../../graphics/Mesh.h"
|
||||||
|
#include "BlocksRenderer.h"
|
||||||
|
#include "../../voxels/Chunk.h"
|
||||||
|
#include "../../world/Level.h"
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
#include <glm/glm.hpp>
|
||||||
|
#include <glm/ext.hpp>
|
||||||
|
|
||||||
|
ChunksRenderer::ChunksRenderer(Level* level, const ContentGfxCache* cache, const EngineSettings& settings)
|
||||||
|
: level(level), cache(cache), settings(settings) {
|
||||||
|
const int MAX_FULL_CUBES = 3000;
|
||||||
|
renderer = std::make_unique<BlocksRenderer>(
|
||||||
|
9 * 6 * 6 * MAX_FULL_CUBES, level->content, cache, settings
|
||||||
|
);
|
||||||
|
|
||||||
|
const uint num_threads = std::thread::hardware_concurrency();
|
||||||
|
for (uint i = 0; i < num_threads; i++) {
|
||||||
|
threads.emplace_back(&ChunksRenderer::threadLoop, this, i);
|
||||||
|
workersBlocked.emplace_back();
|
||||||
|
}
|
||||||
|
std::cout << "created " << num_threads << " chunks rendering threads" << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
ChunksRenderer::~ChunksRenderer() {
|
||||||
|
{
|
||||||
|
std::unique_lock<std::mutex> lock(jobsMutex);
|
||||||
|
working = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
resultsMutex.lock();
|
||||||
|
while (!results.empty()) {
|
||||||
|
mesh_entry entry = results.front();
|
||||||
|
results.pop();
|
||||||
|
entry.locked = false;
|
||||||
|
entry.variable.notify_all();
|
||||||
|
}
|
||||||
|
resultsMutex.unlock();
|
||||||
|
|
||||||
|
jobsMutexCondition.notify_all();
|
||||||
|
for (auto& thread : threads) {
|
||||||
|
thread.join();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ChunksRenderer::threadLoop(int index) {
|
||||||
|
const int MAX_FULL_CUBES = 3000;
|
||||||
|
BlocksRenderer renderer(
|
||||||
|
9 * 6 * 6 * MAX_FULL_CUBES, level->content, cache, settings
|
||||||
|
);
|
||||||
|
|
||||||
|
std::condition_variable variable;
|
||||||
|
std::mutex mutex;
|
||||||
|
bool locked = false;
|
||||||
|
while (working) {
|
||||||
|
std::shared_ptr<Chunk> chunk;
|
||||||
|
{
|
||||||
|
std::unique_lock<std::mutex> lock(jobsMutex);
|
||||||
|
jobsMutexCondition.wait(lock, [this] {
|
||||||
|
return !jobs.empty() || !working;
|
||||||
|
});
|
||||||
|
if (!working) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
chunk = jobs.front();
|
||||||
|
jobs.pop();
|
||||||
|
}
|
||||||
|
process(chunk, renderer);
|
||||||
|
{
|
||||||
|
resultsMutex.lock();
|
||||||
|
results.push(mesh_entry {renderer, variable, index, locked, glm::ivec2(chunk->x, chunk->z)});
|
||||||
|
locked = true;
|
||||||
|
resultsMutex.unlock();
|
||||||
|
}
|
||||||
|
{
|
||||||
|
std::unique_lock<std::mutex> lock(mutex);
|
||||||
|
variable.wait(lock, [&] {
|
||||||
|
return !working || !locked;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ChunksRenderer::process(std::shared_ptr<Chunk> chunk, BlocksRenderer& renderer) {
|
||||||
|
renderer.build(chunk.get(), level->chunksStorage);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<Mesh> ChunksRenderer::render(std::shared_ptr<Chunk> chunk, bool important) {
|
||||||
|
chunk->setModified(false);
|
||||||
|
|
||||||
|
if (important) {
|
||||||
|
Mesh* mesh = renderer->render(chunk.get(), level->chunksStorage);
|
||||||
|
auto sptr = std::shared_ptr<Mesh>(mesh);
|
||||||
|
meshes[glm::ivec2(chunk->x, chunk->z)] = sptr;
|
||||||
|
return sptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
glm::ivec2 key(chunk->x, chunk->z);
|
||||||
|
if (inwork.find(key) != inwork.end()) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
inwork[key] = true;
|
||||||
|
jobsMutex.lock();
|
||||||
|
jobs.push(chunk);
|
||||||
|
jobsMutex.unlock();
|
||||||
|
jobsMutexCondition.notify_one();
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ChunksRenderer::unload(Chunk* chunk) {
|
||||||
|
auto found = meshes.find(glm::ivec2(chunk->x, chunk->z));
|
||||||
|
if (found != meshes.end()) {
|
||||||
|
meshes.erase(found);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<Mesh> ChunksRenderer::getOrRender(std::shared_ptr<Chunk> chunk, bool important) {
|
||||||
|
auto found = meshes.find(glm::ivec2(chunk->x, chunk->z));
|
||||||
|
if (found != meshes.end()){
|
||||||
|
if (chunk->isModified()) {
|
||||||
|
render(chunk, important);
|
||||||
|
}
|
||||||
|
return found->second;
|
||||||
|
}
|
||||||
|
return render(chunk, important);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<Mesh> ChunksRenderer::get(Chunk* chunk) {
|
||||||
|
auto found = meshes.find(glm::ivec2(chunk->x, chunk->z));
|
||||||
|
if (found != meshes.end()) {
|
||||||
|
return found->second;
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ChunksRenderer::update() {
|
||||||
|
resultsMutex.lock();
|
||||||
|
while (!results.empty()) {
|
||||||
|
mesh_entry entry = results.front();
|
||||||
|
results.pop();
|
||||||
|
meshes[entry.key] = std::shared_ptr<Mesh>(entry.renderer.createMesh());
|
||||||
|
inwork.erase(entry.key);
|
||||||
|
entry.locked = false;
|
||||||
|
entry.variable.notify_all();
|
||||||
|
}
|
||||||
|
resultsMutex.unlock();
|
||||||
|
}
|
||||||
@@ -0,0 +1,67 @@
|
|||||||
|
#ifndef SRC_GRAPHICS_CHUNKSRENDERER_H_
|
||||||
|
#define SRC_GRAPHICS_CHUNKSRENDERER_H_
|
||||||
|
|
||||||
|
#include <queue>
|
||||||
|
#include <mutex>
|
||||||
|
#include <thread>
|
||||||
|
#include <memory>
|
||||||
|
#include <vector>
|
||||||
|
#include <unordered_map>
|
||||||
|
#include <glm/glm.hpp>
|
||||||
|
#include <condition_variable>
|
||||||
|
|
||||||
|
#include "../../voxels/Block.h"
|
||||||
|
#include "../../voxels/ChunksStorage.h"
|
||||||
|
#include "../../settings.h"
|
||||||
|
|
||||||
|
class Mesh;
|
||||||
|
class Chunk;
|
||||||
|
class Level;
|
||||||
|
class BlocksRenderer;
|
||||||
|
class ContentGfxCache;
|
||||||
|
|
||||||
|
struct mesh_entry {
|
||||||
|
BlocksRenderer& renderer;
|
||||||
|
std::condition_variable& variable;
|
||||||
|
int workerIndex;
|
||||||
|
bool& locked;
|
||||||
|
glm::ivec2 key;
|
||||||
|
};
|
||||||
|
|
||||||
|
class ChunksRenderer {
|
||||||
|
std::unique_ptr<BlocksRenderer> renderer;
|
||||||
|
Level* level;
|
||||||
|
std::unordered_map<glm::ivec2, std::shared_ptr<Mesh>> meshes;
|
||||||
|
std::unordered_map<glm::ivec2, bool> inwork;
|
||||||
|
std::vector<std::thread> threads;
|
||||||
|
|
||||||
|
std::queue<mesh_entry> results;
|
||||||
|
std::mutex resultsMutex;
|
||||||
|
|
||||||
|
std::queue<std::shared_ptr<Chunk>> jobs;
|
||||||
|
std::condition_variable jobsMutexCondition;
|
||||||
|
std::mutex jobsMutex;
|
||||||
|
|
||||||
|
bool working = true;
|
||||||
|
const ContentGfxCache* cache;
|
||||||
|
const EngineSettings& settings;
|
||||||
|
std::vector<std::unique_lock<std::mutex>> workersBlocked;
|
||||||
|
|
||||||
|
void threadLoop(int index);
|
||||||
|
void process(std::shared_ptr<Chunk> chunk, BlocksRenderer& renderer);
|
||||||
|
public:
|
||||||
|
ChunksRenderer(Level* level,
|
||||||
|
const ContentGfxCache* cache,
|
||||||
|
const EngineSettings& settings);
|
||||||
|
virtual ~ChunksRenderer();
|
||||||
|
|
||||||
|
std::shared_ptr<Mesh> render(std::shared_ptr<Chunk> chunk, bool important);
|
||||||
|
void unload(Chunk* chunk);
|
||||||
|
|
||||||
|
std::shared_ptr<Mesh> getOrRender(std::shared_ptr<Chunk> chunk, bool important);
|
||||||
|
std::shared_ptr<Mesh> get(Chunk* chunk);
|
||||||
|
|
||||||
|
void update();
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // SRC_GRAPHICS_CHUNKSRENDERER_H_
|
||||||
@@ -1,51 +0,0 @@
|
|||||||
#include "ChunksRenderer.h"
|
|
||||||
|
|
||||||
#include "Mesh.h"
|
|
||||||
#include "BlocksRenderer.h"
|
|
||||||
#include "../voxels/Chunk.h"
|
|
||||||
#include "../world/Level.h"
|
|
||||||
|
|
||||||
#include <glm/glm.hpp>
|
|
||||||
#include <glm/ext.hpp>
|
|
||||||
|
|
||||||
using glm::ivec2;
|
|
||||||
|
|
||||||
ChunksRenderer::ChunksRenderer(Level* level, const ContentGfxCache* cache, const EngineSettings& settings) : level(level) {
|
|
||||||
const int MAX_FULL_CUBES = 3000;
|
|
||||||
renderer = new BlocksRenderer(9 * 6 * 6 * MAX_FULL_CUBES, level->content, cache, settings);
|
|
||||||
}
|
|
||||||
|
|
||||||
ChunksRenderer::~ChunksRenderer() {
|
|
||||||
delete renderer;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<Mesh> ChunksRenderer::render(Chunk* chunk) {
|
|
||||||
chunk->setModified(false);
|
|
||||||
Mesh* mesh = renderer->render(chunk, level->chunksStorage);
|
|
||||||
auto sptr = std::shared_ptr<Mesh>(mesh);
|
|
||||||
meshes[ivec2(chunk->x, chunk->z)] = sptr;
|
|
||||||
return sptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ChunksRenderer::unload(Chunk* chunk) {
|
|
||||||
auto found = meshes.find(ivec2(chunk->x, chunk->z));
|
|
||||||
if (found != meshes.end()) {
|
|
||||||
meshes.erase(found);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<Mesh> ChunksRenderer::getOrRender(Chunk* chunk) {
|
|
||||||
auto found = meshes.find(ivec2(chunk->x, chunk->z));
|
|
||||||
if (found != meshes.end() && !chunk->isModified()){
|
|
||||||
return found->second;
|
|
||||||
}
|
|
||||||
return render(chunk);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<Mesh> ChunksRenderer::get(Chunk* chunk) {
|
|
||||||
auto found = meshes.find(ivec2(chunk->x, chunk->z));
|
|
||||||
if (found != meshes.end()) {
|
|
||||||
return found->second;
|
|
||||||
}
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
@@ -1,35 +0,0 @@
|
|||||||
#ifndef SRC_GRAPHICS_CHUNKSRENDERER_H_
|
|
||||||
#define SRC_GRAPHICS_CHUNKSRENDERER_H_
|
|
||||||
|
|
||||||
#include <memory>
|
|
||||||
#include <unordered_map>
|
|
||||||
#include <glm/glm.hpp>
|
|
||||||
#include "../voxels/Block.h"
|
|
||||||
#include "../voxels/ChunksStorage.h"
|
|
||||||
#include "../settings.h"
|
|
||||||
|
|
||||||
class Mesh;
|
|
||||||
class Chunk;
|
|
||||||
class Level;
|
|
||||||
class BlocksRenderer;
|
|
||||||
class ContentGfxCache;
|
|
||||||
|
|
||||||
class ChunksRenderer {
|
|
||||||
BlocksRenderer* renderer;
|
|
||||||
Level* level;
|
|
||||||
std::unordered_map<glm::ivec2, std::shared_ptr<Mesh>> meshes;
|
|
||||||
public:
|
|
||||||
ChunksRenderer(Level* level,
|
|
||||||
const ContentGfxCache* cache,
|
|
||||||
const EngineSettings& settings);
|
|
||||||
virtual ~ChunksRenderer();
|
|
||||||
|
|
||||||
std::shared_ptr<Mesh> render(Chunk* chunk);
|
|
||||||
void unload(Chunk* chunk);
|
|
||||||
|
|
||||||
std::shared_ptr<Mesh> getOrRender(Chunk* chunk);
|
|
||||||
std::shared_ptr<Mesh> get(Chunk* chunk);
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif // SRC_GRAPHICS_CHUNKSRENDERER_H_
|
|
||||||
@@ -291,3 +291,23 @@ double util::parse_double(const std::string& str) {
|
|||||||
double util::parse_double(const std::string& str, size_t offset, size_t len) {
|
double util::parse_double(const std::string& str, size_t offset, size_t len) {
|
||||||
return parse_double(str.substr(offset, len));
|
return parse_double(str.substr(offset, len));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::vector<std::string> util::split(const std::string& str, char delimiter) {
|
||||||
|
std::vector<std::string> result;
|
||||||
|
std::stringstream ss(str);
|
||||||
|
std::string tmp;
|
||||||
|
while (std::getline(ss, tmp, delimiter)) {
|
||||||
|
result.push_back(tmp);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::wstring> util::split(const std::wstring& str, char delimiter) {
|
||||||
|
std::vector<std::wstring> result;
|
||||||
|
std::wstringstream ss(str);
|
||||||
|
std::wstring tmp;
|
||||||
|
while (std::getline(ss, tmp, static_cast<wchar_t>(delimiter))) {
|
||||||
|
result.push_back(tmp);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|||||||
@@ -33,6 +33,9 @@ namespace util {
|
|||||||
|
|
||||||
extern double parse_double(const std::string& str);
|
extern double parse_double(const std::string& str);
|
||||||
extern double parse_double(const std::string& str, size_t offset, size_t len);
|
extern double parse_double(const std::string& str, size_t offset, size_t len);
|
||||||
|
|
||||||
|
extern std::vector<std::string> split(const std::string& str, char delimiter);
|
||||||
|
extern std::vector<std::wstring> split(const std::wstring& str, char delimiter);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // UTIL_STRINGUTIL_H_
|
#endif // UTIL_STRINGUTIL_H_
|
||||||
@@ -21,6 +21,7 @@ protected:
|
|||||||
blockid_t const idBazalt;
|
blockid_t const idBazalt;
|
||||||
public:
|
public:
|
||||||
WorldGenerator(const Content* content);
|
WorldGenerator(const Content* content);
|
||||||
|
virtual ~WorldGenerator() = default;
|
||||||
|
|
||||||
virtual void generate(voxel* voxels, int x, int z, int seed) = 0;
|
virtual void generate(voxel* voxels, int x, int z, int seed) = 0;
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user