f397e79f5b95c8e22886c342235233d8b4a5bf9f
[facial-landmarks-for-cubism.git] / example / demo_win.patch
1 diff -pruN --exclude build ./demo_clean/CMakeLists.txt ./demo_dev/CMakeLists.txt
2 --- ./demo_clean/CMakeLists.txt 2025-05-29 18:28:25.529155300 +0100
3 +++ ./demo_dev/CMakeLists.txt   2025-05-29 20:27:27.435729700 +0100
4 @@ -9,7 +9,7 @@ option(
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-5-r.4)
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 @@ -44,7 +44,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 11)
18 +set(CMAKE_CXX_STANDARD 17)
19  set(CMAKE_CXX_STANDARD_REQUIRED ON)
20  set(CMAKE_CXX_EXTENSIONS OFF)
21  
22 @@ -113,6 +113,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 common source files.
32 @@ -125,12 +128,14 @@ target_link_libraries(${APP_NAME}
33    Framework
34    glfw
35    ${OPENGL_LIBRARIES}
36 +  FacialLandmarksForCubism
37 +  ws2_32
38    # Solve the MSVCRT confliction.
39    debug -NODEFAULTLIB:libcmtd.lib
40    optimized -NODEFAULTLIB:libcmt.lib
41  )
42  # Specify include directories.
43 -target_include_directories(${APP_NAME} PRIVATE ${STB_PATH})
44 +target_include_directories(${APP_NAME} PRIVATE ${STB_PATH} ${CMAKE_CURRENT_SOURCE_DIR}/../../include)
45  # Build in multi-process.
46  target_compile_options(${APP_NAME} PRIVATE /MP)
47  
48 diff -pruN --exclude build ./demo_clean/src/LAppDelegate.cpp ./demo_dev/src/LAppDelegate.cpp
49 --- ./demo_clean/src/LAppDelegate.cpp   2025-05-29 18:28:25.936605800 +0100
50 +++ ./demo_dev/src/LAppDelegate.cpp     2025-05-29 23:48:35.887436800 +0100
51 @@ -43,7 +43,8 @@ void LAppDelegate::ReleaseInstance()
52      s_instance = NULL;
53  }
54  
55 -bool LAppDelegate::Initialize()
56 +bool LAppDelegate::Initialize(int initWindowWidth, int initWindowHeight,
57 +                              const char *windowTitle)
58  {
59      if (DebugLogEnable)
60      {
61 @@ -61,7 +62,13 @@ bool LAppDelegate::Initialize()
62      }
63  
64      // Windowの生成_
65 -    _window = glfwCreateWindow(RenderTargetWidth, RenderTargetHeight, "SAMPLE", NULL, NULL);
66 +    _window = glfwCreateWindow(
67 +        initWindowWidth ? initWindowWidth : RenderTargetWidth,
68 +        initWindowHeight ? initWindowHeight : RenderTargetHeight,
69 +        windowTitle ? windowTitle : "SAMPLE",
70 +        NULL,
71 +        NULL);
72 +
73      if (_window == NULL)
74      {
75          if (DebugLogEnable)
76 @@ -93,10 +100,6 @@ bool LAppDelegate::Initialize()
77      glEnable(GL_BLEND);
78      glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
79  
80 -    //コールバック関数の登録
81 -    glfwSetMouseButtonCallback(_window, EventHandler::OnMouseCallBack);
82 -    glfwSetCursorPosCallback(_window, EventHandler::OnMouseCallBack);
83 -
84      // ウィンドウサイズ記憶
85      int width, height;
86      glfwGetWindowSize(LAppDelegate::GetInstance()->GetWindow(), &width, &height);
87 diff -pruN --exclude build ./demo_clean/src/LAppDelegate.hpp ./demo_dev/src/LAppDelegate.hpp
88 --- ./demo_clean/src/LAppDelegate.hpp   2025-05-29 18:28:26.187813200 +0100
89 +++ ./demo_dev/src/LAppDelegate.hpp     2025-05-29 23:48:48.098223800 +0100
90 @@ -38,7 +38,8 @@ public:
91      /**
92      * @brief   APPに必要なものを初期化する。
93      */
94 -    bool Initialize();
95 +    bool Initialize(int initWindowWidth = 0, int initWindowHeight = 0,
96 +                    const char *windowTitle = "SAMPLE");
97  
98      /**
99      * @brief   解放する。
100 diff -pruN --exclude build ./demo_clean/src/LAppLive2DManager.cpp ./demo_dev/src/LAppLive2DManager.cpp
101 --- ./demo_clean/src/LAppLive2DManager.cpp      2025-05-29 18:28:26.172173900 +0100
102 +++ ./demo_dev/src/LAppLive2DManager.cpp        2025-05-29 23:49:29.615620500 +0100
103 @@ -6,10 +6,7 @@
104   */
105  
106  #include "LAppLive2DManager.hpp"
107 -#include <windows.h>
108 -#include <stdio.h>
109 -#include <stdlib.h>
110 -#include <io.h>
111 +#include <string>
112  #include <GL/glew.h>
113  #include <GLFW/glfw3.h>
114  #include <Rendering/CubismRenderer.hpp>
115 @@ -18,6 +15,7 @@
116  #include "LAppDelegate.hpp"
117  #include "LAppModel.hpp"
118  #include "LAppView.hpp"
119 +#include "LAppSprite.hpp"
120  
121  using namespace Csm;
122  using namespace LAppDefine;
123 @@ -65,12 +63,11 @@ void LAppLive2DManager::ReleaseInstance(
124  
125  LAppLive2DManager::LAppLive2DManager()
126      : _viewMatrix(NULL)
127 -    , _sceneIndex(0)
128 +    , _projScaleFactor(1.0f)
129 +    , _translateX(0.0f)
130 +    , _translateY(0.0f)
131  {
132      _viewMatrix = new CubismMatrix44();
133 -    SetUpModel();
134 -
135 -    ChangeScene(_sceneIndex);
136  }
137  
138  LAppLive2DManager::~LAppLive2DManager()
139 @@ -89,63 +86,6 @@ void LAppLive2DManager::ReleaseAllModel(
140      _models.Clear();
141  }
142  
143 -void LAppLive2DManager::SetUpModel()
144 -{
145 -    // ResourcesPathの中にあるフォルダ名を全てクロールし、モデルが存在するフォルダを定義する。
146 -    // フォルダはあるが同名の.model3.jsonが見つからなかった場合はリストに含めない。
147 -    // 一部文字が受け取れないためワイド文字で受け取ってUTF8に変換し格納する。
148 -
149 -    csmString crawlPath(ResourcesPath);
150 -    crawlPath += "*.*";
151 -
152 -    wchar_t wideStr[MAX_PATH];
153 -    csmChar name[MAX_PATH];
154 -    LAppPal::ConvertMultiByteToWide(crawlPath.GetRawString(), wideStr, MAX_PATH);
155 -
156 -    struct _wfinddata_t fdata;
157 -    intptr_t fh = _wfindfirst(wideStr, &fdata);
158 -    if (fh == -1)
159 -    {
160 -        return;
161 -    }
162 -
163 -    _modelDir.Clear();
164 -
165 -    while (_wfindnext(fh, &fdata) == 0)
166 -    {
167 -        if ((fdata.attrib & _A_SUBDIR) && wcscmp(fdata.name, L"..") != 0)
168 -        {
169 -            LAppPal::ConvertWideToMultiByte(fdata.name, name, MAX_PATH);
170 -
171 -            // フォルダと同名の.model3.jsonがあるか探索する
172 -            csmString model3jsonPath(ResourcesPath);
173 -            model3jsonPath += name;
174 -            model3jsonPath.Append(1, '/');
175 -            model3jsonPath += name;
176 -            model3jsonPath += ".model3.json";
177 -
178 -            LAppPal::ConvertMultiByteToWide(model3jsonPath.GetRawString(), wideStr, MAX_PATH);
179 -
180 -            struct _wfinddata_t fdata2;
181 -            if (_wfindfirst(wideStr, &fdata2) != -1)
182 -            {
183 -                _modelDir.PushBack(csmString(name));
184 -            }
185 -        }
186 -    }
187 -    qsort(_modelDir.GetPtr(), _modelDir.GetSize(), sizeof(csmString), CompareCsmString);
188 -}
189 -
190 -csmVector<csmString> LAppLive2DManager::GetModelDir() const
191 -{
192 -    return _modelDir;
193 -}
194 -
195 -csmInt32 LAppLive2DManager::GetModelDirSize() const
196 -{
197 -    return _modelDir.GetSize();
198 -}
199 -
200  LAppModel* LAppLive2DManager::GetModel(csmUint32 no) const
201  {
202      if (no < _models.GetSize())
203 @@ -172,26 +112,6 @@ void LAppLive2DManager::OnTap(csmFloat32
204      {
205          LAppPal::PrintLogLn("[APP]tap point: {x:%.2f y:%.2f}", x, y);
206      }
207 -
208 -    for (csmUint32 i = 0; i < _models.GetSize(); i++)
209 -    {
210 -        if (_models[i]->HitTest(HitAreaNameHead, x, y))
211 -        {
212 -            if (DebugLogEnable)
213 -            {
214 -                LAppPal::PrintLogLn("[APP]hit area: [%s]", HitAreaNameHead);
215 -            }
216 -            _models[i]->SetRandomExpression();
217 -        }
218 -        else if (_models[i]->HitTest(HitAreaNameBody, x, y))
219 -        {
220 -            if (DebugLogEnable)
221 -            {
222 -                LAppPal::PrintLogLn("[APP]hit area: [%s]", HitAreaNameBody);
223 -            }
224 -            _models[i]->StartRandomMotion(MotionGroupTapBody, PriorityNormal, FinishedMotion, BeganMotion);
225 -        }
226 -    }
227  }
228  
229  void LAppLive2DManager::OnUpdate() const
230 @@ -215,12 +135,15 @@ void LAppLive2DManager::OnUpdate() const
231          {
232              // 横に長いモデルを縦長ウィンドウに表示する際モデルの横サイズでscaleを算出する
233              model->GetModelMatrix()->SetWidth(2.0f);
234 -            projection.Scale(1.0f, static_cast<float>(width) / static_cast<float>(height));
235 +            projection.Scale(_projScaleFactor,
236 +                             _projScaleFactor * static_cast<float>(width) / static_cast<float>(height));
237          }
238          else
239          {
240 -            projection.Scale(static_cast<float>(height) / static_cast<float>(width), 1.0f);
241 +            projection.Scale(_projScaleFactor * static_cast<float>(height) / static_cast<float>(width),
242 +                             _projScaleFactor);
243          }
244 +        projection.Translate(_translateX, _translateY);
245  
246          // 必要があればここで乗算
247          if (_viewMatrix != NULL)
248 @@ -239,34 +162,15 @@ void LAppLive2DManager::OnUpdate() const
249      }
250  }
251  
252 -void LAppLive2DManager::NextScene()
253 +void LAppLive2DManager::SetModel(std::string modelName, bool useOldParamId)
254  {
255 -    csmInt32 no = (_sceneIndex + 1) % GetModelDirSize();
256 -    ChangeScene(no);
257 -}
258 -
259 -void LAppLive2DManager::ChangeScene(Csm::csmInt32 index)
260 -{
261 -    _sceneIndex = index;
262 -    if (DebugLogEnable)
263 -    {
264 -        LAppPal::PrintLogLn("[APP]model index: %d", _sceneIndex);
265 -    }
266 -
267 -    // model3.jsonのパスを決定する.
268 -    // ディレクトリ名とmodel3.jsonの名前を一致していることが条件
269 -    const csmString& model = _modelDir[index];
270 -
271 -    csmString modelPath(ResourcesPath);
272 -    modelPath += model;
273 -    modelPath.Append(1, '/');
274 -
275 -    csmString modelJsonName(model);
276 +    std::string modelPath = ResourcesPath + modelName + "/";
277 +    std::string modelJsonName = modelName;
278      modelJsonName += ".model3.json";
279  
280      ReleaseAllModel();
281 -    _models.PushBack(new LAppModel());
282 -    _models[0]->LoadAssets(modelPath.GetRawString(), modelJsonName.GetRawString());
283 +    _models.PushBack(new LAppModel(useOldParamId));
284 +    _models[0]->LoadAssets(modelPath.c_str(), modelJsonName.c_str());
285  
286      /*
287       * モデル半透明表示を行うサンプルを提示する。
288 @@ -287,8 +191,8 @@ void LAppLive2DManager::ChangeScene(Csm:
289  
290  #if defined(USE_RENDER_TARGET) || defined(USE_MODEL_RENDER_TARGET)
291          // モデル個別にαを付けるサンプルとして、もう1体モデルを作成し、少し位置をずらす
292 -        _models.PushBack(new LAppModel());
293 -        _models[1]->LoadAssets(modelPath.GetRawString(), modelJsonName.GetRawString());
294 +        _models.PushBack(new LAppModel(useOldParamId));
295 +        _models[1]->LoadAssets(modelPath.c_str(), modelJsonName.c_str());
296          _models[1]->GetModelMatrix()->TranslateX(0.2f);
297  #endif
298  
299 @@ -311,3 +215,20 @@ void LAppLive2DManager::SetViewMatrix(Cu
300          _viewMatrix->GetArray()[i] = m->GetArray()[i];
301      }
302  }
303 +
304 +void LAppLive2DManager::SetFacialLandmarkDetector(FacialLandmarkDetector *detector)
305 +{
306 +    for (auto it = _models.Begin(); it != _models.End(); ++it)
307 +    {
308 +        (*it)->SetFacialLandmarkDetector(detector);
309 +    }
310 +}
311 +
312 +void LAppLive2DManager::SetProjectionScaleTranslate(float scaleFactor,
313 +                                                    float translateX,
314 +                                                    float translateY)
315 +{
316 +    _projScaleFactor = scaleFactor;
317 +    _translateX = translateX;
318 +    _translateY = translateY;
319 +}
320 diff -pruN --exclude build ./demo_clean/src/LAppLive2DManager.hpp ./demo_dev/src/LAppLive2DManager.hpp
321 --- ./demo_clean/src/LAppLive2DManager.hpp      2025-05-29 18:28:26.454266400 +0100
322 +++ ./demo_dev/src/LAppLive2DManager.hpp        2025-05-29 23:49:37.447547800 +0100
323 @@ -7,12 +7,15 @@
324  
325  #pragma once
326  
327 +#include <string>
328  #include <CubismFramework.hpp>
329  #include <Math/CubismMatrix44.hpp>
330  #include <Type/csmVector.hpp>
331  
332  class LAppModel;
333  
334 +class FacialLandmarkDetector;
335 +
336  /**
337  * @brief サンプルアプリケーションにおいてCubismModelを管理するクラス<br>
338  *         モデル生成と破棄、タップイベントの処理、モデル切り替えを行う。
339 @@ -37,24 +40,6 @@ public:
340      static void ReleaseInstance();
341  
342      /**
343 -    * @brief   Resources フォルダにあるモデルフォルダ名をセットする
344 -    *
345 -    */
346 -    void SetUpModel();
347 -
348 -    /**
349 -    * @brief   Resources フォルダにあるモデルフォルダ名を取得する
350 -    *
351 -    */
352 -    Csm::csmVector<Csm::csmString> GetModelDir() const;
353 -
354 -    /**
355 -    * @brief   Resources フォルダにあるモデルフォルダのサイズを取得する
356 -    *
357 -    */
358 -    Csm::csmInt32 GetModelDirSize() const;
359 -
360 -    /**
361      * @brief   現在のシーンで保持しているモデルを返す
362      *
363      * @param[in]   no  モデルリストのインデックス値
364 @@ -91,16 +76,14 @@ public:
365      void OnUpdate() const;
366  
367      /**
368 -    * @brief   次のシーンに切り替える<br>
369 -    *           サンプルアプリケーションではモデルセットの切り替えを行う。
370 -    */
371 -    void NextScene();
372 -
373 -    /**
374 -    * @brief   シーンを切り替える<br>
375 -    *           サンプルアプリケーションではモデルセットの切り替えを行う。
376 -    */
377 -    void ChangeScene(Csm::csmInt32 index);
378 +     * @brief Set model data
379 +     *
380 +     * @param[in] modelName : Name of model, should be the same for both
381 +     *                        the directory and the model3.json file
382 +     * @param[in] useOldParamId : If true, translate new (Cubism 3+)
383 +     *                            parameter IDs to old (Cubism 2.1) ones
384 +     */
385 +    void SetModel(std::string modelName, bool useOldParamId);
386  
387      /**
388       * @brief   モデル個数を得る
389 @@ -113,6 +96,24 @@ public:
390       */
391      void SetViewMatrix(Live2D::Cubism::Framework::CubismMatrix44* m);
392  
393 +    /**
394 +     * @brief Set the pointer to the FacialLandmarkDetector instance
395 +     *
396 +     * @param[in] detector : Pointer to FacialLandmarkDetector instance
397 +     */
398 +    void SetFacialLandmarkDetector(FacialLandmarkDetector *detector);
399 +
400 +    /**
401 +     * @brief Set projection scale factor and translation parameters
402 +     *
403 +     * @param[in] scaleFactor : Scale factor applied in both X and Y directions
404 +     * @param[in] translateX : Translation in X direction
405 +     * @param[in] translateY : Translation in Y direction
406 +     */
407 +    void SetProjectionScaleTranslate(float scaleFactor,
408 +                                     float translateX,
409 +                                     float translateY);
410 +
411  private:
412      /**
413      * @brief  コンストラクタ
414 @@ -126,7 +127,8 @@ private:
415  
416      Csm::CubismMatrix44* _viewMatrix; ///< モデル描画に用いるView行列
417      Csm::csmVector<LAppModel*> _models; ///< モデルインスタンスのコンテナ
418 -    Csm::csmInt32 _sceneIndex; ///< 表示するシーンのインデックス値
419  
420 -    Csm::csmVector<Csm::csmString> _modelDir; ///< モデルディレクトリ名のコンテナ
421 +    float _projScaleFactor;
422 +    float _translateX;
423 +    float _translateY;
424  };
425 diff -pruN --exclude build ./demo_clean/src/LAppModel.cpp ./demo_dev/src/LAppModel.cpp
426 --- ./demo_clean/src/LAppModel.cpp      2025-05-29 18:28:26.423050700 +0100
427 +++ ./demo_dev/src/LAppModel.cpp        2025-05-29 23:50:40.648300100 +0100
428 @@ -21,14 +21,18 @@
429  #include "LAppTextureManager.hpp"
430  #include "LAppDelegate.hpp"
431  
432 +#include "facial_landmark_detector.h"
433 +
434  using namespace Live2D::Cubism::Framework;
435  using namespace Live2D::Cubism::Framework::DefaultParameterId;
436  using namespace LAppDefine;
437  
438 -LAppModel::LAppModel()
439 +LAppModel::LAppModel(bool useOldParamId)
440      : LAppModel_Common()
441      , _modelSetting(NULL)
442      , _userTimeSeconds(0.0f)
443 +    , _detector(nullptr)
444 +    , _useOldParamId(useOldParamId)
445  {
446      if (MocConsistencyValidationEnable)
447      {
448 @@ -44,12 +48,12 @@ LAppModel::LAppModel()
449          _debugMode = true;
450      }
451  
452 -    _idParamAngleX = CubismFramework::GetIdManager()->GetId(ParamAngleX);
453 -    _idParamAngleY = CubismFramework::GetIdManager()->GetId(ParamAngleY);
454 -    _idParamAngleZ = CubismFramework::GetIdManager()->GetId(ParamAngleZ);
455 -    _idParamBodyAngleX = CubismFramework::GetIdManager()->GetId(ParamBodyAngleX);
456 -    _idParamEyeBallX = CubismFramework::GetIdManager()->GetId(ParamEyeBallX);
457 -    _idParamEyeBallY = CubismFramework::GetIdManager()->GetId(ParamEyeBallY);
458 +    _idParamAngleX = CubismFramework::GetIdManager()->GetId(_(ParamAngleX));
459 +    _idParamAngleY = CubismFramework::GetIdManager()->GetId(_(ParamAngleY));
460 +    _idParamAngleZ = CubismFramework::GetIdManager()->GetId(_(ParamAngleZ));
461 +    _idParamBodyAngleX = CubismFramework::GetIdManager()->GetId(_(ParamBodyAngleX));
462 +    _idParamEyeBallX = CubismFramework::GetIdManager()->GetId(_(ParamEyeBallX));
463 +    _idParamEyeBallY = CubismFramework::GetIdManager()->GetId(_(ParamEyeBallY));
464  }
465  
466  LAppModel::~LAppModel()
467 @@ -122,33 +126,6 @@ void LAppModel::SetupModel(ICubismModelS
468          DeleteBuffer(buffer, path.GetRawString());
469      }
470  
471 -    //Expression
472 -    if (_modelSetting->GetExpressionCount() > 0)
473 -    {
474 -        const csmInt32 count = _modelSetting->GetExpressionCount();
475 -        for (csmInt32 i = 0; i < count; i++)
476 -        {
477 -            csmString name = _modelSetting->GetExpressionName(i);
478 -            csmString path = _modelSetting->GetExpressionFileName(i);
479 -            path = _modelHomeDir + path;
480 -
481 -            buffer = CreateBuffer(path.GetRawString(), &size);
482 -            ACubismMotion* motion = LoadExpression(buffer, size, name.GetRawString());
483 -
484 -            if (motion)
485 -            {
486 -                if (_expressions[name] != NULL)
487 -                {
488 -                    ACubismMotion::Delete(_expressions[name]);
489 -                    _expressions[name] = NULL;
490 -                }
491 -                _expressions[name] = motion;
492 -            }
493 -
494 -            DeleteBuffer(buffer, path.GetRawString());
495 -        }
496 -    }
497 -
498      //Physics
499      if (strcmp(_modelSetting->GetPhysicsFileName(), "") != 0)
500      {
501 @@ -187,7 +164,7 @@ void LAppModel::SetupModel(ICubismModelS
502          breathParameters.PushBack(CubismBreath::BreathParameterData(_idParamAngleY, 0.0f, 8.0f, 3.5345f, 0.5f));
503          breathParameters.PushBack(CubismBreath::BreathParameterData(_idParamAngleZ, 0.0f, 10.0f, 5.5345f, 0.5f));
504          breathParameters.PushBack(CubismBreath::BreathParameterData(_idParamBodyAngleX, 0.0f, 4.0f, 15.5345f, 0.5f));
505 -        breathParameters.PushBack(CubismBreath::BreathParameterData(CubismFramework::GetIdManager()->GetId(ParamBreath), 0.5f, 0.5f, 3.2345f, 0.5f));
506 +        breathParameters.PushBack(CubismBreath::BreathParameterData(CubismFramework::GetIdManager()->GetId(_(ParamBreath)), 0.5f, 0.5f, 3.2345f, 0.5f));
507  
508          _breath->SetParameters(breathParameters);
509      }
510 @@ -211,21 +188,6 @@ void LAppModel::SetupModel(ICubismModelS
511          }
512      }
513  
514 -    // LipSyncIds
515 -    {
516 -        csmInt32 lipSyncIdCount = _modelSetting->GetLipSyncParameterCount();
517 -        for (csmInt32 i = 0; i < lipSyncIdCount; ++i)
518 -        {
519 -            _lipSyncIds.PushBack(_modelSetting->GetLipSyncParameterId(i));
520 -        }
521 -    }
522 -
523 -    if (_modelSetting == NULL || _modelMatrix == NULL)
524 -    {
525 -        LAppPal::PrintLogLn("Failed to SetupModel().");
526 -        return;
527 -    }
528 -
529      //Layout
530      csmMap<csmString, csmFloat32> layout;
531      _modelSetting->GetLayoutMap(layout);
532 @@ -330,62 +292,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 +    if (_detector)
547      {
548 -        // モーションの再生がない場合、待機モーションの中からランダムで再生する
549 -        StartRandomMotion(MotionGroupIdle, PriorityIdle);
550 -    }
551 -    else
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 -    _opacity = _model->GetModelOpacity();
562 -
563 -    // まばたき
564 -    if (!motionUpdated)
565 -    {
566 -        if (_eyeBlink != NULL)
567 +        // NOTE: Apparently, this LoadParameters/SaveParameters pair
568 +        // is needed for auto breath to work.
569 +        _model->LoadParameters(); // 前回セーブされた状態をロード
570 +        if (_motionManager->IsFinished() && params.randomMotion)
571          {
572 -            // メインモーションの更新がないとき
573 -            _eyeBlink->UpdateParameters(_model, deltaTimeSeconds); // 目パチ
574 +            // モーションの再生がない場合、待機モーションの中からランダムで再生する
575 +            StartRandomMotion(MotionGroupIdle, PriorityIdle);
576          }
577 -    }
578 -
579 -    if (_expressionManager != NULL)
580 -    {
581 -        _expressionManager->UpdateMotion(_model, deltaTimeSeconds); // 表情でパラメータ更新(相対変化)
582 -    }
583 -
584 -    //ドラッグによる変化
585 -    //ドラッグによる顔の向きの調整
586 -    _model->AddParameterValue(_idParamAngleX, _dragX * 30); // -30から30の値を加える
587 -    _model->AddParameterValue(_idParamAngleY, _dragY * 30);
588 -    _model->AddParameterValue(_idParamAngleZ, _dragX * _dragY * -30);
589 -
590 -    //ドラッグによる体の向きの調整
591 -    _model->AddParameterValue(_idParamBodyAngleX, _dragX * 10); // -10から10の値を加える
592 +        else
593 +        {
594 +            _motionManager->UpdateMotion(_model, deltaTimeSeconds); // モーションを更新
595 +        }
596 +        _model->SaveParameters(); // 状態を保存
597  
598 -    //ドラッグによる目の向きの調整
599 -    _model->AddParameterValue(_idParamEyeBallX, _dragX); // -1から1の値を加える
600 -    _model->AddParameterValue(_idParamEyeBallY, _dragY);
601  
602 -    // 呼吸など
603 -    if (_breath != NULL)
604 -    {
605 -        _breath->UpdateParameters(_model, deltaTimeSeconds);
606 +        if (params.autoBlink && _eyeBlink)
607 +        {
608 +            _eyeBlink->UpdateParameters(_model, deltaTimeSeconds);
609 +        }
610 +        else
611 +        {
612 +            _model->SetParameterValue(idMan->GetId(_("ParamEyeLOpen")),
613 +                                      params.leftEyeOpenness);
614 +            _model->SetParameterValue(idMan->GetId(_("ParamEyeROpen")),
615 +                                      params.rightEyeOpenness);
616 +        }
617 +        _model->SetParameterValue(idMan->GetId(_("ParamMouthForm")),
618 +                                  params.mouthForm);
619 +        _model->SetParameterValue(idMan->GetId(_("ParamMouthOpenY")),
620 +                                  params.mouthOpenness);
621 +        _model->SetParameterValue(idMan->GetId(_("ParamEyeLSmile")),
622 +                                  params.leftEyeSmile);
623 +        _model->SetParameterValue(idMan->GetId(_("ParamEyeRSmile")),
624 +                                  params.rightEyeSmile);
625 +        _model->SetParameterValue(idMan->GetId(_("ParamAngleX")),
626 +                                  params.faceXAngle);
627 +        _model->SetParameterValue(idMan->GetId(_("ParamAngleY")),
628 +                                  params.faceYAngle);
629 +        _model->SetParameterValue(idMan->GetId(_("ParamAngleZ")),
630 +                                  params.faceZAngle);
631 +        if (params.autoBreath && _breath)
632 +        {
633 +            // Note: _model->LoadParameters and SaveParameters is needed
634 +            // before - see above.
635 +            _breath->UpdateParameters(_model, deltaTimeSeconds);
636 +        }
637      }
638  
639      // 物理演算の設定
640 @@ -394,22 +351,6 @@ void LAppModel::Update()
641          _physics->Evaluate(_model, deltaTimeSeconds);
642      }
643  
644 -    // リップシンクの設定
645 -    if (_lipSync)
646 -    {
647 -        // リアルタイムでリップシンクを行う場合、システムから音量を取得して0〜1の範囲で値を入力します。
648 -        csmFloat32 value = 0.0f;
649 -
650 -        // 状態更新/RMS値取得
651 -        _wavFileHandler.Update(deltaTimeSeconds);
652 -        value = _wavFileHandler.GetRms();
653 -
654 -        for (csmUint32 i = 0; i < _lipSyncIds.GetSize(); ++i)
655 -        {
656 -            _model->AddParameterValue(_lipSyncIds[i], value, 0.8f);
657 -        }
658 -    }
659 -
660      // ポーズの設定
661      if (_pose != NULL)
662      {
663 @@ -480,7 +421,6 @@ CubismMotionQueueEntryHandle LAppModel::
664      {
665          csmString path = voice;
666          path = _modelHomeDir + path;
667 -        _wavFileHandler.Start(path);
668      }
669  
670      if (_debugMode)
671 @@ -659,3 +599,37 @@ csmBool LAppModel::HasMocConsistencyFrom
672  
673      return consistency;
674  }
675 +
676 +void LAppModel::SetFacialLandmarkDetector(FacialLandmarkDetector *detector)
677 +{
678 +    _detector = detector;
679 +}
680 +
681 +Csm::csmString LAppModel::_(std::string s)
682 +{
683 +    std::string ans;
684 +    if (_useOldParamId)
685 +    {
686 +        if (s == "ParamTere")
687 +        {
688 +            ans = "PARAM_CHEEK";
689 +        }
690 +        else
691 +        {
692 +            for (size_t i = 0; i < s.size(); i++)
693 +            {
694 +                if (std::isupper(s[i]) && i != 0)
695 +                {
696 +                    ans += '_';
697 +                }
698 +                ans += std::toupper(s[i]);
699 +            }
700 +        }
701 +    }
702 +    else
703 +    {
704 +        ans = s;
705 +    }
706 +    return csmString(ans.c_str());
707 +}
708 +
709 diff -pruN --exclude build ./demo_clean/src/LAppModel.hpp ./demo_dev/src/LAppModel.hpp
710 --- ./demo_clean/src/LAppModel.hpp      2025-05-29 18:28:26.391768300 +0100
711 +++ ./demo_dev/src/LAppModel.hpp        2025-05-29 23:50:49.650320100 +0100
712 @@ -12,8 +12,8 @@
713  #include <Type/csmRectF.hpp>
714  #include <Rendering/OpenGL/CubismOffscreenSurface_OpenGLES2.hpp>
715  
716 -#include "LAppWavFileHandler_Common.hpp"
717  #include "LAppModel_Common.hpp"
718 +#include "facial_landmark_detector.h"
719  
720  /**
721   * @brief ユーザーが実際に使用するモデルの実装クラス<br>
722 @@ -25,8 +25,11 @@ class LAppModel : public LAppModel_Commo
723  public:
724      /**
725       * @brief コンストラクタ
726 +     *
727 +     * @param[in] useOldParamId : If true, translate new (Cubism 3+)
728 +     *                            parameter IDs to old (Cubism 2.1)  ones
729       */
730 -    LAppModel();
731 +    LAppModel(bool useOldParamId);
732  
733      /**
734       * @brief デストラクタ
735 @@ -124,6 +127,13 @@ public:
736       */
737      Csm::csmBool HasMocConsistencyFromFile(const Csm::csmChar* mocFileName);
738  
739 +    /**
740 +     * @brief Set the pointer to the FacialLandmarkDetector instance
741 +     *
742 +     * @param[in] detector : Pointer to FacialLandmarkDetector instance
743 +     */
744 +    void SetFacialLandmarkDetector(FacialLandmarkDetector *detector);
745 +
746  protected:
747      /**
748       *  @brief  モデルを描画する処理。モデルを描画する空間のView-Projection行列を渡す。
749 @@ -177,6 +187,17 @@ private:
750      */
751      void ReleaseExpressions();
752  
753 +    /**
754 +     * @brief Translate new (Cubism 3+) parameter IDs to old (Cubism 2.1) ones
755 +     *
756 +     * @param[in] s : New parameter ID
757 +     *
758 +     * @return Old parameter ID
759 +     */
760 +    Csm::csmString _(std::string s);
761 +
762 +    bool _useOldParamId;
763 +
764      Csm::ICubismModelSetting* _modelSetting; ///< モデルセッティング情報
765      Csm::csmString _modelHomeDir; ///< モデルセッティングが置かれたディレクトリ
766      Csm::csmFloat32 _userTimeSeconds; ///< デルタ時間の積算値[秒]
767 @@ -193,7 +214,7 @@ private:
768      const Csm::CubismId* _idParamEyeBallX; ///< パラメータID: ParamEyeBallX
769      const Csm::CubismId* _idParamEyeBallY; ///< パラメータID: ParamEyeBallXY
770  
771 -    LAppWavFileHandler_Common _wavFileHandler; ///< wavファイルハンドラ
772 -
773      Csm::Rendering::CubismOffscreenSurface_OpenGLES2  _renderBuffer;   ///< フレームバッファ以外の描画先
774 +
775 +    FacialLandmarkDetector *_detector;
776  };
777 diff -pruN --exclude build ./demo_clean/src/LAppTextureManager.cpp ./demo_dev/src/LAppTextureManager.cpp
778 --- ./demo_clean/src/LAppTextureManager.cpp     2025-05-29 18:28:26.250309900 +0100
779 +++ ./demo_dev/src/LAppTextureManager.cpp       2025-05-29 23:51:06.229853200 +0100
780 @@ -89,6 +89,46 @@ LAppTextureManager::TextureInfo* LAppTex
781  
782  }
783  
784 +LAppTextureManager::TextureInfo* LAppTextureManager::CreateTextureFromColor(
785 +    uint8_t red, uint8_t green, uint8_t blue, uint8_t alpha
786 +)
787 +{
788 +    constexpr int width = 8, height = 8;
789 +
790 +    uint8_t pixels[height][width][4];
791 +    for (std::size_t h = 0; h < height; h++)
792 +    {
793 +        for (std::size_t w = 0; w < width; w++)
794 +        {
795 +            pixels[h][w][0] = red;
796 +            pixels[h][w][1] = green;
797 +            pixels[h][w][2] = blue;
798 +            pixels[h][w][3] = alpha;
799 +        }
800 +    }
801 +
802 +    GLuint textureId;
803 +    glGenTextures(1, &textureId);
804 +    glBindTexture(GL_TEXTURE_2D, textureId);
805 +    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
806 +
807 +    glGenerateMipmap(GL_TEXTURE_2D);
808 +    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
809 +    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
810 +    glBindTexture(GL_TEXTURE_2D, 0);
811 +
812 +
813 +    LAppTextureManager::TextureInfo* textureInfo = new LAppTextureManager::TextureInfo();
814 +    textureInfo->fileName = "";
815 +    textureInfo->width = width;
816 +    textureInfo->height = height;
817 +    textureInfo->id = textureId;
818 +
819 +    _texturesInfo.PushBack(textureInfo);
820 +
821 +    return textureInfo;
822 +}
823 +
824  void LAppTextureManager::ReleaseTextures()
825  {
826      for (Csm::csmUint32 i = 0; i < _texturesInfo.GetSize(); i++)
827 diff -pruN --exclude build ./demo_clean/src/LAppTextureManager.hpp ./demo_dev/src/LAppTextureManager.hpp
828 --- ./demo_clean/src/LAppTextureManager.hpp     2025-05-29 18:28:26.344512900 +0100
829 +++ ./demo_dev/src/LAppTextureManager.hpp       2025-05-29 23:51:13.635314000 +0100
830 @@ -41,6 +41,8 @@ public:
831      */
832      TextureInfo* CreateTextureFromPngFile(std::string fileName);
833  
834 +    TextureInfo *CreateTextureFromColor(uint8_t red, uint8_t green, uint8_t blue, uint8_t alpha = 255);
835 +
836      /**
837      * @brief 画像の解放
838      *
839 diff -pruN --exclude build ./demo_clean/src/LAppView.cpp ./demo_dev/src/LAppView.cpp
840 --- ./demo_clean/src/LAppView.cpp       2025-05-29 18:28:25.905329300 +0100
841 +++ ./demo_dev/src/LAppView.cpp 2025-05-29 23:51:22.898336200 +0100
842 @@ -83,12 +83,7 @@ void LAppView::Render()
843      int maxWidth, maxHeight;
844      glfwGetWindowSize(LAppDelegate::GetInstance()->GetWindow(), &maxWidth, &maxHeight);
845      _back->SetWindowSize(maxWidth, maxHeight);
846 -    _gear->SetWindowSize(maxWidth, maxHeight);
847 -    _power->SetWindowSize(maxWidth, maxHeight);
848 -
849      _back->Render();
850 -    _gear->Render();
851 -    _power->Render();
852  
853      LAppLive2DManager* Live2DManager = LAppLive2DManager::GetInstance();
854  
855 @@ -131,35 +126,17 @@ void LAppView::InitializeSprite()
856      glfwGetWindowSize(LAppDelegate::GetInstance()->GetWindow(), &width, &height);
857  
858      LAppTextureManager* textureManager = LAppDelegate::GetInstance()->GetTextureManager();
859 -    const string resourcesPath = ResourcesPath;
860  
861 -    string imageName = BackImageName;
862 -    LAppTextureManager::TextureInfo* backgroundTexture = textureManager->CreateTextureFromPngFile(resourcesPath + imageName);
863 +
864 +    LAppTextureManager::TextureInfo* backgroundTexture =
865 +        textureManager->CreateTextureFromColor(0, 255, 0);
866  
867      float x = width * 0.5f;
868      float y = height * 0.5f;
869 -    float fWidth = static_cast<float>(backgroundTexture->width * 2.0f);
870 -    float fHeight = static_cast<float>(height * 0.95f);
871 +    float fWidth = static_cast<float>(width);
872 +    float fHeight = static_cast<float>(height);
873      _back = new LAppSprite(x, y, fWidth, fHeight, backgroundTexture->id, programId);
874  
875 -    imageName = GearImageName;
876 -    LAppTextureManager::TextureInfo* gearTexture = textureManager->CreateTextureFromPngFile(resourcesPath + imageName);
877 -
878 -    x = static_cast<float>(width - gearTexture->width * 0.5f);
879 -    y = static_cast<float>(height - gearTexture->height * 0.5f);
880 -    fWidth = static_cast<float>(gearTexture->width);
881 -    fHeight = static_cast<float>(gearTexture->height);
882 -    _gear = new LAppSprite(x, y, fWidth, fHeight, gearTexture->id, programId);
883 -
884 -    imageName = PowerImageName;
885 -    LAppTextureManager::TextureInfo* powerTexture = textureManager->CreateTextureFromPngFile(resourcesPath + imageName);
886 -
887 -    x = static_cast<float>(width - powerTexture->width * 0.5f);
888 -    y = static_cast<float>(powerTexture->height * 0.5f);
889 -    fWidth = static_cast<float>(powerTexture->width);
890 -    fHeight = static_cast<float>(powerTexture->height);
891 -    _power = new LAppSprite(x, y, fWidth, fHeight, powerTexture->id, programId);
892 -
893      // 画面全体を覆うサイズ
894      x = width * 0.5f;
895      y = height * 0.5f;
896 @@ -198,17 +175,6 @@ void LAppView::OnTouchesEnded(float px,
897          }
898          live2DManager->OnTap(x, y);
899  
900 -        // 歯車にタップしたか
901 -        if (_gear->IsHit(px, py))
902 -        {
903 -            live2DManager->NextScene();
904 -        }
905 -
906 -        // 電源ボタンにタップしたか
907 -        if (_power->IsHit(px, py))
908 -        {
909 -            LAppDelegate::GetInstance()->AppEnd();
910 -        }
911      }
912  }
913  
914 diff -pruN --exclude build ./demo_clean/src/main.cpp ./demo_dev/src/main.cpp
915 --- ./demo_clean/src/main.cpp   2025-05-29 18:28:26.046369000 +0100
916 +++ ./demo_dev/src/main.cpp     2025-05-29 23:48:14.989176800 +0100
917 @@ -5,26 +5,160 @@
918   * that can be found at https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
919   */
920  
921 +#include <thread>
922 +#include <stdexcept>
923 +#include <sstream>
924 +
925 +#ifdef __cpp_lib_filesystem
926 +#include <filesystem>
927 +namespace fs = std::filesystem;
928 +#else
929 +#include <experimental/filesystem>
930 +namespace fs = std::experimental::filesystem;
931 +#endif
932 +
933 +
934  #include "LAppDelegate.hpp"
935 -#include <windows.h>
936 +#include "LAppLive2DManager.hpp"
937 +#include "facial_landmark_detector.h"
938  
939 -int main()
940 +struct CmdArgs
941  {
942 -    // Setting the console character encoding to UTF-8
943 -    UINT preConsoleOutputCP = GetConsoleOutputCP();
944 -    SetConsoleOutputCP(65001);
945 +    int windowWidth;
946 +    int windowHeight;
947 +    std::string windowTitle;
948 +    std::string rootDir;
949 +    float scaleFactor;
950 +    float translateX;
951 +    float translateY;
952 +    std::string modelName;
953 +    bool oldId; // If true, translate new (Cubism 3+) parameter IDs to old (Cubism 2.1) IDs
954 +    std::string cfgPath; // Path to config file for FacialLandmarkDetector
955 +};
956  
957 -    // create the application instance
958 -    if (LAppDelegate::GetInstance()->Initialize() == GL_FALSE)
959 +CmdArgs parseArgv(int argc, char *argv[])
960 +{
961 +    // I think the command-line args are simple enough to not justify using a library...
962 +    CmdArgs cmdArgs;
963 +    // Set default values
964 +    cmdArgs.windowWidth = 600;
965 +    cmdArgs.windowHeight = 600;
966 +    cmdArgs.windowTitle = "FacialLandmarksForCubism example";
967 +    cmdArgs.rootDir = fs::current_path().string();
968 +    cmdArgs.scaleFactor = 4.5f;
969 +    cmdArgs.translateX = 0.0f;
970 +    cmdArgs.translateY = -3.1f;
971 +    cmdArgs.modelName = "Haru";
972 +    cmdArgs.oldId = false;
973 +    cmdArgs.cfgPath = "";
974 +
975 +    int i = 1;
976 +    while (i < argc)
977      {
978 -        SetConsoleOutputCP(preConsoleOutputCP);
979 -        return 1;
980 +        std::string arg = argv[i];
981 +        std::stringstream ss;
982 +
983 +        if (arg == "--window-width" || arg == "-W") // capital W for consistency with height
984 +        {
985 +            ss << argv[i + 1];
986 +            if (!(ss >> cmdArgs.windowWidth))
987 +            {
988 +                throw std::runtime_error("Invalid argument for window width");
989 +            }
990 +        }
991 +        else if (arg == "--window-height" || arg == "-H") // avoiding "-h", typically for help
992 +        {
993 +            ss << argv[i + 1];
994 +            if (!(ss >> cmdArgs.windowHeight))
995 +            {
996 +                throw std::runtime_error("Invalid argument for window height");
997 +            }
998 +        }
999 +        else if (arg == "--window-title" || arg == "-t")
1000 +        {
1001 +            cmdArgs.windowTitle = argv[i + 1];
1002 +        }
1003 +        else if (arg == "--root-dir" || arg == "-d")
1004 +        {
1005 +            cmdArgs.rootDir = argv[i + 1];
1006 +        }
1007 +        else if (arg == "--scale-factor" || arg == "-f")
1008 +        {
1009 +            ss << argv[i + 1];
1010 +            if (!(ss >> cmdArgs.scaleFactor))
1011 +            {
1012 +                throw std::runtime_error("Invalid argument for scale factor");
1013 +            }
1014 +        }
1015 +        else if (arg == "--translate-x" || arg == "-x")
1016 +        {
1017 +            ss << argv[i + 1];
1018 +            if (!(ss >> cmdArgs.translateX))
1019 +            {
1020 +                throw std::runtime_error("Invalid argument for translate X");
1021 +            }
1022 +        }
1023 +        else if (arg == "--translate-y" || arg == "-y")
1024 +        {
1025 +            ss << argv[i + 1];
1026 +            if (!(ss >> cmdArgs.translateY))
1027 +            {
1028 +                throw std::runtime_error("Invalid argument for translate Y");
1029 +            }
1030 +        }
1031 +        else if (arg == "--model" || arg == "-m")
1032 +        {
1033 +            cmdArgs.modelName = argv[i + 1];
1034 +        }
1035 +        else if (arg == "--config" || arg == "-c")
1036 +        {
1037 +            cmdArgs.cfgPath = argv[i + 1];
1038 +        }
1039 +        else if (arg == "--old-param-id" || arg == "-o")
1040 +        {
1041 +            cmdArgs.oldId = (argv[i + 1][0] == '1');
1042 +        }
1043 +        else
1044 +        {
1045 +            throw std::runtime_error("Unrecognized argument: " + arg);
1046 +        }
1047 +
1048 +        i += 2;
1049      }
1050  
1051 -    LAppDelegate::GetInstance()->Run();
1052 +    return cmdArgs;
1053 +}
1054  
1055 -    SetConsoleOutputCP(preConsoleOutputCP);
1056 +int main(int argc, char* argv[])
1057 +{
1058 +    auto cmdArgs = parseArgv(argc, argv);
1059 +
1060 +    LAppDelegate *delegate = LAppDelegate::GetInstance();
1061 +
1062 +    if (!delegate->Initialize(cmdArgs.windowWidth,
1063 +                              cmdArgs.windowHeight,
1064 +                              cmdArgs.windowTitle.c_str()))
1065 +    {
1066 +        throw std::runtime_error("Unable to initialize LAppDelegate");
1067 +    }
1068 +
1069 +    FacialLandmarkDetector detector(cmdArgs.cfgPath);
1070 +
1071 +    std::thread detectorThread(&FacialLandmarkDetector::mainLoop,
1072 +                               &detector);
1073 +
1074 +    LAppLive2DManager *manager = LAppLive2DManager::GetInstance();
1075 +    manager->SetModel(cmdArgs.modelName, cmdArgs.oldId);
1076 +
1077 +    manager->SetProjectionScaleTranslate(cmdArgs.scaleFactor,
1078 +                                         cmdArgs.translateX,
1079 +                                         cmdArgs.translateY);
1080 +    manager->SetFacialLandmarkDetector(&detector);
1081 +
1082 +    delegate->Run();
1083 +
1084 +    detector.stop();
1085 +    detectorThread.join();
1086  
1087      return 0;
1088  }
1089 -