``` bl_info = { "name": "PoseCam", "description": "Real-time animation from PoseCam app", "author": "Cinesaibai", "blender": (3, 3, 1), "location": "3D View > Tools", "category": "Animation" } import time import json import socket import bpy import mathutils import select import threading import traceback import re from bpy.props import (StringProperty, BoolProperty, IntProperty, FloatProperty, FloatVectorProperty, EnumProperty, PointerProperty) from bpy.types import (Panel, Menu, Operator, PropertyGroup) running = False record = False #dfalseef getBonePropertyName(boneName): #return re.sub(r'_([a-z0-9])', lambda x: x.group(1).upper(), boneName).replace("Joint", "") PORT_NUM = 8080 HOSTNAME = socket.gethostname() IP = socket.gethostbyname(HOSTNAME) handshake = json.dumps( { 'HANDSHAKE': { 'name': 'Blender', # will be displayed in the app 'PF': 0, 'rig': 'UE4', # Options: 'UE4', 'Mixamo' 'mode': 'Desktop', # Options: 'Room', 'Desktop', 'Portrait', 'RoomBodyOnly', 'PortraitBodyOnly' 'mirror': 'YES', # Options: 'YES', 'NO' 'syncFPS': 60, 'cameraFPS': 60, # Tries to set camera to this speed. 30 and 60 work on most iphone cameras } } ).encode() class ServerThread(threading.Thread): def __init__(self): threading.Thread.__init__(self) self.running = True def resumeServer(self): self.running = True self.server.running = True def stopServer(self): self.running = False self.server.running = False def run(self): try: self.server = Server() while self.running: self.server.receive() except: pass class Server: def __init__(self): self.server_socket = None def receive(self): #while self.running: connections = {} self.server_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) self.server_socket.bind(('0.0.0.0', PORT_NUM)) message, address = self.server_socket.recvfrom(65304) message = message.decode('utf-8') decoded_message = json.loads(message) if 'sessionUUID' in decoded_message.keys(): # is initial connection, send it the handshake print(decoded_message, address) connections[decoded_message['sessionUUID']] = address self.server_socket.sendto(handshake, address) if 'Body' in decoded_message.keys() and 'Rotations' in decoded_message['Body'].keys(): head_rotations = decoded_message['Body']['Rotations']['head'] quaternion = mathutils.Quaternion(head_rotations) quaternion.w, quaternion.x, quaternion.y, quaternion.z = quaternion[3], quaternion[0], quaternion[1], quaternion[2] bpy.data.objects["Armature"].pose.bones["head"].rotation_quaternion = quaternion spine_rotations = decoded_message['Body']['Rotations']['spine_01'] quaternion = mathutils.Quaternion(spine_rotations) quaternion.w, quaternion.x, quaternion.y, quaternion.z = quaternion[3], quaternion[0], quaternion[1], quaternion[2] bpy.data.objects["Armature"].pose.bones["spine"].rotation_quaternion = quaternion chest_rotations = decoded_message['Body']['Rotations']['spine_02'] quaternion = mathutils.Quaternion(chest_rotations) quaternion.w, quaternion.x, quaternion.y, quaternion.z = quaternion[3], quaternion[0], quaternion[1], quaternion[2] bpy.data.objects["Armature"].pose.bones["chest"].rotation_quaternion = quaternion neck_rotations = decoded_message['Body']['Rotations']['neck_01'] quaternion = mathutils.Quaternion(neck_rotations) quaternion.w, quaternion.x, quaternion.y, quaternion.z = quaternion[3], quaternion[0], quaternion[1], quaternion[2] bpy.data.objects["Armature"].pose.bones["neck"].rotation_quaternion = quaternion shoulder_l_rotations = decoded_message['Body']['Rotations']['clavicle_l'] quaternion = mathutils.Quaternion(shoulder_l_rotations) quaternion.w, quaternion.x, quaternion.y, quaternion.z = quaternion[3], quaternion[0], quaternion[1], quaternion[2] bpy.data.objects["Armature"].pose.bones["shoulder.L"].rotation_quaternion = quaternion shoulder_r_rotations = decoded_message['Body']['Rotations']['clavicle_r'] quaternion = mathutils.Quaternion(shoulder_r_rotations) quaternion.w, quaternion.x, quaternion.y, quaternion.z = quaternion[3], quaternion[0], quaternion[1], quaternion[2] bpy.data.objects["Armature"].pose.bones["shoulder.R"].rotation_quaternion = quaternion shoulder_r_rotations = decoded_message['Body']['Rotations']['upperarm_l'] quaternion = mathutils.Quaternion(shoulder_r_rotations) quaternion.w, quaternion.x, quaternion.y, quaternion.z = quaternion[3], quaternion[0], quaternion[1], quaternion[2] bpy.data.objects["Armature"].pose.bones["upper_arm.L"].rotation_quaternion = quaternion shoulder_r_rotations = decoded_message['Body']['Rotations']['upperarm_r'] quaternion = mathutils.Quaternion(shoulder_r_rotations) quaternion.w, quaternion.x, quaternion.y, quaternion.z = quaternion[3], quaternion[0], quaternion[1], quaternion[2] bpy.data.objects["Armature"].pose.bones["upper_arm.R"].rotation_quaternion = quaternion self.running = True # This is a global so when we run the script again, we can keep the server alive # but change how it works class MyProperties(PropertyGroup): running: BoolProperty( name = "server_running", description = "Is server running?", default = False ) skeleton: StringProperty( name = "Armature", description = "Select an armature", default = "", maxlen = 1024 ) record: BoolProperty( name = "record_running", description = "Is record running?", default = False ) def receivedMotionData(decoded_message): if 'Body' in decoded_message.keys() and 'Rotations' in decoded_message['Body'].keys(): head_rotations = decoded_message['Body']['Rotations']['head'] quaternion = mathutils.Quaternion(head_rotations) quaternion.w, quaternion.x, quaternion.y, quaternion.z = quaternion[3], quaternion[0], quaternion[1], quaternion[2] bpy.data.objects["Armature"].pose.bones["head"].rotation_quaternion = quaternion pass def startOrStopServer(running = False): if running: serverThread.stopServer() print("Server is running. Stop it.") else: print("Start server") serverThread.start() def startServer(): try: if serverThread.running == False: serverThread = ServerThread() serverThread.start() print("Starting server") else: print("Server already running, using new motion handler.") except: serverThread = ServerThread() serverThread.start() print("Starting server") # = Operators = class WM_OT_StartServer(Operator): bl_label = "Start or Stop Server" bl_idname = "mocap.start_server" def execute(self, context): global serverThread global running if running: running = False try: serverThread.stopServer() except: print("Nothing to stop") else: running = True serverThread = ServerThread() serverThread.start() return {'FINISHED'} class RecordingOperator(bpy.types.Operator): bl_idname = "object.recording_operator" bl_label = "Record MOCAP or STOP" def insert_all_keyframe(self): armature = bpy.data.objects["Armature"] head_bone = armature.pose.bones["head"] head_bone.keyframe_insert(data_path="rotation_quaternion", frame = bpy.context.scene.frame_current) armature = bpy.data.objects["Armature"] spine_bone = armature.pose.bones["spine"] spine_bone.keyframe_insert(data_path="rotation_quaternion", frame= bpy.context.scene.frame_current) armature = bpy.data.objects["Armature"] neck_bone = armature.pose.bones["neck"] neck_bone.keyframe_insert(data_path="rotation_quaternion", frame= bpy.context.scene.frame_current) armature = bpy.data.objects["Armature"] chest_bone = armature.pose.bones["chest"] chest_bone.keyframe_insert(data_path="rotation_quaternion", frame= bpy.context.scene.frame_current) def execute(self, context): global record if record: record = False bpy.ops.screen.animation_cancel(restore_frame=False) else: record = True self.insert_all_keyframe() bpy.ops.screen.animation_play() return {'FINISHED'} # = Panel in Object Mode = class OBJECT_PT_CustomPanel(bpy.types.Panel): bl_label = "MoCap-PoseCam Server" bl_idname = "OBJECT_PT_custom_panel" bl_space_type = "VIEW_3D" bl_region_type = "UI" bl_category = "MoCap" #bl_context = "scene" @classmethod def poll(self, context): return context.object is not None def draw(self, context): layout = self.layout scene = context.scene mytool = scene.my_tool global running global record btn_title = "Stop Server" if running else "Start Server" btn_icon = "SNAP_FACE" if running else "PLAY" btn_title2 = "Stop record" if record else "Start record" btn_icon2 = "SNAP_FACE" if record else "PLAY" layout.label(text=IP, icon="MOD_WAVE") layout.operator("mocap.start_server", text=btn_title, icon=btn_icon) layout.prop_search(scene, "skeleton", bpy.data, "armatures", text="Skeleton") layout.operator("object.recording_operator", text=btn_title2, icon=btn_icon2) layout.separator() # = Registration = classes = ( MyProperties, WM_OT_StartServer, OBJECT_PT_CustomPanel, RecordingOperator ) def register(): from bpy.utils import register_class for cls in classes: register_class(cls) bpy.types.Scene.my_tool = PointerProperty(type=MyProperties) bpy.types.Scene.skeleton = StringProperty() def unregister(): from bpy.utils import unregister_class for cls in reversed(classes): unregister_class(cls) del bpy.types.Scene.my_tool del bpy.types.Scene.skeleton # = Test = if __name__ == "__main__": register()