mirror of
https://github.com/danbulant/ambientlightdemo
synced 2026-05-19 04:18:32 +00:00
start work on rpi version
This commit is contained in:
parent
ddc7aa1045
commit
0ce0445426
6 changed files with 160 additions and 24 deletions
15
lights.gd
15
lights.gd
|
|
@ -3,6 +3,7 @@ class_name Lights
|
||||||
|
|
||||||
@export var light_count: int = 1
|
@export var light_count: int = 1
|
||||||
@export var light_radius: float = 5.0
|
@export var light_radius: float = 5.0
|
||||||
|
@export var light_slowdown_speed: float = 1.0
|
||||||
@export var light_template: PackedScene
|
@export var light_template: PackedScene
|
||||||
@export var pattern_repetition: int = 5
|
@export var pattern_repetition: int = 5
|
||||||
|
|
||||||
|
|
@ -19,21 +20,19 @@ func _ready() -> void:
|
||||||
light_instance.position = Vector3(x, 0, z)
|
light_instance.position = Vector3(x, 0, z)
|
||||||
add_child(light_instance)
|
add_child(light_instance)
|
||||||
|
|
||||||
const BASE_DELTA: float = 16
|
|
||||||
|
|
||||||
var virtual_rotation: float = 0.0
|
var virtual_rotation: float = 0.0
|
||||||
|
|
||||||
const FULL_DARK_ROTATION_DELTA := .1
|
const FULL_DARK_ROTATION_DELTA := .1
|
||||||
|
|
||||||
func _process(delta: float) -> void:
|
func _process(delta: float) -> void:
|
||||||
var frames_elapsed = delta / BASE_DELTA
|
virtual_rotation += expected_rotation_delta * (delta * 60)
|
||||||
virtual_rotation += expected_rotation_delta
|
var max_darkness := clampf(abs(expected_rotation_delta / FULL_DARK_ROTATION_DELTA), 0, 1)
|
||||||
var max_darkness = expected_rotation_delta / FULL_DARK_ROTATION_DELTA
|
|
||||||
var i := 0
|
var i := 0
|
||||||
for light in get_children():
|
for light in get_children():
|
||||||
if light is SpotLight3D:
|
if light is SpotLight3D:
|
||||||
var pattern_offset := ((i + int(virtual_rotation * pattern_repetition)) % pattern_repetition) / float(pattern_repetition)
|
var pattern_offset := wrapf((i + (virtual_rotation * pattern_repetition)) / pattern_repetition, 0, 1)
|
||||||
light.light_color = light_color
|
light.light_color = light_color
|
||||||
light.light_energy = 1 - (1 - pattern_offset) * max_darkness
|
light.light_energy = (1 - (1 - pattern_offset) * max_darkness) * light_intensity
|
||||||
i += 1
|
i += 1
|
||||||
expected_rotation_delta = lerp(expected_rotation_delta, 0.0, frames_elapsed * 5)
|
expected_rotation_delta = lerp(expected_rotation_delta, 0.0, delta * light_slowdown_speed)
|
||||||
47
main.gd
47
main.gd
|
|
@ -2,18 +2,33 @@ extends Node3D
|
||||||
|
|
||||||
@onready var lights: Lights = %Lights
|
@onready var lights: Lights = %Lights
|
||||||
@onready var slider: HSlider = %HSlider
|
@onready var slider: HSlider = %HSlider
|
||||||
@onready var plane: MeshInstance3D = %Plane
|
@onready var networked_lights: Lights = %NetworkedLights
|
||||||
|
|
||||||
@export var light_rotation: float = 0.0
|
@export var light_rotation: float = 0.0
|
||||||
@export var color_gradient: Gradient
|
@export var color_gradient: Gradient
|
||||||
|
|
||||||
|
var peer: PacketPeerUDP
|
||||||
|
|
||||||
func _ready():
|
func _ready():
|
||||||
slider.value_changed.connect(set_light_rotation)
|
slider.value_changed.connect(set_local_light_rotation)
|
||||||
|
peer = PacketPeerUDP.new()
|
||||||
|
peer.bind(4433)
|
||||||
|
peer.set_dest_address("rpi", 4444)
|
||||||
|
|
||||||
|
|
||||||
|
func send_data(data: Dictionary) -> void:
|
||||||
|
var json_data := JSON.stringify(data)
|
||||||
|
var byte_array := json_data.to_utf8_buffer()
|
||||||
|
peer.put_packet(byte_array)
|
||||||
|
|
||||||
var error_correction := 0.
|
var error_correction := 0.
|
||||||
|
|
||||||
func set_light_rotation(value: float) -> void:
|
func set_local_light_rotation(value: float) -> void:
|
||||||
value = wrapf(value, 0, 1)
|
value = wrapf(value, 0, 1)
|
||||||
|
var polarized_value := pingpong(value * 4, 1)
|
||||||
|
lights.light_intensity = 1.0 - polarized_value
|
||||||
|
lights.light_color = color_gradient.sample(value)
|
||||||
|
slider.value = value
|
||||||
|
|
||||||
# get shortest diff including wrapping
|
# get shortest diff including wrapping
|
||||||
var diff = light_rotation - value
|
var diff = light_rotation - value
|
||||||
|
|
@ -25,12 +40,26 @@ func set_light_rotation(value: float) -> void:
|
||||||
diff = wrapped_diff
|
diff = wrapped_diff
|
||||||
|
|
||||||
# ignore small movements until a bigger one occurs or until they accumulate enough, to avoid reseting the light movement
|
# ignore small movements until a bigger one occurs or until they accumulate enough, to avoid reseting the light movement
|
||||||
# diff += error_correction
|
diff += error_correction
|
||||||
# if diff < .05:
|
if abs(diff) < .05:
|
||||||
# error_correction = diff
|
error_correction = diff
|
||||||
# return
|
send_data({"value": value})
|
||||||
|
return
|
||||||
|
|
||||||
lights.expected_rotation_delta = diff
|
lights.expected_rotation_delta = diff
|
||||||
light_rotation = value
|
light_rotation = value
|
||||||
lights.light_color = color_gradient.sample(value)
|
send_data({"value": value, "delta": diff})
|
||||||
slider.value = value
|
|
||||||
|
func _process(_delta):
|
||||||
|
if peer.get_available_packet_count() > 0:
|
||||||
|
var array_bytes = peer.get_packet()
|
||||||
|
var packet_string = array_bytes.get_string_from_ascii()
|
||||||
|
var json = JSON.parse_string(packet_string)
|
||||||
|
if json != null:
|
||||||
|
var value = json.value
|
||||||
|
var delta = json.delta
|
||||||
|
networked_lights.expected_rotation_delta = delta
|
||||||
|
|
||||||
|
var polarized_value := pingpong(value * 4, 1)
|
||||||
|
networked_lights.light_intensity = 1.0 - polarized_value
|
||||||
|
networked_lights.light_color = color_gradient.sample(value)
|
||||||
31
main.tscn
31
main.tscn
|
|
@ -24,6 +24,7 @@ sky_material = SubResource("ProceduralSkyMaterial_7dm0k")
|
||||||
[sub_resource type="Environment" id="Environment_0xm2m"]
|
[sub_resource type="Environment" id="Environment_0xm2m"]
|
||||||
background_mode = 2
|
background_mode = 2
|
||||||
sky = SubResource("Sky_ig7tw")
|
sky = SubResource("Sky_ig7tw")
|
||||||
|
glow_enabled = true
|
||||||
|
|
||||||
[node name="Node3D" type="Node3D"]
|
[node name="Node3D" type="Node3D"]
|
||||||
script = ExtResource("1_h2yge")
|
script = ExtResource("1_h2yge")
|
||||||
|
|
@ -31,16 +32,11 @@ 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")
|
||||||
|
|
||||||
[node name="WorldEnvironment" type="WorldEnvironment" parent="."]
|
[node name="Lights" type="Marker3D" parent="Plane"]
|
||||||
environment = SubResource("Environment_0xm2m")
|
|
||||||
|
|
||||||
[node name="Camera3D" type="Camera3D" parent="."]
|
|
||||||
transform = Transform3D(1, 0, 0, 0, -4.371139e-08, 1, 0, -1, -4.371139e-08, 0, 2, 0)
|
|
||||||
|
|
||||||
[node name="Lights" type="Marker3D" parent="."]
|
|
||||||
unique_name_in_owner = true
|
unique_name_in_owner = true
|
||||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.5, 0)
|
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.5, 0)
|
||||||
gizmo_extents = 0.7
|
gizmo_extents = 0.7
|
||||||
|
|
@ -49,6 +45,27 @@ 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="."]
|
||||||
|
environment = SubResource("Environment_0xm2m")
|
||||||
|
|
||||||
|
[node name="Camera3D" type="Camera3D" parent="."]
|
||||||
|
transform = Transform3D(1, 0, 0, 0, -4.371139e-08, 1, 0, -1, -4.371139e-08, 0, 2, 0)
|
||||||
|
|
||||||
[node name="HSlider" type="HSlider" parent="."]
|
[node name="HSlider" type="HSlider" parent="."]
|
||||||
unique_name_in_owner = true
|
unique_name_in_owner = true
|
||||||
anchors_preset = 7
|
anchors_preset = 7
|
||||||
|
|
|
||||||
|
|
@ -14,3 +14,8 @@ config/name="ambientlightdemo"
|
||||||
run/main_scene="uid://bfysabo18nycq"
|
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"
|
||||||
|
|
||||||
|
[rendering]
|
||||||
|
|
||||||
|
anti_aliasing/quality/msaa_2d=2
|
||||||
|
anti_aliasing/quality/msaa_3d=2
|
||||||
|
|
|
||||||
1
rpi/README.md
Normal file
1
rpi/README.md
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
https://learn.adafruit.com/neopixels-on-raspberry-pi/python-usage
|
||||||
85
rpi/lights.py
Normal file
85
rpi/lights.py
Normal file
|
|
@ -0,0 +1,85 @@
|
||||||
|
import socket
|
||||||
|
import time
|
||||||
|
import board
|
||||||
|
import neopixel
|
||||||
|
import json
|
||||||
|
|
||||||
|
num_pixels = 30
|
||||||
|
local_pixels = neopixel.NeoPixel(
|
||||||
|
board.D18, num_pixels, brightness=0.2, auto_write=False, pixel_order=neopixel.GRB
|
||||||
|
)
|
||||||
|
networked_pixels = neopixel.NeoPixel(
|
||||||
|
board.D21, num_pixels, brightness=0.2, auto_write=False, pixel_order=neopixel.GRB
|
||||||
|
)
|
||||||
|
|
||||||
|
colors = ["d062ff", "00b097", "50cc00", "8dcaff", "d062ff"]
|
||||||
|
colors_rgb = [tuple(int(colors[i][j:j+2], 16) for j in (0, 2, 4)) for i in range(len(colors))]
|
||||||
|
color_offsets = [0, .25, .5, .75, 1]
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
FULL_DARK_ROTATION_DELTA = .1
|
||||||
|
PATTERN_REPETITION = 5
|
||||||
|
LIGHT_SLOWDOWN_SPEED = 1
|
||||||
|
|
||||||
|
def clamp(value, min_value, max_value):
|
||||||
|
return max(min_value, min(value, max_value))
|
||||||
|
def wrap(value, min_value, max_value):
|
||||||
|
range_size = max_value - min_value
|
||||||
|
while value < min_value:
|
||||||
|
value += range_size
|
||||||
|
while value >= max_value:
|
||||||
|
value -= range_size
|
||||||
|
return value
|
||||||
|
def lerp(a, b, t):
|
||||||
|
return a + (b - a) * t
|
||||||
|
def pingpong(value, max):
|
||||||
|
value = wrap(value, 0, max * 2)
|
||||||
|
if value > max:
|
||||||
|
value = max * 2 - value
|
||||||
|
return value
|
||||||
|
def sample_color_gradient(t):
|
||||||
|
t = wrap(t, 0, 1)
|
||||||
|
for i in range(len(color_offsets) - 1):
|
||||||
|
if t >= color_offsets[i] and t <= color_offsets[i + 1]:
|
||||||
|
local_t = (t - color_offsets[i]) / (color_offsets[i + 1] - color_offsets[i])
|
||||||
|
color_a = colors_rgb[i]
|
||||||
|
color_b = colors_rgb[i + 1]
|
||||||
|
return tuple(int(lerp(color_a[j], color_b[j], local_t)) for j in range(3))
|
||||||
|
return colors_rgb[-1]
|
||||||
|
def send_data(value, delta):
|
||||||
|
message = json.dumps({"value": value, "delta": delta}).encode('utf-8')
|
||||||
|
sock.sendto(message, (UDP_HOST, UDP_PORT_NETWORKED))
|
||||||
|
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
|
||||||
|
|
||||||
|
class Lights:
|
||||||
|
virtual_rotation = 0
|
||||||
|
color = (255, 255, 255)
|
||||||
|
expected_rotation_delta = 0
|
||||||
|
def __init__(self, pixels):
|
||||||
|
self.pixels = pixels
|
||||||
|
|
||||||
|
def process(self, delta):
|
||||||
|
self.virtual_rotation += self.expected_rotation_delta * (delta * 60)
|
||||||
|
max_darkness = clamp(abs(self.expected_rotation_delta / FULL_DARK_ROTATION_DELTA), 0, 1)
|
||||||
|
for i in range(num_pixels):
|
||||||
|
pattern_offset = wrap((i + (self.virtual_rotation * PATTERN_REPETITION)) / PATTERN_REPETITION, 0, 1)
|
||||||
|
energy = (1 - (1 - pattern_offset) * max_darkness)
|
||||||
|
new_color = tuple(x * energy for x in self.color)
|
||||||
|
self.pixels[i] = new_color
|
||||||
|
self.pixels.show()
|
||||||
|
self.expected_rotation_delta = lerp(self.expected_rotation_delta, 0, delta * LIGHT_SLOWDOWN_SPEED)
|
||||||
Loading…
Reference in a new issue