如何直接使用CubismNativeFramework

最終更新: 2020年1月30日

Cubism SDK for Native中包含的范例项目使用CubismNativeFramework(SDK包/Framework/以下)来处理Cubism模型。
在创建处理Cubism模型的项目时,基于范例项目使用为宜,但在现有应用程序或用户自己的引擎等中实装时,有些情况需要直接处理CubismNativeFramework的API。
但是,由于包中包含的范例项目功能强大,因此仅参考它并不容易理解和区分结构。

下面,我们将介绍以上述用户为对象,直接调用CubismFramework处理模型的最小片段。

CubismFramework周期

使用CubismFramework的步骤如下:

  1. CubismFramework的原始化
  2. 获取模型文件路径
  3. 导入模型
  4. 更新处理
  5. 放弃模型
  6. CubismFramework退出处理

CubismFramework的原始化

CubismFramework的原始化处理如下所示。

CubismFramework通过使用传递给CubismFramework::StartUp()第一参数的分配器来确保内存记忆。
Cubism SDK for Native在CubismNativeSamples中有分配器范例。
CubismFramework中使用的分配器也需要实装来指定和确保对齐。
因此,基本上,因为可以快速嵌入,为此可以使用范例,但如果您想自定义内存记忆的分配,您可以将其替换为用户端实装的那一个,并修改范例并将其嵌入。

有关分配器的具体实装,请参考 此处 ,有关创建自定义内存记忆分配器的注意事项,请参考 此处 。

    // 记录等选项设置。
    CubismFramework::Option _cubismOption;
    
    // 分配器。
    LAppAllocator _cubismAllocator;
    
    // 信息输出函数。
    static void PrintMessage(const Csm::csmChar* message);
    // 设置记录输出级别。在LogLevel_Verbose的情况下,输出详细记录。
    _cubismOption.LoggingLevel = CubismFramework::Option::LogLevel_Verbose;
    _cubismOption.LogFunction = PrintMessage;
    
    // 设置CubismNativeFramework原始化所需的参数。
    CubismFramework::StartUp(&_cubismAllocator, &_cubismOption);
    
    // 原始化CubismFramework。
    CubismFramework::Initialize();
Tips

原始化时只调用一次CubismFramework::Initialize(),然后跳过连续调用,除非CubismFramework被放弃。 但是,一旦CubismFramework被放弃,再次对其进行原始化时将调用CubismFramework::Initialize()。

获取模型文件路径

对于Cubism的嵌入数据集,各相对路径都在model3.json中描述。
可以直接指定一个.moc3文件或者纹理进行导入,但基本上推荐从model3.json获取路径并导入。

要解析model3.json,请使用CubismFramework的CubismModelSettingJson类。

    csmByte* CreateBuffer(const csmChar* path, csmSizeInt* size)
    {
        if (DebugLogEnable)
        {
            LAppPal::PrintLog("[APP]create buffer: %s ", path);
        }
        return LAppPal::LoadFileAsBytes(path, size);
    }

    void DeleteBuffer(csmByte* buffer, const csmChar* path = "")
    {
        if (DebugLogEnable)
        {
            LAppPal::PrintLog("[APP]delete buffer: %s", path);
        }
        LAppPal::ReleaseBytes(buffer);
    }
    // 导入model3.json。
    csmSizeInt size;
    const csmString modelSettingJsonPath = _modelHomeDir + modelSettingJsonName;
    csmByte* buffer = CreateBuffer(modelSettingJsonPath, &size);
    ICubismModelSetting* setting = new CubismModelSettingJson(buffer, size);
    DeleteBuffer(buffer, modelSettingJsonPath.GetRawString());
    
    // 获取model3.json中描述的模型路径。
    csmString moc3Path = _modelSetting->GetModelFileName();
    moc3Path = _modelHomeDir + moc3Path;

导入模型

模型通过CubismModel接口进行导入。

CubismModel的副本由CubismNativeFramework中的CubismUserModel::_mode持有。
使用时,建议从继承CubismUserModel的类中处理。

还可以在外部管理诸如纹理、动态和表情动态等资源。

作为一个例子,这里我们将说明使用继承CubismUserModel的CubismUserModelExtend。

    // 创建模型的副本。
    CubismUserModelExtend* userModel = new CubismUserModelExtend();
	
    // 导入moc3文件。
    buffer = CreateBuffer(moc3Path.GetRawString(), &size);
    userModel->LoadModel(mocBuffer, mocSize);
    DeleteBuffer(buffer, moc3Path.GetRawString());


要导入的moc3文件的路径可以通过上面介绍的model3.json获取,表情、物理模拟、姿势、眨眼、口形同步、用户数据和动态路径也可以从model3.json获取。
在SDK范例中,这些是在导入模型的同时导入的。
各导入方法请参考以下内容。

Tips

上面的代码片段基于在画面上显示1个模型的假设。 如果要同时在画面上显示多个模型,请创建与要显示的模型一样多的CubismUserModel派生类的副本。

更新处理

Cubism模型的更新处理是通过调用CubismModel的接口CubismModel::Update()来完成的,方法和模型原始化一样。
当CubismModel::Update()被调用时,将进行Cubism Core更新处理,从目前为止设置的参数和部件的值更新顶点信息。

    void CubismUserModelExtend::Update()
    {
        // 参数操作。
        _model->SetParameterValue(CubismFramework::GetIdManager()->GetId("ParamAngleX"), value);
        
        // 部件不透明度操作。
        _model->SetPartOpacity(CubismFramework::GetIdManager()->GetId("PartArmL"), opacity);
        
        // 更新模型顶点信息
        _model->Update();
    }

在更新处理中,视线跟踪、物理模拟、动态播放等必须在Update()之前进行。

在CubismModel::Update()之后操作参数值将不会应用。
之后如果再次调用CubismModel::Update()就会进行应用,但是由于负载比较高,建议把这个处理合二为一。

    // 应用在顶点上
    _model->SetParameterValue(CubismFramework::GetIdManager()->GetId("ParamAngleX"), value);
    
    // 更新模型顶点信息
    _model->Update();
    
    // 不应用在顶点上
    _model->SetParameterValue(CubismFramework::GetIdManager()->GetId("ParamAngleX"), value);


播放动态的CubismMotionManager::UpdateMotion()会覆盖所有用于播放动态的ID参数值。
因此,在该处理之前对参数值的任何操作都将被CubismMotionManager::UpdateMotion()覆盖。
建议先进行动态播放处理,再进行视线跟踪等的值操作及物理模拟等。

    // 将正在播放的动态应用到模型中
    _motionManager->UpdateMotion(_model, deltaTimeSeconds);
    
    // 视线跟踪等的值操作和物理模拟处理
    
    // 更新模型顶点信息
    _model->Update();


此外,如果所有的参数都没有用于要播放的动态、或者如果因为动态播放停止等而没有操作参数值,则在前一帧中执行的值操作的结果将保留。
因此,后续的相对参数值操作可能会导致意外结果。
通过在模型中应用动态的处理前后调用CubismModel::LoadParameter()和CubismModel::SaveParameter(),可以重置之后执行的相对值操作。

    // 将所有参数的值恢复到保存时的状态
    _model->LoadParameters();
    
    // 将正在播放的动态应用到模型中
    _motionManager->UpdateMotion(_model, deltaTimeSeconds);
    
    // 保存所有参数的值
    _model->SaveParameters();
    
    
    // 相对值操作处理
    
    
    // 更新模型顶点信息
    _model->Update();


视线追踪、物理模拟、动态播放等处理本身请参考各文档。

通过将更新后的顶点信息等传递给渲染器,可以在画面上绘制Cubism模型。

放弃模型

要放弃模型,请放弃生成的CubismUserModel派生类的副本。
通过该操作,将从CubismUserModel析构函数中放弃该模型保存的动态、表情、物理模拟等信息。

    // 放弃模型数据
    delete userModel;

Cubism Framework退出处理

调用CubismFramework::Dispose()释放CubismFramework保存的通用资源。
在调用CubismFramework::Initialize()之前,不要调用CubismFramework::Dispose()。

另外,在调用CubismFramework::Dispose()之前,必须放弃所有模型。
这是因为放弃模型的处理使用了分配器的Deallocate()。

    // 放弃CubismFramework
    CubismFramework::Dispose();
Tips

通过CubismFramework原始化的数据置入static区域,不依赖于模型数据。 因此,它可以在多个模型之间复用,如果只是想切换模型,则无需调用CubismFramework::Dispose()。

关于本报道,敬请提出您的意见及要求。