ポーズについて

最終更新: 2023年1月26日

ポーズ機能とは

ポーズとは複数の同種パーツのうち、単一のパーツだけをモーションに基づいて切り替えをフェード表示する機能です。
複数の同種パーツとは右手Aと右手Bなど同時に表示されると表示的に矛盾を起こすものを指します。

モーション内のパーツ不透明度の操作を最終的にパーツへ適用する処理部分でもあります。

このページへの理解のために「モデルについて」の「CubismModelクラスでモデルに存在しないパラメータIDを使う」を事前に読むことをお勧めします。

モーションからのパーツ不透明度操作の流れ

モーション再生でのパーツ不透明度操作

パーツ不透明度の整合性を保つため、Original WorkFlow の Frameworkでのモーション再生単体では直接パーツ不透明度に対して操作を行いません。

モーション再生のパーツの不透明度操作は、かわりにパーツと同じIDパラメータへ対しての上書きに置き換わります。
このときモデルに存在しないパラメータIDは仮想パラメータとして値の保持だけ行われます。
モーション切り替えに伴うフェード処理などは行われず、ただ上書きのみが行われます。

OW SDKではモーションへのパーツ不透明度操作の補間方法はステップが適用されることを想定しています。

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
// C++
void CubismMotion::DoUpdateParameters(CubismModel* model, csmFloat32 timeSeconds, csmFloat32 fadeWeight, CubismMotionQueueEntry* motionQueueEntry)
{
/*省略*/
for (; c < _motionData->CurveCount && curves[c].Type == CubismMotionCurveTarget_PartOpacity; ++c)
{
// Find parameter index.
parameterIndex = model->GetParameterIndex(curves[c].Id);
// Skip curve evaluation if no value in sink.
if (parameterIndex == -1)
{
continue;
}
// Evaluate curve and apply value.
value = EvaluateCurve(_motionData, c, time);
model->SetParameterValue(parameterIndex, value);// パーツ不透明度ではなく仮想パラメータへ書き込む。
}
/*省略*/
}
// C++ void CubismMotion::DoUpdateParameters(CubismModel* model, csmFloat32 timeSeconds, csmFloat32 fadeWeight, CubismMotionQueueEntry* motionQueueEntry) { /*省略*/ for (; c < _motionData->CurveCount && curves[c].Type == CubismMotionCurveTarget_PartOpacity; ++c) { // Find parameter index. parameterIndex = model->GetParameterIndex(curves[c].Id); // Skip curve evaluation if no value in sink. if (parameterIndex == -1) { continue; } // Evaluate curve and apply value. value = EvaluateCurve(_motionData, c, time); model->SetParameterValue(parameterIndex, value);// パーツ不透明度ではなく仮想パラメータへ書き込む。 } /*省略*/ }
// C++
	void CubismMotion::DoUpdateParameters(CubismModel* model, csmFloat32 timeSeconds, csmFloat32 fadeWeight, CubismMotionQueueEntry* motionQueueEntry)
    {
/*省略*/
		for (; c < _motionData->CurveCount && curves[c].Type == CubismMotionCurveTarget_PartOpacity; ++c)
        {
            // Find parameter index.
            parameterIndex = model->GetParameterIndex(curves[c].Id);

            // Skip curve evaluation if no value in sink.
            if (parameterIndex == -1)
            {
                continue;
            }

            // Evaluate curve and apply value.
            value = EvaluateCurve(_motionData, c, time);

            model->SetParameterValue(parameterIndex, value);// パーツ不透明度ではなく仮想パラメータへ書き込む。
        }
/*省略*/
    }
Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
// TypeScript
public doUpdateParameters(model: CubismModel, userTimeSeconds: number, fadeWeight: number, motionQueueEntry: CubismMotionQueueEntry): void
{
/*省略*/
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);
}
/*省略*/
}
// TypeScript public doUpdateParameters(model: CubismModel, userTimeSeconds: number, fadeWeight: number, motionQueueEntry: CubismMotionQueueEntry): void { /*省略*/ 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); } /*省略*/ }
// TypeScript
    public doUpdateParameters(model: CubismModel, userTimeSeconds: number, fadeWeight: number, motionQueueEntry: CubismMotionQueueEntry): void
    {
      
/*省略*/

      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);
      }
/*省略*/
    }
Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
// Java
public void doUpdateParameters(CubismModel model, float timeSeconds, float fadeWeight, CubismMotionQueueEntry motionQueueEntry){
/*省略*/
for (CubismMotionCurve curve : curves) {
if (curve.type != CubismMotionCurveTarget.PARAMETER) {
continue;
}
// Findparameter index.
final int parameterIndex = model.getParameterIndex(curve.id);
// Skip curve evaluation if no value.
if (parameterIndex == -1) {
continue;
}
// Evaluate curve and apply value.
value = evaluateCurve(curve, time);
...
model.setParameterValue(parameterIndex, v); // パーツ不透明度ではなく仮想パラメータへ書き込む
}
/*省略*/
}
// Java public void doUpdateParameters(CubismModel model, float timeSeconds, float fadeWeight, CubismMotionQueueEntry motionQueueEntry){ /*省略*/ for (CubismMotionCurve curve : curves) { if (curve.type != CubismMotionCurveTarget.PARAMETER) { continue; } // Findparameter index. final int parameterIndex = model.getParameterIndex(curve.id); // Skip curve evaluation if no value. if (parameterIndex == -1) { continue; } // Evaluate curve and apply value. value = evaluateCurve(curve, time); ... model.setParameterValue(parameterIndex, v); // パーツ不透明度ではなく仮想パラメータへ書き込む } /*省略*/ }
// Java
	public void doUpdateParameters(CubismModel model, float timeSeconds, float fadeWeight, CubismMotionQueueEntry motionQueueEntry){
      
/*省略*/
      
		for (CubismMotionCurve curve : curves) {
          
			if (curve.type != CubismMotionCurveTarget.PARAMETER) {
			continue;
            }

          // Findparameter index.
          final int parameterIndex = model.getParameterIndex(curve.id);

          // Skip curve evaluation if no value.
          if (parameterIndex == -1) {
            continue;
          }
          
          // Evaluate curve and apply value.
          value = evaluateCurve(curve, time);
          
          ...
          
          model.setParameterValue(parameterIndex, v); // パーツ不透明度ではなく仮想パラメータへ書き込む
        }      
/*省略*/
    }

ポーズの適用

モデルの更新処理の最後のフェーズでPose機能を適用することで、.pose3.jsonに記述された情報を基にグループごとにどのパーツを表示するか、仮想パラメータの値を参照して決定していきます。

Poseをモデルに適用するAPIは以下になります

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
// C++
CubismPose::UpdateParameters()
// C++ CubismPose::UpdateParameters()
// C++
CubismPose::UpdateParameters()
Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
// TypeScript
CubismPose.updateParameters()
// TypeScript CubismPose.updateParameters()
// TypeScript
CubismPose.updateParameters()
Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
// Java
CubismPose.updateParameters();
// Java CubismPose.updateParameters();
// Java
CubismPose.updateParameters();

パーツ操作のカーブはステップを推奨されていますがリニアなどを選択している場合、0.001より大きくなった時点で表示状態と認識します。

表示パーツの決定時に操作前のパーツ不透明度から差分時間との比例計算で新しい不透明度がリニア補間として決定します。

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
// C++
void CubismPose::DoFade(CubismModel* model, csmFloat32 deltaTimeSeconds, csmInt32 beginIndex, csmInt32 partGroupCount)
{
csmInt32 visiblePartIndex = -1;
csmFloat32 newOpacity = 1.0f;
const csmFloat32 Phi = 0.5f;
const csmFloat32 BackOpacityThreshold = 0.15f;
// 現在、表示状態になっているパーツを取得
for (csmInt32 i = beginIndex; i < beginIndex + partGroupCount; ++i)
{
csmInt32 partIndex = _partGroups[i].PartIndex;
csmInt32 paramIndex = _partGroups[i].ParameterIndex;
if (model->GetParameterValue(paramIndex) > Epsilon) // const csmFloat32 Epsilon= 0.001f;
{
if (visiblePartIndex >= 0)
{
break;
}
visiblePartIndex = i;
newOpacity = model->GetPartOpacity(partIndex);
// 新しい不透明度を計算
newOpacity += (deltaTimeSeconds / _fadeTimeSeconds);
if (newOpacity > 1.0f)
{
newOpacity = 1.0f;
}
}
}
/*省略*/
}
// C++ void CubismPose::DoFade(CubismModel* model, csmFloat32 deltaTimeSeconds, csmInt32 beginIndex, csmInt32 partGroupCount) { csmInt32 visiblePartIndex = -1; csmFloat32 newOpacity = 1.0f; const csmFloat32 Phi = 0.5f; const csmFloat32 BackOpacityThreshold = 0.15f; // 現在、表示状態になっているパーツを取得 for (csmInt32 i = beginIndex; i < beginIndex + partGroupCount; ++i) { csmInt32 partIndex = _partGroups[i].PartIndex; csmInt32 paramIndex = _partGroups[i].ParameterIndex; if (model->GetParameterValue(paramIndex) > Epsilon) // const csmFloat32 Epsilon= 0.001f; { if (visiblePartIndex >= 0) { break; } visiblePartIndex = i; newOpacity = model->GetPartOpacity(partIndex); // 新しい不透明度を計算 newOpacity += (deltaTimeSeconds / _fadeTimeSeconds); if (newOpacity > 1.0f) { newOpacity = 1.0f; } } } /*省略*/ }
// C++
	void CubismPose::DoFade(CubismModel* model, csmFloat32 deltaTimeSeconds, csmInt32 beginIndex, csmInt32 partGroupCount)
    {
        csmInt32    visiblePartIndex = -1;
        csmFloat32  newOpacity = 1.0f;

        const csmFloat32 Phi = 0.5f;
        const csmFloat32 BackOpacityThreshold = 0.15f;

        // 現在、表示状態になっているパーツを取得
        for (csmInt32 i = beginIndex; i < beginIndex + partGroupCount; ++i)
        {
            csmInt32 partIndex = _partGroups[i].PartIndex;
            csmInt32 paramIndex = _partGroups[i].ParameterIndex;

            if (model->GetParameterValue(paramIndex) > Epsilon) // const csmFloat32 Epsilon= 0.001f;
            {
                if (visiblePartIndex >= 0)
                {
                    break;
                }

                visiblePartIndex = i;
                newOpacity = model->GetPartOpacity(partIndex);

                // 新しい不透明度を計算
                newOpacity += (deltaTimeSeconds / _fadeTimeSeconds);

                if (newOpacity > 1.0f)
                {
                    newOpacity = 1.0f;
                }
            }
        }
/*省略*/
    }
Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
// TypeScript
public doFade(model: CubismModel, deltaTimeSeconds: number, beginIndex: number, partGroupCount: number): void
{
let visiblePartIndex: number = -1;
let newOpacity: number = 1.0;
const phi: number = 0.5;
const backOpacityThreshold: number = 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;
}
}
}
/*省略*/
}
// TypeScript public doFade(model: CubismModel, deltaTimeSeconds: number, beginIndex: number, partGroupCount: number): void { let visiblePartIndex: number = -1; let newOpacity: number = 1.0; const phi: number = 0.5; const backOpacityThreshold: number = 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; } } } /*省略*/ }
// TypeScript
	public doFade(model: CubismModel, deltaTimeSeconds: number, beginIndex: number, partGroupCount: number): void
    {
      let visiblePartIndex: number = -1;
      let newOpacity: number = 1.0;

      const phi: number = 0.5;
      const backOpacityThreshold: number = 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;
          }
        }
      }
/*省略*/
    }
Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
// Java
public void doFade(CubismModel model,
float deltaTimeSeconds,
int beginIndex,
int partGroupCount)
{
int visiblePartIndex = -1;
float newOpacity = 1.0f;
// 現在、表示状態になっているパーツを取得
for (int i = beginIndex; i < beginIndex + partGroupCount; i++) {
final int paramIndex = partGroups.get(i).parameterIndex;
if (model.getParameterValue(paramIndex) > EPSILON) {
if (visiblePartIndex >= 0) {
break;
}
// 新しい不透明度を計算
newOpacity = calculateOpacity(model, i, deltaTimeSeconds);
visiblePartIndex = i;
}
}
/*省略*/
}
// Java public void doFade(CubismModel model, float deltaTimeSeconds, int beginIndex, int partGroupCount) { int visiblePartIndex = -1; float newOpacity = 1.0f; // 現在、表示状態になっているパーツを取得 for (int i = beginIndex; i < beginIndex + partGroupCount; i++) { final int paramIndex = partGroups.get(i).parameterIndex; if (model.getParameterValue(paramIndex) > EPSILON) { if (visiblePartIndex >= 0) { break; } // 新しい不透明度を計算 newOpacity = calculateOpacity(model, i, deltaTimeSeconds); visiblePartIndex = i; } } /*省略*/ }
// Java
	public void doFade(CubismModel model,
                       float deltaTimeSeconds,
                       int beginIndex,
                       int partGroupCount)
	{
      int visiblePartIndex = -1;
      float newOpacity = 1.0f;
      
      // 現在、表示状態になっているパーツを取得
      for (int i = beginIndex; i < beginIndex + partGroupCount; i++) {
        final int paramIndex = partGroups.get(i).parameterIndex;
        
        if (model.getParameterValue(paramIndex) > EPSILON) {
          if (visiblePartIndex >= 0) {
            break;
          }
          
          // 新しい不透明度を計算
          newOpacity = calculateOpacity(model, i, deltaTimeSeconds);
          visiblePartIndex = i;
        }
      }

/*省略*/
  
}

表示パーツとその新しい不透明度が決定したあと、グループ全体に対して不透明度の上書き処理が実行されます。
非表示パーツの新しい不透明度は表示するパーツの不透明度との関係で背景が透けないレベルで不透明度を低下させていきます。

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
// C++
void CubismPose::DoFade(CubismModel* model, csmFloat32 deltaTimeSeconds, csmInt32 beginIndex, csmInt32 partGroupCount)
{
csmInt32 visiblePartIndex = -1;
csmFloat32 newOpacity = 1.0f;
const csmFloat32 Phi = 0.5f;
const csmFloat32 BackOpacityThreshold = 0.15f;
/*省略*/
if (visiblePartIndex < 0)
{
visiblePartIndex = 0;
newOpacity = 1.0f;
}
// 表示パーツ、非表示パーツの不透明度を設定する
for (csmInt32 i = beginIndex; i < beginIndex + partGroupCount; ++i)
{
csmInt32 partsIndex = _partGroups[i].PartIndex;
// 表示パーツの設定
if (visiblePartIndex == i)
{
model->SetPartOpacity(partsIndex, newOpacity); //j/ 先に設定
}
// 非表示パーツの設定
else
{
csmFloat32 opacity = model->GetPartOpacity(partsIndex);
csmFloat32 a1; // 計算によって求められる不透明度
if (newOpacity < Phi)
{
a1 = newOpacity * (Phi - 1) / Phi + 1.0f; // (0,1),(phi,phi)を通る直線式
}
else
{
a1 = (1 - newOpacity) * Phi / (1.0f - Phi); // (1,0),(phi,phi)を通る直線式
}
// 背景の見える割合を制限する場合
csmFloat32 backOpacity = (1.0f - a1) * (1.0f - newOpacity);
if (backOpacity > BackOpacityThreshold)
{
a1 = 1.0f - BackOpacityThreshold / (1.0f - newOpacity);
}
if (opacity > a1)
{
opacity = a1; // 計算の不透明度よりも大きければ(濃ければ)不透明度を上げる
}
model->SetPartOpacity(partsIndex, opacity);
}
}
}
// C++ void CubismPose::DoFade(CubismModel* model, csmFloat32 deltaTimeSeconds, csmInt32 beginIndex, csmInt32 partGroupCount) { csmInt32 visiblePartIndex = -1; csmFloat32 newOpacity = 1.0f; const csmFloat32 Phi = 0.5f; const csmFloat32 BackOpacityThreshold = 0.15f; /*省略*/ if (visiblePartIndex < 0) { visiblePartIndex = 0; newOpacity = 1.0f; } // 表示パーツ、非表示パーツの不透明度を設定する for (csmInt32 i = beginIndex; i < beginIndex + partGroupCount; ++i) { csmInt32 partsIndex = _partGroups[i].PartIndex; // 表示パーツの設定 if (visiblePartIndex == i) { model->SetPartOpacity(partsIndex, newOpacity); //j/ 先に設定 } // 非表示パーツの設定 else { csmFloat32 opacity = model->GetPartOpacity(partsIndex); csmFloat32 a1; // 計算によって求められる不透明度 if (newOpacity < Phi) { a1 = newOpacity * (Phi - 1) / Phi + 1.0f; // (0,1),(phi,phi)を通る直線式 } else { a1 = (1 - newOpacity) * Phi / (1.0f - Phi); // (1,0),(phi,phi)を通る直線式 } // 背景の見える割合を制限する場合 csmFloat32 backOpacity = (1.0f - a1) * (1.0f - newOpacity); if (backOpacity > BackOpacityThreshold) { a1 = 1.0f - BackOpacityThreshold / (1.0f - newOpacity); } if (opacity > a1) { opacity = a1; // 計算の不透明度よりも大きければ(濃ければ)不透明度を上げる } model->SetPartOpacity(partsIndex, opacity); } } }
// C++
	void CubismPose::DoFade(CubismModel* model, csmFloat32 deltaTimeSeconds, csmInt32 beginIndex, csmInt32 partGroupCount)
    {
        csmInt32    visiblePartIndex = -1;
        csmFloat32  newOpacity = 1.0f;

        const csmFloat32 Phi = 0.5f;
        const csmFloat32 BackOpacityThreshold = 0.15f;
/*省略*/

        if (visiblePartIndex < 0)
        {
            visiblePartIndex = 0;
            newOpacity = 1.0f;
        }

        //  表示パーツ、非表示パーツの不透明度を設定する
        for (csmInt32 i = beginIndex; i < beginIndex + partGroupCount; ++i)
        {
            csmInt32 partsIndex = _partGroups[i].PartIndex;

            //  表示パーツの設定
            if (visiblePartIndex == i)
            {
                model->SetPartOpacity(partsIndex, newOpacity); //j/ 先に設定
            }
            // 非表示パーツの設定
            else
            {
                csmFloat32 opacity = model->GetPartOpacity(partsIndex);
                csmFloat32 a1;          // 計算によって求められる不透明度

                if (newOpacity < Phi)
                {
                    a1 = newOpacity * (Phi - 1) / Phi + 1.0f; // (0,1),(phi,phi)を通る直線式
                }
                else
                {
                    a1 = (1 - newOpacity) * Phi / (1.0f - Phi); // (1,0),(phi,phi)を通る直線式
                }

                // 背景の見える割合を制限する場合
                csmFloat32 backOpacity = (1.0f - a1) * (1.0f - newOpacity);

                if (backOpacity > BackOpacityThreshold)
                {
                    a1 = 1.0f - BackOpacityThreshold / (1.0f - newOpacity);
                }

                if (opacity > a1)
                {
                    opacity = a1; // 計算の不透明度よりも大きければ(濃ければ)不透明度を上げる
                }

                model->SetPartOpacity(partsIndex, opacity);
            }
        }
    }
Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
// TypeScript
public doFade(model: CubismModel, deltaTimeSeconds: number, beginIndex: number, partGroupCount: number): void
{
let visiblePartIndex: number = -1;
let newOpacity: number = 1.0;
const phi: number = 0.5;
const backOpacityThreshold: number = 0.15;
/*省略*/
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);
}
}
}
// TypeScript public doFade(model: CubismModel, deltaTimeSeconds: number, beginIndex: number, partGroupCount: number): void { let visiblePartIndex: number = -1; let newOpacity: number = 1.0; const phi: number = 0.5; const backOpacityThreshold: number = 0.15; /*省略*/ 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); } } }
// TypeScript
	public doFade(model: CubismModel, deltaTimeSeconds: number, beginIndex: number, partGroupCount: number): void
    {
      let visiblePartIndex: number = -1;
      let newOpacity: number = 1.0;

      const phi: number = 0.5;
      const backOpacityThreshold: number = 0.15;

/*省略*/

      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);
        }
      }
    }
Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
// Java
public void doFade(CubismModel model,
float deltaTimeSeconds,
int beginIndex,
int partGroupCount)
{
int visiblePartIndex = -1;
float newOpacity = 1.0f;
/*省略*/
if (visiblePartIndex < 0) {
visiblePartIndex = 0;
newOpacity = 1.0f;
}
// 表示パーツ、非表示パーツの不透明度を設定する
for (int i = beginIndex; i < beginIndex + partGroupCount; i++) {
final int partsIndex = partGroups.get(i).partIndex;
// 表示パーツの設定
if (visiblePartIndex == i) {
model.setPartOpacity(partsIndex, newOpacity);
}
// 非表示パーツの設定
else {
final float opacity = model.getPartOpacity(partsIndex);
final float result = calcNonDisplayedPartsOpacity(opacity, newOpacity);
model.setPartOpacity(partsIndex, result);
}
}
}
// Java public void doFade(CubismModel model, float deltaTimeSeconds, int beginIndex, int partGroupCount) { int visiblePartIndex = -1; float newOpacity = 1.0f; /*省略*/ if (visiblePartIndex < 0) { visiblePartIndex = 0; newOpacity = 1.0f; } // 表示パーツ、非表示パーツの不透明度を設定する for (int i = beginIndex; i < beginIndex + partGroupCount; i++) { final int partsIndex = partGroups.get(i).partIndex; // 表示パーツの設定 if (visiblePartIndex == i) { model.setPartOpacity(partsIndex, newOpacity); } // 非表示パーツの設定 else { final float opacity = model.getPartOpacity(partsIndex); final float result = calcNonDisplayedPartsOpacity(opacity, newOpacity); model.setPartOpacity(partsIndex, result); } } }
// Java
	public void doFade(CubismModel model,
                       float deltaTimeSeconds,
                       int beginIndex,
                       int partGroupCount)
	{
      int visiblePartIndex = -1;
      float newOpacity = 1.0f;
      
      /*省略*/
      
      if (visiblePartIndex < 0) {
        visiblePartIndex = 0;
        newOpacity = 1.0f;
      }
      
      // 表示パーツ、非表示パーツの不透明度を設定する
      for (int i = beginIndex; i < beginIndex + partGroupCount; i++) {
        final int partsIndex = partGroups.get(i).partIndex;
        
        // 表示パーツの設定
        if (visiblePartIndex == i) {
          model.setPartOpacity(partsIndex, newOpacity);
        }
        // 非表示パーツの設定
        else {
          final float opacity = model.getPartOpacity(partsIndex);
          final float result = calcNonDisplayedPartsOpacity(opacity, newOpacity);
          model.setPartOpacity(partsIndex, result);
        }
      }
    }

Poseのデータ構造

Poseが取り扱う情報はパラメータ、パーツ情報へのアクセスを高速にするため、パーツIDでアクセスしたときのパラメータ、パーツのインデックスをPartData構造体で固めて保有します。

また連動するデータはLinkとしてPartDataの子要素として保有します。

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
// C++
/**
* @brief パーツにまつわるデータを管理
*
* パーツにまつわる諸々のデータを管理する。
*/
struct PartData
{
/*省略*/
CubismIdHandle PartId; ///< パーツID
csmInt32 ParameterIndex; ///< パラメータのインデックス
csmInt32 PartIndex; ///< パーツのインデックス
csmVector<PartData> Link; ///< 連動するパラメータ
};
// C++ /** * @brief パーツにまつわるデータを管理 * * パーツにまつわる諸々のデータを管理する。 */ struct PartData { /*省略*/ CubismIdHandle PartId; ///< パーツID csmInt32 ParameterIndex; ///< パラメータのインデックス csmInt32 PartIndex; ///< パーツのインデックス csmVector<PartData> Link; ///< 連動するパラメータ };
// C++
	/**
    * @brief パーツにまつわるデータを管理
    *
    * パーツにまつわる諸々のデータを管理する。
    */
    struct PartData
    {
/*省略*/

        CubismIdHandle                     PartId;                 ///< パーツID
        csmInt32                            ParameterIndex;         ///< パラメータのインデックス
        csmInt32                            PartIndex;              ///< パーツのインデックス
        csmVector<PartData>                 Link;                   ///< 連動するパラメータ
    };
Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
// TypeScript
/**
* パーツにまつわるデータを管理
*/
export class PartData
{
/*省略*/
partId: CubismIdHandle; // パーツID
parameterIndex: number; // パラメータのインデックス
partIndex: number; // パーツのインデックス
link: csmVector<PartData>; // 連動するパラメータ
}
// TypeScript /** * パーツにまつわるデータを管理 */ export class PartData { /*省略*/ partId: CubismIdHandle; // パーツID parameterIndex: number; // パラメータのインデックス partIndex: number; // パーツのインデックス link: csmVector<PartData>; // 連動するパラメータ }
// TypeScript
	/**
     * パーツにまつわるデータを管理
     */
    export class PartData
    {
/*省略*/
        
        partId: CubismIdHandle;   // パーツID
        parameterIndex: number; // パラメータのインデックス
        partIndex: number;  // パーツのインデックス
        link: csmVector<PartData>;   // 連動するパラメータ
    }
Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
// Java
/**
* パーツにまつわる諸々のデータを管理する。
*/
public class PartData{
/*省略*/
CubismId partId; // パーツID
int parameterIndex; // パラメータのインデックス
int partIndex; // パーツのインデックス
List<PartData> link // 連動するパラメータ
}
// Java /** * パーツにまつわる諸々のデータを管理する。 */ public class PartData{ /*省略*/ CubismId partId; // パーツID int parameterIndex; // パラメータのインデックス int partIndex; // パーツのインデックス List<PartData> link // 連動するパラメータ }
// Java
    /**
    * パーツにまつわる諸々のデータを管理する。
    */
    public class PartData{
        /*省略*/
 
        CubismId partId; // パーツID
        int parameterIndex; // パラメータのインデックス
        int partIndex; // パーツのインデックス
        List<PartData> link // 連動するパラメータ
    }

CubismPoseはグループ情報を一次元のPartData配列と各グループの個数情報の配列で表現します。

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
// C++
class CubismPose
{
/*省略*/
csmVector<PartData> _partGroups; ///< パーツグループ
csmVector<csmInt32> _partGroupCounts; ///< それぞれのパーツグループの個数
csmFloat32 _fadeTimeSeconds; ///< フェード時間[秒]
csmFloat32 _lastTimeSeconds; ///< 最後に実行した時刻[秒]
CubismModel* _lastModel; ///< 前回操作したモデル
};
// C++ class CubismPose { /*省略*/ csmVector<PartData> _partGroups; ///< パーツグループ csmVector<csmInt32> _partGroupCounts; ///< それぞれのパーツグループの個数 csmFloat32 _fadeTimeSeconds; ///< フェード時間[秒] csmFloat32 _lastTimeSeconds; ///< 最後に実行した時刻[秒] CubismModel* _lastModel; ///< 前回操作したモデル };
// C++
	class CubismPose
    {
/*省略*/

        csmVector<PartData>             _partGroups;                ///< パーツグループ
        csmVector<csmInt32>             _partGroupCounts;           ///< それぞれのパーツグループの個数
        csmFloat32                      _fadeTimeSeconds;           ///< フェード時間[秒]
        csmFloat32                      _lastTimeSeconds;           ///< 最後に実行した時刻[秒]
        CubismModel*                    _lastModel;                 ///< 前回操作したモデル
    };
Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
// TypeScript
export class CubismPose
{
/*省略*/
_partGroups: csmVector<PartData>; // パーツグループ
_partGroupCounts: csmVector<number>; // それぞれのパーツグループの個数
_fadeTimeSeconds: number; // フェード時間[秒]
_lastModel: CubismModel; // 前回操作したモデル
}
// TypeScript export class CubismPose { /*省略*/ _partGroups: csmVector<PartData>; // パーツグループ _partGroupCounts: csmVector<number>; // それぞれのパーツグループの個数 _fadeTimeSeconds: number; // フェード時間[秒] _lastModel: CubismModel; // 前回操作したモデル }
// TypeScript
	export class CubismPose
    {
/*省略*/      

        _partGroups: csmVector<PartData>; // パーツグループ
        _partGroupCounts: csmVector<number>;         // それぞれのパーツグループの個数
        _fadeTimeSeconds: number;           // フェード時間[秒]
        _lastModel: CubismModel;            // 前回操作したモデル
    }
Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
// Java
public class CubismPose{
/*省略*/
List<PartData> partGroups; // パーツグループ
List<Integer> partGroupCounts; // それぞれのパーツグループの個数
float fadeTimeSeconds; // フェード時間[秒]
float lastTimeSeconds; // 最後に実行した時刻[秒]
CubismModel lastModel; // 前回操作したモデル
}
// Java public class CubismPose{ /*省略*/ List<PartData> partGroups; // パーツグループ List<Integer> partGroupCounts; // それぞれのパーツグループの個数 float fadeTimeSeconds; // フェード時間[秒] float lastTimeSeconds; // 最後に実行した時刻[秒] CubismModel lastModel; // 前回操作したモデル }
// Java
    public class CubismPose{
    /*省略*/

        List<PartData> partGroups; // パーツグループ
        List<Integer> partGroupCounts; // それぞれのパーツグループの個数
        float fadeTimeSeconds; // フェード時間[秒]
        float lastTimeSeconds; // 最後に実行した時刻[秒]
        CubismModel lastModel; // 前回操作したモデル
    }

一次元配列のPartDataは各グループの個数情報をもとに配列上の先頭位置と要素数をDoFadeへ渡すことによりグループとして処理がされます。

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
// C++
void CubismPose::UpdateParameters(CubismModel* model, csmFloat32 deltaTimeSeconds)
{
// 前回のモデルと同じではないときは初期化が必要
if (model != _lastModel)
{
// パラメータインデックスの初期化
Reset(model);
}
_lastModel = model;
// 設定から時間を変更すると、経過時間がマイナスになることがあるので、経過時間0として対応。
if (deltaTimeSeconds < 0.0f)
{
deltaTimeSeconds = 0.0f;
}
csmInt32 beginIndex = 0;
for (csmUint32 i = 0; i < _partGroupCounts.GetSize(); i++)
{
csmInt32 partGroupCount = _partGroupCounts[i];
DoFade(model, deltaTimeSeconds, beginIndex, partGroupCount);
beginIndex += partGroupCount;
}
CopyPartOpacities(model);
}
// C++ void CubismPose::UpdateParameters(CubismModel* model, csmFloat32 deltaTimeSeconds) { // 前回のモデルと同じではないときは初期化が必要 if (model != _lastModel) { // パラメータインデックスの初期化 Reset(model); } _lastModel = model; // 設定から時間を変更すると、経過時間がマイナスになることがあるので、経過時間0として対応。 if (deltaTimeSeconds < 0.0f) { deltaTimeSeconds = 0.0f; } csmInt32 beginIndex = 0; for (csmUint32 i = 0; i < _partGroupCounts.GetSize(); i++) { csmInt32 partGroupCount = _partGroupCounts[i]; DoFade(model, deltaTimeSeconds, beginIndex, partGroupCount); beginIndex += partGroupCount; } CopyPartOpacities(model); }
// C++
	void CubismPose::UpdateParameters(CubismModel* model, csmFloat32 deltaTimeSeconds)
    {
        // 前回のモデルと同じではないときは初期化が必要
        if (model != _lastModel)
        {
            // パラメータインデックスの初期化
            Reset(model);
        }

        _lastModel = model;

        // 設定から時間を変更すると、経過時間がマイナスになることがあるので、経過時間0として対応。
        if (deltaTimeSeconds < 0.0f)
        {
            deltaTimeSeconds = 0.0f;
        }

        csmInt32 beginIndex = 0;

        for (csmUint32 i = 0; i < _partGroupCounts.GetSize(); i++)
        {
            csmInt32 partGroupCount = _partGroupCounts[i];

            DoFade(model, deltaTimeSeconds, beginIndex, partGroupCount);

            beginIndex += partGroupCount;
        }

        CopyPartOpacities(model);
    }
Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
// TypeScript
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: number = 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);
}
// TypeScript 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: number = 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); }
// TypeScript
	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: number = 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);
    }
Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
// Java
public void updateParameters(final CubismModel model, float deltaTimeSeconds) {
// 前回のモデルと同じではないときは初期化が必要
if (model != lastModel) {
reset(model);
}
lastModel = model;
// 設定から時間を変更すると、経過時間がマイナスになることがあるので、経過時間0として対応。
if (deltaTimeSeconds < 0.0f) {
deltaTimeSeconds = 0.0f;
}
int beginIndex = 0;
for (final int partGroupCount : partGroupCounts) {
doFade(model, deltaTimeSeconds, beginIndex, partGroupCount);
beginIndex += partGroupCount;
}
copyPartOpacities(model);
}
// Java public void updateParameters(final CubismModel model, float deltaTimeSeconds) { // 前回のモデルと同じではないときは初期化が必要 if (model != lastModel) { reset(model); } lastModel = model; // 設定から時間を変更すると、経過時間がマイナスになることがあるので、経過時間0として対応。 if (deltaTimeSeconds < 0.0f) { deltaTimeSeconds = 0.0f; } int beginIndex = 0; for (final int partGroupCount : partGroupCounts) { doFade(model, deltaTimeSeconds, beginIndex, partGroupCount); beginIndex += partGroupCount; } copyPartOpacities(model); }
// Java
	public void updateParameters(final CubismModel model, float deltaTimeSeconds) {
      // 前回のモデルと同じではないときは初期化が必要
      if (model != lastModel) {
        reset(model);
      }
      lastModel = model;
      
      // 設定から時間を変更すると、経過時間がマイナスになることがあるので、経過時間0として対応。
      if (deltaTimeSeconds < 0.0f) {
        deltaTimeSeconds = 0.0f;
      }
      
      int beginIndex = 0;
      for (final int partGroupCount : partGroupCounts) {
        doFade(model, deltaTimeSeconds, beginIndex, partGroupCount);
        beginIndex += partGroupCount;
      }
      copyPartOpacities(model);
    }

Parent IDによる不透明度の連動

Pose適用処理の最後に呼ばれる以下の関数により、Linkに指定したパーツへ値を伝搬します。

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
// C++
CubismPose::CopyPartOpacities()
// C++ CubismPose::CopyPartOpacities()
// C++
CubismPose::CopyPartOpacities()
Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
// TypeScript
CubismPose.copyPartOpacities()
// TypeScript CubismPose.copyPartOpacities()
// TypeScript
CubismPose.copyPartOpacities()
Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
// Java
CubismPose.copyPartOpacities()
// Java CubismPose.copyPartOpacities()
// Java
CubismPose.copyPartOpacities()

このLinkはOWViewerでParent IDを表記されたPartIDがCubismIdHandleの配列で格納されます。
下の図であればPartManteL001のLinkにはPartArmLB001とPartArmLC001のCubismIdHandleが格納されます。

Linkと表記されていますが親子関係であり、親パーツへの不透明度操作がなければ連動がされないことに注意してください。
Linkに表記された子同士の連動はされません。

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
// C++
void CubismPose::CopyPartOpacities(CubismModel* model)
{
for (csmUint32 groupIndex = 0; groupIndex < _partGroups.GetSize(); ++groupIndex)
{
PartData& partData = _partGroups[groupIndex];
if (partData.Link.GetSize() == 0)
{
continue; // 連動するパラメータはない
}
csmInt32 partIndex = _partGroups[groupIndex].PartIndex;
csmFloat32 opacity = model->GetPartOpacity(partIndex);
for (csmUint32 linkIndex = 0; linkIndex < partData.Link.GetSize(); ++linkIndex)
{
PartData& linkPart = partData.Link[linkIndex];
csmInt32 linkPartIndex = linkPart.PartIndex;
if (linkPartIndex < 0)
{
continue;
}
model->SetPartOpacity(linkPartIndex, opacity);
}
}
}
// C++ void CubismPose::CopyPartOpacities(CubismModel* model) { for (csmUint32 groupIndex = 0; groupIndex < _partGroups.GetSize(); ++groupIndex) { PartData& partData = _partGroups[groupIndex]; if (partData.Link.GetSize() == 0) { continue; // 連動するパラメータはない } csmInt32 partIndex = _partGroups[groupIndex].PartIndex; csmFloat32 opacity = model->GetPartOpacity(partIndex); for (csmUint32 linkIndex = 0; linkIndex < partData.Link.GetSize(); ++linkIndex) { PartData& linkPart = partData.Link[linkIndex]; csmInt32 linkPartIndex = linkPart.PartIndex; if (linkPartIndex < 0) { continue; } model->SetPartOpacity(linkPartIndex, opacity); } } }
// C++
	void CubismPose::CopyPartOpacities(CubismModel* model)
    {
        for (csmUint32 groupIndex = 0; groupIndex < _partGroups.GetSize(); ++groupIndex)
        {
            PartData& partData = _partGroups[groupIndex];

            if (partData.Link.GetSize() == 0)
            {
                continue; // 連動するパラメータはない
            }

            csmInt32    partIndex = _partGroups[groupIndex].PartIndex;
            csmFloat32  opacity = model->GetPartOpacity(partIndex);

            for (csmUint32 linkIndex = 0; linkIndex < partData.Link.GetSize(); ++linkIndex)
            {
                PartData&   linkPart = partData.Link[linkIndex];
                csmInt32    linkPartIndex = linkPart.PartIndex;

                if (linkPartIndex < 0)
                {
                    continue;
                }

                model->SetPartOpacity(linkPartIndex, opacity);
            }
        }
    }
Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
// TypeScript
public copyPartOpacities(model: CubismModel): void
{
for(let groupIndex: number = 0; groupIndex < this._partGroups.getSize(); ++groupIndex)
{
let 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: number = 0; linkIndex < partData.link.getSize(); ++linkIndex)
{
let linkPart: PartData = partData.link.at(linkIndex);
const linkPartIndex: number = linkPart.partIndex;
if(linkPartIndex < 0)
{
continue;
}
model.setPartOpacityByIndex(linkPartIndex, opacity);
}
}
}
// TypeScript public copyPartOpacities(model: CubismModel): void { for(let groupIndex: number = 0; groupIndex < this._partGroups.getSize(); ++groupIndex) { let 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: number = 0; linkIndex < partData.link.getSize(); ++linkIndex) { let linkPart: PartData = partData.link.at(linkIndex); const linkPartIndex: number = linkPart.partIndex; if(linkPartIndex < 0) { continue; } model.setPartOpacityByIndex(linkPartIndex, opacity); } } }
// TypeScript
	public copyPartOpacities(model: CubismModel): void
    {
      for(let groupIndex: number = 0; groupIndex < this._partGroups.getSize(); ++groupIndex)
      {
        let 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: number = 0; linkIndex < partData.link.getSize(); ++linkIndex)
        {
          let linkPart: PartData = partData.link.at(linkIndex);
          const linkPartIndex: number = linkPart.partIndex;

          if(linkPartIndex < 0)
          {
            continue;
          }

          model.setPartOpacityByIndex(linkPartIndex, opacity);
        }
      }
    }
Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
// Java
private void copyPartOpacities(CubismModel model) {
for (PartData partData : partGroups) {
if (partData.linkedParameter == null) {
continue;
}
final int partIndex = partData.partIndex;
final float opacity = model.getPartOpacity(partIndex);
for (PartData linkedPart : partData.linkedParameter) {
final int linkedPartIndex = linkedPart.partIndex;
if (linkedPartIndex < 0) {
continue;
}
model.setPartOpacity(linkedPartIndex, opacity);
}
}
}
// Java private void copyPartOpacities(CubismModel model) { for (PartData partData : partGroups) { if (partData.linkedParameter == null) { continue; } final int partIndex = partData.partIndex; final float opacity = model.getPartOpacity(partIndex); for (PartData linkedPart : partData.linkedParameter) { final int linkedPartIndex = linkedPart.partIndex; if (linkedPartIndex < 0) { continue; } model.setPartOpacity(linkedPartIndex, opacity); } } }
// Java
	private void copyPartOpacities(CubismModel model) {
      for (PartData partData : partGroups) {
        if (partData.linkedParameter == null) {
          continue;
        }
        
        final int partIndex = partData.partIndex;
        final float opacity = model.getPartOpacity(partIndex);
        
        for (PartData linkedPart : partData.linkedParameter) {
          final int linkedPartIndex = linkedPart.partIndex;
          
          if (linkedPartIndex < 0) {
            continue;
          }
          model.setPartOpacity(linkedPartIndex, opacity);
        }
      }
    }

インスタンスの生成(.pose3.jsonファイルの読み込み)

ポーズは.pose3.jsonファイルに保存され、CubismPoseクラスを使用します。
生成には以下の関数を使用します。

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
// C++
CubismPose::Create()
// C++ CubismPose::Create()
// C++
CubismPose::Create()
Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
// TypeScript
CubismPose.create()
// TypeScript CubismPose.create()
// TypeScript
CubismPose.create()
Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
// Java
CubismPose.create()
// Java CubismPose.create()
// Java
CubismPose.create()

実装例

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
// C++
csmString path = _modelSetting->GetPoseFileName();
path = _modelHomeDir + path;
buffer = CreateBuffer(path.GetRawString(), &size);
CubismPose* pose = CubismPose::Create(buffer, size);
DeleteBuffer(buffer, path.GetRawString());
// C++ csmString path = _modelSetting->GetPoseFileName(); path = _modelHomeDir + path; buffer = CreateBuffer(path.GetRawString(), &size); CubismPose* pose = CubismPose::Create(buffer, size); DeleteBuffer(buffer, path.GetRawString());
// C++
	csmString path = _modelSetting->GetPoseFileName();
    path = _modelHomeDir + path;

    buffer = CreateBuffer(path.GetRawString(), &size);
    CubismPose* pose = CubismPose::Create(buffer, size);
    DeleteBuffer(buffer, path.GetRawString());
Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
// TypeScritp
let path: string = _modelSetting.getPoseFileName();
path = _modelHomeDire + path;
fetch(path).then(
(response) =>
{
return response.arrayBuffer();
}
).then(
(arrayBuffer) =>
{
let buffer: ArrayBuffer = arrayBuffer;
let size: number = buffer.byteLength;
let pose: CubismPose = CubismPose.create(buffer, size);
deleteBuffer(buffer, path);
}
);
// TypeScritp let path: string = _modelSetting.getPoseFileName(); path = _modelHomeDire + path; fetch(path).then( (response) => { return response.arrayBuffer(); } ).then( (arrayBuffer) => { let buffer: ArrayBuffer = arrayBuffer; let size: number = buffer.byteLength; let pose: CubismPose = CubismPose.create(buffer, size); deleteBuffer(buffer, path); } );
// TypeScritp
	let path: string = _modelSetting.getPoseFileName();
    path = _modelHomeDire + path;

    fetch(path).then(
        (response) => 
        {
            return response.arrayBuffer();
        }
    ).then(
        (arrayBuffer) =>
        {
            let buffer: ArrayBuffer = arrayBuffer;
            let size: number = buffer.byteLength;

            let pose: CubismPose = CubismPose.create(buffer, size);
            deleteBuffer(buffer, path);
        }
    );
Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
// Java
String path = modelSetting.getPoseFileName();
path = modelHomeDir + path;
buffer = CreateBuffer(path);
CubismPose pose = CubismPose.create(buffer);
// Java String path = modelSetting.getPoseFileName(); path = modelHomeDir + path; buffer = CreateBuffer(path); CubismPose pose = CubismPose.create(buffer);
// Java
	String path = modelSetting.getPoseFileName();
	path = modelHomeDir + path;
	buffer = CreateBuffer(path);
	CubismPose pose = CubismPose.create(buffer);

ポーズの適用

ポーズの適用には以下の関数を使用します。

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
// C++
CubismPose::UpdateParameters()
// C++ CubismPose::UpdateParameters()
// C++
CubismPose::UpdateParameters()
Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
// TypeScript
CubismPose.updateParameters()
// TypeScript CubismPose.updateParameters()
// TypeScript
CubismPose.updateParameters()
Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
// Java
CubismPose.updateParameters()
// Java CubismPose.updateParameters()
// Java
CubismPose.updateParameters()

事前にモーションの適用など仮想的なパラメータの計算を済ませておく必要があります。

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
// C++
void LAppModel::Update()
{
const csmFloat32 deltaTimeSeconds = LAppPal::GetDeltaTime();
_userTimeSeconds += deltaTimeSeconds;
/*省略*/
//-----------------------------------------------------------------
_model->LoadParameters(); // 前回セーブされた状態をロード
if (_motionManager->IsFinished())
{
// モーションの再生がない場合、待機モーションの中からランダムで再生する
StartRandomMotion(MotionGroupIdle, PriorityIdle);
}
else
{
motionUpdated = _motionManager->UpdateMotion(_model, deltaTimeSeconds); // モーションを更新
}
_model->SaveParameters(); // 状態を保存
//-----------------------------------------------------------------
/*省略*/
// ポーズの設定
if (_pose != NULL)
{
_pose->UpdateParameters(_model, deltaTimeSeconds);
}
_model->Update();
}
// C++ void LAppModel::Update() { const csmFloat32 deltaTimeSeconds = LAppPal::GetDeltaTime(); _userTimeSeconds += deltaTimeSeconds; /*省略*/ //----------------------------------------------------------------- _model->LoadParameters(); // 前回セーブされた状態をロード if (_motionManager->IsFinished()) { // モーションの再生がない場合、待機モーションの中からランダムで再生する StartRandomMotion(MotionGroupIdle, PriorityIdle); } else { motionUpdated = _motionManager->UpdateMotion(_model, deltaTimeSeconds); // モーションを更新 } _model->SaveParameters(); // 状態を保存 //----------------------------------------------------------------- /*省略*/ // ポーズの設定 if (_pose != NULL) { _pose->UpdateParameters(_model, deltaTimeSeconds); } _model->Update(); }
// C++
	void LAppModel::Update()
    {
        const csmFloat32 deltaTimeSeconds = LAppPal::GetDeltaTime();
        _userTimeSeconds += deltaTimeSeconds;

/*省略*/

        //-----------------------------------------------------------------
        _model->LoadParameters(); // 前回セーブされた状態をロード
        if (_motionManager->IsFinished())
        {
            // モーションの再生がない場合、待機モーションの中からランダムで再生する
            StartRandomMotion(MotionGroupIdle, PriorityIdle);
        }
        else
        {
            motionUpdated = _motionManager->UpdateMotion(_model, deltaTimeSeconds); // モーションを更新
        }
        _model->SaveParameters(); // 状態を保存
        //-----------------------------------------------------------------

/*省略*/

        // ポーズの設定
        if (_pose != NULL)
        {
            _pose->UpdateParameters(_model, deltaTimeSeconds);
        }

        _model->Update();

    }
Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
// TypeScript
/**
* 更新
*/
public update(): void
{
if(this._state != LoadStep.CompleteSetup) return;
const deltaTimeSeconds: number = LAppPal.getDeltaTime();
this._userTimeSeconds += deltaTimeSeconds;
/*省略*/
//--------------------------------------------------------------------------
this._model.loadParameters(); // 前回セーブされた状態をロード
if(this._motionManager.isFinished())
{
// モーションの再生がない場合、待機モーションの中からランダムで再生する
this.startRandomMotion(LAppDefine.MotionGroupIdle, LAppDefine.PriorityIdle);
}
else
{
motionUpdated = this._motionManager.updateMotion(this._model, deltaTimeSeconds); // モーションを更新
}
this._model.saveParameters(); // 状態を保存
//--------------------------------------------------------------------------
/*省略*/
// ポーズの設定
if(this._pose != null)
{
this._pose.updateParameters(this._model, deltaTimeSeconds);
}
this._model.update();
}
// TypeScript /** * 更新 */ public update(): void { if(this._state != LoadStep.CompleteSetup) return; const deltaTimeSeconds: number = LAppPal.getDeltaTime(); this._userTimeSeconds += deltaTimeSeconds; /*省略*/ //-------------------------------------------------------------------------- this._model.loadParameters(); // 前回セーブされた状態をロード if(this._motionManager.isFinished()) { // モーションの再生がない場合、待機モーションの中からランダムで再生する this.startRandomMotion(LAppDefine.MotionGroupIdle, LAppDefine.PriorityIdle); } else { motionUpdated = this._motionManager.updateMotion(this._model, deltaTimeSeconds); // モーションを更新 } this._model.saveParameters(); // 状態を保存 //-------------------------------------------------------------------------- /*省略*/ // ポーズの設定 if(this._pose != null) { this._pose.updateParameters(this._model, deltaTimeSeconds); } this._model.update(); }
// TypeScript
	/**
     * 更新
     */
    public update(): void
    {
        if(this._state != LoadStep.CompleteSetup) return;
        
        const deltaTimeSeconds: number = LAppPal.getDeltaTime();
        this._userTimeSeconds += deltaTimeSeconds;

/*省略*/

        //--------------------------------------------------------------------------
        this._model.loadParameters();   // 前回セーブされた状態をロード
        if(this._motionManager.isFinished())
        {
            // モーションの再生がない場合、待機モーションの中からランダムで再生する
            this.startRandomMotion(LAppDefine.MotionGroupIdle, LAppDefine.PriorityIdle);
            
        }
        else
        {
            motionUpdated = this._motionManager.updateMotion(this._model, deltaTimeSeconds);    // モーションを更新
        }
        this._model.saveParameters(); // 状態を保存
        //--------------------------------------------------------------------------

 /*省略*/
        
        // ポーズの設定
        if(this._pose != null)
        {
            this._pose.updateParameters(this._model, deltaTimeSeconds);
        }

        this._model.update();
    }
Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
// Java
public void update() {
final float deltaTimeSeconds = LAppPal.getDeltaTime();
userTimeSeconds += deltaTimeSeconds;
/*省略*/
// -------------------------------------------------
model.loadParameters(); // 前回セーブされた状態をロード
if (motionManager.isFinished()) {
// モーションの再生がない場合、待機モーションの中からランダムで再生する
startRandomMotion(LAppDefine.MotionGroup.IDLE.getId(), LAppDefine.Priority.IDLE.getPriority());
} else {
isMotionUpdated = motionManager.updateMotion(model, deltaTimeSeconds);
}
model.saveParameters(); // 状態を保存
// -------------------------------------------------
/*省略*/
if (pose != null) {
pose.updateParameters(model, deltaTimeSeconds);
}
model.update();
}
// Java public void update() { final float deltaTimeSeconds = LAppPal.getDeltaTime(); userTimeSeconds += deltaTimeSeconds; /*省略*/ // ------------------------------------------------- model.loadParameters(); // 前回セーブされた状態をロード if (motionManager.isFinished()) { // モーションの再生がない場合、待機モーションの中からランダムで再生する startRandomMotion(LAppDefine.MotionGroup.IDLE.getId(), LAppDefine.Priority.IDLE.getPriority()); } else { isMotionUpdated = motionManager.updateMotion(model, deltaTimeSeconds); } model.saveParameters(); // 状態を保存 // ------------------------------------------------- /*省略*/ if (pose != null) { pose.updateParameters(model, deltaTimeSeconds); } model.update(); }
// Java
	public void update() {
      final float deltaTimeSeconds = LAppPal.getDeltaTime();
      userTimeSeconds += deltaTimeSeconds;

/*省略*/
      
      // -------------------------------------------------
      model.loadParameters(); // 前回セーブされた状態をロード
      if (motionManager.isFinished()) {
        // モーションの再生がない場合、待機モーションの中からランダムで再生する
        startRandomMotion(LAppDefine.MotionGroup.IDLE.getId(), LAppDefine.Priority.IDLE.getPriority());
      } else {
        isMotionUpdated = motionManager.updateMotion(model, deltaTimeSeconds);
      }
      model.saveParameters(); // 状態を保存
      // -------------------------------------------------
/*省略*/

      if (pose != null) {
        pose.updateParameters(model, deltaTimeSeconds);
      }
      model.update();
    }

破棄

モデルが解放されるタイミングでCubismPoseインスタンスも破棄する必要があります。

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
// C++
CubismPose::Delete(pose);
// C++ CubismPose::Delete(pose);
// C++
CubismPose::Delete(pose);
Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
// TypeScript
CubismPose.delete(pose);
// TypeScript CubismPose.delete(pose);
// TypeScript
CubismPose.delete(pose);

SDK for Javaの場合はガベージコレクションに解放を任せているので破棄する必要はありません。

この記事はお役に立ちましたか?
はいいいえ
この記事に関するご意見・
ご要望をお聞かせください。