Add command-line interface to control model
[mouse-tracker-for-cubism.git] / example / demo.patch
1 diff -pruN --exclude build ./demo_clean/CMakeLists.txt ./demo_dev/CMakeLists.txt
2 --- ./demo_clean/CMakeLists.txt 2020-10-01 22:47:25.846828066 +0100
3 +++ ./demo_dev/CMakeLists.txt   2020-10-01 23:29:15.530233484 +0100
4 @@ -3,7 +3,7 @@ cmake_minimum_required(VERSION 3.16)
5  # Set app name.
6  set(APP_NAME Demo)
7  # Set directory paths.
8 -set(SDK_ROOT_PATH ${CMAKE_CURRENT_SOURCE_DIR}/../../../..)
9 +set(SDK_ROOT_PATH ${CMAKE_CURRENT_SOURCE_DIR}/../CubismSdkForNative-4-r.1)
10  set(CORE_PATH ${SDK_ROOT_PATH}/Core)
11  set(FRAMEWORK_PATH ${SDK_ROOT_PATH}/Framework)
12  set(THIRD_PARTY_PATH ${SDK_ROOT_PATH}/Samples/OpenGL/thirdParty)
13 @@ -32,7 +32,7 @@ set(GLFW_INSTALL OFF CACHE BOOL "" FORCE
14  set(BUILD_UTILS OFF CACHE BOOL "" FORCE)
15  
16  # Specify version of compiler.
17 -set(CMAKE_CXX_STANDARD 14)
18 +set(CMAKE_CXX_STANDARD 17)
19  set(CMAKE_CXX_STANDARD_REQUIRED ON)
20  set(CMAKE_CXX_EXTENSIONS OFF)
21  
22 @@ -64,6 +64,9 @@ target_link_libraries(Framework Live2DCu
23  # Find opengl libraries.
24  find_package(OpenGL REQUIRED)
25  
26 +# Add MouseTrackerForCubism
27 +add_subdirectory(../.. MouseTrackerForCubism_build)
28 +
29  # Make executable app.
30  add_executable(${APP_NAME})
31  # Add source files.
32 @@ -73,9 +76,11 @@ target_link_libraries(${APP_NAME}
33    Framework
34    glfw
35    ${OPENGL_LIBRARIES}
36 +  MouseTrackerForCubism
37 +  stdc++fs
38  )
39  # Specify include directories.
40 -target_include_directories(${APP_NAME} PRIVATE ${STB_PATH})
41 +target_include_directories(${APP_NAME} PRIVATE ${STB_PATH} ${CMAKE_CURRENT_SOURCE_DIR}/../../include)
42  
43  # Copy resource directory to build directory.
44  add_custom_command(
45 diff -pruN --exclude build ./demo_clean/scripts/make_gcc ./demo_dev/scripts/make_gcc
46 --- ./demo_clean/scripts/make_gcc       2020-10-01 22:47:25.854827921 +0100
47 +++ ./demo_dev/scripts/make_gcc 2020-10-12 03:42:07.847955578 +0100
48 @@ -10,4 +10,4 @@ BUILD_PATH=$SCRIPT_PATH/../build/make_gc
49  cmake -S "$CMAKE_PATH" \
50    -B "$BUILD_PATH" \
51    -D CMAKE_BUILD_TYPE=Release
52 -cd "$BUILD_PATH" && make
53 +cd "$BUILD_PATH" && make -j4
54 diff -pruN --exclude build ./demo_clean/src/CMakeLists.txt ./demo_dev/src/CMakeLists.txt
55 --- ./demo_clean/src/CMakeLists.txt     2020-10-01 22:47:25.850827994 +0100
56 +++ ./demo_dev/src/CMakeLists.txt       2020-10-01 22:47:24.842846271 +0100
57 @@ -19,6 +19,4 @@ target_sources(${APP_NAME}
58      ${CMAKE_CURRENT_SOURCE_DIR}/LAppView.cpp
59      ${CMAKE_CURRENT_SOURCE_DIR}/LAppView.hpp
60      ${CMAKE_CURRENT_SOURCE_DIR}/main.cpp
61 -    ${CMAKE_CURRENT_SOURCE_DIR}/TouchManager.cpp
62 -    ${CMAKE_CURRENT_SOURCE_DIR}/TouchManager.hpp
63  )
64 diff -pruN --exclude build ./demo_clean/src/LAppDefine.cpp ./demo_dev/src/LAppDefine.cpp
65 --- ./demo_clean/src/LAppDefine.cpp     2020-10-01 22:47:25.850827994 +0100
66 +++ ./demo_dev/src/LAppDefine.cpp       2020-10-18 04:59:13.238452938 +0100
67 @@ -61,11 +61,11 @@ namespace LAppDefine {
68      const csmInt32 PriorityForce = 3;
69  
70      // デバッグ用ログの表示オプション
71 -    const csmBool DebugLogEnable = true;
72 +    const csmBool DebugLogEnable = false;
73      const csmBool DebugTouchLogEnable = false;
74  
75      // Frameworkから出力するログのレベル設定
76 -    const CubismFramework::Option::LogLevel CubismLoggingLevel = CubismFramework::Option::LogLevel_Verbose;
77 +    const CubismFramework::Option::LogLevel CubismLoggingLevel = CubismFramework::Option::LogLevel_Warning;
78  
79      // デフォルトのレンダーターゲットサイズ
80      const csmInt32 RenderTargetWidth = 1900;
81 diff -pruN --exclude build ./demo_clean/src/LAppDelegate.cpp ./demo_dev/src/LAppDelegate.cpp
82 --- ./demo_clean/src/LAppDelegate.cpp   2020-10-01 22:47:25.850827994 +0100
83 +++ ./demo_dev/src/LAppDelegate.cpp     2020-10-01 22:47:24.698848890 +0100
84 @@ -45,7 +45,8 @@ void LAppDelegate::ReleaseInstance()
85      s_instance = NULL;
86  }
87  
88 -bool LAppDelegate::Initialize()
89 +bool LAppDelegate::Initialize(int initWindowWidth, int initWindowHeight,
90 +                              const char *windowTitle)
91  {
92      if (DebugLogEnable)
93      {
94 @@ -63,7 +64,13 @@ bool LAppDelegate::Initialize()
95      }
96  
97      // Windowの生成_
98 -    _window = glfwCreateWindow(RenderTargetWidth, RenderTargetHeight, "SAMPLE", NULL, NULL);
99 +    _window = glfwCreateWindow(
100 +        initWindowWidth ? initWindowWidth : RenderTargetWidth,
101 +        initWindowHeight ? initWindowHeight : RenderTargetHeight,
102 +        windowTitle ? windowTitle : "SAMPLE",
103 +        NULL,
104 +        NULL);
105 +
106      if (_window == NULL)
107      {
108          if (DebugLogEnable)
109 @@ -95,10 +102,6 @@ bool LAppDelegate::Initialize()
110      glEnable(GL_BLEND);
111      glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
112  
113 -    //コールバック関数の登録
114 -    glfwSetMouseButtonCallback(_window, EventHandler::OnMouseCallBack);
115 -    glfwSetCursorPosCallback(_window, EventHandler::OnMouseCallBack);
116 -
117      // ウィンドウサイズ記憶
118      int width, height;
119      glfwGetWindowSize(LAppDelegate::GetInstance()->GetWindow(), &width, &height);
120 @@ -111,8 +114,6 @@ bool LAppDelegate::Initialize()
121      // Cubism3の初期化
122      InitializeCubism();
123  
124 -    SetRootDirectory();
125 -
126      //load model
127      LAppLive2DManager::GetInstance();
128  
129 @@ -214,49 +215,6 @@ void LAppDelegate::InitializeCubism()
130      LAppPal::UpdateTime();
131  }
132  
133 -void LAppDelegate::OnMouseCallBack(GLFWwindow* window, int button, int action, int modify)
134 -{
135 -    if (_view == NULL)
136 -    {
137 -        return;
138 -    }
139 -    if (GLFW_MOUSE_BUTTON_LEFT != button)
140 -    {
141 -        return;
142 -    }
143 -
144 -    if (GLFW_PRESS == action)
145 -    {
146 -        _captured = true;
147 -        _view->OnTouchesBegan(_mouseX, _mouseY);
148 -    }
149 -    else if (GLFW_RELEASE == action)
150 -    {
151 -        if (_captured)
152 -        {
153 -            _captured = false;
154 -            _view->OnTouchesEnded(_mouseX, _mouseY);
155 -        }
156 -    }
157 -}
158 -
159 -void LAppDelegate::OnMouseCallBack(GLFWwindow* window, double x, double y)
160 -{
161 -    _mouseX = static_cast<float>(x);
162 -    _mouseY = static_cast<float>(y);
163 -
164 -    if (!_captured)
165 -    {
166 -        return;
167 -    }
168 -    if (_view == NULL)
169 -    {
170 -        return;
171 -    }
172 -
173 -    _view->OnTouchesMoved(_mouseX, _mouseY);
174 -}
175 -
176  GLuint LAppDelegate::CreateShader()
177  {
178      //バーテックスシェーダのコンパイル
179 @@ -299,29 +257,9 @@ GLuint LAppDelegate::CreateShader()
180      return programId;
181  }
182  
183 -void LAppDelegate::SetRootDirectory()
184 +void LAppDelegate::SetRootDirectory(std::string rootDir)
185  {
186 -    char path[1024];
187 -    ssize_t len = readlink("/proc/self/exe", path, 1024 - 1);
188 -
189 -    if (len != -1)
190 -    {
191 -        path[len] = '\0';
192 -    }
193 -
194 -    std::string pathString(path);
195 -
196 -    pathString = pathString.substr(0, pathString.rfind("Demo"));
197 -    Csm::csmVector<string> splitStrings = this->Split(pathString, '/');
198 -
199 -    this->_rootDirectory = "";
200 -
201 -    for(int i = 0; i < splitStrings.GetSize(); i++)
202 -    {
203 -        this->_rootDirectory = this->_rootDirectory + "/" +splitStrings[i];
204 -    }
205 -
206 -    this->_rootDirectory += "/";
207 +    this->_rootDirectory = rootDir + "/";
208  }
209  
210  Csm::csmVector<string> LAppDelegate::Split(const std::string& baseString, char delimiter)
211 diff -pruN --exclude build ./demo_clean/src/LAppDelegate.hpp ./demo_dev/src/LAppDelegate.hpp
212 --- ./demo_clean/src/LAppDelegate.hpp   2020-10-01 22:47:25.850827994 +0100
213 +++ ./demo_dev/src/LAppDelegate.hpp     2020-10-01 22:47:24.842846271 +0100
214 @@ -40,7 +40,8 @@ public:
215      /**
216      * @brief   APPに必要なものを初期化する。
217      */
218 -    bool Initialize();
219 +    bool Initialize(int initWindowWidth = 0, int initWindowHeight = 0,
220 +                    const char *windowTitle = "SAMPLE");
221  
222      /**
223      * @brief   解放する。
224 @@ -53,25 +54,6 @@ public:
225      void Run();
226  
227      /**
228 -    * @brief   OpenGL用 glfwSetMouseButtonCallback用関数。
229 -    *
230 -    * @param[in]       window            コールバックを呼んだWindow情報
231 -    * @param[in]       button            ボタン種類
232 -    * @param[in]       action            実行結果
233 -    * @param[in]       modify
234 -    */
235 -    void OnMouseCallBack(GLFWwindow* window, int button, int action, int modify);
236 -
237 -    /**
238 -    * @brief   OpenGL用 glfwSetCursorPosCallback用関数。
239 -    *
240 -    * @param[in]       window            コールバックを呼んだWindow情報
241 -    * @param[in]       x                 x座標
242 -    * @param[in]       y                 x座標
243 -    */
244 -    void OnMouseCallBack(GLFWwindow* window, double x, double y);
245 -
246 -    /**
247      * @brief シェーダーを登録する。
248      */
249      GLuint CreateShader();
250 @@ -98,8 +80,10 @@ public:
251  
252      /**
253       * @brief   ルートディレクトリを設定する。
254 +     *
255 +     * @param[in] rootDir : The root directory to set to.
256       */
257 -    void SetRootDirectory();
258 +    void SetRootDirectory(std::string rootDir);
259  
260      /**
261       * @brief   ルートディレクトリを取得する。
262 @@ -146,24 +130,3 @@ private:
263      int _windowWidth;                            ///< Initialize関数で設定したウィンドウ幅
264      int _windowHeight;                           ///< Initialize関数で設定したウィンドウ高さ
265  };
266 -
267 -class EventHandler
268 -{
269 -public:
270 -    /**
271 -    * @brief   glfwSetMouseButtonCallback用コールバック関数。
272 -    */
273 -    static void OnMouseCallBack(GLFWwindow* window, int button, int action, int modify)
274 -    {
275 -        LAppDelegate::GetInstance()->OnMouseCallBack(window, button, action, modify);
276 -    }
277 -
278 -    /**
279 -    * @brief   glfwSetCursorPosCallback用コールバック関数。
280 -    */
281 -    static void OnMouseCallBack(GLFWwindow* window, double x, double y)
282 -    {
283 -         LAppDelegate::GetInstance()->OnMouseCallBack(window, x, y);
284 -    }
285 -
286 -};
287 diff -pruN --exclude build ./demo_clean/src/LAppLive2DManager.cpp ./demo_dev/src/LAppLive2DManager.cpp
288 --- ./demo_clean/src/LAppLive2DManager.cpp      2020-10-01 22:47:25.850827994 +0100
289 +++ ./demo_dev/src/LAppLive2DManager.cpp        2020-10-02 02:00:49.961556700 +0100
290 @@ -52,9 +52,10 @@ void LAppLive2DManager::ReleaseInstance(
291  
292  LAppLive2DManager::LAppLive2DManager()
293      : _viewMatrix(NULL)
294 -    , _sceneIndex(0)
295 +    , _projScaleFactor(1.0f)
296 +    , _translateX(0.0f)
297 +    , _translateY(0.0f)
298  {
299 -    ChangeScene(_sceneIndex);
300  }
301  
302  LAppLive2DManager::~LAppLive2DManager()
303 @@ -98,26 +99,6 @@ void LAppLive2DManager::OnTap(csmFloat32
304      {
305          LAppPal::PrintLog("[APP]tap point: {x:%.2f y:%.2f}", x, y);
306      }
307 -
308 -    for (csmUint32 i = 0; i < _models.GetSize(); i++)
309 -    {
310 -        if (_models[i]->HitTest(HitAreaNameHead, x, y))
311 -        {
312 -            if (DebugLogEnable)
313 -            {
314 -                LAppPal::PrintLog("[APP]hit area: [%s]", HitAreaNameHead);
315 -            }
316 -            _models[i]->SetRandomExpression();
317 -        }
318 -        else if (_models[i]->HitTest(HitAreaNameBody, x, y))
319 -        {
320 -            if (DebugLogEnable)
321 -            {
322 -                LAppPal::PrintLog("[APP]hit area: [%s]", HitAreaNameBody);
323 -            }
324 -            _models[i]->StartRandomMotion(MotionGroupTapBody, PriorityNormal, FinishedMotion);
325 -        }
326 -    }
327  }
328  
329  void LAppLive2DManager::OnUpdate() const
330 @@ -125,7 +106,9 @@ void LAppLive2DManager::OnUpdate() const
331      CubismMatrix44 projection;
332      int width, height;
333      glfwGetWindowSize(LAppDelegate::GetInstance()->GetWindow(), &width, &height);
334 -    projection.Scale(1.0f, static_cast<float>(width) / static_cast<float>(height));
335 +    projection.Scale(_projScaleFactor,
336 +                     _projScaleFactor * static_cast<float>(width) / static_cast<float>(height));
337 +    projection.Translate(_translateX, _translateY);
338  
339      if (_viewMatrix != NULL)
340      {
341 @@ -148,26 +131,10 @@ void LAppLive2DManager::OnUpdate() const
342      }
343  }
344  
345 -void LAppLive2DManager::NextScene()
346 -{
347 -    csmInt32 no = (_sceneIndex + 1) % ModelDirSize;
348 -    ChangeScene(no);
349 -}
350 -
351 -void LAppLive2DManager::ChangeScene(Csm::csmInt32 index)
352 +void LAppLive2DManager::SetModel(std::string modelName)
353  {
354 -    _sceneIndex = index;
355 -    if (DebugLogEnable)
356 -    {
357 -        LAppPal::PrintLog("[APP]model index: %d", _sceneIndex);
358 -    }
359 -
360 -    // ModelDir[]に保持したディレクトリ名から
361 -    // model3.jsonのパスを決定する.
362 -    // ディレクトリ名とmodel3.jsonの名前を一致させておくこと.
363 -    std::string model = ModelDir[index];
364 -    std::string modelPath = LAppDelegate::GetInstance()->GetRootDirectory() + ResourcesPath + model + "/";
365 -    std::string modelJsonName = ModelDir[index];
366 +    std::string modelPath = LAppDelegate::GetInstance()->GetRootDirectory() + ResourcesPath + modelName + "/";
367 +    std::string modelJsonName = modelName;
368      modelJsonName += ".model3.json";
369  
370      ReleaseAllModel();
371 @@ -215,3 +182,20 @@ csmUint32 LAppLive2DManager::GetModelNum
372  {
373      return _models.GetSize();
374  }
375 +
376 +void LAppLive2DManager::SetTracker(MouseCursorTracker *tracker)
377 +{
378 +    for (auto it = _models.Begin(); it != _models.End(); ++it)
379 +    {
380 +        (*it)->SetTracker(tracker);
381 +    }
382 +}
383 +
384 +void LAppLive2DManager::SetProjectionScaleTranslate(float scaleFactor,
385 +                                                    float translateX,
386 +                                                    float translateY)
387 +{
388 +    _projScaleFactor = scaleFactor;
389 +    _translateX = translateX;
390 +    _translateY = translateY;
391 +}
392 diff -pruN --exclude build ./demo_clean/src/LAppLive2DManager.hpp ./demo_dev/src/LAppLive2DManager.hpp
393 --- ./demo_clean/src/LAppLive2DManager.hpp      2020-10-01 22:47:25.846828066 +0100
394 +++ ./demo_dev/src/LAppLive2DManager.hpp        2020-10-01 23:36:24.583055381 +0100
395 @@ -6,12 +6,15 @@
396   */
397  #pragma once
398  
399 +#include <string>
400  #include <CubismFramework.hpp>
401  #include <Math/CubismMatrix44.hpp>
402  #include <Type/csmVector.hpp>
403  
404  class LAppModel;
405  
406 +class MouseCursorTracker;
407 +
408  /**
409  * @brief サンプルアプリケーションにおいてCubismModelを管理するクラス<br>
410  *         モデル生成と破棄、タップイベントの処理、モデル切り替えを行う。
411 @@ -72,16 +75,12 @@ public:
412      void OnUpdate() const;
413  
414      /**
415 -    * @brief   次のシーンに切り替える<br>
416 -    *           サンプルアプリケーションではモデルセットの切り替えを行う。
417 -    */
418 -    void NextScene();
419 -
420 -    /**
421 -    * @brief   シーンを切り替える<br>
422 -    *           サンプルアプリケーションではモデルセットの切り替えを行う。
423 -    */
424 -    void ChangeScene(Csm::csmInt32 index);
425 +     * @brief Set model data
426 +     *
427 +     * @param[in] modelName : Name of model, should be the same for both
428 +     *                        the directory and the model3.json file
429 +     */
430 +    void SetModel(std::string modelName);
431  
432      /**
433       * @brief   モデル個数を得る
434 @@ -89,6 +88,24 @@ public:
435       */
436      Csm::csmUint32 GetModelNum() const;
437  
438 +    /**
439 +     * @brief Set the pointer to the MouseCursorTracker instance
440 +     *
441 +     * @param[in] tracker : Pointer to MouseCursorTracker instance
442 +     */
443 +    void SetTracker(MouseCursorTracker *tracker);
444 +
445 +    /**
446 +     * @brief Set projection scale factor and translation parameters
447 +     *
448 +     * @param[in] scaleFactor : Scale factor applied in both X and Y directions
449 +     * @param[in] translateX : Translation in X direction
450 +     * @param[in] translateY : Translation in Y direction
451 +     */
452 +    void SetProjectionScaleTranslate(float scaleFactor,
453 +                                     float translateX,
454 +                                     float translateY);
455 +
456  private:
457      /**
458      * @brief  コンストラクタ
459 @@ -102,5 +119,8 @@ private:
460  
461      Csm::CubismMatrix44*        _viewMatrix; ///< モデル描画に用いるView行列
462      Csm::csmVector<LAppModel*>  _models; ///< モデルインスタンスのコンテナ
463 -    Csm::csmInt32               _sceneIndex; ///< 表示するシーンのインデックス値
464 +
465 +    float _projScaleFactor;
466 +    float _translateX;
467 +    float _translateY;
468  };
469 diff -pruN --exclude build ./demo_clean/src/LAppModel.cpp ./demo_dev/src/LAppModel.cpp
470 --- ./demo_clean/src/LAppModel.cpp      2020-10-01 22:47:25.850827994 +0100
471 +++ ./demo_dev/src/LAppModel.cpp        2020-10-18 09:26:08.998822685 +0100
472 @@ -21,6 +21,10 @@
473  #include "LAppTextureManager.hpp"
474  #include "LAppDelegate.hpp"
475  
476 +#include "mouse_cursor_tracker.h"
477 +
478 +#include <iostream>
479 +
480  using namespace Live2D::Cubism::Framework;
481  using namespace Live2D::Cubism::Framework::DefaultParameterId;
482  using namespace LAppDefine;
483 @@ -49,6 +53,7 @@ LAppModel::LAppModel()
484      : CubismUserModel()
485      , _modelSetting(NULL)
486      , _userTimeSeconds(0.0f)
487 +    , _tracker(nullptr)
488  {
489      if (DebugLogEnable)
490      {
491 @@ -335,59 +340,110 @@ void LAppModel::Update()
492      const csmFloat32 deltaTimeSeconds = LAppPal::GetDeltaTime();
493      _userTimeSeconds += deltaTimeSeconds;
494  
495 -    _dragManager->Update(deltaTimeSeconds);
496 -    _dragX = _dragManager->GetX();
497 -    _dragY = _dragManager->GetY();
498 -
499 -    // モーションによるパラメータ更新の有無
500 -    csmBool motionUpdated = false;
501 -
502 -    //-----------------------------------------------------------------
503 -    _model->LoadParameters(); // 前回セーブされた状態をロード
504 -    if (_motionManager->IsFinished())
505 -    {
506 -        // モーションの再生がない場合、待機モーションの中からランダムで再生する
507 -        StartRandomMotion(MotionGroupIdle, PriorityIdle);
508 -    }
509 -    else
510 +    if (_tracker)
511      {
512 -        motionUpdated = _motionManager->UpdateMotion(_model, deltaTimeSeconds); // モーションを更新
513 -    }
514 -    _model->SaveParameters(); // 状態を保存
515 -    //-----------------------------------------------------------------
516 +        auto idMan = CubismFramework::GetIdManager();
517 +        auto params = _tracker->getParams();
518  
519 -    // まばたき
520 -    if (!motionUpdated)
521 -    {
522 -        if (_eyeBlink != NULL)
523 +        _model->LoadParameters(); // 前回セーブされた状態をロード
524 +
525 +        int paramsMotionPriority = static_cast<int>(params.motionPriority);
526 +
527 +        if (paramsMotionPriority != PriorityNone)
528          {
529 -            // メインモーションの更新がないとき
530 -            _eyeBlink->UpdateParameters(_model, deltaTimeSeconds); // 目パチ
531 +            StartMotion(params.motionGroup.c_str(), params.motionNumber,
532 +                        paramsMotionPriority);
533 +        }
534 +        else if (params.randomIdleMotion && _motionManager->IsFinished())
535 +        {
536 +            // モーションの再生がない場合、待機モーションの中からランダムで再生する
537 +            StartRandomMotion(MotionGroupIdle, PriorityIdle);
538          }
539 -    }
540  
541 -    if (_expressionManager != NULL)
542 -    {
543 -        _expressionManager->UpdateMotion(_model, deltaTimeSeconds); // 表情でパラメータ更新(相対変化)
544 -    }
545 +        // FIXME pose does not return to normal after motion
546 +        // if we don't have randomIdleMotion set
547 +        else
548 +        {
549 +            _motionManager->UpdateMotion(_model, deltaTimeSeconds); // モーションを更新
550 +        }
551 +        _model->SaveParameters(); // 状態を保存
552  
553 -    //ドラッグによる変化
554 -    //ドラッグによる顔の向きの調整
555 -    _model->AddParameterValue(_idParamAngleX, _dragX * 30); // -30から30の値を加える
556 -    _model->AddParameterValue(_idParamAngleY, _dragY * 30);
557 -    _model->AddParameterValue(_idParamAngleZ, _dragX * _dragY * -30);
558 +        if (params.expression != "")
559 +        {
560 +            SetExpression(params.expression.c_str());
561 +        }
562 +        if (_expressionManager != NULL)
563 +        {
564 +            _expressionManager->UpdateMotion(_model, deltaTimeSeconds); // 表情でパラメータ更新(相対変化)
565 +        }
566  
567 -    //ドラッグによる体の向きの調整
568 -    _model->AddParameterValue(_idParamBodyAngleX, _dragX * 10); // -10から10の値を加える
569 +        bool autoBlink = params.autoBlink && _eyeBlink;
570 +        auto eyeLOpenIt = params.live2d.find("ParamEyeLOpen");
571 +        auto eyeROpenIt = params.live2d.find("ParamEyeROpen");
572  
573 -    //ドラッグによる目の向きの調整
574 -    _model->AddParameterValue(_idParamEyeBallX, _dragX); // -1から1の値を加える
575 -    _model->AddParameterValue(_idParamEyeBallY, _dragY);
576 +        if (autoBlink)
577 +        {
578 +            // Handle blink first
579 +            _eyeBlink->UpdateParameters(_model, deltaTimeSeconds);
580 +        }
581  
582 -    // 呼吸など
583 -    if (_breath != NULL)
584 -    {
585 -        _breath->UpdateParameters(_model, deltaTimeSeconds);
586 +        if (eyeLOpenIt != params.live2d.end())
587 +        {
588 +            // If value specified, override blinking
589 +            _model->SetParameterValue(idMan->GetId("ParamEyeLOpen"),
590 +                                      eyeLOpenIt->second);
591 +        }
592 +        else if (!autoBlink)
593 +        {
594 +            // If no value specified and no auto blink, set to 1
595 +            _model->SetParameterValue(idMan->GetId("ParamEyeLOpen"), 1);
596 +
597 +        }
598 +
599 +        if (eyeROpenIt != params.live2d.end())
600 +        {
601 +            _model->SetParameterValue(idMan->GetId("ParamEyeROpen"),
602 +                                      eyeROpenIt->second);
603 +        }
604 +        else if (!autoBlink)
605 +        {
606 +            _model->SetParameterValue(idMan->GetId("ParamEyeROpen"), 1);
607 +        }
608 +
609 +
610 +        if (params.useLipSync && _lipSync)
611 +        {
612 +            csmFloat32 value = params.lipSyncParam; // 0 to 1
613 +
614 +            for (csmUint32 i = 0; i < _lipSyncIds.GetSize(); ++i)
615 +            {
616 +                _model->AddParameterValue(_lipSyncIds[i], value, 0.8f);
617 +            }
618 +        }
619 +        else
620 +        {
621 +            _model->SetParameterValue(idMan->GetId("ParamMouthOpenY"),
622 +                                      params.live2d["ParamMouthOpenY"]);
623 +        }
624 +
625 +        for (auto const &entry : params.live2d)
626 +        {
627 +            std::string key = entry.first;
628 +            double val = entry.second;
629 +
630 +            if (key != "ParamEyeLOpen" && key != "ParamEyeROpen" &&
631 +                key != "ParamMouthOpenY")
632 +            {
633 +                _model->SetParameterValue(idMan->GetId(key.c_str()), val);
634 +            }
635 +        }
636 +
637 +        if (params.autoBreath && _breath)
638 +        {
639 +            // Note: _model->LoadParameters and SaveParameters is needed
640 +            // before - see above.
641 +            _breath->UpdateParameters(_model, deltaTimeSeconds);
642 +        }
643      }
644  
645      // 物理演算の設定
646 @@ -396,17 +452,6 @@ void LAppModel::Update()
647          _physics->Evaluate(_model, deltaTimeSeconds);
648      }
649  
650 -    // リップシンクの設定
651 -    if (_lipSync)
652 -    {
653 -        csmFloat32 value = 0; // リアルタイムでリップシンクを行う場合、システムから音量を取得して0〜1の範囲で値を入力します。
654 -
655 -        for (csmUint32 i = 0; i < _lipSyncIds.GetSize(); ++i)
656 -        {
657 -            _model->AddParameterValue(_lipSyncIds[i], value, 0.8f);
658 -        }
659 -    }
660 -
661      // ポーズの設定
662      if (_pose != NULL)
663      {
664 @@ -626,3 +671,14 @@ Csm::Rendering::CubismOffscreenFrame_Ope
665  {
666      return _renderBuffer;
667  }
668 +
669 +void LAppModel::SetTracker(MouseCursorTracker *tracker)
670 +{
671 +    _tracker = tracker;
672 +}
673 +
674 +Csm::ICubismModelSetting* LAppModel::GetModelSetting(void) const
675 +{
676 +    return _modelSetting;
677 +}
678 +
679 diff -pruN --exclude build ./demo_clean/src/LAppModel.hpp ./demo_dev/src/LAppModel.hpp
680 --- ./demo_clean/src/LAppModel.hpp      2020-10-01 22:47:25.850827994 +0100
681 +++ ./demo_dev/src/LAppModel.hpp        2020-10-18 03:04:52.142045751 +0100
682 @@ -13,6 +13,7 @@
683  #include <Type/csmRectF.hpp>
684  #include <Rendering/OpenGL/CubismOffscreenSurface_OpenGLES2.hpp>
685  
686 +#include "mouse_cursor_tracker.h"
687  
688  /**
689   * @brief ユーザーが実際に使用するモデルの実装クラス<br>
690 @@ -113,6 +114,15 @@ public:
691       */
692      Csm::Rendering::CubismOffscreenFrame_OpenGLES2& GetRenderBuffer();
693  
694 +    /**
695 +     * @brief Set the pointer to the MouseCursorTracker instance
696 +     *
697 +     * @param[in] tracker : Pointer to MouseCursorTracker instance
698 +     */
699 +    void SetTracker(MouseCursorTracker *tracker);
700 +
701 +    Csm::ICubismModelSetting* GetModelSetting(void) const;
702 +
703  protected:
704      /**
705       *  @brief  モデルを描画する処理。モデルを描画する空間のView-Projection行列を渡す。
706 @@ -183,6 +193,8 @@ private:
707      const Csm::CubismId* _idParamEyeBallY; ///< パラメータID: ParamEyeBallXY
708  
709      Csm::Rendering::CubismOffscreenFrame_OpenGLES2 _renderBuffer;   ///< フレームバッファ以外の描画先
710 +
711 +    MouseCursorTracker *_tracker;
712  };
713  
714  
715 diff -pruN --exclude build ./demo_clean/src/LAppPal.cpp ./demo_dev/src/LAppPal.cpp
716 --- ./demo_clean/src/LAppPal.cpp        2020-10-01 22:47:25.850827994 +0100
717 +++ ./demo_dev/src/LAppPal.cpp  2020-10-18 04:57:43.289600308 +0100
718 @@ -6,6 +6,7 @@
719   */
720  
721  #include "LAppPal.hpp"
722 +#include <stdexcept>
723  #include <stdio.h>
724  #include <stdlib.h>
725  #include <stdarg.h>
726 @@ -36,7 +37,6 @@ csmByte* LAppPal::LoadFileAsBytes(const
727      if (stat(path, &statBuf) == 0)
728      {
729          size = statBuf.st_size;
730 -        PrintLog(path);
731      }
732  
733      std::fstream file;
734 @@ -45,10 +45,7 @@ csmByte* LAppPal::LoadFileAsBytes(const
735      file.open(path, std::ios::in | std::ios::binary);
736      if (!file.is_open())
737      {
738 -        if (DebugLogEnable)
739 -        {
740 -            PrintLog("file open error");
741 -        }
742 +        throw std::runtime_error("Failed to open file " + filePath);
743          return NULL;
744      }
745      file.read(buf, size);
746 diff -pruN --exclude build ./demo_clean/src/LAppTextureManager.cpp ./demo_dev/src/LAppTextureManager.cpp
747 --- ./demo_clean/src/LAppTextureManager.cpp     2020-10-01 22:47:25.850827994 +0100
748 +++ ./demo_dev/src/LAppTextureManager.cpp       2020-10-01 22:47:24.654849690 +0100
749 @@ -96,6 +96,46 @@ LAppTextureManager::TextureInfo* LAppTex
750  
751  }
752  
753 +LAppTextureManager::TextureInfo* LAppTextureManager::CreateTextureFromColor(
754 +    uint8_t red, uint8_t green, uint8_t blue, uint8_t alpha
755 +)
756 +{
757 +    int width = 8, height = 8;
758 +
759 +    uint8_t pixels[height][width][4];
760 +    for (std::size_t h = 0; h < height; h++)
761 +    {
762 +        for (std::size_t w = 0; w < width; w++)
763 +        {
764 +            pixels[h][w][0] = red;
765 +            pixels[h][w][1] = green;
766 +            pixels[h][w][2] = blue;
767 +            pixels[h][w][3] = alpha;
768 +        }
769 +    }
770 +
771 +    GLuint textureId;
772 +    glGenTextures(1, &textureId);
773 +    glBindTexture(GL_TEXTURE_2D, textureId);
774 +    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
775 +
776 +    glGenerateMipmap(GL_TEXTURE_2D);
777 +    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
778 +    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
779 +    glBindTexture(GL_TEXTURE_2D, 0);
780 +
781 +
782 +    LAppTextureManager::TextureInfo* textureInfo = new LAppTextureManager::TextureInfo();
783 +    textureInfo->fileName = "";
784 +    textureInfo->width = width;
785 +    textureInfo->height = height;
786 +    textureInfo->id = textureId;
787 +
788 +    _textures.PushBack(textureInfo);
789 +
790 +    return textureInfo;
791 +}
792 +
793  void LAppTextureManager::ReleaseTextures()
794  {
795      for (Csm::csmUint32 i = 0; i < _textures.GetSize(); i++)
796 diff -pruN --exclude build ./demo_clean/src/LAppTextureManager.hpp ./demo_dev/src/LAppTextureManager.hpp
797 --- ./demo_clean/src/LAppTextureManager.hpp     2020-10-01 22:47:25.846828066 +0100
798 +++ ./demo_dev/src/LAppTextureManager.hpp       2020-10-01 22:47:24.786847290 +0100
799 @@ -72,6 +72,8 @@ public:
800      */
801      TextureInfo* CreateTextureFromPngFile(std::string fileName);
802  
803 +    TextureInfo *CreateTextureFromColor(uint8_t red, uint8_t green, uint8_t blue, uint8_t alpha = 255);
804 +
805      /**
806      * @brief 画像の解放
807      *
808 diff -pruN --exclude build ./demo_clean/src/LAppView.cpp ./demo_dev/src/LAppView.cpp
809 --- ./demo_clean/src/LAppView.cpp       2020-10-01 22:47:25.850827994 +0100
810 +++ ./demo_dev/src/LAppView.cpp 2020-10-01 22:47:24.602850636 +0100
811 @@ -13,7 +13,6 @@
812  #include "LAppLive2DManager.hpp"
813  #include "LAppTextureManager.hpp"
814  #include "LAppDefine.hpp"
815 -#include "TouchManager.hpp"
816  #include "LAppSprite.hpp"
817  #include "LAppModel.hpp"
818  
819 @@ -26,8 +25,6 @@ using namespace LAppDefine;
820  LAppView::LAppView():
821      _programId(0),
822      _back(NULL),
823 -    _gear(NULL),
824 -    _power(NULL),
825      _renderSprite(NULL),
826      _renderTarget(SelectTarget_None)
827  {
828 @@ -35,8 +32,6 @@ LAppView::LAppView():
829      _clearColor[1] = 1.0f;
830      _clearColor[2] = 1.0f;
831      _clearColor[3] = 0.0f;
832 -    // タッチ関係のイベント管理
833 -    _touchManager = new TouchManager();
834  
835      // デバイス座標からスクリーン座標に変換するための
836      _deviceToScreen = new CubismMatrix44();
837 @@ -52,10 +47,7 @@ LAppView::~LAppView()
838  
839      delete _viewMatrix;
840      delete _deviceToScreen;
841 -    delete _touchManager;
842      delete _back;
843 -    delete _gear;
844 -    delete _power;
845  }
846  
847  void LAppView::Initialize()
848 @@ -97,9 +89,6 @@ void LAppView::Initialize()
849  void LAppView::Render()
850  {
851      _back->Render();
852 -    _gear->Render();
853 -    _power->Render();
854 -
855  
856      LAppLive2DManager* Live2DManager = LAppLive2DManager::GetInstance();
857  
858 @@ -139,35 +128,17 @@ void LAppView::InitializeSprite()
859      glfwGetWindowSize(LAppDelegate::GetInstance()->GetWindow(), &width, &height);
860  
861      LAppTextureManager* textureManager = LAppDelegate::GetInstance()->GetTextureManager();
862 -    const string resourcesPath = LAppDelegate::GetInstance()->GetRootDirectory() + ResourcesPath;
863  
864 -    string imageName = BackImageName;
865 -    LAppTextureManager::TextureInfo* backgroundTexture = textureManager->CreateTextureFromPngFile(resourcesPath + imageName);
866 +
867 +    LAppTextureManager::TextureInfo* backgroundTexture =
868 +        textureManager->CreateTextureFromColor(0, 255, 0);
869  
870      float x = width * 0.5f;
871      float y = height * 0.5f;
872 -    float fWidth = static_cast<float>(backgroundTexture->width * 2.0f);
873 -    float fHeight = static_cast<float>(height) * 0.95f;
874 +    float fWidth = static_cast<float>(width);
875 +    float fHeight = static_cast<float>(height);
876      _back = new LAppSprite(x, y, fWidth, fHeight, backgroundTexture->id, _programId);
877  
878 -    imageName = GearImageName;
879 -    LAppTextureManager::TextureInfo* gearTexture = textureManager->CreateTextureFromPngFile(resourcesPath + imageName);
880 -
881 -    x = static_cast<float>(width - gearTexture->width * 0.5f);
882 -    y = static_cast<float>(height - gearTexture->height * 0.5f);
883 -    fWidth = static_cast<float>(gearTexture->width);
884 -    fHeight = static_cast<float>(gearTexture->height);
885 -    _gear = new LAppSprite(x, y, fWidth, fHeight, gearTexture->id, _programId);
886 -
887 -    imageName = PowerImageName;
888 -    LAppTextureManager::TextureInfo* powerTexture = textureManager->CreateTextureFromPngFile(resourcesPath + imageName);
889 -
890 -    x = static_cast<float>(width - powerTexture->width * 0.5f);
891 -    y = static_cast<float>(powerTexture->height * 0.5f);
892 -    fWidth = static_cast<float>(powerTexture->width);
893 -    fHeight = static_cast<float>(powerTexture->height);
894 -    _power = new LAppSprite(x, y, fWidth, fHeight, powerTexture->id, _programId);
895 -
896      // 画面全体を覆うサイズ
897      x = width * 0.5f;
898      y = height * 0.5f;
899 @@ -175,52 +146,6 @@ void LAppView::InitializeSprite()
900  
901  }
902  
903 -void LAppView::OnTouchesBegan(float px, float py) const
904 -{
905 -    _touchManager->TouchesBegan(px, py);
906 -}
907 -
908 -void LAppView::OnTouchesMoved(float px, float py) const
909 -{
910 -    float viewX = this->TransformViewX(_touchManager->GetX());
911 -    float viewY = this->TransformViewY(_touchManager->GetY());
912 -
913 -    _touchManager->TouchesMoved(px, py);
914 -
915 -    LAppLive2DManager* Live2DManager = LAppLive2DManager::GetInstance();
916 -    Live2DManager->OnDrag(viewX, viewY);
917 -}
918 -
919 -void LAppView::OnTouchesEnded(float px, float py) const
920 -{
921 -    // タッチ終了
922 -    LAppLive2DManager* live2DManager = LAppLive2DManager::GetInstance();
923 -    live2DManager->OnDrag(0.0f, 0.0f);
924 -    {
925 -
926 -        // シングルタップ
927 -        float x = _deviceToScreen->TransformX(_touchManager->GetX()); // 論理座標変換した座標を取得。
928 -        float y = _deviceToScreen->TransformY(_touchManager->GetY()); // 論理座標変換した座標を取得。
929 -        if (DebugTouchLogEnable)
930 -        {
931 -            LAppPal::PrintLog("[APP]touchesEnded x:%.2f y:%.2f", x, y);
932 -        }
933 -        live2DManager->OnTap(x, y);
934 -
935 -        // 歯車にタップしたか
936 -        if (_gear->IsHit(px, py))
937 -        {
938 -            live2DManager->NextScene();
939 -        }
940 -
941 -        // 電源ボタンにタップしたか
942 -        if (_power->IsHit(px, py))
943 -        {
944 -            LAppDelegate::GetInstance()->AppEnd();
945 -        }
946 -    }
947 -}
948 -
949  float LAppView::TransformViewX(float deviceX) const
950  {
951      float screenX = _deviceToScreen->TransformX(deviceX); // 論理座標変換した座標を取得。
952 @@ -362,32 +287,4 @@ void LAppView::ResizeSprite()
953              _back->ResetRect(x, y, fWidth, fHeight);
954          }
955      }
956 -
957 -    if (_power)
958 -    {
959 -        GLuint id = _power->GetTextureId();
960 -        LAppTextureManager::TextureInfo* texInfo = textureManager->GetTextureInfoById(id);
961 -        if (texInfo)
962 -        {
963 -            x = static_cast<float>(width - texInfo->width * 0.5f);
964 -            y = static_cast<float>(texInfo->height * 0.5f);
965 -            fWidth = static_cast<float>(texInfo->width);
966 -            fHeight = static_cast<float>(texInfo->height);
967 -            _power->ResetRect(x, y, fWidth, fHeight);
968 -        }
969 -    }
970 -
971 -    if (_gear)
972 -    {
973 -        GLuint id = _gear->GetTextureId();
974 -        LAppTextureManager::TextureInfo* texInfo = textureManager->GetTextureInfoById(id);
975 -        if (texInfo)
976 -        {
977 -            x = static_cast<float>(width - texInfo->width * 0.5f);
978 -            y = static_cast<float>(height - texInfo->height * 0.5f);
979 -            fWidth = static_cast<float>(texInfo->width);
980 -            fHeight = static_cast<float>(texInfo->height);
981 -            _gear->ResetRect(x, y, fWidth, fHeight);
982 -        }
983 -    }
984  }
985 diff -pruN --exclude build ./demo_clean/src/LAppView.hpp ./demo_dev/src/LAppView.hpp
986 --- ./demo_clean/src/LAppView.hpp       2020-10-01 22:47:25.846828066 +0100
987 +++ ./demo_dev/src/LAppView.hpp 2020-10-01 22:47:24.802846999 +0100
988 @@ -14,7 +14,6 @@
989  #include "CubismFramework.hpp"
990  #include <Rendering/OpenGL/CubismOffscreenSurface_OpenGLES2.hpp>
991  
992 -class TouchManager;
993  class LAppSprite;
994  class LAppModel;
995  
996 @@ -66,30 +65,6 @@ public:
997      void ResizeSprite();
998  
999      /**
1000 -    * @brief タッチされたときに呼ばれる。
1001 -    *
1002 -    * @param[in]       pointX            スクリーンX座標
1003 -    * @param[in]       pointY            スクリーンY座標
1004 -    */
1005 -    void OnTouchesBegan(float pointX, float pointY) const;
1006 -
1007 -    /**
1008 -    * @brief タッチしているときにポインタが動いたら呼ばれる。
1009 -    *
1010 -    * @param[in]       pointX            スクリーンX座標
1011 -    * @param[in]       pointY            スクリーンY座標
1012 -    */
1013 -    void OnTouchesMoved(float pointX, float pointY) const;
1014 -
1015 -    /**
1016 -    * @brief タッチが終了したら呼ばれる。
1017 -    *
1018 -    * @param[in]       pointX            スクリーンX座標
1019 -    * @param[in]       pointY            スクリーンY座標
1020 -    */
1021 -    void OnTouchesEnded(float pointX, float pointY) const;
1022 -
1023 -    /**
1024      * @brief X座標をView座標に変換する。
1025      *
1026      * @param[in]       deviceX            デバイスX座標
1027 @@ -147,13 +122,10 @@ public:
1028      void SetRenderTargetClearColor(float r, float g, float b);
1029  
1030  private:
1031 -    TouchManager* _touchManager;                 ///< タッチマネージャー
1032      Csm::CubismMatrix44* _deviceToScreen;    ///< デバイスからスクリーンへの行列
1033      Csm::CubismViewMatrix* _viewMatrix;      ///< viewMatrix
1034      GLuint _programId;                       ///< シェーダID
1035      LAppSprite* _back;                       ///< 背景画像
1036 -    LAppSprite* _gear;                       ///< ギア画像
1037 -    LAppSprite* _power;                      ///< 電源画像
1038  
1039      // レンダリング先を別ターゲットにする方式の場合に使用
1040      LAppSprite* _renderSprite;                                  ///< モードによっては_renderBufferのテクスチャを描画
1041 diff -pruN --exclude build ./demo_clean/src/main.cpp ./demo_dev/src/main.cpp
1042 --- ./demo_clean/src/main.cpp   2020-10-01 22:47:25.846828066 +0100
1043 +++ ./demo_dev/src/main.cpp     2020-10-18 07:03:46.194220443 +0100
1044 @@ -5,18 +5,182 @@
1045   * that can be found at https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
1046   */
1047  
1048 +#include <thread>
1049 +#include <stdexcept>
1050 +#include <sstream>
1051 +#include <vector>
1052 +#include <utility>
1053 +#include <string>
1054 +
1055 +#ifdef __cpp_lib_filesystem
1056 +#include <filesystem>
1057 +namespace fs = std::filesystem;
1058 +#else
1059 +#include <experimental/filesystem>
1060 +namespace fs = std::experimental::filesystem;
1061 +#endif
1062 +
1063 +#include "ICubismModelSetting.hpp"
1064  #include "LAppDelegate.hpp"
1065 +#include "LAppLive2DManager.hpp"
1066 +#include "LAppModel.hpp"
1067 +#include "mouse_cursor_tracker.h"
1068 +
1069 +struct CmdArgs
1070 +{
1071 +    int windowWidth;
1072 +    int windowHeight;
1073 +    std::string windowTitle;
1074 +    std::string rootDir;
1075 +    float scaleFactor;
1076 +    float translateX;
1077 +    float translateY;
1078 +    std::string modelName;
1079 +    std::string cfgPath; // Path to config file for MouseCursorTracker
1080 +};
1081 +
1082 +CmdArgs parseArgv(int argc, char *argv[])
1083 +{
1084 +    // I think the command-line args are simple enough to not justify using a library...
1085 +    CmdArgs cmdArgs;
1086 +    // Set default values
1087 +    cmdArgs.windowWidth = 600;
1088 +    cmdArgs.windowHeight = 600;
1089 +    cmdArgs.windowTitle = "MouseTrackerForCubism example";
1090 +    cmdArgs.rootDir = fs::current_path();
1091 +    cmdArgs.scaleFactor = 8.0f;
1092 +    cmdArgs.translateX = 0.0f;
1093 +    cmdArgs.translateY = -2.8f;
1094 +    cmdArgs.modelName = "Haru";
1095 +    cmdArgs.cfgPath = "";
1096 +
1097 +    int i = 1;
1098 +    while (i < argc)
1099 +    {
1100 +        std::string arg = argv[i];
1101 +        std::stringstream ss;
1102 +
1103 +        if (arg == "--window-width" || arg == "-W") // capital W for consistency with height
1104 +        {
1105 +            ss << argv[i + 1];
1106 +            if (!(ss >> cmdArgs.windowWidth))
1107 +            {
1108 +                throw std::runtime_error("Invalid argument for window width");
1109 +            }
1110 +        }
1111 +        else if (arg == "--window-height" || arg == "-H") // avoiding "-h", typically for help
1112 +        {
1113 +            ss << argv[i + 1];
1114 +            if (!(ss >> cmdArgs.windowHeight))
1115 +            {
1116 +                throw std::runtime_error("Invalid argument for window height");
1117 +            }
1118 +        }
1119 +        else if (arg == "--window-title" || arg == "-t")
1120 +        {
1121 +            cmdArgs.windowTitle = argv[i + 1];
1122 +        }
1123 +        else if (arg == "--root-dir" || arg == "-d")
1124 +        {
1125 +            cmdArgs.rootDir = argv[i + 1];
1126 +        }
1127 +        else if (arg == "--scale-factor" || arg == "-f")
1128 +        {
1129 +            ss << argv[i + 1];
1130 +            if (!(ss >> cmdArgs.scaleFactor))
1131 +            {
1132 +                throw std::runtime_error("Invalid argument for scale factor");
1133 +            }
1134 +        }
1135 +        else if (arg == "--translate-x" || arg == "-x")
1136 +        {
1137 +            ss << argv[i + 1];
1138 +            if (!(ss >> cmdArgs.translateX))
1139 +            {
1140 +                throw std::runtime_error("Invalid argument for translate X");
1141 +            }
1142 +        }
1143 +        else if (arg == "--translate-y" || arg == "-y")
1144 +        {
1145 +            ss << argv[i + 1];
1146 +            if (!(ss >> cmdArgs.translateY))
1147 +            {
1148 +                throw std::runtime_error("Invalid argument for translate Y");
1149 +            }
1150 +        }
1151 +        else if (arg == "--model" || arg == "-m")
1152 +        {
1153 +            cmdArgs.modelName = argv[i + 1];
1154 +        }
1155 +        else if (arg == "--config" || arg == "-c")
1156 +        {
1157 +            cmdArgs.cfgPath = argv[i + 1];
1158 +        }
1159 +        else
1160 +        {
1161 +            throw std::runtime_error("Unrecognized argument: " + arg);
1162 +        }
1163 +
1164 +        i += 2;
1165 +    }
1166 +
1167 +    return cmdArgs;
1168 +}
1169  
1170  int main(int argc, char* argv[])
1171  {
1172 -    // create the application instance
1173 -    if (LAppDelegate::GetInstance()->Initialize() == GL_FALSE)
1174 +    auto cmdArgs = parseArgv(argc, argv);
1175 +
1176 +    LAppDelegate *delegate = LAppDelegate::GetInstance();
1177 +
1178 +    if (!delegate->Initialize(cmdArgs.windowWidth,
1179 +                              cmdArgs.windowHeight,
1180 +                              cmdArgs.windowTitle.c_str()))
1181      {
1182 -        return 1;
1183 +        throw std::runtime_error("Unable to initialize LAppDelegate");
1184      }
1185  
1186 -    LAppDelegate::GetInstance()->Run();
1187 +    delegate->SetRootDirectory(cmdArgs.rootDir);
1188 +
1189 +    LAppLive2DManager *manager = LAppLive2DManager::GetInstance();
1190 +    manager->SetModel(cmdArgs.modelName);
1191 +
1192 +    manager->SetProjectionScaleTranslate(cmdArgs.scaleFactor,
1193 +                                         cmdArgs.translateX,
1194 +                                         cmdArgs.translateY);
1195 +
1196 +    LAppModel *model = manager->GetModel(0);
1197 +    if (!model) throw std::runtime_error("model is null");
1198 +
1199 +    Live2D::Cubism::Framework::ICubismModelSetting *modelSetting = model->GetModelSetting();
1200 +    if (!modelSetting) throw std::runtime_error("modelSetting is null");
1201 +
1202 +    std::vector<std::pair<std::string, int> > motions;
1203 +    int motionGroupCount = modelSetting->GetMotionGroupCount();
1204 +    for (int i = 0; i < motionGroupCount; i++)
1205 +    {
1206 +        const char *motionGroup = modelSetting->GetMotionGroupName(i);
1207 +        int motionCount = modelSetting->GetMotionCount(motionGroup);
1208 +        motions.push_back(std::make_pair(std::string(motionGroup), motionCount));
1209 +    }
1210 +
1211 +    std::vector<std::string> expressions;
1212 +    int expCount = modelSetting->GetExpressionCount();
1213 +    for (int i = 0; i < expCount; i++)
1214 +    {
1215 +        const char *expName = modelSetting->GetExpressionName(i);
1216 +        expressions.push_back(std::string(expName));
1217 +    }
1218 +
1219 +    MouseCursorTracker tracker(cmdArgs.cfgPath, motions, expressions);
1220 +
1221 +    std::thread trackerThread(&MouseCursorTracker::mainLoop, &tracker);
1222 +    manager->SetTracker(&tracker);
1223 +
1224 +    delegate->Run();
1225 +
1226 +    tracker.stop();
1227 +    trackerThread.join();
1228  
1229      return 0;
1230  }
1231 -