[Android]摸鱼计划:Live2D 内容和载入过程探索 | 七日打卡

1,685 阅读3分钟

零、准备知识

今天我们来分析一下加载一个 Live2D 模型的过程,所以文章焦点都集中在 Live2D 的格式和 SDK 的 API 使用上,其他内容就不多解释了,主要是 cmake、C++ 和 JNI 的基本知识。如果在看源码的过程中搜索到本文,可能要失望而归了。

上一篇文章介绍了 Live2D 和官方 Android Demo 的 Java 层的实现,传送门:

[Android]摸鱼计划:给 App 也加一个 Live2D 吧 | 七日打卡

一、Live2D 的文件构成

(仅介绍 Demo 中的示例模型的文件内容,不能用作参考文档)

这次我们用 Haru 这个模型,上一篇使用的 Mark 虽然简单,但外观差了一点,谁不喜欢美少女呢?

.
├── Haru.2048
│   ├── texture_00.png
│   └── texture_01.png
├── Haru.moc3
├── Haru.model3.json
├── Haru.pose3.json
├── Haru.userdata3.json
├── expressions
│   ├── F01.exp3.json
│   ├── F02.exp3.json
│   ├── F03.exp3.json
│   ├── F04.exp3.json
│   ├── F05.exp3.json
│   ├── F06.exp3.json
│   ├── F07.exp3.json
│   └── F08.exp3.json
└── motions
    ├── haru_g_idle.motion3.json
    ├── haru_g_m01.motion3.json
    ├── haru_g_m02.motion3.json
    ├── haru_g_m03.motion3.json
    ├── haru_g_m04.motion3.json
    ├── haru_g_m05.motion3.json
    ├── haru_g_m06.motion3.json
    ├── haru_g_m07.motion3.json
    ├── haru_g_m08.motion3.json
    ├── haru_g_m09.motion3.json
    ├── haru_g_m10.motion3.json
    ├── haru_g_m11.motion3.json
    ├── haru_g_m12.motion3.json
    ├── haru_g_m13.motion3.json
    ├── haru_g_m14.motion3.json
    ├── haru_g_m15.motion3.json
    ├── haru_g_m16.motion3.json
    ├── haru_g_m17.motion3.json
    ├── haru_g_m18.motion3.json
    ├── haru_g_m19.motion3.json
    ├── haru_g_m20.motion3.json
    ├── haru_g_m21.motion3.json
    ├── haru_g_m22.motion3.json
    ├── haru_g_m23.motion3.json
    ├── haru_g_m24.motion3.json
    ├── haru_g_m25.motion3.json
    └── haru_g_m26.motion3.json

看着挺多,可以分类整理成 7 部分。

  • Haru.2048 | 纹理(贴图)资源文件夹
  • Haru.moc3 | 模型文件
  • Haru.model3.json | 模型全局配置
  • Haru.pose3.json | 模型姿势配置
  • Haru.userdata3.json | 用户数据配置
  • expressions | 人物表情文件夹
  • motions | 人物动作文件夹

(有很多的 "3" 是因为 Live2D 制作软件的版本号是 3)

二、加载模型的过程(Cubism SDK API)

模型的加载从 LAppLive2DManager 的构造函数开始,LAppLive2DManager 是一个懒加载的单例,负责持有并管理一个 Live2D 模型的生命周期,并处理模型和用户的交互操作。Live2D 模型被封装在 LAppModel 对象中,通过 SetupModel 初始化。

模型载入的起始文件是固定的,也就是模型的全局配置文件(xxx.model3.json),代码里也确实是这么处理的:

// ModelDir[]に保持したディレクトリ名から
// model3.jsonのパスを決定する.
// ディレクトリ名とmodel3.jsonの名前を一致させておくこと.
std::string model = ModelDir[index];
std::string modelPath = ResourcesPath + model + "/";
std::string modelJsonName = ModelDir[index];
modelJsonName += ".model3.json";

ReleaseAllModel();
_models.PushBack(new LAppModel());
_models[0]->LoadAssets(modelPath.c_str(), modelJsonName.c_str());

全局配置文件读取后转换成 CubismModelSettingJson 对象,后面的顺序是根据 CubismModelSettingJson 决定的。加载模型的过程要调用 Java 代码加载 assets 的文件,我们可以在这里添加 log 查看具体的读取顺序。

将模型从文件读取到内存后就是一个 CubismUserModel,要把模型数据转换成图像绘制到屏幕上,接下来就是根据 CubismUserModel 初始化 CubismRenderer。

void CubismUserModel::CreateRenderer()
{
    if (_renderer)
    {
        DeleteRenderer();
    }
    _renderer = Rendering::CubismRenderer::Create();
    _renderer->Initialize(_model);
}

此时还没读取到 texture,看上面的日志顺序,texture 文件是最后读到的。CubismRenderer 初始化之后就是读取纹理了,读取到 png 文件之后调用了一系列 OpenGL 的函数创建纹理对象,最后将模型和纹理绑定:

GetRenderer<Rendering::CubismRenderer_OpenGLES2>()->BindTexture(modelTextureNumber, glTextueNumber);

以上过程准备完毕之后,当 nativeOnDrawFrame 触发的时候,调用 CubismRenderer::DrawModel 更新屏幕上的绘制内容即可。


一不小心又搞了一天的 Live2D,明日方舟同人作品还挺多,不过怎么没人做克洛丝呢。

去年学过的日语和被安排的 JNI+OpenCV 的工作经历终于发挥了作用,我也没想到摸鱼时间翻了翻 Cubism SDK Demo 的源码就能梳理出整个 Demo 的工作流程,不得不说,多学知识永远不亏。