#ifndef VOXEL_INSTANCER_H #define VOXEL_INSTANCER_H #include "../../streams/instance_data.h" #include "../../util/array_slice.h" #include "../../util/fixed_array.h" #include "../../util/godot/direct_multimesh_instance.h" #include "../../util/math/rect3i.h" #include "voxel_instance_generator.h" #include "voxel_instance_library.h" #include //#include // Included by node.h lol #include #include #include #include class VoxelLodTerrain; class VoxelInstancerRigidBody; class PhysicsBody; // Note: a large part of this node could be made generic to support the sole idea of instancing within octants? // Even nodes like gridmaps could be rebuilt on top of this, if its concept of "grid" was decoupled. // Add-on to voxel nodes, allowing to spawn elements on the surface. // These elements are rendered with hardware instancing, can have collisions, and also be persistent. class VoxelInstancer : public Spatial, public VoxelInstanceLibrary::IListener { GDCLASS(VoxelInstancer, Spatial) public: static const int MAX_LOD = 8; enum UpMode { UP_MODE_POSITIVE_Y = VoxelInstanceGenerator::UP_MODE_POSITIVE_Y, UP_MODE_SPHERE = VoxelInstanceGenerator::UP_MODE_SPHERE, UP_MODE_COUNT = VoxelInstanceGenerator::UP_MODE_COUNT }; VoxelInstancer(); ~VoxelInstancer(); // Properties void set_up_mode(UpMode mode); UpMode get_up_mode() const; void set_library(Ref library); Ref get_library() const; // Actions void save_all_modified_blocks(); // Event handlers void on_block_data_loaded(Vector3i grid_position, unsigned int lod_index, std::unique_ptr instances); void on_block_enter(Vector3i grid_position, unsigned int lod_index, Array surface_arrays); void on_block_exit(Vector3i grid_position, unsigned int lod_index); void on_area_edited(Rect3i p_voxel_box); void on_body_removed(unsigned int block_index, int instance_index); // Debug int debug_get_block_count() const; String get_configuration_warning() const override; protected: void _notification(int p_what); private: struct Block; struct Layer; void process_mesh_lods(); void add_layer(int layer_id, int lod_index); void remove_layer(int layer_id); int create_block(Layer *layer, uint16_t layer_id, Vector3i grid_position); void remove_block(unsigned int block_index); void set_world(World *world); void clear_blocks(); void clear_blocks_in_layer(int layer_id); void clear_layers(); void update_visibility(); void save_block(Vector3i grid_pos, int lod_index) const; Layer *get_layer(int id); const Layer *get_layer_const(int id) const; void regenerate_layer(uint16_t layer_id, bool regenerate_blocks); void update_layer_meshes(int layer_id); void create_blocks(const VoxelInstanceBlockData *instances_data, Vector3i grid_position, int lod_index, Array surface_arrays); void update_block_from_transforms(int block_index, ArraySlice transforms, Vector3i grid_position, Layer *layer, const VoxelInstanceLibraryItem *item, uint16_t layer_id, World *world, const Transform &block_transform); void on_library_item_changed(int item_id, VoxelInstanceLibraryItem::ChangeType change) override; static void _bind_methods(); struct Block { uint16_t layer_id; uint8_t current_mesh_lod = 0; uint8_t lod_index; Vector3i grid_position; DirectMultiMeshInstance multimesh_instance; // For physics we use nodes because it's easier to manage. // Such instances may be less numerous. Vector bodies; }; struct Layer { unsigned int lod_index; HashMap blocks; }; struct MeshLodDistances { // Multimesh LOD updates based on the distance between the camera and the center of the block. // Two distances are used to implement hysteresis, which allows to avoid oscillating too fast between lods. // TODO Need to investigate if Godot 4 implements LOD for multimeshes // Despite this, due to how Godot 4 implements LOD, it may still be beneficial to have a custom LOD system, // so we can switch to impostors rather than only decimating geometry // Distance above which the mesh starts being used, taking precedence over meshes of lower distance. float enter_distance_squared; // Distance under which the mesh stops being used float exit_distance_squared; }; struct Lod { std::vector layers; // Blocks that have have unsaved changes HashMap modified_blocks; // This is a temporary place to store loaded instances data while it's not visible yet. // These instances are user-authored ones. If a block does not have an entry there, // it will get generated instances. // Can't use `HashMap` because it lacks move semantics. std::unordered_map > loaded_instances_data; FixedArray mesh_lod_distances; Lod() = default; Lod(const Lod &) = delete; // non construction-copyable Lod &operator=(const Lod &) = delete; // non copyable }; UpMode _up_mode = UP_MODE_POSITIVE_Y; FixedArray _lods; std::vector _blocks; // Does not have nulls HashMap _layers; // Each layer corresponds to a library item Ref _library; std::vector _transform_cache; VoxelLodTerrain *_parent; }; VARIANT_ENUM_CAST(VoxelInstancer::UpMode); #endif // VOXEL_INSTANCER_H