Initial commit: AR avatar prototype
包含三个子项目: - avatar-h5-renderer: Live2D Cubism 4 H5 渲染器 (Vite + TS) - avatar_flutter_app: Flutter 容器 App (打包 H5 进 WebView) - gif-export: puppeteer 导出 32 个动作的透明 GIF (供 ESP32 圆屏播放) 模型资源: Haru, Natori (含贴图、moc3、motions, expressions) 设计文档: AI驱动虚拟形象渲染方案_v5.1.md Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
commit
72e7df09cd
66
.gitignore
vendored
Normal file
66
.gitignore
vendored
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
# ===== macOS =====
|
||||||
|
.DS_Store
|
||||||
|
.AppleDouble
|
||||||
|
.LSOverride
|
||||||
|
|
||||||
|
# ===== Editor / IDE =====
|
||||||
|
.idea/
|
||||||
|
.vscode/
|
||||||
|
*.iml
|
||||||
|
*.ipr
|
||||||
|
*.iws
|
||||||
|
*.swp
|
||||||
|
|
||||||
|
# ===== Logs / temp =====
|
||||||
|
*.log
|
||||||
|
*.tmp
|
||||||
|
/tmp/
|
||||||
|
|
||||||
|
# ===== Node (avatar-h5-renderer, gif-export) =====
|
||||||
|
node_modules/
|
||||||
|
dist/
|
||||||
|
.vite/
|
||||||
|
.npm/
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
|
||||||
|
# ===== Flutter / Dart (avatar_flutter_app) =====
|
||||||
|
**/build/
|
||||||
|
**/.dart_tool/
|
||||||
|
**/.flutter-plugins
|
||||||
|
**/.flutter-plugins-dependencies
|
||||||
|
**/.packages
|
||||||
|
**/.pub-cache/
|
||||||
|
**/.pub/
|
||||||
|
**/Pods/
|
||||||
|
**/ios/Flutter/.last_build_id
|
||||||
|
**/ios/Flutter/Generated.xcconfig
|
||||||
|
**/ios/Flutter/flutter_export_environment.sh
|
||||||
|
**/ios/Runner.xcworkspace/xcuserdata/
|
||||||
|
**/ios/Runner.xcodeproj/xcuserdata/
|
||||||
|
**/android/.gradle/
|
||||||
|
**/android/local.properties
|
||||||
|
**/android/app/debug/
|
||||||
|
**/android/app/profile/
|
||||||
|
**/android/app/release/
|
||||||
|
**/macos/Flutter/ephemeral/
|
||||||
|
**/windows/flutter/ephemeral/
|
||||||
|
**/linux/flutter/ephemeral/
|
||||||
|
|
||||||
|
# ===== Python =====
|
||||||
|
__pycache__/
|
||||||
|
*.pyc
|
||||||
|
*.pyo
|
||||||
|
|
||||||
|
# ===== Project-specific large/intermediate files =====
|
||||||
|
# Per-frame PNG dumps generated during GIF recording (intermediate only)
|
||||||
|
gif-export/clips/*_frames/
|
||||||
|
# Texture backup created by black_to_transparent.py
|
||||||
|
**/texture_*.backup.png
|
||||||
|
|
||||||
|
# ===== Secrets =====
|
||||||
|
.env
|
||||||
|
.env.*
|
||||||
|
*.pem
|
||||||
|
*.key
|
||||||
669
AI驱动虚拟形象渲染方案_v5.1.md
Normal file
669
AI驱动虚拟形象渲染方案_v5.1.md
Normal file
@ -0,0 +1,669 @@
|
|||||||
|
# AI 驱动虚拟形象渲染方案 v5.1
|
||||||
|
## 跨产品形态统一架构调研
|
||||||
|
|
||||||
|
| 项目 | 内容 |
|
||||||
|
|---|---|
|
||||||
|
| 产品形态 | ① 罐子(Unity 3D + RK + 全息光仓,洛天依形象在跑)<br>② 洛天依 APP(独立 APP,Unity)<br>③ 小型硬件统一 APP(多设备入口,统一流量与用户)<br>④ ESP32-S3 小硬件(序列帧形态)<br>⑤ 未来 Live2D 形态 / 虚拟明星 / 写实数字人 |
|
||||||
|
| 当前底座 | **火山 RTC AIGC 全平台已跑通**(手机 / 罐子 / ESP32-S3)<br>整套 ASR + LLM + TTS 在云端打包,端到端延迟 < 2s |
|
||||||
|
| 当前痛点 | 形象层 3D 建模 + 手 K 动画,美术资产成本高,新对话/新场景都要手 K 衔接 |
|
||||||
|
| 调研目标 | 用 AI 驱动取代手 K,复用 RTC 已有底座,覆盖跨产品形态 |
|
||||||
|
| 调研日期 | 2026-05-09 |
|
||||||
|
| 适用主体 | 杰森气元科技 / 气元科技 |
|
||||||
|
| 优先级 | **P0 - 立即启动** |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 阅读指引
|
||||||
|
|
||||||
|
> 本文是给彭铭聪 / 张业昌 / 张文琦 review 的方案文档。Air 主导方向,**实操细节由你们决定**。
|
||||||
|
>
|
||||||
|
> **核心思路**:RTC AIGC 已经原生下发字幕、AI 状态、情绪、Function Calling 等信号,Unity 端 / 各端在已有 RTC 回调里多解析这些事件,就能把动作衔接、表情切换、嘴型驱动从"手 K"切换到"AI 驱动"。
|
||||||
|
>
|
||||||
|
> 罐子的洛天依 3D 模型、Animator、shader 全部保留,**不动**;只在事件消费层加东西。
|
||||||
|
>
|
||||||
|
> 文档结构:
|
||||||
|
> - **第一~二章**:架构主线,先看
|
||||||
|
> - **第三章**:罐子(洛天依)的具体落地路径,彭铭聪重点看
|
||||||
|
> - **第四~六章**:其他产品形态参考,未来要做时再翻
|
||||||
|
> - **第七章**:风险和待验证项
|
||||||
|
> - **第八章**:P0 行动清单
|
||||||
|
> - **第九章**:未来技术栈迁移路径(中长期成长方向,彭铭聪也建议看)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 一、关键结论
|
||||||
|
|
||||||
|
> **核心架构:协议层复用 RTC 信令,渲染层按形态分化。**
|
||||||
|
>
|
||||||
|
> 火山 RTC AIGC 已经把 ASR + LLM + TTS 整套打包跑在云端,TTS 输出的字幕(Subtitle)、AI 对话状态、情绪标签、Function Calling 事件**通过 RTC 房间内的标准消息回调下发到端侧**——跟音频流走同一条链路,天然时间对齐。
|
||||||
|
>
|
||||||
|
> 各端(Unity / 手机 / RK / ESP32-S3)已有的 RTC SDK 已经具备接收这些消息的能力。**不需要自建 WebSocket 协议,不需要新连接管理**——只在已有 RTC 回调里加一层事件解析。
|
||||||
|
|
||||||
|
### 1.1 三件事必须分开讨论
|
||||||
|
|
||||||
|
很多技术争议来自把这三件事混在一起:
|
||||||
|
|
||||||
|
| # | 概念 | 跑在哪 | 我们的方案 |
|
||||||
|
|---|---|---|---|
|
||||||
|
| 1 | **AI 推理**(LLM/TTS/ASR) | 云端 | 火山 RTC AIGC(已跑通) |
|
||||||
|
| 2 | **驱动信号下发** | 云端 → 端侧 RTC 信令 | 复用 RTC 已有通道 |
|
||||||
|
| 3 | **形象渲染**(每帧画面) | **端侧本地** | Unity / Cubism / 序列帧 |
|
||||||
|
|
||||||
|
「AI 驱动」指 #1 输出的语义信号通过 #2 送到端侧,驱动 #3 自动渲染。**渲染本身仍在端侧本地跑**——跟所有 VTuber 软件、SEKAI、明日方舟一样的架构。
|
||||||
|
|
||||||
|
### 1.2 火山官方"数字人"功能 ≠ 我们要的方案,明确排除
|
||||||
|
|
||||||
|
| 维度 | 火山数字人 | 我们的需求 |
|
||||||
|
|---|---|---|
|
||||||
|
| 渲染位置 | 云端渲染,推 H264 视频流到端 | 端侧自渲染 |
|
||||||
|
| 形象库 | 火山预制 2D 真人 / 3D 超写实 / 3D 卡通 | 自建 3D(洛天依)/ 未来 Live2D / 序列帧 |
|
||||||
|
| 计费 | 按并发独立计费(付费资源) | 不需要,省钱 |
|
||||||
|
| 灵活度 | 形象由火山管 | 形象、动画、风格我们完全控制 |
|
||||||
|
| 商务限制 | 文档明确"提交工单咨询售前" | 标准 RTC AIGC 不需要额外谈判 |
|
||||||
|
|
||||||
|
> **判定依据**:火山数字人文档明确"SubtitleConfig.SubtitleMode 必须设置为 1(不对齐时间戳)"——启用云端数字人时字幕只是显示用、不携带音频对齐信息(因为云端已经把口型烧进视频流了)。我们要的恰恰是 **SubtitleMode=0(对齐音频时间戳)**——给端侧自渲染用的字时间戳信号。
|
||||||
|
|
||||||
|
**判断技巧**:凡是火山文档里需要"独立计费 / 提交工单咨询 / 数字人 AppId / 并发限制"的能力——是云端付费功能。凡是 RTC AIGC 已经原生支持的能力(字幕、AI 状态、情绪、Function Calling)——是 RTC 标准能力,已经在订阅里。
|
||||||
|
|
||||||
|
**设计原则:只用 RTC 标准能力,不依赖任何独立付费资源**。
|
||||||
|
|
||||||
|
### 1.3 各产品形态可行性
|
||||||
|
|
||||||
|
| 形态 | 渲染技术 | 硬件 | 状态 |
|
||||||
|
|---|---|---|---|
|
||||||
|
| **罐子(洛天依)** | Unity 3D + BlendShape + Animator + 全息光仓 | RK3566 / RK3588S | **已在跑,下一步加 AI 驱动事件层** |
|
||||||
|
| **洛天依 APP**(独立) | Unity(与罐子同一份工程) | iOS / Android | 已有 demo,与罐子同步加 AI 驱动 |
|
||||||
|
| **小型硬件统一 APP** | Flutter + 多渲染层(按设备角色卡热加载) | iOS / Android | 待立项,**不用 Unity** |
|
||||||
|
| ESP32-S3 小硬件 | LVGL + sprite sheet 状态机 | ESP32-S3 | 方案已定,未来按 RTC 信令驱动 |
|
||||||
|
| 未来 Live2D 形态 | Cubism Native SDK | 待定 | 储备方案 |
|
||||||
|
| 未来虚拟明星 / 写实数字人 | Unity 或 Cocos / VRM 标准 | 高端硬件 / 手机 | 储备方案,见第九章 |
|
||||||
|
|
||||||
|
### 1.4 立即启动的 P0 工作
|
||||||
|
|
||||||
|
**这是现在就要开始做的事**:
|
||||||
|
|
||||||
|
1. 服务端在 RTC AIGC 配置里启用 SubtitleConfig,SubtitleMode 设为 0(对齐音频时间戳)
|
||||||
|
2. Unity 端打印 subtitle 消息 JSON,验证时间戳精度(决定后续嘴型驱动算法)
|
||||||
|
3. Unity 端最小可用 demo:罐子上洛天依 AI 对话时嘴型自动同步(取代当前手 K)
|
||||||
|
|
||||||
|
详见第八章。**总工时 3-4 人天,本周可全部完成**。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 二、修订后的数据流
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
sequenceDiagram
|
||||||
|
participant U as 用户
|
||||||
|
participant App as 端侧 App<br/>(Unity罐子/手机/RK/ESP32)
|
||||||
|
participant R as 本地渲染引擎<br/>(Unity / Cubism / LVGL)
|
||||||
|
participant RTC as 火山 RTC<br/>(全平台 SDK)
|
||||||
|
participant AIGC as 火山 AIGC 云<br/>(ASR+LLM+TTS)
|
||||||
|
|
||||||
|
Note over App,R: 模型/资产端侧本地常驻
|
||||||
|
|
||||||
|
U->>App: 说话
|
||||||
|
App->>RTC: 音频上行
|
||||||
|
RTC->>AIGC: 路由到 AIGC 服务
|
||||||
|
AIGC->>AIGC: ASR → LLM(豆包) → TTS
|
||||||
|
|
||||||
|
par 同一条 RTC 房间链路
|
||||||
|
AIGC-->>RTC: TTS 音频流
|
||||||
|
RTC-->>App: 订阅音频流
|
||||||
|
and
|
||||||
|
AIGC-->>RTC: Subtitle 字幕(对齐时间戳)
|
||||||
|
RTC-->>App: onRoomMessageReceived
|
||||||
|
and
|
||||||
|
AIGC-->>RTC: AI 状态事件 / 情绪 / Function Call
|
||||||
|
RTC-->>App: onUserMessageReceived
|
||||||
|
end
|
||||||
|
|
||||||
|
App->>App: 扬声器播放音频
|
||||||
|
App->>R: 字时间戳 → 嘴型参数
|
||||||
|
App->>R: 情绪 → 切换 expression
|
||||||
|
App->>R: Function Call → 触发 motion
|
||||||
|
R-->>U: 屏幕渲染
|
||||||
|
```
|
||||||
|
|
||||||
|
**关键点**:所有 AI 输出走的是同一条 RTC 房间链路,跟音频流天然时间对齐。无独立 WebSocket,无独立连接管理,无新协议设计。
|
||||||
|
|
||||||
|
### 2.1 RTC AIGC 能用的信号(重要:直接对接列表)
|
||||||
|
|
||||||
|
| 信号类型 | 来源 | 用途 |
|
||||||
|
|---|---|---|
|
||||||
|
| **字幕(Subtitle)** | TTS 流式输出 | 驱动嘴型(最关键) |
|
||||||
|
| **AI 状态事件** | 对话状态机 | 触发 idle / listening / thinking / speaking 状态切换 |
|
||||||
|
| **AI 对话任务事件** | 任务级回调 | 错误处理、对话开始/结束 |
|
||||||
|
| **情绪识别与生成** | 情绪模型输出 | 触发 expression 切换 |
|
||||||
|
| **Function Calling 结果** | LLM 工具调用 | 触发预设 motion / 业务动作 |
|
||||||
|
| **自定义 Message** | LLM 通过结构化输出 | LLM 主动插入 motion tag |
|
||||||
|
|
||||||
|
这些能力**已经在你们的 demo 里跑通的 RTC SDK 里**——不需要任何新连接。
|
||||||
|
|
||||||
|
### 2.2 端侧通用消费模式
|
||||||
|
|
||||||
|
每个端的 RTC SDK 都有这两类回调:
|
||||||
|
|
||||||
|
```
|
||||||
|
onRoomMessageReceived(msg) // 房间广播消息(字幕通常走这里)
|
||||||
|
onUserMessageReceived(uid, msg) // 点对点消息(AI 状态事件通常走这里)
|
||||||
|
```
|
||||||
|
|
||||||
|
端侧实现 = 在这两个回调里加一层 JSON 解析 → 写入时间轴消费器 → 驱动渲染层。
|
||||||
|
|
||||||
|
### 2.3 端侧时间轴示例
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
gantt
|
||||||
|
title 端侧 1 秒内的渲染时间轴:「今天天气真好」
|
||||||
|
dateFormat X
|
||||||
|
axisFormat %Lms
|
||||||
|
|
||||||
|
section 音频播放
|
||||||
|
PCM 解码播放 :a1, 0, 1000
|
||||||
|
|
||||||
|
section 嘴型 (字 → viseme)
|
||||||
|
今 (j-in) :p1, 0, 200
|
||||||
|
天 (t-ian) :p2, 200, 400
|
||||||
|
是 (sh-i) :p3, 400, 600
|
||||||
|
好 (h-ao) :p4, 600, 1000
|
||||||
|
|
||||||
|
section 表情
|
||||||
|
expression= happy :e1, 0, 1000
|
||||||
|
|
||||||
|
section 动作
|
||||||
|
motion= idle.smile :m1, 0, 1000
|
||||||
|
```
|
||||||
|
|
||||||
|
端侧只是一个**时间轴消费器**:音频播放线程独立、嘴型按字时间戳改 BlendShape、表情按 emotion 区间切换、motion 按 Function Call 触发。
|
||||||
|
|
||||||
|
### 2.4 「渲染跑在端侧」是工业标准
|
||||||
|
|
||||||
|
需要向团队说明的一件事——这不是新架构,是行业惯例:
|
||||||
|
|
||||||
|
- 全球所有 VTuber 直播软件(VTube Studio、PrprLive、Animaze)都是端侧本地跑
|
||||||
|
- Project SEKAI、明日方舟、原神信箱里的角色,端侧本地渲染
|
||||||
|
- Unity 跑在 Android RK 板上是产业标准做法(车载、零售、家电常见)
|
||||||
|
|
||||||
|
**「AI 驱动虚拟人」的新意不在「跑在云端」,而在驱动信号源**:
|
||||||
|
|
||||||
|
| 传统形象渲染 | AI 驱动 |
|
||||||
|
|---|---|
|
||||||
|
| 主播说话 | LLM 输出文本 |
|
||||||
|
| 主播按表情按钮 | LLM 输出 emotion tag |
|
||||||
|
| 摄像头面捕跟踪嘴型 | TTS 输出字时间戳 |
|
||||||
|
| 主播按动作快捷键 | LLM 输出 Function Call |
|
||||||
|
| 动画师手 K 衔接 | RTC AI 状态事件驱动状态机 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 三、罐子(洛天依)落地路径
|
||||||
|
|
||||||
|
**这是 P0 的核心工作。彭铭聪重点看本章。**
|
||||||
|
|
||||||
|
### 3.1 当前状态梳理
|
||||||
|
|
||||||
|
- Unity 工程已有洛天依 3D 模型(手动建模)
|
||||||
|
- 动画师做了多套动作,彭铭聪在 Unity 里做动作衔接
|
||||||
|
- 已经加了"张嘴"——但应该不是真正的 viseme 嘴型,是简化版张/合(Q 版卡通形象上够用)
|
||||||
|
- RTC + AI 对话已经跑通
|
||||||
|
|
||||||
|
### 3.2 不变的部分
|
||||||
|
|
||||||
|
- 3D 建模 ✅ 保留
|
||||||
|
- BlendShape / 骨骼绑定 ✅ 保留
|
||||||
|
- 现有 Animator 和动作衔接 ✅ 保留
|
||||||
|
- shader 和全息光仓适配 ✅ 保留
|
||||||
|
- RTC 集成 ✅ 保留
|
||||||
|
|
||||||
|
### 3.3 要加的部分
|
||||||
|
|
||||||
|
在 Unity 工程里加一个事件消费层:
|
||||||
|
|
||||||
|
```
|
||||||
|
[业务逻辑层] ← 纯 C# 逻辑,不绑 Unity API,可移植
|
||||||
|
├─ RTCMessageParser.cs // RTC 消息 JSON 解析
|
||||||
|
├─ TimelineConsumer.cs // 事件队列 + 时间轴消费
|
||||||
|
└─ CharacterStateMachine.cs // 角色状态抽象(idle/listening/speaking...)
|
||||||
|
↓ 通过接口调用
|
||||||
|
[渲染适配层] ← 绑 Unity,未来换技术栈时只重写这一层
|
||||||
|
└─ UnityCharacterRenderer.cs // BlendShape / Animator 写入
|
||||||
|
↓
|
||||||
|
[已有 Unity 资产] ← 完全不动
|
||||||
|
├─ 洛天依 3D 模型
|
||||||
|
├─ Animator 状态机
|
||||||
|
└─ shader / 全息光仓适配
|
||||||
|
```
|
||||||
|
|
||||||
|
**关键设计原则:业务逻辑层和渲染适配层解耦**。具体做法:
|
||||||
|
- 业务逻辑层不直接调 `Animator.SetTrigger()` 或 `SkinnedMeshRenderer.SetBlendShapeWeight()`
|
||||||
|
- 改成调一个接口 `ICharacterRenderer`,方法是 `SetMouthOpen(float v)` / `PlayMotion(string name)` / `SwitchExpression(string name)` 这种语义化 API
|
||||||
|
- Unity 实现 `UnityCharacterRenderer : ICharacterRenderer`
|
||||||
|
- 未来想换 Cocos / Three.js / 其他,只重写一个 Renderer 实现,业务逻辑零改动
|
||||||
|
|
||||||
|
> **这个分层在当下不显眼,未来换技术栈时值千金**。详见第九章「未来技术栈迁移路径」。
|
||||||
|
|
||||||
|
完整 Unity 工程结构:
|
||||||
|
|
||||||
|
```
|
||||||
|
[Unity 主线程]
|
||||||
|
├─ RTCEngine(已有)
|
||||||
|
│ └─ OnRoomMessageReceived / OnUserMessageReceived ← 转发给 RTCMessageParser
|
||||||
|
├─ 业务逻辑层(新增,纯 C#)
|
||||||
|
│ ├─ RTCMessageParser
|
||||||
|
│ ├─ TimelineConsumer
|
||||||
|
│ └─ CharacterStateMachine
|
||||||
|
├─ 渲染适配层(新增)
|
||||||
|
│ └─ UnityCharacterRenderer : ICharacterRenderer
|
||||||
|
└─ AudioPlayback(已有,作为时间基准)
|
||||||
|
```
|
||||||
|
|
||||||
|
**改动量很小**,3D 资产和现有动画状态机一行不动。
|
||||||
|
|
||||||
|
### 3.4 全息光仓适配
|
||||||
|
|
||||||
|
LCD 拆背光后,黑色像素 = 透明(光不发出去),亮色像素 = 发光。渲染要求:
|
||||||
|
|
||||||
|
- **纯黑背景**(不要天空盒,不要环境光)
|
||||||
|
- **角色用发光材质**:emission map + Bloom 后处理强化
|
||||||
|
- **避免大面积低亮度区域**(在光仓里看不见,会显得角色少了一块)
|
||||||
|
- **轮廓光强化**:rim light 让角色边缘更清晰
|
||||||
|
- **慎用半透明**:alpha blend 在拆背光后效果难预测,先做实验
|
||||||
|
- **色彩偏向高饱和、亮色调**(青色/紫色/品红在全息感下最强)
|
||||||
|
|
||||||
|
这些规则彭铭聪应该已经在做了,列在这里供未来其他人参考。
|
||||||
|
|
||||||
|
### 3.5 RK 性能预期
|
||||||
|
|
||||||
|
| 板子 | 屏幕 | 单角色 PBR | Unity URP | 全息 shader |
|
||||||
|
|---|---|---|---|---|
|
||||||
|
| RK3566 Mali-G52 2EE | 720p | 30fps(顶点 < 30k) | URP 简化版 | 简单 Bloom |
|
||||||
|
| RK3588S Mali-G610 | 1080p | 60fps 轻松 | URP 完整 | Bloom + 体积光可选 |
|
||||||
|
|
||||||
|
**RK3566 优化注意点**:
|
||||||
|
- 顶点数严控(< 30k),骨骼 < 50
|
||||||
|
- shader 不要用复杂 PBR,半 Lambert + matcap + 发光通道即可
|
||||||
|
- URP 关闭 SSR / SSAO / DOF;Bloom 用低质量档
|
||||||
|
- 720p 是合理目标,强求 1080p 会掉帧
|
||||||
|
|
||||||
|
### 3.6 关于嘴型精度的讨论
|
||||||
|
|
||||||
|
> Air 提到:"洛天依现在嘴型只是张/合,不是真嘴型,对 Q 版卡通形象其实够用。但未来做虚拟明星、真人写实数字人时,真嘴型才比较重要。"
|
||||||
|
|
||||||
|
这个判断对:
|
||||||
|
|
||||||
|
| 形象类型 | 嘴型精度要求 | 实现路径 |
|
||||||
|
|---|---|---|
|
||||||
|
| Q 版卡通(洛天依) | 张/合 + 表情即可 | 字时间戳 → 1 个 BlendShape 开合度,足够 |
|
||||||
|
| 写实/半写实虚拟艺人 | 6 viseme(A/I/U/E/O/sil) | 字 → viseme → 5+ 个 BlendShape 加权 |
|
||||||
|
| 顶级写实数字人 | 15 viseme + 协同发音 | 接 NVIDIA Audio2Face 或类似方案 |
|
||||||
|
|
||||||
|
**当前阶段(洛天依罐子)做最简实现就够**:字时间戳到了 → BlendShape 开合度 = 1(讲话中);字时间戳间隙 → 开合度 = 0;用 LERP 平滑过渡。一个 BlendShape 解决问题。
|
||||||
|
|
||||||
|
未来做虚拟明星(CYBER STAR 那条线)时,再升级到完整 viseme 集,**那时候方案不变,只是 CharacterController 里多写几行映射代码**。
|
||||||
|
|
||||||
|
### 3.7 AI 驱动的真正价值不只在嘴型
|
||||||
|
|
||||||
|
**这里要纠正一个可能的认知偏差**:洛天依嘴型确实不重要,但这不等于 AI 驱动方案对洛天依不重要。
|
||||||
|
|
||||||
|
AI 驱动信令的真正价值在**动作 / 表情 / 状态切换**:
|
||||||
|
|
||||||
|
- 不再手 K 每段对话的动作衔接
|
||||||
|
- AI 说话内容触发情绪 → 自动切表情
|
||||||
|
- LLM 输出 Function Call → 自动播预制动作(挥手、点头、托腮、唱歌起手...)
|
||||||
|
- idle / listening / thinking / speaking 状态机由 RTC AI 状态信号驱动
|
||||||
|
|
||||||
|
**美术资产成本无限堆高的核心问题,是动作和动画**,不是嘴型。彭铭聪现在每加一段新对话就要手 K 衔接的痛点,AI 驱动信令解决的就是这个。
|
||||||
|
|
||||||
|
罐子上**最容易出效果的不是嘴型**,是:
|
||||||
|
1. AI 在思考时(状态 = thinking)→ 自动播"歪头思考"动作
|
||||||
|
2. AI 在听用户说话时(状态 = listening)→ 自动播"侧耳倾听"动作
|
||||||
|
3. AI 说到开心内容时(情绪 = happy)→ 自动切笑脸 + 播"拍手"动作
|
||||||
|
4. 用户问"你会唱歌吗",LLM 触发 Function Call `play_song` → 播预录的唱歌动画
|
||||||
|
|
||||||
|
这些都是预制好的 motion clip,AI 只是按语义触发——美术做一次,无限复用。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 四、其他产品形态参考
|
||||||
|
|
||||||
|
> 这一章是知识储备。当前不需要立即用,但未来做新产品时直接拿来用,不用重新调研。
|
||||||
|
|
||||||
|
### 4.1 当前手机 APP 架构
|
||||||
|
|
||||||
|
**两个 APP 分立**:
|
||||||
|
|
||||||
|
| APP | 形态 | 当前技术栈 | 维护人 |
|
||||||
|
|---|---|---|---|
|
||||||
|
| **洛天依 APP**(独立) | 单 IP 独立产品,3D 形象 | **Unity**(与罐子同一份工程) | 彭铭聪 |
|
||||||
|
| **小型硬件统一 APP** | 多产品入口,承载 ESP32-S3 等小硬件 | 原生(Flutter / 待定) | TBD |
|
||||||
|
|
||||||
|
**为什么分立**:
|
||||||
|
- 洛天依是单一 IP 产品,沉浸式体验,Unity 重资产合理
|
||||||
|
- 小型硬件整合成一个 APP 是为了**统一流量入口和用户管理**,调性轻快,不适合塞 Unity 这种重容器
|
||||||
|
|
||||||
|
### 4.2 小型硬件统一 APP 的渲染方案
|
||||||
|
|
||||||
|
按硬件对应的形象类型,APP 内嵌不同渲染层:
|
||||||
|
|
||||||
|
```
|
||||||
|
[小型硬件统一 APP(Flutter 推荐)]
|
||||||
|
├─ 用户中心 / 流量入口 / 统一登录
|
||||||
|
├─ 设备管理 / 配网 / 固件升级
|
||||||
|
├─ 多产品入口(卡皮巴拉、桌面机器人...)
|
||||||
|
└─ 形象渲染层(按设备角色卡热加载)
|
||||||
|
├─ Cubism Native(如果设备角色是 Live2D)
|
||||||
|
├─ WebView + three.js + three-vrm(如果是 VRM 3D 形象)
|
||||||
|
└─ SpriteRenderer(如果是序列帧形象,与 ESP32-S3 资产一致)
|
||||||
|
└─ 火山 RTC Flutter SDK(已有)
|
||||||
|
```
|
||||||
|
|
||||||
|
**关键决策**:小型硬件 APP **不用 Unity**——理由:
|
||||||
|
- APP 体积要小,启动要快(用户用完即走的轻量场景)
|
||||||
|
- 形象层多样化,要能热切换不同渲染方案
|
||||||
|
- Unity 编出的 APP 包大、启动慢,跟"流量入口 + 用户管理"调性不符
|
||||||
|
|
||||||
|
### 4.3 洛天依 APP 的演进
|
||||||
|
|
||||||
|
当前 Unity 工程在跑,下一步同步加 AI 驱动事件层(与罐子代码尽量复用)。
|
||||||
|
|
||||||
|
未来可能的演进方向:
|
||||||
|
- 短期(2026):保持 Unity,专注 AI 驱动改造
|
||||||
|
- 中长期:见第九章「未来技术栈迁移路径」
|
||||||
|
|
||||||
|
### 4.4 Live2D 形态(未来)
|
||||||
|
|
||||||
|
如果未来某个产品定位需要 Live2D(2D Q 版陪伴角色、复古二次元风格):
|
||||||
|
|
||||||
|
- **渲染**:Cubism Native SDK Android Java / iOS Objective-C++
|
||||||
|
- **嘴型驱动**:字时间戳 → `ParamMouthOpenY` / `ParamMouthForm`
|
||||||
|
- **资产生产**:外包 Live2D 师,5k-3w/形象(2 头身偏便宜)
|
||||||
|
- **商用授权**:见第六章
|
||||||
|
|
||||||
|
### 4.5 3D / VRM 路径(未来虚拟明星 / 写实数字人)
|
||||||
|
|
||||||
|
**适用场景**:未来立项的独立 IP 产品(如 CYBER STAR 虚拟艺人)、批量生产的标准化 3D 形象、jalab.ai 网页端展示。**不替代**罐子/洛天依 APP 现有 Unity 路径。
|
||||||
|
|
||||||
|
如果未来要做:
|
||||||
|
- 全身 / 360°视角的形象
|
||||||
|
- 多角色复用(VRoid Studio 出 VRM,pixiv 标准)
|
||||||
|
- 跨产品形象互通
|
||||||
|
|
||||||
|
**三条集成路径**:
|
||||||
|
|
||||||
|
| 路径 | 出活速度 | 性能 | 推荐场景 |
|
||||||
|
|---|---|---|---|
|
||||||
|
| **A. WebView + three.js + three-vrm** | 快(1-2 周) | 手机 60fps | 默认起步,可嵌入小型硬件统一 APP |
|
||||||
|
| **B. Unity as a Library + UniVRM** | 慢(4-6 周搭基建) | 最好 | 3D 是核心体验、独立 APP |
|
||||||
|
| **C. Filament(thermion)** | 中 | 好 | 2027 年再看 |
|
||||||
|
|
||||||
|
**Path A** = three-vrm 是 pixiv 官方维护,VRM 标准 blend shape / SpringBone / LookAt 全部内置。**这条路径与第 4.2 节的小型硬件统一 APP 渲染层兼容**——VRM 形象可以通过 WebView 容器嵌进去。
|
||||||
|
|
||||||
|
**重要澄清**:
|
||||||
|
- 洛天依**不需要**切到 VRM——Unity 自建模型已经在跑,继续用
|
||||||
|
- VRM 是给**未来批量生产 3D 形象**时用的标准,不是替代当前路径
|
||||||
|
|
||||||
|
### 4.6 序列帧(ESP32-S3)
|
||||||
|
|
||||||
|
ESP32-S3 没有 GLES 跑不了 Live2D / 3D。方案:
|
||||||
|
|
||||||
|
- 云端按形象 ID 预渲染 sprite sheet(emotion × 4-8 帧 + motion × 多个)
|
||||||
|
- 端侧 LVGL 状态机消费 emotion / motion frame
|
||||||
|
- **忽略字时间戳精度**(用音频包络替代驱动嘴型 3 档:闭/半开/全开)
|
||||||
|
- 简化下行带宽
|
||||||
|
|
||||||
|
**关键简化**:火山 RTC ESP32-S3 SDK 已经把信令回调暴露出来——不需要自写 mbedTLS WebSocket Client,直接挂回调即可。预估 C 代码量 < 200 行。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 五、目标硬件能力参考
|
||||||
|
|
||||||
|
| 项目 | CPU | GPU | NPU | 含义 |
|
||||||
|
|---|---|---|---|---|
|
||||||
|
| **RK3566** | 4× A55 @2.0GHz | Mali-G52 2EE | 1 TOPS | 跑单 Unity 3D 角色 + 简单 UI 可,720p 30fps |
|
||||||
|
| **RK3568** | 4× A55 @2.0GHz | Mali-G52 2EE(同 3566) | 0.8 TOPS | 工业版,外设丰富,渲染等价 |
|
||||||
|
| **RK3588S** | 4× A76 + 4× A55 | Mali-G610 MC4 | 6 TOPS | 1080p 60fps 轻松,可叠加端侧 ASR/VAD |
|
||||||
|
| 中端手机 | 骁龙 6/7 系或天玑 7/8 系 | Adreno 6xx / Mali-G68+ | >2 TOPS | 性能远超需求 |
|
||||||
|
|
||||||
|
> **命名澄清**:Rockchip 没有「RK3568S」型号。RK3566 是消费级,RK3568 是工业级,两者 GPU 同款(Mali-G52 2EE 双核)。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 六、Live2D 商用授权评估(未来用得上时再翻)
|
||||||
|
|
||||||
|
> 当前不立即用,但 Live2D 做商业产品需要提前 6 个月走法务流程。储备信息。
|
||||||
|
|
||||||
|
Cubism SDK 商用必须签 Publication License Agreement。
|
||||||
|
|
||||||
|
### 6.1 企业规模分级(按年销售额)
|
||||||
|
|
||||||
|
| 分级 | 年销售门槛(日元) | 约合人民币 |
|
||||||
|
|---|---|---|
|
||||||
|
| General User(个人) | < 1000 万 | < 50 万 RMB |
|
||||||
|
| Small(小企业) | < 1000 万 | 免授权费(部分 AI App 除外) |
|
||||||
|
| Middle(中企业) | 1000 万 ~ 1 亿 | 50 万 ~ 500 万 RMB |
|
||||||
|
| Large(大企业) | ≥ 1 亿 | ≥ 500 万 RMB |
|
||||||
|
|
||||||
|
> ⚠️ 杰森气元和火山 1000 万 CNY/年 框架已签,按汇率约合 2 亿日元,已达 Large-Scale。需要法务介入正式评估。
|
||||||
|
|
||||||
|
### 6.2 AI / Chatbot 应用:Expandable Application 特殊审核
|
||||||
|
|
||||||
|
Live2D 官方明确:「使用 Cubism SDK 作为 AI 或 chatbot 接口的内容」需要走 Expandable Application 流程,**需要 Live2D Inc. 单独审核批准并签订专属 Publication License**。情感陪伴 Agent / 虚拟伴侣类产品**不能套用普通 plan**。
|
||||||
|
|
||||||
|
### 6.3 费用结构参考(Running Royalty Plan)
|
||||||
|
|
||||||
|
- **Large-Scale**:初始费 30 万日元/Region;月费 10 万日元/平台/Region(iOS / Android / HarmonyOS / Web 各算一个平台)
|
||||||
|
- **Middle-Scale**:初始费 5 万日元/Region;月费 2 万日元/平台/Region
|
||||||
|
- 显示 Live2D logo + Showcase 列出可享折扣价
|
||||||
|
|
||||||
|
> **建议**:PoC 阶段不走商用 license,先确认技术路径和形象效果。商业化前 6 个月让法务联系 Live2D 中国区代理,按 Expandable Application 流程提交方案。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 七、风险与待验证项
|
||||||
|
|
||||||
|
修订后剩下的不确定项收敛到很少:
|
||||||
|
|
||||||
|
### 7.1 必须 PoC 验证的(决定算法选型)
|
||||||
|
|
||||||
|
**SubtitleMode=0 实际下发的时间戳精度**:
|
||||||
|
- 是字级(每个字带 begin_ms / end_ms)?还是只到句级?
|
||||||
|
- 时间戳与音频流的实际对齐误差(理想 < 50ms)
|
||||||
|
|
||||||
|
**验证方法**(半天工作量):
|
||||||
|
1. 服务端启用 SubtitleMode=0 调用 StartVoiceChat
|
||||||
|
2. 端侧打印每个 subtitle 消息的完整 JSON
|
||||||
|
3. 同时录音频流
|
||||||
|
4. 用 Audacity 对比字幕时间戳与音频实际发声时间
|
||||||
|
|
||||||
|
**结果分支**:
|
||||||
|
- **结果 A:字级对齐**(最佳)→ 直接驱动嘴型,精度满足 lip sync
|
||||||
|
- **结果 B:只到句级** → 端侧加音量包络辅助:句级时间戳确定开始/结束,音量包络驱动开合度
|
||||||
|
- **结果 C:完全不返回时间戳** → 退回纯音量驱动(精度仍够洛天依的 Q 版张合用)
|
||||||
|
|
||||||
|
无论哪种结果,**整体架构都不变**,只是端侧驱动算法层换实现。
|
||||||
|
|
||||||
|
### 7.2 实操中需要彭铭聪 / 业昌 / 文琦决定的
|
||||||
|
|
||||||
|
- 现有 Animator state 能否被 RTC AI 状态事件直接驱动?(需要 review 现有状态机设计)
|
||||||
|
- Function Calling 触发预制 motion 的工程实现路径(在 Unity 端怎么 dispatch)
|
||||||
|
- 第 3.3 节 `ICharacterRenderer` 接口的具体方法集和签名(这是未来跨引擎可移植性的关键)
|
||||||
|
- 全息光仓 shader 在 RK3566 上的实测帧率和热表现
|
||||||
|
|
||||||
|
### 7.3 已被消除的不确定项(不再需要验证)
|
||||||
|
|
||||||
|
- ~~豆包 TTS 是否独立返回 phoneme 时间戳~~ → RTC AIGC 把 TTS 包了,我们用 Subtitle
|
||||||
|
- ~~自建 WebSocket 协议设计~~ → 不需要,复用 RTC 信令
|
||||||
|
- ~~端侧自管理连接、心跳、重连~~ → RTC SDK 已处理
|
||||||
|
- ~~phoneme 编码 → IPA 映射表~~ → Subtitle 直接出可读文本
|
||||||
|
|
||||||
|
### 7.4 长期风险
|
||||||
|
|
||||||
|
- **Live2D Expandable Application 审核结果不确定**(未来用 Live2D 时再面对)
|
||||||
|
- **Live2D license 费用对中型企业较高**(同上)
|
||||||
|
- **Open-LLM-VTuber license 变更**(如果未来参考其架构需要注意 v1.2 之前是 MIT)
|
||||||
|
- **Unity 中国长期不确定性**(详见第九章)——这是 1-3 年维度的战略风险,当下通过分层架构防御
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 八、P0 行动清单
|
||||||
|
|
||||||
|
### 8.1 本周启动(5 月 12 日 ~ 5 月 16 日)
|
||||||
|
|
||||||
|
| # | 任务 | 负责人 | 工时 | 交付 |
|
||||||
|
|---|---|---|---|---|
|
||||||
|
| **P0-1** | 服务端 RTC AIGC 启用 SubtitleConfig,SubtitleMode=0 | 罐子组 / 业昌 | 0.5 天 | StartVoiceChat 配置就绪 |
|
||||||
|
| **P0-2** | Unity 端打印 subtitle 消息 JSON,验证时间戳精度 | 罐子组 | 0.5 天 | 时间戳精度报告(5 测试句对齐误差) |
|
||||||
|
| **P0-3** | Unity 端 TimelineConsumer + BlendShape 嘴型驱动 demo | 彭铭聪 | 1-2 天 | 罐子上洛天依 AI 对话时嘴型自动同步 |
|
||||||
|
| **P0-4** | Unity 端 RTC AI 状态事件 → Animator state 桥接 | 彭铭聪 | 1 天 | listening/thinking/speaking 自动切动作 |
|
||||||
|
|
||||||
|
**总工时**:3-4 人天,本周内可全部完成。
|
||||||
|
|
||||||
|
**P0 完成后的可见效果**:罐子上现有洛天依 3D 模型,AI 对话时嘴型自动驱动 + 状态自动切动作,**无需任何手 K 嘴型/动作衔接**。这就是从"美术资产成本无限堆高"切换到"AI 驱动"的关键拐点。
|
||||||
|
|
||||||
|
### 8.2 P1 - 2 周内
|
||||||
|
|
||||||
|
- 情绪事件接入:RTC AIGC 情绪信号 → Animator BlendTree(happy/sad/surprised...)
|
||||||
|
- Function Calling 触发预设 motion:定义第一批工具(挥手、点头、托腮、唱歌起手)
|
||||||
|
- 洛天依 APP 同步获得 AI 驱动能力(与罐子是同一份 Unity 工程,自动获益)
|
||||||
|
- 全息光仓适配在 RK3566 / RK3588S 上量化帧率
|
||||||
|
|
||||||
|
### 8.3 P2 - 1 个月内
|
||||||
|
|
||||||
|
- 端侧 SDK 抽象:把事件消费层封装成可复用模块(Unity Package / 各端 lib)
|
||||||
|
- 形象资产规范:motion 命名 / expression 集合 / BlendShape 标准化(为未来批量产形象做准备)
|
||||||
|
- ESP32-S3 sprite sheet 云端预渲染管线设计
|
||||||
|
|
||||||
|
### 8.4 未来项(按需启动)
|
||||||
|
|
||||||
|
- Live2D 商用授权流程启动(决定要做 Live2D 产品时提前 6 个月)
|
||||||
|
- VRM 标准接入(决定要批量 3D 形象时)
|
||||||
|
- 虚拟明星形象的高精度 viseme 升级(CYBER STAR 立项时)
|
||||||
|
- 端侧 Whisper + VAD 打断(RK3588S 高端方案,提升交互自然度)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 九、未来技术栈迁移路径
|
||||||
|
|
||||||
|
> 本章是给彭铭聪等核心开发者的中长期成长方向。Air 与彭铭聪已就此有过深谈:**核心开发者要跟着团队成长,不能永远只做 Unity**。本章把可能的迁移方向和触发条件写清楚,作为长期参考。
|
||||||
|
|
||||||
|
### 9.1 为什么要考虑迁移
|
||||||
|
|
||||||
|
Unity 当下是最优解,但 1-3 年维度有几个不确定项:
|
||||||
|
|
||||||
|
- **Unity 中国(团结引擎)独立运营路径不明朗**——版本分裂、定价模型、政策影响、海外版功能同步存在变数
|
||||||
|
- **Unity 商业模式变化历史**:2023 年的 Runtime Fee 风波说明商业政策可能突变
|
||||||
|
- **国产引擎政策导向**:信创、国产替代趋势可能影响 ToB 产品选型
|
||||||
|
- **Web 化与跨端化趋势**:Three.js / 小程序 3D / 浏览器 WebGPU 在变强,移动端原生不再是唯一答案
|
||||||
|
|
||||||
|
**我们不需要现在就换,但需要现在就让架构能换**。
|
||||||
|
|
||||||
|
### 9.2 触发迁移的信号
|
||||||
|
|
||||||
|
出现以下任一情况,启动迁移评估:
|
||||||
|
|
||||||
|
1. Unity 中国版收费模式或政策对我们形成实质成本/合规压力
|
||||||
|
2. 跨端复用需求超出 Unity 能力边界(小程序、Web、HarmonyOS NEXT 等)
|
||||||
|
3. 国产化要求(ToB 项目、政府采购场景)明确排除海外引擎
|
||||||
|
4. 团队增长后多人维护 Unity 工程的协作成本超过迁移成本
|
||||||
|
5. 出现某个新形象类型,Unity 不是最优解(如 Live2D / VRM 标准 / 复杂 Web 场景)
|
||||||
|
|
||||||
|
### 9.3 候选技术栈对比
|
||||||
|
|
||||||
|
| 候选 | 适配场景 | 优势 | 劣势 | 切换难度 |
|
||||||
|
|---|---|---|---|---|
|
||||||
|
| **Cocos Creator 4** | 主推备选 | 国产、政策最稳;团队已有 Cocos 能力(Avatar Social 在用);3D 能力够用 | 3D 生态不如 Unity 成熟;shader / 物理 / 后处理需重写 | 中(资产可部分复用,逻辑层完全可移植) |
|
||||||
|
| **Three.js + three-vrm** | 走 Web / 跨端轻量化 | 跨平台、Web 化、VRM 标准化、生态活跃 | 性能不如原生;复杂角色掉帧;需要 WebView 容器 | 低(如果走 VRM 标准) |
|
||||||
|
| **Godot 4** | 完全开源场景 | MIT 开源、免费、跨平台 | 国内生态弱、找开发者难、3D 工具链一般 | 中 |
|
||||||
|
| **Unreal Engine** | 顶级画质场景 | 顶级 PBR / 物理 / 影视级渲染 | 包体大、移动端 / 嵌入式不友好;学习曲线陡 | 高 |
|
||||||
|
| **自研 + Three.js / Filament** | 完全可控 | 100% 自主 | 投入巨大,不适合现阶段 | 不考虑 |
|
||||||
|
|
||||||
|
**主推备选是 Cocos Creator 4**:
|
||||||
|
- Avatar Social 项目团队已经在用,能力沉淀可复用
|
||||||
|
- 国产引擎政策最稳
|
||||||
|
- 资产工作流(建模 → 导入 → 动画)和 Unity 接近,迁移学习曲线低
|
||||||
|
- 嵌入式(罐子 RK 板)和手机都能跑
|
||||||
|
|
||||||
|
**Three.js / three-vrm 是另一类备选**:
|
||||||
|
- 不是替代 Unity 做主力,而是补充
|
||||||
|
- 跨端 Web 容器 / 小程序 / VRM 标准角色 用它最合适
|
||||||
|
- 未来如果做 jalab.ai 网页端虚拟艺人展示,必然走 Three.js
|
||||||
|
|
||||||
|
### 9.4 迁移成本估算(以 Cocos 为例)
|
||||||
|
|
||||||
|
按"洛天依完整迁移到 Cocos"测算:
|
||||||
|
|
||||||
|
| 项 | 工作量 | 备注 |
|
||||||
|
|---|---|---|
|
||||||
|
| 3D 资产重新导入 + 材质适配 | 1-2 周 | FBX 可直接导入,shader 要重写 |
|
||||||
|
| Animator 状态机重建 | 1 周 | Cocos animation system 不同 |
|
||||||
|
| 全息光仓 shader 重写 | 1 周 | 后处理管线不同 |
|
||||||
|
| 业务逻辑层接入 | 0.5 天 | **如果做了第 3.3 节的分层,这一步几乎零成本** |
|
||||||
|
| RTC SDK 适配 | 0.5 天 | 火山 RTC Cocos 集成已有 |
|
||||||
|
| 联调与优化 | 1-2 周 | 性能调优、bug 修 |
|
||||||
|
| **总计** | **4-6 周** | 一个工程师全职 |
|
||||||
|
|
||||||
|
**关键洞察**:业务逻辑层(RTC 解析、TimelineConsumer、CharacterStateMachine)的迁移成本几乎为零——因为它们是纯 C# 逻辑,不绑 Unity API。**前提是第 3.3 节的分层设计严格执行**。
|
||||||
|
|
||||||
|
### 9.5 给彭铭聪的成长方向
|
||||||
|
|
||||||
|
短期(2026 年):
|
||||||
|
- 把洛天依 Unity 工程做深做透
|
||||||
|
- AI 驱动事件层是这一年的核心 milestone
|
||||||
|
- 通过分层设计积累"引擎无关"的工程思维
|
||||||
|
|
||||||
|
中期(2027 年):
|
||||||
|
- 学习 Cocos Creator 4,参与 Avatar Social 等 Cocos 项目交叉协作
|
||||||
|
- 评估某个新产品形态用 Cocos 实现的可能性
|
||||||
|
- 主导一次小规模技术栈迁移试点(不动洛天依,从新立项的小产品开始)
|
||||||
|
|
||||||
|
长期(2-3 年后):
|
||||||
|
- 成为"形象渲染层"的技术决策者,能根据产品需求选最合适的引擎
|
||||||
|
- 不被 Unity 这一个工具锁死,技能围绕"3D / 实时渲染 / 角色驱动"这个领域而非"Unity 这个引擎"
|
||||||
|
|
||||||
|
**这是一个工程师从"工具使用者"到"领域专家"的转变路径**。
|
||||||
|
|
||||||
|
### 9.6 当下要做的事
|
||||||
|
|
||||||
|
1. ✅ **架构上做防御**:第 3.3 节的分层设计严格执行
|
||||||
|
2. ✅ **业务逻辑不绑引擎 API**:所有渲染调用走 `ICharacterRenderer` 接口
|
||||||
|
3. ⚠️ **不要现在迁移**:当前痛点是美术资产成本,不是引擎选型,先做完 P0 AI 驱动改造
|
||||||
|
4. ⚠️ **不要在两个引擎间来回切**:决定迁移时一次切换到位,不要并行维护两套
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 附录 A:参考项目链接
|
||||||
|
|
||||||
|
**核心参考(直接对接)**:
|
||||||
|
- 火山 RTC AIGC Demo(重点参考): <https://github.com/volcengine/rtc-aigc-demo>
|
||||||
|
- 火山 RTC AI 实时字幕文档: <https://www.volcengine.com/docs/6348/1337284>
|
||||||
|
- 火山 RTC 获取 AI 状态文档: <https://www.volcengine.com/docs/6348/1415216>
|
||||||
|
- 火山 RTC 情绪识别与生成: <https://www.volcengine.com/docs/6348/2139328>
|
||||||
|
- 火山 RTC Function Calling: <https://www.volcengine.com/docs/6348/1554654>
|
||||||
|
- 火山 RTC 嵌入式硬件集成: <https://www.volcengine.com/docs/6348/1438400>
|
||||||
|
|
||||||
|
**反例(已排除,仅作判断依据)**:
|
||||||
|
- 火山 RTC AI 数字人形象文档: <https://www.volcengine.com/docs/6348/1848567> ← 这是云端渲染推视频流方案,**我们不用这个**,但通过它的文档反推出 SubtitleMode=0 是给端侧渲染用的
|
||||||
|
|
||||||
|
**储备方案参考**:
|
||||||
|
- Cubism SDK 下载: <https://www.live2d.com/en/sdk/download/native/>
|
||||||
|
- Cubism License: <https://www.live2d.com/en/sdk/license/>
|
||||||
|
- three-vrm(pixiv 官方): <https://github.com/pixiv/three-vrm>
|
||||||
|
- TalkingHead(VRM lipsync 参考): <https://github.com/met4citizen/TalkingHead>
|
||||||
|
- Open-LLM-VTuber(参考架构): <https://github.com/Open-LLM-VTuber/Open-LLM-VTuber>
|
||||||
|
- Cocos Creator 4 文档: <https://docs.cocos.com/creator/manual/zh/>
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 附录 B:术语速查
|
||||||
|
|
||||||
|
- **BlendShape / Morph Target**:3D 模型的形变权重,控制嘴型 / 表情 / 五官变化
|
||||||
|
- **Animator / Mecanim**:Unity 的状态机动画系统
|
||||||
|
- **Viseme(视位)**:发音对应的视觉嘴型,多对一映射音素
|
||||||
|
- **Subtitle**:火山 RTC AIGC 下发的字幕事件,包含字内容和时间戳
|
||||||
|
- **SubtitleMode**:火山 RTC 配置项,0 = 对齐音频时间戳(端侧渲染用),1 = 不对齐(云端数字人用)
|
||||||
|
- **Function Calling**:LLM 调用工具/函数的能力,可被业务端解析触发动作
|
||||||
|
- **Cubism**:Live2D 公司的 SDK 品牌
|
||||||
|
- **VRM**:基于 glTF 的 3D 角色文件标准,pixiv 主导
|
||||||
|
- **全息光仓**:LCD 拆背光后的伪全息显示形态,黑像素=透明,亮像素=发光
|
||||||
|
- **Expandable Application**:Live2D 官方对 AI/Chatbot 类应用的特殊分类,需单独审核签约
|
||||||
6
avatar-h5-renderer/copy_resources.js
Normal file
6
avatar-h5-renderer/copy_resources.js
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
/**
|
||||||
|
* Resources are already copied during initial setup into ./public/.
|
||||||
|
* This script is a no-op kept for npm script compatibility.
|
||||||
|
*/
|
||||||
|
"use strict";
|
||||||
|
console.log('[copy_resources] Resources already populated in ./public/.');
|
||||||
168
avatar-h5-renderer/eslint.config.mjs
Normal file
168
avatar-h5-renderer/eslint.config.mjs
Normal file
@ -0,0 +1,168 @@
|
|||||||
|
import eslint from '@eslint/js';
|
||||||
|
import tseslint from 'typescript-eslint';
|
||||||
|
import globals from 'globals';
|
||||||
|
import eslintConfigPrettier from 'eslint-config-prettier';
|
||||||
|
import eslintPluginPrettier from 'eslint-plugin-prettier';
|
||||||
|
|
||||||
|
export default tseslint.config(
|
||||||
|
eslint.configs.recommended,
|
||||||
|
tseslint.configs.eslintRecommended,
|
||||||
|
...tseslint.configs.recommended,
|
||||||
|
...tseslint.configs.recommendedTypeChecked,
|
||||||
|
eslintConfigPrettier,
|
||||||
|
{
|
||||||
|
languageOptions:
|
||||||
|
{
|
||||||
|
parserOptions:
|
||||||
|
{
|
||||||
|
sourceType: 'module',
|
||||||
|
ecmaVersion: 2020,
|
||||||
|
project: './tsconfig.json',
|
||||||
|
},
|
||||||
|
globals:
|
||||||
|
{
|
||||||
|
...globals.browser,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
plugins:
|
||||||
|
{
|
||||||
|
'prettier': eslintPluginPrettier,
|
||||||
|
},
|
||||||
|
rules:
|
||||||
|
{
|
||||||
|
'prettier/prettier': [
|
||||||
|
'error',
|
||||||
|
{
|
||||||
|
singleQuote: true,
|
||||||
|
trailingComma: 'none',
|
||||||
|
arrowParens: 'avoid',
|
||||||
|
}
|
||||||
|
],
|
||||||
|
camelcase: 'off',
|
||||||
|
'@typescript-eslint/naming-convention': [
|
||||||
|
'warn',
|
||||||
|
{
|
||||||
|
selector: 'default',
|
||||||
|
format: ['camelCase'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
selector: 'import',
|
||||||
|
format: ['PascalCase'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
selector: 'variable',
|
||||||
|
format: [],
|
||||||
|
custom: {
|
||||||
|
// 指定の文字列で始まるものと特定の文字を含むものは許容
|
||||||
|
regex: '^[A-Z]|^csm|^iterator|Shader',
|
||||||
|
match: true,
|
||||||
|
},
|
||||||
|
modifiers: ['exported', 'const'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
selector: 'variable',
|
||||||
|
format: ['camelCase'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
selector: 'variable',
|
||||||
|
format: [],
|
||||||
|
custom: {
|
||||||
|
// 指定の文字列で始まるものは許容
|
||||||
|
regex: '^[A-Z]|^s_',
|
||||||
|
match: true,
|
||||||
|
},
|
||||||
|
modifiers: ['global']
|
||||||
|
},
|
||||||
|
{
|
||||||
|
selector: 'enum',
|
||||||
|
format: ['PascalCase'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
selector: 'enumMember',
|
||||||
|
format: [],
|
||||||
|
custom: {
|
||||||
|
// 大文字から始まること
|
||||||
|
regex: '^[A-Z]',
|
||||||
|
match: true,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
selector: 'classProperty',
|
||||||
|
format: ['PascalCase'],
|
||||||
|
modifiers: ['static', 'readonly']
|
||||||
|
},
|
||||||
|
{
|
||||||
|
selector: 'classProperty',
|
||||||
|
format: ['camelCase'],
|
||||||
|
leadingUnderscore: 'allow',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
selector: 'class',
|
||||||
|
format: [],
|
||||||
|
custom: {
|
||||||
|
// 指定の文字列で始まるか、指定の文字列で終わること
|
||||||
|
regex: '^[A-Z]|^csm|^iterator|_WebGL$',
|
||||||
|
match: true,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
selector: 'interface',
|
||||||
|
format: ['camelCase', 'PascalCase'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
selector: 'parameter',
|
||||||
|
format: ['camelCase'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
selector: 'classMethod',
|
||||||
|
format: ['camelCase'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
selector: 'objectLiteralProperty',
|
||||||
|
format: ['camelCase', 'PascalCase'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
selector: 'typeAlias',
|
||||||
|
format: [],
|
||||||
|
custom: {
|
||||||
|
// 指定の文字列で始まるものは許容
|
||||||
|
regex: '^[A-Z]|^[a-z]|^CSM_|^csm|^iterator',
|
||||||
|
match: true,
|
||||||
|
},
|
||||||
|
modifiers: ['exported']
|
||||||
|
},
|
||||||
|
{
|
||||||
|
selector: 'typeAlias',
|
||||||
|
format: ['camelCase'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
selector: 'typeParameter',
|
||||||
|
format: [],
|
||||||
|
custom:
|
||||||
|
{
|
||||||
|
// 「大文字+アンダースコア以外の文字」、あるいは「大文字1文字」
|
||||||
|
// あるいは、「`T`+アンダースコア」で始まる場合
|
||||||
|
regex: '^[A-Z][^_]|^[A-Z]|^T_$',
|
||||||
|
match: true,
|
||||||
|
},
|
||||||
|
leadingUnderscore: 'allow',
|
||||||
|
},
|
||||||
|
], // @typescript-eslint/naming-convention
|
||||||
|
'@typescript-eslint/no-use-before-define': 'off',
|
||||||
|
'@typescript-eslint/ban-ts-comment': 'off',
|
||||||
|
'@typescript-eslint/unbound-method': 'off',
|
||||||
|
'@typescript-eslint/no-unsafe-assignment': 'off',
|
||||||
|
'@typescript-eslint/no-unsafe-return': 'off',
|
||||||
|
'@typescript-eslint/no-floating-promises': 'off',
|
||||||
|
'@typescript-eslint/no-unused-vars': 'off',
|
||||||
|
'@typescript-eslint/no-explicit-any': 'off',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// ignores property はなぜか単独で指定していないと効果がない。
|
||||||
|
ignores: [
|
||||||
|
'**/*.*',
|
||||||
|
'!src/**/*.ts',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
);
|
||||||
20
avatar-h5-renderer/framework/package.json
Normal file
20
avatar-h5-renderer/framework/package.json
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
"private": true,
|
||||||
|
"scripts": {
|
||||||
|
"build": "tsc",
|
||||||
|
"test": "tsc --noEmit",
|
||||||
|
"lint": "eslint",
|
||||||
|
"lint:fix": "eslint --fix",
|
||||||
|
"clean": "rimraf dist"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@eslint/js": "^9.39.2",
|
||||||
|
"eslint": "^9.39.2",
|
||||||
|
"eslint-config-prettier": "^10.1.8",
|
||||||
|
"eslint-plugin-prettier": "^5.5.5",
|
||||||
|
"prettier": "^3.8.1",
|
||||||
|
"rimraf": "^6.1.3",
|
||||||
|
"typescript": "^5.9.3",
|
||||||
|
"typescript-eslint": "^8.57.2"
|
||||||
|
}
|
||||||
|
}
|
||||||
118
avatar-h5-renderer/framework/src/cubismdefaultparameterid.ts
Normal file
118
avatar-h5-renderer/framework/src/cubismdefaultparameterid.ts
Normal file
@ -0,0 +1,118 @@
|
|||||||
|
/**
|
||||||
|
* Copyright(c) Live2D Inc. All rights reserved.
|
||||||
|
*
|
||||||
|
* Use of this source code is governed by the Live2D Open Software license
|
||||||
|
* that can be found at https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief パラメータIDのデフォルト値を保持する定数<br>
|
||||||
|
* デフォルト値の仕様は以下のマニュアルに基づく<br>
|
||||||
|
* https://docs.live2d.com/cubism-editor-manual/standard-parametor-list/
|
||||||
|
*/
|
||||||
|
export const CubismDefaultParameterId = Object.freeze<Record<string, string>>({
|
||||||
|
// パーツID
|
||||||
|
HitAreaPrefix: 'HitArea',
|
||||||
|
HitAreaHead: 'Head',
|
||||||
|
HitAreaBody: 'Body',
|
||||||
|
PartsIdCore: 'Parts01Core',
|
||||||
|
PartsArmPrefix: 'Parts01Arm_',
|
||||||
|
PartsArmLPrefix: 'Parts01ArmL_',
|
||||||
|
PartsArmRPrefix: 'Parts01ArmR_',
|
||||||
|
// パラメータID
|
||||||
|
ParamAngleX: 'ParamAngleX',
|
||||||
|
ParamAngleY: 'ParamAngleY',
|
||||||
|
ParamAngleZ: 'ParamAngleZ',
|
||||||
|
ParamEyeLOpen: 'ParamEyeLOpen',
|
||||||
|
ParamEyeLSmile: 'ParamEyeLSmile',
|
||||||
|
ParamEyeROpen: 'ParamEyeROpen',
|
||||||
|
ParamEyeRSmile: 'ParamEyeRSmile',
|
||||||
|
ParamEyeBallX: 'ParamEyeBallX',
|
||||||
|
ParamEyeBallY: 'ParamEyeBallY',
|
||||||
|
ParamEyeBallForm: 'ParamEyeBallForm',
|
||||||
|
ParamBrowLY: 'ParamBrowLY',
|
||||||
|
ParamBrowRY: 'ParamBrowRY',
|
||||||
|
ParamBrowLX: 'ParamBrowLX',
|
||||||
|
ParamBrowRX: 'ParamBrowRX',
|
||||||
|
ParamBrowLAngle: 'ParamBrowLAngle',
|
||||||
|
ParamBrowRAngle: 'ParamBrowRAngle',
|
||||||
|
ParamBrowLForm: 'ParamBrowLForm',
|
||||||
|
ParamBrowRForm: 'ParamBrowRForm',
|
||||||
|
ParamMouthForm: 'ParamMouthForm',
|
||||||
|
ParamMouthOpenY: 'ParamMouthOpenY',
|
||||||
|
ParamCheek: 'ParamCheek',
|
||||||
|
ParamBodyAngleX: 'ParamBodyAngleX',
|
||||||
|
ParamBodyAngleY: 'ParamBodyAngleY',
|
||||||
|
ParamBodyAngleZ: 'ParamBodyAngleZ',
|
||||||
|
ParamBreath: 'ParamBreath',
|
||||||
|
ParamArmLA: 'ParamArmLA',
|
||||||
|
ParamArmRA: 'ParamArmRA',
|
||||||
|
ParamArmLB: 'ParamArmLB',
|
||||||
|
ParamArmRB: 'ParamArmRB',
|
||||||
|
ParamHandL: 'ParamHandL',
|
||||||
|
ParamHandR: 'ParamHandR',
|
||||||
|
ParamHairFront: 'ParamHairFront',
|
||||||
|
ParamHairSide: 'ParamHairSide',
|
||||||
|
ParamHairBack: 'ParamHairBack',
|
||||||
|
ParamHairFluffy: 'ParamHairFluffy',
|
||||||
|
ParamShoulderY: 'ParamShoulderY',
|
||||||
|
ParamBustX: 'ParamBustX',
|
||||||
|
ParamBustY: 'ParamBustY',
|
||||||
|
ParamBaseX: 'ParamBaseX',
|
||||||
|
ParamBaseY: 'ParamBaseY',
|
||||||
|
ParamNONE: 'NONE:'
|
||||||
|
});
|
||||||
|
|
||||||
|
// Namespace definition for compatibility.
|
||||||
|
import * as $ from './cubismdefaultparameterid';
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-namespace
|
||||||
|
export namespace Live2DCubismFramework {
|
||||||
|
export const HitAreaBody = $.CubismDefaultParameterId.HitAreaBody;
|
||||||
|
export const HitAreaHead = $.CubismDefaultParameterId.HitAreaHead;
|
||||||
|
export const HitAreaPrefix = $.CubismDefaultParameterId.HitAreaPrefix;
|
||||||
|
export const ParamAngleX = $.CubismDefaultParameterId.ParamAngleX;
|
||||||
|
export const ParamAngleY = $.CubismDefaultParameterId.ParamAngleY;
|
||||||
|
export const ParamAngleZ = $.CubismDefaultParameterId.ParamAngleZ;
|
||||||
|
export const ParamArmLA = $.CubismDefaultParameterId.ParamArmLA;
|
||||||
|
export const ParamArmLB = $.CubismDefaultParameterId.ParamArmLB;
|
||||||
|
export const ParamArmRA = $.CubismDefaultParameterId.ParamArmRA;
|
||||||
|
export const ParamArmRB = $.CubismDefaultParameterId.ParamArmRB;
|
||||||
|
export const ParamBaseX = $.CubismDefaultParameterId.ParamBaseX;
|
||||||
|
export const ParamBaseY = $.CubismDefaultParameterId.ParamBaseY;
|
||||||
|
export const ParamBodyAngleX = $.CubismDefaultParameterId.ParamBodyAngleX;
|
||||||
|
export const ParamBodyAngleY = $.CubismDefaultParameterId.ParamBodyAngleY;
|
||||||
|
export const ParamBodyAngleZ = $.CubismDefaultParameterId.ParamBodyAngleZ;
|
||||||
|
export const ParamBreath = $.CubismDefaultParameterId.ParamBreath;
|
||||||
|
export const ParamBrowLAngle = $.CubismDefaultParameterId.ParamBrowLAngle;
|
||||||
|
export const ParamBrowLForm = $.CubismDefaultParameterId.ParamBrowLForm;
|
||||||
|
export const ParamBrowLX = $.CubismDefaultParameterId.ParamBrowLX;
|
||||||
|
export const ParamBrowLY = $.CubismDefaultParameterId.ParamBrowLY;
|
||||||
|
export const ParamBrowRAngle = $.CubismDefaultParameterId.ParamBrowRAngle;
|
||||||
|
export const ParamBrowRForm = $.CubismDefaultParameterId.ParamBrowRForm;
|
||||||
|
export const ParamBrowRX = $.CubismDefaultParameterId.ParamBrowRX;
|
||||||
|
export const ParamBrowRY = $.CubismDefaultParameterId.ParamBrowRY;
|
||||||
|
export const ParamBustX = $.CubismDefaultParameterId.ParamBustX;
|
||||||
|
export const ParamBustY = $.CubismDefaultParameterId.ParamBustY;
|
||||||
|
export const ParamCheek = $.CubismDefaultParameterId.ParamCheek;
|
||||||
|
export const ParamEyeBallForm = $.CubismDefaultParameterId.ParamEyeBallForm;
|
||||||
|
export const ParamEyeBallX = $.CubismDefaultParameterId.ParamEyeBallX;
|
||||||
|
export const ParamEyeBallY = $.CubismDefaultParameterId.ParamEyeBallY;
|
||||||
|
export const ParamEyeLOpen = $.CubismDefaultParameterId.ParamEyeLOpen;
|
||||||
|
export const ParamEyeLSmile = $.CubismDefaultParameterId.ParamEyeLSmile;
|
||||||
|
export const ParamEyeROpen = $.CubismDefaultParameterId.ParamEyeROpen;
|
||||||
|
export const ParamEyeRSmile = $.CubismDefaultParameterId.ParamEyeRSmile;
|
||||||
|
export const ParamHairBack = $.CubismDefaultParameterId.ParamHairBack;
|
||||||
|
export const ParamHairFluffy = $.CubismDefaultParameterId.ParamHairFluffy;
|
||||||
|
export const ParamHairFront = $.CubismDefaultParameterId.ParamHairFront;
|
||||||
|
export const ParamHairSide = $.CubismDefaultParameterId.ParamHairSide;
|
||||||
|
export const ParamHandL = $.CubismDefaultParameterId.ParamHandL;
|
||||||
|
export const ParamHandR = $.CubismDefaultParameterId.ParamHandR;
|
||||||
|
export const ParamMouthForm = $.CubismDefaultParameterId.ParamMouthForm;
|
||||||
|
export const ParamMouthOpenY = $.CubismDefaultParameterId.ParamMouthOpenY;
|
||||||
|
export const ParamNONE = $.CubismDefaultParameterId.ParamNONE;
|
||||||
|
export const ParamShoulderY = $.CubismDefaultParameterId.ParamShoulderY;
|
||||||
|
export const PartsArmLPrefix = $.CubismDefaultParameterId.PartsArmLPrefix;
|
||||||
|
export const PartsArmPrefix = $.CubismDefaultParameterId.PartsArmPrefix;
|
||||||
|
export const PartsArmRPrefix = $.CubismDefaultParameterId.PartsArmRPrefix;
|
||||||
|
export const PartsIdCore = $.CubismDefaultParameterId.PartsIdCore;
|
||||||
|
}
|
||||||
32
avatar-h5-renderer/framework/src/cubismframeworkconfig.ts
Normal file
32
avatar-h5-renderer/framework/src/cubismframeworkconfig.ts
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
/**
|
||||||
|
* Copyright(c) Live2D Inc. All rights reserved.
|
||||||
|
*
|
||||||
|
* Use of this source code is governed by the Live2D Open Software license
|
||||||
|
* that can be found at https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
|
||||||
|
*/
|
||||||
|
|
||||||
|
//========================================================
|
||||||
|
// ログ出力関数の設定
|
||||||
|
//========================================================
|
||||||
|
|
||||||
|
//---------- ログ出力レベル 選択項目 定義 ----------
|
||||||
|
// 詳細ログ出力設定
|
||||||
|
export const CSM_LOG_LEVEL_VERBOSE = 0;
|
||||||
|
// デバッグログ出力設定
|
||||||
|
export const CSM_LOG_LEVEL_DEBUG = 1;
|
||||||
|
// Infoログ出力設定
|
||||||
|
export const CSM_LOG_LEVEL_INFO = 2;
|
||||||
|
// 警告ログ出力設定
|
||||||
|
export const CSM_LOG_LEVEL_WARNING = 3;
|
||||||
|
// エラーログ出力設定
|
||||||
|
export const CSM_LOG_LEVEL_ERROR = 4;
|
||||||
|
// ログ出力オフ設定
|
||||||
|
export const CSM_LOG_LEVEL_OFF = 5;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ログ出力レベル設定。
|
||||||
|
*
|
||||||
|
* 強制的にログ出力レベルを変える時に定義を有効にする。
|
||||||
|
* CSM_LOG_LEVEL_VERBOSE ~ CSM_LOG_LEVEL_OFF を選択する。
|
||||||
|
*/
|
||||||
|
export const CSM_LOG_LEVEL: number = CSM_LOG_LEVEL_VERBOSE;
|
||||||
790
avatar-h5-renderer/framework/src/cubismmodelsettingjson.ts
Normal file
790
avatar-h5-renderer/framework/src/cubismmodelsettingjson.ts
Normal file
@ -0,0 +1,790 @@
|
|||||||
|
/**
|
||||||
|
* Copyright(c) Live2D Inc. All rights reserved.
|
||||||
|
*
|
||||||
|
* Use of this source code is governed by the Live2D Open Software license
|
||||||
|
* that can be found at https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { ICubismModelSetting } from './icubismmodelsetting';
|
||||||
|
import { CubismIdHandle } from './id/cubismid';
|
||||||
|
import { CubismFramework } from './live2dcubismframework';
|
||||||
|
import { CubismJson, Value } from './utils/cubismjson';
|
||||||
|
|
||||||
|
export enum FrequestNode {
|
||||||
|
FrequestNode_Groups, // getRoot().getValueByString(Groups)
|
||||||
|
FrequestNode_Moc, // getRoot().getValueByString(FileReferences).getValueByString(Moc)
|
||||||
|
FrequestNode_Motions, // getRoot().getValueByString(FileReferences).getValueByString(Motions)
|
||||||
|
FrequestNode_Expressions, // getRoot().getValueByString(FileReferences).getValueByString(Expressions)
|
||||||
|
FrequestNode_Textures, // getRoot().getValueByString(FileReferences).getValueByString(Textures)
|
||||||
|
FrequestNode_Physics, // getRoot().getValueByString(FileReferences).getValueByString(Physics)
|
||||||
|
FrequestNode_Pose, // getRoot().getValueByString(FileReferences).getValueByString(Pose)
|
||||||
|
FrequestNode_HitAreas // getRoot().getValueByString(HitAreas)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Model3Jsonパーサー
|
||||||
|
*
|
||||||
|
* model3.jsonファイルをパースして値を取得する
|
||||||
|
*/
|
||||||
|
export class CubismModelSettingJson extends ICubismModelSetting {
|
||||||
|
/**
|
||||||
|
* 引数付きコンストラクタ
|
||||||
|
*
|
||||||
|
* @param buffer Model3Jsonをバイト配列として読み込んだデータバッファ
|
||||||
|
* @param size Model3Jsonのデータサイズ
|
||||||
|
*/
|
||||||
|
public constructor(buffer: ArrayBuffer, size: number) {
|
||||||
|
super();
|
||||||
|
this._json = CubismJson.create(buffer, size);
|
||||||
|
|
||||||
|
if (this.getJson()) {
|
||||||
|
this._jsonValue = [
|
||||||
|
// 順番はenum FrequestNodeと一致させる
|
||||||
|
this.getJson().getRoot().getValueByString(this.groups),
|
||||||
|
this.getJson()
|
||||||
|
.getRoot()
|
||||||
|
.getValueByString(this.fileReferences)
|
||||||
|
.getValueByString(this.moc),
|
||||||
|
this.getJson()
|
||||||
|
.getRoot()
|
||||||
|
.getValueByString(this.fileReferences)
|
||||||
|
.getValueByString(this.motions),
|
||||||
|
this.getJson()
|
||||||
|
.getRoot()
|
||||||
|
.getValueByString(this.fileReferences)
|
||||||
|
.getValueByString(this.expressions),
|
||||||
|
this.getJson()
|
||||||
|
.getRoot()
|
||||||
|
.getValueByString(this.fileReferences)
|
||||||
|
.getValueByString(this.textures),
|
||||||
|
this.getJson()
|
||||||
|
.getRoot()
|
||||||
|
.getValueByString(this.fileReferences)
|
||||||
|
.getValueByString(this.physics),
|
||||||
|
this.getJson()
|
||||||
|
.getRoot()
|
||||||
|
.getValueByString(this.fileReferences)
|
||||||
|
.getValueByString(this.pose),
|
||||||
|
this.getJson().getRoot().getValueByString(this.hitAreas)
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* デストラクタ相当の処理
|
||||||
|
*/
|
||||||
|
public release(): void {
|
||||||
|
CubismJson.delete(this._json);
|
||||||
|
|
||||||
|
this._jsonValue = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CubismJsonオブジェクトを取得する
|
||||||
|
*
|
||||||
|
* @return CubismJson
|
||||||
|
*/
|
||||||
|
public getJson(): CubismJson {
|
||||||
|
return this._json;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mocファイルの名前を取得する
|
||||||
|
* @return Mocファイルの名前
|
||||||
|
*/
|
||||||
|
public getModelFileName(): string {
|
||||||
|
if (!this.isExistModelFile()) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
return this._jsonValue[FrequestNode.FrequestNode_Moc].getRawString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* モデルが使用するテクスチャの数を取得する
|
||||||
|
* テクスチャの数
|
||||||
|
*/
|
||||||
|
public getTextureCount(): number {
|
||||||
|
if (!this.isExistTextureFiles()) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return this._jsonValue[FrequestNode.FrequestNode_Textures].getSize();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* テクスチャが配置されたディレクトリの名前を取得する
|
||||||
|
* @return テクスチャが配置されたディレクトリの名前
|
||||||
|
*/
|
||||||
|
public getTextureDirectory(): string {
|
||||||
|
const texturePath = this._jsonValue[FrequestNode.FrequestNode_Textures]
|
||||||
|
.getValueByIndex(0)
|
||||||
|
.getRawString();
|
||||||
|
|
||||||
|
const pathArray = texturePath.split('/');
|
||||||
|
// 最後の要素はテクスチャ名なので不要
|
||||||
|
const arrayLength = pathArray.length - 1;
|
||||||
|
let textureDirectoryStr = '';
|
||||||
|
|
||||||
|
// 分割したパスを結合
|
||||||
|
for (let i = 0; i < arrayLength; i++) {
|
||||||
|
textureDirectoryStr += pathArray[i];
|
||||||
|
if (i < arrayLength - 1) {
|
||||||
|
textureDirectoryStr += '/';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return textureDirectoryStr;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* モデルが使用するテクスチャの名前を取得する
|
||||||
|
* @param index 配列のインデックス値
|
||||||
|
* @return テクスチャの名前
|
||||||
|
*/
|
||||||
|
public getTextureFileName(index: number): string {
|
||||||
|
return this._jsonValue[FrequestNode.FrequestNode_Textures]
|
||||||
|
.getValueByIndex(index)
|
||||||
|
.getRawString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* モデルに設定された当たり判定の数を取得する
|
||||||
|
* @return モデルに設定された当たり判定の数
|
||||||
|
*/
|
||||||
|
public getHitAreasCount(): number {
|
||||||
|
if (!this.isExistHitAreas()) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return this._jsonValue[FrequestNode.FrequestNode_HitAreas].getSize();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 当たり判定に設定されたIDを取得する
|
||||||
|
*
|
||||||
|
* @param index 配列のindex
|
||||||
|
* @return 当たり判定に設定されたID
|
||||||
|
*/
|
||||||
|
public getHitAreaId(index: number): CubismIdHandle {
|
||||||
|
return CubismFramework.getIdManager().getId(
|
||||||
|
this._jsonValue[FrequestNode.FrequestNode_HitAreas]
|
||||||
|
.getValueByIndex(index)
|
||||||
|
.getValueByString(this.id)
|
||||||
|
.getRawString()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 当たり判定に設定された名前を取得する
|
||||||
|
* @param index 配列のインデックス値
|
||||||
|
* @return 当たり判定に設定された名前
|
||||||
|
*/
|
||||||
|
public getHitAreaName(index: number): string {
|
||||||
|
return this._jsonValue[FrequestNode.FrequestNode_HitAreas]
|
||||||
|
.getValueByIndex(index)
|
||||||
|
.getValueByString(this.name)
|
||||||
|
.getRawString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 物理演算設定ファイルの名前を取得する
|
||||||
|
* @return 物理演算設定ファイルの名前
|
||||||
|
*/
|
||||||
|
public getPhysicsFileName(): string {
|
||||||
|
if (!this.isExistPhysicsFile()) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
return this._jsonValue[FrequestNode.FrequestNode_Physics].getRawString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* パーツ切り替え設定ファイルの名前を取得する
|
||||||
|
* @return パーツ切り替え設定ファイルの名前
|
||||||
|
*/
|
||||||
|
public getPoseFileName(): string {
|
||||||
|
if (!this.isExistPoseFile()) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
return this._jsonValue[FrequestNode.FrequestNode_Pose].getRawString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 表情設定ファイルの数を取得する
|
||||||
|
* @return 表情設定ファイルの数
|
||||||
|
*/
|
||||||
|
public getExpressionCount(): number {
|
||||||
|
if (!this.isExistExpressionFile()) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return this._jsonValue[FrequestNode.FrequestNode_Expressions].getSize();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 表情設定ファイルを識別する名前(別名)を取得する
|
||||||
|
* @param index 配列のインデックス値
|
||||||
|
* @return 表情の名前
|
||||||
|
*/
|
||||||
|
public getExpressionName(index: number): string {
|
||||||
|
return this._jsonValue[FrequestNode.FrequestNode_Expressions]
|
||||||
|
.getValueByIndex(index)
|
||||||
|
.getValueByString(this.name)
|
||||||
|
.getRawString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 表情設定ファイルの名前を取得する
|
||||||
|
* @param index 配列のインデックス値
|
||||||
|
* @return 表情設定ファイルの名前
|
||||||
|
*/
|
||||||
|
public getExpressionFileName(index: number): string {
|
||||||
|
return this._jsonValue[FrequestNode.FrequestNode_Expressions]
|
||||||
|
.getValueByIndex(index)
|
||||||
|
.getValueByString(this.filePath)
|
||||||
|
.getRawString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* モーショングループの数を取得する
|
||||||
|
* @return モーショングループの数
|
||||||
|
*/
|
||||||
|
public getMotionGroupCount(): number {
|
||||||
|
if (!this.isExistMotionGroups()) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return this._jsonValue[FrequestNode.FrequestNode_Motions].getKeys().length;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* モーショングループの名前を取得する
|
||||||
|
* @param index 配列のインデックス値
|
||||||
|
* @return モーショングループの名前
|
||||||
|
*/
|
||||||
|
public getMotionGroupName(index: number): string {
|
||||||
|
if (!this.isExistMotionGroups()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return this._jsonValue[FrequestNode.FrequestNode_Motions].getKeys()[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* モーショングループに含まれるモーションの数を取得する
|
||||||
|
* @param groupName モーショングループの名前
|
||||||
|
* @return モーショングループの数
|
||||||
|
*/
|
||||||
|
public getMotionCount(groupName: string): number {
|
||||||
|
if (!this.isExistMotionGroupName(groupName)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return this._jsonValue[FrequestNode.FrequestNode_Motions]
|
||||||
|
.getValueByString(groupName)
|
||||||
|
.getSize();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* グループ名とインデックス値からモーションファイル名を取得する
|
||||||
|
* @param groupName モーショングループの名前
|
||||||
|
* @param index 配列のインデックス値
|
||||||
|
* @return モーションファイルの名前
|
||||||
|
*/
|
||||||
|
public getMotionFileName(groupName: string, index: number): string {
|
||||||
|
if (!this.isExistMotionGroupName(groupName)) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
return this._jsonValue[FrequestNode.FrequestNode_Motions]
|
||||||
|
.getValueByString(groupName)
|
||||||
|
.getValueByIndex(index)
|
||||||
|
.getValueByString(this.filePath)
|
||||||
|
.getRawString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* モーションに対応するサウンドファイルの名前を取得する
|
||||||
|
* @param groupName モーショングループの名前
|
||||||
|
* @param index 配列のインデックス値
|
||||||
|
* @return サウンドファイルの名前
|
||||||
|
*/
|
||||||
|
public getMotionSoundFileName(groupName: string, index: number): string {
|
||||||
|
if (!this.isExistMotionSoundFile(groupName, index)) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
return this._jsonValue[FrequestNode.FrequestNode_Motions]
|
||||||
|
.getValueByString(groupName)
|
||||||
|
.getValueByIndex(index)
|
||||||
|
.getValueByString(this.soundPath)
|
||||||
|
.getRawString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* モーション開始時のフェードイン処理時間を取得する
|
||||||
|
* @param groupName モーショングループの名前
|
||||||
|
* @param index 配列のインデックス値
|
||||||
|
* @return フェードイン処理時間[秒]
|
||||||
|
*/
|
||||||
|
public getMotionFadeInTimeValue(groupName: string, index: number): number {
|
||||||
|
if (!this.isExistMotionFadeIn(groupName, index)) {
|
||||||
|
return -1.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return this._jsonValue[FrequestNode.FrequestNode_Motions]
|
||||||
|
.getValueByString(groupName)
|
||||||
|
.getValueByIndex(index)
|
||||||
|
.getValueByString(this.fadeInTime)
|
||||||
|
.toFloat();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* モーション終了時のフェードアウト処理時間を取得する
|
||||||
|
* @param groupName モーショングループの名前
|
||||||
|
* @param index 配列のインデックス値
|
||||||
|
* @return フェードアウト処理時間[秒]
|
||||||
|
*/
|
||||||
|
public getMotionFadeOutTimeValue(groupName: string, index: number): number {
|
||||||
|
if (!this.isExistMotionFadeOut(groupName, index)) {
|
||||||
|
return -1.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return this._jsonValue[FrequestNode.FrequestNode_Motions]
|
||||||
|
.getValueByString(groupName)
|
||||||
|
.getValueByIndex(index)
|
||||||
|
.getValueByString(this.fadeOutTime)
|
||||||
|
.toFloat();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ユーザーデータのファイル名を取得する
|
||||||
|
* @return ユーザーデータのファイル名
|
||||||
|
*/
|
||||||
|
public getUserDataFile(): string {
|
||||||
|
if (!this.isExistUserDataFile()) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.getJson()
|
||||||
|
.getRoot()
|
||||||
|
.getValueByString(this.fileReferences)
|
||||||
|
.getValueByString(this.userData)
|
||||||
|
.getRawString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* レイアウト情報を取得する
|
||||||
|
* @param outLayoutMap Mapクラスのインスタンス
|
||||||
|
* @return true レイアウト情報が存在する
|
||||||
|
* @return false レイアウト情報が存在しない
|
||||||
|
*/
|
||||||
|
public getLayoutMap(outLayoutMap: Map<string, number>): boolean {
|
||||||
|
// 存在しない要素にアクセスするとエラーになるためValueがnullの場合はnullを代入する
|
||||||
|
const map: Map<string, Value> = this.getJson()
|
||||||
|
.getRoot()
|
||||||
|
.getValueByString(this.layout)
|
||||||
|
.getMap();
|
||||||
|
|
||||||
|
if (map == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
let ret = false;
|
||||||
|
|
||||||
|
for (const element of map) {
|
||||||
|
outLayoutMap.set(element[0], element[1].toFloat());
|
||||||
|
ret = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 目パチに関連付けられたパラメータの数を取得する
|
||||||
|
* @return 目パチに関連付けられたパラメータの数
|
||||||
|
*/
|
||||||
|
public getEyeBlinkParameterCount(): number {
|
||||||
|
if (!this.isExistEyeBlinkParameters()) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
let num = 0;
|
||||||
|
for (
|
||||||
|
let i = 0;
|
||||||
|
i < this._jsonValue[FrequestNode.FrequestNode_Groups].getSize();
|
||||||
|
i++
|
||||||
|
) {
|
||||||
|
const refI: Value =
|
||||||
|
this._jsonValue[FrequestNode.FrequestNode_Groups].getValueByIndex(i);
|
||||||
|
if (refI.isNull() || refI.isError()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (refI.getValueByString(this.name).getRawString() == this.eyeBlink) {
|
||||||
|
num = refI.getValueByString(this.ids).getVector().length;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return num;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 目パチに関連付けられたパラメータのIDを取得する
|
||||||
|
* @param index 配列のインデックス値
|
||||||
|
* @return パラメータID
|
||||||
|
*/
|
||||||
|
public getEyeBlinkParameterId(index: number): CubismIdHandle {
|
||||||
|
if (!this.isExistEyeBlinkParameters()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (
|
||||||
|
let i = 0;
|
||||||
|
i < this._jsonValue[FrequestNode.FrequestNode_Groups].getSize();
|
||||||
|
i++
|
||||||
|
) {
|
||||||
|
const refI: Value =
|
||||||
|
this._jsonValue[FrequestNode.FrequestNode_Groups].getValueByIndex(i);
|
||||||
|
if (refI.isNull() || refI.isError()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (refI.getValueByString(this.name).getRawString() == this.eyeBlink) {
|
||||||
|
return CubismFramework.getIdManager().getId(
|
||||||
|
refI.getValueByString(this.ids).getValueByIndex(index).getRawString()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* リップシンクに関連付けられたパラメータの数を取得する
|
||||||
|
* @return リップシンクに関連付けられたパラメータの数
|
||||||
|
*/
|
||||||
|
public getLipSyncParameterCount(): number {
|
||||||
|
if (!this.isExistLipSyncParameters()) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
let num = 0;
|
||||||
|
for (
|
||||||
|
let i = 0;
|
||||||
|
i < this._jsonValue[FrequestNode.FrequestNode_Groups].getSize();
|
||||||
|
i++
|
||||||
|
) {
|
||||||
|
const refI: Value =
|
||||||
|
this._jsonValue[FrequestNode.FrequestNode_Groups].getValueByIndex(i);
|
||||||
|
if (refI.isNull() || refI.isError()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (refI.getValueByString(this.name).getRawString() == this.lipSync) {
|
||||||
|
num = refI.getValueByString(this.ids).getVector().length;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return num;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* リップシンクに関連付けられたパラメータの数を取得する
|
||||||
|
* @param index 配列のインデックス値
|
||||||
|
* @return パラメータID
|
||||||
|
*/
|
||||||
|
public getLipSyncParameterId(index: number): CubismIdHandle {
|
||||||
|
if (!this.isExistLipSyncParameters()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (
|
||||||
|
let i = 0;
|
||||||
|
i < this._jsonValue[FrequestNode.FrequestNode_Groups].getSize();
|
||||||
|
i++
|
||||||
|
) {
|
||||||
|
const refI: Value =
|
||||||
|
this._jsonValue[FrequestNode.FrequestNode_Groups].getValueByIndex(i);
|
||||||
|
if (refI.isNull() || refI.isError()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (refI.getValueByString(this.name).getRawString() == this.lipSync) {
|
||||||
|
return CubismFramework.getIdManager().getId(
|
||||||
|
refI.getValueByString(this.ids).getValueByIndex(index).getRawString()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* モデルファイルのキーが存在するかどうかを確認する
|
||||||
|
* @return true キーが存在する
|
||||||
|
* @return false キーが存在しない
|
||||||
|
*/
|
||||||
|
protected isExistModelFile(): boolean {
|
||||||
|
const node: Value = this._jsonValue[FrequestNode.FrequestNode_Moc];
|
||||||
|
return !node.isNull() && !node.isError();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* テクスチャファイルのキーが存在するかどうかを確認する
|
||||||
|
* @return true キーが存在する
|
||||||
|
* @return false キーが存在しない
|
||||||
|
*/
|
||||||
|
protected isExistTextureFiles(): boolean {
|
||||||
|
const node: Value = this._jsonValue[FrequestNode.FrequestNode_Textures];
|
||||||
|
return !node.isNull() && !node.isError();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 当たり判定のキーが存在するかどうかを確認する
|
||||||
|
* @return true キーが存在する
|
||||||
|
* @return false キーが存在しない
|
||||||
|
*/
|
||||||
|
protected isExistHitAreas(): boolean {
|
||||||
|
const node: Value = this._jsonValue[FrequestNode.FrequestNode_HitAreas];
|
||||||
|
return !node.isNull() && !node.isError();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 物理演算ファイルのキーが存在するかどうかを確認する
|
||||||
|
* @return true キーが存在する
|
||||||
|
* @return false キーが存在しない
|
||||||
|
*/
|
||||||
|
protected isExistPhysicsFile(): boolean {
|
||||||
|
const node: Value = this._jsonValue[FrequestNode.FrequestNode_Physics];
|
||||||
|
return !node.isNull() && !node.isError();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ポーズ設定ファイルのキーが存在するかどうかを確認する
|
||||||
|
* @return true キーが存在する
|
||||||
|
* @return false キーが存在しない
|
||||||
|
*/
|
||||||
|
protected isExistPoseFile(): boolean {
|
||||||
|
const node: Value = this._jsonValue[FrequestNode.FrequestNode_Pose];
|
||||||
|
return !node.isNull() && !node.isError();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 表情設定ファイルのキーが存在するかどうかを確認する
|
||||||
|
* @return true キーが存在する
|
||||||
|
* @return false キーが存在しない
|
||||||
|
*/
|
||||||
|
protected isExistExpressionFile(): boolean {
|
||||||
|
const node: Value = this._jsonValue[FrequestNode.FrequestNode_Expressions];
|
||||||
|
return !node.isNull() && !node.isError();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* モーショングループのキーが存在するかどうかを確認する
|
||||||
|
* @return true キーが存在する
|
||||||
|
* @return false キーが存在しない
|
||||||
|
*/
|
||||||
|
protected isExistMotionGroups(): boolean {
|
||||||
|
const node: Value = this._jsonValue[FrequestNode.FrequestNode_Motions];
|
||||||
|
return !node.isNull() && !node.isError();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 引数で指定したモーショングループのキーが存在するかどうかを確認する
|
||||||
|
* @param groupName グループ名
|
||||||
|
* @return true キーが存在する
|
||||||
|
* @return false キーが存在しない
|
||||||
|
*/
|
||||||
|
protected isExistMotionGroupName(groupName: string): boolean {
|
||||||
|
const node: Value =
|
||||||
|
this._jsonValue[FrequestNode.FrequestNode_Motions].getValueByString(
|
||||||
|
groupName
|
||||||
|
);
|
||||||
|
return !node.isNull() && !node.isError();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 引数で指定したモーションに対応するサウンドファイルのキーが存在するかどうかを確認する
|
||||||
|
* @param groupName グループ名
|
||||||
|
* @param index 配列のインデックス値
|
||||||
|
* @return true キーが存在する
|
||||||
|
* @return false キーが存在しない
|
||||||
|
*/
|
||||||
|
protected isExistMotionSoundFile(groupName: string, index: number): boolean {
|
||||||
|
const node: Value = this._jsonValue[FrequestNode.FrequestNode_Motions]
|
||||||
|
.getValueByString(groupName)
|
||||||
|
.getValueByIndex(index)
|
||||||
|
.getValueByString(this.soundPath);
|
||||||
|
return !node.isNull() && !node.isError();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 引数で指定したモーションに対応するフェードイン時間のキーが存在するかどうかを確認する
|
||||||
|
* @param groupName グループ名
|
||||||
|
* @param index 配列のインデックス値
|
||||||
|
* @return true キーが存在する
|
||||||
|
* @return false キーが存在しない
|
||||||
|
*/
|
||||||
|
protected isExistMotionFadeIn(groupName: string, index: number): boolean {
|
||||||
|
const node: Value = this._jsonValue[FrequestNode.FrequestNode_Motions]
|
||||||
|
.getValueByString(groupName)
|
||||||
|
.getValueByIndex(index)
|
||||||
|
.getValueByString(this.fadeInTime);
|
||||||
|
return !node.isNull() && !node.isError();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 引数で指定したモーションに対応するフェードアウト時間のキーが存在するかどうかを確認する
|
||||||
|
* @param groupName グループ名
|
||||||
|
* @param index 配列のインデックス値
|
||||||
|
* @return true キーが存在する
|
||||||
|
* @return false キーが存在しない
|
||||||
|
*/
|
||||||
|
protected isExistMotionFadeOut(groupName: string, index: number): boolean {
|
||||||
|
const node: Value = this._jsonValue[FrequestNode.FrequestNode_Motions]
|
||||||
|
.getValueByString(groupName)
|
||||||
|
.getValueByIndex(index)
|
||||||
|
.getValueByString(this.fadeOutTime);
|
||||||
|
return !node.isNull() && !node.isError();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* UserDataのファイル名が存在するかどうかを確認する
|
||||||
|
* @return true キーが存在する
|
||||||
|
* @return false キーが存在しない
|
||||||
|
*/
|
||||||
|
protected isExistUserDataFile(): boolean {
|
||||||
|
const node: Value = this.getJson()
|
||||||
|
.getRoot()
|
||||||
|
.getValueByString(this.fileReferences)
|
||||||
|
.getValueByString(this.userData);
|
||||||
|
return !node.isNull() && !node.isError();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 目ぱちに対応付けられたパラメータが存在するかどうかを確認する
|
||||||
|
* @return true キーが存在する
|
||||||
|
* @return false キーが存在しない
|
||||||
|
*/
|
||||||
|
protected isExistEyeBlinkParameters(): boolean {
|
||||||
|
if (
|
||||||
|
this._jsonValue[FrequestNode.FrequestNode_Groups].isNull() ||
|
||||||
|
this._jsonValue[FrequestNode.FrequestNode_Groups].isError()
|
||||||
|
) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (
|
||||||
|
let i = 0;
|
||||||
|
i < this._jsonValue[FrequestNode.FrequestNode_Groups].getSize();
|
||||||
|
++i
|
||||||
|
) {
|
||||||
|
if (
|
||||||
|
this._jsonValue[FrequestNode.FrequestNode_Groups]
|
||||||
|
.getValueByIndex(i)
|
||||||
|
.getValueByString(this.name)
|
||||||
|
.getRawString() == this.eyeBlink
|
||||||
|
) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* リップシンクに対応付けられたパラメータが存在するかどうかを確認する
|
||||||
|
* @return true キーが存在する
|
||||||
|
* @return false キーが存在しない
|
||||||
|
*/
|
||||||
|
protected isExistLipSyncParameters(): boolean {
|
||||||
|
if (
|
||||||
|
this._jsonValue[FrequestNode.FrequestNode_Groups].isNull() ||
|
||||||
|
this._jsonValue[FrequestNode.FrequestNode_Groups].isError()
|
||||||
|
) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
for (
|
||||||
|
let i = 0;
|
||||||
|
i < this._jsonValue[FrequestNode.FrequestNode_Groups].getSize();
|
||||||
|
++i
|
||||||
|
) {
|
||||||
|
if (
|
||||||
|
this._jsonValue[FrequestNode.FrequestNode_Groups]
|
||||||
|
.getValueByIndex(i)
|
||||||
|
.getValueByString(this.name)
|
||||||
|
.getRawString() == this.lipSync
|
||||||
|
) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected _json: CubismJson;
|
||||||
|
protected _jsonValue: Array<Value>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Model3Jsonのキー文字列
|
||||||
|
*/
|
||||||
|
protected readonly version = 'Version';
|
||||||
|
protected readonly fileReferences = 'FileReferences';
|
||||||
|
|
||||||
|
protected readonly groups = 'Groups';
|
||||||
|
protected readonly layout = 'Layout';
|
||||||
|
protected readonly hitAreas = 'HitAreas';
|
||||||
|
|
||||||
|
protected readonly moc = 'Moc';
|
||||||
|
protected readonly textures = 'Textures';
|
||||||
|
protected readonly physics = 'Physics';
|
||||||
|
protected readonly pose = 'Pose';
|
||||||
|
protected readonly expressions = 'Expressions';
|
||||||
|
protected readonly motions = 'Motions';
|
||||||
|
|
||||||
|
protected readonly userData = 'UserData';
|
||||||
|
protected readonly name = 'Name';
|
||||||
|
protected readonly filePath = 'File';
|
||||||
|
protected readonly id = 'Id';
|
||||||
|
protected readonly ids = 'Ids';
|
||||||
|
protected readonly target = 'Target';
|
||||||
|
|
||||||
|
// Motions
|
||||||
|
protected readonly idle = 'Idle';
|
||||||
|
protected readonly tapBody = 'TapBody';
|
||||||
|
protected readonly pinchIn = 'PinchIn';
|
||||||
|
protected readonly pinchOut = 'PinchOut';
|
||||||
|
protected readonly shake = 'Shake';
|
||||||
|
protected readonly flickHead = 'FlickHead';
|
||||||
|
protected readonly parameter = 'Parameter';
|
||||||
|
|
||||||
|
protected readonly soundPath = 'Sound';
|
||||||
|
protected readonly fadeInTime = 'FadeInTime';
|
||||||
|
protected readonly fadeOutTime = 'FadeOutTime';
|
||||||
|
|
||||||
|
// Layout
|
||||||
|
protected readonly centerX = 'CenterX';
|
||||||
|
protected readonly centerY = 'CenterY';
|
||||||
|
protected readonly x = 'X';
|
||||||
|
protected readonly y = 'Y';
|
||||||
|
protected readonly width = 'Width';
|
||||||
|
protected readonly height = 'Height';
|
||||||
|
|
||||||
|
protected readonly lipSync = 'LipSync';
|
||||||
|
protected readonly eyeBlink = 'EyeBlink';
|
||||||
|
|
||||||
|
protected readonly initParameter = 'init_param';
|
||||||
|
protected readonly initPartsVisible = 'init_parts_visible';
|
||||||
|
protected readonly val = 'val';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Namespace definition for compatibility.
|
||||||
|
import * as $ from './cubismmodelsettingjson';
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-namespace
|
||||||
|
export namespace Live2DCubismFramework {
|
||||||
|
export const CubismModelSettingJson = $.CubismModelSettingJson;
|
||||||
|
export type CubismModelSettingJson = $.CubismModelSettingJson;
|
||||||
|
export const FrequestNode = $.FrequestNode;
|
||||||
|
export type FrequestNode = $.FrequestNode;
|
||||||
|
}
|
||||||
123
avatar-h5-renderer/framework/src/effect/cubismbreath.ts
Normal file
123
avatar-h5-renderer/framework/src/effect/cubismbreath.ts
Normal file
@ -0,0 +1,123 @@
|
|||||||
|
/**
|
||||||
|
* Copyright(c) Live2D Inc. All rights reserved.
|
||||||
|
*
|
||||||
|
* Use of this source code is governed by the Live2D Open Software license
|
||||||
|
* that can be found at https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { CubismIdHandle } from '../id/cubismid';
|
||||||
|
import { CubismModel } from '../model/cubismmodel';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 呼吸機能
|
||||||
|
*
|
||||||
|
* 呼吸機能を提供する。
|
||||||
|
*/
|
||||||
|
export class CubismBreath {
|
||||||
|
/**
|
||||||
|
* インスタンスの作成
|
||||||
|
*/
|
||||||
|
public static create(): CubismBreath {
|
||||||
|
return new CubismBreath();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* インスタンスの破棄
|
||||||
|
* @param instance 対象のCubismBreath
|
||||||
|
*/
|
||||||
|
public static delete(instance: CubismBreath): void {
|
||||||
|
if (instance != null) {
|
||||||
|
instance = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 呼吸のパラメータの紐づけ
|
||||||
|
* @param breathParameters 呼吸を紐づけたいパラメータのリスト
|
||||||
|
*/
|
||||||
|
public setParameters(breathParameters: Array<BreathParameterData>): void {
|
||||||
|
this._breathParameters = breathParameters;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 呼吸に紐づいているパラメータの取得
|
||||||
|
* @return 呼吸に紐づいているパラメータのリスト
|
||||||
|
*/
|
||||||
|
public getParameters(): Array<BreathParameterData> {
|
||||||
|
return this._breathParameters;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* モデルのパラメータの更新
|
||||||
|
* @param model 対象のモデル
|
||||||
|
* @param deltaTimeSeconds デルタ時間[秒]
|
||||||
|
*/
|
||||||
|
public updateParameters(model: CubismModel, deltaTimeSeconds: number): void {
|
||||||
|
this._currentTime += deltaTimeSeconds;
|
||||||
|
|
||||||
|
const t: number = this._currentTime * 2.0 * Math.PI;
|
||||||
|
|
||||||
|
for (let i = 0; i < this._breathParameters.length; ++i) {
|
||||||
|
const data: BreathParameterData = this._breathParameters[i];
|
||||||
|
|
||||||
|
model.addParameterValueById(
|
||||||
|
data.parameterId,
|
||||||
|
data.offset + data.peak * Math.sin(t / data.cycle),
|
||||||
|
data.weight
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* コンストラクタ
|
||||||
|
*/
|
||||||
|
public constructor() {
|
||||||
|
this._currentTime = 0.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
_breathParameters: Array<BreathParameterData>; // 呼吸にひもづいているパラメータのリスト
|
||||||
|
_currentTime: number; // 積算時間[秒]
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 呼吸のパラメータ情報
|
||||||
|
*/
|
||||||
|
export class BreathParameterData {
|
||||||
|
/**
|
||||||
|
* コンストラクタ
|
||||||
|
* @param parameterId 呼吸をひもづけるパラメータID
|
||||||
|
* @param offset 呼吸を正弦波としたときの、波のオフセット
|
||||||
|
* @param peak 呼吸を正弦波としたときの、波の高さ
|
||||||
|
* @param cycle 呼吸を正弦波としたときの、波の周期
|
||||||
|
* @param weight パラメータへの重み
|
||||||
|
*/
|
||||||
|
constructor(
|
||||||
|
parameterId?: CubismIdHandle,
|
||||||
|
offset?: number,
|
||||||
|
peak?: number,
|
||||||
|
cycle?: number,
|
||||||
|
weight?: number
|
||||||
|
) {
|
||||||
|
this.parameterId = parameterId == undefined ? null : parameterId;
|
||||||
|
this.offset = offset == undefined ? 0.0 : offset;
|
||||||
|
this.peak = peak == undefined ? 0.0 : peak;
|
||||||
|
this.cycle = cycle == undefined ? 0.0 : cycle;
|
||||||
|
this.weight = weight == undefined ? 0.0 : weight;
|
||||||
|
}
|
||||||
|
|
||||||
|
parameterId: CubismIdHandle; // 呼吸をひもづけるパラメータID\
|
||||||
|
offset: number; // 呼吸を正弦波としたときの、波のオフセット
|
||||||
|
peak: number; // 呼吸を正弦波としたときの、波の高さ
|
||||||
|
cycle: number; // 呼吸を正弦波としたときの、波の周期
|
||||||
|
weight: number; // パラメータへの重み
|
||||||
|
}
|
||||||
|
|
||||||
|
// Namespace definition for compatibility.
|
||||||
|
import * as $ from './cubismbreath';
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-namespace
|
||||||
|
export namespace Live2DCubismFramework {
|
||||||
|
export const BreathParameterData = $.BreathParameterData;
|
||||||
|
export type BreathParameterData = $.BreathParameterData;
|
||||||
|
export const CubismBreath = $.CubismBreath;
|
||||||
|
export type CubismBreath = $.CubismBreath;
|
||||||
|
}
|
||||||
234
avatar-h5-renderer/framework/src/effect/cubismeyeblink.ts
Normal file
234
avatar-h5-renderer/framework/src/effect/cubismeyeblink.ts
Normal file
@ -0,0 +1,234 @@
|
|||||||
|
/**
|
||||||
|
* Copyright(c) Live2D Inc. All rights reserved.
|
||||||
|
*
|
||||||
|
* Use of this source code is governed by the Live2D Open Software license
|
||||||
|
* that can be found at https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { ICubismModelSetting } from '../icubismmodelsetting';
|
||||||
|
import { CubismIdHandle } from '../id/cubismid';
|
||||||
|
import { CubismModel } from '../model/cubismmodel';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 自動まばたき機能
|
||||||
|
*
|
||||||
|
* 自動まばたき機能を提供する。
|
||||||
|
*/
|
||||||
|
export class CubismEyeBlink {
|
||||||
|
/**
|
||||||
|
* インスタンスを作成する
|
||||||
|
* @param modelSetting モデルの設定情報
|
||||||
|
* @return 作成されたインスタンス
|
||||||
|
* @note 引数がNULLの場合、パラメータIDが設定されていない空のインスタンスを作成する。
|
||||||
|
*/
|
||||||
|
public static create(
|
||||||
|
modelSetting: ICubismModelSetting = null
|
||||||
|
): CubismEyeBlink {
|
||||||
|
return new CubismEyeBlink(modelSetting);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* インスタンスの破棄
|
||||||
|
* @param eyeBlink 対象のCubismEyeBlink
|
||||||
|
*/
|
||||||
|
public static delete(eyeBlink: CubismEyeBlink): void {
|
||||||
|
if (eyeBlink != null) {
|
||||||
|
eyeBlink = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* まばたきの間隔の設定
|
||||||
|
* @param blinkingInterval まばたきの間隔の時間[秒]
|
||||||
|
*/
|
||||||
|
public setBlinkingInterval(blinkingInterval: number): void {
|
||||||
|
this._blinkingIntervalSeconds = blinkingInterval;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* まばたきのモーションの詳細設定
|
||||||
|
* @param closing まぶたを閉じる動作の所要時間[秒]
|
||||||
|
* @param closed まぶたを閉じている動作の所要時間[秒]
|
||||||
|
* @param opening まぶたを開く動作の所要時間[秒]
|
||||||
|
*/
|
||||||
|
public setBlinkingSetting(
|
||||||
|
closing: number,
|
||||||
|
closed: number,
|
||||||
|
opening: number
|
||||||
|
): void {
|
||||||
|
this._closingSeconds = closing;
|
||||||
|
this._closedSeconds = closed;
|
||||||
|
this._openingSeconds = opening;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* まばたきさせるパラメータIDのリストの設定
|
||||||
|
* @param parameterIds パラメータのIDのリスト
|
||||||
|
*/
|
||||||
|
public setParameterIds(parameterIds: Array<CubismIdHandle>): void {
|
||||||
|
this._parameterIds = parameterIds;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* まばたきさせるパラメータIDのリストの取得
|
||||||
|
* @return パラメータIDのリスト
|
||||||
|
*/
|
||||||
|
public getParameterIds(): Array<CubismIdHandle> {
|
||||||
|
return this._parameterIds;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* モデルのパラメータの更新
|
||||||
|
* @param model 対象のモデル
|
||||||
|
* @param deltaTimeSeconds デルタ時間[秒]
|
||||||
|
*/
|
||||||
|
public updateParameters(model: CubismModel, deltaTimeSeconds: number): void {
|
||||||
|
this._userTimeSeconds += deltaTimeSeconds;
|
||||||
|
let parameterValue: number;
|
||||||
|
let t = 0.0;
|
||||||
|
const blinkingState: EyeState = this._blinkingState;
|
||||||
|
|
||||||
|
switch (blinkingState) {
|
||||||
|
case EyeState.EyeState_Closing:
|
||||||
|
t =
|
||||||
|
(this._userTimeSeconds - this._stateStartTimeSeconds) /
|
||||||
|
this._closingSeconds;
|
||||||
|
|
||||||
|
if (t >= 1.0) {
|
||||||
|
t = 1.0;
|
||||||
|
this._blinkingState = EyeState.EyeState_Closed;
|
||||||
|
this._stateStartTimeSeconds = this._userTimeSeconds;
|
||||||
|
}
|
||||||
|
|
||||||
|
parameterValue = 1.0 - t;
|
||||||
|
|
||||||
|
break;
|
||||||
|
case EyeState.EyeState_Closed:
|
||||||
|
t =
|
||||||
|
(this._userTimeSeconds - this._stateStartTimeSeconds) /
|
||||||
|
this._closedSeconds;
|
||||||
|
|
||||||
|
if (t >= 1.0) {
|
||||||
|
this._blinkingState = EyeState.EyeState_Opening;
|
||||||
|
this._stateStartTimeSeconds = this._userTimeSeconds;
|
||||||
|
}
|
||||||
|
|
||||||
|
parameterValue = 0.0;
|
||||||
|
|
||||||
|
break;
|
||||||
|
case EyeState.EyeState_Opening:
|
||||||
|
t =
|
||||||
|
(this._userTimeSeconds - this._stateStartTimeSeconds) /
|
||||||
|
this._openingSeconds;
|
||||||
|
|
||||||
|
if (t >= 1.0) {
|
||||||
|
t = 1.0;
|
||||||
|
this._blinkingState = EyeState.EyeState_Interval;
|
||||||
|
this._nextBlinkingTime = this.determinNextBlinkingTiming();
|
||||||
|
}
|
||||||
|
|
||||||
|
parameterValue = t;
|
||||||
|
|
||||||
|
break;
|
||||||
|
case EyeState.EyeState_Interval:
|
||||||
|
if (this._nextBlinkingTime < this._userTimeSeconds) {
|
||||||
|
this._blinkingState = EyeState.EyeState_Closing;
|
||||||
|
this._stateStartTimeSeconds = this._userTimeSeconds;
|
||||||
|
}
|
||||||
|
|
||||||
|
parameterValue = 1.0;
|
||||||
|
|
||||||
|
break;
|
||||||
|
case EyeState.EyeState_First:
|
||||||
|
default:
|
||||||
|
this._blinkingState = EyeState.EyeState_Interval;
|
||||||
|
this._nextBlinkingTime = this.determinNextBlinkingTiming();
|
||||||
|
|
||||||
|
parameterValue = 1.0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!CubismEyeBlink.CloseIfZero) {
|
||||||
|
parameterValue = -parameterValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let i = 0; i < this._parameterIds.length; ++i) {
|
||||||
|
model.setParameterValueById(this._parameterIds[i], parameterValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* コンストラクタ
|
||||||
|
* @param modelSetting モデルの設定情報
|
||||||
|
*/
|
||||||
|
public constructor(modelSetting: ICubismModelSetting) {
|
||||||
|
this._blinkingState = EyeState.EyeState_First;
|
||||||
|
this._nextBlinkingTime = 0.0;
|
||||||
|
this._stateStartTimeSeconds = 0.0;
|
||||||
|
this._blinkingIntervalSeconds = 4.0;
|
||||||
|
this._closingSeconds = 0.1;
|
||||||
|
this._closedSeconds = 0.05;
|
||||||
|
this._openingSeconds = 0.15;
|
||||||
|
this._userTimeSeconds = 0.0;
|
||||||
|
this._parameterIds = new Array<CubismIdHandle>();
|
||||||
|
|
||||||
|
if (modelSetting == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._parameterIds.length = modelSetting.getEyeBlinkParameterCount();
|
||||||
|
for (let i = 0; i < modelSetting.getEyeBlinkParameterCount(); ++i) {
|
||||||
|
this._parameterIds[i] = modelSetting.getEyeBlinkParameterId(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 次の瞬きのタイミングの決定
|
||||||
|
*
|
||||||
|
* @return 次のまばたきを行う時刻[秒]
|
||||||
|
*/
|
||||||
|
public determinNextBlinkingTiming(): number {
|
||||||
|
const r: number = Math.random();
|
||||||
|
return (
|
||||||
|
this._userTimeSeconds + r * (2.0 * this._blinkingIntervalSeconds - 1.0)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
_blinkingState: number; // 現在の状態
|
||||||
|
_parameterIds: Array<CubismIdHandle>; // 操作対象のパラメータのIDのリスト
|
||||||
|
_nextBlinkingTime: number; // 次のまばたきの時刻[秒]
|
||||||
|
_stateStartTimeSeconds: number; // 現在の状態が開始した時刻[秒]
|
||||||
|
_blinkingIntervalSeconds: number; // まばたきの間隔[秒]
|
||||||
|
_closingSeconds: number; // まぶたを閉じる動作の所要時間[秒]
|
||||||
|
_closedSeconds: number; // まぶたを閉じている動作の所要時間[秒]
|
||||||
|
_openingSeconds: number; // まぶたを開く動作の所要時間[秒]
|
||||||
|
_userTimeSeconds: number; // デルタ時間の積算値[秒]
|
||||||
|
|
||||||
|
/**
|
||||||
|
* IDで指定された目のパラメータが、0のときに閉じるなら true 、1の時に閉じるなら false 。
|
||||||
|
*/
|
||||||
|
static readonly CloseIfZero: boolean = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* まばたきの状態
|
||||||
|
*
|
||||||
|
* まばたきの状態を表す列挙型
|
||||||
|
*/
|
||||||
|
export enum EyeState {
|
||||||
|
EyeState_First = 0, // 初期状態
|
||||||
|
EyeState_Interval, // まばたきしていない状態
|
||||||
|
EyeState_Closing, // まぶたが閉じていく途中の状態
|
||||||
|
EyeState_Closed, // まぶたが閉じている状態
|
||||||
|
EyeState_Opening // まぶたが開いていく途中の状態
|
||||||
|
}
|
||||||
|
|
||||||
|
// Namespace definition for compatibility.
|
||||||
|
import * as $ from './cubismeyeblink';
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-namespace
|
||||||
|
export namespace Live2DCubismFramework {
|
||||||
|
export const CubismEyeBlink = $.CubismEyeBlink;
|
||||||
|
export type CubismEyeBlink = $.CubismEyeBlink;
|
||||||
|
export const EyeState = $.EyeState;
|
||||||
|
export type EyeState = $.EyeState;
|
||||||
|
}
|
||||||
120
avatar-h5-renderer/framework/src/effect/cubismlook.ts
Normal file
120
avatar-h5-renderer/framework/src/effect/cubismlook.ts
Normal file
@ -0,0 +1,120 @@
|
|||||||
|
/**
|
||||||
|
* Copyright(c) Live2D Inc. All rights reserved.
|
||||||
|
*
|
||||||
|
* Use of this source code is governed by the Live2D Open Software license
|
||||||
|
* that can be found at https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { CubismIdHandle } from '../id/cubismid';
|
||||||
|
import { CubismModel } from '../model/cubismmodel';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ターゲットによるパラメータ追従機能
|
||||||
|
*
|
||||||
|
* ドラッグ入力に対するパラメータ追従機能を提供する。
|
||||||
|
*/
|
||||||
|
export class CubismLook {
|
||||||
|
/**
|
||||||
|
* インスタンスの作成
|
||||||
|
*/
|
||||||
|
public static create(): CubismLook {
|
||||||
|
return new CubismLook();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* インスタンスの破棄
|
||||||
|
* @param instance 対象のCubismDrag
|
||||||
|
*/
|
||||||
|
public static delete(instance: CubismLook): void {
|
||||||
|
if (instance != null) {
|
||||||
|
instance = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ターゲット追従のパラメータの紐づけ
|
||||||
|
* @param lookParameters ターゲット追従を紐づけたいパラメータのリスト
|
||||||
|
*/
|
||||||
|
public setParameters(lookParameters: Array<LookParameterData>): void {
|
||||||
|
this._lookParameters = lookParameters;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ターゲット追従に紐づいているパラメータの取得
|
||||||
|
* @return ターゲット追従に紐づいているパラメータのリスト
|
||||||
|
*/
|
||||||
|
public getParameters(): Array<LookParameterData> {
|
||||||
|
return this._lookParameters;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* モデルのパラメータの更新
|
||||||
|
* @param model 対象のモデル
|
||||||
|
* @param dragX ターゲットのX座標
|
||||||
|
* @param dragY ターゲットのY座標
|
||||||
|
*/
|
||||||
|
public updateParameters(
|
||||||
|
model: CubismModel,
|
||||||
|
dragX: number,
|
||||||
|
dragY: number
|
||||||
|
): void {
|
||||||
|
for (let i = 0; i < this._lookParameters.length; ++i) {
|
||||||
|
const data: LookParameterData = this._lookParameters[i];
|
||||||
|
|
||||||
|
model.addParameterValueById(
|
||||||
|
data.parameterId,
|
||||||
|
data.factorX * dragX +
|
||||||
|
data.factorY * dragY +
|
||||||
|
data.factorXY * dragX * dragY
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* コンストラクタ
|
||||||
|
*/
|
||||||
|
public constructor() {
|
||||||
|
this._lookParameters = new Array<LookParameterData>();
|
||||||
|
}
|
||||||
|
|
||||||
|
_lookParameters: Array<LookParameterData>; // ターゲット追従に紐づいているパラメータのリスト
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ターゲット追従のパラメータ情報
|
||||||
|
*/
|
||||||
|
export class LookParameterData {
|
||||||
|
/**
|
||||||
|
* コンストラクタ
|
||||||
|
* @param parameterId ターゲット追従を紐づけるパラメータID
|
||||||
|
* @param factorX X方向ドラッグ入力に対する係数
|
||||||
|
* @param factorY Y方向ドラッグ入力に対する係数
|
||||||
|
* @param factorXY XY積ドラッグ入力に対する係数
|
||||||
|
*/
|
||||||
|
constructor(
|
||||||
|
parameterId?: CubismIdHandle,
|
||||||
|
factorX?: number,
|
||||||
|
factorY?: number,
|
||||||
|
factorXY?: number
|
||||||
|
) {
|
||||||
|
this.parameterId = parameterId == undefined ? null : parameterId;
|
||||||
|
this.factorX = factorX == undefined ? 0.0 : factorX;
|
||||||
|
this.factorY = factorY == undefined ? 0.0 : factorY;
|
||||||
|
this.factorXY = factorXY == undefined ? 0.0 : factorXY;
|
||||||
|
}
|
||||||
|
|
||||||
|
parameterId: CubismIdHandle; // ターゲット追従を紐づけるパラメータID
|
||||||
|
factorX: number; // X方向ドラッグ入力に対する係数
|
||||||
|
factorY: number; // Y方向ドラッグ入力に対する係数
|
||||||
|
factorXY: number; // XY積ドラッグ入力に対する係数
|
||||||
|
}
|
||||||
|
|
||||||
|
// Namespace definition for compatibility.
|
||||||
|
import * as $ from './cubismlook';
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-namespace
|
||||||
|
export namespace Live2DCubismFramework {
|
||||||
|
export const LookParameterData = $.LookParameterData;
|
||||||
|
export type LookParameterData = $.LookParameterData;
|
||||||
|
export const CubismLook = $.CubismLook;
|
||||||
|
export type CubismLook = $.CubismLook;
|
||||||
|
}
|
||||||
396
avatar-h5-renderer/framework/src/effect/cubismpose.ts
Normal file
396
avatar-h5-renderer/framework/src/effect/cubismpose.ts
Normal file
@ -0,0 +1,396 @@
|
|||||||
|
/**
|
||||||
|
* Copyright(c) Live2D Inc. All rights reserved.
|
||||||
|
*
|
||||||
|
* Use of this source code is governed by the Live2D Open Software license
|
||||||
|
* that can be found at https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { CubismIdHandle } from '../id/cubismid';
|
||||||
|
import { CubismFramework } from '../live2dcubismframework';
|
||||||
|
import { CubismModel } from '../model/cubismmodel';
|
||||||
|
import { CubismJson, Value } from '../utils/cubismjson';
|
||||||
|
|
||||||
|
const Epsilon = 0.001;
|
||||||
|
const DefaultFadeInSeconds = 0.5;
|
||||||
|
|
||||||
|
// Pose.jsonのタグ
|
||||||
|
const FadeIn = 'FadeInTime';
|
||||||
|
const Link = 'Link';
|
||||||
|
const Groups = 'Groups';
|
||||||
|
const Id = 'Id';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* パーツの不透明度の設定
|
||||||
|
*
|
||||||
|
* パーツの不透明度の管理と設定を行う。
|
||||||
|
*/
|
||||||
|
export class CubismPose {
|
||||||
|
/**
|
||||||
|
* インスタンスの作成
|
||||||
|
* @param pose3json pose3.jsonのデータ
|
||||||
|
* @param size pose3.jsonのデータのサイズ[byte]
|
||||||
|
* @return 作成されたインスタンス
|
||||||
|
*/
|
||||||
|
public static create(pose3json: ArrayBuffer, size: number): CubismPose {
|
||||||
|
const json: CubismJson = CubismJson.create(pose3json, size);
|
||||||
|
if (!json) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ret: CubismPose = new CubismPose();
|
||||||
|
const root: Value = json.getRoot();
|
||||||
|
|
||||||
|
// フェード時間の指定
|
||||||
|
if (!root.getValueByString(FadeIn).isNull()) {
|
||||||
|
ret._fadeTimeSeconds = root
|
||||||
|
.getValueByString(FadeIn)
|
||||||
|
.toFloat(DefaultFadeInSeconds);
|
||||||
|
|
||||||
|
if (ret._fadeTimeSeconds < 0.0) {
|
||||||
|
ret._fadeTimeSeconds = DefaultFadeInSeconds;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// パーツグループ
|
||||||
|
const poseListInfo: Value = root.getValueByString(Groups);
|
||||||
|
const poseCount: number = poseListInfo.getSize();
|
||||||
|
|
||||||
|
ret._partGroupCounts.length = poseCount;
|
||||||
|
for (let poseIndex = 0; poseIndex < poseCount; ++poseIndex) {
|
||||||
|
const idListInfo: Value = poseListInfo.getValueByIndex(poseIndex);
|
||||||
|
const idCount: number = idListInfo.getSize();
|
||||||
|
let groupCount = 0;
|
||||||
|
|
||||||
|
for (let groupIndex = 0; groupIndex < idCount; ++groupIndex) {
|
||||||
|
const partInfo: Value = idListInfo.getValueByIndex(groupIndex);
|
||||||
|
const partData: PartData = new PartData();
|
||||||
|
const parameterId: CubismIdHandle =
|
||||||
|
CubismFramework.getIdManager().getId(
|
||||||
|
partInfo.getValueByString(Id).getRawString()
|
||||||
|
);
|
||||||
|
|
||||||
|
partData.partId = parameterId;
|
||||||
|
|
||||||
|
// リンクするパーツの設定
|
||||||
|
if (!partInfo.getValueByString(Link).isNull()) {
|
||||||
|
const linkListInfo: Value = partInfo.getValueByString(Link);
|
||||||
|
const linkCount: number = linkListInfo.getSize();
|
||||||
|
|
||||||
|
for (let linkIndex = 0; linkIndex < linkCount; ++linkIndex) {
|
||||||
|
const linkPart: PartData = new PartData();
|
||||||
|
const linkId: CubismIdHandle = CubismFramework.getIdManager().getId(
|
||||||
|
linkListInfo.getValueByIndex(linkIndex).getString()
|
||||||
|
);
|
||||||
|
|
||||||
|
linkPart.partId = linkId;
|
||||||
|
|
||||||
|
partData.link.push(linkPart);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ret._partGroups.push(partData.clone());
|
||||||
|
|
||||||
|
++groupCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret._partGroupCounts[poseIndex] = groupCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
CubismJson.delete(json);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* インスタンスを破棄する
|
||||||
|
* @param pose 対象のCubismPose
|
||||||
|
*/
|
||||||
|
public static delete(pose: CubismPose): void {
|
||||||
|
if (pose != null) {
|
||||||
|
pose = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* モデルのパラメータの更新
|
||||||
|
* @param model 対象のモデル
|
||||||
|
* @param deltaTimeSeconds デルタ時間[秒]
|
||||||
|
*/
|
||||||
|
public updateParameters(model: CubismModel, deltaTimeSeconds: number): void {
|
||||||
|
// 前回のモデルと同じでない場合は初期化が必要
|
||||||
|
if (model != this._lastModel) {
|
||||||
|
// パラメータインデックスの初期化
|
||||||
|
this.reset(model);
|
||||||
|
}
|
||||||
|
|
||||||
|
this._lastModel = model;
|
||||||
|
|
||||||
|
// 設定から時間を変更すると、経過時間がマイナスになる事があるので、経過時間0として対応
|
||||||
|
if (deltaTimeSeconds < 0.0) {
|
||||||
|
deltaTimeSeconds = 0.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
let beginIndex = 0;
|
||||||
|
|
||||||
|
for (let i = 0; i < this._partGroupCounts.length; i++) {
|
||||||
|
const partGroupCount: number = this._partGroupCounts[i];
|
||||||
|
|
||||||
|
this.doFade(model, deltaTimeSeconds, beginIndex, partGroupCount);
|
||||||
|
|
||||||
|
beginIndex += partGroupCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.copyPartOpacities(model);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 表示を初期化
|
||||||
|
* @param model 対象のモデル
|
||||||
|
* @note 不透明度の初期値が0でないパラメータは、不透明度を1に設定する
|
||||||
|
*/
|
||||||
|
public reset(model: CubismModel): void {
|
||||||
|
let beginIndex = 0;
|
||||||
|
|
||||||
|
for (let i = 0; i < this._partGroupCounts.length; ++i) {
|
||||||
|
const groupCount: number = this._partGroupCounts[i];
|
||||||
|
|
||||||
|
for (let j: number = beginIndex; j < beginIndex + groupCount; ++j) {
|
||||||
|
this._partGroups[j].initialize(model);
|
||||||
|
|
||||||
|
const partsIndex: number = this._partGroups[j].partIndex;
|
||||||
|
const paramIndex: number = this._partGroups[j].parameterIndex;
|
||||||
|
|
||||||
|
if (partsIndex < 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
model.setPartOpacityByIndex(partsIndex, j == beginIndex ? 1.0 : 0.0);
|
||||||
|
model.setParameterValueByIndex(paramIndex, j == beginIndex ? 1.0 : 0.0);
|
||||||
|
|
||||||
|
for (let k = 0; k < this._partGroups[j].link.length; ++k) {
|
||||||
|
this._partGroups[j].link[k].initialize(model);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
beginIndex += groupCount;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* パーツの不透明度をコピー
|
||||||
|
*
|
||||||
|
* @param model 対象のモデル
|
||||||
|
*/
|
||||||
|
public copyPartOpacities(model: CubismModel): void {
|
||||||
|
for (
|
||||||
|
let groupIndex = 0;
|
||||||
|
groupIndex < this._partGroups.length;
|
||||||
|
++groupIndex
|
||||||
|
) {
|
||||||
|
const partData: PartData = this._partGroups[groupIndex];
|
||||||
|
|
||||||
|
if (partData.link.length == 0) {
|
||||||
|
continue; // 連動するパラメータはない
|
||||||
|
}
|
||||||
|
|
||||||
|
const partIndex: number = this._partGroups[groupIndex].partIndex;
|
||||||
|
const opacity: number = model.getPartOpacityByIndex(partIndex);
|
||||||
|
|
||||||
|
for (let linkIndex = 0; linkIndex < partData.link.length; ++linkIndex) {
|
||||||
|
const linkPart: PartData = partData.link[linkIndex];
|
||||||
|
const linkPartIndex: number = linkPart.partIndex;
|
||||||
|
|
||||||
|
if (linkPartIndex < 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
model.setPartOpacityByIndex(linkPartIndex, opacity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* パーツのフェード操作を行う。
|
||||||
|
* @param model 対象のモデル
|
||||||
|
* @param deltaTimeSeconds デルタ時間[秒]
|
||||||
|
* @param beginIndex フェード操作を行うパーツグループの先頭インデックス
|
||||||
|
* @param partGroupCount フェード操作を行うパーツグループの個数
|
||||||
|
*/
|
||||||
|
public doFade(
|
||||||
|
model: CubismModel,
|
||||||
|
deltaTimeSeconds: number,
|
||||||
|
beginIndex: number,
|
||||||
|
partGroupCount: number
|
||||||
|
): void {
|
||||||
|
let visiblePartIndex = -1;
|
||||||
|
let newOpacity = 1.0;
|
||||||
|
|
||||||
|
const phi = 0.5;
|
||||||
|
const backOpacityThreshold = 0.15;
|
||||||
|
|
||||||
|
// 現在、表示状態になっているパーツを取得
|
||||||
|
for (let i: number = beginIndex; i < beginIndex + partGroupCount; ++i) {
|
||||||
|
const partIndex: number = this._partGroups[i].partIndex;
|
||||||
|
const paramIndex: number = this._partGroups[i].parameterIndex;
|
||||||
|
|
||||||
|
if (model.getParameterValueByIndex(paramIndex) > Epsilon) {
|
||||||
|
if (visiblePartIndex >= 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
visiblePartIndex = i;
|
||||||
|
// ゼロ除算の回避
|
||||||
|
if (this._fadeTimeSeconds == 0) {
|
||||||
|
newOpacity = 1.0;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
newOpacity = model.getPartOpacityByIndex(partIndex);
|
||||||
|
|
||||||
|
// 新しい不透明度を計算
|
||||||
|
newOpacity += deltaTimeSeconds / this._fadeTimeSeconds;
|
||||||
|
|
||||||
|
if (newOpacity > 1.0) {
|
||||||
|
newOpacity = 1.0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (visiblePartIndex < 0) {
|
||||||
|
visiblePartIndex = 0;
|
||||||
|
newOpacity = 1.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 表示パーツ、非表示パーツの不透明度を設定する
|
||||||
|
for (let i: number = beginIndex; i < beginIndex + partGroupCount; ++i) {
|
||||||
|
const partsIndex: number = this._partGroups[i].partIndex;
|
||||||
|
|
||||||
|
// 表示パーツの設定
|
||||||
|
if (visiblePartIndex == i) {
|
||||||
|
model.setPartOpacityByIndex(partsIndex, newOpacity); // 先に設定
|
||||||
|
}
|
||||||
|
// 非表示パーツの設定
|
||||||
|
else {
|
||||||
|
let opacity: number = model.getPartOpacityByIndex(partsIndex);
|
||||||
|
let a1: number; // 計算によって求められる不透明度
|
||||||
|
|
||||||
|
if (newOpacity < phi) {
|
||||||
|
a1 = (newOpacity * (phi - 1)) / phi + 1.0; // (0,1),(phi,phi)を通る直線式
|
||||||
|
} else {
|
||||||
|
a1 = ((1 - newOpacity) * phi) / (1.0 - phi); // (1,0),(phi,phi)を通る直線式
|
||||||
|
}
|
||||||
|
|
||||||
|
// 背景の見える割合を制限する場合
|
||||||
|
const backOpacity: number = (1.0 - a1) * (1.0 - newOpacity);
|
||||||
|
|
||||||
|
if (backOpacity > backOpacityThreshold) {
|
||||||
|
a1 = 1.0 - backOpacityThreshold / (1.0 - newOpacity);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (opacity > a1) {
|
||||||
|
opacity = a1; // 計算の不透明度よりも大きければ(濃ければ)不透明度を上げる
|
||||||
|
}
|
||||||
|
|
||||||
|
model.setPartOpacityByIndex(partsIndex, opacity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* コンストラクタ
|
||||||
|
*/
|
||||||
|
public constructor() {
|
||||||
|
this._fadeTimeSeconds = DefaultFadeInSeconds;
|
||||||
|
this._lastModel = null;
|
||||||
|
this._partGroups = new Array<PartData>();
|
||||||
|
this._partGroupCounts = new Array<number>();
|
||||||
|
}
|
||||||
|
|
||||||
|
_partGroups: Array<PartData>; // パーツグループ
|
||||||
|
_partGroupCounts: Array<number>; // それぞれのパーツグループの個数
|
||||||
|
_fadeTimeSeconds: number; // フェード時間[秒]
|
||||||
|
_lastModel: CubismModel; // 前回操作したモデル
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* パーツにまつわるデータを管理
|
||||||
|
*/
|
||||||
|
export class PartData {
|
||||||
|
/**
|
||||||
|
* コンストラクタ
|
||||||
|
*/
|
||||||
|
constructor(v?: PartData) {
|
||||||
|
this.parameterIndex = 0;
|
||||||
|
this.partIndex = 0;
|
||||||
|
this.link = new Array<PartData>();
|
||||||
|
|
||||||
|
if (v != undefined) {
|
||||||
|
this.partId = v.partId;
|
||||||
|
|
||||||
|
this.link.length = v.link.length;
|
||||||
|
for (let i = 0; i < v.link.length; i++) {
|
||||||
|
this.link[i] = v.link[i].clone();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* =演算子のオーバーロード
|
||||||
|
*/
|
||||||
|
public assignment(v: PartData): PartData {
|
||||||
|
this.partId = v.partId;
|
||||||
|
|
||||||
|
let dstIndex: number = this.link.length;
|
||||||
|
this.link.length += v.link.length;
|
||||||
|
for (const partData of v.link) {
|
||||||
|
this.link[dstIndex++] = partData.clone();
|
||||||
|
}
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 初期化
|
||||||
|
* @param model 初期化に使用するモデル
|
||||||
|
*/
|
||||||
|
public initialize(model: CubismModel): void {
|
||||||
|
this.parameterIndex = model.getParameterIndex(this.partId);
|
||||||
|
this.partIndex = model.getPartIndex(this.partId);
|
||||||
|
|
||||||
|
model.setParameterValueByIndex(this.parameterIndex, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* オブジェクトのコピーを生成する
|
||||||
|
*/
|
||||||
|
public clone(): PartData {
|
||||||
|
const clonePartData: PartData = new PartData();
|
||||||
|
|
||||||
|
clonePartData.partId = this.partId;
|
||||||
|
clonePartData.parameterIndex = this.parameterIndex;
|
||||||
|
clonePartData.partIndex = this.partIndex;
|
||||||
|
clonePartData.link = new Array<PartData>();
|
||||||
|
|
||||||
|
clonePartData.link.length = this.link.length;
|
||||||
|
for (let i = 0; i < this.link.length; i++) {
|
||||||
|
clonePartData.link[i] = this.link[i].clone();
|
||||||
|
}
|
||||||
|
|
||||||
|
return clonePartData;
|
||||||
|
}
|
||||||
|
|
||||||
|
partId: CubismIdHandle; // パーツID
|
||||||
|
parameterIndex: number; // パラメータのインデックス
|
||||||
|
partIndex: number; // パーツのインデックス
|
||||||
|
link: Array<PartData>; // 連動するパラメータ
|
||||||
|
}
|
||||||
|
|
||||||
|
// Namespace definition for compatibility.
|
||||||
|
import * as $ from './cubismpose';
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-namespace
|
||||||
|
export namespace Live2DCubismFramework {
|
||||||
|
export const CubismPose = $.CubismPose;
|
||||||
|
export type CubismPose = $.CubismPose;
|
||||||
|
export const PartData = $.PartData;
|
||||||
|
export type PartData = $.PartData;
|
||||||
|
}
|
||||||
51
avatar-h5-renderer/framework/src/icubismallcator.ts
Normal file
51
avatar-h5-renderer/framework/src/icubismallcator.ts
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
/**
|
||||||
|
* Copyright(c) Live2D Inc. All rights reserved.
|
||||||
|
*
|
||||||
|
* Use of this source code is governed by the Live2D Open Software license
|
||||||
|
* that can be found at https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* メモリアロケーションを抽象化したクラス
|
||||||
|
*
|
||||||
|
* メモリ確保・解放処理をプラットフォーム側で実装して
|
||||||
|
* フレームワークから呼び出すためのインターフェース
|
||||||
|
*/
|
||||||
|
export abstract class ICubismAllocator {
|
||||||
|
/**
|
||||||
|
* アラインメント制約なしのヒープ・メモリーを確保します
|
||||||
|
*
|
||||||
|
* @param size 確保するバイト数
|
||||||
|
* @return 成功すると割り当てられたメモリのアドレス。そうでなければ'0'を返す
|
||||||
|
*/
|
||||||
|
public abstract allocate(size: number): any;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* アラインメント制約なしのヒープ・メモリーを解放します。
|
||||||
|
*
|
||||||
|
* @param memory 解放するメモリのアドレス
|
||||||
|
*/
|
||||||
|
public abstract deallocate(memory: any): void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* アラインメント制約有のヒープ・メモリーを確保します。
|
||||||
|
* @param size 確保するバイト数
|
||||||
|
* @param alignment メモリーブロックのアラインメント幅
|
||||||
|
* @return 成功すると割り当てられたメモリのアドレス。そうでなければ'0'を返す
|
||||||
|
*/
|
||||||
|
public abstract allocateAligned(size: number, alignment: number): any;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* アラインメント制約ありのヒープ・メモリーを解放します。
|
||||||
|
* @param alignedMemory 解放するメモリのアドレス
|
||||||
|
*/
|
||||||
|
public abstract deallocateAligned(alignedMemory: any): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Namespace definition for compatibility.
|
||||||
|
import * as $ from './icubismallcator';
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-namespace
|
||||||
|
export namespace Live2DCubismFramework {
|
||||||
|
export const ICubismAllocator = $.ICubismAllocator;
|
||||||
|
export type ICubismAllocator = $.ICubismAllocator;
|
||||||
|
}
|
||||||
202
avatar-h5-renderer/framework/src/icubismmodelsetting.ts
Normal file
202
avatar-h5-renderer/framework/src/icubismmodelsetting.ts
Normal file
@ -0,0 +1,202 @@
|
|||||||
|
/**
|
||||||
|
* Copyright(c) Live2D Inc. All rights reserved.
|
||||||
|
*
|
||||||
|
* Use of this source code is governed by the Live2D Open Software license
|
||||||
|
* that can be found at https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { CubismIdHandle } from './id/cubismid';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* モデル設定情報を取り扱う関数を宣言した純粋仮想クラス。
|
||||||
|
*
|
||||||
|
* このクラスを継承することで、モデル設定情報を取り扱うクラスになる。
|
||||||
|
*/
|
||||||
|
export abstract class ICubismModelSetting {
|
||||||
|
/**
|
||||||
|
* Mocファイルの名前を取得する
|
||||||
|
* @return Mocファイルの名前
|
||||||
|
*/
|
||||||
|
public abstract getModelFileName(): string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* モデルが使用するテクスチャの数を取得する
|
||||||
|
* テクスチャの数
|
||||||
|
*/
|
||||||
|
public abstract getTextureCount(): number;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* テクスチャが配置されたディレクトリの名前を取得する
|
||||||
|
* @return テクスチャが配置されたディレクトリの名前
|
||||||
|
*/
|
||||||
|
public abstract getTextureDirectory(): string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* モデルが使用するテクスチャの名前を取得する
|
||||||
|
* @param index 配列のインデックス値
|
||||||
|
* @return テクスチャの名前
|
||||||
|
*/
|
||||||
|
public abstract getTextureFileName(index: number): string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* モデルに設定された当たり判定の数を取得する
|
||||||
|
* @return モデルに設定された当たり判定の数
|
||||||
|
*/
|
||||||
|
public abstract getHitAreasCount(): number;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 当たり判定に設定されたIDを取得する
|
||||||
|
*
|
||||||
|
* @param index 配列のindex
|
||||||
|
* @return 当たり判定に設定されたID
|
||||||
|
*/
|
||||||
|
public abstract getHitAreaId(index: number): CubismIdHandle;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 当たり判定に設定された名前を取得する
|
||||||
|
* @param index 配列のインデックス値
|
||||||
|
* @return 当たり判定に設定された名前
|
||||||
|
*/
|
||||||
|
public abstract getHitAreaName(index: number): string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 物理演算設定ファイルの名前を取得する
|
||||||
|
* @return 物理演算設定ファイルの名前
|
||||||
|
*/
|
||||||
|
public abstract getPhysicsFileName(): string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* パーツ切り替え設定ファイルの名前を取得する
|
||||||
|
* @return パーツ切り替え設定ファイルの名前
|
||||||
|
*/
|
||||||
|
public abstract getPoseFileName(): string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 表情設定ファイルの数を取得する
|
||||||
|
* @return 表情設定ファイルの数
|
||||||
|
*/
|
||||||
|
public abstract getExpressionCount(): number;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 表情設定ファイルを識別する名前(別名)を取得する
|
||||||
|
* @param index 配列のインデックス値
|
||||||
|
* @return 表情の名前
|
||||||
|
*/
|
||||||
|
public abstract getExpressionName(index: number): string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 表情設定ファイルの名前を取得する
|
||||||
|
* @param index 配列のインデックス値
|
||||||
|
* @return 表情設定ファイルの名前
|
||||||
|
*/
|
||||||
|
public abstract getExpressionFileName(index: number): string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* モーショングループの数を取得する
|
||||||
|
* @return モーショングループの数
|
||||||
|
*/
|
||||||
|
public abstract getMotionGroupCount(): number;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* モーショングループの名前を取得する
|
||||||
|
* @param index 配列のインデックス値
|
||||||
|
* @return モーショングループの名前
|
||||||
|
*/
|
||||||
|
public abstract getMotionGroupName(index: number): string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* モーショングループに含まれるモーションの数を取得する
|
||||||
|
* @param groupName モーショングループの名前
|
||||||
|
* @return モーショングループの数
|
||||||
|
*/
|
||||||
|
public abstract getMotionCount(groupName: string): number;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* グループ名とインデックス値からモーションファイル名を取得する
|
||||||
|
* @param groupName モーショングループの名前
|
||||||
|
* @param index 配列のインデックス値
|
||||||
|
* @return モーションファイルの名前
|
||||||
|
*/
|
||||||
|
public abstract getMotionFileName(groupName: string, index: number): string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* モーションに対応するサウンドファイルの名前を取得する
|
||||||
|
* @param groupName モーショングループの名前
|
||||||
|
* @param index 配列のインデックス値
|
||||||
|
* @return サウンドファイルの名前
|
||||||
|
*/
|
||||||
|
public abstract getMotionSoundFileName(
|
||||||
|
groupName: string,
|
||||||
|
index: number
|
||||||
|
): string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* モーション開始時のフェードイン処理時間を取得する
|
||||||
|
* @param groupName モーショングループの名前
|
||||||
|
* @param index 配列のインデックス値
|
||||||
|
* @return フェードイン処理時間[秒]
|
||||||
|
*/
|
||||||
|
public abstract getMotionFadeInTimeValue(
|
||||||
|
groupName: string,
|
||||||
|
index: number
|
||||||
|
): number;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* モーション終了時のフェードアウト処理時間を取得する
|
||||||
|
* @param groupName モーショングループの名前
|
||||||
|
* @param index 配列のインデックス値
|
||||||
|
* @return フェードアウト処理時間[秒]
|
||||||
|
*/
|
||||||
|
public abstract getMotionFadeOutTimeValue(
|
||||||
|
groupName: string,
|
||||||
|
index: number
|
||||||
|
): number;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ユーザーデータのファイル名を取得する
|
||||||
|
* @return ユーザーデータのファイル名
|
||||||
|
*/
|
||||||
|
public abstract getUserDataFile(): string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* レイアウト情報を取得する
|
||||||
|
* @param outLayoutMap Mapクラスのインスタンス
|
||||||
|
* @return true レイアウト情報が存在する
|
||||||
|
* @return false レイアウト情報が存在しない
|
||||||
|
*/
|
||||||
|
public abstract getLayoutMap(outLayoutMap: Map<string, number>): boolean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 目パチに関連付けられたパラメータの数を取得する
|
||||||
|
* @return 目パチに関連付けられたパラメータの数
|
||||||
|
*/
|
||||||
|
public abstract getEyeBlinkParameterCount(): number;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 目パチに関連付けられたパラメータのIDを取得する
|
||||||
|
* @param index 配列のインデックス値
|
||||||
|
* @return パラメータID
|
||||||
|
*/
|
||||||
|
public abstract getEyeBlinkParameterId(index: number): CubismIdHandle;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* リップシンクに関連付けられたパラメータの数を取得する
|
||||||
|
* @return リップシンクに関連付けられたパラメータの数
|
||||||
|
*/
|
||||||
|
public abstract getLipSyncParameterCount(): number;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* リップシンクに関連付けられたパラメータの数を取得する
|
||||||
|
* @param index 配列のインデックス値
|
||||||
|
* @return パラメータID
|
||||||
|
*/
|
||||||
|
public abstract getLipSyncParameterId(index: number): CubismIdHandle;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Namespace definition for compatibility.
|
||||||
|
import * as $ from './icubismmodelsetting';
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-namespace
|
||||||
|
export namespace Live2DCubismFramework {
|
||||||
|
export const ICubismModelSetting = $.ICubismModelSetting;
|
||||||
|
export type ICubismModelSetting = $.ICubismModelSetting;
|
||||||
|
}
|
||||||
85
avatar-h5-renderer/framework/src/id/cubismid.ts
Normal file
85
avatar-h5-renderer/framework/src/id/cubismid.ts
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
/**
|
||||||
|
* Copyright(c) Live2D Inc. All rights reserved.
|
||||||
|
*
|
||||||
|
* Use of this source code is governed by the Live2D Open Software license
|
||||||
|
* that can be found at https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* パラメータ名・パーツ名・Drawable名を保持
|
||||||
|
*
|
||||||
|
* パラメータ名・パーツ名・Drawable名を保持するクラス。
|
||||||
|
*
|
||||||
|
* @note 指定したID文字列からCubismIdを取得する際はこのクラスの生成メソッドを呼ばず、
|
||||||
|
* CubismIdManager().getId(id)を使用してください
|
||||||
|
*/
|
||||||
|
export class CubismId {
|
||||||
|
/**
|
||||||
|
* 内部で使用するCubismIdクラス生成メソッド
|
||||||
|
*
|
||||||
|
* @param id ID文字列
|
||||||
|
* @return CubismId
|
||||||
|
* @note 指定したID文字列からCubismIdを取得する際は
|
||||||
|
* CubismIdManager().getId(id)を使用してください
|
||||||
|
*/
|
||||||
|
public static createIdInternal(id: string) {
|
||||||
|
return new CubismId(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ID名を取得する
|
||||||
|
*/
|
||||||
|
public getString() {
|
||||||
|
return this._id;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* idを比較
|
||||||
|
* @param c 比較するid
|
||||||
|
* @return 同じならばtrue,異なっていればfalseを返す
|
||||||
|
*/
|
||||||
|
public isEqual(c: string | CubismId): boolean {
|
||||||
|
if (typeof c === 'string') {
|
||||||
|
return this._id == c;
|
||||||
|
} else if (c instanceof CubismId) {
|
||||||
|
return this._id == c._id;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* idを比較
|
||||||
|
* @param c 比較するid
|
||||||
|
* @return 同じならばtrue,異なっていればfalseを返す
|
||||||
|
*/
|
||||||
|
public isNotEqual(c: string | CubismId): boolean {
|
||||||
|
if (typeof c == 'string') {
|
||||||
|
return !(this._id == c);
|
||||||
|
} else if (c instanceof CubismId) {
|
||||||
|
return !(this._id == c._id);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* プライベートコンストラクタ
|
||||||
|
*
|
||||||
|
* @note ユーザーによる生成は許可しません
|
||||||
|
*/
|
||||||
|
private constructor(id: string) {
|
||||||
|
this._id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _id: string; // ID名
|
||||||
|
}
|
||||||
|
|
||||||
|
export declare type CubismIdHandle = CubismId;
|
||||||
|
|
||||||
|
// Namespace definition for compatibility.
|
||||||
|
import * as $ from './cubismid';
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-namespace
|
||||||
|
export namespace Live2DCubismFramework {
|
||||||
|
export const CubismId = $.CubismId;
|
||||||
|
export type CubismId = $.CubismId;
|
||||||
|
export type CubismIdHandle = $.CubismIdHandle;
|
||||||
|
}
|
||||||
114
avatar-h5-renderer/framework/src/id/cubismidmanager.ts
Normal file
114
avatar-h5-renderer/framework/src/id/cubismidmanager.ts
Normal file
@ -0,0 +1,114 @@
|
|||||||
|
/**
|
||||||
|
* Copyright(c) Live2D Inc. All rights reserved.
|
||||||
|
*
|
||||||
|
* Use of this source code is governed by the Live2D Open Software license
|
||||||
|
* that can be found at https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { CubismId } from './cubismid';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ID名の管理
|
||||||
|
*
|
||||||
|
* ID名を管理する。
|
||||||
|
*/
|
||||||
|
export class CubismIdManager {
|
||||||
|
/**
|
||||||
|
* コンストラクタ
|
||||||
|
*/
|
||||||
|
public constructor() {
|
||||||
|
this._ids = new Array<CubismId>();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* デストラクタ相当の処理
|
||||||
|
*/
|
||||||
|
public release(): void {
|
||||||
|
for (let i = 0; i < this._ids.length; ++i) {
|
||||||
|
this._ids[i] = void 0;
|
||||||
|
}
|
||||||
|
this._ids = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ID名をリストから登録
|
||||||
|
*
|
||||||
|
* @param ids ID名リスト
|
||||||
|
* @param count IDの個数
|
||||||
|
*/
|
||||||
|
public registerIds(ids: string[]): void {
|
||||||
|
for (let i = 0; i < ids.length; i++) {
|
||||||
|
this.registerId(ids[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ID名を登録
|
||||||
|
*
|
||||||
|
* @param id ID名
|
||||||
|
*/
|
||||||
|
public registerId(id: string): CubismId {
|
||||||
|
let result: CubismId = null;
|
||||||
|
|
||||||
|
if ('string' == typeof id) {
|
||||||
|
if ((result = this.findId(id)) != null) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
result = CubismId.createIdInternal(id);
|
||||||
|
this._ids.push(result);
|
||||||
|
} else {
|
||||||
|
return this.registerId(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ID名からIDを取得する
|
||||||
|
*
|
||||||
|
* @param id ID名
|
||||||
|
*/
|
||||||
|
public getId(id: string): CubismId {
|
||||||
|
return this.registerId(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ID名からIDの確認
|
||||||
|
*
|
||||||
|
* @return true 存在する
|
||||||
|
* @return false 存在しない
|
||||||
|
*/
|
||||||
|
public isExist(id: string): boolean {
|
||||||
|
if ('string' == typeof id) {
|
||||||
|
return this.findId(id) != null;
|
||||||
|
}
|
||||||
|
return this.isExist(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ID名からIDを検索する。
|
||||||
|
*
|
||||||
|
* @param id ID名
|
||||||
|
* @return 登録されているID。なければNULL。
|
||||||
|
*/
|
||||||
|
private findId(id: string): CubismId {
|
||||||
|
for (let i = 0; i < this._ids.length; ++i) {
|
||||||
|
if (this._ids[i].getString() == id) {
|
||||||
|
return this._ids[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _ids: Array<CubismId>; // 登録されているIDのリスト
|
||||||
|
}
|
||||||
|
|
||||||
|
// Namespace definition for compatibility.
|
||||||
|
import * as $ from './cubismidmanager';
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-namespace
|
||||||
|
export namespace Live2DCubismFramework {
|
||||||
|
export const CubismIdManager = $.CubismIdManager;
|
||||||
|
export type CubismIdManager = $.CubismIdManager;
|
||||||
|
}
|
||||||
288
avatar-h5-renderer/framework/src/live2dcubismframework.ts
Normal file
288
avatar-h5-renderer/framework/src/live2dcubismframework.ts
Normal file
@ -0,0 +1,288 @@
|
|||||||
|
/**
|
||||||
|
* Copyright(c) Live2D Inc. All rights reserved.
|
||||||
|
*
|
||||||
|
* Use of this source code is governed by the Live2D Open Software license
|
||||||
|
* that can be found at https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { CubismIdManager } from './id/cubismidmanager';
|
||||||
|
import { CubismRenderer } from './rendering/cubismrenderer';
|
||||||
|
import {
|
||||||
|
CSM_ASSERT,
|
||||||
|
CubismLogInfo,
|
||||||
|
CubismLogWarning
|
||||||
|
} from './utils/cubismdebug';
|
||||||
|
import { Value } from './utils/cubismjson';
|
||||||
|
|
||||||
|
export function strtod(s: string, endPtr: string[]): number {
|
||||||
|
let index = 0;
|
||||||
|
for (let i = 1; ; i++) {
|
||||||
|
const testC: string = s.slice(i - 1, i);
|
||||||
|
|
||||||
|
// 指数・マイナスの可能性があるのでスキップする
|
||||||
|
if (testC == 'e' || testC == '-' || testC == 'E') {
|
||||||
|
continue;
|
||||||
|
} // 文字列の範囲を広げていく
|
||||||
|
|
||||||
|
const test: string = s.substring(0, i);
|
||||||
|
const number = Number(test);
|
||||||
|
if (isNaN(number)) {
|
||||||
|
// 数値として認識できなくなったので終了
|
||||||
|
break;
|
||||||
|
} // 最後に数値としてできたindexを格納しておく
|
||||||
|
|
||||||
|
index = i;
|
||||||
|
}
|
||||||
|
let d = parseFloat(s); // パースした数値
|
||||||
|
|
||||||
|
if (isNaN(d)) {
|
||||||
|
// 数値として認識できなくなったので終了
|
||||||
|
d = NaN;
|
||||||
|
}
|
||||||
|
|
||||||
|
endPtr[0] = s.slice(index); // 後続の文字列
|
||||||
|
return d;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ファイルスコープの変数を初期化
|
||||||
|
|
||||||
|
let s_isStarted = false;
|
||||||
|
let s_isInitialized = false;
|
||||||
|
let s_option: Option = null;
|
||||||
|
let s_cubismIdManager: CubismIdManager = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Framework内で使う定数の宣言
|
||||||
|
*/
|
||||||
|
export const Constant = Object.freeze<Record<string, number>>({
|
||||||
|
vertexOffset: 0, // メッシュ頂点のオフセット値
|
||||||
|
vertexStep: 2 // メッシュ頂点のステップ値
|
||||||
|
});
|
||||||
|
|
||||||
|
export function csmDelete<T>(address: T): void {
|
||||||
|
if (!address) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
address = void 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Live2D Cubism SDK Original Workflow SDKのエントリポイント
|
||||||
|
* 利用開始時はCubismFramework.initialize()を呼び、CubismFramework.dispose()で終了する。
|
||||||
|
*/
|
||||||
|
export class CubismFramework {
|
||||||
|
/**
|
||||||
|
* Cubism FrameworkのAPIを使用可能にする。
|
||||||
|
* APIを実行する前に必ずこの関数を実行すること。
|
||||||
|
* 一度準備が完了して以降は、再び実行しても内部処理がスキップされます。
|
||||||
|
*
|
||||||
|
* @param option Optionクラスのインスタンス
|
||||||
|
*
|
||||||
|
* @return 準備処理が完了したらtrueが返ります。
|
||||||
|
*/
|
||||||
|
public static startUp(option: Option = null): boolean {
|
||||||
|
if (s_isStarted) {
|
||||||
|
CubismLogInfo('CubismFramework.startUp() is already done.');
|
||||||
|
return s_isStarted;
|
||||||
|
}
|
||||||
|
|
||||||
|
s_option = option;
|
||||||
|
|
||||||
|
if (s_option != null) {
|
||||||
|
Live2DCubismCore.Logging.csmSetLogFunction(s_option.logFunction);
|
||||||
|
}
|
||||||
|
|
||||||
|
s_isStarted = true;
|
||||||
|
|
||||||
|
// Live2D Cubism Coreバージョン情報を表示
|
||||||
|
if (s_isStarted) {
|
||||||
|
const version: number = Live2DCubismCore.Version.csmGetVersion();
|
||||||
|
const major: number = (version & 0xff000000) >> 24;
|
||||||
|
const minor: number = (version & 0x00ff0000) >> 16;
|
||||||
|
const patch: number = version & 0x0000ffff;
|
||||||
|
const versionNumber: number = version;
|
||||||
|
|
||||||
|
CubismLogInfo(
|
||||||
|
`Live2D Cubism Core version: {0}.{1}.{2} ({3})`,
|
||||||
|
('00' + major).slice(-2),
|
||||||
|
('00' + minor).slice(-2),
|
||||||
|
('0000' + patch).slice(-4),
|
||||||
|
versionNumber
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
CubismLogInfo('CubismFramework.startUp() is complete.');
|
||||||
|
|
||||||
|
return s_isStarted;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* StartUp()で初期化したCubismFrameworkの各パラメータをクリアします。
|
||||||
|
* Dispose()したCubismFrameworkを再利用する際に利用してください。
|
||||||
|
*/
|
||||||
|
public static cleanUp(): void {
|
||||||
|
s_isStarted = false;
|
||||||
|
s_isInitialized = false;
|
||||||
|
s_option = null;
|
||||||
|
s_cubismIdManager = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cubism Framework内のリソースを初期化してモデルを表示可能な状態にします。<br>
|
||||||
|
* 再度Initialize()するには先にDispose()を実行する必要があります。
|
||||||
|
*
|
||||||
|
* @param memorySize 初期化時メモリ量 [byte(s)]
|
||||||
|
* 複数モデル表示時などにモデルが更新されない際に使用してください。
|
||||||
|
* 指定する際は必ず1024*1024*16 byte(16MB)以上の値を指定してください。
|
||||||
|
* それ以外はすべて1024*1024*16 byteに丸めます。
|
||||||
|
*/
|
||||||
|
public static initialize(memorySize = 0): void {
|
||||||
|
CSM_ASSERT(s_isStarted);
|
||||||
|
if (!s_isStarted) {
|
||||||
|
CubismLogWarning('CubismFramework is not started.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- s_isInitializedによる連続初期化ガード ---
|
||||||
|
// 連続してリソース確保が行われないようにする。
|
||||||
|
// 再度Initialize()するには先にDispose()を実行する必要がある。
|
||||||
|
if (s_isInitialized) {
|
||||||
|
CubismLogWarning(
|
||||||
|
'CubismFramework.initialize() skipped, already initialized.'
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//---- static 初期化 ----
|
||||||
|
Value.staticInitializeNotForClientCall();
|
||||||
|
|
||||||
|
s_cubismIdManager = new CubismIdManager();
|
||||||
|
|
||||||
|
// --- HACK: 初期化時メモリ量の拡張(単位byte) ---
|
||||||
|
// 複数モデル表示時などにモデルが更新されない際に使用してください。
|
||||||
|
// 指定する際は必ず1024*1024*16 byte(16MB)以上の値を指定してください。
|
||||||
|
// それ以外はすべて1024*1024*16 byteに丸めます。
|
||||||
|
Live2DCubismCore.Memory.initializeAmountOfMemory(memorySize);
|
||||||
|
|
||||||
|
s_isInitialized = true;
|
||||||
|
|
||||||
|
CubismLogInfo('CubismFramework.initialize() is complete.');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cubism Framework内の全てのリソースを解放します。
|
||||||
|
* ただし、外部で確保されたリソースについては解放しません。
|
||||||
|
* 外部で適切に破棄する必要があります。
|
||||||
|
*/
|
||||||
|
public static dispose(): void {
|
||||||
|
CSM_ASSERT(s_isStarted);
|
||||||
|
if (!s_isStarted) {
|
||||||
|
CubismLogWarning('CubismFramework is not started.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- s_isInitializedによる未初期化解放ガード ---
|
||||||
|
// dispose()するには先にinitialize()を実行する必要がある。
|
||||||
|
if (!s_isInitialized) {
|
||||||
|
// false...リソース未確保の場合
|
||||||
|
CubismLogWarning('CubismFramework.dispose() skipped, not initialized.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Value.staticReleaseNotForClientCall();
|
||||||
|
|
||||||
|
s_cubismIdManager.release();
|
||||||
|
s_cubismIdManager = null;
|
||||||
|
|
||||||
|
// レンダラの静的リソース(シェーダプログラム他)を解放する
|
||||||
|
CubismRenderer.staticRelease();
|
||||||
|
|
||||||
|
s_isInitialized = false;
|
||||||
|
|
||||||
|
CubismLogInfo('CubismFramework.dispose() is complete.');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cubism FrameworkのAPIを使用する準備が完了したかどうか
|
||||||
|
* @return APIを使用する準備が完了していればtrueが返ります。
|
||||||
|
*/
|
||||||
|
public static isStarted(): boolean {
|
||||||
|
return s_isStarted;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cubism Frameworkのリソース初期化がすでに行われているかどうか
|
||||||
|
* @return リソース確保が完了していればtrueが返ります
|
||||||
|
*/
|
||||||
|
public static isInitialized(): boolean {
|
||||||
|
return s_isInitialized;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Core APIにバインドしたログ関数を実行する
|
||||||
|
*
|
||||||
|
* @praram message ログメッセージ
|
||||||
|
*/
|
||||||
|
public static coreLogFunction(message: string): void {
|
||||||
|
// Return if logging not possible.
|
||||||
|
if (!Live2DCubismCore.Logging.csmGetLogFunction()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Live2DCubismCore.Logging.csmGetLogFunction()(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 現在のログ出力レベル設定の値を返す。
|
||||||
|
*
|
||||||
|
* @return 現在のログ出力レベル設定の値
|
||||||
|
*/
|
||||||
|
public static getLoggingLevel(): LogLevel {
|
||||||
|
if (s_option != null) {
|
||||||
|
return s_option.loggingLevel;
|
||||||
|
}
|
||||||
|
return LogLevel.LogLevel_Off;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* IDマネージャのインスタンスを取得する
|
||||||
|
* @return CubismManagerクラスのインスタンス
|
||||||
|
*/
|
||||||
|
public static getIdManager(): CubismIdManager {
|
||||||
|
return s_cubismIdManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 静的クラスとして使用する
|
||||||
|
* インスタンス化させない
|
||||||
|
*/
|
||||||
|
private constructor() {}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class Option {
|
||||||
|
logFunction: Live2DCubismCore.csmLogFunction; // ログ出力の関数オブジェクト
|
||||||
|
loggingLevel: LogLevel; // ログ出力レベルの設定
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ログ出力のレベル
|
||||||
|
*/
|
||||||
|
export enum LogLevel {
|
||||||
|
LogLevel_Verbose = 0, // 詳細ログ
|
||||||
|
LogLevel_Debug, // デバッグログ
|
||||||
|
LogLevel_Info, // Infoログ
|
||||||
|
LogLevel_Warning, // 警告ログ
|
||||||
|
LogLevel_Error, // エラーログ
|
||||||
|
LogLevel_Off // ログ出力無効
|
||||||
|
}
|
||||||
|
|
||||||
|
// Namespace definition for compatibility.
|
||||||
|
import * as $ from './live2dcubismframework';
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-namespace
|
||||||
|
export namespace Live2DCubismFramework {
|
||||||
|
export const Constant = $.Constant;
|
||||||
|
export const csmDelete = $.csmDelete;
|
||||||
|
export const CubismFramework = $.CubismFramework;
|
||||||
|
export type CubismFramework = $.CubismFramework;
|
||||||
|
}
|
||||||
376
avatar-h5-renderer/framework/src/math/cubismmath.ts
Normal file
376
avatar-h5-renderer/framework/src/math/cubismmath.ts
Normal file
@ -0,0 +1,376 @@
|
|||||||
|
/**
|
||||||
|
* Copyright(c) Live2D Inc. All rights reserved.
|
||||||
|
*
|
||||||
|
* Use of this source code is governed by the Live2D Open Software license
|
||||||
|
* that can be found at https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { CubismVector2 } from './cubismvector2';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 数値計算などに使用するユーティリティクラス
|
||||||
|
*/
|
||||||
|
export class CubismMath {
|
||||||
|
static readonly Epsilon: number = 0.00001;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 第一引数の値を最小値と最大値の範囲に収めた値を返す
|
||||||
|
*
|
||||||
|
* @param value 収められる値
|
||||||
|
* @param min 範囲の最小値
|
||||||
|
* @param max 範囲の最大値
|
||||||
|
* @return 最小値と最大値の範囲に収めた値
|
||||||
|
*/
|
||||||
|
static range(value: number, min: number, max: number): number {
|
||||||
|
if (value < min) {
|
||||||
|
value = min;
|
||||||
|
} else if (value > max) {
|
||||||
|
value = max;
|
||||||
|
}
|
||||||
|
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* サイン関数の値を求める
|
||||||
|
*
|
||||||
|
* @param x 角度値(ラジアン)
|
||||||
|
* @return サイン関数sin(x)の値
|
||||||
|
*/
|
||||||
|
static sin(x: number): number {
|
||||||
|
return Math.sin(x);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* コサイン関数の値を求める
|
||||||
|
*
|
||||||
|
* @param x 角度値(ラジアン)
|
||||||
|
* @return コサイン関数cos(x)の値
|
||||||
|
*/
|
||||||
|
static cos(x: number): number {
|
||||||
|
return Math.cos(x);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 値の絶対値を求める
|
||||||
|
*
|
||||||
|
* @param x 絶対値を求める値
|
||||||
|
* @return 値の絶対値
|
||||||
|
*/
|
||||||
|
static abs(x: number): number {
|
||||||
|
return Math.abs(x);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 平方根(ルート)を求める
|
||||||
|
* @param x -> 平方根を求める値
|
||||||
|
* @return 値の平方根
|
||||||
|
*/
|
||||||
|
static sqrt(x: number): number {
|
||||||
|
return Math.sqrt(x);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 立方根を求める
|
||||||
|
* @param x -> 立方根を求める値
|
||||||
|
* @return 値の立方根
|
||||||
|
*/
|
||||||
|
static cbrt(x: number): number {
|
||||||
|
if (x === 0) {
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
|
||||||
|
let cx: number = x;
|
||||||
|
const isNegativeNumber: boolean = cx < 0;
|
||||||
|
|
||||||
|
if (isNegativeNumber) {
|
||||||
|
cx = -cx;
|
||||||
|
}
|
||||||
|
|
||||||
|
let ret: number;
|
||||||
|
if (cx === Infinity) {
|
||||||
|
ret = Infinity;
|
||||||
|
} else {
|
||||||
|
ret = Math.exp(Math.log(cx) / 3);
|
||||||
|
ret = (cx / (ret * ret) + 2 * ret) / 3;
|
||||||
|
}
|
||||||
|
return isNegativeNumber ? -ret : ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* イージング処理されたサインを求める
|
||||||
|
* フェードイン・アウト時のイージングに利用できる
|
||||||
|
*
|
||||||
|
* @param value イージングを行う値
|
||||||
|
* @return イージング処理されたサイン値
|
||||||
|
*/
|
||||||
|
static getEasingSine(value: number): number {
|
||||||
|
if (value < 0.0) {
|
||||||
|
return 0.0;
|
||||||
|
} else if (value > 1.0) {
|
||||||
|
return 1.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0.5 - 0.5 * this.cos(value * Math.PI);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 大きい方の値を返す
|
||||||
|
*
|
||||||
|
* @param left 左辺の値
|
||||||
|
* @param right 右辺の値
|
||||||
|
* @return 大きい方の値
|
||||||
|
*/
|
||||||
|
static max(left: number, right: number): number {
|
||||||
|
return left > right ? left : right;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 小さい方の値を返す
|
||||||
|
*
|
||||||
|
* @param left 左辺の値
|
||||||
|
* @param right 右辺の値
|
||||||
|
* @return 小さい方の値
|
||||||
|
*/
|
||||||
|
static min(left: number, right: number): number {
|
||||||
|
return left > right ? right : left;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static clamp(val: number, min: number, max: number): number {
|
||||||
|
if (val < min) {
|
||||||
|
return min;
|
||||||
|
} else if (max < val) {
|
||||||
|
return max;
|
||||||
|
}
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 角度値をラジアン値に変換する
|
||||||
|
*
|
||||||
|
* @param degrees 角度値
|
||||||
|
* @return 角度値から変換したラジアン値
|
||||||
|
*/
|
||||||
|
static degreesToRadian(degrees: number): number {
|
||||||
|
return (degrees / 180.0) * Math.PI;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ラジアン値を角度値に変換する
|
||||||
|
*
|
||||||
|
* @param radian ラジアン値
|
||||||
|
* @return ラジアン値から変換した角度値
|
||||||
|
*/
|
||||||
|
static radianToDegrees(radian: number): number {
|
||||||
|
return (radian * 180.0) / Math.PI;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 2つのベクトルからラジアン値を求める
|
||||||
|
*
|
||||||
|
* @param from 始点ベクトル
|
||||||
|
* @param to 終点ベクトル
|
||||||
|
* @return ラジアン値から求めた方向ベクトル
|
||||||
|
*/
|
||||||
|
static directionToRadian(from: CubismVector2, to: CubismVector2): number {
|
||||||
|
const q1: number = Math.atan2(to.y, to.x);
|
||||||
|
const q2: number = Math.atan2(from.y, from.x);
|
||||||
|
|
||||||
|
let ret: number = q1 - q2;
|
||||||
|
|
||||||
|
while (ret < -Math.PI) {
|
||||||
|
ret += Math.PI * 2.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (ret > Math.PI) {
|
||||||
|
ret -= Math.PI * 2.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 2つのベクトルから角度値を求める
|
||||||
|
*
|
||||||
|
* @param from 始点ベクトル
|
||||||
|
* @param to 終点ベクトル
|
||||||
|
* @return 角度値から求めた方向ベクトル
|
||||||
|
*/
|
||||||
|
static directionToDegrees(from: CubismVector2, to: CubismVector2): number {
|
||||||
|
const radian: number = this.directionToRadian(from, to);
|
||||||
|
let degree: number = this.radianToDegrees(radian);
|
||||||
|
|
||||||
|
if (to.x - from.x > 0.0) {
|
||||||
|
degree = -degree;
|
||||||
|
}
|
||||||
|
|
||||||
|
return degree;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ラジアン値を方向ベクトルに変換する。
|
||||||
|
*
|
||||||
|
* @param totalAngle ラジアン値
|
||||||
|
* @return ラジアン値から変換した方向ベクトル
|
||||||
|
*/
|
||||||
|
|
||||||
|
static radianToDirection(totalAngle: number): CubismVector2 {
|
||||||
|
const ret: CubismVector2 = new CubismVector2();
|
||||||
|
|
||||||
|
ret.x = this.sin(totalAngle);
|
||||||
|
ret.y = this.cos(totalAngle);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 三次方程式の三次項の係数が0になったときに補欠的に二次方程式の解をもとめる。
|
||||||
|
* a * x^2 + b * x + c = 0
|
||||||
|
*
|
||||||
|
* @param a -> 二次項の係数値
|
||||||
|
* @param b -> 一次項の係数値
|
||||||
|
* @param c -> 定数項の値
|
||||||
|
* @return 二次方程式の解
|
||||||
|
*/
|
||||||
|
static quadraticEquation(a: number, b: number, c: number): number {
|
||||||
|
if (this.abs(a) < CubismMath.Epsilon) {
|
||||||
|
if (this.abs(b) < CubismMath.Epsilon) {
|
||||||
|
return -c;
|
||||||
|
}
|
||||||
|
return -c / b;
|
||||||
|
}
|
||||||
|
|
||||||
|
return -(b + this.sqrt(b * b - 4.0 * a * c)) / (2.0 * a);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* カルダノの公式によってベジェのt値に該当する3次方程式の解を求める。
|
||||||
|
* 重解になったときには0.0~1.0の値になる解を返す。
|
||||||
|
*
|
||||||
|
* a * x^3 + b * x^2 + c * x + d = 0
|
||||||
|
*
|
||||||
|
* @param a -> 三次項の係数値
|
||||||
|
* @param b -> 二次項の係数値
|
||||||
|
* @param c -> 一次項の係数値
|
||||||
|
* @param d -> 定数項の値
|
||||||
|
* @return 0.0~1.0の間にある解
|
||||||
|
*/
|
||||||
|
static cardanoAlgorithmForBezier(
|
||||||
|
a: number,
|
||||||
|
b: number,
|
||||||
|
c: number,
|
||||||
|
d: number
|
||||||
|
): number {
|
||||||
|
if (this.abs(a) < CubismMath.Epsilon) {
|
||||||
|
return this.range(this.quadraticEquation(b, c, d), 0.0, 1.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
const ba: number = b / a;
|
||||||
|
const ca: number = c / a;
|
||||||
|
const da: number = d / a;
|
||||||
|
|
||||||
|
const p: number = (3.0 * ca - ba * ba) / 3.0;
|
||||||
|
const p3: number = p / 3.0;
|
||||||
|
const q: number = (2.0 * ba * ba * ba - 9.0 * ba * ca + 27.0 * da) / 27.0;
|
||||||
|
const q2: number = q / 2.0;
|
||||||
|
const discriminant: number = q2 * q2 + p3 * p3 * p3;
|
||||||
|
|
||||||
|
const center = 0.5;
|
||||||
|
const threshold: number = center + 0.01;
|
||||||
|
|
||||||
|
if (discriminant < 0.0) {
|
||||||
|
const mp3: number = -p / 3.0;
|
||||||
|
const mp33: number = mp3 * mp3 * mp3;
|
||||||
|
const r: number = this.sqrt(mp33);
|
||||||
|
const t: number = -q / (2.0 * r);
|
||||||
|
const cosphi: number = this.range(t, -1.0, 1.0);
|
||||||
|
const phi: number = Math.acos(cosphi);
|
||||||
|
const crtr: number = this.cbrt(r);
|
||||||
|
const t1: number = 2.0 * crtr;
|
||||||
|
|
||||||
|
const root1: number = t1 * this.cos(phi / 3.0) - ba / 3.0;
|
||||||
|
if (this.abs(root1 - center) < threshold) {
|
||||||
|
return this.range(root1, 0.0, 1.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
const root2: number =
|
||||||
|
t1 * this.cos((phi + 2.0 * Math.PI) / 3.0) - ba / 3.0;
|
||||||
|
if (this.abs(root2 - center) < threshold) {
|
||||||
|
return this.range(root2, 0.0, 1.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
const root3: number =
|
||||||
|
t1 * this.cos((phi + 4.0 * Math.PI) / 3.0) - ba / 3.0;
|
||||||
|
return this.range(root3, 0.0, 1.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (discriminant == 0.0) {
|
||||||
|
let u1: number;
|
||||||
|
if (q2 < 0.0) {
|
||||||
|
u1 = this.cbrt(-q2);
|
||||||
|
} else {
|
||||||
|
u1 = -this.cbrt(q2);
|
||||||
|
}
|
||||||
|
|
||||||
|
const root1: number = 2.0 * u1 - ba / 3.0;
|
||||||
|
if (this.abs(root1 - center) < threshold) {
|
||||||
|
return this.range(root1, 0.0, 1.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
const root2: number = -u1 - ba / 3.0;
|
||||||
|
return this.range(root2, 0.0, 1.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
const sd: number = this.sqrt(discriminant);
|
||||||
|
const u1: number = this.cbrt(sd - q2);
|
||||||
|
const v1: number = this.cbrt(sd + q2);
|
||||||
|
const root1: number = u1 - v1 - ba / 3.0;
|
||||||
|
return this.range(root1, 0.0, 1.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 浮動小数点の余りを求める。
|
||||||
|
*
|
||||||
|
* @param dividend 被除数(割られる値)
|
||||||
|
* @param divisor 除数(割る値)
|
||||||
|
* @return 余り
|
||||||
|
*/
|
||||||
|
static mod(dividend: number, divisor: number): number {
|
||||||
|
if (
|
||||||
|
!isFinite(dividend) ||
|
||||||
|
divisor === 0 ||
|
||||||
|
isNaN(dividend) ||
|
||||||
|
isNaN(divisor)
|
||||||
|
) {
|
||||||
|
console.warn(
|
||||||
|
`divided: ${dividend}, divisor: ${divisor} mod() returns 'NaN'.`
|
||||||
|
);
|
||||||
|
return NaN;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 絶対値に変換する。
|
||||||
|
const absDividend = Math.abs(dividend);
|
||||||
|
const absDivisor = Math.abs(divisor);
|
||||||
|
|
||||||
|
// 絶対値で割り算する。
|
||||||
|
let result =
|
||||||
|
absDividend - Math.floor(absDividend / absDivisor) * absDivisor;
|
||||||
|
|
||||||
|
// 符号を被除数のものに指定する。
|
||||||
|
result *= Math.sign(dividend);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* コンストラクタ
|
||||||
|
*/
|
||||||
|
private constructor() {}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Namespace definition for compatibility.
|
||||||
|
import * as $ from './cubismmath';
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-namespace
|
||||||
|
export namespace Live2DCubismFramework {
|
||||||
|
export const CubismMath = $.CubismMath;
|
||||||
|
export type CubismMath = $.CubismMath;
|
||||||
|
}
|
||||||
361
avatar-h5-renderer/framework/src/math/cubismmatrix44.ts
Normal file
361
avatar-h5-renderer/framework/src/math/cubismmatrix44.ts
Normal file
@ -0,0 +1,361 @@
|
|||||||
|
/**
|
||||||
|
* Copyright(c) Live2D Inc. All rights reserved.
|
||||||
|
*
|
||||||
|
* Use of this source code is governed by the Live2D Open Software license
|
||||||
|
* that can be found at https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { CubismMath } from './cubismmath';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 4x4の行列
|
||||||
|
*
|
||||||
|
* 4x4行列の便利クラス。
|
||||||
|
*/
|
||||||
|
export class CubismMatrix44 {
|
||||||
|
/**
|
||||||
|
* コンストラクタ
|
||||||
|
*/
|
||||||
|
public constructor() {
|
||||||
|
this._tr = new Float32Array(16); // 4 * 4のサイズ
|
||||||
|
this.loadIdentity();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 受け取った2つの行列の乗算を行う。
|
||||||
|
*
|
||||||
|
* @param a 行列a
|
||||||
|
* @param b 行列b
|
||||||
|
*
|
||||||
|
* @return 乗算結果の行列
|
||||||
|
*/
|
||||||
|
public static multiply(
|
||||||
|
a: Float32Array,
|
||||||
|
b: Float32Array,
|
||||||
|
dst: Float32Array
|
||||||
|
): void {
|
||||||
|
const c: Float32Array = new Float32Array([
|
||||||
|
0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,
|
||||||
|
0.0
|
||||||
|
]);
|
||||||
|
|
||||||
|
const n = 4;
|
||||||
|
|
||||||
|
for (let i = 0; i < n; ++i) {
|
||||||
|
for (let j = 0; j < n; ++j) {
|
||||||
|
for (let k = 0; k < n; ++k) {
|
||||||
|
c[j + i * 4] += a[k + i * 4] * b[j + k * 4];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let i = 0; i < 16; ++i) {
|
||||||
|
dst[i] = c[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 単位行列に初期化する
|
||||||
|
*/
|
||||||
|
public loadIdentity(): void {
|
||||||
|
const c: Float32Array = new Float32Array([
|
||||||
|
1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0,
|
||||||
|
1.0
|
||||||
|
]);
|
||||||
|
|
||||||
|
this.setMatrix(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 行列を設定
|
||||||
|
*
|
||||||
|
* @param tr 16個の浮動小数点数で表される4x4の行列
|
||||||
|
*/
|
||||||
|
public setMatrix(tr: Float32Array): void {
|
||||||
|
for (let i = 0; i < 16; ++i) {
|
||||||
|
this._tr[i] = tr[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 行列を浮動小数点数の配列で取得
|
||||||
|
*
|
||||||
|
* @return 16個の浮動小数点数で表される4x4の行列
|
||||||
|
*/
|
||||||
|
public getArray(): Float32Array {
|
||||||
|
return this._tr;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* X軸の拡大率を取得
|
||||||
|
*
|
||||||
|
* @return X軸の拡大率
|
||||||
|
*/
|
||||||
|
public getScaleX(): number {
|
||||||
|
return this._tr[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Y軸の拡大率を取得する
|
||||||
|
*
|
||||||
|
* @return Y軸の拡大率
|
||||||
|
*/
|
||||||
|
public getScaleY(): number {
|
||||||
|
return this._tr[5];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* X軸の移動量を取得
|
||||||
|
*
|
||||||
|
* @return X軸の移動量
|
||||||
|
*/
|
||||||
|
public getTranslateX(): number {
|
||||||
|
return this._tr[12];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Y軸の移動量を取得
|
||||||
|
*
|
||||||
|
* @return Y軸の移動量
|
||||||
|
*/
|
||||||
|
public getTranslateY(): number {
|
||||||
|
return this._tr[13];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* X軸の値を現在の行列で計算
|
||||||
|
*
|
||||||
|
* @param src X軸の値
|
||||||
|
*
|
||||||
|
* @return 現在の行列で計算されたX軸の値
|
||||||
|
*/
|
||||||
|
public transformX(src: number): number {
|
||||||
|
return this._tr[0] * src + this._tr[12];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Y軸の値を現在の行列で計算
|
||||||
|
*
|
||||||
|
* @param src Y軸の値
|
||||||
|
*
|
||||||
|
* @return 現在の行列で計算されたY軸の値
|
||||||
|
*/
|
||||||
|
public transformY(src: number): number {
|
||||||
|
return this._tr[5] * src + this._tr[13];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* X軸の値を現在の行列で逆計算
|
||||||
|
*/
|
||||||
|
public invertTransformX(src: number): number {
|
||||||
|
return (src - this._tr[12]) / this._tr[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Y軸の値を現在の行列で逆計算
|
||||||
|
*/
|
||||||
|
public invertTransformY(src: number): number {
|
||||||
|
return (src - this._tr[13]) / this._tr[5];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 現在の行列の位置を起点にして移動
|
||||||
|
*
|
||||||
|
* 現在の行列の位置を起点にして相対的に移動する。
|
||||||
|
*
|
||||||
|
* @param x X軸の移動量
|
||||||
|
* @param y Y軸の移動量
|
||||||
|
*/
|
||||||
|
public translateRelative(x: number, y: number): void {
|
||||||
|
const tr1: Float32Array = new Float32Array([
|
||||||
|
1.0,
|
||||||
|
0.0,
|
||||||
|
0.0,
|
||||||
|
0.0,
|
||||||
|
0.0,
|
||||||
|
1.0,
|
||||||
|
0.0,
|
||||||
|
0.0,
|
||||||
|
0.0,
|
||||||
|
0.0,
|
||||||
|
1.0,
|
||||||
|
0.0,
|
||||||
|
x,
|
||||||
|
y,
|
||||||
|
0.0,
|
||||||
|
1.0
|
||||||
|
]);
|
||||||
|
|
||||||
|
CubismMatrix44.multiply(tr1, this._tr, this._tr);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 現在の行列の位置を移動
|
||||||
|
*
|
||||||
|
* 現在の行列の位置を指定した位置へ移動する
|
||||||
|
*
|
||||||
|
* @param x X軸の移動量
|
||||||
|
* @param y y軸の移動量
|
||||||
|
*/
|
||||||
|
public translate(x: number, y: number): void {
|
||||||
|
this._tr[12] = x;
|
||||||
|
this._tr[13] = y;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 現在の行列のX軸の位置を指定した位置へ移動する
|
||||||
|
*
|
||||||
|
* @param x X軸の移動量
|
||||||
|
*/
|
||||||
|
public translateX(x: number): void {
|
||||||
|
this._tr[12] = x;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 現在の行列のY軸の位置を指定した位置へ移動する
|
||||||
|
*
|
||||||
|
* @param y Y軸の移動量
|
||||||
|
*/
|
||||||
|
public translateY(y: number): void {
|
||||||
|
this._tr[13] = y;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 現在の行列の拡大率を相対的に設定する
|
||||||
|
*
|
||||||
|
* @param x X軸の拡大率
|
||||||
|
* @param y Y軸の拡大率
|
||||||
|
*/
|
||||||
|
public scaleRelative(x: number, y: number): void {
|
||||||
|
const tr1: Float32Array = new Float32Array([
|
||||||
|
x,
|
||||||
|
0.0,
|
||||||
|
0.0,
|
||||||
|
0.0,
|
||||||
|
0.0,
|
||||||
|
y,
|
||||||
|
0.0,
|
||||||
|
0.0,
|
||||||
|
0.0,
|
||||||
|
0.0,
|
||||||
|
1.0,
|
||||||
|
0.0,
|
||||||
|
0.0,
|
||||||
|
0.0,
|
||||||
|
0.0,
|
||||||
|
1.0
|
||||||
|
]);
|
||||||
|
|
||||||
|
CubismMatrix44.multiply(tr1, this._tr, this._tr);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 現在の行列の拡大率を指定した倍率に設定する
|
||||||
|
*
|
||||||
|
* @param x X軸の拡大率
|
||||||
|
* @param y Y軸の拡大率
|
||||||
|
*/
|
||||||
|
public scale(x: number, y: number): void {
|
||||||
|
this._tr[0] = x;
|
||||||
|
this._tr[5] = y;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 引数で与えられた行列にこの行列を乗算する。
|
||||||
|
* (引数で与えられた行列) * (この行列)
|
||||||
|
*
|
||||||
|
* @note 関数名と実際の計算内容に乖離があるため、今後計算順が修正される可能性があります。
|
||||||
|
* @param m 行列
|
||||||
|
*/
|
||||||
|
public multiplyByMatrix(m: CubismMatrix44): void {
|
||||||
|
CubismMatrix44.multiply(m.getArray(), this._tr, this._tr);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 現在の行列の逆行列を求める。
|
||||||
|
*
|
||||||
|
* @return 現在の行列で計算された逆行列の値を返す
|
||||||
|
*/
|
||||||
|
public getInvert(): CubismMatrix44 {
|
||||||
|
const r00 = this._tr[0];
|
||||||
|
const r10 = this._tr[1];
|
||||||
|
const r20 = this._tr[2];
|
||||||
|
const r01 = this._tr[4];
|
||||||
|
const r11 = this._tr[5];
|
||||||
|
const r21 = this._tr[6];
|
||||||
|
const r02 = this._tr[8];
|
||||||
|
const r12 = this._tr[9];
|
||||||
|
const r22 = this._tr[10];
|
||||||
|
|
||||||
|
const tx = this._tr[12];
|
||||||
|
const ty = this._tr[13];
|
||||||
|
const tz = this._tr[14];
|
||||||
|
|
||||||
|
const det =
|
||||||
|
r00 * (r11 * r22 - r12 * r21) -
|
||||||
|
r01 * (r10 * r22 - r12 * r20) +
|
||||||
|
r02 * (r10 * r21 - r11 * r20);
|
||||||
|
|
||||||
|
const dst = new CubismMatrix44();
|
||||||
|
|
||||||
|
if (CubismMath.abs(det) < CubismMath.Epsilon) {
|
||||||
|
dst.loadIdentity();
|
||||||
|
return dst;
|
||||||
|
}
|
||||||
|
|
||||||
|
const invDet = 1.0 / det;
|
||||||
|
|
||||||
|
const inv00 = (r11 * r22 - r12 * r21) * invDet;
|
||||||
|
const inv01 = -(r01 * r22 - r02 * r21) * invDet;
|
||||||
|
const inv02 = (r01 * r12 - r02 * r11) * invDet;
|
||||||
|
const inv10 = -(r10 * r22 - r12 * r20) * invDet;
|
||||||
|
const inv11 = (r00 * r22 - r02 * r20) * invDet;
|
||||||
|
const inv12 = -(r00 * r12 - r02 * r10) * invDet;
|
||||||
|
const inv20 = (r10 * r21 - r11 * r20) * invDet;
|
||||||
|
const inv21 = -(r00 * r21 - r01 * r20) * invDet;
|
||||||
|
const inv22 = (r00 * r11 - r01 * r10) * invDet;
|
||||||
|
|
||||||
|
dst._tr[0] = inv00;
|
||||||
|
dst._tr[1] = inv10;
|
||||||
|
dst._tr[2] = inv20;
|
||||||
|
dst._tr[3] = 0.0;
|
||||||
|
dst._tr[4] = inv01;
|
||||||
|
dst._tr[5] = inv11;
|
||||||
|
dst._tr[6] = inv21;
|
||||||
|
dst._tr[7] = 0.0;
|
||||||
|
dst._tr[8] = inv02;
|
||||||
|
dst._tr[9] = inv12;
|
||||||
|
dst._tr[10] = inv22;
|
||||||
|
dst._tr[11] = 0.0;
|
||||||
|
|
||||||
|
dst._tr[12] = -(inv00 * tx + inv01 * ty + inv02 * tz);
|
||||||
|
dst._tr[13] = -(inv10 * tx + inv11 * ty + inv12 * tz);
|
||||||
|
dst._tr[14] = -(inv20 * tx + inv21 * ty + inv22 * tz);
|
||||||
|
dst._tr[15] = 1.0;
|
||||||
|
|
||||||
|
return dst;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* オブジェクトのコピーを生成する
|
||||||
|
*/
|
||||||
|
public clone(): CubismMatrix44 {
|
||||||
|
const cloneMatrix: CubismMatrix44 = new CubismMatrix44();
|
||||||
|
|
||||||
|
for (let i = 0; i < this._tr.length; i++) {
|
||||||
|
cloneMatrix._tr[i] = this._tr[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
return cloneMatrix;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected _tr: Float32Array; // 4x4行列データ
|
||||||
|
}
|
||||||
|
|
||||||
|
// Namespace definition for compatibility.
|
||||||
|
import * as $ from './cubismmatrix44';
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-namespace
|
||||||
|
export namespace Live2DCubismFramework {
|
||||||
|
export const CubismMatrix44 = $.CubismMatrix44;
|
||||||
|
export type CubismMatrix44 = $.CubismMatrix44;
|
||||||
|
}
|
||||||
217
avatar-h5-renderer/framework/src/math/cubismmodelmatrix.ts
Normal file
217
avatar-h5-renderer/framework/src/math/cubismmodelmatrix.ts
Normal file
@ -0,0 +1,217 @@
|
|||||||
|
/**
|
||||||
|
* Copyright(c) Live2D Inc. All rights reserved.
|
||||||
|
*
|
||||||
|
* Use of this source code is governed by the Live2D Open Software license
|
||||||
|
* that can be found at https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { CubismMatrix44 } from './cubismmatrix44';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* モデル座標設定用の4x4行列
|
||||||
|
*
|
||||||
|
* モデル座標設定用の4x4行列クラス
|
||||||
|
*/
|
||||||
|
export class CubismModelMatrix extends CubismMatrix44 {
|
||||||
|
/**
|
||||||
|
* コンストラクタ
|
||||||
|
*
|
||||||
|
* @param w 横幅
|
||||||
|
* @param h 縦幅
|
||||||
|
*/
|
||||||
|
constructor(w?: number, h?: number) {
|
||||||
|
super();
|
||||||
|
|
||||||
|
this._width = w !== undefined ? w : 0.0;
|
||||||
|
this._height = h !== undefined ? h : 0.0;
|
||||||
|
|
||||||
|
this.setHeight(2.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 横幅を設定
|
||||||
|
*
|
||||||
|
* @param w 横幅
|
||||||
|
*/
|
||||||
|
public setWidth(w: number): void {
|
||||||
|
const scaleX: number = w / this._width;
|
||||||
|
const scaleY: number = scaleX;
|
||||||
|
this.scale(scaleX, scaleY);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 縦幅を設定
|
||||||
|
* @param h 縦幅
|
||||||
|
*/
|
||||||
|
public setHeight(h: number): void {
|
||||||
|
const scaleX: number = h / this._height;
|
||||||
|
const scaleY: number = scaleX;
|
||||||
|
this.scale(scaleX, scaleY);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 位置を設定
|
||||||
|
*
|
||||||
|
* @param x X軸の位置
|
||||||
|
* @param y Y軸の位置
|
||||||
|
*/
|
||||||
|
public setPosition(x: number, y: number): void {
|
||||||
|
this.translate(x, y);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 中心位置を設定
|
||||||
|
*
|
||||||
|
* @param x X軸の中心位置
|
||||||
|
* @param y Y軸の中心位置
|
||||||
|
*
|
||||||
|
* @note widthかheightを設定したあとでないと、拡大率が正しく取得できないためずれる。
|
||||||
|
*/
|
||||||
|
public setCenterPosition(x: number, y: number) {
|
||||||
|
this.centerX(x);
|
||||||
|
this.centerY(y);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 上辺の位置を設定する
|
||||||
|
*
|
||||||
|
* @param y 上辺のY軸位置
|
||||||
|
*/
|
||||||
|
public top(y: number): void {
|
||||||
|
this.setY(y);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 下辺の位置を設定する
|
||||||
|
*
|
||||||
|
* @param y 下辺のY軸位置
|
||||||
|
*/
|
||||||
|
public bottom(y: number) {
|
||||||
|
const h: number = this._height * this.getScaleY();
|
||||||
|
|
||||||
|
this.translateY(y - h);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 左辺の位置を設定
|
||||||
|
*
|
||||||
|
* @param x 左辺のX軸位置
|
||||||
|
*/
|
||||||
|
public left(x: number): void {
|
||||||
|
this.setX(x);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 右辺の位置を設定
|
||||||
|
*
|
||||||
|
* @param x 右辺のX軸位置
|
||||||
|
*/
|
||||||
|
public right(x: number): void {
|
||||||
|
const w = this._width * this.getScaleX();
|
||||||
|
|
||||||
|
this.translateX(x - w);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* X軸の中心位置を設定
|
||||||
|
*
|
||||||
|
* @param x X軸の中心位置
|
||||||
|
*/
|
||||||
|
public centerX(x: number): void {
|
||||||
|
const w = this._width * this.getScaleX();
|
||||||
|
|
||||||
|
this.translateX(x - w / 2.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* X軸の位置を設定
|
||||||
|
*
|
||||||
|
* @param x X軸の位置
|
||||||
|
*/
|
||||||
|
public setX(x: number): void {
|
||||||
|
this.translateX(x);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Y軸の中心位置を設定
|
||||||
|
*
|
||||||
|
* @param y Y軸の中心位置
|
||||||
|
*/
|
||||||
|
public centerY(y: number): void {
|
||||||
|
const h: number = this._height * this.getScaleY();
|
||||||
|
|
||||||
|
this.translateY(y - h / 2.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Y軸の位置を設定する
|
||||||
|
*
|
||||||
|
* @param y Y軸の位置
|
||||||
|
*/
|
||||||
|
public setY(y: number): void {
|
||||||
|
this.translateY(y);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* レイアウト情報から位置を設定
|
||||||
|
*
|
||||||
|
* @param layout レイアウト情報
|
||||||
|
*/
|
||||||
|
public setupFromLayout(layout: Map<string, number>): void {
|
||||||
|
const keyWidth = 'width';
|
||||||
|
const keyHeight = 'height';
|
||||||
|
const keyX = 'x';
|
||||||
|
const keyY = 'y';
|
||||||
|
const keyCenterX = 'center_x';
|
||||||
|
const keyCenterY = 'center_y';
|
||||||
|
const keyTop = 'top';
|
||||||
|
const keyBottom = 'bottom';
|
||||||
|
const keyLeft = 'left';
|
||||||
|
const keyRight = 'right';
|
||||||
|
|
||||||
|
for (const item of layout) {
|
||||||
|
const key: string = item[0];
|
||||||
|
const value: number = item[1];
|
||||||
|
|
||||||
|
if (key == keyWidth) {
|
||||||
|
this.setWidth(value);
|
||||||
|
} else if (key == keyHeight) {
|
||||||
|
this.setHeight(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const item of layout) {
|
||||||
|
const key: string = item[0];
|
||||||
|
const value: number = item[1];
|
||||||
|
|
||||||
|
if (key == keyX) {
|
||||||
|
this.setX(value);
|
||||||
|
} else if (key == keyY) {
|
||||||
|
this.setY(value);
|
||||||
|
} else if (key == keyCenterX) {
|
||||||
|
this.centerX(value);
|
||||||
|
} else if (key == keyCenterY) {
|
||||||
|
this.centerY(value);
|
||||||
|
} else if (key == keyTop) {
|
||||||
|
this.top(value);
|
||||||
|
} else if (key == keyBottom) {
|
||||||
|
this.bottom(value);
|
||||||
|
} else if (key == keyLeft) {
|
||||||
|
this.left(value);
|
||||||
|
} else if (key == keyRight) {
|
||||||
|
this.right(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private _width: number; // 横幅
|
||||||
|
private _height: number; // 縦幅
|
||||||
|
}
|
||||||
|
|
||||||
|
// Namespace definition for compatibility.
|
||||||
|
import * as $ from './cubismmodelmatrix';
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-namespace
|
||||||
|
export namespace Live2DCubismFramework {
|
||||||
|
export const CubismModelMatrix = $.CubismModelMatrix;
|
||||||
|
export type CubismModelMatrix = $.CubismModelMatrix;
|
||||||
|
}
|
||||||
169
avatar-h5-renderer/framework/src/math/cubismtargetpoint.ts
Normal file
169
avatar-h5-renderer/framework/src/math/cubismtargetpoint.ts
Normal file
@ -0,0 +1,169 @@
|
|||||||
|
/**
|
||||||
|
* Copyright(c) Live2D Inc. All rights reserved.
|
||||||
|
*
|
||||||
|
* Use of this source code is governed by the Live2D Open Software license
|
||||||
|
* that can be found at https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { CubismMath } from './cubismmath';
|
||||||
|
|
||||||
|
const FrameRate = 30;
|
||||||
|
const Epsilon = 0.01;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 顔の向きの制御機能
|
||||||
|
*
|
||||||
|
* 顔の向きの制御機能を提供するクラス。
|
||||||
|
*/
|
||||||
|
export class CubismTargetPoint {
|
||||||
|
/**
|
||||||
|
* コンストラクタ
|
||||||
|
*/
|
||||||
|
public constructor() {
|
||||||
|
this._faceTargetX = 0.0;
|
||||||
|
this._faceTargetY = 0.0;
|
||||||
|
this._faceX = 0.0;
|
||||||
|
this._faceY = 0.0;
|
||||||
|
this._faceVX = 0.0;
|
||||||
|
this._faceVY = 0.0;
|
||||||
|
this._lastTimeSeconds = 0.0;
|
||||||
|
this._userTimeSeconds = 0.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新処理
|
||||||
|
*/
|
||||||
|
public update(deltaTimeSeconds: number): void {
|
||||||
|
// デルタ時間を加算する
|
||||||
|
this._userTimeSeconds += deltaTimeSeconds;
|
||||||
|
|
||||||
|
// 首を中央から左右に振るときの平均的な速さは 秒速度。加速・減速を考慮して、その2倍を最高速度とする
|
||||||
|
// 顔の振り具合を、中央(0.0)から、左右は(+-1.0)とする
|
||||||
|
const faceParamMaxV: number = 40.0 / 10.0; // 7.5秒間に40分移動(5.3/sc)
|
||||||
|
const maxV: number = (faceParamMaxV * 1.0) / FrameRate; // 1frameあたりに変化できる速度の上限
|
||||||
|
|
||||||
|
if (this._lastTimeSeconds == 0.0) {
|
||||||
|
this._lastTimeSeconds = this._userTimeSeconds;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const deltaTimeWeight: number =
|
||||||
|
(this._userTimeSeconds - this._lastTimeSeconds) * FrameRate;
|
||||||
|
this._lastTimeSeconds = this._userTimeSeconds;
|
||||||
|
|
||||||
|
// 最高速度になるまでの時間を
|
||||||
|
const timeToMaxSpeed = 0.15;
|
||||||
|
const frameToMaxSpeed: number = timeToMaxSpeed * FrameRate; // sec * frame/sec
|
||||||
|
const maxA: number = (deltaTimeWeight * maxV) / frameToMaxSpeed; // 1frameあたりの加速度
|
||||||
|
|
||||||
|
// 目指す向きは、(dx, dy)方向のベクトルとなる
|
||||||
|
const dx: number = this._faceTargetX - this._faceX;
|
||||||
|
const dy: number = this._faceTargetY - this._faceY;
|
||||||
|
|
||||||
|
if (CubismMath.abs(dx) <= Epsilon && CubismMath.abs(dy) <= Epsilon) {
|
||||||
|
return; // 変化なし
|
||||||
|
}
|
||||||
|
|
||||||
|
// 速度の最大よりも大きい場合は、速度を落とす
|
||||||
|
const d: number = CubismMath.sqrt(dx * dx + dy * dy);
|
||||||
|
|
||||||
|
// 進行方向の最大速度ベクトル
|
||||||
|
const vx: number = (maxV * dx) / d;
|
||||||
|
const vy: number = (maxV * dy) / d;
|
||||||
|
|
||||||
|
// 現在の速度から、新規速度への変化(加速度)を求める
|
||||||
|
let ax: number = vx - this._faceVX;
|
||||||
|
let ay: number = vy - this._faceVY;
|
||||||
|
|
||||||
|
const a: number = CubismMath.sqrt(ax * ax + ay * ay);
|
||||||
|
|
||||||
|
// 加速のとき
|
||||||
|
if (a < -maxA || a > maxA) {
|
||||||
|
ax *= maxA / a;
|
||||||
|
ay *= maxA / a;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 加速度を元の速度に足して、新速度とする
|
||||||
|
this._faceVX += ax;
|
||||||
|
this._faceVY += ay;
|
||||||
|
|
||||||
|
// 目的の方向に近づいたとき、滑らかに減速するための処理
|
||||||
|
// 設定された加速度で止まる事の出来る距離と速度の関係から
|
||||||
|
// 現在とりうる最高速度を計算し、それ以上の時は速度を落とす
|
||||||
|
// ※本来、人間は筋力で力(加速度)を調整できるため、より自由度が高いが、簡単な処理で済ませている
|
||||||
|
{
|
||||||
|
// 加速度、速度、距離の関係式。
|
||||||
|
// 2 6 2 3
|
||||||
|
// sqrt(a t + 16 a h t - 8 a h) - a t
|
||||||
|
// v = --------------------------------------
|
||||||
|
// 2
|
||||||
|
// 4 t - 2
|
||||||
|
// (t=1)
|
||||||
|
// 時刻tは、あらかじめ加速度、速度を1/60(フレームレート、単位なし)で
|
||||||
|
// 考えているので、t=1として消してよい(※未検証)
|
||||||
|
|
||||||
|
const maxV: number =
|
||||||
|
0.5 *
|
||||||
|
(CubismMath.sqrt(maxA * maxA + 16.0 * maxA * d - 8.0 * maxA * d) -
|
||||||
|
maxA);
|
||||||
|
const curV: number = CubismMath.sqrt(
|
||||||
|
this._faceVX * this._faceVX + this._faceVY * this._faceVY
|
||||||
|
);
|
||||||
|
|
||||||
|
if (curV > maxV) {
|
||||||
|
// 現在の速度 > 最高速度のとき、最高速度まで減速
|
||||||
|
this._faceVX *= maxV / curV;
|
||||||
|
this._faceVY *= maxV / curV;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this._faceX += this._faceVX;
|
||||||
|
this._faceY += this._faceVY;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* X軸の顔の向きの値を取得
|
||||||
|
*
|
||||||
|
* @return X軸の顔の向きの値(-1.0 ~ 1.0)
|
||||||
|
*/
|
||||||
|
public getX(): number {
|
||||||
|
return this._faceX;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Y軸の顔の向きの値を取得
|
||||||
|
*
|
||||||
|
* @return Y軸の顔の向きの値(-1.0 ~ 1.0)
|
||||||
|
*/
|
||||||
|
public getY(): number {
|
||||||
|
return this._faceY;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 顔の向きの目標値を設定
|
||||||
|
*
|
||||||
|
* @param x X軸の顔の向きの値(-1.0 ~ 1.0)
|
||||||
|
* @param y Y軸の顔の向きの値(-1.0 ~ 1.0)
|
||||||
|
*/
|
||||||
|
public set(x: number, y: number): void {
|
||||||
|
this._faceTargetX = x;
|
||||||
|
this._faceTargetY = y;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _faceTargetX: number; // 顔の向きのX目標値(この値に近づいていく)
|
||||||
|
private _faceTargetY: number; // 顔の向きのY目標値(この値に近づいていく)
|
||||||
|
private _faceX: number; // 顔の向きX(-1.0 ~ 1.0)
|
||||||
|
private _faceY: number; // 顔の向きY(-1.0 ~ 1.0)
|
||||||
|
private _faceVX: number; // 顔の向きの変化速度X
|
||||||
|
private _faceVY: number; // 顔の向きの変化速度Y
|
||||||
|
private _lastTimeSeconds: number; // 最後の実行時間[秒]
|
||||||
|
private _userTimeSeconds: number; // デルタ時間の積算値[秒]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Namespace definition for compatibility.
|
||||||
|
import * as $ from './cubismtargetpoint';
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-namespace
|
||||||
|
export namespace Live2DCubismFramework {
|
||||||
|
export const CubismTargetPoint = $.CubismTargetPoint;
|
||||||
|
export type CubismTargetPoint = $.CubismTargetPoint;
|
||||||
|
}
|
||||||
172
avatar-h5-renderer/framework/src/math/cubismvector2.ts
Normal file
172
avatar-h5-renderer/framework/src/math/cubismvector2.ts
Normal file
@ -0,0 +1,172 @@
|
|||||||
|
/**
|
||||||
|
* Copyright(c) Live2D Inc. All rights reserved.
|
||||||
|
*
|
||||||
|
* Use of this source code is governed by the Live2D Open Software license
|
||||||
|
* that can be found at https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 2次元ベクトル型
|
||||||
|
*
|
||||||
|
* 2次元ベクトル型の機能を提供する。
|
||||||
|
*/
|
||||||
|
export class CubismVector2 {
|
||||||
|
/**
|
||||||
|
* コンストラクタ
|
||||||
|
*/
|
||||||
|
public constructor(
|
||||||
|
public x?: number,
|
||||||
|
public y?: number
|
||||||
|
) {
|
||||||
|
this.x = x == undefined ? 0.0 : x;
|
||||||
|
|
||||||
|
this.y = y == undefined ? 0.0 : y;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ベクトルの加算
|
||||||
|
*
|
||||||
|
* @param vector2 加算するベクトル値
|
||||||
|
* @return 加算結果 ベクトル値
|
||||||
|
*/
|
||||||
|
public add(vector2: CubismVector2): CubismVector2 {
|
||||||
|
const ret: CubismVector2 = new CubismVector2(0.0, 0.0);
|
||||||
|
ret.x = this.x + vector2.x;
|
||||||
|
ret.y = this.y + vector2.y;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ベクトルの減算
|
||||||
|
*
|
||||||
|
* @param vector2 減算するベクトル値
|
||||||
|
* @return 減算結果 ベクトル値
|
||||||
|
*/
|
||||||
|
public substract(vector2: CubismVector2): CubismVector2 {
|
||||||
|
const ret: CubismVector2 = new CubismVector2(0.0, 0.0);
|
||||||
|
ret.x = this.x - vector2.x;
|
||||||
|
ret.y = this.y - vector2.y;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ベクトルの乗算
|
||||||
|
*
|
||||||
|
* @param vector2 乗算するベクトル値
|
||||||
|
* @return 乗算結果 ベクトル値
|
||||||
|
*/
|
||||||
|
public multiply(vector2: CubismVector2): CubismVector2 {
|
||||||
|
const ret: CubismVector2 = new CubismVector2(0.0, 0.0);
|
||||||
|
ret.x = this.x * vector2.x;
|
||||||
|
ret.y = this.y * vector2.y;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ベクトルの乗算(スカラー)
|
||||||
|
*
|
||||||
|
* @param scalar 乗算するスカラー値
|
||||||
|
* @return 乗算結果 ベクトル値
|
||||||
|
*/
|
||||||
|
public multiplyByScaler(scalar: number): CubismVector2 {
|
||||||
|
return this.multiply(new CubismVector2(scalar, scalar));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ベクトルの除算
|
||||||
|
*
|
||||||
|
* @param vector2 除算するベクトル値
|
||||||
|
* @return 除算結果 ベクトル値
|
||||||
|
*/
|
||||||
|
public division(vector2: CubismVector2): CubismVector2 {
|
||||||
|
const ret: CubismVector2 = new CubismVector2(0.0, 0.0);
|
||||||
|
ret.x = this.x / vector2.x;
|
||||||
|
ret.y = this.y / vector2.y;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ベクトルの除算(スカラー)
|
||||||
|
*
|
||||||
|
* @param scalar 除算するスカラー値
|
||||||
|
* @return 除算結果 ベクトル値
|
||||||
|
*/
|
||||||
|
public divisionByScalar(scalar: number): CubismVector2 {
|
||||||
|
return this.division(new CubismVector2(scalar, scalar));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ベクトルの長さを取得する
|
||||||
|
*
|
||||||
|
* @return ベクトルの長さ
|
||||||
|
*/
|
||||||
|
public getLength(): number {
|
||||||
|
return Math.sqrt(this.x * this.x + this.y * this.y);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ベクトルの距離の取得
|
||||||
|
*
|
||||||
|
* @param a 点
|
||||||
|
* @return ベクトルの距離
|
||||||
|
*/
|
||||||
|
public getDistanceWith(a: CubismVector2): number {
|
||||||
|
return Math.sqrt(
|
||||||
|
(this.x - a.x) * (this.x - a.x) + (this.y - a.y) * (this.y - a.y)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ドット積の計算
|
||||||
|
*
|
||||||
|
* @param a 値
|
||||||
|
* @return 結果
|
||||||
|
*/
|
||||||
|
public dot(a: CubismVector2): number {
|
||||||
|
return this.x * a.x + this.y * a.y;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 正規化の適用
|
||||||
|
*/
|
||||||
|
public normalize(): void {
|
||||||
|
const length: number = Math.pow(this.x * this.x + this.y * this.y, 0.5);
|
||||||
|
|
||||||
|
this.x = this.x / length;
|
||||||
|
this.y = this.y / length;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 等しさの確認(等しいか?)
|
||||||
|
*
|
||||||
|
* 値が等しいか?
|
||||||
|
*
|
||||||
|
* @param rhs 確認する値
|
||||||
|
* @return true 値は等しい
|
||||||
|
* @return false 値は等しくない
|
||||||
|
*/
|
||||||
|
public isEqual(rhs: CubismVector2): boolean {
|
||||||
|
return this.x == rhs.x && this.y == rhs.y;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 等しさの確認(等しくないか?)
|
||||||
|
*
|
||||||
|
* 値が等しくないか?
|
||||||
|
*
|
||||||
|
* @param rhs 確認する値
|
||||||
|
* @return true 値は等しくない
|
||||||
|
* @return false 値は等しい
|
||||||
|
*/
|
||||||
|
public isNotEqual(rhs: CubismVector2): boolean {
|
||||||
|
return !this.isEqual(rhs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Namespace definition for compatibility.
|
||||||
|
import * as $ from './cubismvector2';
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-namespace
|
||||||
|
export namespace Live2DCubismFramework {
|
||||||
|
export const CubismVector2 = $.CubismVector2;
|
||||||
|
export type CubismVector2 = $.CubismVector2;
|
||||||
|
}
|
||||||
339
avatar-h5-renderer/framework/src/math/cubismviewmatrix.ts
Normal file
339
avatar-h5-renderer/framework/src/math/cubismviewmatrix.ts
Normal file
@ -0,0 +1,339 @@
|
|||||||
|
/**
|
||||||
|
* Copyright(c) Live2D Inc. All rights reserved.
|
||||||
|
*
|
||||||
|
* Use of this source code is governed by the Live2D Open Software license
|
||||||
|
* that can be found at https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { CubismMatrix44 } from './cubismmatrix44';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* カメラの位置変更に使うと便利な4x4行列
|
||||||
|
*
|
||||||
|
* カメラの位置変更に使うと便利な4x4行列のクラス。
|
||||||
|
*/
|
||||||
|
export class CubismViewMatrix extends CubismMatrix44 {
|
||||||
|
/**
|
||||||
|
* コンストラクタ
|
||||||
|
*/
|
||||||
|
public constructor() {
|
||||||
|
super();
|
||||||
|
this._screenLeft = 0.0;
|
||||||
|
this._screenRight = 0.0;
|
||||||
|
this._screenTop = 0.0;
|
||||||
|
this._screenBottom = 0.0;
|
||||||
|
this._maxLeft = 0.0;
|
||||||
|
this._maxRight = 0.0;
|
||||||
|
this._maxTop = 0.0;
|
||||||
|
this._maxBottom = 0.0;
|
||||||
|
this._maxScale = 0.0;
|
||||||
|
this._minScale = 0.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 移動を調整
|
||||||
|
*
|
||||||
|
* @param x X軸の移動量
|
||||||
|
* @param y Y軸の移動量
|
||||||
|
*/
|
||||||
|
public adjustTranslate(x: number, y: number): void {
|
||||||
|
if (this._tr[0] * this._maxLeft + (this._tr[12] + x) > this._screenLeft) {
|
||||||
|
x = this._screenLeft - this._tr[0] * this._maxLeft - this._tr[12];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this._tr[0] * this._maxRight + (this._tr[12] + x) < this._screenRight) {
|
||||||
|
x = this._screenRight - this._tr[0] * this._maxRight - this._tr[12];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this._tr[5] * this._maxTop + (this._tr[13] + y) < this._screenTop) {
|
||||||
|
y = this._screenTop - this._tr[5] * this._maxTop - this._tr[13];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
this._tr[5] * this._maxBottom + (this._tr[13] + y) >
|
||||||
|
this._screenBottom
|
||||||
|
) {
|
||||||
|
y = this._screenBottom - this._tr[5] * this._maxBottom - this._tr[13];
|
||||||
|
}
|
||||||
|
|
||||||
|
const tr1: Float32Array = new Float32Array([
|
||||||
|
1.0,
|
||||||
|
0.0,
|
||||||
|
0.0,
|
||||||
|
0.0,
|
||||||
|
0.0,
|
||||||
|
1.0,
|
||||||
|
0.0,
|
||||||
|
0.0,
|
||||||
|
0.0,
|
||||||
|
0.0,
|
||||||
|
1.0,
|
||||||
|
0.0,
|
||||||
|
x,
|
||||||
|
y,
|
||||||
|
0.0,
|
||||||
|
1.0
|
||||||
|
]);
|
||||||
|
|
||||||
|
CubismMatrix44.multiply(tr1, this._tr, this._tr);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 拡大率を調整
|
||||||
|
*
|
||||||
|
* @param cx 拡大を行うX軸の中心位置
|
||||||
|
* @param cy 拡大を行うY軸の中心位置
|
||||||
|
* @param scale 拡大率
|
||||||
|
*/
|
||||||
|
public adjustScale(cx: number, cy: number, scale: number): void {
|
||||||
|
const maxScale: number = this.getMaxScale();
|
||||||
|
const minScale: number = this.getMinScale();
|
||||||
|
|
||||||
|
const targetScale = scale * this._tr[0];
|
||||||
|
|
||||||
|
if (targetScale < minScale) {
|
||||||
|
if (this._tr[0] > 0.0) {
|
||||||
|
scale = minScale / this._tr[0];
|
||||||
|
}
|
||||||
|
} else if (targetScale > maxScale) {
|
||||||
|
if (this._tr[0] > 0.0) {
|
||||||
|
scale = maxScale / this._tr[0];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const tr1: Float32Array = new Float32Array([
|
||||||
|
1.0,
|
||||||
|
0.0,
|
||||||
|
0.0,
|
||||||
|
0.0,
|
||||||
|
0.0,
|
||||||
|
1.0,
|
||||||
|
0.0,
|
||||||
|
0.0,
|
||||||
|
0.0,
|
||||||
|
0.0,
|
||||||
|
1.0,
|
||||||
|
0.0,
|
||||||
|
cx,
|
||||||
|
cy,
|
||||||
|
0.0,
|
||||||
|
1.0
|
||||||
|
]);
|
||||||
|
|
||||||
|
const tr2: Float32Array = new Float32Array([
|
||||||
|
scale,
|
||||||
|
0.0,
|
||||||
|
0.0,
|
||||||
|
0.0,
|
||||||
|
0.0,
|
||||||
|
scale,
|
||||||
|
0.0,
|
||||||
|
0.0,
|
||||||
|
0.0,
|
||||||
|
0.0,
|
||||||
|
1.0,
|
||||||
|
0.0,
|
||||||
|
0.0,
|
||||||
|
0.0,
|
||||||
|
0.0,
|
||||||
|
1.0
|
||||||
|
]);
|
||||||
|
|
||||||
|
const tr3: Float32Array = new Float32Array([
|
||||||
|
1.0,
|
||||||
|
0.0,
|
||||||
|
0.0,
|
||||||
|
0.0,
|
||||||
|
0.0,
|
||||||
|
1.0,
|
||||||
|
0.0,
|
||||||
|
0.0,
|
||||||
|
0.0,
|
||||||
|
0.0,
|
||||||
|
1.0,
|
||||||
|
0.0,
|
||||||
|
-cx,
|
||||||
|
-cy,
|
||||||
|
0.0,
|
||||||
|
1.0
|
||||||
|
]);
|
||||||
|
|
||||||
|
CubismMatrix44.multiply(tr3, this._tr, this._tr);
|
||||||
|
CubismMatrix44.multiply(tr2, this._tr, this._tr);
|
||||||
|
CubismMatrix44.multiply(tr1, this._tr, this._tr);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* デバイスに対応する論理座養生の範囲の設定
|
||||||
|
*
|
||||||
|
* @param left 左辺のX軸の位置
|
||||||
|
* @param right 右辺のX軸の位置
|
||||||
|
* @param bottom 下辺のY軸の位置
|
||||||
|
* @param top 上辺のY軸の位置
|
||||||
|
*/
|
||||||
|
public setScreenRect(
|
||||||
|
left: number,
|
||||||
|
right: number,
|
||||||
|
bottom: number,
|
||||||
|
top: number
|
||||||
|
): void {
|
||||||
|
this._screenLeft = left;
|
||||||
|
this._screenRight = right;
|
||||||
|
this._screenBottom = bottom;
|
||||||
|
this._screenTop = top;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* デバイスに対応する論理座標上の移動可能範囲の設定
|
||||||
|
* @param left 左辺のX軸の位置
|
||||||
|
* @param right 右辺のX軸の位置
|
||||||
|
* @param bottom 下辺のY軸の位置
|
||||||
|
* @param top 上辺のY軸の位置
|
||||||
|
*/
|
||||||
|
public setMaxScreenRect(
|
||||||
|
left: number,
|
||||||
|
right: number,
|
||||||
|
bottom: number,
|
||||||
|
top: number
|
||||||
|
): void {
|
||||||
|
this._maxLeft = left;
|
||||||
|
this._maxRight = right;
|
||||||
|
this._maxTop = top;
|
||||||
|
this._maxBottom = bottom;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 最大拡大率の設定
|
||||||
|
* @param maxScale 最大拡大率
|
||||||
|
*/
|
||||||
|
public setMaxScale(maxScale: number): void {
|
||||||
|
this._maxScale = maxScale;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 最小拡大率の設定
|
||||||
|
* @param minScale 最小拡大率
|
||||||
|
*/
|
||||||
|
public setMinScale(minScale: number): void {
|
||||||
|
this._minScale = minScale;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 最大拡大率の取得
|
||||||
|
* @return 最大拡大率
|
||||||
|
*/
|
||||||
|
public getMaxScale(): number {
|
||||||
|
return this._maxScale;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 最小拡大率の取得
|
||||||
|
* @return 最小拡大率
|
||||||
|
*/
|
||||||
|
public getMinScale(): number {
|
||||||
|
return this._minScale;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 拡大率が最大になっているかを確認する
|
||||||
|
*
|
||||||
|
* @return true 拡大率は最大
|
||||||
|
* @return false 拡大率は最大ではない
|
||||||
|
*/
|
||||||
|
public isMaxScale(): boolean {
|
||||||
|
return this.getScaleX() >= this._maxScale;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 拡大率が最小になっているかを確認する
|
||||||
|
*
|
||||||
|
* @return true 拡大率は最小
|
||||||
|
* @return false 拡大率は最小ではない
|
||||||
|
*/
|
||||||
|
public isMinScale(): boolean {
|
||||||
|
return this.getScaleX() <= this._minScale;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* デバイスに対応する論理座標の左辺のX軸位置を取得する
|
||||||
|
* @return デバイスに対応する論理座標の左辺のX軸位置
|
||||||
|
*/
|
||||||
|
public getScreenLeft(): number {
|
||||||
|
return this._screenLeft;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* デバイスに対応する論理座標の右辺のX軸位置を取得する
|
||||||
|
* @return デバイスに対応する論理座標の右辺のX軸位置
|
||||||
|
*/
|
||||||
|
public getScreenRight(): number {
|
||||||
|
return this._screenRight;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* デバイスに対応する論理座標の下辺のY軸位置を取得する
|
||||||
|
* @return デバイスに対応する論理座標の下辺のY軸位置
|
||||||
|
*/
|
||||||
|
public getScreenBottom(): number {
|
||||||
|
return this._screenBottom;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* デバイスに対応する論理座標の上辺のY軸位置を取得する
|
||||||
|
* @return デバイスに対応する論理座標の上辺のY軸位置
|
||||||
|
*/
|
||||||
|
public getScreenTop(): number {
|
||||||
|
return this._screenTop;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 左辺のX軸位置の最大値の取得
|
||||||
|
* @return 左辺のX軸位置の最大値
|
||||||
|
*/
|
||||||
|
public getMaxLeft(): number {
|
||||||
|
return this._maxLeft;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 右辺のX軸位置の最大値の取得
|
||||||
|
* @return 右辺のX軸位置の最大値
|
||||||
|
*/
|
||||||
|
public getMaxRight(): number {
|
||||||
|
return this._maxRight;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 下辺のY軸位置の最大値の取得
|
||||||
|
* @return 下辺のY軸位置の最大値
|
||||||
|
*/
|
||||||
|
public getMaxBottom(): number {
|
||||||
|
return this._maxBottom;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 上辺のY軸位置の最大値の取得
|
||||||
|
* @return 上辺のY軸位置の最大値
|
||||||
|
*/
|
||||||
|
public getMaxTop(): number {
|
||||||
|
return this._maxTop;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _screenLeft: number; // デバイスに対応する論理座標上の範囲(左辺X軸位置)
|
||||||
|
private _screenRight: number; // デバイスに対応する論理座標上の範囲(右辺X軸位置)
|
||||||
|
private _screenTop: number; // デバイスに対応する論理座標上の範囲(上辺Y軸位置)
|
||||||
|
private _screenBottom: number; // デバイスに対応する論理座標上の範囲(下辺Y軸位置)
|
||||||
|
private _maxLeft: number; // 論理座標上の移動可能範囲(左辺X軸位置)
|
||||||
|
private _maxRight: number; // 論理座標上の移動可能範囲(右辺X軸位置)
|
||||||
|
private _maxTop: number; // 論理座標上の移動可能範囲(上辺Y軸位置)
|
||||||
|
private _maxBottom: number; // 論理座標上の移動可能範囲(下辺Y軸位置)
|
||||||
|
private _maxScale: number; // 拡大率の最大値
|
||||||
|
private _minScale: number; // 拡大率の最小値
|
||||||
|
}
|
||||||
|
|
||||||
|
// Namespace definition for compatibility.
|
||||||
|
import * as $ from './cubismviewmatrix';
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-namespace
|
||||||
|
export namespace Live2DCubismFramework {
|
||||||
|
export const CubismViewMatrix = $.CubismViewMatrix;
|
||||||
|
export type CubismViewMatrix = $.CubismViewMatrix;
|
||||||
|
}
|
||||||
155
avatar-h5-renderer/framework/src/model/cubismmoc.ts
Normal file
155
avatar-h5-renderer/framework/src/model/cubismmoc.ts
Normal file
@ -0,0 +1,155 @@
|
|||||||
|
/**
|
||||||
|
* Copyright(c) Live2D Inc. All rights reserved.
|
||||||
|
*
|
||||||
|
* Use of this source code is governed by the Live2D Open Software license
|
||||||
|
* that can be found at https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { CSM_ASSERT, CubismLogError } from '../utils/cubismdebug';
|
||||||
|
import { CubismModel } from './cubismmodel';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mocデータの管理
|
||||||
|
*
|
||||||
|
* Mocデータの管理を行うクラス。
|
||||||
|
*/
|
||||||
|
export class CubismMoc {
|
||||||
|
/**
|
||||||
|
* Mocデータの作成
|
||||||
|
*/
|
||||||
|
public static create(
|
||||||
|
mocBytes: ArrayBuffer,
|
||||||
|
shouldCheckMocConsistency: boolean
|
||||||
|
): CubismMoc {
|
||||||
|
let cubismMoc: CubismMoc = null;
|
||||||
|
|
||||||
|
if (shouldCheckMocConsistency) {
|
||||||
|
// .moc3の整合性を確認
|
||||||
|
const consistency = this.hasMocConsistency(mocBytes);
|
||||||
|
|
||||||
|
if (!consistency) {
|
||||||
|
// 整合性が確認できなければ処理しない
|
||||||
|
CubismLogError(`Inconsistent MOC3.`);
|
||||||
|
return cubismMoc;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const moc: Live2DCubismCore.Moc =
|
||||||
|
Live2DCubismCore.Moc.fromArrayBuffer(mocBytes);
|
||||||
|
|
||||||
|
if (moc) {
|
||||||
|
cubismMoc = new CubismMoc(moc);
|
||||||
|
cubismMoc._mocVersion =
|
||||||
|
Live2DCubismCore.Version.csmGetMocVersion(mocBytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
return cubismMoc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mocデータを削除
|
||||||
|
*
|
||||||
|
* Mocデータを削除する
|
||||||
|
*/
|
||||||
|
public static delete(moc: CubismMoc): void {
|
||||||
|
moc._moc._release();
|
||||||
|
moc._moc = null;
|
||||||
|
moc = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* モデルを作成する
|
||||||
|
*
|
||||||
|
* @return Mocデータから作成されたモデル
|
||||||
|
*/
|
||||||
|
createModel(): CubismModel {
|
||||||
|
let cubismModel: CubismModel = null;
|
||||||
|
|
||||||
|
const model: Live2DCubismCore.Model = Live2DCubismCore.Model.fromMoc(
|
||||||
|
this._moc
|
||||||
|
);
|
||||||
|
|
||||||
|
if (model) {
|
||||||
|
cubismModel = new CubismModel(model);
|
||||||
|
cubismModel.initialize();
|
||||||
|
|
||||||
|
++this._modelCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
return cubismModel;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* モデルを削除する
|
||||||
|
*/
|
||||||
|
deleteModel(model: CubismModel): void {
|
||||||
|
if (model != null) {
|
||||||
|
model.release();
|
||||||
|
model = null;
|
||||||
|
--this._modelCount;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* コンストラクタ
|
||||||
|
*/
|
||||||
|
private constructor(moc: Live2DCubismCore.Moc) {
|
||||||
|
this._moc = moc;
|
||||||
|
this._modelCount = 0;
|
||||||
|
this._mocVersion = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* デストラクタ相当の処理
|
||||||
|
*/
|
||||||
|
public release(): void {
|
||||||
|
CSM_ASSERT(this._modelCount == 0);
|
||||||
|
|
||||||
|
this._moc._release();
|
||||||
|
this._moc = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 最新の.moc3 Versionを取得
|
||||||
|
*/
|
||||||
|
public getLatestMocVersion(): number {
|
||||||
|
return Live2DCubismCore.Version.csmGetLatestMocVersion();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 読み込んだモデルの.moc3 Versionを取得
|
||||||
|
*/
|
||||||
|
public getMocVersion(): number {
|
||||||
|
return this._mocVersion;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mocファイルのbufferから.moc3 Versionを取得
|
||||||
|
* @param mocBytes Mocファイルのバイト配列
|
||||||
|
* @returns .moc3 Version番号
|
||||||
|
*/
|
||||||
|
public static getMocVersionFromBuffer(mocBytes: ArrayBuffer): number {
|
||||||
|
return Live2DCubismCore.Version.csmGetMocVersion(mocBytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* .moc3 の整合性を検証する
|
||||||
|
*/
|
||||||
|
public static hasMocConsistency(mocBytes: ArrayBuffer): boolean {
|
||||||
|
const isConsistent =
|
||||||
|
Live2DCubismCore.Moc.prototype.hasMocConsistency(mocBytes);
|
||||||
|
return isConsistent === 1 ? true : false;
|
||||||
|
}
|
||||||
|
|
||||||
|
_moc: Live2DCubismCore.Moc; // Mocデータ
|
||||||
|
_modelCount: number; // Mocデータから作られたモデルの個数
|
||||||
|
_mocVersion: number; // 読み込んだモデルの.moc3 Version
|
||||||
|
}
|
||||||
|
|
||||||
|
// Namespace definition for compatibility.
|
||||||
|
import * as $ from './cubismmoc';
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-namespace
|
||||||
|
export namespace Live2DCubismFramework {
|
||||||
|
export const CubismMoc = $.CubismMoc;
|
||||||
|
export type CubismMoc = $.CubismMoc;
|
||||||
|
}
|
||||||
1930
avatar-h5-renderer/framework/src/model/cubismmodel.ts
Normal file
1930
avatar-h5-renderer/framework/src/model/cubismmodel.ts
Normal file
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
141
avatar-h5-renderer/framework/src/model/cubismmodeluserdata.ts
Normal file
141
avatar-h5-renderer/framework/src/model/cubismmodeluserdata.ts
Normal file
@ -0,0 +1,141 @@
|
|||||||
|
/**
|
||||||
|
* Copyright(c) Live2D Inc. All rights reserved.
|
||||||
|
*
|
||||||
|
* Use of this source code is governed by the Live2D Open Software license
|
||||||
|
* that can be found at https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { CubismIdHandle } from '../id/cubismid';
|
||||||
|
import { CubismFramework } from '../live2dcubismframework';
|
||||||
|
import { CubismModelUserDataJson } from './cubismmodeluserdatajson';
|
||||||
|
|
||||||
|
const ArtMesh = 'ArtMesh';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ユーザーデータインターフェース
|
||||||
|
*
|
||||||
|
* Jsonから読み込んだユーザーデータを記録しておくための構造体
|
||||||
|
*/
|
||||||
|
export class CubismModelUserDataNode {
|
||||||
|
targetType: CubismIdHandle; // ユーザーデータターゲットタイプ
|
||||||
|
targetId: CubismIdHandle; // ユーザーデータターゲットのID
|
||||||
|
value: string; // ユーザーデータ
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ユーザデータの管理クラス
|
||||||
|
*
|
||||||
|
* ユーザデータをロード、管理、検索インターフェイス、解放までを行う。
|
||||||
|
*/
|
||||||
|
export class CubismModelUserData {
|
||||||
|
/**
|
||||||
|
* インスタンスの作成
|
||||||
|
*
|
||||||
|
* @param buffer userdata3.jsonが読み込まれているバッファ
|
||||||
|
* @param size バッファのサイズ
|
||||||
|
* @return 作成されたインスタンス
|
||||||
|
*/
|
||||||
|
public static create(buffer: ArrayBuffer, size: number): CubismModelUserData {
|
||||||
|
const ret: CubismModelUserData = new CubismModelUserData();
|
||||||
|
|
||||||
|
ret.parseUserData(buffer, size);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* インスタンスを破棄する
|
||||||
|
*
|
||||||
|
* @param modelUserData 破棄するインスタンス
|
||||||
|
*/
|
||||||
|
public static delete(modelUserData: CubismModelUserData): void {
|
||||||
|
if (modelUserData != null) {
|
||||||
|
modelUserData.release();
|
||||||
|
modelUserData = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ArtMeshのユーザーデータのリストの取得
|
||||||
|
*
|
||||||
|
* @return ユーザーデータリスト
|
||||||
|
*/
|
||||||
|
public getArtMeshUserDatas(): Array<CubismModelUserDataNode> {
|
||||||
|
return this._artMeshUserDataNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* userdata3.jsonのパース
|
||||||
|
*
|
||||||
|
* @param buffer userdata3.jsonが読み込まれているバッファ
|
||||||
|
* @param size バッファのサイズ
|
||||||
|
*/
|
||||||
|
public parseUserData(buffer: ArrayBuffer, size: number): void {
|
||||||
|
let json: CubismModelUserDataJson = new CubismModelUserDataJson(
|
||||||
|
buffer,
|
||||||
|
size
|
||||||
|
);
|
||||||
|
if (!json) {
|
||||||
|
json.release();
|
||||||
|
json = void 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const typeOfArtMesh = CubismFramework.getIdManager().getId(ArtMesh);
|
||||||
|
const nodeCount: number = json.getUserDataCount();
|
||||||
|
|
||||||
|
let dstIndex = this._userDataNodes.length;
|
||||||
|
this._userDataNodes.length = nodeCount;
|
||||||
|
for (let i = 0; i < nodeCount; i++) {
|
||||||
|
const addNode: CubismModelUserDataNode = new CubismModelUserDataNode();
|
||||||
|
|
||||||
|
addNode.targetId = json.getUserDataId(i);
|
||||||
|
addNode.targetType = CubismFramework.getIdManager().getId(
|
||||||
|
json.getUserDataTargetType(i)
|
||||||
|
);
|
||||||
|
addNode.value = json.getUserDataValue(i);
|
||||||
|
this._userDataNodes[dstIndex++] = addNode;
|
||||||
|
|
||||||
|
if (addNode.targetType == typeOfArtMesh) {
|
||||||
|
this._artMeshUserDataNode.push(addNode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
json.release();
|
||||||
|
json = void 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* コンストラクタ
|
||||||
|
*/
|
||||||
|
public constructor() {
|
||||||
|
this._userDataNodes = new Array<CubismModelUserDataNode>();
|
||||||
|
this._artMeshUserDataNode = new Array<CubismModelUserDataNode>();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* デストラクタ相当の処理
|
||||||
|
*
|
||||||
|
* ユーザーデータ構造体配列を解放する
|
||||||
|
*/
|
||||||
|
public release(): void {
|
||||||
|
for (let i = 0; i < this._userDataNodes.length; ++i) {
|
||||||
|
this._userDataNodes[i] = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._userDataNodes = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _userDataNodes: Array<CubismModelUserDataNode>; // ユーザーデータ構造体配列
|
||||||
|
private _artMeshUserDataNode: Array<CubismModelUserDataNode>; // 閲覧リストの保持
|
||||||
|
}
|
||||||
|
|
||||||
|
// Namespace definition for compatibility.
|
||||||
|
import * as $ from './cubismmodeluserdata';
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-namespace
|
||||||
|
export namespace Live2DCubismFramework {
|
||||||
|
export const CubismModelUserData = $.CubismModelUserData;
|
||||||
|
export type CubismModelUserData = $.CubismModelUserData;
|
||||||
|
export const CubismModelUserDataNode = $.CubismModelUserDataNode;
|
||||||
|
export type CubismModelUserDataNode = $.CubismModelUserDataNode;
|
||||||
|
}
|
||||||
@ -0,0 +1,117 @@
|
|||||||
|
/**
|
||||||
|
* Copyright(c) Live2D Inc. All rights reserved.
|
||||||
|
*
|
||||||
|
* Use of this source code is governed by the Live2D Open Software license
|
||||||
|
* that can be found at https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { CubismIdHandle } from '../id/cubismid';
|
||||||
|
import { CubismFramework } from '../live2dcubismframework';
|
||||||
|
import { CubismJson } from '../utils/cubismjson';
|
||||||
|
|
||||||
|
const Meta = 'Meta';
|
||||||
|
const UserDataCount = 'UserDataCount';
|
||||||
|
const TotalUserDataSize = 'TotalUserDataSize';
|
||||||
|
const UserData = 'UserData';
|
||||||
|
const Target = 'Target';
|
||||||
|
const Id = 'Id';
|
||||||
|
const Value = 'Value';
|
||||||
|
|
||||||
|
export class CubismModelUserDataJson {
|
||||||
|
/**
|
||||||
|
* コンストラクタ
|
||||||
|
* @param buffer userdata3.jsonが読み込まれているバッファ
|
||||||
|
* @param size バッファのサイズ
|
||||||
|
*/
|
||||||
|
public constructor(buffer: ArrayBuffer, size: number) {
|
||||||
|
this._json = CubismJson.create(buffer, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* デストラクタ相当の処理
|
||||||
|
*/
|
||||||
|
public release(): void {
|
||||||
|
CubismJson.delete(this._json);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ユーザーデータ個数の取得
|
||||||
|
* @return ユーザーデータの個数
|
||||||
|
*/
|
||||||
|
public getUserDataCount(): number {
|
||||||
|
return this._json
|
||||||
|
.getRoot()
|
||||||
|
.getValueByString(Meta)
|
||||||
|
.getValueByString(UserDataCount)
|
||||||
|
.toInt();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ユーザーデータ総文字列数の取得
|
||||||
|
*
|
||||||
|
* @return ユーザーデータ総文字列数
|
||||||
|
*/
|
||||||
|
public getTotalUserDataSize(): number {
|
||||||
|
return this._json
|
||||||
|
.getRoot()
|
||||||
|
.getValueByString(Meta)
|
||||||
|
.getValueByString(TotalUserDataSize)
|
||||||
|
.toInt();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ユーザーデータのタイプの取得
|
||||||
|
*
|
||||||
|
* @return ユーザーデータのタイプ
|
||||||
|
*/
|
||||||
|
public getUserDataTargetType(i: number): string {
|
||||||
|
return this._json
|
||||||
|
.getRoot()
|
||||||
|
.getValueByString(UserData)
|
||||||
|
.getValueByIndex(i)
|
||||||
|
.getValueByString(Target)
|
||||||
|
.getRawString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ユーザーデータのターゲットIDの取得
|
||||||
|
*
|
||||||
|
* @param i インデックス
|
||||||
|
* @return ユーザーデータターゲットID
|
||||||
|
*/
|
||||||
|
public getUserDataId(i: number): CubismIdHandle {
|
||||||
|
return CubismFramework.getIdManager().getId(
|
||||||
|
this._json
|
||||||
|
.getRoot()
|
||||||
|
.getValueByString(UserData)
|
||||||
|
.getValueByIndex(i)
|
||||||
|
.getValueByString(Id)
|
||||||
|
.getRawString()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ユーザーデータの文字列の取得
|
||||||
|
*
|
||||||
|
* @param i インデックス
|
||||||
|
* @return ユーザーデータ
|
||||||
|
*/
|
||||||
|
public getUserDataValue(i: number): string {
|
||||||
|
return this._json
|
||||||
|
.getRoot()
|
||||||
|
.getValueByString(UserData)
|
||||||
|
.getValueByIndex(i)
|
||||||
|
.getValueByString(Value)
|
||||||
|
.getRawString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private _json: CubismJson;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Namespace definition for compatibility.
|
||||||
|
import * as $ from './cubismmodeluserdatajson';
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-namespace
|
||||||
|
export namespace Live2DCubismFramework {
|
||||||
|
export const CubismModelUserDataJson = $.CubismModelUserDataJson;
|
||||||
|
export type CubismModelUserDataJson = $.CubismModelUserDataJson;
|
||||||
|
}
|
||||||
521
avatar-h5-renderer/framework/src/model/cubismusermodel.ts
Normal file
521
avatar-h5-renderer/framework/src/model/cubismusermodel.ts
Normal file
@ -0,0 +1,521 @@
|
|||||||
|
/**
|
||||||
|
* Copyright(c) Live2D Inc. All rights reserved.
|
||||||
|
*
|
||||||
|
* Use of this source code is governed by the Live2D Open Software license
|
||||||
|
* that can be found at https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { CubismBreath } from '../effect/cubismbreath';
|
||||||
|
import { CubismEyeBlink } from '../effect/cubismeyeblink';
|
||||||
|
import { CubismPose } from '../effect/cubismpose';
|
||||||
|
import { ICubismModelSetting } from '../icubismmodelsetting';
|
||||||
|
import { CubismIdHandle } from '../id/cubismid';
|
||||||
|
import { Constant } from '../live2dcubismframework';
|
||||||
|
import { CubismModelMatrix } from '../math/cubismmodelmatrix';
|
||||||
|
import { CubismTargetPoint } from '../math/cubismtargetpoint';
|
||||||
|
import {
|
||||||
|
ACubismMotion,
|
||||||
|
BeganMotionCallback,
|
||||||
|
FinishedMotionCallback
|
||||||
|
} from '../motion/acubismmotion';
|
||||||
|
import { CubismExpressionMotion } from '../motion/cubismexpressionmotion';
|
||||||
|
import { CubismExpressionMotionManager } from '../motion/cubismexpressionmotionmanager';
|
||||||
|
import { CubismMotion } from '../motion/cubismmotion';
|
||||||
|
import { CubismMotionManager } from '../motion/cubismmotionmanager';
|
||||||
|
import { CubismMotionQueueManager } from '../motion/cubismmotionqueuemanager';
|
||||||
|
import { CubismPhysics } from '../physics/cubismphysics';
|
||||||
|
import { CubismRenderer_WebGL } from '../rendering/cubismrenderer_webgl';
|
||||||
|
import { CubismLogError, CubismLogInfo } from '../utils/cubismdebug';
|
||||||
|
import { CubismMoc } from './cubismmoc';
|
||||||
|
import { CubismModel } from './cubismmodel';
|
||||||
|
import { CubismModelUserData } from './cubismmodeluserdata';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ユーザーが実際に使用するモデル
|
||||||
|
*
|
||||||
|
* ユーザーが実際に使用するモデルの基底クラス。これを継承してユーザーが実装する。
|
||||||
|
*/
|
||||||
|
export class CubismUserModel {
|
||||||
|
/**
|
||||||
|
* 初期化状態の取得
|
||||||
|
*
|
||||||
|
* 初期化されている状態か?
|
||||||
|
*
|
||||||
|
* @return true 初期化されている
|
||||||
|
* @return false 初期化されていない
|
||||||
|
*/
|
||||||
|
public isInitialized(): boolean {
|
||||||
|
return this._initialized;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 初期化状態の設定
|
||||||
|
*
|
||||||
|
* 初期化状態を設定する。
|
||||||
|
*
|
||||||
|
* @param v 初期化状態
|
||||||
|
*/
|
||||||
|
public setInitialized(v: boolean): void {
|
||||||
|
this._initialized = v;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新状態の取得
|
||||||
|
*
|
||||||
|
* 更新されている状態か?
|
||||||
|
*
|
||||||
|
* @return true 更新されている
|
||||||
|
* @return false 更新されていない
|
||||||
|
*/
|
||||||
|
public isUpdating(): boolean {
|
||||||
|
return this._updating;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新状態の設定
|
||||||
|
*
|
||||||
|
* 更新状態を設定する
|
||||||
|
*
|
||||||
|
* @param v 更新状態
|
||||||
|
*/
|
||||||
|
public setUpdating(v: boolean): void {
|
||||||
|
this._updating = v;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* マウスドラッグ情報の設定
|
||||||
|
*
|
||||||
|
* @param ドラッグしているカーソルのX位置
|
||||||
|
* @param ドラッグしているカーソルのY位置
|
||||||
|
*/
|
||||||
|
public setDragging(x: number, y: number): void {
|
||||||
|
this._dragManager.set(x, y);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* モデル行列を取得する
|
||||||
|
* @return モデル行列
|
||||||
|
*/
|
||||||
|
public getModelMatrix(): CubismModelMatrix {
|
||||||
|
return this._modelMatrix;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* モデルを描画したバッファを設定する
|
||||||
|
*
|
||||||
|
* @param width モデルを描画したバッファの幅
|
||||||
|
* @param height モデルを描画したバッファの高さ
|
||||||
|
*/
|
||||||
|
public setRenderTargetSize(width: number, height: number): void {
|
||||||
|
if (this._renderer) {
|
||||||
|
this._renderer.setRenderTargetSize(width, height);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 不透明度の設定
|
||||||
|
*
|
||||||
|
* @param a 不透明度
|
||||||
|
*/
|
||||||
|
public setOpacity(a: number): void {
|
||||||
|
this._opacity = a;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 不透明度の取得
|
||||||
|
*
|
||||||
|
* @return 不透明度
|
||||||
|
*/
|
||||||
|
public getOpacity(): number {
|
||||||
|
return this._opacity;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* モデルデータを読み込む
|
||||||
|
*
|
||||||
|
* @param buffer moc3ファイルが読み込まれているバッファ
|
||||||
|
*/
|
||||||
|
public loadModel(buffer: ArrayBuffer, shouldCheckMocConsistency = false) {
|
||||||
|
this._moc = CubismMoc.create(buffer, shouldCheckMocConsistency);
|
||||||
|
|
||||||
|
if (this._moc == null) {
|
||||||
|
CubismLogError('Failed to CubismMoc.create().');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._model = this._moc.createModel();
|
||||||
|
|
||||||
|
if (this._model == null) {
|
||||||
|
CubismLogError('Failed to CreateModel().');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._model.saveParameters();
|
||||||
|
this._modelMatrix = new CubismModelMatrix(
|
||||||
|
this._model.getCanvasWidth(),
|
||||||
|
this._model.getCanvasHeight()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* モーションデータを読み込む
|
||||||
|
* @param buffer motion3.jsonファイルが読み込まれているバッファ
|
||||||
|
* @param size バッファのサイズ
|
||||||
|
* @param name モーションの名前
|
||||||
|
* @param onFinishedMotionHandler モーション再生終了時に呼び出されるコールバック関数
|
||||||
|
* @param onBeganMotionHandler モーション再生開始時に呼び出されるコールバック関数
|
||||||
|
* @param modelSetting モデル設定
|
||||||
|
* @param group モーショングループ名
|
||||||
|
* @param index モーションインデックス
|
||||||
|
* @param shouldCheckMotionConsistency motion3.json整合性チェックするかどうか
|
||||||
|
* @return モーションクラス
|
||||||
|
*/
|
||||||
|
public loadMotion(
|
||||||
|
buffer: ArrayBuffer,
|
||||||
|
size: number,
|
||||||
|
name: string,
|
||||||
|
onFinishedMotionHandler?: FinishedMotionCallback,
|
||||||
|
onBeganMotionHandler?: BeganMotionCallback,
|
||||||
|
modelSetting?: ICubismModelSetting,
|
||||||
|
group?: string,
|
||||||
|
index?: number,
|
||||||
|
shouldCheckMotionConsistency: boolean = false
|
||||||
|
): CubismMotion {
|
||||||
|
if (buffer == null || size == 0) {
|
||||||
|
CubismLogError('Failed to loadMotion().');
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const motion: CubismMotion = CubismMotion.create(
|
||||||
|
buffer,
|
||||||
|
size,
|
||||||
|
onFinishedMotionHandler,
|
||||||
|
onBeganMotionHandler,
|
||||||
|
shouldCheckMotionConsistency
|
||||||
|
);
|
||||||
|
|
||||||
|
if (motion == null) {
|
||||||
|
CubismLogError(`Failed to create motion from buffer in LoadMotion()`);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 必要であればモーションフェード値を上書き
|
||||||
|
if (modelSetting) {
|
||||||
|
const fadeInTime: number = modelSetting.getMotionFadeInTimeValue(
|
||||||
|
group,
|
||||||
|
index
|
||||||
|
);
|
||||||
|
if (fadeInTime >= 0.0) {
|
||||||
|
motion.setFadeInTime(fadeInTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
const fadeOutTime = modelSetting.getMotionFadeOutTimeValue(group, index);
|
||||||
|
if (fadeOutTime >= 0.0) {
|
||||||
|
motion.setFadeOutTime(fadeOutTime);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return motion;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 表情データの読み込み
|
||||||
|
* @param buffer expファイルが読み込まれているバッファ
|
||||||
|
* @param size バッファのサイズ
|
||||||
|
* @param name 表情の名前
|
||||||
|
*/
|
||||||
|
public loadExpression(
|
||||||
|
buffer: ArrayBuffer,
|
||||||
|
size: number,
|
||||||
|
name: string
|
||||||
|
): ACubismMotion {
|
||||||
|
if (buffer == null || size == 0) {
|
||||||
|
CubismLogError('Failed to loadExpression().');
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return CubismExpressionMotion.create(buffer, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ポーズデータの読み込み
|
||||||
|
* @param buffer pose3.jsonが読み込まれているバッファ
|
||||||
|
* @param size バッファのサイズ
|
||||||
|
*/
|
||||||
|
public loadPose(buffer: ArrayBuffer, size: number): void {
|
||||||
|
if (buffer == null || size == 0) {
|
||||||
|
CubismLogError('Failed to loadPose().');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this._pose = CubismPose.create(buffer, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* モデルに付属するユーザーデータを読み込む
|
||||||
|
* @param buffer userdata3.jsonが読み込まれているバッファ
|
||||||
|
* @param size バッファのサイズ
|
||||||
|
*/
|
||||||
|
public loadUserData(buffer: ArrayBuffer, size: number): void {
|
||||||
|
if (buffer == null || size == 0) {
|
||||||
|
CubismLogError('Failed to loadUserData().');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this._modelUserData = CubismModelUserData.create(buffer, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 物理演算データの読み込み
|
||||||
|
* @param buffer physics3.jsonが読み込まれているバッファ
|
||||||
|
* @param size バッファのサイズ
|
||||||
|
*/
|
||||||
|
public loadPhysics(buffer: ArrayBuffer, size: number): void {
|
||||||
|
if (buffer == null || size == 0) {
|
||||||
|
CubismLogError('Failed to loadPhysics().');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this._physics = CubismPhysics.create(buffer, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 当たり判定の取得
|
||||||
|
* @param drawableId 検証したいDrawableのID
|
||||||
|
* @param pointX X位置
|
||||||
|
* @param pointY Y位置
|
||||||
|
* @return true ヒットしている
|
||||||
|
* @return false ヒットしていない
|
||||||
|
*/
|
||||||
|
public isHit(
|
||||||
|
drawableId: CubismIdHandle,
|
||||||
|
pointX: number,
|
||||||
|
pointY: number
|
||||||
|
): boolean {
|
||||||
|
const drawIndex: number = this._model.getDrawableIndex(drawableId);
|
||||||
|
|
||||||
|
if (drawIndex < 0) {
|
||||||
|
return false; // 存在しない場合はfalse
|
||||||
|
}
|
||||||
|
|
||||||
|
const count: number = this._model.getDrawableVertexCount(drawIndex);
|
||||||
|
const vertices: Float32Array = this._model.getDrawableVertices(drawIndex);
|
||||||
|
|
||||||
|
let left: number = vertices[0];
|
||||||
|
let right: number = vertices[0];
|
||||||
|
let top: number = vertices[1];
|
||||||
|
let bottom: number = vertices[1];
|
||||||
|
|
||||||
|
for (let j = 1; j < count; ++j) {
|
||||||
|
const x = vertices[Constant.vertexOffset + j * Constant.vertexStep];
|
||||||
|
const y = vertices[Constant.vertexOffset + j * Constant.vertexStep + 1];
|
||||||
|
|
||||||
|
if (x < left) {
|
||||||
|
left = x; // Min x
|
||||||
|
}
|
||||||
|
|
||||||
|
if (x > right) {
|
||||||
|
right = x; // Max x
|
||||||
|
}
|
||||||
|
|
||||||
|
if (y < top) {
|
||||||
|
top = y; // Min y
|
||||||
|
}
|
||||||
|
|
||||||
|
if (y > bottom) {
|
||||||
|
bottom = y; // Max y
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const tx: number = this._modelMatrix.invertTransformX(pointX);
|
||||||
|
const ty: number = this._modelMatrix.invertTransformY(pointY);
|
||||||
|
|
||||||
|
return left <= tx && tx <= right && top <= ty && ty <= bottom;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* モデルの取得
|
||||||
|
* @return モデル
|
||||||
|
*/
|
||||||
|
public getModel(): CubismModel {
|
||||||
|
return this._model;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 読み込めないMocファイルの.moc3 Versionを取得
|
||||||
|
* @param mocBytes 読み込めないMocファイルのバイト配列
|
||||||
|
* @returns .moc3 Version番号
|
||||||
|
*/
|
||||||
|
public getMocVersionFromBuffer(mocBytes: ArrayBuffer): number {
|
||||||
|
return CubismMoc.getMocVersionFromBuffer(mocBytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* レンダラの取得
|
||||||
|
* @return レンダラ
|
||||||
|
*/
|
||||||
|
public getRenderer(): CubismRenderer_WebGL {
|
||||||
|
return this._renderer;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* レンダラを作成して初期化を実行する
|
||||||
|
* @param width レンダリングする幅
|
||||||
|
* @param height レンダリングする高さ
|
||||||
|
* @param maskBufferCount バッファの生成数
|
||||||
|
*/
|
||||||
|
public createRenderer(
|
||||||
|
width: number,
|
||||||
|
height: number,
|
||||||
|
maskBufferCount = 1
|
||||||
|
): void {
|
||||||
|
if (this._renderer) {
|
||||||
|
this.deleteRenderer();
|
||||||
|
}
|
||||||
|
|
||||||
|
this._renderer = new CubismRenderer_WebGL(width, height);
|
||||||
|
this._renderer.initialize(this._model, maskBufferCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* レンダラの解放
|
||||||
|
*/
|
||||||
|
public deleteRenderer(): void {
|
||||||
|
if (this._renderer != null) {
|
||||||
|
this._renderer.release();
|
||||||
|
this._renderer = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* イベント発火時の標準処理
|
||||||
|
*
|
||||||
|
* Eventが再生処理時にあった場合の処理をする。
|
||||||
|
* 継承で上書きすることを想定している。
|
||||||
|
* 上書きしない場合はログ出力をする。
|
||||||
|
*
|
||||||
|
* @param eventValue 発火したイベントの文字列データ
|
||||||
|
*/
|
||||||
|
public motionEventFired(eventValue: string): void {
|
||||||
|
CubismLogInfo('{0}', eventValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* イベント用のコールバック
|
||||||
|
*
|
||||||
|
* CubismMotionQueueManagerにイベント用に登録するためのCallback。
|
||||||
|
* CubismUserModelの継承先のEventFiredを呼ぶ。
|
||||||
|
*
|
||||||
|
* @param caller 発火したイベントを管理していたモーションマネージャー、比較用
|
||||||
|
* @param eventValue 発火したイベントの文字列データ
|
||||||
|
* @param customData CubismUserModelを継承したインスタンスを想定
|
||||||
|
*/
|
||||||
|
public static cubismDefaultMotionEventCallback(
|
||||||
|
caller: CubismMotionQueueManager,
|
||||||
|
eventValue: string,
|
||||||
|
customData: CubismUserModel
|
||||||
|
): void {
|
||||||
|
const model: CubismUserModel = customData;
|
||||||
|
|
||||||
|
if (model != null) {
|
||||||
|
model.motionEventFired(eventValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* コンストラクタ
|
||||||
|
*/
|
||||||
|
public constructor() {
|
||||||
|
// 各変数初期化
|
||||||
|
this._moc = null;
|
||||||
|
this._model = null;
|
||||||
|
this._motionManager = null;
|
||||||
|
this._expressionManager = null;
|
||||||
|
this._eyeBlink = null;
|
||||||
|
this._breath = null;
|
||||||
|
this._modelMatrix = null;
|
||||||
|
this._pose = null;
|
||||||
|
this._dragManager = null;
|
||||||
|
this._physics = null;
|
||||||
|
this._modelUserData = null;
|
||||||
|
this._initialized = false;
|
||||||
|
this._updating = false;
|
||||||
|
this._opacity = 1.0;
|
||||||
|
this._mocConsistency = false;
|
||||||
|
this._debugMode = false;
|
||||||
|
this._renderer = null;
|
||||||
|
|
||||||
|
// モーションマネージャーを作成
|
||||||
|
this._motionManager = new CubismMotionManager();
|
||||||
|
this._motionManager.setEventCallback(
|
||||||
|
CubismUserModel.cubismDefaultMotionEventCallback,
|
||||||
|
this
|
||||||
|
);
|
||||||
|
|
||||||
|
// 表情マネージャーを作成
|
||||||
|
this._expressionManager = new CubismExpressionMotionManager();
|
||||||
|
|
||||||
|
// ドラッグによるアニメーション
|
||||||
|
this._dragManager = new CubismTargetPoint();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* デストラクタに相当する処理
|
||||||
|
*/
|
||||||
|
public release() {
|
||||||
|
if (this._motionManager != null) {
|
||||||
|
this._motionManager.release();
|
||||||
|
this._motionManager = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this._expressionManager != null) {
|
||||||
|
this._expressionManager.release();
|
||||||
|
this._expressionManager = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this._moc != null) {
|
||||||
|
this._moc.deleteModel(this._model);
|
||||||
|
this._moc.release();
|
||||||
|
this._moc = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._modelMatrix = null;
|
||||||
|
|
||||||
|
CubismPose.delete(this._pose);
|
||||||
|
CubismEyeBlink.delete(this._eyeBlink);
|
||||||
|
CubismBreath.delete(this._breath);
|
||||||
|
|
||||||
|
this._dragManager = null;
|
||||||
|
|
||||||
|
CubismPhysics.delete(this._physics);
|
||||||
|
CubismModelUserData.delete(this._modelUserData);
|
||||||
|
|
||||||
|
this.deleteRenderer();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected _moc: CubismMoc; // Mocデータ
|
||||||
|
protected _model: CubismModel; // Modelインスタンス
|
||||||
|
|
||||||
|
protected _motionManager: CubismMotionManager; // モーション管理
|
||||||
|
protected _expressionManager: CubismExpressionMotionManager; // 表情管理
|
||||||
|
protected _eyeBlink: CubismEyeBlink; // 自動まばたき
|
||||||
|
protected _breath: CubismBreath; // 呼吸
|
||||||
|
protected _modelMatrix: CubismModelMatrix; // モデル行列
|
||||||
|
protected _pose: CubismPose; // ポーズ管理
|
||||||
|
protected _dragManager: CubismTargetPoint; // マウスドラッグ
|
||||||
|
protected _physics: CubismPhysics; // 物理演算
|
||||||
|
protected _modelUserData: CubismModelUserData; // ユーザーデータ
|
||||||
|
|
||||||
|
protected _initialized: boolean; // 初期化されたかどうか
|
||||||
|
protected _updating: boolean; // 更新されたかどうか
|
||||||
|
protected _opacity: number; // 不透明度
|
||||||
|
protected _mocConsistency: boolean; // MOC3整合性検証するかどうか
|
||||||
|
protected _motionConsistency: boolean; // motion3.json整合性検証するかどうか
|
||||||
|
protected _debugMode: boolean; // デバッグモードかどうか
|
||||||
|
|
||||||
|
private _renderer: CubismRenderer_WebGL; // レンダラ
|
||||||
|
}
|
||||||
|
|
||||||
|
// Namespace definition for compatibility.
|
||||||
|
import * as $ from './cubismusermodel';
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-namespace
|
||||||
|
export namespace Live2DCubismFramework {
|
||||||
|
export const CubismUserModel = $.CubismUserModel;
|
||||||
|
export type CubismUserModel = $.CubismUserModel;
|
||||||
|
}
|
||||||
443
avatar-h5-renderer/framework/src/motion/acubismmotion.ts
Normal file
443
avatar-h5-renderer/framework/src/motion/acubismmotion.ts
Normal file
@ -0,0 +1,443 @@
|
|||||||
|
/**
|
||||||
|
* Copyright(c) Live2D Inc. All rights reserved.
|
||||||
|
*
|
||||||
|
* Use of this source code is governed by the Live2D Open Software license
|
||||||
|
* that can be found at https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { CubismMath } from '../math/cubismmath';
|
||||||
|
import { CubismModel } from '../model/cubismmodel';
|
||||||
|
import { CSM_ASSERT, CubismDebug } from '../utils/cubismdebug';
|
||||||
|
import { CubismMotionQueueEntry } from './cubismmotionqueueentry';
|
||||||
|
|
||||||
|
/** モーション再生開始コールバック関数定義 */
|
||||||
|
export type BeganMotionCallback = (self: ACubismMotion) => void;
|
||||||
|
|
||||||
|
/** モーション再生終了コールバック関数定義 */
|
||||||
|
export type FinishedMotionCallback = (self: ACubismMotion) => void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* モーションの抽象基底クラス
|
||||||
|
*
|
||||||
|
* モーションの抽象基底クラス。MotionQueueManagerによってモーションの再生を管理する。
|
||||||
|
*/
|
||||||
|
export abstract class ACubismMotion {
|
||||||
|
/**
|
||||||
|
* インスタンスの破棄
|
||||||
|
*/
|
||||||
|
public static delete(motion: ACubismMotion): void {
|
||||||
|
motion.release();
|
||||||
|
motion = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* コンストラクタ
|
||||||
|
*/
|
||||||
|
public constructor() {
|
||||||
|
this._fadeInSeconds = -1.0;
|
||||||
|
this._fadeOutSeconds = -1.0;
|
||||||
|
this._weight = 1.0;
|
||||||
|
this._offsetSeconds = 0.0; // 再生の開始時刻
|
||||||
|
this._isLoop = false; // ループするか
|
||||||
|
this._isLoopFadeIn = true; // ループ時にフェードインが有効かどうかのフラグ。初期値では有効。
|
||||||
|
this._previousLoopState = this._isLoop;
|
||||||
|
this._firedEventValues = new Array<string>();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* デストラクタ相当の処理
|
||||||
|
*/
|
||||||
|
public release(): void {
|
||||||
|
this._weight = 0.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* モデルのパラメータ
|
||||||
|
* @param model 対象のモデル
|
||||||
|
* @param motionQueueEntry CubismMotionQueueManagerで管理されているモーション
|
||||||
|
* @param userTimeSeconds デルタ時間の積算値[秒]
|
||||||
|
*/
|
||||||
|
public updateParameters(
|
||||||
|
model: CubismModel,
|
||||||
|
motionQueueEntry: CubismMotionQueueEntry,
|
||||||
|
userTimeSeconds: number
|
||||||
|
): void {
|
||||||
|
if (!motionQueueEntry.isAvailable() || motionQueueEntry.isFinished()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.setupMotionQueueEntry(motionQueueEntry, userTimeSeconds);
|
||||||
|
|
||||||
|
const fadeWeight = this.updateFadeWeight(motionQueueEntry, userTimeSeconds);
|
||||||
|
|
||||||
|
//---- 全てのパラメータIDをループする ----
|
||||||
|
this.doUpdateParameters(
|
||||||
|
model,
|
||||||
|
userTimeSeconds,
|
||||||
|
fadeWeight,
|
||||||
|
motionQueueEntry
|
||||||
|
);
|
||||||
|
|
||||||
|
// 後処理
|
||||||
|
// 終了時刻を過ぎたら終了フラグを立てる(CubismMotionQueueManager)
|
||||||
|
if (
|
||||||
|
motionQueueEntry.getEndTime() > 0 &&
|
||||||
|
motionQueueEntry.getEndTime() < userTimeSeconds
|
||||||
|
) {
|
||||||
|
motionQueueEntry.setIsFinished(true); // 終了
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief モデルの再生開始処理
|
||||||
|
*
|
||||||
|
* モーションの再生を開始するためのセットアップを行う。
|
||||||
|
*
|
||||||
|
* @param[in] motionQueueEntry CubismMotionQueueManagerで管理されているモーション
|
||||||
|
* @param[in] userTimeSeconds デルタ時間の積算値[秒]
|
||||||
|
*/
|
||||||
|
public setupMotionQueueEntry(
|
||||||
|
motionQueueEntry: CubismMotionQueueEntry,
|
||||||
|
userTimeSeconds: number
|
||||||
|
) {
|
||||||
|
if (motionQueueEntry == null || motionQueueEntry.isStarted()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!motionQueueEntry.isAvailable()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
motionQueueEntry.setIsStarted(true);
|
||||||
|
motionQueueEntry.setStartTime(userTimeSeconds - this._offsetSeconds); // モーションの開始時刻を記録
|
||||||
|
motionQueueEntry.setFadeInStartTime(userTimeSeconds); // フェードインの開始時刻
|
||||||
|
|
||||||
|
if (motionQueueEntry.getEndTime() < 0.0) {
|
||||||
|
// 開始していないうちに終了設定している場合がある
|
||||||
|
this.adjustEndTime(motionQueueEntry);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 再生開始コールバック
|
||||||
|
if (motionQueueEntry._motion._onBeganMotion) {
|
||||||
|
motionQueueEntry._motion._onBeganMotion(motionQueueEntry._motion);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief モデルのウェイト更新
|
||||||
|
*
|
||||||
|
* モーションのウェイトを更新する。
|
||||||
|
*
|
||||||
|
* @param[in] motionQueueEntry CubismMotionQueueManagerで管理されているモーション
|
||||||
|
* @param[in] userTimeSeconds デルタ時間の積算値[秒]
|
||||||
|
*/
|
||||||
|
public updateFadeWeight(
|
||||||
|
motionQueueEntry: CubismMotionQueueEntry,
|
||||||
|
userTimeSeconds: number
|
||||||
|
): number {
|
||||||
|
if (motionQueueEntry == null) {
|
||||||
|
CubismDebug.print(LogLevel.LogLevel_Error, 'motionQueueEntry is null.');
|
||||||
|
}
|
||||||
|
|
||||||
|
let fadeWeight: number = this._weight; // 現在の値と掛け合わせる割合
|
||||||
|
|
||||||
|
//---- フェードイン・アウトの処理 ----
|
||||||
|
// 単純なサイン関数でイージングする
|
||||||
|
const fadeIn: number =
|
||||||
|
this._fadeInSeconds == 0.0
|
||||||
|
? 1.0
|
||||||
|
: CubismMath.getEasingSine(
|
||||||
|
(userTimeSeconds - motionQueueEntry.getFadeInStartTime()) /
|
||||||
|
this._fadeInSeconds
|
||||||
|
);
|
||||||
|
|
||||||
|
const fadeOut: number =
|
||||||
|
this._fadeOutSeconds == 0.0 || motionQueueEntry.getEndTime() < 0.0
|
||||||
|
? 1.0
|
||||||
|
: CubismMath.getEasingSine(
|
||||||
|
(motionQueueEntry.getEndTime() - userTimeSeconds) /
|
||||||
|
this._fadeOutSeconds
|
||||||
|
);
|
||||||
|
|
||||||
|
fadeWeight = fadeWeight * fadeIn * fadeOut;
|
||||||
|
|
||||||
|
motionQueueEntry.setState(userTimeSeconds, fadeWeight);
|
||||||
|
|
||||||
|
CSM_ASSERT(0.0 <= fadeWeight && fadeWeight <= 1.0);
|
||||||
|
|
||||||
|
return fadeWeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* フェードインの時間を設定する
|
||||||
|
* @param fadeInSeconds フェードインにかかる時間[秒]
|
||||||
|
*/
|
||||||
|
public setFadeInTime(fadeInSeconds: number): void {
|
||||||
|
this._fadeInSeconds = fadeInSeconds;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* フェードアウトの時間を設定する
|
||||||
|
* @param fadeOutSeconds フェードアウトにかかる時間[秒]
|
||||||
|
*/
|
||||||
|
public setFadeOutTime(fadeOutSeconds: number): void {
|
||||||
|
this._fadeOutSeconds = fadeOutSeconds;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* フェードアウトにかかる時間の取得
|
||||||
|
* @return フェードアウトにかかる時間[秒]
|
||||||
|
*/
|
||||||
|
public getFadeOutTime(): number {
|
||||||
|
return this._fadeOutSeconds;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* フェードインにかかる時間の取得
|
||||||
|
* @return フェードインにかかる時間[秒]
|
||||||
|
*/
|
||||||
|
public getFadeInTime(): number {
|
||||||
|
return this._fadeInSeconds;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* モーション適用の重みの設定
|
||||||
|
* @param weight 重み(0.0 - 1.0)
|
||||||
|
*/
|
||||||
|
public setWeight(weight: number): void {
|
||||||
|
this._weight = weight;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* モーション適用の重みの取得
|
||||||
|
* @return 重み(0.0 - 1.0)
|
||||||
|
*/
|
||||||
|
public getWeight(): number {
|
||||||
|
return this._weight;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* モーションの長さの取得
|
||||||
|
* @return モーションの長さ[秒]
|
||||||
|
*
|
||||||
|
* @note ループの時は「-1」。
|
||||||
|
* ループでない場合は、オーバーライドする。
|
||||||
|
* 正の値の時は取得される時間で終了する。
|
||||||
|
* 「-1」の時は外部から停止命令がない限り終わらない処理となる。
|
||||||
|
*/
|
||||||
|
public getDuration(): number {
|
||||||
|
return -1.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* モーションのループ1回分の長さの取得
|
||||||
|
* @return モーションのループ一回分の長さ[秒]
|
||||||
|
*
|
||||||
|
* @note ループしない場合は、getDuration()と同じ値を返す
|
||||||
|
* ループ一回分の長さが定義できない場合(プログラム的に動き続けるサブクラスなど)の場合は「-1」を返す
|
||||||
|
*/
|
||||||
|
public getLoopDuration(): number {
|
||||||
|
return -1.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* モーション再生の開始時刻の設定
|
||||||
|
* @param offsetSeconds モーション再生の開始時刻[秒]
|
||||||
|
*/
|
||||||
|
public setOffsetTime(offsetSeconds: number): void {
|
||||||
|
this._offsetSeconds = offsetSeconds;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ループ情報の設定
|
||||||
|
* @param loop ループ情報
|
||||||
|
*/
|
||||||
|
public setLoop(loop: boolean): void {
|
||||||
|
this._isLoop = loop;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ループ情報の取得
|
||||||
|
* @return true ループする
|
||||||
|
* @return false ループしない
|
||||||
|
*/
|
||||||
|
public getLoop(): boolean {
|
||||||
|
return this._isLoop;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ループ時のフェードイン情報の設定
|
||||||
|
* @param loopFadeIn ループ時のフェードイン情報
|
||||||
|
*/
|
||||||
|
public setLoopFadeIn(loopFadeIn: boolean) {
|
||||||
|
this._isLoopFadeIn = loopFadeIn;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ループ時のフェードイン情報の取得
|
||||||
|
*
|
||||||
|
* @return true する
|
||||||
|
* @return false しない
|
||||||
|
*/
|
||||||
|
public getLoopFadeIn(): boolean {
|
||||||
|
return this._isLoopFadeIn;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* モデルのパラメータ更新
|
||||||
|
*
|
||||||
|
* イベント発火のチェック。
|
||||||
|
* 入力する時間は呼ばれるモーションタイミングを0とした秒数で行う。
|
||||||
|
*
|
||||||
|
* @param beforeCheckTimeSeconds 前回のイベントチェック時間[秒]
|
||||||
|
* @param motionTimeSeconds 今回の再生時間[秒]
|
||||||
|
*/
|
||||||
|
public getFiredEvent(
|
||||||
|
beforeCheckTimeSeconds: number,
|
||||||
|
motionTimeSeconds: number
|
||||||
|
): Array<string> {
|
||||||
|
return this._firedEventValues;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* モーションを更新して、モデルにパラメータ値を反映する
|
||||||
|
* @param model 対象のモデル
|
||||||
|
* @param userTimeSeconds デルタ時間の積算値[秒]
|
||||||
|
* @param weight モーションの重み
|
||||||
|
* @param motionQueueEntry CubismMotionQueueManagerで管理されているモーション
|
||||||
|
* @return true モデルへパラメータ値の反映あり
|
||||||
|
* @return false モデルへのパラメータ値の反映なし(モーションの変化なし)
|
||||||
|
*/
|
||||||
|
public abstract doUpdateParameters(
|
||||||
|
model: CubismModel,
|
||||||
|
userTimeSeconds: number,
|
||||||
|
weight: number,
|
||||||
|
motionQueueEntry: CubismMotionQueueEntry
|
||||||
|
): void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* モーション再生開始コールバックの登録
|
||||||
|
*
|
||||||
|
* モーション再生開始コールバックを登録する。
|
||||||
|
* 以下の状態の際には呼び出されない:
|
||||||
|
* 1. 再生中のモーションが「ループ」として設定されているとき
|
||||||
|
* 2. コールバックが登録されていない時
|
||||||
|
*
|
||||||
|
* @param onBeganMotionHandler モーション再生開始コールバック関数
|
||||||
|
*/
|
||||||
|
public setBeganMotionHandler = (onBeganMotionHandler: BeganMotionCallback) =>
|
||||||
|
(this._onBeganMotion = onBeganMotionHandler);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* モーション再生開始コールバックの取得
|
||||||
|
*
|
||||||
|
* モーション再生開始コールバックを取得する。
|
||||||
|
*
|
||||||
|
* @return 登録されているモーション再生開始コールバック関数
|
||||||
|
*/
|
||||||
|
public getBeganMotionHandler = () => this._onBeganMotion;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* モーション再生終了コールバックの登録
|
||||||
|
*
|
||||||
|
* モーション再生終了コールバックを登録する。
|
||||||
|
* isFinishedフラグを設定するタイミングで呼び出される。
|
||||||
|
* 以下の状態の際には呼び出されない:
|
||||||
|
* 1. 再生中のモーションが「ループ」として設定されているとき
|
||||||
|
* 2. コールバックが登録されていない時
|
||||||
|
*
|
||||||
|
* @param onFinishedMotionHandler モーション再生終了コールバック関数
|
||||||
|
*/
|
||||||
|
public setFinishedMotionHandler = (
|
||||||
|
onFinishedMotionHandler: FinishedMotionCallback
|
||||||
|
) => (this._onFinishedMotion = onFinishedMotionHandler);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* モーション再生終了コールバックの取得
|
||||||
|
*
|
||||||
|
* モーション再生終了コールバックを取得する。
|
||||||
|
*
|
||||||
|
* @return 登録されているモーション再生終了コールバック関数
|
||||||
|
*/
|
||||||
|
public getFinishedMotionHandler = () => this._onFinishedMotion;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 透明度のカーブが存在するかどうかを確認する
|
||||||
|
*
|
||||||
|
* @return true -> キーが存在する
|
||||||
|
* false -> キーが存在しない
|
||||||
|
*/
|
||||||
|
public isExistModelOpacity(): boolean {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 透明度のカーブのインデックスを返す
|
||||||
|
*
|
||||||
|
* @return success:透明度のカーブのインデックス
|
||||||
|
*/
|
||||||
|
public getModelOpacityIndex(): number {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 透明度のIdを返す
|
||||||
|
*
|
||||||
|
* @param index モーションカーブのインデックス
|
||||||
|
* @return success:透明度のId
|
||||||
|
*/
|
||||||
|
public getModelOpacityId(index: number): CubismIdHandle {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 指定時間の透明度の値を返す
|
||||||
|
*
|
||||||
|
* @return success:モーションの現在時間におけるOpacityの値
|
||||||
|
*
|
||||||
|
* @note 更新後の値を取るにはUpdateParameters() の後に呼び出す。
|
||||||
|
*/
|
||||||
|
protected getModelOpacityValue(): number {
|
||||||
|
return 1.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 終了時刻の調整
|
||||||
|
* @param motionQueueEntry CubismMotionQueueManagerで管理されているモーション
|
||||||
|
*/
|
||||||
|
protected adjustEndTime(motionQueueEntry: CubismMotionQueueEntry) {
|
||||||
|
const duration = this.getDuration();
|
||||||
|
|
||||||
|
// duration == -1 の場合はループする
|
||||||
|
const endTime =
|
||||||
|
duration <= 0.0 ? -1 : motionQueueEntry.getStartTime() + duration;
|
||||||
|
|
||||||
|
motionQueueEntry.setEndTime(endTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
public _fadeInSeconds: number; // フェードインにかかる時間[秒]
|
||||||
|
public _fadeOutSeconds: number; // フェードアウトにかかる時間[秒]
|
||||||
|
public _weight: number; // モーションの重み
|
||||||
|
public _offsetSeconds: number; // モーション再生の開始時間[秒]
|
||||||
|
public _isLoop: boolean; // ループが有効かのフラグ
|
||||||
|
public _isLoopFadeIn: boolean; // ループ時にフェードインが有効かどうかのフラグ
|
||||||
|
public _previousLoopState: boolean; // 前回の `_isLoop` の状態
|
||||||
|
public _firedEventValues: Array<string>;
|
||||||
|
|
||||||
|
// モーション再生開始コールバック関数
|
||||||
|
public _onBeganMotion?: BeganMotionCallback;
|
||||||
|
// モーション再生終了コールバック関数
|
||||||
|
public _onFinishedMotion?: FinishedMotionCallback;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Namespace definition for compatibility.
|
||||||
|
import * as $ from './acubismmotion';
|
||||||
|
import { CubismIdHandle } from '../id/cubismid';
|
||||||
|
import { LogLevel } from '../live2dcubismframework';
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-namespace
|
||||||
|
export namespace Live2DCubismFramework {
|
||||||
|
export const ACubismMotion = $.ACubismMotion;
|
||||||
|
export type ACubismMotion = $.ACubismMotion;
|
||||||
|
export type BeganMotionCallback = $.BeganMotionCallback;
|
||||||
|
export type FinishedMotionCallback = $.FinishedMotionCallback;
|
||||||
|
}
|
||||||
@ -0,0 +1,60 @@
|
|||||||
|
/**
|
||||||
|
* Copyright(c) Live2D Inc. All rights reserved.
|
||||||
|
*
|
||||||
|
* Use of this source code is governed by the Live2D Open Software license
|
||||||
|
* that can be found at https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { ICubismUpdater, CubismUpdateOrder } from './icubismupdater';
|
||||||
|
import { CubismModel } from '../model/cubismmodel';
|
||||||
|
import { CubismBreath } from '../effect/cubismbreath';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updater for breath effects.
|
||||||
|
* Handles the management of breath animation through the CubismBreath class.
|
||||||
|
*/
|
||||||
|
export class CubismBreathUpdater extends ICubismUpdater {
|
||||||
|
private _breath: CubismBreath;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
*
|
||||||
|
* @param breath CubismBreath reference
|
||||||
|
*/
|
||||||
|
constructor(breath: CubismBreath);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
*
|
||||||
|
* @param breath CubismBreath reference
|
||||||
|
* @param executionOrder Order of operations
|
||||||
|
*/
|
||||||
|
constructor(breath: CubismBreath, executionOrder: number);
|
||||||
|
|
||||||
|
constructor(breath: CubismBreath, executionOrder?: number) {
|
||||||
|
super(executionOrder ?? CubismUpdateOrder.CubismUpdateOrder_Breath);
|
||||||
|
this._breath = breath;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update process.
|
||||||
|
*
|
||||||
|
* @param model Model to update
|
||||||
|
* @param deltaTimeSeconds Delta time in seconds.
|
||||||
|
*/
|
||||||
|
onLateUpdate(model: CubismModel, deltaTimeSeconds: number): void {
|
||||||
|
if (!model) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._breath.updateParameters(model, deltaTimeSeconds);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Namespace definition for compatibility.
|
||||||
|
import * as $ from './cubismbreathupdater';
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-namespace
|
||||||
|
export namespace Live2DCubismFramework {
|
||||||
|
export const CubismBreathUpdater = $.CubismBreathUpdater;
|
||||||
|
export type CubismBreathUpdater = $.CubismBreathUpdater;
|
||||||
|
}
|
||||||
@ -0,0 +1,365 @@
|
|||||||
|
/**
|
||||||
|
* Copyright(c) Live2D Inc. All rights reserved.
|
||||||
|
*
|
||||||
|
* Use of this source code is governed by the Live2D Open Software license
|
||||||
|
* that can be found at https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { CubismIdHandle } from '../id/cubismid';
|
||||||
|
import { CubismFramework } from '../live2dcubismframework';
|
||||||
|
import { CubismModel } from '../model/cubismmodel';
|
||||||
|
import { CubismJson, Value } from '../utils/cubismjson';
|
||||||
|
import { ACubismMotion } from './acubismmotion';
|
||||||
|
import { CubismMotionQueueEntry } from './cubismmotionqueueentry';
|
||||||
|
|
||||||
|
// exp3.jsonのキーとデフォルト
|
||||||
|
const ExpressionKeyFadeIn = 'FadeInTime';
|
||||||
|
const ExpressionKeyFadeOut = 'FadeOutTime';
|
||||||
|
const ExpressionKeyParameters = 'Parameters';
|
||||||
|
const ExpressionKeyId = 'Id';
|
||||||
|
const ExpressionKeyValue = 'Value';
|
||||||
|
const ExpressionKeyBlend = 'Blend';
|
||||||
|
const BlendValueAdd = 'Add';
|
||||||
|
const BlendValueMultiply = 'Multiply';
|
||||||
|
const BlendValueOverwrite = 'Overwrite';
|
||||||
|
const DefaultFadeTime = 1.0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 表情のモーション
|
||||||
|
*
|
||||||
|
* 表情のモーションクラス。
|
||||||
|
*/
|
||||||
|
export class CubismExpressionMotion extends ACubismMotion {
|
||||||
|
static readonly DefaultAdditiveValue = 0.0; // 加算適用の初期値
|
||||||
|
static readonly DefaultMultiplyValue = 1.0; // 乗算適用の初期値
|
||||||
|
|
||||||
|
/**
|
||||||
|
* インスタンスを作成する。
|
||||||
|
* @param buffer expファイルが読み込まれているバッファ
|
||||||
|
* @param size バッファのサイズ
|
||||||
|
* @return 作成されたインスタンス
|
||||||
|
*/
|
||||||
|
public static create(
|
||||||
|
buffer: ArrayBuffer,
|
||||||
|
size: number
|
||||||
|
): CubismExpressionMotion {
|
||||||
|
const expression: CubismExpressionMotion = new CubismExpressionMotion();
|
||||||
|
expression.parse(buffer, size);
|
||||||
|
return expression;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* モデルのパラメータの更新の実行
|
||||||
|
* @param model 対象のモデル
|
||||||
|
* @param userTimeSeconds デルタ時間の積算値[秒]
|
||||||
|
* @param weight モーションの重み
|
||||||
|
* @param motionQueueEntry CubismMotionQueueManagerで管理されているモーション
|
||||||
|
*/
|
||||||
|
public doUpdateParameters(
|
||||||
|
model: CubismModel,
|
||||||
|
userTimeSeconds: number,
|
||||||
|
weight: number,
|
||||||
|
motionQueueEntry: CubismMotionQueueEntry
|
||||||
|
): void {
|
||||||
|
for (let i = 0; i < this._parameters.length; ++i) {
|
||||||
|
const parameter: ExpressionParameter = this._parameters[i];
|
||||||
|
|
||||||
|
switch (parameter.blendType) {
|
||||||
|
case ExpressionBlendType.Additive: {
|
||||||
|
model.addParameterValueById(
|
||||||
|
parameter.parameterId,
|
||||||
|
parameter.value,
|
||||||
|
weight
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case ExpressionBlendType.Multiply: {
|
||||||
|
model.multiplyParameterValueById(
|
||||||
|
parameter.parameterId,
|
||||||
|
parameter.value,
|
||||||
|
weight
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case ExpressionBlendType.Overwrite: {
|
||||||
|
model.setParameterValueById(
|
||||||
|
parameter.parameterId,
|
||||||
|
parameter.value,
|
||||||
|
weight
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
// 仕様にない値を設定した時はすでに加算モードになっている
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 表情によるモデルのパラメータの計算
|
||||||
|
*
|
||||||
|
* モデルの表情に関するパラメータを計算する。
|
||||||
|
*
|
||||||
|
* @param[in] model 対象のモデル
|
||||||
|
* @param[in] userTimeSeconds デルタ時間の積算値[秒]
|
||||||
|
* @param[in] motionQueueEntry CubismMotionQueueManagerで管理されているモーション
|
||||||
|
* @param[in] expressionParameterValues モデルに適用する各パラメータの値
|
||||||
|
* @param[in] expressionIndex 表情のインデックス
|
||||||
|
* @param[in] fadeWeight 表情のウェイト
|
||||||
|
*/
|
||||||
|
public calculateExpressionParameters(
|
||||||
|
model: CubismModel,
|
||||||
|
userTimeSeconds: number,
|
||||||
|
motionQueueEntry: CubismMotionQueueEntry,
|
||||||
|
expressionParameterValues: Array<ExpressionParameterValue>,
|
||||||
|
expressionIndex: number,
|
||||||
|
fadeWeight: number
|
||||||
|
) {
|
||||||
|
if (motionQueueEntry == null || expressionParameterValues == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!motionQueueEntry.isAvailable()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// モデルに適用する値を計算
|
||||||
|
for (let i = 0; i < expressionParameterValues.length; ++i) {
|
||||||
|
const expressionParameterValue = expressionParameterValues[i];
|
||||||
|
|
||||||
|
if (expressionParameterValue.parameterId == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const currentParameterValue = (expressionParameterValue.overwriteValue =
|
||||||
|
model.getParameterValueById(expressionParameterValue.parameterId));
|
||||||
|
|
||||||
|
const expressionParameters = this.getExpressionParameters();
|
||||||
|
let parameterIndex = -1;
|
||||||
|
for (let j = 0; j < expressionParameters.length; ++j) {
|
||||||
|
if (
|
||||||
|
expressionParameterValue.parameterId !=
|
||||||
|
expressionParameters[j].parameterId
|
||||||
|
) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
parameterIndex = j;
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 再生中のExpressionが参照していないパラメータは初期値を適用
|
||||||
|
if (parameterIndex < 0) {
|
||||||
|
if (expressionIndex == 0) {
|
||||||
|
expressionParameterValue.additiveValue =
|
||||||
|
CubismExpressionMotion.DefaultAdditiveValue;
|
||||||
|
expressionParameterValue.multiplyValue =
|
||||||
|
CubismExpressionMotion.DefaultMultiplyValue;
|
||||||
|
expressionParameterValue.overwriteValue = currentParameterValue;
|
||||||
|
} else {
|
||||||
|
expressionParameterValue.additiveValue = this.calculateValue(
|
||||||
|
expressionParameterValue.additiveValue,
|
||||||
|
CubismExpressionMotion.DefaultAdditiveValue,
|
||||||
|
fadeWeight
|
||||||
|
);
|
||||||
|
expressionParameterValue.multiplyValue = this.calculateValue(
|
||||||
|
expressionParameterValue.multiplyValue,
|
||||||
|
CubismExpressionMotion.DefaultMultiplyValue,
|
||||||
|
fadeWeight
|
||||||
|
);
|
||||||
|
expressionParameterValue.overwriteValue = this.calculateValue(
|
||||||
|
expressionParameterValue.overwriteValue,
|
||||||
|
currentParameterValue,
|
||||||
|
fadeWeight
|
||||||
|
);
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 値を計算
|
||||||
|
const value = expressionParameters[parameterIndex].value;
|
||||||
|
let newAdditiveValue, newMultiplyValue, newOverwriteValue;
|
||||||
|
switch (expressionParameters[parameterIndex].blendType) {
|
||||||
|
case ExpressionBlendType.Additive:
|
||||||
|
newAdditiveValue = value;
|
||||||
|
newMultiplyValue = CubismExpressionMotion.DefaultMultiplyValue;
|
||||||
|
newOverwriteValue = currentParameterValue;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ExpressionBlendType.Multiply:
|
||||||
|
newAdditiveValue = CubismExpressionMotion.DefaultAdditiveValue;
|
||||||
|
newMultiplyValue = value;
|
||||||
|
newOverwriteValue = currentParameterValue;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ExpressionBlendType.Overwrite:
|
||||||
|
newAdditiveValue = CubismExpressionMotion.DefaultAdditiveValue;
|
||||||
|
newMultiplyValue = CubismExpressionMotion.DefaultMultiplyValue;
|
||||||
|
newOverwriteValue = value;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (expressionIndex == 0) {
|
||||||
|
expressionParameterValue.additiveValue = newAdditiveValue;
|
||||||
|
expressionParameterValue.multiplyValue = newMultiplyValue;
|
||||||
|
expressionParameterValue.overwriteValue = newOverwriteValue;
|
||||||
|
} else {
|
||||||
|
expressionParameterValue.additiveValue =
|
||||||
|
expressionParameterValue.additiveValue * (1.0 - fadeWeight) +
|
||||||
|
newAdditiveValue * fadeWeight;
|
||||||
|
expressionParameterValue.multiplyValue =
|
||||||
|
expressionParameterValue.multiplyValue * (1.0 - fadeWeight) +
|
||||||
|
newMultiplyValue * fadeWeight;
|
||||||
|
expressionParameterValue.overwriteValue =
|
||||||
|
expressionParameterValue.overwriteValue * (1.0 - fadeWeight) +
|
||||||
|
newOverwriteValue * fadeWeight;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 表情が参照しているパラメータを取得
|
||||||
|
*
|
||||||
|
* 表情が参照しているパラメータを取得する
|
||||||
|
*
|
||||||
|
* @return 表情パラメータ
|
||||||
|
*/
|
||||||
|
public getExpressionParameters() {
|
||||||
|
return this._parameters;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected parse(buffer: ArrayBuffer, size: number) {
|
||||||
|
const json: CubismJson = CubismJson.create(buffer, size);
|
||||||
|
if (!json) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const root: Value = json.getRoot();
|
||||||
|
|
||||||
|
this.setFadeInTime(
|
||||||
|
root.getValueByString(ExpressionKeyFadeIn).toFloat(DefaultFadeTime)
|
||||||
|
); // フェードイン
|
||||||
|
this.setFadeOutTime(
|
||||||
|
root.getValueByString(ExpressionKeyFadeOut).toFloat(DefaultFadeTime)
|
||||||
|
); // フェードアウト
|
||||||
|
|
||||||
|
// 各パラメータについて
|
||||||
|
const parameterCount = root
|
||||||
|
.getValueByString(ExpressionKeyParameters)
|
||||||
|
.getSize();
|
||||||
|
|
||||||
|
let dstIndex: number = this._parameters.length;
|
||||||
|
this._parameters.length += parameterCount;
|
||||||
|
for (let i = 0; i < parameterCount; ++i) {
|
||||||
|
const param: Value = root
|
||||||
|
.getValueByString(ExpressionKeyParameters)
|
||||||
|
.getValueByIndex(i);
|
||||||
|
const parameterId: CubismIdHandle = CubismFramework.getIdManager().getId(
|
||||||
|
param.getValueByString(ExpressionKeyId).getRawString()
|
||||||
|
); // パラメータID
|
||||||
|
|
||||||
|
const value: number = param
|
||||||
|
.getValueByString(ExpressionKeyValue)
|
||||||
|
.toFloat(); // 値
|
||||||
|
|
||||||
|
// 計算方法の設定
|
||||||
|
let blendType: ExpressionBlendType;
|
||||||
|
|
||||||
|
if (
|
||||||
|
param.getValueByString(ExpressionKeyBlend).isNull() ||
|
||||||
|
param.getValueByString(ExpressionKeyBlend).getString() == BlendValueAdd
|
||||||
|
) {
|
||||||
|
blendType = ExpressionBlendType.Additive;
|
||||||
|
} else if (
|
||||||
|
param.getValueByString(ExpressionKeyBlend).getString() ==
|
||||||
|
BlendValueMultiply
|
||||||
|
) {
|
||||||
|
blendType = ExpressionBlendType.Multiply;
|
||||||
|
} else if (
|
||||||
|
param.getValueByString(ExpressionKeyBlend).getString() ==
|
||||||
|
BlendValueOverwrite
|
||||||
|
) {
|
||||||
|
blendType = ExpressionBlendType.Overwrite;
|
||||||
|
} else {
|
||||||
|
// その他 仕様にない値を設定した時は加算モードにすることで復旧
|
||||||
|
blendType = ExpressionBlendType.Additive;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 設定オブジェクトを作成してリストに追加する
|
||||||
|
const item: ExpressionParameter = new ExpressionParameter();
|
||||||
|
|
||||||
|
item.parameterId = parameterId;
|
||||||
|
item.blendType = blendType;
|
||||||
|
item.value = value;
|
||||||
|
|
||||||
|
this._parameters[dstIndex++] = item;
|
||||||
|
}
|
||||||
|
|
||||||
|
CubismJson.delete(json); // JSONデータは不要になったら削除する
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief ブレンド計算
|
||||||
|
*
|
||||||
|
* 入力された値でブレンド計算をする。
|
||||||
|
*
|
||||||
|
* @param source 現在の値
|
||||||
|
* @param destination 適用する値
|
||||||
|
* @param weight ウェイト
|
||||||
|
* @return 計算結果
|
||||||
|
*/
|
||||||
|
public calculateValue(
|
||||||
|
source: number,
|
||||||
|
destination: number,
|
||||||
|
fadeWeight: number
|
||||||
|
): number {
|
||||||
|
return source * (1.0 - fadeWeight) + destination * fadeWeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* コンストラクタ
|
||||||
|
*/
|
||||||
|
protected constructor() {
|
||||||
|
super();
|
||||||
|
this._parameters = new Array<ExpressionParameter>();
|
||||||
|
}
|
||||||
|
|
||||||
|
private _parameters: Array<ExpressionParameter>; // 表情のパラメータ情報リスト
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 表情パラメータ値の計算方式
|
||||||
|
*/
|
||||||
|
export enum ExpressionBlendType {
|
||||||
|
Additive = 0, // 加算
|
||||||
|
Multiply = 1, // 乗算
|
||||||
|
Overwrite = 2 // 上書き
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 表情のパラメータ情報
|
||||||
|
*/
|
||||||
|
export class ExpressionParameter {
|
||||||
|
parameterId: CubismIdHandle; // パラメータID
|
||||||
|
blendType: ExpressionBlendType; // パラメータの演算種類
|
||||||
|
value: number; // 値
|
||||||
|
}
|
||||||
|
|
||||||
|
// Namespace definition for compatibility.
|
||||||
|
import * as $ from './cubismexpressionmotion';
|
||||||
|
import { ExpressionParameterValue } from './cubismexpressionmotionmanager';
|
||||||
|
import { CubismDefaultParameterId } from '../cubismdefaultparameterid';
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-namespace
|
||||||
|
export namespace Live2DCubismFramework {
|
||||||
|
export const CubismExpressionMotion = $.CubismExpressionMotion;
|
||||||
|
export type CubismExpressionMotion = $.CubismExpressionMotion;
|
||||||
|
export const ExpressionBlendType = $.ExpressionBlendType;
|
||||||
|
export type ExpressionBlendType = $.ExpressionBlendType;
|
||||||
|
export const ExpressionParameter = $.ExpressionParameter;
|
||||||
|
export type ExpressionParameter = $.ExpressionParameter;
|
||||||
|
}
|
||||||
@ -0,0 +1,282 @@
|
|||||||
|
/**
|
||||||
|
* Copyright(c) Live2D Inc. All rights reserved.
|
||||||
|
*
|
||||||
|
* Use of this source code is governed by the Live2D Open Software license
|
||||||
|
* that can be found at https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { CubismId, CubismIdHandle } from '../id/cubismid';
|
||||||
|
import { LogLevel, csmDelete } from '../live2dcubismframework';
|
||||||
|
import { CubismModel } from '../model/cubismmodel';
|
||||||
|
import { CubismExpressionMotion } from './cubismexpressionmotion';
|
||||||
|
import { CubismMotionQueueEntry } from './cubismmotionqueueentry';
|
||||||
|
import { CubismMotionQueueManager } from './cubismmotionqueuemanager';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief パラメータに適用する表情の値を持たせる構造体
|
||||||
|
*/
|
||||||
|
export class ExpressionParameterValue {
|
||||||
|
parameterId: CubismIdHandle; // パラメーターID
|
||||||
|
additiveValue: number; // 加算値
|
||||||
|
multiplyValue: number; // 乗算値
|
||||||
|
overwriteValue: number; // 上書き値
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 表情モーションの管理
|
||||||
|
*
|
||||||
|
* 表情モーションの管理をおこなうクラス。
|
||||||
|
*/
|
||||||
|
export class CubismExpressionMotionManager extends CubismMotionQueueManager {
|
||||||
|
/**
|
||||||
|
* コンストラクタ
|
||||||
|
*/
|
||||||
|
public constructor() {
|
||||||
|
super();
|
||||||
|
this._expressionParameterValues = new Array<ExpressionParameterValue>();
|
||||||
|
this._fadeWeights = new Array<number>();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* デストラクタ相当の処理
|
||||||
|
*/
|
||||||
|
public release(): void {
|
||||||
|
if (this._expressionParameterValues) {
|
||||||
|
csmDelete(this._expressionParameterValues);
|
||||||
|
this._expressionParameterValues = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this._fadeWeights) {
|
||||||
|
csmDelete(this._fadeWeights);
|
||||||
|
this._fadeWeights = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 再生中のモーションのウェイトを取得する。
|
||||||
|
*
|
||||||
|
* @param[in] index 表情のインデックス
|
||||||
|
* @return 表情モーションのウェイト
|
||||||
|
*/
|
||||||
|
public getFadeWeight(index: number): number {
|
||||||
|
if (
|
||||||
|
index < 0 ||
|
||||||
|
this._fadeWeights.length < 1 ||
|
||||||
|
index >= this._fadeWeights.length
|
||||||
|
) {
|
||||||
|
console.warn(
|
||||||
|
'Failed to get the fade weight value. The element at that index does not exist.'
|
||||||
|
);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return this._fadeWeights[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief モーションのウェイトの設定。
|
||||||
|
*
|
||||||
|
* @param[in] index 表情のインデックス
|
||||||
|
* @param[in] index 表情モーションのウェイト
|
||||||
|
*/
|
||||||
|
public setFadeWeight(index: number, expressionFadeWeight: number): void {
|
||||||
|
if (
|
||||||
|
index < 0 ||
|
||||||
|
this._fadeWeights.length < 1 ||
|
||||||
|
this._fadeWeights.length <= index
|
||||||
|
) {
|
||||||
|
console.warn(
|
||||||
|
'Failed to set the fade weight value. The element at that index does not exist.'
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._fadeWeights[index] = expressionFadeWeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief モーションの更新
|
||||||
|
*
|
||||||
|
* モーションを更新して、モデルにパラメータ値を反映する。
|
||||||
|
*
|
||||||
|
* @param[in] model 対象のモデル
|
||||||
|
* @param[in] deltaTimeSeconds デルタ時間[秒]
|
||||||
|
* @return true 更新されている
|
||||||
|
* false 更新されていない
|
||||||
|
*/
|
||||||
|
public updateMotion(model: CubismModel, deltaTimeSeconds: number): boolean {
|
||||||
|
this._userTimeSeconds += deltaTimeSeconds;
|
||||||
|
let updated = false;
|
||||||
|
const motions = this.getCubismMotionQueueEntries();
|
||||||
|
|
||||||
|
let expressionWeight = 0.0;
|
||||||
|
let expressionIndex = 0;
|
||||||
|
|
||||||
|
if (this._fadeWeights.length !== motions.length) {
|
||||||
|
const difference = motions.length - this._fadeWeights.length;
|
||||||
|
let dstIndex: number = this._fadeWeights.length;
|
||||||
|
this._fadeWeights.length += difference;
|
||||||
|
|
||||||
|
// TODO:
|
||||||
|
// https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Array/fill
|
||||||
|
// this._fadeWeights.fill(0.0, dstIndex, this._fadeWeights.length)
|
||||||
|
|
||||||
|
for (let i = 0; i < difference; i++) {
|
||||||
|
this._fadeWeights[dstIndex++] = 0.0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ------- 処理を行う --------
|
||||||
|
// 既にモーションがあれば終了フラグを立てる
|
||||||
|
for (let i = 0; i < this._motions.length; ) {
|
||||||
|
const motionQueueEntry = this._motions[i];
|
||||||
|
|
||||||
|
if (motionQueueEntry == null) {
|
||||||
|
motions.splice(i, 1); //削除
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const expressionMotion = <CubismExpressionMotion>(
|
||||||
|
motionQueueEntry.getCubismMotion()
|
||||||
|
);
|
||||||
|
|
||||||
|
if (expressionMotion == null) {
|
||||||
|
csmDelete(motionQueueEntry);
|
||||||
|
motions.splice(i, 1); //削除
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const expressionParameters = expressionMotion.getExpressionParameters();
|
||||||
|
|
||||||
|
if (motionQueueEntry.isAvailable()) {
|
||||||
|
// 再生中のExpressionが参照しているパラメータをすべてリストアップ
|
||||||
|
for (let i = 0; i < expressionParameters.length; ++i) {
|
||||||
|
if (expressionParameters[i].parameterId == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let index = -1;
|
||||||
|
// リストにパラメータIDが存在するか検索
|
||||||
|
for (let j = 0; j < this._expressionParameterValues.length; ++j) {
|
||||||
|
if (
|
||||||
|
this._expressionParameterValues[j].parameterId !=
|
||||||
|
expressionParameters[i].parameterId
|
||||||
|
) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
index = j;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (index >= 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// パラメータがリストに存在しないなら新規追加
|
||||||
|
const item: ExpressionParameterValue = new ExpressionParameterValue();
|
||||||
|
item.parameterId = expressionParameters[i].parameterId;
|
||||||
|
item.additiveValue = CubismExpressionMotion.DefaultAdditiveValue;
|
||||||
|
item.multiplyValue = CubismExpressionMotion.DefaultMultiplyValue;
|
||||||
|
item.overwriteValue = model.getParameterValueById(item.parameterId);
|
||||||
|
this._expressionParameterValues.push(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ------ 値を計算する ------
|
||||||
|
expressionMotion.setupMotionQueueEntry(
|
||||||
|
motionQueueEntry,
|
||||||
|
this._userTimeSeconds
|
||||||
|
);
|
||||||
|
this.setFadeWeight(
|
||||||
|
expressionIndex,
|
||||||
|
expressionMotion.updateFadeWeight(
|
||||||
|
motionQueueEntry,
|
||||||
|
this._userTimeSeconds
|
||||||
|
)
|
||||||
|
);
|
||||||
|
expressionMotion.calculateExpressionParameters(
|
||||||
|
model,
|
||||||
|
this._userTimeSeconds,
|
||||||
|
motionQueueEntry,
|
||||||
|
this._expressionParameterValues,
|
||||||
|
expressionIndex,
|
||||||
|
this.getFadeWeight(expressionIndex)
|
||||||
|
);
|
||||||
|
|
||||||
|
expressionWeight +=
|
||||||
|
expressionMotion.getFadeInTime() == 0.0
|
||||||
|
? 1.0
|
||||||
|
: CubismMath.getEasingSine(
|
||||||
|
(this._userTimeSeconds - motionQueueEntry.getFadeInStartTime()) /
|
||||||
|
expressionMotion.getFadeInTime()
|
||||||
|
);
|
||||||
|
|
||||||
|
updated = true;
|
||||||
|
|
||||||
|
if (motionQueueEntry.isTriggeredFadeOut()) {
|
||||||
|
// フェードアウト開始
|
||||||
|
motionQueueEntry.startFadeOut(
|
||||||
|
motionQueueEntry.getFadeOutSeconds(),
|
||||||
|
this._userTimeSeconds
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
++i;
|
||||||
|
++expressionIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----- 最新のExpressionのフェードが完了していればそれ以前を削除する ------
|
||||||
|
if (motions.length > 1) {
|
||||||
|
const latestFadeWeight: number = this.getFadeWeight(
|
||||||
|
this._fadeWeights.length - 1
|
||||||
|
);
|
||||||
|
if (latestFadeWeight >= 1.0) {
|
||||||
|
// 配列の最後の要素は削除しない
|
||||||
|
for (let i = motions.length - 2; i >= 0; --i) {
|
||||||
|
const motionQueueEntry = motions[i];
|
||||||
|
csmDelete(motionQueueEntry);
|
||||||
|
motions.splice(i, 1);
|
||||||
|
this._fadeWeights.splice(i, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (expressionWeight > 1.0) {
|
||||||
|
expressionWeight = 1.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// モデルに各値を適用
|
||||||
|
for (let i = 0; i < this._expressionParameterValues.length; ++i) {
|
||||||
|
const expressionParameterValue = this._expressionParameterValues[i];
|
||||||
|
model.setParameterValueById(
|
||||||
|
expressionParameterValue.parameterId,
|
||||||
|
(expressionParameterValue.overwriteValue +
|
||||||
|
expressionParameterValue.additiveValue) *
|
||||||
|
expressionParameterValue.multiplyValue,
|
||||||
|
expressionWeight
|
||||||
|
);
|
||||||
|
|
||||||
|
expressionParameterValue.additiveValue =
|
||||||
|
CubismExpressionMotion.DefaultAdditiveValue;
|
||||||
|
expressionParameterValue.multiplyValue =
|
||||||
|
CubismExpressionMotion.DefaultMultiplyValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
return updated;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _expressionParameterValues: Array<ExpressionParameterValue>; ///< モデルに適用する各パラメータの値
|
||||||
|
private _fadeWeights: Array<number>; ///< 再生中の表情のウェイト
|
||||||
|
private _startExpressionTime: number; ///< 表情の再生開始時刻
|
||||||
|
}
|
||||||
|
|
||||||
|
// Namespace definition for compatibility.
|
||||||
|
import * as $ from './cubismexpressionmotionmanager';
|
||||||
|
import { CubismMath } from '../math/cubismmath';
|
||||||
|
import { CubismDebug, CubismLogError } from '../utils/cubismdebug';
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-namespace
|
||||||
|
export namespace Live2DCubismFramework {
|
||||||
|
export const CubismExpressionMotionManager = $.CubismExpressionMotionManager;
|
||||||
|
export type CubismExpressionMotionManager = $.CubismExpressionMotionManager;
|
||||||
|
}
|
||||||
@ -0,0 +1,66 @@
|
|||||||
|
/**
|
||||||
|
* Copyright(c) Live2D Inc. All rights reserved.
|
||||||
|
*
|
||||||
|
* Use of this source code is governed by the Live2D Open Software license
|
||||||
|
* that can be found at https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { ICubismUpdater, CubismUpdateOrder } from './icubismupdater';
|
||||||
|
import { CubismModel } from '../model/cubismmodel';
|
||||||
|
import { CubismExpressionMotionManager } from './cubismexpressionmotionmanager';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updater for expression effects.
|
||||||
|
* Handles the management of expression motion through the CubismExpressionMotionManager.
|
||||||
|
*/
|
||||||
|
export class CubismExpressionUpdater extends ICubismUpdater {
|
||||||
|
private _expressionManager: CubismExpressionMotionManager;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
*
|
||||||
|
* @param expressionManager CubismExpressionMotionManager reference
|
||||||
|
*/
|
||||||
|
constructor(expressionManager: CubismExpressionMotionManager);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
*
|
||||||
|
* @param expressionManager CubismExpressionMotionManager reference
|
||||||
|
* @param executionOrder Order of operations
|
||||||
|
*/
|
||||||
|
constructor(
|
||||||
|
expressionManager: CubismExpressionMotionManager,
|
||||||
|
executionOrder: number
|
||||||
|
);
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
expressionManager: CubismExpressionMotionManager,
|
||||||
|
executionOrder?: number
|
||||||
|
) {
|
||||||
|
super(executionOrder ?? CubismUpdateOrder.CubismUpdateOrder_Expression);
|
||||||
|
this._expressionManager = expressionManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update process.
|
||||||
|
*
|
||||||
|
* @param model Model to update
|
||||||
|
* @param deltaTimeSeconds Delta time in seconds.
|
||||||
|
*/
|
||||||
|
onLateUpdate(model: CubismModel, deltaTimeSeconds: number): void {
|
||||||
|
if (!model) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._expressionManager.updateMotion(model, deltaTimeSeconds);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Namespace definition for compatibility.
|
||||||
|
import * as $ from './cubismexpressionupdater';
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-namespace
|
||||||
|
export namespace Live2DCubismFramework {
|
||||||
|
export const CubismExpressionUpdater = $.CubismExpressionUpdater;
|
||||||
|
export type CubismExpressionUpdater = $.CubismExpressionUpdater;
|
||||||
|
}
|
||||||
@ -0,0 +1,76 @@
|
|||||||
|
/**
|
||||||
|
* Copyright(c) Live2D Inc. All rights reserved.
|
||||||
|
*
|
||||||
|
* Use of this source code is governed by the Live2D Open Software license
|
||||||
|
* that can be found at https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { ICubismUpdater, CubismUpdateOrder } from './icubismupdater';
|
||||||
|
import { CubismModel } from '../model/cubismmodel';
|
||||||
|
import { CubismEyeBlink } from '../effect/cubismeyeblink';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updater for eye blink effects.
|
||||||
|
* Handles the management of eye blink animation through the CubismEyeBlink class.
|
||||||
|
*/
|
||||||
|
export class CubismEyeBlinkUpdater extends ICubismUpdater {
|
||||||
|
private _motionUpdated: () => boolean;
|
||||||
|
private _eyeBlink: CubismEyeBlink;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
*
|
||||||
|
* @param motionUpdated Motion update flag reference
|
||||||
|
* @param eyeBlink CubismEyeBlink reference
|
||||||
|
*/
|
||||||
|
constructor(motionUpdated: () => boolean, eyeBlink: CubismEyeBlink);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
*
|
||||||
|
* @param motionUpdated Motion update flag reference
|
||||||
|
* @param eyeBlink CubismEyeBlink reference
|
||||||
|
* @param executionOrder Order of operations
|
||||||
|
*/
|
||||||
|
constructor(
|
||||||
|
motionUpdated: () => boolean,
|
||||||
|
eyeBlink: CubismEyeBlink,
|
||||||
|
executionOrder: number
|
||||||
|
);
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
motionUpdated: () => boolean,
|
||||||
|
eyeBlink: CubismEyeBlink,
|
||||||
|
executionOrder?: number
|
||||||
|
) {
|
||||||
|
super(executionOrder ?? CubismUpdateOrder.CubismUpdateOrder_EyeBlink);
|
||||||
|
this._motionUpdated = motionUpdated;
|
||||||
|
this._eyeBlink = eyeBlink;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update process.
|
||||||
|
*
|
||||||
|
* @param model Model to update
|
||||||
|
* @param deltaTimeSeconds Delta time in seconds.
|
||||||
|
*/
|
||||||
|
onLateUpdate(model: CubismModel, deltaTimeSeconds: number): void {
|
||||||
|
if (!model) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this._motionUpdated()) {
|
||||||
|
// メインモーションの更新がないとき
|
||||||
|
// 目パチ
|
||||||
|
this._eyeBlink.updateParameters(model, deltaTimeSeconds);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Namespace definition for compatibility.
|
||||||
|
import * as $ from './cubismeyeblinkupdater';
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-namespace
|
||||||
|
export namespace Live2DCubismFramework {
|
||||||
|
export const CubismEyeBlinkUpdater = $.CubismEyeBlinkUpdater;
|
||||||
|
export type CubismEyeBlinkUpdater = $.CubismEyeBlinkUpdater;
|
||||||
|
}
|
||||||
104
avatar-h5-renderer/framework/src/motion/cubismlipsyncupdater.ts
Normal file
104
avatar-h5-renderer/framework/src/motion/cubismlipsyncupdater.ts
Normal file
@ -0,0 +1,104 @@
|
|||||||
|
/**
|
||||||
|
* Copyright(c) Live2D Inc. All rights reserved.
|
||||||
|
*
|
||||||
|
* Use of this source code is governed by the Live2D Open Software license
|
||||||
|
* that can be found at https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { ICubismUpdater, CubismUpdateOrder } from './icubismupdater';
|
||||||
|
import { CubismModel } from '../model/cubismmodel';
|
||||||
|
import { CubismIdHandle } from '../id/cubismid';
|
||||||
|
import { IParameterProvider } from './iparameterprovider';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updater for lip sync effects.
|
||||||
|
* Handles the management of lip sync animation through parameter providers.
|
||||||
|
*/
|
||||||
|
export class CubismLipSyncUpdater extends ICubismUpdater {
|
||||||
|
private _lipSyncIds: Array<CubismIdHandle>;
|
||||||
|
private _audioProvider: IParameterProvider | null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
*
|
||||||
|
* @param lipSyncIds Array of lip sync parameter IDs
|
||||||
|
* @param audioProvider Audio parameter provider
|
||||||
|
*/
|
||||||
|
constructor(
|
||||||
|
lipSyncIds: Array<CubismIdHandle>,
|
||||||
|
audioProvider: IParameterProvider | null
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
*
|
||||||
|
* @param lipSyncIds Array of lip sync parameter IDs
|
||||||
|
* @param audioProvider Audio parameter provider
|
||||||
|
* @param executionOrder Order of operations
|
||||||
|
*/
|
||||||
|
constructor(
|
||||||
|
lipSyncIds: Array<CubismIdHandle>,
|
||||||
|
audioProvider: IParameterProvider | null,
|
||||||
|
executionOrder: number
|
||||||
|
);
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
lipSyncIds: Array<CubismIdHandle>,
|
||||||
|
audioProvider: IParameterProvider | null,
|
||||||
|
executionOrder?: number
|
||||||
|
) {
|
||||||
|
super(executionOrder ?? CubismUpdateOrder.CubismUpdateOrder_LipSync);
|
||||||
|
this._lipSyncIds = [...lipSyncIds]; // Copy array
|
||||||
|
this._audioProvider = audioProvider;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update process.
|
||||||
|
*
|
||||||
|
* @param model Model to update
|
||||||
|
* @param deltaTimeSeconds Delta time in seconds.
|
||||||
|
*/
|
||||||
|
onLateUpdate(model: CubismModel, deltaTimeSeconds: number): void {
|
||||||
|
if (!model) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this._audioProvider) {
|
||||||
|
const updateSuccessful = this._audioProvider.update(deltaTimeSeconds);
|
||||||
|
if (updateSuccessful) {
|
||||||
|
const lipSyncValue = this._audioProvider.getParameter();
|
||||||
|
|
||||||
|
// Apply lip sync value to all registered parameters
|
||||||
|
for (let i = 0; i < this._lipSyncIds.length; i++) {
|
||||||
|
model.addParameterValueById(this._lipSyncIds[i], lipSyncValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set audio parameter provider.
|
||||||
|
*
|
||||||
|
* @param audioProvider Audio parameter provider to set
|
||||||
|
*/
|
||||||
|
setAudioProvider(audioProvider: IParameterProvider | null): void {
|
||||||
|
this._audioProvider = audioProvider;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get audio parameter provider.
|
||||||
|
*
|
||||||
|
* @return Current audio parameter provider
|
||||||
|
*/
|
||||||
|
getAudioProvider(): IParameterProvider | null {
|
||||||
|
return this._audioProvider;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Namespace definition for compatibility.
|
||||||
|
import * as $ from './cubismlipsyncupdater';
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-namespace
|
||||||
|
export namespace Live2DCubismFramework {
|
||||||
|
export const CubismLipSyncUpdater = $.CubismLipSyncUpdater;
|
||||||
|
export type CubismLipSyncUpdater = $.CubismLipSyncUpdater;
|
||||||
|
}
|
||||||
77
avatar-h5-renderer/framework/src/motion/cubismlookupdater.ts
Normal file
77
avatar-h5-renderer/framework/src/motion/cubismlookupdater.ts
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
/**
|
||||||
|
* Copyright(c) Live2D Inc. All rights reserved.
|
||||||
|
*
|
||||||
|
* Use of this source code is governed by the Live2D Open Software license
|
||||||
|
* that can be found at https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { ICubismUpdater, CubismUpdateOrder } from './icubismupdater';
|
||||||
|
import { CubismModel } from '../model/cubismmodel';
|
||||||
|
import { CubismTargetPoint } from '../math/cubismtargetpoint';
|
||||||
|
import { CubismLook } from '../effect/cubismlook';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updater for look effects.
|
||||||
|
* Handles the management of dragging motion through the MotionQueueManager.
|
||||||
|
*/
|
||||||
|
export class CubismLookUpdater extends ICubismUpdater {
|
||||||
|
private _look: CubismLook;
|
||||||
|
private _dragManager: CubismTargetPoint;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
*
|
||||||
|
* @param look CubismLook reference
|
||||||
|
* @param dragManager CubismTargetPoint reference
|
||||||
|
*/
|
||||||
|
constructor(look: CubismLook, dragManager: CubismTargetPoint);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
*
|
||||||
|
* @param look CubismLook reference
|
||||||
|
* @param dragManager CubismTargetPoint reference
|
||||||
|
* @param executionOrder Order of operations
|
||||||
|
*/
|
||||||
|
constructor(
|
||||||
|
look: CubismLook,
|
||||||
|
dragManager: CubismTargetPoint,
|
||||||
|
executionOrder: number
|
||||||
|
);
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
look: CubismLook,
|
||||||
|
dragManager: CubismTargetPoint,
|
||||||
|
executionOrder?: number
|
||||||
|
) {
|
||||||
|
super(executionOrder ?? CubismUpdateOrder.CubismUpdateOrder_Drag);
|
||||||
|
this._look = look;
|
||||||
|
this._dragManager = dragManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update process.
|
||||||
|
*
|
||||||
|
* @param model Model to update
|
||||||
|
* @param deltaTimeSeconds Delta time in seconds.
|
||||||
|
*/
|
||||||
|
onLateUpdate(model: CubismModel, deltaTimeSeconds: number): void {
|
||||||
|
if (!model) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._dragManager.update(deltaTimeSeconds);
|
||||||
|
const dragX = this._dragManager.getX();
|
||||||
|
const dragY = this._dragManager.getY();
|
||||||
|
|
||||||
|
this._look.updateParameters(model, dragX, dragY);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Namespace definition for compatibility.
|
||||||
|
import * as $ from './cubismlookupdater';
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-namespace
|
||||||
|
export namespace Live2DCubismFramework {
|
||||||
|
export const CubismLookUpdater = $.CubismLookUpdater;
|
||||||
|
export type CubismLookUpdater = $.CubismLookUpdater;
|
||||||
|
}
|
||||||
1235
avatar-h5-renderer/framework/src/motion/cubismmotion.ts
Normal file
1235
avatar-h5-renderer/framework/src/motion/cubismmotion.ts
Normal file
File diff suppressed because it is too large
Load Diff
155
avatar-h5-renderer/framework/src/motion/cubismmotioninternal.ts
Normal file
155
avatar-h5-renderer/framework/src/motion/cubismmotioninternal.ts
Normal file
@ -0,0 +1,155 @@
|
|||||||
|
/**
|
||||||
|
* Copyright(c) Live2D Inc. All rights reserved.
|
||||||
|
*
|
||||||
|
* Use of this source code is governed by the Live2D Open Software license
|
||||||
|
* that can be found at https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { CubismIdHandle } from '../id/cubismid';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief モーションカーブの種類
|
||||||
|
*
|
||||||
|
* モーションカーブの種類。
|
||||||
|
*/
|
||||||
|
export enum CubismMotionCurveTarget {
|
||||||
|
CubismMotionCurveTarget_Model, // モデルに対して
|
||||||
|
CubismMotionCurveTarget_Parameter, // パラメータに対して
|
||||||
|
CubismMotionCurveTarget_PartOpacity // パーツの不透明度に対して
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief モーションカーブのセグメントの種類
|
||||||
|
*
|
||||||
|
* モーションカーブのセグメントの種類。
|
||||||
|
*/
|
||||||
|
export enum CubismMotionSegmentType {
|
||||||
|
CubismMotionSegmentType_Linear = 0, // リニア
|
||||||
|
CubismMotionSegmentType_Bezier = 1, // ベジェ曲線
|
||||||
|
CubismMotionSegmentType_Stepped = 2, // ステップ
|
||||||
|
CubismMotionSegmentType_InverseStepped = 3 // インバースステップ
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief モーションカーブの制御点
|
||||||
|
*
|
||||||
|
* モーションカーブの制御点。
|
||||||
|
*/
|
||||||
|
export class CubismMotionPoint {
|
||||||
|
time = 0.0; // 時間[秒]
|
||||||
|
value = 0.0; // 値
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* モーションカーブのセグメントの評価関数
|
||||||
|
*
|
||||||
|
* @param points モーションカーブの制御点リスト
|
||||||
|
* @param time 評価する時間[秒]
|
||||||
|
*/
|
||||||
|
export interface csmMotionSegmentEvaluationFunction {
|
||||||
|
(points: CubismMotionPoint[], time: number): number;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief モーションカーブのセグメント
|
||||||
|
*
|
||||||
|
* モーションカーブのセグメント。
|
||||||
|
*/
|
||||||
|
export class CubismMotionSegment {
|
||||||
|
/**
|
||||||
|
* @brief コンストラクタ
|
||||||
|
*
|
||||||
|
* コンストラクタ。
|
||||||
|
*/
|
||||||
|
public constructor() {
|
||||||
|
this.evaluate = null;
|
||||||
|
this.basePointIndex = 0;
|
||||||
|
this.segmentType = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
evaluate: csmMotionSegmentEvaluationFunction; // 使用する評価関数
|
||||||
|
basePointIndex: number; // 最初のセグメントへのインデックス
|
||||||
|
segmentType: number; // セグメントの種類
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief モーションカーブ
|
||||||
|
*
|
||||||
|
* モーションカーブ。
|
||||||
|
*/
|
||||||
|
export class CubismMotionCurve {
|
||||||
|
public constructor() {
|
||||||
|
this.type = CubismMotionCurveTarget.CubismMotionCurveTarget_Model;
|
||||||
|
this.segmentCount = 0;
|
||||||
|
this.baseSegmentIndex = 0;
|
||||||
|
this.fadeInTime = 0.0;
|
||||||
|
this.fadeOutTime = 0.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
type: CubismMotionCurveTarget; // カーブの種類
|
||||||
|
id: CubismIdHandle; // カーブのID
|
||||||
|
segmentCount: number; // セグメントの個数
|
||||||
|
baseSegmentIndex: number; // 最初のセグメントのインデックス
|
||||||
|
fadeInTime: number; // フェードインにかかる時間[秒]
|
||||||
|
fadeOutTime: number; // フェードアウトにかかる時間[秒]
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* イベント。
|
||||||
|
*/
|
||||||
|
export class CubismMotionEvent {
|
||||||
|
fireTime = 0.0;
|
||||||
|
value: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief モーションデータ
|
||||||
|
*
|
||||||
|
* モーションデータ。
|
||||||
|
*/
|
||||||
|
export class CubismMotionData {
|
||||||
|
public constructor() {
|
||||||
|
this.duration = 0.0;
|
||||||
|
this.loop = false;
|
||||||
|
this.curveCount = 0;
|
||||||
|
this.eventCount = 0;
|
||||||
|
this.fps = 0.0;
|
||||||
|
|
||||||
|
this.curves = new Array<CubismMotionCurve>();
|
||||||
|
this.segments = new Array<CubismMotionSegment>();
|
||||||
|
this.points = new Array<CubismMotionPoint>();
|
||||||
|
this.events = new Array<CubismMotionEvent>();
|
||||||
|
}
|
||||||
|
|
||||||
|
duration: number; // モーションの長さ[秒]
|
||||||
|
loop: boolean; // ループするかどうか
|
||||||
|
curveCount: number; // カーブの個数
|
||||||
|
eventCount: number; // UserDataの個数
|
||||||
|
fps: number; // フレームレート
|
||||||
|
curves: Array<CubismMotionCurve>; // カーブのリスト
|
||||||
|
segments: Array<CubismMotionSegment>; // セグメントのリスト
|
||||||
|
points: Array<CubismMotionPoint>; // ポイントのリスト
|
||||||
|
events: Array<CubismMotionEvent>; // イベントのリスト
|
||||||
|
}
|
||||||
|
|
||||||
|
// Namespace definition for compatibility.
|
||||||
|
import * as $ from './cubismmotioninternal';
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-namespace
|
||||||
|
export namespace Live2DCubismFramework {
|
||||||
|
export const CubismMotionCurve = $.CubismMotionCurve;
|
||||||
|
export type CubismMotionCurve = $.CubismMotionCurve;
|
||||||
|
export const CubismMotionCurveTarget = $.CubismMotionCurveTarget;
|
||||||
|
export type CubismMotionCurveTarget = $.CubismMotionCurveTarget;
|
||||||
|
export const CubismMotionData = $.CubismMotionData;
|
||||||
|
export type CubismMotionData = $.CubismMotionData;
|
||||||
|
export const CubismMotionEvent = $.CubismMotionEvent;
|
||||||
|
export type CubismMotionEvent = $.CubismMotionEvent;
|
||||||
|
export const CubismMotionPoint = $.CubismMotionPoint;
|
||||||
|
export type CubismMotionPoint = $.CubismMotionPoint;
|
||||||
|
export const CubismMotionSegment = $.CubismMotionSegment;
|
||||||
|
export type CubismMotionSegment = $.CubismMotionSegment;
|
||||||
|
export const CubismMotionSegmentType = $.CubismMotionSegmentType;
|
||||||
|
export type CubismMotionSegmentType = $.CubismMotionSegmentType;
|
||||||
|
export type csmMotionSegmentEvaluationFunction =
|
||||||
|
$.csmMotionSegmentEvaluationFunction;
|
||||||
|
}
|
||||||
463
avatar-h5-renderer/framework/src/motion/cubismmotionjson.ts
Normal file
463
avatar-h5-renderer/framework/src/motion/cubismmotionjson.ts
Normal file
@ -0,0 +1,463 @@
|
|||||||
|
/**
|
||||||
|
* Copyright(c) Live2D Inc. All rights reserved.
|
||||||
|
*
|
||||||
|
* Use of this source code is governed by the Live2D Open Software license
|
||||||
|
* that can be found at https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { CubismIdHandle } from '../id/cubismid';
|
||||||
|
import { CubismFramework } from '../live2dcubismframework';
|
||||||
|
import { CSM_ASSERT, CubismLogWarning } from '../utils/cubismdebug';
|
||||||
|
import { CubismJson, JsonMap } from '../utils/cubismjson';
|
||||||
|
import { CubismMotionSegmentType } from './cubismmotioninternal';
|
||||||
|
|
||||||
|
// JSON keys
|
||||||
|
const Meta = 'Meta';
|
||||||
|
const Duration = 'Duration';
|
||||||
|
const Loop = 'Loop';
|
||||||
|
const AreBeziersRestricted = 'AreBeziersRestricted';
|
||||||
|
const CurveCount = 'CurveCount';
|
||||||
|
const Fps = 'Fps';
|
||||||
|
const TotalSegmentCount = 'TotalSegmentCount';
|
||||||
|
const TotalPointCount = 'TotalPointCount';
|
||||||
|
const Curves = 'Curves';
|
||||||
|
const Target = 'Target';
|
||||||
|
const Id = 'Id';
|
||||||
|
const FadeInTime = 'FadeInTime';
|
||||||
|
const FadeOutTime = 'FadeOutTime';
|
||||||
|
const Segments = 'Segments';
|
||||||
|
const UserData = 'UserData';
|
||||||
|
const UserDataCount = 'UserDataCount';
|
||||||
|
const TotalUserDataSize = 'TotalUserDataSize';
|
||||||
|
const Time = 'Time';
|
||||||
|
const Value = 'Value';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* motion3.jsonのコンテナ。
|
||||||
|
*/
|
||||||
|
export class CubismMotionJson {
|
||||||
|
/**
|
||||||
|
* コンストラクタ
|
||||||
|
* @param buffer motion3.jsonが読み込まれているバッファ
|
||||||
|
* @param size バッファのサイズ
|
||||||
|
*/
|
||||||
|
public constructor(buffer: ArrayBuffer, size: number) {
|
||||||
|
this._json = CubismJson.create(buffer, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* デストラクタ相当の処理
|
||||||
|
*/
|
||||||
|
public release(): void {
|
||||||
|
CubismJson.delete(this._json);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* モーションの長さを取得する
|
||||||
|
* @return モーションの長さ[秒]
|
||||||
|
*/
|
||||||
|
public getMotionDuration(): number {
|
||||||
|
return this._json
|
||||||
|
.getRoot()
|
||||||
|
.getValueByString(Meta)
|
||||||
|
.getValueByString(Duration)
|
||||||
|
.toFloat();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* モーションのループ情報の取得
|
||||||
|
* @return true ループする
|
||||||
|
* @return false ループしない
|
||||||
|
*/
|
||||||
|
public isMotionLoop(): boolean {
|
||||||
|
return this._json
|
||||||
|
.getRoot()
|
||||||
|
.getValueByString(Meta)
|
||||||
|
.getValueByString(Loop)
|
||||||
|
.toBoolean();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* motion3.jsonファイルの整合性チェック
|
||||||
|
*
|
||||||
|
* @return 正常なファイルの場合はtrueを返す。
|
||||||
|
*/
|
||||||
|
hasConsistency(): boolean {
|
||||||
|
let result = true;
|
||||||
|
|
||||||
|
if (!this._json || !this._json.getRoot()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const actualCurveListSize = this._json
|
||||||
|
.getRoot()
|
||||||
|
.getValueByString(Curves)
|
||||||
|
.getVector().length;
|
||||||
|
let actualTotalSegmentCount = 0;
|
||||||
|
let actualTotalPointCount = 0;
|
||||||
|
|
||||||
|
// カウント処理
|
||||||
|
for (
|
||||||
|
let curvePosition = 0;
|
||||||
|
curvePosition < actualCurveListSize;
|
||||||
|
++curvePosition
|
||||||
|
) {
|
||||||
|
for (
|
||||||
|
let segmentPosition = 0;
|
||||||
|
segmentPosition < this.getMotionCurveSegmentCount(curvePosition);
|
||||||
|
) {
|
||||||
|
if (segmentPosition == 0) {
|
||||||
|
actualTotalPointCount += 1;
|
||||||
|
segmentPosition += 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
const segment = this.getMotionCurveSegment(
|
||||||
|
curvePosition,
|
||||||
|
segmentPosition
|
||||||
|
) as CubismMotionSegmentType;
|
||||||
|
|
||||||
|
switch (segment) {
|
||||||
|
case CubismMotionSegmentType.CubismMotionSegmentType_Linear:
|
||||||
|
actualTotalPointCount += 1;
|
||||||
|
segmentPosition += 3;
|
||||||
|
break;
|
||||||
|
case CubismMotionSegmentType.CubismMotionSegmentType_Bezier:
|
||||||
|
actualTotalPointCount += 3;
|
||||||
|
segmentPosition += 7;
|
||||||
|
break;
|
||||||
|
case CubismMotionSegmentType.CubismMotionSegmentType_Stepped:
|
||||||
|
actualTotalPointCount += 1;
|
||||||
|
segmentPosition += 3;
|
||||||
|
break;
|
||||||
|
case CubismMotionSegmentType.CubismMotionSegmentType_InverseStepped:
|
||||||
|
actualTotalPointCount += 1;
|
||||||
|
segmentPosition += 3;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
CSM_ASSERT(0);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
++actualTotalSegmentCount;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 個数チェック
|
||||||
|
if (actualCurveListSize != this.getMotionCurveCount()) {
|
||||||
|
CubismLogWarning('The number of curves does not match the metadata.');
|
||||||
|
result = false;
|
||||||
|
}
|
||||||
|
if (actualTotalSegmentCount != this.getMotionTotalSegmentCount()) {
|
||||||
|
CubismLogWarning('The number of segment does not match the metadata.');
|
||||||
|
result = false;
|
||||||
|
}
|
||||||
|
if (actualTotalPointCount != this.getMotionTotalPointCount()) {
|
||||||
|
CubismLogWarning('The number of point does not match the metadata.');
|
||||||
|
result = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public getEvaluationOptionFlag(flagType: EvaluationOptionFlag): boolean {
|
||||||
|
if (
|
||||||
|
EvaluationOptionFlag.EvaluationOptionFlag_AreBeziersRistricted == flagType
|
||||||
|
) {
|
||||||
|
return this._json
|
||||||
|
.getRoot()
|
||||||
|
.getValueByString(Meta)
|
||||||
|
.getValueByString(AreBeziersRestricted)
|
||||||
|
.toBoolean();
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* モーションカーブの個数の取得
|
||||||
|
* @return モーションカーブの個数
|
||||||
|
*/
|
||||||
|
public getMotionCurveCount(): number {
|
||||||
|
return this._json
|
||||||
|
.getRoot()
|
||||||
|
.getValueByString(Meta)
|
||||||
|
.getValueByString(CurveCount)
|
||||||
|
.toInt();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* モーションのフレームレートの取得
|
||||||
|
* @return フレームレート[FPS]
|
||||||
|
*/
|
||||||
|
public getMotionFps(): number {
|
||||||
|
return this._json
|
||||||
|
.getRoot()
|
||||||
|
.getValueByString(Meta)
|
||||||
|
.getValueByString(Fps)
|
||||||
|
.toFloat();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* モーションのセグメントの総合計の取得
|
||||||
|
* @return モーションのセグメントの取得
|
||||||
|
*/
|
||||||
|
public getMotionTotalSegmentCount(): number {
|
||||||
|
return this._json
|
||||||
|
.getRoot()
|
||||||
|
.getValueByString(Meta)
|
||||||
|
.getValueByString(TotalSegmentCount)
|
||||||
|
.toInt();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* モーションのカーブの制御店の総合計の取得
|
||||||
|
* @return モーションのカーブの制御点の総合計
|
||||||
|
*/
|
||||||
|
public getMotionTotalPointCount(): number {
|
||||||
|
return this._json
|
||||||
|
.getRoot()
|
||||||
|
.getValueByString(Meta)
|
||||||
|
.getValueByString(TotalPointCount)
|
||||||
|
.toInt();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* モーションのフェードイン時間の存在
|
||||||
|
* @return true 存在する
|
||||||
|
* @return false 存在しない
|
||||||
|
*/
|
||||||
|
public isExistMotionFadeInTime(): boolean {
|
||||||
|
return !this._json
|
||||||
|
.getRoot()
|
||||||
|
.getValueByString(Meta)
|
||||||
|
.getValueByString(FadeInTime)
|
||||||
|
.isNull();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* モーションのフェードアウト時間の存在
|
||||||
|
* @return true 存在する
|
||||||
|
* @return false 存在しない
|
||||||
|
*/
|
||||||
|
public isExistMotionFadeOutTime(): boolean {
|
||||||
|
return !this._json
|
||||||
|
.getRoot()
|
||||||
|
.getValueByString(Meta)
|
||||||
|
.getValueByString(FadeOutTime)
|
||||||
|
.isNull();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* モーションのフェードイン時間の取得
|
||||||
|
* @return フェードイン時間[秒]
|
||||||
|
*/
|
||||||
|
public getMotionFadeInTime(): number {
|
||||||
|
return this._json
|
||||||
|
.getRoot()
|
||||||
|
.getValueByString(Meta)
|
||||||
|
.getValueByString(FadeInTime)
|
||||||
|
.toFloat();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* モーションのフェードアウト時間の取得
|
||||||
|
* @return フェードアウト時間[秒]
|
||||||
|
*/
|
||||||
|
public getMotionFadeOutTime(): number {
|
||||||
|
return this._json
|
||||||
|
.getRoot()
|
||||||
|
.getValueByString(Meta)
|
||||||
|
.getValueByString(FadeOutTime)
|
||||||
|
.toFloat();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* モーションのカーブの種類の取得
|
||||||
|
* @param curveIndex カーブのインデックス
|
||||||
|
* @return カーブの種類
|
||||||
|
*/
|
||||||
|
public getMotionCurveTarget(curveIndex: number): string {
|
||||||
|
return this._json
|
||||||
|
.getRoot()
|
||||||
|
.getValueByString(Curves)
|
||||||
|
.getValueByIndex(curveIndex)
|
||||||
|
.getValueByString(Target)
|
||||||
|
.getRawString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* モーションのカーブのIDの取得
|
||||||
|
* @param curveIndex カーブのインデックス
|
||||||
|
* @return カーブのID
|
||||||
|
*/
|
||||||
|
public getMotionCurveId(curveIndex: number): CubismIdHandle {
|
||||||
|
return CubismFramework.getIdManager().getId(
|
||||||
|
this._json
|
||||||
|
.getRoot()
|
||||||
|
.getValueByString(Curves)
|
||||||
|
.getValueByIndex(curveIndex)
|
||||||
|
.getValueByString(Id)
|
||||||
|
.getRawString()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* モーションのカーブのフェードイン時間の存在
|
||||||
|
* @param curveIndex カーブのインデックス
|
||||||
|
* @return true 存在する
|
||||||
|
* @return false 存在しない
|
||||||
|
*/
|
||||||
|
public isExistMotionCurveFadeInTime(curveIndex: number): boolean {
|
||||||
|
return !this._json
|
||||||
|
.getRoot()
|
||||||
|
.getValueByString(Curves)
|
||||||
|
.getValueByIndex(curveIndex)
|
||||||
|
.getValueByString(FadeInTime)
|
||||||
|
.isNull();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* モーションのカーブのフェードアウト時間の存在
|
||||||
|
* @param curveIndex カーブのインデックス
|
||||||
|
* @return true 存在する
|
||||||
|
* @return false 存在しない
|
||||||
|
*/
|
||||||
|
public isExistMotionCurveFadeOutTime(curveIndex: number): boolean {
|
||||||
|
return !this._json
|
||||||
|
.getRoot()
|
||||||
|
.getValueByString(Curves)
|
||||||
|
.getValueByIndex(curveIndex)
|
||||||
|
.getValueByString(FadeOutTime)
|
||||||
|
.isNull();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* モーションのカーブのフェードイン時間の取得
|
||||||
|
* @param curveIndex カーブのインデックス
|
||||||
|
* @return フェードイン時間[秒]
|
||||||
|
*/
|
||||||
|
public getMotionCurveFadeInTime(curveIndex: number): number {
|
||||||
|
return this._json
|
||||||
|
.getRoot()
|
||||||
|
.getValueByString(Curves)
|
||||||
|
.getValueByIndex(curveIndex)
|
||||||
|
.getValueByString(FadeInTime)
|
||||||
|
.toFloat();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* モーションのカーブのフェードアウト時間の取得
|
||||||
|
* @param curveIndex カーブのインデックス
|
||||||
|
* @return フェードアウト時間[秒]
|
||||||
|
*/
|
||||||
|
public getMotionCurveFadeOutTime(curveIndex: number): number {
|
||||||
|
return this._json
|
||||||
|
.getRoot()
|
||||||
|
.getValueByString(Curves)
|
||||||
|
.getValueByIndex(curveIndex)
|
||||||
|
.getValueByString(FadeOutTime)
|
||||||
|
.toFloat();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* モーションのカーブのセグメントの個数を取得する
|
||||||
|
* @param curveIndex カーブのインデックス
|
||||||
|
* @return モーションのカーブのセグメントの個数
|
||||||
|
*/
|
||||||
|
public getMotionCurveSegmentCount(curveIndex: number): number {
|
||||||
|
return this._json
|
||||||
|
.getRoot()
|
||||||
|
.getValueByString(Curves)
|
||||||
|
.getValueByIndex(curveIndex)
|
||||||
|
.getValueByString(Segments)
|
||||||
|
.getVector().length;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* モーションのカーブのセグメントの値の取得
|
||||||
|
* @param curveIndex カーブのインデックス
|
||||||
|
* @param segmentIndex セグメントのインデックス
|
||||||
|
* @return セグメントの値
|
||||||
|
*/
|
||||||
|
public getMotionCurveSegment(
|
||||||
|
curveIndex: number,
|
||||||
|
segmentIndex: number
|
||||||
|
): number {
|
||||||
|
return this._json
|
||||||
|
.getRoot()
|
||||||
|
.getValueByString(Curves)
|
||||||
|
.getValueByIndex(curveIndex)
|
||||||
|
.getValueByString(Segments)
|
||||||
|
.getValueByIndex(segmentIndex)
|
||||||
|
.toFloat();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* イベントの個数の取得
|
||||||
|
* @return イベントの個数
|
||||||
|
*/
|
||||||
|
public getEventCount(): number {
|
||||||
|
return this._json
|
||||||
|
.getRoot()
|
||||||
|
.getValueByString(Meta)
|
||||||
|
.getValueByString(UserDataCount)
|
||||||
|
.toInt();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* イベントの総文字数の取得
|
||||||
|
* @return イベントの総文字数
|
||||||
|
*/
|
||||||
|
public getTotalEventValueSize(): number {
|
||||||
|
return this._json
|
||||||
|
.getRoot()
|
||||||
|
.getValueByString(Meta)
|
||||||
|
.getValueByString(TotalUserDataSize)
|
||||||
|
.toInt();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* イベントの時間の取得
|
||||||
|
* @param userDataIndex イベントのインデックス
|
||||||
|
* @return イベントの時間[秒]
|
||||||
|
*/
|
||||||
|
public getEventTime(userDataIndex: number): number {
|
||||||
|
return this._json
|
||||||
|
.getRoot()
|
||||||
|
.getValueByString(UserData)
|
||||||
|
.getValueByIndex(userDataIndex)
|
||||||
|
.getValueByString(Time)
|
||||||
|
.toFloat();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* イベントの取得
|
||||||
|
* @param userDataIndex イベントのインデックス
|
||||||
|
* @return イベントの文字列
|
||||||
|
*/
|
||||||
|
public getEventValue(userDataIndex: number): string {
|
||||||
|
return this._json
|
||||||
|
.getRoot()
|
||||||
|
.getValueByString(UserData)
|
||||||
|
.getValueByIndex(userDataIndex)
|
||||||
|
.getValueByString(Value)
|
||||||
|
.getRawString();
|
||||||
|
}
|
||||||
|
|
||||||
|
_json: CubismJson; // motion3.jsonのデータ
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief ベジェカーブの解釈方法のフラグタイプ
|
||||||
|
*/
|
||||||
|
export enum EvaluationOptionFlag {
|
||||||
|
EvaluationOptionFlag_AreBeziersRistricted = 0 ///< ベジェハンドルの規制状態
|
||||||
|
}
|
||||||
|
|
||||||
|
// Namespace definition for compatibility.
|
||||||
|
import * as $ from './cubismmotionjson';
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-namespace
|
||||||
|
export namespace Live2DCubismFramework {
|
||||||
|
export const CubismMotionJson = $.CubismMotionJson;
|
||||||
|
export type CubismMotionJson = $.CubismMotionJson;
|
||||||
|
}
|
||||||
126
avatar-h5-renderer/framework/src/motion/cubismmotionmanager.ts
Normal file
126
avatar-h5-renderer/framework/src/motion/cubismmotionmanager.ts
Normal file
@ -0,0 +1,126 @@
|
|||||||
|
/**
|
||||||
|
* Copyright(c) Live2D Inc. All rights reserved.
|
||||||
|
*
|
||||||
|
* Use of this source code is governed by the Live2D Open Software license
|
||||||
|
* that can be found at https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { CubismModel } from '../model/cubismmodel';
|
||||||
|
import { ACubismMotion } from './acubismmotion';
|
||||||
|
import {
|
||||||
|
CubismMotionQueueEntryHandle,
|
||||||
|
CubismMotionQueueManager
|
||||||
|
} from './cubismmotionqueuemanager';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* モーションの管理
|
||||||
|
*
|
||||||
|
* モーションの管理を行うクラス
|
||||||
|
*/
|
||||||
|
export class CubismMotionManager extends CubismMotionQueueManager {
|
||||||
|
/**
|
||||||
|
* コンストラクタ
|
||||||
|
*/
|
||||||
|
public constructor() {
|
||||||
|
super();
|
||||||
|
this._currentPriority = 0;
|
||||||
|
this._reservePriority = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 再生中のモーションの優先度の取得
|
||||||
|
* @return モーションの優先度
|
||||||
|
*/
|
||||||
|
public getCurrentPriority(): number {
|
||||||
|
return this._currentPriority;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 予約中のモーションの優先度を取得する。
|
||||||
|
* @return モーションの優先度
|
||||||
|
*/
|
||||||
|
public getReservePriority(): number {
|
||||||
|
return this._reservePriority;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 予約中のモーションの優先度を設定する。
|
||||||
|
* @param val 優先度
|
||||||
|
*/
|
||||||
|
public setReservePriority(val: number): void {
|
||||||
|
this._reservePriority = val;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 優先度を設定してモーションを開始する。
|
||||||
|
*
|
||||||
|
* @param motion モーション
|
||||||
|
* @param autoDelete 再生が狩猟したモーションのインスタンスを削除するならtrue
|
||||||
|
* @param priority 優先度
|
||||||
|
* @return 開始したモーションの識別番号を返す。個別のモーションが終了したか否かを判定するIsFinished()の引数で使用する。開始できない時は「-1」
|
||||||
|
*/
|
||||||
|
public startMotionPriority(
|
||||||
|
motion: ACubismMotion,
|
||||||
|
autoDelete: boolean,
|
||||||
|
priority: number
|
||||||
|
): CubismMotionQueueEntryHandle {
|
||||||
|
if (priority == this._reservePriority) {
|
||||||
|
this._reservePriority = 0; // 予約を解除
|
||||||
|
}
|
||||||
|
|
||||||
|
this._currentPriority = priority; // 再生中モーションの優先度を設定
|
||||||
|
|
||||||
|
return super.startMotion(motion, autoDelete);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* モーションを更新して、モデルにパラメータ値を反映する。
|
||||||
|
*
|
||||||
|
* @param model 対象のモデル
|
||||||
|
* @param deltaTimeSeconds デルタ時間[秒]
|
||||||
|
* @return true 更新されている
|
||||||
|
* @return false 更新されていない
|
||||||
|
*/
|
||||||
|
public updateMotion(model: CubismModel, deltaTimeSeconds: number): boolean {
|
||||||
|
this._userTimeSeconds += deltaTimeSeconds;
|
||||||
|
|
||||||
|
const updated: boolean = super.doUpdateMotion(model, this._userTimeSeconds);
|
||||||
|
|
||||||
|
if (this.isFinished()) {
|
||||||
|
this._currentPriority = 0; // 再生中のモーションの優先度を解除
|
||||||
|
}
|
||||||
|
|
||||||
|
return updated;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* モーションを予約する。
|
||||||
|
*
|
||||||
|
* @param priority 優先度
|
||||||
|
* @return true 予約できた
|
||||||
|
* @return false 予約できなかった
|
||||||
|
*/
|
||||||
|
public reserveMotion(priority: number): boolean {
|
||||||
|
if (
|
||||||
|
priority <= this._reservePriority ||
|
||||||
|
priority <= this._currentPriority
|
||||||
|
) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._reservePriority = priority;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
_currentPriority: number; // 現在再生中のモーションの優先度
|
||||||
|
_reservePriority: number; // 再生予定のモーションの優先度。再生中は0になる。モーションファイルを別スレッドで読み込むときの機能。
|
||||||
|
}
|
||||||
|
|
||||||
|
// Namespace definition for compatibility.
|
||||||
|
import * as $ from './cubismmotionmanager';
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-namespace
|
||||||
|
export namespace Live2DCubismFramework {
|
||||||
|
export const CubismMotionManager = $.CubismMotionManager;
|
||||||
|
export type CubismMotionManager = $.CubismMotionManager;
|
||||||
|
}
|
||||||
@ -0,0 +1,262 @@
|
|||||||
|
/**
|
||||||
|
* Copyright(c) Live2D Inc. All rights reserved.
|
||||||
|
*
|
||||||
|
* Use of this source code is governed by the Live2D Open Software license
|
||||||
|
* that can be found at https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { ACubismMotion } from './acubismmotion';
|
||||||
|
import { CubismMotionQueueEntryHandle } from './cubismmotionqueuemanager';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CubismMotionQueueManagerで再生している各モーションの管理クラス。
|
||||||
|
*/
|
||||||
|
export class CubismMotionQueueEntry {
|
||||||
|
/**
|
||||||
|
* コンストラクタ
|
||||||
|
*/
|
||||||
|
public constructor() {
|
||||||
|
this._autoDelete = false;
|
||||||
|
this._motion = null;
|
||||||
|
this._available = true;
|
||||||
|
this._finished = false;
|
||||||
|
this._started = false;
|
||||||
|
this._startTimeSeconds = -1.0;
|
||||||
|
this._fadeInStartTimeSeconds = 0.0;
|
||||||
|
this._endTimeSeconds = -1.0;
|
||||||
|
this._stateTimeSeconds = 0.0;
|
||||||
|
this._stateWeight = 0.0;
|
||||||
|
this._lastEventCheckSeconds = 0.0;
|
||||||
|
this._motionQueueEntryHandle = this;
|
||||||
|
this._fadeOutSeconds = 0.0;
|
||||||
|
this._isTriggeredFadeOut = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* デストラクタ相当の処理
|
||||||
|
*/
|
||||||
|
public release(): void {
|
||||||
|
if (this._autoDelete && this._motion) {
|
||||||
|
ACubismMotion.delete(this._motion); //
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* フェードアウト時間と開始判定の設定
|
||||||
|
* @param fadeOutSeconds フェードアウトにかかる時間[秒]
|
||||||
|
*/
|
||||||
|
public setFadeOut(fadeOutSeconds: number): void {
|
||||||
|
this._fadeOutSeconds = fadeOutSeconds;
|
||||||
|
this._isTriggeredFadeOut = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* フェードアウトの開始
|
||||||
|
* @param fadeOutSeconds フェードアウトにかかる時間[秒]
|
||||||
|
* @param userTimeSeconds デルタ時間の積算値[秒]
|
||||||
|
*/
|
||||||
|
public startFadeOut(fadeOutSeconds: number, userTimeSeconds: number): void {
|
||||||
|
const newEndTimeSeconds: number = userTimeSeconds + fadeOutSeconds;
|
||||||
|
this._isTriggeredFadeOut = true;
|
||||||
|
|
||||||
|
if (
|
||||||
|
this._endTimeSeconds < 0.0 ||
|
||||||
|
newEndTimeSeconds < this._endTimeSeconds
|
||||||
|
) {
|
||||||
|
this._endTimeSeconds = newEndTimeSeconds;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* モーションの終了の確認
|
||||||
|
*
|
||||||
|
* @return true モーションが終了した
|
||||||
|
* @return false 終了していない
|
||||||
|
*/
|
||||||
|
public isFinished(): boolean {
|
||||||
|
return this._finished;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* モーションの開始の確認
|
||||||
|
* @return true モーションが開始した
|
||||||
|
* @return false 開始していない
|
||||||
|
*/
|
||||||
|
public isStarted(): boolean {
|
||||||
|
return this._started;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* モーションの開始時刻の取得
|
||||||
|
* @return モーションの開始時刻[秒]
|
||||||
|
*/
|
||||||
|
public getStartTime(): number {
|
||||||
|
return this._startTimeSeconds;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* フェードインの開始時刻の取得
|
||||||
|
* @return フェードインの開始時刻[秒]
|
||||||
|
*/
|
||||||
|
public getFadeInStartTime(): number {
|
||||||
|
return this._fadeInStartTimeSeconds;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* フェードインの終了時刻の取得
|
||||||
|
* @return フェードインの終了時刻の取得
|
||||||
|
*/
|
||||||
|
public getEndTime(): number {
|
||||||
|
return this._endTimeSeconds;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* モーションの開始時刻の設定
|
||||||
|
* @param startTime モーションの開始時刻
|
||||||
|
*/
|
||||||
|
public setStartTime(startTime: number): void {
|
||||||
|
this._startTimeSeconds = startTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* フェードインの開始時刻の設定
|
||||||
|
* @param startTime フェードインの開始時刻[秒]
|
||||||
|
*/
|
||||||
|
public setFadeInStartTime(startTime: number): void {
|
||||||
|
this._fadeInStartTimeSeconds = startTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* フェードインの終了時刻の設定
|
||||||
|
* @param endTime フェードインの終了時刻[秒]
|
||||||
|
*/
|
||||||
|
public setEndTime(endTime: number): void {
|
||||||
|
this._endTimeSeconds = endTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* モーションの終了の設定
|
||||||
|
* @param f trueならモーションの終了
|
||||||
|
*/
|
||||||
|
public setIsFinished(f: boolean): void {
|
||||||
|
this._finished = f;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* モーション開始の設定
|
||||||
|
* @param f trueならモーションの開始
|
||||||
|
*/
|
||||||
|
public setIsStarted(f: boolean): void {
|
||||||
|
this._started = f;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* モーションの有効性の確認
|
||||||
|
* @return true モーションは有効
|
||||||
|
* @return false モーションは無効
|
||||||
|
*/
|
||||||
|
public isAvailable(): boolean {
|
||||||
|
return this._available;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* モーションの有効性の設定
|
||||||
|
* @param v trueならモーションは有効
|
||||||
|
*/
|
||||||
|
public setIsAvailable(v: boolean): void {
|
||||||
|
this._available = v;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* モーションの状態の設定
|
||||||
|
* @param timeSeconds 現在時刻[秒]
|
||||||
|
* @param weight モーション尾重み
|
||||||
|
*/
|
||||||
|
public setState(timeSeconds: number, weight: number): void {
|
||||||
|
this._stateTimeSeconds = timeSeconds;
|
||||||
|
this._stateWeight = weight;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* モーションの現在時刻の取得
|
||||||
|
* @return モーションの現在時刻[秒]
|
||||||
|
*/
|
||||||
|
public getStateTime(): number {
|
||||||
|
return this._stateTimeSeconds;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* モーションの重みの取得
|
||||||
|
* @return モーションの重み
|
||||||
|
*/
|
||||||
|
public getStateWeight(): number {
|
||||||
|
return this._stateWeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 最後にイベントの発火をチェックした時間を取得
|
||||||
|
*
|
||||||
|
* @return 最後にイベントの発火をチェックした時間[秒]
|
||||||
|
*/
|
||||||
|
public getLastCheckEventSeconds(): number {
|
||||||
|
return this._lastEventCheckSeconds;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 最後にイベントをチェックした時間を設定
|
||||||
|
* @param checkSeconds 最後にイベントをチェックした時間[秒]
|
||||||
|
*/
|
||||||
|
public setLastCheckEventSeconds(checkSeconds: number): void {
|
||||||
|
this._lastEventCheckSeconds = checkSeconds;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* フェードアウト開始判定の取得
|
||||||
|
* @return フェードアウト開始するかどうか
|
||||||
|
*/
|
||||||
|
public isTriggeredFadeOut(): boolean {
|
||||||
|
return this._isTriggeredFadeOut;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* フェードアウト時間の取得
|
||||||
|
* @return フェードアウト時間[秒]
|
||||||
|
*/
|
||||||
|
public getFadeOutSeconds(): number {
|
||||||
|
return this._fadeOutSeconds;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* モーションの取得
|
||||||
|
*
|
||||||
|
* @return モーション
|
||||||
|
*/
|
||||||
|
public getCubismMotion(): ACubismMotion {
|
||||||
|
return this._motion;
|
||||||
|
}
|
||||||
|
|
||||||
|
_autoDelete: boolean; // 自動削除
|
||||||
|
_motion: ACubismMotion; // モーション
|
||||||
|
|
||||||
|
_available: boolean; // 有効化フラグ
|
||||||
|
_finished: boolean; // 終了フラグ
|
||||||
|
_started: boolean; // 開始フラグ
|
||||||
|
_startTimeSeconds: number; // モーション再生開始時刻[秒]
|
||||||
|
_fadeInStartTimeSeconds: number; // フェードイン開始時刻(ループの時は初回のみ)[秒]
|
||||||
|
_endTimeSeconds: number; // 終了予定時刻[秒]
|
||||||
|
_stateTimeSeconds: number; // 時刻の状態[秒]
|
||||||
|
_stateWeight: number; // 重みの状態
|
||||||
|
_lastEventCheckSeconds: number; // 最終のMotion側のチェックした時間
|
||||||
|
private _fadeOutSeconds: number; // フェードアウト時間[秒]
|
||||||
|
private _isTriggeredFadeOut: boolean; // フェードアウト開始フラグ
|
||||||
|
|
||||||
|
_motionQueueEntryHandle: CubismMotionQueueEntryHandle; // インスタンスごとに一意の値を持つ識別番号
|
||||||
|
}
|
||||||
|
|
||||||
|
// Namespace definition for compatibility.
|
||||||
|
import * as $ from './cubismmotionqueueentry';
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-namespace
|
||||||
|
export namespace Live2DCubismFramework {
|
||||||
|
export const CubismMotionQueueEntry = $.CubismMotionQueueEntry;
|
||||||
|
export type CubismMotionQueueEntry = $.CubismMotionQueueEntry;
|
||||||
|
}
|
||||||
@ -0,0 +1,329 @@
|
|||||||
|
/**
|
||||||
|
* Copyright(c) Live2D Inc. All rights reserved.
|
||||||
|
*
|
||||||
|
* Use of this source code is governed by the Live2D Open Software license
|
||||||
|
* that can be found at https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { ACubismMotion } from './acubismmotion';
|
||||||
|
import { CubismMotionQueueEntry } from './cubismmotionqueueentry';
|
||||||
|
import { CubismModel } from '../model/cubismmodel';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* モーション再生の管理
|
||||||
|
*
|
||||||
|
* モーション再生の管理用クラス。CubismMotionモーションなどACubismMotionのサブクラスを再生するために使用する。
|
||||||
|
*
|
||||||
|
* @note 再生中に別のモーションが StartMotion()された場合は、新しいモーションに滑らかに変化し旧モーションは中断する。
|
||||||
|
* 表情用モーション、体用モーションなどを分けてモーション化した場合など、
|
||||||
|
* 複数のモーションを同時に再生させる場合は、複数のCubismMotionQueueManagerインスタンスを使用する。
|
||||||
|
*/
|
||||||
|
export class CubismMotionQueueManager {
|
||||||
|
/**
|
||||||
|
* コンストラクタ
|
||||||
|
*/
|
||||||
|
public constructor() {
|
||||||
|
this._userTimeSeconds = 0.0;
|
||||||
|
this._eventCallBack = null;
|
||||||
|
this._eventCustomData = null;
|
||||||
|
this._motions = new Array<CubismMotionQueueEntry>();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* デストラクタ
|
||||||
|
*/
|
||||||
|
public release(): void {
|
||||||
|
for (let i = 0; i < this._motions.length; ++i) {
|
||||||
|
if (this._motions[i]) {
|
||||||
|
this._motions[i].release();
|
||||||
|
this._motions[i] = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this._motions = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 指定したモーションの開始
|
||||||
|
*
|
||||||
|
* 指定したモーションを開始する。同じタイプのモーションが既にある場合は、既存のモーションに終了フラグを立て、フェードアウトを開始させる。
|
||||||
|
*
|
||||||
|
* @param motion 開始するモーション
|
||||||
|
* @param autoDelete 再生が終了したモーションのインスタンスを削除するなら true
|
||||||
|
* @param userTimeSeconds Deprecated: デルタ時間の積算値[秒] 関数内で参照していないため使用は非推奨。
|
||||||
|
* @return 開始したモーションの識別番号を返す。個別のモーションが終了したか否かを判定するIsFinished()の引数で使用する。開始できない時は「-1」
|
||||||
|
*/
|
||||||
|
public startMotion(
|
||||||
|
motion: ACubismMotion,
|
||||||
|
autoDelete: boolean,
|
||||||
|
userTimeSeconds?: number
|
||||||
|
): CubismMotionQueueEntryHandle {
|
||||||
|
if (motion == null) {
|
||||||
|
return InvalidMotionQueueEntryHandleValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let motionQueueEntry: CubismMotionQueueEntry = null;
|
||||||
|
|
||||||
|
// 既にモーションがあれば終了フラグを立てる
|
||||||
|
for (let i = 0; i < this._motions.length; ++i) {
|
||||||
|
motionQueueEntry = this._motions[i];
|
||||||
|
if (motionQueueEntry == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
motionQueueEntry.setFadeOut(motionQueueEntry._motion.getFadeOutTime()); // フェードアウト設定
|
||||||
|
}
|
||||||
|
|
||||||
|
motionQueueEntry = new CubismMotionQueueEntry(); // 終了時に破棄する
|
||||||
|
motionQueueEntry._autoDelete = autoDelete;
|
||||||
|
motionQueueEntry._motion = motion;
|
||||||
|
|
||||||
|
this._motions.push(motionQueueEntry);
|
||||||
|
|
||||||
|
return motionQueueEntry._motionQueueEntryHandle;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 全てのモーションの終了の確認
|
||||||
|
* @return true 全て終了している
|
||||||
|
* @return false 終了していない
|
||||||
|
*/
|
||||||
|
public isFinished(): boolean {
|
||||||
|
// ------- 処理を行う -------
|
||||||
|
// 既にモーションがあれば終了フラグを立てる
|
||||||
|
|
||||||
|
for (let i = 0; i < this._motions.length; ) {
|
||||||
|
let motionQueueEntry: CubismMotionQueueEntry = this._motions[i];
|
||||||
|
|
||||||
|
if (motionQueueEntry == null) {
|
||||||
|
this._motions.splice(i, 1); // 削除
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const motion: ACubismMotion = motionQueueEntry._motion;
|
||||||
|
|
||||||
|
if (motion == null) {
|
||||||
|
motionQueueEntry.release();
|
||||||
|
motionQueueEntry = null;
|
||||||
|
this._motions.splice(i, 1); // 削除
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----- 終了済みの処理があれば削除する ------
|
||||||
|
if (!motionQueueEntry.isFinished()) {
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 指定したモーションの終了の確認
|
||||||
|
* @param motionQueueEntryNumber モーションの識別番号
|
||||||
|
* @return true 全て終了している
|
||||||
|
* @return false 終了していない
|
||||||
|
*/
|
||||||
|
public isFinishedByHandle(
|
||||||
|
motionQueueEntryNumber: CubismMotionQueueEntryHandle
|
||||||
|
): boolean {
|
||||||
|
for (let i = 0; i < this._motions.length; i++) {
|
||||||
|
const motionQueueEntry: CubismMotionQueueEntry = this._motions[i];
|
||||||
|
|
||||||
|
if (motionQueueEntry == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
motionQueueEntry._motionQueueEntryHandle == motionQueueEntryNumber &&
|
||||||
|
!motionQueueEntry.isFinished()
|
||||||
|
) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 全てのモーションを停止する
|
||||||
|
*/
|
||||||
|
public stopAllMotions(): void {
|
||||||
|
// ------- 処理を行う -------
|
||||||
|
// 既にモーションがあれば終了フラグを立てる
|
||||||
|
|
||||||
|
for (let i = 0; i < this._motions.length; i++) {
|
||||||
|
const motionQueueEntry: CubismMotionQueueEntry = this._motions[i];
|
||||||
|
|
||||||
|
if (motionQueueEntry == null) {
|
||||||
|
this._motions.splice(i, 1); // 削除
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----- 終了済みの処理があれば削除する ------
|
||||||
|
motionQueueEntry.release();
|
||||||
|
this._motions.splice(i, 1); // 削除
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief CubismMotionQueueEntryの配列の取得
|
||||||
|
*
|
||||||
|
* CubismMotionQueueEntryの配列を取得する。
|
||||||
|
*
|
||||||
|
* @return CubismMotionQueueEntryの配列へのポインタ
|
||||||
|
* NULL 見つからなかった
|
||||||
|
*/
|
||||||
|
public getCubismMotionQueueEntries(): Array<CubismMotionQueueEntry> {
|
||||||
|
return this._motions;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 指定したCubismMotionQueueEntryの取得
|
||||||
|
|
||||||
|
* @param motionQueueEntryNumber モーションの識別番号
|
||||||
|
* @return 指定したCubismMotionQueueEntry
|
||||||
|
* @return null 見つからなかった
|
||||||
|
*/
|
||||||
|
public getCubismMotionQueueEntry(
|
||||||
|
motionQueueEntryNumber: any
|
||||||
|
): CubismMotionQueueEntry {
|
||||||
|
//------- 処理を行う -------
|
||||||
|
|
||||||
|
for (let i = 0; i < this._motions.length; i++) {
|
||||||
|
const motionQueueEntry: CubismMotionQueueEntry = this._motions[i];
|
||||||
|
|
||||||
|
if (motionQueueEntry == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (motionQueueEntry._motionQueueEntryHandle == motionQueueEntryNumber) {
|
||||||
|
return motionQueueEntry;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* イベントを受け取るCallbackの登録
|
||||||
|
*
|
||||||
|
* @param callback コールバック関数
|
||||||
|
* @param customData コールバックに返されるデータ
|
||||||
|
*/
|
||||||
|
public setEventCallback(
|
||||||
|
callback: CubismMotionEventFunction,
|
||||||
|
customData: any = null
|
||||||
|
): void {
|
||||||
|
this._eventCallBack = callback;
|
||||||
|
this._eventCustomData = customData;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* モーションを更新して、モデルにパラメータ値を反映する。
|
||||||
|
*
|
||||||
|
* @param model 対象のモデル
|
||||||
|
* @param userTimeSeconds デルタ時間の積算値[秒]
|
||||||
|
* @return true モデルへパラメータ値の反映あり
|
||||||
|
* @return false モデルへパラメータ値の反映なし(モーションの変化なし)
|
||||||
|
*/
|
||||||
|
public doUpdateMotion(model: CubismModel, userTimeSeconds: number): boolean {
|
||||||
|
let updated = false;
|
||||||
|
|
||||||
|
// ------- 処理を行う --------
|
||||||
|
// 既にモーションがあれば終了フラグを立てる
|
||||||
|
|
||||||
|
for (let i = 0; i < this._motions.length; ) {
|
||||||
|
let motionQueueEntry: CubismMotionQueueEntry = this._motions[i];
|
||||||
|
|
||||||
|
if (motionQueueEntry == null) {
|
||||||
|
this._motions.splice(i, 1); // 削除
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const motion: ACubismMotion = motionQueueEntry._motion;
|
||||||
|
|
||||||
|
if (motion == null) {
|
||||||
|
motionQueueEntry.release();
|
||||||
|
motionQueueEntry = null;
|
||||||
|
this._motions.splice(i, 1); // 削除
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ------ 値を反映する ------
|
||||||
|
motion.updateParameters(model, motionQueueEntry, userTimeSeconds);
|
||||||
|
updated = true;
|
||||||
|
|
||||||
|
// ------ ユーザトリガーイベントを検査する ----
|
||||||
|
const firedList: Array<string> = motion.getFiredEvent(
|
||||||
|
motionQueueEntry.getLastCheckEventSeconds() -
|
||||||
|
motionQueueEntry.getStartTime(),
|
||||||
|
userTimeSeconds - motionQueueEntry.getStartTime()
|
||||||
|
);
|
||||||
|
|
||||||
|
for (let i = 0; i < firedList.length; ++i) {
|
||||||
|
this._eventCallBack(this, firedList[i], this._eventCustomData);
|
||||||
|
}
|
||||||
|
|
||||||
|
motionQueueEntry.setLastCheckEventSeconds(userTimeSeconds);
|
||||||
|
|
||||||
|
// ------ 終了済みの処理があれば削除する ------
|
||||||
|
if (motionQueueEntry.isFinished()) {
|
||||||
|
motionQueueEntry.release();
|
||||||
|
motionQueueEntry = null;
|
||||||
|
this._motions.splice(i, 1); // 削除
|
||||||
|
} else {
|
||||||
|
if (motionQueueEntry.isTriggeredFadeOut()) {
|
||||||
|
motionQueueEntry.startFadeOut(
|
||||||
|
motionQueueEntry.getFadeOutSeconds(),
|
||||||
|
userTimeSeconds
|
||||||
|
);
|
||||||
|
}
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return updated;
|
||||||
|
}
|
||||||
|
_userTimeSeconds: number; // デルタ時間の積算値[秒]
|
||||||
|
|
||||||
|
_motions: Array<CubismMotionQueueEntry>; // モーション
|
||||||
|
_eventCallBack: CubismMotionEventFunction; // コールバック関数
|
||||||
|
_eventCustomData: any; // コールバックに戻されるデータ
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* イベントのコールバック関数を定義
|
||||||
|
*
|
||||||
|
* イベントのコールバックに登録できる関数の型情報
|
||||||
|
* @param caller 発火したイベントを再生させたCubismMotionQueueManager
|
||||||
|
* @param eventValue 発火したイベントの文字列データ
|
||||||
|
* @param customData コールバックに返される登録時に指定されたデータ
|
||||||
|
*/
|
||||||
|
export interface CubismMotionEventFunction {
|
||||||
|
(caller: CubismMotionQueueManager, eventValue: string, customData: any): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* モーションの識別番号
|
||||||
|
*
|
||||||
|
* モーションの識別番号の定義
|
||||||
|
*/
|
||||||
|
export declare type CubismMotionQueueEntryHandle = any;
|
||||||
|
export const InvalidMotionQueueEntryHandleValue: CubismMotionQueueEntryHandle =
|
||||||
|
-1;
|
||||||
|
|
||||||
|
// Namespace definition for compatibility.
|
||||||
|
import * as $ from './cubismmotionqueuemanager';
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-namespace
|
||||||
|
export namespace Live2DCubismFramework {
|
||||||
|
export const CubismMotionQueueManager = $.CubismMotionQueueManager;
|
||||||
|
export type CubismMotionQueueManager = $.CubismMotionQueueManager;
|
||||||
|
export const InvalidMotionQueueEntryHandleValue =
|
||||||
|
$.InvalidMotionQueueEntryHandleValue;
|
||||||
|
export type CubismMotionQueueEntryHandle = $.CubismMotionQueueEntryHandle;
|
||||||
|
export type CubismMotionEventFunction = $.CubismMotionEventFunction;
|
||||||
|
}
|
||||||
@ -0,0 +1,60 @@
|
|||||||
|
/**
|
||||||
|
* Copyright(c) Live2D Inc. All rights reserved.
|
||||||
|
*
|
||||||
|
* Use of this source code is governed by the Live2D Open Software license
|
||||||
|
* that can be found at https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { ICubismUpdater, CubismUpdateOrder } from './icubismupdater';
|
||||||
|
import { CubismModel } from '../model/cubismmodel';
|
||||||
|
import { CubismPhysics } from '../physics/cubismphysics';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updater for physics effects.
|
||||||
|
* Handles the management of physics simulation through the CubismPhysics class.
|
||||||
|
*/
|
||||||
|
export class CubismPhysicsUpdater extends ICubismUpdater {
|
||||||
|
private _physics: CubismPhysics;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
*
|
||||||
|
* @param physics CubismPhysics reference
|
||||||
|
*/
|
||||||
|
constructor(physics: CubismPhysics);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
*
|
||||||
|
* @param physics CubismPhysics reference
|
||||||
|
* @param executionOrder Order of operations
|
||||||
|
*/
|
||||||
|
constructor(physics: CubismPhysics, executionOrder: number);
|
||||||
|
|
||||||
|
constructor(physics: CubismPhysics, executionOrder?: number) {
|
||||||
|
super(executionOrder ?? CubismUpdateOrder.CubismUpdateOrder_Physics);
|
||||||
|
this._physics = physics;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update process.
|
||||||
|
*
|
||||||
|
* @param model Model to update
|
||||||
|
* @param deltaTimeSeconds Delta time in seconds.
|
||||||
|
*/
|
||||||
|
onLateUpdate(model: CubismModel, deltaTimeSeconds: number): void {
|
||||||
|
if (!model) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._physics.evaluate(model, deltaTimeSeconds);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Namespace definition for compatibility.
|
||||||
|
import * as $ from './cubismphysicsupdater';
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-namespace
|
||||||
|
export namespace Live2DCubismFramework {
|
||||||
|
export const CubismPhysicsUpdater = $.CubismPhysicsUpdater;
|
||||||
|
export type CubismPhysicsUpdater = $.CubismPhysicsUpdater;
|
||||||
|
}
|
||||||
60
avatar-h5-renderer/framework/src/motion/cubismposeupdater.ts
Normal file
60
avatar-h5-renderer/framework/src/motion/cubismposeupdater.ts
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
/**
|
||||||
|
* Copyright(c) Live2D Inc. All rights reserved.
|
||||||
|
*
|
||||||
|
* Use of this source code is governed by the Live2D Open Software license
|
||||||
|
* that can be found at https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { ICubismUpdater, CubismUpdateOrder } from './icubismupdater';
|
||||||
|
import { CubismModel } from '../model/cubismmodel';
|
||||||
|
import { CubismPose } from '../effect/cubismpose';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updater for pose effects.
|
||||||
|
* Handles the management of pose animation through the CubismPose class.
|
||||||
|
*/
|
||||||
|
export class CubismPoseUpdater extends ICubismUpdater {
|
||||||
|
private _pose: CubismPose;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
*
|
||||||
|
* @param pose CubismPose reference
|
||||||
|
*/
|
||||||
|
constructor(pose: CubismPose);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
*
|
||||||
|
* @param pose CubismPose reference
|
||||||
|
* @param executionOrder Order of operations
|
||||||
|
*/
|
||||||
|
constructor(pose: CubismPose, executionOrder: number);
|
||||||
|
|
||||||
|
constructor(pose: CubismPose, executionOrder?: number) {
|
||||||
|
super(executionOrder ?? CubismUpdateOrder.CubismUpdateOrder_Pose);
|
||||||
|
this._pose = pose;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update process.
|
||||||
|
*
|
||||||
|
* @param model Model to update
|
||||||
|
* @param deltaTimeSeconds Delta time in seconds.
|
||||||
|
*/
|
||||||
|
onLateUpdate(model: CubismModel, deltaTimeSeconds: number): void {
|
||||||
|
if (!model) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._pose.updateParameters(model, deltaTimeSeconds);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Namespace definition for compatibility.
|
||||||
|
import * as $ from './cubismposeupdater';
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-namespace
|
||||||
|
export namespace Live2DCubismFramework {
|
||||||
|
export const CubismPoseUpdater = $.CubismPoseUpdater;
|
||||||
|
export type CubismPoseUpdater = $.CubismPoseUpdater;
|
||||||
|
}
|
||||||
180
avatar-h5-renderer/framework/src/motion/cubismupdatescheduler.ts
Normal file
180
avatar-h5-renderer/framework/src/motion/cubismupdatescheduler.ts
Normal file
@ -0,0 +1,180 @@
|
|||||||
|
/**
|
||||||
|
* Copyright(c) Live2D Inc. All rights reserved.
|
||||||
|
*
|
||||||
|
* Use of this source code is governed by the Live2D Open Software license
|
||||||
|
* that can be found at https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { ICubismUpdater, ICubismUpdaterChangeListener } from './icubismupdater';
|
||||||
|
import { CubismModel } from '../model/cubismmodel';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Scheduler for managing and updating ICubismUpdater instances.
|
||||||
|
* Handles the management of update order and execution through a sorted list.
|
||||||
|
*/
|
||||||
|
export class CubismUpdateScheduler implements ICubismUpdaterChangeListener {
|
||||||
|
private _cubismUpdatableList: ICubismUpdater[];
|
||||||
|
private _needsSort: boolean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
*/
|
||||||
|
constructor() {
|
||||||
|
this._cubismUpdatableList = [];
|
||||||
|
this._needsSort = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Destructor equivalent - releases all updaters and removes listeners
|
||||||
|
*/
|
||||||
|
public release(): void {
|
||||||
|
// Remove all listeners before clearing
|
||||||
|
for (const updater of this._cubismUpdatableList) {
|
||||||
|
if (updater) {
|
||||||
|
updater.removeChangeListener(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Clear the list - in TypeScript we don't need to manually delete objects
|
||||||
|
// as they will be garbage collected when no longer referenced
|
||||||
|
this._cubismUpdatableList.length = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds ICubismUpdater to the update list.
|
||||||
|
* The list will be automatically sorted by execution order before the next update.
|
||||||
|
*
|
||||||
|
* @param updatable The ICubismUpdater instance to be added.
|
||||||
|
*/
|
||||||
|
public addUpdatableList(updatable: ICubismUpdater): void {
|
||||||
|
if (!updatable) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for duplicate registration
|
||||||
|
if (this.hasUpdatable(updatable)) {
|
||||||
|
return; // Already exists, skip adding
|
||||||
|
}
|
||||||
|
|
||||||
|
this._cubismUpdatableList.push(updatable);
|
||||||
|
updatable.addChangeListener(this);
|
||||||
|
this._needsSort = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes ICubismUpdater from the update list.
|
||||||
|
*
|
||||||
|
* @param updatable The ICubismUpdater instance to be removed.
|
||||||
|
* @return true if the updater was found and removed, false otherwise.
|
||||||
|
*/
|
||||||
|
public removeUpdatableList(updatable: ICubismUpdater): boolean {
|
||||||
|
if (!updatable) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const index = this._cubismUpdatableList.indexOf(updatable);
|
||||||
|
if (index >= 0) {
|
||||||
|
this._cubismUpdatableList.splice(index, 1);
|
||||||
|
updatable.removeChangeListener(this);
|
||||||
|
// Note: removal doesn't require re-sorting
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sorts the update list using the ICubismUpdater sort function.
|
||||||
|
*/
|
||||||
|
public sortUpdatableList(): void {
|
||||||
|
this._cubismUpdatableList.sort(ICubismUpdater.sortFunction);
|
||||||
|
this._needsSort = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates every element in the list.
|
||||||
|
* The list is automatically sorted by execution order before execution.
|
||||||
|
*
|
||||||
|
* @param model Model to update
|
||||||
|
* @param deltaTimeSeconds Delta time in seconds.
|
||||||
|
*/
|
||||||
|
public onLateUpdate(model: CubismModel, deltaTimeSeconds: number): void {
|
||||||
|
if (!model) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Automatically sort if needed to ensure execution order
|
||||||
|
if (this._needsSort) {
|
||||||
|
this.sortUpdatableList();
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let i = 0; i < this._cubismUpdatableList.length; ++i) {
|
||||||
|
const updater = this._cubismUpdatableList[i];
|
||||||
|
if (updater) {
|
||||||
|
updater.onLateUpdate(model, deltaTimeSeconds);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the number of updaters in the list.
|
||||||
|
*
|
||||||
|
* @return Number of updaters
|
||||||
|
*/
|
||||||
|
public getUpdatableCount(): number {
|
||||||
|
return this._cubismUpdatableList.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the updater at the specified index.
|
||||||
|
*
|
||||||
|
* @param index Index of the updater to retrieve
|
||||||
|
* @return The updater at the specified index, or null if index is out of bounds
|
||||||
|
*/
|
||||||
|
public getUpdatable(index: number): ICubismUpdater | null {
|
||||||
|
if (index < 0 || index >= this._cubismUpdatableList.length) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return this._cubismUpdatableList[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the specified updater exists in the list.
|
||||||
|
*
|
||||||
|
* @param updatable The updater to check for
|
||||||
|
* @return true if the updater exists in the list, false otherwise
|
||||||
|
*/
|
||||||
|
public hasUpdatable(updatable: ICubismUpdater): boolean {
|
||||||
|
return this._cubismUpdatableList.indexOf(updatable) >= 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clears all updaters from the list.
|
||||||
|
*/
|
||||||
|
public clearUpdatableList(): void {
|
||||||
|
// Remove listeners before clearing
|
||||||
|
for (const updater of this._cubismUpdatableList) {
|
||||||
|
if (updater) {
|
||||||
|
updater.removeChangeListener(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this._cubismUpdatableList.length = 0;
|
||||||
|
this._needsSort = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when an updater's execution order has changed.
|
||||||
|
* Marks the list for re-sorting.
|
||||||
|
*
|
||||||
|
* @param updater The updater that was changed
|
||||||
|
*/
|
||||||
|
public onUpdaterChanged(updater: ICubismUpdater): void {
|
||||||
|
this._needsSort = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Namespace definition for compatibility.
|
||||||
|
import * as $ from './cubismupdatescheduler';
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-namespace
|
||||||
|
export namespace Live2DCubismFramework {
|
||||||
|
export const CubismUpdateScheduler = $.CubismUpdateScheduler;
|
||||||
|
export type CubismUpdateScheduler = $.CubismUpdateScheduler;
|
||||||
|
}
|
||||||
126
avatar-h5-renderer/framework/src/motion/icubismupdater.ts
Normal file
126
avatar-h5-renderer/framework/src/motion/icubismupdater.ts
Normal file
@ -0,0 +1,126 @@
|
|||||||
|
/**
|
||||||
|
* Copyright(c) Live2D Inc. All rights reserved.
|
||||||
|
*
|
||||||
|
* Use of this source code is governed by the Live2D Open Software license
|
||||||
|
* that can be found at https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { CubismModel } from '../model/cubismmodel';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface for listening to ICubismUpdater changes.
|
||||||
|
*/
|
||||||
|
export interface ICubismUpdaterChangeListener {
|
||||||
|
/**
|
||||||
|
* Called when an updater's execution order has changed.
|
||||||
|
*
|
||||||
|
* @param updater The updater that was changed
|
||||||
|
*/
|
||||||
|
onUpdaterChanged(updater: ICubismUpdater): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum CubismUpdateOrder {
|
||||||
|
CubismUpdateOrder_EyeBlink = 200,
|
||||||
|
CubismUpdateOrder_Expression = 300,
|
||||||
|
CubismUpdateOrder_Drag = 400,
|
||||||
|
CubismUpdateOrder_Breath = 500,
|
||||||
|
CubismUpdateOrder_Physics = 600,
|
||||||
|
CubismUpdateOrder_LipSync = 700,
|
||||||
|
CubismUpdateOrder_Pose = 800,
|
||||||
|
CubismUpdateOrder_Max = Number.MAX_SAFE_INTEGER
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Abstract base class for motions.<br>
|
||||||
|
* Handles the management of motion playback through the CubismUpdateScheduler.
|
||||||
|
*/
|
||||||
|
export abstract class ICubismUpdater {
|
||||||
|
/**
|
||||||
|
* Comparison function used when sorting ICubismUpdater objects.
|
||||||
|
*
|
||||||
|
* @param left The first ICubismUpdater object to be compared.
|
||||||
|
* @param right The second ICubismUpdater object to be compared.
|
||||||
|
*
|
||||||
|
* @return negative if left should be placed before right,
|
||||||
|
* positive if right should be placed before left,
|
||||||
|
* zero if they are equal.
|
||||||
|
*/
|
||||||
|
static sortFunction(left: ICubismUpdater, right: ICubismUpdater): number {
|
||||||
|
if (!left || !right) {
|
||||||
|
if (!left && !right) return 0;
|
||||||
|
if (!left) return 1; // null/undefined elements go to end
|
||||||
|
if (!right) return -1;
|
||||||
|
}
|
||||||
|
return left.getExecutionOrder() - right.getExecutionOrder();
|
||||||
|
}
|
||||||
|
|
||||||
|
private _executionOrder: number;
|
||||||
|
private _changeListeners: ICubismUpdaterChangeListener[] = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
*/
|
||||||
|
constructor(executionOrder: number = 0) {
|
||||||
|
this._executionOrder = executionOrder;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update process.
|
||||||
|
*
|
||||||
|
* @param model Model to update
|
||||||
|
* @param deltaTimeSeconds Delta time in seconds.
|
||||||
|
*/
|
||||||
|
abstract onLateUpdate(model: CubismModel, deltaTimeSeconds: number): void;
|
||||||
|
|
||||||
|
getExecutionOrder(): number {
|
||||||
|
return this._executionOrder;
|
||||||
|
}
|
||||||
|
|
||||||
|
setExecutionOrder(executionOrder: number): void {
|
||||||
|
if (this._executionOrder !== executionOrder) {
|
||||||
|
this._executionOrder = executionOrder;
|
||||||
|
this.notifyChangeListeners();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a listener to be notified when this updater's properties change.
|
||||||
|
*
|
||||||
|
* @param listener The listener to add
|
||||||
|
*/
|
||||||
|
addChangeListener(listener: ICubismUpdaterChangeListener): void {
|
||||||
|
if (listener && this._changeListeners.indexOf(listener) === -1) {
|
||||||
|
this._changeListeners.push(listener);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes a listener from the notification list.
|
||||||
|
*
|
||||||
|
* @param listener The listener to remove
|
||||||
|
*/
|
||||||
|
removeChangeListener(listener: ICubismUpdaterChangeListener): void {
|
||||||
|
const index = this._changeListeners.indexOf(listener);
|
||||||
|
if (index >= 0) {
|
||||||
|
this._changeListeners.splice(index, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Notifies all registered listeners that this updater has changed.
|
||||||
|
*/
|
||||||
|
private notifyChangeListeners(): void {
|
||||||
|
for (const listener of this._changeListeners) {
|
||||||
|
listener.onUpdaterChanged(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Namespace definition for compatibility.
|
||||||
|
import * as $ from './icubismupdater';
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-namespace
|
||||||
|
export namespace Live2DCubismFramework {
|
||||||
|
export const ICubismUpdater = $.ICubismUpdater;
|
||||||
|
export type ICubismUpdater = $.ICubismUpdater;
|
||||||
|
export type ICubismUpdaterChangeListener = $.ICubismUpdaterChangeListener;
|
||||||
|
}
|
||||||
@ -0,0 +1,41 @@
|
|||||||
|
/**
|
||||||
|
* Copyright(c) Live2D Inc. All rights reserved.
|
||||||
|
*
|
||||||
|
* Use of this source code is governed by the Live2D Open Software license
|
||||||
|
* that can be found at https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface class for providing parameter values.<br>
|
||||||
|
* Defines the base interface for classes that supply parameter values to the model.
|
||||||
|
*/
|
||||||
|
export abstract class IParameterProvider {
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
*/
|
||||||
|
constructor() {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update process.
|
||||||
|
*
|
||||||
|
* @param deltaTimeSeconds Delta time in seconds (optional).
|
||||||
|
*
|
||||||
|
* @return true if the update is successful.
|
||||||
|
*/
|
||||||
|
abstract update(deltaTimeSeconds?: number): boolean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the current value of the parameter.
|
||||||
|
*
|
||||||
|
* @return The parameter value as a floating-point number.
|
||||||
|
*/
|
||||||
|
abstract getParameter(): number;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Namespace definition for compatibility.
|
||||||
|
import * as $ from './iparameterprovider';
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-namespace
|
||||||
|
export namespace Live2DCubismFramework {
|
||||||
|
export const IParameterProvider = $.IParameterProvider;
|
||||||
|
export type IParameterProvider = $.IParameterProvider;
|
||||||
|
}
|
||||||
1358
avatar-h5-renderer/framework/src/physics/cubismphysics.ts
Normal file
1358
avatar-h5-renderer/framework/src/physics/cubismphysics.ts
Normal file
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,251 @@
|
|||||||
|
/**
|
||||||
|
* Copyright(c) Live2D Inc. All rights reserved.
|
||||||
|
*
|
||||||
|
* Use of this source code is governed by the Live2D Open Software license
|
||||||
|
* that can be found at https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { CubismIdHandle } from '../id/cubismid';
|
||||||
|
import { CubismVector2 } from '../math/cubismvector2';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 物理演算の適用先の種類
|
||||||
|
*/
|
||||||
|
export enum CubismPhysicsTargetType {
|
||||||
|
CubismPhysicsTargetType_Parameter // パラメータに対して適用
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 物理演算の入力の種類
|
||||||
|
*/
|
||||||
|
export enum CubismPhysicsSource {
|
||||||
|
CubismPhysicsSource_X, // X軸の位置から
|
||||||
|
CubismPhysicsSource_Y, // Y軸の位置から
|
||||||
|
CubismPhysicsSource_Angle // 角度から
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 物理演算で使用する外部の力
|
||||||
|
*
|
||||||
|
* 物理演算で使用する外部の力。
|
||||||
|
*/
|
||||||
|
export class PhysicsJsonEffectiveForces {
|
||||||
|
constructor() {
|
||||||
|
this.gravity = new CubismVector2(0, 0);
|
||||||
|
this.wind = new CubismVector2(0, 0);
|
||||||
|
}
|
||||||
|
gravity: CubismVector2; // 重力
|
||||||
|
wind: CubismVector2; // 風
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 物理演算のパラメータ情報
|
||||||
|
*/
|
||||||
|
export class CubismPhysicsParameter {
|
||||||
|
id: CubismIdHandle; // パラメータ
|
||||||
|
targetType: CubismPhysicsTargetType; // 適用先の種類
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 物理演算の正規化情報
|
||||||
|
*/
|
||||||
|
export class CubismPhysicsNormalization {
|
||||||
|
minimum: number; // 最大値
|
||||||
|
maximum: number; // 最小値
|
||||||
|
defalut: number; // デフォルト値
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 物理演算の演算委使用する物理点の情報
|
||||||
|
*/
|
||||||
|
export class CubismPhysicsParticle {
|
||||||
|
constructor() {
|
||||||
|
this.initialPosition = new CubismVector2(0, 0);
|
||||||
|
this.position = new CubismVector2(0, 0);
|
||||||
|
this.lastPosition = new CubismVector2(0, 0);
|
||||||
|
this.lastGravity = new CubismVector2(0, 0);
|
||||||
|
this.force = new CubismVector2(0, 0);
|
||||||
|
this.velocity = new CubismVector2(0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
initialPosition: CubismVector2; // 初期位置
|
||||||
|
mobility: number; // 動きやすさ
|
||||||
|
delay: number; // 遅れ
|
||||||
|
acceleration: number; // 加速度
|
||||||
|
radius: number; // 距離
|
||||||
|
position: CubismVector2; // 現在の位置
|
||||||
|
lastPosition: CubismVector2; // 最後の位置
|
||||||
|
lastGravity: CubismVector2; // 最後の重力
|
||||||
|
force: CubismVector2; // 現在かかっている力
|
||||||
|
velocity: CubismVector2; // 現在の速度
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 物理演算の物理点の管理
|
||||||
|
*/
|
||||||
|
export class CubismPhysicsSubRig {
|
||||||
|
constructor() {
|
||||||
|
this.normalizationPosition = new CubismPhysicsNormalization();
|
||||||
|
this.normalizationAngle = new CubismPhysicsNormalization();
|
||||||
|
}
|
||||||
|
inputCount: number; // 入力の個数
|
||||||
|
outputCount: number; // 出力の個数
|
||||||
|
particleCount: number; // 物理点の個数
|
||||||
|
baseInputIndex: number; // 入力の最初のインデックス
|
||||||
|
baseOutputIndex: number; // 出力の最初のインデックス
|
||||||
|
baseParticleIndex: number; // 物理点の最初のインデックス
|
||||||
|
normalizationPosition: CubismPhysicsNormalization; // 正規化された位置
|
||||||
|
normalizationAngle: CubismPhysicsNormalization; // 正規化された角度
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 正規化されたパラメータの取得関数の宣言
|
||||||
|
* @param targetTranslation // 演算結果の移動値
|
||||||
|
* @param targetAngle // 演算結果の角度
|
||||||
|
* @param value // パラメータの値
|
||||||
|
* @param parameterMinimunValue // パラメータの最小値
|
||||||
|
* @param parameterMaximumValue // パラメータの最大値
|
||||||
|
* @param parameterDefaultValue // パラメータのデフォルト値
|
||||||
|
* @param normalizationPosition // 正規化された位置
|
||||||
|
* @param normalizationAngle // 正規化された角度
|
||||||
|
* @param isInverted // 値が反転されているか?
|
||||||
|
* @param weight // 重み
|
||||||
|
*/
|
||||||
|
export interface normalizedPhysicsParameterValueGetter {
|
||||||
|
(
|
||||||
|
targetTranslation: CubismVector2,
|
||||||
|
targetAngle: { angle: number },
|
||||||
|
value: number,
|
||||||
|
parameterMinimunValue: number,
|
||||||
|
parameterMaximumValue: number,
|
||||||
|
parameterDefaultValue: number,
|
||||||
|
normalizationPosition: CubismPhysicsNormalization,
|
||||||
|
normalizationAngle: CubismPhysicsNormalization,
|
||||||
|
isInverted: boolean,
|
||||||
|
weight: number
|
||||||
|
): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 物理演算の値の取得関数の宣言
|
||||||
|
* @param translation 移動値
|
||||||
|
* @param particles 物理点のリスト
|
||||||
|
* @param isInverted 値が反映されているか
|
||||||
|
* @param parentGravity 重力
|
||||||
|
* @return 値
|
||||||
|
*/
|
||||||
|
export interface physicsValueGetter {
|
||||||
|
(
|
||||||
|
translation: CubismVector2,
|
||||||
|
particles: CubismPhysicsParticle[],
|
||||||
|
particleIndex: number,
|
||||||
|
isInverted: boolean,
|
||||||
|
parentGravity: CubismVector2
|
||||||
|
): number;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 物理演算のスケールの取得関数の宣言
|
||||||
|
* @param translationScale 移動値のスケール
|
||||||
|
* @param angleScale 角度のスケール
|
||||||
|
* @return スケール値
|
||||||
|
*/
|
||||||
|
export interface physicsScaleGetter {
|
||||||
|
(translationScale: CubismVector2, angleScale: number): number;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 物理演算の入力情報
|
||||||
|
*/
|
||||||
|
export class CubismPhysicsInput {
|
||||||
|
constructor() {
|
||||||
|
this.source = new CubismPhysicsParameter();
|
||||||
|
}
|
||||||
|
source: CubismPhysicsParameter; // 入力元のパラメータ
|
||||||
|
sourceParameterIndex: number; // 入力元のパラメータのインデックス
|
||||||
|
weight: number; // 重み
|
||||||
|
type: number; // 入力の種類
|
||||||
|
reflect: boolean; // 値が反転されているかどうか
|
||||||
|
getNormalizedParameterValue: normalizedPhysicsParameterValueGetter; // 正規化されたパラメータ値の取得関数
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 物理演算の出力情報
|
||||||
|
*
|
||||||
|
* 物理演算の出力情報。
|
||||||
|
*/
|
||||||
|
export class CubismPhysicsOutput {
|
||||||
|
constructor() {
|
||||||
|
this.destination = new CubismPhysicsParameter();
|
||||||
|
this.translationScale = new CubismVector2(0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
destination: CubismPhysicsParameter; // 出力先のパラメータ
|
||||||
|
destinationParameterIndex: number; // 出力先のパラメータのインデックス
|
||||||
|
vertexIndex: number; // 振り子のインデックス
|
||||||
|
translationScale: CubismVector2; // 移動値のスケール
|
||||||
|
angleScale: number; // 角度のスケール
|
||||||
|
weight: number; // 重み
|
||||||
|
type: CubismPhysicsSource; // 出力の種類
|
||||||
|
reflect: boolean; // 値が反転されているかどうか
|
||||||
|
valueBelowMinimum: number; // 最小値を下回った時の値
|
||||||
|
valueExceededMaximum: number; // 最大値をこえた時の値
|
||||||
|
getValue: physicsValueGetter; // 物理演算の値の取得関数
|
||||||
|
getScale: physicsScaleGetter; // 物理演算のスケール値の取得関数
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 物理演算のデータ
|
||||||
|
*
|
||||||
|
* 物理演算のデータ。
|
||||||
|
*/
|
||||||
|
export class CubismPhysicsRig {
|
||||||
|
constructor() {
|
||||||
|
this.settings = new Array<CubismPhysicsSubRig>();
|
||||||
|
this.inputs = new Array<CubismPhysicsInput>();
|
||||||
|
this.outputs = new Array<CubismPhysicsOutput>();
|
||||||
|
this.particles = new Array<CubismPhysicsParticle>();
|
||||||
|
this.gravity = new CubismVector2(0, 0);
|
||||||
|
this.wind = new CubismVector2(0, 0);
|
||||||
|
this.fps = 0.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
subRigCount: number; // 物理演算の物理点の個数
|
||||||
|
settings: Array<CubismPhysicsSubRig>; // 物理演算の物理点の管理のリスト
|
||||||
|
inputs: Array<CubismPhysicsInput>; // 物理演算の入力のリスト
|
||||||
|
outputs: Array<CubismPhysicsOutput>; // 物理演算の出力のリスト
|
||||||
|
particles: Array<CubismPhysicsParticle>; // 物理演算の物理点のリスト
|
||||||
|
gravity: CubismVector2; // 重力
|
||||||
|
wind: CubismVector2; // 風
|
||||||
|
fps: number; //物理演算動作FPS
|
||||||
|
}
|
||||||
|
|
||||||
|
// Namespace definition for compatibility.
|
||||||
|
import * as $ from './cubismphysicsinternal';
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-namespace
|
||||||
|
export namespace Live2DCubismFramework {
|
||||||
|
export const CubismPhysicsInput = $.CubismPhysicsInput;
|
||||||
|
export type CubismPhysicsInput = $.CubismPhysicsInput;
|
||||||
|
export const CubismPhysicsNormalization = $.CubismPhysicsNormalization;
|
||||||
|
export type CubismPhysicsNormalization = $.CubismPhysicsNormalization;
|
||||||
|
export const CubismPhysicsOutput = $.CubismPhysicsOutput;
|
||||||
|
export type CubismPhysicsOutput = $.CubismPhysicsOutput;
|
||||||
|
export const CubismPhysicsParameter = $.CubismPhysicsParameter;
|
||||||
|
export type CubismPhysicsParameter = $.CubismPhysicsParameter;
|
||||||
|
export const CubismPhysicsParticle = $.CubismPhysicsParticle;
|
||||||
|
export type CubismPhysicsParticle = $.CubismPhysicsParticle;
|
||||||
|
export const CubismPhysicsRig = $.CubismPhysicsRig;
|
||||||
|
export type CubismPhysicsRig = $.CubismPhysicsRig;
|
||||||
|
export const CubismPhysicsSource = $.CubismPhysicsSource;
|
||||||
|
export type CubismPhysicsSource = $.CubismPhysicsSource;
|
||||||
|
export const CubismPhysicsSubRig = $.CubismPhysicsSubRig;
|
||||||
|
export type CubismPhysicsSubRig = $.CubismPhysicsSubRig;
|
||||||
|
export const CubismPhysicsTargetType = $.CubismPhysicsTargetType;
|
||||||
|
export type CubismPhysicsTargetType = $.CubismPhysicsTargetType;
|
||||||
|
export const PhysicsJsonEffectiveForces = $.PhysicsJsonEffectiveForces;
|
||||||
|
export type PhysicsJsonEffectiveForces = $.PhysicsJsonEffectiveForces;
|
||||||
|
export type normalizedPhysicsParameterValueGetter =
|
||||||
|
$.normalizedPhysicsParameterValueGetter;
|
||||||
|
export type physicsScaleGetter = $.physicsScaleGetter;
|
||||||
|
export type physicsValueGetter = $.physicsValueGetter;
|
||||||
|
}
|
||||||
658
avatar-h5-renderer/framework/src/physics/cubismphysicsjson.ts
Normal file
658
avatar-h5-renderer/framework/src/physics/cubismphysicsjson.ts
Normal file
@ -0,0 +1,658 @@
|
|||||||
|
/**
|
||||||
|
* Copyright(c) Live2D Inc. All rights reserved.
|
||||||
|
*
|
||||||
|
* Use of this source code is governed by the Live2D Open Software license
|
||||||
|
* that can be found at https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { CubismIdHandle } from '../id/cubismid';
|
||||||
|
import { CubismFramework } from '../live2dcubismframework';
|
||||||
|
import { CubismVector2 } from '../math/cubismvector2';
|
||||||
|
import { CubismJson } from '../utils/cubismjson';
|
||||||
|
|
||||||
|
// JSON keys
|
||||||
|
const Position = 'Position';
|
||||||
|
const X = 'X';
|
||||||
|
const Y = 'Y';
|
||||||
|
const Angle = 'Angle';
|
||||||
|
const Type = 'Type';
|
||||||
|
const Id = 'Id';
|
||||||
|
|
||||||
|
// Meta
|
||||||
|
const Meta = 'Meta';
|
||||||
|
const EffectiveForces = 'EffectiveForces';
|
||||||
|
const TotalInputCount = 'TotalInputCount';
|
||||||
|
const TotalOutputCount = 'TotalOutputCount';
|
||||||
|
const PhysicsSettingCount = 'PhysicsSettingCount';
|
||||||
|
const Gravity = 'Gravity';
|
||||||
|
const Wind = 'Wind';
|
||||||
|
const VertexCount = 'VertexCount';
|
||||||
|
const Fps = 'Fps';
|
||||||
|
|
||||||
|
// PhysicsSettings
|
||||||
|
const PhysicsSettings = 'PhysicsSettings';
|
||||||
|
const Normalization = 'Normalization';
|
||||||
|
const Minimum = 'Minimum';
|
||||||
|
const Maximum = 'Maximum';
|
||||||
|
const Default = 'Default';
|
||||||
|
const Reflect = 'Reflect';
|
||||||
|
const Weight = 'Weight';
|
||||||
|
|
||||||
|
// Input
|
||||||
|
const Input = 'Input';
|
||||||
|
const Source = 'Source';
|
||||||
|
|
||||||
|
// Output
|
||||||
|
const Output = 'Output';
|
||||||
|
const Scale = 'Scale';
|
||||||
|
const VertexIndex = 'VertexIndex';
|
||||||
|
const Destination = 'Destination';
|
||||||
|
|
||||||
|
// Particle
|
||||||
|
const Vertices = 'Vertices';
|
||||||
|
const Mobility = 'Mobility';
|
||||||
|
const Delay = 'Delay';
|
||||||
|
const Radius = 'Radius';
|
||||||
|
const Acceleration = 'Acceleration';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* physics3.jsonのコンテナ。
|
||||||
|
*/
|
||||||
|
export class CubismPhysicsJson {
|
||||||
|
/**
|
||||||
|
* コンストラクタ
|
||||||
|
* @param buffer physics3.jsonが読み込まれているバッファ
|
||||||
|
* @param size バッファのサイズ
|
||||||
|
*/
|
||||||
|
public constructor(buffer: ArrayBuffer, size: number) {
|
||||||
|
this._json = CubismJson.create(buffer, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* デストラクタ相当の処理
|
||||||
|
*/
|
||||||
|
public release(): void {
|
||||||
|
CubismJson.delete(this._json);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 重力の取得
|
||||||
|
* @return 重力
|
||||||
|
*/
|
||||||
|
public getGravity(): CubismVector2 {
|
||||||
|
const ret: CubismVector2 = new CubismVector2(0, 0);
|
||||||
|
ret.x = this._json
|
||||||
|
.getRoot()
|
||||||
|
.getValueByString(Meta)
|
||||||
|
.getValueByString(EffectiveForces)
|
||||||
|
.getValueByString(Gravity)
|
||||||
|
.getValueByString(X)
|
||||||
|
.toFloat();
|
||||||
|
ret.y = this._json
|
||||||
|
.getRoot()
|
||||||
|
.getValueByString(Meta)
|
||||||
|
.getValueByString(EffectiveForces)
|
||||||
|
.getValueByString(Gravity)
|
||||||
|
.getValueByString(Y)
|
||||||
|
.toFloat();
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 風の取得
|
||||||
|
* @return 風
|
||||||
|
*/
|
||||||
|
public getWind(): CubismVector2 {
|
||||||
|
const ret: CubismVector2 = new CubismVector2(0, 0);
|
||||||
|
ret.x = this._json
|
||||||
|
.getRoot()
|
||||||
|
.getValueByString(Meta)
|
||||||
|
.getValueByString(EffectiveForces)
|
||||||
|
.getValueByString(Wind)
|
||||||
|
.getValueByString(X)
|
||||||
|
.toFloat();
|
||||||
|
ret.y = this._json
|
||||||
|
.getRoot()
|
||||||
|
.getValueByString(Meta)
|
||||||
|
.getValueByString(EffectiveForces)
|
||||||
|
.getValueByString(Wind)
|
||||||
|
.getValueByString(Y)
|
||||||
|
.toFloat();
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 物理演算設定FPSの取得
|
||||||
|
* @return 物理演算設定FPS
|
||||||
|
*/
|
||||||
|
public getFps(): number {
|
||||||
|
return this._json
|
||||||
|
.getRoot()
|
||||||
|
.getValueByString(Meta)
|
||||||
|
.getValueByString(Fps)
|
||||||
|
.toFloat(0.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 物理店の管理の個数の取得
|
||||||
|
* @return 物理店の管理の個数
|
||||||
|
*/
|
||||||
|
public getSubRigCount(): number {
|
||||||
|
return this._json
|
||||||
|
.getRoot()
|
||||||
|
.getValueByString(Meta)
|
||||||
|
.getValueByString(PhysicsSettingCount)
|
||||||
|
.toInt();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 入力の総合計の取得
|
||||||
|
* @return 入力の総合計
|
||||||
|
*/
|
||||||
|
public getTotalInputCount(): number {
|
||||||
|
return this._json
|
||||||
|
.getRoot()
|
||||||
|
.getValueByString(Meta)
|
||||||
|
.getValueByString(TotalInputCount)
|
||||||
|
.toInt();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 出力の総合計の取得
|
||||||
|
* @return 出力の総合計
|
||||||
|
*/
|
||||||
|
public getTotalOutputCount(): number {
|
||||||
|
return this._json
|
||||||
|
.getRoot()
|
||||||
|
.getValueByString(Meta)
|
||||||
|
.getValueByString(TotalOutputCount)
|
||||||
|
.toInt();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 物理点の個数の取得
|
||||||
|
* @return 物理点の個数
|
||||||
|
*/
|
||||||
|
public getVertexCount(): number {
|
||||||
|
return this._json
|
||||||
|
.getRoot()
|
||||||
|
.getValueByString(Meta)
|
||||||
|
.getValueByString(VertexCount)
|
||||||
|
.toInt();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 正規化された位置の最小値の取得
|
||||||
|
* @param physicsSettingIndex 物理演算の設定のインデックス
|
||||||
|
* @return 正規化された位置の最小値
|
||||||
|
*/
|
||||||
|
public getNormalizationPositionMinimumValue(
|
||||||
|
physicsSettingIndex: number
|
||||||
|
): number {
|
||||||
|
return this._json
|
||||||
|
.getRoot()
|
||||||
|
.getValueByString(PhysicsSettings)
|
||||||
|
.getValueByIndex(physicsSettingIndex)
|
||||||
|
.getValueByString(Normalization)
|
||||||
|
.getValueByString(Position)
|
||||||
|
.getValueByString(Minimum)
|
||||||
|
.toFloat();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 正規化された位置の最大値の取得
|
||||||
|
* @param physicsSettingIndex 物理演算の設定のインデックス
|
||||||
|
* @return 正規化された位置の最大値
|
||||||
|
*/
|
||||||
|
public getNormalizationPositionMaximumValue(
|
||||||
|
physicsSettingIndex: number
|
||||||
|
): number {
|
||||||
|
return this._json
|
||||||
|
.getRoot()
|
||||||
|
.getValueByString(PhysicsSettings)
|
||||||
|
.getValueByIndex(physicsSettingIndex)
|
||||||
|
.getValueByString(Normalization)
|
||||||
|
.getValueByString(Position)
|
||||||
|
.getValueByString(Maximum)
|
||||||
|
.toFloat();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 正規化された位置のデフォルト値の取得
|
||||||
|
* @param physicsSettingIndex 物理演算の設定のインデックス
|
||||||
|
* @return 正規化された位置のデフォルト値
|
||||||
|
*/
|
||||||
|
public getNormalizationPositionDefaultValue(
|
||||||
|
physicsSettingIndex: number
|
||||||
|
): number {
|
||||||
|
return this._json
|
||||||
|
.getRoot()
|
||||||
|
.getValueByString(PhysicsSettings)
|
||||||
|
.getValueByIndex(physicsSettingIndex)
|
||||||
|
.getValueByString(Normalization)
|
||||||
|
.getValueByString(Position)
|
||||||
|
.getValueByString(Default)
|
||||||
|
.toFloat();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 正規化された角度の最小値の取得
|
||||||
|
* @param physicsSettingIndex 物理演算の設定のインデックス
|
||||||
|
* @return 正規化された角度の最小値
|
||||||
|
*/
|
||||||
|
public getNormalizationAngleMinimumValue(
|
||||||
|
physicsSettingIndex: number
|
||||||
|
): number {
|
||||||
|
return this._json
|
||||||
|
.getRoot()
|
||||||
|
.getValueByString(PhysicsSettings)
|
||||||
|
.getValueByIndex(physicsSettingIndex)
|
||||||
|
.getValueByString(Normalization)
|
||||||
|
.getValueByString(Angle)
|
||||||
|
.getValueByString(Minimum)
|
||||||
|
.toFloat();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 正規化された角度の最大値の取得
|
||||||
|
* @param physicsSettingIndex
|
||||||
|
* @return 正規化された角度の最大値
|
||||||
|
*/
|
||||||
|
public getNormalizationAngleMaximumValue(
|
||||||
|
physicsSettingIndex: number
|
||||||
|
): number {
|
||||||
|
return this._json
|
||||||
|
.getRoot()
|
||||||
|
.getValueByString(PhysicsSettings)
|
||||||
|
.getValueByIndex(physicsSettingIndex)
|
||||||
|
.getValueByString(Normalization)
|
||||||
|
.getValueByString(Angle)
|
||||||
|
.getValueByString(Maximum)
|
||||||
|
.toFloat();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 正規化された角度のデフォルト値の取得
|
||||||
|
* @param physicsSettingIndex 物理演算の設定のインデックス
|
||||||
|
* @return 正規化された角度のデフォルト値
|
||||||
|
*/
|
||||||
|
public getNormalizationAngleDefaultValue(
|
||||||
|
physicsSettingIndex: number
|
||||||
|
): number {
|
||||||
|
return this._json
|
||||||
|
.getRoot()
|
||||||
|
.getValueByString(PhysicsSettings)
|
||||||
|
.getValueByIndex(physicsSettingIndex)
|
||||||
|
.getValueByString(Normalization)
|
||||||
|
.getValueByString(Angle)
|
||||||
|
.getValueByString(Default)
|
||||||
|
.toFloat();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 入力の個数の取得
|
||||||
|
* @param physicsSettingIndex 物理演算の設定のインデックス
|
||||||
|
* @return 入力の個数
|
||||||
|
*/
|
||||||
|
public getInputCount(physicsSettingIndex: number): number {
|
||||||
|
return this._json
|
||||||
|
.getRoot()
|
||||||
|
.getValueByString(PhysicsSettings)
|
||||||
|
.getValueByIndex(physicsSettingIndex)
|
||||||
|
.getValueByString(Input)
|
||||||
|
.getVector().length;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 入力の重みの取得
|
||||||
|
* @param physicsSettingIndex 物理演算の設定のインデックス
|
||||||
|
* @param inputIndex 入力のインデックス
|
||||||
|
* @return 入力の重み
|
||||||
|
*/
|
||||||
|
public getInputWeight(
|
||||||
|
physicsSettingIndex: number,
|
||||||
|
inputIndex: number
|
||||||
|
): number {
|
||||||
|
return this._json
|
||||||
|
.getRoot()
|
||||||
|
.getValueByString(PhysicsSettings)
|
||||||
|
.getValueByIndex(physicsSettingIndex)
|
||||||
|
.getValueByString(Input)
|
||||||
|
.getValueByIndex(inputIndex)
|
||||||
|
.getValueByString(Weight)
|
||||||
|
.toFloat();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 入力の反転の取得
|
||||||
|
* @param physicsSettingIndex 物理演算の設定のインデックス
|
||||||
|
* @param inputIndex 入力のインデックス
|
||||||
|
* @return 入力の反転
|
||||||
|
*/
|
||||||
|
public getInputReflect(
|
||||||
|
physicsSettingIndex: number,
|
||||||
|
inputIndex: number
|
||||||
|
): boolean {
|
||||||
|
return this._json
|
||||||
|
.getRoot()
|
||||||
|
.getValueByString(PhysicsSettings)
|
||||||
|
.getValueByIndex(physicsSettingIndex)
|
||||||
|
.getValueByString(Input)
|
||||||
|
.getValueByIndex(inputIndex)
|
||||||
|
.getValueByString(Reflect)
|
||||||
|
.toBoolean();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 入力の種類の取得
|
||||||
|
* @param physicsSettingIndex 物理演算の設定のインデックス
|
||||||
|
* @param inputIndex 入力のインデックス
|
||||||
|
* @return 入力の種類
|
||||||
|
*/
|
||||||
|
public getInputType(physicsSettingIndex: number, inputIndex: number): string {
|
||||||
|
return this._json
|
||||||
|
.getRoot()
|
||||||
|
.getValueByString(PhysicsSettings)
|
||||||
|
.getValueByIndex(physicsSettingIndex)
|
||||||
|
.getValueByString(Input)
|
||||||
|
.getValueByIndex(inputIndex)
|
||||||
|
.getValueByString(Type)
|
||||||
|
.getRawString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 入力元のIDの取得
|
||||||
|
* @param physicsSettingIndex 物理演算の設定のインデックス
|
||||||
|
* @param inputIndex 入力のインデックス
|
||||||
|
* @return 入力元のID
|
||||||
|
*/
|
||||||
|
public getInputSourceId(
|
||||||
|
physicsSettingIndex: number,
|
||||||
|
inputIndex: number
|
||||||
|
): CubismIdHandle {
|
||||||
|
return CubismFramework.getIdManager().getId(
|
||||||
|
this._json
|
||||||
|
.getRoot()
|
||||||
|
.getValueByString(PhysicsSettings)
|
||||||
|
.getValueByIndex(physicsSettingIndex)
|
||||||
|
.getValueByString(Input)
|
||||||
|
.getValueByIndex(inputIndex)
|
||||||
|
.getValueByString(Source)
|
||||||
|
.getValueByString(Id)
|
||||||
|
.getRawString()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 出力の個数の取得
|
||||||
|
* @param physicsSettingIndex 物理演算の設定のインデックス
|
||||||
|
* @return 出力の個数
|
||||||
|
*/
|
||||||
|
public getOutputCount(physicsSettingIndex: number): number {
|
||||||
|
return this._json
|
||||||
|
.getRoot()
|
||||||
|
.getValueByString(PhysicsSettings)
|
||||||
|
.getValueByIndex(physicsSettingIndex)
|
||||||
|
.getValueByString(Output)
|
||||||
|
.getVector().length;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 出力の物理点のインデックスの取得
|
||||||
|
* @param physicsSettingIndex 物理演算の設定のインデックス
|
||||||
|
* @param outputIndex 出力のインデックス
|
||||||
|
* @return 出力の物理点のインデックス
|
||||||
|
*/
|
||||||
|
public getOutputVertexIndex(
|
||||||
|
physicsSettingIndex: number,
|
||||||
|
outputIndex: number
|
||||||
|
): number {
|
||||||
|
return this._json
|
||||||
|
.getRoot()
|
||||||
|
.getValueByString(PhysicsSettings)
|
||||||
|
.getValueByIndex(physicsSettingIndex)
|
||||||
|
.getValueByString(Output)
|
||||||
|
.getValueByIndex(outputIndex)
|
||||||
|
.getValueByString(VertexIndex)
|
||||||
|
.toInt();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 出力の角度のスケールを取得する
|
||||||
|
* @param physicsSettingIndex 物理演算の設定のインデックス
|
||||||
|
* @param outputIndex 出力のインデックス
|
||||||
|
* @return 出力の角度のスケール
|
||||||
|
*/
|
||||||
|
public getOutputAngleScale(
|
||||||
|
physicsSettingIndex: number,
|
||||||
|
outputIndex: number
|
||||||
|
): number {
|
||||||
|
return this._json
|
||||||
|
.getRoot()
|
||||||
|
.getValueByString(PhysicsSettings)
|
||||||
|
.getValueByIndex(physicsSettingIndex)
|
||||||
|
.getValueByString(Output)
|
||||||
|
.getValueByIndex(outputIndex)
|
||||||
|
.getValueByString(Scale)
|
||||||
|
.toFloat();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 出力の重みの取得
|
||||||
|
* @param physicsSettingIndex 物理演算の設定のインデックス
|
||||||
|
* @param outputIndex 出力のインデックス
|
||||||
|
* @return 出力の重み
|
||||||
|
*/
|
||||||
|
public getOutputWeight(
|
||||||
|
physicsSettingIndex: number,
|
||||||
|
outputIndex: number
|
||||||
|
): number {
|
||||||
|
return this._json
|
||||||
|
.getRoot()
|
||||||
|
.getValueByString(PhysicsSettings)
|
||||||
|
.getValueByIndex(physicsSettingIndex)
|
||||||
|
.getValueByString(Output)
|
||||||
|
.getValueByIndex(outputIndex)
|
||||||
|
.getValueByString(Weight)
|
||||||
|
.toFloat();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 出力先のIDの取得
|
||||||
|
* @param physicsSettingIndex 物理演算の設定のインデックス
|
||||||
|
* @param outputIndex 出力のインデックス
|
||||||
|
* @return 出力先のID
|
||||||
|
*/
|
||||||
|
public getOutputDestinationId(
|
||||||
|
physicsSettingIndex: number,
|
||||||
|
outputIndex: number
|
||||||
|
): CubismIdHandle {
|
||||||
|
return CubismFramework.getIdManager().getId(
|
||||||
|
this._json
|
||||||
|
.getRoot()
|
||||||
|
.getValueByString(PhysicsSettings)
|
||||||
|
.getValueByIndex(physicsSettingIndex)
|
||||||
|
.getValueByString(Output)
|
||||||
|
.getValueByIndex(outputIndex)
|
||||||
|
.getValueByString(Destination)
|
||||||
|
.getValueByString(Id)
|
||||||
|
.getRawString()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 出力の種類の取得
|
||||||
|
* @param physicsSettingIndex 物理演算の設定のインデックス
|
||||||
|
* @param outputIndex 出力のインデックス
|
||||||
|
* @return 出力の種類
|
||||||
|
*/
|
||||||
|
public getOutputType(
|
||||||
|
physicsSettingIndex: number,
|
||||||
|
outputIndex: number
|
||||||
|
): string {
|
||||||
|
return this._json
|
||||||
|
.getRoot()
|
||||||
|
.getValueByString(PhysicsSettings)
|
||||||
|
.getValueByIndex(physicsSettingIndex)
|
||||||
|
.getValueByString(Output)
|
||||||
|
.getValueByIndex(outputIndex)
|
||||||
|
.getValueByString(Type)
|
||||||
|
.getRawString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 出力の反転の取得
|
||||||
|
* @param physicsSettingIndex 物理演算のインデックス
|
||||||
|
* @param outputIndex 出力のインデックス
|
||||||
|
* @return 出力の反転
|
||||||
|
*/
|
||||||
|
public getOutputReflect(
|
||||||
|
physicsSettingIndex: number,
|
||||||
|
outputIndex: number
|
||||||
|
): boolean {
|
||||||
|
return this._json
|
||||||
|
.getRoot()
|
||||||
|
.getValueByString(PhysicsSettings)
|
||||||
|
.getValueByIndex(physicsSettingIndex)
|
||||||
|
.getValueByString(Output)
|
||||||
|
.getValueByIndex(outputIndex)
|
||||||
|
.getValueByString(Reflect)
|
||||||
|
.toBoolean();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 物理点の個数の取得
|
||||||
|
* @param physicsSettingIndex 物理演算男設定のインデックス
|
||||||
|
* @return 物理点の個数
|
||||||
|
*/
|
||||||
|
public getParticleCount(physicsSettingIndex: number): number {
|
||||||
|
return this._json
|
||||||
|
.getRoot()
|
||||||
|
.getValueByString(PhysicsSettings)
|
||||||
|
.getValueByIndex(physicsSettingIndex)
|
||||||
|
.getValueByString(Vertices)
|
||||||
|
.getVector().length;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 物理点の動きやすさの取得
|
||||||
|
* @param physicsSettingIndex 物理演算の設定のインデックス
|
||||||
|
* @param vertexIndex 物理点のインデックス
|
||||||
|
* @return 物理点の動きやすさ
|
||||||
|
*/
|
||||||
|
public getParticleMobility(
|
||||||
|
physicsSettingIndex: number,
|
||||||
|
vertexIndex: number
|
||||||
|
): number {
|
||||||
|
return this._json
|
||||||
|
.getRoot()
|
||||||
|
.getValueByString(PhysicsSettings)
|
||||||
|
.getValueByIndex(physicsSettingIndex)
|
||||||
|
.getValueByString(Vertices)
|
||||||
|
.getValueByIndex(vertexIndex)
|
||||||
|
.getValueByString(Mobility)
|
||||||
|
.toFloat();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 物理点の遅れの取得
|
||||||
|
* @param physicsSettingIndex 物理演算の設定のインデックス
|
||||||
|
* @param vertexIndex 物理点のインデックス
|
||||||
|
* @return 物理点の遅れ
|
||||||
|
*/
|
||||||
|
public getParticleDelay(
|
||||||
|
physicsSettingIndex: number,
|
||||||
|
vertexIndex: number
|
||||||
|
): number {
|
||||||
|
return this._json
|
||||||
|
.getRoot()
|
||||||
|
.getValueByString(PhysicsSettings)
|
||||||
|
.getValueByIndex(physicsSettingIndex)
|
||||||
|
.getValueByString(Vertices)
|
||||||
|
.getValueByIndex(vertexIndex)
|
||||||
|
.getValueByString(Delay)
|
||||||
|
.toFloat();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 物理点の加速度の取得
|
||||||
|
* @param physicsSettingIndex 物理演算の設定
|
||||||
|
* @param vertexIndex 物理点のインデックス
|
||||||
|
* @return 物理点の加速度
|
||||||
|
*/
|
||||||
|
public getParticleAcceleration(
|
||||||
|
physicsSettingIndex: number,
|
||||||
|
vertexIndex: number
|
||||||
|
): number {
|
||||||
|
return this._json
|
||||||
|
.getRoot()
|
||||||
|
.getValueByString(PhysicsSettings)
|
||||||
|
.getValueByIndex(physicsSettingIndex)
|
||||||
|
.getValueByString(Vertices)
|
||||||
|
.getValueByIndex(vertexIndex)
|
||||||
|
.getValueByString(Acceleration)
|
||||||
|
.toFloat();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 物理点の距離の取得
|
||||||
|
* @param physicsSettingIndex 物理演算の設定のインデックス
|
||||||
|
* @param vertexIndex 物理点のインデックス
|
||||||
|
* @return 物理点の距離
|
||||||
|
*/
|
||||||
|
public getParticleRadius(
|
||||||
|
physicsSettingIndex: number,
|
||||||
|
vertexIndex: number
|
||||||
|
): number {
|
||||||
|
return this._json
|
||||||
|
.getRoot()
|
||||||
|
.getValueByString(PhysicsSettings)
|
||||||
|
.getValueByIndex(physicsSettingIndex)
|
||||||
|
.getValueByString(Vertices)
|
||||||
|
.getValueByIndex(vertexIndex)
|
||||||
|
.getValueByString(Radius)
|
||||||
|
.toFloat();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 物理点の位置の取得
|
||||||
|
* @param physicsSettingIndex 物理演算の設定のインデックス
|
||||||
|
* @param vertexInde 物理点のインデックス
|
||||||
|
* @return 物理点の位置
|
||||||
|
*/
|
||||||
|
public getParticlePosition(
|
||||||
|
physicsSettingIndex: number,
|
||||||
|
vertexIndex: number
|
||||||
|
): CubismVector2 {
|
||||||
|
const ret: CubismVector2 = new CubismVector2(0, 0);
|
||||||
|
ret.x = this._json
|
||||||
|
.getRoot()
|
||||||
|
.getValueByString(PhysicsSettings)
|
||||||
|
.getValueByIndex(physicsSettingIndex)
|
||||||
|
.getValueByString(Vertices)
|
||||||
|
.getValueByIndex(vertexIndex)
|
||||||
|
.getValueByString(Position)
|
||||||
|
.getValueByString(X)
|
||||||
|
.toFloat();
|
||||||
|
ret.y = this._json
|
||||||
|
.getRoot()
|
||||||
|
.getValueByString(PhysicsSettings)
|
||||||
|
.getValueByIndex(physicsSettingIndex)
|
||||||
|
.getValueByString(Vertices)
|
||||||
|
.getValueByIndex(vertexIndex)
|
||||||
|
.getValueByString(Position)
|
||||||
|
.getValueByString(Y)
|
||||||
|
.toFloat();
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
_json: CubismJson; // physics3.jsonデータ
|
||||||
|
}
|
||||||
|
|
||||||
|
// Namespace definition for compatibility.
|
||||||
|
import * as $ from './cubismphysicsjson';
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-namespace
|
||||||
|
export namespace Live2DCubismFramework {
|
||||||
|
export const CubismPhysicsJson = $.CubismPhysicsJson;
|
||||||
|
export type CubismPhysicsJson = $.CubismPhysicsJson;
|
||||||
|
}
|
||||||
@ -0,0 +1,994 @@
|
|||||||
|
/**
|
||||||
|
* Copyright(c) Live2D Inc. All rights reserved.
|
||||||
|
*
|
||||||
|
* Use of this source code is governed by the Live2D Open Software license
|
||||||
|
* that can be found at https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { Constant } from '../live2dcubismframework';
|
||||||
|
import { csmRect } from '../type/csmrectf';
|
||||||
|
import { CubismMatrix44 } from '../math/cubismmatrix44';
|
||||||
|
import { CubismModel } from '../model/cubismmodel';
|
||||||
|
import { CubismClippingContext, CubismTextureColor } from './cubismrenderer';
|
||||||
|
import { CubismLogError, CubismLogWarning } from '../utils/cubismdebug';
|
||||||
|
|
||||||
|
const ColorChannelCount = 4; // 実験時に1チャンネルの場合は1、RGBだけの場合は3、アルファも含める場合は4
|
||||||
|
const ClippingMaskMaxCountOnDefault = 36; // 通常のフレームバッファ一枚あたりのマスク最大数
|
||||||
|
const ClippingMaskMaxCountOnMultiRenderTexture = 32; // フレームバッファが2枚以上ある場合のフレームバッファ一枚あたりのマスク最大数
|
||||||
|
|
||||||
|
export type ClippingContextConstructor<
|
||||||
|
T_ClippingContext extends CubismClippingContext
|
||||||
|
> = new (
|
||||||
|
manager: CubismClippingManager<T_ClippingContext>,
|
||||||
|
drawableMasks: Int32Array,
|
||||||
|
drawableMaskCounts: number
|
||||||
|
) => T_ClippingContext;
|
||||||
|
|
||||||
|
export interface ICubismClippingManager {
|
||||||
|
getClippingMaskBufferSize(): number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export abstract class CubismClippingManager<
|
||||||
|
T_ClippingContext extends CubismClippingContext
|
||||||
|
> implements ICubismClippingManager {
|
||||||
|
/**
|
||||||
|
* コンストラクタ
|
||||||
|
*/
|
||||||
|
public constructor(
|
||||||
|
clippingContextFactory: ClippingContextConstructor<T_ClippingContext>
|
||||||
|
) {
|
||||||
|
this._renderTextureCount = 0;
|
||||||
|
this._clippingMaskBufferSize = 256;
|
||||||
|
this._clippingContextListForMask = new Array<T_ClippingContext>();
|
||||||
|
this._clippingContextListForDraw = new Array<T_ClippingContext>();
|
||||||
|
this._clippingContextListForOffscreen = new Array<T_ClippingContext>();
|
||||||
|
this._tmpBoundsOnModel = new csmRect();
|
||||||
|
this._tmpMatrix = new CubismMatrix44();
|
||||||
|
this._tmpMatrixForMask = new CubismMatrix44();
|
||||||
|
this._tmpMatrixForDraw = new CubismMatrix44();
|
||||||
|
this._clearedMaskBufferFlags = new Array<boolean>();
|
||||||
|
|
||||||
|
this._clippingContexttConstructor = clippingContextFactory;
|
||||||
|
|
||||||
|
this._channelColors = [
|
||||||
|
new CubismTextureColor(1.0, 0.0, 0.0, 0.0),
|
||||||
|
new CubismTextureColor(0.0, 1.0, 0.0, 0.0),
|
||||||
|
new CubismTextureColor(0.0, 0.0, 1.0, 0.0),
|
||||||
|
new CubismTextureColor(0.0, 0.0, 0.0, 1.0)
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* デストラクタ相当の処理
|
||||||
|
*/
|
||||||
|
public release(): void {
|
||||||
|
for (let i = 0; i < this._clippingContextListForMask.length; i++) {
|
||||||
|
if (this._clippingContextListForMask[i]) {
|
||||||
|
this._clippingContextListForMask[i].release();
|
||||||
|
this._clippingContextListForMask[i] = void 0;
|
||||||
|
}
|
||||||
|
this._clippingContextListForMask[i] = null;
|
||||||
|
}
|
||||||
|
this._clippingContextListForMask = null;
|
||||||
|
|
||||||
|
// _clippingContextListForDrawは_clippingContextListForMaskにあるインスタンスを指している。上記の処理により要素ごとのDELETEは不要。
|
||||||
|
for (let i = 0; i < this._clippingContextListForDraw.length; i++) {
|
||||||
|
this._clippingContextListForDraw[i] = null;
|
||||||
|
}
|
||||||
|
this._clippingContextListForDraw = null;
|
||||||
|
|
||||||
|
for (let i = 0; i < this._channelColors.length; i++) {
|
||||||
|
this._channelColors[i] = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._channelColors = null;
|
||||||
|
|
||||||
|
if (this._clearedMaskBufferFlags != null) {
|
||||||
|
this._clearedMaskBufferFlags.length = 0;
|
||||||
|
}
|
||||||
|
this._clearedMaskBufferFlags = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* マネージャの初期化処理
|
||||||
|
* クリッピングマスクを使う描画オブジェクトの登録を行う
|
||||||
|
* @param model モデルのインスタンス
|
||||||
|
* @param renderTextureCount バッファの生成数
|
||||||
|
*/
|
||||||
|
public initializeForDrawable(
|
||||||
|
model: CubismModel,
|
||||||
|
renderTextureCount: number
|
||||||
|
): void {
|
||||||
|
// レンダーテクスチャの合計枚数の設定
|
||||||
|
// 1以上の整数でない場合はそれぞれ警告を出す
|
||||||
|
if (renderTextureCount % 1 != 0) {
|
||||||
|
CubismLogWarning(
|
||||||
|
'The number of render textures must be specified as an integer. The decimal point is rounded down and corrected to an integer.'
|
||||||
|
);
|
||||||
|
// 小数点以下を除去
|
||||||
|
renderTextureCount = ~~renderTextureCount;
|
||||||
|
}
|
||||||
|
if (renderTextureCount < 1) {
|
||||||
|
CubismLogWarning(
|
||||||
|
'The number of render textures must be an integer greater than or equal to 1. Set the number of render textures to 1.'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
// 負の値が使われている場合は強制的に1枚と設定する
|
||||||
|
this._renderTextureCount = renderTextureCount < 1 ? 1 : renderTextureCount;
|
||||||
|
|
||||||
|
this._clearedMaskBufferFlags = new Array<boolean>(this._renderTextureCount);
|
||||||
|
|
||||||
|
// クリッピングマスクを使う描画オブジェクトをすべて登録する
|
||||||
|
// クリッピングマスクは、通常数個程度に限定して使うものとする
|
||||||
|
this._clippingContextListForDraw.length = model.getDrawableCount();
|
||||||
|
for (let i = 0; i < model.getDrawableCount(); i++) {
|
||||||
|
if (model.getDrawableMaskCounts()[i] <= 0) {
|
||||||
|
// クリッピングマスクが使用されていないアートメッシュ(多くの場合使用しない)
|
||||||
|
this._clippingContextListForDraw[i] = null;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 既にあるClipContextと同じかチェックする
|
||||||
|
let clippingContext: T_ClippingContext = this.findSameClip(
|
||||||
|
model.getDrawableMasks()[i],
|
||||||
|
model.getDrawableMaskCounts()[i]
|
||||||
|
);
|
||||||
|
if (clippingContext == null) {
|
||||||
|
// 同一のマスクが存在していない場合は生成する
|
||||||
|
|
||||||
|
clippingContext = new this._clippingContexttConstructor(
|
||||||
|
this,
|
||||||
|
model.getDrawableMasks()[i],
|
||||||
|
model.getDrawableMaskCounts()[i]
|
||||||
|
);
|
||||||
|
this._clippingContextListForMask.push(clippingContext);
|
||||||
|
}
|
||||||
|
|
||||||
|
clippingContext.addClippedDrawable(i);
|
||||||
|
|
||||||
|
this._clippingContextListForDraw[i] = clippingContext;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* オフスクリーン用の初期化処理
|
||||||
|
*
|
||||||
|
* @param model モデルのインスタンス
|
||||||
|
* @param maskBufferCount オフスクリーン用のマスクバッファの数
|
||||||
|
*/
|
||||||
|
public initializeForOffscreen(
|
||||||
|
model: CubismModel,
|
||||||
|
maskBufferCount: number
|
||||||
|
): void {
|
||||||
|
this._renderTextureCount = maskBufferCount;
|
||||||
|
|
||||||
|
// レンダーテクスチャのクリアフラグの設定
|
||||||
|
this._clearedMaskBufferFlags.length = this._renderTextureCount;
|
||||||
|
for (let i = 0; i < this._renderTextureCount; ++i) {
|
||||||
|
this._clearedMaskBufferFlags[i] = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
//クリッピングマスクを使う描画オブジェクトを全て登録する
|
||||||
|
//クリッピングマスクは、通常数個程度に限定して使うものとする
|
||||||
|
this._clippingContextListForOffscreen.length = model.getOffscreenCount();
|
||||||
|
for (let i = 0; i < model.getOffscreenCount(); ++i) {
|
||||||
|
if (model.getOffscreenMaskCounts()[i] <= 0) {
|
||||||
|
//クリッピングマスクが使用されていないオフスクリーン(多くの場合使用しない)
|
||||||
|
this._clippingContextListForOffscreen.push(null);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 既にあるClipContextと同じかチェックする
|
||||||
|
let cc = this.findSameClip(
|
||||||
|
model.getOffscreenMasks()[i],
|
||||||
|
model.getOffscreenMaskCounts()[i]
|
||||||
|
);
|
||||||
|
if (cc == null) {
|
||||||
|
// 同一のマスクが存在していない場合は生成する
|
||||||
|
cc = new this._clippingContexttConstructor(
|
||||||
|
this,
|
||||||
|
model.getOffscreenMasks()[i],
|
||||||
|
model.getOffscreenMaskCounts()[i]
|
||||||
|
);
|
||||||
|
this._clippingContextListForMask.push(cc);
|
||||||
|
}
|
||||||
|
|
||||||
|
cc.addClippedOffscreen(i);
|
||||||
|
|
||||||
|
this._clippingContextListForOffscreen[i] = cc;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 既にマスクを作っているかを確認
|
||||||
|
* 作っている様であれば該当するクリッピングマスクのインスタンスを返す
|
||||||
|
* 作っていなければNULLを返す
|
||||||
|
* @param drawableMasks 描画オブジェクトをマスクする描画オブジェクトのリスト
|
||||||
|
* @param drawableMaskCounts 描画オブジェクトをマスクする描画オブジェクトの数
|
||||||
|
* @return 該当するクリッピングマスクが存在すればインスタンスを返し、なければNULLを返す
|
||||||
|
*/
|
||||||
|
public findSameClip(
|
||||||
|
drawableMasks: Int32Array,
|
||||||
|
drawableMaskCounts: number
|
||||||
|
): T_ClippingContext {
|
||||||
|
// 作成済みClippingContextと一致するか確認
|
||||||
|
for (let i = 0; i < this._clippingContextListForMask.length; i++) {
|
||||||
|
const clippingContext: T_ClippingContext =
|
||||||
|
this._clippingContextListForMask[i];
|
||||||
|
const count: number = clippingContext._clippingIdCount;
|
||||||
|
|
||||||
|
// 個数が違う場合は別物
|
||||||
|
if (count != drawableMaskCounts) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let sameCount = 0;
|
||||||
|
|
||||||
|
// 同じIDを持つか確認。配列の数が同じなので、一致した個数が同じなら同じ物を持つとする
|
||||||
|
for (let j = 0; j < count; j++) {
|
||||||
|
const clipId: number = clippingContext._clippingIdList[j];
|
||||||
|
|
||||||
|
for (let k = 0; k < count; k++) {
|
||||||
|
if (drawableMasks[k] == clipId) {
|
||||||
|
sameCount++;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sameCount == count) {
|
||||||
|
return clippingContext;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null; // 見つからなかった
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 高精細マスク処理用の行列を計算する
|
||||||
|
* @param model モデルのインスタンス
|
||||||
|
* @param isRightHanded 処理が右手系であるか
|
||||||
|
*/
|
||||||
|
public setupMatrixForHighPrecision(
|
||||||
|
model: CubismModel,
|
||||||
|
isRightHanded: boolean
|
||||||
|
): void {
|
||||||
|
// 全てのクリッピングを用意する
|
||||||
|
// 同じクリップ(複数の場合はまとめて一つのクリップ)を使う場合は1度だけ設定する
|
||||||
|
let usingClipCount = 0;
|
||||||
|
for (
|
||||||
|
let clipIndex = 0;
|
||||||
|
clipIndex < this._clippingContextListForMask.length;
|
||||||
|
clipIndex++
|
||||||
|
) {
|
||||||
|
// 1つのクリッピングマスクに関して
|
||||||
|
const cc: T_ClippingContext = this._clippingContextListForMask[clipIndex];
|
||||||
|
|
||||||
|
// このクリップを利用する描画オブジェクト群全体を囲む矩形を計算
|
||||||
|
this.calcClippedDrawableTotalBounds(model, cc);
|
||||||
|
|
||||||
|
if (cc._isUsing) {
|
||||||
|
usingClipCount++; // 使用中としてカウント
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// マスク行列作成処理
|
||||||
|
if (usingClipCount > 0) {
|
||||||
|
this.setupLayoutBounds(0);
|
||||||
|
|
||||||
|
// サイズがレンダーテクスチャの枚数と合わない場合は合わせる
|
||||||
|
if (this._clearedMaskBufferFlags.length != this._renderTextureCount) {
|
||||||
|
this._clearedMaskBufferFlags.length = this._renderTextureCount;
|
||||||
|
for (let i = 0; i < this._renderTextureCount; i++) {
|
||||||
|
this._clearedMaskBufferFlags[i] = false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// マスクのクリアフラグを毎フレーム開始時に初期化
|
||||||
|
for (let i = 0; i < this._renderTextureCount; i++) {
|
||||||
|
this._clearedMaskBufferFlags[i] = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 実際にマスクを生成する
|
||||||
|
// 全てのマスクをどの様にレイアウトして描くかを決定し、ClipContext , ClippedDrawContext に記憶する
|
||||||
|
for (
|
||||||
|
let clipIndex = 0;
|
||||||
|
clipIndex < this._clippingContextListForMask.length;
|
||||||
|
clipIndex++
|
||||||
|
) {
|
||||||
|
// --- 実際に1つのマスクを描く ---
|
||||||
|
const clipContext: T_ClippingContext =
|
||||||
|
this._clippingContextListForMask[clipIndex];
|
||||||
|
const allClippedDrawRect: csmRect = clipContext._allClippedDrawRect; //このマスクを使う、全ての描画オブジェクトの論理座標上の囲み矩形
|
||||||
|
const layoutBoundsOnTex01 = clipContext._layoutBounds; //この中にマスクを収める
|
||||||
|
const margin = 0.05;
|
||||||
|
let scaleX = 0.0;
|
||||||
|
let scaleY = 0.0;
|
||||||
|
const ppu: number = model.getPixelsPerUnit();
|
||||||
|
const maskPixelSize: number = clipContext
|
||||||
|
.getClippingManager()
|
||||||
|
.getClippingMaskBufferSize();
|
||||||
|
const physicalMaskWidth: number =
|
||||||
|
layoutBoundsOnTex01.width * maskPixelSize;
|
||||||
|
const physicalMaskHeight: number =
|
||||||
|
layoutBoundsOnTex01.height * maskPixelSize;
|
||||||
|
|
||||||
|
this._tmpBoundsOnModel.setRect(allClippedDrawRect);
|
||||||
|
if (this._tmpBoundsOnModel.width * ppu > physicalMaskWidth) {
|
||||||
|
this._tmpBoundsOnModel.expand(allClippedDrawRect.width * margin, 0.0);
|
||||||
|
scaleX = layoutBoundsOnTex01.width / this._tmpBoundsOnModel.width;
|
||||||
|
} else {
|
||||||
|
scaleX = ppu / physicalMaskWidth;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this._tmpBoundsOnModel.height * ppu > physicalMaskHeight) {
|
||||||
|
this._tmpBoundsOnModel.expand(
|
||||||
|
0.0,
|
||||||
|
allClippedDrawRect.height * margin
|
||||||
|
);
|
||||||
|
scaleY = layoutBoundsOnTex01.height / this._tmpBoundsOnModel.height;
|
||||||
|
} else {
|
||||||
|
scaleY = ppu / physicalMaskHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
// マスク生成時に使う行列を求める
|
||||||
|
this.createMatrixForMask(
|
||||||
|
isRightHanded,
|
||||||
|
layoutBoundsOnTex01,
|
||||||
|
scaleX,
|
||||||
|
scaleY
|
||||||
|
);
|
||||||
|
|
||||||
|
clipContext._matrixForMask.setMatrix(this._tmpMatrixForMask.getArray());
|
||||||
|
clipContext._matrixForDraw.setMatrix(this._tmpMatrixForDraw.getArray());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* オフスクリーンの高精細マスク処理用の行列を計算する
|
||||||
|
*
|
||||||
|
* @param model モデルのインスタンス
|
||||||
|
* @param isRightHanded 処理が右手系であるか
|
||||||
|
* @param mvp モデルビュー投影行列
|
||||||
|
*/
|
||||||
|
public setupMatrixForOffscreenHighPrecision(
|
||||||
|
model: CubismModel,
|
||||||
|
isRightHanded: boolean,
|
||||||
|
mvp: CubismMatrix44
|
||||||
|
): void {
|
||||||
|
// 全てのクリッピングを用意する
|
||||||
|
// 同じクリップ(複数の場合はまとめて1つのクリップ)を使う場合は1度だけ設定する
|
||||||
|
let usingClipCount = 0;
|
||||||
|
for (
|
||||||
|
let clipIndex = 0;
|
||||||
|
clipIndex < this._clippingContextListForMask.length;
|
||||||
|
clipIndex++
|
||||||
|
) {
|
||||||
|
// 1つのクリッピングマスクに関して
|
||||||
|
const cc: T_ClippingContext = this._clippingContextListForMask[clipIndex];
|
||||||
|
|
||||||
|
// このクリップを利用する描画オブジェクト群全体を囲む矩形を計算
|
||||||
|
this.calcClippedOffscreenTotalBounds(model, cc);
|
||||||
|
|
||||||
|
if (cc._isUsing) {
|
||||||
|
usingClipCount++; //使用中としてカウント
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (usingClipCount <= 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// マスク行列作成処理
|
||||||
|
this.setupLayoutBounds(0);
|
||||||
|
|
||||||
|
// サイズがレンダーテクスチャの枚数と合わない場合は合わせる
|
||||||
|
if (this._clearedMaskBufferFlags.length != this._renderTextureCount) {
|
||||||
|
this._clearedMaskBufferFlags.length = this._renderTextureCount;
|
||||||
|
|
||||||
|
for (let i = 0; i < this._renderTextureCount; ++i) {
|
||||||
|
this._clearedMaskBufferFlags[i] = false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// マスクのクリアフラグを毎フレーム開始時に初期化
|
||||||
|
for (let i = 0; i < this._renderTextureCount; ++i) {
|
||||||
|
this._clearedMaskBufferFlags[i] = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 実際にマスクを生成する
|
||||||
|
// 全てのマスクをどの様にレイアウトして描くかを決定し、ClipContext , ClippedDrawContext に記憶する
|
||||||
|
for (
|
||||||
|
let clipIndex = 0;
|
||||||
|
clipIndex < this._clippingContextListForMask.length;
|
||||||
|
clipIndex++
|
||||||
|
) {
|
||||||
|
// --- 実際に1つのマスクを描く ---
|
||||||
|
const clipContext = this._clippingContextListForMask[clipIndex];
|
||||||
|
const allClippedDrawRect = clipContext._allClippedDrawRect; //このマスクを使う、全ての描画オブジェクトの論理座標上の囲み矩形
|
||||||
|
const layoutBoundsOnTex01 = clipContext._layoutBounds; //この中にマスクを収める
|
||||||
|
const margin = 0.05;
|
||||||
|
let scaleX = 0.0;
|
||||||
|
let scaleY = 0.0;
|
||||||
|
const ppu = model.getPixelsPerUnit();
|
||||||
|
const maskPixel = clipContext
|
||||||
|
.getClippingManager()
|
||||||
|
.getClippingMaskBufferSize();
|
||||||
|
const physicalMaskWidth = layoutBoundsOnTex01.width * maskPixel;
|
||||||
|
const physicalMaskHeight = layoutBoundsOnTex01.height * maskPixel;
|
||||||
|
|
||||||
|
this._tmpBoundsOnModel.setRect(allClippedDrawRect);
|
||||||
|
if (this._tmpBoundsOnModel.width * ppu > physicalMaskWidth) {
|
||||||
|
this._tmpBoundsOnModel.expand(allClippedDrawRect.width * margin, 0.0);
|
||||||
|
scaleX = layoutBoundsOnTex01.width / this._tmpBoundsOnModel.width;
|
||||||
|
} else {
|
||||||
|
scaleX = ppu / physicalMaskWidth;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this._tmpBoundsOnModel.height * ppu > physicalMaskHeight) {
|
||||||
|
this._tmpBoundsOnModel.expand(0.0, allClippedDrawRect.height * margin);
|
||||||
|
scaleY = layoutBoundsOnTex01.height / this._tmpBoundsOnModel.height;
|
||||||
|
} else {
|
||||||
|
scaleY = ppu / physicalMaskHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
// マスク生成時に使う行列を求める
|
||||||
|
this.createMatrixForMask(
|
||||||
|
isRightHanded,
|
||||||
|
layoutBoundsOnTex01,
|
||||||
|
scaleX,
|
||||||
|
scaleY
|
||||||
|
);
|
||||||
|
|
||||||
|
clipContext._matrixForMask.setMatrix(this._tmpMatrixForMask.getArray());
|
||||||
|
clipContext._matrixForDraw.setMatrix(this._tmpMatrixForDraw.getArray());
|
||||||
|
|
||||||
|
// clipContext * mvp^-1
|
||||||
|
const invertMvp = mvp.getInvert();
|
||||||
|
clipContext._matrixForDraw.multiplyByMatrix(invertMvp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* マスクを使う描画オブジェクトの全体の矩形を計算する。
|
||||||
|
*
|
||||||
|
* @param model モデルのインスタンス
|
||||||
|
* @param clippingContext クリッピングコンテキスト
|
||||||
|
*/
|
||||||
|
public calcClippedOffscreenTotalBounds(
|
||||||
|
model: CubismModel,
|
||||||
|
clippingContext: T_ClippingContext
|
||||||
|
): void {
|
||||||
|
// 被クリッピングマスク(マスクされる描画オブジェクト)の全体の矩形
|
||||||
|
let clippedDrawTotalMinX = Number.MAX_VALUE,
|
||||||
|
clippedDrawTotalMinY = Number.MAX_VALUE;
|
||||||
|
let clippedDrawTotalMaxX = -Number.MAX_VALUE,
|
||||||
|
clippedDrawTotalMaxY = -Number.MAX_VALUE;
|
||||||
|
|
||||||
|
// このマスクが実際に必要か判定する
|
||||||
|
// このクリッピングを利用する「描画オブジェクト」がひとつでも使用可能であればマスクを生成する必要がある
|
||||||
|
const clippedOffscreenCount =
|
||||||
|
clippingContext._clippedOffscreenIndexList.length;
|
||||||
|
|
||||||
|
const clippedOffscreenChildDrawableIndexList = new Array<number>();
|
||||||
|
for (
|
||||||
|
let clippedOffscreenIndex = 0;
|
||||||
|
clippedOffscreenIndex < clippedOffscreenCount;
|
||||||
|
clippedOffscreenIndex++
|
||||||
|
) {
|
||||||
|
// マスクを使用する描画オブジェクトの描画される矩形を求める
|
||||||
|
const offscreenIndex =
|
||||||
|
clippingContext._clippedOffscreenIndexList[clippedOffscreenIndex];
|
||||||
|
|
||||||
|
this.getOffscreenChildDrawableIndexList(
|
||||||
|
model,
|
||||||
|
offscreenIndex,
|
||||||
|
clippedOffscreenChildDrawableIndexList
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const childDrawableCount = clippedOffscreenChildDrawableIndexList.length;
|
||||||
|
for (
|
||||||
|
let childDrawableIndex = 0;
|
||||||
|
childDrawableIndex < childDrawableCount;
|
||||||
|
childDrawableIndex++
|
||||||
|
) {
|
||||||
|
const drawableVertexCount = model.getDrawableVertexCount(
|
||||||
|
clippedOffscreenChildDrawableIndexList[childDrawableIndex]
|
||||||
|
);
|
||||||
|
const drawableVertexes = model.getDrawableVertices(
|
||||||
|
clippedOffscreenChildDrawableIndexList[childDrawableIndex]
|
||||||
|
);
|
||||||
|
|
||||||
|
let minX = Number.MAX_VALUE,
|
||||||
|
minY = Number.MAX_VALUE;
|
||||||
|
let maxX = -Number.MAX_VALUE,
|
||||||
|
maxY = -Number.MAX_VALUE;
|
||||||
|
|
||||||
|
const loop = drawableVertexCount * Constant.vertexStep;
|
||||||
|
for (
|
||||||
|
let pi = Constant.vertexOffset;
|
||||||
|
pi < loop;
|
||||||
|
pi += Constant.vertexStep
|
||||||
|
) {
|
||||||
|
const x = drawableVertexes[pi];
|
||||||
|
const y = drawableVertexes[pi + 1];
|
||||||
|
if (x < minX) minX = x;
|
||||||
|
if (x > maxX) maxX = x;
|
||||||
|
if (y < minY) minY = y;
|
||||||
|
if (y > maxY) maxY = y;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (minX == Number.MAX_VALUE) continue; //有効な点がひとつも取れなかったのでスキップする
|
||||||
|
|
||||||
|
// 全体の矩形に反映
|
||||||
|
if (minX < clippedDrawTotalMinX) clippedDrawTotalMinX = minX;
|
||||||
|
if (minY < clippedDrawTotalMinY) clippedDrawTotalMinY = minY;
|
||||||
|
if (maxX > clippedDrawTotalMaxX) clippedDrawTotalMaxX = maxX;
|
||||||
|
if (maxY > clippedDrawTotalMaxY) clippedDrawTotalMaxY = maxY;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (clippedDrawTotalMinX == Number.MAX_VALUE) {
|
||||||
|
clippingContext._allClippedDrawRect.x = 0.0;
|
||||||
|
clippingContext._allClippedDrawRect.y = 0.0;
|
||||||
|
clippingContext._allClippedDrawRect.width = 0.0;
|
||||||
|
clippingContext._allClippedDrawRect.height = 0.0;
|
||||||
|
clippingContext._isUsing = false;
|
||||||
|
} else {
|
||||||
|
clippingContext._isUsing = true;
|
||||||
|
const w = clippedDrawTotalMaxX - clippedDrawTotalMinX;
|
||||||
|
const h = clippedDrawTotalMaxY - clippedDrawTotalMinY;
|
||||||
|
clippingContext._allClippedDrawRect.x = clippedDrawTotalMinX;
|
||||||
|
clippingContext._allClippedDrawRect.y = clippedDrawTotalMinY;
|
||||||
|
clippingContext._allClippedDrawRect.width = w;
|
||||||
|
clippingContext._allClippedDrawRect.height = h;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* マスクを使う描画オブジェクトの全体の矩形を計算する。
|
||||||
|
*
|
||||||
|
* @param model モデルのインスタンス
|
||||||
|
* @param offscreenIndex オフスクリーンのインデックス
|
||||||
|
* @param childDrawableIndexList オフスクリーンの子Drawableのインデックスリスト
|
||||||
|
*/
|
||||||
|
public getOffscreenChildDrawableIndexList(
|
||||||
|
model: CubismModel,
|
||||||
|
offscreenIndex: number,
|
||||||
|
childDrawableIndexList: Array<number>
|
||||||
|
): void {
|
||||||
|
// 親オブジェクトを取得
|
||||||
|
const ownerIndex = model.getOffscreenOwnerIndices()[offscreenIndex];
|
||||||
|
|
||||||
|
// パーツのみ
|
||||||
|
this.getPartChildDrawableIndexList(
|
||||||
|
model,
|
||||||
|
ownerIndex,
|
||||||
|
childDrawableIndexList
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* パーツの子Drawableのインデックスリストを取得する。
|
||||||
|
*
|
||||||
|
* @param model モデルのインスタンス
|
||||||
|
* @param partIndex パーツのインデックス
|
||||||
|
* @param childDrawableIndexList パーツの子Drawableのインデックスリスト
|
||||||
|
*/
|
||||||
|
public getPartChildDrawableIndexList(
|
||||||
|
model: CubismModel,
|
||||||
|
partIndex: number,
|
||||||
|
childDrawableIndexList: Array<number>
|
||||||
|
): void {
|
||||||
|
const childDrawObjects =
|
||||||
|
model.getPartsHierarchy()[partIndex].childDrawObjects;
|
||||||
|
childDrawableIndexList.push(...childDrawObjects.drawableIndices);
|
||||||
|
|
||||||
|
for (let i = 0; i < childDrawObjects.offscreenIndices.length; ++i) {
|
||||||
|
this.getOffscreenChildDrawableIndexList(
|
||||||
|
model,
|
||||||
|
childDrawObjects.offscreenIndices[i],
|
||||||
|
childDrawableIndexList
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* マスク作成・描画用の行列を作成する。
|
||||||
|
* @param isRightHanded 座標を右手系として扱うかを指定
|
||||||
|
* @param layoutBoundsOnTex01 マスクを収める領域
|
||||||
|
* @param scaleX 描画オブジェクトの伸縮率
|
||||||
|
* @param scaleY 描画オブジェクトの伸縮率
|
||||||
|
*/
|
||||||
|
public createMatrixForMask(
|
||||||
|
isRightHanded: boolean,
|
||||||
|
layoutBoundsOnTex01: csmRect,
|
||||||
|
scaleX: number,
|
||||||
|
scaleY: number
|
||||||
|
): void {
|
||||||
|
this._tmpMatrix.loadIdentity();
|
||||||
|
{
|
||||||
|
// Layout0..1 を -1..1に変換
|
||||||
|
this._tmpMatrix.translateRelative(-1.0, -1.0);
|
||||||
|
this._tmpMatrix.scaleRelative(2.0, 2.0);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
// view to Layout0..1
|
||||||
|
this._tmpMatrix.translateRelative(
|
||||||
|
layoutBoundsOnTex01.x,
|
||||||
|
layoutBoundsOnTex01.y
|
||||||
|
); //new = [translate]
|
||||||
|
this._tmpMatrix.scaleRelative(scaleX, scaleY); //new = [translate][scale]
|
||||||
|
this._tmpMatrix.translateRelative(
|
||||||
|
-this._tmpBoundsOnModel.x,
|
||||||
|
-this._tmpBoundsOnModel.y
|
||||||
|
); //new = [translate][scale][translate]
|
||||||
|
}
|
||||||
|
// tmpMatrixForMask が計算結果
|
||||||
|
this._tmpMatrixForMask.setMatrix(this._tmpMatrix.getArray());
|
||||||
|
|
||||||
|
this._tmpMatrix.loadIdentity();
|
||||||
|
{
|
||||||
|
this._tmpMatrix.translateRelative(
|
||||||
|
layoutBoundsOnTex01.x,
|
||||||
|
layoutBoundsOnTex01.y * (isRightHanded ? -1.0 : 1.0)
|
||||||
|
); //new = [translate]
|
||||||
|
this._tmpMatrix.scaleRelative(
|
||||||
|
scaleX,
|
||||||
|
scaleY * (isRightHanded ? -1.0 : 1.0)
|
||||||
|
); //new = [translate][scale]
|
||||||
|
this._tmpMatrix.translateRelative(
|
||||||
|
-this._tmpBoundsOnModel.x,
|
||||||
|
-this._tmpBoundsOnModel.y
|
||||||
|
); //new = [translate][scale][translate]
|
||||||
|
}
|
||||||
|
|
||||||
|
this._tmpMatrixForDraw.setMatrix(this._tmpMatrix.getArray());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* クリッピングコンテキストを配置するレイアウト
|
||||||
|
* 指定された数のレンダーテクスチャを極力いっぱいに使ってマスクをレイアウトする
|
||||||
|
* マスクグループの数が4以下ならRGBA各チャンネルに一つずつマスクを配置し、5以上6以下ならRGBAを2,2,1,1と配置する。
|
||||||
|
*
|
||||||
|
* @param usingClipCount 配置するクリッピングコンテキストの数
|
||||||
|
*/
|
||||||
|
public setupLayoutBounds(usingClipCount: number): void {
|
||||||
|
const useClippingMaskMaxCount =
|
||||||
|
this._renderTextureCount <= 1
|
||||||
|
? ClippingMaskMaxCountOnDefault
|
||||||
|
: ClippingMaskMaxCountOnMultiRenderTexture * this._renderTextureCount;
|
||||||
|
|
||||||
|
if (usingClipCount <= 0 || usingClipCount > useClippingMaskMaxCount) {
|
||||||
|
if (usingClipCount > useClippingMaskMaxCount) {
|
||||||
|
// マスクの制限数の警告を出す
|
||||||
|
CubismLogError(
|
||||||
|
'not supported mask count : {0}\n[Details] render texture count : {1}, mask count : {2}',
|
||||||
|
usingClipCount - useClippingMaskMaxCount,
|
||||||
|
this._renderTextureCount,
|
||||||
|
usingClipCount
|
||||||
|
);
|
||||||
|
}
|
||||||
|
// この場合は一つのマスクターゲットを毎回クリアして使用する
|
||||||
|
for (
|
||||||
|
let index = 0;
|
||||||
|
index < this._clippingContextListForMask.length;
|
||||||
|
index++
|
||||||
|
) {
|
||||||
|
const clipContext: T_ClippingContext =
|
||||||
|
this._clippingContextListForMask[index];
|
||||||
|
clipContext._layoutChannelIndex = 0; // どうせ毎回消すので固定
|
||||||
|
clipContext._layoutBounds.x = 0.0;
|
||||||
|
clipContext._layoutBounds.y = 0.0;
|
||||||
|
clipContext._layoutBounds.width = 1.0;
|
||||||
|
clipContext._layoutBounds.height = 1.0;
|
||||||
|
clipContext._bufferIndex = 0;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// レンダーテクスチャが1枚なら9分割する(最大36枚)
|
||||||
|
const layoutCountMaxValue = this._renderTextureCount <= 1 ? 9 : 8;
|
||||||
|
|
||||||
|
// 指定された数のレンダーテクスチャを極力いっぱいに使ってマスクをレイアウトする(デフォルトなら1)。
|
||||||
|
// マスクグループの数が4以下ならRGBA各チャンネルに1つずつマスクを配置し、5以上6以下ならRGBAを2,2,1,1と配置する。
|
||||||
|
let countPerSheetDiv: number = usingClipCount / this._renderTextureCount; // レンダーテクスチャ1枚あたり何枚割り当てるか。
|
||||||
|
const reduceLayoutTextureCount: number =
|
||||||
|
usingClipCount % this._renderTextureCount; // レイアウトの数を1枚減らすレンダーテクスチャの数(この数だけのレンダーテクスチャが対象)。
|
||||||
|
|
||||||
|
// 1枚に割り当てるマスクの分割数を取りたいため、小数点は切り上げる
|
||||||
|
countPerSheetDiv = Math.ceil(countPerSheetDiv);
|
||||||
|
|
||||||
|
// RGBAを順番に使っていく
|
||||||
|
let divCount: number = countPerSheetDiv / ColorChannelCount; // 1チャンネルに配置する基本のマスク
|
||||||
|
const modCount: number = countPerSheetDiv % ColorChannelCount; // 余り、この番号のチャンネルまでに一つずつ配分する(インデックスではない)
|
||||||
|
|
||||||
|
// 小数点は切り捨てる
|
||||||
|
divCount = ~~divCount;
|
||||||
|
|
||||||
|
// RGBAそれぞれのチャンネルを用意していく(0:R, 1:G, 2:B, 3:A)
|
||||||
|
let curClipIndex = 0; // 順番に設定していく
|
||||||
|
|
||||||
|
for (
|
||||||
|
let renderTextureIndex = 0;
|
||||||
|
renderTextureIndex < this._renderTextureCount;
|
||||||
|
renderTextureIndex++
|
||||||
|
) {
|
||||||
|
for (
|
||||||
|
let channelIndex = 0;
|
||||||
|
channelIndex < ColorChannelCount;
|
||||||
|
channelIndex++
|
||||||
|
) {
|
||||||
|
// このチャンネルにレイアウトする数
|
||||||
|
// NOTE: レイアウト数 = 1チャンネルに配置する基本のマスク + 余りのマスクを置くチャンネルなら1つ追加
|
||||||
|
let layoutCount: number = divCount + (channelIndex < modCount ? 1 : 0);
|
||||||
|
|
||||||
|
// レイアウトの数を1枚減らす場合にそれを行うチャンネルを決定
|
||||||
|
// divが0の時は正常なインデックスの範囲内になるように調整
|
||||||
|
const checkChannelIndex = modCount + (divCount < 1 ? -1 : 0);
|
||||||
|
|
||||||
|
// 今回が対象のチャンネルかつ、レイアウトの数を1枚減らすレンダーテクスチャが存在する場合
|
||||||
|
if (channelIndex == checkChannelIndex && reduceLayoutTextureCount > 0) {
|
||||||
|
// 現在のレンダーテクスチャが、対象のレンダーテクスチャであればレイアウトの数を1枚減らす。
|
||||||
|
layoutCount -= !(renderTextureIndex < reduceLayoutTextureCount)
|
||||||
|
? 1
|
||||||
|
: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 分割方法を決定する
|
||||||
|
if (layoutCount == 0) {
|
||||||
|
// 何もしない
|
||||||
|
} else if (layoutCount == 1) {
|
||||||
|
// 全てをそのまま使う
|
||||||
|
const clipContext: T_ClippingContext =
|
||||||
|
this._clippingContextListForMask[curClipIndex++];
|
||||||
|
clipContext._layoutChannelIndex = channelIndex;
|
||||||
|
clipContext._layoutBounds.x = 0.0;
|
||||||
|
clipContext._layoutBounds.y = 0.0;
|
||||||
|
clipContext._layoutBounds.width = 1.0;
|
||||||
|
clipContext._layoutBounds.height = 1.0;
|
||||||
|
clipContext._bufferIndex = renderTextureIndex;
|
||||||
|
} else if (layoutCount == 2) {
|
||||||
|
for (let i = 0; i < layoutCount; i++) {
|
||||||
|
let xpos: number = i % 2;
|
||||||
|
|
||||||
|
// 小数点は切り捨てる
|
||||||
|
xpos = ~~xpos;
|
||||||
|
|
||||||
|
const cc: T_ClippingContext =
|
||||||
|
this._clippingContextListForMask[curClipIndex++];
|
||||||
|
cc._layoutChannelIndex = channelIndex;
|
||||||
|
|
||||||
|
// UVを2つに分解して使う
|
||||||
|
cc._layoutBounds.x = xpos * 0.5;
|
||||||
|
cc._layoutBounds.y = 0.0;
|
||||||
|
cc._layoutBounds.width = 0.5;
|
||||||
|
cc._layoutBounds.height = 1.0;
|
||||||
|
cc._bufferIndex = renderTextureIndex;
|
||||||
|
}
|
||||||
|
} else if (layoutCount <= 4) {
|
||||||
|
// 4分割して使う
|
||||||
|
for (let i = 0; i < layoutCount; i++) {
|
||||||
|
let xpos: number = i % 2;
|
||||||
|
let ypos: number = i / 2;
|
||||||
|
|
||||||
|
// 小数点は切り捨てる
|
||||||
|
xpos = ~~xpos;
|
||||||
|
ypos = ~~ypos;
|
||||||
|
|
||||||
|
const cc = this._clippingContextListForMask[curClipIndex++];
|
||||||
|
cc._layoutChannelIndex = channelIndex;
|
||||||
|
|
||||||
|
cc._layoutBounds.x = xpos * 0.5;
|
||||||
|
cc._layoutBounds.y = ypos * 0.5;
|
||||||
|
cc._layoutBounds.width = 0.5;
|
||||||
|
cc._layoutBounds.height = 0.5;
|
||||||
|
cc._bufferIndex = renderTextureIndex;
|
||||||
|
}
|
||||||
|
} else if (layoutCount <= layoutCountMaxValue) {
|
||||||
|
// 9分割して使う
|
||||||
|
for (let i = 0; i < layoutCount; i++) {
|
||||||
|
let xpos = i % 3;
|
||||||
|
let ypos = i / 3;
|
||||||
|
|
||||||
|
// 小数点は切り捨てる
|
||||||
|
xpos = ~~xpos;
|
||||||
|
ypos = ~~ypos;
|
||||||
|
|
||||||
|
const cc: T_ClippingContext =
|
||||||
|
this._clippingContextListForMask[curClipIndex++];
|
||||||
|
cc._layoutChannelIndex = channelIndex;
|
||||||
|
|
||||||
|
cc._layoutBounds.x = xpos / 3.0;
|
||||||
|
cc._layoutBounds.y = ypos / 3.0;
|
||||||
|
cc._layoutBounds.width = 1.0 / 3.0;
|
||||||
|
cc._layoutBounds.height = 1.0 / 3.0;
|
||||||
|
cc._bufferIndex = renderTextureIndex;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// マスクの制限枚数を超えた場合の処理
|
||||||
|
CubismLogError(
|
||||||
|
'not supported mask count : {0}\n[Details] render texture count : {1}, mask count : {2}',
|
||||||
|
usingClipCount - useClippingMaskMaxCount,
|
||||||
|
this._renderTextureCount,
|
||||||
|
usingClipCount
|
||||||
|
);
|
||||||
|
|
||||||
|
// SetupShaderProgramでオーバーアクセスが発生するので仮で数値を入れる
|
||||||
|
// もちろん描画結果は正しいものではなくなる
|
||||||
|
for (let index = 0; index < layoutCount; index++) {
|
||||||
|
const cc: T_ClippingContext =
|
||||||
|
this._clippingContextListForMask[curClipIndex++];
|
||||||
|
|
||||||
|
cc._layoutChannelIndex = 0;
|
||||||
|
|
||||||
|
cc._layoutBounds.x = 0.0;
|
||||||
|
cc._layoutBounds.y = 0.0;
|
||||||
|
cc._layoutBounds.width = 1.0;
|
||||||
|
cc._layoutBounds.height = 1.0;
|
||||||
|
cc._bufferIndex = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* マスクされる描画オブジェクト群全体を囲む矩形(モデル座標系)を計算する
|
||||||
|
* @param model モデルのインスタンス
|
||||||
|
* @param clippingContext クリッピングマスクのコンテキスト
|
||||||
|
*/
|
||||||
|
public calcClippedDrawableTotalBounds(
|
||||||
|
model: CubismModel,
|
||||||
|
clippingContext: T_ClippingContext
|
||||||
|
): void {
|
||||||
|
// 被クリッピングマスク(マスクされる描画オブジェクト)の全体の矩形
|
||||||
|
let clippedDrawTotalMinX: number = Number.MAX_VALUE;
|
||||||
|
let clippedDrawTotalMinY: number = Number.MAX_VALUE;
|
||||||
|
let clippedDrawTotalMaxX: number = Number.MIN_VALUE;
|
||||||
|
let clippedDrawTotalMaxY: number = Number.MIN_VALUE;
|
||||||
|
|
||||||
|
// このマスクが実際に必要か判定する
|
||||||
|
// このクリッピングを利用する「描画オブジェクト」がひとつでも使用可能であればマスクを生成する必要がある
|
||||||
|
const clippedDrawCount: number =
|
||||||
|
clippingContext._clippedDrawableIndexList.length;
|
||||||
|
|
||||||
|
for (
|
||||||
|
let clippedDrawableIndex = 0;
|
||||||
|
clippedDrawableIndex < clippedDrawCount;
|
||||||
|
clippedDrawableIndex++
|
||||||
|
) {
|
||||||
|
// マスクを使用する描画オブジェクトの描画される矩形を求める
|
||||||
|
const drawableIndex: number =
|
||||||
|
clippingContext._clippedDrawableIndexList[clippedDrawableIndex];
|
||||||
|
|
||||||
|
const drawableVertexCount: number =
|
||||||
|
model.getDrawableVertexCount(drawableIndex);
|
||||||
|
const drawableVertexes: Float32Array =
|
||||||
|
model.getDrawableVertices(drawableIndex);
|
||||||
|
|
||||||
|
let minX: number = Number.MAX_VALUE;
|
||||||
|
let minY: number = Number.MAX_VALUE;
|
||||||
|
let maxX: number = -Number.MAX_VALUE;
|
||||||
|
let maxY: number = -Number.MAX_VALUE;
|
||||||
|
|
||||||
|
const loop: number = drawableVertexCount * Constant.vertexStep;
|
||||||
|
for (
|
||||||
|
let pi: number = Constant.vertexOffset;
|
||||||
|
pi < loop;
|
||||||
|
pi += Constant.vertexStep
|
||||||
|
) {
|
||||||
|
const x: number = drawableVertexes[pi];
|
||||||
|
const y: number = drawableVertexes[pi + 1];
|
||||||
|
|
||||||
|
if (x < minX) {
|
||||||
|
minX = x;
|
||||||
|
}
|
||||||
|
if (x > maxX) {
|
||||||
|
maxX = x;
|
||||||
|
}
|
||||||
|
if (y < minY) {
|
||||||
|
minY = y;
|
||||||
|
}
|
||||||
|
if (y > maxY) {
|
||||||
|
maxY = y;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 有効な点が一つも取れなかったのでスキップ
|
||||||
|
if (minX == Number.MAX_VALUE) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 全体の矩形に反映
|
||||||
|
if (minX < clippedDrawTotalMinX) {
|
||||||
|
clippedDrawTotalMinX = minX;
|
||||||
|
}
|
||||||
|
if (minY < clippedDrawTotalMinY) {
|
||||||
|
clippedDrawTotalMinY = minY;
|
||||||
|
}
|
||||||
|
if (maxX > clippedDrawTotalMaxX) {
|
||||||
|
clippedDrawTotalMaxX = maxX;
|
||||||
|
}
|
||||||
|
if (maxY > clippedDrawTotalMaxY) {
|
||||||
|
clippedDrawTotalMaxY = maxY;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (clippedDrawTotalMinX == Number.MAX_VALUE) {
|
||||||
|
clippingContext._allClippedDrawRect.x = 0.0;
|
||||||
|
clippingContext._allClippedDrawRect.y = 0.0;
|
||||||
|
clippingContext._allClippedDrawRect.width = 0.0;
|
||||||
|
clippingContext._allClippedDrawRect.height = 0.0;
|
||||||
|
clippingContext._isUsing = false;
|
||||||
|
} else {
|
||||||
|
clippingContext._isUsing = true;
|
||||||
|
const w: number = clippedDrawTotalMaxX - clippedDrawTotalMinX;
|
||||||
|
const h: number = clippedDrawTotalMaxY - clippedDrawTotalMinY;
|
||||||
|
clippingContext._allClippedDrawRect.x = clippedDrawTotalMinX;
|
||||||
|
clippingContext._allClippedDrawRect.y = clippedDrawTotalMinY;
|
||||||
|
clippingContext._allClippedDrawRect.width = w;
|
||||||
|
clippingContext._allClippedDrawRect.height = h;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 画面描画に使用するクリッピングマスクのリストを取得する
|
||||||
|
* @return 画面描画に使用するクリッピングマスクのリスト
|
||||||
|
*/
|
||||||
|
public getClippingContextListForDraw(): Array<T_ClippingContext> {
|
||||||
|
return this._clippingContextListForDraw;
|
||||||
|
}
|
||||||
|
|
||||||
|
public getClippingContextListForOffscreen(): Array<T_ClippingContext> {
|
||||||
|
return this._clippingContextListForOffscreen;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* クリッピングマスクバッファのサイズを取得する
|
||||||
|
* @return クリッピングマスクバッファのサイズ
|
||||||
|
*/
|
||||||
|
public getClippingMaskBufferSize(): number {
|
||||||
|
return this._clippingMaskBufferSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* このバッファのレンダーテクスチャの枚数を取得する
|
||||||
|
* @return このバッファのレンダーテクスチャの枚数
|
||||||
|
*/
|
||||||
|
public getRenderTextureCount(): number {
|
||||||
|
return this._renderTextureCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* カラーチャンネル(RGBA)のフラグを取得する
|
||||||
|
* @param channelNo カラーチャンネル(RGBA)の番号(0:R, 1:G, 2:B, 3:A)
|
||||||
|
*/
|
||||||
|
public getChannelFlagAsColor(channelNo: number): CubismTextureColor {
|
||||||
|
return this._channelColors[channelNo];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* クリッピングマスクバッファのサイズを設定する
|
||||||
|
* @param size クリッピングマスクバッファのサイズ
|
||||||
|
*/
|
||||||
|
public setClippingMaskBufferSize(size: number): void {
|
||||||
|
this._clippingMaskBufferSize = size;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected _clearedMaskBufferFlags: Array<boolean>; //マスクのクリアフラグの配列
|
||||||
|
|
||||||
|
protected _channelColors: Array<CubismTextureColor>;
|
||||||
|
protected _clippingContextListForMask: Array<T_ClippingContext>; // マスク用クリッピングコンテキストのリスト
|
||||||
|
protected _clippingContextListForDraw: Array<T_ClippingContext>; // 描画用クリッピングコンテキストのリスト
|
||||||
|
protected _clippingContextListForOffscreen: Array<T_ClippingContext>; // オフスクリーン用クリッピングコンテキストのリスト
|
||||||
|
protected _clippingMaskBufferSize: number; // クリッピングマスクのバッファサイズ(初期値:256)
|
||||||
|
protected _renderTextureCount: number; // 生成するレンダーテクスチャの枚数
|
||||||
|
|
||||||
|
protected _tmpMatrix: CubismMatrix44; // マスク計算用の行列
|
||||||
|
protected _tmpMatrixForMask: CubismMatrix44; // マスク計算用の行列
|
||||||
|
protected _tmpMatrixForDraw: CubismMatrix44; // マスク計算用の行列
|
||||||
|
protected _tmpBoundsOnModel: csmRect; // マスク配置計算用の矩形
|
||||||
|
|
||||||
|
protected _clippingContexttConstructor: ClippingContextConstructor<T_ClippingContext>;
|
||||||
|
}
|
||||||
@ -0,0 +1,591 @@
|
|||||||
|
/**
|
||||||
|
* Copyright(c) Live2D Inc. All rights reserved.
|
||||||
|
*
|
||||||
|
* Use of this source code is governed by the Live2D Open Software license
|
||||||
|
* that can be found at https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { updateSize } from '../utils/cubismarrayutils';
|
||||||
|
import { CubismLogError } from '../utils/cubismdebug';
|
||||||
|
import { CubismRenderTarget_WebGL } from './cubismrendertarget_webgl';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* フレームバッファなどのコンテナのクラス
|
||||||
|
*/
|
||||||
|
class CubismRenderTargetContainer {
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
*
|
||||||
|
* @param colorBuffer カラーバッファ
|
||||||
|
* @param renderTexture レンダーテクスチャ
|
||||||
|
* @param inUse 使用中かどうか
|
||||||
|
*/
|
||||||
|
public constructor(
|
||||||
|
colorBuffer: WebGLTexture = null,
|
||||||
|
renderTexture: WebGLFramebuffer = null,
|
||||||
|
inUse: boolean = false
|
||||||
|
) {
|
||||||
|
this.colorBuffer = colorBuffer;
|
||||||
|
this.renderTexture = renderTexture;
|
||||||
|
this.inUse = inUse;
|
||||||
|
}
|
||||||
|
|
||||||
|
public clear(): void {
|
||||||
|
this.colorBuffer = null;
|
||||||
|
this.renderTexture = null;
|
||||||
|
this.inUse = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* カラーバッファを取得
|
||||||
|
*
|
||||||
|
* @returns カラーバッファ
|
||||||
|
*/
|
||||||
|
public getColorBuffer(): WebGLTexture {
|
||||||
|
return this.colorBuffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* レンダーテクスチャを取得
|
||||||
|
*
|
||||||
|
* @returns レンダーテクスチャ
|
||||||
|
*/
|
||||||
|
public getRenderTexture(): WebGLFramebuffer {
|
||||||
|
return this.renderTexture;
|
||||||
|
}
|
||||||
|
|
||||||
|
public colorBuffer: WebGLTexture; // colorBuffer
|
||||||
|
public renderTexture: WebGLFramebuffer; // renderTarget
|
||||||
|
public inUse: boolean; // Whether this container's render target is currently in use
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* WebGLContextごとのリソース管理を行う内部クラス
|
||||||
|
*/
|
||||||
|
class CubismWebGLContextManager {
|
||||||
|
constructor(gl: WebGLRenderingContext | WebGL2RenderingContext) {
|
||||||
|
this.gl = gl;
|
||||||
|
this.offscreenRenderTargetContainers =
|
||||||
|
new Array<CubismRenderTargetContainer>();
|
||||||
|
this.previousActiveRenderTextureMaxCount = 0;
|
||||||
|
this.currentActiveRenderTextureCount = 0;
|
||||||
|
this.hasResetThisFrame = false;
|
||||||
|
this.width = 0;
|
||||||
|
this.height = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public release(): void {
|
||||||
|
if (this.offscreenRenderTargetContainers != null) {
|
||||||
|
for (
|
||||||
|
let index = 0;
|
||||||
|
index < this.offscreenRenderTargetContainers.length;
|
||||||
|
++index
|
||||||
|
) {
|
||||||
|
const container = this.offscreenRenderTargetContainers[index];
|
||||||
|
this.gl.deleteTexture(container.colorBuffer);
|
||||||
|
this.gl.deleteFramebuffer(container.renderTexture);
|
||||||
|
}
|
||||||
|
this.offscreenRenderTargetContainers.length = 0;
|
||||||
|
this.offscreenRenderTargetContainers = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public gl: WebGLRenderingContext | WebGL2RenderingContext; // WebGLContext
|
||||||
|
public offscreenRenderTargetContainers: Array<CubismRenderTargetContainer>; // オフスクリーン描画用レンダーターゲットのリスト
|
||||||
|
public previousActiveRenderTextureMaxCount: number; // 直前のアクティブなレンダーターゲットの最大数
|
||||||
|
public currentActiveRenderTextureCount: number; // 現在のアクティブなレンダーターゲットの数
|
||||||
|
public hasResetThisFrame: boolean; // 今フレームでリセットされたかどうか
|
||||||
|
public width: number; // 幅
|
||||||
|
public height: number; // 高さ
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* WebGL用オフスクリーン描画機能を管理するマネージャ
|
||||||
|
* オフスクリーン描画機能に必要なフレームバッファなどを含むコンテナを管理する。
|
||||||
|
* 複数のWebGLContextに対応。
|
||||||
|
*/
|
||||||
|
export class CubismWebGLOffscreenManager {
|
||||||
|
/**
|
||||||
|
* コンストラクタ
|
||||||
|
*/
|
||||||
|
private constructor() {
|
||||||
|
this._contextManagers = new Map<
|
||||||
|
WebGLRenderingContext | WebGL2RenderingContext,
|
||||||
|
CubismWebGLContextManager
|
||||||
|
>();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* デストラクタ相当の処理
|
||||||
|
*/
|
||||||
|
public release(): void {
|
||||||
|
if (this._contextManagers != null) {
|
||||||
|
for (const manager of this._contextManagers.values()) {
|
||||||
|
manager.release();
|
||||||
|
}
|
||||||
|
this._contextManagers.clear();
|
||||||
|
this._contextManagers = null;
|
||||||
|
}
|
||||||
|
CubismWebGLOffscreenManager._instance = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* インスタンスの取得
|
||||||
|
*
|
||||||
|
* @return インスタンス
|
||||||
|
*/
|
||||||
|
public static getInstance(): CubismWebGLOffscreenManager {
|
||||||
|
if (this._instance == null) {
|
||||||
|
this._instance = new CubismWebGLOffscreenManager();
|
||||||
|
}
|
||||||
|
|
||||||
|
return this._instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* WebGLContextに対応するマネージャーを取得または作成
|
||||||
|
*
|
||||||
|
* @param gl WebGLRenderingContextまたはWebGL2RenderingContext
|
||||||
|
* @return WebGLContextManager
|
||||||
|
*/
|
||||||
|
private getContextManager(
|
||||||
|
gl: WebGLRenderingContext | WebGL2RenderingContext
|
||||||
|
): CubismWebGLContextManager {
|
||||||
|
if (!this._contextManagers.has(gl)) {
|
||||||
|
this._contextManagers.set(gl, new CubismWebGLContextManager(gl));
|
||||||
|
}
|
||||||
|
return this._contextManagers.get(gl);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 指定されたWebGLContextのマネージャーを削除
|
||||||
|
*
|
||||||
|
* @param gl WebGLRenderingContextまたはWebGL2RenderingContext
|
||||||
|
*/
|
||||||
|
public removeContext(
|
||||||
|
gl: WebGLRenderingContext | WebGL2RenderingContext
|
||||||
|
): void {
|
||||||
|
if (this._contextManagers.has(gl)) {
|
||||||
|
const manager = this._contextManagers.get(gl);
|
||||||
|
manager.release();
|
||||||
|
this._contextManagers.delete(gl);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 初期化処理
|
||||||
|
*
|
||||||
|
* @param gl WebGLRenderingContextまたはWebGL2RenderingContext
|
||||||
|
* @param width 幅
|
||||||
|
* @param height 高さ
|
||||||
|
*/
|
||||||
|
public initialize(
|
||||||
|
gl: WebGLRenderingContext | WebGL2RenderingContext,
|
||||||
|
width: number,
|
||||||
|
height: number
|
||||||
|
): void {
|
||||||
|
const contextManager = this.getContextManager(gl);
|
||||||
|
|
||||||
|
// initialize offscreenRenderTargetContainers
|
||||||
|
if (contextManager.offscreenRenderTargetContainers != null) {
|
||||||
|
for (
|
||||||
|
let index = 0;
|
||||||
|
index < contextManager.offscreenRenderTargetContainers.length;
|
||||||
|
++index
|
||||||
|
) {
|
||||||
|
const container = contextManager.offscreenRenderTargetContainers[index];
|
||||||
|
contextManager.gl.deleteTexture(container.colorBuffer);
|
||||||
|
contextManager.gl.deleteFramebuffer(container.renderTexture);
|
||||||
|
container.clear();
|
||||||
|
}
|
||||||
|
contextManager.offscreenRenderTargetContainers.length = 0;
|
||||||
|
} else {
|
||||||
|
contextManager.offscreenRenderTargetContainers =
|
||||||
|
new Array<CubismRenderTargetContainer>();
|
||||||
|
}
|
||||||
|
|
||||||
|
contextManager.width = width;
|
||||||
|
contextManager.height = height;
|
||||||
|
contextManager.previousActiveRenderTextureMaxCount = 0;
|
||||||
|
contextManager.currentActiveRenderTextureCount = 0;
|
||||||
|
contextManager.hasResetThisFrame = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* モデルを描画する前に呼び出すフレーム開始時の処理を行う
|
||||||
|
*
|
||||||
|
* @param gl WebGLRenderingContextまたはWebGL2RenderingContext
|
||||||
|
*/
|
||||||
|
public beginFrameProcess(
|
||||||
|
gl: WebGLRenderingContext | WebGL2RenderingContext
|
||||||
|
): void {
|
||||||
|
const contextManager = this.getContextManager(gl);
|
||||||
|
if (contextManager.hasResetThisFrame) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
contextManager.previousActiveRenderTextureMaxCount = 0;
|
||||||
|
contextManager.hasResetThisFrame = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* モデルの描画が終わった後に呼び出すフレーム終了時の処理
|
||||||
|
*
|
||||||
|
* @param gl WebGLRenderingContextまたはWebGL2RenderingContext
|
||||||
|
*/
|
||||||
|
public endFrameProcess(
|
||||||
|
gl: WebGLRenderingContext | WebGL2RenderingContext
|
||||||
|
): void {
|
||||||
|
const contextManager = this.getContextManager(gl);
|
||||||
|
contextManager.hasResetThisFrame = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* コンテナサイズの取得
|
||||||
|
*
|
||||||
|
* @param gl WebGLRenderingContextまたはWebGL2RenderingContext
|
||||||
|
*/
|
||||||
|
public getContainerSize(
|
||||||
|
gl: WebGLRenderingContext | WebGL2RenderingContext
|
||||||
|
): number {
|
||||||
|
const contextManager = this.getContextManager(gl);
|
||||||
|
if (contextManager.offscreenRenderTargetContainers == null) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return contextManager.offscreenRenderTargetContainers.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 使用可能なリソースコンテナの取得
|
||||||
|
*
|
||||||
|
* @param gl WebGLRenderingContextまたはWebGL2RenderingContext
|
||||||
|
* @param width 幅
|
||||||
|
* @param height 高さ
|
||||||
|
* @param previousFramebuffer 前のフレームバッファ
|
||||||
|
* @return 使用可能なリソースコンテナ
|
||||||
|
*/
|
||||||
|
public getOffscreenRenderTargetContainers(
|
||||||
|
gl: WebGLRenderingContext | WebGL2RenderingContext,
|
||||||
|
width: number,
|
||||||
|
height: number,
|
||||||
|
previousFramebuffer: WebGLFramebuffer
|
||||||
|
): CubismRenderTargetContainer {
|
||||||
|
const contextManager = this.getContextManager(gl);
|
||||||
|
|
||||||
|
// コンテナが初期化されていないか、サイズが変わったら初期化し直す
|
||||||
|
if (
|
||||||
|
contextManager.width != width ||
|
||||||
|
contextManager.height != height ||
|
||||||
|
contextManager.offscreenRenderTargetContainers == null
|
||||||
|
) {
|
||||||
|
this.initialize(gl, width, height);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 使用数を更新
|
||||||
|
this.updateRenderTargetContainerCount(gl);
|
||||||
|
|
||||||
|
// 使われていないリソースコンテナがあればそれを返す
|
||||||
|
const container = this.getUnusedOffscreenRenderTargetContainer(gl);
|
||||||
|
if (container != null) {
|
||||||
|
return container;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 使われていないリソースコンテナがなければ新たに作成する
|
||||||
|
const offscreenRenderTextureContainer =
|
||||||
|
this.createOffscreenRenderTargetContainer(
|
||||||
|
gl,
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
previousFramebuffer
|
||||||
|
);
|
||||||
|
|
||||||
|
return offscreenRenderTextureContainer;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* リソースコンテナの使用状態を取得
|
||||||
|
*
|
||||||
|
* @param gl WebGLRenderingContextまたはWebGL2RenderingContext
|
||||||
|
* @param renderTexture WebGLFramebuffer
|
||||||
|
* @return 使用中はtrue、未使用の場合はfalse
|
||||||
|
*/
|
||||||
|
public getUsingRenderTextureState(
|
||||||
|
gl: WebGLRenderingContext | WebGL2RenderingContext,
|
||||||
|
renderTexture: WebGLFramebuffer
|
||||||
|
): boolean {
|
||||||
|
const contextManager = this.getContextManager(gl);
|
||||||
|
for (
|
||||||
|
let index = 0;
|
||||||
|
index < contextManager.offscreenRenderTargetContainers.length;
|
||||||
|
++index
|
||||||
|
) {
|
||||||
|
if (
|
||||||
|
contextManager.offscreenRenderTargetContainers[index].renderTexture ==
|
||||||
|
renderTexture
|
||||||
|
) {
|
||||||
|
return contextManager.offscreenRenderTargetContainers[index].inUse;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* リソースコンテナの使用を開始する。
|
||||||
|
*
|
||||||
|
* @param gl WebGLRenderingContextまたはWebGL2RenderingContext
|
||||||
|
* @param renderTexture WebGLFramebuffer
|
||||||
|
*/
|
||||||
|
public startUsingRenderTexture(
|
||||||
|
gl: WebGLRenderingContext | WebGL2RenderingContext,
|
||||||
|
renderTexture: WebGLFramebuffer
|
||||||
|
): void {
|
||||||
|
const contextManager = this.getContextManager(gl);
|
||||||
|
for (
|
||||||
|
let index = 0;
|
||||||
|
index < contextManager.offscreenRenderTargetContainers.length;
|
||||||
|
++index
|
||||||
|
) {
|
||||||
|
if (
|
||||||
|
contextManager.offscreenRenderTargetContainers[index].renderTexture !=
|
||||||
|
renderTexture
|
||||||
|
) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
contextManager.offscreenRenderTargetContainers[index].inUse = true;
|
||||||
|
|
||||||
|
this.updateRenderTargetContainerCount(gl);
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* リソースコンテナの使用を終了する。
|
||||||
|
*
|
||||||
|
* @param gl WebGLRenderingContextまたはWebGL2RenderingContext
|
||||||
|
* @param renderTexture WebGLFramebuffer
|
||||||
|
*/
|
||||||
|
public stopUsingRenderTexture(
|
||||||
|
gl: WebGLRenderingContext | WebGL2RenderingContext,
|
||||||
|
renderTexture: WebGLFramebuffer
|
||||||
|
): void {
|
||||||
|
const contextManager = this.getContextManager(gl);
|
||||||
|
for (
|
||||||
|
let index = 0;
|
||||||
|
index < contextManager.offscreenRenderTargetContainers.length;
|
||||||
|
++index
|
||||||
|
) {
|
||||||
|
if (
|
||||||
|
contextManager.offscreenRenderTargetContainers[index].renderTexture !=
|
||||||
|
renderTexture
|
||||||
|
) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
contextManager.offscreenRenderTargetContainers[index].inUse = false;
|
||||||
|
|
||||||
|
contextManager.currentActiveRenderTextureCount--;
|
||||||
|
if (contextManager.currentActiveRenderTextureCount < 0) {
|
||||||
|
contextManager.currentActiveRenderTextureCount = 0;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* リソースコンテナの使用を全て終了する。
|
||||||
|
*
|
||||||
|
* @param gl WebGLRenderingContextまたはWebGL2RenderingContext
|
||||||
|
*/
|
||||||
|
public stopUsingAllRenderTextures(
|
||||||
|
gl: WebGLRenderingContext | WebGL2RenderingContext
|
||||||
|
): void {
|
||||||
|
const contextManager = this.getContextManager(gl);
|
||||||
|
for (
|
||||||
|
let index = 0;
|
||||||
|
index < contextManager.offscreenRenderTargetContainers.length;
|
||||||
|
++index
|
||||||
|
) {
|
||||||
|
contextManager.offscreenRenderTargetContainers[index].inUse = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
contextManager.currentActiveRenderTextureCount = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 使用されていないリソースコンテナを解放する。
|
||||||
|
*
|
||||||
|
* @param gl WebGLRenderingContextまたはWebGL2RenderingContext
|
||||||
|
*/
|
||||||
|
public releaseStaleRenderTextures(
|
||||||
|
gl: WebGLRenderingContext | WebGL2RenderingContext
|
||||||
|
): void {
|
||||||
|
const contextManager = this.getContextManager(gl);
|
||||||
|
const listSize = contextManager.offscreenRenderTargetContainers.length;
|
||||||
|
|
||||||
|
if (contextManager.hasResetThisFrame || listSize === 0) {
|
||||||
|
// 使用する量が変化する場合は開放しない
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 未使用な場所を開放して直前の最大数までリサイズする
|
||||||
|
let findPos = 0;
|
||||||
|
let resize = contextManager.previousActiveRenderTextureMaxCount;
|
||||||
|
for (
|
||||||
|
let i = listSize;
|
||||||
|
contextManager.previousActiveRenderTextureMaxCount < i;
|
||||||
|
--i
|
||||||
|
) {
|
||||||
|
const index = i - 1;
|
||||||
|
if (contextManager.offscreenRenderTargetContainers[index].inUse) {
|
||||||
|
// 空いている場所探して移動させる
|
||||||
|
let isFind = false;
|
||||||
|
for (
|
||||||
|
;
|
||||||
|
findPos < contextManager.previousActiveRenderTextureMaxCount;
|
||||||
|
++findPos
|
||||||
|
) {
|
||||||
|
if (!contextManager.offscreenRenderTargetContainers[findPos].inUse) {
|
||||||
|
const tempContainer =
|
||||||
|
contextManager.offscreenRenderTargetContainers[findPos];
|
||||||
|
contextManager.offscreenRenderTargetContainers[findPos] =
|
||||||
|
contextManager.offscreenRenderTargetContainers[index];
|
||||||
|
contextManager.offscreenRenderTargetContainers[findPos].inUse =
|
||||||
|
true;
|
||||||
|
contextManager.offscreenRenderTargetContainers[index] =
|
||||||
|
tempContainer;
|
||||||
|
contextManager.offscreenRenderTargetContainers[index].inUse = false;
|
||||||
|
isFind = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!isFind) {
|
||||||
|
// 空いている場所が見つからなかったら現状のサイズでリサイズする
|
||||||
|
resize = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const container = contextManager.offscreenRenderTargetContainers[index];
|
||||||
|
contextManager.gl.bindTexture(contextManager.gl.TEXTURE_2D, null);
|
||||||
|
contextManager.gl.deleteTexture(container.colorBuffer);
|
||||||
|
contextManager.gl.bindFramebuffer(contextManager.gl.FRAMEBUFFER, null);
|
||||||
|
contextManager.gl.deleteFramebuffer(container.renderTexture);
|
||||||
|
container.clear();
|
||||||
|
}
|
||||||
|
updateSize(contextManager.offscreenRenderTargetContainers, resize);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 直前のアクティブなレンダーターゲットの最大数を取得
|
||||||
|
*
|
||||||
|
* @param gl WebGLRenderingContextまたはWebGL2RenderingContext
|
||||||
|
* @returns 直前のアクティブなレンダーターゲットの最大数
|
||||||
|
*/
|
||||||
|
public getPreviousActiveRenderTextureCount(
|
||||||
|
gl: WebGLRenderingContext | WebGL2RenderingContext
|
||||||
|
): number {
|
||||||
|
const contextManager = this.getContextManager(gl);
|
||||||
|
return contextManager.previousActiveRenderTextureMaxCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 現在のアクティブなレンダーターゲットの数を取得
|
||||||
|
*
|
||||||
|
* @param gl WebGLRenderingContextまたはWebGL2RenderingContext
|
||||||
|
* @returns 現在のアクティブなレンダーターゲットの数
|
||||||
|
*/
|
||||||
|
public getCurrentActiveRenderTextureCount(
|
||||||
|
gl: WebGLRenderingContext | WebGL2RenderingContext
|
||||||
|
): number {
|
||||||
|
const contextManager = this.getContextManager(gl);
|
||||||
|
return contextManager.currentActiveRenderTextureCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 現在のアクティブなレンダーターゲットの数を更新
|
||||||
|
*
|
||||||
|
* @param gl WebGLRenderingContextまたはWebGL2RenderingContext
|
||||||
|
*/
|
||||||
|
public updateRenderTargetContainerCount(
|
||||||
|
gl: WebGLRenderingContext | WebGL2RenderingContext
|
||||||
|
): void {
|
||||||
|
const contextManager = this.getContextManager(gl);
|
||||||
|
++contextManager.currentActiveRenderTextureCount;
|
||||||
|
|
||||||
|
// 最大数更新
|
||||||
|
contextManager.previousActiveRenderTextureMaxCount =
|
||||||
|
contextManager.currentActiveRenderTextureCount >
|
||||||
|
contextManager.previousActiveRenderTextureMaxCount
|
||||||
|
? contextManager.currentActiveRenderTextureCount
|
||||||
|
: contextManager.previousActiveRenderTextureMaxCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 使用されていないリソースコンテナの取得
|
||||||
|
*
|
||||||
|
* @param gl WebGLRenderingContextまたはWebGL2RenderingContext
|
||||||
|
* @return 使用されていないリソースコンテナ
|
||||||
|
*/
|
||||||
|
public getUnusedOffscreenRenderTargetContainer(
|
||||||
|
gl: WebGLRenderingContext | WebGL2RenderingContext
|
||||||
|
): CubismRenderTargetContainer {
|
||||||
|
const contextManager = this.getContextManager(gl);
|
||||||
|
// 使われていないリソースコンテナがあればそれを返す
|
||||||
|
for (
|
||||||
|
let index = 0;
|
||||||
|
index < contextManager.offscreenRenderTargetContainers.length;
|
||||||
|
++index
|
||||||
|
) {
|
||||||
|
const container = contextManager.offscreenRenderTargetContainers[index];
|
||||||
|
if (container.inUse == false) {
|
||||||
|
container.inUse = true;
|
||||||
|
return container;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 新たにリソースコンテナを作成する。
|
||||||
|
*
|
||||||
|
* @param gl WebGLRenderingContextまたはWebGL2RenderingContext
|
||||||
|
* @param width 幅
|
||||||
|
* @param height 高さ
|
||||||
|
* @param previousFramebuffer 前のフレームバッファ
|
||||||
|
* @return 作成されたリソースコンテナ
|
||||||
|
*/
|
||||||
|
public createOffscreenRenderTargetContainer(
|
||||||
|
gl: WebGLRenderingContext | WebGL2RenderingContext,
|
||||||
|
width: number,
|
||||||
|
height: number,
|
||||||
|
previousFramebuffer: WebGLFramebuffer
|
||||||
|
): CubismRenderTargetContainer {
|
||||||
|
const renderTarget = new CubismRenderTarget_WebGL();
|
||||||
|
|
||||||
|
if (
|
||||||
|
!renderTarget.createRenderTarget(gl, width, height, previousFramebuffer)
|
||||||
|
) {
|
||||||
|
CubismLogError('Failed to create offscreen render texture.');
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const offscreenRenderTextureContainer = new CubismRenderTargetContainer(
|
||||||
|
renderTarget.getColorBuffer(),
|
||||||
|
renderTarget.getRenderTexture(),
|
||||||
|
true
|
||||||
|
);
|
||||||
|
|
||||||
|
const contextManager = this.getContextManager(gl);
|
||||||
|
contextManager.offscreenRenderTargetContainers.push(
|
||||||
|
offscreenRenderTextureContainer
|
||||||
|
);
|
||||||
|
|
||||||
|
return offscreenRenderTextureContainer;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static _instance: CubismWebGLOffscreenManager; // オフスクリーン描画用レンダーターゲットマネージャ
|
||||||
|
private _contextManagers: Map<
|
||||||
|
WebGLRenderingContext | WebGL2RenderingContext,
|
||||||
|
CubismWebGLContextManager
|
||||||
|
>; // WebGLContextごとのマネージャー
|
||||||
|
}
|
||||||
@ -0,0 +1,240 @@
|
|||||||
|
/**
|
||||||
|
* Copyright(c) Live2D Inc. All rights reserved.
|
||||||
|
*
|
||||||
|
* Use of this source code is governed by the Live2D Open Software license
|
||||||
|
* that can be found at https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { CubismRenderTarget_WebGL } from './cubismrendertarget_webgl';
|
||||||
|
import { CubismWebGLOffscreenManager } from './cubismoffscreenmanager';
|
||||||
|
import { CubismLogError } from '../utils/cubismdebug';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* WebGL用オフスクリーンサーフェス
|
||||||
|
* マスクの描画及びオフスクリーン機能に必要なフレームバッファなどを管理する。
|
||||||
|
*/
|
||||||
|
export class CubismOffscreenRenderTarget_WebGL extends CubismRenderTarget_WebGL {
|
||||||
|
/**
|
||||||
|
* リソースコンテナマネージャを初期化する。
|
||||||
|
*
|
||||||
|
* @param displayBufferWidth レンダーターゲットの幅
|
||||||
|
* @param displayBufferHeight レンダーターゲットの高さ
|
||||||
|
*/
|
||||||
|
private initializeOffscreenManager(
|
||||||
|
gl: WebGLRenderingContext | WebGL2RenderingContext,
|
||||||
|
displayBufferWidth: number,
|
||||||
|
displayBufferHeight: number
|
||||||
|
): void {
|
||||||
|
this._gl = gl;
|
||||||
|
this._webGLOffscreenManager = CubismWebGLOffscreenManager.getInstance();
|
||||||
|
if (this._webGLOffscreenManager.getContainerSize(gl) === 0) {
|
||||||
|
this._webGLOffscreenManager.initialize(
|
||||||
|
gl,
|
||||||
|
displayBufferWidth,
|
||||||
|
displayBufferHeight
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* オフスクリーン描画用レンダーターゲットをセットする。
|
||||||
|
*
|
||||||
|
* @param gl WebGLRenderingContextまたはWebGL2RenderingContext
|
||||||
|
* NOTE: Cubism 5.3以降のモデルが使用される場合はWebGL2RenderingContextを使用すること。
|
||||||
|
* @param displayBufferWidth レンダーターゲットの幅
|
||||||
|
* @param displayBufferHeight レンダーターゲットの高さ
|
||||||
|
* @param previousFramebuffer 前のフレームバッファ
|
||||||
|
*/
|
||||||
|
public setOffscreenRenderTarget(
|
||||||
|
gl: WebGLRenderingContext | WebGL2RenderingContext,
|
||||||
|
displayBufferWidth: number,
|
||||||
|
displayBufferHeight: number,
|
||||||
|
previousFramebuffer: WebGLFramebuffer
|
||||||
|
): void {
|
||||||
|
// マネージャがなければ初期化
|
||||||
|
if (this._webGLOffscreenManager == null) {
|
||||||
|
this.initializeOffscreenManager(
|
||||||
|
gl,
|
||||||
|
displayBufferWidth,
|
||||||
|
displayBufferHeight
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 使用可能なリソースコンテナを取得する
|
||||||
|
const offscreenRenderTargetContainer =
|
||||||
|
this._webGLOffscreenManager.getOffscreenRenderTargetContainers(
|
||||||
|
gl,
|
||||||
|
displayBufferWidth,
|
||||||
|
displayBufferHeight,
|
||||||
|
previousFramebuffer
|
||||||
|
);
|
||||||
|
|
||||||
|
if (offscreenRenderTargetContainer == null) {
|
||||||
|
CubismLogError('Failed to acquire offscreen render texture container.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._colorBuffer = offscreenRenderTargetContainer.getColorBuffer();
|
||||||
|
this._renderTexture = offscreenRenderTargetContainer.getRenderTexture();
|
||||||
|
|
||||||
|
this._bufferWidth = displayBufferWidth;
|
||||||
|
this._bufferHeight = displayBufferHeight;
|
||||||
|
|
||||||
|
this._gl = gl;
|
||||||
|
|
||||||
|
if (this._renderTexture == null) {
|
||||||
|
this._renderTexture = previousFramebuffer;
|
||||||
|
CubismLogError('Failed to create offscreen render texture.');
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* リソースコンテナの使用状態を取得
|
||||||
|
*
|
||||||
|
* @return 使用中はtrue、未使用の場合はfalse
|
||||||
|
*/
|
||||||
|
public getUsingRenderTextureState(): boolean {
|
||||||
|
if (this._webGLOffscreenManager == null || this._gl == null) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return this._webGLOffscreenManager.getUsingRenderTextureState(
|
||||||
|
this._gl,
|
||||||
|
this._renderTexture
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* リソースコンテナの使用を開始する。
|
||||||
|
*/
|
||||||
|
public startUsingRenderTexture(): void {
|
||||||
|
if (this._webGLOffscreenManager == null || this._gl == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._webGLOffscreenManager.startUsingRenderTexture(
|
||||||
|
this._gl,
|
||||||
|
this._renderTexture
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* リソースコンテナの使用を終了する。
|
||||||
|
*/
|
||||||
|
public stopUsingRenderTexture(): void {
|
||||||
|
if (this._webGLOffscreenManager == null || this._gl == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._webGLOffscreenManager.stopUsingRenderTexture(
|
||||||
|
this._gl,
|
||||||
|
this._renderTexture
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* オフスクリーンのインデックスを設定する。
|
||||||
|
*
|
||||||
|
* @param offscreenIndex オフスクリーンのインデックス
|
||||||
|
*/
|
||||||
|
public setOffscreenIndex(offscreenIndex: number): void {
|
||||||
|
this._offscreenIndex = offscreenIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* オフスクリーンのインデックスを取得する。
|
||||||
|
*
|
||||||
|
* @return オフスクリーンのインデックス
|
||||||
|
*/
|
||||||
|
public getOffscreenIndex(): number {
|
||||||
|
return this._offscreenIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 以前のオフスクリーン描画用レンダーターゲットを設定する。
|
||||||
|
*
|
||||||
|
* @param oldOffscreen 以前のオフスクリーン描画用レンダーターゲット
|
||||||
|
*/
|
||||||
|
public setOldOffscreen(
|
||||||
|
oldOffscreen: CubismOffscreenRenderTarget_WebGL
|
||||||
|
): void {
|
||||||
|
this._oldOffscreen = oldOffscreen;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 以前のオフスクリーン描画用レンダーターゲットを取得する。
|
||||||
|
*
|
||||||
|
* @return 以前のオフスクリーン描画用レンダーターゲット
|
||||||
|
*/
|
||||||
|
public getOldOffscreen(): CubismOffscreenRenderTarget_WebGL {
|
||||||
|
return this._oldOffscreen;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 親のオフスクリーン描画用レンダーターゲットを設定する。
|
||||||
|
*
|
||||||
|
* @param parentOffscreenRenderTarget 親のオフスクリーン描画用レンダーターゲット
|
||||||
|
*/
|
||||||
|
public setParentPartOffscreen(
|
||||||
|
parentOffscreenRenderTarget: CubismOffscreenRenderTarget_WebGL
|
||||||
|
): void {
|
||||||
|
this._parentOffscreenRenderTarget = parentOffscreenRenderTarget;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 親のオフスクリーン描画用レンダーターゲットを取得する。
|
||||||
|
*
|
||||||
|
* @return 親のオフスクリーン描画用レンダーターゲット
|
||||||
|
*/
|
||||||
|
public getParentPartOffscreen(): CubismOffscreenRenderTarget_WebGL {
|
||||||
|
return this._parentOffscreenRenderTarget;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* コンストラクタ
|
||||||
|
*/
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
this._offscreenIndex = -1;
|
||||||
|
this._parentOffscreenRenderTarget = null;
|
||||||
|
this._oldOffscreen = null;
|
||||||
|
this._webGLOffscreenManager = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public release(): void {
|
||||||
|
if (
|
||||||
|
this._webGLOffscreenManager != null &&
|
||||||
|
this._gl != null &&
|
||||||
|
this._renderTexture != null
|
||||||
|
) {
|
||||||
|
this._webGLOffscreenManager.stopUsingRenderTexture(
|
||||||
|
this._gl,
|
||||||
|
this._renderTexture
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this._colorBuffer && this._gl) {
|
||||||
|
this._gl.deleteTexture(this._colorBuffer);
|
||||||
|
this._colorBuffer = null;
|
||||||
|
}
|
||||||
|
if (this._renderTexture && this._gl) {
|
||||||
|
this._gl.deleteFramebuffer(this._renderTexture);
|
||||||
|
this._renderTexture = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this._webGLOffscreenManager != null) {
|
||||||
|
this._webGLOffscreenManager = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._oldOffscreen = null;
|
||||||
|
this._parentOffscreenRenderTarget = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _offscreenIndex: number; // オフスクリーンのインデックス
|
||||||
|
private _parentOffscreenRenderTarget: CubismOffscreenRenderTarget_WebGL; // 親のオフスクリーン描画用レンダーターゲット
|
||||||
|
private _oldOffscreen: CubismOffscreenRenderTarget_WebGL; // 以前のオフスクリーン描画用レンダーターゲット
|
||||||
|
private _webGLOffscreenManager: CubismWebGLOffscreenManager; // オフスクリーン描画用レンダーターゲットマネージャ
|
||||||
|
protected _gl: WebGLRenderingContext | WebGL2RenderingContext; // WebGLコンテキスト
|
||||||
|
}
|
||||||
425
avatar-h5-renderer/framework/src/rendering/cubismrenderer.ts
Normal file
425
avatar-h5-renderer/framework/src/rendering/cubismrenderer.ts
Normal file
@ -0,0 +1,425 @@
|
|||||||
|
/**
|
||||||
|
* Copyright(c) Live2D Inc. All rights reserved.
|
||||||
|
*
|
||||||
|
* Use of this source code is governed by the Live2D Open Software license
|
||||||
|
* that can be found at https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { CubismMath } from '../math/cubismmath';
|
||||||
|
import { CubismMatrix44 } from '../math/cubismmatrix44';
|
||||||
|
import { CubismModel } from '../model/cubismmodel';
|
||||||
|
import { csmRect } from '../type/csmrectf';
|
||||||
|
import { ICubismClippingManager } from './cubismclippingmanager';
|
||||||
|
import { CubismLogInfo } from '../utils/cubismdebug';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* モデル描画を処理するレンダラ
|
||||||
|
*
|
||||||
|
* サブクラスに環境依存の描画命令を記述する。
|
||||||
|
*/
|
||||||
|
export abstract class CubismRenderer {
|
||||||
|
/**
|
||||||
|
* レンダラのインスタンスを生成して取得する
|
||||||
|
*
|
||||||
|
* @return レンダラのインスタンス
|
||||||
|
*/
|
||||||
|
public static create(): CubismRenderer {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* レンダラのインスタンスを解放する
|
||||||
|
*/
|
||||||
|
public static delete(renderer: CubismRenderer): void {
|
||||||
|
renderer = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* レンダラの初期化処理を実行する
|
||||||
|
* 引数に渡したモデルからレンダラの初期化処理に必要な情報を取り出すことができる
|
||||||
|
*
|
||||||
|
* @param model モデルのインスタンス
|
||||||
|
*/
|
||||||
|
public initialize(model: CubismModel): void {
|
||||||
|
this._model = model;
|
||||||
|
|
||||||
|
// ブレンドモード使用時は必ず高精細にする
|
||||||
|
if (model.isBlendModeEnabled()) {
|
||||||
|
this.useHighPrecisionMask(true);
|
||||||
|
CubismLogInfo(
|
||||||
|
'This model uses a high-resolution mask because it operates in blend mode.'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* モデルを描画する
|
||||||
|
* @param shaderPath ブレンドモード用シェーダのパス
|
||||||
|
*/
|
||||||
|
public drawModel(shaderPath: string = null): void {
|
||||||
|
if (this.getModel() == null) return;
|
||||||
|
|
||||||
|
// NOTE: WebGL最適化のため、デフォルトではコメントアウト
|
||||||
|
//this.saveProfile();
|
||||||
|
|
||||||
|
this.doDrawModel(shaderPath);
|
||||||
|
|
||||||
|
// NOTE: WebGL最適化のため、デフォルトではコメントアウト
|
||||||
|
//this.restoreProfile();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Model-View-Projection 行列をセットする
|
||||||
|
* 配列は複製されるので、元の配列は外で破棄して良い
|
||||||
|
*
|
||||||
|
* @param matrix44 Model-View-Projection 行列
|
||||||
|
*/
|
||||||
|
public setMvpMatrix(matrix44: CubismMatrix44): void {
|
||||||
|
this._mvpMatrix4x4.setMatrix(matrix44.getArray());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Model-View-Projection 行列を取得する
|
||||||
|
*
|
||||||
|
* @return Model-View-Projection 行列
|
||||||
|
*/
|
||||||
|
public getMvpMatrix(): CubismMatrix44 {
|
||||||
|
return this._mvpMatrix4x4;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* モデルの色をセットする
|
||||||
|
* 各色0.0~1.0の間で指定する(1.0が標準の状態)
|
||||||
|
*
|
||||||
|
* @param red 赤チャンネルの値
|
||||||
|
* @param green 緑チャンネルの値
|
||||||
|
* @param blue 青チャンネルの値
|
||||||
|
* @param alpha αチャンネルの値
|
||||||
|
*/
|
||||||
|
public setModelColor(
|
||||||
|
red: number,
|
||||||
|
green: number,
|
||||||
|
blue: number,
|
||||||
|
alpha: number
|
||||||
|
): void {
|
||||||
|
this._modelColor.r = CubismMath.clamp(red, 0.0, 1.0);
|
||||||
|
this._modelColor.g = CubismMath.clamp(green, 0.0, 1.0);
|
||||||
|
this._modelColor.b = CubismMath.clamp(blue, 0.0, 1.0);
|
||||||
|
this._modelColor.a = CubismMath.clamp(alpha, 0.0, 1.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* モデルの色を取得する
|
||||||
|
* 各色0.0~1.0の間で指定する(1.0が標準の状態)
|
||||||
|
*
|
||||||
|
* @return RGBAのカラー情報
|
||||||
|
*/
|
||||||
|
public getModelColor(): CubismTextureColor {
|
||||||
|
return JSON.parse(JSON.stringify(this._modelColor));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 透明度を考慮したモデルの色を計算する。
|
||||||
|
*
|
||||||
|
* @param opacity 透明度
|
||||||
|
*
|
||||||
|
* @return RGBAのカラー情報
|
||||||
|
*/
|
||||||
|
getModelColorWithOpacity(opacity: number): CubismTextureColor {
|
||||||
|
const modelColorRGBA: CubismTextureColor = this.getModelColor();
|
||||||
|
modelColorRGBA.a *= opacity;
|
||||||
|
if (this.isPremultipliedAlpha()) {
|
||||||
|
modelColorRGBA.r *= modelColorRGBA.a;
|
||||||
|
modelColorRGBA.g *= modelColorRGBA.a;
|
||||||
|
modelColorRGBA.b *= modelColorRGBA.a;
|
||||||
|
}
|
||||||
|
return modelColorRGBA;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 乗算済みαの有効・無効をセットする
|
||||||
|
* 有効にするならtrue、無効にするならfalseをセットする
|
||||||
|
*/
|
||||||
|
public setIsPremultipliedAlpha(enable: boolean): void {
|
||||||
|
this._isPremultipliedAlpha = enable;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 乗算済みαの有効・無効を取得する
|
||||||
|
* @return true 乗算済みのα有効
|
||||||
|
* false 乗算済みのα無効
|
||||||
|
*/
|
||||||
|
public isPremultipliedAlpha(): boolean {
|
||||||
|
return this._isPremultipliedAlpha;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* カリング(片面描画)の有効・無効をセットする。
|
||||||
|
* 有効にするならtrue、無効にするならfalseをセットする
|
||||||
|
*/
|
||||||
|
public setIsCulling(culling: boolean): void {
|
||||||
|
this._isCulling = culling;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* カリング(片面描画)の有効・無効を取得する。
|
||||||
|
*
|
||||||
|
* @return true カリング有効
|
||||||
|
* false カリング無効
|
||||||
|
*/
|
||||||
|
public isCulling(): boolean {
|
||||||
|
return this._isCulling;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* テクスチャの異方性フィルタリングのパラメータをセットする
|
||||||
|
* パラメータ値の影響度はレンダラの実装に依存する
|
||||||
|
*
|
||||||
|
* @param n パラメータの値
|
||||||
|
*/
|
||||||
|
public setAnisotropy(n: number): void {
|
||||||
|
this._anisotropy = n;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* テクスチャの異方性フィルタリングのパラメータをセットする
|
||||||
|
*
|
||||||
|
* @return 異方性フィルタリングのパラメータ
|
||||||
|
*/
|
||||||
|
public getAnisotropy(): number {
|
||||||
|
return this._anisotropy;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* レンダリングするモデルを取得する
|
||||||
|
*
|
||||||
|
* @return レンダリングするモデル
|
||||||
|
*/
|
||||||
|
public getModel(): CubismModel {
|
||||||
|
return this._model;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* マスク描画の方式を変更する。
|
||||||
|
* falseの場合、マスクを1枚のテクスチャに分割してレンダリングする(デフォルト)
|
||||||
|
* 高速だが、マスク個数の上限が36に限定され、質も荒くなる
|
||||||
|
* trueの場合、パーツ描画の前にその都度必要なマスクを描き直す
|
||||||
|
* レンダリング品質は高いが描画処理負荷は増す
|
||||||
|
*
|
||||||
|
* @param high 高精細マスクに切り替えるか?
|
||||||
|
*/
|
||||||
|
public useHighPrecisionMask(high: boolean): void {
|
||||||
|
this._useHighPrecisionMask = high;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* マスクの描画方式を取得する
|
||||||
|
*
|
||||||
|
* @return true 高精細方式
|
||||||
|
* false デフォルト
|
||||||
|
*/
|
||||||
|
public isUsingHighPrecisionMask(): boolean {
|
||||||
|
return this._useHighPrecisionMask;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* モデルを描画したバッファのサイズを設定
|
||||||
|
*
|
||||||
|
* @param[in] width -> モデルを描画したバッファの幅
|
||||||
|
* @param[in] height -> モデルを描画したバッファの高さ
|
||||||
|
*/
|
||||||
|
public setRenderTargetSize(width: number, height: number): void {
|
||||||
|
this._modelRenderTargetWidth = width;
|
||||||
|
this._modelRenderTargetHeight = height;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* コンストラクタ
|
||||||
|
*/
|
||||||
|
protected constructor(width: number, height: number) {
|
||||||
|
this._modelRenderTargetWidth = width;
|
||||||
|
this._modelRenderTargetHeight = height;
|
||||||
|
this._isCulling = false;
|
||||||
|
this._isPremultipliedAlpha = false;
|
||||||
|
this._anisotropy = 0.0;
|
||||||
|
this._model = null;
|
||||||
|
this._modelColor = new CubismTextureColor();
|
||||||
|
this._useHighPrecisionMask = false;
|
||||||
|
|
||||||
|
// 単位行列に初期化
|
||||||
|
this._mvpMatrix4x4 = new CubismMatrix44();
|
||||||
|
this._mvpMatrix4x4.loadIdentity();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* モデル描画直前のオフスクリーン設定を行う
|
||||||
|
*/
|
||||||
|
public abstract beforeDrawModelRenderTarget(): void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* モデル描画直後のオフスクリーン設定を行う
|
||||||
|
*/
|
||||||
|
public abstract afterDrawModelRenderTarget(): void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* モデル描画の実装
|
||||||
|
* @param shaderPath ブレンドモード用シェーダのパス
|
||||||
|
*/
|
||||||
|
public abstract doDrawModel(shaderPath: string): void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* モデル描画直前のレンダラのステートを保持する
|
||||||
|
*/
|
||||||
|
protected abstract saveProfile(): void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* モデル描画直前のレンダラのステートを復帰する
|
||||||
|
*/
|
||||||
|
protected abstract restoreProfile(): void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* レンダラが保持する静的なリソースを開放する
|
||||||
|
*/
|
||||||
|
public static staticRelease: any;
|
||||||
|
|
||||||
|
protected _mvpMatrix4x4: CubismMatrix44; // Model-View-Projection 行列
|
||||||
|
protected _modelColor: CubismTextureColor; // モデル自体のカラー(RGBA)
|
||||||
|
protected _isCulling: boolean; // カリングが有効ならtrue
|
||||||
|
protected _isPremultipliedAlpha: boolean; // 乗算済みαならtrue
|
||||||
|
protected _anisotropy: any; // テクスチャの異方性フィルタリングのパラメータ
|
||||||
|
protected _model: CubismModel; // レンダリング対象のモデル
|
||||||
|
protected _useHighPrecisionMask: boolean; // falseの場合、マスクを纏めて描画する trueの場合、マスクはパーツ描画ごとに書き直す
|
||||||
|
|
||||||
|
protected _modelRenderTargetWidth: number;
|
||||||
|
protected _modelRenderTargetHeight: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum CubismBlendMode {
|
||||||
|
CubismBlendMode_Normal = 0, // 通常
|
||||||
|
CubismBlendMode_Additive = 1, // 加算
|
||||||
|
CubismBlendMode_Multiplicative = 2 // 乗算
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* オブジェクトのタイプ
|
||||||
|
*/
|
||||||
|
export enum DrawableObjectType {
|
||||||
|
DrawableObjectType_Drawable = 0,
|
||||||
|
DrawableObjectType_Offscreen = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* テクスチャの色をRGBAで扱うためのクラス
|
||||||
|
*/
|
||||||
|
export class CubismTextureColor {
|
||||||
|
/**
|
||||||
|
* コンストラクタ
|
||||||
|
*/
|
||||||
|
constructor(r = 1.0, g = 1.0, b = 1.0, a = 1.0) {
|
||||||
|
this.r = r;
|
||||||
|
this.g = g;
|
||||||
|
this.b = b;
|
||||||
|
this.a = a;
|
||||||
|
}
|
||||||
|
|
||||||
|
r: number; // 赤チャンネル
|
||||||
|
g: number; // 緑チャンネル
|
||||||
|
b: number; // 青チャンネル
|
||||||
|
a: number; // αチャンネル
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* クリッピングマスクのコンテキスト
|
||||||
|
*/
|
||||||
|
export abstract class CubismClippingContext {
|
||||||
|
/**
|
||||||
|
* 引数付きコンストラクタ
|
||||||
|
*/
|
||||||
|
public constructor(clippingDrawableIndices: Int32Array, clipCount: number) {
|
||||||
|
// クリップしている(=マスク用の)Drawableのインデックスリスト
|
||||||
|
this._clippingIdList = clippingDrawableIndices;
|
||||||
|
|
||||||
|
// マスクの数
|
||||||
|
this._clippingIdCount = clipCount;
|
||||||
|
|
||||||
|
this._allClippedDrawRect = new csmRect();
|
||||||
|
this._layoutBounds = new csmRect();
|
||||||
|
|
||||||
|
this._clippedDrawableIndexList = [];
|
||||||
|
this._clippedOffscreenIndexList = [];
|
||||||
|
|
||||||
|
this._matrixForMask = new CubismMatrix44();
|
||||||
|
this._matrixForDraw = new CubismMatrix44();
|
||||||
|
|
||||||
|
this._bufferIndex = 0;
|
||||||
|
this._layoutChannelIndex = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* このマスクを管理するマネージャのインスタンスを取得する
|
||||||
|
* @return クリッピングマネージャのインスタンス
|
||||||
|
*/
|
||||||
|
public abstract getClippingManager(): ICubismClippingManager;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* デストラクタ相当の処理
|
||||||
|
*/
|
||||||
|
public release(): void {
|
||||||
|
if (this._layoutBounds != null) {
|
||||||
|
this._layoutBounds = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this._allClippedDrawRect != null) {
|
||||||
|
this._allClippedDrawRect = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this._clippedDrawableIndexList != null) {
|
||||||
|
this._clippedDrawableIndexList = null;
|
||||||
|
}
|
||||||
|
if (this._clippedOffscreenIndexList != null) {
|
||||||
|
this._clippedOffscreenIndexList = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* このマスクにクリップされる描画オブジェクトを追加する
|
||||||
|
*
|
||||||
|
* @param drawableIndex クリッピング対象に追加する描画オブジェクトのインデックス
|
||||||
|
*/
|
||||||
|
public addClippedDrawable(drawableIndex: number) {
|
||||||
|
this._clippedDrawableIndexList.push(drawableIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* このマスクにクリップされるオフスクリーンオブジェクトを追加する
|
||||||
|
*
|
||||||
|
* @param offscreenIndex クリッピング対象に追加するオフスクリーンオブジェクトのインデックス
|
||||||
|
*/
|
||||||
|
public addClippedOffscreen(offscreenIndex: number) {
|
||||||
|
this._clippedOffscreenIndexList.push(offscreenIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
public _isUsing: boolean; // 現在の描画状態でマスクの準備が必要ならtrue
|
||||||
|
public readonly _clippingIdList: Int32Array; // クリッピングマスクのIDリスト
|
||||||
|
public _clippingIdCount: number; // クリッピングマスクの数
|
||||||
|
public _layoutChannelIndex: number; // RGBAのいずれのチャンネルにこのクリップを配置するか(0:R, 1:G, 2:B, 3:A)
|
||||||
|
public _layoutBounds: csmRect; // マスク用チャンネルのどの領域にマスクを入れるか(View座標-1~1, UVは0~1に直す)
|
||||||
|
public _allClippedDrawRect: csmRect; // このクリッピングで、クリッピングされるすべての描画オブジェクトの囲み矩形(毎回更新)
|
||||||
|
public _matrixForMask: CubismMatrix44; // マスクの位置計算結果を保持する行列
|
||||||
|
public _matrixForDraw: CubismMatrix44; // 描画オブジェクトの位置計算結果を保持する行列
|
||||||
|
public _clippedDrawableIndexList: number[]; // このマスクにクリップされる描画オブジェクトのリスト
|
||||||
|
public _clippedOffscreenIndexList: number[]; // このマスクにクリップされるオフスクリーンオブジェクトのリスト
|
||||||
|
public _bufferIndex: number; // このマスクが割り当てられるレンダーテクスチャ(フレームバッファ)やカラーバッファのインデックス
|
||||||
|
}
|
||||||
|
|
||||||
|
// Namespace definition for compatibility.
|
||||||
|
import * as $ from './cubismrenderer';
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-namespace
|
||||||
|
export namespace Live2DCubismFramework {
|
||||||
|
export const CubismBlendMode = $.CubismBlendMode;
|
||||||
|
export type CubismBlendMode = $.CubismBlendMode;
|
||||||
|
export const CubismRenderer = $.CubismRenderer;
|
||||||
|
export type CubismRenderer = $.CubismRenderer;
|
||||||
|
export const CubismTextureColor = $.CubismTextureColor;
|
||||||
|
export type CubismTextureColor = $.CubismTextureColor;
|
||||||
|
}
|
||||||
1780
avatar-h5-renderer/framework/src/rendering/cubismrenderer_webgl.ts
Normal file
1780
avatar-h5-renderer/framework/src/rendering/cubismrenderer_webgl.ts
Normal file
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,289 @@
|
|||||||
|
/**
|
||||||
|
* Copyright(c) Live2D Inc. All rights reserved.
|
||||||
|
*
|
||||||
|
* Use of this source code is governed by the Live2D Open Software license
|
||||||
|
* that can be found at https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { CubismLogError } from '../utils/cubismdebug';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* WebGL用オフスクリーンサーフェス
|
||||||
|
* マスクの描画に必要なフレームバッファなどを管理する。
|
||||||
|
*/
|
||||||
|
export class CubismRenderTarget_WebGL {
|
||||||
|
/**
|
||||||
|
* WebGL2RenderingContext.blitFramebuffer() でバッファのコピーを行う。
|
||||||
|
*
|
||||||
|
* @param src コピー元のオフスクリーンサーフェス
|
||||||
|
* @param dst コピー先のオフスクリーンサーフェス
|
||||||
|
*/
|
||||||
|
public static copyBuffer(
|
||||||
|
gl: WebGL2RenderingContext,
|
||||||
|
src: CubismRenderTarget_WebGL,
|
||||||
|
dst: CubismRenderTarget_WebGL
|
||||||
|
): void {
|
||||||
|
if (src == null || dst == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(gl instanceof WebGL2RenderingContext)) {
|
||||||
|
throw new Error('WebGL2RenderingContext is required for buffer copy.');
|
||||||
|
}
|
||||||
|
|
||||||
|
const previousFramebuffer = gl.getParameter(
|
||||||
|
gl.FRAMEBUFFER_BINDING
|
||||||
|
) as WebGLFramebuffer;
|
||||||
|
|
||||||
|
// 各オフスクリーンサーフェスのレンダーテクスチャをバインド
|
||||||
|
gl.bindFramebuffer(gl.READ_FRAMEBUFFER, src.getRenderTexture());
|
||||||
|
gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, dst.getRenderTexture());
|
||||||
|
|
||||||
|
// バッファのコピーを実行
|
||||||
|
gl.blitFramebuffer(
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
src.getBufferWidth(),
|
||||||
|
src.getBufferHeight(),
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
dst.getBufferWidth(),
|
||||||
|
dst.getBufferHeight(),
|
||||||
|
gl.COLOR_BUFFER_BIT,
|
||||||
|
gl.NEAREST
|
||||||
|
);
|
||||||
|
|
||||||
|
// コピー後、元のフレームバッファを復元
|
||||||
|
gl.bindFramebuffer(gl.FRAMEBUFFER, previousFramebuffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 描画を開始する。
|
||||||
|
*
|
||||||
|
* @param restoreFbo EndDraw時に復元するFBOを指定する。nullを指定すると、beginDraw時に現在のFBOを記憶しておく。
|
||||||
|
*/
|
||||||
|
public beginDraw(restoreFbo: WebGLFramebuffer = null): void {
|
||||||
|
if (this._renderTexture == null) {
|
||||||
|
console.error('_renderTexture is null');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// バックバッファのサーフェイスを記憶しておく。
|
||||||
|
if (restoreFbo == null) {
|
||||||
|
this._oldFbo = this._gl.getParameter(this._gl.FRAMEBUFFER_BINDING);
|
||||||
|
} else {
|
||||||
|
this._oldFbo = restoreFbo;
|
||||||
|
}
|
||||||
|
|
||||||
|
// RenderTextureをactiveにセット
|
||||||
|
this._gl.bindFramebuffer(this._gl.FRAMEBUFFER, this._renderTexture);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 描画を終了し、バックバッファのサーフェイスを復元する。
|
||||||
|
*/
|
||||||
|
public endDraw(): void {
|
||||||
|
// バックバッファのサーフェイスを復元
|
||||||
|
this._gl.bindFramebuffer(this._gl.FRAMEBUFFER, this._oldFbo);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* バインドされているカラーバッファのクリアを行う。
|
||||||
|
*
|
||||||
|
* @param r 赤の成分 (0.0 - 1.0)
|
||||||
|
* @param g 緑の成分 (0.0 - 1.0)
|
||||||
|
* @param b 青の成分 (0.0 - 1.0)
|
||||||
|
* @param a アルファの成分 (0.0 - 1.0)
|
||||||
|
*/
|
||||||
|
public clear(r: number, g: number, b: number, a: number): void {
|
||||||
|
// クリア処理
|
||||||
|
this._gl.clearColor(r, g, b, a);
|
||||||
|
this._gl.clear(this._gl.COLOR_BUFFER_BIT);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* オフスクリーンサーフェスを作成する。
|
||||||
|
*
|
||||||
|
* @param gl WebGLRenderingContextまたはWebGL2RenderingContext
|
||||||
|
* NOTE: Cubism 5.3以降のモデルが使用される場合はWebGL2RenderingContextを使用すること。
|
||||||
|
* @param displayBufferWidth オフスクリーンサーフェスの幅
|
||||||
|
* @param displayBufferHeight オフスクリーンサーフェスの高さ
|
||||||
|
* @param previousFramebuffer 前のフレームバッファ
|
||||||
|
*
|
||||||
|
* @return 成功した場合はtrue、失敗した場合はfalse
|
||||||
|
*/
|
||||||
|
public createRenderTarget(
|
||||||
|
gl: WebGLRenderingContext | WebGL2RenderingContext,
|
||||||
|
displayBufferWidth: number,
|
||||||
|
displayBufferHeight: number,
|
||||||
|
previousFramebuffer: WebGLFramebuffer
|
||||||
|
): boolean {
|
||||||
|
this.destroyRenderTarget();
|
||||||
|
|
||||||
|
this._colorBuffer = gl.createTexture();
|
||||||
|
gl.bindTexture(gl.TEXTURE_2D, this._colorBuffer);
|
||||||
|
gl.texImage2D(
|
||||||
|
gl.TEXTURE_2D,
|
||||||
|
0,
|
||||||
|
gl.RGBA,
|
||||||
|
displayBufferWidth,
|
||||||
|
displayBufferHeight,
|
||||||
|
0,
|
||||||
|
gl.RGBA,
|
||||||
|
gl.UNSIGNED_BYTE,
|
||||||
|
null
|
||||||
|
);
|
||||||
|
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
|
||||||
|
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
|
||||||
|
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
|
||||||
|
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
|
||||||
|
|
||||||
|
gl.bindTexture(gl.TEXTURE_2D, null);
|
||||||
|
|
||||||
|
// フレームバッファを作成
|
||||||
|
const ret = gl.createFramebuffer();
|
||||||
|
if (ret == null) {
|
||||||
|
CubismLogError('Failed to create framebuffer');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 作成したフレームバッファをバインド
|
||||||
|
gl.bindFramebuffer(gl.FRAMEBUFFER, ret);
|
||||||
|
gl.framebufferTexture2D(
|
||||||
|
gl.FRAMEBUFFER,
|
||||||
|
gl.COLOR_ATTACHMENT0,
|
||||||
|
gl.TEXTURE_2D,
|
||||||
|
this._colorBuffer,
|
||||||
|
0
|
||||||
|
);
|
||||||
|
|
||||||
|
// 状態をチェック
|
||||||
|
const status = gl.checkFramebufferStatus(gl.FRAMEBUFFER);
|
||||||
|
|
||||||
|
// フレームバッファが完全でない場合はエラーを出力して以前のフレームバッファを復元
|
||||||
|
if (status !== gl.FRAMEBUFFER_COMPLETE) {
|
||||||
|
CubismLogError('Framebuffer is not complete');
|
||||||
|
gl.bindFramebuffer(gl.FRAMEBUFFER, previousFramebuffer);
|
||||||
|
gl.deleteFramebuffer(ret);
|
||||||
|
|
||||||
|
this.destroyRenderTarget();
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._renderTexture = ret;
|
||||||
|
this._bufferWidth = displayBufferWidth;
|
||||||
|
this._bufferHeight = displayBufferHeight;
|
||||||
|
|
||||||
|
this._gl = gl;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* レンダーターゲットを破棄する。
|
||||||
|
*/
|
||||||
|
public destroyRenderTarget(): void {
|
||||||
|
if (this._colorBuffer) {
|
||||||
|
this._gl.bindTexture(this._gl.TEXTURE_2D, null);
|
||||||
|
this._gl.deleteTexture(this._colorBuffer);
|
||||||
|
this._colorBuffer = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this._renderTexture) {
|
||||||
|
this._gl.bindFramebuffer(this._gl.FRAMEBUFFER, null);
|
||||||
|
this._gl.deleteFramebuffer(this._renderTexture);
|
||||||
|
this._renderTexture = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* WebGLのコンテキストを取得する。
|
||||||
|
*
|
||||||
|
* @return WebGLRenderingContextまたはWebGL2RenderingContext
|
||||||
|
*/
|
||||||
|
public getGL(): WebGLRenderingContext | WebGL2RenderingContext {
|
||||||
|
return this._gl;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* レンダーテクスチャを取得する。
|
||||||
|
*
|
||||||
|
* @return WebGLFramebuffer
|
||||||
|
*/
|
||||||
|
public getRenderTexture(): WebGLFramebuffer {
|
||||||
|
return this._renderTexture;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* カラーバッファを取得する。
|
||||||
|
*
|
||||||
|
* @return WebGLTexture
|
||||||
|
*/
|
||||||
|
public getColorBuffer(): WebGLTexture {
|
||||||
|
return this._colorBuffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* カラーバッファの幅を取得する。
|
||||||
|
*
|
||||||
|
* @return カラーバッファの幅
|
||||||
|
*/
|
||||||
|
public getBufferWidth(): number {
|
||||||
|
return this._bufferWidth;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* カラーバッファの高さを取得する。
|
||||||
|
*
|
||||||
|
* @return カラーバッファの高さ
|
||||||
|
*/
|
||||||
|
public getBufferHeight(): number {
|
||||||
|
return this._bufferHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* オフスクリーンサーフェスが有効かどうかを確認する。
|
||||||
|
*
|
||||||
|
* @return 有効な場合はtrue、無効な場合はfalse
|
||||||
|
*/
|
||||||
|
public isValid(): boolean {
|
||||||
|
return this._renderTexture != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 以前のフレームバッファを取得する。
|
||||||
|
*
|
||||||
|
* @return 以前のフレームバッファ
|
||||||
|
*/
|
||||||
|
public getOldFBO(): WebGLFramebuffer {
|
||||||
|
return this._oldFbo;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* コンストラクタ
|
||||||
|
*/
|
||||||
|
constructor() {
|
||||||
|
this._gl = null;
|
||||||
|
this._colorBuffer = null;
|
||||||
|
this._renderTexture = null;
|
||||||
|
this._bufferWidth = 0;
|
||||||
|
this._bufferHeight = 0;
|
||||||
|
this._oldFbo = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected _gl: WebGLRenderingContext | WebGL2RenderingContext; // WebGLのコンテキスト
|
||||||
|
protected _colorBuffer: WebGLTexture; // カラーバッファ
|
||||||
|
protected _renderTexture: WebGLFramebuffer; // フレームバッファ
|
||||||
|
protected _bufferWidth: number; // カラーバッファの幅
|
||||||
|
protected _bufferHeight: number; // カラーバッファの高さ
|
||||||
|
private _oldFbo: WebGLFramebuffer; // 以前のフレームバッファ
|
||||||
|
}
|
||||||
|
|
||||||
|
// Namespace definition for compatibility.
|
||||||
|
import * as $ from './cubismrendertarget_webgl';
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-namespace
|
||||||
|
export namespace Live2DCubismFramework {
|
||||||
|
export const CubismOffscreenSurface_WebGL = $.CubismRenderTarget_WebGL;
|
||||||
|
export type CubismOffscreenSurface_WebGL = $.CubismRenderTarget_WebGL;
|
||||||
|
}
|
||||||
2063
avatar-h5-renderer/framework/src/rendering/cubismshader_webgl.ts
Normal file
2063
avatar-h5-renderer/framework/src/rendering/cubismshader_webgl.ts
Normal file
File diff suppressed because it is too large
Load Diff
89
avatar-h5-renderer/framework/src/type/csmrectf.ts
Normal file
89
avatar-h5-renderer/framework/src/type/csmrectf.ts
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
/**
|
||||||
|
* Copyright(c) Live2D Inc. All rights reserved.
|
||||||
|
*
|
||||||
|
* Use of this source code is governed by the Live2D Open Software license
|
||||||
|
* that can be found at https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 矩形形状(座標・長さはfloat値)を定義するクラス
|
||||||
|
*/
|
||||||
|
export class csmRect {
|
||||||
|
/**
|
||||||
|
* コンストラクタ
|
||||||
|
* @param x 左端X座標
|
||||||
|
* @param y 上端Y座標
|
||||||
|
* @param w 幅
|
||||||
|
* @param h 高さ
|
||||||
|
*/
|
||||||
|
public constructor(x?: number, y?: number, w?: number, h?: number) {
|
||||||
|
this.x = x;
|
||||||
|
this.y = y;
|
||||||
|
this.width = w;
|
||||||
|
this.height = h;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 矩形中央のX座標を取得する
|
||||||
|
*/
|
||||||
|
public getCenterX(): number {
|
||||||
|
return this.x + 0.5 * this.width;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 矩形中央のY座標を取得する
|
||||||
|
*/
|
||||||
|
public getCenterY(): number {
|
||||||
|
return this.y + 0.5 * this.height;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 右側のX座標を取得する
|
||||||
|
*/
|
||||||
|
public getRight(): number {
|
||||||
|
return this.x + this.width;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 下端のY座標を取得する
|
||||||
|
*/
|
||||||
|
public getBottom(): number {
|
||||||
|
return this.y + this.height;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 矩形に値をセットする
|
||||||
|
* @param r 矩形のインスタンス
|
||||||
|
*/
|
||||||
|
public setRect(r: csmRect): void {
|
||||||
|
this.x = r.x;
|
||||||
|
this.y = r.y;
|
||||||
|
this.width = r.width;
|
||||||
|
this.height = r.height;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 矩形中央を軸にして縦横を拡縮する
|
||||||
|
* @param w 幅方向に拡縮する量
|
||||||
|
* @param h 高さ方向に拡縮する量
|
||||||
|
*/
|
||||||
|
public expand(w: number, h: number) {
|
||||||
|
this.x -= w;
|
||||||
|
this.y -= h;
|
||||||
|
this.width += w * 2.0;
|
||||||
|
this.height += h * 2.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public x: number; // 左端X座標
|
||||||
|
public y: number; // 上端Y座標
|
||||||
|
public width: number; // 幅
|
||||||
|
public height: number; // 高さ
|
||||||
|
}
|
||||||
|
|
||||||
|
// Namespace definition for compatibility.
|
||||||
|
import * as $ from './csmrectf';
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-namespace
|
||||||
|
export namespace Live2DCubismFramework {
|
||||||
|
export const csmRect = $.csmRect;
|
||||||
|
export type csmRect = $.csmRect;
|
||||||
|
}
|
||||||
42
avatar-h5-renderer/framework/src/utils/cubismarrayutils.ts
Normal file
42
avatar-h5-renderer/framework/src/utils/cubismarrayutils.ts
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
/**
|
||||||
|
* Copyright(c) Live2D Inc. All rights reserved.
|
||||||
|
*
|
||||||
|
* Use of this source code is governed by the Live2D Open Software license
|
||||||
|
* that can be found at https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Arrayのサイズを変更する。
|
||||||
|
* @param curArray
|
||||||
|
* @param newSize
|
||||||
|
* @param value
|
||||||
|
* @param callPlacementNew
|
||||||
|
*/
|
||||||
|
export function updateSize<T>(
|
||||||
|
curArray: Array<T>,
|
||||||
|
newSize: number,
|
||||||
|
value: any = null,
|
||||||
|
callPlacementNew: boolean = null
|
||||||
|
): void {
|
||||||
|
const curSize: number = curArray.length;
|
||||||
|
|
||||||
|
if (curSize < newSize) {
|
||||||
|
if (callPlacementNew) {
|
||||||
|
for (let i: number = curArray.length; i < newSize; i++) {
|
||||||
|
if (typeof value == 'function') {
|
||||||
|
// new
|
||||||
|
curArray[i] = JSON.parse(JSON.stringify(new value()));
|
||||||
|
} // プリミティブ型なので値渡し
|
||||||
|
else {
|
||||||
|
curArray[i] = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (let i: number = curArray.length; i < newSize; i++) {
|
||||||
|
curArray[i] = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
curArray.length = newSize;
|
||||||
|
}
|
||||||
|
}
|
||||||
162
avatar-h5-renderer/framework/src/utils/cubismdebug.ts
Normal file
162
avatar-h5-renderer/framework/src/utils/cubismdebug.ts
Normal file
@ -0,0 +1,162 @@
|
|||||||
|
/**
|
||||||
|
* Copyright(c) Live2D Inc. All rights reserved.
|
||||||
|
*
|
||||||
|
* Use of this source code is governed by the Live2D Open Software license
|
||||||
|
* that can be found at https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import {
|
||||||
|
CSM_LOG_LEVEL,
|
||||||
|
CSM_LOG_LEVEL_DEBUG,
|
||||||
|
CSM_LOG_LEVEL_ERROR,
|
||||||
|
CSM_LOG_LEVEL_INFO,
|
||||||
|
CSM_LOG_LEVEL_VERBOSE,
|
||||||
|
CSM_LOG_LEVEL_WARNING
|
||||||
|
} from '../cubismframeworkconfig';
|
||||||
|
import { CubismFramework, LogLevel } from '../live2dcubismframework';
|
||||||
|
|
||||||
|
export const CubismLogPrint = (level: LogLevel, fmt: string, args: any[]) => {
|
||||||
|
CubismDebug.print(level, '[CSM]' + fmt, args);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const CubismLogPrintIn = (level: LogLevel, fmt: string, args: any[]) => {
|
||||||
|
CubismLogPrint(level, fmt + '\n', args);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const CSM_ASSERT = (expr: any) => {
|
||||||
|
console.assert(expr);
|
||||||
|
};
|
||||||
|
|
||||||
|
export let CubismLogVerbose: (fmt: string, ...args: any[]) => void;
|
||||||
|
export let CubismLogDebug: (fmt: string, ...args: any[]) => void;
|
||||||
|
export let CubismLogInfo: (fmt: string, ...args: any[]) => void;
|
||||||
|
export let CubismLogWarning: (fmt: string, ...args: any[]) => void;
|
||||||
|
export let CubismLogError: (fmt: string, ...args: any[]) => void;
|
||||||
|
|
||||||
|
if (CSM_LOG_LEVEL <= CSM_LOG_LEVEL_VERBOSE) {
|
||||||
|
CubismLogVerbose = (fmt: string, ...args: any[]) => {
|
||||||
|
CubismLogPrintIn(LogLevel.LogLevel_Verbose, '[V]' + fmt, args);
|
||||||
|
};
|
||||||
|
|
||||||
|
CubismLogDebug = (fmt: string, ...args: any[]) => {
|
||||||
|
CubismLogPrintIn(LogLevel.LogLevel_Debug, '[D]' + fmt, args);
|
||||||
|
};
|
||||||
|
|
||||||
|
CubismLogInfo = (fmt: string, ...args: any[]) => {
|
||||||
|
CubismLogPrintIn(LogLevel.LogLevel_Info, '[I]' + fmt, args);
|
||||||
|
};
|
||||||
|
|
||||||
|
CubismLogWarning = (fmt: string, ...args: any[]) => {
|
||||||
|
CubismLogPrintIn(LogLevel.LogLevel_Warning, '[W]' + fmt, args);
|
||||||
|
};
|
||||||
|
|
||||||
|
CubismLogError = (fmt: string, ...args: any[]) => {
|
||||||
|
CubismLogPrintIn(LogLevel.LogLevel_Error, '[E]' + fmt, args);
|
||||||
|
};
|
||||||
|
} else if (CSM_LOG_LEVEL == CSM_LOG_LEVEL_DEBUG) {
|
||||||
|
CubismLogDebug = (fmt: string, ...args: any[]) => {
|
||||||
|
CubismLogPrintIn(LogLevel.LogLevel_Debug, '[D]' + fmt, args);
|
||||||
|
};
|
||||||
|
|
||||||
|
CubismLogInfo = (fmt: string, ...args: any[]) => {
|
||||||
|
CubismLogPrintIn(LogLevel.LogLevel_Info, '[I]' + fmt, args);
|
||||||
|
};
|
||||||
|
|
||||||
|
CubismLogWarning = (fmt: string, ...args: any[]) => {
|
||||||
|
CubismLogPrintIn(LogLevel.LogLevel_Warning, '[W]' + fmt, args);
|
||||||
|
};
|
||||||
|
|
||||||
|
CubismLogError = (fmt: string, ...args: any[]) => {
|
||||||
|
CubismLogPrintIn(LogLevel.LogLevel_Error, '[E]' + fmt, args);
|
||||||
|
};
|
||||||
|
} else if (CSM_LOG_LEVEL == CSM_LOG_LEVEL_INFO) {
|
||||||
|
CubismLogInfo = (fmt: string, ...args: any[]) => {
|
||||||
|
CubismLogPrintIn(LogLevel.LogLevel_Info, '[I]' + fmt, args);
|
||||||
|
};
|
||||||
|
|
||||||
|
CubismLogWarning = (fmt: string, ...args: any[]) => {
|
||||||
|
CubismLogPrintIn(LogLevel.LogLevel_Warning, '[W]' + fmt, args);
|
||||||
|
};
|
||||||
|
|
||||||
|
CubismLogError = (fmt: string, ...args: any[]) => {
|
||||||
|
CubismLogPrintIn(LogLevel.LogLevel_Error, '[E]' + fmt, args);
|
||||||
|
};
|
||||||
|
} else if (CSM_LOG_LEVEL == CSM_LOG_LEVEL_WARNING) {
|
||||||
|
CubismLogWarning = (fmt: string, ...args: any[]) => {
|
||||||
|
CubismLogPrintIn(LogLevel.LogLevel_Warning, '[W]' + fmt, args);
|
||||||
|
};
|
||||||
|
|
||||||
|
CubismLogError = (fmt: string, ...args: any[]) => {
|
||||||
|
CubismLogPrintIn(LogLevel.LogLevel_Error, '[E]' + fmt, args);
|
||||||
|
};
|
||||||
|
} else if (CSM_LOG_LEVEL == CSM_LOG_LEVEL_ERROR) {
|
||||||
|
CubismLogError = (fmt: string, ...args: any[]) => {
|
||||||
|
CubismLogPrintIn(LogLevel.LogLevel_Error, '[E]' + fmt, args);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* デバッグ用のユーティリティクラス。
|
||||||
|
* ログの出力、バイトのダンプなど
|
||||||
|
*/
|
||||||
|
export class CubismDebug {
|
||||||
|
/**
|
||||||
|
* ログを出力する。第一引数にログレベルを設定する。
|
||||||
|
* CubismFramework.initialize()時にオプションで設定されたログ出力レベルを下回る場合はログに出さない。
|
||||||
|
*
|
||||||
|
* @param logLevel ログレベルの設定
|
||||||
|
* @param format 書式付き文字列
|
||||||
|
* @param args 可変長引数
|
||||||
|
*/
|
||||||
|
public static print(logLevel: LogLevel, format: string, args?: any[]): void {
|
||||||
|
// オプションで設定されたログ出力レベルを下回る場合はログに出さない
|
||||||
|
if (logLevel < CubismFramework.getLoggingLevel()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const logPrint: Live2DCubismCore.csmLogFunction =
|
||||||
|
CubismFramework.coreLogFunction;
|
||||||
|
|
||||||
|
if (!logPrint) return;
|
||||||
|
|
||||||
|
const buffer: string = format.replace(/\{(\d+)\}/g, (m, k) => {
|
||||||
|
return args[k];
|
||||||
|
});
|
||||||
|
logPrint(buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* データから指定した長さだけダンプ出力する。
|
||||||
|
* CubismFramework.initialize()時にオプションで設定されたログ出力レベルを下回る場合はログに出さない。
|
||||||
|
*
|
||||||
|
* @param logLevel ログレベルの設定
|
||||||
|
* @param data ダンプするデータ
|
||||||
|
* @param length ダンプする長さ
|
||||||
|
*/
|
||||||
|
public static dumpBytes(
|
||||||
|
logLevel: LogLevel,
|
||||||
|
data: Uint8Array,
|
||||||
|
length: number
|
||||||
|
): void {
|
||||||
|
for (let i = 0; i < length; i++) {
|
||||||
|
if (i % 16 == 0 && i > 0) this.print(logLevel, '\n');
|
||||||
|
else if (i % 8 == 0 && i > 0) this.print(logLevel, ' ');
|
||||||
|
this.print(logLevel, '{0} ', [data[i] & 0xff]);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.print(logLevel, '\n');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* private コンストラクタ
|
||||||
|
*/
|
||||||
|
private constructor() {}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Namespace definition for compatibility.
|
||||||
|
import * as $ from './cubismdebug';
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-namespace
|
||||||
|
export namespace Live2DCubismFramework {
|
||||||
|
export const CubismDebug = $.CubismDebug;
|
||||||
|
export type CubismDebug = $.CubismDebug;
|
||||||
|
}
|
||||||
1244
avatar-h5-renderer/framework/src/utils/cubismjson.ts
Normal file
1244
avatar-h5-renderer/framework/src/utils/cubismjson.ts
Normal file
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,99 @@
|
|||||||
|
/**
|
||||||
|
* Copyright(c) Live2D Inc. All rights reserved.
|
||||||
|
*
|
||||||
|
* Use of this source code is governed by the Live2D Open Software license
|
||||||
|
* that can be found at https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import {
|
||||||
|
JsonArray,
|
||||||
|
JsonBoolean,
|
||||||
|
JsonFloat,
|
||||||
|
JsonMap,
|
||||||
|
JsonNullvalue,
|
||||||
|
JsonString,
|
||||||
|
Value
|
||||||
|
} from './cubismjson';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CubismJsonで実装されているJsonパーサを使用せず、
|
||||||
|
* TypeScript標準のJsonパーサなどを使用し出力された結果を
|
||||||
|
* Cubism SDKで定義されているJSONエレメントの要素に
|
||||||
|
* 置き換える処理をするクラス。
|
||||||
|
*/
|
||||||
|
export class CubismJsonExtension {
|
||||||
|
static parseJsonObject(obj: Value, map: JsonMap) {
|
||||||
|
Object.keys(obj).forEach(key => {
|
||||||
|
if (typeof obj[key] == 'boolean') {
|
||||||
|
const convValue = Boolean(obj[key]);
|
||||||
|
map.put(key, new JsonBoolean(convValue));
|
||||||
|
} else if (typeof obj[key] == 'string') {
|
||||||
|
const convValue = String(obj[key]);
|
||||||
|
map.put(key, new JsonString(convValue));
|
||||||
|
} else if (typeof obj[key] == 'number') {
|
||||||
|
const convValue = Number(obj[key]);
|
||||||
|
map.put(key, new JsonFloat(convValue));
|
||||||
|
} else if (obj[key] instanceof Array) {
|
||||||
|
// HACK: Array 単体で変換できないので unknown に変更してから Value にしている
|
||||||
|
map.put(
|
||||||
|
key,
|
||||||
|
CubismJsonExtension.parseJsonArray(obj[key] as unknown as Value)
|
||||||
|
);
|
||||||
|
} else if (obj[key] instanceof Object) {
|
||||||
|
map.put(
|
||||||
|
key,
|
||||||
|
CubismJsonExtension.parseJsonObject(obj[key], new JsonMap())
|
||||||
|
);
|
||||||
|
} else if (obj[key] == null) {
|
||||||
|
map.put(key, new JsonNullvalue());
|
||||||
|
} else {
|
||||||
|
// どれにも当てはまらない場合でも処理する
|
||||||
|
map.put(key, obj[key]);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static parseJsonArray(obj: Value) {
|
||||||
|
const arr = new JsonArray();
|
||||||
|
Object.keys(obj).forEach(key => {
|
||||||
|
const convKey = Number(key);
|
||||||
|
if (typeof convKey == 'number') {
|
||||||
|
if (typeof obj[key] == 'boolean') {
|
||||||
|
const convValue = Boolean(obj[key]);
|
||||||
|
arr.add(new JsonBoolean(convValue));
|
||||||
|
} else if (typeof obj[key] == 'string') {
|
||||||
|
const convValue = String(obj[key]);
|
||||||
|
arr.add(new JsonString(convValue));
|
||||||
|
} else if (typeof obj[key] == 'number') {
|
||||||
|
const convValue = Number(obj[key]);
|
||||||
|
arr.add(new JsonFloat(convValue));
|
||||||
|
} else if (obj[key] instanceof Array) {
|
||||||
|
// HACK: Array 単体で変換できないので unknown に変更してから Value にしている
|
||||||
|
arr.add(this.parseJsonArray(obj[key] as unknown as Value));
|
||||||
|
} else if (obj[key] instanceof Object) {
|
||||||
|
arr.add(this.parseJsonObject(obj[key], new JsonMap()));
|
||||||
|
} else if (obj[key] == null) {
|
||||||
|
arr.add(new JsonNullvalue());
|
||||||
|
} else {
|
||||||
|
// どれにも当てはまらない場合でも処理する
|
||||||
|
arr.add(obj[key]);
|
||||||
|
}
|
||||||
|
} else if (obj[key] instanceof Array) {
|
||||||
|
// HACK: Array 単体で変換できないので unknown に変更してから Value にしている
|
||||||
|
arr.add(this.parseJsonArray(obj[key] as unknown as Value));
|
||||||
|
} else if (obj[key] instanceof Object) {
|
||||||
|
arr.add(this.parseJsonObject(obj[key], new JsonMap()));
|
||||||
|
} else if (obj[key] == null) {
|
||||||
|
arr.add(new JsonNullvalue());
|
||||||
|
} else {
|
||||||
|
const convValue = Array(obj[key]);
|
||||||
|
// 配列ともObjectとも判定できなかった場合でも処理する
|
||||||
|
for (let i = 0; i < convValue.length; i++) {
|
||||||
|
arr.add(convValue[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return arr;
|
||||||
|
}
|
||||||
|
}
|
||||||
129
avatar-h5-renderer/framework/src/utils/cubismstring.ts
Normal file
129
avatar-h5-renderer/framework/src/utils/cubismstring.ts
Normal file
@ -0,0 +1,129 @@
|
|||||||
|
/**
|
||||||
|
* Copyright(c) Live2D Inc. All rights reserved.
|
||||||
|
*
|
||||||
|
* Use of this source code is governed by the Live2D Open Software license
|
||||||
|
* that can be found at https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
|
||||||
|
*/
|
||||||
|
|
||||||
|
export class CubismString {
|
||||||
|
/**
|
||||||
|
* 標準出力の書式を適用した文字列を取得する。
|
||||||
|
* @param format 標準出力の書式指定文字列
|
||||||
|
* @param ...args 書式指定文字列に渡す文字列
|
||||||
|
* @return 書式を適用した文字列
|
||||||
|
*/
|
||||||
|
public static getFormatedString(format: string, ...args: any[]): string {
|
||||||
|
const ret: string = format;
|
||||||
|
return ret.replace(
|
||||||
|
/\{(\d+)\}/g,
|
||||||
|
(
|
||||||
|
m,
|
||||||
|
k // m="{0}", k="0"
|
||||||
|
) => {
|
||||||
|
return args[k];
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* textがstartWordで始まっているかどうかを返す
|
||||||
|
* @param test 検査対象の文字列
|
||||||
|
* @param startWord 比較対象の文字列
|
||||||
|
* @return true textがstartWordで始まっている
|
||||||
|
* @return false textがstartWordで始まっていない
|
||||||
|
*/
|
||||||
|
public static isStartWith(text: string, startWord: string): boolean {
|
||||||
|
let textIndex = 0;
|
||||||
|
let startWordIndex = 0;
|
||||||
|
while (startWord[startWordIndex] != '\0') {
|
||||||
|
if (
|
||||||
|
text[textIndex] == '\0' ||
|
||||||
|
text[textIndex++] != startWord[startWordIndex++]
|
||||||
|
) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* position位置の文字から数字を解析する。
|
||||||
|
*
|
||||||
|
* @param string 文字列
|
||||||
|
* @param length 文字列の長さ
|
||||||
|
* @param position 解析したい文字の位置
|
||||||
|
* @param outEndPos 一文字も読み込まなかった場合はエラー値(-1)が入る
|
||||||
|
* @return 解析結果の数値
|
||||||
|
*/
|
||||||
|
public static stringToFloat(
|
||||||
|
string: string,
|
||||||
|
length: number,
|
||||||
|
position: number,
|
||||||
|
outEndPos: number[]
|
||||||
|
): number {
|
||||||
|
let i: number = position;
|
||||||
|
let minus = false; // マイナスフラグ
|
||||||
|
let period = false;
|
||||||
|
let v1 = 0;
|
||||||
|
|
||||||
|
//負号の確認
|
||||||
|
let c: number = parseInt(string[i]);
|
||||||
|
if (c < 0) {
|
||||||
|
minus = true;
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
//整数部の確認
|
||||||
|
for (; i < length; i++) {
|
||||||
|
const c = string[i];
|
||||||
|
if (0 <= parseInt(c) && parseInt(c) <= 9) {
|
||||||
|
v1 = v1 * 10 + (parseInt(c) - 0);
|
||||||
|
} else if (c == '.') {
|
||||||
|
period = true;
|
||||||
|
i++;
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//小数部の確認
|
||||||
|
if (period) {
|
||||||
|
let mul = 0.1;
|
||||||
|
for (; i < length; i++) {
|
||||||
|
c = parseFloat(string[i]) & 0xff;
|
||||||
|
if (0 <= c && c <= 9) {
|
||||||
|
v1 += mul * (c - 0);
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
mul *= 0.1; //一桁下げる
|
||||||
|
if (!c) break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i == position) {
|
||||||
|
//一文字も読み込まなかった場合
|
||||||
|
outEndPos[0] = -1; //エラー値が入るので呼び出し元で適切な処理を行う
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (minus) v1 = -v1;
|
||||||
|
|
||||||
|
outEndPos[0] = i;
|
||||||
|
return v1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* コンストラクタ呼び出し不可な静的クラスにする。
|
||||||
|
*/
|
||||||
|
private constructor() {}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Namespace definition for compatibility.
|
||||||
|
import * as $ from './cubismstring';
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-namespace
|
||||||
|
export namespace Live2DCubismFramework {
|
||||||
|
export const CubismString = $.CubismString;
|
||||||
|
export type CubismString = $.CubismString;
|
||||||
|
}
|
||||||
24
avatar-h5-renderer/framework/tsconfig.json
Normal file
24
avatar-h5-renderer/framework/tsconfig.json
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "es6",
|
||||||
|
"moduleResolution": "node",
|
||||||
|
"esModuleInterop": true,
|
||||||
|
"experimentalDecorators": true,
|
||||||
|
"forceConsistentCasingInFileNames": true,
|
||||||
|
"outDir": "./dist",
|
||||||
|
"declaration": true,
|
||||||
|
"declarationMap": true,
|
||||||
|
"sourceMap": true,
|
||||||
|
"emitDecoratorMetadata": true,
|
||||||
|
"noImplicitAny": true,
|
||||||
|
"useUnknownInCatchVariables": true
|
||||||
|
},
|
||||||
|
"include": [
|
||||||
|
"src/**/*.ts",
|
||||||
|
"../Core/*.ts"
|
||||||
|
],
|
||||||
|
"exclude": [
|
||||||
|
"node_modules",
|
||||||
|
"dist"
|
||||||
|
]
|
||||||
|
}
|
||||||
269
avatar-h5-renderer/index.html
Normal file
269
avatar-h5-renderer/index.html
Normal file
@ -0,0 +1,269 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="zh">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no">
|
||||||
|
<title>Avatar H5 Renderer (PoC)</title>
|
||||||
|
<style>
|
||||||
|
html, body {
|
||||||
|
overflow: hidden;
|
||||||
|
margin: 0;
|
||||||
|
height: 100%;
|
||||||
|
font-family: -apple-system, "PingFang SC", "Microsoft YaHei", sans-serif;
|
||||||
|
background: #1a1a1f;
|
||||||
|
}
|
||||||
|
body {
|
||||||
|
background-image: url('/back_class_normal.png');
|
||||||
|
background-size: cover;
|
||||||
|
background-position: center;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
}
|
||||||
|
html {
|
||||||
|
overscroll-behavior-x: none;
|
||||||
|
touch-action: none;
|
||||||
|
}
|
||||||
|
body {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
body > canvas {
|
||||||
|
width: 100vw;
|
||||||
|
height: 100vh;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 调试面板 */
|
||||||
|
#debug-panel {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
right: 0;
|
||||||
|
width: 320px;
|
||||||
|
max-height: 100vh;
|
||||||
|
overflow-y: auto;
|
||||||
|
padding: 16px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
background: rgba(15, 15, 20, 0.85);
|
||||||
|
backdrop-filter: blur(10px);
|
||||||
|
color: #e5e5e5;
|
||||||
|
font-size: 13px;
|
||||||
|
line-height: 1.5;
|
||||||
|
border-left: 1px solid #333;
|
||||||
|
z-index: 999;
|
||||||
|
}
|
||||||
|
#debug-panel h3 {
|
||||||
|
margin: 0 0 8px;
|
||||||
|
font-size: 14px;
|
||||||
|
color: #80c0ff;
|
||||||
|
letter-spacing: 0.5px;
|
||||||
|
}
|
||||||
|
#debug-panel section {
|
||||||
|
margin-bottom: 14px;
|
||||||
|
padding-bottom: 12px;
|
||||||
|
border-bottom: 1px solid #2a2a2f;
|
||||||
|
}
|
||||||
|
#debug-panel section:last-child { border-bottom: none; }
|
||||||
|
#debug-panel .row {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 6px;
|
||||||
|
}
|
||||||
|
#debug-panel button {
|
||||||
|
padding: 6px 10px;
|
||||||
|
border: 1px solid #444;
|
||||||
|
background: #222;
|
||||||
|
color: #ddd;
|
||||||
|
border-radius: 4px;
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 12px;
|
||||||
|
transition: all 0.12s;
|
||||||
|
}
|
||||||
|
#debug-panel button:hover {
|
||||||
|
background: #2d4a6b;
|
||||||
|
border-color: #5588cc;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
#debug-panel button.primary {
|
||||||
|
background: #2766b6;
|
||||||
|
border-color: #4488dd;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
#debug-panel button.primary:hover {
|
||||||
|
background: #3a85d6;
|
||||||
|
}
|
||||||
|
#debug-panel input[type=range] {
|
||||||
|
width: 100%;
|
||||||
|
margin-top: 6px;
|
||||||
|
}
|
||||||
|
#debug-panel .value {
|
||||||
|
color: #80c0ff;
|
||||||
|
font-family: ui-monospace, Menlo, monospace;
|
||||||
|
}
|
||||||
|
.hint {
|
||||||
|
font-size: 11px;
|
||||||
|
color: #888;
|
||||||
|
margin-top: 6px;
|
||||||
|
}
|
||||||
|
code {
|
||||||
|
color: #80c0ff;
|
||||||
|
font-family: ui-monospace, Menlo, monospace;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<!-- Live2D Cubism Core -->
|
||||||
|
<script src="/Core/live2dcubismcore.js"></script>
|
||||||
|
<!-- App entry -->
|
||||||
|
<script src="./src/main.ts" type="module"></script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="debug-panel">
|
||||||
|
<section>
|
||||||
|
<h3>角色状态</h3>
|
||||||
|
<div class="row">
|
||||||
|
<button data-act="state" data-arg="idle">idle 待机</button>
|
||||||
|
<button data-act="state" data-arg="listening">listening 倾听</button>
|
||||||
|
<button data-act="state" data-arg="thinking">thinking 思考</button>
|
||||||
|
<button data-act="state" data-arg="speaking">speaking 说话</button>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section>
|
||||||
|
<h3>表情切换</h3>
|
||||||
|
<div class="row" id="expression-buttons">
|
||||||
|
<span class="hint">加载中...</span>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section>
|
||||||
|
<h3>动作播放</h3>
|
||||||
|
<div class="row" id="motion-buttons">
|
||||||
|
<span class="hint">加载中...</span>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section>
|
||||||
|
<h3>嘴型驱动 <span class="value" id="mouth-value">0.00</span></h3>
|
||||||
|
<input type="range" id="mouth-slider" min="0" max="100" value="0">
|
||||||
|
<div class="row" style="margin-top:6px">
|
||||||
|
<button data-act="mouth" data-arg="0">闭</button>
|
||||||
|
<button data-act="mouth" data-arg="0.5">半开</button>
|
||||||
|
<button data-act="mouth" data-arg="1">全开</button>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section>
|
||||||
|
<h3>语义动作</h3>
|
||||||
|
<div class="row" id="action-buttons"></div>
|
||||||
|
<div class="hint">对应未来 LLM Function Call 触发</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section>
|
||||||
|
<h3>跳舞</h3>
|
||||||
|
<div class="row">
|
||||||
|
<button class="primary" data-act="dance-start">💃 开始跳舞</button>
|
||||||
|
<button data-act="dance-stop">停止</button>
|
||||||
|
</div>
|
||||||
|
<div class="hint">程序化驱动身体参数 + 循环切 Dance motion</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section>
|
||||||
|
<h3>事件模拟</h3>
|
||||||
|
<div class="row">
|
||||||
|
<button class="primary" data-act="mock-conversation">▶ 播放模拟对话</button>
|
||||||
|
</div>
|
||||||
|
<div class="hint">listening → thinking → 微笑+说话 → idle</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section>
|
||||||
|
<h3>Console API</h3>
|
||||||
|
<div class="hint">
|
||||||
|
浏览器控制台可直接调:<br>
|
||||||
|
<code>avatar.setExpression("Smile")</code><br>
|
||||||
|
<code>avatar.playMotion("Idle", 0)</code><br>
|
||||||
|
<code>avatar.setMouthOpen(0.8)</code><br>
|
||||||
|
<code>avatar.listExpressions()</code><br>
|
||||||
|
<code>avatar.listMotions()</code>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script type="module">
|
||||||
|
function waitReady() {
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
const check = () => {
|
||||||
|
if (window.avatar) {
|
||||||
|
const exps = window.avatar.listExpressions();
|
||||||
|
const mots = window.avatar.listMotions();
|
||||||
|
if (exps.length > 0 || Object.keys(mots).length > 0) {
|
||||||
|
resolve();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
setTimeout(check, 150);
|
||||||
|
};
|
||||||
|
check();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
waitReady().then(() => {
|
||||||
|
const expBox = document.getElementById('expression-buttons');
|
||||||
|
expBox.innerHTML = '';
|
||||||
|
window.avatar.listExpressions().forEach((name) => {
|
||||||
|
const btn = document.createElement('button');
|
||||||
|
btn.textContent = name;
|
||||||
|
btn.onclick = () => window.avatar.setExpression(name);
|
||||||
|
expBox.appendChild(btn);
|
||||||
|
});
|
||||||
|
|
||||||
|
const motionBox = document.getElementById('motion-buttons');
|
||||||
|
motionBox.innerHTML = '';
|
||||||
|
const motions = window.avatar.listMotions();
|
||||||
|
Object.entries(motions).forEach(([group, count]) => {
|
||||||
|
for (let i = 0; i < count; i++) {
|
||||||
|
const btn = document.createElement('button');
|
||||||
|
btn.textContent = `${group}#${i}`;
|
||||||
|
btn.onclick = () => window.avatar.playMotion(group, i);
|
||||||
|
motionBox.appendChild(btn);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const actionBox = document.getElementById('action-buttons');
|
||||||
|
actionBox.innerHTML = '';
|
||||||
|
window.avatar.listActions().forEach((name) => {
|
||||||
|
const btn = document.createElement('button');
|
||||||
|
btn.textContent = name;
|
||||||
|
btn.onclick = () => window.avatar.playAction(name);
|
||||||
|
actionBox.appendChild(btn);
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log('[debug-panel] ready. avatar API:', window.avatar);
|
||||||
|
});
|
||||||
|
|
||||||
|
const slider = document.getElementById('mouth-slider');
|
||||||
|
const mouthVal = document.getElementById('mouth-value');
|
||||||
|
slider.addEventListener('input', () => {
|
||||||
|
const v = slider.value / 100;
|
||||||
|
mouthVal.textContent = v.toFixed(2);
|
||||||
|
window.avatar?.setMouthOpen(v);
|
||||||
|
});
|
||||||
|
|
||||||
|
document.querySelectorAll('[data-act]').forEach((btn) => {
|
||||||
|
btn.addEventListener('click', () => {
|
||||||
|
const act = btn.dataset.act;
|
||||||
|
const arg = btn.dataset.arg;
|
||||||
|
if (act === 'state') window.avatar?.setState(arg);
|
||||||
|
else if (act === 'mouth') {
|
||||||
|
const v = parseFloat(arg);
|
||||||
|
window.avatar?.setMouthOpen(v);
|
||||||
|
slider.value = v * 100;
|
||||||
|
mouthVal.textContent = v.toFixed(2);
|
||||||
|
} else if (act === 'mock-conversation') {
|
||||||
|
window.avatar?.playMockConversation();
|
||||||
|
} else if (act === 'dance-start') {
|
||||||
|
window.avatar?.startDance();
|
||||||
|
} else if (act === 'dance-stop') {
|
||||||
|
window.avatar?.stopDance();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
3868
avatar-h5-renderer/package-lock.json
generated
Normal file
3868
avatar-h5-renderer/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
28
avatar-h5-renderer/package.json
Normal file
28
avatar-h5-renderer/package.json
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
{
|
||||||
|
"private": true,
|
||||||
|
"scripts": {
|
||||||
|
"start": "node copy_resources.js && vite --host",
|
||||||
|
"build": "tsc --noEmit && node copy_resources.js && vite build --mode development",
|
||||||
|
"build:prod": "node copy_resources.js && vite build",
|
||||||
|
"copy_resources": "node copy_resources.js",
|
||||||
|
"test": "tsc --noEmit",
|
||||||
|
"lint": "eslint",
|
||||||
|
"lint:fix": "eslint --fix",
|
||||||
|
"serve": "vite preview --port 5000 --host",
|
||||||
|
"clean": "rimraf dist"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@eslint/js": "^9.39.2",
|
||||||
|
"eslint": "^9.39.2",
|
||||||
|
"eslint-config-prettier": "^10.1.8",
|
||||||
|
"eslint-plugin-prettier": "^5.5.5",
|
||||||
|
"prettier": "^3.8.1",
|
||||||
|
"rimraf": "^6.1.3",
|
||||||
|
"typescript": "^5.9.3",
|
||||||
|
"typescript-eslint": "^8.57.2",
|
||||||
|
"vite": "^8.0.2"
|
||||||
|
},
|
||||||
|
"optionalDependencies": {
|
||||||
|
"fsevents": "*"
|
||||||
|
}
|
||||||
|
}
|
||||||
426
avatar-h5-renderer/public/Core/CHANGELOG.md
Normal file
426
avatar-h5-renderer/public/Core/CHANGELOG.md
Normal file
@ -0,0 +1,426 @@
|
|||||||
|
# Changelog
|
||||||
|
|
||||||
|
All notable changes to this project will be documented in this file.
|
||||||
|
|
||||||
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
||||||
|
|
||||||
|
|
||||||
|
## 2026-04-02
|
||||||
|
|
||||||
|
### Removed
|
||||||
|
|
||||||
|
* [Unity] Remove Android x86 library.
|
||||||
|
|
||||||
|
|
||||||
|
## 2026-01-29
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
* Add Arm64 static library for iphonesimulator.
|
||||||
|
|
||||||
|
|
||||||
|
## 2026-01-08
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
* Upgrade Core version to 06.00.0001.
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
* Fix offscreen opacity calculation.
|
||||||
|
|
||||||
|
|
||||||
|
## 2025-10-30
|
||||||
|
|
||||||
|
### Removed
|
||||||
|
|
||||||
|
* [Native] Remove Visual Studio 2015 (MSVC 140) static library.
|
||||||
|
|
||||||
|
|
||||||
|
## 2025-10-14
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
* Add `csmGetMocVersion(mocBytes: ArrayBuffer)` with simplified arguments from `csmGetMocVersion(moc: Moc, mocBytes: ArrayBuffer)`.
|
||||||
|
* The previous version `csmGetMocVersion(moc: Moc, mocBytes: ArrayBuffer)` is now deprecated and is planned to be removed in the future.
|
||||||
|
|
||||||
|
|
||||||
|
## 2025-08-26
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
* Enhanced model rendering features have been added.
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
* Upgrade Core version to 06.00.0000.
|
||||||
|
|
||||||
|
|
||||||
|
## 2025-07-17
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
* [Unity,Native,Java] Implement support for Android 16KB page size.
|
||||||
|
|
||||||
|
|
||||||
|
## 2025-04-24
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
* Add the function `csmGetParameterRepeats`.
|
||||||
|
* This function retrieves whether the parameters are set to repeat.
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
* Upgrade Core version to 05.01.0000.
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
* Fix `csmGetParameterKeyCounts()` and `csmGetParameterKeyValues()` symbols in the DLL.
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
* Upgrade Core version to 05.01.0000.
|
||||||
|
|
||||||
|
|
||||||
|
## 2024-12-19
|
||||||
|
|
||||||
|
### Removed
|
||||||
|
|
||||||
|
* [Native] Remove Visual Studio 2013 (MSVC 120) static library.
|
||||||
|
|
||||||
|
|
||||||
|
## 2024-11-07
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
* [Native] Add experimental support `arm64` library for linux.
|
||||||
|
|
||||||
|
### Removed
|
||||||
|
|
||||||
|
* [Unity,Native,Java] Remove Android ARM v7 library.
|
||||||
|
|
||||||
|
|
||||||
|
## 2024-04-04
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
* [Unity] Add library(.so) for HarmonyOS build.
|
||||||
|
|
||||||
|
|
||||||
|
## 2024-03-26
|
||||||
|
|
||||||
|
### Remove
|
||||||
|
|
||||||
|
* [Unity] Remove built with Emscripten 1.38.48.
|
||||||
|
* Unity 2021.2 or later uses only Core under `Assets/Live2D/Cubism/Plugins/Experimental/Emscripten/latest`.
|
||||||
|
|
||||||
|
|
||||||
|
## 2023-09-28
|
||||||
|
|
||||||
|
### Remove
|
||||||
|
|
||||||
|
* Remove bitcode from IOS build.
|
||||||
|
|
||||||
|
|
||||||
|
## 2023-08-17
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
* Enhance Blend Shape features.
|
||||||
|
* Please see [here](https://docs.live2d.com/en/cubism-editor-manual/blend-shape/).
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
* Upgrade Core version to 05.00.0000.
|
||||||
|
|
||||||
|
|
||||||
|
## 2023-05-09
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
* Change the GCC version of the library for Linux from 6.5.0 to 8.3.0.
|
||||||
|
|
||||||
|
|
||||||
|
## 2023-03-16
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
* Fix a case in which the index of the mask's drawable object was negative value for `csmGetDrawableMasks()`.
|
||||||
|
* Fix a problem in which `csmHasMocConsistency()` was returned as 0 even though the MOC3 file was in the correct format.
|
||||||
|
* This problem was occurring in some models using the blendshape weight limit settings.
|
||||||
|
* Fix a problem that could cause a crash if a MOC3 file that is not in the correct format is loaded with `csmHasMocConsistency()`.
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
* Upgrade Core version to 04.02.0004.
|
||||||
|
|
||||||
|
|
||||||
|
## 2023-03-10
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
* Add the function `csmHasMocConsistency`.
|
||||||
|
* This function verifies that the `MOC3` file is valid.
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
* Upgrade Core version to 04.02.0003.
|
||||||
|
|
||||||
|
|
||||||
|
## 2023-02-21
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
* [Web] Added classes related to `Memory`.
|
||||||
|
* Add the funciton `initializeAmountOfMemory()` to adjust the amount of memory at initialization.
|
||||||
|
|
||||||
|
|
||||||
|
## 2022-10-28
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
* [Java] Remove unnecessary methods.
|
||||||
|
|
||||||
|
|
||||||
|
## 2022-10-06
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
* [Java] Add AAR file for Android.
|
||||||
|
|
||||||
|
|
||||||
|
## 2022-09-08
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
* Add the multilingual supported documents.
|
||||||
|
* Support Visual Studio 2022.
|
||||||
|
|
||||||
|
|
||||||
|
## 2022-08-04
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
* [Web] Fix `csmGetMocVersion` function argument.
|
||||||
|
|
||||||
|
|
||||||
|
## 2022-07-07
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
* Add functions
|
||||||
|
* `csmGetParameterTypes`
|
||||||
|
* `csmGetDrawableParentPartIndices`
|
||||||
|
|
||||||
|
* Add type `csmMocVersion` and enum. This type is the return value of `csmGetMocVersion`, `csmGetLatestMocVersion`.
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
* Upgrade Core version to 04.02.0002.
|
||||||
|
|
||||||
|
|
||||||
|
## 2022-06-02
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
* Upgrade Core version to 04.02.0001.
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
* Fixed a bug that caused Multiply Color / Screen Color of different objects to be applied.
|
||||||
|
|
||||||
|
|
||||||
|
## 2022-05-19
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
* Support new Multiply Color / Screen Color features.
|
||||||
|
* Support new Blend Shape features.
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
* Upgrade Core version to 04.02.0000. This upgrade is following Cubism Editor 4.2 features.
|
||||||
|
|
||||||
|
|
||||||
|
## 2022-02-10
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
* [Unity] Add bitcode library(.bc) for Emscripten latest version build.
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
* [Unity] Change the bitcode file directory location.
|
||||||
|
* emsdk latest version build bitcode file in `latest` directory.
|
||||||
|
* emsdk 1.38.48 build bitcode file in `1_38_48` directory.
|
||||||
|
|
||||||
|
|
||||||
|
## 2021-12-09
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
* Add static library(.a) for Mac Catalyst.
|
||||||
|
|
||||||
|
|
||||||
|
## 2021-10-07
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
* Add `x86_64` library for Android.
|
||||||
|
* Add `arm64` library for macOS.
|
||||||
|
|
||||||
|
|
||||||
|
## 2021-03-09
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
* Add funtcions for Viewer.
|
||||||
|
* `csmGetParameterKeyCounts`
|
||||||
|
* `csmGetParameterKeyValues`
|
||||||
|
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
* Update Core version to `04.01.0000`.
|
||||||
|
|
||||||
|
|
||||||
|
## 2020-01-30
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
* Add static library(.lib) for statically linking DLL.
|
||||||
|
* Add symbol file for Windows dynamic library (dll).
|
||||||
|
|
||||||
|
|
||||||
|
## 2019-11-19
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
* Fix linking static libraries for Windows (.lib).
|
||||||
|
|
||||||
|
|
||||||
|
## 2019-11-14
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
* Support Visual Studio 2019.
|
||||||
|
* Support macOS dynamic library (dylib).
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
* Update Windows dynamic library: Use Visual Studio 2019 for building.
|
||||||
|
|
||||||
|
### Security
|
||||||
|
|
||||||
|
* Bundle certificate and notary ticket to macOS shared library.
|
||||||
|
|
||||||
|
|
||||||
|
## 2019-09-04
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
* Support new Inverted Masking features.
|
||||||
|
* Support ARM64 architecture for Universal Windows Platform.
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
* Upgrade Core version to 04.00.0000 (67108864). This upgrade is following Cubism Editor 4.0 features.
|
||||||
|
* Add calling convention for *Windows/x86 DLL* only.
|
||||||
|
|
||||||
|
### Removed
|
||||||
|
|
||||||
|
* Remove bitcode binary due to suspension of *Cubism Bindings.*
|
||||||
|
|
||||||
|
|
||||||
|
## 2019-04-09
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
* Support Universal Windows Platform for Windows Store Application.
|
||||||
|
|
||||||
|
|
||||||
|
## 2019-01-31
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
* Add API to get the parent part of the specified part.
|
||||||
|
* Add API to get moc3 version.
|
||||||
|
|
||||||
|
|
||||||
|
## 2018-12-20
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
* [Native] Add new function: `csmGetPartParentPartIndices`.
|
||||||
|
* [Native, 3.3 Support] Support new Warp Deformer features.
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
* Upgrade Core version to 03.03.0000 (50528256). This upgrade is following Cubism Editor 3.3 features.
|
||||||
|
|
||||||
|
|
||||||
|
## 2018-08-22
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
* [Native] Add support for Neon.
|
||||||
|
|
||||||
|
|
||||||
|
## 2018-05-14
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
* [Native] Add Windows **Visual C++ 2013** library.
|
||||||
|
* [Windows] Add runtime library choice `MT`, `MD`, `MTd`, `MDd`.
|
||||||
|
* [iOS] Add support for iPhone Simulator SDK.
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
* Fix an error occurred when linking libraries for Android `arm64-v8a`.
|
||||||
|
|
||||||
|
|
||||||
|
## 2017-11-17
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
* Fix processing of vertex index.
|
||||||
|
|
||||||
|
|
||||||
|
## 2017-10-05
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
* Provide bitcode for iOS.
|
||||||
|
|
||||||
|
|
||||||
|
## 2017-08-09
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
* [Native] Add Android *arm64-v8a* ABI library.
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
* Fix drawing order in certain scenarios.
|
||||||
|
|
||||||
|
|
||||||
|
## 2017-07-12
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
* Add experimental support for Emscripten.
|
||||||
|
* Add `CHANGELOG.md`.
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
* Fix access violation in certain scenarios.
|
||||||
|
* Fix update result in certain scenarios.
|
||||||
|
|
||||||
|
|
||||||
|
## 2017-05-02
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
* [Native] Add experimental support for Raspberry PI.
|
||||||
|
* Add `README.md`.
|
||||||
7
avatar-h5-renderer/public/Core/LICENSE.md
Normal file
7
avatar-h5-renderer/public/Core/LICENSE.md
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
## Live2D Proprietary Software License
|
||||||
|
|
||||||
|
Live2D Cubism Core is available under Live2D Proprietary Software License.
|
||||||
|
|
||||||
|
* [Live2D Proprietary Software License Agreement](https://www.live2d.com/eula/live2d-proprietary-software-license-agreement_en.html)
|
||||||
|
* [Live2D Proprietary Software 使用許諾契約書](https://www.live2d.com/eula/live2d-proprietary-software-license-agreement_jp.html)
|
||||||
|
* [Live2D Proprietary Software 使用授权协议](https://www.live2d.com/eula/live2d-proprietary-software-license-agreement_cn.html)
|
||||||
30
avatar-h5-renderer/public/Core/README.ja.md
Normal file
30
avatar-h5-renderer/public/Core/README.ja.md
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
[English](README.md) / [日本語](README.ja.md)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# Live2D Cubism Core
|
||||||
|
|
||||||
|
このフォルダーには、JavaScriptまたはTypeScriptアプリケーションを開発するためのコアライブラリファイルが含まれています。
|
||||||
|
|
||||||
|
|
||||||
|
## ファイルリスト
|
||||||
|
|
||||||
|
### live2dcubismcore.d.ts
|
||||||
|
|
||||||
|
このファイルには、`live2dcubismcore.js`に関するTypeScriptの型情報が含まれています。
|
||||||
|
TypeScriptで開発する場合は、このファイルを`live2dcubismcore.js`とともに使用してください。
|
||||||
|
|
||||||
|
### live2dcubismcore.js
|
||||||
|
|
||||||
|
このファイルには、CubismCoreの機能といくつかのラッパーが含まれています。
|
||||||
|
JavaScriptで開発する場合は、このファイルを使用してください。
|
||||||
|
|
||||||
|
### live2dcubismcore.js.map
|
||||||
|
|
||||||
|
このファイルは、`live2dcubismcore.d.ts`と`live2dcubismcore.js`の間のソースマップです。
|
||||||
|
デバッグ時にこのファイルを使用します。
|
||||||
|
|
||||||
|
### live2dcubismcore.min.js
|
||||||
|
|
||||||
|
このファイルは、`live2dcubismcore.js`のminify版です。
|
||||||
|
このファイルを本番環境で使用します。
|
||||||
30
avatar-h5-renderer/public/Core/README.md
Normal file
30
avatar-h5-renderer/public/Core/README.md
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
[English](README.md) / [日本語](README.ja.md)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# Live2D Cubism Core
|
||||||
|
|
||||||
|
This folder contains core library files for developing JavaScript or TypeScript applications.
|
||||||
|
|
||||||
|
|
||||||
|
## File List
|
||||||
|
|
||||||
|
### live2dcubismcore.d.ts
|
||||||
|
|
||||||
|
This file contains typescript type information about `live2dcubismcore.js`.
|
||||||
|
Use this file with `live2dcubismcore.js` when developing with TypeScript.
|
||||||
|
|
||||||
|
### live2dcubismcore.js
|
||||||
|
|
||||||
|
This file contains Cubism Core features and some wrapper features.
|
||||||
|
Use this file when developing with JavaScript.
|
||||||
|
|
||||||
|
### live2dcubismcore.js.map
|
||||||
|
|
||||||
|
This file is the source map between `live2dcubismcore.d.ts` and `live2dcubismcore.js`.
|
||||||
|
Use this file when debugging.
|
||||||
|
|
||||||
|
### live2dcubismcore.min.js
|
||||||
|
|
||||||
|
This file is the minified version of `live2dcubismcore.js`.
|
||||||
|
Use this file in production.
|
||||||
6
avatar-h5-renderer/public/Core/RedistributableFiles.txt
Normal file
6
avatar-h5-renderer/public/Core/RedistributableFiles.txt
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
The following is a list of files available for redistribution
|
||||||
|
under the terms of the Live2D Proprietary Software License Agreement:
|
||||||
|
|
||||||
|
- live2dcubismcore.d.ts
|
||||||
|
- live2dcubismcore.js
|
||||||
|
- live2dcubismcore.min.js
|
||||||
463
avatar-h5-renderer/public/Core/live2dcubismcore.d.ts
vendored
Normal file
463
avatar-h5-renderer/public/Core/live2dcubismcore.d.ts
vendored
Normal file
@ -0,0 +1,463 @@
|
|||||||
|
/**
|
||||||
|
* Copyright(c) Live2D Inc. All rights reserved.
|
||||||
|
*
|
||||||
|
* Use of this source code is governed by the Live2D Proprietary Software license
|
||||||
|
* that can be found at https://www.live2d.com/eula/live2d-proprietary-software-license-agreement_en.html.
|
||||||
|
*/
|
||||||
|
declare namespace Live2DCubismCore {
|
||||||
|
/** Cubism version identifier. */
|
||||||
|
type csmVersion = number;
|
||||||
|
/** moc3 version identifier. */
|
||||||
|
type csmMocVersion = number;
|
||||||
|
/** Parameter type identifier. */
|
||||||
|
type csmParameterType = number;
|
||||||
|
/** Necessary alignment for mocs (in bytes). */
|
||||||
|
const AlignofMoc: number;
|
||||||
|
/** Necessary alignment for models (in bytes). */
|
||||||
|
const AlignofModel: number;
|
||||||
|
/** .moc3 file version Unknown */
|
||||||
|
const MocVersion_Unknown: number;
|
||||||
|
/** .moc3 file version 3.0.00 - 3.2.07 */
|
||||||
|
const MocVersion_30: number;
|
||||||
|
/** .moc3 file version 3.3.00 - 3.3.03 */
|
||||||
|
const MocVersion_33: number;
|
||||||
|
/** .moc3 file version 4.0.00 - 4.1.05 */
|
||||||
|
const MocVersion_40: number;
|
||||||
|
/** .moc3 file version 4.2.00 - 4.2.04 */
|
||||||
|
const MocVersion_42: number;
|
||||||
|
/** .moc3 file version 5.0.00 - 5.2.03 */
|
||||||
|
const MocVersion_50: number;
|
||||||
|
/** .moc3 file version 5.3.00 - */
|
||||||
|
const MocVersion_53: number;
|
||||||
|
/** Normal Parameter. */
|
||||||
|
const ParameterType_Normal: number;
|
||||||
|
/** Parameter for blend shape. */
|
||||||
|
const ParameterType_BlendShape: number;
|
||||||
|
/** Normal blend. */
|
||||||
|
const ColorBlendType_Normal: number;
|
||||||
|
/** Add blend. */
|
||||||
|
const ColorBlendType_Add: number;
|
||||||
|
/** AddGlow blend. */
|
||||||
|
const ColorBlendType_AddGlow: number;
|
||||||
|
/** Darken blend. */
|
||||||
|
const ColorBlendType_Darken: number;
|
||||||
|
/** Multiply blend. */
|
||||||
|
const ColorBlendType_Multiply: number;
|
||||||
|
/** ColorBurn blend. */
|
||||||
|
const ColorBlendType_ColorBurn: number;
|
||||||
|
/** LinearBurn blend. */
|
||||||
|
const ColorBlendType_LinearBurn: number;
|
||||||
|
/** Lighten blend. */
|
||||||
|
const ColorBlendType_Lighten: number;
|
||||||
|
/** Screen blend. */
|
||||||
|
const ColorBlendType_Screen: number;
|
||||||
|
/** ColorDodge blend. */
|
||||||
|
const ColorBlendType_ColorDodge: number;
|
||||||
|
/** Overlay blend. */
|
||||||
|
const ColorBlendType_Overlay: number;
|
||||||
|
/** SoftLight blend. */
|
||||||
|
const ColorBlendType_SoftLight: number;
|
||||||
|
/** HardLight blend. */
|
||||||
|
const ColorBlendType_HardLight: number;
|
||||||
|
/** LinearLight blend. */
|
||||||
|
const ColorBlendType_LinearLight: number;
|
||||||
|
/** Hue blend. */
|
||||||
|
const ColorBlendType_Hue: number;
|
||||||
|
/** Color blend. */
|
||||||
|
const ColorBlendType_Color: number;
|
||||||
|
/** Add compatible blend. */
|
||||||
|
const ColorBlendType_AddCompatible: number;
|
||||||
|
/** Multiply compatible blend. */
|
||||||
|
const ColorBlendType_MultiplyCompatible: number;
|
||||||
|
/** Over blend. */
|
||||||
|
const AlphaBlendType_Over: number;
|
||||||
|
/** Atop blend. */
|
||||||
|
const AlphaBlendType_Atop: number;
|
||||||
|
/** Out blend. */
|
||||||
|
const AlphaBlendType_Out: number;
|
||||||
|
/** ConjointOver blend. */
|
||||||
|
const AlphaBlendType_ConjointOver: number;
|
||||||
|
/** DisjointOver blend. */
|
||||||
|
const AlphaBlendType_DisjointOver: number;
|
||||||
|
/** Log handler.
|
||||||
|
*
|
||||||
|
* @param message Null-terminated string message to log.
|
||||||
|
*/
|
||||||
|
interface csmLogFunction {
|
||||||
|
(message: string): void;
|
||||||
|
}
|
||||||
|
/** Cubism version. */
|
||||||
|
class Version {
|
||||||
|
/**
|
||||||
|
* Queries Core version.
|
||||||
|
*
|
||||||
|
* @return Core version.
|
||||||
|
*/
|
||||||
|
static csmGetVersion(): csmVersion;
|
||||||
|
/**
|
||||||
|
* Gets Moc file supported latest version.
|
||||||
|
*
|
||||||
|
* @return Moc file latest format version.
|
||||||
|
*/
|
||||||
|
static csmGetLatestMocVersion(): csmMocVersion;
|
||||||
|
/**
|
||||||
|
* Gets Moc file's format version
|
||||||
|
*
|
||||||
|
* @param {Moc} moc Moc
|
||||||
|
* @param {ArrayBuffer} mocBytes Arraybuffer
|
||||||
|
* @returns {csmMocVersion} Moc file format version.
|
||||||
|
*/
|
||||||
|
static csmGetMocVersion(moc: Moc, mocBytes: ArrayBuffer): csmMocVersion;
|
||||||
|
/**
|
||||||
|
* Gets Moc file's format version
|
||||||
|
*
|
||||||
|
* @param {ArrayBuffer} mocBytes Moc bytes.
|
||||||
|
* @returns {csmMocVersion} Moc file format version.
|
||||||
|
*/
|
||||||
|
static csmGetMocVersion(mocBytes: ArrayBuffer): csmMocVersion;
|
||||||
|
private constructor();
|
||||||
|
}
|
||||||
|
/** Cubism logging. */
|
||||||
|
class Logging {
|
||||||
|
private static logFunction;
|
||||||
|
/**
|
||||||
|
* Sets log handler.
|
||||||
|
*
|
||||||
|
* @param handler Handler to use.
|
||||||
|
*/
|
||||||
|
static csmSetLogFunction(handler: csmLogFunction): void;
|
||||||
|
/**
|
||||||
|
* Queries log handler.
|
||||||
|
*
|
||||||
|
* @return Log handler.
|
||||||
|
*/
|
||||||
|
static csmGetLogFunction(): csmLogFunction;
|
||||||
|
/**
|
||||||
|
* Wrap log function.
|
||||||
|
*
|
||||||
|
* @param messagePtr number
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
private static wrapLogFunction;
|
||||||
|
private constructor();
|
||||||
|
}
|
||||||
|
/** Cubism moc. */
|
||||||
|
class Moc {
|
||||||
|
/**
|
||||||
|
* Checks consistency of a moc.
|
||||||
|
*
|
||||||
|
* @param mocBytes Moc bytes.
|
||||||
|
*
|
||||||
|
* @returns '1' if Moc is valid; '0' otherwise.
|
||||||
|
*/
|
||||||
|
hasMocConsistency(mocBytes: ArrayBuffer): number;
|
||||||
|
/** Creates [[Moc]] from [[ArrayBuffer]].
|
||||||
|
*
|
||||||
|
* @param buffer Array buffer
|
||||||
|
*
|
||||||
|
* @return [[Moc]] on success; [[null]] otherwise.
|
||||||
|
*/
|
||||||
|
static fromArrayBuffer(buffer: ArrayBuffer): Moc;
|
||||||
|
/** Releases instance. */
|
||||||
|
_release(): void;
|
||||||
|
/** Native moc. */
|
||||||
|
_ptr: number;
|
||||||
|
/**
|
||||||
|
* Initializes instance.
|
||||||
|
*
|
||||||
|
* @param mocBytes Moc bytes.
|
||||||
|
*/
|
||||||
|
private constructor();
|
||||||
|
}
|
||||||
|
/** Cubism model. */
|
||||||
|
class Model {
|
||||||
|
/** Parameters. */
|
||||||
|
parameters: Parameters;
|
||||||
|
/** Parts. */
|
||||||
|
parts: Parts;
|
||||||
|
/** Drawables. */
|
||||||
|
drawables: Drawables;
|
||||||
|
/** Offscreen. */
|
||||||
|
offscreens: Offscreens;
|
||||||
|
/** Canvas information. */
|
||||||
|
canvasinfo: CanvasInfo;
|
||||||
|
/** Object render orders. */
|
||||||
|
private renderOrders;
|
||||||
|
/**
|
||||||
|
* Creates [[Model]] from [[Moc]].
|
||||||
|
*
|
||||||
|
* @param moc Moc
|
||||||
|
*
|
||||||
|
* @return [[Model]] on success; [[null]] otherwise.
|
||||||
|
*/
|
||||||
|
static fromMoc(moc: Moc): Model;
|
||||||
|
/**
|
||||||
|
* Gets object render orders.
|
||||||
|
*
|
||||||
|
* @returns {Int32Array} Object render orders.
|
||||||
|
*/
|
||||||
|
getRenderOrders(): Int32Array;
|
||||||
|
/** Updates instance. */
|
||||||
|
update(): void;
|
||||||
|
/** Releases instance. */
|
||||||
|
release(): void;
|
||||||
|
/** Native model. */
|
||||||
|
_ptr: number;
|
||||||
|
/**
|
||||||
|
* Initializes instance.
|
||||||
|
*
|
||||||
|
* @param moc Moc
|
||||||
|
*/
|
||||||
|
private constructor();
|
||||||
|
}
|
||||||
|
/** Canvas information interface. */
|
||||||
|
class CanvasInfo {
|
||||||
|
/** Width of native model canvas. */
|
||||||
|
CanvasWidth: number;
|
||||||
|
/** Height of native model canvas. */
|
||||||
|
CanvasHeight: number;
|
||||||
|
/** Coordinate origin of X axis. */
|
||||||
|
CanvasOriginX: number;
|
||||||
|
/** Coordinate origin of Y axis. */
|
||||||
|
CanvasOriginY: number;
|
||||||
|
/** Pixels per unit of native model. */
|
||||||
|
PixelsPerUnit: number;
|
||||||
|
/**
|
||||||
|
* Initializes instance.
|
||||||
|
*
|
||||||
|
* @param modelPtr Native model pointer.
|
||||||
|
*/
|
||||||
|
constructor(modelPtr: number);
|
||||||
|
}
|
||||||
|
/** Cubism model parameters */
|
||||||
|
class Parameters {
|
||||||
|
/** Parameter count. */
|
||||||
|
count: number;
|
||||||
|
/** Parameter IDs. */
|
||||||
|
ids: Array<string>;
|
||||||
|
/** Minimum parameter values. */
|
||||||
|
minimumValues: Float32Array;
|
||||||
|
/** Parameter types. */
|
||||||
|
types: Int32Array;
|
||||||
|
/** Maximum parameter values. */
|
||||||
|
maximumValues: Float32Array;
|
||||||
|
/** Default parameter values. */
|
||||||
|
defaultValues: Float32Array;
|
||||||
|
/** Parameter values. */
|
||||||
|
values: Float32Array;
|
||||||
|
/** Parameter Repeat informations. */
|
||||||
|
repeats: Int32Array;
|
||||||
|
/** Number of key values of each parameter. */
|
||||||
|
keyCounts: Int32Array;
|
||||||
|
/** Key values of each parameter. */
|
||||||
|
keyValues: Array<Float32Array>;
|
||||||
|
/**
|
||||||
|
* Initializes instance.
|
||||||
|
*
|
||||||
|
* @param modelPtr Native model.
|
||||||
|
*/
|
||||||
|
constructor(modelPtr: number);
|
||||||
|
}
|
||||||
|
/** Cubism model parts */
|
||||||
|
class Parts {
|
||||||
|
/** Part count. */
|
||||||
|
count: number;
|
||||||
|
/** Part IDs. */
|
||||||
|
ids: Array<string>;
|
||||||
|
/** Opacity values. */
|
||||||
|
opacities: Float32Array;
|
||||||
|
/** Part's parent part indices. */
|
||||||
|
parentIndices: Int32Array;
|
||||||
|
/** Part's offscreen indices. If the part does not use an offscreen, the value is '-1'. */
|
||||||
|
offscreenIndices: Int32Array;
|
||||||
|
/**
|
||||||
|
* Initializes instance.
|
||||||
|
*
|
||||||
|
* @param modelPtr Native model.
|
||||||
|
*/
|
||||||
|
constructor(modelPtr: number);
|
||||||
|
}
|
||||||
|
/** Cubism model drawables */
|
||||||
|
class Drawables {
|
||||||
|
/** Drawable count. */
|
||||||
|
count: number;
|
||||||
|
/** Drawable IDs. */
|
||||||
|
ids: Array<string>;
|
||||||
|
/** Constant drawable flags. */
|
||||||
|
constantFlags: Uint8Array;
|
||||||
|
/** Dynamic drawable flags. */
|
||||||
|
dynamicFlags: Uint8Array;
|
||||||
|
/** Drawable texture indices. */
|
||||||
|
textureIndices: Int32Array;
|
||||||
|
/** Drawable draw orders. */
|
||||||
|
drawOrders: Int32Array;
|
||||||
|
/** Drawable opacities. */
|
||||||
|
opacities: Float32Array;
|
||||||
|
/** Mask count for each drawable. */
|
||||||
|
maskCounts: Int32Array;
|
||||||
|
/** Masks for each drawable. */
|
||||||
|
masks: Array<Int32Array>;
|
||||||
|
/** Number of vertices of each drawable. */
|
||||||
|
vertexCounts: Int32Array;
|
||||||
|
/** 2D vertex position data of each drawable. */
|
||||||
|
vertexPositions: Array<Float32Array>;
|
||||||
|
/** 2D texture coordinate data of each drawables. */
|
||||||
|
vertexUvs: Array<Float32Array>;
|
||||||
|
/** Number of triangle indices for each drawable. */
|
||||||
|
indexCounts: Int32Array;
|
||||||
|
/** Triangle index data for each drawable. */
|
||||||
|
indices: Array<Uint16Array>;
|
||||||
|
/** Information multiply color. */
|
||||||
|
multiplyColors: Float32Array;
|
||||||
|
/** Information Screen color. */
|
||||||
|
screenColors: Float32Array;
|
||||||
|
/** Indices of drawables parent part. */
|
||||||
|
parentPartIndices: Int32Array;
|
||||||
|
/** Blend modes of drawables. */
|
||||||
|
blendModes: Int32Array;
|
||||||
|
/** Resets all dynamic drawable flags.. */
|
||||||
|
resetDynamicFlags(): void;
|
||||||
|
/** Native model. */
|
||||||
|
private _modelPtr;
|
||||||
|
/**
|
||||||
|
* Initializes instance.
|
||||||
|
*
|
||||||
|
* @param modelPtr Native model.
|
||||||
|
*/
|
||||||
|
constructor(modelPtr: number);
|
||||||
|
}
|
||||||
|
/** Cubism model offscreens */
|
||||||
|
class Offscreens {
|
||||||
|
/** Number of offscreens. */
|
||||||
|
count: number;
|
||||||
|
/** Offscreen blend modes. */
|
||||||
|
blendModes: Int32Array;
|
||||||
|
/** Offscreen opacities. */
|
||||||
|
opacities: Float32Array;
|
||||||
|
/** Offscreen's owner indices. */
|
||||||
|
ownerIndices: Int32Array;
|
||||||
|
/** Offscreen's multiply colors. */
|
||||||
|
multiplyColors: Float32Array;
|
||||||
|
/** Offscreen's screen colors. */
|
||||||
|
screenColors: Float32Array;
|
||||||
|
/** Offscreen's mask counts. */
|
||||||
|
maskCounts: Int32Array;
|
||||||
|
/** Offscreen's masks. */
|
||||||
|
masks: Array<Int32Array>;
|
||||||
|
/** Offscreen's constant flags. */
|
||||||
|
constantFlags: Uint8Array;
|
||||||
|
/**
|
||||||
|
* Initializes instance.
|
||||||
|
*
|
||||||
|
* @param modelPtr Native model.
|
||||||
|
*/
|
||||||
|
constructor(modelPtr: number);
|
||||||
|
}
|
||||||
|
/** Utility functions. */
|
||||||
|
class Utils {
|
||||||
|
/**
|
||||||
|
* Checks whether flag is set in bitfield.
|
||||||
|
*
|
||||||
|
* @param bitfield Bitfield to query against.
|
||||||
|
*
|
||||||
|
* @return [[true]] if bit set; [[false]] otherwise
|
||||||
|
*/
|
||||||
|
static hasBlendAdditiveBit(bitfield: number): boolean;
|
||||||
|
/**
|
||||||
|
* Checks whether flag is set in bitfield.
|
||||||
|
*
|
||||||
|
* @param bitfield Bitfield to query against.
|
||||||
|
*
|
||||||
|
* @return [[true]] if bit set; [[false]] otherwise
|
||||||
|
*/
|
||||||
|
static hasBlendMultiplicativeBit(bitfield: number): boolean;
|
||||||
|
/**
|
||||||
|
* Checks whether flag is set in bitfield.
|
||||||
|
*
|
||||||
|
* @param bitfield Bitfield to query against.
|
||||||
|
*
|
||||||
|
* @return [[true]] if bit set; [[false]] otherwise
|
||||||
|
*/
|
||||||
|
static hasIsDoubleSidedBit(bitfield: number): boolean;
|
||||||
|
/**
|
||||||
|
* Checks whether flag is set in bitfield.
|
||||||
|
*
|
||||||
|
* @param bitfield Bitfield to query against.
|
||||||
|
*
|
||||||
|
* @return [[true]] if bit set; [[false]] otherwise
|
||||||
|
*/
|
||||||
|
static hasIsInvertedMaskBit(bitfield: number): boolean;
|
||||||
|
/**
|
||||||
|
* Checks whether flag is set in bitfield.
|
||||||
|
*
|
||||||
|
* @param bitfield Bitfield to query against.
|
||||||
|
*
|
||||||
|
* @return [[true]] if bit set; [[false]] otherwise
|
||||||
|
*/
|
||||||
|
static hasIsVisibleBit(bitfield: number): boolean;
|
||||||
|
/**
|
||||||
|
* Checks whether flag is set in bitfield.
|
||||||
|
*
|
||||||
|
* @param bitfield Bitfield to query against.
|
||||||
|
*
|
||||||
|
* @return [[true]] if bit set; [[false]] otherwise
|
||||||
|
*/
|
||||||
|
static hasVisibilityDidChangeBit(bitfield: number): boolean;
|
||||||
|
/**
|
||||||
|
* Checks whether flag is set in bitfield.
|
||||||
|
*
|
||||||
|
* @param bitfield Bitfield to query against.
|
||||||
|
*
|
||||||
|
* @return [[true]] if bit set; [[false]] otherwise
|
||||||
|
*/
|
||||||
|
static hasOpacityDidChangeBit(bitfield: number): boolean;
|
||||||
|
/**
|
||||||
|
* Checks whether flag is set in bitfield.
|
||||||
|
*
|
||||||
|
* @param bitfield Bitfield to query against.
|
||||||
|
*
|
||||||
|
* @return [[true]] if bit set; [[false]] otherwise
|
||||||
|
*/
|
||||||
|
static hasDrawOrderDidChangeBit(bitfield: number): boolean;
|
||||||
|
/**
|
||||||
|
* Checks whether flag is set in bitfield.
|
||||||
|
*
|
||||||
|
* @param bitfield Bitfield to query against.
|
||||||
|
*
|
||||||
|
* @return [[true]] if bit set; [[false]] otherwise
|
||||||
|
*/
|
||||||
|
static hasRenderOrderDidChangeBit(bitfield: number): boolean;
|
||||||
|
/**
|
||||||
|
* Checks whether flag is set in bitfield.
|
||||||
|
*
|
||||||
|
* @param bitfield Bitfield to query against.
|
||||||
|
*
|
||||||
|
* @return [[true]] if bit set; [[false]] otherwise
|
||||||
|
*/
|
||||||
|
static hasVertexPositionsDidChangeBit(bitfield: number): boolean;
|
||||||
|
/**
|
||||||
|
* Checks whether flag is set in bitfield.
|
||||||
|
*
|
||||||
|
* @param bitfield Bitfield to query against.
|
||||||
|
*
|
||||||
|
* @return [[true]] if bit set; [[false]] otherwise
|
||||||
|
*/
|
||||||
|
static hasBlendColorDidChangeBit(bitfield: number): boolean;
|
||||||
|
}
|
||||||
|
/** Memory functions. */
|
||||||
|
class Memory {
|
||||||
|
/**
|
||||||
|
* HACK:
|
||||||
|
* Extend memory size allocated during module initialization.
|
||||||
|
* If the specified size is less than or equal to 16777216(byte), the default of 16 MB is allocated.
|
||||||
|
*
|
||||||
|
* @see https://github.com/emscripten-core/emscripten/blob/main/src/settings.js#L161
|
||||||
|
*
|
||||||
|
* @param size allocated memory size [byte(s)]
|
||||||
|
*/
|
||||||
|
static initializeAmountOfMemory(size: number): void;
|
||||||
|
private constructor();
|
||||||
|
}
|
||||||
|
/** Emscripten Cubism Core module. */
|
||||||
|
}
|
||||||
830
avatar-h5-renderer/public/Core/live2dcubismcore.js
Normal file
830
avatar-h5-renderer/public/Core/live2dcubismcore.js
Normal file
File diff suppressed because one or more lines are too long
1
avatar-h5-renderer/public/Core/live2dcubismcore.js.map
Normal file
1
avatar-h5-renderer/public/Core/live2dcubismcore.js.map
Normal file
File diff suppressed because one or more lines are too long
9
avatar-h5-renderer/public/Core/live2dcubismcore.min.js
vendored
Normal file
9
avatar-h5-renderer/public/Core/live2dcubismcore.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
@ -0,0 +1,54 @@
|
|||||||
|
/**
|
||||||
|
* Copyright(c) Live2D Inc. All rights reserved.
|
||||||
|
*
|
||||||
|
* Use of this source code is governed by the Live2D Open Software license
|
||||||
|
* that can be found at https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
vec4 OverlapRgba(vec3 color, vec3 colorSource, vec3 colorDestination, vec3 parameter)
|
||||||
|
{
|
||||||
|
vec3 rgb = color * parameter.x + colorSource * parameter.y + colorDestination * parameter.z;
|
||||||
|
float alpha = parameter.x + parameter.y + parameter.z;
|
||||||
|
return vec4(rgb, alpha);
|
||||||
|
}
|
||||||
|
|
||||||
|
#if defined(ALPHA_BLEND_OVER)
|
||||||
|
vec4 AlphaBlend(vec3 color, vec4 colorSource, vec4 colorDestination)
|
||||||
|
{
|
||||||
|
vec3 parameter = vec3(colorSource.a * colorDestination.a, colorSource.a * (1.0 - colorDestination.a), colorDestination.a * (1.0 - colorSource.a));
|
||||||
|
return OverlapRgba(color, colorSource.rgb, colorDestination.rgb, parameter);
|
||||||
|
}
|
||||||
|
|
||||||
|
#elif defined(ALPHA_BLEND_ATOP)
|
||||||
|
vec4 AlphaBlend(vec3 color, vec4 colorSource, vec4 colorDestination)
|
||||||
|
{
|
||||||
|
vec3 parameter = vec3(colorSource.a * colorDestination.a, 0, colorDestination.a * (1.0 - colorSource.a));
|
||||||
|
return OverlapRgba(color, colorSource.rgb, colorDestination.rgb, parameter);
|
||||||
|
}
|
||||||
|
|
||||||
|
#elif defined(ALPHA_BLEND_OUT)
|
||||||
|
vec4 AlphaBlend(vec3 color, vec4 colorSource, vec4 colorDestination)
|
||||||
|
{
|
||||||
|
vec3 parameter = vec3(0.0, 0.0, colorDestination.a * (1.0 - colorSource.a));
|
||||||
|
return OverlapRgba(color, colorSource.rgb, colorDestination.rgb, parameter);
|
||||||
|
}
|
||||||
|
|
||||||
|
#elif defined(ALPHA_BLEND_CONJOINTOVER)
|
||||||
|
vec4 AlphaBlend(vec3 color, vec4 colorSource, vec4 colorDestination)
|
||||||
|
{
|
||||||
|
vec3 parameter = vec3(min(colorSource.a, colorDestination.a), max(colorSource.a - colorDestination.a, 0.0), max(colorDestination.a - colorSource.a, 0.0));
|
||||||
|
return OverlapRgba(color, colorSource.rgb, colorDestination.rgb, parameter);
|
||||||
|
}
|
||||||
|
|
||||||
|
#elif defined(ALPHA_BLEND_DISJOINTOVER)
|
||||||
|
vec4 AlphaBlend(vec3 color, vec4 colorSource, vec4 colorDestination)
|
||||||
|
{
|
||||||
|
vec3 parameter = vec3(max(colorSource.a + colorDestination.a - 1.0, 0.0), min(colorSource.a, 1.0 - colorDestination.a), min(colorDestination.a, 1.0 - colorSource.a));
|
||||||
|
return OverlapRgba(color, colorSource.rgb, colorDestination.rgb, parameter);
|
||||||
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
#error not supported alpha blend function
|
||||||
|
|
||||||
|
#endif
|
||||||
@ -0,0 +1,298 @@
|
|||||||
|
/**
|
||||||
|
* Copyright(c) Live2D Inc. All rights reserved.
|
||||||
|
*
|
||||||
|
* Use of this source code is governed by the Live2D Open Software license
|
||||||
|
* that can be found at https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#if defined(COLOR_BLEND_NORMAL)
|
||||||
|
vec3 ColorBlend(vec3 colorSource, vec3 colorDestination)
|
||||||
|
{
|
||||||
|
return colorSource;
|
||||||
|
}
|
||||||
|
|
||||||
|
#elif defined(COLOR_BLEND_ADDCOMPATIBLE)
|
||||||
|
vec3 ColorBlend(vec3 colorSource, vec3 colorDestination)
|
||||||
|
{
|
||||||
|
return vec3(0.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#elif defined(COLOR_BLEND_MULTCOMPATIBLE)
|
||||||
|
vec3 ColorBlend(vec3 colorSource, vec3 colorDestination)
|
||||||
|
{
|
||||||
|
return vec3(0.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#elif defined(COLOR_BLEND_ADD)
|
||||||
|
vec3 ColorBlend(vec3 colorSource, vec3 colorDestination)
|
||||||
|
{
|
||||||
|
return min(colorSource + colorDestination, 1.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#elif defined(COLOR_BLEND_ADDGLOW)
|
||||||
|
vec3 ColorBlend(vec3 colorSource, vec3 colorDestination)
|
||||||
|
{
|
||||||
|
return colorSource + colorDestination;
|
||||||
|
}
|
||||||
|
|
||||||
|
#elif defined(COLOR_BLEND_DARKEN)
|
||||||
|
vec3 ColorBlend(vec3 colorSource, vec3 colorDestination)
|
||||||
|
{
|
||||||
|
return min(colorSource, colorDestination);
|
||||||
|
}
|
||||||
|
|
||||||
|
#elif defined(COLOR_BLEND_MULTIPLY)
|
||||||
|
vec3 ColorBlend(vec3 colorSource, vec3 colorDestination)
|
||||||
|
{
|
||||||
|
return colorSource * colorDestination;
|
||||||
|
}
|
||||||
|
|
||||||
|
#elif defined(COLOR_BLEND_COLORBURN)
|
||||||
|
float ColorBurn(float colorSource, float colorDestination)
|
||||||
|
{
|
||||||
|
if (abs(colorDestination - 1.0) < 0.000001)
|
||||||
|
{
|
||||||
|
return 1.0;
|
||||||
|
}
|
||||||
|
else if (abs(colorSource) < 0.000001)
|
||||||
|
{
|
||||||
|
return 0.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1.0 - min(1.0, (1.0 - colorDestination) / colorSource);
|
||||||
|
}
|
||||||
|
|
||||||
|
vec3 ColorBlend(vec3 colorSource, vec3 colorDestination)
|
||||||
|
{
|
||||||
|
return vec3(
|
||||||
|
ColorBurn(colorSource.r, colorDestination.r),
|
||||||
|
ColorBurn(colorSource.g, colorDestination.g),
|
||||||
|
ColorBurn(colorSource.b, colorDestination.b)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#elif defined(COLOR_BLEND_LINEARBURN)
|
||||||
|
vec3 ColorBlend(vec3 colorSource, vec3 colorDestination)
|
||||||
|
{
|
||||||
|
return max(vec3(0.0), colorSource + colorDestination - 1.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#elif defined(COLOR_BLEND_LIGHTEN)
|
||||||
|
vec3 ColorBlend(vec3 colorSource, vec3 colorDestination)
|
||||||
|
{
|
||||||
|
return max(colorSource, colorDestination);
|
||||||
|
}
|
||||||
|
|
||||||
|
#elif defined(COLOR_BLEND_SCREEN)
|
||||||
|
vec3 ColorBlend(vec3 colorSource, vec3 colorDestination)
|
||||||
|
{
|
||||||
|
return colorSource + colorDestination - colorSource * colorDestination;
|
||||||
|
}
|
||||||
|
|
||||||
|
#elif defined(COLOR_BLEND_COLORDODGE)
|
||||||
|
float ColorDodge(float colorSource, float colorDestination)
|
||||||
|
{
|
||||||
|
if (colorDestination <= 0.0)
|
||||||
|
{
|
||||||
|
return 0.0;
|
||||||
|
}
|
||||||
|
else if (colorSource == 1.0)
|
||||||
|
{
|
||||||
|
return 1.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return min(1.0, colorDestination / (1.0 - colorSource));
|
||||||
|
}
|
||||||
|
|
||||||
|
vec3 ColorBlend(vec3 colorSource, vec3 colorDestination)
|
||||||
|
{
|
||||||
|
return vec3(
|
||||||
|
ColorDodge(colorSource.r, colorDestination.r),
|
||||||
|
ColorDodge(colorSource.g, colorDestination.g),
|
||||||
|
ColorDodge(colorSource.b, colorDestination.b)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#elif defined(COLOR_BLEND_OVERLAY)
|
||||||
|
float Overlay(float colorSource, float colorDestination)
|
||||||
|
{
|
||||||
|
float mul = 2.0 * colorSource * colorDestination;
|
||||||
|
float scr = 1.0 - 2.0 * (1.0 - colorSource) * (1.0 - colorDestination) ;
|
||||||
|
return colorDestination < 0.5 ? mul : scr ;
|
||||||
|
}
|
||||||
|
|
||||||
|
vec3 ColorBlend(vec3 colorSource, vec3 colorDestination)
|
||||||
|
{
|
||||||
|
return vec3(
|
||||||
|
Overlay(colorSource.r, colorDestination.r),
|
||||||
|
Overlay(colorSource.g, colorDestination.g),
|
||||||
|
Overlay(colorSource.b, colorDestination.b)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#elif defined(COLOR_BLEND_SOFTLIGHT)
|
||||||
|
float SoftLight(float colorSource, float colorDestination)
|
||||||
|
{
|
||||||
|
float val1 = colorDestination - (1.0 - 2.0 * colorSource) * colorDestination * (1.0 - colorDestination);
|
||||||
|
float val2 = colorDestination + (2.0 * colorSource - 1.0) * colorDestination * ((16.0 * colorDestination - 12.0) * colorDestination + 3.0);
|
||||||
|
float val3 = colorDestination + (2.0 * colorSource - 1.0) * (sqrt(colorDestination) - colorDestination);
|
||||||
|
|
||||||
|
if (colorSource <= 0.5)
|
||||||
|
{
|
||||||
|
return val1;
|
||||||
|
}
|
||||||
|
else if (colorDestination <= 0.25)
|
||||||
|
{
|
||||||
|
return val2;
|
||||||
|
}
|
||||||
|
|
||||||
|
return val3;
|
||||||
|
}
|
||||||
|
|
||||||
|
vec3 ColorBlend(vec3 colorSource, vec3 colorDestination)
|
||||||
|
{
|
||||||
|
return vec3(
|
||||||
|
SoftLight(colorSource.r, colorDestination.r),
|
||||||
|
SoftLight(colorSource.g, colorDestination.g),
|
||||||
|
SoftLight(colorSource.b, colorDestination.b)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#elif defined(COLOR_BLEND_HARDLIGHT)
|
||||||
|
float HardLight(float colorSource, float colorDestination)
|
||||||
|
{
|
||||||
|
float mul = 2.0 * colorSource * colorDestination;
|
||||||
|
float scr = 1.0 - 2.0 * (1.0 - colorSource) * (1.0 - colorDestination);
|
||||||
|
|
||||||
|
if (colorSource < 0.5)
|
||||||
|
{
|
||||||
|
return mul;
|
||||||
|
}
|
||||||
|
|
||||||
|
return scr;
|
||||||
|
}
|
||||||
|
|
||||||
|
vec3 ColorBlend(vec3 colorSource, vec3 colorDestination)
|
||||||
|
{
|
||||||
|
return vec3(
|
||||||
|
HardLight(colorSource.r, colorDestination.r),
|
||||||
|
HardLight(colorSource.g, colorDestination.g),
|
||||||
|
HardLight(colorSource.b, colorDestination.b)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#elif defined(COLOR_BLEND_LINEARLIGHT)
|
||||||
|
float LinearLight(float colorSource, float colorDestination)
|
||||||
|
{
|
||||||
|
float burn = max(0.0, 2.0 * colorSource + colorDestination - 1.0);
|
||||||
|
float dodge = min(1.0, 2.0 * (colorSource - 0.5) + colorDestination);
|
||||||
|
|
||||||
|
if (colorSource < 0.5)
|
||||||
|
{
|
||||||
|
return burn;
|
||||||
|
}
|
||||||
|
|
||||||
|
return dodge;
|
||||||
|
}
|
||||||
|
|
||||||
|
vec3 ColorBlend(vec3 colorSource, vec3 colorDestination)
|
||||||
|
{
|
||||||
|
return vec3(
|
||||||
|
LinearLight(colorSource.r, colorDestination.r),
|
||||||
|
LinearLight(colorSource.g, colorDestination.g),
|
||||||
|
LinearLight(colorSource.b, colorDestination.b)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#elif defined(COLOR_BLEND_HUE) || defined(COLOR_BLEND_COLOR)
|
||||||
|
const float rCoeff = 0.30;
|
||||||
|
const float gCoeff = 0.59;
|
||||||
|
const float bCoeff = 0.11;
|
||||||
|
|
||||||
|
float GetMax(vec3 rgbC)
|
||||||
|
{
|
||||||
|
return max(rgbC.r, max(rgbC.g, rgbC.b));
|
||||||
|
}
|
||||||
|
|
||||||
|
float GetMin(vec3 rgbC)
|
||||||
|
{
|
||||||
|
return min(rgbC.r, min(rgbC.g, rgbC.b));
|
||||||
|
}
|
||||||
|
|
||||||
|
float GetRange(vec3 rgbC)
|
||||||
|
{
|
||||||
|
return max(rgbC.r, max(rgbC.g, rgbC.b)) - min(rgbC.r, min(rgbC.g, rgbC.b));
|
||||||
|
}
|
||||||
|
|
||||||
|
float Saturation(vec3 rgbC)
|
||||||
|
{
|
||||||
|
return GetRange(rgbC);
|
||||||
|
}
|
||||||
|
|
||||||
|
float Luma(vec3 rgbC)
|
||||||
|
{
|
||||||
|
return rCoeff * rgbC.r + gCoeff * rgbC.g + bCoeff * rgbC.b;
|
||||||
|
}
|
||||||
|
|
||||||
|
vec3 ClipColor(vec3 rgbC)
|
||||||
|
{
|
||||||
|
float luma = Luma(rgbC);
|
||||||
|
float maxv = GetMax(rgbC);
|
||||||
|
float minv = GetMin(rgbC);
|
||||||
|
vec3 outputColor = rgbC;
|
||||||
|
|
||||||
|
outputColor = minv < 0.0 ? luma + (outputColor - luma) * luma / (luma - minv) : outputColor;
|
||||||
|
outputColor = maxv > 1.0 ? luma + (outputColor - luma) * (1.0 - luma) / (maxv - luma) : outputColor;
|
||||||
|
|
||||||
|
return outputColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
vec3 SetLuma(vec3 rgbC, float luma)
|
||||||
|
{
|
||||||
|
return ClipColor(rgbC + (luma - Luma(rgbC)));
|
||||||
|
}
|
||||||
|
|
||||||
|
vec3 SetSaturation(vec3 rgbC, float saturation)
|
||||||
|
{
|
||||||
|
float maxv = GetMax(rgbC);
|
||||||
|
float minv = GetMin(rgbC);
|
||||||
|
float medv = rgbC.r + rgbC.g + rgbC.b - maxv - minv;
|
||||||
|
float outputMax, outputMed, outputMin;
|
||||||
|
|
||||||
|
outputMax = minv < maxv ? saturation : 0.0;
|
||||||
|
outputMed = minv < maxv ? (medv - minv) * saturation / (maxv - minv) : 0.0;
|
||||||
|
outputMin = 0.0;
|
||||||
|
|
||||||
|
if(rgbC.r == maxv)
|
||||||
|
{
|
||||||
|
return rgbC.b < rgbC.g ? vec3(outputMax, outputMed, outputMin) : vec3(outputMax, outputMin, outputMed);
|
||||||
|
}
|
||||||
|
else if(rgbC.g == maxv)
|
||||||
|
{
|
||||||
|
return rgbC.r < rgbC.b ? vec3(outputMin, outputMax, outputMed) : vec3(outputMed, outputMax, outputMin);
|
||||||
|
}
|
||||||
|
else // if(rgbC.b == maxv)
|
||||||
|
{
|
||||||
|
return rgbC.g < rgbC.r ? vec3(outputMed, outputMin, outputMax) : vec3(outputMin, outputMed, outputMax);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#if defined(COLOR_BLEND_HUE)
|
||||||
|
vec3 ColorBlend(vec3 colorSource, vec3 colorDestination)
|
||||||
|
{
|
||||||
|
return SetLuma(SetSaturation(colorSource, Saturation(colorDestination)), Luma(colorDestination));
|
||||||
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
vec3 ColorBlend(vec3 colorSource, vec3 colorDestination)
|
||||||
|
{
|
||||||
|
return SetLuma(colorSource, Luma(colorDestination)) ;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#else
|
||||||
|
#error not supported color blend function.
|
||||||
|
|
||||||
|
#endif
|
||||||
@ -0,0 +1,18 @@
|
|||||||
|
/**
|
||||||
|
* Copyright(c) Live2D Inc. All rights reserved.
|
||||||
|
*
|
||||||
|
* Use of this source code is governed by the Live2D Open Software license
|
||||||
|
* that can be found at https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
precision mediump float;
|
||||||
|
|
||||||
|
varying vec2 v_texCoord;
|
||||||
|
uniform vec4 u_baseColor;
|
||||||
|
uniform sampler2D s_texture0;
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
gl_FragColor = texture2D(s_texture0, v_texCoord) * u_baseColor;
|
||||||
|
}
|
||||||
@ -0,0 +1,29 @@
|
|||||||
|
/**
|
||||||
|
* Copyright(c) Live2D Inc. All rights reserved.
|
||||||
|
*
|
||||||
|
* Use of this source code is governed by the Live2D Open Software license
|
||||||
|
* that can be found at https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
precision mediump float;
|
||||||
|
varying vec2 v_texCoord; //v2f.texcoord
|
||||||
|
varying vec4 v_clipPos;
|
||||||
|
uniform sampler2D s_texture0; //_MainTex
|
||||||
|
uniform sampler2D s_texture1; // _ClippingMaskTex
|
||||||
|
uniform vec4 u_channelFlag;
|
||||||
|
uniform vec4 u_baseColor; //v2f.color
|
||||||
|
uniform vec4 u_multiplyColor;
|
||||||
|
uniform vec4 u_screenColor;
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
vec4 texColor = texture2D(s_texture0, v_texCoord);
|
||||||
|
texColor.rgb = texColor.rgb * u_multiplyColor.rgb;
|
||||||
|
texColor.rgb = (texColor.rgb + u_screenColor.rgb * texColor.a) - (texColor.rgb * u_screenColor.rgb);
|
||||||
|
vec4 col_formask = texColor * u_baseColor;
|
||||||
|
vec4 clipMask = (1.0 - texture2D(s_texture1, v_clipPos.xy / v_clipPos.w)) * u_channelFlag;
|
||||||
|
float maskVal = clipMask.r + clipMask.g + clipMask.b + clipMask.a;
|
||||||
|
col_formask = col_formask * (1.0 - maskVal);
|
||||||
|
gl_FragColor = col_formask;
|
||||||
|
}
|
||||||
@ -0,0 +1,29 @@
|
|||||||
|
/**
|
||||||
|
* Copyright(c) Live2D Inc. All rights reserved.
|
||||||
|
*
|
||||||
|
* Use of this source code is governed by the Live2D Open Software license
|
||||||
|
* that can be found at https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
precision mediump float;
|
||||||
|
varying vec2 v_texCoord; //v2f.texcoord
|
||||||
|
varying vec4 v_clipPos;
|
||||||
|
uniform vec4 u_baseColor; //v2f.color
|
||||||
|
uniform vec4 u_channelFlag;
|
||||||
|
uniform sampler2D s_texture0; //_MainTex
|
||||||
|
uniform sampler2D s_texture1; // _ClippingMaskTex
|
||||||
|
uniform vec4 u_multiplyColor;
|
||||||
|
uniform vec4 u_screenColor;
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
vec4 texColor = texture2D(s_texture0, v_texCoord);
|
||||||
|
texColor.rgb = texColor.rgb * u_multiplyColor.rgb;
|
||||||
|
texColor.rgb = (texColor.rgb + u_screenColor.rgb * texColor.a) - (texColor.rgb * u_screenColor.rgb);
|
||||||
|
vec4 col_formask = texColor * u_baseColor;
|
||||||
|
vec4 clipMask = (1.0 - texture2D(s_texture1, v_clipPos.xy / v_clipPos.w)) * u_channelFlag;
|
||||||
|
float maskVal = clipMask.r + clipMask.g + clipMask.b + clipMask.a;
|
||||||
|
col_formask = col_formask * maskVal;
|
||||||
|
gl_FragColor = col_formask;
|
||||||
|
}
|
||||||
@ -0,0 +1,23 @@
|
|||||||
|
/**
|
||||||
|
* Copyright(c) Live2D Inc. All rights reserved.
|
||||||
|
*
|
||||||
|
* Use of this source code is governed by the Live2D Open Software license
|
||||||
|
* that can be found at https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
precision mediump float;
|
||||||
|
varying vec2 v_texCoord; //v2f.texcoord
|
||||||
|
uniform vec4 u_baseColor; //v2f.color
|
||||||
|
uniform sampler2D s_texture0; //_MainTex
|
||||||
|
uniform vec4 u_multiplyColor;
|
||||||
|
uniform vec4 u_screenColor;
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
vec4 texColor = texture2D(s_texture0, v_texCoord);
|
||||||
|
texColor.rgb = texColor.rgb * u_multiplyColor.rgb;
|
||||||
|
texColor.rgb = (texColor.rgb + u_screenColor.rgb * texColor.a) - (texColor.rgb * u_screenColor.rgb);
|
||||||
|
vec4 color = texColor * u_baseColor;
|
||||||
|
gl_FragColor = vec4(color.rgb, color.a);
|
||||||
|
}
|
||||||
@ -0,0 +1,67 @@
|
|||||||
|
/**
|
||||||
|
* Copyright(c) Live2D Inc. All rights reserved.
|
||||||
|
*
|
||||||
|
* Use of this source code is governed by the Live2D Open Software license
|
||||||
|
* that can be found at https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
varying vec2 v_texCoord; //v2f.texcoord
|
||||||
|
varying vec2 v_blendCoord;
|
||||||
|
varying vec4 v_clipPos;
|
||||||
|
uniform sampler2D s_texture0; //_MainTex
|
||||||
|
uniform sampler2D s_blendTexture;
|
||||||
|
uniform vec4 u_baseColor; //v2f.color
|
||||||
|
uniform vec4 u_multiplyColor;
|
||||||
|
uniform vec4 u_screenColor;
|
||||||
|
uniform sampler2D s_texture1; // _ClippingMaskTex
|
||||||
|
uniform float u_invertClippingMask;
|
||||||
|
uniform vec4 u_channelFlag;
|
||||||
|
|
||||||
|
vec3 ColorBlend(vec3 colorSource, vec3 colorDestination);
|
||||||
|
vec4 AlphaBlend(vec3 C, vec3 Cs, float As, vec3 Cd, float Ad);
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
vec4 renderTextureColor = texture2D(s_blendTexture, v_blendCoord);
|
||||||
|
vec3 colorDestination = renderTextureColor.rgb;
|
||||||
|
float alphaDestination = renderTextureColor.a;
|
||||||
|
|
||||||
|
if (alphaDestination < 0.00001)
|
||||||
|
{
|
||||||
|
colorDestination = vec3(0.0, 0.0, 0.0);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
colorDestination /= alphaDestination;
|
||||||
|
}
|
||||||
|
|
||||||
|
vec4 texColor = texture2D(s_texture0, v_texCoord);
|
||||||
|
texColor.rgb *= u_multiplyColor.rgb;
|
||||||
|
texColor.rgb = (texColor.rgb + u_screenColor.rgb) - (texColor.rgb * u_screenColor.rgb);
|
||||||
|
|
||||||
|
texColor *= u_baseColor;
|
||||||
|
vec3 colorSource = texColor.rgb;
|
||||||
|
float alphaSource = texColor.a;
|
||||||
|
|
||||||
|
if (alphaSource < 0.00001)
|
||||||
|
{
|
||||||
|
colorSource = vec3(0.0, 0.0, 0.0);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
colorSource /= alphaSource;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef CLIPPING_MASK
|
||||||
|
float maskVal = 1.0;
|
||||||
|
vec4 clipMask = (1.0 - texture2D(s_texture1, v_clipPos.xy / v_clipPos.w)) * u_channelFlag;
|
||||||
|
maskVal = clipMask.r + clipMask.g + clipMask.b + clipMask.a;
|
||||||
|
maskVal = abs(u_invertClippingMask - maskVal);
|
||||||
|
|
||||||
|
alphaSource *= maskVal;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
vec4 source = vec4(colorSource.r, colorSource.g, colorSource.b, alphaSource);
|
||||||
|
vec4 destination = vec4(colorDestination.r, colorDestination.g, colorDestination.b, alphaDestination);
|
||||||
|
|
||||||
|
gl_FragColor = AlphaBlend(ColorBlend(colorSource, colorDestination), source, destination);
|
||||||
|
}
|
||||||
@ -0,0 +1,25 @@
|
|||||||
|
/**
|
||||||
|
* Copyright(c) Live2D Inc. All rights reserved.
|
||||||
|
*
|
||||||
|
* Use of this source code is governed by the Live2D Open Software license
|
||||||
|
* that can be found at https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
precision mediump float;
|
||||||
|
varying vec2 v_texCoord; //v2f.texcoord
|
||||||
|
varying vec4 v_myPos;
|
||||||
|
uniform vec4 u_baseColor; //v2f.color
|
||||||
|
uniform vec4 u_channelFlag;
|
||||||
|
uniform sampler2D s_texture0; //_MainTex
|
||||||
|
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
float isInside =
|
||||||
|
step(u_baseColor.x, v_myPos.x/v_myPos.w)
|
||||||
|
* step(u_baseColor.y, v_myPos.y/v_myPos.w)
|
||||||
|
* step(v_myPos.x/v_myPos.w, u_baseColor.z)
|
||||||
|
* step(v_myPos.y/v_myPos.w, u_baseColor.w);
|
||||||
|
gl_FragColor = u_channelFlag * texture2D(s_texture0, v_texCoord).a * isInside;
|
||||||
|
}
|
||||||
@ -0,0 +1,19 @@
|
|||||||
|
/**
|
||||||
|
* Copyright(c) Live2D Inc. All rights reserved.
|
||||||
|
*
|
||||||
|
* Use of this source code is governed by the Live2D Open Software license
|
||||||
|
* that can be found at https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
attribute vec4 a_position;
|
||||||
|
attribute vec2 a_texCoord;
|
||||||
|
varying vec2 v_texCoord;
|
||||||
|
uniform mat4 u_matrix;
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
gl_Position = u_matrix * a_position;
|
||||||
|
v_texCoord = a_texCoord;
|
||||||
|
v_texCoord.y = 1.0 - v_texCoord.y;
|
||||||
|
}
|
||||||
@ -0,0 +1,31 @@
|
|||||||
|
/**
|
||||||
|
* Copyright(c) Live2D Inc. All rights reserved.
|
||||||
|
*
|
||||||
|
* Use of this source code is governed by the Live2D Open Software license
|
||||||
|
* that can be found at https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
attribute vec4 a_position;
|
||||||
|
attribute vec2 a_texCoord;
|
||||||
|
varying vec2 v_texCoord;
|
||||||
|
varying vec2 v_blendCoord;
|
||||||
|
varying vec4 v_clipPos;
|
||||||
|
uniform mat4 u_matrix;
|
||||||
|
uniform mat4 u_clipMatrix;
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
gl_Position = u_matrix * a_position;
|
||||||
|
|
||||||
|
#ifdef CLIPPING_MASK
|
||||||
|
v_clipPos = u_clipMatrix * a_position;
|
||||||
|
#else
|
||||||
|
v_clipPos = vec4(0.0);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
v_texCoord = a_texCoord;
|
||||||
|
v_texCoord.y = 1.0 - v_texCoord.y;
|
||||||
|
vec2 ndcPos = gl_Position.xy / gl_Position.w;
|
||||||
|
v_blendCoord = ndcPos * 0.5 + 0.5;
|
||||||
|
}
|
||||||
@ -0,0 +1,17 @@
|
|||||||
|
/**
|
||||||
|
* Copyright(c) Live2D Inc. All rights reserved.
|
||||||
|
*
|
||||||
|
* Use of this source code is governed by the Live2D Open Software license
|
||||||
|
* that can be found at https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
attribute vec4 a_position;
|
||||||
|
attribute vec2 a_texCoord;
|
||||||
|
varying vec2 v_texCoord;
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
v_texCoord = a_texCoord;
|
||||||
|
gl_Position = a_position;
|
||||||
|
}
|
||||||
@ -0,0 +1,22 @@
|
|||||||
|
/**
|
||||||
|
* Copyright(c) Live2D Inc. All rights reserved.
|
||||||
|
*
|
||||||
|
* Use of this source code is governed by the Live2D Open Software license
|
||||||
|
* that can be found at https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
attribute vec4 a_position;
|
||||||
|
attribute vec2 a_texCoord;
|
||||||
|
varying vec2 v_texCoord;
|
||||||
|
varying vec4 v_clipPos;
|
||||||
|
uniform mat4 u_matrix;
|
||||||
|
uniform mat4 u_clipMatrix;
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
gl_Position = u_matrix * a_position;
|
||||||
|
v_clipPos = u_clipMatrix * a_position;
|
||||||
|
v_texCoord = a_texCoord;
|
||||||
|
v_texCoord.y = 1.0 - v_texCoord.y;
|
||||||
|
}
|
||||||
@ -0,0 +1,21 @@
|
|||||||
|
/**
|
||||||
|
* Copyright(c) Live2D Inc. All rights reserved.
|
||||||
|
*
|
||||||
|
* Use of this source code is governed by the Live2D Open Software license
|
||||||
|
* that can be found at https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
attribute vec4 a_position;
|
||||||
|
attribute vec2 a_texCoord;
|
||||||
|
varying vec2 v_texCoord;
|
||||||
|
varying vec4 v_myPos;
|
||||||
|
uniform mat4 u_clipMatrix;
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
gl_Position = u_clipMatrix * a_position;
|
||||||
|
v_myPos = u_clipMatrix * a_position;
|
||||||
|
v_texCoord = a_texCoord;
|
||||||
|
v_texCoord.y = 1.0 - v_texCoord.y;
|
||||||
|
}
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 1.5 MiB |
Binary file not shown.
|
After Width: | Height: | Size: 1.1 MiB |
334
avatar-h5-renderer/public/Resources/Haru/Haru.cdi3.json
Normal file
334
avatar-h5-renderer/public/Resources/Haru/Haru.cdi3.json
Normal file
@ -0,0 +1,334 @@
|
|||||||
|
{
|
||||||
|
"Version": 3,
|
||||||
|
"Parameters": [
|
||||||
|
{
|
||||||
|
"Id": "ParamAngleX",
|
||||||
|
"GroupId": "ParamGroupFace",
|
||||||
|
"Name": "角度 X"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Id": "ParamAngleY",
|
||||||
|
"GroupId": "ParamGroupFace",
|
||||||
|
"Name": "角度 Y"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Id": "ParamAngleZ",
|
||||||
|
"GroupId": "ParamGroupFace",
|
||||||
|
"Name": "角度 Z"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Id": "ParamTere",
|
||||||
|
"GroupId": "ParamGroupFace",
|
||||||
|
"Name": "照れ"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Id": "ParamFaceForm",
|
||||||
|
"GroupId": "ParamGroupFace",
|
||||||
|
"Name": "顔の拡縮"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Id": "ParamEyeLOpen",
|
||||||
|
"GroupId": "ParamGroupEyes",
|
||||||
|
"Name": "左目 開閉"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Id": "ParamEyeLSmile",
|
||||||
|
"GroupId": "ParamGroupEyes",
|
||||||
|
"Name": "左目 笑顔"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Id": "ParamEyeROpen",
|
||||||
|
"GroupId": "ParamGroupEyes",
|
||||||
|
"Name": "右目 開閉"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Id": "ParamEyeRSmile",
|
||||||
|
"GroupId": "ParamGroupEyes",
|
||||||
|
"Name": "右目 笑顔"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Id": "ParamEyeForm",
|
||||||
|
"GroupId": "ParamGroupEyes",
|
||||||
|
"Name": "眼 変形"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Id": "ParamEyeBallForm",
|
||||||
|
"GroupId": "ParamGroupEyes",
|
||||||
|
"Name": "目玉 収縮"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Id": "ParamTear",
|
||||||
|
"GroupId": "ParamGroupEyes",
|
||||||
|
"Name": "涙"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Id": "ParamEyeBallX",
|
||||||
|
"GroupId": "ParamGroupEyes",
|
||||||
|
"Name": "目玉 X"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Id": "ParamEyeBallY",
|
||||||
|
"GroupId": "ParamGroupEyes",
|
||||||
|
"Name": "目玉 Y"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Id": "ParamBrowLY",
|
||||||
|
"GroupId": "ParamGroup",
|
||||||
|
"Name": "左眉 上下"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Id": "ParamBrowRY",
|
||||||
|
"GroupId": "ParamGroup",
|
||||||
|
"Name": "右眉 上下"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Id": "ParamBrowLX",
|
||||||
|
"GroupId": "ParamGroup",
|
||||||
|
"Name": "左眉 左右"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Id": "ParamBrowRX",
|
||||||
|
"GroupId": "ParamGroup",
|
||||||
|
"Name": "右眉 左右"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Id": "ParamBrowLAngle",
|
||||||
|
"GroupId": "ParamGroup",
|
||||||
|
"Name": "左眉 角度"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Id": "ParamBrowRAngle",
|
||||||
|
"GroupId": "ParamGroup",
|
||||||
|
"Name": "右眉 角度"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Id": "ParamBrowLForm",
|
||||||
|
"GroupId": "ParamGroup",
|
||||||
|
"Name": "左眉 変形"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Id": "ParamBrowRForm",
|
||||||
|
"GroupId": "ParamGroup",
|
||||||
|
"Name": "右眉 変形"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Id": "ParamMouthForm",
|
||||||
|
"GroupId": "ParamGroupMouth",
|
||||||
|
"Name": "口 変形"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Id": "ParamMouthOpenY",
|
||||||
|
"GroupId": "ParamGroupMouth",
|
||||||
|
"Name": "口 開閉"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Id": "ParamScarf",
|
||||||
|
"GroupId": "ParamGroup2",
|
||||||
|
"Name": "スカーフ揺れ"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Id": "ParamBodyAngleX",
|
||||||
|
"GroupId": "ParamGroup2",
|
||||||
|
"Name": "体の回転 X"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Id": "ParamBodyAngleY",
|
||||||
|
"GroupId": "ParamGroup2",
|
||||||
|
"Name": "体の回転 Y"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Id": "ParamBodyAngleZ",
|
||||||
|
"GroupId": "ParamGroup2",
|
||||||
|
"Name": "体の回転 Z"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Id": "ParamBodyUpper",
|
||||||
|
"GroupId": "ParamGroup2",
|
||||||
|
"Name": "上体"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Id": "ParamBreath",
|
||||||
|
"GroupId": "ParamGroup2",
|
||||||
|
"Name": "呼吸"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Id": "ParamBustY",
|
||||||
|
"GroupId": "ParamGroup2",
|
||||||
|
"Name": "胸 揺れ"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Id": "ParamArmLA",
|
||||||
|
"GroupId": "ParamGroupArms",
|
||||||
|
"Name": "左腕 A"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Id": "ParamArmRA",
|
||||||
|
"GroupId": "ParamGroupArms",
|
||||||
|
"Name": "右腕 A"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Id": "ParamArmLB",
|
||||||
|
"GroupId": "ParamGroupArms",
|
||||||
|
"Name": "右腕 B"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Id": "ParamArmRB",
|
||||||
|
"GroupId": "ParamGroupArms",
|
||||||
|
"Name": "左腕 B"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Id": "ParamHandChangeR",
|
||||||
|
"GroupId": "ParamGroupArms",
|
||||||
|
"Name": "右手切替"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Id": "ParamHandAngleR",
|
||||||
|
"GroupId": "ParamGroupArms",
|
||||||
|
"Name": "右手首角度"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Id": "ParamHandDhangeL",
|
||||||
|
"GroupId": "ParamGroupArms",
|
||||||
|
"Name": "左手切替"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Id": "ParamHandAngleL",
|
||||||
|
"GroupId": "ParamGroupArms",
|
||||||
|
"Name": "左手首角度"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Id": "ParamHairFront",
|
||||||
|
"GroupId": "ParamGroup3",
|
||||||
|
"Name": "髪揺れ 前"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Id": "ParamHairSide",
|
||||||
|
"GroupId": "ParamGroup3",
|
||||||
|
"Name": "髪揺れ 横"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Id": "ParamHairBack",
|
||||||
|
"GroupId": "ParamGroup3",
|
||||||
|
"Name": "髪揺れ 後"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"ParameterGroups": [
|
||||||
|
{
|
||||||
|
"Id": "ParamGroupFace",
|
||||||
|
"GroupId": "",
|
||||||
|
"Name": "顔"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Id": "ParamGroupEyes",
|
||||||
|
"GroupId": "",
|
||||||
|
"Name": "目"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Id": "ParamGroup",
|
||||||
|
"GroupId": "",
|
||||||
|
"Name": "眉毛"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Id": "ParamGroupMouth",
|
||||||
|
"GroupId": "",
|
||||||
|
"Name": "口"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Id": "ParamGroup2",
|
||||||
|
"GroupId": "",
|
||||||
|
"Name": "胴体"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Id": "ParamGroupArms",
|
||||||
|
"GroupId": "",
|
||||||
|
"Name": "腕"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Id": "ParamGroup3",
|
||||||
|
"GroupId": "",
|
||||||
|
"Name": "髪揺れ"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"Parts": [
|
||||||
|
{
|
||||||
|
"Id": "Part01Core",
|
||||||
|
"Name": "コアパーツ"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Id": "Part01Hoho001",
|
||||||
|
"Name": "頬"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Id": "Part01Brow001",
|
||||||
|
"Name": "まゆ毛"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Id": "Part01Tear",
|
||||||
|
"Name": "涙"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Id": "Part01EyeBall001",
|
||||||
|
"Name": "目玉"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Id": "Part01Eye001",
|
||||||
|
"Name": "目"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Id": "Part01Nose001",
|
||||||
|
"Name": "鼻"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Id": "Part01Mouth001",
|
||||||
|
"Name": "口"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Id": "Part01Face001",
|
||||||
|
"Name": "顔"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Id": "Part01Ear001",
|
||||||
|
"Name": "耳"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Id": "Part01Neck001",
|
||||||
|
"Name": "首"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Id": "Part01HairFront001",
|
||||||
|
"Name": "前髪"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Id": "Part01HairSide001",
|
||||||
|
"Name": "横髪"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Id": "Part01HairBack001",
|
||||||
|
"Name": "後ろ髪"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Id": "Part01ArmRB001",
|
||||||
|
"Name": "左腕 B"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Id": "Part01ArmLB001",
|
||||||
|
"Name": "右腕 B"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Id": "Part01ArmRA001",
|
||||||
|
"Name": "右腕 A"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Id": "Part01ArmLA001",
|
||||||
|
"Name": "左腕 A"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Id": "Part01Body001",
|
||||||
|
"Name": "制服"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Id": "Part01Sketch",
|
||||||
|
"Name": "[ 下絵 ]"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
BIN
avatar-h5-renderer/public/Resources/Haru/Haru.moc3
Normal file
BIN
avatar-h5-renderer/public/Resources/Haru/Haru.moc3
Normal file
Binary file not shown.
246
avatar-h5-renderer/public/Resources/Haru/Haru.model3.json
Normal file
246
avatar-h5-renderer/public/Resources/Haru/Haru.model3.json
Normal file
@ -0,0 +1,246 @@
|
|||||||
|
{
|
||||||
|
"Version": 3,
|
||||||
|
"FileReferences": {
|
||||||
|
"Moc": "Haru.moc3",
|
||||||
|
"Textures": [
|
||||||
|
"Haru.2048/texture_00.png",
|
||||||
|
"Haru.2048/texture_01.png"
|
||||||
|
],
|
||||||
|
"Physics": "Haru.physics3.json",
|
||||||
|
"Pose": "Haru.pose3.json",
|
||||||
|
"DisplayInfo": "Haru.cdi3.json",
|
||||||
|
"Expressions": [
|
||||||
|
{
|
||||||
|
"Name": "F01",
|
||||||
|
"File": "expressions/F01.exp3.json"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "F02",
|
||||||
|
"File": "expressions/F02.exp3.json"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "F03",
|
||||||
|
"File": "expressions/F03.exp3.json"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "F04",
|
||||||
|
"File": "expressions/F04.exp3.json"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "F05",
|
||||||
|
"File": "expressions/F05.exp3.json"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "F06",
|
||||||
|
"File": "expressions/F06.exp3.json"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "F07",
|
||||||
|
"File": "expressions/F07.exp3.json"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "F08",
|
||||||
|
"File": "expressions/F08.exp3.json"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"Motions": {
|
||||||
|
"Idle": [
|
||||||
|
{
|
||||||
|
"File": "motions/haru_g_idle.motion3.json",
|
||||||
|
"FadeInTime": 0.3,
|
||||||
|
"FadeOutTime": 0.3
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"File": "motions/haru_g_m15.motion3.json",
|
||||||
|
"FadeInTime": 0.3,
|
||||||
|
"FadeOutTime": 0.3
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"TapBody": [
|
||||||
|
{
|
||||||
|
"File": "motions/haru_g_m26.motion3.json",
|
||||||
|
"FadeInTime": 0.3,
|
||||||
|
"FadeOutTime": 0.3,
|
||||||
|
"Sound": "sounds/haru_talk_13.wav"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"File": "motions/haru_g_m06.motion3.json",
|
||||||
|
"FadeInTime": 0.3,
|
||||||
|
"FadeOutTime": 0.3,
|
||||||
|
"Sound": "sounds/haru_Info_14.wav"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"File": "motions/haru_g_m20.motion3.json",
|
||||||
|
"FadeInTime": 0.3,
|
||||||
|
"FadeOutTime": 0.3,
|
||||||
|
"Sound": "sounds/haru_normal_6.wav"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"File": "motions/haru_g_m09.motion3.json",
|
||||||
|
"FadeInTime": 0.3,
|
||||||
|
"FadeOutTime": 0.3
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"Dance": [
|
||||||
|
{
|
||||||
|
"File": "motions/haru_g_m01.motion3.json",
|
||||||
|
"FadeInTime": 0.3,
|
||||||
|
"FadeOutTime": 0.3
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"File": "motions/haru_g_m02.motion3.json",
|
||||||
|
"FadeInTime": 0.3,
|
||||||
|
"FadeOutTime": 0.3
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"File": "motions/haru_g_m03.motion3.json",
|
||||||
|
"FadeInTime": 0.3,
|
||||||
|
"FadeOutTime": 0.3
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"File": "motions/haru_g_m04.motion3.json",
|
||||||
|
"FadeInTime": 0.3,
|
||||||
|
"FadeOutTime": 0.3
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"File": "motions/haru_g_m05.motion3.json",
|
||||||
|
"FadeInTime": 0.3,
|
||||||
|
"FadeOutTime": 0.3
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"File": "motions/haru_g_m06.motion3.json",
|
||||||
|
"FadeInTime": 0.3,
|
||||||
|
"FadeOutTime": 0.3
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"File": "motions/haru_g_m07.motion3.json",
|
||||||
|
"FadeInTime": 0.3,
|
||||||
|
"FadeOutTime": 0.3
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"File": "motions/haru_g_m08.motion3.json",
|
||||||
|
"FadeInTime": 0.3,
|
||||||
|
"FadeOutTime": 0.3
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"File": "motions/haru_g_m09.motion3.json",
|
||||||
|
"FadeInTime": 0.3,
|
||||||
|
"FadeOutTime": 0.3
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"File": "motions/haru_g_m10.motion3.json",
|
||||||
|
"FadeInTime": 0.3,
|
||||||
|
"FadeOutTime": 0.3
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"File": "motions/haru_g_m11.motion3.json",
|
||||||
|
"FadeInTime": 0.3,
|
||||||
|
"FadeOutTime": 0.3
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"File": "motions/haru_g_m12.motion3.json",
|
||||||
|
"FadeInTime": 0.3,
|
||||||
|
"FadeOutTime": 0.3
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"File": "motions/haru_g_m13.motion3.json",
|
||||||
|
"FadeInTime": 0.3,
|
||||||
|
"FadeOutTime": 0.3
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"File": "motions/haru_g_m14.motion3.json",
|
||||||
|
"FadeInTime": 0.3,
|
||||||
|
"FadeOutTime": 0.3
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"File": "motions/haru_g_m15.motion3.json",
|
||||||
|
"FadeInTime": 0.3,
|
||||||
|
"FadeOutTime": 0.3
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"File": "motions/haru_g_m16.motion3.json",
|
||||||
|
"FadeInTime": 0.3,
|
||||||
|
"FadeOutTime": 0.3
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"File": "motions/haru_g_m17.motion3.json",
|
||||||
|
"FadeInTime": 0.3,
|
||||||
|
"FadeOutTime": 0.3
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"File": "motions/haru_g_m18.motion3.json",
|
||||||
|
"FadeInTime": 0.3,
|
||||||
|
"FadeOutTime": 0.3
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"File": "motions/haru_g_m19.motion3.json",
|
||||||
|
"FadeInTime": 0.3,
|
||||||
|
"FadeOutTime": 0.3
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"File": "motions/haru_g_m20.motion3.json",
|
||||||
|
"FadeInTime": 0.3,
|
||||||
|
"FadeOutTime": 0.3
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"File": "motions/haru_g_m21.motion3.json",
|
||||||
|
"FadeInTime": 0.3,
|
||||||
|
"FadeOutTime": 0.3
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"File": "motions/haru_g_m22.motion3.json",
|
||||||
|
"FadeInTime": 0.3,
|
||||||
|
"FadeOutTime": 0.3
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"File": "motions/haru_g_m23.motion3.json",
|
||||||
|
"FadeInTime": 0.3,
|
||||||
|
"FadeOutTime": 0.3
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"File": "motions/haru_g_m24.motion3.json",
|
||||||
|
"FadeInTime": 0.3,
|
||||||
|
"FadeOutTime": 0.3
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"File": "motions/haru_g_m25.motion3.json",
|
||||||
|
"FadeInTime": 0.3,
|
||||||
|
"FadeOutTime": 0.3
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"File": "motions/haru_g_m26.motion3.json",
|
||||||
|
"FadeInTime": 0.3,
|
||||||
|
"FadeOutTime": 0.3
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"UserData": "Haru.userdata3.json"
|
||||||
|
},
|
||||||
|
"Groups": [
|
||||||
|
{
|
||||||
|
"Target": "Parameter",
|
||||||
|
"Name": "EyeBlink",
|
||||||
|
"Ids": [
|
||||||
|
"ParamEyeLOpen",
|
||||||
|
"ParamEyeROpen"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Target": "Parameter",
|
||||||
|
"Name": "LipSync",
|
||||||
|
"Ids": [
|
||||||
|
"ParamMouthOpenY"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"HitAreas": [
|
||||||
|
{
|
||||||
|
"Id": "HitArea",
|
||||||
|
"Name": "Head"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Id": "HitArea2",
|
||||||
|
"Name": "Body"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
373
avatar-h5-renderer/public/Resources/Haru/Haru.physics3.json
Normal file
373
avatar-h5-renderer/public/Resources/Haru/Haru.physics3.json
Normal file
@ -0,0 +1,373 @@
|
|||||||
|
{
|
||||||
|
"Version": 3,
|
||||||
|
"Meta": {
|
||||||
|
"PhysicsSettingCount": 4,
|
||||||
|
"TotalInputCount": 14,
|
||||||
|
"TotalOutputCount": 4,
|
||||||
|
"VertexCount": 8,
|
||||||
|
"EffectiveForces": {
|
||||||
|
"Gravity": {
|
||||||
|
"X": 0,
|
||||||
|
"Y": -1
|
||||||
|
},
|
||||||
|
"Wind": {
|
||||||
|
"X": 0,
|
||||||
|
"Y": 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"PhysicsDictionary": [
|
||||||
|
{
|
||||||
|
"Id": "PhysicsSetting1",
|
||||||
|
"Name": "前髪"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Id": "PhysicsSetting2",
|
||||||
|
"Name": "横髪"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Id": "PhysicsSetting3",
|
||||||
|
"Name": "後ろ髪"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Id": "PhysicsSetting4",
|
||||||
|
"Name": "スカーフ"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"PhysicsSettings": [
|
||||||
|
{
|
||||||
|
"Id": "PhysicsSetting1",
|
||||||
|
"Input": [
|
||||||
|
{
|
||||||
|
"Source": {
|
||||||
|
"Target": "Parameter",
|
||||||
|
"Id": "ParamAngleX"
|
||||||
|
},
|
||||||
|
"Weight": 60,
|
||||||
|
"Type": "X",
|
||||||
|
"Reflect": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Source": {
|
||||||
|
"Target": "Parameter",
|
||||||
|
"Id": "ParamAngleZ"
|
||||||
|
},
|
||||||
|
"Weight": 60,
|
||||||
|
"Type": "Angle",
|
||||||
|
"Reflect": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Source": {
|
||||||
|
"Target": "Parameter",
|
||||||
|
"Id": "ParamBodyAngleX"
|
||||||
|
},
|
||||||
|
"Weight": 40,
|
||||||
|
"Type": "X",
|
||||||
|
"Reflect": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Source": {
|
||||||
|
"Target": "Parameter",
|
||||||
|
"Id": "ParamBodyAngleZ"
|
||||||
|
},
|
||||||
|
"Weight": 40,
|
||||||
|
"Type": "Angle",
|
||||||
|
"Reflect": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"Output": [
|
||||||
|
{
|
||||||
|
"Destination": {
|
||||||
|
"Target": "Parameter",
|
||||||
|
"Id": "ParamHairFront"
|
||||||
|
},
|
||||||
|
"VertexIndex": 1,
|
||||||
|
"Scale": 1.821,
|
||||||
|
"Weight": 100,
|
||||||
|
"Type": "Angle",
|
||||||
|
"Reflect": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"Vertices": [
|
||||||
|
{
|
||||||
|
"Position": {
|
||||||
|
"X": 0,
|
||||||
|
"Y": 0
|
||||||
|
},
|
||||||
|
"Mobility": 1,
|
||||||
|
"Delay": 1,
|
||||||
|
"Acceleration": 1,
|
||||||
|
"Radius": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Position": {
|
||||||
|
"X": 0,
|
||||||
|
"Y": 8
|
||||||
|
},
|
||||||
|
"Mobility": 0.95,
|
||||||
|
"Delay": 0.8,
|
||||||
|
"Acceleration": 1.5,
|
||||||
|
"Radius": 8
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"Normalization": {
|
||||||
|
"Position": {
|
||||||
|
"Minimum": -10,
|
||||||
|
"Default": 0,
|
||||||
|
"Maximum": 10
|
||||||
|
},
|
||||||
|
"Angle": {
|
||||||
|
"Minimum": -10,
|
||||||
|
"Default": 0,
|
||||||
|
"Maximum": 10
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Id": "PhysicsSetting2",
|
||||||
|
"Input": [
|
||||||
|
{
|
||||||
|
"Source": {
|
||||||
|
"Target": "Parameter",
|
||||||
|
"Id": "ParamAngleX"
|
||||||
|
},
|
||||||
|
"Weight": 60,
|
||||||
|
"Type": "X",
|
||||||
|
"Reflect": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Source": {
|
||||||
|
"Target": "Parameter",
|
||||||
|
"Id": "ParamAngleZ"
|
||||||
|
},
|
||||||
|
"Weight": 60,
|
||||||
|
"Type": "Angle",
|
||||||
|
"Reflect": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Source": {
|
||||||
|
"Target": "Parameter",
|
||||||
|
"Id": "ParamBodyAngleX"
|
||||||
|
},
|
||||||
|
"Weight": 40,
|
||||||
|
"Type": "X",
|
||||||
|
"Reflect": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Source": {
|
||||||
|
"Target": "Parameter",
|
||||||
|
"Id": "ParamBodyAngleZ"
|
||||||
|
},
|
||||||
|
"Weight": 40,
|
||||||
|
"Type": "Angle",
|
||||||
|
"Reflect": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"Output": [
|
||||||
|
{
|
||||||
|
"Destination": {
|
||||||
|
"Target": "Parameter",
|
||||||
|
"Id": "ParamHairSide"
|
||||||
|
},
|
||||||
|
"VertexIndex": 1,
|
||||||
|
"Scale": 1.593,
|
||||||
|
"Weight": 100,
|
||||||
|
"Type": "Angle",
|
||||||
|
"Reflect": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"Vertices": [
|
||||||
|
{
|
||||||
|
"Position": {
|
||||||
|
"X": 0,
|
||||||
|
"Y": 0
|
||||||
|
},
|
||||||
|
"Mobility": 1,
|
||||||
|
"Delay": 1,
|
||||||
|
"Acceleration": 1,
|
||||||
|
"Radius": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Position": {
|
||||||
|
"X": 0,
|
||||||
|
"Y": 8
|
||||||
|
},
|
||||||
|
"Mobility": 0.95,
|
||||||
|
"Delay": 0.8,
|
||||||
|
"Acceleration": 1,
|
||||||
|
"Radius": 8
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"Normalization": {
|
||||||
|
"Position": {
|
||||||
|
"Minimum": -10,
|
||||||
|
"Default": 0,
|
||||||
|
"Maximum": 10
|
||||||
|
},
|
||||||
|
"Angle": {
|
||||||
|
"Minimum": -10,
|
||||||
|
"Default": 0,
|
||||||
|
"Maximum": 10
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Id": "PhysicsSetting3",
|
||||||
|
"Input": [
|
||||||
|
{
|
||||||
|
"Source": {
|
||||||
|
"Target": "Parameter",
|
||||||
|
"Id": "ParamAngleX"
|
||||||
|
},
|
||||||
|
"Weight": 60,
|
||||||
|
"Type": "X",
|
||||||
|
"Reflect": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Source": {
|
||||||
|
"Target": "Parameter",
|
||||||
|
"Id": "ParamAngleZ"
|
||||||
|
},
|
||||||
|
"Weight": 60,
|
||||||
|
"Type": "Angle",
|
||||||
|
"Reflect": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Source": {
|
||||||
|
"Target": "Parameter",
|
||||||
|
"Id": "ParamBodyAngleX"
|
||||||
|
},
|
||||||
|
"Weight": 40,
|
||||||
|
"Type": "X",
|
||||||
|
"Reflect": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Source": {
|
||||||
|
"Target": "Parameter",
|
||||||
|
"Id": "ParamBodyAngleZ"
|
||||||
|
},
|
||||||
|
"Weight": 40,
|
||||||
|
"Type": "Angle",
|
||||||
|
"Reflect": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"Output": [
|
||||||
|
{
|
||||||
|
"Destination": {
|
||||||
|
"Target": "Parameter",
|
||||||
|
"Id": "ParamHairBack"
|
||||||
|
},
|
||||||
|
"VertexIndex": 1,
|
||||||
|
"Scale": 1.943,
|
||||||
|
"Weight": 100,
|
||||||
|
"Type": "Angle",
|
||||||
|
"Reflect": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"Vertices": [
|
||||||
|
{
|
||||||
|
"Position": {
|
||||||
|
"X": 0,
|
||||||
|
"Y": 0
|
||||||
|
},
|
||||||
|
"Mobility": 1,
|
||||||
|
"Delay": 1,
|
||||||
|
"Acceleration": 1,
|
||||||
|
"Radius": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Position": {
|
||||||
|
"X": 0,
|
||||||
|
"Y": 8
|
||||||
|
},
|
||||||
|
"Mobility": 0.95,
|
||||||
|
"Delay": 0.8,
|
||||||
|
"Acceleration": 1.5,
|
||||||
|
"Radius": 8
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"Normalization": {
|
||||||
|
"Position": {
|
||||||
|
"Minimum": -10,
|
||||||
|
"Default": 0,
|
||||||
|
"Maximum": 10
|
||||||
|
},
|
||||||
|
"Angle": {
|
||||||
|
"Minimum": -10,
|
||||||
|
"Default": 0,
|
||||||
|
"Maximum": 10
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Id": "PhysicsSetting4",
|
||||||
|
"Input": [
|
||||||
|
{
|
||||||
|
"Source": {
|
||||||
|
"Target": "Parameter",
|
||||||
|
"Id": "ParamBodyAngleX"
|
||||||
|
},
|
||||||
|
"Weight": 100,
|
||||||
|
"Type": "X",
|
||||||
|
"Reflect": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Source": {
|
||||||
|
"Target": "Parameter",
|
||||||
|
"Id": "ParamBodyAngleZ"
|
||||||
|
},
|
||||||
|
"Weight": 100,
|
||||||
|
"Type": "Angle",
|
||||||
|
"Reflect": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"Output": [
|
||||||
|
{
|
||||||
|
"Destination": {
|
||||||
|
"Target": "Parameter",
|
||||||
|
"Id": "ParamScarf"
|
||||||
|
},
|
||||||
|
"VertexIndex": 1,
|
||||||
|
"Scale": 0.873,
|
||||||
|
"Weight": 100,
|
||||||
|
"Type": "Angle",
|
||||||
|
"Reflect": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"Vertices": [
|
||||||
|
{
|
||||||
|
"Position": {
|
||||||
|
"X": 0,
|
||||||
|
"Y": 0
|
||||||
|
},
|
||||||
|
"Mobility": 1,
|
||||||
|
"Delay": 1,
|
||||||
|
"Acceleration": 1,
|
||||||
|
"Radius": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Position": {
|
||||||
|
"X": 0,
|
||||||
|
"Y": 10
|
||||||
|
},
|
||||||
|
"Mobility": 0.9,
|
||||||
|
"Delay": 0.6,
|
||||||
|
"Acceleration": 1.5,
|
||||||
|
"Radius": 10
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"Normalization": {
|
||||||
|
"Position": {
|
||||||
|
"Minimum": -10,
|
||||||
|
"Default": 0,
|
||||||
|
"Maximum": 10
|
||||||
|
},
|
||||||
|
"Angle": {
|
||||||
|
"Minimum": -10,
|
||||||
|
"Default": 0,
|
||||||
|
"Maximum": 10
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
25
avatar-h5-renderer/public/Resources/Haru/Haru.pose3.json
Normal file
25
avatar-h5-renderer/public/Resources/Haru/Haru.pose3.json
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
{
|
||||||
|
"Type": "Live2D Pose",
|
||||||
|
"Groups": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"Id": "Part01ArmRA001",
|
||||||
|
"Link": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Id": "Part01ArmRB001",
|
||||||
|
"Link": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"Id": "Part01ArmLA001",
|
||||||
|
"Link": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Id": "Part01ArmLB001",
|
||||||
|
"Link": []
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
}
|
||||||
24
avatar-h5-renderer/public/Resources/Haru/Haru.userdata3.json
Normal file
24
avatar-h5-renderer/public/Resources/Haru/Haru.userdata3.json
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
{
|
||||||
|
"Version": 3,
|
||||||
|
"Meta": {
|
||||||
|
"UserDataCount": 3,
|
||||||
|
"TotalUserDataSize": 9
|
||||||
|
},
|
||||||
|
"UserData": [
|
||||||
|
{
|
||||||
|
"Target": "ArtMesh",
|
||||||
|
"Id": "D_PSD_27",
|
||||||
|
"Value": "tai"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Target": "ArtMesh",
|
||||||
|
"Id": "D_PSD_25",
|
||||||
|
"Value": "tai"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Target": "ArtMesh",
|
||||||
|
"Id": "D_PSD_24",
|
||||||
|
"Value": "tai"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
"Type": "Live2D Expression",
|
||||||
|
"Parameters": [
|
||||||
|
{
|
||||||
|
"Id": "ParamMouthForm",
|
||||||
|
"Value": 0.27,
|
||||||
|
"Blend": "Add"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@ -0,0 +1,35 @@
|
|||||||
|
{
|
||||||
|
"Type": "Live2D Expression",
|
||||||
|
"Parameters": [
|
||||||
|
{
|
||||||
|
"Id": "ParamBrowLY",
|
||||||
|
"Value": -1,
|
||||||
|
"Blend": "Add"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Id": "ParamBrowRY",
|
||||||
|
"Value": -1,
|
||||||
|
"Blend": "Add"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Id": "ParamBrowLForm",
|
||||||
|
"Value": 1,
|
||||||
|
"Blend": "Add"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Id": "ParamBrowRForm",
|
||||||
|
"Value": 1,
|
||||||
|
"Blend": "Add"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Id": "ParamMouthOpenY",
|
||||||
|
"Value": 1,
|
||||||
|
"Blend": "Add"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Id": "ParamEyeForm",
|
||||||
|
"Value": 0.54,
|
||||||
|
"Blend": "Add"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user