godot_voxel/terrain/variable_lod/voxel_mesh_block_vlt.h
Marc Gilleron c7d8c2557c Attempting to give priority to main mesh building over transition meshes.
This effectively creates them after ALL main meshes have been built,
resulting in a seemingly faster convergence of LOD, however when moving
around it causes some cracks to appear briefly. This can be bad if the
terrain is dark and the underlying skybox is bright. Might need to be
optional, or find a different approach.
2022-06-22 22:05:25 +01:00

113 lines
3.6 KiB
C++

#ifndef VOXEL_MESH_BLOCK_VLT_H
#define VOXEL_MESH_BLOCK_VLT_H
#include "../../util/tasks/time_spread_task_runner.h"
#include "../voxel_mesh_block.h"
namespace zylann::voxel {
class BuildTransitionMeshTask;
// Stores mesh and collider for one chunk of `VoxelTerrain`.
// It doesn't store voxel data, because it may be using different block size, or different data structure.
class VoxelMeshBlockVLT : public VoxelMeshBlock {
public:
enum FadingState { //
FADING_NONE,
FADING_IN,
FADING_OUT
};
uint8_t lod_index = 0;
FadingState fading_state = FADING_NONE;
float fading_progress = 0.f;
// Voxel LOD works by splitting a block into up to 8 higher-resolution blocks.
// The parent block and its children can be called a "LOD group".
// Only non-overlapping blocks in a LOD group can be considered active at once.
// So when LOD fading is used, we no longer use `visible` to find which block is active,
// because blocks can use a cross-fade effect. Overlapping blocks of the same LOD group can be visible at once.
// Hence the need to use this boolean.
bool active = false;
bool got_first_mesh_update = false;
uint64_t last_collider_update_time = 0;
bool has_deferred_collider_update = false;
std::vector<Array> deferred_collider_data;
VoxelMeshBlockVLT(const Vector3i bpos, unsigned int size, unsigned int p_lod_index);
~VoxelMeshBlockVLT();
void set_world(Ref<World3D> p_world);
void set_visible(bool visible);
bool update_fading(float speed);
void set_parent_visible(bool parent_visible);
void set_mesh(Ref<Mesh> mesh, DirectMeshInstance::GIMode gi_mode);
void set_transition_mask(uint8_t m);
inline uint8_t get_transition_mask() const {
return _transition_mask;
}
void set_gi_mode(DirectMeshInstance::GIMode mode);
void set_transition_mesh(Ref<Mesh> mesh, unsigned int side, DirectMeshInstance::GIMode gi_mode);
void set_pending_transition_mesh_task(unsigned int side, BuildTransitionMeshTask &task);
void on_transition_mesh_task_completed(const BuildTransitionMeshTask &task);
void cancel_transition_mesh_tasks();
void cancel_transition_mesh_task(unsigned int side);
void set_shader_material(Ref<ShaderMaterial> material);
inline Ref<ShaderMaterial> get_shader_material() const {
return _shader_material;
}
void set_parent_transform(const Transform3D &parent_transform);
void update_transition_mesh_transform(unsigned int side, const Transform3D &parent_transform);
template <typename F>
void for_each_mesh_instance_with_transform(F f) const {
const Transform3D local_transform(Basis(), _position_in_voxels);
const Transform3D world_transform = local_transform;
f(_mesh_instance, world_transform);
for (unsigned int i = 0; i < _transition_mesh_instances.size(); ++i) {
const DirectMeshInstance &mi = _transition_mesh_instances[i];
if (mi.is_valid()) {
f(mi, world_transform);
}
}
}
private:
void _set_visible(bool visible);
inline bool _is_transition_visible(unsigned int side) const {
return _transition_mask & (1 << side);
}
Ref<ShaderMaterial> _shader_material;
// Not owned. Keeping a reference to cancel it if the block gets destroyed.
FixedArray<BuildTransitionMeshTask *, Cube::SIDE_COUNT> _pending_transition_mesh_tasks;
FixedArray<DirectMeshInstance, Cube::SIDE_COUNT> _transition_mesh_instances;
uint8_t _transition_mask = 0;
#ifdef VOXEL_DEBUG_LOD_MATERIALS
Ref<Material> _debug_material;
Ref<Material> _debug_transition_material;
#endif
};
Ref<ArrayMesh> build_mesh(Span<const VoxelMesher::Output::Surface> surfaces, Mesh::PrimitiveType primitive, int flags,
Ref<Material> material);
} // namespace zylann::voxel
#endif // VOXEL_MESH_BLOCK_VLT_H