Remove BlockState and replace it with loading_blocks set and enum inside VoxelBlock

This commit is contained in:
Marc Gilleron 2019-05-09 19:11:17 +01:00
parent 73d5d9993c
commit cf7ece7a58
4 changed files with 57 additions and 81 deletions

View File

@ -9,9 +9,17 @@
// Internal structure holding a reference to mesh visuals, physics and a block of voxel data. // Internal structure holding a reference to mesh visuals, physics and a block of voxel data.
class VoxelBlock { class VoxelBlock {
public: public:
enum MeshState {
MESH_NEVER_UPDATED = 0,
MESH_UP_TO_DATE,
MESH_UPDATE_NOT_SENT, // The mesh is out of date, but no update request have been sent
MESH_UPDATE_SENT // The mesh is out of date, and an update request is pending
};
Ref<VoxelBuffer> voxels; Ref<VoxelBuffer> voxels;
Vector3i pos; // TODO Rename position Vector3i pos; // TODO Rename position
unsigned int lod_index = 0; unsigned int lod_index = 0;
MeshState mesh_state = MESH_NEVER_UPDATED;
// The mesh might be null, but we don't know if it's actually empty or if it's loading. // 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. // This boolean tells if we attempted to mesh this block at least once.

View File

@ -112,12 +112,7 @@ void VoxelLodTerrain::immerge_block(Vector3i block_pos, unsigned int lod_index)
// TODO Schedule block saving when supported // TODO Schedule block saving when supported
lod.map->remove_block(block_pos, VoxelMap::NoAction()); lod.map->remove_block(block_pos, VoxelMap::NoAction());
BlockState state = BLOCK_NONE; lod.loading_blocks.erase(block_pos);
Map<Vector3i, BlockState>::Element *E = lod.block_states.find(block_pos);
if (E) {
state = E->value();
lod.block_states.erase(E);
}
// Blocks in the update queue will be cancelled in _process, // Blocks in the update queue will be cancelled in _process,
// because it's too expensive to linear-search all blocks for each block // because it's too expensive to linear-search all blocks for each block
@ -211,27 +206,19 @@ Dictionary VoxelLodTerrain::get_block_info(Vector3 fbpos, unsigned int lod_index
const Lod &lod = _lods[lod_index]; const Lod &lod = _lods[lod_index];
Vector3i bpos(fbpos); Vector3i bpos(fbpos);
BlockState state;
const Map<Vector3i, BlockState>::Element *E = lod.block_states.find(bpos);
if (E) {
state = E->value();
} else {
if (lod.map->has_block(bpos)) {
state = BLOCK_IDLE;
} else {
state = BLOCK_NONE;
}
}
bool meshed = false; bool meshed = false;
bool visible = false; bool visible = false;
int loading_state = 0;
const VoxelBlock *block = lod.map->get_block(bpos); const VoxelBlock *block = lod.map->get_block(bpos);
if (block) { if (block) {
meshed = block->has_been_meshed; meshed = block->has_been_meshed;
visible = block->is_visible(); visible = block->is_visible();
loading_state = 2;
} else if (lod.loading_blocks.has(bpos)) {
loading_state = 1;
} }
d["state"] = state; d["loading"] = loading_state;
d["meshed"] = meshed; d["meshed"] = meshed;
d["visible"] = visible; d["visible"] = visible;
return d; return d;
@ -324,23 +311,6 @@ Vector3 VoxelLodTerrain::get_viewer_pos() const {
return Vector3(); return Vector3();
} }
static void remove_positions_outside_box(
std::vector<Vector3i> &positions,
Rect3i box,
Map<Vector3i, VoxelLodTerrain::BlockState> &state_map) {
for (int i = 0; i < positions.size(); ++i) {
const Vector3i bpos = positions[i];
if (!box.contains(bpos)) {
int last = positions.size() - 1;
positions[i] = positions[last];
positions.resize(last);
state_map.erase(bpos);
--i;
}
}
}
void VoxelLodTerrain::load_block_and_neighbors(const Vector3i &p_bpos, unsigned int lod_index) { void VoxelLodTerrain::load_block_and_neighbors(const Vector3i &p_bpos, unsigned int lod_index) {
CRASH_COND(lod_index >= get_lod_count()); CRASH_COND(lod_index >= get_lod_count());
Lod &lod = _lods[lod_index]; Lod &lod = _lods[lod_index];
@ -358,10 +328,9 @@ void VoxelLodTerrain::load_block_and_neighbors(const Vector3i &p_bpos, unsigned
VoxelBlock *block = lod.map->get_block(bpos); VoxelBlock *block = lod.map->get_block(bpos);
if (block == nullptr) { if (block == nullptr) {
Map<Vector3i, BlockState>::Element *E = lod.block_states.find(bpos); if (!lod.loading_blocks.has(bpos)) {
if (E == nullptr) {
lod.blocks_to_load.push_back(bpos); lod.blocks_to_load.push_back(bpos);
lod.block_states.insert(bpos, BLOCK_LOAD); lod.loading_blocks.insert(bpos);
} }
} }
} }
@ -369,6 +338,23 @@ void VoxelLodTerrain::load_block_and_neighbors(const Vector3i &p_bpos, unsigned
} }
} }
static void remove_positions_outside_box(
std::vector<Vector3i> &positions,
Rect3i box,
Set<Vector3i> &position_set) {
for (int i = 0; i < positions.size(); ++i) {
const Vector3i bpos = positions[i];
if (!box.contains(bpos)) {
int last = positions.size() - 1;
positions[i] = positions[last];
positions.resize(last);
position_set.erase(bpos);
--i;
}
}
}
void VoxelLodTerrain::_process() { void VoxelLodTerrain::_process() {
if (get_lod_count() == 0) { if (get_lod_count() == 0) {
@ -403,8 +389,8 @@ void VoxelLodTerrain::_process() {
Rect3i prev_box = Rect3i::from_center_extents(lod.last_viewer_block_pos, Vector3i(lod.last_view_distance_blocks)); Rect3i prev_box = Rect3i::from_center_extents(lod.last_viewer_block_pos, Vector3i(lod.last_view_distance_blocks));
// Eliminate pending blocks that aren't needed // Eliminate pending blocks that aren't needed
remove_positions_outside_box(lod.blocks_to_load, new_box, lod.block_states); remove_positions_outside_box(lod.blocks_to_load, new_box, lod.loading_blocks);
remove_positions_outside_box(lod.blocks_pending_update, new_box, lod.block_states); remove_positions_outside_box(lod.blocks_pending_update, new_box, lod.loading_blocks);
if (prev_box != new_box) { if (prev_box != new_box) {
@ -460,6 +446,8 @@ void VoxelLodTerrain::_process() {
self->load_block_and_neighbors(child_pos, child_lod_index); self->load_block_and_neighbors(child_pos, child_lod_index);
} else if (!block->has_been_meshed) { } else if (!block->has_been_meshed) {
// TODO There is a case where a whole region of map cannot load,
// because we end here everytime instead of the above.
can = false; can = false;
} }
} }
@ -577,8 +565,8 @@ void VoxelLodTerrain::_process() {
Lod &lod = _lods[eo.lod]; Lod &lod = _lods[eo.lod];
Map<Vector3i, BlockState>::Element *state = lod.block_states.find(eo.block_position); Set<Vector3i>::Element *E = lod.loading_blocks.find(eo.block_position);
if (state == nullptr || state->value() != BLOCK_LOAD) { if (E == nullptr) {
// That block was not requested, or is no longer needed. drop it... // That block was not requested, or is no longer needed. drop it...
continue; continue;
} }
@ -596,7 +584,7 @@ void VoxelLodTerrain::_process() {
// The block will be made visible only by LodOctree // The block will be made visible only by LodOctree
block->set_visible(false); block->set_visible(false);
lod.block_states.erase(state); lod.loading_blocks.erase(E);
// if the block is surrounded or any of its neighbors becomes surrounded, and are marked to mesh, // if the block is surrounded or any of its neighbors becomes surrounded, and are marked to mesh,
// it should be added to meshing requests // it should be added to meshing requests
@ -610,19 +598,16 @@ void VoxelLodTerrain::_process() {
if (lod.map->is_block_surrounded(npos)) { if (lod.map->is_block_surrounded(npos)) {
Map<Vector3i, BlockState>::Element *state = lod.block_states.find(npos); VoxelBlock *nblock = lod.map->get_block(npos);
if (state && state->value() == BLOCK_UPDATE_NOT_SENT) { CRASH_COND(nblock == nullptr);
if (nblock->mesh_state == VoxelBlock::MESH_UPDATE_NOT_SENT) {
// Assuming it is scheduled to be updated already. // Assuming it is scheduled to be updated already.
// In case of BLOCK_UPDATE_SENT, we'll have to resend it. // In case of BLOCK_UPDATE_SENT, we'll have to resend it.
continue; continue;
} }
if (state) { nblock->mesh_state = VoxelBlock::MESH_UPDATE_NOT_SENT;
state->value() = BLOCK_UPDATE_NOT_SENT;
} else {
lod.block_states.insert(npos, BLOCK_UPDATE_NOT_SENT);
}
lod.blocks_pending_update.push_back(npos); lod.blocks_pending_update.push_back(npos);
} }
} }
@ -631,7 +616,7 @@ void VoxelLodTerrain::_process() {
} else { } else {
// Only update the block, neighbors will probably follow if needed // Only update the block, neighbors will probably follow if needed
lod.block_states[eo.block_position] = BLOCK_UPDATE_NOT_SENT; block->mesh_state = VoxelBlock::MESH_UPDATE_NOT_SENT;
lod.blocks_pending_update.push_back(eo.block_position); lod.blocks_pending_update.push_back(eo.block_position);
} }
} }
@ -649,9 +634,9 @@ void VoxelLodTerrain::_process() {
for (int i = 0; i < lod.blocks_pending_update.size(); ++i) { for (int i = 0; i < lod.blocks_pending_update.size(); ++i) {
Vector3i block_pos = lod.blocks_pending_update[i]; Vector3i block_pos = lod.blocks_pending_update[i];
Map<Vector3i, BlockState>::Element *block_state = lod.block_states.find(block_pos); VoxelBlock *block = lod.map->get_block(block_pos);
CRASH_COND(block_state == NULL); CRASH_COND(block == nullptr);
CRASH_COND(block_state->value() != BLOCK_UPDATE_NOT_SENT); CRASH_COND(block->mesh_state != VoxelBlock::MESH_UPDATE_NOT_SENT);
// TODO Perhaps we could do a bit of early-rejection before spending time in buffer copy? // TODO Perhaps we could do a bit of early-rejection before spending time in buffer copy?
@ -676,7 +661,7 @@ void VoxelLodTerrain::_process() {
iblock.lod = lod_index; iblock.lod = lod_index;
input.blocks.push_back(iblock); input.blocks.push_back(iblock);
block_state->value() = BLOCK_UPDATE_SENT; block->mesh_state = VoxelBlock::MESH_UPDATE_SENT;
} }
lod.blocks_pending_update.clear(); lod.blocks_pending_update.clear();
@ -727,17 +712,16 @@ void VoxelLodTerrain::_process() {
Lod &lod = _lods[ob.lod]; Lod &lod = _lods[ob.lod];
Map<Vector3i, BlockState>::Element *state = lod.block_states.find(ob.position);
if (state && state->value() == BLOCK_UPDATE_SENT) {
lod.block_states.erase(state);
}
VoxelBlock *block = lod.map->get_block(ob.position); VoxelBlock *block = lod.map->get_block(ob.position);
if (block == NULL) { if (block == NULL) {
// That block is no longer loaded, drop the result // That block is no longer loaded, drop the result
continue; continue;
} }
if (block->mesh_state == VoxelBlock::MESH_UPDATE_SENT) {
block->mesh_state = VoxelBlock::MESH_UP_TO_DATE;
}
Ref<ArrayMesh> mesh; Ref<ArrayMesh> mesh;
mesh.instance(); mesh.instance();
@ -821,10 +805,4 @@ void VoxelLodTerrain::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::REAL, "lod_split_scale"), "set_lod_split_scale", "get_lod_split_scale"); ADD_PROPERTY(PropertyInfo(Variant::REAL, "lod_split_scale"), "set_lod_split_scale", "get_lod_split_scale");
ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "viewer_path"), "set_viewer_path", "get_viewer_path"); ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "viewer_path"), "set_viewer_path", "get_viewer_path");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "material", PROPERTY_HINT_RESOURCE_TYPE, "Material"), "set_material", "get_material"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "material", PROPERTY_HINT_RESOURCE_TYPE, "Material"), "set_material", "get_material");
BIND_ENUM_CONSTANT(BLOCK_NONE);
BIND_ENUM_CONSTANT(BLOCK_LOAD);
BIND_ENUM_CONSTANT(BLOCK_UPDATE_NOT_SENT);
BIND_ENUM_CONSTANT(BLOCK_UPDATE_SENT);
BIND_ENUM_CONSTANT(BLOCK_IDLE);
} }

View File

@ -20,14 +20,6 @@ class VoxelLodTerrain : public Spatial {
public: public:
static const int MAX_LOD = 32; static const int MAX_LOD = 32;
enum BlockState {
BLOCK_NONE, // There is no block
BLOCK_LOAD, // The block is loading
BLOCK_UPDATE_NOT_SENT, // The block needs an update but wasn't sent yet
BLOCK_UPDATE_SENT, // The block needs an update which was sent
BLOCK_IDLE // The block is up to date
};
VoxelLodTerrain(); VoxelLodTerrain();
~VoxelLodTerrain(); ~VoxelLodTerrain();
@ -107,8 +99,7 @@ private:
// Each LOD works in a set of coordinates spanning 2x more voxels the higher their index is // Each LOD works in a set of coordinates spanning 2x more voxels the higher their index is
struct Lod { struct Lod {
Ref<VoxelMap> map; Ref<VoxelMap> map;
Set<Vector3i> loading_blocks;
Map<Vector3i, BlockState> block_states;
std::vector<Vector3i> blocks_pending_update; std::vector<Vector3i> blocks_pending_update;
// These are relative to this LOD, in block coordinates // These are relative to this LOD, in block coordinates
@ -124,6 +115,4 @@ private:
Stats _stats; Stats _stats;
}; };
VARIANT_ENUM_CAST(VoxelLodTerrain::BlockState)
#endif // VOXEL_LOD_TERRAIN_HPP #endif // VOXEL_LOD_TERRAIN_HPP

View File

@ -13,12 +13,13 @@
class VoxelMap; class VoxelMap;
class VoxelLibrary; class VoxelLibrary;
// Infinite paged terrain made of voxel blocks. // Infinite paged terrain made of voxel blocks all with the same level of detail.
// Voxels are polygonized around the viewer by distance in a large cubic space. // Voxels are polygonized around the viewer by distance in a large cubic space.
// Data is streamed using a VoxelProvider. // Data is streamed using a VoxelProvider.
class VoxelTerrain : public Spatial { class VoxelTerrain : public Spatial {
GDCLASS(VoxelTerrain, Spatial) GDCLASS(VoxelTerrain, Spatial)
public: public:
// TODO Remove this, it's obsoleted by VoxelBlock::MeshState
enum BlockDirtyState { enum BlockDirtyState {
BLOCK_NONE, // There is no block BLOCK_NONE, // There is no block
BLOCK_LOAD, // The block is loading BLOCK_LOAD, // The block is loading