diff --git a/.eslintrc.yml b/.eslintrc.yml index 7420c0b..9d17f9a 100644 --- a/.eslintrc.yml +++ b/.eslintrc.yml @@ -23,7 +23,6 @@ rules: - error - allow: - constructors - '@typescript-eslint/no-namespace': warn 'no-fallthrough': warn '@typescript-eslint/unbound-method': off 'no-inner-declarations': off diff --git a/.gitignore b/.gitignore index 9313e9b..edc3515 100644 --- a/.gitignore +++ b/.gitignore @@ -10,4 +10,5 @@ dist/ # Exclude some VSCode setting files. .vscode/* !/.vscode/extensions.json +!/.vscode/settings.json !/.vscode/tasks.json diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..97bef4c --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "typescript.tsdk": "./node_modules/typescript/lib" +} diff --git a/package.json b/package.json index 6635103..0625a00 100644 --- a/package.json +++ b/package.json @@ -3,8 +3,8 @@ "scripts": { "build": "tsc", "test": "tsc --noEmit", - "lint": "eslint src -f codeframe --ext .ts", - "lint:fix": "eslint src -f codeframe --ext .ts --fix", + "lint": "eslint src --ext .ts", + "lint:fix": "eslint src --ext .ts --fix", "clean": "rimraf dist" }, "devDependencies": { diff --git a/src/cubismdefaultparameterid.ts b/src/cubismdefaultparameterid.ts index 4d3168f..1d716d5 100644 --- a/src/cubismdefaultparameterid.ts +++ b/src/cubismdefaultparameterid.ts @@ -10,56 +10,109 @@ * デフォルト値の仕様は以下のマニュアルに基づく
* https://docs.live2d.com/cubism-editor-manual/standard-parametor-list/ */ -export namespace Live2DCubismFramework { +export const CubismDefaultParameterId = Object.freeze>({ // パーツID - export const HitAreaPrefix = 'HitArea'; - export const HitAreaHead = 'Head'; - export const HitAreaBody = 'Body'; - export const PartsIdCore = 'Parts01Core'; - export const PartsArmPrefix = 'Parts01Arm_'; - export const PartsArmLPrefix = 'Parts01ArmL_'; - export const PartsArmRPrefix = 'Parts01ArmR_'; - + HitAreaPrefix: 'HitArea', + HitAreaHead: 'Head', + HitAreaBody: 'Body', + PartsIdCore: 'Parts01Core', + PartsArmPrefix: 'Parts01Arm_', + PartsArmLPrefix: 'Parts01ArmL_', + PartsArmRPrefix: 'Parts01ArmR_', // パラメータID - export const ParamAngleX = 'ParamAngleX'; - export const ParamAngleY = 'ParamAngleY'; - export const ParamAngleZ = 'ParamAngleZ'; - export const ParamEyeLOpen = 'ParamEyeLOpen'; - export const ParamEyeLSmile = 'ParamEyeLSmile'; - export const ParamEyeROpen = 'ParamEyeROpen'; - export const ParamEyeRSmile = 'ParamEyeRSmile'; - export const ParamEyeBallX = 'ParamEyeBallX'; - export const ParamEyeBallY = 'ParamEyeBallY'; - export const ParamEyeBallForm = 'ParamEyeBallForm'; - export const ParamBrowLY = 'ParamBrowLY'; - export const ParamBrowRY = 'ParamBrowRY'; - export const ParamBrowLX = 'ParamBrowLX'; - export const ParamBrowRX = 'ParamBrowRX'; - export const ParamBrowLAngle = 'ParamBrowLAngle'; - export const ParamBrowRAngle = 'ParamBrowRAngle'; - export const ParamBrowLForm = 'ParamBrowLForm'; - export const ParamBrowRForm = 'ParamBrowRForm'; - export const ParamMouthForm = 'ParamMouthForm'; - export const ParamMouthOpenY = 'ParamMouthOpenY'; - export const ParamCheek = 'ParamCheek'; - export const ParamBodyAngleX = 'ParamBodyAngleX'; - export const ParamBodyAngleY = 'ParamBodyAngleY'; - export const ParamBodyAngleZ = 'ParamBodyAngleZ'; - export const ParamBreath = 'ParamBreath'; - export const ParamArmLA = 'ParamArmLA'; - export const ParamArmRA = 'ParamArmRA'; - export const ParamArmLB = 'ParamArmLB'; - export const ParamArmRB = 'ParamArmRB'; - export const ParamHandL = 'ParamHandL'; - export const ParamHandR = 'ParamHandR'; - export const ParamHairFront = 'ParamHairFront'; - export const ParamHairSide = 'ParamHairSide'; - export const ParamHairBack = 'ParamHairBack'; - export const ParamHairFluffy = 'ParamHairFluffy'; - export const ParamShoulderY = 'ParamShoulderY'; - export const ParamBustX = 'ParamBustX'; - export const ParamBustY = 'ParamBustY'; - export const ParamBaseX = 'ParamBaseX'; - export const ParamBaseY = 'ParamBaseY'; - export const ParamNONE = 'NONE:'; + ParamAngleX: 'ParamAngleX', + ParamAngleY: 'ParamAngleY', + ParamAngleZ: 'ParamAngleZ', + ParamEyeLOpen: 'ParamEyeLOpen', + ParamEyeLSmile: 'ParamEyeLSmile', + ParamEyeROpen: 'ParamEyeROpen', + ParamEyeRSmile: 'ParamEyeRSmile', + ParamEyeBallX: 'ParamEyeBallX', + ParamEyeBallY: 'ParamEyeBallY', + ParamEyeBallForm: 'ParamEyeBallForm', + ParamBrowLY: 'ParamBrowLY', + ParamBrowRY: 'ParamBrowRY', + ParamBrowLX: 'ParamBrowLX', + ParamBrowRX: 'ParamBrowRX', + ParamBrowLAngle: 'ParamBrowLAngle', + ParamBrowRAngle: 'ParamBrowRAngle', + ParamBrowLForm: 'ParamBrowLForm', + ParamBrowRForm: 'ParamBrowRForm', + ParamMouthForm: 'ParamMouthForm', + ParamMouthOpenY: 'ParamMouthOpenY', + ParamCheek: 'ParamCheek', + ParamBodyAngleX: 'ParamBodyAngleX', + ParamBodyAngleY: 'ParamBodyAngleY', + ParamBodyAngleZ: 'ParamBodyAngleZ', + ParamBreath: 'ParamBreath', + ParamArmLA: 'ParamArmLA', + ParamArmRA: 'ParamArmRA', + ParamArmLB: 'ParamArmLB', + ParamArmRB: 'ParamArmRB', + ParamHandL: 'ParamHandL', + ParamHandR: 'ParamHandR', + ParamHairFront: 'ParamHairFront', + ParamHairSide: 'ParamHairSide', + ParamHairBack: 'ParamHairBack', + ParamHairFluffy: 'ParamHairFluffy', + ParamShoulderY: 'ParamShoulderY', + ParamBustX: 'ParamBustX', + ParamBustY: 'ParamBustY', + ParamBaseX: 'ParamBaseX', + ParamBaseY: 'ParamBaseY', + ParamNONE: 'NONE:' +}); + +// Namespace definition for compatibility. +import * as $ from './cubismdefaultparameterid'; +// eslint-disable-next-line @typescript-eslint/no-namespace +export namespace Live2DCubismFramework { + export const HitAreaBody = $.CubismDefaultParameterId.HitAreaBody; + export const HitAreaHead = $.CubismDefaultParameterId.HitAreaHead; + export const HitAreaPrefix = $.CubismDefaultParameterId.HitAreaPrefix; + export const ParamAngleX = $.CubismDefaultParameterId.ParamAngleX; + export const ParamAngleY = $.CubismDefaultParameterId.ParamAngleY; + export const ParamAngleZ = $.CubismDefaultParameterId.ParamAngleZ; + export const ParamArmLA = $.CubismDefaultParameterId.ParamArmLA; + export const ParamArmLB = $.CubismDefaultParameterId.ParamArmLB; + export const ParamArmRA = $.CubismDefaultParameterId.ParamArmRA; + export const ParamArmRB = $.CubismDefaultParameterId.ParamArmRB; + export const ParamBaseX = $.CubismDefaultParameterId.ParamBaseX; + export const ParamBaseY = $.CubismDefaultParameterId.ParamBaseY; + export const ParamBodyAngleX = $.CubismDefaultParameterId.ParamBodyAngleX; + export const ParamBodyAngleY = $.CubismDefaultParameterId.ParamBodyAngleY; + export const ParamBodyAngleZ = $.CubismDefaultParameterId.ParamBodyAngleZ; + export const ParamBreath = $.CubismDefaultParameterId.ParamBreath; + export const ParamBrowLAngle = $.CubismDefaultParameterId.ParamBrowLAngle; + export const ParamBrowLForm = $.CubismDefaultParameterId.ParamBrowLForm; + export const ParamBrowLX = $.CubismDefaultParameterId.ParamBrowLX; + export const ParamBrowLY = $.CubismDefaultParameterId.ParamBrowLY; + export const ParamBrowRAngle = $.CubismDefaultParameterId.ParamBrowRAngle; + export const ParamBrowRForm = $.CubismDefaultParameterId.ParamBrowRForm; + export const ParamBrowRX = $.CubismDefaultParameterId.ParamBrowRX; + export const ParamBrowRY = $.CubismDefaultParameterId.ParamBrowRY; + export const ParamBustX = $.CubismDefaultParameterId.ParamBustX; + export const ParamBustY = $.CubismDefaultParameterId.ParamBustY; + export const ParamCheek = $.CubismDefaultParameterId.ParamCheek; + export const ParamEyeBallForm = $.CubismDefaultParameterId.ParamEyeBallForm; + export const ParamEyeBallX = $.CubismDefaultParameterId.ParamEyeBallX; + export const ParamEyeBallY = $.CubismDefaultParameterId.ParamEyeBallY; + export const ParamEyeLOpen = $.CubismDefaultParameterId.ParamEyeLOpen; + export const ParamEyeLSmile = $.CubismDefaultParameterId.ParamEyeLSmile; + export const ParamEyeROpen = $.CubismDefaultParameterId.ParamEyeROpen; + export const ParamEyeRSmile = $.CubismDefaultParameterId.ParamEyeRSmile; + export const ParamHairBack = $.CubismDefaultParameterId.ParamHairBack; + export const ParamHairFluffy = $.CubismDefaultParameterId.ParamHairFluffy; + export const ParamHairFront = $.CubismDefaultParameterId.ParamHairFront; + export const ParamHairSide = $.CubismDefaultParameterId.ParamHairSide; + export const ParamHandL = $.CubismDefaultParameterId.ParamHandL; + export const ParamHandR = $.CubismDefaultParameterId.ParamHandR; + export const ParamMouthForm = $.CubismDefaultParameterId.ParamMouthForm; + export const ParamMouthOpenY = $.CubismDefaultParameterId.ParamMouthOpenY; + export const ParamNONE = $.CubismDefaultParameterId.ParamNONE; + export const ParamShoulderY = $.CubismDefaultParameterId.ParamShoulderY; + export const PartsArmLPrefix = $.CubismDefaultParameterId.PartsArmLPrefix; + export const PartsArmPrefix = $.CubismDefaultParameterId.PartsArmPrefix; + export const PartsArmRPrefix = $.CubismDefaultParameterId.PartsArmRPrefix; + export const PartsIdCore = $.CubismDefaultParameterId.PartsIdCore; } diff --git a/src/cubismmodelsettingjson.ts b/src/cubismmodelsettingjson.ts index 40c5715..5e635c6 100644 --- a/src/cubismmodelsettingjson.ts +++ b/src/cubismmodelsettingjson.ts @@ -5,838 +5,826 @@ * that can be found at https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html. */ -import { Live2DCubismFramework as cubismframework } from './live2dcubismframework'; -import { Live2DCubismFramework as icubismmodelsetting } from './icubismmodelsetting'; -import { Live2DCubismFramework as cubismid } from './id/cubismid'; -import { Live2DCubismFramework as cubismjson } from './utils/cubismjson'; -import { Live2DCubismFramework as csmmap } from './type/csmmap'; -import { Live2DCubismFramework as csmvector } from './type/csmvector'; -import csmVector = csmvector.csmVector; -import csmMap = csmmap.csmMap; -import iterator = csmmap.iterator; -import CubismFramework = cubismframework.CubismFramework; -import CubismIdHandle = cubismid.CubismIdHandle; -import CubismJson = cubismjson.CubismJson; -import Value = cubismjson.Value; -import ICubismModelSetting = icubismmodelsetting.ICubismModelSetting; +import { ICubismModelSetting } from './icubismmodelsetting'; +import { CubismIdHandle } from './id/cubismid'; +import { CubismFramework } from './live2dcubismframework'; +import { csmMap, iterator } from './type/csmmap'; +import { csmVector } from './type/csmvector'; +import { CubismJson, Value } from './utils/cubismjson'; -export namespace Live2DCubismFramework { +/** + * Model3Jsonのキー文字列 + */ + +// JSON Keys +const Version = 'Version'; +const FileReferences = 'FileReferences'; +const Groups = 'Groups'; +const Layout = 'Layout'; +const HitAreas = 'HitAreas'; + +const Moc = 'Moc'; +const Textures = 'Textures'; +const Physics = 'Physics'; +const Pose = 'Pose'; +const Expressions = 'Expressions'; +const Motions = 'Motions'; + +const UserData = 'UserData'; +const Name = 'Name'; +const FilePath = 'File'; +const Id = 'Id'; +const Ids = 'Ids'; +const Target = 'Target'; + +// Motions +const Idle = 'Idle'; +const TapBody = 'TapBody'; +const PinchIn = 'PinchIn'; +const PinchOut = 'PinchOut'; +const Shake = 'Shake'; +const FlickHead = 'FlickHead'; +const Parameter = 'Parameter'; + +const SoundPath = 'Sound'; +const FadeInTime = 'FadeInTime'; +const FadeOutTime = 'FadeOutTime'; + +// Layout +const CenterX = 'CenterX'; +const CenterY = 'CenterY'; +const X = 'X'; +const Y = 'Y'; +const Width = 'Width'; +const Height = 'Height'; + +const LipSync = 'LipSync'; +const EyeBlink = 'EyeBlink'; + +const InitParameter = 'init_param'; +const InitPartsVisible = 'init_parts_visible'; +const Val = 'val'; + +enum FrequestNode { + FrequestNode_Groups, // getRoot().getValueByString(Groups) + FrequestNode_Moc, // getRoot().getValueByString(FileReferences).getValueByString(Moc) + FrequestNode_Motions, // getRoot().getValueByString(FileReferences).getValueByString(Motions) + FrequestNode_Expressions, // getRoot().getValueByString(FileReferences).getValueByString(Expressions) + FrequestNode_Textures, // getRoot().getValueByString(FileReferences).getValueByString(Textures) + FrequestNode_Physics, // getRoot().getValueByString(FileReferences).getValueByString(Physics) + FrequestNode_Pose, // getRoot().getValueByString(FileReferences).getValueByString(Pose) + FrequestNode_HitAreas // getRoot().getValueByString(HitAreas) +} + +/** + * Model3Jsonパーサー + * + * model3.jsonファイルをパースして値を取得する + */ +export class CubismModelSettingJson extends ICubismModelSetting { /** - * Model3Jsonのキー文字列 + * 引数付きコンストラクタ + * + * @param buffer Model3Jsonをバイト配列として読み込んだデータバッファ + * @param size Model3Jsonのデータサイズ */ + public constructor(buffer: ArrayBuffer, size: number) { + super(); + this._json = CubismJson.create(buffer, size); - // JSON Keys - const Version = 'Version'; - const FileReferences = 'FileReferences'; - const Groups = 'Groups'; - const Layout = 'Layout'; - const HitAreas = 'HitAreas'; + if (this._json) { + this._jsonValue = new csmVector(); - const Moc = 'Moc'; - const Textures = 'Textures'; - const Physics = 'Physics'; - const Pose = 'Pose'; - const Expressions = 'Expressions'; - const Motions = 'Motions'; - - const UserData = 'UserData'; - const Name = 'Name'; - const FilePath = 'File'; - const Id = 'Id'; - const Ids = 'Ids'; - const Target = 'Target'; - - // Motions - const Idle = 'Idle'; - const TapBody = 'TapBody'; - const PinchIn = 'PinchIn'; - const PinchOut = 'PinchOut'; - const Shake = 'Shake'; - const FlickHead = 'FlickHead'; - const Parameter = 'Parameter'; - - const SoundPath = 'Sound'; - const FadeInTime = 'FadeInTime'; - const FadeOutTime = 'FadeOutTime'; - - // Layout - const CenterX = 'CenterX'; - const CenterY = 'CenterY'; - const X = 'X'; - const Y = 'Y'; - const Width = 'Width'; - const Height = 'Height'; - - const LipSync = 'LipSync'; - const EyeBlink = 'EyeBlink'; - - const InitParameter = 'init_param'; - const InitPartsVisible = 'init_parts_visible'; - const Val = 'val'; - - enum FrequestNode { - FrequestNode_Groups, // getRoot().getValueByString(Groups) - FrequestNode_Moc, // getRoot().getValueByString(FileReferences).getValueByString(Moc) - FrequestNode_Motions, // getRoot().getValueByString(FileReferences).getValueByString(Motions) - FrequestNode_Expressions, // getRoot().getValueByString(FileReferences).getValueByString(Expressions) - FrequestNode_Textures, // getRoot().getValueByString(FileReferences).getValueByString(Textures) - FrequestNode_Physics, // getRoot().getValueByString(FileReferences).getValueByString(Physics) - FrequestNode_Pose, // getRoot().getValueByString(FileReferences).getValueByString(Pose) - FrequestNode_HitAreas // getRoot().getValueByString(HitAreas) + // 順番はenum FrequestNodeと一致させる + this._jsonValue.pushBack(this._json.getRoot().getValueByString(Groups)); + this._jsonValue.pushBack( + this._json + .getRoot() + .getValueByString(FileReferences) + .getValueByString(Moc) + ); + this._jsonValue.pushBack( + this._json + .getRoot() + .getValueByString(FileReferences) + .getValueByString(Motions) + ); + this._jsonValue.pushBack( + this._json + .getRoot() + .getValueByString(FileReferences) + .getValueByString(Expressions) + ); + this._jsonValue.pushBack( + this._json + .getRoot() + .getValueByString(FileReferences) + .getValueByString(Textures) + ); + this._jsonValue.pushBack( + this._json + .getRoot() + .getValueByString(FileReferences) + .getValueByString(Physics) + ); + this._jsonValue.pushBack( + this._json + .getRoot() + .getValueByString(FileReferences) + .getValueByString(Pose) + ); + this._jsonValue.pushBack(this._json.getRoot().getValueByString(HitAreas)); + } } /** - * Model3Jsonパーサー - * - * model3.jsonファイルをパースして値を取得する + * デストラクタ相当の処理 */ - export class CubismModelSettingJson extends ICubismModelSetting { - /** - * 引数付きコンストラクタ - * - * @param buffer Model3Jsonをバイト配列として読み込んだデータバッファ - * @param size Model3Jsonのデータサイズ - */ - public constructor(buffer: ArrayBuffer, size: number) { - super(); - this._json = CubismJson.create(buffer, size); + public release(): void { + CubismJson.delete(this._json); - if (this._json) { - this._jsonValue = new csmVector(); + this._jsonValue = null; + } - // 順番はenum FrequestNodeと一致させる - this._jsonValue.pushBack(this._json.getRoot().getValueByString(Groups)); - this._jsonValue.pushBack( - this._json - .getRoot() - .getValueByString(FileReferences) - .getValueByString(Moc) - ); - this._jsonValue.pushBack( - this._json - .getRoot() - .getValueByString(FileReferences) - .getValueByString(Motions) - ); - this._jsonValue.pushBack( - this._json - .getRoot() - .getValueByString(FileReferences) - .getValueByString(Expressions) - ); - this._jsonValue.pushBack( - this._json - .getRoot() - .getValueByString(FileReferences) - .getValueByString(Textures) - ); - this._jsonValue.pushBack( - this._json - .getRoot() - .getValueByString(FileReferences) - .getValueByString(Physics) - ); - this._jsonValue.pushBack( - this._json - .getRoot() - .getValueByString(FileReferences) - .getValueByString(Pose) - ); - this._jsonValue.pushBack( - this._json.getRoot().getValueByString(HitAreas) - ); - } + /** + * CubismJsonオブジェクトを取得する + * + * @return CubismJson + */ + public GetJson(): CubismJson { + return this._json; + } + + /** + * Mocファイルの名前を取得する + * @return Mocファイルの名前 + */ + public getModelFileName(): string { + if (!this.isExistModelFile()) { + return ''; + } + return this._jsonValue.at(FrequestNode.FrequestNode_Moc).getRawString(); + } + + /** + * モデルが使用するテクスチャの数を取得する + * テクスチャの数 + */ + public getTextureCount(): number { + if (!this.isExistTextureFiles()) { + return 0; } - /** - * デストラクタ相当の処理 - */ - public release(): void { - CubismJson.delete(this._json); + return this._jsonValue.at(FrequestNode.FrequestNode_Textures).getSize(); + } - this._jsonValue = null; + /** + * テクスチャが配置されたディレクトリの名前を取得する + * @return テクスチャが配置されたディレクトリの名前 + */ + public getTextureDirectory(): string { + return this._jsonValue + .at(FrequestNode.FrequestNode_Textures) + .getRawString(); + } + + /** + * モデルが使用するテクスチャの名前を取得する + * @param index 配列のインデックス値 + * @return テクスチャの名前 + */ + public getTextureFileName(index: number): string { + return this._jsonValue + .at(FrequestNode.FrequestNode_Textures) + .getValueByIndex(index) + .getRawString(); + } + + /** + * モデルに設定された当たり判定の数を取得する + * @return モデルに設定された当たり判定の数 + */ + public getHitAreasCount(): number { + if (!this.isExistHitAreas()) { + return 0; } - /** - * CubismJsonオブジェクトを取得する - * - * @return CubismJson - */ - public GetJson(): CubismJson { - return this._json; - } + return this._jsonValue.at(FrequestNode.FrequestNode_HitAreas).getSize(); + } - /** - * Mocファイルの名前を取得する - * @return Mocファイルの名前 - */ - public getModelFileName(): string { - if (!this.isExistModelFile()) { - return ''; - } - return this._jsonValue.at(FrequestNode.FrequestNode_Moc).getRawString(); - } - - /** - * モデルが使用するテクスチャの数を取得する - * テクスチャの数 - */ - public getTextureCount(): number { - if (!this.isExistTextureFiles()) { - return 0; - } - - return this._jsonValue.at(FrequestNode.FrequestNode_Textures).getSize(); - } - - /** - * テクスチャが配置されたディレクトリの名前を取得する - * @return テクスチャが配置されたディレクトリの名前 - */ - public getTextureDirectory(): string { - return this._jsonValue - .at(FrequestNode.FrequestNode_Textures) - .getRawString(); - } - - /** - * モデルが使用するテクスチャの名前を取得する - * @param index 配列のインデックス値 - * @return テクスチャの名前 - */ - public getTextureFileName(index: number): string { - return this._jsonValue - .at(FrequestNode.FrequestNode_Textures) - .getValueByIndex(index) - .getRawString(); - } - - /** - * モデルに設定された当たり判定の数を取得する - * @return モデルに設定された当たり判定の数 - */ - public getHitAreasCount(): number { - if (!this.isExistHitAreas()) { - return 0; - } - - return this._jsonValue.at(FrequestNode.FrequestNode_HitAreas).getSize(); - } - - /** - * 当たり判定に設定されたIDを取得する - * - * @param index 配列のindex - * @return 当たり判定に設定されたID - */ - public getHitAreaId(index: number): CubismIdHandle { - return CubismFramework.getIdManager().getId( - this._jsonValue - .at(FrequestNode.FrequestNode_HitAreas) - .getValueByIndex(index) - .getValueByString(Id) - .getRawString() - ); - } - - /** - * 当たり判定に設定された名前を取得する - * @param index 配列のインデックス値 - * @return 当たり判定に設定された名前 - */ - public getHitAreaName(index: number): string { - return this._jsonValue + /** + * 当たり判定に設定されたIDを取得する + * + * @param index 配列のindex + * @return 当たり判定に設定されたID + */ + public getHitAreaId(index: number): CubismIdHandle { + return CubismFramework.getIdManager().getId( + this._jsonValue .at(FrequestNode.FrequestNode_HitAreas) .getValueByIndex(index) - .getValueByString(Name) - .getRawString(); - } - - /** - * 物理演算設定ファイルの名前を取得する - * @return 物理演算設定ファイルの名前 - */ - public getPhysicsFileName(): string { - if (!this.isExistPhysicsFile()) { - return ''; - } - - return this._jsonValue - .at(FrequestNode.FrequestNode_Physics) - .getRawString(); - } - - /** - * パーツ切り替え設定ファイルの名前を取得する - * @return パーツ切り替え設定ファイルの名前 - */ - public getPoseFileName(): string { - if (!this.isExistPoseFile()) { - return ''; - } - - return this._jsonValue.at(FrequestNode.FrequestNode_Pose).getRawString(); - } - - /** - * 表情設定ファイルの数を取得する - * @return 表情設定ファイルの数 - */ - public getExpressionCount(): number { - if (!this.isExistExpressionFile()) { - return 0; - } - - return this._jsonValue - .at(FrequestNode.FrequestNode_Expressions) - .getSize(); - } - - /** - * 表情設定ファイルを識別する名前(別名)を取得する - * @param index 配列のインデックス値 - * @return 表情の名前 - */ - public getExpressionName(index: number): string { - return this._jsonValue - .at(FrequestNode.FrequestNode_Expressions) - .getValueByIndex(index) - .getValueByString(Name) - .getRawString(); - } - - /** - * 表情設定ファイルの名前を取得する - * @param index 配列のインデックス値 - * @return 表情設定ファイルの名前 - */ - public getExpressionFileName(index: number): string { - return this._jsonValue - .at(FrequestNode.FrequestNode_Expressions) - .getValueByIndex(index) - .getValueByString(FilePath) - .getRawString(); - } - - /** - * モーショングループの数を取得する - * @return モーショングループの数 - */ - public getMotionGroupCount(): number { - if (!this.isExistMotionGroups()) { - return 0; - } - - return this._jsonValue - .at(FrequestNode.FrequestNode_Motions) - .getKeys() - .getSize(); - } - - /** - * モーショングループの名前を取得する - * @param index 配列のインデックス値 - * @return モーショングループの名前 - */ - public getMotionGroupName(index: number): string { - if (!this.isExistMotionGroups()) { - return null; - } - - return this._jsonValue - .at(FrequestNode.FrequestNode_Motions) - .getKeys() - .at(index); - } - - /** - * モーショングループに含まれるモーションの数を取得する - * @param groupName モーショングループの名前 - * @return モーショングループの数 - */ - public getMotionCount(groupName: string): number { - if (!this.isExistMotionGroupName(groupName)) { - return 0; - } - - return this._jsonValue - .at(FrequestNode.FrequestNode_Motions) - .getValueByString(groupName) - .getSize(); - } - - /** - * グループ名とインデックス値からモーションファイル名を取得する - * @param groupName モーショングループの名前 - * @param index 配列のインデックス値 - * @return モーションファイルの名前 - */ - public getMotionFileName(groupName: string, index: number): string { - if (!this.isExistMotionGroupName(groupName)) { - return ''; - } - - return this._jsonValue - .at(FrequestNode.FrequestNode_Motions) - .getValueByString(groupName) - .getValueByIndex(index) - .getValueByString(FilePath) - .getRawString(); - } - - /** - * モーションに対応するサウンドファイルの名前を取得する - * @param groupName モーショングループの名前 - * @param index 配列のインデックス値 - * @return サウンドファイルの名前 - */ - public getMotionSoundFileName(groupName: string, index: number): string { - if (!this.isExistMotionSoundFile(groupName, index)) { - return ''; - } - - return this._jsonValue - .at(FrequestNode.FrequestNode_Motions) - .getValueByString(groupName) - .getValueByIndex(index) - .getValueByString(SoundPath) - .getRawString(); - } - - /** - * モーション開始時のフェードイン処理時間を取得する - * @param groupName モーショングループの名前 - * @param index 配列のインデックス値 - * @return フェードイン処理時間[秒] - */ - public getMotionFadeInTimeValue(groupName: string, index: number): number { - if (!this.isExistMotionFadeIn(groupName, index)) { - return -1.0; - } - - return this._jsonValue - .at(FrequestNode.FrequestNode_Motions) - .getValueByString(groupName) - .getValueByIndex(index) - .getValueByString(FadeInTime) - .toFloat(); - } - - /** - * モーション終了時のフェードアウト処理時間を取得する - * @param groupName モーショングループの名前 - * @param index 配列のインデックス値 - * @return フェードアウト処理時間[秒] - */ - public getMotionFadeOutTimeValue(groupName: string, index: number): number { - if (!this.isExistMotionFadeOut(groupName, index)) { - return -1.0; - } - - return this._jsonValue - .at(FrequestNode.FrequestNode_Motions) - .getValueByString(groupName) - .getValueByIndex(index) - .getValueByString(FadeOutTime) - .toFloat(); - } - - /** - * ユーザーデータのファイル名を取得する - * @return ユーザーデータのファイル名 - */ - public getUserDataFile(): string { - if (!this.isExistUserDataFile()) { - return ''; - } - - return this._json - .getRoot() - .getValueByString(FileReferences) - .getValueByString(UserData) - .getRawString(); - } - - /** - * レイアウト情報を取得する - * @param outLayoutMap csmMapクラスのインスタンス - * @return true レイアウト情報が存在する - * @return false レイアウト情報が存在しない - */ - public getLayoutMap(outLayoutMap: csmMap): boolean { - // 存在しない要素にアクセスするとエラーになるためValueがnullの場合はnullを代入する - const map: csmMap = this._json - .getRoot() - .getValueByString(Layout) - .getMap(); - - if (map == null) { - return false; - } - - let ret = false; - - for ( - const ite: iterator = map.begin(); - ite.notEqual(map.end()); - ite.preIncrement() - ) { - outLayoutMap.setValue(ite.ptr().first, ite.ptr().second.toFloat()); - ret = true; - } - - return ret; - } - - /** - * 目パチに関連付けられたパラメータの数を取得する - * @return 目パチに関連付けられたパラメータの数 - */ - public getEyeBlinkParameterCount(): number { - if (!this.isExistEyeBlinkParameters()) { - return 0; - } - - let num = 0; - for ( - let i = 0; - i < this._jsonValue.at(FrequestNode.FrequestNode_Groups).getSize(); - i++ - ) { - const refI: Value = this._jsonValue - .at(FrequestNode.FrequestNode_Groups) - .getValueByIndex(i); - if (refI.isNull() || refI.isError()) { - continue; - } - - if (refI.getValueByString(Name).getRawString() == EyeBlink) { - num = refI - .getValueByString(Ids) - .getVector() - .getSize(); - break; - } - } - - return num; - } - - /** - * 目パチに関連付けられたパラメータのIDを取得する - * @param index 配列のインデックス値 - * @return パラメータID - */ - public getEyeBlinkParameterId(index: number): CubismIdHandle { - if (!this.isExistEyeBlinkParameters()) { - return null; - } - - for ( - let i = 0; - i < this._jsonValue.at(FrequestNode.FrequestNode_Groups).getSize(); - i++ - ) { - const refI: Value = this._jsonValue - .at(FrequestNode.FrequestNode_Groups) - .getValueByIndex(i); - if (refI.isNull() || refI.isError()) { - continue; - } - - if (refI.getValueByString(Name).getRawString() == EyeBlink) { - return CubismFramework.getIdManager().getId( - refI - .getValueByString(Ids) - .getValueByIndex(index) - .getRawString() - ); - } - } - return null; - } - - /** - * リップシンクに関連付けられたパラメータの数を取得する - * @return リップシンクに関連付けられたパラメータの数 - */ - public getLipSyncParameterCount(): number { - if (!this.isExistLipSyncParameters()) { - return 0; - } - - let num = 0; - for ( - let i = 0; - i < this._jsonValue.at(FrequestNode.FrequestNode_Groups).getSize(); - i++ - ) { - const refI: Value = this._jsonValue - .at(FrequestNode.FrequestNode_Groups) - .getValueByIndex(i); - if (refI.isNull() || refI.isError()) { - continue; - } - - if (refI.getValueByString(Name).getRawString() == LipSync) { - num = refI - .getValueByString(Ids) - .getVector() - .getSize(); - break; - } - } - - return num; - } - - /** - * リップシンクに関連付けられたパラメータの数を取得する - * @param index 配列のインデックス値 - * @return パラメータID - */ - public getLipSyncParameterId(index: number): CubismIdHandle { - if (!this.isExistLipSyncParameters()) { - return null; - } - - for ( - let i = 0; - i < this._jsonValue.at(FrequestNode.FrequestNode_Groups).getSize(); - i++ - ) { - const refI: Value = this._jsonValue - .at(FrequestNode.FrequestNode_Groups) - .getValueByIndex(i); - if (refI.isNull() || refI.isError()) { - continue; - } - - if (refI.getValueByString(Name).getRawString() == LipSync) { - return CubismFramework.getIdManager().getId( - refI - .getValueByString(Ids) - .getValueByIndex(index) - .getRawString() - ); - } - } - return null; - } - - /** - * モデルファイルのキーが存在するかどうかを確認する - * @return true キーが存在する - * @return false キーが存在しない - */ - private isExistModelFile(): boolean { - const node: Value = this._jsonValue.at(FrequestNode.FrequestNode_Moc); - return !node.isNull() && !node.isError(); - } - - /** - * テクスチャファイルのキーが存在するかどうかを確認する - * @return true キーが存在する - * @return false キーが存在しない - */ - private isExistTextureFiles(): boolean { - const node: Value = this._jsonValue.at( - FrequestNode.FrequestNode_Textures - ); - return !node.isNull() && !node.isError(); - } - - /** - * 当たり判定のキーが存在するかどうかを確認する - * @return true キーが存在する - * @return false キーが存在しない - */ - private isExistHitAreas(): boolean { - const node: Value = this._jsonValue.at( - FrequestNode.FrequestNode_HitAreas - ); - return !node.isNull() && !node.isError(); - } - - /** - * 物理演算ファイルのキーが存在するかどうかを確認する - * @return true キーが存在する - * @return false キーが存在しない - */ - private isExistPhysicsFile(): boolean { - const node: Value = this._jsonValue.at(FrequestNode.FrequestNode_Physics); - return !node.isNull() && !node.isError(); - } - - /** - * ポーズ設定ファイルのキーが存在するかどうかを確認する - * @return true キーが存在する - * @return false キーが存在しない - */ - private isExistPoseFile(): boolean { - const node: Value = this._jsonValue.at(FrequestNode.FrequestNode_Pose); - return !node.isNull() && !node.isError(); - } - - /** - * 表情設定ファイルのキーが存在するかどうかを確認する - * @return true キーが存在する - * @return false キーが存在しない - */ - private isExistExpressionFile(): boolean { - const node: Value = this._jsonValue.at( - FrequestNode.FrequestNode_Expressions - ); - return !node.isNull() && !node.isError(); - } - - /** - * モーショングループのキーが存在するかどうかを確認する - * @return true キーが存在する - * @return false キーが存在しない - */ - private isExistMotionGroups(): boolean { - const node: Value = this._jsonValue.at(FrequestNode.FrequestNode_Motions); - return !node.isNull() && !node.isError(); - } - - /** - * 引数で指定したモーショングループのキーが存在するかどうかを確認する - * @param groupName グループ名 - * @return true キーが存在する - * @return false キーが存在しない - */ - private isExistMotionGroupName(groupName: string): boolean { - const node: Value = this._jsonValue - .at(FrequestNode.FrequestNode_Motions) - .getValueByString(groupName); - return !node.isNull() && !node.isError(); - } - - /** - * 引数で指定したモーションに対応するサウンドファイルのキーが存在するかどうかを確認する - * @param groupName グループ名 - * @param index 配列のインデックス値 - * @return true キーが存在する - * @return false キーが存在しない - */ - private isExistMotionSoundFile(groupName: string, index: number): boolean { - const node: Value = this._jsonValue - .at(FrequestNode.FrequestNode_Motions) - .getValueByString(groupName) - .getValueByIndex(index) - .getValueByString(SoundPath); - return !node.isNull() && !node.isError(); - } - - /** - * 引数で指定したモーションに対応するフェードイン時間のキーが存在するかどうかを確認する - * @param groupName グループ名 - * @param index 配列のインデックス値 - * @return true キーが存在する - * @return false キーが存在しない - */ - private isExistMotionFadeIn(groupName: string, index: number): boolean { - const node: Value = this._jsonValue - .at(FrequestNode.FrequestNode_Motions) - .getValueByString(groupName) - .getValueByIndex(index) - .getValueByString(FadeInTime); - return !node.isNull() && !node.isError(); - } - - /** - * 引数で指定したモーションに対応するフェードアウト時間のキーが存在するかどうかを確認する - * @param groupName グループ名 - * @param index 配列のインデックス値 - * @return true キーが存在する - * @return false キーが存在しない - */ - private isExistMotionFadeOut(groupName: string, index: number): boolean { - const node: Value = this._jsonValue - .at(FrequestNode.FrequestNode_Motions) - .getValueByString(groupName) - .getValueByIndex(index) - .getValueByString(FadeOutTime); - return !node.isNull() && !node.isError(); - } - - /** - * UserDataのファイル名が存在するかどうかを確認する - * @return true キーが存在する - * @return false キーが存在しない - */ - private isExistUserDataFile(): boolean { - const node: Value = this._json - .getRoot() - .getValueByString(FileReferences) - .getValueByString(UserData); - return !node.isNull() && !node.isError(); - } - - /** - * 目ぱちに対応付けられたパラメータが存在するかどうかを確認する - * @return true キーが存在する - * @return false キーが存在しない - */ - private isExistEyeBlinkParameters(): boolean { - if ( - this._jsonValue.at(FrequestNode.FrequestNode_Groups).isNull() || - this._jsonValue.at(FrequestNode.FrequestNode_Groups).isError() - ) { - return false; - } - - for ( - let i = 0; - i < this._jsonValue.at(FrequestNode.FrequestNode_Groups).getSize(); - ++i - ) { - if ( - this._jsonValue - .at(FrequestNode.FrequestNode_Groups) - .getValueByIndex(i) - .getValueByString(Name) - .getRawString() == EyeBlink - ) { - return true; - } - } - - return false; - } - - /** - * リップシンクに対応付けられたパラメータが存在するかどうかを確認する - * @return true キーが存在する - * @return false キーが存在しない - */ - private isExistLipSyncParameters(): boolean { - if ( - this._jsonValue.at(FrequestNode.FrequestNode_Groups).isNull() || - this._jsonValue.at(FrequestNode.FrequestNode_Groups).isError() - ) { - return false; - } - for ( - let i = 0; - i < this._jsonValue.at(FrequestNode.FrequestNode_Groups).getSize(); - ++i - ) { - if ( - this._jsonValue - .at(FrequestNode.FrequestNode_Groups) - .getValueByIndex(i) - .getValueByString(Name) - .getRawString() == LipSync - ) { - return true; - } - } - return false; - } - - private _json: CubismJson; - private _jsonValue: csmVector; + .getValueByString(Id) + .getRawString() + ); } + + /** + * 当たり判定に設定された名前を取得する + * @param index 配列のインデックス値 + * @return 当たり判定に設定された名前 + */ + public getHitAreaName(index: number): string { + return this._jsonValue + .at(FrequestNode.FrequestNode_HitAreas) + .getValueByIndex(index) + .getValueByString(Name) + .getRawString(); + } + + /** + * 物理演算設定ファイルの名前を取得する + * @return 物理演算設定ファイルの名前 + */ + public getPhysicsFileName(): string { + if (!this.isExistPhysicsFile()) { + return ''; + } + + return this._jsonValue.at(FrequestNode.FrequestNode_Physics).getRawString(); + } + + /** + * パーツ切り替え設定ファイルの名前を取得する + * @return パーツ切り替え設定ファイルの名前 + */ + public getPoseFileName(): string { + if (!this.isExistPoseFile()) { + return ''; + } + + return this._jsonValue.at(FrequestNode.FrequestNode_Pose).getRawString(); + } + + /** + * 表情設定ファイルの数を取得する + * @return 表情設定ファイルの数 + */ + public getExpressionCount(): number { + if (!this.isExistExpressionFile()) { + return 0; + } + + return this._jsonValue.at(FrequestNode.FrequestNode_Expressions).getSize(); + } + + /** + * 表情設定ファイルを識別する名前(別名)を取得する + * @param index 配列のインデックス値 + * @return 表情の名前 + */ + public getExpressionName(index: number): string { + return this._jsonValue + .at(FrequestNode.FrequestNode_Expressions) + .getValueByIndex(index) + .getValueByString(Name) + .getRawString(); + } + + /** + * 表情設定ファイルの名前を取得する + * @param index 配列のインデックス値 + * @return 表情設定ファイルの名前 + */ + public getExpressionFileName(index: number): string { + return this._jsonValue + .at(FrequestNode.FrequestNode_Expressions) + .getValueByIndex(index) + .getValueByString(FilePath) + .getRawString(); + } + + /** + * モーショングループの数を取得する + * @return モーショングループの数 + */ + public getMotionGroupCount(): number { + if (!this.isExistMotionGroups()) { + return 0; + } + + return this._jsonValue + .at(FrequestNode.FrequestNode_Motions) + .getKeys() + .getSize(); + } + + /** + * モーショングループの名前を取得する + * @param index 配列のインデックス値 + * @return モーショングループの名前 + */ + public getMotionGroupName(index: number): string { + if (!this.isExistMotionGroups()) { + return null; + } + + return this._jsonValue + .at(FrequestNode.FrequestNode_Motions) + .getKeys() + .at(index); + } + + /** + * モーショングループに含まれるモーションの数を取得する + * @param groupName モーショングループの名前 + * @return モーショングループの数 + */ + public getMotionCount(groupName: string): number { + if (!this.isExistMotionGroupName(groupName)) { + return 0; + } + + return this._jsonValue + .at(FrequestNode.FrequestNode_Motions) + .getValueByString(groupName) + .getSize(); + } + + /** + * グループ名とインデックス値からモーションファイル名を取得する + * @param groupName モーショングループの名前 + * @param index 配列のインデックス値 + * @return モーションファイルの名前 + */ + public getMotionFileName(groupName: string, index: number): string { + if (!this.isExistMotionGroupName(groupName)) { + return ''; + } + + return this._jsonValue + .at(FrequestNode.FrequestNode_Motions) + .getValueByString(groupName) + .getValueByIndex(index) + .getValueByString(FilePath) + .getRawString(); + } + + /** + * モーションに対応するサウンドファイルの名前を取得する + * @param groupName モーショングループの名前 + * @param index 配列のインデックス値 + * @return サウンドファイルの名前 + */ + public getMotionSoundFileName(groupName: string, index: number): string { + if (!this.isExistMotionSoundFile(groupName, index)) { + return ''; + } + + return this._jsonValue + .at(FrequestNode.FrequestNode_Motions) + .getValueByString(groupName) + .getValueByIndex(index) + .getValueByString(SoundPath) + .getRawString(); + } + + /** + * モーション開始時のフェードイン処理時間を取得する + * @param groupName モーショングループの名前 + * @param index 配列のインデックス値 + * @return フェードイン処理時間[秒] + */ + public getMotionFadeInTimeValue(groupName: string, index: number): number { + if (!this.isExistMotionFadeIn(groupName, index)) { + return -1.0; + } + + return this._jsonValue + .at(FrequestNode.FrequestNode_Motions) + .getValueByString(groupName) + .getValueByIndex(index) + .getValueByString(FadeInTime) + .toFloat(); + } + + /** + * モーション終了時のフェードアウト処理時間を取得する + * @param groupName モーショングループの名前 + * @param index 配列のインデックス値 + * @return フェードアウト処理時間[秒] + */ + public getMotionFadeOutTimeValue(groupName: string, index: number): number { + if (!this.isExistMotionFadeOut(groupName, index)) { + return -1.0; + } + + return this._jsonValue + .at(FrequestNode.FrequestNode_Motions) + .getValueByString(groupName) + .getValueByIndex(index) + .getValueByString(FadeOutTime) + .toFloat(); + } + + /** + * ユーザーデータのファイル名を取得する + * @return ユーザーデータのファイル名 + */ + public getUserDataFile(): string { + if (!this.isExistUserDataFile()) { + return ''; + } + + return this._json + .getRoot() + .getValueByString(FileReferences) + .getValueByString(UserData) + .getRawString(); + } + + /** + * レイアウト情報を取得する + * @param outLayoutMap csmMapクラスのインスタンス + * @return true レイアウト情報が存在する + * @return false レイアウト情報が存在しない + */ + public getLayoutMap(outLayoutMap: csmMap): boolean { + // 存在しない要素にアクセスするとエラーになるためValueがnullの場合はnullを代入する + const map: csmMap = this._json + .getRoot() + .getValueByString(Layout) + .getMap(); + + if (map == null) { + return false; + } + + let ret = false; + + for ( + const ite: iterator = map.begin(); + ite.notEqual(map.end()); + ite.preIncrement() + ) { + outLayoutMap.setValue(ite.ptr().first, ite.ptr().second.toFloat()); + ret = true; + } + + return ret; + } + + /** + * 目パチに関連付けられたパラメータの数を取得する + * @return 目パチに関連付けられたパラメータの数 + */ + public getEyeBlinkParameterCount(): number { + if (!this.isExistEyeBlinkParameters()) { + return 0; + } + + let num = 0; + for ( + let i = 0; + i < this._jsonValue.at(FrequestNode.FrequestNode_Groups).getSize(); + i++ + ) { + const refI: Value = this._jsonValue + .at(FrequestNode.FrequestNode_Groups) + .getValueByIndex(i); + if (refI.isNull() || refI.isError()) { + continue; + } + + if (refI.getValueByString(Name).getRawString() == EyeBlink) { + num = refI + .getValueByString(Ids) + .getVector() + .getSize(); + break; + } + } + + return num; + } + + /** + * 目パチに関連付けられたパラメータのIDを取得する + * @param index 配列のインデックス値 + * @return パラメータID + */ + public getEyeBlinkParameterId(index: number): CubismIdHandle { + if (!this.isExistEyeBlinkParameters()) { + return null; + } + + for ( + let i = 0; + i < this._jsonValue.at(FrequestNode.FrequestNode_Groups).getSize(); + i++ + ) { + const refI: Value = this._jsonValue + .at(FrequestNode.FrequestNode_Groups) + .getValueByIndex(i); + if (refI.isNull() || refI.isError()) { + continue; + } + + if (refI.getValueByString(Name).getRawString() == EyeBlink) { + return CubismFramework.getIdManager().getId( + refI + .getValueByString(Ids) + .getValueByIndex(index) + .getRawString() + ); + } + } + return null; + } + + /** + * リップシンクに関連付けられたパラメータの数を取得する + * @return リップシンクに関連付けられたパラメータの数 + */ + public getLipSyncParameterCount(): number { + if (!this.isExistLipSyncParameters()) { + return 0; + } + + let num = 0; + for ( + let i = 0; + i < this._jsonValue.at(FrequestNode.FrequestNode_Groups).getSize(); + i++ + ) { + const refI: Value = this._jsonValue + .at(FrequestNode.FrequestNode_Groups) + .getValueByIndex(i); + if (refI.isNull() || refI.isError()) { + continue; + } + + if (refI.getValueByString(Name).getRawString() == LipSync) { + num = refI + .getValueByString(Ids) + .getVector() + .getSize(); + break; + } + } + + return num; + } + + /** + * リップシンクに関連付けられたパラメータの数を取得する + * @param index 配列のインデックス値 + * @return パラメータID + */ + public getLipSyncParameterId(index: number): CubismIdHandle { + if (!this.isExistLipSyncParameters()) { + return null; + } + + for ( + let i = 0; + i < this._jsonValue.at(FrequestNode.FrequestNode_Groups).getSize(); + i++ + ) { + const refI: Value = this._jsonValue + .at(FrequestNode.FrequestNode_Groups) + .getValueByIndex(i); + if (refI.isNull() || refI.isError()) { + continue; + } + + if (refI.getValueByString(Name).getRawString() == LipSync) { + return CubismFramework.getIdManager().getId( + refI + .getValueByString(Ids) + .getValueByIndex(index) + .getRawString() + ); + } + } + return null; + } + + /** + * モデルファイルのキーが存在するかどうかを確認する + * @return true キーが存在する + * @return false キーが存在しない + */ + private isExistModelFile(): boolean { + const node: Value = this._jsonValue.at(FrequestNode.FrequestNode_Moc); + return !node.isNull() && !node.isError(); + } + + /** + * テクスチャファイルのキーが存在するかどうかを確認する + * @return true キーが存在する + * @return false キーが存在しない + */ + private isExistTextureFiles(): boolean { + const node: Value = this._jsonValue.at(FrequestNode.FrequestNode_Textures); + return !node.isNull() && !node.isError(); + } + + /** + * 当たり判定のキーが存在するかどうかを確認する + * @return true キーが存在する + * @return false キーが存在しない + */ + private isExistHitAreas(): boolean { + const node: Value = this._jsonValue.at(FrequestNode.FrequestNode_HitAreas); + return !node.isNull() && !node.isError(); + } + + /** + * 物理演算ファイルのキーが存在するかどうかを確認する + * @return true キーが存在する + * @return false キーが存在しない + */ + private isExistPhysicsFile(): boolean { + const node: Value = this._jsonValue.at(FrequestNode.FrequestNode_Physics); + return !node.isNull() && !node.isError(); + } + + /** + * ポーズ設定ファイルのキーが存在するかどうかを確認する + * @return true キーが存在する + * @return false キーが存在しない + */ + private isExistPoseFile(): boolean { + const node: Value = this._jsonValue.at(FrequestNode.FrequestNode_Pose); + return !node.isNull() && !node.isError(); + } + + /** + * 表情設定ファイルのキーが存在するかどうかを確認する + * @return true キーが存在する + * @return false キーが存在しない + */ + private isExistExpressionFile(): boolean { + const node: Value = this._jsonValue.at( + FrequestNode.FrequestNode_Expressions + ); + return !node.isNull() && !node.isError(); + } + + /** + * モーショングループのキーが存在するかどうかを確認する + * @return true キーが存在する + * @return false キーが存在しない + */ + private isExistMotionGroups(): boolean { + const node: Value = this._jsonValue.at(FrequestNode.FrequestNode_Motions); + return !node.isNull() && !node.isError(); + } + + /** + * 引数で指定したモーショングループのキーが存在するかどうかを確認する + * @param groupName グループ名 + * @return true キーが存在する + * @return false キーが存在しない + */ + private isExistMotionGroupName(groupName: string): boolean { + const node: Value = this._jsonValue + .at(FrequestNode.FrequestNode_Motions) + .getValueByString(groupName); + return !node.isNull() && !node.isError(); + } + + /** + * 引数で指定したモーションに対応するサウンドファイルのキーが存在するかどうかを確認する + * @param groupName グループ名 + * @param index 配列のインデックス値 + * @return true キーが存在する + * @return false キーが存在しない + */ + private isExistMotionSoundFile(groupName: string, index: number): boolean { + const node: Value = this._jsonValue + .at(FrequestNode.FrequestNode_Motions) + .getValueByString(groupName) + .getValueByIndex(index) + .getValueByString(SoundPath); + return !node.isNull() && !node.isError(); + } + + /** + * 引数で指定したモーションに対応するフェードイン時間のキーが存在するかどうかを確認する + * @param groupName グループ名 + * @param index 配列のインデックス値 + * @return true キーが存在する + * @return false キーが存在しない + */ + private isExistMotionFadeIn(groupName: string, index: number): boolean { + const node: Value = this._jsonValue + .at(FrequestNode.FrequestNode_Motions) + .getValueByString(groupName) + .getValueByIndex(index) + .getValueByString(FadeInTime); + return !node.isNull() && !node.isError(); + } + + /** + * 引数で指定したモーションに対応するフェードアウト時間のキーが存在するかどうかを確認する + * @param groupName グループ名 + * @param index 配列のインデックス値 + * @return true キーが存在する + * @return false キーが存在しない + */ + private isExistMotionFadeOut(groupName: string, index: number): boolean { + const node: Value = this._jsonValue + .at(FrequestNode.FrequestNode_Motions) + .getValueByString(groupName) + .getValueByIndex(index) + .getValueByString(FadeOutTime); + return !node.isNull() && !node.isError(); + } + + /** + * UserDataのファイル名が存在するかどうかを確認する + * @return true キーが存在する + * @return false キーが存在しない + */ + private isExistUserDataFile(): boolean { + const node: Value = this._json + .getRoot() + .getValueByString(FileReferences) + .getValueByString(UserData); + return !node.isNull() && !node.isError(); + } + + /** + * 目ぱちに対応付けられたパラメータが存在するかどうかを確認する + * @return true キーが存在する + * @return false キーが存在しない + */ + private isExistEyeBlinkParameters(): boolean { + if ( + this._jsonValue.at(FrequestNode.FrequestNode_Groups).isNull() || + this._jsonValue.at(FrequestNode.FrequestNode_Groups).isError() + ) { + return false; + } + + for ( + let i = 0; + i < this._jsonValue.at(FrequestNode.FrequestNode_Groups).getSize(); + ++i + ) { + if ( + this._jsonValue + .at(FrequestNode.FrequestNode_Groups) + .getValueByIndex(i) + .getValueByString(Name) + .getRawString() == EyeBlink + ) { + return true; + } + } + + return false; + } + + /** + * リップシンクに対応付けられたパラメータが存在するかどうかを確認する + * @return true キーが存在する + * @return false キーが存在しない + */ + private isExistLipSyncParameters(): boolean { + if ( + this._jsonValue.at(FrequestNode.FrequestNode_Groups).isNull() || + this._jsonValue.at(FrequestNode.FrequestNode_Groups).isError() + ) { + return false; + } + for ( + let i = 0; + i < this._jsonValue.at(FrequestNode.FrequestNode_Groups).getSize(); + ++i + ) { + if ( + this._jsonValue + .at(FrequestNode.FrequestNode_Groups) + .getValueByIndex(i) + .getValueByString(Name) + .getRawString() == LipSync + ) { + return true; + } + } + return false; + } + + private _json: CubismJson; + private _jsonValue: csmVector; +} + +// Namespace definition for compatibility. +import * as $ from './cubismmodelsettingjson'; +// eslint-disable-next-line @typescript-eslint/no-namespace +export namespace Live2DCubismFramework { + export const CubismModelSettingJson = $.CubismModelSettingJson; + export type CubismModelSettingJson = $.CubismModelSettingJson; } diff --git a/src/effect/cubismbreath.ts b/src/effect/cubismbreath.ts index 91362b4..0f35d2e 100644 --- a/src/effect/cubismbreath.ts +++ b/src/effect/cubismbreath.ts @@ -5,120 +5,120 @@ * that can be found at https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html. */ -import { Live2DCubismFramework as csmvector } from '../type/csmvector'; -import { Live2DCubismFramework as cubismmodel } from '../model/cubismmodel'; -import { Live2DCubismFramework as cubismid } from '../id/cubismid'; -import CubismIdHandle = cubismid.CubismIdHandle; -import CubismModel = cubismmodel.CubismModel; -import csmVector = csmvector.csmVector; +import { CubismIdHandle } from '../id/cubismid'; +import { CubismModel } from '../model/cubismmodel'; +import { csmVector } from '../type/csmvector'; -export namespace Live2DCubismFramework { +/** + * 呼吸機能 + * + * 呼吸機能を提供する。 + */ +export class CubismBreath { /** - * 呼吸機能 - * - * 呼吸機能を提供する。 + * インスタンスの作成 */ - export class CubismBreath { - /** - * インスタンスの作成 - */ - public static create(): CubismBreath { - return new CubismBreath(); - } - - /** - * インスタンスの破棄 - * @param instance 対象のCubismBreath - */ - public static delete(instance: CubismBreath): void { - if (instance != null) { - instance = null; - } - } - - /** - * 呼吸のパラメータの紐づけ - * @param breathParameters 呼吸を紐づけたいパラメータのリスト - */ - public setParameters( - breathParameters: csmVector - ): void { - this._breathParameters = breathParameters; - } - - /** - * 呼吸に紐づいているパラメータの取得 - * @return 呼吸に紐づいているパラメータのリスト - */ - public getParameters(): csmVector { - return this._breathParameters; - } - - /** - * モデルのパラメータの更新 - * @param model 対象のモデル - * @param deltaTimeSeconds デルタ時間[秒] - */ - public updateParameters( - model: CubismModel, - deltaTimeSeconds: number - ): void { - this._currentTime += deltaTimeSeconds; - - const t: number = this._currentTime * 2.0 * 3.14159; - - for (let i = 0; i < this._breathParameters.getSize(); ++i) { - const data: BreathParameterData = this._breathParameters.at(i); - - model.addParameterValueById( - data.parameterId, - data.offset + data.peak * Math.sin(t / data.cycle), - data.weight - ); - } - } - - /** - * コンストラクタ - */ - public constructor() { - this._currentTime = 0.0; - } - - _breathParameters: csmVector; // 呼吸にひもづいているパラメータのリスト - _currentTime: number; // 積算時間[秒] + public static create(): CubismBreath { + return new CubismBreath(); } /** - * 呼吸のパラメータ情報 + * インスタンスの破棄 + * @param instance 対象のCubismBreath */ - export class BreathParameterData { - /** - * コンストラクタ - * @param parameterId 呼吸をひもづけるパラメータID - * @param offset 呼吸を正弦波としたときの、波のオフセット - * @param peak 呼吸を正弦波としたときの、波の高さ - * @param cycle 呼吸を正弦波としたときの、波の周期 - * @param weight パラメータへの重み - */ - constructor( - parameterId?: CubismIdHandle, - offset?: number, - peak?: number, - cycle?: number, - weight?: number - ) { - this.parameterId = parameterId == undefined ? null : parameterId; - this.offset = offset == undefined ? 0.0 : offset; - this.peak = peak == undefined ? 0.0 : peak; - this.cycle = cycle == undefined ? 0.0 : cycle; - this.weight = weight == undefined ? 0.0 : weight; + public static delete(instance: CubismBreath): void { + if (instance != null) { + instance = null; } - - parameterId: CubismIdHandle; // 呼吸をひもづけるパラメータID\ - offset: number; // 呼吸を正弦波としたときの、波のオフセット - peak: number; // 呼吸を正弦波としたときの、波の高さ - cycle: number; // 呼吸を正弦波としたときの、波の周期 - weight: number; // パラメータへの重み } + + /** + * 呼吸のパラメータの紐づけ + * @param breathParameters 呼吸を紐づけたいパラメータのリスト + */ + public setParameters(breathParameters: csmVector): void { + this._breathParameters = breathParameters; + } + + /** + * 呼吸に紐づいているパラメータの取得 + * @return 呼吸に紐づいているパラメータのリスト + */ + public getParameters(): csmVector { + return this._breathParameters; + } + + /** + * モデルのパラメータの更新 + * @param model 対象のモデル + * @param deltaTimeSeconds デルタ時間[秒] + */ + public updateParameters(model: CubismModel, deltaTimeSeconds: number): void { + this._currentTime += deltaTimeSeconds; + + const t: number = this._currentTime * 2.0 * 3.14159; + + for (let i = 0; i < this._breathParameters.getSize(); ++i) { + const data: BreathParameterData = this._breathParameters.at(i); + + model.addParameterValueById( + data.parameterId, + data.offset + data.peak * Math.sin(t / data.cycle), + data.weight + ); + } + } + + /** + * コンストラクタ + */ + public constructor() { + this._currentTime = 0.0; + } + + _breathParameters: csmVector; // 呼吸にひもづいているパラメータのリスト + _currentTime: number; // 積算時間[秒] +} + +/** + * 呼吸のパラメータ情報 + */ +export class BreathParameterData { + /** + * コンストラクタ + * @param parameterId 呼吸をひもづけるパラメータID + * @param offset 呼吸を正弦波としたときの、波のオフセット + * @param peak 呼吸を正弦波としたときの、波の高さ + * @param cycle 呼吸を正弦波としたときの、波の周期 + * @param weight パラメータへの重み + */ + constructor( + parameterId?: CubismIdHandle, + offset?: number, + peak?: number, + cycle?: number, + weight?: number + ) { + this.parameterId = parameterId == undefined ? null : parameterId; + this.offset = offset == undefined ? 0.0 : offset; + this.peak = peak == undefined ? 0.0 : peak; + this.cycle = cycle == undefined ? 0.0 : cycle; + this.weight = weight == undefined ? 0.0 : weight; + } + + parameterId: CubismIdHandle; // 呼吸をひもづけるパラメータID\ + offset: number; // 呼吸を正弦波としたときの、波のオフセット + peak: number; // 呼吸を正弦波としたときの、波の高さ + cycle: number; // 呼吸を正弦波としたときの、波の周期 + weight: number; // パラメータへの重み +} + +// Namespace definition for compatibility. +import * as $ from './cubismbreath'; +// eslint-disable-next-line @typescript-eslint/no-namespace +export namespace Live2DCubismFramework { + export const BreathParameterData = $.BreathParameterData; + export type BreathParameterData = $.BreathParameterData; + export const CubismBreath = $.CubismBreath; + export type CubismBreath = $.CubismBreath; } diff --git a/src/effect/cubismeyeblink.ts b/src/effect/cubismeyeblink.ts index 74774fd..efb969f 100644 --- a/src/effect/cubismeyeblink.ts +++ b/src/effect/cubismeyeblink.ts @@ -5,228 +5,229 @@ * that can be found at https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html. */ -import { Live2DCubismFramework as csmvector } from '../type/csmvector'; -import { Live2DCubismFramework as icubismmodelsetting } from '../icubismmodelsetting'; -import { Live2DCubismFramework as cubismid } from '../id/cubismid'; -import { Live2DCubismFramework as cubismmodel } from '../model/cubismmodel'; -import CubismModel = cubismmodel.CubismModel; -import CubismIdHandle = cubismid.CubismIdHandle; -import ICubismModelSetting = icubismmodelsetting.ICubismModelSetting; -import csmVector = csmvector.csmVector; +import { ICubismModelSetting } from '../icubismmodelsetting'; +import { CubismIdHandle } from '../id/cubismid'; +import { CubismModel } from '../model/cubismmodel'; +import { csmVector } from '../type/csmvector'; -export namespace Live2DCubismFramework { +/** + * 自動まばたき機能 + * + * 自動まばたき機能を提供する。 + */ +export class CubismEyeBlink { /** - * 自動まばたき機能 - * - * 自動まばたき機能を提供する。 + * インスタンスを作成する + * @param modelSetting モデルの設定情報 + * @return 作成されたインスタンス + * @note 引数がNULLの場合、パラメータIDが設定されていない空のインスタンスを作成する。 */ - export class CubismEyeBlink { - /** - * インスタンスを作成する - * @param modelSetting モデルの設定情報 - * @return 作成されたインスタンス - * @note 引数がNULLの場合、パラメータIDが設定されていない空のインスタンスを作成する。 - */ - public static create( - modelSetting: ICubismModelSetting = null - ): CubismEyeBlink { - return new CubismEyeBlink(modelSetting); + public static create( + modelSetting: ICubismModelSetting = null + ): CubismEyeBlink { + return new CubismEyeBlink(modelSetting); + } + + /** + * インスタンスの破棄 + * @param eyeBlink 対象のCubismEyeBlink + */ + public static delete(eyeBlink: CubismEyeBlink): void { + if (eyeBlink != null) { + eyeBlink = null; } + } - /** - * インスタンスの破棄 - * @param eyeBlink 対象のCubismEyeBlink - */ - public static delete(eyeBlink: CubismEyeBlink): void { - if (eyeBlink != null) { - eyeBlink = null; - } - } + /** + * まばたきの間隔の設定 + * @param blinkingInterval まばたきの間隔の時間[秒] + */ + public setBlinkingInterval(blinkingInterval: number): void { + this._blinkingIntervalSeconds = blinkingInterval; + } - /** - * まばたきの間隔の設定 - * @param blinkingInterval まばたきの間隔の時間[秒] - */ - public setBlinkingInterval(blinkingInterval: number): void { - this._blinkingIntervalSeconds = blinkingInterval; - } + /** + * まばたきのモーションの詳細設定 + * @param closing まぶたを閉じる動作の所要時間[秒] + * @param closed まぶたを閉じている動作の所要時間[秒] + * @param opening まぶたを開く動作の所要時間[秒] + */ + public setBlinkingSetting( + closing: number, + closed: number, + opening: number + ): void { + this._closingSeconds = closing; + this._closedSeconds = closed; + this._openingSeconds = opening; + } - /** - * まばたきのモーションの詳細設定 - * @param closing まぶたを閉じる動作の所要時間[秒] - * @param closed まぶたを閉じている動作の所要時間[秒] - * @param opening まぶたを開く動作の所要時間[秒] - */ - public setBlinkingSetting( - closing: number, - closed: number, - opening: number - ): void { - this._closingSeconds = closing; - this._closedSeconds = closed; - this._openingSeconds = opening; - } + /** + * まばたきさせるパラメータIDのリストの設定 + * @param parameterIds パラメータのIDのリスト + */ + public setParameterIds(parameterIds: csmVector): void { + this._parameterIds = parameterIds; + } - /** - * まばたきさせるパラメータIDのリストの設定 - * @param parameterIds パラメータのIDのリスト - */ - public setParameterIds(parameterIds: csmVector): void { - this._parameterIds = parameterIds; - } + /** + * まばたきさせるパラメータIDのリストの取得 + * @return パラメータIDのリスト + */ + public getParameterIds(): csmVector { + return this._parameterIds; + } - /** - * まばたきさせるパラメータIDのリストの取得 - * @return パラメータIDのリスト - */ - public getParameterIds(): csmVector { - return this._parameterIds; - } + /** + * モデルのパラメータの更新 + * @param model 対象のモデル + * @param deltaTimeSeconds デルタ時間[秒] + */ + public updateParameters(model: CubismModel, deltaTimeSeconds: number): void { + this._userTimeSeconds += deltaTimeSeconds; + let parameterValue: number; + let t = 0.0; - /** - * モデルのパラメータの更新 - * @param model 対象のモデル - * @param deltaTimeSeconds デルタ時間[秒] - */ - public updateParameters( - model: CubismModel, - deltaTimeSeconds: number - ): void { - this._userTimeSeconds += deltaTimeSeconds; - let parameterValue: number; - let t = 0.0; + switch (this._blinkingState) { + case EyeState.EyeState_Closing: + t = + (this._userTimeSeconds - this._stateStartTimeSeconds) / + this._closingSeconds; - switch (this._blinkingState) { - case EyeState.EyeState_Closing: - t = - (this._userTimeSeconds - this._stateStartTimeSeconds) / - this._closingSeconds; + if (t >= 1.0) { + t = 1.0; + this._blinkingState = EyeState.EyeState_Closed; + this._stateStartTimeSeconds = this._userTimeSeconds; + } - if (t >= 1.0) { - t = 1.0; - this._blinkingState = EyeState.EyeState_Closed; - this._stateStartTimeSeconds = this._userTimeSeconds; - } + parameterValue = 1.0 - t; - parameterValue = 1.0 - t; + break; + case EyeState.EyeState_Closed: + t = + (this._userTimeSeconds - this._stateStartTimeSeconds) / + this._closedSeconds; - break; - case EyeState.EyeState_Closed: - t = - (this._userTimeSeconds - this._stateStartTimeSeconds) / - this._closedSeconds; + if (t >= 1.0) { + this._blinkingState = EyeState.EyeState_Opening; + this._stateStartTimeSeconds = this._userTimeSeconds; + } - if (t >= 1.0) { - this._blinkingState = EyeState.EyeState_Opening; - this._stateStartTimeSeconds = this._userTimeSeconds; - } + parameterValue = 0.0; - parameterValue = 0.0; + break; + case EyeState.EyeState_Opening: + t = + (this._userTimeSeconds - this._stateStartTimeSeconds) / + this._openingSeconds; - break; - case EyeState.EyeState_Opening: - t = - (this._userTimeSeconds - this._stateStartTimeSeconds) / - this._openingSeconds; - - if (t >= 1.0) { - t = 1.0; - this._blinkingState = EyeState.EyeState_Interval; - this._nextBlinkingTime = this.determinNextBlinkingTiming(); - } - - parameterValue = t; - - break; - case EyeState.EyeState_Interval: - if (this._nextBlinkingTime < this._userTimeSeconds) { - this._blinkingState = EyeState.EyeState_Closing; - this._stateStartTimeSeconds = this._userTimeSeconds; - } - - parameterValue = 1.0; - - break; - case EyeState.EyeState_First: - default: + if (t >= 1.0) { + t = 1.0; this._blinkingState = EyeState.EyeState_Interval; this._nextBlinkingTime = this.determinNextBlinkingTiming(); + } - parameterValue = 1.0; - break; - } + parameterValue = t; - if (!CubismEyeBlink.CloseIfZero) { - parameterValue = -parameterValue; - } + break; + case EyeState.EyeState_Interval: + if (this._nextBlinkingTime < this._userTimeSeconds) { + this._blinkingState = EyeState.EyeState_Closing; + this._stateStartTimeSeconds = this._userTimeSeconds; + } - for (let i = 0; i < this._parameterIds.getSize(); ++i) { - model.setParameterValueById(this._parameterIds.at(i), parameterValue); - } + parameterValue = 1.0; + + break; + case EyeState.EyeState_First: + default: + this._blinkingState = EyeState.EyeState_Interval; + this._nextBlinkingTime = this.determinNextBlinkingTiming(); + + parameterValue = 1.0; + break; } - /** - * コンストラクタ - * @param modelSetting モデルの設定情報 - */ - public constructor(modelSetting: ICubismModelSetting) { - this._blinkingState = EyeState.EyeState_First; - this._nextBlinkingTime = 0.0; - this._stateStartTimeSeconds = 0.0; - this._blinkingIntervalSeconds = 4.0; - this._closingSeconds = 0.1; - this._closedSeconds = 0.05; - this._openingSeconds = 0.15; - this._userTimeSeconds = 0.0; - this._parameterIds = new csmVector(); - - if (modelSetting == null) { - return; - } - - for (let i = 0; i < modelSetting.getEyeBlinkParameterCount(); ++i) { - this._parameterIds.pushBack(modelSetting.getEyeBlinkParameterId(i)); - } + if (!CubismEyeBlink.CloseIfZero) { + parameterValue = -parameterValue; } - /** - * 次の瞬きのタイミングの決定 - * - * @return 次のまばたきを行う時刻[秒] - */ - public determinNextBlinkingTiming(): number { - const r: number = Math.random(); - return ( - this._userTimeSeconds + r * (2.0 * this._blinkingIntervalSeconds - 1.0) - ); + for (let i = 0; i < this._parameterIds.getSize(); ++i) { + model.setParameterValueById(this._parameterIds.at(i), parameterValue); } - - _blinkingState: number; // 現在の状態 - _parameterIds: csmVector; // 操作対象のパラメータのIDのリスト - _nextBlinkingTime: number; // 次のまばたきの時刻[秒] - _stateStartTimeSeconds: number; // 現在の状態が開始した時刻[秒] - _blinkingIntervalSeconds: number; // まばたきの間隔[秒] - _closingSeconds: number; // まぶたを閉じる動作の所要時間[秒] - _closedSeconds: number; // まぶたを閉じている動作の所要時間[秒] - _openingSeconds: number; // まぶたを開く動作の所要時間[秒] - _userTimeSeconds: number; // デルタ時間の積算値[秒] - - /** - * IDで指定された目のパラメータが、0のときに閉じるなら true 、1の時に閉じるなら false 。 - */ - static readonly CloseIfZero: boolean = true; } /** - * まばたきの状態 - * - * まばたきの状態を表す列挙型 + * コンストラクタ + * @param modelSetting モデルの設定情報 */ - export enum EyeState { - EyeState_First = 0, // 初期状態 - EyeState_Interval, // まばたきしていない状態 - EyeState_Closing, // まぶたが閉じていく途中の状態 - EyeState_Closed, // まぶたが閉じている状態 - EyeState_Opening // まぶたが開いていく途中の状態 + public constructor(modelSetting: ICubismModelSetting) { + this._blinkingState = EyeState.EyeState_First; + this._nextBlinkingTime = 0.0; + this._stateStartTimeSeconds = 0.0; + this._blinkingIntervalSeconds = 4.0; + this._closingSeconds = 0.1; + this._closedSeconds = 0.05; + this._openingSeconds = 0.15; + this._userTimeSeconds = 0.0; + this._parameterIds = new csmVector(); + + if (modelSetting == null) { + return; + } + + for (let i = 0; i < modelSetting.getEyeBlinkParameterCount(); ++i) { + this._parameterIds.pushBack(modelSetting.getEyeBlinkParameterId(i)); + } } + + /** + * 次の瞬きのタイミングの決定 + * + * @return 次のまばたきを行う時刻[秒] + */ + public determinNextBlinkingTiming(): number { + const r: number = Math.random(); + return ( + this._userTimeSeconds + r * (2.0 * this._blinkingIntervalSeconds - 1.0) + ); + } + + _blinkingState: number; // 現在の状態 + _parameterIds: csmVector; // 操作対象のパラメータのIDのリスト + _nextBlinkingTime: number; // 次のまばたきの時刻[秒] + _stateStartTimeSeconds: number; // 現在の状態が開始した時刻[秒] + _blinkingIntervalSeconds: number; // まばたきの間隔[秒] + _closingSeconds: number; // まぶたを閉じる動作の所要時間[秒] + _closedSeconds: number; // まぶたを閉じている動作の所要時間[秒] + _openingSeconds: number; // まぶたを開く動作の所要時間[秒] + _userTimeSeconds: number; // デルタ時間の積算値[秒] + + /** + * IDで指定された目のパラメータが、0のときに閉じるなら true 、1の時に閉じるなら false 。 + */ + static readonly CloseIfZero: boolean = true; +} + +/** + * まばたきの状態 + * + * まばたきの状態を表す列挙型 + */ +export enum EyeState { + EyeState_First = 0, // 初期状態 + EyeState_Interval, // まばたきしていない状態 + EyeState_Closing, // まぶたが閉じていく途中の状態 + EyeState_Closed, // まぶたが閉じている状態 + EyeState_Opening // まぶたが開いていく途中の状態 +} + +// Namespace definition for compatibility. +import * as $ from './cubismeyeblink'; +// eslint-disable-next-line @typescript-eslint/no-namespace +export namespace Live2DCubismFramework { + export const CubismEyeBlink = $.CubismEyeBlink; + export type CubismEyeBlink = $.CubismEyeBlink; + export const EyeState = $.EyeState; + export type EyeState = $.EyeState; } diff --git a/src/effect/cubismpose.ts b/src/effect/cubismpose.ts index 5c47d18..ceaedee 100644 --- a/src/effect/cubismpose.ts +++ b/src/effect/cubismpose.ts @@ -5,352 +5,322 @@ * that can be found at https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html. */ -import { Live2DCubismFramework as cubismid } from '../id/cubismid'; -import { Live2DCubismFramework as csmvector } from '../type/csmvector'; -import { Live2DCubismFramework as cubismmodel } from '../model/cubismmodel'; -import { Live2DCubismFramework as cubismframework } from '../live2dcubismframework'; -import { Live2DCubismFramework as cubismjson } from '../utils/cubismjson'; -import CubismIdHandle = cubismid.CubismIdHandle; -import csmVector = csmvector.csmVector; -import iterator = csmvector.iterator; -import CubismModel = cubismmodel.CubismModel; -import CubismFramework = cubismframework.CubismFramework; -import CubismJson = cubismjson.CubismJson; -import Value = cubismjson.Value; +import { CubismIdHandle } from '../id/cubismid'; +import { CubismFramework } from '../live2dcubismframework'; +import { CubismModel } from '../model/cubismmodel'; +import { csmVector, iterator } from '../type/csmvector'; +import { CubismJson, Value } from '../utils/cubismjson'; -export namespace Live2DCubismFramework { - const Epsilon = 0.001; - const DefaultFadeInSeconds = 0.5; +const Epsilon = 0.001; +const DefaultFadeInSeconds = 0.5; - // Pose.jsonのタグ - const FadeIn = 'FadeInTime'; - const Link = 'Link'; - const Groups = 'Groups'; - const Id = 'Id'; +// Pose.jsonのタグ +const FadeIn = 'FadeInTime'; +const Link = 'Link'; +const Groups = 'Groups'; +const Id = 'Id'; +/** + * パーツの不透明度の設定 + * + * パーツの不透明度の管理と設定を行う。 + */ +export class CubismPose { /** - * パーツの不透明度の設定 - * - * パーツの不透明度の管理と設定を行う。 + * インスタンスの作成 + * @param pose3json pose3.jsonのデータ + * @param size pose3.jsonのデータのサイズ[byte] + * @return 作成されたインスタンス */ - export class CubismPose { - /** - * インスタンスの作成 - * @param pose3json pose3.jsonのデータ - * @param size pose3.jsonのデータのサイズ[byte] - * @return 作成されたインスタンス - */ - public static create(pose3json: ArrayBuffer, size: number): CubismPose { - const ret: CubismPose = new CubismPose(); - const json: CubismJson = CubismJson.create(pose3json, size); - const root: Value = json.getRoot(); + public static create(pose3json: ArrayBuffer, size: number): CubismPose { + const ret: CubismPose = new CubismPose(); + const json: CubismJson = CubismJson.create(pose3json, size); + const root: Value = json.getRoot(); - // フェード時間の指定 - if (!root.getValueByString(FadeIn).isNull()) { - ret._fadeTimeSeconds = root - .getValueByString(FadeIn) - .toFloat(DefaultFadeInSeconds); + // フェード時間の指定 + if (!root.getValueByString(FadeIn).isNull()) { + ret._fadeTimeSeconds = root + .getValueByString(FadeIn) + .toFloat(DefaultFadeInSeconds); - if (ret._fadeTimeSeconds <= 0.0) { - ret._fadeTimeSeconds = DefaultFadeInSeconds; - } - } - - // パーツグループ - const poseListInfo: Value = root.getValueByString(Groups); - const poseCount: number = poseListInfo.getSize(); - - for (let poseIndex = 0; poseIndex < poseCount; ++poseIndex) { - const idListInfo: Value = poseListInfo.getValueByIndex(poseIndex); - const idCount: number = idListInfo.getSize(); - let groupCount = 0; - - for (let groupIndex = 0; groupIndex < idCount; ++groupIndex) { - const partInfo: Value = idListInfo.getValueByIndex(groupIndex); - const partData: PartData = new PartData(); - const parameterId: CubismIdHandle = CubismFramework.getIdManager().getId( - partInfo.getValueByString(Id).getRawString() - ); - - partData.partId = parameterId; - - // リンクするパーツの設定 - if (!partInfo.getValueByString(Link).isNull()) { - const linkListInfo: Value = partInfo.getValueByString(Link); - const linkCount: number = linkListInfo.getSize(); - - for (let linkIndex = 0; linkIndex < linkCount; ++linkIndex) { - const linkPart: PartData = new PartData(); - const linkId: CubismIdHandle = CubismFramework.getIdManager().getId( - linkListInfo.getValueByIndex(linkIndex).getString() - ); - - linkPart.partId = linkId; - - partData.link.pushBack(linkPart); - } - } - - ret._partGroups.pushBack(partData.clone()); - - ++groupCount; - } - - ret._partGroupCounts.pushBack(groupCount); - } - - CubismJson.delete(json); - - return ret; - } - - /** - * インスタンスを破棄する - * @param pose 対象のCubismPose - */ - public static delete(pose: CubismPose): void { - if (pose != null) { - pose = null; + if (ret._fadeTimeSeconds <= 0.0) { + ret._fadeTimeSeconds = DefaultFadeInSeconds; } } - /** - * モデルのパラメータの更新 - * @param model 対象のモデル - * @param deltaTimeSeconds デルタ時間[秒] - */ - public updateParameters( - model: CubismModel, - deltaTimeSeconds: number - ): void { - // 前回のモデルと同じでない場合は初期化が必要 - if (model != this._lastModel) { - // パラメータインデックスの初期化 - this.reset(model); + // パーツグループ + const poseListInfo: Value = root.getValueByString(Groups); + const poseCount: number = poseListInfo.getSize(); + + for (let poseIndex = 0; poseIndex < poseCount; ++poseIndex) { + const idListInfo: Value = poseListInfo.getValueByIndex(poseIndex); + const idCount: number = idListInfo.getSize(); + let groupCount = 0; + + for (let groupIndex = 0; groupIndex < idCount; ++groupIndex) { + const partInfo: Value = idListInfo.getValueByIndex(groupIndex); + const partData: PartData = new PartData(); + const parameterId: CubismIdHandle = CubismFramework.getIdManager().getId( + partInfo.getValueByString(Id).getRawString() + ); + + partData.partId = parameterId; + + // リンクするパーツの設定 + if (!partInfo.getValueByString(Link).isNull()) { + const linkListInfo: Value = partInfo.getValueByString(Link); + const linkCount: number = linkListInfo.getSize(); + + for (let linkIndex = 0; linkIndex < linkCount; ++linkIndex) { + const linkPart: PartData = new PartData(); + const linkId: CubismIdHandle = CubismFramework.getIdManager().getId( + linkListInfo.getValueByIndex(linkIndex).getString() + ); + + linkPart.partId = linkId; + + partData.link.pushBack(linkPart); + } + } + + ret._partGroups.pushBack(partData.clone()); + + ++groupCount; } - this._lastModel = model; - - // 設定から時間を変更すると、経過時間がマイナスになる事があるので、経過時間0として対応 - if (deltaTimeSeconds < 0.0) { - deltaTimeSeconds = 0.0; - } - - let beginIndex = 0; - - for (let i = 0; i < this._partGroupCounts.getSize(); i++) { - const partGroupCount: number = this._partGroupCounts.at(i); - - this.doFade(model, deltaTimeSeconds, beginIndex, partGroupCount); - - beginIndex += partGroupCount; - } - - this.copyPartOpacities(model); + ret._partGroupCounts.pushBack(groupCount); } - /** - * 表示を初期化 - * @param model 対象のモデル - * @note 不透明度の初期値が0でないパラメータは、不透明度を1に設定する - */ - public reset(model: CubismModel): void { - let beginIndex = 0; + CubismJson.delete(json); - for (let i = 0; i < this._partGroupCounts.getSize(); ++i) { - const groupCount: number = this._partGroupCounts.at(i); - - for (let j: number = beginIndex; j < beginIndex + groupCount; ++j) { - this._partGroups.at(j).initialize(model); - - const partsIndex: number = this._partGroups.at(j).partIndex; - const paramIndex: number = this._partGroups.at(j).parameterIndex; - - if (partsIndex < 0) { - continue; - } - - model.setPartOpacityByIndex(partsIndex, j == beginIndex ? 1.0 : 0.0); - model.setParameterValueByIndex( - paramIndex, - j == beginIndex ? 1.0 : 0.0 - ); - - for (let k = 0; k < this._partGroups.at(j).link.getSize(); ++k) { - this._partGroups - .at(j) - .link.at(k) - .initialize(model); - } - } - - beginIndex += groupCount; - } - } - - /** - * パーツの不透明度をコピー - * - * @param model 対象のモデル - */ - public copyPartOpacities(model: CubismModel): void { - for ( - let groupIndex = 0; - groupIndex < this._partGroups.getSize(); - ++groupIndex - ) { - const partData: PartData = this._partGroups.at(groupIndex); - - if (partData.link.getSize() == 0) { - continue; // 連動するパラメータはない - } - - const partIndex: number = this._partGroups.at(groupIndex).partIndex; - const opacity: number = model.getPartOpacityByIndex(partIndex); - - for ( - let linkIndex = 0; - linkIndex < partData.link.getSize(); - ++linkIndex - ) { - const linkPart: PartData = partData.link.at(linkIndex); - const linkPartIndex: number = linkPart.partIndex; - - if (linkPartIndex < 0) { - continue; - } - - model.setPartOpacityByIndex(linkPartIndex, opacity); - } - } - } - - /** - * パーツのフェード操作を行う。 - * @param model 対象のモデル - * @param deltaTimeSeconds デルタ時間[秒] - * @param beginIndex フェード操作を行うパーツグループの先頭インデックス - * @param partGroupCount フェード操作を行うパーツグループの個数 - */ - public doFade( - model: CubismModel, - deltaTimeSeconds: number, - beginIndex: number, - partGroupCount: number - ): void { - let visiblePartIndex = -1; - let newOpacity = 1.0; - - const phi = 0.5; - const backOpacityThreshold = 0.15; - - // 現在、表示状態になっているパーツを取得 - for (let i: number = beginIndex; i < beginIndex + partGroupCount; ++i) { - const partIndex: number = this._partGroups.at(i).partIndex; - const paramIndex: number = this._partGroups.at(i).parameterIndex; - - if (model.getParameterValueByIndex(paramIndex) > Epsilon) { - if (visiblePartIndex >= 0) { - break; - } - - visiblePartIndex = i; - newOpacity = model.getPartOpacityByIndex(partIndex); - - // 新しい不透明度を計算 - newOpacity += deltaTimeSeconds / this._fadeTimeSeconds; - - if (newOpacity > 1.0) { - newOpacity = 1.0; - } - } - } - - if (visiblePartIndex < 0) { - visiblePartIndex = 0; - newOpacity = 1.0; - } - - // 表示パーツ、非表示パーツの不透明度を設定する - for (let i: number = beginIndex; i < beginIndex + partGroupCount; ++i) { - const partsIndex: number = this._partGroups.at(i).partIndex; - - // 表示パーツの設定 - if (visiblePartIndex == i) { - model.setPartOpacityByIndex(partsIndex, newOpacity); // 先に設定 - } - // 非表示パーツの設定 - else { - let opacity: number = model.getPartOpacityByIndex(partsIndex); - let a1: number; // 計算によって求められる不透明度 - - if (newOpacity < phi) { - a1 = (newOpacity * (phi - 1)) / phi + 1.0; // (0,1),(phi,phi)を通る直線式 - } else { - a1 = ((1 - newOpacity) * phi) / (1.0 - phi); // (1,0),(phi,phi)を通る直線式 - } - - // 背景の見える割合を制限する場合 - const backOpacity: number = (1.0 - a1) * (1.0 - newOpacity); - - if (backOpacity > backOpacityThreshold) { - a1 = 1.0 - backOpacityThreshold / (1.0 - newOpacity); - } - - if (opacity > a1) { - opacity = a1; // 計算の不透明度よりも大きければ(濃ければ)不透明度を上げる - } - - model.setPartOpacityByIndex(partsIndex, opacity); - } - } - } - - /** - * コンストラクタ - */ - public constructor() { - this._fadeTimeSeconds = DefaultFadeInSeconds; - this._lastModel = null; - this._partGroups = new csmVector(); - this._partGroupCounts = new csmVector(); - } - - _partGroups: csmVector; // パーツグループ - _partGroupCounts: csmVector; // それぞれのパーツグループの個数 - _fadeTimeSeconds: number; // フェード時間[秒] - _lastModel: CubismModel; // 前回操作したモデル + return ret; } /** - * パーツにまつわるデータを管理 + * インスタンスを破棄する + * @param pose 対象のCubismPose */ - export class PartData { - /** - * コンストラクタ - */ - constructor(v?: PartData) { - this.parameterIndex = 0; - this.partIndex = 0; - this.link = new csmVector(); + public static delete(pose: CubismPose): void { + if (pose != null) { + pose = null; + } + } - if (v != undefined) { - this.partId = v.partId; + /** + * モデルのパラメータの更新 + * @param model 対象のモデル + * @param deltaTimeSeconds デルタ時間[秒] + */ + public updateParameters(model: CubismModel, deltaTimeSeconds: number): void { + // 前回のモデルと同じでない場合は初期化が必要 + if (model != this._lastModel) { + // パラメータインデックスの初期化 + this.reset(model); + } - for ( - const ite: iterator = v.link.begin(); - ite.notEqual(v.link.end()); - ite.preIncrement() - ) { - this.link.pushBack(ite.ptr().clone()); + this._lastModel = model; + + // 設定から時間を変更すると、経過時間がマイナスになる事があるので、経過時間0として対応 + if (deltaTimeSeconds < 0.0) { + deltaTimeSeconds = 0.0; + } + + let beginIndex = 0; + + for (let i = 0; i < this._partGroupCounts.getSize(); i++) { + const partGroupCount: number = this._partGroupCounts.at(i); + + this.doFade(model, deltaTimeSeconds, beginIndex, partGroupCount); + + beginIndex += partGroupCount; + } + + this.copyPartOpacities(model); + } + + /** + * 表示を初期化 + * @param model 対象のモデル + * @note 不透明度の初期値が0でないパラメータは、不透明度を1に設定する + */ + public reset(model: CubismModel): void { + let beginIndex = 0; + + for (let i = 0; i < this._partGroupCounts.getSize(); ++i) { + const groupCount: number = this._partGroupCounts.at(i); + + for (let j: number = beginIndex; j < beginIndex + groupCount; ++j) { + this._partGroups.at(j).initialize(model); + + const partsIndex: number = this._partGroups.at(j).partIndex; + const paramIndex: number = this._partGroups.at(j).parameterIndex; + + if (partsIndex < 0) { + continue; + } + + model.setPartOpacityByIndex(partsIndex, j == beginIndex ? 1.0 : 0.0); + model.setParameterValueByIndex(paramIndex, j == beginIndex ? 1.0 : 0.0); + + for (let k = 0; k < this._partGroups.at(j).link.getSize(); ++k) { + this._partGroups + .at(j) + .link.at(k) + .initialize(model); + } + } + + beginIndex += groupCount; + } + } + + /** + * パーツの不透明度をコピー + * + * @param model 対象のモデル + */ + public copyPartOpacities(model: CubismModel): void { + for ( + let groupIndex = 0; + groupIndex < this._partGroups.getSize(); + ++groupIndex + ) { + const partData: PartData = this._partGroups.at(groupIndex); + + if (partData.link.getSize() == 0) { + continue; // 連動するパラメータはない + } + + const partIndex: number = this._partGroups.at(groupIndex).partIndex; + const opacity: number = model.getPartOpacityByIndex(partIndex); + + for ( + let linkIndex = 0; + linkIndex < partData.link.getSize(); + ++linkIndex + ) { + const linkPart: PartData = partData.link.at(linkIndex); + const linkPartIndex: number = linkPart.partIndex; + + if (linkPartIndex < 0) { + continue; + } + + model.setPartOpacityByIndex(linkPartIndex, opacity); + } + } + } + + /** + * パーツのフェード操作を行う。 + * @param model 対象のモデル + * @param deltaTimeSeconds デルタ時間[秒] + * @param beginIndex フェード操作を行うパーツグループの先頭インデックス + * @param partGroupCount フェード操作を行うパーツグループの個数 + */ + public doFade( + model: CubismModel, + deltaTimeSeconds: number, + beginIndex: number, + partGroupCount: number + ): void { + let visiblePartIndex = -1; + let newOpacity = 1.0; + + const phi = 0.5; + const backOpacityThreshold = 0.15; + + // 現在、表示状態になっているパーツを取得 + for (let i: number = beginIndex; i < beginIndex + partGroupCount; ++i) { + const partIndex: number = this._partGroups.at(i).partIndex; + const paramIndex: number = this._partGroups.at(i).parameterIndex; + + if (model.getParameterValueByIndex(paramIndex) > Epsilon) { + if (visiblePartIndex >= 0) { + break; + } + + visiblePartIndex = i; + newOpacity = model.getPartOpacityByIndex(partIndex); + + // 新しい不透明度を計算 + newOpacity += deltaTimeSeconds / this._fadeTimeSeconds; + + if (newOpacity > 1.0) { + newOpacity = 1.0; } } } - /** - * =演算子のオーバーロード - */ - public assignment(v: PartData): PartData { + if (visiblePartIndex < 0) { + visiblePartIndex = 0; + newOpacity = 1.0; + } + + // 表示パーツ、非表示パーツの不透明度を設定する + for (let i: number = beginIndex; i < beginIndex + partGroupCount; ++i) { + const partsIndex: number = this._partGroups.at(i).partIndex; + + // 表示パーツの設定 + if (visiblePartIndex == i) { + model.setPartOpacityByIndex(partsIndex, newOpacity); // 先に設定 + } + // 非表示パーツの設定 + else { + let opacity: number = model.getPartOpacityByIndex(partsIndex); + let a1: number; // 計算によって求められる不透明度 + + if (newOpacity < phi) { + a1 = (newOpacity * (phi - 1)) / phi + 1.0; // (0,1),(phi,phi)を通る直線式 + } else { + a1 = ((1 - newOpacity) * phi) / (1.0 - phi); // (1,0),(phi,phi)を通る直線式 + } + + // 背景の見える割合を制限する場合 + const backOpacity: number = (1.0 - a1) * (1.0 - newOpacity); + + if (backOpacity > backOpacityThreshold) { + a1 = 1.0 - backOpacityThreshold / (1.0 - newOpacity); + } + + if (opacity > a1) { + opacity = a1; // 計算の不透明度よりも大きければ(濃ければ)不透明度を上げる + } + + model.setPartOpacityByIndex(partsIndex, opacity); + } + } + } + + /** + * コンストラクタ + */ + public constructor() { + this._fadeTimeSeconds = DefaultFadeInSeconds; + this._lastModel = null; + this._partGroups = new csmVector(); + this._partGroupCounts = new csmVector(); + } + + _partGroups: csmVector; // パーツグループ + _partGroupCounts: csmVector; // それぞれのパーツグループの個数 + _fadeTimeSeconds: number; // フェード時間[秒] + _lastModel: CubismModel; // 前回操作したモデル +} + +/** + * パーツにまつわるデータを管理 + */ +export class PartData { + /** + * コンストラクタ + */ + constructor(v?: PartData) { + this.parameterIndex = 0; + this.partIndex = 0; + this.link = new csmVector(); + + if (v != undefined) { this.partId = v.partId; for ( @@ -360,46 +330,71 @@ export namespace Live2DCubismFramework { ) { this.link.pushBack(ite.ptr().clone()); } - - return this; } - - /** - * 初期化 - * @param model 初期化に使用するモデル - */ - public initialize(model: CubismModel): void { - this.parameterIndex = model.getParameterIndex(this.partId); - this.partIndex = model.getPartIndex(this.partId); - - model.setParameterValueByIndex(this.parameterIndex, 1); - } - - /** - * オブジェクトのコピーを生成する - */ - public clone(): PartData { - const clonePartData: PartData = new PartData(); - - clonePartData.partId = this.partId; - clonePartData.parameterIndex = this.parameterIndex; - clonePartData.partIndex = this.partIndex; - clonePartData.link = new csmVector(); - - for ( - let ite: iterator = this.link.begin(); - ite.notEqual(this.link.end()); - ite.increment() - ) { - clonePartData.link.pushBack(ite.ptr().clone()); - } - - return clonePartData; - } - - partId: CubismIdHandle; // パーツID - parameterIndex: number; // パラメータのインデックス - partIndex: number; // パーツのインデックス - link: csmVector; // 連動するパラメータ } + + /** + * =演算子のオーバーロード + */ + public assignment(v: PartData): PartData { + this.partId = v.partId; + + for ( + const ite: iterator = v.link.begin(); + ite.notEqual(v.link.end()); + ite.preIncrement() + ) { + this.link.pushBack(ite.ptr().clone()); + } + + return this; + } + + /** + * 初期化 + * @param model 初期化に使用するモデル + */ + public initialize(model: CubismModel): void { + this.parameterIndex = model.getParameterIndex(this.partId); + this.partIndex = model.getPartIndex(this.partId); + + model.setParameterValueByIndex(this.parameterIndex, 1); + } + + /** + * オブジェクトのコピーを生成する + */ + public clone(): PartData { + const clonePartData: PartData = new PartData(); + + clonePartData.partId = this.partId; + clonePartData.parameterIndex = this.parameterIndex; + clonePartData.partIndex = this.partIndex; + clonePartData.link = new csmVector(); + + for ( + let ite: iterator = this.link.begin(); + ite.notEqual(this.link.end()); + ite.increment() + ) { + clonePartData.link.pushBack(ite.ptr().clone()); + } + + return clonePartData; + } + + partId: CubismIdHandle; // パーツID + parameterIndex: number; // パラメータのインデックス + partIndex: number; // パーツのインデックス + link: csmVector; // 連動するパラメータ +} + +// Namespace definition for compatibility. +import * as $ from './cubismpose'; +// eslint-disable-next-line @typescript-eslint/no-namespace +export namespace Live2DCubismFramework { + export const CubismPose = $.CubismPose; + export type CubismPose = $.CubismPose; + export const PartData = $.PartData; + export type PartData = $.PartData; } diff --git a/src/icubismallcator.ts b/src/icubismallcator.ts index 52b7740..5e10ace 100644 --- a/src/icubismallcator.ts +++ b/src/icubismallcator.ts @@ -5,41 +5,47 @@ * that can be found at https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html. */ -export namespace Live2DCubismFramework { +/** + * メモリアロケーションを抽象化したクラス + * + * メモリ確保・解放処理をプラットフォーム側で実装して + * フレームワークから呼び出すためのインターフェース + */ +export abstract class ICubismAllocator { /** - * メモリアロケーションを抽象化したクラス + * アラインメント制約なしのヒープ・メモリーを確保します * - * メモリ確保・解放処理をプラットフォーム側で実装して - * フレームワークから呼び出すためのインターフェース + * @param size 確保するバイト数 + * @return 成功すると割り当てられたメモリのアドレス。そうでなければ'0'を返す */ - export abstract class ICubismAllocator { - /** - * アラインメント制約なしのヒープ・メモリーを確保します - * - * @param size 確保するバイト数 - * @return 成功すると割り当てられたメモリのアドレス。そうでなければ'0'を返す - */ - public abstract allocate(size: number): any; + public abstract allocate(size: number): any; - /** - * アラインメント制約なしのヒープ・メモリーを解放します。 - * - * @param memory 解放するメモリのアドレス - */ - public abstract deallocate(memory: any): void; + /** + * アラインメント制約なしのヒープ・メモリーを解放します。 + * + * @param memory 解放するメモリのアドレス + */ + public abstract deallocate(memory: any): void; - /** - * アラインメント制約有のヒープ・メモリーを確保します。 - * @param size 確保するバイト数 - * @param alignment メモリーブロックのアラインメント幅 - * @return 成功すると割り当てられたメモリのアドレス。そうでなければ'0'を返す - */ - public abstract allocateAligned(size: number, alignment: number): any; + /** + * アラインメント制約有のヒープ・メモリーを確保します。 + * @param size 確保するバイト数 + * @param alignment メモリーブロックのアラインメント幅 + * @return 成功すると割り当てられたメモリのアドレス。そうでなければ'0'を返す + */ + public abstract allocateAligned(size: number, alignment: number): any; - /** - * アラインメント制約ありのヒープ・メモリーを解放します。 - * @param alignedMemory 解放するメモリのアドレス - */ - public abstract deallocateAligned(alignedMemory: any): void; - } + /** + * アラインメント制約ありのヒープ・メモリーを解放します。 + * @param alignedMemory 解放するメモリのアドレス + */ + public abstract deallocateAligned(alignedMemory: any): void; +} + +// Namespace definition for compatibility. +import * as $ from './icubismallcator'; +// eslint-disable-next-line @typescript-eslint/no-namespace +export namespace Live2DCubismFramework { + export const ICubismAllocator = $.ICubismAllocator; + export type ICubismAllocator = $.ICubismAllocator; } diff --git a/src/icubismmodelsetting.ts b/src/icubismmodelsetting.ts index 222b43c..9c92e1c 100644 --- a/src/icubismmodelsetting.ts +++ b/src/icubismmodelsetting.ts @@ -5,195 +5,199 @@ * that can be found at https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html. */ -import { Live2DCubismFramework as cubismid } from './id/cubismid'; -import { Live2DCubismFramework as csmmap } from './type/csmmap'; -import csmMap = csmmap.csmMap; -import CubismIdHandle = cubismid.CubismIdHandle; +import { CubismIdHandle } from './id/cubismid'; +import { csmMap } from './type/csmmap'; -export namespace Live2DCubismFramework { +/** + * モデル設定情報を取り扱う関数を宣言した純粋仮想クラス。 + * + * このクラスを継承することで、モデル設定情報を取り扱うクラスになる。 + */ +export abstract class ICubismModelSetting { /** - * モデル設定情報を取り扱う関数を宣言した純粋仮想クラス。 - * - * このクラスを継承することで、モデル設定情報を取り扱うクラスになる。 + * Mocファイルの名前を取得する + * @return Mocファイルの名前 */ - export abstract class ICubismModelSetting { - /** - * Mocファイルの名前を取得する - * @return Mocファイルの名前 - */ - public abstract getModelFileName(): string; + public abstract getModelFileName(): string; - /** - * モデルが使用するテクスチャの数を取得する - * テクスチャの数 - */ - public abstract getTextureCount(): number; + /** + * モデルが使用するテクスチャの数を取得する + * テクスチャの数 + */ + public abstract getTextureCount(): number; - /** - * テクスチャが配置されたディレクトリの名前を取得する - * @return テクスチャが配置されたディレクトリの名前 - */ - public abstract getTextureDirectory(): string; + /** + * テクスチャが配置されたディレクトリの名前を取得する + * @return テクスチャが配置されたディレクトリの名前 + */ + public abstract getTextureDirectory(): string; - /** - * モデルが使用するテクスチャの名前を取得する - * @param index 配列のインデックス値 - * @return テクスチャの名前 - */ - public abstract getTextureFileName(index: number): string; + /** + * モデルが使用するテクスチャの名前を取得する + * @param index 配列のインデックス値 + * @return テクスチャの名前 + */ + public abstract getTextureFileName(index: number): string; - /** - * モデルに設定された当たり判定の数を取得する - * @return モデルに設定された当たり判定の数 - */ - public abstract getHitAreasCount(): number; + /** + * モデルに設定された当たり判定の数を取得する + * @return モデルに設定された当たり判定の数 + */ + public abstract getHitAreasCount(): number; - /** - * 当たり判定に設定されたIDを取得する - * - * @param index 配列のindex - * @return 当たり判定に設定されたID - */ - public abstract getHitAreaId(index: number): CubismIdHandle; + /** + * 当たり判定に設定されたIDを取得する + * + * @param index 配列のindex + * @return 当たり判定に設定されたID + */ + public abstract getHitAreaId(index: number): CubismIdHandle; - /** - * 当たり判定に設定された名前を取得する - * @param index 配列のインデックス値 - * @return 当たり判定に設定された名前 - */ - public abstract getHitAreaName(index: number): string; + /** + * 当たり判定に設定された名前を取得する + * @param index 配列のインデックス値 + * @return 当たり判定に設定された名前 + */ + public abstract getHitAreaName(index: number): string; - /** - * 物理演算設定ファイルの名前を取得する - * @return 物理演算設定ファイルの名前 - */ - public abstract getPhysicsFileName(): string; + /** + * 物理演算設定ファイルの名前を取得する + * @return 物理演算設定ファイルの名前 + */ + public abstract getPhysicsFileName(): string; - /** - * パーツ切り替え設定ファイルの名前を取得する - * @return パーツ切り替え設定ファイルの名前 - */ - public abstract getPoseFileName(): string; + /** + * パーツ切り替え設定ファイルの名前を取得する + * @return パーツ切り替え設定ファイルの名前 + */ + public abstract getPoseFileName(): string; - /** - * 表情設定ファイルの数を取得する - * @return 表情設定ファイルの数 - */ - public abstract getExpressionCount(): number; + /** + * 表情設定ファイルの数を取得する + * @return 表情設定ファイルの数 + */ + public abstract getExpressionCount(): number; - /** - * 表情設定ファイルを識別する名前(別名)を取得する - * @param index 配列のインデックス値 - * @return 表情の名前 - */ - public abstract getExpressionName(index: number): string; + /** + * 表情設定ファイルを識別する名前(別名)を取得する + * @param index 配列のインデックス値 + * @return 表情の名前 + */ + public abstract getExpressionName(index: number): string; - /** - * 表情設定ファイルの名前を取得する - * @param index 配列のインデックス値 - * @return 表情設定ファイルの名前 - */ - public abstract getExpressionFileName(index: number): string; + /** + * 表情設定ファイルの名前を取得する + * @param index 配列のインデックス値 + * @return 表情設定ファイルの名前 + */ + public abstract getExpressionFileName(index: number): string; - /** - * モーショングループの数を取得する - * @return モーショングループの数 - */ - public abstract getMotionGroupCount(): number; + /** + * モーショングループの数を取得する + * @return モーショングループの数 + */ + public abstract getMotionGroupCount(): number; - /** - * モーショングループの名前を取得する - * @param index 配列のインデックス値 - * @return モーショングループの名前 - */ - public abstract getMotionGroupName(index: number): string; + /** + * モーショングループの名前を取得する + * @param index 配列のインデックス値 + * @return モーショングループの名前 + */ + public abstract getMotionGroupName(index: number): string; - /** - * モーショングループに含まれるモーションの数を取得する - * @param groupName モーショングループの名前 - * @return モーショングループの数 - */ - public abstract getMotionCount(groupName: string): number; + /** + * モーショングループに含まれるモーションの数を取得する + * @param groupName モーショングループの名前 + * @return モーショングループの数 + */ + public abstract getMotionCount(groupName: string): number; - /** - * グループ名とインデックス値からモーションファイル名を取得する - * @param groupName モーショングループの名前 - * @param index 配列のインデックス値 - * @return モーションファイルの名前 - */ - public abstract getMotionFileName(groupName: string, index: number): string; + /** + * グループ名とインデックス値からモーションファイル名を取得する + * @param groupName モーショングループの名前 + * @param index 配列のインデックス値 + * @return モーションファイルの名前 + */ + public abstract getMotionFileName(groupName: string, index: number): string; - /** - * モーションに対応するサウンドファイルの名前を取得する - * @param groupName モーショングループの名前 - * @param index 配列のインデックス値 - * @return サウンドファイルの名前 - */ - public abstract getMotionSoundFileName( - groupName: string, - index: number - ): string; + /** + * モーションに対応するサウンドファイルの名前を取得する + * @param groupName モーショングループの名前 + * @param index 配列のインデックス値 + * @return サウンドファイルの名前 + */ + public abstract getMotionSoundFileName( + groupName: string, + index: number + ): string; - /** - * モーション開始時のフェードイン処理時間を取得する - * @param groupName モーショングループの名前 - * @param index 配列のインデックス値 - * @return フェードイン処理時間[秒] - */ - public abstract getMotionFadeInTimeValue( - groupName: string, - index: number - ): number; + /** + * モーション開始時のフェードイン処理時間を取得する + * @param groupName モーショングループの名前 + * @param index 配列のインデックス値 + * @return フェードイン処理時間[秒] + */ + public abstract getMotionFadeInTimeValue( + groupName: string, + index: number + ): number; - /** - * モーション終了時のフェードアウト処理時間を取得する - * @param groupName モーショングループの名前 - * @param index 配列のインデックス値 - * @return フェードアウト処理時間[秒] - */ - public abstract getMotionFadeOutTimeValue( - groupName: string, - index: number - ): number; + /** + * モーション終了時のフェードアウト処理時間を取得する + * @param groupName モーショングループの名前 + * @param index 配列のインデックス値 + * @return フェードアウト処理時間[秒] + */ + public abstract getMotionFadeOutTimeValue( + groupName: string, + index: number + ): number; - /** - * ユーザーデータのファイル名を取得する - * @return ユーザーデータのファイル名 - */ - public abstract getUserDataFile(): string; + /** + * ユーザーデータのファイル名を取得する + * @return ユーザーデータのファイル名 + */ + public abstract getUserDataFile(): string; - /** - * レイアウト情報を取得する - * @param outLayoutMap csmMapクラスのインスタンス - * @return true レイアウト情報が存在する - * @return false レイアウト情報が存在しない - */ - public abstract getLayoutMap(outLayoutMap: csmMap): boolean; + /** + * レイアウト情報を取得する + * @param outLayoutMap csmMapクラスのインスタンス + * @return true レイアウト情報が存在する + * @return false レイアウト情報が存在しない + */ + public abstract getLayoutMap(outLayoutMap: csmMap): boolean; - /** - * 目パチに関連付けられたパラメータの数を取得する - * @return 目パチに関連付けられたパラメータの数 - */ - public abstract getEyeBlinkParameterCount(): number; + /** + * 目パチに関連付けられたパラメータの数を取得する + * @return 目パチに関連付けられたパラメータの数 + */ + public abstract getEyeBlinkParameterCount(): number; - /** - * 目パチに関連付けられたパラメータのIDを取得する - * @param index 配列のインデックス値 - * @return パラメータID - */ - public abstract getEyeBlinkParameterId(index: number): CubismIdHandle; + /** + * 目パチに関連付けられたパラメータのIDを取得する + * @param index 配列のインデックス値 + * @return パラメータID + */ + public abstract getEyeBlinkParameterId(index: number): CubismIdHandle; - /** - * リップシンクに関連付けられたパラメータの数を取得する - * @return リップシンクに関連付けられたパラメータの数 - */ - public abstract getLipSyncParameterCount(): number; + /** + * リップシンクに関連付けられたパラメータの数を取得する + * @return リップシンクに関連付けられたパラメータの数 + */ + public abstract getLipSyncParameterCount(): number; - /** - * リップシンクに関連付けられたパラメータの数を取得する - * @param index 配列のインデックス値 - * @return パラメータID - */ - public abstract getLipSyncParameterId(index: number): CubismIdHandle; - } + /** + * リップシンクに関連付けられたパラメータの数を取得する + * @param index 配列のインデックス値 + * @return パラメータID + */ + public abstract getLipSyncParameterId(index: number): CubismIdHandle; +} + +// Namespace definition for compatibility. +import * as $ from './icubismmodelsetting'; +// eslint-disable-next-line @typescript-eslint/no-namespace +export namespace Live2DCubismFramework { + export const ICubismModelSetting = $.ICubismModelSetting; + export type ICubismModelSetting = $.ICubismModelSetting; } diff --git a/src/id/cubismid.ts b/src/id/cubismid.ts index 5c8c9eb..2d72b0c 100644 --- a/src/id/cubismid.ts +++ b/src/id/cubismid.ts @@ -5,69 +5,75 @@ * that can be found at https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html. */ -import { Live2DCubismFramework as csmstring } from '../type/csmstring'; -import csmString = csmstring.csmString; +import { csmString } from '../type/csmstring'; -export namespace Live2DCubismFramework { +/** + * パラメータ名・パーツ名・Drawable名を保持 + * + * パラメータ名・パーツ名・Drawable名を保持するクラス。 + */ +export class CubismId { /** - * パラメータ名・パーツ名・Drawable名を保持 - * - * パラメータ名・パーツ名・Drawable名を保持するクラス。 + * ID名を取得する */ - export class CubismId { - /** - * ID名を取得する - */ - public getString(): csmString { - return this._id; - } - - /** - * コンストラクタ - */ - public constructor(id: string | csmString) { - if (typeof id === 'string') { - this._id = new csmString(id); - return; - } - - this._id = id; - } - - /** - * idを比較 - * @param c 比較するid - * @return 同じならばtrue,異なっていればfalseを返す - */ - public isEqual(c: string | csmString | CubismId): boolean { - if (typeof c === 'string') { - return this._id.isEqual(c); - } else if (c instanceof csmString) { - return this._id.isEqual(c.s); - } else if (c instanceof CubismId) { - return this._id.isEqual(c._id.s); - } - return false; - } - - /** - * idを比較 - * @param c 比較するid - * @return 同じならばtrue,異なっていればfalseを返す - */ - public isNotEqual(c: string | csmString | CubismId): boolean { - if (typeof c == 'string') { - return !this._id.isEqual(c); - } else if (c instanceof csmString) { - return !this._id.isEqual(c.s); - } else if (c instanceof CubismId) { - return !this._id.isEqual(c._id.s); - } - return false; - } - - private _id: csmString; // ID名 + public getString(): csmString { + return this._id; } - export declare type CubismIdHandle = CubismId; + /** + * コンストラクタ + */ + public constructor(id: string | csmString) { + if (typeof id === 'string') { + this._id = new csmString(id); + return; + } + + this._id = id; + } + + /** + * idを比較 + * @param c 比較するid + * @return 同じならばtrue,異なっていればfalseを返す + */ + public isEqual(c: string | csmString | CubismId): boolean { + if (typeof c === 'string') { + return this._id.isEqual(c); + } else if (c instanceof csmString) { + return this._id.isEqual(c.s); + } else if (c instanceof CubismId) { + return this._id.isEqual(c._id.s); + } + return false; + } + + /** + * idを比較 + * @param c 比較するid + * @return 同じならばtrue,異なっていればfalseを返す + */ + public isNotEqual(c: string | csmString | CubismId): boolean { + if (typeof c == 'string') { + return !this._id.isEqual(c); + } else if (c instanceof csmString) { + return !this._id.isEqual(c.s); + } else if (c instanceof CubismId) { + return !this._id.isEqual(c._id.s); + } + return false; + } + + private _id: csmString; // ID名 +} + +export declare type CubismIdHandle = CubismId; + +// Namespace definition for compatibility. +import * as $ from './cubismid'; +// eslint-disable-next-line @typescript-eslint/no-namespace +export namespace Live2DCubismFramework { + export const CubismId = $.CubismId; + export type CubismId = $.CubismId; + export type CubismIdHandle = $.CubismIdHandle; } diff --git a/src/id/cubismidmanager.ts b/src/id/cubismidmanager.ts index 04d5c66..4f8ecde 100644 --- a/src/id/cubismidmanager.ts +++ b/src/id/cubismidmanager.ts @@ -5,114 +5,117 @@ * that can be found at https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html. */ -import { Live2DCubismFramework as csmvector } from '../type/csmvector'; -import { Live2DCubismFramework as cubismid } from './cubismid'; -import { Live2DCubismFramework as csmstring } from '../type/csmstring'; -import csmString = csmstring.csmString; -import CubismId = cubismid.CubismId; -import csmVector = csmvector.csmVector; +import { csmString } from '../type/csmstring'; +import { csmVector } from '../type/csmvector'; +import { CubismId } from './cubismid'; -export namespace Live2DCubismFramework { +/** + * ID名の管理 + * + * ID名を管理する。 + */ +export class CubismIdManager { /** - * ID名の管理 - * - * ID名を管理する。 + * コンストラクタ */ - export class CubismIdManager { - /** - * コンストラクタ - */ - public constructor() { - this._ids = new csmVector(); - } - - /** - * デストラクタ相当の処理 - */ - public release(): void { - for (let i = 0; i < this._ids.getSize(); ++i) { - this._ids.set(i, void 0); - } - this._ids = null; - } - - /** - * ID名をリストから登録 - * - * @param ids ID名リスト - * @param count IDの個数 - */ - public registerIds(ids: string[] | csmString[]): void { - for (let i = 0; i < ids.length; i++) { - this.registerId(ids[i]); - } - } - - /** - * ID名を登録 - * - * @param id ID名 - */ - public registerId(id: string | csmString): CubismId { - let result: CubismId = null; - - if ('string' == typeof id) { - if ((result = this.findId(id)) != null) { - return result; - } - - result = new CubismId(id); - this._ids.pushBack(result); - } else { - return this.registerId(id.s); - } - - return result; - } - - /** - * ID名からIDを取得する - * - * @param id ID名 - */ - public getId(id: csmString | string): CubismId { - return this.registerId(id); - } - - /** - * ID名からIDの確認 - * - * @return true 存在する - * @return false 存在しない - */ - public isExist(id: csmString | string): boolean { - if ('string' == typeof id) { - return this.findId(id) != null; - } - return this.isExist(id.s); - } - - /** - * ID名からIDを検索する。 - * - * @param id ID名 - * @return 登録されているID。なければNULL。 - */ - private findId(id: string): CubismId { - for (let i = 0; i < this._ids.getSize(); ++i) { - if ( - this._ids - .at(i) - .getString() - .isEqual(id) - ) { - return this._ids.at(i); - } - } - - return null; - } - - private _ids: csmVector; // 登録されているIDのリスト + public constructor() { + this._ids = new csmVector(); } + + /** + * デストラクタ相当の処理 + */ + public release(): void { + for (let i = 0; i < this._ids.getSize(); ++i) { + this._ids.set(i, void 0); + } + this._ids = null; + } + + /** + * ID名をリストから登録 + * + * @param ids ID名リスト + * @param count IDの個数 + */ + public registerIds(ids: string[] | csmString[]): void { + for (let i = 0; i < ids.length; i++) { + this.registerId(ids[i]); + } + } + + /** + * ID名を登録 + * + * @param id ID名 + */ + public registerId(id: string | csmString): CubismId { + let result: CubismId = null; + + if ('string' == typeof id) { + if ((result = this.findId(id)) != null) { + return result; + } + + result = new CubismId(id); + this._ids.pushBack(result); + } else { + return this.registerId(id.s); + } + + return result; + } + + /** + * ID名からIDを取得する + * + * @param id ID名 + */ + public getId(id: csmString | string): CubismId { + return this.registerId(id); + } + + /** + * ID名からIDの確認 + * + * @return true 存在する + * @return false 存在しない + */ + public isExist(id: csmString | string): boolean { + if ('string' == typeof id) { + return this.findId(id) != null; + } + return this.isExist(id.s); + } + + /** + * ID名からIDを検索する。 + * + * @param id ID名 + * @return 登録されているID。なければNULL。 + */ + private findId(id: string): CubismId { + for (let i = 0; i < this._ids.getSize(); ++i) { + if ( + this._ids + .at(i) + .getString() + .isEqual(id) + ) { + return this._ids.at(i); + } + } + + return null; + } + + private _ids: csmVector; // 登録されているIDのリスト +} + +// Namespace definition for compatibility. +import * as $ from './cubismidmanager'; +// eslint-disable-next-line @typescript-eslint/no-namespace +export namespace Live2DCubismFramework { + export const CubismIdManager = $.CubismIdManager; + export type CubismIdManager = $.CubismIdManager; } diff --git a/src/live2dcubismframework.ts b/src/live2dcubismframework.ts index a7a51b3..17dcefb 100644 --- a/src/live2dcubismframework.ts +++ b/src/live2dcubismframework.ts @@ -5,17 +5,14 @@ * that can be found at https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html. */ -import { Live2DCubismFramework as cubismjson } from './utils/cubismjson'; -import { Live2DCubismFramework as cubismidmanager } from './id/cubismidmanager'; -import { Live2DCubismFramework as cubismrenderer } from './rendering/cubismrenderer'; +import { CubismIdManager } from './id/cubismidmanager'; +import { CubismRenderer } from './rendering/cubismrenderer'; import { + CSM_ASSERT, CubismLogInfo, - CubismLogWarning, - CSM_ASSERT + CubismLogWarning } from './utils/cubismdebug'; -import Value = cubismjson.Value; -import CubismIdManager = cubismidmanager.CubismIdManager; -import CubismRenderer = cubismrenderer.CubismRenderer; +import { Value } from './utils/cubismjson'; export function strtod(s: string, endPtr: string[]): number { let index = 0; @@ -47,211 +44,209 @@ export function strtod(s: string, endPtr: string[]): number { return d; } -export namespace Live2DCubismFramework { - // ファイルスコープの変数を初期化 +// ファイルスコープの変数を初期化 - let s_isStarted = false; - let s_isInitialized = false; - let s_option: Option = null; - let s_cubismIdManager: CubismIdManager = null; +let s_isStarted = false; +let s_isInitialized = false; +let s_option: Option = null; +let s_cubismIdManager: CubismIdManager = null; - /** - * Framework内で使う定数の宣言 - */ - export namespace Constant { - export const vertexOffset = 0; // メッシュ頂点のオフセット値 - export const vertexStep = 2; // メッシュ頂点のステップ値 +/** + * Framework内で使う定数の宣言 + */ +export const Constant = Object.freeze>({ + vertexOffset: 0, // メッシュ頂点のオフセット値 + vertexStep: 2 // メッシュ頂点のステップ値 +}); + +export function csmDelete(address: T): void { + if (!address) { + return; } - export function csmDelete(address: T): void { - if (!address) { + address = void 0; +} + +/** + * Live2D Cubism SDK Original Workflow SDKのエントリポイント + * 利用開始時はCubismFramework.initialize()を呼び、CubismFramework.dispose()で終了する。 + */ +export class CubismFramework { + /** + * Cubism FrameworkのAPIを使用可能にする。 + * APIを実行する前に必ずこの関数を実行すること。 + * 一度準備が完了して以降は、再び実行しても内部処理がスキップされます。 + * + * @param option Optionクラスのインスタンス + * + * @return 準備処理が完了したらtrueが返ります。 + */ + public static startUp(option: Option = null): boolean { + if (s_isStarted) { + CubismLogInfo('CubismFramework.startUp() is already done.'); + return s_isStarted; + } + + s_option = option; + + if (s_option != null) { + Live2DCubismCore.Logging.csmSetLogFunction(s_option.logFunction); + } + + s_isStarted = true; + + // Live2D Cubism Coreバージョン情報を表示 + if (s_isStarted) { + const version: number = Live2DCubismCore.Version.csmGetVersion(); + const major: number = (version & 0xff000000) >> 24; + const minor: number = (version & 0x00ff0000) >> 16; + const patch: number = version & 0x0000ffff; + const versionNumber: number = version; + + CubismLogInfo( + `Live2D Cubism Core version: {0}.{1}.{2} ({3})`, + ('00' + major).slice(-2), + ('00' + minor).slice(-2), + ('0000' + patch).slice(-4), + versionNumber + ); + } + + CubismLogInfo('CubismFramework.startUp() is complete.'); + + return s_isStarted; + } + + /** + * StartUp()で初期化したCubismFrameworkの各パラメータをクリアします。 + * Dispose()したCubismFrameworkを再利用する際に利用してください。 + */ + public static cleanUp(): void { + s_isStarted = false; + s_isInitialized = false; + s_option = null; + s_cubismIdManager = null; + } + + /** + * Cubism Framework内のリソースを初期化してモデルを表示可能な状態にします。
+ * 再度Initialize()するには先にDispose()を実行する必要があります。 + */ + public static initialize(): void { + CSM_ASSERT(s_isStarted); + if (!s_isStarted) { + CubismLogWarning('CubismFramework is not started.'); return; } - address = void 0; + // --- s_isInitializedによる連続初期化ガード --- + // 連続してリソース確保が行われないようにする。 + // 再度Initialize()するには先にDispose()を実行する必要がある。 + if (s_isInitialized) { + CubismLogWarning( + 'CubismFramework.initialize() skipped, already initialized.' + ); + return; + } + + //---- static 初期化 ---- + Value.staticInitializeNotForClientCall(); + + s_cubismIdManager = new CubismIdManager(); + + s_isInitialized = true; + + CubismLogInfo('CubismFramework.initialize() is complete.'); } /** - * Live2D Cubism SDK Original Workflow SDKのエントリポイント - * 利用開始時はCubismFramework.initialize()を呼び、CubismFramework.dispose()で終了する。 + * Cubism Framework内の全てのリソースを解放します。 + * ただし、外部で確保されたリソースについては解放しません。 + * 外部で適切に破棄する必要があります。 */ - export class CubismFramework { - /** - * Cubism FrameworkのAPIを使用可能にする。 - * APIを実行する前に必ずこの関数を実行すること。 - * 一度準備が完了して以降は、再び実行しても内部処理がスキップされます。 - * - * @param option Optionクラスのインスタンス - * - * @return 準備処理が完了したらtrueが返ります。 - */ - public static startUp(option: Option = null): boolean { - if (s_isStarted) { - CubismLogInfo('CubismFramework.startUp() is already done.'); - return s_isStarted; - } - - s_option = option; - - if (s_option != null) { - Live2DCubismCore.Logging.csmSetLogFunction(s_option.logFunction); - } - - s_isStarted = true; - - // Live2D Cubism Coreバージョン情報を表示 - if (s_isStarted) { - const version: number = Live2DCubismCore.Version.csmGetVersion(); - const major: number = (version & 0xff000000) >> 24; - const minor: number = (version & 0x00ff0000) >> 16; - const patch: number = version & 0x0000ffff; - const versionNumber: number = version; - - CubismLogInfo( - `Live2D Cubism Core version: {0}.{1}.{2} ({3})`, - ('00' + major).slice(-2), - ('00' + minor).slice(-2), - ('0000' + patch).slice(-4), - versionNumber - ); - } - - CubismLogInfo('CubismFramework.startUp() is complete.'); - - return s_isStarted; + public static dispose(): void { + CSM_ASSERT(s_isStarted); + if (!s_isStarted) { + CubismLogWarning('CubismFramework is not started.'); + return; } - /** - * StartUp()で初期化したCubismFrameworkの各パラメータをクリアします。 - * Dispose()したCubismFrameworkを再利用する際に利用してください。 - */ - public static cleanUp(): void { - s_isStarted = false; - s_isInitialized = false; - s_option = null; - s_cubismIdManager = null; + // --- s_isInitializedによる未初期化解放ガード --- + // dispose()するには先にinitialize()を実行する必要がある。 + if (!s_isInitialized) { + // false...リソース未確保の場合 + CubismLogWarning('CubismFramework.dispose() skipped, not initialized.'); + return; } - /** - * Cubism Framework内のリソースを初期化してモデルを表示可能な状態にします。
- * 再度Initialize()するには先にDispose()を実行する必要があります。 - */ - public static initialize(): void { - CSM_ASSERT(s_isStarted); - if (!s_isStarted) { - CubismLogWarning('CubismFramework is not started.'); - return; - } + Value.staticReleaseNotForClientCall(); - // --- s_isInitializedによる連続初期化ガード --- - // 連続してリソース確保が行われないようにする。 - // 再度Initialize()するには先にDispose()を実行する必要がある。 - if (s_isInitialized) { - CubismLogWarning( - 'CubismFramework.initialize() skipped, already initialized.' - ); - return; - } + s_cubismIdManager.release(); + s_cubismIdManager = null; - //---- static 初期化 ---- - Value.staticInitializeNotForClientCall(); + // レンダラの静的リソース(シェーダプログラム他)を解放する + CubismRenderer.staticRelease(); - s_cubismIdManager = new CubismIdManager(); + s_isInitialized = false; - s_isInitialized = true; - - CubismLogInfo('CubismFramework.initialize() is complete.'); - } - - /** - * Cubism Framework内の全てのリソースを解放します。 - * ただし、外部で確保されたリソースについては解放しません。 - * 外部で適切に破棄する必要があります。 - */ - public static dispose(): void { - CSM_ASSERT(s_isStarted); - if (!s_isStarted) { - CubismLogWarning('CubismFramework is not started.'); - return; - } - - // --- s_isInitializedによる未初期化解放ガード --- - // dispose()するには先にinitialize()を実行する必要がある。 - if (!s_isInitialized) { - // false...リソース未確保の場合 - CubismLogWarning('CubismFramework.dispose() skipped, not initialized.'); - return; - } - - Value.staticReleaseNotForClientCall(); - - s_cubismIdManager.release(); - s_cubismIdManager = null; - - // レンダラの静的リソース(シェーダプログラム他)を解放する - CubismRenderer.staticRelease(); - - s_isInitialized = false; - - CubismLogInfo('CubismFramework.dispose() is complete.'); - } - - /** - * Cubism FrameworkのAPIを使用する準備が完了したかどうか - * @return APIを使用する準備が完了していればtrueが返ります。 - */ - public static isStarted(): boolean { - return s_isStarted; - } - - /** - * Cubism Frameworkのリソース初期化がすでに行われているかどうか - * @return リソース確保が完了していればtrueが返ります - */ - public static isInitialized(): boolean { - return s_isInitialized; - } - - /** - * Core APIにバインドしたログ関数を実行する - * - * @praram message ログメッセージ - */ - public static coreLogFunction(message: string): void { - // Return if logging not possible. - if (!Live2DCubismCore.Logging.csmGetLogFunction()) { - return; - } - - Live2DCubismCore.Logging.csmGetLogFunction()(message); - } - - /** - * 現在のログ出力レベル設定の値を返す。 - * - * @return 現在のログ出力レベル設定の値 - */ - public static getLoggingLevel(): LogLevel { - if (s_option != null) { - return s_option.loggingLevel; - } - return LogLevel.LogLevel_Off; - } - - /** - * IDマネージャのインスタンスを取得する - * @return CubismManagerクラスのインスタンス - */ - public static getIdManager(): CubismIdManager { - return s_cubismIdManager; - } - - /** - * 静的クラスとして使用する - * インスタンス化させない - */ - private constructor() {} + CubismLogInfo('CubismFramework.dispose() is complete.'); } + + /** + * Cubism FrameworkのAPIを使用する準備が完了したかどうか + * @return APIを使用する準備が完了していればtrueが返ります。 + */ + public static isStarted(): boolean { + return s_isStarted; + } + + /** + * Cubism Frameworkのリソース初期化がすでに行われているかどうか + * @return リソース確保が完了していればtrueが返ります + */ + public static isInitialized(): boolean { + return s_isInitialized; + } + + /** + * Core APIにバインドしたログ関数を実行する + * + * @praram message ログメッセージ + */ + public static coreLogFunction(message: string): void { + // Return if logging not possible. + if (!Live2DCubismCore.Logging.csmGetLogFunction()) { + return; + } + + Live2DCubismCore.Logging.csmGetLogFunction()(message); + } + + /** + * 現在のログ出力レベル設定の値を返す。 + * + * @return 現在のログ出力レベル設定の値 + */ + public static getLoggingLevel(): LogLevel { + if (s_option != null) { + return s_option.loggingLevel; + } + return LogLevel.LogLevel_Off; + } + + /** + * IDマネージャのインスタンスを取得する + * @return CubismManagerクラスのインスタンス + */ + public static getIdManager(): CubismIdManager { + return s_cubismIdManager; + } + + /** + * 静的クラスとして使用する + * インスタンス化させない + */ + private constructor() {} } export class Option { @@ -270,3 +265,13 @@ export enum LogLevel { LogLevel_Error, // エラーログ LogLevel_Off // ログ出力無効 } + +// Namespace definition for compatibility. +import * as $ from './live2dcubismframework'; +// eslint-disable-next-line @typescript-eslint/no-namespace +export namespace Live2DCubismFramework { + export const Constant = $.Constant; + export const csmDelete = $.csmDelete; + export const CubismFramework = $.CubismFramework; + export type CubismFramework = $.CubismFramework; +} diff --git a/src/math/cubismmath.ts b/src/math/cubismmath.ts index db806bd..704c311 100644 --- a/src/math/cubismmath.ts +++ b/src/math/cubismmath.ts @@ -5,191 +5,196 @@ * that can be found at https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html. */ -import { Live2DCubismFramework as cubismvector2 } from './cubismvector2'; -import CubismVector2 = cubismvector2.CubismVector2; +import { CubismVector2 } from './cubismvector2'; -export namespace Live2DCubismFramework { +/** + * 数値計算などに使用するユーティリティクラス + */ +export class CubismMath { /** - * 数値計算などに使用するユーティリティクラス + * 第一引数の値を最小値と最大値の範囲に収めた値を返す + * + * @param value 収められる値 + * @param min 範囲の最小値 + * @param max 範囲の最大値 + * @return 最小値と最大値の範囲に収めた値 */ - export class CubismMath { - /** - * 第一引数の値を最小値と最大値の範囲に収めた値を返す - * - * @param value 収められる値 - * @param min 範囲の最小値 - * @param max 範囲の最大値 - * @return 最小値と最大値の範囲に収めた値 - */ - static range(value: number, min: number, max: number): number { - if (value < min) { - value = min; - } else if (value > max) { - value = max; - } - - return value; + static range(value: number, min: number, max: number): number { + if (value < min) { + value = min; + } else if (value > max) { + value = max; } - /** - * サイン関数の値を求める - * - * @param x 角度値(ラジアン) - * @return サイン関数sin(x)の値 - */ - static sin(x: number): number { - return Math.sin(x); - } - - /** - * コサイン関数の値を求める - * - * @param x 角度値(ラジアン) - * @return コサイン関数cos(x)の値 - */ - static cos(x: number): number { - return Math.cos(x); - } - - /** - * 値の絶対値を求める - * - * @param x 絶対値を求める値 - * @return 値の絶対値 - */ - static abs(x: number): number { - return Math.abs(x); - } - - /** - * 平方根(ルート)を求める - * @param x -> 平方根を求める値 - * @return 値の平方根 - */ - static sqrt(x: number): number { - return Math.sqrt(x); - } - - /** - * イージング処理されたサインを求める - * フェードイン・アウト時のイージングに利用できる - * - * @param value イージングを行う値 - * @return イージング処理されたサイン値 - */ - static getEasingSine(value: number): number { - if (value < 0.0) { - return 0.0; - } else if (value > 1.0) { - return 1.0; - } - - return 0.5 - 0.5 * this.cos(value * Math.PI); - } - - /** - * 大きい方の値を返す - * - * @param left 左辺の値 - * @param right 右辺の値 - * @return 大きい方の値 - */ - static max(left: number, right: number): number { - return left > right ? left : right; - } - - /** - * 小さい方の値を返す - * - * @param left 左辺の値 - * @param right 右辺の値 - * @return 小さい方の値 - */ - static min(left: number, right: number): number { - return left > right ? right : left; - } - - /** - * 角度値をラジアン値に変換する - * - * @param degrees 角度値 - * @return 角度値から変換したラジアン値 - */ - static degreesToRadian(degrees: number): number { - return (degrees / 180.0) * Math.PI; - } - - /** - * ラジアン値を角度値に変換する - * - * @param radian ラジアン値 - * @return ラジアン値から変換した角度値 - */ - static radianToDegrees(radian: number): number { - return (radian * 180.0) / Math.PI; - } - - /** - * 2つのベクトルからラジアン値を求める - * - * @param from 始点ベクトル - * @param to 終点ベクトル - * @return ラジアン値から求めた方向ベクトル - */ - static directionToRadian(from: CubismVector2, to: CubismVector2): number { - const q1: number = Math.atan2(to.y, to.x); - const q2: number = Math.atan2(from.y, from.x); - - let ret: number = q1 - q2; - - while (ret < -Math.PI) { - ret += Math.PI * 2.0; - } - - while (ret > Math.PI) { - ret -= Math.PI * 2.0; - } - - return ret; - } - - /** - * 2つのベクトルから角度値を求める - * - * @param from 始点ベクトル - * @param to 終点ベクトル - * @return 角度値から求めた方向ベクトル - */ - static directionToDegrees(from: CubismVector2, to: CubismVector2): number { - const radian: number = this.directionToRadian(from, to); - let degree: number = this.radianToDegrees(radian); - - if (to.x - from.x > 0.0) { - degree = -degree; - } - - return degree; - } - - /** - * ラジアン値を方向ベクトルに変換する。 - * - * @param totalAngle ラジアン値 - * @return ラジアン値から変換した方向ベクトル - */ - - static radianToDirection(totalAngle: number): CubismVector2 { - const ret: CubismVector2 = new CubismVector2(); - - ret.x = this.sin(totalAngle); - ret.y = this.cos(totalAngle); - - return ret; - } - - /** - * コンストラクタ - */ - private constructor() {} + return value; } + + /** + * サイン関数の値を求める + * + * @param x 角度値(ラジアン) + * @return サイン関数sin(x)の値 + */ + static sin(x: number): number { + return Math.sin(x); + } + + /** + * コサイン関数の値を求める + * + * @param x 角度値(ラジアン) + * @return コサイン関数cos(x)の値 + */ + static cos(x: number): number { + return Math.cos(x); + } + + /** + * 値の絶対値を求める + * + * @param x 絶対値を求める値 + * @return 値の絶対値 + */ + static abs(x: number): number { + return Math.abs(x); + } + + /** + * 平方根(ルート)を求める + * @param x -> 平方根を求める値 + * @return 値の平方根 + */ + static sqrt(x: number): number { + return Math.sqrt(x); + } + + /** + * イージング処理されたサインを求める + * フェードイン・アウト時のイージングに利用できる + * + * @param value イージングを行う値 + * @return イージング処理されたサイン値 + */ + static getEasingSine(value: number): number { + if (value < 0.0) { + return 0.0; + } else if (value > 1.0) { + return 1.0; + } + + return 0.5 - 0.5 * this.cos(value * Math.PI); + } + + /** + * 大きい方の値を返す + * + * @param left 左辺の値 + * @param right 右辺の値 + * @return 大きい方の値 + */ + static max(left: number, right: number): number { + return left > right ? left : right; + } + + /** + * 小さい方の値を返す + * + * @param left 左辺の値 + * @param right 右辺の値 + * @return 小さい方の値 + */ + static min(left: number, right: number): number { + return left > right ? right : left; + } + + /** + * 角度値をラジアン値に変換する + * + * @param degrees 角度値 + * @return 角度値から変換したラジアン値 + */ + static degreesToRadian(degrees: number): number { + return (degrees / 180.0) * Math.PI; + } + + /** + * ラジアン値を角度値に変換する + * + * @param radian ラジアン値 + * @return ラジアン値から変換した角度値 + */ + static radianToDegrees(radian: number): number { + return (radian * 180.0) / Math.PI; + } + + /** + * 2つのベクトルからラジアン値を求める + * + * @param from 始点ベクトル + * @param to 終点ベクトル + * @return ラジアン値から求めた方向ベクトル + */ + static directionToRadian(from: CubismVector2, to: CubismVector2): number { + const q1: number = Math.atan2(to.y, to.x); + const q2: number = Math.atan2(from.y, from.x); + + let ret: number = q1 - q2; + + while (ret < -Math.PI) { + ret += Math.PI * 2.0; + } + + while (ret > Math.PI) { + ret -= Math.PI * 2.0; + } + + return ret; + } + + /** + * 2つのベクトルから角度値を求める + * + * @param from 始点ベクトル + * @param to 終点ベクトル + * @return 角度値から求めた方向ベクトル + */ + static directionToDegrees(from: CubismVector2, to: CubismVector2): number { + const radian: number = this.directionToRadian(from, to); + let degree: number = this.radianToDegrees(radian); + + if (to.x - from.x > 0.0) { + degree = -degree; + } + + return degree; + } + + /** + * ラジアン値を方向ベクトルに変換する。 + * + * @param totalAngle ラジアン値 + * @return ラジアン値から変換した方向ベクトル + */ + + static radianToDirection(totalAngle: number): CubismVector2 { + const ret: CubismVector2 = new CubismVector2(); + + ret.x = this.sin(totalAngle); + ret.y = this.cos(totalAngle); + + return ret; + } + + /** + * コンストラクタ + */ + private constructor() {} +} + +// Namespace definition for compatibility. +import * as $ from './cubismmath'; +// eslint-disable-next-line @typescript-eslint/no-namespace +export namespace Live2DCubismFramework { + export const CubismMath = $.CubismMath; + export type CubismMath = $.CubismMath; } diff --git a/src/math/cubismmatrix44.ts b/src/math/cubismmatrix44.ts index fe01733..eba595b 100644 --- a/src/math/cubismmatrix44.ts +++ b/src/math/cubismmatrix44.ts @@ -5,304 +5,310 @@ * that can be found at https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html. */ -export namespace Live2DCubismFramework { +/** + * 4x4の行列 + * + * 4x4行列の便利クラス。 + */ +export class CubismMatrix44 { /** - * 4x4の行列 - * - * 4x4行列の便利クラス。 + * コンストラクタ */ - export class CubismMatrix44 { - /** - * コンストラクタ - */ - public constructor() { - this._tr = new Float32Array(16); // 4 * 4のサイズ - this.loadIdentity(); - } + public constructor() { + this._tr = new Float32Array(16); // 4 * 4のサイズ + this.loadIdentity(); + } - /** - * 受け取った2つの行列の乗算を行う。 - * - * @param a 行列a - * @param b 行列b - * @return 乗算結果の行列 - */ - public static multiply( - a: Float32Array, - b: Float32Array, - dst: Float32Array - ): void { - const c: Float32Array = new Float32Array([ - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0 - ]); + /** + * 受け取った2つの行列の乗算を行う。 + * + * @param a 行列a + * @param b 行列b + * @return 乗算結果の行列 + */ + public static multiply( + a: Float32Array, + b: Float32Array, + dst: Float32Array + ): void { + const c: Float32Array = new Float32Array([ + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0 + ]); - const n = 4; + const n = 4; - for (let i = 0; i < n; ++i) { - for (let j = 0; j < n; ++j) { - for (let k = 0; k < n; ++k) { - c[j + i * 4] += a[k + i * 4] * b[j + k * 4]; - } + for (let i = 0; i < n; ++i) { + for (let j = 0; j < n; ++j) { + for (let k = 0; k < n; ++k) { + c[j + i * 4] += a[k + i * 4] * b[j + k * 4]; } } - - for (let i = 0; i < 16; ++i) { - dst[i] = c[i]; - } } - /** - * 単位行列に初期化する - */ - public loadIdentity(): void { - const c: Float32Array = new Float32Array([ - 1.0, - 0.0, - 0.0, - 0.0, - 0.0, - 1.0, - 0.0, - 0.0, - 0.0, - 0.0, - 1.0, - 0.0, - 0.0, - 0.0, - 0.0, - 1.0 - ]); - - this.setMatrix(c); + for (let i = 0; i < 16; ++i) { + dst[i] = c[i]; } - - /** - * 行列を設定 - * - * @param tr 16個の浮動小数点数で表される4x4の行列 - */ - public setMatrix(tr: Float32Array): void { - for (let i = 0; i < 16; ++i) { - this._tr[i] = tr[i]; - } - } - - /** - * 行列を浮動小数点数の配列で取得 - * - * @return 16個の浮動小数点数で表される4x4の行列 - */ - public getArray(): Float32Array { - return this._tr; - } - - /** - * X軸の拡大率を取得 - * @return X軸の拡大率 - */ - public getScaleX(): number { - return this._tr[0]; - } - - /** - * Y軸の拡大率を取得する - * - * @return Y軸の拡大率 - */ - public getScaleY(): number { - return this._tr[5]; - } - - /** - * X軸の移動量を取得 - * @return X軸の移動量 - */ - public getTranslateX(): number { - return this._tr[12]; - } - - /** - * Y軸の移動量を取得 - * @return Y軸の移動量 - */ - public getTranslateY(): number { - return this._tr[13]; - } - - /** - * X軸の値を現在の行列で計算 - * - * @param src X軸の値 - * @return 現在の行列で計算されたX軸の値 - */ - public transformX(src: number): number { - return this._tr[0] * src + this._tr[12]; - } - - /** - * Y軸の値を現在の行列で計算 - * - * @param src Y軸の値 - * @return 現在の行列で計算されたY軸の値 - */ - public transformY(src: number): number { - return this._tr[5] * src + this._tr[13]; - } - - /** - * X軸の値を現在の行列で逆計算 - */ - public invertTransformX(src: number): number { - return (src - this._tr[12]) / this._tr[0]; - } - - /** - * Y軸の値を現在の行列で逆計算 - */ - public invertTransformY(src: number): number { - return (src - this._tr[13]) / this._tr[5]; - } - - /** - * 現在の行列の位置を起点にして移動 - * - * 現在の行列の位置を起点にして相対的に移動する。 - * - * @param x X軸の移動量 - * @param y Y軸の移動量 - */ - public translateRelative(x: number, y: number): void { - const tr1: Float32Array = new Float32Array([ - 1.0, - 0.0, - 0.0, - 0.0, - 0.0, - 1.0, - 0.0, - 0.0, - 0.0, - 0.0, - 1.0, - 0.0, - x, - y, - 0.0, - 1.0 - ]); - - CubismMatrix44.multiply(tr1, this._tr, this._tr); - } - - /** - * 現在の行列の位置を移動 - * - * 現在の行列の位置を指定した位置へ移動する - * - * @param x X軸の移動量 - * @param y y軸の移動量 - */ - public translate(x: number, y: number): void { - this._tr[12] = x; - this._tr[13] = y; - } - - /** - * 現在の行列のX軸の位置を指定した位置へ移動する - * - * @param x X軸の移動量 - */ - public translateX(x: number): void { - this._tr[12] = x; - } - - /** - * 現在の行列のY軸の位置を指定した位置へ移動する - * - * @param y Y軸の移動量 - */ - public translateY(y: number): void { - this._tr[13] = y; - } - - /** - * 現在の行列の拡大率を相対的に設定する - * - * @param x X軸の拡大率 - * @param y Y軸の拡大率 - */ - public scaleRelative(x: number, y: number): void { - const tr1: Float32Array = new Float32Array([ - x, - 0.0, - 0.0, - 0.0, - 0.0, - y, - 0.0, - 0.0, - 0.0, - 0.0, - 1.0, - 0.0, - 0.0, - 0.0, - 0.0, - 1.0 - ]); - - CubismMatrix44.multiply(tr1, this._tr, this._tr); - } - - /** - * 現在の行列の拡大率を指定した倍率に設定する - * - * @param x X軸の拡大率 - * @param y Y軸の拡大率 - */ - public scale(x: number, y: number): void { - this._tr[0] = x; - this._tr[5] = y; - } - - /** - * 現在の行列に行列を乗算 - * - * @param m 行列 - */ - public multiplyByMatrix(m: CubismMatrix44): void { - CubismMatrix44.multiply(m.getArray(), this._tr, this._tr); - } - - /** - * オブジェクトのコピーを生成する - */ - public clone(): CubismMatrix44 { - const cloneMatrix: CubismMatrix44 = new CubismMatrix44(); - - for (let i = 0; i < this._tr.length; i++) { - cloneMatrix._tr[i] = this._tr[i]; - } - - return cloneMatrix; - } - - protected _tr: Float32Array; // 4x4行列データ } + + /** + * 単位行列に初期化する + */ + public loadIdentity(): void { + const c: Float32Array = new Float32Array([ + 1.0, + 0.0, + 0.0, + 0.0, + 0.0, + 1.0, + 0.0, + 0.0, + 0.0, + 0.0, + 1.0, + 0.0, + 0.0, + 0.0, + 0.0, + 1.0 + ]); + + this.setMatrix(c); + } + + /** + * 行列を設定 + * + * @param tr 16個の浮動小数点数で表される4x4の行列 + */ + public setMatrix(tr: Float32Array): void { + for (let i = 0; i < 16; ++i) { + this._tr[i] = tr[i]; + } + } + + /** + * 行列を浮動小数点数の配列で取得 + * + * @return 16個の浮動小数点数で表される4x4の行列 + */ + public getArray(): Float32Array { + return this._tr; + } + + /** + * X軸の拡大率を取得 + * @return X軸の拡大率 + */ + public getScaleX(): number { + return this._tr[0]; + } + + /** + * Y軸の拡大率を取得する + * + * @return Y軸の拡大率 + */ + public getScaleY(): number { + return this._tr[5]; + } + + /** + * X軸の移動量を取得 + * @return X軸の移動量 + */ + public getTranslateX(): number { + return this._tr[12]; + } + + /** + * Y軸の移動量を取得 + * @return Y軸の移動量 + */ + public getTranslateY(): number { + return this._tr[13]; + } + + /** + * X軸の値を現在の行列で計算 + * + * @param src X軸の値 + * @return 現在の行列で計算されたX軸の値 + */ + public transformX(src: number): number { + return this._tr[0] * src + this._tr[12]; + } + + /** + * Y軸の値を現在の行列で計算 + * + * @param src Y軸の値 + * @return 現在の行列で計算されたY軸の値 + */ + public transformY(src: number): number { + return this._tr[5] * src + this._tr[13]; + } + + /** + * X軸の値を現在の行列で逆計算 + */ + public invertTransformX(src: number): number { + return (src - this._tr[12]) / this._tr[0]; + } + + /** + * Y軸の値を現在の行列で逆計算 + */ + public invertTransformY(src: number): number { + return (src - this._tr[13]) / this._tr[5]; + } + + /** + * 現在の行列の位置を起点にして移動 + * + * 現在の行列の位置を起点にして相対的に移動する。 + * + * @param x X軸の移動量 + * @param y Y軸の移動量 + */ + public translateRelative(x: number, y: number): void { + const tr1: Float32Array = new Float32Array([ + 1.0, + 0.0, + 0.0, + 0.0, + 0.0, + 1.0, + 0.0, + 0.0, + 0.0, + 0.0, + 1.0, + 0.0, + x, + y, + 0.0, + 1.0 + ]); + + CubismMatrix44.multiply(tr1, this._tr, this._tr); + } + + /** + * 現在の行列の位置を移動 + * + * 現在の行列の位置を指定した位置へ移動する + * + * @param x X軸の移動量 + * @param y y軸の移動量 + */ + public translate(x: number, y: number): void { + this._tr[12] = x; + this._tr[13] = y; + } + + /** + * 現在の行列のX軸の位置を指定した位置へ移動する + * + * @param x X軸の移動量 + */ + public translateX(x: number): void { + this._tr[12] = x; + } + + /** + * 現在の行列のY軸の位置を指定した位置へ移動する + * + * @param y Y軸の移動量 + */ + public translateY(y: number): void { + this._tr[13] = y; + } + + /** + * 現在の行列の拡大率を相対的に設定する + * + * @param x X軸の拡大率 + * @param y Y軸の拡大率 + */ + public scaleRelative(x: number, y: number): void { + const tr1: Float32Array = new Float32Array([ + x, + 0.0, + 0.0, + 0.0, + 0.0, + y, + 0.0, + 0.0, + 0.0, + 0.0, + 1.0, + 0.0, + 0.0, + 0.0, + 0.0, + 1.0 + ]); + + CubismMatrix44.multiply(tr1, this._tr, this._tr); + } + + /** + * 現在の行列の拡大率を指定した倍率に設定する + * + * @param x X軸の拡大率 + * @param y Y軸の拡大率 + */ + public scale(x: number, y: number): void { + this._tr[0] = x; + this._tr[5] = y; + } + + /** + * 現在の行列に行列を乗算 + * + * @param m 行列 + */ + public multiplyByMatrix(m: CubismMatrix44): void { + CubismMatrix44.multiply(m.getArray(), this._tr, this._tr); + } + + /** + * オブジェクトのコピーを生成する + */ + public clone(): CubismMatrix44 { + const cloneMatrix: CubismMatrix44 = new CubismMatrix44(); + + for (let i = 0; i < this._tr.length; i++) { + cloneMatrix._tr[i] = this._tr[i]; + } + + return cloneMatrix; + } + + protected _tr: Float32Array; // 4x4行列データ +} + +// Namespace definition for compatibility. +import * as $ from './cubismmatrix44'; +// eslint-disable-next-line @typescript-eslint/no-namespace +export namespace Live2DCubismFramework { + export const CubismMatrix44 = $.CubismMatrix44; + export type CubismMatrix44 = $.CubismMatrix44; } diff --git a/src/math/cubismmodelmatrix.ts b/src/math/cubismmodelmatrix.ts index e654381..124f35e 100644 --- a/src/math/cubismmodelmatrix.ts +++ b/src/math/cubismmodelmatrix.ts @@ -5,219 +5,222 @@ * that can be found at https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html. */ -import { Live2DCubismFramework as csmmap } from '../type/csmmap'; -import { Live2DCubismFramework as cubismmatrix44 } from './cubismmatrix44'; -import csmMap = csmmap.csmMap; -import iterator = csmmap.iterator; -import CubismMatrix44 = cubismmatrix44.CubismMatrix44; +import { csmMap, iterator } from '../type/csmmap'; +import { CubismMatrix44 } from './cubismmatrix44'; -export namespace Live2DCubismFramework { +/** + * モデル座標設定用の4x4行列 + * + * モデル座標設定用の4x4行列クラス + */ +export class CubismModelMatrix extends CubismMatrix44 { /** - * モデル座標設定用の4x4行列 + * コンストラクタ * - * モデル座標設定用の4x4行列クラス + * @param w 横幅 + * @param h 縦幅 */ - export class CubismModelMatrix extends CubismMatrix44 { - /** - * コンストラクタ - * - * @param w 横幅 - * @param h 縦幅 - */ - constructor(w?: number, h?: number) { - super(); + constructor(w?: number, h?: number) { + super(); - this._width = w !== undefined ? w : 0.0; - this._height = h !== undefined ? h : 0.0; + this._width = w !== undefined ? w : 0.0; + this._height = h !== undefined ? h : 0.0; - this.setHeight(1.0); - } - - /** - * 横幅を設定 - * - * @param w 横幅 - */ - public setWidth(w: number): void { - const scaleX: number = w / this._width; - const scaleY: number = scaleX; - this.scale(scaleX, scaleY); - } - - /** - * 縦幅を設定 - * @param h 縦幅 - */ - public setHeight(h: number): void { - const scaleX: number = h / this._height; - const scaleY: number = scaleX; - this.scale(scaleX, scaleY); - } - - /** - * 位置を設定 - * - * @param x X軸の位置 - * @param y Y軸の位置 - */ - public setPosition(x: number, y: number): void { - this.translate(x, y); - } - - /** - * 中心位置を設定 - * - * @param x X軸の中心位置 - * @param y Y軸の中心位置 - * - * @note widthかheightを設定したあとでないと、拡大率が正しく取得できないためずれる。 - */ - public setCenterPosition(x: number, y: number) { - this.centerX(x); - this.centerY(y); - } - - /** - * 上辺の位置を設定する - * - * @param y 上辺のY軸位置 - */ - public top(y: number): void { - this.setY(y); - } - - /** - * 下辺の位置を設定する - * - * @param y 下辺のY軸位置 - */ - public bottom(y: number) { - const h: number = this._height * this.getScaleY(); - - this.translateY(y - h); - } - - /** - * 左辺の位置を設定 - * - * @param x 左辺のX軸位置 - */ - public left(x: number): void { - this.setX(x); - } - - /** - * 右辺の位置を設定 - * - * @param x 右辺のX軸位置 - */ - public right(x: number): void { - const w = this._width * this.getScaleX(); - - this.translateX(x - w); - } - - /** - * X軸の中心位置を設定 - * - * @param x X軸の中心位置 - */ - public centerX(x: number): void { - const w = this._width * this.getScaleX(); - - this.translateX(x - w / 2.0); - } - - /** - * X軸の位置を設定 - * - * @param x X軸の位置 - */ - public setX(x: number): void { - this.translateX(x); - } - - /** - * Y軸の中心位置を設定 - * - * @param y Y軸の中心位置 - */ - public centerY(y: number): void { - const h: number = this._height * this.getScaleY(); - - this.translateY(y - h / 2.0); - } - - /** - * Y軸の位置を設定する - * - * @param y Y軸の位置 - */ - public setY(y: number): void { - this.translateY(y); - } - - /** - * レイアウト情報から位置を設定 - * - * @param layout レイアウト情報 - */ - public setupFromLayout(layout: csmMap): void { - const keyWidth = 'width'; - const keyHeight = 'height'; - const keyX = 'x'; - const keyY = 'y'; - const keyCenterX = 'center_x'; - const keyCenterY = 'center_y'; - const keyTop = 'top'; - const keyBottom = 'bottom'; - const keyLeft = 'left'; - const keyRight = 'right'; - - for ( - const ite: iterator = layout.begin(); - ite.notEqual(layout.end()); - ite.preIncrement() - ) { - const key: string = ite.ptr().first; - const value: number = ite.ptr().second; - - if (key == keyWidth) { - this.setWidth(value); - } else if (key == keyHeight) { - this.setHeight(value); - } - } - - for ( - const ite: iterator = layout.begin(); - ite.notEqual(layout.end()); - ite.preIncrement() - ) { - const key: string = ite.ptr().first; - const value: number = ite.ptr().second; - - if (key == keyX) { - this.setX(value); - } else if (key == keyY) { - this.setY(value); - } else if (key == keyCenterX) { - this.centerX(value); - } else if (key == keyCenterY) { - this.centerY(value); - } else if (key == keyTop) { - this.top(value); - } else if (key == keyBottom) { - this.bottom(value); - } else if (key == keyLeft) { - this.left(value); - } else if (key == keyRight) { - this.right(value); - } - } - } - - private _width: number; // 横幅 - private _height: number; // 縦幅 + this.setHeight(1.0); } + + /** + * 横幅を設定 + * + * @param w 横幅 + */ + public setWidth(w: number): void { + const scaleX: number = w / this._width; + const scaleY: number = scaleX; + this.scale(scaleX, scaleY); + } + + /** + * 縦幅を設定 + * @param h 縦幅 + */ + public setHeight(h: number): void { + const scaleX: number = h / this._height; + const scaleY: number = scaleX; + this.scale(scaleX, scaleY); + } + + /** + * 位置を設定 + * + * @param x X軸の位置 + * @param y Y軸の位置 + */ + public setPosition(x: number, y: number): void { + this.translate(x, y); + } + + /** + * 中心位置を設定 + * + * @param x X軸の中心位置 + * @param y Y軸の中心位置 + * + * @note widthかheightを設定したあとでないと、拡大率が正しく取得できないためずれる。 + */ + public setCenterPosition(x: number, y: number) { + this.centerX(x); + this.centerY(y); + } + + /** + * 上辺の位置を設定する + * + * @param y 上辺のY軸位置 + */ + public top(y: number): void { + this.setY(y); + } + + /** + * 下辺の位置を設定する + * + * @param y 下辺のY軸位置 + */ + public bottom(y: number) { + const h: number = this._height * this.getScaleY(); + + this.translateY(y - h); + } + + /** + * 左辺の位置を設定 + * + * @param x 左辺のX軸位置 + */ + public left(x: number): void { + this.setX(x); + } + + /** + * 右辺の位置を設定 + * + * @param x 右辺のX軸位置 + */ + public right(x: number): void { + const w = this._width * this.getScaleX(); + + this.translateX(x - w); + } + + /** + * X軸の中心位置を設定 + * + * @param x X軸の中心位置 + */ + public centerX(x: number): void { + const w = this._width * this.getScaleX(); + + this.translateX(x - w / 2.0); + } + + /** + * X軸の位置を設定 + * + * @param x X軸の位置 + */ + public setX(x: number): void { + this.translateX(x); + } + + /** + * Y軸の中心位置を設定 + * + * @param y Y軸の中心位置 + */ + public centerY(y: number): void { + const h: number = this._height * this.getScaleY(); + + this.translateY(y - h / 2.0); + } + + /** + * Y軸の位置を設定する + * + * @param y Y軸の位置 + */ + public setY(y: number): void { + this.translateY(y); + } + + /** + * レイアウト情報から位置を設定 + * + * @param layout レイアウト情報 + */ + public setupFromLayout(layout: csmMap): void { + const keyWidth = 'width'; + const keyHeight = 'height'; + const keyX = 'x'; + const keyY = 'y'; + const keyCenterX = 'center_x'; + const keyCenterY = 'center_y'; + const keyTop = 'top'; + const keyBottom = 'bottom'; + const keyLeft = 'left'; + const keyRight = 'right'; + + for ( + const ite: iterator = layout.begin(); + ite.notEqual(layout.end()); + ite.preIncrement() + ) { + const key: string = ite.ptr().first; + const value: number = ite.ptr().second; + + if (key == keyWidth) { + this.setWidth(value); + } else if (key == keyHeight) { + this.setHeight(value); + } + } + + for ( + const ite: iterator = layout.begin(); + ite.notEqual(layout.end()); + ite.preIncrement() + ) { + const key: string = ite.ptr().first; + const value: number = ite.ptr().second; + + if (key == keyX) { + this.setX(value); + } else if (key == keyY) { + this.setY(value); + } else if (key == keyCenterX) { + this.centerX(value); + } else if (key == keyCenterY) { + this.centerY(value); + } else if (key == keyTop) { + this.top(value); + } else if (key == keyBottom) { + this.bottom(value); + } else if (key == keyLeft) { + this.left(value); + } else if (key == keyRight) { + this.right(value); + } + } + } + + private _width: number; // 横幅 + private _height: number; // 縦幅 +} + +// Namespace definition for compatibility. +import * as $ from './cubismmodelmatrix'; +// eslint-disable-next-line @typescript-eslint/no-namespace +export namespace Live2DCubismFramework { + export const CubismModelMatrix = $.CubismModelMatrix; + export type CubismModelMatrix = $.CubismModelMatrix; } diff --git a/src/math/cubismtargetpoint.ts b/src/math/cubismtargetpoint.ts index 52b6c16..9ee2e59 100644 --- a/src/math/cubismtargetpoint.ts +++ b/src/math/cubismtargetpoint.ts @@ -5,160 +5,165 @@ * that can be found at https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html. */ -import { Live2DCubismFramework as cubismmath } from './cubismmath'; -import CubismMath = cubismmath.CubismMath; +import { CubismMath } from './cubismmath'; -export namespace Live2DCubismFramework { - const FrameRate = 30; - const Epsilon = 0.01; +const FrameRate = 30; +const Epsilon = 0.01; + +/** + * 顔の向きの制御機能 + * + * 顔の向きの制御機能を提供するクラス。 + */ +export class CubismTargetPoint { + /** + * コンストラクタ + */ + public constructor() { + this._faceTargetX = 0.0; + this._faceTargetY = 0.0; + this._faceX = 0.0; + this._faceY = 0.0; + this._faceVX = 0.0; + this._faceVY = 0.0; + this._lastTimeSeconds = 0.0; + this._userTimeSeconds = 0.0; + } /** - * 顔の向きの制御機能 - * - * 顔の向きの制御機能を提供するクラス。 + * 更新処理 */ - export class CubismTargetPoint { - /** - * コンストラクタ - */ - public constructor() { - this._faceTargetX = 0.0; - this._faceTargetY = 0.0; - this._faceX = 0.0; - this._faceY = 0.0; - this._faceVX = 0.0; - this._faceVY = 0.0; - this._lastTimeSeconds = 0.0; - this._userTimeSeconds = 0.0; - } + public update(deltaTimeSeconds: number): void { + // デルタ時間を加算する + this._userTimeSeconds += deltaTimeSeconds; - /** - * 更新処理 - */ - public update(deltaTimeSeconds: number): void { - // デルタ時間を加算する - this._userTimeSeconds += deltaTimeSeconds; + // 首を中央から左右に振るときの平均的な速さは 秒速度。加速・減速を考慮して、その2倍を最高速度とする + // 顔の振り具合を、中央(0.0)から、左右は(+-1.0)とする + const faceParamMaxV: number = 40.0 / 10.0; // 7.5秒間に40分移動(5.3/sc) + const maxV: number = (faceParamMaxV * 1.0) / FrameRate; // 1frameあたりに変化できる速度の上限 - // 首を中央から左右に振るときの平均的な速さは 秒速度。加速・減速を考慮して、その2倍を最高速度とする - // 顔の振り具合を、中央(0.0)から、左右は(+-1.0)とする - const faceParamMaxV: number = 40.0 / 10.0; // 7.5秒間に40分移動(5.3/sc) - const maxV: number = (faceParamMaxV * 1.0) / FrameRate; // 1frameあたりに変化できる速度の上限 - - if (this._lastTimeSeconds == 0.0) { - this._lastTimeSeconds = this._userTimeSeconds; - return; - } - - const deltaTimeWeight: number = - (this._userTimeSeconds - this._lastTimeSeconds) * FrameRate; + if (this._lastTimeSeconds == 0.0) { this._lastTimeSeconds = this._userTimeSeconds; + return; + } - // 最高速度になるまでの時間を - const timeToMaxSpeed = 0.15; - const frameToMaxSpeed: number = timeToMaxSpeed * FrameRate; // sec * frame/sec - const maxA: number = (deltaTimeWeight * maxV) / frameToMaxSpeed; // 1frameあたりの加速度 + const deltaTimeWeight: number = + (this._userTimeSeconds - this._lastTimeSeconds) * FrameRate; + this._lastTimeSeconds = this._userTimeSeconds; - // 目指す向きは、(dx, dy)方向のベクトルとなる - const dx: number = this._faceTargetX - this._faceX; - const dy: number = this._faceTargetY - this._faceY; + // 最高速度になるまでの時間を + const timeToMaxSpeed = 0.15; + const frameToMaxSpeed: number = timeToMaxSpeed * FrameRate; // sec * frame/sec + const maxA: number = (deltaTimeWeight * maxV) / frameToMaxSpeed; // 1frameあたりの加速度 - if (CubismMath.abs(dx) <= Epsilon && CubismMath.abs(dy) <= Epsilon) { - return; // 変化なし + // 目指す向きは、(dx, dy)方向のベクトルとなる + const dx: number = this._faceTargetX - this._faceX; + const dy: number = this._faceTargetY - this._faceY; + + if (CubismMath.abs(dx) <= Epsilon && CubismMath.abs(dy) <= Epsilon) { + return; // 変化なし + } + + // 速度の最大よりも大きい場合は、速度を落とす + const d: number = CubismMath.sqrt(dx * dx + dy * dy); + + // 進行方向の最大速度ベクトル + const vx: number = (maxV * dx) / d; + const vy: number = (maxV * dy) / d; + + // 現在の速度から、新規速度への変化(加速度)を求める + let ax: number = vx - this._faceVX; + let ay: number = vy - this._faceVY; + + const a: number = CubismMath.sqrt(ax * ax + ay * ay); + + // 加速のとき + if (a < -maxA || a > maxA) { + ax *= maxA / a; + ay *= maxA / a; + } + + // 加速度を元の速度に足して、新速度とする + this._faceVX += ax; + this._faceVY += ay; + + // 目的の方向に近づいたとき、滑らかに減速するための処理 + // 設定された加速度で止まる事の出来る距離と速度の関係から + // 現在とりうる最高速度を計算し、それ以上の時は速度を落とす + // ※本来、人間は筋力で力(加速度)を調整できるため、より自由度が高いが、簡単な処理で済ませている + { + // 加速度、速度、距離の関係式。 + // 2 6 2 3 + // sqrt(a t + 16 a h t - 8 a h) - a t + // v = -------------------------------------- + // 2 + // 4 t - 2 + // (t=1) + // 時刻tは、あらかじめ加速度、速度を1/60(フレームレート、単位なし)で + // 考えているので、t=1として消してよい(※未検証) + + const maxV: number = + 0.5 * + (CubismMath.sqrt(maxA * maxA + 16.0 * maxA * d - 8.0 * maxA * d) - + maxA); + const curV: number = CubismMath.sqrt( + this._faceVX * this._faceVX + this._faceVY * this._faceVY + ); + + if (curV > maxV) { + // 現在の速度 > 最高速度のとき、最高速度まで減速 + this._faceVX *= maxV / curV; + this._faceVY *= maxV / curV; } - - // 速度の最大よりも大きい場合は、速度を落とす - const d: number = CubismMath.sqrt(dx * dx + dy * dy); - - // 進行方向の最大速度ベクトル - const vx: number = (maxV * dx) / d; - const vy: number = (maxV * dy) / d; - - // 現在の速度から、新規速度への変化(加速度)を求める - let ax: number = vx - this._faceVX; - let ay: number = vy - this._faceVY; - - const a: number = CubismMath.sqrt(ax * ax + ay * ay); - - // 加速のとき - if (a < -maxA || a > maxA) { - ax *= maxA / a; - ay *= maxA / a; - } - - // 加速度を元の速度に足して、新速度とする - this._faceVX += ax; - this._faceVY += ay; - - // 目的の方向に近づいたとき、滑らかに減速するための処理 - // 設定された加速度で止まる事の出来る距離と速度の関係から - // 現在とりうる最高速度を計算し、それ以上の時は速度を落とす - // ※本来、人間は筋力で力(加速度)を調整できるため、より自由度が高いが、簡単な処理で済ませている - { - // 加速度、速度、距離の関係式。 - // 2 6 2 3 - // sqrt(a t + 16 a h t - 8 a h) - a t - // v = -------------------------------------- - // 2 - // 4 t - 2 - // (t=1) - // 時刻tは、あらかじめ加速度、速度を1/60(フレームレート、単位なし)で - // 考えているので、t=1として消してよい(※未検証) - - const maxV: number = - 0.5 * - (CubismMath.sqrt(maxA * maxA + 16.0 * maxA * d - 8.0 * maxA * d) - - maxA); - const curV: number = CubismMath.sqrt( - this._faceVX * this._faceVX + this._faceVY * this._faceVY - ); - - if (curV > maxV) { - // 現在の速度 > 最高速度のとき、最高速度まで減速 - this._faceVX *= maxV / curV; - this._faceVY *= maxV / curV; - } - } - - this._faceX += this._faceVX; - this._faceY += this._faceVY; } - /** - * X軸の顔の向きの値を取得 - * - * @return X軸の顔の向きの値(-1.0 ~ 1.0) - */ - public getX(): number { - return this._faceX; - } - - /** - * Y軸の顔の向きの値を取得 - * - * @return Y軸の顔の向きの値(-1.0 ~ 1.0) - */ - public getY(): number { - return this._faceY; - } - - /** - * 顔の向きの目標値を設定 - * - * @param x X軸の顔の向きの値(-1.0 ~ 1.0) - * @param y Y軸の顔の向きの値(-1.0 ~ 1.0) - */ - public set(x: number, y: number): void { - this._faceTargetX = x; - this._faceTargetY = y; - } - - private _faceTargetX: number; // 顔の向きのX目標値(この値に近づいていく) - private _faceTargetY: number; // 顔の向きのY目標値(この値に近づいていく) - private _faceX: number; // 顔の向きX(-1.0 ~ 1.0) - private _faceY: number; // 顔の向きY(-1.0 ~ 1.0) - private _faceVX: number; // 顔の向きの変化速度X - private _faceVY: number; // 顔の向きの変化速度Y - private _lastTimeSeconds: number; // 最後の実行時間[秒] - private _userTimeSeconds: number; // デルタ時間の積算値[秒] + this._faceX += this._faceVX; + this._faceY += this._faceVY; } + + /** + * X軸の顔の向きの値を取得 + * + * @return X軸の顔の向きの値(-1.0 ~ 1.0) + */ + public getX(): number { + return this._faceX; + } + + /** + * Y軸の顔の向きの値を取得 + * + * @return Y軸の顔の向きの値(-1.0 ~ 1.0) + */ + public getY(): number { + return this._faceY; + } + + /** + * 顔の向きの目標値を設定 + * + * @param x X軸の顔の向きの値(-1.0 ~ 1.0) + * @param y Y軸の顔の向きの値(-1.0 ~ 1.0) + */ + public set(x: number, y: number): void { + this._faceTargetX = x; + this._faceTargetY = y; + } + + private _faceTargetX: number; // 顔の向きのX目標値(この値に近づいていく) + private _faceTargetY: number; // 顔の向きのY目標値(この値に近づいていく) + private _faceX: number; // 顔の向きX(-1.0 ~ 1.0) + private _faceY: number; // 顔の向きY(-1.0 ~ 1.0) + private _faceVX: number; // 顔の向きの変化速度X + private _faceVY: number; // 顔の向きの変化速度Y + private _lastTimeSeconds: number; // 最後の実行時間[秒] + private _userTimeSeconds: number; // デルタ時間の積算値[秒] +} + +// Namespace definition for compatibility. +import * as $ from './cubismtargetpoint'; +// eslint-disable-next-line @typescript-eslint/no-namespace +export namespace Live2DCubismFramework { + export const CubismTargetPoint = $.CubismTargetPoint; + export type CubismTargetPoint = $.CubismTargetPoint; } diff --git a/src/math/cubismvector2.ts b/src/math/cubismvector2.ts index b85c45a..1a7dc3d 100644 --- a/src/math/cubismvector2.ts +++ b/src/math/cubismvector2.ts @@ -5,159 +5,165 @@ * that can be found at https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html. */ -export namespace Live2DCubismFramework { +/** + * 2次元ベクトル型 + * + * 2次元ベクトル型の機能を提供する。 + */ +export class CubismVector2 { /** - * 2次元ベクトル型 - * - * 2次元ベクトル型の機能を提供する。 + * コンストラクタ */ - export class CubismVector2 { - /** - * コンストラクタ - */ - public constructor(public x?: number, public y?: number) { - this.x = x == undefined ? 0.0 : x; + public constructor(public x?: number, public y?: number) { + this.x = x == undefined ? 0.0 : x; - this.y = y == undefined ? 0.0 : y; - } + this.y = y == undefined ? 0.0 : y; + } - /** - * ベクトルの加算 - * - * @param vector2 加算するベクトル値 - * @return 加算結果 ベクトル値 - */ - public add(vector2: CubismVector2): CubismVector2 { - const ret: CubismVector2 = new CubismVector2(0.0, 0.0); - ret.x = this.x + vector2.x; - ret.y = this.y + vector2.y; - return ret; - } + /** + * ベクトルの加算 + * + * @param vector2 加算するベクトル値 + * @return 加算結果 ベクトル値 + */ + public add(vector2: CubismVector2): CubismVector2 { + const ret: CubismVector2 = new CubismVector2(0.0, 0.0); + ret.x = this.x + vector2.x; + ret.y = this.y + vector2.y; + return ret; + } - /** - * ベクトルの減算 - * - * @param vector2 減算するベクトル値 - * @return 減算結果 ベクトル値 - */ - public substract(vector2: CubismVector2): CubismVector2 { - const ret: CubismVector2 = new CubismVector2(0.0, 0.0); - ret.x = this.x - vector2.x; - ret.y = this.y - vector2.y; - return ret; - } + /** + * ベクトルの減算 + * + * @param vector2 減算するベクトル値 + * @return 減算結果 ベクトル値 + */ + public substract(vector2: CubismVector2): CubismVector2 { + const ret: CubismVector2 = new CubismVector2(0.0, 0.0); + ret.x = this.x - vector2.x; + ret.y = this.y - vector2.y; + return ret; + } - /** - * ベクトルの乗算 - * - * @param vector2 乗算するベクトル値 - * @return 乗算結果 ベクトル値 - */ - public multiply(vector2: CubismVector2): CubismVector2 { - const ret: CubismVector2 = new CubismVector2(0.0, 0.0); - ret.x = this.x * vector2.x; - ret.y = this.y * vector2.y; - return ret; - } + /** + * ベクトルの乗算 + * + * @param vector2 乗算するベクトル値 + * @return 乗算結果 ベクトル値 + */ + public multiply(vector2: CubismVector2): CubismVector2 { + const ret: CubismVector2 = new CubismVector2(0.0, 0.0); + ret.x = this.x * vector2.x; + ret.y = this.y * vector2.y; + return ret; + } - /** - * ベクトルの乗算(スカラー) - * - * @param scalar 乗算するスカラー値 - * @return 乗算結果 ベクトル値 - */ - public multiplyByScaler(scalar: number): CubismVector2 { - return this.multiply(new CubismVector2(scalar, scalar)); - } + /** + * ベクトルの乗算(スカラー) + * + * @param scalar 乗算するスカラー値 + * @return 乗算結果 ベクトル値 + */ + public multiplyByScaler(scalar: number): CubismVector2 { + return this.multiply(new CubismVector2(scalar, scalar)); + } - /** - * ベクトルの除算 - * - * @param vector2 除算するベクトル値 - * @return 除算結果 ベクトル値 - */ - public division(vector2: CubismVector2): CubismVector2 { - const ret: CubismVector2 = new CubismVector2(0.0, 0.0); - ret.x = this.x / vector2.x; - ret.y = this.y / vector2.y; - return ret; - } + /** + * ベクトルの除算 + * + * @param vector2 除算するベクトル値 + * @return 除算結果 ベクトル値 + */ + public division(vector2: CubismVector2): CubismVector2 { + const ret: CubismVector2 = new CubismVector2(0.0, 0.0); + ret.x = this.x / vector2.x; + ret.y = this.y / vector2.y; + return ret; + } - /** - * ベクトルの除算(スカラー) - * - * @param scalar 除算するスカラー値 - * @return 除算結果 ベクトル値 - */ - public divisionByScalar(scalar: number): CubismVector2 { - return this.division(new CubismVector2(scalar, scalar)); - } + /** + * ベクトルの除算(スカラー) + * + * @param scalar 除算するスカラー値 + * @return 除算結果 ベクトル値 + */ + public divisionByScalar(scalar: number): CubismVector2 { + return this.division(new CubismVector2(scalar, scalar)); + } - /** - * ベクトルの長さを取得する - * - * @return ベクトルの長さ - */ - public getLength(): number { - return Math.sqrt(this.x * this.x + this.y * this.y); - } + /** + * ベクトルの長さを取得する + * + * @return ベクトルの長さ + */ + public getLength(): number { + return Math.sqrt(this.x * this.x + this.y * this.y); + } - /** - * ベクトルの距離の取得 - * - * @param a 点 - * @return ベクトルの距離 - */ - public getDistanceWith(a: CubismVector2): number { - return Math.sqrt( - (this.x - a.x) * (this.x - a.x) + (this.y - a.y) * (this.y - a.y) - ); - } + /** + * ベクトルの距離の取得 + * + * @param a 点 + * @return ベクトルの距離 + */ + public getDistanceWith(a: CubismVector2): number { + return Math.sqrt( + (this.x - a.x) * (this.x - a.x) + (this.y - a.y) * (this.y - a.y) + ); + } - /** - * ドット積の計算 - * - * @param a 値 - * @return 結果 - */ - public dot(a: CubismVector2): number { - return this.x * a.x + this.y * a.y; - } + /** + * ドット積の計算 + * + * @param a 値 + * @return 結果 + */ + public dot(a: CubismVector2): number { + return this.x * a.x + this.y * a.y; + } - /** - * 正規化の適用 - */ - public normalize(): void { - const length: number = Math.pow(this.x * this.x + this.y * this.y, 0.5); + /** + * 正規化の適用 + */ + public normalize(): void { + const length: number = Math.pow(this.x * this.x + this.y * this.y, 0.5); - this.x = this.x / length; - this.y = this.y / length; - } + this.x = this.x / length; + this.y = this.y / length; + } - /** - * 等しさの確認(等しいか?) - * - * 値が等しいか? - * - * @param rhs 確認する値 - * @return true 値は等しい - * @return false 値は等しくない - */ - public isEqual(rhs: CubismVector2): boolean { - return this.x == rhs.x && this.y == rhs.y; - } + /** + * 等しさの確認(等しいか?) + * + * 値が等しいか? + * + * @param rhs 確認する値 + * @return true 値は等しい + * @return false 値は等しくない + */ + public isEqual(rhs: CubismVector2): boolean { + return this.x == rhs.x && this.y == rhs.y; + } - /** - * 等しさの確認(等しくないか?) - * - * 値が等しくないか? - * - * @param rhs 確認する値 - * @return true 値は等しくない - * @return false 値は等しい - */ - public isNotEqual(rhs: CubismVector2): boolean { - return !this.isEqual(rhs); - } + /** + * 等しさの確認(等しくないか?) + * + * 値が等しくないか? + * + * @param rhs 確認する値 + * @return true 値は等しくない + * @return false 値は等しい + */ + public isNotEqual(rhs: CubismVector2): boolean { + return !this.isEqual(rhs); } } + +// Namespace definition for compatibility. +import * as $ from './cubismvector2'; +// eslint-disable-next-line @typescript-eslint/no-namespace +export namespace Live2DCubismFramework { + export const CubismVector2 = $.CubismVector2; + export type CubismVector2 = $.CubismVector2; +} diff --git a/src/math/cubismviewmatrix.ts b/src/math/cubismviewmatrix.ts index fc23a41..c81a05a 100644 --- a/src/math/cubismviewmatrix.ts +++ b/src/math/cubismviewmatrix.ts @@ -5,333 +5,335 @@ * that can be found at https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html. */ -import { Live2DCubismFramework as cubismmatrix44 } from './cubismmatrix44'; -import CubismMatrix44 = cubismmatrix44.CubismMatrix44; +import { CubismMatrix44 } from './cubismmatrix44'; -export namespace Live2DCubismFramework { +/** + * カメラの位置変更に使うと便利な4x4行列 + * + * カメラの位置変更に使うと便利な4x4行列のクラス。 + */ +export class CubismViewMatrix extends CubismMatrix44 { /** - * カメラの位置変更に使うと便利な4x4行列 - * - * カメラの位置変更に使うと便利な4x4行列のクラス。 + * コンストラクタ */ - export class CubismViewMatrix extends CubismMatrix44 { - /** - * コンストラクタ - */ - public constructor() { - super(); - this._screenLeft = 0.0; - this._screenRight = 0.0; - this._screenTop = 0.0; - this._screenBottom = 0.0; - this._maxLeft = 0.0; - this._maxRight = 0.0; - this._maxTop = 0.0; - this._maxBottom = 0.0; - this._maxScale = 0.0; - this._minScale = 0.0; - } - - /** - * 移動を調整 - * - * @param x X軸の移動量 - * @param y Y軸の移動量 - */ - public adjustTranslate(x: number, y: number): void { - if (this._tr[0] * this._maxLeft + (this._tr[12] + x) > this._screenLeft) { - x = this._screenLeft - this._tr[0] * this._maxLeft - this._tr[12]; - } - - if ( - this._tr[0] * this._maxRight + (this._tr[12] + x) < - this._screenRight - ) { - x = this._screenRight - this._tr[0] * this._maxRight - this._tr[12]; - } - - if (this._tr[5] * this._maxTop + (this._tr[13] + y) < this._screenTop) { - y = this._screenTop - this._tr[5] * this._maxTop - this._tr[13]; - } - - if ( - this._tr[5] * this._maxBottom + (this._tr[13] + y) > - this._screenBottom - ) { - y = this._screenBottom - this._tr[5] * this._maxBottom - this._tr[13]; - } - - const tr1: Float32Array = new Float32Array([ - 1.0, - 0.0, - 0.0, - 0.0, - 0.0, - 1.0, - 0.0, - 0.0, - 0.0, - 0.0, - 1.0, - 0.0, - x, - y, - 0.0, - 1.0 - ]); - - CubismMatrix44.multiply(tr1, this._tr, this._tr); - } - - /** - * 拡大率を調整 - * - * @param cx 拡大を行うX軸の中心位置 - * @param cy 拡大を行うY軸の中心位置 - * @param scale 拡大率 - */ - public adjustScale(cx: number, cy: number, scale: number): void { - const maxScale: number = this.getMaxScale(); - const minScale: number = this.getMinScale(); - - const targetScale = scale * this._tr[0]; - - if (targetScale < minScale) { - if (this._tr[0] > 0.0) { - scale = minScale / this._tr[0]; - } - } else if (targetScale > maxScale) { - if (this._tr[0] > 0.0) { - scale = maxScale / this._tr[0]; - } - } - - const tr1: Float32Array = new Float32Array([ - 1.0, - 0.0, - 0.0, - 0.0, - 0.0, - 1.0, - 0.0, - 0.0, - 0.0, - 0.0, - 1.0, - 0.0, - cx, - cy, - 0.0, - 1.0 - ]); - - const tr2: Float32Array = new Float32Array([ - scale, - 0.0, - 0.0, - 0.0, - 0.0, - scale, - 0.0, - 0.0, - 0.0, - 0.0, - 1.0, - 0.0, - 0.0, - 0.0, - 0.0, - 1.0 - ]); - - const tr3: Float32Array = new Float32Array([ - 1.0, - 0.0, - 0.0, - 0.0, - 0.0, - 1.0, - 0.0, - 0.0, - 0.0, - 0.0, - 1.0, - 0.0, - -cx, - -cy, - 0.0, - 1.0 - ]); - - CubismMatrix44.multiply(tr3, this._tr, this._tr); - CubismMatrix44.multiply(tr2, this._tr, this._tr); - CubismMatrix44.multiply(tr1, this._tr, this._tr); - } - - /** - * デバイスに対応する論理座養生の範囲の設定 - * - * @param left 左辺のX軸の位置 - * @param right 右辺のX軸の位置 - * @param bottom 下辺のY軸の位置 - * @param top 上辺のY軸の位置 - */ - public setScreenRect( - left: number, - right: number, - bottom: number, - top: number - ): void { - this._screenLeft = left; - this._screenRight = right; - this._screenBottom = bottom; - this._screenTop = top; - } - - /** - * デバイスに対応する論理座標上の移動可能範囲の設定 - * @param left 左辺のX軸の位置 - * @param right 右辺のX軸の位置 - * @param bottom 下辺のY軸の位置 - * @param top 上辺のY軸の位置 - */ - public setMaxScreenRect( - left: number, - right: number, - bottom: number, - top: number - ): void { - this._maxLeft = left; - this._maxRight = right; - this._maxTop = top; - this._maxBottom = bottom; - } - - /** - * 最大拡大率の設定 - * @param maxScale 最大拡大率 - */ - public setMaxScale(maxScale: number): void { - this._maxScale = maxScale; - } - - /** - * 最小拡大率の設定 - * @param minScale 最小拡大率 - */ - public setMinScale(minScale: number): void { - this._minScale = minScale; - } - - /** - * 最大拡大率の取得 - * @return 最大拡大率 - */ - public getMaxScale(): number { - return this._maxScale; - } - - /** - * 最小拡大率の取得 - * @return 最小拡大率 - */ - public getMinScale(): number { - return this._minScale; - } - - /** - * 拡大率が最大になっているかを確認する - * - * @return true 拡大率は最大 - * @return false 拡大率は最大ではない - */ - public isMaxScale(): boolean { - return this.getScaleX() >= this._maxScale; - } - - /** - * 拡大率が最小になっているかを確認する - * - * @return true 拡大率は最小 - * @return false 拡大率は最小ではない - */ - public isMinScale(): boolean { - return this.getScaleX() <= this._minScale; - } - - /** - * デバイスに対応する論理座標の左辺のX軸位置を取得する - * @return デバイスに対応する論理座標の左辺のX軸位置 - */ - public getScreenLeft(): number { - return this._screenLeft; - } - - /** - * デバイスに対応する論理座標の右辺のX軸位置を取得する - * @return デバイスに対応する論理座標の右辺のX軸位置 - */ - public getScreenRight(): number { - return this._screenRight; - } - - /** - * デバイスに対応する論理座標の下辺のY軸位置を取得する - * @return デバイスに対応する論理座標の下辺のY軸位置 - */ - public getScreenBottom(): number { - return this._screenBottom; - } - - /** - * デバイスに対応する論理座標の上辺のY軸位置を取得する - * @return デバイスに対応する論理座標の上辺のY軸位置 - */ - public getScreenTop(): number { - return this._screenTop; - } - - /** - * 左辺のX軸位置の最大値の取得 - * @return 左辺のX軸位置の最大値 - */ - public getMaxLeft(): number { - return this._maxLeft; - } - - /** - * 右辺のX軸位置の最大値の取得 - * @return 右辺のX軸位置の最大値 - */ - public getMaxRight(): number { - return this._maxRight; - } - - /** - * 下辺のY軸位置の最大値の取得 - * @return 下辺のY軸位置の最大値 - */ - public getMaxBottom(): number { - return this._maxBottom; - } - - /** - * 上辺のY軸位置の最大値の取得 - * @return 上辺のY軸位置の最大値 - */ - public getMaxTop(): number { - return this._maxTop; - } - - private _screenLeft: number; // デバイスに対応する論理座標上の範囲(左辺X軸位置) - private _screenRight: number; // デバイスに対応する論理座標上の範囲(右辺X軸位置) - private _screenTop: number; // デバイスに対応する論理座標上の範囲(上辺Y軸位置) - private _screenBottom: number; // デバイスに対応する論理座標上の範囲(下辺Y軸位置) - private _maxLeft: number; // 論理座標上の移動可能範囲(左辺X軸位置) - private _maxRight: number; // 論理座標上の移動可能範囲(右辺X軸位置) - private _maxTop: number; // 論理座標上の移動可能範囲(上辺Y軸位置) - private _maxBottom: number; // 論理座標上の移動可能範囲(下辺Y軸位置) - private _maxScale: number; // 拡大率の最大値 - private _minScale: number; // 拡大率の最小値 + public constructor() { + super(); + this._screenLeft = 0.0; + this._screenRight = 0.0; + this._screenTop = 0.0; + this._screenBottom = 0.0; + this._maxLeft = 0.0; + this._maxRight = 0.0; + this._maxTop = 0.0; + this._maxBottom = 0.0; + this._maxScale = 0.0; + this._minScale = 0.0; } + + /** + * 移動を調整 + * + * @param x X軸の移動量 + * @param y Y軸の移動量 + */ + public adjustTranslate(x: number, y: number): void { + if (this._tr[0] * this._maxLeft + (this._tr[12] + x) > this._screenLeft) { + x = this._screenLeft - this._tr[0] * this._maxLeft - this._tr[12]; + } + + if (this._tr[0] * this._maxRight + (this._tr[12] + x) < this._screenRight) { + x = this._screenRight - this._tr[0] * this._maxRight - this._tr[12]; + } + + if (this._tr[5] * this._maxTop + (this._tr[13] + y) < this._screenTop) { + y = this._screenTop - this._tr[5] * this._maxTop - this._tr[13]; + } + + if ( + this._tr[5] * this._maxBottom + (this._tr[13] + y) > + this._screenBottom + ) { + y = this._screenBottom - this._tr[5] * this._maxBottom - this._tr[13]; + } + + const tr1: Float32Array = new Float32Array([ + 1.0, + 0.0, + 0.0, + 0.0, + 0.0, + 1.0, + 0.0, + 0.0, + 0.0, + 0.0, + 1.0, + 0.0, + x, + y, + 0.0, + 1.0 + ]); + + CubismMatrix44.multiply(tr1, this._tr, this._tr); + } + + /** + * 拡大率を調整 + * + * @param cx 拡大を行うX軸の中心位置 + * @param cy 拡大を行うY軸の中心位置 + * @param scale 拡大率 + */ + public adjustScale(cx: number, cy: number, scale: number): void { + const maxScale: number = this.getMaxScale(); + const minScale: number = this.getMinScale(); + + const targetScale = scale * this._tr[0]; + + if (targetScale < minScale) { + if (this._tr[0] > 0.0) { + scale = minScale / this._tr[0]; + } + } else if (targetScale > maxScale) { + if (this._tr[0] > 0.0) { + scale = maxScale / this._tr[0]; + } + } + + const tr1: Float32Array = new Float32Array([ + 1.0, + 0.0, + 0.0, + 0.0, + 0.0, + 1.0, + 0.0, + 0.0, + 0.0, + 0.0, + 1.0, + 0.0, + cx, + cy, + 0.0, + 1.0 + ]); + + const tr2: Float32Array = new Float32Array([ + scale, + 0.0, + 0.0, + 0.0, + 0.0, + scale, + 0.0, + 0.0, + 0.0, + 0.0, + 1.0, + 0.0, + 0.0, + 0.0, + 0.0, + 1.0 + ]); + + const tr3: Float32Array = new Float32Array([ + 1.0, + 0.0, + 0.0, + 0.0, + 0.0, + 1.0, + 0.0, + 0.0, + 0.0, + 0.0, + 1.0, + 0.0, + -cx, + -cy, + 0.0, + 1.0 + ]); + + CubismMatrix44.multiply(tr3, this._tr, this._tr); + CubismMatrix44.multiply(tr2, this._tr, this._tr); + CubismMatrix44.multiply(tr1, this._tr, this._tr); + } + + /** + * デバイスに対応する論理座養生の範囲の設定 + * + * @param left 左辺のX軸の位置 + * @param right 右辺のX軸の位置 + * @param bottom 下辺のY軸の位置 + * @param top 上辺のY軸の位置 + */ + public setScreenRect( + left: number, + right: number, + bottom: number, + top: number + ): void { + this._screenLeft = left; + this._screenRight = right; + this._screenBottom = bottom; + this._screenTop = top; + } + + /** + * デバイスに対応する論理座標上の移動可能範囲の設定 + * @param left 左辺のX軸の位置 + * @param right 右辺のX軸の位置 + * @param bottom 下辺のY軸の位置 + * @param top 上辺のY軸の位置 + */ + public setMaxScreenRect( + left: number, + right: number, + bottom: number, + top: number + ): void { + this._maxLeft = left; + this._maxRight = right; + this._maxTop = top; + this._maxBottom = bottom; + } + + /** + * 最大拡大率の設定 + * @param maxScale 最大拡大率 + */ + public setMaxScale(maxScale: number): void { + this._maxScale = maxScale; + } + + /** + * 最小拡大率の設定 + * @param minScale 最小拡大率 + */ + public setMinScale(minScale: number): void { + this._minScale = minScale; + } + + /** + * 最大拡大率の取得 + * @return 最大拡大率 + */ + public getMaxScale(): number { + return this._maxScale; + } + + /** + * 最小拡大率の取得 + * @return 最小拡大率 + */ + public getMinScale(): number { + return this._minScale; + } + + /** + * 拡大率が最大になっているかを確認する + * + * @return true 拡大率は最大 + * @return false 拡大率は最大ではない + */ + public isMaxScale(): boolean { + return this.getScaleX() >= this._maxScale; + } + + /** + * 拡大率が最小になっているかを確認する + * + * @return true 拡大率は最小 + * @return false 拡大率は最小ではない + */ + public isMinScale(): boolean { + return this.getScaleX() <= this._minScale; + } + + /** + * デバイスに対応する論理座標の左辺のX軸位置を取得する + * @return デバイスに対応する論理座標の左辺のX軸位置 + */ + public getScreenLeft(): number { + return this._screenLeft; + } + + /** + * デバイスに対応する論理座標の右辺のX軸位置を取得する + * @return デバイスに対応する論理座標の右辺のX軸位置 + */ + public getScreenRight(): number { + return this._screenRight; + } + + /** + * デバイスに対応する論理座標の下辺のY軸位置を取得する + * @return デバイスに対応する論理座標の下辺のY軸位置 + */ + public getScreenBottom(): number { + return this._screenBottom; + } + + /** + * デバイスに対応する論理座標の上辺のY軸位置を取得する + * @return デバイスに対応する論理座標の上辺のY軸位置 + */ + public getScreenTop(): number { + return this._screenTop; + } + + /** + * 左辺のX軸位置の最大値の取得 + * @return 左辺のX軸位置の最大値 + */ + public getMaxLeft(): number { + return this._maxLeft; + } + + /** + * 右辺のX軸位置の最大値の取得 + * @return 右辺のX軸位置の最大値 + */ + public getMaxRight(): number { + return this._maxRight; + } + + /** + * 下辺のY軸位置の最大値の取得 + * @return 下辺のY軸位置の最大値 + */ + public getMaxBottom(): number { + return this._maxBottom; + } + + /** + * 上辺のY軸位置の最大値の取得 + * @return 上辺のY軸位置の最大値 + */ + public getMaxTop(): number { + return this._maxTop; + } + + private _screenLeft: number; // デバイスに対応する論理座標上の範囲(左辺X軸位置) + private _screenRight: number; // デバイスに対応する論理座標上の範囲(右辺X軸位置) + private _screenTop: number; // デバイスに対応する論理座標上の範囲(上辺Y軸位置) + private _screenBottom: number; // デバイスに対応する論理座標上の範囲(下辺Y軸位置) + private _maxLeft: number; // 論理座標上の移動可能範囲(左辺X軸位置) + private _maxRight: number; // 論理座標上の移動可能範囲(右辺X軸位置) + private _maxTop: number; // 論理座標上の移動可能範囲(上辺Y軸位置) + private _maxBottom: number; // 論理座標上の移動可能範囲(下辺Y軸位置) + private _maxScale: number; // 拡大率の最大値 + private _minScale: number; // 拡大率の最小値 +} + +// Namespace definition for compatibility. +import * as $ from './cubismviewmatrix'; +// eslint-disable-next-line @typescript-eslint/no-namespace +export namespace Live2DCubismFramework { + export const CubismViewMatrix = $.CubismViewMatrix; + export type CubismViewMatrix = $.CubismViewMatrix; } diff --git a/src/model/cubismmoc.ts b/src/model/cubismmoc.ts index 4733495..5e2acec 100644 --- a/src/model/cubismmoc.ts +++ b/src/model/cubismmoc.ts @@ -5,96 +5,101 @@ * that can be found at https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html. */ -import { Live2DCubismFramework as cubismmodel } from './cubismmodel'; -import CubismModel = cubismmodel.CubismModel; import { CSM_ASSERT } from '../utils/cubismdebug'; +import { CubismModel } from './cubismmodel'; -export namespace Live2DCubismFramework { +/** + * Mocデータの管理 + * + * Mocデータの管理を行うクラス。 + */ +export class CubismMoc { /** - * Mocデータの管理 - * - * Mocデータの管理を行うクラス。 + * Mocデータの作成 */ - export class CubismMoc { - /** - * Mocデータの作成 - */ - public static create(mocBytes: ArrayBuffer): CubismMoc { - let cubismMoc: CubismMoc = null; - const moc: Live2DCubismCore.Moc = Live2DCubismCore.Moc.fromArrayBuffer( - mocBytes - ); + public static create(mocBytes: ArrayBuffer): CubismMoc { + let cubismMoc: CubismMoc = null; + const moc: Live2DCubismCore.Moc = Live2DCubismCore.Moc.fromArrayBuffer( + mocBytes + ); - if (moc) { - cubismMoc = new CubismMoc(moc); - } - - return cubismMoc; + if (moc) { + cubismMoc = new CubismMoc(moc); } - /** - * Mocデータを削除 - * - * Mocデータを削除する - */ - public static delete(moc: CubismMoc): void { - moc._moc._release(); - moc._moc = null; - moc = null; - } - - /** - * モデルを作成する - * - * @return Mocデータから作成されたモデル - */ - createModel(): CubismModel { - let cubismModel: CubismModel = null; - - const model: Live2DCubismCore.Model = Live2DCubismCore.Model.fromMoc( - this._moc - ); - - if (model) { - cubismModel = new CubismModel(model); - cubismModel.initialize(); - - ++this._modelCount; - } - - return cubismModel; - } - - /** - * モデルを削除する - */ - deleteModel(model: CubismModel): void { - if (model != null) { - model.release(); - model = null; - --this._modelCount; - } - } - - /** - * コンストラクタ - */ - private constructor(moc: Live2DCubismCore.Moc) { - this._moc = moc; - this._modelCount = 0; - } - - /** - * デストラクタ相当の処理 - */ - public release(): void { - CSM_ASSERT(this._modelCount == 0); - - this._moc._release(); - this._moc = null; - } - - _moc: Live2DCubismCore.Moc; // Mocデータ - _modelCount: number; // Mocデータから作られたモデルの個数 + return cubismMoc; } + + /** + * Mocデータを削除 + * + * Mocデータを削除する + */ + public static delete(moc: CubismMoc): void { + moc._moc._release(); + moc._moc = null; + moc = null; + } + + /** + * モデルを作成する + * + * @return Mocデータから作成されたモデル + */ + createModel(): CubismModel { + let cubismModel: CubismModel = null; + + const model: Live2DCubismCore.Model = Live2DCubismCore.Model.fromMoc( + this._moc + ); + + if (model) { + cubismModel = new CubismModel(model); + cubismModel.initialize(); + + ++this._modelCount; + } + + return cubismModel; + } + + /** + * モデルを削除する + */ + deleteModel(model: CubismModel): void { + if (model != null) { + model.release(); + model = null; + --this._modelCount; + } + } + + /** + * コンストラクタ + */ + private constructor(moc: Live2DCubismCore.Moc) { + this._moc = moc; + this._modelCount = 0; + } + + /** + * デストラクタ相当の処理 + */ + public release(): void { + CSM_ASSERT(this._modelCount == 0); + + this._moc._release(); + this._moc = null; + } + + _moc: Live2DCubismCore.Moc; // Mocデータ + _modelCount: number; // Mocデータから作られたモデルの個数 +} + +// Namespace definition for compatibility. +import * as $ from './cubismmoc'; +// eslint-disable-next-line @typescript-eslint/no-namespace +export namespace Live2DCubismFramework { + export const CubismMoc = $.CubismMoc; + export type CubismMoc = $.CubismMoc; } diff --git a/src/model/cubismmodel.ts b/src/model/cubismmodel.ts index f520c19..5cfb14a 100644 --- a/src/model/cubismmodel.ts +++ b/src/model/cubismmodel.ts @@ -5,818 +5,814 @@ * that can be found at https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html. */ -import { Live2DCubismFramework as cubismrenderer } from '../rendering/cubismrenderer'; -import { Live2DCubismFramework as cubismid } from '../id/cubismid'; -import { Live2DCubismFramework as cubismframework } from '../live2dcubismframework'; -import { Live2DCubismFramework as csmmap } from '../type/csmmap'; -import { Live2DCubismFramework as csmvector } from '../type/csmvector'; +import { CubismIdHandle } from '../id/cubismid'; +import { CubismFramework } from '../live2dcubismframework'; +import { CubismBlendMode } from '../rendering/cubismrenderer'; +import { csmMap } from '../type/csmmap'; +import { csmVector } from '../type/csmvector'; import { CSM_ASSERT } from '../utils/cubismdebug'; -import CubismFramework = cubismframework.CubismFramework; -import CubismBlendMode = cubismrenderer.CubismBlendMode; -import csmVector = csmvector.csmVector; -import csmMap = csmmap.csmMap; -import CubismIdHandle = cubismid.CubismIdHandle; -export namespace Live2DCubismFramework { +/** + * モデル + * + * Mocデータから生成されるモデルのクラス。 + */ +export class CubismModel { /** - * モデル - * - * Mocデータから生成されるモデルのクラス。 + * モデルのパラメータの更新 */ - export class CubismModel { - /** - * モデルのパラメータの更新 - */ - public update(): void { - // Update model - this._model.update(); + public update(): void { + // Update model + this._model.update(); - this._model.drawables.resetDynamicFlags(); + this._model.drawables.resetDynamicFlags(); + } + + /** + * キャンバスの幅を取得する + */ + public getCanvasWidth(): number { + if (this._model == null) { + return 0.0; } - /** - * キャンバスの幅を取得する - */ - public getCanvasWidth(): number { - if (this._model == null) { - return 0.0; + return ( + this._model.canvasinfo.CanvasWidth / this._model.canvasinfo.PixelsPerUnit + ); + } + + /** + * キャンバスの高さを取得する + */ + public getCanvasHeight(): number { + if (this._model == null) { + return 0.0; + } + + return ( + this._model.canvasinfo.CanvasHeight / this._model.canvasinfo.PixelsPerUnit + ); + } + + /** + * パラメータを保存する + */ + public saveParameters(): void { + const parameterCount: number = this._model.parameters.count; + const savedParameterCount: number = this._savedParameters.getSize(); + + for (let i = 0; i < parameterCount; ++i) { + if (i < savedParameterCount) { + this._savedParameters.set(i, this._parameterValues[i]); + } else { + this._savedParameters.pushBack(this._parameterValues[i]); } - - return ( - this._model.canvasinfo.CanvasWidth / - this._model.canvasinfo.PixelsPerUnit - ); } + } - /** - * キャンバスの高さを取得する - */ - public getCanvasHeight(): number { - if (this._model == null) { - return 0.0; - } + /** + * モデルを取得 + */ + public getModel(): Live2DCubismCore.Model { + return this._model; + } - return ( - this._model.canvasinfo.CanvasHeight / - this._model.canvasinfo.PixelsPerUnit - ); - } + /** + * パーツのインデックスを取得 + * @param partId パーツのID + * @return パーツのインデックス + */ + public getPartIndex(partId: CubismIdHandle): number { + let partIndex: number; + const partCount: number = this._model.parts.count; - /** - * パラメータを保存する - */ - public saveParameters(): void { - const parameterCount: number = this._model.parameters.count; - const savedParameterCount: number = this._savedParameters.getSize(); - - for (let i = 0; i < parameterCount; ++i) { - if (i < savedParameterCount) { - this._savedParameters.set(i, this._parameterValues[i]); - } else { - this._savedParameters.pushBack(this._parameterValues[i]); - } + for (partIndex = 0; partIndex < partCount; ++partIndex) { + if (partId == this._partIds.at(partIndex)) { + return partIndex; } } - /** - * モデルを取得 - */ - public getModel(): Live2DCubismCore.Model { - return this._model; + // モデルに存在していない場合、非存在パーツIDリスト内にあるかを検索し、そのインデックスを返す + if (this._notExistPartId.isExist(partId)) { + return this._notExistPartId.getValue(partId); } - /** - * パーツのインデックスを取得 - * @param partId パーツのID - * @return パーツのインデックス - */ - public getPartIndex(partId: CubismIdHandle): number { - let partIndex: number; - const partCount: number = this._model.parts.count; + // 非存在パーツIDリストにない場合、新しく要素を追加する + partIndex = partCount + this._notExistPartId.getSize(); + this._notExistPartId.setValue(partId, partIndex); + this._notExistPartOpacities.appendKey(partIndex); - for (partIndex = 0; partIndex < partCount; ++partIndex) { - if (partId == this._partIds.at(partIndex)) { - return partIndex; - } - } + return partIndex; + } - // モデルに存在していない場合、非存在パーツIDリスト内にあるかを検索し、そのインデックスを返す - if (this._notExistPartId.isExist(partId)) { - return this._notExistPartId.getValue(partId); - } + /** + * パーツの個数の取得 + * @return パーツの個数 + */ + public getPartCount(): number { + const partCount: number = this._model.parts.count; + return partCount; + } - // 非存在パーツIDリストにない場合、新しく要素を追加する - partIndex = partCount + this._notExistPartId.getSize(); - this._notExistPartId.setValue(partId, partIndex); - this._notExistPartOpacities.appendKey(partIndex); - - return partIndex; + /** + * パーツの不透明度の設定(Index) + * @param partIndex パーツのインデックス + * @param opacity 不透明度 + */ + public setPartOpacityByIndex(partIndex: number, opacity: number): void { + if (this._notExistPartOpacities.isExist(partIndex)) { + this._notExistPartOpacities.setValue(partIndex, opacity); + return; } - /** - * パーツの個数の取得 - * @return パーツの個数 - */ - public getPartCount(): number { - const partCount: number = this._model.parts.count; - return partCount; + // インデックスの範囲内検知 + CSM_ASSERT(0 <= partIndex && partIndex < this.getPartCount()); + + this._partOpacities[partIndex] = opacity; + } + + /** + * パーツの不透明度の設定(Id) + * @param partId パーツのID + * @param opacity パーツの不透明度 + */ + public setPartOpacityById(partId: CubismIdHandle, opacity: number): void { + // 高速化のためにPartIndexを取得できる機構になっているが、外部からの設定の時は呼び出し頻度が低いため不要 + const index: number = this.getPartIndex(partId); + + if (index < 0) { + return; // パーツがないのでスキップ } - /** - * パーツの不透明度の設定(Index) - * @param partIndex パーツのインデックス - * @param opacity 不透明度 - */ - public setPartOpacityByIndex(partIndex: number, opacity: number): void { - if (this._notExistPartOpacities.isExist(partIndex)) { - this._notExistPartOpacities.setValue(partIndex, opacity); - return; - } + this.setPartOpacityByIndex(index, opacity); + } - // インデックスの範囲内検知 - CSM_ASSERT(0 <= partIndex && partIndex < this.getPartCount()); - - this._partOpacities[partIndex] = opacity; + /** + * パーツの不透明度の取得(index) + * @param partIndex パーツのインデックス + * @return パーツの不透明度 + */ + public getPartOpacityByIndex(partIndex: number): number { + if (this._notExistPartOpacities.isExist(partIndex)) { + // モデルに存在しないパーツIDの場合、非存在パーツリストから不透明度を返す。 + return this._notExistPartOpacities.getValue(partIndex); } - /** - * パーツの不透明度の設定(Id) - * @param partId パーツのID - * @param opacity パーツの不透明度 - */ - public setPartOpacityById(partId: CubismIdHandle, opacity: number): void { - // 高速化のためにPartIndexを取得できる機構になっているが、外部からの設定の時は呼び出し頻度が低いため不要 - const index: number = this.getPartIndex(partId); + // インデックスの範囲内検知 + CSM_ASSERT(0 <= partIndex && partIndex < this.getPartCount()); - if (index < 0) { - return; // パーツがないのでスキップ - } + return this._partOpacities[partIndex]; + } - this.setPartOpacityByIndex(index, opacity); + /** + * パーツの不透明度の取得(id) + * @param partId パーツのId + * @return パーツの不透明度 + */ + public getPartOpacityById(partId: CubismIdHandle): number { + // 高速化のためにPartIndexを取得できる機構になっているが、外部からの設定の時は呼び出し頻度が低いため不要 + const index: number = this.getPartIndex(partId); + + if (index < 0) { + return 0; // パーツが無いのでスキップ } - /** - * パーツの不透明度の取得(index) - * @param partIndex パーツのインデックス - * @return パーツの不透明度 - */ - public getPartOpacityByIndex(partIndex: number): number { - if (this._notExistPartOpacities.isExist(partIndex)) { - // モデルに存在しないパーツIDの場合、非存在パーツリストから不透明度を返す。 - return this._notExistPartOpacities.getValue(partIndex); + return this.getPartOpacityByIndex(index); + } + + /** + * パラメータのインデックスの取得 + * @param パラメータID + * @return パラメータのインデックス + */ + public getParameterIndex(parameterId: CubismIdHandle): number { + let parameterIndex: number; + const idCount: number = this._model.parameters.count; + + for (parameterIndex = 0; parameterIndex < idCount; ++parameterIndex) { + if (parameterId != this._parameterIds.at(parameterIndex)) { + continue; } - // インデックスの範囲内検知 - CSM_ASSERT(0 <= partIndex && partIndex < this.getPartCount()); - - return this._partOpacities[partIndex]; - } - - /** - * パーツの不透明度の取得(id) - * @param partId パーツのId - * @return パーツの不透明度 - */ - public getPartOpacityById(partId: CubismIdHandle): number { - // 高速化のためにPartIndexを取得できる機構になっているが、外部からの設定の時は呼び出し頻度が低いため不要 - const index: number = this.getPartIndex(partId); - - if (index < 0) { - return 0; // パーツが無いのでスキップ - } - - return this.getPartOpacityByIndex(index); - } - - /** - * パラメータのインデックスの取得 - * @param パラメータID - * @return パラメータのインデックス - */ - public getParameterIndex(parameterId: CubismIdHandle): number { - let parameterIndex: number; - const idCount: number = this._model.parameters.count; - - for (parameterIndex = 0; parameterIndex < idCount; ++parameterIndex) { - if (parameterId != this._parameterIds.at(parameterIndex)) { - continue; - } - - return parameterIndex; - } - - // モデルに存在していない場合、非存在パラメータIDリスト内を検索し、そのインデックスを返す - if (this._notExistParameterId.isExist(parameterId)) { - return this._notExistParameterId.getValue(parameterId); - } - - // 非存在パラメータIDリストにない場合新しく要素を追加する - parameterIndex = - this._model.parameters.count + this._notExistParameterId.getSize(); - - this._notExistParameterId.setValue(parameterId, parameterIndex); - this._notExistParameterValues.appendKey(parameterIndex); - return parameterIndex; } - /** - * パラメータの個数の取得 - * @return パラメータの個数 - */ - public getParameterCount(): number { - return this._model.parameters.count; + // モデルに存在していない場合、非存在パラメータIDリスト内を検索し、そのインデックスを返す + if (this._notExistParameterId.isExist(parameterId)) { + return this._notExistParameterId.getValue(parameterId); } - /** - * パラメータの最大値の取得 - * @param parameterIndex パラメータのインデックス - * @return パラメータの最大値 - */ - public getParameterMaximumValue(parameterIndex: number): number { - return this._model.parameters.maximumValues[parameterIndex]; + // 非存在パラメータIDリストにない場合新しく要素を追加する + parameterIndex = + this._model.parameters.count + this._notExistParameterId.getSize(); + + this._notExistParameterId.setValue(parameterId, parameterIndex); + this._notExistParameterValues.appendKey(parameterIndex); + + return parameterIndex; + } + + /** + * パラメータの個数の取得 + * @return パラメータの個数 + */ + public getParameterCount(): number { + return this._model.parameters.count; + } + + /** + * パラメータの最大値の取得 + * @param parameterIndex パラメータのインデックス + * @return パラメータの最大値 + */ + public getParameterMaximumValue(parameterIndex: number): number { + return this._model.parameters.maximumValues[parameterIndex]; + } + + /** + * パラメータの最小値の取得 + * @param parameterIndex パラメータのインデックス + * @return パラメータの最小値 + */ + public getParameterMinimumValue(parameterIndex: number): number { + return this._model.parameters.minimumValues[parameterIndex]; + } + + /** + * パラメータのデフォルト値の取得 + * @param parameterIndex パラメータのインデックス + * @return パラメータのデフォルト値 + */ + public getParameterDefaultValue(parameterIndex: number): number { + return this._model.parameters.defaultValues[parameterIndex]; + } + + /** + * パラメータの値の取得 + * @param parameterIndex パラメータのインデックス + * @return パラメータの値 + */ + public getParameterValueByIndex(parameterIndex: number): number { + if (this._notExistParameterValues.isExist(parameterIndex)) { + return this._notExistParameterValues.getValue(parameterIndex); } - /** - * パラメータの最小値の取得 - * @param parameterIndex パラメータのインデックス - * @return パラメータの最小値 - */ - public getParameterMinimumValue(parameterIndex: number): number { - return this._model.parameters.minimumValues[parameterIndex]; - } + // インデックスの範囲内検知 + CSM_ASSERT( + 0 <= parameterIndex && parameterIndex < this.getParameterCount() + ); - /** - * パラメータのデフォルト値の取得 - * @param parameterIndex パラメータのインデックス - * @return パラメータのデフォルト値 - */ - public getParameterDefaultValue(parameterIndex: number): number { - return this._model.parameters.defaultValues[parameterIndex]; - } + return this._parameterValues[parameterIndex]; + } - /** - * パラメータの値の取得 - * @param parameterIndex パラメータのインデックス - * @return パラメータの値 - */ - public getParameterValueByIndex(parameterIndex: number): number { - if (this._notExistParameterValues.isExist(parameterIndex)) { - return this._notExistParameterValues.getValue(parameterIndex); - } + /** + * パラメータの値の取得 + * @param parameterId パラメータのID + * @return パラメータの値 + */ + public getParameterValueById(parameterId: CubismIdHandle): number { + // 高速化のためにparameterIndexを取得できる機構になっているが、外部からの設定の時は呼び出し頻度が低いため不要 + const parameterIndex: number = this.getParameterIndex(parameterId); + return this.getParameterValueByIndex(parameterIndex); + } - // インデックスの範囲内検知 - CSM_ASSERT( - 0 <= parameterIndex && parameterIndex < this.getParameterCount() - ); - - return this._parameterValues[parameterIndex]; - } - - /** - * パラメータの値の取得 - * @param parameterId パラメータのID - * @return パラメータの値 - */ - public getParameterValueById(parameterId: CubismIdHandle): number { - // 高速化のためにparameterIndexを取得できる機構になっているが、外部からの設定の時は呼び出し頻度が低いため不要 - const parameterIndex: number = this.getParameterIndex(parameterId); - return this.getParameterValueByIndex(parameterIndex); - } - - /** - * パラメータの値の設定 - * @param parameterIndex パラメータのインデックス - * @param value パラメータの値 - * @param weight 重み - */ - public setParameterValueByIndex( - parameterIndex: number, - value: number, - weight = 1.0 - ): void { - if (this._notExistParameterValues.isExist(parameterIndex)) { - this._notExistParameterValues.setValue( - parameterIndex, - weight == 1 - ? value - : this._notExistParameterValues.getValue(parameterIndex) * - (1 - weight) + - value * weight - ); - - return; - } - - // インデックスの範囲内検知 - CSM_ASSERT( - 0 <= parameterIndex && parameterIndex < this.getParameterCount() - ); - - if (this._model.parameters.maximumValues[parameterIndex] < value) { - value = this._model.parameters.maximumValues[parameterIndex]; - } - if (this._model.parameters.minimumValues[parameterIndex] > value) { - value = this._model.parameters.minimumValues[parameterIndex]; - } - - this._parameterValues[parameterIndex] = + /** + * パラメータの値の設定 + * @param parameterIndex パラメータのインデックス + * @param value パラメータの値 + * @param weight 重み + */ + public setParameterValueByIndex( + parameterIndex: number, + value: number, + weight = 1.0 + ): void { + if (this._notExistParameterValues.isExist(parameterIndex)) { + this._notExistParameterValues.setValue( + parameterIndex, weight == 1 ? value - : (this._parameterValues[parameterIndex] = - this._parameterValues[parameterIndex] * (1 - weight) + - value * weight); - } - - /** - * パラメータの値の設定 - * @param parameterId パラメータのID - * @param value パラメータの値 - * @param weight 重み - */ - public setParameterValueById( - parameterId: CubismIdHandle, - value: number, - weight = 1.0 - ): void { - const index: number = this.getParameterIndex(parameterId); - this.setParameterValueByIndex(index, value, weight); - } - - /** - * パラメータの値の加算(index) - * @param parameterIndex パラメータインデックス - * @param value 加算する値 - * @param weight 重み - */ - public addParameterValueByIndex( - parameterIndex: number, - value: number, - weight = 1.0 - ): void { - this.setParameterValueByIndex( - parameterIndex, - this.getParameterValueByIndex(parameterIndex) + value * weight + : this._notExistParameterValues.getValue(parameterIndex) * + (1 - weight) + + value * weight ); + + return; } - /** - * パラメータの値の加算(id) - * @param parameterId パラメータID - * @param value 加算する値 - * @param weight 重み - */ - public addParameterValueById( - parameterId: any, - value: number, - weight = 1.0 - ): void { - const index: number = this.getParameterIndex(parameterId); - this.addParameterValueByIndex(index, value, weight); + // インデックスの範囲内検知 + CSM_ASSERT( + 0 <= parameterIndex && parameterIndex < this.getParameterCount() + ); + + if (this._model.parameters.maximumValues[parameterIndex] < value) { + value = this._model.parameters.maximumValues[parameterIndex]; + } + if (this._model.parameters.minimumValues[parameterIndex] > value) { + value = this._model.parameters.minimumValues[parameterIndex]; } - /** - * パラメータの値の乗算 - * @param parameterId パラメータのID - * @param value 乗算する値 - * @param weight 重み - */ - public multiplyParameterValueById( - parameterId: CubismIdHandle, - value: number, - weight = 1.0 - ): void { - const index: number = this.getParameterIndex(parameterId); - this.multiplyParameterValueByIndex(index, value, weight); - } - - /** - * パラメータの値の乗算 - * @param parameterIndex パラメータのインデックス - * @param value 乗算する値 - * @param weight 重み - */ - public multiplyParameterValueByIndex( - parameterIndex: number, - value: number, - weight = 1.0 - ): void { - this.setParameterValueByIndex( - parameterIndex, - this.getParameterValueByIndex(parameterIndex) * - (1.0 + (value - 1.0) * weight) - ); - } - - /** - * Drawableのインデックスの取得 - * @param drawableId DrawableのID - * @return Drawableのインデックス - */ - public getDrawableIndex(drawableId: CubismIdHandle): number { - const drawableCount = this._model.drawables.count; - - for ( - let drawableIndex = 0; - drawableIndex < drawableCount; - ++drawableIndex - ) { - if (this._drawableIds.at(drawableIndex) == drawableId) { - return drawableIndex; - } - } - - return -1; - } - - /** - * Drawableの個数の取得 - * @return drawableの個数 - */ - public getDrawableCount(): number { - const drawableCount = this._model.drawables.count; - return drawableCount; - } - - /** - * DrawableのIDを取得する - * @param drawableIndex Drawableのインデックス - * @return drawableのID - */ - public getDrawableId(drawableIndex: number): CubismIdHandle { - const parameterIds: string[] = this._model.drawables.ids; - return CubismFramework.getIdManager().getId(parameterIds[drawableIndex]); - } - - /** - * Drawableの描画順リストの取得 - * @return Drawableの描画順リスト - */ - public getDrawableRenderOrders(): Int32Array { - const renderOrders: Int32Array = this._model.drawables.renderOrders; - return renderOrders; - } - - /** - * Drawableのテクスチャインデックスリストの取得 - * @param drawableIndex Drawableのインデックス - * @return drawableのテクスチャインデックスリスト - */ - public getDrawableTextureIndices(drawableIndex: number): number { - const textureIndices: Int32Array = this._model.drawables.textureIndices; - return textureIndices[drawableIndex]; - } - - /** - * DrawableのVertexPositionsの変化情報の取得 - * - * 直近のCubismModel.update関数でDrawableの頂点情報が変化したかを取得する。 - * - * @param drawableIndex Drawableのインデックス - * @retval true Drawableの頂点情報が直近のCubismModel.update関数で変化した - * @retval false Drawableの頂点情報が直近のCubismModel.update関数で変化していない - */ - public getDrawableDynamicFlagVertexPositionsDidChange( - drawableIndex: number - ): boolean { - const dynamicFlags: Uint8Array = this._model.drawables.dynamicFlags; - return Live2DCubismCore.Utils.hasVertexPositionsDidChangeBit( - dynamicFlags[drawableIndex] - ); - } - - /** - * Drawableの頂点インデックスの個数の取得 - * @param drawableIndex Drawableのインデックス - * @return drawableの頂点インデックスの個数 - */ - public getDrawableVertexIndexCount(drawableIndex: number): number { - const indexCounts: Int32Array = this._model.drawables.indexCounts; - return indexCounts[drawableIndex]; - } - - /** - * Drawableの頂点の個数の取得 - * @param drawableIndex Drawableのインデックス - * @return drawableの頂点の個数 - */ - public getDrawableVertexCount(drawableIndex: number): number { - const vertexCounts = this._model.drawables.vertexCounts; - return vertexCounts[drawableIndex]; - } - - /** - * Drawableの頂点リストの取得 - * @param drawableIndex drawableのインデックス - * @return drawableの頂点リスト - */ - public getDrawableVertices(drawableIndex: number): Float32Array { - return this.getDrawableVertexPositions(drawableIndex); - } - - /** - * Drawableの頂点インデックスリストの取得 - * @param drarableIndex Drawableのインデックス - * @return drawableの頂点インデックスリスト - */ - public getDrawableVertexIndices(drawableIndex: number): Uint16Array { - const indicesArray: Uint16Array[] = this._model.drawables.indices; - return indicesArray[drawableIndex]; - } - - /** - * Drawableの頂点リストの取得 - * @param drawableIndex Drawableのインデックス - * @return drawableの頂点リスト - */ - public getDrawableVertexPositions(drawableIndex: number): Float32Array { - const verticesArray: Float32Array[] = this._model.drawables - .vertexPositions; - return verticesArray[drawableIndex]; - } - - /** - * Drawableの頂点のUVリストの取得 - * @param drawableIndex Drawableのインデックス - * @return drawableの頂点UVリスト - */ - public getDrawableVertexUvs(drawableIndex: number): Float32Array { - const uvsArray: Float32Array[] = this._model.drawables.vertexUvs; - return uvsArray[drawableIndex]; - } - - /** - * Drawableの不透明度の取得 - * @param drawableIndex Drawableのインデックス - * @return drawableの不透明度 - */ - public getDrawableOpacity(drawableIndex: number): number { - const opacities: Float32Array = this._model.drawables.opacities; - return opacities[drawableIndex]; - } - - /** - * Drawableのカリング情報の取得 - * @param drawableIndex Drawableのインデックス - * @return drawableのカリング情報 - */ - public getDrawableCulling(drawableIndex: number): boolean { - const constantFlags = this._model.drawables.constantFlags; - - return !Live2DCubismCore.Utils.hasIsDoubleSidedBit( - constantFlags[drawableIndex] - ); - } - - /** - * Drawableのブレンドモードを取得 - * @param drawableIndex Drawableのインデックス - * @return drawableのブレンドモード - */ - public getDrawableBlendMode(drawableIndex: number): CubismBlendMode { - const constantFlags = this._model.drawables.constantFlags; - - return Live2DCubismCore.Utils.hasBlendAdditiveBit( - constantFlags[drawableIndex] - ) - ? CubismBlendMode.CubismBlendMode_Additive - : Live2DCubismCore.Utils.hasBlendMultiplicativeBit( - constantFlags[drawableIndex] - ) - ? CubismBlendMode.CubismBlendMode_Multiplicative - : CubismBlendMode.CubismBlendMode_Normal; - } - - /** - * Drawableのマスクの反転使用の取得 - * - * Drawableのマスク使用時の反転設定を取得する。 - * マスクを使用しない場合は無視される。 - * - * @param drawableIndex Drawableのインデックス - * @return Drawableの反転設定 - */ - public getDrawableInvertedMaskBit(drawableIndex: number): boolean { - const constantFlags: Uint8Array = this._model.drawables.constantFlags; - - return Live2DCubismCore.Utils.hasIsInvertedMaskBit( - constantFlags[drawableIndex] - ); - } - - /** - * Drawableのクリッピングマスクリストの取得 - * @return Drawableのクリッピングマスクリスト - */ - public getDrawableMasks(): Int32Array[] { - const masks: Int32Array[] = this._model.drawables.masks; - return masks; - } - - /** - * Drawableのクリッピングマスクの個数リストの取得 - * @return Drawableのクリッピングマスクの個数リスト - */ - public getDrawableMaskCounts(): Int32Array { - const maskCounts: Int32Array = this._model.drawables.maskCounts; - return maskCounts; - } - - /** - * クリッピングマスクの使用状態 - * - * @return true クリッピングマスクを使用している - * @return false クリッピングマスクを使用していない - */ - public isUsingMasking(): boolean { - for (let d = 0; d < this._model.drawables.count; ++d) { - if (this._model.drawables.maskCounts[d] <= 0) { - continue; - } - return true; - } - return false; - } - - /** - * Drawableの表示情報を取得する - * - * @param drawableIndex Drawableのインデックス - * @return true Drawableが表示 - * @return false Drawableが非表示 - */ - public getDrawableDynamicFlagIsVisible(drawableIndex: number): boolean { - const dynamicFlags: Uint8Array = this._model.drawables.dynamicFlags; - return Live2DCubismCore.Utils.hasIsVisibleBit( - dynamicFlags[drawableIndex] - ); - } - - /** - * DrawableのDrawOrderの変化情報の取得 - * - * 直近のCubismModel.update関数でdrawableのdrawOrderが変化したかを取得する。 - * drawOrderはartMesh上で指定する0から1000の情報 - * @param drawableIndex drawableのインデックス - * @return true drawableの不透明度が直近のCubismModel.update関数で変化した - * @return false drawableの不透明度が直近のCubismModel.update関数で変化している - */ - public getDrawableDynamicFlagVisibilityDidChange( - drawableIndex: number - ): boolean { - const dynamicFlags: Uint8Array = this._model.drawables.dynamicFlags; - return Live2DCubismCore.Utils.hasVisibilityDidChangeBit( - dynamicFlags[drawableIndex] - ); - } - - /** - * Drawableの不透明度の変化情報の取得 - * - * 直近のCubismModel.update関数でdrawableの不透明度が変化したかを取得する。 - * - * @param drawableIndex drawableのインデックス - * @return true Drawableの不透明度が直近のCubismModel.update関数で変化した - * @return false Drawableの不透明度が直近のCubismModel.update関数で変化してない - */ - public getDrawableDynamicFlagOpacityDidChange( - drawableIndex: number - ): boolean { - const dynamicFlags: Uint8Array = this._model.drawables.dynamicFlags; - return Live2DCubismCore.Utils.hasOpacityDidChangeBit( - dynamicFlags[drawableIndex] - ); - } - - /** - * Drawableの描画順序の変化情報の取得 - * - * 直近のCubismModel.update関数でDrawableの描画の順序が変化したかを取得する。 - * - * @param drawableIndex Drawableのインデックス - * @return true Drawableの描画の順序が直近のCubismModel.update関数で変化した - * @return false Drawableの描画の順序が直近のCubismModel.update関数で変化してない - */ - public getDrawableDynamicFlagRenderOrderDidChange( - drawableIndex: number - ): boolean { - const dynamicFlags: Uint8Array = this._model.drawables.dynamicFlags; - return Live2DCubismCore.Utils.hasRenderOrderDidChangeBit( - dynamicFlags[drawableIndex] - ); - } - - /** - * 保存されたパラメータの読み込み - */ - public loadParameters(): void { - let parameterCount: number = this._model.parameters.count; - const savedParameterCount: number = this._savedParameters.getSize(); - - if (parameterCount > savedParameterCount) { - parameterCount = savedParameterCount; - } - - for (let i = 0; i < parameterCount; ++i) { - this._parameterValues[i] = this._savedParameters.at(i); - } - } - - /** - * 初期化する - */ - public initialize(): void { - CSM_ASSERT(this._model); - - this._parameterValues = this._model.parameters.values; - this._partOpacities = this._model.parts.opacities; - this._parameterMaximumValues = this._model.parameters.maximumValues; - this._parameterMinimumValues = this._model.parameters.minimumValues; - - { - const parameterIds: string[] = this._model.parameters.ids; - const parameterCount: number = this._model.parameters.count; - - this._parameterIds.prepareCapacity(parameterCount); - for (let i = 0; i < parameterCount; ++i) { - this._parameterIds.pushBack( - CubismFramework.getIdManager().getId(parameterIds[i]) - ); - } - } - - { - const partIds: string[] = this._model.parts.ids; - const partCount: number = this._model.parts.count; - - this._partIds.prepareCapacity(partCount); - for (let i = 0; i < partCount; ++i) { - this._partIds.pushBack( - CubismFramework.getIdManager().getId(partIds[i]) - ); - } - } - - { - const drawableIds: string[] = this._model.drawables.ids; - const drawableCount: number = this._model.drawables.count; - - this._drawableIds.prepareCapacity(drawableCount); - for (let i = 0; i < drawableCount; ++i) { - this._drawableIds.pushBack( - CubismFramework.getIdManager().getId(drawableIds[i]) - ); - } - } - } - - /** - * コンストラクタ - * @param model モデル - */ - public constructor(model: Live2DCubismCore.Model) { - this._model = model; - this._parameterValues = null; - this._parameterMaximumValues = null; - this._parameterMinimumValues = null; - this._partOpacities = null; - this._savedParameters = new csmVector(); - this._parameterIds = new csmVector(); - this._drawableIds = new csmVector(); - this._partIds = new csmVector(); - - this._notExistPartId = new csmMap(); - this._notExistParameterId = new csmMap(); - this._notExistParameterValues = new csmMap(); - this._notExistPartOpacities = new csmMap(); - } - - /** - * デストラクタ相当の処理 - */ - public release(): void { - this._model.release(); - this._model = null; - } - - private _notExistPartOpacities: csmMap; // 存在していないパーツの不透明度のリスト - private _notExistPartId: csmMap; // 存在していないパーツIDのリスト - - private _notExistParameterValues: csmMap; // 存在していないパラメータの値のリスト - private _notExistParameterId: csmMap; // 存在していないパラメータIDのリスト - - private _savedParameters: csmVector; // 保存されたパラメータ - - private _model: Live2DCubismCore.Model; // モデル - - private _parameterValues: Float32Array; // パラメータの値のリスト - private _parameterMaximumValues: Float32Array; // パラメータの最大値のリスト - private _parameterMinimumValues: Float32Array; // パラメータの最小値のリスト - - private _partOpacities: Float32Array; // パーツの不透明度のリスト - - private _parameterIds: csmVector; - private _partIds: csmVector; - private _drawableIds: csmVector; + this._parameterValues[parameterIndex] = + weight == 1 + ? value + : (this._parameterValues[parameterIndex] = + this._parameterValues[parameterIndex] * (1 - weight) + + value * weight); } + + /** + * パラメータの値の設定 + * @param parameterId パラメータのID + * @param value パラメータの値 + * @param weight 重み + */ + public setParameterValueById( + parameterId: CubismIdHandle, + value: number, + weight = 1.0 + ): void { + const index: number = this.getParameterIndex(parameterId); + this.setParameterValueByIndex(index, value, weight); + } + + /** + * パラメータの値の加算(index) + * @param parameterIndex パラメータインデックス + * @param value 加算する値 + * @param weight 重み + */ + public addParameterValueByIndex( + parameterIndex: number, + value: number, + weight = 1.0 + ): void { + this.setParameterValueByIndex( + parameterIndex, + this.getParameterValueByIndex(parameterIndex) + value * weight + ); + } + + /** + * パラメータの値の加算(id) + * @param parameterId パラメータID + * @param value 加算する値 + * @param weight 重み + */ + public addParameterValueById( + parameterId: any, + value: number, + weight = 1.0 + ): void { + const index: number = this.getParameterIndex(parameterId); + this.addParameterValueByIndex(index, value, weight); + } + + /** + * パラメータの値の乗算 + * @param parameterId パラメータのID + * @param value 乗算する値 + * @param weight 重み + */ + public multiplyParameterValueById( + parameterId: CubismIdHandle, + value: number, + weight = 1.0 + ): void { + const index: number = this.getParameterIndex(parameterId); + this.multiplyParameterValueByIndex(index, value, weight); + } + + /** + * パラメータの値の乗算 + * @param parameterIndex パラメータのインデックス + * @param value 乗算する値 + * @param weight 重み + */ + public multiplyParameterValueByIndex( + parameterIndex: number, + value: number, + weight = 1.0 + ): void { + this.setParameterValueByIndex( + parameterIndex, + this.getParameterValueByIndex(parameterIndex) * + (1.0 + (value - 1.0) * weight) + ); + } + + /** + * Drawableのインデックスの取得 + * @param drawableId DrawableのID + * @return Drawableのインデックス + */ + public getDrawableIndex(drawableId: CubismIdHandle): number { + const drawableCount = this._model.drawables.count; + + for ( + let drawableIndex = 0; + drawableIndex < drawableCount; + ++drawableIndex + ) { + if (this._drawableIds.at(drawableIndex) == drawableId) { + return drawableIndex; + } + } + + return -1; + } + + /** + * Drawableの個数の取得 + * @return drawableの個数 + */ + public getDrawableCount(): number { + const drawableCount = this._model.drawables.count; + return drawableCount; + } + + /** + * DrawableのIDを取得する + * @param drawableIndex Drawableのインデックス + * @return drawableのID + */ + public getDrawableId(drawableIndex: number): CubismIdHandle { + const parameterIds: string[] = this._model.drawables.ids; + return CubismFramework.getIdManager().getId(parameterIds[drawableIndex]); + } + + /** + * Drawableの描画順リストの取得 + * @return Drawableの描画順リスト + */ + public getDrawableRenderOrders(): Int32Array { + const renderOrders: Int32Array = this._model.drawables.renderOrders; + return renderOrders; + } + + /** + * Drawableのテクスチャインデックスリストの取得 + * @param drawableIndex Drawableのインデックス + * @return drawableのテクスチャインデックスリスト + */ + public getDrawableTextureIndices(drawableIndex: number): number { + const textureIndices: Int32Array = this._model.drawables.textureIndices; + return textureIndices[drawableIndex]; + } + + /** + * DrawableのVertexPositionsの変化情報の取得 + * + * 直近のCubismModel.update関数でDrawableの頂点情報が変化したかを取得する。 + * + * @param drawableIndex Drawableのインデックス + * @retval true Drawableの頂点情報が直近のCubismModel.update関数で変化した + * @retval false Drawableの頂点情報が直近のCubismModel.update関数で変化していない + */ + public getDrawableDynamicFlagVertexPositionsDidChange( + drawableIndex: number + ): boolean { + const dynamicFlags: Uint8Array = this._model.drawables.dynamicFlags; + return Live2DCubismCore.Utils.hasVertexPositionsDidChangeBit( + dynamicFlags[drawableIndex] + ); + } + + /** + * Drawableの頂点インデックスの個数の取得 + * @param drawableIndex Drawableのインデックス + * @return drawableの頂点インデックスの個数 + */ + public getDrawableVertexIndexCount(drawableIndex: number): number { + const indexCounts: Int32Array = this._model.drawables.indexCounts; + return indexCounts[drawableIndex]; + } + + /** + * Drawableの頂点の個数の取得 + * @param drawableIndex Drawableのインデックス + * @return drawableの頂点の個数 + */ + public getDrawableVertexCount(drawableIndex: number): number { + const vertexCounts = this._model.drawables.vertexCounts; + return vertexCounts[drawableIndex]; + } + + /** + * Drawableの頂点リストの取得 + * @param drawableIndex drawableのインデックス + * @return drawableの頂点リスト + */ + public getDrawableVertices(drawableIndex: number): Float32Array { + return this.getDrawableVertexPositions(drawableIndex); + } + + /** + * Drawableの頂点インデックスリストの取得 + * @param drarableIndex Drawableのインデックス + * @return drawableの頂点インデックスリスト + */ + public getDrawableVertexIndices(drawableIndex: number): Uint16Array { + const indicesArray: Uint16Array[] = this._model.drawables.indices; + return indicesArray[drawableIndex]; + } + + /** + * Drawableの頂点リストの取得 + * @param drawableIndex Drawableのインデックス + * @return drawableの頂点リスト + */ + public getDrawableVertexPositions(drawableIndex: number): Float32Array { + const verticesArray: Float32Array[] = this._model.drawables.vertexPositions; + return verticesArray[drawableIndex]; + } + + /** + * Drawableの頂点のUVリストの取得 + * @param drawableIndex Drawableのインデックス + * @return drawableの頂点UVリスト + */ + public getDrawableVertexUvs(drawableIndex: number): Float32Array { + const uvsArray: Float32Array[] = this._model.drawables.vertexUvs; + return uvsArray[drawableIndex]; + } + + /** + * Drawableの不透明度の取得 + * @param drawableIndex Drawableのインデックス + * @return drawableの不透明度 + */ + public getDrawableOpacity(drawableIndex: number): number { + const opacities: Float32Array = this._model.drawables.opacities; + return opacities[drawableIndex]; + } + + /** + * Drawableのカリング情報の取得 + * @param drawableIndex Drawableのインデックス + * @return drawableのカリング情報 + */ + public getDrawableCulling(drawableIndex: number): boolean { + const constantFlags = this._model.drawables.constantFlags; + + return !Live2DCubismCore.Utils.hasIsDoubleSidedBit( + constantFlags[drawableIndex] + ); + } + + /** + * Drawableのブレンドモードを取得 + * @param drawableIndex Drawableのインデックス + * @return drawableのブレンドモード + */ + public getDrawableBlendMode(drawableIndex: number): CubismBlendMode { + const constantFlags = this._model.drawables.constantFlags; + + return Live2DCubismCore.Utils.hasBlendAdditiveBit( + constantFlags[drawableIndex] + ) + ? CubismBlendMode.CubismBlendMode_Additive + : Live2DCubismCore.Utils.hasBlendMultiplicativeBit( + constantFlags[drawableIndex] + ) + ? CubismBlendMode.CubismBlendMode_Multiplicative + : CubismBlendMode.CubismBlendMode_Normal; + } + + /** + * Drawableのマスクの反転使用の取得 + * + * Drawableのマスク使用時の反転設定を取得する。 + * マスクを使用しない場合は無視される。 + * + * @param drawableIndex Drawableのインデックス + * @return Drawableの反転設定 + */ + public getDrawableInvertedMaskBit(drawableIndex: number): boolean { + const constantFlags: Uint8Array = this._model.drawables.constantFlags; + + return Live2DCubismCore.Utils.hasIsInvertedMaskBit( + constantFlags[drawableIndex] + ); + } + + /** + * Drawableのクリッピングマスクリストの取得 + * @return Drawableのクリッピングマスクリスト + */ + public getDrawableMasks(): Int32Array[] { + const masks: Int32Array[] = this._model.drawables.masks; + return masks; + } + + /** + * Drawableのクリッピングマスクの個数リストの取得 + * @return Drawableのクリッピングマスクの個数リスト + */ + public getDrawableMaskCounts(): Int32Array { + const maskCounts: Int32Array = this._model.drawables.maskCounts; + return maskCounts; + } + + /** + * クリッピングマスクの使用状態 + * + * @return true クリッピングマスクを使用している + * @return false クリッピングマスクを使用していない + */ + public isUsingMasking(): boolean { + for (let d = 0; d < this._model.drawables.count; ++d) { + if (this._model.drawables.maskCounts[d] <= 0) { + continue; + } + return true; + } + return false; + } + + /** + * Drawableの表示情報を取得する + * + * @param drawableIndex Drawableのインデックス + * @return true Drawableが表示 + * @return false Drawableが非表示 + */ + public getDrawableDynamicFlagIsVisible(drawableIndex: number): boolean { + const dynamicFlags: Uint8Array = this._model.drawables.dynamicFlags; + return Live2DCubismCore.Utils.hasIsVisibleBit(dynamicFlags[drawableIndex]); + } + + /** + * DrawableのDrawOrderの変化情報の取得 + * + * 直近のCubismModel.update関数でdrawableのdrawOrderが変化したかを取得する。 + * drawOrderはartMesh上で指定する0から1000の情報 + * @param drawableIndex drawableのインデックス + * @return true drawableの不透明度が直近のCubismModel.update関数で変化した + * @return false drawableの不透明度が直近のCubismModel.update関数で変化している + */ + public getDrawableDynamicFlagVisibilityDidChange( + drawableIndex: number + ): boolean { + const dynamicFlags: Uint8Array = this._model.drawables.dynamicFlags; + return Live2DCubismCore.Utils.hasVisibilityDidChangeBit( + dynamicFlags[drawableIndex] + ); + } + + /** + * Drawableの不透明度の変化情報の取得 + * + * 直近のCubismModel.update関数でdrawableの不透明度が変化したかを取得する。 + * + * @param drawableIndex drawableのインデックス + * @return true Drawableの不透明度が直近のCubismModel.update関数で変化した + * @return false Drawableの不透明度が直近のCubismModel.update関数で変化してない + */ + public getDrawableDynamicFlagOpacityDidChange( + drawableIndex: number + ): boolean { + const dynamicFlags: Uint8Array = this._model.drawables.dynamicFlags; + return Live2DCubismCore.Utils.hasOpacityDidChangeBit( + dynamicFlags[drawableIndex] + ); + } + + /** + * Drawableの描画順序の変化情報の取得 + * + * 直近のCubismModel.update関数でDrawableの描画の順序が変化したかを取得する。 + * + * @param drawableIndex Drawableのインデックス + * @return true Drawableの描画の順序が直近のCubismModel.update関数で変化した + * @return false Drawableの描画の順序が直近のCubismModel.update関数で変化してない + */ + public getDrawableDynamicFlagRenderOrderDidChange( + drawableIndex: number + ): boolean { + const dynamicFlags: Uint8Array = this._model.drawables.dynamicFlags; + return Live2DCubismCore.Utils.hasRenderOrderDidChangeBit( + dynamicFlags[drawableIndex] + ); + } + + /** + * 保存されたパラメータの読み込み + */ + public loadParameters(): void { + let parameterCount: number = this._model.parameters.count; + const savedParameterCount: number = this._savedParameters.getSize(); + + if (parameterCount > savedParameterCount) { + parameterCount = savedParameterCount; + } + + for (let i = 0; i < parameterCount; ++i) { + this._parameterValues[i] = this._savedParameters.at(i); + } + } + + /** + * 初期化する + */ + public initialize(): void { + CSM_ASSERT(this._model); + + this._parameterValues = this._model.parameters.values; + this._partOpacities = this._model.parts.opacities; + this._parameterMaximumValues = this._model.parameters.maximumValues; + this._parameterMinimumValues = this._model.parameters.minimumValues; + + { + const parameterIds: string[] = this._model.parameters.ids; + const parameterCount: number = this._model.parameters.count; + + this._parameterIds.prepareCapacity(parameterCount); + for (let i = 0; i < parameterCount; ++i) { + this._parameterIds.pushBack( + CubismFramework.getIdManager().getId(parameterIds[i]) + ); + } + } + + { + const partIds: string[] = this._model.parts.ids; + const partCount: number = this._model.parts.count; + + this._partIds.prepareCapacity(partCount); + for (let i = 0; i < partCount; ++i) { + this._partIds.pushBack( + CubismFramework.getIdManager().getId(partIds[i]) + ); + } + } + + { + const drawableIds: string[] = this._model.drawables.ids; + const drawableCount: number = this._model.drawables.count; + + this._drawableIds.prepareCapacity(drawableCount); + for (let i = 0; i < drawableCount; ++i) { + this._drawableIds.pushBack( + CubismFramework.getIdManager().getId(drawableIds[i]) + ); + } + } + } + + /** + * コンストラクタ + * @param model モデル + */ + public constructor(model: Live2DCubismCore.Model) { + this._model = model; + this._parameterValues = null; + this._parameterMaximumValues = null; + this._parameterMinimumValues = null; + this._partOpacities = null; + this._savedParameters = new csmVector(); + this._parameterIds = new csmVector(); + this._drawableIds = new csmVector(); + this._partIds = new csmVector(); + + this._notExistPartId = new csmMap(); + this._notExistParameterId = new csmMap(); + this._notExistParameterValues = new csmMap(); + this._notExistPartOpacities = new csmMap(); + } + + /** + * デストラクタ相当の処理 + */ + public release(): void { + this._model.release(); + this._model = null; + } + + private _notExistPartOpacities: csmMap; // 存在していないパーツの不透明度のリスト + private _notExistPartId: csmMap; // 存在していないパーツIDのリスト + + private _notExistParameterValues: csmMap; // 存在していないパラメータの値のリスト + private _notExistParameterId: csmMap; // 存在していないパラメータIDのリスト + + private _savedParameters: csmVector; // 保存されたパラメータ + + private _model: Live2DCubismCore.Model; // モデル + + private _parameterValues: Float32Array; // パラメータの値のリスト + private _parameterMaximumValues: Float32Array; // パラメータの最大値のリスト + private _parameterMinimumValues: Float32Array; // パラメータの最小値のリスト + + private _partOpacities: Float32Array; // パーツの不透明度のリスト + + private _parameterIds: csmVector; + private _partIds: csmVector; + private _drawableIds: csmVector; +} + +// Namespace definition for compatibility. +import * as $ from './cubismmodel'; +// eslint-disable-next-line @typescript-eslint/no-namespace +export namespace Live2DCubismFramework { + export const CubismModel = $.CubismModel; + export type CubismModel = $.CubismModel; } diff --git a/src/model/cubismmodeluserdata.ts b/src/model/cubismmodeluserdata.ts index f1381fa..407fb55 100644 --- a/src/model/cubismmodeluserdata.ts +++ b/src/model/cubismmodeluserdata.ts @@ -5,132 +5,132 @@ * that can be found at https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html. */ -import { Live2DCubismFramework as cubismmodeluserdatajson } from './cubismmodeluserdatajson'; -import { Live2DCubismFramework as cubismid } from '../id/cubismid'; -import { Live2DCubismFramework as csmstring } from '../type/csmstring'; -import { Live2DCubismFramework as csmvector } from '../type/csmvector'; -import { Live2DCubismFramework as cubismframework } from '../live2dcubismframework'; -import CubismFramework = cubismframework.CubismFramework; -import csmVector = csmvector.csmVector; -import csmString = csmstring.csmString; -import CubismIdHandle = cubismid.CubismIdHandle; -import CubismModelUserDataJson = cubismmodeluserdatajson.CubismModelUserDataJson; +import { CubismIdHandle } from '../id/cubismid'; +import { CubismFramework } from '../live2dcubismframework'; +import { csmString } from '../type/csmstring'; +import { csmVector } from '../type/csmvector'; +import { CubismModelUserDataJson } from './cubismmodeluserdatajson'; -export namespace Live2DCubismFramework { - const ArtMesh = 'ArtMesh'; +const ArtMesh = 'ArtMesh'; - /** - * ユーザーデータインターフェース - * - * Jsonから読み込んだユーザーデータを記録しておくための構造体 - */ - export class CubismModelUserDataNode { - targetType: CubismIdHandle; // ユーザーデータターゲットタイプ - targetId: CubismIdHandle; // ユーザーデータターゲットのID - value: csmString; // ユーザーデータ - } - - /** - * ユーザデータの管理クラス - * - * ユーザデータをロード、管理、検索インターフェイス、解放までを行う。 - */ - export class CubismModelUserData { - /** - * インスタンスの作成 - * - * @param buffer userdata3.jsonが読み込まれているバッファ - * @param size バッファのサイズ - * @return 作成されたインスタンス - */ - public static create( - buffer: ArrayBuffer, - size: number - ): CubismModelUserData { - const ret: CubismModelUserData = new CubismModelUserData(); - - ret.parseUserData(buffer, size); - - return ret; - } - - /** - * インスタンスを破棄する - * - * @param modelUserData 破棄するインスタンス - */ - public static delete(modelUserData: CubismModelUserData): void { - if (modelUserData != null) { - modelUserData.release(); - modelUserData = null; - } - } - - /** - * ArtMeshのユーザーデータのリストの取得 - * - * @return ユーザーデータリスト - */ - public getArtMeshUserDatas(): csmVector { - return this._artMeshUserDataNode; - } - - /** - * userdata3.jsonのパース - * - * @param buffer userdata3.jsonが読み込まれているバッファ - * @param size バッファのサイズ - */ - public parseUserData(buffer: ArrayBuffer, size: number): void { - let json: CubismModelUserDataJson = new CubismModelUserDataJson( - buffer, - size - ); - - const typeOfArtMesh = CubismFramework.getIdManager().getId(ArtMesh); - const nodeCount: number = json.getUserDataCount(); - - for (let i = 0; i < nodeCount; i++) { - const addNode: CubismModelUserDataNode = new CubismModelUserDataNode(); - - addNode.targetId = json.getUserDataId(i); - addNode.targetType = CubismFramework.getIdManager().getId( - json.getUserDataTargetType(i) - ); - addNode.value = new csmString(json.getUserDataValue(i)); - this._userDataNodes.pushBack(addNode); - - if (addNode.targetType == typeOfArtMesh) { - this._artMeshUserDataNode.pushBack(addNode); - } - } - - json.release(); - json = void 0; - } - - /** - * コンストラクタ - */ - public constructor() { - this._userDataNodes = new csmVector(); - this._artMeshUserDataNode = new csmVector(); - } - - /** - * デストラクタ相当の処理 - * - * ユーザーデータ構造体配列を解放する - */ - public release(): void { - for (let i = 0; i < this._userDataNodes.getSize(); ++i) { - this._userDataNodes.set(i, null); - } - - this._userDataNodes = null; - } - - private _userDataNodes: csmVector; // ユーザーデータ構造体配列 - private _artMeshUserDataNode: csmVector; // 閲覧リストの保持 - } +/** + * ユーザーデータインターフェース + * + * Jsonから読み込んだユーザーデータを記録しておくための構造体 + */ +export class CubismModelUserDataNode { + targetType: CubismIdHandle; // ユーザーデータターゲットタイプ + targetId: CubismIdHandle; // ユーザーデータターゲットのID + value: csmString; // ユーザーデータ +} + +/** + * ユーザデータの管理クラス + * + * ユーザデータをロード、管理、検索インターフェイス、解放までを行う。 + */ +export class CubismModelUserData { + /** + * インスタンスの作成 + * + * @param buffer userdata3.jsonが読み込まれているバッファ + * @param size バッファのサイズ + * @return 作成されたインスタンス + */ + public static create(buffer: ArrayBuffer, size: number): CubismModelUserData { + const ret: CubismModelUserData = new CubismModelUserData(); + + ret.parseUserData(buffer, size); + + return ret; + } + + /** + * インスタンスを破棄する + * + * @param modelUserData 破棄するインスタンス + */ + public static delete(modelUserData: CubismModelUserData): void { + if (modelUserData != null) { + modelUserData.release(); + modelUserData = null; + } + } + + /** + * ArtMeshのユーザーデータのリストの取得 + * + * @return ユーザーデータリスト + */ + public getArtMeshUserDatas(): csmVector { + return this._artMeshUserDataNode; + } + + /** + * userdata3.jsonのパース + * + * @param buffer userdata3.jsonが読み込まれているバッファ + * @param size バッファのサイズ + */ + public parseUserData(buffer: ArrayBuffer, size: number): void { + let json: CubismModelUserDataJson = new CubismModelUserDataJson( + buffer, + size + ); + + const typeOfArtMesh = CubismFramework.getIdManager().getId(ArtMesh); + const nodeCount: number = json.getUserDataCount(); + + for (let i = 0; i < nodeCount; i++) { + const addNode: CubismModelUserDataNode = new CubismModelUserDataNode(); + + addNode.targetId = json.getUserDataId(i); + addNode.targetType = CubismFramework.getIdManager().getId( + json.getUserDataTargetType(i) + ); + addNode.value = new csmString(json.getUserDataValue(i)); + this._userDataNodes.pushBack(addNode); + + if (addNode.targetType == typeOfArtMesh) { + this._artMeshUserDataNode.pushBack(addNode); + } + } + + json.release(); + json = void 0; + } + + /** + * コンストラクタ + */ + public constructor() { + this._userDataNodes = new csmVector(); + this._artMeshUserDataNode = new csmVector(); + } + + /** + * デストラクタ相当の処理 + * + * ユーザーデータ構造体配列を解放する + */ + public release(): void { + for (let i = 0; i < this._userDataNodes.getSize(); ++i) { + this._userDataNodes.set(i, null); + } + + this._userDataNodes = null; + } + + private _userDataNodes: csmVector; // ユーザーデータ構造体配列 + private _artMeshUserDataNode: csmVector; // 閲覧リストの保持 +} + +// Namespace definition for compatibility. +import * as $ from './cubismmodeluserdata'; +// eslint-disable-next-line @typescript-eslint/no-namespace +export namespace Live2DCubismFramework { + export const CubismModelUserData = $.CubismModelUserData; + export type CubismModelUserData = $.CubismModelUserData; + export const CubismModelUserDataNode = $.CubismModelUserDataNode; + export type CubismModelUserDataNode = $.CubismModelUserDataNode; } diff --git a/src/model/cubismmodeluserdatajson.ts b/src/model/cubismmodeluserdatajson.ts index 0d9334d..a691a70 100644 --- a/src/model/cubismmodeluserdatajson.ts +++ b/src/model/cubismmodeluserdatajson.ts @@ -5,110 +5,113 @@ * that can be found at https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html. */ -import { Live2DCubismFramework as cubismjson } from '../utils/cubismjson'; -import { Live2DCubismFramework as cubismid } from '../id/cubismid'; -import { Live2DCubismFramework as cubismframework } from '../live2dcubismframework'; -import CubismFramework = cubismframework.CubismFramework; -import CubismIdHandle = cubismid.CubismIdHandle; -import CubismJson = cubismjson.CubismJson; +import { CubismIdHandle } from '../id/cubismid'; +import { CubismFramework } from '../live2dcubismframework'; +import { CubismJson } from '../utils/cubismjson'; -export namespace Live2DCubismFramework { - const Meta = 'Meta'; - const UserDataCount = 'UserDataCount'; - const TotalUserDataSize = 'TotalUserDataSize'; - const UserData = 'UserData'; - const Target = 'Target'; - const Id = 'Id'; - const Value = 'Value'; +const Meta = 'Meta'; +const UserDataCount = 'UserDataCount'; +const TotalUserDataSize = 'TotalUserDataSize'; +const UserData = 'UserData'; +const Target = 'Target'; +const Id = 'Id'; +const Value = 'Value'; - export class CubismModelUserDataJson { - /** - * コンストラクタ - * @param buffer userdata3.jsonが読み込まれているバッファ - * @param size バッファのサイズ - */ - public constructor(buffer: ArrayBuffer, size: number) { - this._json = CubismJson.create(buffer, size); - } - - /** - * デストラクタ相当の処理 - */ - public release(): void { - CubismJson.delete(this._json); - } - - /** - * ユーザーデータ個数の取得 - * @return ユーザーデータの個数 - */ - public getUserDataCount(): number { - return this._json - .getRoot() - .getValueByString(Meta) - .getValueByString(UserDataCount) - .toInt(); - } - - /** - * ユーザーデータ総文字列数の取得 - * - * @return ユーザーデータ総文字列数 - */ - public getTotalUserDataSize(): number { - return this._json - .getRoot() - .getValueByString(Meta) - .getValueByString(TotalUserDataSize) - .toInt(); - } - - /** - * ユーザーデータのタイプの取得 - * - * @return ユーザーデータのタイプ - */ - public getUserDataTargetType(i: number): string { - return this._json - .getRoot() - .getValueByString(UserData) - .getValueByIndex(i) - .getValueByString(Target) - .getRawString(); - } - - /** - * ユーザーデータのターゲットIDの取得 - * - * @param i インデックス - * @return ユーザーデータターゲットID - */ - public getUserDataId(i: number): CubismIdHandle { - return CubismFramework.getIdManager().getId( - this._json - .getRoot() - .getValueByString(UserData) - .getValueByIndex(i) - .getValueByString(Id) - .getRawString() - ); - } - - /** - * ユーザーデータの文字列の取得 - * - * @param i インデックス - * @return ユーザーデータ - */ - public getUserDataValue(i: number): string { - return this._json - .getRoot() - .getValueByString(UserData) - .getValueByIndex(i) - .getValueByString(Value) - .getRawString(); - } - - private _json: CubismJson; +export class CubismModelUserDataJson { + /** + * コンストラクタ + * @param buffer userdata3.jsonが読み込まれているバッファ + * @param size バッファのサイズ + */ + public constructor(buffer: ArrayBuffer, size: number) { + this._json = CubismJson.create(buffer, size); } + + /** + * デストラクタ相当の処理 + */ + public release(): void { + CubismJson.delete(this._json); + } + + /** + * ユーザーデータ個数の取得 + * @return ユーザーデータの個数 + */ + public getUserDataCount(): number { + return this._json + .getRoot() + .getValueByString(Meta) + .getValueByString(UserDataCount) + .toInt(); + } + + /** + * ユーザーデータ総文字列数の取得 + * + * @return ユーザーデータ総文字列数 + */ + public getTotalUserDataSize(): number { + return this._json + .getRoot() + .getValueByString(Meta) + .getValueByString(TotalUserDataSize) + .toInt(); + } + + /** + * ユーザーデータのタイプの取得 + * + * @return ユーザーデータのタイプ + */ + public getUserDataTargetType(i: number): string { + return this._json + .getRoot() + .getValueByString(UserData) + .getValueByIndex(i) + .getValueByString(Target) + .getRawString(); + } + + /** + * ユーザーデータのターゲットIDの取得 + * + * @param i インデックス + * @return ユーザーデータターゲットID + */ + public getUserDataId(i: number): CubismIdHandle { + return CubismFramework.getIdManager().getId( + this._json + .getRoot() + .getValueByString(UserData) + .getValueByIndex(i) + .getValueByString(Id) + .getRawString() + ); + } + + /** + * ユーザーデータの文字列の取得 + * + * @param i インデックス + * @return ユーザーデータ + */ + public getUserDataValue(i: number): string { + return this._json + .getRoot() + .getValueByString(UserData) + .getValueByIndex(i) + .getValueByString(Value) + .getRawString(); + } + + private _json: CubismJson; +} + +// Namespace definition for compatibility. +import * as $ from './cubismmodeluserdatajson'; +// eslint-disable-next-line @typescript-eslint/no-namespace +export namespace Live2DCubismFramework { + export const CubismModelUserDataJson = $.CubismModelUserDataJson; + export type CubismModelUserDataJson = $.CubismModelUserDataJson; } diff --git a/src/model/cubismusermodel.ts b/src/model/cubismusermodel.ts index 56e9a59..d6e4cdf 100644 --- a/src/model/cubismusermodel.ts +++ b/src/model/cubismusermodel.ts @@ -5,449 +5,436 @@ * that can be found at https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html. */ -import { Live2DCubismFramework as cubismframework } from '../live2dcubismframework'; -import { Live2DCubismFramework as cubismmotionmanager } from '../motion/cubismmotionmanager'; -import { Live2DCubismFramework as cubismtargetpoint } from '../math/cubismtargetpoint'; -import { Live2DCubismFramework as cubismmodelmatrix } from '../math/cubismmodelmatrix'; -import { Live2DCubismFramework as cubismmoc } from './cubismmoc'; -import { Live2DCubismFramework as cubismmodel } from './cubismmodel'; -import { Live2DCubismFramework as acubismmotion } from '../motion/acubismmotion'; -import { Live2DCubismFramework as cubismmotion } from '../motion/cubismmotion'; -import { Live2DCubismFramework as cubismexpressionmotion } from '../motion/cubismexpressionmotion'; -import { Live2DCubismFramework as cubismpose } from '../effect/cubismpose'; -import { Live2DCubismFramework as cubismmodeluserdata } from './cubismmodeluserdata'; -import { Live2DCubismFramework as cubismphysics } from '../physics/cubismphysics'; -import { Live2DCubismFramework as cubismid } from '../id/cubismid'; -import { Live2DCubismFramework as csmstring } from '../type/csmstring'; -import { Live2DCubismFramework as cubismmotionqueuemanager } from '../motion/cubismmotionqueuemanager'; -import { Live2DCubismFramework as cubismbreath } from '../effect/cubismbreath'; -import { Live2DCubismFramework as cubismeyeblink } from '../effect/cubismeyeblink'; -import { Live2DCubismFramework as cubismrenderer_webgl } from '../rendering/cubismrenderer_webgl'; +import { CubismBreath } from '../effect/cubismbreath'; +import { CubismEyeBlink } from '../effect/cubismeyeblink'; +import { CubismPose } from '../effect/cubismpose'; +import { CubismIdHandle } from '../id/cubismid'; +import { Constant } from '../live2dcubismframework'; +import { CubismModelMatrix } from '../math/cubismmodelmatrix'; +import { CubismTargetPoint } from '../math/cubismtargetpoint'; +import { ACubismMotion, FinishedMotionCallback } from '../motion/acubismmotion'; +import { CubismExpressionMotion } from '../motion/cubismexpressionmotion'; +import { CubismMotion } from '../motion/cubismmotion'; +import { CubismMotionManager } from '../motion/cubismmotionmanager'; +import { CubismMotionQueueManager } from '../motion/cubismmotionqueuemanager'; +import { CubismPhysics } from '../physics/cubismphysics'; +import { CubismRenderer_WebGL } from '../rendering/cubismrenderer_webgl'; +import { csmString } from '../type/csmstring'; import { CubismLogError, CubismLogInfo } from '../utils/cubismdebug'; -import CubismRenderer_WebGL = cubismrenderer_webgl.CubismRenderer_WebGL; -import CubismEyeBlink = cubismeyeblink.CubismEyeBlink; -import CubismBreath = cubismbreath.CubismBreath; -import CubismMotionQueueManager = cubismmotionqueuemanager.CubismMotionQueueManager; -import csmString = csmstring.csmString; -import Constant = cubismframework.Constant; -import CubismIdHandle = cubismid.CubismIdHandle; -import CubismPhysics = cubismphysics.CubismPhysics; -import CubismModelUserData = cubismmodeluserdata.CubismModelUserData; -import CubismPose = cubismpose.CubismPose; -import CubismExpressionMotion = cubismexpressionmotion.CubismExpressionMotion; -import CubismMotion = cubismmotion.CubismMotion; -import ACubismMotion = acubismmotion.ACubismMotion; -import FinishedMotionCallback = acubismmotion.FinishedMotionCallback; -import CubismModel = cubismmodel.CubismModel; -import CubismMoc = cubismmoc.CubismMoc; -import CubismModelMatrix = cubismmodelmatrix.CubismModelMatrix; -import CubismTargetPoint = cubismtargetpoint.CubismTargetPoint; -import CubismMotionManager = cubismmotionmanager.CubismMotionManager; +import { CubismMoc } from './cubismmoc'; +import { CubismModel } from './cubismmodel'; +import { CubismModelUserData } from './cubismmodeluserdata'; -export namespace Live2DCubismFramework { +/** + * ユーザーが実際に使用するモデル + * + * ユーザーが実際に使用するモデルの基底クラス。これを継承してユーザーが実装する。 + */ +export class CubismUserModel { /** - * ユーザーが実際に使用するモデル + * 初期化状態の取得 * - * ユーザーが実際に使用するモデルの基底クラス。これを継承してユーザーが実装する。 + * 初期化されている状態か? + * + * @return true 初期化されている + * @return false 初期化されていない */ - export class CubismUserModel { - /** - * 初期化状態の取得 - * - * 初期化されている状態か? - * - * @return true 初期化されている - * @return false 初期化されていない - */ - public isInitialized(): boolean { - return this._initialized; + public isInitialized(): boolean { + return this._initialized; + } + + /** + * 初期化状態の設定 + * + * 初期化状態を設定する。 + * + * @param v 初期化状態 + */ + public setInitialized(v: boolean): void { + this._initialized = v; + } + + /** + * 更新状態の取得 + * + * 更新されている状態か? + * + * @return true 更新されている + * @return false 更新されていない + */ + public isUpdating(): boolean { + return this._updating; + } + + /** + * 更新状態の設定 + * + * 更新状態を設定する + * + * @param v 更新状態 + */ + public setUpdating(v: boolean): void { + this._updating = v; + } + + /** + * マウスドラッグ情報の設定 + * @param ドラッグしているカーソルのX位置 + * @param ドラッグしているカーソルのY位置 + */ + public setDragging(x: number, y: number): void { + this._dragManager.set(x, y); + } + + /** + * 加速度の情報を設定する + * @param x X軸方向の加速度 + * @param y Y軸方向の加速度 + * @param z Z軸方向の加速度 + */ + public setAcceleration(x: number, y: number, z: number): void { + this._accelerationX = x; + this._accelerationY = y; + this._accelerationZ = z; + } + + /** + * モデル行列を取得する + * @return モデル行列 + */ + public getModelMatrix(): CubismModelMatrix { + return this._modelMatrix; + } + + /** + * 不透明度の設定 + * @param a 不透明度 + */ + public setOpacity(a: number): void { + this._opacity = a; + } + + /** + * 不透明度の取得 + * @return 不透明度 + */ + public getOpacity(): number { + return this._opacity; + } + + /** + * モデルデータを読み込む + * + * @param buffer moc3ファイルが読み込まれているバッファ + */ + public loadModel(buffer: ArrayBuffer) { + this._moc = CubismMoc.create(buffer); + this._model = this._moc.createModel(); + this._model.saveParameters(); + + if (this._moc == null || this._model == null) { + CubismLogError('Failed to CreateModel().'); + return; } - /** - * 初期化状態の設定 - * - * 初期化状態を設定する。 - * - * @param v 初期化状態 - */ - public setInitialized(v: boolean): void { - this._initialized = v; + this._modelMatrix = new CubismModelMatrix( + this._model.getCanvasWidth(), + this._model.getCanvasHeight() + ); + } + + /** + * モーションデータを読み込む + * @param buffer motion3.jsonファイルが読み込まれているバッファ + * @param size バッファのサイズ + * @param name モーションの名前 + * @param onFinishedMotionHandler モーション再生終了時に呼び出されるコールバック関数 + * @return モーションクラス + */ + public loadMotion = ( + buffer: ArrayBuffer, + size: number, + name: string, + onFinishedMotionHandler?: FinishedMotionCallback + ) => CubismMotion.create(buffer, size, onFinishedMotionHandler); + + /** + * 表情データの読み込み + * @param buffer expファイルが読み込まれているバッファ + * @param size バッファのサイズ + * @param name 表情の名前 + */ + public loadExpression( + buffer: ArrayBuffer, + size: number, + name: string + ): ACubismMotion { + return CubismExpressionMotion.create(buffer, size); + } + + /** + * ポーズデータの読み込み + * @param buffer pose3.jsonが読み込まれているバッファ + * @param size バッファのサイズ + */ + public loadPose(buffer: ArrayBuffer, size: number): void { + this._pose = CubismPose.create(buffer, size); + } + + /** + * モデルに付属するユーザーデータを読み込む + * @param buffer userdata3.jsonが読み込まれているバッファ + * @param size バッファのサイズ + */ + public loadUserData(buffer: ArrayBuffer, size: number): void { + this._modelUserData = CubismModelUserData.create(buffer, size); + } + + /** + * 物理演算データの読み込み + * @param buffer physics3.jsonが読み込まれているバッファ + * @param size バッファのサイズ + */ + public loadPhysics(buffer: ArrayBuffer, size: number): void { + this._physics = CubismPhysics.create(buffer, size); + } + + /** + * 当たり判定の取得 + * @param drawableId 検証したいDrawableのID + * @param pointX X位置 + * @param pointY Y位置 + * @return true ヒットしている + * @return false ヒットしていない + */ + public isHit( + drawableId: CubismIdHandle, + pointX: number, + pointY: number + ): boolean { + const drawIndex: number = this._model.getDrawableIndex(drawableId); + + if (drawIndex < 0) { + return false; // 存在しない場合はfalse } - /** - * 更新状態の取得 - * - * 更新されている状態か? - * - * @return true 更新されている - * @return false 更新されていない - */ - public isUpdating(): boolean { - return this._updating; - } + const count: number = this._model.getDrawableVertexCount(drawIndex); + const vertices: Float32Array = this._model.getDrawableVertices(drawIndex); - /** - * 更新状態の設定 - * - * 更新状態を設定する - * - * @param v 更新状態 - */ - public setUpdating(v: boolean): void { - this._updating = v; - } + let left: number = vertices[0]; + let right: number = vertices[0]; + let top: number = vertices[1]; + let bottom: number = vertices[1]; - /** - * マウスドラッグ情報の設定 - * @param ドラッグしているカーソルのX位置 - * @param ドラッグしているカーソルのY位置 - */ - public setDragging(x: number, y: number): void { - this._dragManager.set(x, y); - } + for (let j = 1; j < count; ++j) { + const x = vertices[Constant.vertexOffset + j * Constant.vertexStep]; + const y = vertices[Constant.vertexOffset + j * Constant.vertexStep + 1]; - /** - * 加速度の情報を設定する - * @param x X軸方向の加速度 - * @param y Y軸方向の加速度 - * @param z Z軸方向の加速度 - */ - public setAcceleration(x: number, y: number, z: number): void { - this._accelerationX = x; - this._accelerationY = y; - this._accelerationZ = z; - } - - /** - * モデル行列を取得する - * @return モデル行列 - */ - public getModelMatrix(): CubismModelMatrix { - return this._modelMatrix; - } - - /** - * 不透明度の設定 - * @param a 不透明度 - */ - public setOpacity(a: number): void { - this._opacity = a; - } - - /** - * 不透明度の取得 - * @return 不透明度 - */ - public getOpacity(): number { - return this._opacity; - } - - /** - * モデルデータを読み込む - * - * @param buffer moc3ファイルが読み込まれているバッファ - */ - public loadModel(buffer: ArrayBuffer) { - this._moc = CubismMoc.create(buffer); - this._model = this._moc.createModel(); - this._model.saveParameters(); - - if (this._moc == null || this._model == null) { - CubismLogError('Failed to CreateModel().'); - return; + if (x < left) { + left = x; // Min x } - this._modelMatrix = new CubismModelMatrix( - this._model.getCanvasWidth(), - this._model.getCanvasHeight() - ); - } - - /** - * モーションデータを読み込む - * @param buffer motion3.jsonファイルが読み込まれているバッファ - * @param size バッファのサイズ - * @param name モーションの名前 - * @param onFinishedMotionHandler モーション再生終了時に呼び出されるコールバック関数 - * @return モーションクラス - */ - public loadMotion = ( - buffer: ArrayBuffer, - size: number, - name: string, - onFinishedMotionHandler?: FinishedMotionCallback - ) => CubismMotion.create(buffer, size, onFinishedMotionHandler); - - /** - * 表情データの読み込み - * @param buffer expファイルが読み込まれているバッファ - * @param size バッファのサイズ - * @param name 表情の名前 - */ - public loadExpression( - buffer: ArrayBuffer, - size: number, - name: string - ): ACubismMotion { - return CubismExpressionMotion.create(buffer, size); - } - - /** - * ポーズデータの読み込み - * @param buffer pose3.jsonが読み込まれているバッファ - * @param size バッファのサイズ - */ - public loadPose(buffer: ArrayBuffer, size: number): void { - this._pose = CubismPose.create(buffer, size); - } - - /** - * モデルに付属するユーザーデータを読み込む - * @param buffer userdata3.jsonが読み込まれているバッファ - * @param size バッファのサイズ - */ - public loadUserData(buffer: ArrayBuffer, size: number): void { - this._modelUserData = CubismModelUserData.create(buffer, size); - } - - /** - * 物理演算データの読み込み - * @param buffer physics3.jsonが読み込まれているバッファ - * @param size バッファのサイズ - */ - public loadPhysics(buffer: ArrayBuffer, size: number): void { - this._physics = CubismPhysics.create(buffer, size); - } - - /** - * 当たり判定の取得 - * @param drawableId 検証したいDrawableのID - * @param pointX X位置 - * @param pointY Y位置 - * @return true ヒットしている - * @return false ヒットしていない - */ - public isHit( - drawableId: CubismIdHandle, - pointX: number, - pointY: number - ): boolean { - const drawIndex: number = this._model.getDrawableIndex(drawableId); - - if (drawIndex < 0) { - return false; // 存在しない場合はfalse + if (x > right) { + right = x; // Max x } - const count: number = this._model.getDrawableVertexCount(drawIndex); - const vertices: Float32Array = this._model.getDrawableVertices(drawIndex); - - let left: number = vertices[0]; - let right: number = vertices[0]; - let top: number = vertices[1]; - let bottom: number = vertices[1]; - - for (let j = 1; j < count; ++j) { - const x = vertices[Constant.vertexOffset + j * Constant.vertexStep]; - const y = vertices[Constant.vertexOffset + j * Constant.vertexStep + 1]; - - if (x < left) { - left = x; // Min x - } - - if (x > right) { - right = x; // Max x - } - - if (y < top) { - top = y; // Min y - } - - if (y > bottom) { - bottom = y; // Max y - } + if (y < top) { + top = y; // Min y } - const tx: number = this._modelMatrix.invertTransformX(pointX); - const ty: number = this._modelMatrix.invertTransformY(pointY); - - return left <= tx && tx <= right && top <= ty && ty <= bottom; - } - - /** - * モデルの取得 - * @return モデル - */ - public getModel(): CubismModel { - return this._model; - } - - /** - * レンダラの取得 - * @return レンダラ - */ - public getRenderer(): CubismRenderer_WebGL { - return this._renderer; - } - - /** - * レンダラを作成して初期化を実行する - */ - public createRenderer(): void { - if (this._renderer) { - this.deleteRenderer(); - } - - this._renderer = new CubismRenderer_WebGL(); - this._renderer.initialize(this._model); - } - - /** - * レンダラの解放 - */ - public deleteRenderer(): void { - if (this._renderer != null) { - this._renderer.release(); - this._renderer = null; + if (y > bottom) { + bottom = y; // Max y } } - /** - * イベント発火時の標準処理 - * - * Eventが再生処理時にあった場合の処理をする。 - * 継承で上書きすることを想定している。 - * 上書きしない場合はログ出力をする。 - * - * @param eventValue 発火したイベントの文字列データ - */ - public motionEventFired(eventValue: csmString): void { - CubismLogInfo('{0}', eventValue.s); - } + const tx: number = this._modelMatrix.invertTransformX(pointX); + const ty: number = this._modelMatrix.invertTransformY(pointY); - /** - * イベント用のコールバック - * - * CubismMotionQueueManagerにイベント用に登録するためのCallback。 - * CubismUserModelの継承先のEventFiredを呼ぶ。 - * - * @param caller 発火したイベントを管理していたモーションマネージャー、比較用 - * @param eventValue 発火したイベントの文字列データ - * @param customData CubismUserModelを継承したインスタンスを想定 - */ - public static cubismDefaultMotionEventCallback( - caller: CubismMotionQueueManager, - eventValue: csmString, - customData: CubismUserModel - ): void { - const model: CubismUserModel = customData; + return left <= tx && tx <= right && top <= ty && ty <= bottom; + } - if (model != null) { - model.motionEventFired(eventValue); - } - } + /** + * モデルの取得 + * @return モデル + */ + public getModel(): CubismModel { + return this._model; + } - /** - * コンストラクタ - */ - public constructor() { - // 各変数初期化 - this._moc = null; - this._model = null; - this._motionManager = null; - this._expressionManager = null; - this._eyeBlink = null; - this._breath = null; - this._modelMatrix = null; - this._pose = null; - this._dragManager = null; - this._physics = null; - this._modelUserData = null; - this._initialized = false; - this._updating = false; - this._opacity = 1.0; - this._lipsync = true; - this._lastLipSyncValue = 0.0; - this._dragX = 0.0; - this._dragY = 0.0; - this._accelerationX = 0.0; - this._accelerationY = 0.0; - this._accelerationZ = 0.0; - this._debugMode = false; - this._renderer = null; - - // モーションマネージャーを作成 - this._motionManager = new CubismMotionManager(); - this._motionManager.setEventCallback( - CubismUserModel.cubismDefaultMotionEventCallback, - this - ); - - // 表情マネージャーを作成 - this._expressionManager = new CubismMotionManager(); - - // ドラッグによるアニメーション - this._dragManager = new CubismTargetPoint(); - } - - /** - * デストラクタに相当する処理 - */ - public release() { - if (this._motionManager != null) { - this._motionManager.release(); - this._motionManager = null; - } - - if (this._expressionManager != null) { - this._expressionManager.release(); - this._expressionManager = null; - } - - if (this._moc != null) { - this._moc.deleteModel(this._model); - this._moc.release(); - this._moc = null; - } - - this._modelMatrix = null; - - CubismPose.delete(this._pose); - CubismEyeBlink.delete(this._eyeBlink); - CubismBreath.delete(this._breath); - - this._dragManager = null; - - CubismPhysics.delete(this._physics); - CubismModelUserData.delete(this._modelUserData); + /** + * レンダラの取得 + * @return レンダラ + */ + public getRenderer(): CubismRenderer_WebGL { + return this._renderer; + } + /** + * レンダラを作成して初期化を実行する + */ + public createRenderer(): void { + if (this._renderer) { this.deleteRenderer(); } - protected _moc: CubismMoc; // Mocデータ - protected _model: CubismModel; // Modelインスタンス - - protected _motionManager: CubismMotionManager; // モーション管理 - protected _expressionManager: CubismMotionManager; // 表情管理 - protected _eyeBlink: CubismEyeBlink; // 自動まばたき - protected _breath: CubismBreath; // 呼吸 - protected _modelMatrix: CubismModelMatrix; // モデル行列 - protected _pose: CubismPose; // ポーズ管理 - protected _dragManager: CubismTargetPoint; // マウスドラッグ - protected _physics: CubismPhysics; // 物理演算 - protected _modelUserData: CubismModelUserData; // ユーザーデータ - - protected _initialized: boolean; // 初期化されたかどうか - protected _updating: boolean; // 更新されたかどうか - protected _opacity: number; // 不透明度 - protected _lipsync: boolean; // リップシンクするかどうか - protected _lastLipSyncValue: number; // 最後のリップシンクの制御地 - protected _dragX: number; // マウスドラッグのX位置 - protected _dragY: number; // マウスドラッグのY位置 - protected _accelerationX: number; // X軸方向の加速度 - protected _accelerationY: number; // Y軸方向の加速度 - protected _accelerationZ: number; // Z軸方向の加速度 - protected _debugMode: boolean; // デバッグモードかどうか - - private _renderer: CubismRenderer_WebGL; // レンダラ + this._renderer = new CubismRenderer_WebGL(); + this._renderer.initialize(this._model); } + + /** + * レンダラの解放 + */ + public deleteRenderer(): void { + if (this._renderer != null) { + this._renderer.release(); + this._renderer = null; + } + } + + /** + * イベント発火時の標準処理 + * + * Eventが再生処理時にあった場合の処理をする。 + * 継承で上書きすることを想定している。 + * 上書きしない場合はログ出力をする。 + * + * @param eventValue 発火したイベントの文字列データ + */ + public motionEventFired(eventValue: csmString): void { + CubismLogInfo('{0}', eventValue.s); + } + + /** + * イベント用のコールバック + * + * CubismMotionQueueManagerにイベント用に登録するためのCallback。 + * CubismUserModelの継承先のEventFiredを呼ぶ。 + * + * @param caller 発火したイベントを管理していたモーションマネージャー、比較用 + * @param eventValue 発火したイベントの文字列データ + * @param customData CubismUserModelを継承したインスタンスを想定 + */ + public static cubismDefaultMotionEventCallback( + caller: CubismMotionQueueManager, + eventValue: csmString, + customData: CubismUserModel + ): void { + const model: CubismUserModel = customData; + + if (model != null) { + model.motionEventFired(eventValue); + } + } + + /** + * コンストラクタ + */ + public constructor() { + // 各変数初期化 + this._moc = null; + this._model = null; + this._motionManager = null; + this._expressionManager = null; + this._eyeBlink = null; + this._breath = null; + this._modelMatrix = null; + this._pose = null; + this._dragManager = null; + this._physics = null; + this._modelUserData = null; + this._initialized = false; + this._updating = false; + this._opacity = 1.0; + this._lipsync = true; + this._lastLipSyncValue = 0.0; + this._dragX = 0.0; + this._dragY = 0.0; + this._accelerationX = 0.0; + this._accelerationY = 0.0; + this._accelerationZ = 0.0; + this._debugMode = false; + this._renderer = null; + + // モーションマネージャーを作成 + this._motionManager = new CubismMotionManager(); + this._motionManager.setEventCallback( + CubismUserModel.cubismDefaultMotionEventCallback, + this + ); + + // 表情マネージャーを作成 + this._expressionManager = new CubismMotionManager(); + + // ドラッグによるアニメーション + this._dragManager = new CubismTargetPoint(); + } + + /** + * デストラクタに相当する処理 + */ + public release() { + if (this._motionManager != null) { + this._motionManager.release(); + this._motionManager = null; + } + + if (this._expressionManager != null) { + this._expressionManager.release(); + this._expressionManager = null; + } + + if (this._moc != null) { + this._moc.deleteModel(this._model); + this._moc.release(); + this._moc = null; + } + + this._modelMatrix = null; + + CubismPose.delete(this._pose); + CubismEyeBlink.delete(this._eyeBlink); + CubismBreath.delete(this._breath); + + this._dragManager = null; + + CubismPhysics.delete(this._physics); + CubismModelUserData.delete(this._modelUserData); + + this.deleteRenderer(); + } + + protected _moc: CubismMoc; // Mocデータ + protected _model: CubismModel; // Modelインスタンス + + protected _motionManager: CubismMotionManager; // モーション管理 + protected _expressionManager: CubismMotionManager; // 表情管理 + protected _eyeBlink: CubismEyeBlink; // 自動まばたき + protected _breath: CubismBreath; // 呼吸 + protected _modelMatrix: CubismModelMatrix; // モデル行列 + protected _pose: CubismPose; // ポーズ管理 + protected _dragManager: CubismTargetPoint; // マウスドラッグ + protected _physics: CubismPhysics; // 物理演算 + protected _modelUserData: CubismModelUserData; // ユーザーデータ + + protected _initialized: boolean; // 初期化されたかどうか + protected _updating: boolean; // 更新されたかどうか + protected _opacity: number; // 不透明度 + protected _lipsync: boolean; // リップシンクするかどうか + protected _lastLipSyncValue: number; // 最後のリップシンクの制御地 + protected _dragX: number; // マウスドラッグのX位置 + protected _dragY: number; // マウスドラッグのY位置 + protected _accelerationX: number; // X軸方向の加速度 + protected _accelerationY: number; // Y軸方向の加速度 + protected _accelerationZ: number; // Z軸方向の加速度 + protected _debugMode: boolean; // デバッグモードかどうか + + private _renderer: CubismRenderer_WebGL; // レンダラ +} + +// Namespace definition for compatibility. +import * as $ from './cubismusermodel'; +// eslint-disable-next-line @typescript-eslint/no-namespace +export namespace Live2DCubismFramework { + export const CubismUserModel = $.CubismUserModel; + export type CubismUserModel = $.CubismUserModel; } diff --git a/src/motion/acubismmotion.ts b/src/motion/acubismmotion.ts index 7391e3d..c22c664 100644 --- a/src/motion/acubismmotion.ts +++ b/src/motion/acubismmotion.ts @@ -5,274 +5,276 @@ * that can be found at https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html. */ -import { Live2DCubismFramework as cubismmath } from '../math/cubismmath'; -import { Live2DCubismFramework as cubismmodel } from '../model/cubismmodel'; -import { Live2DCubismFramework as cubismmotionqueueentry } from './cubismmotionqueueentry'; -import { Live2DCubismFramework as csmstring } from '../type/csmstring'; -import { Live2DCubismFramework as csmvector } from '../type/csmvector'; +import { CubismMath } from '../math/cubismmath'; +import { CubismModel } from '../model/cubismmodel'; +import { csmString } from '../type/csmstring'; +import { csmVector } from '../type/csmvector'; import { CSM_ASSERT } from '../utils/cubismdebug'; -import csmVector = csmvector.csmVector; -import csmString = csmstring.csmString; -import CubismMotionQueueEntry = cubismmotionqueueentry.CubismMotionQueueEntry; -import CubismModel = cubismmodel.CubismModel; -import CubismMath = cubismmath.CubismMath; +import { CubismMotionQueueEntry } from './cubismmotionqueueentry'; -export namespace Live2DCubismFramework { - /** モーション再生終了コールバック関数定義 */ - export type FinishedMotionCallback = (self: ACubismMotion) => void; +/** モーション再生終了コールバック関数定義 */ +export type FinishedMotionCallback = (self: ACubismMotion) => void; + +/** + * モーションの抽象基底クラス + * + * モーションの抽象基底クラス。MotionQueueManagerによってモーションの再生を管理する。 + */ +export abstract class ACubismMotion { + /** + * インスタンスの破棄 + */ + public static delete(motion: ACubismMotion): void { + motion.release(); + motion = void 0; + motion = null; + } /** - * モーションの抽象基底クラス - * - * モーションの抽象基底クラス。MotionQueueManagerによってモーションの再生を管理する。 + * コンストラクタ */ - export abstract class ACubismMotion { - /** - * インスタンスの破棄 - */ - public static delete(motion: ACubismMotion): void { - motion.release(); - motion = void 0; - motion = null; - } - - /** - * コンストラクタ - */ - public constructor() { - this._fadeInSeconds = -1.0; - this._fadeOutSeconds = -1.0; - this._weight = 1.0; - this._offsetSeconds = 0.0; // 再生の開始時刻 - this._firedEventValues = new csmVector(); - } - - /** - * デストラクタ相当の処理 - */ - public release(): void { - this._weight = 0.0; - } - - /** - * モデルのパラメータ - * @param model 対象のモデル - * @param motionQueueEntry CubismMotionQueueManagerで管理されているモーション - * @param userTimeSeconds デルタ時間の積算値[秒] - */ - public updateParameters( - model: CubismModel, - motionQueueEntry: CubismMotionQueueEntry, - userTimeSeconds: number - ): void { - if (!motionQueueEntry.isAvailable() || motionQueueEntry.isFinished()) { - return; - } - - if (!motionQueueEntry.isStarted()) { - motionQueueEntry.setIsStarted(true); - motionQueueEntry.setStartTime(userTimeSeconds - this._offsetSeconds); // モーションの開始時刻を記録 - motionQueueEntry.setFadeInStartTime(userTimeSeconds); // フェードインの開始時刻 - - const duration: number = this.getDuration(); - - if (motionQueueEntry.getEndTime() < 0) { - // 開始していないうちに終了設定している場合がある。 - motionQueueEntry.setEndTime( - duration <= 0 ? -1 : motionQueueEntry.getStartTime() + duration - ); - // duration == -1 の場合はループする - } - } - - let fadeWeight: number = this._weight; // 現在の値と掛け合わせる割合 - - //---- フェードイン・アウトの処理 ---- - // 単純なサイン関数でイージングする - const fadeIn: number = - this._fadeInSeconds == 0.0 - ? 1.0 - : CubismMath.getEasingSine( - (userTimeSeconds - motionQueueEntry.getFadeInStartTime()) / - this._fadeInSeconds - ); - - const fadeOut: number = - this._fadeOutSeconds == 0.0 || motionQueueEntry.getEndTime() < 0.0 - ? 1.0 - : CubismMath.getEasingSine( - (motionQueueEntry.getEndTime() - userTimeSeconds) / - this._fadeOutSeconds - ); - - fadeWeight = fadeWeight * fadeIn * fadeOut; - - motionQueueEntry.setState(userTimeSeconds, fadeWeight); - - CSM_ASSERT(0.0 <= fadeWeight && fadeWeight <= 1.0); - - //---- 全てのパラメータIDをループする ---- - this.doUpdateParameters( - model, - userTimeSeconds, - fadeWeight, - motionQueueEntry - ); - - // 後処理 - // 終了時刻を過ぎたら終了フラグを立てる(CubismMotionQueueManager) - if ( - motionQueueEntry.getEndTime() > 0 && - motionQueueEntry.getEndTime() < userTimeSeconds - ) { - motionQueueEntry.setIsFinished(true); // 終了 - } - } - - /** - * フェードインの時間を設定する - * @param fadeInSeconds フェードインにかかる時間[秒] - */ - public setFadeInTime(fadeInSeconds: number): void { - this._fadeInSeconds = fadeInSeconds; - } - - /** - * フェードアウトの時間を設定する - * @param fadeOutSeconds フェードアウトにかかる時間[秒] - */ - public setFadeOutTime(fadeOutSeconds: number): void { - this._fadeOutSeconds = fadeOutSeconds; - } - - /** - * フェードアウトにかかる時間の取得 - * @return フェードアウトにかかる時間[秒] - */ - public getFadeOutTime(): number { - return this._fadeOutSeconds; - } - - /** - * フェードインにかかる時間の取得 - * @return フェードインにかかる時間[秒] - */ - public getFadeInTime(): number { - return this._fadeInSeconds; - } - - /** - * モーション適用の重みの設定 - * @param weight 重み(0.0 - 1.0) - */ - public setWeight(weight: number): void { - this._weight = weight; - } - - /** - * モーション適用の重みの取得 - * @return 重み(0.0 - 1.0) - */ - public getWeight(): number { - return this._weight; - } - - /** - * モーションの長さの取得 - * @return モーションの長さ[秒] - * - * @note ループの時は「-1」。 - * ループでない場合は、オーバーライドする。 - * 正の値の時は取得される時間で終了する。 - * 「-1」の時は外部から停止命令がない限り終わらない処理となる。 - */ - public getDuration(): number { - return -1.0; - } - - /** - * モーションのループ1回分の長さの取得 - * @return モーションのループ一回分の長さ[秒] - * - * @note ループしない場合は、getDuration()と同じ値を返す - * ループ一回分の長さが定義できない場合(プログラム的に動き続けるサブクラスなど)の場合は「-1」を返す - */ - public getLoopDuration(): number { - return -1.0; - } - - /** - * モーション再生の開始時刻の設定 - * @param offsetSeconds モーション再生の開始時刻[秒] - */ - public setOffsetTime(offsetSeconds: number): void { - this._offsetSeconds = offsetSeconds; - } - - /** - * モデルのパラメータ更新 - * - * イベント発火のチェック。 - * 入力する時間は呼ばれるモーションタイミングを0とした秒数で行う。 - * - * @param beforeCheckTimeSeconds 前回のイベントチェック時間[秒] - * @param motionTimeSeconds 今回の再生時間[秒] - */ - public getFiredEvent( - beforeCheckTimeSeconds: number, - motionTimeSeconds: number - ): csmVector { - return this._firedEventValues; - } - - /** - * モーションを更新して、モデルにパラメータ値を反映する - * @param model 対象のモデル - * @param userTimeSeconds デルタ時間の積算値[秒] - * @param weight モーションの重み - * @param motionQueueEntry CubismMotionQueueManagerで管理されているモーション - * @return true モデルへパラメータ値の反映あり - * @return false モデルへのパラメータ値の反映なし(モーションの変化なし) - */ - public abstract doUpdateParameters( - model: CubismModel, - userTimeSeconds: number, - weight: number, - motionQueueEntry: CubismMotionQueueEntry - ): void; - - /** - * モーション再生終了コールバックの登録 - * - * モーション再生終了コールバックを登録する。 - * isFinishedフラグを設定するタイミングで呼び出される。 - * 以下の状態の際には呼び出されない: - * 1. 再生中のモーションが「ループ」として設定されているとき - * 2. コールバックが登録されていない時 - * - * @param onFinishedMotionHandler モーション再生終了コールバック関数 - */ - public setFinishedMotionHandler = ( - onFinishedMotionHandler: FinishedMotionCallback - ) => (this._onFinishedMotion = onFinishedMotionHandler); - - /** - * モーション再生終了コールバックの取得 - * - * モーション再生終了コールバックを取得する。 - * - * @return 登録されているモーション再生終了コールバック関数 - */ - public getFinishedMotionHandler = () => this._onFinishedMotion; - - public _fadeInSeconds: number; // フェードインにかかる時間[秒] - public _fadeOutSeconds: number; // フェードアウトにかかる時間[秒] - public _weight: number; // モーションの重み - public _offsetSeconds: number; // モーション再生の開始時間[秒] - - public _firedEventValues: csmVector; - - // モーション再生終了コールバック関数 - public _onFinishedMotion?: FinishedMotionCallback; + public constructor() { + this._fadeInSeconds = -1.0; + this._fadeOutSeconds = -1.0; + this._weight = 1.0; + this._offsetSeconds = 0.0; // 再生の開始時刻 + this._firedEventValues = new csmVector(); } + + /** + * デストラクタ相当の処理 + */ + public release(): void { + this._weight = 0.0; + } + + /** + * モデルのパラメータ + * @param model 対象のモデル + * @param motionQueueEntry CubismMotionQueueManagerで管理されているモーション + * @param userTimeSeconds デルタ時間の積算値[秒] + */ + public updateParameters( + model: CubismModel, + motionQueueEntry: CubismMotionQueueEntry, + userTimeSeconds: number + ): void { + if (!motionQueueEntry.isAvailable() || motionQueueEntry.isFinished()) { + return; + } + + if (!motionQueueEntry.isStarted()) { + motionQueueEntry.setIsStarted(true); + motionQueueEntry.setStartTime(userTimeSeconds - this._offsetSeconds); // モーションの開始時刻を記録 + motionQueueEntry.setFadeInStartTime(userTimeSeconds); // フェードインの開始時刻 + + const duration: number = this.getDuration(); + + if (motionQueueEntry.getEndTime() < 0) { + // 開始していないうちに終了設定している場合がある。 + motionQueueEntry.setEndTime( + duration <= 0 ? -1 : motionQueueEntry.getStartTime() + duration + ); + // duration == -1 の場合はループする + } + } + + let fadeWeight: number = this._weight; // 現在の値と掛け合わせる割合 + + //---- フェードイン・アウトの処理 ---- + // 単純なサイン関数でイージングする + const fadeIn: number = + this._fadeInSeconds == 0.0 + ? 1.0 + : CubismMath.getEasingSine( + (userTimeSeconds - motionQueueEntry.getFadeInStartTime()) / + this._fadeInSeconds + ); + + const fadeOut: number = + this._fadeOutSeconds == 0.0 || motionQueueEntry.getEndTime() < 0.0 + ? 1.0 + : CubismMath.getEasingSine( + (motionQueueEntry.getEndTime() - userTimeSeconds) / + this._fadeOutSeconds + ); + + fadeWeight = fadeWeight * fadeIn * fadeOut; + + motionQueueEntry.setState(userTimeSeconds, fadeWeight); + + CSM_ASSERT(0.0 <= fadeWeight && fadeWeight <= 1.0); + + //---- 全てのパラメータIDをループする ---- + this.doUpdateParameters( + model, + userTimeSeconds, + fadeWeight, + motionQueueEntry + ); + + // 後処理 + // 終了時刻を過ぎたら終了フラグを立てる(CubismMotionQueueManager) + if ( + motionQueueEntry.getEndTime() > 0 && + motionQueueEntry.getEndTime() < userTimeSeconds + ) { + motionQueueEntry.setIsFinished(true); // 終了 + } + } + + /** + * フェードインの時間を設定する + * @param fadeInSeconds フェードインにかかる時間[秒] + */ + public setFadeInTime(fadeInSeconds: number): void { + this._fadeInSeconds = fadeInSeconds; + } + + /** + * フェードアウトの時間を設定する + * @param fadeOutSeconds フェードアウトにかかる時間[秒] + */ + public setFadeOutTime(fadeOutSeconds: number): void { + this._fadeOutSeconds = fadeOutSeconds; + } + + /** + * フェードアウトにかかる時間の取得 + * @return フェードアウトにかかる時間[秒] + */ + public getFadeOutTime(): number { + return this._fadeOutSeconds; + } + + /** + * フェードインにかかる時間の取得 + * @return フェードインにかかる時間[秒] + */ + public getFadeInTime(): number { + return this._fadeInSeconds; + } + + /** + * モーション適用の重みの設定 + * @param weight 重み(0.0 - 1.0) + */ + public setWeight(weight: number): void { + this._weight = weight; + } + + /** + * モーション適用の重みの取得 + * @return 重み(0.0 - 1.0) + */ + public getWeight(): number { + return this._weight; + } + + /** + * モーションの長さの取得 + * @return モーションの長さ[秒] + * + * @note ループの時は「-1」。 + * ループでない場合は、オーバーライドする。 + * 正の値の時は取得される時間で終了する。 + * 「-1」の時は外部から停止命令がない限り終わらない処理となる。 + */ + public getDuration(): number { + return -1.0; + } + + /** + * モーションのループ1回分の長さの取得 + * @return モーションのループ一回分の長さ[秒] + * + * @note ループしない場合は、getDuration()と同じ値を返す + * ループ一回分の長さが定義できない場合(プログラム的に動き続けるサブクラスなど)の場合は「-1」を返す + */ + public getLoopDuration(): number { + return -1.0; + } + + /** + * モーション再生の開始時刻の設定 + * @param offsetSeconds モーション再生の開始時刻[秒] + */ + public setOffsetTime(offsetSeconds: number): void { + this._offsetSeconds = offsetSeconds; + } + + /** + * モデルのパラメータ更新 + * + * イベント発火のチェック。 + * 入力する時間は呼ばれるモーションタイミングを0とした秒数で行う。 + * + * @param beforeCheckTimeSeconds 前回のイベントチェック時間[秒] + * @param motionTimeSeconds 今回の再生時間[秒] + */ + public getFiredEvent( + beforeCheckTimeSeconds: number, + motionTimeSeconds: number + ): csmVector { + return this._firedEventValues; + } + + /** + * モーションを更新して、モデルにパラメータ値を反映する + * @param model 対象のモデル + * @param userTimeSeconds デルタ時間の積算値[秒] + * @param weight モーションの重み + * @param motionQueueEntry CubismMotionQueueManagerで管理されているモーション + * @return true モデルへパラメータ値の反映あり + * @return false モデルへのパラメータ値の反映なし(モーションの変化なし) + */ + public abstract doUpdateParameters( + model: CubismModel, + userTimeSeconds: number, + weight: number, + motionQueueEntry: CubismMotionQueueEntry + ): void; + + /** + * モーション再生終了コールバックの登録 + * + * モーション再生終了コールバックを登録する。 + * isFinishedフラグを設定するタイミングで呼び出される。 + * 以下の状態の際には呼び出されない: + * 1. 再生中のモーションが「ループ」として設定されているとき + * 2. コールバックが登録されていない時 + * + * @param onFinishedMotionHandler モーション再生終了コールバック関数 + */ + public setFinishedMotionHandler = ( + onFinishedMotionHandler: FinishedMotionCallback + ) => (this._onFinishedMotion = onFinishedMotionHandler); + + /** + * モーション再生終了コールバックの取得 + * + * モーション再生終了コールバックを取得する。 + * + * @return 登録されているモーション再生終了コールバック関数 + */ + public getFinishedMotionHandler = () => this._onFinishedMotion; + + public _fadeInSeconds: number; // フェードインにかかる時間[秒] + public _fadeOutSeconds: number; // フェードアウトにかかる時間[秒] + public _weight: number; // モーションの重み + public _offsetSeconds: number; // モーション再生の開始時間[秒] + + public _firedEventValues: csmVector; + + // モーション再生終了コールバック関数 + public _onFinishedMotion?: FinishedMotionCallback; +} + +// Namespace definition for compatibility. +import * as $ from './acubismmotion'; +// eslint-disable-next-line @typescript-eslint/no-namespace +export namespace Live2DCubismFramework { + export const ACubismMotion = $.ACubismMotion; + export type ACubismMotion = $.ACubismMotion; + export type FinishedMotionCallback = $.FinishedMotionCallback; } diff --git a/src/motion/cubismexpressionmotion.ts b/src/motion/cubismexpressionmotion.ts index 3fd679c..9ffa94e 100644 --- a/src/motion/cubismexpressionmotion.ts +++ b/src/motion/cubismexpressionmotion.ts @@ -5,195 +5,195 @@ * that can be found at https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html. */ -import { Live2DCubismFramework as acubismmotion } from './acubismmotion'; -import { Live2DCubismFramework as cubismjson } from '../utils/cubismjson'; -import { Live2DCubismFramework as cubismid } from '../id/cubismid'; -import { Live2DCubismFramework as cubismframework } from '../live2dcubismframework'; -import { Live2DCubismFramework as cubismmodel } from '../model/cubismmodel'; -import { Live2DCubismFramework as cubismmotionqueueentry } from './cubismmotionqueueentry'; -import { Live2DCubismFramework as csmvector } from '../type/csmvector'; -import JsonFloat = cubismjson.JsonFloat; -import csmVector = csmvector.csmVector; -import CubismMotionQueueEntry = cubismmotionqueueentry.CubismMotionQueueEntry; -import CubismModel = cubismmodel.CubismModel; -import CubismFramework = cubismframework.CubismFramework; -import CubismIdHandle = cubismid.CubismIdHandle; -import CubismJson = cubismjson.CubismJson; -import Value = cubismjson.Value; -import ACubismMotion = acubismmotion.ACubismMotion; +import { CubismIdHandle } from '../id/cubismid'; +import { CubismFramework } from '../live2dcubismframework'; +import { CubismModel } from '../model/cubismmodel'; +import { csmVector } from '../type/csmvector'; +import { CubismJson, Value } from '../utils/cubismjson'; +import { ACubismMotion } from './acubismmotion'; +import { CubismMotionQueueEntry } from './cubismmotionqueueentry'; -export namespace Live2DCubismFramework { - // exp3.jsonのキーとデフォルト - const ExpressionKeyFadeIn = 'FadeInTime'; - const ExpressionKeyFadeOut = 'FadeOutTime'; - const ExpressionKeyParameters = 'Parameters'; - const ExpressionKeyId = 'Id'; - const ExpressionKeyValue = 'Value'; - const ExpressionKeyBlend = 'Blend'; - const BlendValueAdd = 'Add'; - const BlendValueMultiply = 'Multiply'; - const BlendValueOverwrite = 'Overwrite'; - const DefaultFadeTime = 1.0; +// exp3.jsonのキーとデフォルト +const ExpressionKeyFadeIn = 'FadeInTime'; +const ExpressionKeyFadeOut = 'FadeOutTime'; +const ExpressionKeyParameters = 'Parameters'; +const ExpressionKeyId = 'Id'; +const ExpressionKeyValue = 'Value'; +const ExpressionKeyBlend = 'Blend'; +const BlendValueAdd = 'Add'; +const BlendValueMultiply = 'Multiply'; +const BlendValueOverwrite = 'Overwrite'; +const DefaultFadeTime = 1.0; +/** + * 表情のモーション + * + * 表情のモーションクラス。 + */ +export class CubismExpressionMotion extends ACubismMotion { /** - * 表情のモーション - * - * 表情のモーションクラス。 + * インスタンスを作成する。 + * @param buffer expファイルが読み込まれているバッファ + * @param size バッファのサイズ + * @return 作成されたインスタンス */ - export class CubismExpressionMotion extends ACubismMotion { - /** - * インスタンスを作成する。 - * @param buffer expファイルが読み込まれているバッファ - * @param size バッファのサイズ - * @return 作成されたインスタンス - */ - public static create( - buffer: ArrayBuffer, - size: number - ): CubismExpressionMotion { - const expression: CubismExpressionMotion = new CubismExpressionMotion(); + public static create( + buffer: ArrayBuffer, + size: number + ): CubismExpressionMotion { + const expression: CubismExpressionMotion = new CubismExpressionMotion(); - const json: CubismJson = CubismJson.create(buffer, size); - const root: Value = json.getRoot(); + const json: CubismJson = CubismJson.create(buffer, size); + const root: Value = json.getRoot(); - expression.setFadeInTime( - root.getValueByString(ExpressionKeyFadeIn).toFloat(DefaultFadeTime) - ); // フェードイン - expression.setFadeOutTime( - root.getValueByString(ExpressionKeyFadeOut).toFloat(DefaultFadeTime) - ); // フェードアウト + expression.setFadeInTime( + root.getValueByString(ExpressionKeyFadeIn).toFloat(DefaultFadeTime) + ); // フェードイン + expression.setFadeOutTime( + root.getValueByString(ExpressionKeyFadeOut).toFloat(DefaultFadeTime) + ); // フェードアウト - // 各パラメータについて - const parameterCount = root + // 各パラメータについて + const parameterCount = root + .getValueByString(ExpressionKeyParameters) + .getSize(); + expression._parameters.prepareCapacity(parameterCount); + + for (let i = 0; i < parameterCount; ++i) { + const param: Value = root .getValueByString(ExpressionKeyParameters) - .getSize(); - expression._parameters.prepareCapacity(parameterCount); + .getValueByIndex(i); + const parameterId: CubismIdHandle = CubismFramework.getIdManager().getId( + param.getValueByString(ExpressionKeyId).getRawString() + ); // パラメータID - for (let i = 0; i < parameterCount; ++i) { - const param: Value = root - .getValueByString(ExpressionKeyParameters) - .getValueByIndex(i); - const parameterId: CubismIdHandle = CubismFramework.getIdManager().getId( - param.getValueByString(ExpressionKeyId).getRawString() - ); // パラメータID + const value: number = param + .getValueByString(ExpressionKeyValue) + .toFloat(); // 値 - const value: number = param - .getValueByString(ExpressionKeyValue) - .toFloat(); // 値 + // 計算方法の設定 + let blendType: ExpressionBlendType; - // 計算方法の設定 - let blendType: ExpressionBlendType; - - if ( - param.getValueByString(ExpressionKeyBlend).isNull() || - param.getValueByString(ExpressionKeyBlend).getString() == - BlendValueAdd - ) { - blendType = ExpressionBlendType.ExpressionBlendType_Add; - } else if ( - param.getValueByString(ExpressionKeyBlend).getString() == - BlendValueMultiply - ) { - blendType = ExpressionBlendType.ExpressionBlendType_Multiply; - } else if ( - param.getValueByString(ExpressionKeyBlend).getString() == - BlendValueOverwrite - ) { - blendType = ExpressionBlendType.ExpressionBlendType_Overwrite; - } else { - // その他 仕様にない値を設定した時は加算モードにすることで復旧 - blendType = ExpressionBlendType.ExpressionBlendType_Add; - } - - // 設定オブジェクトを作成してリストに追加する - const item: ExpressionParameter = new ExpressionParameter(); - - item.parameterId = parameterId; - item.blendType = blendType; - item.value = value; - - expression._parameters.pushBack(item); + if ( + param.getValueByString(ExpressionKeyBlend).isNull() || + param.getValueByString(ExpressionKeyBlend).getString() == BlendValueAdd + ) { + blendType = ExpressionBlendType.ExpressionBlendType_Add; + } else if ( + param.getValueByString(ExpressionKeyBlend).getString() == + BlendValueMultiply + ) { + blendType = ExpressionBlendType.ExpressionBlendType_Multiply; + } else if ( + param.getValueByString(ExpressionKeyBlend).getString() == + BlendValueOverwrite + ) { + blendType = ExpressionBlendType.ExpressionBlendType_Overwrite; + } else { + // その他 仕様にない値を設定した時は加算モードにすることで復旧 + blendType = ExpressionBlendType.ExpressionBlendType_Add; } - CubismJson.delete(json); // JSONデータは不要になったら削除する - return expression; + // 設定オブジェクトを作成してリストに追加する + const item: ExpressionParameter = new ExpressionParameter(); + + item.parameterId = parameterId; + item.blendType = blendType; + item.value = value; + + expression._parameters.pushBack(item); } - /** - * モデルのパラメータの更新の実行 - * @param model 対象のモデル - * @param userTimeSeconds デルタ時間の積算値[秒] - * @param weight モーションの重み - * @param motionQueueEntry CubismMotionQueueManagerで管理されているモーション - */ - public doUpdateParameters( - model: CubismModel, - userTimeSeconds: number, - weight: number, - motionQueueEntry: CubismMotionQueueEntry - ): void { - for (let i = 0; i < this._parameters.getSize(); ++i) { - const parameter: ExpressionParameter = this._parameters.at(i); - - switch (parameter.blendType) { - case ExpressionBlendType.ExpressionBlendType_Add: { - model.addParameterValueById( - parameter.parameterId, - parameter.value, - weight - ); - break; - } - case ExpressionBlendType.ExpressionBlendType_Multiply: { - model.multiplyParameterValueById( - parameter.parameterId, - parameter.value, - weight - ); - break; - } - case ExpressionBlendType.ExpressionBlendType_Overwrite: { - model.setParameterValueById( - parameter.parameterId, - parameter.value, - weight - ); - break; - } - default: - // 仕様にない値を設定した時はすでに加算モードになっている - break; - } - } - } - - /** - * コンストラクタ - */ - constructor() { - super(); - - this._parameters = new csmVector(); - } - - _parameters: csmVector; // 表情のパラメータ情報リスト + CubismJson.delete(json); // JSONデータは不要になったら削除する + return expression; } /** - * 表情パラメータ値の計算方式 + * モデルのパラメータの更新の実行 + * @param model 対象のモデル + * @param userTimeSeconds デルタ時間の積算値[秒] + * @param weight モーションの重み + * @param motionQueueEntry CubismMotionQueueManagerで管理されているモーション */ - export enum ExpressionBlendType { - ExpressionBlendType_Add = 0, // 加算 - ExpressionBlendType_Multiply = 1, // 乗算 - ExpressionBlendType_Overwrite = 2 // 上書き + public doUpdateParameters( + model: CubismModel, + userTimeSeconds: number, + weight: number, + motionQueueEntry: CubismMotionQueueEntry + ): void { + for (let i = 0; i < this._parameters.getSize(); ++i) { + const parameter: ExpressionParameter = this._parameters.at(i); + + switch (parameter.blendType) { + case ExpressionBlendType.ExpressionBlendType_Add: { + model.addParameterValueById( + parameter.parameterId, + parameter.value, + weight + ); + break; + } + case ExpressionBlendType.ExpressionBlendType_Multiply: { + model.multiplyParameterValueById( + parameter.parameterId, + parameter.value, + weight + ); + break; + } + case ExpressionBlendType.ExpressionBlendType_Overwrite: { + model.setParameterValueById( + parameter.parameterId, + parameter.value, + weight + ); + break; + } + default: + // 仕様にない値を設定した時はすでに加算モードになっている + break; + } + } } /** - * 表情のパラメータ情報 + * コンストラクタ */ - export class ExpressionParameter { - parameterId: CubismIdHandle; // パラメータID - blendType: ExpressionBlendType; // パラメータの演算種類 - value: number; // 値 + constructor() { + super(); + + this._parameters = new csmVector(); } + + _parameters: csmVector; // 表情のパラメータ情報リスト +} + +/** + * 表情パラメータ値の計算方式 + */ +export enum ExpressionBlendType { + ExpressionBlendType_Add = 0, // 加算 + ExpressionBlendType_Multiply = 1, // 乗算 + ExpressionBlendType_Overwrite = 2 // 上書き +} + +/** + * 表情のパラメータ情報 + */ +export class ExpressionParameter { + parameterId: CubismIdHandle; // パラメータID + blendType: ExpressionBlendType; // パラメータの演算種類 + value: number; // 値 +} + +// Namespace definition for compatibility. +import * as $ from './cubismexpressionmotion'; +// eslint-disable-next-line @typescript-eslint/no-namespace +export namespace Live2DCubismFramework { + export const CubismExpressionMotion = $.CubismExpressionMotion; + export type CubismExpressionMotion = $.CubismExpressionMotion; + export const ExpressionBlendType = $.ExpressionBlendType; + export type ExpressionBlendType = $.ExpressionBlendType; + export const ExpressionParameter = $.ExpressionParameter; + export type ExpressionParameter = $.ExpressionParameter; } diff --git a/src/motion/cubismmotion.ts b/src/motion/cubismmotion.ts index 7dd13df..17c5e05 100644 --- a/src/motion/cubismmotion.ts +++ b/src/motion/cubismmotion.ts @@ -5,941 +5,929 @@ * that can be found at https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html. */ -import { Live2DCubismFramework as cubismmotionjson } from './cubismmotionjson'; -import { Live2DCubismFramework as cubismmotioninternal } from './cubismmotioninternal'; -import { Live2DCubismFramework as acubismmotion } from './acubismmotion'; -import { Live2DCubismFramework as cubismmodel } from '../model/cubismmodel'; -import { Live2DCubismFramework as cubismframework } from '../live2dcubismframework'; -import { Live2DCubismFramework as cubismmotionqueueentry } from './cubismmotionqueueentry'; -import { Live2DCubismFramework as cubismmath } from '../math/cubismmath'; -import { Live2DCubismFramework as csmvector } from '../type/csmvector'; -import { Live2DCubismFramework as cubismid } from '../id/cubismid'; -import { Live2DCubismFramework as csmstring } from '../type/csmstring'; -import { CubismLogDebug, CSM_ASSERT } from '../utils/cubismdebug'; -import csmString = csmstring.csmString; -import CubismMotionData = cubismmotioninternal.CubismMotionData; -import CubismMotionSegment = cubismmotioninternal.CubismMotionSegment; -import CubismMotionPoint = cubismmotioninternal.CubismMotionPoint; -import CubismMotionEvent = cubismmotioninternal.CubismMotionEvent; -import CubismMotionSegmentType = cubismmotioninternal.CubismMotionSegmentType; -import CubismIdHandle = cubismid.CubismIdHandle; -import CubismMotionCurve = cubismmotioninternal.CubismMotionCurve; -import CubismMotionCurveTarget = cubismmotioninternal.CubismMotionCurveTarget; -import csmVector = csmvector.csmVector; -import CubismMath = cubismmath.CubismMath; -import CubismMotionQueueEntry = cubismmotionqueueentry.CubismMotionQueueEntry; -import CubismFramework = cubismframework.CubismFramework; -import CubismModel = cubismmodel.CubismModel; -import ACubismMotion = acubismmotion.ACubismMotion; -import FinishedMotionCallback = acubismmotion.FinishedMotionCallback; -import CubismMotionJson = cubismmotionjson.CubismMotionJson; +import { CubismIdHandle } from '../id/cubismid'; +import { CubismFramework } from '../live2dcubismframework'; +import { CubismMath } from '../math/cubismmath'; +import { CubismModel } from '../model/cubismmodel'; +import { csmString } from '../type/csmstring'; +import { csmVector } from '../type/csmvector'; +import { CSM_ASSERT, CubismLogDebug } from '../utils/cubismdebug'; +import { ACubismMotion, FinishedMotionCallback } from './acubismmotion'; +import { + CubismMotionCurve, + CubismMotionCurveTarget, + CubismMotionData, + CubismMotionEvent, + CubismMotionPoint, + CubismMotionSegment, + CubismMotionSegmentType +} from './cubismmotioninternal'; +import { CubismMotionJson } from './cubismmotionjson'; +import { CubismMotionQueueEntry } from './cubismmotionqueueentry'; -export namespace Live2DCubismFramework { - const EffectNameEyeBlink = 'EyeBlink'; - const EffectNameLipSync = 'LipSync'; - const TargetNameModel = 'Model'; - const TargetNameParameter = 'Parameter'; - const TargetNamePartOpacity = 'PartOpacity'; +const EffectNameEyeBlink = 'EyeBlink'; +const EffectNameLipSync = 'LipSync'; +const TargetNameModel = 'Model'; +const TargetNameParameter = 'Parameter'; +const TargetNamePartOpacity = 'PartOpacity'; - function lerpPoints( - a: CubismMotionPoint, - b: CubismMotionPoint, - t: number - ): CubismMotionPoint { - const result: CubismMotionPoint = new CubismMotionPoint(); +function lerpPoints( + a: CubismMotionPoint, + b: CubismMotionPoint, + t: number +): CubismMotionPoint { + const result: CubismMotionPoint = new CubismMotionPoint(); - result.time = a.time + (b.time - a.time) * t; - result.value = a.value + (b.value - a.value) * t; + result.time = a.time + (b.time - a.time) * t; + result.value = a.value + (b.value - a.value) * t; - return result; + return result; +} + +function linearEvaluate(points: CubismMotionPoint[], time: number): number { + let t: number = (time - points[0].time) / (points[1].time - points[0].time); + + if (t < 0.0) { + t = 0.0; } - function linearEvaluate(points: CubismMotionPoint[], time: number): number { - let t: number = (time - points[0].time) / (points[1].time - points[0].time); + return points[0].value + (points[1].value - points[0].value) * t; +} - if (t < 0.0) { - t = 0.0; +function bezierEvaluate(points: CubismMotionPoint[], time: number): number { + let t: number = (time - points[0].time) / (points[3].time - points[0].time); + + if (t < 0.0) { + t = 0.0; + } + + const p01: CubismMotionPoint = lerpPoints(points[0], points[1], t); + const p12: CubismMotionPoint = lerpPoints(points[1], points[2], t); + const p23: CubismMotionPoint = lerpPoints(points[2], points[3], t); + + const p012: CubismMotionPoint = lerpPoints(p01, p12, t); + const p123: CubismMotionPoint = lerpPoints(p12, p23, t); + + return lerpPoints(p012, p123, t).value; +} + +function steppedEvaluate(points: CubismMotionPoint[], time: number): number { + return points[0].value; +} + +function inverseSteppedEvaluate( + points: CubismMotionPoint[], + time: number +): number { + return points[1].value; +} + +function evaluateCurve( + motionData: CubismMotionData, + index: number, + time: number +): number { + // Find segment to evaluate. + const curve: CubismMotionCurve = motionData.curves.at(index); + + let target = -1; + const totalSegmentCount: number = curve.baseSegmentIndex + curve.segmentCount; + let pointPosition = 0; + for (let i: number = curve.baseSegmentIndex; i < totalSegmentCount; ++i) { + // Get first point of next segment. + pointPosition = + motionData.segments.at(i).basePointIndex + + (motionData.segments.at(i).segmentType == + CubismMotionSegmentType.CubismMotionSegmentType_Bezier + ? 3 + : 1); + + // Break if time lies within current segment. + if (motionData.points.at(pointPosition).time > time) { + target = i; + break; } - - return points[0].value + (points[1].value - points[0].value) * t; } - function bezierEvaluate(points: CubismMotionPoint[], time: number): number { - let t: number = (time - points[0].time) / (points[3].time - points[0].time); - - if (t < 0.0) { - t = 0.0; - } - - const p01: CubismMotionPoint = lerpPoints(points[0], points[1], t); - const p12: CubismMotionPoint = lerpPoints(points[1], points[2], t); - const p23: CubismMotionPoint = lerpPoints(points[2], points[3], t); - - const p012: CubismMotionPoint = lerpPoints(p01, p12, t); - const p123: CubismMotionPoint = lerpPoints(p12, p23, t); - - return lerpPoints(p012, p123, t).value; + if (target == -1) { + return motionData.points.at(pointPosition).value; } - function steppedEvaluate(points: CubismMotionPoint[], time: number): number { - return points[0].value; - } + const segment: CubismMotionSegment = motionData.segments.at(target); - function inverseSteppedEvaluate( - points: CubismMotionPoint[], - time: number - ): number { - return points[1].value; - } + return segment.evaluate(motionData.points.get(segment.basePointIndex), time); +} - function evaluateCurve( - motionData: CubismMotionData, - index: number, - time: number - ): number { - // Find segment to evaluate. - const curve: CubismMotionCurve = motionData.curves.at(index); +/** + * モーションクラス + * + * モーションのクラス。 + */ +export class CubismMotion extends ACubismMotion { + /** + * インスタンスを作成する + * + * @param buffer motion3.jsonが読み込まれているバッファ + * @param size バッファのサイズ + * @param onFinishedMotionHandler モーション再生終了時に呼び出されるコールバック関数 + * @return 作成されたインスタンス + */ + public static create( + buffer: ArrayBuffer, + size: number, + onFinishedMotionHandler?: FinishedMotionCallback + ): CubismMotion { + const ret = new CubismMotion(); - let target = -1; - const totalSegmentCount: number = - curve.baseSegmentIndex + curve.segmentCount; - let pointPosition = 0; - for (let i: number = curve.baseSegmentIndex; i < totalSegmentCount; ++i) { - // Get first point of next segment. - pointPosition = - motionData.segments.at(i).basePointIndex + - (motionData.segments.at(i).segmentType == - CubismMotionSegmentType.CubismMotionSegmentType_Bezier - ? 3 - : 1); + ret.parse(buffer, size); + ret._sourceFrameRate = ret._motionData.fps; + ret._loopDurationSeconds = ret._motionData.duration; + ret._onFinishedMotion = onFinishedMotionHandler; - // Break if time lies within current segment. - if (motionData.points.at(pointPosition).time > time) { - target = i; - break; - } - } - - if (target == -1) { - return motionData.points.at(pointPosition).value; - } - - const segment: CubismMotionSegment = motionData.segments.at(target); - - return segment.evaluate( - motionData.points.get(segment.basePointIndex), - time - ); + // NOTE: Editorではループありのモーション書き出しは非対応 + // ret->_loop = (ret->_motionData->Loop > 0); + return ret; } /** - * モーションクラス - * - * モーションのクラス。 + * モデルのパラメータの更新の実行 + * @param model 対象のモデル + * @param userTimeSeconds 現在の時刻[秒] + * @param fadeWeight モーションの重み + * @param motionQueueEntry CubismMotionQueueManagerで管理されているモーション */ - export class CubismMotion extends ACubismMotion { - /** - * インスタンスを作成する - * - * @param buffer motion3.jsonが読み込まれているバッファ - * @param size バッファのサイズ - * @param onFinishedMotionHandler モーション再生終了時に呼び出されるコールバック関数 - * @return 作成されたインスタンス - */ - public static create( - buffer: ArrayBuffer, - size: number, - onFinishedMotionHandler?: FinishedMotionCallback - ): CubismMotion { - const ret = new CubismMotion(); - - ret.parse(buffer, size); - ret._sourceFrameRate = ret._motionData.fps; - ret._loopDurationSeconds = ret._motionData.duration; - ret._onFinishedMotion = onFinishedMotionHandler; - - // NOTE: Editorではループありのモーション書き出しは非対応 - // ret->_loop = (ret->_motionData->Loop > 0); - return ret; - } - - /** - * モデルのパラメータの更新の実行 - * @param model 対象のモデル - * @param userTimeSeconds 現在の時刻[秒] - * @param fadeWeight モーションの重み - * @param motionQueueEntry CubismMotionQueueManagerで管理されているモーション - */ - public doUpdateParameters( - model: CubismModel, - userTimeSeconds: number, - fadeWeight: number, - motionQueueEntry: CubismMotionQueueEntry - ): void { - if (this._modelCurveIdEyeBlink == null) { - this._modelCurveIdEyeBlink = CubismFramework.getIdManager().getId( - EffectNameEyeBlink - ); - } - - if (this._modelCurveIdLipSync == null) { - this._modelCurveIdLipSync = CubismFramework.getIdManager().getId( - EffectNameLipSync - ); - } - - let timeOffsetSeconds: number = - userTimeSeconds - motionQueueEntry.getStartTime(); - - if (timeOffsetSeconds < 0.0) { - timeOffsetSeconds = 0.0; // エラー回避 - } - - let lipSyncValue: number = Number.MAX_VALUE; - let eyeBlinkValue: number = Number.MAX_VALUE; - - //まばたき、リップシンクのうちモーションの適用を検出するためのビット(maxFlagCount個まで - const MaxTargetSize = 64; - let lipSyncFlags = 0; - let eyeBlinkFlags = 0; - - //瞬き、リップシンクのターゲット数が上限を超えている場合 - if (this._eyeBlinkParameterIds.getSize() > MaxTargetSize) { - CubismLogDebug( - 'too many eye blink targets : {0}', - this._eyeBlinkParameterIds.getSize() - ); - } - if (this._lipSyncParameterIds.getSize() > MaxTargetSize) { - CubismLogDebug( - 'too many lip sync targets : {0}', - this._lipSyncParameterIds.getSize() - ); - } - - const tmpFadeIn: number = - this._fadeInSeconds <= 0.0 - ? 1.0 - : CubismMath.getEasingSine( - (userTimeSeconds - motionQueueEntry.getFadeInStartTime()) / - this._fadeInSeconds - ); - - const tmpFadeOut: number = - this._fadeOutSeconds <= 0.0 || motionQueueEntry.getEndTime() < 0.0 - ? 1.0 - : CubismMath.getEasingSine( - (motionQueueEntry.getEndTime() - userTimeSeconds) / - this._fadeOutSeconds - ); - let value: number; - let c: number, parameterIndex: number; - - // 'Repeat' time as necessary. - let time: number = timeOffsetSeconds; - - if (this._isLoop) { - while (time > this._motionData.duration) { - time -= this._motionData.duration; - } - } - - const curves: csmVector = this._motionData.curves; - - // Evaluate model curves. - for ( - c = 0; - c < this._motionData.curveCount && - curves.at(c).type == - CubismMotionCurveTarget.CubismMotionCurveTarget_Model; - ++c - ) { - // Evaluate curve and call handler. - value = evaluateCurve(this._motionData, c, time); - - if (curves.at(c).id == this._modelCurveIdEyeBlink) { - eyeBlinkValue = value; - } else if (curves.at(c).id == this._modelCurveIdLipSync) { - lipSyncValue = value; - } - } - - let parameterMotionCurveCount = 0; - - for ( - ; - c < this._motionData.curveCount && - curves.at(c).type == - CubismMotionCurveTarget.CubismMotionCurveTarget_Parameter; - ++c - ) { - parameterMotionCurveCount++; - - // Find parameter index. - parameterIndex = model.getParameterIndex(curves.at(c).id); - - // Skip curve evaluation if no value in sink. - if (parameterIndex == -1) { - continue; - } - - const sourceValue: number = model.getParameterValueByIndex( - parameterIndex - ); - - // Evaluate curve and apply value. - value = evaluateCurve(this._motionData, c, time); - - if (eyeBlinkValue != Number.MAX_VALUE) { - for ( - let i = 0; - i < this._eyeBlinkParameterIds.getSize() && i < MaxTargetSize; - ++i - ) { - if (this._eyeBlinkParameterIds.at(i) == curves.at(c).id) { - value *= eyeBlinkValue; - eyeBlinkFlags |= 1 << i; - break; - } - } - } - - if (lipSyncValue != Number.MAX_VALUE) { - for ( - let i = 0; - i < this._lipSyncParameterIds.getSize() && i < MaxTargetSize; - ++i - ) { - if (this._lipSyncParameterIds.at(i) == curves.at(c).id) { - value += lipSyncValue; - lipSyncFlags |= 1 << i; - break; - } - } - } - - let v: number; - - // パラメータごとのフェード - if (curves.at(c).fadeInTime < 0.0 && curves.at(c).fadeOutTime < 0.0) { - // モーションのフェードを適用 - v = sourceValue + (value - sourceValue) * fadeWeight; - } else { - // パラメータに対してフェードインかフェードアウトが設定してある場合はそちらを適用 - let fin: number; - let fout: number; - - if (curves.at(c).fadeInTime < 0.0) { - fin = tmpFadeIn; - } else { - fin = - curves.at(c).fadeInTime == 0.0 - ? 1.0 - : CubismMath.getEasingSine( - (userTimeSeconds - motionQueueEntry.getFadeInStartTime()) / - curves.at(c).fadeInTime - ); - } - - if (curves.at(c).fadeOutTime < 0.0) { - fout = tmpFadeOut; - } else { - fout = - curves.at(c).fadeOutTime == 0.0 || - motionQueueEntry.getEndTime() < 0.0 - ? 1.0 - : CubismMath.getEasingSine( - (motionQueueEntry.getEndTime() - userTimeSeconds) / - curves.at(c).fadeOutTime - ); - } - - const paramWeight: number = this._weight * fin * fout; - - // パラメータごとのフェードを適用 - v = sourceValue + (value - sourceValue) * paramWeight; - } - - model.setParameterValueByIndex(parameterIndex, v, 1.0); - } - - { - if (eyeBlinkValue != Number.MAX_VALUE) { - for ( - let i = 0; - i < this._eyeBlinkParameterIds.getSize() && i < MaxTargetSize; - ++i - ) { - const sourceValue: number = model.getParameterValueById( - this._eyeBlinkParameterIds.at(i) - ); - - // モーションでの上書きがあった時にはまばたきは適用しない - if ((eyeBlinkFlags >> i) & 0x01) { - continue; - } - - const v: number = - sourceValue + (eyeBlinkValue - sourceValue) * fadeWeight; - - model.setParameterValueById(this._eyeBlinkParameterIds.at(i), v); - } - } - - if (lipSyncValue != Number.MAX_VALUE) { - for ( - let i = 0; - i < this._lipSyncParameterIds.getSize() && i < MaxTargetSize; - ++i - ) { - const sourceValue: number = model.getParameterValueById( - this._lipSyncParameterIds.at(i) - ); - - // モーションでの上書きがあった時にはリップシンクは適用しない - if ((lipSyncFlags >> i) & 0x01) { - continue; - } - - const v: number = - sourceValue + (lipSyncValue - sourceValue) * fadeWeight; - - model.setParameterValueById(this._lipSyncParameterIds.at(i), v); - } - } - } - - for ( - ; - c < this._motionData.curveCount && - curves.at(c).type == - CubismMotionCurveTarget.CubismMotionCurveTarget_PartOpacity; - ++c - ) { - // Find parameter index. - parameterIndex = model.getParameterIndex(curves.at(c).id); - - // Skip curve evaluation if no value in sink. - if (parameterIndex == -1) { - continue; - } - - // Evaluate curve and apply value. - value = evaluateCurve(this._motionData, c, time); - - model.setParameterValueByIndex(parameterIndex, value); - } - - if (timeOffsetSeconds >= this._motionData.duration) { - if (this._isLoop) { - motionQueueEntry.setStartTime(userTimeSeconds); // 最初の状態へ - if (this._isLoopFadeIn) { - // ループ内でループ用フェードインが有効の時は、フェードイン設定し直し - motionQueueEntry.setFadeInStartTime(userTimeSeconds); - } - } else { - if (this._onFinishedMotion) { - this._onFinishedMotion(this); - } - - motionQueueEntry.setIsFinished(true); - } - } - this._lastWeight = fadeWeight; - } - - /** - * ループ情報の設定 - * @param loop ループ情報 - */ - public setIsLoop(loop: boolean): void { - this._isLoop = loop; - } - - /** - * ループ情報の取得 - * @return true ループする - * @return false ループしない - */ - public isLoop(): boolean { - return this._isLoop; - } - - /** - * ループ時のフェードイン情報の設定 - * @param loopFadeIn ループ時のフェードイン情報 - */ - public setIsLoopFadeIn(loopFadeIn: boolean): void { - this._isLoopFadeIn = loopFadeIn; - } - - /** - * ループ時のフェードイン情報の取得 - * - * @return true する - * @return false しない - */ - public isLoopFadeIn(): boolean { - return this._isLoopFadeIn; - } - - /** - * モーションの長さを取得する。 - * - * @return モーションの長さ[秒] - */ - public getDuration(): number { - return this._isLoop ? -1.0 : this._loopDurationSeconds; - } - - /** - * モーションのループ時の長さを取得する。 - * - * @return モーションのループ時の長さ[秒] - */ - public getLoopDuration(): number { - return this._loopDurationSeconds; - } - - /** - * パラメータに対するフェードインの時間を設定する。 - * - * @param parameterId パラメータID - * @param value フェードインにかかる時間[秒] - */ - public setParameterFadeInTime( - parameterId: CubismIdHandle, - value: number - ): void { - const curves: csmVector = this._motionData.curves; - - for (let i = 0; i < this._motionData.curveCount; ++i) { - if (parameterId == curves.at(i).id) { - curves.at(i).fadeInTime = value; - return; - } - } - } - - /** - * パラメータに対するフェードアウトの時間の設定 - * @param parameterId パラメータID - * @param value フェードアウトにかかる時間[秒] - */ - public setParameterFadeOutTime( - parameterId: CubismIdHandle, - value: number - ): void { - const curves: csmVector = this._motionData.curves; - - for (let i = 0; i < this._motionData.curveCount; ++i) { - if (parameterId == curves.at(i).id) { - curves.at(i).fadeOutTime = value; - return; - } - } - } - - /** - * パラメータに対するフェードインの時間の取得 - * @param parameterId パラメータID - * @return フェードインにかかる時間[秒] - */ - public getParameterFadeInTime(parameterId: CubismIdHandle): number { - const curves: csmVector = this._motionData.curves; - - for (let i = 0; i < this._motionData.curveCount; ++i) { - if (parameterId == curves.at(i).id) { - return curves.at(i).fadeInTime; - } - } - - return -1; - } - - /** - * パラメータに対するフェードアウトの時間を取得 - * - * @param parameterId パラメータID - * @return フェードアウトにかかる時間[秒] - */ - public getParameterFadeOutTime(parameterId: CubismIdHandle): number { - const curves: csmVector = this._motionData.curves; - - for (let i = 0; i < this._motionData.curveCount; ++i) { - if (parameterId == curves.at(i).id) { - return curves.at(i).fadeOutTime; - } - } - - return -1; - } - - /** - * 自動エフェクトがかかっているパラメータIDリストの設定 - * @param eyeBlinkParameterIds 自動まばたきがかかっているパラメータIDのリスト - * @param lipSyncParameterIds リップシンクがかかっているパラメータIDのリスト - */ - public setEffectIds( - eyeBlinkParameterIds: csmVector, - lipSyncParameterIds: csmVector - ): void { - this._eyeBlinkParameterIds = eyeBlinkParameterIds; - this._lipSyncParameterIds = lipSyncParameterIds; - } - - /** - * コンストラクタ - */ - public constructor() { - super(); - this._sourceFrameRate = 30.0; - this._loopDurationSeconds = -1.0; - this._isLoop = false; // trueから false へデフォルトを変更 - this._isLoopFadeIn = true; // ループ時にフェードインが有効かどうかのフラグ - this._lastWeight = 0.0; - this._motionData = null; - this._modelCurveIdEyeBlink = null; - this._modelCurveIdLipSync = null; - this._eyeBlinkParameterIds = null; - this._lipSyncParameterIds = null; - } - - /** - * デストラクタ相当の処理 - */ - public release(): void { - this._motionData = void 0; - this._motionData = null; - } - - /** - * motion3.jsonをパースする。 - * - * @param motionJson motion3.jsonが読み込まれているバッファ - * @param size バッファのサイズ - */ - public parse(motionJson: ArrayBuffer, size: number): void { - this._motionData = new CubismMotionData(); - - let json: CubismMotionJson = new CubismMotionJson(motionJson, size); - - this._motionData.duration = json.getMotionDuration(); - this._motionData.loop = json.isMotionLoop(); - this._motionData.curveCount = json.getMotionCurveCount(); - this._motionData.fps = json.getMotionFps(); - this._motionData.eventCount = json.getEventCount(); - - if (json.isExistMotionFadeInTime()) { - this._fadeInSeconds = - json.getMotionFadeInTime() < 0.0 ? 1.0 : json.getMotionFadeInTime(); - } else { - this._fadeInSeconds = 1.0; - } - - if (json.isExistMotionFadeOutTime()) { - this._fadeOutSeconds = - json.getMotionFadeOutTime() < 0.0 ? 1.0 : json.getMotionFadeOutTime(); - } else { - this._fadeOutSeconds = 1.0; - } - - this._motionData.curves.updateSize( - this._motionData.curveCount, - CubismMotionCurve, - true + public doUpdateParameters( + model: CubismModel, + userTimeSeconds: number, + fadeWeight: number, + motionQueueEntry: CubismMotionQueueEntry + ): void { + if (this._modelCurveIdEyeBlink == null) { + this._modelCurveIdEyeBlink = CubismFramework.getIdManager().getId( + EffectNameEyeBlink ); - this._motionData.segments.updateSize( - json.getMotionTotalSegmentCount(), - CubismMotionSegment, - true + } + + if (this._modelCurveIdLipSync == null) { + this._modelCurveIdLipSync = CubismFramework.getIdManager().getId( + EffectNameLipSync ); - this._motionData.points.updateSize( - json.getMotionTotalPointCount(), - CubismMotionPoint, - true + } + + let timeOffsetSeconds: number = + userTimeSeconds - motionQueueEntry.getStartTime(); + + if (timeOffsetSeconds < 0.0) { + timeOffsetSeconds = 0.0; // エラー回避 + } + + let lipSyncValue: number = Number.MAX_VALUE; + let eyeBlinkValue: number = Number.MAX_VALUE; + + //まばたき、リップシンクのうちモーションの適用を検出するためのビット(maxFlagCount個まで + const MaxTargetSize = 64; + let lipSyncFlags = 0; + let eyeBlinkFlags = 0; + + //瞬き、リップシンクのターゲット数が上限を超えている場合 + if (this._eyeBlinkParameterIds.getSize() > MaxTargetSize) { + CubismLogDebug( + 'too many eye blink targets : {0}', + this._eyeBlinkParameterIds.getSize() ); - this._motionData.events.updateSize( - this._motionData.eventCount, - CubismMotionEvent, - true + } + if (this._lipSyncParameterIds.getSize() > MaxTargetSize) { + CubismLogDebug( + 'too many lip sync targets : {0}', + this._lipSyncParameterIds.getSize() + ); + } + + const tmpFadeIn: number = + this._fadeInSeconds <= 0.0 + ? 1.0 + : CubismMath.getEasingSine( + (userTimeSeconds - motionQueueEntry.getFadeInStartTime()) / + this._fadeInSeconds + ); + + const tmpFadeOut: number = + this._fadeOutSeconds <= 0.0 || motionQueueEntry.getEndTime() < 0.0 + ? 1.0 + : CubismMath.getEasingSine( + (motionQueueEntry.getEndTime() - userTimeSeconds) / + this._fadeOutSeconds + ); + let value: number; + let c: number, parameterIndex: number; + + // 'Repeat' time as necessary. + let time: number = timeOffsetSeconds; + + if (this._isLoop) { + while (time > this._motionData.duration) { + time -= this._motionData.duration; + } + } + + const curves: csmVector = this._motionData.curves; + + // Evaluate model curves. + for ( + c = 0; + c < this._motionData.curveCount && + curves.at(c).type == + CubismMotionCurveTarget.CubismMotionCurveTarget_Model; + ++c + ) { + // Evaluate curve and call handler. + value = evaluateCurve(this._motionData, c, time); + + if (curves.at(c).id == this._modelCurveIdEyeBlink) { + eyeBlinkValue = value; + } else if (curves.at(c).id == this._modelCurveIdLipSync) { + lipSyncValue = value; + } + } + + let parameterMotionCurveCount = 0; + + for ( + ; + c < this._motionData.curveCount && + curves.at(c).type == + CubismMotionCurveTarget.CubismMotionCurveTarget_Parameter; + ++c + ) { + parameterMotionCurveCount++; + + // Find parameter index. + parameterIndex = model.getParameterIndex(curves.at(c).id); + + // Skip curve evaluation if no value in sink. + if (parameterIndex == -1) { + continue; + } + + const sourceValue: number = model.getParameterValueByIndex( + parameterIndex ); - let totalPointCount = 0; - let totalSegmentCount = 0; + // Evaluate curve and apply value. + value = evaluateCurve(this._motionData, c, time); - // Curves - for ( - let curveCount = 0; - curveCount < this._motionData.curveCount; - ++curveCount - ) { - if (json.getMotionCurveTarget(curveCount) == TargetNameModel) { - this._motionData.curves.at(curveCount).type = - CubismMotionCurveTarget.CubismMotionCurveTarget_Model; - } else if ( - json.getMotionCurveTarget(curveCount) == TargetNameParameter - ) { - this._motionData.curves.at(curveCount).type = - CubismMotionCurveTarget.CubismMotionCurveTarget_Parameter; - } else if ( - json.getMotionCurveTarget(curveCount) == TargetNamePartOpacity - ) { - this._motionData.curves.at(curveCount).type = - CubismMotionCurveTarget.CubismMotionCurveTarget_PartOpacity; - } - - this._motionData.curves.at(curveCount).id = json.getMotionCurveId( - curveCount - ); - - this._motionData.curves.at( - curveCount - ).baseSegmentIndex = totalSegmentCount; - - this._motionData.curves.at( - curveCount - ).fadeInTime = json.isExistMotionCurveFadeInTime(curveCount) - ? json.getMotionCurveFadeInTime(curveCount) - : -1.0; - this._motionData.curves.at( - curveCount - ).fadeOutTime = json.isExistMotionCurveFadeOutTime(curveCount) - ? json.getMotionCurveFadeOutTime(curveCount) - : -1.0; - - // Segments + if (eyeBlinkValue != Number.MAX_VALUE) { for ( - let segmentPosition = 0; - segmentPosition < json.getMotionCurveSegmentCount(curveCount); - + let i = 0; + i < this._eyeBlinkParameterIds.getSize() && i < MaxTargetSize; + ++i ) { - if (segmentPosition == 0) { + if (this._eyeBlinkParameterIds.at(i) == curves.at(c).id) { + value *= eyeBlinkValue; + eyeBlinkFlags |= 1 << i; + break; + } + } + } + + if (lipSyncValue != Number.MAX_VALUE) { + for ( + let i = 0; + i < this._lipSyncParameterIds.getSize() && i < MaxTargetSize; + ++i + ) { + if (this._lipSyncParameterIds.at(i) == curves.at(c).id) { + value += lipSyncValue; + lipSyncFlags |= 1 << i; + break; + } + } + } + + let v: number; + + // パラメータごとのフェード + if (curves.at(c).fadeInTime < 0.0 && curves.at(c).fadeOutTime < 0.0) { + // モーションのフェードを適用 + v = sourceValue + (value - sourceValue) * fadeWeight; + } else { + // パラメータに対してフェードインかフェードアウトが設定してある場合はそちらを適用 + let fin: number; + let fout: number; + + if (curves.at(c).fadeInTime < 0.0) { + fin = tmpFadeIn; + } else { + fin = + curves.at(c).fadeInTime == 0.0 + ? 1.0 + : CubismMath.getEasingSine( + (userTimeSeconds - motionQueueEntry.getFadeInStartTime()) / + curves.at(c).fadeInTime + ); + } + + if (curves.at(c).fadeOutTime < 0.0) { + fout = tmpFadeOut; + } else { + fout = + curves.at(c).fadeOutTime == 0.0 || + motionQueueEntry.getEndTime() < 0.0 + ? 1.0 + : CubismMath.getEasingSine( + (motionQueueEntry.getEndTime() - userTimeSeconds) / + curves.at(c).fadeOutTime + ); + } + + const paramWeight: number = this._weight * fin * fout; + + // パラメータごとのフェードを適用 + v = sourceValue + (value - sourceValue) * paramWeight; + } + + model.setParameterValueByIndex(parameterIndex, v, 1.0); + } + + { + if (eyeBlinkValue != Number.MAX_VALUE) { + for ( + let i = 0; + i < this._eyeBlinkParameterIds.getSize() && i < MaxTargetSize; + ++i + ) { + const sourceValue: number = model.getParameterValueById( + this._eyeBlinkParameterIds.at(i) + ); + + // モーションでの上書きがあった時にはまばたきは適用しない + if ((eyeBlinkFlags >> i) & 0x01) { + continue; + } + + const v: number = + sourceValue + (eyeBlinkValue - sourceValue) * fadeWeight; + + model.setParameterValueById(this._eyeBlinkParameterIds.at(i), v); + } + } + + if (lipSyncValue != Number.MAX_VALUE) { + for ( + let i = 0; + i < this._lipSyncParameterIds.getSize() && i < MaxTargetSize; + ++i + ) { + const sourceValue: number = model.getParameterValueById( + this._lipSyncParameterIds.at(i) + ); + + // モーションでの上書きがあった時にはリップシンクは適用しない + if ((lipSyncFlags >> i) & 0x01) { + continue; + } + + const v: number = + sourceValue + (lipSyncValue - sourceValue) * fadeWeight; + + model.setParameterValueById(this._lipSyncParameterIds.at(i), v); + } + } + } + + for ( + ; + c < this._motionData.curveCount && + curves.at(c).type == + CubismMotionCurveTarget.CubismMotionCurveTarget_PartOpacity; + ++c + ) { + // Find parameter index. + parameterIndex = model.getParameterIndex(curves.at(c).id); + + // Skip curve evaluation if no value in sink. + if (parameterIndex == -1) { + continue; + } + + // Evaluate curve and apply value. + value = evaluateCurve(this._motionData, c, time); + + model.setParameterValueByIndex(parameterIndex, value); + } + + if (timeOffsetSeconds >= this._motionData.duration) { + if (this._isLoop) { + motionQueueEntry.setStartTime(userTimeSeconds); // 最初の状態へ + if (this._isLoopFadeIn) { + // ループ内でループ用フェードインが有効の時は、フェードイン設定し直し + motionQueueEntry.setFadeInStartTime(userTimeSeconds); + } + } else { + if (this._onFinishedMotion) { + this._onFinishedMotion(this); + } + + motionQueueEntry.setIsFinished(true); + } + } + this._lastWeight = fadeWeight; + } + + /** + * ループ情報の設定 + * @param loop ループ情報 + */ + public setIsLoop(loop: boolean): void { + this._isLoop = loop; + } + + /** + * ループ情報の取得 + * @return true ループする + * @return false ループしない + */ + public isLoop(): boolean { + return this._isLoop; + } + + /** + * ループ時のフェードイン情報の設定 + * @param loopFadeIn ループ時のフェードイン情報 + */ + public setIsLoopFadeIn(loopFadeIn: boolean): void { + this._isLoopFadeIn = loopFadeIn; + } + + /** + * ループ時のフェードイン情報の取得 + * + * @return true する + * @return false しない + */ + public isLoopFadeIn(): boolean { + return this._isLoopFadeIn; + } + + /** + * モーションの長さを取得する。 + * + * @return モーションの長さ[秒] + */ + public getDuration(): number { + return this._isLoop ? -1.0 : this._loopDurationSeconds; + } + + /** + * モーションのループ時の長さを取得する。 + * + * @return モーションのループ時の長さ[秒] + */ + public getLoopDuration(): number { + return this._loopDurationSeconds; + } + + /** + * パラメータに対するフェードインの時間を設定する。 + * + * @param parameterId パラメータID + * @param value フェードインにかかる時間[秒] + */ + public setParameterFadeInTime( + parameterId: CubismIdHandle, + value: number + ): void { + const curves: csmVector = this._motionData.curves; + + for (let i = 0; i < this._motionData.curveCount; ++i) { + if (parameterId == curves.at(i).id) { + curves.at(i).fadeInTime = value; + return; + } + } + } + + /** + * パラメータに対するフェードアウトの時間の設定 + * @param parameterId パラメータID + * @param value フェードアウトにかかる時間[秒] + */ + public setParameterFadeOutTime( + parameterId: CubismIdHandle, + value: number + ): void { + const curves: csmVector = this._motionData.curves; + + for (let i = 0; i < this._motionData.curveCount; ++i) { + if (parameterId == curves.at(i).id) { + curves.at(i).fadeOutTime = value; + return; + } + } + } + + /** + * パラメータに対するフェードインの時間の取得 + * @param parameterId パラメータID + * @return フェードインにかかる時間[秒] + */ + public getParameterFadeInTime(parameterId: CubismIdHandle): number { + const curves: csmVector = this._motionData.curves; + + for (let i = 0; i < this._motionData.curveCount; ++i) { + if (parameterId == curves.at(i).id) { + return curves.at(i).fadeInTime; + } + } + + return -1; + } + + /** + * パラメータに対するフェードアウトの時間を取得 + * + * @param parameterId パラメータID + * @return フェードアウトにかかる時間[秒] + */ + public getParameterFadeOutTime(parameterId: CubismIdHandle): number { + const curves: csmVector = this._motionData.curves; + + for (let i = 0; i < this._motionData.curveCount; ++i) { + if (parameterId == curves.at(i).id) { + return curves.at(i).fadeOutTime; + } + } + + return -1; + } + + /** + * 自動エフェクトがかかっているパラメータIDリストの設定 + * @param eyeBlinkParameterIds 自動まばたきがかかっているパラメータIDのリスト + * @param lipSyncParameterIds リップシンクがかかっているパラメータIDのリスト + */ + public setEffectIds( + eyeBlinkParameterIds: csmVector, + lipSyncParameterIds: csmVector + ): void { + this._eyeBlinkParameterIds = eyeBlinkParameterIds; + this._lipSyncParameterIds = lipSyncParameterIds; + } + + /** + * コンストラクタ + */ + public constructor() { + super(); + this._sourceFrameRate = 30.0; + this._loopDurationSeconds = -1.0; + this._isLoop = false; // trueから false へデフォルトを変更 + this._isLoopFadeIn = true; // ループ時にフェードインが有効かどうかのフラグ + this._lastWeight = 0.0; + this._motionData = null; + this._modelCurveIdEyeBlink = null; + this._modelCurveIdLipSync = null; + this._eyeBlinkParameterIds = null; + this._lipSyncParameterIds = null; + } + + /** + * デストラクタ相当の処理 + */ + public release(): void { + this._motionData = void 0; + this._motionData = null; + } + + /** + * motion3.jsonをパースする。 + * + * @param motionJson motion3.jsonが読み込まれているバッファ + * @param size バッファのサイズ + */ + public parse(motionJson: ArrayBuffer, size: number): void { + this._motionData = new CubismMotionData(); + + let json: CubismMotionJson = new CubismMotionJson(motionJson, size); + + this._motionData.duration = json.getMotionDuration(); + this._motionData.loop = json.isMotionLoop(); + this._motionData.curveCount = json.getMotionCurveCount(); + this._motionData.fps = json.getMotionFps(); + this._motionData.eventCount = json.getEventCount(); + + if (json.isExistMotionFadeInTime()) { + this._fadeInSeconds = + json.getMotionFadeInTime() < 0.0 ? 1.0 : json.getMotionFadeInTime(); + } else { + this._fadeInSeconds = 1.0; + } + + if (json.isExistMotionFadeOutTime()) { + this._fadeOutSeconds = + json.getMotionFadeOutTime() < 0.0 ? 1.0 : json.getMotionFadeOutTime(); + } else { + this._fadeOutSeconds = 1.0; + } + + this._motionData.curves.updateSize( + this._motionData.curveCount, + CubismMotionCurve, + true + ); + this._motionData.segments.updateSize( + json.getMotionTotalSegmentCount(), + CubismMotionSegment, + true + ); + this._motionData.points.updateSize( + json.getMotionTotalPointCount(), + CubismMotionPoint, + true + ); + this._motionData.events.updateSize( + this._motionData.eventCount, + CubismMotionEvent, + true + ); + + let totalPointCount = 0; + let totalSegmentCount = 0; + + // Curves + for ( + let curveCount = 0; + curveCount < this._motionData.curveCount; + ++curveCount + ) { + if (json.getMotionCurveTarget(curveCount) == TargetNameModel) { + this._motionData.curves.at(curveCount).type = + CubismMotionCurveTarget.CubismMotionCurveTarget_Model; + } else if (json.getMotionCurveTarget(curveCount) == TargetNameParameter) { + this._motionData.curves.at(curveCount).type = + CubismMotionCurveTarget.CubismMotionCurveTarget_Parameter; + } else if ( + json.getMotionCurveTarget(curveCount) == TargetNamePartOpacity + ) { + this._motionData.curves.at(curveCount).type = + CubismMotionCurveTarget.CubismMotionCurveTarget_PartOpacity; + } + + this._motionData.curves.at(curveCount).id = json.getMotionCurveId( + curveCount + ); + + this._motionData.curves.at( + curveCount + ).baseSegmentIndex = totalSegmentCount; + + this._motionData.curves.at( + curveCount + ).fadeInTime = json.isExistMotionCurveFadeInTime(curveCount) + ? json.getMotionCurveFadeInTime(curveCount) + : -1.0; + this._motionData.curves.at( + curveCount + ).fadeOutTime = json.isExistMotionCurveFadeOutTime(curveCount) + ? json.getMotionCurveFadeOutTime(curveCount) + : -1.0; + + // Segments + for ( + let segmentPosition = 0; + segmentPosition < json.getMotionCurveSegmentCount(curveCount); + + ) { + if (segmentPosition == 0) { + this._motionData.segments.at( + totalSegmentCount + ).basePointIndex = totalPointCount; + + this._motionData.points.at( + totalPointCount + ).time = json.getMotionCurveSegment(curveCount, segmentPosition); + this._motionData.points.at( + totalPointCount + ).value = json.getMotionCurveSegment(curveCount, segmentPosition + 1); + + totalPointCount += 1; + segmentPosition += 2; + } else { + this._motionData.segments.at(totalSegmentCount).basePointIndex = + totalPointCount - 1; + } + + const segment: number = json.getMotionCurveSegment( + curveCount, + segmentPosition + ); + switch (segment) { + case CubismMotionSegmentType.CubismMotionSegmentType_Linear: { + this._motionData.segments.at(totalSegmentCount).segmentType = + CubismMotionSegmentType.CubismMotionSegmentType_Linear; this._motionData.segments.at( totalSegmentCount - ).basePointIndex = totalPointCount; + ).evaluate = linearEvaluate; this._motionData.points.at( totalPointCount - ).time = json.getMotionCurveSegment(curveCount, segmentPosition); + ).time = json.getMotionCurveSegment( + curveCount, + segmentPosition + 1 + ); this._motionData.points.at( totalPointCount ).value = json.getMotionCurveSegment( curveCount, - segmentPosition + 1 + segmentPosition + 2 ); totalPointCount += 1; - segmentPosition += 2; - } else { - this._motionData.segments.at(totalSegmentCount).basePointIndex = - totalPointCount - 1; + segmentPosition += 3; + + break; + } + case CubismMotionSegmentType.CubismMotionSegmentType_Bezier: { + this._motionData.segments.at(totalSegmentCount).segmentType = + CubismMotionSegmentType.CubismMotionSegmentType_Bezier; + this._motionData.segments.at( + totalSegmentCount + ).evaluate = bezierEvaluate; + + this._motionData.points.at( + totalPointCount + ).time = json.getMotionCurveSegment( + curveCount, + segmentPosition + 1 + ); + this._motionData.points.at( + totalPointCount + ).value = json.getMotionCurveSegment( + curveCount, + segmentPosition + 2 + ); + + this._motionData.points.at( + totalPointCount + 1 + ).time = json.getMotionCurveSegment( + curveCount, + segmentPosition + 3 + ); + this._motionData.points.at( + totalPointCount + 1 + ).value = json.getMotionCurveSegment( + curveCount, + segmentPosition + 4 + ); + + this._motionData.points.at( + totalPointCount + 2 + ).time = json.getMotionCurveSegment( + curveCount, + segmentPosition + 5 + ); + this._motionData.points.at( + totalPointCount + 2 + ).value = json.getMotionCurveSegment( + curveCount, + segmentPosition + 6 + ); + + totalPointCount += 3; + segmentPosition += 7; + + break; } - const segment: number = json.getMotionCurveSegment( - curveCount, - segmentPosition - ); - switch (segment) { - case CubismMotionSegmentType.CubismMotionSegmentType_Linear: { - this._motionData.segments.at(totalSegmentCount).segmentType = - CubismMotionSegmentType.CubismMotionSegmentType_Linear; - this._motionData.segments.at( - totalSegmentCount - ).evaluate = linearEvaluate; + case CubismMotionSegmentType.CubismMotionSegmentType_Stepped: { + this._motionData.segments.at(totalSegmentCount).segmentType = + CubismMotionSegmentType.CubismMotionSegmentType_Stepped; + this._motionData.segments.at( + totalSegmentCount + ).evaluate = steppedEvaluate; - this._motionData.points.at( - totalPointCount - ).time = json.getMotionCurveSegment( - curveCount, - segmentPosition + 1 - ); - this._motionData.points.at( - totalPointCount - ).value = json.getMotionCurveSegment( - curveCount, - segmentPosition + 2 - ); + this._motionData.points.at( + totalPointCount + ).time = json.getMotionCurveSegment( + curveCount, + segmentPosition + 1 + ); + this._motionData.points.at( + totalPointCount + ).value = json.getMotionCurveSegment( + curveCount, + segmentPosition + 2 + ); - totalPointCount += 1; - segmentPosition += 3; + totalPointCount += 1; + segmentPosition += 3; - break; - } - case CubismMotionSegmentType.CubismMotionSegmentType_Bezier: { - this._motionData.segments.at(totalSegmentCount).segmentType = - CubismMotionSegmentType.CubismMotionSegmentType_Bezier; - this._motionData.segments.at( - totalSegmentCount - ).evaluate = bezierEvaluate; - - this._motionData.points.at( - totalPointCount - ).time = json.getMotionCurveSegment( - curveCount, - segmentPosition + 1 - ); - this._motionData.points.at( - totalPointCount - ).value = json.getMotionCurveSegment( - curveCount, - segmentPosition + 2 - ); - - this._motionData.points.at( - totalPointCount + 1 - ).time = json.getMotionCurveSegment( - curveCount, - segmentPosition + 3 - ); - this._motionData.points.at( - totalPointCount + 1 - ).value = json.getMotionCurveSegment( - curveCount, - segmentPosition + 4 - ); - - this._motionData.points.at( - totalPointCount + 2 - ).time = json.getMotionCurveSegment( - curveCount, - segmentPosition + 5 - ); - this._motionData.points.at( - totalPointCount + 2 - ).value = json.getMotionCurveSegment( - curveCount, - segmentPosition + 6 - ); - - totalPointCount += 3; - segmentPosition += 7; - - break; - } - - case CubismMotionSegmentType.CubismMotionSegmentType_Stepped: { - this._motionData.segments.at(totalSegmentCount).segmentType = - CubismMotionSegmentType.CubismMotionSegmentType_Stepped; - this._motionData.segments.at( - totalSegmentCount - ).evaluate = steppedEvaluate; - - this._motionData.points.at( - totalPointCount - ).time = json.getMotionCurveSegment( - curveCount, - segmentPosition + 1 - ); - this._motionData.points.at( - totalPointCount - ).value = json.getMotionCurveSegment( - curveCount, - segmentPosition + 2 - ); - - totalPointCount += 1; - segmentPosition += 3; - - break; - } - - case CubismMotionSegmentType.CubismMotionSegmentType_InverseStepped: { - this._motionData.segments.at(totalSegmentCount).segmentType = - CubismMotionSegmentType.CubismMotionSegmentType_InverseStepped; - this._motionData.segments.at( - totalSegmentCount - ).evaluate = inverseSteppedEvaluate; - - this._motionData.points.at( - totalPointCount - ).time = json.getMotionCurveSegment( - curveCount, - segmentPosition + 1 - ); - this._motionData.points.at( - totalPointCount - ).value = json.getMotionCurveSegment( - curveCount, - segmentPosition + 2 - ); - - totalPointCount += 1; - segmentPosition += 3; - - break; - } - default: { - CSM_ASSERT(0); - break; - } + break; } - ++this._motionData.curves.at(curveCount).segmentCount; - ++totalSegmentCount; + case CubismMotionSegmentType.CubismMotionSegmentType_InverseStepped: { + this._motionData.segments.at(totalSegmentCount).segmentType = + CubismMotionSegmentType.CubismMotionSegmentType_InverseStepped; + this._motionData.segments.at( + totalSegmentCount + ).evaluate = inverseSteppedEvaluate; + + this._motionData.points.at( + totalPointCount + ).time = json.getMotionCurveSegment( + curveCount, + segmentPosition + 1 + ); + this._motionData.points.at( + totalPointCount + ).value = json.getMotionCurveSegment( + curveCount, + segmentPosition + 2 + ); + + totalPointCount += 1; + segmentPosition += 3; + + break; + } + default: { + CSM_ASSERT(0); + break; + } } - } - for ( - let userdatacount = 0; - userdatacount < json.getEventCount(); - ++userdatacount - ) { - this._motionData.events.at(userdatacount).fireTime = json.getEventTime( - userdatacount - ); - this._motionData.events.at(userdatacount).value = json.getEventValue( - userdatacount - ); + ++this._motionData.curves.at(curveCount).segmentCount; + ++totalSegmentCount; } - - json.release(); - json = void 0; - json = null; } - /** - * モデルのパラメータ更新 - * - * イベント発火のチェック。 - * 入力する時間は呼ばれるモーションタイミングを0とした秒数で行う。 - * - * @param beforeCheckTimeSeconds 前回のイベントチェック時間[秒] - * @param motionTimeSeconds 今回の再生時間[秒] - */ - public getFiredEvent( - beforeCheckTimeSeconds: number, - motionTimeSeconds: number - ): csmVector { - this._firedEventValues.updateSize(0); - - // イベントの発火チェック - for (let u = 0; u < this._motionData.eventCount; ++u) { - if ( - this._motionData.events.at(u).fireTime > beforeCheckTimeSeconds && - this._motionData.events.at(u).fireTime <= motionTimeSeconds - ) { - this._firedEventValues.pushBack( - new csmString(this._motionData.events.at(u).value.s) - ); - } - } - - return this._firedEventValues; + for ( + let userdatacount = 0; + userdatacount < json.getEventCount(); + ++userdatacount + ) { + this._motionData.events.at(userdatacount).fireTime = json.getEventTime( + userdatacount + ); + this._motionData.events.at(userdatacount).value = json.getEventValue( + userdatacount + ); } - public _sourceFrameRate: number; // ロードしたファイルのFPS。記述が無ければデフォルト値15fpsとなる - public _loopDurationSeconds: number; // mtnファイルで定義される一連のモーションの長さ - public _isLoop: boolean; // ループするか? - public _isLoopFadeIn: boolean; // ループ時にフェードインが有効かどうかのフラグ。初期値では有効。 - public _lastWeight: number; // 最後に設定された重み - - public _motionData: CubismMotionData; // 実際のモーションデータ本体 - - public _eyeBlinkParameterIds: csmVector; // 自動まばたきを適用するパラメータIDハンドルのリスト。 モデル(モデルセッティング)とパラメータを対応付ける。 - public _lipSyncParameterIds: csmVector; // リップシンクを適用するパラメータIDハンドルのリスト。 モデル(モデルセッティング)とパラメータを対応付ける。 - - public _modelCurveIdEyeBlink: CubismIdHandle; // モデルが持つ自動まばたき用パラメータIDのハンドル。 モデルとモーションを対応付ける。 - public _modelCurveIdLipSync: CubismIdHandle; // モデルが持つリップシンク用パラメータIDのハンドル。 モデルとモーションを対応付ける。 + json.release(); + json = void 0; + json = null; } + + /** + * モデルのパラメータ更新 + * + * イベント発火のチェック。 + * 入力する時間は呼ばれるモーションタイミングを0とした秒数で行う。 + * + * @param beforeCheckTimeSeconds 前回のイベントチェック時間[秒] + * @param motionTimeSeconds 今回の再生時間[秒] + */ + public getFiredEvent( + beforeCheckTimeSeconds: number, + motionTimeSeconds: number + ): csmVector { + this._firedEventValues.updateSize(0); + + // イベントの発火チェック + for (let u = 0; u < this._motionData.eventCount; ++u) { + if ( + this._motionData.events.at(u).fireTime > beforeCheckTimeSeconds && + this._motionData.events.at(u).fireTime <= motionTimeSeconds + ) { + this._firedEventValues.pushBack( + new csmString(this._motionData.events.at(u).value.s) + ); + } + } + + return this._firedEventValues; + } + + public _sourceFrameRate: number; // ロードしたファイルのFPS。記述が無ければデフォルト値15fpsとなる + public _loopDurationSeconds: number; // mtnファイルで定義される一連のモーションの長さ + public _isLoop: boolean; // ループするか? + public _isLoopFadeIn: boolean; // ループ時にフェードインが有効かどうかのフラグ。初期値では有効。 + public _lastWeight: number; // 最後に設定された重み + + public _motionData: CubismMotionData; // 実際のモーションデータ本体 + + public _eyeBlinkParameterIds: csmVector; // 自動まばたきを適用するパラメータIDハンドルのリスト。 モデル(モデルセッティング)とパラメータを対応付ける。 + public _lipSyncParameterIds: csmVector; // リップシンクを適用するパラメータIDハンドルのリスト。 モデル(モデルセッティング)とパラメータを対応付ける。 + + public _modelCurveIdEyeBlink: CubismIdHandle; // モデルが持つ自動まばたき用パラメータIDのハンドル。 モデルとモーションを対応付ける。 + public _modelCurveIdLipSync: CubismIdHandle; // モデルが持つリップシンク用パラメータIDのハンドル。 モデルとモーションを対応付ける。 +} + +// Namespace definition for compatibility. +import * as $ from './cubismmotion'; +// eslint-disable-next-line @typescript-eslint/no-namespace +export namespace Live2DCubismFramework { + export const CubismMotion = $.CubismMotion; + export type CubismMotion = $.CubismMotion; } diff --git a/src/motion/cubismmotioninternal.ts b/src/motion/cubismmotioninternal.ts index 705a336..8b48faa 100644 --- a/src/motion/cubismmotioninternal.ts +++ b/src/motion/cubismmotioninternal.ts @@ -5,136 +5,152 @@ * that can be found at https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html. */ -import { Live2DCubismFramework as cubismid } from '../id/cubismid'; -import { Live2DCubismFramework as csmstring } from '../type/csmstring'; -import { Live2DCubismFramework as csmvector } from '../type/csmvector'; -import csmVector = csmvector.csmVector; -import csmString = csmstring.csmString; -import CubismIdHandle = cubismid.CubismIdHandle; +import { CubismIdHandle } from '../id/cubismid'; +import { csmString } from '../type/csmstring'; +import { csmVector } from '../type/csmvector'; -export namespace Live2DCubismFramework { - /** - * @brief モーションカーブの種類 - * - * モーションカーブの種類。 - */ - export enum CubismMotionCurveTarget { - CubismMotionCurveTarget_Model, // モデルに対して - CubismMotionCurveTarget_Parameter, // パラメータに対して - CubismMotionCurveTarget_PartOpacity // パーツの不透明度に対して - } - - /** - * @brief モーションカーブのセグメントの種類 - * - * モーションカーブのセグメントの種類。 - */ - export enum CubismMotionSegmentType { - CubismMotionSegmentType_Linear = 0, // リニア - CubismMotionSegmentType_Bezier = 1, // ベジェ曲線 - CubismMotionSegmentType_Stepped = 2, // ステップ - CubismMotionSegmentType_InverseStepped = 3 // インバースステップ - } - - /** - * @brief モーションカーブの制御点 - * - * モーションカーブの制御点。 - */ - export class CubismMotionPoint { - time = 0.0; // 時間[秒] - value = 0.0; // 値 - } - - /** - * モーションカーブのセグメントの評価関数 - * - * @param points モーションカーブの制御点リスト - * @param time 評価する時間[秒] - */ - export interface csmMotionSegmentEvaluationFunction { - (points: CubismMotionPoint[], time: number): number; - } - - /** - * @brief モーションカーブのセグメント - * - * モーションカーブのセグメント。 - */ - export class CubismMotionSegment { - /** - * @brief コンストラクタ - * - * コンストラクタ。 - */ - public constructor() { - this.evaluate = null; - this.basePointIndex = 0; - this.segmentType = 0; - } - - evaluate: csmMotionSegmentEvaluationFunction; // 使用する評価関数 - basePointIndex: number; // 最初のセグメントへのインデックス - segmentType: number; // セグメントの種類 - } - - /** - * @brief モーションカーブ - * - * モーションカーブ。 - */ - export class CubismMotionCurve { - public constructor() { - this.type = CubismMotionCurveTarget.CubismMotionCurveTarget_Model; - this.segmentCount = 0; - this.baseSegmentIndex = 0; - this.fadeInTime = 0.0; - this.fadeOutTime = 0.0; - } - - type: CubismMotionCurveTarget; // カーブの種類 - id: CubismIdHandle; // カーブのID - segmentCount: number; // セグメントの個数 - baseSegmentIndex: number; // 最初のセグメントのインデックス - fadeInTime: number; // フェードインにかかる時間[秒] - fadeOutTime: number; // フェードアウトにかかる時間[秒] - } - - /** - * イベント。 - */ - export class CubismMotionEvent { - fireTime = 0.0; - value: csmString; - } - - /** - * @brief モーションデータ - * - * モーションデータ。 - */ - export class CubismMotionData { - public constructor() { - this.duration = 0.0; - this.loop = false; - this.curveCount = 0; - this.eventCount = 0; - this.fps = 0.0; - - this.curves = new csmVector(); - this.segments = new csmVector(); - this.points = new csmVector(); - this.events = new csmVector(); - } - - duration: number; // モーションの長さ[秒] - loop: boolean; // ループするかどうか - curveCount: number; // カーブの個数 - eventCount: number; // UserDataの個数 - fps: number; // フレームレート - curves: csmVector; // カーブのリスト - segments: csmVector; // セグメントのリスト - points: csmVector; // ポイントのリスト - events: csmVector; // イベントのリスト - } +/** + * @brief モーションカーブの種類 + * + * モーションカーブの種類。 + */ +export enum CubismMotionCurveTarget { + CubismMotionCurveTarget_Model, // モデルに対して + CubismMotionCurveTarget_Parameter, // パラメータに対して + CubismMotionCurveTarget_PartOpacity // パーツの不透明度に対して +} + +/** + * @brief モーションカーブのセグメントの種類 + * + * モーションカーブのセグメントの種類。 + */ +export enum CubismMotionSegmentType { + CubismMotionSegmentType_Linear = 0, // リニア + CubismMotionSegmentType_Bezier = 1, // ベジェ曲線 + CubismMotionSegmentType_Stepped = 2, // ステップ + CubismMotionSegmentType_InverseStepped = 3 // インバースステップ +} + +/** + * @brief モーションカーブの制御点 + * + * モーションカーブの制御点。 + */ +export class CubismMotionPoint { + time = 0.0; // 時間[秒] + value = 0.0; // 値 +} + +/** + * モーションカーブのセグメントの評価関数 + * + * @param points モーションカーブの制御点リスト + * @param time 評価する時間[秒] + */ +export interface csmMotionSegmentEvaluationFunction { + (points: CubismMotionPoint[], time: number): number; +} + +/** + * @brief モーションカーブのセグメント + * + * モーションカーブのセグメント。 + */ +export class CubismMotionSegment { + /** + * @brief コンストラクタ + * + * コンストラクタ。 + */ + public constructor() { + this.evaluate = null; + this.basePointIndex = 0; + this.segmentType = 0; + } + + evaluate: csmMotionSegmentEvaluationFunction; // 使用する評価関数 + basePointIndex: number; // 最初のセグメントへのインデックス + segmentType: number; // セグメントの種類 +} + +/** + * @brief モーションカーブ + * + * モーションカーブ。 + */ +export class CubismMotionCurve { + public constructor() { + this.type = CubismMotionCurveTarget.CubismMotionCurveTarget_Model; + this.segmentCount = 0; + this.baseSegmentIndex = 0; + this.fadeInTime = 0.0; + this.fadeOutTime = 0.0; + } + + type: CubismMotionCurveTarget; // カーブの種類 + id: CubismIdHandle; // カーブのID + segmentCount: number; // セグメントの個数 + baseSegmentIndex: number; // 最初のセグメントのインデックス + fadeInTime: number; // フェードインにかかる時間[秒] + fadeOutTime: number; // フェードアウトにかかる時間[秒] +} + +/** + * イベント。 + */ +export class CubismMotionEvent { + fireTime = 0.0; + value: csmString; +} + +/** + * @brief モーションデータ + * + * モーションデータ。 + */ +export class CubismMotionData { + public constructor() { + this.duration = 0.0; + this.loop = false; + this.curveCount = 0; + this.eventCount = 0; + this.fps = 0.0; + + this.curves = new csmVector(); + this.segments = new csmVector(); + this.points = new csmVector(); + this.events = new csmVector(); + } + + duration: number; // モーションの長さ[秒] + loop: boolean; // ループするかどうか + curveCount: number; // カーブの個数 + eventCount: number; // UserDataの個数 + fps: number; // フレームレート + curves: csmVector; // カーブのリスト + segments: csmVector; // セグメントのリスト + points: csmVector; // ポイントのリスト + events: csmVector; // イベントのリスト +} + +// Namespace definition for compatibility. +import * as $ from './cubismmotioninternal'; +// eslint-disable-next-line @typescript-eslint/no-namespace +export namespace Live2DCubismFramework { + export const CubismMotionCurve = $.CubismMotionCurve; + export type CubismMotionCurve = $.CubismMotionCurve; + export const CubismMotionCurveTarget = $.CubismMotionCurveTarget; + export type CubismMotionCurveTarget = $.CubismMotionCurveTarget; + export const CubismMotionData = $.CubismMotionData; + export type CubismMotionData = $.CubismMotionData; + export const CubismMotionEvent = $.CubismMotionEvent; + export type CubismMotionEvent = $.CubismMotionEvent; + export const CubismMotionPoint = $.CubismMotionPoint; + export type CubismMotionPoint = $.CubismMotionPoint; + export const CubismMotionSegment = $.CubismMotionSegment; + export type CubismMotionSegment = $.CubismMotionSegment; + export const CubismMotionSegmentType = $.CubismMotionSegmentType; + export type CubismMotionSegmentType = $.CubismMotionSegmentType; + export type csmMotionSegmentEvaluationFunction = $.csmMotionSegmentEvaluationFunction; } diff --git a/src/motion/cubismmotionjson.ts b/src/motion/cubismmotionjson.ts index 77eed8f..30d3ec6 100644 --- a/src/motion/cubismmotionjson.ts +++ b/src/motion/cubismmotionjson.ts @@ -5,355 +5,357 @@ * that can be found at https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html. */ -import { Live2DCubismFramework as cubismjson } from '../utils/cubismjson'; -import { Live2DCubismFramework as cubismid } from '../id/cubismid'; -import { Live2DCubismFramework as cubismframework } from '../live2dcubismframework'; -import { Live2DCubismFramework as csmstring } from '../type/csmstring'; -import csmString = csmstring.csmString; -import CubismFramework = cubismframework.CubismFramework; -import CubismIdHandle = cubismid.CubismIdHandle; -import CubismJson = cubismjson.CubismJson; +import { CubismIdHandle } from '../id/cubismid'; +import { CubismFramework } from '../live2dcubismframework'; +import { csmString } from '../type/csmstring'; +import { CubismJson } from '../utils/cubismjson'; -export namespace Live2DCubismFramework { - // JSON keys - const Meta = 'Meta'; - const Duration = 'Duration'; - const Loop = 'Loop'; - const CurveCount = 'CurveCount'; - const Fps = 'Fps'; - const TotalSegmentCount = 'TotalSegmentCount'; - const TotalPointCount = 'TotalPointCount'; - const Curves = 'Curves'; - const Target = 'Target'; - const Id = 'Id'; - const FadeInTime = 'FadeInTime'; - const FadeOutTime = 'FadeOutTime'; - const Segments = 'Segments'; - const UserData = 'UserData'; - const UserDataCount = 'UserDataCount'; - const TotalUserDataSize = 'TotalUserDataSize'; - const Time = 'Time'; - const Value = 'Value'; +// JSON keys +const Meta = 'Meta'; +const Duration = 'Duration'; +const Loop = 'Loop'; +const CurveCount = 'CurveCount'; +const Fps = 'Fps'; +const TotalSegmentCount = 'TotalSegmentCount'; +const TotalPointCount = 'TotalPointCount'; +const Curves = 'Curves'; +const Target = 'Target'; +const Id = 'Id'; +const FadeInTime = 'FadeInTime'; +const FadeOutTime = 'FadeOutTime'; +const Segments = 'Segments'; +const UserData = 'UserData'; +const UserDataCount = 'UserDataCount'; +const TotalUserDataSize = 'TotalUserDataSize'; +const Time = 'Time'; +const Value = 'Value'; + +/** + * motion3.jsonのコンテナ。 + */ +export class CubismMotionJson { + /** + * コンストラクタ + * @param buffer motion3.jsonが読み込まれているバッファ + * @param size バッファのサイズ + */ + public constructor(buffer: ArrayBuffer, size: number) { + this._json = CubismJson.create(buffer, size); + } /** - * motion3.jsonのコンテナ。 + * デストラクタ相当の処理 */ - export class CubismMotionJson { - /** - * コンストラクタ - * @param buffer motion3.jsonが読み込まれているバッファ - * @param size バッファのサイズ - */ - public constructor(buffer: ArrayBuffer, size: number) { - this._json = CubismJson.create(buffer, size); - } + public release(): void { + CubismJson.delete(this._json); + } - /** - * デストラクタ相当の処理 - */ - public release(): void { - CubismJson.delete(this._json); - } + /** + * モーションの長さを取得する + * @return モーションの長さ[秒] + */ + public getMotionDuration(): number { + return this._json + .getRoot() + .getValueByString(Meta) + .getValueByString(Duration) + .toFloat(); + } - /** - * モーションの長さを取得する - * @return モーションの長さ[秒] - */ - public getMotionDuration(): number { - return this._json - .getRoot() - .getValueByString(Meta) - .getValueByString(Duration) - .toFloat(); - } + /** + * モーションのループ情報の取得 + * @return true ループする + * @return false ループしない + */ + public isMotionLoop(): boolean { + return this._json + .getRoot() + .getValueByString(Meta) + .getValueByString(Loop) + .toBoolean(); + } - /** - * モーションのループ情報の取得 - * @return true ループする - * @return false ループしない - */ - public isMotionLoop(): boolean { - return this._json - .getRoot() - .getValueByString(Meta) - .getValueByString(Loop) - .toBoolean(); - } + /** + * モーションカーブの個数の取得 + * @return モーションカーブの個数 + */ + public getMotionCurveCount(): number { + return this._json + .getRoot() + .getValueByString(Meta) + .getValueByString(CurveCount) + .toInt(); + } - /** - * モーションカーブの個数の取得 - * @return モーションカーブの個数 - */ - public getMotionCurveCount(): number { - return this._json - .getRoot() - .getValueByString(Meta) - .getValueByString(CurveCount) - .toInt(); - } + /** + * モーションのフレームレートの取得 + * @return フレームレート[FPS] + */ + public getMotionFps(): number { + return this._json + .getRoot() + .getValueByString(Meta) + .getValueByString(Fps) + .toFloat(); + } - /** - * モーションのフレームレートの取得 - * @return フレームレート[FPS] - */ - public getMotionFps(): number { - return this._json - .getRoot() - .getValueByString(Meta) - .getValueByString(Fps) - .toFloat(); - } + /** + * モーションのセグメントの総合計の取得 + * @return モーションのセグメントの取得 + */ + public getMotionTotalSegmentCount(): number { + return this._json + .getRoot() + .getValueByString(Meta) + .getValueByString(TotalSegmentCount) + .toInt(); + } - /** - * モーションのセグメントの総合計の取得 - * @return モーションのセグメントの取得 - */ - public getMotionTotalSegmentCount(): number { - return this._json - .getRoot() - .getValueByString(Meta) - .getValueByString(TotalSegmentCount) - .toInt(); - } + /** + * モーションのカーブの制御店の総合計の取得 + * @return モーションのカーブの制御点の総合計 + */ + public getMotionTotalPointCount(): number { + return this._json + .getRoot() + .getValueByString(Meta) + .getValueByString(TotalPointCount) + .toInt(); + } - /** - * モーションのカーブの制御店の総合計の取得 - * @return モーションのカーブの制御点の総合計 - */ - public getMotionTotalPointCount(): number { - return this._json - .getRoot() - .getValueByString(Meta) - .getValueByString(TotalPointCount) - .toInt(); - } + /** + * モーションのフェードイン時間の存在 + * @return true 存在する + * @return false 存在しない + */ + public isExistMotionFadeInTime(): boolean { + return !this._json + .getRoot() + .getValueByString(Meta) + .getValueByString(FadeInTime) + .isNull(); + } - /** - * モーションのフェードイン時間の存在 - * @return true 存在する - * @return false 存在しない - */ - public isExistMotionFadeInTime(): boolean { - return !this._json - .getRoot() - .getValueByString(Meta) - .getValueByString(FadeInTime) - .isNull(); - } + /** + * モーションのフェードアウト時間の存在 + * @return true 存在する + * @return false 存在しない + */ + public isExistMotionFadeOutTime(): boolean { + return !this._json + .getRoot() + .getValueByString(Meta) + .getValueByString(FadeOutTime) + .isNull(); + } - /** - * モーションのフェードアウト時間の存在 - * @return true 存在する - * @return false 存在しない - */ - public isExistMotionFadeOutTime(): boolean { - return !this._json - .getRoot() - .getValueByString(Meta) - .getValueByString(FadeOutTime) - .isNull(); - } + /** + * モーションのフェードイン時間の取得 + * @return フェードイン時間[秒] + */ + public getMotionFadeInTime(): number { + return this._json + .getRoot() + .getValueByString(Meta) + .getValueByString(FadeInTime) + .toFloat(); + } - /** - * モーションのフェードイン時間の取得 - * @return フェードイン時間[秒] - */ - public getMotionFadeInTime(): number { - return this._json - .getRoot() - .getValueByString(Meta) - .getValueByString(FadeInTime) - .toFloat(); - } + /** + * モーションのフェードアウト時間の取得 + * @return フェードアウト時間[秒] + */ + public getMotionFadeOutTime(): number { + return this._json + .getRoot() + .getValueByString(Meta) + .getValueByString(FadeOutTime) + .toFloat(); + } - /** - * モーションのフェードアウト時間の取得 - * @return フェードアウト時間[秒] - */ - public getMotionFadeOutTime(): number { - return this._json - .getRoot() - .getValueByString(Meta) - .getValueByString(FadeOutTime) - .toFloat(); - } + /** + * モーションのカーブの種類の取得 + * @param curveIndex カーブのインデックス + * @return カーブの種類 + */ + public getMotionCurveTarget(curveIndex: number): string { + return this._json + .getRoot() + .getValueByString(Curves) + .getValueByIndex(curveIndex) + .getValueByString(Target) + .getRawString(); + } - /** - * モーションのカーブの種類の取得 - * @param curveIndex カーブのインデックス - * @return カーブの種類 - */ - public getMotionCurveTarget(curveIndex: number): string { - return this._json + /** + * モーションのカーブのIDの取得 + * @param curveIndex カーブのインデックス + * @return カーブのID + */ + public getMotionCurveId(curveIndex: number): CubismIdHandle { + return CubismFramework.getIdManager().getId( + this._json .getRoot() .getValueByString(Curves) .getValueByIndex(curveIndex) - .getValueByString(Target) - .getRawString(); - } + .getValueByString(Id) + .getRawString() + ); + } - /** - * モーションのカーブのIDの取得 - * @param curveIndex カーブのインデックス - * @return カーブのID - */ - public getMotionCurveId(curveIndex: number): CubismIdHandle { - return CubismFramework.getIdManager().getId( - this._json - .getRoot() - .getValueByString(Curves) - .getValueByIndex(curveIndex) - .getValueByString(Id) - .getRawString() - ); - } + /** + * モーションのカーブのフェードイン時間の存在 + * @param curveIndex カーブのインデックス + * @return true 存在する + * @return false 存在しない + */ + public isExistMotionCurveFadeInTime(curveIndex: number): boolean { + return !this._json + .getRoot() + .getValueByString(Curves) + .getValueByIndex(curveIndex) + .getValueByString(FadeInTime) + .isNull(); + } - /** - * モーションのカーブのフェードイン時間の存在 - * @param curveIndex カーブのインデックス - * @return true 存在する - * @return false 存在しない - */ - public isExistMotionCurveFadeInTime(curveIndex: number): boolean { - return !this._json - .getRoot() - .getValueByString(Curves) - .getValueByIndex(curveIndex) - .getValueByString(FadeInTime) - .isNull(); - } + /** + * モーションのカーブのフェードアウト時間の存在 + * @param curveIndex カーブのインデックス + * @return true 存在する + * @return false 存在しない + */ + public isExistMotionCurveFadeOutTime(curveIndex: number): boolean { + return !this._json + .getRoot() + .getValueByString(Curves) + .getValueByIndex(curveIndex) + .getValueByString(FadeOutTime) + .isNull(); + } - /** - * モーションのカーブのフェードアウト時間の存在 - * @param curveIndex カーブのインデックス - * @return true 存在する - * @return false 存在しない - */ - public isExistMotionCurveFadeOutTime(curveIndex: number): boolean { - return !this._json - .getRoot() - .getValueByString(Curves) - .getValueByIndex(curveIndex) - .getValueByString(FadeOutTime) - .isNull(); - } + /** + * モーションのカーブのフェードイン時間の取得 + * @param curveIndex カーブのインデックス + * @return フェードイン時間[秒] + */ + public getMotionCurveFadeInTime(curveIndex: number): number { + return this._json + .getRoot() + .getValueByString(Curves) + .getValueByIndex(curveIndex) + .getValueByString(FadeInTime) + .toFloat(); + } - /** - * モーションのカーブのフェードイン時間の取得 - * @param curveIndex カーブのインデックス - * @return フェードイン時間[秒] - */ - public getMotionCurveFadeInTime(curveIndex: number): number { - return this._json - .getRoot() - .getValueByString(Curves) - .getValueByIndex(curveIndex) - .getValueByString(FadeInTime) - .toFloat(); - } + /** + * モーションのカーブのフェードアウト時間の取得 + * @param curveIndex カーブのインデックス + * @return フェードアウト時間[秒] + */ + public getMotionCurveFadeOutTime(curveIndex: number): number { + return this._json + .getRoot() + .getValueByString(Curves) + .getValueByIndex(curveIndex) + .getValueByString(FadeOutTime) + .toFloat(); + } - /** - * モーションのカーブのフェードアウト時間の取得 - * @param curveIndex カーブのインデックス - * @return フェードアウト時間[秒] - */ - public getMotionCurveFadeOutTime(curveIndex: number): number { - return this._json - .getRoot() - .getValueByString(Curves) - .getValueByIndex(curveIndex) - .getValueByString(FadeOutTime) - .toFloat(); - } + /** + * モーションのカーブのセグメントの個数を取得する + * @param curveIndex カーブのインデックス + * @return モーションのカーブのセグメントの個数 + */ + public getMotionCurveSegmentCount(curveIndex: number): number { + return this._json + .getRoot() + .getValueByString(Curves) + .getValueByIndex(curveIndex) + .getValueByString(Segments) + .getVector() + .getSize(); + } - /** - * モーションのカーブのセグメントの個数を取得する - * @param curveIndex カーブのインデックス - * @return モーションのカーブのセグメントの個数 - */ - public getMotionCurveSegmentCount(curveIndex: number): number { - return this._json - .getRoot() - .getValueByString(Curves) - .getValueByIndex(curveIndex) - .getValueByString(Segments) - .getVector() - .getSize(); - } + /** + * モーションのカーブのセグメントの値の取得 + * @param curveIndex カーブのインデックス + * @param segmentIndex セグメントのインデックス + * @return セグメントの値 + */ + public getMotionCurveSegment( + curveIndex: number, + segmentIndex: number + ): number { + return this._json + .getRoot() + .getValueByString(Curves) + .getValueByIndex(curveIndex) + .getValueByString(Segments) + .getValueByIndex(segmentIndex) + .toFloat(); + } - /** - * モーションのカーブのセグメントの値の取得 - * @param curveIndex カーブのインデックス - * @param segmentIndex セグメントのインデックス - * @return セグメントの値 - */ - public getMotionCurveSegment( - curveIndex: number, - segmentIndex: number - ): number { - return this._json - .getRoot() - .getValueByString(Curves) - .getValueByIndex(curveIndex) - .getValueByString(Segments) - .getValueByIndex(segmentIndex) - .toFloat(); - } + /** + * イベントの個数の取得 + * @return イベントの個数 + */ + public getEventCount(): number { + return this._json + .getRoot() + .getValueByString(Meta) + .getValueByString(UserDataCount) + .toInt(); + } - /** - * イベントの個数の取得 - * @return イベントの個数 - */ - public getEventCount(): number { - return this._json - .getRoot() - .getValueByString(Meta) - .getValueByString(UserDataCount) - .toInt(); - } + /** + * イベントの総文字数の取得 + * @return イベントの総文字数 + */ + public getTotalEventValueSize(): number { + return this._json + .getRoot() + .getValueByString(Meta) + .getValueByString(TotalUserDataSize) + .toInt(); + } - /** - * イベントの総文字数の取得 - * @return イベントの総文字数 - */ - public getTotalEventValueSize(): number { - return this._json - .getRoot() - .getValueByString(Meta) - .getValueByString(TotalUserDataSize) - .toInt(); - } + /** + * イベントの時間の取得 + * @param userDataIndex イベントのインデックス + * @return イベントの時間[秒] + */ + public getEventTime(userDataIndex: number): number { + return this._json + .getRoot() + .getValueByString(UserData) + .getValueByIndex(userDataIndex) + .getValueByString(Time) + .toInt(); + } - /** - * イベントの時間の取得 - * @param userDataIndex イベントのインデックス - * @return イベントの時間[秒] - */ - public getEventTime(userDataIndex: number): number { - return this._json + /** + * イベントの取得 + * @param userDataIndex イベントのインデックス + * @return イベントの文字列 + */ + public getEventValue(userDataIndex: number): csmString { + return new csmString( + this._json .getRoot() .getValueByString(UserData) .getValueByIndex(userDataIndex) - .getValueByString(Time) - .toInt(); - } - - /** - * イベントの取得 - * @param userDataIndex イベントのインデックス - * @return イベントの文字列 - */ - public getEventValue(userDataIndex: number): csmString { - return new csmString( - this._json - .getRoot() - .getValueByString(UserData) - .getValueByIndex(userDataIndex) - .getValueByString(Value) - .getRawString() - ); - } - - _json: CubismJson; // motion3.jsonのデータ + .getValueByString(Value) + .getRawString() + ); } + + _json: CubismJson; // motion3.jsonのデータ +} + +// Namespace definition for compatibility. +import * as $ from './cubismmotionjson'; +// eslint-disable-next-line @typescript-eslint/no-namespace +export namespace Live2DCubismFramework { + export const CubismMotionJson = $.CubismMotionJson; + export type CubismMotionJson = $.CubismMotionJson; } diff --git a/src/motion/cubismmotionmanager.ts b/src/motion/cubismmotionmanager.ts index 5f24d9d..9d1ea48 100644 --- a/src/motion/cubismmotionmanager.ts +++ b/src/motion/cubismmotionmanager.ts @@ -5,120 +5,122 @@ * that can be found at https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html. */ -import { Live2DCubismFramework as cubismmotionqueuemanager } from './cubismmotionqueuemanager'; -import { Live2DCubismFramework as acubismmotion } from './acubismmotion'; -import { Live2DCubismFramework as cubismmodel } from '../model/cubismmodel'; -import CubismMotionQueueEntryHandle = cubismmotionqueuemanager.CubismMotionQueueEntryHandle; -import CubismModel = cubismmodel.CubismModel; -import ACubismMotion = acubismmotion.ACubismMotion; -import CubismMotionQueueManager = cubismmotionqueuemanager.CubismMotionQueueManager; +import { CubismModel } from '../model/cubismmodel'; +import { ACubismMotion } from './acubismmotion'; +import { + CubismMotionQueueEntryHandle, + CubismMotionQueueManager +} from './cubismmotionqueuemanager'; -export namespace Live2DCubismFramework { +/** + * モーションの管理 + * + * モーションの管理を行うクラス + */ +export class CubismMotionManager extends CubismMotionQueueManager { /** - * モーションの管理 - * - * モーションの管理を行うクラス + * コンストラクタ */ - export class CubismMotionManager extends CubismMotionQueueManager { - /** - * コンストラクタ - */ - public constructor() { - super(); - this._currentPriority = 0; - this._reservePriority = 0; - } - - /** - * 再生中のモーションの優先度の取得 - * @return モーションの優先度 - */ - public getCurrentPriority(): number { - return this._currentPriority; - } - - /** - * 予約中のモーションの優先度を取得する。 - * @return モーションの優先度 - */ - public getReservePriority(): number { - return this._reservePriority; - } - - /** - * 予約中のモーションの優先度を設定する。 - * @param val 優先度 - */ - public setReservePriority(val: number): void { - this._reservePriority = val; - } - - /** - * 優先度を設定してモーションを開始する。 - * - * @param motion モーション - * @param autoDelete 再生が狩猟したモーションのインスタンスを削除するならtrue - * @param priority 優先度 - * @return 開始したモーションの識別番号を返す。個別のモーションが終了したか否かを判定するIsFinished()の引数で使用する。開始できない時は「-1」 - */ - public startMotionPriority( - motion: ACubismMotion, - autoDelete: boolean, - priority: number - ): CubismMotionQueueEntryHandle { - if (priority == this._reservePriority) { - this._reservePriority = 0; // 予約を解除 - } - - this._currentPriority = priority; // 再生中モーションの優先度を設定 - - return super.startMotion(motion, autoDelete, this._userTimeSeconds); - } - - /** - * モーションを更新して、モデルにパラメータ値を反映する。 - * - * @param model 対象のモデル - * @param deltaTimeSeconds デルタ時間[秒] - * @return true 更新されている - * @return false 更新されていない - */ - public updateMotion(model: CubismModel, deltaTimeSeconds: number): boolean { - this._userTimeSeconds += deltaTimeSeconds; - - const updated: boolean = super.doUpdateMotion( - model, - this._userTimeSeconds - ); - - if (this.isFinished()) { - this._currentPriority = 0; // 再生中のモーションの優先度を解除 - } - - return updated; - } - - /** - * モーションを予約する。 - * - * @param priority 優先度 - * @return true 予約できた - * @return false 予約できなかった - */ - public reserveMotion(priority: number): boolean { - if ( - priority <= this._reservePriority || - priority <= this._currentPriority - ) { - return false; - } - - this._reservePriority = priority; - - return true; - } - - _currentPriority: number; // 現在再生中のモーションの優先度 - _reservePriority: number; // 再生予定のモーションの優先度。再生中は0になる。モーションファイルを別スレッドで読み込むときの機能。 + public constructor() { + super(); + this._currentPriority = 0; + this._reservePriority = 0; } + + /** + * 再生中のモーションの優先度の取得 + * @return モーションの優先度 + */ + public getCurrentPriority(): number { + return this._currentPriority; + } + + /** + * 予約中のモーションの優先度を取得する。 + * @return モーションの優先度 + */ + public getReservePriority(): number { + return this._reservePriority; + } + + /** + * 予約中のモーションの優先度を設定する。 + * @param val 優先度 + */ + public setReservePriority(val: number): void { + this._reservePriority = val; + } + + /** + * 優先度を設定してモーションを開始する。 + * + * @param motion モーション + * @param autoDelete 再生が狩猟したモーションのインスタンスを削除するならtrue + * @param priority 優先度 + * @return 開始したモーションの識別番号を返す。個別のモーションが終了したか否かを判定するIsFinished()の引数で使用する。開始できない時は「-1」 + */ + public startMotionPriority( + motion: ACubismMotion, + autoDelete: boolean, + priority: number + ): CubismMotionQueueEntryHandle { + if (priority == this._reservePriority) { + this._reservePriority = 0; // 予約を解除 + } + + this._currentPriority = priority; // 再生中モーションの優先度を設定 + + return super.startMotion(motion, autoDelete, this._userTimeSeconds); + } + + /** + * モーションを更新して、モデルにパラメータ値を反映する。 + * + * @param model 対象のモデル + * @param deltaTimeSeconds デルタ時間[秒] + * @return true 更新されている + * @return false 更新されていない + */ + public updateMotion(model: CubismModel, deltaTimeSeconds: number): boolean { + this._userTimeSeconds += deltaTimeSeconds; + + const updated: boolean = super.doUpdateMotion(model, this._userTimeSeconds); + + if (this.isFinished()) { + this._currentPriority = 0; // 再生中のモーションの優先度を解除 + } + + return updated; + } + + /** + * モーションを予約する。 + * + * @param priority 優先度 + * @return true 予約できた + * @return false 予約できなかった + */ + public reserveMotion(priority: number): boolean { + if ( + priority <= this._reservePriority || + priority <= this._currentPriority + ) { + return false; + } + + this._reservePriority = priority; + + return true; + } + + _currentPriority: number; // 現在再生中のモーションの優先度 + _reservePriority: number; // 再生予定のモーションの優先度。再生中は0になる。モーションファイルを別スレッドで読み込むときの機能。 +} + +// Namespace definition for compatibility. +import * as $ from './cubismmotionmanager'; +// eslint-disable-next-line @typescript-eslint/no-namespace +export namespace Live2DCubismFramework { + export const CubismMotionManager = $.CubismMotionManager; + export type CubismMotionManager = $.CubismMotionManager; } diff --git a/src/motion/cubismmotionqueueentry.ts b/src/motion/cubismmotionqueueentry.ts index 7c431e3..c9dca2e 100644 --- a/src/motion/cubismmotionqueueentry.ts +++ b/src/motion/cubismmotionqueueentry.ts @@ -5,245 +5,249 @@ * that can be found at https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html. */ -import { Live2DCubismFramework as acubismmotion } from './acubismmotion'; -import { Live2DCubismFramework as cubismmotionqueuemanager } from './cubismmotionqueuemanager'; -import CubismMotionQueueEntryHandle = cubismmotionqueuemanager.CubismMotionQueueEntryHandle; -import ACubismMotion = acubismmotion.ACubismMotion; +import { ACubismMotion } from './acubismmotion'; +import { CubismMotionQueueEntryHandle } from './cubismmotionqueuemanager'; -export namespace Live2DCubismFramework { +/** + * CubismMotionQueueManagerで再生している各モーションの管理クラス。 + */ +export class CubismMotionQueueEntry { /** - * CubismMotionQueueManagerで再生している各モーションの管理クラス。 + * コンストラクタ */ - export class CubismMotionQueueEntry { - /** - * コンストラクタ - */ - public constructor() { - this._autoDelete = false; - this._motion = null; - this._available = true; - this._finished = false; - this._started = false; - this._startTimeSeconds = -1.0; - this._fadeInStartTimeSeconds = 0.0; - this._endTimeSeconds = -1.0; - this._stateTimeSeconds = 0.0; - this._stateWeight = 0.0; - this._lastEventCheckSeconds = 0.0; - this._motionQueueEntryHandle = this; - this._fadeOutSeconds = 0.0; - this._isTriggeredFadeOut = false; - } - - /** - * デストラクタ相当の処理 - */ - public release(): void { - if (this._autoDelete && this._motion) { - ACubismMotion.delete(this._motion); // - } - } - - /** - * フェードアウト時間と開始判定の設定 - * @param fadeOutSeconds フェードアウトにかかる時間[秒] - */ - public setFadeOut(fadeOutSeconds: number): void { - this._fadeOutSeconds = fadeOutSeconds; - this._isTriggeredFadeOut = true; - } - - /** - * フェードアウトの開始 - * @param fadeOutSeconds フェードアウトにかかる時間[秒] - * @param userTimeSeconds デルタ時間の積算値[秒] - */ - public startFadeOut(fadeOutSeconds: number, userTimeSeconds: number): void { - const newEndTimeSeconds: number = userTimeSeconds + fadeOutSeconds; - this._isTriggeredFadeOut = true; - - if ( - this._endTimeSeconds < 0.0 || - newEndTimeSeconds < this._endTimeSeconds - ) { - this._endTimeSeconds = newEndTimeSeconds; - } - } - - /** - * モーションの終了の確認 - * - * @return true モーションが終了した - * @return false 終了していない - */ - public isFinished(): boolean { - return this._finished; - } - - /** - * モーションの開始の確認 - * @return true モーションが開始した - * @return false 開始していない - */ - public isStarted(): boolean { - return this._started; - } - - /** - * モーションの開始時刻の取得 - * @return モーションの開始時刻[秒] - */ - public getStartTime(): number { - return this._startTimeSeconds; - } - - /** - * フェードインの開始時刻の取得 - * @return フェードインの開始時刻[秒] - */ - public getFadeInStartTime(): number { - return this._fadeInStartTimeSeconds; - } - - /** - * フェードインの終了時刻の取得 - * @return フェードインの終了時刻の取得 - */ - public getEndTime(): number { - return this._endTimeSeconds; - } - - /** - * モーションの開始時刻の設定 - * @param startTime モーションの開始時刻 - */ - public setStartTime(startTime: number): void { - this._startTimeSeconds = startTime; - } - - /** - * フェードインの開始時刻の設定 - * @param startTime フェードインの開始時刻[秒] - */ - public setFadeInStartTime(startTime: number): void { - this._fadeInStartTimeSeconds = startTime; - } - - /** - * フェードインの終了時刻の設定 - * @param endTime フェードインの終了時刻[秒] - */ - public setEndTime(endTime: number): void { - this._endTimeSeconds = endTime; - } - - /** - * モーションの終了の設定 - * @param f trueならモーションの終了 - */ - public setIsFinished(f: boolean): void { - this._finished = f; - } - - /** - * モーション開始の設定 - * @param f trueならモーションの開始 - */ - public setIsStarted(f: boolean): void { - this._started = f; - } - - /** - * モーションの有効性の確認 - * @return true モーションは有効 - * @return false モーションは無効 - */ - public isAvailable(): boolean { - return this._available; - } - - /** - * モーションの有効性の設定 - * @param v trueならモーションは有効 - */ - public setIsAvailable(v: boolean): void { - this._available = v; - } - - /** - * モーションの状態の設定 - * @param timeSeconds 現在時刻[秒] - * @param weight モーション尾重み - */ - public setState(timeSeconds: number, weight: number): void { - this._stateTimeSeconds = timeSeconds; - this._stateWeight = weight; - } - - /** - * モーションの現在時刻の取得 - * @return モーションの現在時刻[秒] - */ - public getStateTime(): number { - return this._stateTimeSeconds; - } - - /** - * モーションの重みの取得 - * @return モーションの重み - */ - public getStateWeight(): number { - return this._stateWeight; - } - - /** - * 最後にイベントの発火をチェックした時間を取得 - * - * @return 最後にイベントの発火をチェックした時間[秒] - */ - public getLastCheckEventSeconds(): number { - return this._lastEventCheckSeconds; - } - - /** - * 最後にイベントをチェックした時間を設定 - * @param checkSeconds 最後にイベントをチェックした時間[秒] - */ - public setLastCheckEventSeconds(checkSeconds: number): void { - this._lastEventCheckSeconds = checkSeconds; - } - - /** - * フェードアウト開始判定の取得 - * @return フェードアウト開始するかどうか - */ - public isTriggeredFadeOut(): boolean { - return this._isTriggeredFadeOut && this._endTimeSeconds < 0.0; - } - - /** - * フェードアウト時間の取得 - * @return フェードアウト時間[秒] - */ - public getFadeOutSeconds(): number { - return this._fadeOutSeconds; - } - - _autoDelete: boolean; // 自動削除 - _motion: ACubismMotion; // モーション - - _available: boolean; // 有効化フラグ - _finished: boolean; // 終了フラグ - _started: boolean; // 開始フラグ - _startTimeSeconds: number; // モーション再生開始時刻[秒] - _fadeInStartTimeSeconds: number; // フェードイン開始時刻(ループの時は初回のみ)[秒] - _endTimeSeconds: number; // 終了予定時刻[秒] - _stateTimeSeconds: number; // 時刻の状態[秒] - _stateWeight: number; // 重みの状態 - _lastEventCheckSeconds: number; // 最終のMotion側のチェックした時間 - private _fadeOutSeconds: number; // フェードアウト時間[秒] - private _isTriggeredFadeOut: boolean; // フェードアウト開始フラグ - - _motionQueueEntryHandle: CubismMotionQueueEntryHandle; // インスタンスごとに一意の値を持つ識別番号 + public constructor() { + this._autoDelete = false; + this._motion = null; + this._available = true; + this._finished = false; + this._started = false; + this._startTimeSeconds = -1.0; + this._fadeInStartTimeSeconds = 0.0; + this._endTimeSeconds = -1.0; + this._stateTimeSeconds = 0.0; + this._stateWeight = 0.0; + this._lastEventCheckSeconds = 0.0; + this._motionQueueEntryHandle = this; + this._fadeOutSeconds = 0.0; + this._isTriggeredFadeOut = false; } + + /** + * デストラクタ相当の処理 + */ + public release(): void { + if (this._autoDelete && this._motion) { + ACubismMotion.delete(this._motion); // + } + } + + /** + * フェードアウト時間と開始判定の設定 + * @param fadeOutSeconds フェードアウトにかかる時間[秒] + */ + public setFadeOut(fadeOutSeconds: number): void { + this._fadeOutSeconds = fadeOutSeconds; + this._isTriggeredFadeOut = true; + } + + /** + * フェードアウトの開始 + * @param fadeOutSeconds フェードアウトにかかる時間[秒] + * @param userTimeSeconds デルタ時間の積算値[秒] + */ + public startFadeOut(fadeOutSeconds: number, userTimeSeconds: number): void { + const newEndTimeSeconds: number = userTimeSeconds + fadeOutSeconds; + this._isTriggeredFadeOut = true; + + if ( + this._endTimeSeconds < 0.0 || + newEndTimeSeconds < this._endTimeSeconds + ) { + this._endTimeSeconds = newEndTimeSeconds; + } + } + + /** + * モーションの終了の確認 + * + * @return true モーションが終了した + * @return false 終了していない + */ + public isFinished(): boolean { + return this._finished; + } + + /** + * モーションの開始の確認 + * @return true モーションが開始した + * @return false 開始していない + */ + public isStarted(): boolean { + return this._started; + } + + /** + * モーションの開始時刻の取得 + * @return モーションの開始時刻[秒] + */ + public getStartTime(): number { + return this._startTimeSeconds; + } + + /** + * フェードインの開始時刻の取得 + * @return フェードインの開始時刻[秒] + */ + public getFadeInStartTime(): number { + return this._fadeInStartTimeSeconds; + } + + /** + * フェードインの終了時刻の取得 + * @return フェードインの終了時刻の取得 + */ + public getEndTime(): number { + return this._endTimeSeconds; + } + + /** + * モーションの開始時刻の設定 + * @param startTime モーションの開始時刻 + */ + public setStartTime(startTime: number): void { + this._startTimeSeconds = startTime; + } + + /** + * フェードインの開始時刻の設定 + * @param startTime フェードインの開始時刻[秒] + */ + public setFadeInStartTime(startTime: number): void { + this._fadeInStartTimeSeconds = startTime; + } + + /** + * フェードインの終了時刻の設定 + * @param endTime フェードインの終了時刻[秒] + */ + public setEndTime(endTime: number): void { + this._endTimeSeconds = endTime; + } + + /** + * モーションの終了の設定 + * @param f trueならモーションの終了 + */ + public setIsFinished(f: boolean): void { + this._finished = f; + } + + /** + * モーション開始の設定 + * @param f trueならモーションの開始 + */ + public setIsStarted(f: boolean): void { + this._started = f; + } + + /** + * モーションの有効性の確認 + * @return true モーションは有効 + * @return false モーションは無効 + */ + public isAvailable(): boolean { + return this._available; + } + + /** + * モーションの有効性の設定 + * @param v trueならモーションは有効 + */ + public setIsAvailable(v: boolean): void { + this._available = v; + } + + /** + * モーションの状態の設定 + * @param timeSeconds 現在時刻[秒] + * @param weight モーション尾重み + */ + public setState(timeSeconds: number, weight: number): void { + this._stateTimeSeconds = timeSeconds; + this._stateWeight = weight; + } + + /** + * モーションの現在時刻の取得 + * @return モーションの現在時刻[秒] + */ + public getStateTime(): number { + return this._stateTimeSeconds; + } + + /** + * モーションの重みの取得 + * @return モーションの重み + */ + public getStateWeight(): number { + return this._stateWeight; + } + + /** + * 最後にイベントの発火をチェックした時間を取得 + * + * @return 最後にイベントの発火をチェックした時間[秒] + */ + public getLastCheckEventSeconds(): number { + return this._lastEventCheckSeconds; + } + + /** + * 最後にイベントをチェックした時間を設定 + * @param checkSeconds 最後にイベントをチェックした時間[秒] + */ + public setLastCheckEventSeconds(checkSeconds: number): void { + this._lastEventCheckSeconds = checkSeconds; + } + + /** + * フェードアウト開始判定の取得 + * @return フェードアウト開始するかどうか + */ + public isTriggeredFadeOut(): boolean { + return this._isTriggeredFadeOut && this._endTimeSeconds < 0.0; + } + + /** + * フェードアウト時間の取得 + * @return フェードアウト時間[秒] + */ + public getFadeOutSeconds(): number { + return this._fadeOutSeconds; + } + + _autoDelete: boolean; // 自動削除 + _motion: ACubismMotion; // モーション + + _available: boolean; // 有効化フラグ + _finished: boolean; // 終了フラグ + _started: boolean; // 開始フラグ + _startTimeSeconds: number; // モーション再生開始時刻[秒] + _fadeInStartTimeSeconds: number; // フェードイン開始時刻(ループの時は初回のみ)[秒] + _endTimeSeconds: number; // 終了予定時刻[秒] + _stateTimeSeconds: number; // 時刻の状態[秒] + _stateWeight: number; // 重みの状態 + _lastEventCheckSeconds: number; // 最終のMotion側のチェックした時間 + private _fadeOutSeconds: number; // フェードアウト時間[秒] + private _isTriggeredFadeOut: boolean; // フェードアウト開始フラグ + + _motionQueueEntryHandle: CubismMotionQueueEntryHandle; // インスタンスごとに一意の値を持つ識別番号 +} + +// Namespace definition for compatibility. +import * as $ from './cubismmotionqueueentry'; +// eslint-disable-next-line @typescript-eslint/no-namespace +export namespace Live2DCubismFramework { + export const CubismMotionQueueEntry = $.CubismMotionQueueEntry; + export type CubismMotionQueueEntry = $.CubismMotionQueueEntry; } diff --git a/src/motion/cubismmotionqueuemanager.ts b/src/motion/cubismmotionqueuemanager.ts index 597f4fd..a5df256 100644 --- a/src/motion/cubismmotionqueuemanager.ts +++ b/src/motion/cubismmotionqueuemanager.ts @@ -5,346 +5,345 @@ * that can be found at https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html. */ -import { Live2DCubismFramework as acubismmotion } from './acubismmotion'; -import { Live2DCubismFramework as cubismmotionqueueentry } from './cubismmotionqueueentry'; -import { Live2DCubismFramework as csmvector } from '../type/csmvector'; -import { Live2DCubismFramework as cubismmodel } from '../model/cubismmodel'; -import { Live2DCubismFramework as csmstring } from '../type/csmstring'; -import csmString = csmstring.csmString; -import CubismModel = cubismmodel.CubismModel; -import csmVector = csmvector.csmVector; -import iterator = csmvector.iterator; -import CubismMotionQueueEntry = cubismmotionqueueentry.CubismMotionQueueEntry; -import ACubismMotion = acubismmotion.ACubismMotion; +import { ACubismMotion } from './acubismmotion'; +import { CubismMotionQueueEntry } from './cubismmotionqueueentry'; +import { csmVector, iterator } from '../type/csmvector'; +import { CubismModel } from '../model/cubismmodel'; +import { csmString } from '../type/csmstring'; -export namespace Live2DCubismFramework { +/** + * モーション再生の管理 + * + * モーション再生の管理用クラス。CubismMotionモーションなどACubismMotionのサブクラスを再生するために使用する。 + * + * @note 再生中に別のモーションが StartMotion()された場合は、新しいモーションに滑らかに変化し旧モーションは中断する。 + * 表情用モーション、体用モーションなどを分けてモーション化した場合など、 + * 複数のモーションを同時に再生させる場合は、複数のCubismMotionQueueManagerインスタンスを使用する。 + */ +export class CubismMotionQueueManager { /** - * モーション再生の管理 - * - * モーション再生の管理用クラス。CubismMotionモーションなどACubismMotionのサブクラスを再生するために使用する。 - * - * @note 再生中に別のモーションが StartMotion()された場合は、新しいモーションに滑らかに変化し旧モーションは中断する。 - * 表情用モーション、体用モーションなどを分けてモーション化した場合など、 - * 複数のモーションを同時に再生させる場合は、複数のCubismMotionQueueManagerインスタンスを使用する。 + * コンストラクタ */ - export class CubismMotionQueueManager { - /** - * コンストラクタ - */ - public constructor() { - this._userTimeSeconds = 0.0; - this._eventCallBack = null; - this._eventCustomData = null; - this._motions = new csmVector(); + public constructor() { + this._userTimeSeconds = 0.0; + this._eventCallBack = null; + this._eventCustomData = null; + this._motions = new csmVector(); + } + + /** + * デストラクタ + */ + public release(): void { + for (let i = 0; i < this._motions.getSize(); ++i) { + if (this._motions.at(i)) { + this._motions.at(i).release(); + this._motions.set(i, void 0); + this._motions.set(i, null); + } } - /** - * デストラクタ - */ - public release(): void { - for (let i = 0; i < this._motions.getSize(); ++i) { - if (this._motions.at(i)) { - this._motions.at(i).release(); - this._motions.set(i, void 0); - this._motions.set(i, null); - } + this._motions = null; + } + + /** + * 指定したモーションの開始 + * + * 指定したモーションを開始する。同じタイプのモーションが既にある場合は、既存のモーションに終了フラグを立て、フェードアウトを開始させる。 + * + * @param motion 開始するモーション + * @param autoDelete 再生が終了したモーションのインスタンスを削除するなら true + * @param userTimeSeconds デルタ時間の積算値[秒] + * @return 開始したモーションの識別番号を返す。個別のモーションが終了したか否かを判定するIsFinished()の引数で使用する。開始できない時は「-1」 + */ + public startMotion( + motion: ACubismMotion, + autoDelete: boolean, + userTimeSeconds: number + ): CubismMotionQueueEntryHandle { + if (motion == null) { + return InvalidMotionQueueEntryHandleValue; + } + + let motionQueueEntry: CubismMotionQueueEntry = null; + + // 既にモーションがあれば終了フラグを立てる + for (let i = 0; i < this._motions.getSize(); ++i) { + motionQueueEntry = this._motions.at(i); + if (motionQueueEntry == null) { + continue; } - this._motions = null; + motionQueueEntry.setFadeOut(motionQueueEntry._motion.getFadeOutTime()); // フェードアウト設定 } - /** - * 指定したモーションの開始 - * - * 指定したモーションを開始する。同じタイプのモーションが既にある場合は、既存のモーションに終了フラグを立て、フェードアウトを開始させる。 - * - * @param motion 開始するモーション - * @param autoDelete 再生が終了したモーションのインスタンスを削除するなら true - * @param userTimeSeconds デルタ時間の積算値[秒] - * @return 開始したモーションの識別番号を返す。個別のモーションが終了したか否かを判定するIsFinished()の引数で使用する。開始できない時は「-1」 - */ - public startMotion( - motion: ACubismMotion, - autoDelete: boolean, - userTimeSeconds: number - ): CubismMotionQueueEntryHandle { + motionQueueEntry = new CubismMotionQueueEntry(); // 終了時に破棄する + motionQueueEntry._autoDelete = autoDelete; + motionQueueEntry._motion = motion; + + this._motions.pushBack(motionQueueEntry); + + return motionQueueEntry._motionQueueEntryHandle; + } + + /** + * 全てのモーションの終了の確認 + * @return true 全て終了している + * @return false 終了していない + */ + public isFinished(): boolean { + // ------- 処理を行う ------- + // 既にモーションがあれば終了フラグを立てる + + for ( + let ite: iterator = this._motions.begin(); + ite.notEqual(this._motions.end()); + + ) { + let motionQueueEntry: CubismMotionQueueEntry = ite.ptr(); + + if (motionQueueEntry == null) { + ite = this._motions.erase(ite); // 削除 + continue; + } + + const motion: ACubismMotion = motionQueueEntry._motion; + if (motion == null) { - return InvalidMotionQueueEntryHandleValue; - } - - let motionQueueEntry: CubismMotionQueueEntry = null; - - // 既にモーションがあれば終了フラグを立てる - for (let i = 0; i < this._motions.getSize(); ++i) { - motionQueueEntry = this._motions.at(i); - if (motionQueueEntry == null) { - continue; - } - - motionQueueEntry.setFadeOut(motionQueueEntry._motion.getFadeOutTime()); // フェードアウト設定 - } - - motionQueueEntry = new CubismMotionQueueEntry(); // 終了時に破棄する - motionQueueEntry._autoDelete = autoDelete; - motionQueueEntry._motion = motion; - - this._motions.pushBack(motionQueueEntry); - - return motionQueueEntry._motionQueueEntryHandle; - } - - /** - * 全てのモーションの終了の確認 - * @return true 全て終了している - * @return false 終了していない - */ - public isFinished(): boolean { - // ------- 処理を行う ------- - // 既にモーションがあれば終了フラグを立てる - - for ( - let ite: iterator = this._motions.begin(); - ite.notEqual(this._motions.end()); - - ) { - let motionQueueEntry: CubismMotionQueueEntry = ite.ptr(); - - if (motionQueueEntry == null) { - ite = this._motions.erase(ite); // 削除 - continue; - } - - const motion: ACubismMotion = motionQueueEntry._motion; - - if (motion == null) { - motionQueueEntry.release(); - motionQueueEntry = void 0; - motionQueueEntry = null; - ite = this._motions.erase(ite); // 削除 - continue; - } - - // ----- 終了済みの処理があれば削除する ------ - if (!motionQueueEntry.isFinished()) { - return false; - } else { - ite.preIncrement(); - } - } - - return true; - } - - /** - * 指定したモーションの終了の確認 - * @param motionQueueEntryNumber モーションの識別番号 - * @return true 全て終了している - * @return false 終了していない - */ - public isFinishedByHandle( - motionQueueEntryNumber: CubismMotionQueueEntryHandle - ): boolean { - // 既にモーションがあれば終了フラグを立てる - for ( - let ite: iterator = this._motions.begin(); - ite.notEqual(this._motions.end()); - ite.increment() - ) { - const motionQueueEntry: CubismMotionQueueEntry = ite.ptr(); - - if (motionQueueEntry == null) { - continue; - } - - if ( - motionQueueEntry._motionQueueEntryHandle == motionQueueEntryNumber && - !motionQueueEntry.isFinished() - ) { - return false; - } - } - return true; - } - - /** - * 全てのモーションを停止する - */ - public stopAllMotions(): void { - // ------- 処理を行う ------- - // 既にモーションがあれば終了フラグを立てる - - for ( - let ite: iterator = this._motions.begin(); - ite.notEqual(this._motions.end()); - - ) { - let motionQueueEntry: CubismMotionQueueEntry = ite.ptr(); - - if (motionQueueEntry == null) { - ite = this._motions.erase(ite); - - continue; - } - - // ----- 終了済みの処理があれば削除する ------ motionQueueEntry.release(); motionQueueEntry = void 0; motionQueueEntry = null; ite = this._motions.erase(ite); // 削除 + continue; + } + + // ----- 終了済みの処理があれば削除する ------ + if (!motionQueueEntry.isFinished()) { + return false; + } else { + ite.preIncrement(); } } - /** - * 指定したCubismMotionQueueEntryの取得 - - * @param motionQueueEntryNumber モーションの識別番号 - * @return 指定したCubismMotionQueueEntry - * @return null 見つからなかった - */ - public getCubismMotionQueueEntry( - motionQueueEntryNumber: any - ): CubismMotionQueueEntry { - //------- 処理を行う ------- - // 既にモーションがあれば終了フラグを立てる - for ( - let ite: iterator = this._motions.begin(); - ite.notEqual(this._motions.end()); - ite.preIncrement() - ) { - const motionQueueEntry: CubismMotionQueueEntry = ite.ptr(); - - if (motionQueueEntry == null) { - continue; - } - - if ( - motionQueueEntry._motionQueueEntryHandle == motionQueueEntryNumber - ) { - return motionQueueEntry; - } - } - - return null; - } - - /** - * イベントを受け取るCallbackの登録 - * - * @param callback コールバック関数 - * @param customData コールバックに返されるデータ - */ - public setEventCallback( - callback: CubismMotionEventFunction, - customData: any = null - ): void { - this._eventCallBack = callback; - this._eventCustomData = customData; - } - - /** - * モーションを更新して、モデルにパラメータ値を反映する。 - * - * @param model 対象のモデル - * @param userTimeSeconds デルタ時間の積算値[秒] - * @return true モデルへパラメータ値の反映あり - * @return false モデルへパラメータ値の反映なし(モーションの変化なし) - */ - public doUpdateMotion( - model: CubismModel, - userTimeSeconds: number - ): boolean { - let updated = false; - - // ------- 処理を行う -------- - // 既にモーションがあれば終了フラグを立てる - - for ( - let ite: iterator = this._motions.begin(); - ite.notEqual(this._motions.end()); - - ) { - let motionQueueEntry: CubismMotionQueueEntry = ite.ptr(); - - if (motionQueueEntry == null) { - ite = this._motions.erase(ite); // 削除 - continue; - } - - const motion: ACubismMotion = motionQueueEntry._motion; - - if (motion == null) { - motionQueueEntry.release(); - motionQueueEntry = void 0; - motionQueueEntry = null; - ite = this._motions.erase(ite); // 削除 - - continue; - } - - // ------ 値を反映する ------ - motion.updateParameters(model, motionQueueEntry, userTimeSeconds); - updated = true; - - // ------ ユーザトリガーイベントを検査する ---- - const firedList: csmVector = motion.getFiredEvent( - motionQueueEntry.getLastCheckEventSeconds() - - motionQueueEntry.getStartTime(), - userTimeSeconds - motionQueueEntry.getStartTime() - ); - - for (let i = 0; i < firedList.getSize(); ++i) { - this._eventCallBack(this, firedList.at(i), this._eventCustomData); - } - - motionQueueEntry.setLastCheckEventSeconds(userTimeSeconds); - - // ------ 終了済みの処理があれば削除する ------ - if (motionQueueEntry.isFinished()) { - motionQueueEntry.release(); - motionQueueEntry = void 0; - motionQueueEntry = null; - ite = this._motions.erase(ite); // 削除 - } else { - if (motionQueueEntry.isTriggeredFadeOut()) { - motionQueueEntry.startFadeOut( - motionQueueEntry.getFadeOutSeconds(), - userTimeSeconds - ); - } - ite.preIncrement(); - } - } - - return updated; - } - _userTimeSeconds: number; // デルタ時間の積算値[秒] - - _motions: csmVector; // モーション - _eventCallBack: CubismMotionEventFunction; // コールバック関数 - _eventCustomData: any; // コールバックに戻されるデータ + return true; } /** - * イベントのコールバック関数を定義 - * - * イベントのコールバックに登録できる関数の型情報 - * @param caller 発火したイベントを再生させたCubismMotionQueueManager - * @param eventValue 発火したイベントの文字列データ - * @param customData コールバックに返される登録時に指定されたデータ + * 指定したモーションの終了の確認 + * @param motionQueueEntryNumber モーションの識別番号 + * @return true 全て終了している + * @return false 終了していない */ - export interface CubismMotionEventFunction { - ( - caller: CubismMotionQueueManager, - eventValue: csmString, - customData: any - ): void; + public isFinishedByHandle( + motionQueueEntryNumber: CubismMotionQueueEntryHandle + ): boolean { + // 既にモーションがあれば終了フラグを立てる + for ( + let ite: iterator = this._motions.begin(); + ite.notEqual(this._motions.end()); + ite.increment() + ) { + const motionQueueEntry: CubismMotionQueueEntry = ite.ptr(); + + if (motionQueueEntry == null) { + continue; + } + + if ( + motionQueueEntry._motionQueueEntryHandle == motionQueueEntryNumber && + !motionQueueEntry.isFinished() + ) { + return false; + } + } + return true; } /** - * モーションの識別番号 - * - * モーションの識別番号の定義 + * 全てのモーションを停止する */ - export declare type CubismMotionQueueEntryHandle = any; - export const InvalidMotionQueueEntryHandleValue: CubismMotionQueueEntryHandle = -1; + public stopAllMotions(): void { + // ------- 処理を行う ------- + // 既にモーションがあれば終了フラグを立てる + + for ( + let ite: iterator = this._motions.begin(); + ite.notEqual(this._motions.end()); + + ) { + let motionQueueEntry: CubismMotionQueueEntry = ite.ptr(); + + if (motionQueueEntry == null) { + ite = this._motions.erase(ite); + + continue; + } + + // ----- 終了済みの処理があれば削除する ------ + motionQueueEntry.release(); + motionQueueEntry = void 0; + motionQueueEntry = null; + ite = this._motions.erase(ite); // 削除 + } + } + + /** + * 指定したCubismMotionQueueEntryの取得 + + * @param motionQueueEntryNumber モーションの識別番号 + * @return 指定したCubismMotionQueueEntry + * @return null 見つからなかった + */ + public getCubismMotionQueueEntry( + motionQueueEntryNumber: any + ): CubismMotionQueueEntry { + //------- 処理を行う ------- + // 既にモーションがあれば終了フラグを立てる + for ( + let ite: iterator = this._motions.begin(); + ite.notEqual(this._motions.end()); + ite.preIncrement() + ) { + const motionQueueEntry: CubismMotionQueueEntry = ite.ptr(); + + if (motionQueueEntry == null) { + continue; + } + + if (motionQueueEntry._motionQueueEntryHandle == motionQueueEntryNumber) { + return motionQueueEntry; + } + } + + return null; + } + + /** + * イベントを受け取るCallbackの登録 + * + * @param callback コールバック関数 + * @param customData コールバックに返されるデータ + */ + public setEventCallback( + callback: CubismMotionEventFunction, + customData: any = null + ): void { + this._eventCallBack = callback; + this._eventCustomData = customData; + } + + /** + * モーションを更新して、モデルにパラメータ値を反映する。 + * + * @param model 対象のモデル + * @param userTimeSeconds デルタ時間の積算値[秒] + * @return true モデルへパラメータ値の反映あり + * @return false モデルへパラメータ値の反映なし(モーションの変化なし) + */ + public doUpdateMotion(model: CubismModel, userTimeSeconds: number): boolean { + let updated = false; + + // ------- 処理を行う -------- + // 既にモーションがあれば終了フラグを立てる + + for ( + let ite: iterator = this._motions.begin(); + ite.notEqual(this._motions.end()); + + ) { + let motionQueueEntry: CubismMotionQueueEntry = ite.ptr(); + + if (motionQueueEntry == null) { + ite = this._motions.erase(ite); // 削除 + continue; + } + + const motion: ACubismMotion = motionQueueEntry._motion; + + if (motion == null) { + motionQueueEntry.release(); + motionQueueEntry = void 0; + motionQueueEntry = null; + ite = this._motions.erase(ite); // 削除 + + continue; + } + + // ------ 値を反映する ------ + motion.updateParameters(model, motionQueueEntry, userTimeSeconds); + updated = true; + + // ------ ユーザトリガーイベントを検査する ---- + const firedList: csmVector = motion.getFiredEvent( + motionQueueEntry.getLastCheckEventSeconds() - + motionQueueEntry.getStartTime(), + userTimeSeconds - motionQueueEntry.getStartTime() + ); + + for (let i = 0; i < firedList.getSize(); ++i) { + this._eventCallBack(this, firedList.at(i), this._eventCustomData); + } + + motionQueueEntry.setLastCheckEventSeconds(userTimeSeconds); + + // ------ 終了済みの処理があれば削除する ------ + if (motionQueueEntry.isFinished()) { + motionQueueEntry.release(); + motionQueueEntry = void 0; + motionQueueEntry = null; + ite = this._motions.erase(ite); // 削除 + } else { + if (motionQueueEntry.isTriggeredFadeOut()) { + motionQueueEntry.startFadeOut( + motionQueueEntry.getFadeOutSeconds(), + userTimeSeconds + ); + } + ite.preIncrement(); + } + } + + return updated; + } + _userTimeSeconds: number; // デルタ時間の積算値[秒] + + _motions: csmVector; // モーション + _eventCallBack: CubismMotionEventFunction; // コールバック関数 + _eventCustomData: any; // コールバックに戻されるデータ +} + +/** + * イベントのコールバック関数を定義 + * + * イベントのコールバックに登録できる関数の型情報 + * @param caller 発火したイベントを再生させたCubismMotionQueueManager + * @param eventValue 発火したイベントの文字列データ + * @param customData コールバックに返される登録時に指定されたデータ + */ +export interface CubismMotionEventFunction { + ( + caller: CubismMotionQueueManager, + eventValue: csmString, + customData: any + ): void; +} + +/** + * モーションの識別番号 + * + * モーションの識別番号の定義 + */ +export declare type CubismMotionQueueEntryHandle = any; +export const InvalidMotionQueueEntryHandleValue: CubismMotionQueueEntryHandle = -1; + +// Namespace definition for compatibility. +import * as $ from './cubismmotionqueuemanager'; +// eslint-disable-next-line @typescript-eslint/no-namespace +export namespace Live2DCubismFramework { + export const CubismMotionQueueManager = $.CubismMotionQueueManager; + export type CubismMotionQueueManager = $.CubismMotionQueueManager; + export const InvalidMotionQueueEntryHandleValue = + $.InvalidMotionQueueEntryHandleValue; + export type CubismMotionQueueEntryHandle = $.CubismMotionQueueEntryHandle; + export type CubismMotionEventFunction = $.CubismMotionEventFunction; } diff --git a/src/physics/cubismphysics.ts b/src/physics/cubismphysics.ts index 67e0f3a..9fe9a50 100644 --- a/src/physics/cubismphysics.ts +++ b/src/physics/cubismphysics.ts @@ -5,945 +5,930 @@ * that can be found at https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html. */ -import { Live2DCubismFramework as cubismphysicsinternal } from './cubismphysicsinternal'; -import { Live2DCubismFramework as cubismmodel } from '../model/cubismmodel'; -import { Live2DCubismFramework as cubismvector2 } from '../math/cubismvector2'; -import { Live2DCubismFramework as cubismmath } from '../math/cubismmath'; -import { Live2DCubismFramework as cubismphysicsjson } from './cubismphysicsjson'; -import CubismPhysicsJson = cubismphysicsjson.CubismPhysicsJson; -import CubismMath = cubismmath.CubismMath; -import CubismPhysicsRig = cubismphysicsinternal.CubismPhysicsRig; -import CubismPhysicsSubRig = cubismphysicsinternal.CubismPhysicsSubRig; -import CubismPhysicsInput = cubismphysicsinternal.CubismPhysicsInput; -import CubismPhysicsOutput = cubismphysicsinternal.CubismPhysicsOutput; -import CubismPhysicsParticle = cubismphysicsinternal.CubismPhysicsParticle; -import CubismPhysicsSource = cubismphysicsinternal.CubismPhysicsSource; -import CubismPhysicsTargetType = cubismphysicsinternal.CubismPhysicsTargetType; -import CubismPhysicsNormalization = cubismphysicsinternal.CubismPhysicsNormalization; -import CubismVector2 = cubismvector2.CubismVector2; -import CubismModel = cubismmodel.CubismModel; +import { CubismMath } from '../math/cubismmath'; +import { CubismVector2 } from '../math/cubismvector2'; +import { CubismModel } from '../model/cubismmodel'; +import { + CubismPhysicsInput, + CubismPhysicsNormalization, + CubismPhysicsOutput, + CubismPhysicsParticle, + CubismPhysicsRig, + CubismPhysicsSource, + CubismPhysicsSubRig, + CubismPhysicsTargetType +} from './cubismphysicsinternal'; +import { CubismPhysicsJson } from './cubismphysicsjson'; -export namespace Live2DCubismFramework { - // physics types tags. - const PhysicsTypeTagX = 'X'; - const PhysicsTypeTagY = 'Y'; - const PhysicsTypeTagAngle = 'Angle'; +// physics types tags. +const PhysicsTypeTagX = 'X'; +const PhysicsTypeTagY = 'Y'; +const PhysicsTypeTagAngle = 'Angle'; - // Constant of air resistance. - const AirResistance = 5.0; +// Constant of air resistance. +const AirResistance = 5.0; - // Constant of maximum weight of input and output ratio. - const MaximumWeight = 100.0; +// Constant of maximum weight of input and output ratio. +const MaximumWeight = 100.0; - // Constant of threshold of movement. - const MovementThreshold = 0.001; +// Constant of threshold of movement. +const MovementThreshold = 0.001; +/** + * 物理演算クラス + */ +export class CubismPhysics { /** - * 物理演算クラス + * インスタンスの作成 + * @param buffer physics3.jsonが読み込まれているバッファ + * @param size バッファのサイズ + * @return 作成されたインスタンス */ - export class CubismPhysics { - /** - * インスタンスの作成 - * @param buffer physics3.jsonが読み込まれているバッファ - * @param size バッファのサイズ - * @return 作成されたインスタンス - */ - public static create(buffer: ArrayBuffer, size: number): CubismPhysics { - const ret: CubismPhysics = new CubismPhysics(); + public static create(buffer: ArrayBuffer, size: number): CubismPhysics { + const ret: CubismPhysics = new CubismPhysics(); - ret.parse(buffer, size); - ret._physicsRig.gravity.y = 0; - - return ret; - } - - /** - * インスタンスを破棄する - * @param physics 破棄するインスタンス - */ - public static delete(physics: CubismPhysics): void { - if (physics != null) { - physics.release(); - physics = null; - } - } - - /** - * 物理演算の評価 - * @param model 物理演算の結果を適用するモデル - * @param deltaTimeSeconds デルタ時間[秒] - */ - public evaluate(model: CubismModel, deltaTimeSeconds: number): void { - let totalAngle: { angle: number }; - let weight: number; - let radAngle: number; - let outputValue: number; - const totalTranslation: CubismVector2 = new CubismVector2(); - let currentSetting: CubismPhysicsSubRig; - let currentInput: CubismPhysicsInput[]; - let currentOutput: CubismPhysicsOutput[]; - let currentParticles: CubismPhysicsParticle[]; - - let parameterValue: Float32Array; - let parameterMaximumValue: Float32Array; - let parameterMinimumValue: Float32Array; - let parameterDefaultValue: Float32Array; - - parameterValue = model.getModel().parameters.values; - parameterMaximumValue = model.getModel().parameters.maximumValues; - parameterMinimumValue = model.getModel().parameters.minimumValues; - parameterDefaultValue = model.getModel().parameters.defaultValues; - - for ( - let settingIndex = 0; - settingIndex < this._physicsRig.subRigCount; - ++settingIndex - ) { - totalAngle = { angle: 0.0 }; - totalTranslation.x = 0.0; - totalTranslation.y = 0.0; - currentSetting = this._physicsRig.settings.at(settingIndex); - currentInput = this._physicsRig.inputs.get( - currentSetting.baseInputIndex - ); - currentOutput = this._physicsRig.outputs.get( - currentSetting.baseOutputIndex - ); - currentParticles = this._physicsRig.particles.get( - currentSetting.baseParticleIndex - ); - - // Load input parameters - for (let i = 0; i < currentSetting.inputCount; ++i) { - weight = currentInput[i].weight / MaximumWeight; - - if (currentInput[i].sourceParameterIndex == -1) { - currentInput[i].sourceParameterIndex = model.getParameterIndex( - currentInput[i].source.id - ); - } - - currentInput[i].getNormalizedParameterValue( - totalTranslation, - totalAngle, - parameterValue[currentInput[i].sourceParameterIndex], - parameterMinimumValue[currentInput[i].sourceParameterIndex], - parameterMaximumValue[currentInput[i].sourceParameterIndex], - parameterDefaultValue[currentInput[i].sourceParameterIndex], - currentSetting.normalizationPosition, - currentSetting.normalizationAngle, - currentInput[i].reflect, - weight - ); - } - - radAngle = CubismMath.degreesToRadian(-totalAngle.angle); - - totalTranslation.x = - totalTranslation.x * CubismMath.cos(radAngle) - - totalTranslation.y * CubismMath.sin(radAngle); - totalTranslation.y = - totalTranslation.x * CubismMath.sin(radAngle) + - totalTranslation.y * CubismMath.cos(radAngle); - - // Calculate particles position. - updateParticles( - currentParticles, - currentSetting.particleCount, - totalTranslation, - totalAngle.angle, - this._options.wind, - MovementThreshold * currentSetting.normalizationPosition.maximum, - deltaTimeSeconds, - AirResistance - ); - - // Update output parameters. - for (let i = 0; i < currentSetting.outputCount; ++i) { - const particleIndex = currentOutput[i].vertexIndex; - - if ( - particleIndex < 1 || - particleIndex >= currentSetting.particleCount - ) { - break; - } - - if (currentOutput[i].destinationParameterIndex == -1) { - currentOutput[ - i - ].destinationParameterIndex = model.getParameterIndex( - currentOutput[i].destination.id - ); - } - - const translation: CubismVector2 = new CubismVector2(); - translation.x = - currentParticles[particleIndex].position.x - - currentParticles[particleIndex - 1].position.x; - translation.y = - currentParticles[particleIndex].position.y - - currentParticles[particleIndex - 1].position.y; - - outputValue = currentOutput[i].getValue( - translation, - currentParticles, - particleIndex, - currentOutput[i].reflect, - this._options.gravity - ); - - const destinationParameterIndex: number = - currentOutput[i].destinationParameterIndex; - const outParameterValue: Float32Array = - !Float32Array.prototype.slice && - 'subarray' in Float32Array.prototype - ? JSON.parse( - JSON.stringify( - parameterValue.subarray(destinationParameterIndex) - ) - ) // 値渡しするため、JSON.parse, JSON.stringify - : parameterValue.slice(destinationParameterIndex); - - updateOutputParameterValue( - outParameterValue, - parameterMinimumValue[destinationParameterIndex], - parameterMaximumValue[destinationParameterIndex], - outputValue, - currentOutput[i] - ); - - // 値を反映 - for ( - let offset: number = destinationParameterIndex, outParamIndex = 0; - offset < parameterValue.length; - offset++, outParamIndex++ - ) { - parameterValue[offset] = outParameterValue[outParamIndex]; - } - } - } - } - - /** - * オプションの設定 - * @param options オプション - */ - public setOptions(options: Options): void { - this._options = options; - } - - /** - * オプションの取得 - * @return オプション - */ - public getOption(): Options { - return this._options; - } - - /** - * コンストラクタ - */ - public constructor() { - this._physicsRig = null; - - // set default options - this._options = new Options(); - this._options.gravity.y = -1.0; - this._options.gravity.x = 0; - this._options.wind.x = 0; - this._options.wind.y = 0; - } - - /** - * デストラクタ相当の処理 - */ - public release(): void { - this._physicsRig = void 0; - this._physicsRig = null; - } - - /** - * physics3.jsonをパースする。 - * @param physicsJson physics3.jsonが読み込まれているバッファ - * @param size バッファのサイズ - */ - public parse(physicsJson: ArrayBuffer, size: number): void { - this._physicsRig = new CubismPhysicsRig(); - - let json: CubismPhysicsJson = new CubismPhysicsJson(physicsJson, size); - - this._physicsRig.gravity = json.getGravity(); - this._physicsRig.wind = json.getWind(); - this._physicsRig.subRigCount = json.getSubRigCount(); - - this._physicsRig.settings.updateSize( - this._physicsRig.subRigCount, - CubismPhysicsSubRig, - true - ); - this._physicsRig.inputs.updateSize( - json.getTotalInputCount(), - CubismPhysicsInput, - true - ); - this._physicsRig.outputs.updateSize( - json.getTotalOutputCount(), - CubismPhysicsOutput, - true - ); - this._physicsRig.particles.updateSize( - json.getVertexCount(), - CubismPhysicsParticle, - true - ); - - let inputIndex = 0, - outputIndex = 0, - particleIndex = 0; - - for (let i = 0; i < this._physicsRig.settings.getSize(); ++i) { - this._physicsRig.settings.at( - i - ).normalizationPosition.minimum = json.getNormalizationPositionMinimumValue( - i - ); - this._physicsRig.settings.at( - i - ).normalizationPosition.maximum = json.getNormalizationPositionMaximumValue( - i - ); - this._physicsRig.settings.at( - i - ).normalizationPosition.defalut = json.getNormalizationPositionDefaultValue( - i - ); - - this._physicsRig.settings.at( - i - ).normalizationAngle.minimum = json.getNormalizationAngleMinimumValue( - i - ); - this._physicsRig.settings.at( - i - ).normalizationAngle.maximum = json.getNormalizationAngleMaximumValue( - i - ); - this._physicsRig.settings.at( - i - ).normalizationAngle.defalut = json.getNormalizationAngleDefaultValue( - i - ); - - // Input - this._physicsRig.settings.at(i).inputCount = json.getInputCount(i); - this._physicsRig.settings.at(i).baseInputIndex = inputIndex; - - for (let j = 0; j < this._physicsRig.settings.at(i).inputCount; ++j) { - this._physicsRig.inputs.at(inputIndex + j).sourceParameterIndex = -1; - this._physicsRig.inputs.at( - inputIndex + j - ).weight = json.getInputWeight(i, j); - this._physicsRig.inputs.at( - inputIndex + j - ).reflect = json.getInputReflect(i, j); - - if (json.getInputType(i, j) == PhysicsTypeTagX) { - this._physicsRig.inputs.at(inputIndex + j).type = - CubismPhysicsSource.CubismPhysicsSource_X; - this._physicsRig.inputs.at( - inputIndex + j - ).getNormalizedParameterValue = getInputTranslationXFromNormalizedParameterValue; - } else if (json.getInputType(i, j) == PhysicsTypeTagY) { - this._physicsRig.inputs.at(inputIndex + j).type = - CubismPhysicsSource.CubismPhysicsSource_Y; - this._physicsRig.inputs.at( - inputIndex + j - ).getNormalizedParameterValue = getInputTranslationYFromNormalizedParamterValue; - } else if (json.getInputType(i, j) == PhysicsTypeTagAngle) { - this._physicsRig.inputs.at(inputIndex + j).type = - CubismPhysicsSource.CubismPhysicsSource_Angle; - this._physicsRig.inputs.at( - inputIndex + j - ).getNormalizedParameterValue = getInputAngleFromNormalizedParameterValue; - } - - this._physicsRig.inputs.at(inputIndex + j).source.targetType = - CubismPhysicsTargetType.CubismPhysicsTargetType_Parameter; - this._physicsRig.inputs.at( - inputIndex + j - ).source.id = json.getInputSourceId(i, j); - } - inputIndex += this._physicsRig.settings.at(i).inputCount; - - // Output - this._physicsRig.settings.at(i).outputCount = json.getOutputCount(i); - this._physicsRig.settings.at(i).baseOutputIndex = outputIndex; - - for (let j = 0; j < this._physicsRig.settings.at(i).outputCount; ++j) { - this._physicsRig.outputs.at( - outputIndex + j - ).destinationParameterIndex = -1; - this._physicsRig.outputs.at( - outputIndex + j - ).vertexIndex = json.getOutputVertexIndex(i, j); - this._physicsRig.outputs.at( - outputIndex + j - ).angleScale = json.getOutputAngleScale(i, j); - this._physicsRig.outputs.at( - outputIndex + j - ).weight = json.getOutputWeight(i, j); - this._physicsRig.outputs.at(outputIndex + j).destination.targetType = - CubismPhysicsTargetType.CubismPhysicsTargetType_Parameter; - - this._physicsRig.outputs.at( - outputIndex + j - ).destination.id = json.getOutputDestinationId(i, j); - - if (json.getOutputType(i, j) == PhysicsTypeTagX) { - this._physicsRig.outputs.at(outputIndex + j).type = - CubismPhysicsSource.CubismPhysicsSource_X; - this._physicsRig.outputs.at( - outputIndex + j - ).getValue = getOutputTranslationX; - this._physicsRig.outputs.at( - outputIndex + j - ).getScale = getOutputScaleTranslationX; - } else if (json.getOutputType(i, j) == PhysicsTypeTagY) { - this._physicsRig.outputs.at(outputIndex + j).type = - CubismPhysicsSource.CubismPhysicsSource_Y; - this._physicsRig.outputs.at( - outputIndex + j - ).getValue = getOutputTranslationY; - this._physicsRig.outputs.at( - outputIndex + j - ).getScale = getOutputScaleTranslationY; - } else if (json.getOutputType(i, j) == PhysicsTypeTagAngle) { - this._physicsRig.outputs.at(outputIndex + j).type = - CubismPhysicsSource.CubismPhysicsSource_Angle; - this._physicsRig.outputs.at( - outputIndex + j - ).getValue = getOutputAngle; - this._physicsRig.outputs.at( - outputIndex + j - ).getScale = getOutputScaleAngle; - } - - this._physicsRig.outputs.at( - outputIndex + j - ).reflect = json.getOutputReflect(i, j); - } - outputIndex += this._physicsRig.settings.at(i).outputCount; - - // Particle - this._physicsRig.settings.at(i).particleCount = json.getParticleCount( - i - ); - this._physicsRig.settings.at(i).baseParticleIndex = particleIndex; - - for ( - let j = 0; - j < this._physicsRig.settings.at(i).particleCount; - ++j - ) { - this._physicsRig.particles.at( - particleIndex + j - ).mobility = json.getParticleMobility(i, j); - this._physicsRig.particles.at( - particleIndex + j - ).delay = json.getParticleDelay(i, j); - this._physicsRig.particles.at( - particleIndex + j - ).acceleration = json.getParticleAcceleration(i, j); - this._physicsRig.particles.at( - particleIndex + j - ).radius = json.getParticleRadius(i, j); - this._physicsRig.particles.at( - particleIndex + j - ).position = json.getParticlePosition(i, j); - } - - particleIndex += this._physicsRig.settings.at(i).particleCount; - } - - this.initialize(); - - json.release(); - json = void 0; - json = null; - } - - /** - * 初期化する - */ - public initialize(): void { - let strand: CubismPhysicsParticle[]; - let currentSetting: CubismPhysicsSubRig; - let radius: CubismVector2; - - for ( - let settingIndex = 0; - settingIndex < this._physicsRig.subRigCount; - ++settingIndex - ) { - currentSetting = this._physicsRig.settings.at(settingIndex); - strand = this._physicsRig.particles.get( - currentSetting.baseParticleIndex - ); - - // Initialize the top of particle. - strand[0].initialPosition = new CubismVector2(0.0, 0.0); - strand[0].lastPosition = new CubismVector2( - strand[0].initialPosition.x, - strand[0].initialPosition.y - ); - strand[0].lastGravity = new CubismVector2(0.0, -1.0); - strand[0].lastGravity.y *= -1.0; - strand[0].velocity = new CubismVector2(0.0, 0.0); - strand[0].force = new CubismVector2(0.0, 0.0); - - // Initialize paritcles. - for (let i = 1; i < currentSetting.particleCount; ++i) { - radius = new CubismVector2(0.0, 0.0); - radius.y = strand[i].radius; - strand[i].initialPosition = new CubismVector2( - strand[i - 1].initialPosition.x + radius.x, - strand[i - 1].initialPosition.y + radius.y - ); - strand[i].position = new CubismVector2( - strand[i].initialPosition.x, - strand[i].initialPosition.y - ); - strand[i].lastPosition = new CubismVector2( - strand[i].initialPosition.x, - strand[i].initialPosition.y - ); - strand[i].lastGravity = new CubismVector2(0.0, -1.0); - strand[i].lastGravity.y *= -1.0; - strand[i].velocity = new CubismVector2(0.0, 0.0); - strand[i].force = new CubismVector2(0.0, 0.0); - } - } - } - - _physicsRig: CubismPhysicsRig; // 物理演算のデータ - _options: Options; // オプション - } - - /** - * 物理演算のオプション - */ - export class Options { - constructor() { - this.gravity = new CubismVector2(0, 0); - this.wind = new CubismVector2(0, 0); - } - - gravity: CubismVector2; // 重力方向 - wind: CubismVector2; // 風の方向 - } - - /** - * Gets sign. - * - * @param value Evaluation target value. - * - * @return Sign of value. - */ - function sign(value: number): number { - let ret = 0; - - if (value > 0.0) { - ret = 1; - } else if (value < 0.0) { - ret = -1; - } + ret.parse(buffer, size); + ret._physicsRig.gravity.y = 0; return ret; } - function getInputTranslationXFromNormalizedParameterValue( - targetTranslation: CubismVector2, - targetAngle: { angle: number }, - value: number, - parameterMinimumValue: number, - parameterMaximumValue: number, - parameterDefaultValue: number, - normalizationPosition: CubismPhysicsNormalization, - normalizationAngle: CubismPhysicsNormalization, - isInverted: boolean, - weight: number - ): void { - targetTranslation.x += - normalizeParameterValue( - value, - parameterMinimumValue, - parameterMaximumValue, - parameterDefaultValue, - normalizationPosition.minimum, - normalizationPosition.maximum, - normalizationPosition.defalut, - isInverted - ) * weight; - } - - function getInputTranslationYFromNormalizedParamterValue( - targetTranslation: CubismVector2, - targetAngle: { angle: number }, - value: number, - parameterMinimumValue: number, - parameterMaximumValue: number, - parameterDefaultValue: number, - normalizationPosition: CubismPhysicsNormalization, - normalizationAngle: CubismPhysicsNormalization, - isInverted: boolean, - weight: number - ): void { - targetTranslation.y += - normalizeParameterValue( - value, - parameterMinimumValue, - parameterMaximumValue, - parameterDefaultValue, - normalizationPosition.minimum, - normalizationPosition.maximum, - normalizationPosition.defalut, - isInverted - ) * weight; - } - - function getInputAngleFromNormalizedParameterValue( - targetTranslation: CubismVector2, - targetAngle: { angle: number }, - value: number, - parameterMinimumValue: number, - parameterMaximumValue: number, - parameterDefaultValue: number, - normalizaitionPosition: CubismPhysicsNormalization, - normalizationAngle: CubismPhysicsNormalization, - isInverted: boolean, - weight: number - ): void { - targetAngle.angle += - normalizeParameterValue( - value, - parameterMinimumValue, - parameterMaximumValue, - parameterDefaultValue, - normalizationAngle.minimum, - normalizationAngle.maximum, - normalizationAngle.defalut, - isInverted - ) * weight; - } - - function getOutputTranslationX( - translation: CubismVector2, - particles: CubismPhysicsParticle[], - particleIndex: number, - isInverted: boolean, - parentGravity: CubismVector2 - ): number { - let outputValue: number = translation.x; - - if (isInverted) { - outputValue *= -1.0; - } - - return outputValue; - } - - function getOutputTranslationY( - translation: CubismVector2, - particles: CubismPhysicsParticle[], - particleIndex: number, - isInverted: boolean, - parentGravity: CubismVector2 - ): number { - let outputValue: number = translation.y; - - if (isInverted) { - outputValue *= -1.0; - } - return outputValue; - } - - function getOutputAngle( - translation: CubismVector2, - particles: CubismPhysicsParticle[], - particleIndex: number, - isInverted: boolean, - parentGravity: CubismVector2 - ): number { - let outputValue: number; - - if (particleIndex >= 2) { - parentGravity = particles[particleIndex - 1].position.substract( - particles[particleIndex - 2].position - ); - } else { - parentGravity = parentGravity.multiplyByScaler(-1.0); - } - - outputValue = CubismMath.directionToRadian(parentGravity, translation); - - if (isInverted) { - outputValue *= -1.0; - } - - return outputValue; - } - - function getRangeValue(min: number, max: number): number { - const maxValue: number = CubismMath.max(min, max); - const minValue: number = CubismMath.min(min, max); - - return CubismMath.abs(maxValue - minValue); - } - - function getDefaultValue(min: number, max: number): number { - const minValue: number = CubismMath.min(min, max); - return minValue + getRangeValue(min, max) / 2.0; - } - - function getOutputScaleTranslationX( - translationScale: CubismVector2, - angleScale: number - ): number { - return JSON.parse(JSON.stringify(translationScale.x)); - } - - function getOutputScaleTranslationY( - translationScale: CubismVector2, - angleScale: number - ): number { - return JSON.parse(JSON.stringify(translationScale.y)); - } - - function getOutputScaleAngle( - translationScale: CubismVector2, - angleScale: number - ): number { - return JSON.parse(JSON.stringify(angleScale)); - } - /** - * Updates particles. - * - * @param strand Target array of particle. - * @param strandCount Count of particle. - * @param totalTranslation Total translation value. - * @param totalAngle Total angle. - * @param windDirection Direction of Wind. - * @param thresholdValue Threshold of movement. - * @param deltaTimeSeconds Delta time. - * @param airResistance Air resistance. + * インスタンスを破棄する + * @param physics 破棄するインスタンス */ - function updateParticles( - strand: CubismPhysicsParticle[], - strandCount: number, - totalTranslation: CubismVector2, - totalAngle: number, - windDirection: CubismVector2, - thresholdValue: number, - deltaTimeSeconds: number, - airResistance: number - ) { - let totalRadian: number; - let delay: number; - let radian: number; - let currentGravity: CubismVector2; - let direction: CubismVector2 = new CubismVector2(0.0, 0.0); - let velocity: CubismVector2 = new CubismVector2(0.0, 0.0); - let force: CubismVector2 = new CubismVector2(0.0, 0.0); - let newDirection: CubismVector2 = new CubismVector2(0.0, 0.0); - - strand[0].position = new CubismVector2( - totalTranslation.x, - totalTranslation.y - ); - - totalRadian = CubismMath.degreesToRadian(totalAngle); - currentGravity = CubismMath.radianToDirection(totalRadian); - currentGravity.normalize(); - - for (let i = 1; i < strandCount; ++i) { - strand[i].force = currentGravity - .multiplyByScaler(strand[i].acceleration) - .add(windDirection); - - strand[i].lastPosition = new CubismVector2( - strand[i].position.x, - strand[i].position.y - ); - - delay = strand[i].delay * deltaTimeSeconds * 30.0; - - direction = strand[i].position.substract(strand[i - 1].position); - - radian = - CubismMath.directionToRadian(strand[i].lastGravity, currentGravity) / - airResistance; - - direction.x = - CubismMath.cos(radian) * direction.x - - direction.y * CubismMath.sin(radian); - direction.y = - CubismMath.sin(radian) * direction.x + - direction.y * CubismMath.cos(radian); - - strand[i].position = strand[i - 1].position.add(direction); - - velocity = strand[i].velocity.multiplyByScaler(delay); - force = strand[i].force.multiplyByScaler(delay).multiplyByScaler(delay); - - strand[i].position = strand[i].position.add(velocity).add(force); - - newDirection = strand[i].position.substract(strand[i - 1].position); - newDirection.normalize(); - - strand[i].position = strand[i - 1].position.add( - newDirection.multiplyByScaler(strand[i].radius) - ); - - if (CubismMath.abs(strand[i].position.x) < thresholdValue) { - strand[i].position.x = 0.0; - } - - if (delay != 0.0) { - strand[i].velocity = strand[i].position.substract( - strand[i].lastPosition - ); - strand[i].velocity = strand[i].velocity.divisionByScalar(delay); - strand[i].velocity = strand[i].velocity.multiplyByScaler( - strand[i].mobility - ); - } - - strand[i].force = new CubismVector2(0.0, 0.0); - strand[i].lastGravity = new CubismVector2( - currentGravity.x, - currentGravity.y - ); + public static delete(physics: CubismPhysics): void { + if (physics != null) { + physics.release(); + physics = null; } } /** - * Updates output parameter value. - * @param parameterValue Target parameter value. - * @param parameterValueMinimum Minimum of parameter value. - * @param parameterValueMaximum Maximum of parameter value. - * @param translation Translation value. + * 物理演算の評価 + * @param model 物理演算の結果を適用するモデル + * @param deltaTimeSeconds デルタ時間[秒] */ - function updateOutputParameterValue( - parameterValue: Float32Array, - parameterValueMinimum: number, - parameterValueMaximum: number, - translation: number, - output: CubismPhysicsOutput - ): void { - let outputScale: number; - let value: number; + public evaluate(model: CubismModel, deltaTimeSeconds: number): void { + let totalAngle: { angle: number }; let weight: number; + let radAngle: number; + let outputValue: number; + const totalTranslation: CubismVector2 = new CubismVector2(); + let currentSetting: CubismPhysicsSubRig; + let currentInput: CubismPhysicsInput[]; + let currentOutput: CubismPhysicsOutput[]; + let currentParticles: CubismPhysicsParticle[]; - outputScale = output.getScale(output.translationScale, output.angleScale); + let parameterValue: Float32Array; + let parameterMaximumValue: Float32Array; + let parameterMinimumValue: Float32Array; + let parameterDefaultValue: Float32Array; - value = translation * outputScale; + parameterValue = model.getModel().parameters.values; + parameterMaximumValue = model.getModel().parameters.maximumValues; + parameterMinimumValue = model.getModel().parameters.minimumValues; + parameterDefaultValue = model.getModel().parameters.defaultValues; - if (value < parameterValueMinimum) { - if (value < output.valueBelowMinimum) { - output.valueBelowMinimum = value; + for ( + let settingIndex = 0; + settingIndex < this._physicsRig.subRigCount; + ++settingIndex + ) { + totalAngle = { angle: 0.0 }; + totalTranslation.x = 0.0; + totalTranslation.y = 0.0; + currentSetting = this._physicsRig.settings.at(settingIndex); + currentInput = this._physicsRig.inputs.get(currentSetting.baseInputIndex); + currentOutput = this._physicsRig.outputs.get( + currentSetting.baseOutputIndex + ); + currentParticles = this._physicsRig.particles.get( + currentSetting.baseParticleIndex + ); + + // Load input parameters + for (let i = 0; i < currentSetting.inputCount; ++i) { + weight = currentInput[i].weight / MaximumWeight; + + if (currentInput[i].sourceParameterIndex == -1) { + currentInput[i].sourceParameterIndex = model.getParameterIndex( + currentInput[i].source.id + ); + } + + currentInput[i].getNormalizedParameterValue( + totalTranslation, + totalAngle, + parameterValue[currentInput[i].sourceParameterIndex], + parameterMinimumValue[currentInput[i].sourceParameterIndex], + parameterMaximumValue[currentInput[i].sourceParameterIndex], + parameterDefaultValue[currentInput[i].sourceParameterIndex], + currentSetting.normalizationPosition, + currentSetting.normalizationAngle, + currentInput[i].reflect, + weight + ); } - value = parameterValueMinimum; - } else if (value > parameterValueMaximum) { - if (value > output.valueExceededMaximum) { - output.valueExceededMaximum = value; + radAngle = CubismMath.degreesToRadian(-totalAngle.angle); + + totalTranslation.x = + totalTranslation.x * CubismMath.cos(radAngle) - + totalTranslation.y * CubismMath.sin(radAngle); + totalTranslation.y = + totalTranslation.x * CubismMath.sin(radAngle) + + totalTranslation.y * CubismMath.cos(radAngle); + + // Calculate particles position. + updateParticles( + currentParticles, + currentSetting.particleCount, + totalTranslation, + totalAngle.angle, + this._options.wind, + MovementThreshold * currentSetting.normalizationPosition.maximum, + deltaTimeSeconds, + AirResistance + ); + + // Update output parameters. + for (let i = 0; i < currentSetting.outputCount; ++i) { + const particleIndex = currentOutput[i].vertexIndex; + + if ( + particleIndex < 1 || + particleIndex >= currentSetting.particleCount + ) { + break; + } + + if (currentOutput[i].destinationParameterIndex == -1) { + currentOutput[i].destinationParameterIndex = model.getParameterIndex( + currentOutput[i].destination.id + ); + } + + const translation: CubismVector2 = new CubismVector2(); + translation.x = + currentParticles[particleIndex].position.x - + currentParticles[particleIndex - 1].position.x; + translation.y = + currentParticles[particleIndex].position.y - + currentParticles[particleIndex - 1].position.y; + + outputValue = currentOutput[i].getValue( + translation, + currentParticles, + particleIndex, + currentOutput[i].reflect, + this._options.gravity + ); + + const destinationParameterIndex: number = + currentOutput[i].destinationParameterIndex; + const outParameterValue: Float32Array = + !Float32Array.prototype.slice && 'subarray' in Float32Array.prototype + ? JSON.parse( + JSON.stringify( + parameterValue.subarray(destinationParameterIndex) + ) + ) // 値渡しするため、JSON.parse, JSON.stringify + : parameterValue.slice(destinationParameterIndex); + + updateOutputParameterValue( + outParameterValue, + parameterMinimumValue[destinationParameterIndex], + parameterMaximumValue[destinationParameterIndex], + outputValue, + currentOutput[i] + ); + + // 値を反映 + for ( + let offset: number = destinationParameterIndex, outParamIndex = 0; + offset < parameterValue.length; + offset++, outParamIndex++ + ) { + parameterValue[offset] = outParameterValue[outParamIndex]; + } } - - value = parameterValueMaximum; - } - - weight = output.weight / MaximumWeight; - - if (weight >= 1.0) { - parameterValue[0] = value; - } else { - value = parameterValue[0] * (1.0 - weight) + value * weight; - parameterValue[0] = value; } } - function normalizeParameterValue( - value: number, - parameterMinimum: number, - parameterMaximum: number, - parameterDefault: number, - normalizedMinimum: number, - normalizedMaximum: number, - normalizedDefault: number, - isInverted: boolean - ) { - let result = 0.0; + /** + * オプションの設定 + * @param options オプション + */ + public setOptions(options: Options): void { + this._options = options; + } - const maxValue: number = CubismMath.max(parameterMaximum, parameterMinimum); + /** + * オプションの取得 + * @return オプション + */ + public getOption(): Options { + return this._options; + } - if (maxValue < value) { - value = maxValue; - } + /** + * コンストラクタ + */ + public constructor() { + this._physicsRig = null; - const minValue: number = CubismMath.min(parameterMaximum, parameterMinimum); + // set default options + this._options = new Options(); + this._options.gravity.y = -1.0; + this._options.gravity.x = 0; + this._options.wind.x = 0; + this._options.wind.y = 0; + } - if (minValue > value) { - value = minValue; - } + /** + * デストラクタ相当の処理 + */ + public release(): void { + this._physicsRig = void 0; + this._physicsRig = null; + } - const minNormValue: number = CubismMath.min( - normalizedMinimum, - normalizedMaximum + /** + * physics3.jsonをパースする。 + * @param physicsJson physics3.jsonが読み込まれているバッファ + * @param size バッファのサイズ + */ + public parse(physicsJson: ArrayBuffer, size: number): void { + this._physicsRig = new CubismPhysicsRig(); + + let json: CubismPhysicsJson = new CubismPhysicsJson(physicsJson, size); + + this._physicsRig.gravity = json.getGravity(); + this._physicsRig.wind = json.getWind(); + this._physicsRig.subRigCount = json.getSubRigCount(); + + this._physicsRig.settings.updateSize( + this._physicsRig.subRigCount, + CubismPhysicsSubRig, + true ); - const maxNormValue: number = CubismMath.max( - normalizedMinimum, - normalizedMaximum + this._physicsRig.inputs.updateSize( + json.getTotalInputCount(), + CubismPhysicsInput, + true + ); + this._physicsRig.outputs.updateSize( + json.getTotalOutputCount(), + CubismPhysicsOutput, + true + ); + this._physicsRig.particles.updateSize( + json.getVertexCount(), + CubismPhysicsParticle, + true ); - const middleNormValue: number = normalizedDefault; - const middleValue: number = getDefaultValue(minValue, maxValue); - const paramValue: number = value - middleValue; + let inputIndex = 0, + outputIndex = 0, + particleIndex = 0; - switch (sign(paramValue)) { - case 1: { - const nLength: number = maxNormValue - middleNormValue; - const pLength: number = maxValue - middleValue; + for (let i = 0; i < this._physicsRig.settings.getSize(); ++i) { + this._physicsRig.settings.at( + i + ).normalizationPosition.minimum = json.getNormalizationPositionMinimumValue( + i + ); + this._physicsRig.settings.at( + i + ).normalizationPosition.maximum = json.getNormalizationPositionMaximumValue( + i + ); + this._physicsRig.settings.at( + i + ).normalizationPosition.defalut = json.getNormalizationPositionDefaultValue( + i + ); - if (pLength != 0.0) { - result = paramValue * (nLength / pLength); - result += middleNormValue; + this._physicsRig.settings.at( + i + ).normalizationAngle.minimum = json.getNormalizationAngleMinimumValue(i); + this._physicsRig.settings.at( + i + ).normalizationAngle.maximum = json.getNormalizationAngleMaximumValue(i); + this._physicsRig.settings.at( + i + ).normalizationAngle.defalut = json.getNormalizationAngleDefaultValue(i); + + // Input + this._physicsRig.settings.at(i).inputCount = json.getInputCount(i); + this._physicsRig.settings.at(i).baseInputIndex = inputIndex; + + for (let j = 0; j < this._physicsRig.settings.at(i).inputCount; ++j) { + this._physicsRig.inputs.at(inputIndex + j).sourceParameterIndex = -1; + this._physicsRig.inputs.at(inputIndex + j).weight = json.getInputWeight( + i, + j + ); + this._physicsRig.inputs.at( + inputIndex + j + ).reflect = json.getInputReflect(i, j); + + if (json.getInputType(i, j) == PhysicsTypeTagX) { + this._physicsRig.inputs.at(inputIndex + j).type = + CubismPhysicsSource.CubismPhysicsSource_X; + this._physicsRig.inputs.at( + inputIndex + j + ).getNormalizedParameterValue = getInputTranslationXFromNormalizedParameterValue; + } else if (json.getInputType(i, j) == PhysicsTypeTagY) { + this._physicsRig.inputs.at(inputIndex + j).type = + CubismPhysicsSource.CubismPhysicsSource_Y; + this._physicsRig.inputs.at( + inputIndex + j + ).getNormalizedParameterValue = getInputTranslationYFromNormalizedParamterValue; + } else if (json.getInputType(i, j) == PhysicsTypeTagAngle) { + this._physicsRig.inputs.at(inputIndex + j).type = + CubismPhysicsSource.CubismPhysicsSource_Angle; + this._physicsRig.inputs.at( + inputIndex + j + ).getNormalizedParameterValue = getInputAngleFromNormalizedParameterValue; } - break; + this._physicsRig.inputs.at(inputIndex + j).source.targetType = + CubismPhysicsTargetType.CubismPhysicsTargetType_Parameter; + this._physicsRig.inputs.at( + inputIndex + j + ).source.id = json.getInputSourceId(i, j); } - case -1: { - const nLength: number = minNormValue - middleNormValue; - const pLength: number = minValue - middleValue; + inputIndex += this._physicsRig.settings.at(i).inputCount; - if (pLength != 0.0) { - result = paramValue * (nLength / pLength); - result += middleNormValue; + // Output + this._physicsRig.settings.at(i).outputCount = json.getOutputCount(i); + this._physicsRig.settings.at(i).baseOutputIndex = outputIndex; + + for (let j = 0; j < this._physicsRig.settings.at(i).outputCount; ++j) { + this._physicsRig.outputs.at( + outputIndex + j + ).destinationParameterIndex = -1; + this._physicsRig.outputs.at( + outputIndex + j + ).vertexIndex = json.getOutputVertexIndex(i, j); + this._physicsRig.outputs.at( + outputIndex + j + ).angleScale = json.getOutputAngleScale(i, j); + this._physicsRig.outputs.at( + outputIndex + j + ).weight = json.getOutputWeight(i, j); + this._physicsRig.outputs.at(outputIndex + j).destination.targetType = + CubismPhysicsTargetType.CubismPhysicsTargetType_Parameter; + + this._physicsRig.outputs.at( + outputIndex + j + ).destination.id = json.getOutputDestinationId(i, j); + + if (json.getOutputType(i, j) == PhysicsTypeTagX) { + this._physicsRig.outputs.at(outputIndex + j).type = + CubismPhysicsSource.CubismPhysicsSource_X; + this._physicsRig.outputs.at( + outputIndex + j + ).getValue = getOutputTranslationX; + this._physicsRig.outputs.at( + outputIndex + j + ).getScale = getOutputScaleTranslationX; + } else if (json.getOutputType(i, j) == PhysicsTypeTagY) { + this._physicsRig.outputs.at(outputIndex + j).type = + CubismPhysicsSource.CubismPhysicsSource_Y; + this._physicsRig.outputs.at( + outputIndex + j + ).getValue = getOutputTranslationY; + this._physicsRig.outputs.at( + outputIndex + j + ).getScale = getOutputScaleTranslationY; + } else if (json.getOutputType(i, j) == PhysicsTypeTagAngle) { + this._physicsRig.outputs.at(outputIndex + j).type = + CubismPhysicsSource.CubismPhysicsSource_Angle; + this._physicsRig.outputs.at( + outputIndex + j + ).getValue = getOutputAngle; + this._physicsRig.outputs.at( + outputIndex + j + ).getScale = getOutputScaleAngle; } - break; + this._physicsRig.outputs.at( + outputIndex + j + ).reflect = json.getOutputReflect(i, j); } - case 0: { - result = middleNormValue; + outputIndex += this._physicsRig.settings.at(i).outputCount; - break; - } - default: { - break; + // Particle + this._physicsRig.settings.at(i).particleCount = json.getParticleCount(i); + this._physicsRig.settings.at(i).baseParticleIndex = particleIndex; + + for (let j = 0; j < this._physicsRig.settings.at(i).particleCount; ++j) { + this._physicsRig.particles.at( + particleIndex + j + ).mobility = json.getParticleMobility(i, j); + this._physicsRig.particles.at( + particleIndex + j + ).delay = json.getParticleDelay(i, j); + this._physicsRig.particles.at( + particleIndex + j + ).acceleration = json.getParticleAcceleration(i, j); + this._physicsRig.particles.at( + particleIndex + j + ).radius = json.getParticleRadius(i, j); + this._physicsRig.particles.at( + particleIndex + j + ).position = json.getParticlePosition(i, j); } + + particleIndex += this._physicsRig.settings.at(i).particleCount; } - return isInverted ? result : result * -1.0; + this.initialize(); + + json.release(); + json = void 0; + json = null; + } + + /** + * 初期化する + */ + public initialize(): void { + let strand: CubismPhysicsParticle[]; + let currentSetting: CubismPhysicsSubRig; + let radius: CubismVector2; + + for ( + let settingIndex = 0; + settingIndex < this._physicsRig.subRigCount; + ++settingIndex + ) { + currentSetting = this._physicsRig.settings.at(settingIndex); + strand = this._physicsRig.particles.get(currentSetting.baseParticleIndex); + + // Initialize the top of particle. + strand[0].initialPosition = new CubismVector2(0.0, 0.0); + strand[0].lastPosition = new CubismVector2( + strand[0].initialPosition.x, + strand[0].initialPosition.y + ); + strand[0].lastGravity = new CubismVector2(0.0, -1.0); + strand[0].lastGravity.y *= -1.0; + strand[0].velocity = new CubismVector2(0.0, 0.0); + strand[0].force = new CubismVector2(0.0, 0.0); + + // Initialize paritcles. + for (let i = 1; i < currentSetting.particleCount; ++i) { + radius = new CubismVector2(0.0, 0.0); + radius.y = strand[i].radius; + strand[i].initialPosition = new CubismVector2( + strand[i - 1].initialPosition.x + radius.x, + strand[i - 1].initialPosition.y + radius.y + ); + strand[i].position = new CubismVector2( + strand[i].initialPosition.x, + strand[i].initialPosition.y + ); + strand[i].lastPosition = new CubismVector2( + strand[i].initialPosition.x, + strand[i].initialPosition.y + ); + strand[i].lastGravity = new CubismVector2(0.0, -1.0); + strand[i].lastGravity.y *= -1.0; + strand[i].velocity = new CubismVector2(0.0, 0.0); + strand[i].force = new CubismVector2(0.0, 0.0); + } + } + } + + _physicsRig: CubismPhysicsRig; // 物理演算のデータ + _options: Options; // オプション +} + +/** + * 物理演算のオプション + */ +export class Options { + constructor() { + this.gravity = new CubismVector2(0, 0); + this.wind = new CubismVector2(0, 0); + } + + gravity: CubismVector2; // 重力方向 + wind: CubismVector2; // 風の方向 +} + +/** + * Gets sign. + * + * @param value Evaluation target value. + * + * @return Sign of value. + */ +function sign(value: number): number { + let ret = 0; + + if (value > 0.0) { + ret = 1; + } else if (value < 0.0) { + ret = -1; + } + + return ret; +} + +function getInputTranslationXFromNormalizedParameterValue( + targetTranslation: CubismVector2, + targetAngle: { angle: number }, + value: number, + parameterMinimumValue: number, + parameterMaximumValue: number, + parameterDefaultValue: number, + normalizationPosition: CubismPhysicsNormalization, + normalizationAngle: CubismPhysicsNormalization, + isInverted: boolean, + weight: number +): void { + targetTranslation.x += + normalizeParameterValue( + value, + parameterMinimumValue, + parameterMaximumValue, + parameterDefaultValue, + normalizationPosition.minimum, + normalizationPosition.maximum, + normalizationPosition.defalut, + isInverted + ) * weight; +} + +function getInputTranslationYFromNormalizedParamterValue( + targetTranslation: CubismVector2, + targetAngle: { angle: number }, + value: number, + parameterMinimumValue: number, + parameterMaximumValue: number, + parameterDefaultValue: number, + normalizationPosition: CubismPhysicsNormalization, + normalizationAngle: CubismPhysicsNormalization, + isInverted: boolean, + weight: number +): void { + targetTranslation.y += + normalizeParameterValue( + value, + parameterMinimumValue, + parameterMaximumValue, + parameterDefaultValue, + normalizationPosition.minimum, + normalizationPosition.maximum, + normalizationPosition.defalut, + isInverted + ) * weight; +} + +function getInputAngleFromNormalizedParameterValue( + targetTranslation: CubismVector2, + targetAngle: { angle: number }, + value: number, + parameterMinimumValue: number, + parameterMaximumValue: number, + parameterDefaultValue: number, + normalizaitionPosition: CubismPhysicsNormalization, + normalizationAngle: CubismPhysicsNormalization, + isInverted: boolean, + weight: number +): void { + targetAngle.angle += + normalizeParameterValue( + value, + parameterMinimumValue, + parameterMaximumValue, + parameterDefaultValue, + normalizationAngle.minimum, + normalizationAngle.maximum, + normalizationAngle.defalut, + isInverted + ) * weight; +} + +function getOutputTranslationX( + translation: CubismVector2, + particles: CubismPhysicsParticle[], + particleIndex: number, + isInverted: boolean, + parentGravity: CubismVector2 +): number { + let outputValue: number = translation.x; + + if (isInverted) { + outputValue *= -1.0; + } + + return outputValue; +} + +function getOutputTranslationY( + translation: CubismVector2, + particles: CubismPhysicsParticle[], + particleIndex: number, + isInverted: boolean, + parentGravity: CubismVector2 +): number { + let outputValue: number = translation.y; + + if (isInverted) { + outputValue *= -1.0; + } + return outputValue; +} + +function getOutputAngle( + translation: CubismVector2, + particles: CubismPhysicsParticle[], + particleIndex: number, + isInverted: boolean, + parentGravity: CubismVector2 +): number { + let outputValue: number; + + if (particleIndex >= 2) { + parentGravity = particles[particleIndex - 1].position.substract( + particles[particleIndex - 2].position + ); + } else { + parentGravity = parentGravity.multiplyByScaler(-1.0); + } + + outputValue = CubismMath.directionToRadian(parentGravity, translation); + + if (isInverted) { + outputValue *= -1.0; + } + + return outputValue; +} + +function getRangeValue(min: number, max: number): number { + const maxValue: number = CubismMath.max(min, max); + const minValue: number = CubismMath.min(min, max); + + return CubismMath.abs(maxValue - minValue); +} + +function getDefaultValue(min: number, max: number): number { + const minValue: number = CubismMath.min(min, max); + return minValue + getRangeValue(min, max) / 2.0; +} + +function getOutputScaleTranslationX( + translationScale: CubismVector2, + angleScale: number +): number { + return JSON.parse(JSON.stringify(translationScale.x)); +} + +function getOutputScaleTranslationY( + translationScale: CubismVector2, + angleScale: number +): number { + return JSON.parse(JSON.stringify(translationScale.y)); +} + +function getOutputScaleAngle( + translationScale: CubismVector2, + angleScale: number +): number { + return JSON.parse(JSON.stringify(angleScale)); +} + +/** + * Updates particles. + * + * @param strand Target array of particle. + * @param strandCount Count of particle. + * @param totalTranslation Total translation value. + * @param totalAngle Total angle. + * @param windDirection Direction of Wind. + * @param thresholdValue Threshold of movement. + * @param deltaTimeSeconds Delta time. + * @param airResistance Air resistance. + */ +function updateParticles( + strand: CubismPhysicsParticle[], + strandCount: number, + totalTranslation: CubismVector2, + totalAngle: number, + windDirection: CubismVector2, + thresholdValue: number, + deltaTimeSeconds: number, + airResistance: number +) { + let totalRadian: number; + let delay: number; + let radian: number; + let currentGravity: CubismVector2; + let direction: CubismVector2 = new CubismVector2(0.0, 0.0); + let velocity: CubismVector2 = new CubismVector2(0.0, 0.0); + let force: CubismVector2 = new CubismVector2(0.0, 0.0); + let newDirection: CubismVector2 = new CubismVector2(0.0, 0.0); + + strand[0].position = new CubismVector2( + totalTranslation.x, + totalTranslation.y + ); + + totalRadian = CubismMath.degreesToRadian(totalAngle); + currentGravity = CubismMath.radianToDirection(totalRadian); + currentGravity.normalize(); + + for (let i = 1; i < strandCount; ++i) { + strand[i].force = currentGravity + .multiplyByScaler(strand[i].acceleration) + .add(windDirection); + + strand[i].lastPosition = new CubismVector2( + strand[i].position.x, + strand[i].position.y + ); + + delay = strand[i].delay * deltaTimeSeconds * 30.0; + + direction = strand[i].position.substract(strand[i - 1].position); + + radian = + CubismMath.directionToRadian(strand[i].lastGravity, currentGravity) / + airResistance; + + direction.x = + CubismMath.cos(radian) * direction.x - + direction.y * CubismMath.sin(radian); + direction.y = + CubismMath.sin(radian) * direction.x + + direction.y * CubismMath.cos(radian); + + strand[i].position = strand[i - 1].position.add(direction); + + velocity = strand[i].velocity.multiplyByScaler(delay); + force = strand[i].force.multiplyByScaler(delay).multiplyByScaler(delay); + + strand[i].position = strand[i].position.add(velocity).add(force); + + newDirection = strand[i].position.substract(strand[i - 1].position); + newDirection.normalize(); + + strand[i].position = strand[i - 1].position.add( + newDirection.multiplyByScaler(strand[i].radius) + ); + + if (CubismMath.abs(strand[i].position.x) < thresholdValue) { + strand[i].position.x = 0.0; + } + + if (delay != 0.0) { + strand[i].velocity = strand[i].position.substract(strand[i].lastPosition); + strand[i].velocity = strand[i].velocity.divisionByScalar(delay); + strand[i].velocity = strand[i].velocity.multiplyByScaler( + strand[i].mobility + ); + } + + strand[i].force = new CubismVector2(0.0, 0.0); + strand[i].lastGravity = new CubismVector2( + currentGravity.x, + currentGravity.y + ); } } + +/** + * Updates output parameter value. + * @param parameterValue Target parameter value. + * @param parameterValueMinimum Minimum of parameter value. + * @param parameterValueMaximum Maximum of parameter value. + * @param translation Translation value. + */ +function updateOutputParameterValue( + parameterValue: Float32Array, + parameterValueMinimum: number, + parameterValueMaximum: number, + translation: number, + output: CubismPhysicsOutput +): void { + let outputScale: number; + let value: number; + let weight: number; + + outputScale = output.getScale(output.translationScale, output.angleScale); + + value = translation * outputScale; + + if (value < parameterValueMinimum) { + if (value < output.valueBelowMinimum) { + output.valueBelowMinimum = value; + } + + value = parameterValueMinimum; + } else if (value > parameterValueMaximum) { + if (value > output.valueExceededMaximum) { + output.valueExceededMaximum = value; + } + + value = parameterValueMaximum; + } + + weight = output.weight / MaximumWeight; + + if (weight >= 1.0) { + parameterValue[0] = value; + } else { + value = parameterValue[0] * (1.0 - weight) + value * weight; + parameterValue[0] = value; + } +} + +function normalizeParameterValue( + value: number, + parameterMinimum: number, + parameterMaximum: number, + parameterDefault: number, + normalizedMinimum: number, + normalizedMaximum: number, + normalizedDefault: number, + isInverted: boolean +) { + let result = 0.0; + + const maxValue: number = CubismMath.max(parameterMaximum, parameterMinimum); + + if (maxValue < value) { + value = maxValue; + } + + const minValue: number = CubismMath.min(parameterMaximum, parameterMinimum); + + if (minValue > value) { + value = minValue; + } + + const minNormValue: number = CubismMath.min( + normalizedMinimum, + normalizedMaximum + ); + const maxNormValue: number = CubismMath.max( + normalizedMinimum, + normalizedMaximum + ); + const middleNormValue: number = normalizedDefault; + + const middleValue: number = getDefaultValue(minValue, maxValue); + const paramValue: number = value - middleValue; + + switch (sign(paramValue)) { + case 1: { + const nLength: number = maxNormValue - middleNormValue; + const pLength: number = maxValue - middleValue; + + if (pLength != 0.0) { + result = paramValue * (nLength / pLength); + result += middleNormValue; + } + + break; + } + case -1: { + const nLength: number = minNormValue - middleNormValue; + const pLength: number = minValue - middleValue; + + if (pLength != 0.0) { + result = paramValue * (nLength / pLength); + result += middleNormValue; + } + + break; + } + case 0: { + result = middleNormValue; + + break; + } + default: { + break; + } + } + + return isInverted ? result : result * -1.0; +} + +// Namespace definition for compatibility. +import * as $ from './cubismphysics'; +// eslint-disable-next-line @typescript-eslint/no-namespace +export namespace Live2DCubismFramework { + export const CubismPhysics = $.CubismPhysics; + export type CubismPhysics = $.CubismPhysics; + export const Options = $.Options; + export type Options = $.Options; +} diff --git a/src/physics/cubismphysicsinternal.ts b/src/physics/cubismphysicsinternal.ts index 3832ca4..8c73722 100644 --- a/src/physics/cubismphysicsinternal.ts +++ b/src/physics/cubismphysicsinternal.ts @@ -5,221 +5,245 @@ * that can be found at https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html. */ -import { Live2DCubismFramework as cubismvector2 } from '../math/cubismvector2'; -import { Live2DCubismFramework as cubismid } from '../id/cubismid'; -import { Live2DCubismFramework as csmvector } from '../type/csmvector'; -import csmVector = csmvector.csmVector; -import CubismIdHandle = cubismid.CubismIdHandle; -import CubismVector2 = cubismvector2.CubismVector2; +import { CubismIdHandle } from '../id/cubismid'; +import { CubismVector2 } from '../math/cubismvector2'; +import { csmVector } from '../type/csmvector'; -export namespace Live2DCubismFramework { - /** - * 物理演算の適用先の種類 - */ - export enum CubismPhysicsTargetType { - CubismPhysicsTargetType_Parameter // パラメータに対して適用 - } - - /** - * 物理演算の入力の種類 - */ - export enum CubismPhysicsSource { - CubismPhysicsSource_X, // X軸の位置から - CubismPhysicsSource_Y, // Y軸の位置から - CubismPhysicsSource_Angle // 角度から - } - - /** - * @brief 物理演算で使用する外部の力 - * - * 物理演算で使用する外部の力。 - */ - export class PhysicsJsonEffectiveForces { - constructor() { - this.gravity = new CubismVector2(0, 0); - this.wind = new CubismVector2(0, 0); - } - gravity: CubismVector2; // 重力 - wind: CubismVector2; // 風 - } - - /** - * 物理演算のパラメータ情報 - */ - export class CubismPhysicsParameter { - id: CubismIdHandle; // パラメータ - targetType: CubismPhysicsTargetType; // 適用先の種類 - } - - /** - * 物理演算の正規化情報 - */ - export class CubismPhysicsNormalization { - minimum: number; // 最大値 - maximum: number; // 最小値 - defalut: number; // デフォルト値 - } - - /** - * 物理演算の演算委使用する物理点の情報 - */ - export class CubismPhysicsParticle { - constructor() { - this.initialPosition = new CubismVector2(0, 0); - this.position = new CubismVector2(0, 0); - this.lastPosition = new CubismVector2(0, 0); - this.lastGravity = new CubismVector2(0, 0); - this.force = new CubismVector2(0, 0); - this.velocity = new CubismVector2(0, 0); - } - - initialPosition: CubismVector2; // 初期位置 - mobility: number; // 動きやすさ - delay: number; // 遅れ - acceleration: number; // 加速度 - radius: number; // 距離 - position: CubismVector2; // 現在の位置 - lastPosition: CubismVector2; // 最後の位置 - lastGravity: CubismVector2; // 最後の重力 - force: CubismVector2; // 現在かかっている力 - velocity: CubismVector2; // 現在の速度 - } - - /** - * 物理演算の物理点の管理 - */ - export class CubismPhysicsSubRig { - constructor() { - this.normalizationPosition = new CubismPhysicsNormalization(); - this.normalizationAngle = new CubismPhysicsNormalization(); - } - inputCount: number; // 入力の個数 - outputCount: number; // 出力の個数 - particleCount: number; // 物理点の個数 - baseInputIndex: number; // 入力の最初のインデックス - baseOutputIndex: number; // 出力の最初のインデックス - baseParticleIndex: number; // 物理点の最初のインデックス - normalizationPosition: CubismPhysicsNormalization; // 正規化された位置 - normalizationAngle: CubismPhysicsNormalization; // 正規化された角度 - } - - /** - * 正規化されたパラメータの取得関数の宣言 - * @param targetTranslation // 演算結果の移動値 - * @param targetAngle // 演算結果の角度 - * @param value // パラメータの値 - * @param parameterMinimunValue // パラメータの最小値 - * @param parameterMaximumValue // パラメータの最大値 - * @param parameterDefaultValue // パラメータのデフォルト値 - * @param normalizationPosition // 正規化された位置 - * @param normalizationAngle // 正規化された角度 - * @param isInverted // 値が反転されているか? - * @param weight // 重み - */ - export interface normalizedPhysicsParameterValueGetter { - ( - targetTranslation: CubismVector2, - targetAngle: { angle: number }, - value: number, - parameterMinimunValue: number, - parameterMaximumValue: number, - parameterDefaultValue: number, - normalizationPosition: CubismPhysicsNormalization, - normalizationAngle: CubismPhysicsNormalization, - isInverted: boolean, - weight: number - ): void; - } - - /** - * 物理演算の値の取得関数の宣言 - * @param translation 移動値 - * @param particles 物理点のリスト - * @param isInverted 値が反映されているか - * @param parentGravity 重力 - * @return 値 - */ - export interface physicsValueGetter { - ( - translation: CubismVector2, - particles: CubismPhysicsParticle[], - particleIndex: number, - isInverted: boolean, - parentGravity: CubismVector2 - ): number; - } - - /** - * 物理演算のスケールの取得関数の宣言 - * @param translationScale 移動値のスケール - * @param angleScale 角度のスケール - * @return スケール値 - */ - export interface physicsScaleGetter { - (translationScale: CubismVector2, angleScale: number): number; - } - - /** - * 物理演算の入力情報 - */ - export class CubismPhysicsInput { - constructor() { - this.source = new CubismPhysicsParameter(); - } - source: CubismPhysicsParameter; // 入力元のパラメータ - sourceParameterIndex: number; // 入力元のパラメータのインデックス - weight: number; // 重み - type: number; // 入力の種類 - reflect: boolean; // 値が反転されているかどうか - getNormalizedParameterValue: normalizedPhysicsParameterValueGetter; // 正規化されたパラメータ値の取得関数 - } - - /** - * @brief 物理演算の出力情報 - * - * 物理演算の出力情報。 - */ - export class CubismPhysicsOutput { - constructor() { - this.destination = new CubismPhysicsParameter(); - this.translationScale = new CubismVector2(0, 0); - } - - destination: CubismPhysicsParameter; // 出力先のパラメータ - destinationParameterIndex: number; // 出力先のパラメータのインデックス - vertexIndex: number; // 振り子のインデックス - translationScale: CubismVector2; // 移動値のスケール - angleScale: number; // 角度のスケール - weight: number; // 重み - type: CubismPhysicsSource; // 出力の種類 - reflect: boolean; // 値が反転されているかどうか - valueBelowMinimum: number; // 最小値を下回った時の値 - valueExceededMaximum: number; // 最大値をこえた時の値 - getValue: physicsValueGetter; // 物理演算の値の取得関数 - getScale: physicsScaleGetter; // 物理演算のスケール値の取得関数 - } - - /** - * @brief 物理演算のデータ - * - * 物理演算のデータ。 - */ - export class CubismPhysicsRig { - constructor() { - this.settings = new csmVector(); - this.inputs = new csmVector(); - this.outputs = new csmVector(); - this.particles = new csmVector(); - this.gravity = new CubismVector2(0, 0); - this.wind = new CubismVector2(0, 0); - } - - subRigCount: number; // 物理演算の物理点の個数 - settings: csmVector; // 物理演算の物理点の管理のリスト - inputs: csmVector; // 物理演算の入力のリスト - outputs: csmVector; // 物理演算の出力のリスト - particles: csmVector; // 物理演算の物理点のリスト - gravity: CubismVector2; // 重力 - wind: CubismVector2; // 風 - } +/** + * 物理演算の適用先の種類 + */ +export enum CubismPhysicsTargetType { + CubismPhysicsTargetType_Parameter // パラメータに対して適用 +} + +/** + * 物理演算の入力の種類 + */ +export enum CubismPhysicsSource { + CubismPhysicsSource_X, // X軸の位置から + CubismPhysicsSource_Y, // Y軸の位置から + CubismPhysicsSource_Angle // 角度から +} + +/** + * @brief 物理演算で使用する外部の力 + * + * 物理演算で使用する外部の力。 + */ +export class PhysicsJsonEffectiveForces { + constructor() { + this.gravity = new CubismVector2(0, 0); + this.wind = new CubismVector2(0, 0); + } + gravity: CubismVector2; // 重力 + wind: CubismVector2; // 風 +} + +/** + * 物理演算のパラメータ情報 + */ +export class CubismPhysicsParameter { + id: CubismIdHandle; // パラメータ + targetType: CubismPhysicsTargetType; // 適用先の種類 +} + +/** + * 物理演算の正規化情報 + */ +export class CubismPhysicsNormalization { + minimum: number; // 最大値 + maximum: number; // 最小値 + defalut: number; // デフォルト値 +} + +/** + * 物理演算の演算委使用する物理点の情報 + */ +export class CubismPhysicsParticle { + constructor() { + this.initialPosition = new CubismVector2(0, 0); + this.position = new CubismVector2(0, 0); + this.lastPosition = new CubismVector2(0, 0); + this.lastGravity = new CubismVector2(0, 0); + this.force = new CubismVector2(0, 0); + this.velocity = new CubismVector2(0, 0); + } + + initialPosition: CubismVector2; // 初期位置 + mobility: number; // 動きやすさ + delay: number; // 遅れ + acceleration: number; // 加速度 + radius: number; // 距離 + position: CubismVector2; // 現在の位置 + lastPosition: CubismVector2; // 最後の位置 + lastGravity: CubismVector2; // 最後の重力 + force: CubismVector2; // 現在かかっている力 + velocity: CubismVector2; // 現在の速度 +} + +/** + * 物理演算の物理点の管理 + */ +export class CubismPhysicsSubRig { + constructor() { + this.normalizationPosition = new CubismPhysicsNormalization(); + this.normalizationAngle = new CubismPhysicsNormalization(); + } + inputCount: number; // 入力の個数 + outputCount: number; // 出力の個数 + particleCount: number; // 物理点の個数 + baseInputIndex: number; // 入力の最初のインデックス + baseOutputIndex: number; // 出力の最初のインデックス + baseParticleIndex: number; // 物理点の最初のインデックス + normalizationPosition: CubismPhysicsNormalization; // 正規化された位置 + normalizationAngle: CubismPhysicsNormalization; // 正規化された角度 +} + +/** + * 正規化されたパラメータの取得関数の宣言 + * @param targetTranslation // 演算結果の移動値 + * @param targetAngle // 演算結果の角度 + * @param value // パラメータの値 + * @param parameterMinimunValue // パラメータの最小値 + * @param parameterMaximumValue // パラメータの最大値 + * @param parameterDefaultValue // パラメータのデフォルト値 + * @param normalizationPosition // 正規化された位置 + * @param normalizationAngle // 正規化された角度 + * @param isInverted // 値が反転されているか? + * @param weight // 重み + */ +export interface normalizedPhysicsParameterValueGetter { + ( + targetTranslation: CubismVector2, + targetAngle: { angle: number }, + value: number, + parameterMinimunValue: number, + parameterMaximumValue: number, + parameterDefaultValue: number, + normalizationPosition: CubismPhysicsNormalization, + normalizationAngle: CubismPhysicsNormalization, + isInverted: boolean, + weight: number + ): void; +} + +/** + * 物理演算の値の取得関数の宣言 + * @param translation 移動値 + * @param particles 物理点のリスト + * @param isInverted 値が反映されているか + * @param parentGravity 重力 + * @return 値 + */ +export interface physicsValueGetter { + ( + translation: CubismVector2, + particles: CubismPhysicsParticle[], + particleIndex: number, + isInverted: boolean, + parentGravity: CubismVector2 + ): number; +} + +/** + * 物理演算のスケールの取得関数の宣言 + * @param translationScale 移動値のスケール + * @param angleScale 角度のスケール + * @return スケール値 + */ +export interface physicsScaleGetter { + (translationScale: CubismVector2, angleScale: number): number; +} + +/** + * 物理演算の入力情報 + */ +export class CubismPhysicsInput { + constructor() { + this.source = new CubismPhysicsParameter(); + } + source: CubismPhysicsParameter; // 入力元のパラメータ + sourceParameterIndex: number; // 入力元のパラメータのインデックス + weight: number; // 重み + type: number; // 入力の種類 + reflect: boolean; // 値が反転されているかどうか + getNormalizedParameterValue: normalizedPhysicsParameterValueGetter; // 正規化されたパラメータ値の取得関数 +} + +/** + * @brief 物理演算の出力情報 + * + * 物理演算の出力情報。 + */ +export class CubismPhysicsOutput { + constructor() { + this.destination = new CubismPhysicsParameter(); + this.translationScale = new CubismVector2(0, 0); + } + + destination: CubismPhysicsParameter; // 出力先のパラメータ + destinationParameterIndex: number; // 出力先のパラメータのインデックス + vertexIndex: number; // 振り子のインデックス + translationScale: CubismVector2; // 移動値のスケール + angleScale: number; // 角度のスケール + weight: number; // 重み + type: CubismPhysicsSource; // 出力の種類 + reflect: boolean; // 値が反転されているかどうか + valueBelowMinimum: number; // 最小値を下回った時の値 + valueExceededMaximum: number; // 最大値をこえた時の値 + getValue: physicsValueGetter; // 物理演算の値の取得関数 + getScale: physicsScaleGetter; // 物理演算のスケール値の取得関数 +} + +/** + * @brief 物理演算のデータ + * + * 物理演算のデータ。 + */ +export class CubismPhysicsRig { + constructor() { + this.settings = new csmVector(); + this.inputs = new csmVector(); + this.outputs = new csmVector(); + this.particles = new csmVector(); + this.gravity = new CubismVector2(0, 0); + this.wind = new CubismVector2(0, 0); + } + + subRigCount: number; // 物理演算の物理点の個数 + settings: csmVector; // 物理演算の物理点の管理のリスト + inputs: csmVector; // 物理演算の入力のリスト + outputs: csmVector; // 物理演算の出力のリスト + particles: csmVector; // 物理演算の物理点のリスト + gravity: CubismVector2; // 重力 + wind: CubismVector2; // 風 +} + +// Namespace definition for compatibility. +import * as $ from './cubismphysicsinternal'; +// eslint-disable-next-line @typescript-eslint/no-namespace +export namespace Live2DCubismFramework { + export const CubismPhysicsInput = $.CubismPhysicsInput; + export type CubismPhysicsInput = $.CubismPhysicsInput; + export const CubismPhysicsNormalization = $.CubismPhysicsNormalization; + export type CubismPhysicsNormalization = $.CubismPhysicsNormalization; + export const CubismPhysicsOutput = $.CubismPhysicsOutput; + export type CubismPhysicsOutput = $.CubismPhysicsOutput; + export const CubismPhysicsParameter = $.CubismPhysicsParameter; + export type CubismPhysicsParameter = $.CubismPhysicsParameter; + export const CubismPhysicsParticle = $.CubismPhysicsParticle; + export type CubismPhysicsParticle = $.CubismPhysicsParticle; + export const CubismPhysicsRig = $.CubismPhysicsRig; + export type CubismPhysicsRig = $.CubismPhysicsRig; + export const CubismPhysicsSource = $.CubismPhysicsSource; + export type CubismPhysicsSource = $.CubismPhysicsSource; + export const CubismPhysicsSubRig = $.CubismPhysicsSubRig; + export type CubismPhysicsSubRig = $.CubismPhysicsSubRig; + export const CubismPhysicsTargetType = $.CubismPhysicsTargetType; + export type CubismPhysicsTargetType = $.CubismPhysicsTargetType; + export const PhysicsJsonEffectiveForces = $.PhysicsJsonEffectiveForces; + export type PhysicsJsonEffectiveForces = $.PhysicsJsonEffectiveForces; + export type normalizedPhysicsParameterValueGetter = $.normalizedPhysicsParameterValueGetter; + export type physicsScaleGetter = $.physicsScaleGetter; + export type physicsValueGetter = $.physicsValueGetter; } diff --git a/src/physics/cubismphysicsjson.ts b/src/physics/cubismphysicsjson.ts index 296765b..35c4529 100644 --- a/src/physics/cubismphysicsjson.ts +++ b/src/physics/cubismphysicsjson.ts @@ -5,645 +5,644 @@ * that can be found at https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html. */ -import { Live2DCubismFramework as cubismjson } from '../utils/cubismjson'; -import { Live2DCubismFramework as cubismvector2 } from '../math/cubismvector2'; -import { Live2DCubismFramework as cubismid } from '../id/cubismid'; -import { Live2DCubismFramework as cubismframework } from '../live2dcubismframework'; -import CubismFramework = cubismframework.CubismFramework; -import CubismIdHandle = cubismid.CubismIdHandle; -import CubismVector2 = cubismvector2.CubismVector2; -import CubismJson = cubismjson.CubismJson; +import { CubismIdHandle } from '../id/cubismid'; +import { CubismFramework } from '../live2dcubismframework'; +import { CubismVector2 } from '../math/cubismvector2'; +import { CubismJson } from '../utils/cubismjson'; -export namespace Live2DCubismFramework { - // JSON keys - const Position = 'Position'; - const X = 'X'; - const Y = 'Y'; - const Angle = 'Angle'; - const Type = 'Type'; - const Id = 'Id'; +// JSON keys +const Position = 'Position'; +const X = 'X'; +const Y = 'Y'; +const Angle = 'Angle'; +const Type = 'Type'; +const Id = 'Id'; - // Meta - const Meta = 'Meta'; - const EffectiveForces = 'EffectiveForces'; - const TotalInputCount = 'TotalInputCount'; - const TotalOutputCount = 'TotalOutputCount'; - const PhysicsSettingCount = 'PhysicsSettingCount'; - const Gravity = 'Gravity'; - const Wind = 'Wind'; - const VertexCount = 'VertexCount'; +// Meta +const Meta = 'Meta'; +const EffectiveForces = 'EffectiveForces'; +const TotalInputCount = 'TotalInputCount'; +const TotalOutputCount = 'TotalOutputCount'; +const PhysicsSettingCount = 'PhysicsSettingCount'; +const Gravity = 'Gravity'; +const Wind = 'Wind'; +const VertexCount = 'VertexCount'; - // PhysicsSettings - const PhysicsSettings = 'PhysicsSettings'; - const Normalization = 'Normalization'; - const Minimum = 'Minimum'; - const Maximum = 'Maximum'; - const Default = 'Default'; - const Reflect = 'Reflect'; - const Weight = 'Weight'; +// PhysicsSettings +const PhysicsSettings = 'PhysicsSettings'; +const Normalization = 'Normalization'; +const Minimum = 'Minimum'; +const Maximum = 'Maximum'; +const Default = 'Default'; +const Reflect = 'Reflect'; +const Weight = 'Weight'; - // Input - const Input = 'Input'; - const Source = 'Source'; +// Input +const Input = 'Input'; +const Source = 'Source'; - // Output - const Output = 'Output'; - const Scale = 'Scale'; - const VertexIndex = 'VertexIndex'; - const Destination = 'Destination'; +// Output +const Output = 'Output'; +const Scale = 'Scale'; +const VertexIndex = 'VertexIndex'; +const Destination = 'Destination'; - // Particle - const Vertices = 'Vertices'; - const Mobility = 'Mobility'; - const Delay = 'Delay'; - const Radius = 'Radius'; - const Acceleration = 'Acceleration'; +// Particle +const Vertices = 'Vertices'; +const Mobility = 'Mobility'; +const Delay = 'Delay'; +const Radius = 'Radius'; +const Acceleration = 'Acceleration'; + +/** + * physics3.jsonのコンテナ。 + */ +export class CubismPhysicsJson { + /** + * コンストラクタ + * @param buffer physics3.jsonが読み込まれているバッファ + * @param size バッファのサイズ + */ + public constructor(buffer: ArrayBuffer, size: number) { + this._json = CubismJson.create(buffer, size); + } /** - * physics3.jsonのコンテナ。 + * デストラクタ相当の処理 */ - export class CubismPhysicsJson { - /** - * コンストラクタ - * @param buffer physics3.jsonが読み込まれているバッファ - * @param size バッファのサイズ - */ - public constructor(buffer: ArrayBuffer, size: number) { - this._json = CubismJson.create(buffer, size); - } - - /** - * デストラクタ相当の処理 - */ - public release(): void { - CubismJson.delete(this._json); - } - - /** - * 重力の取得 - * @return 重力 - */ - public getGravity(): CubismVector2 { - const ret: CubismVector2 = new CubismVector2(0, 0); - ret.x = this._json - .getRoot() - .getValueByString(Meta) - .getValueByString(EffectiveForces) - .getValueByString(Gravity) - .getValueByString(X) - .toFloat(); - ret.y = this._json - .getRoot() - .getValueByString(Meta) - .getValueByString(EffectiveForces) - .getValueByString(Gravity) - .getValueByString(Y) - .toFloat(); - return ret; - } - - /** - * 風の取得 - * @return 風 - */ - public getWind(): CubismVector2 { - const ret: CubismVector2 = new CubismVector2(0, 0); - ret.x = this._json - .getRoot() - .getValueByString(Meta) - .getValueByString(EffectiveForces) - .getValueByString(Wind) - .getValueByString(X) - .toFloat(); - ret.y = this._json - .getRoot() - .getValueByString(Meta) - .getValueByString(EffectiveForces) - .getValueByString(Wind) - .getValueByString(Y) - .toFloat(); - return ret; - } - - /** - * 物理店の管理の個数の取得 - * @return 物理店の管理の個数 - */ - public getSubRigCount(): number { - return this._json - .getRoot() - .getValueByString(Meta) - .getValueByString(PhysicsSettingCount) - .toInt(); - } - - /** - * 入力の総合計の取得 - * @return 入力の総合計 - */ - public getTotalInputCount(): number { - return this._json - .getRoot() - .getValueByString(Meta) - .getValueByString(TotalInputCount) - .toInt(); - } - - /** - * 出力の総合計の取得 - * @return 出力の総合計 - */ - public getTotalOutputCount(): number { - return this._json - .getRoot() - .getValueByString(Meta) - .getValueByString(TotalOutputCount) - .toInt(); - } - - /** - * 物理点の個数の取得 - * @return 物理点の個数 - */ - public getVertexCount(): number { - return this._json - .getRoot() - .getValueByString(Meta) - .getValueByString(VertexCount) - .toInt(); - } - - /** - * 正規化された位置の最小値の取得 - * @param physicsSettingIndex 物理演算の設定のインデックス - * @return 正規化された位置の最小値 - */ - public getNormalizationPositionMinimumValue( - physicsSettingIndex: number - ): number { - return this._json - .getRoot() - .getValueByString(PhysicsSettings) - .getValueByIndex(physicsSettingIndex) - .getValueByString(Normalization) - .getValueByString(Position) - .getValueByString(Minimum) - .toFloat(); - } - - /** - * 正規化された位置の最大値の取得 - * @param physicsSettingIndex 物理演算の設定のインデックス - * @return 正規化された位置の最大値 - */ - public getNormalizationPositionMaximumValue( - physicsSettingIndex: number - ): number { - return this._json - .getRoot() - .getValueByString(PhysicsSettings) - .getValueByIndex(physicsSettingIndex) - .getValueByString(Normalization) - .getValueByString(Position) - .getValueByString(Maximum) - .toFloat(); - } - - /** - * 正規化された位置のデフォルト値の取得 - * @param physicsSettingIndex 物理演算の設定のインデックス - * @return 正規化された位置のデフォルト値 - */ - public getNormalizationPositionDefaultValue( - physicsSettingIndex: number - ): number { - return this._json - .getRoot() - .getValueByString(PhysicsSettings) - .getValueByIndex(physicsSettingIndex) - .getValueByString(Normalization) - .getValueByString(Position) - .getValueByString(Default) - .toFloat(); - } - - /** - * 正規化された角度の最小値の取得 - * @param physicsSettingIndex 物理演算の設定のインデックス - * @return 正規化された角度の最小値 - */ - public getNormalizationAngleMinimumValue( - physicsSettingIndex: number - ): number { - return this._json - .getRoot() - .getValueByString(PhysicsSettings) - .getValueByIndex(physicsSettingIndex) - .getValueByString(Normalization) - .getValueByString(Angle) - .getValueByString(Minimum) - .toFloat(); - } - - /** - * 正規化された角度の最大値の取得 - * @param physicsSettingIndex - * @return 正規化された角度の最大値 - */ - public getNormalizationAngleMaximumValue( - physicsSettingIndex: number - ): number { - return this._json - .getRoot() - .getValueByString(PhysicsSettings) - .getValueByIndex(physicsSettingIndex) - .getValueByString(Normalization) - .getValueByString(Angle) - .getValueByString(Maximum) - .toFloat(); - } - - /** - * 正規化された角度のデフォルト値の取得 - * @param physicsSettingIndex 物理演算の設定のインデックス - * @return 正規化された角度のデフォルト値 - */ - public getNormalizationAngleDefaultValue( - physicsSettingIndex: number - ): number { - return this._json - .getRoot() - .getValueByString(PhysicsSettings) - .getValueByIndex(physicsSettingIndex) - .getValueByString(Normalization) - .getValueByString(Angle) - .getValueByString(Default) - .toFloat(); - } - - /** - * 入力の個数の取得 - * @param physicsSettingIndex 物理演算の設定のインデックス - * @return 入力の個数 - */ - public getInputCount(physicsSettingIndex: number): number { - return this._json - .getRoot() - .getValueByString(PhysicsSettings) - .getValueByIndex(physicsSettingIndex) - .getValueByString(Input) - .getVector() - .getSize(); - } - - /** - * 入力の重みの取得 - * @param physicsSettingIndex 物理演算の設定のインデックス - * @param inputIndex 入力のインデックス - * @return 入力の重み - */ - public getInputWeight( - physicsSettingIndex: number, - inputIndex: number - ): number { - return this._json - .getRoot() - .getValueByString(PhysicsSettings) - .getValueByIndex(physicsSettingIndex) - .getValueByString(Input) - .getValueByIndex(inputIndex) - .getValueByString(Weight) - .toFloat(); - } - - /** - * 入力の反転の取得 - * @param physicsSettingIndex 物理演算の設定のインデックス - * @param inputIndex 入力のインデックス - * @return 入力の反転 - */ - public getInputReflect( - physicsSettingIndex: number, - inputIndex: number - ): boolean { - return this._json - .getRoot() - .getValueByString(PhysicsSettings) - .getValueByIndex(physicsSettingIndex) - .getValueByString(Input) - .getValueByIndex(inputIndex) - .getValueByString(Reflect) - .toBoolean(); - } - - /** - * 入力の種類の取得 - * @param physicsSettingIndex 物理演算の設定のインデックス - * @param inputIndex 入力のインデックス - * @return 入力の種類 - */ - public getInputType( - physicsSettingIndex: number, - inputIndex: number - ): string { - return this._json - .getRoot() - .getValueByString(PhysicsSettings) - .getValueByIndex(physicsSettingIndex) - .getValueByString(Input) - .getValueByIndex(inputIndex) - .getValueByString(Type) - .getRawString(); - } - - /** - * 入力元のIDの取得 - * @param physicsSettingIndex 物理演算の設定のインデックス - * @param inputIndex 入力のインデックス - * @return 入力元のID - */ - public getInputSourceId( - physicsSettingIndex: number, - inputIndex: number - ): CubismIdHandle { - return CubismFramework.getIdManager().getId( - this._json - .getRoot() - .getValueByString(PhysicsSettings) - .getValueByIndex(physicsSettingIndex) - .getValueByString(Input) - .getValueByIndex(inputIndex) - .getValueByString(Source) - .getValueByString(Id) - .getRawString() - ); - } - - /** - * 出力の個数の取得 - * @param physicsSettingIndex 物理演算の設定のインデックス - * @return 出力の個数 - */ - public getOutputCount(physicsSettingIndex: number): number { - return this._json - .getRoot() - .getValueByString(PhysicsSettings) - .getValueByIndex(physicsSettingIndex) - .getValueByString(Output) - .getVector() - .getSize(); - } - - /** - * 出力の物理点のインデックスの取得 - * @param physicsSettingIndex 物理演算の設定のインデックス - * @param outputIndex 出力のインデックス - * @return 出力の物理点のインデックス - */ - public getOutputVertexIndex( - physicsSettingIndex: number, - outputIndex: number - ): number { - return this._json - .getRoot() - .getValueByString(PhysicsSettings) - .getValueByIndex(physicsSettingIndex) - .getValueByString(Output) - .getValueByIndex(outputIndex) - .getValueByString(VertexIndex) - .toInt(); - } - - /** - * 出力の角度のスケールを取得する - * @param physicsSettingIndex 物理演算の設定のインデックス - * @param outputIndex 出力のインデックス - * @return 出力の角度のスケール - */ - public getOutputAngleScale( - physicsSettingIndex: number, - outputIndex: number - ): number { - return this._json - .getRoot() - .getValueByString(PhysicsSettings) - .getValueByIndex(physicsSettingIndex) - .getValueByString(Output) - .getValueByIndex(outputIndex) - .getValueByString(Scale) - .toFloat(); - } - - /** - * 出力の重みの取得 - * @param physicsSettingIndex 物理演算の設定のインデックス - * @param outputIndex 出力のインデックス - * @return 出力の重み - */ - public getOutputWeight( - physicsSettingIndex: number, - outputIndex: number - ): number { - return this._json - .getRoot() - .getValueByString(PhysicsSettings) - .getValueByIndex(physicsSettingIndex) - .getValueByString(Output) - .getValueByIndex(outputIndex) - .getValueByString(Weight) - .toFloat(); - } - - /** - * 出力先のIDの取得 - * @param physicsSettingIndex 物理演算の設定のインデックス - * @param outputIndex 出力のインデックス - * @return 出力先のID - */ - public getOutputDestinationId( - physicsSettingIndex: number, - outputIndex: number - ): CubismIdHandle { - return CubismFramework.getIdManager().getId( - this._json - .getRoot() - .getValueByString(PhysicsSettings) - .getValueByIndex(physicsSettingIndex) - .getValueByString(Output) - .getValueByIndex(outputIndex) - .getValueByString(Destination) - .getValueByString(Id) - .getRawString() - ); - } - - /** - * 出力の種類の取得 - * @param physicsSettingIndex 物理演算の設定のインデックス - * @param outputIndex 出力のインデックス - * @return 出力の種類 - */ - public getOutputType( - physicsSettingIndex: number, - outputIndex: number - ): string { - return this._json - .getRoot() - .getValueByString(PhysicsSettings) - .getValueByIndex(physicsSettingIndex) - .getValueByString(Output) - .getValueByIndex(outputIndex) - .getValueByString(Type) - .getRawString(); - } - - /** - * 出力の反転の取得 - * @param physicsSettingIndex 物理演算のインデックス - * @param outputIndex 出力のインデックス - * @return 出力の反転 - */ - public getOutputReflect( - physicsSettingIndex: number, - outputIndex: number - ): boolean { - return this._json - .getRoot() - .getValueByString(PhysicsSettings) - .getValueByIndex(physicsSettingIndex) - .getValueByString(Output) - .getValueByIndex(outputIndex) - .getValueByString(Reflect) - .toBoolean(); - } - - /** - * 物理点の個数の取得 - * @param physicsSettingIndex 物理演算男設定のインデックス - * @return 物理点の個数 - */ - public getParticleCount(physicsSettingIndex: number): number { - return this._json - .getRoot() - .getValueByString(PhysicsSettings) - .getValueByIndex(physicsSettingIndex) - .getValueByString(Vertices) - .getVector() - .getSize(); - } - - /** - * 物理点の動きやすさの取得 - * @param physicsSettingIndex 物理演算の設定のインデックス - * @param vertexIndex 物理点のインデックス - * @return 物理点の動きやすさ - */ - public getParticleMobility( - physicsSettingIndex: number, - vertexIndex: number - ): number { - return this._json - .getRoot() - .getValueByString(PhysicsSettings) - .getValueByIndex(physicsSettingIndex) - .getValueByString(Vertices) - .getValueByIndex(vertexIndex) - .getValueByString(Mobility) - .toFloat(); - } - - /** - * 物理点の遅れの取得 - * @param physicsSettingIndex 物理演算の設定のインデックス - * @param vertexIndex 物理点のインデックス - * @return 物理点の遅れ - */ - public getParticleDelay( - physicsSettingIndex: number, - vertexIndex: number - ): number { - return this._json - .getRoot() - .getValueByString(PhysicsSettings) - .getValueByIndex(physicsSettingIndex) - .getValueByString(Vertices) - .getValueByIndex(vertexIndex) - .getValueByString(Delay) - .toFloat(); - } - - /** - * 物理点の加速度の取得 - * @param physicsSettingIndex 物理演算の設定 - * @param vertexIndex 物理点のインデックス - * @return 物理点の加速度 - */ - public getParticleAcceleration( - physicsSettingIndex: number, - vertexIndex: number - ): number { - return this._json - .getRoot() - .getValueByString(PhysicsSettings) - .getValueByIndex(physicsSettingIndex) - .getValueByString(Vertices) - .getValueByIndex(vertexIndex) - .getValueByString(Acceleration) - .toFloat(); - } - - /** - * 物理点の距離の取得 - * @param physicsSettingIndex 物理演算の設定のインデックス - * @param vertexIndex 物理点のインデックス - * @return 物理点の距離 - */ - public getParticleRadius( - physicsSettingIndex: number, - vertexIndex: number - ): number { - return this._json - .getRoot() - .getValueByString(PhysicsSettings) - .getValueByIndex(physicsSettingIndex) - .getValueByString(Vertices) - .getValueByIndex(vertexIndex) - .getValueByString(Radius) - .toFloat(); - } - - /** - * 物理点の位置の取得 - * @param physicsSettingIndex 物理演算の設定のインデックス - * @param vertexInde 物理点のインデックス - * @return 物理点の位置 - */ - public getParticlePosition( - physicsSettingIndex: number, - vertexIndex: number - ): CubismVector2 { - const ret: CubismVector2 = new CubismVector2(0, 0); - ret.x = this._json - .getRoot() - .getValueByString(PhysicsSettings) - .getValueByIndex(physicsSettingIndex) - .getValueByString(Vertices) - .getValueByIndex(vertexIndex) - .getValueByString(Position) - .getValueByString(X) - .toFloat(); - ret.y = this._json - .getRoot() - .getValueByString(PhysicsSettings) - .getValueByIndex(physicsSettingIndex) - .getValueByString(Vertices) - .getValueByIndex(vertexIndex) - .getValueByString(Position) - .getValueByString(Y) - .toFloat(); - return ret; - } - - _json: CubismJson; // physics3.jsonデータ + public release(): void { + CubismJson.delete(this._json); } + + /** + * 重力の取得 + * @return 重力 + */ + public getGravity(): CubismVector2 { + const ret: CubismVector2 = new CubismVector2(0, 0); + ret.x = this._json + .getRoot() + .getValueByString(Meta) + .getValueByString(EffectiveForces) + .getValueByString(Gravity) + .getValueByString(X) + .toFloat(); + ret.y = this._json + .getRoot() + .getValueByString(Meta) + .getValueByString(EffectiveForces) + .getValueByString(Gravity) + .getValueByString(Y) + .toFloat(); + return ret; + } + + /** + * 風の取得 + * @return 風 + */ + public getWind(): CubismVector2 { + const ret: CubismVector2 = new CubismVector2(0, 0); + ret.x = this._json + .getRoot() + .getValueByString(Meta) + .getValueByString(EffectiveForces) + .getValueByString(Wind) + .getValueByString(X) + .toFloat(); + ret.y = this._json + .getRoot() + .getValueByString(Meta) + .getValueByString(EffectiveForces) + .getValueByString(Wind) + .getValueByString(Y) + .toFloat(); + return ret; + } + + /** + * 物理店の管理の個数の取得 + * @return 物理店の管理の個数 + */ + public getSubRigCount(): number { + return this._json + .getRoot() + .getValueByString(Meta) + .getValueByString(PhysicsSettingCount) + .toInt(); + } + + /** + * 入力の総合計の取得 + * @return 入力の総合計 + */ + public getTotalInputCount(): number { + return this._json + .getRoot() + .getValueByString(Meta) + .getValueByString(TotalInputCount) + .toInt(); + } + + /** + * 出力の総合計の取得 + * @return 出力の総合計 + */ + public getTotalOutputCount(): number { + return this._json + .getRoot() + .getValueByString(Meta) + .getValueByString(TotalOutputCount) + .toInt(); + } + + /** + * 物理点の個数の取得 + * @return 物理点の個数 + */ + public getVertexCount(): number { + return this._json + .getRoot() + .getValueByString(Meta) + .getValueByString(VertexCount) + .toInt(); + } + + /** + * 正規化された位置の最小値の取得 + * @param physicsSettingIndex 物理演算の設定のインデックス + * @return 正規化された位置の最小値 + */ + public getNormalizationPositionMinimumValue( + physicsSettingIndex: number + ): number { + return this._json + .getRoot() + .getValueByString(PhysicsSettings) + .getValueByIndex(physicsSettingIndex) + .getValueByString(Normalization) + .getValueByString(Position) + .getValueByString(Minimum) + .toFloat(); + } + + /** + * 正規化された位置の最大値の取得 + * @param physicsSettingIndex 物理演算の設定のインデックス + * @return 正規化された位置の最大値 + */ + public getNormalizationPositionMaximumValue( + physicsSettingIndex: number + ): number { + return this._json + .getRoot() + .getValueByString(PhysicsSettings) + .getValueByIndex(physicsSettingIndex) + .getValueByString(Normalization) + .getValueByString(Position) + .getValueByString(Maximum) + .toFloat(); + } + + /** + * 正規化された位置のデフォルト値の取得 + * @param physicsSettingIndex 物理演算の設定のインデックス + * @return 正規化された位置のデフォルト値 + */ + public getNormalizationPositionDefaultValue( + physicsSettingIndex: number + ): number { + return this._json + .getRoot() + .getValueByString(PhysicsSettings) + .getValueByIndex(physicsSettingIndex) + .getValueByString(Normalization) + .getValueByString(Position) + .getValueByString(Default) + .toFloat(); + } + + /** + * 正規化された角度の最小値の取得 + * @param physicsSettingIndex 物理演算の設定のインデックス + * @return 正規化された角度の最小値 + */ + public getNormalizationAngleMinimumValue( + physicsSettingIndex: number + ): number { + return this._json + .getRoot() + .getValueByString(PhysicsSettings) + .getValueByIndex(physicsSettingIndex) + .getValueByString(Normalization) + .getValueByString(Angle) + .getValueByString(Minimum) + .toFloat(); + } + + /** + * 正規化された角度の最大値の取得 + * @param physicsSettingIndex + * @return 正規化された角度の最大値 + */ + public getNormalizationAngleMaximumValue( + physicsSettingIndex: number + ): number { + return this._json + .getRoot() + .getValueByString(PhysicsSettings) + .getValueByIndex(physicsSettingIndex) + .getValueByString(Normalization) + .getValueByString(Angle) + .getValueByString(Maximum) + .toFloat(); + } + + /** + * 正規化された角度のデフォルト値の取得 + * @param physicsSettingIndex 物理演算の設定のインデックス + * @return 正規化された角度のデフォルト値 + */ + public getNormalizationAngleDefaultValue( + physicsSettingIndex: number + ): number { + return this._json + .getRoot() + .getValueByString(PhysicsSettings) + .getValueByIndex(physicsSettingIndex) + .getValueByString(Normalization) + .getValueByString(Angle) + .getValueByString(Default) + .toFloat(); + } + + /** + * 入力の個数の取得 + * @param physicsSettingIndex 物理演算の設定のインデックス + * @return 入力の個数 + */ + public getInputCount(physicsSettingIndex: number): number { + return this._json + .getRoot() + .getValueByString(PhysicsSettings) + .getValueByIndex(physicsSettingIndex) + .getValueByString(Input) + .getVector() + .getSize(); + } + + /** + * 入力の重みの取得 + * @param physicsSettingIndex 物理演算の設定のインデックス + * @param inputIndex 入力のインデックス + * @return 入力の重み + */ + public getInputWeight( + physicsSettingIndex: number, + inputIndex: number + ): number { + return this._json + .getRoot() + .getValueByString(PhysicsSettings) + .getValueByIndex(physicsSettingIndex) + .getValueByString(Input) + .getValueByIndex(inputIndex) + .getValueByString(Weight) + .toFloat(); + } + + /** + * 入力の反転の取得 + * @param physicsSettingIndex 物理演算の設定のインデックス + * @param inputIndex 入力のインデックス + * @return 入力の反転 + */ + public getInputReflect( + physicsSettingIndex: number, + inputIndex: number + ): boolean { + return this._json + .getRoot() + .getValueByString(PhysicsSettings) + .getValueByIndex(physicsSettingIndex) + .getValueByString(Input) + .getValueByIndex(inputIndex) + .getValueByString(Reflect) + .toBoolean(); + } + + /** + * 入力の種類の取得 + * @param physicsSettingIndex 物理演算の設定のインデックス + * @param inputIndex 入力のインデックス + * @return 入力の種類 + */ + public getInputType(physicsSettingIndex: number, inputIndex: number): string { + return this._json + .getRoot() + .getValueByString(PhysicsSettings) + .getValueByIndex(physicsSettingIndex) + .getValueByString(Input) + .getValueByIndex(inputIndex) + .getValueByString(Type) + .getRawString(); + } + + /** + * 入力元のIDの取得 + * @param physicsSettingIndex 物理演算の設定のインデックス + * @param inputIndex 入力のインデックス + * @return 入力元のID + */ + public getInputSourceId( + physicsSettingIndex: number, + inputIndex: number + ): CubismIdHandle { + return CubismFramework.getIdManager().getId( + this._json + .getRoot() + .getValueByString(PhysicsSettings) + .getValueByIndex(physicsSettingIndex) + .getValueByString(Input) + .getValueByIndex(inputIndex) + .getValueByString(Source) + .getValueByString(Id) + .getRawString() + ); + } + + /** + * 出力の個数の取得 + * @param physicsSettingIndex 物理演算の設定のインデックス + * @return 出力の個数 + */ + public getOutputCount(physicsSettingIndex: number): number { + return this._json + .getRoot() + .getValueByString(PhysicsSettings) + .getValueByIndex(physicsSettingIndex) + .getValueByString(Output) + .getVector() + .getSize(); + } + + /** + * 出力の物理点のインデックスの取得 + * @param physicsSettingIndex 物理演算の設定のインデックス + * @param outputIndex 出力のインデックス + * @return 出力の物理点のインデックス + */ + public getOutputVertexIndex( + physicsSettingIndex: number, + outputIndex: number + ): number { + return this._json + .getRoot() + .getValueByString(PhysicsSettings) + .getValueByIndex(physicsSettingIndex) + .getValueByString(Output) + .getValueByIndex(outputIndex) + .getValueByString(VertexIndex) + .toInt(); + } + + /** + * 出力の角度のスケールを取得する + * @param physicsSettingIndex 物理演算の設定のインデックス + * @param outputIndex 出力のインデックス + * @return 出力の角度のスケール + */ + public getOutputAngleScale( + physicsSettingIndex: number, + outputIndex: number + ): number { + return this._json + .getRoot() + .getValueByString(PhysicsSettings) + .getValueByIndex(physicsSettingIndex) + .getValueByString(Output) + .getValueByIndex(outputIndex) + .getValueByString(Scale) + .toFloat(); + } + + /** + * 出力の重みの取得 + * @param physicsSettingIndex 物理演算の設定のインデックス + * @param outputIndex 出力のインデックス + * @return 出力の重み + */ + public getOutputWeight( + physicsSettingIndex: number, + outputIndex: number + ): number { + return this._json + .getRoot() + .getValueByString(PhysicsSettings) + .getValueByIndex(physicsSettingIndex) + .getValueByString(Output) + .getValueByIndex(outputIndex) + .getValueByString(Weight) + .toFloat(); + } + + /** + * 出力先のIDの取得 + * @param physicsSettingIndex 物理演算の設定のインデックス + * @param outputIndex 出力のインデックス + * @return 出力先のID + */ + public getOutputDestinationId( + physicsSettingIndex: number, + outputIndex: number + ): CubismIdHandle { + return CubismFramework.getIdManager().getId( + this._json + .getRoot() + .getValueByString(PhysicsSettings) + .getValueByIndex(physicsSettingIndex) + .getValueByString(Output) + .getValueByIndex(outputIndex) + .getValueByString(Destination) + .getValueByString(Id) + .getRawString() + ); + } + + /** + * 出力の種類の取得 + * @param physicsSettingIndex 物理演算の設定のインデックス + * @param outputIndex 出力のインデックス + * @return 出力の種類 + */ + public getOutputType( + physicsSettingIndex: number, + outputIndex: number + ): string { + return this._json + .getRoot() + .getValueByString(PhysicsSettings) + .getValueByIndex(physicsSettingIndex) + .getValueByString(Output) + .getValueByIndex(outputIndex) + .getValueByString(Type) + .getRawString(); + } + + /** + * 出力の反転の取得 + * @param physicsSettingIndex 物理演算のインデックス + * @param outputIndex 出力のインデックス + * @return 出力の反転 + */ + public getOutputReflect( + physicsSettingIndex: number, + outputIndex: number + ): boolean { + return this._json + .getRoot() + .getValueByString(PhysicsSettings) + .getValueByIndex(physicsSettingIndex) + .getValueByString(Output) + .getValueByIndex(outputIndex) + .getValueByString(Reflect) + .toBoolean(); + } + + /** + * 物理点の個数の取得 + * @param physicsSettingIndex 物理演算男設定のインデックス + * @return 物理点の個数 + */ + public getParticleCount(physicsSettingIndex: number): number { + return this._json + .getRoot() + .getValueByString(PhysicsSettings) + .getValueByIndex(physicsSettingIndex) + .getValueByString(Vertices) + .getVector() + .getSize(); + } + + /** + * 物理点の動きやすさの取得 + * @param physicsSettingIndex 物理演算の設定のインデックス + * @param vertexIndex 物理点のインデックス + * @return 物理点の動きやすさ + */ + public getParticleMobility( + physicsSettingIndex: number, + vertexIndex: number + ): number { + return this._json + .getRoot() + .getValueByString(PhysicsSettings) + .getValueByIndex(physicsSettingIndex) + .getValueByString(Vertices) + .getValueByIndex(vertexIndex) + .getValueByString(Mobility) + .toFloat(); + } + + /** + * 物理点の遅れの取得 + * @param physicsSettingIndex 物理演算の設定のインデックス + * @param vertexIndex 物理点のインデックス + * @return 物理点の遅れ + */ + public getParticleDelay( + physicsSettingIndex: number, + vertexIndex: number + ): number { + return this._json + .getRoot() + .getValueByString(PhysicsSettings) + .getValueByIndex(physicsSettingIndex) + .getValueByString(Vertices) + .getValueByIndex(vertexIndex) + .getValueByString(Delay) + .toFloat(); + } + + /** + * 物理点の加速度の取得 + * @param physicsSettingIndex 物理演算の設定 + * @param vertexIndex 物理点のインデックス + * @return 物理点の加速度 + */ + public getParticleAcceleration( + physicsSettingIndex: number, + vertexIndex: number + ): number { + return this._json + .getRoot() + .getValueByString(PhysicsSettings) + .getValueByIndex(physicsSettingIndex) + .getValueByString(Vertices) + .getValueByIndex(vertexIndex) + .getValueByString(Acceleration) + .toFloat(); + } + + /** + * 物理点の距離の取得 + * @param physicsSettingIndex 物理演算の設定のインデックス + * @param vertexIndex 物理点のインデックス + * @return 物理点の距離 + */ + public getParticleRadius( + physicsSettingIndex: number, + vertexIndex: number + ): number { + return this._json + .getRoot() + .getValueByString(PhysicsSettings) + .getValueByIndex(physicsSettingIndex) + .getValueByString(Vertices) + .getValueByIndex(vertexIndex) + .getValueByString(Radius) + .toFloat(); + } + + /** + * 物理点の位置の取得 + * @param physicsSettingIndex 物理演算の設定のインデックス + * @param vertexInde 物理点のインデックス + * @return 物理点の位置 + */ + public getParticlePosition( + physicsSettingIndex: number, + vertexIndex: number + ): CubismVector2 { + const ret: CubismVector2 = new CubismVector2(0, 0); + ret.x = this._json + .getRoot() + .getValueByString(PhysicsSettings) + .getValueByIndex(physicsSettingIndex) + .getValueByString(Vertices) + .getValueByIndex(vertexIndex) + .getValueByString(Position) + .getValueByString(X) + .toFloat(); + ret.y = this._json + .getRoot() + .getValueByString(PhysicsSettings) + .getValueByIndex(physicsSettingIndex) + .getValueByString(Vertices) + .getValueByIndex(vertexIndex) + .getValueByString(Position) + .getValueByString(Y) + .toFloat(); + return ret; + } + + _json: CubismJson; // physics3.jsonデータ +} + +// Namespace definition for compatibility. +import * as $ from './cubismphysicsjson'; +// eslint-disable-next-line @typescript-eslint/no-namespace +export namespace Live2DCubismFramework { + export const CubismPhysicsJson = $.CubismPhysicsJson; + export type CubismPhysicsJson = $.CubismPhysicsJson; } diff --git a/src/rendering/cubismrenderer.ts b/src/rendering/cubismrenderer.ts index 3bed02a..c69aaad 100644 --- a/src/rendering/cubismrenderer.ts +++ b/src/rendering/cubismrenderer.ts @@ -5,263 +5,271 @@ * that can be found at https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html. */ -import { Live2DCubismFramework as cubismmatrix44 } from '../math/cubismmatrix44'; -import { Live2DCubismFramework as cubismmodel } from '../model/cubismmodel'; -import CubismModel = cubismmodel.CubismModel; -import CubismMatrix44 = cubismmatrix44.CubismMatrix44; +import { CubismMatrix44 } from '../math/cubismmatrix44'; +import { CubismModel } from '../model/cubismmodel'; -export namespace Live2DCubismFramework { +/** + * モデル描画を処理するレンダラ + * + * サブクラスに環境依存の描画命令を記述する。 + */ +export abstract class CubismRenderer { /** - * モデル描画を処理するレンダラ + * レンダラのインスタンスを生成して取得する * - * サブクラスに環境依存の描画命令を記述する。 + * @return レンダラのインスタンス */ - export abstract class CubismRenderer { - /** - * レンダラのインスタンスを生成して取得する - * - * @return レンダラのインスタンス - */ - public static create(): CubismRenderer { - return null; - } - - /** - * レンダラのインスタンスを解放する - */ - public static delete(renderer: CubismRenderer): void { - renderer = null; - } - - /** - * レンダラの初期化処理を実行する - * 引数に渡したモデルからレンダラの初期化処理に必要な情報を取り出すことができる - * @param model モデルのインスタンス - */ - public initialize(model: CubismModel): void { - this._model = model; - } - - /** - * モデルを描画する - */ - public drawModel(): void { - if (this.getModel() == null) return; - - this.doDrawModel(); - } - - /** - * Model-View-Projection 行列をセットする - * 配列は複製されるので、元の配列は外で破棄して良い - * @param matrix44 Model-View-Projection 行列 - */ - public setMvpMatrix(matrix44: CubismMatrix44): void { - this._mvpMatrix4x4.setMatrix(matrix44.getArray()); - } - - /** - * Model-View-Projection 行列を取得する - * @return Model-View-Projection 行列 - */ - public getMvpMatrix(): CubismMatrix44 { - return this._mvpMatrix4x4; - } - - /** - * モデルの色をセットする - * 各色0.0~1.0の間で指定する(1.0が標準の状態) - * @param red 赤チャンネルの値 - * @param green 緑チャンネルの値 - * @param blue 青チャンネルの値 - * @param alpha αチャンネルの値 - */ - public setModelColor( - red: number, - green: number, - blue: number, - alpha: number - ): void { - if (red < 0.0) { - red = 0.0; - } else if (red > 1.0) { - red = 1.0; - } - - if (green < 0.0) { - green = 0.0; - } else if (green > 1.0) { - green = 1.0; - } - - if (blue < 0.0) { - blue = 0.0; - } else if (blue > 1.0) { - blue = 1.0; - } - - if (alpha < 0.0) { - alpha = 0.0; - } else if (alpha > 1.0) { - alpha = 1.0; - } - - this._modelColor.R = red; - this._modelColor.G = green; - this._modelColor.B = blue; - this._modelColor.A = alpha; - } - - /** - * モデルの色を取得する - * 各色0.0~1.0の間で指定する(1.0が標準の状態) - * - * @return RGBAのカラー情報 - */ - public getModelColor(): CubismTextureColor { - return JSON.parse(JSON.stringify(this._modelColor)); - } - - /** - * 乗算済みαの有効・無効をセットする - * 有効にするならtrue、無効にするならfalseをセットする - */ - public setIsPremultipliedAlpha(enable: boolean): void { - this._isPremultipliedAlpha = enable; - } - - /** - * 乗算済みαの有効・無効を取得する - * @return true 乗算済みのα有効 - * @return false 乗算済みのα無効 - */ - public isPremultipliedAlpha(): boolean { - return this._isPremultipliedAlpha; - } - - /** - * カリング(片面描画)の有効・無効をセットする。 - * 有効にするならtrue、無効にするならfalseをセットする - */ - public setIsCulling(culling: boolean): void { - this._isCulling = culling; - } - - /** - * カリング(片面描画)の有効・無効を取得する。 - * @return true カリング有効 - * @return false カリング無効 - */ - public isCulling(): boolean { - return this._isCulling; - } - - /** - * テクスチャの異方性フィルタリングのパラメータをセットする - * パラメータ値の影響度はレンダラの実装に依存する - * @param n パラメータの値 - */ - public setAnisotropy(n: number): void { - this._anisortopy = n; - } - - /** - * テクスチャの異方性フィルタリングのパラメータをセットする - * @return 異方性フィルタリングのパラメータ - */ - public getAnisotropy(): number { - return this._anisortopy; - } - - /** - * レンダリングするモデルを取得する - * @return レンダリングするモデル - */ - public getModel(): CubismModel { - return this._model; - } - - /** - * コンストラクタ - */ - protected constructor() { - this._isCulling = false; - this._isPremultipliedAlpha = false; - this._anisortopy = 0.0; - this._model = null; - this._modelColor = new CubismTextureColor(); - - // 単位行列に初期化 - this._mvpMatrix4x4 = new CubismMatrix44(); - this._mvpMatrix4x4.loadIdentity(); - } - - /** - * モデル描画の実装 - */ - public abstract doDrawModel(): void; - - /** - * 描画オブジェクト(アートメッシュ)を描画する - * ポリゴンメッシュとテクスチャ番号をセットで渡す。 - * @param textureNo 描画するテクスチャ番号 - * @param indexCount 描画オブジェクトのインデックス値 - * @param vertexCount ポリゴンメッシュの頂点数 - * @param indexArray ポリゴンメッシュ頂点のインデックス配列 - * @param vertexArray ポリゴンメッシュの頂点配列 - * @param uvArray uv配列 - * @param opacity 不透明度 - * @param colorBlendMode カラーブレンディングのタイプ - * @param invertedMask マスク使用時のマスクの反転使用 - */ - public abstract drawMesh( - textureNo: number, - indexCount: number, - vertexCount: number, - indexArray: Uint16Array, - vertexArray: Float32Array, - uvArray: Float32Array, - opacity: number, - colorBlendMode: CubismBlendMode, - invertedMask: boolean - ): void; - - /** - * レンダラが保持する静的なリソースを開放する - */ - public static staticRelease: Function; - - protected _mvpMatrix4x4: CubismMatrix44; // Model-View-Projection 行列 - protected _modelColor: CubismTextureColor; // モデル自体のカラー(RGBA) - protected _isCulling: boolean; // カリングが有効ならtrue - protected _isPremultipliedAlpha: boolean; // 乗算済みαならtrue - protected _anisortopy: any; // テクスチャの異方性フィルタリングのパラメータ - protected _model: CubismModel; // レンダリング対象のモデル - } - - export enum CubismBlendMode { - CubismBlendMode_Normal = 0, // 通常 - CubismBlendMode_Additive = 1, // 加算 - CubismBlendMode_Multiplicative = 2 // 乗算 + public static create(): CubismRenderer { + return null; } /** - * テクスチャの色をRGBAで扱うためのクラス + * レンダラのインスタンスを解放する */ - export class CubismTextureColor { - /** - * コンストラクタ - */ - constructor() { - this.R = 1.0; - this.G = 1.0; - this.B = 1.0; - this.A = 1.0; + public static delete(renderer: CubismRenderer): void { + renderer = null; + } + + /** + * レンダラの初期化処理を実行する + * 引数に渡したモデルからレンダラの初期化処理に必要な情報を取り出すことができる + * @param model モデルのインスタンス + */ + public initialize(model: CubismModel): void { + this._model = model; + } + + /** + * モデルを描画する + */ + public drawModel(): void { + if (this.getModel() == null) return; + + this.doDrawModel(); + } + + /** + * Model-View-Projection 行列をセットする + * 配列は複製されるので、元の配列は外で破棄して良い + * @param matrix44 Model-View-Projection 行列 + */ + public setMvpMatrix(matrix44: CubismMatrix44): void { + this._mvpMatrix4x4.setMatrix(matrix44.getArray()); + } + + /** + * Model-View-Projection 行列を取得する + * @return Model-View-Projection 行列 + */ + public getMvpMatrix(): CubismMatrix44 { + return this._mvpMatrix4x4; + } + + /** + * モデルの色をセットする + * 各色0.0~1.0の間で指定する(1.0が標準の状態) + * @param red 赤チャンネルの値 + * @param green 緑チャンネルの値 + * @param blue 青チャンネルの値 + * @param alpha αチャンネルの値 + */ + public setModelColor( + red: number, + green: number, + blue: number, + alpha: number + ): void { + if (red < 0.0) { + red = 0.0; + } else if (red > 1.0) { + red = 1.0; } - R: number; // 赤チャンネル - G: number; // 緑チャンネル - B: number; // 青チャンネル - A: number; // αチャンネル + if (green < 0.0) { + green = 0.0; + } else if (green > 1.0) { + green = 1.0; + } + + if (blue < 0.0) { + blue = 0.0; + } else if (blue > 1.0) { + blue = 1.0; + } + + if (alpha < 0.0) { + alpha = 0.0; + } else if (alpha > 1.0) { + alpha = 1.0; + } + + this._modelColor.R = red; + this._modelColor.G = green; + this._modelColor.B = blue; + this._modelColor.A = alpha; } + + /** + * モデルの色を取得する + * 各色0.0~1.0の間で指定する(1.0が標準の状態) + * + * @return RGBAのカラー情報 + */ + public getModelColor(): CubismTextureColor { + return JSON.parse(JSON.stringify(this._modelColor)); + } + + /** + * 乗算済みαの有効・無効をセットする + * 有効にするならtrue、無効にするならfalseをセットする + */ + public setIsPremultipliedAlpha(enable: boolean): void { + this._isPremultipliedAlpha = enable; + } + + /** + * 乗算済みαの有効・無効を取得する + * @return true 乗算済みのα有効 + * @return false 乗算済みのα無効 + */ + public isPremultipliedAlpha(): boolean { + return this._isPremultipliedAlpha; + } + + /** + * カリング(片面描画)の有効・無効をセットする。 + * 有効にするならtrue、無効にするならfalseをセットする + */ + public setIsCulling(culling: boolean): void { + this._isCulling = culling; + } + + /** + * カリング(片面描画)の有効・無効を取得する。 + * @return true カリング有効 + * @return false カリング無効 + */ + public isCulling(): boolean { + return this._isCulling; + } + + /** + * テクスチャの異方性フィルタリングのパラメータをセットする + * パラメータ値の影響度はレンダラの実装に依存する + * @param n パラメータの値 + */ + public setAnisotropy(n: number): void { + this._anisortopy = n; + } + + /** + * テクスチャの異方性フィルタリングのパラメータをセットする + * @return 異方性フィルタリングのパラメータ + */ + public getAnisotropy(): number { + return this._anisortopy; + } + + /** + * レンダリングするモデルを取得する + * @return レンダリングするモデル + */ + public getModel(): CubismModel { + return this._model; + } + + /** + * コンストラクタ + */ + protected constructor() { + this._isCulling = false; + this._isPremultipliedAlpha = false; + this._anisortopy = 0.0; + this._model = null; + this._modelColor = new CubismTextureColor(); + + // 単位行列に初期化 + this._mvpMatrix4x4 = new CubismMatrix44(); + this._mvpMatrix4x4.loadIdentity(); + } + + /** + * モデル描画の実装 + */ + public abstract doDrawModel(): void; + + /** + * 描画オブジェクト(アートメッシュ)を描画する + * ポリゴンメッシュとテクスチャ番号をセットで渡す。 + * @param textureNo 描画するテクスチャ番号 + * @param indexCount 描画オブジェクトのインデックス値 + * @param vertexCount ポリゴンメッシュの頂点数 + * @param indexArray ポリゴンメッシュ頂点のインデックス配列 + * @param vertexArray ポリゴンメッシュの頂点配列 + * @param uvArray uv配列 + * @param opacity 不透明度 + * @param colorBlendMode カラーブレンディングのタイプ + * @param invertedMask マスク使用時のマスクの反転使用 + */ + public abstract drawMesh( + textureNo: number, + indexCount: number, + vertexCount: number, + indexArray: Uint16Array, + vertexArray: Float32Array, + uvArray: Float32Array, + opacity: number, + colorBlendMode: CubismBlendMode, + invertedMask: boolean + ): void; + + /** + * レンダラが保持する静的なリソースを開放する + */ + public static staticRelease: Function; + + protected _mvpMatrix4x4: CubismMatrix44; // Model-View-Projection 行列 + protected _modelColor: CubismTextureColor; // モデル自体のカラー(RGBA) + protected _isCulling: boolean; // カリングが有効ならtrue + protected _isPremultipliedAlpha: boolean; // 乗算済みαならtrue + protected _anisortopy: any; // テクスチャの異方性フィルタリングのパラメータ + protected _model: CubismModel; // レンダリング対象のモデル +} + +export enum CubismBlendMode { + CubismBlendMode_Normal = 0, // 通常 + CubismBlendMode_Additive = 1, // 加算 + CubismBlendMode_Multiplicative = 2 // 乗算 +} + +/** + * テクスチャの色をRGBAで扱うためのクラス + */ +export class CubismTextureColor { + /** + * コンストラクタ + */ + constructor() { + this.R = 1.0; + this.G = 1.0; + this.B = 1.0; + this.A = 1.0; + } + + R: number; // 赤チャンネル + G: number; // 緑チャンネル + B: number; // 青チャンネル + A: number; // αチャンネル +} + +// Namespace definition for compatibility. +import * as $ from './cubismrenderer'; +// eslint-disable-next-line @typescript-eslint/no-namespace +export namespace Live2DCubismFramework { + export const CubismBlendMode = $.CubismBlendMode; + export type CubismBlendMode = $.CubismBlendMode; + export const CubismRenderer = $.CubismRenderer; + export type CubismRenderer = $.CubismRenderer; + export const CubismTextureColor = $.CubismTextureColor; + export type CubismTextureColor = $.CubismTextureColor; } diff --git a/src/rendering/cubismrenderer_webgl.ts b/src/rendering/cubismrenderer_webgl.ts index be11637..51290a1 100644 --- a/src/rendering/cubismrenderer_webgl.ts +++ b/src/rendering/cubismrenderer_webgl.ts @@ -5,984 +5,1103 @@ * that can be found at https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html. */ -import { Live2DCubismFramework as cubismframework } from '../live2dcubismframework'; -import { Live2DCubismFramework as csmrect } from '../type/csmrectf'; -import { Live2DCubismFramework as cubismrenderer } from './cubismrenderer'; -import { Live2DCubismFramework as cubismmodel } from '../model/cubismmodel'; -import { Live2DCubismFramework as cubsimmatrix44 } from '../math/cubismmatrix44'; -import { Live2DCubismFramework as csmmap } from '../type/csmmap'; -import { Live2DCubismFramework as csmvector } from '../type/csmvector'; +import { Constant } from '../live2dcubismframework'; +import { CubismMatrix44 } from '../math/cubismmatrix44'; +import { CubismModel } from '../model/cubismmodel'; +import { csmMap } from '../type/csmmap'; +import { csmRect } from '../type/csmrectf'; +import { csmVector } from '../type/csmvector'; import { CubismLogError } from '../utils/cubismdebug'; -import Constant = cubismframework.Constant; -import CubismMatrix44 = cubsimmatrix44.CubismMatrix44; -import csmRect = csmrect.csmRect; -import csmMap = csmmap.csmMap; -import csmVector = csmvector.csmVector; -import CubismModel = cubismmodel.CubismModel; -import CubismRenderer = cubismrenderer.CubismRenderer; -import CubismBlendMode = cubismrenderer.CubismBlendMode; -import CubismTextureColor = cubismrenderer.CubismTextureColor; +import { + CubismBlendMode, + CubismRenderer, + CubismTextureColor +} from './cubismrenderer'; -export namespace Live2DCubismFramework { - const ColorChannelCount = 4; // 実験時に1チャンネルの場合は1、RGBだけの場合は3、アルファも含める場合は4 +const ColorChannelCount = 4; // 実験時に1チャンネルの場合は1、RGBだけの場合は3、アルファも含める場合は4 - const shaderCount = 10; // シェーダーの数 = マスク生成用 + (通常用 + 加算 + 乗算) * (マスク無の乗算済アルファ対応版 + マスク有の乗算済アルファ対応版 + マスク有反転の乗算済アルファ対応版) - let s_instance: CubismShader_WebGL; - let s_viewport: number[]; - let s_fbo: WebGLFramebuffer; +const shaderCount = 10; // シェーダーの数 = マスク生成用 + (通常用 + 加算 + 乗算) * (マスク無の乗算済アルファ対応版 + マスク有の乗算済アルファ対応版 + マスク有反転の乗算済アルファ対応版) +let s_instance: CubismShader_WebGL; +let s_viewport: number[]; +let s_fbo: WebGLFramebuffer; + +/** + * クリッピングマスクの処理を実行するクラス + */ +export class CubismClippingManager_WebGL { + /** + * カラーチャンネル(RGBA)のフラグを取得する + * @param channelNo カラーチャンネル(RGBA)の番号(0:R, 1:G, 2:B, 3:A) + */ + public getChannelFlagAsColor(channelNo: number): CubismTextureColor { + return this._channelColors.at(channelNo); + } /** - * クリッピングマスクの処理を実行するクラス + * テンポラリのレンダーテクスチャのアドレスを取得する + * FrameBufferObjectが存在しない場合、新しく生成する + * + * @return レンダーテクスチャのアドレス */ - export class CubismClippingManager_WebGL { - /** - * カラーチャンネル(RGBA)のフラグを取得する - * @param channelNo カラーチャンネル(RGBA)の番号(0:R, 1:G, 2:B, 3:A) - */ - public getChannelFlagAsColor(channelNo: number): CubismTextureColor { - return this._channelColors.at(channelNo); + public getMaskRenderTexture(): WebGLFramebuffer { + let ret: WebGLFramebuffer = 0; + + // テンポラリのRenderTextureを取得する + if (this._maskTexture && this._maskTexture.texture != 0) { + // 前回使ったものを返す + this._maskTexture.frameNo = this._currentFrameNo; + ret = this._maskTexture.texture; } - /** - * テンポラリのレンダーテクスチャのアドレスを取得する - * FrameBufferObjectが存在しない場合、新しく生成する - * - * @return レンダーテクスチャのアドレス - */ - public getMaskRenderTexture(): WebGLFramebuffer { - let ret: WebGLFramebuffer = 0; + if (ret == 0) { + // FrameBufferObjectが存在しない場合、新しく生成する - // テンポラリのRenderTextureを取得する - if (this._maskTexture && this._maskTexture.texture != 0) { - // 前回使ったものを返す - this._maskTexture.frameNo = this._currentFrameNo; - ret = this._maskTexture.texture; - } + // クリッピングバッファサイズを取得 + const size: number = this._clippingMaskBufferSize; - if (ret == 0) { - // FrameBufferObjectが存在しない場合、新しく生成する + this._colorBuffer = this.gl.createTexture(); + this.gl.bindTexture(this.gl.TEXTURE_2D, this._colorBuffer); + this.gl.texImage2D( + this.gl.TEXTURE_2D, + 0, + this.gl.RGBA, + size, + size, + 0, + this.gl.RGBA, + this.gl.UNSIGNED_BYTE, + null + ); + this.gl.texParameteri( + this.gl.TEXTURE_2D, + this.gl.TEXTURE_WRAP_S, + this.gl.CLAMP_TO_EDGE + ); + this.gl.texParameteri( + this.gl.TEXTURE_2D, + this.gl.TEXTURE_WRAP_T, + this.gl.CLAMP_TO_EDGE + ); + this.gl.texParameteri( + this.gl.TEXTURE_2D, + this.gl.TEXTURE_MIN_FILTER, + this.gl.LINEAR + ); + this.gl.texParameteri( + this.gl.TEXTURE_2D, + this.gl.TEXTURE_MAG_FILTER, + this.gl.LINEAR + ); + this.gl.bindTexture(this.gl.TEXTURE_2D, null); - // クリッピングバッファサイズを取得 - const size: number = this._clippingMaskBufferSize; + ret = this.gl.createFramebuffer(); + this.gl.bindFramebuffer(this.gl.FRAMEBUFFER, ret); + this.gl.framebufferTexture2D( + this.gl.FRAMEBUFFER, + this.gl.COLOR_ATTACHMENT0, + this.gl.TEXTURE_2D, + this._colorBuffer, + 0 + ); + this.gl.bindFramebuffer(this.gl.FRAMEBUFFER, s_fbo); - this._colorBuffer = this.gl.createTexture(); - this.gl.bindTexture(this.gl.TEXTURE_2D, this._colorBuffer); - this.gl.texImage2D( - this.gl.TEXTURE_2D, - 0, - this.gl.RGBA, - size, - size, - 0, - this.gl.RGBA, - this.gl.UNSIGNED_BYTE, - null - ); - this.gl.texParameteri( - this.gl.TEXTURE_2D, - this.gl.TEXTURE_WRAP_S, - this.gl.CLAMP_TO_EDGE - ); - this.gl.texParameteri( - this.gl.TEXTURE_2D, - this.gl.TEXTURE_WRAP_T, - this.gl.CLAMP_TO_EDGE - ); - this.gl.texParameteri( - this.gl.TEXTURE_2D, - this.gl.TEXTURE_MIN_FILTER, - this.gl.LINEAR - ); - this.gl.texParameteri( - this.gl.TEXTURE_2D, - this.gl.TEXTURE_MAG_FILTER, - this.gl.LINEAR - ); - this.gl.bindTexture(this.gl.TEXTURE_2D, null); - - ret = this.gl.createFramebuffer(); - this.gl.bindFramebuffer(this.gl.FRAMEBUFFER, ret); - this.gl.framebufferTexture2D( - this.gl.FRAMEBUFFER, - this.gl.COLOR_ATTACHMENT0, - this.gl.TEXTURE_2D, - this._colorBuffer, - 0 - ); - this.gl.bindFramebuffer(this.gl.FRAMEBUFFER, s_fbo); - - this._maskTexture = new CubismRenderTextureResource( - this._currentFrameNo, - ret - ); - } - - return ret; + this._maskTexture = new CubismRenderTextureResource( + this._currentFrameNo, + ret + ); } - /** - * WebGLレンダリングコンテキストを設定する - * @param gl WebGLレンダリングコンテキスト - */ - public setGL(gl: WebGLRenderingContext): void { - this.gl = gl; - } + return ret; + } - /** - * マスクされる描画オブジェクト群全体を囲む矩形(モデル座標系)を計算する - * @param model モデルのインスタンス - * @param clippingContext クリッピングマスクのコンテキスト - */ - public calcClippedDrawTotalBounds( - model: CubismModel, - clippingContext: CubismClippingContext - ): void { - // 被クリッピングマスク(マスクされる描画オブジェクト)の全体の矩形 - let clippedDrawTotalMinX: number = Number.MAX_VALUE; - let clippedDrawTotalMinY: number = Number.MAX_VALUE; - let clippedDrawTotalMaxX: number = Number.MIN_VALUE; - let clippedDrawTotalMaxY: number = Number.MIN_VALUE; + /** + * WebGLレンダリングコンテキストを設定する + * @param gl WebGLレンダリングコンテキスト + */ + public setGL(gl: WebGLRenderingContext): void { + this.gl = gl; + } - // このマスクが実際に必要か判定する - // このクリッピングを利用する「描画オブジェクト」がひとつでも使用可能であればマスクを生成する必要がある - const clippedDrawCount: number = - clippingContext._clippedDrawableIndexList.length; + /** + * マスクされる描画オブジェクト群全体を囲む矩形(モデル座標系)を計算する + * @param model モデルのインスタンス + * @param clippingContext クリッピングマスクのコンテキスト + */ + public calcClippedDrawTotalBounds( + model: CubismModel, + clippingContext: CubismClippingContext + ): void { + // 被クリッピングマスク(マスクされる描画オブジェクト)の全体の矩形 + let clippedDrawTotalMinX: number = Number.MAX_VALUE; + let clippedDrawTotalMinY: number = Number.MAX_VALUE; + let clippedDrawTotalMaxX: number = Number.MIN_VALUE; + let clippedDrawTotalMaxY: number = Number.MIN_VALUE; + // このマスクが実際に必要か判定する + // このクリッピングを利用する「描画オブジェクト」がひとつでも使用可能であればマスクを生成する必要がある + const clippedDrawCount: number = + clippingContext._clippedDrawableIndexList.length; + + for ( + let clippedDrawableIndex = 0; + clippedDrawableIndex < clippedDrawCount; + clippedDrawableIndex++ + ) { + // マスクを使用する描画オブジェクトの描画される矩形を求める + const drawableIndex: number = + clippingContext._clippedDrawableIndexList[clippedDrawableIndex]; + + const drawableVertexCount: number = model.getDrawableVertexCount( + drawableIndex + ); + const drawableVertexes: Float32Array = model.getDrawableVertices( + drawableIndex + ); + + let minX: number = Number.MAX_VALUE; + let minY: number = Number.MAX_VALUE; + let maxX: number = Number.MIN_VALUE; + let maxY: number = Number.MIN_VALUE; + + const loop: number = drawableVertexCount * Constant.vertexStep; for ( - let clippedDrawableIndex = 0; - clippedDrawableIndex < clippedDrawCount; - clippedDrawableIndex++ + let pi: number = Constant.vertexOffset; + pi < loop; + pi += Constant.vertexStep ) { - // マスクを使用する描画オブジェクトの描画される矩形を求める - const drawableIndex: number = - clippingContext._clippedDrawableIndexList[clippedDrawableIndex]; + const x: number = drawableVertexes[pi]; + const y: number = drawableVertexes[pi + 1]; - const drawableVertexCount: number = model.getDrawableVertexCount( - drawableIndex - ); - const drawableVertexes: Float32Array = model.getDrawableVertices( - drawableIndex - ); - - let minX: number = Number.MAX_VALUE; - let minY: number = Number.MAX_VALUE; - let maxX: number = Number.MIN_VALUE; - let maxY: number = Number.MIN_VALUE; - - const loop: number = drawableVertexCount * Constant.vertexStep; - for ( - let pi: number = Constant.vertexOffset; - pi < loop; - pi += Constant.vertexStep - ) { - const x: number = drawableVertexes[pi]; - const y: number = drawableVertexes[pi + 1]; - - if (x < minX) { - minX = x; - } - if (x > maxX) { - maxX = x; - } - if (y < minY) { - minY = y; - } - if (y > maxY) { - maxY = y; - } + if (x < minX) { + minX = x; } - - // 有効な点が一つも取れなかったのでスキップ - if (minX == Number.MAX_VALUE) { - continue; + if (x > maxX) { + maxX = x; } - - // 全体の矩形に反映 - if (minX < clippedDrawTotalMinX) { - clippedDrawTotalMinX = minX; + if (y < minY) { + minY = y; } - if (minY < clippedDrawTotalMinY) { - clippedDrawTotalMinY = minY; - } - if (maxX > clippedDrawTotalMaxX) { - clippedDrawTotalMaxX = maxX; - } - if (maxY > clippedDrawTotalMaxY) { - clippedDrawTotalMaxY = maxY; - } - - if (clippedDrawTotalMinX == Number.MAX_VALUE) { - clippingContext._allClippedDrawRect.x = 0.0; - clippingContext._allClippedDrawRect.y = 0.0; - clippingContext._allClippedDrawRect.width = 0.0; - clippingContext._allClippedDrawRect.height = 0.0; - clippingContext._isUsing = false; - } else { - clippingContext._isUsing = true; - const w: number = clippedDrawTotalMaxX - clippedDrawTotalMinX; - const h: number = clippedDrawTotalMaxY - clippedDrawTotalMinY; - clippingContext._allClippedDrawRect.x = clippedDrawTotalMinX; - clippingContext._allClippedDrawRect.y = clippedDrawTotalMinY; - clippingContext._allClippedDrawRect.width = w; - clippingContext._allClippedDrawRect.height = h; + if (y > maxY) { + maxY = y; } } - } - /** - * コンストラクタ - */ - public constructor() { - this._maskRenderTexture = null; - this._colorBuffer = null; - this._currentFrameNo = 0; - this._clippingMaskBufferSize = 256; - this._clippingContextListForMask = new csmVector(); - this._clippingContextListForDraw = new csmVector(); - this._channelColors = new csmVector(); - this._tmpBoundsOnModel = new csmRect(); - this._tmpMatrix = new CubismMatrix44(); - this._tmpMatrixForMask = new CubismMatrix44(); - this._tmpMatrixForDraw = new CubismMatrix44(); + // 有効な点が一つも取れなかったのでスキップ + if (minX == Number.MAX_VALUE) { + continue; + } + + // 全体の矩形に反映 + if (minX < clippedDrawTotalMinX) { + clippedDrawTotalMinX = minX; + } + if (minY < clippedDrawTotalMinY) { + clippedDrawTotalMinY = minY; + } + if (maxX > clippedDrawTotalMaxX) { + clippedDrawTotalMaxX = maxX; + } + if (maxY > clippedDrawTotalMaxY) { + clippedDrawTotalMaxY = maxY; + } + + if (clippedDrawTotalMinX == Number.MAX_VALUE) { + clippingContext._allClippedDrawRect.x = 0.0; + clippingContext._allClippedDrawRect.y = 0.0; + clippingContext._allClippedDrawRect.width = 0.0; + clippingContext._allClippedDrawRect.height = 0.0; + clippingContext._isUsing = false; + } else { + clippingContext._isUsing = true; + const w: number = clippedDrawTotalMaxX - clippedDrawTotalMinX; + const h: number = clippedDrawTotalMaxY - clippedDrawTotalMinY; + clippingContext._allClippedDrawRect.x = clippedDrawTotalMinX; + clippingContext._allClippedDrawRect.y = clippedDrawTotalMinY; + clippingContext._allClippedDrawRect.width = w; + clippingContext._allClippedDrawRect.height = h; + } + } + } + + /** + * コンストラクタ + */ + public constructor() { + this._maskRenderTexture = null; + this._colorBuffer = null; + this._currentFrameNo = 0; + this._clippingMaskBufferSize = 256; + this._clippingContextListForMask = new csmVector(); + this._clippingContextListForDraw = new csmVector(); + this._channelColors = new csmVector(); + this._tmpBoundsOnModel = new csmRect(); + this._tmpMatrix = new CubismMatrix44(); + this._tmpMatrixForMask = new CubismMatrix44(); + this._tmpMatrixForDraw = new CubismMatrix44(); + this._maskTexture = null; + + let tmp: CubismTextureColor = new CubismTextureColor(); + tmp.R = 1.0; + tmp.G = 0.0; + tmp.B = 0.0; + tmp.A = 0.0; + this._channelColors.pushBack(tmp); + + tmp = new CubismTextureColor(); + tmp.R = 0.0; + tmp.G = 1.0; + tmp.B = 0.0; + tmp.A = 0.0; + this._channelColors.pushBack(tmp); + + tmp = new CubismTextureColor(); + tmp.R = 0.0; + tmp.G = 0.0; + tmp.B = 1.0; + tmp.A = 0.0; + this._channelColors.pushBack(tmp); + + tmp = new CubismTextureColor(); + tmp.R = 0.0; + tmp.G = 0.0; + tmp.B = 0.0; + tmp.A = 1.0; + this._channelColors.pushBack(tmp); + } + + /** + * デストラクタ相当の処理 + */ + public release(): void { + for (let i = 0; i < this._clippingContextListForMask.getSize(); i++) { + if (this._clippingContextListForMask.at(i)) { + this._clippingContextListForMask.at(i).release(); + this._clippingContextListForMask.set(i, void 0); + } + this._clippingContextListForMask.set(i, null); + } + this._clippingContextListForMask = null; + + // _clippingContextListForDrawは_clippingContextListForMaskにあるインスタンスを指している。上記の処理により要素ごとのDELETEは不要。 + for (let i = 0; i < this._clippingContextListForDraw.getSize(); i++) { + this._clippingContextListForDraw.set(i, null); + } + this._clippingContextListForDraw = null; + + if (this._maskTexture) { + this.gl.deleteFramebuffer(this._maskTexture.texture); this._maskTexture = null; - - let tmp: CubismTextureColor = new CubismTextureColor(); - tmp.R = 1.0; - tmp.G = 0.0; - tmp.B = 0.0; - tmp.A = 0.0; - this._channelColors.pushBack(tmp); - - tmp = new CubismTextureColor(); - tmp.R = 0.0; - tmp.G = 1.0; - tmp.B = 0.0; - tmp.A = 0.0; - this._channelColors.pushBack(tmp); - - tmp = new CubismTextureColor(); - tmp.R = 0.0; - tmp.G = 0.0; - tmp.B = 1.0; - tmp.A = 0.0; - this._channelColors.pushBack(tmp); - - tmp = new CubismTextureColor(); - tmp.R = 0.0; - tmp.G = 0.0; - tmp.B = 0.0; - tmp.A = 1.0; - this._channelColors.pushBack(tmp); } - /** - * デストラクタ相当の処理 - */ - public release(): void { - for (let i = 0; i < this._clippingContextListForMask.getSize(); i++) { - if (this._clippingContextListForMask.at(i)) { - this._clippingContextListForMask.at(i).release(); - this._clippingContextListForMask.set(i, void 0); - } - this._clippingContextListForMask.set(i, null); - } - this._clippingContextListForMask = null; - - // _clippingContextListForDrawは_clippingContextListForMaskにあるインスタンスを指している。上記の処理により要素ごとのDELETEは不要。 - for (let i = 0; i < this._clippingContextListForDraw.getSize(); i++) { - this._clippingContextListForDraw.set(i, null); - } - this._clippingContextListForDraw = null; - - if (this._maskTexture) { - this.gl.deleteFramebuffer(this._maskTexture.texture); - this._maskTexture = null; - } - - for (let i = 0; i < this._channelColors.getSize(); i++) { - this._channelColors.set(i, null); - } - - this._channelColors = null; - - // テクスチャ解放 - this.gl.deleteTexture(this._colorBuffer); - this._colorBuffer = null; + for (let i = 0; i < this._channelColors.getSize(); i++) { + this._channelColors.set(i, null); } - /** - * マネージャの初期化処理 - * クリッピングマスクを使う描画オブジェクトの登録を行う - * @param model モデルのインスタンス - * @param drawableCount 描画オブジェクトの数 - * @param drawableMasks 描画オブジェクトをマスクする描画オブジェクトのインデックスのリスト - * @param drawableCounts 描画オブジェクトをマスクする描画オブジェクトの数 - */ - public initialize( - model: CubismModel, - drawableCount: number, - drawableMasks: Int32Array[], - drawableMaskCounts: Int32Array - ): void { - // クリッピングマスクを使う描画オブジェクトをすべて登録する - // クリッピングマスクは、通常数個程度に限定して使うものとする - for (let i = 0; i < drawableCount; i++) { - if (drawableMaskCounts[i] <= 0) { - // クリッピングマスクが使用されていないアートメッシュ(多くの場合使用しない) - this._clippingContextListForDraw.pushBack(null); - continue; - } + this._channelColors = null; - // 既にあるClipContextと同じかチェックする - let clippingContext: CubismClippingContext = this.findSameClip( + // テクスチャ解放 + this.gl.deleteTexture(this._colorBuffer); + this._colorBuffer = null; + } + + /** + * マネージャの初期化処理 + * クリッピングマスクを使う描画オブジェクトの登録を行う + * @param model モデルのインスタンス + * @param drawableCount 描画オブジェクトの数 + * @param drawableMasks 描画オブジェクトをマスクする描画オブジェクトのインデックスのリスト + * @param drawableCounts 描画オブジェクトをマスクする描画オブジェクトの数 + */ + public initialize( + model: CubismModel, + drawableCount: number, + drawableMasks: Int32Array[], + drawableMaskCounts: Int32Array + ): void { + // クリッピングマスクを使う描画オブジェクトをすべて登録する + // クリッピングマスクは、通常数個程度に限定して使うものとする + for (let i = 0; i < drawableCount; i++) { + if (drawableMaskCounts[i] <= 0) { + // クリッピングマスクが使用されていないアートメッシュ(多くの場合使用しない) + this._clippingContextListForDraw.pushBack(null); + continue; + } + + // 既にあるClipContextと同じかチェックする + let clippingContext: CubismClippingContext = this.findSameClip( + drawableMasks[i], + drawableMaskCounts[i] + ); + if (clippingContext == null) { + // 同一のマスクが存在していない場合は生成する + clippingContext = new CubismClippingContext( + this, drawableMasks[i], drawableMaskCounts[i] ); - if (clippingContext == null) { - // 同一のマスクが存在していない場合は生成する - clippingContext = new CubismClippingContext( - this, - drawableMasks[i], - drawableMaskCounts[i] - ); - this._clippingContextListForMask.pushBack(clippingContext); - } + this._clippingContextListForMask.pushBack(clippingContext); + } - clippingContext.addClippedDrawable(i); + clippingContext.addClippedDrawable(i); - this._clippingContextListForDraw.pushBack(clippingContext); + this._clippingContextListForDraw.pushBack(clippingContext); + } + } + + /** + * クリッピングコンテキストを作成する。モデル描画時に実行する。 + * @param model モデルのインスタンス + * @param renderer レンダラのインスタンス + */ + public setupClippingContext( + model: CubismModel, + renderer: CubismRenderer_WebGL + ): void { + this._currentFrameNo++; + + // 全てのクリッピングを用意する + // 同じクリップ(複数の場合はまとめて一つのクリップ)を使う場合は1度だけ設定する + let usingClipCount = 0; + for ( + let clipIndex = 0; + clipIndex < this._clippingContextListForMask.getSize(); + clipIndex++ + ) { + // 1つのクリッピングマスクに関して + const cc: CubismClippingContext = this._clippingContextListForMask.at( + clipIndex + ); + + // このクリップを利用する描画オブジェクト群全体を囲む矩形を計算 + this.calcClippedDrawTotalBounds(model, cc); + + if (cc._isUsing) { + usingClipCount++; // 使用中としてカウント } } - /** - * クリッピングコンテキストを作成する。モデル描画時に実行する。 - * @param model モデルのインスタンス - * @param renderer レンダラのインスタンス - */ - public setupClippingContext( - model: CubismModel, - renderer: CubismRenderer_WebGL - ): void { - this._currentFrameNo++; + // マスク作成処理 + if (usingClipCount > 0) { + // 生成したFrameBufferと同じサイズでビューポートを設定 + this.gl.viewport( + 0, + 0, + this._clippingMaskBufferSize, + this._clippingMaskBufferSize + ); - // 全てのクリッピングを用意する - // 同じクリップ(複数の場合はまとめて一つのクリップ)を使う場合は1度だけ設定する - let usingClipCount = 0; + // マスクをactiveにする + this._maskRenderTexture = this.getMaskRenderTexture(); + + // モデル描画時にDrawMeshNowに渡される変換(モデルtoワールド座標変換) + const modelToWorldF: CubismMatrix44 = renderer.getMvpMatrix(); + + renderer.preDraw(); // バッファをクリアする + + // 各マスクのレイアウトを決定していく + this.setupLayoutBounds(usingClipCount); + + // ---------- マスク描画処理 ---------- + // マスク用RenderTextureをactiveにセット + this.gl.bindFramebuffer(this.gl.FRAMEBUFFER, this._maskRenderTexture); + + // マスクをクリアする + // (仮仕様) 1が無効(描かれない)領域、0が有効(描かれる)領域。(シェーダーCd*Csで0に近い値をかけてマスクを作る。1をかけると何も起こらない) + this.gl.clearColor(1.0, 1.0, 1.0, 1.0); + this.gl.clear(this.gl.COLOR_BUFFER_BIT); + + // 実際にマスクを生成する + // 全てのマスクをどのようにレイアウトして描くかを決定し、ClipContext, ClippedDrawContextに記憶する for ( let clipIndex = 0; clipIndex < this._clippingContextListForMask.getSize(); clipIndex++ ) { - // 1つのクリッピングマスクに関して - const cc: CubismClippingContext = this._clippingContextListForMask.at( + // --- 実際に1つのマスクを描く --- + const clipContext: CubismClippingContext = this._clippingContextListForMask.at( clipIndex ); + const allClipedDrawRect: csmRect = clipContext._allClippedDrawRect; // このマスクを使う、すべての描画オブジェクトの論理座標上の囲み矩形 + const layoutBoundsOnTex01: csmRect = clipContext._layoutBounds; // この中にマスクを収める - // このクリップを利用する描画オブジェクト群全体を囲む矩形を計算 - this.calcClippedDrawTotalBounds(model, cc); + // モデル座標上の矩形を、適宜マージンを付けて使う + const MARGIN = 0.05; + this._tmpBoundsOnModel.setRect(allClipedDrawRect); + this._tmpBoundsOnModel.expand( + allClipedDrawRect.width * MARGIN, + allClipedDrawRect.height * MARGIN + ); + //########## 本来は割り当てられた領域の全体を使わず必要最低限のサイズがよい - if (cc._isUsing) { - usingClipCount++; // 使用中としてカウント + // シェーダ用の計算式を求める。回転を考慮しない場合は以下のとおり + // movePeriod' = movePeriod * scaleX + offX [[ movePeriod' = (movePeriod - tmpBoundsOnModel.movePeriod)*scale + layoutBoundsOnTex01.movePeriod ]] + const scaleX: number = + layoutBoundsOnTex01.width / this._tmpBoundsOnModel.width; + const scaleY: number = + layoutBoundsOnTex01.height / this._tmpBoundsOnModel.height; + + // マスク生成時に使う行列を求める + { + // シェーダに渡す行列を求める <<<<<<<<<<<<<<<<<<<<<<<< 要最適化(逆順に計算すればシンプルにできる) + this._tmpMatrix.loadIdentity(); + { + // layout0..1 を -1..1に変換 + this._tmpMatrix.translateRelative(-1.0, -1.0); + this._tmpMatrix.scaleRelative(2.0, 2.0); + } + { + // view to layout0..1 + this._tmpMatrix.translateRelative( + layoutBoundsOnTex01.x, + layoutBoundsOnTex01.y + ); + this._tmpMatrix.scaleRelative(scaleX, scaleY); // new = [translate][scale] + this._tmpMatrix.translateRelative( + -this._tmpBoundsOnModel.x, + -this._tmpBoundsOnModel.y + ); + // new = [translate][scale][translate] + } + // tmpMatrixForMaskが計算結果 + this._tmpMatrixForMask.setMatrix(this._tmpMatrix.getArray()); + } + + //--------- draw時の mask 参照用行列を計算 + { + // シェーダに渡す行列を求める <<<<<<<<<<<<<<<<<<<<<<<< 要最適化(逆順に計算すればシンプルにできる) + this._tmpMatrix.loadIdentity(); + { + this._tmpMatrix.translateRelative( + layoutBoundsOnTex01.x, + layoutBoundsOnTex01.y + ); + this._tmpMatrix.scaleRelative(scaleX, scaleY); // new = [translate][scale] + this._tmpMatrix.translateRelative( + -this._tmpBoundsOnModel.x, + -this._tmpBoundsOnModel.y + ); + // new = [translate][scale][translate] + } + this._tmpMatrixForDraw.setMatrix(this._tmpMatrix.getArray()); + } + clipContext._matrixForMask.setMatrix(this._tmpMatrixForMask.getArray()); + clipContext._matrixForDraw.setMatrix(this._tmpMatrixForDraw.getArray()); + + const clipDrawCount: number = clipContext._clippingIdCount; + for (let i = 0; i < clipDrawCount; i++) { + const clipDrawIndex: number = clipContext._clippingIdList[i]; + + // 頂点情報が更新されておらず、信頼性がない場合は描画をパスする + if ( + !model.getDrawableDynamicFlagVertexPositionsDidChange(clipDrawIndex) + ) { + continue; + } + + renderer.setIsCulling( + model.getDrawableCulling(clipDrawIndex) != false + ); + + // 今回専用の変換を適用して描く + // チャンネルも切り替える必要がある(A,R,G,B) + renderer.setClippingContextBufferForMask(clipContext); + renderer.drawMesh( + model.getDrawableTextureIndices(clipDrawIndex), + model.getDrawableVertexIndexCount(clipDrawIndex), + model.getDrawableVertexCount(clipDrawIndex), + model.getDrawableVertexIndices(clipDrawIndex), + model.getDrawableVertices(clipDrawIndex), + model.getDrawableVertexUvs(clipDrawIndex), + model.getDrawableOpacity(clipDrawIndex), + CubismBlendMode.CubismBlendMode_Normal, // クリッピングは通常描画を強制 + false // マスク生成時はクリッピングの反転使用は全く関係がない + ); } } - // マスク作成処理 - if (usingClipCount > 0) { - // 生成したFrameBufferと同じサイズでビューポートを設定 - this.gl.viewport( - 0, - 0, - this._clippingMaskBufferSize, - this._clippingMaskBufferSize - ); + // --- 後処理 --- + this.gl.bindFramebuffer(this.gl.FRAMEBUFFER, s_fbo); // 描画対象を戻す + renderer.setClippingContextBufferForMask(null); - // マスクをactiveにする - this._maskRenderTexture = this.getMaskRenderTexture(); + this.gl.viewport( + s_viewport[0], + s_viewport[1], + s_viewport[2], + s_viewport[3] + ); + } + } - // モデル描画時にDrawMeshNowに渡される変換(モデルtoワールド座標変換) - const modelToWorldF: CubismMatrix44 = renderer.getMvpMatrix(); + /** + * 既にマスクを作っているかを確認 + * 作っている様であれば該当するクリッピングマスクのインスタンスを返す + * 作っていなければNULLを返す + * @param drawableMasks 描画オブジェクトをマスクする描画オブジェクトのリスト + * @param drawableMaskCounts 描画オブジェクトをマスクする描画オブジェクトの数 + * @return 該当するクリッピングマスクが存在すればインスタンスを返し、なければNULLを返す + */ + public findSameClip( + drawableMasks: Int32Array, + drawableMaskCounts: number + ): CubismClippingContext { + // 作成済みClippingContextと一致するか確認 + for (let i = 0; i < this._clippingContextListForMask.getSize(); i++) { + const clippingContext: CubismClippingContext = this._clippingContextListForMask.at( + i + ); + const count: number = clippingContext._clippingIdCount; - renderer.preDraw(); // バッファをクリアする + // 個数が違う場合は別物 + if (count != drawableMaskCounts) { + continue; + } - // 各マスクのレイアウトを決定していく - this.setupLayoutBounds(usingClipCount); + let sameCount = 0; - // ---------- マスク描画処理 ---------- - // マスク用RenderTextureをactiveにセット - this.gl.bindFramebuffer(this.gl.FRAMEBUFFER, this._maskRenderTexture); + // 同じIDを持つか確認。配列の数が同じなので、一致した個数が同じなら同じ物を持つとする + for (let j = 0; j < count; j++) { + const clipId: number = clippingContext._clippingIdList[j]; - // マスクをクリアする - // (仮仕様) 1が無効(描かれない)領域、0が有効(描かれる)領域。(シェーダーCd*Csで0に近い値をかけてマスクを作る。1をかけると何も起こらない) - this.gl.clearColor(1.0, 1.0, 1.0, 1.0); - this.gl.clear(this.gl.COLOR_BUFFER_BIT); - - // 実際にマスクを生成する - // 全てのマスクをどのようにレイアウトして描くかを決定し、ClipContext, ClippedDrawContextに記憶する - for ( - let clipIndex = 0; - clipIndex < this._clippingContextListForMask.getSize(); - clipIndex++ - ) { - // --- 実際に1つのマスクを描く --- - const clipContext: CubismClippingContext = this._clippingContextListForMask.at( - clipIndex - ); - const allClipedDrawRect: csmRect = clipContext._allClippedDrawRect; // このマスクを使う、すべての描画オブジェクトの論理座標上の囲み矩形 - const layoutBoundsOnTex01: csmRect = clipContext._layoutBounds; // この中にマスクを収める - - // モデル座標上の矩形を、適宜マージンを付けて使う - const MARGIN = 0.05; - this._tmpBoundsOnModel.setRect(allClipedDrawRect); - this._tmpBoundsOnModel.expand( - allClipedDrawRect.width * MARGIN, - allClipedDrawRect.height * MARGIN - ); - //########## 本来は割り当てられた領域の全体を使わず必要最低限のサイズがよい - - // シェーダ用の計算式を求める。回転を考慮しない場合は以下のとおり - // movePeriod' = movePeriod * scaleX + offX [[ movePeriod' = (movePeriod - tmpBoundsOnModel.movePeriod)*scale + layoutBoundsOnTex01.movePeriod ]] - const scaleX: number = - layoutBoundsOnTex01.width / this._tmpBoundsOnModel.width; - const scaleY: number = - layoutBoundsOnTex01.height / this._tmpBoundsOnModel.height; - - // マスク生成時に使う行列を求める - { - // シェーダに渡す行列を求める <<<<<<<<<<<<<<<<<<<<<<<< 要最適化(逆順に計算すればシンプルにできる) - this._tmpMatrix.loadIdentity(); - { - // layout0..1 を -1..1に変換 - this._tmpMatrix.translateRelative(-1.0, -1.0); - this._tmpMatrix.scaleRelative(2.0, 2.0); - } - { - // view to layout0..1 - this._tmpMatrix.translateRelative( - layoutBoundsOnTex01.x, - layoutBoundsOnTex01.y - ); - this._tmpMatrix.scaleRelative(scaleX, scaleY); // new = [translate][scale] - this._tmpMatrix.translateRelative( - -this._tmpBoundsOnModel.x, - -this._tmpBoundsOnModel.y - ); - // new = [translate][scale][translate] - } - // tmpMatrixForMaskが計算結果 - this._tmpMatrixForMask.setMatrix(this._tmpMatrix.getArray()); - } - - //--------- draw時の mask 参照用行列を計算 - { - // シェーダに渡す行列を求める <<<<<<<<<<<<<<<<<<<<<<<< 要最適化(逆順に計算すればシンプルにできる) - this._tmpMatrix.loadIdentity(); - { - this._tmpMatrix.translateRelative( - layoutBoundsOnTex01.x, - layoutBoundsOnTex01.y - ); - this._tmpMatrix.scaleRelative(scaleX, scaleY); // new = [translate][scale] - this._tmpMatrix.translateRelative( - -this._tmpBoundsOnModel.x, - -this._tmpBoundsOnModel.y - ); - // new = [translate][scale][translate] - } - this._tmpMatrixForDraw.setMatrix(this._tmpMatrix.getArray()); - } - clipContext._matrixForMask.setMatrix( - this._tmpMatrixForMask.getArray() - ); - clipContext._matrixForDraw.setMatrix( - this._tmpMatrixForDraw.getArray() - ); - - const clipDrawCount: number = clipContext._clippingIdCount; - for (let i = 0; i < clipDrawCount; i++) { - const clipDrawIndex: number = clipContext._clippingIdList[i]; - - // 頂点情報が更新されておらず、信頼性がない場合は描画をパスする - if ( - !model.getDrawableDynamicFlagVertexPositionsDidChange( - clipDrawIndex - ) - ) { - continue; - } - - renderer.setIsCulling( - model.getDrawableCulling(clipDrawIndex) != false - ); - - // 今回専用の変換を適用して描く - // チャンネルも切り替える必要がある(A,R,G,B) - renderer.setClippingContextBufferForMask(clipContext); - renderer.drawMesh( - model.getDrawableTextureIndices(clipDrawIndex), - model.getDrawableVertexIndexCount(clipDrawIndex), - model.getDrawableVertexCount(clipDrawIndex), - model.getDrawableVertexIndices(clipDrawIndex), - model.getDrawableVertices(clipDrawIndex), - model.getDrawableVertexUvs(clipDrawIndex), - model.getDrawableOpacity(clipDrawIndex), - CubismBlendMode.CubismBlendMode_Normal, // クリッピングは通常描画を強制 - false // マスク生成時はクリッピングの反転使用は全く関係がない - ); + for (let k = 0; k < count; k++) { + if (drawableMasks[k] == clipId) { + sameCount++; + break; } } + } - // --- 後処理 --- - this.gl.bindFramebuffer(this.gl.FRAMEBUFFER, s_fbo); // 描画対象を戻す - renderer.setClippingContextBufferForMask(null); - - this.gl.viewport( - s_viewport[0], - s_viewport[1], - s_viewport[2], - s_viewport[3] - ); + if (sameCount == count) { + return clippingContext; } } - /** - * 既にマスクを作っているかを確認 - * 作っている様であれば該当するクリッピングマスクのインスタンスを返す - * 作っていなければNULLを返す - * @param drawableMasks 描画オブジェクトをマスクする描画オブジェクトのリスト - * @param drawableMaskCounts 描画オブジェクトをマスクする描画オブジェクトの数 - * @return 該当するクリッピングマスクが存在すればインスタンスを返し、なければNULLを返す - */ - public findSameClip( - drawableMasks: Int32Array, - drawableMaskCounts: number - ): CubismClippingContext { - // 作成済みClippingContextと一致するか確認 - for (let i = 0; i < this._clippingContextListForMask.getSize(); i++) { - const clippingContext: CubismClippingContext = this._clippingContextListForMask.at( - i + return null; // 見つからなかった + } + + /** + * クリッピングコンテキストを配置するレイアウト + * 一つのレンダーテクスチャを極力いっぱいに使ってマスクをレイアウトする + * マスクグループの数が4以下ならRGBA各チャンネルに一つずつマスクを配置し、5以上6以下ならRGBAを2,2,1,1と配置する。 + * + * @param usingClipCount 配置するクリッピングコンテキストの数 + */ + public setupLayoutBounds(usingClipCount: number): void { + // ひとつのRenderTextureを極力いっぱいに使ってマスクをレイアウトする + // マスクグループの数が4以下ならRGBA各チャンネルに1つずつマスクを配置し、5以上6以下ならRGBAを2,2,1,1と配置する + + // RGBAを順番に使っていく + let div: number = usingClipCount / ColorChannelCount; // 1チャンネルに配置する基本のマスク + let mod: number = usingClipCount % ColorChannelCount; // 余り、この番号のチャンネルまでに一つずつ配分する + + // 小数点は切り捨てる + div = ~~div; + mod = ~~mod; + + // RGBAそれぞれのチャンネルを用意していく(0:R, 1:G, 2:B, 3:A) + let curClipIndex = 0; // 順番に設定していく + + for (let channelNo = 0; channelNo < ColorChannelCount; channelNo++) { + // このチャンネルにレイアウトする数 + const layoutCount: number = div + (channelNo < mod ? 1 : 0); + + // 分割方法を決定する + if (layoutCount == 0) { + // 何もしない + } else if (layoutCount == 1) { + // 全てをそのまま使う + const clipContext: CubismClippingContext = this._clippingContextListForMask.at( + curClipIndex++ ); - const count: number = clippingContext._clippingIdCount; + clipContext._layoutChannelNo = channelNo; + clipContext._layoutBounds.x = 0.0; + clipContext._layoutBounds.y = 0.0; + clipContext._layoutBounds.width = 1.0; + clipContext._layoutBounds.height = 1.0; + } else if (layoutCount == 2) { + for (let i = 0; i < layoutCount; i++) { + let xpos: number = i % 2; - // 個数が違う場合は別物 - if (count != drawableMaskCounts) { - continue; - } + // 小数点は切り捨てる + xpos = ~~xpos; - let sameCount = 0; - - // 同じIDを持つか確認。配列の数が同じなので、一致した個数が同じなら同じ物を持つとする - for (let j = 0; j < count; j++) { - const clipId: number = clippingContext._clippingIdList[j]; - - for (let k = 0; k < count; k++) { - if (drawableMasks[k] == clipId) { - sameCount++; - break; - } - } - } - - if (sameCount == count) { - return clippingContext; - } - } - - return null; // 見つからなかった - } - - /** - * クリッピングコンテキストを配置するレイアウト - * 一つのレンダーテクスチャを極力いっぱいに使ってマスクをレイアウトする - * マスクグループの数が4以下ならRGBA各チャンネルに一つずつマスクを配置し、5以上6以下ならRGBAを2,2,1,1と配置する。 - * - * @param usingClipCount 配置するクリッピングコンテキストの数 - */ - public setupLayoutBounds(usingClipCount: number): void { - // ひとつのRenderTextureを極力いっぱいに使ってマスクをレイアウトする - // マスクグループの数が4以下ならRGBA各チャンネルに1つずつマスクを配置し、5以上6以下ならRGBAを2,2,1,1と配置する - - // RGBAを順番に使っていく - let div: number = usingClipCount / ColorChannelCount; // 1チャンネルに配置する基本のマスク - let mod: number = usingClipCount % ColorChannelCount; // 余り、この番号のチャンネルまでに一つずつ配分する - - // 小数点は切り捨てる - div = ~~div; - mod = ~~mod; - - // RGBAそれぞれのチャンネルを用意していく(0:R, 1:G, 2:B, 3:A) - let curClipIndex = 0; // 順番に設定していく - - for (let channelNo = 0; channelNo < ColorChannelCount; channelNo++) { - // このチャンネルにレイアウトする数 - const layoutCount: number = div + (channelNo < mod ? 1 : 0); - - // 分割方法を決定する - if (layoutCount == 0) { - // 何もしない - } else if (layoutCount == 1) { - // 全てをそのまま使う - const clipContext: CubismClippingContext = this._clippingContextListForMask.at( + const cc: CubismClippingContext = this._clippingContextListForMask.at( curClipIndex++ ); - clipContext._layoutChannelNo = channelNo; - clipContext._layoutBounds.x = 0.0; - clipContext._layoutBounds.y = 0.0; - clipContext._layoutBounds.width = 1.0; - clipContext._layoutBounds.height = 1.0; - } else if (layoutCount == 2) { - for (let i = 0; i < layoutCount; i++) { - let xpos: number = i % 2; + cc._layoutChannelNo = channelNo; - // 小数点は切り捨てる - xpos = ~~xpos; - - const cc: CubismClippingContext = this._clippingContextListForMask.at( - curClipIndex++ - ); - cc._layoutChannelNo = channelNo; - - cc._layoutBounds.x = xpos * 0.5; - cc._layoutBounds.y = 0.0; - cc._layoutBounds.width = 0.5; - cc._layoutBounds.height = 1.0; - // UVを2つに分解して使う - } - } else if (layoutCount <= 4) { - // 4分割して使う - for (let i = 0; i < layoutCount; i++) { - let xpos: number = i % 2; - let ypos: number = i / 2; - - // 小数点は切り捨てる - xpos = ~~xpos; - ypos = ~~ypos; - - const cc = this._clippingContextListForMask.at(curClipIndex++); - cc._layoutChannelNo = channelNo; - - cc._layoutBounds.x = xpos * 0.5; - cc._layoutBounds.y = ypos * 0.5; - cc._layoutBounds.width = 0.5; - cc._layoutBounds.height = 0.5; - } - } else if (layoutCount <= 9) { - // 9分割して使う - for (let i = 0; i < layoutCount; i++) { - let xpos = i % 3; - let ypos = i / 3; - - // 小数点は切り捨てる - xpos = ~~xpos; - ypos = ~~ypos; - - const cc: CubismClippingContext = this._clippingContextListForMask.at( - curClipIndex++ - ); - cc._layoutChannelNo = channelNo; - - cc._layoutBounds.x = xpos / 3.0; - cc._layoutBounds.y = ypos / 3.0; - cc._layoutBounds.width = 1.0 / 3.0; - cc._layoutBounds.height = 1.0 / 3.0; - } - } else { - CubismLogError('not supported mask count : {0}', layoutCount); + cc._layoutBounds.x = xpos * 0.5; + cc._layoutBounds.y = 0.0; + cc._layoutBounds.width = 0.5; + cc._layoutBounds.height = 1.0; + // UVを2つに分解して使う } + } else if (layoutCount <= 4) { + // 4分割して使う + for (let i = 0; i < layoutCount; i++) { + let xpos: number = i % 2; + let ypos: number = i / 2; + + // 小数点は切り捨てる + xpos = ~~xpos; + ypos = ~~ypos; + + const cc = this._clippingContextListForMask.at(curClipIndex++); + cc._layoutChannelNo = channelNo; + + cc._layoutBounds.x = xpos * 0.5; + cc._layoutBounds.y = ypos * 0.5; + cc._layoutBounds.width = 0.5; + cc._layoutBounds.height = 0.5; + } + } else if (layoutCount <= 9) { + // 9分割して使う + for (let i = 0; i < layoutCount; i++) { + let xpos = i % 3; + let ypos = i / 3; + + // 小数点は切り捨てる + xpos = ~~xpos; + ypos = ~~ypos; + + const cc: CubismClippingContext = this._clippingContextListForMask.at( + curClipIndex++ + ); + cc._layoutChannelNo = channelNo; + + cc._layoutBounds.x = xpos / 3.0; + cc._layoutBounds.y = ypos / 3.0; + cc._layoutBounds.width = 1.0 / 3.0; + cc._layoutBounds.height = 1.0 / 3.0; + } + } else { + CubismLogError('not supported mask count : {0}', layoutCount); } } - - /** - * カラーバッファを取得する - * @return カラーバッファ - */ - public getColorBuffer(): WebGLTexture { - return this._colorBuffer; - } - - /** - * 画面描画に使用するクリッピングマスクのリストを取得する - * @return 画面描画に使用するクリッピングマスクのリスト - */ - public getClippingContextListForDraw(): csmVector { - return this._clippingContextListForDraw; - } - - /** - * クリッピングマスクバッファのサイズを設定する - * @param size クリッピングマスクバッファのサイズ - */ - public setClippingMaskBufferSize(size: number): void { - this._clippingMaskBufferSize = size; - } - - /** - * クリッピングマスクバッファのサイズを取得する - * @return クリッピングマスクバッファのサイズ - */ - public getClippingMaskBufferSize(): number { - return this._clippingMaskBufferSize; - } - - public _maskRenderTexture: WebGLFramebuffer; // マスク用レンダーテクスチャのアドレス - public _colorBuffer: WebGLTexture; // マスク用カラーバッファーのアドレス - public _currentFrameNo: number; // マスクテクスチャに与えるフレーム番号 - - public _channelColors: csmVector; - public _maskTexture: CubismRenderTextureResource; // マスク用のテクスチャリソースのリスト - public _clippingContextListForMask: csmVector; // マスク用クリッピングコンテキストのリスト - public _clippingContextListForDraw: csmVector; // 描画用クリッピングコンテキストのリスト - public _clippingMaskBufferSize: number; // クリッピングマスクのバッファサイズ(初期値:256) - - private _tmpMatrix: CubismMatrix44; // マスク計算用の行列 - private _tmpMatrixForMask: CubismMatrix44; // マスク計算用の行列 - private _tmpMatrixForDraw: CubismMatrix44; // マスク計算用の行列 - private _tmpBoundsOnModel: csmRect; // マスク配置計算用の矩形 - - gl: WebGLRenderingContext; // WebGLレンダリングコンテキスト } /** - * レンダーテクスチャのリソースを定義する構造体 - * クリッピングマスクで使用する + * カラーバッファを取得する + * @return カラーバッファ */ - export class CubismRenderTextureResource { - /** - * 引数付きコンストラクタ - * @param frameNo レンダラーのフレーム番号 - * @param texture テクスチャのアドレス - */ - public constructor(frameNo: number, texture: WebGLFramebuffer) { - this.frameNo = frameNo; - this.texture = texture; - } - - public frameNo: number; // レンダラのフレーム番号 - public texture: WebGLFramebuffer; // テクスチャのアドレス + public getColorBuffer(): WebGLTexture { + return this._colorBuffer; } /** - * クリッピングマスクのコンテキスト + * 画面描画に使用するクリッピングマスクのリストを取得する + * @return 画面描画に使用するクリッピングマスクのリスト */ - export class CubismClippingContext { - /** - * 引数付きコンストラクタ - */ - public constructor( - manager: CubismClippingManager_WebGL, - clippingDrawableIndices: Int32Array, - clipCount: number - ) { - this._owner = manager; - - // クリップしている(=マスク用の)Drawableのインデックスリスト - this._clippingIdList = clippingDrawableIndices; - - // マスクの数 - this._clippingIdCount = clipCount; - - this._allClippedDrawRect = new csmRect(); - this._layoutBounds = new csmRect(); - - this._clippedDrawableIndexList = []; - - this._matrixForMask = new CubismMatrix44(); - this._matrixForDraw = new CubismMatrix44(); - } - - /** - * デストラクタ相当の処理 - */ - public release(): void { - if (this._layoutBounds != null) { - this._layoutBounds = null; - } - - if (this._allClippedDrawRect != null) { - this._allClippedDrawRect = null; - } - - if (this._clippedDrawableIndexList != null) { - this._clippedDrawableIndexList = null; - } - } - - /** - * このマスクにクリップされる描画オブジェクトを追加する - * - * @param drawableIndex クリッピング対象に追加する描画オブジェクトのインデックス - */ - public addClippedDrawable(drawableIndex: number) { - this._clippedDrawableIndexList.push(drawableIndex); - } - - /** - * このマスクを管理するマネージャのインスタンスを取得する - * @return クリッピングマネージャのインスタンス - */ - public getClippingManager(): CubismClippingManager_WebGL { - return this._owner; - } - - public setGl(gl: WebGLRenderingContext): void { - this._owner.setGL(gl); - } - - public _isUsing: boolean; // 現在の描画状態でマスクの準備が必要ならtrue - public readonly _clippingIdList: Int32Array; // クリッピングマスクのIDリスト - public _clippingIdCount: number; // クリッピングマスクの数 - public _layoutChannelNo: number; // RGBAのいずれのチャンネルにこのクリップを配置するか(0:R, 1:G, 2:B, 3:A) - public _layoutBounds: csmRect; // マスク用チャンネルのどの領域にマスクを入れるか(View座標-1~1, UVは0~1に直す) - public _allClippedDrawRect: csmRect; // このクリッピングで、クリッピングされるすべての描画オブジェクトの囲み矩形(毎回更新) - public _matrixForMask: CubismMatrix44; // マスクの位置計算結果を保持する行列 - public _matrixForDraw: CubismMatrix44; // 描画オブジェクトの位置計算結果を保持する行列 - public _clippedDrawableIndexList: number[]; // このマスクにクリップされる描画オブジェクトのリスト - - private _owner: CubismClippingManager_WebGL; // このマスクを管理しているマネージャのインスタンス + public getClippingContextListForDraw(): csmVector { + return this._clippingContextListForDraw; } /** - * WebGL用のシェーダープログラムを生成・破棄するクラス - * シングルトンなクラスであり、CubismShader_WebGL.getInstanceからアクセスする。 + * クリッピングマスクバッファのサイズを設定する + * @param size クリッピングマスクバッファのサイズ */ - export class CubismShader_WebGL { - /** - * インスタンスを取得する(シングルトン) - * @return インスタンス - */ - public static getInstance(): CubismShader_WebGL { - if (s_instance == null) { - s_instance = new CubismShader_WebGL(); + public setClippingMaskBufferSize(size: number): void { + this._clippingMaskBufferSize = size; + } + + /** + * クリッピングマスクバッファのサイズを取得する + * @return クリッピングマスクバッファのサイズ + */ + public getClippingMaskBufferSize(): number { + return this._clippingMaskBufferSize; + } + + public _maskRenderTexture: WebGLFramebuffer; // マスク用レンダーテクスチャのアドレス + public _colorBuffer: WebGLTexture; // マスク用カラーバッファーのアドレス + public _currentFrameNo: number; // マスクテクスチャに与えるフレーム番号 + + public _channelColors: csmVector; + public _maskTexture: CubismRenderTextureResource; // マスク用のテクスチャリソースのリスト + public _clippingContextListForMask: csmVector; // マスク用クリッピングコンテキストのリスト + public _clippingContextListForDraw: csmVector; // 描画用クリッピングコンテキストのリスト + public _clippingMaskBufferSize: number; // クリッピングマスクのバッファサイズ(初期値:256) + + private _tmpMatrix: CubismMatrix44; // マスク計算用の行列 + private _tmpMatrixForMask: CubismMatrix44; // マスク計算用の行列 + private _tmpMatrixForDraw: CubismMatrix44; // マスク計算用の行列 + private _tmpBoundsOnModel: csmRect; // マスク配置計算用の矩形 + + gl: WebGLRenderingContext; // WebGLレンダリングコンテキスト +} + +/** + * レンダーテクスチャのリソースを定義する構造体 + * クリッピングマスクで使用する + */ +export class CubismRenderTextureResource { + /** + * 引数付きコンストラクタ + * @param frameNo レンダラーのフレーム番号 + * @param texture テクスチャのアドレス + */ + public constructor(frameNo: number, texture: WebGLFramebuffer) { + this.frameNo = frameNo; + this.texture = texture; + } + + public frameNo: number; // レンダラのフレーム番号 + public texture: WebGLFramebuffer; // テクスチャのアドレス +} + +/** + * クリッピングマスクのコンテキスト + */ +export class CubismClippingContext { + /** + * 引数付きコンストラクタ + */ + public constructor( + manager: CubismClippingManager_WebGL, + clippingDrawableIndices: Int32Array, + clipCount: number + ) { + this._owner = manager; + + // クリップしている(=マスク用の)Drawableのインデックスリスト + this._clippingIdList = clippingDrawableIndices; + + // マスクの数 + this._clippingIdCount = clipCount; + + this._allClippedDrawRect = new csmRect(); + this._layoutBounds = new csmRect(); + + this._clippedDrawableIndexList = []; + + this._matrixForMask = new CubismMatrix44(); + this._matrixForDraw = new CubismMatrix44(); + } + + /** + * デストラクタ相当の処理 + */ + public release(): void { + if (this._layoutBounds != null) { + this._layoutBounds = null; + } + + if (this._allClippedDrawRect != null) { + this._allClippedDrawRect = null; + } + + if (this._clippedDrawableIndexList != null) { + this._clippedDrawableIndexList = null; + } + } + + /** + * このマスクにクリップされる描画オブジェクトを追加する + * + * @param drawableIndex クリッピング対象に追加する描画オブジェクトのインデックス + */ + public addClippedDrawable(drawableIndex: number) { + this._clippedDrawableIndexList.push(drawableIndex); + } + + /** + * このマスクを管理するマネージャのインスタンスを取得する + * @return クリッピングマネージャのインスタンス + */ + public getClippingManager(): CubismClippingManager_WebGL { + return this._owner; + } + + public setGl(gl: WebGLRenderingContext): void { + this._owner.setGL(gl); + } + + public _isUsing: boolean; // 現在の描画状態でマスクの準備が必要ならtrue + public readonly _clippingIdList: Int32Array; // クリッピングマスクのIDリスト + public _clippingIdCount: number; // クリッピングマスクの数 + public _layoutChannelNo: number; // RGBAのいずれのチャンネルにこのクリップを配置するか(0:R, 1:G, 2:B, 3:A) + public _layoutBounds: csmRect; // マスク用チャンネルのどの領域にマスクを入れるか(View座標-1~1, UVは0~1に直す) + public _allClippedDrawRect: csmRect; // このクリッピングで、クリッピングされるすべての描画オブジェクトの囲み矩形(毎回更新) + public _matrixForMask: CubismMatrix44; // マスクの位置計算結果を保持する行列 + public _matrixForDraw: CubismMatrix44; // 描画オブジェクトの位置計算結果を保持する行列 + public _clippedDrawableIndexList: number[]; // このマスクにクリップされる描画オブジェクトのリスト + + private _owner: CubismClippingManager_WebGL; // このマスクを管理しているマネージャのインスタンス +} + +/** + * WebGL用のシェーダープログラムを生成・破棄するクラス + * シングルトンなクラスであり、CubismShader_WebGL.getInstanceからアクセスする。 + */ +export class CubismShader_WebGL { + /** + * インスタンスを取得する(シングルトン) + * @return インスタンス + */ + public static getInstance(): CubismShader_WebGL { + if (s_instance == null) { + s_instance = new CubismShader_WebGL(); - return s_instance; - } return s_instance; } + return s_instance; + } - /** - * インスタンスを開放する(シングルトン) - */ - public static deleteInstance(): void { - if (s_instance) { - s_instance.release(); - s_instance = null; - } + /** + * インスタンスを開放する(シングルトン) + */ + public static deleteInstance(): void { + if (s_instance) { + s_instance.release(); + s_instance = null; + } + } + + /** + * privateなコンストラクタ + */ + private constructor() { + this._shaderSets = new csmVector(); + } + + /** + * デストラクタ相当の処理 + */ + public release(): void { + this.releaseShaderProgram(); + } + + /** + * シェーダープログラムの一連のセットアップを実行する + * @param renderer レンダラのインスタンス + * @param textureId GPUのテクスチャID + * @param vertexCount ポリゴンメッシュの頂点数 + * @param vertexArray ポリゴンメッシュの頂点配列 + * @param indexArray インデックスバッファの頂点配列 + * @param uvArray uv配列 + * @param opacity 不透明度 + * @param colorBlendMode カラーブレンディングのタイプ + * @param baseColor ベースカラー + * @param isPremultipliedAlpha 乗算済みアルファかどうか + * @param matrix4x4 Model-View-Projection行列 + * @param invertedMask マスクを反転して使用するフラグ + */ + public setupShaderProgram( + renderer: CubismRenderer_WebGL, + textureId: WebGLTexture, + vertexCount: number, + vertexArray: Float32Array, + indexArray: Uint16Array, + uvArray: Float32Array, + bufferData: { + vertex: WebGLBuffer; + uv: WebGLBuffer; + index: WebGLBuffer; + }, + opacity: number, + colorBlendMode: CubismBlendMode, + baseColor: CubismTextureColor, + isPremultipliedAlpha: boolean, + matrix4x4: CubismMatrix44, + invertedMask: boolean + ): void { + if (!isPremultipliedAlpha) { + CubismLogError('NoPremultipliedAlpha is not allowed'); } - /** - * privateなコンストラクタ - */ - private constructor() { - this._shaderSets = new csmVector(); + if (this._shaderSets.getSize() == 0) { + this.generateShaders(); } - /** - * デストラクタ相当の処理 - */ - public release(): void { - this.releaseShaderProgram(); - } + // Blending + let SRC_COLOR: number; + let DST_COLOR: number; + let SRC_ALPHA: number; + let DST_ALPHA: number; - /** - * シェーダープログラムの一連のセットアップを実行する - * @param renderer レンダラのインスタンス - * @param textureId GPUのテクスチャID - * @param vertexCount ポリゴンメッシュの頂点数 - * @param vertexArray ポリゴンメッシュの頂点配列 - * @param indexArray インデックスバッファの頂点配列 - * @param uvArray uv配列 - * @param opacity 不透明度 - * @param colorBlendMode カラーブレンディングのタイプ - * @param baseColor ベースカラー - * @param isPremultipliedAlpha 乗算済みアルファかどうか - * @param matrix4x4 Model-View-Projection行列 - * @param invertedMask マスクを反転して使用するフラグ - */ - public setupShaderProgram( - renderer: CubismRenderer_WebGL, - textureId: WebGLTexture, - vertexCount: number, - vertexArray: Float32Array, - indexArray: Uint16Array, - uvArray: Float32Array, - bufferData: { - vertex: WebGLBuffer; - uv: WebGLBuffer; - index: WebGLBuffer; - }, - opacity: number, - colorBlendMode: CubismBlendMode, - baseColor: CubismTextureColor, - isPremultipliedAlpha: boolean, - matrix4x4: CubismMatrix44, - invertedMask: boolean - ): void { - if (!isPremultipliedAlpha) { - CubismLogError('NoPremultipliedAlpha is not allowed'); + if (renderer.getClippingContextBufferForMask() != null) { + // マスク生成時 + const shaderSet: CubismShaderSet = this._shaderSets.at( + ShaderNames.ShaderNames_SetupMask + ); + this.gl.useProgram(shaderSet.shaderProgram); + + // テクスチャ設定 + this.gl.activeTexture(this.gl.TEXTURE0); + this.gl.bindTexture(this.gl.TEXTURE_2D, textureId); + this.gl.uniform1i(shaderSet.samplerTexture0Location, 0); + + // 頂点配列の設定(VBO) + if (bufferData.vertex == null) { + bufferData.vertex = this.gl.createBuffer(); + } + this.gl.bindBuffer(this.gl.ARRAY_BUFFER, bufferData.vertex); + this.gl.bufferData( + this.gl.ARRAY_BUFFER, + vertexArray, + this.gl.DYNAMIC_DRAW + ); + this.gl.enableVertexAttribArray(shaderSet.attributePositionLocation); + this.gl.vertexAttribPointer( + shaderSet.attributePositionLocation, + 2, + this.gl.FLOAT, + false, + 0, + 0 + ); + + // テクスチャ頂点の設定 + if (bufferData.uv == null) { + bufferData.uv = this.gl.createBuffer(); + } + this.gl.bindBuffer(this.gl.ARRAY_BUFFER, bufferData.uv); + this.gl.bufferData(this.gl.ARRAY_BUFFER, uvArray, this.gl.DYNAMIC_DRAW); + this.gl.enableVertexAttribArray(shaderSet.attributeTexCoordLocation); + this.gl.vertexAttribPointer( + shaderSet.attributeTexCoordLocation, + 2, + this.gl.FLOAT, + false, + 0, + 0 + ); + + // チャンネル + const channelNo: number = renderer.getClippingContextBufferForMask() + ._layoutChannelNo; + const colorChannel: CubismTextureColor = renderer + .getClippingContextBufferForMask() + .getClippingManager() + .getChannelFlagAsColor(channelNo); + this.gl.uniform4f( + shaderSet.uniformChannelFlagLocation, + colorChannel.R, + colorChannel.G, + colorChannel.B, + colorChannel.A + ); + + this.gl.uniformMatrix4fv( + shaderSet.uniformClipMatrixLocation, + false, + renderer.getClippingContextBufferForMask()._matrixForMask.getArray() + ); + + const rect: csmRect = renderer.getClippingContextBufferForMask() + ._layoutBounds; + + this.gl.uniform4f( + shaderSet.uniformBaseColorLocation, + rect.x * 2.0 - 1.0, + rect.y * 2.0 - 1.0, + rect.getRight() * 2.0 - 1.0, + rect.getBottom() * 2.0 - 1.0 + ); + + SRC_COLOR = this.gl.ZERO; + DST_COLOR = this.gl.ONE_MINUS_SRC_COLOR; + SRC_ALPHA = this.gl.ZERO; + DST_ALPHA = this.gl.ONE_MINUS_SRC_ALPHA; + } // マスク生成以外の場合 + else { + const masked: boolean = + renderer.getClippingContextBufferForDraw() != null; // この描画オブジェクトはマスク対象か + const offset: number = masked ? (invertedMask ? 2 : 1) : 0; + + let shaderSet: CubismShaderSet = new CubismShaderSet(); + + switch (colorBlendMode) { + case CubismBlendMode.CubismBlendMode_Normal: + default: + shaderSet = this._shaderSets.at( + ShaderNames.ShaderNames_NormalPremultipliedAlpha + offset + ); + SRC_COLOR = this.gl.ONE; + DST_COLOR = this.gl.ONE_MINUS_SRC_ALPHA; + SRC_ALPHA = this.gl.ONE; + DST_ALPHA = this.gl.ONE_MINUS_SRC_ALPHA; + break; + + case CubismBlendMode.CubismBlendMode_Additive: + shaderSet = this._shaderSets.at( + ShaderNames.ShaderNames_AddPremultipliedAlpha + offset + ); + SRC_COLOR = this.gl.ONE; + DST_COLOR = this.gl.ONE; + SRC_ALPHA = this.gl.ZERO; + DST_ALPHA = this.gl.ONE; + break; + + case CubismBlendMode.CubismBlendMode_Multiplicative: + shaderSet = this._shaderSets.at( + ShaderNames.ShaderNames_MultPremultipliedAlpha + offset + ); + SRC_COLOR = this.gl.DST_COLOR; + DST_COLOR = this.gl.ONE_MINUS_SRC_ALPHA; + SRC_ALPHA = this.gl.ZERO; + DST_ALPHA = this.gl.ONE; + break; } - if (this._shaderSets.getSize() == 0) { - this.generateShaders(); + this.gl.useProgram(shaderSet.shaderProgram); + + // 頂点配列の設定 + if (bufferData.vertex == null) { + bufferData.vertex = this.gl.createBuffer(); } + this.gl.bindBuffer(this.gl.ARRAY_BUFFER, bufferData.vertex); + this.gl.bufferData( + this.gl.ARRAY_BUFFER, + vertexArray, + this.gl.DYNAMIC_DRAW + ); + this.gl.enableVertexAttribArray(shaderSet.attributePositionLocation); + this.gl.vertexAttribPointer( + shaderSet.attributePositionLocation, + 2, + this.gl.FLOAT, + false, + 0, + 0 + ); - // Blending - let SRC_COLOR: number; - let DST_COLOR: number; - let SRC_ALPHA: number; - let DST_ALPHA: number; + // テクスチャ頂点の設定 + if (bufferData.uv == null) { + bufferData.uv = this.gl.createBuffer(); + } + this.gl.bindBuffer(this.gl.ARRAY_BUFFER, bufferData.uv); + this.gl.bufferData(this.gl.ARRAY_BUFFER, uvArray, this.gl.DYNAMIC_DRAW); + this.gl.enableVertexAttribArray(shaderSet.attributeTexCoordLocation); + this.gl.vertexAttribPointer( + shaderSet.attributeTexCoordLocation, + 2, + this.gl.FLOAT, + false, + 0, + 0 + ); - if (renderer.getClippingContextBufferForMask() != null) { - // マスク生成時 - const shaderSet: CubismShaderSet = this._shaderSets.at( - ShaderNames.ShaderNames_SetupMask - ); - this.gl.useProgram(shaderSet.shaderProgram); + if (masked) { + this.gl.activeTexture(this.gl.TEXTURE1); + const tex: WebGLTexture = renderer + .getClippingContextBufferForDraw() + .getClippingManager() + .getColorBuffer(); + this.gl.bindTexture(this.gl.TEXTURE_2D, tex); + this.gl.uniform1i(shaderSet.samplerTexture1Location, 1); - // テクスチャ設定 - this.gl.activeTexture(this.gl.TEXTURE0); - this.gl.bindTexture(this.gl.TEXTURE_2D, textureId); - this.gl.uniform1i(shaderSet.samplerTexture0Location, 0); - - // 頂点配列の設定(VBO) - if (bufferData.vertex == null) { - bufferData.vertex = this.gl.createBuffer(); - } - this.gl.bindBuffer(this.gl.ARRAY_BUFFER, bufferData.vertex); - this.gl.bufferData( - this.gl.ARRAY_BUFFER, - vertexArray, - this.gl.DYNAMIC_DRAW - ); - this.gl.enableVertexAttribArray(shaderSet.attributePositionLocation); - this.gl.vertexAttribPointer( - shaderSet.attributePositionLocation, - 2, - this.gl.FLOAT, + // view座標をClippingContextの座標に変換するための行列を設定 + this.gl.uniformMatrix4fv( + shaderSet.uniformClipMatrixLocation, false, - 0, - 0 + renderer.getClippingContextBufferForDraw()._matrixForDraw.getArray() ); - // テクスチャ頂点の設定 - if (bufferData.uv == null) { - bufferData.uv = this.gl.createBuffer(); - } - this.gl.bindBuffer(this.gl.ARRAY_BUFFER, bufferData.uv); - this.gl.bufferData(this.gl.ARRAY_BUFFER, uvArray, this.gl.DYNAMIC_DRAW); - this.gl.enableVertexAttribArray(shaderSet.attributeTexCoordLocation); - this.gl.vertexAttribPointer( - shaderSet.attributeTexCoordLocation, - 2, - this.gl.FLOAT, - false, - 0, - 0 - ); - - // チャンネル - const channelNo: number = renderer.getClippingContextBufferForMask() + // 使用するカラーチャンネルを設定 + const channelNo: number = renderer.getClippingContextBufferForDraw() ._layoutChannelNo; const colorChannel: CubismTextureColor = renderer - .getClippingContextBufferForMask() + .getClippingContextBufferForDraw() .getClippingManager() .getChannelFlagAsColor(channelNo); this.gl.uniform4f( @@ -992,1268 +1111,1112 @@ export namespace Live2DCubismFramework { colorChannel.B, colorChannel.A ); - - this.gl.uniformMatrix4fv( - shaderSet.uniformClipMatrixLocation, - false, - renderer.getClippingContextBufferForMask()._matrixForMask.getArray() - ); - - const rect: csmRect = renderer.getClippingContextBufferForMask() - ._layoutBounds; - - this.gl.uniform4f( - shaderSet.uniformBaseColorLocation, - rect.x * 2.0 - 1.0, - rect.y * 2.0 - 1.0, - rect.getRight() * 2.0 - 1.0, - rect.getBottom() * 2.0 - 1.0 - ); - - SRC_COLOR = this.gl.ZERO; - DST_COLOR = this.gl.ONE_MINUS_SRC_COLOR; - SRC_ALPHA = this.gl.ZERO; - DST_ALPHA = this.gl.ONE_MINUS_SRC_ALPHA; - } // マスク生成以外の場合 - else { - const masked: boolean = - renderer.getClippingContextBufferForDraw() != null; // この描画オブジェクトはマスク対象か - const offset: number = masked ? (invertedMask ? 2 : 1) : 0; - - let shaderSet: CubismShaderSet = new CubismShaderSet(); - - switch (colorBlendMode) { - case CubismBlendMode.CubismBlendMode_Normal: - default: - shaderSet = this._shaderSets.at( - ShaderNames.ShaderNames_NormalPremultipliedAlpha + offset - ); - SRC_COLOR = this.gl.ONE; - DST_COLOR = this.gl.ONE_MINUS_SRC_ALPHA; - SRC_ALPHA = this.gl.ONE; - DST_ALPHA = this.gl.ONE_MINUS_SRC_ALPHA; - break; - - case CubismBlendMode.CubismBlendMode_Additive: - shaderSet = this._shaderSets.at( - ShaderNames.ShaderNames_AddPremultipliedAlpha + offset - ); - SRC_COLOR = this.gl.ONE; - DST_COLOR = this.gl.ONE; - SRC_ALPHA = this.gl.ZERO; - DST_ALPHA = this.gl.ONE; - break; - - case CubismBlendMode.CubismBlendMode_Multiplicative: - shaderSet = this._shaderSets.at( - ShaderNames.ShaderNames_MultPremultipliedAlpha + offset - ); - SRC_COLOR = this.gl.DST_COLOR; - DST_COLOR = this.gl.ONE_MINUS_SRC_ALPHA; - SRC_ALPHA = this.gl.ZERO; - DST_ALPHA = this.gl.ONE; - break; - } - - this.gl.useProgram(shaderSet.shaderProgram); - - // 頂点配列の設定 - if (bufferData.vertex == null) { - bufferData.vertex = this.gl.createBuffer(); - } - this.gl.bindBuffer(this.gl.ARRAY_BUFFER, bufferData.vertex); - this.gl.bufferData( - this.gl.ARRAY_BUFFER, - vertexArray, - this.gl.DYNAMIC_DRAW - ); - this.gl.enableVertexAttribArray(shaderSet.attributePositionLocation); - this.gl.vertexAttribPointer( - shaderSet.attributePositionLocation, - 2, - this.gl.FLOAT, - false, - 0, - 0 - ); - - // テクスチャ頂点の設定 - if (bufferData.uv == null) { - bufferData.uv = this.gl.createBuffer(); - } - this.gl.bindBuffer(this.gl.ARRAY_BUFFER, bufferData.uv); - this.gl.bufferData(this.gl.ARRAY_BUFFER, uvArray, this.gl.DYNAMIC_DRAW); - this.gl.enableVertexAttribArray(shaderSet.attributeTexCoordLocation); - this.gl.vertexAttribPointer( - shaderSet.attributeTexCoordLocation, - 2, - this.gl.FLOAT, - false, - 0, - 0 - ); - - if (masked) { - this.gl.activeTexture(this.gl.TEXTURE1); - const tex: WebGLTexture = renderer - .getClippingContextBufferForDraw() - .getClippingManager() - .getColorBuffer(); - this.gl.bindTexture(this.gl.TEXTURE_2D, tex); - this.gl.uniform1i(shaderSet.samplerTexture1Location, 1); - - // view座標をClippingContextの座標に変換するための行列を設定 - this.gl.uniformMatrix4fv( - shaderSet.uniformClipMatrixLocation, - false, - renderer.getClippingContextBufferForDraw()._matrixForDraw.getArray() - ); - - // 使用するカラーチャンネルを設定 - const channelNo: number = renderer.getClippingContextBufferForDraw() - ._layoutChannelNo; - const colorChannel: CubismTextureColor = renderer - .getClippingContextBufferForDraw() - .getClippingManager() - .getChannelFlagAsColor(channelNo); - this.gl.uniform4f( - shaderSet.uniformChannelFlagLocation, - colorChannel.R, - colorChannel.G, - colorChannel.B, - colorChannel.A - ); - } - - // テクスチャ設定 - this.gl.activeTexture(this.gl.TEXTURE0); - this.gl.bindTexture(this.gl.TEXTURE_2D, textureId); - this.gl.uniform1i(shaderSet.samplerTexture0Location, 0); - - // 座標変換 - this.gl.uniformMatrix4fv( - shaderSet.uniformMatrixLocation, - false, - matrix4x4.getArray() - ); - - this.gl.uniform4f( - shaderSet.uniformBaseColorLocation, - baseColor.R, - baseColor.G, - baseColor.B, - baseColor.A - ); } - // IBOを作成し、データを転送 - if (bufferData.index == null) { - bufferData.index = this.gl.createBuffer(); - } - this.gl.bindBuffer(this.gl.ELEMENT_ARRAY_BUFFER, bufferData.index); - this.gl.bufferData( - this.gl.ELEMENT_ARRAY_BUFFER, - indexArray, - this.gl.DYNAMIC_DRAW - ); - this.gl.blendFuncSeparate(SRC_COLOR, DST_COLOR, SRC_ALPHA, DST_ALPHA); - } + // テクスチャ設定 + this.gl.activeTexture(this.gl.TEXTURE0); + this.gl.bindTexture(this.gl.TEXTURE_2D, textureId); + this.gl.uniform1i(shaderSet.samplerTexture0Location, 0); - /** - * シェーダープログラムを解放する - */ - public releaseShaderProgram(): void { - for (let i = 0; i < this._shaderSets.getSize(); i++) { - this.gl.deleteProgram(this._shaderSets.at(i).shaderProgram); - this._shaderSets.at(i).shaderProgram = 0; - this._shaderSets.set(i, void 0); - this._shaderSets.set(i, null); - } - } - - /** - * シェーダープログラムを初期化する - * @param vertShaderSrc 頂点シェーダのソース - * @param fragShaderSrc フラグメントシェーダのソース - */ - public generateShaders(): void { - for (let i = 0; i < shaderCount; i++) { - this._shaderSets.pushBack(new CubismShaderSet()); - } - - this._shaderSets.at(0).shaderProgram = this.loadShaderProgram( - vertexShaderSrcSetupMask, - fragmentShaderSrcsetupMask + // 座標変換 + this.gl.uniformMatrix4fv( + shaderSet.uniformMatrixLocation, + false, + matrix4x4.getArray() ); - this._shaderSets.at(1).shaderProgram = this.loadShaderProgram( - vertexShaderSrc, - fragmentShaderSrcPremultipliedAlpha - ); - this._shaderSets.at(2).shaderProgram = this.loadShaderProgram( - vertexShaderSrcMasked, - fragmentShaderSrcMaskPremultipliedAlpha - ); - this._shaderSets.at(3).shaderProgram = this.loadShaderProgram( - vertexShaderSrcMasked, - fragmentShaderSrcMaskInvertedPremultipliedAlpha - ); - - // 加算も通常と同じシェーダーを利用する - this._shaderSets.at(4).shaderProgram = this._shaderSets.at( - 1 - ).shaderProgram; - this._shaderSets.at(5).shaderProgram = this._shaderSets.at( - 2 - ).shaderProgram; - this._shaderSets.at(6).shaderProgram = this._shaderSets.at( - 3 - ).shaderProgram; - - // 乗算も通常と同じシェーダーを利用する - this._shaderSets.at(7).shaderProgram = this._shaderSets.at( - 1 - ).shaderProgram; - this._shaderSets.at(8).shaderProgram = this._shaderSets.at( - 2 - ).shaderProgram; - this._shaderSets.at(9).shaderProgram = this._shaderSets.at( - 3 - ).shaderProgram; - - // SetupMask - this._shaderSets.at( - 0 - ).attributePositionLocation = this.gl.getAttribLocation( - this._shaderSets.at(0).shaderProgram, - 'a_position' - ); - this._shaderSets.at( - 0 - ).attributeTexCoordLocation = this.gl.getAttribLocation( - this._shaderSets.at(0).shaderProgram, - 'a_texCoord' - ); - this._shaderSets.at( - 0 - ).samplerTexture0Location = this.gl.getUniformLocation( - this._shaderSets.at(0).shaderProgram, - 's_texture0' - ); - this._shaderSets.at( - 0 - ).uniformClipMatrixLocation = this.gl.getUniformLocation( - this._shaderSets.at(0).shaderProgram, - 'u_clipMatrix' - ); - this._shaderSets.at( - 0 - ).uniformChannelFlagLocation = this.gl.getUniformLocation( - this._shaderSets.at(0).shaderProgram, - 'u_channelFlag' - ); - this._shaderSets.at( - 0 - ).uniformBaseColorLocation = this.gl.getUniformLocation( - this._shaderSets.at(0).shaderProgram, - 'u_baseColor' - ); - - // 通常(PremultipliedAlpha) - this._shaderSets.at( - 1 - ).attributePositionLocation = this.gl.getAttribLocation( - this._shaderSets.at(1).shaderProgram, - 'a_position' - ); - this._shaderSets.at( - 1 - ).attributeTexCoordLocation = this.gl.getAttribLocation( - this._shaderSets.at(1).shaderProgram, - 'a_texCoord' - ); - this._shaderSets.at( - 1 - ).samplerTexture0Location = this.gl.getUniformLocation( - this._shaderSets.at(1).shaderProgram, - 's_texture0' - ); - this._shaderSets.at(1).uniformMatrixLocation = this.gl.getUniformLocation( - this._shaderSets.at(1).shaderProgram, - 'u_matrix' - ); - this._shaderSets.at( - 1 - ).uniformBaseColorLocation = this.gl.getUniformLocation( - this._shaderSets.at(1).shaderProgram, - 'u_baseColor' - ); - - // 通常(クリッピング、PremultipliedAlpha) - this._shaderSets.at( - 2 - ).attributePositionLocation = this.gl.getAttribLocation( - this._shaderSets.at(2).shaderProgram, - 'a_position' - ); - this._shaderSets.at( - 2 - ).attributeTexCoordLocation = this.gl.getAttribLocation( - this._shaderSets.at(2).shaderProgram, - 'a_texCoord' - ); - this._shaderSets.at( - 2 - ).samplerTexture0Location = this.gl.getUniformLocation( - this._shaderSets.at(2).shaderProgram, - 's_texture0' - ); - this._shaderSets.at( - 2 - ).samplerTexture1Location = this.gl.getUniformLocation( - this._shaderSets.at(2).shaderProgram, - 's_texture1' - ); - this._shaderSets.at(2).uniformMatrixLocation = this.gl.getUniformLocation( - this._shaderSets.at(2).shaderProgram, - 'u_matrix' - ); - this._shaderSets.at( - 2 - ).uniformClipMatrixLocation = this.gl.getUniformLocation( - this._shaderSets.at(2).shaderProgram, - 'u_clipMatrix' - ); - this._shaderSets.at( - 2 - ).uniformChannelFlagLocation = this.gl.getUniformLocation( - this._shaderSets.at(2).shaderProgram, - 'u_channelFlag' - ); - this._shaderSets.at( - 2 - ).uniformBaseColorLocation = this.gl.getUniformLocation( - this._shaderSets.at(2).shaderProgram, - 'u_baseColor' - ); - - // 通常(クリッピング・反転, PremultipliedAlpha) - this._shaderSets.at( - 3 - ).attributePositionLocation = this.gl.getAttribLocation( - this._shaderSets.at(3).shaderProgram, - 'a_position' - ); - this._shaderSets.at( - 3 - ).attributeTexCoordLocation = this.gl.getAttribLocation( - this._shaderSets.at(3).shaderProgram, - 'a_texCoord' - ); - this._shaderSets.at( - 3 - ).samplerTexture0Location = this.gl.getUniformLocation( - this._shaderSets.at(3).shaderProgram, - 's_texture0' - ); - this._shaderSets.at( - 3 - ).samplerTexture1Location = this.gl.getUniformLocation( - this._shaderSets.at(3).shaderProgram, - 's_texture1' - ); - this._shaderSets.at(3).uniformMatrixLocation = this.gl.getUniformLocation( - this._shaderSets.at(3).shaderProgram, - 'u_matrix' - ); - this._shaderSets.at( - 3 - ).uniformClipMatrixLocation = this.gl.getUniformLocation( - this._shaderSets.at(3).shaderProgram, - 'u_clipMatrix' - ); - this._shaderSets.at( - 3 - ).uniformChannelFlagLocation = this.gl.getUniformLocation( - this._shaderSets.at(3).shaderProgram, - 'u_channelFlag' - ); - this._shaderSets.at( - 3 - ).uniformBaseColorLocation = this.gl.getUniformLocation( - this._shaderSets.at(3).shaderProgram, - 'u_baseColor' - ); - - // 加算(PremultipliedAlpha) - this._shaderSets.at( - 4 - ).attributePositionLocation = this.gl.getAttribLocation( - this._shaderSets.at(4).shaderProgram, - 'a_position' - ); - this._shaderSets.at( - 4 - ).attributeTexCoordLocation = this.gl.getAttribLocation( - this._shaderSets.at(4).shaderProgram, - 'a_texCoord' - ); - this._shaderSets.at( - 4 - ).samplerTexture0Location = this.gl.getUniformLocation( - this._shaderSets.at(4).shaderProgram, - 's_texture0' - ); - this._shaderSets.at(4).uniformMatrixLocation = this.gl.getUniformLocation( - this._shaderSets.at(4).shaderProgram, - 'u_matrix' - ); - this._shaderSets.at( - 4 - ).uniformBaseColorLocation = this.gl.getUniformLocation( - this._shaderSets.at(4).shaderProgram, - 'u_baseColor' - ); - - // 加算(クリッピング、PremultipliedAlpha) - this._shaderSets.at( - 5 - ).attributePositionLocation = this.gl.getAttribLocation( - this._shaderSets.at(5).shaderProgram, - 'a_position' - ); - this._shaderSets.at( - 5 - ).attributeTexCoordLocation = this.gl.getAttribLocation( - this._shaderSets.at(5).shaderProgram, - 'a_texCoord' - ); - this._shaderSets.at( - 5 - ).samplerTexture0Location = this.gl.getUniformLocation( - this._shaderSets.at(5).shaderProgram, - 's_texture0' - ); - this._shaderSets.at( - 5 - ).samplerTexture1Location = this.gl.getUniformLocation( - this._shaderSets.at(5).shaderProgram, - 's_texture1' - ); - this._shaderSets.at(5).uniformMatrixLocation = this.gl.getUniformLocation( - this._shaderSets.at(5).shaderProgram, - 'u_matrix' - ); - this._shaderSets.at( - 5 - ).uniformClipMatrixLocation = this.gl.getUniformLocation( - this._shaderSets.at(5).shaderProgram, - 'u_clipMatrix' - ); - this._shaderSets.at( - 5 - ).uniformChannelFlagLocation = this.gl.getUniformLocation( - this._shaderSets.at(5).shaderProgram, - 'u_channelFlag' - ); - this._shaderSets.at( - 5 - ).uniformBaseColorLocation = this.gl.getUniformLocation( - this._shaderSets.at(5).shaderProgram, - 'u_baseColor' - ); - - // 加算(クリッピング・反転、PremultipliedAlpha) - this._shaderSets.at( - 6 - ).attributePositionLocation = this.gl.getAttribLocation( - this._shaderSets.at(6).shaderProgram, - 'a_position' - ); - this._shaderSets.at( - 6 - ).attributeTexCoordLocation = this.gl.getAttribLocation( - this._shaderSets.at(6).shaderProgram, - 'a_texCoord' - ); - this._shaderSets.at( - 6 - ).samplerTexture0Location = this.gl.getUniformLocation( - this._shaderSets.at(6).shaderProgram, - 's_texture0' - ); - this._shaderSets.at( - 6 - ).samplerTexture1Location = this.gl.getUniformLocation( - this._shaderSets.at(6).shaderProgram, - 's_texture1' - ); - this._shaderSets.at(6).uniformMatrixLocation = this.gl.getUniformLocation( - this._shaderSets.at(6).shaderProgram, - 'u_matrix' - ); - this._shaderSets.at( - 6 - ).uniformClipMatrixLocation = this.gl.getUniformLocation( - this._shaderSets.at(6).shaderProgram, - 'u_clipMatrix' - ); - this._shaderSets.at( - 6 - ).uniformChannelFlagLocation = this.gl.getUniformLocation( - this._shaderSets.at(6).shaderProgram, - 'u_channelFlag' - ); - this._shaderSets.at( - 6 - ).uniformBaseColorLocation = this.gl.getUniformLocation( - this._shaderSets.at(6).shaderProgram, - 'u_baseColor' - ); - - // 乗算(PremultipliedAlpha) - this._shaderSets.at( - 7 - ).attributePositionLocation = this.gl.getAttribLocation( - this._shaderSets.at(7).shaderProgram, - 'a_position' - ); - this._shaderSets.at( - 7 - ).attributeTexCoordLocation = this.gl.getAttribLocation( - this._shaderSets.at(7).shaderProgram, - 'a_texCoord' - ); - this._shaderSets.at( - 7 - ).samplerTexture0Location = this.gl.getUniformLocation( - this._shaderSets.at(7).shaderProgram, - 's_texture0' - ); - this._shaderSets.at(7).uniformMatrixLocation = this.gl.getUniformLocation( - this._shaderSets.at(7).shaderProgram, - 'u_matrix' - ); - this._shaderSets.at( - 7 - ).uniformBaseColorLocation = this.gl.getUniformLocation( - this._shaderSets.at(7).shaderProgram, - 'u_baseColor' - ); - - // 乗算(クリッピング、PremultipliedAlpha) - this._shaderSets.at( - 8 - ).attributePositionLocation = this.gl.getAttribLocation( - this._shaderSets.at(8).shaderProgram, - 'a_position' - ); - this._shaderSets.at( - 8 - ).attributeTexCoordLocation = this.gl.getAttribLocation( - this._shaderSets.at(8).shaderProgram, - 'a_texCoord' - ); - this._shaderSets.at( - 8 - ).samplerTexture0Location = this.gl.getUniformLocation( - this._shaderSets.at(8).shaderProgram, - 's_texture0' - ); - this._shaderSets.at( - 8 - ).samplerTexture1Location = this.gl.getUniformLocation( - this._shaderSets.at(8).shaderProgram, - 's_texture1' - ); - this._shaderSets.at(8).uniformMatrixLocation = this.gl.getUniformLocation( - this._shaderSets.at(8).shaderProgram, - 'u_matrix' - ); - this._shaderSets.at( - 8 - ).uniformClipMatrixLocation = this.gl.getUniformLocation( - this._shaderSets.at(8).shaderProgram, - 'u_clipMatrix' - ); - this._shaderSets.at( - 8 - ).uniformChannelFlagLocation = this.gl.getUniformLocation( - this._shaderSets.at(8).shaderProgram, - 'u_channelFlag' - ); - this._shaderSets.at( - 8 - ).uniformBaseColorLocation = this.gl.getUniformLocation( - this._shaderSets.at(8).shaderProgram, - 'u_baseColor' - ); - - // 乗算(クリッピング・反転、PremultipliedAlpha) - this._shaderSets.at( - 9 - ).attributePositionLocation = this.gl.getAttribLocation( - this._shaderSets.at(9).shaderProgram, - 'a_position' - ); - this._shaderSets.at( - 9 - ).attributeTexCoordLocation = this.gl.getAttribLocation( - this._shaderSets.at(9).shaderProgram, - 'a_texCoord' - ); - this._shaderSets.at( - 9 - ).samplerTexture0Location = this.gl.getUniformLocation( - this._shaderSets.at(9).shaderProgram, - 's_texture0' - ); - this._shaderSets.at( - 9 - ).samplerTexture1Location = this.gl.getUniformLocation( - this._shaderSets.at(9).shaderProgram, - 's_texture1' - ); - this._shaderSets.at(9).uniformMatrixLocation = this.gl.getUniformLocation( - this._shaderSets.at(9).shaderProgram, - 'u_matrix' - ); - this._shaderSets.at( - 9 - ).uniformClipMatrixLocation = this.gl.getUniformLocation( - this._shaderSets.at(9).shaderProgram, - 'u_clipMatrix' - ); - this._shaderSets.at( - 9 - ).uniformChannelFlagLocation = this.gl.getUniformLocation( - this._shaderSets.at(9).shaderProgram, - 'u_channelFlag' - ); - this._shaderSets.at( - 9 - ).uniformBaseColorLocation = this.gl.getUniformLocation( - this._shaderSets.at(9).shaderProgram, - 'u_baseColor' + this.gl.uniform4f( + shaderSet.uniformBaseColorLocation, + baseColor.R, + baseColor.G, + baseColor.B, + baseColor.A ); } - /** - * シェーダプログラムをロードしてアドレスを返す - * @param vertexShaderSource 頂点シェーダのソース - * @param fragmentShaderSource フラグメントシェーダのソース - * @return シェーダプログラムのアドレス - */ - public loadShaderProgram( - vertexShaderSource: string, - fragmentShaderSource: string - ): WebGLProgram { - // Create Shader Program - let shaderProgram: WebGLProgram = this.gl.createProgram(); - - let vertShader = this.compileShaderSource( - this.gl.VERTEX_SHADER, - vertexShaderSource - ); - - if (!vertShader) { - CubismLogError('Vertex shader compile error!'); - return 0; - } - - let fragShader = this.compileShaderSource( - this.gl.FRAGMENT_SHADER, - fragmentShaderSource - ); - if (!fragShader) { - CubismLogError('Vertex shader compile error!'); - return 0; - } - - // Attach vertex shader to program - this.gl.attachShader(shaderProgram, vertShader); - - // Attach fragment shader to program - this.gl.attachShader(shaderProgram, fragShader); - - // link program - this.gl.linkProgram(shaderProgram); - const linkStatus = this.gl.getProgramParameter( - shaderProgram, - this.gl.LINK_STATUS - ); - - // リンクに失敗したらシェーダーを削除 - if (!linkStatus) { - CubismLogError('Failed to link program: {0}', shaderProgram); - - this.gl.deleteShader(vertShader); - vertShader = 0; - - this.gl.deleteShader(fragShader); - fragShader = 0; - - if (shaderProgram) { - this.gl.deleteProgram(shaderProgram); - shaderProgram = 0; - } - - return 0; - } - - // Release vertex and fragment shaders. - this.gl.deleteShader(vertShader); - this.gl.deleteShader(fragShader); - - return shaderProgram; + // IBOを作成し、データを転送 + if (bufferData.index == null) { + bufferData.index = this.gl.createBuffer(); } - - /** - * シェーダープログラムをコンパイルする - * @param shaderType シェーダタイプ(Vertex/Fragment) - * @param shaderSource シェーダソースコード - * - * @return コンパイルされたシェーダープログラム - */ - public compileShaderSource( - shaderType: GLenum, - shaderSource: string - ): WebGLProgram { - const source: string = shaderSource; - - const shader: WebGLProgram = this.gl.createShader(shaderType); - this.gl.shaderSource(shader, source); - this.gl.compileShader(shader); - - if (!shader) { - const log: string = this.gl.getShaderInfoLog(shader); - CubismLogError('Shader compile log: {0} ', log); - } - - const status: any = this.gl.getShaderParameter( - shader, - this.gl.COMPILE_STATUS - ); - if (!status) { - this.gl.deleteShader(shader); - return null; - } - - return shader; - } - - public setGl(gl: WebGLRenderingContext): void { - this.gl = gl; - } - - _shaderSets: csmVector; // ロードしたシェーダープログラムを保持する変数 - gl: WebGLRenderingContext; // webglコンテキスト + this.gl.bindBuffer(this.gl.ELEMENT_ARRAY_BUFFER, bufferData.index); + this.gl.bufferData( + this.gl.ELEMENT_ARRAY_BUFFER, + indexArray, + this.gl.DYNAMIC_DRAW + ); + this.gl.blendFuncSeparate(SRC_COLOR, DST_COLOR, SRC_ALPHA, DST_ALPHA); } /** - * CubismShader_WebGLのインナークラス + * シェーダープログラムを解放する */ - export class CubismShaderSet { - shaderProgram: WebGLProgram; // シェーダープログラムのアドレス - attributePositionLocation: GLuint; // シェーダープログラムに渡す変数のアドレス(Position) - attributeTexCoordLocation: GLuint; // シェーダープログラムに渡す変数のアドレス(TexCoord) - uniformMatrixLocation: WebGLUniformLocation; // シェーダープログラムに渡す変数のアドレス(Matrix) - uniformClipMatrixLocation: WebGLUniformLocation; // シェーダープログラムに渡す変数のアドレス(ClipMatrix) - samplerTexture0Location: WebGLUniformLocation; // シェーダープログラムに渡す変数のアドレス(Texture0) - samplerTexture1Location: WebGLUniformLocation; // シェーダープログラムに渡す変数のアドレス(Texture1) - uniformBaseColorLocation: WebGLUniformLocation; // シェーダープログラムに渡す変数のアドレス(BaseColor) - uniformChannelFlagLocation: WebGLUniformLocation; // シェーダープログラムに渡す変数のアドレス(ChannelFlag) + public releaseShaderProgram(): void { + for (let i = 0; i < this._shaderSets.getSize(); i++) { + this.gl.deleteProgram(this._shaderSets.at(i).shaderProgram); + this._shaderSets.at(i).shaderProgram = 0; + this._shaderSets.set(i, void 0); + this._shaderSets.set(i, null); + } } - export enum ShaderNames { + /** + * シェーダープログラムを初期化する + * @param vertShaderSrc 頂点シェーダのソース + * @param fragShaderSrc フラグメントシェーダのソース + */ + public generateShaders(): void { + for (let i = 0; i < shaderCount; i++) { + this._shaderSets.pushBack(new CubismShaderSet()); + } + + this._shaderSets.at(0).shaderProgram = this.loadShaderProgram( + vertexShaderSrcSetupMask, + fragmentShaderSrcsetupMask + ); + + this._shaderSets.at(1).shaderProgram = this.loadShaderProgram( + vertexShaderSrc, + fragmentShaderSrcPremultipliedAlpha + ); + this._shaderSets.at(2).shaderProgram = this.loadShaderProgram( + vertexShaderSrcMasked, + fragmentShaderSrcMaskPremultipliedAlpha + ); + this._shaderSets.at(3).shaderProgram = this.loadShaderProgram( + vertexShaderSrcMasked, + fragmentShaderSrcMaskInvertedPremultipliedAlpha + ); + + // 加算も通常と同じシェーダーを利用する + this._shaderSets.at(4).shaderProgram = this._shaderSets.at(1).shaderProgram; + this._shaderSets.at(5).shaderProgram = this._shaderSets.at(2).shaderProgram; + this._shaderSets.at(6).shaderProgram = this._shaderSets.at(3).shaderProgram; + + // 乗算も通常と同じシェーダーを利用する + this._shaderSets.at(7).shaderProgram = this._shaderSets.at(1).shaderProgram; + this._shaderSets.at(8).shaderProgram = this._shaderSets.at(2).shaderProgram; + this._shaderSets.at(9).shaderProgram = this._shaderSets.at(3).shaderProgram; + // SetupMask - ShaderNames_SetupMask, + this._shaderSets.at( + 0 + ).attributePositionLocation = this.gl.getAttribLocation( + this._shaderSets.at(0).shaderProgram, + 'a_position' + ); + this._shaderSets.at( + 0 + ).attributeTexCoordLocation = this.gl.getAttribLocation( + this._shaderSets.at(0).shaderProgram, + 'a_texCoord' + ); + this._shaderSets.at(0).samplerTexture0Location = this.gl.getUniformLocation( + this._shaderSets.at(0).shaderProgram, + 's_texture0' + ); + this._shaderSets.at( + 0 + ).uniformClipMatrixLocation = this.gl.getUniformLocation( + this._shaderSets.at(0).shaderProgram, + 'u_clipMatrix' + ); + this._shaderSets.at( + 0 + ).uniformChannelFlagLocation = this.gl.getUniformLocation( + this._shaderSets.at(0).shaderProgram, + 'u_channelFlag' + ); + this._shaderSets.at( + 0 + ).uniformBaseColorLocation = this.gl.getUniformLocation( + this._shaderSets.at(0).shaderProgram, + 'u_baseColor' + ); - // Normal - ShaderNames_NormalPremultipliedAlpha, - ShaderNames_NormalMaskedPremultipliedAlpha, - ShaderNames_NomralMaskedInvertedPremultipliedAlpha, + // 通常(PremultipliedAlpha) + this._shaderSets.at( + 1 + ).attributePositionLocation = this.gl.getAttribLocation( + this._shaderSets.at(1).shaderProgram, + 'a_position' + ); + this._shaderSets.at( + 1 + ).attributeTexCoordLocation = this.gl.getAttribLocation( + this._shaderSets.at(1).shaderProgram, + 'a_texCoord' + ); + this._shaderSets.at(1).samplerTexture0Location = this.gl.getUniformLocation( + this._shaderSets.at(1).shaderProgram, + 's_texture0' + ); + this._shaderSets.at(1).uniformMatrixLocation = this.gl.getUniformLocation( + this._shaderSets.at(1).shaderProgram, + 'u_matrix' + ); + this._shaderSets.at( + 1 + ).uniformBaseColorLocation = this.gl.getUniformLocation( + this._shaderSets.at(1).shaderProgram, + 'u_baseColor' + ); - // Add - ShaderNames_AddPremultipliedAlpha, - ShaderNames_AddMaskedPremultipliedAlpha, - ShaderNames_AddMaskedPremultipliedAlphaInverted, + // 通常(クリッピング、PremultipliedAlpha) + this._shaderSets.at( + 2 + ).attributePositionLocation = this.gl.getAttribLocation( + this._shaderSets.at(2).shaderProgram, + 'a_position' + ); + this._shaderSets.at( + 2 + ).attributeTexCoordLocation = this.gl.getAttribLocation( + this._shaderSets.at(2).shaderProgram, + 'a_texCoord' + ); + this._shaderSets.at(2).samplerTexture0Location = this.gl.getUniformLocation( + this._shaderSets.at(2).shaderProgram, + 's_texture0' + ); + this._shaderSets.at(2).samplerTexture1Location = this.gl.getUniformLocation( + this._shaderSets.at(2).shaderProgram, + 's_texture1' + ); + this._shaderSets.at(2).uniformMatrixLocation = this.gl.getUniformLocation( + this._shaderSets.at(2).shaderProgram, + 'u_matrix' + ); + this._shaderSets.at( + 2 + ).uniformClipMatrixLocation = this.gl.getUniformLocation( + this._shaderSets.at(2).shaderProgram, + 'u_clipMatrix' + ); + this._shaderSets.at( + 2 + ).uniformChannelFlagLocation = this.gl.getUniformLocation( + this._shaderSets.at(2).shaderProgram, + 'u_channelFlag' + ); + this._shaderSets.at( + 2 + ).uniformBaseColorLocation = this.gl.getUniformLocation( + this._shaderSets.at(2).shaderProgram, + 'u_baseColor' + ); - // Mult - ShaderNames_MultPremultipliedAlpha, - ShaderNames_MultMaskedPremultipliedAlpha, - ShaderNames_MultMaskedPremultipliedAlphaInverted - } + // 通常(クリッピング・反転, PremultipliedAlpha) + this._shaderSets.at( + 3 + ).attributePositionLocation = this.gl.getAttribLocation( + this._shaderSets.at(3).shaderProgram, + 'a_position' + ); + this._shaderSets.at( + 3 + ).attributeTexCoordLocation = this.gl.getAttribLocation( + this._shaderSets.at(3).shaderProgram, + 'a_texCoord' + ); + this._shaderSets.at(3).samplerTexture0Location = this.gl.getUniformLocation( + this._shaderSets.at(3).shaderProgram, + 's_texture0' + ); + this._shaderSets.at(3).samplerTexture1Location = this.gl.getUniformLocation( + this._shaderSets.at(3).shaderProgram, + 's_texture1' + ); + this._shaderSets.at(3).uniformMatrixLocation = this.gl.getUniformLocation( + this._shaderSets.at(3).shaderProgram, + 'u_matrix' + ); + this._shaderSets.at( + 3 + ).uniformClipMatrixLocation = this.gl.getUniformLocation( + this._shaderSets.at(3).shaderProgram, + 'u_clipMatrix' + ); + this._shaderSets.at( + 3 + ).uniformChannelFlagLocation = this.gl.getUniformLocation( + this._shaderSets.at(3).shaderProgram, + 'u_channelFlag' + ); + this._shaderSets.at( + 3 + ).uniformBaseColorLocation = this.gl.getUniformLocation( + this._shaderSets.at(3).shaderProgram, + 'u_baseColor' + ); - export const vertexShaderSrcSetupMask = - 'attribute vec4 a_position;' + - 'attribute vec2 a_texCoord;' + - 'varying vec2 v_texCoord;' + - 'varying vec4 v_myPos;' + - 'uniform mat4 u_clipMatrix;' + - 'void main()' + - '{' + - ' gl_Position = u_clipMatrix * a_position;' + - ' v_myPos = u_clipMatrix * a_position;' + - ' v_texCoord = a_texCoord;' + - ' v_texCoord.y = 1.0 - v_texCoord.y;' + - '}'; - export const fragmentShaderSrcsetupMask = - 'precision mediump float;' + - 'varying vec2 v_texCoord;' + - 'varying vec4 v_myPos;' + - 'uniform vec4 u_baseColor;' + - 'uniform vec4 u_channelFlag;' + - 'uniform sampler2D s_texture0;' + - 'void main()' + - '{' + - ' float isInside = ' + - ' step(u_baseColor.x, v_myPos.x/v_myPos.w)' + - ' * step(u_baseColor.y, v_myPos.y/v_myPos.w)' + - ' * step(v_myPos.x/v_myPos.w, u_baseColor.z)' + - ' * step(v_myPos.y/v_myPos.w, u_baseColor.w);' + - ' gl_FragColor = u_channelFlag * texture2D(s_texture0, v_texCoord).a * isInside;' + - '}'; + // 加算(PremultipliedAlpha) + this._shaderSets.at( + 4 + ).attributePositionLocation = this.gl.getAttribLocation( + this._shaderSets.at(4).shaderProgram, + 'a_position' + ); + this._shaderSets.at( + 4 + ).attributeTexCoordLocation = this.gl.getAttribLocation( + this._shaderSets.at(4).shaderProgram, + 'a_texCoord' + ); + this._shaderSets.at(4).samplerTexture0Location = this.gl.getUniformLocation( + this._shaderSets.at(4).shaderProgram, + 's_texture0' + ); + this._shaderSets.at(4).uniformMatrixLocation = this.gl.getUniformLocation( + this._shaderSets.at(4).shaderProgram, + 'u_matrix' + ); + this._shaderSets.at( + 4 + ).uniformBaseColorLocation = this.gl.getUniformLocation( + this._shaderSets.at(4).shaderProgram, + 'u_baseColor' + ); - //----- バーテックスシェーダプログラム ----- - // Normal & Add & Mult 共通 - export const vertexShaderSrc = - 'attribute vec4 a_position;' + //v.vertex - 'attribute vec2 a_texCoord;' + //v.texcoord - 'varying vec2 v_texCoord;' + //v2f.texcoord - 'uniform mat4 u_matrix;' + - 'void main()' + - '{' + - ' gl_Position = u_matrix * a_position;' + - ' v_texCoord = a_texCoord;' + - ' v_texCoord.y = 1.0 - v_texCoord.y;' + - '}'; + // 加算(クリッピング、PremultipliedAlpha) + this._shaderSets.at( + 5 + ).attributePositionLocation = this.gl.getAttribLocation( + this._shaderSets.at(5).shaderProgram, + 'a_position' + ); + this._shaderSets.at( + 5 + ).attributeTexCoordLocation = this.gl.getAttribLocation( + this._shaderSets.at(5).shaderProgram, + 'a_texCoord' + ); + this._shaderSets.at(5).samplerTexture0Location = this.gl.getUniformLocation( + this._shaderSets.at(5).shaderProgram, + 's_texture0' + ); + this._shaderSets.at(5).samplerTexture1Location = this.gl.getUniformLocation( + this._shaderSets.at(5).shaderProgram, + 's_texture1' + ); + this._shaderSets.at(5).uniformMatrixLocation = this.gl.getUniformLocation( + this._shaderSets.at(5).shaderProgram, + 'u_matrix' + ); + this._shaderSets.at( + 5 + ).uniformClipMatrixLocation = this.gl.getUniformLocation( + this._shaderSets.at(5).shaderProgram, + 'u_clipMatrix' + ); + this._shaderSets.at( + 5 + ).uniformChannelFlagLocation = this.gl.getUniformLocation( + this._shaderSets.at(5).shaderProgram, + 'u_channelFlag' + ); + this._shaderSets.at( + 5 + ).uniformBaseColorLocation = this.gl.getUniformLocation( + this._shaderSets.at(5).shaderProgram, + 'u_baseColor' + ); - // Normal & Add & Mult 共通(クリッピングされたものの描画用) - export const vertexShaderSrcMasked = - 'attribute vec4 a_position;' + - 'attribute vec2 a_texCoord;' + - 'varying vec2 v_texCoord;' + - 'varying vec4 v_clipPos;' + - 'uniform mat4 u_matrix;' + - 'uniform mat4 u_clipMatrix;' + - 'void main()' + - '{' + - ' gl_Position = u_matrix * a_position;' + - ' v_clipPos = u_clipMatrix * a_position;' + - ' v_texCoord = a_texCoord;' + - ' v_texCoord.y = 1.0 - v_texCoord.y;' + - '}'; + // 加算(クリッピング・反転、PremultipliedAlpha) + this._shaderSets.at( + 6 + ).attributePositionLocation = this.gl.getAttribLocation( + this._shaderSets.at(6).shaderProgram, + 'a_position' + ); + this._shaderSets.at( + 6 + ).attributeTexCoordLocation = this.gl.getAttribLocation( + this._shaderSets.at(6).shaderProgram, + 'a_texCoord' + ); + this._shaderSets.at(6).samplerTexture0Location = this.gl.getUniformLocation( + this._shaderSets.at(6).shaderProgram, + 's_texture0' + ); + this._shaderSets.at(6).samplerTexture1Location = this.gl.getUniformLocation( + this._shaderSets.at(6).shaderProgram, + 's_texture1' + ); + this._shaderSets.at(6).uniformMatrixLocation = this.gl.getUniformLocation( + this._shaderSets.at(6).shaderProgram, + 'u_matrix' + ); + this._shaderSets.at( + 6 + ).uniformClipMatrixLocation = this.gl.getUniformLocation( + this._shaderSets.at(6).shaderProgram, + 'u_clipMatrix' + ); + this._shaderSets.at( + 6 + ).uniformChannelFlagLocation = this.gl.getUniformLocation( + this._shaderSets.at(6).shaderProgram, + 'u_channelFlag' + ); + this._shaderSets.at( + 6 + ).uniformBaseColorLocation = this.gl.getUniformLocation( + this._shaderSets.at(6).shaderProgram, + 'u_baseColor' + ); - //----- フラグメントシェーダプログラム ----- - // Normal & Add & Mult 共通 (PremultipliedAlpha) - export const fragmentShaderSrcPremultipliedAlpha = - 'precision mediump float;' + - 'varying vec2 v_texCoord;' + //v2f.texcoord - 'uniform vec4 u_baseColor;' + - 'uniform sampler2D s_texture0;' + //_MainTex - 'void main()' + - '{' + - ' gl_FragColor = texture2D(s_texture0 , v_texCoord) * u_baseColor;' + - '}'; + // 乗算(PremultipliedAlpha) + this._shaderSets.at( + 7 + ).attributePositionLocation = this.gl.getAttribLocation( + this._shaderSets.at(7).shaderProgram, + 'a_position' + ); + this._shaderSets.at( + 7 + ).attributeTexCoordLocation = this.gl.getAttribLocation( + this._shaderSets.at(7).shaderProgram, + 'a_texCoord' + ); + this._shaderSets.at(7).samplerTexture0Location = this.gl.getUniformLocation( + this._shaderSets.at(7).shaderProgram, + 's_texture0' + ); + this._shaderSets.at(7).uniformMatrixLocation = this.gl.getUniformLocation( + this._shaderSets.at(7).shaderProgram, + 'u_matrix' + ); + this._shaderSets.at( + 7 + ).uniformBaseColorLocation = this.gl.getUniformLocation( + this._shaderSets.at(7).shaderProgram, + 'u_baseColor' + ); - // Normal (クリッピングされたものの描画用、PremultipliedAlpha兼用) - export const fragmentShaderSrcMaskPremultipliedAlpha = - 'precision mediump float;' + - 'varying vec2 v_texCoord;' + - 'varying vec4 v_clipPos;' + - 'uniform vec4 u_baseColor;' + - 'uniform vec4 u_channelFlag;' + - 'uniform sampler2D s_texture0;' + - 'uniform sampler2D s_texture1;' + - 'void main()' + - '{' + - ' vec4 col_formask = texture2D(s_texture0 , v_texCoord) * u_baseColor;' + - ' vec4 clipMask = (1.0 - texture2D(s_texture1, v_clipPos.xy / v_clipPos.w)) * u_channelFlag;' + - ' float maskVal = clipMask.r + clipMask.g + clipMask.b + clipMask.a;' + - ' col_formask = col_formask * maskVal;' + - ' gl_FragColor = col_formask;' + - '}'; + // 乗算(クリッピング、PremultipliedAlpha) + this._shaderSets.at( + 8 + ).attributePositionLocation = this.gl.getAttribLocation( + this._shaderSets.at(8).shaderProgram, + 'a_position' + ); + this._shaderSets.at( + 8 + ).attributeTexCoordLocation = this.gl.getAttribLocation( + this._shaderSets.at(8).shaderProgram, + 'a_texCoord' + ); + this._shaderSets.at(8).samplerTexture0Location = this.gl.getUniformLocation( + this._shaderSets.at(8).shaderProgram, + 's_texture0' + ); + this._shaderSets.at(8).samplerTexture1Location = this.gl.getUniformLocation( + this._shaderSets.at(8).shaderProgram, + 's_texture1' + ); + this._shaderSets.at(8).uniformMatrixLocation = this.gl.getUniformLocation( + this._shaderSets.at(8).shaderProgram, + 'u_matrix' + ); + this._shaderSets.at( + 8 + ).uniformClipMatrixLocation = this.gl.getUniformLocation( + this._shaderSets.at(8).shaderProgram, + 'u_clipMatrix' + ); + this._shaderSets.at( + 8 + ).uniformChannelFlagLocation = this.gl.getUniformLocation( + this._shaderSets.at(8).shaderProgram, + 'u_channelFlag' + ); + this._shaderSets.at( + 8 + ).uniformBaseColorLocation = this.gl.getUniformLocation( + this._shaderSets.at(8).shaderProgram, + 'u_baseColor' + ); - // Normal & Add & Mult 共通(クリッピングされて反転使用の描画用、PremultipliedAlphaの場合) - export const fragmentShaderSrcMaskInvertedPremultipliedAlpha = - 'precision mediump float;' + - 'varying vec2 v_texCoord;' + - 'varying vec4 v_clipPos;' + - 'uniform sampler2D s_texture0;' + - 'uniform sampler2D s_texture1;' + - 'uniform vec4 u_channelFlag;' + - 'uniform vec4 u_baseColor;' + - 'void main()' + - '{' + - 'vec4 col_formask = texture2D(s_texture0, v_texCoord) * u_baseColor;' + - 'vec4 clipMask = (1.0 - texture2D(s_texture1, v_clipPos.xy / v_clipPos.w)) * u_channelFlag;' + - 'float maskVal = clipMask.r + clipMask.g + clipMask.b + clipMask.a;' + - 'col_formask = col_formask * (1.0 - maskVal);' + - 'gl_FragColor = col_formask;' + - '}'; - - /** - * WebGL用の描画命令を実装したクラス - */ - export class CubismRenderer_WebGL extends CubismRenderer { - /** - * レンダラの初期化処理を実行する - * 引数に渡したモデルからレンダラの初期化処理に必要な情報を取り出すことができる - * - * @param model モデルのインスタンス - */ - public initialize(model: CubismModel): void { - if (model.isUsingMasking()) { - this._clippingManager = new CubismClippingManager_WebGL(); // クリッピングマスク・バッファ前処理方式を初期化 - this._clippingManager.initialize( - model, - model.getDrawableCount(), - model.getDrawableMasks(), - model.getDrawableMaskCounts() - ); - } - - this._sortedDrawableIndexList.resize(model.getDrawableCount(), 0); - - super.initialize(model); // 親クラスの処理を呼ぶ - } - - /** - * WebGLテクスチャのバインド処理 - * CubismRendererにテクスチャを設定し、CubismRenderer内でその画像を参照するためのIndex値を戻り値とする - * @param modelTextureNo セットするモデルテクスチャの番号 - * @param glTextureNo WebGLテクスチャの番号 - */ - public bindTexture(modelTextureNo: number, glTexture: WebGLTexture): void { - this._textures.setValue(modelTextureNo, glTexture); - } - - /** - * WebGLにバインドされたテクスチャのリストを取得する - * @return テクスチャのリスト - */ - public getBindedTextures(): csmMap { - return this._textures; - } - - /** - * クリッピングマスクバッファのサイズを設定する - * マスク用のFrameBufferを破棄、再作成する為処理コストは高い - * @param size クリッピングマスクバッファのサイズ - */ - public setClippingMaskBufferSize(size: number) { - // FrameBufferのサイズを変更するためにインスタンスを破棄・再作成する - this._clippingManager.release(); - this._clippingManager = void 0; - this._clippingManager = null; - - this._clippingManager = new CubismClippingManager_WebGL(); - - this._clippingManager.setClippingMaskBufferSize(size); - - this._clippingManager.initialize( - this.getModel(), - this.getModel().getDrawableCount(), - this.getModel().getDrawableMasks(), - this.getModel().getDrawableMaskCounts() - ); - } - - /** - * クリッピングマスクバッファのサイズを取得する - * @return クリッピングマスクバッファのサイズ - */ - public getClippingMaskBufferSize(): number { - return this._clippingManager.getClippingMaskBufferSize(); - } - - /** - * コンストラクタ - */ - public constructor() { - super(); - this._clippingContextBufferForMask = null; - this._clippingContextBufferForDraw = null; - this._clippingManager = new CubismClippingManager_WebGL(); - this.firstDraw = true; - this._textures = new csmMap(); - this._sortedDrawableIndexList = new csmVector(); - this._bufferData = { - vertex: WebGLBuffer = null, - uv: WebGLBuffer = null, - index: WebGLBuffer = null - }; - - // テクスチャ対応マップの容量を確保しておく - this._textures.prepareCapacity(32, true); - } - - /** - * デストラクタ相当の処理 - */ - public release(): void { - this._clippingManager.release(); - this._clippingManager = void 0; - this._clippingManager = null; - - this.gl.deleteBuffer(this._bufferData.vertex); - this._bufferData.vertex = null; - this.gl.deleteBuffer(this._bufferData.uv); - this._bufferData.uv = null; - this.gl.deleteBuffer(this._bufferData.index); - this._bufferData.index = null; - this._bufferData = null; - - this._textures = null; - } - - /** - * モデルを描画する実際の処理 - */ - public doDrawModel(): void { - //------------ クリッピングマスク・バッファ前処理方式の場合 ------------ - if (this._clippingManager != null) { - this.preDraw(); - this._clippingManager.setupClippingContext(this.getModel(), this); - } - - // 上記クリッピング処理内でも一度PreDrawを呼ぶので注意!! - this.preDraw(); - - const drawableCount: number = this.getModel().getDrawableCount(); - const renderOrder: Int32Array = this.getModel().getDrawableRenderOrders(); - - // インデックスを描画順でソート - for (let i = 0; i < drawableCount; ++i) { - const order: number = renderOrder[i]; - this._sortedDrawableIndexList.set(order, i); - } - - // 描画 - for (let i = 0; i < drawableCount; ++i) { - const drawableIndex: number = this._sortedDrawableIndexList.at(i); - - // Drawableが表示状態でなければ処理をパスする - if (!this.getModel().getDrawableDynamicFlagIsVisible(drawableIndex)) { - continue; - } - - // クリッピングマスクをセットする - this.setClippingContextBufferForDraw( - this._clippingManager != null - ? this._clippingManager - .getClippingContextListForDraw() - .at(drawableIndex) - : null - ); - - this.setIsCulling(this.getModel().getDrawableCulling(drawableIndex)); - - this.drawMesh( - this.getModel().getDrawableTextureIndices(drawableIndex), - this.getModel().getDrawableVertexIndexCount(drawableIndex), - this.getModel().getDrawableVertexCount(drawableIndex), - this.getModel().getDrawableVertexIndices(drawableIndex), - this.getModel().getDrawableVertices(drawableIndex), - this.getModel().getDrawableVertexUvs(drawableIndex), - this.getModel().getDrawableOpacity(drawableIndex), - this.getModel().getDrawableBlendMode(drawableIndex), - this.getModel().getDrawableInvertedMaskBit(drawableIndex) - ); - } - } - - /** - * [オーバーライド] - * 描画オブジェクト(アートメッシュ)を描画する。 - * ポリゴンメッシュとテクスチャ番号をセットで渡す。 - * @param textureNo 描画するテクスチャ番号 - * @param indexCount 描画オブジェクトのインデックス値 - * @param vertexCount ポリゴンメッシュの頂点数 - * @param indexArray ポリゴンメッシュのインデックス配列 - * @param vertexArray ポリゴンメッシュの頂点配列 - * @param uvArray uv配列 - * @param opacity 不透明度 - * @param colorBlendMode カラー合成タイプ - * @param invertedMask マスク使用時のマスクの反転使用 - */ - public drawMesh( - textureNo: number, - indexCount: number, - vertexCount: number, - indexArray: Uint16Array, - vertexArray: Float32Array, - uvArray: Float32Array, - opacity: number, - colorBlendMode: CubismBlendMode, - invertedMask: boolean - ): void { - // 裏面描画の有効・無効 - if (this.isCulling()) { - this.gl.enable(this.gl.CULL_FACE); - } else { - this.gl.disable(this.gl.CULL_FACE); - } - - this.gl.frontFace(this.gl.CCW); // Cubism SDK OpenGLはマスク・アートメッシュ共にCCWが表面 - - const modelColorRGBA: CubismTextureColor = this.getModelColor(); - - if (this.getClippingContextBufferForMask() == null) { - // マスク生成時以外 - modelColorRGBA.A *= opacity; - if (this.isPremultipliedAlpha()) { - modelColorRGBA.R *= modelColorRGBA.A; - modelColorRGBA.G *= modelColorRGBA.A; - modelColorRGBA.B *= modelColorRGBA.A; - } - } - - let drawtexture: WebGLTexture; // シェーダに渡すテクスチャ - - // テクスチャマップからバインド済みテクスチャIDを取得 - // バインドされていなければダミーのテクスチャIDをセットする - if (this._textures.getValue(textureNo) != null) { - drawtexture = this._textures.getValue(textureNo); - } else { - drawtexture = null; - } - - CubismShader_WebGL.getInstance().setupShaderProgram( - this, - drawtexture, - vertexCount, - vertexArray, - indexArray, - uvArray, - this._bufferData, - opacity, - colorBlendMode, - modelColorRGBA, - this.isPremultipliedAlpha(), - this.getMvpMatrix(), - invertedMask - ); - - // ポリゴンメッシュを描画する - this.gl.drawElements( - this.gl.TRIANGLES, - indexCount, - this.gl.UNSIGNED_SHORT, - 0 - ); - - // 後処理 - this.gl.useProgram(null); - this.setClippingContextBufferForDraw(null); - this.setClippingContextBufferForMask(null); - } - - /** - * レンダラが保持する静的なリソースを解放する - * WebGLの静的なシェーダープログラムを解放する - */ - public static doStaticRelease(): void { - CubismShader_WebGL.deleteInstance(); - } - - /** - * レンダーステートを設定する - * @param fbo アプリケーション側で指定しているフレームバッファ - * @param viewport ビューポート - */ - public setRenderState(fbo: WebGLFramebuffer, viewport: number[]): void { - s_fbo = fbo; - s_viewport = viewport; - } - - /** - * 描画開始時の追加処理 - * モデルを描画する前にクリッピングマスクに必要な処理を実装している - */ - public preDraw(): void { - if (this.firstDraw) { - this.firstDraw = false; - - // 拡張機能を有効にする - this._anisortopy = - this.gl.getExtension('EXT_texture_filter_anisotropic') || - this.gl.getExtension('WEBKIT_EXT_texture_filter_anisotropic') || - this.gl.getExtension('MOZ_EXT_texture_filter_anisotropic'); - } - - this.gl.disable(this.gl.SCISSOR_TEST); - this.gl.disable(this.gl.STENCIL_TEST); - this.gl.disable(this.gl.DEPTH_TEST); - - // カリング(1.0beta3) - this.gl.frontFace(this.gl.CW); - - this.gl.enable(this.gl.BLEND); - this.gl.colorMask(true, true, true, true); - - this.gl.bindBuffer(this.gl.ARRAY_BUFFER, null); // 前にバッファがバインドされていたら破棄する必要がある - this.gl.bindBuffer(this.gl.ELEMENT_ARRAY_BUFFER, null); - } - - /** - * マスクテクスチャに描画するクリッピングコンテキストをセットする - */ - public setClippingContextBufferForMask(clip: CubismClippingContext) { - this._clippingContextBufferForMask = clip; - } - - /** - * マスクテクスチャに描画するクリッピングコンテキストを取得する - * @return マスクテクスチャに描画するクリッピングコンテキスト - */ - public getClippingContextBufferForMask(): CubismClippingContext { - return this._clippingContextBufferForMask; - } - - /** - * 画面上に描画するクリッピングコンテキストをセットする - */ - public setClippingContextBufferForDraw(clip: CubismClippingContext): void { - this._clippingContextBufferForDraw = clip; - } - - /** - * 画面上に描画するクリッピングコンテキストを取得する - * @return 画面上に描画するクリッピングコンテキスト - */ - public getClippingContextBufferForDraw(): CubismClippingContext { - return this._clippingContextBufferForDraw; - } - - /** - * glの設定 - */ - public startUp(gl: WebGLRenderingContext): void { - this.gl = gl; - this._clippingManager.setGL(gl); - CubismShader_WebGL.getInstance().setGl(gl); - } - - _textures: csmMap; // モデルが参照するテクスチャとレンダラでバインドしているテクスチャとのマップ - _sortedDrawableIndexList: csmVector; // 描画オブジェクトのインデックスを描画順に並べたリスト - _clippingManager: CubismClippingManager_WebGL; // クリッピングマスク管理オブジェクト - _clippingContextBufferForMask: CubismClippingContext; // マスクテクスチャに描画するためのクリッピングコンテキスト - _clippingContextBufferForDraw: CubismClippingContext; // 画面上描画するためのクリッピングコンテキスト - firstDraw: boolean; - _bufferData: { - vertex: WebGLBuffer; - uv: WebGLBuffer; - index: WebGLBuffer; - }; // 頂点バッファデータ - gl: WebGLRenderingContext; // webglコンテキスト + // 乗算(クリッピング・反転、PremultipliedAlpha) + this._shaderSets.at( + 9 + ).attributePositionLocation = this.gl.getAttribLocation( + this._shaderSets.at(9).shaderProgram, + 'a_position' + ); + this._shaderSets.at( + 9 + ).attributeTexCoordLocation = this.gl.getAttribLocation( + this._shaderSets.at(9).shaderProgram, + 'a_texCoord' + ); + this._shaderSets.at(9).samplerTexture0Location = this.gl.getUniformLocation( + this._shaderSets.at(9).shaderProgram, + 's_texture0' + ); + this._shaderSets.at(9).samplerTexture1Location = this.gl.getUniformLocation( + this._shaderSets.at(9).shaderProgram, + 's_texture1' + ); + this._shaderSets.at(9).uniformMatrixLocation = this.gl.getUniformLocation( + this._shaderSets.at(9).shaderProgram, + 'u_matrix' + ); + this._shaderSets.at( + 9 + ).uniformClipMatrixLocation = this.gl.getUniformLocation( + this._shaderSets.at(9).shaderProgram, + 'u_clipMatrix' + ); + this._shaderSets.at( + 9 + ).uniformChannelFlagLocation = this.gl.getUniformLocation( + this._shaderSets.at(9).shaderProgram, + 'u_channelFlag' + ); + this._shaderSets.at( + 9 + ).uniformBaseColorLocation = this.gl.getUniformLocation( + this._shaderSets.at(9).shaderProgram, + 'u_baseColor' + ); } /** - * レンダラが保持する静的なリソースを開放する + * シェーダプログラムをロードしてアドレスを返す + * @param vertexShaderSource 頂点シェーダのソース + * @param fragmentShaderSource フラグメントシェーダのソース + * @return シェーダプログラムのアドレス */ - CubismRenderer.staticRelease = (): void => { - CubismRenderer_WebGL.doStaticRelease(); - }; + public loadShaderProgram( + vertexShaderSource: string, + fragmentShaderSource: string + ): WebGLProgram { + // Create Shader Program + let shaderProgram: WebGLProgram = this.gl.createProgram(); + + let vertShader = this.compileShaderSource( + this.gl.VERTEX_SHADER, + vertexShaderSource + ); + + if (!vertShader) { + CubismLogError('Vertex shader compile error!'); + return 0; + } + + let fragShader = this.compileShaderSource( + this.gl.FRAGMENT_SHADER, + fragmentShaderSource + ); + if (!fragShader) { + CubismLogError('Vertex shader compile error!'); + return 0; + } + + // Attach vertex shader to program + this.gl.attachShader(shaderProgram, vertShader); + + // Attach fragment shader to program + this.gl.attachShader(shaderProgram, fragShader); + + // link program + this.gl.linkProgram(shaderProgram); + const linkStatus = this.gl.getProgramParameter( + shaderProgram, + this.gl.LINK_STATUS + ); + + // リンクに失敗したらシェーダーを削除 + if (!linkStatus) { + CubismLogError('Failed to link program: {0}', shaderProgram); + + this.gl.deleteShader(vertShader); + vertShader = 0; + + this.gl.deleteShader(fragShader); + fragShader = 0; + + if (shaderProgram) { + this.gl.deleteProgram(shaderProgram); + shaderProgram = 0; + } + + return 0; + } + + // Release vertex and fragment shaders. + this.gl.deleteShader(vertShader); + this.gl.deleteShader(fragShader); + + return shaderProgram; + } + + /** + * シェーダープログラムをコンパイルする + * @param shaderType シェーダタイプ(Vertex/Fragment) + * @param shaderSource シェーダソースコード + * + * @return コンパイルされたシェーダープログラム + */ + public compileShaderSource( + shaderType: GLenum, + shaderSource: string + ): WebGLProgram { + const source: string = shaderSource; + + const shader: WebGLProgram = this.gl.createShader(shaderType); + this.gl.shaderSource(shader, source); + this.gl.compileShader(shader); + + if (!shader) { + const log: string = this.gl.getShaderInfoLog(shader); + CubismLogError('Shader compile log: {0} ', log); + } + + const status: any = this.gl.getShaderParameter( + shader, + this.gl.COMPILE_STATUS + ); + if (!status) { + this.gl.deleteShader(shader); + return null; + } + + return shader; + } + + public setGl(gl: WebGLRenderingContext): void { + this.gl = gl; + } + + _shaderSets: csmVector; // ロードしたシェーダープログラムを保持する変数 + gl: WebGLRenderingContext; // webglコンテキスト +} + +/** + * CubismShader_WebGLのインナークラス + */ +export class CubismShaderSet { + shaderProgram: WebGLProgram; // シェーダープログラムのアドレス + attributePositionLocation: GLuint; // シェーダープログラムに渡す変数のアドレス(Position) + attributeTexCoordLocation: GLuint; // シェーダープログラムに渡す変数のアドレス(TexCoord) + uniformMatrixLocation: WebGLUniformLocation; // シェーダープログラムに渡す変数のアドレス(Matrix) + uniformClipMatrixLocation: WebGLUniformLocation; // シェーダープログラムに渡す変数のアドレス(ClipMatrix) + samplerTexture0Location: WebGLUniformLocation; // シェーダープログラムに渡す変数のアドレス(Texture0) + samplerTexture1Location: WebGLUniformLocation; // シェーダープログラムに渡す変数のアドレス(Texture1) + uniformBaseColorLocation: WebGLUniformLocation; // シェーダープログラムに渡す変数のアドレス(BaseColor) + uniformChannelFlagLocation: WebGLUniformLocation; // シェーダープログラムに渡す変数のアドレス(ChannelFlag) +} + +export enum ShaderNames { + // SetupMask + ShaderNames_SetupMask, + + // Normal + ShaderNames_NormalPremultipliedAlpha, + ShaderNames_NormalMaskedPremultipliedAlpha, + ShaderNames_NomralMaskedInvertedPremultipliedAlpha, + + // Add + ShaderNames_AddPremultipliedAlpha, + ShaderNames_AddMaskedPremultipliedAlpha, + ShaderNames_AddMaskedPremultipliedAlphaInverted, + + // Mult + ShaderNames_MultPremultipliedAlpha, + ShaderNames_MultMaskedPremultipliedAlpha, + ShaderNames_MultMaskedPremultipliedAlphaInverted +} + +export const vertexShaderSrcSetupMask = + 'attribute vec4 a_position;' + + 'attribute vec2 a_texCoord;' + + 'varying vec2 v_texCoord;' + + 'varying vec4 v_myPos;' + + 'uniform mat4 u_clipMatrix;' + + 'void main()' + + '{' + + ' gl_Position = u_clipMatrix * a_position;' + + ' v_myPos = u_clipMatrix * a_position;' + + ' v_texCoord = a_texCoord;' + + ' v_texCoord.y = 1.0 - v_texCoord.y;' + + '}'; +export const fragmentShaderSrcsetupMask = + 'precision mediump float;' + + 'varying vec2 v_texCoord;' + + 'varying vec4 v_myPos;' + + 'uniform vec4 u_baseColor;' + + 'uniform vec4 u_channelFlag;' + + 'uniform sampler2D s_texture0;' + + 'void main()' + + '{' + + ' float isInside = ' + + ' step(u_baseColor.x, v_myPos.x/v_myPos.w)' + + ' * step(u_baseColor.y, v_myPos.y/v_myPos.w)' + + ' * step(v_myPos.x/v_myPos.w, u_baseColor.z)' + + ' * step(v_myPos.y/v_myPos.w, u_baseColor.w);' + + ' gl_FragColor = u_channelFlag * texture2D(s_texture0, v_texCoord).a * isInside;' + + '}'; + +//----- バーテックスシェーダプログラム ----- +// Normal & Add & Mult 共通 +export const vertexShaderSrc = + 'attribute vec4 a_position;' + //v.vertex + 'attribute vec2 a_texCoord;' + //v.texcoord + 'varying vec2 v_texCoord;' + //v2f.texcoord + 'uniform mat4 u_matrix;' + + 'void main()' + + '{' + + ' gl_Position = u_matrix * a_position;' + + ' v_texCoord = a_texCoord;' + + ' v_texCoord.y = 1.0 - v_texCoord.y;' + + '}'; + +// Normal & Add & Mult 共通(クリッピングされたものの描画用) +export const vertexShaderSrcMasked = + 'attribute vec4 a_position;' + + 'attribute vec2 a_texCoord;' + + 'varying vec2 v_texCoord;' + + 'varying vec4 v_clipPos;' + + 'uniform mat4 u_matrix;' + + 'uniform mat4 u_clipMatrix;' + + 'void main()' + + '{' + + ' gl_Position = u_matrix * a_position;' + + ' v_clipPos = u_clipMatrix * a_position;' + + ' v_texCoord = a_texCoord;' + + ' v_texCoord.y = 1.0 - v_texCoord.y;' + + '}'; + +//----- フラグメントシェーダプログラム ----- +// Normal & Add & Mult 共通 (PremultipliedAlpha) +export const fragmentShaderSrcPremultipliedAlpha = + 'precision mediump float;' + + 'varying vec2 v_texCoord;' + //v2f.texcoord + 'uniform vec4 u_baseColor;' + + 'uniform sampler2D s_texture0;' + //_MainTex + 'void main()' + + '{' + + ' gl_FragColor = texture2D(s_texture0 , v_texCoord) * u_baseColor;' + + '}'; + +// Normal (クリッピングされたものの描画用、PremultipliedAlpha兼用) +export const fragmentShaderSrcMaskPremultipliedAlpha = + 'precision mediump float;' + + 'varying vec2 v_texCoord;' + + 'varying vec4 v_clipPos;' + + 'uniform vec4 u_baseColor;' + + 'uniform vec4 u_channelFlag;' + + 'uniform sampler2D s_texture0;' + + 'uniform sampler2D s_texture1;' + + 'void main()' + + '{' + + ' vec4 col_formask = texture2D(s_texture0 , v_texCoord) * u_baseColor;' + + ' vec4 clipMask = (1.0 - texture2D(s_texture1, v_clipPos.xy / v_clipPos.w)) * u_channelFlag;' + + ' float maskVal = clipMask.r + clipMask.g + clipMask.b + clipMask.a;' + + ' col_formask = col_formask * maskVal;' + + ' gl_FragColor = col_formask;' + + '}'; + +// Normal & Add & Mult 共通(クリッピングされて反転使用の描画用、PremultipliedAlphaの場合) +export const fragmentShaderSrcMaskInvertedPremultipliedAlpha = + 'precision mediump float;' + + 'varying vec2 v_texCoord;' + + 'varying vec4 v_clipPos;' + + 'uniform sampler2D s_texture0;' + + 'uniform sampler2D s_texture1;' + + 'uniform vec4 u_channelFlag;' + + 'uniform vec4 u_baseColor;' + + 'void main()' + + '{' + + 'vec4 col_formask = texture2D(s_texture0, v_texCoord) * u_baseColor;' + + 'vec4 clipMask = (1.0 - texture2D(s_texture1, v_clipPos.xy / v_clipPos.w)) * u_channelFlag;' + + 'float maskVal = clipMask.r + clipMask.g + clipMask.b + clipMask.a;' + + 'col_formask = col_formask * (1.0 - maskVal);' + + 'gl_FragColor = col_formask;' + + '}'; + +/** + * WebGL用の描画命令を実装したクラス + */ +export class CubismRenderer_WebGL extends CubismRenderer { + /** + * レンダラの初期化処理を実行する + * 引数に渡したモデルからレンダラの初期化処理に必要な情報を取り出すことができる + * + * @param model モデルのインスタンス + */ + public initialize(model: CubismModel): void { + if (model.isUsingMasking()) { + this._clippingManager = new CubismClippingManager_WebGL(); // クリッピングマスク・バッファ前処理方式を初期化 + this._clippingManager.initialize( + model, + model.getDrawableCount(), + model.getDrawableMasks(), + model.getDrawableMaskCounts() + ); + } + + this._sortedDrawableIndexList.resize(model.getDrawableCount(), 0); + + super.initialize(model); // 親クラスの処理を呼ぶ + } + + /** + * WebGLテクスチャのバインド処理 + * CubismRendererにテクスチャを設定し、CubismRenderer内でその画像を参照するためのIndex値を戻り値とする + * @param modelTextureNo セットするモデルテクスチャの番号 + * @param glTextureNo WebGLテクスチャの番号 + */ + public bindTexture(modelTextureNo: number, glTexture: WebGLTexture): void { + this._textures.setValue(modelTextureNo, glTexture); + } + + /** + * WebGLにバインドされたテクスチャのリストを取得する + * @return テクスチャのリスト + */ + public getBindedTextures(): csmMap { + return this._textures; + } + + /** + * クリッピングマスクバッファのサイズを設定する + * マスク用のFrameBufferを破棄、再作成する為処理コストは高い + * @param size クリッピングマスクバッファのサイズ + */ + public setClippingMaskBufferSize(size: number) { + // FrameBufferのサイズを変更するためにインスタンスを破棄・再作成する + this._clippingManager.release(); + this._clippingManager = void 0; + this._clippingManager = null; + + this._clippingManager = new CubismClippingManager_WebGL(); + + this._clippingManager.setClippingMaskBufferSize(size); + + this._clippingManager.initialize( + this.getModel(), + this.getModel().getDrawableCount(), + this.getModel().getDrawableMasks(), + this.getModel().getDrawableMaskCounts() + ); + } + + /** + * クリッピングマスクバッファのサイズを取得する + * @return クリッピングマスクバッファのサイズ + */ + public getClippingMaskBufferSize(): number { + return this._clippingManager.getClippingMaskBufferSize(); + } + + /** + * コンストラクタ + */ + public constructor() { + super(); + this._clippingContextBufferForMask = null; + this._clippingContextBufferForDraw = null; + this._clippingManager = new CubismClippingManager_WebGL(); + this.firstDraw = true; + this._textures = new csmMap(); + this._sortedDrawableIndexList = new csmVector(); + this._bufferData = { + vertex: WebGLBuffer = null, + uv: WebGLBuffer = null, + index: WebGLBuffer = null + }; + + // テクスチャ対応マップの容量を確保しておく + this._textures.prepareCapacity(32, true); + } + + /** + * デストラクタ相当の処理 + */ + public release(): void { + this._clippingManager.release(); + this._clippingManager = void 0; + this._clippingManager = null; + + this.gl.deleteBuffer(this._bufferData.vertex); + this._bufferData.vertex = null; + this.gl.deleteBuffer(this._bufferData.uv); + this._bufferData.uv = null; + this.gl.deleteBuffer(this._bufferData.index); + this._bufferData.index = null; + this._bufferData = null; + + this._textures = null; + } + + /** + * モデルを描画する実際の処理 + */ + public doDrawModel(): void { + //------------ クリッピングマスク・バッファ前処理方式の場合 ------------ + if (this._clippingManager != null) { + this.preDraw(); + this._clippingManager.setupClippingContext(this.getModel(), this); + } + + // 上記クリッピング処理内でも一度PreDrawを呼ぶので注意!! + this.preDraw(); + + const drawableCount: number = this.getModel().getDrawableCount(); + const renderOrder: Int32Array = this.getModel().getDrawableRenderOrders(); + + // インデックスを描画順でソート + for (let i = 0; i < drawableCount; ++i) { + const order: number = renderOrder[i]; + this._sortedDrawableIndexList.set(order, i); + } + + // 描画 + for (let i = 0; i < drawableCount; ++i) { + const drawableIndex: number = this._sortedDrawableIndexList.at(i); + + // Drawableが表示状態でなければ処理をパスする + if (!this.getModel().getDrawableDynamicFlagIsVisible(drawableIndex)) { + continue; + } + + // クリッピングマスクをセットする + this.setClippingContextBufferForDraw( + this._clippingManager != null + ? this._clippingManager + .getClippingContextListForDraw() + .at(drawableIndex) + : null + ); + + this.setIsCulling(this.getModel().getDrawableCulling(drawableIndex)); + + this.drawMesh( + this.getModel().getDrawableTextureIndices(drawableIndex), + this.getModel().getDrawableVertexIndexCount(drawableIndex), + this.getModel().getDrawableVertexCount(drawableIndex), + this.getModel().getDrawableVertexIndices(drawableIndex), + this.getModel().getDrawableVertices(drawableIndex), + this.getModel().getDrawableVertexUvs(drawableIndex), + this.getModel().getDrawableOpacity(drawableIndex), + this.getModel().getDrawableBlendMode(drawableIndex), + this.getModel().getDrawableInvertedMaskBit(drawableIndex) + ); + } + } + + /** + * [オーバーライド] + * 描画オブジェクト(アートメッシュ)を描画する。 + * ポリゴンメッシュとテクスチャ番号をセットで渡す。 + * @param textureNo 描画するテクスチャ番号 + * @param indexCount 描画オブジェクトのインデックス値 + * @param vertexCount ポリゴンメッシュの頂点数 + * @param indexArray ポリゴンメッシュのインデックス配列 + * @param vertexArray ポリゴンメッシュの頂点配列 + * @param uvArray uv配列 + * @param opacity 不透明度 + * @param colorBlendMode カラー合成タイプ + * @param invertedMask マスク使用時のマスクの反転使用 + */ + public drawMesh( + textureNo: number, + indexCount: number, + vertexCount: number, + indexArray: Uint16Array, + vertexArray: Float32Array, + uvArray: Float32Array, + opacity: number, + colorBlendMode: CubismBlendMode, + invertedMask: boolean + ): void { + // 裏面描画の有効・無効 + if (this.isCulling()) { + this.gl.enable(this.gl.CULL_FACE); + } else { + this.gl.disable(this.gl.CULL_FACE); + } + + this.gl.frontFace(this.gl.CCW); // Cubism SDK OpenGLはマスク・アートメッシュ共にCCWが表面 + + const modelColorRGBA: CubismTextureColor = this.getModelColor(); + + if (this.getClippingContextBufferForMask() == null) { + // マスク生成時以外 + modelColorRGBA.A *= opacity; + if (this.isPremultipliedAlpha()) { + modelColorRGBA.R *= modelColorRGBA.A; + modelColorRGBA.G *= modelColorRGBA.A; + modelColorRGBA.B *= modelColorRGBA.A; + } + } + + let drawtexture: WebGLTexture; // シェーダに渡すテクスチャ + + // テクスチャマップからバインド済みテクスチャIDを取得 + // バインドされていなければダミーのテクスチャIDをセットする + if (this._textures.getValue(textureNo) != null) { + drawtexture = this._textures.getValue(textureNo); + } else { + drawtexture = null; + } + + CubismShader_WebGL.getInstance().setupShaderProgram( + this, + drawtexture, + vertexCount, + vertexArray, + indexArray, + uvArray, + this._bufferData, + opacity, + colorBlendMode, + modelColorRGBA, + this.isPremultipliedAlpha(), + this.getMvpMatrix(), + invertedMask + ); + + // ポリゴンメッシュを描画する + this.gl.drawElements( + this.gl.TRIANGLES, + indexCount, + this.gl.UNSIGNED_SHORT, + 0 + ); + + // 後処理 + this.gl.useProgram(null); + this.setClippingContextBufferForDraw(null); + this.setClippingContextBufferForMask(null); + } + + /** + * レンダラが保持する静的なリソースを解放する + * WebGLの静的なシェーダープログラムを解放する + */ + public static doStaticRelease(): void { + CubismShader_WebGL.deleteInstance(); + } + + /** + * レンダーステートを設定する + * @param fbo アプリケーション側で指定しているフレームバッファ + * @param viewport ビューポート + */ + public setRenderState(fbo: WebGLFramebuffer, viewport: number[]): void { + s_fbo = fbo; + s_viewport = viewport; + } + + /** + * 描画開始時の追加処理 + * モデルを描画する前にクリッピングマスクに必要な処理を実装している + */ + public preDraw(): void { + if (this.firstDraw) { + this.firstDraw = false; + + // 拡張機能を有効にする + this._anisortopy = + this.gl.getExtension('EXT_texture_filter_anisotropic') || + this.gl.getExtension('WEBKIT_EXT_texture_filter_anisotropic') || + this.gl.getExtension('MOZ_EXT_texture_filter_anisotropic'); + } + + this.gl.disable(this.gl.SCISSOR_TEST); + this.gl.disable(this.gl.STENCIL_TEST); + this.gl.disable(this.gl.DEPTH_TEST); + + // カリング(1.0beta3) + this.gl.frontFace(this.gl.CW); + + this.gl.enable(this.gl.BLEND); + this.gl.colorMask(true, true, true, true); + + this.gl.bindBuffer(this.gl.ARRAY_BUFFER, null); // 前にバッファがバインドされていたら破棄する必要がある + this.gl.bindBuffer(this.gl.ELEMENT_ARRAY_BUFFER, null); + } + + /** + * マスクテクスチャに描画するクリッピングコンテキストをセットする + */ + public setClippingContextBufferForMask(clip: CubismClippingContext) { + this._clippingContextBufferForMask = clip; + } + + /** + * マスクテクスチャに描画するクリッピングコンテキストを取得する + * @return マスクテクスチャに描画するクリッピングコンテキスト + */ + public getClippingContextBufferForMask(): CubismClippingContext { + return this._clippingContextBufferForMask; + } + + /** + * 画面上に描画するクリッピングコンテキストをセットする + */ + public setClippingContextBufferForDraw(clip: CubismClippingContext): void { + this._clippingContextBufferForDraw = clip; + } + + /** + * 画面上に描画するクリッピングコンテキストを取得する + * @return 画面上に描画するクリッピングコンテキスト + */ + public getClippingContextBufferForDraw(): CubismClippingContext { + return this._clippingContextBufferForDraw; + } + + /** + * glの設定 + */ + public startUp(gl: WebGLRenderingContext): void { + this.gl = gl; + this._clippingManager.setGL(gl); + CubismShader_WebGL.getInstance().setGl(gl); + } + + _textures: csmMap; // モデルが参照するテクスチャとレンダラでバインドしているテクスチャとのマップ + _sortedDrawableIndexList: csmVector; // 描画オブジェクトのインデックスを描画順に並べたリスト + _clippingManager: CubismClippingManager_WebGL; // クリッピングマスク管理オブジェクト + _clippingContextBufferForMask: CubismClippingContext; // マスクテクスチャに描画するためのクリッピングコンテキスト + _clippingContextBufferForDraw: CubismClippingContext; // 画面上描画するためのクリッピングコンテキスト + firstDraw: boolean; + _bufferData: { + vertex: WebGLBuffer; + uv: WebGLBuffer; + index: WebGLBuffer; + }; // 頂点バッファデータ + gl: WebGLRenderingContext; // webglコンテキスト +} + +/** + * レンダラが保持する静的なリソースを開放する + */ +CubismRenderer.staticRelease = (): void => { + CubismRenderer_WebGL.doStaticRelease(); +}; + +// Namespace definition for compatibility. +import * as $ from './cubismrenderer_webgl'; +// eslint-disable-next-line @typescript-eslint/no-namespace +export namespace Live2DCubismFramework { + export const CubismClippingContext = $.CubismClippingContext; + export type CubismClippingContext = $.CubismClippingContext; + export const CubismClippingManager_WebGL = $.CubismClippingManager_WebGL; + export type CubismClippingManager_WebGL = $.CubismClippingManager_WebGL; + export const CubismRenderTextureResource = $.CubismRenderTextureResource; + export type CubismRenderTextureResource = $.CubismRenderTextureResource; + export const CubismRenderer_WebGL = $.CubismRenderer_WebGL; + export type CubismRenderer_WebGL = $.CubismRenderer_WebGL; + export const CubismShaderSet = $.CubismShaderSet; + export type CubismShaderSet = $.CubismShaderSet; + export const CubismShader_WebGL = $.CubismShader_WebGL; + export type CubismShader_WebGL = $.CubismShader_WebGL; + export const ShaderNames = $.ShaderNames; + export type ShaderNames = $.ShaderNames; } diff --git a/src/type/csmmap.ts b/src/type/csmmap.ts index 31d26ca..ba7d467 100644 --- a/src/type/csmmap.ts +++ b/src/type/csmmap.ts @@ -7,299 +7,309 @@ import { CubismLogDebug } from '../utils/cubismdebug'; -export namespace Live2DCubismFramework { +/** + * Key-Valueのペアを定義するクラス + * csmMapクラスの内部データで使用する。 + */ +export class csmPair<_KeyT, _ValT> { /** - * Key-Valueのペアを定義するクラス - * csmMapクラスの内部データで使用する。 + * コンストラクタ + * @param key Keyとしてセットする値 + * @param value Valueとしてセットする値 */ - export class csmPair<_KeyT, _ValT> { - /** - * コンストラクタ - * @param key Keyとしてセットする値 - * @param value Valueとしてセットする値 - */ - public constructor(key?: _KeyT, value?: _ValT) { - this.first = key == undefined ? null : key; + public constructor(key?: _KeyT, value?: _ValT) { + this.first = key == undefined ? null : key; - this.second = value == undefined ? null : value; - } - - public first: _KeyT; // keyとして用いる変数 - public second: _ValT; // valueとして用いる変数 + this.second = value == undefined ? null : value; } + public first: _KeyT; // keyとして用いる変数 + public second: _ValT; // valueとして用いる変数 +} + +/** + * マップ型 + */ +export class csmMap<_KeyT, _ValT> { /** - * マップ型 + * 引数付きコンストラクタ + * @param size 初期化時点で確保するサイズ */ - export class csmMap<_KeyT, _ValT> { - /** - * 引数付きコンストラクタ - * @param size 初期化時点で確保するサイズ - */ - public constructor(size?: number) { - if (size != undefined) { - if (size < 1) { - this._keyValues = []; - this._dummyValue = null; - this._size = 0; - } else { - this._keyValues = new Array(size); - this._size = size; - } - } else { + public constructor(size?: number) { + if (size != undefined) { + if (size < 1) { this._keyValues = []; this._dummyValue = null; this._size = 0; - } - } - - /** - * デストラクタ - */ - public release() { - this.clear(); - } - - /** - * キーを追加する - * @param key 新たに追加するキー - */ - public appendKey(key: _KeyT): void { - // 新しくKey/Valueのペアを作る - this.prepareCapacity(this._size + 1, false); // 1つ以上入る隙間を作る - // 新しいkey/valueのインデックスは_size - - this._keyValues[this._size] = new csmPair<_KeyT, _ValT>(key); - this._size += 1; - } - - /** - * 添字演算子[key]のオーバーロード(get) - * @param key 添字から特定されるValue値 - */ - public getValue(key: _KeyT): _ValT { - let found = -1; - - for (let i = 0; i < this._size; i++) { - if (this._keyValues[i].first == key) { - found = i; - break; - } - } - - if (found >= 0) { - return this._keyValues[found].second; } else { - this.appendKey(key); // 新規キーを追加 - return this._keyValues[this._size - 1].second; + this._keyValues = new Array(size); + this._size = size; } - } - - /** - * 添字演算子[key]のオーバーロード(set) - * @param key 添字から特定されるValue値 - * @param value 代入するValue値 - */ - public setValue(key: _KeyT, value: _ValT): void { - let found = -1; - - for (let i = 0; i < this._size; i++) { - if (this._keyValues[i].first == key) { - found = i; - break; - } - } - - if (found >= 0) { - this._keyValues[found].second = value; - } else { - this.appendKey(key); // 新規キーを追加 - this._keyValues[this._size - 1].second = value; - } - } - - /** - * 引数で渡したKeyを持つ要素が存在するか - * @param key 存在を確認するkey - * @return true 引数で渡したkeyを持つ要素が存在する - * @return false 引数で渡したkeyを持つ要素が存在しない - */ - public isExist(key: _KeyT): boolean { - for (let i = 0; i < this._size; i++) { - if (this._keyValues[i].first == key) { - return true; - } - } - return false; - } - - /** - * keyValueのポインタを全て解放する - */ - public clear(): void { - this._keyValues = void 0; - this._keyValues = null; + } else { this._keyValues = []; - + this._dummyValue = null; this._size = 0; } - - /** - * コンテナのサイズを取得する - * - * @return コンテナのサイズ - */ - public getSize(): number { - return this._size; - } - - /** - * コンテナのキャパシティを確保する - * @param newSize 新たなキャパシティ。引数の値が現在のサイズ未満の場合は何もしない。 - * @param fitToSize trueなら指定したサイズに合わせる。falseならサイズを2倍確保しておく。 - */ - public prepareCapacity(newSize: number, fitToSize: boolean): void { - if (newSize > this._keyValues.length) { - if (this._keyValues.length == 0) { - if (!fitToSize && newSize < csmMap.DefaultSize) - newSize = csmMap.DefaultSize; - this._keyValues.length = newSize; - } else { - if (!fitToSize && newSize < this._keyValues.length * 2) - newSize = this._keyValues.length * 2; - this._keyValues.length = newSize; - } - } - } - - /** - * コンテナの先頭要素を返す - */ - public begin(): iterator<_KeyT, _ValT> { - const ite: iterator<_KeyT, _ValT> = new iterator<_KeyT, _ValT>(this, 0); - return ite; - } - - /** - * コンテナの終端要素を返す - */ - public end(): iterator<_KeyT, _ValT> { - const ite: iterator<_KeyT, _ValT> = new iterator<_KeyT, _ValT>( - this, - this._size - ); // 終了 - return ite; - } - - /** - * コンテナから要素を削除する - * - * @param ite 削除する要素 - */ - public erase(ite: iterator<_KeyT, _ValT>): iterator<_KeyT, _ValT> { - const index: number = ite._index; - if (index < 0 || this._size <= index) { - return ite; // 削除範囲外 - } - - // 削除 - this._keyValues.splice(index, 1); - --this._size; - - const ite2: iterator<_KeyT, _ValT> = new iterator<_KeyT, _ValT>( - this, - index - ); // 終了 - return ite2; - } - - /** - * コンテナの値を32ビット符号付き整数型でダンプする - */ - public dumpAsInt() { - for (let i = 0; i < this._size; i++) { - CubismLogDebug('{0} ,', this._keyValues[i]); - CubismLogDebug('\n'); - } - } - - public static readonly DefaultSize = 10; // コンテナの初期化のデフォルトサイズ - public _keyValues: csmPair<_KeyT, _ValT>[]; // key-valueペアの配列 - public _dummyValue: _ValT; // 空の値を返す為のダミー - public _size: number; // コンテナの要素数 } /** - * csmMapのイテレータ + * デストラクタ */ - export class iterator<_KeyT, _ValT> { - /** - * コンストラクタ - */ - constructor(v?: csmMap<_KeyT, _ValT>, idx?: number) { - this._map = v != undefined ? v : new csmMap<_KeyT, _ValT>(); - - this._index = idx != undefined ? idx : 0; - } - - /** - * =演算子のオーバーロード - */ - public set(ite: iterator<_KeyT, _ValT>): iterator<_KeyT, _ValT> { - this._index = ite._index; - this._map = ite._map; - return this; - } - - /** - * 前置き++演算子のオーバーロード - */ - public preIncrement(): iterator<_KeyT, _ValT> { - ++this._index; - return this; - } - - /** - * 前置き--演算子のオーバーロード - */ - public preDecrement(): iterator<_KeyT, _ValT> { - --this._index; - return this; - } - - /** - * 後置き++演算子のオーバーロード - */ - public increment(): iterator<_KeyT, _ValT> { - const iteold = new iterator<_KeyT, _ValT>(this._map, this._index++); // 古い値を保存 - return iteold; - } - - /** - * 後置き--演算子のオーバーロード - */ - public decrement(): iterator<_KeyT, _ValT> { - const iteold = new iterator<_KeyT, _ValT>(this._map, this._index); // 古い値を保存 - this._map = iteold._map; - this._index = iteold._index; - return this; - } - - /** - * *演算子のオーバーロード - */ - public ptr(): csmPair<_KeyT, _ValT> { - return this._map._keyValues[this._index]; - } - - /** - * !=演算 - */ - public notEqual(ite: iterator<_KeyT, _ValT>): boolean { - return this._index != ite._index || this._map != ite._map; - } - - _index: number; // コンテナのインデックス値 - _map: csmMap<_KeyT, _ValT>; // コンテナ + public release() { + this.clear(); } + + /** + * キーを追加する + * @param key 新たに追加するキー + */ + public appendKey(key: _KeyT): void { + // 新しくKey/Valueのペアを作る + this.prepareCapacity(this._size + 1, false); // 1つ以上入る隙間を作る + // 新しいkey/valueのインデックスは_size + + this._keyValues[this._size] = new csmPair<_KeyT, _ValT>(key); + this._size += 1; + } + + /** + * 添字演算子[key]のオーバーロード(get) + * @param key 添字から特定されるValue値 + */ + public getValue(key: _KeyT): _ValT { + let found = -1; + + for (let i = 0; i < this._size; i++) { + if (this._keyValues[i].first == key) { + found = i; + break; + } + } + + if (found >= 0) { + return this._keyValues[found].second; + } else { + this.appendKey(key); // 新規キーを追加 + return this._keyValues[this._size - 1].second; + } + } + + /** + * 添字演算子[key]のオーバーロード(set) + * @param key 添字から特定されるValue値 + * @param value 代入するValue値 + */ + public setValue(key: _KeyT, value: _ValT): void { + let found = -1; + + for (let i = 0; i < this._size; i++) { + if (this._keyValues[i].first == key) { + found = i; + break; + } + } + + if (found >= 0) { + this._keyValues[found].second = value; + } else { + this.appendKey(key); // 新規キーを追加 + this._keyValues[this._size - 1].second = value; + } + } + + /** + * 引数で渡したKeyを持つ要素が存在するか + * @param key 存在を確認するkey + * @return true 引数で渡したkeyを持つ要素が存在する + * @return false 引数で渡したkeyを持つ要素が存在しない + */ + public isExist(key: _KeyT): boolean { + for (let i = 0; i < this._size; i++) { + if (this._keyValues[i].first == key) { + return true; + } + } + return false; + } + + /** + * keyValueのポインタを全て解放する + */ + public clear(): void { + this._keyValues = void 0; + this._keyValues = null; + this._keyValues = []; + + this._size = 0; + } + + /** + * コンテナのサイズを取得する + * + * @return コンテナのサイズ + */ + public getSize(): number { + return this._size; + } + + /** + * コンテナのキャパシティを確保する + * @param newSize 新たなキャパシティ。引数の値が現在のサイズ未満の場合は何もしない。 + * @param fitToSize trueなら指定したサイズに合わせる。falseならサイズを2倍確保しておく。 + */ + public prepareCapacity(newSize: number, fitToSize: boolean): void { + if (newSize > this._keyValues.length) { + if (this._keyValues.length == 0) { + if (!fitToSize && newSize < csmMap.DefaultSize) + newSize = csmMap.DefaultSize; + this._keyValues.length = newSize; + } else { + if (!fitToSize && newSize < this._keyValues.length * 2) + newSize = this._keyValues.length * 2; + this._keyValues.length = newSize; + } + } + } + + /** + * コンテナの先頭要素を返す + */ + public begin(): iterator<_KeyT, _ValT> { + const ite: iterator<_KeyT, _ValT> = new iterator<_KeyT, _ValT>(this, 0); + return ite; + } + + /** + * コンテナの終端要素を返す + */ + public end(): iterator<_KeyT, _ValT> { + const ite: iterator<_KeyT, _ValT> = new iterator<_KeyT, _ValT>( + this, + this._size + ); // 終了 + return ite; + } + + /** + * コンテナから要素を削除する + * + * @param ite 削除する要素 + */ + public erase(ite: iterator<_KeyT, _ValT>): iterator<_KeyT, _ValT> { + const index: number = ite._index; + if (index < 0 || this._size <= index) { + return ite; // 削除範囲外 + } + + // 削除 + this._keyValues.splice(index, 1); + --this._size; + + const ite2: iterator<_KeyT, _ValT> = new iterator<_KeyT, _ValT>( + this, + index + ); // 終了 + return ite2; + } + + /** + * コンテナの値を32ビット符号付き整数型でダンプする + */ + public dumpAsInt() { + for (let i = 0; i < this._size; i++) { + CubismLogDebug('{0} ,', this._keyValues[i]); + CubismLogDebug('\n'); + } + } + + public static readonly DefaultSize = 10; // コンテナの初期化のデフォルトサイズ + public _keyValues: csmPair<_KeyT, _ValT>[]; // key-valueペアの配列 + public _dummyValue: _ValT; // 空の値を返す為のダミー + public _size: number; // コンテナの要素数 +} + +/** + * csmMapのイテレータ + */ +export class iterator<_KeyT, _ValT> { + /** + * コンストラクタ + */ + constructor(v?: csmMap<_KeyT, _ValT>, idx?: number) { + this._map = v != undefined ? v : new csmMap<_KeyT, _ValT>(); + + this._index = idx != undefined ? idx : 0; + } + + /** + * =演算子のオーバーロード + */ + public set(ite: iterator<_KeyT, _ValT>): iterator<_KeyT, _ValT> { + this._index = ite._index; + this._map = ite._map; + return this; + } + + /** + * 前置き++演算子のオーバーロード + */ + public preIncrement(): iterator<_KeyT, _ValT> { + ++this._index; + return this; + } + + /** + * 前置き--演算子のオーバーロード + */ + public preDecrement(): iterator<_KeyT, _ValT> { + --this._index; + return this; + } + + /** + * 後置き++演算子のオーバーロード + */ + public increment(): iterator<_KeyT, _ValT> { + const iteold = new iterator<_KeyT, _ValT>(this._map, this._index++); // 古い値を保存 + return iteold; + } + + /** + * 後置き--演算子のオーバーロード + */ + public decrement(): iterator<_KeyT, _ValT> { + const iteold = new iterator<_KeyT, _ValT>(this._map, this._index); // 古い値を保存 + this._map = iteold._map; + this._index = iteold._index; + return this; + } + + /** + * *演算子のオーバーロード + */ + public ptr(): csmPair<_KeyT, _ValT> { + return this._map._keyValues[this._index]; + } + + /** + * !=演算 + */ + public notEqual(ite: iterator<_KeyT, _ValT>): boolean { + return this._index != ite._index || this._map != ite._map; + } + + _index: number; // コンテナのインデックス値 + _map: csmMap<_KeyT, _ValT>; // コンテナ +} + +// Namespace definition for compatibility. +import * as $ from './csmmap'; +// eslint-disable-next-line @typescript-eslint/no-namespace +export namespace Live2DCubismFramework { + export const csmMap = $.csmMap; + export type csmMap = $.csmMap; + export const csmPair = $.csmPair; + export type csmPair = $.csmPair; + export const iterator = $.iterator; + export type iterator = $.iterator; } diff --git a/src/type/csmrectf.ts b/src/type/csmrectf.ts index ff8c330..074d727 100644 --- a/src/type/csmrectf.ts +++ b/src/type/csmrectf.ts @@ -5,79 +5,85 @@ * that can be found at https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html. */ -export namespace Live2DCubismFramework { +/** + * 矩形形状(座標・長さはfloat値)を定義するクラス + */ +export class csmRect { /** - * 矩形形状(座標・長さはfloat値)を定義するクラス + * コンストラクタ + * @param x 左端X座標 + * @param y 上端Y座標 + * @param w 幅 + * @param h 高さ */ - export class csmRect { - /** - * コンストラクタ - * @param x 左端X座標 - * @param y 上端Y座標 - * @param w 幅 - * @param h 高さ - */ - public constructor(x?: number, y?: number, w?: number, h?: number) { - this.x = x; - this.y = y; - this.width = w; - this.height = h; - } - - /** - * 矩形中央のX座標を取得する - */ - public getCenterX(): number { - return this.x + 0.5 * this.width; - } - - /** - * 矩形中央のY座標を取得する - */ - public getCenterY(): number { - return this.y + 0.5 * this.height; - } - - /** - * 右側のX座標を取得する - */ - public getRight(): number { - return this.x + this.width; - } - - /** - * 下端のY座標を取得する - */ - public getBottom(): number { - return this.y + this.height; - } - - /** - * 矩形に値をセットする - * @param r 矩形のインスタンス - */ - public setRect(r: csmRect): void { - this.x = r.x; - this.y = r.y; - this.width = r.width; - this.height = r.height; - } - - /** - * 矩形中央を軸にして縦横を拡縮する - * @param w 幅方向に拡縮する量 - * @param h 高さ方向に拡縮する量 - */ - public expand(w: number, h: number) { - this.x -= w; - this.y -= h; - this.width += w * 2.0; - this.height += h * 2.0; - } - - public x: number; // 左端X座標 - public y: number; // 上端Y座標 - public width: number; // 幅 - public height: number; // 高さ + public constructor(x?: number, y?: number, w?: number, h?: number) { + this.x = x; + this.y = y; + this.width = w; + this.height = h; } + + /** + * 矩形中央のX座標を取得する + */ + public getCenterX(): number { + return this.x + 0.5 * this.width; + } + + /** + * 矩形中央のY座標を取得する + */ + public getCenterY(): number { + return this.y + 0.5 * this.height; + } + + /** + * 右側のX座標を取得する + */ + public getRight(): number { + return this.x + this.width; + } + + /** + * 下端のY座標を取得する + */ + public getBottom(): number { + return this.y + this.height; + } + + /** + * 矩形に値をセットする + * @param r 矩形のインスタンス + */ + public setRect(r: csmRect): void { + this.x = r.x; + this.y = r.y; + this.width = r.width; + this.height = r.height; + } + + /** + * 矩形中央を軸にして縦横を拡縮する + * @param w 幅方向に拡縮する量 + * @param h 高さ方向に拡縮する量 + */ + public expand(w: number, h: number) { + this.x -= w; + this.y -= h; + this.width += w * 2.0; + this.height += h * 2.0; + } + + public x: number; // 左端X座標 + public y: number; // 上端Y座標 + public width: number; // 幅 + public height: number; // 高さ +} + +// Namespace definition for compatibility. +import * as $ from './csmrectf'; +// eslint-disable-next-line @typescript-eslint/no-namespace +export namespace Live2DCubismFramework { + export const csmRect = $.csmRect; + export type csmRect = $.csmRect; } diff --git a/src/type/csmstring.ts b/src/type/csmstring.ts index af69faf..df735e9 100644 --- a/src/type/csmstring.ts +++ b/src/type/csmstring.ts @@ -5,97 +5,103 @@ * that can be found at https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html. */ -export namespace Live2DCubismFramework { +/** + * 文字列クラス。 + */ +export class csmString { /** - * 文字列クラス。 + * 文字列を後方に追加する + * + * @param c 追加する文字列 + * @return 更新された文字列 */ - export class csmString { - /** - * 文字列を後方に追加する - * - * @param c 追加する文字列 - * @return 更新された文字列 - */ - public append(c: string, length?: number): csmString { - this.s += length !== undefined ? c.substr(0, length) : c; + public append(c: string, length?: number): csmString { + this.s += length !== undefined ? c.substr(0, length) : c; - return this; - } - - /** - * 文字サイズを拡張して文字を埋める - * @param length 拡張する文字数 - * @param v 埋める文字 - * @return 更新された文字列 - */ - public expansion(length: number, v: string): csmString { - for (let i = 0; i < length; i++) { - this.append(v); - } - - return this; - } - - /** - * 文字列の長さをバイト数で取得する - */ - public getBytes(): number { - return encodeURIComponent(this.s).replace(/%../g, 'x').length; - } - - /** - * 文字列の長さを返す - */ - public getLength(): number { - return this.s.length; - } - - /** - * 文字列比較 < - * @param s 比較する文字列 - * @return true: 比較する文字列より小さい - * @return false: 比較する文字列より大きい - */ - public isLess(s: csmString): boolean { - return this.s < s.s; - } - - /** - * 文字列比較 > - * @param s 比較する文字列 - * @return true: 比較する文字列より大きい - * @return false: 比較する文字列より小さい - */ - public isGreat(s: csmString): boolean { - return this.s > s.s; - } - - /** - * 文字列比較 == - * @param s 比較する文字列 - * @return true: 比較する文字列と等しい - * @return false: 比較する文字列と異なる - */ - public isEqual(s: string): boolean { - return this.s == s; - } - - /** - * 文字列が空かどうか - * @return true: 空の文字列 - * @return false: 値が設定されている - */ - public isEmpty(): boolean { - return this.s.length == 0; - } - - /** - * 引数付きコンストラクタ - */ - public constructor(s: string) { - this.s = s; - } - - s: string; + return this; } + + /** + * 文字サイズを拡張して文字を埋める + * @param length 拡張する文字数 + * @param v 埋める文字 + * @return 更新された文字列 + */ + public expansion(length: number, v: string): csmString { + for (let i = 0; i < length; i++) { + this.append(v); + } + + return this; + } + + /** + * 文字列の長さをバイト数で取得する + */ + public getBytes(): number { + return encodeURIComponent(this.s).replace(/%../g, 'x').length; + } + + /** + * 文字列の長さを返す + */ + public getLength(): number { + return this.s.length; + } + + /** + * 文字列比較 < + * @param s 比較する文字列 + * @return true: 比較する文字列より小さい + * @return false: 比較する文字列より大きい + */ + public isLess(s: csmString): boolean { + return this.s < s.s; + } + + /** + * 文字列比較 > + * @param s 比較する文字列 + * @return true: 比較する文字列より大きい + * @return false: 比較する文字列より小さい + */ + public isGreat(s: csmString): boolean { + return this.s > s.s; + } + + /** + * 文字列比較 == + * @param s 比較する文字列 + * @return true: 比較する文字列と等しい + * @return false: 比較する文字列と異なる + */ + public isEqual(s: string): boolean { + return this.s == s; + } + + /** + * 文字列が空かどうか + * @return true: 空の文字列 + * @return false: 値が設定されている + */ + public isEmpty(): boolean { + return this.s.length == 0; + } + + /** + * 引数付きコンストラクタ + */ + public constructor(s: string) { + this.s = s; + } + + s: string; +} + +// Namespace definition for compatibility. +import * as $ from './csmstring'; +// eslint-disable-next-line @typescript-eslint/no-namespace +export namespace Live2DCubismFramework { + export const csmString = $.csmString; + export type csmString = $.csmString; } diff --git a/src/type/csmvector.ts b/src/type/csmvector.ts index a0c3c17..a054f87 100644 --- a/src/type/csmvector.ts +++ b/src/type/csmvector.ts @@ -5,340 +5,348 @@ * that can be found at https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html. */ -export namespace Live2DCubismFramework { +/** + * ベクター型(可変配列型) + */ +export class csmVector { /** - * ベクター型(可変配列型) + * 引数付きコンストラクタ + * @param iniitalCapacity 初期化後のキャパシティ。データサイズは_capacity * sizeof(T) + * @param zeroClear trueなら初期化時に確保した領域を0で埋める */ - export class csmVector { - /** - * 引数付きコンストラクタ - * @param iniitalCapacity 初期化後のキャパシティ。データサイズは_capacity * sizeof(T) - * @param zeroClear trueなら初期化時に確保した領域を0で埋める - */ - constructor(initialCapacity = 0) { - if (initialCapacity < 1) { - this._ptr = []; - this._capacity = 0; - this._size = 0; - } else { - this._ptr = new Array(initialCapacity); - this._capacity = initialCapacity; - this._size = 0; - } - } - - /** - * インデックスで指定した要素を返す - */ - public at(index: number): T { - return this._ptr[index]; - } - - /** - * 要素をセット - * @param index 要素をセットするインデックス - * @param value セットする要素 - */ - public set(index: number, value: T): void { - this._ptr[index] = value; - } - - /** - * コンテナを取得する - */ - public get(offset = 0): T[] { - const ret: T[] = new Array(); - for (let i = offset; i < this._size; i++) { - ret.push(this._ptr[i]); - } - return ret; - } - - /** - * pushBack処理、コンテナに新たな要素を追加する - * @param value PushBack処理で追加する値 - */ - public pushBack(value: T): void { - if (this._size >= this._capacity) { - this.prepareCapacity( - this._capacity == 0 ? csmVector.s_defaultSize : this._capacity * 2 - ); - } - - this._ptr[this._size++] = value; - } - - /** - * コンテナの全要素を解放する - */ - public clear(): void { - this._ptr.length = 0; + constructor(initialCapacity = 0) { + if (initialCapacity < 1) { + this._ptr = []; + this._capacity = 0; + this._size = 0; + } else { + this._ptr = new Array(initialCapacity); + this._capacity = initialCapacity; this._size = 0; } + } - /** - * コンテナの要素数を返す - * @return コンテナの要素数 - */ - public getSize(): number { - return this._size; + /** + * インデックスで指定した要素を返す + */ + public at(index: number): T { + return this._ptr[index]; + } + + /** + * 要素をセット + * @param index 要素をセットするインデックス + * @param value セットする要素 + */ + public set(index: number, value: T): void { + this._ptr[index] = value; + } + + /** + * コンテナを取得する + */ + public get(offset = 0): T[] { + const ret: T[] = new Array(); + for (let i = offset; i < this._size; i++) { + ret.push(this._ptr[i]); + } + return ret; + } + + /** + * pushBack処理、コンテナに新たな要素を追加する + * @param value PushBack処理で追加する値 + */ + public pushBack(value: T): void { + if (this._size >= this._capacity) { + this.prepareCapacity( + this._capacity == 0 ? csmVector.s_defaultSize : this._capacity * 2 + ); } - /** - * コンテナの全要素に対して代入処理を行う - * @param newSize 代入処理後のサイズ - * @param value 要素に代入する値 - */ - public assign(newSize: number, value: T): void { - const curSize = this._size; + this._ptr[this._size++] = value; + } - if (curSize < newSize) { - this.prepareCapacity(newSize); // capacity更新 - } + /** + * コンテナの全要素を解放する + */ + public clear(): void { + this._ptr.length = 0; + this._size = 0; + } - for (let i = 0; i < newSize; i++) { - this._ptr[i] = value; - } + /** + * コンテナの要素数を返す + * @return コンテナの要素数 + */ + public getSize(): number { + return this._size; + } - this._size = newSize; + /** + * コンテナの全要素に対して代入処理を行う + * @param newSize 代入処理後のサイズ + * @param value 要素に代入する値 + */ + public assign(newSize: number, value: T): void { + const curSize = this._size; + + if (curSize < newSize) { + this.prepareCapacity(newSize); // capacity更新 } - /** - * サイズ変更 - */ - public resize(newSize: number, value: T = null): void { - this.updateSize(newSize, value, true); + for (let i = 0; i < newSize; i++) { + this._ptr[i] = value; } - /** - * サイズ変更 - */ - public updateSize( - newSize: number, - value: any = null, - callPlacementNew = true - ): void { - const curSize: number = this._size; + this._size = newSize; + } - if (curSize < newSize) { - this.prepareCapacity(newSize); // capacity更新 + /** + * サイズ変更 + */ + public resize(newSize: number, value: T = null): void { + this.updateSize(newSize, value, true); + } - if (callPlacementNew) { - for (let i: number = this._size; i < newSize; i++) { - if (typeof value == 'function') { - // new - this._ptr[i] = JSON.parse(JSON.stringify(new value())); - } // プリミティブ型なので値渡し - else { - this._ptr[i] = value; - } - } - } else { - for (let i: number = this._size; i < newSize; i++) { + /** + * サイズ変更 + */ + public updateSize( + newSize: number, + value: any = null, + callPlacementNew = true + ): void { + const curSize: number = this._size; + + if (curSize < newSize) { + this.prepareCapacity(newSize); // capacity更新 + + if (callPlacementNew) { + for (let i: number = this._size; i < newSize; i++) { + if (typeof value == 'function') { + // new + this._ptr[i] = JSON.parse(JSON.stringify(new value())); + } // プリミティブ型なので値渡し + else { this._ptr[i] = value; } } } else { - // newSize <= this._size - //--- - const sub = this._size - newSize; - this._ptr.splice(this._size - sub, sub); // 不要なので破棄する - } - this._size = newSize; - } - - /** - * コンテナにコンテナ要素を挿入する - * @param position 挿入する位置 - * @param begin 挿入するコンテナの開始位置 - * @param end 挿入するコンテナの終端位置 - */ - public insert( - position: iterator, - begin: iterator, - end: iterator - ): void { - let dstSi: number = position._index; - const srcSi: number = begin._index; - const srcEi: number = end._index; - - const addCount: number = srcEi - srcSi; - - this.prepareCapacity(this._size + addCount); - - // 挿入用の既存データをシフトして隙間を作る - const addSize = this._size - dstSi; - if (addSize > 0) { - for (let i = 0; i < addSize; i++) { - this._ptr.splice(dstSi + i, 0, null); + for (let i: number = this._size; i < newSize; i++) { + this._ptr[i] = value; } } - - for (let i: number = srcSi; i < srcEi; i++, dstSi++) { - this._ptr[dstSi] = begin._vector._ptr[i]; - } - - this._size = this._size + addCount; + } else { + // newSize <= this._size + //--- + const sub = this._size - newSize; + this._ptr.splice(this._size - sub, sub); // 不要なので破棄する } - - /** - * コンテナからインデックスで指定した要素を削除する - * @param index インデックス値 - * @return true 削除実行 - * @return false 削除範囲外 - */ - public remove(index: number): boolean { - if (index < 0 || this._size <= index) { - return false; // 削除範囲外 - } - - this._ptr.splice(index, 1); - --this._size; - - return true; - } - - /** - * コンテナから要素を削除して他の要素をシフトする - * @param ite 削除する要素 - */ - public erase(ite: iterator): iterator { - const index: number = ite._index; - if (index < 0 || this._size <= index) { - return ite; // 削除範囲外 - } - - // 削除 - this._ptr.splice(index, 1); - --this._size; - - const ite2: iterator = new iterator(this, index); // 終了 - return ite2; - } - - /** - * コンテナのキャパシティを確保する - * @param newSize 新たなキャパシティ。引数の値が現在のサイズ未満の場合は何もしない. - */ - public prepareCapacity(newSize: number): void { - if (newSize > this._capacity) { - if (this._capacity == 0) { - this._ptr = new Array(newSize); - this._capacity = newSize; - } else { - this._ptr.length = newSize; - this._capacity = newSize; - } - } - } - - /** - * コンテナの先頭要素を返す - */ - public begin(): iterator { - const ite: iterator = - this._size == 0 ? this.end() : new iterator(this, 0); - return ite; - } - - /** - * コンテナの終端要素を返す - */ - public end(): iterator { - const ite: iterator = new iterator(this, this._size); - return ite; - } - - public getOffset(offset: number): csmVector { - const newVector = new csmVector(); - newVector._ptr = this.get(offset); - newVector._size = this.get(offset).length; - newVector._capacity = this.get(offset).length; - - return newVector; - } - - _ptr: T[]; // コンテナの先頭アドレス - _size: number; // コンテナの要素数 - _capacity: number; // コンテナのキャパシティ - - static readonly s_defaultSize = 10; // コンテナ初期化のデフォルトサイズ + this._size = newSize; } - export class iterator { - /** - * コンストラクタ - */ - public constructor(v?: csmVector, index?: number) { - this._vector = v != undefined ? v : null; - this._index = index != undefined ? index : 0; + /** + * コンテナにコンテナ要素を挿入する + * @param position 挿入する位置 + * @param begin 挿入するコンテナの開始位置 + * @param end 挿入するコンテナの終端位置 + */ + public insert( + position: iterator, + begin: iterator, + end: iterator + ): void { + let dstSi: number = position._index; + const srcSi: number = begin._index; + const srcEi: number = end._index; + + const addCount: number = srcEi - srcSi; + + this.prepareCapacity(this._size + addCount); + + // 挿入用の既存データをシフトして隙間を作る + const addSize = this._size - dstSi; + if (addSize > 0) { + for (let i = 0; i < addSize; i++) { + this._ptr.splice(dstSi + i, 0, null); + } } - /** - * 代入 - */ - public set(ite: iterator): iterator { - this._index = ite._index; - this._vector = ite._vector; - return this; + for (let i: number = srcSi; i < srcEi; i++, dstSi++) { + this._ptr[dstSi] = begin._vector._ptr[i]; } - /** - * 前置き++演算 - */ - public preIncrement(): iterator { - ++this._index; - return this; - } - - /** - * 前置き--演算 - */ - public preDecrement(): iterator { - --this._index; - return this; - } - - /** - * 後置き++演算子 - */ - public increment(): iterator { - const iteold = new iterator(this._vector, this._index++); // 古い値を保存 - return iteold; - } - - /** - * 後置き--演算子 - */ - public decrement(): iterator { - const iteold = new iterator(this._vector, this._index--); // 古い値を保存 - return iteold; - } - - /** - * ptr - */ - public ptr(): T { - return this._vector._ptr[this._index]; - } - - /** - * =演算子のオーバーロード - */ - public substitution(ite: iterator): iterator { - this._index = ite._index; - this._vector = ite._vector; - return this; - } - - /** - * !=演算子のオーバーロード - */ - public notEqual(ite: iterator): boolean { - return this._index != ite._index || this._vector != ite._vector; - } - - _index: number; // コンテナのインデックス値 - _vector: csmVector; // コンテナ + this._size = this._size + addCount; } + + /** + * コンテナからインデックスで指定した要素を削除する + * @param index インデックス値 + * @return true 削除実行 + * @return false 削除範囲外 + */ + public remove(index: number): boolean { + if (index < 0 || this._size <= index) { + return false; // 削除範囲外 + } + + this._ptr.splice(index, 1); + --this._size; + + return true; + } + + /** + * コンテナから要素を削除して他の要素をシフトする + * @param ite 削除する要素 + */ + public erase(ite: iterator): iterator { + const index: number = ite._index; + if (index < 0 || this._size <= index) { + return ite; // 削除範囲外 + } + + // 削除 + this._ptr.splice(index, 1); + --this._size; + + const ite2: iterator = new iterator(this, index); // 終了 + return ite2; + } + + /** + * コンテナのキャパシティを確保する + * @param newSize 新たなキャパシティ。引数の値が現在のサイズ未満の場合は何もしない. + */ + public prepareCapacity(newSize: number): void { + if (newSize > this._capacity) { + if (this._capacity == 0) { + this._ptr = new Array(newSize); + this._capacity = newSize; + } else { + this._ptr.length = newSize; + this._capacity = newSize; + } + } + } + + /** + * コンテナの先頭要素を返す + */ + public begin(): iterator { + const ite: iterator = + this._size == 0 ? this.end() : new iterator(this, 0); + return ite; + } + + /** + * コンテナの終端要素を返す + */ + public end(): iterator { + const ite: iterator = new iterator(this, this._size); + return ite; + } + + public getOffset(offset: number): csmVector { + const newVector = new csmVector(); + newVector._ptr = this.get(offset); + newVector._size = this.get(offset).length; + newVector._capacity = this.get(offset).length; + + return newVector; + } + + _ptr: T[]; // コンテナの先頭アドレス + _size: number; // コンテナの要素数 + _capacity: number; // コンテナのキャパシティ + + static readonly s_defaultSize = 10; // コンテナ初期化のデフォルトサイズ +} + +export class iterator { + /** + * コンストラクタ + */ + public constructor(v?: csmVector, index?: number) { + this._vector = v != undefined ? v : null; + this._index = index != undefined ? index : 0; + } + + /** + * 代入 + */ + public set(ite: iterator): iterator { + this._index = ite._index; + this._vector = ite._vector; + return this; + } + + /** + * 前置き++演算 + */ + public preIncrement(): iterator { + ++this._index; + return this; + } + + /** + * 前置き--演算 + */ + public preDecrement(): iterator { + --this._index; + return this; + } + + /** + * 後置き++演算子 + */ + public increment(): iterator { + const iteold = new iterator(this._vector, this._index++); // 古い値を保存 + return iteold; + } + + /** + * 後置き--演算子 + */ + public decrement(): iterator { + const iteold = new iterator(this._vector, this._index--); // 古い値を保存 + return iteold; + } + + /** + * ptr + */ + public ptr(): T { + return this._vector._ptr[this._index]; + } + + /** + * =演算子のオーバーロード + */ + public substitution(ite: iterator): iterator { + this._index = ite._index; + this._vector = ite._vector; + return this; + } + + /** + * !=演算子のオーバーロード + */ + public notEqual(ite: iterator): boolean { + return this._index != ite._index || this._vector != ite._vector; + } + + _index: number; // コンテナのインデックス値 + _vector: csmVector; // コンテナ +} + +// Namespace definition for compatibility. +import * as $ from './csmvector'; +// eslint-disable-next-line @typescript-eslint/no-namespace +export namespace Live2DCubismFramework { + export const csmVector = $.csmVector; + export type csmVector = $.csmVector; + export const iterator = $.iterator; + export type iterator = $.iterator; } diff --git a/src/utils/cubismdebug.ts b/src/utils/cubismdebug.ts index fccc4cb..4d6f143 100644 --- a/src/utils/cubismdebug.ts +++ b/src/utils/cubismdebug.ts @@ -5,21 +5,18 @@ * that can be found at https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html. */ -import { - Live2DCubismFramework as cubismframework, - LogLevel -} from '../live2dcubismframework'; import { CSM_LOG_LEVEL, - CSM_LOG_LEVEL_VERBOSE, CSM_LOG_LEVEL_DEBUG, + CSM_LOG_LEVEL_ERROR, CSM_LOG_LEVEL_INFO, - CSM_LOG_LEVEL_WARNING, - CSM_LOG_LEVEL_ERROR + CSM_LOG_LEVEL_VERBOSE, + CSM_LOG_LEVEL_WARNING } from '../cubismframeworkconfig'; +import { CubismFramework, LogLevel } from '../live2dcubismframework'; export const CubismLogPrint = (level: LogLevel, fmt: string, args: any[]) => { - Live2DCubismFramework.CubismDebug.print(level, '[CSM]' + fmt, args); + CubismDebug.print(level, '[CSM]' + fmt, args); }; export const CubismLogPrintIn = (level: LogLevel, fmt: string, args: any[]) => { @@ -98,69 +95,68 @@ if (CSM_LOG_LEVEL <= CSM_LOG_LEVEL_VERBOSE) { }; } -//------------ LIVE2D NAMESPACE ------------ -export namespace Live2DCubismFramework { +/** + * デバッグ用のユーティリティクラス。 + * ログの出力、バイトのダンプなど + */ +export class CubismDebug { /** - * デバッグ用のユーティリティクラス。 - * ログの出力、バイトのダンプなど + * ログを出力する。第一引数にログレベルを設定する。 + * CubismFramework.initialize()時にオプションで設定されたログ出力レベルを下回る場合はログに出さない。 + * + * @param logLevel ログレベルの設定 + * @param format 書式付き文字列 + * @param args 可変長引数 */ - export class CubismDebug { - /** - * ログを出力する。第一引数にログレベルを設定する。 - * CubismFramework.initialize()時にオプションで設定されたログ出力レベルを下回る場合はログに出さない。 - * - * @param logLevel ログレベルの設定 - * @param format 書式付き文字列 - * @param args 可変長引数 - */ - public static print( - logLevel: LogLevel, - format: string, - args?: any[] - ): void { - // オプションで設定されたログ出力レベルを下回る場合はログに出さない - if (logLevel < cubismframework.CubismFramework.getLoggingLevel()) { - return; - } - - const logPrint: Live2DCubismCore.csmLogFunction = - cubismframework.CubismFramework.coreLogFunction; - - if (!logPrint) return; - - const buffer: string = format.replace(/\{(\d+)\}/g, (m, k) => { - return args[k]; - }); - logPrint(buffer); + public static print(logLevel: LogLevel, format: string, args?: any[]): void { + // オプションで設定されたログ出力レベルを下回る場合はログに出さない + if (logLevel < CubismFramework.getLoggingLevel()) { + return; } - /** - * データから指定した長さだけダンプ出力する。 - * CubismFramework.initialize()時にオプションで設定されたログ出力レベルを下回る場合はログに出さない。 - * - * @param logLevel ログレベルの設定 - * @param data ダンプするデータ - * @param length ダンプする長さ - */ - public static dumpBytes( - logLevel: LogLevel, - data: Uint8Array, - length: number - ): void { - for (let i = 0; i < length; i++) { - if (i % 16 == 0 && i > 0) this.print(logLevel, '\n'); - else if (i % 8 == 0 && i > 0) this.print(logLevel, ' '); - this.print(logLevel, '{0} ', [data[i] & 0xff]); - } + const logPrint: Live2DCubismCore.csmLogFunction = + CubismFramework.coreLogFunction; - this.print(logLevel, '\n'); - } + if (!logPrint) return; - /** - * private コンストラクタ - */ - private constructor() {} + const buffer: string = format.replace(/\{(\d+)\}/g, (m, k) => { + return args[k]; + }); + logPrint(buffer); } + + /** + * データから指定した長さだけダンプ出力する。 + * CubismFramework.initialize()時にオプションで設定されたログ出力レベルを下回る場合はログに出さない。 + * + * @param logLevel ログレベルの設定 + * @param data ダンプするデータ + * @param length ダンプする長さ + */ + public static dumpBytes( + logLevel: LogLevel, + data: Uint8Array, + length: number + ): void { + for (let i = 0; i < length; i++) { + if (i % 16 == 0 && i > 0) this.print(logLevel, '\n'); + else if (i % 8 == 0 && i > 0) this.print(logLevel, ' '); + this.print(logLevel, '{0} ', [data[i] & 0xff]); + } + + this.print(logLevel, '\n'); + } + + /** + * private コンストラクタ + */ + private constructor() {} } -//------------ LIVE2D NAMESPACE ------------ +// Namespace definition for compatibility. +import * as $ from './cubismdebug'; +// eslint-disable-next-line @typescript-eslint/no-namespace +export namespace Live2DCubismFramework { + export const CubismDebug = $.CubismDebug; + export type CubismDebug = $.CubismDebug; +} diff --git a/src/utils/cubismjson.ts b/src/utils/cubismjson.ts index 10e91f6..93a41a3 100644 --- a/src/utils/cubismjson.ts +++ b/src/utils/cubismjson.ts @@ -5,1242 +5,1249 @@ * that can be found at https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html. */ -import { Live2DCubismFramework as csmstring } from '../type/csmstring'; -import { Live2DCubismFramework as csmmap } from '../type/csmmap'; -import { Live2DCubismFramework as csmvector } from '../type/csmvector'; -import { CubismLogInfo } from './cubismdebug'; import { strtod } from '../live2dcubismframework'; -import csmVector = csmvector.csmVector; -import csmVector_iterator = csmvector.iterator; -import csmMap = csmmap.csmMap; -import csmMap_iterator = csmmap.iterator; -import csmString = csmstring.csmString; +import { csmMap, iterator as csmMap_iterator } from '../type/csmmap'; +import { csmString } from '../type/csmstring'; +import { csmVector, iterator as csmVector_iterator } from '../type/csmvector'; +import { CubismLogInfo } from './cubismdebug'; -export namespace Live2DCubismFramework { - // StaticInitializeNotForClientCall()で初期化する - const CSM_JSON_ERROR_TYPE_MISMATCH = 'Error: type mismatch'; - const CSM_JSON_ERROR_INDEX_OF_BOUNDS = 'Error: index out of bounds'; +// StaticInitializeNotForClientCall()で初期化する +const CSM_JSON_ERROR_TYPE_MISMATCH = 'Error: type mismatch'; +const CSM_JSON_ERROR_INDEX_OF_BOUNDS = 'Error: index out of bounds'; + +/** + * パースしたJSONエレメントの要素の基底クラス。 + */ +export abstract class Value { + /** + * コンストラクタ + */ + public constructor() {} /** - * パースしたJSONエレメントの要素の基底クラス。 + * 要素を文字列型で返す(csmString型) */ - export abstract class Value { - /** - * コンストラクタ - */ - public constructor() {} + public abstract getString(defaultValue?: string, indent?: string): string; - /** - * 要素を文字列型で返す(csmString型) - */ - public abstract getString(defaultValue?: string, indent?: string): string; - - /** - * 要素を文字列型で返す(string) - */ - public getRawString(defaultValue?: string, indent?: string): string { - return this.getString(defaultValue, indent); - } - - /** - * 要素を数値型で返す(number) - */ - public toInt(defaultValue = 0): number { - return defaultValue; - } - - /** - * 要素を数値型で返す(number) - */ - public toFloat(defaultValue = 0): number { - return defaultValue; - } - - /** - * 要素を真偽値で返す(boolean) - */ - public toBoolean(defaultValue = false): boolean { - return defaultValue; - } - - /** - * サイズを返す - */ - public getSize(): number { - return 0; - } - - /** - * 要素を配列で返す(Value[]) - */ - public getArray(defaultValue: Value[] = null): Value[] { - return defaultValue; - } - - /** - * 要素をコンテナで返す(array) - */ - public getVector(defaultValue?: csmVector): csmVector { - return defaultValue; - } - - /** - * 要素をマップで返す(csmMap) - */ - public getMap(defaultValue?: csmMap): csmMap { - return defaultValue; - } - - /** - * 添字演算子[index] - */ - public getValueByIndex(index: number): Value { - return Value.errorValue.setErrorNotForClientCall( - CSM_JSON_ERROR_TYPE_MISMATCH - ); - } - - /** - * 添字演算子[string | csmString] - */ - public getValueByString(s: string | csmString): Value { - return Value.nullValue.setErrorNotForClientCall( - CSM_JSON_ERROR_TYPE_MISMATCH - ); - } - - /** - * マップのキー一覧をコンテナで返す - * - * @return マップのキーの一覧 - */ - public getKeys(): csmVector { - return Value.s_dummyKeys; - } - - /** - * Valueの種類がエラー値ならtrue - */ - public isError(): boolean { - return false; - } - - /** - * Valueの種類がnullならtrue - */ - public isNull(): boolean { - return false; - } - - /** - * Valueの種類が真偽値ならtrue - */ - public isBool(): boolean { - return false; - } - - /** - * Valueの種類が数値型ならtrue - */ - public isFloat(): boolean { - return false; - } - - /** - * Valueの種類が文字列ならtrue - */ - public isString(): boolean { - return false; - } - - /** - * Valueの種類が配列ならtrue - */ - public isArray(): boolean { - return false; - } - - /** - * Valueの種類がマップ型ならtrue - */ - public isMap(): boolean { - return false; - } - - /** - * 引数の値と等しければtrue - */ - public equals(value: csmString): boolean; - public equals(value: string): boolean; - public equals(value: number): boolean; - public equals(value: boolean): boolean; - public equals(value: any): boolean { - return false; - } - - /** - * Valueの値が静的ならtrue、静的なら解放しない - */ - public isStatic(): boolean { - return false; - } - - /** - * Valueにエラー値をセットする - */ - public setErrorNotForClientCall(errorStr: string): Value { - return JsonError.errorValue; - } - - /** - * 初期化用メソッド - */ - public static staticInitializeNotForClientCall(): void { - JsonBoolean.trueValue = new JsonBoolean(true); - JsonBoolean.falseValue = new JsonBoolean(false); - - JsonError.errorValue = new JsonError('ERROR', true); - this.nullValue = new JsonNullvalue(); - - Value.s_dummyKeys = new csmVector(); - } - - /** - * リリース用メソッド - */ - public static staticReleaseNotForClientCall(): void { - JsonBoolean.trueValue = null; - JsonBoolean.falseValue = null; - JsonError.errorValue = null; - Value.nullValue = null; - Value.s_dummyKeys = null; - - JsonBoolean.trueValue = null; - JsonBoolean.falseValue = null; - JsonError.errorValue = null; - Value.nullValue = null; - Value.s_dummyKeys = null; - } - - protected _stringBuffer: string; // 文字列バッファ - - private static s_dummyKeys: csmVector; // ダミーキー - - public static errorValue: Value; // 一時的な返り値として返すエラー。 CubismFramework::Disposeするまではdeleteしない - public static nullValue: Value; // 一時的な返り値として返すNULL。 CubismFramework::Disposeするまではdeleteしない + /** + * 要素を文字列型で返す(string) + */ + public getRawString(defaultValue?: string, indent?: string): string { + return this.getString(defaultValue, indent); } /** - * Ascii文字のみ対応した最小限の軽量JSONパーサ。 - * 仕様はJSONのサブセットとなる。 - * 設定ファイル(model3.json)などのロード用 - * - * [未対応項目] - * ・日本語などの非ASCII文字 - * ・eによる指数表現 + * 要素を数値型で返す(number) */ - export class CubismJson { - /** - * コンストラクタ - */ - public constructor(buffer?: ArrayBuffer, length?: number) { - this._error = null; - this._lineCount = 0; - this._root = null; + public toInt(defaultValue = 0): number { + return defaultValue; + } - if (buffer != undefined) { - this.parseBytes(buffer, length); - } + /** + * 要素を数値型で返す(number) + */ + public toFloat(defaultValue = 0): number { + return defaultValue; + } + + /** + * 要素を真偽値で返す(boolean) + */ + public toBoolean(defaultValue = false): boolean { + return defaultValue; + } + + /** + * サイズを返す + */ + public getSize(): number { + return 0; + } + + /** + * 要素を配列で返す(Value[]) + */ + public getArray(defaultValue: Value[] = null): Value[] { + return defaultValue; + } + + /** + * 要素をコンテナで返す(array) + */ + public getVector(defaultValue?: csmVector): csmVector { + return defaultValue; + } + + /** + * 要素をマップで返す(csmMap) + */ + public getMap(defaultValue?: csmMap): csmMap { + return defaultValue; + } + + /** + * 添字演算子[index] + */ + public getValueByIndex(index: number): Value { + return Value.errorValue.setErrorNotForClientCall( + CSM_JSON_ERROR_TYPE_MISMATCH + ); + } + + /** + * 添字演算子[string | csmString] + */ + public getValueByString(s: string | csmString): Value { + return Value.nullValue.setErrorNotForClientCall( + CSM_JSON_ERROR_TYPE_MISMATCH + ); + } + + /** + * マップのキー一覧をコンテナで返す + * + * @return マップのキーの一覧 + */ + public getKeys(): csmVector { + return Value.s_dummyKeys; + } + + /** + * Valueの種類がエラー値ならtrue + */ + public isError(): boolean { + return false; + } + + /** + * Valueの種類がnullならtrue + */ + public isNull(): boolean { + return false; + } + + /** + * Valueの種類が真偽値ならtrue + */ + public isBool(): boolean { + return false; + } + + /** + * Valueの種類が数値型ならtrue + */ + public isFloat(): boolean { + return false; + } + + /** + * Valueの種類が文字列ならtrue + */ + public isString(): boolean { + return false; + } + + /** + * Valueの種類が配列ならtrue + */ + public isArray(): boolean { + return false; + } + + /** + * Valueの種類がマップ型ならtrue + */ + public isMap(): boolean { + return false; + } + + /** + * 引数の値と等しければtrue + */ + public equals(value: csmString): boolean; + public equals(value: string): boolean; + public equals(value: number): boolean; + public equals(value: boolean): boolean; + public equals(value: any): boolean { + return false; + } + + /** + * Valueの値が静的ならtrue、静的なら解放しない + */ + public isStatic(): boolean { + return false; + } + + /** + * Valueにエラー値をセットする + */ + public setErrorNotForClientCall(errorStr: string): Value { + return JsonError.errorValue; + } + + /** + * 初期化用メソッド + */ + public static staticInitializeNotForClientCall(): void { + JsonBoolean.trueValue = new JsonBoolean(true); + JsonBoolean.falseValue = new JsonBoolean(false); + + JsonError.errorValue = new JsonError('ERROR', true); + this.nullValue = new JsonNullvalue(); + + Value.s_dummyKeys = new csmVector(); + } + + /** + * リリース用メソッド + */ + public static staticReleaseNotForClientCall(): void { + JsonBoolean.trueValue = null; + JsonBoolean.falseValue = null; + JsonError.errorValue = null; + Value.nullValue = null; + Value.s_dummyKeys = null; + + JsonBoolean.trueValue = null; + JsonBoolean.falseValue = null; + JsonError.errorValue = null; + Value.nullValue = null; + Value.s_dummyKeys = null; + } + + protected _stringBuffer: string; // 文字列バッファ + + private static s_dummyKeys: csmVector; // ダミーキー + + public static errorValue: Value; // 一時的な返り値として返すエラー。 CubismFramework::Disposeするまではdeleteしない + public static nullValue: Value; // 一時的な返り値として返すNULL。 CubismFramework::Disposeするまではdeleteしない +} + +/** + * Ascii文字のみ対応した最小限の軽量JSONパーサ。 + * 仕様はJSONのサブセットとなる。 + * 設定ファイル(model3.json)などのロード用 + * + * [未対応項目] + * ・日本語などの非ASCII文字 + * ・eによる指数表現 + */ +export class CubismJson { + /** + * コンストラクタ + */ + public constructor(buffer?: ArrayBuffer, length?: number) { + this._error = null; + this._lineCount = 0; + this._root = null; + + if (buffer != undefined) { + this.parseBytes(buffer, length); + } + } + + /** + * バイトデータから直接ロードしてパースする + * + * @param buffer バッファ + * @param size バッファサイズ + * @return CubismJsonクラスのインスタンス。失敗したらNULL + */ + public static create(buffer: ArrayBuffer, size: number) { + const json = new CubismJson(); + const succeeded: boolean = json.parseBytes(buffer, size); + + if (!succeeded) { + CubismJson.delete(json); + return null; + } else { + return json; + } + } + + /** + * パースしたJSONオブジェクトの解放処理 + * + * @param instance CubismJsonクラスのインスタンス + */ + public static delete(instance: CubismJson) { + instance = null; + } + + /** + * パースしたJSONのルート要素を返す + */ + public getRoot(): Value { + return this._root; + } + + /** + * UnicodeのバイナリをStringに変換 + * + * @param buffer 変換するバイナリデータ + * @return 変換後の文字列 + */ + public arrayBufferToString(buffer: ArrayBuffer): string { + const uint8Array: Uint8Array = new Uint8Array(buffer); + let str = ''; + + for (let i = 0, len: number = uint8Array.length; i < len; ++i) { + str += '%' + this.pad(uint8Array[i].toString(16)); } - /** - * バイトデータから直接ロードしてパースする - * - * @param buffer バッファ - * @param size バッファサイズ - * @return CubismJsonクラスのインスタンス。失敗したらNULL - */ - public static create(buffer: ArrayBuffer, size: number) { - const json = new CubismJson(); - const succeeded: boolean = json.parseBytes(buffer, size); + str = decodeURIComponent(str); + return str; + } - if (!succeeded) { - CubismJson.delete(json); - return null; - } else { - return json; - } + /** + * エンコード、パディング + */ + private pad(n: string): string { + return n.length < 2 ? '0' + n : n; + } + + /** + * JSONのパースを実行する + * @param buffer パース対象のデータバイト + * @param size データバイトのサイズ + * return true : 成功 + * return false: 失敗 + */ + public parseBytes(buffer: ArrayBuffer, size: number): boolean { + const endPos: number[] = new Array(1); // 参照渡しにするため配列 + const decodeBuffer: string = this.arrayBufferToString(buffer); + this._root = this.parseValue(decodeBuffer, size, 0, endPos); + + if (this._error) { + let strbuf = '\0'; + strbuf = 'Json parse error : @line ' + (this._lineCount + 1) + '\n'; + this._root = new JsonString(strbuf); + + CubismLogInfo('{0}', this._root.getRawString()); + return false; + } else if (this._root == null) { + this._root = new JsonError(new csmString(this._error), false); // rootは解放されるのでエラーオブジェクトを別途作成する + return false; } + return true; + } - /** - * パースしたJSONオブジェクトの解放処理 - * - * @param instance CubismJsonクラスのインスタンス - */ - public static delete(instance: CubismJson) { - instance = null; - } + /** + * パース時のエラー値を返す + */ + public getParseError(): string { + return this._error; + } - /** - * パースしたJSONのルート要素を返す - */ - public getRoot(): Value { - return this._root; - } + /** + * ルート要素の次の要素がファイルの終端だったらtrueを返す + */ + public checkEndOfFile(): boolean { + return this._root.getArray()[1].equals('EOF'); + } - /** - * UnicodeのバイナリをStringに変換 - * - * @param buffer 変換するバイナリデータ - * @return 変換後の文字列 - */ - public arrayBufferToString(buffer: ArrayBuffer): string { - const uint8Array: Uint8Array = new Uint8Array(buffer); - let str = ''; + /** + * JSONエレメントからValue(float,String,Value*,Array,null,true,false)をパースする + * エレメントの書式に応じて内部でParseString(), ParseObject(), ParseArray()を呼ぶ + * + * @param buffer JSONエレメントのバッファ + * @param length パースする長さ + * @param begin パースを開始する位置 + * @param outEndPos パース終了時の位置 + * @return パースから取得したValueオブジェクト + */ + protected parseValue( + buffer: string, + length: number, + begin: number, + outEndPos: number[] + ) { + if (this._error) return null; - for (let i = 0, len: number = uint8Array.length; i < len; ++i) { - str += '%' + this.pad(uint8Array[i].toString(16)); - } + let o: Value = null; + let i: number = begin; + let f: number; - str = decodeURIComponent(str); - return str; - } - - /** - * エンコード、パディング - */ - private pad(n: string): string { - return n.length < 2 ? '0' + n : n; - } - - /** - * JSONのパースを実行する - * @param buffer パース対象のデータバイト - * @param size データバイトのサイズ - * return true : 成功 - * return false: 失敗 - */ - public parseBytes(buffer: ArrayBuffer, size: number): boolean { - const endPos: number[] = new Array(1); // 参照渡しにするため配列 - const decodeBuffer: string = this.arrayBufferToString(buffer); - this._root = this.parseValue(decodeBuffer, size, 0, endPos); - - if (this._error) { - let strbuf = '\0'; - strbuf = 'Json parse error : @line ' + (this._lineCount + 1) + '\n'; - this._root = new JsonString(strbuf); - - CubismLogInfo('{0}', this._root.getRawString()); - return false; - } else if (this._root == null) { - this._root = new JsonError(new csmString(this._error), false); // rootは解放されるのでエラーオブジェクトを別途作成する - return false; - } - return true; - } - - /** - * パース時のエラー値を返す - */ - public getParseError(): string { - return this._error; - } - - /** - * ルート要素の次の要素がファイルの終端だったらtrueを返す - */ - public checkEndOfFile(): boolean { - return this._root.getArray()[1].equals('EOF'); - } - - /** - * JSONエレメントからValue(float,String,Value*,Array,null,true,false)をパースする - * エレメントの書式に応じて内部でParseString(), ParseObject(), ParseArray()を呼ぶ - * - * @param buffer JSONエレメントのバッファ - * @param length パースする長さ - * @param begin パースを開始する位置 - * @param outEndPos パース終了時の位置 - * @return パースから取得したValueオブジェクト - */ - protected parseValue( - buffer: string, - length: number, - begin: number, - outEndPos: number[] - ) { - if (this._error) return null; - - let o: Value = null; - let i: number = begin; - let f: number; - - for (; i < length; i++) { - const c: string = buffer[i]; - switch (c) { - case '-': - case '.': - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': { - const afterString: string[] = new Array(1); // 参照渡しにするため - f = strtod(buffer.slice(i), afterString); - outEndPos[0] = buffer.indexOf(afterString[0]); - return new JsonFloat(f); + for (; i < length; i++) { + const c: string = buffer[i]; + switch (c) { + case '-': + case '.': + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': { + const afterString: string[] = new Array(1); // 参照渡しにするため + f = strtod(buffer.slice(i), afterString); + outEndPos[0] = buffer.indexOf(afterString[0]); + return new JsonFloat(f); + } + case '"': + return new JsonString( + this.parseString(buffer, length, i + 1, outEndPos) + ); // \"の次の文字から + case '[': + o = this.parseArray(buffer, length, i + 1, outEndPos); + return o; + case '{': + o = this.parseObject(buffer, length, i + 1, outEndPos); + return o; + case 'n': // null以外にない + if (i + 3 < length) { + o = new JsonNullvalue(); // 解放できるようにする + outEndPos[0] = i + 4; + } else { + this._error = 'parse null'; } - case '"': - return new JsonString( - this.parseString(buffer, length, i + 1, outEndPos) - ); // \"の次の文字から - case '[': - o = this.parseArray(buffer, length, i + 1, outEndPos); - return o; - case '{': - o = this.parseObject(buffer, length, i + 1, outEndPos); - return o; - case 'n': // null以外にない - if (i + 3 < length) { - o = new JsonNullvalue(); // 解放できるようにする - outEndPos[0] = i + 4; - } else { - this._error = 'parse null'; - } - return o; - case 't': // true以外にない - if (i + 3 < length) { - o = JsonBoolean.trueValue; - outEndPos[0] = i + 4; - } else { - this._error = 'parse true'; - } - return o; - case 'f': // false以外にない - if (i + 4 < length) { - o = JsonBoolean.falseValue; - outEndPos[0] = i + 5; - } else { - this._error = "illegal ',' position"; - } - return o; - case ',': // Array separator + return o; + case 't': // true以外にない + if (i + 3 < length) { + o = JsonBoolean.trueValue; + outEndPos[0] = i + 4; + } else { + this._error = 'parse true'; + } + return o; + case 'f': // false以外にない + if (i + 4 < length) { + o = JsonBoolean.falseValue; + outEndPos[0] = i + 5; + } else { this._error = "illegal ',' position"; - return null; - case ']': // 不正な}だがスキップする。配列の最後に不要な , があると思われる - outEndPos[0] = i; // 同じ文字を再処理 - return null; + } + return o; + case ',': // Array separator + this._error = "illegal ',' position"; + return null; + case ']': // 不正な}だがスキップする。配列の最後に不要な , があると思われる + outEndPos[0] = i; // 同じ文字を再処理 + return null; + case '\n': + this._lineCount++; + case ' ': + case '\t': + case '\r': + default: + // スキップ + break; + } + } + + this._error = 'illegal end of value'; + return null; + } + + /** + * 次の「"」までの文字列をパースする。 + * + * @param string -> パース対象の文字列 + * @param length -> パースする長さ + * @param begin -> パースを開始する位置 + * @param outEndPos -> パース終了時の位置 + * @return パースした文F字列要素 + */ + protected parseString( + string: string, + length: number, + begin: number, + outEndPos: number[] + ): string { + if (this._error) return null; + + let i = begin; + let c: string, c2: string; + const ret: csmString = new csmString(''); + let bufStart: number = begin; // sbufに登録されていない文字の開始位置 + + for (; i < length; i++) { + c = string[i]; + + switch (c) { + case '"': { + // 終端の”、エスケープ文字は別に処理されるのでここに来ない + outEndPos[0] = i + 1; // ”の次の文字 + ret.append(string.slice(bufStart), i - bufStart); // 前の文字までを登録する + return ret.s; + } + case '//': { + // エスケープの場合 + i++; // 2文字をセットで扱う + + if (i - 1 > bufStart) { + ret.append(string.slice(bufStart), i - bufStart); // 前の文字までを登録する + } + bufStart = i + 1; // エスケープ(2文字)の次の文字から + + if (i < length) { + c2 = string[i]; + + switch (c2) { + case '\\': + ret.expansion(1, '\\'); + break; + case '"': + ret.expansion(1, '"'); + break; + case '/': + ret.expansion(1, '/'); + break; + case 'b': + ret.expansion(1, '\b'); + break; + case 'f': + ret.expansion(1, '\f'); + break; + case 'n': + ret.expansion(1, '\n'); + break; + case 'r': + ret.expansion(1, '\r'); + break; + case 't': + ret.expansion(1, '\t'); + break; + case 'u': + this._error = 'parse string/unicord escape not supported'; + break; + default: + break; + } + } else { + this._error = 'parse string/escape error'; + } + } + default: { + break; + } + } + } + + this._error = 'parse string/illegal end'; + return null; + } + + /** + * JSONのオブジェクトエレメントをパースしてValueオブジェクトを返す + * + * @param buffer JSONエレメントのバッファ + * @param length パースする長さ + * @param begin パースを開始する位置 + * @param outEndPos パース終了時の位置 + * @return パースから取得したValueオブジェクト + */ + protected parseObject( + buffer: string, + length: number, + begin: number, + outEndPos: number[] + ): Value { + if (this._error) return null; + const ret: JsonMap = new JsonMap(); + + // Key: Value + let key = ''; + let i: number = begin; + let c = ''; + const localRetEndPos2: number[] = Array(1); + let ok = false; + + // , が続く限りループ + for (; i < length; i++) { + FOR_LOOP: for (; i < length; i++) { + c = buffer[i]; + + switch (c) { + case '"': + key = this.parseString(buffer, length, i + 1, localRetEndPos2); + if (this._error) { + return null; + } + + i = localRetEndPos2[0]; + ok = true; + break FOR_LOOP; //-- loopから出る + case '}': // 閉じカッコ + outEndPos[0] = i + 1; + return ret; // 空 + case ':': + this._error = "illegal ':' position"; + break; case '\n': this._lineCount++; - case ' ': - case '\t': - case '\r': default: - // スキップ - break; + break; // スキップする文字 } } + if (!ok) { + this._error = 'key not found'; + return null; + } - this._error = 'illegal end of value'; - return null; - } + ok = false; - /** - * 次の「"」までの文字列をパースする。 - * - * @param string -> パース対象の文字列 - * @param length -> パースする長さ - * @param begin -> パースを開始する位置 - * @param outEndPos -> パース終了時の位置 - * @return パースした文F字列要素 - */ - protected parseString( - string: string, - length: number, - begin: number, - outEndPos: number[] - ): string { - if (this._error) return null; - - let i = begin; - let c: string, c2: string; - const ret: csmString = new csmString(''); - let bufStart: number = begin; // sbufに登録されていない文字の開始位置 - - for (; i < length; i++) { - c = string[i]; + // : をチェック + FOR_LOOP2: for (; i < length; i++) { + c = buffer[i]; switch (c) { - case '"': { - // 終端の”、エスケープ文字は別に処理されるのでここに来ない - outEndPos[0] = i + 1; // ”の次の文字 - ret.append(string.slice(bufStart), i - bufStart); // 前の文字までを登録する - return ret.s; - } - case '//': { - // エスケープの場合 - i++; // 2文字をセットで扱う - - if (i - 1 > bufStart) { - ret.append(string.slice(bufStart), i - bufStart); // 前の文字までを登録する - } - bufStart = i + 1; // エスケープ(2文字)の次の文字から - - if (i < length) { - c2 = string[i]; - - switch (c2) { - case '\\': - ret.expansion(1, '\\'); - break; - case '"': - ret.expansion(1, '"'); - break; - case '/': - ret.expansion(1, '/'); - break; - case 'b': - ret.expansion(1, '\b'); - break; - case 'f': - ret.expansion(1, '\f'); - break; - case 'n': - ret.expansion(1, '\n'); - break; - case 'r': - ret.expansion(1, '\r'); - break; - case 't': - ret.expansion(1, '\t'); - break; - case 'u': - this._error = 'parse string/unicord escape not supported'; - break; - default: - break; - } - } else { - this._error = 'parse string/escape error'; - } - } - default: { + case ':': + ok = true; + i++; + break FOR_LOOP2; + case '}': + this._error = "illegal '}' position"; break; - } + case '\n': + this._lineCount++; + // case ' ': case '\t' : case '\r': + default: + break; // スキップする文字 } } - this._error = 'parse string/illegal end'; - return null; - } - - /** - * JSONのオブジェクトエレメントをパースしてValueオブジェクトを返す - * - * @param buffer JSONエレメントのバッファ - * @param length パースする長さ - * @param begin パースを開始する位置 - * @param outEndPos パース終了時の位置 - * @return パースから取得したValueオブジェクト - */ - protected parseObject( - buffer: string, - length: number, - begin: number, - outEndPos: number[] - ): Value { - if (this._error) return null; - const ret: JsonMap = new JsonMap(); - - // Key: Value - let key = ''; - let i: number = begin; - let c = ''; - const localRetEndPos2: number[] = Array(1); - let ok = false; - - // , が続く限りループ - for (; i < length; i++) { - FOR_LOOP: for (; i < length; i++) { - c = buffer[i]; - - switch (c) { - case '"': - key = this.parseString(buffer, length, i + 1, localRetEndPos2); - if (this._error) { - return null; - } - - i = localRetEndPos2[0]; - ok = true; - break FOR_LOOP; //-- loopから出る - case '}': // 閉じカッコ - outEndPos[0] = i + 1; - return ret; // 空 - case ':': - this._error = "illegal ':' position"; - break; - case '\n': - this._lineCount++; - default: - break; // スキップする文字 - } - } - if (!ok) { - this._error = 'key not found'; - return null; - } - - ok = false; - - // : をチェック - FOR_LOOP2: for (; i < length; i++) { - c = buffer[i]; - - switch (c) { - case ':': - ok = true; - i++; - break FOR_LOOP2; - case '}': - this._error = "illegal '}' position"; - break; - case '\n': - this._lineCount++; - // case ' ': case '\t' : case '\r': - default: - break; // スキップする文字 - } - } - - if (!ok) { - this._error = "':' not found"; - return null; - } - - // 値をチェック - const value: Value = this.parseValue( - buffer, - length, - i, - localRetEndPos2 - ); - if (this._error) { - return null; - } - - i = localRetEndPos2[0]; - - // ret.put(key, value); - ret.put(key, value); - - FOR_LOOP3: for (; i < length; i++) { - c = buffer[i]; - - switch (c) { - case ',': - break FOR_LOOP3; - case '}': - outEndPos[0] = i + 1; - return ret; // 正常終了 - case '\n': - this._lineCount++; - default: - break; // スキップ - } - } + if (!ok) { + this._error = "':' not found"; + return null; } - this._error = 'illegal end of perseObject'; - return null; - } - - /** - * 次の「"」までの文字列をパースする。 - * @param buffer JSONエレメントのバッファ - * @param length パースする長さ - * @param begin パースを開始する位置 - * @param outEndPos パース終了時の位置 - * @return パースから取得したValueオブジェクト - */ - protected parseArray( - buffer: string, - length: number, - begin: number, - outEndPos: number[] - ): Value { - if (this._error) return null; - let ret: JsonArray = new JsonArray(); - - // key : value - let i: number = begin; - let c: string; - const localRetEndpos2: number[] = new Array(1); - - // , が続く限りループ - for (; i < length; i++) { - // : をチェック - const value: Value = this.parseValue( - buffer, - length, - i, - localRetEndpos2 - ); - - if (this._error) { - return null; - } - i = localRetEndpos2[0]; - - if (value) { - ret.add(value); - } - - // FOR_LOOP3: - // boolean breakflag = false; - FOR_LOOP: for (; i < length; i++) { - c = buffer[i]; - - switch (c) { - case ',': - // breakflag = true; - // break; // 次のKEY, VAlUEへ - break FOR_LOOP; - case ']': - outEndPos[0] = i + 1; - return ret; // 終了 - case '\n': - ++this._lineCount; - //case ' ': case '\t': case '\r': - default: - break; // スキップ - } - } + // 値をチェック + const value: Value = this.parseValue(buffer, length, i, localRetEndPos2); + if (this._error) { + return null; } - ret = void 0; - this._error = 'illegal end of parseObject'; - return null; + i = localRetEndPos2[0]; + + // ret.put(key, value); + ret.put(key, value); + + FOR_LOOP3: for (; i < length; i++) { + c = buffer[i]; + + switch (c) { + case ',': + break FOR_LOOP3; + case '}': + outEndPos[0] = i + 1; + return ret; // 正常終了 + case '\n': + this._lineCount++; + default: + break; // スキップ + } + } } - _error: string; // パース時のエラー - _lineCount: number; // エラー報告に用いる行数カウント - _root: Value; // パースされたルート要素 + this._error = 'illegal end of perseObject'; + return null; } /** - * パースしたJSONの要素をfloat値として扱う + * 次の「"」までの文字列をパースする。 + * @param buffer JSONエレメントのバッファ + * @param length パースする長さ + * @param begin パースを開始する位置 + * @param outEndPos パース終了時の位置 + * @return パースから取得したValueオブジェクト */ - export class JsonFloat extends Value { - /** - * コンストラクタ - */ - constructor(v: number) { - super(); + protected parseArray( + buffer: string, + length: number, + begin: number, + outEndPos: number[] + ): Value { + if (this._error) return null; + let ret: JsonArray = new JsonArray(); - this._value = v; - } + // key : value + let i: number = begin; + let c: string; + const localRetEndpos2: number[] = new Array(1); - /** - * Valueの種類が数値型ならtrue - */ - public isFloat(): boolean { - return true; - } + // , が続く限りループ + for (; i < length; i++) { + // : をチェック + const value: Value = this.parseValue(buffer, length, i, localRetEndpos2); - /** - * 要素を文字列で返す(csmString型) - */ - public getString(defaultValue: string, indent: string): string { - const strbuf = '\0'; - this._value = parseFloat(strbuf); - this._stringBuffer = strbuf; + if (this._error) { + return null; + } + i = localRetEndpos2[0]; - return this._stringBuffer; - } + if (value) { + ret.add(value); + } - /** - * 要素を数値型で返す(number) - */ - public toInt(defaultValue = 0): number { - return parseInt(this._value.toString()); - } + // FOR_LOOP3: + // boolean breakflag = false; + FOR_LOOP: for (; i < length; i++) { + c = buffer[i]; - /** - * 要素を数値型で返す(number) - */ - public toFloat(defaultValue = 0.0): number { - return this._value; - } - - /** - * 引数の値と等しければtrue - */ - public equals(value: csmString): boolean; - public equals(value: string): boolean; - public equals(value: number): boolean; - public equals(value: boolean): boolean; - public equals(value: any): boolean { - if ('number' === typeof value) { - // int - if (Math.round(value)) { - return false; - } - // float - else { - return value == this._value; + switch (c) { + case ',': + // breakflag = true; + // break; // 次のKEY, VAlUEへ + break FOR_LOOP; + case ']': + outEndPos[0] = i + 1; + return ret; // 終了 + case '\n': + ++this._lineCount; + //case ' ': case '\t': case '\r': + default: + break; // スキップ } } - return false; } - private _value: number; // JSON要素の値 + ret = void 0; + this._error = 'illegal end of parseObject'; + return null; + } + + _error: string; // パース時のエラー + _lineCount: number; // エラー報告に用いる行数カウント + _root: Value; // パースされたルート要素 +} + +/** + * パースしたJSONの要素をfloat値として扱う + */ +export class JsonFloat extends Value { + /** + * コンストラクタ + */ + constructor(v: number) { + super(); + + this._value = v; } /** - * パースしたJSONの要素を真偽値として扱う + * Valueの種類が数値型ならtrue */ - export class JsonBoolean extends Value { - /** - * Valueの種類が真偽値ならtrue - */ - public isBool(): boolean { - return true; - } - - /** - * 要素を真偽値で返す(boolean) - */ - public toBoolean(defaultValue = false): boolean { - return this._boolValue; - } - - /** - * 要素を文字列で返す(csmString型) - */ - public getString(defaultValue: string, indent: string): string { - this._stringBuffer = this._boolValue ? 'true' : 'false'; - - return this._stringBuffer; - } - - /** - * 引数の値と等しければtrue - */ - public equals(value: csmString): boolean; - public equals(value: string): boolean; - public equals(value: number): boolean; - public equals(value: boolean): boolean; - public equals(value: any): boolean { - if ('boolean' === typeof value) { - return value == this._boolValue; - } - return false; - } - - /** - * Valueの値が静的ならtrue, 静的なら解放しない - */ - public isStatic(): boolean { - return true; - } - - /** - * 引数付きコンストラクタ - */ - public constructor(v: boolean) { - super(); - - this._boolValue = v; - } - - static trueValue: JsonBoolean; // true - static falseValue: JsonBoolean; // false - - private _boolValue: boolean; // JSON要素の値 + public isFloat(): boolean { + return true; } /** - * パースしたJSONの要素を文字列として扱う + * 要素を文字列で返す(csmString型) */ - export class JsonString extends Value { - /** - * 引数付きコンストラクタ - */ - public constructor(s: string); - public constructor(s: csmString); - public constructor(s: any) { - super(); + public getString(defaultValue: string, indent: string): string { + const strbuf = '\0'; + this._value = parseFloat(strbuf); + this._stringBuffer = strbuf; - if ('string' === typeof s) { - this._stringBuffer = s; - } - - if (s instanceof csmString) { - this._stringBuffer = s.s; - } - } - - /** - * Valueの種類が文字列ならtrue - */ - public isString(): boolean { - return true; - } - - /** - * 要素を文字列で返す(csmString型) - */ - public getString(defaultValue: string, indent: string): string { - return this._stringBuffer; - } - - /** - * 引数の値と等しければtrue - */ - public equals(value: csmString): boolean; - public equals(value: string): boolean; - public equals(value: number): boolean; - public equals(value: boolean): boolean; - public equals(value: any): boolean { - if ('string' === typeof value) { - return this._stringBuffer == value; - } - - if (value instanceof csmString) { - return this._stringBuffer == value.s; - } - - return false; - } + return this._stringBuffer; } /** - * JSONパース時のエラー結果。文字列型のようにふるまう + * 要素を数値型で返す(number) */ - export class JsonError extends JsonString { - /** - * Valueの値が静的ならtrue、静的なら解放しない - */ - public isStatic(): boolean { - return this._isStatic; - } + public toInt(defaultValue = 0): number { + return parseInt(this._value.toString()); + } - /** - * エラー情報をセットする - */ - public setErrorNotForClientCall(s: string): Value { + /** + * 要素を数値型で返す(number) + */ + public toFloat(defaultValue = 0.0): number { + return this._value; + } + + /** + * 引数の値と等しければtrue + */ + public equals(value: csmString): boolean; + public equals(value: string): boolean; + public equals(value: number): boolean; + public equals(value: boolean): boolean; + public equals(value: any): boolean { + if ('number' === typeof value) { + // int + if (Math.round(value)) { + return false; + } + // float + else { + return value == this._value; + } + } + return false; + } + + private _value: number; // JSON要素の値 +} + +/** + * パースしたJSONの要素を真偽値として扱う + */ +export class JsonBoolean extends Value { + /** + * Valueの種類が真偽値ならtrue + */ + public isBool(): boolean { + return true; + } + + /** + * 要素を真偽値で返す(boolean) + */ + public toBoolean(defaultValue = false): boolean { + return this._boolValue; + } + + /** + * 要素を文字列で返す(csmString型) + */ + public getString(defaultValue: string, indent: string): string { + this._stringBuffer = this._boolValue ? 'true' : 'false'; + + return this._stringBuffer; + } + + /** + * 引数の値と等しければtrue + */ + public equals(value: csmString): boolean; + public equals(value: string): boolean; + public equals(value: number): boolean; + public equals(value: boolean): boolean; + public equals(value: any): boolean { + if ('boolean' === typeof value) { + return value == this._boolValue; + } + return false; + } + + /** + * Valueの値が静的ならtrue, 静的なら解放しない + */ + public isStatic(): boolean { + return true; + } + + /** + * 引数付きコンストラクタ + */ + public constructor(v: boolean) { + super(); + + this._boolValue = v; + } + + static trueValue: JsonBoolean; // true + static falseValue: JsonBoolean; // false + + private _boolValue: boolean; // JSON要素の値 +} + +/** + * パースしたJSONの要素を文字列として扱う + */ +export class JsonString extends Value { + /** + * 引数付きコンストラクタ + */ + public constructor(s: string); + public constructor(s: csmString); + public constructor(s: any) { + super(); + + if ('string' === typeof s) { this._stringBuffer = s; - return this; } - /** - * 引数付きコンストラクタ - */ - public constructor(s: csmString | string, isStatic: boolean) { - if ('string' === typeof s) { - super(s); - } else { - super(s); - } - this._isStatic = isStatic; - } - - /** - * Valueの種類がエラー値ならtrue - */ - public isError(): boolean { - return true; - } - - protected _isStatic: boolean; // 静的なValueかどうか - } - - /** - * パースしたJSONの要素をNULL値として持つ - */ - export class JsonNullvalue extends Value { - /** - * Valueの種類がNULL値ならtrue - */ - public isNull(): boolean { - return true; - } - - /** - * 要素を文字列で返す(csmString型) - */ - public getString(defaultValue: string, indent: string): string { - return this._stringBuffer; - } - - /** - * Valueの値が静的ならtrue, 静的なら解放しない - */ - public isStatic(): boolean { - return true; - } - - /** - * コンストラクタ - */ - public constructor() { - super(); - - this._stringBuffer = 'NullValue'; + if (s instanceof csmString) { + this._stringBuffer = s.s; } } /** - * パースしたJSONの要素を配列として持つ + * Valueの種類が文字列ならtrue */ - export class JsonArray extends Value { - /** - * コンストラクタ - */ - public constructor() { - super(); - this._array = new csmVector(); + public isString(): boolean { + return true; + } + + /** + * 要素を文字列で返す(csmString型) + */ + public getString(defaultValue: string, indent: string): string { + return this._stringBuffer; + } + + /** + * 引数の値と等しければtrue + */ + public equals(value: csmString): boolean; + public equals(value: string): boolean; + public equals(value: number): boolean; + public equals(value: boolean): boolean; + public equals(value: any): boolean { + if ('string' === typeof value) { + return this._stringBuffer == value; } - /** - * デストラクタ相当の処理 - */ - public release(): void { - for ( - let ite: csmVector_iterator = this._array.begin(); - ite.notEqual(this._array.end()); - ite.preIncrement() - ) { - let v: Value = ite.ptr(); + if (value instanceof csmString) { + return this._stringBuffer == value.s; + } - if (v && !v.isStatic()) { - v = void 0; - v = null; - } + return false; + } +} + +/** + * JSONパース時のエラー結果。文字列型のようにふるまう + */ +export class JsonError extends JsonString { + /** + * Valueの値が静的ならtrue、静的なら解放しない + */ + public isStatic(): boolean { + return this._isStatic; + } + + /** + * エラー情報をセットする + */ + public setErrorNotForClientCall(s: string): Value { + this._stringBuffer = s; + return this; + } + + /** + * 引数付きコンストラクタ + */ + public constructor(s: csmString | string, isStatic: boolean) { + if ('string' === typeof s) { + super(s); + } else { + super(s); + } + this._isStatic = isStatic; + } + + /** + * Valueの種類がエラー値ならtrue + */ + public isError(): boolean { + return true; + } + + protected _isStatic: boolean; // 静的なValueかどうか +} + +/** + * パースしたJSONの要素をNULL値として持つ + */ +export class JsonNullvalue extends Value { + /** + * Valueの種類がNULL値ならtrue + */ + public isNull(): boolean { + return true; + } + + /** + * 要素を文字列で返す(csmString型) + */ + public getString(defaultValue: string, indent: string): string { + return this._stringBuffer; + } + + /** + * Valueの値が静的ならtrue, 静的なら解放しない + */ + public isStatic(): boolean { + return true; + } + + /** + * コンストラクタ + */ + public constructor() { + super(); + + this._stringBuffer = 'NullValue'; + } +} + +/** + * パースしたJSONの要素を配列として持つ + */ +export class JsonArray extends Value { + /** + * コンストラクタ + */ + public constructor() { + super(); + this._array = new csmVector(); + } + + /** + * デストラクタ相当の処理 + */ + public release(): void { + for ( + let ite: csmVector_iterator = this._array.begin(); + ite.notEqual(this._array.end()); + ite.preIncrement() + ) { + let v: Value = ite.ptr(); + + if (v && !v.isStatic()) { + v = void 0; + v = null; } } + } - /** - * Valueの種類が配列ならtrue - */ - public isArray(): boolean { - return true; - } + /** + * Valueの種類が配列ならtrue + */ + public isArray(): boolean { + return true; + } - /** - * 添字演算子[index] - */ - public getValueByIndex(index: number): Value { - if (index < 0 || this._array.getSize() <= index) { - return Value.errorValue.setErrorNotForClientCall( - CSM_JSON_ERROR_INDEX_OF_BOUNDS - ); - } - - const v: Value = this._array.at(index); - - if (v == null) { - return Value.nullValue; - } - - return v; - } - - /** - * 添字演算子[string | csmString] - */ - public getValueByString(s: string | csmString): Value { + /** + * 添字演算子[index] + */ + public getValueByIndex(index: number): Value { + if (index < 0 || this._array.getSize() <= index) { return Value.errorValue.setErrorNotForClientCall( - CSM_JSON_ERROR_TYPE_MISMATCH + CSM_JSON_ERROR_INDEX_OF_BOUNDS ); } - /** - * 要素を文字列で返す(csmString型) - */ - public getString(defaultValue: string, indent: string): string { - const stringBuffer: string = indent + '[\n'; - - for ( - let ite: csmVector_iterator = this._array.begin(); - ite.notEqual(this._array.end()); - ite.increment() - ) { - const v: Value = ite.ptr(); - this._stringBuffer += indent + '' + v.getString(indent + ' ') + '\n'; - } - - this._stringBuffer = stringBuffer + indent + ']\n'; - - return this._stringBuffer; - } - - /** - * 配列要素を追加する - * @param v 追加する要素 - */ - public add(v: Value): void { - this._array.pushBack(v); - } - - /** - * 要素をコンテナで返す(csmVector) - */ - public getVector(defaultValue: csmVector = null): csmVector { - return this._array; - } - - /** - * 要素の数を返す - */ - public getSize(): number { - return this._array.getSize(); - } - - private _array: csmVector; // JSON要素の値 - } - - /** - * パースしたJSONの要素をマップとして持つ - */ - export class JsonMap extends Value { - /** - * コンストラクタ - */ - public constructor() { - super(); - this._map = new csmMap(); - } - - /** - * デストラクタ相当の処理 - */ - public release(): void { - const ite: csmMap_iterator = this._map.begin(); - - while (ite.notEqual(this._map.end())) { - let v: Value = ite.ptr().second; - - if (v && !v.isStatic()) { - v = void 0; - v = null; - } - - ite.preIncrement(); - } - } - - /** - * Valueの値がMap型ならtrue - */ - public isMap(): boolean { - return true; - } - - /** - * 添字演算子[string | csmString] - */ - public getValueByString(s: string | csmString): Value { - if (s instanceof csmString) { - const ret: Value = this._map.getValue(s.s); - if (ret == null) { - return Value.nullValue; - } - return ret; - } - - for ( - let iter: csmMap_iterator = this._map.begin(); - iter.notEqual(this._map.end()); - iter.preIncrement() - ) { - if (iter.ptr().first == s) { - if (iter.ptr().second == null) { - return Value.nullValue; - } - return iter.ptr().second; - } - } + const v: Value = this._array.at(index); + if (v == null) { return Value.nullValue; } - /** - * 添字演算子[index] - */ - public getValueByIndex(index: number): Value { - return Value.errorValue.setErrorNotForClientCall( - CSM_JSON_ERROR_TYPE_MISMATCH - ); + return v; + } + + /** + * 添字演算子[string | csmString] + */ + public getValueByString(s: string | csmString): Value { + return Value.errorValue.setErrorNotForClientCall( + CSM_JSON_ERROR_TYPE_MISMATCH + ); + } + + /** + * 要素を文字列で返す(csmString型) + */ + public getString(defaultValue: string, indent: string): string { + const stringBuffer: string = indent + '[\n'; + + for ( + let ite: csmVector_iterator = this._array.begin(); + ite.notEqual(this._array.end()); + ite.increment() + ) { + const v: Value = ite.ptr(); + this._stringBuffer += indent + '' + v.getString(indent + ' ') + '\n'; } - /** - * 要素を文字列で返す(csmString型) - */ - public getString(defaultValue: string, indent: string) { - this._stringBuffer = indent + '{\n'; + this._stringBuffer = stringBuffer + indent + ']\n'; + + return this._stringBuffer; + } + + /** + * 配列要素を追加する + * @param v 追加する要素 + */ + public add(v: Value): void { + this._array.pushBack(v); + } + + /** + * 要素をコンテナで返す(csmVector) + */ + public getVector(defaultValue: csmVector = null): csmVector { + return this._array; + } + + /** + * 要素の数を返す + */ + public getSize(): number { + return this._array.getSize(); + } + + private _array: csmVector; // JSON要素の値 +} + +/** + * パースしたJSONの要素をマップとして持つ + */ +export class JsonMap extends Value { + /** + * コンストラクタ + */ + public constructor() { + super(); + this._map = new csmMap(); + } + + /** + * デストラクタ相当の処理 + */ + public release(): void { + const ite: csmMap_iterator = this._map.begin(); + + while (ite.notEqual(this._map.end())) { + let v: Value = ite.ptr().second; + + if (v && !v.isStatic()) { + v = void 0; + v = null; + } + + ite.preIncrement(); + } + } + + /** + * Valueの値がMap型ならtrue + */ + public isMap(): boolean { + return true; + } + + /** + * 添字演算子[string | csmString] + */ + public getValueByString(s: string | csmString): Value { + if (s instanceof csmString) { + const ret: Value = this._map.getValue(s.s); + if (ret == null) { + return Value.nullValue; + } + return ret; + } + + for ( + let iter: csmMap_iterator = this._map.begin(); + iter.notEqual(this._map.end()); + iter.preIncrement() + ) { + if (iter.ptr().first == s) { + if (iter.ptr().second == null) { + return Value.nullValue; + } + return iter.ptr().second; + } + } + + return Value.nullValue; + } + + /** + * 添字演算子[index] + */ + public getValueByIndex(index: number): Value { + return Value.errorValue.setErrorNotForClientCall( + CSM_JSON_ERROR_TYPE_MISMATCH + ); + } + + /** + * 要素を文字列で返す(csmString型) + */ + public getString(defaultValue: string, indent: string) { + this._stringBuffer = indent + '{\n'; + + const ite: csmMap_iterator = this._map.begin(); + while (ite.notEqual(this._map.end())) { + const key = ite.ptr().first; + const v: Value = ite.ptr().second; + + this._stringBuffer += + indent + ' ' + key + ' : ' + v.getString(indent + ' ') + ' \n'; + ite.preIncrement(); + } + + this._stringBuffer += indent + '}\n'; + + return this._stringBuffer; + } + + /** + * 要素をMap型で返す + */ + public getMap(defaultValue?: csmMap): csmMap { + return this._map; + } + + /** + * Mapに要素を追加する + */ + public put(key: string, v: Value): void { + this._map.setValue(key, v); + } + + /** + * Mapからキーのリストを取得する + */ + public getKeys(): csmVector { + if (!this._keys) { + this._keys = new csmVector(); const ite: csmMap_iterator = this._map.begin(); - while (ite.notEqual(this._map.end())) { - const key = ite.ptr().first; - const v: Value = ite.ptr().second; - this._stringBuffer += - indent + ' ' + key + ' : ' + v.getString(indent + ' ') + ' \n'; + while (ite.notEqual(this._map.end())) { + const key: string = ite.ptr().first; + this._keys.pushBack(key); ite.preIncrement(); } - - this._stringBuffer += indent + '}\n'; - - return this._stringBuffer; } - - /** - * 要素をMap型で返す - */ - public getMap(defaultValue?: csmMap): csmMap { - return this._map; - } - - /** - * Mapに要素を追加する - */ - public put(key: string, v: Value): void { - this._map.setValue(key, v); - } - - /** - * Mapからキーのリストを取得する - */ - public getKeys(): csmVector { - if (!this._keys) { - this._keys = new csmVector(); - - const ite: csmMap_iterator = this._map.begin(); - - while (ite.notEqual(this._map.end())) { - const key: string = ite.ptr().first; - this._keys.pushBack(key); - ite.preIncrement(); - } - } - return this._keys; - } - - /** - * Mapの要素数を取得する - */ - public getSize(): number { - return this._keys.getSize(); - } - - private _map: csmMap; // JSON要素の値 - private _keys: csmVector; // JSON要素の値 + return this._keys; } + + /** + * Mapの要素数を取得する + */ + public getSize(): number { + return this._keys.getSize(); + } + + private _map: csmMap; // JSON要素の値 + private _keys: csmVector; // JSON要素の値 +} + +// Namespace definition for compatibility. +import * as $ from './cubismjson'; +// eslint-disable-next-line @typescript-eslint/no-namespace +export namespace Live2DCubismFramework { + export const CubismJson = $.CubismJson; + export type CubismJson = $.CubismJson; + export const JsonArray = $.JsonArray; + export type JsonArray = $.JsonArray; + export const JsonBoolean = $.JsonBoolean; + export type JsonBoolean = $.JsonBoolean; + export const JsonError = $.JsonError; + export type JsonError = $.JsonError; + export const JsonFloat = $.JsonFloat; + export type JsonFloat = $.JsonFloat; + export const JsonMap = $.JsonMap; + export type JsonMap = $.JsonMap; + export const JsonNullvalue = $.JsonNullvalue; + export type JsonNullvalue = $.JsonNullvalue; + export const JsonString = $.JsonString; + export type JsonString = $.JsonString; + export const Value = $.Value; + export type Value = $.Value; } diff --git a/src/utils/cubismstring.ts b/src/utils/cubismstring.ts index a835347..eaeccd4 100644 --- a/src/utils/cubismstring.ts +++ b/src/utils/cubismstring.ts @@ -5,119 +5,125 @@ * that can be found at https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html. */ -export namespace Live2DCubismFramework { - export class CubismString { - /** - * 標準出力の書式を適用した文字列を取得する。 - * @param format 標準出力の書式指定文字列 - * @param ...args 書式指定文字列に渡す文字列 - * @return 書式を適用した文字列 - */ - public static getFormatedString(format: string, ...args: any[]): string { - const ret: string = format; - return ret.replace( - /\{(\d+)\}/g, - ( - m, - k // m="{0}", k="0" - ) => { - return args[k]; - } - ); - } - - /** - * textがstartWordで始まっているかどうかを返す - * @param test 検査対象の文字列 - * @param startWord 比較対象の文字列 - * @return true textがstartWordで始まっている - * @return false textがstartWordで始まっていない - */ - public static isStartWith(text: string, startWord: string): boolean { - let textIndex = 0; - let startWordIndex = 0; - while (startWord[startWordIndex] != '\0') { - if ( - text[textIndex] == '\0' || - text[textIndex++] != startWord[startWordIndex++] - ) { - return false; - } +export class CubismString { + /** + * 標準出力の書式を適用した文字列を取得する。 + * @param format 標準出力の書式指定文字列 + * @param ...args 書式指定文字列に渡す文字列 + * @return 書式を適用した文字列 + */ + public static getFormatedString(format: string, ...args: any[]): string { + const ret: string = format; + return ret.replace( + /\{(\d+)\}/g, + ( + m, + k // m="{0}", k="0" + ) => { + return args[k]; } - return false; + ); + } + + /** + * textがstartWordで始まっているかどうかを返す + * @param test 検査対象の文字列 + * @param startWord 比較対象の文字列 + * @return true textがstartWordで始まっている + * @return false textがstartWordで始まっていない + */ + public static isStartWith(text: string, startWord: string): boolean { + let textIndex = 0; + let startWordIndex = 0; + while (startWord[startWordIndex] != '\0') { + if ( + text[textIndex] == '\0' || + text[textIndex++] != startWord[startWordIndex++] + ) { + return false; + } + } + return false; + } + + /** + * position位置の文字から数字を解析する。 + * + * @param string 文字列 + * @param length 文字列の長さ + * @param position 解析したい文字の位置 + * @param outEndPos 一文字も読み込まなかった場合はエラー値(-1)が入る + * @return 解析結果の数値 + */ + public static stringToFloat( + string: string, + length: number, + position: number, + outEndPos: number[] + ): number { + let i: number = position; + let minus = false; // マイナスフラグ + let period = false; + let v1 = 0; + + //負号の確認 + let c: number = parseInt(string[i]); + if (c < 0) { + minus = true; + i++; } - /** - * position位置の文字から数字を解析する。 - * - * @param string 文字列 - * @param length 文字列の長さ - * @param position 解析したい文字の位置 - * @param outEndPos 一文字も読み込まなかった場合はエラー値(-1)が入る - * @return 解析結果の数値 - */ - public static stringToFloat( - string: string, - length: number, - position: number, - outEndPos: number[] - ): number { - let i: number = position; - let minus = false; // マイナスフラグ - let period = false; - let v1 = 0; - - //負号の確認 - let c: number = parseInt(string[i]); - if (c < 0) { - minus = true; + //整数部の確認 + for (; i < length; i++) { + const c = string[i]; + if (0 <= parseInt(c) && parseInt(c) <= 9) { + v1 = v1 * 10 + (parseInt(c) - 0); + } else if (c == '.') { + period = true; i++; + break; + } else { + break; } + } - //整数部の確認 + //小数部の確認 + if (period) { + let mul = 0.1; for (; i < length; i++) { - const c = string[i]; - if (0 <= parseInt(c) && parseInt(c) <= 9) { - v1 = v1 * 10 + (parseInt(c) - 0); - } else if (c == '.') { - period = true; - i++; - break; + c = parseFloat(string[i]) & 0xff; + if (0 <= c && c <= 9) { + v1 += mul * (c - 0); } else { break; } + mul *= 0.1; //一桁下げる + if (!c) break; } - - //小数部の確認 - if (period) { - let mul = 0.1; - for (; i < length; i++) { - c = parseFloat(string[i]) & 0xff; - if (0 <= c && c <= 9) { - v1 += mul * (c - 0); - } else { - break; - } - mul *= 0.1; //一桁下げる - if (!c) break; - } - } - - if (i == position) { - //一文字も読み込まなかった場合 - outEndPos[0] = -1; //エラー値が入るので呼び出し元で適切な処理を行う - return 0; - } - - if (minus) v1 = -v1; - - outEndPos[0] = i; - return v1; } - /** - * コンストラクタ呼び出し不可な静的クラスにする。 - */ - private constructor() {} + if (i == position) { + //一文字も読み込まなかった場合 + outEndPos[0] = -1; //エラー値が入るので呼び出し元で適切な処理を行う + return 0; + } + + if (minus) v1 = -v1; + + outEndPos[0] = i; + return v1; } + + /** + * コンストラクタ呼び出し不可な静的クラスにする。 + */ + private constructor() {} +} + +// Namespace definition for compatibility. +import * as $ from './cubismstring'; +// eslint-disable-next-line @typescript-eslint/no-namespace +export namespace Live2DCubismFramework { + export const CubismString = $.CubismString; + export type CubismString = $.CubismString; }