phaser4-rex-plugins
Version:
399 lines (325 loc) • 12.2 kB
text/typescript
/**
* 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 { CubismIdHandle } from '../id/cubismid';
import { CubismFramework } from '../live2dcubismframework';
import { CubismModel } from '../model/cubismmodel';
import { csmVector, iterator } from '../type/csmvector';
import { CubismJson, Value } from '../utils/cubismjson';
const Epsilon = 0.001;
const DefaultFadeInSeconds = 0.5;
// Pose.jsonのタグ
const FadeIn = 'FadeInTime';
const Link = 'Link';
const Groups = 'Groups';
const Id = 'Id';
/**
* パーツの不透明度の設定
*
* パーツの不透明度の管理と設定を行う。
*/
export class CubismPose {
/**
* インスタンスの作成
* @param pose3json pose3.jsonのデータ
* @param size pose3.jsonのデータのサイズ[byte]
* @return 作成されたインスタンス
*/
public static create(pose3json: ArrayBuffer, size: number): CubismPose {
const ret: CubismPose = new CubismPose();
const json: CubismJson = CubismJson.create(pose3json, size);
const root: Value = json.getRoot();
// フェード時間の指定
if (!root.getValueByString(FadeIn).isNull()) {
ret._fadeTimeSeconds = root
.getValueByString(FadeIn)
.toFloat(DefaultFadeInSeconds);
if (ret._fadeTimeSeconds <= 0.0) {
ret._fadeTimeSeconds = DefaultFadeInSeconds;
}
}
// パーツグループ
const poseListInfo: Value = root.getValueByString(Groups);
const poseCount: number = poseListInfo.getSize();
for (let poseIndex = 0; poseIndex < poseCount; ++poseIndex) {
const idListInfo: Value = poseListInfo.getValueByIndex(poseIndex);
const idCount: number = idListInfo.getSize();
let groupCount = 0;
for (let groupIndex = 0; groupIndex < idCount; ++groupIndex) {
const partInfo: Value = idListInfo.getValueByIndex(groupIndex);
const partData: PartData = new PartData();
const parameterId: CubismIdHandle =
CubismFramework.getIdManager().getId(
partInfo.getValueByString(Id).getRawString()
);
partData.partId = parameterId;
// リンクするパーツの設定
if (!partInfo.getValueByString(Link).isNull()) {
const linkListInfo: Value = partInfo.getValueByString(Link);
const linkCount: number = linkListInfo.getSize();
for (let linkIndex = 0; linkIndex < linkCount; ++linkIndex) {
const linkPart: PartData = new PartData();
const linkId: CubismIdHandle = CubismFramework.getIdManager().getId(
linkListInfo.getValueByIndex(linkIndex).getString()
);
linkPart.partId = linkId;
partData.link.pushBack(linkPart);
}
}
ret._partGroups.pushBack(partData.clone());
++groupCount;
}
ret._partGroupCounts.pushBack(groupCount);
}
CubismJson.delete(json);
return ret;
}
/**
* インスタンスを破棄する
* @param pose 対象のCubismPose
*/
public static delete(pose: CubismPose): void {
if (pose != null) {
pose = null;
}
}
/**
* モデルのパラメータの更新
* @param model 対象のモデル
* @param deltaTimeSeconds デルタ時間[秒]
*/
public updateParameters(model: CubismModel, deltaTimeSeconds: number): void {
// 前回のモデルと同じでない場合は初期化が必要
if (model != this._lastModel) {
// パラメータインデックスの初期化
this.reset(model);
}
this._lastModel = model;
// 設定から時間を変更すると、経過時間がマイナスになる事があるので、経過時間0として対応
if (deltaTimeSeconds < 0.0) {
deltaTimeSeconds = 0.0;
}
let beginIndex = 0;
for (let i = 0; i < this._partGroupCounts.getSize(); i++) {
const partGroupCount: number = this._partGroupCounts.at(i);
this.doFade(model, deltaTimeSeconds, beginIndex, partGroupCount);
beginIndex += partGroupCount;
}
this.copyPartOpacities(model);
}
/**
* 表示を初期化
* @param model 対象のモデル
* @note 不透明度の初期値が0でないパラメータは、不透明度を1に設定する
*/
public reset(model: CubismModel): void {
let beginIndex = 0;
for (let i = 0; i < this._partGroupCounts.getSize(); ++i) {
const groupCount: number = this._partGroupCounts.at(i);
for (let j: number = beginIndex; j < beginIndex + groupCount; ++j) {
this._partGroups.at(j).initialize(model);
const partsIndex: number = this._partGroups.at(j).partIndex;
const paramIndex: number = this._partGroups.at(j).parameterIndex;
if (partsIndex < 0) {
continue;
}
model.setPartOpacityByIndex(partsIndex, j == beginIndex ? 1.0 : 0.0);
model.setParameterValueByIndex(paramIndex, j == beginIndex ? 1.0 : 0.0);
for (let k = 0; k < this._partGroups.at(j).link.getSize(); ++k) {
this._partGroups.at(j).link.at(k).initialize(model);
}
}
beginIndex += groupCount;
}
}
/**
* パーツの不透明度をコピー
*
* @param model 対象のモデル
*/
public copyPartOpacities(model: CubismModel): void {
for (
let groupIndex = 0;
groupIndex < this._partGroups.getSize();
++groupIndex
) {
const partData: PartData = this._partGroups.at(groupIndex);
if (partData.link.getSize() == 0) {
continue; // 連動するパラメータはない
}
const partIndex: number = this._partGroups.at(groupIndex).partIndex;
const opacity: number = model.getPartOpacityByIndex(partIndex);
for (
let linkIndex = 0;
linkIndex < partData.link.getSize();
++linkIndex
) {
const linkPart: PartData = partData.link.at(linkIndex);
const linkPartIndex: number = linkPart.partIndex;
if (linkPartIndex < 0) {
continue;
}
model.setPartOpacityByIndex(linkPartIndex, opacity);
}
}
}
/**
* パーツのフェード操作を行う。
* @param model 対象のモデル
* @param deltaTimeSeconds デルタ時間[秒]
* @param beginIndex フェード操作を行うパーツグループの先頭インデックス
* @param partGroupCount フェード操作を行うパーツグループの個数
*/
public doFade(
model: CubismModel,
deltaTimeSeconds: number,
beginIndex: number,
partGroupCount: number
): void {
let visiblePartIndex = -1;
let newOpacity = 1.0;
const phi = 0.5;
const backOpacityThreshold = 0.15;
// 現在、表示状態になっているパーツを取得
for (let i: number = beginIndex; i < beginIndex + partGroupCount; ++i) {
const partIndex: number = this._partGroups.at(i).partIndex;
const paramIndex: number = this._partGroups.at(i).parameterIndex;
if (model.getParameterValueByIndex(paramIndex) > Epsilon) {
if (visiblePartIndex >= 0) {
break;
}
visiblePartIndex = i;
newOpacity = model.getPartOpacityByIndex(partIndex);
// 新しい不透明度を計算
newOpacity += deltaTimeSeconds / this._fadeTimeSeconds;
if (newOpacity > 1.0) {
newOpacity = 1.0;
}
}
}
if (visiblePartIndex < 0) {
visiblePartIndex = 0;
newOpacity = 1.0;
}
// 表示パーツ、非表示パーツの不透明度を設定する
for (let i: number = beginIndex; i < beginIndex + partGroupCount; ++i) {
const partsIndex: number = this._partGroups.at(i).partIndex;
// 表示パーツの設定
if (visiblePartIndex == i) {
model.setPartOpacityByIndex(partsIndex, newOpacity); // 先に設定
}
// 非表示パーツの設定
else {
let opacity: number = model.getPartOpacityByIndex(partsIndex);
let a1: number; // 計算によって求められる不透明度
if (newOpacity < phi) {
a1 = (newOpacity * (phi - 1)) / phi + 1.0; // (0,1),(phi,phi)を通る直線式
} else {
a1 = ((1 - newOpacity) * phi) / (1.0 - phi); // (1,0),(phi,phi)を通る直線式
}
// 背景の見える割合を制限する場合
const backOpacity: number = (1.0 - a1) * (1.0 - newOpacity);
if (backOpacity > backOpacityThreshold) {
a1 = 1.0 - backOpacityThreshold / (1.0 - newOpacity);
}
if (opacity > a1) {
opacity = a1; // 計算の不透明度よりも大きければ(濃ければ)不透明度を上げる
}
model.setPartOpacityByIndex(partsIndex, opacity);
}
}
}
/**
* コンストラクタ
*/
public constructor() {
this._fadeTimeSeconds = DefaultFadeInSeconds;
this._lastModel = null;
this._partGroups = new csmVector<PartData>();
this._partGroupCounts = new csmVector<number>();
}
_partGroups: csmVector<PartData>; // パーツグループ
_partGroupCounts: csmVector<number>; // それぞれのパーツグループの個数
_fadeTimeSeconds: number; // フェード時間[秒]
_lastModel: CubismModel; // 前回操作したモデル
}
/**
* パーツにまつわるデータを管理
*/
export class PartData {
/**
* コンストラクタ
*/
constructor(v?: PartData) {
this.parameterIndex = 0;
this.partIndex = 0;
this.link = new csmVector<PartData>();
if (v != undefined) {
this.partId = v.partId;
for (
const ite: iterator<PartData> = v.link.begin();
ite.notEqual(v.link.end());
ite.preIncrement()
) {
this.link.pushBack(ite.ptr().clone());
}
}
}
/**
* =演算子のオーバーロード
*/
public assignment(v: PartData): PartData {
this.partId = v.partId;
for (
const ite: iterator<PartData> = v.link.begin();
ite.notEqual(v.link.end());
ite.preIncrement()
) {
this.link.pushBack(ite.ptr().clone());
}
return this;
}
/**
* 初期化
* @param model 初期化に使用するモデル
*/
public initialize(model: CubismModel): void {
this.parameterIndex = model.getParameterIndex(this.partId);
this.partIndex = model.getPartIndex(this.partId);
model.setParameterValueByIndex(this.parameterIndex, 1);
}
/**
* オブジェクトのコピーを生成する
*/
public clone(): PartData {
const clonePartData: PartData = new PartData();
clonePartData.partId = this.partId;
clonePartData.parameterIndex = this.parameterIndex;
clonePartData.partIndex = this.partIndex;
clonePartData.link = new csmVector<PartData>();
for (
let ite: iterator<PartData> = this.link.begin();
ite.notEqual(this.link.end());
ite.increment()
) {
clonePartData.link.pushBack(ite.ptr().clone());
}
return clonePartData;
}
partId: CubismIdHandle; // パーツID
parameterIndex: number; // パラメータのインデックス
partIndex: number; // パーツのインデックス
link: csmVector<PartData>; // 連動するパラメータ
}
// Namespace definition for compatibility.
import * as $ from './cubismpose';
// eslint-disable-next-line @typescript-eslint/no-namespace
export namespace Live2DCubismFramework {
export const CubismPose = $.CubismPose;
export type CubismPose = $.CubismPose;
export const PartData = $.PartData;
export type PartData = $.PartData;
}