From 91ce62f680e6969fe58f3941f0c9159c8457e259 Mon Sep 17 00:00:00 2001 From: Christopher Arndt Date: Thu, 2 Nov 2023 05:22:31 +0100 Subject: [PATCH] Initial commit Signed-off-by: Christopher Arndt --- .gitattributes | 2 + .gitignore | 4 ++ OSCReceiver.gd | 167 +++++++++++++++++++++++++++++++++++++++++++++ Slider.gd | 9 +++ ToggleButton.gd | 9 +++ UI.gd | 23 +++++++ UI.tscn | 116 +++++++++++++++++++++++++++++++ default_env.tres | 4 ++ export_presets.cfg | 39 +++++++++++ osc_receiver.tscn | 7 ++ project.godot | 28 ++++++++ 11 files changed, 408 insertions(+) create mode 100644 .gitattributes create mode 100644 .gitignore create mode 100644 OSCReceiver.gd create mode 100644 Slider.gd create mode 100644 ToggleButton.gd create mode 100644 UI.gd create mode 100644 UI.tscn create mode 100644 default_env.tres create mode 100644 export_presets.cfg create mode 100644 osc_receiver.tscn create mode 100644 project.godot diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..8ad74f7 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,2 @@ +# Normalize EOL for all files that Git considers text files. +* text=auto eol=lf diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..2baf1a1 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +export/ +.godot/ +.godot/ +*.translation diff --git a/OSCReceiver.gd b/OSCReceiver.gd new file mode 100644 index 0000000..37e64f5 --- /dev/null +++ b/OSCReceiver.gd @@ -0,0 +1,167 @@ +extends Node + + +@export var default_address:String = "0.0.0.0" +@export var default_port: int = 9001 +@export var poll_interval: float = 0.05 +@export var debug: bool = false +var _server: UDPServer +var _observers: Dictionary +var _timer: Timer + + +func _init(): + _observers = {} + + _timer = Timer.new() + _timer.autostart = false + _timer.one_shot = false + _timer.timeout.connect(self._poll) + add_child(_timer) + + +func _exit_tree(): + stop_server() + + +func register_callback(oscaddress, argtypes, callback): + if not _observers.has([oscaddress, argtypes]): + _observers[[oscaddress, argtypes]] = [] + + _observers[[oscaddress, argtypes]].append(callback) + + +func start_server(address:String = default_address, port:int = default_port): + _server = UDPServer.new() + + if _server.listen(port, address) != OK: + _debug("OSCReceiver could not bind to port: %s" % port) + else: + _debug("OSCReceiver listening on port: %s" % port) + + _timer.start(poll_interval) + + +func stop_server(): + _timer.stop() + remove_child(_timer) + _timer.free() + + if _server: + _server.stop() + + +func _poll(): + if not _server.is_listening(): + return + + _server.poll() + + if _server.is_connection_available(): + var peer: PacketPeerUDP = _server.take_connection() + var packet = peer.get_packet() + var sender_ip = peer.get_packet_ip() + var sender_port = peer.get_packet_port() + + _debug("Accepted peer: %s:%s" % [sender_ip, sender_port]) + + var result = _parse_osc_addr_and_types(packet) + var address = result[0] + var types = result[1] + var offset = result[2] + + _debug("OSC address: %s" % address) + _debug("OSC arg types: %s" % types) + + if _observers.has([address, types]): + var values = _parse_osc_values(packet, types, offset) + + if values == null: + _debug("Invalid/Unsupported OSC message.") + elif values.size() != types.length(): + _debug("Mismatch between expected / received number of OSC arguments.") + else: + values.append({ + "ip": sender_ip, + "port": sender_port, + "address": address, + "types": types + }) + + for callback in _observers[[address, types]]: + callback.callv(values) + +func _parse_osc_addr_and_types(packet): + var asep = packet.find(0) + var address = packet.slice(0, asep).get_string_from_ascii() + + var toffset = asep + (4 - asep % 4) + assert(char(packet.decode_u8(toffset)) == ",") + var tsep = packet.find(0, toffset) + var types = packet.slice(toffset + 1, tsep).get_string_from_ascii() + + return [address, types, tsep + (4 - tsep % 4)] + + +func _parse_osc_values(packet, types, offset): + var values = [] + var stream = StreamPeerBuffer.new() + stream.set_data_array(packet.slice(offset)) + stream.set_big_endian(true) + + for type_id in types: + match type_id: + "i": + values.append(stream.get_32()) + "h": + values.append(stream.get_62()) + "f": + values.append(stream.get_float()) + "d": + values.append(stream.get_double()) + "c": + values.append(char(stream.get_32())) + "s", "S": + var value = PackedStringArray() + var null_found = false + + while not null_found: + for _i in range(4): + var ch = stream.get_u8() + if not null_found and ch != 0: + value.append(char(ch)) + else: + null_found = true + + values.append("".join(value)) + "b": + var count = stream.get_u32() + values.append(stream.get_data(count)) + + if count % 4: + stream.seek(stream.get_position() + (count % 4)) + "t": + values.append(stream.get_data(8)) + "m", "r": + values.append([ + stream.get_u8(), + stream.get_u8(), + stream.get_u8(), + stream.get_u8(), + ]) + "T": + values.append(true) + "F": + values.append(false) + "I", "N": + values.append(null) + _: + _debug("Argument type '%s' not yet supported." % type_id) + return + + return values + + +func _debug(msg): + if debug: + print(msg) diff --git a/Slider.gd b/Slider.gd new file mode 100644 index 0000000..c931e69 --- /dev/null +++ b/Slider.gd @@ -0,0 +1,9 @@ +extends VSlider + + +func recv_osc(val, msg_info): + print("Sender IP: %s" % msg_info["ip"]) + print("Sender Port: %d" % msg_info["port"]) + print("Adress: %s" % msg_info["address"]) + print("Types: %s" % msg_info["types"]) + set_value(clampf(val, 0.0, 1.0)) diff --git a/ToggleButton.gd b/ToggleButton.gd new file mode 100644 index 0000000..cb72837 --- /dev/null +++ b/ToggleButton.gd @@ -0,0 +1,9 @@ +extends Button + + +func recv_osc(val, msg_info): + print("Sender IP: %s" % msg_info["ip"]) + print("Sender Port: %d" % msg_info["port"]) + print("Adress: %s" % msg_info["address"]) + print("Types: %s" % msg_info["types"]) + set_pressed_no_signal(bool(val)) diff --git a/UI.gd b/UI.gd new file mode 100644 index 0000000..59e4e7f --- /dev/null +++ b/UI.gd @@ -0,0 +1,23 @@ +extends Control + + +func _ready(): + var scene = preload("res://osc_receiver.tscn") + var osc_server = scene.instantiate() + add_child(osc_server) + + # configure all sensors + for i in range(4): + var nodename = "VSlider%d" % (i + 1) + var osc_addr = "/slider/%d/set" % i + var slider_node = find_child(nodename) + osc_server.register_callback(osc_addr, "f", slider_node.recv_osc) + + for i in range(4): + var nodename = "Button%d" % (i + 1) + var osc_addr = "/button/%d/set" % i + var button_node = find_child(nodename) + osc_server.register_callback(osc_addr, "i", button_node.recv_osc) + + # start listening for osc messages + osc_server.start_server() diff --git a/UI.tscn b/UI.tscn new file mode 100644 index 0000000..fe50916 --- /dev/null +++ b/UI.tscn @@ -0,0 +1,116 @@ +[gd_scene load_steps=8 format=3 uid="uid://8xyprd3yldfg"] + +[ext_resource type="Script" path="res://UI.gd" id="1_ft657"] +[ext_resource type="Script" path="res://Slider.gd" id="2_310mb"] +[ext_resource type="Script" path="res://ToggleButton.gd" id="3_ct66k"] + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_y0b18"] +bg_color = Color(1, 0, 0, 1) + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_iu8u7"] +bg_color = Color(0, 1, 0, 1) + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_7e33t"] +bg_color = Color(0, 0, 1, 1) + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_eeife"] +bg_color = Color(1, 1, 0.0235294, 1) + +[node name="UI" type="Control"] +layout_mode = 3 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +script = ExtResource("1_ft657") + +[node name="MarginContainer" type="MarginContainer" parent="."] +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +theme_override_constants/margin_left = 20 +theme_override_constants/margin_top = 20 +theme_override_constants/margin_right = 20 +theme_override_constants/margin_bottom = 20 + +[node name="GridContainer" type="GridContainer" parent="MarginContainer"] +layout_mode = 2 +theme_override_constants/h_separation = 10 +theme_override_constants/v_separation = 10 +columns = 4 + +[node name="VSlider1" type="VSlider" parent="MarginContainer/GridContainer"] +custom_minimum_size = Vector2(50, 300) +layout_mode = 2 +size_flags_horizontal = 3 +size_flags_vertical = 3 +max_value = 1.0 +step = 0.01 +tick_count = 11 +ticks_on_borders = true +script = ExtResource("2_310mb") + +[node name="VSlider2" type="VSlider" parent="MarginContainer/GridContainer"] +custom_minimum_size = Vector2(50, 300) +layout_mode = 2 +size_flags_horizontal = 3 +size_flags_vertical = 3 +max_value = 1.0 +step = 0.01 +tick_count = 11 +ticks_on_borders = true +script = ExtResource("2_310mb") + +[node name="VSlider3" type="VSlider" parent="MarginContainer/GridContainer"] +custom_minimum_size = Vector2(50, 300) +layout_mode = 2 +size_flags_horizontal = 3 +size_flags_vertical = 3 +max_value = 1.0 +step = 0.01 +tick_count = 11 +ticks_on_borders = true +script = ExtResource("2_310mb") + +[node name="VSlider4" type="VSlider" parent="MarginContainer/GridContainer"] +custom_minimum_size = Vector2(50, 300) +layout_mode = 2 +size_flags_horizontal = 3 +size_flags_vertical = 3 +max_value = 1.0 +step = 0.01 +tick_count = 11 +ticks_on_borders = true +script = ExtResource("2_310mb") + +[node name="Button1" type="Button" parent="MarginContainer/GridContainer"] +layout_mode = 2 +theme_override_styles/pressed = SubResource("StyleBoxFlat_y0b18") +toggle_mode = true +text = "1" +script = ExtResource("3_ct66k") + +[node name="Button2" type="Button" parent="MarginContainer/GridContainer"] +layout_mode = 2 +theme_override_styles/pressed = SubResource("StyleBoxFlat_iu8u7") +toggle_mode = true +text = "2" +script = ExtResource("3_ct66k") + +[node name="Button3" type="Button" parent="MarginContainer/GridContainer"] +layout_mode = 2 +theme_override_styles/pressed = SubResource("StyleBoxFlat_7e33t") +toggle_mode = true +text = "3" +script = ExtResource("3_ct66k") + +[node name="Button4" type="Button" parent="MarginContainer/GridContainer"] +layout_mode = 2 +theme_override_styles/pressed = SubResource("StyleBoxFlat_eeife") +toggle_mode = true +text = "4 " +script = ExtResource("3_ct66k") diff --git a/default_env.tres b/default_env.tres new file mode 100644 index 0000000..ed81c1c --- /dev/null +++ b/default_env.tres @@ -0,0 +1,4 @@ +[gd_resource type="Environment" format=2] + +[resource] +background_mode = 2 diff --git a/export_presets.cfg b/export_presets.cfg new file mode 100644 index 0000000..1a2dc01 --- /dev/null +++ b/export_presets.cfg @@ -0,0 +1,39 @@ +[preset.0] + +name="Linux/X11" +platform="Linux/X11" +runnable=true +dedicated_server=false +custom_features="" +export_filter="all_resources" +include_filter="" +exclude_filter="" +export_path="export/osc_receiver_demo.x86_64" +encryption_include_filters="" +encryption_exclude_filters="" +encrypt_pck=false +encrypt_directory=false + +[preset.0.options] + +custom_template/debug="" +custom_template/release="" +debug/export_console_wrapper=1 +binary_format/embed_pck=false +texture_format/bptc=true +texture_format/s3tc=true +texture_format/etc=false +texture_format/etc2=false +binary_format/architecture="x86_64" +ssh_remote_deploy/enabled=false +ssh_remote_deploy/host="user@host_ip" +ssh_remote_deploy/port="22" +ssh_remote_deploy/extra_args_ssh="" +ssh_remote_deploy/extra_args_scp="" +ssh_remote_deploy/run_script="#!/usr/bin/env bash +export DISPLAY=:0 +unzip -o -q \"{temp_dir}/{archive_name}\" -d \"{temp_dir}\" +\"{temp_dir}/{exe_name}\" {cmd_args}" +ssh_remote_deploy/cleanup_script="#!/usr/bin/env bash +kill $(pgrep -x -f \"{temp_dir}/{exe_name} {cmd_args}\") +rm -rf \"{temp_dir}\"" diff --git a/osc_receiver.tscn b/osc_receiver.tscn new file mode 100644 index 0000000..17a2e85 --- /dev/null +++ b/osc_receiver.tscn @@ -0,0 +1,7 @@ +[gd_scene load_steps=2 format=3 uid="uid://bqci5bw1h0lh7"] + +[ext_resource type="Script" path="res://OSCReceiver.gd" id="1_ujyu5"] + +[node name="OSCReceiver" type="Node"] +script = ExtResource("1_ujyu5") +debug = true diff --git a/project.godot b/project.godot new file mode 100644 index 0000000..ba231f4 --- /dev/null +++ b/project.godot @@ -0,0 +1,28 @@ +; Engine configuration file. +; It's best edited using the editor UI and not directly, +; since the parameters that go here are not all obvious. +; +; Format: +; [section] ; section goes between [] +; param=value ; assign values to parameters + +config_version=5 + +[application] + +config/name="OSCReceiver Demo" +run/main_scene="res://UI.tscn" +config/features=PackedStringArray("4.1") + +[debug] + +settings/fps/force_fps=30 + +[display] + +window/size/viewport_width=300 +window/size/viewport_height=400 + +[rendering] + +environment/default_environment="res://default_env.tres"