Skip to main content
1 of 2

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()