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