diff options
Diffstat (limited to 'editor')
-rw-r--r-- | editor/editor_help.cpp | 5 | ||||
-rw-r--r-- | editor/import/editor_scene_importer_gltf.cpp | 282 | ||||
-rw-r--r-- | editor/import/editor_scene_importer_gltf.h | 39 | ||||
-rw-r--r-- | editor/plugins/tile_set_editor_plugin.cpp | 283 | ||||
-rw-r--r-- | editor/plugins/tile_set_editor_plugin.h | 26 |
5 files changed, 422 insertions, 213 deletions
diff --git a/editor/editor_help.cpp b/editor/editor_help.cpp index 814da4b5f4..41bcc7a1ea 100644 --- a/editor/editor_help.cpp +++ b/editor/editor_help.cpp @@ -947,6 +947,7 @@ Error EditorHelp::_goto_desc(const String &p_class, int p_vscr) { class_desc->pop(); class_desc->pop(); + class_desc->push_font(doc_code_font); class_desc->push_indent(1); class_desc->push_table(2); class_desc->set_table_column_expand(1, 1); @@ -1002,6 +1003,7 @@ Error EditorHelp::_goto_desc(const String &p_class, int p_vscr) { class_desc->pop(); //table class_desc->pop(); + class_desc->pop(); // font class_desc->add_newline(); class_desc->add_newline(); } @@ -1072,6 +1074,7 @@ Error EditorHelp::_goto_desc(const String &p_class, int p_vscr) { class_desc->pop(); class_desc->add_newline(); + class_desc->add_newline(); class_desc->push_indent(1); @@ -1393,7 +1396,9 @@ Error EditorHelp::_goto_desc(const String &p_class, int p_vscr) { for (int i = 0; i < methods.size(); i++) { + class_desc->push_font(doc_code_font); _add_method(methods[i], false); + class_desc->pop(); class_desc->add_newline(); class_desc->push_color(text_color); diff --git a/editor/import/editor_scene_importer_gltf.cpp b/editor/import/editor_scene_importer_gltf.cpp index 00eb69a568..189e98ea68 100644 --- a/editor/import/editor_scene_importer_gltf.cpp +++ b/editor/import/editor_scene_importer_gltf.cpp @@ -185,11 +185,13 @@ Error EditorSceneImporterGLTF::_parse_nodes(GLTFState &state) { } if (n.has("skin")) { node->skin = n["skin"]; + /* if (!state.skin_users.has(node->skin)) { state.skin_users[node->skin] = Vector<int>(); } state.skin_users[node->skin].push_back(i); + */ } if (n.has("matrix")) { node->xform = _arr_to_xform(n["matrix"]); @@ -1316,8 +1318,10 @@ Error EditorSceneImporterGLTF::_parse_skins(GLTFState &state) { for (int j = 0; j < joints.size(); j++) { int index = joints[j]; ERR_FAIL_INDEX_V(index, state.nodes.size(), ERR_PARSE_ERROR); - state.nodes[index]->joint_skin = state.skins.size(); - state.nodes[index]->joint_bone = j; + GLTFNode::Joint joint; + joint.skin = state.skins.size(); + joint.bone = j; + state.nodes[index]->joints.push_back(joint); GLTFSkin::Bone bone; bone.node = index; if (bind_matrices.size()) { @@ -1331,7 +1335,7 @@ Error EditorSceneImporterGLTF::_parse_skins(GLTFState &state) { if (d.has("skeleton")) { int skeleton = d["skeleton"]; ERR_FAIL_INDEX_V(skeleton, state.nodes.size(), ERR_PARSE_ERROR); - state.nodes[skeleton]->skeleton_skin = state.skins.size(); + //state.nodes[skeleton]->skeleton_skin = state.skins.size(); print_line("setting skeleton skin to" + itos(skeleton)); skin.skeleton = skeleton; } @@ -1341,7 +1345,7 @@ Error EditorSceneImporterGLTF::_parse_skins(GLTFState &state) { } //locate the right place to put a Skeleton node - + /* if (state.skin_users.has(i)) { Vector<int> users = state.skin_users[i]; int skin_node = -1; @@ -1382,6 +1386,7 @@ Error EditorSceneImporterGLTF::_parse_skins(GLTFState &state) { state.nodes[skin_node]->skeleton_children.push_back(i); } } + */ state.skins.push_back(skin); } print_line("total skins: " + itos(state.skins.size())); @@ -1577,7 +1582,7 @@ void EditorSceneImporterGLTF::_assign_scene_names(GLTFState &state) { if (n->name == "") { if (n->mesh >= 0) { n->name = "Mesh"; - } else if (n->joint_skin >= 0) { + } else if (n->joints.size()) { n->name = "Bone"; } else { n->name = "Node"; @@ -1607,6 +1612,7 @@ void EditorSceneImporterGLTF::_generate_node(GLTFState &state, int p_node, Node } node = mi; + } else if (n->camera >= 0) { ERR_FAIL_INDEX(n->camera, state.cameras.size()); Camera *camera = memnew(Camera); @@ -1625,18 +1631,20 @@ void EditorSceneImporterGLTF::_generate_node(GLTFState &state, int p_node, Node node->set_name(n->name); - if (n->child_of_skeleton >= 0) { - //move skeleton around and place it on node, as the node _is_ a skeleton. - Skeleton *s = skeletons[n->child_of_skeleton]; - p_parent = s; - } - p_parent->add_child(node); node->set_owner(p_owner); node->set_transform(n->xform); - n->godot_node = node; + n->godot_nodes.push_back(node); + + if (n->skin >= 0 && Object::cast_to<MeshInstance>(node)) { + MeshInstance *mi = Object::cast_to<MeshInstance>(node); + //move skeleton around and place it on node, as the node _is_ a skeleton. + Skeleton *s = skeletons[n->skin]; + mi->set_skeleton_path(mi->get_path_to(s)); + } +#if 0 for (int i = 0; i < n->skeleton_children.size(); i++) { Skeleton *s = skeletons[n->skeleton_children[i]]; @@ -1644,36 +1652,39 @@ void EditorSceneImporterGLTF::_generate_node(GLTFState &state, int p_node, Node node->add_child(s); s->set_owner(p_owner); } - +#endif for (int i = 0; i < n->children.size(); i++) { - if (state.nodes[n->children[i]]->joint_skin >= 0) { - _generate_bone(state, n->children[i], skeletons, -1); + if (state.nodes[n->children[i]]->joints.size()) { + _generate_bone(state, n->children[i], skeletons, Vector<int>()); } else { _generate_node(state, n->children[i], node, p_owner, skeletons); } } } -void EditorSceneImporterGLTF::_generate_bone(GLTFState &state, int p_node, Vector<Skeleton *> &skeletons, int p_parent_bone) { +void EditorSceneImporterGLTF::_generate_bone(GLTFState &state, int p_node, Vector<Skeleton *> &skeletons, const Vector<int> &p_parent_bones) { ERR_FAIL_INDEX(p_node, state.nodes.size()); GLTFNode *n = state.nodes[p_node]; + Vector<int> parent_bones; - ERR_FAIL_COND(n->joint_skin < 0); + for (int i = 0; i < n->joints.size(); i++) { + ERR_FAIL_COND(n->joints[i].skin < 0); - int bone_index = skeletons[n->joint_skin]->get_bone_count(); - skeletons[n->joint_skin]->add_bone(n->name); - if (p_parent_bone >= 0) { - skeletons[n->joint_skin]->set_bone_parent(bone_index, p_parent_bone); - } - skeletons[n->joint_skin]->set_bone_rest(bone_index, state.skins[n->joint_skin].bones[n->joint_bone].inverse_bind.affine_inverse()); + int bone_index = skeletons[n->joints[i].skin]->get_bone_count(); + skeletons[n->joints[i].skin]->add_bone(n->name); + if (p_parent_bones.size()) { + skeletons[n->joints[i].skin]->set_bone_parent(bone_index, p_parent_bones[i]); + } + skeletons[n->joints[i].skin]->set_bone_rest(bone_index, state.skins[n->joints[i].skin].bones[n->joints[i].bone].inverse_bind.affine_inverse()); - n->godot_node = skeletons[n->joint_skin]; - n->godot_bone_index = bone_index; + n->godot_nodes.push_back(skeletons[n->joints[i].skin]); + n->joints[i].godot_bone_index = bone_index; + parent_bones.push_back(bone_index); + } for (int i = 0; i < n->children.size(); i++) { - ERR_CONTINUE(state.nodes[n->children[i]]->joint_skin < 0); - _generate_bone(state, n->children[i], skeletons, bone_index); + _generate_bone(state, n->children[i], skeletons, parent_bones); } } @@ -1818,141 +1829,104 @@ void EditorSceneImporterGLTF::_import_animation(GLTFState &state, AnimationPlaye NodePath node_path; GLTFNode *node = state.nodes[E->key()]; - ERR_CONTINUE(!node->godot_node); - - if (node->godot_bone_index >= 0) { - Skeleton *sk = (Skeleton *)node->godot_node; - String path = ap->get_parent()->get_path_to(sk); - String bone = sk->get_bone_name(node->godot_bone_index); - node_path = path + ":" + bone; - } else { - node_path = ap->get_parent()->get_path_to(node->godot_node); - } - - float length = 0; + for (int i = 0; i < node->godot_nodes.size(); i++) { - for (int i = 0; i < track.rotation_track.times.size(); i++) { - length = MAX(length, track.rotation_track.times[i]); - } - for (int i = 0; i < track.translation_track.times.size(); i++) { - length = MAX(length, track.translation_track.times[i]); - } - for (int i = 0; i < track.scale_track.times.size(); i++) { - length = MAX(length, track.scale_track.times[i]); - } - - for (int i = 0; i < track.weight_tracks.size(); i++) { - for (int j = 0; j < track.weight_tracks[i].times.size(); j++) { - length = MAX(length, track.weight_tracks[i].times[j]); + if (node->joints.size()) { + Skeleton *sk = (Skeleton *)node->godot_nodes[i]; + String path = ap->get_parent()->get_path_to(sk); + String bone = sk->get_bone_name(node->joints[i].godot_bone_index); + node_path = path + ":" + bone; + } else { + node_path = ap->get_parent()->get_path_to(node->godot_nodes[i]); } - } - - animation->set_length(length); - - if (track.rotation_track.values.size() || track.translation_track.values.size() || track.scale_track.values.size()) { - //make transform track - int track_idx = animation->get_track_count(); - animation->add_track(Animation::TYPE_TRANSFORM); - animation->track_set_path(track_idx, node_path); - //first determine animation length - float increment = 1.0 / float(bake_fps); - float time = 0.0; + float length = 0; - Vector3 base_pos; - Quat base_rot; - Vector3 base_scale = Vector3(1, 1, 1); - - if (!track.rotation_track.values.size()) { - base_rot = state.nodes[E->key()]->rotation; + for (int i = 0; i < track.rotation_track.times.size(); i++) { + length = MAX(length, track.rotation_track.times[i]); } - - if (!track.translation_track.values.size()) { - base_pos = state.nodes[E->key()]->translation; + for (int i = 0; i < track.translation_track.times.size(); i++) { + length = MAX(length, track.translation_track.times[i]); + } + for (int i = 0; i < track.scale_track.times.size(); i++) { + length = MAX(length, track.scale_track.times[i]); } - if (!track.scale_track.values.size()) { - base_scale = state.nodes[E->key()]->scale; + for (int i = 0; i < track.weight_tracks.size(); i++) { + for (int j = 0; j < track.weight_tracks[i].times.size(); j++) { + length = MAX(length, track.weight_tracks[i].times[j]); + } } - bool last = false; - while (true) { + animation->set_length(length); + + if (track.rotation_track.values.size() || track.translation_track.values.size() || track.scale_track.values.size()) { + //make transform track + int track_idx = animation->get_track_count(); + animation->add_track(Animation::TYPE_TRANSFORM); + animation->track_set_path(track_idx, node_path); + //first determine animation length - Vector3 pos = base_pos; - Quat rot = base_rot; - Vector3 scale = base_scale; + float increment = 1.0 / float(bake_fps); + float time = 0.0; - if (track.translation_track.times.size()) { + Vector3 base_pos; + Quat base_rot; + Vector3 base_scale = Vector3(1, 1, 1); - pos = _interpolate_track<Vector3>(track.translation_track.times, track.translation_track.values, time, track.translation_track.interpolation); + if (!track.rotation_track.values.size()) { + base_rot = state.nodes[E->key()]->rotation; } - if (track.rotation_track.times.size()) { + if (!track.translation_track.values.size()) { + base_pos = state.nodes[E->key()]->translation; + } - rot = _interpolate_track<Quat>(track.rotation_track.times, track.rotation_track.values, time, track.rotation_track.interpolation); + if (!track.scale_track.values.size()) { + base_scale = state.nodes[E->key()]->scale; } - if (track.scale_track.times.size()) { + bool last = false; + while (true) { - scale = _interpolate_track<Vector3>(track.scale_track.times, track.scale_track.values, time, track.scale_track.interpolation); - } + Vector3 pos = base_pos; + Quat rot = base_rot; + Vector3 scale = base_scale; - if (node->godot_bone_index >= 0) { + if (track.translation_track.times.size()) { - Transform xform; - xform.basis = Basis(rot); - xform.basis.scale(scale); - xform.origin = pos; + pos = _interpolate_track<Vector3>(track.translation_track.times, track.translation_track.values, time, track.translation_track.interpolation); + } - Skeleton *skeleton = skeletons[node->joint_skin]; - int bone = node->godot_bone_index; - xform = skeleton->get_bone_rest(bone).affine_inverse() * xform; + if (track.rotation_track.times.size()) { - rot = xform.basis; - rot.normalize(); - scale = xform.basis.get_scale(); - pos = xform.origin; - } + rot = _interpolate_track<Quat>(track.rotation_track.times, track.rotation_track.values, time, track.rotation_track.interpolation); + } - animation->transform_track_insert_key(track_idx, time, pos, rot, scale); + if (track.scale_track.times.size()) { - if (last) { - break; - } - time += increment; - if (time >= length) { - last = true; - time = length; - } - } - } + scale = _interpolate_track<Vector3>(track.scale_track.times, track.scale_track.values, time, track.scale_track.interpolation); + } - for (int i = 0; i < track.weight_tracks.size(); i++) { - ERR_CONTINUE(node->mesh < 0 || node->mesh >= state.meshes.size()); - const GLTFMesh &mesh = state.meshes[node->mesh]; - String prop = "blend_shapes/" + mesh.mesh->get_blend_shape_name(i); - node_path = String(node_path) + ":" + prop; + if (node->joints.size()) { - int track_idx = animation->get_track_count(); - animation->add_track(Animation::TYPE_VALUE); - animation->track_set_path(track_idx, node_path); + Transform xform; + xform.basis = Basis(rot); + xform.basis.scale(scale); + xform.origin = pos; - if (track.weight_tracks[i].interpolation <= GLTFAnimation::INTERP_STEP) { - animation->track_set_interpolation_type(track_idx, track.weight_tracks[i].interpolation == GLTFAnimation::INTERP_STEP ? Animation::INTERPOLATION_NEAREST : Animation::INTERPOLATION_NEAREST); - for (int j = 0; j < track.weight_tracks[i].times.size(); j++) { - float t = track.weight_tracks[i].times[j]; - float w = track.weight_tracks[i].values[j]; - animation->track_insert_key(track_idx, t, w); - } - } else { - //must bake, apologies. - float increment = 1.0 / float(bake_fps); - float time = 0.0; + Skeleton *skeleton = skeletons[node->joints[i].skin]; + int bone = node->joints[i].godot_bone_index; + xform = skeleton->get_bone_rest(bone).affine_inverse() * xform; - bool last = false; - while (true) { + rot = xform.basis; + rot.normalize(); + scale = xform.basis.get_scale(); + pos = xform.origin; + } + + animation->transform_track_insert_key(track_idx, time, pos, rot, scale); - _interpolate_track<float>(track.weight_tracks[i].times, track.weight_tracks[i].values, time, track.weight_tracks[i].interpolation); if (last) { break; } @@ -1963,6 +1937,44 @@ void EditorSceneImporterGLTF::_import_animation(GLTFState &state, AnimationPlaye } } } + + for (int i = 0; i < track.weight_tracks.size(); i++) { + ERR_CONTINUE(node->mesh < 0 || node->mesh >= state.meshes.size()); + const GLTFMesh &mesh = state.meshes[node->mesh]; + String prop = "blend_shapes/" + mesh.mesh->get_blend_shape_name(i); + node_path = String(node_path) + ":" + prop; + + int track_idx = animation->get_track_count(); + animation->add_track(Animation::TYPE_VALUE); + animation->track_set_path(track_idx, node_path); + + if (track.weight_tracks[i].interpolation <= GLTFAnimation::INTERP_STEP) { + animation->track_set_interpolation_type(track_idx, track.weight_tracks[i].interpolation == GLTFAnimation::INTERP_STEP ? Animation::INTERPOLATION_NEAREST : Animation::INTERPOLATION_NEAREST); + for (int j = 0; j < track.weight_tracks[i].times.size(); j++) { + float t = track.weight_tracks[i].times[j]; + float w = track.weight_tracks[i].values[j]; + animation->track_insert_key(track_idx, t, w); + } + } else { + //must bake, apologies. + float increment = 1.0 / float(bake_fps); + float time = 0.0; + + bool last = false; + while (true) { + + _interpolate_track<float>(track.weight_tracks[i].times, track.weight_tracks[i].values, time, track.weight_tracks[i].interpolation); + if (last) { + break; + } + time += increment; + if (time >= length) { + last = true; + time = length; + } + } + } + } } } @@ -1987,8 +1999,8 @@ Spatial *EditorSceneImporterGLTF::_generate_scene(GLTFState &state, int p_bake_f skeletons.push_back(s); } for (int i = 0; i < state.root_nodes.size(); i++) { - if (state.nodes[state.root_nodes[i]]->joint_skin >= 0) { - _generate_bone(state, state.root_nodes[i], skeletons, -1); + if (state.nodes[state.root_nodes[i]]->joints.size()) { + _generate_bone(state, state.root_nodes[i], skeletons, Vector<int>()); } else { _generate_node(state, state.root_nodes[i], root, root, skeletons); } diff --git a/editor/import/editor_scene_importer_gltf.h b/editor/import/editor_scene_importer_gltf.h index 91c584a05a..abbdfa418b 100644 --- a/editor/import/editor_scene_importer_gltf.h +++ b/editor/import/editor_scene_importer_gltf.h @@ -52,18 +52,29 @@ class EditorSceneImporterGLTF : public EditorSceneImporter { Transform xform; String name; - Node *godot_node; - int godot_bone_index; + //Node *godot_node; + //int godot_bone_index; int mesh; int camera; int skin; - int skeleton_skin; - int child_of_skeleton; // put as children of skeleton - Vector<int> skeleton_children; //skeleton put as children of this + //int skeleton_skin; + //int child_of_skeleton; // put as children of skeleton + //Vector<int> skeleton_children; //skeleton put as children of this + + struct Joint { + int skin; + int bone; + int godot_bone_index; + + Joint() { + skin = -1; + bone = -1; + godot_bone_index = -1; + } + }; - int joint_skin; - int joint_bone; + Vector<Joint> joints; //keep them for animation Vector3 translation; @@ -71,17 +82,15 @@ class EditorSceneImporterGLTF : public EditorSceneImporter { Vector3 scale; Vector<int> children; + Vector<Node *> godot_nodes; GLTFNode() { - godot_node = NULL; - godot_bone_index = -1; - joint_skin = -1; - joint_bone = -1; - child_of_skeleton = -1; - skeleton_skin = -1; + // child_of_skeleton = -1; + // skeleton_skin = -1; mesh = -1; camera = -1; parent = -1; + skin = -1; scale = Vector3(1, 1, 1); } }; @@ -235,7 +244,7 @@ class EditorSceneImporterGLTF : public EditorSceneImporter { Vector<GLTFAnimation> animations; - Map<int, Vector<int> > skin_users; //cache skin users + //Map<int, Vector<int> > skin_users; //cache skin users ~GLTFState() { for (int i = 0; i < nodes.size(); i++) { @@ -269,7 +278,7 @@ class EditorSceneImporterGLTF : public EditorSceneImporter { Vector<Basis> _decode_accessor_as_basis(GLTFState &state, int p_accessor, bool p_for_vertex); Vector<Transform> _decode_accessor_as_xform(GLTFState &state, int p_accessor, bool p_for_vertex); - void _generate_bone(GLTFState &state, int p_node, Vector<Skeleton *> &skeletons, int p_parent_bone); + void _generate_bone(GLTFState &state, int p_node, Vector<Skeleton *> &skeletons, const Vector<int> &p_parent_bones); void _generate_node(GLTFState &state, int p_node, Node *p_parent, Node *p_owner, Vector<Skeleton *> &skeletons); void _import_animation(GLTFState &state, AnimationPlayer *ap, int index, int bake_fps, Vector<Skeleton *> skeletons); diff --git a/editor/plugins/tile_set_editor_plugin.cpp b/editor/plugins/tile_set_editor_plugin.cpp index b56585f62c..a8e4d73cd2 100644 --- a/editor/plugins/tile_set_editor_plugin.cpp +++ b/editor/plugins/tile_set_editor_plugin.cpp @@ -343,12 +343,13 @@ AutotileEditor::AutotileEditor(EditorNode *p_editor) { split->add_child(property_editor); helper = memnew(AutotileEditorHelper(this)); - property_editor->call_deferred("edit", helper); + property_editor->edit(helper); // Editor dragging_point = -1; creating_shape = false; + snap_step = Vector2(32, 32); set_custom_minimum_size(Size2(0, 150)); @@ -426,10 +427,78 @@ AutotileEditor::AutotileEditor(EditorNode *p_editor) { tools[SHAPE_KEEP_INSIDE_TILE]->set_toggle_mode(true); tools[SHAPE_KEEP_INSIDE_TILE]->set_pressed(true); tool_containers[TOOLBAR_SHAPE]->add_child(tools[SHAPE_KEEP_INSIDE_TILE]); - tools[SHAPE_SNAP_TO_BITMASK_GRID] = memnew(ToolButton); - tools[SHAPE_SNAP_TO_BITMASK_GRID]->set_toggle_mode(true); - tools[SHAPE_SNAP_TO_BITMASK_GRID]->set_pressed(true); - tool_containers[TOOLBAR_SHAPE]->add_child(tools[SHAPE_SNAP_TO_BITMASK_GRID]); + tools[SHAPE_GRID_SNAP] = memnew(ToolButton); + tools[SHAPE_GRID_SNAP]->set_toggle_mode(true); + tools[SHAPE_GRID_SNAP]->connect("toggled", this, "_on_grid_snap_toggled"); + tool_containers[TOOLBAR_SHAPE]->add_child(tools[SHAPE_GRID_SNAP]); + + hb_grid = memnew(HBoxContainer); + tool_containers[TOOLBAR_SHAPE]->add_child(hb_grid); + + hb_grid->add_child(memnew(VSeparator)); + hb_grid->add_child(memnew(Label(TTR("Offset:")))); + + sb_off_x = memnew(SpinBox); + sb_off_x->set_min(-256); + sb_off_x->set_max(256); + sb_off_x->set_step(1); + sb_off_x->set_value(snap_offset.x); + sb_off_x->set_suffix("px"); + sb_off_x->connect("value_changed", this, "_set_snap_off_x"); + hb_grid->add_child(sb_off_x); + + sb_off_y = memnew(SpinBox); + sb_off_y->set_min(-256); + sb_off_y->set_max(256); + sb_off_y->set_step(1); + sb_off_y->set_value(snap_offset.y); + sb_off_y->set_suffix("px"); + sb_off_y->connect("value_changed", this, "_set_snap_off_y"); + hb_grid->add_child(sb_off_y); + + hb_grid->add_child(memnew(VSeparator)); + hb_grid->add_child(memnew(Label(TTR("Step:")))); + + sb_step_x = memnew(SpinBox); + sb_step_x->set_min(-256); + sb_step_x->set_max(256); + sb_step_x->set_step(1); + sb_step_x->set_value(snap_step.x); + sb_step_x->set_suffix("px"); + sb_step_x->connect("value_changed", this, "_set_snap_step_x"); + hb_grid->add_child(sb_step_x); + + sb_step_y = memnew(SpinBox); + sb_step_y->set_min(-256); + sb_step_y->set_max(256); + sb_step_y->set_step(1); + sb_step_y->set_value(snap_step.y); + sb_step_y->set_suffix("px"); + sb_step_y->connect("value_changed", this, "_set_snap_step_y"); + hb_grid->add_child(sb_step_y); + + hb_grid->add_child(memnew(VSeparator)); + hb_grid->add_child(memnew(Label(TTR("Separation:")))); + + sb_sep_x = memnew(SpinBox); + sb_sep_x->set_min(0); + sb_sep_x->set_max(256); + sb_sep_x->set_step(1); + sb_sep_x->set_value(snap_separation.x); + sb_sep_x->set_suffix("px"); + sb_sep_x->connect("value_changed", this, "_set_snap_sep_x"); + hb_grid->add_child(sb_sep_x); + + sb_sep_y = memnew(SpinBox); + sb_sep_y->set_min(0); + sb_sep_y->set_max(256); + sb_sep_y->set_step(1); + sb_sep_y->set_value(snap_separation.y); + sb_sep_y->set_suffix("px"); + sb_sep_y->connect("value_changed", this, "_set_snap_sep_y"); + hb_grid->add_child(sb_sep_y); + + hb_grid->hide(); spin_priority = memnew(SpinBox); spin_priority->set_min(1); @@ -489,6 +558,13 @@ void AutotileEditor::_bind_methods() { ClassDB::bind_method("_on_workspace_input", &AutotileEditor::_on_workspace_input); ClassDB::bind_method("_on_tool_clicked", &AutotileEditor::_on_tool_clicked); ClassDB::bind_method("_on_priority_changed", &AutotileEditor::_on_priority_changed); + ClassDB::bind_method("_on_grid_snap_toggled", &AutotileEditor::_on_grid_snap_toggled); + ClassDB::bind_method("_set_snap_step_x", &AutotileEditor::_set_snap_step_x); + ClassDB::bind_method("_set_snap_step_y", &AutotileEditor::_set_snap_step_y); + ClassDB::bind_method("_set_snap_off_x", &AutotileEditor::_set_snap_off_x); + ClassDB::bind_method("_set_snap_off_y", &AutotileEditor::_set_snap_off_y); + ClassDB::bind_method("_set_snap_sep_x", &AutotileEditor::_set_snap_sep_x); + ClassDB::bind_method("_set_snap_sep_y", &AutotileEditor::_set_snap_sep_y); } void AutotileEditor::_notification(int p_what) { @@ -501,7 +577,7 @@ void AutotileEditor::_notification(int p_what) { tools[SHAPE_NEW_POLYGON]->set_icon(get_icon("CollisionPolygon2D", "EditorIcons")); tools[SHAPE_DELETE]->set_icon(get_icon("Remove", "EditorIcons")); tools[SHAPE_KEEP_INSIDE_TILE]->set_icon(get_icon("Snap", "EditorIcons")); - tools[SHAPE_SNAP_TO_BITMASK_GRID]->set_icon(get_icon("SnapGrid", "EditorIcons")); + tools[SHAPE_GRID_SNAP]->set_icon(get_icon("SnapGrid", "EditorIcons")); tools[ZOOM_OUT]->set_icon(get_icon("ZoomLess", "EditorIcons")); tools[ZOOM_1]->set_icon(get_icon("ZoomReset", "EditorIcons")); tools[ZOOM_IN]->set_icon(get_icon("ZoomMore", "EditorIcons")); @@ -632,6 +708,7 @@ void AutotileEditor::_on_workspace_draw() { Vector2 coord = edited_shape_coord; draw_highlight_tile(coord); draw_polygon_shapes(); + draw_grid_snap(); } break; case EDITMODE_PRIORITY: { spin_priority->set_value(tile_set->autotile_get_subtile_priority(get_current_tile(), edited_shape_coord)); @@ -880,15 +957,15 @@ void AutotileEditor::_on_workspace_input(const Ref<InputEvent> &p_ie) { Vector<TileSet::ShapeData> sd = tile_set->tile_get_shapes(get_current_tile()); for (int i = 0; i < sd.size(); i++) { if (sd[i].autotile_coord == coord) { - Ref<ConcavePolygonShape2D> shape = sd[i].shape; + Ref<ConvexPolygonShape2D> shape = sd[i].shape; if (shape.is_valid()) { - //FIXME: i need a way to know if the point is countained on the polygon instead of the rect + Rect2 bounding_rect; PoolVector2Array polygon; - bounding_rect.position = shape->get_segments()[0]; - for (int j = 0; j < shape->get_segments().size(); j += 2) { - polygon.push_back(shape->get_segments()[j] + shape_anchor); - bounding_rect.expand_to(shape->get_segments()[j] + shape_anchor); + bounding_rect.position = shape->get_points()[0]; + for (int j = 0; j < shape->get_points().size(); j++) { + polygon.push_back(shape->get_points()[j] + shape_anchor); + bounding_rect.expand_to(shape->get_points()[j] + shape_anchor); } if (bounding_rect.has_point(mb->get_position())) { current_shape = polygon; @@ -905,17 +982,17 @@ void AutotileEditor::_on_workspace_input(const Ref<InputEvent> &p_ie) { if (dragging_point >= 0) { dragging_point = -1; - PoolVector<Vector2> segments; - segments.resize(current_shape.size() * 2); - PoolVector<Vector2>::Write w = segments.write(); + Vector<Vector2> points; for (int i = 0; i < current_shape.size(); i++) { - w[(i << 1) + 0] = current_shape[i] - shape_anchor; - w[(i << 1) + 1] = current_shape[(i + 1) % current_shape.size()] - shape_anchor; + Vector2 p = current_shape[i]; + if (tools[SHAPE_GRID_SNAP]->is_pressed() || tools[SHAPE_KEEP_INSIDE_TILE]->is_pressed()) { + p = snap_point(p); + } + points.push_back(p - shape_anchor); } - w = PoolVector<Vector2>::Write(); - edited_collision_shape->set_segments(segments); + edited_collision_shape->set_points(points); workspace->update(); } @@ -982,11 +1059,30 @@ void AutotileEditor::_on_workspace_input(const Ref<InputEvent> &p_ie) { current_shape.push_back(pos); workspace->update(); } else { + int t_id = get_current_tile(); + if (t_id >= 0) { + Vector<TileSet::ShapeData> sd = tile_set->tile_get_shapes(t_id); + for (int i = 0; i < sd.size(); i++) { + if (sd[i].autotile_coord == edited_shape_coord) { + Ref<ConvexPolygonShape2D> shape = sd[i].shape; + + if (!shape.is_null()) { + sd.remove(i); + tile_set->tile_set_shapes(get_current_tile(), sd); + edited_collision_shape = Ref<Shape2D>(); + current_shape.resize(0); + workspace->update(); + } + break; + } + } + } + creating_shape = true; current_shape.resize(0); current_shape.push_back(snap_point(pos)); } - } else if (mb->is_pressed() && mb->get_button_index() == BUTTON_RIGHT) { + } else if (mb->is_pressed() && mb->get_button_index() == BUTTON_RIGHT && current_shape.size() > 2) { if (creating_shape) { close_shape(shape_anchor); } @@ -1034,7 +1130,7 @@ void AutotileEditor::_on_tool_clicked(int p_tool) { if (index >= 0) { sd.remove(index); tile_set->tile_set_shapes(get_current_tile(), sd); - edited_collision_shape = Ref<ConcavePolygonShape2D>(); + edited_collision_shape = Ref<Shape2D>(); current_shape.resize(0); workspace->update(); } @@ -1081,6 +1177,43 @@ void AutotileEditor::_on_priority_changed(float val) { workspace->update(); } +void AutotileEditor::_on_grid_snap_toggled(bool p_val) { + if (p_val) + hb_grid->show(); + else + hb_grid->hide(); + workspace->update(); +} + +void AutotileEditor::_set_snap_step_x(float p_val) { + snap_step.x = p_val; + workspace->update(); +} + +void AutotileEditor::_set_snap_step_y(float p_val) { + snap_step.y = p_val; + workspace->update(); +} + +void AutotileEditor::_set_snap_off_x(float p_val) { + snap_offset.x = p_val; + workspace->update(); +} + +void AutotileEditor::_set_snap_off_y(float p_val) { + snap_offset.y = p_val; + workspace->update(); +} +void AutotileEditor::_set_snap_sep_x(float p_val) { + snap_separation.x = p_val; + workspace->update(); +} + +void AutotileEditor::_set_snap_sep_y(float p_val) { + snap_separation.y = p_val; + workspace->update(); +} + void AutotileEditor::draw_highlight_tile(Vector2 coord, const Vector<Vector2> &other_highlighted) { Vector2 size = tile_set->autotile_get_size(get_current_tile()); @@ -1103,6 +1236,49 @@ void AutotileEditor::draw_highlight_tile(Vector2 coord, const Vector<Vector2> &o } } +void AutotileEditor::draw_grid_snap() { + if (tools[SHAPE_GRID_SNAP]->is_pressed()) { + Color grid_color = Color(0.39, 0, 1, 0.2f); + Size2 s = workspace->get_size(); + + Vector2 size = tile_set->autotile_get_size(get_current_tile()); + + int width_count = (int)(s.width / (snap_step.x + snap_separation.x)); + int height_count = (int)(s.height / (snap_step.y + snap_separation.y)); + + if (snap_step.x != 0) { + int last_p = 0; + for (int i = 0; i <= width_count; i++) { + if (i == 0 && snap_offset.x != 0) { + last_p = snap_offset.x; + } + if (snap_separation.x != 0 && i != 0) { + workspace->draw_rect(Rect2(last_p, 0, snap_separation.x, s.height), grid_color); + last_p += snap_separation.x; + } else + workspace->draw_line(Point2(last_p, 0), Point2(last_p, s.height), grid_color); + + last_p += snap_step.x; + } + } + + if (snap_step.y != 0) { + int last_p = 0; + for (int i = 0; i <= height_count; i++) { + if (i == 0 && snap_offset.y != 0) { + last_p = snap_offset.y; + } + if (snap_separation.x != 0 && i != 0) { + workspace->draw_rect(Rect2(0, last_p, s.width, snap_separation.y), grid_color); + last_p += snap_separation.y; + } else + workspace->draw_line(Point2(0, last_p), Point2(s.width, last_p), grid_color); + last_p += snap_step.y; + } + } + } +} + void AutotileEditor::draw_polygon_shapes() { int t_id = get_current_tile(); @@ -1119,7 +1295,7 @@ void AutotileEditor::draw_polygon_shapes() { anchor.y += tile_set->autotile_get_spacing(t_id); anchor.x *= coord.x; anchor.y *= coord.y; - Ref<ConcavePolygonShape2D> shape = sd[i].shape; + Ref<ConvexPolygonShape2D> shape = sd[i].shape; if (shape.is_valid()) { Color c_bg; Color c_border; @@ -1138,19 +1314,22 @@ void AutotileEditor::draw_polygon_shapes() { colors.push_back(c_bg); } } else { - for (int j = 0; j < shape->get_segments().size(); j += 2) { - polygon.push_back(shape->get_segments()[j] + anchor); + for (int j = 0; j < shape->get_points().size(); j++) { + polygon.push_back(shape->get_points()[j] + anchor); colors.push_back(c_bg); } } - workspace->draw_polygon(polygon, colors); + if (polygon.size() > 2) { + workspace->draw_polygon(polygon, colors); + } if (coord == edited_shape_coord) { - for (int j = 0; j < shape->get_segments().size(); j += 2) { - workspace->draw_line(shape->get_segments()[j] + anchor, shape->get_segments()[j + 1] + anchor, c_border, 1, true); + for (int j = 0; j < shape->get_points().size() - 1; j++) { + workspace->draw_line(shape->get_points()[j] + anchor, shape->get_points()[j + 1] + anchor, c_border, 1, true); } + if (shape == edited_collision_shape) { for (int j = 0; j < current_shape.size(); j++) { - workspace->draw_circle(current_shape[j], 5, Color(1, 0, 0)); + workspace->draw_circle(current_shape[j], 8 / workspace->get_scale().x, Color(1, 0, 0, 0.7f)); } } } @@ -1198,7 +1377,7 @@ void AutotileEditor::draw_polygon_shapes() { workspace->draw_line(shape->get_polygon()[shape->get_polygon().size() - 1] + anchor, shape->get_polygon()[0] + anchor, c_border, 1, true); if (shape == edited_occlusion_shape) { for (int j = 0; j < current_shape.size(); j++) { - workspace->draw_circle(current_shape[j], 5, Color(1, 0, 0)); + workspace->draw_circle(current_shape[j], 8 / workspace->get_scale().x, Color(1, 0, 0)); } } } @@ -1248,7 +1427,7 @@ void AutotileEditor::draw_polygon_shapes() { } if (shape == edited_navigation_shape) { for (int j = 0; j < current_shape.size(); j++) { - workspace->draw_circle(current_shape[j], 5, Color(1, 0, 0)); + workspace->draw_circle(current_shape[j], 8 / workspace->get_scale().x, Color(1, 0, 0)); } } } @@ -1270,22 +1449,21 @@ void AutotileEditor::close_shape(const Vector2 &shape_anchor) { creating_shape = false; if (edit_mode == EDITMODE_COLLISION) { - Ref<ConcavePolygonShape2D> shape = memnew(ConcavePolygonShape2D); + if (current_shape.size() >= 3) { + Ref<ConvexPolygonShape2D> shape = memnew(ConvexPolygonShape2D); - PoolVector<Vector2> segments; - segments.resize(current_shape.size() * 2); - PoolVector<Vector2>::Write w = segments.write(); + Vector<Vector2> segments; - for (int i = 0; i < current_shape.size(); i++) { - w[(i << 1) + 0] = current_shape[i] - shape_anchor; - w[(i << 1) + 1] = current_shape[(i + 1) % current_shape.size()] - shape_anchor; - } + for (int i = 0; i < current_shape.size(); i++) { + segments.push_back(current_shape[i] - shape_anchor); + } - w = PoolVector<Vector2>::Write(); - shape->set_segments(segments); + shape->set_points(segments); + + tile_set->tile_add_shape(get_current_tile(), shape, Transform2D(), false, edited_shape_coord); + edited_collision_shape = shape; + } - tile_set->tile_add_shape(get_current_tile(), shape, Transform2D(), false, edited_shape_coord); - edited_collision_shape = shape; tools[TOOL_SELECT]->set_pressed(true); workspace->update(); } else if (edit_mode == EDITMODE_OCCLUSION) { @@ -1338,6 +1516,10 @@ Vector2 AutotileEditor::snap_point(const Vector2 &point) { anchor.x *= (tile_size.x + spacing); anchor.y *= (tile_size.y + spacing); Rect2 region(anchor, tile_size); + if (tools[SHAPE_GRID_SNAP]->is_pressed()) { + p.x = Math::snap_scalar_seperation(snap_offset.x, snap_step.x, p.x, snap_separation.x); + p.y = Math::snap_scalar_seperation(snap_offset.y, snap_step.y, p.y, snap_separation.y); + } if (tools[SHAPE_KEEP_INSIDE_TILE]->is_pressed()) { if (p.x < region.position.x) p.x = region.position.x; @@ -1348,23 +1530,6 @@ Vector2 AutotileEditor::snap_point(const Vector2 &point) { if (p.y > region.position.y + region.size.y) p.y = region.position.y + region.size.y; } - if (tools[SHAPE_SNAP_TO_BITMASK_GRID]->is_pressed()) { - Vector2 p2 = p; - if (tile_set->autotile_get_bitmask_mode(get_current_tile()) == TileSet::BITMASK_2X2) { - p2.x = Math::stepify(p2.x, tile_size.x / 2); - p2.y = Math::stepify(p2.y, tile_size.y / 2); - if ((p2 - p).length_squared() <= MAX(tile_size.y / 4, MIN_DISTANCE_SQUARED)) { - p = p2; - } - } else if (tile_set->autotile_get_bitmask_mode(get_current_tile()) == TileSet::BITMASK_3X3) { - p2.x = Math::stepify(p2.x, tile_size.x / 3); - p2.y = Math::stepify(p2.y, tile_size.y / 3); - if ((p2 - p).length_squared() <= MAX(tile_size.y / 6, MIN_DISTANCE_SQUARED)) { - p = p2; - } - } - } - p.floor(); return p; } diff --git a/editor/plugins/tile_set_editor_plugin.h b/editor/plugins/tile_set_editor_plugin.h index d60d0d5c3c..34284cb90f 100644 --- a/editor/plugins/tile_set_editor_plugin.h +++ b/editor/plugins/tile_set_editor_plugin.h @@ -33,7 +33,7 @@ #include "editor/editor_name_dialog.h" #include "editor/editor_node.h" #include "scene/2d/sprite.h" -#include "scene/resources/concave_polygon_shape_2d.h" +#include "scene/resources/convex_polygon_shape_2d.h" #include "scene/resources/tile_set.h" class AutotileEditorHelper; @@ -70,7 +70,7 @@ class AutotileEditor : public Control { SHAPE_CREATE_FROM_BITMASK, SHAPE_CREATE_FROM_NOT_BITMASK, SHAPE_KEEP_INSIDE_TILE, - SHAPE_SNAP_TO_BITMASK_GRID, + SHAPE_GRID_SNAP, ZOOM_OUT, ZOOM_1, ZOOM_IN, @@ -78,7 +78,7 @@ class AutotileEditor : public Control { }; Ref<TileSet> tile_set; - Ref<ConcavePolygonShape2D> edited_collision_shape; + Ref<ConvexPolygonShape2D> edited_collision_shape; Ref<OccluderPolygon2D> edited_occlusion_shape; Ref<NavigationPolygon> edited_navigation_shape; @@ -91,10 +91,21 @@ class AutotileEditor : public Control { Button *tool_editmode[EDITMODE_MAX]; HBoxContainer *tool_containers[TOOLBAR_MAX]; HBoxContainer *toolbar; + HBoxContainer *hb_grid; ToolButton *tools[TOOL_MAX]; SpinBox *spin_priority; + SpinBox *sb_step_y; + SpinBox *sb_step_x; + SpinBox *sb_off_y; + SpinBox *sb_off_x; + SpinBox *sb_sep_y; + SpinBox *sb_sep_x; EditMode edit_mode; + Vector2 snap_step; + Vector2 snap_offset; + Vector2 snap_separation; + bool creating_shape; int dragging_point; Vector2 edited_shape_coord; @@ -119,9 +130,16 @@ private: void _on_workspace_input(const Ref<InputEvent> &p_ie); void _on_tool_clicked(int p_tool); void _on_priority_changed(float val); + void _on_grid_snap_toggled(bool p_val); + void _set_snap_step_x(float p_val); + void _set_snap_step_y(float p_val); + void _set_snap_off_x(float p_val); + void _set_snap_off_y(float p_val); + void _set_snap_sep_x(float p_val); + void _set_snap_sep_y(float p_val); void draw_highlight_tile(Vector2 coord, const Vector<Vector2> &other_highlighted = Vector<Vector2>()); - void draw_grid(const Vector2 &size, int spacing); + void draw_grid_snap(); void draw_polygon_shapes(); void close_shape(const Vector2 &shape_anchor); Vector2 snap_point(const Vector2 &point); |