128 lines
5.0 KiB
GDScript
128 lines
5.0 KiB
GDScript
extends Path3D
|
|
# Script by Elijah Martin/Palin_drome
|
|
@export_range(3,200,1) var number_of_segments = 10
|
|
@export_range(3,50,1) var mesh_sides = 6
|
|
@export var cable_thickness = 0.1
|
|
@export_range(0.0, 0.99, 0.01) var joint_bias_or_stiffness = 0.2
|
|
@export_range(0.0, 64.0, 0.1) var max_impulse = 2.0
|
|
@export_range(0.0, 100.0, 0.1,"0 to disable to 100 to make completely stable") var physics_dampening = 0.5
|
|
@export var cable_segment_mass = 2.0
|
|
@export var fixed_start_point = true
|
|
@export var fixed_end_point = true
|
|
@export var rigidbody_attached_to_start : RigidBody3D
|
|
@export var rigidbody_attached_to_end : RigidBody3D
|
|
@export var material : Material
|
|
@onready var mesh := $CSGPolygon3D
|
|
@onready var distance = curve.get_baked_length()
|
|
# instances
|
|
var segments : Array[RigidBody3D]
|
|
var joints : Array[PinJoint3D]
|
|
var curve_points : Array
|
|
|
|
# Called when the node enters the scene tree for the first time.
|
|
func _ready() -> void:
|
|
# store position and rotation
|
|
var rotation_buffer = rotation
|
|
rotation = Vector3(0,0,0)
|
|
var position_buffer = position
|
|
position = Vector3(0,0,0)
|
|
|
|
# Duplicate the curve to ensure its unique and resets properly on reloads, avoiding drift due to re-adding the position_buffer
|
|
var cloned_curve = curve.duplicate() # Duplicate curve
|
|
curve = cloned_curve # Assign it as the active curve of the Path3D
|
|
|
|
# CSG Mesh shape array
|
|
var myShape : PackedVector2Array
|
|
for i in (number_of_segments+1):
|
|
curve_points.append(curve.sample_baked((distance*(i))/(number_of_segments), true))
|
|
curve.clear_points()
|
|
|
|
# create cable segments
|
|
for i in number_of_segments:
|
|
# create rigidbodies
|
|
segments.append(RigidBody3D.new())
|
|
self.add_child(segments[i])
|
|
# position rigidbodies between the joints
|
|
segments[i].position = curve_points[i] + (curve_points[i+1] - curve_points[i])/2
|
|
# create collision shape 3Ds
|
|
segments[i].add_child(CollisionShape3D.new())
|
|
# add capsule shape
|
|
segments[i].get_child(0).shape = CapsuleShape3D.new()
|
|
segments[i].get_child(0).shape.radius = cable_thickness
|
|
segments[i].get_child(0).shape.height = (curve_points[i+1]-curve_points[i]).length()
|
|
# small offset added to prevent Vector UP
|
|
segments[i].look_at_from_position(curve_points[i] + (curve_points[i+1] - curve_points[i])/2 + Vector3(0.001, 0, -0.001), curve_points[i+1])
|
|
segments[i].rotation.x += PI/2
|
|
segments[i].linear_damp = physics_dampening
|
|
segments[i].mass = cable_segment_mass
|
|
# create pin joints
|
|
# attach joints to rigidbodies path
|
|
if i == 0 && fixed_start_point:
|
|
#joints.append(ConeTwistJoint3D.new())
|
|
joints.append(PinJoint3D.new())
|
|
self.add_child(joints[i])
|
|
joints[i].position = curve_points[i]
|
|
joints[i].set_param(PinJoint3D.PARAM_BIAS, joint_bias_or_stiffness)
|
|
joints[i].set_param(PinJoint3D.PARAM_IMPULSE_CLAMP, max_impulse)
|
|
else:
|
|
#joints.append(ConeTwistJoint3D.new())
|
|
joints.append(PinJoint3D.new())
|
|
self.add_child(joints[i])
|
|
joints[i].position = curve_points[i]
|
|
joints[i].node_a = segments[i-1].get_path()
|
|
joints[i].node_b = segments[i].get_path()
|
|
#add new curve points
|
|
curve.add_point(curve_points[i])
|
|
curve.add_point(curve_points[number_of_segments])
|
|
# setup mesh array
|
|
for i in mesh_sides:
|
|
myShape.append(Vector2(sin(2*PI*(i+1)/mesh_sides), cos(2*PI*(i+1)/mesh_sides)) * cable_thickness)
|
|
|
|
# setup mesh
|
|
mesh.polygon = myShape
|
|
if material != null:
|
|
mesh.material = material
|
|
|
|
# restore position and rotation
|
|
rotation = rotation_buffer
|
|
# lock joints and segments position and rotation
|
|
for segment in segments:
|
|
segment.top_level = true
|
|
segment.position += position_buffer
|
|
for joint in joints:
|
|
joint.top_level = true
|
|
joint.position += position_buffer
|
|
|
|
# set rotation and position to 0
|
|
# fix start point
|
|
rotation = Vector3(0,0,0)
|
|
if fixed_start_point:
|
|
joints[0].node_b = segments[0].get_path()
|
|
if fixed_end_point:
|
|
#joints.append(ConeTwistJoint3D.new())
|
|
joints.append(PinJoint3D.new())
|
|
self.add_child(joints[-1])
|
|
joints[-1].position = curve_points[-1] + position_buffer
|
|
joints[-1].node_a = segments[number_of_segments - 1].get_path()
|
|
# attach rigid bodies if they exist
|
|
if rigidbody_attached_to_start != null:
|
|
joints[0].node_b = rigidbody_attached_to_start.get_path()
|
|
if rigidbody_attached_to_end != null:
|
|
if fixed_end_point == false:
|
|
joints.append(PinJoint3D.new())
|
|
self.add_child(joints[-1])
|
|
joints[-1].position = curve_points[-1] + position_buffer
|
|
joints[-1].node_a = segments[-1].get_path()
|
|
joints[-1].node_b = rigidbody_attached_to_end.get_path()
|
|
|
|
|
|
func _physics_process(_delta: float) -> void:
|
|
# update curve positions
|
|
for p in (curve.point_count):
|
|
if p < (number_of_segments):
|
|
# get the first segment and subtract it's basis * distance to point at the endpoint
|
|
curve.set_point_position(p, segments[p].position + segments[p].transform.basis.y * segments[p].get_child(0).shape.height/2)
|
|
else:
|
|
# get the last segment and add it's basis * distance to point at the endpoint
|
|
curve.set_point_position(p, segments[p-1].position - segments[p-1].transform.basis.y * segments[p-1].get_child(0).shape.height/2)
|