This commit is contained in:
Misaki
2025-12-04 19:11:29 +08:00
commit bb600bbbc4
2741 changed files with 364700 additions and 0 deletions
+52
View File
@@ -0,0 +1,52 @@
/**
* Copyright(c) Live2D Inc. All rights reserved.
*
* Use of this source code is governed by the Live2D Open Software license
* that can be found at https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
*/
#pragma once
#include <CubismFramework.hpp>
#include <ICubismAllocator.hpp>
/**
* @brief メモリアロケーションを実装するクラス。
*
* メモリ確保・解放処理のインターフェースの実装。
* フレームワークから呼び出される。
*
*/
class LAppAllocator : public Csm::ICubismAllocator
{
/**
* @brief メモリ領域を割り当てる。
*
* @param[in] size 割り当てたいサイズ。
* @return 指定したメモリ領域
*/
void* Allocate(const Csm::csmSizeType size);
/**
* @brief メモリ領域を解放する
*
* @param[in] memory 解放するメモリ。
*/
void Deallocate(void* memory);
/**
* @brief
*
* @param[in] size 割り当てたいサイズ。
* @param[in] alignment 割り当てたいサイズ。
* @return alignedAddress
*/
void* AllocateAligned(const Csm::csmSizeType size, const Csm::csmUint32 alignment);
/**
* @brief
*
* @param[in] alignedMemory 解放するメモリ。
*/
void DeallocateAligned(void* alignedMemory);
};
+65
View File
@@ -0,0 +1,65 @@
/**
* Copyright(c) Live2D Inc. All rights reserved.
*
* Use of this source code is governed by the Live2D Open Software license
* that can be found at https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
*/
#pragma once
#include <CubismFramework.hpp>
/**
* @brief Sample Appで使用する定数
*
*/
namespace LAppDefine {
using namespace Csm;
extern const csmFloat32 ViewScale; ///< 拡大縮小率
extern const csmFloat32 ViewMaxScale; ///< 拡大縮小率の最大値
extern const csmFloat32 ViewMinScale; ///< 拡大縮小率の最小値
extern const csmFloat32 ViewLogicalLeft; ///< 論理的なビュー座標系の左端の値
extern const csmFloat32 ViewLogicalRight; ///< 論理的なビュー座標系の右端の値
extern const csmFloat32 ViewLogicalBottom; ///< 論理的なビュー座標系の下端の値
extern const csmFloat32 ViewLogicalTop; ///< 論理的なビュー座標系の上端の値
extern const csmFloat32 ViewLogicalMaxLeft; ///< 論理的なビュー座標系の左端の最大値
extern const csmFloat32 ViewLogicalMaxRight; ///< 論理的なビュー座標系の右端の最大値
extern const csmFloat32 ViewLogicalMaxBottom; ///< 論理的なビュー座標系の下端の最大値
extern const csmFloat32 ViewLogicalMaxTop; ///< 論理的なビュー座標系の上端の最大値
extern const csmChar* ResourcesPath; ///< 素材パス
extern const csmChar* BackImageName; ///< 背景画像ファイル
extern const csmChar* GearImageName; ///< 歯車画像ファイル
extern const csmChar* PowerImageName; ///< 終了ボタン画像ファイル
// モデル定義--------------------------------------------
// 外部定義ファイル(json)と合わせる
extern const csmChar* MotionGroupIdle; ///< アイドリング時に再生するモーションのリスト
extern const csmChar* MotionGroupTapBody; ///< 体をタップした時に再生するモーションのリスト
// 外部定義ファイル(json)と合わせる
extern const csmChar* HitAreaNameHead; ///< 当たり判定の[Head]タグ
extern const csmChar* HitAreaNameBody; ///< 当たり判定の[Body]タグ
// モーションの優先度定数
extern const csmInt32 PriorityNone; ///< モーションの優先度定数: 0
extern const csmInt32 PriorityIdle; ///< モーションの優先度定数: 1
extern const csmInt32 PriorityNormal; ///< モーションの優先度定数: 2
extern const csmInt32 PriorityForce; ///< モーションの優先度定数: 3
extern const csmBool MocConsistencyValidationEnable; ///< MOC3の整合性検証機能の有効・無効
// デバッグ用ログの表示
extern const csmBool DebugLogEnable; ///< デバッグ用ログ表示の有効・無効
extern const csmBool DebugTouchLogEnable; ///< タッチ処理のデバッグ用ログ表示の有効・無効
// Frameworkから出力するログのレベル設定
extern const CubismFramework::Option::LogLevel CubismLoggingLevel;
// デフォルトのレンダーターゲットサイズ
extern const csmInt32 RenderTargetWidth;
extern const csmInt32 RenderTargetHeight;
}
+163
View File
@@ -0,0 +1,163 @@
/**
* Copyright(c) Live2D Inc. All rights reserved.
*
* Use of this source code is governed by the Live2D Open Software license
* that can be found at https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
*/
#pragma once
#include <GL/glew.h>
#include <GLFW/glfw3.h>
#include "LAppAllocator.hpp"
#include "GLCore.h"
class LAppView;
class LAppTextureManager;
/**
* @brief アプリケーションクラス。
* Cubism SDK の管理を行う。
*/
class LAppDelegate
{
public:
/**
* @brief クラスのインスタンス(シングルトン)を返す。<br>
* インスタンスが生成されていない場合は内部でインスタンを生成する。
*
* @return クラスのインスタンス
*/
static LAppDelegate* GetInstance();
/**
* @brief クラスのインスタンス(シングルトン)を解放する。
*
*/
static void ReleaseInstance();
// 新增
void resize(int width, int height);
// 新增
void update();
/**
* @brief APPに必要なものを初期化する。
*/
//bool Initialize();
bool Initialize(GLCore* window);
/**
* @brief 解放する。
*/
void Release();
/**
* @brief 実行処理。
*/
//void Run(); // Misaki 修改
/**
* @brief OpenGL用 glfwSetMouseButtonCallback用関数。
*
* @param[in] window コールバックを呼んだWindow情報
* @param[in] button ボタン種類
* @param[in] action 実行結果
* @param[in] modify
*/
void OnMouseCallBack(GLFWwindow* window, int button, int action, int modify);
/**
* @brief OpenGL用 glfwSetCursorPosCallback用関数。
*
* @param[in] window コールバックを呼んだWindow情報
* @param[in] x x座標
* @param[in] y x座標
*/
void OnMouseCallBack(GLFWwindow* window, double x, double y);
/**
* @brief シェーダーを登録する。
*/
GLuint CreateShader();
/**
* @brief Window情報を取得する。
*/
GLCore* GetWindow() { return _window; } // Misaki 修改
/**
* @brief View情報を取得する。
*/
LAppView* GetView() { return _view; }
/**
* @brief アプリケーションを終了するかどうか。
*/
bool GetIsEnd() { return _isEnd; }
/**
* @brief アプリケーションを終了する。
*/
void AppEnd() { _isEnd = true; }
LAppTextureManager* GetTextureManager() { return _textureManager; }
private:
/**
* @brief コンストラクタ
*/
LAppDelegate();
/**
* @brief デストラクタ
*/
~LAppDelegate();
/**
* @brief Cubism SDK の初期化
*/
void InitializeCubism();
/**
* @brief CreateShader内部関数 エラーチェック
*/
bool CheckShader(GLuint shaderId);
LAppAllocator _cubismAllocator; ///< Cubism SDK Allocator
Csm::CubismFramework::Option _cubismOption; ///< Cubism SDK Option
//GLFWwindow* _window; ///< OpenGL ウィンドウ
GLCore* _window; ///< Misaki 修改
LAppView* _view; ///< View情報
bool _captured; ///< クリックしているか
float _mouseX; ///< マウスX座標
float _mouseY; ///< マウスY座標
bool _isEnd; ///< APP終了しているか
LAppTextureManager* _textureManager; ///< テクスチャマネージャー
int _windowWidth; ///< Initialize関数で設定したウィンドウ幅
int _windowHeight; ///< Initialize関数で設定したウィンドウ高さ
};
class EventHandler
{
public:
/**
* @brief glfwSetMouseButtonCallback用コールバック関数。
*/
static void OnMouseCallBack(GLFWwindow* window, int button, int action, int modify)
{
LAppDelegate::GetInstance()->OnMouseCallBack(window, button, action, modify);
}
/**
* @brief glfwSetCursorPosCallback用コールバック関数。
*/
static void OnMouseCallBack(GLFWwindow* window, double x, double y)
{
LAppDelegate::GetInstance()->OnMouseCallBack(window, x, y);
}
};
+170
View File
@@ -0,0 +1,170 @@
/**
* Copyright(c) Live2D Inc. All rights reserved.
*
* Use of this source code is governed by the Live2D Open Software license
* that can be found at https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
*/
#pragma once
#include <CubismFramework.hpp>
#include <Math/CubismMatrix44.hpp>
#include <Type/csmVector.hpp>
#include <string>
class LAppModel;
/**
* @brief サンプルアプリケーションにおいてCubismModelを管理するクラス<br>
* モデル生成と破棄、タップイベントの処理、モデル切り替えを行う。
*
*/
class LAppLive2DManager
{
public:
/**
* @brief クラスのインスタンス(シングルトン)を返す。<br>
* インスタンスが生成されていない場合は内部でインスタンを生成する。
*
* @return クラスのインスタンス
*/
static LAppLive2DManager* GetInstance();
/**
* @brief クラスのインスタンス(シングルトン)を解放する。
*
*/
static void ReleaseInstance();
/**
* @brief Resources フォルダにあるモデルフォルダ名をセットする
*
*
* 无需多言,这个也宣布弃用了,这个函数功能是自动扫描路径下所有模型
*/
void SetUpModel();
/**
* @brief Resources フォルダにあるモデルフォルダ名を取得する
*
*/
Csm::csmVector<Csm::csmString> GetModelDir() const;
/**
* @brief Resources フォルダにあるモデルフォルダのサイズを取得する
*
*/
Csm::csmInt32 GetModelDirSize() const;
/**
* @brief 現在のシーンで保持しているモデルを返す
*
* @param[in] no モデルリストのインデックス値
* @return モデルのインスタンスを返す。インデックス値が範囲外の場合はNULLを返す。
*/
LAppModel* GetModel(Csm::csmUint32 no) const;
/**
* @brief 現在のシーンで保持しているすべてのモデルを解放する
*
*/
void ReleaseAllModel();
/**
* @brief 画面をドラッグしたときの処理
*
* @param[in] x 画面のX座標
* @param[in] y 画面のY座標
*/
void OnDrag(Csm::csmFloat32 x, Csm::csmFloat32 y) const;
/**
* @brief 画面をタップしたときの処理
*
* @param[in] x 画面のX座標
* @param[in] y 画面のY座標
*/
void OnTap(Csm::csmFloat32 x, Csm::csmFloat32 y);
/**
* @brief 画面を更新するときの処理
* モデルの更新処理および描画処理を行う
*/
void OnUpdate() const;
/**
* @brief 从指定路径加载模型
*
* @param[in] modelPath 模型文件的路径
* @param[in] fileName 模型文件的名称
*
* Misaki 增设于2024.12.17 <br>
*
* 举个例子:<br>
* LoadModelFromPath("Resources/Haru/", "Haru.model3.json");<br>
*
* 至于为什么要这样拆分,只是为了适应底层的模型加载函数<br>
* 你可以选择再上层封装,将传入的路径拆分为路径和文件名,然后调用本函数即可<br>
*/
void LoadModelFromPath(const std::string& modelPath, const std::string& fileName);
/**
* @brief 次のシーンに切り替える<br>
* サンプルアプリケーションではモデルセットの切り替えを行う。
*/
void NextScene();
/**
* @brief シーンを切り替える<br>
* サンプルアプリケーションではモデルセットの切り替えを行う。
*
* 如果你看到了这段文字,就代表你用的是被Misaki修改后的版本
* 那么现在告诉你,这个函数已经弃用,请使用LoadModelFromPath来进行导入模型文件
* 以及自行设计模型选择器
* 至于为什么修改,原生的SDK是自动查找指定目录的模型文件,自动加载的
* 但这样就与实际使用的需求大不相同,我们需要能够手动选择
*/
void ChangeScene(Csm::csmInt32 index);
/**
* @brief 启动当前活动模型的唇形同步并播放指定的 WAV 文件
* @param wavFilePath WAV 文件的路径
*/
void StartLipSync(const Csm::csmString& wavFilePath);
/**
* @brief 启动指定模型的唇形同步并播放指定的 WAV 文件
* @param modelIndex 模型索引
* @param wavFilePath WAV 文件的路径
*/
void StartLipSync(Live2D::Cubism::Framework::csmUint32 modelIndex, const Csm::csmString& wavFilePath);
/**
* @brief モデル個数を得る
* @return 所持モデル個数
*/
Csm::csmUint32 GetModelNum() const;
/**
* @brief viewMatrixをセットする
*/
void SetViewMatrix(Live2D::Cubism::Framework::CubismMatrix44* m);
private:
/**
* @brief コンストラクタ
*/
LAppLive2DManager();
/**
* @brief デストラクタ
*/
virtual ~LAppLive2DManager();
Csm::CubismMatrix44* _viewMatrix; ///< モデル描画に用いるView行列
Csm::csmVector<LAppModel*> _models; ///< モデルインスタンスのコンテナ
Csm::csmInt32 _sceneIndex; ///< 表示するシーンのインデックス値
Csm::csmVector<Csm::csmString> _modelDir; ///< モデルディレクトリ名のコンテナ
};
+231
View File
@@ -0,0 +1,231 @@
/**
* Copyright(c) Live2D Inc. All rights reserved.
*
* Use of this source code is governed by the Live2D Open Software license
* that can be found at https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
*/
#pragma once
#include <CubismFramework.hpp>
#include <Model/CubismUserModel.hpp>
#include <ICubismModelSetting.hpp>
#include <Type/csmRectF.hpp>
#include <Rendering/OpenGL/CubismOffscreenSurface_OpenGLES2.hpp>
#include "LAppWavFileHandler.hpp"
/**
* @brief ユーザーが実際に使用するモデルの実装クラス<br>
* モデル生成、機能コンポーネント生成、更新処理とレンダリングの呼び出しを行う。
*
*/
class LAppModel : public Csm::CubismUserModel
{
public:
/**
* @brief コンストラクタ
*/
LAppModel();
/**
* @brief デストラクタ
*
*/
virtual ~LAppModel();
/**
* @brief 获得Idle动画总数量
* @author Misaki
* @return int
*/
int getIdleMotionCount();
/**
* @brief 获得TapBody动画总数量
* @author Misaki
* @return int
*/
int getTapBodyMotionCount();
/**
* @brief 启动唇形同步并播放指定的 WAV 文件
* @param filePath WAV 文件的路径(csmString 类型)
* @author Misaki
*/
void StartLipSync(const Csm::csmString& filePath);
/**
* @brief 停止唇形同步并停止播放指定的 WAV 文件
* @author Misaki
*/
void StopLipSync();
/**
* @brief model3.jsonが置かれたディレクトリとファイルパスからモデルを生成する
*
*/
void LoadAssets(const Csm::csmChar* dir, const Csm::csmChar* fileName);
/**
* @brief レンダラを再構築する
*
*/
void ReloadRenderer();
/**
* @brief モデルの更新処理。モデルのパラメータから描画状態を決定する。
*
*/
void Update();
/**
* @brief モデルを描画する処理。モデルを描画する空間のView-Projection行列を渡す。
*
* @param[in] matrix View-Projection行列
*/
void Draw(Csm::CubismMatrix44& matrix);
/**
* @brief 引数で指定したモーションの再生を開始する。
*
* @param[in] group モーショングループ名
* @param[in] no グループ内の番号
* @param[in] priority 優先度
* @param[in] onFinishedMotionHandler モーション再生終了時に呼び出されるコールバック関数。NULLの場合、呼び出されない。
* @return 開始したモーションの識別番号を返す。個別のモーションが終了したか否かを判定するIsFinished()の引数で使用する。開始できない時は「-1」
*/
Csm::CubismMotionQueueEntryHandle StartMotion(const Csm::csmChar* group, Csm::csmInt32 no, Csm::csmInt32 priority, Csm::ACubismMotion::FinishedMotionCallback onFinishedMotionHandler = NULL);
/**
* @brief 开始播放随机选择的动画。
*
* @param[in] group 动画组名称
* @param[in] priority 优先级
* @param[in] onFinishedMotionHandler 动画播放结束时被调用的回调函数。如果为NULL,则不被调用。
* @return 返回开始播放的动画的标识编号。用于作为判断特定动画是否结束的IsFinished()函数的参数。如果无法开始播放,则返回「-1」
*/
Csm::CubismMotionQueueEntryHandle StartRandomMotion(const Csm::csmChar* group, Csm::csmInt32 priority, Csm::ACubismMotion::FinishedMotionCallback onFinishedMotionHandler = NULL);
/**
* @brief 引数で指定した表情モーションをセットする
*
* @param expressionID 表情モーションのID
*/
void SetExpression(const Csm::csmChar* expressionID);
/**
* @brief ランダムに選ばれた表情モーションをセットする
*
*/
void SetRandomExpression();
/**
* @brief イベントの発火を受け取る
*
*/
virtual void MotionEventFired(const Live2D::Cubism::Framework::csmString& eventValue);
/**
* @brief 当たり判定テスト。<br>
* 指定IDの頂点リストから矩形を計算し、座標が矩形範囲内か判定する。
*
* @param[in] hitAreaName 当たり判定をテストする対象のID
* @param[in] x 判定を行うX座標
* @param[in] y 判定を行うY座標
*/
virtual Csm::csmBool HitTest(const Csm::csmChar* hitAreaName, Csm::csmFloat32 x, Csm::csmFloat32 y);
/**
* @brief 別ターゲットに描画する際に使用するバッファの取得
*/
Csm::Rendering::CubismOffscreenSurface_OpenGLES2& GetRenderBuffer();
/**
* @brief .moc3ファイルの整合性をチェックする
*
* @param[in] mocName MOC3ファイル名
* @return MOC3に整合性があれば'true'、そうでなければ'false'。
*/
Csm::csmBool HasMocConsistencyFromFile(const Csm::csmChar* mocFileName);
protected:
/**
* @brief モデルを描画する処理。モデルを描画する空間のView-Projection行列を渡す。
*
*/
void DoDraw();
private:
/**
* @brief model3.jsonからモデルを生成する。<br>
* model3.jsonの記述に従ってモデル生成、モーション、物理演算などのコンポーネント生成を行う。
*
* @param[in] setting ICubismModelSettingのインスタンス
*
*/
void SetupModel(Csm::ICubismModelSetting* setting);
/**
* @brief OpenGLのテクスチャユニットにテクスチャをロードする
*
*/
void SetupTextures();
/**
* @brief モーションデータをグループ名から一括でロードする。<br>
* モーションデータの名前は内部でModelSettingから取得する。
*
* @param[in] group モーションデータのグループ名
*/
void PreloadMotionGroup(const Csm::csmChar* group);
/**
* @brief モーションデータをグループ名から一括で解放する。<br>
* モーションデータの名前は内部でModelSettingから取得する。
*
* @param[in] group モーションデータのグループ名
*/
void ReleaseMotionGroup(const Csm::csmChar* group) const;
/**
* @brief すべてのモーションデータの解放
*
* すべてのモーションデータを解放する。
*/
void ReleaseMotions();
/**
* @brief すべての表情データの解放
*
* すべての表情データを解放する。
*/
void ReleaseExpressions();
Csm::ICubismModelSetting* _modelSetting; ///< モデルセッティング情報
Csm::csmString _modelHomeDir; ///< モデルセッティングが置かれたディレクトリ
Csm::csmFloat32 _userTimeSeconds; ///< デルタ時間の積算値[秒]
Csm::csmVector<Csm::CubismIdHandle> _eyeBlinkIds; ///< モデルに設定されたまばたき機能用パラメータID
Csm::csmVector<Csm::CubismIdHandle> _lipSyncIds; ///< モデルに設定されたリップシンク機能用パラメータID
Csm::csmMap<Csm::csmString, Csm::ACubismMotion*> _motions; ///< 読み込まれているモーションのリスト
Csm::csmMap<Csm::csmString, Csm::ACubismMotion*> _expressions; ///< 読み込まれている表情のリスト
Csm::csmVector<Csm::csmRectF> _hitArea;
Csm::csmVector<Csm::csmRectF> _userArea;
const Csm::CubismId* _idParamAngleX; ///< パラメータID: ParamAngleX
const Csm::CubismId* _idParamAngleY; ///< パラメータID: ParamAngleX
const Csm::CubismId* _idParamAngleZ; ///< パラメータID: ParamAngleX
const Csm::CubismId* _idParamBodyAngleX; ///< パラメータID: ParamBodyAngleX
const Csm::CubismId* _idParamEyeBallX; ///< パラメータID: ParamEyeBallX
const Csm::CubismId* _idParamEyeBallY; ///< パラメータID: ParamEyeBallXY
LAppWavFileHandler _wavFileHandler; ///< wavファイルハンドラ WAV文件处理器
// 低通滤波器参数
Live2D::Cubism::Framework::csmFloat32 alpha = 0.8f; // 滤波系数,范围在0到1之间,值越小,平滑效果越强
Live2D::Cubism::Framework::csmFloat32 filteredValue = 0.0f; // 滤波后的值
Csm::Rendering::CubismOffscreenSurface_OpenGLES2 _renderBuffer; ///< フレームバッファ以外の描画先
};
+100
View File
@@ -0,0 +1,100 @@
/**
* Copyright(c) Live2D Inc. All rights reserved.
*
* Use of this source code is governed by the Live2D Open Software license
* that can be found at https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
*/
#pragma once
#include <CubismFramework.hpp>
#include <string>
/**
* @brief プラットフォーム依存機能を抽象化する Cubism Platform Abstraction Layer.
*
* ファイル読み込みや時刻取得等のプラットフォームに依存する関数をまとめる
*
*/
class LAppPal
{
public:
/**
* @brief ファイルをバイトデータとして読み込む
*
* ファイルをバイトデータとして読み込む
*
* @param[in] filePath 読み込み対象ファイルのパス
* @param[out] outSize ファイルサイズ
* @return バイトデータ
*/
static Csm::csmByte* LoadFileAsBytes(const std::string filePath, Csm::csmSizeInt* outSize);
/**
* @brief バイトデータを解放する
*
* バイトデータを解放する
*
* @param[in] byteData 解放したいバイトデータ
*/
static void ReleaseBytes(Csm::csmByte* byteData);
/**
* @biref デルタ時間(前回フレームとの差分)を取得する
*
* @return デルタ時間[ms]
*
*/
static Csm::csmFloat32 GetDeltaTime();
static void UpdateTime();
/**
* @brief ログを出力する
*
* ログを出力する
*
* @param[in] format 書式付文字列
* @param[in] ... (可変長引数)文字列
*
*/
static void PrintLog(const Csm::csmChar* format, ...);
/**
* @brief ログを出力し最後に改行する
*
* ログを出力し最後に改行する
*
* @param[in] format 書式付文字列
* @param[in] ... (可変長引数)文字列
*
*/
static void PrintLogLn(const Csm::csmChar* format, ...);
/**
* @brief メッセージを出力する
*
* メッセージを出力する
*
* @param[in] message 文字列
*
*/
static void PrintMessage(const Csm::csmChar* message);
/**
* @brief メッセージを出力し最後に改行する
*
* メッセージを出力し最後に改行する
*
* @param[in] message 文字列
*
*/
static void PrintMessageLn(const Csm::csmChar* message);
private:
static double s_currentFrame;
static double s_lastFrame;
static double s_deltaTime;
};
+117
View File
@@ -0,0 +1,117 @@
/**
* Copyright(c) Live2D Inc. All rights reserved.
*
* Use of this source code is governed by the Live2D Open Software license
* that can be found at https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
*/
#pragma once
#include <GL/glew.h>
#include <GLFW/glfw3.h>
/**
* @brief スプライトを実装するクラス。
*
* テクスチャID、Rectの管理。
*
*/
class LAppSprite
{
public:
/**
* @brief Rect 構造体。
*/
struct Rect
{
public:
float left; ///< 左辺
float right; ///< 右辺
float up; ///< 上辺
float down; ///< 下辺
};
/**
* @brief コンストラクタ
*
* @param[in] x x座標
* @param[in] y y座標
* @param[in] width 横幅
* @param[in] height 高さ
* @param[in] textureId テクスチャID
* @param[in] programId シェーダID
*/
LAppSprite(float x, float y, float width, float height, GLuint textureId, GLuint programId);
/**
* @brief デストラクタ
*/
~LAppSprite();
/**
* @brief Getter テクスチャID
* @return テクスチャIDを返す
*/
GLuint GetTextureId() { return _textureId; }
/**
* @brief 描画する
*
*/
void Render() const;
/**
* @brief テクスチャIDを指定して描画する
*
*/
void RenderImmidiate(GLuint textureId, const GLfloat uvVertex[8]) const;
/**
* @brief コンストラクタ
*
* @param[in] pointX x座標
* @param[in] pointY y座標
*/
bool IsHit(float pointX, float pointY) const;
/**
* @brief 色設定
*
* @param[in] r (0.0~1.0)
* @param[in] g (0.0~1.0)
* @param[in] b (0.0~1.0)
* @param[in] a (0.0~1.0)
*/
void SetColor(float r, float g, float b, float a);
/**
* @brief サイズ再設定
*
* @param[in] x x座標
* @param[in] y y座標
* @param[in] width 横幅
* @param[in] height 高さ
*/
void ResetRect(float x, float y, float width, float height);
/**
* @brief ウインドウサイズ設定
*
* @param[in] width 横幅
* @param[in] height 高さ
*/
void SetWindowSize(int width, int height);
private:
GLuint _textureId; ///< テクスチャID
Rect _rect; ///< 矩形
int _positionLocation; ///< 位置アトリビュート
int _uvLocation; ///< UVアトリビュート
int _textureLocation; ///< テクスチャアトリビュート
int _colorLocation; ///< カラーアトリビュート
float _spriteColor[4]; ///< 表示カラー
int _maxWidth; ///< ウインドウ幅
int _maxHeight; ///< ウインドウ高さ
};
@@ -0,0 +1,108 @@
/**
* Copyright(c) Live2D Inc. All rights reserved.
*
* Use of this source code is governed by the Live2D Open Software license
* that can be found at https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
*/
#pragma once
#include <string>
#include <GL/glew.h>
#include <GLFW/glfw3.h>
#include <Type/csmVector.hpp>
/**
* @brief テクスチャ管理クラス
*
* 画像読み込み、管理を行うクラス。
*/
class LAppTextureManager
{
public:
/**
* @brief 画像情報構造体
*/
struct TextureInfo
{
GLuint id; ///< テクスチャID
int width; ///< 横幅
int height; ///< 高さ
std::string fileName; ///< ファイル名
};
/**
* @brief コンストラクタ
*/
LAppTextureManager();
/**
* @brief デストラクタ
*
*/
~LAppTextureManager();
/**
* @brief プリマルチプライ処理
*
* @param[in] red 画像のRed値
* @param[in] green 画像のGreen値
* @param[in] blue 画像のBlue値
* @param[in] alpha 画像のAlpha値
*
* @return プリマルチプライ処理後のカラー値
*/
inline unsigned int Premultiply(unsigned char red, unsigned char green, unsigned char blue, unsigned char alpha)
{
return static_cast<unsigned>(\
(red * (alpha + 1) >> 8) | \
((green * (alpha + 1) >> 8) << 8) | \
((blue * (alpha + 1) >> 8) << 16) | \
(((alpha)) << 24) \
);
}
/**
* @brief 画像読み込み
*
* @param[in] fileName 読み込む画像ファイルパス名
* @return 画像情報。読み込み失敗時はNULLを返す
*/
TextureInfo* CreateTextureFromPngFile(std::string fileName);
/**
* @brief 画像の解放
*
* 配列に存在する画像全てを解放する
*/
void ReleaseTextures();
/**
* @brief 画像の解放
*
* 指定したテクスチャIDの画像を解放する
* @param[in] textureId 解放するテクスチャID
**/
void ReleaseTexture(Csm::csmUint32 textureId);
/**
* @brief 画像の解放
*
* 指定した名前の画像を解放する
* @param[in] fileName 解放する画像ファイルパス名
**/
void ReleaseTexture(std::string fileName);
/**
* @brief テクスチャIDからテクスチャ情報を得る
*
* @param textureId[in] 取得したいテクスチャID
* @return テクスチャが存在していればTextureInfoが返る
*/
TextureInfo* GetTextureInfoById(GLuint textureId) const;
private:
Csm::csmVector<TextureInfo*> _textures;
};
+163
View File
@@ -0,0 +1,163 @@
/**
* Copyright(c) Live2D Inc. All rights reserved.
*
* Use of this source code is governed by the Live2D Open Software license
* that can be found at https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
*/
#pragma once
#include <GL/glew.h>
#include <GLFW/glfw3.h>
#include <Math/CubismMatrix44.hpp>
#include <Math/CubismViewMatrix.hpp>
#include "CubismFramework.hpp"
#include <Rendering/OpenGL/CubismOffscreenSurface_OpenGLES2.hpp>
class TouchManager;
class LAppSprite;
class LAppModel;
/**
* @brief 描画クラス
*/
class LAppView
{
public:
/**
* @brief LAppModelのレンダリング先
*/
enum SelectTarget
{
SelectTarget_None, ///< デフォルトのフレームバッファにレンダリング
SelectTarget_ModelFrameBuffer, ///< LAppModelが各自持つフレームバッファにレンダリング
SelectTarget_ViewFrameBuffer, ///< LAppViewの持つフレームバッファにレンダリング
};
/**
* @brief コンストラクタ
*/
LAppView();
/**
* @brief デストラクタ
*/
~LAppView();
/**
* @brief 初期化する。
*/
void Initialize();
/**
* @brief 描画する。
*/
void Render();
/**
* @brief 画像の初期化を行う。
*/
void InitializeSprite();
/**
* @brief スプライト系のサイズ再設定
*/
void ResizeSprite();
/**
* @brief タッチされたときに呼ばれる。
*
* @param[in] pointX スクリーンX座標
* @param[in] pointY スクリーンY座標
*/
void OnTouchesBegan(float pointX, float pointY) const;
/**
* @brief タッチしているときにポインタが動いたら呼ばれる。
*
* @param[in] pointX スクリーンX座標
* @param[in] pointY スクリーンY座標
*/
void OnTouchesMoved(float pointX, float pointY) const;
/**
* @brief タッチが終了したら呼ばれる。
*
* @param[in] pointX スクリーンX座標
* @param[in] pointY スクリーンY座標
*/
void OnTouchesEnded(float pointX, float pointY) const;
/**
* @brief X座標をView座標に変換する。
*
* @param[in] deviceX デバイスX座標
*/
float TransformViewX(float deviceX) const;
/**
* @brief Y座標をView座標に変換する。
*
* @param[in] deviceY デバイスY座標
*/
float TransformViewY(float deviceY) const;
/**
* @brief X座標をScreen座標に変換する。
*
* @param[in] deviceX デバイスX座標
*/
float TransformScreenX(float deviceX) const;
/**
* @brief Y座標をScreen座標に変換する。
*
* @param[in] deviceY デバイスY座標
*/
float TransformScreenY(float deviceY) const;
/**
* @brief モデル1体を描画する直前にコールされる
*/
void PreModelDraw(LAppModel& refModel);
/**
* @brief モデル1体を描画した直後にコールされる
*/
void PostModelDraw(LAppModel& refModel);
/**
* @brief 別レンダリングターゲットにモデルを描画するサンプルで
* 描画時のαを決定する
*/
float GetSpriteAlpha(int assign) const;
/**
* @brief レンダリング先を切り替える
*/
void SwitchRenderingTarget(SelectTarget targetType);
/**
* @brief レンダリング先をデフォルト以外に切り替えた際の背景クリア色設定
* @param[in] r 赤(0.0~1.0)
* @param[in] g 緑(0.0~1.0)
* @param[in] b 青(0.0~1.0)
*/
void SetRenderTargetClearColor(float r, float g, float b);
private:
TouchManager* _touchManager; ///< タッチマネージャー
Csm::CubismMatrix44* _deviceToScreen; ///< デバイスからスクリーンへの行列
Csm::CubismViewMatrix* _viewMatrix; ///< viewMatrix
GLuint _programId; ///< シェーダID
//LAppSprite* _back; ///< 背景画像
//LAppSprite* _gear; ///< ギア画像
//LAppSprite* _power; ///< 電源画像
// レンダリング先を別ターゲットにする方式の場合に使用
LAppSprite* _renderSprite; ///< モードによっては_renderBufferのテクスチャを描画
Csm::Rendering::CubismOffscreenSurface_OpenGLES2 _renderBuffer; ///< モードによってはCubismモデル結果をこっちにレンダリング
SelectTarget _renderTarget; ///< レンダリング先の選択肢
float _clearColor[4]; ///< レンダリングターゲットのクリアカラー
};
@@ -0,0 +1,234 @@
/**
* Copyright(c) Live2D Inc. All rights reserved.
*
* Use of this source code is governed by the Live2D Open Software license
* that can be found at https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
*/
#pragma once
#include <CubismFramework.hpp>
#include <Type/csmVector.hpp>
/**
* @brief wavファイルハンドラ
* @attention 16bit wav ファイル読み込みのみ実装済み
*/
class LAppWavFileHandler
{
public:
/**
* @brief 読み込んだwavfileの情報
*/
struct WavFileInfo
{
/**
* @brief コンストラクタ
*/
WavFileInfo() : _fileName(""), _numberOfChannels(0),
_bitsPerSample(0), _samplingRate(0), _samplesPerChannel(0),
_avgBytesPerSec(0), _blockAlign(0)
{ }
Csm::csmString _fileName; ///< ファイル名
Csm::csmUint32 _numberOfChannels; ///< チャンネル数
Csm::csmUint32 _bitsPerSample; ///< サンプルあたりビット数
Csm::csmUint32 _samplingRate; ///< サンプリングレート
Csm::csmUint32 _samplesPerChannel; ///< 1チャンネルあたり総サンプル数
Csm::csmUint32 _avgBytesPerSec; ///< 平均データ速度
Csm::csmUint32 _blockAlign; ///< ブロックサイズ
} _wavFileInfo;
/**
* @brief コンストラクタ
*/
LAppWavFileHandler();
/**
* @brief デストラクタ
*/
~LAppWavFileHandler();
/**
* @brief wavファイルハンドラの内部状態更新
*
* @param[in] deltaTimeSeconds デルタ時間[秒]
* @retval true 更新されている
* @retval false 更新されていない
*/
Csm::csmBool Update(Csm::csmFloat32 deltaTimeSeconds);
/**
* @brief 引数で指定したwavファイルの読み込みを開始する
*
* @param[in] filePath wavファイルのパス
*/
void Start(const Csm::csmString& filePath);
/**
* @brief 現在のRMS値取得
*
* @retval csmFloat32 RMS値
*/
Csm::csmFloat32 GetRms() const;
/**
* @brief ファイル情報を取得
*
* @retval ファイル情報
*/
const WavFileInfo& GetWavFileInfo() const;
/**
* @brief 正規化前のデータを取得
*
* @retval 正規化前のデータ
*/
const Csm::csmByte* GetRawData() const;
/**
* @brief 正規化前のデータの大きさを取得
*
* @retval 正規化前のデータの大きさ
*/
Csm::csmUint64 GetRawDataSize() const;
/**
* @brief 正規化データを取得する
*
* @retval 正規化データ
*/
Csm::csmVector<Csm::csmFloat32> GetPcmData() const;
/**
* @brief 引数で指定したチャンネルの正規化データを取得する
*
* @param[in] dst 格納先
* @param[in] useChannel 使用するチャンネル
*/
void GetPcmDataChannel(Csm::csmFloat32* dst, Csm::csmUint32 useChannel) const;
/**
* @brief -11の範囲の1サンプル取得
*
* @param[in] bitsPerSample ビット深度
* @param[in] data 正規化したいデータ
* @param[in] dataSize 正規化したいデータの大きさ
*
* @retval csmFloat32 正規化されたサンプル
*/
static Csm::csmFloat32 NormalizePcmSample(Csm::csmUint32 bitsPerSample, Csm::csmByte* data, Csm::csmUint32 dataSize);
private:
/**
* @brief wavファイルのロード
*
* @param[in] filePath wavファイルのパス
* @retval true 読み込み成功
* @retval false 読み込み失敗
*/
Csm::csmBool LoadWavFile(const Csm::csmString& filePath);
/**
* @brief PCMデータの解放
*/
void ReleasePcmData();
/**
* @brief -11の範囲の1サンプル取得
* @retval csmFloat32 正規化されたサンプル
*/
Csm::csmFloat32 GetPcmSample();
/**
* @brief バイトリーダ
*/
struct ByteReader {
/**
* @brief コンストラクタ
*/
ByteReader() : _fileByte(NULL), _fileSize(0), _readOffset(0)
{ }
/**
* @brief 8ビット読み込み
* @return Csm::csmUint8 読み取った8ビット値
*/
Csm::csmUint8 Get8()
{
const Csm::csmUint8 ret = _fileByte[_readOffset];
_readOffset++;
return ret;
}
/**
* @brief 16ビット読み込み(リトルエンディアン)
* @return Csm::csmUint16 読み取った16ビット値
*/
Csm::csmUint16 Get16LittleEndian()
{
const Csm::csmUint16 ret = (_fileByte[_readOffset + 1] << 8) | _fileByte[_readOffset];
_readOffset += 2;
return ret;
}
/**
* @brief 24ビット読み込み(リトルエンディアン)
* @return Csm::csmUint32 読み取った24ビット値(下位24ビットに設定)
*/
Csm::csmUint32 Get24LittleEndian()
{
const Csm::csmUint32 ret =
(_fileByte[_readOffset + 2] << 16) | (_fileByte[_readOffset + 1] << 8)
| _fileByte[_readOffset];
_readOffset += 3;
return ret;
}
/**
* @brief 32ビット読み込み(リトルエンディアン)
* @return Csm::csmUint32 読み取った32ビット値
*/
Csm::csmUint32 Get32LittleEndian()
{
const Csm::csmUint32 ret =
(_fileByte[_readOffset + 3] << 24) | (_fileByte[_readOffset + 2] << 16)
| (_fileByte[_readOffset + 1] << 8) | _fileByte[_readOffset];
_readOffset += 4;
return ret;
}
/**
* @brief シグネチャの取得と参照文字列との一致チェック
* @param[in] reference 検査対象のシグネチャ文字列
* @retval true 一致している
* @retval false 一致していない
*/
Csm::csmBool GetCheckSignature(const Csm::csmString& reference)
{
Csm::csmChar getSignature[4] = { 0, 0, 0, 0 };
const Csm::csmChar* referenceString = reference.GetRawString();
if (reference.GetLength() != 4)
{
return false;
}
for (Csm::csmUint32 signatureOffset = 0; signatureOffset < 4; signatureOffset++)
{
getSignature[signatureOffset] = static_cast<Csm::csmChar>(Get8());
}
return (getSignature[0] == referenceString[0]) && (getSignature[1] == referenceString[1])
&& (getSignature[2] == referenceString[2]) && (getSignature[3] == referenceString[3]);
}
Csm::csmByte* _fileByte; ///< ロードしたファイルのバイト列
Csm::csmSizeInt _fileSize; ///< ファイルサイズ
Csm::csmUint32 _readOffset; ///< ファイル参照位置
} _byteReader;
Csm::csmByte* _rawData; ///< 正規化される前のバイト列
Csm::csmUint64 _rawDataSize; ///< 正規化される前のバイト列の大きさ
Csm::csmFloat32** _pcmData; ///< -1から1の範囲で表現された音声データ配列
Csm::csmUint32 _sampleOffset; ///< サンプル参照位置
Csm::csmFloat32 _lastRms; ///< 最後に計測したRMS値
Csm::csmFloat32 _userTimeSeconds; ///< デルタ時間の積算値[秒]
};
+104
View File
@@ -0,0 +1,104 @@
/**
* Copyright(c) Live2D Inc. All rights reserved.
*
* Use of this source code is governed by the Live2D Open Software license
* that can be found at https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
*/
#pragma once
class TouchManager
{
public:
TouchManager();
float GetCenterX() const { return _lastX; }
float GetCenterY() const { return _lastY; }
float GetDeltaX() const { return _deltaX; }
float GetDeltaY() const{ return _deltaY; }
float GetStartX() const{ return _startX; }
float GetStartY() const{ return _startY; }
float GetScale() const { return _scale; }
float GetX() const{ return _lastX; }
float GetY() const{ return _lastY; }
float GetX1() const{ return _lastX1; }
float GetY1() const{ return _lastY1; }
float GetX2() const{ return _lastX2; }
float GetY2() const{ return _lastY2; }
bool IsSingleTouch() const { return _touchSingle; }
bool IsFlickAvailable() const { return _flipAvailable; }
void DisableFlick() { _flipAvailable = false; }
/*
* @brief タッチ開始時イベント
*
* @param[in] deviceY タッチした画面のyの値
* @param[in] deviceX タッチした画面のxの値
*/
void TouchesBegan(float deviceX, float deviceY);
/*
* @brief ドラッグ時のイベント
*
* @param[in] deviceX タッチした画面のyの値
* @param[in] deviceY タッチした画面のxの値
*/
void TouchesMoved(float deviceX, float deviceY);
/*
* @brief ドラッグ時のイベント
*
* @param[in] deviceX1 1つめのタッチした画面のxの値
* @param[in] deviceY1 1つめのタッチした画面のyの値
* @param[in] deviceX2 2つめのタッチした画面のxの値
* @param[in] deviceY2 2つめのタッチした画面のyの値
*/
void TouchesMoved(float deviceX1, float deviceY1, float deviceX2, float deviceY2);
/*
* @brief フリックの距離測定
*
* @return フリック距離
*/
float GetFlickDistance() const;
private:
/*
* @brief 点1から点2への距離を求める
*
* @param[in] x1 1つめのタッチした画面のxの値
* @param[in] y1 1つめのタッチした画面のyの値
* @param[in] x2 2つめのタッチした画面のxの値
* @param[in] y2 2つめのタッチした画面のyの値
* @return 2点の距離
*/
float CalculateDistance(float x1, float y1, float x2, float y2) const;
/*
* 二つの値から、移動量を求める。
* 違う方向の場合は移動量0。同じ方向の場合は、絶対値が小さい方の値を参照する
*
* @param[in] v1 1つめの移動量
* @param[in] v2 2つめの移動量
*
* @return 小さい方の移動量
*/
float CalculateMovingAmount(float v1, float v2);
float _startY; // タッチを開始した時のxの値
float _startX; // タッチを開始した時のyの値
float _lastX; // シングルタッチ時のxの値
float _lastY; // シングルタッチ時のyの値
float _lastX1; // ダブルタッチ時の一つ目のxの値
float _lastY1; // ダブルタッチ時の一つ目のyの値
float _lastX2; // ダブルタッチ時の二つ目のxの値
float _lastY2; // ダブルタッチ時の二つ目のyの値
float _lastTouchDistance; // 2本以上でタッチしたときの指の距離
float _deltaX; // 前回の値から今回の値へのxの移動距離。
float _deltaY; // 前回の値から今回の値へのyの移動距離。
float _scale; // このフレームで掛け合わせる拡大率。拡大操作中以外は1。
bool _touchSingle; // シングルタッチ時はtrue
bool _flipAvailable; // フリップが有効かどうか
};
+55
View File
@@ -0,0 +1,55 @@
/**
* Copyright(c) Live2D Inc. All rights reserved.
*
* Use of this source code is governed by the Live2D Open Software license
* that can be found at https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
*/
#include "LAppAllocator.hpp"
#include "stdlib.h"
using namespace Csm;
void* LAppAllocator::Allocate(const csmSizeType size)
{
return malloc(size);
}
void LAppAllocator::Deallocate(void* memory)
{
free(memory);
}
void* LAppAllocator::AllocateAligned(const csmSizeType size, const csmUint32 alignment)
{
size_t offset, shift, alignedAddress;
void* allocation;
void** preamble;
offset = alignment - 1 + sizeof(void*);
allocation = Allocate(size + static_cast<csmUint32>(offset));
alignedAddress = reinterpret_cast<size_t>(allocation) + sizeof(void*);
shift = alignedAddress % alignment;
if (shift)
{
alignedAddress += (alignment - shift);
}
preamble = reinterpret_cast<void**>(alignedAddress);
preamble[-1] = allocation;
return reinterpret_cast<void*>(alignedAddress);
}
void LAppAllocator::DeallocateAligned(void* alignedMemory)
{
void** preamble;
preamble = static_cast<void**>(alignedMemory);
Deallocate(preamble[-1]);
}
+68
View File
@@ -0,0 +1,68 @@
/**
* Copyright(c) Live2D Inc. All rights reserved.
*
* Use of this source code is governed by the Live2D Open Software license
* that can be found at https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
*/
#include "LAppDefine.hpp"
#include <CubismFramework.hpp>
namespace LAppDefine {
using namespace Csm;
// 画面
const csmFloat32 ViewScale = 1.0f;
const csmFloat32 ViewMaxScale = 2.0f;
const csmFloat32 ViewMinScale = 0.8f;
const csmFloat32 ViewLogicalLeft = -1.0f;
const csmFloat32 ViewLogicalRight = 1.0f;
const csmFloat32 ViewLogicalBottom = -1.0f;
const csmFloat32 ViewLogicalTop = -1.0f;
const csmFloat32 ViewLogicalMaxLeft = -2.0f;
const csmFloat32 ViewLogicalMaxRight = 2.0f;
const csmFloat32 ViewLogicalMaxBottom = -2.0f;
const csmFloat32 ViewLogicalMaxTop = 2.0f;
// 相対パス 弃用相对路径,采用绝对路径或相对路径
const csmChar* ResourcesPath = "Resources/";
// // モデルの後ろにある背景の画像ファイル 弃用
// const csmChar* BackImageName = "back_class_normal.png";
// // 歯車 弃用
// const csmChar* GearImageName = "icon_gear.png";
// // 終了ボタン 通通弃用
// const csmChar* PowerImageName = "close.png";
// モデル定義------------------------------------------
// 外部定義ファイル(json)と合わせる
const csmChar* MotionGroupIdle = "Idle"; // アイドリング
const csmChar* MotionGroupTapBody = "TapBody"; // 体をタップしたとき
// 外部定義ファイル(json)と合わせる
const csmChar* HitAreaNameHead = "Head";
const csmChar* HitAreaNameBody = "Body";
// モーションの優先度定数
const csmInt32 PriorityNone = 0;
const csmInt32 PriorityIdle = 1;
const csmInt32 PriorityNormal = 2;
const csmInt32 PriorityForce = 3;
// MOC3の整合性検証オプション
const csmBool MocConsistencyValidationEnable = true;
// デバッグ用ログの表示オプション
const csmBool DebugLogEnable = true;
const csmBool DebugTouchLogEnable = false;
// Frameworkから出力するログのレベル設定
const CubismFramework::Option::LogLevel CubismLoggingLevel = CubismFramework::Option::LogLevel_Verbose;
// デフォルトのレンダーターゲットサイズ
const csmInt32 RenderTargetWidth = 1900;
const csmInt32 RenderTargetHeight = 1000;
}
+374
View File
@@ -0,0 +1,374 @@
/**
* Copyright(c) Live2D Inc. All rights reserved.
*
* Use of this source code is governed by the Live2D Open Software license
* that can be found at https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
*/
#include "LAppDelegate.hpp"
#include <iostream>
#include <GL/glew.h>
#include <GLFW/glfw3.h>
#include "LAppView.hpp"
#include "LAppPal.hpp"
#include "LAppDefine.hpp"
#include "LAppLive2DManager.hpp"
#include "LAppTextureManager.hpp"
using namespace Csm;
using namespace std;
using namespace LAppDefine;
namespace {
LAppDelegate* s_instance = NULL;
}
LAppDelegate* LAppDelegate::GetInstance()
{
if (s_instance == NULL)
{
s_instance = new LAppDelegate();
}
return s_instance;
}
void LAppDelegate::ReleaseInstance()
{
if (s_instance != NULL)
{
delete s_instance;
}
s_instance = NULL;
}
bool LAppDelegate::Initialize(GLCore* window)
{
if (DebugLogEnable)
{
LAppPal::PrintLogLn("START");
}
// GLFWの初期化
if (glfwInit() == GL_FALSE)
{
if (DebugLogEnable)
{
LAppPal::PrintLogLn("Can't initilize GLFW");
}
return GL_FALSE;
}
// Windowの生成_
//_window = glfwCreateWindow(RenderTargetWidth, RenderTargetHeight, "SAMPLE", NULL, NULL);
_window = window; // Misaki 修改
if (_window == NULL)
{
if (DebugLogEnable)
{
LAppPal::PrintLogLn("Can't create GLFW window.");
}
glfwTerminate();
return GL_FALSE;
}
// Windowのコンテキストをカレントに設定
//glfwMakeContextCurrent(_window);
_window->makeCurrent(); // Misaki 修改
glfwSwapInterval(1);
if (glewInit() != GLEW_OK) {
if (DebugLogEnable)
{
LAppPal::PrintLogLn("Can't initilize glew.");
}
glfwTerminate();
return GL_FALSE;
}
//テクスチャサンプリング設定
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
//透過設定
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
// Misaki 修改
//コールバック関数の登録
//glfwSetMouseButtonCallback(_window, EventHandler::OnMouseCallBack);
//glfwSetCursorPosCallback(_window, EventHandler::OnMouseCallBack);
// ウィンドウサイズ記憶
//int width, height;
//glfwGetWindowSize(LAppDelegate::GetInstance()->GetWindow(), &width, &height);
_windowWidth = _window->width(); // Misaki 修改
_windowHeight = _window->height();
//AppViewの初期化
_view->Initialize();
// Cubism SDK の初期化
InitializeCubism();
return GL_TRUE;
}
void LAppDelegate::Release()
{
// Windowの削除
//glfwDestroyWindow(_window); // Misaki 修改
glfwTerminate();
delete _textureManager;
delete _view;
// リソースを解放
LAppLive2DManager::ReleaseInstance();
//Cubism SDK の解放
CubismFramework::Dispose();
}
/* Msaki 修改
void LAppDelegate::Run()
{
//メインループ
while (glfwWindowShouldClose(_window) == GL_FALSE && !_isEnd)
{
int width, height;
glfwGetWindowSize(LAppDelegate::GetInstance()->GetWindow(), &width, &height);
if( (_windowWidth!=width || _windowHeight!=height) && width>0 && height>0)
{
//AppViewの初期化
_view->Initialize();
// スプライトサイズを再設定
_view->ResizeSprite();
// サイズを保存しておく
_windowWidth = width;
_windowHeight = height;
// ビューポート変更
glViewport(0, 0, width, height);
}
// 時間更新
LAppPal::UpdateTime();
// 画面の初期化
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glClearDepth(1.0);
//描画更新
_view->Render();
// バッファの入れ替え
glfwSwapBuffers(_window);
// Poll for and process events
glfwPollEvents();
}
Release();
LAppDelegate::ReleaseInstance();
}
*/
// 分散run实现的功能
void LAppDelegate::resize(int width, int height)
{
if ((_windowWidth != width || _windowHeight != height) && width > 0 && height > 0)
{
//AppViewの初期化
_view->Initialize();
// スプライトサイズを再設定
_view->ResizeSprite();
// サイズを保存しておく
_windowWidth = width;
_windowHeight = height;
// ビューポート変更
glViewport(0, 0, width, height);
}
else
{
glViewport(0, 0, width, height);
}
}
void LAppDelegate::update()
{
// 時間更新
LAppPal::UpdateTime();
// 画面の初期化
//glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glClearColor(0.0f, 0.0f, 0.0f, 0.0f); // 渲染背景为透明
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glClearDepth(1.0);
//描画更新
_view->Render();
}
LAppDelegate::LAppDelegate():
_cubismOption(),
_window(NULL),
_captured(false),
_mouseX(0.0f),
_mouseY(0.0f),
_isEnd(false),
_windowWidth(0),
_windowHeight(0)
{
_view = new LAppView();
_textureManager = new LAppTextureManager();
}
LAppDelegate::~LAppDelegate()
{
}
void LAppDelegate::InitializeCubism()
{
//setup cubism
_cubismOption.LogFunction = LAppPal::PrintMessage;
_cubismOption.LoggingLevel = LAppDefine::CubismLoggingLevel;
_cubismOption.LoadFileFunction = LAppPal::LoadFileAsBytes;
_cubismOption.ReleaseBytesFunction = LAppPal::ReleaseBytes;
Csm::CubismFramework::StartUp(&_cubismAllocator, &_cubismOption);
//Initialize cubism
CubismFramework::Initialize();
//load model
LAppLive2DManager::GetInstance();
//default proj
CubismMatrix44 projection;
LAppPal::UpdateTime();
_view->InitializeSprite();
}
void LAppDelegate::OnMouseCallBack(GLFWwindow* window, int button, int action, int modify)
{
if (_view == NULL)
{
return;
}
if (GLFW_MOUSE_BUTTON_LEFT != button)
{
return;
}
if (GLFW_PRESS == action)
{
_captured = true;
_view->OnTouchesBegan(_mouseX, _mouseY);
}
else if (GLFW_RELEASE == action)
{
if (_captured)
{
_captured = false;
_view->OnTouchesEnded(_mouseX, _mouseY);
}
}
}
void LAppDelegate::OnMouseCallBack(GLFWwindow* window, double x, double y)
{
_mouseX = static_cast<float>(x);
_mouseY = static_cast<float>(y);
if (!_captured)
{
return;
}
if (_view == NULL)
{
return;
}
_view->OnTouchesMoved(_mouseX, _mouseY);
}
GLuint LAppDelegate::CreateShader()
{
//バーテックスシェーダのコンパイル
GLuint vertexShaderId = glCreateShader(GL_VERTEX_SHADER);
const char* vertexShader =
"#version 120\n"
"attribute vec3 position;"
"attribute vec2 uv;"
"varying vec2 vuv;"
"void main(void){"
" gl_Position = vec4(position, 1.0);"
" vuv = uv;"
"}";
glShaderSource(vertexShaderId, 1, &vertexShader, NULL);
glCompileShader(vertexShaderId);
if(!CheckShader(vertexShaderId))
{
return 0;
}
//フラグメントシェーダのコンパイル
GLuint fragmentShaderId = glCreateShader(GL_FRAGMENT_SHADER);
const char* fragmentShader =
"#version 120\n"
"varying vec2 vuv;"
"uniform sampler2D texture;"
"uniform vec4 baseColor;"
"void main(void){"
" gl_FragColor = texture2D(texture, vuv) * baseColor;"
"}";
glShaderSource(fragmentShaderId, 1, &fragmentShader, NULL);
glCompileShader(fragmentShaderId);
if (!CheckShader(fragmentShaderId))
{
return 0;
}
//プログラムオブジェクトの作成
GLuint programId = glCreateProgram();
glAttachShader(programId, vertexShaderId);
glAttachShader(programId, fragmentShaderId);
// リンク
glLinkProgram(programId);
glUseProgram(programId);
return programId;
}
bool LAppDelegate::CheckShader(GLuint shaderId)
{
GLint status;
GLint logLength;
glGetShaderiv(shaderId, GL_INFO_LOG_LENGTH, &logLength);
if (logLength > 0)
{
GLchar* log = reinterpret_cast<GLchar*>(CSM_MALLOC(logLength));
glGetShaderInfoLog(shaderId, logLength, &logLength, log);
CubismLogError("Shader compile log: %s", log);
CSM_FREE(log);
}
glGetShaderiv(shaderId, GL_COMPILE_STATUS, &status);
if (status == GL_FALSE)
{
glDeleteShader(shaderId);
return false;
}
return true;
}
+429
View File
@@ -0,0 +1,429 @@
/**
* Copyright(c) Live2D Inc. All rights reserved.
*
* Use of this source code is governed by the Live2D Open Software license
* that can be found at https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
*/
#include "LAppLive2DManager.hpp"
#include <stdio.h>
#include <stdlib.h>
#if defined(_WIN32)
#include <io.h> // 保持 Windows 下继续用旧实现
#include <direct.h>
#else
#include <filesystem> // Linux / macOS 用 std::filesystem
#include <algorithm>
#endif
#include <GL/glew.h>
#include <GLFW/glfw3.h>
#include <Rendering/CubismRenderer.hpp>
#include "LAppPal.hpp"
#include "LAppDefine.hpp"
#include "LAppDelegate.hpp"
#include "LAppModel.hpp"
#include "LAppView.hpp"
using namespace Csm;
using namespace LAppDefine;
using namespace std;
namespace {
LAppLive2DManager* s_instance = NULL;
void FinishedMotion(ACubismMotion* self)
{
LAppPal::PrintLogLn("Motion Finished: %x", self);
}
int CompareCsmString(const void* a, const void* b)
{
return strcmp(reinterpret_cast<const Csm::csmString*>(a)->GetRawString(),
reinterpret_cast<const Csm::csmString*>(b)->GetRawString());
}
}
LAppLive2DManager* LAppLive2DManager::GetInstance()
{
if (s_instance == NULL)
{
s_instance = new LAppLive2DManager();
}
return s_instance;
}
void LAppLive2DManager::ReleaseInstance()
{
if (s_instance != NULL)
{
delete s_instance;
}
s_instance = NULL;
}
LAppLive2DManager::LAppLive2DManager()
: _viewMatrix(NULL)
, _sceneIndex(0)
{
_viewMatrix = new CubismMatrix44();
//SetUpModel();
// Resources/Haru/ Haru.model3.json
LoadModelFromPath("Resources/Live2DModels/KITU17/", "KITU17.model3.json");
//ChangeScene(_sceneIndex);
}
LAppLive2DManager::~LAppLive2DManager()
{
ReleaseAllModel();
delete _viewMatrix;
}
void LAppLive2DManager::ReleaseAllModel()
{
for (csmUint32 i = 0; i < _models.GetSize(); i++)
{
delete _models[i];
}
_models.Clear();
}
void LAppLive2DManager::SetUpModel()
{
// ResourcesPathの中にあるフォルダ名を全てクロールし、モデルが存在するフォルダを定義する。
// フォルダはあるが同名の.model3.jsonが見つからなかった場合はリストに含めない。
// 遍历ResourcesPath中的所有文件夹名称,定义包含模型的文件夹。
// 如果文件夹存在但未找到同名的.model3.json文件,则不将其包含在列表中。
_modelDir.Clear();
#if defined(_WIN32) // Windows 旧代码
csmString crawlPath(ResourcesPath);
crawlPath += "*.*";
struct _finddata_t fdata;
intptr_t fh = _findfirst(crawlPath.GetRawString(), &fdata);
if (fh == -1) return;
while (_findnext(fh, &fdata) == 0)
{
if ((fdata.attrib & _A_SUBDIR) && strcmp(fdata.name, "..") != 0)
{
csmString modelJson(ResourcesPath);
modelJson += fdata.name;
modelJson.Append(1, '/');
modelJson += fdata.name;
modelJson += ".model3.json";
struct _finddata_t fdata2;
if (_findfirst(modelJson.GetRawString(), &fdata2) != -1)
_modelDir.PushBack(csmString(fdata.name));
}
}
qsort(_modelDir.GetPtr(), _modelDir.GetSize(), sizeof(csmString), CompareCsmString);
#else // Linux / macOS 代码
namespace fs = std::filesystem;
const fs::path resDir(ResourcesPath);
if (!fs::exists(resDir) || !fs::is_directory(resDir))
return;
for (const auto& entry : fs::directory_iterator(resDir))
{
if (!entry.is_directory()) continue;
const std::string& dirName = entry.path().filename().string();
if (dirName == "." || dirName == "..") continue;
fs::path modelJson = entry.path() / (dirName + ".model3.json");
if (fs::exists(modelJson))
_modelDir.PushBack(csmString(dirName.c_str()));
}
/* 保持与原代码相同的排序 */
qsort(_modelDir.GetPtr(), _modelDir.GetSize(), sizeof(csmString), CompareCsmString);
#endif
}
csmVector<csmString> LAppLive2DManager::GetModelDir() const
{
return _modelDir;
}
csmInt32 LAppLive2DManager::GetModelDirSize() const
{
return _modelDir.GetSize();
}
LAppModel* LAppLive2DManager::GetModel(csmUint32 no) const
{
if (no < _models.GetSize())
{
return _models[no];
}
return NULL;
}
/**
* 处理鼠标拖拽事件
* @param x
* @param y
*/
void LAppLive2DManager::OnDrag(csmFloat32 x, csmFloat32 y) const
{
for (csmUint32 i = 0; i < _models.GetSize(); i++)
{
LAppModel* model = GetModel(i);
model->SetDragging(x, y);
}
}
/**
* 处理鼠标点击事件
* @param x
* @param y
* @return
*/
void LAppLive2DManager::OnTap(csmFloat32 x, csmFloat32 y)
{
if (DebugLogEnable)
{
LAppPal::PrintLogLn("[APP]tap point: {x:%.2f y:%.2f}", x, y);
}
for (csmUint32 i = 0; i < _models.GetSize(); i++)
{
if (_models[i]->HitTest(HitAreaNameHead, x, y))
{
if (DebugLogEnable)
{
LAppPal::PrintLogLn("[APP]hit area: [%s]", HitAreaNameHead);
}
_models[i]->SetRandomExpression();
}
else if (_models[i]->HitTest(HitAreaNameBody, x, y))
{
if (DebugLogEnable)
{
LAppPal::PrintLogLn("[APP]hit area: [%s]", HitAreaNameBody);
}
_models[i]->StartRandomMotion(MotionGroupTapBody, PriorityNormal, FinishedMotion); // TODO
}
}
}
void LAppLive2DManager::OnUpdate() const
{
int width, height;
//glfwGetWindowSize(LAppDelegate::GetInstance()->GetWindow(), &width, &height);
width = LAppDelegate::GetInstance()->GetWindow()->width();
height = LAppDelegate::GetInstance()->GetWindow()->height();
csmUint32 modelCount = _models.GetSize();
for (csmUint32 i = 0; i < modelCount; ++i)
{
CubismMatrix44 projection;
LAppModel* model = GetModel(i);
if (model->GetModel() == NULL)
{
LAppPal::PrintLogLn("Failed to model->GetModel().");
continue;
}
if (model->GetModel()->GetCanvasWidth() > 1.0f && width < height)
{
// 横に長いモデルを縦長ウィンドウに表示する際モデルの横サイズでscaleを算出する
model->GetModelMatrix()->SetWidth(2.0f);
projection.Scale(1.0f, static_cast<float>(width) / static_cast<float>(height));
}
else
{
projection.Scale(static_cast<float>(height) / static_cast<float>(width), 1.0f);
}
// 必要があればここで乗算
if (_viewMatrix != NULL)
{
projection.MultiplyByMatrix(_viewMatrix);
}
// モデル1体描画前コール
LAppDelegate::GetInstance()->GetView()->PreModelDraw(*model);
model->Update();
model->Draw(projection);///< 参照渡しなのでprojectionは変質する
// モデル1体描画後コール
LAppDelegate::GetInstance()->GetView()->PostModelDraw(*model);
}
}
void LAppLive2DManager::LoadModelFromPath(const std::string& modelPath, const std::string& fileName)
{
csmString modelPathStr(modelPath.c_str());
csmString modelJsonName(fileName.c_str());
ReleaseAllModel(); // 释放当前所有模型 有一个点要注意,在这里先释放然后再Push模型实例,以及加载模型实例
_models.PushBack(new LAppModel()); // 这样在加载的时候都使用的models[0]这一个位置,自行实现模型选择器要注意注意
_models[0]->LoadAssets(modelPathStr.GetRawString(), modelJsonName.GetRawString());
/*
* 提供一个半透明表示模型的示例。
* 如果定义了USE_RENDER_TARGET或USE_MODEL_RENDER_TARGET
* 则将模型绘制到另一个渲染目标上,并将绘制结果作为纹理应用到另一个精灵上。
*/
{
#if defined(USE_RENDER_TARGET)
// 如果选择将绘制操作在LAppView持有的目标上进行,则选择此选项
LAppView::SelectTarget useRenderTarget = LAppView::SelectTarget_ViewFrameBuffer;
#elif defined(USE_MODEL_RENDER_TARGET)
// 如果选择将绘制操作在各个LAppModel持有的目标上进行,则选择此选项
LAppView::SelectTarget useRenderTarget = LAppView::SelectTarget_ModelFrameBuffer;
#else
// 默认情况下,渲染到主帧缓冲区(通常是直接渲染到屏幕)
LAppView::SelectTarget useRenderTarget = LAppView::SelectTarget_None;
#endif
#if defined(USE_RENDER_TARGET) || defined(USE_MODEL_RENDER_TARGET)
// 作为给模型单独设置α(透明度)的示例,创建另一个模型实例并稍微移动其位置
_models.PushBack(new LAppModel());
_models[1]->LoadAssets(modelPath.GetRawString(), modelJsonName.GetRawString());
_models[1]->GetModelMatrix()->TranslateX(0.2f);
#endif
LAppDelegate::GetInstance()->GetView()->SwitchRenderingTarget(useRenderTarget);
// 当选择其他渲染目标时的背景清除颜色
float clearColor[3] = { 1.0f, 1.0f, 1.0f };
LAppDelegate::GetInstance()->GetView()->SetRenderTargetClearColor(clearColor[0], clearColor[1], clearColor[2]);
}
}
void LAppLive2DManager::NextScene()
{
csmInt32 no = (_sceneIndex + 1) % GetModelDirSize();
ChangeScene(no);
}
void LAppLive2DManager::ChangeScene(Csm::csmInt32 index)
{
_sceneIndex = index;
if (DebugLogEnable)
{
LAppPal::PrintLogLn("[APP]model index: %d", _sceneIndex);
}
// model3.jsonのパスを決定する.
// ディレクトリ名とmodel3.jsonの名前を一致していることが条件
const csmString& model = _modelDir[index];
csmString modelPath(ResourcesPath);
modelPath += model;
modelPath.Append(1, '/');
csmString modelJsonName(model);
modelJsonName += ".model3.json";
ReleaseAllModel();
_models.PushBack(new LAppModel());
_models[0]->LoadAssets(modelPath.GetRawString(), modelJsonName.GetRawString());
/*
* 提供一个半透明表示模型的示例。
* 如果定义了USE_RENDER_TARGET或USE_MODEL_RENDER_TARGET
* 则将模型绘制到另一个渲染目标上,并将绘制结果作为纹理应用到另一个精灵上。
*/
{
#if defined(USE_RENDER_TARGET)
// 如果选择将绘制操作在LAppView持有的目标上进行,则选择此选项
LAppView::SelectTarget useRenderTarget = LAppView::SelectTarget_ViewFrameBuffer;
#elif defined(USE_MODEL_RENDER_TARGET)
// 如果选择将绘制操作在各个LAppModel持有的目标上进行,则选择此选项
LAppView::SelectTarget useRenderTarget = LAppView::SelectTarget_ModelFrameBuffer;
#else
// 默认情况下,渲染到主帧缓冲区(通常是直接渲染到屏幕)
LAppView::SelectTarget useRenderTarget = LAppView::SelectTarget_None;
#endif
#if defined(USE_RENDER_TARGET) || defined(USE_MODEL_RENDER_TARGET)
// 作为给模型单独设置α(透明度)的示例,创建另一个模型实例并稍微移动其位置
_models.PushBack(new LAppModel());
_models[1]->LoadAssets(modelPath.GetRawString(), modelJsonName.GetRawString());
_models[1]->GetModelMatrix()->TranslateX(0.2f);
#endif
LAppDelegate::GetInstance()->GetView()->SwitchRenderingTarget(useRenderTarget);
// 当选择其他渲染目标时的背景清除颜色
float clearColor[3] = { 1.0f, 1.0f, 1.0f };
LAppDelegate::GetInstance()->GetView()->SetRenderTargetClearColor(clearColor[0], clearColor[1], clearColor[2]);
}
}
/**
* @brief 启动当前活动模型的唇形同步并播放指定的 WAV 文件
* @param wavFilePath WAV 文件的路径
*/
void LAppLive2DManager::StartLipSync(const Csm::csmString& wavFilePath)
{
if (_models.GetSize() == 0)
{
LAppPal::PrintLogLn("[APP] Error: No model is loaded.");
return;
}
// TODO Mark
// 获取当前活动模型(只有一个模型的情况)
LAppModel* model = _models[0];
if (model == nullptr)
{
LAppPal::PrintLogLn("[APP] Error: Current model is null.");
return;
}
// 调用模型的 StartLipSync 方法
model->StartLipSync(wavFilePath);
}
/**
* @brief 启动指定模型的唇形同步并播放指定的 WAV 文件
* @param modelIndex 模型索引
* @param wavFilePath WAV 文件的路径
*/
void LAppLive2DManager::StartLipSync(csmUint32 modelIndex, const Csm::csmString& wavFilePath)
{
if (modelIndex >= _models.GetSize())
{
LAppPal::PrintLogLn("[APP] Error: Model index out of range.");
return;
}
// 获取指定模型
LAppModel* model = _models[modelIndex];
if (model == nullptr)
{
LAppPal::PrintLogLn("[APP] Error: Model is null.");
return;
}
// 调用模型的 StartLipSync 方法
model->StartLipSync(wavFilePath);
}
csmUint32 LAppLive2DManager::GetModelNum() const
{
return _models.GetSize();
}
void LAppLive2DManager::SetViewMatrix(CubismMatrix44* m)
{
for (int i = 0; i < 16; i++) {
_viewMatrix->GetArray()[i] = m->GetArray()[i];
}
}
+775
View File
@@ -0,0 +1,775 @@
/**
* Copyright(c) Live2D Inc. All rights reserved.
*
* Use of this source code is governed by the Live2D Open Software license
* that can be found at https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
*/
#include "LAppModel.hpp"
#include <fstream>
#include <vector>
#include <CubismModelSettingJson.hpp>
#include <Motion/CubismMotion.hpp>
#include <Physics/CubismPhysics.hpp>
#include <CubismDefaultParameterId.hpp>
#include <Rendering/OpenGL/CubismRenderer_OpenGLES2.hpp>
#include <Utils/CubismString.hpp>
#include <Id/CubismIdManager.hpp>
#include <Motion/CubismMotionQueueEntry.hpp>
#include "LAppDefine.hpp"
#include "LAppPal.hpp"
#include "LAppTextureManager.hpp"
#include "LAppDelegate.hpp"
using namespace Live2D::Cubism::Framework;
using namespace Live2D::Cubism::Framework::DefaultParameterId;
using namespace LAppDefine;
namespace {
csmByte* CreateBuffer(const csmChar* path, csmSizeInt* size)
{
if (DebugLogEnable)
{
LAppPal::PrintLogLn("[APP]create buffer: %s ", path);
}
return LAppPal::LoadFileAsBytes(path, size);
}
void DeleteBuffer(csmByte* buffer, const csmChar* path = "")
{
if (DebugLogEnable)
{
LAppPal::PrintLogLn("[APP]delete buffer: %s", path);
}
LAppPal::ReleaseBytes(buffer);
}
}
LAppModel::LAppModel()
: CubismUserModel()
, _modelSetting(NULL)
, _userTimeSeconds(0.0f)
{
if (MocConsistencyValidationEnable)
{
_mocConsistency = true;
}
if (DebugLogEnable)
{
_debugMode = true;
}
_idParamAngleX = CubismFramework::GetIdManager()->GetId(ParamAngleX);
_idParamAngleY = CubismFramework::GetIdManager()->GetId(ParamAngleY);
_idParamAngleZ = CubismFramework::GetIdManager()->GetId(ParamAngleZ);
_idParamBodyAngleX = CubismFramework::GetIdManager()->GetId(ParamBodyAngleX);
_idParamEyeBallX = CubismFramework::GetIdManager()->GetId(ParamEyeBallX);
_idParamEyeBallY = CubismFramework::GetIdManager()->GetId(ParamEyeBallY);
}
LAppModel::~LAppModel()
{
_renderBuffer.DestroyOffscreenSurface();
ReleaseMotions();
ReleaseExpressions();
for (csmInt32 i = 0; i < _modelSetting->GetMotionGroupCount(); i++)
{
const csmChar* group = _modelSetting->GetMotionGroupName(i);
ReleaseMotionGroup(group);
}
delete(_modelSetting);
}
void LAppModel::LoadAssets(const csmChar* dir, const csmChar* fileName)
{
_modelHomeDir = dir;
if (_debugMode)
{
LAppPal::PrintLogLn("[APP]load model setting: %s", fileName);
}
csmSizeInt size;
const csmString path = csmString(dir) + fileName;
csmByte* buffer = CreateBuffer(path.GetRawString(), &size);
ICubismModelSetting* setting = new CubismModelSettingJson(buffer, size);
DeleteBuffer(buffer, path.GetRawString());
SetupModel(setting);
if (_model == NULL)
{
LAppPal::PrintLogLn("Failed to LoadAssets().");
return;
}
CreateRenderer();
SetupTextures();
}
void LAppModel::SetupModel(ICubismModelSetting* setting)
{
_updating = true;
_initialized = false;
_modelSetting = setting;
csmByte* buffer;
csmSizeInt size;
//Cubism Model
if (strcmp(_modelSetting->GetModelFileName(), "") != 0)
{
csmString path = _modelSetting->GetModelFileName();
path = _modelHomeDir + path;
if (_debugMode)
{
LAppPal::PrintLogLn("[APP]create model: %s", setting->GetModelFileName());
}
buffer = CreateBuffer(path.GetRawString(), &size);
LoadModel(buffer, size, _mocConsistency);
DeleteBuffer(buffer, path.GetRawString());
}
//Expression
if (_modelSetting->GetExpressionCount() > 0)
{
const csmInt32 count = _modelSetting->GetExpressionCount();
for (csmInt32 i = 0; i < count; i++)
{
csmString name = _modelSetting->GetExpressionName(i);
csmString path = _modelSetting->GetExpressionFileName(i);
path = _modelHomeDir + path;
buffer = CreateBuffer(path.GetRawString(), &size);
ACubismMotion* motion = LoadExpression(buffer, size, name.GetRawString());
if (motion)
{
if (_expressions[name] != NULL)
{
ACubismMotion::Delete(_expressions[name]);
_expressions[name] = NULL;
}
_expressions[name] = motion;
}
DeleteBuffer(buffer, path.GetRawString());
}
}
//Physics
if (strcmp(_modelSetting->GetPhysicsFileName(), "") != 0)
{
csmString path = _modelSetting->GetPhysicsFileName();
path = _modelHomeDir + path;
buffer = CreateBuffer(path.GetRawString(), &size);
LoadPhysics(buffer, size);
DeleteBuffer(buffer, path.GetRawString());
}
//Pose
if (strcmp(_modelSetting->GetPoseFileName(), "") != 0)
{
csmString path = _modelSetting->GetPoseFileName();
path = _modelHomeDir + path;
buffer = CreateBuffer(path.GetRawString(), &size);
LoadPose(buffer, size);
DeleteBuffer(buffer, path.GetRawString());
}
//EyeBlink
if (_modelSetting->GetEyeBlinkParameterCount() > 0)
{
_eyeBlink = CubismEyeBlink::Create(_modelSetting);
}
//Breath
{
_breath = CubismBreath::Create();
csmVector<CubismBreath::BreathParameterData> breathParameters;
breathParameters.PushBack(CubismBreath::BreathParameterData(_idParamAngleX, 0.0f, 15.0f, 6.5345f, 0.5f));
breathParameters.PushBack(CubismBreath::BreathParameterData(_idParamAngleY, 0.0f, 8.0f, 3.5345f, 0.5f));
breathParameters.PushBack(CubismBreath::BreathParameterData(_idParamAngleZ, 0.0f, 10.0f, 5.5345f, 0.5f));
breathParameters.PushBack(CubismBreath::BreathParameterData(_idParamBodyAngleX, 0.0f, 4.0f, 15.5345f, 0.5f));
breathParameters.PushBack(CubismBreath::BreathParameterData(CubismFramework::GetIdManager()->GetId(ParamBreath), 0.5f, 0.5f, 3.2345f, 0.5f));
_breath->SetParameters(breathParameters);
}
//UserData
if (strcmp(_modelSetting->GetUserDataFile(), "") != 0)
{
csmString path = _modelSetting->GetUserDataFile();
path = _modelHomeDir + path;
buffer = CreateBuffer(path.GetRawString(), &size);
LoadUserData(buffer, size);
DeleteBuffer(buffer, path.GetRawString());
}
// EyeBlinkIds
{
csmInt32 eyeBlinkIdCount = _modelSetting->GetEyeBlinkParameterCount();
for (csmInt32 i = 0; i < eyeBlinkIdCount; ++i)
{
_eyeBlinkIds.PushBack(_modelSetting->GetEyeBlinkParameterId(i));
}
}
// LipSyncIds
{
csmInt32 lipSyncIdCount = _modelSetting->GetLipSyncParameterCount();
if(_debugMode)
{
LAppPal::PrintLogLn("[APP]LipSyncIdCount: %d", lipSyncIdCount);
}
for (csmInt32 i = 0; i < lipSyncIdCount; ++i)
{
_lipSyncIds.PushBack(_modelSetting->GetLipSyncParameterId(i));
}
}
if (_modelSetting == NULL || _modelMatrix == NULL)
{
LAppPal::PrintLogLn("Failed to SetupModel().");
return;
}
//Layout
csmMap<csmString, csmFloat32> layout;
_modelSetting->GetLayoutMap(layout);
_modelMatrix->SetupFromLayout(layout);
_model->SaveParameters();
for (csmInt32 i = 0; i < _modelSetting->GetMotionGroupCount(); i++)
{
const csmChar* group = _modelSetting->GetMotionGroupName(i);
PreloadMotionGroup(group);
}
_motionManager->StopAllMotions();
_updating = false;
_initialized = true;
}
void LAppModel::PreloadMotionGroup(const csmChar* group)
{
const csmInt32 count = _modelSetting->GetMotionCount(group);
for (csmInt32 i = 0; i < count; i++)
{
//ex) idle_0
csmString name = Utils::CubismString::GetFormatedString("%s_%d", group, i);
csmString path = _modelSetting->GetMotionFileName(group, i);
path = _modelHomeDir + path;
if (_debugMode)
{
LAppPal::PrintLogLn("[APP]load motion: %s => [%s_%d] ", path.GetRawString(), group, i);
}
csmByte* buffer;
csmSizeInt size;
buffer = CreateBuffer(path.GetRawString(), &size);
CubismMotion* tmpMotion = static_cast<CubismMotion*>(LoadMotion(buffer, size, name.GetRawString()));
if (tmpMotion)
{
csmFloat32 fadeTime = _modelSetting->GetMotionFadeInTimeValue(group, i);
if (fadeTime >= 0.0f)
{
tmpMotion->SetFadeInTime(fadeTime);
}
fadeTime = _modelSetting->GetMotionFadeOutTimeValue(group, i);
if (fadeTime >= 0.0f)
{
tmpMotion->SetFadeOutTime(fadeTime);
}
tmpMotion->SetEffectIds(_eyeBlinkIds, _lipSyncIds);
if (_motions[name] != NULL)
{
ACubismMotion::Delete(_motions[name]);
}
_motions[name] = tmpMotion;
}
DeleteBuffer(buffer, path.GetRawString());
}
}
void LAppModel::ReleaseMotionGroup(const csmChar* group) const
{
const csmInt32 count = _modelSetting->GetMotionCount(group);
for (csmInt32 i = 0; i < count; i++)
{
csmString voice = _modelSetting->GetMotionSoundFileName(group, i);
if (strcmp(voice.GetRawString(), "") != 0)
{
csmString path = voice;
path = _modelHomeDir + path;
}
}
}
/**
* @brief すべてのモーションデータの解放
*
* すべてのモーションデータを解放する。
*/
void LAppModel::ReleaseMotions()
{
for (csmMap<csmString, ACubismMotion*>::const_iterator iter = _motions.Begin(); iter != _motions.End(); ++iter)
{
ACubismMotion::Delete(iter->Second);
}
_motions.Clear();
}
/**
* @brief すべての表情データの解放
*
* すべての表情データを解放する。
*/
void LAppModel::ReleaseExpressions()
{
for (csmMap<csmString, ACubismMotion*>::const_iterator iter = _expressions.Begin(); iter != _expressions.End(); ++iter)
{
ACubismMotion::Delete(iter->Second);
}
_expressions.Clear();
}
void LAppModel::Update()
{
const csmFloat32 deltaTimeSeconds = LAppPal::GetDeltaTime();
_userTimeSeconds += deltaTimeSeconds;
_dragManager->Update(deltaTimeSeconds);
_dragX = _dragManager->GetX();
_dragY = _dragManager->GetY();
// モーションによるパラメータ更新の有無
csmBool motionUpdated = false;
//-----------------------------------------------------------------
_model->LoadParameters(); // 前回セーブされた状態をロード
if (_motionManager->IsFinished()) // 判断动画是否播放完成
{
// モーションの再生がない場合、待機モーションの中からランダムで再生する
// 如果没有动画播放,则从等待动画中随机选择一个进行播放
StartRandomMotion(MotionGroupIdle, PriorityIdle); // TODO
}
else
{
motionUpdated = _motionManager->UpdateMotion(_model, deltaTimeSeconds); // モーションを更新
}
_model->SaveParameters(); // 状態を保存
//-----------------------------------------------------------------
// 不透明度
_opacity = _model->GetModelOpacity();
// まばたき 眨眼
if (!motionUpdated)
{
if (_eyeBlink != NULL)
{
// メインモーションの更新がないとき 当主动作没有更新时
_eyeBlink->UpdateParameters(_model, deltaTimeSeconds); // 目パチ
}
}
if (_expressionManager != NULL)
{
_expressionManager->UpdateMotion(_model, deltaTimeSeconds); // 表情でパラメータ更新(相対変化)
}
//ドラッグによる変化
//ドラッグによる顔の向きの調整
_model->AddParameterValue(_idParamAngleX, _dragX * 30); // -30から30の値を加える
_model->AddParameterValue(_idParamAngleY, _dragY * 30);
_model->AddParameterValue(_idParamAngleZ, _dragX * _dragY * -30);
//ドラッグによる体の向きの調整
_model->AddParameterValue(_idParamBodyAngleX, _dragX * 10); // -10から10の値を加える
//ドラッグによる目の向きの調整
_model->AddParameterValue(_idParamEyeBallX, _dragX); // -1から1の値を加える
_model->AddParameterValue(_idParamEyeBallY, _dragY);
// 呼吸など
if (_breath != NULL)
{
_breath->UpdateParameters(_model, deltaTimeSeconds);
}
// 物理演算の設定
if (_physics != NULL)
{
_physics->Evaluate(_model, deltaTimeSeconds);
}
// リップシンクの設定 口型设定
if (_lipSync)
{
// リアルタイムでリップシンクを行う場合、システムから音量を取得して0〜1の範囲で値を入力します。
// 在实时进行口型同步的情况下,从系统中获取音量,并输入一个在0到1范围内的值。
csmFloat32 value = 0.0f;
// 状態更新/RMS値取得
_wavFileHandler.Update(deltaTimeSeconds);
value = _wavFileHandler.GetRms();
// 应用低通滤波器
filteredValue = alpha * value + (1.0f - alpha) * filteredValue;
for (csmUint32 i = 0; i < _lipSyncIds.GetSize(); ++i)
{
_model->AddParameterValue(_lipSyncIds[i], filteredValue, 1.0f);
// if(_debugMode)
// {
// LAppPal::PrintLogLn("[APP] LipSync Parameter ID: %s", _lipSyncIds[i]->GetString().GetRawString());
// }
}
/** _lipSyncIds->GetString().GetRawString()这个参数存的是
* "ParamMouthForm",
"ParamMouthOpenY"这类似的参数
*/
}
// ポーズの設定
if (_pose != NULL)
{
_pose->UpdateParameters(_model, deltaTimeSeconds);
}
_model->Update();
}
/**
* @brief 启动唇形同步并播放指定的 WAV 文件
* @param filePath WAV 文件的路径(csmString 类型)
*/
void LAppModel::StartLipSync(const Csm::csmString& filePath)
{
// 检查文件路径是否有效
if (filePath.GetLength() == 0)
{
LAppPal::PrintLogLn("[APP] Error: WAV file path is empty.");
return;
}
// TODO Mark
// 启动 WAV 文件播放
_wavFileHandler.Start(filePath);
// 确保唇形同步功能已启用
_lipSync = true;
if (_debugMode)
{
LAppPal::PrintLogLn("[APP] LipSync started with WAV file: %s", filePath.GetRawString());
}
}
void LAppModel::StopLipSync()
{
_lipSync = false; // 停止唇形同步
}
CubismMotionQueueEntryHandle LAppModel::StartMotion(const csmChar* group, csmInt32 no, csmInt32 priority, ACubismMotion::FinishedMotionCallback onFinishedMotionHandler)
{
if (priority == PriorityForce)
{
_motionManager->SetReservePriority(priority);
}
else if (!_motionManager->ReserveMotion(priority))
{
if (_debugMode)
{
LAppPal::PrintLogLn("[APP]can't start motion.");
}
return InvalidMotionQueueEntryHandleValue;
}
const csmString fileName = _modelSetting->GetMotionFileName(group, no);
//ex) idle_0
csmString name = Utils::CubismString::GetFormatedString("%s_%d", group, no);
CubismMotion* motion = static_cast<CubismMotion*>(_motions[name.GetRawString()]);
csmBool autoDelete = false;
if (motion == NULL)
{
csmString path = fileName;
path = _modelHomeDir + path;
csmByte* buffer;
csmSizeInt size;
buffer = CreateBuffer(path.GetRawString(), &size);
motion = static_cast<CubismMotion*>(LoadMotion(buffer, size, NULL, onFinishedMotionHandler));
if (motion)
{
csmFloat32 fadeTime = _modelSetting->GetMotionFadeInTimeValue(group, no);
if (fadeTime >= 0.0f)
{
motion->SetFadeInTime(fadeTime);
}
fadeTime = _modelSetting->GetMotionFadeOutTimeValue(group, no);
if (fadeTime >= 0.0f)
{
motion->SetFadeOutTime(fadeTime);
}
motion->SetEffectIds(_eyeBlinkIds, _lipSyncIds);
autoDelete = true; // 終了時にメモリから削除
}
DeleteBuffer(buffer, path.GetRawString());
}
else
{
motion->SetFinishedMotionHandler(onFinishedMotionHandler);
}
//voice
csmString voice = _modelSetting->GetMotionSoundFileName(group, no);
if (strcmp(voice.GetRawString(), "") != 0)
{
csmString path = voice;
path = _modelHomeDir + path;
_wavFileHandler.Start(path);
}
if (_debugMode)
{
LAppPal::PrintLogLn("[APP]start motion: [%s_%d]", group, no);
}
return _motionManager->StartMotionPriority(motion, autoDelete, priority);
}
/**
* 动画组随机更新
* @param group 动画组名
* @param priority 优先级
* @return 动画队列句柄
* @note 优先级大于等于PriorityForce时,会强制更新,且不会返回InvalidMotionQueueEntryHandleValue
*/
CubismMotionQueueEntryHandle LAppModel::StartRandomMotion(const csmChar* group, csmInt32 priority, ACubismMotion::FinishedMotionCallback onFinishedMotionHandler)
{
// 检查动画组是否存在,也就是查看是否有可播放的动画,没有就返回无效的句柄(InvalidMotionQueueEntryHandleValue
if (_modelSetting->GetMotionCount(group) == 0)
{
return InvalidMotionQueueEntryHandleValue;
}
// csmInt32 temp = _modelSetting->GetMotionCount(group);
// TODO: 准备接手这个函数
csmInt32 no = rand() % _modelSetting->GetMotionCount(group);
// 有就返回动画队列句柄
return StartMotion(group, no, priority, onFinishedMotionHandler);
}
/** 讲一下两种动画的不同
* MotionGroupIdle
动画内容通常是 循环播放的、较为平缓的动作。
例如:
轻微的呼吸动作。
身体的轻微晃动。
头部的轻微转动。
MotionGroupTapBody
动画内容通常是 一次性播放的、较为明显的动作。
例如:
挥手。
点头。
跳跃。
其他与用户交互相关的动作。
* 下面的两个函数分别就是获取这两种动作的数量的,不同的动作对应着一个序号,播放的序号不可以超过下面函数返回的最大值
* 要注意的是部分模型可能没有动画
*/
int LAppModel::getIdleMotionCount()
{
return _modelSetting->GetMotionCount(MotionGroupIdle);
}
int LAppModel::getTapBodyMotionCount()
{
return _modelSetting->GetMotionCount(MotionGroupTapBody);
}
void LAppModel::DoDraw()
{
if (_model == NULL)
{
return;
}
GetRenderer<Rendering::CubismRenderer_OpenGLES2>()->DrawModel();
}
void LAppModel::Draw(CubismMatrix44& matrix)
{
if (_model == NULL)
{
return;
}
matrix.MultiplyByMatrix(_modelMatrix);
GetRenderer<Rendering::CubismRenderer_OpenGLES2>()->SetMvpMatrix(&matrix);
DoDraw();
}
csmBool LAppModel::HitTest(const csmChar* hitAreaName, csmFloat32 x, csmFloat32 y)
{
// 透明時は当たり判定なし。
if (_opacity < 1)
{
return false;
}
const csmInt32 count = _modelSetting->GetHitAreasCount();
for (csmInt32 i = 0; i < count; i++)
{
if (strcmp(_modelSetting->GetHitAreaName(i), hitAreaName) == 0)
{
const CubismIdHandle drawID = _modelSetting->GetHitAreaId(i);
return IsHit(drawID, x, y);
}
}
return false; // 存在しない場合はfalse
}
void LAppModel::SetExpression(const csmChar* expressionID)
{
ACubismMotion* motion = _expressions[expressionID];
if (_debugMode)
{
LAppPal::PrintLogLn("[APP]expression: [%s]", expressionID);
}
if (motion != NULL)
{
_expressionManager->StartMotionPriority(motion, false, PriorityForce);
}
else
{
if (_debugMode) LAppPal::PrintLogLn("[APP]expression[%s] is null ", expressionID);
}
}
void LAppModel::SetRandomExpression()
{
if (_expressions.GetSize() == 0)
{
return;
}
csmInt32 no = rand() % _expressions.GetSize();
csmMap<csmString, ACubismMotion*>::const_iterator map_ite;
csmInt32 i = 0;
for (map_ite = _expressions.Begin(); map_ite != _expressions.End(); map_ite++)
{
if (i == no)
{
csmString name = (*map_ite).First;
SetExpression(name.GetRawString());
return;
}
i++;
}
}
void LAppModel::ReloadRenderer()
{
DeleteRenderer();
CreateRenderer();
SetupTextures();
}
void LAppModel::SetupTextures()
{
for (csmInt32 modelTextureNumber = 0; modelTextureNumber < _modelSetting->GetTextureCount(); modelTextureNumber++)
{
// テクスチャ名が空文字だった場合はロード・バインド処理をスキップ
if (strcmp(_modelSetting->GetTextureFileName(modelTextureNumber), "") == 0)
{
continue;
}
//OpenGLのテクスチャユニットにテクスチャをロードする
csmString texturePath = _modelSetting->GetTextureFileName(modelTextureNumber);
texturePath = _modelHomeDir + texturePath;
LAppTextureManager::TextureInfo* texture = LAppDelegate::GetInstance()->GetTextureManager()->CreateTextureFromPngFile(texturePath.GetRawString());
const csmInt32 glTextueNumber = texture->id;
//OpenGL
GetRenderer<Rendering::CubismRenderer_OpenGLES2>()->BindTexture(modelTextureNumber, glTextueNumber);
}
#ifdef PREMULTIPLIED_ALPHA_ENABLE
GetRenderer<Rendering::CubismRenderer_OpenGLES2>()->IsPremultipliedAlpha(true);
#else
GetRenderer<Rendering::CubismRenderer_OpenGLES2>()->IsPremultipliedAlpha(false);
#endif
}
void LAppModel::MotionEventFired(const csmString& eventValue)
{
CubismLogInfo("%s is fired on LAppModel!!", eventValue.GetRawString());
}
Csm::Rendering::CubismOffscreenSurface_OpenGLES2& LAppModel::GetRenderBuffer()
{
return _renderBuffer;
}
csmBool LAppModel::HasMocConsistencyFromFile(const csmChar* mocFileName)
{
CSM_ASSERT(strcmp(mocFileName, ""));
csmByte* buffer;
csmSizeInt size;
csmString path = mocFileName;
path = _modelHomeDir + path;
buffer = CreateBuffer(path.GetRawString(), &size);
csmBool consistency = CubismMoc::HasMocConsistencyFromUnrevivedMoc(buffer, size);
if (!consistency)
{
CubismLogInfo("Inconsistent MOC3.");
}
else
{
CubismLogInfo("Consistent MOC3.");
}
DeleteBuffer(buffer);
return consistency;
}
+141
View File
@@ -0,0 +1,141 @@
/**
* Copyright(c) Live2D Inc. All rights reserved.
*
* Use of this source code is governed by the Live2D Open Software license
* that can be found at https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
*/
#include "LAppPal.hpp"
#include <cstdio>
#include <stdarg.h>
#include <sys/stat.h>
#include <iostream>
#include <fstream>
#include <GL/glew.h>
#include <GLFW/glfw3.h>
#include <Model/CubismMoc.hpp>
#include "LAppDefine.hpp"
#ifdef _WIN32
# include <io.h>
# define VSNPRINTF(buf, sz, fmt, args) \
vsnprintf_s(buf, sz, _TRUNCATE, fmt, args)
#else
# include <cstdio> // vsnprintf
# define VSNPRINTF(buf, sz, fmt, args) \
vsnprintf(buf, sz, fmt, args)
#endif
using std::endl;
using namespace Csm;
using namespace std;
using namespace LAppDefine;
double LAppPal::s_currentFrame = 0.0;
double LAppPal::s_lastFrame = 0.0;
double LAppPal::s_deltaTime = 0.0;
csmByte* LAppPal::LoadFileAsBytes(const string filePath, csmSizeInt* outSize)
{
//filePath;//
const char* path = filePath.c_str();
int size = 0;
struct stat statBuf;
if (stat(path, &statBuf) == 0)
{
size = statBuf.st_size;
if (size == 0)
{
if (DebugLogEnable)
{
PrintLogLn("Stat succeeded but file size is zero. path:%s", path);
}
return NULL;
}
}
else
{
if (DebugLogEnable)
{
PrintLogLn("Stat failed. errno:%d path:%s", errno, path);
}
return NULL;
}
std::fstream file;
file.open(path, std::ios::in | std::ios::binary);
if (!file.is_open())
{
if (DebugLogEnable)
{
PrintLogLn("File open failed. path:%s", path);
}
return NULL;
}
char* buf = new char[size];
file.read(buf, size);
file.close();
*outSize = size;
return reinterpret_cast<csmByte*>(buf);
}
void LAppPal::ReleaseBytes(csmByte* byteData)
{
delete[] byteData;
}
csmFloat32 LAppPal::GetDeltaTime()
{
return static_cast<csmFloat32>(s_deltaTime);
}
void LAppPal::UpdateTime()
{
s_currentFrame = glfwGetTime();
s_deltaTime = s_currentFrame - s_lastFrame;
s_lastFrame = s_currentFrame;
}
void LAppPal::PrintLog(const csmChar* format, ...)
{
va_list args;
csmChar buf[256];
va_start(args, format);
VSNPRINTF(buf, sizeof(buf), format, args); // 標準出力でレンダリング
#ifdef CSM_DEBUG_MEMORY_LEAKING
// メモリリークチェック時は大量の標準出力がはしり重いのでprintfを利用する
std::printf(buf);
#else
std::cout << buf;
#endif
va_end(args);
}
void LAppPal::PrintLogLn(const Csm::csmChar* format, ...)
{
va_list args;
csmChar buf[256];
va_start(args, format);
VSNPRINTF(buf, sizeof(buf), format, args); // 標準出力でレンダリング
#ifdef CSM_DEBUG_MEMORY_LEAKING
// メモリリークチェック時は大量の標準出力がはしり重いのでprintfを利用する
std::printf("%s\n", buf);
#else
std::cout << buf << std::endl;
#endif
va_end(args);
}
void LAppPal::PrintMessage(const csmChar* message)
{
PrintLog("%s", message);
}
void LAppPal::PrintMessageLn(const csmChar* message)
{
PrintLogLn("%s", message);
}
+146
View File
@@ -0,0 +1,146 @@
/**
* Copyright(c) Live2D Inc. All rights reserved.
*
* Use of this source code is governed by the Live2D Open Software license
* that can be found at https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
*/
#include "LAppSprite.hpp"
LAppSprite::LAppSprite(float x, float y, float width, float height, GLuint textureId, GLuint programId)
: _rect()
{
_rect.left = (x - width * 0.5f);
_rect.right = (x + width * 0.5f);
_rect.up = (y + height * 0.5f);
_rect.down = (y - height * 0.5f);
_textureId = textureId;
// 何番目のattribute変数か
_positionLocation = glGetAttribLocation(programId, "position");
_uvLocation = glGetAttribLocation(programId, "uv");
_textureLocation = glGetUniformLocation(programId, "texture");
_colorLocation = glGetUniformLocation(programId, "baseColor");
_spriteColor[0] = 1.0f;
_spriteColor[1] = 1.0f;
_spriteColor[2] = 1.0f;
_spriteColor[3] = 1.0f;
}
LAppSprite::~LAppSprite()
{
}
void LAppSprite::Render() const
{
if (_maxWidth == 0 || _maxHeight == 0)
{
return; // この際は描画できず
}
const GLfloat uvVertex[] =
{
1.0f, 0.0f,
0.0f, 0.0f,
0.0f, 1.0f,
1.0f, 1.0f,
};
// attribute属性を有効にする
glEnableVertexAttribArray(_positionLocation);
glEnableVertexAttribArray(_uvLocation);
// uniform属性の登録
glUniform1i(_textureLocation, 0);
// 頂点データ
float positionVertex[] =
{
(_rect.right - _maxWidth * 0.5f) / (_maxWidth * 0.5f), (_rect.up - _maxHeight * 0.5f) / (_maxHeight * 0.5f),
(_rect.left - _maxWidth * 0.5f) / (_maxWidth * 0.5f), (_rect.up - _maxHeight * 0.5f) / (_maxHeight * 0.5f),
(_rect.left - _maxWidth * 0.5f) / (_maxWidth * 0.5f), (_rect.down - _maxHeight * 0.5f) / (_maxHeight * 0.5f),
(_rect.right - _maxWidth * 0.5f) / (_maxWidth * 0.5f), (_rect.down - _maxHeight * 0.5f) / (_maxHeight * 0.5f)
};
// attribute属性を登録
glVertexAttribPointer(_positionLocation, 2, GL_FLOAT, false, 0, positionVertex);
glVertexAttribPointer(_uvLocation, 2, GL_FLOAT, false, 0, uvVertex);
glUniform4f(_colorLocation, _spriteColor[0], _spriteColor[1], _spriteColor[2], _spriteColor[3]);
// モデルの描画
glBindTexture(GL_TEXTURE_2D, _textureId);
glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
}
void LAppSprite::RenderImmidiate(GLuint textureId, const GLfloat uvVertex[8]) const
{
if (_maxWidth == 0 || _maxHeight == 0)
{
return; // この際は描画できず
}
// attribute属性を有効にする
glEnableVertexAttribArray(_positionLocation);
glEnableVertexAttribArray(_uvLocation);
// uniform属性の登録
glUniform1i(_textureLocation, 0);
// 頂点データ
float positionVertex[] =
{
(_rect.right - _maxWidth * 0.5f) / (_maxWidth * 0.5f), (_rect.up - _maxHeight * 0.5f) / (_maxHeight * 0.5f),
(_rect.left - _maxWidth * 0.5f) / (_maxWidth * 0.5f), (_rect.up - _maxHeight * 0.5f) / (_maxHeight * 0.5f),
(_rect.left - _maxWidth * 0.5f) / (_maxWidth * 0.5f), (_rect.down - _maxHeight * 0.5f) / (_maxHeight * 0.5f),
(_rect.right - _maxWidth * 0.5f) / (_maxWidth * 0.5f), (_rect.down - _maxHeight * 0.5f) / (_maxHeight * 0.5f)
};
// attribute属性を登録
glVertexAttribPointer(_positionLocation, 2, GL_FLOAT, false, 0, positionVertex);
glVertexAttribPointer(_uvLocation, 2, GL_FLOAT, false, 0, uvVertex);
glUniform4f(_colorLocation, _spriteColor[0], _spriteColor[1], _spriteColor[2], _spriteColor[3]);
// モデルの描画
glBindTexture(GL_TEXTURE_2D, textureId);
glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
}
bool LAppSprite::IsHit(float pointX, float pointY) const
{
if (_maxWidth == 0 || _maxHeight == 0)
{
return false; // この際は描画できず
}
//Y座標は変換する必要あり
float y = _maxHeight - pointY;
return (pointX >= _rect.left && pointX <= _rect.right && y <= _rect.up && y >= _rect.down);
}
void LAppSprite::SetColor(float r, float g, float b, float a)
{
_spriteColor[0] = r;
_spriteColor[1] = g;
_spriteColor[2] = b;
_spriteColor[3] = a;
}
void LAppSprite::ResetRect(float x, float y, float width, float height)
{
_rect.left = (x - width * 0.5f);
_rect.right = (x + width * 0.5f);
_rect.up = (y + height * 0.5f);
_rect.down = (y - height * 0.5f);
}
void LAppSprite::SetWindowSize(int width, int height)
{
_maxWidth = width;
_maxHeight = height;
}
@@ -0,0 +1,140 @@
/**
* Copyright(c) Live2D Inc. All rights reserved.
*
* Use of this source code is governed by the Live2D Open Software license
* that can be found at https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
*/
#include "LAppTextureManager.hpp"
#include <iostream>
#define STBI_NO_STDIO
#define STBI_ONLY_PNG
#define STB_IMAGE_IMPLEMENTATION
#include "stb_image.h"
#include "LAppPal.hpp"
LAppTextureManager::LAppTextureManager()
{
}
LAppTextureManager::~LAppTextureManager()
{
ReleaseTextures();
}
LAppTextureManager::TextureInfo* LAppTextureManager::CreateTextureFromPngFile(std::string fileName)
{
//search loaded texture already.
for (Csm::csmUint32 i = 0; i < _textures.GetSize(); i++)
{
if (_textures[i]->fileName == fileName)
{
return _textures[i];
}
}
GLuint textureId;
int width, height, channels;
unsigned int size;
unsigned char* png;
unsigned char* address;
address = LAppPal::LoadFileAsBytes(fileName, &size);
// png情報を取得する
png = stbi_load_from_memory(
address,
static_cast<int>(size),
&width,
&height,
&channels,
STBI_rgb_alpha);
{
#ifdef PREMULTIPLIED_ALPHA_ENABLE
unsigned int* fourBytes = reinterpret_cast<unsigned int*>(png);
for (int i = 0; i < width * height; i++)
{
unsigned char* p = png + i * 4;
fourBytes[i] = Premultiply(p[0], p[1], p[2], p[3]);
}
#endif
}
// OpenGL用のテクスチャを生成する
glGenTextures(1, &textureId);
glBindTexture(GL_TEXTURE_2D, textureId);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, png);
glGenerateMipmap(GL_TEXTURE_2D);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glBindTexture(GL_TEXTURE_2D, 0);
// 解放処理
stbi_image_free(png);
LAppPal::ReleaseBytes(address);
LAppTextureManager::TextureInfo* textureInfo = new LAppTextureManager::TextureInfo();
if (textureInfo != NULL)
{
textureInfo->fileName = fileName;
textureInfo->width = width;
textureInfo->height = height;
textureInfo->id = textureId;
_textures.PushBack(textureInfo);
}
return textureInfo;
}
void LAppTextureManager::ReleaseTextures()
{
for (Csm::csmUint32 i = 0; i < _textures.GetSize(); i++)
{
delete _textures[i];
}
_textures.Clear();
}
void LAppTextureManager::ReleaseTexture(Csm::csmUint32 textureId)
{
for (Csm::csmUint32 i = 0; i < _textures.GetSize(); i++)
{
if (_textures[i]->id != textureId)
{
continue;
}
delete _textures[i];
_textures.Remove(i);
break;
}
}
void LAppTextureManager::ReleaseTexture(std::string fileName)
{
for (Csm::csmUint32 i = 0; i < _textures.GetSize(); i++)
{
if (_textures[i]->fileName == fileName)
{
delete _textures[i];
_textures.Remove(i);
break;
}
}
}
LAppTextureManager::TextureInfo* LAppTextureManager::GetTextureInfoById(GLuint textureId) const
{
for (Csm::csmUint32 i = 0; i < _textures.GetSize(); i++)
{
if (_textures[i]->id == textureId)
{
return _textures[i];
}
}
return NULL;
}
+429
View File
@@ -0,0 +1,429 @@
/**
* Copyright(c) Live2D Inc. All rights reserved.
*
* Use of this source code is governed by the Live2D Open Software license
* that can be found at https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
*/
#include "LAppView.hpp"
#include <math.h>
#include <string>
#include "LAppPal.hpp"
#include "LAppDelegate.hpp"
#include "LAppLive2DManager.hpp"
#include "LAppTextureManager.hpp"
#include "LAppDefine.hpp"
#include "TouchManager.hpp"
#include "LAppSprite.hpp"
#include "LAppModel.hpp"
using namespace std;
using namespace LAppDefine;
LAppView::LAppView():
_programId(0),
//_back(NULL),
//_gear(NULL),
//_power(NULL),
_renderSprite(NULL),
_renderTarget(SelectTarget_None)
{
_clearColor[0] = 1.0f;
_clearColor[1] = 1.0f;
_clearColor[2] = 1.0f;
_clearColor[3] = 0.0f;
// タッチ関係のイベント管理
_touchManager = new TouchManager();
// デバイス座標からスクリーン座標に変換するための
_deviceToScreen = new CubismMatrix44();
// 画面の表示の拡大縮小や移動の変換を行う行列
_viewMatrix = new CubismViewMatrix();
}
LAppView::~LAppView()
{
_renderBuffer.DestroyOffscreenSurface();
delete _renderSprite;
delete _viewMatrix;
delete _deviceToScreen;
delete _touchManager;
//delete _back;
//delete _gear;
//delete _power;
}
void LAppView::Initialize()
{
int width, height;
//glfwGetWindowSize(LAppDelegate::GetInstance()->GetWindow(), &width, &height);
width = LAppDelegate::GetInstance()->GetWindow()->width();
height = LAppDelegate::GetInstance()->GetWindow()->height();
if(width==0 || height==0)
{
return;
}
// 縦サイズを基準とする
float ratio = static_cast<float>(width) / static_cast<float>(height);
float left = -ratio;
float right = ratio;
float bottom = ViewLogicalLeft;
float top = ViewLogicalRight;
_viewMatrix->SetScreenRect(left, right, bottom, top); // デバイスに対応する画面の範囲。 Xの左端, Xの右端, Yの下端, Yの上端
_viewMatrix->Scale(ViewScale, ViewScale);
_deviceToScreen->LoadIdentity(); // サイズが変わった際などリセット必須
if (width > height)
{
float screenW = fabsf(right - left);
_deviceToScreen->ScaleRelative(screenW / width, -screenW / width);
}
else
{
float screenH = fabsf(top - bottom);
_deviceToScreen->ScaleRelative(screenH / height, -screenH / height);
}
_deviceToScreen->TranslateRelative(-width * 0.5f, -height * 0.5f);
// 表示範囲の設定
_viewMatrix->SetMaxScale(ViewMaxScale); // 限界拡大率
_viewMatrix->SetMinScale(ViewMinScale); // 限界縮小率
// 表示できる最大範囲
_viewMatrix->SetMaxScreenRect(
ViewLogicalMaxLeft,
ViewLogicalMaxRight,
ViewLogicalMaxBottom,
ViewLogicalMaxTop
);
}
void LAppView::Render()
{
// 画面サイズを取得する
int maxWidth, maxHeight;
//glfwGetWindowSize(LAppDelegate::GetInstance()->GetWindow(), &maxWidth, &maxHeight);
maxWidth = LAppDelegate::GetInstance()->GetWindow()->width();
maxHeight = LAppDelegate::GetInstance()->GetWindow()->height();
//_back->SetWindowSize(maxWidth, maxHeight);
//_gear->SetWindowSize(maxWidth, maxHeight);
//_power->SetWindowSize(maxWidth, maxHeight);
//_back->Render();
//_gear->Render();
//_power->Render();
LAppLive2DManager* Live2DManager = LAppLive2DManager::GetInstance();
Live2DManager->SetViewMatrix(_viewMatrix);
// Cubism更新・描画
Live2DManager->OnUpdate();
// 各モデルが持つ描画ターゲットをテクスチャとする場合
if (_renderTarget == SelectTarget_ModelFrameBuffer && _renderSprite)
{
const GLfloat uvVertex[] =
{
1.0f, 1.0f,
0.0f, 1.0f,
0.0f, 0.0f,
1.0f, 0.0f,
};
for (csmUint32 i = 0; i < Live2DManager->GetModelNum(); i++)
{
LAppModel* model = Live2DManager->GetModel(i);
float alpha = i < 1 ? 1.0f : model->GetOpacity(); // 片方のみ不透明度を取得できるようにする
_renderSprite->SetColor(1.0f, 1.0f, 1.0f, alpha);
if (model)
{
_renderSprite->SetWindowSize(maxWidth, maxHeight);
_renderSprite->RenderImmidiate( model->GetRenderBuffer().GetColorBuffer(), uvVertex);
}
}
}
}
void LAppView::InitializeSprite()
{
_programId = LAppDelegate::GetInstance()->CreateShader();
int width, height;
//glfwGetWindowSize(LAppDelegate::GetInstance()->GetWindow(), &width, &height);
width = LAppDelegate::GetInstance()->GetWindow()->width();
height = LAppDelegate::GetInstance()->GetWindow()->height();
LAppTextureManager* textureManager = LAppDelegate::GetInstance()->GetTextureManager();
const string resourcesPath = ResourcesPath;
// string imageName = BackImageName;
// LAppTextureManager::TextureInfo* backgroundTexture = textureManager->CreateTextureFromPngFile(resourcesPath + imageName);
// float x = width * 0.5f;
// float y = height * 0.5f;
// float fWidth = static_cast<float>(backgroundTexture->width * 2.0f);
// float fHeight = static_cast<float>(height * 0.95f);
//_back = new LAppSprite(x, y, fWidth, fHeight, backgroundTexture->id, _programId);
// imageName = GearImageName;
// LAppTextureManager::TextureInfo* gearTexture = textureManager->CreateTextureFromPngFile(resourcesPath + imageName);
// x = static_cast<float>(width - gearTexture->width * 0.5f);
// y = static_cast<float>(height - gearTexture->height * 0.5f);
// fWidth = static_cast<float>(gearTexture->width);
// fHeight = static_cast<float>(gearTexture->height);
//_gear = new LAppSprite(x, y, fWidth, fHeight, gearTexture->id, _programId);
// imageName = PowerImageName;
// LAppTextureManager::TextureInfo* powerTexture = textureManager->CreateTextureFromPngFile(resourcesPath + imageName);
//
// x = static_cast<float>(width - powerTexture->width * 0.5f);
// y = static_cast<float>(powerTexture->height * 0.5f);
// fWidth = static_cast<float>(powerTexture->width);
// fHeight = static_cast<float>(powerTexture->height);
// //_power = new LAppSprite(x, y, fWidth, fHeight, powerTexture->id, _programId);
//
// // 画面全体を覆うサイズ
// x = width * 0.5f;
// y = height * 0.5f;
// _renderSprite = new LAppSprite(x, y, static_cast<float>(width), static_cast<float>(height), 0, _programId);
}
void LAppView::OnTouchesBegan(float px, float py) const
{
_touchManager->TouchesBegan(px, py);
}
void LAppView::OnTouchesMoved(float px, float py) const
{
float viewX = this->TransformViewX(_touchManager->GetX());
float viewY = this->TransformViewY(_touchManager->GetY());
_touchManager->TouchesMoved(px, py);
LAppLive2DManager* Live2DManager = LAppLive2DManager::GetInstance();
Live2DManager->OnDrag(viewX, viewY);
}
void LAppView::OnTouchesEnded(float px, float py) const
{
// タッチ終了
LAppLive2DManager* live2DManager = LAppLive2DManager::GetInstance();
live2DManager->OnDrag(0.0f, 0.0f);
{
// シングルタップ
float x = _deviceToScreen->TransformX(_touchManager->GetX()); // 論理座標変換した座標を取得。
float y = _deviceToScreen->TransformY(_touchManager->GetY()); // 論理座標変換した座標を取得。
if (DebugTouchLogEnable)
{
LAppPal::PrintLogLn("[APP]touchesEnded x:%.2f y:%.2f", x, y);
}
live2DManager->OnTap(x, y);
// 歯車にタップしたか
/*if (_gear->IsHit(px, py))
{
live2DManager->NextScene();
}*/
// 電源ボタンにタップしたか
/*if (_power->IsHit(px, py))
{
LAppDelegate::GetInstance()->AppEnd();
}*/
}
}
float LAppView::TransformViewX(float deviceX) const
{
float screenX = _deviceToScreen->TransformX(deviceX); // 論理座標変換した座標を取得。
return _viewMatrix->InvertTransformX(screenX); // 拡大、縮小、移動後の値。
}
float LAppView::TransformViewY(float deviceY) const
{
float screenY = _deviceToScreen->TransformY(deviceY); // 論理座標変換した座標を取得。
return _viewMatrix->InvertTransformY(screenY); // 拡大、縮小、移動後の値。
}
float LAppView::TransformScreenX(float deviceX) const
{
return _deviceToScreen->TransformX(deviceX);
}
float LAppView::TransformScreenY(float deviceY) const
{
return _deviceToScreen->TransformY(deviceY);
}
void LAppView::PreModelDraw(LAppModel& refModel)
{
// 別のレンダリングターゲットへ向けて描画する場合の使用するフレームバッファ
Csm::Rendering::CubismOffscreenSurface_OpenGLES2* useTarget = NULL;
if (_renderTarget != SelectTarget_None)
{// 別のレンダリングターゲットへ向けて描画する場合
// 使用するターゲット
useTarget = (_renderTarget == SelectTarget_ViewFrameBuffer) ? &_renderBuffer : &refModel.GetRenderBuffer();
if (!useTarget->IsValid())
{// 描画ターゲット内部未作成の場合はここで作成
int width, height;
/*glfwGetWindowSize(LAppDelegate::GetInstance()->GetWindow(), &width, &height);*/
width = LAppDelegate::GetInstance()->GetWindow()->width();
height = LAppDelegate::GetInstance()->GetWindow()->height();
if (width != 0 && height != 0)
{
// モデル描画キャンバス
useTarget->CreateOffscreenSurface(static_cast<csmUint32>(width), static_cast<csmUint32>(height));
}
}
// レンダリング開始
useTarget->BeginDraw();
useTarget->Clear(_clearColor[0], _clearColor[1], _clearColor[2], _clearColor[3]); // 背景クリアカラー
}
}
void LAppView::PostModelDraw(LAppModel& refModel)
{
// 別のレンダリングターゲットへ向けて描画する場合の使用するフレームバッファ
Csm::Rendering::CubismOffscreenSurface_OpenGLES2* useTarget = NULL;
if (_renderTarget != SelectTarget_None)
{// 別のレンダリングターゲットへ向けて描画する場合
// 使用するターゲット
useTarget = (_renderTarget == SelectTarget_ViewFrameBuffer) ? &_renderBuffer : &refModel.GetRenderBuffer();
// レンダリング終了
useTarget->EndDraw();
// LAppViewの持つフレームバッファを使うなら、スプライトへの描画はここ
if (_renderTarget == SelectTarget_ViewFrameBuffer && _renderSprite)
{
const GLfloat uvVertex[] =
{
1.0f, 1.0f,
0.0f, 1.0f,
0.0f, 0.0f,
1.0f, 0.0f,
};
_renderSprite->SetColor(1.0f, 1.0f, 1.0f, GetSpriteAlpha(0));
// 画面サイズを取得する
int maxWidth, maxHeight;
/*glfwGetWindowSize(LAppDelegate::GetInstance()->GetWindow(), &maxWidth, &maxHeight);*/
maxWidth = LAppDelegate::GetInstance()->GetWindow()->width(); // Misaki 修改
maxHeight = LAppDelegate::GetInstance()->GetWindow()->height();
_renderSprite->SetWindowSize(maxWidth, maxHeight);
_renderSprite->RenderImmidiate(useTarget->GetColorBuffer(), uvVertex);
}
}
}
void LAppView::SwitchRenderingTarget(SelectTarget targetType)
{
_renderTarget = targetType;
}
void LAppView::SetRenderTargetClearColor(float r, float g, float b)
{
_clearColor[0] = r;
_clearColor[1] = g;
_clearColor[2] = b;
}
float LAppView::GetSpriteAlpha(int assign) const
{
// assignの数値に応じて適当に決定
float alpha = 0.25f + static_cast<float>(assign) * 0.5f; // サンプルとしてαに適当な差をつける
if (alpha > 1.0f)
{
alpha = 1.0f;
}
if (alpha < 0.1f)
{
alpha = 0.1f;
}
return alpha;
}
void LAppView::ResizeSprite()
{
LAppTextureManager* textureManager = LAppDelegate::GetInstance()->GetTextureManager();
if (!textureManager)
{
return;
}
// 描画領域サイズ
int width, height;
//glfwGetWindowSize(LAppDelegate::GetInstance()->GetWindow(), &width, &height);
width = LAppDelegate::GetInstance()->GetWindow()->width();
height = LAppDelegate::GetInstance()->GetWindow()->height();
float x = 0.0f;
float y = 0.0f;
float fWidth = 0.0f;
float fHeight = 0.0f;
/*if (_back)
{
GLuint id = _back->GetTextureId();
LAppTextureManager::TextureInfo* texInfo = textureManager->GetTextureInfoById(id);
if (texInfo)
{
x = width * 0.5f;
y = height * 0.5f;
fWidth = static_cast<float>(texInfo->width * 2);
fHeight = static_cast<float>(height) * 0.95f;
_back->ResetRect(x, y, fWidth, fHeight);
}
}*/
/*if (_power)
{
GLuint id = _power->GetTextureId();
LAppTextureManager::TextureInfo* texInfo = textureManager->GetTextureInfoById(id);
if (texInfo)
{
x = static_cast<float>(width - texInfo->width * 0.5f);
y = static_cast<float>(texInfo->height * 0.5f);
fWidth = static_cast<float>(texInfo->width);
fHeight = static_cast<float>(texInfo->height);
_power->ResetRect(x, y, fWidth, fHeight);
}
}*/
/*if (_gear)
{
GLuint id = _gear->GetTextureId();
LAppTextureManager::TextureInfo* texInfo = textureManager->GetTextureInfoById(id);
if (texInfo)
{
x = static_cast<float>(width - texInfo->width * 0.5f);
y = static_cast<float>(height - texInfo->height * 0.5f);
fWidth = static_cast<float>(texInfo->width);
fHeight = static_cast<float>(texInfo->height);
_gear->ResetRect(x, y, fWidth, fHeight);
}
}*/
}
@@ -0,0 +1,352 @@
/**
* Copyright(c) Live2D Inc. All rights reserved.
*
* Use of this source code is governed by the Live2D Open Software license
* that can be found at https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
*/
#include "LAppWavFileHandler.hpp"
#include <cmath>
#include <cstdint>
#include "LAppPal.hpp"
LAppWavFileHandler::LAppWavFileHandler()
: _rawData(NULL)
, _pcmData(NULL)
, _userTimeSeconds(0.0f)
, _lastRms(0.0f)
, _sampleOffset(0)
{
}
LAppWavFileHandler::~LAppWavFileHandler()
{
if (_rawData != NULL)
{
CSM_FREE(_rawData);
}
if (_pcmData != NULL)
{
ReleasePcmData();
}
}
Csm::csmBool LAppWavFileHandler::Update(Csm::csmFloat32 deltaTimeSeconds)
{
Csm::csmUint32 goalOffset;
Csm::csmFloat32 rms;
// データロード前/ファイル末尾に達した場合は更新しない
if ((_pcmData == NULL)
|| (_sampleOffset >= _wavFileInfo._samplesPerChannel))
{
_lastRms = 0.0f;
return false;
}
// 経過時間後の状態を保持
_userTimeSeconds += deltaTimeSeconds;
goalOffset = static_cast<Csm::csmUint32>(_userTimeSeconds * _wavFileInfo._samplingRate);
if (goalOffset > _wavFileInfo._samplesPerChannel)
{
goalOffset = _wavFileInfo._samplesPerChannel;
}
// RMS計測
rms = 0.0f;
for (Csm::csmUint32 channelCount = 0; channelCount < _wavFileInfo._numberOfChannels; channelCount++)
{
for (Csm::csmUint32 sampleCount = _sampleOffset; sampleCount < goalOffset; sampleCount++)
{
Csm::csmFloat32 pcm = _pcmData[channelCount][sampleCount];
rms += pcm * pcm;
}
}
rms = sqrt(rms / (_wavFileInfo._numberOfChannels * (goalOffset - _sampleOffset)));
_lastRms = rms;
_sampleOffset = goalOffset;
return true;
}
void LAppWavFileHandler::Start(const Csm::csmString& filePath)
{
// WAVファイルのロード
if (!LoadWavFile(filePath))
{
return;
}
// サンプル参照位置を初期化
_sampleOffset = 0;
_userTimeSeconds = 0.0f;
// RMS値をリセット
_lastRms = 0.0f;
}
Csm::csmFloat32 LAppWavFileHandler::GetRms() const
{
return _lastRms;
}
const LAppWavFileHandler::WavFileInfo& LAppWavFileHandler::GetWavFileInfo() const
{
return _wavFileInfo;
}
const Csm::csmByte* LAppWavFileHandler::GetRawData() const
{
return _rawData;
}
Csm::csmUint64 LAppWavFileHandler::GetRawDataSize() const
{
return _rawDataSize;
}
Csm::csmVector<Csm::csmFloat32> LAppWavFileHandler::GetPcmData() const
{
Csm::csmVector<Csm::csmFloat32> buffer;
for (Csm::csmUint32 sampleCount = 0; sampleCount < _wavFileInfo._samplesPerChannel; sampleCount++)
{
for (Csm::csmUint32 channelCount = 0; channelCount < _wavFileInfo._numberOfChannels; channelCount++)
{
buffer.PushBack(_pcmData[channelCount][sampleCount]);
}
}
return buffer;
}
void LAppWavFileHandler::GetPcmDataChannel(Csm::csmFloat32* dst, Csm::csmUint32 useChannel) const
{
for (Csm::csmUint32 sampleCount = 0; sampleCount < _wavFileInfo._samplesPerChannel; sampleCount++)
{
dst[sampleCount] = _pcmData[useChannel][sampleCount];
}
}
Csm::csmFloat32 LAppWavFileHandler::NormalizePcmSample(Csm::csmUint32 bitsPerSample, Csm::csmByte* data, Csm::csmUint32 dataSize)
{
Csm::csmInt32 pcm32;
// 32ビット幅に拡張してから-1~1の範囲に丸める
switch (bitsPerSample)
{
case 8:
if (1 <= dataSize)
{
const Csm::csmUint8 ret = data[0];
pcm32 = static_cast<Csm::csmInt32>(ret) - 128;
pcm32 <<= 24;
}
else
{
pcm32 = 0;
}
break;
case 16:
if (2 <= dataSize)
{
const Csm::csmUint16 ret = (data[1] << 8) | data[0];
pcm32 = ret << 16;
}
else
{
pcm32 = 0;
}
break;
case 24:
if (3 <= dataSize)
{
const Csm::csmUint32 ret = (data[2] << 16) | (data[1] << 8) | data[0];
pcm32 = ret << 8;
}
else
{
pcm32 = 0;
}
break;
case 32:
if (4 <= dataSize)
{
const Csm::csmUint32 ret = (data[3] << 24) | (data[2] << 16) | (data[1] << 8) | data[0];
pcm32 = ret << 0;
}
else
{
pcm32 = 0;
}
break;
default:
// 対応していないビット幅
pcm32 = 0;
break;
}
return static_cast<Csm::csmFloat32>(pcm32) / INT32_MAX;
}
Csm::csmBool LAppWavFileHandler::LoadWavFile(const Csm::csmString& filePath)
{
Csm::csmBool ret;
// 既にwavファイルロード済みならば領域開放
if (_rawData != NULL)
{
CSM_FREE(_rawData);
_rawDataSize = 0;
}
if (_pcmData != NULL)
{
ReleasePcmData();
}
// ファイルロード
_byteReader._fileByte = LAppPal::LoadFileAsBytes(filePath.GetRawString(), &(_byteReader._fileSize));
_byteReader._readOffset = 0;
// ファイルロードに失敗しているか、先頭のシグネチャ"RIFF"を入れるサイズもない場合は失敗
if ((_byteReader._fileByte == NULL) || (_byteReader._fileSize < 4))
{
return false;
}
// ファイル名
_wavFileInfo._fileName = filePath;
do {
// シグネチャ "RIFF"
if (!_byteReader.GetCheckSignature("RIFF"))
{
ret = false;
break;
}
// ファイルサイズ-8(読み飛ばし)
_byteReader.Get32LittleEndian();
// シグネチャ "WAVE"
if (!_byteReader.GetCheckSignature("WAVE"))
{
ret = false;
break;
}
// シグネチャ "fmt "
if (!_byteReader.GetCheckSignature("fmt "))
{
ret = false;
break;
}
// fmtチャンクサイズ
const Csm::csmUint32 fmtChunkSize = _byteReader.Get32LittleEndian();
// フォーマットIDは1(リニアPCM)以外受け付けない
if (_byteReader.Get16LittleEndian() != 1)
{
ret = false;
break;
}
// チャンネル数
_wavFileInfo._numberOfChannels = _byteReader.Get16LittleEndian();
// サンプリングレート
_wavFileInfo._samplingRate = _byteReader.Get32LittleEndian();
// 平均データ速度
_wavFileInfo._avgBytesPerSec = _byteReader.Get32LittleEndian();
// ブロックサイズ
_wavFileInfo._blockAlign = _byteReader.Get16LittleEndian();
// 量子化ビット数
_wavFileInfo._bitsPerSample = _byteReader.Get16LittleEndian();
// fmtチャンクの拡張部分の読み飛ばし
if (fmtChunkSize > 16)
{
_byteReader._readOffset += (fmtChunkSize - 16);
}
// "data"チャンクが出現するまで読み飛ばし
while (!(_byteReader.GetCheckSignature("data"))
&& (_byteReader._readOffset < _byteReader._fileSize))
{
_byteReader._readOffset += _byteReader.Get32LittleEndian();
}
// ファイル内に"data"チャンクが出現しなかった
if (_byteReader._readOffset >= _byteReader._fileSize)
{
ret = false;
break;
}
// サンプル数
{
const Csm::csmUint32 dataChunkSize = _byteReader.Get32LittleEndian();
_wavFileInfo._samplesPerChannel = (dataChunkSize * 8) / (_wavFileInfo._bitsPerSample * _wavFileInfo._numberOfChannels);
}
// 領域確保
_rawDataSize = static_cast<Csm::csmUint64>(_wavFileInfo._blockAlign) * static_cast<Csm::csmUint64>(_wavFileInfo._samplesPerChannel);
_rawData = static_cast<Csm::csmByte*>(CSM_MALLOC(sizeof(Csm::csmByte) * _rawDataSize));
_pcmData = static_cast<Csm::csmFloat32**>(CSM_MALLOC(sizeof(Csm::csmFloat32*) * _wavFileInfo._numberOfChannels));
for (Csm::csmUint32 channelCount = 0; channelCount < _wavFileInfo._numberOfChannels; channelCount++)
{
_pcmData[channelCount] = static_cast<Csm::csmFloat32*>(CSM_MALLOC(sizeof(Csm::csmFloat32) * _wavFileInfo._samplesPerChannel));
}
// 波形データ取得
Csm::csmUint64 rawPos = 0;
for (Csm::csmUint32 sampleCount = 0; sampleCount < _wavFileInfo._samplesPerChannel; sampleCount++)
{
for (Csm::csmUint32 channelCount = 0; channelCount < _wavFileInfo._numberOfChannels; channelCount++)
{
// 正規化前
for (Csm::csmUint32 byteCount = 0; byteCount < _wavFileInfo._bitsPerSample / 8; byteCount++)
{
_rawData[rawPos++] = _byteReader._fileByte[_byteReader._readOffset + byteCount];
}
// 正規化後
_pcmData[channelCount][sampleCount] = GetPcmSample();
}
}
ret = true;
} while (false);
// ファイル開放
LAppPal::ReleaseBytes(_byteReader._fileByte);
_byteReader._fileByte = NULL;
_byteReader._fileSize = 0;
return ret;
}
Csm::csmFloat32 LAppWavFileHandler::GetPcmSample()
{
Csm::csmInt32 pcm32;
// 32ビット幅に拡張してから-1~1の範囲に丸める
switch (_wavFileInfo._bitsPerSample)
{
case 8:
pcm32 = static_cast<Csm::csmInt32>(_byteReader.Get8()) - 128;
pcm32 <<= 24;
break;
case 16:
pcm32 = _byteReader.Get16LittleEndian() << 16;
break;
case 24:
pcm32 = _byteReader.Get24LittleEndian() << 8;
break;
default:
// 対応していないビット幅
pcm32 = 0;
break;
}
return static_cast<Csm::csmFloat32>(pcm32) / INT32_MAX;
}
void LAppWavFileHandler::ReleasePcmData()
{
for (Csm::csmUint32 channelCount = 0; channelCount < _wavFileInfo._numberOfChannels; channelCount++)
{
CSM_FREE(_pcmData[channelCount]);
}
CSM_FREE(_pcmData);
_pcmData = NULL;
}
+97
View File
@@ -0,0 +1,97 @@
/**
* Copyright(c) Live2D Inc. All rights reserved.
*
* Use of this source code is governed by the Live2D Open Software license
* that can be found at https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
*/
#include "TouchManager.hpp"
#include <math.h>
TouchManager::TouchManager()
: _startY(0.0f)
, _startX(0.0f)
, _lastX(0.0f)
, _lastY(0.0f)
, _lastX1(0.0f)
, _lastY1(0.0f)
, _lastX2(0.0f)
, _lastY2(0.0f)
, _lastTouchDistance(0.0f)
, _deltaX(0.0f)
, _deltaY(0.0f)
, _scale(1.0f)
, _touchSingle(false)
, _flipAvailable(false)
{ }
void TouchManager::TouchesBegan(float deviceX, float deviceY)
{
_lastX = deviceX;
_lastY = deviceY;
_startX = deviceX;
_startY = deviceY;
_lastTouchDistance = -1.0f;
_flipAvailable = true;
_touchSingle = true;
}
void TouchManager::TouchesMoved(float deviceX, float deviceY)
{
_lastX = deviceX;
_lastY = deviceY;
_lastTouchDistance = -1.0f;
_touchSingle = true;
}
void TouchManager::TouchesMoved(float deviceX1, float deviceY1, float deviceX2, float deviceY2)
{
float distance = CalculateDistance(deviceX1, deviceY1, deviceX2, deviceY2);
float centerX = (deviceX1 + deviceX2) * 0.5f;
float centerY = (deviceY1 + deviceY2) * 0.5f;
if (_lastTouchDistance > 0.0f)
{
_scale = powf(distance / _lastTouchDistance, 0.75f);
_deltaX = CalculateMovingAmount(deviceX1 - _lastX1, deviceX2 - _lastX2);
_deltaY = CalculateMovingAmount(deviceY1 - _lastY1, deviceY2 - _lastY2);
}
else
{
_scale = 1.0f;
_deltaX = 0.0f;
_deltaY = 0.0f;
}
_lastX = centerX;
_lastY = centerY;
_lastX1 = deviceX1;
_lastY1 = deviceY1;
_lastX2 = deviceX2;
_lastY2 = deviceY2;
_lastTouchDistance = distance;
_touchSingle = false;
}
float TouchManager::GetFlickDistance() const
{
return CalculateDistance(_startX, _startY, _lastX, _lastY);
}
float TouchManager::CalculateDistance(float x1, float y1, float x2, float y2) const
{
return sqrtf((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2));
}
float TouchManager::CalculateMovingAmount(float v1, float v2)
{
if ((v1 > 0.0f) != (v2 > 0.0f))
{
return 0.0f;
}
float sign = v1 > 0.0f ? 1.0f : -1.0f;
float absoluteValue1 = fabsf(v1);
float absoluteValue2 = fabsf(v2);
return sign * ((absoluteValue1 < absoluteValue2) ? absoluteValue1 : absoluteValue2);
}