Threads
This commit is contained in:
parent
4717774885
commit
23dc60707c
5
TODO.md
5
TODO.md
|
@ -3,6 +3,7 @@
|
|||
## Data
|
||||
- [x] Generate noise
|
||||
- [x] Density
|
||||
- [ ] Robin hood map / hopscotch_map
|
||||
- [ ] Octree world
|
||||
- [ ] Serialize
|
||||
- [ ] In memory RLE
|
||||
|
@ -18,7 +19,7 @@
|
|||
- https://speciesdevblog.files.wordpress.com/2012/11/biomemap.png
|
||||
- [ ] Leak test
|
||||
- Valgrind
|
||||
- [ ] EnkiTS
|
||||
- Massif
|
||||
- [ ] Server
|
||||
- [ ] ZeroMQ
|
||||
|
||||
|
@ -49,6 +50,8 @@
|
|||
- [ ] LOD
|
||||
- [ ] Dual MC
|
||||
- [ ] Collision
|
||||
- [ ] Dynamic index size
|
||||
- [ ] Chunk size performance
|
||||
- [ ] Render with glBufferSubData
|
||||
- [x] Frustum Culling
|
||||
- [ ] Occlusion Culling
|
||||
|
|
|
@ -3,7 +3,9 @@
|
|||
#include "../data/glm.hpp"
|
||||
#include "../render/buffer/Buffer.hpp"
|
||||
#include "../data/geometry/Frustum.hpp"
|
||||
#include "../data/geometry/Faces.hpp"
|
||||
#include <memory>
|
||||
#include <toml.h>
|
||||
|
||||
class World;
|
||||
class Chunk;
|
||||
|
@ -19,12 +21,14 @@ namespace contouring {
|
|||
virtual void update(const camera_pos &pos) = 0;
|
||||
|
||||
/// Chunk data change
|
||||
virtual void onUpdate(const chunk_pos &pos, const std::unordered_map<chunk_pos, std::shared_ptr<Chunk>>& data) = 0; // TODO: get precise border data
|
||||
virtual void onUpdate(const chunk_pos &pos, const std::unordered_map<chunk_pos, std::shared_ptr<Chunk>>& data, Faces neighbors) = 0;
|
||||
/// Chunk existante ping
|
||||
/// @note notify for chunks entering view while moving
|
||||
virtual void onNotify(const chunk_pos &pos, const std::unordered_map<chunk_pos, std::shared_ptr<Chunk>> &data) = 0;
|
||||
/// Display ImGui config
|
||||
virtual void onGui() = 0;
|
||||
/// Get options
|
||||
virtual std::string getOptions() = 0;
|
||||
|
||||
/// Get buffers in frustum with model matrices
|
||||
/// @note buffers invalidated after update
|
||||
|
|
|
@ -19,6 +19,20 @@ namespace contouring {
|
|||
}
|
||||
}
|
||||
|
||||
AbstractFlat::AbstractFlat(const std::string& str): Abstract() {
|
||||
auto opt = toml::parse(str);
|
||||
loadDistance = opt["load_distance"].value_or(loadDistance);
|
||||
keepDistance = opt["keep_distance"].value_or(keepDistance);
|
||||
}
|
||||
std::string AbstractFlat::getOptions() {
|
||||
std::ostringstream ss;
|
||||
ss << toml::table({
|
||||
{"load_distance", loadDistance},
|
||||
{"keep_distance", keepDistance}
|
||||
});
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
void AbstractFlat::onGui() {
|
||||
ImGui::SliderInt("Load Distance", &loadDistance, 1, keepDistance);
|
||||
ImGui::SliderInt("Keep Distance", &keepDistance, loadDistance+1, 21);
|
||||
|
|
|
@ -8,8 +8,8 @@ namespace contouring {
|
|||
/// Generating mesh for chunks 1:1
|
||||
class AbstractFlat: public Abstract {
|
||||
public:
|
||||
AbstractFlat(): Abstract() { }
|
||||
virtual ~AbstractFlat() { }
|
||||
AbstractFlat(const std::string &str);
|
||||
virtual ~AbstractFlat() {}
|
||||
|
||||
/// Each frame ping. Used to clear out of range
|
||||
void update(const camera_pos &) override;
|
||||
|
@ -17,6 +17,8 @@ namespace contouring {
|
|||
/// Display ImGui config
|
||||
void onGui() override;
|
||||
|
||||
std::string getOptions() override;
|
||||
|
||||
/// Get buffers in frustum with model matrices
|
||||
/// @note buffers invalidated after update
|
||||
void getModels(std::vector<std::pair<glm::mat4, Buffer *const>> &out, const std::optional<Frustum> &frustum, float scale) override;
|
||||
|
@ -32,7 +34,7 @@ namespace contouring {
|
|||
std::unordered_map<chunk_pos, Buffer *> buffers;
|
||||
chunk_pos center = chunk_pos(INT_MAX);
|
||||
|
||||
int loadDistance = 3; //TODO: handle config
|
||||
int loadDistance = 3;
|
||||
int keepDistance = 4;
|
||||
};
|
||||
}
|
|
@ -12,9 +12,10 @@ namespace contouring {
|
|||
virtual ~Dummy() { }
|
||||
|
||||
void update(const camera_pos &) override { }
|
||||
void onUpdate(const chunk_pos &, const std::unordered_map<chunk_pos, std::shared_ptr<Chunk>> &) override { }
|
||||
void onUpdate(const chunk_pos &, const std::unordered_map<chunk_pos, std::shared_ptr<Chunk>> &, Faces) override {}
|
||||
void onNotify(const chunk_pos &, const std::unordered_map<chunk_pos, std::shared_ptr<Chunk>> &) override { }
|
||||
void onGui() override { }
|
||||
std::string getOptions() override { return ""; }
|
||||
void getModels(std::vector<std::pair<glm::mat4, Buffer *const>> &, const std::optional<Frustum>&, float) override { }
|
||||
};
|
||||
}
|
|
@ -1,54 +1,105 @@
|
|||
#include "FlatSurroundingBox.hpp"
|
||||
|
||||
#include "boxing.hpp"
|
||||
#include "../render/buffer/ShortIndexedBuffer.hpp"
|
||||
#include "../world/Chunk.hpp"
|
||||
#include <Remotery.h>
|
||||
#include <imgui.h>
|
||||
|
||||
namespace contouring {
|
||||
void FlatSurroundingBox::onUpdate(const chunk_pos &pos, const std::unordered_map<chunk_pos, std::shared_ptr<Chunk>> &data) {
|
||||
if(!inLoadRange(pos))
|
||||
return;
|
||||
FlatSurroundingBox::FlatSurroundingBox(const std::string &opt) : AbstractFlat(opt) {
|
||||
for (size_t i = 1; i <= 4; i++) {
|
||||
workers.push_back(std::thread([&] {
|
||||
while (running) {
|
||||
std::pair<chunk_pos, surrounding::chunks> ctx;
|
||||
loadQueue.wait();
|
||||
if (loadQueue.pop(ctx)) {
|
||||
rmt_ScopedCPUSample(ProcessContouring, 0);
|
||||
std::vector<VertexData> vertices;
|
||||
render(ctx.second, vertices);
|
||||
{
|
||||
rmt_ScopedCPUSample(Index, 0);
|
||||
loadedQueue.push({ctx.first, ShortIndexedBuffer::Data(vertices)});
|
||||
}
|
||||
}
|
||||
}
|
||||
}));
|
||||
}
|
||||
}
|
||||
FlatSurroundingBox::~FlatSurroundingBox() {
|
||||
running = false;
|
||||
loadQueue.notify();
|
||||
|
||||
surrounding::chunks surrounding;
|
||||
if(surrounding::load(surrounding, pos, data)) {
|
||||
std::vector<VertexData> vertices;
|
||||
render(surrounding, vertices);
|
||||
ShortIndexedBuffer::Data data(vertices);
|
||||
|
||||
const auto buffer = new ShortIndexedBuffer(GL_TRIANGLES, data);
|
||||
const auto it = buffers.find(pos);
|
||||
if (it != buffers.end()) {
|
||||
if(it->second != NULL)
|
||||
delete it->second;
|
||||
|
||||
it->second = buffer;
|
||||
} else {
|
||||
buffers.emplace(pos, buffer);
|
||||
}
|
||||
for(auto& worker: workers) {
|
||||
if (worker.joinable())
|
||||
worker.join();
|
||||
}
|
||||
}
|
||||
|
||||
void FlatSurroundingBox::enqueue(const chunk_pos &pos, const std::unordered_map<chunk_pos, std::shared_ptr<Chunk>> &data) {
|
||||
rmt_ScopedCPUSample(EnqueueContouring, RMTSF_Aggregate);
|
||||
const auto dist2 = glm::length2(pos - center);
|
||||
if (dist2 <= loadDistance * loadDistance) {
|
||||
surrounding::chunks surrounding;
|
||||
if(surrounding::load(surrounding, pos, data)) {
|
||||
loadQueue.push(pos, surrounding, -dist2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FlatSurroundingBox::onUpdate(const chunk_pos &pos, const std::unordered_map<chunk_pos, std::shared_ptr<Chunk>> &data, Faces neighbors) {
|
||||
enqueue(pos, data);
|
||||
if (neighbors && Faces::Right)
|
||||
enqueue(pos + g_face_offsets[static_cast<int>(Face::Right)], data);
|
||||
|
||||
if (neighbors && Faces::Left)
|
||||
enqueue(pos + g_face_offsets[static_cast<int>(Face::Left)], data);
|
||||
|
||||
if (neighbors && Faces::Up)
|
||||
enqueue(pos + g_face_offsets[static_cast<int>(Face::Up)], data);
|
||||
|
||||
if (neighbors && Faces::Down)
|
||||
enqueue(pos + g_face_offsets[static_cast<int>(Face::Down)], data);
|
||||
|
||||
if (neighbors && Faces::Forward)
|
||||
enqueue(pos + g_face_offsets[static_cast<int>(Face::Forward)], data);
|
||||
|
||||
if (neighbors && Faces::Backward)
|
||||
enqueue(pos + g_face_offsets[static_cast<int>(Face::Backward)], data);
|
||||
}
|
||||
|
||||
void FlatSurroundingBox::onNotify(const chunk_pos &pos, const std::unordered_map<chunk_pos, std::shared_ptr<Chunk>> &data) {
|
||||
if (!inLoadRange(pos) || buffers.find(pos) != buffers.end())
|
||||
return;
|
||||
if (buffers.find(pos) == buffers.end()) {
|
||||
enqueue(pos, data);
|
||||
}
|
||||
}
|
||||
|
||||
surrounding::chunks surrounding;
|
||||
if(surrounding::load(surrounding, pos, data)) {
|
||||
std::vector<VertexData> vertices;
|
||||
render(surrounding, vertices);
|
||||
ShortIndexedBuffer::Data data(vertices);
|
||||
|
||||
const auto buffer = new ShortIndexedBuffer(GL_TRIANGLES, data);
|
||||
const auto it = buffers.find(pos);
|
||||
void FlatSurroundingBox::update(const camera_pos& pos) {
|
||||
AbstractFlat::update(pos);
|
||||
std::pair<chunk_pos, ShortIndexedBuffer::Data> out;
|
||||
reports.load.push(loadQueue.size());
|
||||
//MAYBE: clear out of range loadQueue.trim(keepDistance * keepDistance)
|
||||
reports.loaded.push(loadedQueue.size());
|
||||
while(loadedQueue.pop(out)) {
|
||||
const auto buffer = new ShortIndexedBuffer(GL_TRIANGLES, out.second);
|
||||
const auto it = buffers.find(out.first);
|
||||
if (it != buffers.end()) {
|
||||
if(it->second != NULL)
|
||||
delete it->second;
|
||||
|
||||
it->second = buffer;
|
||||
} else {
|
||||
buffers.emplace(pos, buffer);
|
||||
buffers.emplace(out.first, buffer);
|
||||
}
|
||||
}
|
||||
reports.count.push(buffers.size());
|
||||
}
|
||||
|
||||
void FlatSurroundingBox::onGui() {
|
||||
ImGui::PlotHistogram("Count", reports.count.buffer.get(), reports.count.size, 0, std::to_string(reports.count.current()).c_str(), 0);
|
||||
ImGui::PlotHistogram("Loading", reports.load.buffer.get(), reports.load.size, 0, std::to_string(reports.load.current()).c_str(), 0);
|
||||
ImGui::PlotHistogram("Waiting", reports.loaded.buffer.get(), reports.loaded.size, 0, std::to_string(reports.loaded.current()).c_str(), 0);
|
||||
ImGui::Separator();
|
||||
AbstractFlat::onGui();
|
||||
}
|
||||
|
||||
bool FlatSurroundingBox::isTransparent(const surrounding::chunks &surrounding, const std::pair<ushort, ushort> &idx) {
|
||||
|
|
|
@ -3,19 +3,45 @@
|
|||
#include "AbstractFlat.hpp"
|
||||
#include "surrounding.hpp"
|
||||
|
||||
class VertexData;
|
||||
#include "../data/safe_queue.hpp"
|
||||
#include "../data/safe_priority_queue.hpp"
|
||||
#include "../data/circular_buffer.hpp"
|
||||
#include "../render/buffer/ShortIndexedBuffer.hpp"
|
||||
#include <thread>
|
||||
|
||||
#define REPORT_BUFFER_SIZE 128
|
||||
|
||||
namespace contouring {
|
||||
class FlatSurroundingBox: public AbstractFlat {
|
||||
public:
|
||||
FlatSurroundingBox(): AbstractFlat() { }
|
||||
~FlatSurroundingBox() { }
|
||||
FlatSurroundingBox(const std::string&);
|
||||
virtual ~FlatSurroundingBox();
|
||||
|
||||
void update(const camera_pos&) override;
|
||||
|
||||
void onGui() override;
|
||||
|
||||
/// Chunk data change
|
||||
void onUpdate(const chunk_pos &, const std::unordered_map<chunk_pos, std::shared_ptr<Chunk>> &) override;
|
||||
void onUpdate(const chunk_pos &, const std::unordered_map<chunk_pos, std::shared_ptr<Chunk>> &, Faces) override;
|
||||
/// Chunk existante ping
|
||||
/// @note notify for chunks entering view while moving
|
||||
void onNotify(const chunk_pos &, const std::unordered_map<chunk_pos, std::shared_ptr<Chunk>> &) override;
|
||||
|
||||
protected:
|
||||
safe_priority_queue_map<chunk_pos, surrounding::chunks, int> loadQueue;
|
||||
safe_queue<std::pair<chunk_pos, ShortIndexedBuffer::Data>> loadedQueue;
|
||||
|
||||
struct report {
|
||||
circular_buffer<float> count = circular_buffer<float>(REPORT_BUFFER_SIZE, 0); // MAYBE: store int
|
||||
circular_buffer<float> load = circular_buffer<float>(REPORT_BUFFER_SIZE, 0);
|
||||
circular_buffer<float> loaded = circular_buffer<float>(REPORT_BUFFER_SIZE, 0);
|
||||
} reports;
|
||||
|
||||
bool running = true;
|
||||
std::vector<std::thread> workers;
|
||||
|
||||
void enqueue(const chunk_pos &, const std::unordered_map<chunk_pos, std::shared_ptr<Chunk>> &);
|
||||
|
||||
private:
|
||||
static inline bool isTransparent(const surrounding::chunks &surrounding, const std::pair<ushort, ushort> &idx);
|
||||
static void render(const surrounding::chunks &surrounding, std::vector<VertexData> &vertices);
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
#include "index.hpp"
|
||||
#include "Dummy.hpp"
|
||||
#include "FlatSurroundingBox.hpp"
|
||||
|
||||
namespace contouring {
|
||||
int idxByName(const std::string& name) {
|
||||
for (size_t i = 0; i < names.size(); i++) {
|
||||
if(name == names[i])
|
||||
return (int)i;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::shared_ptr<Abstract> load(int idx, const std::map<std::string, std::string>& data) {
|
||||
switch (idx) {
|
||||
case 1:
|
||||
return std::make_shared<Dummy>();
|
||||
|
||||
default:
|
||||
return std::make_shared<FlatSurroundingBox>(data.at(names[0]));
|
||||
}
|
||||
}
|
||||
void save(int idx, std::shared_ptr<Abstract>& ct, std::map<std::string, std::string>& data) {
|
||||
data.insert_or_assign(names[idx], ct->getOptions());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
#pragma once
|
||||
|
||||
#include "Abstract.hpp"
|
||||
|
||||
namespace contouring {
|
||||
static const std::array<std::string, 2> names = {"FlatBox", "Dummy"};
|
||||
static const std::string cnames = "FlatBox\0Dummy\0";
|
||||
|
||||
int idxByName(const std::string &name);
|
||||
std::shared_ptr<Abstract> load(int idx, const std::map<std::string, std::string> &data);
|
||||
void save(int idx, std::shared_ptr<Abstract> &ct, std::map<std::string, std::string> &data);
|
||||
}
|
|
@ -0,0 +1,133 @@
|
|||
#pragma once
|
||||
|
||||
#include <tuple>
|
||||
#include <vector>
|
||||
#include <unordered_set>
|
||||
#include <unordered_map>
|
||||
#include <mutex>
|
||||
#include <condition_variable>
|
||||
#include <algorithm>
|
||||
|
||||
/// Thread safe queue with unique keys updating priority and value
|
||||
template <class K, class V, class W>
|
||||
class safe_priority_queue_map {
|
||||
private:
|
||||
static bool cmpByWeight(const std::pair<K, W> &a, const std::pair<K, W> &b) {
|
||||
return a.second < b.second;
|
||||
}
|
||||
|
||||
std::vector<std::pair<K, W>> heap;
|
||||
std::unordered_map<K, V> map;
|
||||
std::mutex mutex;
|
||||
std::condition_variable cv;
|
||||
|
||||
public:
|
||||
void push(const K& key, const V& val, const W& weight) {
|
||||
std::unique_lock<std::mutex> lock(mutex);
|
||||
heap.push_back({key, weight});
|
||||
std::push_heap(heap.begin(), heap.end(), cmpByWeight);
|
||||
map.insert_or_assign(key, val);
|
||||
cv.notify_one();
|
||||
}
|
||||
|
||||
bool pop(std::pair<K, V>& out) {
|
||||
std::unique_lock<std::mutex> lock(mutex);
|
||||
if (heap.empty())
|
||||
return false;
|
||||
|
||||
std::pop_heap(heap.begin(), heap.end(), cmpByWeight);
|
||||
const auto priority = heap.back();
|
||||
heap.pop_back();
|
||||
|
||||
const auto it = map.find(priority.first);
|
||||
if(it == map.end())
|
||||
return false;
|
||||
|
||||
out = std::make_pair(it->first, it->second);
|
||||
map.erase(it);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool empty() {
|
||||
std::unique_lock<std::mutex> lock(mutex);
|
||||
return heap.empty();
|
||||
}
|
||||
|
||||
size_t size() {
|
||||
std::unique_lock<std::mutex> lock(mutex);
|
||||
return map.size();
|
||||
}
|
||||
|
||||
void notify() {
|
||||
cv.notify_all();
|
||||
}
|
||||
|
||||
void wait() {
|
||||
std::unique_lock<std::mutex> lock(mutex);
|
||||
if (heap.empty())
|
||||
cv.wait(lock);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
/// Thread safe queue with unique keys updating priority
|
||||
template <class K, class W>
|
||||
class safe_priority_queue {
|
||||
private:
|
||||
static bool cmpByWeight(const std::pair<K, W> &a, const std::pair<K, W> &b) {
|
||||
return a.second < b.second;
|
||||
}
|
||||
|
||||
std::vector<std::pair<K, W>> heap;
|
||||
std::unordered_set<K> set;
|
||||
std::mutex mutex;
|
||||
std::condition_variable cv;
|
||||
|
||||
public:
|
||||
void push(const K& key, const W& weight) {
|
||||
std::unique_lock<std::mutex> lock(mutex);
|
||||
heap.push_back({key, weight});
|
||||
std::push_heap(heap.begin(), heap.end(), cmpByWeight);
|
||||
set.insert(key);
|
||||
cv.notify_one();
|
||||
}
|
||||
|
||||
bool pop(K& out) {
|
||||
std::unique_lock<std::mutex> lock(mutex);
|
||||
if (heap.empty())
|
||||
return false;
|
||||
|
||||
std::pop_heap(heap.begin(), heap.end(), cmpByWeight);
|
||||
const auto priority = heap.back();
|
||||
heap.pop_back();
|
||||
|
||||
const auto it = set.find(priority.first);
|
||||
if(it == set.end())
|
||||
return false;
|
||||
|
||||
out = *it;
|
||||
set.erase(it);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool empty() {
|
||||
std::unique_lock<std::mutex> lock(mutex);
|
||||
return heap.empty();
|
||||
}
|
||||
|
||||
size_t size() {
|
||||
std::unique_lock<std::mutex> lock(mutex);
|
||||
return set.size();
|
||||
}
|
||||
|
||||
void notify() {
|
||||
cv.notify_all();
|
||||
}
|
||||
|
||||
void wait() {
|
||||
std::unique_lock<std::mutex> lock(mutex);
|
||||
if (heap.empty())
|
||||
cv.wait(lock);
|
||||
}
|
||||
|
||||
};
|
|
@ -0,0 +1,52 @@
|
|||
#pragma once
|
||||
|
||||
#include <queue>
|
||||
#include <mutex>
|
||||
#include <condition_variable>
|
||||
|
||||
/// Thread safe queue
|
||||
template <class T>
|
||||
class safe_queue {
|
||||
private:
|
||||
std::queue<T> queue;
|
||||
std::mutex mutex;
|
||||
std::condition_variable cv;
|
||||
|
||||
public:
|
||||
void push(const T& in) {
|
||||
std::unique_lock<std::mutex> lock(mutex);
|
||||
queue.push(in);
|
||||
cv.notify_one();
|
||||
}
|
||||
|
||||
bool pop(T& out) {
|
||||
std::unique_lock<std::mutex> lock(mutex);
|
||||
if (queue.empty())
|
||||
return false;
|
||||
|
||||
out = queue.front();
|
||||
queue.pop();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool empty() {
|
||||
std::unique_lock<std::mutex> lock(mutex);
|
||||
return queue.empty();
|
||||
}
|
||||
|
||||
size_t size() {
|
||||
std::unique_lock<std::mutex> lock(mutex);
|
||||
return queue.size();
|
||||
}
|
||||
|
||||
void notify() {
|
||||
cv.notify_all();
|
||||
}
|
||||
|
||||
void wait() {
|
||||
std::unique_lock<std::mutex> lock(mutex);
|
||||
if(queue.empty())
|
||||
cv.wait(lock);
|
||||
}
|
||||
|
||||
};
|
|
@ -1,5 +1,4 @@
|
|||
#ifndef SAFE_UNIQUE_QUEUE_HPP
|
||||
#define SAFE_UNIQUE_QUEUE_HPP
|
||||
#pragma once
|
||||
|
||||
#include <queue>
|
||||
#include <unordered_set>
|
||||
|
@ -12,7 +11,7 @@ class safe_unique_queue {
|
|||
private:
|
||||
std::queue<T> queue;
|
||||
std::unordered_set<T> set;
|
||||
TracyLockable(std::mutex, mutex);
|
||||
std::mutex mutex;
|
||||
std::condition_variable cv;
|
||||
|
||||
public:
|
||||
|
@ -72,5 +71,3 @@ public:
|
|||
cv.wait(lock);
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
|
@ -14,6 +14,7 @@
|
|||
#include "../render/Renderer.hpp"
|
||||
#include "../world/World.hpp"
|
||||
#include "../control/Camera.hpp"
|
||||
#include "../contouring/index.hpp"
|
||||
|
||||
inline ImColor fromHex(const std::string& str) {
|
||||
int rgb[3] = {UCHAR_MAX};
|
||||
|
@ -48,7 +49,11 @@ struct options {
|
|||
world.keepDistance = config["world"]["keep_distance"].value_or(world.keepDistance);
|
||||
voxel_size = config["world"]["unit_size"].value_or(1.f);
|
||||
|
||||
//TODO: contouring
|
||||
culling = config["mesh"]["culling"].value_or(true);
|
||||
contouring_idx = contouring::idxByName(config["mesh"]["mode"].value_or(std::string("")));
|
||||
for(const auto name: contouring::names) {
|
||||
contouring_data.emplace(name, config["mesh"]["options"][name].value_or(std::string("")));
|
||||
}
|
||||
|
||||
camera.far = config["camera"]["far"].value_or(camera.far);
|
||||
camera.near = config["camera"]["near"].value_or(camera.near);
|
||||
|
@ -87,6 +92,16 @@ struct options {
|
|||
{"keep_distance", world.keepDistance},
|
||||
{"unit_size", voxel_size}
|
||||
}));
|
||||
config.insert_or_assign("mesh", toml::table({
|
||||
{"culling", culling},
|
||||
{"mode", contouring::names[contouring_idx]},
|
||||
{"options", toml::table()}
|
||||
}));
|
||||
for(auto opt: contouring_data) {
|
||||
if(!opt.second.empty())
|
||||
config["mesh"]["options"].as_table()->insert_or_assign(opt.first, opt.second);
|
||||
}
|
||||
|
||||
config.insert_or_assign("camera", toml::table({
|
||||
{"far", camera.far},
|
||||
{"near", camera.near},
|
||||
|
@ -124,8 +139,9 @@ struct options {
|
|||
float voxel_size;
|
||||
|
||||
bool show_debug_contouring;
|
||||
int contouring_idx = 0;
|
||||
bool culling = true; // TODO: move to contouring
|
||||
bool culling;
|
||||
int contouring_idx;
|
||||
std::map<std::string, std::string> contouring_data;
|
||||
|
||||
bool show_debug_controls;
|
||||
Camera::options camera;
|
||||
|
|
15
src/main.cpp
15
src/main.cpp
|
@ -19,7 +19,6 @@
|
|||
|
||||
#include "render/Renderer.hpp"
|
||||
#include "world/World.hpp"
|
||||
#include "contouring/FlatSurroundingBox.hpp"
|
||||
|
||||
#include "data/state.h"
|
||||
|
||||
|
@ -48,10 +47,6 @@ int main(int, char *[]){
|
|||
|
||||
renderer->LightInvDir = glm::vec3(-0.5f, 2, -2);
|
||||
|
||||
World world = World(options.world);
|
||||
world.setContouring(std::make_shared<contouring::FlatSurroundingBox>()); //FIXME: from idx
|
||||
state.contouring = world.getContouring();
|
||||
|
||||
Remotery *rmt;
|
||||
rmt_CreateGlobalInstance(&rmt);
|
||||
rmt_BindOpenGL();
|
||||
|
@ -59,6 +54,10 @@ int main(int, char *[]){
|
|||
std::cout << "Profiling !" << std::endl;
|
||||
#endif
|
||||
|
||||
World world = World(options.world);
|
||||
world.setContouring(contouring::load(options.contouring_idx, options.contouring_data));
|
||||
state.contouring = world.getContouring();
|
||||
|
||||
do {
|
||||
const double startTime = glfwGetTime();
|
||||
{ // Update
|
||||
|
@ -117,9 +116,8 @@ int main(int, char *[]){
|
|||
}
|
||||
if(actions && UI::Actions::ChangeContouring) {
|
||||
state.contouring = NULL;
|
||||
//TODO: save options
|
||||
//FIXME: world.setContouring(from options.contouring_idx)
|
||||
//state.contouring = world.getContouring();
|
||||
world.setContouring(contouring::load(options.contouring_idx, options.contouring_data));
|
||||
state.contouring = world.getContouring();
|
||||
}
|
||||
renderer->SkyEnable = options.renderer.skybox;
|
||||
}
|
||||
|
@ -180,6 +178,7 @@ int main(int, char *[]){
|
|||
// Close OpenGL window and terminate GLFW
|
||||
glfwTerminate();
|
||||
|
||||
contouring::save(options.contouring_idx, state.contouring, options.contouring_data);
|
||||
options.save();
|
||||
|
||||
return 0;
|
||||
|
|
|
@ -86,7 +86,6 @@ UI::Actions UI::draw(options &options, state &state, const reports &reports, GLu
|
|||
}
|
||||
if (ImGui::Checkbox("Wireframe", &options.renderer.wireframe))
|
||||
glPolygonMode(GL_FRONT_AND_BACK, options.renderer.wireframe ? GL_LINE : GL_FILL);
|
||||
ImGui::Checkbox("Culling", &options.culling);
|
||||
ImGui::Text("Textures '%s'", options.renderer.textures.c_str()); // MAYBE: select
|
||||
if (ImGui::SliderFloat("LOD", &options.renderer.mipMapLOD, -1, 1)) {
|
||||
actions = actions | Actions::RendererTextures;
|
||||
|
@ -115,9 +114,12 @@ UI::Actions UI::draw(options &options, state &state, const reports &reports, GLu
|
|||
|
||||
if (options.show_debug_contouring) {
|
||||
ImGui::Begin("Debug: Contouring", &options.show_debug_contouring, ImGuiWindowFlags_AlwaysAutoResize);
|
||||
if(ImGui::Combo("Contouring", &options.contouring_idx, /*contouring::names*/"\0")) {
|
||||
const auto prev_idx = options.contouring_idx;
|
||||
if(ImGui::Combo("Contouring", &options.contouring_idx, contouring::cnames.c_str())) {
|
||||
actions = actions | Actions::ChangeContouring;
|
||||
contouring::save(prev_idx, state.contouring, options.contouring_data);
|
||||
}
|
||||
ImGui::Checkbox("Culling", &options.culling);
|
||||
state.contouring->onGui();
|
||||
ImGui::End();
|
||||
}
|
||||
|
|
|
@ -3,6 +3,9 @@
|
|||
#include "vboindexer.hpp"
|
||||
|
||||
ShortIndexedBuffer::Data::Data(const std::vector<VertexData> &vs, const std::vector<GLushort> &indices): indices(indices) {
|
||||
vertices.reserve(vs.size());
|
||||
materials.reserve(vs.size());
|
||||
normals.reserve(vs.size());
|
||||
for (auto vertex : vs) {
|
||||
vertices.push_back(vertex.Position);
|
||||
materials.push_back(vertex.Material);
|
||||
|
@ -82,12 +85,16 @@ uint ShortIndexedBuffer::draw(Buffer::params params) {
|
|||
return IndexSize;
|
||||
}
|
||||
|
||||
#include <iostream>
|
||||
void ShortIndexedBuffer::setData(const ShortIndexedBuffer::Data& data) {
|
||||
glGenBuffers(1, &IndexBufferID);
|
||||
glGenBuffers(1, &MaterialBufferID);
|
||||
glGenBuffers(1, &NormalBufferID);
|
||||
|
||||
IndexSize = data.indices.size();
|
||||
if(IndexSize != data.indices.size()) {
|
||||
std::cout << "ShortBuffer overflow: " << data.indices.size() << std::endl;
|
||||
}
|
||||
setIndicies(IndexSize * sizeof(GLushort), &data.indices[0]);
|
||||
setVertices(data.vertices.size() * sizeof(glm::vec3), &data.vertices[0]);
|
||||
setMaterials(data.materials.size() * sizeof(GLushort), &data.materials[0]);
|
||||
|
|
|
@ -45,8 +45,9 @@ void indexVBO(
|
|||
{
|
||||
std::map<VertexData,unsigned int> VertexToOutIndex;
|
||||
|
||||
out_indices.reserve(in_vertices.size());
|
||||
// For each input vertex
|
||||
for ( unsigned int i=0; i<in_vertices.size(); i++ ){
|
||||
for (unsigned int i = 0; i < in_vertices.size(); i++) {
|
||||
|
||||
VertexData packed = {in_vertices[i], in_materials[i], in_normals[i]};
|
||||
|
||||
|
@ -76,6 +77,7 @@ void indexVBO(
|
|||
{
|
||||
std::map<VertexData, GLushort> VertexToOutIndex;
|
||||
|
||||
out_indices.reserve(in_vertices.size());
|
||||
// For each input vertex
|
||||
for (size_t i=0; i<in_vertices.size(); i++ ){
|
||||
// Try to find a similar vertex in out_XXXX
|
||||
|
|
|
@ -54,5 +54,5 @@ private:
|
|||
/// Chunk data
|
||||
std::array<Voxel, CHUNK_SIZE> voxels;
|
||||
/// Require update
|
||||
bool upToDate = false;
|
||||
bool upToDate = true;
|
||||
};
|
||||
|
|
|
@ -3,13 +3,40 @@
|
|||
#include "../contouring/Dummy.hpp"
|
||||
#include <Remotery.h>
|
||||
|
||||
World::World(const World::options &options): contouring(std::make_shared<contouring::Dummy>()) {
|
||||
World::World(const World::options &options): loadPool(2), contouring(std::make_shared<contouring::Dummy>()) {
|
||||
setOptions(options);
|
||||
}
|
||||
World::~World() {
|
||||
contouring = NULL;
|
||||
}
|
||||
|
||||
World::LoadPool::LoadPool(size_t count) {
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
workers.push_back(std::thread([&] {
|
||||
while (running) {
|
||||
chunk_pos ctx;
|
||||
loadQueue.wait();
|
||||
if (loadQueue.pop(ctx)) {
|
||||
rmt_ScopedCPUSample(ProcessGenerate, 0);
|
||||
loadedQueue.push({ctx, std::make_shared<Chunk>(ctx, generator)});
|
||||
}
|
||||
}
|
||||
}));
|
||||
}
|
||||
}
|
||||
World::LoadPool::~LoadPool() {
|
||||
running = false;
|
||||
loadQueue.notify();
|
||||
|
||||
for (auto &worker : workers) {
|
||||
if (worker.joinable())
|
||||
worker.join();
|
||||
}
|
||||
}
|
||||
inline void World::LoadPool::push(const chunk_pos &pos, int weight) { loadQueue.push(pos, weight); }
|
||||
inline bool World::LoadPool::pop(std::pair<chunk_pos, std::shared_ptr<Chunk>> &out) { return loadedQueue.pop(out); }
|
||||
inline size_t World::LoadPool::size() { return loadQueue.size(); }
|
||||
|
||||
void World::update(const camera_pos& pos, World::report& rep) {
|
||||
const chunk_pos newPos = glm::divide(pos, chunk_voxel_pos(CHUNK_LENGTH));
|
||||
const auto chunkChange = newPos != last_pos;
|
||||
|
@ -19,7 +46,7 @@ void World::update(const camera_pos& pos, World::report& rep) {
|
|||
// Update alive chunks
|
||||
{
|
||||
rmt_ScopedCPUSample(Update, 0);
|
||||
for(auto [chunkPos, chunk]: chunks) {
|
||||
for (auto &[chunkPos, chunk]: chunks) {
|
||||
if (glm::length2(last_pos - chunkPos) > keepDistance * keepDistance
|
||||
&& unloadQueue.push(chunkPos)) {
|
||||
//TODO: unloadCount++;
|
||||
|
@ -27,61 +54,52 @@ void World::update(const camera_pos& pos, World::report& rep) {
|
|||
}
|
||||
|
||||
if (chunk->update()) { // MAYBE: also contour joints
|
||||
contouring->onUpdate(chunkPos, chunks); //TODO: get update update_type(simple(pos), complex)
|
||||
contouring->onUpdate(chunkPos, chunks, Faces::None); //TODO: get update update_type(simple(pos), complex)
|
||||
} else if (chunkChange) { //NOTE: must be solved before octrees
|
||||
contouring->onNotify(chunkPos, chunks);
|
||||
}
|
||||
}
|
||||
}
|
||||
contouring->update(pos);
|
||||
{
|
||||
rmt_ScopedCPUSample(Contouring, 0);
|
||||
contouring->update(pos);
|
||||
}
|
||||
|
||||
rep.chunk_unload.push(unloadQueue.size());
|
||||
// Unload dead chunks
|
||||
{
|
||||
rmt_ScopedCPUSample(Unload, 0);
|
||||
for (size_t i = 0; i < 8 && !unloadQueue.empty(); i++) {
|
||||
for (size_t i = 0; i < 32 && !unloadQueue.empty(); i++) {
|
||||
chunks.extract(unloadQueue.pop());
|
||||
//TODO: save to file
|
||||
}
|
||||
}
|
||||
|
||||
// Find missing chunks (~240ms from max loadDistance)
|
||||
// Find missing chunks
|
||||
if(chunkChange) {
|
||||
rmt_ScopedCPUSample(ToLoad, 0);
|
||||
std::vector<chunk_pos> to_load;
|
||||
//TODO: circle point algo
|
||||
for (int x = -loadDistance; x <= loadDistance; x++) {
|
||||
for (int y = -loadDistance; y <= loadDistance; y++) {
|
||||
for (int z = -loadDistance; z <= loadDistance; z++) {
|
||||
if (x * x + y * y + z * z <= loadDistance * loadDistance) {
|
||||
const auto dist2 = x * x + y * y + z * z;
|
||||
if (dist2 <= loadDistance * loadDistance) {
|
||||
const chunk_pos p = last_pos + glm::ivec3(x, y, z);
|
||||
if (chunks.find(p) == chunks.end()) {
|
||||
to_load.push_back(p);
|
||||
loadPool.push(p, -dist2);
|
||||
}
|
||||
}
|
||||
}}}
|
||||
std::sort(to_load.begin(), to_load.end(), [](const chunk_pos &a, const chunk_pos &b) {
|
||||
return glm::length2(a) < glm::length2(b);
|
||||
});
|
||||
for(auto p: to_load) {
|
||||
loadQueue.push(p);
|
||||
}
|
||||
}
|
||||
|
||||
rep.chunk_load.push(loadQueue.size());
|
||||
// Load chunks
|
||||
rep.chunk_load.push(loadPool.size());
|
||||
// Loaded chunks
|
||||
{
|
||||
rmt_ScopedCPUSample(Load, 0);
|
||||
for (size_t i = 0; i < 8 && !loadQueue.empty(); i++) {
|
||||
const auto pos = loadQueue.pop();
|
||||
const auto chunk = std::make_shared<Chunk>(pos, generator);
|
||||
chunks.insert({pos, chunk});
|
||||
//trigger surronding render
|
||||
//MAYBE: contouring dependant
|
||||
for (size_t i = 0; i < 6; i++) {
|
||||
const auto it = chunks.find(pos + g_face_offsets[i]);
|
||||
if (it != chunks.end())
|
||||
it->second->invalidate();
|
||||
}
|
||||
std::pair<chunk_pos, std::shared_ptr<Chunk>> loaded;
|
||||
while (loadPool.pop(loaded)) {
|
||||
chunks.insert(loaded);
|
||||
contouring->onUpdate(loaded.first, chunks, Faces::All);
|
||||
}
|
||||
}
|
||||
rep.chunk_count.push(chunks.size());
|
||||
|
|
|
@ -1,7 +1,10 @@
|
|||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <thread>
|
||||
#include "../data/unique_queue.hpp"
|
||||
#include "../data/safe_queue.hpp"
|
||||
#include "../data/safe_priority_queue.hpp"
|
||||
#include "../data/circular_buffer.hpp"
|
||||
#include "Chunk.hpp"
|
||||
#define REPORT_BUFFER_SIZE 128
|
||||
|
@ -45,10 +48,27 @@ public:
|
|||
private:
|
||||
chunk_pos last_pos = chunk_pos(INT_MAX);
|
||||
|
||||
Generator generator;
|
||||
std::unordered_map<chunk_pos, std::shared_ptr<Chunk>> chunks;
|
||||
|
||||
unique_queue<chunk_pos> loadQueue; // TODO: unique priority
|
||||
/// Generating worker pool
|
||||
class LoadPool {
|
||||
public:
|
||||
LoadPool(size_t size);
|
||||
~LoadPool();
|
||||
|
||||
Generator generator;
|
||||
inline void push(const chunk_pos &pos, int weight);
|
||||
inline bool pop(std::pair<chunk_pos, std::shared_ptr<Chunk>> &out);
|
||||
inline size_t size();
|
||||
|
||||
private:
|
||||
std::vector<std::thread> workers;
|
||||
bool running = true;
|
||||
|
||||
safe_priority_queue<chunk_pos, int> loadQueue;
|
||||
safe_queue<std::pair<chunk_pos, std::shared_ptr<Chunk>>> loadedQueue;
|
||||
};
|
||||
LoadPool loadPool;
|
||||
unique_queue<chunk_pos> unloadQueue;
|
||||
|
||||
int loadDistance;
|
||||
|
|
Loading…
Reference in New Issue