-
Notifications
You must be signed in to change notification settings - Fork 137
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Instancer storage rework #523
Conversation
Good start. I'll work on 4, placement in the tree by region.
Next I'll work on 2/3. |
Great!
MMI and transforms should be reletive to the region origin. |
I have this to return cell location local to the region.
I'm still just poking around the edges. I'm going to build up _instances simultaneously w/ _multimeshes so it doesn't break the old until I know the new works. |
Thinking about it more, I think the transforms in each cell should be relative to the region still. |
The last commit is the first intrusive change to the data structure. It works a little bit, with a lot broken. I'm done for the day in case you want to review it or take a crack and it. I'll start looking again in 10-12 hours. It works enough to paint foliage on the ground at the 0, 0 cell of any region. Outside of that first cell, the height starts to increase linearly. I'm accumulating height somewhere. Undo sort of works, but doesn't fully remove everything, which means destroy and/or destroy_by_location aren't 100%. That's about it. Not much else works. Lots of bugs. If you click the Demo node, there is a dump_tree bool you can click to print the Terrain3D tree and see the MMIs. |
Added a 3rd modified value to the tuple (making it a triple now i guess?) allows much more targeted updates, especially when there are a lot of cells & transforms present. update vertex spacing and update transforms are now both working. removes instances ive started on, but it has issues, only seems to work in region 0, and causes the entire region to clear / suddenly have blank arrays. Maybe need a different approach to it. i didnt touch undo, some issues persist. |
When I loaded up my previous data it crashed when there was no tuple, so I fixed it. Also it was very slow to add. I removed, which cleared them all, then it stopped giving me so many problems by starting with new data. Issues:
After this I clicked around, changed vertex scaling a few times, then was able to paint here. |
Before my comits last night, cell mmis were being translated to their cell positions, which would have required offsetting the transforms per cell rather than per region. Keeping transforms per region makes things easier all around. However loading the current iteration with old data would mean it makes assumptions about the location of the mmis when updating. For now i suggest we test with fresh data each comit until things are stable and everything is working. Then look at converting old data. Remove instances was the last thing I looked at for about 30mins so its pretty broken atm. I have some thoughts on how to make it work correctly. |
ed06faf does fully clear when removing, but causes crashes if adding afterwards? added a destroy call to end of the vertex spacing update, was getting some serious duplication problems with the mmi themselves without it. I think we need a better way to remove the mmi for a cell once its empty. the underlying data seems OK now, just the handling of the MMIs needs some attention. |
Things are a bit of a mess atm with the nested for loops. Vertex spacing is broken still for remove/update. Rather than looping every cell, a similar lookup method to get_region_loc(), would make it possible to calculate the potential cell ids needed, and then check if they exist, bringing the worst case of 16k cells for 2k regions checked per mesh to only cells in the size of the area checked. |
I spent all day banging my head on the crash on close and don't have much to show for it, except that it is an engine bug. It crashes in Resource::GDClass because Godot frees the GDExtension first, then asks a Resource what class it is. It has a non-null _extension pointer which triggers the conditions to trigger it. I can't tell what class it is, other than a Resource. I've done extensive testing trying to tell, printing out instance IDs from all Objects and Resources we're creating, but haven't found anything yet.
Other issues:
|
outside of upgrading old data, everything should be working now, and needs testing. there is a strange issue when repeatedly changing region size without saving between, that causes instancer data to just vanish for some regions. |
Lots of good stuff here. Things are coming along well. Most things are working including undo and storing data within the regions. I put this is the current comprehensive list of issues up top. |
src/terrain_3d_instancer.cpp
Outdated
@@ -573,6 +573,7 @@ void Terrain3DInstancer::remove_instances(const Vector3 &p_global_position, cons | |||
MultiMeshInstance3D *mmi = cast_to<MultiMeshInstance3D>(mesh_mmis[cell]); | |||
remove_from_tree(mmi); | |||
memdelete_safely(mmi); | |||
mesh_mmis.erase(cell); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How could this fix a crash?
mesh_mmis stores v2i (mesh_id, lod)
Dictionary mesh_mmis = region_mmis[Vector2i(m, 0)];
cells are v2i(0-31, 0-31)
Vector2i cell = cell_queue[c]; -> c_locs.keys();
This line should do nothing, not finding the cell key in the mesh dict, most of the time, but might occasionally delete a valid mesh/lod if the cell matches eg around (0,0,).
I'm working on consolidating mmi deletion from update_transforms, remove_instances, and destroy_by_location into one function.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Without that line, I belive that the mesh_mmis dict would be left with a not-really-null refrence to an mmi that is queue free'd?
when update mmis is called, it would try to access the mmi that was just freed, as it would still be keyed.
I may be wrong.
having some set/get/check/delete functions for the data is 100% needed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see my confusion. We are using different naming conventions.
In your code, you're doing this:
Dictionary region_mmis = _mmi_nodes[region_loc];
Whereas I call it this
Dictionary mesh_dict = _mmi_nodes[region_loc];
_mmi_nodes contains a dictionary of mesh dictionaries keyed by region location. ie, location-> mesh dictionary. So I think mesh_dict is more accurate. Whereas region_mmis or region_dict is what we might call _mmi_nodes itself; a dictionary of region dictionaries keyed by location.
On the next level, you have this:
Dictionary mesh_mmis = region_mmis[Vector2i(m, 0)];
Whereas I've written:
Dictionary cell_dict = mesh_dict[mesh_key];
Here we have a dictionary of cells keyed by mesh_id,lod. So I think cell_dict is a more accurate name.
Further, we are also using multiple nested dictionaries with similar but different structures to track both MMIs and triple[arrays].
Region::_instances{mesh_id:int} -> cell{v2i} -> [ TypedArray<Transform3D>, PackedColorArray, modified:bool ]
_mmi_nodes{region_loc} -> mesh{v2i(mesh_id,lod)} -> cell{v2i} -> MultiMeshInstance3D
I have written both of these in different areas:
Dictionary mesh_dict = region->get_instances();
Dictionary cell_dict = mesh_dict[mesh_key];
Dictionary mesh_dict = _mmi_nodes[region_loc];
Dictionary cell_dict = mesh_dict[mesh_key];
Clearly this is unclear when looking at code if mesh_dict or cell_dict are referring to the data set or the MMI set.
This is confusing to us and impossible for newcomers. So I propose we standardize on this naming convention for the dictionaries to differentiate the two in our variables:
// MM Resources stored in Terrain3DRegion::_instances as
// _instances{mesh_id:int} -> cell{v2i} -> [ TypedArray<Transform3D>, PackedColorArray, modified:bool ]
Dictionary mesh_inst_dict = region->get_instances();
Dictionary cell_inst_dict = mesh_inst_dict[mesh_id];
Array triple = cell_inst_dict[cell];
// MMI Objects attached to tree, freed in destructor, stored as
// _mmi_nodes{region_loc} -> mesh{v2i(mesh_id,lod)} -> cell{v2i} -> MultiMeshInstance3D
Dictionary mesh_mmi_dict = _mmi_nodes[region_loc];
Dictionary cell_mmi_dict = mesh_mmi_dict[mesh_key];
mmi = cell_mmi_dict[cell];
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, yeah, hard agree on the naming convention.
|
I have exposed visibility but it's weird. Defaults to 1024m for now. 0 is disabled. Issues:
Edit: Fixed range calculation by disabling visibility fade mode, and enabled fade in the material. |
Solving that removal problem is going to need some work Density per cell needs reducing by the ratio of (cell area inside the radius of the ring : full cell area) |
The original went:
This goes:
What about:
|
Remove_instances issues:
int mesh_count = _terrain->get_assets()->get_mesh_count();
for (int r = 0; r < region_queue.size(); r++) {
Ref<Terrain3DRegion> region = data->get_region(region_loc);
_backup_region(region); // Note: region hasn't changed yet and might not. Move lower
Dictionary mesh_inst_dict = region->get_instances();
Array mesh_types = mesh_inst_dict.keys();
if (mesh_types.size() == 0) {
continue;
}
// For this mesh id, or all mesh ids
for (int m = (modifier_shift ? 0 : mesh_id); m <= (modifier_shift ? mesh_count - 1 : mesh_id); m++) {
int mesh_id = mesh_types[m]; // Crash
|
Your last fix did pretty well to fix the crash and being able to remove other non-selected meshes. Continuing to test things went well until I CTRL+SHIFT removed a selection of 5 mesh types. As I got to the end of the instances this warning popped up many times for all of the mesh ids. It did remove the instances properly. However running the dump_data function I see the mesh_inst_dicts are still present, though they have no cells. They need to be erased from the parent instances dict. Though I do see a check for it in the code, but apparently it's not happening (or being done to a copy). I updated swap_ids and removed some other functions we don't need. We're getting close. |
More testing on the crash on close:
void Terrain3DEditor::_store_undo() {
IS_INIT_COND_MESG(_terrain->get_plugin() == nullptr, "_terrain isn't initialized, returning", VOID);
if (_tool < 0 || _tool >= TOOL_MAX) {
return;
}
LOG(DEBUG, "Finalize undo & redo snapshots");
Dictionary redo_data;
// Store current locations; Original backed up in start_operation()
redo_data["region_locations"] = _terrain->get_data()->get_region_locations().duplicate();
// Store original and current backups of edited regions
_undo_data["edited_regions"] = _original_regions; // ** causes latent crash **
redo_data["edited_regions"] = _edited_regions; I can return right before that line or comment it out and it won't crash. If I return right after or leave it uncommented it will crash.
Alternatively, clearing the dictionary rather than reassigning it also prevents the crash. However it breaks our undo/redo. Still this suggests maybe the issue is in GDExtension instead of Godot.
I'm bug reporting now, then will attempt to figure out a solution to make a new data pointer so undo works without triggering the bug. |
ecede92
to
92ba372
Compare
92ba372
to
3d28a6d
Compare
Fixes #479
Implements region localised storage of instancer transforms.
See discussion
#524
1. Embed vertex_spacing in transforms, relative to local region.
Add vertex_spacing to Terrain3DRegion.
Only change vertex_spacing of transforms on load/set_vs().
2. Split MM data down to 32x32 storage
append_location -> append_region -> append_cell -> storage in 32x32
3. Change MM/MMI generation off of new data structure
4. Add region container in tree / instancer
5. Support changing region size
6. Upgrade old data from 0.9.2 and 0.9.3
Bug squash and misc: