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