godot_voxel/terrain/variable_lod/voxel_lod_terrain_update_data.h
Marc Gilleron 86ba74ce3a Some changes and fixes related modifiers
- VoxelLodTerrain no longer caches generated voxels by default, so
  generating on the fly is no longer exclusive to full load mode.
  Might add an option later, but not for now (VoxelTerrain is still
  unaffected and keeps caching them)
- The "Cached" state is represented with blocks having no voxel data,
  so it needs extra checks in some areas to avoid null access
- Fix generate task was not including modifiers after the base generator
- The "save_generator_output" option on streams now means such blocks are
  considered edited
- Modifying modifiers now clears cached generated blocks
  intersecting with them.
- Fix "re-generate" was erasing the internal stack of modifiers
- Added docs
2022-06-18 23:14:18 +01:00

189 lines
6.0 KiB
C++

#ifndef VOXEL_LOD_TERRAIN_UPDATE_DATA_H
#define VOXEL_LOD_TERRAIN_UPDATE_DATA_H
#include "../../constants/voxel_constants.h"
#include "../../generators/voxel_generator.h"
#include "../../storage/voxel_data_map.h"
#include "../../streams/voxel_stream.h"
#include "../../util/fixed_array.h"
#include "../voxel_mesh_map.h"
#include "lod_octree.h"
#include <map>
#include <unordered_set>
namespace zylann {
class AsyncDependencyTracker;
namespace voxel {
// Settings and states needed for the multi-threaded part of the update loop of VoxelLodTerrain.
// See `VoxelLodTerrainUpdateTask` for more info.
struct VoxelLodTerrainUpdateData {
struct OctreeItem {
LodOctree octree;
};
struct TransitionUpdate {
Vector3i block_position;
uint8_t transition_mask;
};
struct BlockLocation {
Vector3i position;
uint8_t lod;
};
struct BlockToSave {
std::shared_ptr<VoxelBufferInternal> voxels;
Vector3i position;
uint8_t lod;
};
// These values don't change during the update task.
struct Settings {
// Area within which voxels can exist.
// Note, these bounds might not be exactly represented. This volume is chunk-based, so the result will be
// approximated to the closest chunk.
Box3i bounds_in_voxels;
unsigned int lod_count = 0;
// Distance between a viewer and the end of LOD0
float lod_distance = 0.f;
unsigned int view_distance_voxels = 512;
bool full_load_mode = false;
bool run_stream_in_editor = true;
unsigned int mesh_block_size_po2 = 4;
};
enum MeshState {
MESH_NEVER_UPDATED = 0, // TODO Redundant with MESH_NEED_UPDATE?
MESH_UP_TO_DATE,
MESH_NEED_UPDATE, // The mesh is out of date but was not yet scheduled for update
MESH_UPDATE_NOT_SENT, // The mesh is out of date and was scheduled for update, but no request have been sent
// yet
MESH_UPDATE_SENT // The mesh is out of date, and an update request was sent, pending response
};
struct MeshBlockState {
std::atomic<MeshState> state;
uint8_t transition_mask;
bool active;
bool pending_transition_update;
MeshBlockState() :
state(MESH_NEVER_UPDATED), transition_mask(0), active(false), pending_transition_update(false) {}
};
// Version of the mesh map designed to be mainly used for the threaded update task.
// It contains states used to determine when to actually load/unload meshes.
struct MeshMapState {
// Values in this map are expected to have stable addresses.
std::unordered_map<Vector3i, MeshBlockState> map;
// Locked for writing when blocks get inserted or removed from the map.
// If you need to lock more than one Lod, always do so in increasing order, to avoid deadlocks.
// IMPORTANT:
// - The update task will add and remove blocks from this map.
// - Threads outside the update task must never add or remove blocks to the map (even with locking),
// unless the task is not running in parallel.
RWLock map_lock;
};
// Each LOD works in a set of coordinates spanning 2x more voxels the higher their index is
struct Lod {
// Keeping track of asynchronously loading blocks so we don't try to redundantly load them
std::unordered_set<Vector3i> loading_blocks;
BinaryMutex loading_blocks_mutex;
// These are relative to this LOD, in block coordinates
Vector3i last_viewer_data_block_pos;
int last_view_distance_data_blocks = 0;
MeshMapState mesh_map_state;
std::vector<Vector3i> blocks_pending_update;
Vector3i last_viewer_mesh_block_pos;
int last_view_distance_mesh_blocks = 0;
// Deferred outputs to main thread
std::vector<Vector3i> mesh_blocks_to_unload;
std::vector<TransitionUpdate> mesh_blocks_to_update_transitions;
std::vector<Vector3i> mesh_blocks_to_activate;
std::vector<Vector3i> mesh_blocks_to_deactivate;
inline bool has_loading_block(const Vector3i &pos) const {
return loading_blocks.find(pos) != loading_blocks.end();
}
};
struct AsyncEdit {
IThreadedTask *task;
Box3i box;
std::shared_ptr<AsyncDependencyTracker> task_tracker;
};
struct RunningAsyncEdit {
std::shared_ptr<AsyncDependencyTracker> tracker;
Box3i box;
};
struct Stats {
uint32_t blocked_lods = 0;
uint32_t time_detect_required_blocks = 0;
uint32_t time_io_requests = 0;
uint32_t time_mesh_requests = 0;
uint32_t time_total = 0;
};
// Data modified by the update task
struct State {
// This terrain type is a sparse grid of octrees.
// Indexed by a grid coordinate whose step is the size of the highest-LOD block.
// Not using a pointer because Map storage is stable.
// TODO Optimization: could be replaced with a grid data structure
std::map<Vector3i, OctreeItem> lod_octrees;
Box3i last_octree_region_box;
Vector3i local_viewer_pos_previous_octree_update;
bool had_blocked_octree_nodes_previous_update = false;
bool force_update_octrees_next_update = false;
FixedArray<Lod, constants::MAX_LOD> lods;
// This is the entry point for notifying data changes, which will cause mesh updates.
// Contains blocks that were edited and need their LOD counterparts to be updated.
// Scheduling is only done at LOD0 because it is the only editable LOD.
std::vector<Vector3i> blocks_pending_lodding_lod0;
BinaryMutex blocks_pending_lodding_lod0_mutex;
std::vector<AsyncEdit> pending_async_edits;
BinaryMutex pending_async_edits_mutex;
std::vector<RunningAsyncEdit> running_async_edits;
// Areas where generated stuff has changed. Similar to an edit, but non-destructive.
std::vector<Box3i> changed_generated_areas;
BinaryMutex changed_generated_areas_mutex;
Stats stats;
};
// Set to true when the update task is finished
std::atomic_bool task_is_complete;
// Will be locked as long as the update task is running.
BinaryMutex completion_mutex;
Settings settings;
State state;
// After this call, no locking is necessary, as no other thread should be using the data.
// However it can stall for longer, so prefer using it when doing structural changes, such as changing LOD count,
// LOD distances, or the way the update logic runs.
void wait_for_end_of_task() {
MutexLock lock(completion_mutex);
}
};
} // namespace voxel
} // namespace zylann
#endif // VOXEL_LOD_TERRAIN_UPDATE_DATA_H