Update video link
[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-07-12 16:16:33.999809687 +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-07-12 16:16:33.999809687 +0100
65 +++ ./demo_dev/scripts/make_gcc 2020-07-11 21:22:23.615043956 +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-07-12 16:16:33.999809687 +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-07-12 16:16:33.999809687 +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-07-12 16:16:33.999809687 +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-07-12 16:16:33.999809687 +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-07-12 16:16:33.999809687 +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-07-12 16:16:33.999809687 +0100
475 +++ ./demo_dev/src/LAppModel.cpp        2020-07-11 15:57:43.784019311 +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 @@ -174,27 +152,6 @@ void LAppModel::SetupModel(ICubismModelS
517          DeleteBuffer(buffer, path.GetRawString());
518      }
519  
520 -    //EyeBlink
521 -    if (_modelSetting->GetEyeBlinkParameterCount() > 0)
522 -    {
523 -        _eyeBlink = CubismEyeBlink::Create(_modelSetting);
524 -    }
525 -
526 -    //Breath
527 -    {
528 -        _breath = CubismBreath::Create();
529 -
530 -        csmVector<CubismBreath::BreathParameterData> breathParameters;
531 -
532 -        breathParameters.PushBack(CubismBreath::BreathParameterData(_idParamAngleX, 0.0f, 15.0f, 6.5345f, 0.5f));
533 -        breathParameters.PushBack(CubismBreath::BreathParameterData(_idParamAngleY, 0.0f, 8.0f, 3.5345f, 0.5f));
534 -        breathParameters.PushBack(CubismBreath::BreathParameterData(_idParamAngleZ, 0.0f, 10.0f, 5.5345f, 0.5f));
535 -        breathParameters.PushBack(CubismBreath::BreathParameterData(_idParamBodyAngleX, 0.0f, 4.0f, 15.5345f, 0.5f));
536 -        breathParameters.PushBack(CubismBreath::BreathParameterData(CubismFramework::GetIdManager()->GetId(ParamBreath), 0.5f, 0.5f, 3.2345f, 0.5f));
537 -
538 -        _breath->SetParameters(breathParameters);
539 -    }
540 -
541      //UserData
542      if (strcmp(_modelSetting->GetUserDataFile(), "") != 0)
543      {
544 @@ -205,24 +162,6 @@ void LAppModel::SetupModel(ICubismModelS
545          DeleteBuffer(buffer, path.GetRawString());
546      }
547  
548 -    // EyeBlinkIds
549 -    {
550 -        csmInt32 eyeBlinkIdCount = _modelSetting->GetEyeBlinkParameterCount();
551 -        for (csmInt32 i = 0; i < eyeBlinkIdCount; ++i)
552 -        {
553 -            _eyeBlinkIds.PushBack(_modelSetting->GetEyeBlinkParameterId(i));
554 -        }
555 -    }
556 -
557 -    // LipSyncIds
558 -    {
559 -        csmInt32 lipSyncIdCount = _modelSetting->GetLipSyncParameterCount();
560 -        for (csmInt32 i = 0; i < lipSyncIdCount; ++i)
561 -        {
562 -            _lipSyncIds.PushBack(_modelSetting->GetLipSyncParameterId(i));
563 -        }
564 -    }
565 -
566      //Layout
567      csmMap<csmString, csmFloat32> layout;
568      _modelSetting->GetLayoutMap(layout);
569 @@ -230,14 +169,6 @@ void LAppModel::SetupModel(ICubismModelS
570  
571      _model->SaveParameters();
572  
573 -    for (csmInt32 i = 0; i < _modelSetting->GetMotionGroupCount(); i++)
574 -    {
575 -        const csmChar* group = _modelSetting->GetMotionGroupName(i);
576 -        PreloadMotionGroup(group);
577 -    }
578 -
579 -    _motionManager->StopAllMotions();
580 -
581      _updating = false;
582      _initialized = true;
583  }
584 @@ -335,59 +266,29 @@ void LAppModel::Update()
585      const csmFloat32 deltaTimeSeconds = LAppPal::GetDeltaTime();
586      _userTimeSeconds += deltaTimeSeconds;
587  
588 -    _dragManager->Update(deltaTimeSeconds);
589 -    _dragX = _dragManager->GetX();
590 -    _dragY = _dragManager->GetY();
591 -
592 -    // モーションによるパラメータ更新の有無
593 -    csmBool motionUpdated = false;
594 -
595 -    //-----------------------------------------------------------------
596 -    _model->LoadParameters(); // 前回セーブされた状態をロード
597 -    if (_motionManager->IsFinished())
598 -    {
599 -        // モーションの再生がない場合、待機モーションの中からランダムで再生する
600 -        StartRandomMotion(MotionGroupIdle, PriorityIdle);
601 -    }
602 -    else
603 -    {
604 -        motionUpdated = _motionManager->UpdateMotion(_model, deltaTimeSeconds); // モーションを更新
605 -    }
606 -    _model->SaveParameters(); // 状態を保存
607 -    //-----------------------------------------------------------------
608 -
609 -    // まばたき
610 -    if (!motionUpdated)
611 -    {
612 -        if (_eyeBlink != NULL)
613 -        {
614 -            // メインモーションの更新がないとき
615 -            _eyeBlink->UpdateParameters(_model, deltaTimeSeconds); // 目パチ
616 -        }
617 -    }
618 -
619 -    if (_expressionManager != NULL)
620 +    if (_detector)
621      {
622 -        _expressionManager->UpdateMotion(_model, deltaTimeSeconds); // 表情でパラメータ更新(相対変化)
623 -    }
624 -
625 -    //ドラッグによる変化
626 -    //ドラッグによる顔の向きの調整
627 -    _model->AddParameterValue(_idParamAngleX, _dragX * 30); // -30から30の値を加える
628 -    _model->AddParameterValue(_idParamAngleY, _dragY * 30);
629 -    _model->AddParameterValue(_idParamAngleZ, _dragX * _dragY * -30);
630 -
631 -    //ドラッグによる体の向きの調整
632 -    _model->AddParameterValue(_idParamBodyAngleX, _dragX * 10); // -10から10の値を加える
633 -
634 -    //ドラッグによる目の向きの調整
635 -    _model->AddParameterValue(_idParamEyeBallX, _dragX); // -1から1の値を加える
636 -    _model->AddParameterValue(_idParamEyeBallY, _dragY);
637 +        auto idMan = CubismFramework::GetIdManager();
638 +        auto params = _detector->getParams();
639  
640 -    // 呼吸など
641 -    if (_breath != NULL)
642 -    {
643 -        _breath->UpdateParameters(_model, deltaTimeSeconds);
644 +        _model->SetParameterValue(idMan->GetId("ParamEyeLOpen"),
645 +                                  params.leftEyeOpenness);
646 +        _model->SetParameterValue(idMan->GetId("ParamEyeROpen"),
647 +                                  params.rightEyeOpenness);
648 +        _model->SetParameterValue(idMan->GetId("ParamMouthForm"),
649 +                                  params.mouthForm);
650 +        _model->SetParameterValue(idMan->GetId("ParamMouthOpenY"),
651 +                                  params.mouthOpenness);
652 +        _model->SetParameterValue(idMan->GetId("ParamEyeLSmile"),
653 +                                  params.leftEyeSmile);
654 +        _model->SetParameterValue(idMan->GetId("ParamEyeRSmile"),
655 +                                  params.rightEyeSmile);
656 +        _model->SetParameterValue(idMan->GetId("ParamAngleX"),
657 +                                  params.faceXAngle);
658 +        _model->SetParameterValue(idMan->GetId("ParamAngleY"),
659 +                                  params.faceYAngle);
660 +        _model->SetParameterValue(idMan->GetId("ParamAngleZ"),
661 +                                  params.faceZAngle);
662      }
663  
664      // 物理演算の設定
665 @@ -396,17 +297,6 @@ void LAppModel::Update()
666          _physics->Evaluate(_model, deltaTimeSeconds);
667      }
668  
669 -    // リップシンクの設定
670 -    if (_lipSync)
671 -    {
672 -        csmFloat32 value = 0; // リアルタイムでリップシンクを行う場合、システムから音量を取得して0〜1の範囲で値を入力します。
673 -
674 -        for (csmUint32 i = 0; i < _lipSyncIds.GetSize(); ++i)
675 -        {
676 -            _model->AddParameterValue(_lipSyncIds[i], value, 0.8f);
677 -        }
678 -    }
679 -
680      // ポーズの設定
681      if (_pose != NULL)
682      {
683 @@ -626,3 +516,9 @@ Csm::Rendering::CubismOffscreenFrame_Ope
684  {
685      return _renderBuffer;
686  }
687 +
688 +void LAppModel::SetFacialLandmarkDetector(FacialLandmarkDetector *detector)
689 +{
690 +    _detector = detector;
691 +}
692 +
693 diff -pruN --exclude build ./demo_clean/src/LAppModel.hpp ./demo_dev/src/LAppModel.hpp
694 --- ./demo_clean/src/LAppModel.hpp      2020-07-12 16:16:33.999809687 +0100
695 +++ ./demo_dev/src/LAppModel.hpp        2020-07-11 15:40:18.977286166 +0100
696 @@ -13,6 +13,7 @@
697  #include <Type/csmRectF.hpp>
698  #include <Rendering/OpenGL/CubismOffscreenSurface_OpenGLES2.hpp>
699  
700 +#include "facial_landmark_detector.h"
701  
702  /**
703   * @brief ユーザーが実際に使用するモデルの実装クラス<br>
704 @@ -113,6 +114,13 @@ public:
705       */
706      Csm::Rendering::CubismOffscreenFrame_OpenGLES2& GetRenderBuffer();
707  
708 +    /**
709 +     * @brief Set the pointer to the FacialLandmarkDetector instance
710 +     *
711 +     * @param[in] detector : Pointer to FacialLandmarkDetector instance
712 +     */
713 +    void SetFacialLandmarkDetector(FacialLandmarkDetector *detector);
714 +
715  protected:
716      /**
717       *  @brief  モデルを描画する処理。モデルを描画する空間のView-Projection行列を渡す。
718 @@ -183,6 +191,8 @@ private:
719      const Csm::CubismId* _idParamEyeBallY; ///< パラメータID: ParamEyeBallXY
720  
721      Csm::Rendering::CubismOffscreenFrame_OpenGLES2 _renderBuffer;   ///< フレームバッファ以外の描画先
722 +
723 +    FacialLandmarkDetector *_detector;
724  };
725  
726  
727 diff -pruN --exclude build ./demo_clean/src/LAppPal.cpp ./demo_dev/src/LAppPal.cpp
728 --- ./demo_clean/src/LAppPal.cpp        2020-07-12 16:16:33.999809687 +0100
729 +++ ./demo_dev/src/LAppPal.cpp  2020-07-11 23:29:09.084910139 +0100
730 @@ -6,6 +6,7 @@
731   */
732  
733  #include "LAppPal.hpp"
734 +#include <stdexcept>
735  #include <stdio.h>
736  #include <stdlib.h>
737  #include <stdarg.h>
738 @@ -45,10 +46,7 @@ csmByte* LAppPal::LoadFileAsBytes(const
739      file.open(path, std::ios::in | std::ios::binary);
740      if (!file.is_open())
741      {
742 -        if (DebugLogEnable)
743 -        {
744 -            PrintLog("file open error");
745 -        }
746 +        throw std::runtime_error("Failed to open file " + filePath);
747          return NULL;
748      }
749      file.read(buf, size);
750 diff -pruN --exclude build ./demo_clean/src/LAppTextureManager.cpp ./demo_dev/src/LAppTextureManager.cpp
751 --- ./demo_clean/src/LAppTextureManager.cpp     2020-07-12 16:16:33.999809687 +0100
752 +++ ./demo_dev/src/LAppTextureManager.cpp       2020-07-11 22:22:18.004965003 +0100
753 @@ -96,6 +96,46 @@ LAppTextureManager::TextureInfo* LAppTex
754  
755  }
756  
757 +LAppTextureManager::TextureInfo* LAppTextureManager::CreateTextureFromColor(
758 +    uint8_t red, uint8_t green, uint8_t blue, uint8_t alpha
759 +)
760 +{
761 +    int width = 8, height = 8;
762 +
763 +    uint8_t pixels[height][width][4];
764 +    for (std::size_t h = 0; h < height; h++)
765 +    {
766 +        for (std::size_t w = 0; w < width; w++)
767 +        {
768 +            pixels[h][w][0] = red;
769 +            pixels[h][w][1] = green;
770 +            pixels[h][w][2] = blue;
771 +            pixels[h][w][3] = alpha;
772 +        }
773 +    }
774 +
775 +    GLuint textureId;
776 +    glGenTextures(1, &textureId);
777 +    glBindTexture(GL_TEXTURE_2D, textureId);
778 +    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
779 +
780 +    glGenerateMipmap(GL_TEXTURE_2D);
781 +    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
782 +    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
783 +    glBindTexture(GL_TEXTURE_2D, 0);
784 +
785 +
786 +    LAppTextureManager::TextureInfo* textureInfo = new LAppTextureManager::TextureInfo();
787 +    textureInfo->fileName = "";
788 +    textureInfo->width = width;
789 +    textureInfo->height = height;
790 +    textureInfo->id = textureId;
791 +
792 +    _textures.PushBack(textureInfo);
793 +
794 +    return textureInfo;
795 +}
796 +
797  void LAppTextureManager::ReleaseTextures()
798  {
799      for (Csm::csmUint32 i = 0; i < _textures.GetSize(); i++)
800 diff -pruN --exclude build ./demo_clean/src/LAppTextureManager.hpp ./demo_dev/src/LAppTextureManager.hpp
801 --- ./demo_clean/src/LAppTextureManager.hpp     2020-07-12 16:16:33.999809687 +0100
802 +++ ./demo_dev/src/LAppTextureManager.hpp       2020-07-11 17:36:31.180131039 +0100
803 @@ -72,6 +72,8 @@ public:
804      */
805      TextureInfo* CreateTextureFromPngFile(std::string fileName);
806  
807 +    TextureInfo *CreateTextureFromColor(uint8_t red, uint8_t green, uint8_t blue, uint8_t alpha = 255);
808 +
809      /**
810      * @brief 画像の解放
811      *
812 diff -pruN --exclude build ./demo_clean/src/LAppView.cpp ./demo_dev/src/LAppView.cpp
813 --- ./demo_clean/src/LAppView.cpp       2020-07-12 16:16:34.003809759 +0100
814 +++ ./demo_dev/src/LAppView.cpp 2020-07-11 17:38:06.905451955 +0100
815 @@ -13,7 +13,6 @@
816  #include "LAppLive2DManager.hpp"
817  #include "LAppTextureManager.hpp"
818  #include "LAppDefine.hpp"
819 -#include "TouchManager.hpp"
820  #include "LAppSprite.hpp"
821  #include "LAppModel.hpp"
822  
823 @@ -26,8 +25,6 @@ using namespace LAppDefine;
824  LAppView::LAppView():
825      _programId(0),
826      _back(NULL),
827 -    _gear(NULL),
828 -    _power(NULL),
829      _renderSprite(NULL),
830      _renderTarget(SelectTarget_None)
831  {
832 @@ -35,8 +32,6 @@ LAppView::LAppView():
833      _clearColor[1] = 1.0f;
834      _clearColor[2] = 1.0f;
835      _clearColor[3] = 0.0f;
836 -    // タッチ関係のイベント管理
837 -    _touchManager = new TouchManager();
838  
839      // デバイス座標からスクリーン座標に変換するための
840      _deviceToScreen = new CubismMatrix44();
841 @@ -52,10 +47,7 @@ LAppView::~LAppView()
842  
843      delete _viewMatrix;
844      delete _deviceToScreen;
845 -    delete _touchManager;
846      delete _back;
847 -    delete _gear;
848 -    delete _power;
849  }
850  
851  void LAppView::Initialize()
852 @@ -97,9 +89,6 @@ void LAppView::Initialize()
853  void LAppView::Render()
854  {
855      _back->Render();
856 -    _gear->Render();
857 -    _power->Render();
858 -
859  
860      LAppLive2DManager* Live2DManager = LAppLive2DManager::GetInstance();
861  
862 @@ -139,35 +128,17 @@ void LAppView::InitializeSprite()
863      glfwGetWindowSize(LAppDelegate::GetInstance()->GetWindow(), &width, &height);
864  
865      LAppTextureManager* textureManager = LAppDelegate::GetInstance()->GetTextureManager();
866 -    const string resourcesPath = LAppDelegate::GetInstance()->GetRootDirectory() + ResourcesPath;
867  
868 -    string imageName = BackImageName;
869 -    LAppTextureManager::TextureInfo* backgroundTexture = textureManager->CreateTextureFromPngFile(resourcesPath + imageName);
870 +
871 +    LAppTextureManager::TextureInfo* backgroundTexture =
872 +        textureManager->CreateTextureFromColor(0, 255, 0);
873  
874      float x = width * 0.5f;
875      float y = height * 0.5f;
876 -    float fWidth = static_cast<float>(backgroundTexture->width * 2.0f);
877 -    float fHeight = static_cast<float>(height) * 0.95f;
878 +    float fWidth = static_cast<float>(width);
879 +    float fHeight = static_cast<float>(height);
880      _back = new LAppSprite(x, y, fWidth, fHeight, backgroundTexture->id, _programId);
881  
882 -    imageName = GearImageName;
883 -    LAppTextureManager::TextureInfo* gearTexture = textureManager->CreateTextureFromPngFile(resourcesPath + imageName);
884 -
885 -    x = static_cast<float>(width - gearTexture->width * 0.5f);
886 -    y = static_cast<float>(height - gearTexture->height * 0.5f);
887 -    fWidth = static_cast<float>(gearTexture->width);
888 -    fHeight = static_cast<float>(gearTexture->height);
889 -    _gear = new LAppSprite(x, y, fWidth, fHeight, gearTexture->id, _programId);
890 -
891 -    imageName = PowerImageName;
892 -    LAppTextureManager::TextureInfo* powerTexture = textureManager->CreateTextureFromPngFile(resourcesPath + imageName);
893 -
894 -    x = static_cast<float>(width - powerTexture->width * 0.5f);
895 -    y = static_cast<float>(powerTexture->height * 0.5f);
896 -    fWidth = static_cast<float>(powerTexture->width);
897 -    fHeight = static_cast<float>(powerTexture->height);
898 -    _power = new LAppSprite(x, y, fWidth, fHeight, powerTexture->id, _programId);
899 -
900      // 画面全体を覆うサイズ
901      x = width * 0.5f;
902      y = height * 0.5f;
903 @@ -175,52 +146,6 @@ void LAppView::InitializeSprite()
904  
905  }
906  
907 -void LAppView::OnTouchesBegan(float px, float py) const
908 -{
909 -    _touchManager->TouchesBegan(px, py);
910 -}
911 -
912 -void LAppView::OnTouchesMoved(float px, float py) const
913 -{
914 -    float viewX = this->TransformViewX(_touchManager->GetX());
915 -    float viewY = this->TransformViewY(_touchManager->GetY());
916 -
917 -    _touchManager->TouchesMoved(px, py);
918 -
919 -    LAppLive2DManager* Live2DManager = LAppLive2DManager::GetInstance();
920 -    Live2DManager->OnDrag(viewX, viewY);
921 -}
922 -
923 -void LAppView::OnTouchesEnded(float px, float py) const
924 -{
925 -    // タッチ終了
926 -    LAppLive2DManager* live2DManager = LAppLive2DManager::GetInstance();
927 -    live2DManager->OnDrag(0.0f, 0.0f);
928 -    {
929 -
930 -        // シングルタップ
931 -        float x = _deviceToScreen->TransformX(_touchManager->GetX()); // 論理座標変換した座標を取得。
932 -        float y = _deviceToScreen->TransformY(_touchManager->GetY()); // 論理座標変換した座標を取得。
933 -        if (DebugTouchLogEnable)
934 -        {
935 -            LAppPal::PrintLog("[APP]touchesEnded x:%.2f y:%.2f", x, y);
936 -        }
937 -        live2DManager->OnTap(x, y);
938 -
939 -        // 歯車にタップしたか
940 -        if (_gear->IsHit(px, py))
941 -        {
942 -            live2DManager->NextScene();
943 -        }
944 -
945 -        // 電源ボタンにタップしたか
946 -        if (_power->IsHit(px, py))
947 -        {
948 -            LAppDelegate::GetInstance()->AppEnd();
949 -        }
950 -    }
951 -}
952 -
953  float LAppView::TransformViewX(float deviceX) const
954  {
955      float screenX = _deviceToScreen->TransformX(deviceX); // 論理座標変換した座標を取得。
956 @@ -362,32 +287,4 @@ void LAppView::ResizeSprite()
957              _back->ResetRect(x, y, fWidth, fHeight);
958          }
959      }
960 -
961 -    if (_power)
962 -    {
963 -        GLuint id = _power->GetTextureId();
964 -        LAppTextureManager::TextureInfo* texInfo = textureManager->GetTextureInfoById(id);
965 -        if (texInfo)
966 -        {
967 -            x = static_cast<float>(width - texInfo->width * 0.5f);
968 -            y = static_cast<float>(texInfo->height * 0.5f);
969 -            fWidth = static_cast<float>(texInfo->width);
970 -            fHeight = static_cast<float>(texInfo->height);
971 -            _power->ResetRect(x, y, fWidth, fHeight);
972 -        }
973 -    }
974 -
975 -    if (_gear)
976 -    {
977 -        GLuint id = _gear->GetTextureId();
978 -        LAppTextureManager::TextureInfo* texInfo = textureManager->GetTextureInfoById(id);
979 -        if (texInfo)
980 -        {
981 -            x = static_cast<float>(width - texInfo->width * 0.5f);
982 -            y = static_cast<float>(height - texInfo->height * 0.5f);
983 -            fWidth = static_cast<float>(texInfo->width);
984 -            fHeight = static_cast<float>(texInfo->height);
985 -            _gear->ResetRect(x, y, fWidth, fHeight);
986 -        }
987 -    }
988  }
989 diff -pruN --exclude build ./demo_clean/src/LAppView.hpp ./demo_dev/src/LAppView.hpp
990 --- ./demo_clean/src/LAppView.hpp       2020-07-12 16:16:33.999809687 +0100
991 +++ ./demo_dev/src/LAppView.hpp 2020-07-11 17:38:25.541708705 +0100
992 @@ -14,7 +14,6 @@
993  #include "CubismFramework.hpp"
994  #include <Rendering/OpenGL/CubismOffscreenSurface_OpenGLES2.hpp>
995  
996 -class TouchManager;
997  class LAppSprite;
998  class LAppModel;
999  
1000 @@ -66,30 +65,6 @@ public:
1001      void ResizeSprite();
1002  
1003      /**
1004 -    * @brief タッチされたときに呼ばれる。
1005 -    *
1006 -    * @param[in]       pointX            スクリーンX座標
1007 -    * @param[in]       pointY            スクリーンY座標
1008 -    */
1009 -    void OnTouchesBegan(float pointX, float pointY) const;
1010 -
1011 -    /**
1012 -    * @brief タッチしているときにポインタが動いたら呼ばれる。
1013 -    *
1014 -    * @param[in]       pointX            スクリーンX座標
1015 -    * @param[in]       pointY            スクリーンY座標
1016 -    */
1017 -    void OnTouchesMoved(float pointX, float pointY) const;
1018 -
1019 -    /**
1020 -    * @brief タッチが終了したら呼ばれる。
1021 -    *
1022 -    * @param[in]       pointX            スクリーンX座標
1023 -    * @param[in]       pointY            スクリーンY座標
1024 -    */
1025 -    void OnTouchesEnded(float pointX, float pointY) const;
1026 -
1027 -    /**
1028      * @brief X座標をView座標に変換する。
1029      *
1030      * @param[in]       deviceX            デバイスX座標
1031 @@ -147,13 +122,10 @@ public:
1032      void SetRenderTargetClearColor(float r, float g, float b);
1033  
1034  private:
1035 -    TouchManager* _touchManager;                 ///< タッチマネージャー
1036      Csm::CubismMatrix44* _deviceToScreen;    ///< デバイスからスクリーンへの行列
1037      Csm::CubismViewMatrix* _viewMatrix;      ///< viewMatrix
1038      GLuint _programId;                       ///< シェーダID
1039      LAppSprite* _back;                       ///< 背景画像
1040 -    LAppSprite* _gear;                       ///< ギア画像
1041 -    LAppSprite* _power;                      ///< 電源画像
1042  
1043      // レンダリング先を別ターゲットにする方式の場合に使用
1044      LAppSprite* _renderSprite;                                  ///< モードによっては_renderBufferのテクスチャを描画
1045 diff -pruN --exclude build ./demo_clean/src/main.cpp ./demo_dev/src/main.cpp
1046 --- ./demo_clean/src/main.cpp   2020-07-12 16:16:33.999809687 +0100
1047 +++ ./demo_dev/src/main.cpp     2020-07-12 15:06:29.194034887 +0100
1048 @@ -5,18 +5,156 @@
1049   * that can be found at https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
1050   */
1051  
1052 +#include <thread>
1053 +#include <stdexcept>
1054 +#include <sstream>
1055 +
1056 +#ifdef __cpp_lib_filesystem
1057 +#include <filesystem>
1058 +namespace fs = std::filesystem;
1059 +#else
1060 +#include <experimental/filesystem>
1061 +namespace fs = std::experimental::filesystem;
1062 +#endif
1063 +
1064 +
1065  #include "LAppDelegate.hpp"
1066 +#include "LAppLive2DManager.hpp"
1067 +#include "facial_landmark_detector.h"
1068 +
1069 +struct CmdArgs
1070 +{
1071 +    int windowWidth;
1072 +    int windowHeight;
1073 +    std::string windowTitle;
1074 +    std::string rootDir;
1075 +    float scaleFactor;
1076 +    float translateX;
1077 +    float translateY;
1078 +    std::string modelName;
1079 +    std::string cfgPath; // Path to config file for FacialLandmarkDetector
1080 +};
1081 +
1082 +CmdArgs parseArgv(int argc, char *argv[])
1083 +{
1084 +    // I think the command-line args are simple enough to not justify using a library...
1085 +    CmdArgs cmdArgs;
1086 +    // Set default values
1087 +    cmdArgs.windowWidth = 600;
1088 +    cmdArgs.windowHeight = 600;
1089 +    cmdArgs.windowTitle = "FacialLandmarksForCubism example";
1090 +    cmdArgs.rootDir = fs::current_path();
1091 +    cmdArgs.scaleFactor = 8.0f;
1092 +    cmdArgs.translateX = 0.0f;
1093 +    cmdArgs.translateY = -2.8f;
1094 +    cmdArgs.modelName = "Haru";
1095 +    cmdArgs.cfgPath = "";
1096 +
1097 +    int i = 1;
1098 +    while (i < argc)
1099 +    {
1100 +        std::string arg = argv[i];
1101 +        std::stringstream ss;
1102 +
1103 +        if (arg == "--window-width" || arg == "-W") // capital W for consistency with height
1104 +        {
1105 +            ss << argv[i + 1];
1106 +            if (!(ss >> cmdArgs.windowWidth))
1107 +            {
1108 +                throw std::runtime_error("Invalid argument for window width");
1109 +            }
1110 +        }
1111 +        else if (arg == "--window-height" || arg == "-H") // avoiding "-h", typically for help
1112 +        {
1113 +            ss << argv[i + 1];
1114 +            if (!(ss >> cmdArgs.windowHeight))
1115 +            {
1116 +                throw std::runtime_error("Invalid argument for window height");
1117 +            }
1118 +        }
1119 +        else if (arg == "--window-title" || arg == "-t")
1120 +        {
1121 +            cmdArgs.windowTitle = argv[i + 1];
1122 +        }
1123 +        else if (arg == "--root-dir" || arg == "-d")
1124 +        {
1125 +            cmdArgs.rootDir = argv[i + 1];
1126 +        }
1127 +        else if (arg == "--scale-factor" || arg == "-f")
1128 +        {
1129 +            ss << argv[i + 1];
1130 +            if (!(ss >> cmdArgs.scaleFactor))
1131 +            {
1132 +                throw std::runtime_error("Invalid argument for scale factor");
1133 +            }
1134 +        }
1135 +        else if (arg == "--translate-x" || arg == "-x")
1136 +        {
1137 +            ss << argv[i + 1];
1138 +            if (!(ss >> cmdArgs.translateX))
1139 +            {
1140 +                throw std::runtime_error("Invalid argument for translate X");
1141 +            }
1142 +        }
1143 +        else if (arg == "--translate-y" || arg == "-y")
1144 +        {
1145 +            ss << argv[i + 1];
1146 +            if (!(ss >> cmdArgs.translateY))
1147 +            {
1148 +                throw std::runtime_error("Invalid argument for translate Y");
1149 +            }
1150 +        }
1151 +        else if (arg == "--model" || arg == "-m")
1152 +        {
1153 +            cmdArgs.modelName = argv[i + 1];
1154 +        }
1155 +        else if (arg == "--config" || arg == "-c")
1156 +        {
1157 +            cmdArgs.cfgPath = argv[i + 1];
1158 +        }
1159 +        else
1160 +        {
1161 +            throw std::runtime_error("Unrecognized argument: " + arg);
1162 +        }
1163 +
1164 +        i += 2;
1165 +    }
1166 +
1167 +    return cmdArgs;
1168 +}
1169  
1170  int main(int argc, char* argv[])
1171  {
1172 -    // create the application instance
1173 -    if (LAppDelegate::GetInstance()->Initialize() == GL_FALSE)
1174 +    auto cmdArgs = parseArgv(argc, argv);
1175 +
1176 +    LAppDelegate *delegate = LAppDelegate::GetInstance();
1177 +
1178 +    if (!delegate->Initialize(cmdArgs.windowWidth,
1179 +                              cmdArgs.windowHeight,
1180 +                              cmdArgs.windowTitle.c_str()))
1181      {
1182 -        return 1;
1183 +        throw std::runtime_error("Unable to initialize LAppDelegate");
1184      }
1185  
1186 -    LAppDelegate::GetInstance()->Run();
1187 +    delegate->SetRootDirectory(cmdArgs.rootDir);
1188 +
1189 +    FacialLandmarkDetector detector(cmdArgs.cfgPath);
1190 +
1191 +    std::thread detectorThread(&FacialLandmarkDetector::mainLoop,
1192 +                               &detector);
1193 +
1194 +    LAppLive2DManager *manager = LAppLive2DManager::GetInstance();
1195 +    manager->SetModel(cmdArgs.modelName);
1196 +
1197 +    manager->SetProjectionScaleTranslate(cmdArgs.scaleFactor,
1198 +                                         cmdArgs.translateX,
1199 +                                         cmdArgs.translateY);
1200 +    manager->SetFacialLandmarkDetector(&detector);
1201 +
1202 +    delegate->Run();
1203 +
1204 +    detector.stop();
1205 +    detectorThread.join();
1206  
1207      return 0;
1208  }
1209 -