Update to Cubism 5 SDK for Web R1 beta1

This commit is contained in:
wada
2023-08-17 11:05:59 +09:00
parent 6971f204ce
commit ae7a189f98
34 changed files with 3968 additions and 2626 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,32 @@ 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.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
@ -182,6 +208,7 @@ 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.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

View File

@ -4,7 +4,7 @@
# Cubism Web Framework
Live2D Cubism 4 Editor で出力したモデルをアプリケーションで利用するためのフレームワークです。
Live2D Cubism Editor で出力したモデルをアプリケーションで利用するためのフレームワークです。
モデルを表示、操作するための各種機能を提供します。
モデルをロードするには Live2D Cubism Core ライブラリと組み合わせて使用します。
@ -17,18 +17,27 @@ Live2D Cubism 4 Editor で出力したモデルをアプリケーションで利
本 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.1.0
* 18.16.0
* 16.20.0
* 20.5.1
* 18.17.1
* 16.20.2
### TypeScript
5.0.4
5.1.6
## 開発環境構築

View File

@ -4,7 +4,7 @@
# Cubism Web Framework
This is a framework for using models output by Live2D Cubism 4 Editor in applications.
This is a framework for using models output by Live2D Cubism Editor in applications.
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.
@ -17,17 +17,26 @@ You can use it as a JavaScript library that can be used in the browser by buildi
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
* 20.1.0
* 18.16.0
* 16.20.0
* 20.5.1
* 18.17.1
* 16.20.2
### TypeScript
5.0.4
5.1.6
## Development environment construction

1738
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.59.5",
"@typescript-eslint/parser": "^5.59.5",
"eslint": "^8.40.0",
"eslint-config-prettier": "^8.8.0",
"eslint-plugin-prettier": "^4.2.1",
"prettier": "^2.8.8",
"rimraf": "^5.0.0",
"typescript": "^5.0.4"
"@typescript-eslint/eslint-plugin": "^6.4.0",
"@typescript-eslint/parser": "^6.4.0",
"eslint": "^8.47.0",
"eslint-config-prettier": "^9.0.0",
"eslint-plugin-prettier": "^5.0.0",
"prettier": "^3.0.1",
"rimraf": "^5.0.1",
"typescript": "^5.1.6"
}
}

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 指定したID文字列からCubismIdを取得する際はこのクラスの生成メソッドを呼ばず、
* CubismIdManager().getId(id)を使用してください
*/
export class CubismId {
/**
* 内部で使用するCubismIdクラス生成メソッド
*
* @param id ID文字列
* @returns CubismId
* @note 指定したID文字列からCubismIdを取得する際は
* 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 {
@ -274,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);

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

@ -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';
@ -846,6 +846,18 @@ export class CubismModel {
return this._model.parameters.defaultValues[parameterIndex];
}
/**
* 指定したパラメータindexのIDを取得
*
* @param parameterIndex パラメータのインデックス
* @returns パラメータID
*/
public getParameterId(parameterIndex: number): CubismIdHandle {
return CubismFramework.getIdManager().getId(
this._model.parameters.ids[parameterIndex]
);
}
/**
* パラメータの値の取得
* @param parameterIndex パラメータのインデックス

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';
@ -371,7 +372,7 @@ export class CubismUserModel {
);
// 表情マネージャーを作成
this._expressionManager = new CubismMotionManager();
this._expressionManager = new CubismExpressionMotionManager();
// ドラッグによるアニメーション
this._dragManager = new CubismTargetPoint();
@ -415,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; // モデル行列

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;
}
/**

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ファイルが読み込まれているバッファ
@ -63,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,
@ -71,7 +74,7 @@ export class CubismExpressionMotion extends ACubismMotion {
);
break;
}
case ExpressionBlendType.ExpressionBlendType_Multiply: {
case ExpressionBlendType.Multiply: {
model.multiplyParameterValueById(
parameter.parameterId,
parameter.value,
@ -79,7 +82,7 @@ export class CubismExpressionMotion extends ACubismMotion {
);
break;
}
case ExpressionBlendType.ExpressionBlendType_Overwrite: {
case ExpressionBlendType.Overwrite: {
model.setParameterValueById(
parameter.parameterId,
parameter.value,
@ -94,6 +97,163 @@ export class CubismExpressionMotion extends ACubismMotion {
}
}
/**
* @brief 表情によるモデルのパラメータの計算
*
* モデルの表情に関するパラメータを計算する。
*
* @param[in] model 対象のモデル
* @param[in] userTimeSeconds デルタ時間の積算値[秒]
* @param[in] motionQueueEntry CubismMotionQueueManagerで管理されているモーション
* @param[in] expressionParameterValues モデルに適用する各パラメータの値
* @param[in] expressionIndex 表情のインデックス
*/
public calculateExpressionParameters(
model: CubismModel,
userTimeSeconds: number,
motionQueueEntry: CubismMotionQueueEntry,
expressionParameterValues: csmVector<ExpressionParameterValue>,
expressionIndex: number
) {
if (!motionQueueEntry.isAvailable()) {
return;
}
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;
}
}
}
/**
* @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();
@ -130,20 +290,20 @@ export class CubismExpressionMotion extends ACubismMotion {
param.getValueByString(ExpressionKeyBlend).isNull() ||
param.getValueByString(ExpressionKeyBlend).getString() == BlendValueAdd
) {
blendType = ExpressionBlendType.ExpressionBlendType_Add;
blendType = ExpressionBlendType.Additive;
} else if (
param.getValueByString(ExpressionKeyBlend).getString() ==
BlendValueMultiply
) {
blendType = ExpressionBlendType.ExpressionBlendType_Multiply;
blendType = ExpressionBlendType.Multiply;
} else if (
param.getValueByString(ExpressionKeyBlend).getString() ==
BlendValueOverwrite
) {
blendType = ExpressionBlendType.ExpressionBlendType_Overwrite;
blendType = ExpressionBlendType.Overwrite;
} else {
// その他 仕様にない値を設定した時は加算モードにすることで復旧
blendType = ExpressionBlendType.ExpressionBlendType_Add;
blendType = ExpressionBlendType.Additive;
}
// 設定オブジェクトを作成してリストに追加する
@ -159,25 +319,40 @@ export class CubismExpressionMotion extends ACubismMotion {
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;
}
_parameters: csmVector<ExpressionParameter>; // 表情のパラメータ情報リスト
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 // 上書き
}
/**
@ -191,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';
@ -212,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);

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

@ -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

@ -183,6 +183,18 @@ export class CubismMotionQueueManager {
}
}
/**
* @brief CubismMotionQueueEntryの配列の取得
*
* CubismMotionQueueEntryの配列を取得する。
*
* @return CubismMotionQueueEntryの配列へのポインタ
* @retval NULL 見つからなかった
*/
public getCubismMotionQueueEntries(): csmVector<CubismMotionQueueEntry> {
return this._motions;
}
/**
* 指定したCubismMotionQueueEntryの取得

View File

@ -17,7 +17,7 @@ import {
CubismPhysicsRig,
CubismPhysicsSource,
CubismPhysicsSubRig,
CubismPhysicsTargetType,
CubismPhysicsTargetType
} from './cubismphysicsinternal';
import { CubismPhysicsJson } from './cubismphysicsjson';

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 // 角度から
}
/**

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());
}
/**
* クリッピングコンテキストを配置するレイアウト
* 指定された数のレンダーテクスチャを極力いっぱいに使ってマスクをレイアウトする
* マスクグループの数が4以下ならRGBA各チャンネルに一つずつマスクを配置し、5以上6以下ならRGBAを2,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 カラーチャンネルRGBAの番号0: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';
/**
* モデル描画を処理するレンダラ
@ -123,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;
}
/**
* 乗算済みαの有効・無効をセットする
* 有効にするならtrue、無効にするならfalseをセットする
@ -224,33 +244,6 @@ 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;
/**
* モデル描画直前のレンダラのステートを保持する
*/
@ -278,7 +271,7 @@ export abstract class CubismRenderer {
export enum CubismBlendMode {
CubismBlendMode_Normal = 0, // 通常
CubismBlendMode_Additive = 1, // 加算
CubismBlendMode_Multiplicative = 2, // 乗算
CubismBlendMode_Multiplicative = 2 // 乗算
}
/**
@ -301,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

@ -12,7 +12,7 @@ import {
JsonMap,
JsonNullvalue,
JsonString,
Value,
Value
} from './cubismjson';
/**