Skip to content

Commit

Permalink
Fix the poke lag (#489)
Browse files Browse the repository at this point in the history
Fix minor gdlint issues

Added ability to enable/disable pushing bodies. Added control over collision layer and mask. Organized properties into categories.
  • Loading branch information
Malcolmnixon committed Jul 29, 2023
1 parent 7e1f7e5 commit c446462
Show file tree
Hide file tree
Showing 4 changed files with 205 additions and 33 deletions.
1 change: 1 addition & 0 deletions addons/godot-xr-tools/player/player_body.tscn
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
[ext_resource type="Script" path="res://addons/godot-xr-tools/player/player_body.gd" id="1"]

[node name="PlayerBody" type="CharacterBody3D" groups=["player_body"]]
process_priority = -100
top_level = true
collision_layer = 524288
collision_mask = 1023
Expand Down
102 changes: 96 additions & 6 deletions addons/godot-xr-tools/player/poke/poke.gd
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,56 @@ class_name XRToolsPoke
extends Node3D


## XR Tools Poke Script
##
## This node a finger push mechanism that can be attached to a finger bone
## using a [BoneAttachment3D].
##
## The poke can interact with user interfaces, and can optionally push rigid
## bodies.


# Default layer of 18:player-hands
const DEFAULT_LAYER := 0b0000_0000_0000_0010_0000_0000_0000_0000

# Default mask of 0xFFFF (1..16)
# - 1:static-world
# - 2:dynamic-world
# - 3:pickable-objects
# - 4:wall-walking
# - 5:grappling-target
const DEFAULT_MASK := 0b0000_0000_0000_0000_1111_1111_1111_1111


## Enables or disables the poke functionality
@export var enabled : bool = true: set = set_enabled

## Sets the radius of the poke mesh and collision
@export var radius : float = 0.005: set = set_radius
@export var teleport_distance : float = 0.1: set = set_teleport_distance

## Set the color of the poke mesh
@export var color : Color = Color(0.8, 0.8, 1.0, 0.5): set = set_color

## Set the poke teleport distance
@export var teleport_distance : float = 0.1: set = set_teleport_distance

@export_category("Poke Collison")

## Sets the collision layer
@export_flags_3d_physics var layer : int = DEFAULT_LAYER: set = set_layer

## Sets the collision mask
@export_flags_3d_physics var mask : int = DEFAULT_MASK: set = set_mask

## Enables or disables pushing bodies
@export var push_bodies : bool = true: set = set_push_bodies

## Control the stiffness of the finger
@export var stiffness : float = 10.0: set = set_stiffness

## Control the maximum force the finger can push with
@export var maximum_force : float = 1.0: set = set_maximum_force


var is_ready = false
var material : StandardMaterial3D
Expand Down Expand Up @@ -44,11 +89,51 @@ func _update_radius() -> void:
func set_teleport_distance(new_distance : float) -> void:
teleport_distance = new_distance
if is_ready:
_update_set_teleport_distance()
_update_teleport_distance()

func _update_set_teleport_distance() -> void:
func _update_teleport_distance() -> void:
$PokeBody.teleport_distance = teleport_distance

func set_push_bodies(new_push_bodies : bool) -> void:
push_bodies = new_push_bodies
if is_ready:
_update_push_bodies()

func _update_push_bodies() -> void:
$PokeBody.push_bodies = push_bodies

func set_layer(new_layer : int) -> void:
layer = new_layer
if is_ready:
_update_layer()

func _update_layer() -> void:
$PokeBody.collision_layer = layer

func set_mask(new_mask : int) -> void:
mask = new_mask
if is_ready:
_update_mask()

func _update_mask() -> void:
$PokeBody.collision_mask = mask

func set_stiffness(new_stiffness : float) -> void:
stiffness = new_stiffness
if is_ready:
_update_stiffness()

func _update_stiffness() -> void:
$PokeBody.stiffness = stiffness

func set_maximum_force(new_maximum_force : float) -> void:
maximum_force = new_maximum_force
if is_ready:
_update_maximum_force()

func _update_maximum_force() -> void:
$PokeBody.maximum_force = maximum_force

func set_color(new_color : Color) -> void:
color = new_color
if is_ready:
Expand Down Expand Up @@ -76,7 +161,12 @@ func _ready():

_update_enabled()
_update_radius()
_update_set_teleport_distance()
_update_teleport_distance()
_update_layer()
_update_mask()
_update_push_bodies()
_update_stiffness()
_update_maximum_force()
_update_color()

func _process(_delta):
Expand All @@ -93,7 +183,7 @@ func _process(_delta):
set_process(false)


func _on_PokeBody_body_entered(body):
func _on_PokeBody_body_contact_start(body):
# We are going to poke this body at our current position.
# This will be slightly above the object but since this
# mostly targets Viewport2Din3D, this will work
Expand All @@ -110,7 +200,7 @@ func _on_PokeBody_body_entered(body):
if target:
set_process(true)

func _on_PokeBody_body_exited(body):
func _on_PokeBody_body_contact_end(body):
if body.has_signal("pointer_released"):
body.emit_signal("pointer_released", last_collided_at)
elif body.has_method("pointer_released"):
Expand Down
19 changes: 10 additions & 9 deletions addons/godot-xr-tools/player/poke/poke.tscn
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
[gd_scene load_steps=5 format=3]
[gd_scene load_steps=6 format=3 uid="uid://bjcxf427un2wp"]

[ext_resource type="Script" path="res://addons/godot-xr-tools/player/poke/poke.gd" id="1"]
[ext_resource type="Script" path="res://addons/godot-xr-tools/player/poke/poke_body.gd" id="2"]
Expand All @@ -14,25 +14,26 @@ height = 0.01
radial_segments = 32
rings = 16

[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_yhep2"]
transparency = 1
shading_mode = 0
albedo_color = Color(0.8, 0.8, 1, 0.5)

[node name="Poke" type="Node3D"]
script = ExtResource("1")

[node name="PokeBody" type="RigidBody3D" parent="."]
[node name="PokeBody" type="StaticBody3D" parent="."]
top_level = true
collision_layer = 131072
collision_mask = 65535
gravity_scale = 0.0
custom_integrator = true
max_contacts_reported = 1
contact_monitor = true
script = ExtResource("2")
teleport_distance = 0.1

[node name="CollisionShape" type="CollisionShape3D" parent="PokeBody"]
shape = SubResource("1")

[node name="MeshInstance" type="MeshInstance3D" parent="PokeBody"]
mesh = SubResource("2")
surface_material_override/0 = SubResource("StandardMaterial3D_yhep2")

[connection signal="body_entered" from="PokeBody" to="." method="_on_PokeBody_body_entered"]
[connection signal="body_exited" from="PokeBody" to="." method="_on_PokeBody_body_exited"]
[connection signal="body_contact_end" from="PokeBody" to="." method="_on_PokeBody_body_contact_end"]
[connection signal="body_contact_start" from="PokeBody" to="." method="_on_PokeBody_body_contact_start"]
116 changes: 98 additions & 18 deletions addons/godot-xr-tools/player/poke/poke_body.gd
Original file line number Diff line number Diff line change
@@ -1,23 +1,103 @@
extends RigidBody3D
extends StaticBody3D

# distance at which we teleport our poke body
@export var teleport_distance : float = 0.2

func _integrate_forces(state: PhysicsDirectBodyState3D):
# get the position of our parent that we are following
var following_transform = get_parent().global_transform
## Signal called when we start to contact an object
signal body_contact_start(node)

# see how much we need to move
var delta_movement = following_transform.origin - state.transform.origin
var delta_length = delta_movement.length()
## Signal called when we end contact with an object
signal body_contact_end(node)

if delta_length > teleport_distance:
# teleport our poke body to its new location
state.angular_velocity = Vector3()
state.linear_velocity = Vector3()
state.transform.origin = following_transform.origin

## Distance at which we teleport our poke body
@export var teleport_distance : float = 0.1

## Enable or disable pushing rigid bodies
@export var push_bodies : bool = true

## Stiffness of the finger
@export var stiffness : float = 10.0

## Maximum finger force
@export var maximum_force : float = 1.0


# Node currently in contact with
var _contact : Node3D = null

# Target XRToolsPoke
@onready var _target : XRToolsPoke = get_parent()


# Add support for is_xr_class on XRTools classes
func is_xr_class(name : String) -> bool:
return name == "XRToolsPokeBody"


# Try moving to the parent Poke node
func _physics_process(_delta):
# Get the current position and contact
var old_position := global_position
var old_contact := _contact

# Calculate the movement to perform
var target_position := _target.global_position
var to_target := target_position - old_position
var effort := to_target.length()

# Decide whether to teleport or slide
if effort > teleport_distance:
# Perform Teleport
global_position = target_position
else:
# trigger physics to move our body in one step
state.angular_velocity = Vector3()
state.linear_velocity = delta_movement / state.step
state.integrate_forces()
# Perform Slide (up to 4 times)
_contact = null
var force_direction := to_target.normalized()
var next_direction := force_direction
for n in 4:
# Calculate how efficiently we can move/slide
var efficiency := next_direction.dot(force_direction)
if efficiency <= 0.0 or effort <= 0.0:
break

# Perform the move
var step_physical := effort * efficiency
var collision := move_and_collide(next_direction * step_physical)

# If no collision then we have moved the rest of the distance
if not collision:
break

# Calculate how much of the move remains [0..1]
var remains := collision.get_remainder().length() / step_physical
remains = clamp(remains, 0.0, 1.0)

# Update the remaining effort
effort *= remains

# Calculate the next slide direction
next_direction = force_direction.slide(collision.get_normal()).normalized()

# Save the contact
_contact = collision.get_collider()

# Optionally support pushing rigid bodies
if push_bodies:
var contact_rigid := _contact as RigidBody3D
if contact_rigid:
# Calculate the finger force
var force := target_position - global_position
force *= stiffness
force = force.limit_length(maximum_force)

# Apply as an impulse
contact_rigid.apply_impulse(
force,
collision.get_position() - contact_rigid.global_position)

# Report when we stop being in contact with the current object
if old_contact and old_contact != _contact:
body_contact_end.emit(old_contact)

# Report when we start touching a new object
if _contact and _contact != old_contact:
body_contact_start.emit(_contact)

0 comments on commit c446462

Please sign in to comment.