Skip to content
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

Add static body creation equivalent for all collision shape sibling creations #86627

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
320 changes: 153 additions & 167 deletions editor/plugins/mesh_instance_3d_editor_plugin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -59,200 +59,159 @@ void MeshInstance3DEditor::edit(MeshInstance3D *p_mesh) {
node = p_mesh;
}

void MeshInstance3DEditor::_menu_option(int p_option) {
Ref<Mesh> mesh = node->get_mesh();
if (mesh.is_null()) {
err_dialog->set_text(TTR("Mesh is empty!"));
err_dialog->popup_centered();
return;
}

Vector<Ref<Shape3D>> MeshInstance3DEditor::create_shape_from_mesh(Ref<Mesh> p_mesh, int p_option, bool p_verbose) {
Vector<Ref<Shape3D>> shapes;
switch (p_option) {
case MENU_OPTION_CREATE_STATIC_TRIMESH_BODY: {
EditorSelection *editor_selection = EditorNode::get_singleton()->get_editor_selection();
EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();

List<Node *> selection = editor_selection->get_selected_node_list();

if (selection.is_empty()) {
Ref<ConcavePolygonShape3D> shape = mesh->create_trimesh_shape();
if (shape.is_null()) {
err_dialog->set_text(TTR("Couldn't create a Trimesh collision shape."));
err_dialog->popup_centered();
return;
}

CollisionShape3D *cshape = memnew(CollisionShape3D);
cshape->set_shape(shape);
StaticBody3D *body = memnew(StaticBody3D);
body->add_child(cshape, true);

Node *owner = get_tree()->get_edited_scene_root();

ur->create_action(TTR("Create Static Trimesh Body"));
ur->add_do_method(node, "add_child", body, true);
ur->add_do_method(body, "set_owner", owner);
ur->add_do_method(cshape, "set_owner", owner);
ur->add_do_method(Node3DEditor::get_singleton(), SceneStringNames::get_singleton()->_request_gizmo, body);
ur->add_do_method(Node3DEditor::get_singleton(), SceneStringNames::get_singleton()->_request_gizmo, cshape);
ur->add_do_reference(body);
ur->add_undo_method(node, "remove_child", body);
ur->commit_action();
return;
}
case SHAPE_TYPE_TRIMESH: {
shapes.push_back(p_mesh->create_trimesh_shape());

ur->create_action(TTR("Create Static Trimesh Body"));

for (Node *E : selection) {
MeshInstance3D *instance = Object::cast_to<MeshInstance3D>(E);
if (!instance) {
continue;
}

Ref<Mesh> m = instance->get_mesh();
if (m.is_null()) {
continue;
}

Ref<ConcavePolygonShape3D> shape = m->create_trimesh_shape();
if (shape.is_null()) {
continue;
}

CollisionShape3D *cshape = memnew(CollisionShape3D);
cshape->set_shape(shape);
StaticBody3D *body = memnew(StaticBody3D);
body->add_child(cshape, true);

Node *owner = get_tree()->get_edited_scene_root();

ur->add_do_method(instance, "add_child", body, true);
ur->add_do_method(body, "set_owner", owner);
ur->add_do_method(cshape, "set_owner", owner);
ur->add_do_method(Node3DEditor::get_singleton(), SceneStringNames::get_singleton()->_request_gizmo, body);
ur->add_do_method(Node3DEditor::get_singleton(), SceneStringNames::get_singleton()->_request_gizmo, cshape);
ur->add_do_reference(body);
ur->add_undo_method(instance, "remove_child", body);
if (p_verbose && shapes.is_empty()) {
err_dialog->set_text(TTR("Couldn't create a Trimesh collision shape."));
err_dialog->popup_centered();
}

ur->commit_action();

} break;

case MENU_OPTION_CREATE_TRIMESH_COLLISION_SHAPE: {
if (node == get_tree()->get_edited_scene_root()) {
err_dialog->set_text(TTR("This doesn't work on scene root!"));
err_dialog->popup_centered();
return;
}
case SHAPE_TYPE_SINGLE_CONVEX: {
shapes.push_back(p_mesh->create_convex_shape(true, false));

Ref<ConcavePolygonShape3D> shape = mesh->create_trimesh_shape();
if (shape.is_null()) {
return;
if (p_verbose && shapes.is_empty()) {
err_dialog->set_text(TTR("Couldn't create a single collision shape."));
err_dialog->popup_centered();
}

CollisionShape3D *cshape = memnew(CollisionShape3D);
cshape->set_shape(shape);
cshape->set_transform(node->get_transform());

Node *owner = get_tree()->get_edited_scene_root();

EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();

ur->create_action(TTR("Create Trimesh Static Shape"));

ur->add_do_method(node->get_parent(), "add_child", cshape, true);
ur->add_do_method(node->get_parent(), "move_child", cshape, node->get_index() + 1);
ur->add_do_method(cshape, "set_owner", owner);
ur->add_do_method(Node3DEditor::get_singleton(), SceneStringNames::get_singleton()->_request_gizmo, cshape);
ur->add_do_reference(cshape);
ur->add_undo_method(node->get_parent(), "remove_child", cshape);
ur->commit_action();
} break;

case MENU_OPTION_CREATE_SINGLE_CONVEX_COLLISION_SHAPE:
case MENU_OPTION_CREATE_SIMPLIFIED_CONVEX_COLLISION_SHAPE: {
if (node == get_tree()->get_edited_scene_root()) {
err_dialog->set_text(TTR("Can't create a single convex collision shape for the scene root."));
case SHAPE_TYPE_SIMPLIFIED_CONVEX: {
shapes.push_back(p_mesh->create_convex_shape(true, true));

if (p_verbose && shapes.is_empty()) {
err_dialog->set_text(TTR("Couldn't create a simplified collision shape."));
err_dialog->popup_centered();
return;
}
} break;

bool simplify = (p_option == MENU_OPTION_CREATE_SIMPLIFIED_CONVEX_COLLISION_SHAPE);
case SHAPE_TYPE_MULTIPLE_CONVEX: {
Ref<MeshConvexDecompositionSettings> settings;
settings.instantiate();
settings->set_max_convex_hulls(32);
settings->set_max_concavity(0.001);

Ref<ConvexPolygonShape3D> shape = mesh->create_convex_shape(true, simplify);
shapes = p_mesh->convex_decompose(settings);

if (shape.is_null()) {
err_dialog->set_text(TTR("Couldn't create a single convex collision shape."));
if (p_verbose && shapes.is_empty()) {
err_dialog->set_text(TTR("Couldn't create any collision shapes."));
err_dialog->popup_centered();
return;
}
EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
} break;

if (simplify) {
ur->create_action(TTR("Create Simplified Convex Shape"));
} else {
ur->create_action(TTR("Create Single Convex Shape"));
}
default:
break;
}
return shapes;
}

CollisionShape3D *cshape = memnew(CollisionShape3D);
cshape->set_shape(shape);
cshape->set_transform(node->get_transform());
void MeshInstance3DEditor::_create_collision_shape() {
int placement_option = shape_placement->get_selected();
int shape_type_option = shape_type->get_selected();

Node *owner = get_tree()->get_edited_scene_root();
EditorSelection *editor_selection = EditorNode::get_singleton()->get_editor_selection();
EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();

ur->add_do_method(node->get_parent(), "add_child", cshape, true);
ur->add_do_method(node->get_parent(), "move_child", cshape, node->get_index() + 1);
ur->add_do_method(cshape, "set_owner", owner);
ur->add_do_method(Node3DEditor::get_singleton(), SceneStringNames::get_singleton()->_request_gizmo, cshape);
ur->add_do_reference(cshape);
ur->add_undo_method(node->get_parent(), "remove_child", cshape);
switch (shape_type_option) {
case SHAPE_TYPE_TRIMESH: {
ur->create_action(TTR(placement_option == SHAPE_PLACEMENT_SIBLING ? "Create Trimesh Collision Shape Sibling" : "Create Trimesh Static Body"));
} break;
case SHAPE_TYPE_SINGLE_CONVEX: {
ur->create_action(TTR(placement_option == SHAPE_PLACEMENT_SIBLING ? "Create Single Convex Collision Shape Sibling" : "Create Single Convex Static Body"));
} break;
case SHAPE_TYPE_SIMPLIFIED_CONVEX: {
ur->create_action(TTR(placement_option == SHAPE_PLACEMENT_SIBLING ? "Create Simplified Convex Collision Shape Sibling" : "Create Simplified Convex Static Body"));
} break;
case SHAPE_TYPE_MULTIPLE_CONVEX: {
ur->create_action(TTR(placement_option == SHAPE_PLACEMENT_SIBLING ? "Create Multiple Convex Collision Shape Siblings" : "Create Multiple Convex Static Body"));
} break;
default:
break;
}

ur->commit_action();
List<Node *> selection = editor_selection->get_selected_node_list();

} break;
bool verbose = false;
if (selection.is_empty()) {
selection.push_back(node);
verbose = true;
}

case MENU_OPTION_CREATE_MULTIPLE_CONVEX_COLLISION_SHAPES: {
if (node == get_tree()->get_edited_scene_root()) {
err_dialog->set_text(TTR("Can't create multiple convex collision shapes for the scene root."));
for (Node *E : selection) {
if (placement_option == SHAPE_PLACEMENT_SIBLING && E == get_tree()->get_edited_scene_root()) {
if (verbose) {
err_dialog->set_text(TTR("Can't create a collision shape as sibling for the scene root."));
err_dialog->popup_centered();
return;
}
continue;
}

Ref<MeshConvexDecompositionSettings> settings = Ref<MeshConvexDecompositionSettings>();
settings.instantiate();
settings->set_max_convex_hulls(32);
settings->set_max_concavity(0.001);
MeshInstance3D *instance = Object::cast_to<MeshInstance3D>(E);
if (!instance) {
continue;
}

Vector<Ref<Shape3D>> shapes = mesh->convex_decompose(settings);
Ref<Mesh> m = instance->get_mesh();
if (m.is_null()) {
continue;
}

if (!shapes.size()) {
err_dialog->set_text(TTR("Couldn't create any collision shapes."));
err_dialog->popup_centered();
return;
}
EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
Vector<Ref<Shape3D>> shapes = create_shape_from_mesh(m, shape_type_option, verbose);
if (shapes.is_empty()) {
return;
}

Node *owner = get_tree()->get_edited_scene_root();
if (placement_option == SHAPE_PLACEMENT_STATIC_BODY_CHILD) {
StaticBody3D *body = memnew(StaticBody3D);

ur->create_action(TTR("Create Multiple Convex Shapes"));
ur->add_do_method(instance, "add_child", body, true);
ur->add_do_method(body, "set_owner", owner);
ur->add_do_method(Node3DEditor::get_singleton(), SceneStringNames::get_singleton()->_request_gizmo, body);

for (int i = 0; i < shapes.size(); i++) {
for (Ref<Shape3D> shape : shapes) {
CollisionShape3D *cshape = memnew(CollisionShape3D);
cshape->set_shape(shape);
body->add_child(cshape, true);
ur->add_do_method(cshape, "set_owner", owner);
ur->add_do_method(Node3DEditor::get_singleton(), SceneStringNames::get_singleton()->_request_gizmo, cshape);
}
ur->add_do_reference(body);
ur->add_undo_method(instance, "remove_child", body);
} else {
for (Ref<Shape3D> shape : shapes) {
CollisionShape3D *cshape = memnew(CollisionShape3D);
cshape->set_shape(shape);
cshape->set_name("CollisionShape3D");

cshape->set_shape(shapes[i]);
cshape->set_transform(node->get_transform());

Node *owner = get_tree()->get_edited_scene_root();

ur->add_do_method(node->get_parent(), "add_child", cshape);
ur->add_do_method(node->get_parent(), "move_child", cshape, node->get_index() + 1);
ur->add_do_method(E->get_parent(), "add_child", cshape);
ur->add_do_method(E->get_parent(), "move_child", cshape, E->get_index() + 1);
ur->add_do_method(cshape, "set_owner", owner);
ur->add_do_method(Node3DEditor::get_singleton(), SceneStringNames::get_singleton()->_request_gizmo, cshape);
ur->add_do_reference(cshape);
ur->add_undo_method(node->get_parent(), "remove_child", cshape);
}
ur->commit_action();
}
}

ur->commit_action();
}

void MeshInstance3DEditor::_menu_option(int p_option) {
Ref<Mesh> mesh = node->get_mesh();
if (mesh.is_null()) {
err_dialog->set_text(TTR("Mesh is empty!"));
err_dialog->popup_centered();
return;
}

switch (p_option) {
case MENU_OPTION_CREATE_COLLISION_SHAPE: {
shape_dialog->popup_centered();
} break;

case MENU_OPTION_CREATE_NAVMESH: {
Expand Down Expand Up @@ -571,18 +530,7 @@ MeshInstance3DEditor::MeshInstance3DEditor() {
options->set_switch_on_hover(true);
Node3DEditor::get_singleton()->add_control_to_menu_panel(options);

options->get_popup()->add_item(TTR("Create Trimesh Static Body"), MENU_OPTION_CREATE_STATIC_TRIMESH_BODY);
options->get_popup()->set_item_tooltip(-1, TTR("Creates a StaticBody3D and assigns a polygon-based collision shape to it automatically.\nThis is the most accurate (but slowest) option for collision detection."));
options->get_popup()->add_separator();
options->get_popup()->add_item(TTR("Create Trimesh Collision Sibling"), MENU_OPTION_CREATE_TRIMESH_COLLISION_SHAPE);
options->get_popup()->set_item_tooltip(-1, TTR("Creates a polygon-based collision shape.\nThis is the most accurate (but slowest) option for collision detection."));
options->get_popup()->add_item(TTR("Create Single Convex Collision Sibling"), MENU_OPTION_CREATE_SINGLE_CONVEX_COLLISION_SHAPE);
options->get_popup()->set_item_tooltip(-1, TTR("Creates a single convex collision shape.\nThis is the fastest (but least accurate) option for collision detection."));
options->get_popup()->add_item(TTR("Create Simplified Convex Collision Sibling"), MENU_OPTION_CREATE_SIMPLIFIED_CONVEX_COLLISION_SHAPE);
options->get_popup()->set_item_tooltip(-1, TTR("Creates a simplified convex collision shape.\nThis is similar to single collision shape, but can result in a simpler geometry in some cases, at the cost of accuracy."));
options->get_popup()->add_item(TTR("Create Multiple Convex Collision Siblings"), MENU_OPTION_CREATE_MULTIPLE_CONVEX_COLLISION_SHAPES);
options->get_popup()->set_item_tooltip(-1, TTR("Creates a polygon-based collision shape.\nThis is a performance middle-ground between a single convex collision and a polygon-based collision."));
options->get_popup()->add_separator();
options->get_popup()->add_item(TTR("Create Collision Shape..."), MENU_OPTION_CREATE_COLLISION_SHAPE);
options->get_popup()->add_item(TTR("Create Navigation Mesh"), MENU_OPTION_CREATE_NAVMESH);
options->get_popup()->add_separator();
options->get_popup()->add_item(TTR("Create Outline Mesh..."), MENU_OPTION_CREATE_OUTLINE_MESH);
Expand Down Expand Up @@ -613,6 +561,44 @@ MeshInstance3DEditor::MeshInstance3DEditor() {
add_child(outline_dialog);
outline_dialog->connect("confirmed", callable_mp(this, &MeshInstance3DEditor::_create_outline_mesh));

shape_dialog = memnew(ConfirmationDialog);
shape_dialog->set_title(TTR("Create Collision Shape"));
shape_dialog->set_ok_button_text(TTR("Create"));

VBoxContainer *shape_dialog_vbc = memnew(VBoxContainer);
shape_dialog->add_child(shape_dialog_vbc);

Label *l = memnew(Label);
l->set_text(TTR("Collision Shape placement"));
shape_dialog_vbc->add_child(l);

shape_placement = memnew(OptionButton);
shape_placement->set_h_size_flags(SIZE_EXPAND_FILL);
shape_placement->add_item(TTR("Sibling"), SHAPE_PLACEMENT_SIBLING);
shape_placement->set_item_tooltip(-1, TTR("Creates collision shapes as Sibling."));
shape_placement->add_item(TTR("Static Body Child"), SHAPE_PLACEMENT_STATIC_BODY_CHILD);
shape_placement->set_item_tooltip(-1, TTR("Creates a StaticBody3D as child and assigns collision shapes to it."));
shape_dialog_vbc->add_child(shape_placement);

l = memnew(Label);
l->set_text(TTR("Collision Shape Type"));
shape_dialog_vbc->add_child(l);

shape_type = memnew(OptionButton);
shape_type->set_h_size_flags(SIZE_EXPAND_FILL);
shape_type->add_item(TTR("Trimesh"), SHAPE_TYPE_TRIMESH);
shape_type->set_item_tooltip(-1, TTR("Creates a polygon-based collision shape.\nThis is the most accurate (but slowest) option for collision detection."));
shape_type->add_item(TTR("Single Convex"), SHAPE_TYPE_SINGLE_CONVEX);
shape_type->set_item_tooltip(-1, TTR("Creates a single convex collision shape.\nThis is the fastest (but least accurate) option for collision detection."));
shape_type->add_item(TTR("Simplified Convex"), SHAPE_TYPE_SIMPLIFIED_CONVEX);
shape_type->set_item_tooltip(-1, TTR("Creates a simplified convex collision shape.\nThis is similar to single collision shape, but can result in a simpler geometry in some cases, at the cost of accuracy."));
shape_type->add_item(TTR("Multiple Convex"), SHAPE_TYPE_MULTIPLE_CONVEX);
shape_type->set_item_tooltip(-1, TTR("Creates a polygon-based collision shape.\nThis is a performance middle-ground between a single convex collision and a polygon-based collision."));
shape_dialog_vbc->add_child(shape_type);

add_child(shape_dialog);
shape_dialog->connect("confirmed", callable_mp(this, &MeshInstance3DEditor::_create_collision_shape));

err_dialog = memnew(AcceptDialog);
add_child(err_dialog);

Expand Down
Loading
Loading