Compare commits

..

10 Commits

Author SHA1 Message Date
wada ff606d5bee Update to Cubism 5 SDK for Web R1 beta2 2023-09-28 11:15:33 +09:00
wada ae7a189f98 Update to Cubism 5 SDK for Web R1 beta1 2023-08-17 11:05:59 +09:00
wada 6971f204ce Update to Cubism 4 SDK for Web R7 2023-05-25 11:33:53 +09:00
wada a37b93d49a Update to Cubism 4 SDK for Web R6_2 2023-03-16 21:49:14 +09:00
wada 2289dd088b Update to Cubism 4 SDK for Web R6_1 2023-03-10 21:37:45 +09:00
wada 00275677be Update to Cubism 4 SDK for Web R6 2023-02-21 11:08:57 +09:00
wada e3df50532b Update to Cubism 4 SDK for Web R5 2022-09-08 11:20:20 +09:00
wada e115df6e1e Update to Cubism 4 SDK for Web R5 beta5 2022-08-04 11:17:45 +09:00
wada 4e14ef622d Update to Cubism 4 SDK for Web R5 beta4 2022-07-07 11:30:03 +09:00
wada 55b7a9233e Update to Cubism 4 SDK for Web R5 beta3 2022-06-16 11:48:09 +09:00
38 changed files with 7175 additions and 2826 deletions

View File

@ -15,6 +15,7 @@ rules:
prettier/prettier:
- error
- singleQuote: true
trailingComma: none
camelcase: "off"
'@typescript-eslint/naming-convention':
- warn
@ -58,6 +59,8 @@ rules:
'@typescript-eslint/no-unsafe-member-access': off
'@typescript-eslint/no-unsafe-argument': off
'@typescript-eslint/no-unsafe-call': off
'@typescript-eslint/no-explicit-any': off
'@typescript-eslint/no-unused-vars': off
'no-inner-declarations': off
'no-global-assign': off
'prefer-const': warn

View File

@ -5,6 +5,132 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
## [5-r.1-beta.2] - 2023-09-28
### Added
* Add a comment for clarity for the function whose usage is not intuitive.
## [5-r.1-beta.1] - 2023-08-17
### Added
* Add the function to get the ID of a given parameter.(`CubismModel.getParameterId`)
* Add the `CubismExpressionMotionManager` class.
### Changed
* Change the visibility of the `CubismId` constructor to private.
* Please use `CubismFramework.getIdManager().getId()` to get `CubismId`.
* Change the word `DrawMesh` to `DrawMeshWebGL`.
### Fixed
* Fix a bug that the value applied by multiply was not appropriate during expression transitions.
* Fix the structure of the class in renderer.
* Fix a issue where `ARRAY_BUFFER` was used on multiple targets.
* Separate shader class from `cubismrenderer` class.
* Separate the high precision mask process from the clipping mask setup process.
### Removed
* Remove several arguments of `DrawMesh` function.
## [4-r.7] - 2023-05-25
### Added
* Add compiler options `noImplicitAny` and `useUnknownInCatchVariables` to `tsconfig.json`.
* Add some function for checking consistency of MOC3.
* Add the function of checking consistency on reviving a MOC3. (`CubismMoc::Create`)
* Add a function to parse the opacity from `.motion3.json`.
* Add some functions to change Multiply and Screen colors on a per part basis.
### Changed
* Change access specifier for `CubismExpressionMotion`.
### Fixed
* Fix to support added compiler options `noImplicitAny` and `useUnknownInCatchVariables`.
## [4-r.6.2] - 2023-03-16
### Fixed
* Fix some problems related to Cubism Core.
* See `CHANGELOG.md` in Core.
## [4-r.6.1] - 2023-03-10
### Added
* Add function to validate MOC3 files.
## [4-r.6] - 2023-02-21
### Added
* Add support for high-precision masks.
* The number of render textures used can now be increased arbitrarily.
* The maximum number of masks when using multiple render textures has been increased to "number of render textures * 32".
* Add API to allow users to configure culling.
### Changed
* Change to not reference `CubismClippingManager_WebGL` on models that do not use clipping masks.
### Fixed
* Fix a crash when a `WebGLRenderingContext` is not registered with `Cubism Renderer_WebGL`.
* It now displays a warning and does not draw models.
* Fix a bug when displaying a model with culling set, some of the other drawn images are missing.
* Fix a bug that caused update information for some models not to be updated when multiple models are displayed.
* Call the function to extend the initial memory with CubismFramework.initialize(). See `CHANGELOG.md` in Core.
## [4-r.5] - 2022-09-08
### Added
* Add the multilingual supported documents.
* Add immediate stabilization of physics.
* Implemented a process to switch between `CubismJson` parsing and `JSON.parse()`.
## [4-r.5-beta.5] - 2022-08-04
### Fixed
* Fix `csmGetMocVersion` function argument.
* Fix a bug in which processing was interrupted when an invalid vertex was specified in the middle of a physics operation.
* Fix crash with exception when reading .moc3 files of unsupported versions.
* Fix physics system input to be split by the physics setting time.
## [4-r.5-beta.4] - 2022-07-07
### Added
* Add a function to get the latest .moc3 Version and the .moc3 Version of the loaded model.
* Add a function to get the type of parameters of the model.
* Add a function to get the parent part of the model's Drawable.
## [4-r.5-beta.3] - 2022-06-16
### Fixed
* `getDrawableTextureIndices` function in `CubismModel` has been renamed to `getDrawableTextureIndex` because the name was not correct.
* `getDrawableTextureIndices` function is marked as deprecated.
* Fix physics system behaviour when exists Physics Fps Setting in .physics3.json.
## [4-r.5-beta.2] - 2022-06-02
### Fixed
@ -89,6 +215,16 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
* Reformat code using Prettier and ESLint.
[5-r.1-beta.2]: https://github.com/Live2D/CubismWebFramework/compare/5-r.1-beta.1...5-r.1-beta.2
[5-r.1-beta.1]: https://github.com/Live2D/CubismWebFramework/compare/4-r.7...5-r.1-beta.1
[4-r.7]: https://github.com/Live2D/CubismWebFramework/compare/4-r.6.2...4-r.7
[4-r.6.2]: https://github.com/Live2D/CubismWebFramework/compare/4-r.6.1...4-r.6.2
[4-r.6.1]: https://github.com/Live2D/CubismWebFramework/compare/4-r.6...4-r.6.1
[4-r.6]: https://github.com/Live2D/CubismWebFramework/compare/4-r.5...4-r.6
[4-r.5]: https://github.com/Live2D/CubismWebFramework/compare/4-r.5-beta.5...4-r.5
[4-r.5-beta.5]: https://github.com/Live2D/CubismWebFramework/compare/4-r.5-beta.4...4-r.5-beta.5
[4-r.5-beta.4]: https://github.com/Live2D/CubismWebFramework/compare/4-r.5-beta.3...4-r.5-beta.4
[4-r.5-beta.3]: https://github.com/Live2D/CubismWebFramework/compare/4-r.5-beta.2...4-r.5-beta.3
[4-r.5-beta.2]: https://github.com/Live2D/CubismWebFramework/compare/4-r.5-beta.1...4-r.5-beta.2
[4-r.5-beta.1]: https://github.com/Live2D/CubismWebFramework/compare/4-r.4...4-r.5-beta.1
[4-r.4]: https://github.com/Live2D/CubismWebFramework/compare/4-r.3...4-r.4

172
README.ja.md Normal file
View File

@ -0,0 +1,172 @@
[English](README.md) / [日本語](README.ja.md)
---
# Cubism Web Framework
Live2D Cubism Editor で出力したモデルをアプリケーションで利用するためのフレームワークです。
モデルを表示、操作するための各種機能を提供します。
モデルをロードするには Live2D Cubism Core ライブラリと組み合わせて使用します。
ビルドを行うことで、ブラウザで利用可能な JavaScript ライブラリとして利用することができます。
## ライセンス
本 SDK を使用する前に、[ライセンス](LICENSE.md)をご確認ください。
## Cubism 5新機能や過去バージョンとの互換性について
本 SDK はCubism 5に対応した製品です。
Cubism 5 Editorに搭載された新機能のSDK対応については [こちら](https://docs.live2d.com/cubism-sdk-manual/cubism-5-new-functions/)をご確認ください。
過去バージョンのCubism SDKとの互換性については [こちら](https://docs.live2d.com/cubism-sdk-manual/compatibility-with-cubism-5/)をご確認ください。
## 開発環境
### Node.js
* 20.7.0
* 18.18.0
### TypeScript
5.2.2
## 開発環境構築
1. [Node.js] と [Visual Studio Code] をインストールします
1. Visual Studio Code で本プロジェクトを開き、推奨拡張機能をインストールします
* 拡張機能タブから `@recommended` と入力することで確認できます
1. コマンドパレット(*View > Command Palette...*)で `>Tasks: Run Task` を入力してタスク一覧を表示します
1. `npm: install` を選択して依存パッケージのダウンロードを行います
コマンドパレットのタスク一覧から各種コマンドを実行することができます。
NOTE: デバック用の設定は、`.vscode/tasks.json` に記述しています。
## タスク一覧
### `npm: build`
ソースファイルのビルドを行い、`dist` ディレクトリに出力します。
`tsconfig.json` を編集することで設定内容を変更できます。
### `npm: test`
TypeScript の型チェックテストを行います。
`tsconfig.json` を編集することで設定内容を変更できます。
### `npm: lint`
`src` ディレクトリ内の TypeScript ファイルの静的解析を行います。
`.eslintrc.yml` を編集することで設定内容を変更できます。
### `npm: lint:fix`
`src` ディレクトリ内の TypeScript ファイルの静的解析及び自動修正を行います。
`.eslintrc.yml` を編集することで設定内容を変更できます。
### `npm: clean`
ビルド成果物ディレクトリ(`dist`)を削除します。
## コンポーネント
### effect
自動まばたきやリップシンクなど、モデルに対してモーション情報をエフェクト的に付加する機能を提供します。
### id
モデルに設定されたパラメータ名・パーツ名・Drawable名を独自の型で管理する機能を提供します。
### math
行列計算やベクトル計算など、モデルの操作や描画に必要な算術演算の機能を提供します。
### model
モデルを取り扱うための各種機能(生成、更新、破棄)を提供します。
### motion
モデルにモーションデータを適用するための各種機能(モーション再生、パラメータブレンド)を提供します。
### physics
モデルに物理演算による変形操作を適用するための機能を提供します。
### rendering
モデルを描画するためのグラフィックス命令を実装したレンダラを提供します。
### type
フレームワーク内で使用する型定義を提供します。
### utils
JSONパーサーやログ出力などのユーティリティ機能を提供します。
## Live2D Cubism Core for Web
当リポジトリには Cubism Core for Web は同梱されていません。
[Cubism SDK for Web] からダウンロードしてください。
[Cubism SDK for Web]: https://www.live2d.com/download/cubism-sdk/download-web/
## サンプル
標準的なアプリケーションの実装例は [CubismWebSamples] を参照ください。
[CubismWebSamples]: https://github.com/Live2D/CubismWebSamples
## マニュアル
[Cubism SDK Manual](https://docs.live2d.com/cubism-sdk-manual/top/)
## 変更履歴
当リポジトリの変更履歴については [CHANGELOG.md](CHANGELOG.md) を参照ください。
## プロジェクトへの貢献
プロジェクトに貢献する方法はたくさんあります。バグのログの記録、このGitHubでのプルリクエストの送信、Live2Dコミュニティでの問題の報告と提案の作成です。
### フォークとプルリクエスト
修正、改善、さらには新機能をもたらすかどうかにかかわらず、プルリクエストに感謝します。ただし、ラッパーは可能な限り軽量で浅くなるように設計されているため、バグ修正とメモリ/パフォーマンスの改善のみを行う必要があることに注意してください。メインリポジトリを可能な限りクリーンに保つために、必要に応じて個人用フォークと機能ブランチを作成してください。
### バグ
Live2Dコミュニティでは、問題のレポートと機能リクエストを定期的にチェックしています。バグレポートを提出する前に、Live2Dコミュニティで検索して、問題のレポートまたは機能リクエストがすでに投稿されているかどうかを確認してください。問題がすでに存在する場合は、関連するコメントを追記してください。
### 提案
SDKの将来についてのフィードバックにも関心があります。Live2Dコミュニティで提案や機能のリクエストを送信できます。このプロセスをより効果的にするために、それらをより明確に定義するのに役立つより多くの情報を含めるようお願いしています。
## フォーラム
ユーザー同士でCubism SDKの活用方法の提案や質問をしたい場合は、是非フォーラムをご活用ください。
- [Live2D 公式クリエイターズフォーラム](https://creatorsforum.live2d.com/)
- [Live2D Creator's Forum(English)](https://community.live2d.com/)

131
README.md
View File

@ -1,144 +1,171 @@
[English](README.md) / [日本語](README.ja.md)
---
# Cubism Web Framework
Live2D Cubism 4 Editor で出力したモデルをアプリケーションで利用するためのフレームワークです。
This is a framework for using models output by Live2D Cubism Editor in applications.
モデルを表示、操作するための各種機能を提供します。
モデルをロードするには Live2D Cubism Core ライブラリと組み合わせて使用します。
It provides various functions for displaying and manipulating the model.
It is used in conjunction with the Live2D Cubism Core library to load the model.
ビルドを行うことで、ブラウザで利用可能な JavaScript ライブラリとして利用することができます。
You can use it as a JavaScript library that can be used in the browser by building.
## ライセンス
## License
本 SDK を使用する前に、[ライセンス](LICENSE.md)をご確認ください。
Please check the [license](LICENSE.md) before using this SDK.
## 開発環境
## Compatibility with Cubism 5 new features and previous Cubism SDK versions
This SDK is compatible with Cubism 5.
For SDK compatibility with new features in Cubism 5 Editor, please refer to [here](https://docs.live2d.com/en/cubism-sdk-manual/cubism-5-new-functions/).
For compatibility with previous versions of Cubism SDK, please refer to [here](https://docs.live2d.com/en/cubism-sdk-manual/compatibility-with-cubism-5/).
## Development environment
### Node.js
* 18.1.0
* 17.9.0
* 16.15.0
* 14.19.2
* 20.7.0
* 18.18.0
### TypeScript
4.6.4
5.2.2
## 開発環境構築
## Development environment construction
1. [Node.js] と [Visual Studio Code] をインストールします
1. Visual Studio Code で本プロジェクトを開き、推奨拡張機能をインストールします
* 拡張機能タブから `@recommended` と入力することで確認できます
1. コマンドパレット(*View > Command Palette...*)で `>Tasks: Run Task` を入力してタスク一覧を表示します
1. `npm: install` を選択して依存パッケージのダウンロードを行います
1. Install [Node.js] and [Visual Studio Code]
1. Open this project in Visual Studio Code and install the recommended extensions
* You can check them by typing `@recommended` from the Extensions tab
1. Enter `>Tasks: Run Task` in the command palette (*View > Command Palette...*) to display the task list
1. Select `npm: install` to download the dependent packages
コマンドパレットのタスク一覧から各種コマンドを実行することができます。
You can execute various commands from the task list on the command palette.
NOTE: デバック用の設定は、`.vscode/tasks.json` に記述しています。
NOTE: Settings for debugging are described in `.vscode/tasks.json`.
## タスク一覧
## Task list
### `npm: build`
ソースファイルのビルドを行い、`dist` ディレクトリに出力します。
Builds the source file and outputs to the `dist` directory.
`tsconfig.json` を編集することで設定内容を変更できます。
You can change the settings by editing `tsconfig.json`.
### `npm: test`
TypeScript の型チェックテストを行います。
Performs a TypeScript type check test.
`tsconfig.json` を編集することで設定内容を変更できます。
You can change the settings by editing `tsconfig.json`.
### `npm: lint`
`src` ディレクトリ内の TypeScript ファイルの静的解析を行います。
Performs static analysis of TypeScript files in the `src` directory.
`.eslintrc.yml` を編集することで設定内容を変更できます。
You can change the settings by editing `.eslintrc.yml`.
### `npm: lint:fix`
`src` ディレクトリ内の TypeScript ファイルの静的解析及び自動修正を行います。
Performs static analysis and automatic modification of TypeScript files in the `src` directory.
`.eslintrc.yml` を編集することで設定内容を変更できます。
You can change the settings by editing `.eslintrc.yml`.
### `npm: clean`
ビルド成果物ディレクトリ(`dist`)を削除します。
Deletes the build deliverable directory (`dist`).
## コンポーネント
## Components
### effect
自動まばたきやリップシンクなど、モデルに対してモーション情報をエフェクト的に付加する機能を提供します。
Provides functions such as automatic blinking and lip sync to add motion information as an effect to the model.
### id
モデルに設定されたパラメータ名・パーツ名・Drawable名を独自の型で管理する機能を提供します。
Provides functions to manage the parameter name, part name, and Drawable name set in the model with unique types.
### math
行列計算やベクトル計算など、モデルの操作や描画に必要な算術演算の機能を提供します。
Provides arithmetic functions required for manipulating and drawing the model, such as matrix and vector calculations.
### model
モデルを取り扱うための各種機能(生成、更新、破棄)を提供します。
Provides various functions (generate, update, destroy) for handling the model.
### motion
モデルにモーションデータを適用するための各種機能(モーション再生、パラメータブレンド)を提供します。
Provides various functions (motion playback, parameter blending) for applying motion data to the model.
### physics
モデルに物理演算による変形操作を適用するための機能を提供します。
Provides functions for applying transformation manipulations due to physics to the model.
### rendering
モデルを描画するためのグラフィックス命令を実装したレンダラを提供します。
Provides a renderer that implements graphics instructions for drawing the model.
### type
フレームワーク内で使用する型定義を提供します。
Provides type definitions for use within the framework.
### utils
JSONパーサーやログ出力などのユーティリティ機能を提供します。
Provides utility functions such as JSON parser and log output.
## Live2D Cubism Core for Web
当リポジトリには Cubism Core for Web は同梱されていません。
Cubism Core for Web is not included in this repository.
[Cubism SDK for Web] からダウンロードしてください。
Please download it from the [Cubism SDK for Web].
[Cubism SDK for Web]: https://www.live2d.com/download/cubism-sdk/download-web/
## サンプル
## Samples
標準的なアプリケーションの実装例は [CubismWebSamples] を参照ください。
Please refer to [CubismWebSamples] for implementation examples of standard applications.
[CubismWebSamples]: https://github.com/Live2D/CubismWebSamples
## マニュアル
## Manual
[Cubism SDK Manual](https://docs.live2d.com/cubism-sdk-manual/top/)
## 変更履歴
## Changelog
当リポジトリの変更履歴については [CHANGELOG.md](CHANGELOG.md) を参照ください。
Please refer to [CHANGELOG.md](CHANGELOG.md) for the changelog of this repository.
## コミュニティ
## Contributing
ユーザー同士でCubism SDKの活用方法の提案や質問をしたい場合は、是非コミュニティをご活用ください。
There are many ways to contribute to the project: logging bugs, submitting pull requests on this GitHub, and reporting issues and making suggestions in Live2D Community.
- [Live2D 公式コミュニティ](https://creatorsforum.live2d.com/)
- [Live2D community(English)](https://community.live2d.com/)
### Forking And Pull Requests
We very much appreciate your pull requests, whether they bring fixes, improvements, or even new features. Note, however, that the wrapper is designed to be as lightweight and shallow as possible and should therefore only be subject to bug fixes and memory/performance improvements. To keep the main repository as clean as possible, create a personal fork and feature branches there as needed.
### Bugs
We are regularly checking issue-reports and feature requests at Live2D Community. Before filing a bug report, please do a search in Live2D Community to see if the issue-report or feature request has already been posted. If you find your issue already exists, make relevant comments and add your reaction.
### Suggestions
We're also interested in your feedback for the future of the SDK. You can submit a suggestion or feature request at Live2D Community. To make this process more effective, we're asking that you include more information to help define them more clearly.
## Forum
If you want to suggest or ask questions about how to use the Cubism SDK between users, please use the forum.
- [Live2D Creator's Forum](https://community.live2d.com/)
- [Live2D 公式クリエイターズフォーラム (Japanese)](https://creatorsforum.live2d.com/)

2681
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -8,13 +8,13 @@
"clean": "rimraf dist"
},
"devDependencies": {
"@typescript-eslint/eslint-plugin": "^5.23.0",
"@typescript-eslint/parser": "^5.23.0",
"eslint": "^8.15.0",
"eslint-config-prettier": "^8.5.0",
"eslint-plugin-prettier": "^4.0.0",
"prettier": "^2.6.2",
"rimraf": "^3.0.2",
"typescript": "^4.6.4"
"@typescript-eslint/eslint-plugin": "^6.7.2",
"@typescript-eslint/parser": "^6.7.2",
"eslint": "^8.49.0",
"eslint-config-prettier": "^9.0.0",
"eslint-plugin-prettier": "^5.0.0",
"prettier": "^3.0.3",
"rimraf": "^5.0.1",
"typescript": "^5.2.2"
}
}

View File

@ -60,7 +60,7 @@ export const CubismDefaultParameterId = Object.freeze<Record<string, string>>({
ParamBustY: 'ParamBustY',
ParamBaseX: 'ParamBaseX',
ParamBaseY: 'ParamBaseY',
ParamNONE: 'NONE:',
ParamNONE: 'NONE:'
});
// Namespace definition for compatibility.

View File

@ -73,7 +73,7 @@ enum FrequestNode {
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)
FrequestNode_HitAreas // getRoot().getValueByString(HitAreas)
}
/**

View File

@ -219,7 +219,7 @@ export enum EyeState {
EyeState_Interval, // まばたきしていない状態
EyeState_Closing, // まぶたが閉じていく途中の状態
EyeState_Closed, // まぶたが閉じている状態
EyeState_Opening, // まぶたが開いていく途中の状態
EyeState_Opening // まぶたが開いていく途中の状態
}
// Namespace definition for compatibility.

View File

@ -11,8 +11,23 @@ import { csmString } from '../type/csmstring';
* Drawable
*
* Drawable
*
* @note IDCubismId
* CubismIdManager().getId(id)使
*/
export class CubismId {
/**
* 使CubismId
*
* @param id ID
* @returns CubismId
* @note IDCubismId
* CubismIdManager().getId(id)使
*/
public static _createIdInternal(id: string | csmString) {
return new CubismId(id);
}
/**
* ID
*/
@ -20,18 +35,6 @@ export class CubismId {
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
@ -64,6 +67,20 @@ export class CubismId {
return false;
}
/**
*
*
* @note
*/
private constructor(id: string | csmString) {
if (typeof id === 'string') {
this._id = new csmString(id);
return;
}
this._id = id;
}
private _id: csmString; // ID名
}

View File

@ -57,7 +57,7 @@ export class CubismIdManager {
return result;
}
result = new CubismId(id);
result = CubismId._createIdInternal(id);
this._ids.pushBack(result);
} else {
return this.registerId(id.s);

View File

@ -10,7 +10,7 @@ import { CubismRenderer } from './rendering/cubismrenderer';
import {
CSM_ASSERT,
CubismLogInfo,
CubismLogWarning,
CubismLogWarning
} from './utils/cubismdebug';
import { Value } from './utils/cubismjson';
@ -56,7 +56,7 @@ let s_cubismIdManager: CubismIdManager = null;
*/
export const Constant = Object.freeze<Record<string, number>>({
vertexOffset: 0, // メッシュ頂点のオフセット値
vertexStep: 2, // メッシュ頂点のステップ値
vertexStep: 2 // メッシュ頂点のステップ値
});
export function csmDelete<T>(address: T): void {
@ -131,8 +131,13 @@ export class CubismFramework {
/**
* Cubism Framework<br>
* Initialize()Dispose()
*
* @param memorySize [byte(s)]
* 使
* 1024*1024*16 byte(16MB)
* 1024*1024*16 byte
*/
public static initialize(): void {
public static initialize(memorySize = 0): void {
CSM_ASSERT(s_isStarted);
if (!s_isStarted) {
CubismLogWarning('CubismFramework is not started.');
@ -154,6 +159,12 @@ export class CubismFramework {
s_cubismIdManager = new CubismIdManager();
// --- HACK: 初期化時メモリ量の拡張(単位byte) ---
// 複数モデル表示時などにモデルが更新されない際に使用してください。
// 指定する際は必ず1024*1024*16 byte(16MB)以上の値を指定してください。
// それ以外はすべて1024*1024*16 byteに丸めます。
Live2DCubismCore.Memory.initializeAmountOfMemory(memorySize);
s_isInitialized = true;
CubismLogInfo('CubismFramework.initialize() is complete.');
@ -263,7 +274,7 @@ export enum LogLevel {
LogLevel_Info, // Infoログ
LogLevel_Warning, // 警告ログ
LogLevel_Error, // エラーログ
LogLevel_Off, // ログ出力無効
LogLevel_Off // ログ出力無効
}
// Namespace definition for compatibility.

View File

@ -33,7 +33,7 @@ export class CubismMatrix44 {
): 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,
0.0
]);
const n = 4;
@ -57,7 +57,7 @@ export class CubismMatrix44 {
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,
1.0
]);
this.setMatrix(c);
@ -175,7 +175,7 @@ export class CubismMatrix44 {
x,
y,
0.0,
1.0,
1.0
]);
CubismMatrix44.multiply(tr1, this._tr, this._tr);
@ -235,7 +235,7 @@ export class CubismMatrix44 {
0.0,
0.0,
0.0,
1.0,
1.0
]);
CubismMatrix44.multiply(tr1, this._tr, this._tr);
@ -253,8 +253,10 @@ export class CubismMatrix44 {
}
/**
*
*
* () * ()
*
* @note
* @param m
*/
public multiplyByMatrix(m: CubismMatrix44): void {

View File

@ -14,7 +14,10 @@ export class CubismVector2 {
/**
*
*/
public constructor(public x?: number, public y?: number) {
public constructor(
public x?: number,
public y?: number
) {
this.x = x == undefined ? 0.0 : x;
this.y = y == undefined ? 0.0 : y;

View File

@ -72,7 +72,7 @@ export class CubismViewMatrix extends CubismMatrix44 {
x,
y,
0.0,
1.0,
1.0
]);
CubismMatrix44.multiply(tr1, this._tr, this._tr);
@ -117,7 +117,7 @@ export class CubismViewMatrix extends CubismMatrix44 {
cx,
cy,
0.0,
1.0,
1.0
]);
const tr2: Float32Array = new Float32Array([
@ -136,7 +136,7 @@ export class CubismViewMatrix extends CubismMatrix44 {
0.0,
0.0,
0.0,
1.0,
1.0
]);
const tr3: Float32Array = new Float32Array([
@ -155,7 +155,7 @@ export class CubismViewMatrix extends CubismMatrix44 {
-cx,
-cy,
0.0,
1.0,
1.0
]);
CubismMatrix44.multiply(tr3, this._tr, this._tr);

View File

@ -5,7 +5,7 @@
* that can be found at https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
*/
import { CSM_ASSERT } from '../utils/cubismdebug';
import { CSM_ASSERT, CubismLogError } from '../utils/cubismdebug';
import { CubismModel } from './cubismmodel';
/**
@ -17,13 +17,32 @@ export class CubismMoc {
/**
* Moc
*/
public static create(mocBytes: ArrayBuffer): CubismMoc {
public static create(
mocBytes: ArrayBuffer,
shouldCheckMocConsistency: boolean
): CubismMoc {
let cubismMoc: CubismMoc = null;
if (shouldCheckMocConsistency) {
// .moc3の整合性を確認
const consistency = this.hasMocConsistency(mocBytes);
if (!consistency) {
// 整合性が確認できなければ処理しない
CubismLogError(`Inconsistent MOC3.`);
return cubismMoc;
}
}
const moc: Live2DCubismCore.Moc =
Live2DCubismCore.Moc.fromArrayBuffer(mocBytes);
if (moc) {
cubismMoc = new CubismMoc(moc);
cubismMoc._mocVersion = Live2DCubismCore.Version.csmGetMocVersion(
moc,
mocBytes
);
}
return cubismMoc;
@ -79,6 +98,7 @@ export class CubismMoc {
private constructor(moc: Live2DCubismCore.Moc) {
this._moc = moc;
this._modelCount = 0;
this._mocVersion = 0;
}
/**
@ -91,8 +111,32 @@ export class CubismMoc {
this._moc = null;
}
/**
* .moc3 Version
*/
public getLatestMocVersion(): number {
return Live2DCubismCore.Version.csmGetLatestMocVersion();
}
/**
* .moc3 Version
*/
public getMocVersion(): number {
return this._mocVersion;
}
/**
* .moc3
*/
public static hasMocConsistency(mocBytes: ArrayBuffer): boolean {
const isConsistent =
Live2DCubismCore.Moc.prototype.hasMocConsistency(mocBytes);
return isConsistent === 1 ? true : false;
}
_moc: Live2DCubismCore.Moc; // Mocデータ
_modelCount: number; // Mocデータから作られたモデルの個数
_mocVersion: number; // 読み込んだモデルの.moc3 Version
}
// Namespace definition for compatibility.

View File

@ -9,7 +9,7 @@ import { CubismIdHandle } from '../id/cubismid';
import { CubismFramework } from '../live2dcubismframework';
import {
CubismBlendMode,
CubismTextureColor,
CubismTextureColor
} from '../rendering/cubismrenderer';
import { csmMap } from '../type/csmmap';
import { csmVector } from '../type/csmvector';
@ -20,8 +20,50 @@ import { CSM_ASSERT } from '../utils/cubismdebug';
*
*/
export class DrawableColorData {
isOverwritten = false;
Color: CubismTextureColor = new CubismTextureColor();
constructor(
isOverwritten = false,
color: CubismTextureColor = new CubismTextureColor()
) {
this.isOverwritten = isOverwritten;
this.Color = color;
}
public isOverwritten: boolean;
public Color: CubismTextureColor;
}
/**
* @brief RGBA
*/
export class PartColorData {
constructor(
isOverwritten = false,
color: CubismTextureColor = new CubismTextureColor()
) {
this.isOverwritten = isOverwritten;
this.Color = color;
}
public isOverwritten: boolean;
public Color: CubismTextureColor;
}
/**
*
*/
export class DrawableCullingData {
/**
*
*
* @param isOverwritten
* @param isCulling
*/
public constructor(isOverwritten = false, isCulling = false) {
this.isOverwritten = isOverwritten;
this.isCulling = isCulling;
}
public isOverwritten: boolean;
public isCulling: boolean;
}
/**
@ -40,6 +82,18 @@ export class CubismModel {
this._model.drawables.resetDynamicFlags();
}
/**
* PixelsPerUnit
* @returns PixelsPerUnit
*/
public getPixelsPerUnit(): number {
if (this._model == null) {
return 0.0;
}
return this._model.canvasinfo.PixelsPerUnit;
}
/**
*
*/
@ -183,6 +237,150 @@ export class CubismModel {
this._userScreenColors.at(index).Color.B = b;
this._userScreenColors.at(index).Color.A = a;
}
/**
* part
* @param partIndex part
* @returns part
*/
public getPartMultiplyColor(partIndex: number): CubismTextureColor {
return this._userPartMultiplyColors.at(partIndex).Color;
}
/**
* part
* @param partIndex part
* @returns part
*/
public getPartScreenColor(partIndex: number): CubismTextureColor {
return this._userPartScreenColors.at(partIndex).Color;
}
/**
* partOverwriteColor setter
* @param partIndex part
* @param r R
* @param g G
* @param b B
* @param a A
* @param partColors part
* @param drawableColors partDrawable
*/
public setPartColor(
partIndex: number,
r: number,
g: number,
b: number,
a: number,
partColors: csmVector<PartColorData>,
drawableColors: csmVector<DrawableColorData>
) {
partColors.at(partIndex).Color.R = r;
partColors.at(partIndex).Color.G = g;
partColors.at(partIndex).Color.B = b;
partColors.at(partIndex).Color.A = a;
if (partColors.at(partIndex).isOverwritten) {
for (
let i = 0;
i < this._partChildDrawables.at(partIndex).getSize();
++i
) {
const drawableIndex = this._partChildDrawables.at(partIndex).at(i);
drawableColors.at(drawableIndex).Color.R = r;
drawableColors.at(drawableIndex).Color.G = g;
drawableColors.at(drawableIndex).Color.B = b;
drawableColors.at(drawableIndex).Color.A = a;
}
}
}
/**
*
* @param partIndex part
* @param color (CubismTextureColor)
*/
public setPartMultiplyColorByTextureColor(
partIndex: number,
color: CubismTextureColor
) {
this.setPartMultiplyColorByRGBA(
partIndex,
color.R,
color.G,
color.B,
color.A
);
}
/**
*
* @param partIndex part
* @param r R
* @param g G
* @param b B
* @param a A
*/
public setPartMultiplyColorByRGBA(
partIndex: number,
r: number,
g: number,
b: number,
a: number
) {
this.setPartColor(
partIndex,
r,
g,
b,
a,
this._userPartMultiplyColors,
this._userMultiplyColors
);
}
/**
*
* @param partIndex part
* @param color (CubismTextureColor)
*/
public setPartScreenColorByTextureColor(
partIndex: number,
color: CubismTextureColor
) {
this.setPartScreenColorByRGBA(
partIndex,
color.R,
color.G,
color.B,
color.A
);
}
/**
*
* @param partIndex part
* @param r R
* @param g G
* @param b B
* @param a A
*/
public setPartScreenColorByRGBA(
partIndex: number,
r: number,
g: number,
b: number,
a: number
) {
this.setPartColor(
partIndex,
r,
g,
b,
a,
this._userPartScreenColors,
this._userScreenColors
);
}
/**
* SDK
@ -239,7 +437,7 @@ export class CubismModel {
public getOverwriteFlagForDrawableScreenColors(
drawableindex: number
): boolean {
return this._userMultiplyColors.at(drawableindex).isOverwritten;
return this._userScreenColors.at(drawableindex).isOverwritten;
}
/**
@ -266,6 +464,188 @@ export class CubismModel {
this._userScreenColors.at(drawableindex).isOverwritten = value;
}
/**
* SDKpart
* @param partIndex part
* @returns true -> SDK
* false -> 使
*/
public getOverwriteColorForPartMultiplyColors(partIndex: number) {
return this._userPartMultiplyColors.at(partIndex).isOverwritten;
}
/**
* SDKpart
* @param partIndex part
* @returns true -> SDK
* false -> 使
*/
public getOverwriteColorForPartScreenColors(partIndex: number) {
return this._userPartScreenColors.at(partIndex).isOverwritten;
}
/**
* partOverwriteFlag setter
* @param partIndex part
* @param value true -> SDK
* false -> 使
* @param partColors part
* @param drawableColors partDrawable
*/
public setOverwriteColorForPartColors(
partIndex: number,
value: boolean,
partColors: csmVector<PartColorData>,
drawableColors: csmVector<DrawableColorData>
) {
partColors.at(partIndex).isOverwritten = value;
for (let i = 0; i < this._partChildDrawables.at(partIndex).getSize(); ++i) {
const drawableIndex = this._partChildDrawables.at(partIndex).at(i);
drawableColors.at(drawableIndex).isOverwritten = value;
if (value) {
drawableColors.at(drawableIndex).Color.R =
partColors.at(partIndex).Color.R;
drawableColors.at(drawableIndex).Color.G =
partColors.at(partIndex).Color.G;
drawableColors.at(drawableIndex).Color.B =
partColors.at(partIndex).Color.B;
drawableColors.at(drawableIndex).Color.A =
partColors.at(partIndex).Color.A;
}
}
}
/**
* SDKpart
* @param partIndex part
* @param value true -> SDK
* false -> 使
*/
public setOverwriteColorForPartMultiplyColors(
partIndex: number,
value: boolean
) {
this._userPartMultiplyColors.at(partIndex).isOverwritten = value;
this.setOverwriteColorForPartColors(
partIndex,
value,
this._userPartMultiplyColors,
this._userMultiplyColors
);
}
/**
* SDKpart
* @param partIndex part
* @param value true -> SDK
* false -> 使
*/
public setOverwriteColorForPartScreenColors(
partIndex: number,
value: boolean
) {
this._userPartScreenColors.at(partIndex).isOverwritten = value;
this.setOverwriteColorForPartColors(
partIndex,
value,
this._userPartScreenColors,
this._userScreenColors
);
}
/**
* Drawable
*
* @param drawableIndex Drawable
* @return Drawable
*/
public getDrawableCulling(drawableIndex: number): boolean {
if (
this.getOverwriteFlagForModelCullings() ||
this.getOverwriteFlagForDrawableCullings(drawableIndex)
) {
return this._userCullings.at(drawableIndex).isCulling;
}
const constantFlags = this._model.drawables.constantFlags;
return !Live2DCubismCore.Utils.hasIsDoubleSidedBit(
constantFlags[drawableIndex]
);
}
/**
* Drawable
*
* @param drawableIndex Drawable
* @param isCulling
*/
public setDrawableCulling(drawableIndex: number, isCulling: boolean): void {
this._userCullings.at(drawableIndex).isCulling = isCulling;
}
/**
* SDK
*
* @retval true -> SDK使
* @retval false -> 使
*/
public getOverwriteFlagForModelCullings(): boolean {
return this._isOverwrittenCullings;
}
/**
* SDK
*
* @param isOverwrittenCullings SDK使true使false
*/
public setOverwriteFlagForModelCullings(
isOverwrittenCullings: boolean
): void {
this._isOverwrittenCullings = isOverwrittenCullings;
}
/**
*
* @param drawableIndex Drawable
* @retval true -> SDK使
* @retval false -> 使
*/
public getOverwriteFlagForDrawableCullings(drawableIndex: number): boolean {
return this._userCullings.at(drawableIndex).isOverwritten;
}
/**
*
* @param drawableIndex Drawable
* @param isOverwrittenCullings SDK使true使false
*/
public setOverwriteFlagForDrawableCullings(
drawableIndex: number,
isOverwrittenCullings: boolean
): void {
this._userCullings.at(drawableIndex).isOverwritten = isOverwrittenCullings;
}
/**
*
*
* @returns
*/
public getModelOapcity(): number {
return this._modelOpacity;
}
/**
*
*
* @param value
*/
public setModelOapcity(value: number) {
this._modelOpacity = value;
}
/**
*
*/
@ -301,6 +681,17 @@ export class CubismModel {
return partIndex;
}
/**
* ID
*
* @param partIndex
* @return ID
*/
public getPartId(partIndex: number): CubismIdHandle {
const partId = this._model.parts.ids[partIndex];
return CubismFramework.getIdManager().getId(partId);
}
/**
*
* @return
@ -416,6 +807,18 @@ export class CubismModel {
return this._model.parameters.count;
}
/**
*
* @param parameterIndex
* @return csmParameterType_Normal ->
* csmParameterType_BlendShape ->
*/
public getParameterType(
parameterIndex: number
): Live2DCubismCore.csmParameterType {
return this._model.parameters.types[parameterIndex];
}
/**
*
* @param parameterIndex
@ -443,6 +846,18 @@ export class CubismModel {
return this._model.parameters.defaultValues[parameterIndex];
}
/**
* indexID
*
* @param parameterIndex
* @returns ID
*/
public getParameterId(parameterIndex: number): CubismIdHandle {
return CubismFramework.getIdManager().getId(
this._model.parameters.ids[parameterIndex]
);
}
/**
*
* @param parameterIndex
@ -646,11 +1061,23 @@ export class CubismModel {
}
/**
* @deprecated
* getDrawableTextureIndex
*
* Drawable
* @param drawableIndex Drawable
* @return drawable
*/
public getDrawableTextureIndices(drawableIndex: number): number {
return this.getDrawableTextureIndex(drawableIndex);
}
/**
* Drawable
* @param drawableIndex Drawable
* @return drawable
*/
public getDrawableTextureIndex(drawableIndex: number): number {
const textureIndices: Int32Array = this._model.drawables.textureIndices;
return textureIndices[drawableIndex];
}
@ -704,7 +1131,7 @@ export class CubismModel {
/**
* Drawable
* @param drarableIndex Drawable
* @param drawableIndex Drawable
* @return drawable
*/
public getDrawableVertexIndices(drawableIndex: number): Uint16Array {
@ -777,16 +1204,12 @@ export class CubismModel {
}
/**
* Drawable
* Drawable
* @param drawableIndex Drawable
* @return drawable
* @return drawable
*/
public getDrawableCulling(drawableIndex: number): boolean {
const constantFlags = this._model.drawables.constantFlags;
return !Live2DCubismCore.Utils.hasIsDoubleSidedBit(
constantFlags[drawableIndex]
);
public getDrawableParentPartIndex(drawableIndex: number): number {
return this._model.drawables.parentPartIndices[drawableIndex];
}
/**
@ -982,9 +1405,9 @@ export class CubismModel {
}
}
const partCount: number = this._model.parts.count;
{
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) {
@ -992,31 +1415,98 @@ export class CubismModel {
CubismFramework.getIdManager().getId(partIds[i])
);
}
this._userPartMultiplyColors.prepareCapacity(partCount);
this._userPartScreenColors.prepareCapacity(partCount);
this._partChildDrawables.prepareCapacity(partCount);
}
{
const drawableIds: string[] = this._model.drawables.ids;
const drawableCount: number = this._model.drawables.count;
this._userMultiplyColors = new csmVector<DrawableColorData>();
this._userMultiplyColors.updateSize(
drawableCount,
DrawableColorData,
true
this._userMultiplyColors.prepareCapacity(drawableCount);
this._userScreenColors.prepareCapacity(drawableCount);
// カリング設定
this._userCullings.prepareCapacity(drawableCount);
const userCulling: DrawableCullingData = new DrawableCullingData(
false,
false
);
this._userScreenColors = new csmVector<DrawableColorData>();
this._userScreenColors.updateSize(drawableCount, DrawableColorData, true);
// Part
{
for (let i = 0; i < partCount; ++i) {
const multiplyColor: CubismTextureColor = new CubismTextureColor(
1.0,
1.0,
1.0,
1.0
);
const screenColor: CubismTextureColor = new CubismTextureColor(
0.0,
0.0,
0.0,
1.0
);
this._drawableIds.prepareCapacity(drawableCount);
for (let i = 0; i < drawableCount; ++i) {
this._drawableIds.pushBack(
CubismFramework.getIdManager().getId(drawableIds[i])
);
const userMultiplyColor: PartColorData = new PartColorData(
false,
multiplyColor
);
const userScreenColor: PartColorData = new PartColorData(
false,
screenColor
);
// shaderに影響しない色で初期化
this.setMultiplyColorByRGBA(i, 1.0, 1.0, 1.0, 1.0);
this.setScreenColorByRGBA(i, 0.0, 0.0, 0.0, 1.0);
this._userPartMultiplyColors.pushBack(userMultiplyColor);
this._userPartScreenColors.pushBack(userScreenColor);
this._partChildDrawables.pushBack(new csmVector<number>());
this._partChildDrawables.at(i).prepareCapacity(drawableCount);
}
}
// Drawables
{
for (let i = 0; i < drawableCount; ++i) {
const multiplyColor: CubismTextureColor = new CubismTextureColor(
1.0,
1.0,
1.0,
1.0
);
const screenColor: CubismTextureColor = new CubismTextureColor(
0.0,
0.0,
0.0,
1.0
);
const userMultiplyColor: DrawableColorData = new DrawableColorData(
false,
multiplyColor
);
const userScreenColor: DrawableColorData = new DrawableColorData(
false,
screenColor
);
this._drawableIds.pushBack(
CubismFramework.getIdManager().getId(drawableIds[i])
);
this._userMultiplyColors.pushBack(userMultiplyColor);
this._userScreenColors.pushBack(userScreenColor);
this._userCullings.pushBack(userCulling);
const parentIndex = this.getDrawableParentPartIndex(i);
if (parentIndex >= 0) {
this._partChildDrawables.at(parentIndex).pushBack(i);
}
}
}
}
}
@ -1037,8 +1527,15 @@ export class CubismModel {
this._partIds = new csmVector<CubismIdHandle>();
this._isOverwrittenModelMultiplyColors = false;
this._isOverwrittenModelScreenColors = false;
this._userMultiplyColors = null;
this._userScreenColors = null;
this._isOverwrittenCullings = false;
this._modelOpacity = 1.0;
this._userMultiplyColors = new csmVector<DrawableColorData>();
this._userScreenColors = new csmVector<DrawableColorData>();
this._userCullings = new csmVector<DrawableCullingData>();
this._userPartMultiplyColors = new csmVector<PartColorData>();
this._userPartScreenColors = new csmVector<PartColorData>();
this._partChildDrawables = new csmVector<csmVector<number>>();
this._notExistPartId = new csmMap<CubismIdHandle, number>();
this._notExistParameterId = new csmMap<CubismIdHandle, number>();
@ -1066,6 +1563,9 @@ export class CubismModel {
private _isOverwrittenModelScreenColors: boolean; // SDK上でモデル全体のスクリーン色を上書きするか判定するフラグ
private _userMultiplyColors: csmVector<DrawableColorData>; // Drawableごとに設定する乗算色と上書きフラグを管理するリスト
private _userScreenColors: csmVector<DrawableColorData>; // Drawableごとに設定するスクリーン色と上書きフラグを管理するリスト
private _userPartScreenColors: csmVector<PartColorData>; // Part 乗算色の配列
private _userPartMultiplyColors: csmVector<PartColorData>; // Part スクリーン色の配列
private _partChildDrawables: csmVector<csmVector<number>>; // Partの子DrawableIndexの配列
private _model: Live2DCubismCore.Model; // モデル
@ -1075,9 +1575,14 @@ export class CubismModel {
private _partOpacities: Float32Array; // パーツの不透明度のリスト
private _modelOpacity: number; // モデルの不透明度
private _parameterIds: csmVector<CubismIdHandle>;
private _partIds: csmVector<CubismIdHandle>;
private _drawableIds: csmVector<CubismIdHandle>;
private _isOverwrittenCullings: boolean; // モデルのカリング設定をすべて上書きするか?
private _userCullings: csmVector<DrawableCullingData>; // カリング設定の配列
}
// Namespace definition for compatibility.

View File

@ -14,6 +14,7 @@ import { CubismModelMatrix } from '../math/cubismmodelmatrix';
import { CubismTargetPoint } from '../math/cubismtargetpoint';
import { ACubismMotion, FinishedMotionCallback } from '../motion/acubismmotion';
import { CubismExpressionMotion } from '../motion/cubismexpressionmotion';
import { CubismExpressionMotionManager } from '../motion/cubismexpressionmotionmanager';
import { CubismMotion } from '../motion/cubismmotion';
import { CubismMotionManager } from '../motion/cubismmotionmanager';
import { CubismMotionQueueManager } from '../motion/cubismmotionqueuemanager';
@ -127,16 +128,22 @@ export class CubismUserModel {
*
* @param buffer moc3
*/
public loadModel(buffer: ArrayBuffer) {
this._moc = CubismMoc.create(buffer);
this._model = this._moc.createModel();
this._model.saveParameters();
public loadModel(buffer: ArrayBuffer, shouldCheckMocConsistency = false) {
this._moc = CubismMoc.create(buffer, shouldCheckMocConsistency);
if (this._moc == null || this._model == null) {
if (this._moc == null) {
CubismLogError('Failed to CubismMoc.create().');
return;
}
this._model = this._moc.createModel();
if (this._model == null) {
CubismLogError('Failed to CreateModel().');
return;
}
this._model.saveParameters();
this._modelMatrix = new CubismModelMatrix(
this._model.getCanvasWidth(),
this._model.getCanvasHeight()
@ -271,14 +278,15 @@ export class CubismUserModel {
/**
*
* @param maskBufferCount
*/
public createRenderer(): void {
public createRenderer(maskBufferCount = 1): void {
if (this._renderer) {
this.deleteRenderer();
}
this._renderer = new CubismRenderer_WebGL();
this._renderer.initialize(this._model);
this._renderer.initialize(this._model, maskBufferCount);
}
/**
@ -352,6 +360,7 @@ export class CubismUserModel {
this._accelerationX = 0.0;
this._accelerationY = 0.0;
this._accelerationZ = 0.0;
this._mocConsistency = false;
this._debugMode = false;
this._renderer = null;
@ -363,7 +372,7 @@ export class CubismUserModel {
);
// 表情マネージャーを作成
this._expressionManager = new CubismMotionManager();
this._expressionManager = new CubismExpressionMotionManager();
// ドラッグによるアニメーション
this._dragManager = new CubismTargetPoint();
@ -407,7 +416,7 @@ export class CubismUserModel {
protected _model: CubismModel; // Modelインスタンス
protected _motionManager: CubismMotionManager; // モーション管理
protected _expressionManager: CubismMotionManager; // 表情管理
protected _expressionManager: CubismExpressionMotionManager; // 表情管理
protected _eyeBlink: CubismEyeBlink; // 自動まばたき
protected _breath: CubismBreath; // 呼吸
protected _modelMatrix: CubismModelMatrix; // モデル行列
@ -426,6 +435,7 @@ export class CubismUserModel {
protected _accelerationX: number; // X軸方向の加速度
protected _accelerationY: number; // Y軸方向の加速度
protected _accelerationZ: number; // Z軸方向の加速度
protected _mocConsistency: boolean; // MOC3一貫性検証するかどうか
protected _debugMode: boolean; // デバッグモードかどうか
private _renderer: CubismRenderer_WebGL; // レンダラ

View File

@ -78,6 +78,38 @@ export abstract class ACubismMotion {
}
}
const fadeWeight = this.updateFadeWeight(motionQueueEntry, userTimeSeconds);
//---- 全てのパラメータIDをループする ----
this.doUpdateParameters(
model,
userTimeSeconds,
fadeWeight,
motionQueueEntry
);
// 後処理
// 終了時刻を過ぎたら終了フラグを立てる(CubismMotionQueueManager)
if (
motionQueueEntry.getEndTime() > 0 &&
motionQueueEntry.getEndTime() < userTimeSeconds
) {
motionQueueEntry.setIsFinished(true); // 終了
}
}
/**
* @brief
*
*
*
* @param[in] motionQueueEntry CubismMotionQueueManager
* @param[in] userTimeSeconds []
*/
public updateFadeWeight(
motionQueueEntry: CubismMotionQueueEntry,
userTimeSeconds: number
): number {
let fadeWeight: number = this._weight; // 現在の値と掛け合わせる割合
//---- フェードイン・アウトの処理 ----
@ -104,22 +136,7 @@ export abstract class ACubismMotion {
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); // 終了
}
return fadeWeight;
}
/**
@ -258,6 +275,46 @@ export abstract class ACubismMotion {
*/
public getFinishedMotionHandler = () => this._onFinishedMotion;
/**
*
*
* @returns true ->
* false ->
*/
public isExistModelOpacity(): boolean {
return false;
}
/**
*
*
* @returns success:
*/
public getModelOpacityIndex(): number {
return -1;
}
/**
* Id
*
* @param index
* @returns success:Id
*/
public getModelOpacityId(index: number): CubismIdHandle {
return null;
}
/**
*
*
* @returns success:Opacity
*
* @note UpdateParameters()
*/
protected getModelOpacityValue(): number {
return 1.0;
}
public _fadeInSeconds: number; // フェードインにかかる時間[秒]
public _fadeOutSeconds: number; // フェードアウトにかかる時間[秒]
public _weight: number; // モーションの重み
@ -271,6 +328,7 @@ export abstract class ACubismMotion {
// Namespace definition for compatibility.
import * as $ from './acubismmotion';
import { CubismIdHandle } from '../id/cubismid';
// eslint-disable-next-line @typescript-eslint/no-namespace
export namespace Live2DCubismFramework {
export const ACubismMotion = $.ACubismMotion;

View File

@ -31,6 +31,9 @@ const DefaultFadeTime = 1.0;
*
*/
export class CubismExpressionMotion extends ACubismMotion {
static readonly DefaultAdditiveValue = 0.0; // 加算適用の初期値
static readonly DefaultMultiplyValue = 1.0; // 乗算適用の初期値
/**
*
* @param buffer exp
@ -42,69 +45,7 @@ export class CubismExpressionMotion extends ACubismMotion {
size: number
): CubismExpressionMotion {
const expression: CubismExpressionMotion = new CubismExpressionMotion();
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)
); // フェードアウト
// 各パラメータについて
const parameterCount = root
.getValueByString(ExpressionKeyParameters)
.getSize();
expression._parameters.prepareCapacity(parameterCount);
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(); // 値
// 計算方法の設定
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);
}
CubismJson.delete(json); // JSONデータは不要になったら削除する
expression.parse(buffer, size);
return expression;
}
@ -125,7 +66,7 @@ export class CubismExpressionMotion extends ACubismMotion {
const parameter: ExpressionParameter = this._parameters.at(i);
switch (parameter.blendType) {
case ExpressionBlendType.ExpressionBlendType_Add: {
case ExpressionBlendType.Additive: {
model.addParameterValueById(
parameter.parameterId,
parameter.value,
@ -133,7 +74,7 @@ export class CubismExpressionMotion extends ACubismMotion {
);
break;
}
case ExpressionBlendType.ExpressionBlendType_Multiply: {
case ExpressionBlendType.Multiply: {
model.multiplyParameterValueById(
parameter.parameterId,
parameter.value,
@ -141,7 +82,7 @@ export class CubismExpressionMotion extends ACubismMotion {
);
break;
}
case ExpressionBlendType.ExpressionBlendType_Overwrite: {
case ExpressionBlendType.Overwrite: {
model.setParameterValueById(
parameter.parameterId,
parameter.value,
@ -157,24 +98,261 @@ export class CubismExpressionMotion extends ACubismMotion {
}
/**
*
* @brief
*
*
*
* @param[in] model
* @param[in] userTimeSeconds []
* @param[in] motionQueueEntry CubismMotionQueueManager
* @param[in] expressionParameterValues
* @param[in] expressionIndex
*/
constructor() {
super();
public calculateExpressionParameters(
model: CubismModel,
userTimeSeconds: number,
motionQueueEntry: CubismMotionQueueEntry,
expressionParameterValues: csmVector<ExpressionParameterValue>,
expressionIndex: number
) {
if (!motionQueueEntry.isAvailable()) {
return;
}
this._parameters = new csmVector<ExpressionParameter>();
if (!motionQueueEntry.isStarted()) {
motionQueueEntry.setIsStarted(true);
motionQueueEntry.setStartTime(userTimeSeconds - this._offsetSeconds); // モーションの開始時刻を記録
motionQueueEntry.setFadeInStartTime(userTimeSeconds); // フェードインの開始時刻
const duration = this.getDuration();
if (motionQueueEntry.getEndTime() < 0.0) {
// 開始していないうちに終了設定している場合がある
motionQueueEntry.setEndTime(
duration <= 0.0 ? -1 : motionQueueEntry.getStartTime() + duration
);
// duration == -1 の場合はループする
}
}
this._fadeWeight = this.updateFadeWeight(motionQueueEntry, userTimeSeconds);
// モデルに適用する値を計算
for (let i = 0; i < expressionParameterValues.getSize(); ++i) {
const expressionParameterValue = expressionParameterValues.at(i);
if (expressionParameterValue.parameterId == null) {
continue;
}
const currentParameterValue = (expressionParameterValue.overwriteValue =
model.getParameterValueById(expressionParameterValue.parameterId));
const expressionParameters = this.getExpressionParameters();
let parameterIndex = -1;
for (let j = 0; j < expressionParameters.getSize(); ++j) {
if (
expressionParameterValue.parameterId !=
expressionParameters.at(j).parameterId
) {
continue;
}
parameterIndex = j;
break;
}
// 再生中のExpressionが参照していないパラメータは初期値を適用
if (parameterIndex < 0) {
if (expressionIndex == 0) {
expressionParameterValue.additiveValue =
CubismExpressionMotion.DefaultAdditiveValue;
expressionParameterValue.multiplyValue =
CubismExpressionMotion.DefaultMultiplyValue;
expressionParameterValue.overwriteValue = currentParameterValue;
} else {
expressionParameterValue.additiveValue = this.calculateValue(
expressionParameterValue.additiveValue,
CubismExpressionMotion.DefaultAdditiveValue
);
expressionParameterValue.multiplyValue = this.calculateValue(
expressionParameterValue.multiplyValue,
CubismExpressionMotion.DefaultMultiplyValue
);
expressionParameterValue.overwriteValue = this.calculateValue(
expressionParameterValue.overwriteValue,
currentParameterValue
);
}
continue;
}
// 値を計算
const value = expressionParameters.at(parameterIndex).value;
let newAdditiveValue, newMultiplyValue, newOverwriteValue;
switch (expressionParameters.at(parameterIndex).blendType) {
case ExpressionBlendType.Additive:
newAdditiveValue = value;
newMultiplyValue = CubismExpressionMotion.DefaultMultiplyValue;
newOverwriteValue = currentParameterValue;
break;
case ExpressionBlendType.Multiply:
newAdditiveValue = CubismExpressionMotion.DefaultAdditiveValue;
newMultiplyValue = value;
newOverwriteValue = currentParameterValue;
break;
case ExpressionBlendType.Overwrite:
newAdditiveValue = CubismExpressionMotion.DefaultAdditiveValue;
newMultiplyValue = CubismExpressionMotion.DefaultMultiplyValue;
newOverwriteValue = value;
break;
default:
return;
}
if (expressionIndex == 0) {
expressionParameterValue.additiveValue = newAdditiveValue;
expressionParameterValue.multiplyValue = newMultiplyValue;
expressionParameterValue.overwriteValue = newOverwriteValue;
} else {
expressionParameterValue.additiveValue =
expressionParameterValue.additiveValue * (1.0 - this._fadeWeight) +
newAdditiveValue * this._fadeWeight;
expressionParameterValue.multiplyValue =
expressionParameterValue.multiplyValue * (1.0 - this._fadeWeight) +
newMultiplyValue * this._fadeWeight;
expressionParameterValue.overwriteValue =
expressionParameterValue.overwriteValue * (1.0 - this._fadeWeight) +
newOverwriteValue * this._fadeWeight;
}
}
}
_parameters: csmVector<ExpressionParameter>; // 表情のパラメータ情報リスト
/**
* @brief
*
*
*
* @return
*/
public getExpressionParameters() {
return this._parameters;
}
/**
* @brief
*
*
*
* @returns
*/
public getFadeWeight() {
return this._fadeWeight;
}
protected parse(buffer: ArrayBuffer, size: number) {
const json: CubismJson = CubismJson.create(buffer, size);
const root: Value = json.getRoot();
this.setFadeInTime(
root.getValueByString(ExpressionKeyFadeIn).toFloat(DefaultFadeTime)
); // フェードイン
this.setFadeOutTime(
root.getValueByString(ExpressionKeyFadeOut).toFloat(DefaultFadeTime)
); // フェードアウト
// 各パラメータについて
const parameterCount = root
.getValueByString(ExpressionKeyParameters)
.getSize();
this._parameters.prepareCapacity(parameterCount);
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(); // 値
// 計算方法の設定
let blendType: ExpressionBlendType;
if (
param.getValueByString(ExpressionKeyBlend).isNull() ||
param.getValueByString(ExpressionKeyBlend).getString() == BlendValueAdd
) {
blendType = ExpressionBlendType.Additive;
} else if (
param.getValueByString(ExpressionKeyBlend).getString() ==
BlendValueMultiply
) {
blendType = ExpressionBlendType.Multiply;
} else if (
param.getValueByString(ExpressionKeyBlend).getString() ==
BlendValueOverwrite
) {
blendType = ExpressionBlendType.Overwrite;
} else {
// その他 仕様にない値を設定した時は加算モードにすることで復旧
blendType = ExpressionBlendType.Additive;
}
// 設定オブジェクトを作成してリストに追加する
const item: ExpressionParameter = new ExpressionParameter();
item.parameterId = parameterId;
item.blendType = blendType;
item.value = value;
this._parameters.pushBack(item);
}
CubismJson.delete(json); // JSONデータは不要になったら削除する
}
/**
* @brief
*
*
*
* @param source
* @param destination
* @param weight
* @returns
*/
public calculateValue(source: number, destination: number): number {
return source * (1.0 - this._fadeWeight) + destination * this._fadeWeight;
}
/**
*
*/
protected constructor() {
super();
this._parameters = new csmVector<ExpressionParameter>();
this._fadeWeight = 0.0;
}
private _parameters: csmVector<ExpressionParameter>; // 表情のパラメータ情報リスト
private _fadeWeight: number; // 表情の現在のウェイト
}
/**
*
*/
export enum ExpressionBlendType {
ExpressionBlendType_Add = 0, // 加算
ExpressionBlendType_Multiply = 1, // 乗算
ExpressionBlendType_Overwrite = 2, // 上書き
Additive = 0, // 加算
Multiply = 1, // 乗算
Overwrite = 2 // 上書き
}
/**
@ -188,6 +366,8 @@ export class ExpressionParameter {
// Namespace definition for compatibility.
import * as $ from './cubismexpressionmotion';
import { ExpressionParameterValue } from './cubismexpressionmotionmanager';
import { CubismDefaultParameterId } from '../cubismdefaultparameterid';
// eslint-disable-next-line @typescript-eslint/no-namespace
export namespace Live2DCubismFramework {
export const CubismExpressionMotion = $.CubismExpressionMotion;

View File

@ -0,0 +1,267 @@
import { CubismId, CubismIdHandle } from '../id/cubismid';
import { csmDelete } from '../live2dcubismframework';
import { CubismModel } from '../model/cubismmodel';
import { csmVector, iterator } from '../type/csmvector';
import { ACubismMotion } from './acubismmotion';
import { CubismExpressionMotion } from './cubismexpressionmotion';
import { CubismMotionQueueEntry } from './cubismmotionqueueentry';
import {
CubismMotionQueueEntryHandle,
CubismMotionQueueManager
} from './cubismmotionqueuemanager';
/**
* @brief
*/
export class ExpressionParameterValue {
parameterId: CubismIdHandle; // パラメーターID
additiveValue: number; // 加算値
multiplyValue: number; // 乗算値
overwriteValue: number; // 上書き値
}
/**
* @brief
*
*
*/
export class CubismExpressionMotionManager extends CubismMotionQueueManager {
/**
*
*/
public constructor() {
super();
this._currentPriority = 0;
this._reservePriority = 0;
this._expressionParameterValues = new csmVector<ExpressionParameterValue>();
}
/**
*
*/
public release(): void {
if (this._expressionParameterValues) {
csmDelete(this._expressionParameterValues);
this._expressionParameterValues = null;
}
}
/**
* @brief
*
*
*
* @returns
*/
public getCurrentPriority(): number {
return this._currentPriority;
}
/**
* @brief
*
*
*
* @return
*/
public getReservePriority(): number {
return this._reservePriority;
}
/**
* @brief
*
*
*
* @param[in] priority
*/
public setReservePriority(priority: number) {
this._reservePriority = priority;
}
/**
* @brief
*
*
*
* @param[in] motion
* @param[in] autoDelete true
* @param[in] priority
* @return IsFinished()使-1
*/
public startMotionPriority(
motion: ACubismMotion,
autoDelete: boolean,
priority: number
): CubismMotionQueueEntryHandle {
if (priority == this.getReservePriority()) {
this.setReservePriority(0);
}
this._currentPriority = priority;
return this.startMotion(motion, autoDelete, this._userTimeSeconds);
}
/**
* @brief
*
*
*
* @param[in] model
* @param[in] deltaTimeSeconds []
* @retval true
* @retval false
*/
public updateMotion(model: CubismModel, deltaTimeSeconds: number): boolean {
this._userTimeSeconds += deltaTimeSeconds;
let updated = false;
const motions = this.getCubismMotionQueueEntries();
let expressionWeight = 0.0;
let expressionIndex = 0;
// ------- 処理を行う --------
// 既にモーションがあれば終了フラグを立てる
for (
let ite: iterator<CubismMotionQueueEntry> = this._motions.begin();
ite.notEqual(this._motions.end());
) {
const motionQueueEntry = ite.ptr();
if (motionQueueEntry == null) {
ite = motions.erase(ite); //削除
continue;
}
const expressionMotion = <CubismExpressionMotion>(
motionQueueEntry.getCubismMotion()
);
if (expressionMotion == null) {
csmDelete(motionQueueEntry);
ite = motions.erase(ite); //削除
continue;
}
const expressionParameters = expressionMotion.getExpressionParameters();
if (motionQueueEntry.isAvailable()) {
// 再生中のExpressionが参照しているパラメータをすべてリストアップ
for (let i = 0; i < expressionParameters.getSize(); ++i) {
if (expressionParameters.at(i).parameterId == null) {
continue;
}
let index = -1;
// リストにパラメータIDが存在するか検索
for (let j = 0; j < this._expressionParameterValues.getSize(); ++j) {
if (
this._expressionParameterValues.at(j).parameterId !=
expressionParameters.at(i).parameterId
) {
continue;
}
index = j;
break;
}
if (index >= 0) {
continue;
}
// パラメータがリストに存在しないなら新規追加
const item: ExpressionParameterValue = new ExpressionParameterValue();
item.parameterId = expressionParameters.at(i).parameterId;
item.additiveValue = CubismExpressionMotion.DefaultAdditiveValue;
item.multiplyValue = CubismExpressionMotion.DefaultMultiplyValue;
item.overwriteValue = model.getParameterValueById(item.parameterId);
this._expressionParameterValues.pushBack(item);
}
}
// ------ 値を計算する ------
expressionMotion.calculateExpressionParameters(
model,
this._userTimeSeconds,
motionQueueEntry,
this._expressionParameterValues,
expressionIndex
);
expressionWeight +=
expressionMotion.getFadeInTime() == 0.0
? 1.0
: CubismMath.getEasingSine(
(this._userTimeSeconds - motionQueueEntry.getFadeInStartTime()) /
expressionMotion.getFadeInTime()
);
updated = true;
if (motionQueueEntry.isTriggeredFadeOut()) {
// フェードアウト開始
motionQueueEntry.startFadeOut(
motionQueueEntry.getFadeOutSeconds(),
this._userTimeSeconds
);
}
ite.preIncrement();
++expressionIndex;
}
// ----- 最新のExpressionのフェードが完了していればそれ以前を削除する ------
if (motions.getSize() > 1) {
const expressionMotion = <CubismExpressionMotion>(
motions.at(motions.getSize() - 1).getCubismMotion()
);
if (expressionMotion.getFadeWeight() >= 1.0) {
// 配列の最後の要素は削除しない
for (let i = motions.getSize() - 2; i >= 0; --i) {
const motionQueueEntry = motions.at(i);
csmDelete(motionQueueEntry);
motions.remove(i);
}
}
}
if (expressionWeight > 1.0) {
expressionWeight = 1.0;
}
// モデルに各値を適用
for (let i = 0; i < this._expressionParameterValues.getSize(); ++i) {
const expressionParameterValue = this._expressionParameterValues.at(i);
model.setParameterValueById(
expressionParameterValue.parameterId,
(expressionParameterValue.overwriteValue +
expressionParameterValue.additiveValue) *
expressionParameterValue.multiplyValue,
expressionWeight
);
expressionParameterValue.additiveValue =
CubismExpressionMotion.DefaultAdditiveValue;
expressionParameterValue.multiplyValue =
CubismExpressionMotion.DefaultMultiplyValue;
}
return updated;
}
private _expressionParameterValues: csmVector<ExpressionParameterValue>; ///< モデルに適用する各パラメータの値
private _currentPriority: number; ///< 現在再生中のモーションの優先度
private _reservePriority: number; ///< 再生予定のモーションの優先度。再生中は0になる。モーションファイルを別スレッドで読み込むときの機能。
private _startExpressionTime: number; ///< 表情の再生開始時刻
}
// Namespace definition for compatibility.
import * as $ from './cubismexpressionmotionmanager';
import { CubismMath } from '../math/cubismmath';
// eslint-disable-next-line @typescript-eslint/no-namespace
export namespace Live2DCubismFramework {
export const CubismExpressionMotionManager = $.CubismExpressionMotionManager;
export type CubismExpressionMotionManager = $.CubismExpressionMotionManager;
}

View File

@ -14,7 +14,7 @@ import { csmVector } from '../type/csmvector';
import {
CSM_ASSERT,
CubismLogDebug,
CubismLogWarning,
CubismLogWarning
} from '../utils/cubismdebug';
import { ACubismMotion, FinishedMotionCallback } from './acubismmotion';
import {
@ -24,7 +24,7 @@ import {
CubismMotionEvent,
CubismMotionPoint,
CubismMotionSegment,
CubismMotionSegmentType,
CubismMotionSegmentType
} from './cubismmotioninternal';
import { CubismMotionJson, EvaluationOptionFlag } from './cubismmotionjson';
import { CubismMotionQueueEntry } from './cubismmotionqueueentry';
@ -35,6 +35,9 @@ const TargetNameModel = 'Model';
const TargetNameParameter = 'Parameter';
const TargetNamePartOpacity = 'PartOpacity';
// Id
const IdNameOpacity = 'Opacity';
/**
* Cubism SDK R2 true false
*/
@ -209,7 +212,7 @@ function evaluateCurve(
// Get first point of next segment.
pointPosition =
motionData.segments.at(i).basePointIndex +
(motionData.segments.at(i).segmentType ==
((motionData.segments.at(i).segmentType as CubismMotionSegmentType) ==
CubismMotionSegmentType.CubismMotionSegmentType_Bezier
? 3
: 1);
@ -284,6 +287,11 @@ export class CubismMotion extends ACubismMotion {
CubismFramework.getIdManager().getId(EffectNameLipSync);
}
if (this._modelCurveIdOpacity == null) {
this._modelCurveIdOpacity =
CubismFramework.getIdManager().getId(IdNameOpacity);
}
let timeOffsetSeconds: number =
userTimeSeconds - motionQueueEntry.getStartTime();
@ -357,6 +365,9 @@ export class CubismMotion extends ACubismMotion {
eyeBlinkValue = value;
} else if (curves.at(c).id == this._modelCurveIdLipSync) {
lipSyncValue = value;
} else if (curves.at(c).id == this._modelCurveIdOpacity) {
this._modelOpacity = value;
model.setModelOapcity(this.getModelOpacityValue());
}
}
@ -696,8 +707,10 @@ export class CubismMotion extends ACubismMotion {
this._motionData = null;
this._modelCurveIdEyeBlink = null;
this._modelCurveIdLipSync = null;
this._modelCurveIdOpacity = null;
this._eyeBlinkParameterIds = null;
this._lipSyncParameterIds = null;
this._modelOpacity = 1.0;
}
/**
@ -971,6 +984,81 @@ export class CubismMotion extends ACubismMotion {
return this._firedEventValues;
}
/**
*
*
* @returns true ->
* false ->
*/
public isExistModelOpacity(): boolean {
for (let i = 0; i < this._motionData.curveCount; i++) {
const curve: CubismMotionCurve = this._motionData.curves.at(i);
if (curve.type != CubismMotionCurveTarget.CubismMotionCurveTarget_Model) {
continue;
}
if (curve.id.getString().s.localeCompare(IdNameOpacity) == 0) {
return true;
}
}
return false;
}
/**
*
*
* @returns success:
*/
public getModelOpacityIndex(): number {
if (this.isExistModelOpacity()) {
for (let i = 0; i < this._motionData.curveCount; i++) {
const curve: CubismMotionCurve = this._motionData.curves.at(i);
if (
curve.type != CubismMotionCurveTarget.CubismMotionCurveTarget_Model
) {
continue;
}
if (curve.id.getString().s.localeCompare(IdNameOpacity) == 0) {
return i;
}
}
}
return -1;
}
/**
* Id
*
* @param index
* @returns success:
*/
public getModelOpacityId(index: number): CubismIdHandle {
if (index != -1) {
const curve: CubismMotionCurve = this._motionData.curves.at(index);
if (curve.type == CubismMotionCurveTarget.CubismMotionCurveTarget_Model) {
if (curve.id.getString().s.localeCompare(IdNameOpacity) == 0) {
return CubismFramework.getIdManager().getId(curve.id.getString().s);
}
}
}
return null;
}
/**
*
*
* @returns success:Opacity
*/
public getModelOpacityValue(): number {
return this._modelOpacity;
}
public _sourceFrameRate: number; // ロードしたファイルのFPS。記述が無ければデフォルト値15fpsとなる
public _loopDurationSeconds: number; // mtnファイルで定義される一連のモーションの長さ
public _isLoop: boolean; // ループするか?
@ -984,6 +1072,9 @@ export class CubismMotion extends ACubismMotion {
public _modelCurveIdEyeBlink: CubismIdHandle; // モデルが持つ自動まばたき用パラメータIDのハンドル。 モデルとモーションを対応付ける。
public _modelCurveIdLipSync: CubismIdHandle; // モデルが持つリップシンク用パラメータIDのハンドル。 モデルとモーションを対応付ける。
public _modelCurveIdOpacity: CubismIdHandle; // モデルが持つ不透明度用パラメータIDのハンドル。 モデルとモーションを対応付ける。
public _modelOpacity: number; // モーションから取得した不透明度
}
// Namespace definition for compatibility.

View File

@ -17,7 +17,7 @@ import { csmVector } from '../type/csmvector';
export enum CubismMotionCurveTarget {
CubismMotionCurveTarget_Model, // モデルに対して
CubismMotionCurveTarget_Parameter, // パラメータに対して
CubismMotionCurveTarget_PartOpacity, // パーツの不透明度に対して
CubismMotionCurveTarget_PartOpacity // パーツの不透明度に対して
}
/**
@ -29,7 +29,7 @@ export enum CubismMotionSegmentType {
CubismMotionSegmentType_Linear = 0, // リニア
CubismMotionSegmentType_Bezier = 1, // ベジェ曲線
CubismMotionSegmentType_Stepped = 2, // ステップ
CubismMotionSegmentType_InverseStepped = 3, // インバースステップ
CubismMotionSegmentType_InverseStepped = 3 // インバースステップ
}
/**

View File

@ -8,7 +8,7 @@
import { CubismIdHandle } from '../id/cubismid';
import { CubismFramework } from '../live2dcubismframework';
import { csmString } from '../type/csmstring';
import { CubismJson } from '../utils/cubismjson';
import { CubismJson, JsonMap } from '../utils/cubismjson';
// JSON keys
const Meta = 'Meta';
@ -76,7 +76,7 @@ export class CubismMotionJson {
.toBoolean();
}
public getEvaluationOptionFlag(flagType: number): boolean {
public getEvaluationOptionFlag(flagType: EvaluationOptionFlag): boolean {
if (
EvaluationOptionFlag.EvaluationOptionFlag_AreBeziersRistricted == flagType
) {
@ -371,7 +371,7 @@ export class CubismMotionJson {
* @brief
*/
export enum EvaluationOptionFlag {
EvaluationOptionFlag_AreBeziersRistricted = 0, ///< ベジェハンドルの規制状態
EvaluationOptionFlag_AreBeziersRistricted = 0 ///< ベジェハンドルの規制状態
}
// Namespace definition for compatibility.

View File

@ -9,7 +9,7 @@ import { CubismModel } from '../model/cubismmodel';
import { ACubismMotion } from './acubismmotion';
import {
CubismMotionQueueEntryHandle,
CubismMotionQueueManager,
CubismMotionQueueManager
} from './cubismmotionqueuemanager';
/**

View File

@ -226,6 +226,15 @@ export class CubismMotionQueueEntry {
return this._fadeOutSeconds;
}
/**
*
*
* @return
*/
public getCubismMotion(): ACubismMotion {
return this._motion;
}
_autoDelete: boolean; // 自動削除
_motion: ACubismMotion; // モーション

View File

@ -184,12 +184,24 @@ export class CubismMotionQueueManager {
}
/**
* CubismMotionQueueEntry
* @brief CubismMotionQueueEntry
*
* CubismMotionQueueEntry
*
* @return CubismMotionQueueEntry
* @retval NULL
*/
public getCubismMotionQueueEntries(): csmVector<CubismMotionQueueEntry> {
return this._motions;
}
* @param motionQueueEntryNumber
* @return CubismMotionQueueEntry
* @return null
*/
/**
* CubismMotionQueueEntry
* @param motionQueueEntryNumber
* @return CubismMotionQueueEntry
* @return null
*/
public getCubismMotionQueueEntry(
motionQueueEntryNumber: any
): CubismMotionQueueEntry {

View File

@ -7,6 +7,7 @@
import { CubismMath } from '../math/cubismmath';
import { CubismVector2 } from '../math/cubismvector2';
import { csmVector } from '../type/csmvector';
import { CubismModel } from '../model/cubismmodel';
import {
CubismPhysicsInput,
@ -16,7 +17,7 @@ import {
CubismPhysicsRig,
CubismPhysicsSource,
CubismPhysicsSubRig,
CubismPhysicsTargetType,
CubismPhysicsTargetType
} from './cubismphysicsinternal';
import { CubismPhysicsJson } from './cubismphysicsjson';
@ -34,6 +35,9 @@ const MaximumWeight = 100.0;
// Constant of threshold of movement.
const MovementThreshold = 0.001;
// Constant of maximum allowed delta time
const MaxDeltaTime = 5.0;
/**
*
*/
@ -64,196 +68,6 @@ export class CubismPhysics {
}
}
/**
*
* @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
@ -268,6 +82,8 @@ export class CubismPhysics {
this._physicsRig.wind = json.getWind();
this._physicsRig.subRigCount = json.getSubRigCount();
this._physicsRig.fps = json.getFps();
this._physicsRig.settings.updateSize(
this._physicsRig.subRigCount,
CubismPhysicsSubRig,
@ -289,6 +105,9 @@ export class CubismPhysics {
true
);
this._currentRigOutputs.clear();
this._previousRigOutputs.clear();
let inputIndex = 0,
outputIndex = 0,
particleIndex = 0;
@ -326,19 +145,22 @@ export class CubismPhysics {
CubismPhysicsSource.CubismPhysicsSource_X;
this._physicsRig.inputs.at(
inputIndex + j
).getNormalizedParameterValue = getInputTranslationXFromNormalizedParameterValue;
).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;
).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;
).getNormalizedParameterValue =
getInputAngleFromNormalizedParameterValue;
}
this._physicsRig.inputs.at(inputIndex + j).source.targetType =
@ -352,7 +174,21 @@ export class CubismPhysics {
this._physicsRig.settings.at(i).outputCount = json.getOutputCount(i);
this._physicsRig.settings.at(i).baseOutputIndex = outputIndex;
const currentRigOutput = new PhysicsOutput();
currentRigOutput.outputs.resize(
this._physicsRig.settings.at(i).outputCount
);
const previousRigOutput = new PhysicsOutput();
previousRigOutput.outputs.resize(
this._physicsRig.settings.at(i).outputCount
);
for (let j = 0; j < this._physicsRig.settings.at(i).outputCount; ++j) {
// initialize
currentRigOutput.outputs.set(j, 0.0);
previousRigOutput.outputs.set(j, 0.0);
this._physicsRig.outputs.at(outputIndex + j).destinationParameterIndex =
-1;
this._physicsRig.outputs.at(outputIndex + j).vertexIndex =
@ -393,6 +229,10 @@ export class CubismPhysics {
this._physicsRig.outputs.at(outputIndex + j).reflect =
json.getOutputReflect(i, j);
}
this._currentRigOutputs.pushBack(currentRigOutput);
this._previousRigOutputs.pushBack(previousRigOutput);
outputIndex += this._physicsRig.settings.at(i).outputCount;
// Particle
@ -422,6 +262,541 @@ export class CubismPhysics {
json = null;
}
/**
*
* @param model
*/
public stabilization(model: CubismModel): void {
let totalAngle: { angle: number };
let weight: number;
let radAngle: number;
let outputValue: number;
const totalTranslation: CubismVector2 = new CubismVector2();
let currentSetting: CubismPhysicsSubRig;
let currentInputs: CubismPhysicsInput[];
let currentOutputs: CubismPhysicsOutput[];
let currentParticles: CubismPhysicsParticle[];
let parameterValues: Float32Array;
let parameterMaximumValues: Float32Array;
let parameterMinimumValues: Float32Array;
let parameterDefaultValues: Float32Array;
parameterValues = model.getModel().parameters.values;
parameterMaximumValues = model.getModel().parameters.maximumValues;
parameterMinimumValues = model.getModel().parameters.minimumValues;
parameterDefaultValues = model.getModel().parameters.defaultValues;
if ((this._parameterCaches?.length ?? 0) < model.getParameterCount()) {
this._parameterCaches = new Float32Array(model.getParameterCount());
}
if ((this._parameterInputCaches?.length ?? 0) < model.getParameterCount()) {
this._parameterInputCaches = new Float32Array(model.getParameterCount());
}
for (let j = 0; j < model.getParameterCount(); ++j) {
this._parameterCaches[j] = parameterValues[j];
this._parameterInputCaches[j] = parameterValues[j];
}
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);
currentInputs = this._physicsRig.inputs.get(
currentSetting.baseInputIndex
);
currentOutputs = 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 = currentInputs[i].weight / MaximumWeight;
if (currentInputs[i].sourceParameterIndex == -1) {
currentInputs[i].sourceParameterIndex = model.getParameterIndex(
currentInputs[i].source.id
);
}
currentInputs[i].getNormalizedParameterValue(
totalTranslation,
totalAngle,
parameterValues[currentInputs[i].sourceParameterIndex],
parameterMinimumValues[currentInputs[i].sourceParameterIndex],
parameterMaximumValues[currentInputs[i].sourceParameterIndex],
parameterDefaultValues[currentInputs[i].sourceParameterIndex],
currentSetting.normalizationPosition,
currentSetting.normalizationAngle,
currentInputs[i].reflect,
weight
);
this._parameterCaches[currentInputs[i].sourceParameterIndex] =
parameterValues[currentInputs[i].sourceParameterIndex];
}
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.
updateParticlesForStabilization(
currentParticles,
currentSetting.particleCount,
totalTranslation,
totalAngle.angle,
this._options.wind,
MovementThreshold * currentSetting.normalizationPosition.maximum
);
// Update output parameters.
for (let i = 0; i < currentSetting.outputCount; ++i) {
const particleIndex = currentOutputs[i].vertexIndex;
if (currentOutputs[i].destinationParameterIndex == -1) {
currentOutputs[i].destinationParameterIndex = model.getParameterIndex(
currentOutputs[i].destination.id
);
}
if (
particleIndex < 1 ||
particleIndex >= currentSetting.particleCount
) {
continue;
}
let translation: CubismVector2 = new CubismVector2();
translation = currentParticles[particleIndex].position.substract(
currentParticles[particleIndex - 1].position
);
outputValue = currentOutputs[i].getValue(
translation,
currentParticles,
particleIndex,
currentOutputs[i].reflect,
this._options.gravity
);
this._currentRigOutputs.at(settingIndex).outputs.set(i, outputValue);
this._previousRigOutputs.at(settingIndex).outputs.set(i, outputValue);
const destinationParameterIndex: number =
currentOutputs[i].destinationParameterIndex;
const outParameterCaches: Float32Array =
!Float32Array.prototype.slice && 'subarray' in Float32Array.prototype
? JSON.parse(
JSON.stringify(
parameterValues.subarray(destinationParameterIndex)
)
) // 値渡しするため、JSON.parse, JSON.stringify
: parameterValues.slice(destinationParameterIndex);
updateOutputParameterValue(
outParameterCaches,
parameterMinimumValues[destinationParameterIndex],
parameterMaximumValues[destinationParameterIndex],
outputValue,
currentOutputs[i]
);
// 値を反映
for (
let offset: number = destinationParameterIndex, outParamIndex = 0;
offset < this._parameterCaches.length;
offset++, outParamIndex++
) {
parameterValues[offset] = this._parameterCaches[offset] =
outParameterCaches[outParamIndex];
}
}
}
}
/**
*
*
* Pendulum interpolation weights
*
*
* The result of the pendulum calculation is saved and
* the output to the parameters is interpolated with the saved previous result of the pendulum calculation.
*
* [1][2]
* The figure shows the interpolation between [1] and [2].
*
*
* The weight of the interpolation are determined by the current time seen between
* the latest pendulum calculation timing and the next timing.
*
* [2][4](3)
* Figure shows the weight of position (3) as seen between [2] and [4].
*
*
* As an interpretation, the pendulum calculation and weights are misaligned.
*
* physics3.jsonFPS
* If there is no FPS information in physics3.json, it is always set in the previous pendulum state.
*
*
* The purpose of this specification is to avoid the quivering appearance caused by deviations from the interpolation range.
*
* ------------ time -------------->
*
* |+++++|------| <- weight
* ==[1]====#=====[2]---(3)----(4)
* ^ output contents
*
* 1:_previousRigOutputs
* 2:_currentRigOutputs
* 3:_currentRemainTime (now rendering)
* 4:next particles timing
* @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 currentInputs: CubismPhysicsInput[];
let currentOutputs: CubismPhysicsOutput[];
let currentParticles: CubismPhysicsParticle[];
if (0.0 >= deltaTimeSeconds) {
return;
}
let parameterValues: Float32Array;
let parameterMaximumValues: Float32Array;
let parameterMinimumValues: Float32Array;
let parameterDefaultValues: Float32Array;
let physicsDeltaTime: number;
this._currentRemainTime += deltaTimeSeconds;
if (this._currentRemainTime > MaxDeltaTime) {
this._currentRemainTime = 0.0;
}
parameterValues = model.getModel().parameters.values;
parameterMaximumValues = model.getModel().parameters.maximumValues;
parameterMinimumValues = model.getModel().parameters.minimumValues;
parameterDefaultValues = model.getModel().parameters.defaultValues;
if ((this._parameterCaches?.length ?? 0) < model.getParameterCount()) {
this._parameterCaches = new Float32Array(model.getParameterCount());
}
if ((this._parameterInputCaches?.length ?? 0) < model.getParameterCount()) {
this._parameterInputCaches = new Float32Array(model.getParameterCount());
for (let j = 0; j < model.getParameterCount(); ++j) {
this._parameterInputCaches[j] = parameterValues[j];
}
}
if (this._physicsRig.fps > 0.0) {
physicsDeltaTime = 1.0 / this._physicsRig.fps;
} else {
physicsDeltaTime = deltaTimeSeconds;
}
while (this._currentRemainTime >= physicsDeltaTime) {
// copyRigOutputs _currentRigOutputs to _previousRigOutputs
for (
let settingIndex = 0;
settingIndex < this._physicsRig.subRigCount;
++settingIndex
) {
currentSetting = this._physicsRig.settings.at(settingIndex);
currentOutputs = this._physicsRig.outputs.get(
currentSetting.baseOutputIndex
);
for (let i = 0; i < currentSetting.outputCount; ++i) {
this._previousRigOutputs
.at(settingIndex)
.outputs.set(
i,
this._currentRigOutputs.at(settingIndex).outputs.at(i)
);
}
}
// 入力キャッシュとパラメータで線形補間してUpdateParticlesするタイミングでの入力を計算する。
// Calculate the input at the timing to UpdateParticles by linear interpolation with the _parameterInputCache and parameterValue.
// _parameterCacheはグループ間での値の伝搬の役割があるので_parameterInputCacheとの分離が必要。
// _parameterCache needs to be separated from _parameterInputCache because of its role in propagating values between groups.
const inputWeight = physicsDeltaTime / this._currentRemainTime;
for (let j = 0; j < model.getParameterCount(); ++j) {
this._parameterCaches[j] =
this._parameterInputCaches[j] * (1.0 - inputWeight) +
parameterValues[j] * inputWeight;
this._parameterInputCaches[j] = this._parameterCaches[j];
}
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);
currentInputs = this._physicsRig.inputs.get(
currentSetting.baseInputIndex
);
currentOutputs = 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 = currentInputs[i].weight / MaximumWeight;
if (currentInputs[i].sourceParameterIndex == -1) {
currentInputs[i].sourceParameterIndex = model.getParameterIndex(
currentInputs[i].source.id
);
}
currentInputs[i].getNormalizedParameterValue(
totalTranslation,
totalAngle,
this._parameterCaches[currentInputs[i].sourceParameterIndex],
parameterMinimumValues[currentInputs[i].sourceParameterIndex],
parameterMaximumValues[currentInputs[i].sourceParameterIndex],
parameterDefaultValues[currentInputs[i].sourceParameterIndex],
currentSetting.normalizationPosition,
currentSetting.normalizationAngle,
currentInputs[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,
physicsDeltaTime,
AirResistance
);
// Update output parameters.
for (let i = 0; i < currentSetting.outputCount; ++i) {
const particleIndex = currentOutputs[i].vertexIndex;
if (currentOutputs[i].destinationParameterIndex == -1) {
currentOutputs[i].destinationParameterIndex =
model.getParameterIndex(currentOutputs[i].destination.id);
}
if (
particleIndex < 1 ||
particleIndex >= currentSetting.particleCount
) {
continue;
}
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 = currentOutputs[i].getValue(
translation,
currentParticles,
particleIndex,
currentOutputs[i].reflect,
this._options.gravity
);
this._currentRigOutputs.at(settingIndex).outputs.set(i, outputValue);
const destinationParameterIndex: number =
currentOutputs[i].destinationParameterIndex;
const outParameterCaches: Float32Array =
!Float32Array.prototype.slice &&
'subarray' in Float32Array.prototype
? JSON.parse(
JSON.stringify(
this._parameterCaches.subarray(destinationParameterIndex)
)
) // 値渡しするため、JSON.parse, JSON.stringify
: this._parameterCaches.slice(destinationParameterIndex);
updateOutputParameterValue(
outParameterCaches,
parameterMinimumValues[destinationParameterIndex],
parameterMaximumValues[destinationParameterIndex],
outputValue,
currentOutputs[i]
);
// 値を反映
for (
let offset: number = destinationParameterIndex, outParamIndex = 0;
offset < this._parameterCaches.length;
offset++, outParamIndex++
) {
this._parameterCaches[offset] = outParameterCaches[outParamIndex];
}
}
}
this._currentRemainTime -= physicsDeltaTime;
}
const alpha: number = this._currentRemainTime / physicsDeltaTime;
this.interpolate(model, alpha);
}
/**
*
*
* @param model
* @param weight
*/
public interpolate(model: CubismModel, weight: number): void {
let currentOutputs: CubismPhysicsOutput[];
let currentSetting: CubismPhysicsSubRig;
let parameterValues: Float32Array;
let parameterMaximumValues: Float32Array;
let parameterMinimumValues: Float32Array;
parameterValues = model.getModel().parameters.values;
parameterMaximumValues = model.getModel().parameters.maximumValues;
parameterMinimumValues = model.getModel().parameters.minimumValues;
for (
let settingIndex = 0;
settingIndex < this._physicsRig.subRigCount;
++settingIndex
) {
currentSetting = this._physicsRig.settings.at(settingIndex);
currentOutputs = this._physicsRig.outputs.get(
currentSetting.baseOutputIndex
);
// Load input parameters.
for (let i = 0; i < currentSetting.outputCount; ++i) {
if (currentOutputs[i].destinationParameterIndex == -1) {
continue;
}
const destinationParameterIndex: number =
currentOutputs[i].destinationParameterIndex;
const outParameterValues: Float32Array =
!Float32Array.prototype.slice && 'subarray' in Float32Array.prototype
? JSON.parse(
JSON.stringify(
parameterValues.subarray(destinationParameterIndex)
)
) // 値渡しするため、JSON.parse, JSON.stringify
: parameterValues.slice(destinationParameterIndex);
updateOutputParameterValue(
outParameterValues,
parameterMinimumValues[destinationParameterIndex],
parameterMaximumValues[destinationParameterIndex],
this._previousRigOutputs.at(settingIndex).outputs.at(i) *
(1 - weight) +
this._currentRigOutputs.at(settingIndex).outputs.at(i) * weight,
currentOutputs[i]
);
// 値を反映
for (
let offset: number = destinationParameterIndex, outParamIndex = 0;
offset < parameterValues.length;
offset++, outParamIndex++
) {
parameterValues[offset] = outParameterValues[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.0;
this._options.wind.x = 0.0;
this._options.wind.y = 0.0;
this._currentRigOutputs = new csmVector<PhysicsOutput>();
this._previousRigOutputs = new csmVector<PhysicsOutput>();
this._currentRemainTime = 0.0;
this._parameterCaches = null;
this._parameterInputCaches = null;
}
/**
*
*/
public release(): void {
this._physicsRig = void 0;
this._physicsRig = null;
}
/**
*
*/
@ -449,7 +824,7 @@ export class CubismPhysics {
strand[0].velocity = new CubismVector2(0.0, 0.0);
strand[0].force = new CubismVector2(0.0, 0.0);
// Initialize paritcles.
// Initialize particles.
for (let i = 1; i < currentSetting.particleCount; ++i) {
radius = new CubismVector2(0.0, 0.0);
radius.y = strand[i].radius;
@ -475,6 +850,14 @@ export class CubismPhysics {
_physicsRig: CubismPhysicsRig; // 物理演算のデータ
_options: Options; // オプション
_currentRigOutputs: csmVector<PhysicsOutput>; ///< 最新の振り子計算の結果
_previousRigOutputs: csmVector<PhysicsOutput>; ///< 一つ前の振り子計算の結果
_currentRemainTime: number; ///< 物理演算が処理していない時間
_parameterCaches: Float32Array; ///< Evaluateで利用するパラメータのキャッシュ
_parameterInputCaches: Float32Array; ///< UpdateParticlesが動くときの入力をキャッシュ
}
/**
@ -490,6 +873,17 @@ export class Options {
wind: CubismVector2; // 風の方向
}
/**
*
*/
export class PhysicsOutput {
constructor() {
this.outputs = new csmVector<number>(0);
}
outputs: csmVector<number>; // 物理演算出力結果
}
/**
* Gets sign.
*
@ -773,6 +1167,66 @@ function updateParticles(
}
}
/**
* Updates particles for stabilization.
*
* @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.
*/
function updateParticlesForStabilization(
strand: CubismPhysicsParticle[],
strandCount: number,
totalTranslation: CubismVector2,
totalAngle: number,
windDirection: CubismVector2,
thresholdValue: number
) {
let totalRadian: number;
let currentGravity: CubismVector2;
let force: 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
);
strand[i].velocity = new CubismVector2(0.0, 0.0);
force = strand[i].force;
force.normalize();
force = force.multiplyByScaler(strand[i].radius);
strand[i].position = strand[i - 1].position.add(force);
if (CubismMath.abs(strand[i].position.x) < thresholdValue) {
strand[i].position.x = 0.0;
}
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.

View File

@ -13,7 +13,7 @@ import { csmVector } from '../type/csmvector';
*
*/
export enum CubismPhysicsTargetType {
CubismPhysicsTargetType_Parameter, // パラメータに対して適用
CubismPhysicsTargetType_Parameter // パラメータに対して適用
}
/**
@ -22,7 +22,7 @@ export enum CubismPhysicsTargetType {
export enum CubismPhysicsSource {
CubismPhysicsSource_X, // X軸の位置から
CubismPhysicsSource_Y, // Y軸の位置から
CubismPhysicsSource_Angle, // 角度から
CubismPhysicsSource_Angle // 角度から
}
/**
@ -208,6 +208,7 @@ export class CubismPhysicsRig {
this.particles = new csmVector<CubismPhysicsParticle>();
this.gravity = new CubismVector2(0, 0);
this.wind = new CubismVector2(0, 0);
this.fps = 0.0;
}
subRigCount: number; // 物理演算の物理点の個数
@ -217,6 +218,7 @@ export class CubismPhysicsRig {
particles: csmVector<CubismPhysicsParticle>; // 物理演算の物理点のリスト
gravity: CubismVector2; // 重力
wind: CubismVector2; // 風
fps: number; //物理演算動作FPS
}
// Namespace definition for compatibility.

View File

@ -27,6 +27,7 @@ const PhysicsSettingCount = 'PhysicsSettingCount';
const Gravity = 'Gravity';
const Wind = 'Wind';
const VertexCount = 'VertexCount';
const Fps = 'Fps';
// PhysicsSettings
const PhysicsSettings = 'PhysicsSettings';
@ -120,6 +121,18 @@ export class CubismPhysicsJson {
return ret;
}
/**
* FPS
* @return FPS
*/
public getFps(): number {
return this._json
.getRoot()
.getValueByString(Meta)
.getValueByString(Fps)
.toFloat(0.0);
}
/**
*
* @return

View File

@ -0,0 +1,706 @@
/**
* Copyright(c) Live2D Inc. All rights reserved.
*
* Use of this source code is governed by the Live2D Open Software license
* that can be found at https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
*/
import { Constant } from '../live2dcubismframework';
import { csmVector } from '../type/csmvector';
import { csmRect } from '../type/csmrectf';
import { CubismMatrix44 } from '../math/cubismmatrix44';
import { CubismModel } from '../model/cubismmodel';
import { CubismClippingContext, CubismTextureColor } from './cubismrenderer';
import { CubismLogError, CubismLogWarning } from '../utils/cubismdebug';
const ColorChannelCount = 4; // 実験時に1チャンネルの場合は1、RGBだけの場合は3、アルファも含める場合は4
const ClippingMaskMaxCountOnDefault = 36; // 通常のフレームバッファ一枚あたりのマスク最大数
const ClippingMaskMaxCountOnMultiRenderTexture = 32; // フレームバッファが2枚以上ある場合のフレームバッファ一枚あたりのマスク最大数
export type ClippingContextConstructor<
T_ClippingContext extends CubismClippingContext
> = new (
manager: CubismClippingManager<T_ClippingContext>,
drawableMasks: Int32Array,
drawableMaskCounts: number
) => T_ClippingContext;
export interface ICubismClippingManager {
getClippingMaskBufferSize(): number;
}
export abstract class CubismClippingManager<
T_ClippingContext extends CubismClippingContext
> implements ICubismClippingManager
{
/**
*
*/
public constructor(
clippingContextFactory: ClippingContextConstructor<T_ClippingContext>
) {
this._renderTextureCount = 0;
this._clippingMaskBufferSize = 256;
this._clippingContextListForMask = new csmVector<T_ClippingContext>();
this._clippingContextListForDraw = new csmVector<T_ClippingContext>();
this._channelColors = new csmVector<CubismTextureColor>();
this._tmpBoundsOnModel = new csmRect();
this._tmpMatrix = new CubismMatrix44();
this._tmpMatrixForMask = new CubismMatrix44();
this._tmpMatrixForDraw = new CubismMatrix44();
this._clippingContexttConstructor = clippingContextFactory;
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;
for (let i = 0; i < this._channelColors.getSize(); i++) {
this._channelColors.set(i, null);
}
this._channelColors = null;
if (this._clearedFrameBufferFlags != null) {
this._clearedFrameBufferFlags.clear();
}
this._clearedFrameBufferFlags = null;
}
/**
*
* 使
* @param model
* @param renderTextureCount
*/
public initialize(model: CubismModel, renderTextureCount: number): void {
// レンダーテクスチャの合計枚数の設定
// 1以上の整数でない場合はそれぞれ警告を出す
if (renderTextureCount % 1 != 0) {
CubismLogWarning(
'The number of render textures must be specified as an integer. The decimal point is rounded down and corrected to an integer.'
);
// 小数点以下を除去
renderTextureCount = ~~renderTextureCount;
}
if (renderTextureCount < 1) {
CubismLogWarning(
'The number of render textures must be an integer greater than or equal to 1. Set the number of render textures to 1.'
);
}
// 負の値が使われている場合は強制的に1枚と設定する
this._renderTextureCount = renderTextureCount < 1 ? 1 : renderTextureCount;
this._clearedFrameBufferFlags = new csmVector<boolean>(
this._renderTextureCount
);
// クリッピングマスクを使う描画オブジェクトをすべて登録する
// クリッピングマスクは、通常数個程度に限定して使うものとする
for (let i = 0; i < model.getDrawableCount(); i++) {
if (model.getDrawableMaskCounts()[i] <= 0) {
// クリッピングマスクが使用されていないアートメッシュ(多くの場合使用しない)
this._clippingContextListForDraw.pushBack(null);
continue;
}
// 既にあるClipContextと同じかチェックする
let clippingContext: T_ClippingContext = this.findSameClip(
model.getDrawableMasks()[i],
model.getDrawableMaskCounts()[i]
);
if (clippingContext == null) {
// 同一のマスクが存在していない場合は生成する
clippingContext = new this._clippingContexttConstructor(
this,
model.getDrawableMasks()[i],
model.getDrawableMaskCounts()[i]
);
this._clippingContextListForMask.pushBack(clippingContext);
}
clippingContext.addClippedDrawable(i);
this._clippingContextListForDraw.pushBack(clippingContext);
}
}
/**
*
*
* NULL
* @param drawableMasks
* @param drawableMaskCounts
* @return NULL
*/
public findSameClip(
drawableMasks: Int32Array,
drawableMaskCounts: number
): T_ClippingContext {
// 作成済みClippingContextと一致するか確認
for (let i = 0; i < this._clippingContextListForMask.getSize(); i++) {
const clippingContext: T_ClippingContext =
this._clippingContextListForMask.at(i);
const count: number = clippingContext._clippingIdCount;
// 個数が違う場合は別物
if (count != drawableMaskCounts) {
continue;
}
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; // 見つからなかった
}
/**
*
* @param model
* @param isRightHanded
*/
public setupMatrixForHighPrecision(
model: CubismModel,
isRightHanded: boolean
): void {
// 全てのクリッピングを用意する
// 同じクリップ複数の場合はまとめて一つのクリップを使う場合は1度だけ設定する
let usingClipCount = 0;
for (
let clipIndex = 0;
clipIndex < this._clippingContextListForMask.getSize();
clipIndex++
) {
// 1つのクリッピングマスクに関して
const cc: T_ClippingContext =
this._clippingContextListForMask.at(clipIndex);
// このクリップを利用する描画オブジェクト群全体を囲む矩形を計算
this.calcClippedDrawTotalBounds(model, cc);
if (cc._isUsing) {
usingClipCount++; // 使用中としてカウント
}
}
// マスク行列作成処理
if (usingClipCount > 0) {
this.setupLayoutBounds(0);
// サイズがレンダーテクスチャの枚数と合わない場合は合わせる
if (this._clearedFrameBufferFlags.getSize() != this._renderTextureCount) {
this._clearedFrameBufferFlags.clear();
for (let i = 0; i < this._renderTextureCount; i++) {
this._clearedFrameBufferFlags.pushBack(false);
}
} else {
// マスクのクリアフラグを毎フレーム開始時に初期化
for (let i = 0; i < this._renderTextureCount; i++) {
this._clearedFrameBufferFlags.set(i, false);
}
}
// 実際にマスクを生成する
// 全てのマスクをどの様にレイアウトして描くかを決定し、ClipContext , ClippedDrawContext に記憶する
for (
let clipIndex = 0;
clipIndex < this._clippingContextListForMask.getSize();
clipIndex++
) {
// --- 実際に1つのマスクを描く ---
const clipContext: T_ClippingContext =
this._clippingContextListForMask.at(clipIndex);
const allClippedDrawRect: csmRect = clipContext._allClippedDrawRect; //このマスクを使う、全ての描画オブジェクトの論理座標上の囲み矩形
const layoutBoundsOnTex01 = clipContext._layoutBounds; //この中にマスクを収める
const MARGIN = 0.05;
let scaleX = 0.0;
let scaleY = 0.0;
const ppu: number = model.getPixelsPerUnit();
const maskPixelSize: number = clipContext
.getClippingManager()
.getClippingMaskBufferSize();
const physicalMaskWidth: number =
layoutBoundsOnTex01.width * maskPixelSize;
const physicalMaskHeight: number =
layoutBoundsOnTex01.height * maskPixelSize;
this._tmpBoundsOnModel.setRect(allClippedDrawRect);
if (this._tmpBoundsOnModel.width * ppu > physicalMaskWidth) {
this._tmpBoundsOnModel.expand(allClippedDrawRect.width * MARGIN, 0.0);
scaleX = layoutBoundsOnTex01.width / this._tmpBoundsOnModel.width;
} else {
scaleX = ppu / physicalMaskWidth;
}
if (this._tmpBoundsOnModel.height * ppu > physicalMaskHeight) {
this._tmpBoundsOnModel.expand(
0.0,
allClippedDrawRect.height * MARGIN
);
scaleY = layoutBoundsOnTex01.height / this._tmpBoundsOnModel.height;
} else {
scaleY = ppu / physicalMaskHeight;
}
// マスク生成時に使う行列を求める
this.createMatrixForMask(
isRightHanded,
layoutBoundsOnTex01,
scaleX,
scaleY
);
clipContext._matrixForMask.setMatrix(this._tmpMatrixForMask.getArray());
clipContext._matrixForDraw.setMatrix(this._tmpMatrixForDraw.getArray());
}
}
}
/**
*
* @param isRightHanded
* @param layoutBoundsOnTex01
* @param scaleX
* @param scaleY
*/
public createMatrixForMask(
isRightHanded: boolean,
layoutBoundsOnTex01: csmRect,
scaleX: number,
scaleY: number
): void {
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
); //new = [translate]
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());
this._tmpMatrix.loadIdentity();
{
this._tmpMatrix.translateRelative(
layoutBoundsOnTex01.x,
layoutBoundsOnTex01.y * (isRightHanded ? -1.0 : 1.0)
); //new = [translate]
this._tmpMatrix.scaleRelative(
scaleX,
scaleY * (isRightHanded ? -1.0 : 1.0)
); //new = [translate][scale]
this._tmpMatrix.translateRelative(
-this._tmpBoundsOnModel.x,
-this._tmpBoundsOnModel.y
); //new = [translate][scale][translate]
}
this._tmpMatrixForDraw.setMatrix(this._tmpMatrix.getArray());
}
/**
*
* 使
* 4RGBA56RGBA2,2,1,1
*
* @param usingClipCount
*/
public setupLayoutBounds(usingClipCount: number): void {
const useClippingMaskMaxCount =
this._renderTextureCount <= 1
? ClippingMaskMaxCountOnDefault
: ClippingMaskMaxCountOnMultiRenderTexture * this._renderTextureCount;
if (usingClipCount <= 0 || usingClipCount > useClippingMaskMaxCount) {
if (usingClipCount > useClippingMaskMaxCount) {
// マスクの制限数の警告を出す
CubismLogError(
'not supported mask count : {0}\n[Details] render texture count : {1}, mask count : {2}',
usingClipCount - useClippingMaskMaxCount,
this._renderTextureCount,
usingClipCount
);
}
// この場合は一つのマスクターゲットを毎回クリアして使用する
for (
let index = 0;
index < this._clippingContextListForMask.getSize();
index++
) {
const clipContext: T_ClippingContext =
this._clippingContextListForMask.at(index);
clipContext._layoutChannelNo = 0; // どうせ毎回消すので固定
clipContext._layoutBounds.x = 0.0;
clipContext._layoutBounds.y = 0.0;
clipContext._layoutBounds.width = 1.0;
clipContext._layoutBounds.height = 1.0;
clipContext._bufferIndex = 0;
}
return;
}
// レンダーテクスチャが1枚なら9分割する最大36枚
const layoutCountMaxValue = this._renderTextureCount <= 1 ? 9 : 8;
// 指定された数のレンダーテクスチャを極力いっぱいに使ってマスクをレイアウトするデフォルトなら1
// マスクグループの数が4以下ならRGBA各チャンネルに1つずつマスクを配置し、5以上6以下ならRGBAを2,2,1,1と配置する
let countPerSheetDiv: number = usingClipCount / this._renderTextureCount; // レンダーテクスチャ1枚あたり何枚割り当てるか
let countPerSheetMod: number = usingClipCount % this._renderTextureCount; // この番号のレンダーテクスチャまでに一つずつ配分する
// 小数点は切り捨てる
countPerSheetDiv = ~~countPerSheetDiv;
countPerSheetMod = ~~countPerSheetMod;
// RGBAを順番に使っていく
let div: number = countPerSheetDiv / ColorChannelCount; // 1チャンネルに配置する基本のマスク
let mod: number = countPerSheetDiv % ColorChannelCount; // 余り、この番号のチャンネルまでに一つずつ配分する
// 小数点は切り捨てる
div = ~~div;
mod = ~~mod;
// RGBAそれぞれのチャンネルを用意していく0:R, 1:G, 2:B, 3:A
let curClipIndex = 0; // 順番に設定していく
for (
let renderTextureNo = 0;
renderTextureNo < this._renderTextureCount;
renderTextureNo++
) {
for (let channelNo = 0; channelNo < ColorChannelCount; channelNo++) {
// このチャンネルにレイアウトする数
let layoutCount: number = div + (channelNo < mod ? 1 : 0);
// このレンダーテクスチャにまだ割り当てられていなければ追加する
const checkChannelNo = mod + 1 >= ColorChannelCount ? 0 : mod + 1;
if (layoutCount < layoutCountMaxValue && channelNo == checkChannelNo) {
layoutCount += renderTextureNo < countPerSheetMod ? 1 : 0;
}
// 分割方法を決定する
if (layoutCount == 0) {
// 何もしない
} else if (layoutCount == 1) {
// 全てをそのまま使う
const clipContext: T_ClippingContext =
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;
clipContext._bufferIndex = renderTextureNo;
} else if (layoutCount == 2) {
for (let i = 0; i < layoutCount; i++) {
let xpos: number = i % 2;
// 小数点は切り捨てる
xpos = ~~xpos;
const cc: T_ClippingContext = this._clippingContextListForMask.at(
curClipIndex++
);
cc._layoutChannelNo = channelNo;
// UVを2つに分解して使う
cc._layoutBounds.x = xpos * 0.5;
cc._layoutBounds.y = 0.0;
cc._layoutBounds.width = 0.5;
cc._layoutBounds.height = 1.0;
cc._bufferIndex = renderTextureNo;
}
} 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;
cc._bufferIndex = renderTextureNo;
}
} else if (layoutCount <= layoutCountMaxValue) {
// 9分割して使う
for (let i = 0; i < layoutCount; i++) {
let xpos = i % 3;
let ypos = i / 3;
// 小数点は切り捨てる
xpos = ~~xpos;
ypos = ~~ypos;
const cc: T_ClippingContext = 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;
cc._bufferIndex = renderTextureNo;
}
} else {
// マスクの制限枚数を超えた場合の処理
CubismLogError(
'not supported mask count : {0}\n[Details] render texture count : {1}, mask count : {2}',
usingClipCount - useClippingMaskMaxCount,
this._renderTextureCount,
usingClipCount
);
// SetupShaderProgramでオーバーアクセスが発生するので仮で数値を入れる
// もちろん描画結果は正しいものではなくなる
for (let index = 0; index < layoutCount; index++) {
const cc: T_ClippingContext = this._clippingContextListForMask.at(
curClipIndex++
);
cc._layoutChannelNo = 0;
cc._layoutBounds.x = 0.0;
cc._layoutBounds.y = 0.0;
cc._layoutBounds.width = 1.0;
cc._layoutBounds.height = 1.0;
cc._bufferIndex = 0;
}
}
}
}
}
/**
*
* @param model
* @param clippingContext
*/
public calcClippedDrawTotalBounds(
model: CubismModel,
clippingContext: T_ClippingContext
): 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.MAX_VALUE;
let maxY: number = -Number.MAX_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 (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;
}
}
}
/**
* 使
* @return 使
*/
public getClippingContextListForDraw(): csmVector<T_ClippingContext> {
return this._clippingContextListForDraw;
}
/**
*
* @return
*/
public getClippingMaskBufferSize(): number {
return this._clippingMaskBufferSize;
}
/**
*
* @return
*/
public getRenderTextureCount(): number {
return this._renderTextureCount;
}
/**
* RGBA
* @param channelNo RGBA0:R, 1:G, 2:B, 3:A
*/
public getChannelFlagAsColor(channelNo: number): CubismTextureColor {
return this._channelColors.at(channelNo);
}
/**
*
* @param size
*/
public setClippingMaskBufferSize(size: number): void {
this._clippingMaskBufferSize = size;
}
protected _clearedFrameBufferFlags: csmVector<boolean>; //マスクのクリアフラグの配列
protected _channelColors: csmVector<CubismTextureColor>;
protected _clippingContextListForMask: csmVector<T_ClippingContext>; // マスク用クリッピングコンテキストのリスト
protected _clippingContextListForDraw: csmVector<T_ClippingContext>; // 描画用クリッピングコンテキストのリスト
protected _clippingMaskBufferSize: number; // クリッピングマスクのバッファサイズ(初期値:256
protected _renderTextureCount: number; // 生成するレンダーテクスチャの枚数
protected _tmpMatrix: CubismMatrix44; // マスク計算用の行列
protected _tmpMatrixForMask: CubismMatrix44; // マスク計算用の行列
protected _tmpMatrixForDraw: CubismMatrix44; // マスク計算用の行列
protected _tmpBoundsOnModel: csmRect; // マスク配置計算用の矩形
protected _clippingContexttConstructor: ClippingContextConstructor<T_ClippingContext>;
}

View File

@ -7,6 +7,8 @@
import { CubismMatrix44 } from '../math/cubismmatrix44';
import { CubismModel } from '../model/cubismmodel';
import { csmRect } from '../type/csmrectf';
import { ICubismClippingManager } from './cubismclippingmanager';
/**
*
@ -45,7 +47,11 @@ export abstract class CubismRenderer {
public drawModel(): void {
if (this.getModel() == null) return;
this.saveProfile();
this.doDrawModel();
this.restoreProfile();
}
/**
@ -119,6 +125,24 @@ export abstract class CubismRenderer {
return JSON.parse(JSON.stringify(this._modelColor));
}
/**
*
*
* @param opacity
*
* @return RGBA
*/
getModelColorWithOpacity(opacity: number): CubismTextureColor {
const modelColorRGBA: CubismTextureColor = this.getModelColor();
modelColorRGBA.A *= opacity;
if (this.isPremultipliedAlpha()) {
modelColorRGBA.R *= modelColorRGBA.A;
modelColorRGBA.G *= modelColorRGBA.A;
modelColorRGBA.B *= modelColorRGBA.A;
}
return modelColorRGBA;
}
/**
* α
* truefalse
@ -178,6 +202,27 @@ export abstract class CubismRenderer {
return this._model;
}
/**
*
* false1
* 36
* true
*
* @param high
*/
public useHighPrecisionMask(high: boolean): void {
this._useHighPrecisionMask = high;
}
/**
*
* @return true
* @return false
*/
public isUsingHighPrecisionMask(): boolean {
return this._useHighPrecisionMask;
}
/**
*
*/
@ -187,6 +232,7 @@ export abstract class CubismRenderer {
this._anisotropy = 0.0;
this._model = null;
this._modelColor = new CubismTextureColor();
this._useHighPrecisionMask = false;
// 単位行列に初期化
this._mvpMatrix4x4 = new CubismMatrix44();
@ -199,31 +245,14 @@ export abstract class CubismRenderer {
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,
multiplyColor: CubismTextureColor,
screenColor: CubismTextureColor,
opacity: number,
colorBlendMode: CubismBlendMode,
invertedMask: boolean
): void;
protected abstract saveProfile(): void;
/**
*
*/
protected abstract restoreProfile(): void;
/**
*
@ -236,12 +265,13 @@ export abstract class CubismRenderer {
protected _isPremultipliedAlpha: boolean; // 乗算済みαならtrue
protected _anisotropy: any; // テクスチャの異方性フィルタリングのパラメータ
protected _model: CubismModel; // レンダリング対象のモデル
protected _useHighPrecisionMask: boolean; // falseの場合、マスクを纏めて描画する trueの場合、マスクはパーツ描画ごとに書き直す
}
export enum CubismBlendMode {
CubismBlendMode_Normal = 0, // 通常
CubismBlendMode_Additive = 1, // 加算
CubismBlendMode_Multiplicative = 2, // 乗算
CubismBlendMode_Multiplicative = 2 // 乗算
}
/**
@ -251,11 +281,11 @@ export class CubismTextureColor {
/**
*
*/
constructor() {
this.R = 1.0;
this.G = 1.0;
this.B = 1.0;
this.A = 1.0;
constructor(r = 1.0, g = 1.0, b = 1.0, a = 1.0) {
this.R = r;
this.G = g;
this.B = b;
this.A = a;
}
R: number; // 赤チャンネル
@ -264,6 +294,75 @@ export class CubismTextureColor {
A: number; // αチャンネル
}
/**
*
*/
export abstract class CubismClippingContext {
/**
*
*/
public constructor(clippingDrawableIndices: Int32Array, clipCount: number) {
// クリップしているマスク用の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();
this._bufferIndex = 0;
}
/**
*
* @return
*/
public abstract getClippingManager(): ICubismClippingManager;
/**
*
*/
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);
}
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[]; // このマスクにクリップされる描画オブジェクトのリスト
public _bufferIndex: number; // このマスクが割り当てられるレンダーテクスチャ(フレームバッファ)やカラーバッファのインデックス
}
// Namespace definition for compatibility.
import * as $ from './cubismrenderer';
// eslint-disable-next-line @typescript-eslint/no-namespace

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -11,7 +11,7 @@ import {
CSM_LOG_LEVEL_ERROR,
CSM_LOG_LEVEL_INFO,
CSM_LOG_LEVEL_VERBOSE,
CSM_LOG_LEVEL_WARNING,
CSM_LOG_LEVEL_WARNING
} from '../cubismframeworkconfig';
import { CubismFramework, LogLevel } from '../live2dcubismframework';

View File

@ -214,6 +214,8 @@ export abstract class Value {
public static errorValue: Value; // 一時的な返り値として返すエラー。 CubismFramework::Disposeするまではdeleteしない
public static nullValue: Value; // 一時的な返り値として返すNULL。 CubismFramework::Disposeするまではdeleteしない
[key: string]: any; // 明示的に連想配列をany型で指定
}
/**
@ -235,7 +237,7 @@ export class CubismJson {
this._root = null;
if (buffer != undefined) {
this.parseBytes(buffer, length);
this.parseBytes(buffer, length, this._parseCallback);
}
}
@ -248,7 +250,11 @@ export class CubismJson {
*/
public static create(buffer: ArrayBuffer, size: number) {
const json = new CubismJson();
const succeeded: boolean = json.parseBytes(buffer, size);
const succeeded: boolean = json.parseBytes(
buffer,
size,
json._parseCallback
);
if (!succeeded) {
CubismJson.delete(json);
@ -280,7 +286,7 @@ export class CubismJson {
* @param buffer
* @return
*/
public arrayBufferToString(buffer: ArrayBuffer): string {
public static arrayBufferToString(buffer: ArrayBuffer): string {
const uint8Array: Uint8Array = new Uint8Array(buffer);
let str = '';
@ -295,7 +301,7 @@ export class CubismJson {
/**
*
*/
private pad(n: string): string {
private static pad(n: string): string {
return n.length < 2 ? '0' + n : n;
}
@ -306,10 +312,20 @@ export class CubismJson {
* return true :
* return false:
*/
public parseBytes(buffer: ArrayBuffer, size: number): boolean {
public parseBytes(
buffer: ArrayBuffer,
size: number,
parseCallback?: parseJsonObject
): boolean {
const endPos: number[] = new Array<number>(1); // 参照渡しにするため配列
const decodeBuffer: string = this.arrayBufferToString(buffer);
this._root = this.parseValue(decodeBuffer, size, 0, endPos);
const decodeBuffer: string = CubismJson.arrayBufferToString(buffer);
if (parseCallback == undefined) {
this._root = this.parseValue(decodeBuffer, size, 0, endPos);
} else {
// TypeScript標準のJSONパーサを使う
this._root = parseCallback(JSON.parse(decodeBuffer), new JsonMap());
}
if (this._error) {
let strbuf = '\0';
@ -704,11 +720,17 @@ export class CubismJson {
return null;
}
_parseCallback: parseJsonObject = CubismJsonExtension.parseJsonObject; // パース時に使う処理のコールバック関数
_error: string; // パース時のエラー
_lineCount: number; // エラー報告に用いる行数カウント
_root: Value; // パースされたルート要素
}
interface parseJsonObject {
(obj: Value, map: JsonMap): JsonMap;
}
/**
* JSONfloat
*/
@ -1230,6 +1252,7 @@ export class JsonMap extends Value {
// Namespace definition for compatibility.
import * as $ from './cubismjson';
import { CubismJsonExtension } from './cubismjsonextension';
// eslint-disable-next-line @typescript-eslint/no-namespace
export namespace Live2DCubismFramework {
export const CubismJson = $.CubismJson;

View File

@ -0,0 +1,93 @@
/**
* Copyright(c) Live2D Inc. All rights reserved.
*
* Use of this source code is governed by the Live2D Open Software license
* that can be found at https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
*/
import {
JsonArray,
JsonBoolean,
JsonFloat,
JsonMap,
JsonNullvalue,
JsonString,
Value
} from './cubismjson';
/**
* CubismJsonJson使
* TypeScriptJson使
* Cubism SDKJSON
*
*/
export class CubismJsonExtension {
static parseJsonObject(obj: Value, map: JsonMap) {
Object.keys(obj).forEach((key) => {
if (typeof obj[key] == 'boolean') {
const convValue = Boolean(obj[key]);
map.put(key, new JsonBoolean(convValue));
} else if (typeof obj[key] == 'string') {
const convValue = String(obj[key]);
map.put(key, new JsonString(convValue));
} else if (typeof obj[key] == 'number') {
const convValue = Number(obj[key]);
map.put(key, new JsonFloat(convValue));
} else if (obj[key] instanceof Array) {
map.put(key, CubismJsonExtension.parseJsonArray(obj[key]));
} else if (obj[key] instanceof Object) {
map.put(
key,
CubismJsonExtension.parseJsonObject(obj[key], new JsonMap())
);
} else if (obj[key] == null) {
map.put(key, new JsonNullvalue());
} else {
// どれにも当てはまらない場合でも処理する
map.put(key, obj[key]);
}
});
return map;
}
protected static parseJsonArray(obj: Value) {
const arr = new JsonArray();
Object.keys(obj).forEach((key) => {
const convKey = Number(key);
if (typeof convKey == 'number') {
if (typeof obj[key] == 'boolean') {
const convValue = Boolean(obj[key]);
arr.add(new JsonBoolean(convValue));
} else if (typeof obj[key] == 'string') {
const convValue = String(obj[key]);
arr.add(new JsonString(convValue));
} else if (typeof obj[key] == 'number') {
const convValue = Number(obj[key]);
arr.add(new JsonFloat(convValue));
} else if (obj[key] instanceof Array) {
arr.add(this.parseJsonArray(obj[key]));
} else if (obj[key] instanceof Object) {
arr.add(this.parseJsonObject(obj[key], new JsonMap()));
} else if (obj[key] == null) {
arr.add(new JsonNullvalue());
} else {
// どれにも当てはまらない場合でも処理する
arr.add(obj[key]);
}
} else if (obj[key] instanceof Array) {
arr.add(this.parseJsonArray(obj[key]));
} else if (obj[key] instanceof Object) {
arr.add(this.parseJsonObject(obj[key], new JsonMap()));
} else if (obj[key] == null) {
arr.add(new JsonNullvalue());
} else {
const convValue = Array(obj[key]);
// 配列ともObjectとも判定できなかった場合でも処理する
for (let i = 0; i < convValue.length; i++) {
arr.add(convValue[i]);
}
}
});
return arr;
}
}

View File

@ -9,7 +9,9 @@
"declaration": true,
"declarationMap": true,
"sourceMap": true,
"emitDecoratorMetadata": true
"emitDecoratorMetadata": true,
"noImplicitAny": true,
"useUnknownInCatchVariables": true
},
"include": [
"src/**/*.ts",