mirror of
https://github.com/danbulant/ambientlightdemo
synced 2026-05-19 04:18:32 +00:00
progress on single shared light
This commit is contained in:
parent
10a1b6d9d8
commit
7e5bfe891c
8 changed files with 151 additions and 25 deletions
25
main.gd
25
main.gd
|
|
@ -2,18 +2,21 @@ extends Node3D
|
||||||
|
|
||||||
@onready var lights: Lights = %Lights
|
@onready var lights: Lights = %Lights
|
||||||
@onready var slider: HSlider = %HSlider
|
@onready var slider: HSlider = %HSlider
|
||||||
@onready var networked_lights: Lights = %NetworkedLights
|
|
||||||
|
|
||||||
@export var light_rotation: float = 0.0
|
@export var light_rotation: float = 0.0
|
||||||
|
@export var light_delta: float = 0.0
|
||||||
@export var color_gradient: Gradient
|
@export var color_gradient: Gradient
|
||||||
|
|
||||||
|
var remote_light_rotation: float = 0.0
|
||||||
|
var remote_light_delta: float = 0.0
|
||||||
|
|
||||||
var peer: PacketPeerUDP
|
var peer: PacketPeerUDP
|
||||||
|
|
||||||
func _ready():
|
func _ready():
|
||||||
slider.value_changed.connect(set_local_light_rotation)
|
slider.value_changed.connect(set_local_light_rotation)
|
||||||
peer = PacketPeerUDP.new()
|
peer = PacketPeerUDP.new()
|
||||||
peer.bind(4433)
|
peer.bind(4433)
|
||||||
peer.set_dest_address("rpi", 4444)
|
peer.set_dest_address("rpi1", 4444)
|
||||||
|
|
||||||
|
|
||||||
func send_data(data: Dictionary) -> void:
|
func send_data(data: Dictionary) -> void:
|
||||||
|
|
@ -46,11 +49,21 @@ func set_local_light_rotation(value: float) -> void:
|
||||||
send_data({"value": value})
|
send_data({"value": value})
|
||||||
return
|
return
|
||||||
|
|
||||||
|
error_correction = 0.
|
||||||
|
|
||||||
lights.expected_rotation_delta = diff
|
lights.expected_rotation_delta = diff
|
||||||
|
light_delta = diff
|
||||||
light_rotation = value
|
light_rotation = value
|
||||||
send_data({"value": value, "delta": diff})
|
send_data({"value": value, "delta": diff})
|
||||||
|
|
||||||
func _process(_delta):
|
func _process(_delta):
|
||||||
|
var joystick_x = Input.get_joy_axis(0, JOY_AXIS_LEFT_X)
|
||||||
|
var joystick_y = Input.get_joy_axis(0, JOY_AXIS_LEFT_Y)
|
||||||
|
var joystick = Vector2(joystick_x, joystick_y)
|
||||||
|
if joystick.length() > 0.2:
|
||||||
|
var angle = (atan2(joystick_y, joystick_x) / TAU) + 0.25
|
||||||
|
angle = wrapf(angle, 0, 1)
|
||||||
|
set_local_light_rotation(angle)
|
||||||
if peer.get_available_packet_count() > 0:
|
if peer.get_available_packet_count() > 0:
|
||||||
var array_bytes = peer.get_packet()
|
var array_bytes = peer.get_packet()
|
||||||
var packet_string = array_bytes.get_string_from_ascii()
|
var packet_string = array_bytes.get_string_from_ascii()
|
||||||
|
|
@ -58,8 +71,8 @@ func _process(_delta):
|
||||||
if json != null:
|
if json != null:
|
||||||
var value = json.value
|
var value = json.value
|
||||||
var delta = json.delta
|
var delta = json.delta
|
||||||
networked_lights.expected_rotation_delta = delta
|
# networked_lights.expected_rotation_delta = delta
|
||||||
|
|
||||||
var polarized_value := pingpong(value * 4, 1)
|
# var polarized_value := pingpong(value * 4, 1)
|
||||||
networked_lights.light_intensity = 1.0 - polarized_value
|
# networked_lights.light_intensity = 1.0 - polarized_value
|
||||||
networked_lights.light_color = color_gradient.sample(value)
|
# networked_lights.light_color = color_gradient.sample(value)
|
||||||
18
main.tscn
18
main.tscn
|
|
@ -32,7 +32,6 @@ color_gradient = SubResource("Gradient_h2yge")
|
||||||
|
|
||||||
[node name="Plane" type="MeshInstance3D" parent="."]
|
[node name="Plane" type="MeshInstance3D" parent="."]
|
||||||
unique_name_in_owner = true
|
unique_name_in_owner = true
|
||||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -1, 0, 0)
|
|
||||||
mesh = SubResource("CylinderMesh_0xm2m")
|
mesh = SubResource("CylinderMesh_0xm2m")
|
||||||
surface_material_override/0 = SubResource("StandardMaterial3D_7dm0k")
|
surface_material_override/0 = SubResource("StandardMaterial3D_7dm0k")
|
||||||
|
|
||||||
|
|
@ -45,26 +44,11 @@ light_count = 20
|
||||||
light_radius = 0.7
|
light_radius = 0.7
|
||||||
light_template = ExtResource("2_0xm2m")
|
light_template = ExtResource("2_0xm2m")
|
||||||
|
|
||||||
[node name="NetworkedPlane" type="MeshInstance3D" parent="."]
|
|
||||||
unique_name_in_owner = true
|
|
||||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0)
|
|
||||||
mesh = SubResource("CylinderMesh_0xm2m")
|
|
||||||
surface_material_override/0 = SubResource("StandardMaterial3D_7dm0k")
|
|
||||||
|
|
||||||
[node name="NetworkedLights" type="Marker3D" parent="NetworkedPlane"]
|
|
||||||
unique_name_in_owner = true
|
|
||||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.5, 0)
|
|
||||||
gizmo_extents = 0.7
|
|
||||||
script = ExtResource("1_ig7tw")
|
|
||||||
light_count = 20
|
|
||||||
light_radius = 0.7
|
|
||||||
light_template = ExtResource("2_0xm2m")
|
|
||||||
|
|
||||||
[node name="WorldEnvironment" type="WorldEnvironment" parent="."]
|
[node name="WorldEnvironment" type="WorldEnvironment" parent="."]
|
||||||
environment = SubResource("Environment_0xm2m")
|
environment = SubResource("Environment_0xm2m")
|
||||||
|
|
||||||
[node name="Camera3D" type="Camera3D" parent="."]
|
[node name="Camera3D" type="Camera3D" parent="."]
|
||||||
transform = Transform3D(1, 0, 0, 0, -4.371139e-08, 1, 0, -1, -4.371139e-08, 0, 2, 0)
|
transform = Transform3D(1, 0, 0, 0, -4.371139e-08, 1, 0, -1, -4.371139e-08, 0, 1.4683123, 0)
|
||||||
|
|
||||||
[node name="HSlider" type="HSlider" parent="."]
|
[node name="HSlider" type="HSlider" parent="."]
|
||||||
unique_name_in_owner = true
|
unique_name_in_owner = true
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,19 @@ run/main_scene="uid://bfysabo18nycq"
|
||||||
config/features=PackedStringArray("4.5", "Forward Plus")
|
config/features=PackedStringArray("4.5", "Forward Plus")
|
||||||
config/icon="res://icon.svg"
|
config/icon="res://icon.svg"
|
||||||
|
|
||||||
|
[input]
|
||||||
|
|
||||||
|
forward={
|
||||||
|
"deadzone": 0.2,
|
||||||
|
"events": [Object(InputEventJoypadMotion,"resource_local_to_scene":false,"resource_name":"","device":-1,"axis":1,"axis_value":-1.0,"script":null)
|
||||||
|
]
|
||||||
|
}
|
||||||
|
right={
|
||||||
|
"deadzone": 0.2,
|
||||||
|
"events": [Object(InputEventJoypadMotion,"resource_local_to_scene":false,"resource_name":"","device":-1,"axis":0,"axis_value":1.0,"script":null)
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
[rendering]
|
[rendering]
|
||||||
|
|
||||||
anti_aliasing/quality/msaa_2d=2
|
anti_aliasing/quality/msaa_2d=2
|
||||||
|
|
|
||||||
|
|
@ -30,4 +30,13 @@ def sample_color_gradient(t):
|
||||||
color_a = colors_rgb[i]
|
color_a = colors_rgb[i]
|
||||||
color_b = colors_rgb[i + 1]
|
color_b = colors_rgb[i + 1]
|
||||||
return tuple(int(lerp(color_a[j], color_b[j], local_t)) for j in range(3))
|
return tuple(int(lerp(color_a[j], color_b[j], local_t)) for j in range(3))
|
||||||
return colors_rgb[-1]
|
return colors_rgb[-1]
|
||||||
|
def shortest_diff(old, new):
|
||||||
|
diff = old - new
|
||||||
|
wrapped_diff = (new + 1) - old
|
||||||
|
if abs(wrapped_diff) < abs(diff):
|
||||||
|
diff = -wrapped_diff
|
||||||
|
wrapped_diff = (old + 1) - new
|
||||||
|
if abs(wrapped_diff) < abs(diff):
|
||||||
|
diff = wrapped_diff
|
||||||
|
return diff
|
||||||
|
|
@ -23,3 +23,5 @@ class Lights:
|
||||||
self.pixels[i] = new_color
|
self.pixels[i] = new_color
|
||||||
self.pixels.show()
|
self.pixels.show()
|
||||||
self.expected_rotation_delta = lerp(self.expected_rotation_delta, 0, delta * LIGHT_SLOWDOWN_SPEED)
|
self.expected_rotation_delta = lerp(self.expected_rotation_delta, 0, delta * LIGHT_SLOWDOWN_SPEED)
|
||||||
|
|
||||||
|
lights = Lights(pixels)
|
||||||
73
rpi/main.py
Normal file
73
rpi/main.py
Normal file
|
|
@ -0,0 +1,73 @@
|
||||||
|
import socket
|
||||||
|
import time
|
||||||
|
import json
|
||||||
|
from lights import lights
|
||||||
|
from stepper import stepper
|
||||||
|
import gdmath
|
||||||
|
from readangle import read_angle_f
|
||||||
|
import threading
|
||||||
|
|
||||||
|
UDP_HOST = "steamdeck"
|
||||||
|
UDP_PORT_LOCAL = 4444
|
||||||
|
UDP_PORT_NETWORKED = 4433
|
||||||
|
|
||||||
|
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||||
|
sock.bind(("", UDP_PORT_LOCAL))
|
||||||
|
sock.setblocking(0)
|
||||||
|
|
||||||
|
def send_json(data):
|
||||||
|
message = json.dumps(data).encode('utf-8')
|
||||||
|
sock.sendto(message, (UDP_HOST, UDP_PORT_NETWORKED))
|
||||||
|
|
||||||
|
def receive_json():
|
||||||
|
try:
|
||||||
|
data, _ = sock.recvfrom(1024)
|
||||||
|
return json.loads(data.decode('utf-8'))
|
||||||
|
except BlockingIOError:
|
||||||
|
return None
|
||||||
|
|
||||||
|
local_rotation = 0
|
||||||
|
local_delta = 0
|
||||||
|
remote_rotation = 0
|
||||||
|
remote_delta = 0
|
||||||
|
|
||||||
|
def read_thread_fn():
|
||||||
|
global local_rotation, local_delta
|
||||||
|
while True:
|
||||||
|
old_rotation = local_rotation
|
||||||
|
local_rotation = read_angle_f()
|
||||||
|
local_delta = gdmath.shortest_diff(old_rotation, local_rotation)
|
||||||
|
send_json({"value": local_rotation, "delta": local_delta})
|
||||||
|
|
||||||
|
def lights_fn():
|
||||||
|
global local_rotation, local_delta, remote_rotation, remote_delta
|
||||||
|
last_time = time.time()
|
||||||
|
delta = 1/60
|
||||||
|
while True:
|
||||||
|
lights.color = gdmath.sample_color_gradient(local_rotation + remote_rotation)
|
||||||
|
lights.expected_rotation_delta = local_delta
|
||||||
|
lights.process(delta)
|
||||||
|
current_time = time.time()
|
||||||
|
time.sleep(1/60)
|
||||||
|
delta = current_time - last_time
|
||||||
|
last_time = current_time
|
||||||
|
|
||||||
|
def network_recv_fn():
|
||||||
|
global remote_rotation, remote_delta
|
||||||
|
while True:
|
||||||
|
data = receive_json()
|
||||||
|
if data:
|
||||||
|
remote_rotation = data["value"]
|
||||||
|
remote_delta = data["delta"]
|
||||||
|
time.sleep(0.01)
|
||||||
|
|
||||||
|
read_thread = threading.Thread(target=read_thread_fn)
|
||||||
|
read_thread.start()
|
||||||
|
lights_thread = threading.Thread(target=lights_fn)
|
||||||
|
lights_thread.start()
|
||||||
|
network_thread = threading.Thread(target=network_recv_fn)
|
||||||
|
network_thread.start()
|
||||||
|
|
||||||
|
read_thread.join()
|
||||||
|
lights_thread.join()
|
||||||
|
network_thread.join()
|
||||||
14
rpi/readangle.py
Normal file
14
rpi/readangle.py
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
import smbus2
|
||||||
|
|
||||||
|
# Define I2C address and bus
|
||||||
|
AS5600_ADDR = 0x36
|
||||||
|
ANGLE_REG = 0x0E
|
||||||
|
|
||||||
|
bus = smbus2.SMBus(1)
|
||||||
|
|
||||||
|
def read_angle_f():
|
||||||
|
# Read two bytes from the angle register
|
||||||
|
raw_data = bus.read_i2c_block_data(AS5600_ADDR, ANGLE_REG, 2)
|
||||||
|
angle = (raw_data[0] << 8) | raw_data[1] # Combine MSB and LSB
|
||||||
|
angle = angle & 0x0FFF # Mask to 12 bits
|
||||||
|
return (angle / 4096.0) # convert to 0-1
|
||||||
|
|
@ -47,4 +47,22 @@ class StepperMotor:
|
||||||
else:
|
else:
|
||||||
self.single_step_back()
|
self.single_step_back()
|
||||||
def pos(self):
|
def pos(self):
|
||||||
return self.current_step % self.step_count
|
return self.current_step % self.step_count
|
||||||
|
def fpos(self):
|
||||||
|
return (self.current_step % self.step_count) / self.step_count
|
||||||
|
|
||||||
|
def single_step_towards(self, target_pos):
|
||||||
|
current_pos = self.pos()
|
||||||
|
# Determine shortest direction
|
||||||
|
# includes wrap-around (it's a circular motion motor)
|
||||||
|
diff = (target_pos - current_pos + self.step_count) % self.step_count
|
||||||
|
if diff > self.step_count / 2:
|
||||||
|
self.single_step_back()
|
||||||
|
else:
|
||||||
|
self.single_step()
|
||||||
|
def angle_to_pos(self, angle):
|
||||||
|
return int((angle % 360) / 360 * self.step_count)
|
||||||
|
def fpos_to_pos(self, fpos):
|
||||||
|
return int((fpos % 1) * self.step_count)
|
||||||
|
|
||||||
|
stepper = StepperMotor()
|
||||||
Loading…
Reference in a new issue