Add command-line interface to control model
authorAdrian Iain Lam <adrianiainlam@users.noreply.github.com>
Sun, 18 Oct 2020 08:33:33 +0000 (09:33 +0100)
committerAdrian Iain Lam <adrianiainlam@users.noreply.github.com>
Sun, 18 Oct 2020 08:33:33 +0000 (09:33 +0100)
This can now:
 - Start motions
 - Set expressions
 - Manually set Live2D parameters

Some config file params renamed.

CMakeLists.txt
config.txt
example/demo.patch
example/generate_patch.sh
include/mouse_cursor_tracker.h
src/mouse_cursor_tracker.cpp

index 8780521..3a2ccf1 100644 (file)
@@ -4,6 +4,7 @@ project(MouseTrackerForCubism_project)
 
 find_library(xdo_LIBS NAMES xdo libxdo PATHS /usr/lib REQUIRED)
 find_library(pulse_LIBS NAMES pulse PATHS /usr/lib REQUIRED)
+find_library(readline_LIBS NAMES readline libreadline /usr/lib REQUIRED)
 
 include_directories(include)
 
@@ -17,4 +18,4 @@ set_target_properties(
     include/mouse_cursor_tracker.h
 )
 
-target_link_libraries(MouseTrackerForCubism ${xdo_LIBS} ${pulse_LIBS} pulse-simple)
+target_link_libraries(MouseTrackerForCubism ${xdo_LIBS} ${pulse_LIBS} pulse-simple ${readline_LIBS})
index 1591db1..174f9e3 100644 (file)
@@ -7,7 +7,7 @@ sleep_ms 5
 # Automatic functionality in Live2D
 autoBlink 1
 autoBreath 1
-randomMotion 0
+randomIdleMotion 1
 useLipSync 1
 
 # Lip sync configurations
@@ -39,12 +39,12 @@ mouthForm 0
 # If you have multiple screens, select the ID of the one you want to track.
 screen 0
 
-# The "middle" position, i.e. the coordinates of the cursor where
+# The "origin" position, i.e. the coordinates of the cursor where
 # the Live2D model will be looking straight ahead.
 # For a 1920x1080 screen, {1600, 870} will be somewhere near the
 # bottom right corner.
-middle_x 1600
-middle_y 870
+origin_x 1600
+origin_y 870
 
 # The bounding box. These are the limits of the coordinates where the
 # Live2D model will be looking 30 degrees to each side.
index 945a1ce..304b687 100644 (file)
@@ -1,5 +1,5 @@
 diff -pruN --exclude build ./demo_clean/CMakeLists.txt ./demo_dev/CMakeLists.txt
---- ./demo_clean/CMakeLists.txt        2020-10-02 02:01:04.825787688 +0100
+--- ./demo_clean/CMakeLists.txt        2020-10-01 22:47:25.846828066 +0100
 +++ ./demo_dev/CMakeLists.txt  2020-10-01 23:29:15.530233484 +0100
 @@ -3,7 +3,7 @@ cmake_minimum_required(VERSION 3.16)
  # Set app name.
@@ -43,8 +43,8 @@ diff -pruN --exclude build ./demo_clean/CMakeLists.txt ./demo_dev/CMakeLists.txt
  # Copy resource directory to build directory.
  add_custom_command(
 diff -pruN --exclude build ./demo_clean/scripts/make_gcc ./demo_dev/scripts/make_gcc
---- ./demo_clean/scripts/make_gcc      2020-10-02 02:01:04.825787688 +0100
-+++ ./demo_dev/scripts/make_gcc        2020-10-01 23:43:42.213875065 +0100
+--- ./demo_clean/scripts/make_gcc      2020-10-01 22:47:25.854827921 +0100
++++ ./demo_dev/scripts/make_gcc        2020-10-12 03:42:07.847955578 +0100
 @@ -10,4 +10,4 @@ BUILD_PATH=$SCRIPT_PATH/../build/make_gc
  cmake -S "$CMAKE_PATH" \
    -B "$BUILD_PATH" \
@@ -52,7 +52,7 @@ diff -pruN --exclude build ./demo_clean/scripts/make_gcc ./demo_dev/scripts/make
 -cd "$BUILD_PATH" && make
 +cd "$BUILD_PATH" && make -j4
 diff -pruN --exclude build ./demo_clean/src/CMakeLists.txt ./demo_dev/src/CMakeLists.txt
---- ./demo_clean/src/CMakeLists.txt    2020-10-02 02:01:04.829787750 +0100
+--- ./demo_clean/src/CMakeLists.txt    2020-10-01 22:47:25.850827994 +0100
 +++ ./demo_dev/src/CMakeLists.txt      2020-10-01 22:47:24.842846271 +0100
 @@ -19,6 +19,4 @@ target_sources(${APP_NAME}
      ${CMAKE_CURRENT_SOURCE_DIR}/LAppView.cpp
@@ -61,8 +61,25 @@ diff -pruN --exclude build ./demo_clean/src/CMakeLists.txt ./demo_dev/src/CMakeL
 -    ${CMAKE_CURRENT_SOURCE_DIR}/TouchManager.cpp
 -    ${CMAKE_CURRENT_SOURCE_DIR}/TouchManager.hpp
  )
+diff -pruN --exclude build ./demo_clean/src/LAppDefine.cpp ./demo_dev/src/LAppDefine.cpp
+--- ./demo_clean/src/LAppDefine.cpp    2020-10-01 22:47:25.850827994 +0100
++++ ./demo_dev/src/LAppDefine.cpp      2020-10-18 04:59:13.238452938 +0100
+@@ -61,11 +61,11 @@ namespace LAppDefine {
+     const csmInt32 PriorityForce = 3;
+     // デバッグ用ログの表示オプション
+-    const csmBool DebugLogEnable = true;
++    const csmBool DebugLogEnable = false;
+     const csmBool DebugTouchLogEnable = false;
+     // Frameworkから出力するログのレベル設定
+-    const CubismFramework::Option::LogLevel CubismLoggingLevel = CubismFramework::Option::LogLevel_Verbose;
++    const CubismFramework::Option::LogLevel CubismLoggingLevel = CubismFramework::Option::LogLevel_Warning;
+     // デフォルトのレンダーターゲットサイズ
+     const csmInt32 RenderTargetWidth = 1900;
 diff -pruN --exclude build ./demo_clean/src/LAppDelegate.cpp ./demo_dev/src/LAppDelegate.cpp
---- ./demo_clean/src/LAppDelegate.cpp  2020-10-02 02:01:04.829787750 +0100
+--- ./demo_clean/src/LAppDelegate.cpp  2020-10-01 22:47:25.850827994 +0100
 +++ ./demo_dev/src/LAppDelegate.cpp    2020-10-01 22:47:24.698848890 +0100
 @@ -45,7 +45,8 @@ void LAppDelegate::ReleaseInstance()
      s_instance = NULL;
@@ -192,7 +209,7 @@ diff -pruN --exclude build ./demo_clean/src/LAppDelegate.cpp ./demo_dev/src/LApp
  
  Csm::csmVector<string> LAppDelegate::Split(const std::string& baseString, char delimiter)
 diff -pruN --exclude build ./demo_clean/src/LAppDelegate.hpp ./demo_dev/src/LAppDelegate.hpp
---- ./demo_clean/src/LAppDelegate.hpp  2020-10-02 02:01:04.829787750 +0100
+--- ./demo_clean/src/LAppDelegate.hpp  2020-10-01 22:47:25.850827994 +0100
 +++ ./demo_dev/src/LAppDelegate.hpp    2020-10-01 22:47:24.842846271 +0100
 @@ -40,7 +40,8 @@ public:
      /**
@@ -268,7 +285,7 @@ diff -pruN --exclude build ./demo_clean/src/LAppDelegate.hpp ./demo_dev/src/LApp
 -
 -};
 diff -pruN --exclude build ./demo_clean/src/LAppLive2DManager.cpp ./demo_dev/src/LAppLive2DManager.cpp
---- ./demo_clean/src/LAppLive2DManager.cpp     2020-10-02 02:01:04.829787750 +0100
+--- ./demo_clean/src/LAppLive2DManager.cpp     2020-10-01 22:47:25.850827994 +0100
 +++ ./demo_dev/src/LAppLive2DManager.cpp       2020-10-02 02:00:49.961556700 +0100
 @@ -52,9 +52,10 @@ void LAppLive2DManager::ReleaseInstance(
  
@@ -373,7 +390,7 @@ diff -pruN --exclude build ./demo_clean/src/LAppLive2DManager.cpp ./demo_dev/src
 +    _translateY = translateY;
 +}
 diff -pruN --exclude build ./demo_clean/src/LAppLive2DManager.hpp ./demo_dev/src/LAppLive2DManager.hpp
---- ./demo_clean/src/LAppLive2DManager.hpp     2020-10-02 02:01:04.825787688 +0100
+--- ./demo_clean/src/LAppLive2DManager.hpp     2020-10-01 22:47:25.846828066 +0100
 +++ ./demo_dev/src/LAppLive2DManager.hpp       2020-10-01 23:36:24.583055381 +0100
 @@ -6,12 +6,15 @@
   */
@@ -450,18 +467,20 @@ diff -pruN --exclude build ./demo_clean/src/LAppLive2DManager.hpp ./demo_dev/src
 +    float _translateY;
  };
 diff -pruN --exclude build ./demo_clean/src/LAppModel.cpp ./demo_dev/src/LAppModel.cpp
---- ./demo_clean/src/LAppModel.cpp     2020-10-02 02:01:04.825787688 +0100
-+++ ./demo_dev/src/LAppModel.cpp       2020-10-01 23:34:43.482626010 +0100
-@@ -21,6 +21,8 @@
+--- ./demo_clean/src/LAppModel.cpp     2020-10-01 22:47:25.850827994 +0100
++++ ./demo_dev/src/LAppModel.cpp       2020-10-18 09:26:08.998822685 +0100
+@@ -21,6 +21,10 @@
  #include "LAppTextureManager.hpp"
  #include "LAppDelegate.hpp"
  
 +#include "mouse_cursor_tracker.h"
 +
++#include <iostream>
++
  using namespace Live2D::Cubism::Framework;
  using namespace Live2D::Cubism::Framework::DefaultParameterId;
  using namespace LAppDefine;
-@@ -49,6 +51,7 @@ LAppModel::LAppModel()
+@@ -49,6 +53,7 @@ LAppModel::LAppModel()
      : CubismUserModel()
      , _modelSetting(NULL)
      , _userTimeSeconds(0.0f)
@@ -469,38 +488,7 @@ diff -pruN --exclude build ./demo_clean/src/LAppModel.cpp ./demo_dev/src/LAppMod
  {
      if (DebugLogEnable)
      {
-@@ -128,30 +131,6 @@ void LAppModel::SetupModel(ICubismModelS
-         DeleteBuffer(buffer, path.GetRawString());
-     }
--    //Expression
--    if (_modelSetting->GetExpressionCount() > 0)
--    {
--        const csmInt32 count = _modelSetting->GetExpressionCount();
--        for (csmInt32 i = 0; i < count; i++)
--        {
--            csmString name = _modelSetting->GetExpressionName(i);
--            csmString path = _modelSetting->GetExpressionFileName(i);
--            path = _modelHomeDir + path;
--
--            buffer = CreateBuffer(path.GetRawString(), &size);
--            ACubismMotion* motion = LoadExpression(buffer, size, name.GetRawString());
--
--            if (_expressions[name] != NULL)
--            {
--                ACubismMotion::Delete(_expressions[name]);
--                _expressions[name] = NULL;
--            }
--            _expressions[name] = motion;
--
--            DeleteBuffer(buffer, path.GetRawString());
--        }
--    }
--
-     //Physics
-     if (strcmp(_modelSetting->GetPhysicsFileName(), "") != 0)
-     {
-@@ -335,59 +314,72 @@ void LAppModel::Update()
+@@ -335,59 +340,110 @@ void LAppModel::Update()
      const csmFloat32 deltaTimeSeconds = LAppPal::GetDeltaTime();
      _userTimeSeconds += deltaTimeSeconds;
  
@@ -514,13 +502,13 @@ diff -pruN --exclude build ./demo_clean/src/LAppModel.cpp ./demo_dev/src/LAppMod
 -    //-----------------------------------------------------------------
 -    _model->LoadParameters(); // 前回セーブされた状態をロード
 -    if (_motionManager->IsFinished())
-+    if (_tracker)
-     {
+-    {
 -        // モーションの再生がない場合、待機モーションの中からランダムで再生する
 -        StartRandomMotion(MotionGroupIdle, PriorityIdle);
 -    }
 -    else
--    {
++    if (_tracker)
+     {
 -        motionUpdated = _motionManager->UpdateMotion(_model, deltaTimeSeconds); // モーションを更新
 -    }
 -    _model->SaveParameters(); // 状態を保存
@@ -532,61 +520,97 @@ diff -pruN --exclude build ./demo_clean/src/LAppModel.cpp ./demo_dev/src/LAppMod
 -    if (!motionUpdated)
 -    {
 -        if (_eyeBlink != NULL)
-+        // NOTE: Apparently, this LoadParameters/SaveParameters pair
-+        // is needed for auto breath to work.
 +        _model->LoadParameters(); // 前回セーブされた状態をロード
-+        if (_motionManager->IsFinished() && params.randomMotion)
++
++        int paramsMotionPriority = static_cast<int>(params.motionPriority);
++
++        if (paramsMotionPriority != PriorityNone)
          {
 -            // メインモーションの更新がないとき
 -            _eyeBlink->UpdateParameters(_model, deltaTimeSeconds); // 目パチ
++            StartMotion(params.motionGroup.c_str(), params.motionNumber,
++                        paramsMotionPriority);
++        }
++        else if (params.randomIdleMotion && _motionManager->IsFinished())
++        {
 +            // モーションの再生がない場合、待機モーションの中からランダムで再生する
 +            StartRandomMotion(MotionGroupIdle, PriorityIdle);
          }
 -    }
-+        else
-+        {
-+            _motionManager->UpdateMotion(_model, deltaTimeSeconds); // モーションを更新
-+        }
-+        _model->SaveParameters(); // 状態を保存
  
 -    if (_expressionManager != NULL)
 -    {
 -        _expressionManager->UpdateMotion(_model, deltaTimeSeconds); // 表情でパラメータ更新(相対変化)
 -    }
++        // FIXME pose does not return to normal after motion
++        // if we don't have randomIdleMotion set
++        else
++        {
++            _motionManager->UpdateMotion(_model, deltaTimeSeconds); // モーションを更新
++        }
++        _model->SaveParameters(); // 状態を保存
  
 -    //ドラッグによる変化
 -    //ドラッグによる顔の向きの調整
 -    _model->AddParameterValue(_idParamAngleX, _dragX * 30); // -30から30の値を加える
 -    _model->AddParameterValue(_idParamAngleY, _dragY * 30);
 -    _model->AddParameterValue(_idParamAngleZ, _dragX * _dragY * -30);
-+        if (params.autoBlink && _eyeBlink)
++        if (params.expression != "")
 +        {
-+            _eyeBlink->UpdateParameters(_model, deltaTimeSeconds);
++            SetExpression(params.expression.c_str());
 +        }
-+        else
++        if (_expressionManager != NULL)
 +        {
-+            _model->SetParameterValue(idMan->GetId("ParamEyeLOpen"),
-+                                      params.leftEyeOpenness);
-+            _model->SetParameterValue(idMan->GetId("ParamEyeROpen"),
-+                                      params.rightEyeOpenness);
++            _expressionManager->UpdateMotion(_model, deltaTimeSeconds); // 表情でパラメータ更新(相対変化)
 +        }
  
 -    //ドラッグによる体の向きの調整
 -    _model->AddParameterValue(_idParamBodyAngleX, _dragX * 10); // -10から10の値を加える
-+        _model->SetParameterValue(idMan->GetId("ParamMouthForm"),
-+                                  params.mouthForm);
++        bool autoBlink = params.autoBlink && _eyeBlink;
++        auto eyeLOpenIt = params.live2d.find("ParamEyeLOpen");
++        auto eyeROpenIt = params.live2d.find("ParamEyeROpen");
  
 -    //ドラッグによる目の向きの調整
 -    _model->AddParameterValue(_idParamEyeBallX, _dragX); // -1から1の値を加える
 -    _model->AddParameterValue(_idParamEyeBallY, _dragY);
-+        if (params.useLipSync && _lipSync)
++        if (autoBlink)
 +        {
-+            csmFloat32 value = params.lipSyncParam; // 0 to 1
++            // Handle blink first
++            _eyeBlink->UpdateParameters(_model, deltaTimeSeconds);
++        }
  
 -    // 呼吸など
 -    if (_breath != NULL)
 -    {
 -        _breath->UpdateParameters(_model, deltaTimeSeconds);
++        if (eyeLOpenIt != params.live2d.end())
++        {
++            // If value specified, override blinking
++            _model->SetParameterValue(idMan->GetId("ParamEyeLOpen"),
++                                      eyeLOpenIt->second);
++        }
++        else if (!autoBlink)
++        {
++            // If no value specified and no auto blink, set to 1
++            _model->SetParameterValue(idMan->GetId("ParamEyeLOpen"), 1);
++
++        }
++
++        if (eyeROpenIt != params.live2d.end())
++        {
++            _model->SetParameterValue(idMan->GetId("ParamEyeROpen"),
++                                      eyeROpenIt->second);
++        }
++        else if (!autoBlink)
++        {
++            _model->SetParameterValue(idMan->GetId("ParamEyeROpen"), 1);
++        }
++
++
++        if (params.useLipSync && _lipSync)
++        {
++            csmFloat32 value = params.lipSyncParam; // 0 to 1
++
 +            for (csmUint32 i = 0; i < _lipSyncIds.GetSize(); ++i)
 +            {
 +                _model->AddParameterValue(_lipSyncIds[i], value, 0.8f);
@@ -595,19 +619,21 @@ diff -pruN --exclude build ./demo_clean/src/LAppModel.cpp ./demo_dev/src/LAppMod
 +        else
 +        {
 +            _model->SetParameterValue(idMan->GetId("ParamMouthOpenY"),
-+                                      params.mouthOpenness);
++                                      params.live2d["ParamMouthOpenY"]);
++        }
++
++        for (auto const &entry : params.live2d)
++        {
++            std::string key = entry.first;
++            double val = entry.second;
++
++            if (key != "ParamEyeLOpen" && key != "ParamEyeROpen" &&
++                key != "ParamMouthOpenY")
++            {
++                _model->SetParameterValue(idMan->GetId(key.c_str()), val);
++            }
 +        }
 +
-+        _model->SetParameterValue(idMan->GetId("ParamEyeLSmile"),
-+                                  params.leftEyeSmile);
-+        _model->SetParameterValue(idMan->GetId("ParamEyeRSmile"),
-+                                  params.rightEyeSmile);
-+        _model->SetParameterValue(idMan->GetId("ParamAngleX"),
-+                                  params.faceXAngle);
-+        _model->SetParameterValue(idMan->GetId("ParamAngleY"),
-+                                  params.faceYAngle);
-+        _model->SetParameterValue(idMan->GetId("ParamAngleZ"),
-+                                  params.faceZAngle);
 +        if (params.autoBreath && _breath)
 +        {
 +            // Note: _model->LoadParameters and SaveParameters is needed
@@ -617,7 +643,7 @@ diff -pruN --exclude build ./demo_clean/src/LAppModel.cpp ./demo_dev/src/LAppMod
      }
  
      // 物理演算の設定
-@@ -396,17 +388,6 @@ void LAppModel::Update()
+@@ -396,17 +452,6 @@ void LAppModel::Update()
          _physics->Evaluate(_model, deltaTimeSeconds);
      }
  
@@ -635,7 +661,7 @@ diff -pruN --exclude build ./demo_clean/src/LAppModel.cpp ./demo_dev/src/LAppMod
      // ポーズの設定
      if (_pose != NULL)
      {
-@@ -626,3 +607,9 @@ Csm::Rendering::CubismOffscreenFrame_Ope
+@@ -626,3 +671,14 @@ Csm::Rendering::CubismOffscreenFrame_Ope
  {
      return _renderBuffer;
  }
@@ -645,9 +671,14 @@ diff -pruN --exclude build ./demo_clean/src/LAppModel.cpp ./demo_dev/src/LAppMod
 +    _tracker = tracker;
 +}
 +
++Csm::ICubismModelSetting* LAppModel::GetModelSetting(void) const
++{
++    return _modelSetting;
++}
++
 diff -pruN --exclude build ./demo_clean/src/LAppModel.hpp ./demo_dev/src/LAppModel.hpp
---- ./demo_clean/src/LAppModel.hpp     2020-10-02 02:01:04.829787750 +0100
-+++ ./demo_dev/src/LAppModel.hpp       2020-10-01 23:35:39.254849094 +0100
+--- ./demo_clean/src/LAppModel.hpp     2020-10-01 22:47:25.850827994 +0100
++++ ./demo_dev/src/LAppModel.hpp       2020-10-18 03:04:52.142045751 +0100
 @@ -13,6 +13,7 @@
  #include <Type/csmRectF.hpp>
  #include <Rendering/OpenGL/CubismOffscreenSurface_OpenGLES2.hpp>
@@ -656,7 +687,7 @@ diff -pruN --exclude build ./demo_clean/src/LAppModel.hpp ./demo_dev/src/LAppMod
  
  /**
   * @brief ユーザーが実際に使用するモデルの実装クラス<br>
-@@ -113,6 +114,13 @@ public:
+@@ -113,6 +114,15 @@ public:
       */
      Csm::Rendering::CubismOffscreenFrame_OpenGLES2& GetRenderBuffer();
  
@@ -667,10 +698,12 @@ diff -pruN --exclude build ./demo_clean/src/LAppModel.hpp ./demo_dev/src/LAppMod
 +     */
 +    void SetTracker(MouseCursorTracker *tracker);
 +
++    Csm::ICubismModelSetting* GetModelSetting(void) const;
++
  protected:
      /**
       *  @brief  モデルを描画する処理。モデルを描画する空間のView-Projection行列を渡す。
-@@ -183,6 +191,8 @@ private:
+@@ -183,6 +193,8 @@ private:
      const Csm::CubismId* _idParamEyeBallY; ///< パラメータID: ParamEyeBallXY
  
      Csm::Rendering::CubismOffscreenFrame_OpenGLES2 _renderBuffer;   ///< フレームバッファ以外の描画先
@@ -680,8 +713,8 @@ diff -pruN --exclude build ./demo_clean/src/LAppModel.hpp ./demo_dev/src/LAppMod
  
  
 diff -pruN --exclude build ./demo_clean/src/LAppPal.cpp ./demo_dev/src/LAppPal.cpp
---- ./demo_clean/src/LAppPal.cpp       2020-10-02 02:01:04.829787750 +0100
-+++ ./demo_dev/src/LAppPal.cpp 2020-10-01 22:47:24.722848453 +0100
+--- ./demo_clean/src/LAppPal.cpp       2020-10-01 22:47:25.850827994 +0100
++++ ./demo_dev/src/LAppPal.cpp 2020-10-18 04:57:43.289600308 +0100
 @@ -6,6 +6,7 @@
   */
  
@@ -690,7 +723,15 @@ diff -pruN --exclude build ./demo_clean/src/LAppPal.cpp ./demo_dev/src/LAppPal.c
  #include <stdio.h>
  #include <stdlib.h>
  #include <stdarg.h>
-@@ -45,10 +46,7 @@ csmByte* LAppPal::LoadFileAsBytes(const
+@@ -36,7 +37,6 @@ csmByte* LAppPal::LoadFileAsBytes(const
+     if (stat(path, &statBuf) == 0)
+     {
+         size = statBuf.st_size;
+-        PrintLog(path);
+     }
+     std::fstream file;
+@@ -45,10 +45,7 @@ csmByte* LAppPal::LoadFileAsBytes(const
      file.open(path, std::ios::in | std::ios::binary);
      if (!file.is_open())
      {
@@ -703,7 +744,7 @@ diff -pruN --exclude build ./demo_clean/src/LAppPal.cpp ./demo_dev/src/LAppPal.c
      }
      file.read(buf, size);
 diff -pruN --exclude build ./demo_clean/src/LAppTextureManager.cpp ./demo_dev/src/LAppTextureManager.cpp
---- ./demo_clean/src/LAppTextureManager.cpp    2020-10-02 02:01:04.833787812 +0100
+--- ./demo_clean/src/LAppTextureManager.cpp    2020-10-01 22:47:25.850827994 +0100
 +++ ./demo_dev/src/LAppTextureManager.cpp      2020-10-01 22:47:24.654849690 +0100
 @@ -96,6 +96,46 @@ LAppTextureManager::TextureInfo* LAppTex
  
@@ -753,7 +794,7 @@ diff -pruN --exclude build ./demo_clean/src/LAppTextureManager.cpp ./demo_dev/sr
  {
      for (Csm::csmUint32 i = 0; i < _textures.GetSize(); i++)
 diff -pruN --exclude build ./demo_clean/src/LAppTextureManager.hpp ./demo_dev/src/LAppTextureManager.hpp
---- ./demo_clean/src/LAppTextureManager.hpp    2020-10-02 02:01:04.825787688 +0100
+--- ./demo_clean/src/LAppTextureManager.hpp    2020-10-01 22:47:25.846828066 +0100
 +++ ./demo_dev/src/LAppTextureManager.hpp      2020-10-01 22:47:24.786847290 +0100
 @@ -72,6 +72,8 @@ public:
      */
@@ -765,7 +806,7 @@ diff -pruN --exclude build ./demo_clean/src/LAppTextureManager.hpp ./demo_dev/sr
      * @brief 画像の解放
      *
 diff -pruN --exclude build ./demo_clean/src/LAppView.cpp ./demo_dev/src/LAppView.cpp
---- ./demo_clean/src/LAppView.cpp      2020-10-02 02:01:04.833787812 +0100
+--- ./demo_clean/src/LAppView.cpp      2020-10-01 22:47:25.850827994 +0100
 +++ ./demo_dev/src/LAppView.cpp        2020-10-01 22:47:24.602850636 +0100
 @@ -13,7 +13,6 @@
  #include "LAppLive2DManager.hpp"
@@ -942,7 +983,7 @@ diff -pruN --exclude build ./demo_clean/src/LAppView.cpp ./demo_dev/src/LAppView
 -    }
  }
 diff -pruN --exclude build ./demo_clean/src/LAppView.hpp ./demo_dev/src/LAppView.hpp
---- ./demo_clean/src/LAppView.hpp      2020-10-02 02:01:04.825787688 +0100
+--- ./demo_clean/src/LAppView.hpp      2020-10-01 22:47:25.846828066 +0100
 +++ ./demo_dev/src/LAppView.hpp        2020-10-01 22:47:24.802846999 +0100
 @@ -14,7 +14,6 @@
  #include "CubismFramework.hpp"
@@ -998,15 +1039,18 @@ diff -pruN --exclude build ./demo_clean/src/LAppView.hpp ./demo_dev/src/LAppView
      // レンダリング先を別ターゲットにする方式の場合に使用
      LAppSprite* _renderSprite;                                  ///< モードによっては_renderBufferのテクスチャを描画
 diff -pruN --exclude build ./demo_clean/src/main.cpp ./demo_dev/src/main.cpp
---- ./demo_clean/src/main.cpp  2020-10-02 02:01:04.825787688 +0100
-+++ ./demo_dev/src/main.cpp    2020-10-01 23:42:12.845205308 +0100
-@@ -5,18 +5,154 @@
+--- ./demo_clean/src/main.cpp  2020-10-01 22:47:25.846828066 +0100
++++ ./demo_dev/src/main.cpp    2020-10-18 07:03:46.194220443 +0100
+@@ -5,18 +5,182 @@
   * that can be found at https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
   */
  
 +#include <thread>
 +#include <stdexcept>
 +#include <sstream>
++#include <vector>
++#include <utility>
++#include <string>
 +
 +#ifdef __cpp_lib_filesystem
 +#include <filesystem>
@@ -1016,8 +1060,10 @@ diff -pruN --exclude build ./demo_clean/src/main.cpp ./demo_dev/src/main.cpp
 +namespace fs = std::experimental::filesystem;
 +#endif
 +
++#include "ICubismModelSetting.hpp"
  #include "LAppDelegate.hpp"
 +#include "LAppLive2DManager.hpp"
++#include "LAppModel.hpp"
 +#include "mouse_cursor_tracker.h"
 +
 +struct CmdArgs
@@ -1140,16 +1186,39 @@ diff -pruN --exclude build ./demo_clean/src/main.cpp ./demo_dev/src/main.cpp
 -    LAppDelegate::GetInstance()->Run();
 +    delegate->SetRootDirectory(cmdArgs.rootDir);
 +
-+    MouseCursorTracker tracker(cmdArgs.cfgPath);
-+
-+    std::thread trackerThread(&MouseCursorTracker::mainLoop, &tracker);
-+
 +    LAppLive2DManager *manager = LAppLive2DManager::GetInstance();
 +    manager->SetModel(cmdArgs.modelName);
 +
 +    manager->SetProjectionScaleTranslate(cmdArgs.scaleFactor,
 +                                         cmdArgs.translateX,
 +                                         cmdArgs.translateY);
++
++    LAppModel *model = manager->GetModel(0);
++    if (!model) throw std::runtime_error("model is null");
++
++    Live2D::Cubism::Framework::ICubismModelSetting *modelSetting = model->GetModelSetting();
++    if (!modelSetting) throw std::runtime_error("modelSetting is null");
++
++    std::vector<std::pair<std::string, int> > motions;
++    int motionGroupCount = modelSetting->GetMotionGroupCount();
++    for (int i = 0; i < motionGroupCount; i++)
++    {
++        const char *motionGroup = modelSetting->GetMotionGroupName(i);
++        int motionCount = modelSetting->GetMotionCount(motionGroup);
++        motions.push_back(std::make_pair(std::string(motionGroup), motionCount));
++    }
++
++    std::vector<std::string> expressions;
++    int expCount = modelSetting->GetExpressionCount();
++    for (int i = 0; i < expCount; i++)
++    {
++        const char *expName = modelSetting->GetExpressionName(i);
++        expressions.push_back(std::string(expName));
++    }
++
++    MouseCursorTracker tracker(cmdArgs.cfgPath, motions, expressions);
++
++    std::thread trackerThread(&MouseCursorTracker::mainLoop, &tracker);
 +    manager->SetTracker(&tracker);
 +
 +    delegate->Run();
index 068f4b0..738ff0a 100755 (executable)
@@ -1,5 +1,5 @@
 #!/bin/sh
 
 mkdir -p demo_clean
-cp -r CubismSdkForNative-4-r.1/Samples/OpenGL/Demo/proj.linux.cmake/* ./demo_clean/
+cp -p -r CubismSdkForNative-4-r.1/Samples/OpenGL/Demo/proj.linux.cmake/* ./demo_clean/
 diff -pruN --exclude build ./demo_clean ./demo_dev > ./demo.patch
index a53713f..6cd91c4 100644 (file)
@@ -26,8 +26,11 @@ SOFTWARE.
 ****/
 
 #include <string>
-#include <map>
 #include <thread>
+#include <vector>
+#include <utility>
+#include <mutex>
+#include <map>
 extern "C"
 {
 #include <xdo.h>
@@ -37,28 +40,38 @@ extern "C"
 class MouseCursorTracker
 {
 public:
-    MouseCursorTracker(std::string cfgPath);
+    MouseCursorTracker(std::string cfgPath,
+                       std::vector<std::pair<std::string, int> > motions = {},
+                       std::vector<std::string> expressions = {});
     ~MouseCursorTracker();
 
+    enum class MotionPriority
+    {
+        // See LAppDefine.cpp in Demo
+        none,
+        idle,
+        normal,
+        force
+    };
+
     struct Params
     {
-        double leftEyeOpenness;
-        double rightEyeOpenness;
-        double leftEyeSmile;
-        double rightEyeSmile;
-        double mouthOpenness;
-        double mouthForm;
-        double faceXAngle;
-        double faceYAngle;
-        double faceZAngle;
+        std::map<std::string, double> live2d;
+
         bool autoBlink;
         bool autoBreath;
-        bool randomMotion;
+        bool randomIdleMotion;
         bool useLipSync;
         double lipSyncParam;
+
+        MotionPriority motionPriority;
+        std::string motionGroup;
+        int motionNumber;
+
+        std::string expression;
     };
 
-    Params getParams(void) const;
+    Params getParams(void);
 
     void stop(void);
 
@@ -76,7 +89,7 @@ private:
         int sleepMs;
         bool autoBlink;
         bool autoBreath;
-        bool randomMotion;
+        bool randomIdleMotion;
         bool useLipSync;
         double lipSyncGain;
         double lipSyncCutOff;
@@ -87,9 +100,11 @@ private:
         int left;
         int right;
         int screen;
-        Coord middle;
+        Coord origin;
     } m_cfg;
 
+    std::map<std::string, double> m_overrideMap;
+
     bool m_stop;
 
     Coord m_curPos;
@@ -97,10 +112,19 @@ private:
     xdo_t *m_xdo;
 
     std::thread m_getVolumeThread;
+    std::thread m_parseCommandThread;
     void audioLoop(void);
+    void cliLoop(void);
+    void processCommand(std::string);
     double m_currentVol;
     pa_simple *m_pulse;
 
+    MotionPriority m_motionPriority;
+    std::string m_motionGroup;
+    int m_motionNumber;
+    std::mutex m_motionMutex;
+    std::string m_expression;
+
     void populateDefaultConfig(void);
     void parseConfig(std::string cfgPath);
 };
index 8ccdb35..9892482 100644 (file)
@@ -27,15 +27,22 @@ SOFTWARE.
 #include <fstream>
 #include <sstream>
 #include <vector>
+#include <utility>
+#include <algorithm>
 #include <cstdlib>
 #include <cmath>
+#include <cassert>
 
 #include <iostream>
+#include <cctype>
+#include <mutex>
 
 extern "C"
 {
 #include <xdo.h>
 #include <pulse/simple.h>
+#include <readline/readline.h>
+#include <readline/history.h>
 }
 #include "mouse_cursor_tracker.h"
 
@@ -49,9 +56,209 @@ static double rms(float *buf, std::size_t count)
     return std::sqrt(sum / count);
 }
 
-MouseCursorTracker::MouseCursorTracker(std::string cfgPath)
+static std::vector<std::string> split(std::string s)
+{
+    std::vector<std::string> v;
+    std::string tmp;
+
+    for (std::size_t i = 0; i < s.length(); i++)
+    {
+        char c = s[i];
+        if (std::isspace(c))
+        {
+            if (tmp != "")
+            {
+                v.push_back(tmp);
+                tmp = "";
+            }
+        }
+        else
+        {
+            tmp += c;
+        }
+    }
+    if (tmp != "")
+    {
+        v.push_back(tmp);
+    }
+    return v;
+}
+
+
+/* Using readline callback functions means that we need to pass
+ * information from our class to the callback, and the only way
+ * to do so is using globals.
+ */
+
+// Taken from https://github.com/eliben/code-for-blog/blob/master/2016/readline-samples/utils.cpp
+static std::string longest_common_prefix(std::string s,
+                                         const std::vector<std::string>& candidates) {
+    assert(candidates.size() > 0);
+    if (candidates.size() == 1) {
+        return candidates[0];
+    }
+
+    std::string prefix(s);
+    while (true) {
+        // Each iteration of this loop advances to the next location in all the
+        // candidates and sees if they match up to it.
+        size_t nextloc = prefix.size();
+        auto i = candidates.begin();
+        if (i->size() <= nextloc) {
+            return prefix;
+        }
+        char nextchar = (*(i++))[nextloc];
+        for (; i != candidates.end(); ++i) {
+            if (i->size() <= nextloc || (*i)[nextloc] != nextchar) {
+                // Bail out if there's a mismatch for this candidate.
+                return prefix;
+            }
+        }
+        // All candidates have contents[nextloc] == nextchar, so we can safely
+        // extend the prefix.
+        prefix.append(1, nextchar);
+    }
+
+    assert(0 && "unreachable");
+}
+
+std::vector<std::string> commands =
+{
+    "help", "motion", "expression", "set", "clear"
+};
+
+std::vector<std::string> live2dParams =
+{ // https://docs.live2d.com/cubism-editor-manual/standard-parametor-list/?locale=ja
+    "ParamAngleX", "ParamAngleY", "ParamAngleZ",
+    "ParamEyeLOpen", "ParamEyeLSmile", "ParamEyeROpen", "ParamEyeRSmile",
+    "ParamEyeBallX", "ParamEyeBallY", "ParamEyeBallForm",
+    "ParamBrowLY", "ParamBrowRY", "ParamBrowLX", "ParamBrowRX",
+    "ParamBrowLAngle", "ParamBrowRAngle", "ParamBrowLForm", "ParamBrowRForm",
+    "ParamMouthForm", "ParamMouthOpenY", "ParamTere",
+    "ParamBodyAngleX", "ParamBodyAngleY", "ParamBodyAngleZ", "ParamBreath",
+    "ParamArmLA", "ParamArmRA", "ParamArmLB", "ParamArmRB",
+    "ParamHandL", "ParamHandR",
+    "ParamHairFront", "ParamHairSide", "ParamHairBack", "ParamHairFluffy",
+    "ParamShoulderY", "ParamBustX", "ParamBustY",
+    "ParamBaseX", "ParamBaseY"
+};
+std::vector<std::pair<std::string, int> > MCT_motions;
+std::vector<std::string> MCT_expressions;
+std::map<std::string, double> *MCT_overrideMap;
+
+static char **cliCompletionFunction(const char *textCStr, int start, int end)
+{
+    // Reference: https://github.com/eliben/code-for-blog/blob/master/2016/readline-samples/readline-complete-subcommand.cpp
+    rl_attempted_completion_over = 1;
+
+    std::string line(rl_line_buffer);
+
+    std::vector<std::string> cmdline = split(line);
+
+    std::vector<std::string> constructed;
+    std::vector<std::string> *vocab = nullptr;
+
+    if (cmdline.size() == 0 ||
+        (cmdline.size() == 1 && line.back() != ' ') ||
+        cmdline[0] == "help")
+    {
+        vocab = &commands;
+    }
+    else if (cmdline[0] == "motion")
+    {
+        for (auto it = MCT_motions.begin(); it != MCT_motions.end(); ++it)
+        {
+            if ((cmdline.size() == 1 && line.back() == ' ') ||
+                (cmdline.size() == 2 && line.back() != ' '))
+            { // motionGroup
+                {
+                    constructed.push_back(it->first);
+                }
+            }
+            else if ((cmdline.size() == 2 && line.back() == ' ') ||
+                     (cmdline.size() == 3 && line.back() != ' '))
+            { // motionNumber
+                if (it->first == cmdline[1])
+                {
+                    for (int i = 0; i < it->second; i++)
+                    {
+                        constructed.push_back(std::to_string(i));
+                    }
+                    break;
+                }
+            }
+            else if (cmdline.size() <= 4)
+            { // priority
+                for (int i = 0; i < 4; i++)
+                {
+                    constructed.push_back(std::to_string(i));
+                }
+            }
+        }
+
+        vocab = &constructed;
+    }
+    else if (cmdline[0] == "expression")
+    {
+        vocab = &MCT_expressions;
+    }
+    else if (cmdline[0] == "set")
+    {
+        if ((cmdline.size() % 2 == 0 && line.back() != ' ') ||
+            (cmdline.size() % 2 == 1 && line.back() == ' '))
+        {
+            vocab = &live2dParams;
+        }
+    }
+    else if (cmdline[0] == "clear")
+    {
+        constructed.push_back("all");
+        for (auto const &entry : *MCT_overrideMap)
+        {
+            constructed.push_back(entry.first);
+        }
+        vocab = &constructed;
+    }
+
+    if (!vocab)
+    {
+        return nullptr;
+    }
+
+    std::string text(textCStr);
+    std::vector<std::string> matches;
+    std::copy_if(vocab->begin(), vocab->end(), std::back_inserter(matches),
+                 [&text](const std::string &s)
+                 {
+                     return (s.size() >= text.size() &&
+                             s.compare(0, text.size(), text) == 0);
+                 });
+
+    if (matches.empty())
+    {
+        return nullptr;
+    }
+
+    char** array =
+        static_cast<char**>(malloc((2 + matches.size()) * sizeof(*array)));
+    array[0] = strdup(longest_common_prefix(text, matches).c_str());
+    size_t ptr = 1;
+    for (const auto& m : matches) {
+        array[ptr++] = strdup(m.c_str());
+    }
+    array[ptr] = nullptr;
+    return array;
+}
+
+MouseCursorTracker::MouseCursorTracker(std::string cfgPath,
+                                       std::vector<std::pair<std::string, int> > motions,
+                                       std::vector<std::string> expressions)
     : m_stop(false)
 {
+    m_motionPriority = MotionPriority::none;
+    m_motionNumber = 0;
+
+
     parseConfig(cfgPath);
     m_xdo = xdo_new(nullptr);
 
@@ -69,6 +276,11 @@ MouseCursorTracker::MouseCursorTracker(std::string cfgPath)
     }
 
     m_getVolumeThread = std::thread(&MouseCursorTracker::audioLoop, this);
+    m_parseCommandThread = std::thread(&MouseCursorTracker::cliLoop, this);
+
+    MCT_motions = motions;
+    MCT_expressions = expressions;
+    MCT_overrideMap = &m_overrideMap;
 }
 
 void MouseCursorTracker::audioLoop(void)
@@ -89,10 +301,167 @@ void MouseCursorTracker::audioLoop(void)
     delete[] buf;
 }
 
+void MouseCursorTracker::cliLoop(void)
+{
+    rl_catch_signals = 0;
+    rl_attempted_completion_function = cliCompletionFunction;
+    while (!m_stop)
+    {
+        char *buf = readline(">> ");
+
+        if (buf)
+        {
+            std::string cmdline(buf);
+            free(buf);
+            processCommand(cmdline);
+        }
+        else
+        {
+            std::cout << "Exiting CLI loop. Use Ctrl+C to exit the whole process." << std::endl;
+            stop();
+        }
+    }
+}
+
+
+void MouseCursorTracker::processCommand(std::string cmdline)
+{
+    auto cmdSplit = split(cmdline);
+
+    if (cmdSplit.size() > 0)
+    {
+        add_history(cmdline.c_str());
+
+        if (cmdSplit[0] == "help")
+        {
+            if (cmdSplit.size() == 1)
+            {
+                std::cout << "Available commands: motion set clear\n"
+                          << "Type \"help <command>\" for more help" << std::endl;
+            }
+            else if (cmdSplit[1] == "motion")
+            {
+                std::cout << "motion <motionGroup> <motionNumber> [<priority>]\n"
+                          << "motionGroup: The motion name in the .model3.json file\n"
+                          << "motionNumber: The index of this motion in the .model3.json file, 0-indexed\n"
+                          << "priority: 0 = none, 1 = idle, 2 = normal, 3 = force (default normal)" << std::endl;
+            }
+            else if (cmdSplit[1] == "expression")
+            {
+                std::cout << "expression <expressionName>\n"
+                          << "expressionName: Name of expression in the .model3.json file" << std::endl;
+            }
+            else if (cmdSplit[1] == "set")
+            {
+                std::cout << "set <param1> <value1> [<param2> <value2> ...]\n"
+                          << "Set parameter value. Overrides any tracking."
+                          << "See live2D documentation for full list of params" << std::endl;
+            }
+            else if (cmdSplit[1] == "clear")
+            {
+                std::cout << "clear <param1> [<param2> ...]\n"
+                          << "Clear parameter value. Re-enables tracking if it was overridden by \"set\"\n"
+                          << "You can also use \"clear all\" to clear everything" << std::endl;
+            }
+            else
+            {
+                std::cout << "Unrecognized command" << std::endl;
+            }
+        }
+        else if (cmdSplit[0] == "motion")
+        {
+            if (cmdSplit.size() == 3 || cmdSplit.size() == 4)
+            {
+                std::unique_lock<std::mutex> lock(m_motionMutex, std::defer_lock);
+                lock.lock();
+                m_motionGroup = cmdSplit[1];
+                try
+                {
+                    m_motionNumber = std::stoi(cmdSplit[2]);
+                    if (cmdSplit.size() == 4)
+                    {
+                        m_motionPriority = static_cast<MotionPriority>(std::stoi(cmdSplit[3]));
+                    }
+                    else
+                    {
+                        m_motionPriority = MotionPriority::normal;
+                    }
+                }
+                catch (const std::exception &e)
+                {
+                    std::cerr << "std::stoi failed" << std::endl;
+                }
+                lock.unlock();
+            }
+            else
+            {
+                std::cerr << "Incorrect command, expecting 2 or 3 arguments" << std::endl;
+                std::cerr << "motion motionGroup motionNumber [motionPriority]" << std::endl;
+            }
+        }
+        else if (cmdSplit[0] == "expression")
+        {
+            if (cmdSplit.size() == 2)
+            {
+                std::unique_lock<std::mutex> lock(m_motionMutex, std::defer_lock);
+                lock.lock();
+                m_expression = cmdSplit[1];
+                lock.unlock();
+            }
+            else
+            {
+                std::cerr << "Incorrect command, expecting 1 argument: expressionName" << std::endl;
+            }
+        }
+        else if (cmdSplit[0] == "set")
+        {
+            if (cmdSplit.size() % 2 != 1)
+            {
+                // "set param1 value1 param2 value2 ..."
+                std::cerr << "Incorrect number of arguments for command 'set'" << std::endl;
+            }
+            for (std::size_t i = 1; i < cmdSplit.size(); i += 2)
+            {
+                try
+                {
+                    m_overrideMap[cmdSplit[i]] = std::stod(cmdSplit[i + 1]);
+                }
+                catch (const std::exception &e)
+                {
+                    std::cerr << "std::stod failed" << std::endl;
+                }
+
+                std::cerr << "Debug: setting " << cmdSplit[i] << std::endl;
+            }
+        }
+        else if (cmdSplit[0] == "clear")
+        {
+            for (std::size_t i = 1; i < cmdSplit.size(); i++)
+            {
+                if (cmdSplit[i] == "all")
+                {
+                    m_overrideMap.clear();
+                    break;
+                }
+                std::size_t removed = m_overrideMap.erase(cmdSplit[i]);
+                if (removed == 0)
+                {
+                    std::cerr << "Warning: key " << cmdSplit[i] << " not found" << std::endl;
+                }
+            }
+        }
+        else
+        {
+            std::cerr << "Unknown command" << std::endl;
+        }
+    }
+}
+
 MouseCursorTracker::~MouseCursorTracker()
 {
     xdo_free(m_xdo);
     m_getVolumeThread.join();
+    m_parseCommandThread.join();
     pa_simple_free(m_pulse);
 }
 
@@ -101,47 +470,42 @@ void MouseCursorTracker::stop(void)
     m_stop = true;
 }
 
-MouseCursorTracker::Params MouseCursorTracker::getParams(void) const
+MouseCursorTracker::Params MouseCursorTracker::getParams(void)
 {
     Params params = Params();
 
-    int xOffset = m_curPos.x - m_cfg.middle.x;
-    int leftRange = m_cfg.middle.x - m_cfg.left;
-    int rightRange = m_cfg.right - m_cfg.middle.x;
+    int xOffset = m_curPos.x - m_cfg.origin.x;
+    int leftRange = m_cfg.origin.x - m_cfg.left;
+    int rightRange = m_cfg.right - m_cfg.origin.x;
 
     if (xOffset > 0) // i.e. to the right
     {
-        params.faceXAngle = 30.0 * xOffset / rightRange;
+        params.live2d["ParamAngleX"] = 30.0 * xOffset / rightRange;
     }
     else // to the left
     {
-        params.faceXAngle = 30.0 * xOffset / leftRange;
+        params.live2d["ParamAngleX"] = 30.0 * xOffset / leftRange;
     }
 
-    int yOffset = m_curPos.y - m_cfg.middle.y;
-    int topRange = m_cfg.middle.y - m_cfg.top;
-    int bottomRange = m_cfg.bottom - m_cfg.middle.y;
+    int yOffset = m_curPos.y - m_cfg.origin.y;
+    int topRange = m_cfg.origin.y - m_cfg.top;
+    int bottomRange = m_cfg.bottom - m_cfg.origin.y;
 
     if (yOffset > 0) // downwards
     {
-        params.faceYAngle = -30.0 * yOffset / bottomRange;
+        params.live2d["ParamAngleY"] = -30.0 * yOffset / bottomRange;
     }
     else // upwards
     {
-        params.faceYAngle = -30.0 * yOffset / topRange;
+        params.live2d["ParamAngleY"] = -30.0 * yOffset / topRange;
     }
 
-    params.faceZAngle = 0;
-
-    params.leftEyeOpenness = 1;
-    params.rightEyeOpenness = 1;
-
     params.autoBlink = m_cfg.autoBlink;
     params.autoBreath = m_cfg.autoBreath;
-    params.randomMotion = m_cfg.randomMotion;
+    params.randomIdleMotion = m_cfg.randomIdleMotion;
     params.useLipSync = m_cfg.useLipSync;
 
-    params.mouthForm = m_cfg.mouthForm;
+    params.live2d["ParamMouthForm"] = m_cfg.mouthForm;
 
     if (m_cfg.useLipSync)
     {
@@ -156,10 +520,38 @@ MouseCursorTracker::Params MouseCursorTracker::getParams(void) const
         }
     }
 
+    // Don't block in getParams()
+    std::unique_lock<std::mutex> lock(m_motionMutex, std::try_to_lock);
+    if (lock.owns_lock())
+    {
+        if (m_motionPriority != MotionPriority::none)
+        {
+            params.motionPriority = m_motionPriority;
+            params.motionGroup = m_motionGroup;
+            params.motionNumber = m_motionNumber;
+
+            m_motionPriority = MotionPriority::none;
+            m_motionGroup = "";
+            m_motionNumber = 0;
+        }
+        params.expression = m_expression;
+        m_expression = "";
+        lock.unlock();
+    }
 
     // Leave everything else as zero
 
 
+    // Process overrides
+    for (auto const &x : m_overrideMap)
+    {
+        std::string key = x.first;
+        double val = x.second;
+
+        params.live2d[key] = val;
+    }
+
+
     return params;
 }
 
@@ -230,11 +622,11 @@ void MouseCursorTracker::parseConfig(std::string cfgPath)
                         throw std::runtime_error("Error parsing autoBreath");
                     }
                 }
-                else if (paramName == "randomMotion")
+                else if (paramName == "randomIdleMotion")
                 {
-                    if (!(ss >> m_cfg.randomMotion))
+                    if (!(ss >> m_cfg.randomIdleMotion))
                     {
-                        throw std::runtime_error("Error parsing randomMotion");
+                        throw std::runtime_error("Error parsing randomIdleMotion");
                     }
                 }
                 else if (paramName == "useLipSync")
@@ -279,18 +671,18 @@ void MouseCursorTracker::parseConfig(std::string cfgPath)
                         throw std::runtime_error("Error parsing screen");
                     }
                 }
-                else if (paramName == "middle_x")
+                else if (paramName == "origin_x")
                 {
-                    if (!(ss >> m_cfg.middle.x))
+                    if (!(ss >> m_cfg.origin.x))
                     {
-                        throw std::runtime_error("Error parsing middle_x");
+                        throw std::runtime_error("Error parsing origin_x");
                     }
                 }
-                else if (paramName == "middle_y")
+                else if (paramName == "origin_y")
                 {
-                    if (!(ss >> m_cfg.middle.y))
+                    if (!(ss >> m_cfg.origin.y))
                     {
-                        throw std::runtime_error("Error parsing middle_y");
+                        throw std::runtime_error("Error parsing origin_y");
                     }
                 }
                 else if (paramName == "top")
@@ -335,7 +727,7 @@ void MouseCursorTracker::populateDefaultConfig(void)
     m_cfg.sleepMs = 5;
     m_cfg.autoBlink = true;
     m_cfg.autoBreath = true;
-    m_cfg.randomMotion = false;
+    m_cfg.randomIdleMotion = false;
     m_cfg.useLipSync = true;
     m_cfg.lipSyncGain = 10;
     m_cfg.lipSyncCutOff = 0.15;
@@ -347,6 +739,6 @@ void MouseCursorTracker::populateDefaultConfig(void)
     m_cfg.right = 1919;  // These will be the full screen for 1920x1080
 
     m_cfg.screen = 0;
-    m_cfg.middle = {1600, 870}; // Somewhere near the bottom right
+    m_cfg.origin = {1600, 870}; // Somewhere near the bottom right
 }