Skip to content

Commit

Permalink
Barebones edition, untested WIP
Browse files Browse the repository at this point in the history
  • Loading branch information
Zylann committed Sep 5, 2019
1 parent 2e1467e commit 4ab688c
Show file tree
Hide file tree
Showing 21 changed files with 656 additions and 58 deletions.
31 changes: 30 additions & 1 deletion math/rect3i.h
Expand Up @@ -4,6 +4,7 @@
#include "vector3i.h"
#include <core/variant.h>

// TODO Could be renamed to something more sensical, like Box3i
class Rect3i {

public:
Expand All @@ -28,6 +29,10 @@ class Rect3i {
return Rect3i(center - extents, 2 * extents);
}

static inline Rect3i from_min_max(Vector3i p_min, Vector3i p_max) {
return Rect3i(p_min, p_max - p_min);
}

static inline Rect3i get_bounding_box(Rect3i a, Rect3i b) {

Rect3i box;
Expand Down Expand Up @@ -100,7 +105,7 @@ class Rect3i {
}

template <typename A>
void difference(const Rect3i &b, A action) {
void difference(const Rect3i &b, A action) const {

if (!intersects(b)) {
action(*this);
Expand Down Expand Up @@ -156,6 +161,30 @@ class Rect3i {
action(Rect3i(a_rect_pos, a_rect_size));
}
}

inline Rect3i padded(int m) const {
return Rect3i(
pos.x - m,
pos.y - m,
pos.z - m,
size.x + 2 * m,
size.y + 2 * m,
size.z + 2 * m);
}

inline Rect3i downscaled(int step_size) const {
Rect3i o;
o.pos = pos.udiv(step_size);
Vector3i max_pos = (pos + size - Vector3i(1)).udiv(step_size);
o.size = max_pos - o.pos + Vector3i(1);
return o;
}

inline void clip(const Rect3i lim) {
const Vector3i max_pos = lim.pos + lim.size;
pos.clamp_to(lim.pos, max_pos);
size = Vector3i::min(size, max_pos - pos);
}
};

inline bool operator!=(const Rect3i &a, const Rect3i &b) {
Expand Down
8 changes: 8 additions & 0 deletions math/vector3i.h
Expand Up @@ -111,6 +111,10 @@ struct Vector3i {
sort_min_max(a.z, b.z);
}

inline Vector3i udiv(int d) const {
return Vector3i(::udiv(x, d), ::udiv(y, d), ::udiv(z, d));
}

inline Vector3i udiv(const Vector3i d) const {
return Vector3i(::udiv(x, d.x), ::udiv(y, d.y), ::udiv(z, d.z));
}
Expand All @@ -135,6 +139,10 @@ struct Vector3i {
return x == y && y == z;
}

static inline Vector3i min(const Vector3i a, const Vector3i b) {
return Vector3i(::min(a.x, b.x), ::min(a.y, b.y), ::min(a.z, b.z));
}

private:
static _FORCE_INLINE_ void sort_min_max(int &a, int &b) {
if (a > b) {
Expand Down
1 change: 1 addition & 0 deletions register_types.cpp
Expand Up @@ -42,6 +42,7 @@ void register_voxel_types() {
// Helpers
ClassDB::register_class<VoxelBoxMover>();
ClassDB::register_class<VoxelIsoSurfaceTool>();
ClassDB::register_class<VoxelTool>();

// Meshers
ClassDB::register_class<VoxelMesher>();
Expand Down
4 changes: 4 additions & 0 deletions terrain/voxel_block.cpp
Expand Up @@ -174,3 +174,7 @@ void VoxelBlock::set_parent_visible(bool parent_visible) {
_parent_visible = parent_visible;
_set_visible(_visible && _parent_visible);
}

void VoxelBlock::set_needs_lodding(bool need_lodding) {
_needs_lodding = need_lodding;
}
11 changes: 10 additions & 1 deletion terrain/voxel_block.h
Expand Up @@ -20,7 +20,9 @@ class VoxelBlock {
Ref<VoxelBuffer> voxels;
Vector3i position;
unsigned int lod_index = 0;
bool modified = false; // Indicates if this block should be saved

// Indicates if this block is different from the time it was loaded (should be saved)
bool modified = false;

static VoxelBlock *create(Vector3i bpos, Ref<VoxelBuffer> buffer, unsigned int size, unsigned int p_lod_index);

Expand All @@ -35,7 +37,11 @@ class VoxelBlock {
void mark_been_meshed();
bool has_been_meshed() const;

void set_needs_lodding(bool need_lodding);
inline bool get_needs_lodding() const { return _needs_lodding; }

void set_world(World *world);

void set_visible(bool visible);
bool is_visible() const;

Expand Down Expand Up @@ -63,6 +69,9 @@ class VoxelBlock {
// The mesh might be null, but we don't know if it's actually empty or if it's loading.
// This boolean tells if we attempted to mesh this block at least once.
bool _has_been_meshed = false;

// The block was edited, which requires its LOD counterparts to be recomputed
bool _needs_lodding = false;
};

#endif // VOXEL_BLOCK_H
2 changes: 1 addition & 1 deletion terrain/voxel_box_mover.cpp
Expand Up @@ -118,7 +118,7 @@ Vector3 VoxelBoxMover::get_motion(Vector3 pos, Vector3 motion, AABB aabb, VoxelT

// Collect collisions with the terrain

Ref<VoxelMap> voxels_ref = terrain->get_map();
Ref<VoxelMap> voxels_ref = terrain->get_storage();
ERR_FAIL_COND_V(voxels_ref.is_null(), Vector3());
VoxelMap &voxels = **voxels_ref;

Expand Down
104 changes: 96 additions & 8 deletions terrain/voxel_lod_terrain.cpp
Expand Up @@ -2,6 +2,7 @@
#include "../math/rect3i.h"
#include "../streams/voxel_stream_file.h"
#include "../util/profiling_clock.h"
#include "../voxel_tool_lod_terrain.h"
#include "voxel_map.h"
#include "voxel_mesh_updater.h"
#include <core/core_string_names.h>
Expand All @@ -22,6 +23,8 @@ VoxelLodTerrain::VoxelLodTerrain() {

_lods[0].map.instance();

// TODO Being able to set a LOD smaller than the stream is probably a bad idea,
// Because it prevents edits from propagating up to the last one, they will be left out of sync
set_lod_count(4);
set_lod_split_scale(3);
}
Expand Down Expand Up @@ -106,7 +109,10 @@ void VoxelLodTerrain::_on_stream_params_changed() {

// The whole map might change, so make all area dirty
// TODO Actually, we should regenerate the whole map, not just update all its blocks
make_all_view_dirty_deferred();
for (int i = 0; i < get_lod_count(); ++i) {
Lod &lod = _lods[i];
lod.last_view_distance_blocks = 0;
}
}

void VoxelLodTerrain::set_block_size_po2(unsigned int p_block_size_po2) {
Expand Down Expand Up @@ -144,13 +150,34 @@ void VoxelLodTerrain::_set_block_size_po2(int p_block_size_po2) {
_lods[0].map->create(p_block_size_po2, 0);
}

void VoxelLodTerrain::make_all_view_dirty_deferred() {
for (int i = 0; i < get_lod_count(); ++i) {
Lod &lod = _lods[i];
lod.last_view_distance_blocks = 0;
// Marks intersecting blocks in the area as modified, updates LODs and schedules remeshing.
// The provided box must be at LOD0 coordinates.
void VoxelLodTerrain::post_edit_area(Rect3i p_box) {

Rect3i box = p_box.padded(1);
Rect3i bbox = box.downscaled(get_block_size());

bbox.for_each_cell([this](Vector3i block_pos_lod0) {
post_edit_block_lod0(block_pos_lod0);
});
}

void VoxelLodTerrain::post_edit_block_lod0(Vector3i block_pos_lod0) {

Lod &lod0 = _lods[0];
VoxelBlock *block = lod0.map->get_block(block_pos_lod0);
ERR_FAIL_COND(block == nullptr);

if (!block->get_needs_lodding()) {
block->set_needs_lodding(true);
lod0.blocks_pending_lodding.push_back(block_pos_lod0);
}
}

Ref<VoxelTool> VoxelLodTerrain::get_voxel_tool() {
return Ref<VoxelToolLodTerrain>(memnew(VoxelToolLodTerrain(this, _lods[0].map)));
}

int VoxelLodTerrain::get_view_distance() const {
return 0;
}
Expand Down Expand Up @@ -559,15 +586,20 @@ void VoxelLodTerrain::_process() {
Vector3 viewer_pos = get_viewer_pos(viewer_direction);
Vector3i viewer_block_pos = _lods[0].map->voxel_to_block(viewer_pos);

ProfilingClock profiling_clock;

_stats.dropped_block_loads = 0;
_stats.dropped_block_meshs = 0;
_stats.blocked_lods = 0;

// Here we go...

// Remove blocks falling out of block region extent
// Update pending LOD data modifications due to edits.
// These are deferred from edits so we can batch them.
// It has to happen first because blocks can be unloaded afterwards.
flush_pending_lod_edits();

ProfilingClock profiling_clock;

// Unload blocks falling out of block region extent
// TODO Obsoleted by octree grid?
{
// TODO Could it actually be enough to have a rolling update on all blocks?
Expand Down Expand Up @@ -1075,6 +1107,60 @@ void VoxelLodTerrain::_process() {
_stats.time_process_update_responses = profiling_clock.restart();
}

void VoxelLodTerrain::flush_pending_lod_edits() {

// Only LOD0 is editable at the moment, so we'll downscale from there
Lod &lod0 = _lods[0];

for (int i = 0; i < lod0.blocks_pending_lodding.size(); ++i) {
Vector3i block_pos_lod0 = lod0.blocks_pending_lodding[i];

Vector3i bpos = block_pos_lod0;
int half_bs = get_block_size();
VoxelBlock *prev_block = nullptr;

for (int lod_index = 0; lod_index < _lod_count; ++lod_index) {

Lod &lod = _lods[lod_index];
VoxelBlock *block = lod.map->get_block(bpos);
// The block and its lower LODs are expected to be available.
// Otherwise it means the function was called too late
CRASH_COND(block == nullptr);

// Mark block for remesh
if (block->get_mesh_state() != VoxelBlock::MESH_UPDATE_NOT_SENT) {
block->set_mesh_state(VoxelBlock::MESH_UPDATE_NOT_SENT);
lod.blocks_pending_update.push_back(bpos);
}

// Mark block as modified
if (!block->modified) {
print_line(String("Marking block {0}[lod{1}] as modified").format(varray(bpos.to_vec3(), lod_index)));
block->modified = true;
}

block->set_needs_lodding(false);

if (lod_index != 0) {

Vector3i prev_bpos = bpos;
Vector3i rel = prev_bpos - ((bpos >> 2) << 2); // Yup, you read it right... could implement `&` for vectors tho

// Update lower LOD
// This must always be done after an edit before it gets saved, otherwise LODs won't match and it will look ugly.
VoxelBuffer *voxels = *block->voxels;
CRASH_COND(voxels == nullptr);
// TODO Try to narrow to edited region instead of taking whole block
prev_block->voxels->downscale_to(*voxels, Vector3i(), prev_block->voxels->get_size(), rel * half_bs);
}

prev_block = block;
}
}

lod0.blocks_pending_lodding.clear();
}

Dictionary VoxelLodTerrain::get_statistics() const {

Dictionary d;
Expand Down Expand Up @@ -1128,6 +1214,8 @@ void VoxelLodTerrain::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_statistics"), &VoxelLodTerrain::get_statistics);
ClassDB::bind_method(D_METHOD("voxel_to_block_position", "lod_index"), &VoxelLodTerrain::voxel_to_block_position);

ClassDB::bind_method(D_METHOD("get_voxel_tool"), &VoxelLodTerrain::get_voxel_tool);

ClassDB::bind_method(D_METHOD("_on_stream_params_changed"), &VoxelLodTerrain::_on_stream_params_changed);

ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "stream", PROPERTY_HINT_RESOURCE_TYPE, "VoxelStream"), "set_stream", "get_stream");
Expand Down
13 changes: 12 additions & 1 deletion terrain/voxel_lod_terrain.h
Expand Up @@ -2,6 +2,7 @@
#define VOXEL_LOD_TERRAIN_HPP

#include "../streams/voxel_stream.h"
#include "../voxel_tool.h"
#include "lod_octree.h"
#include "voxel_data_loader.h"
#include "voxel_map.h"
Expand Down Expand Up @@ -57,6 +58,12 @@ class VoxelLodTerrain : public Spatial {
unsigned int get_block_size_pow2() const;
void set_block_size_po2(unsigned int p_block_size_po2);

// These must be called after an edit
void post_edit_area(Rect3i p_box);
void post_edit_block_lod0(Vector3i bpos);

Ref<VoxelTool> get_voxel_tool();

struct Stats {
VoxelMeshUpdater::Stats updater;
VoxelDataLoader::Stats stream;
Expand All @@ -81,7 +88,6 @@ class VoxelLodTerrain : public Spatial {

private:
unsigned int get_block_size() const;
void make_all_view_dirty_deferred();
Spatial *get_viewer() const;
void immerge_block(Vector3i block_pos, int lod_index);

Expand All @@ -99,6 +105,8 @@ class VoxelLodTerrain : public Spatial {

void _on_stream_params_changed();

void flush_pending_lod_edits();

template <typename A>
void for_all_blocks(A &action) {
for (int lod_index = 0; lod_index < MAX_LOD; ++lod_index) {
Expand Down Expand Up @@ -144,6 +152,9 @@ class VoxelLodTerrain : public Spatial {
Set<Vector3i> loading_blocks;
std::vector<Vector3i> blocks_pending_update;

// Blocks that were edited and need their LOD counterparts to be updated
std::vector<Vector3i> blocks_pending_lodding;

// These are relative to this LOD, in block coordinates
Vector3i last_viewer_block_pos;
int last_view_distance_blocks = 0;
Expand Down

0 comments on commit 4ab688c

Please sign in to comment.