Update to Cubism 4 SDK for Web R1

This commit is contained in:
Jun Koyama
2020-01-30 18:28:13 +09:00
parent ce2585a919
commit 3b711b8a80
54 changed files with 16247 additions and 0 deletions

278
src/motion/acubismmotion.ts Normal file
View File

@ -0,0 +1,278 @@
/**
* 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 { Live2DCubismFramework as cubismmath } from '../math/cubismmath';
import { Live2DCubismFramework as cubismmodel } from '../model/cubismmodel';
import { Live2DCubismFramework as cubismmotionqueueentry } from './cubismmotionqueueentry';
import { Live2DCubismFramework as csmstring } from '../type/csmstring';
import { Live2DCubismFramework as csmvector } from '../type/csmvector';
import { CSM_ASSERT } from '../utils/cubismdebug';
import csmVector = csmvector.csmVector;
import csmString = csmstring.csmString;
import CubismMotionQueueEntry = cubismmotionqueueentry.CubismMotionQueueEntry;
import CubismModel = cubismmodel.CubismModel;
import CubismMath = cubismmath.CubismMath;
export namespace Live2DCubismFramework {
/** モーション再生終了コールバック関数定義 */
export type FinishedMotionCallback = (self: ACubismMotion) => void;
/**
* モーションの抽象基底クラス
*
* モーションの抽象基底クラス。MotionQueueManagerによってモーションの再生を管理する。
*/
export abstract class ACubismMotion {
/**
* インスタンスの破棄
*/
public static delete(motion: ACubismMotion): void {
motion.release();
motion = void 0;
motion = null;
}
/**
* コンストラクタ
*/
public constructor() {
this._fadeInSeconds = -1.0;
this._fadeOutSeconds = -1.0;
this._weight = 1.0;
this._offsetSeconds = 0.0; // 再生の開始時刻
this._firedEventValues = new csmVector<csmString>();
}
/**
* デストラクタ相当の処理
*/
public release(): void {
this._weight = 0.0;
}
/**
* モデルのパラメータ
* @param model 対象のモデル
* @param motionQueueEntry CubismMotionQueueManagerで管理されているモーション
* @param userTimeSeconds デルタ時間の積算値[秒]
*/
public updateParameters(
model: CubismModel,
motionQueueEntry: CubismMotionQueueEntry,
userTimeSeconds: number
): void {
if (!motionQueueEntry.isAvailable() || motionQueueEntry.isFinished()) {
return;
}
if (!motionQueueEntry.isStarted()) {
motionQueueEntry.setIsStarted(true);
motionQueueEntry.setStartTime(userTimeSeconds - this._offsetSeconds); // モーションの開始時刻を記録
motionQueueEntry.setFadeInStartTime(userTimeSeconds); // フェードインの開始時刻
const duration: number = this.getDuration();
if (motionQueueEntry.getEndTime() < 0) {
// 開始していないうちに終了設定している場合がある。
motionQueueEntry.setEndTime(
duration <= 0 ? -1 : motionQueueEntry.getStartTime() + duration
);
// duration == -1 の場合はループする
}
}
let fadeWeight: number = this._weight; // 現在の値と掛け合わせる割合
//---- フェードイン・アウトの処理 ----
// 単純なサイン関数でイージングする
const fadeIn: number =
this._fadeInSeconds == 0.0
? 1.0
: CubismMath.getEasingSine(
(userTimeSeconds - motionQueueEntry.getFadeInStartTime()) /
this._fadeInSeconds
);
const fadeOut: number =
this._fadeOutSeconds == 0.0 || motionQueueEntry.getEndTime() < 0.0
? 1.0
: CubismMath.getEasingSine(
(motionQueueEntry.getEndTime() - userTimeSeconds) /
this._fadeOutSeconds
);
fadeWeight = fadeWeight * fadeIn * fadeOut;
motionQueueEntry.setState(userTimeSeconds, fadeWeight);
CSM_ASSERT(0.0 <= fadeWeight && fadeWeight <= 1.0);
//---- 全てのパラメータIDをループする ----
this.doUpdateParameters(
model,
userTimeSeconds,
fadeWeight,
motionQueueEntry
);
// 後処理
// 終了時刻を過ぎたら終了フラグを立てる(CubismMotionQueueManager)
if (
motionQueueEntry.getEndTime() > 0 &&
motionQueueEntry.getEndTime() < userTimeSeconds
) {
motionQueueEntry.setIsFinished(true); // 終了
}
}
/**
* フェードインの時間を設定する
* @param fadeInSeconds フェードインにかかる時間[秒]
*/
public setFadeInTime(fadeInSeconds: number): void {
this._fadeInSeconds = fadeInSeconds;
}
/**
* フェードアウトの時間を設定する
* @param fadeOutSeconds フェードアウトにかかる時間[秒]
*/
public setFadeOutTime(fadeOutSeconds: number): void {
this._fadeOutSeconds = fadeOutSeconds;
}
/**
* フェードアウトにかかる時間の取得
* @return フェードアウトにかかる時間[秒]
*/
public getFadeOutTime(): number {
return this._fadeOutSeconds;
}
/**
* フェードインにかかる時間の取得
* @return フェードインにかかる時間[秒]
*/
public getFadeInTime(): number {
return this._fadeInSeconds;
}
/**
* モーション適用の重みの設定
* @param weight 重み0.0 - 1.0
*/
public setWeight(weight: number): void {
this._weight = weight;
}
/**
* モーション適用の重みの取得
* @return 重み0.0 - 1.0
*/
public getWeight(): number {
return this._weight;
}
/**
* モーションの長さの取得
* @return モーションの長さ[秒]
*
* @note ループの時は「-1」。
* ループでない場合は、オーバーライドする。
* 正の値の時は取得される時間で終了する。
* 「-1」の時は外部から停止命令がない限り終わらない処理となる。
*/
public getDuration(): number {
return -1.0;
}
/**
* モーションのループ1回分の長さの取得
* @return モーションのループ一回分の長さ[秒]
*
* @note ループしない場合は、getDuration()と同じ値を返す
* ループ一回分の長さが定義できない場合(プログラム的に動き続けるサブクラスなど)の場合は「-1」を返す
*/
public getLoopDuration(): number {
return -1.0;
}
/**
* モーション再生の開始時刻の設定
* @param offsetSeconds モーション再生の開始時刻[秒]
*/
public setOffsetTime(offsetSeconds: number): void {
this._offsetSeconds = offsetSeconds;
}
/**
* モデルのパラメータ更新
*
* イベント発火のチェック。
* 入力する時間は呼ばれるモーションタイミングを0とした秒数で行う。
*
* @param beforeCheckTimeSeconds 前回のイベントチェック時間[秒]
* @param motionTimeSeconds 今回の再生時間[秒]
*/
public getFiredEvent(
beforeCheckTimeSeconds: number,
motionTimeSeconds: number
): csmVector<csmString> {
return this._firedEventValues;
}
/**
* モーションを更新して、モデルにパラメータ値を反映する
* @param model 対象のモデル
* @param userTimeSeconds デルタ時間の積算値[秒]
* @param weight モーションの重み
* @param motionQueueEntry CubismMotionQueueManagerで管理されているモーション
* @return true モデルへパラメータ値の反映あり
* @return false モデルへのパラメータ値の反映なし(モーションの変化なし)
*/
public abstract doUpdateParameters(
model: CubismModel,
userTimeSeconds: number,
weight: number,
motionQueueEntry: CubismMotionQueueEntry
): void;
/**
* モーション再生終了コールバックの登録
*
* モーション再生終了コールバックを登録する。
* isFinishedフラグを設定するタイミングで呼び出される。
* 以下の状態の際には呼び出されない:
* 1. 再生中のモーションが「ループ」として設定されているとき
* 2. コールバックが登録されていない時
*
* @param onFinishedMotionHandler モーション再生終了コールバック関数
*/
public setFinishedMotionHandler = (
onFinishedMotionHandler: FinishedMotionCallback
) => (this._onFinishedMotion = onFinishedMotionHandler);
/**
* モーション再生終了コールバックの取得
*
* モーション再生終了コールバックを取得する。
*
* @return 登録されているモーション再生終了コールバック関数
*/
public getFinishedMotionHandler = () => this._onFinishedMotion;
public _fadeInSeconds: number; // フェードインにかかる時間[秒]
public _fadeOutSeconds: number; // フェードアウトにかかる時間[秒]
public _weight: number; // モーションの重み
public _offsetSeconds: number; // モーション再生の開始時間[秒]
public _firedEventValues: csmVector<csmString>;
// モーション再生終了コールバック関数
public _onFinishedMotion?: FinishedMotionCallback;
}
}

View File

@ -0,0 +1,199 @@
/**
* 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 { Live2DCubismFramework as acubismmotion } from './acubismmotion';
import { Live2DCubismFramework as cubismjson } from '../utils/cubismjson';
import { Live2DCubismFramework as cubismid } from '../id/cubismid';
import { Live2DCubismFramework as cubismframework } from '../live2dcubismframework';
import { Live2DCubismFramework as cubismmodel } from '../model/cubismmodel';
import { Live2DCubismFramework as cubismmotionqueueentry } from './cubismmotionqueueentry';
import { Live2DCubismFramework as csmvector } from '../type/csmvector';
import JsonFloat = cubismjson.JsonFloat;
import csmVector = csmvector.csmVector;
import CubismMotionQueueEntry = cubismmotionqueueentry.CubismMotionQueueEntry;
import CubismModel = cubismmodel.CubismModel;
import CubismFramework = cubismframework.CubismFramework;
import CubismIdHandle = cubismid.CubismIdHandle;
import CubismJson = cubismjson.CubismJson;
import Value = cubismjson.Value;
import ACubismMotion = acubismmotion.ACubismMotion;
export namespace Live2DCubismFramework {
// exp3.jsonのキーとデフォルト
const ExpressionKeyFadeIn = 'FadeInTime';
const ExpressionKeyFadeOut = 'FadeOutTime';
const ExpressionKeyParameters = 'Parameters';
const ExpressionKeyId = 'Id';
const ExpressionKeyValue = 'Value';
const ExpressionKeyBlend = 'Blend';
const BlendValueAdd = 'Add';
const BlendValueMultiply = 'Multiply';
const BlendValueOverwrite = 'Overwrite';
const DefaultFadeTime = 1.0;
/**
* 表情のモーション
*
* 表情のモーションクラス。
*/
export class CubismExpressionMotion extends ACubismMotion {
/**
* インスタンスを作成する。
* @param buffer expファイルが読み込まれているバッファ
* @param size バッファのサイズ
* @return 作成されたインスタンス
*/
public static create(
buffer: ArrayBuffer,
size: number
): CubismExpressionMotion {
const expression: CubismExpressionMotion = new CubismExpressionMotion();
const json: CubismJson = CubismJson.create(buffer, size);
const root: Value = json.getRoot();
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データは不要になったら削除する
return expression;
}
/**
* モデルのパラメータの更新の実行
* @param model 対象のモデル
* @param userTimeSeconds デルタ時間の積算値[秒]
* @param weight モーションの重み
* @param motionQueueEntry CubismMotionQueueManagerで管理されているモーション
*/
public doUpdateParameters(
model: CubismModel,
userTimeSeconds: number,
weight: number,
motionQueueEntry: CubismMotionQueueEntry
): void {
for (let i = 0; i < this._parameters.getSize(); ++i) {
const parameter: ExpressionParameter = this._parameters.at(i);
switch (parameter.blendType) {
case ExpressionBlendType.ExpressionBlendType_Add: {
model.addParameterValueById(
parameter.parameterId,
parameter.value,
weight
);
break;
}
case ExpressionBlendType.ExpressionBlendType_Multiply: {
model.multiplyParameterValueById(
parameter.parameterId,
parameter.value,
weight
);
break;
}
case ExpressionBlendType.ExpressionBlendType_Overwrite: {
model.setParameterValueById(
parameter.parameterId,
parameter.value,
weight
);
break;
}
default:
// 仕様にない値を設定した時はすでに加算モードになっている
break;
}
}
}
/**
* コンストラクタ
*/
constructor() {
super();
this._parameters = new csmVector<ExpressionParameter>();
}
_parameters: csmVector<ExpressionParameter>; // 表情のパラメータ情報リスト
}
/**
* 表情パラメータ値の計算方式
*/
export enum ExpressionBlendType {
ExpressionBlendType_Add = 0, // 加算
ExpressionBlendType_Multiply = 1, // 乗算
ExpressionBlendType_Overwrite = 2 // 上書き
}
/**
* 表情のパラメータ情報
*/
export class ExpressionParameter {
parameterId: CubismIdHandle; // パラメータID
blendType: ExpressionBlendType; // パラメータの演算種類
value: number; // 値
}
}

945
src/motion/cubismmotion.ts Normal file
View File

@ -0,0 +1,945 @@
/**
* 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 { Live2DCubismFramework as cubismmotionjson } from './cubismmotionjson';
import { Live2DCubismFramework as cubismmotioninternal } from './cubismmotioninternal';
import { Live2DCubismFramework as acubismmotion } from './acubismmotion';
import { Live2DCubismFramework as cubismmodel } from '../model/cubismmodel';
import { Live2DCubismFramework as cubismframework } from '../live2dcubismframework';
import { Live2DCubismFramework as cubismmotionqueueentry } from './cubismmotionqueueentry';
import { Live2DCubismFramework as cubismmath } from '../math/cubismmath';
import { Live2DCubismFramework as csmvector } from '../type/csmvector';
import { Live2DCubismFramework as cubismid } from '../id/cubismid';
import { Live2DCubismFramework as csmstring } from '../type/csmstring';
import { CubismLogDebug, CSM_ASSERT } from '../utils/cubismdebug';
import csmString = csmstring.csmString;
import CubismMotionData = cubismmotioninternal.CubismMotionData;
import CubismMotionSegment = cubismmotioninternal.CubismMotionSegment;
import CubismMotionPoint = cubismmotioninternal.CubismMotionPoint;
import CubismMotionEvent = cubismmotioninternal.CubismMotionEvent;
import CubismMotionSegmentType = cubismmotioninternal.CubismMotionSegmentType;
import CubismIdHandle = cubismid.CubismIdHandle;
import CubismMotionCurve = cubismmotioninternal.CubismMotionCurve;
import CubismMotionCurveTarget = cubismmotioninternal.CubismMotionCurveTarget;
import csmVector = csmvector.csmVector;
import CubismMath = cubismmath.CubismMath;
import CubismMotionQueueEntry = cubismmotionqueueentry.CubismMotionQueueEntry;
import CubismFramework = cubismframework.CubismFramework;
import CubismModel = cubismmodel.CubismModel;
import ACubismMotion = acubismmotion.ACubismMotion;
import FinishedMotionCallback = acubismmotion.FinishedMotionCallback;
import CubismMotionJson = cubismmotionjson.CubismMotionJson;
export namespace Live2DCubismFramework {
const EffectNameEyeBlink = 'EyeBlink';
const EffectNameLipSync = 'LipSync';
const TargetNameModel = 'Model';
const TargetNameParameter = 'Parameter';
const TargetNamePartOpacity = 'PartOpacity';
function lerpPoints(
a: CubismMotionPoint,
b: CubismMotionPoint,
t: number
): CubismMotionPoint {
const result: CubismMotionPoint = new CubismMotionPoint();
result.time = a.time + (b.time - a.time) * t;
result.value = a.value + (b.value - a.value) * t;
return result;
}
function linearEvaluate(points: CubismMotionPoint[], time: number): number {
let t: number = (time - points[0].time) / (points[1].time - points[0].time);
if (t < 0.0) {
t = 0.0;
}
return points[0].value + (points[1].value - points[0].value) * t;
}
function bezierEvaluate(points: CubismMotionPoint[], time: number): number {
let t: number = (time - points[0].time) / (points[3].time - points[0].time);
if (t < 0.0) {
t = 0.0;
}
const p01: CubismMotionPoint = lerpPoints(points[0], points[1], t);
const p12: CubismMotionPoint = lerpPoints(points[1], points[2], t);
const p23: CubismMotionPoint = lerpPoints(points[2], points[3], t);
const p012: CubismMotionPoint = lerpPoints(p01, p12, t);
const p123: CubismMotionPoint = lerpPoints(p12, p23, t);
return lerpPoints(p012, p123, t).value;
}
function steppedEvaluate(points: CubismMotionPoint[], time: number): number {
return points[0].value;
}
function inverseSteppedEvaluate(
points: CubismMotionPoint[],
time: number
): number {
return points[1].value;
}
function evaluateCurve(
motionData: CubismMotionData,
index: number,
time: number
): number {
// Find segment to evaluate.
const curve: CubismMotionCurve = motionData.curves.at(index);
let target = -1;
const totalSegmentCount: number =
curve.baseSegmentIndex + curve.segmentCount;
let pointPosition = 0;
for (let i: number = curve.baseSegmentIndex; i < totalSegmentCount; ++i) {
// Get first point of next segment.
pointPosition =
motionData.segments.at(i).basePointIndex +
(motionData.segments.at(i).segmentType ==
CubismMotionSegmentType.CubismMotionSegmentType_Bezier
? 3
: 1);
// Break if time lies within current segment.
if (motionData.points.at(pointPosition).time > time) {
target = i;
break;
}
}
if (target == -1) {
return motionData.points.at(pointPosition).value;
}
const segment: CubismMotionSegment = motionData.segments.at(target);
return segment.evaluate(
motionData.points.get(segment.basePointIndex),
time
);
}
/**
* モーションクラス
*
* モーションのクラス。
*/
export class CubismMotion extends ACubismMotion {
/**
* インスタンスを作成する
*
* @param buffer motion3.jsonが読み込まれているバッファ
* @param size バッファのサイズ
* @param onFinishedMotionHandler モーション再生終了時に呼び出されるコールバック関数
* @return 作成されたインスタンス
*/
public static create(
buffer: ArrayBuffer,
size: number,
onFinishedMotionHandler?: FinishedMotionCallback
): CubismMotion {
const ret = new CubismMotion();
ret.parse(buffer, size);
ret._sourceFrameRate = ret._motionData.fps;
ret._loopDurationSeconds = ret._motionData.duration;
ret._onFinishedMotion = onFinishedMotionHandler;
// NOTE: Editorではループありのモーション書き出しは非対応
// ret->_loop = (ret->_motionData->Loop > 0);
return ret;
}
/**
* モデルのパラメータの更新の実行
* @param model 対象のモデル
* @param userTimeSeconds 現在の時刻[秒]
* @param fadeWeight モーションの重み
* @param motionQueueEntry CubismMotionQueueManagerで管理されているモーション
*/
public doUpdateParameters(
model: CubismModel,
userTimeSeconds: number,
fadeWeight: number,
motionQueueEntry: CubismMotionQueueEntry
): void {
if (this._modelCurveIdEyeBlink == null) {
this._modelCurveIdEyeBlink = CubismFramework.getIdManager().getId(
EffectNameEyeBlink
);
}
if (this._modelCurveIdLipSync == null) {
this._modelCurveIdLipSync = CubismFramework.getIdManager().getId(
EffectNameLipSync
);
}
let timeOffsetSeconds: number =
userTimeSeconds - motionQueueEntry.getStartTime();
if (timeOffsetSeconds < 0.0) {
timeOffsetSeconds = 0.0; // エラー回避
}
let lipSyncValue: number = Number.MAX_VALUE;
let eyeBlinkValue: number = Number.MAX_VALUE;
//まばたき、リップシンクのうちモーションの適用を検出するためのビットmaxFlagCount個まで
const MaxTargetSize = 64;
let lipSyncFlags = 0;
let eyeBlinkFlags = 0;
//瞬き、リップシンクのターゲット数が上限を超えている場合
if (this._eyeBlinkParameterIds.getSize() > MaxTargetSize) {
CubismLogDebug(
'too many eye blink targets : {0}',
this._eyeBlinkParameterIds.getSize()
);
}
if (this._lipSyncParameterIds.getSize() > MaxTargetSize) {
CubismLogDebug(
'too many lip sync targets : {0}',
this._lipSyncParameterIds.getSize()
);
}
const tmpFadeIn: number =
this._fadeInSeconds <= 0.0
? 1.0
: CubismMath.getEasingSine(
(userTimeSeconds - motionQueueEntry.getFadeInStartTime()) /
this._fadeInSeconds
);
const tmpFadeOut: number =
this._fadeOutSeconds <= 0.0 || motionQueueEntry.getEndTime() < 0.0
? 1.0
: CubismMath.getEasingSine(
(motionQueueEntry.getEndTime() - userTimeSeconds) /
this._fadeOutSeconds
);
let value: number;
let c: number, parameterIndex: number;
// 'Repeat' time as necessary.
let time: number = timeOffsetSeconds;
if (this._isLoop) {
while (time > this._motionData.duration) {
time -= this._motionData.duration;
}
}
const curves: csmVector<CubismMotionCurve> = this._motionData.curves;
// Evaluate model curves.
for (
c = 0;
c < this._motionData.curveCount &&
curves.at(c).type ==
CubismMotionCurveTarget.CubismMotionCurveTarget_Model;
++c
) {
// Evaluate curve and call handler.
value = evaluateCurve(this._motionData, c, time);
if (curves.at(c).id == this._modelCurveIdEyeBlink) {
eyeBlinkValue = value;
} else if (curves.at(c).id == this._modelCurveIdLipSync) {
lipSyncValue = value;
}
}
let parameterMotionCurveCount = 0;
for (
;
c < this._motionData.curveCount &&
curves.at(c).type ==
CubismMotionCurveTarget.CubismMotionCurveTarget_Parameter;
++c
) {
parameterMotionCurveCount++;
// Find parameter index.
parameterIndex = model.getParameterIndex(curves.at(c).id);
// Skip curve evaluation if no value in sink.
if (parameterIndex == -1) {
continue;
}
const sourceValue: number = model.getParameterValueByIndex(
parameterIndex
);
// Evaluate curve and apply value.
value = evaluateCurve(this._motionData, c, time);
if (eyeBlinkValue != Number.MAX_VALUE) {
for (
let i = 0;
i < this._eyeBlinkParameterIds.getSize() && i < MaxTargetSize;
++i
) {
if (this._eyeBlinkParameterIds.at(i) == curves.at(c).id) {
value *= eyeBlinkValue;
eyeBlinkFlags |= 1 << i;
break;
}
}
}
if (lipSyncValue != Number.MAX_VALUE) {
for (
let i = 0;
i < this._lipSyncParameterIds.getSize() && i < MaxTargetSize;
++i
) {
if (this._lipSyncParameterIds.at(i) == curves.at(c).id) {
value += lipSyncValue;
lipSyncFlags |= 1 << i;
break;
}
}
}
let v: number;
// パラメータごとのフェード
if (curves.at(c).fadeInTime < 0.0 && curves.at(c).fadeOutTime < 0.0) {
// モーションのフェードを適用
v = sourceValue + (value - sourceValue) * fadeWeight;
} else {
// パラメータに対してフェードインかフェードアウトが設定してある場合はそちらを適用
let fin: number;
let fout: number;
if (curves.at(c).fadeInTime < 0.0) {
fin = tmpFadeIn;
} else {
fin =
curves.at(c).fadeInTime == 0.0
? 1.0
: CubismMath.getEasingSine(
(userTimeSeconds - motionQueueEntry.getFadeInStartTime()) /
curves.at(c).fadeInTime
);
}
if (curves.at(c).fadeOutTime < 0.0) {
fout = tmpFadeOut;
} else {
fout =
curves.at(c).fadeOutTime == 0.0 ||
motionQueueEntry.getEndTime() < 0.0
? 1.0
: CubismMath.getEasingSine(
(motionQueueEntry.getEndTime() - userTimeSeconds) /
curves.at(c).fadeOutTime
);
}
const paramWeight: number = this._weight * fin * fout;
// パラメータごとのフェードを適用
v = sourceValue + (value - sourceValue) * paramWeight;
}
model.setParameterValueByIndex(parameterIndex, v, 1.0);
}
{
if (eyeBlinkValue != Number.MAX_VALUE) {
for (
let i = 0;
i < this._eyeBlinkParameterIds.getSize() && i < MaxTargetSize;
++i
) {
const sourceValue: number = model.getParameterValueById(
this._eyeBlinkParameterIds.at(i)
);
// モーションでの上書きがあった時にはまばたきは適用しない
if ((eyeBlinkFlags >> i) & 0x01) {
continue;
}
const v: number =
sourceValue + (eyeBlinkValue - sourceValue) * fadeWeight;
model.setParameterValueById(this._eyeBlinkParameterIds.at(i), v);
}
}
if (lipSyncValue != Number.MAX_VALUE) {
for (
let i = 0;
i < this._lipSyncParameterIds.getSize() && i < MaxTargetSize;
++i
) {
const sourceValue: number = model.getParameterValueById(
this._lipSyncParameterIds.at(i)
);
// モーションでの上書きがあった時にはリップシンクは適用しない
if ((lipSyncFlags >> i) & 0x01) {
continue;
}
const v: number =
sourceValue + (lipSyncValue - sourceValue) * fadeWeight;
model.setParameterValueById(this._lipSyncParameterIds.at(i), v);
}
}
}
for (
;
c < this._motionData.curveCount &&
curves.at(c).type ==
CubismMotionCurveTarget.CubismMotionCurveTarget_PartOpacity;
++c
) {
// Find parameter index.
parameterIndex = model.getParameterIndex(curves.at(c).id);
// Skip curve evaluation if no value in sink.
if (parameterIndex == -1) {
continue;
}
// Evaluate curve and apply value.
value = evaluateCurve(this._motionData, c, time);
model.setParameterValueByIndex(parameterIndex, value);
}
if (timeOffsetSeconds >= this._motionData.duration) {
if (this._isLoop) {
motionQueueEntry.setStartTime(userTimeSeconds); // 最初の状態へ
if (this._isLoopFadeIn) {
// ループ内でループ用フェードインが有効の時は、フェードイン設定し直し
motionQueueEntry.setFadeInStartTime(userTimeSeconds);
}
} else {
if (this._onFinishedMotion) {
this._onFinishedMotion(this);
}
motionQueueEntry.setIsFinished(true);
}
}
this._lastWeight = fadeWeight;
}
/**
* ループ情報の設定
* @param loop ループ情報
*/
public setIsLoop(loop: boolean): void {
this._isLoop = loop;
}
/**
* ループ情報の取得
* @return true ループする
* @return false ループしない
*/
public isLoop(): boolean {
return this._isLoop;
}
/**
* ループ時のフェードイン情報の設定
* @param loopFadeIn ループ時のフェードイン情報
*/
public setIsLoopFadeIn(loopFadeIn: boolean): void {
this._isLoopFadeIn = loopFadeIn;
}
/**
* ループ時のフェードイン情報の取得
*
* @return true する
* @return false しない
*/
public isLoopFadeIn(): boolean {
return this._isLoopFadeIn;
}
/**
* モーションの長さを取得する。
*
* @return モーションの長さ[秒]
*/
public getDuration(): number {
return this._isLoop ? -1.0 : this._loopDurationSeconds;
}
/**
* モーションのループ時の長さを取得する。
*
* @return モーションのループ時の長さ[秒]
*/
public getLoopDuration(): number {
return this._loopDurationSeconds;
}
/**
* パラメータに対するフェードインの時間を設定する。
*
* @param parameterId パラメータID
* @param value フェードインにかかる時間[秒]
*/
public setParameterFadeInTime(
parameterId: CubismIdHandle,
value: number
): void {
const curves: csmVector<CubismMotionCurve> = this._motionData.curves;
for (let i = 0; i < this._motionData.curveCount; ++i) {
if (parameterId == curves.at(i).id) {
curves.at(i).fadeInTime = value;
return;
}
}
}
/**
* パラメータに対するフェードアウトの時間の設定
* @param parameterId パラメータID
* @param value フェードアウトにかかる時間[秒]
*/
public setParameterFadeOutTime(
parameterId: CubismIdHandle,
value: number
): void {
const curves: csmVector<CubismMotionCurve> = this._motionData.curves;
for (let i = 0; i < this._motionData.curveCount; ++i) {
if (parameterId == curves.at(i).id) {
curves.at(i).fadeOutTime = value;
return;
}
}
}
/**
* パラメータに対するフェードインの時間の取得
* @param parameterId パラメータID
* @return フェードインにかかる時間[秒]
*/
public getParameterFadeInTime(parameterId: CubismIdHandle): number {
const curves: csmVector<CubismMotionCurve> = this._motionData.curves;
for (let i = 0; i < this._motionData.curveCount; ++i) {
if (parameterId == curves.at(i).id) {
return curves.at(i).fadeInTime;
}
}
return -1;
}
/**
* パラメータに対するフェードアウトの時間を取得
*
* @param parameterId パラメータID
* @return フェードアウトにかかる時間[秒]
*/
public getParameterFadeOutTime(parameterId: CubismIdHandle): number {
const curves: csmVector<CubismMotionCurve> = this._motionData.curves;
for (let i = 0; i < this._motionData.curveCount; ++i) {
if (parameterId == curves.at(i).id) {
return curves.at(i).fadeOutTime;
}
}
return -1;
}
/**
* 自動エフェクトがかかっているパラメータIDリストの設定
* @param eyeBlinkParameterIds 自動まばたきがかかっているパラメータIDのリスト
* @param lipSyncParameterIds リップシンクがかかっているパラメータIDのリスト
*/
public setEffectIds(
eyeBlinkParameterIds: csmVector<CubismIdHandle>,
lipSyncParameterIds: csmVector<CubismIdHandle>
): void {
this._eyeBlinkParameterIds = eyeBlinkParameterIds;
this._lipSyncParameterIds = lipSyncParameterIds;
}
/**
* コンストラクタ
*/
public constructor() {
super();
this._sourceFrameRate = 30.0;
this._loopDurationSeconds = -1.0;
this._isLoop = false; // trueから false へデフォルトを変更
this._isLoopFadeIn = true; // ループ時にフェードインが有効かどうかのフラグ
this._lastWeight = 0.0;
this._motionData = null;
this._modelCurveIdEyeBlink = null;
this._modelCurveIdLipSync = null;
this._eyeBlinkParameterIds = null;
this._lipSyncParameterIds = null;
}
/**
* デストラクタ相当の処理
*/
public release(): void {
this._motionData = void 0;
this._motionData = null;
}
/**
* motion3.jsonをパースする。
*
* @param motionJson motion3.jsonが読み込まれているバッファ
* @param size バッファのサイズ
*/
public parse(motionJson: ArrayBuffer, size: number): void {
this._motionData = new CubismMotionData();
let json: CubismMotionJson = new CubismMotionJson(motionJson, size);
this._motionData.duration = json.getMotionDuration();
this._motionData.loop = json.isMotionLoop();
this._motionData.curveCount = json.getMotionCurveCount();
this._motionData.fps = json.getMotionFps();
this._motionData.eventCount = json.getEventCount();
if (json.isExistMotionFadeInTime()) {
this._fadeInSeconds =
json.getMotionFadeInTime() < 0.0 ? 1.0 : json.getMotionFadeInTime();
} else {
this._fadeInSeconds = 1.0;
}
if (json.isExistMotionFadeOutTime()) {
this._fadeOutSeconds =
json.getMotionFadeOutTime() < 0.0 ? 1.0 : json.getMotionFadeOutTime();
} else {
this._fadeOutSeconds = 1.0;
}
this._motionData.curves.updateSize(
this._motionData.curveCount,
CubismMotionCurve,
true
);
this._motionData.segments.updateSize(
json.getMotionTotalSegmentCount(),
CubismMotionSegment,
true
);
this._motionData.points.updateSize(
json.getMotionTotalPointCount(),
CubismMotionPoint,
true
);
this._motionData.events.updateSize(
this._motionData.eventCount,
CubismMotionEvent,
true
);
let totalPointCount = 0;
let totalSegmentCount = 0;
// Curves
for (
let curveCount = 0;
curveCount < this._motionData.curveCount;
++curveCount
) {
if (json.getMotionCurveTarget(curveCount) == TargetNameModel) {
this._motionData.curves.at(curveCount).type =
CubismMotionCurveTarget.CubismMotionCurveTarget_Model;
} else if (
json.getMotionCurveTarget(curveCount) == TargetNameParameter
) {
this._motionData.curves.at(curveCount).type =
CubismMotionCurveTarget.CubismMotionCurveTarget_Parameter;
} else if (
json.getMotionCurveTarget(curveCount) == TargetNamePartOpacity
) {
this._motionData.curves.at(curveCount).type =
CubismMotionCurveTarget.CubismMotionCurveTarget_PartOpacity;
}
this._motionData.curves.at(curveCount).id = json.getMotionCurveId(
curveCount
);
this._motionData.curves.at(
curveCount
).baseSegmentIndex = totalSegmentCount;
this._motionData.curves.at(
curveCount
).fadeInTime = json.isExistMotionCurveFadeInTime(curveCount)
? json.getMotionCurveFadeInTime(curveCount)
: -1.0;
this._motionData.curves.at(
curveCount
).fadeOutTime = json.isExistMotionCurveFadeOutTime(curveCount)
? json.getMotionCurveFadeOutTime(curveCount)
: -1.0;
// Segments
for (
let segmentPosition = 0;
segmentPosition < json.getMotionCurveSegmentCount(curveCount);
) {
if (segmentPosition == 0) {
this._motionData.segments.at(
totalSegmentCount
).basePointIndex = totalPointCount;
this._motionData.points.at(
totalPointCount
).time = json.getMotionCurveSegment(curveCount, segmentPosition);
this._motionData.points.at(
totalPointCount
).value = json.getMotionCurveSegment(
curveCount,
segmentPosition + 1
);
totalPointCount += 1;
segmentPosition += 2;
} else {
this._motionData.segments.at(totalSegmentCount).basePointIndex =
totalPointCount - 1;
}
const segment: number = json.getMotionCurveSegment(
curveCount,
segmentPosition
);
switch (segment) {
case CubismMotionSegmentType.CubismMotionSegmentType_Linear: {
this._motionData.segments.at(totalSegmentCount).segmentType =
CubismMotionSegmentType.CubismMotionSegmentType_Linear;
this._motionData.segments.at(
totalSegmentCount
).evaluate = linearEvaluate;
this._motionData.points.at(
totalPointCount
).time = json.getMotionCurveSegment(
curveCount,
segmentPosition + 1
);
this._motionData.points.at(
totalPointCount
).value = json.getMotionCurveSegment(
curveCount,
segmentPosition + 2
);
totalPointCount += 1;
segmentPosition += 3;
break;
}
case CubismMotionSegmentType.CubismMotionSegmentType_Bezier: {
this._motionData.segments.at(totalSegmentCount).segmentType =
CubismMotionSegmentType.CubismMotionSegmentType_Bezier;
this._motionData.segments.at(
totalSegmentCount
).evaluate = bezierEvaluate;
this._motionData.points.at(
totalPointCount
).time = json.getMotionCurveSegment(
curveCount,
segmentPosition + 1
);
this._motionData.points.at(
totalPointCount
).value = json.getMotionCurveSegment(
curveCount,
segmentPosition + 2
);
this._motionData.points.at(
totalPointCount + 1
).time = json.getMotionCurveSegment(
curveCount,
segmentPosition + 3
);
this._motionData.points.at(
totalPointCount + 1
).value = json.getMotionCurveSegment(
curveCount,
segmentPosition + 4
);
this._motionData.points.at(
totalPointCount + 2
).time = json.getMotionCurveSegment(
curveCount,
segmentPosition + 5
);
this._motionData.points.at(
totalPointCount + 2
).value = json.getMotionCurveSegment(
curveCount,
segmentPosition + 6
);
totalPointCount += 3;
segmentPosition += 7;
break;
}
case CubismMotionSegmentType.CubismMotionSegmentType_Stepped: {
this._motionData.segments.at(totalSegmentCount).segmentType =
CubismMotionSegmentType.CubismMotionSegmentType_Stepped;
this._motionData.segments.at(
totalSegmentCount
).evaluate = steppedEvaluate;
this._motionData.points.at(
totalPointCount
).time = json.getMotionCurveSegment(
curveCount,
segmentPosition + 1
);
this._motionData.points.at(
totalPointCount
).value = json.getMotionCurveSegment(
curveCount,
segmentPosition + 2
);
totalPointCount += 1;
segmentPosition += 3;
break;
}
case CubismMotionSegmentType.CubismMotionSegmentType_InverseStepped: {
this._motionData.segments.at(totalSegmentCount).segmentType =
CubismMotionSegmentType.CubismMotionSegmentType_InverseStepped;
this._motionData.segments.at(
totalSegmentCount
).evaluate = inverseSteppedEvaluate;
this._motionData.points.at(
totalPointCount
).time = json.getMotionCurveSegment(
curveCount,
segmentPosition + 1
);
this._motionData.points.at(
totalPointCount
).value = json.getMotionCurveSegment(
curveCount,
segmentPosition + 2
);
totalPointCount += 1;
segmentPosition += 3;
break;
}
default: {
CSM_ASSERT(0);
break;
}
}
++this._motionData.curves.at(curveCount).segmentCount;
++totalSegmentCount;
}
}
for (
let userdatacount = 0;
userdatacount < json.getEventCount();
++userdatacount
) {
this._motionData.events.at(userdatacount).fireTime = json.getEventTime(
userdatacount
);
this._motionData.events.at(userdatacount).value = json.getEventValue(
userdatacount
);
}
json.release();
json = void 0;
json = null;
}
/**
* モデルのパラメータ更新
*
* イベント発火のチェック。
* 入力する時間は呼ばれるモーションタイミングを0とした秒数で行う。
*
* @param beforeCheckTimeSeconds 前回のイベントチェック時間[秒]
* @param motionTimeSeconds 今回の再生時間[秒]
*/
public getFiredEvent(
beforeCheckTimeSeconds: number,
motionTimeSeconds: number
): csmVector<csmString> {
this._firedEventValues.updateSize(0);
// イベントの発火チェック
for (let u = 0; u < this._motionData.eventCount; ++u) {
if (
this._motionData.events.at(u).fireTime > beforeCheckTimeSeconds &&
this._motionData.events.at(u).fireTime <= motionTimeSeconds
) {
this._firedEventValues.pushBack(
new csmString(this._motionData.events.at(u).value.s)
);
}
}
return this._firedEventValues;
}
public _sourceFrameRate: number; // ロードしたファイルのFPS。記述が無ければデフォルト値15fpsとなる
public _loopDurationSeconds: number; // mtnファイルで定義される一連のモーションの長さ
public _isLoop: boolean; // ループするか?
public _isLoopFadeIn: boolean; // ループ時にフェードインが有効かどうかのフラグ。初期値では有効。
public _lastWeight: number; // 最後に設定された重み
public _motionData: CubismMotionData; // 実際のモーションデータ本体
public _eyeBlinkParameterIds: csmVector<CubismIdHandle>; // 自動まばたきを適用するパラメータIDハンドルのリスト。 モデル(モデルセッティング)とパラメータを対応付ける。
public _lipSyncParameterIds: csmVector<CubismIdHandle>; // リップシンクを適用するパラメータIDハンドルのリスト。 モデル(モデルセッティング)とパラメータを対応付ける。
public _modelCurveIdEyeBlink: CubismIdHandle; // モデルが持つ自動まばたき用パラメータIDのハンドル。 モデルとモーションを対応付ける。
public _modelCurveIdLipSync: CubismIdHandle; // モデルが持つリップシンク用パラメータIDのハンドル。 モデルとモーションを対応付ける。
}
}

View File

@ -0,0 +1,140 @@
/**
* Copyright(c) Live2D Inc. All rights reserved.
*
* Use of this source code is governed by the Live2D Open Software license
* that can be found at https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
*/
import { Live2DCubismFramework as cubismid } from '../id/cubismid';
import { Live2DCubismFramework as csmstring } from '../type/csmstring';
import { Live2DCubismFramework as csmvector } from '../type/csmvector';
import csmVector = csmvector.csmVector;
import csmString = csmstring.csmString;
import CubismIdHandle = cubismid.CubismIdHandle;
export namespace Live2DCubismFramework {
/**
* @brief モーションカーブの種類
*
* モーションカーブの種類。
*/
export enum CubismMotionCurveTarget {
CubismMotionCurveTarget_Model, // モデルに対して
CubismMotionCurveTarget_Parameter, // パラメータに対して
CubismMotionCurveTarget_PartOpacity // パーツの不透明度に対して
}
/**
* @brief モーションカーブのセグメントの種類
*
* モーションカーブのセグメントの種類。
*/
export enum CubismMotionSegmentType {
CubismMotionSegmentType_Linear = 0, // リニア
CubismMotionSegmentType_Bezier = 1, // ベジェ曲線
CubismMotionSegmentType_Stepped = 2, // ステップ
CubismMotionSegmentType_InverseStepped = 3 // インバースステップ
}
/**
* @brief モーションカーブの制御点
*
* モーションカーブの制御点。
*/
export class CubismMotionPoint {
time = 0.0; // 時間[秒]
value = 0.0; // 値
}
/**
* モーションカーブのセグメントの評価関数
*
* @param points モーションカーブの制御点リスト
* @param time 評価する時間[秒]
*/
export interface csmMotionSegmentEvaluationFunction {
(points: CubismMotionPoint[], time: number): number;
}
/**
* @brief モーションカーブのセグメント
*
* モーションカーブのセグメント。
*/
export class CubismMotionSegment {
/**
* @brief コンストラクタ
*
* コンストラクタ。
*/
public constructor() {
this.evaluate = null;
this.basePointIndex = 0;
this.segmentType = 0;
}
evaluate: csmMotionSegmentEvaluationFunction; // 使用する評価関数
basePointIndex: number; // 最初のセグメントへのインデックス
segmentType: number; // セグメントの種類
}
/**
* @brief モーションカーブ
*
* モーションカーブ。
*/
export class CubismMotionCurve {
public constructor() {
this.type = CubismMotionCurveTarget.CubismMotionCurveTarget_Model;
this.segmentCount = 0;
this.baseSegmentIndex = 0;
this.fadeInTime = 0.0;
this.fadeOutTime = 0.0;
}
type: CubismMotionCurveTarget; // カーブの種類
id: CubismIdHandle; // カーブのID
segmentCount: number; // セグメントの個数
baseSegmentIndex: number; // 最初のセグメントのインデックス
fadeInTime: number; // フェードインにかかる時間[秒]
fadeOutTime: number; // フェードアウトにかかる時間[秒]
}
/**
* イベント。
*/
export class CubismMotionEvent {
fireTime = 0.0;
value: csmString;
}
/**
* @brief モーションデータ
*
* モーションデータ。
*/
export class CubismMotionData {
public constructor() {
this.duration = 0.0;
this.loop = false;
this.curveCount = 0;
this.eventCount = 0;
this.fps = 0.0;
this.curves = new csmVector<CubismMotionCurve>();
this.segments = new csmVector<CubismMotionSegment>();
this.points = new csmVector<CubismMotionPoint>();
this.events = new csmVector<CubismMotionEvent>();
}
duration: number; // モーションの長さ[秒]
loop: boolean; // ループするかどうか
curveCount: number; // カーブの個数
eventCount: number; // UserDataの個数
fps: number; // フレームレート
curves: csmVector<CubismMotionCurve>; // カーブのリスト
segments: csmVector<CubismMotionSegment>; // セグメントのリスト
points: csmVector<CubismMotionPoint>; // ポイントのリスト
events: csmVector<CubismMotionEvent>; // イベントのリスト
}
}

View File

@ -0,0 +1,359 @@
/**
* 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 { Live2DCubismFramework as cubismjson } from '../utils/cubismjson';
import { Live2DCubismFramework as cubismid } from '../id/cubismid';
import { Live2DCubismFramework as cubismframework } from '../live2dcubismframework';
import { Live2DCubismFramework as csmstring } from '../type/csmstring';
import csmString = csmstring.csmString;
import CubismFramework = cubismframework.CubismFramework;
import CubismIdHandle = cubismid.CubismIdHandle;
import CubismJson = cubismjson.CubismJson;
export namespace Live2DCubismFramework {
// JSON keys
const Meta = 'Meta';
const Duration = 'Duration';
const Loop = 'Loop';
const CurveCount = 'CurveCount';
const Fps = 'Fps';
const TotalSegmentCount = 'TotalSegmentCount';
const TotalPointCount = 'TotalPointCount';
const Curves = 'Curves';
const Target = 'Target';
const Id = 'Id';
const FadeInTime = 'FadeInTime';
const FadeOutTime = 'FadeOutTime';
const Segments = 'Segments';
const UserData = 'UserData';
const UserDataCount = 'UserDataCount';
const TotalUserDataSize = 'TotalUserDataSize';
const Time = 'Time';
const Value = 'Value';
/**
* motion3.jsonのコンテナ。
*/
export class CubismMotionJson {
/**
* コンストラクタ
* @param buffer motion3.jsonが読み込まれているバッファ
* @param size バッファのサイズ
*/
public constructor(buffer: ArrayBuffer, size: number) {
this._json = CubismJson.create(buffer, size);
}
/**
* デストラクタ相当の処理
*/
public release(): void {
CubismJson.delete(this._json);
}
/**
* モーションの長さを取得する
* @return モーションの長さ[秒]
*/
public getMotionDuration(): number {
return this._json
.getRoot()
.getValueByString(Meta)
.getValueByString(Duration)
.toFloat();
}
/**
* モーションのループ情報の取得
* @return true ループする
* @return false ループしない
*/
public isMotionLoop(): boolean {
return this._json
.getRoot()
.getValueByString(Meta)
.getValueByString(Loop)
.toBoolean();
}
/**
* モーションカーブの個数の取得
* @return モーションカーブの個数
*/
public getMotionCurveCount(): number {
return this._json
.getRoot()
.getValueByString(Meta)
.getValueByString(CurveCount)
.toInt();
}
/**
* モーションのフレームレートの取得
* @return フレームレート[FPS]
*/
public getMotionFps(): number {
return this._json
.getRoot()
.getValueByString(Meta)
.getValueByString(Fps)
.toFloat();
}
/**
* モーションのセグメントの総合計の取得
* @return モーションのセグメントの取得
*/
public getMotionTotalSegmentCount(): number {
return this._json
.getRoot()
.getValueByString(Meta)
.getValueByString(TotalSegmentCount)
.toInt();
}
/**
* モーションのカーブの制御店の総合計の取得
* @return モーションのカーブの制御点の総合計
*/
public getMotionTotalPointCount(): number {
return this._json
.getRoot()
.getValueByString(Meta)
.getValueByString(TotalPointCount)
.toInt();
}
/**
* モーションのフェードイン時間の存在
* @return true 存在する
* @return false 存在しない
*/
public isExistMotionFadeInTime(): boolean {
return !this._json
.getRoot()
.getValueByString(Meta)
.getValueByString(FadeInTime)
.isNull();
}
/**
* モーションのフェードアウト時間の存在
* @return true 存在する
* @return false 存在しない
*/
public isExistMotionFadeOutTime(): boolean {
return !this._json
.getRoot()
.getValueByString(Meta)
.getValueByString(FadeOutTime)
.isNull();
}
/**
* モーションのフェードイン時間の取得
* @return フェードイン時間[秒]
*/
public getMotionFadeInTime(): number {
return this._json
.getRoot()
.getValueByString(Meta)
.getValueByString(FadeInTime)
.toFloat();
}
/**
* モーションのフェードアウト時間の取得
* @return フェードアウト時間[秒]
*/
public getMotionFadeOutTime(): number {
return this._json
.getRoot()
.getValueByString(Meta)
.getValueByString(FadeOutTime)
.toFloat();
}
/**
* モーションのカーブの種類の取得
* @param curveIndex カーブのインデックス
* @return カーブの種類
*/
public getMotionCurveTarget(curveIndex: number): string {
return this._json
.getRoot()
.getValueByString(Curves)
.getValueByIndex(curveIndex)
.getValueByString(Target)
.getRawString();
}
/**
* モーションのカーブのIDの取得
* @param curveIndex カーブのインデックス
* @return カーブのID
*/
public getMotionCurveId(curveIndex: number): CubismIdHandle {
return CubismFramework.getIdManager().getId(
this._json
.getRoot()
.getValueByString(Curves)
.getValueByIndex(curveIndex)
.getValueByString(Id)
.getRawString()
);
}
/**
* モーションのカーブのフェードイン時間の存在
* @param curveIndex カーブのインデックス
* @return true 存在する
* @return false 存在しない
*/
public isExistMotionCurveFadeInTime(curveIndex: number): boolean {
return !this._json
.getRoot()
.getValueByString(Curves)
.getValueByIndex(curveIndex)
.getValueByString(FadeInTime)
.isNull();
}
/**
* モーションのカーブのフェードアウト時間の存在
* @param curveIndex カーブのインデックス
* @return true 存在する
* @return false 存在しない
*/
public isExistMotionCurveFadeOutTime(curveIndex: number): boolean {
return !this._json
.getRoot()
.getValueByString(Curves)
.getValueByIndex(curveIndex)
.getValueByString(FadeOutTime)
.isNull();
}
/**
* モーションのカーブのフェードイン時間の取得
* @param curveIndex カーブのインデックス
* @return フェードイン時間[秒]
*/
public getMotionCurveFadeInTime(curveIndex: number): number {
return this._json
.getRoot()
.getValueByString(Curves)
.getValueByIndex(curveIndex)
.getValueByString(FadeInTime)
.toFloat();
}
/**
* モーションのカーブのフェードアウト時間の取得
* @param curveIndex カーブのインデックス
* @return フェードアウト時間[秒]
*/
public getMotionCurveFadeOutTime(curveIndex: number): number {
return this._json
.getRoot()
.getValueByString(Curves)
.getValueByIndex(curveIndex)
.getValueByString(FadeOutTime)
.toFloat();
}
/**
* モーションのカーブのセグメントの個数を取得する
* @param curveIndex カーブのインデックス
* @return モーションのカーブのセグメントの個数
*/
public getMotionCurveSegmentCount(curveIndex: number): number {
return this._json
.getRoot()
.getValueByString(Curves)
.getValueByIndex(curveIndex)
.getValueByString(Segments)
.getVector()
.getSize();
}
/**
* モーションのカーブのセグメントの値の取得
* @param curveIndex カーブのインデックス
* @param segmentIndex セグメントのインデックス
* @return セグメントの値
*/
public getMotionCurveSegment(
curveIndex: number,
segmentIndex: number
): number {
return this._json
.getRoot()
.getValueByString(Curves)
.getValueByIndex(curveIndex)
.getValueByString(Segments)
.getValueByIndex(segmentIndex)
.toFloat();
}
/**
* イベントの個数の取得
* @return イベントの個数
*/
public getEventCount(): number {
return this._json
.getRoot()
.getValueByString(Meta)
.getValueByString(UserDataCount)
.toInt();
}
/**
* イベントの総文字数の取得
* @return イベントの総文字数
*/
public getTotalEventValueSize(): number {
return this._json
.getRoot()
.getValueByString(Meta)
.getValueByString(TotalUserDataSize)
.toInt();
}
/**
* イベントの時間の取得
* @param userDataIndex イベントのインデックス
* @return イベントの時間[秒]
*/
public getEventTime(userDataIndex: number): number {
return this._json
.getRoot()
.getValueByString(UserData)
.getValueByIndex(userDataIndex)
.getValueByString(Time)
.toInt();
}
/**
* イベントの取得
* @param userDataIndex イベントのインデックス
* @return イベントの文字列
*/
public getEventValue(userDataIndex: number): csmString {
return new csmString(
this._json
.getRoot()
.getValueByString(UserData)
.getValueByIndex(userDataIndex)
.getValueByString(Value)
.getRawString()
);
}
_json: CubismJson; // motion3.jsonのデータ
}
}

View File

@ -0,0 +1,124 @@
/**
* 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 { Live2DCubismFramework as cubismmotionqueuemanager } from './cubismmotionqueuemanager';
import { Live2DCubismFramework as acubismmotion } from './acubismmotion';
import { Live2DCubismFramework as cubismmodel } from '../model/cubismmodel';
import CubismMotionQueueEntryHandle = cubismmotionqueuemanager.CubismMotionQueueEntryHandle;
import CubismModel = cubismmodel.CubismModel;
import ACubismMotion = acubismmotion.ACubismMotion;
import CubismMotionQueueManager = cubismmotionqueuemanager.CubismMotionQueueManager;
export namespace Live2DCubismFramework {
/**
* モーションの管理
*
* モーションの管理を行うクラス
*/
export class CubismMotionManager extends CubismMotionQueueManager {
/**
* コンストラクタ
*/
public constructor() {
super();
this._currentPriority = 0;
this._reservePriority = 0;
}
/**
* 再生中のモーションの優先度の取得
* @return モーションの優先度
*/
public getCurrentPriority(): number {
return this._currentPriority;
}
/**
* 予約中のモーションの優先度を取得する。
* @return モーションの優先度
*/
public getReservePriority(): number {
return this._reservePriority;
}
/**
* 予約中のモーションの優先度を設定する。
* @param val 優先度
*/
public setReservePriority(val: number): void {
this._reservePriority = val;
}
/**
* 優先度を設定してモーションを開始する。
*
* @param motion モーション
* @param autoDelete 再生が狩猟したモーションのインスタンスを削除するならtrue
* @param priority 優先度
* @return 開始したモーションの識別番号を返す。個別のモーションが終了したか否かを判定するIsFinished()の引数で使用する。開始できない時は「-1」
*/
public startMotionPriority(
motion: ACubismMotion,
autoDelete: boolean,
priority: number
): CubismMotionQueueEntryHandle {
if (priority == this._reservePriority) {
this._reservePriority = 0; // 予約を解除
}
this._currentPriority = priority; // 再生中モーションの優先度を設定
return super.startMotion(motion, autoDelete, this._userTimeSeconds);
}
/**
* モーションを更新して、モデルにパラメータ値を反映する。
*
* @param model 対象のモデル
* @param deltaTimeSeconds デルタ時間[秒]
* @return true 更新されている
* @return false 更新されていない
*/
public updateMotion(model: CubismModel, deltaTimeSeconds: number): boolean {
this._userTimeSeconds += deltaTimeSeconds;
const updated: boolean = super.doUpdateMotion(
model,
this._userTimeSeconds
);
if (this.isFinished()) {
this._currentPriority = 0; // 再生中のモーションの優先度を解除
}
return updated;
}
/**
* モーションを予約する。
*
* @param priority 優先度
* @return true 予約できた
* @return false 予約できなかった
*/
public reserveMotion(priority: number): boolean {
if (
priority <= this._reservePriority ||
priority <= this._currentPriority
) {
return false;
}
this._reservePriority = priority;
return true;
}
_currentPriority: number; // 現在再生中のモーションの優先度
_reservePriority: number; // 再生予定のモーションの優先度。再生中は0になる。モーションファイルを別スレッドで読み込むときの機能。
}
}

View File

@ -0,0 +1,219 @@
/**
* 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 { Live2DCubismFramework as acubismmotion } from './acubismmotion';
import { Live2DCubismFramework as cubismmotionqueuemanager } from './cubismmotionqueuemanager';
import CubismMotionQueueEntryHandle = cubismmotionqueuemanager.CubismMotionQueueEntryHandle;
import ACubismMotion = acubismmotion.ACubismMotion;
export namespace Live2DCubismFramework {
/**
* CubismMotionQueueManagerで再生している各モーションの管理クラス。
*/
export class CubismMotionQueueEntry {
/**
* コンストラクタ
*/
public constructor() {
this._autoDelete = false;
this._motion = null;
this._available = true;
this._finished = false;
this._started = false;
this._startTimeSeconds = -1.0;
this._fadeInStartTimeSeconds = 0.0;
this._endTimeSeconds = -1.0;
this._stateTimeSeconds = 0.0;
this._stateWeight = 0.0;
this._lastEventCheckSeconds = 0.0;
this._motionQueueEntryHandle = this;
}
/**
* デストラクタ相当の処理
*/
public release(): void {
if (this._autoDelete && this._motion) {
ACubismMotion.delete(this._motion); //
}
}
/**
* フェードアウトの開始
* @param fadeOutSeconds フェードアウトにかかる時間[秒]
* @param userTimeSeconds デルタ時間の積算値[秒]
*/
public startFadeout(fadeoutSeconds: number, userTimeSeconds: number): void {
const newEndTimeSeconds: number = userTimeSeconds + fadeoutSeconds;
if (
this._endTimeSeconds < 0.0 ||
newEndTimeSeconds < this._endTimeSeconds
) {
this._endTimeSeconds = newEndTimeSeconds;
}
}
/**
* モーションの終了の確認
*
* @return true モーションが終了した
* @return false 終了していない
*/
public isFinished(): boolean {
return this._finished;
}
/**
* モーションの開始の確認
* @return true モーションが開始した
* @return false 開始していない
*/
public isStarted(): boolean {
return this._started;
}
/**
* モーションの開始時刻の取得
* @return モーションの開始時刻[秒]
*/
public getStartTime(): number {
return this._startTimeSeconds;
}
/**
* フェードインの開始時刻の取得
* @return フェードインの開始時刻[秒]
*/
public getFadeInStartTime(): number {
return this._fadeInStartTimeSeconds;
}
/**
* フェードインの終了時刻の取得
* @return フェードインの終了時刻の取得
*/
public getEndTime(): number {
return this._endTimeSeconds;
}
/**
* モーションの開始時刻の設定
* @param startTime モーションの開始時刻
*/
public setStartTime(startTime: number): void {
this._startTimeSeconds = startTime;
}
/**
* フェードインの開始時刻の設定
* @param startTime フェードインの開始時刻[秒]
*/
public setFadeInStartTime(startTime: number): void {
this._fadeInStartTimeSeconds = startTime;
}
/**
* フェードインの終了時刻の設定
* @param endTime フェードインの終了時刻[秒]
*/
public setEndTime(endTime: number): void {
this._endTimeSeconds = endTime;
}
/**
* モーションの終了の設定
* @param f trueならモーションの終了
*/
public setIsFinished(f: boolean): void {
this._finished = f;
}
/**
* モーション開始の設定
* @param f trueならモーションの開始
*/
public setIsStarted(f: boolean): void {
this._started = f;
}
/**
* モーションの有効性の確認
* @return true モーションは有効
* @return false モーションは無効
*/
public isAvailable(): boolean {
return this._available;
}
/**
* モーションの有効性の設定
* @param v trueならモーションは有効
*/
public setIsAvailable(v: boolean): void {
this._available = v;
}
/**
* モーションの状態の設定
* @param timeSeconds 現在時刻[秒]
* @param weight モーション尾重み
*/
public setState(timeSeconds: number, weight: number): void {
this._stateTimeSeconds = timeSeconds;
this._stateWeight = weight;
}
/**
* モーションの現在時刻の取得
* @return モーションの現在時刻[秒]
*/
public getStateTime(): number {
return this._stateTimeSeconds;
}
/**
* モーションの重みの取得
* @return モーションの重み
*/
public getStateWeight(): number {
return this._stateWeight;
}
/**
* 最後にイベントの発火をチェックした時間を取得
*
* @return 最後にイベントの発火をチェックした時間[秒]
*/
public getLastCheckEventTime(): number {
return this._lastEventCheckSeconds;
}
/**
* 最後にイベントをチェックした時間を設定
* @param checkTime 最後にイベントをチェックした時間[秒]
*/
public setLastCheckEventTime(checkTime: number): void {
this._lastEventCheckSeconds = checkTime;
}
_autoDelete: boolean; // 自動削除
_motion: ACubismMotion; // モーション
_available: boolean; // 有効化フラグ
_finished: boolean; // 終了フラグ
_started: boolean; // 開始フラグ
_startTimeSeconds: number; // モーション再生開始時刻[秒]
_fadeInStartTimeSeconds: number; // フェードイン開始時刻(ループの時は初回のみ)[秒]
_endTimeSeconds: number; // 終了予定時刻[秒]
_stateTimeSeconds: number; // 時刻の状態[秒]
_stateWeight: number; // 重みの状態
_lastEventCheckSeconds: number; // 最終のMotion側のチェックした時間
_motionQueueEntryHandle: CubismMotionQueueEntryHandle; // インスタンスごとに一意の値を持つ識別番号
}
}

View File

@ -0,0 +1,347 @@
/**
* 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 { Live2DCubismFramework as acubismmotion } from './acubismmotion';
import { Live2DCubismFramework as cubismmotionqueueentry } from './cubismmotionqueueentry';
import { Live2DCubismFramework as csmvector } from '../type/csmvector';
import { Live2DCubismFramework as cubismmodel } from '../model/cubismmodel';
import { Live2DCubismFramework as csmstring } from '../type/csmstring';
import csmString = csmstring.csmString;
import CubismModel = cubismmodel.CubismModel;
import csmVector = csmvector.csmVector;
import iterator = csmvector.iterator;
import CubismMotionQueueEntry = cubismmotionqueueentry.CubismMotionQueueEntry;
import ACubismMotion = acubismmotion.ACubismMotion;
export namespace Live2DCubismFramework {
/**
* モーション再生の管理
*
* モーション再生の管理用クラス。CubismMotionモーションなどACubismMotionのサブクラスを再生するために使用する。
*
* @note 再生中に別のモーションが StartMotion()された場合は、新しいモーションに滑らかに変化し旧モーションは中断する。
* 表情用モーション、体用モーションなどを分けてモーション化した場合など、
* 複数のモーションを同時に再生させる場合は、複数のCubismMotionQueueManagerインスタンスを使用する。
*/
export class CubismMotionQueueManager {
/**
* コンストラクタ
*/
public constructor() {
this._userTimeSeconds = 0.0;
this._eventCallBack = null;
this._eventCustomData = null;
this._motions = new csmVector<CubismMotionQueueEntry>();
}
/**
* デストラクタ
*/
public release(): void {
for (let i = 0; i < this._motions.getSize(); ++i) {
if (this._motions.at(i)) {
this._motions.at(i).release();
this._motions.set(i, void 0);
this._motions.set(i, null);
}
}
this._motions = null;
}
/**
* 指定したモーションの開始
*
* 指定したモーションを開始する。同じタイプのモーションが既にある場合は、既存のモーションに終了フラグを立て、フェードアウトを開始させる。
*
* @param motion 開始するモーション
* @param autoDelete 再生が終了したモーションのインスタンスを削除するなら true
* @param userTimeSeconds デルタ時間の積算値[秒]
* @return 開始したモーションの識別番号を返す。個別のモーションが終了したか否かを判定するIsFinished()の引数で使用する。開始できない時は「-1」
*/
public startMotion(
motion: ACubismMotion,
autoDelete: boolean,
userTimeSeconds: number
): CubismMotionQueueEntryHandle {
if (motion == null) {
return InvalidMotionQueueEntryHandleValue;
}
let motionQueueEntry: CubismMotionQueueEntry = null;
// 既にモーションがあれば終了フラグを立てる
for (let i = 0; i < this._motions.getSize(); ++i) {
motionQueueEntry = this._motions.at(i);
if (motionQueueEntry == null) {
continue;
}
motionQueueEntry.startFadeout(
motionQueueEntry._motion.getFadeOutTime(),
userTimeSeconds
); // フェードアウトを開始し終了する
}
motionQueueEntry = new CubismMotionQueueEntry(); // 終了時に破棄する
motionQueueEntry._autoDelete = autoDelete;
motionQueueEntry._motion = motion;
this._motions.pushBack(motionQueueEntry);
return motionQueueEntry._motionQueueEntryHandle;
}
/**
* 全てのモーションの終了の確認
* @return true 全て終了している
* @return false 終了していない
*/
public isFinished(): boolean {
// ------- 処理を行う -------
// 既にモーションがあれば終了フラグを立てる
for (
let ite: iterator<CubismMotionQueueEntry> = this._motions.begin();
ite.notEqual(this._motions.end());
) {
let motionQueueEntry: CubismMotionQueueEntry = ite.ptr();
if (motionQueueEntry == null) {
ite = this._motions.erase(ite); // 削除
continue;
}
const motion: ACubismMotion = motionQueueEntry._motion;
if (motion == null) {
motionQueueEntry.release();
motionQueueEntry = void 0;
motionQueueEntry = null;
ite = this._motions.erase(ite); // 削除
continue;
}
// ----- 終了済みの処理があれば削除する ------
if (!motionQueueEntry.isFinished()) {
return false;
} else {
ite.preIncrement();
}
}
return true;
}
/**
* 指定したモーションの終了の確認
* @param motionQueueEntryNumber モーションの識別番号
* @return true 全て終了している
* @return false 終了していない
*/
public isFinishedByHandle(
motionQueueEntryNumber: CubismMotionQueueEntryHandle
): boolean {
// 既にモーションがあれば終了フラグを立てる
for (
let ite: iterator<CubismMotionQueueEntry> = this._motions.begin();
ite.notEqual(this._motions.end());
ite.increment()
) {
const motionQueueEntry: CubismMotionQueueEntry = ite.ptr();
if (motionQueueEntry == null) {
continue;
}
if (
motionQueueEntry._motionQueueEntryHandle == motionQueueEntryNumber &&
!motionQueueEntry.isFinished()
) {
return false;
}
}
return true;
}
/**
* 全てのモーションを停止する
*/
public stopAllMotions(): void {
// ------- 処理を行う -------
// 既にモーションがあれば終了フラグを立てる
for (
let ite: iterator<CubismMotionQueueEntry> = this._motions.begin();
ite.notEqual(this._motions.end());
) {
let motionQueueEntry: CubismMotionQueueEntry = ite.ptr();
if (motionQueueEntry == null) {
ite = this._motions.erase(ite);
continue;
}
// ----- 終了済みの処理があれば削除する ------
motionQueueEntry.release();
motionQueueEntry = void 0;
motionQueueEntry = null;
ite = this._motions.erase(ite); // 削除
}
}
/**
* 指定したCubismMotionQueueEntryの取得
* @param motionQueueEntryNumber モーションの識別番号
* @return 指定したCubismMotionQueueEntry
* @return null 見つからなかった
*/
public getCubismMotionQueueEntry(
motionQueueEntryNumber: any
): CubismMotionQueueEntry {
//------- 処理を行う -------
// 既にモーションがあれば終了フラグを立てる
for (
let ite: iterator<CubismMotionQueueEntry> = this._motions.begin();
ite.notEqual(this._motions.end());
ite.preIncrement()
) {
const motionQueueEntry: CubismMotionQueueEntry = ite.ptr();
if (motionQueueEntry == null) {
continue;
}
if (
motionQueueEntry._motionQueueEntryHandle == motionQueueEntryNumber
) {
return motionQueueEntry;
}
}
return null;
}
/**
* イベントを受け取るCallbackの登録
*
* @param callback コールバック関数
* @param customData コールバックに返されるデータ
*/
public setEventCallback(
callback: CubismMotionEventFunction,
customData: any = null
): void {
this._eventCallBack = callback;
this._eventCustomData = customData;
}
/**
* モーションを更新して、モデルにパラメータ値を反映する。
*
* @param model 対象のモデル
* @param userTimeSeconds デルタ時間の積算値[秒]
* @return true モデルへパラメータ値の反映あり
* @return false モデルへパラメータ値の反映なし(モーションの変化なし)
*/
public doUpdateMotion(
model: CubismModel,
userTimeSeconds: number
): boolean {
let updated = false;
// ------- 処理を行う --------
// 既にモーションがあれば終了フラグを立てる
for (
let ite: iterator<CubismMotionQueueEntry> = this._motions.begin();
ite.notEqual(this._motions.end());
) {
let motionQueueEntry: CubismMotionQueueEntry = ite.ptr();
if (motionQueueEntry == null) {
ite = this._motions.erase(ite); // 削除
continue;
}
const motion: ACubismMotion = motionQueueEntry._motion;
if (motion == null) {
motionQueueEntry.release();
motionQueueEntry = void 0;
motionQueueEntry = null;
ite = this._motions.erase(ite); // 削除
continue;
}
// ------ 値を反映する ------
motion.updateParameters(model, motionQueueEntry, userTimeSeconds);
updated = true;
// ------ ユーザトリガーイベントを検査する ----
const firedList: csmVector<csmString> = motion.getFiredEvent(
motionQueueEntry.getLastCheckEventTime() -
motionQueueEntry.getStartTime(),
userTimeSeconds - motionQueueEntry.getStartTime()
);
for (let i = 0; i < firedList.getSize(); ++i) {
this._eventCallBack(this, firedList.at(i), this._eventCustomData);
}
motionQueueEntry.setLastCheckEventTime(userTimeSeconds);
// ------ 終了済みの処理があれば削除する ------
if (motionQueueEntry.isFinished()) {
motionQueueEntry.release();
motionQueueEntry = void 0;
motionQueueEntry = null;
ite = this._motions.erase(ite); // 削除
} else {
ite.preIncrement();
}
}
return updated;
}
_userTimeSeconds: number; // デルタ時間の積算値[秒]
_motions: csmVector<CubismMotionQueueEntry>; // モーション
_eventCallBack: CubismMotionEventFunction; // コールバック関数
_eventCustomData: any; // コールバックに戻されるデータ
}
/**
* イベントのコールバック関数を定義
*
* イベントのコールバックに登録できる関数の型情報
* @param caller 発火したイベントを再生させたCubismMotionQueueManager
* @param eventValue 発火したイベントの文字列データ
* @param customData コールバックに返される登録時に指定されたデータ
*/
export interface CubismMotionEventFunction {
(
caller: CubismMotionQueueManager,
eventValue: csmString,
customData: any
): void;
}
/**
* モーションの識別番号
*
* モーションの識別番号の定義
*/
export declare type CubismMotionQueueEntryHandle = any;
export const InvalidMotionQueueEntryHandleValue: CubismMotionQueueEntryHandle = -1;
}