diff --git a/Notes.md b/Notes.md deleted file mode 100644 index 6f2ce06..0000000 --- a/Notes.md +++ /dev/null @@ -1,22 +0,0 @@ -# Resources - -- [vrpn-rs description of VRPN protocol](https://github.com/vrpn/vrpn-rs/blob/main/Protocol.md) - -# Design - -Allow for a session based design - data needs to be collected per block -and immediatly made available. Minimal or no internal state keeping! - -- first collect names and ids of message_types and senders (stored in session) -- register listeners in session -- listeners inject captured data with signals - -- keep naming "classic" ??? ... above session is a tracker with a sender with sensors - -# Todo -- Testset for Quaternion rotations -- Axis as subscene - -# Internal Notes - -- Graphics Interaction Lab OptiTrack system is at 212.201.64.122 diff --git a/VRPN.gd b/VRPN.gd deleted file mode 100644 index 1a64591..0000000 --- a/VRPN.gd +++ /dev/null @@ -1,197 +0,0 @@ -extends Node -class_name VRPN - -# tracking associated data -enum TrackingData { POS_QUAT, VELOCITY, ACCELERATION } - -# magic cookie -const magic_cookie_start : String = "vrpn: ver." -# kinda redundant as we take the seq number as well -static var header_size : int = aligned_size(20) - - -var sensors : Dictionary[int,String] = {} -var messages : Dictionary[int,String] = {} - -signal connected(s:StreamPeerTCP) -signal data(data:Array) -signal disconnected -signal error - -@export var tracker_receivers : Array[VRPN_Receiver] = [] - -@onready var _stream: StreamPeerTCP = StreamPeerTCP.new() - -@export var vrpn_server : String = "127.0.0.1" -@export var vrpn_port : int = 3883 - -func _ready() -> void: - - self.connect_to_host(vrpn_server,vrpn_port) - - if not connected.has_connections(): - connected.connect(self._on_connected) - if not data.has_connections(): - data.connect(self._on_data) - if not disconnected.has_connections(): - disconnected.connect(self._on_disconnected) - if not error.has_connections(): - error.connect(self._on_error) - -func _process(delta: float) -> void: - var old_status = _stream.get_status() - _stream.poll() - var new_status = _stream.get_status() - if old_status != new_status: - match new_status: - _stream.STATUS_NONE: - emit_signal("disconnected") - _stream.STATUS_CONNECTING: - print("Connecting.") - _stream.STATUS_CONNECTED: - print("Connected.") - emit_signal("connected",_stream.poll() -) - _stream.STATUS_ERROR: - print("Error with socket stream.") - emit_signal("error") - - if new_status == _stream.STATUS_CONNECTED: - var available_bytes: int = _stream.get_available_bytes() - if available_bytes > 0: - var res = _stream.get_partial_data(available_bytes) - if res[0] != OK: - emit_signal("error") - else: - emit_signal("data", res[1]) - -func connect_to_host(host: String, port: int) -> void: - print("Connecting to %s:%d" % [host, port]) - if _stream.connect_to_host(host, port) != OK: - print("Error connecting to host.") - emit_signal("error") - -func send(data: PackedByteArray) -> bool: - if _stream.get_status() != _stream.STATUS_CONNECTED: - print("Error: Stream is not currently connected.") - return false - var error: int = _stream.put_data(data) - if error != OK: - print("Error writing to stream: ", error) - return false - return true - - -func _on_data(data : Array): - var bytes = PackedByteArray(data) - var as_cookie = bytes.get_string_from_ascii() - - # Cookie Hack! - if as_cookie.begins_with(VRPN.magic_cookie_start): # - # kaboom we just send back the same cookie :) - self.send(bytes) - else: - VRPN.marshall_block(bytes,self) - - -func _on_connected(s : StreamPeerTCP): - print("Connected to",s.get_connected_host()) # Replace with function body. - -func _on_disconnected(): - print("Disconnected") # Replace with function body. - -func _on_error(): - print("Error") # Replace with function body. - - -static func marshall_block(data : PackedByteArray,session : VRPN) -> void: - - # need to fix that - var block_offset : int = 0 - - while data.size() > block_offset: - # reader for stream - var header := StreamPeerBuffer.new() - # get block addresses - header.data_array = data.slice(block_offset,block_offset+header_size) - # make sure we read as big endian - header.big_endian = true - - # read header - var length := header.get_32() as int # length of message - var time_sec := header.get_32() as int # datetime sec - var time_msec := header.get_32() as int # datetime micro sec - var sender_id := header.get_32() as int # sender id - var message_type := header.get_32() as int # type of message (payload) - var sequence_num := header.get_32() as int # inofficial sequence number (padding) - - if false: - print("length '%d'" % length) - print("time_sec '%d'" % time_sec) - print("time_msec '%d'" % time_msec) - print("sender_id '%d'" % sender_id) - print("message_type '%d'" % message_type) - print("sequence_num '%d'" % sequence_num) - - marshall_body(data.slice(block_offset+header_size,block_offset+length),message_type,sender_id,session) - - # next datablock - block_offset += aligned_size(length) - -static func decode_string(stream : StreamPeerBuffer) -> String: - var len = stream.get_32() - return stream.get_string(len) - - -static func marshall_body(data : PackedByteArray,message_type : int, sender_id: int, session : VRPN): - var body := StreamPeerBuffer.new() - body.data_array = data - body.big_endian = true - - # only take message_type directly for negative (-1,-2) - # messages that provide dynamic descriptors - if message_type < 0: - # message and sender descriptions - match message_type: - -1: # sensor names - var name = decode_string(body) - print("sensor name is '%s' with '%d" % [name,sender_id]) - session.sensors[sender_id] = name - -2: # message names - var name = decode_string(body) - print("message name is '%s' for message_type '%d'" % [name,sender_id]) - session.messages[sender_id] = name - return - - # now we use the string identifiers - # because they are supposedly dynamically assigned - match session.messages[message_type]: - 'vrpn_Tracker Pos_Quat': # quat pos - # get id - var sensor_id = body.get_32() - var padding = body.get_32() # padding - var pos = Vector3(body.get_double(),body.get_double(),body.get_double()) - # VRPN quaternions are w,xyz - var quat_w = body.get_double() - var quat_x = body.get_double() - var quat_y = body.get_double() - var quat_z = body.get_double() - #var quat = Quaternion(quat_x,quat_y,quat_z,quat_w).normalized() - # bug? Documentation of Godot Quaternion c'tor is x,y,x,w - # but implementation follow classic w,x,y,z order - var quat = Quaternion(quat_w,quat_x,quat_y,quat_z).normalized() - - for r in session.tracker_receivers: - r._on_pos_quat({ - "tracker" : session.sensors[sender_id], - "sensor" : sensor_id, - "position" : pos, - "rotation" : quat - }) - _: - pass - #print("unhandled message type {0}".format([message_type])) - - -static func aligned_size(actual_size : int, alignment : int = 8) -> int: - return (actual_size + alignment - 1) & ~(alignment - 1) diff --git a/VRPN.gd.uid b/VRPN.gd.uid deleted file mode 100644 index 06400fa..0000000 --- a/VRPN.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://dmq3i7qmo1qe0 diff --git a/VRPN_Receiver.gd b/VRPN_Receiver.gd deleted file mode 100644 index 5e6dc23..0000000 --- a/VRPN_Receiver.gd +++ /dev/null @@ -1,17 +0,0 @@ -extends Node3D - -class_name VRPN_Receiver - -@export var tracker_name : String = "Tracker0" -@export var tracker_sensor : int = 0 -@export var tracker_use_position : bool = true -@export var tracker_use_rotation : bool = true - -func _on_pos_quat(tracker_data : Dictionary): - - if tracker_data['tracker'] == tracker_name and tracker_data['sensor'] == tracker_sensor: - if tracker_use_position: - self.global_position = tracker_data['position'] - if tracker_use_rotation: - var rotation := tracker_data['rotation'] as Quaternion - self.global_basis = Basis(rotation) diff --git a/VRPN_Receiver.gd.uid b/VRPN_Receiver.gd.uid deleted file mode 100644 index fcbe47b..0000000 --- a/VRPN_Receiver.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://dpj1wrvfsiq4v diff --git a/Root.tscn b/addons/vrpn/Root.tscn similarity index 93% rename from Root.tscn rename to addons/vrpn/Root.tscn index 2fa2a31..eb24b99 100644 --- a/Root.tscn +++ b/addons/vrpn/Root.tscn @@ -1,8 +1,8 @@ [gd_scene load_steps=5 format=3 uid="uid://bj5ykdjle10tt"] -[ext_resource type="Script" uid="uid://dmq3i7qmo1qe0" path="res://VRPN.gd" id="2_24d08"] -[ext_resource type="Script" uid="uid://dpj1wrvfsiq4v" path="res://VRPN_Receiver.gd" id="2_170dk"] -[ext_resource type="PackedScene" uid="uid://b426fy7d6jw2d" path="res://axis.blend" id="3_170dk"] +[ext_resource type="Script" uid="uid://dmq3i7qmo1qe0" path="res://addons/vrpn/scripts/VRPN.gd" id="2_24d08"] +[ext_resource type="Script" uid="uid://dpj1wrvfsiq4v" path="res://addons/vrpn/scripts/VRPN_Receiver.gd" id="2_170dk"] +[ext_resource type="PackedScene" uid="uid://b426fy7d6jw2d" path="res://addons/vrpn/assets/axis.blend" id="3_170dk"] [sub_resource type="PlaneMesh" id="PlaneMesh_24d08"] size = Vector2(6, 2) @@ -21,9 +21,9 @@ shadow_enabled = true [node name="VRPN" type="Node3D" parent="Root" node_paths=PackedStringArray("tracker_receivers")] script = ExtResource("2_24d08") tracker_receivers = [NodePath("RB1"), NodePath("RB2"), NodePath("../SpinTracker/Offset0/Tracker0"), NodePath("../SpinTracker/Tracker1"), NodePath("../SpinTracker/Offset2/Tracker2")] -vrpn_server = "212.201.64.122" [node name="RB1" type="Node3D" parent="Root/VRPN"] +visible = false script = ExtResource("2_170dk") tracker_name = "RB1" @@ -31,6 +31,7 @@ tracker_name = "RB1" transform = Transform3D(0.1, 0, 0, 0, 0.1, 0, 0, 0, 0.1, 0, 0, 0) [node name="RB2" type="Node3D" parent="Root/VRPN"] +visible = false script = ExtResource("2_170dk") tracker_name = "RB2" @@ -46,11 +47,11 @@ mesh = SubResource("PlaneMesh_24d08") [node name="SpinTracker" type="Node3D" parent="Root"] transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.764802, 0) -visible = false [node name="Tracker1" type="Node3D" parent="Root/SpinTracker"] script = ExtResource("2_170dk") tracker_name = "Tracker1" +tracker_use_position = false [node name="axis2" parent="Root/SpinTracker/Tracker1" instance=ExtResource("3_170dk")] transform = Transform3D(0.1, 0, 0, 0, 0.1, 0, 0, 0, 0.1, 0, 0, 0) @@ -65,6 +66,7 @@ transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -1, 0, 0) [node name="Tracker2" type="Node3D" parent="Root/SpinTracker/Offset2"] script = ExtResource("2_170dk") tracker_name = "Tracker2" +tracker_use_position = false [node name="axis" parent="Root/SpinTracker/Offset2/Tracker2" instance=ExtResource("3_170dk")] transform = Transform3D(0.1, 0, 0, 0, 0.1, 0, 0, 0, 0.1, 0, 0, 0) @@ -78,6 +80,7 @@ transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0) [node name="Tracker0" type="Node3D" parent="Root/SpinTracker/Offset0"] script = ExtResource("2_170dk") +tracker_use_position = false [node name="axis" parent="Root/SpinTracker/Offset0/Tracker0" instance=ExtResource("3_170dk")] transform = Transform3D(0.1, 0, 0, 0, 0.1, 0, 0, 0, 0.1, 0, 0, 0) diff --git a/vrpn.test.cfg b/addons/vrpn/vrpn.test.cfg similarity index 100% rename from vrpn.test.cfg rename to addons/vrpn/vrpn.test.cfg diff --git a/axis.blend b/axis.blend deleted file mode 100644 index 6ed72dd..0000000 Binary files a/axis.blend and /dev/null differ diff --git a/axis.blend.import b/axis.blend.import deleted file mode 100644 index 7503db5..0000000 --- a/axis.blend.import +++ /dev/null @@ -1,62 +0,0 @@ -[remap] - -importer="scene" -importer_version=1 -type="PackedScene" -uid="uid://b426fy7d6jw2d" -path="res://.godot/imported/axis.blend-5b7f41137e49e6b98e717dd423c9150b.scn" - -[deps] - -source_file="res://axis.blend" -dest_files=["res://.godot/imported/axis.blend-5b7f41137e49e6b98e717dd423c9150b.scn"] - -[params] - -nodes/root_type="" -nodes/root_name="" -nodes/apply_root_scale=true -nodes/root_scale=1.0 -nodes/import_as_skeleton_bones=false -nodes/use_node_type_suffixes=true -meshes/ensure_tangents=true -meshes/generate_lods=true -meshes/create_shadow_meshes=true -meshes/light_baking=1 -meshes/lightmap_texel_size=0.2 -meshes/force_disable_compression=false -skins/use_named_skins=true -animation/import=true -animation/fps=30 -animation/trimming=false -animation/remove_immutable_tracks=true -animation/import_rest_as_RESET=false -import_script/path="" -_subresources={ -"nodes": { -"PATH:Camera": { -"import/skip_import": true -}, -"PATH:Light": { -"import/skip_import": true -} -} -} -blender/nodes/visible=0 -blender/nodes/active_collection_only=false -blender/nodes/punctual_lights=true -blender/nodes/cameras=true -blender/nodes/custom_properties=true -blender/nodes/modifiers=1 -blender/meshes/colors=false -blender/meshes/uvs=true -blender/meshes/normals=true -blender/meshes/export_geometry_nodes_instances=false -blender/meshes/tangents=true -blender/meshes/skins=2 -blender/meshes/export_bones_deforming_mesh_only=false -blender/materials/unpack_enabled=true -blender/materials/export_materials=1 -blender/animation/limit_playback=true -blender/animation/always_sample=true -blender/animation/group_tracks=true diff --git a/icon.svg b/icon.svg deleted file mode 100644 index 9d8b7fa..0000000 --- a/icon.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/icon.svg.import b/icon.svg.import deleted file mode 100644 index 25183c1..0000000 --- a/icon.svg.import +++ /dev/null @@ -1,37 +0,0 @@ -[remap] - -importer="texture" -type="CompressedTexture2D" -uid="uid://cdprcmtx102rp" -path="res://.godot/imported/icon.svg-218a8f2b3041327d8a5756f3a245f83b.ctex" -metadata={ -"vram_texture": false -} - -[deps] - -source_file="res://icon.svg" -dest_files=["res://.godot/imported/icon.svg-218a8f2b3041327d8a5756f3a245f83b.ctex"] - -[params] - -compress/mode=0 -compress/high_quality=false -compress/lossy_quality=0.7 -compress/hdr_compression=1 -compress/normal_map=0 -compress/channel_pack=0 -mipmaps/generate=false -mipmaps/limit=-1 -roughness/mode=0 -roughness/src_normal="" -process/fix_alpha_border=true -process/premult_alpha=false -process/normal_map_invert_y=false -process/hdr_as_srgb=false -process/hdr_clamp_exposure=false -process/size_limit=0 -detect_3d/compress_to=1 -svg/scale=1.0 -editor/scale_with_editor_scale=false -editor/convert_colors_with_editor_theme=false diff --git a/project.godot b/project.godot index 70fb73a..19b923a 100644 --- a/project.godot +++ b/project.godot @@ -13,4 +13,4 @@ config_version=5 config/name="uvrpn" run/main_scene="uid://bj5ykdjle10tt" config/features=PackedStringArray("4.4", "Forward Plus") -config/icon="res://icon.svg" +config/icon="uid://cdprcmtx102rp"