restructure the repository

This commit is contained in:
Hartmut Seichter 2025-07-02 16:15:04 +02:00
parent 63230b7754
commit 936acf8703
12 changed files with 9 additions and 344 deletions

View file

@ -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

197
VRPN.gd
View file

@ -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)

View file

@ -1 +0,0 @@
uid://dmq3i7qmo1qe0

View file

@ -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)

View file

@ -1 +0,0 @@
uid://dpj1wrvfsiq4v

View file

@ -1,8 +1,8 @@
[gd_scene load_steps=5 format=3 uid="uid://bj5ykdjle10tt"] [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://dmq3i7qmo1qe0" path="res://addons/vrpn/scripts/VRPN.gd" id="2_24d08"]
[ext_resource type="Script" uid="uid://dpj1wrvfsiq4v" path="res://VRPN_Receiver.gd" id="2_170dk"] [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://axis.blend" id="3_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"] [sub_resource type="PlaneMesh" id="PlaneMesh_24d08"]
size = Vector2(6, 2) size = Vector2(6, 2)
@ -21,9 +21,9 @@ shadow_enabled = true
[node name="VRPN" type="Node3D" parent="Root" node_paths=PackedStringArray("tracker_receivers")] [node name="VRPN" type="Node3D" parent="Root" node_paths=PackedStringArray("tracker_receivers")]
script = ExtResource("2_24d08") script = ExtResource("2_24d08")
tracker_receivers = [NodePath("RB1"), NodePath("RB2"), NodePath("../SpinTracker/Offset0/Tracker0"), NodePath("../SpinTracker/Tracker1"), NodePath("../SpinTracker/Offset2/Tracker2")] 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"] [node name="RB1" type="Node3D" parent="Root/VRPN"]
visible = false
script = ExtResource("2_170dk") script = ExtResource("2_170dk")
tracker_name = "RB1" 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) 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"] [node name="RB2" type="Node3D" parent="Root/VRPN"]
visible = false
script = ExtResource("2_170dk") script = ExtResource("2_170dk")
tracker_name = "RB2" tracker_name = "RB2"
@ -46,11 +47,11 @@ mesh = SubResource("PlaneMesh_24d08")
[node name="SpinTracker" type="Node3D" parent="Root"] [node name="SpinTracker" type="Node3D" parent="Root"]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.764802, 0) 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"] [node name="Tracker1" type="Node3D" parent="Root/SpinTracker"]
script = ExtResource("2_170dk") script = ExtResource("2_170dk")
tracker_name = "Tracker1" tracker_name = "Tracker1"
tracker_use_position = false
[node name="axis2" parent="Root/SpinTracker/Tracker1" instance=ExtResource("3_170dk")] [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) 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"] [node name="Tracker2" type="Node3D" parent="Root/SpinTracker/Offset2"]
script = ExtResource("2_170dk") script = ExtResource("2_170dk")
tracker_name = "Tracker2" tracker_name = "Tracker2"
tracker_use_position = false
[node name="axis" parent="Root/SpinTracker/Offset2/Tracker2" instance=ExtResource("3_170dk")] [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) 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"] [node name="Tracker0" type="Node3D" parent="Root/SpinTracker/Offset0"]
script = ExtResource("2_170dk") script = ExtResource("2_170dk")
tracker_use_position = false
[node name="axis" parent="Root/SpinTracker/Offset0/Tracker0" instance=ExtResource("3_170dk")] [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) transform = Transform3D(0.1, 0, 0, 0, 0.1, 0, 0, 0, 0.1, 0, 0, 0)

Binary file not shown.

View file

@ -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

View file

@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="128" height="128"><rect width="124" height="124" x="2" y="2" fill="#363d52" stroke="#212532" stroke-width="4" rx="14"/><g fill="#fff" transform="translate(12.322 12.322)scale(.101)"><path d="M105 673v33q407 354 814 0v-33z"/><path fill="#478cbf" d="m105 673 152 14q12 1 15 14l4 67 132 10 8-61q2-11 15-15h162q13 4 15 15l8 61 132-10 4-67q3-13 15-14l152-14V427q30-39 56-81-35-59-83-108-43 20-82 47-40-37-88-64 7-51 8-102-59-28-123-42-26 43-46 89-49-7-98 0-20-46-46-89-64 14-123 42 1 51 8 102-48 27-88 64-39-27-82-47-48 49-83 108 26 42 56 81zm0 33v39c0 276 813 276 814 0v-39l-134 12-5 69q-2 10-14 13l-162 11q-12 0-16-11l-10-65H446l-10 65q-4 11-16 11l-162-11q-12-3-14-13l-5-69z"/><path d="M483 600c0 34 58 34 58 0v-86c0-34-58-34-58 0z"/><circle cx="725" cy="526" r="90"/><circle cx="299" cy="526" r="90"/></g><g fill="#414042" transform="translate(12.322 12.322)scale(.101)"><circle cx="307" cy="532" r="60"/><circle cx="717" cy="532" r="60"/></g></svg>

Before

Width:  |  Height:  |  Size: 994 B

View file

@ -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

View file

@ -13,4 +13,4 @@ config_version=5
config/name="uvrpn" config/name="uvrpn"
run/main_scene="uid://bj5ykdjle10tt" run/main_scene="uid://bj5ykdjle10tt"
config/features=PackedStringArray("4.4", "Forward Plus") config/features=PackedStringArray("4.4", "Forward Plus")
config/icon="res://icon.svg" config/icon="uid://cdprcmtx102rp"