Package: vmcp
Virtual Motion Capture (VMC) protocol package for PythonTheVMC protocolis used for
realtime Motion Capture transmission and based on
theOpen Sound Control (OSC) protcol.
ThisPythonmodule is an
highly modular implementation of it's most basic features.FeaturesVMC protocolRoot transform(Supported specs: v2.0.0, v2.1.0)Bone transformDevice transform(Supported specs: v2.2.0, v2.3.0)BlendshapesState changes(Supported specs: v1.0.0, v2.5.0, v2.7.0)Relative timeand it'sreceiving events.OSC protocolMultiple independent sender- and receiver-channelsTimeTag supportSpecialsIndependent usage of the VMC and OSC protocol layers possible.Backend agnostic (feel free to replaceosc4py3with something else).InstallationIf youonly want to use the VMC protocol layer,
you can simply install it viapip install vmcp(without OSC).If you want it ready-to-use including OSC, install it with the dependencies
ofyour choosen backend.
For example forvmcp.osc.backend.osc4py3.as_comthreadsyou install it viapip install vmcp[osc4py3](recommended)UsageYou may find the following sections in the documentation helpful:vmcp.protocolvmcp.typingvmcp.eventsvmcp.facadesvmcp.oscVirtual Motion Capture (VMC) protocolBasic VMCP example#!/usr/bin/env python3# -*- coding: utf-8 -*-# SPDX-License-Identifier: AGPL-3.0-or-later"""Basic VMC protocol example."""frommathimportradians# OSCfromvmcp.oscimportOSCfromvmcp.osc.typingimportMessagefromvmcp.osc.backend.osc4py3importas_comthreadsasbackend# VMC protocol layerfromvmcp.eventsimport(Event,RootTransformEvent,BoneTransformEvent,BlendShapeEvent,BlendShapeApplyEvent,DeviceTransformEvent,StateEvent,RelativeTimeEvent)fromvmcp.typingimport(CoordinateVector,Quaternion,Bone,DeviceType,BlendShapeKeyasAbstractBlendShapeKey,ModelState,Timestamp)fromvmcp.protocolimport(root_transform,bone_transform,device_transform,blendshape,blendshape_apply,state,time)# Facades for easy usage (optional)fromvmcp.facadesimporton_receive# Required, if blend shapes are used.classBlendShapeKey(AbstractBlendShapeKey):"""Example model blend shape keys.Depends on the used avatar model."""# Eye blinkEYES_BLINK_R="Blink_R"# Face expressionsFACE_FUN="Fun"LISTENING=Truedefreceived(event:Event):"""Receive transmission."""print(event)ifisinstance(event,DeviceTransformEvent):print(event.device_type)print(event.is_local)ifisinstance(event,RelativeTimeEvent):globalLISTENING# pylint: disable=global-statementLISTENING=Falsetry:osc=OSC(backend)withosc.open():# Receiverin1=osc.create_receiver("127.0.0.1",39539,"receiver1").open()on_receive(in1,RootTransformEvent,received)on_receive(in1,BoneTransformEvent,received)on_receive(in1,DeviceTransformEvent,received)on_receive(in1,BlendShapeEvent,received)on_receive(in1,BlendShapeApplyEvent,received)on_receive(in1,StateEvent,received)on_receive(in1,RelativeTimeEvent,received)# Senderosc.create_sender("127.0.0.1",39539,"sender1").open().send((Message(*root_transform(CoordinateVector(.5,.2,.5),Quaternion.identity())),Message(*bone_transform(Bone.LEFT_UPPER_LEG,CoordinateVector.identity(),Quaternion.from_euler(0,0,radians(-45)))),Message(*bone_transform(Bone.RIGHT_LOWER_ARM,CoordinateVector.identity(),Quaternion(0,0,0.3826834323650898,0.9238795325112867))),Message(*device_transform(DeviceType.HMD,"Head",CoordinateVector.identity(),Quaternion.identity())),Message(*blendshape(BlendShapeKey.FACE_FUN,1.0)),Message(*blendshape(BlendShapeKey.EYES_BLINK_R,1.0)),Message(*blendshape_apply()),Message(*state(ModelState.LOADED)),Message(*time(Timestamp()))))# ProcessingwhileLISTENING:osc.run()exceptKeyboardInterrupt:print("Canceled.")finally:osc.close()Only Open Sound Control (OSC) protocolYou are free use use the OSC protocol directly.Basic OSC example#!/usr/bin/env python3# -*- coding: utf-8 -*-# SPDX-License-Identifier: AGPL-3.0-or-later"""Basic OSC protocol example."""fromtypingimportAnyfromvmcp.oscimportOSCfromvmcp.osc.typingimportMessagefromvmcp.osc.backend.osc4py3importas_comthreadsasbackendLISTENING:bool=Truedefreceived(*args:Any):"""Receive transmission."""globalLISTENING# pylint: disable=global-statementprint(args)LISTENING=Falsetry:osc=OSC(backend)withosc.open():# Receiver channelin1=osc.create_receiver("127.0.0.1",39539,"receiver1")in1.register_handler("/test/one",received)in1.open()# Sender channelout1=osc.create_sender("127.0.0.1",39539,"sender1").open()out1.send(Message("/test/one",",sif",["first",672,8.871]))# Additional sender channelosc.create_sender("127.0.0.1",39540,"sender2").open().send((Message("/test/one",",sif",["second",672,8.871]),Message("/test/two",",sif",["third",234,2.513])))# ProcessingwhileLISTENING:osc.run()exceptKeyboardInterrupt:print("Cancheled.")finally:osc.close()VMC protocol layer onlyIf you are only want to use the VMC protocol layer
without the underlying OSC stuff.VMCP only example#!/usr/bin/env python3# -*- coding: utf-8 -*-# SPDX-License-Identifier: AGPL-3.0-or-later"""VMCP protocol layer only example."""fromvmcp.typingimport(CoordinateVector,Quaternion)fromvmcp.protocolimportroot_transformfromvmcp.eventsimport(Event,RootTransformEvent)defreceived(event:Event):"""Receive transmission."""print(repr(event))position=CoordinateVector(1.0,2.0,3.0)rotation=Quaternion.identity()print(root_transform(position,rotation))received(RootTransformEvent.from_message_data("example",RootTransformEvent.ADDRESS_PATTERN,("root",position.x,position.y,position.z,rotation.x,rotation.y,rotation.z,rotation.w)))In this case it'sNOT requiredto install any backend dependencies.Additional examplesBasic OSC with loggingBasic OSC with custom receiving parameters(Backend dependant)Basic OSC with TimeTag's(Backend dependant)Currently available backendsvmcp.osc.backend.osc4py3.as_eventloopYou need to callOSC.run()to process message sending and receiving.
Depending on the amount of traffic, multiple calls may be required.Dependenciesosc4py3Installpip install vmcp[osc4py3]vmcp.osc.backend.osc4py3.as_comthreadsOnly message receiving requiresOSC.run()calls.
Sending runs in separate threads.
This backend might be your choice to avoid race conditions inside your application.Dependenciesosc4py3Installpip install vmcp[osc4py3]vmcp.osc.backend.osc4py3.as_allthreadsYou don't need to callOSC.run(), but it doesn't harm.
Everything runs in separate threads.Dependenciesosc4py3Installpip install vmcp[osc4py3]Known bugsIf you want to use thevmcp.osc.backend.osc4py3.as_comthreadsbackend,
then you need to downgradeosc4py3to version1.0.3(pip install osc4py3==1.0.3).If you want to use logging with anyvmcp.osc.backend.osc4py3.*backend,
then you need to useosc4py3version1.0.4or higher.ContributionInstallGit LFSand(Mini)condaMake sure, thatcondais available for git (for example runecho ". /c/tools/miniconda3/etc/profile.d/conda.sh" >> ~/.profilein GitBash for Windows).Prepare LFS by issuing the commandgit lfs installonce.Fork and clone this repository.Install git hooks by issuing the commandgit config core.hooksPath .githooks.Create conda environment and activate it:conda create -n vmcp python=3.10
conda activate vmcpInstall the package in editable mode with all tools:pip install -e ".[osc4py3,dev]"If you finished your work, uninstall it by:python setup.py develop -uYou can delete your conda environment by:conda remove -n vmcp --allTODOAdd more VMC protocol commands.Add abstract application patterns forAssistant,PerformerandMarionette.AddVRMmodel parser.Add more OSC backends.Project state meaningsUnstable:Still anpossiblybroken draft or breaking issues are known.Refactoring:Structural and technical changes have currently priority.Stable:Everything seems working as expected.Fixes only:The project is maintained at the minimum level to apply at least fixes.Takeover:The project is currently being taked over by another team and will possibly move.Not maintained:The project is not maintained any more (ready to take over).Feature requests are welcomed all the time, of course! ;-)LicenseThis project is free under the terms of the AGPL 3.0 (or later) license.
For more details please see the LICENSE file or:GNU