Commit a5e2d032 authored by Bruno Laurencich's avatar Bruno Laurencich

receive operator

parent a9f0e4ea
......@@ -79,6 +79,9 @@ class Chord_Properties(bpy.types.AddonPreferences):
show_advanced = props.BoolProperty( name="Show advanced panel", default = False )
capture_quat_patt = props.StringProperty( name = "Capture OSC pattern",
description = "Regex pattern to filter bone quaternion data only", default = r"^/Chordata/(\w.*)" )
def target_update(self, context):
"""TODO: this function should block from selecting a non armature typed object """
pass
......@@ -89,8 +92,22 @@ class Chord_Scene_Properties(bpy.types.PropertyGroup):
target = bpy.props.PointerProperty(name = "Source armature object",
description="The Armature to display the capture",
type=bpy.types.Object)#, update=target_update
helpers = bpy.props.PointerProperty(name = "Sensor helpers parent",
description="An empty that have as children sensor helper objects",
type=bpy.types.Object)#, update=target_update
# ====== End of GLOBAL PROPERTIES =======
# =======================================
# = BONE PROPERTIES =
# =======================================
bpy.types.Bone.capture_bone = bpy.props.BoolProperty(name = "Capture Bone",
description = "Chordata will use this bone to place motion capture data", default = True)
# ====== End of BONE PROPERTIES =======
def register():
bpy.utils.register_class(Chord_Scene_Properties)
bpy.types.Scene.chordata = \
......
No preview for this file type
import bpy
import threading
from pythonosc import dispatcher
from pythonosc import osc_server
# from pythonosc import dispatcher
# from pythonosc import osc_server
import time, threading
from re import search as regex
from mathutils import *
......@@ -15,39 +15,49 @@ class Error(Exception):
def __init__(self, msg):
self.msg = msg
class Chordata:
def __init__(self):
self.object = None
bpy.ops.object.mode_set(mode="OBJECT")
self.message = " Chordata operator | Calibrating... Press AKEY to end "
for o in D.objects:
if o.type == "ARMATURE":
self.object = o
if self.object is None:
class Armature_Handler:
#TODO: create a global control for the visualization handling (hiding helpers or mesh)
def __init__(self, armature_object, helpers):
if not armature_object or armature_object.type != "ARMATURE":
raise Error("There's no Armature in the scene.")
self.object = armature_object
self.meshes = [ch for ch in armature_object.children if ch.type== "MESH"]
self.helpers = {ob.name: ob for ob in helpers.children }
self.pose = self.object.pose.bones
self.bones = self.object.data.bones
self.temp = {}
self.arm_repr = {}
print("Bones found in armature")
for b in self.bones:
if b.name == "no-capture-bone":
if not b.capture_bone:
continue
b.use_inherit_rotation = False
D.objects[b.name].rotation_mode = "QUATERNION"
has_helper = ""
try:
self.helpers[b.name].rotation_mode = "QUATERNION"
self.helpers[b.name].hide = False
has_helper = "Helper found"
except Exception:
pass
self.arm_repr[b.name] = {}
self.arm_repr[b.name]["chord_quat"] = Quaternion()
self.arm_repr[b.name]["avg_quat"] = Quaternion()
self.arm_repr[b.name]["diff_quat"] = False
self.arm_repr[b.name]["local_q"] = b.matrix_local.to_quaternion()
print(" [{}] {}".format(b.name, has_helper))
self.temp[b.name] = {}
self.temp[b.name]["chord_quat"] = Quaternion()
self.temp[b.name]["avg_quat"] = Quaternion()
self.temp[b.name]["diff_quat"] = False
self.temp[b.name]["local_q"] = b.matrix_local.to_quaternion()
for m in self.meshes: m.hide = True
print(" [{}]".format(b.name))
self.message = " Chordata operator | Calibrating... Press ENTER to start posing "
def __del__(self):
for m in self.meshes: m.hide = False
for key,h in self.helpers.items(): h.hide = True
def set_key(self, context):
......@@ -64,74 +74,71 @@ class Chordata:
b.rotation_quaternion.identity()
def put_quad_on_bones(self):
# for b in self.bones:
# self.pose[b.name].rotation_quaternion = b["chord_quat"]
# print(self.pose[b.name].rotation_quaternion)
#
for key, b in self.temp.items():
D.objects[key].rotation_quaternion = b['chord_quat']
if not b["diff_quat"]:
b["avg_quat"] = b['chord_quat'].slerp(b["avg_quat"], 0.5)
def put_quad_on_bones(self, bone_name, quat):
self.helpers[bone_name].rotation_quaternion = quat
b = self.arm_repr[bone_name]
else:
#this set the bone to the same rotation than the cube in world space
q = b["local_q"].conjugated() * b['chord_quat'].copy() * b["local_q"]
self.object.pose.bones[key].rotation_quaternion = q * b["diff_quat"].conjugated()
# for key, b in self.arm_repr.items():
if not b["diff_quat"]:
b["avg_quat"] = quat.slerp(b["avg_quat"], 0.5)
else:
#this sets the bone to the same rotation than the cube in world space
q = b["local_q"].conjugated() * quat.copy() * b["local_q"]
self.object.pose.bones[bone_name].rotation_quaternion = q * b["diff_quat"].conjugated()
def get_rot_diff(self):
for key, b in self.temp.items():
for m in self.meshes: m.hide = False
for key, b in self.arm_repr.items():
self.helpers[key].hide = True
q0 = b["local_q"].conjugated() * b['avg_quat'].copy() * b["local_q"]
b["diff_quat"] = self.object.pose.bones[key].rotation_quaternion.rotation_difference(q0)
self.message = " Chordata operator | posing... (Press Q, W, E to switch layers)"
self.message = " Chordata operator | Posing... Press ESC to end "
def start_server(self, IP = "", PORT = 6565):
"""
Start the OSC server on different thread and attach a handler to the OSC address "/quats"
"""
#create the dispatcher
self.dispatcher = dispatcher.Dispatcher()
#register a handler to get the recived values on the address /Chordata
self.dispatcher.map("/Chordata/.*", receive, self)
# def start_server(self, IP = "", PORT = 6565):
# """
# Start the OSC server on different thread and attach a handler to the OSC address "/quats"
# """
# #create the dispatcher
# self.dispatcher = dispatcher.Dispatcher()
# #register a handler to get the recived values on the address /Chordata
# self.dispatcher.map("/Chordata/.*", receive, self)
#Start evetything
self.server = osc_server.BlockingOSCUDPServer((IP, PORT), self.dispatcher)
self.st = threading.Thread( target = self.server.serve_forever,)
print("Serving on {}".format(self.server.server_address))
self.st.start()
def close_server(self):
print ("\nClosing OSCServer.")
self.server.shutdown()
print ("Waiting for Server-thread to finish")
self.st.join()
print ("Done")
def receive(addr, objs, *osc_vals):
sensor = regex("^/Chordata/(\w.*)", addr)
if not sensor or sensor.lastindex < 1:
print("Invalid address")
return
# print(osc_vals)
try:
for x in range(len(osc_vals)):
objs[0].temp[sensor.group(1)]["chord_quat"][x] = float(osc_vals[x])
# #Start evetything
# self.server = osc_server.BlockingOSCUDPServer((IP, PORT), self.dispatcher)
# self.st = threading.Thread( target = self.server.serve_forever,)
# print("Serving on {}".format(self.server.server_address))
# self.st.start()
# def close_server(self):
# print ("\nClosing OSCServer.")
# self.server.shutdown()
# print ("Waiting for Server-thread to finish")
# self.st.join()
# print ("Done")
# def receive(addr, objs, *osc_vals):
# sensor = regex("^/Chordata/(\w.*)", addr)
# if not sensor or sensor.lastindex < 1:
# print("Invalid address")
# return
# # print(osc_vals)
# try:
# for x in range(len(osc_vals)):
# objs[0].temp[sensor.group(1)]["chord_quat"][x] = float(osc_vals[x])
except IndexError as e:
print("An OSC lecture came with too much values")
return
except Exception as e:
print("There was an error while reading 0SC data")
print(e)
return
# except IndexError as e:
# print("An OSC lecture came with too much values")
# return
# except Exception as e:
# print("There was an error while reading 0SC data")
# print(e)
# return
......
......@@ -134,4 +134,17 @@ class Chord_advanced_panel(bpy.types.Panel):
layout.label("Notochord hostname")
layout.prop(self.chord_global, "notochord_hostname", text="")
target = context.scene.chordata.target
if target and target.mode == "POSE":
active_b = target.data.bones.active
if active_b:
box = layout.box()
box.label("Bone [%s]" % active_b.name)
box.prop(active_b, "capture_bone")
layout.label("Capture OSC pattern")
layout.prop(self.chord_global, "capture_quat_patt", text= "")
layout.label("Sensor helpers parent")
layout.prop(context.scene.chordata, "helpers", text="", icon="OUTLINER_DATA_EMPTY")
import bpy
from mathutils import Quaternion
from sys import path
from os.path import join, abspath, dirname
path.append(join(dirname(abspath(__file__)), "osc4py3-1.0.4"))
from osc4py3.as_comthreads import *
from osc4py3 import oscmethod as osm
from osc4py3 import oscchannel as osch
from re import compile
# from struct import pack as struct_pack
# import socket
if "u" in locals():
import imp
imp.reload(u)
imp.reload(arm)
else:
from . import utils as u
from . import armature as arm
D = bpy.data
......@@ -33,27 +38,32 @@ class Chord_Receive_OSC(bpy.types.Operator):
bl_label = "Chordata: Receive pose Data"
bl_options = {'REGISTER'}
def handlerfunction(self, addr, w, x, y, z):
print("{} ({:06.4f} {:06.4f} {:06.4f} {:06.4f})".format(addr, w, x, y, z))
D.objects["Cube"].rotation_quaternion.w = w
D.objects["Cube"].rotation_quaternion.x = x
D.objects["Cube"].rotation_quaternion.y = y
D.objects["Cube"].rotation_quaternion.z = z
def text(self, text = None):
if text:
u.write_blender_console(self.context, text)
for a in self.views_3d:
a.header_text_set(text)
else:
for a in self.views_3d:
a.header_text_set()
# ----------- MODAL -----------
def modal(self, context, event):
if event.type in {'RIGHTMOUSE', 'ESC'} or not self.chord_global.receiving:
if event.type == 'TIMER':
osc_process()
return {'PASS_THROUGH'}
elif event.type == 'RET' and event.value == "RELEASE":
self.armature.get_rot_diff()
self.text(self.armature.message)
elif event.type in {'RIGHTMOUSE', 'ESC'} or not self.chord_global.receiving:
if self.chord_global.manage_notochord:
bpy.ops.chordata.close_notochord("EXEC_DEFAULT")
self.report({"WARNING"}, "Connection canceled")
self.cancel(context)
return {'CANCELLED'}
if event.type == 'TIMER':
osc_process()
return {'PASS_THROUGH'}
# ----------- INVOKE -----------
......@@ -69,9 +79,22 @@ class Chord_Receive_OSC(bpy.types.Operator):
# ----------- EXECUTE -----------
def execute(self, context):
self.context = context
self.views_3d = [area for area in bpy.context.screen.areas if area.type == 'VIEW_3D' ]
if not context.scene.chordata.target:
self.report({"ERROR"}, "No armature object selected")
return {"CANCELLED"}
chord_scene = context.scene.chordata
self.armature = arm.Armature_Handler(chord_scene.target, chord_scene.helpers)
self.armature.reset_pose()
self.text(self.armature.message)
self.chord_global = context.user_preferences.addons[__package__].preferences
self.chord_global.receiving = True
self.chord_regex = compile(self.chord_global.capture_quat_patt)
wm = context.window_manager
self._timer = wm.event_timer_add( \
......@@ -79,6 +102,13 @@ class Chord_Receive_OSC(bpy.types.Operator):
context.window)
wm.modal_handler_add(self)
if not self.start_server(): return {"CANCELLED"}
return {'RUNNING_MODAL'}
# ----------- SERVER -----------
def start_server(self):
osc_startup()
server = u.transmition_methods(False)[self.chord_global.trans_method][2]
......@@ -93,28 +123,47 @@ class Chord_Receive_OSC(bpy.types.Operator):
except Exception as e:
u.write_blender_console(context, str(e))
self.report({"ERROR"}, "There was a problem creating the UDP server")
return False
### This was included directly in the library code:
# udpsock = osch.get_channel("chordata_armature_server").udpsock
# mreq = struct_pack("4sl",
# socket.inet_aton("239.0.0.1"),
# socket.INADDR_ANY)
# udpsock.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, mreq)
###
osc_method("*", self.handlerfunction,\
argscheme=osm.OSCARG_ADDRESS + osm.OSCARG_DATAUNPACK)
#"/Chordata/.*"
osc_method("*", self.receive_bones,\
argscheme=osm.OSCARG_ADDRESS + osm.OSCARG_DATA)
self.report({"INFO"}, "Receiving OSC")
return True
def receive_bones(self, addr, values):
sensor = self.chord_regex.search(addr)
if not sensor or sensor.lastindex < 1:
print("Invalid address", addr)
return
return {'RUNNING_MODAL'}
self.armature.put_quad_on_bones(sensor.group(1), Quaternion(values))
# print("{} ({:06.4f} {:06.4f} {:06.4f} {:06.4f})".format(addr, w, x, y, z))
# D.objects["Cube"].rotation_quaternion.w = values[0]
# D.objects["Cube"].rotation_quaternion.x = values[1]
# D.objects["Cube"].rotation_quaternion.y = values[2]
# D.objects["Cube"].rotation_quaternion.z = values[3]
# ----------- CANCEL -----------
def cancel(self, context):
del self.armature
wm = context.window_manager
wm.event_timer_remove(self._timer)
osch.get_channel("chordata_armature_server").terminate()
self.chord_global.receiving = False
self.text(None)
osc_terminate()
......@@ -19,8 +19,6 @@ def get_local_ip():
s.close()
return IP
def get_chordata_armature():
for ob in bpy.data.objects:
if ob.name.lower() == "chordata":
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment