summaryrefslogtreecommitdiff
path: root/tools/editor
diff options
context:
space:
mode:
authorJuan Linietsky <reduzio@gmail.com>2014-09-02 23:13:40 -0300
committerDana Olson <dana@shineuponthee.com>2014-09-15 17:43:28 -0400
commit247e7ed716a4c0bd5140c2b2a938f7118e84fb14 (patch)
tree2487f84d19ea60c4140c085164219a893a4f8566 /tools/editor
parentdbae857b293231882307c52217e9569a17da0f23 (diff)
3D Physics and Other Stuff
-=-=-=-=-=-=-=-=-=-=-=-=-= -New Vehicle (Based on Bullet's RaycastVehicle) - Vehiclebody/VehicleWheel. Demo will come soon, old vehicle (CarBody) will go away soon too. -A lot of fixes to the 3D physics engine -Added KinematicBody with demo -Fixed the space query API for 2D (demo will come soon). 3D is WIP. -Fixed long-standing bug with body_enter/body_exit for Area and Area2D -Performance variables now includes physics (active bodies, collision pairs and islands) -Ability to see what's inside of instanced scenes! -Fixed Blend Shapes (no bs+skeleton yet) -Added an Android JavaClassWrapper singleton for using Android native classes directly from GDScript. This is very Alpha!
Diffstat (limited to 'tools/editor')
-rw-r--r--tools/editor/icons/icon_instance_options.pngbin0 -> 523 bytes
-rw-r--r--tools/editor/io_plugins/editor_import_collada.cpp9
-rw-r--r--tools/editor/plugins/baked_light_baker.cpp2
-rw-r--r--tools/editor/property_editor.cpp18
-rw-r--r--tools/editor/scene_tree_dock.cpp49
-rw-r--r--tools/editor/scene_tree_dock.h1
-rw-r--r--tools/editor/scene_tree_editor.cpp87
-rw-r--r--tools/editor/scene_tree_editor.h11
-rw-r--r--tools/editor/spatial_editor_gizmos.cpp69
-rw-r--r--tools/editor/spatial_editor_gizmos.h15
10 files changed, 251 insertions, 10 deletions
diff --git a/tools/editor/icons/icon_instance_options.png b/tools/editor/icons/icon_instance_options.png
new file mode 100644
index 0000000000..2d3e98b2ea
--- /dev/null
+++ b/tools/editor/icons/icon_instance_options.png
Binary files differ
diff --git a/tools/editor/io_plugins/editor_import_collada.cpp b/tools/editor/io_plugins/editor_import_collada.cpp
index 15a671d623..2dce3fa8f5 100644
--- a/tools/editor/io_plugins/editor_import_collada.cpp
+++ b/tools/editor/io_plugins/editor_import_collada.cpp
@@ -564,6 +564,7 @@ Error ColladaImport::_create_mesh_surfaces(Ref<Mesh>& p_mesh,const Map<String,Co
bool local_xform_mirror=p_local_xform.basis.determinant() < 0;
if (p_morph_data) {
+
//add morphie target
ERR_FAIL_COND_V( !p_morph_data->targets.has("MORPH_TARGET"), ERR_INVALID_DATA );
String mt = p_morph_data->targets["MORPH_TARGET"];
@@ -1478,8 +1479,11 @@ Error ColladaImport::_create_resources(Collada::Node *p_node) {
Transform apply_xform;
Vector<int> bone_remap;
+ print_line("mesh: "+String(mi->get_name()));
+
if (ng->controller) {
+ print_line("has controller");
if (collada.state.skin_controller_data_map.has(ng->source)) {
@@ -1528,9 +1532,12 @@ Error ColladaImport::_create_resources(Collada::Node *p_node) {
bone_remap[i]=bone_remap_map[str];
}
} else if (collada.state.morph_controller_data_map.has(ng->source)) {
+ print_line("is morph "+ng->source);
//it's a morph!!
- morph = &collada.state.morph_controller_data_map[meshid];
+ morph = &collada.state.morph_controller_data_map[ng->source];
meshid=morph->mesh;
+ printf("KKmorph: %p\n",morph);
+ print_line("morph mshid: "+meshid);
} else {
ERR_EXPLAIN("Controller Instance Source '"+ng->source+"' is neither skin or morph!");
ERR_FAIL_V( ERR_INVALID_DATA );
diff --git a/tools/editor/plugins/baked_light_baker.cpp b/tools/editor/plugins/baked_light_baker.cpp
index 1574ce81d7..dea83e0ff8 100644
--- a/tools/editor/plugins/baked_light_baker.cpp
+++ b/tools/editor/plugins/baked_light_baker.cpp
@@ -338,7 +338,7 @@ void BakedLightBaker::_fix_lights() {
}
if (dl.type==VS::LIGHT_OMNI) {
- dl.area=4.0*Math_PI*pow(dl.radius,2.0);
+ dl.area=4.0*Math_PI*pow(dl.radius,2.0f);
dl.constant=1.0/3.5;
} else {
diff --git a/tools/editor/property_editor.cpp b/tools/editor/property_editor.cpp
index 4ac2ff0594..645d967a4b 100644
--- a/tools/editor/property_editor.cpp
+++ b/tools/editor/property_editor.cpp
@@ -38,6 +38,7 @@
#include "scene/scene_string_names.h"
#include "editor_settings.h"
#include "editor_import_export.h"
+#include "editor_node.h"
void CustomPropertyEditor::_notification(int p_what) {
@@ -1619,6 +1620,7 @@ CustomPropertyEditor::CustomPropertyEditor() {
scene_tree = memnew( SceneTreeDialog );
add_child(scene_tree);
scene_tree->connect("selected", this,"_node_path_selected");
+ scene_tree->get_tree()->set_show_enabled_subscene(true);
texture_preview = memnew( TextureFrame );
add_child( texture_preview);
@@ -2037,6 +2039,17 @@ void PropertyEditor::update_tree() {
List<PropertyInfo> plist;
obj->get_property_list(&plist,true);
+ bool draw_red=false;
+
+ {
+ Node *nod = obj->cast_to<Node>();
+ Node *es = EditorNode::get_singleton()->get_edited_scene();
+ if (nod && es!=nod && nod->get_owner()!=es) {
+ draw_red=true;
+ }
+ }
+
+
Color sscolor=get_color("prop_subsection","Editor");
TreeItem * current_category=NULL;
@@ -2141,11 +2154,16 @@ void PropertyEditor::update_tree() {
item->set_metadata( 0, d );
item->set_metadata( 1, p.name );
+
+ if (draw_red)
+ item->set_custom_color(0,Color(0.8,0.4,0.20));
+
if (p.name==selected_property) {
item->select(1);
}
+
//printf("property %s type %i\n",p.name.ascii().get_data(),p.type);
switch( p.type ) {
diff --git a/tools/editor/scene_tree_dock.cpp b/tools/editor/scene_tree_dock.cpp
index c4c3a10f74..e7f4beb46e 100644
--- a/tools/editor/scene_tree_dock.cpp
+++ b/tools/editor/scene_tree_dock.cpp
@@ -108,6 +108,9 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) {
case TOOL_NEW: {
+
+ if (!_validate_no_foreign())
+ break;
create_dialog->popup_centered_ratio();
} break;
case TOOL_INSTANCE: {
@@ -124,6 +127,9 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) {
break;
}
+ if (!_validate_no_foreign())
+ break;
+
file->set_mode(FileDialog::MODE_OPEN_FILE);
List<String> extensions;
ResourceLoader::get_recognized_extensions_for_type("PackedScene",&extensions);
@@ -147,6 +153,8 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) {
if (!current)
break;
+ if (!_validate_no_foreign())
+ break;
connect_dialog->popup_centered_ratio();
connect_dialog->set_node(current);
@@ -156,6 +164,8 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) {
Node *current = scene_tree->get_selected();
if (!current)
break;
+ if (!_validate_no_foreign())
+ break;
groups_editor->set_current(current);
groups_editor->popup_centered_ratio();
} break;
@@ -165,6 +175,9 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) {
if (!selected)
break;
+ if (!_validate_no_foreign())
+ break;
+
Ref<Script> existing = selected->get_script();
if (existing.is_valid())
editor->push_item(existing.ptr());
@@ -183,6 +196,7 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) {
if (!scene_tree->get_selected())
break;
+
if (scene_tree->get_selected()==edited_scene) {
@@ -195,6 +209,9 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) {
}
+ if (!_validate_no_foreign())
+ break;
+
Node * node=scene_tree->get_selected();
ERR_FAIL_COND(!node->get_parent());
int current_pos = node->get_index();
@@ -214,6 +231,7 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) {
if (!edited_scene)
break;
+
if (editor_selection->is_selected(edited_scene)) {
@@ -225,6 +243,9 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) {
break;
}
+ if (!_validate_no_foreign())
+ break;
+
List<Node*> selection = editor_selection->get_selected_node_list();
List<Node*> reselect;
@@ -313,6 +334,7 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) {
if (!scene_tree->get_selected())
break;
+
if (editor_selection->is_selected(edited_scene)) {
@@ -324,6 +346,9 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) {
break;
}
+ if (!_validate_no_foreign())
+ break;
+
List<Node*> nodes = editor_selection->get_selected_node_list();
Set<Node*> nodeset;
for(List<Node*>::Element *E=nodes.front();E;E=E->next()) {
@@ -341,6 +366,9 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) {
if (remove_list.empty())
return;
+ if (!_validate_no_foreign())
+ break;
+
if (p_confirm_override) {
_delete_confirm();
@@ -707,6 +735,25 @@ void SceneTreeDock::_node_prerenamed(Node* p_node, const String& p_new_name) {
}
+bool SceneTreeDock::_validate_no_foreign() {
+
+ List<Node*> selection = editor_selection->get_selected_node_list();
+
+ for (List<Node*>::Element *E=selection.front();E;E=E->next()) {
+
+ if (E->get()!=edited_scene && E->get()->get_owner()!=edited_scene) {
+
+ accept->get_ok()->set_text("Makes Sense!");
+ accept->set_text("Can't operate on nodes from a foreign scene!");
+ accept->popup_centered(Size2(300,70));;
+ return false;
+
+ }
+ }
+
+ return true;
+}
+
void SceneTreeDock::_node_reparent(NodePath p_path,bool p_node_only) {
@@ -894,7 +941,7 @@ void SceneTreeDock::_delete_confirm() {
void SceneTreeDock::_update_tool_buttons() {
Node *sel = scene_tree->get_selected();
- bool disable = !sel;
+ bool disable = !sel || (sel!=edited_scene && sel->get_owner()!=edited_scene);
bool disable_root = disable || sel->get_parent()==scene_root;
tool_buttons[TOOL_INSTANCE]->set_disabled(disable);
diff --git a/tools/editor/scene_tree_dock.h b/tools/editor/scene_tree_dock.h
index 99ef16e6f6..e55a54377a 100644
--- a/tools/editor/scene_tree_dock.h
+++ b/tools/editor/scene_tree_dock.h
@@ -115,6 +115,7 @@ class SceneTreeDock : public VBoxContainer {
void _import_subscene();
+ bool _validate_no_foreign();
void _fill_path_renames(Vector<StringName> base_path,Vector<StringName> new_base_path,Node * p_node, List<Pair<NodePath,NodePath> > *p_renames);
diff --git a/tools/editor/scene_tree_editor.cpp b/tools/editor/scene_tree_editor.cpp
index e0202be84e..59bc24487a 100644
--- a/tools/editor/scene_tree_editor.cpp
+++ b/tools/editor/scene_tree_editor.cpp
@@ -45,6 +45,40 @@ Node *SceneTreeEditor::get_scene_node() {
return NULL;
}
+
+void SceneTreeEditor::_subscene_option(int p_idx) {
+
+ Object *obj = ObjectDB::get_instance(instance_node);
+ if (!obj)
+ return;
+ Node *node = obj->cast_to<Node>();
+ if (!node)
+ return;
+
+ switch(p_idx) {
+
+ case SCENE_MENU_SHOW_CHILDREN: {
+
+ if (node->has_meta("__editor_show_subtree")) {
+ instance_menu->set_item_checked(0,true);
+ node->set_meta("__editor_show_subtree",Variant());
+ _update_tree();
+ } else {
+ node->set_meta("__editor_show_subtree",true);
+ _update_tree();
+ }
+
+ } break;
+ case SCENE_MENU_OPEN: {
+
+ emit_signal("open",node->get_filename());
+ } break;
+
+ }
+
+}
+
+
void SceneTreeEditor::_cell_button_pressed(Object *p_item,int p_column,int p_id) {
TreeItem *item=p_item->cast_to<TreeItem>();
@@ -57,7 +91,19 @@ void SceneTreeEditor::_cell_button_pressed(Object *p_item,int p_column,int p_id)
if (p_id==BUTTON_SUBSCENE) {
//open scene request
- emit_signal("open",n->get_filename());
+ Rect2 item_rect = tree->get_item_rect(item,0);
+ item_rect.pos.y-=tree->get_scroll().y;
+ item_rect.pos+=tree->get_global_pos();
+ instance_menu->set_pos(item_rect.pos+Vector2(0,item_rect.size.y));
+ instance_menu->set_size(Vector2(item_rect.size.x,0));
+ if (n->has_meta("__editor_show_subtree"))
+ instance_menu->set_item_checked(0,true);
+ else
+ instance_menu->set_item_checked(0,false);
+
+ instance_menu->popup();
+ instance_node=n->get_instance_ID();
+ //emit_signal("open",n->get_filename());
} else if (p_id==BUTTON_SCRIPT) {
RefPtr script=n->get_script();
if (!script.is_null())
@@ -119,9 +165,19 @@ void SceneTreeEditor::_add_nodes(Node *p_node,TreeItem *p_parent) {
// only owned nodes are editable, since nodes can create their own (manually owned) child nodes,
// which the editor needs not to know about.
-
- if (!display_foreign && p_node->get_owner()!=get_scene_node() && p_node!=get_scene_node())
- return;
+
+ bool part_of_subscene=false;
+
+ if (!display_foreign && p_node->get_owner()!=get_scene_node() && p_node!=get_scene_node()) {
+
+ if ((show_enabled_subscene || can_open_instance) && p_node->get_owner() && p_node->get_owner()->get_owner()==get_scene_node() && p_node->get_owner()->has_meta("__editor_show_subtree")) {
+
+ part_of_subscene=true;
+ //allow
+ } else {
+ return;
+ }
+ }
TreeItem *item = tree->create_item(p_parent);
item->set_text(0, p_node->get_name() );
@@ -143,8 +199,12 @@ void SceneTreeEditor::_add_nodes(Node *p_node,TreeItem *p_parent) {
icon=get_icon( (has_icon(p_node->get_type(),"EditorIcons")?p_node->get_type():String("Object")),"EditorIcons");
item->set_icon(0, icon );
item->set_metadata( 0,p_node->get_path() );
-
- if (marked.has(p_node)) {
+ if (part_of_subscene) {
+
+ //item->set_selectable(0,marked_selectable);
+ item->set_custom_color(0,Color(0.8,0.4,0.20));
+
+ } else if (marked.has(p_node)) {
item->set_selectable(0,marked_selectable);
item->set_custom_color(0,Color(0.8,0.1,0.10));
@@ -163,7 +223,7 @@ void SceneTreeEditor::_add_nodes(Node *p_node,TreeItem *p_parent) {
if (p_node!=get_scene_node() && p_node->get_filename()!="" && can_open_instance) {
- item->add_button(0,get_icon("Load","EditorIcons"),BUTTON_SUBSCENE);
+ item->add_button(0,get_icon("InstanceOptions","EditorIcons"),BUTTON_SUBSCENE);
item->set_tooltip(0,"Instance: "+p_node->get_filename());
}
@@ -425,6 +485,7 @@ void SceneTreeEditor::_notification(int p_what) {
get_scene()->connect("tree_changed",this,"_tree_changed");
get_scene()->connect("node_removed",this,"_node_removed");
+ instance_menu->set_item_icon(2,get_icon("Load","EditorIcons"));
tree->connect("item_collapsed",this,"_cell_collapsed");
// get_scene()->connect("tree_changed",this,"_tree_changed",Vector<Variant>(),CONNECT_DEFERRED);
@@ -646,6 +707,7 @@ void SceneTreeEditor::_cell_collapsed(Object *p_obj) {
}
+
void SceneTreeEditor::_bind_methods() {
ObjectTypeDB::bind_method("_tree_changed",&SceneTreeEditor::_tree_changed);
@@ -659,6 +721,7 @@ void SceneTreeEditor::_bind_methods() {
ObjectTypeDB::bind_method("_selection_changed",&SceneTreeEditor::_selection_changed);
ObjectTypeDB::bind_method("_cell_button_pressed",&SceneTreeEditor::_cell_button_pressed);
ObjectTypeDB::bind_method("_cell_collapsed",&SceneTreeEditor::_cell_collapsed);
+ ObjectTypeDB::bind_method("_subscene_option",&SceneTreeEditor::_subscene_option);
ObjectTypeDB::bind_method("_node_script_changed",&SceneTreeEditor::_node_script_changed);
ObjectTypeDB::bind_method("_node_visibility_changed",&SceneTreeEditor::_node_visibility_changed);
@@ -714,10 +777,20 @@ SceneTreeEditor::SceneTreeEditor(bool p_label,bool p_can_rename, bool p_can_open
error = memnew( AcceptDialog );
add_child(error);
+ show_enabled_subscene=false;
+
last_hash=0;
pending_test_update=false;
updating_tree=false;
blocked=0;
+
+ instance_menu = memnew( PopupMenu );
+ instance_menu->add_check_item("Show Children",SCENE_MENU_SHOW_CHILDREN);
+ instance_menu->add_separator();
+ instance_menu->add_item("Open in Editor",SCENE_MENU_OPEN);
+ instance_menu->connect("item_pressed",this,"_subscene_option");
+ add_child(instance_menu);
+
}
diff --git a/tools/editor/scene_tree_editor.h b/tools/editor/scene_tree_editor.h
index 19375ba638..5e88c5a41d 100644
--- a/tools/editor/scene_tree_editor.h
+++ b/tools/editor/scene_tree_editor.h
@@ -51,8 +51,15 @@ class SceneTreeEditor : public Control {
BUTTON_GROUP=4,
};
+ enum {
+ SCENE_MENU_SHOW_CHILDREN,
+ SCENE_MENU_OPEN,
+ };
+
Tree *tree;
Node *selected;
+ PopupMenu *instance_menu;
+ ObjectID instance_node;
AcceptDialog *error;
@@ -78,6 +85,7 @@ class SceneTreeEditor : public Control {
bool can_rename;
bool can_open_instance;
bool updating_tree;
+ bool show_enabled_subscene;
void _renamed();
UndoRedo *undo_redo;
@@ -95,6 +103,7 @@ class SceneTreeEditor : public Control {
void _update_selection(TreeItem *item);
void _node_script_changed(Node *p_node);
void _node_visibility_changed(Node *p_node);
+ void _subscene_option(int p_idx);
void _selection_changed();
Node *get_scene_node();
@@ -112,6 +121,8 @@ public:
void set_can_rename(bool p_can_rename) { can_rename=p_can_rename; }
void set_editor_selection(EditorSelection *p_selection);
+ void set_show_enabled_subscene(bool p_show) { show_enabled_subscene=p_show; }
+
void update_tree() { _update_tree(); }
SceneTreeEditor(bool p_label=true,bool p_can_rename=false, bool p_can_open_instance=false);
diff --git a/tools/editor/spatial_editor_gizmos.cpp b/tools/editor/spatial_editor_gizmos.cpp
index c5bf43884e..c2acfed8ff 100644
--- a/tools/editor/spatial_editor_gizmos.cpp
+++ b/tools/editor/spatial_editor_gizmos.cpp
@@ -1538,6 +1538,70 @@ CarWheelSpatialGizmo::CarWheelSpatialGizmo(CarWheel* p_car_wheel){
}
+/////
+
+
+void VehicleWheelSpatialGizmo::redraw() {
+
+ clear();
+
+
+ Vector<Vector3> points;
+
+ float r = car_wheel->get_radius();
+ const int skip=10;
+ for(int i=0;i<=360;i+=skip) {
+
+ float ra=Math::deg2rad(i);
+ float rb=Math::deg2rad(i+skip);
+ Point2 a = Vector2(Math::sin(ra),Math::cos(ra))*r;
+ Point2 b = Vector2(Math::sin(rb),Math::cos(rb))*r;
+
+ points.push_back(Vector3(0,a.x,a.y));
+ points.push_back(Vector3(0,b.x,b.y));
+
+ const int springsec=4;
+
+ for(int j=0;j<springsec;j++) {
+ float t = car_wheel->get_suspension_rest_length()*5;
+ points.push_back(Vector3(a.x,i/360.0*t/springsec+j*(t/springsec),a.y)*0.2);
+ points.push_back(Vector3(b.x,(i+skip)/360.0*t/springsec+j*(t/springsec),b.y)*0.2);
+ }
+
+
+ }
+
+ //travel
+ points.push_back(Vector3(0,0,0));
+ points.push_back(Vector3(0,car_wheel->get_suspension_rest_length(),0));
+
+ //axis
+ points.push_back(Vector3(r*0.2,car_wheel->get_suspension_rest_length(),0));
+ points.push_back(Vector3(-r*0.2,car_wheel->get_suspension_rest_length(),0));
+ //axis
+ points.push_back(Vector3(r*0.2,0,0));
+ points.push_back(Vector3(-r*0.2,0,0));
+
+ //forward line
+ points.push_back(Vector3(0,-r,0));
+ points.push_back(Vector3(0,-r,r*2));
+ points.push_back(Vector3(0,-r,r*2));
+ points.push_back(Vector3(r*2*0.2,-r,r*2*0.8));
+ points.push_back(Vector3(0,-r,r*2));
+ points.push_back(Vector3(-r*2*0.2,-r,r*2*0.8));
+
+ add_lines(points,SpatialEditorGizmos::singleton->car_wheel_material);
+ add_collision_segments(points);
+
+}
+
+VehicleWheelSpatialGizmo::VehicleWheelSpatialGizmo(VehicleWheel* p_car_wheel){
+
+ set_spatial_node(p_car_wheel);
+ car_wheel=p_car_wheel;
+}
+
+
///
@@ -2292,6 +2356,11 @@ Ref<SpatialEditorGizmo> SpatialEditorGizmos::get_gizmo(Spatial *p_spatial) {
Ref<CarWheelSpatialGizmo> misg = memnew( CarWheelSpatialGizmo(p_spatial->cast_to<CarWheel>()) );
return misg;
}
+ if (p_spatial->cast_to<VehicleWheel>()) {
+
+ Ref<VehicleWheelSpatialGizmo> misg = memnew( VehicleWheelSpatialGizmo(p_spatial->cast_to<VehicleWheel>()) );
+ return misg;
+ }
return Ref<SpatialEditorGizmo>();
}
diff --git a/tools/editor/spatial_editor_gizmos.h b/tools/editor/spatial_editor_gizmos.h
index e5c3417166..f519b84a29 100644
--- a/tools/editor/spatial_editor_gizmos.h
+++ b/tools/editor/spatial_editor_gizmos.h
@@ -45,6 +45,7 @@
#include "scene/3d/ray_cast.h"
#include "scene/3d/navigation_mesh.h"
#include "scene/3d/car_body.h"
+#include "scene/3d/vehicle_body.h"
class Camera;
@@ -328,6 +329,20 @@ public:
};
+class VehicleWheelSpatialGizmo : public SpatialGizmoTool {
+
+ OBJ_TYPE(VehicleWheelSpatialGizmo,SpatialGizmoTool);
+
+ VehicleWheel* car_wheel;
+
+public:
+
+ void redraw();
+ VehicleWheelSpatialGizmo(VehicleWheel* p_car_wheel=NULL);
+
+};
+
+
class NavigationMeshSpatialGizmo : public SpatialGizmoTool {
OBJ_TYPE(NavigationMeshSpatialGizmo,SpatialGizmoTool);