Add blender simulation
This commit is contained in:
parent
659d9dccbe
commit
4c7faca61b
1 changed files with 346 additions and 0 deletions
346
simulate.py
Executable file
346
simulate.py
Executable file
|
@ -0,0 +1,346 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
# Ugly, but working:
|
||||
#
|
||||
# 1. Start blender
|
||||
# 2. Import your light strip with one vertex per LED, ensure vertex index matches LED index
|
||||
# 3. Load this script in blender and run it
|
||||
# 4. Start animation
|
||||
# 5. Switch to material preview or rendered view
|
||||
# 6. Broadcast your WLED DNRGB updates to the config below
|
||||
|
||||
#FIXME: This might need a bit of https://b3d.interplanety.org/en/accessing-custom-attributes-created-in-geometry-nodes/ to get the input indices
|
||||
|
||||
HOST = "localhost"
|
||||
PORT = 21324
|
||||
|
||||
#import LeitLightLightLeitLeidLite as l6
|
||||
|
||||
import bpy
|
||||
import mathutils
|
||||
|
||||
import socket
|
||||
|
||||
|
||||
|
||||
obj = bpy.data.objects['strips']
|
||||
|
||||
if True:
|
||||
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||
sock.bind((HOST, PORT))
|
||||
sock.setblocking(False)
|
||||
|
||||
ledCount = len(obj.data.vertices)
|
||||
|
||||
print("%d LEDs" % ledCount)
|
||||
state = bytearray([0x00,0x00,0x00] * ledCount)
|
||||
durations = bytearray([0x00] * ledCount)
|
||||
|
||||
|
||||
mesh = obj.data #bpy.context.collection.objects["strips"].data
|
||||
def setAttrib(attribName, data):
|
||||
if attribName in mesh.attributes:
|
||||
attribute = mesh.attributes[attribName]
|
||||
else:
|
||||
attribute = mesh.attributes.new(name=attribName, type="FLOAT", domain="POINT")
|
||||
#attribute_values = [i for i in range(len(mesh.vertices))]
|
||||
attribute.data.foreach_set("value", data)
|
||||
|
||||
# Created using https://extensions.blender.org/add-ons/node-to-python/
|
||||
def createMaterial(materialName):
|
||||
|
||||
mat = bpy.data.materials.new(name = materialName)
|
||||
mat.use_nodes = True
|
||||
|
||||
material = mat.node_tree
|
||||
#start with a clean node tree
|
||||
for node in material.nodes:
|
||||
material.nodes.remove(node)
|
||||
material.color_tag = 'NONE'
|
||||
material.description = ""
|
||||
material.default_group_node_width = 140
|
||||
|
||||
#node Material Output
|
||||
material_output = material.nodes.new("ShaderNodeOutputMaterial")
|
||||
material_output.name = "Material Output"
|
||||
material_output.is_active_output = True
|
||||
material_output.target = 'ALL'
|
||||
#Displacement
|
||||
material_output.inputs[2].default_value = (0.0, 0.0, 0.0)
|
||||
#Thickness
|
||||
material_output.inputs[3].default_value = 0.0
|
||||
|
||||
#node Emission
|
||||
emission = material.nodes.new("ShaderNodeEmission")
|
||||
emission.name = "Emission"
|
||||
#Color
|
||||
emission.inputs[0].default_value = (1.0, 0.0, 0.018193421885371208, 1.0)
|
||||
|
||||
#node Add Shader
|
||||
add_shader = material.nodes.new("ShaderNodeAddShader")
|
||||
add_shader.name = "Add Shader"
|
||||
|
||||
#node Emission.001
|
||||
emission_001 = material.nodes.new("ShaderNodeEmission")
|
||||
emission_001.name = "Emission.001"
|
||||
#Color
|
||||
emission_001.inputs[0].default_value = (0.021969836205244064, 1.0, 0.0, 1.0)
|
||||
|
||||
#node Add Shader.001
|
||||
add_shader_001 = material.nodes.new("ShaderNodeAddShader")
|
||||
add_shader_001.name = "Add Shader.001"
|
||||
|
||||
#node Emission.002
|
||||
emission_002 = material.nodes.new("ShaderNodeEmission")
|
||||
emission_002.name = "Emission.002"
|
||||
#Color
|
||||
emission_002.inputs[0].default_value = (0.0, 0.0016676115337759256, 1.0, 1.0)
|
||||
|
||||
#node Attribute
|
||||
attribute = material.nodes.new("ShaderNodeAttribute")
|
||||
attribute.name = "Attribute"
|
||||
attribute.attribute_name = "red"
|
||||
attribute.attribute_type = 'GEOMETRY'
|
||||
|
||||
#node Attribute.001
|
||||
attribute_001 = material.nodes.new("ShaderNodeAttribute")
|
||||
attribute_001.name = "Attribute.001"
|
||||
attribute_001.attribute_name = "green"
|
||||
attribute_001.attribute_type = 'GEOMETRY'
|
||||
|
||||
#node Attribute.002
|
||||
attribute_002 = material.nodes.new("ShaderNodeAttribute")
|
||||
attribute_002.name = "Attribute.002"
|
||||
attribute_002.attribute_name = "blue"
|
||||
attribute_002.attribute_type = 'GEOMETRY'
|
||||
|
||||
|
||||
#Set locations
|
||||
material_output.location = (300.0, 300.0)
|
||||
emission.location = (-90.9997329711914, 320.24908447265625)
|
||||
add_shader.location = (119.9999771118164, 290.62481689453125)
|
||||
emission_001.location = (-95.54425048828125, 215.4246063232422)
|
||||
add_shader_001.location = (115.45545959472656, 163.01229858398438)
|
||||
emission_002.location = (-100.0887680053711, 87.81208801269531)
|
||||
attribute.location = (-358.0585021972656, 399.44940185546875)
|
||||
attribute_001.location = (-350.34014892578125, 246.54885864257812)
|
||||
attribute_002.location = (-354.8846740722656, 114.37872314453125)
|
||||
|
||||
#Set dimensions
|
||||
material_output.width, material_output.height = 140.0, 100.0
|
||||
emission.width, emission.height = 140.0, 100.0
|
||||
add_shader.width, add_shader.height = 140.0, 100.0
|
||||
emission_001.width, emission_001.height = 140.0, 100.0
|
||||
add_shader_001.width, add_shader_001.height = 140.0, 100.0
|
||||
emission_002.width, emission_002.height = 140.0, 100.0
|
||||
attribute.width, attribute.height = 140.0, 100.0
|
||||
attribute_001.width, attribute_001.height = 140.0, 100.0
|
||||
attribute_002.width, attribute_002.height = 140.0, 100.0
|
||||
|
||||
#emission.Emission -> add_shader.Shader
|
||||
material.links.new(emission.outputs[0], add_shader.inputs[0])
|
||||
#emission_001.Emission -> add_shader.Shader
|
||||
material.links.new(emission_001.outputs[0], add_shader.inputs[1])
|
||||
#emission_002.Emission -> add_shader_001.Shader
|
||||
material.links.new(emission_002.outputs[0], add_shader_001.inputs[1])
|
||||
#add_shader.Shader -> add_shader_001.Shader
|
||||
material.links.new(add_shader.outputs[0], add_shader_001.inputs[0])
|
||||
#attribute.Fac -> emission.Strength
|
||||
material.links.new(attribute.outputs[2], emission.inputs[1])
|
||||
#attribute_002.Fac -> emission_002.Strength
|
||||
material.links.new(attribute_002.outputs[2], emission_002.inputs[1])
|
||||
#attribute_001.Fac -> emission_001.Strength
|
||||
material.links.new(attribute_001.outputs[2], emission_001.inputs[1])
|
||||
#add_shader_001.Shader -> material_output.Surface
|
||||
material.links.new(add_shader_001.outputs[0], material_output.inputs[0])
|
||||
return mat
|
||||
|
||||
|
||||
# Created using https://extensions.blender.org/add-ons/node-to-python/
|
||||
def createGeoNodes(geoNodeName, material):
|
||||
geometry_nodes = bpy.data.node_groups.new(type = 'GeometryNodeTree', name = geoNodeName)
|
||||
|
||||
geometry_nodes.color_tag = 'NONE'
|
||||
geometry_nodes.description = ""
|
||||
geometry_nodes.default_group_node_width = 140
|
||||
|
||||
|
||||
geometry_nodes.is_modifier = True
|
||||
|
||||
#geometry_nodes interface
|
||||
#Socket Geometry
|
||||
geometry_socket = geometry_nodes.interface.new_socket(name = "Geometry", in_out='OUTPUT', socket_type = 'NodeSocketGeometry')
|
||||
geometry_socket.attribute_domain = 'POINT'
|
||||
|
||||
#Socket Geometry
|
||||
geometry_socket_1 = geometry_nodes.interface.new_socket(name = "Geometry", in_out='INPUT', socket_type = 'NodeSocketGeometry')
|
||||
geometry_socket_1.attribute_domain = 'POINT'
|
||||
|
||||
|
||||
#initialize geometry_nodes nodes
|
||||
#node Group Input
|
||||
group_input = geometry_nodes.nodes.new("NodeGroupInput")
|
||||
group_input.name = "Group Input"
|
||||
|
||||
#node Group Output
|
||||
group_output = geometry_nodes.nodes.new("NodeGroupOutput")
|
||||
group_output.name = "Group Output"
|
||||
group_output.is_active_output = True
|
||||
|
||||
#node Instance on Points
|
||||
instance_on_points = geometry_nodes.nodes.new("GeometryNodeInstanceOnPoints")
|
||||
instance_on_points.name = "Instance on Points"
|
||||
#Selection
|
||||
instance_on_points.inputs[1].default_value = True
|
||||
#Pick Instance
|
||||
instance_on_points.inputs[3].default_value = False
|
||||
#Instance Index
|
||||
instance_on_points.inputs[4].default_value = 0
|
||||
#Rotation
|
||||
instance_on_points.inputs[5].default_value = (0.0, 0.0, 0.0)
|
||||
#Scale
|
||||
instance_on_points.inputs[6].default_value = (1.0, 1.0, 1.0)
|
||||
|
||||
#node Ico Sphere
|
||||
ico_sphere = geometry_nodes.nodes.new("GeometryNodeMeshIcoSphere")
|
||||
ico_sphere.name = "Ico Sphere"
|
||||
#Radius
|
||||
ico_sphere.inputs[0].default_value = 1.0
|
||||
#Subdivisions
|
||||
ico_sphere.inputs[1].default_value = 1
|
||||
|
||||
#node Store Named Attribute
|
||||
store_named_attribute = geometry_nodes.nodes.new("GeometryNodeStoreNamedAttribute")
|
||||
store_named_attribute.name = "Store Named Attribute"
|
||||
store_named_attribute.data_type = 'INT'
|
||||
store_named_attribute.domain = 'POINT'
|
||||
#Selection
|
||||
store_named_attribute.inputs[1].default_value = True
|
||||
#Name
|
||||
store_named_attribute.inputs[2].default_value = "index"
|
||||
|
||||
#node Index
|
||||
index = geometry_nodes.nodes.new("GeometryNodeInputIndex")
|
||||
index.name = "Index"
|
||||
|
||||
#node Realize Instances
|
||||
realize_instances = geometry_nodes.nodes.new("GeometryNodeRealizeInstances")
|
||||
realize_instances.name = "Realize Instances"
|
||||
#Selection
|
||||
realize_instances.inputs[1].default_value = True
|
||||
#Realize All
|
||||
realize_instances.inputs[2].default_value = True
|
||||
#Depth
|
||||
realize_instances.inputs[3].default_value = 0
|
||||
|
||||
#node Capture Attribute
|
||||
capture_attribute = geometry_nodes.nodes.new("GeometryNodeCaptureAttribute")
|
||||
capture_attribute.name = "Capture Attribute"
|
||||
capture_attribute.active_index = 0
|
||||
capture_attribute.capture_items.clear()
|
||||
capture_attribute.capture_items.new('FLOAT', "Index")
|
||||
capture_attribute.capture_items["Index"].data_type = 'INT'
|
||||
capture_attribute.domain = 'POINT'
|
||||
|
||||
#node Set Material
|
||||
set_material = geometry_nodes.nodes.new("GeometryNodeSetMaterial")
|
||||
set_material.name = "Set Material"
|
||||
#Selection
|
||||
set_material.inputs[1].default_value = True
|
||||
set_material.inputs[2].default_value = material
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#Set locations
|
||||
group_input.location = (-537.77783203125, 192.87576293945312)
|
||||
group_output.location = (1020.145263671875, 338.443115234375)
|
||||
instance_on_points.location = (-40.82560348510742, 80.0461654663086)
|
||||
ico_sphere.location = (-380.4179382324219, -85.53570556640625)
|
||||
store_named_attribute.location = (503.62579345703125, 345.2210388183594)
|
||||
index.location = (-583.5319213867188, 81.38147735595703)
|
||||
realize_instances.location = (235.0821075439453, 147.38453674316406)
|
||||
capture_attribute.location = (-276.59991455078125, 312.9664306640625)
|
||||
set_material.location = (815.2620239257812, 334.15179443359375)
|
||||
|
||||
#Set dimensions
|
||||
group_input.width, group_input.height = 140.0, 100.0
|
||||
group_output.width, group_output.height = 140.0, 100.0
|
||||
instance_on_points.width, instance_on_points.height = 140.0, 100.0
|
||||
ico_sphere.width, ico_sphere.height = 140.0, 100.0
|
||||
store_named_attribute.width, store_named_attribute.height = 140.0, 100.0
|
||||
index.width, index.height = 140.0, 100.0
|
||||
realize_instances.width, realize_instances.height = 140.0, 100.0
|
||||
capture_attribute.width, capture_attribute.height = 140.0, 100.0
|
||||
set_material.width, set_material.height = 140.0, 100.0
|
||||
|
||||
#initialize geometry_nodes links
|
||||
#ico_sphere.Mesh -> instance_on_points.Instance
|
||||
geometry_nodes.links.new(ico_sphere.outputs[0], instance_on_points.inputs[2])
|
||||
#group_input.Geometry -> capture_attribute.Geometry
|
||||
geometry_nodes.links.new(group_input.outputs[0], capture_attribute.inputs[0])
|
||||
#capture_attribute.Geometry -> instance_on_points.Points
|
||||
geometry_nodes.links.new(capture_attribute.outputs[0], instance_on_points.inputs[0])
|
||||
#index.Index -> capture_attribute.Index
|
||||
geometry_nodes.links.new(index.outputs[0], capture_attribute.inputs[1])
|
||||
#capture_attribute.Index -> store_named_attribute.Value
|
||||
geometry_nodes.links.new(capture_attribute.outputs[1], store_named_attribute.inputs[3])
|
||||
#instance_on_points.Instances -> realize_instances.Geometry
|
||||
geometry_nodes.links.new(instance_on_points.outputs[0], realize_instances.inputs[0])
|
||||
#realize_instances.Geometry -> store_named_attribute.Geometry
|
||||
geometry_nodes.links.new(realize_instances.outputs[0], store_named_attribute.inputs[0])
|
||||
#store_named_attribute.Geometry -> set_material.Geometry
|
||||
geometry_nodes.links.new(store_named_attribute.outputs[0], set_material.inputs[0])
|
||||
#set_material.Geometry -> group_output.Geometry
|
||||
geometry_nodes.links.new(set_material.outputs[0], group_output.inputs[0])
|
||||
return geometry_nodes
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
def my_handler(scene):
|
||||
global state
|
||||
print("Frame Change", scene.frame_current)
|
||||
|
||||
|
||||
try:
|
||||
data, addr = sock.recvfrom(4096)
|
||||
#print("received message: %s" % data)
|
||||
|
||||
assert(data[0] == 4) # DNRGB
|
||||
duration = data[1]
|
||||
baseIndex = (data[2] << 8) | data[3]
|
||||
payloadLength = len(data) - 4
|
||||
assert(payloadLength % 3 == 0)
|
||||
count = payloadLength // 3
|
||||
newState = data[4:]
|
||||
|
||||
|
||||
beforeCount = baseIndex
|
||||
afterIndex = baseIndex + count
|
||||
state = state[0:beforeCount*3] + newState + state[afterIndex*3:]
|
||||
|
||||
except BlockingIOError:
|
||||
pass
|
||||
|
||||
|
||||
setAttrib('red', [x / 255.0 for x in state[0::3]])
|
||||
setAttrib('green', [x / 255.0 for x in state[1::3]])
|
||||
setAttrib('blue', [x / 255.0 for x in state[2::3]])
|
||||
|
||||
def regHandler(my_handler):
|
||||
for func in bpy.app.handlers.frame_change_post[:]:
|
||||
if func.__name__ == my_handler.__name__:
|
||||
bpy.app.handlers.frame_change_post.remove(func)
|
||||
bpy.app.handlers.frame_change_pre.append(my_handler)
|
||||
|
||||
material = createMaterial("l6-geometry-nodes")
|
||||
geometry_nodes = createGeoNodes("l6-material", material)
|
||||
|
||||
mod = obj.modifiers.new('l6-modifier', 'NODES')
|
||||
mod.node_group = geometry_nodes
|
||||
|
||||
regHandler(my_handler)
|
||||
|
Loading…
Add table
Reference in a new issue