将 Live2D Cubism 模型一键打包为自包含 HTML 文件。
支持模型/纹理/着色器/脚本全部 Base64 内嵌到单个 HTML,浏览器直接双击打开即可看到 Live2D 模型,无需服务器、无需 npm、无需任何运行时依赖。
项目结构
Samples/Live2DMTF_QtCpp/
├── CMakeLists.txt # CMake 构建配置
├── build.bat # 一键编译脚本
├── deploy.bat # 部署脚本 (windeployqt 补全 Qt DLL)
├── src/ # 源码
│ ├── main.cpp # 入口
│ ├── mainwindow.h/cpp # 主窗口 (UI + 事件处理)
│ ├── model_discovery.h/cpp # 模型发现 (.model3.json 解析)
│ ├── resource_collector.h/cpp # 资源收集 (Base64 编码)
│ ├── html_generator.h/cpp # HTML 生成 (JS 补丁 + 拦截器注入)
│ ├── verbose_logger.h/cpp # 日志
│ └── resource_paths.h # 运行时数据路径常量
└── build_test/ # 编译输出目录
编译与部署
环境
编译
build.bat脚本自动执行 cmake -G "MinGW Makefiles" → mingw32-make -j4,产物 build_mingw\Live2DMTF.exe。
部署
deploy.bat将 EXE + data 目录复制到 jlasr\,并调用 windeployqt 补全所有 Qt 依赖 DLL,生成可分发的完整程序包。
CMake 参数
核心技术
模型发现 model_discovery.cpp
数据结构:
struct ModelInfo {
QString modelName;
QString modelDir;
QMap<QString, QStringList> files; // "moc"→["warma.moc3"], ...
QMap<QString, QStringList> motionGroups; // "Idle"→["motions/yu.motion3.json"], ...
};解析流程:
1. 在指定目录下查找 *.model3.json
2. 用 QJsonDocument::fromJson() 解析 JSON
3. 遍历 FileReferences 节点提取 Moc/Textures/Physics/Pose/UserData/Motions
4. 独立扫描 motions/ 子目录 (拾取未在 model3.json 中声明的动作文件)
5. 独立扫描 *.cdi3.json 文件
资源收集 resource_collector.cpp
数据结构:
struct ResourceEntry {
QString appUrl; // "../../Resources/warma/warma.moc3"
QString srcPath; // "C:\...\warma\warma.moc3" (绝对路径)
QString b64; // Base64 编码后的字符串
QString mime; // MIME 类型
qint64 rawSize; // 原始字节大小
};返回类型: QPair<QMap<QString, ResourceEntry>, QVector<ResourceEntry>> — 字典用于 O(1) 查找,列表用于顺序遍历复制。
Base64 编码: 使用 Qt 内置的 QByteArray::toBase64(),无需外部库。编码膨胀率约 +33%。
MIME 检测: guessMime() 通过文件扩展名映射: .png→image/png, .json→application/json, .moc3→application/octet-stream, .frag/.vert→text/plain。
详细日志 (verbose): 每个资源文件输出 hex dump (每行 64 字节, 最多 200 行)、完整 Base64 (每行 120 字符)、文件大小与膨胀率。
HTML 生成 html_generator.cpp
JS 补丁函数: patchBuiltJs() 通过 QString::replace() 对 Vite 打包的应用代码做字符串替换:
原始 JS: warma → 新模型名
原始 JS: this._viewMatrix.scale(1,1) → this._viewMatrix.scale(1.2,1.2)
原始 JS: ve=.8, → ve=0.5,
原始 JS: n.createTextureFromPngFile(...height*0.95...) → 全屏拉伸
原始 JS: //# sourceMappingURL=... → 移除 (减小体积)
拦截器注入: 生成两种 JavaScript 拦截器:
- 内嵌模式 embed_res=True): 注入 __RESOURCE_MAP 字典, 包含所有资源的 Base64 数据window.fetch 被劫持, 精确匹配或后缀匹配返回内存中的 Response
- 外挂模式 embed_res=False): 注入 __URL_MAP, 将 ../../Resources/... 映射为 ./res/_.._.._Resources_... 文件路径, 拦截器重定向 fetch() 到此路径
Image 拦截器通过 Object.defineProperty(HTMLImageElement.prototype, 'src', ...) 劫持 src setter, 内嵌模式下替换为 data:image/png;base64,... Data URI。
异步生成 QThread + GenerateWorker
核心生成逻辑在当前 QThread 之外的**工作线程**中执行, 避免阻塞 UI:
MainWindow::onGenerate()
↓
new QThread + GenerateWorker::moveToThread()
↓ signals/slots
├── simpleLog(const QString&) → appendLog()
├── verboseLog(const QString&) → appendDetail()
├── progressChanged(int,int) → onProgress()
├── elapsedTime(double) → onElapsed()
└── finished(bool,QString) → onDone()
批量日志刷新 flushLog/flushDetail): 日志和详细数据先存入 QStringList 缓冲区, 通过 100ms 间隔的 QTimer::singleShot 定时器批量写入 QPlainTextEdit, 避免频繁 GUI 更新导致界面卡死。
耗时计算: 工作线程中 QDateTime::currentMSecsSinceEpoch() 对比 t0 计算已用时间, 每 200ms 通过 QTimer 触发信号更新 UI。
ETA 估算: elapsed / progress * (total - progress) 计算剩余时间。
详细日志 verbose_logger.cpp
类设计:
class VerboseLogger {
QFile* m_file = nullptr; // 日志文件句柄
QTextStream* m_stream = nullptr; // 流式写入
QString m_logPath; // 日志文件完整路径
bool open(const QString& outDir); // 创建 live2dmtf_verbose_<时间戳>.log
void log(const QString& msg); // 每行带毫秒时间戳 [HH:mm:ss.zzz]
void close(); // 写入结束标记, 关闭文件
};日志文件路径: {outputDir}/live2dmtf_verbose_{yyyyMMdd_HHmmss}.log。
资源路径管理 resource_paths.h
所有运行时数据文件路径集中在 ResourcePaths 命名空间, 统一从 QCoreApplication::applicationDirPath() + "/data" 派生:
namespace ResourcePaths {
inline QString dataDir() { return exeDir() + "/data"; }
inline QString coreJs() { return dataDir() + "/live2dcubismcore.js"; }
inline QString builtJs() { return dataDir() + "/index-DiW7gcuz.js"; }
inline QString shaderDir() { return dataDir(); }
}嵌入组合
技术参数总览
编译器与工具链
运行时数据
性能指标
License
本项目依赖 Live2D Cubism SDK,使用须遵守 [Live2D 开放软件许可协议](https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html)。
软件分发
仅供学习使用,下载的文件请在24小时内删除
源代码:
软件:
Github:https://github.com/lazylizilazy/Live2DMTF
Live2DMTF — Live2D Model To File (C++ Edition)
本文采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处。
评论交流
欢迎留下你的想法