Commit 47177748 authored by Shu's avatar Shu 🍠
Browse files

Extract contouring

parent aef06c0c
......@@ -44,7 +44,7 @@
{
"label": "exec",
"type": "shell",
"command": "./atomic",
"command": "./univerxel",
"options": {
"cwd": "${workspaceRoot}/build"
}
......@@ -52,7 +52,7 @@
{
"label": "exec memcheck",
"type": "shell",
"command": "valgrind --leak-check=full --show-leak-kinds=all --fair-sched=yes ./atomic",
"command": "valgrind --leak-check=full --show-leak-kinds=all --fair-sched=yes ./univerxel",
"options": {
"cwd": "${workspaceRoot}/build"
}
......@@ -60,7 +60,7 @@
{
"label": "exec helgrind",
"type": "shell",
"command": "valgrind --tool=helgrind --fair-sched=yes ./atomic",
"command": "valgrind --tool=helgrind --fair-sched=yes ./univerxel",
"options": {
"cwd": "${workspaceRoot}/build"
}
......@@ -68,7 +68,7 @@
{
"label": "exec callgrind",
"type": "shell",
"command": "valgrind --tool=callgrind --dump-instr=yes --simulate-cache=yes --fair-sched=yes ./atomic",
"command": "valgrind --tool=callgrind --dump-instr=yes --simulate-cache=yes --fair-sched=yes ./univerxel",
"options": {
"cwd": "${workspaceRoot}/build"
}
......
......@@ -17,12 +17,10 @@
- https://imgur.com/a/bh2iy
- https://speciesdevblog.files.wordpress.com/2012/11/biomemap.png
- [ ] Leak test
- https://www.deleaker.com
- https://www.parasoft.com
- Valgrind
- Gprof /pprof
- [ ] ZeroMQ
- [ ] EnkiTS
- [ ] Server
- [ ] ZeroMQ
## Rendering
- [x] Render triangle
......@@ -53,4 +51,6 @@
- [ ] Collision
- [ ] Render with glBufferSubData
- [x] Frustum Culling
- [ ] Occlusion Culling
\ No newline at end of file
- [ ] Occlusion Culling
- [ ] Document
\ No newline at end of file
#pragma once
#include "../data/glm.hpp"
#include "../render/buffer/Buffer.hpp"
#include "../data/geometry/Frustum.hpp"
#include <memory>
class World;
class Chunk;
namespace contouring {
/// Generating mesh from world data
class Abstract {
public:
Abstract() { }
virtual ~Abstract() { }
/// Each frame ping.
/// Mostly used for cleanup and to flush buffers data using main thread
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
/// 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 buffers in frustum with model matrices
/// @note buffers invalidated after update
virtual void getModels(std::vector<std::pair<glm::mat4, Buffer *const>> &buffers, const std::optional<Frustum>& frustum, float scale) = 0;
};
}
\ No newline at end of file
#include "AbstractFlat.hpp"
#include <imgui.h>
#include "../world/Chunk.hpp"
namespace contouring {
void AbstractFlat::update(const camera_pos& pos) {
center = glm::divide(pos, chunk_voxel_pos(CHUNK_LENGTH));
auto it = buffers.begin();
while (it != buffers.end()) { // Remove out of range buffers
if (inKeepRange(it->first)) {
it++;
} else {
if(it->second != NULL)
delete it->second;
it = buffers.erase(it);
}
}
}
void AbstractFlat::onGui() {
ImGui::SliderInt("Load Distance", &loadDistance, 1, keepDistance);
ImGui::SliderInt("Keep Distance", &keepDistance, loadDistance+1, 21);
}
void AbstractFlat::getModels(std::vector<std::pair<glm::mat4, Buffer *const>> &out, const std::optional<Frustum> &frustum, float scale) {
const auto scaling = glm::scale(glm::mat4(1), glm::vec3(scale));
for (const auto [pos, buffer] : buffers) {
if (buffer != NULL && (!frustum.has_value() || frustum.value().contains(Box::fromMin(scale * glm::vec3(pos) * glm::vec3(CHUNK_LENGTH), scale * glm::vec3(CHUNK_LENGTH)))))
out.push_back({glm::translate(scaling, glm::vec3(pos) * glm::vec3(CHUNK_LENGTH)), buffer});
}
}
}
\ No newline at end of file
#pragma once
#include "Abstract.hpp"
class World;
class Chunk;
namespace contouring {
/// Generating mesh for chunks 1:1
class AbstractFlat: public Abstract {
public:
AbstractFlat(): Abstract() { }
virtual ~AbstractFlat() { }
/// Each frame ping. Used to clear out of range
void update(const camera_pos &) override;
/// Display ImGui config
void onGui() 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;
protected:
bool inline inLoadRange(const chunk_pos& point) const {
return glm::length2(center - point) <= loadDistance * loadDistance;
}
bool inline inKeepRange(const chunk_pos& point) const {
return glm::length2(center - point) <= keepDistance * keepDistance;
}
std::unordered_map<chunk_pos, Buffer *> buffers;
chunk_pos center = chunk_pos(INT_MAX);
int loadDistance = 3; //TODO: handle config
int keepDistance = 4;
};
}
\ No newline at end of file
#pragma once
#include "Abstract.hpp"
class World;
class Chunk;
namespace contouring {
/// Useless contouring
class Dummy: public Abstract {
public:
Dummy(): Abstract() { }
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 onNotify(const chunk_pos &, const std::unordered_map<chunk_pos, std::shared_ptr<Chunk>> &) override { }
void onGui() override { }
void getModels(std::vector<std::pair<glm::mat4, Buffer *const>> &, const std::optional<Frustum>&, float) override { }
};
}
\ No newline at end of file
#pragma once
#include "../world/Chunk.hpp"
#include "boxing.hpp"
class FlatBox {
public:
FlatBox();
~FlatBox();
private:
static inline bool isTransparent(const Voxel *voxels, const std::optional<ushort>& idx) {
return idx.has_value() ? voxels[idx.value()].Density < UCHAR_MAX : true; // MAYBE: materials::transparent
}
public:
static void render(const Voxel *voxels, std::vector<VertexData> &vertices)
{
vertices.clear();
for (ushort i = 0; i < CHUNK_SIZE; i++) {
if (voxels[i].Density > 0) {
Faces faces = voxels[i].Density < UCHAR_MAX ? Faces::All :
(isTransparent(voxels, Chunk::getNeighborIdx(i, Face::Right)) & Faces::Right) |
(isTransparent(voxels, Chunk::getNeighborIdx(i, Face::Left)) & Faces::Left) |
(isTransparent(voxels, Chunk::getNeighborIdx(i, Face::Up)) & Faces::Up) |
(isTransparent(voxels, Chunk::getNeighborIdx(i, Face::Down)) & Faces::Down) |
(isTransparent(voxels, Chunk::getNeighborIdx(i, Face::Forward)) & Faces::Forward) |
(isTransparent(voxels, Chunk::getNeighborIdx(i, Face::Backward)) & Faces::Backward);
contouring::box::addCube(vertices, Chunk::getPosition(i), voxels[i].Material, faces, glm::vec3(voxels[i].Density * 1.f / UCHAR_MAX));
}
}
}
};
#pragma once
#include "FlatSurroundingBox.hpp"
#include "chunk_surrounding.hpp"
#include "boxing.hpp"
using namespace contouring;
#include "../render/buffer/ShortIndexedBuffer.hpp"
#include "../world/Chunk.hpp"
class SurrondingFlatBox {
public:
SurrondingFlatBox();
~SurrondingFlatBox();
namespace contouring {
void FlatSurroundingBox::onUpdate(const chunk_pos &pos, const std::unordered_map<chunk_pos, std::shared_ptr<Chunk>> &data) {
if(!inLoadRange(pos))
return;
private:
static inline bool isTransparent(const surrounding::chunks& surrounding, const std::pair<ushort, ushort>& idx) {
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);
}
}
}
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;
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);
}
}
}
bool FlatSurroundingBox::isTransparent(const surrounding::chunks &surrounding, const std::pair<ushort, ushort> &idx) {
return surrounding[idx.first]->begin()[idx.second].Density < UCHAR_MAX; // MAYBE: materials::transparent
}
public:
static void render(const surrounding::chunks& surrounding, std::vector<VertexData> &vertices)
{
void FlatSurroundingBox::render(const surrounding::chunks &surrounding, std::vector<VertexData> &vertices) {
const auto voxels = surrounding[surrounding::CENTER]->begin();
vertices.clear();
for (ushort i = 0; i < CHUNK_SIZE; i++) {
......@@ -27,8 +67,8 @@ public:
(isTransparent(surrounding, surrounding::getNeighborIdx(i, Face::Down)) & Faces::Down) |
(isTransparent(surrounding, surrounding::getNeighborIdx(i, Face::Forward)) & Faces::Forward) |
(isTransparent(surrounding, surrounding::getNeighborIdx(i, Face::Backward)) & Faces::Backward);
contouring::box::addCube(vertices, Chunk::getPosition(i), voxels[i].Material, faces, glm::vec3(voxels[i].Density * 1.f / UCHAR_MAX));
box::addCube(vertices, Chunk::getPosition(i), voxels[i].Material, faces, glm::vec3(voxels[i].Density * 1.f / UCHAR_MAX));
}
}
}
};
}
#pragma once
#include "AbstractFlat.hpp"
#include "surrounding.hpp"
class VertexData;
namespace contouring {
class FlatSurroundingBox: public AbstractFlat {
public:
FlatSurroundingBox(): AbstractFlat() { }
~FlatSurroundingBox() { }
/// Chunk data change
void onUpdate(const chunk_pos &, const std::unordered_map<chunk_pos, std::shared_ptr<Chunk>> &) 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;
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);
};
}
#pragma once
#include "surrounding.hpp"
#include "../world/Chunk.hpp"
#include <array>
#include <memory>
namespace contouring::surrounding {
const auto CENTER = 6;
typedef std::array<std::shared_ptr<const Chunk>, 7> chunks;
bool load(chunks &out, const chunk_pos &chunkPos, const std::unordered_map<chunk_pos, std::shared_ptr<Chunk>> &chunks) {
{
const auto it = chunks.find(chunkPos);
if (it == chunks.end())
return false;
out[CENTER] = it->second;
}
for (size_t i = 0; i < CENTER; i++) {
const auto it = chunks.find(chunkPos + g_face_offsets[i]);
if (it == chunks.end())
return false;
out[i] = it->second;
}
return true;
}
std::pair<ushort, ushort> getNeighborIdx(ushort idx, Face face) {
switch (face) {
......
#pragma once
#include "../data/geometry/Faces.hpp"
#include <memory>
class Chunk;
namespace contouring::surrounding {
const auto CENTER = 6;
typedef std::array<std::shared_ptr<const Chunk>, CENTER+1> chunks;
bool load(chunks &out, const chunk_pos &chunkPos, const std::unordered_map<chunk_pos, std::shared_ptr<Chunk>> &chunks);
std::pair<ushort, ushort> getNeighborIdx(ushort idx, Face face);
}
#pragma once
#include "../glm.hpp"
enum class Face {
Right, Left, Up, Down, Forward, Backward
};
......
......@@ -12,7 +12,7 @@ class safe_unique_queue {
private:
std::queue<T> queue;
std::unordered_set<T> set;
std::mutex mutex;
TracyLockable(std::mutex, mutex);
std::condition_variable cv;
public:
......
......@@ -143,6 +143,8 @@ struct state {
camera_pos position;
std::optional<std::pair<voxel_pos, Voxel>> look_at = {};
std::shared_ptr<contouring::Abstract> contouring;
char console_buffer[256];
};
......
......@@ -19,7 +19,7 @@
#include "render/Renderer.hpp"
#include "world/World.hpp"
#include "contouring/FlatBox.hpp"
#include "contouring/FlatSurroundingBox.hpp"
#include "data/state.h"
......@@ -49,6 +49,8 @@ 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);
......@@ -113,6 +115,12 @@ int main(int, char *[]){
if(actions && UI::Actions::Camera) {
camera.setOptions(options.camera);
}
if(actions && UI::Actions::ChangeContouring) {
state.contouring = NULL;
//TODO: save options
//FIXME: world.setContouring(from options.contouring_idx)
//state.contouring = world.getContouring();
}
renderer->SkyEnable = options.renderer.skybox;
}
{ // Rendering
......@@ -121,12 +129,12 @@ int main(int, char *[]){
auto pass = renderer->getPass();
pass.start();
std::vector<std::pair<glm::mat4, Buffer *>> models;
std::vector<std::pair<glm::mat4, Buffer *const>> models;
std::optional<Frustum> furstum;
if(options.culling) {
world.getModels(models, options.voxel_size, camera.getFrustum());
} else {
world.getModels(models, options.voxel_size);
furstum = {camera.getFrustum()};
}
world.getContouring()->getModels(models, camera.getFrustum(), options.voxel_size);
reports.main.models_count = 0;
reports.main.tris_count = 0;
rmt_ScopedOpenGLSample(Render);
......
......@@ -3,6 +3,7 @@
#include "imgui_impl_glfw.h"
#include "imgui_impl_opengl3.h"
#include "pass/Program.hpp"
#include "../contouring/Abstract.hpp"
void UI::setup(GLFWwindow* window) {
// Setup Dear ImGui context
......@@ -112,16 +113,14 @@ UI::Actions UI::draw(options &options, state &state, const reports &reports, GLu
ImGui::End();
}
/*if (show_debug_contouring) {
ImGui::Begin("Debug: Contouring", &show_debug_contouring, ImGuiWindowFlags_AlwaysAutoResize);
if(ImGui::Combo("Contouring", &contouring_idx, contouring::names)) {
world.setContouring(contouring::getContouring(contouring_idx, &world));
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")) {
actions = actions | Actions::ChangeContouring;
}
if (ImGui::Button("Render all"))
world.renderAll();
world.getContouring()->onGui();
state.contouring->onGui();
ImGui::End();
}*/
}
if (options.show_debug_controls) {
ImGui::Begin("Debug: Controls", &options.show_debug_controls, ImGuiWindowFlags_AlwaysAutoResize);
......
......@@ -15,6 +15,7 @@ namespace UI {
RendererTextures = 1 << 4,
World = 1 << 5,
Camera = 1 << 6,
ChangeContouring = 1 << 7,
};
inline Actions operator|(Actions a, Actions b) {
return static_cast<Actions>(static_cast<int>(a) | static_cast<int>(b));
......
......@@ -8,7 +8,6 @@
#define CHUNK_LENGTH2 (CHUNK_LENGTH * CHUNK_LENGTH)
#define CHUNK_SIZE (CHUNK_LENGTH2 * CHUNK_LENGTH)
class Buffer;
/// World part as linear 3d voxel array
struct Chunk {
public:
......@@ -51,8 +50,6 @@ public:
}
static std::optional<ushort> getNeighborIdx(ushort idx, Face dir);
Buffer *buffer = NULL;
private:
/// Chunk data
std::array<Voxel, CHUNK_SIZE> voxels;
......
#include "World.hpp"
#include "../contouring/SurrondingFlatBox.hpp"
#include "../render/buffer/ShortIndexedBuffer.hpp"
#include "../contouring/Dummy.hpp"
#include <Remotery.h>
World::World(const World::options& options) {
World::World(const World::options &options): contouring(std::make_shared<contouring::Dummy>()) {
setOptions(options);
}
World::~World() { }
World::~World() {
contouring = NULL;
}
void World::update(const camera_pos& pos, World::report& rep) {
const chunk_pos newPos = glm::divide(pos, chunk_voxel_pos(CHUNK_LENGTH));
......@@ -19,44 +20,20 @@ void World::update(const camera_pos& pos, World::report& rep) {
{
rmt_ScopedCPUSample(Update, 0);
for(auto [chunkPos, chunk]: chunks) {
const glm::ivec3 dist = last_pos - chunkPos;
if (dist.x * dist.x + dist.y * dist.y + dist.z * dist.z > keepDistance * keepDistance
if (glm::length2(last_pos - chunkPos) > keepDistance * keepDistance
&& unloadQueue.push(chunkPos)) {
//TODO: unloadCount++;
continue;
}
if (chunk->update()) { // MAYBE: update joints
//TODO: extract in contouring
contouring::surrounding::chunks surrounding;
surrounding[contouring::surrounding::CENTER] = chunk;
bool all_found = true;
for (size_t i = 0; all_found && i < contouring::surrounding::CENTER; i++) {
const auto it = chunks.find(chunkPos + g_face_offsets[i]);
if (it != chunks.end())
surrounding[i] = it->second;
else
all_found = false;
}
if(all_found) {
rmt_ScopedCPUSample(Render, 0);
if (chunk->buffer != NULL)
delete chunk->buffer;
std::vector<VertexData> vertices;
{
rmt_ScopedCPUSample(Contouring, 0);
SurrondingFlatBox::render(surrounding, vertices);
}
ShortIndexedBuffer::Data data(vertices); // NOTE: indexing is really slow
{
rmt_ScopedCPUSample(Uploading, 0);
chunk->buffer = new ShortIndexedBuffer(GL_TRIANGLES, data);
}
}
if (chunk->update()) { // MAYBE: also contour joints
contouring->onUpdate(chunkPos, chunks); //TODO: get update update_type(simple(pos), complex)
} else if (chunkChange) { //NOTE: must be solved before octrees
contouring->onNotify(chunkPos, chunks);
}
}
}
contouring->update(pos);
rep.chunk_unload.push(unloadQueue.size());
// Unload dead chunks
......@@ -99,7 +76,8 @@ void World::update(const camera_pos& pos, World::report& rep) {
const auto chunk = std::make_shared<Chunk>(pos, generator);
chunks.insert({pos, chunk});
//trigger surronding render
for (size_t i = 0; i < contouring::surrounding::CENTER; i++) {
//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();
......@@ -113,17 +91,7 @@ void World::setOptions(const World::options& options) {
keepDistance = options.keepDistance;
}
void World::getModels(std::vector<std::pair<glm::mat4, Buffer*>> &out, float scale) const {
const auto scaling = glm::scale(glm::mat4(1), glm::vec3(scale));
for(const auto [pos, chunk]: chunks) {
if(chunk->buffer != NULL)