#include "voxel_lod_terrain.h" #include "../constants/voxel_string_names.h" #include "../edition/voxel_tool_lod_terrain.h" #include "../meshers/transvoxel/voxel_mesher_transvoxel.h" #include "../server/voxel_server.h" #include "../util/funcs.h" #include "../util/godot/funcs.h" #include "../util/macros.h" #include "../util/math/rect3i.h" #include "../util/profiling.h" #include "../util/profiling_clock.h" #include "instancing/voxel_instancer.h" #include #include #include #include namespace { Ref build_mesh(const Vector surfaces, Mesh::PrimitiveType primitive, int compression_flags, Ref material) { Ref mesh; mesh.instance(); unsigned int surface_index = 0; for (int i = 0; i < surfaces.size(); ++i) { Array surface = surfaces[i]; if (surface.empty()) { continue; } CRASH_COND(surface.size() != Mesh::ARRAY_MAX); if (!is_surface_triangulated(surface)) { continue; } mesh->add_surface_from_arrays(primitive, surface, Array(), compression_flags); mesh->surface_set_material(surface_index, material); // No multi-material supported yet ++surface_index; } if (is_mesh_empty(mesh)) { mesh = Ref(); } return mesh; } // To use on loaded blocks static inline void schedule_mesh_update(VoxelMeshBlock *block, std::vector &blocks_pending_update) { if (block->get_mesh_state() != VoxelMeshBlock::MESH_UPDATE_NOT_SENT) { if (block->active) { // Schedule an update block->set_mesh_state(VoxelMeshBlock::MESH_UPDATE_NOT_SENT); blocks_pending_update.push_back(block->position); } else { // Just mark it as needing update, so the visibility system will schedule its update when needed block->set_mesh_state(VoxelMeshBlock::MESH_NEED_UPDATE); } } } struct BeforeUnloadDataAction { std::vector &blocks_to_save; bool save; void operator()(VoxelDataBlock *block) { // Save if modified // TODO Don't ask for save if the stream doesn't support it! if (save && block->is_modified()) { //print_line(String("Scheduling save for block {0}").format(varray(block->position.to_vec3()))); VoxelLodTerrain::BlockToSave b; // We don't copy since the block will be unloaded anyways b.voxels = block->voxels; b.position = block->position; b.lod = block->lod_index; blocks_to_save.push_back(b); } } }; struct BeforeUnloadMeshAction { std::vector > &shader_material_pool; void operator()(VoxelMeshBlock *block) { // Recycle material Ref sm = block->get_shader_material(); if (sm.is_valid()) { shader_material_pool.push_back(sm); block->set_shader_material(Ref()); } } }; struct ScheduleSaveAction { std::vector &blocks_to_save; void operator()(VoxelDataBlock *block) { // Save if modified // TODO Don't ask for save if the stream doesn't support it! if (block->is_modified()) { //print_line(String("Scheduling save for block {0}").format(varray(block->position.to_vec3()))); VoxelLodTerrain::BlockToSave b; RWLockRead lock(block->voxels->get_lock()); b.voxels = block->voxels->duplicate(true); b.position = block->position; b.lod = block->lod_index; blocks_to_save.push_back(b); block->set_modified(false); } } }; static inline uint64_t get_ticks_msec() { return OS::get_singleton()->get_ticks_msec(); } } // namespace VoxelLodTerrain::VoxelLodTerrain() { // Note: don't do anything heavy in the constructor. // Godot may create and destroy dozens of instances of all node types on startup, // due to how ClassDB gets its default values. PRINT_VERBOSE("Construct VoxelLodTerrain"); set_notify_transform(true); // Doing this to setup the defaults set_process_mode(_process_mode); // Infinite by default _bounds_in_voxels = Rect3i::from_center_extents(Vector3i(0), Vector3i(VoxelConstants::MAX_VOLUME_EXTENT)); _volume_id = VoxelServer::get_singleton()->add_volume(&_reception_buffers, VoxelServer::VOLUME_SPARSE_OCTREE); VoxelServer::get_singleton()->set_volume_octree_lod_distance(_volume_id, get_lod_distance()); // 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_distance(48.f); // For ease of use in editor Ref default_mesher; default_mesher.instance(); _mesher = default_mesher; } VoxelLodTerrain::~VoxelLodTerrain() { PRINT_VERBOSE("Destroy VoxelLodTerrain"); VoxelServer::get_singleton()->remove_volume(_volume_id); // Instancer can take care of itself } Ref VoxelLodTerrain::get_material() const { return _material; } void VoxelLodTerrain::set_material(Ref p_material) { _material = p_material; } unsigned int VoxelLodTerrain::get_data_block_size() const { return _lods[0].data_map.get_block_size(); } unsigned int VoxelLodTerrain::get_data_block_size_pow2() const { return _lods[0].data_map.get_block_size_pow2(); } unsigned int VoxelLodTerrain::get_mesh_block_size_pow2() const { return _lods[0].mesh_map.get_block_size_pow2(); } unsigned int VoxelLodTerrain::get_mesh_block_size() const { return _lods[0].mesh_map.get_block_size(); } void VoxelLodTerrain::set_stream(Ref p_stream) { if (p_stream == _stream) { return; } _stream = p_stream; #ifdef TOOLS_ENABLED if (_stream.is_valid()) { if (Engine::get_singleton()->is_editor_hint()) { Ref