1
0
Fork 0

Push It To The Limit + HastyNoise

This commit is contained in:
May B. 2020-08-01 23:31:01 +02:00
parent 6ace01afca
commit 7d125b81d1
30 changed files with 278 additions and 161 deletions

6
.vscode/tasks.json vendored
View File

@ -10,7 +10,7 @@
"label": "cmake",
"type": "shell",
"command": "cmake",
"args": [".."],
"args": ["-DFIXED_WINDOW=1", ".."],
"options": {
"cwd": "${workspaceRoot}/build"
}
@ -19,7 +19,7 @@
"label": "cmake info",
"type": "shell",
"command": "cmake",
"args": ["-DCMAKE_BUILD_TYPE=RelWithDebInfo", ".."],
"args": ["-DFIXED_WINDOW=1", "-DCMAKE_BUILD_TYPE=RelWithDebInfo", ".."],
"options": {
"cwd": "${workspaceRoot}/build"
}
@ -28,7 +28,7 @@
"label": "cmake debug",
"type": "shell",
"command": "cmake",
"args": ["-DCMAKE_BUILD_TYPE=Debug", ".."],
"args": ["-DFIXED_WINDOW=1", "-DCMAKE_BUILD_TYPE=Debug", ".."],
"options": {
"cwd": "${workspaceRoot}/build"
}

View File

@ -4,7 +4,8 @@ project (univerxel VERSION 0.0.1)
cmake_policy(SET CMP0072 NEW)
find_package(OpenGL REQUIRED)
option(PROFILING "Build with profiling" OFF)
option(PROFILING "Build with profiling" 0)
option(FIXED_WINDOW "Lock window size: Force floating on i3" 0)
if(NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE Release)
@ -52,12 +53,7 @@ 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 RMT_ENABLED=1 RMT_USE_OPENGL=1)
else(PROFILING)
target_compile_definitions(univerxel PRIVATE RMT_ENABLED=0)
endif(PROFILING)
add_dependencies(univerxel generate_dictionary)
target_compile_definitions(univerxel PRIVATE RMT_ENABLED=${PROFILING} RMT_USE_OPENGL=${PROFILING} FIXED_WINDOW=${FIXED_WINDOW} HN_USE_FILESYSTEM=1)
file(COPY content/shaders DESTINATION ${CMAKE_BINARY_DIR}/content)
file(COPY content/textures DESTINATION ${CMAKE_BINARY_DIR}/content)
@ -70,14 +66,13 @@ add_custom_target(docs
)
# Zstd dictionary
file(GLOB SMP_SOURCES "src/chunk_sampler.cpp" "src/world/Chunk.cpp" "include/FastNoiseSIMD/*.cpp")
add_executable(chunk_sampler EXCLUDE_FROM_ALL ${SMP_SOURCES})
target_compile_features(chunk_sampler PUBLIC cxx_std_17)
target_link_libraries(chunk_sampler ${LINKED_LIBS})
target_include_directories(chunk_sampler PRIVATE "include/FastNoiseSIMD")
file(GLOB SMP_SOURCES "src/zstd_sampler.cpp" "src/world/Chunk.cpp" "include/FastNoiseSIMD/*.cpp")
add_executable(zstd_sampler EXCLUDE_FROM_ALL ${SMP_SOURCES})
target_compile_features(zstd_sampler PUBLIC cxx_std_17)
target_link_libraries(zstd_sampler ${LINKED_LIBS})
target_include_directories(zstd_sampler PRIVATE "include/FastNoiseSIMD")
# target_compile_definitions(zstd_sampler PRIVATE HN_USE_FILESYSTEM=1)
add_custom_command(OUTPUT "${CMAKE_BINARY_DIR}/content/zstd.dict"
COMMAND "${CMAKE_BINARY_DIR}/chunk_sampler"
COMMAND zstd --train "${CMAKE_BINARY_DIR}/samples/*" -o "${CMAKE_BINARY_DIR}/content/zstd.dict"
DEPENDS chunk_sampler)
COMMAND "${CMAKE_BINARY_DIR}/zstd_sampler" DEPENDS zstd_sampler)
add_custom_target(generate_dictionary DEPENDS "${CMAKE_BINARY_DIR}/content/zstd.dict")

View File

@ -35,6 +35,7 @@
- [ ] Server
- [ ] ZeroMQ
- [x] Logger
- [ ] FastNoiseSIMD / HastyNoise double precision
## Rendering
- [x] Render triangle
@ -56,6 +57,7 @@
- [ ] Deferred
- [ ] Cascaded shadow maps
- [ ] RayCast
- [x] Float precision problem
## Contouring
- [x] Box contouring

View File

@ -1,33 +0,0 @@
/**
* \file chunk_sampler.cpp
* \brief Generate uncompressed chunks
* \author Maelys Bois
* \version 0.0.1
*
* Generate random uncompressed chunks for Zstd dictionary training.
*/
#include "world/Chunk.hpp"
#include <cstdlib>
#include <ctime>
#include <filesystem>
#include <fstream>
#include <iostream>
const auto SAMPLE_SIZE = 10000;
/// Entry point
int main(int /*unused*/, char * /*unused*/[])
{
std::srand(std::time(nullptr));
world::Generator generator(std::rand());
std::filesystem::create_directories("samples");
for (size_t i = 0; i < SAMPLE_SIZE; i++)
{
world::Chunk chunk(chunk_pos(std::rand(), std::rand(), std::rand()), generator);
std::ofstream out("samples/" + std::to_string(i));
chunk.write(out);
out.close();
}
return 0;
}

View File

@ -4,7 +4,6 @@
#include "../data/geometry/Frustum.hpp"
#include "../data/geometry/Faces.hpp"
#include "../world/forward.h"
typedef glm::vec3 camera_pos;
/// Mesh creation
namespace contouring {
@ -16,10 +15,10 @@ namespace contouring {
/// Each frame ping.
/// Mostly used for cleanup and to flush buffers data using main thread
virtual void update(const camera_pos &pos) = 0;
virtual void update(const voxel_pos &pos) = 0;
/// Chunk data change
virtual void onUpdate(const chunk_pos &pos, const world::chunk_map& data, geometry::Faces neighbors) = 0;
virtual void onUpdate(const chunk_pos &pos, const world::chunk_map &data, geometry::Faces neighbors) = 0;
/// Chunk existante ping
/// @note notify for chunks entering view while moving
virtual void onNotify(const chunk_pos &pos, const world::chunk_map &data) = 0;
@ -30,6 +29,6 @@ namespace contouring {
/// Get buffers in frustum with model matrices
/// @note buffers invalidated after update
virtual void getModels(std::vector<std::pair<glm::mat4, buffer::Abstract *const>> &buffers, const std::optional<geometry::Frustum>& frustum, float scale) = 0;
virtual void getModels(std::vector<std::pair<glm::mat4, buffer::Abstract *const>> &buffers, const std::optional<geometry::Frustum>& frustum, const glm::llvec3& offset, int density) = 0;
};
}

View File

@ -5,7 +5,7 @@
#include "../world/Chunk.hpp"
namespace contouring {
void AbstractFlat::update(const camera_pos& pos) {
void AbstractFlat::update(const voxel_pos& pos) {
center = glm::divide(pos, chunk_voxel_pos(CHUNK_LENGTH));
auto it = buffers.begin();
while (it != buffers.end()) { // Remove out of range buffers
@ -39,11 +39,12 @@ namespace contouring {
ImGui::SliderInt("Keep Distance", &keepDistance, loadDistance+1, 21);
}
void AbstractFlat::getModels(std::vector<std::pair<glm::mat4, buffer::Abstract *const>> &out, const std::optional<geometry::Frustum> &frustum, float scale) {
const auto scaling = glm::scale(glm::mat4(1), glm::vec3(scale));
void AbstractFlat::getModels(std::vector<std::pair<glm::mat4, buffer::Abstract *const>> &out, const std::optional<geometry::Frustum> &frustum, const glm::llvec3& offset, int density) {
const auto scaling = glm::scale(glm::mat4(1), glm::vec3(1.f / density));
for (const auto [pos, buffer] : buffers) {
if (buffer != NULL && (!frustum.has_value() || frustum.value().contains(geometry::Box::fromMin(scale * glm::vec3(pos) * glm::vec3(CHUNK_LENGTH), scale * glm::vec3(CHUNK_LENGTH)))))
out.emplace_back(glm::translate(scaling, glm::vec3(pos) * glm::vec3(CHUNK_LENGTH)), buffer);
const glm::vec3 fPos = glm::vec3(glm::llvec3(pos) * glm::llvec3(CHUNK_LENGTH) - offset * glm::llvec3(density)) / glm::vec3(density);
if (buffer != NULL && (!frustum.has_value() || frustum.value().contains(geometry::Box::fromMin(fPos, glm::vec3(CHUNK_LENGTH / (float)density)))))
out.emplace_back(glm::translate(scaling, fPos * (float)density), buffer);
}
}
}

View File

@ -11,7 +11,7 @@ namespace contouring {
virtual ~AbstractFlat() {}
/// Each frame ping. Used to clear out of range
void update(const camera_pos &) override;
void update(const voxel_pos &) override;
/// Display ImGui config
void onGui() override;
@ -20,7 +20,7 @@ namespace contouring {
/// Get buffers in frustum with model matrices
/// @note buffers invalidated after update
void getModels(std::vector<std::pair<glm::mat4, buffer::Abstract *const>> &out, const std::optional<geometry::Frustum> &frustum, float scale) override;
void getModels(std::vector<std::pair<glm::mat4, buffer::Abstract *const>> &out, const std::optional<geometry::Frustum> &frustum, const glm::llvec3& offset, int density) override;
protected:
bool inline inLoadRange(const chunk_pos& point) const {

View File

@ -9,11 +9,11 @@ namespace contouring {
Dummy(): Abstract() { }
virtual ~Dummy() { }
void update(const camera_pos &) override { }
void update(const voxel_pos &) override { }
void onUpdate(const chunk_pos &, const world::chunk_map &, geometry::Faces) override {}
void onNotify(const chunk_pos &, const world::chunk_map &) override { }
void onGui() override { }
std::string getOptions() override { return ""; }
void getModels(std::vector<std::pair<glm::mat4, buffer::Abstract *const>> &, const std::optional<geometry::Frustum>&, float) override { }
void getModels(std::vector<std::pair<glm::mat4, buffer::Abstract *const>> &, const std::optional<geometry::Frustum>&, const glm::llvec3&, int) override { }
};
}

View File

@ -75,7 +75,7 @@ namespace contouring {
}
}
void FlatDualMC::update(const camera_pos& pos) {
void FlatDualMC::update(const voxel_pos& pos) {
AbstractFlat::update(pos);
std::pair<chunk_pos, buffer::ShortIndexed::Data> out;
reports.load.push(loadQueue.size());

View File

@ -19,7 +19,7 @@ namespace contouring {
FlatDualMC(const std::string&);
virtual ~FlatDualMC();
void update(const camera_pos&) override;
void update(const voxel_pos&) override;
void onGui() override;

View File

@ -74,7 +74,7 @@ namespace contouring {
}
}
void FlatSurroundingBox::update(const camera_pos& pos) {
void FlatSurroundingBox::update(const voxel_pos& pos) {
AbstractFlat::update(pos);
std::pair<chunk_pos, buffer::ShortIndexed::Data> out;
reports.load.push(loadQueue.size());

View File

@ -19,7 +19,7 @@ namespace contouring {
FlatSurroundingBox(const std::string&);
virtual ~FlatSurroundingBox();
void update(const camera_pos&) override;
void update(const voxel_pos&) override;
void onGui() override;

View File

@ -15,15 +15,15 @@ namespace contouring::surrounding {
std::pair<ushort, ushort> getNeighborIdx(ushort idx, geometry::Face face);
typedef std::array<std::shared_ptr<const world::Chunk>, 8> corners;
const glm::ivec3 g_corner_offsets[8] = {
glm::ivec3(0, 0, 0),
glm::ivec3(0, 0, 1),
glm::ivec3(0, 1, 0),
glm::ivec3(0, 1, 1),
glm::ivec3(1, 0, 0),
glm::ivec3(1, 0, 1),
glm::ivec3(1, 1, 0),
glm::ivec3(1, 1, 1),
const chunk_pos g_corner_offsets[8] = {
chunk_pos(0, 0, 0),
chunk_pos(0, 0, 1),
chunk_pos(0, 1, 0),
chunk_pos(0, 1, 1),
chunk_pos(1, 0, 0),
chunk_pos(1, 0, 1),
chunk_pos(1, 1, 0),
chunk_pos(1, 1, 1),
};
bool load(corners &out, const chunk_pos &chunkPos, const world::chunk_map &chunks);

View File

@ -3,7 +3,8 @@
#include <glm/gtc/matrix_transform.hpp>
#include "../render/window.hpp"
Camera::Camera(GLFWwindow *window, const InputMap& inputs, const Camera::options& opt): window(window), inputs(inputs), o(opt) {
Camera::Camera(GLFWwindow *window, const InputMap& inputs, const Camera::options& opt): window(window), inputs(inputs),
Position(voxel_pos(1ll << 24), 1), o(opt){
updateProjection();
}
Camera::~Camera() { }
@ -57,36 +58,37 @@ void Camera::update(bool captureMouse, bool captureKeys) {
if(captureKeys) {
// Move forward
if (inputs.isDown(Input::Forward)) {
Position += direction * deltaTime * o.speed;
Position.offset += direction * deltaTime * o.speed;
}
// Move backward
if (inputs.isDown(Input::Backward)) {
Position -= direction * deltaTime * o.speed;
Position.offset -= direction * deltaTime * o.speed;
}
// Strafe right
if (inputs.isDown(Input::Right)) {
Position += right * deltaTime * o.speed;
Position.offset += right * deltaTime * o.speed;
}
// Strafe left
if (inputs.isDown(Input::Left)) {
Position -= right * deltaTime * o.speed;
Position.offset -= right * deltaTime * o.speed;
}
// Move up
if (inputs.isDown(Input::Up)) {
Position += up * deltaTime * o.speed;
Position.offset += up * deltaTime * o.speed;
}
// Move down
if (inputs.isDown(Input::Down)) {
Position -= up * deltaTime * o.speed;
Position.offset -= up * deltaTime * o.speed;
}
Position.center();
}
// MAYBE: only if moved
// MAYBE: save frustum
// Camera matrix
ViewMatrix = glm::lookAt(
Position, // Camera is here
Position + direction, // and looks here : at the same position, plus "direction"
Position.offset, // Camera is here
Position.offset + direction, // and looks here : at the same position, plus "direction"
up // Head is up (set to 0,-1,0 to look upside-down)
);

View File

@ -7,7 +7,7 @@
#include "../data/glm.hpp"
#include "../data/geometry/Frustum.hpp"
#include "../data/geometry/Ray.hpp"
typedef glm::vec3 camera_pos;
#include "../world/position.h"
/// Moving perspective camera
class Camera {
@ -36,7 +36,7 @@ public:
constexpr glm::mat4 getViewMatrix() const { return ViewMatrix; }
constexpr glm::mat4 getProjectionMatrix() const { return ProjectionMatrix; }
constexpr camera_pos getPosition() const { return Position; }
camera_pos getPosition() const { return Position; }
constexpr float getDepth() const { return o.far; }
private:
@ -47,7 +47,7 @@ private:
glm::mat4 ProjectionMatrix;
void updateProjection();
camera_pos Position = glm::vec3(0, 0, 5);
camera_pos Position;
float HorizontalAngle = 3.14f;
float VerticalAngle = 0.0f;

View File

@ -1,16 +1,16 @@
#pragma once
#include "../glm.hpp"
#include "../../world/position.h"
/// Math utils
namespace geometry {
enum class Face {
Right, Left, Up, Down, Forward, Backward
};
const glm::ivec3 g_face_offsets[6] = {
glm::ivec3(1,0,0), glm::ivec3(-1,0,0),
glm::ivec3(0,1,0), glm::ivec3(0,-1,0),
glm::ivec3(0,0,1), glm::ivec3(0,0,-1),
const chunk_pos g_face_offsets[6] = {
chunk_pos(1,0,0), chunk_pos(-1,0,0),
chunk_pos(0,1,0), chunk_pos(0,-1,0),
chunk_pos(0,0,1), chunk_pos(0,0,-1),
};
enum class Faces {

View File

@ -1,30 +1,30 @@
#pragma once
#include "Box.hpp"
#include "../glm.hpp"
#include "../../world/position.h"
namespace geometry {
/// Raycast with distance
struct Ray {
glm::vec3 from;
camera_pos from;
glm::vec3 dir;
float dist;
// MAYBE: Ray(const glm::mat4& view_matrix) { }
Ray(const glm::vec3& from, const glm::vec3& dir, float dist): from(from), dir(glm::normalize(dir)), dist(dist) { }
Ray(const camera_pos& from, const glm::vec3& dir, float dist): from(from), dir(glm::normalize(dir)), dist(dist) { }
inline Ray operator/(float scale) const noexcept {
return Ray(from / scale, dir, dist / scale);
inline Ray operator*(float scale) const noexcept {
return Ray(from * scale, dir, dist * scale);
}
/// Get path points in integer grid
/// @note not precise enough
inline void grid(std::vector<glm::lvec3>& points) const {
glm::lvec3 current = from + glm::vec3(.5f);
const glm::lvec3 d = dir * dist;
const glm::lvec3 inc = glm::lvec3((d.x < 0) ? -1 : 1, (d.y < 0) ? -1 : 1, (d.z < 0) ? -1 : 1);
const glm::lvec3 size = glm::abs(d);
const glm::lvec3 delta = size << 1ll;
inline void grid(std::vector<glm::llvec3>& points) const {
glm::llvec3 current = from.as_voxel();
const glm::llvec3 d = dir * dist;
const glm::llvec3 inc = glm::llvec3((d.x < 0) ? -1 : 1, (d.y < 0) ? -1 : 1, (d.z < 0) ? -1 : 1);
const glm::llvec3 size = glm::abs(d);
const glm::llvec3 delta = size << 1ll;
if ((size.x >= size.y) && (size.x >= size.z)) {
int err_1 = delta.y - size.x;

View File

@ -3,8 +3,8 @@
#include <glm/glm.hpp>
namespace glm {
typedef vec<3, long long> lvec3;
typedef vec<4, long long> lvec4;
typedef vec<3, long long> llvec3;
typedef vec<3, long> lvec3;
typedef vec<3, ushort> usvec3;
typedef vec<3, unsigned char> ucvec3;
}

View File

@ -17,16 +17,19 @@ namespace glm {
constexpr uint inline rem(long long value, uint m) {
return value < 0 ? ((value+1) % (long long)m) + m - 1 : value % (long long)m;
}
constexpr int inline div(long long value, uint m) {
constexpr long inline div(long long value, uint m) {
return value < 0 ? ((value+1) / (long long)m) - 1 : value / (long long)m;
}
constexpr ucvec3 inline modulo(const lvec3& value, const ucvec3& m) {
constexpr ucvec3 inline modulo(const llvec3& value, const ucvec3& m) {
return ucvec3(rem(value.x, m.x), rem(value.y, m.y), rem(value.z, m.z));
}
constexpr ivec3 inline divide(const lvec3 &value, const ucvec3 &m) {
return ivec3(div(value.x, m.x), div(value.y, m.y), div(value.z, m.z));
constexpr lvec3 inline divide(const llvec3 &value, const ucvec3 &m) {
return lvec3(div(value.x, m.x), div(value.y, m.y), div(value.z, m.z));
}
constexpr std::pair<ivec3, ucvec3> inline split(const lvec3 &value, const ucvec3 &m) {
constexpr lvec3 inline divide(const llvec3 &value, const uvec3 &m) {
return lvec3(div(value.x, m.x), div(value.y, m.y), div(value.z, m.z));
}
constexpr std::pair<lvec3, ucvec3> inline split(const llvec3 &value, const ucvec3 &m) {
return {divide(value, m), modulo(value, m)};
}
}

View File

@ -97,14 +97,14 @@ int main(int /*unused*/, char */*unused*/[]){
renderer->lookFrom(camera);
state.position = camera.getPosition();
state.look_at = world.raycast(camera.getRay() / options.voxel_size);
state.look_at = world.raycast(camera.getRay() * options.voxel_density);
if (state.capture_mouse && state.look_at.has_value()) {
if (inputs.isPressing(Mouse::Left))
world.setCube(state.look_at.value().first, world::Voxel{0, 0}, options.tool.radius);
else if (inputs.isPressing(Mouse::Right))
world.setCube(state.look_at.value().first, world::Voxel{UCHAR_MAX, options.tool.material}, options.tool.radius);
}
world.update(state.position / options.voxel_size, reports.world);
world.update((state.position * options.voxel_density).as_voxel(), reports.world);
inputs.saveKeys();
reports.main.update.push((glfwGetTime() - partTime) * 1000);
}
@ -159,7 +159,8 @@ int main(int /*unused*/, char */*unused*/[]){
if(options.culling) {
frustum = {camera.getFrustum()};
}
world.getContouring()->getModels(models, frustum, options.voxel_size);
const auto offset = state.position.raw_as_long();
world.getContouring()->getModels(models, frustum, offset, options.voxel_density);
reports.main.models_count = 0;
reports.main.tris_count = 0;
rmt_ScopedOpenGLSample(Render);
@ -170,7 +171,7 @@ int main(int /*unused*/, char */*unused*/[]){
if(state.look_at.has_value()) {
lookProgram->useIt();
lookProgram->start(renderer);
const auto model = glm::scale(glm::translate(glm::scale(glm::mat4(1), glm::vec3(options.voxel_size)), glm::vec3(state.look_at.value().first) - glm::vec3(.5 + options.tool.radius)), glm::vec3(1 + options.tool.radius * 2));
const auto model = glm::scale(glm::translate(glm::scale(glm::mat4(1), 1.f / glm::vec3(options.voxel_density)), glm::vec3(state.look_at.value().first - offset * glm::llvec3(options.voxel_density)) - glm::vec3(.5 + options.tool.radius)), glm::vec3(1 + options.tool.radius * 2));
lookBuffer.draw(lookProgram->setup(renderer, model));
}
renderer->postProcess();

View File

@ -112,13 +112,15 @@ UI::Actions UI::draw(options &options, state &state, const reports &reports, GLu
if (ImGui::SliderInt("Load distance", &options.world.loadDistance, 1, options.world.keepDistance) |
ImGui::SliderInt("Keep distance", &options.world.keepDistance, options.world.loadDistance + 1, 21)) {
actions = actions | Actions::World;
const auto far = std::clamp(options.camera.far, (options.world.loadDistance - 1.5f) * CHUNK_LENGTH * options.voxel_size, (options.world.keepDistance + .5f) * CHUNK_LENGTH * options.voxel_size);
const auto far = std::clamp(options.camera.far, (options.world.loadDistance - 1.5f) * CHUNK_LENGTH / options.voxel_density, (options.world.keepDistance + .5f) * CHUNK_LENGTH / options.voxel_density);
if(far != options.camera.far) {
options.camera.far = far;
actions = actions | Actions::Camera;
}
}
ImGui::SliderFloat("Voxel size", &options.voxel_size, .1, 2);
if(ImGui::SliderInt("Voxel density", &options.voxel_density, 1, CHUNK_LENGTH * REGION_LENGTH)) {
options.voxel_density = pow(2, ceil(log(options.voxel_density) / log(2)));
}
ImGui::End();
}
@ -144,13 +146,14 @@ UI::Actions UI::draw(options &options, state &state, const reports &reports, GLu
if (options.show_debug_controls) {
ImGui::Begin("Debug: Controls", &options.show_debug_controls, ImGuiWindowFlags_AlwaysAutoResize);
ImGui::Text("Position: (%.3f, %.3f, %.3f)", state.position.x, state.position.y, state.position.z);
const auto p = state.position.as_voxel(options.voxel_density);
ImGui::Text("Position: (%lld, %lld, %lld)", p.x, p.y, p.z);
ImGui::Separator();
{
bool changePerspective = false;
changePerspective |= ImGui::SliderAngle("FoV", &options.camera.fov, 30, 110);
changePerspective |= ImGui::SliderFloat("Near", &options.camera.near, 0.01, 10);
changePerspective |= ImGui::SliderFloat("Far", &options.camera.far, (options.world.loadDistance - 1.5) * CHUNK_LENGTH * options.voxel_size, (options.world.keepDistance + .5) * CHUNK_LENGTH * options.voxel_size);
changePerspective |= ImGui::SliderFloat("Far", &options.camera.far, (options.world.loadDistance - 1.5) * CHUNK_LENGTH / options.voxel_density, (options.world.keepDistance + .5) * CHUNK_LENGTH / options.voxel_density);
changePerspective |= ImGui::SliderFloat("Move speed", &options.camera.speed, 0.1, 50);
changePerspective |= ImGui::SliderInt("Sensibility", &options.camera.sensibility, 1, 100, "%d%%");
if(changePerspective) {
@ -164,7 +167,7 @@ UI::Actions UI::draw(options &options, state &state, const reports &reports, GLu
ImGui::Begin("Editor", &options.editor_show, ImGuiWindowFlags_AlwaysAutoResize);
if (state.look_at.has_value()) {
ImGui::Text("Look at: (%lld, %lld, %lld) (%s, %.1f)", state.look_at.value().first.x, state.look_at.value().first.y, state.look_at.value().first.z, world::materials::textures[state.look_at.value().second.Material].c_str(), state.look_at.value().second.Density * 1. / UCHAR_MAX);
ImGui::Text("(%.3f, %.3f, %.3f)", state.look_at.value().first.x * options.voxel_size, state.look_at.value().first.y * options.voxel_size, state.look_at.value().first.z * options.voxel_size);
ImGui::Text("(%.3f, %.3f, %.3f)", state.look_at.value().first.x * 1. / options.voxel_density, state.look_at.value().first.y * 1. / options.voxel_density, state.look_at.value().first.z * 1. / options.voxel_density);
} else {
ImGui::Text("Look at: none");
}

View File

@ -29,7 +29,9 @@ GLFWwindow* createWindow(int samples) {
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); // To make MacOS happy; should not be needed
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
glfwWindowHint(GLFW_RESIZABLE, false); //FIXME: Dev-only: Force floating on i3
#if FIXED_WINDOW
glfwWindowHint(GLFW_RESIZABLE, false); //Note: Dev-only: Force floating on i3
#endif
window = glfwCreateWindow(DEFAULT_WIDTH, DEFAULT_HEIGHT, "Univerxel", NULL, NULL);
if (window == NULL) {

View File

@ -50,7 +50,7 @@ struct options {
world.loadDistance = config["world"]["load_distance"].value_or(world.loadDistance);
world.keepDistance = config["world"]["keep_distance"].value_or(world.keepDistance);
world.folderPath = config["world"]["path"].value_or(world.folderPath);
voxel_size = config["world"]["unit_size"].value_or(1.f);
voxel_density = config["world"]["voxel_density"].value_or(1);
culling = config["mesh"]["culling"].value_or(true);
contouring_idx = contouring::idxByName(config["mesh"]["mode"].value_or(std::string("")));
@ -99,7 +99,7 @@ struct options {
config.insert_or_assign("world", toml::table({
{"load_distance", world.loadDistance},
{"keep_distance", world.keepDistance},
{"unit_size", voxel_size},
{"voxel_density", voxel_density},
{"path", world.folderPath}
}));
config.insert_or_assign("mesh", toml::table({
@ -154,7 +154,7 @@ struct options {
bool show_debug_world;
world::Universe::options world;
float voxel_size;
int voxel_density;
bool show_debug_contouring;
bool culling;
@ -180,7 +180,7 @@ struct options {
/// Live state
struct state {
bool capture_mouse = true;
camera_pos position;
camera_pos position = camera_pos(voxel_pos(0), 1);
std::optional<std::pair<voxel_pos, world::Voxel>> look_at = {};
std::shared_ptr<contouring::Abstract> contouring;

View File

@ -17,8 +17,8 @@ Chunk::Chunk(const chunk_pos& pos, Generator& rnd) {
voxels[i].Material = voxels[i].Density > 0 ? 1 + std::clamp(static_cast<int>(std::lrint((materialSet[i] + 1) / 2 * (materials::count - 2))),
0, materials::count - 2) : 0; //NOTE: map (approx -1, 1) to (1, mat_max)
}
FastNoiseSIMD::FreeNoiseSet(densitySet);
FastNoiseSIMD::FreeNoiseSet(materialSet);
rnd.free(densitySet);
rnd.free(materialSet);
}
Chunk::Chunk(std::istream& str) {
if constexpr (RLE) {

View File

@ -1,6 +1,10 @@
#pragma once
#include <FastNoiseSIMD.h> // NOLINT
#if HASTY
#include <hastyNoise.h>
#else
#include <FastNoiseSIMD.h>
#endif
#include <tuple>
#include "position.h"
@ -8,34 +12,61 @@ namespace world {
/// Noise generator
class Generator {
public:
using set = float *;
#if HASTY
using noise = std::unique_ptr<HastyNoise::NoiseSIMD>;
#else
using noise = FastNoiseSIMD *;
#endif
Generator(int seed = 42) {
#if HASTY
printf("double!!!\n");
const size_t fastestSimd = HastyNoise::GetFastestSIMD();
densityNoise = HastyNoise::CreateNoise(seed, fastestSimd);
materialNoise = HastyNoise::CreateNoise(seed * 5, fastestSimd);
materialNoise->SetNoiseType(HastyNoise::NoiseType::Cellular); // NOTE: probably heavy
materialNoise->SetCellularReturnType(HastyNoise::CellularReturnType::Value);
materialNoise->SetCellularDistanceFunction(HastyNoise::CellularDistance::Natural);
materialNoise->SetFrequency(.1);
#else
densityNoise = FastNoiseSIMD::NewFastNoiseSIMD(seed);
materialNoise = FastNoiseSIMD::NewFastNoiseSIMD(seed * 5);
materialNoise->SetNoiseType(FastNoiseSIMD::Cellular); // NOTE: probably heavy
materialNoise->SetCellularReturnType(FastNoiseSIMD::CellValue);
materialNoise->SetCellularDistanceFunction(FastNoiseSIMD::Natural);
materialNoise->SetFrequency(.1);
#endif
}
~Generator() {
#if !HASTY
delete densityNoise;
delete materialNoise;
#endif
}
/// Get block of given size with index pos.
/// @note owning pointers, @see FastNoiseSIMD::FreeNoiseSet
inline std::pair<float*, float*> getChunk(const chunk_pos& pos, int size) {
return {
/// @note owning pointers, call free(set set)
inline std::pair<set, set> getChunk(const chunk_pos& pos, int size) {
auto pair = std::make_pair(
densityNoise->GetNoiseSet(pos.x * size, pos.y * size, pos.z * size, size, size, size),
materialNoise->GetNoiseSet(pos.x * size, pos.y * size, pos.z * size, size, size, size),
};
materialNoise->GetNoiseSet(pos.x * size, pos.y * size, pos.z * size, size, size, size)
);
#if HASTY
return {pair.first.release(), pair.second.release()};
#else
return pair;
#endif
}
inline void freeChunk(std::pair<float*, float*>& set) {
FastNoiseSIMD::FreeNoiseSet(set.first);
FastNoiseSIMD::FreeNoiseSet(set.second);
static inline void free(set set) {
#if HASTY
HastyNoise::SetDeleter()(set);
#else
FastNoiseSIMD::FreeNoiseSet(set);
#endif
}
private:
FastNoiseSIMD *densityNoise;
FastNoiseSIMD *materialNoise;
noise densityNoise;
noise materialNoise;
};
}

View File

@ -112,7 +112,7 @@ std::shared_ptr<Region> Universe::getRegion(const region_pos& pos) {
return unique->insert({pos, reg}).first->second;
}
void Universe::update(const camera_pos& pos, Universe::report& rep) {
void Universe::update(const voxel_pos& pos, Universe::report& rep) {
const chunk_pos newPos = glm::divide(pos, chunk_voxel_pos(CHUNK_LENGTH));
const auto chunkChange = last_pos != newPos;
last_pos = newPos;
@ -152,7 +152,7 @@ void Universe::update(const camera_pos& pos, Universe::report& rep) {
for (int z = -loadDistance; z <= loadDistance; z++) {
const auto dist2 = x * x + y * y + z * z;
if (dist2 <= loadDistance * loadDistance) {
const chunk_pos p = last_pos + glm::ivec3(x, y, z);
const auto p = last_pos + chunk_pos(x, y, z);
if (chunks.find(p) == chunks.end()) {
loadQueue.push(p, -dist2);
}
@ -172,14 +172,13 @@ void Universe::update(const camera_pos& pos, Universe::report& rep) {
}
rep.chunk_count.push(chunks.size());
{
if(!rep.chunk_load.current() && !rep.chunk_unload.current()) {
rmt_ScopedCPUSample(Region, 0);
const auto unique = regions.lock(); // MAYBE: shared then unique
rep.region_count.push(unique->size());
const auto me = glm::divide(last_pos, glm::ivec3(REGION_LENGTH));
const auto me = glm::divide(last_pos, glm::uvec3(REGION_LENGTH));
for (auto it = unique->begin(); it != unique->end(); it++) {
if (glm::length2(it->first - me) > keepDistance) {
LOG_D("Remove region");
if (glm::length2(glm::lvec3(it->first) - me) > keepDistance) { //FIXME: probably a limit
unique->erase(it);
break; //NOTE: save one max per frame
}
@ -200,13 +199,13 @@ std::optional<std::pair<voxel_pos, Voxel>> Universe::raycast(const Ray &ray) con
std::vector<voxel_pos> points;
ray.grid(points);
std::shared_ptr<Chunk> chunk = NULL;
chunk_pos chunk_pos(INT_MAX);
chunk_pos chunk_vec(INT_MAX);
for(auto point: points) {
const auto pos = glm::divide(point, glm::ivec3(CHUNK_LENGTH));
if(pos != chunk_pos) {
const chunk_pos pos = glm::divide(point, glm::uvec3(CHUNK_LENGTH));
if(pos != chunk_vec) {
if(const auto& newChunk = at(pos)) {
chunk = newChunk.value();
chunk_pos = pos;
chunk_vec = pos;
}
}
if(chunk != NULL) {
@ -219,9 +218,9 @@ std::optional<std::pair<voxel_pos, Voxel>> Universe::raycast(const Ray &ray) con
}
std::optional<Item> Universe::set(const voxel_pos& pos, const Voxel& val) {
const auto chunkPos = glm::divide(pos, glm::ivec3(CHUNK_LENGTH));
const auto chunkPos = glm::divide(pos, glm::uvec3(CHUNK_LENGTH));
if(const auto& chunk = at(chunkPos)) {
return {chunk.value()->breakAt(glm::modulo(pos, glm::ivec3(CHUNK_LENGTH)), val)};
return {chunk.value()->breakAt(glm::modulo(pos, glm::uvec3(CHUNK_LENGTH)), val)};
} else {
return {};
}
@ -232,7 +231,7 @@ ItemList Universe::setCube(const voxel_pos& pos, const Voxel& val, int radius) {
for (int y = -radius; y <= radius; y++) {
for (int x = -radius; x <= radius; x++) {
//TODO: list.pop(val)
list.add(set(pos + glm::lvec3(x, y, z), val));
list.add(set(pos + glm::llvec3(x, y, z), val));
}}}
return list;
}

View File

@ -13,7 +13,6 @@ using namespace libguarded;
#include "Voxel.hpp"
#include "region/index.hpp"
#include "Generator.hpp"
typedef glm::vec3 camera_pos;
#define REPORT_BUFFER_SIZE 128
@ -52,7 +51,7 @@ namespace world {
~Universe();
/// Update physics and contouring
void update(const camera_pos& pos, report& rep);
void update(const voxel_pos &pos, report &rep);
/// Apply new options
void setOptions(const options &);

15
src/world/position.cpp Normal file
View File

@ -0,0 +1,15 @@
#include "position.h"
#include "../data/math.hpp"
camera_pos::camera_pos(const voxel_pos &pos, int density): raw(glm::divide(pos, glm::uvec3(REGION_LENGTH * CHUNK_LENGTH * density))), offset(0) { }
glm::llvec3 camera_pos::raw_as_long() const {
return glm::llvec3(raw) * glm::llvec3(REGION_LENGTH * CHUNK_LENGTH);
}
voxel_pos camera_pos::as_voxel(int density) const {
return raw_as_long() * glm::llvec3(density) + voxel_pos(offset * glm::vec3(density));
}
void camera_pos::center() {
const auto diff = glm::divide(offset, glm::uvec3(REGION_LENGTH * CHUNK_LENGTH));
raw += diff;
offset -= diff * static_cast<long>(REGION_LENGTH * CHUNK_LENGTH);
}

View File

@ -1,12 +1,47 @@
/**
* chunk_voxel_pos: u8 (contains CHUNK_LENGTH)
* chunk_pos: i56
* region_chunk_pos: u8 (contains REGION_LENGTH)
* region_pos: i48 (NOTE: trimmed to i32)
* voxel_pos: i64
*
* camera_offset: f32
* camera_pos: region_pos + camera_offset
*
* known limits:
* - noise: f32 2^24 spaghettification
* - noise: i32 2^32 loop
* - position: i32(chunk_pos)+u5(CHUNK_LENGTH) 2^36 no rendering (NOTE: only with chunk_pos as int)
* - position: i32(region_pos)+u5(REGION_LENGTH)+u5(CHUNK_LENGTH) 2^42 loop
*/
#pragma once
#include "../data/glm.hpp"
#define CHUNK_LENGTH 32
#define REGION_LENGTH 32
const auto CHUNK_LENGTH = 32;
const auto REGION_LENGTH = 32;
typedef glm::lvec3 voxel_pos;
typedef glm::ivec3 chunk_pos;
typedef glm::ucvec3 chunk_voxel_pos;
typedef glm::ivec3 region_pos;
typedef glm::ucvec3 region_chunk_pos;
using voxel_pos = glm::llvec3;
using chunk_pos = glm::lvec3;
using chunk_voxel_pos = glm::ucvec3;
using region_pos = glm::ivec3;
using region_chunk_pos = glm::ucvec3;
struct camera_pos {
using raw_t = region_pos;
using offset_t = glm::vec3;
camera_pos(const raw_t &raw, const offset_t &offset) : raw(raw), offset(offset){};
camera_pos(const voxel_pos& pos, int density);
raw_t raw;
offset_t offset;
voxel_pos as_voxel(int density = 1) const;
void center();
glm::llvec3 raw_as_long() const;
glm::dvec3 as_double() const;
inline camera_pos operator/(int i) const { return camera_pos(raw / i, offset / (i * 1.f)); }
inline camera_pos operator*(int i) const { return camera_pos(raw * i, offset * (i * 1.f)); }
};

63
src/zstd_sampler.cpp Normal file
View File

@ -0,0 +1,63 @@
/**
* \file zstd_sampler.cpp
* \brief Generate uncompressed chunks
* \author Maelys Bois
* \version 0.0.1
*
* Generate random uncompressed chunks for Zstd dictionary training.
*/
#include "world/Chunk.hpp"
#include <cstdlib>
#include <ctime>
#include <chrono>
#include <fstream>
#include <iostream>
#include <vector>
#include <zstd.h>
#include <zdict.h>
const auto KB = 1000;
const auto COUNT = 100;
const auto SIZE = COUNT * KB;
const auto SAMPLES = 100;
const auto RANGE = 1 << 18;
/// Entry point
int main(int /*unused*/, char * /*unused*/[])
{
std::srand(std::time(nullptr));
world::Generator generator(std::rand());
std::vector<char> samples;
samples.reserve(SIZE * SAMPLES);
std::vector<size_t> sizes;
sizes.reserve(SAMPLES * 10);
std::cout << "Generating..." << std::endl;
std::chrono::nanoseconds gen_time(0);
while(samples.size() < SIZE * SAMPLES) {
const auto start = std::chrono::high_resolution_clock::now();
world::Chunk chunk(chunk_pos(std::rand() % RANGE, std::rand() % RANGE, std::rand() % RANGE), generator);
gen_time += (std::chrono::high_resolution_clock::now() - start);
std::ostringstream oss;
chunk.write(oss);
const auto str = oss.str();
samples.insert(samples.end(), str.begin(), str.end());
sizes.push_back(str.size());
}
std::cout << gen_time.count() / sizes.size() << "ns/chunk" << std::endl;
std::vector<char> dict(SIZE);
std::cout << "Training on " << sizes.size() << " samples..." << std::endl;
const auto actualSize = ZDICT_trainFromBuffer(dict.data(), dict.size(), samples.data(), sizes.data(), sizes.size());
if(ZSTD_isError(actualSize)) {
std::cout << "Error: " << ZSTD_getErrorName(actualSize) << std::endl;
return 1;
}
std::cout << "Dictionary of " << actualSize / KB << "kb" << std::endl;
std::ofstream out("content/zstd.dict");
out.write(dict.data(), actualSize);
out.close();
return 0;
}