Instancing persistence is now opt-in

master
Marc Gilleron 2021-02-08 22:20:18 +00:00
parent 7f47b07e0c
commit c6fdbb675b
3 changed files with 158 additions and 109 deletions

View File

@ -175,6 +175,15 @@ void VoxelInstanceGenerator::generate_transforms(
out_transforms.push_back(t); 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) { void VoxelInstanceGenerator::set_density(float density) {

View File

@ -205,20 +205,21 @@ VoxelInstancer::UpMode VoxelInstancer::get_up_mode() const {
return _up_mode; return _up_mode;
} }
int VoxelInstancer::add_layer(int lod_index) { int VoxelInstancer::generate_persistent_id() const {
// Find unused ID
int id = 0; int id = 0;
for (size_t i = 0; i < _layers.size(); ++i) { for (size_t i = 0; i < _layers.size(); ++i) {
const Layer *layer = _layers[i]; const Layer *layer = _layers[i];
if (layer != nullptr) { if (layer != nullptr) {
id = max(layer->id + 1, id); id = max(layer->persistent_id + 1, id);
break; break;
} }
} }
// Should not happen // Should not happen
ERR_FAIL_COND_V(id > Layer::MAX_ID, -1); ERR_FAIL_COND_V(id > Layer::MAX_ID, -1);
return id;
}
int VoxelInstancer::add_layer(int lod_index) {
int layer_index = -1; int layer_index = -1;
for (size_t i = 0; i < _layers.size(); ++i) { for (size_t i = 0; i < _layers.size(); ++i) {
if (_layers[i] == nullptr) { if (_layers[i] == nullptr) {
@ -233,7 +234,6 @@ int VoxelInstancer::add_layer(int lod_index) {
Layer *layer = memnew(Layer); Layer *layer = memnew(Layer);
layer->lod_index = lod_index; layer->lod_index = lod_index;
layer->id = id;
if (Engine::get_singleton()->is_editor_hint()) { if (Engine::get_singleton()->is_editor_hint()) {
// Put a default generator // Put a default generator
@ -256,6 +256,21 @@ void VoxelInstancer::set_layer_generator(int layer_index, Ref<VoxelInstanceGener
layer->generator = generator; 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) { void VoxelInstancer::set_layer_mesh(int layer_index, Ref<Mesh> mesh) {
ERR_FAIL_INDEX(layer_index, _layers.size()); ERR_FAIL_INDEX(layer_index, _layers.size());
Layer *layer = _layers[layer_index]; 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]; Lod &lod = _lods[lod_index];
auto it = lod.loaded_instances_data.find(grid_position); auto it = lod.loaded_instances_data.find(grid_position);
const VoxelInstanceBlockData *instances_data = nullptr;
if (it != lod.loaded_instances_data.end()) { if (it != lod.loaded_instances_data.end()) {
ERR_FAIL_COND(it->second == nullptr); instances_data = it->second.get();
create_loaded_blocks(*it->second, grid_position, lod_index); ERR_FAIL_COND(instances_data == nullptr);
// Data no longer needed in this form
lod.loaded_instances_data.erase(grid_position);
} else { // We could do this:
generate_block_on_each_layer(grid_position, lod_index, surface_arrays); // 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) { 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); 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) { for (size_t i = 0; i < _layers.size(); ++i) {
const Layer *layer = _layers[i]; const Layer *layer = _layers[i];
if (layer != nullptr && layer->id == id) { if (layer != nullptr && layer->persistent_id == id) {
return i; return i;
} }
} }
return -1; return -1;
} }
void VoxelInstancer::create_loaded_blocks(const VoxelInstanceBlockData &instances_data, Vector3i grid_position, static bool contains_layer_id(const VoxelInstanceBlockData &instances_data, int id) {
int lod_index) { for (auto it = instances_data.layers.begin(); it != instances_data.layers.end(); ++it) {
const VoxelInstanceBlockData::LayerData &layer = *it;
VOXEL_PROFILE_SCOPE(); if (layer.id == id) {
return true;
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;
} }
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) { void VoxelInstancer::create_blocks(const VoxelInstanceBlockData *instances_data_ptr, Vector3i grid_position,
if (surface_arrays.size() == 0) { int lod_index, Array surface_arrays) {
return;
}
PoolVector3Array vertices = surface_arrays[ArrayMesh::ARRAY_VERTEX];
if (vertices.size() == 0) {
return;
}
VOXEL_PROFILE_SCOPE(); VOXEL_PROFILE_SCOPE();
PoolVector3Array normals = surface_arrays[ArrayMesh::ARRAY_NORMAL];
//Ref<VoxelGeneratorGraph> graph_generator = _parent->get_stream();
Lod &lod = _lods[lod_index]; Lod &lod = _lods[lod_index];
const Transform parent_transform = get_global_transform(); const Transform parent_transform = get_global_transform();
Ref<World> world_ref = get_world(); 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_local_transform = Transform(Basis(), (grid_position * lod_block_size).to_vec3());
const Transform block_transform = parent_transform * block_local_transform; const Transform block_transform = parent_transform * block_local_transform;
for (auto it = lod.layers.begin(); it != lod.layers.end(); ++it) { // Load from data if any
const int layer_index = *it; if (instances_data_ptr != nullptr) {
VOXEL_PROFILE_SCOPE();
const VoxelInstanceBlockData &instances_data = *instances_data_ptr;
Layer *layer = _layers[layer_index]; for (auto layer_it = instances_data.layers.begin(); layer_it != instances_data.layers.end(); ++layer_it) {
CRASH_COND(layer == nullptr); const VoxelInstanceBlockData::LayerData &layer_data = *layer_it;
if (layer->generator.is_null()) { const int layer_index = find_layer_by_persistent_id(layer_data.id);
continue; 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); PoolVector3Array vertices = surface_arrays[ArrayMesh::ARRAY_VERTEX];
if (vertices.size() == 0) {
if (block_index_ptr != nullptr) { return;
// The block was already made?
continue;
} }
_transform_cache.clear(); PoolVector3Array normals = surface_arrays[ArrayMesh::ARRAY_NORMAL];
ERR_FAIL_COND(normals.size() == 0);
layer->generator->generate_transforms( for (auto it = lod.layers.begin(); it != lod.layers.end(); ++it) {
_transform_cache, const int layer_index = *it;
grid_position,
lod_index,
layer_index,
surface_arrays,
block_local_transform,
static_cast<VoxelInstanceGenerator::UpMode>(_up_mode));
if (_transform_cache.size() == 0) { Layer *layer = _layers[layer_index];
continue; 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]; Layer *layer = _layers[layer_index];
CRASH_COND(layer == nullptr); CRASH_COND(layer == nullptr);
if (!layer->persistent) {
continue;
}
ERR_FAIL_COND(layer->persistent_id < 0);
int *block_index_ptr = layer->blocks.getptr(grid_pos); int *block_index_ptr = layer->blocks.getptr(grid_pos);
if (block_index_ptr != nullptr) { 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); const int instance_count = get_visible_instance_count(multimesh);
ERR_FAIL_COND(layer->id < 0); layer_data.id = layer->persistent_id;
layer_data.id = layer->id;
if (layer->generator.is_valid()) { if (layer->generator.is_valid()) {
layer_data.scale_min = layer->generator->get_min_scale(); 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"), ClassDB::bind_method(D_METHOD("set_layer_generator", "layer_index", "generator"),
&VoxelInstancer::set_layer_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", "layer_index", "mesh"), &VoxelInstancer::set_layer_mesh);
ClassDB::bind_method(D_METHOD("set_layer_mesh_material_override", "layer_index", "material"), ClassDB::bind_method(D_METHOD("set_layer_mesh_material_override", "layer_index", "material"),
&VoxelInstancer::set_layer_material_override); &VoxelInstancer::set_layer_material_override);

View File

@ -45,6 +45,7 @@ public:
int add_layer(int lod_index); int add_layer(int lod_index);
void set_layer_generator(int layer_index, Ref<VoxelInstanceGenerator> generator); 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_mesh(int layer_index, Ref<Mesh> mesh);
void set_layer_material_override(int layer_index, Ref<Material> material); void set_layer_material_override(int layer_index, Ref<Material> material);
@ -85,11 +86,12 @@ private:
void clear_instances(); void clear_instances();
void update_visibility(); void update_visibility();
void save_block(Vector3i grid_pos, int lod_index) const; 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, void create_block_from_transforms(ArraySlice<const Transform> transforms,
Vector3i grid_position, Layer *layer, unsigned int layer_index, World *world, Vector3i grid_position, Layer *layer, unsigned int layer_index, World *world,
@ -114,11 +116,14 @@ private:
struct Layer { struct Layer {
static const int MAX_ID = 0xffff; 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. // Note: layer indexes are used only for fast access, they are not persistent.
int id = -1; int persistent_id = -1;
// TODO Allow non-persistent layers? // If a layer is persistent, any change to its instances will be saved if the volume has a stream
// These won't need an ID, will not be saved, and will always re-generate. Also rename `id` to `persistent_id`. // 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; int lod_index = 0;