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