├── icon.png ├── resources ├── red.tres ├── yellow.tres └── checkerboard.shader ├── default_env.tres ├── LICENSE ├── inverse_kinematics ├── inverse_kinematic_solver.tscn ├── leg_animator.tscn ├── demo2.tscn └── demo3.tscn └── project.godot /icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bzdghf/godot-spider/HEAD/icon.png -------------------------------------------------------------------------------- /resources/red.tres: -------------------------------------------------------------------------------- 1 | [gd_resource type="SpatialMaterial" format=2] 2 | 3 | [resource] 4 | albedo_color = Color( 0.341176, 0, 0, 1 ) 5 | -------------------------------------------------------------------------------- /resources/yellow.tres: -------------------------------------------------------------------------------- 1 | [gd_resource type="SpatialMaterial" format=2] 2 | 3 | [resource] 4 | albedo_color = Color( 1, 0.701961, 0, 1 ) 5 | -------------------------------------------------------------------------------- /default_env.tres: -------------------------------------------------------------------------------- 1 | [gd_resource type="Environment" load_steps=2 format=2] 2 | 3 | [sub_resource type="ProceduralSky" id=1] 4 | 5 | [resource] 6 | background_mode = 3 7 | background_sky = SubResource( 1 ) 8 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 bzdghf 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /inverse_kinematics/inverse_kinematic_solver.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=2 format=2] 2 | 3 | [sub_resource type="GDScript" id=1] 4 | script/source = "# helpful links: https://www.youtube.com/watch?v=e6Gjhr1IP6w https://www.youtube.com/watch?v=qqOAzn05fvk https://www.youtube.com/watch?v=EEP4Vgcnjxs 5 | tool 6 | extends Skeleton 7 | 8 | #export (NodePath) var root_joint_path setget _set_root_node 9 | export var iteration_count = 10 10 | export var target_position = Vector3.ZERO 11 | export var pole_position = Vector3.ZERO 12 | export var pole_rotation = Vector3.ZERO 13 | var joints = [] 14 | var joint_lengths = 0 15 | var positions = [] 16 | var max_length = 0 17 | 18 | func set_joints(joints): # there is ordering to joints. joints[0] is root 19 | self.joints = joints 20 | joint_lengths = [] 21 | max_length = 0 22 | for i in range(1, joints.size()): 23 | joint_lengths.append((joints[i].global_transform.origin - joints[i-1].global_transform.origin).length()) 24 | max_length += joint_lengths[joint_lengths.size()-1] 25 | positions = [] 26 | for joint in joints: 27 | positions.append(joint.global_transform.origin) 28 | 29 | func update_joint_transforms(): 30 | if !joints or joints.size() == 0: 31 | return 32 | for i in range (0, joints.size()): 33 | positions[i] = joints[i].global_transform.origin 34 | if joints[0].global_transform.origin.distance_squared_to(target_position) >= max_length*max_length: # full stretch: 35 | var direction = (target_position - positions[0]).normalized() 36 | for i in range(1, positions.size()): 37 | positions[i] = positions[i-1] + direction * joint_lengths[i-1] 38 | else: 39 | for i in range(iteration_count): 40 | # backward 41 | var epsilon = 0.01 42 | if positions[positions.size()-1].distance_squared_to(target_position) < epsilon*epsilon: 43 | break 44 | for i in range(positions.size()-1, 0, -1): 45 | if i == positions.size()-1: 46 | positions[i] = target_position 47 | else: 48 | positions[i] = positions[i+1] + (positions[i] - positions[i+1]).normalized() * joint_lengths[i] 49 | # forward 50 | for i in range(1, positions.size()): 51 | positions[i] = positions[i-1] + (positions[i] - positions[i-1]).normalized() * joint_lengths[i-1] 52 | for i in range (1, positions.size()-1): # we can only move the middle joints (towards pole) 53 | var normal = (positions[i+1]-positions[i-1]).normalized() 54 | var plane = Plane(normal, positions[i-1].dot(normal)) # put a plane on i-1 so we can check how much middle node must be rotated to reach pole projection on the plane 55 | var projected_pole = plane.project(pole_position) 56 | var projected_joint = plane.project(positions[i]) 57 | var angle = (projected_joint-positions[i-1]).angle_to(projected_pole-positions[i-1]) 58 | if (projected_joint-positions[i-1]).cross(projected_pole-positions[i-1]).dot(plane.normal) < 0: # need signed angle 59 | angle = -angle 60 | positions[i] = Quat(normal, angle).normalized() * (positions[i] - positions[i-1]) + positions[i-1] 61 | for i in range (0, joints.size()): 62 | joints[i].global_transform.origin = positions[i] 63 | var next_position = positions[i+1] if i < joints.size()-1 else target_position 64 | var up = pole_rotation - positions[i] # keep up directed at pole 65 | joints[i].look_at(next_position, up) 66 | " 67 | 68 | [node name="inverse_kinematic_solver" type="Skeleton"] 69 | transform = Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0 ) 70 | script = SubResource( 1 ) 71 | iteration_count = 1 72 | -------------------------------------------------------------------------------- /resources/checkerboard.shader: -------------------------------------------------------------------------------- 1 | //https://github.com/CptPotato/GodotThings/tree/master/ProceduralCheckerboard 2 | shader_type spatial; 3 | render_mode blend_mix, depth_draw_always, cull_back, diffuse_burley, specular_schlick_ggx; 4 | 5 | uniform vec4 albedo : hint_color = vec4(0.6f, 0.6f, 0.6f, 1.0f); 6 | uniform float squares = 1.0; 7 | uniform float edge_blur = 1.0; 8 | uniform float specular; 9 | uniform float metallic : hint_range(0, 1) = 0.0f; 10 | uniform float roughness : hint_range(0, 1) = 0.5f; 11 | uniform float point_size : hint_range(0, 128); 12 | 13 | varying vec3 Position; 14 | varying vec3 Normal; 15 | 16 | void vertex() 17 | { 18 | Position = VERTEX; 19 | Normal = NORMAL; 20 | } 21 | 22 | float SquareAntialiased(float coord, float dd) // antialiased square wave 23 | { 24 | coord += dd * 0.5f; 25 | 26 | dd = max(dd, 0.0001f); 27 | float invdd = 1.0f / dd; 28 | 29 | coord = mod(coord, 1.0f); 30 | float c = min(coord * invdd, 0.5f - (coord - 0.5f) / dd + 0.5f); 31 | c = clamp(c, 0.0f, 1.0f); 32 | 33 | return c; 34 | } 35 | 36 | float SXOR(float a, float b) // "smooth" xor, used to combine checker patterns 37 | { 38 | return abs(a - b); 39 | } 40 | 41 | vec4 SXOR4(vec4 a, vec4 b) 42 | { 43 | return abs(a - b); 44 | } 45 | 46 | float Checkers(vec3 coord, vec3 ddxyz, vec3 tripCoeff) 47 | { 48 | vec3 fade = clamp(1.0f / (ddxyz * 2.0f) - 1.0f, 0.0f, 1.0f); 49 | 50 | float cx = SquareAntialiased(coord.x, ddxyz.x); 51 | float cy = SquareAntialiased(coord.y, ddxyz.y); 52 | float cz = SquareAntialiased(coord.z, ddxyz.z); 53 | 54 | vec3 filter = clamp(1.0f / ddxyz * 0.5f - 0.5f, 0.0f, 1.0f); // smooth, manual filtering 55 | 56 | float c = SXOR(cy * filter.y, cz * filter.z) * tripCoeff.x; 57 | c += SXOR(cz * filter.z, cx * filter.x) * tripCoeff.y; 58 | c += SXOR(cx * filter.x, cy * filter.y) * tripCoeff.z; 59 | c += 1.0f - dot(filter, vec3(0.3333f)); 60 | 61 | return c; 62 | } 63 | 64 | vec4 texturePointSmooth(sampler2D smp, vec2 uv, vec2 tex_size, vec2 filter_width) 65 | { 66 | float fade = clamp(max(1.0f / filter_width.x, 1.0f / filter_width.y) - 1.0f, 0.0f, 1.0f); 67 | filter_width = max(filter_width, vec2(1.0f)); 68 | vec2 uv_pixels = uv * tex_size; 69 | 70 | vec2 uv_pixels_floor = round(uv_pixels) - vec2(0.5f); 71 | vec2 uv_dxy_pixels = uv_pixels - uv_pixels_floor; 72 | 73 | uv_dxy_pixels = clamp((uv_dxy_pixels - vec2(0.5f)) * filter_width + vec2(0.5f), 0.0f, 1.0f); 74 | 75 | uv = uv_pixels_floor / tex_size; 76 | 77 | return mix(textureLod(smp, uv + uv_dxy_pixels / tex_size, 0.0f), vec4(0.5f, 0.5f, 0.5f, 1.0f), fade); 78 | } 79 | 80 | vec4 TextureTriplanar(sampler2D smp, vec2 texSize, vec3 coord, vec3 ddxyz, vec3 tripCoeff) 81 | { 82 | vec4 cx = texturePointSmooth(smp, coord.yz, texSize, 1.0f / ddxyz.yz / texSize); 83 | vec4 cy = texturePointSmooth(smp, coord.zx, texSize, 1.0f / ddxyz.zx / texSize); 84 | vec4 cz = texturePointSmooth(smp, coord.xy, texSize, 1.0f / ddxyz.xy / texSize); 85 | 86 | vec3 filter = clamp(1.0f / ddxyz * 0.5f - 0.5f, 0.0f, 1.0f); // smooth, manual filtering 87 | 88 | vec4 c = cx * tripCoeff.x; 89 | c += cy * tripCoeff.y; 90 | c += cz * tripCoeff.z; 91 | 92 | return c; 93 | } 94 | 95 | void fragment() 96 | { 97 | ALBEDO = albedo.rgb; 98 | 99 | vec3 coord = Position.xyz * squares; 100 | vec3 ddxyz = fwidth(coord) * edge_blur; 101 | 102 | vec3 tripCoeff = max((Normal * Normal * 2.0f - 0.5f), 0.001f); 103 | tripCoeff /= (tripCoeff.x + tripCoeff.y + tripCoeff.z); 104 | float checkers = Checkers(coord, ddxyz, tripCoeff); 105 | 106 | coord = Position.xyz; 107 | ddxyz = fwidth(coord); 108 | float checkersL = 1.0f - Checkers(coord, ddxyz, tripCoeff); 109 | 110 | checkers = mix(checkers, checkersL, 0.7f); 111 | 112 | ALBEDO = mix(ALBEDO * 0.3f, ALBEDO, mix(checkers, 1.0f, metallic * roughness)); 113 | ROUGHNESS = mix(1.0f, 0.5f, checkers * (1.0f - roughness * 0.333f)) * roughness; 114 | 115 | METALLIC = metallic; 116 | SPECULAR = specular; 117 | } 118 | -------------------------------------------------------------------------------- /project.godot: -------------------------------------------------------------------------------- 1 | ; Engine configuration file. 2 | ; It's best edited using the editor UI and not directly, 3 | ; since the parameters that go here are not all obvious. 4 | ; 5 | ; Format: 6 | ; [section] ; section goes between [] 7 | ; param=value ; assign values to parameters 8 | 9 | config_version=4 10 | 11 | _global_script_classes=[ ] 12 | _global_script_class_icons={ 13 | 14 | } 15 | 16 | [application] 17 | 18 | config/name="spider" 19 | config/icon="res://icon.png" 20 | 21 | [autoload] 22 | 23 | debug="*res://debug.gd" 24 | 25 | [debug] 26 | 27 | gdscript/warnings/unused_variable=false 28 | gdscript/warnings/unused_argument=false 29 | gdscript/warnings/return_value_discarded=false 30 | 31 | [input] 32 | 33 | ui_accept={ 34 | "deadzone": 0.5, 35 | "events": [ ] 36 | } 37 | ui_select={ 38 | "deadzone": 0.5, 39 | "events": [ ] 40 | } 41 | ui_cancel={ 42 | "deadzone": 0.5, 43 | "events": [ ] 44 | } 45 | ui_focus_next={ 46 | "deadzone": 0.5, 47 | "events": [ ] 48 | } 49 | ui_focus_prev={ 50 | "deadzone": 0.5, 51 | "events": [ ] 52 | } 53 | ui_left={ 54 | "deadzone": 0.5, 55 | "events": [ Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":false,"meta":false,"command":false,"pressed":false,"scancode":65,"unicode":0,"echo":false,"script":null) 56 | ] 57 | } 58 | ui_right={ 59 | "deadzone": 0.5, 60 | "events": [ Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":false,"meta":false,"command":false,"pressed":false,"scancode":68,"unicode":0,"echo":false,"script":null) 61 | ] 62 | } 63 | ui_up={ 64 | "deadzone": 0.5, 65 | "events": [ Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":false,"meta":false,"command":false,"pressed":false,"scancode":87,"unicode":0,"echo":false,"script":null) 66 | ] 67 | } 68 | ui_down={ 69 | "deadzone": 0.5, 70 | "events": [ Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":false,"meta":false,"command":false,"pressed":false,"scancode":83,"unicode":0,"echo":false,"script":null) 71 | ] 72 | } 73 | ui_page_up={ 74 | "deadzone": 0.5, 75 | "events": [ ] 76 | } 77 | ui_page_down={ 78 | "deadzone": 0.5, 79 | "events": [ ] 80 | } 81 | ui_home={ 82 | "deadzone": 0.5, 83 | "events": [ ] 84 | } 85 | ui_end={ 86 | "deadzone": 0.5, 87 | "events": [ ] 88 | } 89 | next_demo={ 90 | "deadzone": 0.5, 91 | "events": [ Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":false,"meta":false,"command":false,"pressed":false,"scancode":16777218,"unicode":0,"echo":false,"script":null) 92 | ] 93 | } 94 | open_settings={ 95 | "deadzone": 0.5, 96 | "events": [ Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":false,"meta":false,"command":false,"pressed":false,"scancode":16777217,"unicode":0,"echo":false,"script":null) 97 | ] 98 | } 99 | toggle_debug={ 100 | "deadzone": 0.5, 101 | "events": [ Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":false,"meta":false,"command":false,"pressed":false,"scancode":96,"unicode":0,"echo":false,"script":null) 102 | ] 103 | } 104 | move_units={ 105 | "deadzone": 0.5, 106 | "events": [ Object(InputEventMouseButton,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":false,"meta":false,"command":false,"button_mask":0,"position":Vector2( 0, 0 ),"global_position":Vector2( 0, 0 ),"factor":1.0,"button_index":2,"pressed":false,"doubleclick":false,"script":null) 107 | ] 108 | } 109 | select_units={ 110 | "deadzone": 0.5, 111 | "events": [ Object(InputEventMouseButton,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":false,"meta":false,"command":false,"button_mask":0,"position":Vector2( 0, 0 ),"global_position":Vector2( 0, 0 ),"factor":1.0,"button_index":1,"pressed":false,"doubleclick":false,"script":null) 112 | ] 113 | } 114 | open_chat={ 115 | "deadzone": 0.5, 116 | "events": [ Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":false,"meta":false,"command":false,"pressed":false,"scancode":16777221,"unicode":0,"echo":false,"script":null) 117 | ] 118 | } 119 | 120 | [rendering] 121 | 122 | quality/filters/msaa=3 123 | environment/default_environment="res://default_env.tres" 124 | -------------------------------------------------------------------------------- /inverse_kinematics/leg_animator.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=4 format=2] 2 | 3 | [ext_resource path="res://inverse_kinematics/inverse_kinematic_solver.tscn" type="PackedScene" id=1] 4 | 5 | [sub_resource type="GDScript" id=1] 6 | script/source = "tool 7 | extends Spatial 8 | 9 | export (NodePath) var rest_position_path 10 | export (NodePath) var ray_cast_position_path 11 | export(NodePath) var pole_position_path 12 | export(NodePath) var pole_rotation_path 13 | export(NodePath) var root_joint_path setget _set_root_joint_path 14 | 15 | export var step_interval_ms = 400.0 16 | export var step_clock_offset_ms = 200.0 17 | export var step_duration_ms = 100.0 18 | export var step_height = 0.5 19 | export var step_prediction_ratio = 1.0 20 | export var step_min_distance = 0.3 21 | var step_min_distance_squared = step_min_distance*step_min_distance # use squared value to avoid root calculations 22 | var previous_step_time = 0 23 | var target_position = Vector3.ZERO 24 | var next_position = Vector3.ZERO 25 | var previous_position = Vector3.ZERO 26 | # debug 27 | onready var target_debug = _make_mesh(Color(0, 1, 0)) 28 | onready var next_position_debug = _make_mesh(Color(0, 0, 1)) 29 | onready var previous_position_debug = _make_mesh(Color(1, 0, 0)) 30 | 31 | func _make_mesh(color): 32 | var mesh_instance = MeshInstance.new() 33 | add_child(mesh_instance) 34 | mesh_instance.mesh = SphereMesh.new() 35 | mesh_instance.global_scale(Vector3(0.1, 0.1, 0.1)) 36 | var material = SpatialMaterial.new() 37 | material.albedo_color = color 38 | mesh_instance.material_override = material 39 | return mesh_instance 40 | 41 | func _set_root_joint_path(node_path): 42 | root_joint_path = node_path 43 | if node_path and previous_step_time != 0: 44 | var joints = _extract_joints(get_node(root_joint_path)) 45 | $inverse_kinematic_solver.set_joints(joints) 46 | 47 | func _ready(): 48 | var joints = _extract_joints(get_node(root_joint_path)) 49 | $inverse_kinematic_solver.set_joints(joints) 50 | 51 | func _extract_joints(root_joint_node): 52 | var current_node = root_joint_node 53 | var joints = [] 54 | while current_node != null: 55 | var prev_node = current_node 56 | current_node = null 57 | for child in prev_node.get_children(): 58 | if child is BoneAttachment: 59 | current_node = child 60 | joints.append(child) 61 | print('found ', joints.size(), ' joints') 62 | return joints 63 | 64 | var previous_tick = 0 65 | func _process(delta): 66 | var rest_position = get_node(rest_position_path).global_transform.origin 67 | var ray_cast_position = get_node(ray_cast_position_path).global_transform.origin 68 | var current_time = OS.get_ticks_msec() 69 | var tick = floor((current_time+step_clock_offset_ms) / step_interval_ms) # adding offset helps this instance to get its next tick earlier than instances without offset 70 | # update cast positions 71 | # if current_time - previous_step_time >= step_interval_ms: 72 | if tick - previous_tick > 0: 73 | previous_step_time = current_time 74 | previous_tick = tick 75 | var cast_start = ray_cast_position 76 | var cast_end = rest_position 77 | var last_step_delta = rest_position - previous_position # use delta to rest position instead of previous end position, otherwise predictions are too late 78 | cast_end += step_prediction_ratio * last_step_delta 79 | cast_end += Vector3.DOWN * 2 # ensure we cast to under the surface, this shouldnt be too large because it effects movement_prediction (triangle to cast_end) 80 | var cast_result = get_world().direct_space_state.intersect_ray(cast_start, cast_end) 81 | previous_position = next_position 82 | next_position = cast_result.position if cast_result else rest_position 83 | # interpolate target to new position 84 | if previous_position.distance_squared_to(next_position) > step_min_distance_squared: 85 | var transition_ratio = min(1, (current_time - previous_step_time) / step_duration_ms) 86 | target_position = lerp(previous_position, next_position, transition_ratio) 87 | target_position.y += step_height - abs(lerp(-step_height, step_height, transition_ratio)) 88 | # else: # small jittering feels unnatural, better not move at all 89 | # $target.global_transform.origin = $current_position.global_transform.origin 90 | # solve ik position to target 91 | $inverse_kinematic_solver.iteration_count = 1 92 | $inverse_kinematic_solver.target_position = target_position 93 | $inverse_kinematic_solver.pole_position = get_node(pole_position_path).global_transform.origin 94 | $inverse_kinematic_solver.pole_rotation = get_node(pole_rotation_path).global_transform.origin 95 | $inverse_kinematic_solver.update_joint_transforms() 96 | if Engine.is_editor_hint(): 97 | target_debug.global_transform.origin = target_position 98 | next_position_debug.global_transform.origin = next_position 99 | previous_position_debug.global_transform.origin = previous_position 100 | " 101 | 102 | [sub_resource type="GDScript" id=2] 103 | script/source = "# translated from unity ik: https://www.youtube.com/watch?v=qqOAzn05fvk 104 | tool 105 | extends Skeleton 106 | 107 | #export (NodePath) var root_joint_path setget _set_root_node 108 | export var iteration_count = 10 109 | export var target_position = Vector3.ZERO 110 | export var pole_position = Vector3.ZERO 111 | export var pole_rotation = Vector3.ZERO 112 | var joints = [] 113 | var joint_lengths = 0 114 | var positions = [] 115 | var max_length = 0 116 | 117 | func set_joints(joints): # there is ordering to joints. joints[0] is root 118 | self.joints = joints 119 | joint_lengths = [] 120 | max_length = 0 121 | for i in range(1, joints.size()): 122 | joint_lengths.append((joints[i].global_transform.origin - joints[i-1].global_transform.origin).length()) 123 | max_length += joint_lengths[joint_lengths.size()-1] 124 | positions = [] 125 | for joint in joints: 126 | positions.append(joint.global_transform.origin) 127 | 128 | func update_joint_transforms(): 129 | if !joints or joints.size() == 0: 130 | return 131 | for i in range (0, joints.size()): 132 | positions[i] = joints[i].global_transform.origin 133 | # full stretch: 134 | if joints[0].global_transform.origin.distance_squared_to(target_position) >= max_length*max_length: 135 | var direction = (target_position - positions[0]).normalized() 136 | for i in range(1, positions.size()): 137 | positions[i] = positions[i-1] + direction * joint_lengths[i-1] 138 | else: 139 | for i in range(iteration_count): 140 | # backward 141 | var epsilon = 0.01 142 | if positions[positions.size()-1].distance_squared_to(target_position) < epsilon*epsilon: 143 | break 144 | for i in range(positions.size()-1, 0, -1): 145 | if i == positions.size()-1: 146 | positions[i] = target_position 147 | else: 148 | positions[i] = positions[i+1] + (positions[i] - positions[i+1]).normalized() * joint_lengths[i] 149 | # forward 150 | for i in range(1, positions.size()): 151 | positions[i] = positions[i-1] + (positions[i] - positions[i-1]).normalized() * joint_lengths[i-1] 152 | for i in range (1, positions.size()-1): # we can only move the middle joints (towards pole) 153 | var normal = (positions[i+1]-positions[i-1]).normalized() 154 | var plane = Plane(normal, positions[i-1].dot(normal)) # put a plane on i-1 so we can check how much middle node must be rotated to reach pole projection on the plane 155 | var projected_pole = plane.project(pole_position) 156 | var projected_joint = plane.project(positions[i]) 157 | var angle = (projected_joint-positions[i-1]).angle_to(projected_pole-positions[i-1]) 158 | if (projected_joint-positions[i-1]).cross(projected_pole-positions[i-1]).dot(plane.normal) < 0: # need signed angle 159 | angle = -angle 160 | positions[i] = Quat(normal, angle).normalized() * (positions[i] - positions[i-1]) + positions[i-1] 161 | for i in range (0, joints.size()): 162 | joints[i].global_transform.origin = positions[i] 163 | var next_position = positions[i+1] if i < joints.size()-1 else target_position 164 | var up = pole_rotation - positions[i] # keep up directed at pole 165 | joints[i].look_at(next_position, up) 166 | 167 | #func quat_from_to_rotation(from, to): 168 | # var axis = from.cross(to).normalized() 169 | # var angle = from.angle_to(to) 170 | # var quat = Quat(axis, angle) 171 | # return quat 172 | " 173 | 174 | [node name="leg_animator" type="Spatial"] 175 | transform = Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, -0.22896 ) 176 | script = SubResource( 1 ) 177 | step_prediction_ratio = 0.5 178 | 179 | [node name="inverse_kinematic_solver" parent="." instance=ExtResource( 1 )] 180 | transform = Transform( -1.62921e-07, 0, 1, 0, 1, 0, -1, 0, -1.62921e-07, -0.443955, 1.27712, -1.66315 ) 181 | script = SubResource( 2 ) 182 | target_position = Vector3( -2.05167, 0.411238, -1.27402 ) 183 | pole_position = Vector3( -1.4306, 2.65326, -1.34367 ) 184 | pole_rotation = Vector3( -1.4306, 2.04545, -1.34367 ) 185 | -------------------------------------------------------------------------------- /inverse_kinematics/demo2.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=11 format=2] 2 | 3 | [ext_resource path="res://resources/checkerboard.shader" type="Shader" id=1] 4 | [ext_resource path="res://inverse_kinematics/inverse_kinematic_solver.tscn" type="PackedScene" id=2] 5 | 6 | [sub_resource type="GDScript" id=1] 7 | script/source = "tool 8 | extends Spatial 9 | 10 | export (NodePath) var rest_position_path 11 | export (NodePath) var ray_cast_position_path 12 | export(NodePath) var pole_position_path 13 | export(NodePath) var pole_rotation_path 14 | export(NodePath) var root_joint_path setget _set_root_joint_path 15 | 16 | export var step_interval_ms = 400.0 17 | export var step_duration_ms = 100.0 18 | export var step_height = 0.5 19 | export var step_prediction_ratio = 1.0 20 | export var step_min_distance = 0.3 21 | var step_min_distance_squared = step_min_distance*step_min_distance # use squared value to avoid root calculations 22 | var previous_step_time = 0 23 | var target_position = Vector3.ZERO 24 | var next_position = Vector3.ZERO 25 | var previous_position = Vector3.ZERO 26 | # debug 27 | onready var target_debug = _make_mesh(Color(0, 1, 0)) 28 | onready var next_position_debug = _make_mesh(Color(0, 0, 1)) 29 | onready var previous_position_debug = _make_mesh(Color(1, 0, 0)) 30 | 31 | func _make_mesh(color): 32 | var mesh_instance = MeshInstance.new() 33 | add_child(mesh_instance) 34 | mesh_instance.mesh = SphereMesh.new() 35 | mesh_instance.global_scale(Vector3(0.1, 0.1, 0.1)) 36 | var material = SpatialMaterial.new() 37 | material.albedo_color = color 38 | mesh_instance.material_override = material 39 | return mesh_instance 40 | 41 | func _set_root_joint_path(node_path): 42 | root_joint_path = node_path 43 | if node_path and previous_step_time != 0: 44 | var joints = _extract_joints(get_node(root_joint_path)) 45 | $inverse_kinematic_solver.set_joints(joints) 46 | 47 | func _ready(): 48 | var joints = _extract_joints(get_node(root_joint_path)) 49 | $inverse_kinematic_solver.set_joints(joints) 50 | 51 | func _extract_joints(root_joint_node): 52 | var current_node = root_joint_node 53 | var joints = [] 54 | while current_node != null: 55 | var prev_node = current_node 56 | current_node = null 57 | for child in prev_node.get_children(): 58 | if child is BoneAttachment: 59 | current_node = child 60 | joints.append(child) 61 | print('found ', joints.size(), ' joints') 62 | return joints 63 | 64 | func _process(delta): 65 | var rest_position = get_node(rest_position_path).global_transform.origin 66 | var ray_cast_position = get_node(ray_cast_position_path).global_transform.origin 67 | # if Engine.is_editor_hint(): _ready() # avoid restarting editor 68 | var current_time = OS.get_ticks_msec() 69 | # update cast positions 70 | if current_time - previous_step_time >= step_interval_ms: 71 | previous_step_time = current_time 72 | var cast_start = ray_cast_position 73 | var cast_end = rest_position 74 | var last_step_delta = rest_position - previous_position # use delta to rest position instead of previous end position, otherwise predictions are too late 75 | cast_end += step_prediction_ratio * last_step_delta 76 | cast_end += Vector3.DOWN * 2 # ensure we cast to under the surface, this shouldnt be too large because it effects movement_prediction (triangle to cast_end) 77 | var cast_result = get_world().direct_space_state.intersect_ray(cast_start, cast_end) 78 | previous_position = next_position 79 | next_position = cast_result.position if cast_result else rest_position 80 | # interpolate target to new position 81 | if previous_position.distance_squared_to(next_position) > step_min_distance_squared: 82 | var transition_ratio = min(1, (current_time - previous_step_time) / step_duration_ms) 83 | target_position = lerp(previous_position, next_position, transition_ratio) 84 | target_position.y += step_height - abs(lerp(-step_height, step_height, transition_ratio)) 85 | # else: # small jittering feels unnatural, better not move at all 86 | # $target.global_transform.origin = $current_position.global_transform.origin 87 | # solve ik position to target 88 | $inverse_kinematic_solver.iteration_count = 1 89 | $inverse_kinematic_solver.target_position = target_position 90 | $inverse_kinematic_solver.pole_position = get_node(pole_position_path).global_transform.origin 91 | $inverse_kinematic_solver.pole_rotation = get_node(pole_rotation_path).global_transform.origin 92 | $inverse_kinematic_solver.update_joint_transforms() 93 | if Engine.is_editor_hint(): 94 | target_debug.global_transform.origin = target_position 95 | next_position_debug.global_transform.origin = next_position 96 | previous_position_debug.global_transform.origin = previous_position 97 | " 98 | 99 | [sub_resource type="CubeMesh" id=2] 100 | size = Vector3( 0.2, 0.2, 1 ) 101 | 102 | [sub_resource type="Environment" id=3] 103 | background_mode = 1 104 | ambient_light_color = Color( 0.443137, 0.443137, 0.443137, 1 ) 105 | 106 | [sub_resource type="PlaneMesh" id=4] 107 | size = Vector2( 20, 20 ) 108 | 109 | [sub_resource type="ShaderMaterial" id=5] 110 | shader = ExtResource( 1 ) 111 | shader_param/albedo = Color( 0.180392, 0.180392, 0.180392, 1 ) 112 | shader_param/squares = 1.0 113 | shader_param/edge_blur = 1.0 114 | shader_param/specular = null 115 | shader_param/metallic = 0.0 116 | shader_param/roughness = 1.0 117 | shader_param/point_size = null 118 | 119 | [sub_resource type="ConcavePolygonShape" id=6] 120 | data = PoolVector3Array( 10, 0, 10, -10, 0, 10, 10, 0, -10, -10, 0, 10, -10, 0, -10, 10, 0, -10 ) 121 | 122 | [sub_resource type="SphereMesh" id=7] 123 | radius = 0.5 124 | height = 1.0 125 | 126 | [sub_resource type="SphereShape" id=8] 127 | radius = 0.5 128 | 129 | [node name="demo" type="Node"] 130 | 131 | [node name="creature_controller" type="Spatial" parent="."] 132 | transform = Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 0.287161, 0, 0.578431 ) 133 | 134 | [node name="leg_animator" type="Spatial" parent="creature_controller"] 135 | transform = Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, -0.22896 ) 136 | script = SubResource( 1 ) 137 | rest_position_path = NodePath("../rest_position") 138 | ray_cast_position_path = NodePath("../ray_cast_position") 139 | pole_position_path = NodePath("../pole_position") 140 | pole_rotation_path = NodePath("../pole_rotation") 141 | root_joint_path = NodePath("../joint") 142 | step_interval_ms = 400.0 143 | step_duration_ms = 100.0 144 | step_height = 0.5 145 | step_prediction_ratio = 0.5 146 | step_min_distance = 0.3 147 | 148 | [node name="inverse_kinematic_solver" parent="creature_controller/leg_animator" instance=ExtResource( 2 )] 149 | transform = Transform( -1.62921e-07, 0, 1, 0, 1, 0, -1, 0, -1.62921e-07, -0.443955, 1.27712, -1.66315 ) 150 | target_position = Vector3( -1.94711, 0, -0.995644 ) 151 | pole_position = Vector3( -1.14344, 2.65326, -1.01799 ) 152 | pole_rotation = Vector3( -1.14344, 2.04545, -1.01799 ) 153 | 154 | [node name="pole_position" type="Position3D" parent="creature_controller"] 155 | transform = Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, -1.4306, 2.65326, -1.59642 ) 156 | 157 | [node name="pole_rotation" type="Position3D" parent="creature_controller"] 158 | transform = Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, -1.4306, 2.04545, -1.59642 ) 159 | 160 | [node name="rest_position" type="Position3D" parent="creature_controller"] 161 | transform = Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, -2.57291, 0.0623209, -1.5626 ) 162 | 163 | [node name="ray_cast_position" type="Position3D" parent="creature_controller"] 164 | transform = Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, -1.8317, 1.59827, -1.58852 ) 165 | 166 | [node name="joint" type="BoneAttachment" parent="creature_controller"] 167 | transform = Transform( 0.00739333, 0.0722536, 0.997167, 0.0507115, 0.995911, -0.0725349, -0.997621, 0.0511601, 0.00370284, -0.145906, 1.42988, -1.61819 ) 168 | 169 | [node name="MeshInstance" type="MeshInstance" parent="creature_controller/joint"] 170 | transform = Transform( 1, -6.42426e-06, 2.85953e-05, 6.42613e-06, 1, 7.10599e-07, -2.85804e-05, -6.9011e-07, 1, 3.24249e-05, -1.78814e-06, -0.499994 ) 171 | mesh = SubResource( 2 ) 172 | material/0 = null 173 | 174 | [node name="joint" type="BoneAttachment" parent="creature_controller/joint"] 175 | transform = Transform( 0.994689, -0.0135087, 0.0807247, -0.0357256, 0.81218, 0.579075, -0.0734371, -0.578081, 0.81169, -0.000110149, 1.46627e-05, -1.07391 ) 176 | 177 | [node name="MeshInstance" type="MeshInstance" parent="creature_controller/joint/joint"] 178 | transform = Transform( 1, 0, 7.45058e-09, 7.45058e-09, 1, 2.79397e-09, 7.45058e-09, 2.51457e-08, 1, 4.76837e-07, -2.38419e-07, -0.5 ) 179 | mesh = SubResource( 2 ) 180 | material/0 = null 181 | 182 | [node name="joint" type="BoneAttachment" parent="creature_controller/joint/joint"] 183 | transform = Transform( -0.999432, -0.0217039, 0.00849484, 0.0207045, -0.657099, 0.751701, -0.0107593, 0.751594, 0.657438, -0.000141978, 2.99215e-05, -0.997697 ) 184 | 185 | [node name="MeshInstance2" type="MeshInstance" parent="creature_controller/joint/joint/joint"] 186 | transform = Transform( 1, -0.000118151, -7.96393e-05, 0.000118166, 1, -3.14424e-05, 7.96542e-05, 3.15653e-05, 1, -4.1008e-05, -1.5676e-05, -0.5 ) 187 | mesh = SubResource( 2 ) 188 | material/0 = null 189 | 190 | [node name="joint" type="BoneAttachment" parent="creature_controller/joint/joint/joint"] 191 | transform = Transform( 0.998864, -0.0103726, 0.00215022, 0.0103668, 0.998958, 0.00225561, -0.00217044, -0.00223021, 1.00033, 2.69413e-05, 0.0033232, -1.00367 ) 192 | 193 | [node name="environment" type="WorldEnvironment" parent="."] 194 | environment = SubResource( 3 ) 195 | 196 | [node name="light" type="DirectionalLight" parent="environment"] 197 | transform = Transform( -0.119471, 0.629567, -0.767705, 0.128496, 0.776545, 0.61682, 0.984487, -0.024955, -0.173672, -7.88634, 4.27265, 0 ) 198 | 199 | [node name="camera" type="Camera" parent="."] 200 | transform = Transform( -1.62921e-07, 0.34202, -0.939693, 0, 0.939693, 0.34202, 1, 5.57222e-08, -1.53095e-07, -6, 2, 0 ) 201 | 202 | [node name="StaticBody" type="StaticBody" parent="."] 203 | 204 | [node name="floor" type="MeshInstance" parent="StaticBody"] 205 | mesh = SubResource( 4 ) 206 | material/0 = SubResource( 5 ) 207 | 208 | [node name="CollisionShape" type="CollisionShape" parent="StaticBody"] 209 | shape = SubResource( 6 ) 210 | 211 | [node name="StaticBody2" type="StaticBody" parent="."] 212 | transform = Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, -2.01306, 0, -1.55644 ) 213 | 214 | [node name="MeshInstance" type="MeshInstance" parent="StaticBody2"] 215 | mesh = SubResource( 7 ) 216 | material/0 = null 217 | 218 | [node name="CollisionShape" type="CollisionShape" parent="StaticBody2"] 219 | shape = SubResource( 8 ) 220 | -------------------------------------------------------------------------------- /inverse_kinematics/demo3.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=13 format=2] 2 | 3 | [ext_resource path="res://resources/checkerboard.shader" type="Shader" id=1] 4 | [ext_resource path="res://inverse_kinematics/leg_animator.tscn" type="PackedScene" id=2] 5 | [ext_resource path="res://resources/red.tres" type="Material" id=3] 6 | [ext_resource path="res://resources/yellow.tres" type="Material" id=4] 7 | 8 | [sub_resource type="CubeMesh" id=1] 9 | size = Vector3( 0.2, 0.2, 1 ) 10 | 11 | [sub_resource type="CubeMesh" id=2] 12 | 13 | [sub_resource type="SphereMesh" id=3] 14 | 15 | [sub_resource type="SpatialMaterial" id=4] 16 | albedo_color = Color( 0, 0, 0, 1 ) 17 | 18 | [sub_resource type="Environment" id=5] 19 | background_mode = 1 20 | ambient_light_color = Color( 0.443137, 0.443137, 0.443137, 1 ) 21 | 22 | [sub_resource type="PlaneMesh" id=6] 23 | size = Vector2( 20, 20 ) 24 | 25 | [sub_resource type="ShaderMaterial" id=7] 26 | shader = ExtResource( 1 ) 27 | shader_param/albedo = Color( 0.180392, 0.180392, 0.180392, 1 ) 28 | shader_param/squares = 1.0 29 | shader_param/edge_blur = 1.0 30 | shader_param/specular = null 31 | shader_param/metallic = 0.0 32 | shader_param/roughness = 1.0 33 | shader_param/point_size = null 34 | 35 | [sub_resource type="ConcavePolygonShape" id=8] 36 | data = PoolVector3Array( 10, 0, 10, -10, 0, 10, 10, 0, -10, -10, 0, 10, -10, 0, -10, 10, 0, -10 ) 37 | 38 | [node name="demo" type="Node"] 39 | 40 | [node name="spider_controller" type="Spatial" parent="."] 41 | transform = Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, -4.67414, -0.382, 2.71914 ) 42 | 43 | [node name="leg_animator1" parent="spider_controller" instance=ExtResource( 2 )] 44 | transform = Transform( 1, -2.91038e-10, 0, 1.16415e-10, 1, 0, -1.86265e-09, -1.86265e-09, 1, 0, 0.44924, 0.4 ) 45 | rest_position_path = NodePath("../leg_animator1/rest_position") 46 | ray_cast_position_path = NodePath("../leg_animator1/ray_cast_position") 47 | pole_position_path = NodePath("../leg_animator1/pole_position") 48 | pole_rotation_path = NodePath("../leg_animator1/pole_rotation") 49 | root_joint_path = NodePath("../leg_animator1/joint") 50 | step_interval_ms = 500.0 51 | step_clock_offset_ms = 0.0 52 | step_duration_ms = 200.0 53 | step_height = 1.0 54 | step_min_distance = 0.3 55 | 56 | [node name="pole_position" type="Position3D" parent="spider_controller/leg_animator1"] 57 | transform = Transform( 1, 3.72529e-09, -1.86265e-09, -3.72529e-09, 1, 0, 0, -1.74623e-10, 1, -2.5, 3, 7.15256e-07 ) 58 | 59 | [node name="pole_rotation" type="Position3D" parent="spider_controller/leg_animator1"] 60 | transform = Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, -3.5, 3.5, 2.38419e-07 ) 61 | 62 | [node name="rest_position" type="Position3D" parent="spider_controller/leg_animator1"] 63 | transform = Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, -2.192, 0, 0 ) 64 | 65 | [node name="ray_cast_position" type="Position3D" parent="spider_controller/leg_animator1"] 66 | transform = Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, -2.15659, 0.75843, 0 ) 67 | 68 | [node name="joint" type="BoneAttachment" parent="spider_controller/leg_animator1"] 69 | transform = Transform( 0.00739333, 0.0722536, 0.997167, 0.0507115, 0.995911, -0.0725349, -0.997621, 0.0511601, 0.00370284, 0, 0.5, 0 ) 70 | 71 | [node name="MeshInstance" type="MeshInstance" parent="spider_controller/leg_animator1/joint"] 72 | transform = Transform( 1, -6.42426e-06, 2.85953e-05, 6.42613e-06, 1, 7.10599e-07, -2.85804e-05, -6.9011e-07, 1, 3.24249e-05, -1.78814e-06, -0.499994 ) 73 | mesh = SubResource( 1 ) 74 | material/0 = ExtResource( 4 ) 75 | 76 | [node name="joint" type="BoneAttachment" parent="spider_controller/leg_animator1/joint"] 77 | transform = Transform( 0.964653, 0.190724, 0.0742352, -0.161867, 0.936184, -0.282676, -0.1233, 0.263141, 0.951881, 0.0042634, -0.00080198, -1.07128 ) 78 | 79 | [node name="MeshInstance" type="MeshInstance" parent="spider_controller/leg_animator1/joint/joint"] 80 | transform = Transform( 1, -7.45058e-09, -5.96046e-08, 0, 1, -1.86265e-09, 0, 2.8871e-08, 1, 0, -1.78814e-07, -0.5 ) 81 | mesh = SubResource( 1 ) 82 | material/0 = ExtResource( 4 ) 83 | 84 | [node name="joint" type="BoneAttachment" parent="spider_controller/leg_animator1/joint/joint"] 85 | transform = Transform( 0.997527, 0.0501546, -0.0422989, 0.0505622, -0.175583, 0.981821, 0.0419107, -0.981911, -0.177728, -0.000126123, -4.12464e-05, -1.00237 ) 86 | 87 | [node name="MeshInstance2" type="MeshInstance" parent="spider_controller/leg_animator1/joint/joint/joint"] 88 | transform = Transform( 1, -0.000118151, -7.96393e-05, 0.000118166, 1, -3.14424e-05, 7.96542e-05, 3.15653e-05, 1, -4.1008e-05, -1.5676e-05, -0.5 ) 89 | mesh = SubResource( 1 ) 90 | material/0 = ExtResource( 4 ) 91 | 92 | [node name="joint" type="BoneAttachment" parent="spider_controller/leg_animator1/joint/joint/joint"] 93 | transform = Transform( 0.997549, -0.0108013, 0.00635374, 0.0107919, 0.998921, -0.000787615, -0.00633078, 0.000855543, 0.999825, 0.000128031, 0.00172472, -1.01107 ) 94 | 95 | [node name="leg_animator2" parent="spider_controller" instance=ExtResource( 2 )] 96 | transform = Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.44924, -0.449437 ) 97 | rest_position_path = NodePath("../../spider_controller/leg_animator2/rest_position") 98 | ray_cast_position_path = NodePath("../../spider_controller/leg_animator2/ray_cast_position") 99 | pole_position_path = NodePath("../../spider_controller/leg_animator2/pole_position") 100 | pole_rotation_path = NodePath("../../spider_controller/leg_animator2/pole_rotation") 101 | root_joint_path = NodePath("../../spider_controller/leg_animator2/joint") 102 | step_interval_ms = 500.0 103 | step_clock_offset_ms = 250.0 104 | step_duration_ms = 200.0 105 | step_height = 1.0 106 | step_min_distance = 0.3 107 | 108 | [node name="pole_position" type="Position3D" parent="spider_controller/leg_animator2"] 109 | transform = Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, -2.5, 3, 9.53674e-07 ) 110 | 111 | [node name="pole_rotation" type="Position3D" parent="spider_controller/leg_animator2"] 112 | transform = Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, -3.5, 3.5, 2.38419e-07 ) 113 | 114 | [node name="rest_position" type="Position3D" parent="spider_controller/leg_animator2"] 115 | transform = Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, -2.192, 0, 0 ) 116 | 117 | [node name="ray_cast_position" type="Position3D" parent="spider_controller/leg_animator2"] 118 | transform = Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, -2.15659, 0.75843, 0 ) 119 | 120 | [node name="joint" type="BoneAttachment" parent="spider_controller/leg_animator2"] 121 | transform = Transform( 0.00739333, 0.0722536, 0.997167, 0.0507115, 0.995911, -0.0725349, -0.997621, 0.0511601, 0.00370284, 0, 0.5, 0 ) 122 | 123 | [node name="MeshInstance" type="MeshInstance" parent="spider_controller/leg_animator2/joint"] 124 | transform = Transform( 1, -6.4224e-06, 2.85935e-05, 6.4224e-06, 1, 6.98958e-07, -2.8586e-05, -6.92206e-07, 1, 3.29018e-05, -1.90735e-06, -0.499994 ) 125 | mesh = SubResource( 1 ) 126 | material/0 = ExtResource( 4 ) 127 | 128 | [node name="joint" type="BoneAttachment" parent="spider_controller/leg_animator2/joint"] 129 | transform = Transform( 0.969327, 0.172279, 0.0623062, -0.147547, 0.939042, -0.28156, -0.106909, 0.266031, 0.953507, 0.000630617, -0.000973761, -1.07139 ) 130 | 131 | [node name="MeshInstance" type="MeshInstance" parent="spider_controller/leg_animator2/joint/joint"] 132 | transform = Transform( 1, -7.45058e-09, -5.96046e-08, 0, 1, -1.86265e-09, 0, 2.8871e-08, 1, 0, -1.78814e-07, -0.5 ) 133 | mesh = SubResource( 1 ) 134 | material/0 = ExtResource( 4 ) 135 | 136 | [node name="joint" type="BoneAttachment" parent="spider_controller/leg_animator2/joint/joint"] 137 | transform = Transform( 0.998088, 0.0431756, -0.0362722, 0.0434807, -0.178478, 0.981599, 0.0359841, -0.981898, -0.180019, -0.000106096, -3.51667e-05, -1.00181 ) 138 | 139 | [node name="MeshInstance2" type="MeshInstance" parent="spider_controller/leg_animator2/joint/joint/joint"] 140 | transform = Transform( 1, -0.000118151, -7.96393e-05, 0.000118166, 1, -3.14424e-05, 7.96542e-05, 3.15653e-05, 1, -4.1008e-05, -1.5676e-05, -0.5 ) 141 | mesh = SubResource( 1 ) 142 | material/0 = ExtResource( 4 ) 143 | 144 | [node name="joint" type="BoneAttachment" parent="spider_controller/leg_animator2/joint/joint/joint"] 145 | transform = Transform( 0.997601, -0.0251536, 0.0146916, 0.0251126, 0.998785, 0.0011294, -0.0146888, -0.000758692, 0.999721, 6.4373e-05, 0.00185156, -1.01098 ) 146 | 147 | [node name="leg_animator3" parent="spider_controller" instance=ExtResource( 2 )] 148 | transform = Transform( -1, 0, -8.74228e-08, 0, 1, 0, 8.74228e-08, 0, -1, -2.98023e-08, 0.44924, -0.449437 ) 149 | rest_position_path = NodePath("../leg_animator3/rest_position") 150 | ray_cast_position_path = NodePath("../leg_animator3/ray_cast_position") 151 | pole_position_path = NodePath("../leg_animator3/pole_position") 152 | pole_rotation_path = NodePath("../leg_animator3/pole_rotation") 153 | root_joint_path = NodePath("../leg_animator3/joint") 154 | step_interval_ms = 500.0 155 | step_clock_offset_ms = 0.0 156 | step_duration_ms = 200.0 157 | step_height = 1.0 158 | step_min_distance = 0.3 159 | 160 | [node name="pole_position" type="Position3D" parent="spider_controller/leg_animator3"] 161 | transform = Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, -2.5, 3, 9.53674e-07 ) 162 | 163 | [node name="pole_rotation" type="Position3D" parent="spider_controller/leg_animator3"] 164 | transform = Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, -3.5, 3.5, 2.38419e-07 ) 165 | 166 | [node name="rest_position" type="Position3D" parent="spider_controller/leg_animator3"] 167 | transform = Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, -2.192, 0, 0 ) 168 | 169 | [node name="ray_cast_position" type="Position3D" parent="spider_controller/leg_animator3"] 170 | transform = Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, -2.15659, 0.75843, 0 ) 171 | 172 | [node name="joint" type="BoneAttachment" parent="spider_controller/leg_animator3"] 173 | transform = Transform( 0.00739333, 0.0722536, 0.997167, 0.0507115, 0.995911, -0.0725349, -0.997621, 0.0511601, 0.00370284, 0, 0.5, 0 ) 174 | 175 | [node name="MeshInstance" type="MeshInstance" parent="spider_controller/leg_animator3/joint"] 176 | transform = Transform( 1, -6.42426e-06, 2.85953e-05, 6.42613e-06, 1, 7.10599e-07, -2.85804e-05, -6.9011e-07, 1, 3.24249e-05, -1.78814e-06, -0.499994 ) 177 | mesh = SubResource( 1 ) 178 | material/0 = ExtResource( 4 ) 179 | 180 | [node name="joint" type="BoneAttachment" parent="spider_controller/leg_animator3/joint"] 181 | transform = Transform( 0.978771, -0.0810872, -0.0864266, 0.055086, 0.953728, -0.266788, 0.103757, 0.258572, 0.956926, 0.000721693, -0.00120556, -1.07127 ) 182 | 183 | [node name="MeshInstance" type="MeshInstance" parent="spider_controller/leg_animator3/joint/joint"] 184 | transform = Transform( 1, -7.45058e-09, -5.96046e-08, 0, 1, -1.86265e-09, 0, 2.8871e-08, 1, 0, -1.78814e-07, -0.5 ) 185 | mesh = SubResource( 1 ) 186 | material/0 = ExtResource( 4 ) 187 | 188 | [node name="joint" type="BoneAttachment" parent="spider_controller/leg_animator3/joint/joint"] 189 | transform = Transform( 0.998181, -0.0437732, 0.0365498, -0.0436019, -0.171372, 0.982925, -0.0368475, -0.983009, -0.172985, 0.000151157, -2.81334e-05, -1.00115 ) 190 | 191 | [node name="MeshInstance2" type="MeshInstance" parent="spider_controller/leg_animator3/joint/joint/joint"] 192 | transform = Transform( 1, -0.000118151, -7.96393e-05, 0.000118166, 1, -3.14424e-05, 7.96542e-05, 3.15653e-05, 1, -4.1008e-05, -1.5676e-05, -0.5 ) 193 | mesh = SubResource( 1 ) 194 | material/0 = ExtResource( 4 ) 195 | 196 | [node name="joint" type="BoneAttachment" parent="spider_controller/leg_animator3/joint/joint/joint"] 197 | transform = Transform( 0.993779, 0.0119062, -0.00672279, -0.0117552, 0.99879, 0.0134223, 0.00684078, -0.01333, 0.999703, -0.000299931, 0.00198889, -1.01054 ) 198 | 199 | [node name="leg_animator4" parent="spider_controller" instance=ExtResource( 2 )] 200 | transform = Transform( -1, 0, -8.74228e-08, 0, 1, 0, 8.74228e-08, 0, -1, 2.98023e-08, 0.44924, 0.4 ) 201 | rest_position_path = NodePath("../../spider_controller/leg_animator4/rest_position") 202 | ray_cast_position_path = NodePath("../../spider_controller/leg_animator4/ray_cast_position") 203 | pole_position_path = NodePath("../../spider_controller/leg_animator4/pole_position") 204 | pole_rotation_path = NodePath("../../spider_controller/leg_animator4/pole_rotation") 205 | root_joint_path = NodePath("../../spider_controller/leg_animator4/joint") 206 | step_interval_ms = 500.0 207 | step_clock_offset_ms = 250.0 208 | step_duration_ms = 200.0 209 | step_height = 1.0 210 | step_min_distance = 0.3 211 | 212 | [node name="pole_position" type="Position3D" parent="spider_controller/leg_animator4"] 213 | transform = Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, -2.5, 3, 9.53674e-07 ) 214 | 215 | [node name="pole_rotation" type="Position3D" parent="spider_controller/leg_animator4"] 216 | transform = Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, -3.5, 3.5, 2.38419e-07 ) 217 | 218 | [node name="rest_position" type="Position3D" parent="spider_controller/leg_animator4"] 219 | transform = Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, -2.192, 0, 0 ) 220 | 221 | [node name="ray_cast_position" type="Position3D" parent="spider_controller/leg_animator4"] 222 | transform = Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, -2.15659, 0.75843, 0 ) 223 | 224 | [node name="joint" type="BoneAttachment" parent="spider_controller/leg_animator4"] 225 | transform = Transform( 0.00739333, 0.0722536, 0.997167, 0.0507115, 0.995911, -0.0725349, -0.997621, 0.0511601, 0.00370284, 0, 0.5, 0 ) 226 | 227 | [node name="MeshInstance" type="MeshInstance" parent="spider_controller/leg_animator4/joint"] 228 | transform = Transform( 1, -6.42426e-06, 2.85953e-05, 6.42613e-06, 1, 7.10599e-07, -2.85804e-05, -6.9011e-07, 1, 3.24249e-05, -1.78814e-06, -0.499994 ) 229 | mesh = SubResource( 1 ) 230 | material/0 = ExtResource( 4 ) 231 | 232 | [node name="joint" type="BoneAttachment" parent="spider_controller/leg_animator4/joint"] 233 | transform = Transform( 0.981627, -0.0618007, -0.0723024, 0.0395184, 0.952128, -0.275028, 0.0855895, 0.269177, 0.956222, 0.0062027, -0.00163591, -1.06755 ) 234 | 235 | [node name="MeshInstance" type="MeshInstance" parent="spider_controller/leg_animator4/joint/joint"] 236 | transform = Transform( 1, -7.45058e-09, -5.96046e-08, 0, 1, -1.86265e-09, 0, 2.8871e-08, 1, 0, -1.78814e-07, -0.5 ) 237 | mesh = SubResource( 1 ) 238 | material/0 = ExtResource( 4 ) 239 | 240 | [node name="joint" type="BoneAttachment" parent="spider_controller/leg_animator4/joint/joint"] 241 | transform = Transform( 0.998555, -0.0356696, 0.0301011, -0.0360482, -0.178545, 0.981857, -0.0297143, -0.982082, -0.179598, 0.000127077, -2.37226e-05, -1.00182 ) 242 | 243 | [node name="MeshInstance2" type="MeshInstance" parent="spider_controller/leg_animator4/joint/joint/joint"] 244 | transform = Transform( 1, -0.000118151, -7.96393e-05, 0.000118166, 1, -3.14424e-05, 7.96542e-05, 3.15653e-05, 1, -4.1008e-05, -1.5676e-05, -0.5 ) 245 | mesh = SubResource( 1 ) 246 | material/0 = ExtResource( 4 ) 247 | 248 | [node name="joint" type="BoneAttachment" parent="spider_controller/leg_animator4/joint/joint/joint"] 249 | transform = Transform( 0.997618, 0.0251758, -0.0147673, -0.025128, 0.999226, 0.00086228, 0.0147579, -0.00048997, 0.999722, -0.000223637, 0.0022471, -1.01065 ) 250 | 251 | [node name="body" type="MeshInstance" parent="spider_controller"] 252 | transform = Transform( 0.8, 0, 0, -4.65661e-10, 0.4, 0, -1.86265e-09, 0, 0.8, 0, 1, 0.050025 ) 253 | mesh = SubResource( 2 ) 254 | material/0 = ExtResource( 3 ) 255 | 256 | [node name="eye1" type="MeshInstance" parent="spider_controller"] 257 | transform = Transform( 0.25, 0, 0, 0, 0.25, 0, 0, 0, 0.25, -0.26625, 1, -0.618 ) 258 | mesh = SubResource( 3 ) 259 | material/0 = SubResource( 4 ) 260 | 261 | [node name="eye2" type="MeshInstance" parent="spider_controller"] 262 | transform = Transform( 0.25, 0, 0, 0, 0.25, 0, 0, 0, 0.25, 0.294625, 1, -0.618 ) 263 | mesh = SubResource( 3 ) 264 | material/0 = SubResource( 4 ) 265 | 266 | [node name="environment" type="WorldEnvironment" parent="."] 267 | environment = SubResource( 5 ) 268 | 269 | [node name="light" type="DirectionalLight" parent="environment"] 270 | transform = Transform( -0.119471, 0.629567, -0.767705, 0.128496, 0.776545, 0.61682, 0.984487, -0.024955, -0.173672, -7.88634, 4.27265, 0 ) 271 | 272 | [node name="StaticBody" type="StaticBody" parent="environment"] 273 | 274 | [node name="floor" type="MeshInstance" parent="environment/StaticBody"] 275 | mesh = SubResource( 6 ) 276 | material/0 = SubResource( 7 ) 277 | 278 | [node name="CollisionShape" type="CollisionShape" parent="environment/StaticBody"] 279 | shape = SubResource( 8 ) 280 | --------------------------------------------------------------------------------