Parameter Operation
Updated: 12/08/2022
CubismIdHandle
To identify the parameters in Cubism as the final step, obtain the Index, which is a sequence of parameters.
When identifying by ID, the ID, which is a string of parameters, is matched.
The Native and Web Frameworks provide a type called CubismIdHandle to reduce the cost of collation calculations.
Java does not allow for a new type to be defined, so it is handled differently.
Native
The actual CubismIdHandle type is a pointer type of the CubismId class and can be obtained by the CubismIdManager::GetId function of the managing class.
The CubismIdManager::GetId function returns the same pointer if the strings are the same,
so you can compare instances of CubismIdHandle to each other and guarantee that they are the same string if they have the same pointer address.
CubismIdManager instances can be accessed with the static CubismFramework::GetIdManager function.
// C++ CubismIdHandle idA = CubismFramework::GetIdManager()->GetId("ParamAngleX") CubismIdHandle idB = CubismFramework::GetIdManager()->GetId("ParamAngleX") CubismIdHandle idC = CubismFramework::GetIdManager()->GetId("ParamAngleY") csmBool ab = (idA == idB); //true csmBool bc = (idB == idC); //false
The value manipulation functions provide two methods of access: by Index and by CubismIdHandle.
Web
The actual CubismIdHandle type is an object type of the CubismId class and can be obtained by the CubismIdManager.getId function of the managing class.
The CubismIdManager.getId function returns the same instance if the strings are the same,
so you can compare instances of CubismIdHandle to each other and guarantee that they are the same string if they are the same object.
CubismIdManager instances can be accessed with the static CubismFramework.getIdManager function.
The value manipulation functions provide two methods of access: by Index and by CubismIdHandle.
// TypeScript let idA: CubismIdHandle = CubismFramework.getIdManager().getId("ParamAngleX"); let idB: CubismIdHandle = CubismFramework.getIdManager().getId("ParamAngleX"); let idC: CubismIdHandle = CubismFramework.getIdManager().getId("ParamAngleY"); let ab = idA.isEqual(idB); //true let bc = idB.isEqual(idC); //false
Java
SDK for Java unlike the above two, the CubismIdHandle type does not exist in the CubismIdManager.getId function of the management class.
The function CubismIdManager.getId of the management class returns CubismId type.
Since CubismId overrides the equals function, comparing CubismId with each other using the equals function will guarantee that they are the same string.
CubismIdManager instances can be accessed with the static CubismFramework.getIdManager function.
// Java CubismId idA = CubismFramework.getIdManager().getId("ParamAngleX"); CubismId idB = CubismFramework.getIdManager().getId("ParamAngleX"); CubismId idC = CubismFramework.getIdManager().getId("ParamAngleY"); boolean ab = idA.equals(idB); // true boolean bc = idB.equals(idC); // false
The value manipulation functions provide two methods of access: by Index and by CubismId.
Set Parameters
Parameters are normally played back from the motion, but values can also be specified directly.
There are three types of functions to manipulate parameters, as shown below, for each method of calculating the value to be applied.
1. Overwrite values
Use one of the following functions
- CubismModel::SetParameterValue function in Native (C++)
- CubismModel.setParameterValueById function in Web (TypeScript)
- CubismModel.setParameterValue function in Java
Set the ID or index of the parameter for the first argument, the value for the second argument, and the degree of influence for the third argument.
The degree of influence can be omitted, in which case it will be 1.
For example, setting it to 0.5 would leave 50% of the previous value affected.
Example
// C++ _model->SetParameterValue(CubismFramework::GetIdManager()->GetId("ParamAngleX"), 30.0f, 1.0f);
// TypeScript _model.setParameterValueById(CubismFramework.getIdManager().getId("ParamAngleX"), 30.0, 1.0);
// Java model.setParameterValue(CubismFramework.getIdManager().getId("ParamAngleX"), 30.0f, 1.0f);
2. Add to current value
Use one of the following functions
- CubismModel::AddParameterValue function in Native (C++)
- CubismModel.addParameterValueById function in Web (TypeScript)
- CubismModel.addParameterValue function in Java
The arguments are the same as for any of the following functions
- CubismModel::SetParameterValue function in Native (C++)
- CubismModel.setParameterValueById function in Web (TypeScript)
- CubismModel.setParameterValue function in Java
The value set by this function is added to the value currently set for that parameter.
Example
// C++ _model->AddParameterValue(CubismFramework::GetIdManager()->GetId("ParamAngleX"), 1.0f, 1.0f);
// TypeScript _model.addParameterValueById(CubismFramework.getIdManager().getId("ParamAngleX"), 1.0, 1.0);
// Java model.addParameterValue(CubismFramework.getIdManager().getId("ParamAngleX"), 1.0f, 1.0f);
3. Multiply by current value
Use one of the following functions
- CubismModel::MultiplyParameterValue function in Native (C++)
- CubismModel.multiplyParameterValueById function in Web (TypeScript)
- CubismModel.multiplyParameterValue function in Java
The arguments are the same as for any of the following functions
- CubismModel::MultiplyParameterValue function
- CubismModel.multiplyParameterValueById function in Web (TypeScript)
- CubismModel.multiplyParameterValue function in Java
The value set by this function is multiplied by the value currently set for that parameter.
Example
// C++ _model->MultiplyParameterValue(CubismFramework::GetIdManager()->GetId("ParamAngleX"), 2.0f, 1.0f);
// TypeScript _model.multiplyParameterValueById(CubismFramework.getIdManager().getId("ParamAngleX"), 2.0, 1.0);
// Java model.multiplyParameterValue(CubismFramework.getIdManager().getId("ParamAngleX"), 2.0f, 1.0f);
In addition, one of the following functions is used to obtain the current parameter values.
- CubismModel::GetParameterValue function in Native(C++)
- CubismModel.getParameterValueById function in Web(TypeScript)
- CubismModel.getParameterValue function in Java
Specify Parameters by Index
There are two ways to specify parameter IDs: by ID type, which is generated from a string, or by index.
Indexing is recommended if the frequency of calls is high, since it is faster to use indexes after caching them, such as by acquiring indexes in advance.
Parameter indices can be obtained with any of the following functions
- CubismModel::GetParameterIndex function in Native (C++)
- CubismModel.getParameterIndex function in Web (TypeScript)
- CubismModel.getParameterIndex function in Java
Example
// C++ // During initialization csmInt32 paramAngleX; paramAngleX = _model->GetParameterIndex(CubismFramework::GetIdManager()->GetId("ParamAngleX")); // When setting parameters _model->SetParameterValue( paramAngleX, 30.0f , 1.0f);
// TypeScript // During initialization let paramAngleX: number; paramAngleX = _model.getParameterIndex(CubismFramework.getIdManager().getId("ParamAngleX")); // When setting parameters _model.setParameterValueByIndex(paramAngleX, 30.0, 1.0);
// Java // During initialization int paramAngleXIndex; paramAngleXIndex = model.getParameterIndex(CubismFramework.getIdManager().getId("ParamAngleX")); // When setting parameters model.setParameterValue(paramAngleXIndex, 30.0f, 1.0f);
Save and Restore Parameter Values
To temporarily store the current parameter values of a model, use one of the following functions
- CubismModel::SaveParameters function in Native (C++)
- CubismModel.saveParameters function in Web (TypeScript)
- CubismModel.saveParameters function in Java
Use one of the following functions to restore temporarily saved model parameter values
- CubismModel::LoadParameters function in Native (C++)
- CubismModel.loadParameters function in Web (TypeScript)
- CubismModel.loadParameters function in Java
Example
// C++ // During initialization csmInt32 paramAngleX; paramAngleX = _model->GetParameterIndex(CubismFramework::GetIdManager()->GetId("ParamAngleX")); // Set the parameter to 30 _model->SetParameterValue( paramAngleX, 30.0f ,1.0f ); // Temporarily save all current parameter values _model->SaveParameters(); // Set to 0 _model->SetParameterValue( paramAngleX, 0.0f ,1.0f ); //output: value = 0 printf("value = %f",_model->GetParameterValue( paramAngleX ) ); // Restore the state at the last saveParam _model->LoadParameters(); //output: value = 30 printf("value = %f",_model->GetParameterValue( paramAngleX ) );
// TypeScript // During initialization let paramAngleX: number; paramAngleX = _model.getParameterIndex(CubismFramework.getIdManager().getId("ParamAngleX")); // Set the parameter to 30 _model.setParameterValueByIndex(paramAngleX, 30.0, 1.0); // Temporarily save all current parameter values _model.saveParameters(); // Set to 0 _model.setParameterValue(paramAngleX, 0.0, 1.0); // output: value = 0 LAppPal.printLog("value = {0}", _model.getParameterValueByIndex(paramAngleX)); // Restore the state at the last saveParam _model.loadParameters(); // output: value = 30 LAppPal.printLog("value = {0}", _model.getParameterValueByIndex(paramAngleX));
// Java // During initialization int paramAngleXIndex; paramAngleXIndex = model.getParameterIndex(CubismFramework.getIdManager().getId("ParamAngleX")); // Set the parameter to 30 model.setParameterValue(paramAngleXIndex, 30.0f, 1.0f); // Temporarily save all current parameter values. model.saveParameters(); // Set to 0 model.setParameterValue(paramAngleXIndex, 0.0f, 1.0f); // output: value = 0 LAppPal.printLog("value = " + model.getParametetValue(paramAngleXIndex)); // Restore the state at the last saveParameters. model.loadParameters(); // output: value = 30 LAppPal.printLog("value = " + model.getParameterValue(paramAngleXIndex));
Usage of SaveParameters and LoadParameters in LAppModel::Update
SaveParameters and LoadParameters as APIs save and call values.
The actual LAppModel::Update function calls LoadParameters to reset to the previous status,
applies motion playback, and then executes SaveParameters.
// C++ void LAppModel::Update() { const csmFloat32 deltaTimeSeconds = LAppPal::GetDeltaTime(); _userTimeSeconds += deltaTimeSeconds; _dragManager->Update(deltaTimeSeconds); _dragX = _dragManager->GetX(); _dragY = _dragManager->GetY(); csmBool motionUpdated = false; //----------------------------------------------------------------- _model->LoadParameters(); if (_motionManager->IsFinished()) { StartRandomMotion(MotionGroupIdle, PriorityIdle); } else { motionUpdated = _motionManager->UpdateMotion(_model, deltaTimeSeconds); } _model->SaveParameters(); //----------------------------------------------------------------- if (!motionUpdated) { if (_eyeBlink ! = NULL) { _eyeBlink->UpdateParameters(_model, deltaTimeSeconds); // Set } } if (_expressionManager ! = NULL) { _expressionManager->UpdateMotion(_model, deltaTimeSeconds); // Add Set Mult } _model->AddParameterValue(_idParamAngleX, _dragX * 30); _model->AddParameterValue(_idParamAngleY, _dragY * 30); _model->AddParameterValue(_idParamAngleZ, _dragX * _dragY * -30); _model->AddParameterValue(_idParamBodyAngleX, _dragX * 10); _model->AddParameterValue(_idParamEyeBallX, _dragX); _model->AddParameterValue(_idParamEyeBallY, _dragY); if (_breath ! = NULL) { _breath->UpdateParameters(_model, deltaTimeSeconds); // Add } if (_physics ! = NULL) { _physics->Evaluate(_model, deltaTimeSeconds); // Set } if (_lipSync) { csmFloat32 value = 0; for (csmUint32 i = 0; i < _lipSyncIds.GetSize(); ++i) { _model->AddParameterValue(_lipSyncIds[i], value, 0.8f); } } if (_pose ! = NULL) { _pose->UpdateParameters(_model, deltaTimeSeconds); // No operations on parameters. } _model->Update(); }
// TypeScript public update(): void { if(this._state ! = LoadStep.CompleteSetup) return; const deltaTimeSeconds: number = LAppPal.getDeltaTime(); this._userTimeSeconds += deltaTimeSeconds; this._dragManager.update(deltaTimeSeconds); this._dragX = this._dragManager.getX(); this._dragY = this._dragManager.getY(); let motionUpdated = false; //-------------------------------------------------------------------------- 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(!motionUpdated) { if(this._eyeBlink ! = null) { this._eyeBlink.updateParameters(this._model, deltaTimeSeconds); // Set } } if(this._expressionManager ! = null) { this._expressionManager.updateMotion(this._model, deltaTimeSeconds); // Add Set Mult } this._model.addParameterValueById(this._idParamAngleX, this._dragX * 30); this._model.addParameterValueById(this._idParamAngleY, this._dragY * 30); this._model.addParameterValueById(this._idParamAngleZ, this._dragX * this._dragY * -30); this._model.addParameterValueById(this._idParamBodyAngleX, this._dragX * 10); this._model.addParameterValueById(this._idParamEyeBallX, this._dragX); this._model.addParameterValueById(this._idParamEyeBallY, this._dragY); if(this._breath ! = null) { this._breath.updateParameters(this._model, deltaTimeSeconds); // Add } if(this._physics ! = null) { this._physics.evaluate(this._model, deltaTimeSeconds); // Set } if(this._lipsync) { let value: number = 0; for(let i: number = 0; i < this._lipSyncIds.getSize(); ++i) { this._model.addParameterValueById(this._lipSyncIds.at(i), value, 0.8); } } if(this._pose ! = null) { this._pose.updateParameters(this._model, deltaTimeSeconds); // No operations on parameters. } this._model.update(); }
// Java public void update() { final float deltaTimeSeconds = LAppPal.getDeltaTime(); userTimeSeconds += deltaTimeSeconds; dragManager.update(deltaTimeSeconds); dragX = dragManager.getX(); dragY = dragManager.getY(); boolean isMotionUpdated = false; //-------------------------------------------------------------- model.loadParameters(); // If there is no motion playback, playback is performed at random from among the standby motions. if (motionManager.isFinished()) { startRandomMotion(LAppDefine.MotionGroup.IDLE.getId(), LAppDefine.Priority.IDLE.getPriority()); } else { isMotionUpdated = motionManager.updateMotion(model, deltaTimeSeconds); } model.saveParameters(); // ------------------------------------------------------------- // Blink only when main motion is not updated. if (!isMotionUpdated) { if (eyeBlink ! = null) { eyeBlink.updateParameters(model, deltaTimeSeconds); // Set } } if (expressionManager ! = null) { expressionManager.updateMotion(model, deltaTimeSeconds); // Add Set Mult } // Adjust face orientation by dragging. model.addParameterValue(idParamAngleX, dragX * 30); model.addParameterValue(idParamAngleY, dragY * 30); model.addParameterValue(idParamAngleZ, dragX * dragY * (-30)); // Adjust body orientation by dragging. model.addParameterValue(idParamBodyAngleX, dragX * 10); // Adjust eye orientation by dragging. model.addParameterValue(idParamEyeBallX, dragX); model.addParameterValue(idParamEyeBallY, dragY); if (breath ! = null) { breath.updateParameters(model, deltaTimeSeconds); // Add } if (physics ! = null) { physics.evaluate(model, deltaTimeSeconds); // Set } if (lipSync) { // For real-time lip-sync, get the volume from the system and enter a value in the range between 0 and 1. float value = 0.0f; for (CubismId lipSyncId : lipSyncIds) { model.addParameterValue(lipSyncId, value, 0.8f); } } if (pose ! = null) { pose.updateParameters(model, deltaTimeSeconds); // No operations on parameters. } model.update(); }
The purpose of this method is to create a base for additive and multiplicative calculations by overwriting parameters
that were not played or not specified by motion playback with values before any other operations in Update.
Without this feature, an Add to an unspecified parameter in a motion will cause the value to be added at each update, resulting in a out-of-range value.
Although you can create a base for addition and multiplication with only Load before motion playback,
you can retain the last state of the motion by including Save after motion playback.
Get and Set the Opacity of a Part
Use one of the following functions to set the opacity of the part
- CubismModel::SetPartOpacity function in Native (C++)
- CubismModel.setPartOpacityById function in Web (TypeScript)
- CubismModel.setPartOpactiy function in Java
Use one of the following functions to obtain the opacity of a part
- CubismModel::GetPartOpacity function in Native (C++)
- CubismModel.getPartOpacityById function in Web (TypeScript)
- CubismModel.getPartOpacity function in Java
Example
// C++ // Set opacity of face parts to 0.5 _model->SetPartOpacity( CubismFramework::GetIdManager()->GetId("PartArmLB001"),0.5f );
// TypeScript // Set opacity of face parts to 0.5 _model.setPartOpacityById(CubismFramework.getIdManager().getId("PartArmLB001"), 0.5);
// Java // Set opacity of face parts to 0.5 model.setPartOpacity(CubismFramework.getIdManager().getId("PartArmLB001"), 0.5f);
Timing and Cost of Parameter Application
When setting parameters, only the parameter values are rewritten and no vertex calculations are performed.
After the parameters are changed, the vertices are calculated by one of the following functions.
- CubismModel::Updatel function in Native (C++)
- CubismModel.updatel function in Web (TypeScript)
- CubismModel.updatel function in Java
The model is then drawn after the parameters are applied using one of the following functions.
- CubismRenderer::DrawModel function in Native (C++)
- CubismRenderer.drawModel function in Web (TypeScript)
- CubismRenderer.drawModel function in Java
Importance of the Order in Which Parameters Are Calculated
There are three types of parameter operations: overwrite, add, and multiply.
Performing the multiplication calculation first does not affect later additions or overwrites.
If the overwrite is performed last, all previous calculation results are ignored.
It is common to apply these operations in the order of overwrite, add, and multiply.
In situations where it is difficult to see the calculation as a program component,
it is necessary to know which operation performs which calculation and to set the order in which the components are applied in sequence.
Let’s see what code differences cause the differences in eye opening shown in the GIF above.
This is an example of applying eye blinking and facial expressions to the sample model “haru” included in the SDK.
As for the facial expression settings, f06.exp3.json, which opens and closes the eyes twice as wide, is being played.
Only the C++ code is shown because the only issue is the order of computation.
An Update of A to set the facial expression of the multiplication operation after the automatic eye blinking of the overwriting operation
// C++ // Eye blinking if (!motionUpdated) { if (_eyeBlink ! = NULL) { // When the main motion is not updated. _eyeBlink->UpdateParameters(_model, deltaTimeSeconds); // Eye blinking } } if (_expressionManager ! = NULL) { _expressionManager->UpdateMotion(_model, deltaTimeSeconds); // Parameter update with facial expression (relative change). }
// TypeScript // Eye blinking if (!motionUpdated) { if (this._eyeBlink ! = NULL) { // When the main motion is not updated. this._eyeBlink->UpdateParameters(this._model, deltaTimeSeconds); // Eye blinking } } if (this._expressionManager ! = NULL) { this._expressionManager.updateMotion(this._model, deltaTimeSeconds); // Parameter update with facial expression (relative change). }
An Update of B for automatic eye blinking of the overwrite operation after setting the facial expression of the multiplication operation
Updating B (Native)
// C++ if (_expressionManager ! = NULL) { _expressionManager->UpdateMotion(_model, deltaTimeSeconds); // Parameter update with facial expression (relative change). } // Eye blinking if (!motionUpdated) { if (_eyeBlink ! = NULL) { // When the main motion is not updated. _eyeBlink->UpdateParameters(_model, deltaTimeSeconds); // Eye blinking } }
// TypeScript if (this._expressionManager ! = NULL) { this._expressionManager.updateMotion(this._model, deltaTimeSeconds); // Parameter update with facial expression (relative change). } // Eye blinking if (!motionUpdated) { if (this._eyeBlink ! = NULL) { // When the main motion is not updated. this._eyeBlink.updateParameters(this._model, deltaTimeSeconds); // Eye blinking } }
In A, you can confirm that the eyes are wide open after blinking due to the effect of the facial expression.
In B, on the other hand, the facial expression effect is overridden by blinking and reduced to standard eye opening and closing.
The nuanced expressions of the model can be very different if the order of the calculations is different.
The order of calculation of motions, expressions, and functions should also be shared with the designer.