Update to Cubism 4 SDK for Web R5 beta3

master
wada 2022-06-16 11:48:09 +09:00
parent 375c664d63
commit 55b7a9233e
6 changed files with 417 additions and 193 deletions

View File

@ -4,6 +4,14 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
## [4-r.5-beta.3] - 2022-06-16
### Fixed
* `getDrawableTextureIndices` function in `CubismModel` has been renamed to `getDrawableTextureIndex` because the name was not correct.
* `getDrawableTextureIndices` function is marked as deprecated.
* Fix physics system behaviour when exists Physics Fps Setting in .physics3.json.
## [4-r.5-beta.2] - 2022-06-02
@ -89,6 +97,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
* Reformat code using Prettier and ESLint.
[4-r.5-beta.3]: https://github.com/Live2D/CubismWebFramework/compare/4-r.5-beta.2...4-r.5-beta.3
[4-r.5-beta.2]: https://github.com/Live2D/CubismWebFramework/compare/4-r.5-beta.1...4-r.5-beta.2
[4-r.5-beta.1]: https://github.com/Live2D/CubismWebFramework/compare/4-r.4...4-r.5-beta.1
[4-r.4]: https://github.com/Live2D/CubismWebFramework/compare/4-r.3...4-r.4

View File

@ -646,11 +646,23 @@ export class CubismModel {
}
/**
* @deprecated
* getDrawableTextureIndex
*
* Drawable
* @param drawableIndex Drawable
* @return drawable
*/
public getDrawableTextureIndices(drawableIndex: number): number {
return this.getDrawableTextureIndex(drawableIndex);
}
/**
* Drawable
* @param drawableIndex Drawable
* @return drawable
*/
public getDrawableTextureIndex(drawableIndex: number): number {
const textureIndices: Int32Array = this._model.drawables.textureIndices;
return textureIndices[drawableIndex];
}

View File

@ -7,6 +7,7 @@
import { CubismMath } from '../math/cubismmath';
import { CubismVector2 } from '../math/cubismvector2';
import { csmVector } from '../type/csmvector';
import { CubismModel } from '../model/cubismmodel';
import {
CubismPhysicsInput,
@ -34,6 +35,9 @@ const MaximumWeight = 100.0;
// Constant of threshold of movement.
const MovementThreshold = 0.001;
// Constant of maximum allowed delta time
const MaxDeltaTime = 5.0;
/**
*
*/
@ -64,196 +68,6 @@ export class CubismPhysics {
}
}
/**
*
* @param model
* @param deltaTimeSeconds []
*/
public evaluate(model: CubismModel, deltaTimeSeconds: number): void {
let totalAngle: { angle: number };
let weight: number;
let radAngle: number;
let outputValue: number;
const totalTranslation: CubismVector2 = new CubismVector2();
let currentSetting: CubismPhysicsSubRig;
let currentInput: CubismPhysicsInput[];
let currentOutput: CubismPhysicsOutput[];
let currentParticles: CubismPhysicsParticle[];
let parameterValue: Float32Array;
let parameterMaximumValue: Float32Array;
let parameterMinimumValue: Float32Array;
let parameterDefaultValue: Float32Array;
parameterValue = model.getModel().parameters.values;
parameterMaximumValue = model.getModel().parameters.maximumValues;
parameterMinimumValue = model.getModel().parameters.minimumValues;
parameterDefaultValue = model.getModel().parameters.defaultValues;
for (
let settingIndex = 0;
settingIndex < this._physicsRig.subRigCount;
++settingIndex
) {
totalAngle = { angle: 0.0 };
totalTranslation.x = 0.0;
totalTranslation.y = 0.0;
currentSetting = this._physicsRig.settings.at(settingIndex);
currentInput = this._physicsRig.inputs.get(currentSetting.baseInputIndex);
currentOutput = this._physicsRig.outputs.get(
currentSetting.baseOutputIndex
);
currentParticles = this._physicsRig.particles.get(
currentSetting.baseParticleIndex
);
// Load input parameters
for (let i = 0; i < currentSetting.inputCount; ++i) {
weight = currentInput[i].weight / MaximumWeight;
if (currentInput[i].sourceParameterIndex == -1) {
currentInput[i].sourceParameterIndex = model.getParameterIndex(
currentInput[i].source.id
);
}
currentInput[i].getNormalizedParameterValue(
totalTranslation,
totalAngle,
parameterValue[currentInput[i].sourceParameterIndex],
parameterMinimumValue[currentInput[i].sourceParameterIndex],
parameterMaximumValue[currentInput[i].sourceParameterIndex],
parameterDefaultValue[currentInput[i].sourceParameterIndex],
currentSetting.normalizationPosition,
currentSetting.normalizationAngle,
currentInput[i].reflect,
weight
);
}
radAngle = CubismMath.degreesToRadian(-totalAngle.angle);
totalTranslation.x =
totalTranslation.x * CubismMath.cos(radAngle) -
totalTranslation.y * CubismMath.sin(radAngle);
totalTranslation.y =
totalTranslation.x * CubismMath.sin(radAngle) +
totalTranslation.y * CubismMath.cos(radAngle);
// Calculate particles position.
updateParticles(
currentParticles,
currentSetting.particleCount,
totalTranslation,
totalAngle.angle,
this._options.wind,
MovementThreshold * currentSetting.normalizationPosition.maximum,
deltaTimeSeconds,
AirResistance
);
// Update output parameters.
for (let i = 0; i < currentSetting.outputCount; ++i) {
const particleIndex = currentOutput[i].vertexIndex;
if (
particleIndex < 1 ||
particleIndex >= currentSetting.particleCount
) {
break;
}
if (currentOutput[i].destinationParameterIndex == -1) {
currentOutput[i].destinationParameterIndex = model.getParameterIndex(
currentOutput[i].destination.id
);
}
const translation: CubismVector2 = new CubismVector2();
translation.x =
currentParticles[particleIndex].position.x -
currentParticles[particleIndex - 1].position.x;
translation.y =
currentParticles[particleIndex].position.y -
currentParticles[particleIndex - 1].position.y;
outputValue = currentOutput[i].getValue(
translation,
currentParticles,
particleIndex,
currentOutput[i].reflect,
this._options.gravity
);
const destinationParameterIndex: number =
currentOutput[i].destinationParameterIndex;
const outParameterValue: Float32Array =
!Float32Array.prototype.slice && 'subarray' in Float32Array.prototype
? JSON.parse(
JSON.stringify(
parameterValue.subarray(destinationParameterIndex)
)
) // 値渡しするため、JSON.parse, JSON.stringify
: parameterValue.slice(destinationParameterIndex);
updateOutputParameterValue(
outParameterValue,
parameterMinimumValue[destinationParameterIndex],
parameterMaximumValue[destinationParameterIndex],
outputValue,
currentOutput[i]
);
// 値を反映
for (
let offset: number = destinationParameterIndex, outParamIndex = 0;
offset < parameterValue.length;
offset++, outParamIndex++
) {
parameterValue[offset] = outParameterValue[outParamIndex];
}
}
}
}
/**
*
* @param options
*/
public setOptions(options: Options): void {
this._options = options;
}
/**
*
* @return
*/
public getOption(): Options {
return this._options;
}
/**
*
*/
public constructor() {
this._physicsRig = null;
// set default options
this._options = new Options();
this._options.gravity.y = -1.0;
this._options.gravity.x = 0;
this._options.wind.x = 0;
this._options.wind.y = 0;
}
/**
*
*/
public release(): void {
this._physicsRig = void 0;
this._physicsRig = null;
}
/**
* physics3.json
* @param physicsJson physics3.json
@ -268,6 +82,8 @@ export class CubismPhysics {
this._physicsRig.wind = json.getWind();
this._physicsRig.subRigCount = json.getSubRigCount();
this._physicsRig.fps = json.getFps();
this._physicsRig.settings.updateSize(
this._physicsRig.subRigCount,
CubismPhysicsSubRig,
@ -289,6 +105,9 @@ export class CubismPhysics {
true
);
this._currentRigOutputs.clear();
this._previousRigOutputs.clear();
let inputIndex = 0,
outputIndex = 0,
particleIndex = 0;
@ -352,6 +171,18 @@ export class CubismPhysics {
this._physicsRig.settings.at(i).outputCount = json.getOutputCount(i);
this._physicsRig.settings.at(i).baseOutputIndex = outputIndex;
let currentRigOutput = new PhysicsOutput();
currentRigOutput.output.resize(
this._physicsRig.settings.at(i).outputCount
);
this._currentRigOutputs.pushBack(currentRigOutput);
let previousRigOutput = new PhysicsOutput();
previousRigOutput.output.resize(
this._physicsRig.settings.at(i).outputCount
);
this._previousRigOutputs.pushBack(previousRigOutput);
for (let j = 0; j < this._physicsRig.settings.at(i).outputCount; ++j) {
this._physicsRig.outputs.at(outputIndex + j).destinationParameterIndex =
-1;
@ -422,6 +253,345 @@ export class CubismPhysics {
json = null;
}
/**
*
*
* Pendulum interpolation weights
*
*
* The result of the pendulum calculation is saved and
* the output to the parameters is interpolated with the saved previous result of the pendulum calculation.
*
* [1][2]
* The figure shows the interpolation between [1] and [2].
*
*
* The weight of the interpolation are determined by the current time seen between
* the latest pendulum calculation timing and the next timing.
*
* [2][4](3)
* Figure shows the weight of position (3) as seen between [2] and [4].
*
*
* As an interpretation, the pendulum calculation and weights are misaligned.
*
* physics3.jsonFPS
* If there is no FPS information in physics3.json, it is always set in the previous pendulum state.
*
*
* The purpose of this specification is to avoid the quivering appearance caused by deviations from the interpolation range.
*
* ------------ time -------------->
*
* |+++++|------| <- weight
* ==[1]====#=====[2]---(3)----(4)
* ^ output contents
*
* 1:_previousRigOutputs
* 2:_currentRigOutputs
* 3:_currentRemainTime (now rendering)
* 4:next particles timing
* @param model
* @param deltaTimeSeconds []
*/
public evaluate(model: CubismModel, deltaTimeSeconds: number): void {
let totalAngle: { angle: number };
let weight: number;
let radAngle: number;
let outputValue: number;
const totalTranslation: CubismVector2 = new CubismVector2();
let currentSetting: CubismPhysicsSubRig;
let currentInput: CubismPhysicsInput[];
let currentOutput: CubismPhysicsOutput[];
let currentParticles: CubismPhysicsParticle[];
if (0.0 >= deltaTimeSeconds) {
return;
}
let parameterValue: Float32Array;
let parameterMaximumValue: Float32Array;
let parameterMinimumValue: Float32Array;
let parameterDefaultValue: Float32Array;
let physicsDeltaTime: number;
this._currentRemainTime += deltaTimeSeconds;
if (this._currentRemainTime > MaxDeltaTime) {
this._currentRemainTime = 0.0;
}
parameterValue = model.getModel().parameters.values;
parameterMaximumValue = model.getModel().parameters.maximumValues;
parameterMinimumValue = model.getModel().parameters.minimumValues;
parameterDefaultValue = model.getModel().parameters.defaultValues;
this._parameterCache = new Float32Array(model.getParameterCount());
if (this._physicsRig.fps > 0.0) {
physicsDeltaTime = 1.0 / this._physicsRig.fps;
} else {
physicsDeltaTime = deltaTimeSeconds;
}
while (this._currentRemainTime >= physicsDeltaTime) {
// copyRigOutputs _currentRigOutputs to _previousRigOutputs
for (
let settingIndex = 0;
settingIndex < this._physicsRig.subRigCount;
++settingIndex
) {
currentSetting = this._physicsRig.settings.at(settingIndex);
currentOutput = this._physicsRig.outputs.get(
currentSetting.baseOutputIndex
);
for (let i = 0; i < currentSetting.outputCount; ++i) {
this._previousRigOutputs.at(settingIndex).output[i] =
this._currentRigOutputs.at(settingIndex).output[i];
}
}
for (let j = 0; j < model.getParameterCount(); ++j) {
this._parameterCache[j] = parameterValue[j];
}
for (
let settingIndex = 0;
settingIndex < this._physicsRig.subRigCount;
++settingIndex
) {
totalAngle = { angle: 0.0 };
totalTranslation.x = 0.0;
totalTranslation.y = 0.0;
currentSetting = this._physicsRig.settings.at(settingIndex);
currentInput = this._physicsRig.inputs.get(
currentSetting.baseInputIndex
);
currentOutput = this._physicsRig.outputs.get(
currentSetting.baseOutputIndex
);
currentParticles = this._physicsRig.particles.get(
currentSetting.baseParticleIndex
);
// Load input parameters
for (let i = 0; i < currentSetting.inputCount; ++i) {
weight = currentInput[i].weight / MaximumWeight;
if (currentInput[i].sourceParameterIndex == -1) {
currentInput[i].sourceParameterIndex = model.getParameterIndex(
currentInput[i].source.id
);
}
currentInput[i].getNormalizedParameterValue(
totalTranslation,
totalAngle,
this._parameterCache[currentInput[i].sourceParameterIndex],
parameterMinimumValue[currentInput[i].sourceParameterIndex],
parameterMaximumValue[currentInput[i].sourceParameterIndex],
parameterDefaultValue[currentInput[i].sourceParameterIndex],
currentSetting.normalizationPosition,
currentSetting.normalizationAngle,
currentInput[i].reflect,
weight
);
}
radAngle = CubismMath.degreesToRadian(-totalAngle.angle);
totalTranslation.x =
totalTranslation.x * CubismMath.cos(radAngle) -
totalTranslation.y * CubismMath.sin(radAngle);
totalTranslation.y =
totalTranslation.x * CubismMath.sin(radAngle) +
totalTranslation.y * CubismMath.cos(radAngle);
// Calculate particles position.
updateParticles(
currentParticles,
currentSetting.particleCount,
totalTranslation,
totalAngle.angle,
this._options.wind,
MovementThreshold * currentSetting.normalizationPosition.maximum,
physicsDeltaTime,
AirResistance
);
// Update output parameters.
for (let i = 0; i < currentSetting.outputCount; ++i) {
const particleIndex = currentOutput[i].vertexIndex;
if (
particleIndex < 1 ||
particleIndex >= currentSetting.particleCount
) {
break;
}
if (currentOutput[i].destinationParameterIndex == -1) {
currentOutput[i].destinationParameterIndex =
model.getParameterIndex(currentOutput[i].destination.id);
}
const translation: CubismVector2 = new CubismVector2();
translation.x =
currentParticles[particleIndex].position.x -
currentParticles[particleIndex - 1].position.x;
translation.y =
currentParticles[particleIndex].position.y -
currentParticles[particleIndex - 1].position.y;
outputValue = currentOutput[i].getValue(
translation,
currentParticles,
particleIndex,
currentOutput[i].reflect,
this._options.gravity
);
this._currentRigOutputs.at(settingIndex).output[i] = outputValue;
const destinationParameterIndex: number =
currentOutput[i].destinationParameterIndex;
const outParameterCache: Float32Array =
!Float32Array.prototype.slice &&
'subarray' in Float32Array.prototype
? JSON.parse(
JSON.stringify(
this._parameterCache.subarray(destinationParameterIndex)
)
) // 値渡しするため、JSON.parse, JSON.stringify
: this._parameterCache.slice(destinationParameterIndex);
updateOutputParameterValue(
outParameterCache,
parameterMinimumValue[destinationParameterIndex],
parameterMaximumValue[destinationParameterIndex],
outputValue,
currentOutput[i]
);
// 値を反映
for (
let offset: number = destinationParameterIndex, outParamIndex = 0;
offset < this._parameterCache.length;
offset++, outParamIndex++
) {
this._parameterCache[offset] = outParameterCache[outParamIndex];
}
}
}
this._currentRemainTime -= physicsDeltaTime;
}
const alpha: number = this._currentRemainTime / physicsDeltaTime;
this.interpolate(model, alpha);
}
/**
*
*
* @param model
* @param weight
*/
public interpolate(model: CubismModel, weight: number): void {
let currentOutput: CubismPhysicsOutput[];
let currentSetting: CubismPhysicsSubRig;
let parameterValue: Float32Array;
let parameterMaximumValue: Float32Array;
let parameterMinimumValue: Float32Array;
parameterValue = model.getModel().parameters.values;
parameterMaximumValue = model.getModel().parameters.maximumValues;
parameterMinimumValue = model.getModel().parameters.minimumValues;
for (
let settingIndex = 0;
settingIndex < this._physicsRig.subRigCount;
++settingIndex
) {
currentSetting = this._physicsRig.settings.at(settingIndex);
currentOutput = this._physicsRig.outputs.get(
currentSetting.baseOutputIndex
);
// Load input parameters.
for (let i = 0; i < currentSetting.outputCount; ++i) {
const destinationParameterIndex: number =
currentOutput[i].destinationParameterIndex;
const outParameterValue: Float32Array =
!Float32Array.prototype.slice && 'subarray' in Float32Array.prototype
? JSON.parse(
JSON.stringify(
parameterValue.subarray(destinationParameterIndex)
)
) // 値渡しするため、JSON.parse, JSON.stringify
: parameterValue.slice(destinationParameterIndex);
updateOutputParameterValue(
outParameterValue,
parameterMinimumValue[destinationParameterIndex],
parameterMaximumValue[destinationParameterIndex],
this._previousRigOutputs.at(settingIndex).output[i] * (1 - weight) +
this._currentRigOutputs.at(settingIndex).output[i] * weight,
currentOutput[i]
);
// 値を反映
for (
let offset: number = destinationParameterIndex, outParamIndex = 0;
offset < parameterValue.length;
offset++, outParamIndex++
) {
parameterValue[offset] = outParameterValue[outParamIndex];
}
}
}
}
/**
*
* @param options
*/
public setOptions(options: Options): void {
this._options = options;
}
/**
*
* @return
*/
public getOption(): Options {
return this._options;
}
/**
*
*/
public constructor() {
this._physicsRig = null;
// set default options
this._options = new Options();
this._options.gravity.y = -1.0;
this._options.gravity.x = 0.0;
this._options.wind.x = 0.0;
this._options.wind.y = 0.0;
this._currentRigOutputs = new csmVector<PhysicsOutput>();
this._previousRigOutputs = new csmVector<PhysicsOutput>();
this._currentRemainTime = 0.0;
this._parameterCache = null;
}
/**
*
*/
public release(): void {
this._physicsRig = void 0;
this._physicsRig = null;
}
/**
*
*/
@ -449,7 +619,7 @@ export class CubismPhysics {
strand[0].velocity = new CubismVector2(0.0, 0.0);
strand[0].force = new CubismVector2(0.0, 0.0);
// Initialize paritcles.
// Initialize particles.
for (let i = 1; i < currentSetting.particleCount; ++i) {
radius = new CubismVector2(0.0, 0.0);
radius.y = strand[i].radius;
@ -475,6 +645,13 @@ export class CubismPhysics {
_physicsRig: CubismPhysicsRig; // 物理演算のデータ
_options: Options; // オプション
_currentRigOutputs: csmVector<PhysicsOutput>; ///< 最新の振り子計算の結果
_previousRigOutputs: csmVector<PhysicsOutput>; ///< 一つ前の振り子計算の結果
_currentRemainTime: number; ///< 物理演算が処理していない時間
_parameterCache: Float32Array; ///< Evaluateで利用するパラメータのキャッシュ
}
/**
@ -490,6 +667,17 @@ export class Options {
wind: CubismVector2; // 風の方向
}
/**
*
*/
export class PhysicsOutput {
constructor() {
this.output = new csmVector<number>(0);
}
output: csmVector<number>; // 物理演算出力結果
}
/**
* Gets sign.
*

View File

@ -208,6 +208,7 @@ export class CubismPhysicsRig {
this.particles = new csmVector<CubismPhysicsParticle>();
this.gravity = new CubismVector2(0, 0);
this.wind = new CubismVector2(0, 0);
this.fps = 0.0;
}
subRigCount: number; // 物理演算の物理点の個数
@ -217,6 +218,7 @@ export class CubismPhysicsRig {
particles: csmVector<CubismPhysicsParticle>; // 物理演算の物理点のリスト
gravity: CubismVector2; // 重力
wind: CubismVector2; // 風
fps: number; //物理演算動作FPS
}
// Namespace definition for compatibility.

View File

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

View File

@ -502,7 +502,7 @@ export class CubismClippingManager_WebGL {
// チャンネルも切り替える必要がある(A,R,G,B)
renderer.setClippingContextBufferForMask(clipContext);
renderer.drawMesh(
model.getDrawableTextureIndices(clipDrawIndex),
model.getDrawableTextureIndex(clipDrawIndex),
model.getDrawableVertexIndexCount(clipDrawIndex),
model.getDrawableVertexCount(clipDrawIndex),
model.getDrawableVertexIndices(clipDrawIndex),
@ -2115,7 +2115,7 @@ export class CubismRenderer_WebGL extends CubismRenderer {
this.setIsCulling(this.getModel().getDrawableCulling(drawableIndex));
this.drawMesh(
this.getModel().getDrawableTextureIndices(drawableIndex),
this.getModel().getDrawableTextureIndex(drawableIndex),
this.getModel().getDrawableVertexIndexCount(drawableIndex),
this.getModel().getDrawableVertexCount(drawableIndex),
this.getModel().getDrawableVertexIndices(drawableIndex),