From 795a90154ae9fd36ecefe6a7dd31c621baad609f Mon Sep 17 00:00:00 2001 From: Bastiaan Olij Date: Fri, 16 Oct 2020 23:34:53 +1100 Subject: [PATCH] Work done during stream 16 and 17 --- .gitattributes | 6 ++ .gitignore | 5 + README.md | 10 ++ addons/gdpose/SkeletonPopup.gd | 32 ++++++ addons/gdpose/SkeletonPopup.tscn | 11 ++ addons/gdpose/bonegizmo.gd | 179 +++++++++++++++++++++++++++++++ addons/gdpose/bonegizmoplugin.gd | 22 ++++ addons/gdpose/plugin.cfg | 7 ++ addons/gdpose/plugin.gd | 39 +++++++ 9 files changed, 311 insertions(+) create mode 100644 .gitattributes create mode 100644 .gitignore create mode 100644 addons/gdpose/SkeletonPopup.gd create mode 100644 addons/gdpose/SkeletonPopup.tscn create mode 100644 addons/gdpose/bonegizmo.gd create mode 100644 addons/gdpose/bonegizmoplugin.gd create mode 100644 addons/gdpose/plugin.cfg create mode 100644 addons/gdpose/plugin.gd diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..94b635c --- /dev/null +++ b/.gitattributes @@ -0,0 +1,6 @@ +*.gd eol=lf +*.tscn eol=lf +*.cfg eol=lf +*.godot eol=lf +*.tres eol=lf +*.import eol=lf diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..464dbb6 --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +.import/ +*.dblite +*.TMP +logs +Thumbs.db diff --git a/README.md b/README.md index 890e93c..bdb309c 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,12 @@ # gdpose Plugin to make skeletons poseable in the editor + +Note: this is still a work in progress + +For more info see my streams on Youtube: +- [Stream #16](https://youtu.be/LtJCLWorenc) +- [Stream #17](https://youtu.be/zFVmMPe9Skc) + +# License +This project is provided under an MIT license. +See LICENSE for further details. diff --git a/addons/gdpose/SkeletonPopup.gd b/addons/gdpose/SkeletonPopup.gd new file mode 100644 index 0000000..0cb1ad9 --- /dev/null +++ b/addons/gdpose/SkeletonPopup.gd @@ -0,0 +1,32 @@ +tool +extends MenuButton + +var skeleton : Skeleton = null + +func edit(new_skeleton : Skeleton): + skeleton = new_skeleton + + # update our menu + var popup = get_popup() + if popup: + popup.clear() + + if skeleton: + for idx in range(0, skeleton.get_bone_count()): + var parent = skeleton.get_bone_parent(idx) + if parent != -1: + var name = skeleton.get_bone_name(idx) + popup.add_item(name, idx) + +func select_bone(id): + print("select bone " + str(id)) + + var gizmo : BoneSpatialGizmo = skeleton.gizmo + if gizmo: + gizmo.set_selected_bone(id) + skeleton.update_gizmo() + +func _ready(): + var popup = get_popup() + if popup: + popup.connect("id_pressed", self, "select_bone") diff --git a/addons/gdpose/SkeletonPopup.tscn b/addons/gdpose/SkeletonPopup.tscn new file mode 100644 index 0000000..39c8d37 --- /dev/null +++ b/addons/gdpose/SkeletonPopup.tscn @@ -0,0 +1,11 @@ +[gd_scene load_steps=2 format=2] + +[ext_resource path="res://addons/gdpose/SkeletonPopup.gd" type="Script" id=1] + +[node name="SkeletonPopup" type="MenuButton"] +text = "GDPose" +items = [ "Test1", null, 0, false, false, 0, 0, null, "", false, "Test2", null, 0, false, false, 1, 0, null, "", false ] +script = ExtResource( 1 ) +__meta__ = { +"_edit_use_anchors_": false +} diff --git a/addons/gdpose/bonegizmo.gd b/addons/gdpose/bonegizmo.gd new file mode 100644 index 0000000..a1e21bf --- /dev/null +++ b/addons/gdpose/bonegizmo.gd @@ -0,0 +1,179 @@ +tool +extends EditorSpatialGizmo + +class_name BoneSpatialGizmo + +var offset = 0.1 +var handle_dist = 0.1 # Distance of gizmo to bone origin +var selected_bone = -1 # Bone we are animating + +var is_posing = false +var start_pose = Transform() +var start_point = Vector2() + +var handle_idx = Array() # Handles we've added + +const handle_rot_x = -2 +const handle_rot_y = -3 +const handle_rot_z = -4 + +func set_selected_bone(idx): + var skeleton : Skeleton = get_spatial_node() + if !skeleton: + return + + if idx >= 0 and idx < skeleton.get_bone_count(): + selected_bone = idx + else: + selected_bone = -1 + +func get_handle_name(index): + var skeleton : Skeleton = get_spatial_node() + if !skeleton: + return "No skeleton" + + var idx = handle_idx[index] + if idx == handle_rot_x: + return "Rotate X" + elif idx == handle_rot_y: + return "Rotate Y" + elif idx == handle_rot_z: + return "Rotate Z" + else: + return "huh???" + +func get_handle_value(index): + var skeleton : Skeleton = get_spatial_node() + if !skeleton: + return "No skeleton" + + var idx = handle_idx[index] + if selected_bone >= 0: + return skeleton.get_bone_pose(selected_bone) + else: + return "No selected bone" + +func set_handle(index, camera, point): + var idx = handle_idx[index] + var skeleton : Skeleton = get_spatial_node() + if !skeleton: + return "No skeleton" + + if !is_posing: + start_point = point + start_pose = skeleton.get_bone_pose(selected_bone) + is_posing = true + print("start posing " + str(start_pose)) + return + + var moved = point - start_point + var distance = moved.x + moved.y + + # calculate our new transform (in local space) + var new_pose : Transform = start_pose + if idx == handle_rot_x: + # rotate around X + new_pose = new_pose.rotated(start_pose.basis.x.normalized(), distance * 0.01) + elif idx == handle_rot_y: + # rotate around Y + new_pose = new_pose.rotated(start_pose.basis.y.normalized(), distance * 0.01) + elif idx == handle_rot_z: + # rotate around Z + new_pose = new_pose.rotated(start_pose.basis.z.normalized(), distance * 0.01) + + skeleton.set_bone_pose(selected_bone, new_pose) + skeleton.update_gizmo() + +func commit_handle(index, restore, cancel = false): + # var idx = handle_idx[index] + + print("Commit") + is_posing = false + + var skeleton : Skeleton = get_spatial_node() + if !skeleton: + return + + if selected_bone == -1: + return + + if (cancel): + skeleton.set_bone_pose(selected_bone, restore) + +func redraw(): + clear() + + var skeleton : Skeleton = get_spatial_node() + if !skeleton: + return + + var lines_material = get_plugin().get_material("skeleton", self) + var selected_material = get_plugin().get_material("selected", self) + var handles_material = get_plugin().get_material("handles", self) + var handles = PoolVector3Array() + handle_idx.clear() + + # loop through our bones + for idx in range(0, skeleton.get_bone_count()): + var parent = skeleton.get_bone_parent(idx) + if parent != -1: + var lines = PoolVector3Array() + var parent_transform = skeleton.get_bone_global_pose(parent) + var bone_transform = skeleton.get_bone_global_pose(idx) + + var parent_pos = parent_transform.origin + var bone_pos = bone_transform.origin + var delta = bone_pos - parent_pos + var length = delta.length() + + var p1 = parent_pos + (delta * offset) + parent_transform.basis.x * length * offset + var p2 = parent_pos + (delta * offset) + parent_transform.basis.z * length * offset + var p3 = parent_pos + (delta * offset) - parent_transform.basis.x * length * offset + var p4 = parent_pos + (delta * offset) - parent_transform.basis.z * length * offset + + lines.push_back(parent_pos) + lines.push_back(p1) + lines.push_back(p1) + lines.push_back(bone_pos) + + lines.push_back(parent_pos) + lines.push_back(p2) + lines.push_back(p2) + lines.push_back(bone_pos) + + lines.push_back(parent_pos) + lines.push_back(p3) + lines.push_back(p3) + lines.push_back(bone_pos) + + lines.push_back(parent_pos) + lines.push_back(p4) + lines.push_back(p4) + lines.push_back(bone_pos) + + lines.push_back(p1) + lines.push_back(p2) + lines.push_back(p2) + lines.push_back(p3) + lines.push_back(p3) + lines.push_back(p4) + lines.push_back(p4) + lines.push_back(p1) + + if parent == selected_bone: + add_lines(lines, selected_material, false) + else: + add_lines(lines, lines_material, false) + + if idx == selected_bone: + handles.push_back(bone_pos + bone_transform.basis.x * handle_dist) + handle_idx.push_back(handle_rot_x) + + handles.push_back(bone_pos + bone_transform.basis.y * handle_dist) + handle_idx.push_back(handle_rot_y) + + handles.push_back(bone_pos + bone_transform.basis.z * handle_dist) + handle_idx.push_back(handle_rot_z) + + if handles.size() > 0: + add_handles(handles, handles_material) diff --git a/addons/gdpose/bonegizmoplugin.gd b/addons/gdpose/bonegizmoplugin.gd new file mode 100644 index 0000000..0d9e356 --- /dev/null +++ b/addons/gdpose/bonegizmoplugin.gd @@ -0,0 +1,22 @@ +tool +extends EditorSpatialGizmoPlugin + +const BoneGizmo = preload("res://addons/gdpose/bonegizmo.gd") + +func get_name(): + return "BoneGizmo" + +func get_priority(): + return 100 + +func create_gizmo(spatial): + if spatial is Skeleton: + return BoneGizmo.new() + else: + return null + +func _init(): + create_material("skeleton", Color(0.6, 0.6, 0.0)) + create_material("selected", Color(1.0, 1.0, 0.0)) + create_handle_material("handles") + diff --git a/addons/gdpose/plugin.cfg b/addons/gdpose/plugin.cfg new file mode 100644 index 0000000..91ac9df --- /dev/null +++ b/addons/gdpose/plugin.cfg @@ -0,0 +1,7 @@ +[plugin] + +name="gdpose" +description="Pose editor" +author="Bastiaan Olij" +version="1" +script="plugin.gd" diff --git a/addons/gdpose/plugin.gd b/addons/gdpose/plugin.gd new file mode 100644 index 0000000..cb1c370 --- /dev/null +++ b/addons/gdpose/plugin.gd @@ -0,0 +1,39 @@ +tool +extends EditorPlugin + +const BoneGizmoPlugin = preload("res://addons/gdpose/bonegizmoplugin.gd") +const SkeletonPopup = preload("res://addons/gdpose/SkeletonPopup.tscn") + +var skeleton_popup_instance +var bone_gizmo = BoneGizmoPlugin.new() + +func get_plugin_name(): + return "GD Pose Plugin" + +func _enter_tree(): + # Initialization of the plugin goes here. + add_spatial_gizmo_plugin(bone_gizmo) + + # add our menu + skeleton_popup_instance = SkeletonPopup.instance() + add_control_to_container(EditorPlugin.CONTAINER_SPATIAL_EDITOR_MENU, skeleton_popup_instance) + skeleton_popup_instance.visible = false + +func _exit_tree(): + remove_spatial_gizmo_plugin(bone_gizmo) + if skeleton_popup_instance: + remove_control_from_container(EditorPlugin.CONTAINER_SPATIAL_EDITOR_MENU, skeleton_popup_instance) + skeleton_popup_instance.queue_free() + skeleton_popup_instance = null + +func make_visible(visible): + if skeleton_popup_instance: + skeleton_popup_instance.visible = visible + +func handles(object): + return object is Skeleton + +func edit(object): + var skeleton : Skeleton = object + if skeleton_popup_instance: + skeleton_popup_instance.edit(skeleton)