Improvements and fixes to occluders
Improvements: * Occluder3D is now an abstract type inherited by: ArrayOccluder3D, QuadOccluder3D, BoxOccluder3D, SphereOccluder3D and PolygonOccluder3D. ArrayOccluder3D serves the same purpose as the old Occluder3D (triangle mesh occluder) while the rest are primitives that can be used to manually place simple occluders. * Occluder baking can now apply simplification. The "bake_simplification_distance" property can be used to set a world-space distance as the desired maximum error, set to 0.1 by default. * Occluders can now be generated on import. Using the "occ" and "occonly" keywords (similar to "col" and "colonly" for colliders) or by enabling on MeshInstance3Ds in the scene's import window. Fixes: * Fixed saving of occluder files after bake. * Fixed a small error where occluders didn't correctly update in the rendering server. Bonus content: * Generalized "CollisionPolygon3DEditor" so it can also be used to edit Resources. Renamed it to "Polygon3DEditor" since it was already being used by other things, not just colliders. * Fixed a small bug in "EditorPropertyArray" where a call to "remove" was left after the "remove_at" rename.
This commit is contained in:
parent
6f425242dc
commit
dd970482c5
23 changed files with 1198 additions and 189 deletions
|
|
@ -39,6 +39,7 @@
|
|||
#include "scene/3d/importer_mesh_instance_3d.h"
|
||||
#include "scene/3d/mesh_instance_3d.h"
|
||||
#include "scene/3d/navigation_region_3d.h"
|
||||
#include "scene/3d/occluder_instance_3d.h"
|
||||
#include "scene/3d/physics_body_3d.h"
|
||||
#include "scene/3d/vehicle_body_3d.h"
|
||||
#include "scene/animation/animation_player.h"
|
||||
|
|
@ -371,10 +372,10 @@ static void _pre_gen_shape_list(Ref<ImporterMesh> &mesh, Vector<Ref<Shape3D>> &r
|
|||
}
|
||||
}
|
||||
|
||||
Node *ResourceImporterScene::_pre_fix_node(Node *p_node, Node *p_root, Map<Ref<ImporterMesh>, Vector<Ref<Shape3D>>> &collision_map, List<Pair<NodePath, Node *>> &r_node_renames) {
|
||||
Node *ResourceImporterScene::_pre_fix_node(Node *p_node, Node *p_root, Map<Ref<ImporterMesh>, Vector<Ref<Shape3D>>> &r_collision_map, Pair<PackedVector3Array, PackedInt32Array> *r_occluder_arrays, List<Pair<NodePath, Node *>> &r_node_renames) {
|
||||
// Children first.
|
||||
for (int i = 0; i < p_node->get_child_count(); i++) {
|
||||
Node *r = _pre_fix_node(p_node->get_child(i), p_root, collision_map, r_node_renames);
|
||||
Node *r = _pre_fix_node(p_node->get_child(i), p_root, r_collision_map, r_occluder_arrays, r_node_renames);
|
||||
if (!r) {
|
||||
i--; // Was erased.
|
||||
}
|
||||
|
|
@ -498,14 +499,14 @@ Node *ResourceImporterScene::_pre_fix_node(Node *p_node, Node *p_root, Map<Ref<I
|
|||
|
||||
if (mesh.is_valid()) {
|
||||
Vector<Ref<Shape3D>> shapes;
|
||||
if (collision_map.has(mesh)) {
|
||||
shapes = collision_map[mesh];
|
||||
if (r_collision_map.has(mesh)) {
|
||||
shapes = r_collision_map[mesh];
|
||||
} else if (_teststr(name, "colonly")) {
|
||||
_pre_gen_shape_list(mesh, shapes, false);
|
||||
collision_map[mesh] = shapes;
|
||||
r_collision_map[mesh] = shapes;
|
||||
} else if (_teststr(name, "convcolonly")) {
|
||||
_pre_gen_shape_list(mesh, shapes, true);
|
||||
collision_map[mesh] = shapes;
|
||||
r_collision_map[mesh] = shapes;
|
||||
}
|
||||
|
||||
if (shapes.size()) {
|
||||
|
|
@ -560,8 +561,8 @@ Node *ResourceImporterScene::_pre_fix_node(Node *p_node, Node *p_root, Map<Ref<I
|
|||
|
||||
if (mesh.is_valid()) {
|
||||
Vector<Ref<Shape3D>> shapes;
|
||||
if (collision_map.has(mesh)) {
|
||||
shapes = collision_map[mesh];
|
||||
if (r_collision_map.has(mesh)) {
|
||||
shapes = r_collision_map[mesh];
|
||||
} else {
|
||||
_pre_gen_shape_list(mesh, shapes, true);
|
||||
}
|
||||
|
|
@ -586,14 +587,14 @@ Node *ResourceImporterScene::_pre_fix_node(Node *p_node, Node *p_root, Map<Ref<I
|
|||
if (mesh.is_valid()) {
|
||||
Vector<Ref<Shape3D>> shapes;
|
||||
String fixed_name;
|
||||
if (collision_map.has(mesh)) {
|
||||
shapes = collision_map[mesh];
|
||||
if (r_collision_map.has(mesh)) {
|
||||
shapes = r_collision_map[mesh];
|
||||
} else if (_teststr(name, "col")) {
|
||||
_pre_gen_shape_list(mesh, shapes, false);
|
||||
collision_map[mesh] = shapes;
|
||||
r_collision_map[mesh] = shapes;
|
||||
} else if (_teststr(name, "convcol")) {
|
||||
_pre_gen_shape_list(mesh, shapes, true);
|
||||
collision_map[mesh] = shapes;
|
||||
r_collision_map[mesh] = shapes;
|
||||
}
|
||||
|
||||
if (_teststr(name, "col")) {
|
||||
|
|
@ -635,7 +636,31 @@ Node *ResourceImporterScene::_pre_fix_node(Node *p_node, Node *p_root, Map<Ref<I
|
|||
p_node->replace_by(nmi);
|
||||
memdelete(p_node);
|
||||
p_node = nmi;
|
||||
} else if (_teststr(name, "occ") || _teststr(name, "occonly")) {
|
||||
if (isroot) {
|
||||
return p_node;
|
||||
}
|
||||
ImporterMeshInstance3D *mi = Object::cast_to<ImporterMeshInstance3D>(p_node);
|
||||
if (mi) {
|
||||
Ref<ImporterMesh> mesh = mi->get_mesh();
|
||||
|
||||
if (mesh.is_valid()) {
|
||||
if (r_occluder_arrays) {
|
||||
OccluderInstance3D::bake_single_node(mi, 0.0f, r_occluder_arrays->first, r_occluder_arrays->second);
|
||||
}
|
||||
if (_teststr(name, "occ")) {
|
||||
String fixed_name = _fixstr(name, "occ");
|
||||
if (!fixed_name.is_empty()) {
|
||||
if (mi->get_parent() && !mi->get_parent()->has_node(fixed_name)) {
|
||||
mi->set_name(fixed_name);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
memdelete(p_node);
|
||||
p_node = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (Object::cast_to<ImporterMeshInstance3D>(p_node)) {
|
||||
//last attempt, maybe collision inside the mesh data
|
||||
|
||||
|
|
@ -644,16 +669,21 @@ Node *ResourceImporterScene::_pre_fix_node(Node *p_node, Node *p_root, Map<Ref<I
|
|||
Ref<ImporterMesh> mesh = mi->get_mesh();
|
||||
if (!mesh.is_null()) {
|
||||
Vector<Ref<Shape3D>> shapes;
|
||||
if (collision_map.has(mesh)) {
|
||||
shapes = collision_map[mesh];
|
||||
if (r_collision_map.has(mesh)) {
|
||||
shapes = r_collision_map[mesh];
|
||||
} else if (_teststr(mesh->get_name(), "col")) {
|
||||
_pre_gen_shape_list(mesh, shapes, false);
|
||||
collision_map[mesh] = shapes;
|
||||
r_collision_map[mesh] = shapes;
|
||||
mesh->set_name(_fixstr(mesh->get_name(), "col"));
|
||||
} else if (_teststr(mesh->get_name(), "convcol")) {
|
||||
_pre_gen_shape_list(mesh, shapes, true);
|
||||
collision_map[mesh] = shapes;
|
||||
r_collision_map[mesh] = shapes;
|
||||
mesh->set_name(_fixstr(mesh->get_name(), "convcol"));
|
||||
} else if (_teststr(mesh->get_name(), "occ")) {
|
||||
if (r_occluder_arrays) {
|
||||
OccluderInstance3D::bake_single_node(mi, 0.0f, r_occluder_arrays->first, r_occluder_arrays->second);
|
||||
}
|
||||
mesh->set_name(_fixstr(mesh->get_name(), "occ"));
|
||||
}
|
||||
|
||||
if (shapes.size()) {
|
||||
|
|
@ -677,10 +707,10 @@ Node *ResourceImporterScene::_pre_fix_node(Node *p_node, Node *p_root, Map<Ref<I
|
|||
return p_node;
|
||||
}
|
||||
|
||||
Node *ResourceImporterScene::_post_fix_node(Node *p_node, Node *p_root, Map<Ref<ImporterMesh>, Vector<Ref<Shape3D>>> &collision_map, Set<Ref<ImporterMesh>> &r_scanned_meshes, const Dictionary &p_node_data, const Dictionary &p_material_data, const Dictionary &p_animation_data, float p_animation_fps) {
|
||||
Node *ResourceImporterScene::_post_fix_node(Node *p_node, Node *p_root, Map<Ref<ImporterMesh>, Vector<Ref<Shape3D>>> &collision_map, Pair<PackedVector3Array, PackedInt32Array> &r_occluder_arrays, Set<Ref<ImporterMesh>> &r_scanned_meshes, const Dictionary &p_node_data, const Dictionary &p_material_data, const Dictionary &p_animation_data, float p_animation_fps) {
|
||||
// children first
|
||||
for (int i = 0; i < p_node->get_child_count(); i++) {
|
||||
Node *r = _post_fix_node(p_node->get_child(i), p_root, collision_map, r_scanned_meshes, p_node_data, p_material_data, p_animation_data, p_animation_fps);
|
||||
Node *r = _post_fix_node(p_node->get_child(i), p_root, collision_map, r_occluder_arrays, r_scanned_meshes, p_node_data, p_material_data, p_animation_data, p_animation_fps);
|
||||
if (!r) {
|
||||
i--; //was erased
|
||||
}
|
||||
|
|
@ -883,6 +913,32 @@ Node *ResourceImporterScene::_post_fix_node(Node *p_node, Node *p_root, Map<Ref<
|
|||
}
|
||||
}
|
||||
|
||||
if (Object::cast_to<ImporterMeshInstance3D>(p_node)) {
|
||||
ImporterMeshInstance3D *mi = Object::cast_to<ImporterMeshInstance3D>(p_node);
|
||||
|
||||
Ref<ImporterMesh> m = mi->get_mesh();
|
||||
|
||||
if (m.is_valid()) {
|
||||
if (node_settings.has("generate/occluder")) {
|
||||
int occluder_mode = node_settings["generate/occluder"];
|
||||
|
||||
if (occluder_mode != OCCLUDER_DISABLED) {
|
||||
float simplification_dist = 0.0f;
|
||||
if (node_settings.has("occluder/simplification_distance")) {
|
||||
simplification_dist = node_settings["occluder/simplification_distance"];
|
||||
}
|
||||
|
||||
OccluderInstance3D::bake_single_node(mi, simplification_dist, r_occluder_arrays.first, r_occluder_arrays.second);
|
||||
|
||||
if (occluder_mode == OCCLUDER_OCCLUDER_ONLY) {
|
||||
memdelete(p_node);
|
||||
p_node = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (Object::cast_to<AnimationPlayer>(p_node)) {
|
||||
AnimationPlayer *ap = Object::cast_to<AnimationPlayer>(p_node);
|
||||
|
||||
|
|
@ -1255,6 +1311,9 @@ void ResourceImporterScene::get_internal_import_options(InternalImportCategory p
|
|||
r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "primitive/radius", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), 1.0));
|
||||
r_options->push_back(ImportOption(PropertyInfo(Variant::VECTOR3, "primitive/position", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), Vector3()));
|
||||
r_options->push_back(ImportOption(PropertyInfo(Variant::VECTOR3, "primitive/rotation", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), Vector3()));
|
||||
|
||||
r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "generate/occluder", PROPERTY_HINT_ENUM, "Disabled,Mesh + Occluder,Occluder Only", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), 0));
|
||||
r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "occluder/simplification_distance", PROPERTY_HINT_RANGE, "0.0,2.0,0.01", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), 0.1f));
|
||||
} break;
|
||||
case INTERNAL_IMPORT_CATEGORY_MESH: {
|
||||
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "save_to_file/enabled", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), false));
|
||||
|
|
@ -1376,6 +1435,11 @@ bool ResourceImporterScene::get_internal_option_visibility(InternalImportCategor
|
|||
(physics_shape == SHAPE_TYPE_CYLINDER ||
|
||||
physics_shape == SHAPE_TYPE_CAPSULE);
|
||||
}
|
||||
|
||||
if (p_option == "occluder/simplification_distance") {
|
||||
// Show only if occluder generation is enabled
|
||||
return p_options.has("generate/occluder") && p_options["generate/occluder"].operator signed int() != OCCLUDER_DISABLED;
|
||||
}
|
||||
} break;
|
||||
case INTERNAL_IMPORT_CATEGORY_MESH: {
|
||||
if (p_option == "save_to_file/path" || p_option == "save_to_file/make_streamable") {
|
||||
|
|
@ -1869,7 +1933,7 @@ Node *ResourceImporterScene::pre_import(const String &p_source_file) {
|
|||
|
||||
Map<Ref<ImporterMesh>, Vector<Ref<Shape3D>>> collision_map;
|
||||
List<Pair<NodePath, Node *>> node_renames;
|
||||
_pre_fix_node(scene, scene, collision_map, node_renames);
|
||||
_pre_fix_node(scene, scene, collision_map, nullptr, node_renames);
|
||||
|
||||
return scene;
|
||||
}
|
||||
|
|
@ -1944,15 +2008,16 @@ Error ResourceImporterScene::import(const String &p_source_file, const String &p
|
|||
|
||||
Set<Ref<ImporterMesh>> scanned_meshes;
|
||||
Map<Ref<ImporterMesh>, Vector<Ref<Shape3D>>> collision_map;
|
||||
Pair<PackedVector3Array, PackedInt32Array> occluder_arrays;
|
||||
List<Pair<NodePath, Node *>> node_renames;
|
||||
|
||||
_pre_fix_node(scene, scene, collision_map, node_renames);
|
||||
_pre_fix_node(scene, scene, collision_map, &occluder_arrays, node_renames);
|
||||
|
||||
for (int i = 0; i < post_importer_plugins.size(); i++) {
|
||||
post_importer_plugins.write[i]->pre_process(scene, p_options);
|
||||
}
|
||||
|
||||
_post_fix_node(scene, scene, collision_map, scanned_meshes, node_data, material_data, animation_data, fps);
|
||||
_post_fix_node(scene, scene, collision_map, occluder_arrays, scanned_meshes, node_data, material_data, animation_data, fps);
|
||||
|
||||
String root_type = p_options["nodes/root_type"];
|
||||
root_type = root_type.split(" ")[0]; // full root_type is "ClassName (filename.gd)" for a script global class.
|
||||
|
|
@ -1989,6 +2054,15 @@ Error ResourceImporterScene::import(const String &p_source_file, const String &p
|
|||
scene->set_name(p_save_path.get_file().get_basename());
|
||||
}
|
||||
|
||||
if (!occluder_arrays.first.is_empty() && !occluder_arrays.second.is_empty()) {
|
||||
Ref<ArrayOccluder3D> occ = memnew(ArrayOccluder3D);
|
||||
occ->set_arrays(occluder_arrays.first, occluder_arrays.second);
|
||||
OccluderInstance3D *occluder_instance = memnew(OccluderInstance3D);
|
||||
occluder_instance->set_occluder(occ);
|
||||
scene->add_child(occluder_instance, true);
|
||||
occluder_instance->set_owner(scene);
|
||||
}
|
||||
|
||||
bool gen_lods = bool(p_options["meshes/generate_lods"]);
|
||||
bool create_shadow_meshes = bool(p_options["meshes/create_shadow_meshes"]);
|
||||
int light_bake_mode = p_options["meshes/light_baking"];
|
||||
|
|
|
|||
|
|
@ -178,6 +178,12 @@ class ResourceImporterScene : public ResourceImporter {
|
|||
NAVMESH_NAVMESH_ONLY,
|
||||
};
|
||||
|
||||
enum OccluderMode {
|
||||
OCCLUDER_DISABLED,
|
||||
OCCLUDER_MESH_AND_OCCLUDER,
|
||||
OCCLUDER_OCCLUDER_ONLY,
|
||||
};
|
||||
|
||||
enum MeshOverride {
|
||||
MESH_OVERRIDE_DEFAULT,
|
||||
MESH_OVERRIDE_ENABLE,
|
||||
|
|
@ -261,8 +267,8 @@ public:
|
|||
// Import scenes *after* everything else (such as textures).
|
||||
virtual int get_import_order() const override { return ResourceImporter::IMPORT_ORDER_SCENE; }
|
||||
|
||||
Node *_pre_fix_node(Node *p_node, Node *p_root, Map<Ref<ImporterMesh>, Vector<Ref<Shape3D>>> &collision_map, List<Pair<NodePath, Node *>> &r_node_renames);
|
||||
Node *_post_fix_node(Node *p_node, Node *p_root, Map<Ref<ImporterMesh>, Vector<Ref<Shape3D>>> &collision_map, Set<Ref<ImporterMesh>> &r_scanned_meshes, const Dictionary &p_node_data, const Dictionary &p_material_data, const Dictionary &p_animation_data, float p_animation_fps);
|
||||
Node *_pre_fix_node(Node *p_node, Node *p_root, Map<Ref<ImporterMesh>, Vector<Ref<Shape3D>>> &r_collision_map, Pair<PackedVector3Array, PackedInt32Array> *r_occluder_arrays, List<Pair<NodePath, Node *>> &r_node_renames);
|
||||
Node *_post_fix_node(Node *p_node, Node *p_root, Map<Ref<ImporterMesh>, Vector<Ref<Shape3D>>> &collision_map, Pair<PackedVector3Array, PackedInt32Array> &r_occluder_arrays, Set<Ref<ImporterMesh>> &r_scanned_meshes, const Dictionary &p_node_data, const Dictionary &p_material_data, const Dictionary &p_animation_data, float p_animation_fps);
|
||||
|
||||
Ref<Animation> _save_animation_to_file(Ref<Animation> anim, bool p_save_to_file, String p_save_to_path, bool p_keep_custom_tracks);
|
||||
void _create_clips(AnimationPlayer *anim, const Array &p_clips, bool p_bake_all);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue