Anisotropy textures and chunk edit animation
This commit is contained in:
parent
aa7a196c9a
commit
f0f12da614
|
@ -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
|
||||
|
||||
|
|
135
TODO.md
135
TODO.md
|
@ -1,40 +1,38 @@
|
|||
# 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
|
||||
- [ ] 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;
|
||||
|
|
|
@ -1,112 +0,0 @@
|
|||
#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()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,41 +0,0 @@
|
|||
#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
|
|
@ -1,6 +1,5 @@
|
|||
#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},
|
||||
{"pbr", renderer.voxel.pbr},
|
||||
{"triplanar", renderer.voxel.triplanar},
|
||||
{"stochastic", renderer.voxel.stochastic},
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
#include <algorithm>
|
||||
#include "../data/math.hpp"
|
||||
#include <Tracy.hpp>
|
||||
|
||||
using namespace world;
|
||||
|
||||
|
@ -59,7 +60,18 @@ void Chunk::write(std::ostream& str, bool rle) const {
|
|||
}
|
||||
}
|
||||
|
||||
std::optional<Faces> Chunk::update() {
|
||||
std::optional<Faces> Chunk::update(float deltaTime, bool animate) {
|
||||
ZoneScopedN("Chunk");
|
||||
for(auto it = edits.begin(); it != edits.end();) {
|
||||
it->delay -= deltaTime;
|
||||
if(it->delay <= 0 && animate) {
|
||||
invalidate(it->idx);
|
||||
it = edits.erase(it);
|
||||
} else {
|
||||
it++;
|
||||
}
|
||||
}
|
||||
|
||||
if(upToDate) {
|
||||
return {};
|
||||
} else {
|
||||
|
@ -68,8 +80,7 @@ std::optional<Faces> Chunk::update() {
|
|||
}
|
||||
}
|
||||
|
||||
void Chunk::set(ushort idx, const Voxel& val) {
|
||||
voxels[idx] = val;
|
||||
void Chunk::invalidate(ushort idx) {
|
||||
invalidate(
|
||||
((!getNeighborIdx(idx, Face::Up).has_value()) & Faces::Up) |
|
||||
((!getNeighborIdx(idx, Face::Down).has_value()) & Faces::Down) |
|
||||
|
@ -78,6 +89,22 @@ void Chunk::set(ushort idx, const Voxel& val) {
|
|||
((!getNeighborIdx(idx, Face::Forward).has_value()) & Faces::Forward) |
|
||||
((!getNeighborIdx(idx, Face::Backward).has_value()) & Faces::Backward));
|
||||
}
|
||||
void Chunk::set(ushort idx, const Voxel& val) {
|
||||
voxels[idx] = val;
|
||||
invalidate(idx);
|
||||
}
|
||||
std::optional<Item> Chunk::replace(chunk_voxel_idx idx, const Voxel& val, float delay) {
|
||||
const auto res = voxels[idx];
|
||||
if (val.value != res.value) {
|
||||
if(delay > 0) {
|
||||
voxels[idx] = val;
|
||||
edits.emplace_back<Edit>({idx, res, delay});
|
||||
} else {
|
||||
set(idx, val);
|
||||
}
|
||||
}
|
||||
return {Item{res.density(), res.material()}}; //TODO: materials break table
|
||||
}
|
||||
|
||||
std::optional<chunk_voxel_idx> Chunk::getNeighborIdx(chunk_voxel_idx idx, Face dir) {
|
||||
switch (dir) {
|
||||
|
|
|
@ -19,39 +19,44 @@ namespace world {
|
|||
Chunk(std::istream& str, bool rle = RLE);
|
||||
~Chunk();
|
||||
|
||||
struct Edit {
|
||||
chunk_voxel_idx idx;
|
||||
Voxel value;
|
||||
float delay;
|
||||
};
|
||||
|
||||
/// Update voxels
|
||||
/// @return if modified neighbors to update
|
||||
std::optional<Faces> update();
|
||||
std::optional<Faces> update(float deltaTime, bool animate);
|
||||
|
||||
// Notify for render
|
||||
/// Notify for render
|
||||
inline void invalidate(Faces faces) {
|
||||
upToDate = false;
|
||||
toUpdate = toUpdate | faces;
|
||||
modified = true;
|
||||
}
|
||||
// Get voxel from index
|
||||
inline void invalidate(chunk_voxel_idx idx);
|
||||
/// Get voxel from index
|
||||
inline const Voxel& get(chunk_voxel_idx idx) const {
|
||||
return voxels[idx];
|
||||
}
|
||||
// Get voxel from position
|
||||
/// Get voxel from position
|
||||
inline const Voxel& getAt(const chunk_voxel_pos& pos) const {
|
||||
return get(glm::toIdx(pos));
|
||||
}
|
||||
// Set voxel from index
|
||||
/// Get pending changes
|
||||
const std::vector<Edit> &getEdits() const { return edits; }
|
||||
/// Set voxel from index
|
||||
void set(chunk_voxel_idx idx, const Voxel& val);
|
||||
// Set voxel from position
|
||||
/// Set voxel from position
|
||||
void setAt(const chunk_voxel_pos& pos, const Voxel& val) {
|
||||
set(glm::toIdx(pos), val);
|
||||
}
|
||||
// Break voxel
|
||||
std::optional<Item> replace(chunk_voxel_idx idx, const Voxel& val) {
|
||||
const auto res = voxels[idx];
|
||||
set(idx, val);
|
||||
return {Item{res.density(), res.material()}}; //TODO: materials break table
|
||||
}
|
||||
// Is player modified
|
||||
/// Break voxel
|
||||
std::optional<Item> replace(chunk_voxel_idx idx, const Voxel &val, float delay = 0);
|
||||
/// Is player modified
|
||||
inline bool isModified() const { return modified; }
|
||||
// Write to file.
|
||||
/// Write to file.
|
||||
void write(std::ostream& str, bool rle = RLE) const;
|
||||
|
||||
static std::optional<chunk_voxel_idx> getNeighborIdx(chunk_voxel_idx idx, Face dir);
|
||||
|
@ -59,6 +64,8 @@ namespace world {
|
|||
private:
|
||||
/// Chunk data
|
||||
std::array<Voxel, CHUNK_SIZE> voxels;
|
||||
/// Temporary changes
|
||||
std::vector<Edit> edits;
|
||||
/// Require update
|
||||
bool upToDate = true;
|
||||
/// Neighbors to update
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
#include <Tracy.hpp> //NOLINT
|
||||
#include <common/TracySystem.hpp> // NOLINT
|
||||
#include <filesystem>
|
||||
#include <random>
|
||||
|
||||
#include "../contouring/Dummy.hpp"
|
||||
#include "Chunk.hpp"
|
||||
|
@ -199,6 +200,8 @@ void Universe::update(const voxel_pos& pos, float deltaTime) {
|
|||
size_t chunk_count = 0;
|
||||
size_t region_count = 0;
|
||||
#endif
|
||||
auto rng = std::mt19937(std::rand());
|
||||
const auto contouringThreshold = rng.max() / (1 + contouring->getQueueSize());
|
||||
const bool queuesEmpty = loadQueue.empty() && saveQueue.empty();
|
||||
bool allLazy = true;
|
||||
auto it = areas.begin();
|
||||
|
@ -231,7 +234,7 @@ void Universe::update(const voxel_pos& pos, float deltaTime) {
|
|||
it_c = chunks.erase(it_c);
|
||||
}else {
|
||||
const area_<chunk_pos> acPos = std::make_pair(it->first, it_c->first);
|
||||
if (const auto neighbors = it_c->second->update()) {
|
||||
if (const auto neighbors = it_c->second->update(deltaTime, rng() < contouringThreshold)) {
|
||||
contouring->onUpdate(acPos, diff, chunks, neighbors.value());
|
||||
} else if (chunkChangeArea) {
|
||||
contouring->onNotify(acPos, diff, chunks);
|
||||
|
@ -393,10 +396,11 @@ ItemList Universe::setCube(const area_<voxel_pos>& pos, const Voxel& val, int ra
|
|||
for (int y = -radius; y <= radius; y++) {
|
||||
for (int x = -radius; x <= radius; x++) {
|
||||
//TODO: list.pop(val)
|
||||
const auto split = glm::splitIdx(pos.second + voxel_pos(x, y, z));
|
||||
const auto offset = voxel_pos(x, y, z);
|
||||
const auto split = glm::splitIdx(pos.second + offset);
|
||||
if(chunks.inRange(split.first))
|
||||
if(const auto chunk = it->second->setChunks().findInRange(split.first))
|
||||
list.add(chunk.value()->replace(split.second, val));
|
||||
list.add(chunk.value()->replace(split.second, val, glm::length2(offset) / radius * .05f));
|
||||
}}}
|
||||
}
|
||||
return list;
|
||||
|
|
Loading…
Reference in New Issue