Compare commits
11 Commits
d53299ebc8
...
bce746ca01
Author | SHA1 | Date |
---|---|---|
May B. | bce746ca01 | |
May B. | 310efea675 | |
May B. | ff9942691a | |
May B. | 8d0c9194b0 | |
May B. | a05378c274 | |
May B. | f7b34f74f8 | |
May B. | 5c6ccd48e5 | |
May B. | f75afb5e1e | |
May B. | 7d125b81d1 | |
May B. | 6ace01afca | |
May B. | 21b95ba5fe |
|
@ -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"
|
||||
}
|
||||
|
|
|
@ -4,13 +4,15 @@ 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)
|
||||
endif()
|
||||
|
||||
set(CMAKE_CXX_FLAGS "-Wall -Wextra")
|
||||
# set(CMAKE_CXX_CLANG_TIDY "clang-tidy;-checks=clang-analyzer-*,cppcoreguidelines-*,performance-*,readability-*,-readability-braces-around-statements,-readability-uppercase-literal-suffix")
|
||||
|
||||
find_program(CCACHE_FOUND ccache)
|
||||
if(CCACHE_FOUND)
|
||||
|
@ -44,18 +46,14 @@ set(INCLUDE_LIBS
|
|||
"include/toml++"
|
||||
"include/Remotery/lib"
|
||||
"include/robin_hood"
|
||||
"include/libguarded"
|
||||
)
|
||||
|
||||
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)
|
||||
|
@ -68,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")
|
36
TODO.md
36
TODO.md
|
@ -4,10 +4,9 @@
|
|||
- [x] Generate noise
|
||||
- [x] Density
|
||||
- [x] Robin hood map
|
||||
- [ ] In memory RLE
|
||||
- [ ] Octree world
|
||||
- [ ] Remove density
|
||||
- [x] Serialize
|
||||
- [ ] Variable length encoding
|
||||
- [x] Group files
|
||||
- [x] Zstd + custom grouping
|
||||
- [~] Find best region size
|
||||
|
@ -15,13 +14,21 @@
|
|||
- [x] Low memory: Keep only ifstream
|
||||
- [x] High memory: Save multiple
|
||||
- [x] Unload unused
|
||||
- [ ] In memory RLE
|
||||
- [x] Edition
|
||||
- [ ] Entity
|
||||
- [ ] Planet
|
||||
- [ ] CubicSphere
|
||||
- [ ] Healpix
|
||||
- [ ] Galaxy
|
||||
- [~] Entity
|
||||
- [x] Basic
|
||||
- [ ] Instanced
|
||||
- https://learnopengl.com/Advanced-OpenGL/Instancing
|
||||
- [ ] Inheritance
|
||||
- [ ] ECS
|
||||
- [ ] Relative to area
|
||||
- [x] Area
|
||||
- [x] Offset
|
||||
- [ ] Rotation
|
||||
- [ ] Planet
|
||||
- [ ] CubicSphere
|
||||
- [ ] Healpix
|
||||
- [ ] Galaxy
|
||||
- [ ] Biomes
|
||||
- https://imgur.com/kM8b5Zq
|
||||
- https://imgur.com/a/bh2iy
|
||||
|
@ -30,14 +37,14 @@
|
|||
- Valgrind
|
||||
- Xtree-memory
|
||||
- [ ] sanitizer
|
||||
- [ ] clang-tidy
|
||||
- [ ] cmake
|
||||
- https://github.com/microsoft/GSL/blob/master/include/gsl/pointers
|
||||
- [x] clang-tidy
|
||||
- [ ] clang -fall
|
||||
- [ ] Server
|
||||
- [ ] ZeroMQ
|
||||
- [ ] Mutex guard
|
||||
- [ ] Shared mutex
|
||||
- [ ] Logger
|
||||
- [x] Logger
|
||||
- [ ] FastNoiseSIMD / HastyNoise double precision
|
||||
- [x] Generational identifier
|
||||
- [ ] Limit map usage
|
||||
|
||||
## Rendering
|
||||
- [x] Render triangle
|
||||
|
@ -59,6 +66,7 @@
|
|||
- [ ] Deferred
|
||||
- [ ] Cascaded shadow maps
|
||||
- [ ] RayCast
|
||||
- [x] Float precision problem
|
||||
|
||||
## Contouring
|
||||
- [x] Box contouring
|
||||
|
|
|
@ -6,5 +6,5 @@ layout(location = 0) out vec4 color;
|
|||
in vec4 Color;
|
||||
|
||||
void main(){
|
||||
color = Color;
|
||||
color = vec4(Color.xyz, .5);
|
||||
}
|
|
@ -10,14 +10,14 @@ uniform sampler2DArray HOSAtlas;
|
|||
uniform mat4 View;
|
||||
uniform vec3 FogColor;
|
||||
|
||||
#ifdef BLEND
|
||||
#ifdef GEOMETRY
|
||||
in GeometryData
|
||||
#else
|
||||
in VertexData
|
||||
#endif
|
||||
{
|
||||
vec3 Position_worldspace;
|
||||
#ifdef BLEND
|
||||
vec3 Position_modelspace;
|
||||
#ifdef GEOMETRY
|
||||
flat uint Materials[3];
|
||||
vec3 MaterialRatio;
|
||||
#else
|
||||
|
@ -39,6 +39,7 @@ vec3 expand(vec3 v) {
|
|||
}
|
||||
|
||||
vec4 getTexture(sampler2DArray sample, vec2 UV) {
|
||||
#ifdef GEOMETRY
|
||||
#ifdef BLEND
|
||||
vec4 colx = texture(sample, vec3(UV, vs.Materials[0]));
|
||||
if(vs.Materials[1] == vs.Materials[0]) {
|
||||
|
@ -50,6 +51,12 @@ vec4 getTexture(sampler2DArray sample, vec2 UV) {
|
|||
vs.Materials[2] == vs.Materials[1] ? mix(coly, colx, vs.MaterialRatio.x) :
|
||||
colx * vs.MaterialRatio.x + coly * vs.MaterialRatio.y + texture(sample, vec3(UV, vs.Materials[2])) * vs.MaterialRatio.z);
|
||||
}
|
||||
#else
|
||||
int mainMaterial = vs.MaterialRatio.x >= vs.MaterialRatio.y ?
|
||||
(vs.MaterialRatio.x >= vs.MaterialRatio.z ? 0 : 2) :
|
||||
(vs.MaterialRatio.y >= vs.MaterialRatio.z ? 1 : 2);
|
||||
return texture(sample, vec3(UV, vs.Materials[mainMaterial]));
|
||||
#endif
|
||||
#else
|
||||
return texture(sample, vec3(UV, vs.Material));
|
||||
#endif
|
||||
|
@ -71,9 +78,9 @@ void main() {
|
|||
vec3 blendWeights = abs(vs.FaceNormal_modelspace);
|
||||
blendWeights = blendWeights - plateauSize;
|
||||
blendWeights = pow(max(blendWeights, 0), vec3(transitionSpeed));
|
||||
vec2 UVx = vs.Position_worldspace.yz * texScale;
|
||||
vec2 UVy = vs.Position_worldspace.zx * texScale;
|
||||
vec2 UVz = vs.Position_worldspace.xy * texScale;
|
||||
vec2 UVx = vs.Position_modelspace.yz * texScale;
|
||||
vec2 UVy = vs.Position_modelspace.zx * texScale;
|
||||
vec2 UVz = vs.Position_modelspace.xy * texScale;
|
||||
|
||||
vec3 tex = getTriTexture(TextureAtlas, UVx, UVy, UVz, blendWeights);
|
||||
|
||||
|
@ -95,7 +102,7 @@ void main() {
|
|||
// Cheap planar
|
||||
vec3 blendWeights = abs(vs.FaceNormal_modelspace);
|
||||
vec3 nrm = normalize(pow(blendWeights, vec3(80)));
|
||||
vec2 UV = (vec2(vs.Position_worldspace.xy * nrm.z) + vec2(vs.Position_worldspace.yz * nrm.x) + vec2(vs.Position_worldspace.zx * nrm.y)) * texScale;
|
||||
vec2 UV = (vec2(vs.Position_modelspace.xy * nrm.z) + vec2(vs.Position_modelspace.yz * nrm.x) + vec2(vs.Position_modelspace.zx * nrm.y)) * texScale;
|
||||
|
||||
vec3 tex = getTexture(TextureAtlas, UV).rgb;
|
||||
#ifdef PBR
|
||||
|
|
|
@ -5,7 +5,7 @@ layout (triangles) in;
|
|||
layout (triangle_strip, max_vertices = 3) out;
|
||||
|
||||
in VertexData {
|
||||
vec3 Position_worldspace;
|
||||
vec3 Position_modelspace;
|
||||
flat uint Material;
|
||||
vec3 FaceNormal_modelspace;
|
||||
#ifdef PBR
|
||||
|
@ -19,7 +19,7 @@ in VertexData {
|
|||
} vs_in[];
|
||||
|
||||
out GeometryData {
|
||||
vec3 Position_worldspace;
|
||||
vec3 Position_modelspace;
|
||||
flat uint Materials[3];
|
||||
vec3 MaterialRatio;
|
||||
vec3 FaceNormal_modelspace;
|
||||
|
@ -37,7 +37,7 @@ void main() {
|
|||
for(int i = 0; i < gl_in.length(); i++) {
|
||||
|
||||
gl_Position = gl_in[i].gl_Position;
|
||||
gs.Position_worldspace = vs_in[i].Position_worldspace;
|
||||
gs.Position_modelspace = vs_in[i].Position_modelspace;
|
||||
gs.FaceNormal_modelspace = vs_in[i].FaceNormal_modelspace;
|
||||
gs.FaceNormal_worldspace = vs_in[i].FaceNormal_worldspace;
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@ layout(location = 1) in uint Material_model;
|
|||
layout(location = 2) in vec3 Normal_modelspace;
|
||||
|
||||
out VertexData {
|
||||
vec3 Position_worldspace;
|
||||
vec3 Position_modelspace;
|
||||
flat uint Material;
|
||||
vec3 FaceNormal_modelspace;
|
||||
#ifdef PBR
|
||||
|
@ -27,10 +27,11 @@ uniform float FogDepth;
|
|||
|
||||
void main(){
|
||||
gl_Position = MVP * vec4(Position_modelspace, 1);
|
||||
vs.Position_worldspace = (Model * vec4(Position_modelspace,1)).xyz;
|
||||
vs.Position_modelspace = Position_modelspace;
|
||||
vec3 Position_worldspace = (Model * vec4(Position_modelspace,1)).xyz;
|
||||
|
||||
#ifdef FOG
|
||||
vs.Depth = length((View * vec4(vs.Position_worldspace,1)).xyz) / FogDepth;
|
||||
vs.Depth = length((View * vec4(Position_worldspace,1)).xyz) / FogDepth;
|
||||
#endif
|
||||
|
||||
vs.Material = Material_model;
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
Copyright (c) 2016, Ansel Sermersheim
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
@ -0,0 +1,198 @@
|
|||
/***********************************************************************
|
||||
*
|
||||
* Copyright (c) 2015-2020 Ansel Sermersheim
|
||||
*
|
||||
* This file is part of CsLibGuarded.
|
||||
*
|
||||
* CsLibGuarded is free software, released under the BSD 2-Clause license.
|
||||
* For license details refer to LICENSE provided with this project.
|
||||
*
|
||||
* CopperSpice is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
*
|
||||
* https://opensource.org/licenses/BSD-2-Clause
|
||||
*
|
||||
***********************************************************************/
|
||||
|
||||
#ifndef CSLIBGUARDED_PLAIN_GUARDED_H
|
||||
#define CSLIBGUARDED_PLAIN_GUARDED_H
|
||||
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
|
||||
namespace libguarded
|
||||
{
|
||||
|
||||
/**
|
||||
\headerfile cs_plain_guarded.h <CsLibGuarded/cs_plain_guarded.h>
|
||||
|
||||
This templated class wraps an object and allows only one thread at a
|
||||
time to access the protected object.
|
||||
|
||||
This class will use std::mutex for the internal locking mechanism by
|
||||
default. Other classes which are useful for the mutex type are
|
||||
std::recursive_mutex, std::timed_mutex, and
|
||||
std::recursive_timed_mutex.
|
||||
|
||||
The handle returned by the various lock methods is moveable but not
|
||||
copyable.
|
||||
*/
|
||||
template <typename T, typename M = std::mutex>
|
||||
class plain_guarded
|
||||
{
|
||||
private:
|
||||
class deleter;
|
||||
|
||||
public:
|
||||
using handle = std::unique_ptr<T, deleter>;
|
||||
|
||||
/**
|
||||
Construct a guarded object. This constructor will accept any
|
||||
number of parameters, all of which are forwarded to the
|
||||
constructor of T.
|
||||
*/
|
||||
template <typename... Us>
|
||||
plain_guarded(Us &&... data);
|
||||
|
||||
/**
|
||||
Acquire a handle to the protected object. As a side effect, the
|
||||
protected object will be locked from access by any other
|
||||
thread. The lock will be automatically released when the handle
|
||||
is destroyed.
|
||||
*/
|
||||
[[nodiscard]] handle lock();
|
||||
|
||||
/**
|
||||
Attempt to acquire a handle to the protected object. Returns a
|
||||
null handle if the object is already locked. As a side effect,
|
||||
the protected object will be locked from access by any other
|
||||
thread. The lock will be automatically released when the handle
|
||||
is destroyed.
|
||||
*/
|
||||
[[nodiscard]] handle try_lock();
|
||||
|
||||
/**
|
||||
Attempt to acquire a handle to the protected object. As a side
|
||||
effect, the protected object will be locked from access by any
|
||||
other thread. The lock will be automatically released when the
|
||||
handle is destroyed.
|
||||
|
||||
Returns a null handle if the object is already locked, and does
|
||||
not become available for locking before the time duration has
|
||||
elapsed.
|
||||
|
||||
Calling this method requires that the underlying mutex type M
|
||||
supports the try_lock_for method. This is not true if M is the
|
||||
default std::mutex.
|
||||
*/
|
||||
template <class Duration>
|
||||
[[nodiscard]] handle try_lock_for(const Duration &duration);
|
||||
|
||||
/**
|
||||
Attempt to acquire a handle to the protected object. As a side
|
||||
effect, the protected object will be locked from access by any other
|
||||
thread. The lock will be automatically released when the handle is
|
||||
destroyed.
|
||||
|
||||
Returns a null handle if the object is already locked, and does not
|
||||
become available for locking before reaching the specified timepoint.
|
||||
|
||||
Calling this method requires that the underlying mutex type M
|
||||
supports the try_lock_until method. This is not true if M is the
|
||||
default std::mutex.
|
||||
*/
|
||||
template <class TimePoint>
|
||||
[[nodiscard]] handle try_lock_until(const TimePoint &timepoint);
|
||||
|
||||
private:
|
||||
T m_obj;
|
||||
M m_mutex;
|
||||
};
|
||||
|
||||
template <typename T, typename M>
|
||||
class plain_guarded<T, M>::deleter
|
||||
{
|
||||
public:
|
||||
using pointer = T *;
|
||||
|
||||
deleter(std::unique_lock<M> lock);
|
||||
|
||||
void operator()(T *ptr);
|
||||
|
||||
private:
|
||||
std::unique_lock<M> m_lock;
|
||||
};
|
||||
|
||||
template <typename T, typename M>
|
||||
plain_guarded<T, M>::deleter::deleter(std::unique_lock<M> lock)
|
||||
: m_lock(std::move(lock))
|
||||
{
|
||||
}
|
||||
|
||||
template <typename T, typename M>
|
||||
void plain_guarded<T, M>::deleter::operator()(T *)
|
||||
{
|
||||
if (m_lock.owns_lock()) {
|
||||
m_lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, typename M>
|
||||
template <typename... Us>
|
||||
plain_guarded<T, M>::plain_guarded(Us &&... data)
|
||||
: m_obj(std::forward<Us>(data)...)
|
||||
{
|
||||
}
|
||||
|
||||
template <typename T, typename M>
|
||||
auto plain_guarded<T, M>::lock() -> handle
|
||||
{
|
||||
std::unique_lock<M> lock(m_mutex);
|
||||
return handle(&m_obj, deleter(std::move(lock)));
|
||||
}
|
||||
|
||||
template <typename T, typename M>
|
||||
auto plain_guarded<T, M>::try_lock() -> handle
|
||||
{
|
||||
std::unique_lock<M> lock(m_mutex, std::try_to_lock);
|
||||
|
||||
if (lock.owns_lock()) {
|
||||
return handle(&m_obj, deleter(std::move(lock)));
|
||||
} else {
|
||||
return handle(nullptr, deleter(std::move(lock)));
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, typename M>
|
||||
template <typename Duration>
|
||||
auto plain_guarded<T, M>::try_lock_for(const Duration &d) -> handle
|
||||
{
|
||||
std::unique_lock<M> lock(m_mutex, d);
|
||||
|
||||
if (lock.owns_lock()) {
|
||||
return handle(&m_obj, deleter(std::move(lock)));
|
||||
} else {
|
||||
return handle(nullptr, deleter(std::move(lock)));
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, typename M>
|
||||
template <typename TimePoint>
|
||||
auto plain_guarded<T, M>::try_lock_until(const TimePoint &tp) -> handle
|
||||
{
|
||||
std::unique_lock<M> lock(m_mutex, tp);
|
||||
|
||||
if (lock.owns_lock()) {
|
||||
return handle(&m_obj, deleter(std::move(lock)));
|
||||
} else {
|
||||
return handle(nullptr, deleter(std::move(lock)));
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, typename M = std::mutex>
|
||||
using guarded [[deprecated("renamed to plain_guarded")]] = plain_guarded<T, M>;
|
||||
|
||||
} // namespace libguarded
|
||||
|
||||
#endif
|
|
@ -0,0 +1,234 @@
|
|||
/***********************************************************************
|
||||
*
|
||||
* Copyright (c) 2015-2020 Ansel Sermersheim
|
||||
*
|
||||
* This file is part of CsLibGuarded.
|
||||
*
|
||||
* CsLibGuarded is free software, released under the BSD 2-Clause license.
|
||||
* For license details refer to LICENSE provided with this project.
|
||||
*
|
||||
* CopperSpice is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
*
|
||||
* https://opensource.org/licenses/BSD-2-Clause
|
||||
*
|
||||
***********************************************************************/
|
||||
|
||||
#ifndef CSLIBGUARDED_SHARED_GUARDED_H
|
||||
#define CSLIBGUARDED_SHARED_GUARDED_H
|
||||
|
||||
#include <memory>
|
||||
#include <shared_mutex>
|
||||
|
||||
namespace libguarded
|
||||
{
|
||||
|
||||
/**
|
||||
\headerfile cs_shared_guarded.h <CsLibGuarded/cs_shared_guarded.h>
|
||||
|
||||
This templated class wraps an object and allows only one thread at
|
||||
a time to modify the protected object.
|
||||
|
||||
This class will use std::shared_timed_mutex for the internal
|
||||
locking mechanism by default. In C++17 the std::shared_mutex class
|
||||
is also available.
|
||||
|
||||
The handle returned by the various lock methods is moveable but not
|
||||
copyable.
|
||||
*/
|
||||
template <typename T, typename M = std::shared_timed_mutex, typename L = std::shared_lock<M>>
|
||||
class shared_guarded
|
||||
{
|
||||
private:
|
||||
class deleter;
|
||||
class shared_deleter;
|
||||
|
||||
public:
|
||||
using handle = std::unique_ptr<T, deleter>;
|
||||
using shared_handle = std::unique_ptr<const T, shared_deleter>;
|
||||
|
||||
template <typename... Us>
|
||||
shared_guarded(Us &&... data);
|
||||
|
||||
// exclusive access
|
||||
[[nodiscard]] handle lock();
|
||||
[[nodiscard]] handle try_lock();
|
||||
|
||||
template <class Duration>
|
||||
[[nodiscard]] handle try_lock_for(const Duration &duration);
|
||||
|
||||
template <class TimePoint>
|
||||
[[nodiscard]] handle try_lock_until(const TimePoint &timepoint);
|
||||
|
||||
// shared access, note "shared" in method names
|
||||
[[nodiscard]] shared_handle lock_shared() const;
|
||||
[[nodiscard]] shared_handle try_lock_shared() const;
|
||||
|
||||
template <class Duration>
|
||||
[[nodiscard]] shared_handle try_lock_shared_for(const Duration &duration) const;
|
||||
|
||||
template <class TimePoint>
|
||||
[[nodiscard]] shared_handle try_lock_shared_until(const TimePoint &timepoint) const;
|
||||
|
||||
private:
|
||||
T m_obj;
|
||||
mutable M m_mutex;
|
||||
};
|
||||
|
||||
template <typename T, typename M, typename L>
|
||||
class shared_guarded<T, M, L>::deleter
|
||||
{
|
||||
public:
|
||||
using pointer = T *;
|
||||
|
||||
deleter(std::unique_lock<M> lock);
|
||||
|
||||
void operator()(T *ptr);
|
||||
|
||||
private:
|
||||
std::unique_lock<M> m_lock;
|
||||
};
|
||||
|
||||
template <typename T, typename M, typename L>
|
||||
shared_guarded<T, M, L>::deleter::deleter(std::unique_lock<M> lock)
|
||||
: m_lock(std::move(lock))
|
||||
{
|
||||
}
|
||||
|
||||
template <typename T, typename M, typename L>
|
||||
void shared_guarded<T, M, L>::deleter::operator()(T *)
|
||||
{
|
||||
if (m_lock.owns_lock()) {
|
||||
m_lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, typename M, typename L>
|
||||
class shared_guarded<T, M, L>::shared_deleter
|
||||
{
|
||||
public:
|
||||
using pointer = const T *;
|
||||
|
||||
shared_deleter(L lock);
|
||||
|
||||
void operator()(const T *ptr);
|
||||
|
||||
private:
|
||||
L m_lock;
|
||||
};
|
||||
|
||||
template <typename T, typename M, typename L>
|
||||
shared_guarded<T, M, L>::shared_deleter::shared_deleter(L lock)
|
||||
: m_lock(std::move(lock))
|
||||
{
|
||||
}
|
||||
|
||||
template <typename T, typename M, typename L>
|
||||
void shared_guarded<T, M, L>::shared_deleter::operator()(const T *)
|
||||
{
|
||||
if (m_lock.owns_lock()) {
|
||||
m_lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, typename M, typename L>
|
||||
template <typename... Us>
|
||||
shared_guarded<T, M, L>::shared_guarded(Us &&... data)
|
||||
: m_obj(std::forward<Us>(data)...)
|
||||
{
|
||||
}
|
||||
|
||||
template <typename T, typename M, typename L>
|
||||
auto shared_guarded<T, M, L>::lock() -> handle
|
||||
{
|
||||
std::unique_lock<M> lock(m_mutex);
|
||||
return handle(&m_obj, deleter(std::move(lock)));
|
||||
}
|
||||
|
||||
template <typename T, typename M, typename L>
|
||||
auto shared_guarded<T, M, L>::try_lock() -> handle
|
||||
{
|
||||
std::unique_lock<M> lock(m_mutex, std::try_to_lock);
|
||||
|
||||
if (lock.owns_lock()) {
|
||||
return handle(&m_obj, deleter(std::move(lock)));
|
||||
} else {
|
||||
return handle(nullptr, deleter(std::move(lock)));
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, typename M, typename L>
|
||||
template <typename Duration>
|
||||
auto shared_guarded<T, M, L>::try_lock_for(const Duration &duration) -> handle
|
||||
{
|
||||
std::unique_lock<M> lock(m_mutex, duration);
|
||||
|
||||
if (lock.owns_lock()) {
|
||||
return handle(&m_obj, deleter(std::move(lock)));
|
||||
} else {
|
||||
return handle(nullptr, deleter(std::move(lock)));
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, typename M, typename L>
|
||||
template <typename TimePoint>
|
||||
auto shared_guarded<T, M, L>::try_lock_until(const TimePoint &timepoint) -> handle
|
||||
{
|
||||
std::unique_lock<M> lock(m_mutex, timepoint);
|
||||
|
||||
if (lock.owns_lock()) {
|
||||
return handle(&m_obj, deleter(std::move(lock)));
|
||||
} else {
|
||||
return handle(nullptr, deleter(std::move(lock)));
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, typename M, typename L>
|
||||
auto shared_guarded<T, M, L>::lock_shared() const -> shared_handle
|
||||
{
|
||||
L lock(m_mutex);
|
||||
return shared_handle(&m_obj, shared_deleter(std::move(lock)));
|
||||
}
|
||||
|
||||
template <typename T, typename M, typename L>
|
||||
auto shared_guarded<T, M, L>::try_lock_shared() const -> shared_handle
|
||||
{
|
||||
L lock(m_mutex, std::try_to_lock);
|
||||
|
||||
if (lock.owns_lock()) {
|
||||
return shared_handle(&m_obj, shared_deleter(std::move(lock)));
|
||||
} else {
|
||||
return shared_handle(nullptr, shared_deleter(std::move(lock)));
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, typename M, typename L>
|
||||
template <typename Duration>
|
||||
auto shared_guarded<T, M, L>::try_lock_shared_for(const Duration &d) const -> shared_handle
|
||||
{
|
||||
L lock(m_mutex, d);
|
||||
|
||||
if (lock.owns_lock()) {
|
||||
return shared_handle(&m_obj, shared_deleter(std::move(lock)));
|
||||
} else {
|
||||
return shared_handle(nullptr, shared_deleter(std::move(lock)));
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, typename M, typename L>
|
||||
template <typename TimePoint>
|
||||
auto shared_guarded<T, M, L>::try_lock_shared_until(const TimePoint &tp) const -> shared_handle
|
||||
{
|
||||
L lock(m_mutex, tp);
|
||||
|
||||
if (lock.owns_lock()) {
|
||||
return shared_handle(&m_obj, shared_deleter(std::move(lock)));
|
||||
} else {
|
||||
return shared_handle(nullptr, shared_deleter(std::move(lock)));
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace libguarded
|
||||
|
||||
#endif
|
|
@ -1,32 +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 <iostream>
|
||||
#include <fstream>
|
||||
#include <ctime>
|
||||
#include <filesystem>
|
||||
|
||||
/// Entry point
|
||||
int main(int, char *[])
|
||||
{
|
||||
std::srand(std::time(nullptr));
|
||||
world::Generator generator(std::rand());
|
||||
std::filesystem::create_directories("samples");
|
||||
for (size_t i = 0; i < 10000; 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;
|
||||
}
|
|
@ -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,20 +15,23 @@ 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, const world::area_map &areas) = 0;
|
||||
|
||||
/// Chunk data change
|
||||
virtual void onUpdate(const chunk_pos &pos, const world::chunk_map& data, geometry::Faces neighbors) = 0;
|
||||
/// @param offset priority position offset
|
||||
virtual void onUpdate(const area_<chunk_pos> &pos, const chunk_pos &offset, const world::ChunkContainer &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;
|
||||
virtual void onNotify(const area_<chunk_pos> &pos, const chunk_pos &offset, const world::ChunkContainer &data) = 0;
|
||||
/// Display ImGui config
|
||||
virtual void onGui() = 0;
|
||||
/// Get options
|
||||
virtual std::string getOptions() = 0;
|
||||
virtual std::string getOptions() const = 0;
|
||||
// Get camera recommended far range
|
||||
virtual std::pair<float, float> getFarRange() const = 0;
|
||||
|
||||
/// 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;
|
||||
};
|
||||
}
|
|
@ -1,23 +1,42 @@
|
|||
#include "AbstractFlat.hpp"
|
||||
|
||||
#include <imgui.h>
|
||||
#include <imgui.h> // NOLINT
|
||||
#include <toml.h>
|
||||
#include "../world/Chunk.hpp"
|
||||
#include "../world/Area.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;
|
||||
size_t AbstractFlat::clear(const voxel_pos& pos, const world::area_map& areas) {
|
||||
size_t buffer_count = 0;
|
||||
auto it_a = buffers.begin();
|
||||
while (it_a != buffers.end()) { // Remove out of range buffers
|
||||
if (const auto area = areas.find(it_a->first); area != areas.end()) {
|
||||
//Update
|
||||
it_a->second.first = area->second->getOffset();
|
||||
const auto center = glm::divide(pos - it_a->second.first.as_voxel());
|
||||
auto &bfs = it_a->second.second;
|
||||
auto it = bfs.begin();
|
||||
while(it != bfs.end()) {
|
||||
if (glm::length2(center - it->first) > glm::pow2(keepDistance)) {
|
||||
if(it->second != NULL)
|
||||
delete it->second;
|
||||
|
||||
it = buffers.erase(it);
|
||||
it = bfs.erase(it);
|
||||
} else {
|
||||
buffer_count++;
|
||||
++it;
|
||||
}
|
||||
}
|
||||
++it_a;
|
||||
} else {
|
||||
for(auto& buffer: it_a->second.second) {
|
||||
if(buffer.second != NULL)
|
||||
delete buffer.second;
|
||||
}
|
||||
it_a = buffers.erase(it_a);
|
||||
}
|
||||
}
|
||||
return buffer_count;
|
||||
}
|
||||
|
||||
AbstractFlat::AbstractFlat(const std::string& str): Abstract() {
|
||||
|
@ -25,7 +44,7 @@ namespace contouring {
|
|||
loadDistance = opt["load_distance"].value_or(loadDistance);
|
||||
keepDistance = opt["keep_distance"].value_or(keepDistance);
|
||||
}
|
||||
std::string AbstractFlat::getOptions() {
|
||||
std::string AbstractFlat::getOptions() const {
|
||||
std::ostringstream ss;
|
||||
ss << toml::table({
|
||||
{"load_distance", loadDistance},
|
||||
|
@ -33,17 +52,22 @@ namespace contouring {
|
|||
});
|
||||
return ss.str();
|
||||
}
|
||||
std::pair<float, float> AbstractFlat::getFarRange() const {
|
||||
return std::make_pair((loadDistance - 1.5) * CHUNK_LENGTH, (keepDistance + .5) * CHUNK_LENGTH);
|
||||
}
|
||||
|
||||
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::Abstract *const>> &out, const std::optional<geometry::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(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);
|
||||
}
|
||||
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 [_, area] : buffers) {
|
||||
for (const auto [pos, buffer] : area.second) {
|
||||
const glm::vec3 fPos = (glm::vec3(area.first.raw_as_long() + glm::multiply(pos) - offset * glm::llvec3(density)) + area.first.offset) / 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);
|
||||
}}
|
||||
}
|
||||
}
|
|
@ -10,28 +10,20 @@ namespace contouring {
|
|||
AbstractFlat(const std::string &str);
|
||||
virtual ~AbstractFlat() {}
|
||||
|
||||
/// Each frame ping. Used to clear out of range
|
||||
void update(const camera_pos &) override;
|
||||
|
||||
/// Display ImGui config
|
||||
void onGui() override;
|
||||
|
||||
std::string getOptions() override;
|
||||
std::string getOptions() const override;
|
||||
std::pair<float, float> getFarRange() const override;
|
||||
|
||||
/// 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 {
|
||||
return glm::length2(center - point) <= loadDistance * loadDistance;
|
||||
}
|
||||
bool inline inKeepRange(const chunk_pos& point) const {
|
||||
return glm::length2(center - point) <= keepDistance * keepDistance;
|
||||
}
|
||||
size_t clear(const voxel_pos &, const world::area_map &areas);
|
||||
|
||||
robin_hood::unordered_map<chunk_pos, buffer::Abstract *> buffers;
|
||||
chunk_pos center = chunk_pos(INT_MAX);
|
||||
robin_hood::unordered_map<area_id, robin_hood::pair<area_pos, robin_hood::unordered_map<chunk_pos, buffer::Abstract *>>> buffers;
|
||||
|
||||
int loadDistance = 3;
|
||||
int keepDistance = 4;
|
||||
|
|
|
@ -9,11 +9,12 @@ namespace contouring {
|
|||
Dummy(): Abstract() { }
|
||||
virtual ~Dummy() { }
|
||||
|
||||
void update(const camera_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 update(const voxel_pos &, const world::area_map &) override {}
|
||||
void onUpdate(const area_<chunk_pos> &, const chunk_pos &, const world::ChunkContainer &, geometry::Faces) override {}
|
||||
void onNotify(const area_<chunk_pos> &, const chunk_pos &, const world::ChunkContainer &) 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 { }
|
||||
std::string getOptions() const override { return ""; }
|
||||
std::pair<float, float> getFarRange() const override { return std::make_pair(0, 0); }
|
||||
void getModels(std::vector<std::pair<glm::mat4, buffer::Abstract *const>> &, const std::optional<geometry::Frustum>&, const glm::llvec3&, int) override { }
|
||||
};
|
||||
}
|
|
@ -2,8 +2,8 @@
|
|||
|
||||
#include "../world/Chunk.hpp"
|
||||
#include "../world/materials.hpp"
|
||||
#include <Remotery.h>
|
||||
#include <imgui.h>
|
||||
#include <Remotery.h> // NOLINT
|
||||
#include <imgui.h> // NOLINT
|
||||
#include <toml.h>
|
||||
#include "dualmc.h"
|
||||
|
||||
|
@ -16,7 +16,7 @@ namespace contouring {
|
|||
for (size_t i = 1; i <= 2; i++) {
|
||||
workers.emplace_back([&] {
|
||||
while (running) {
|
||||
std::pair<chunk_pos, surrounding::corners> ctx;
|
||||
std::pair<area_<chunk_pos>, surrounding::corners> ctx;
|
||||
loadQueue.wait();
|
||||
if (loadQueue.pop(ctx)) {
|
||||
rmt_ScopedCPUSample(ProcessContouring, 0);
|
||||
|
@ -30,7 +30,7 @@ namespace contouring {
|
|||
}
|
||||
FlatDualMC::~FlatDualMC() {
|
||||
running = false;
|
||||
loadQueue.notify();
|
||||
loadQueue.notify_all();
|
||||
|
||||
for(auto& worker: workers) {
|
||||
if (worker.joinable())
|
||||
|
@ -38,7 +38,7 @@ namespace contouring {
|
|||
}
|
||||
}
|
||||
|
||||
std::string FlatDualMC::getOptions() {
|
||||
std::string FlatDualMC::getOptions() const {
|
||||
std::ostringstream ss;
|
||||
ss << toml::table({
|
||||
{"load_distance", loadDistance},
|
||||
|
@ -49,51 +49,52 @@ namespace contouring {
|
|||
return ss.str();
|
||||
}
|
||||
|
||||
void FlatDualMC::enqueue(const chunk_pos &pos, const world::chunk_map &data) {
|
||||
void FlatDualMC::enqueue(const area_<chunk_pos> &pos, const chunk_pos &offset, const world::ChunkContainer &data) {
|
||||
rmt_ScopedCPUSample(EnqueueContouring, RMTSF_Aggregate);
|
||||
const auto dist2 = glm::length2(pos - center);
|
||||
const auto dist2 = glm::length2(offset - pos.second);
|
||||
if (dist2 <= loadDistance * loadDistance) {
|
||||
surrounding::corners surrounding;
|
||||
if(surrounding::load(surrounding, pos, data)) {
|
||||
if(surrounding::load(surrounding, pos.second, data)) {
|
||||
loadQueue.push(pos, surrounding, -dist2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FlatDualMC::onUpdate(const chunk_pos &pos, const world::chunk_map &data, geometry::Faces neighbors) {
|
||||
enqueue(pos, data);
|
||||
void FlatDualMC::onUpdate(const area_<chunk_pos> &pos, const chunk_pos &offset, const world::ChunkContainer &data, geometry::Faces neighbors) {
|
||||
enqueue(pos, offset, data);
|
||||
if (neighbors && (geometry::Faces::Left | geometry::Faces::Down | geometry::Faces::Backward)) {
|
||||
for (size_t i = 1; i < 8; i++) {
|
||||
enqueue(pos - surrounding::g_corner_offsets[i], data);
|
||||
enqueue(std::make_pair(pos.first, pos.second - surrounding::g_corner_offsets[i]), offset, data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FlatDualMC::onNotify(const chunk_pos &pos, const world::chunk_map &data) {
|
||||
if (buffers.find(pos) == buffers.end()) {
|
||||
enqueue(pos, data);
|
||||
void FlatDualMC::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 FlatDualMC::update(const camera_pos& pos) {
|
||||
AbstractFlat::update(pos);
|
||||
std::pair<chunk_pos, buffer::ShortIndexed::Data> out;
|
||||
void FlatDualMC::update(const voxel_pos& pos, const world::area_map& areas) {
|
||||
std::pair<area_<chunk_pos>, buffer::ShortIndexed::Data> out;
|
||||
reports.load.push(loadQueue.size());
|
||||
//MAYBE: clear out of range loadQueue.trim(keepDistance * keepDistance)
|
||||
reports.loaded.push(loadedQueue.size());
|
||||
while(loadedQueue.pop(out)) {
|
||||
const auto buffer = new buffer::ShortIndexed(GL_TRIANGLES, out.second);
|
||||
const auto it = buffers.find(out.first);
|
||||
if (it != buffers.end()) {
|
||||
auto &bfs = buffers[out.first.first].second; //NOTE: buffer.first uninitialized (will be set in clear())
|
||||
if (const auto it = bfs.find(out.first.second); it != bfs.end()) {
|
||||
if(it->second != NULL)
|
||||
delete it->second;
|
||||
|
||||
it->second = buffer;
|
||||
} else {
|
||||
buffers.emplace(out.first, buffer);
|
||||
bfs.emplace(out.first.second, buffer);
|
||||
}
|
||||
}
|
||||
reports.count.push(buffers.size());
|
||||
size_t count = AbstractFlat::clear(pos, areas);
|
||||
reports.count.push(count);
|
||||
}
|
||||
|
||||
void FlatDualMC::onGui() {
|
||||
|
@ -109,15 +110,16 @@ namespace contouring {
|
|||
|
||||
void FlatDualMC::render(const surrounding::corners &surrounding, buffer::ShortIndexed::Data &out) const {
|
||||
const int SIZE = CHUNK_LENGTH + 3;
|
||||
std::vector<dualmc::DualMC<float>::Point> grid;
|
||||
std::array<dualmc::DualMC<float>::Point, SIZE * SIZE * SIZE> grid;
|
||||
{
|
||||
grid.reserve(SIZE * SIZE * SIZE);
|
||||
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->getAt(chunk_voxel_pos(x % CHUNK_LENGTH, y % CHUNK_LENGTH, z % CHUNK_LENGTH));
|
||||
grid.emplace_back(voxel.Density * 1.f / UCHAR_MAX, voxel.Material);
|
||||
const auto &voxel = chunk->get(glm::toIdx(x % CHUNK_LENGTH, y % CHUNK_LENGTH, z % CHUNK_LENGTH));
|
||||
cell.x = voxel.density() * 1.f / world::Voxel::DENSITY_MAX;
|
||||
cell.w = voxel.material();
|
||||
}}}
|
||||
}
|
||||
{
|
||||
|
|
|
@ -9,8 +9,6 @@
|
|||
#include "../render/buffer/ShortIndexed.hpp"
|
||||
#include <thread>
|
||||
|
||||
#define REPORT_BUFFER_SIZE 128
|
||||
|
||||
using namespace data;
|
||||
namespace contouring {
|
||||
/// Dual Marching Cube 1:1 contouring
|
||||
|
@ -19,32 +17,32 @@ namespace contouring {
|
|||
FlatDualMC(const std::string&);
|
||||
virtual ~FlatDualMC();
|
||||
|
||||
void update(const camera_pos&) override;
|
||||
void update(const voxel_pos&, const world::area_map&) override;
|
||||
|
||||
void onGui() override;
|
||||
|
||||
std::string getOptions() override;
|
||||
std::string getOptions() const override;
|
||||
|
||||
/// Chunk data change
|
||||
void onUpdate(const chunk_pos &, const world::chunk_map &, geometry::Faces) override;
|
||||
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 chunk_pos &, const world::chunk_map &) override;
|
||||
void onNotify(const area_<chunk_pos> &, const chunk_pos &, const world::ChunkContainer &) override;
|
||||
|
||||
protected:
|
||||
safe_priority_queue_map<chunk_pos, surrounding::corners, int> loadQueue;
|
||||
safe_queue<std::pair<chunk_pos, buffer::ShortIndexed::Data>> loadedQueue;
|
||||
safe_priority_queue_map<area_<chunk_pos>, surrounding::corners, int, area_hash> loadQueue;
|
||||
safe_queue<std::pair<area_<chunk_pos>, buffer::ShortIndexed::Data>> loadedQueue;
|
||||
|
||||
struct report {
|
||||
circular_buffer<float> count = circular_buffer<float>(REPORT_BUFFER_SIZE, 0); // MAYBE: store int
|
||||
circular_buffer<float> load = circular_buffer<float>(REPORT_BUFFER_SIZE, 0);
|
||||
circular_buffer<float> loaded = circular_buffer<float>(REPORT_BUFFER_SIZE, 0);
|
||||
report_buffer count;
|
||||
report_buffer load;
|
||||
report_buffer loaded;
|
||||
} reports;
|
||||
|
||||
bool running = true;
|
||||
std::vector<std::thread> workers;
|
||||
|
||||
void enqueue(const chunk_pos &, const world::chunk_map &);
|
||||
void enqueue(const area_<chunk_pos> &, const chunk_pos &offset, const world::ChunkContainer &);
|
||||
|
||||
float iso = .1f;
|
||||
bool manifold = true;
|
||||
|
|
|
@ -2,8 +2,8 @@
|
|||
|
||||
#include "boxing.hpp"
|
||||
#include "../world/Chunk.hpp"
|
||||
#include <Remotery.h>
|
||||
#include <imgui.h>
|
||||
#include <Remotery.h> // NOLINT
|
||||
#include <imgui.h> // NOLINT
|
||||
|
||||
using namespace geometry;
|
||||
namespace contouring {
|
||||
|
@ -11,7 +11,7 @@ namespace contouring {
|
|||
for (size_t i = 1; i <= 4; i++) {
|
||||
workers.emplace_back([&] {
|
||||
while (running) {
|
||||
std::pair<chunk_pos, surrounding::faces> ctx;
|
||||
std::pair<area_<chunk_pos>, surrounding::faces> ctx;
|
||||
loadQueue.wait();
|
||||
if (loadQueue.pop(ctx)) {
|
||||
rmt_ScopedCPUSample(ProcessContouring, 0);
|
||||
|
@ -28,7 +28,7 @@ namespace contouring {
|
|||
}
|
||||
FlatSurroundingBox::~FlatSurroundingBox() {
|
||||
running = false;
|
||||
loadQueue.notify();
|
||||
loadQueue.notify_all();
|
||||
|
||||
for(auto& worker: workers) {
|
||||
if (worker.joinable())
|
||||
|
@ -36,63 +36,64 @@ namespace contouring {
|
|||
}
|
||||
}
|
||||
|
||||
void FlatSurroundingBox::enqueue(const chunk_pos &pos, const world::chunk_map &data) {
|
||||
void FlatSurroundingBox::enqueue(const area_<chunk_pos> &pos, const chunk_pos& offset, const world::ChunkContainer &data) {
|
||||
rmt_ScopedCPUSample(EnqueueContouring, RMTSF_Aggregate);
|
||||
const auto dist2 = glm::length2(pos - center);
|
||||
const auto dist2 = glm::length2(offset - pos.second);
|
||||
if (dist2 <= loadDistance * loadDistance) {
|
||||
surrounding::faces surrounding;
|
||||
if(surrounding::load(surrounding, pos, data)) {
|
||||
if(surrounding::load(surrounding, pos.second, data)) {
|
||||
loadQueue.push(pos, surrounding, -dist2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FlatSurroundingBox::onUpdate(const chunk_pos &pos, const world::chunk_map &data, Faces neighbors) {
|
||||
enqueue(pos, data);
|
||||
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(pos + g_face_offsets[static_cast<int>(Face::Right)], data);
|
||||
enqueue(std::make_pair(pos.first, pos.second + g_face_offsets[static_cast<int>(Face::Right)]), offset, data);
|
||||
|
||||
if (neighbors && Faces::Left)
|
||||
enqueue(pos + g_face_offsets[static_cast<int>(Face::Left)], data);
|
||||
enqueue(std::make_pair(pos.first, pos.second + g_face_offsets[static_cast<int>(Face::Left)]), offset, data);
|
||||
|
||||
if (neighbors && Faces::Up)
|
||||
enqueue(pos + g_face_offsets[static_cast<int>(Face::Up)], data);
|
||||
enqueue(std::make_pair(pos.first, pos.second + g_face_offsets[static_cast<int>(Face::Up)]), offset, data);
|
||||
|
||||
if (neighbors && Faces::Down)
|
||||
enqueue(pos + g_face_offsets[static_cast<int>(Face::Down)], data);
|
||||
enqueue(std::make_pair(pos.first, pos.second + g_face_offsets[static_cast<int>(Face::Down)]), offset, data);
|
||||
|
||||
if (neighbors && Faces::Forward)
|
||||
enqueue(pos + g_face_offsets[static_cast<int>(Face::Forward)], data);
|
||||
enqueue(std::make_pair(pos.first, pos.second + g_face_offsets[static_cast<int>(Face::Forward)]), offset, data);
|
||||
|
||||
if (neighbors && Faces::Backward)
|
||||
enqueue(pos + g_face_offsets[static_cast<int>(Face::Backward)], data);
|
||||
enqueue(std::make_pair(pos.first, pos.second + g_face_offsets[static_cast<int>(Face::Backward)]), offset, data);
|
||||
}
|
||||
|
||||
void FlatSurroundingBox::onNotify(const chunk_pos &pos, const world::chunk_map &data) {
|
||||
if (buffers.find(pos) == buffers.end()) {
|
||||
enqueue(pos, 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 camera_pos& pos) {
|
||||
AbstractFlat::update(pos);
|
||||
std::pair<chunk_pos, buffer::ShortIndexed::Data> out;
|
||||
void FlatSurroundingBox::update(const voxel_pos& pos, const world::area_map& areas) {
|
||||
std::pair<area_<chunk_pos>, buffer::ShortIndexed::Data> out;
|
||||
reports.load.push(loadQueue.size());
|
||||
//MAYBE: clear out of range loadQueue.trim(keepDistance * keepDistance)
|
||||
reports.loaded.push(loadedQueue.size());
|
||||
while(loadedQueue.pop(out)) {
|
||||
const auto buffer = new buffer::ShortIndexed(GL_TRIANGLES, out.second);
|
||||
const auto it = buffers.find(out.first);
|
||||
if (it != buffers.end()) {
|
||||
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 {
|
||||
buffers.emplace(out.first, buffer);
|
||||
bfs.emplace(out.first.second, buffer);
|
||||
}
|
||||
}
|
||||
reports.count.push(buffers.size());
|
||||
size_t count = AbstractFlat::clear(pos, areas);
|
||||
reports.count.push(count);
|
||||
}
|
||||
|
||||
void FlatSurroundingBox::onGui() {
|
||||
|
@ -104,22 +105,22 @@ namespace contouring {
|
|||
}
|
||||
|
||||
bool FlatSurroundingBox::isTransparent(const surrounding::faces &surrounding, const std::pair<ushort, ushort> &idx) {
|
||||
return surrounding[idx.first]->get(idx.second).Density < UCHAR_MAX; // MAYBE: materials::transparent
|
||||
return surrounding[idx.first]->get(idx.second).density() < world::Voxel::DENSITY_MAX; // MAYBE: materials::transparent
|
||||
}
|
||||
|
||||
void FlatSurroundingBox::render(const surrounding::faces &surrounding, std::vector<buffer::VertexData> &vertices) {
|
||||
const auto center = surrounding[surrounding::CENTER];
|
||||
vertices.clear();
|
||||
for (ushort i = 0; i < CHUNK_SIZE; i++) {
|
||||
if (center->get(i).Density > 0) {
|
||||
Faces faces = center->get(i).Density < UCHAR_MAX ? Faces::All :
|
||||
for (ushort i = 0; i < world::CHUNK_SIZE; i++) {
|
||||
if (center->get(i).density() > 0) {
|
||||
Faces faces = center->get(i).density() < world::Voxel::DENSITY_MAX ? 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, world::Chunk::getPosition(i), center->get(i).Material, faces, glm::vec3(center->get(i).Density * 1.f / UCHAR_MAX));
|
||||
box::addCube(vertices, glm::fromIdx(i), center->get(i).material(), faces, glm::vec3(center->get(i).density() * 1.f / world::Voxel::DENSITY_MAX));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,8 +9,6 @@
|
|||
#include "../render/buffer/ShortIndexed.hpp"
|
||||
#include <thread>
|
||||
|
||||
#define REPORT_BUFFER_SIZE 128
|
||||
|
||||
using namespace data;
|
||||
namespace contouring {
|
||||
/// Stupid cubes 1:1 contouring
|
||||
|
@ -19,30 +17,30 @@ namespace contouring {
|
|||
FlatSurroundingBox(const std::string&);
|
||||
virtual ~FlatSurroundingBox();
|
||||
|
||||
void update(const camera_pos&) override;
|
||||
void update(const voxel_pos&, const world::area_map&) override;
|
||||
|
||||
void onGui() override;
|
||||
|
||||
/// Chunk data change
|
||||
void onUpdate(const chunk_pos &, const world::chunk_map &, geometry::Faces) override;
|
||||
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 chunk_pos &, const world::chunk_map &) override;
|
||||
void onNotify(const area_<chunk_pos> &, const chunk_pos &, const world::ChunkContainer &) override;
|
||||
|
||||
protected:
|
||||
safe_priority_queue_map<chunk_pos, surrounding::faces, int> loadQueue;
|
||||
safe_queue<std::pair<chunk_pos, buffer::ShortIndexed::Data>> loadedQueue;
|
||||
safe_priority_queue_map<area_<chunk_pos>, surrounding::faces, int, area_hash> loadQueue;
|
||||
safe_queue<std::pair<area_<chunk_pos>, buffer::ShortIndexed::Data>> loadedQueue;
|
||||
|
||||
struct report {
|
||||
circular_buffer<float> count = circular_buffer<float>(REPORT_BUFFER_SIZE, 0); // MAYBE: store int
|
||||
circular_buffer<float> load = circular_buffer<float>(REPORT_BUFFER_SIZE, 0);
|
||||
circular_buffer<float> loaded = circular_buffer<float>(REPORT_BUFFER_SIZE, 0);
|
||||
report_buffer count;
|
||||
report_buffer load;
|
||||
report_buffer loaded;
|
||||
} reports;
|
||||
|
||||
bool running = true;
|
||||
std::vector<std::thread> workers;
|
||||
|
||||
void enqueue(const chunk_pos &, const world::chunk_map &);
|
||||
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);
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
#include <cstdint>
|
||||
|
||||
// stl includes
|
||||
#include <unordered_map>
|
||||
#include <robin_hood.h>
|
||||
#include <vector>
|
||||
|
||||
namespace dualmc {
|
||||
|
@ -211,7 +211,7 @@ protected:
|
|||
};
|
||||
|
||||
/// Hash map for shared vertex index computations
|
||||
std::unordered_map<DualPointKey,QuadIndexType,DualPointKeyHash> pointToIndex;
|
||||
robin_hood::unordered_map<DualPointKey,QuadIndexType,DualPointKeyHash> pointToIndex;
|
||||
};
|
||||
|
||||
// inline function definitions
|
||||
|
|
|
@ -1,72 +1,79 @@
|
|||
#include "surrounding.hpp"
|
||||
|
||||
#include "../world/Chunk.hpp"
|
||||
#include "../world/Area.hpp"
|
||||
#include "../data/math.hpp"
|
||||
|
||||
using namespace geometry;
|
||||
namespace contouring::surrounding {
|
||||
bool load(faces &out, const chunk_pos &chunkPos, const world::chunk_map &chunks) {
|
||||
bool load(faces &out, const chunk_pos &chunkPos, const world::ChunkContainer &chunks) {
|
||||
{
|
||||
const auto it = chunks.find(chunkPos);
|
||||
if (it == chunks.end())
|
||||
const auto chunk = chunks.findInRange(chunkPos);
|
||||
if (!chunk.has_value())
|
||||
return false;
|
||||
|
||||
out[CENTER] = it->second;
|
||||
out[CENTER] = chunk.value();
|
||||
}
|
||||
for (size_t i = 0; i < CENTER; i++) {
|
||||
const auto it = chunks.find(chunkPos + g_face_offsets[i]);
|
||||
if (it == chunks.end())
|
||||
const auto chunk = chunks.findOrEmpty(chunkPos + g_face_offsets[i]);
|
||||
if (!chunk.has_value())
|
||||
return false;
|
||||
|
||||
out[i] = it->second;
|
||||
out[i] = chunk.value();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
std::pair<ushort, ushort> getNeighborIdx(ushort idx, Face face) {
|
||||
std::pair<glm::idx, glm::idx> getNeighborIdx(glm::idx idx, Face face) {
|
||||
switch (face) {
|
||||
case Face::Forward:
|
||||
if (idx % CHUNK_LENGTH >= CHUNK_LENGTH - 1)
|
||||
return {static_cast<int>(Face::Forward), idx - (CHUNK_LENGTH - 1)};
|
||||
if (idx % glm::IDX_LENGTH >= glm::IDX_LENGTH - 1)
|
||||
return {static_cast<int>(Face::Forward), idx - (glm::IDX_LENGTH - 1)};
|
||||
return {CENTER, idx + 1};
|
||||
|
||||
case Face::Backward:
|
||||
if (idx % CHUNK_LENGTH <= 0)
|
||||
return {static_cast<int>(Face::Backward), idx + (CHUNK_LENGTH - 1)};
|
||||
if (idx % glm::IDX_LENGTH <= 0)
|
||||
return {static_cast<int>(Face::Backward), idx + (glm::IDX_LENGTH - 1)};
|
||||
return {CENTER, idx - 1};
|
||||
|
||||
case Face::Up:
|
||||
if ((idx / CHUNK_LENGTH) % CHUNK_LENGTH >= CHUNK_LENGTH - 1)
|
||||
return {static_cast<int>(Face::Up), idx - (CHUNK_LENGTH2 - CHUNK_LENGTH)};
|
||||
return {CENTER, idx + CHUNK_LENGTH};
|
||||
if ((idx / glm::IDX_LENGTH) % glm::IDX_LENGTH >= glm::IDX_LENGTH - 1)
|
||||
return {static_cast<int>(Face::Up), idx - (glm::IDX_LENGTH2 - glm::IDX_LENGTH)};
|
||||
return {CENTER, idx + glm::IDX_LENGTH};
|
||||
|
||||
case Face::Down:
|
||||
if ((idx / CHUNK_LENGTH) % CHUNK_LENGTH <= 0)
|
||||
return {static_cast<int>(Face::Down), idx + (CHUNK_LENGTH2 - CHUNK_LENGTH)};
|
||||
return {CENTER, idx - CHUNK_LENGTH};
|
||||
if ((idx / glm::IDX_LENGTH) % glm::IDX_LENGTH <= 0)
|
||||
return {static_cast<int>(Face::Down), idx + (glm::IDX_LENGTH2 - glm::IDX_LENGTH)};
|
||||
return {CENTER, idx - glm::IDX_LENGTH};
|
||||
|
||||
case Face::Right:
|
||||
if (idx / CHUNK_LENGTH2 >= CHUNK_LENGTH - 1)
|
||||
return {static_cast<int>(Face::Right), idx - (CHUNK_SIZE - CHUNK_LENGTH2)};
|
||||
return {CENTER, idx + CHUNK_LENGTH2};
|
||||
if (idx / glm::IDX_LENGTH2 >= glm::IDX_LENGTH - 1)
|
||||
return {static_cast<int>(Face::Right), idx - (glm::IDX_SIZE - glm::IDX_LENGTH2)};
|
||||
return {CENTER, idx + glm::IDX_LENGTH2};
|
||||
|
||||
case Face::Left:
|
||||
if (idx / CHUNK_LENGTH2 <= 0)
|
||||
return {static_cast<int>(Face::Left), idx + (CHUNK_SIZE - CHUNK_LENGTH2)};
|
||||
return {CENTER, idx - CHUNK_LENGTH2};
|
||||
if (idx / glm::IDX_LENGTH2 <= 0)
|
||||
return {static_cast<int>(Face::Left), idx + (glm::IDX_SIZE - glm::IDX_LENGTH2)};
|
||||
return {CENTER, idx - glm::IDX_LENGTH2};
|
||||
|
||||
default:
|
||||
return {CENTER, idx};
|
||||
}
|
||||
}
|
||||
|
||||
bool load(corners &out, const chunk_pos &chunkPos, const world::chunk_map &chunks) {
|
||||
for (size_t i = 0; i < 8; i++) {
|
||||
const auto it = chunks.find(chunkPos + g_corner_offsets[i]);
|
||||
if (it == chunks.end())
|
||||
bool load(corners &out, const chunk_pos &chunkPos, const world::ChunkContainer &chunks) {
|
||||
{
|
||||
const auto chunk = chunks.findInRange(chunkPos);
|
||||
if(!chunk.has_value())
|
||||
return false;
|
||||
|
||||
out[i] = it->second;
|
||||
out[0] = chunk.value();
|
||||
}
|
||||
for (size_t i = 1; i < 8; i++) {
|
||||
const auto chunk = chunks.findOrEmpty(chunkPos + g_corner_offsets[i]);
|
||||
if (!chunk.has_value())
|
||||
return false;
|
||||
|
||||
out[i] = chunk.value();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -10,21 +10,21 @@ namespace contouring::surrounding {
|
|||
const auto CENTER = 6;
|
||||
typedef std::array<std::shared_ptr<const world::Chunk>, CENTER+1> faces;
|
||||
|
||||
bool load(faces &out, const chunk_pos &chunkPos, const world::chunk_map &chunks);
|
||||
bool load(faces &out, const chunk_pos &chunkPos, const world::ChunkContainer &chunks);
|
||||
|
||||
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);
|
||||
bool load(corners &out, const chunk_pos &chunkPos, const world::ChunkContainer &chunks);
|
||||
}
|
||||
|
|
|
@ -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(0), 1), o(opt){
|
||||
updateProjection();
|
||||
}
|
||||
Camera::~Camera() { }
|
||||
|
@ -12,14 +13,7 @@ void Camera::updateProjection() {
|
|||
ProjectionMatrix = glm::perspective(o.fov, RATIO, o.near, o.far);
|
||||
}
|
||||
|
||||
void Camera::update(bool captureMouse, bool captureKeys) {
|
||||
// glfwGetTime is called only once, the first time this function is called
|
||||
static double lastTime = glfwGetTime();
|
||||
|
||||
// Compute time difference between current and last frame
|
||||
double currentTime = glfwGetTime();
|
||||
float deltaTime = float(currentTime - lastTime);
|
||||
|
||||
void Camera::update(bool captureMouse, bool captureKeys, float deltaTime) {
|
||||
// Get mouse position
|
||||
if(captureMouse) {
|
||||
int viewportX, viewportY;
|
||||
|
@ -57,39 +51,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)
|
||||
);
|
||||
|
||||
// For the next frame, the "last time" will be "now"
|
||||
lastTime = currentTime;
|
||||
}
|
|
@ -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 {
|
||||
|
@ -22,13 +22,9 @@ public:
|
|||
};
|
||||
|
||||
Camera(GLFWwindow*, const InputMap&, const options&);
|
||||
Camera(Camera &&) = default;
|
||||
Camera(const Camera &) = default;
|
||||
Camera &operator=(Camera &&) = default;
|
||||
Camera &operator=(const Camera &) = default;
|
||||
~Camera();
|
||||
|
||||
void update(bool captureMouse, bool captureKeys);
|
||||
void update(bool captureMouse, bool captureKeys, float deltaTime);
|
||||
void setOptions(const options &options) {
|
||||
o = options;
|
||||
updateProjection();
|
||||
|
@ -40,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:
|
||||
|
@ -51,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;
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#pragma once
|
||||
|
||||
#include <unordered_map>
|
||||
#include <robin_hood.h>
|
||||
#include <vector>
|
||||
|
||||
#include <GL/glew.h>
|
||||
|
@ -8,7 +8,7 @@
|
|||
|
||||
enum class Input {
|
||||
Forward, Backward, Left, Right, Up, Down,
|
||||
Mouse, Debug
|
||||
Mouse, Debug, Throw
|
||||
};
|
||||
enum class Mouse {
|
||||
Left = GLFW_MOUSE_BUTTON_LEFT,
|
||||
|
@ -19,10 +19,6 @@ enum class Mouse {
|
|||
class InputMap {
|
||||
public:
|
||||
InputMap(GLFWwindow*);
|
||||
InputMap(InputMap &&) = default;
|
||||
InputMap(const InputMap &) = default;
|
||||
InputMap &operator=(InputMap &&) = default;
|
||||
InputMap &operator=(const InputMap &) = default;
|
||||
~InputMap();
|
||||
|
||||
void saveKeys();
|
||||
|
@ -41,7 +37,7 @@ public:
|
|||
|
||||
private:
|
||||
GLFWwindow *window;
|
||||
std::unordered_map<Input, int> Map = {
|
||||
robin_hood::unordered_map<Input, int> Map = {
|
||||
{Input::Forward, GLFW_KEY_W},
|
||||
{Input::Backward, GLFW_KEY_S},
|
||||
{Input::Left, GLFW_KEY_A},
|
||||
|
@ -50,11 +46,12 @@ private:
|
|||
{Input::Down, GLFW_KEY_LEFT_SHIFT},
|
||||
{Input::Mouse, GLFW_KEY_E},
|
||||
{Input::Debug, GLFW_KEY_F3},
|
||||
{Input::Throw, GLFW_KEY_B}
|
||||
};
|
||||
const std::vector<Input> Toggles = {
|
||||
Input::Mouse, Input::Debug
|
||||
Input::Mouse, Input::Debug, Input::Throw
|
||||
};
|
||||
std::unordered_map<Input, bool> Previous;
|
||||
robin_hood::unordered_map<Input, bool> Previous;
|
||||
|
||||
bool PreviousLeft;
|
||||
bool PreviousRight;
|
||||
|
|
|
@ -25,4 +25,8 @@ namespace data {
|
|||
return buffer[last];
|
||||
}
|
||||
};
|
||||
|
||||
struct report_buffer: circular_buffer<float> {
|
||||
report_buffer() : circular_buffer(256, 0) {}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
#define GREY "\033[0;90m"
|
||||
#define GREEN "\033[0;32m"
|
||||
#define BROWN "\033[0;33m"
|
||||
#define BLUE "\033[0;34m"
|
||||
#define RED "\033[1;31m"
|
||||
#define YELLOW "\033[1;33m"
|
||||
#define BOLD "\033[1;1m"
|
||||
#define BLUE_BOLD "\033[1;34m"
|
||||
#define END_COLOR "\033[0m"
|
||||
#define UNDERLINE "\e[4m"
|
||||
#define END_STYLE "\e[0m"
|
|
@ -0,0 +1,221 @@
|
|||
#pragma once
|
||||
|
||||
#include <cassert>
|
||||
#include <vector>
|
||||
#include <optional>
|
||||
|
||||
namespace data::generational {
|
||||
struct id {
|
||||
id(size_t index, size_t generation = 0): index(index), generation(generation) { }
|
||||
id(): id(0) { }
|
||||
size_t index;
|
||||
size_t generation;
|
||||
|
||||
bool operator==(const id &i) const { return index == i.index && generation == i.generation; }
|
||||
};
|
||||
|
||||
class allocator {
|
||||
public:
|
||||
allocator() { }
|
||||
template <typename C>
|
||||
allocator(const C& map) {
|
||||
for(const auto& [key, _]: map) {
|
||||
if (const auto size = entries.size(); key >= size) {
|
||||
entries.resize(key + 1);
|
||||
for (size_t i = size; i < entries.size(); i++) {
|
||||
entries[i].is_live = false;
|
||||
freed.push_back(i);
|
||||
}
|
||||
}
|
||||
assert(!entries[key].is_live);
|
||||
entries[key].is_live = true;
|
||||
}
|
||||
}
|
||||
id alloc() {
|
||||
if(freed.empty()) {
|
||||
const auto idx = entries.size();
|
||||
entries.emplace_back();
|
||||
return id(idx);
|
||||
} else {
|
||||
const auto idx = freed.back();
|
||||
freed.pop_back();
|
||||
auto &entry = entries[idx];
|
||||
assert(!entry.is_live);
|
||||
entry.is_live = true;
|
||||
return id(idx, ++entries[idx].generation);
|
||||
}
|
||||
}
|
||||
bool is_live(id id) const {
|
||||
return id.index < entries.size() &&
|
||||
entries[id.index].is_live &&
|
||||
entries[id.index].generation == id.generation;
|
||||
}
|
||||
bool free(id id) {
|
||||
if(!is_live(id))
|
||||
return false;
|
||||
|
||||
entries[id.index].is_live = false;
|
||||
freed.push_back(id.index);
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
struct entry {
|
||||
bool is_live = true;
|
||||
size_t generation = 0;
|
||||
};
|
||||
std::vector<entry> entries;
|
||||
std::vector<size_t> freed;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
class vector {
|
||||
public:
|
||||
vector() { }
|
||||
template<typename C>
|
||||
vector(const C& map) {
|
||||
for(const auto& [key, value]: map) {
|
||||
if (const auto size = entries.size(); key >= size) {
|
||||
entries.resize(key + 1);
|
||||
}
|
||||
entries[key].value = value;
|
||||
}
|
||||
for (size_t i = 0; i < entries.size(); i++) {
|
||||
if(!entries[i].value.has_value())
|
||||
freed.push_back(i);
|
||||
}
|
||||
}
|
||||
|
||||
id push(const T& in) {
|
||||
if(freed.empty()) {
|
||||
const auto idx = entries.size();
|
||||
entries.emplace_back(in);
|
||||
return id(idx);
|
||||
} else {
|
||||
const auto idx = freed.back();
|
||||
freed.pop_back();
|
||||
auto &entry = entries[idx];
|
||||
assert(!entry.value.has_value());
|
||||
entry.value = in;
|
||||
return id(idx, ++entries[idx].generation);
|
||||
}
|
||||
}
|
||||
template <typename... _Args>
|
||||
id emplace(_Args &&... __args) {
|
||||
if(freed.empty()) {
|
||||
const auto idx = entries.size();
|
||||
entries.emplace_back(std::forward<_Args>(__args)...);
|
||||
return id(idx);
|
||||
} else {
|
||||
const auto idx = freed.back();
|
||||
freed.pop_back();
|
||||
auto &entry = entries[idx];
|
||||
assert(!entry.value.has_value());
|
||||
entry.value.emplace(std::forward<_Args>(__args)...);
|
||||
return id(idx, ++entries[idx].generation);
|
||||
}
|
||||
}
|
||||
bool put(id idx, const T& in) {
|
||||
if(idx.index >= entries.size())
|
||||
return false;
|
||||
|
||||
if(entries[idx.index].generation != idx.generation || entries[idx.index].value.has_value())
|
||||
return false;
|
||||
|
||||
entries[idx.index].value = in;
|
||||
return true;
|
||||
}
|
||||
T& at(id idx) {
|
||||
assert(contains(idx));
|
||||
return entries[idx.index].value.value();
|
||||
}
|
||||
|
||||
bool contains(id idx) const {
|
||||
return idx.index < entries.size() &&
|
||||
entries[idx.index].generation == idx.generation &&
|
||||
entries[idx.index].value.has_value();
|
||||
}
|
||||
|
||||
bool free(id idx) {
|
||||
if(!contains(idx))
|
||||
return false;
|
||||
|
||||
entries[idx.index].value = std::nullopt;
|
||||
freed.push_back(idx.index);
|
||||
return true;
|
||||
}
|
||||
|
||||
template<typename apply>
|
||||
void iter(apply fn) const {
|
||||
for (size_t i = 0; i < entries.size(); i++) {
|
||||
const auto &entry = entries[i];
|
||||
if(entry.value.has_value()) {
|
||||
fn(id(i, entry.generation), entry.value.value());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<typename apply>
|
||||
void for_each(apply fn) {
|
||||
for (size_t i = 0; i < entries.size(); i++) {
|
||||
auto &entry = entries[i];
|
||||
if(entry.value.has_value()) {
|
||||
fn(id(i, entry.generation), entry.value.value());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<typename extractor>
|
||||
void extract(extractor fn) {
|
||||
for (size_t i = 0; i < entries.size(); i++) {
|
||||
auto &entry = entries[i];
|
||||
if(entry.value.has_value()) {
|
||||
if(fn(id(i, entry.generation), entry.value.value()))
|
||||
entry.value = std::nullopt;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<typename extractor>
|
||||
void remove(extractor fn) {
|
||||
for (size_t i = 0; i < entries.size(); i++) {
|
||||
auto &entry = entries[i];
|
||||
if(entry.value.has_value()) {
|
||||
if(fn(id(i, entry.generation), entry.value.value())) {
|
||||
entry.value = std::nullopt;
|
||||
freed.push_back(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
size_t size() const {
|
||||
return std::count_if(entries.begin(), entries.end(),
|
||||
[](const entry &e) { return e.value.has_value(); });
|
||||
}
|
||||
|
||||
private:
|
||||
struct entry {
|
||||
entry(): value(std::nullopt), generation(0) { }
|
||||
entry(const T &in, size_t gen = 0):
|
||||
value(in), generation(gen) { }
|
||||
template <typename... _Args>
|
||||
entry(_Args &&... __args): generation(0) {
|
||||
value.emplace(std::forward<_Args>(__args)...);
|
||||
}
|
||||
std::optional<T> value;
|
||||
size_t generation;
|
||||
};
|
||||
std::vector<entry> entries;
|
||||
std::vector<size_t> freed;
|
||||
};
|
||||
}
|
||||
|
||||
namespace std {
|
||||
template<>
|
||||
struct hash<data::generational::id> {
|
||||
std::size_t operator()(const data::generational::id& i) const noexcept {
|
||||
return std::hash<size_t>{}(i.index);
|
||||
}
|
||||
};
|
||||
}
|
|
@ -1,7 +1,6 @@
|
|||
#pragma once
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
#include <iostream>
|
||||
|
||||
namespace geometry {
|
||||
/// Axis Aligned Floating Box
|
||||
|
@ -11,7 +10,7 @@ namespace geometry {
|
|||
};
|
||||
|
||||
Box(glm::vec3 min, glm::vec3 max): Min(min), Max(max) {
|
||||
assert(("Min > Max", max.x >= min.x && max.y >= min.y && max.z >= min.z));
|
||||
assert((max.x >= min.x && max.y >= min.y && max.z >= min.z) && "Min > Max");
|
||||
}
|
||||
inline static Box fromCenter(glm::vec3 center, float radius) { return Box(center - radius, center + radius); }
|
||||
inline static Box fromMin(glm::vec3 min, glm::vec3 size) { return Box(min, min + size); }
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -0,0 +1,49 @@
|
|||
#pragma once
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
|
||||
namespace geometry {
|
||||
/// Axis Aligned Long Box
|
||||
struct IBox {
|
||||
using pos_t = glm::llvec3;
|
||||
enum class ContainmentType {
|
||||
Disjoint = false, Intersects, Contains
|
||||
};
|
||||
|
||||
IBox(pos_t min, pos_t max): Min(min), Max(max) {
|
||||
assert((max.x >= min.x && max.y >= min.y && max.z >= min.z) && "Min > Max");
|
||||
}
|
||||
inline static IBox fromCenter(pos_t center, pos_t::value_type radius) { return IBox(center - radius, center + radius); }
|
||||
inline static IBox fromMin(pos_t min, pos_t size) { return IBox(min, min + size); }
|
||||
|
||||
pos_t Min;
|
||||
pos_t Max;
|
||||
|
||||
ContainmentType contains(const IBox& box) const {
|
||||
//test if all corner is in the same side of a face by just checking min and max
|
||||
if (box.Max.x < Min.x
|
||||
|| box.Min.x > Max.x
|
||||
|| box.Max.y < Min.y
|
||||
|| box.Min.y > Max.y
|
||||
|| box.Max.z < Min.z
|
||||
|| box.Min.z > Max.z)
|
||||
return ContainmentType::Disjoint;
|
||||
|
||||
|
||||
if (box.Min.x >= Min.x
|
||||
&& box.Max.x <= Max.x
|
||||
&& box.Min.y >= Min.y
|
||||
&& box.Max.y <= Max.y
|
||||
&& box.Min.z >= Min.z
|
||||
&& box.Max.z <= Max.z)
|
||||
return ContainmentType::Contains;
|
||||
|
||||
return ContainmentType::Intersects;
|
||||
}
|
||||
constexpr bool contains(const pos_t& pos) const {
|
||||
return pos.x >= Min.x && pos.x <= Max.x &&
|
||||
pos.y >= Min.y && pos.y <= Max.y &&
|
||||
pos.z >= Min.z && pos.z <= Max.z;
|
||||
}
|
||||
};
|
||||
}
|
|
@ -1,30 +1,30 @@
|
|||
#pragma once
|
||||
|
||||
#include "Box.hpp"
|
||||
#include "../glm.hpp"
|
||||
#include "IBox.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;
|
||||
|
@ -83,5 +83,44 @@ namespace geometry {
|
|||
}
|
||||
points.push_back(current);
|
||||
}
|
||||
|
||||
IBox::ContainmentType intersect(const IBox& box) const {
|
||||
const glm::llvec3 start = from.as_voxel();
|
||||
if(box.contains(start))
|
||||
return IBox::ContainmentType::Contains;
|
||||
|
||||
const auto inv = 1. / glm::dvec3(dir);
|
||||
glm::f64 tmin = ((inv.x < 0 ? box.Max : box.Min).x - start.x) * inv.x;
|
||||
glm::f64 tmax = ((inv.x < 0 ? box.Min : box.Max).x - start.x) * inv.x;
|
||||
glm::f64 tymin = ((inv.y < 0 ? box.Max : box.Min).y - start.y) * inv.y;
|
||||
glm::f64 tymax = ((inv.y < 0 ? box.Min : box.Max).y - start.y) * inv.y;
|
||||
|
||||
if ((tmin > tymax) || (tymin > tmax)){
|
||||
return IBox::ContainmentType::Disjoint;
|
||||
}
|
||||
if (tymin > tmin) {
|
||||
tmin = tymin;
|
||||
}
|
||||
if (tymax < tmax){
|
||||
tmax = tymax;
|
||||
}
|
||||
|
||||
glm::f64 tzmin = ((inv.z < 0 ? box.Max : box.Min).z - start.z) * inv.z;
|
||||
glm::f64 tzmax = ((inv.z < 0 ? box.Min : box.Max).z - start.z) * inv.z;
|
||||
|
||||
if ((tmin > tzmax) || (tzmin > tmax)){
|
||||
return IBox::ContainmentType::Disjoint;
|
||||
}
|
||||
if (tzmin > tmin){
|
||||
tmin = tzmin;
|
||||
}
|
||||
if (tzmax < tmax){
|
||||
tmax = tzmax;
|
||||
}
|
||||
// this last check is different from the 'ray' case in below references:
|
||||
// we need to check that the segment is on the span of the line
|
||||
// that intersects the box
|
||||
return tmax<0.0f || tmin> 1.0f ? IBox::ContainmentType::Intersects : IBox::ContainmentType::Disjoint;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
#include "glm.hpp"
|
||||
#include "math.hpp"
|
||||
|
||||
using namespace glm;
|
||||
ifvec3::ifvec3(const llvec3 &pos, uint density) {
|
||||
const auto d = IDX_LENGTH2 * density;
|
||||
raw = divide(pos, glm::uvec3(d));
|
||||
offset = glm::vec3(rem(pos.x, d), rem(pos.y, d), rem(pos.z, d));
|
||||
if(density > 1) center();
|
||||
}
|
||||
llvec3 ifvec3::raw_as_long() const {
|
||||
return llvec3(raw) * llvec3(IDX_LENGTH2);
|
||||
}
|
||||
llvec3 ifvec3::as_voxel(int density) const {
|
||||
return raw_as_long() * llvec3(density) + llvec3(offset * vec3(density));
|
||||
}
|
||||
void ifvec3::center() {
|
||||
const auto diff = divide(offset, uvec3(IDX_LENGTH2));
|
||||
raw += diff;
|
||||
offset -= diff * static_cast<long>(IDX_LENGTH2);
|
||||
}
|
|
@ -3,8 +3,43 @@
|
|||
#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;
|
||||
|
||||
const auto IDX_LENGTH = 32;
|
||||
const auto IDX_LENGTH2 = IDX_LENGTH * IDX_LENGTH;
|
||||
const auto IDX_SIZE = IDX_LENGTH2 * IDX_LENGTH;
|
||||
using idx = glm::u16;
|
||||
|
||||
/// Combination of ivec3 and vec3 as a big and high precision
|
||||
/// Scale to IDX_LENGTH2
|
||||
struct ifvec3 {
|
||||
using raw_t = glm::ivec3;
|
||||
using offset_t = glm::vec3;
|
||||
|
||||
ifvec3(): raw(0), offset(0) { }
|
||||
ifvec3(const raw_t &raw, const offset_t &offset, bool recenter = true) : raw(raw), offset(offset) {
|
||||
if(recenter) center();
|
||||
}
|
||||
ifvec3(const glm::llvec3 &pos, uint density = 1);
|
||||
|
||||
raw_t raw;
|
||||
offset_t offset;
|
||||
|
||||
glm::llvec3 as_voxel(int density = 1) const;
|
||||
void center();
|
||||
glm::llvec3 raw_as_long() const;
|
||||
glm::dvec3 as_double() const;
|
||||
|
||||
inline const ifvec3 &operator+=(const offset_t &v) {
|
||||
offset += v;
|
||||
center();
|
||||
return *this;
|
||||
}
|
||||
inline ifvec3 operator+(const offset_t& v) const { return ifvec3(raw, offset + v); }
|
||||
inline ifvec3 operator/(int i) const { return ifvec3(raw / i, offset / (i * 1.f), false); }
|
||||
inline ifvec3 operator*(int i) const { return ifvec3(raw * i, offset * (i * 1.f)); }
|
||||
};
|
||||
}
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
#include "logger.hpp"
|
||||
#include <chrono>
|
||||
#include <ctime>
|
||||
|
||||
namespace logger {
|
||||
std::_Put_time<char> now() {
|
||||
const auto tp = std::chrono::system_clock::now();
|
||||
const auto timet = std::chrono::system_clock::to_time_t(tp);
|
||||
return std::put_time(std::localtime(&timet), "%Y-%m-%d %X");
|
||||
}
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
#pragma once
|
||||
|
||||
#include "colors.h"
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
|
||||
#define _OUT(expr) {std::ostringstream oss; oss << expr << std::endl; std::cout << oss.str();}
|
||||
#define LOG(expr) _OUT("[" << logger::now() << "] " << expr)
|
||||
#define LOG_E(expr) _OUT("[" << RED << logger::now() << END_COLOR << "] " << expr)
|
||||
#define LOG_W(expr) _OUT("[" << YELLOW << logger::now() << END_COLOR << "] " << expr)
|
||||
#define LOG_I(expr) _OUT("[" << GREEN << logger::now() << END_COLOR << "] " << expr)
|
||||
#define LOG_D(expr) _OUT("[" << GREY << logger::now() << END_COLOR << "] " << expr)
|
||||
|
||||
namespace logger {
|
||||
std::_Put_time<char> now();
|
||||
}
|
|
@ -7,9 +7,12 @@ namespace glm {
|
|||
constexpr ivec3 inline iround(const vec3& p) {
|
||||
return ivec3(std::round<int>(p.x), std::round<int>(p.y), std::round<int>(p.z));
|
||||
}
|
||||
constexpr int inline length2(const ivec3& a) {
|
||||
constexpr long inline length2(const ivec3& a) {
|
||||
return a.x * a.x + a.y * a.y + a.z * a.z;
|
||||
}
|
||||
constexpr long inline pow2(int v) {
|
||||
return v * v;
|
||||
}
|
||||
constexpr ivec3 inline diff(const ivec3& a, const ivec3& b) {
|
||||
return glm::abs(glm::abs(a) - glm::abs(b));
|
||||
}
|
||||
|
@ -17,16 +20,35 @@ 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 = ucvec3(IDX_LENGTH)) {
|
||||
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 uvec3 &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 ucvec3 &m = ucvec3(IDX_LENGTH)) {
|
||||
return lvec3(div(value.x, m.x), div(value.y, m.y), div(value.z, m.z));
|
||||
}
|
||||
constexpr llvec3 inline multiply(const lvec3 &value, const ucvec3 &m = ucvec3(IDX_LENGTH)) {
|
||||
return llvec3(value) * llvec3(m);
|
||||
}
|
||||
constexpr std::pair<lvec3, ucvec3> inline split(const llvec3 &value, const ucvec3 &m = ucvec3(IDX_LENGTH)) {
|
||||
return {divide(value, m), modulo(value, m)};
|
||||
}
|
||||
constexpr ucvec3 inline fromIdx(idx idx) {
|
||||
assert(idx < IDX_SIZE);
|
||||
return ucvec3(idx / IDX_LENGTH2, (idx / IDX_LENGTH) % IDX_LENGTH, idx % IDX_LENGTH);
|
||||
}
|
||||
constexpr idx inline toIdx(glm::u8 x, glm::u8 y, glm::u8 z) {
|
||||
return (x * IDX_LENGTH + y) * IDX_LENGTH + z;
|
||||
}
|
||||
constexpr idx inline toIdx(ucvec3 pos) {
|
||||
return toIdx(pos.x, pos.y, pos.z);
|
||||
}
|
||||
constexpr std::pair<lvec3, idx> inline splitIdx(const llvec3 &value, const ucvec3 &m = ucvec3(IDX_LENGTH)) {
|
||||
return {divide(value, m), toIdx(rem(value.x, m.x), rem(value.y, m.y), rem(value.z, m.z))};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
|
||||
#include <tuple>
|
||||
#include <vector>
|
||||
#include <unordered_set>
|
||||
#include <robin_hood.h>
|
||||
#include <mutex>
|
||||
#include <condition_variable>
|
||||
|
@ -10,7 +9,7 @@
|
|||
|
||||
namespace data {
|
||||
/// Thread safe queue with unique keys updating priority and value
|
||||
template <class K, class V, class W>
|
||||
template <class K, class V, class W, class hash = robin_hood::hash<K>>
|
||||
class safe_priority_queue_map {
|
||||
private:
|
||||
static bool cmpByWeight(const std::pair<K, W> &a, const std::pair<K, W> &b) {
|
||||
|
@ -18,7 +17,7 @@ namespace data {
|
|||
}
|
||||
|
||||
std::vector<std::pair<K, W>> heap;
|
||||
robin_hood::unordered_map<K, V> map;
|
||||
robin_hood::unordered_map<K, V, hash> map;
|
||||
std::mutex mutex;
|
||||
std::condition_variable cv;
|
||||
|
||||
|
@ -59,9 +58,12 @@ namespace data {
|
|||
return map.size();
|
||||
}
|
||||
|
||||
void notify() {
|
||||
void notify_all() {
|
||||
cv.notify_all();
|
||||
}
|
||||
void notify_one() {
|
||||
cv.notify_one();
|
||||
}
|
||||
|
||||
void wait() {
|
||||
std::unique_lock<std::mutex> lock(mutex);
|
||||
|
@ -121,9 +123,12 @@ namespace data {
|
|||
return set.size();
|
||||
}
|
||||
|
||||
void notify() {
|
||||
void notify_all() {
|
||||
cv.notify_all();
|
||||
}
|
||||
void notify_one() {
|
||||
cv.notify_one();
|
||||
}
|
||||
|
||||
void wait() {
|
||||
std::unique_lock<std::mutex> lock(mutex);
|
||||
|
|
|
@ -3,7 +3,6 @@
|
|||
#include <queue>
|
||||
#include <mutex>
|
||||
#include <condition_variable>
|
||||
#include <robin_hood.h>
|
||||
|
||||
namespace data {
|
||||
/// Thread safe queue
|
||||
|
@ -48,9 +47,12 @@ namespace data {
|
|||
return queue.size();
|
||||
}
|
||||
|
||||
void notify() {
|
||||
void notify_all() {
|
||||
cv.notify_all();
|
||||
}
|
||||
void notify_one() {
|
||||
cv.notify_one();
|
||||
}
|
||||
|
||||
void wait() {
|
||||
std::unique_lock<std::mutex> lock(mutex);
|
||||
|
|
|
@ -7,11 +7,11 @@
|
|||
|
||||
namespace data {
|
||||
/// Thread safe queue discarding duplicate values
|
||||
template <class T>
|
||||
template <class T, class hash = robin_hood::hash<T>>
|
||||
class safe_unique_queue {
|
||||
private:
|
||||
std::queue<T> queue;
|
||||
robin_hood::unordered_flat_set<T> set;
|
||||
robin_hood::unordered_flat_set<T, hash> set;
|
||||
std::mutex mutex;
|
||||
std::condition_variable cv;
|
||||
|
||||
|
@ -62,9 +62,12 @@ namespace data {
|
|||
return set.size();
|
||||
}
|
||||
|
||||
void notify() {
|
||||
void notify_all() {
|
||||
cv.notify_all();
|
||||
}
|
||||
void notify_one() {
|
||||
cv.notify_one();
|
||||
}
|
||||
|
||||
void wait() {
|
||||
std::unique_lock<std::mutex> lock(mutex);
|
||||
|
|
75
src/main.cpp
75
src/main.cpp
|
@ -20,10 +20,11 @@
|
|||
#include "state.h"
|
||||
#include "data/math.hpp"
|
||||
|
||||
#include <Remotery.h>
|
||||
#include <Remotery.h> // NOLINT
|
||||
|
||||
/// Entry point
|
||||
int main(int, char *[]){
|
||||
int main(int /*unused*/, char */*unused*/[]){
|
||||
LOG("Univerxel");
|
||||
options options;
|
||||
state state;
|
||||
reports reports;
|
||||
|
@ -33,7 +34,7 @@ int main(int, char *[]){
|
|||
return 1;
|
||||
|
||||
glClearColor(options.renderer.clear_color.x, options.renderer.clear_color.y, options.renderer.clear_color.z, options.renderer.clear_color.w);
|
||||
glfwSwapInterval(options.target_fps < MIN_FPS);
|
||||
glfwSwapInterval(static_cast<int>(options.target_fps < MIN_FPS));
|
||||
|
||||
InputMap inputs(window);
|
||||
Camera camera(window, inputs, options.camera);
|
||||
|
@ -73,11 +74,11 @@ int main(int, char *[]){
|
|||
glm::vec4(1, 1, 1, 1), glm::vec4(1, 1, 1, 1),
|
||||
});
|
||||
|
||||
#if RMT_ENABLED
|
||||
Remotery *rmt;
|
||||
rmt_CreateGlobalInstance(&rmt);
|
||||
rmt_BindOpenGL();
|
||||
#if RMT_ENABLED
|
||||
std::cout << "Profiling !" << std::endl;
|
||||
LOG("Profiling !");
|
||||
#endif
|
||||
|
||||
world::Universe world = world::Universe(options.world);
|
||||
|
@ -88,31 +89,39 @@ int main(int, char *[]){
|
|||
const double startTime = glfwGetTime();
|
||||
{ // Update
|
||||
rmt_ScopedCPUSample(Update, 0);
|
||||
static double lastTime = glfwGetTime();
|
||||
const double partTime = glfwGetTime();
|
||||
const float deltaTime = float(partTime - lastTime);
|
||||
inputs.toggle(state.capture_mouse, Input::Mouse);
|
||||
inputs.toggle(options.show_debug_menu, Input::Debug);
|
||||
|
||||
camera.update(state.capture_mouse, !UI::isFocus());
|
||||
camera.update(state.capture_mouse, !UI::isFocus(), deltaTime);
|
||||
renderer->lookFrom(camera);
|
||||
|
||||
state.position = camera.getPosition();
|
||||
state.look_at = world.raycast(camera.getRay() / options.voxel_size);
|
||||
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);
|
||||
state.look_at = world.raycast(camera.getRay() * options.voxel_density);
|
||||
if (state.capture_mouse) {
|
||||
if (state.look_at.has_value()) {
|
||||
if (inputs.isPressing(Mouse::Left))
|
||||
world.setCube(state.look_at.value().pos, world::Voxel(0), options.tool.radius);
|
||||
else if (inputs.isPressing(Mouse::Right))
|
||||
world.setCube(state.look_at.value().pos, world::Voxel(options.tool.material, world::Voxel::DENSITY_MAX), options.tool.radius);
|
||||
}
|
||||
if (inputs.isDown(Input::Throw)) {
|
||||
world.addEntity(entity_id(0), {state.position * options.voxel_density, glm::vec3(10, 0, 0)});
|
||||
}
|
||||
}
|
||||
world.update(state.position / options.voxel_size, reports.world);
|
||||
world.update((state.position * options.voxel_density).as_voxel(), deltaTime, reports.world);
|
||||
inputs.saveKeys();
|
||||
reports.main.update.push((glfwGetTime() - partTime) * 1000);
|
||||
lastTime = partTime;
|
||||
}
|
||||
|
||||
{
|
||||
rmt_ScopedCPUSample(UI, 0);
|
||||
const auto actions = UI::draw(options, state, reports, aimTexture);
|
||||
if (actions && UI::Actions::FPS) {
|
||||
glfwSwapInterval(options.target_fps < MIN_FPS);
|
||||
glfwSwapInterval(static_cast<int>(options.target_fps < MIN_FPS));
|
||||
}
|
||||
if (actions && UI::Actions::FullScreen) {
|
||||
// MAYBE: real fullscreen
|
||||
|
@ -153,24 +162,40 @@ int main(int, char *[]){
|
|||
auto pass = renderer->getPass();
|
||||
pass.start();
|
||||
|
||||
std::vector<std::pair<glm::mat4, buffer::Abstract *const>> models;
|
||||
reports.main.models_count = 0;
|
||||
reports.main.tris_count = 0;
|
||||
std::optional<geometry::Frustum> frustum;
|
||||
if(options.culling) {
|
||||
frustum = {camera.getFrustum()};
|
||||
}
|
||||
world.getContouring()->getModels(models, frustum, options.voxel_size);
|
||||
reports.main.models_count = 0;
|
||||
reports.main.tris_count = 0;
|
||||
rmt_ScopedOpenGLSample(Render);
|
||||
for (auto [model, buffer] : models) {
|
||||
reports.main.models_count++;
|
||||
reports.main.tris_count += buffer->draw(pass.setup(model));
|
||||
const auto offset = state.position.raw_as_long();
|
||||
{ // Chunks
|
||||
std::vector<std::pair<glm::mat4, buffer::Abstract *const>> models;
|
||||
world.getContouring()->getModels(models, frustum, offset, options.voxel_density);
|
||||
rmt_ScopedOpenGLSample(Render);
|
||||
for (auto [model, buffer] : models) {
|
||||
reports.main.models_count++;
|
||||
reports.main.tris_count += buffer->draw(pass.setup(model));
|
||||
}
|
||||
}
|
||||
if(state.look_at.has_value()) {
|
||||
{ // Entities
|
||||
std::vector<std::pair<std::vector<glm::mat4>, buffer::Abstract *const>> models;
|
||||
world.getEntitiesModels(models, frustum, offset, options.voxel_density);
|
||||
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));
|
||||
lookBuffer.draw(lookProgram->setup(renderer, model));
|
||||
for (auto [mats, buffer]: models) {
|
||||
for(auto model: mats) {
|
||||
reports.main.models_count++;
|
||||
reports.main.tris_count += lookBuffer.draw(lookProgram->setup(renderer, model));
|
||||
}
|
||||
}
|
||||
}
|
||||
if(state.look_at.has_value()) { // Indicator
|
||||
lookProgram->useIt();
|
||||
lookProgram->start(renderer);
|
||||
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().pos.second + state.look_at.value().offset - offset * glm::llvec3(options.voxel_density)) - glm::vec3(.5 + options.tool.radius)), glm::vec3(1 + options.tool.radius * 2));
|
||||
reports.main.models_count++;
|
||||
reports.main.tris_count += lookBuffer.draw(lookProgram->setup(renderer, model));
|
||||
}
|
||||
renderer->postProcess();
|
||||
|
||||
|
|
|
@ -79,8 +79,14 @@ UI::Actions UI::draw(options &options, state &state, const reports &reports, GLu
|
|||
changeRenderer |= ImGui::Checkbox("PBR", &options.renderer.main.pbr);
|
||||
ImGui::SameLine();
|
||||
changeRenderer |= ImGui::Checkbox("Triplanar", &options.renderer.main.triplanar);
|
||||
|
||||
changeRenderer |= ImGui::Checkbox("Geometry", &options.renderer.main.geometry);
|
||||
ImGui::SameLine();
|
||||
changeRenderer |= ImGui::Checkbox("Blend", &options.renderer.main.blend);
|
||||
if(options.renderer.main.geometry) {
|
||||
changeRenderer |= ImGui::Checkbox("Blend", &options.renderer.main.blend);
|
||||
} else {
|
||||
ImGui::TextDisabled("Blend");
|
||||
}
|
||||
|
||||
changeRenderer |= ImGui::Checkbox("Fog", &options.renderer.main.fog);
|
||||
ImGui::SameLine();
|
||||
|
@ -107,18 +113,16 @@ UI::Actions UI::draw(options &options, state &state, const reports &reports, GLu
|
|||
ImGui::PlotHistogram("Loading", reports.world.chunk_load.buffer.get(), reports.world.chunk_load.size, 0, std::to_string(reports.world.chunk_load.current()).c_str(), 0);
|
||||
ImGui::PlotHistogram("Saving", reports.world.chunk_unload.buffer.get(), reports.world.chunk_unload.size, 0, std::to_string(reports.world.chunk_unload.current()).c_str(), 0);
|
||||
ImGui::PlotHistogram("Regions", reports.world.region_count.buffer.get(), reports.world.region_count.size, 0, std::to_string(reports.world.region_count.current()).c_str(), 0);
|
||||
ImGui::PlotHistogram("Entities", reports.world.entity_count.buffer.get(), reports.world.entity_count.size, 0, std::to_string(reports.world.entity_count.current()).c_str(), 0);
|
||||
ImGui::Separator();
|
||||
ImGui::Text("Path: %s", options.world.folderPath.c_str());
|
||||
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);
|
||||
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();
|
||||
}
|
||||
|
||||
|
@ -142,29 +146,41 @@ UI::Actions UI::draw(options &options, state &state, const reports &reports, GLu
|
|||
ImGui::End();
|
||||
}
|
||||
|
||||
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);
|
||||
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("Move speed", &options.camera.speed, 0.1, 50);
|
||||
changePerspective |= ImGui::SliderInt("Sensibility", &options.camera.sensibility, 1, 100, "%d%%");
|
||||
if(changePerspective) {
|
||||
actions = actions | Actions::Camera;
|
||||
{
|
||||
const auto farRange = state.contouring->getFarRange();
|
||||
if (options.show_debug_controls) {
|
||||
ImGui::Begin("Debug: Controls", &options.show_debug_controls, ImGuiWindowFlags_AlwaysAutoResize);
|
||||
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, farRange.first / options.voxel_density, farRange.second / 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) {
|
||||
actions = actions | Actions::Camera;
|
||||
}
|
||||
}
|
||||
ImGui::End();
|
||||
}
|
||||
const auto far = std::clamp(options.camera.far, farRange.first / options.voxel_density, farRange.second / options.voxel_density);
|
||||
if(far != options.camera.far) {
|
||||
options.camera.far = far;
|
||||
actions = actions | Actions::Camera;
|
||||
}
|
||||
ImGui::End();
|
||||
}
|
||||
|
||||
if (options.editor_show) {
|
||||
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);
|
||||
const auto &look = state.look_at.value();
|
||||
ImGui::Text("Look at: (%ld: %lld, %lld, %lld) (%s, %.1f)", look.pos.first.index, look.pos.second.x, look.pos.second.y, look.pos.second.z,
|
||||
world::materials::textures[look.value.material()].c_str(), look.value.density() * 1. / world::Voxel::DENSITY_MAX);
|
||||
const auto w_pos = look.pos.second + look.offset;
|
||||
ImGui::Text("(%.3f, %.3f, %.3f)", w_pos.x * 1. / options.voxel_density, w_pos.y * 1. / options.voxel_density, w_pos.z * 1. / options.voxel_density);
|
||||
} else {
|
||||
ImGui::Text("Look at: none");
|
||||
}
|
||||
|
|
|
@ -3,9 +3,9 @@
|
|||
#include <GL/glew.h>
|
||||
#include <GLFW/glfw3.h>
|
||||
|
||||
class options;
|
||||
class state;
|
||||
class reports;
|
||||
struct options;
|
||||
struct state;
|
||||
struct reports;
|
||||
namespace UI {
|
||||
/// Retro actions to state
|
||||
enum class Actions {
|
||||
|
|
|
@ -8,6 +8,7 @@ namespace buffer {
|
|||
struct params {
|
||||
/// Bind only vertices positions
|
||||
bool vertexOnly;
|
||||
size_t instances = 1;
|
||||
};
|
||||
|
||||
/// Abstract OpenGL Buffer
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#include "ShortIndexed.hpp"
|
||||
|
||||
#include "vboindexer.hpp"
|
||||
#include "../../data/logger.hpp"
|
||||
|
||||
using namespace buffer;
|
||||
|
||||
|
@ -95,7 +96,7 @@ void ShortIndexed::setData(const ShortIndexed::Data& data) {
|
|||
|
||||
IndexSize = data.indices.size();
|
||||
if(IndexSize != data.indices.size()) {
|
||||
std::cout << "ShortBuffer overflow: " << data.indices.size() << std::endl;
|
||||
LOG_E("ShortBuffer overflow: " << data.indices.size());
|
||||
}
|
||||
setIndicies(IndexSize * sizeof(GLushort), &data.indices[0]);
|
||||
setVertices(data.vertices.size() * sizeof(glm::vec3), &data.vertices[0]);
|
||||
|
|
|
@ -39,13 +39,13 @@ namespace buffer {
|
|||
ShortIndexed(GLenum shape, const typename ShortIndexed::Data &data);
|
||||
virtual ~ShortIndexed();
|
||||
|
||||
uint draw(params params) override;
|
||||
|
||||
private:
|
||||
void enableAllAttribs();
|
||||
void disableAllAttribs();
|
||||
void enableIndex();
|
||||
|
||||
uint draw(params params) override;
|
||||
|
||||
private:
|
||||
GLuint IndexBufferID;
|
||||
GLushort IndexSize = 0;
|
||||
|
||||
|
|
|
@ -13,13 +13,16 @@ MainProgram::MainProgram(const MainProgram::options& opts): Program() {
|
|||
flags.emplace_back("TRIPLANAR");
|
||||
if (opts.fog)
|
||||
flags.emplace_back("FOG");
|
||||
if (opts.blend)
|
||||
flags.emplace_back("BLEND");
|
||||
if (opts.geometry) {
|
||||
flags.emplace_back("GEOMETRY");
|
||||
if (opts.blend)
|
||||
flags.emplace_back("BLEND");
|
||||
}
|
||||
|
||||
std::vector<Shader*> shaders;
|
||||
shaders.push_back(loadShader(GL_VERTEX_SHADER, flags));
|
||||
shaders.push_back(loadShader(GL_FRAGMENT_SHADER, flags));
|
||||
if (opts.blend)
|
||||
if (opts.geometry)
|
||||
shaders.push_back(loadShader(GL_GEOMETRY_SHADER, flags));
|
||||
load(shaders);
|
||||
|
||||
|
|
|
@ -12,8 +12,10 @@ namespace pass {
|
|||
bool pbr = true;
|
||||
/// Triplanar texture mapping
|
||||
bool triplanar = false;
|
||||
/// Blend voxel with mixed materials
|
||||
bool blend = true;
|
||||
/// Active geometry pass
|
||||
bool geometry = true;
|
||||
/// Blend voxel with mixed materials (requires geometry)
|
||||
bool blend = false;
|
||||
/// Depth fog
|
||||
bool fog = true;
|
||||
};
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
#define MIN_WIDTH 854
|
||||
#define MIN_HEIGHT 480
|
||||
|
||||
void framebuffer_size_callback(GLFWwindow *window, int width, int height) {
|
||||
void framebuffer_size_callback(GLFWwindow *, int width, int height) {
|
||||
glViewport(0, 0, width, height);
|
||||
}
|
||||
|
||||
|
@ -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) {
|
||||
|
|
42
src/state.h
42
src/state.h
|
@ -3,7 +3,6 @@
|
|||
#define UI_MARGIN 5
|
||||
#define MIN_FPS 24
|
||||
#define MAX_FPS 240
|
||||
#define REPORT_BUFFER_SIZE 128
|
||||
|
||||
#include <toml.h>
|
||||
#include <fstream>
|
||||
|
@ -17,14 +16,15 @@
|
|||
#include "contouring/index.hpp"
|
||||
|
||||
inline glm::vec4 fromHex(const std::string& str) {
|
||||
int rgb[3] = {UCHAR_MAX};
|
||||
std::array<int, 3> rgb = {UCHAR_MAX};
|
||||
sscanf(str.c_str() + 1, "%02X%02X%02X", (unsigned int *)&rgb[0], (unsigned int *)&rgb[1], (unsigned int *)&rgb[2]);
|
||||
return glm::vec4(rgb[0] * 1.f / UCHAR_MAX, rgb[1] * 1.f / UCHAR_MAX, rgb[2] * 1.f / UCHAR_MAX, 1);
|
||||
}
|
||||
inline std::string toHexa(const glm::vec4& rgb) {
|
||||
auto out = (char*)malloc(8 * sizeof(char));
|
||||
sprintf(out, "#%02X%02X%02X", (int)(rgb.x * UCHAR_MAX), (int)(rgb.y * UCHAR_MAX), (int)(rgb.z * UCHAR_MAX));
|
||||
return std::string(out);
|
||||
std::ostringstream sstr;
|
||||
sstr << std::hex << std::setw(2) << std::setfill('0') <<
|
||||
static_cast<int>(rgb.x * UCHAR_MAX) << static_cast<int>(rgb.y * UCHAR_MAX) << static_cast<int>(rgb.z * UCHAR_MAX) << std::endl;
|
||||
return sstr.str();
|
||||
}
|
||||
|
||||
/// Savable game options
|
||||
|
@ -41,6 +41,7 @@ struct options {
|
|||
renderer.mipMapLOD = config["render"]["texture_quality"].value_or(renderer.mipMapLOD);
|
||||
renderer.main.pbr = config["render"]["pbr"].value_or(renderer.main.pbr);
|
||||
renderer.main.triplanar = config["render"]["triplanar"].value_or(renderer.main.triplanar);
|
||||
renderer.main.geometry = config["render"]["geometry"].value_or(renderer.main.geometry);
|
||||
renderer.main.blend = config["render"]["blend"].value_or(renderer.main.blend);
|
||||
renderer.main.fog = config["render"]["fog"].value_or(renderer.main.fog);
|
||||
const std::string fog = config["render"]["fog_color"].value_or(std::string{"#000000"});
|
||||
|
@ -50,11 +51,11 @@ 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("")));
|
||||
for(const auto name: contouring::names) {
|
||||
for(const auto& name: contouring::names) {
|
||||
contouring_data.emplace(name, config["mesh"]["options"][name].value_or(std::string("")));
|
||||
}
|
||||
|
||||
|
@ -91,6 +92,7 @@ struct options {
|
|||
{"texture_quality", renderer.mipMapLOD},
|
||||
{"pbr", renderer.main.pbr},
|
||||
{"triplanar", renderer.main.triplanar},
|
||||
{"geometry", renderer.main.geometry},
|
||||
{"blend", renderer.main.blend},
|
||||
{"fog", renderer.main.fog},
|
||||
{"fog_color", toHexa(renderer.clear_color)},
|
||||
|
@ -99,7 +101,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({
|
||||
|
@ -107,9 +109,9 @@ struct options {
|
|||
{"mode", contouring::names[contouring_idx]},
|
||||
{"options", toml::table()}
|
||||
}));
|
||||
for(auto opt: contouring_data) {
|
||||
if(!opt.second.empty())
|
||||
config["mesh"]["options"].as_table()->insert_or_assign(opt.first, opt.second);
|
||||
for(const auto& [key, val]: contouring_data) {
|
||||
if(!val.empty())
|
||||
config["mesh"]["options"].as_table()->insert_or_assign(key, val);
|
||||
}
|
||||
|
||||
config.insert_or_assign("camera", toml::table({
|
||||
|
@ -154,7 +156,7 @@ struct options {
|
|||
|
||||
bool show_debug_world;
|
||||
world::Universe::options world;
|
||||
float voxel_size;
|
||||
int voxel_density;
|
||||
|
||||
bool show_debug_contouring;
|
||||
bool culling;
|
||||
|
@ -180,12 +182,12 @@ struct options {
|
|||
/// Live state
|
||||
struct state {
|
||||
bool capture_mouse = true;
|
||||
camera_pos position;
|
||||
std::optional<std::pair<voxel_pos, world::Voxel>> look_at = {};
|
||||
camera_pos position = camera_pos(voxel_pos(0), 1);
|
||||
std::optional<world::Universe::ray_target> look_at = {};
|
||||
|
||||
std::shared_ptr<contouring::Abstract> contouring;
|
||||
|
||||
char console_buffer[256];
|
||||
std::array<char, 256> console_buffer;
|
||||
};
|
||||
|
||||
/// Readonly metrics
|
||||
|
@ -193,11 +195,11 @@ struct reports {
|
|||
struct main {
|
||||
size_t tris_count = 0;
|
||||
size_t models_count = 0;
|
||||
circular_buffer<float> fps = circular_buffer<float>(REPORT_BUFFER_SIZE, 0);
|
||||
circular_buffer<float> update = circular_buffer<float>(REPORT_BUFFER_SIZE, 0);
|
||||
circular_buffer<float> render = circular_buffer<float>(REPORT_BUFFER_SIZE, 0);
|
||||
circular_buffer<float> swap = circular_buffer<float>(REPORT_BUFFER_SIZE, 0);
|
||||
circular_buffer<float> wait = circular_buffer<float>(REPORT_BUFFER_SIZE, 0);
|
||||
report_buffer fps;
|
||||
report_buffer update;
|
||||
report_buffer render;
|
||||
report_buffer swap;
|
||||
report_buffer wait;
|
||||
} main;
|
||||
world::Universe::report world;
|
||||
};
|
|
@ -0,0 +1,26 @@
|
|||
#include "Area.hpp"
|
||||
|
||||
#include "Chunk.hpp"
|
||||
|
||||
using namespace world;
|
||||
|
||||
std::optional<std::shared_ptr<Chunk>> ChunkContainer::findInRange(const chunk_pos &pos) const {
|
||||
const auto it = find(pos);
|
||||
return it != end() ? std::make_optional(it->second) : std::nullopt;
|
||||
}
|
||||
std::optional<std::shared_ptr<const Chunk>> ChunkContainer::findOrEmpty(const chunk_pos &pos) const {
|
||||
return inRange(pos) ? findInRange(pos) : std::make_optional(world::EMPTY_CHUNK);
|
||||
}
|
||||
|
||||
std::shared_ptr<Region> Area::getRegion(const std::string& folderPath, const area_<region_pos>& pos) {
|
||||
{ // Found
|
||||
const auto shared = regions.lock_shared();
|
||||
const auto it = shared->find(pos.second);
|
||||
if(it != shared->end())
|
||||
return it->second;
|
||||
}
|
||||
// Reading
|
||||
const auto reg = std::make_shared<Region>(folderPath, pos);
|
||||
const auto unique = regions.lock();
|
||||
return unique->insert({pos.second, reg}).first->second;
|
||||
}
|
|
@ -0,0 +1,75 @@
|
|||
#pragma once
|
||||
|
||||
#include "forward.h"
|
||||
#include <shared_mutex_guarded.h>
|
||||
using namespace libguarded;
|
||||
#include "region/index.hpp"
|
||||
#include "Generator.hpp"
|
||||
#include "../data/geometry/IBox.hpp"
|
||||
|
||||
namespace world {
|
||||
/// Chunk map with restricted access
|
||||
struct ChunkContainer: robin_hood::unordered_map<chunk_pos, std::shared_ptr<Chunk>> {
|
||||
private:
|
||||
int radius;
|
||||
|
||||
public:
|
||||
/// Area radius in chunks
|
||||
constexpr inline int getRadius() const { return radius; }
|
||||
ChunkContainer(int radius): radius(radius) {
|
||||
assert(radius > 0 && radius < (1 << 22) / CHUNK_LENGTH);
|
||||
}
|
||||
inline bool inRange(const chunk_pos& pos) const {
|
||||
return glm::abs(pos.x) < radius && glm::abs(pos.y) < radius && glm::abs(pos.z) < radius;
|
||||
}
|
||||
std::optional<std::shared_ptr<Chunk>> findInRange(const chunk_pos &pos) const;
|
||||
std::optional<std::shared_ptr<const Chunk>> findOrEmpty(const chunk_pos &pos) const;
|
||||
};
|
||||
|
||||
/// Area (aka big group of chunks)
|
||||
struct Area {
|
||||
public:
|
||||
using regions_t = robin_hood::unordered_map<region_pos, std::shared_ptr<Region>>;
|
||||
|
||||
struct params {
|
||||
voxel_pos center;
|
||||
int radius;
|
||||
int seed = 42;
|
||||
};
|
||||
|
||||
/// radius: size in chunk (length = radius * 2 + 1)
|
||||
Area(const params& p): center(p.center), chunks(p.radius), generator(p.seed) { }
|
||||
|
||||
inline const area_pos &getOffset() const { return center; }
|
||||
inline geometry::IBox getBounding() const {
|
||||
const auto c = center.as_voxel();
|
||||
return geometry::IBox(c - voxel_pos(CHUNK_LENGTH * (chunks.getRadius() - 1)),
|
||||
c + voxel_pos(CHUNK_LENGTH * chunks.getRadius()));
|
||||
}
|
||||
/// Move offset return if chunk_change
|
||||
bool inline move(const area_pos::offset_t &offset) {
|
||||
const auto prev = glm::divide(center.as_voxel());
|
||||
center = center + offset;
|
||||
return prev != glm::divide(center.as_voxel());
|
||||
}
|
||||
|
||||
inline const ChunkContainer &getChunks() const { return chunks; }
|
||||
inline ChunkContainer &setChunks() { return chunks; }
|
||||
|
||||
std::shared_ptr<Region> getRegion(const std::string& folderPath, const area_<region_pos> &);
|
||||
shared_guarded<regions_t>::handle getRegions() { return regions.lock(); }
|
||||
|
||||
inline Generator &getGenerator() { return generator; }
|
||||
|
||||
inline params getParams() const { return params{center.as_voxel(), chunks.getRadius(), generator.getSeed()}; }
|
||||
|
||||
private:
|
||||
area_pos center;
|
||||
|
||||
//TODO: rotation
|
||||
ChunkContainer chunks;
|
||||
shared_guarded<regions_t> regions;
|
||||
|
||||
Generator generator;
|
||||
};
|
||||
}
|
|
@ -2,76 +2,74 @@
|
|||
|
||||
#include "materials.hpp"
|
||||
#include <algorithm>
|
||||
#include "../data/math.hpp"
|
||||
|
||||
using namespace world;
|
||||
|
||||
#define DENSITY 0.f
|
||||
#define GRANULARITY 30.f
|
||||
#define RLE 1
|
||||
constexpr auto DENSITY = 0.f;
|
||||
constexpr auto GRANULARITY = 30.f;
|
||||
|
||||
Chunk::Chunk(const chunk_pos& pos, Generator& rnd) {
|
||||
const auto [densitySet, materialSet] = rnd.getChunk(pos, CHUNK_LENGTH);
|
||||
|
||||
for (size_t i = 0; i < CHUNK_SIZE; i++) {
|
||||
voxels[i].Density = std::clamp((densitySet[i] + DENSITY) * GRANULARITY, 0.f, 1.f) * UCHAR_MAX;
|
||||
voxels[i].Material = voxels[i].Density > 0 ? 1 + std::clamp(static_cast<int>(std::lrint((materialSet[i] + 1) / 2 * (materials::count - 2))),
|
||||
const auto density = std::clamp((densitySet[i] + DENSITY) * GRANULARITY, 0.f, 1.f) * Voxel::DENSITY_MAX;
|
||||
const auto material = 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)
|
||||
voxels[i] = Voxel(material, density);
|
||||
}
|
||||
FastNoiseSIMD::FreeNoiseSet(densitySet);
|
||||
FastNoiseSIMD::FreeNoiseSet(materialSet);
|
||||
rnd.free(densitySet);
|
||||
rnd.free(materialSet);
|
||||
}
|
||||
Chunk::Chunk(std::istream& str) {
|
||||
#ifdef RLE
|
||||
ushort i = 0;
|
||||
while(!str.eof()) {
|
||||
ushort count;
|
||||
Voxel voxel;
|
||||
str.read(reinterpret_cast<char *>(&count), sizeof(count));
|
||||
str.read(reinterpret_cast<char *>(&voxel.Density), sizeof(Voxel::Density));
|
||||
str.read(reinterpret_cast<char *>(&voxel.Material), sizeof(Voxel::Material));
|
||||
str.peek();
|
||||
for (; count > 0; count--) {
|
||||
voxels[i] = voxel;
|
||||
i++;
|
||||
#include <iostream>
|
||||
Chunk::Chunk(std::istream& str, bool rle) {
|
||||
if(rle) {
|
||||
ushort i = 0;
|
||||
while(!str.eof()) {
|
||||
ushort count;
|
||||
Voxel voxel;
|
||||
str.read(reinterpret_cast<char *>(&count), sizeof(count));
|
||||
str.read(reinterpret_cast<char *>(&voxel), sizeof(voxel));
|
||||
str.peek();
|
||||
for (; count > 0; count--) {
|
||||
voxels[i] = voxel;
|
||||
i++;
|
||||
}
|
||||
}
|
||||
assert(i == CHUNK_SIZE && "Mismatch data length");
|
||||
} else {
|
||||
for(auto& voxel: voxels) {
|
||||
str.read(reinterpret_cast<char *>(&voxel), sizeof(voxel));
|
||||
}
|
||||
}
|
||||
assert(("Mismatch data length", i == CHUNK_SIZE-1));
|
||||
#else
|
||||
for(auto& voxel: voxels) {
|
||||
str.read(reinterpret_cast<char *>(&voxel.Density), sizeof(Voxel::Density));
|
||||
str.read(reinterpret_cast<char *>(&voxel.Material), sizeof(Voxel::Material));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
Chunk::~Chunk() { }
|
||||
|
||||
void Chunk::write(std::ostream& str) const {
|
||||
#ifdef RLE
|
||||
auto it = voxels.begin();
|
||||
ushort counter = 1;
|
||||
Voxel current = *it;
|
||||
while(true) {
|
||||
it++;
|
||||
const auto end = (it == voxels.end());
|
||||
if(end || current.Density != it->Density || current.Material != it->Material) {
|
||||
str.write(reinterpret_cast<char *>(&counter), sizeof(counter));
|
||||
str.write(reinterpret_cast<char *>(¤t.Density), sizeof(current.Density));
|
||||
str.write(reinterpret_cast<char *>(¤t.Material), sizeof(current.Material));
|
||||
if(end)
|
||||
break;
|
||||
void Chunk::write(std::ostream& str, bool rle) const {
|
||||
if (rle) {
|
||||
const auto *it = voxels.begin();
|
||||
ushort counter = 1;
|
||||
Voxel current = *it;
|
||||
while(true) {
|
||||
++it;
|
||||
const auto end = (it == voxels.end());
|
||||
if(end || current.value != it->value) {
|
||||
str.write(reinterpret_cast<char *>(&counter), sizeof(counter));
|
||||
str.write(reinterpret_cast<char *>(¤t), sizeof(current));
|
||||
if(end)
|
||||
break;
|
||||
|
||||
current = *it;
|
||||
counter = 1;
|
||||
} else {
|
||||
counter++;
|
||||
current = *it;
|
||||
counter = 1;
|
||||
} else {
|
||||
counter++;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for(auto current: voxels) {
|
||||
str.write(reinterpret_cast<char *>(¤t), sizeof(current));
|
||||
}
|
||||
}
|
||||
#else
|
||||
for(auto current: voxels) {
|
||||
str.write(reinterpret_cast<char *>(¤t.Density), sizeof(current.Density));
|
||||
str.write(reinterpret_cast<char *>(¤t.Material), sizeof(current.Material));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
std::optional<Faces> Chunk::update() {
|
||||
|
@ -94,37 +92,37 @@ void Chunk::set(ushort idx, const Voxel& val) {
|
|||
((!getNeighborIdx(idx, Face::Backward).has_value()) & Faces::Backward));
|
||||
}
|
||||
|
||||
std::optional<ushort> Chunk::getNeighborIdx(ushort idx, Face face) {
|
||||
switch (face) {
|
||||
std::optional<chunk_voxel_idx> Chunk::getNeighborIdx(chunk_voxel_idx idx, Face dir) {
|
||||
switch (dir) {
|
||||
case Face::Forward:
|
||||
if (idx % CHUNK_LENGTH >= CHUNK_LENGTH - 1)
|
||||
if (idx % glm::IDX_LENGTH >= glm::IDX_LENGTH - 1)
|
||||
return {};
|
||||
return idx + 1;
|
||||
|
||||
case Face::Backward:
|
||||
if (idx % CHUNK_LENGTH <= 0)
|
||||
if (idx % glm::IDX_LENGTH <= 0)
|
||||
return {};
|
||||
return idx - 1;
|
||||
|
||||
case Face::Up:
|
||||
if ((idx / CHUNK_LENGTH) % CHUNK_LENGTH >= CHUNK_LENGTH - 1)
|
||||
if ((idx / glm::IDX_LENGTH) % glm::IDX_LENGTH >= glm::IDX_LENGTH - 1)
|
||||
return {};
|
||||
return idx + CHUNK_LENGTH;
|
||||
return idx + glm::IDX_LENGTH;
|
||||
|
||||
case Face::Down:
|
||||
if ((idx / CHUNK_LENGTH) % CHUNK_LENGTH <= 0)
|
||||
if ((idx / glm::IDX_LENGTH) % glm::IDX_LENGTH <= 0)
|
||||
return {};
|
||||
return idx - CHUNK_LENGTH;
|
||||
return idx - glm::IDX_LENGTH;
|
||||
|
||||
case Face::Right:
|
||||
if (idx / CHUNK_LENGTH2 >= CHUNK_LENGTH - 1)
|
||||
if (idx / glm::IDX_LENGTH2 >= glm::IDX_LENGTH - 1)
|
||||
return {};
|
||||
return idx + CHUNK_LENGTH2;
|
||||
return idx + glm::IDX_LENGTH2;
|
||||
|
||||
case Face::Left:
|
||||
if (idx / CHUNK_LENGTH2 <= 0)
|
||||
if (idx / glm::IDX_LENGTH2 <= 0)
|
||||
return {};
|
||||
return idx - CHUNK_LENGTH2;
|
||||
return idx - glm::IDX_LENGTH2;
|
||||
|
||||
default:
|
||||
return {};
|
||||
|
|
|
@ -1,21 +1,25 @@
|
|||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <sstream>
|
||||
#include "Generator.hpp"
|
||||
#include "Voxel.hpp"
|
||||
#include "../data/geometry/Faces.hpp"
|
||||
#include <sstream>
|
||||
#include "../data/math.hpp"
|
||||
|
||||
/// Chunk length
|
||||
#define CHUNK_LENGTH2 (CHUNK_LENGTH * CHUNK_LENGTH)
|
||||
#define CHUNK_SIZE (CHUNK_LENGTH2 * CHUNK_LENGTH)
|
||||
|
||||
namespace world {
|
||||
const auto CHUNK_LENGTH2 = CHUNK_LENGTH * CHUNK_LENGTH;
|
||||
const auto CHUNK_SIZE = CHUNK_LENGTH2 * CHUNK_LENGTH;
|
||||
|
||||
constexpr auto RLE = true; //NOTE: only 2.7% gain after zstd
|
||||
using namespace geometry;
|
||||
/// World part as linear 3d voxel array
|
||||
struct Chunk {
|
||||
class Chunk {
|
||||
public:
|
||||
Chunk() {}
|
||||
Chunk(const chunk_pos& pos, Generator& rnd);
|
||||
Chunk(std::istream& str);
|
||||
Chunk(std::istream& str, bool rle = RLE);
|
||||
~Chunk();
|
||||
|
||||
/// Update voxels
|
||||
|
@ -29,42 +33,31 @@ namespace world {
|
|||
modified = true;
|
||||
}
|
||||
// Get voxel from index
|
||||
inline const Voxel& get(ushort idx) const {
|
||||
inline const Voxel& get(chunk_voxel_idx idx) const {
|
||||
return voxels[idx];
|
||||
}
|
||||
// Get voxel from position
|
||||
inline const Voxel& getAt(const chunk_voxel_pos& pos) const {
|
||||
return get(getIdx(pos));
|
||||
return get(glm::toIdx(pos));
|
||||
}
|
||||
// Set voxel from index
|
||||
void set(ushort idx, const Voxel& val);
|
||||
void set(chunk_voxel_idx idx, const Voxel& val);
|
||||
// Set voxel from position
|
||||
void setAt(const chunk_voxel_pos& pos, const Voxel& val) {
|
||||
set(getIdx(pos), val);
|
||||
set(glm::toIdx(pos), val);
|
||||
}
|
||||
// Break voxel
|
||||
Item breakAt(const chunk_voxel_pos& pos, const Voxel& val) {
|
||||
const auto idx = getIdx(pos);
|
||||
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};
|
||||
return {Item{res.density(), res.material()}}; //TODO: materials break table
|
||||
}
|
||||
// Is player modified
|
||||
inline bool isModified() const { return modified; }
|
||||
// Write to file.
|
||||
// Using RLE
|
||||
void write(std::ostream& str) const;
|
||||
void write(std::ostream& str, bool rle = RLE) const;
|
||||
|
||||
static inline chunk_voxel_pos getPosition(ushort idx) {
|
||||
return chunk_voxel_pos(idx / CHUNK_LENGTH2, (idx / CHUNK_LENGTH) % CHUNK_LENGTH, idx % CHUNK_LENGTH);
|
||||
}
|
||||
static inline ushort getIdx(chunk_voxel_pos pos) {
|
||||
return getIdx(pos.x, pos.y, pos.z);
|
||||
}
|
||||
static inline ushort getIdx(uint x, uint y, uint z) {
|
||||
return (x * CHUNK_LENGTH + y) * CHUNK_LENGTH + z;
|
||||
}
|
||||
static std::optional<ushort> getNeighborIdx(ushort idx, Face dir);
|
||||
static std::optional<chunk_voxel_idx> getNeighborIdx(chunk_voxel_idx idx, Face dir);
|
||||
|
||||
private:
|
||||
/// Chunk data
|
||||
|
@ -76,4 +69,7 @@ namespace world {
|
|||
/// Modified by player
|
||||
bool modified = false;
|
||||
};
|
||||
|
||||
/// Chunk full of air
|
||||
static const std::shared_ptr<const Chunk> EMPTY_CHUNK = std::make_shared<Chunk>();
|
||||
}
|
||||
|
|
|
@ -1,6 +1,10 @@
|
|||
#pragma once
|
||||
|
||||
#if HASTY
|
||||
#include <hastyNoise.h>
|
||||
#else
|
||||
#include <FastNoiseSIMD.h>
|
||||
#endif
|
||||
#include <tuple>
|
||||
#include "position.h"
|
||||
|
||||
|
@ -8,34 +12,64 @@ namespace world {
|
|||
/// Noise generator
|
||||
class Generator {
|
||||
public:
|
||||
Generator(int seed = 42) {
|
||||
using set = float *;
|
||||
#if HASTY
|
||||
using noise = std::unique_ptr<HastyNoise::NoiseSIMD>;
|
||||
#else
|
||||
using noise = FastNoiseSIMD *;
|
||||
#endif
|
||||
Generator(int seed = 42): seed(seed) {
|
||||
#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;
|
||||
inline int getSeed() const { return seed; }
|
||||
|
||||
private:
|
||||
int seed;
|
||||
noise densityNoise;
|
||||
noise materialNoise;
|
||||
};
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
#include "Universe.hpp"
|
||||
|
||||
#include <Remotery.h>
|
||||
#include <Remotery.h> // NOLINT
|
||||
#include <filesystem>
|
||||
|
||||
#include "../contouring/Dummy.hpp"
|
||||
|
@ -8,7 +8,9 @@
|
|||
|
||||
using namespace world;
|
||||
|
||||
Universe::Universe(const Universe::options &options): generator(42), regionDict("content/zstd.dict"), contouring(std::make_shared<contouring::Dummy>()) {
|
||||
const auto AREAS_FILE = "/areas.idx";
|
||||
|
||||
Universe::Universe(const Universe::options &options): dicts("content/zstd.dict"), contouring(std::make_shared<contouring::Dummy>()) {
|
||||
setOptions(options);
|
||||
folderPath = options.folderPath;
|
||||
struct vec_istream: std::streambuf {
|
||||
|
@ -17,223 +19,361 @@ Universe::Universe(const Universe::options &options): generator(42), regionDict(
|
|||
}
|
||||
};
|
||||
running = true;
|
||||
std::filesystem::create_directories(folderPath);
|
||||
|
||||
// Load workers
|
||||
std::filesystem::create_directories(folderPath);
|
||||
{
|
||||
std::ifstream index(folderPath + AREAS_FILE);
|
||||
if(index.good()) {
|
||||
size_t size = 0;
|
||||
index.read(reinterpret_cast<char *>(&size), sizeof(size));
|
||||
std::map<size_t, Area::params> tmp;
|
||||
while(!index.eof()) {
|
||||
size_t id = UINT32_MAX;
|
||||
index.read(reinterpret_cast<char *>(&id), sizeof(size_t));
|
||||
Area::params params{voxel_pos(0), 0};
|
||||
index.read(reinterpret_cast<char *>(¶ms.center.x), sizeof(voxel_pos::value_type));
|
||||
index.read(reinterpret_cast<char *>(¶ms.center.y), sizeof(voxel_pos::value_type));
|
||||
index.read(reinterpret_cast<char *>(¶ms.center.z), sizeof(voxel_pos::value_type));
|
||||
index.read(reinterpret_cast<char *>(¶ms.radius), sizeof(int));
|
||||
index.read(reinterpret_cast<char *>(¶ms.seed), sizeof(int));
|
||||
[[maybe_unused]]
|
||||
auto ok = tmp.emplace(id, params).second;
|
||||
assert(ok && "Duplicated area");
|
||||
index.peek();
|
||||
}
|
||||
assert(tmp.size() == size && "Corrupted areas index");
|
||||
far_areas = data::generational::vector<Area::params>(tmp);
|
||||
LOG_D(far_areas.size() << " areas loaded");
|
||||
} else {
|
||||
LOG_E("No index file!!! Probably a new world...");
|
||||
//TODO: generate universe
|
||||
far_areas.emplace(Area::params{voxel_pos(0), 1 << 20});
|
||||
far_areas.emplace(Area::params{voxel_pos(0), 1, 43});
|
||||
}
|
||||
index.close();
|
||||
}
|
||||
|
||||
entities.emplace(nullptr, glm::vec3(1), glm::vec3(2));
|
||||
|
||||
// Workers
|
||||
for (size_t i = 0; i < 4; i++) {
|
||||
loadWorkers.emplace_back([&] {
|
||||
const auto ctx = regionDict.make_reader();
|
||||
workers.emplace_back([&] {
|
||||
const auto read_ctx = dicts.make_reader();
|
||||
const auto write_ctx = dicts.make_writer();
|
||||
while (running) {
|
||||
chunk_pos pos;
|
||||
loadQueue.wait();
|
||||
if (loadQueue.pop(pos)) {
|
||||
if (std::pair<area_<chunk_pos>, std::shared_ptr<Area>> task; loadQueue.pop(task)) {
|
||||
//MAYBE: loadQueue.take to avoid duplicated work on fast move
|
||||
rmt_ScopedCPUSample(ProcessLoad, 0);
|
||||
const region_pos rPos = glm::divide(pos, region_chunk_pos(REGION_LENGTH));
|
||||
const region_chunk_pos cPos = glm::modulo(pos, region_chunk_pos(REGION_LENGTH));
|
||||
const auto reg = getRegion(rPos);
|
||||
const auto &pos = task.first;
|
||||
const auto rcPos = glm::split(pos.second);
|
||||
const auto reg = task.second->getRegion(folderPath, std::make_pair(pos.first, rcPos.first));
|
||||
Region::data data;
|
||||
if(reg->read(cPos, ctx, data)) {
|
||||
if(reg->read(rcPos.second, read_ctx, data)) {
|
||||
rmt_ScopedCPUSample(ProcessRead, 0);
|
||||
vec_istream idata(data);
|
||||
std::istream iss(&idata);
|
||||
loadedQueue.push({pos, std::make_shared<Chunk>(iss)});
|
||||
} else {
|
||||
rmt_ScopedCPUSample(ProcessGenerate, 0);
|
||||
loadedQueue.push({pos, std::make_shared<Chunk>(pos, generator)});
|
||||
loadedQueue.push({pos, std::make_shared<Chunk>(pos.second, task.second->getGenerator())});
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Save workers
|
||||
for (size_t i = 0; i < 2; i++) {
|
||||
saveWorkers.emplace_back([&] {
|
||||
const auto ctx = regionDict.make_writer();
|
||||
while (running) {
|
||||
robin_hood::pair<chunk_pos, std::shared_ptr<Chunk>> task;
|
||||
saveQueue.wait();
|
||||
if (saveQueue.pop(task) && task.second->isModified()) {
|
||||
} else if(save_task_t task; saveQueue.pop(task)) {
|
||||
//MAYBE: queue.take to avoid concurent write or duplicated work on fast move
|
||||
rmt_ScopedCPUSample(ProcessSave, 0);
|
||||
std::ostringstream out;
|
||||
task.second->write(out);
|
||||
const region_pos rPos = glm::divide(task.first, region_chunk_pos(REGION_LENGTH));
|
||||
const region_chunk_pos cPos = glm::modulo(task.first, region_chunk_pos(REGION_LENGTH));
|
||||
const auto reg = getRegion(rPos);
|
||||
reg->write(cPos, ctx, out.str());
|
||||
if(task.second.second->isModified()) {
|
||||
std::ostringstream out;
|
||||
task.second.second->write(out);
|
||||
const auto rcPos = glm::split(task.second.first);
|
||||
const auto reg = task.first.second->getRegion(folderPath, std::make_pair(task.first.first, rcPos.first));
|
||||
reg->write(rcPos.second, write_ctx, out.str());
|
||||
}
|
||||
} else {
|
||||
loadQueue.wait();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
Universe::~Universe() {
|
||||
contouring = NULL;
|
||||
contouring = nullptr;
|
||||
|
||||
// Save all
|
||||
for(auto& pair: chunks) {
|
||||
saveQueue.push(pair);
|
||||
for(auto& area: areas) {
|
||||
for(const auto& chunk: area.second->getChunks()) {
|
||||
saveQueue.emplace(area, chunk);
|
||||
}
|
||||
}
|
||||
loadQueue.notify_all();
|
||||
|
||||
if (auto size = saveQueue.size(); size > 0) {
|
||||
std::cout << std::endl;
|
||||
LOG_I("Saving " << size << " chunks");
|
||||
const auto SAVE_CHECK_TIME = 500;
|
||||
do {
|
||||
std::cout << "\rSaving... " << size << " " << std::flush;
|
||||
std::this_thread::sleep_for(std::chrono::microseconds(500));
|
||||
std::this_thread::sleep_for(std::chrono::microseconds(SAVE_CHECK_TIME));
|
||||
size = saveQueue.size();
|
||||
} while (size > 0);
|
||||
std::cout << std::endl;
|
||||
}
|
||||
saveAreas();
|
||||
|
||||
running = false;
|
||||
loadQueue.notify();
|
||||
saveQueue.notify();
|
||||
loadQueue.notify_all();
|
||||
|
||||
for (auto &worker: loadWorkers) {
|
||||
if (worker.joinable())
|
||||
worker.join();
|
||||
}
|
||||
for (auto &worker: saveWorkers) {
|
||||
for (auto &worker: workers) {
|
||||
if (worker.joinable())
|
||||
worker.join();
|
||||
}
|
||||
LOG_D("Universe disappeared");
|
||||
}
|
||||
|
||||
std::shared_ptr<Region> Universe::getRegion(const region_pos& pos) {
|
||||
std::shared_lock lock(regionMutex);
|
||||
const auto it = regionCache.find(pos);
|
||||
if(it == regionCache.end()) {
|
||||
lock.unlock();
|
||||
const auto reg = std::make_shared<Region>(folderPath, pos);
|
||||
std::unique_lock u_lock(regionMutex);
|
||||
return regionCache.insert({pos, reg}).first->second;
|
||||
} else {
|
||||
return it->second;
|
||||
// Write areas index (warn: file io)
|
||||
void Universe::saveAreas() const {
|
||||
std::ofstream index(folderPath + AREAS_FILE, std::ios::out | std::ios::binary);
|
||||
if(!index.good()) {
|
||||
LOG_E("Areas index write error");
|
||||
return;
|
||||
}
|
||||
{
|
||||
size_t size = areas.size() + far_areas.size();
|
||||
index.write(reinterpret_cast<char *>(&size), sizeof(size));
|
||||
}
|
||||
std::function write = [&](area_id id, Area::params params) {
|
||||
auto idx = id.index;
|
||||
index.write(reinterpret_cast<char *>(&idx), sizeof(size_t));
|
||||
index.write(reinterpret_cast<char *>(¶ms.center.x), sizeof(voxel_pos::value_type));
|
||||
index.write(reinterpret_cast<char *>(¶ms.center.y), sizeof(voxel_pos::value_type));
|
||||
index.write(reinterpret_cast<char *>(¶ms.center.z), sizeof(voxel_pos::value_type));
|
||||
index.write(reinterpret_cast<char *>(¶ms.radius), sizeof(int));
|
||||
index.write(reinterpret_cast<char *>(¶ms.seed), sizeof(int));
|
||||
};
|
||||
for(const auto& area: areas) {
|
||||
write(area.first, area.second->getParams());
|
||||
}
|
||||
far_areas.iter(write);
|
||||
if(!index.good())
|
||||
LOG_E("Areas index write error");
|
||||
|
||||
index.close();
|
||||
}
|
||||
|
||||
void Universe::update(const camera_pos& pos, Universe::report& rep) {
|
||||
const chunk_pos newPos = glm::divide(pos, chunk_voxel_pos(CHUNK_LENGTH));
|
||||
void Universe::update(const voxel_pos& pos, float deltaTime, Universe::report& rep) {
|
||||
rmt_ScopedCPUSample(Universe, 0);
|
||||
const chunk_pos newPos = glm::divide(pos);
|
||||
const auto chunkChange = last_pos != newPos;
|
||||
last_pos = newPos;
|
||||
rmt_ScopedCPUSample(Universe, 0);
|
||||
|
||||
// Update alive chunks
|
||||
{
|
||||
rmt_ScopedCPUSample(Update, 0);
|
||||
auto it = chunks.begin();
|
||||
while (it != chunks.end()) {
|
||||
if (glm::length2(last_pos - it->first) > keepDistance * keepDistance) {
|
||||
saveQueue.push(*it);
|
||||
it = chunks.erase(it);
|
||||
if(chunkChange) {
|
||||
rmt_ScopedCPUSample(Far, 0);
|
||||
far_areas.extract([&](area_id id, Area::params params){
|
||||
if (const chunk_pos diff = glm::divide(pos - params.center);
|
||||
glm::length2(diff) > glm::pow2(loadDistance + params.radius))
|
||||
return false;
|
||||
|
||||
LOG_I("Load area " << id.index);
|
||||
areas.emplace(id, std::make_shared<Area>(params));
|
||||
return true;
|
||||
});
|
||||
}
|
||||
{ // Update alive areas
|
||||
rmt_ScopedCPUSample(World, 0);
|
||||
size_t chunk_count = 0;
|
||||
size_t region_count = 0;
|
||||
const bool queuesEmpty = loadQueue.empty() && saveQueue.empty();
|
||||
bool allLazy = true;
|
||||
auto it = areas.begin();
|
||||
while (it != areas.end()) {
|
||||
rmt_ScopedCPUSample(Area, 0);
|
||||
const bool chunkChangeArea = (false && it->first == 1 && it->second->move(glm::vec3(deltaTime))) || chunkChange; // TODO: area.velocity
|
||||
const chunk_pos diff = glm::divide(pos - it->second->getOffset().as_voxel());
|
||||
auto &chunks = it->second->setChunks();
|
||||
if (glm::length2(diff) > glm::pow2(keepDistance + it->second->getChunks().getRadius())) {
|
||||
auto it_c = chunks.begin();
|
||||
while(it_c != chunks.end()) {
|
||||
saveQueue.emplace(*it, *it_c);
|
||||
it_c = chunks.erase(it_c);
|
||||
}
|
||||
LOG_I("Unload area " << it->first.index);
|
||||
[[maybe_unused]]
|
||||
auto ok = far_areas.put(it->first, it->second->getParams());
|
||||
assert(ok);
|
||||
it = areas.erase(it);
|
||||
saveAreas();
|
||||
} else {
|
||||
if (const auto neighbors = it->second->update()) {
|
||||
contouring->onUpdate(it->first, chunks, neighbors.value()); //TODO: get update update_type(simple(pos), complex)
|
||||
} else if (chunkChange) {
|
||||
contouring->onNotify(it->first, chunks);
|
||||
bool lazyArea = queuesEmpty;
|
||||
{ // Update alive chunks
|
||||
rmt_ScopedCPUSample(Alive, 0);
|
||||
auto it_c = chunks.begin();
|
||||
while(it_c != chunks.end()) {
|
||||
if (glm::length2(diff - it_c->first) > glm::pow2(keepDistance)) {
|
||||
saveQueue.emplace(*it, *it_c);
|
||||
lazyArea = false;
|
||||
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()) {
|
||||
contouring->onUpdate(acPos, diff, chunks, neighbors.value());
|
||||
} else if (chunkChangeArea) {
|
||||
contouring->onNotify(acPos, diff, chunks);
|
||||
}
|
||||
++it_c;
|
||||
chunk_count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (chunkChangeArea) { // Enqueue missing chunks
|
||||
rmt_ScopedCPUSample(Missing, 0);
|
||||
//TODO: need dist so no easy sphere fill
|
||||
for (int x = -loadDistance; x <= loadDistance; x++) {
|
||||
for (int y = -loadDistance; y <= loadDistance; y++) {
|
||||
for (int z = -loadDistance; z <= loadDistance; z++) {
|
||||
const auto dist2 = x * x + y * y + z * z;
|
||||
if (dist2 <= loadDistance * loadDistance) {
|
||||
const auto p = diff + chunk_pos(x, y, z);
|
||||
if (chunks.inRange(p) && chunks.find(p) == chunks.end()) {
|
||||
loadQueue.push(std::make_pair(it->first, p), it->second, -dist2);
|
||||
lazyArea = false;
|
||||
}
|
||||
}
|
||||
}}}
|
||||
}
|
||||
allLazy &= lazyArea;
|
||||
if (lazyArea) { // Clear un-used regions
|
||||
rmt_ScopedCPUSample(Region, 0);
|
||||
const auto unique = it->second->getRegions(); // MAYBE: shared then unique
|
||||
region_count += unique->size();
|
||||
for (auto it_r = unique->begin(); it_r != unique->end(); ++it_r) {
|
||||
if (glm::length2(diff - glm::lvec3(it_r->first) * glm::lvec3(REGION_LENGTH)) > glm::pow2(keepDistance + REGION_LENGTH * 2)) {
|
||||
unique->erase(it_r); //FIXME: may wait for os file access (long)
|
||||
break; //NOTE: save one only max per frame
|
||||
}
|
||||
}
|
||||
}
|
||||
++it;
|
||||
}
|
||||
}
|
||||
rep.chunk_count.push(chunk_count);
|
||||
rep.region_count.push(allLazy ? region_count : rep.region_count.current());
|
||||
}
|
||||
rep.chunk_load.push(loadQueue.size());
|
||||
rep.chunk_unload.push(saveQueue.size());
|
||||
{
|
||||
rmt_ScopedCPUSample(Contouring, 0);
|
||||
contouring->update(pos);
|
||||
contouring->update(pos, areas);
|
||||
//MAYBE: if(chunkChange) contouring->notify(chunks);
|
||||
}
|
||||
|
||||
// Find missing chunks
|
||||
if(chunkChange) {
|
||||
rmt_ScopedCPUSample(ToLoad, 0);
|
||||
//NOTE: need dist so no easy sphere fill
|
||||
for (int x = -loadDistance; x <= loadDistance; x++) {
|
||||
for (int y = -loadDistance; y <= loadDistance; y++) {
|
||||
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);
|
||||
if (chunks.find(p) == chunks.end()) {
|
||||
loadQueue.push(p, -dist2);
|
||||
}
|
||||
}
|
||||
}}}
|
||||
{ // Update entities
|
||||
rmt_ScopedCPUSample(Entities, 0);
|
||||
size_t entity_count = 0;
|
||||
entities.for_each([&](entity_id, Entity &val) {
|
||||
val.instances.remove([&](entity_id, Entity::Instance &inst) {
|
||||
entity_count++;
|
||||
inst.pos += inst.velocity * deltaTime;
|
||||
return glm::length2(glm::divide(pos - inst.pos.as_voxel())) > glm::pow2(keepDistance);
|
||||
});
|
||||
});
|
||||
rep.entity_count.push(entity_count);
|
||||
}
|
||||
|
||||
rep.chunk_load.push(loadQueue.size());
|
||||
// Loaded chunks
|
||||
{
|
||||
{ // Store loaded chunks
|
||||
rmt_ScopedCPUSample(Load, 0);
|
||||
robin_hood::pair<chunk_pos, std::shared_ptr<Chunk>> loaded;
|
||||
robin_hood::pair<area_<chunk_pos>, std::shared_ptr<Chunk>> loaded;
|
||||
while (loadedQueue.pop(loaded)) {
|
||||
chunks.insert(loaded);
|
||||
contouring->onUpdate(loaded.first, chunks, Faces::All);
|
||||
}
|
||||
}
|
||||
rep.chunk_count.push(chunks.size());
|
||||
|
||||
{
|
||||
rmt_ScopedCPUSample(Region, 0);
|
||||
std::unique_lock lock(regionMutex);
|
||||
const auto me = glm::divide(last_pos, glm::ivec3(REGION_LENGTH));
|
||||
for (auto it = regionCache.begin(); it != regionCache.end();) {
|
||||
if (glm::length2(it->first - me) > keepDistance) {
|
||||
std::cout << "rem" << std::endl;
|
||||
it = regionCache.erase(it);
|
||||
} else {
|
||||
it++;
|
||||
if (const auto it = areas.find(loaded.first.first); it != areas.end()) {
|
||||
auto &chunks = it->second->setChunks();
|
||||
chunks.emplace(loaded.first.second, loaded.second);
|
||||
const chunk_pos diff = glm::divide(pos - it->second->getOffset().as_voxel());
|
||||
contouring->onUpdate(loaded.first, diff, chunks, Faces::All);
|
||||
}
|
||||
}
|
||||
}
|
||||
rep.region_count.push(regionCache.size());
|
||||
}
|
||||
void Universe::setOptions(const Universe::options& options) {
|
||||
loadDistance = options.loadDistance;
|
||||
keepDistance = options.keepDistance;
|
||||
}
|
||||
|
||||
void Universe::setContouring(std::shared_ptr<contouring::Abstract> ct) {
|
||||
void Universe::setContouring(const std::shared_ptr<contouring::Abstract>& ct) {
|
||||
contouring = ct;
|
||||
last_pos = chunk_pos(INT_MAX); // trigger chunkChange on next update
|
||||
}
|
||||
|
||||
std::optional<std::pair<voxel_pos, Voxel>> Universe::raycast(const Ray &ray) const {
|
||||
std::optional<Universe::ray_target> Universe::raycast(const Ray &ray) const {
|
||||
//MAYBE: ray + offset to get float precision
|
||||
std::vector<voxel_pos> points;
|
||||
ray.grid(points);
|
||||
std::shared_ptr<Chunk> chunk = NULL;
|
||||
chunk_pos chunk_pos(INT_MAX);
|
||||
for(auto point: points) {
|
||||
const auto pos = glm::divide(point, glm::ivec3(CHUNK_LENGTH));
|
||||
if(pos != chunk_pos) {
|
||||
if(const auto& newChunk = at(pos)) {
|
||||
chunk = newChunk.value();
|
||||
chunk_pos = pos;
|
||||
std::optional<Universe::ray_target> target = std::nullopt;
|
||||
size_t dist = points.size();
|
||||
for(auto& area: areas) {
|
||||
if(ray.intersect(area.second->getBounding()) != IBox::ContainmentType::Disjoint) {
|
||||
const auto &offset = area.second->getOffset().as_voxel();
|
||||
const auto &chunks = area.second->getChunks();
|
||||
std::shared_ptr<Chunk> chunk = nullptr;
|
||||
chunk_pos chunk_vec(INT_MAX);
|
||||
for (size_t i = 0; i < dist; i++) {
|
||||
const auto pos = points[i] - offset;
|
||||
const chunk_pos cPos = glm::divide(pos);
|
||||
if(cPos != chunk_vec) {
|
||||
if (const auto it = chunks.find(cPos); it != chunks.end()) {
|
||||
chunk = it->second;
|
||||
chunk_vec = cPos;
|
||||
} else {
|
||||
chunk = nullptr;
|
||||
}
|
||||
}
|
||||
if(chunk != nullptr) {
|
||||
const auto voxel = chunk->getAt(glm::modulo(pos));
|
||||
if(voxel.density() > 0) {
|
||||
target = {ray_target{{area.first, pos}, voxel, offset}};
|
||||
dist = i;
|
||||
i = points.size();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if(chunk != NULL) {
|
||||
const auto voxel = chunk->getAt(glm::modulo(point, glm::uvec3(CHUNK_LENGTH)));
|
||||
if(voxel.Density > 0)
|
||||
return {{point, voxel}};
|
||||
}
|
||||
}
|
||||
return target;
|
||||
}
|
||||
|
||||
std::optional<Item> Universe::set(const area_<voxel_pos>& pos, const Voxel& val) {
|
||||
if(const auto it = areas.find(pos.first); it != areas.end()) {
|
||||
auto &chunks = it->second->setChunks();
|
||||
const auto split = glm::splitIdx(pos.second);
|
||||
if(chunks.inRange(split.first))
|
||||
if(const auto chunk = chunks.findInRange(split.first))
|
||||
return {chunk.value()->replace(split.second, val)};
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
std::optional<Item> Universe::set(const voxel_pos& pos, const Voxel& val) {
|
||||
const auto chunkPos = glm::divide(pos, glm::ivec3(CHUNK_LENGTH));
|
||||
if(const auto& chunk = at(chunkPos)) {
|
||||
return {chunk.value()->breakAt(glm::modulo(pos, glm::ivec3(CHUNK_LENGTH)), val)};
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
ItemList Universe::setCube(const voxel_pos& pos, const Voxel& val, int radius) {
|
||||
ItemList Universe::setCube(const area_<voxel_pos>& pos, const Voxel& val, int radius) {
|
||||
ItemList list;
|
||||
for (int z = -radius; z <= radius; z++) {
|
||||
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));
|
||||
}}}
|
||||
if(const auto it = areas.find(pos.first); it != areas.end()) {
|
||||
auto& chunks = it->second->setChunks();
|
||||
for (int z = -radius; z <= radius; z++) {
|
||||
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));
|
||||
if(chunks.inRange(split.first))
|
||||
if(const auto chunk = it->second->setChunks().findInRange(split.first))
|
||||
list.add(chunk.value()->replace(split.second, val));
|
||||
}}}
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
entity_instance_id Universe::addEntity(entity_id type, const Entity::Instance &instance) {
|
||||
return std::make_pair(type, entities.at(type).instances.push(instance));
|
||||
}
|
||||
void Universe::getEntitiesModels(std::vector<std::pair<std::vector<glm::mat4>, buffer::Abstract *const>> &buffers, const std::optional<geometry::Frustum> &frustum, const glm::llvec3 &offset, int density) {
|
||||
entities.iter([&](entity_id, const Entity &entity) {
|
||||
std::vector<glm::mat4> mats;
|
||||
entity.instances.iter([&](entity_id, const Entity::Instance &inst) {
|
||||
const glm::vec3 fPos = (glm::vec3(inst.pos.raw_as_long() - offset * glm::llvec3(density)) + inst.pos.offset) / glm::vec3(density);
|
||||
if (!frustum.has_value() || frustum.value().contains(geometry::Box::fromMin(fPos, entity.size)))
|
||||
mats.emplace_back(glm::scale(glm::translate(glm::mat4(1), fPos * (float)density), entity.scale));
|
||||
});
|
||||
if(!mats.empty())
|
||||
buffers.emplace_back(mats, entity.buffer);
|
||||
});
|
||||
}
|
|
@ -1,25 +1,23 @@
|
|||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <thread>
|
||||
#include <shared_mutex>
|
||||
#include "../data/math.hpp"
|
||||
#include "../data/safe_queue.hpp"
|
||||
#include "../data/safe_priority_queue.hpp"
|
||||
#include "../data/circular_buffer.hpp"
|
||||
#include "../data/geometry/Ray.hpp"
|
||||
#include "../data/geometry/Frustum.hpp"
|
||||
#include "forward.h"
|
||||
#include "Area.hpp"
|
||||
#include "Voxel.hpp"
|
||||
#include "region/index.hpp"
|
||||
#include "Generator.hpp"
|
||||
typedef glm::vec3 camera_pos;
|
||||
|
||||
#define REPORT_BUFFER_SIZE 128
|
||||
|
||||
namespace contouring {
|
||||
class Abstract;
|
||||
};
|
||||
namespace buffer {
|
||||
class Abstract;
|
||||
}
|
||||
|
||||
using namespace data;
|
||||
/// Universe data
|
||||
|
@ -39,33 +37,58 @@ namespace world {
|
|||
/// Reports to UI
|
||||
struct report {
|
||||
/// Chunks in memory
|
||||
circular_buffer<float> chunk_count = circular_buffer<float>(REPORT_BUFFER_SIZE, 0); // MAYBE: store int
|
||||
report_buffer chunk_count;
|
||||
/// Loaded chunks
|
||||
circular_buffer<float> chunk_load = circular_buffer<float>(REPORT_BUFFER_SIZE, 0);
|
||||
report_buffer chunk_load;
|
||||
/// Saved chunks
|
||||
circular_buffer<float> chunk_unload = circular_buffer<float>(REPORT_BUFFER_SIZE, 0);
|
||||
report_buffer chunk_unload;
|
||||
/// Regions in memory
|
||||
circular_buffer<float> region_count = circular_buffer<float>(REPORT_BUFFER_SIZE, 0);
|
||||
report_buffer region_count;
|
||||
/// Entity instances
|
||||
report_buffer entity_count;
|
||||
};
|
||||
|
||||
Universe(const options &);
|
||||
~Universe();
|
||||
|
||||
/// Update physics and contouring
|
||||
void update(const camera_pos& pos, report& rep);
|
||||
void update(const voxel_pos &pos, float deltaTime, report &rep);
|
||||
/// Apply new options
|
||||
void setOptions(const options &);
|
||||
|
||||
struct ray_target {
|
||||
area_<voxel_pos> pos;
|
||||
Voxel value;
|
||||
voxel_pos offset;
|
||||
};
|
||||
/// Get nearest voxel colliding ray
|
||||
/// @note ray in world scale
|
||||
std::optional<std::pair<voxel_pos, Voxel>> raycast(const geometry::Ray &ray) const;
|
||||
std::optional<ray_target> raycast(const geometry::Ray &ray) const;
|
||||
/// Set voxel at pos
|
||||
std::optional<Item> set(const voxel_pos &pos, const Voxel &val);
|
||||
std::optional<Item> set(const area_<voxel_pos> &pos, const Voxel &val);
|
||||
/// Set cube of voxel with pos as center
|
||||
ItemList setCube(const voxel_pos &pos, const Voxel &val, int radius);
|
||||
/// MAYBE: allow set multi area
|
||||
ItemList setCube(const area_<voxel_pos> &pos, const Voxel &val, int radius);
|
||||
|
||||
/// Entities commun properties
|
||||
struct Entity {
|
||||
Entity(buffer::Abstract* buffer, const glm::vec3& size = glm::vec3(1), const glm::vec3& scale = glm::vec3(1)):
|
||||
buffer(buffer), size(size), scale(scale) { };
|
||||
buffer::Abstract* buffer;
|
||||
glm::vec3 size;
|
||||
glm::vec3 scale;
|
||||
struct Instance {
|
||||
glm::ifvec3 pos;
|
||||
glm::vec3 velocity;
|
||||
};
|
||||
data::generational::vector<Instance> instances;
|
||||
};
|
||||
/// Instante entity
|
||||
entity_instance_id addEntity(entity_id type, const Entity::Instance &instance);
|
||||
void getEntitiesModels(std::vector<std::pair<std::vector<glm::mat4>, buffer::Abstract *const>> &buffers, const std::optional<geometry::Frustum> &frustum, const glm::llvec3 &offset, int density);
|
||||
|
||||
/// Change contouring worker
|
||||
void setContouring(std::shared_ptr<contouring::Abstract>);
|
||||
void setContouring(const std::shared_ptr<contouring::Abstract>& ct);
|
||||
/// Get current contouring worker
|
||||
std::shared_ptr<contouring::Abstract> getContouring() const {
|
||||
return contouring;
|
||||
|
@ -74,34 +97,28 @@ namespace world {
|
|||
private:
|
||||
chunk_pos last_pos = chunk_pos(INT_MAX);
|
||||
|
||||
/// Data
|
||||
chunk_map chunks;
|
||||
/// Alive areas containing chunks
|
||||
area_map areas;
|
||||
using area_it_t = robin_hood::pair<area_id, std::shared_ptr<Area>>;
|
||||
/// Dead areas
|
||||
data::generational::vector<Area::params> far_areas;
|
||||
void saveAreas() const;
|
||||
|
||||
std::optional<std::shared_ptr<Chunk>> at(const chunk_pos& pos) const {
|
||||
const auto it = chunks.find(pos);
|
||||
if(it == chunks.end())
|
||||
return {};
|
||||
return {it->second};
|
||||
}
|
||||
|
||||
Generator generator;
|
||||
data::generational::vector<Entity> entities;
|
||||
|
||||
bool running = true;
|
||||
std::vector<std::thread> loadWorkers;
|
||||
safe_priority_queue<chunk_pos, int> loadQueue;
|
||||
safe_queue<robin_hood::pair<chunk_pos, std::shared_ptr<Chunk>>> loadedQueue;
|
||||
std::vector<std::thread> workers;
|
||||
safe_priority_queue_map<area_<chunk_pos>, std::shared_ptr<Area>, int, area_hash> loadQueue; //NOTE: consider Area const (getRegion uses mutex)
|
||||
safe_queue<robin_hood::pair<area_<chunk_pos>, std::shared_ptr<Chunk>>> loadedQueue;
|
||||
|
||||
std::vector<std::thread> saveWorkers;
|
||||
safe_queue<robin_hood::pair<chunk_pos, std::shared_ptr<Chunk>>> saveQueue; //NOTE: consider const Chunk
|
||||
using save_task_t = std::pair<area_it_t, robin_hood::pair<chunk_pos, std::shared_ptr<Chunk>>>;
|
||||
data::safe_queue<save_task_t> saveQueue; //NOTE: consider Area and Chunk const
|
||||
|
||||
int loadDistance;
|
||||
int keepDistance;
|
||||
std::string folderPath;
|
||||
|
||||
std::shared_mutex regionMutex; //MAYBE: shared_guard
|
||||
robin_hood::unordered_map<region_pos, std::shared_ptr<Region>> regionCache;
|
||||
dict_set regionDict;
|
||||
std::shared_ptr<Region> getRegion(const region_pos &);
|
||||
dict_set dicts;
|
||||
|
||||
/// Contouring worker
|
||||
std::shared_ptr<contouring::Abstract> contouring;
|
||||
|
|
|
@ -5,13 +5,35 @@
|
|||
namespace world {
|
||||
/// Universe unit
|
||||
struct Voxel {
|
||||
/// Packed value
|
||||
/// swap(1) + material(12) + density(3)
|
||||
uint16_t value;
|
||||
using material_t = uint_fast16_t;
|
||||
using density_t = uint_fast8_t;
|
||||
constexpr static const density_t DENSITY_MAX = 0b0111;
|
||||
|
||||
Voxel(uint16_t value = 0): value(value) { }
|
||||
Voxel(material_t material, density_t density, bool swap = false) {
|
||||
assert(density <= DENSITY_MAX);
|
||||
assert(material < (1 << 12));
|
||||
value = (swap & 0b1000'0000'0000'0000) |
|
||||
((material << 3) & 0b0111'1111'1111'1000) |
|
||||
(density & DENSITY_MAX);
|
||||
}
|
||||
|
||||
/// Quantity of material
|
||||
/// FIXME: low density area are cheatty
|
||||
/// @note v < iso * UCHAR_MAX are useless
|
||||
unsigned char Density;
|
||||
constexpr inline density_t density() const {
|
||||
return value & DENSITY_MAX;
|
||||
}
|
||||
/// Material type
|
||||
/// @see world::materials
|
||||
unsigned short Material;
|
||||
constexpr inline material_t material() const {
|
||||
return (value & 0b0111'1111'1111'1000) >> 3;
|
||||
}
|
||||
/// Swap value
|
||||
/// Use external metadata table
|
||||
constexpr inline bool swap() const {
|
||||
return value & 0b1000'0000'0000'0000;
|
||||
}
|
||||
};
|
||||
/// Stock of material
|
||||
struct Item {
|
||||
|
@ -19,7 +41,7 @@ namespace world {
|
|||
unsigned long long Count;
|
||||
/// Material type
|
||||
/// @see world::materials
|
||||
unsigned short Material;
|
||||
Voxel::material_t Material;
|
||||
};
|
||||
/// List of materials
|
||||
struct ItemList: std::map<unsigned short, unsigned long long> {
|
||||
|
|
|
@ -1,9 +1,12 @@
|
|||
#pragma once
|
||||
|
||||
#include "../data/safe_queue.hpp"
|
||||
#include <robin_hood.h>
|
||||
#include "position.h"
|
||||
|
||||
namespace world {
|
||||
class Chunk;
|
||||
typedef robin_hood::unordered_map<chunk_pos, std::shared_ptr<Chunk>> chunk_map;
|
||||
class Area;
|
||||
using area_map = robin_hood::unordered_map<area_id, std::shared_ptr<Area>>;
|
||||
class ChunkContainer;
|
||||
}
|
|
@ -6,6 +6,7 @@
|
|||
namespace world::materials {
|
||||
/// Materials count
|
||||
static const auto count = 9;
|
||||
static_assert(count < (USHRT_MAX >> 4)); //NOTE: for byte packing see Voxel
|
||||
/// Materials textures
|
||||
static const std::array<std::string, count> textures = {{"Air", "Sand", "Dirt", "Stone_path", "Mapl", "Seaside_rock", "Stone_wall", "Rough_rock", "Alien"}};
|
||||
/// Materials roughness.
|
||||
|
|
|
@ -1,12 +1,53 @@
|
|||
/**
|
||||
* chunk_voxel_pos: u8 (contains u5: CHUNK_LENGTH)
|
||||
* chunk_voxel_idx: u16
|
||||
* chunk_pos: i56
|
||||
* region_chunk_pos: u8 (contains u5: REGION_LENGTH)
|
||||
* region_chunk_idx: u16
|
||||
* 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 <functional>
|
||||
#include "../data/glm.hpp"
|
||||
#include "../data/generational.hpp"
|
||||
|
||||
#define CHUNK_LENGTH 32
|
||||
#define REGION_LENGTH 32
|
||||
const auto CHUNK_LENGTH = glm::IDX_LENGTH;
|
||||
const auto REGION_LENGTH = glm::IDX_LENGTH;
|
||||
|
||||
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 chunk_voxel_idx = glm::u16;
|
||||
using region_pos = glm::ivec3;
|
||||
using region_chunk_pos = glm::ucvec3;
|
||||
using region_chunk_idx = glm::u16;
|
||||
|
||||
using area_id = data::generational::id;
|
||||
template <class pos>
|
||||
using area_ = std::pair<area_id, pos>;
|
||||
struct area_hash {
|
||||
template <typename pos>
|
||||
std::size_t operator()(area_<pos> const& a) const noexcept {
|
||||
std::size_t h1 = std::hash<area_id>{}(a.first);
|
||||
std::size_t h2 = std::hash<pos>{}(a.second);
|
||||
return h1 ^ (h2 << 1);
|
||||
}
|
||||
};
|
||||
using area_pos = glm::ifvec3;
|
||||
|
||||
using entity_id = data::generational::id;
|
||||
using entity_instance_id = std::pair<entity_id, data::generational::id>;
|
||||
|
||||
using camera_pos = glm::ifvec3;
|
||||
|
|
|
@ -6,9 +6,9 @@ using namespace world;
|
|||
|
||||
#define REMOVE_CORRUPTED 1
|
||||
|
||||
FileRegion::FileRegion(const std::string &folderPath, const region_pos &pos) {
|
||||
path = folderPath + '/' + std::to_string(pos.x) + '.' +
|
||||
std::to_string(pos.y) + '.' + std::to_string(pos.z) + ".map";
|
||||
FileRegion::FileRegion(const std::string &folderPath, const area_<region_pos> &pos) {
|
||||
path = folderPath + '/' + std::to_string(pos.first.index) + '.' + std::to_string(pos.second.x) + '.' +
|
||||
std::to_string(pos.second.y) + '.' + std::to_string(pos.second.z) + ".map";
|
||||
|
||||
load();
|
||||
}
|
||||
|
@ -45,14 +45,14 @@ void FileRegion::load() {
|
|||
|
||||
// Ignore content
|
||||
if(!index.insert({pos, std::make_pair(size, file.tellg())}).second) {
|
||||
std::cout << "Duplicated chunk: " << path << ":" << (int)pos.x << "." << (int)pos.y << "." << (int)pos.z << std::endl;
|
||||
LOG_E("Duplicated chunk: " << path << ":" << (int)pos.x << "." << (int)pos.y << "." << (int)pos.z);
|
||||
}
|
||||
file.ignore(size);
|
||||
file.peek();
|
||||
}
|
||||
|
||||
if(file.bad()) {
|
||||
std::cout << "region corrupted read " << path << std::endl;
|
||||
LOG_E("region corrupted read " << path);
|
||||
}
|
||||
|
||||
assert(index.size() == chunkCount);
|
||||
|
@ -73,10 +73,10 @@ bool FileRegion::read(const region_chunk_pos& pos, const read_ctx& ctx, data& ou
|
|||
out.resize(maxSize);
|
||||
const auto actualSize = ZSTD_decompress_usingDDict(ctx.ctx, out.data(), out.size(), in->data(), in->size(), ctx.dict);
|
||||
if(ZSTD_isError(actualSize)) {
|
||||
std::cout << "Corrupted region chunk: " << path << ":" << (int)pos.x << "." << (int)pos.y << "." << (int)pos.z << " "
|
||||
<< ZSTD_getErrorName(actualSize) << std::endl;
|
||||
LOG_E("Corrupted region chunk: " << path << ":" << (int)pos.x << "." << (int)pos.y << "." << (int)pos.z << " "
|
||||
<< ZSTD_getErrorName(actualSize));
|
||||
#ifdef REMOVE_CORRUPTED
|
||||
std::cout << "Removing" << std::endl;
|
||||
LOG_W("Removing");
|
||||
index.erase(it);
|
||||
lock.unlock();
|
||||
save(std::nullopt);
|
||||
|
@ -93,8 +93,8 @@ void FileRegion::write(const region_chunk_pos& pos, const write_ctx& ctx, const
|
|||
|
||||
const auto actualSize = ZSTD_compress_usingCDict(ctx.ctx, buffer->data(), buffer->capacity(), in.data(), in.size(), ctx.dict);
|
||||
if (ZSTD_isError(actualSize)) {
|
||||
std::cout << "Corrupted chunk save: " << path << ":" << (int)pos.x << "." << (int)pos.y << "." << (int)pos.z << " "
|
||||
<< ZSTD_getErrorName(actualSize) << std::endl;
|
||||
LOG_E("Corrupted chunk save: " << path << ":" << (int)pos.x << "." << (int)pos.y << "." << (int)pos.z << " "
|
||||
<< ZSTD_getErrorName(actualSize));
|
||||
return;
|
||||
}
|
||||
buffer->resize(actualSize);
|
||||
|
@ -108,7 +108,7 @@ void FileRegion::save(std::optional<std::pair<region_chunk_pos, std::unique_ptr<
|
|||
|
||||
std::ofstream tmpFile(tmpPath, std::ios::out | std::ios::binary);
|
||||
if (!tmpFile.good()) {
|
||||
std::cout << "Corrupted region path: " << tmpPath << std::endl;
|
||||
LOG_E("Corrupted region path: " << tmpPath);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -165,7 +165,7 @@ void FileRegion::save(std::optional<std::pair<region_chunk_pos, std::unique_ptr<
|
|||
}
|
||||
|
||||
if (!tmpFile.good()) {
|
||||
std::cout << "region corrupted write " << tmpPath << std::endl;
|
||||
LOG_E("Region corrupted write " << tmpPath);
|
||||
tmpFile.close();
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -11,7 +11,7 @@ namespace world {
|
|||
///Group of chunks saved as a single file only pointer
|
||||
class FileRegion {
|
||||
public:
|
||||
FileRegion(const std::string& folderPath, const region_pos &pos);
|
||||
FileRegion(const std::string& folderPath, const area_<region_pos> &pos);
|
||||
~FileRegion();
|
||||
|
||||
typedef std::vector<char> data;
|
||||
|
@ -27,7 +27,7 @@ namespace world {
|
|||
|
||||
std::shared_mutex mutex;
|
||||
std::ifstream file;
|
||||
robin_hood::unordered_flat_map<region_chunk_pos, std::pair<ushort, std::streampos>> index;
|
||||
robin_hood::unordered_map<region_chunk_pos, std::pair<ushort, std::streampos>> index;
|
||||
|
||||
void load();
|
||||
};
|
||||
|
|
|
@ -5,9 +5,9 @@ using namespace world;
|
|||
#define REMOVE_CORRUPTED 1
|
||||
#define LAZYNESS 8
|
||||
|
||||
MemoryRegion::MemoryRegion(const std::string &folderPath, const region_pos &pos) {
|
||||
path = folderPath + '/' + std::to_string(pos.x) + '.' +
|
||||
std::to_string(pos.y) + '.' + std::to_string(pos.z) + ".map";
|
||||
MemoryRegion::MemoryRegion(const std::string &folderPath, const area_<region_pos> &pos) {
|
||||
path = folderPath + '/' + std::to_string(pos.first.index) + '.' + std::to_string(pos.second.x) + '.' +
|
||||
std::to_string(pos.second.y) + '.' + std::to_string(pos.second.z) + ".map";
|
||||
|
||||
load();
|
||||
}
|
||||
|
@ -57,13 +57,13 @@ void MemoryRegion::load() {
|
|||
data->resize(size);
|
||||
file.read(data->data(), data->size());
|
||||
if(!content.insert({pos, data}).second) {
|
||||
std::cout << "Duplicated chunk: " << path << ":" << (int)pos.x << "." << (int)pos.y << "." << (int)pos.z << std::endl;
|
||||
LOG_E("Duplicated chunk: " << path << ":" << (int)pos.x << "." << (int)pos.y << "." << (int)pos.z);
|
||||
}
|
||||
file.peek();
|
||||
}
|
||||
|
||||
if(file.bad()) {
|
||||
std::cout << "region corrupted read " << path << std::endl;
|
||||
LOG_E("Region corrupted read " << path);
|
||||
}
|
||||
assert(content.size() == chunkCount);
|
||||
file.close();
|
||||
|
@ -81,10 +81,10 @@ bool MemoryRegion::read(const region_chunk_pos& pos, const read_ctx& ctx, data&
|
|||
out.resize(maxSize);
|
||||
const auto actualSize = ZSTD_decompress_usingDDict(ctx.ctx, out.data(), out.size(), in->data(), in->size(), ctx.dict);
|
||||
if(ZSTD_isError(actualSize)) {
|
||||
std::cout << "Corrupted region chunk: " << path << ":" << (int)pos.x << "." << (int)pos.y << "." << (int)pos.z << " "
|
||||
<< ZSTD_getErrorName(actualSize) << std::endl;
|
||||
LOG_E("Corrupted region chunk: " << path << ":" << (int)pos.x << "." << (int)pos.y << "." << (int)pos.z << " "
|
||||
<< ZSTD_getErrorName(actualSize));
|
||||
#ifdef REMOVE_CORRUPTED
|
||||
std::cout << "Removing" << std::endl;
|
||||
LOG_W("Removing");
|
||||
lock.unlock();
|
||||
{
|
||||
std::unique_lock ulock(mutex);
|
||||
|
@ -104,8 +104,8 @@ void MemoryRegion::write(const region_chunk_pos& pos, const write_ctx& ctx, cons
|
|||
|
||||
const auto actualSize = ZSTD_compress_usingCDict(ctx.ctx, buffer->data(), buffer->capacity(), in.data(), in.size(), ctx.dict);
|
||||
if (ZSTD_isError(actualSize)) {
|
||||
std::cout << "Corrupted chunk save: " << path << ":" << (int)pos.x << "." << (int)pos.y << "." << (int)pos.z << " "
|
||||
<< ZSTD_getErrorName(actualSize) << std::endl;
|
||||
LOG_E("Corrupted chunk save: " << path << ":" << (int)pos.x << "." << (int)pos.y << "." << (int)pos.z << " "
|
||||
<< ZSTD_getErrorName(actualSize));
|
||||
return;
|
||||
}
|
||||
buffer->resize(actualSize);
|
||||
|
@ -134,7 +134,7 @@ void MemoryRegion::save(bool force) {
|
|||
|
||||
std::ofstream file(path, std::ios::out | std::ios::binary);
|
||||
if (!file.good()) {
|
||||
std::cout << "Corrupted region path: " << path << std::endl;
|
||||
LOG_E("Corrupted region path: " << path);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -169,7 +169,7 @@ void MemoryRegion::save(bool force) {
|
|||
}
|
||||
|
||||
if (!file.good()) {
|
||||
std::cout << "region corrupted write " << path << std::endl;
|
||||
LOG_E("Region corrupted write " << path);
|
||||
file.close();
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -12,7 +12,7 @@ namespace world {
|
|||
///Group of chunks saved as a single file in memory
|
||||
class MemoryRegion {
|
||||
public:
|
||||
MemoryRegion(const std::string& folderPath, const region_pos &pos);
|
||||
MemoryRegion(const std::string& folderPath, const area_<region_pos> &pos);
|
||||
~MemoryRegion();
|
||||
|
||||
typedef std::vector<char> data;
|
||||
|
@ -27,7 +27,7 @@ namespace world {
|
|||
//TODO: use tickets to remove unused regions
|
||||
|
||||
std::shared_mutex mutex;
|
||||
robin_hood::unordered_flat_map<region_chunk_pos, data*> content;
|
||||
robin_hood::unordered_map<region_chunk_pos, data*> content;
|
||||
bool changed = false;
|
||||
|
||||
void load();
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
#pragma once
|
||||
|
||||
#include <zstd.h>
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <vector>
|
||||
#include <cassert>
|
||||
#include "../../data/logger.hpp"
|
||||
|
||||
namespace world {
|
||||
struct read_ctx {
|
||||
|
@ -27,7 +27,7 @@ namespace world {
|
|||
dict_set(const std::string& path) {
|
||||
std::ifstream is(path, std::ios::in | std::ios::binary | std::ios::ate);
|
||||
if(!is.good()) {
|
||||
std::cout << "missing dict " << path << std::endl;
|
||||
LOG_E("Missing dict " << path);
|
||||
exit(1);
|
||||
}
|
||||
const auto end = is.tellg();
|
||||
|
|
|
@ -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;
|
||||
}
|
Loading…
Reference in New Issue