Upgrade to Cubism 5 Release R4.
[facial-landmarks-for-cubism.git] / example / demo_win.patch
... / ...
CommitLineData
1diff -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
48diff -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);
87diff -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 解放する。
100diff -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+}
320diff -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 };
425diff -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+
709diff -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 };
777diff -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++)
827diff -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 *
839diff -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
914diff -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-