2017-01-16 08:04:19 +01:00
/**************************************************************************/
2020-03-27 08:44:44 +01:00
/* mesh_instance_3d_editor_plugin.cpp */
2017-01-16 08:04:19 +01:00
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
2018-01-05 00:50:27 +01:00
2020-03-27 08:44:44 +01:00
# include "mesh_instance_3d_editor_plugin.h"
2016-05-23 22:10:26 +02:00
2022-02-12 02:46:22 +01:00
# include "editor/editor_node.h"
2023-08-13 02:33:39 +02:00
# include "editor/editor_string_names.h"
2022-03-25 18:06:46 +01:00
# include "editor/editor_undo_redo_manager.h"
2024-05-06 17:04:19 +02:00
# include "editor/multi_node_edit.h"
2023-04-07 18:59:49 +02:00
# include "editor/plugins/node_3d_editor_plugin.h"
2024-01-15 13:14:55 +01:00
# include "editor/themes/editor_scale.h"
2020-03-26 22:49:16 +01:00
# include "scene/3d/navigation_region_3d.h"
2024-02-26 07:15:31 +01:00
# include "scene/3d/physics/collision_shape_3d.h"
# include "scene/3d/physics/physics_body_3d.h"
# include "scene/3d/physics/static_body_3d.h"
2016-05-23 22:10:26 +02:00
# include "scene/gui/box_container.h"
2023-04-07 18:59:49 +02:00
# include "scene/gui/dialogs.h"
2022-11-19 12:45:49 +01:00
# include "scene/gui/menu_button.h"
2023-04-07 18:59:49 +02:00
# include "scene/gui/spin_box.h"
2021-08-13 18:42:45 +02:00
# include "scene/resources/3d/concave_polygon_shape_3d.h"
# include "scene/resources/3d/convex_polygon_shape_3d.h"
# include "scene/resources/3d/primitive_meshes.h"
2016-05-23 22:10:26 +02:00
2020-03-27 08:44:44 +01:00
void MeshInstance3DEditor : : _node_removed ( Node * p_node ) {
2016-05-23 22:10:26 +02:00
if ( p_node = = node ) {
2020-04-02 01:20:12 +02:00
node = nullptr ;
2016-05-23 22:10:26 +02:00
options - > hide ( ) ;
}
}
2020-03-27 08:44:44 +01:00
void MeshInstance3DEditor : : edit ( MeshInstance3D * p_mesh ) {
2016-05-23 22:10:26 +02:00
node = p_mesh ;
}
2023-12-30 06:24:01 +01:00
Vector < Ref < Shape3D > > MeshInstance3DEditor : : create_shape_from_mesh ( Ref < Mesh > p_mesh , int p_option , bool p_verbose ) {
Vector < Ref < Shape3D > > shapes ;
2016-05-23 22:10:26 +02:00
switch ( p_option ) {
2023-12-30 06:24:01 +01:00
case SHAPE_TYPE_TRIMESH : {
shapes . push_back ( p_mesh - > create_trimesh_shape ( ) ) ;
2016-05-23 22:10:26 +02:00
2023-12-30 06:24:01 +01:00
if ( p_verbose & & shapes . is_empty ( ) ) {
err_dialog - > set_text ( TTR ( " Couldn't create a Trimesh collision shape. " ) ) ;
err_dialog - > popup_centered ( ) ;
2016-05-23 22:10:26 +02:00
}
} break ;
2023-12-30 06:24:01 +01:00
case SHAPE_TYPE_SINGLE_CONVEX : {
shapes . push_back ( p_mesh - > create_convex_shape ( true , false ) ) ;
2016-05-23 22:10:26 +02:00
2023-12-30 06:24:01 +01:00
if ( p_verbose & & shapes . is_empty ( ) ) {
err_dialog - > set_text ( TTR ( " Couldn't create a single collision shape. " ) ) ;
err_dialog - > popup_centered ( ) ;
2020-05-14 16:41:43 +02:00
}
2019-04-10 22:46:04 +02:00
} break ;
2021-07-07 21:14:12 +02:00
2023-12-30 06:24:01 +01:00
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. " ) ) ;
2020-03-06 18:00:16 +01:00
err_dialog - > popup_centered ( ) ;
2020-01-31 16:30:27 +01:00
}
2023-12-30 06:24:01 +01:00
} break ;
2020-01-31 16:30:27 +01:00
2023-12-30 06:24:01 +01:00
case SHAPE_TYPE_MULTIPLE_CONVEX : {
Ref < MeshConvexDecompositionSettings > settings ;
settings . instantiate ( ) ;
settings - > set_max_convex_hulls ( 32 ) ;
settings - > set_max_concavity ( 0.001 ) ;
2021-07-07 21:14:12 +02:00
2023-12-30 06:24:01 +01:00
shapes = p_mesh - > convex_decompose ( settings ) ;
2020-01-31 16:30:27 +01:00
2023-12-30 06:24:01 +01:00
if ( p_verbose & & shapes . is_empty ( ) ) {
err_dialog - > set_text ( TTR ( " Couldn't create any collision shapes. " ) ) ;
2020-03-06 18:00:16 +01:00
err_dialog - > popup_centered ( ) ;
2020-01-31 16:30:27 +01:00
}
2023-12-30 06:24:01 +01:00
} break ;
2020-01-31 16:30:27 +01:00
2023-12-30 06:24:01 +01:00
default :
break ;
}
return shapes ;
}
2020-01-31 16:30:27 +01:00
2023-12-30 06:24:01 +01:00
void MeshInstance3DEditor : : _create_collision_shape ( ) {
int placement_option = shape_placement - > get_selected ( ) ;
int shape_type_option = shape_type - > get_selected ( ) ;
2020-01-31 16:30:27 +01:00
2023-12-30 06:24:01 +01:00
EditorSelection * editor_selection = EditorNode : : get_singleton ( ) - > get_editor_selection ( ) ;
EditorUndoRedoManager * ur = EditorUndoRedoManager : : get_singleton ( ) ;
2020-01-31 16:30:27 +01:00
2023-12-30 06:24:01 +01:00
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 ;
}
2020-01-31 16:30:27 +01:00
2023-12-30 06:24:01 +01:00
List < Node * > selection = editor_selection - > get_selected_node_list ( ) ;
2020-01-31 16:30:27 +01:00
2023-12-30 06:24:01 +01:00
bool verbose = false ;
if ( selection . is_empty ( ) ) {
selection . push_back ( node ) ;
verbose = true ;
}
2021-07-07 21:14:12 +02:00
2023-12-30 06:24:01 +01:00
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. " ) ) ;
2020-03-06 18:00:16 +01:00
err_dialog - > popup_centered ( ) ;
2019-04-10 22:46:04 +02:00
}
2023-12-30 06:24:01 +01:00
continue ;
}
2019-04-10 22:46:04 +02:00
2023-12-30 06:24:01 +01:00
MeshInstance3D * instance = Object : : cast_to < MeshInstance3D > ( E ) ;
if ( ! instance ) {
continue ;
}
2023-01-26 16:10:26 +01:00
2023-12-30 06:24:01 +01:00
Ref < Mesh > m = instance - > get_mesh ( ) ;
if ( m . is_null ( ) ) {
continue ;
}
2019-04-10 22:46:04 +02:00
2023-12-30 06:24:01 +01:00
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 ) ;
2019-04-10 22:46:04 +02:00
2023-12-30 06:24:01 +01:00
ur - > add_do_method ( instance , " add_child " , body , true ) ;
ur - > add_do_method ( body , " set_owner " , owner ) ;
2023-09-04 17:01:33 +02:00
ur - > add_do_method ( Node3DEditor : : get_singleton ( ) , SceneStringName ( _request_gizmo ) , body ) ;
2019-04-10 22:46:04 +02:00
2023-12-30 06:24:01 +01:00
for ( Ref < Shape3D > shape : shapes ) {
2020-03-26 22:49:16 +01:00
CollisionShape3D * cshape = memnew ( CollisionShape3D ) ;
2023-12-30 06:24:01 +01:00
cshape - > set_shape ( shape ) ;
body - > add_child ( cshape , true ) ;
ur - > add_do_method ( cshape , " set_owner " , owner ) ;
2023-09-04 17:01:33 +02:00
ur - > add_do_method ( Node3DEditor : : get_singleton ( ) , SceneStringName ( _request_gizmo ) , cshape ) ;
2023-12-30 06:24:01 +01:00
}
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 ) ;
2022-04-29 08:06:48 +02:00
cshape - > set_name ( " CollisionShape3D " ) ;
2019-11-02 15:08:50 +01:00
cshape - > set_transform ( node - > get_transform ( ) ) ;
2024-05-06 17:04:19 +02:00
ur - > add_do_method ( E , " add_sibling " , cshape , true ) ;
2019-04-10 22:46:04 +02:00
ur - > add_do_method ( cshape , " set_owner " , owner ) ;
2023-09-04 17:01:33 +02:00
ur - > add_do_method ( Node3DEditor : : get_singleton ( ) , SceneStringName ( _request_gizmo ) , cshape ) ;
2019-04-10 22:46:04 +02:00
ur - > add_do_reference ( cshape ) ;
ur - > add_undo_method ( node - > get_parent ( ) , " remove_child " , cshape ) ;
}
2023-12-30 06:24:01 +01:00
}
}
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 ;
}
2016-05-23 22:10:26 +02:00
2023-12-30 06:24:01 +01:00
switch ( p_option ) {
case MENU_OPTION_CREATE_COLLISION_SHAPE : {
shape_dialog - > popup_centered ( ) ;
2016-05-23 22:10:26 +02:00
} break ;
case MENU_OPTION_CREATE_NAVMESH : {
Ref < NavigationMesh > nmesh = memnew ( NavigationMesh ) ;
2020-05-14 16:41:43 +02:00
if ( nmesh . is_null ( ) ) {
2016-05-23 22:10:26 +02:00
return ;
2020-05-14 16:41:43 +02:00
}
2016-05-23 22:10:26 +02:00
nmesh - > create_from_mesh ( mesh ) ;
2020-03-26 22:49:16 +01:00
NavigationRegion3D * nmi = memnew ( NavigationRegion3D ) ;
2016-05-23 22:10:26 +02:00
nmi - > set_navigation_mesh ( nmesh ) ;
2022-04-29 08:06:48 +02:00
Node * owner = get_tree ( ) - > get_edited_scene_root ( ) ;
2016-05-23 22:10:26 +02:00
2022-12-23 23:53:16 +01:00
EditorUndoRedoManager * ur = EditorUndoRedoManager : : get_singleton ( ) ;
2016-05-23 22:10:26 +02:00
ur - > create_action ( TTR ( " Create Navigation Mesh " ) ) ;
2021-10-21 16:46:07 +02:00
ur - > add_do_method ( node , " add_child " , nmi , true ) ;
2016-05-23 22:10:26 +02:00
ur - > add_do_method ( nmi , " set_owner " , owner ) ;
2023-09-04 17:01:33 +02:00
ur - > add_do_method ( Node3DEditor : : get_singleton ( ) , SceneStringName ( _request_gizmo ) , nmi ) ;
2016-05-23 22:10:26 +02:00
ur - > add_do_reference ( nmi ) ;
ur - > add_undo_method ( node , " remove_child " , nmi ) ;
ur - > commit_action ( ) ;
} break ;
case MENU_OPTION_CREATE_OUTLINE_MESH : {
outline_dialog - > popup_centered ( Vector2 ( 200 , 90 ) ) ;
} break ;
2022-06-19 02:01:19 +02:00
case MENU_OPTION_CREATE_DEBUG_TANGENTS : {
2022-12-23 23:53:16 +01:00
EditorUndoRedoManager * ur = EditorUndoRedoManager : : get_singleton ( ) ;
2022-06-19 02:01:19 +02:00
ur - > create_action ( TTR ( " Create Debug Tangents " ) ) ;
MeshInstance3D * tangents = node - > create_debug_tangents_node ( ) ;
if ( tangents ) {
Node * owner = get_tree ( ) - > get_edited_scene_root ( ) ;
ur - > add_do_reference ( tangents ) ;
ur - > add_do_method ( node , " add_child " , tangents , true ) ;
ur - > add_do_method ( tangents , " set_owner " , owner ) ;
ur - > add_undo_method ( node , " remove_child " , tangents ) ;
}
ur - > commit_action ( ) ;
} break ;
2017-12-09 18:11:26 +01:00
case MENU_OPTION_CREATE_UV2 : {
2023-10-17 14:55:47 +02:00
Ref < Mesh > mesh2 = node - > get_mesh ( ) ;
if ( ! mesh . is_valid ( ) ) {
err_dialog - > set_text ( TTR ( " No mesh to unwrap. " ) ) ;
err_dialog - > popup_centered ( ) ;
return ;
}
// Test if we are allowed to unwrap this mesh resource.
String path = mesh2 - > get_path ( ) ;
int srpos = path . find ( " :: " ) ;
if ( srpos ! = - 1 ) {
String base = path . substr ( 0 , srpos ) ;
if ( ResourceLoader : : get_resource_type ( base ) = = " PackedScene " ) {
if ( ! get_tree ( ) - > get_edited_scene_root ( ) | | get_tree ( ) - > get_edited_scene_root ( ) - > get_scene_file_path ( ) ! = base ) {
err_dialog - > set_text ( TTR ( " Mesh cannot unwrap UVs because it does not belong to the edited scene. Make it unique first. " ) ) ;
err_dialog - > popup_centered ( ) ;
return ;
}
} else {
if ( FileAccess : : exists ( path + " .import " ) ) {
err_dialog - > set_text ( TTR ( " Mesh cannot unwrap UVs because it belongs to another resource which was imported from another file type. Make it unique first. " ) ) ;
err_dialog - > popup_centered ( ) ;
return ;
}
}
} else {
if ( FileAccess : : exists ( path + " .import " ) ) {
err_dialog - > set_text ( TTR ( " Mesh cannot unwrap UVs because it was imported from another file type. Make it unique first. " ) ) ;
err_dialog - > popup_centered ( ) ;
return ;
}
}
Ref < PrimitiveMesh > primitive_mesh = mesh2 ;
2023-10-07 02:18:55 +02:00
if ( primitive_mesh . is_valid ( ) ) {
EditorUndoRedoManager * ur = EditorUndoRedoManager : : get_singleton ( ) ;
ur - > create_action ( TTR ( " Unwrap UV2 " ) ) ;
ur - > add_do_method ( * primitive_mesh , " set_add_uv2 " , true ) ;
ur - > add_undo_method ( * primitive_mesh , " set_add_uv2 " , primitive_mesh - > get_add_uv2 ( ) ) ;
ur - > commit_action ( ) ;
} else {
2023-10-17 14:55:47 +02:00
Ref < ArrayMesh > array_mesh = mesh2 ;
if ( ! array_mesh . is_valid ( ) ) {
2023-10-07 02:18:55 +02:00
err_dialog - > set_text ( TTR ( " Contained Mesh is not of type ArrayMesh. " ) ) ;
err_dialog - > popup_centered ( ) ;
return ;
}
2017-12-09 18:11:26 +01:00
2023-10-17 14:55:47 +02:00
// Preemptively evaluate common fail cases for lightmap unwrapping.
{
if ( array_mesh - > get_blend_shape_count ( ) > 0 ) {
err_dialog - > set_text ( TTR ( " Can't unwrap mesh with blend shapes. " ) ) ;
err_dialog - > popup_centered ( ) ;
return ;
}
for ( int i = 0 ; i < array_mesh - > get_surface_count ( ) ; i + + ) {
Mesh : : PrimitiveType primitive = array_mesh - > surface_get_primitive_type ( i ) ;
if ( primitive ! = Mesh : : PRIMITIVE_TRIANGLES ) {
err_dialog - > set_text ( TTR ( " Only triangles are supported for lightmap unwrap. " ) ) ;
2023-10-07 02:18:55 +02:00
err_dialog - > popup_centered ( ) ;
return ;
}
2023-10-17 14:55:47 +02:00
uint64_t format = array_mesh - > surface_get_format ( i ) ;
2023-11-02 18:30:15 +01:00
if ( ! ( format & Mesh : : ArrayFormat : : ARRAY_FORMAT_NORMAL ) ) {
2023-10-17 14:55:47 +02:00
err_dialog - > set_text ( TTR ( " Normals are required for lightmap unwrap. " ) ) ;
2023-10-07 02:18:55 +02:00
err_dialog - > popup_centered ( ) ;
return ;
}
2022-04-29 08:06:48 +02:00
}
}
2023-10-07 02:18:55 +02:00
2023-10-17 14:55:47 +02:00
Ref < ArrayMesh > unwrapped_mesh = array_mesh - > duplicate ( false ) ;
2023-10-07 02:18:55 +02:00
Error err = unwrapped_mesh - > lightmap_unwrap ( node - > get_global_transform ( ) ) ;
if ( err ! = OK ) {
err_dialog - > set_text ( TTR ( " UV Unwrap failed, mesh may not be manifold? " ) ) ;
2022-04-29 08:06:48 +02:00
err_dialog - > popup_centered ( ) ;
return ;
}
2023-10-07 02:18:55 +02:00
EditorUndoRedoManager * ur = EditorUndoRedoManager : : get_singleton ( ) ;
ur - > create_action ( TTR ( " Unwrap UV2 " ) ) ;
2022-04-29 08:06:48 +02:00
2023-10-07 02:18:55 +02:00
ur - > add_do_method ( node , " set_mesh " , unwrapped_mesh ) ;
ur - > add_do_reference ( node ) ;
2023-10-17 14:55:47 +02:00
ur - > add_do_reference ( array_mesh . ptr ( ) ) ;
2022-04-29 08:06:48 +02:00
2023-10-17 14:55:47 +02:00
ur - > add_undo_method ( node , " set_mesh " , array_mesh ) ;
2023-10-07 02:18:55 +02:00
ur - > add_undo_reference ( unwrapped_mesh . ptr ( ) ) ;
2022-04-29 08:06:48 +02:00
2023-10-07 02:18:55 +02:00
ur - > commit_action ( ) ;
}
2017-12-09 18:11:26 +01:00
} break ;
case MENU_OPTION_DEBUG_UV1 : {
2019-02-12 21:10:08 +01:00
Ref < Mesh > mesh2 = node - > get_mesh ( ) ;
if ( ! mesh2 . is_valid ( ) ) {
2017-12-09 18:11:26 +01:00
err_dialog - > set_text ( TTR ( " No mesh to debug. " ) ) ;
2020-03-06 18:00:16 +01:00
err_dialog - > popup_centered ( ) ;
2017-12-09 18:11:26 +01:00
return ;
}
_create_uv_lines ( 0 ) ;
} break ;
case MENU_OPTION_DEBUG_UV2 : {
2019-02-12 21:10:08 +01:00
Ref < Mesh > mesh2 = node - > get_mesh ( ) ;
if ( ! mesh2 . is_valid ( ) ) {
2017-12-09 18:11:26 +01:00
err_dialog - > set_text ( TTR ( " No mesh to debug. " ) ) ;
2020-03-06 18:00:16 +01:00
err_dialog - > popup_centered ( ) ;
2017-12-09 18:11:26 +01:00
return ;
}
_create_uv_lines ( 1 ) ;
} break ;
}
}
2020-03-27 08:44:44 +01:00
struct MeshInstance3DEditorEdgeSort {
2017-12-09 18:11:26 +01:00
Vector2 a ;
Vector2 b ;
2022-05-19 17:00:06 +02:00
static uint32_t hash ( const MeshInstance3DEditorEdgeSort & p_edge ) {
2022-06-18 16:20:55 +02:00
uint32_t h = hash_murmur3_one_32 ( HashMapHasherDefault : : hash ( p_edge . a ) ) ;
return hash_fmix32 ( hash_murmur3_one_32 ( HashMapHasherDefault : : hash ( p_edge . b ) , h ) ) ;
2022-05-19 17:00:06 +02:00
}
bool operator = = ( const MeshInstance3DEditorEdgeSort & p_b ) const {
return a = = p_b . a & & b = = p_b . b ;
2017-12-09 18:11:26 +01:00
}
2020-03-27 08:44:44 +01:00
MeshInstance3DEditorEdgeSort ( ) { }
MeshInstance3DEditorEdgeSort ( const Vector2 & p_a , const Vector2 & p_b ) {
2017-12-09 18:11:26 +01:00
if ( p_a < p_b ) {
a = p_a ;
b = p_b ;
} else {
b = p_a ;
a = p_b ;
}
}
} ;
2020-03-27 08:44:44 +01:00
void MeshInstance3DEditor : : _create_uv_lines ( int p_layer ) {
2017-12-09 18:11:26 +01:00
Ref < Mesh > mesh = node - > get_mesh ( ) ;
ERR_FAIL_COND ( ! mesh . is_valid ( ) ) ;
2022-05-19 17:00:06 +02:00
HashSet < MeshInstance3DEditorEdgeSort , MeshInstance3DEditorEdgeSort > edges ;
2017-12-09 18:11:26 +01:00
uv_lines . clear ( ) ;
for ( int i = 0 ; i < mesh - > get_surface_count ( ) ; i + + ) {
2020-05-14 16:41:43 +02:00
if ( mesh - > surface_get_primitive_type ( i ) ! = Mesh : : PRIMITIVE_TRIANGLES ) {
2017-12-09 18:11:26 +01:00
continue ;
2020-05-14 16:41:43 +02:00
}
2017-12-09 18:11:26 +01:00
Array a = mesh - > surface_get_arrays ( i ) ;
2020-02-17 22:06:54 +01:00
Vector < Vector2 > uv = a [ p_layer = = 0 ? Mesh : : ARRAY_TEX_UV : Mesh : : ARRAY_TEX_UV2 ] ;
2017-12-09 18:11:26 +01:00
if ( uv . size ( ) = = 0 ) {
2021-07-25 01:11:24 +02:00
err_dialog - > set_text ( vformat ( TTR ( " Mesh has no UV in layer %d. " ) , p_layer + 1 ) ) ;
2020-03-06 18:00:16 +01:00
err_dialog - > popup_centered ( ) ;
2017-12-09 18:11:26 +01:00
return ;
}
2020-02-17 22:06:54 +01:00
const Vector2 * r = uv . ptr ( ) ;
2017-12-09 18:11:26 +01:00
2020-02-17 22:06:54 +01:00
Vector < int > indices = a [ Mesh : : ARRAY_INDEX ] ;
2020-04-02 01:20:12 +02:00
const int * ri = nullptr ;
2017-12-09 18:11:26 +01:00
int ic ;
if ( indices . size ( ) ) {
ic = indices . size ( ) ;
2020-02-17 22:06:54 +01:00
ri = indices . ptr ( ) ;
2017-12-09 18:11:26 +01:00
} else {
ic = uv . size ( ) ;
}
for ( int j = 0 ; j < ic ; j + = 3 ) {
for ( int k = 0 ; k < 3 ; k + + ) {
2020-03-27 08:44:44 +01:00
MeshInstance3DEditorEdgeSort edge ;
2020-03-31 13:50:25 +02:00
if ( ri ) {
2017-12-09 18:11:26 +01:00
edge . a = r [ ri [ j + k ] ] ;
edge . b = r [ ri [ j + ( ( k + 1 ) % 3 ) ] ] ;
} else {
edge . a = r [ j + k ] ;
edge . b = r [ j + ( ( k + 1 ) % 3 ) ] ;
}
2020-05-14 16:41:43 +02:00
if ( edges . has ( edge ) ) {
2017-12-09 18:11:26 +01:00
continue ;
2020-05-14 16:41:43 +02:00
}
2017-12-09 18:11:26 +01:00
uv_lines . push_back ( edge . a ) ;
uv_lines . push_back ( edge . b ) ;
edges . insert ( edge ) ;
}
}
2016-05-23 22:10:26 +02:00
}
2017-12-09 18:11:26 +01:00
2020-03-06 18:00:16 +01:00
debug_uv_dialog - > popup_centered ( ) ;
2017-12-09 18:11:26 +01:00
}
2020-03-27 08:44:44 +01:00
void MeshInstance3DEditor : : _debug_uv_draw ( ) {
2020-05-14 16:41:43 +02:00
if ( uv_lines . size ( ) = = 0 ) {
2017-12-09 18:11:26 +01:00
return ;
2020-05-14 16:41:43 +02:00
}
2017-12-09 18:11:26 +01:00
debug_uv - > set_clip_contents ( true ) ;
2023-08-13 02:33:39 +02:00
debug_uv - > draw_rect ( Rect2 ( Vector2 ( ) , debug_uv - > get_size ( ) ) , get_theme_color ( SNAME ( " dark_color_3 " ) , EditorStringName ( Editor ) ) ) ;
2017-12-09 18:11:26 +01:00
debug_uv - > draw_set_transform ( Vector2 ( ) , 0 , debug_uv - > get_size ( ) ) ;
2021-07-25 01:11:24 +02:00
// Use a translucent color to allow overlapping triangles to be visible.
2023-08-13 02:33:39 +02:00
debug_uv - > draw_multiline ( uv_lines , get_theme_color ( SNAME ( " mono_color " ) , EditorStringName ( Editor ) ) * Color ( 1 , 1 , 1 , 0.5 ) ) ;
2016-05-23 22:10:26 +02:00
}
2020-03-27 08:44:44 +01:00
void MeshInstance3DEditor : : _create_outline_mesh ( ) {
2016-05-23 22:10:26 +02:00
Ref < Mesh > mesh = node - > get_mesh ( ) ;
if ( mesh . is_null ( ) ) {
2020-03-30 18:22:57 +02:00
err_dialog - > set_text ( TTR ( " MeshInstance3D lacks a Mesh. " ) ) ;
2020-03-06 18:00:16 +01:00
err_dialog - > popup_centered ( ) ;
2016-05-23 22:10:26 +02:00
return ;
}
2016-06-21 00:57:21 +02:00
if ( mesh - > get_surface_count ( ) = = 0 ) {
2021-12-12 13:29:46 +01:00
err_dialog - > set_text ( TTR ( " Mesh has no surface to create outlines from. " ) ) ;
2020-03-06 18:00:16 +01:00
err_dialog - > popup_centered ( ) ;
2018-04-01 17:06:47 +02:00
return ;
} else if ( mesh - > get_surface_count ( ) = = 1 & & mesh - > surface_get_primitive_type ( 0 ) ! = Mesh : : PRIMITIVE_TRIANGLES ) {
2020-03-30 18:22:57 +02:00
err_dialog - > set_text ( TTR ( " Mesh primitive type is not PRIMITIVE_TRIANGLES. " ) ) ;
2020-03-06 18:00:16 +01:00
err_dialog - > popup_centered ( ) ;
2016-06-21 00:57:21 +02:00
return ;
}
2017-01-04 05:16:14 +01:00
Ref < Mesh > mesho = mesh - > create_outline ( outline_size - > get_value ( ) ) ;
2016-05-23 22:10:26 +02:00
if ( mesho . is_null ( ) ) {
2020-03-30 18:22:57 +02:00
err_dialog - > set_text ( TTR ( " Could not create outline. " ) ) ;
2020-03-06 18:00:16 +01:00
err_dialog - > popup_centered ( ) ;
2016-05-23 22:10:26 +02:00
return ;
}
2020-03-26 22:49:16 +01:00
MeshInstance3D * mi = memnew ( MeshInstance3D ) ;
2016-05-23 22:10:26 +02:00
mi - > set_mesh ( mesho ) ;
2022-04-29 08:06:48 +02:00
Node * owner = get_tree ( ) - > get_edited_scene_root ( ) ;
2016-05-23 22:10:26 +02:00
2022-12-23 23:53:16 +01:00
EditorUndoRedoManager * ur = EditorUndoRedoManager : : get_singleton ( ) ;
2016-05-23 22:10:26 +02:00
ur - > create_action ( TTR ( " Create Outline " ) ) ;
2021-10-21 16:46:07 +02:00
ur - > add_do_method ( node , " add_child " , mi , true ) ;
2016-05-23 22:10:26 +02:00
ur - > add_do_method ( mi , " set_owner " , owner ) ;
2023-09-04 17:01:33 +02:00
ur - > add_do_method ( Node3DEditor : : get_singleton ( ) , SceneStringName ( _request_gizmo ) , mi ) ;
2016-05-23 22:10:26 +02:00
ur - > add_do_reference ( mi ) ;
ur - > add_undo_method ( node , " remove_child " , mi ) ;
ur - > commit_action ( ) ;
}
2023-09-13 13:14:07 +02:00
void MeshInstance3DEditor : : _notification ( int p_what ) {
switch ( p_what ) {
case NOTIFICATION_THEME_CHANGED : {
options - > set_icon ( get_editor_theme_icon ( SNAME ( " MeshInstance3D " ) ) ) ;
} break ;
}
2016-05-23 22:10:26 +02:00
}
2020-03-27 08:44:44 +01:00
MeshInstance3DEditor : : MeshInstance3DEditor ( ) {
2016-05-23 22:10:26 +02:00
options = memnew ( MenuButton ) ;
2023-09-13 13:14:07 +02:00
options - > set_text ( TTR ( " Mesh " ) ) ;
2019-04-25 15:27:33 +02:00
options - > set_switch_on_hover ( true ) ;
2020-03-26 22:49:16 +01:00
Node3DEditor : : get_singleton ( ) - > add_control_to_menu_panel ( options ) ;
2016-05-23 22:10:26 +02:00
2023-12-30 06:24:01 +01:00
options - > get_popup ( ) - > add_item ( TTR ( " Create Collision Shape... " ) , MENU_OPTION_CREATE_COLLISION_SHAPE ) ;
2016-05-23 22:10:26 +02:00
options - > get_popup ( ) - > add_item ( TTR ( " Create Navigation Mesh " ) , MENU_OPTION_CREATE_NAVMESH ) ;
options - > get_popup ( ) - > add_separator ( ) ;
2018-04-22 19:36:01 +02:00
options - > get_popup ( ) - > add_item ( TTR ( " Create Outline Mesh... " ) , MENU_OPTION_CREATE_OUTLINE_MESH ) ;
2020-03-26 22:49:16 +01:00
options - > get_popup ( ) - > set_item_tooltip ( options - > get_popup ( ) - > get_item_count ( ) - 1 , TTR ( " Creates a static outline mesh. The outline mesh will have its normals flipped automatically. \n This can be used instead of the StandardMaterial Grow property when using that property isn't possible. " ) ) ;
2022-06-19 02:01:19 +02:00
options - > get_popup ( ) - > add_item ( TTR ( " Create Debug Tangents " ) , MENU_OPTION_CREATE_DEBUG_TANGENTS ) ;
2017-12-09 18:11:26 +01:00
options - > get_popup ( ) - > add_separator ( ) ;
options - > get_popup ( ) - > add_item ( TTR ( " View UV1 " ) , MENU_OPTION_DEBUG_UV1 ) ;
options - > get_popup ( ) - > add_item ( TTR ( " View UV2 " ) , MENU_OPTION_DEBUG_UV2 ) ;
options - > get_popup ( ) - > add_item ( TTR ( " Unwrap UV2 for Lightmap/AO " ) , MENU_OPTION_CREATE_UV2 ) ;
2016-05-23 22:10:26 +02:00
2020-03-27 08:44:44 +01:00
options - > get_popup ( ) - > connect ( " id_pressed " , callable_mp ( this , & MeshInstance3DEditor : : _menu_option ) ) ;
2016-05-23 22:10:26 +02:00
outline_dialog = memnew ( ConfirmationDialog ) ;
outline_dialog - > set_title ( TTR ( " Create Outline Mesh " ) ) ;
2022-07-08 02:31:19 +02:00
outline_dialog - > set_ok_button_text ( TTR ( " Create " ) ) ;
2016-05-23 22:10:26 +02:00
VBoxContainer * outline_dialog_vbc = memnew ( VBoxContainer ) ;
outline_dialog - > add_child ( outline_dialog_vbc ) ;
2017-01-10 05:49:55 +01:00
//outline_dialog->set_child_rect(outline_dialog_vbc);
2016-05-23 22:10:26 +02:00
outline_size = memnew ( SpinBox ) ;
outline_size - > set_min ( 0.001 ) ;
outline_size - > set_max ( 1024 ) ;
outline_size - > set_step ( 0.001 ) ;
2017-01-04 05:16:14 +01:00
outline_size - > set_value ( 0.05 ) ;
2016-05-23 22:10:26 +02:00
outline_dialog_vbc - > add_margin_child ( TTR ( " Outline Size: " ) , outline_size ) ;
add_child ( outline_dialog ) ;
2020-03-27 08:44:44 +01:00
outline_dialog - > connect ( " confirmed " , callable_mp ( this , & MeshInstance3DEditor : : _create_outline_mesh ) ) ;
2016-05-23 22:10:26 +02:00
2023-12-30 06:24:01 +01:00
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. \n This 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. \n This 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. \n This 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. \n This 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 ) ) ;
2016-05-23 22:10:26 +02:00
err_dialog = memnew ( AcceptDialog ) ;
add_child ( err_dialog ) ;
2017-12-09 18:11:26 +01:00
debug_uv_dialog = memnew ( AcceptDialog ) ;
2019-12-27 03:31:55 +01:00
debug_uv_dialog - > set_title ( TTR ( " UV Channel Debug " ) ) ;
2017-12-09 18:11:26 +01:00
add_child ( debug_uv_dialog ) ;
debug_uv = memnew ( Control ) ;
debug_uv - > set_custom_minimum_size ( Size2 ( 600 , 600 ) * EDSCALE ) ;
2024-05-13 16:56:03 +02:00
debug_uv - > connect ( SceneStringName ( draw ) , callable_mp ( this , & MeshInstance3DEditor : : _debug_uv_draw ) ) ;
2017-12-09 18:11:26 +01:00
debug_uv_dialog - > add_child ( debug_uv ) ;
2016-05-23 22:10:26 +02:00
}
2020-03-27 08:44:44 +01:00
void MeshInstance3DEditorPlugin : : edit ( Object * p_object ) {
2024-05-06 17:04:19 +02:00
{
MeshInstance3D * mi = Object : : cast_to < MeshInstance3D > ( p_object ) ;
if ( mi ) {
mesh_editor - > edit ( mi ) ;
return ;
}
}
Ref < MultiNodeEdit > mne = Ref < MultiNodeEdit > ( p_object ) ;
Node * edited_scene = EditorNode : : get_singleton ( ) - > get_edited_scene ( ) ;
if ( mne . is_valid ( ) & & edited_scene ) {
for ( int i = 0 ; i < mne - > get_node_count ( ) ; i + + ) {
MeshInstance3D * mi = Object : : cast_to < MeshInstance3D > ( edited_scene - > get_node ( mne - > get_node ( i ) ) ) ;
if ( mi ) {
mesh_editor - > edit ( mi ) ;
return ;
}
}
}
mesh_editor - > edit ( nullptr ) ;
2016-05-23 22:10:26 +02:00
}
2020-03-27 08:44:44 +01:00
bool MeshInstance3DEditorPlugin : : handles ( Object * p_object ) const {
2024-05-06 17:04:19 +02:00
if ( Object : : cast_to < MeshInstance3D > ( p_object ) ) {
return true ;
}
Ref < MultiNodeEdit > mne = Ref < MultiNodeEdit > ( p_object ) ;
Node * edited_scene = EditorNode : : get_singleton ( ) - > get_edited_scene ( ) ;
if ( mne . is_valid ( ) & & edited_scene ) {
bool has_mesh = false ;
for ( int i = 0 ; i < mne - > get_node_count ( ) ; i + + ) {
if ( Object : : cast_to < MeshInstance3D > ( edited_scene - > get_node ( mne - > get_node ( i ) ) ) ) {
if ( has_mesh ) {
return true ;
} else {
has_mesh = true ;
}
}
}
}
return false ;
2016-05-23 22:10:26 +02:00
}
2020-03-27 08:44:44 +01:00
void MeshInstance3DEditorPlugin : : make_visible ( bool p_visible ) {
2016-05-23 22:10:26 +02:00
if ( p_visible ) {
mesh_editor - > options - > show ( ) ;
} else {
mesh_editor - > options - > hide ( ) ;
2020-04-02 01:20:12 +02:00
mesh_editor - > edit ( nullptr ) ;
2016-05-23 22:10:26 +02:00
}
}
2022-01-27 10:36:51 +01:00
MeshInstance3DEditorPlugin : : MeshInstance3DEditorPlugin ( ) {
2020-03-27 08:44:44 +01:00
mesh_editor = memnew ( MeshInstance3DEditor ) ;
2022-09-07 01:30:54 +02:00
EditorNode : : get_singleton ( ) - > get_main_screen_control ( ) - > add_child ( mesh_editor ) ;
2016-05-23 22:10:26 +02:00
mesh_editor - > options - > hide ( ) ;
}
2020-03-27 08:44:44 +01:00
MeshInstance3DEditorPlugin : : ~ MeshInstance3DEditorPlugin ( ) {
2016-05-23 22:10:26 +02:00
}