About Models (Native)

Updated: 11/07/2019

Edit Model Information

Basically, model information is created in Modeler.
The movement of vertices and other objects relative to the parameters is recorded in the .moc3 file.
Other physics operations and user data attached to the ArtMesh are output as separate files. The .model3.json file is what keeps track of all file references related to the model.

You can output .moc3 files from Modeler all at once. “Exporting Data for Embedded Use”
You can also use Cubism Viewer (for OW) to add built-in settings for motion and facial expression files and pose files.

Import from .model3.json File Using Framework

When loading using the Framework, it is assumed that the necessary information for the model is extracted from the .model3.json file and an instance inheriting from the CubismUserModel class is maintained for use.

// C++
const csmChar* dir = "example/";
const csmChar* fileName = "example.model3.json";
const csmString jsonpath = csmString(dir) + fileName;
csmSizeInt size;
csmByte* buffer = CreateBuffer(jsonpath.GetRawString(), &size);
ICubismModelSetting* setting = new CubismModelSettingJson(buffer, size);
DeleteBuffer(buffer, jsonpath.GetRawString());

Each element extracted into the ICubismModelSetting class can be loaded with the CubismUserModel::Load~~ system function.

// C++
    csmSizeInt size;
    //Cubism Model
    if (strcmp(setting->GetModelFileName(), "") != 0)
    {
        csmString path = setting->GetModelFileName();
        path = dir + path;

        if (_debugMode)LAppPal::PrintLog("[APP]create model: %s", setting->GetModelFileName());

        csmByte* buffer = CreateBuffer(path.GetRawString(), &size);
        LoadModel(buffer, size);
        DeleteBuffer(buffer, path.GetRawString());

    }

The overall loading flow from the .model3.json file can be easily grasped by following the flow from the LAppModel::LoadAssets function in the sample to LAppModel::SetupModel function, CubismUserModel::CreateRenderer function, and LAppModel:: SetupTextures function.


Create an Instance (Import .moc3 Files)

First, load the .moc3 file into memory.
Pass the read buffer and size to the CubismMoc::Create function to first create a CubismMoc instance.
Next, call the CubismMoc::CreateModel function to create a CubismModel instance.
From this CubismModel instance, the user manipulates parameters and acquires information for drawing.

The CubismMoc::CreateModel function counts the number of models created by the function inside CubismMoc, and when discarding a CubismMoc, all CubismModels generated from all CubismMocs to be discarded must be discarded.

// C++
csmString path = "example.moc3";
path = dir + path;
csmSizeInt size;
csmByte* buffer = CreateBuffer(path.GetRawString(), &size);
CubismMoc* moc = CubismMoc::Create(buffer, size);
CubismModel* model = _moc->CreateModel();

Graphic Environment Associations

The Framework uses classes derived from the CubismRenderer class to make the model texture information independent of the graphics API.
All graphics-related information associated with the model is managed by classes derived from the CubismRenderer class.
By generating classes derived from the CubismRenderer class for the graphics API and registering a CubismModel instance with the CubismRenderer::Initialize function, CubismRenderer and CubismModel instances are linked.

// C++
Rendering::CubismRenderer* renderer = Rendering::CubismRenderer::Create();

renderer->Initialize(model);

Associate Textures

The textures that models have in the Framework are managed by a class derived from the CubismRenderer class.
However, that functionality avoids dependence on the graphics API, so its methods are not registered in the CubismRenderer class.
Note the change in method specifications depending on the target graphics API.
The OpenGL process uses the CubismRenderer_OpenGLES2::BindTexture function in the CubismRenderer_OpenGLES2 class, which is derived from the CubismRenderer class, to register the textures.
The first argument is the texture number of the model, identified in the Editor by the number of the texture atlas.
The second argument is the texture’s OpenGL management number.

The example below shows texture loading in Cocos2d-x.

// C++
// Load texture into OpenGL texture unit.
csmInt32 modelTextureNumber = 0;
csmString texturePath = "example.2048/texture_00.png";
texturePath = dir + texturePath;

//cocos2d
Texture2D* texture = Director::getInstance()->getTextureCache()->addImage(std::string(texturePath.GetRawString()));
const csmInt32 glTextureNumber = texture->getName();
const Texture2D::TexParams texParams = { GL_LINEAR_MIPMAP_LINEAR, GL_LINEAR, GL_CLAMP_TO_EDGE, GL_CLAMP_TO_EDGE };
texture->setTexParameters(texParams);
texture->generateMipmap();

//OpenGL
GetRenderer<Rendering::CubismRenderer_OpenGLES2>()->BindTexture(modelTextureNumber, glTextureNumber);

If Texture is not managed by Cocos2d-x, but OpenGL alone, it is registered through application side management.

// C++
// Load texture into OpenGL texture unit.
csmString texturePath = _modelSetting->GetTextureFileName(modelTextureNumber);
texturePath = _modelHomeDir + texturePath;

LAppTextureManager::TextureInfo* texture = LAppDelegate::GetInstance()->GetTextureManager()->CreateTextureFromPngFile(texturePath.GetRawString());
const csmInt32 glTextureNumber = texture->id;

//OpenGL
GetRenderer<Rendering::CubismRenderer_OpenGLES2>()->BindTexture(modelTextureNumber, glTextureNumber);

Specify Display Position and Size

With the previous settings, the model can be drawn, but in many cases, the display position and scale are too different to be shown on the screen as is.

See “DrawableVertexPositions Range” for the vertex range returned by the csmGetDrawableVertexPositions function.
To adjust the size, use the CubismModelMatrix class and the CubismModel::GetCanvasWidth and CubismModel::GetCanvasHeight functions.

// C++
CubismModelMatrix* modelMatrix = CSM_NEW CubismModelMatrix(_model->GetCanvasWidth(), _model->GetCanvasHeight());

This matrix is multiplied by the Projection matrix before drawing and passed to the renderer as an MVP matrix.

// C++
CubismMatrix44 projectionMatrix;
projectionMatrix.Scale(1, window.width / window.height);
projectionMatrix.MultiplyByMatrix(modelMatrix);

GetRenderer<Rendering::CubismRenderer_OpenGLES2>()->SetMvpMatrix(&projectionMatrix);

GetRenderer<Rendering::CubismRenderer_OpenGLES2>()->DrawModel();

Update Vertices

Execute the CubismModel::Update function to reflect parameter operations from the CubismModel instance to the vertices of the ArtMesh.

// C++
model->Update();

Draw

Drawing is not executed from the CubismModel instance, but by commanding the renderer associated with it.

// C++
CubismMatrix44 projectionMatrix;
projectionMatrix.Scale(1, window.width / window.height);
projectionMatrix.MultiplyByMatrix(modelMatrix);

GetRenderer<Rendering::CubismRenderer_OpenGLES2>()->SetMvpMatrix(&projectionMatrix);

GetRenderer<Rendering::CubismRenderer_OpenGLES2>()->DrawModel();

Discard

Destroying an instance requires destroying all CubismlModels and then destroying CubismMoc.
To preserve this order, CubismModel is deleted by CubismMoc::DeleteModel.
If this destruction is not performed via this function, a warning will be issued because the number of models checked inside CubismMoc does not match.

// C++
CubismUserModel::~CubismUserModel()
{
    CSM_DELETE(_motionManager);
    CSM_DELETE(_expressionManager);
    _moc->DeleteModel(_model); // <<<
    CubismMoc::Delete(_moc); // <<<
    CSM_DELETE(_modelMatrix);
    CubismPose::Delete(_pose);
    CubismEyeBlink::Delete(_eyeBlink);
    CubismBreath::Delete(_breath);
    CSM_DELETE(_dragManager);
    CubismPhysics::Delete(_physics);
    CubismModelUserData::Delete(_modelUserData);

    DeleteRenderer();
}
// C++
CubismMoc::~CubismMoc()
{
    CSM_ASSERT(_modelCount == 0);

    CSM_FREE_ALLIGNED(_moc);
}

void CubismMoc::DeleteModel(CubismModel* model)
{
    CSM_DELETE_SELF(CubismModel, model);
    --_modelCount;
}

Use Parameter IDs that Do Not Exist in the Model in the CubismModel Class

In the operations of the CubismModel class parameters and part opacity, there is a functionality to handle IDs that do not exist in the .moc3 file.
This functionality is used by the CubismMotion class, the CubismPose class, and other classes, and if creating a new mechanism using this function, be careful to use an ID that does not conflict with existing functions.

Also, when reading the Framework, keep in mind that it may be linked to other functions using created parameters.
Note: Accessing a non-existent maximum, minimum, or initial value will result in an error.

// C++
csmInt32 CubismModel::GetParameterIndex(CubismIdHandle parameterId)
{
    csmInt32            parameterIndex;
    const csmChar**     idArray = Core::csmGetParameterIds(_model);
    csmInt32            idCount = Core::csmGetParameterCount(_model);


    for (parameterIndex = 0; parameterIndex < idCount; ++parameterIndex)
    {
        if (parameterId ! = _parameterIds[parameterIndex])
        {
            continue;
        }

        return parameterIndex;
    }

    // If not present in the model, search in the non-existent parameter ID list and return its index.
    if (_notExistParameterId.IsExist(parameterId))
    {
        return _notExistParameterId[parameterId];
    }

    // Add a new element if it is not in the non-existent parameter ID list.
    parameterIndex = Core::csmGetParameterCount(_model) + _notExistParameterId.GetSize();

    _notExistParameterId[parameterId] = parameterIndex; 
    _notExistParameterValues.AppendKey(parameterIndex); 

    return parameterIndex; 
}
Please let us know what you think about this article.