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

Anisotropy textures and chunk edit animation

parent aa7a196c
......@@ -49,10 +49,9 @@ add_executable(univerxel "src/main.cpp" ${SOURCES} ${INCLUDE_SOURCES})
target_compile_features(univerxel PUBLIC cxx_std_17)
target_link_libraries(univerxel ${LINKED_LIBS})
target_include_directories(univerxel PRIVATE ${INCLUDE_LIBS})
if(PROFILING)
target_compile_definitions(univerxel PRIVATE TRACY_ENABLE=1 FIXED_WINDOW=${FIXED_WINDOW} HN_USE_FILESYSTEM=1)
else(PROFILING)
target_compile_definitions(univerxel PRIVATE FIXED_WINDOW=${FIXED_WINDOW} HN_USE_FILESYSTEM=1)
if(PROFILING)
add_compile_definitions(TRACY_ENABLE=1)
endif(PROFILING)
file(COPY resource/content DESTINATION ${CMAKE_BINARY_DIR})
......
......@@ -52,6 +52,7 @@ To get a local copy up and running, follow these simple steps.
#### Optionally
* Python: utility scripts
* Tracy v0.7: profiling
### Installation
......
# Features
# Milestones
## Data
- [x] Generate noise
- [x] Density
- [x] Robin hood map
- [ ] Memory usage
- [ ] In memory RLE
- [ ] Octree world
- [x] Serialize
- [x] Group files
- [x] Zstd + custom grouping
- [~] Find best region size
- [x] Zstd Train dictionary
- [x] Low memory: Keep only ifstream
- [x] High memory: Save multiple
- [x] Unload unused
- [x] Edition
- [~] Entity
- [x] Basic
- [x] Instanced
- https://learnopengl.com/Advanced-OpenGL/Instancing
- [ ] Inheritance
- [ ] ECS
- [ ] Relative to area
- [ ] Player as entity
- [ ] Entities block world changes
- [x] Area
- [x] Offset
- [ ] Rotation
- [~] Planet
## Hello screen again
- [ ] Extract OpenGL
- [ ] Minimal Vulkan
- [ ] ImGui
- [ ] Config (yaml)
## Hello other
- [ ] Chat
- [ ] Auth
- [ ] Embedded
- [ ] Standalone
## Hello world
- [ ] Map stream
- [ ] Contouring
- [ ] Edit
- [~] Occlusion Culling
- [ ] Iterator ray
- [ ] Cast from chunk center
## Hello darkness
- [ ] ECS
- [ ] Area
- [ ] Rotation
- [~] CubeSphere
- [ ] Area corrected CubeSphere
- [ ] Corrected Normals
- [ ] Surface curvature
- [ ] Curvature avare frustum
- [x] Sphere
- [ ] Healpix
- [ ] Surface features
- [ ] Biomes
......@@ -42,64 +40,19 @@
- https://imgur.com/a/bh2iy
- https://speciesdevblog.files.wordpress.com/2012/11/biomemap.png
- [ ] Galaxy
- [ ] Leak test
- Valgrind
- Xtree-memory
- [ ] sanitizer
- [x] clang-tidy
- [ ] clang -fall
- [ ] Server
- [ ] ZeroMQ
- [~] Workers
- [x] Basic
- [x] Use system infos
- [ ] Pool
- [x] Logger
- [ ] FastNoiseSIMD / HastyNoise double precision
- [x] Generational identifier
- [ ] Limit map usage
## Rendering
- [x] Render triangle
- [x] Avoid texture noise
- [x] Better cheap planar
- [x] MipMap LOD
- [x] Fog
- [x] Multi Samples
- [x] SRGB
- [ ] HDR
- https://www.youtube.com/watch?v=iikdcAA7cww
- Toon shading
- Eye adaptation
- [ ] Post processing
- https://learnopengl.com/Advanced-OpenGL/Anti-Aliasing
- Bloom
- [x] Skybox
- [ ] Environment mapping
- [ ] Deferred
- [ ] Cascaded shadow maps
- [ ] Vulkan
- [ ] Dynamic SIMD libs
- [ ] FastNoiseSIMD / HastyNoise double precision
- [ ] Octree
- [ ] Cross plateforme encoding
- [ ] HDR
- https://www.youtube.com/watch?v=iikdcAA7cww
- Toon shading
- Eye adaptation
- [ ] Post processing
- https://learnopengl.com/Advanced-OpenGL/Anti-Aliasing
- Bloom
- [ ] Procedural Skybox
- [ ] Deferred
- [ ] Cascaded shadow maps
- [ ] Ray Tracing
- [~] Transparency
- [x] Float precision problem
## Contouring
- [x] Box contouring
- [x] Ignore sides
- [~] LOD
- [x] Generate lod
- [x] Display lod
- [x] Select level count
- [ ] Group low lod buffers
- [ ] Octree
- [x] Dual MC
- [~] Collision
- [ ] Dynamic index size
- [x] Chunk size performance
- [ ] Render with glBufferSubData
- [x] Frustum Culling
- [~] Occlusion Culling
- [x] Primitive raycast
- [ ] Iterator ray
- [ ] Cast from chunk center
- [x] Document
\ No newline at end of file
- [ ] Transparency
......@@ -28,9 +28,12 @@ namespace contouring {
virtual void onGui() = 0;
/// Get options
virtual std::string getOptions() const = 0;
// Get camera recommended far range
/// Get camera recommended far range
virtual std::pair<float, float> getFarRange() const = 0;
/// Get pending elements
virtual size_t getQueueSize() = 0;
using area_info = std::tuple<area_pos, voxel_pos::value_type, float>;
using draw_call = const std::function<void(glm::mat4, buffer::Abstract *const, const area_info&, const voxel_pos&)> &;
......
......@@ -15,6 +15,7 @@ namespace contouring {
void onGui() override { }
std::string getOptions() const override { return ""; }
std::pair<float, float> getFarRange() const override { return std::make_pair(0, 0); }
size_t getQueueSize() override { return 0; }
void getModels(draw_call, const std::optional<geometry::Frustum> &, const glm::llvec3 &, int) override {}
void getModels(draw_call, const glm::ifvec3 &, float, const std::vector<glm::vec3> &, const glm::llvec3 &, int) override {}
};
......
......@@ -82,7 +82,12 @@ namespace contouring {
return ss.str();
}
void FlatDualMC::enqueue(const area_<chunk_pos> &pos, const chunk_pos &offset, const world::ChunkContainer &data) {
size_t FlatDualMC::getQueueSize() {
return loadQueue.size();
}
void FlatDualMC::enqueue(const area_<chunk_pos> &pos, const chunk_pos &offset, const world::ChunkContainer &data)
{
ZoneScopedN("EnqueueContouring");
const auto dist2 = glm::length2(offset - pos.second);
if (dist2 <= loadDistance * loadDistance) {
......@@ -203,15 +208,30 @@ namespace contouring {
std::array<dualmc::DualMC<float>::Point, SIZE * SIZE * SIZE> grid;
{
ZoneScopedN("Load");
const auto setCell = [&](int x, int y, int z, const world::Voxel &voxel) {
auto &cell = grid[((z * SIZE) + y) * SIZE + x];
cell.w = voxel.material();
cell.x = voxel.density_ratio() * (!world::materials::invisibility[cell.w] && (transparency || !world::materials::transparency[cell.w]));
};
for (int z = 0; z < SIZE; z++) {
for (int y = 0; y < SIZE; y++) {
for (int x = 0; x < SIZE; x++) {
auto &cell = grid[((z * SIZE) + y) * SIZE + x];
const auto &chunk = surrounding[(z >= CHUNK_LENGTH) + (y >= CHUNK_LENGTH)*2 + (x >= CHUNK_LENGTH)*4];
const auto &voxel = chunk->get(glm::toIdx(x % CHUNK_LENGTH, y % CHUNK_LENGTH, z % CHUNK_LENGTH));
cell.w = voxel.material();
cell.x = voxel.density_ratio() * (!world::materials::invisibility[cell.w] && (transparency || !world::materials::transparency[cell.w]));
setCell(x, y, z, voxel);
}}}
for (size_t i = 0; i < surrounding.size(); i++) {
auto &edits = surrounding[i]->getEdits();
auto offset = glm::ivec3(surrounding::g_corner_offsets[i]) * CHUNK_LENGTH;
for (auto it = edits.end(); it != edits.begin();) {
it--;
auto p = offset + glm::ivec3(glm::fromIdx(it->idx));
if(p.x < SIZE && p.y < SIZE && p.z < SIZE) {
setCell(p.x, p.y, p.z, it->value);
}
}
}
}
{
std::vector<dualmc::Vertex> dmc_vertices;
......
......@@ -22,6 +22,7 @@ namespace contouring {
void onGui() override;
std::string getOptions() const override;
size_t getQueueSize() override;
/// Chunk data change
void onUpdate(const area_<chunk_pos> &, const chunk_pos &, const world::ChunkContainer &, geometry::Faces) override;
......
#include "FlatSurroundingBox.hpp"
#include "boxing.hpp"
#include "../world/Chunk.hpp"
#include <imgui.h> // NOLINT
using namespace geometry;
namespace contouring {
FlatSurroundingBox::FlatSurroundingBox(const std::string &opt) : AbstractFlat(opt) {
for (size_t i = 1; i <= 4; i++) {
workers.emplace_back([&] {
while (running) {
std::pair<area_<chunk_pos>, surrounding::faces> ctx;
loadQueue.wait();
if (loadQueue.pop(ctx)) {
std::vector<buffer::PackedVertexData> vertices;
render(ctx.second, vertices);
{
loadedQueue.emplace(ctx.first, vertices);
}
}
}
});
}
}
FlatSurroundingBox::~FlatSurroundingBox() {
running = false;
loadQueue.notify_all();
for(auto& worker: workers) {
if (worker.joinable())
worker.join();
}
}
void FlatSurroundingBox::enqueue(const area_<chunk_pos> &pos, const chunk_pos& offset, const world::ChunkContainer &data) {
const auto dist2 = glm::length2(offset - pos.second);
if (dist2 <= loadDistance * loadDistance) {
surrounding::faces surrounding;
if(surrounding::load(surrounding, pos.second, data)) {
loadQueue.push(pos, surrounding, -dist2);
}
}
}
void FlatSurroundingBox::onUpdate(const area_<chunk_pos> &pos, const chunk_pos& offset, const world::ChunkContainer &data, Faces neighbors) {
enqueue(pos, offset, data);
if (neighbors && Faces::Right)
enqueue(std::make_pair(pos.first, pos.second + g_face_offsets[static_cast<int>(Face::Right)]), offset, data);
if (neighbors && Faces::Left)
enqueue(std::make_pair(pos.first, pos.second + g_face_offsets[static_cast<int>(Face::Left)]), offset, data);
if (neighbors && Faces::Up)
enqueue(std::make_pair(pos.first, pos.second + g_face_offsets[static_cast<int>(Face::Up)]), offset, data);
if (neighbors && Faces::Down)
enqueue(std::make_pair(pos.first, pos.second + g_face_offsets[static_cast<int>(Face::Down)]), offset, data);
if (neighbors && Faces::Forward)
enqueue(std::make_pair(pos.first, pos.second + g_face_offsets[static_cast<int>(Face::Forward)]), offset, data);
if (neighbors && Faces::Backward)
enqueue(std::make_pair(pos.first, pos.second + g_face_offsets[static_cast<int>(Face::Backward)]), offset, data);
}
void FlatSurroundingBox::onNotify(const area_<chunk_pos> &pos, const chunk_pos& offset, const world::ChunkContainer &data) {
const auto it = buffers.find(pos.first);
if(it == buffers.end() || it->second.second.find(pos.second) == it->second.second.end()) {
enqueue(pos, offset, data);
}
}
void FlatSurroundingBox::update(const voxel_pos& pos, const world::area_map& areas) {
std::pair<area_<chunk_pos>, buffer::ShortIndexed::Data> out;
//MAYBE: clear out of range loadQueue.trim(keepDistance * keepDistance)
while(loadedQueue.pop(out)) {
const auto buffer = new buffer::ShortIndexed(GL_TRIANGLES, out.second);
auto& bfs = buffers[out.first.first].second; //NOTE: buffer.first uninitialized
if (const auto it = bfs.find(out.first.second); it != bfs.end()) {
if(it->second != NULL)
delete it->second;
it->second = buffer;
} else {
bfs.emplace(out.first.second, buffer);
}
}
AbstractFlat::clear(pos, areas);
}
bool FlatSurroundingBox::isTransparent(const surrounding::faces &surrounding, const std::pair<ushort, ushort> &idx) {
return !surrounding[idx.first]->get(idx.second).is_full();
}
void FlatSurroundingBox::render(const surrounding::faces &surrounding, std::vector<buffer::PackedVertexData> &vertices) {
const auto center = surrounding[surrounding::CENTER];
vertices.clear();
for (ushort i = 0; i < CHUNK_SIZE; i++) {
if (center->get(i).is_solid()) {
Faces faces = !center->get(i).is_full() ? Faces::All :
(isTransparent(surrounding, surrounding::getNeighborIdx(i, Face::Right)) & Faces::Right) |
(isTransparent(surrounding, surrounding::getNeighborIdx(i, Face::Left)) & Faces::Left) |
(isTransparent(surrounding, surrounding::getNeighborIdx(i, Face::Up)) & Faces::Up) |
(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);
box::addCube(vertices, glm::fromIdx(i), center->get(i).texture(), faces, glm::vec3(center->get(i).density_ratio()));
}
}
}
}
#pragma once
#include "AbstractFlat.hpp"
#include "surrounding.hpp"
#include "../data/safe_queue.hpp"
#include "../data/safe_priority_queue.hpp"
#include "../data/circular_buffer.hpp"
#include "../render/buffer/ShortIndexed.hpp"
#include <thread>
using namespace data;
namespace contouring {
/// Stupid cubes 1:1 contouring
class FlatSurroundingBox: public AbstractFlat {
public:
FlatSurroundingBox(const std::string&);
virtual ~FlatSurroundingBox();
void update(const voxel_pos&, const world::area_map&) override;
/// Chunk data change
void onUpdate(const area_<chunk_pos> &, const chunk_pos &, const world::ChunkContainer &, geometry::Faces) override;
/// Chunk existante ping
/// @note notify for chunks entering view while moving
void onNotify(const area_<chunk_pos> &, const chunk_pos &, const world::ChunkContainer &) override;
protected:
safe_priority_queue_map<area_<chunk_pos>, surrounding::faces, int, area_hash> loadQueue;
safe_queue<std::pair<area_<chunk_pos>, buffer::ShortIndexed::Data>> loadedQueue;
bool running = true;
std::vector<std::thread> workers;
void enqueue(const area_<chunk_pos> &, const chunk_pos& offset, const world::ChunkContainer &);
private:
static inline bool isTransparent(const surrounding::faces &surrounding, const std::pair<ushort, ushort> &idx);
static void render(const surrounding::faces &surrounding, std::vector<buffer::PackedVertexData> &vertices);
};
} // namespace contouring
#include "index.hpp"
#include "Dummy.hpp"
#include "FlatSurroundingBox.hpp"
#include "FlatDualMC.hpp"
namespace contouring {
......@@ -17,9 +16,6 @@ namespace contouring {
case 2:
return std::make_shared<Dummy>();
case 1:
return std::make_shared<FlatSurroundingBox>(data.at(names[1]));
default:
return std::make_shared<FlatDualMC>(data.at(names[0]));
}
......
......@@ -4,7 +4,7 @@
#include <map>
namespace contouring {
static const std::array<std::string, 3> names = {"FlatDualMC", "FlatBox", "Dummy"};
static const std::array<std::string, 2> names = {"FlatDualMC", "Dummy"};
int idxByName(const std::string &name);
std::shared_ptr<Abstract> load(int idx, const std::map<std::string, std::string> &data);
......
......@@ -99,6 +99,7 @@ int main(int /*unused*/, char */*unused*/[]){
}
if (state.capture_mouse) {
if (state.look_at.has_value()) {
ZoneScopedN("Edit");
if (inputs.isPressing(Mouse::Left))
world.setCube(state.look_at.value().pos, world::Voxel(world::materials::AIR, options.tool.empty_air * world::Voxel::DENSITY_MAX), options.tool.radius);
else if (inputs.isPressing(Mouse::Right))
......@@ -137,7 +138,7 @@ int main(int /*unused*/, char */*unused*/[]){
renderer->reloadShaders(options.renderer.voxel);
}
if(actions && UI::Actions::RendererTextures) {
renderer->reloadTextures(options.renderer.textures, options.renderer.mipMapLOD);
renderer->reloadTextures(options.renderer.textures, options.renderer.mipMapLOD, options.renderer.anisotropy);
}
if(actions && UI::Actions::World) {
world.setOptions(options.world);
......
......@@ -40,7 +40,7 @@ Renderer::Renderer(const Renderer::options& options):
IndicatorPass = std::make_unique<pass::ColorProgram>();
FogColor = glm::vec3(options.clear_color.x, options.clear_color.y, options.clear_color.z);
loadTextures(options.textures, options.mipMapLOD);
loadTextures(options.textures, options.mipMapLOD, options.anisotropy);
}
Renderer::~Renderer() {
......@@ -83,9 +83,9 @@ void Renderer::reloadShaders(const pass::VoxelProgram::options& options) {
WorldPass = std::make_unique<pass::WorldProgram>(options);
EntityPass = std::make_unique<pass::EntityProgram>(options);
}
void Renderer::reloadTextures(const std::string& texturePath, float mipMapLOD) {
void Renderer::reloadTextures(const std::string& texturePath, float mipMapLOD, float anisotropy) {
unloadTextures();
loadTextures(texturePath, mipMapLOD);
loadTextures(texturePath, mipMapLOD, anisotropy);
}
void Renderer::unloadTextures() {
......@@ -93,14 +93,14 @@ void Renderer::unloadTextures() {
glDeleteTextures(1, &NormalAtlas);
glDeleteTextures(1, &TextureAtlas);
}
void Renderer::loadTextures(const std::string& texturePath, float mipMapLOD) {
void Renderer::loadTextures(const std::string& texturePath, float mipMapLOD, float anisotropy) {
std::vector<std::string> terrainTextures;
for(const auto& texture: world::materials::textures) {
terrainTextures.emplace_back(texturePath + "/terrain/" + texture);
}
TextureAtlas = pass::Program::loadTextureArray(terrainTextures, "", mipMapLOD);
NormalAtlas = pass::Program::loadTextureArray(terrainTextures, ".nrm", mipMapLOD);
HOSAtlas = pass::Program::loadTextureArray(terrainTextures, ".hos", mipMapLOD);
TextureAtlas = pass::Program::loadTextureArray(terrainTextures, "", mipMapLOD, anisotropy * anisotropy);
NormalAtlas = pass::Program::loadTextureArray(terrainTextures, ".nrm", mipMapLOD, anisotropy * anisotropy);
HOSAtlas = pass::Program::loadTextureArray(terrainTextures, ".hos", mipMapLOD, anisotropy * anisotropy);
Skybox = pass::Program::loadTextureCube(texturePath + "/sky/Space_tray");
}
......
......@@ -25,6 +25,8 @@ public:
std::string textures = "1024-realistic";
/// Textures quality
float mipMapLOD = -.5;
/// Textures anisotropic mapping
int anisotropy = 0;
/// Depth color
glm::vec4 clear_color;
};
......@@ -82,7 +84,7 @@ public:
/// Apply camera matrices
void lookFrom(const Camera&);
void reloadShaders(const pass::VoxelProgram::options &);
void reloadTextures(const std::string &, float mipMapLOD = 0);
void reloadTextures(const std::string &, float mipMapLOD, float anisotropy);
private:
GLuint VertexArrayID;
......@@ -101,6 +103,6 @@ private:
GLuint HOSAtlas;
GLuint Skybox;
void loadTextures(const std::string &, float mipMapLOD = 0);
void loadTextures(const std::string &, float mipMapLOD, float anisotropy);
void unloadTextures();
};
......@@ -110,7 +110,8 @@ 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::Text("Textures '%s'", options.renderer.textures.c_str()); // MAYBE: select
if (ImGui::SliderFloat("LOD", &options.renderer.mipMapLOD, -1, 1)) {
if (ImGui::SliderFloat("LOD", &options.renderer.mipMapLOD, -1, 1) |
ImGui::SliderInt("Anisotropy", &options.renderer.anisotropy, 0, 8)) {
actions |= Actions::RendererTextures;
}
ImGui::End();
......
......@@ -48,12 +48,12 @@ void Program::useIt() {
GLuint Program::loadTexture(const std::string& name, bool linear) {
return loadDDS(TEXTURES_DIR + name + ".dds", linear);
}
GLuint Program::loadTextureArray(const std::vector<std::string> &names, const std::string& suffix, float mipMapLOD) {
GLuint Program::loadTextureArray(const std::vector<std::string> &names, const std::string& suffix, float mipMapLOD, float anisotropy) {
std::vector<std::string> paths;
std::transform(names.begin(), names.end(), std::back_inserter(paths),
[suffix](const std::string &name) -> std::string { return TEXTURES_DIR + name + suffix + ".dds"; });
return loadDDSArray(paths, mipMapLOD);
return loadDDSArray(paths, mipMapLOD, anisotropy);
}
GLuint Program::loadTextureCube(const std::string &name) {
......
......@@ -20,7 +20,7 @@ namespace pass {
void useIt();
static GLuint loadTexture(const std::string &name, bool linear = true);
static GLuint loadTextureArray(const std::vector<std::string> &names, const std::string &suffix = "", float mipMapLOD = 0);
static GLuint loadTextureArray(const std::vector<std::string> &names, const std::string &suffix = "", float mipMapLOD = 0, float anisotropy = 0);
static GLuint loadTextureCube(const std::string &name);
protected:
......
......@@ -111,7 +111,7 @@ GLuint loadDDS(const std::string& imagepath, bool linear){
}
GLuint loadDDSArray(const std::vector<std::string>& imagepaths, float mipMapLOD) {
GLuint loadDDSArray(const std::vector<std::string>& imagepaths, float mipMapLOD, float anisotropy) {
unsigned char header[124];
......@@ -177,6 +177,7 @@ GLuint loadDDSArray(const std::vector<std::string>& imagepaths, float mipMapLOD)
glTextureParameteri(textureID, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTextureParameteri(textureID, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
glTextureParameterf(textureID, GL_TEXTURE_LOD_BIAS, mipMapLOD);
glTextureParameterf(textureID, GL_TEXTURE_MAX_ANISOTROPY, anisotropy);
glGenerateTextureMipmap(textureID);
......
......@@ -7,6 +7,6 @@
// Load a .DDS file using GLFW's own loader
GLuint loadDDS(const std::string& imagepath, bool linear = true);
// Load a list of .DDS files
GLuint loadDDSArray(const std::vector<std::string> &imagepaths, float mipMapLOD = 0);
GLuint loadDDSArray(const std::vector<std::string> &imagepaths, float mipMapLOD, float anisotropy);
// Load a .DDS cubemap
GLuint loadDDSCube(const std::array<std::string, 6> &imagepaths);
......@@ -39,6 +39,7 @@ struct options {
renderer.textures = config["render"]["textures"].value_or(renderer.textures);
renderer.mipMapLOD = config["render"]["texture_quality"].value_or(renderer.mipMapLOD);
renderer.anisotropy = config["render"]["texture_angular_quality"].value_or(renderer.anisotropy);
renderer.voxel.pbr = config["render"]["pbr"].value_or(renderer.voxel.pbr);
renderer.voxel.triplanar = config["render"]["triplanar"].value_or(renderer.voxel.triplanar);
renderer.voxel.stochastic = config["render"]["stochastic"].value_or(renderer.voxel.stochastic);
......@@ -94,6 +95,7 @@ struct options {
config.insert_or_assign("render", toml::table({
{"textures", renderer.textures},
{"texture_quality", renderer.mipMapLOD},
{"texture_angular_quality", renderer.anisotropy},