Instancing persistence is now opt-in
parent
7f47b07e0c
commit
c6fdbb675b
|
@ -175,6 +175,15 @@ void VoxelInstanceGenerator::generate_transforms(
|
|||
|
||||
out_transforms.push_back(t);
|
||||
}
|
||||
|
||||
// TODO Investigate if this helps (won't help with authored terrain)
|
||||
// if (graph_generator.is_valid()) {
|
||||
// for (size_t i = 0; i < _transform_cache.size(); ++i) {
|
||||
// Transform &t = _transform_cache[i];
|
||||
// const Vector3 up = t.get_basis().get_axis(Vector3::AXIS_Y);
|
||||
// t.origin = graph_generator->approximate_surface(t.origin, up * 0.5f);
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
void VoxelInstanceGenerator::set_density(float density) {
|
||||
|
|
|
@ -205,20 +205,21 @@ VoxelInstancer::UpMode VoxelInstancer::get_up_mode() const {
|
|||
return _up_mode;
|
||||
}
|
||||
|
||||
int VoxelInstancer::add_layer(int lod_index) {
|
||||
// Find unused ID
|
||||
int VoxelInstancer::generate_persistent_id() const {
|
||||
int id = 0;
|
||||
for (size_t i = 0; i < _layers.size(); ++i) {
|
||||
const Layer *layer = _layers[i];
|
||||
if (layer != nullptr) {
|
||||
id = max(layer->id + 1, id);
|
||||
id = max(layer->persistent_id + 1, id);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Should not happen
|
||||
ERR_FAIL_COND_V(id > Layer::MAX_ID, -1);
|
||||
return id;
|
||||
}
|
||||
|
||||
int VoxelInstancer::add_layer(int lod_index) {
|
||||
int layer_index = -1;
|
||||
for (size_t i = 0; i < _layers.size(); ++i) {
|
||||
if (_layers[i] == nullptr) {
|
||||
|
@ -233,7 +234,6 @@ int VoxelInstancer::add_layer(int lod_index) {
|
|||
|
||||
Layer *layer = memnew(Layer);
|
||||
layer->lod_index = lod_index;
|
||||
layer->id = id;
|
||||
|
||||
if (Engine::get_singleton()->is_editor_hint()) {
|
||||
// Put a default generator
|
||||
|
@ -256,6 +256,21 @@ void VoxelInstancer::set_layer_generator(int layer_index, Ref<VoxelInstanceGener
|
|||
layer->generator = generator;
|
||||
}
|
||||
|
||||
void VoxelInstancer::set_layer_persistent(int layer_index, bool persistent) {
|
||||
ERR_FAIL_INDEX(layer_index, _layers.size());
|
||||
Layer *layer = _layers[layer_index];
|
||||
ERR_FAIL_COND(layer == nullptr);
|
||||
|
||||
if (persistent) {
|
||||
layer->persistent = true;
|
||||
if (layer->persistent_id < 0 || find_layer_by_persistent_id(layer->persistent_id) != -1) {
|
||||
layer->persistent_id = generate_persistent_id();
|
||||
}
|
||||
} else {
|
||||
layer->persistent = false;
|
||||
}
|
||||
}
|
||||
|
||||
void VoxelInstancer::set_layer_mesh(int layer_index, Ref<Mesh> mesh) {
|
||||
ERR_FAIL_INDEX(layer_index, _layers.size());
|
||||
Layer *layer = _layers[layer_index];
|
||||
|
@ -434,16 +449,18 @@ void VoxelInstancer::on_block_enter(Vector3i grid_position, int lod_index, Array
|
|||
Lod &lod = _lods[lod_index];
|
||||
|
||||
auto it = lod.loaded_instances_data.find(grid_position);
|
||||
const VoxelInstanceBlockData *instances_data = nullptr;
|
||||
|
||||
if (it != lod.loaded_instances_data.end()) {
|
||||
ERR_FAIL_COND(it->second == nullptr);
|
||||
create_loaded_blocks(*it->second, grid_position, lod_index);
|
||||
// Data no longer needed in this form
|
||||
lod.loaded_instances_data.erase(grid_position);
|
||||
instances_data = it->second.get();
|
||||
ERR_FAIL_COND(instances_data == nullptr);
|
||||
|
||||
} else {
|
||||
generate_block_on_each_layer(grid_position, lod_index, surface_arrays);
|
||||
// We could do this:
|
||||
// lod.loaded_instances_data.erase(grid_position);
|
||||
// but we'd loose the information that this block was edited, so keeping it for now
|
||||
}
|
||||
|
||||
create_blocks(instances_data, grid_position, lod_index, surface_arrays);
|
||||
}
|
||||
|
||||
void VoxelInstancer::on_block_exit(Vector3i grid_position, int lod_index) {
|
||||
|
@ -547,76 +564,32 @@ void VoxelInstancer::create_block_from_transforms(ArraySlice<const Transform> tr
|
|||
layer->blocks.set(grid_position, block_index);
|
||||
}
|
||||
|
||||
int VoxelInstancer::find_layer_by_id(int id) const {
|
||||
int VoxelInstancer::find_layer_by_persistent_id(int id) const {
|
||||
ERR_FAIL_COND_V(id < 0, -1);
|
||||
for (size_t i = 0; i < _layers.size(); ++i) {
|
||||
const Layer *layer = _layers[i];
|
||||
if (layer != nullptr && layer->id == id) {
|
||||
if (layer != nullptr && layer->persistent_id == id) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
void VoxelInstancer::create_loaded_blocks(const VoxelInstanceBlockData &instances_data, Vector3i grid_position,
|
||||
int lod_index) {
|
||||
|
||||
VOXEL_PROFILE_SCOPE();
|
||||
|
||||
Lod &lod = _lods[lod_index];
|
||||
const Transform parent_transform = get_global_transform();
|
||||
Ref<World> world_ref = get_world();
|
||||
ERR_FAIL_COND(world_ref.is_null());
|
||||
World *world = *world_ref;
|
||||
|
||||
const int lod_block_size = _parent->get_block_size() << lod_index;
|
||||
const Transform block_local_transform = Transform(Basis(), (grid_position * lod_block_size).to_vec3());
|
||||
const Transform block_transform = parent_transform * block_local_transform;
|
||||
|
||||
for (auto layer_it = instances_data.layers.begin(); layer_it != instances_data.layers.end(); ++layer_it) {
|
||||
const VoxelInstanceBlockData::LayerData &layer_data = *layer_it;
|
||||
|
||||
const int layer_index = find_layer_by_id(layer_data.id);
|
||||
if (layer_index == -1) {
|
||||
ERR_PRINT(String("Could not find associated layer ID {0} from loaded instances data")
|
||||
.format(varray(layer_data.id)));
|
||||
continue;
|
||||
static bool contains_layer_id(const VoxelInstanceBlockData &instances_data, int id) {
|
||||
for (auto it = instances_data.layers.begin(); it != instances_data.layers.end(); ++it) {
|
||||
const VoxelInstanceBlockData::LayerData &layer = *it;
|
||||
if (layer.id == id) {
|
||||
return true;
|
||||
}
|
||||
|
||||
Layer *layer = _layers[layer_index];
|
||||
CRASH_COND(layer == nullptr);
|
||||
|
||||
const int *block_index_ptr = layer->blocks.getptr(grid_position);
|
||||
|
||||
if (block_index_ptr != nullptr) {
|
||||
// The block was already made?
|
||||
continue;
|
||||
}
|
||||
|
||||
static_assert(sizeof(VoxelInstanceBlockData::InstanceData) == sizeof(Transform),
|
||||
"Assuming instance data only contains a transform for now");
|
||||
ArraySlice<const Transform> transforms(
|
||||
reinterpret_cast<const Transform *>(layer_data.instances.data()), layer_data.instances.size());
|
||||
create_block_from_transforms(transforms, grid_position, layer, layer_index, world, block_transform);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void VoxelInstancer::generate_block_on_each_layer(Vector3i grid_position, int lod_index, Array surface_arrays) {
|
||||
if (surface_arrays.size() == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
PoolVector3Array vertices = surface_arrays[ArrayMesh::ARRAY_VERTEX];
|
||||
|
||||
if (vertices.size() == 0) {
|
||||
return;
|
||||
}
|
||||
void VoxelInstancer::create_blocks(const VoxelInstanceBlockData *instances_data_ptr, Vector3i grid_position,
|
||||
int lod_index, Array surface_arrays) {
|
||||
|
||||
VOXEL_PROFILE_SCOPE();
|
||||
|
||||
PoolVector3Array normals = surface_arrays[ArrayMesh::ARRAY_NORMAL];
|
||||
|
||||
//Ref<VoxelGeneratorGraph> graph_generator = _parent->get_stream();
|
||||
|
||||
Lod &lod = _lods[lod_index];
|
||||
const Transform parent_transform = get_global_transform();
|
||||
Ref<World> world_ref = get_world();
|
||||
|
@ -627,49 +600,104 @@ void VoxelInstancer::generate_block_on_each_layer(Vector3i grid_position, int lo
|
|||
const Transform block_local_transform = Transform(Basis(), (grid_position * lod_block_size).to_vec3());
|
||||
const Transform block_transform = parent_transform * block_local_transform;
|
||||
|
||||
for (auto it = lod.layers.begin(); it != lod.layers.end(); ++it) {
|
||||
const int layer_index = *it;
|
||||
// Load from data if any
|
||||
if (instances_data_ptr != nullptr) {
|
||||
VOXEL_PROFILE_SCOPE();
|
||||
const VoxelInstanceBlockData &instances_data = *instances_data_ptr;
|
||||
|
||||
Layer *layer = _layers[layer_index];
|
||||
CRASH_COND(layer == nullptr);
|
||||
for (auto layer_it = instances_data.layers.begin(); layer_it != instances_data.layers.end(); ++layer_it) {
|
||||
const VoxelInstanceBlockData::LayerData &layer_data = *layer_it;
|
||||
|
||||
if (layer->generator.is_null()) {
|
||||
continue;
|
||||
const int layer_index = find_layer_by_persistent_id(layer_data.id);
|
||||
if (layer_index == -1) {
|
||||
ERR_PRINT(String("Could not find associated layer ID {0} from loaded instances data")
|
||||
.format(varray(layer_data.id)));
|
||||
continue;
|
||||
}
|
||||
|
||||
Layer *layer = _layers[layer_index];
|
||||
CRASH_COND(layer == nullptr);
|
||||
|
||||
if (!layer->persistent) {
|
||||
// That layer is no longer persistent so we'll have to ignore authored data...
|
||||
WARN_PRINT(String("Layer index={0} received loaded data but is no longer persistent. "
|
||||
"Loaded data will be ignored.")
|
||||
.format(varray(layer_index)));
|
||||
continue;
|
||||
}
|
||||
|
||||
const int *block_index_ptr = layer->blocks.getptr(grid_position);
|
||||
if (block_index_ptr != nullptr) {
|
||||
// The block was already made?
|
||||
continue;
|
||||
}
|
||||
|
||||
// TODO Don't create blocks if there are no transforms?
|
||||
|
||||
static_assert(sizeof(VoxelInstanceBlockData::InstanceData) == sizeof(Transform),
|
||||
"Assuming instance data only contains a transform for now");
|
||||
ArraySlice<const Transform> transforms(
|
||||
reinterpret_cast<const Transform *>(layer_data.instances.data()), layer_data.instances.size());
|
||||
create_block_from_transforms(transforms, grid_position, layer, layer_index, world, block_transform);
|
||||
}
|
||||
}
|
||||
|
||||
// Generate other blocks
|
||||
{
|
||||
VOXEL_PROFILE_SCOPE();
|
||||
|
||||
if (surface_arrays.size() == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const int *block_index_ptr = layer->blocks.getptr(grid_position);
|
||||
|
||||
if (block_index_ptr != nullptr) {
|
||||
// The block was already made?
|
||||
continue;
|
||||
PoolVector3Array vertices = surface_arrays[ArrayMesh::ARRAY_VERTEX];
|
||||
if (vertices.size() == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
_transform_cache.clear();
|
||||
PoolVector3Array normals = surface_arrays[ArrayMesh::ARRAY_NORMAL];
|
||||
ERR_FAIL_COND(normals.size() == 0);
|
||||
|
||||
layer->generator->generate_transforms(
|
||||
_transform_cache,
|
||||
grid_position,
|
||||
lod_index,
|
||||
layer_index,
|
||||
surface_arrays,
|
||||
block_local_transform,
|
||||
static_cast<VoxelInstanceGenerator::UpMode>(_up_mode));
|
||||
for (auto it = lod.layers.begin(); it != lod.layers.end(); ++it) {
|
||||
const int layer_index = *it;
|
||||
|
||||
if (_transform_cache.size() == 0) {
|
||||
continue;
|
||||
Layer *layer = _layers[layer_index];
|
||||
CRASH_COND(layer == nullptr);
|
||||
|
||||
if (layer->generator.is_null()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const int *block_index_ptr = layer->blocks.getptr(grid_position);
|
||||
if (block_index_ptr != nullptr) {
|
||||
// The block was already made?
|
||||
continue;
|
||||
}
|
||||
|
||||
if (layer->persistent && instances_data_ptr != nullptr &&
|
||||
contains_layer_id(*instances_data_ptr, layer->persistent_id)) {
|
||||
// Don't generate, it received modified data
|
||||
continue;
|
||||
}
|
||||
|
||||
_transform_cache.clear();
|
||||
|
||||
layer->generator->generate_transforms(
|
||||
_transform_cache,
|
||||
grid_position,
|
||||
lod_index,
|
||||
layer_index,
|
||||
surface_arrays,
|
||||
block_local_transform,
|
||||
static_cast<VoxelInstanceGenerator::UpMode>(_up_mode));
|
||||
|
||||
if (_transform_cache.size() == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
create_block_from_transforms(to_slice_const(_transform_cache),
|
||||
grid_position, layer, layer_index, world, block_transform);
|
||||
}
|
||||
|
||||
// TODO Investigate if this helps (won't help with authored terrain)
|
||||
// if (graph_generator.is_valid()) {
|
||||
// for (size_t i = 0; i < _transform_cache.size(); ++i) {
|
||||
// Transform &t = _transform_cache[i];
|
||||
// const Vector3 up = t.get_basis().get_axis(Vector3::AXIS_Y);
|
||||
// t.origin = graph_generator->approximate_surface(t.origin, up * 0.5f);
|
||||
// }
|
||||
// }
|
||||
|
||||
create_block_from_transforms(to_slice_const(_transform_cache),
|
||||
grid_position, layer, layer_index, world, block_transform);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -698,6 +726,11 @@ void VoxelInstancer::save_block(Vector3i grid_pos, int lod_index) const {
|
|||
Layer *layer = _layers[layer_index];
|
||||
CRASH_COND(layer == nullptr);
|
||||
|
||||
if (!layer->persistent) {
|
||||
continue;
|
||||
}
|
||||
ERR_FAIL_COND(layer->persistent_id < 0);
|
||||
|
||||
int *block_index_ptr = layer->blocks.getptr(grid_pos);
|
||||
|
||||
if (block_index_ptr != nullptr) {
|
||||
|
@ -716,8 +749,7 @@ void VoxelInstancer::save_block(Vector3i grid_pos, int lod_index) const {
|
|||
|
||||
const int instance_count = get_visible_instance_count(multimesh);
|
||||
|
||||
ERR_FAIL_COND(layer->id < 0);
|
||||
layer_data.id = layer->id;
|
||||
layer_data.id = layer->persistent_id;
|
||||
|
||||
if (layer->generator.is_valid()) {
|
||||
layer_data.scale_min = layer->generator->get_min_scale();
|
||||
|
@ -914,6 +946,9 @@ void VoxelInstancer::_bind_methods() {
|
|||
ClassDB::bind_method(D_METHOD("set_layer_generator", "layer_index", "generator"),
|
||||
&VoxelInstancer::set_layer_generator);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_layer_persistent", "layer_index", "persistent"),
|
||||
&VoxelInstancer::set_layer_persistent);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_layer_mesh", "layer_index", "mesh"), &VoxelInstancer::set_layer_mesh);
|
||||
ClassDB::bind_method(D_METHOD("set_layer_mesh_material_override", "layer_index", "material"),
|
||||
&VoxelInstancer::set_layer_material_override);
|
||||
|
|
|
@ -45,6 +45,7 @@ public:
|
|||
int add_layer(int lod_index);
|
||||
|
||||
void set_layer_generator(int layer_index, Ref<VoxelInstanceGenerator> generator);
|
||||
void set_layer_persistent(int layer_index, bool persistent);
|
||||
|
||||
void set_layer_mesh(int layer_index, Ref<Mesh> mesh);
|
||||
void set_layer_material_override(int layer_index, Ref<Material> material);
|
||||
|
@ -85,11 +86,12 @@ private:
|
|||
void clear_instances();
|
||||
void update_visibility();
|
||||
void save_block(Vector3i grid_pos, int lod_index) const;
|
||||
void generate_block_on_each_layer(Vector3i grid_pos, int lod_index, Array surface_arrays);
|
||||
|
||||
int find_layer_by_id(int id) const;
|
||||
int find_layer_by_persistent_id(int id) const;
|
||||
int generate_persistent_id() const;
|
||||
|
||||
void create_loaded_blocks(const VoxelInstanceBlockData &instances_data, Vector3i grid_position, int lod_index);
|
||||
void create_blocks(const VoxelInstanceBlockData *instances_data, Vector3i grid_position, int lod_index,
|
||||
Array surface_arrays);
|
||||
|
||||
void create_block_from_transforms(ArraySlice<const Transform> transforms,
|
||||
Vector3i grid_position, Layer *layer, unsigned int layer_index, World *world,
|
||||
|
@ -114,11 +116,14 @@ private:
|
|||
struct Layer {
|
||||
static const int MAX_ID = 0xffff;
|
||||
|
||||
// This ID identifies the layer uniquely within the current volume, and within saved data.
|
||||
// This ID identifies the layer uniquely within saved data. Two layers cannot use the same ID.
|
||||
// Note: layer indexes are used only for fast access, they are not persistent.
|
||||
int id = -1;
|
||||
// TODO Allow non-persistent layers?
|
||||
// These won't need an ID, will not be saved, and will always re-generate. Also rename `id` to `persistent_id`.
|
||||
int persistent_id = -1;
|
||||
// If a layer is persistent, any change to its instances will be saved if the volume has a stream
|
||||
// supporting instances. It will also not generate on top of modified surfaces.
|
||||
// If a layer is not persistent, changes won't get saved, and it will keep generating on all compliant
|
||||
// surfaces.
|
||||
bool persistent = false;
|
||||
|
||||
int lod_index = 0;
|
||||
|
||||
|
|
Loading…
Reference in New Issue