├── .gitignore
├── .npmignore
├── LICENSE
├── README-zh_CN.md
├── README.md
├── doc
├── QA-zh_CN.md
├── dragonbones_bin_format_1.0-zh_CN.md
├── dragonbones_json_format_5.5-zh_CN.md
├── dragonbones_json_format_5.5.md
└── 如何在 Egret 中使用 Live2D 的动画资源.md
├── out
└── resource
│ ├── helper.html
│ ├── player
│ ├── alone.html
│ ├── index.html
│ └── local.html
│ └── viewer
│ ├── alone.html
│ ├── index.html
│ └── local.html
├── package.json
├── src
├── action
│ ├── formatFormat.ts
│ ├── fromLive2D.ts
│ ├── fromSpine.ts
│ ├── toBinary.ts
│ ├── toFormat.ts
│ ├── toNew.ts
│ ├── toSpine.ts
│ └── toWeb.ts
├── common
│ ├── nodeUtils.ts
│ ├── object.ts
│ ├── server.ts
│ ├── types.ts
│ └── utils.ts
├── convertFrom.ts
├── convertTo.ts
├── format
│ ├── dragonBonesFormat.ts
│ ├── dragonBonesFormatV23.ts
│ ├── geom.ts
│ ├── live2DFormat.ts
│ ├── resFormat.ts
│ ├── spineFormat.ts
│ └── utils.ts
├── helper
│ ├── helper.ts
│ └── helperRemote.ts
├── remote.ts
└── test
│ └── testDemos.ts
├── tsconfig.json
├── tslint.json
└── typings.json
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 |
8 | # Runtime data
9 | pids
10 | *.pid
11 | *.seed
12 | *.pid.lock
13 |
14 | # Directory for instrumented libs generated by jscoverage/JSCover
15 | lib-cov
16 |
17 | # Coverage directory used by tools like istanbul
18 | coverage
19 |
20 | # nyc test coverage
21 | .nyc_output
22 |
23 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
24 | .grunt
25 |
26 | # Bower dependency directory (https://bower.io/)
27 | bower_components
28 |
29 | # node-waf configuration
30 | .lock-wscript
31 |
32 | # Compiled binary addons (http://nodejs.org/api/addons.html)
33 | build/Release
34 |
35 | # Dependency directories
36 | node_modules/
37 | jspm_packages/
38 |
39 | # Typescript v1 declaration files
40 | typings/
41 |
42 | # Optional npm cache directory
43 | .npm
44 |
45 | # Optional eslint cache
46 | .eslintcache
47 |
48 | # Optional REPL history
49 | .node_repl_history
50 |
51 | # Output of 'npm pack'
52 | *.tgz
53 |
54 | # Yarn Integrity file
55 | .yarn-integrity
56 |
57 | # dotenv environment variables file
58 | .env
59 |
60 | .vscode
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | src
3 | backup
4 | .vscode
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2017 DragonBones
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README-zh_CN.md:
--------------------------------------------------------------------------------
1 | # DragonBones tools
2 | [README in English](./README.md)
3 | ## [常见问题](./doc/QA-zh_CN.md)
4 |
5 | ## JSON 格式文档
6 | * [V 5.5](./doc/dragonbones_json_format_5.5-zh_CN.md)
7 |
8 | ## 如何安装
9 | * 安装 [Node.JS](https://nodejs.org/)。
10 | * 安装支持 html5 的浏览器,并且该浏览器是默认浏览器。
11 | * 命令行执行如下命令:
12 | * $ `npm install dragonbones-tools --global`
13 |
14 | ## 帮助
15 | * 使用 `2db` 命令将其他动画格式文件转换为龙骨 JSON 格式文件,使用 `--help` 命令查看 api 帮助。
16 |
17 | $ `2db --help`
18 | * use `db2` 命令将龙骨 JSON 格式文件转换为其他动画格式文件,使用 `--help` 命令查看 api 帮助。
19 |
20 | $ `db2 --help`
21 |
22 | ## 如何使用
23 | * 将当面目录下所有的 Spine JSON 格式文件转换为龙骨 JSON 格式文件。
24 |
25 | $ `2db -t spine`
26 | * 将当面目录下所有的 Live2d JSON 格式文件转换为龙骨 JSON 格式文件。
27 |
28 | $ `2db -t live2d`
29 | * 将当面目录下所有的龙骨 JSON 格式文件转换为最新的龙骨 JSON 格式文件。
30 |
31 | $ `db2 -t new`
32 | * 将当面目录下所有的龙骨 JSON 格式文件转换为 Spine JSON 格式文件。
33 |
34 | $ `db2 -t spine`
35 | * 将当面目录下所有包含 `hero` 关键字的龙骨 JSON 格式文件转换为龙骨二进制格式文件。
36 |
37 | $ `db2 -t binary -f hero`
38 | * 将输入目录所有的龙骨 JSON 格式文件转换为龙骨二进制格式文件并输出到指定目录。
39 |
40 | $ `db2 -t binary -i d:/input -o d:/output -d`
41 |
42 | ## 注意事项
43 | * 请确认在转换文件之前备份原始文件。
44 |
45 | ## 如何编译
46 | * $ `npm install`
47 | * $ `npm install typescript --global`
48 | * $ `tsc`
49 | * $ `npm link`
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # DragonBones tools
2 | [中文 README](./README-zh_CN.md)
3 | ## JSON format
4 | * [V 5.5](./doc/dragonbones_json_format_5.5.md)
5 |
6 | ## Installation
7 | * Install [Node.JS](https://nodejs.org/).
8 | * Install a browser that supports html5.
9 | * Execute the following command from the command line:
10 | * $ `npm install dragonbones-tools --global`
11 |
12 | ## Help
13 | * use `2db` convert other format files to DragonBones json format files.
14 |
15 | $ `2db --help`
16 | * use `db2` convert DragonBones json format files to other format files.
17 |
18 | $ `db2 --help`
19 |
20 | ## How to use
21 | * Convert Spine json format files to DragonBones json format files in current path.
22 |
23 | $ `2db -t spine`
24 | * Convert Live2d json format files to DragonBones json format files in current path.
25 |
26 | $ `2db -t live2d`
27 | * Convert old DragonBones json format files to new DragonBones json format files in current path.
28 |
29 | $ `db2 -t new`
30 | * Convert DragonBones json format files to Spine json format files in current path.
31 |
32 | $ `db2 -t spine`
33 | * Convert DragonBones json format files (file path contains "hero" key word) to DragonBones binary format files in current path.
34 |
35 | $ `db2 -t binary -f hero`
36 | * Convert DragonBones json format files to DragonBones binary format files from input path to output path and delete raw files.
37 |
38 | $ `db2 -t binary -i d:/input -o d:/output -d`
39 |
40 | ## Notice
41 | * Make sure backup your raw resources before convert.
42 |
43 | ## How to build
44 | * $ `npm install`
45 | * $ `npm install typescript --global`
46 | * $ `tsc`
47 | * $ `npm link`
--------------------------------------------------------------------------------
/doc/QA-zh_CN.md:
--------------------------------------------------------------------------------
1 | * 问:龙骨的官网在哪里?
2 | * 答:
3 | * 龙骨官网:http://dragonbones.com/ (仅仅做为官网,维护稍慢)
4 | * github 龙骨官网:https://github.com/DragonBones/ (源码和例子都在这里,维护良好,建议以此为准)
5 |
6 | * 问:为什么 PS 导出插件会报各种莫名其妙的错误?
7 | * 答:建议使用完整版的 PS 而不是各种精简版的 PS。
8 |
9 | * 问:为什么无法导出文件或无法预览?
10 | * 答:确保设备的浏览器是支持 html5 的浏览器。项目名称不可含有特殊符号等,例如空格、小数点。
11 |
12 | * 问:文件、骨架、骨骼、插槽、显示资源、动画的命名有什么规范吗?
13 | * 答:为了不给程序开发带来不必要的麻烦,尽量只使用英文,尽量不要使用奇怪的符号。
14 |
15 | * 问:库的文件结构有什么特殊要求吗?
16 | * 答:某些引擎可能不支持多级文件夹,而找不到贴图,这个时候将贴图资源都放到库的根目录可以解决这类问题。
17 |
18 | * 问:为什么浏览器支持 html5 仍然无法导出文件或无法预览?
19 | * 答:可能由于 DragonBones Pro 的缓存损坏造成的,建议删除缓存文件夹 `C:\Users\{你的用户名}\AppData\Roaming\DragonBonesPro\Local Store`。
20 |
21 | * 问:为什么导入的 Spine 文件不太正确?
22 | * 答:DragonBones Pro 的导入插件有一些兼容问题,可以尝试全新的转换方式 [DragonBones Tools](https://github.com/DragonBones/Tools) ,该转换插件需要依赖 NodeJS 和 npm。转换后的文件可以在 [DragonBones Viewer](https://dbplayer.egret-labs.org/viewer/v1/index.html) 中查看(将转换的文件全选,并拖拽到浏览器窗口即可),这些文件可以在龙骨运行时中良好的运行,但目前还无法在 DragonBones Pro 中运行,DragonBones Pro 有一些兼容问题。
23 |
24 | * 问:为什么修改项目的 library 后,会出现各种错误?比如找不到图片等?
25 | * 答:因为 DragonBonesPro 的 library 功能开发的不够完善,为了保证工程地安全,建议不要使用此功能。
26 |
27 | * 问:为什么我修改了图片名后,可能出现找不到图片的情况?
28 | * 答:因为 DragonBonesPro 的 library 功能开发的不够完善,为了保证工程地安全,建议不要使用此功能。
29 |
30 | * 问:Flash 动画怎么导入 DragonBones Pro?
31 | * 答:可部分参考[这篇文章](http://dragonbones.com/2015/getting_startedV20_cn.htmlhttp://dragonbones.com/2015/getting_startedV20_cn.html),将插件导出的 JSON 导入到 DragonBones Pro 即可。
32 |
33 | * 问:为什么导出的动画贴图在游戏引擎会有黑边?
34 | * 答:Cocos Creater 的[解决办法](http://forum.cocos.com/t/creater-blend-premultiply-alpha/43260/3),Unity 同理。
35 |
--------------------------------------------------------------------------------
/doc/dragonbones_bin_format_1.0-zh_CN.md:
--------------------------------------------------------------------------------
1 | # DragonBones 5.1 二进制数据格式标准说明
2 |
3 | ##
4 | * 不需要反序列化
5 | * 运行效率
6 | * 扩展性
7 | * 结构复杂度
8 | * 文件尺寸
9 |
10 |
11 | ## Data Fromat
12 | * [Data Tag](#data_tag)
13 | * [Header](#header)
14 | * [Color](#color)
15 | * [Weights](#weights)
16 | * [Surface](#surface)
17 | * [Mesh](#mesh)
18 | * [Timeline](#timeline)
19 | * [Frame](#frame)
20 | * [Tween Frame](#tween_frame)
21 | * [Action Frame](#action_frame)
22 | * [ZOrder Frame](#zorder_frame)
23 | * [FFD Frame](#ffd_frame)
24 | * [Tween Type](#tween_type)
25 |
26 |
27 |
Data Tag
28 |
29 | Name | Data Type | Size (Bytes)
30 | :---:|:---------:|:-----------:
31 | Tag | Uint32 | 4
32 | Version | Uint32 | 4
33 | | |
34 |
35 |
36 |
37 |
38 | Name | Data Type | Size (Bytes)
39 | :---:|:---------:|:-----------:
40 | Header Length | Uint32 | 4
41 | Header | Uint16 | 2
42 | ... | ... | ...
43 | | |
44 |
45 | ```javascript
46 | {
47 | // DragonBones 数据名称(请区分 DragonBones 数据名称和骨架名称,一个 DragonBones 数据可包含多个骨架)
48 | "name": "dragonBonesName",
49 | // 数据版本
50 | "version": "5.0",
51 | // 最低兼容版本
52 | "compatibleVersion": "4.5",
53 | // 动画帧频
54 | "frameRate": 24,
55 | // 自定义数据 (可选属性 默认: null)
56 | "userData": null,
57 |
58 | // 区块偏移和长度
59 | "offset":[
60 | IntOffset, length,
61 | FloatOffset, length,
62 | FrameIntOffset, length,
63 | FrameFloatOffset, length,
64 | FrameOffset, length,
65 | TimelineOffset, length,
66 | ColorOffset, length
67 | ],
68 |
69 | "search":{
70 | "color": 0;
71 | }
72 |
73 | // 骨架列表
74 | "armature": [{
75 | // 骨架名称
76 | "name": "armatureName",
77 | // 动画帧频 (可选属性 默认: 使用 DragonBones 数据的动画帧频)
78 | "frameRate": 24,
79 | // 骨架类型 (可选属性 默认: "Armature")
80 | // ["Armature": 骨骼动画, "MovieClip": 基本动画, "Stage": 场景动画]
81 | "type": "Armature",
82 | // 自定义数据 (可选属性 默认: null)
83 | "userData": null,
84 |
85 | // 区块偏移
86 | "offset":[IntOffset, FloatOffset, FrameOffset],
87 |
88 | // 骨骼列表
89 | "bone": [{
90 | // 骨骼名称
91 | "name": "boneName",
92 | // 父级骨骼名称
93 | "parent": "parentBoneName",
94 | // 是否继承移动 (可选属性 默认: true)
95 | "inheritTranslation": true,
96 | // 是否继承旋转 (可选属性 默认: true)
97 | "inheritRotation": true,
98 | // 是否继承缩放 (可选属性 默认: true)
99 | "inheritScale": true,
100 | // 是否继承镜像 (可选属性 默认: true)
101 | "inheritReflection": true,
102 | // 长度 (可选属性 默认: 0.00)
103 | "length": 0.00,
104 | // 自定义数据 (可选属性 默认: null)
105 | "userData": null,
106 |
107 | // 注册到骨架的位移/ 旋转/ 斜切/ 缩放 (可选属性 默认: null)
108 | "transform": {
109 | "x": 0.0000,
110 | "y": 0.0000,
111 | "r": 0.0000,
112 | "sk": 0.0000,
113 | "scX": 1.0000,
114 | "scY": 1.0000
115 | }
116 | }],
117 |
118 | // 插槽列表
119 | "slot": [{
120 | // 插槽名称
121 | "name": "slotName",
122 | // 插槽所属骨骼名称
123 | "parent": "parentBoneName",
124 | // 插槽默认显示对象
125 | "displayIndex": 0,
126 | // 插槽显示混合模式
127 | "blendMode": null,
128 | // 自定义数据 (可选属性 默认: null)
129 | "userData": null,
130 | // 插槽默认颜色
131 | "color": {
132 | "aM": 100,
133 | "rM": 100,
134 | "gM": 100,
135 | "bM": 100,
136 | "aO": 0,
137 | "rO": 0,
138 | "gO": 0,
139 | "bO": 0,
140 | }
141 | }],
142 |
143 | // 皮肤列表
144 | "skin": [{
145 | // 皮肤名称
146 | "name": "skinName",
147 |
148 | // 皮肤插槽配置列表
149 | "slot": {
150 | "name": [{
151 | // 显示对象名称
152 | "name": "displayName",
153 | // 显示对象类型
154 | "type": "image",
155 |
156 | "subType": "rectangle",
157 | // 如果共享网格是否继承 FFD 动画
158 | "inheritFFD": true,
159 | // 数据地址
160 | "offset": 10000,
161 | // 矩形或椭圆的宽高 (可选属性 默认: 0, 仅对边界框有效),
162 | "width": 100, "height": 100
163 | //
164 | "share": "meshName",
165 | // 子骨架指向的骨架名称或网格包含的贴图名称 (可选属性 默认: null, 仅对子骨架、网格有效)
166 | "path": "path",
167 |
168 | // 图片显示对象的轴点 (可选属性 默认: null)
169 | "pivot": {
170 | "x": 0.50, // 水平轴点 [0.00~1.00] (可选属性 默认: 0.50)
171 | "y": 0.50, // 垂直轴点 [0.00~1.00] (可选属性 默认: 0.50)
172 | },
173 |
174 | // 注册到骨骼的位移/ 旋转/ 斜切/ 缩放 (可选属性 默认: null)
175 | "transform": {
176 | "x": 0.0000,
177 | "y": 0.0000,
178 | "rt": 0.0000,
179 | "sk": 0.0000,
180 | "scX": 1.0000,
181 | "scY": 1.0000
182 | },
183 |
184 | // 添加到舞台后的默认行为列表 (可选属性 默认: null)
185 | "actions":[{
186 | "type": "play",
187 | "name": "animationName"
188 | }]
189 | }]
190 | }
191 | }],
192 |
193 | // ik 约束列表
194 | "ik": [{
195 | // ik 约束名称
196 | "name": "ikName",
197 | // 绑定骨骼名称
198 | "bone": "boneName",
199 | // 目标骨骼名称
200 | "target": "ikBoneName",
201 | // 弯曲方向 (可选属性 默认: true)
202 | // [true: 正方向/ 顺时针, false: 反方向/ 逆时针]
203 | "bendPositive": true,
204 | // 骨骼链的长度 (可选属性 默认: 0)
205 | // [0: 只约束 bone, n: 约束 bone 及 bone 向上 n 级的父骨骼]
206 | "chain": 0,
207 | // 权重 [0.00: 不约束 ~ 1.00: 完全约束] (可选属性 默认: 1.00)
208 | "weight": 1.00
209 | }],
210 |
211 | // 添加到舞台后的默认行为列表 (可选属性 默认: null)
212 | "defaultActions":[{
213 | "type": "play",
214 | "name": "animationName"
215 | }],
216 |
217 | // 动画列表
218 | "animation": [{
219 | // 动画名称
220 | "name": "animationName",
221 | // 持续的帧 (可选属性 默认: 1)
222 | "duration": 1,
223 | // 循环播放次数 [0: 循环播放无限次, n: 循环播放 n 次] (可选属性 默认: 1)
224 | "playTimes": 1,
225 | // 动画淡入时间 (以秒为单位,可选属性 默认: 0)
226 | "fadeInTime": 1.00,
227 | // 动画时间的缩放
228 | "scale": 1.00,
229 | // 自定义数据 (可选属性 默认: null)
230 | "userData": null,
231 |
232 | // 行为时间轴地址
233 | "action": 00000,
234 | // 层级时间轴地址
235 | "zOrder": 00001,
236 |
237 | // 区块偏移
238 | "offset":[FrameIntOffset, FrameFloatOffset, FrameOffset],
239 |
240 | "bone": {
241 | "name": [TimelineTag, TimelineOffset, TimelineTag, TimelineOffset, ...]
242 | },
243 |
244 | "slot": {
245 | "name": [TimelineTag, TimelineOffset, TimelineTag, TimelineOffset, ...]
246 | },
247 |
248 | "animation": {
249 | "name": [TimelineTag, TimelineOffset, TimelineTag, TimelineOffset, ...]
250 | }
251 | }]
252 | }]
253 | }
254 | ```
255 |
256 |
257 | Color (Int Array)
258 |
259 | Name | Data Type | Size (Bytes) | Value range
260 | :---:|:---------:|:------------:|:----------:
261 | Alpha Multiplier | Int16 | 2 | 0 ~ 100
262 | Red Multiplier | Int16 | 2 | 0 ~ 100
263 | Greed Multiplier | Int16 | 2 | 0 ~ 100
264 | Blue Multiplier | Int16 | 2 | 0 ~ 100
265 | Alpha Offset | Int16 | 2 | -256 ~ 256
266 | Red Offset | Int16 | 2 | -256 ~ 256
267 | Greed Offset | Int16 | 2 | -256 ~ 256
268 | Blue Offset | Int16 | 2 | -256 ~ 256
269 | | |
270 |
271 |
272 | Weights (Int Array)
273 |
274 | Name | Data Type | Size (Bytes) | Value range
275 | :---:|:---------:|:------------:|:-----------:
276 | Bone Count | Int16 | 2 |
277 | Float Array Offset | Int16 | 2 |
278 | Bone Indices | Int16 | 2 |
279 | ... | ... | ...
280 | Vertex Bone Count, Vertex Bone Indices | Int16 | 2 | 3, 4, 7, 6
281 | ... | ... | ...
282 | | |
283 | Weight, X, Y (Float Array) | Float32 | 4 | 1.0, 12.3, 45.6
284 | ... | ... | ...
285 | | |
286 |
287 |
288 | Surface (Int Array)
289 |
290 | Name | Data Type | Size (Bytes) | Value range
291 | :---:|:---------:|:------------:|:-----------:
292 | Vertex Count | Int16 | 2 |
293 | Empty | Int16 | 2 | 0
294 | Float Array Offset | Int16 | 2 |
295 | Empty | Int16 | 2 | -1
296 | | |
297 | Vertices (Float Array) | Float32 | 4 |
298 | ... | ... | ...
299 | | |
300 |
301 |
302 | Mesh (Int Array)
303 |
304 | Name | Data Type | Size (Bytes) | Value range
305 | :---:|:---------:|:------------:|:-----------:
306 | Vertex Count | Int16 | 2 |
307 | Triangle Count | Int16 | 2 |
308 | Float Array Offset | Int16 | 2 |
309 | Weight Offset | Int16 | 2 | -1: No Weights, N: [Weights](#weidhts) Offset
310 | Vertex indices | Int16 | 2 |
311 | ... | ... | ...
312 | | |
313 | Vertices (Float Array) | Float32 | 4 |
314 | ... | ... | ...
315 | UVs (Float Array) | Float32 | 4 |
316 | ... | ... | ...
317 | | |
318 |
319 |
320 | Path / Polygon BoundingBox (Int Array)
321 |
322 | Name | Data Type | Size (Bytes) | Value range
323 | :---:|:---------:|:------------:|:-----------:
324 | Vertex Count | Int16 | 2 |
325 | Empty | Int16 | 2 | 0
326 | Float Array Offset | Int16 | 2 |
327 | Weight Offset | Int16 | 2 | -1: No Weights, N: [Weights](#weidhts) Offset
328 | | |
329 | Vertices (Float Array) | Float32 | 4 |
330 | ... | ... | ...
331 | | |
332 |
333 |
334 | Timeline (Uint Array)
335 |
336 | Name | Data Type | Size (Bytes)
337 | :---:|:---------:|:-----------:
338 | Scale | Uint16 | 2
339 | Offset | Uint16 | 2
340 | Key Frame Count | Uint16 | 2
341 | Frame Value Count or Value Offset (Int Array or Float Array) | Uint16 | 2
342 | Frame Value Offset (Frame Int Array or Frame Float Array) | Uint16 | 2
343 | Frame Array Offsets | Uint16 | 2
344 | ... | ... | ...
345 | | |
346 |
347 |
348 | Frame (Frame Array)
349 |
350 | Name | Data Type | Size (Bytes)
351 | :---:|:---------:|:-----------:
352 | Position | Int16 | 2
353 | | |
354 |
355 |
356 | Tween Frame (Frame Array)
357 |
358 | Name | Data Type | Size (Bytes) | Value range
359 | :---:|:---------:|:------------:|:----------:
360 | Tween Type | Int16 | 2 | [Tween Type](#tween_type)
361 | Tween Easing or Curve Sample Count | Int16 | 2 | 0 ~ 100 or Count
362 | Curve Samples | Int16 | 2
363 | ... | ... | ...
364 | | |
365 |
366 |
367 | Action Frame (Frame Array)
368 |
369 | Name | Data Type | Size (Bytes)
370 | :---:|:---------:|:-----------:
371 | Action Count | Int16 | 2
372 | Action Indices | Int16 | 2
373 | ... | ... | ...
374 | | |
375 |
376 |
377 | ZOrder Frame (Frame Array)
378 |
379 | Name | Data Type | Size (Bytes)
380 | :---:|:---------:|:-----------:
381 | ZOrder Count | Int16 | 2
382 | ZOrders | Int16 | 2
383 | ... | ... | ...
384 | | |
385 |
386 |
387 | Tween Type
388 |
389 | Value | Type
390 | :----:|:---:
391 | 0 | None
392 | 1 | Linear
393 | 2 | Curve
394 | 3 | EaseInQuad
395 | 4 | EaseOutQuad
396 | 5 | EaseInOutQuad
397 | ... | ...
398 | |
--------------------------------------------------------------------------------
/doc/dragonbones_json_format_5.5-zh_CN.md:
--------------------------------------------------------------------------------
1 | # DragonBones 5.5 JSON 数据格式
2 | [English](./dragonbones_json_format_5.5.md)
3 |
4 | ```javascript
5 | {
6 | // 龙骨数据名称。
7 | "name": "dragonBonesName",
8 |
9 | // 数据版本。
10 | "version": "5.5",
11 |
12 | // 数据兼容的最低版本。
13 | "compatibleVersion": "5.5",
14 |
15 | // 动画帧率。 (可选属性,默认:24)
16 | "frameRate": 24,
17 |
18 | // 自定义数据。 (可选属性,默认:null)
19 | "userData": null,
20 |
21 | // 骨架列表。 (可选属性,默认:null)
22 | "armature": [{
23 |
24 | // 骨架名称。
25 | "name": "armatureName",
26 |
27 | // 动画帧率。 (可选属性,默认:null)
28 | // [null: 使用龙骨数据的帧率, N: 动画帧率]
29 | "frameRate": 24,
30 |
31 | // 非必要。
32 | "type": "Armature",
33 |
34 | // 自定义数据。 (可选属性,默认:null)
35 | "userData": null,
36 |
37 | // 当该骨架做为子骨架加入到父骨架时的默认行为列表。 (可选属性,默认:null)
38 | "defaultActions": [
39 | {
40 | "gotoAndPlay": "animationName"
41 | }
42 | ],
43 |
44 | // 骨骼列表。 (可选属性,默认:null)
45 | "bone": [{
46 |
47 | // 骨骼名称。
48 | "name": "boneName",
49 |
50 | // 父骨骼名称。 (可选属性,默认:null)
51 | "parent": "parentBoneName",
52 |
53 | // 自定义数据。 (可选属性,默认:null)
54 | "userData": null,
55 |
56 | // 该骨骼相对与父骨骼或骨架的基础变换。 (可选属性,默认:null)
57 | "transform": {
58 | "x": 0.0, // 水平位移。 (可选属性,默认:0.0)
59 | "y": 0.0, // 垂直位移。 (可选属性,默认:0.0)
60 | "skX": 0.0, // 水平倾斜。 (可选属性,默认:0.0)
61 | "skY": 0.0, // 垂直倾斜。 (可选属性,默认:0.0)
62 | "scX": 1.0, // 水平缩放。 (可选属性,默认:1.0)
63 | "scY": 1.0 // 垂直缩放。 (可选属性,默认:1.0)
64 | }
65 | }],
66 |
67 | // 插槽列表。
68 | "slot": [{
69 |
70 | // 插槽名称。
71 | "name": "slotName",
72 |
73 | // 父骨骼名称。
74 | "parent": "parentBoneName",
75 |
76 | // 默认的显示对象索引。 (可选属性,默认:0)
77 | "displayIndex": 0,
78 |
79 | // 混合模式。 (可选属性,默认:null)
80 | "blendMode": null,
81 |
82 | // 自定义数据。 (可选属性,默认:null)
83 | "userData": null,
84 |
85 | // 颜色变换。 (可选属性,默认:null)
86 | "color": {
87 | "aM": 100, // 透明相乘因子。 [0~100] (可选属性,默认:100)
88 | "rM": 100, // 红色相乘因子。 [0~100] (可选属性,默认:100)
89 | "gM": 100, // 绿色相乘因子。 [0~100] (可选属性,默认:100)
90 | "bM": 100, // 蓝色相乘因子。 [0~100] (可选属性,默认:100)
91 | "aO": 0, // 透明偏移。 [-255~255] (可选属性,默认:0)
92 | "rO": 0, // 红色偏移。 [-255~255] (可选属性,默认:0)
93 | "gO": 0, // 绿色偏移。 [-255~255] (可选属性,默认:0)
94 | "bO": 0, // 蓝色偏移。 [-255~255] (可选属性,默认:0)
95 | }
96 | }],
97 |
98 | // 皮肤列表。
99 | "skin": [{
100 |
101 | // 皮肤名称。
102 | "name": "skinName",
103 |
104 | // 插槽列表。
105 | "slot": [{
106 |
107 | // 插槽名称。
108 | "name": "slotName",
109 |
110 | // 显示对象列表。
111 | "display": [{
112 |
113 | // 显示对象名称。
114 | "name": "displayName",
115 |
116 | // 显示对象类型。 (可选属性,默认:"image")
117 | // ["image": 贴图的矩形, "armature": 嵌套的子骨架, "mesh": 贴图的网格, "boundingBox": 自定义边界框]
118 | "type": "image",
119 |
120 | // 显示对象的资源路径。 (可选属性,默认:null)
121 | "path": null,
122 |
123 | // 共享网格的名称。 (可选属性,默认:null)
124 | "share": "meshName",
125 |
126 | // 是否继承共享网格的形变动画。 (可选属性,默认:true)
127 | "inheritDeform": true,
128 |
129 | // 显示对象的子类型。
130 | // 如果是边界框: (可选属性,默认:"rectangle")
131 | // ["rectangle": 矩形边界框, "ellipse": 椭圆边界框, "polygon": 多边形边界框]
132 | "subType": "rectangle",
133 |
134 | // 非必要。
135 | "color": 0x000000,
136 |
137 | // 相对于骨骼的变换。 (可选属性,默认:null)
138 | "transform": {
139 | "x": 0.0, // 水平位移。 (可选属性,默认:0.0)
140 | "y": 0.0, // 垂直位移。 (可选属性,默认:0.0)
141 | "skX": 0.0, // 水平倾斜。 (可选属性,默认:0.0)
142 | "skY": 0.0, // 垂直倾斜。 (可选属性,默认:0.0)
143 | "scX": 1.0, // 水平缩放。 (可选属性,默认:1.0)
144 | "scY": 1.0 // 垂直缩放。 (可选属性,默认:1.0)
145 | },
146 |
147 | // 相对轴点。 (可选属性,默认:null)
148 | "pivot": {
149 | "x": 0.5, // 水平位移。 [0.0~1.0] (可选属性,默认:0.5)
150 | "y": 0.5, // 垂直位移。 [0.0~1.0] (可选属性,默认:0.5)
151 | },
152 |
153 | // 显示对象的尺寸。 (仅对边界框有效)
154 | "width": 100,
155 | "height": 100,
156 |
157 | "vertices": [-64.0, -64.0, 64.0, -64.0, 64.0, 64.0, -64.0, 64.0],
158 |
159 | "uvs": [0.0, 0.0, 1.0, 0.0, 1.0, 1.0, 0.0, 1.0],
160 |
161 | "triangles": [0, 1, 2, 2, 3, 0],
162 |
163 | "weights": [1, 0, 1.0, 2, 0, 0.5, 1, 0.5],
164 |
165 | "slotPose": [1.0, 0.0, 0.0, 1.0, 0.0, 0.0],
166 |
167 | "bonePose": [0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0],
168 |
169 | // 替换子骨架的默认行为列表。 (可选属性,默认:null)
170 | "actions": [
171 | {
172 | "gotoAndPlay": "animationName"
173 | }
174 | ]
175 | }]
176 | }]
177 | }],
178 |
179 | // IK 约束列表。
180 | "ik": [{
181 |
182 | // IK 约束名称。
183 | "name": "ikContaintName",
184 |
185 | // 约束骨骼的名称。
186 | "bone": "boneName",
187 |
188 | // 约束目标骨骼的名称
189 | "target": "ikBoneName",
190 |
191 | // IK 约束的方向。 (可选属性,默认:true)
192 | // [true: 正方向 / 顺时针, false: 反方向 / 逆时针]
193 | "bendPositive": true,
194 |
195 | // 被约束的骨骼链长度。
196 | // [0: 至约束骨骼, N: 该骨骼和向上 N 级的父骨骼] (可选属性,默认:0)
197 | "chain": 0,
198 |
199 | // 约束权重。 [0.0~1.0] (可选属性,默认:1.0)
200 | "weight": 1.0
201 | }],
202 |
203 | // 动画列表。
204 | "animation": [{
205 |
206 | // 动画名称。
207 | "name": "animationName",
208 |
209 | // 动画播放次数。 [0: 循环播放, N: 播放 N 次] (可选属性,默认:1)
210 | "playTimes": 1,
211 |
212 | // 动画长度。 (可选属性,默认:1)
213 | "duration": 1,
214 |
215 | // 行为关键帧列表。 (可选属性,默认:null)
216 | "frame": [{
217 |
218 | // 关键帧长度。 (可选属性,默认:1)
219 | "duration": 1,
220 |
221 | // 该帧的行为列表。 (可选属性,默认:null)
222 | "actions": [{
223 |
224 | // 行为类型。 [0: 播放动画, 10: 帧事件, 11: 帧声音事件] (可选属性,默认:0)
225 | "type": 0,
226 |
227 | // 行为的名称。 (动画名称或事件名称)
228 | "name": "actionName",
229 |
230 | // 骨骼名称。 (可选属性,默认:null)
231 | "bone": "boneName",
232 |
233 | // 插槽名称。 (可选属性,默认:null)
234 | "slot": "slotName",
235 |
236 | // 自定义数据列表。 (可选属性,默认:null)
237 | "ints":[0, 1, 2],
238 | "floats":[0.01, 1.01, 2.01],
239 | "strings":["a", "b", "c"]
240 | }]
241 | }],
242 |
243 | // z 排序时间轴。
244 | "zOrder": {
245 | "frame": [{
246 |
247 | // 关键帧长度。 (可选属性,默认:1)
248 | "duration": 1,
249 |
250 | // 该帧插槽 z 排序偏移列表。 [slotIndexA, offsetA, slotIndexB, offsetB, ...] (可选属性,默认:null)
251 | "zOrder": [0, 2, 4, 1, 6, -1]
252 | }]
253 | },
254 |
255 | // 骨骼时间轴列表。
256 | "bone": [{
257 |
258 | // 骨骼名称。
259 | "name": "boneName",
260 |
261 | // 时间轴缩放。 (可选属性,默认:0.0)
262 | "scale": 1.0,
263 |
264 | // 时间轴偏移。 (可选属性,默认:0.0)
265 | "offset": 0.0,
266 |
267 | // 位移关键帧列表。 (可选属性,默认:null)
268 | "translateFrame": [{
269 |
270 | // 关键帧长度。 (可选属性,默认:1)
271 | "duration": 1,
272 |
273 | // 补间类型 [0.0: 线形, null: 不补间]. (可选属性,默认:0)
274 | "tweenEasing": 0.0,
275 |
276 | // 补间贝塞尔曲线。 [x1, y1, x2, y2, ...] (可选属性,默认:null)
277 | "curve": [0.0, 0.0, 1.0, 1.0],
278 |
279 | // 该帧骨骼的水平位移。 (可选属性,默认:0.0)
280 | "x": 0.0,
281 |
282 | // 该帧骨骼的垂直位移。 (可选属性,默认:0.0)
283 | "y": 0.00,
284 | }],
285 |
286 | // 旋转关键帧列表。 (可选属性,默认:null)
287 | "rotateFrame": [{
288 |
289 | // 关键帧长度。 (可选属性,默认:1)
290 | "duration": 1,
291 |
292 | // 补间类型 [0.0: 线形, null: 不补间]. (可选属性,默认:0)
293 | "tweenEasing": 0.0,
294 |
295 | // 补间贝塞尔曲线。 [x1, y1, x2, y2, ...] (可选属性,默认:null)
296 | "curve": [0.0, 0.0, 1.0, 1.0],
297 |
298 | // 补间的旋转方式。 (可选属性,默认:0)
299 | // [0: 最小角度旋转, 1: 顺时针旋转, -1: 逆时针旋转, N: 至少顺时针旋转 N 圈, -N: 至少逆时针旋转 N 圈]
300 | "clockwise": 0,
301 |
302 | // 该帧骨骼的旋转。 [-PI ~ PI] (可选属性,默认:0.0)
303 | "rotate": 0.0,
304 |
305 | // 该帧骨骼的倾斜。 [-PI ~ PI] (可选属性,默认:0.0)
306 | "skew": 0.0
307 | }],
308 |
309 | // 缩放关键帧列表。 (可选属性,默认:null)
310 | "scaleFrame": [{
311 |
312 | // 关键帧长度。 (可选属性,默认:1)
313 | "duration": 1,
314 |
315 | // 补间类型 [0.0: 线形, null: 不补间]. (可选属性,默认:0)
316 | "tweenEasing": 0.0,
317 |
318 | // 补间贝塞尔曲线。 [x1, y1, x2, y2, ...] (可选属性,默认:null)
319 | "curve": [0.0, 0.0, 1.0, 1.0],
320 |
321 | // 该帧骨骼的水平缩放。 (可选属性,默认:1.0)
322 | "x": 1.0,
323 |
324 | // 该帧骨骼的垂直缩放。 (可选属性,默认:1.0)
325 | "y": 1.0
326 | }]
327 | }],
328 |
329 | // 插槽时间轴列表。
330 | "slot": [{
331 |
332 | // 时间轴名称。
333 | "name": "slotName",
334 |
335 | // 显示关键帧列表。 (可选属性,默认:null)
336 | "displayFrame": [{
337 |
338 | // 关键帧长度。 (可选属性,默认:1)
339 | "duration": 1,
340 |
341 | // 该帧插槽的显示对象索引。 (可选属性,默认:1)
342 | "value": 0,
343 |
344 | // 该帧嵌套子骨架的行为列表。 (可选属性,默认:null)
345 | "actions": [
346 | {
347 | "gotoAndPlay": "animationName"
348 | }
349 | ]
350 | }],
351 |
352 | // 颜色关键帧列表。 (可选属性,默认:null)
353 | "colorFrame": [{
354 |
355 | // 关键帧长度。 (可选属性,默认:1)
356 | "duration": 1,
357 |
358 | // 补间类型 [0.0: 线形, null: 不补间]. (可选属性,默认:0)
359 | "tweenEasing": 0.0,
360 |
361 | // 补间贝塞尔曲线。 [x1, y1, x2, y2, ...] (可选属性,默认:null)
362 | "curve": [0.0, 0.0, 1.0, 1.0],
363 |
364 | // 该帧插槽的颜色变换。 (可选属性,默认:null)
365 | "color": {
366 | "aM": 100, // 透明相乘因子。 [0~100] (可选属性,默认:100)
367 | "rM": 100, // 红色相乘因子。 [0~100] (可选属性,默认:100)
368 | "gM": 100, // 绿色相乘因子。 [0~100] (可选属性,默认:100)
369 | "bM": 100, // 蓝色相乘因子。 [0~100] (可选属性,默认:100)
370 | "aO": 0, // 透明偏移。 [-255~255] (可选属性,默认:0)
371 | "rO": 0, // 红色偏移。 [-255~255] (可选属性,默认:0)
372 | "gO": 0, // 绿色偏移。 [-255~255] (可选属性,默认:0)
373 | "bO": 0, // 蓝色偏移。 [-255~255] (可选属性,默认:0)
374 | }
375 | }]
376 | }],
377 |
378 | // FFD 时间轴列表。(可选属性,默认:null)
379 | "ffd": [{
380 |
381 | // 网格名称。
382 | "name": "meshName",
383 |
384 | // 皮肤名称。
385 | "skin": "skinName",
386 |
387 | // 插槽名称。
388 | "slot": "slotName",
389 |
390 | "frame": [{
391 |
392 | // 关键帧长度。 (可选属性,默认:1)
393 | "duration": 1,
394 |
395 | // 补间类型 [0.0: 线形, null: 不补间]. (可选属性,默认:0)
396 | "tweenEasing": 0.0,
397 |
398 | // 补间贝塞尔曲线。 [x1, y1, x2, y2, ...] (可选属性,默认:null)
399 | "curve": [0.0, 0.0, 1.0, 1.0],
400 |
401 | // 变形顶点列表的索引偏移,偏移之前的数据都是 0。 (可选属性,默认:0)
402 | "offset": 0,
403 |
404 | // 变形顶点列表,队尾为 0 的数据会别省略。
405 | // [x0, y0, x1, y1, ...] (可选属性,默认:null)
406 | "vertices": [0.1, 0.1]
407 | }]
408 | }],
409 |
410 | // IK 约束时间轴。 (可选属性,默认:null)
411 | "ik": [{
412 |
413 | // IK 约束名称。
414 | "name": "meshName",
415 |
416 | "frame": [{
417 |
418 | // 关键帧长度。 (可选属性,默认:1)
419 | "duration": 1,
420 |
421 | // 补间类型 [0.0: 线形, null: 不补间]. (可选属性,默认:0)
422 | "tweenEasing": 0.0,
423 |
424 | // 补间贝塞尔曲线。 [x1, y1, x2, y2, ...] (可选属性,默认:null)
425 | "curve": [0.0, 0.0, 1.0, 1.0],
426 |
427 | // 该帧 IK 约束的方向。 (可选属性,默认:true)
428 | "bendPositive": true,
429 |
430 | // 该帧 IK 约束的权重。 (可选属性,默认:1.0)
431 | "weight": 1.0
432 | }]
433 | }]
434 | }]
435 | }]
436 | }
437 | ```
--------------------------------------------------------------------------------
/doc/dragonbones_json_format_5.5.md:
--------------------------------------------------------------------------------
1 | # DragonBones 5.5 JSON format
2 | [中文](./dragonbones_json_format_5.5-zh_CN.md)
3 |
4 | ```javascript
5 | {
6 | // The name of the DragonBones data.
7 | "name": "dragonBonesName",
8 |
9 | // The version of the DragonBones data.
10 | "version": "5.5",
11 |
12 | // The minimum compatible version of the DragonBones data.
13 | "compatibleVersion": "5.5",
14 |
15 | // The frame rate of animations. (Optional property, default: 24)
16 | "frameRate": 24,
17 |
18 | // The custom user data. (Optional property, default: null)
19 | "userData": null,
20 |
21 | // A list of the armatures. (Optional property, default: null)
22 | "armature": [{
23 |
24 | // The name of the armature.
25 | "name": "armatureName",
26 |
27 | // The frame rate of animations. (Optional property, default: null)
28 | // [null: Same as the frame rate of the DragonBones data, N: The frame rate.]
29 | "frameRate": 24,
30 |
31 | // Nonessential.
32 | "type": "Armature",
33 |
34 | // The custom user data. (Optional property, default: null)
35 | "userData": null,
36 |
37 | // A list of default actions when added to a parent armature. (Optional property, default: null)
38 | "defaultActions": [
39 | {
40 | "gotoAndPlay": "animationName"
41 | }
42 | ],
43 |
44 | // A list of the bones. (Optional property, default: null)
45 | "bone": [{
46 |
47 | // The name of the bone.
48 | "name": "boneName",
49 |
50 | // The name of the parent bone. (Optional property, default: null)
51 | "parent": "parentBoneName",
52 |
53 | // The custom user data. (Optional property, default: null)
54 | "userData": null,
55 |
56 | // The transform of the bone relative to the parent bone or the armature for the base pose.
57 | // (Optional property, default: null)
58 | "transform": {
59 | "x": 0.0, // The horizontal translate. (Optional property, default: 0.0)
60 | "y": 0.0, // The vertical translate. (Optional property, default: 0.0)
61 | "skX": 0.0, // The horizontal skew. (Optional property, default: 0.0)
62 | "skY": 0.0, // The vertical skew. (Optional property, default: 0.0)
63 | "scX": 1.0, // The horizontal scale. (Optional property, default: 1.0)
64 | "scY": 1.0 // The vertical scale. (Optional property, default: 1.0)
65 | }
66 | }],
67 |
68 | // A list of the slots.
69 | "slot": [{
70 |
71 | // The name of the slot.
72 | "name": "slotName",
73 |
74 | // The name of the parent bone.
75 | "parent": "parentBoneName",
76 |
77 | // The default display index of the slot. (Optional property, default: 0)
78 | "displayIndex": 0,
79 |
80 | // The blend mode of the slot. (Optional property, default: null)
81 | "blendMode": null,
82 |
83 | // The custom user data. (Optional property, default: null)
84 | "userData": null,
85 |
86 | // The color transform of the slot. (Optional property, default: null)
87 | "color": {
88 | "aM": 100, // The alpha multiplier. [0~100] (Optional property, default: 100)
89 | "rM": 100, // The red multiplier. [0~100] (Optional property, default: 100)
90 | "gM": 100, // The green multiplier. [0~100] (Optional property, default: 100)
91 | "bM": 100, // The blue multiplier. [0~100] (Optional property, default: 100)
92 | "aO": 0, // The alpha offset. [-255~255] (Optional property, default: 0)
93 | "rO": 0, // The red offset. [-255~255] (Optional property, default: 0)
94 | "gO": 0, // The green offset. [-255~255] (Optional property, default: 0)
95 | "bO": 0, // The blue offset. [-255~255] (Optional property, default: 0)
96 | }
97 | }],
98 |
99 | // A list of the skins.
100 | "skin": [{
101 |
102 | // The name of the skin.
103 | "name": "skinName",
104 |
105 | // A list of the slots.
106 | "slot": [{
107 |
108 | // The name of the slot.
109 | "name": "slotName",
110 |
111 | // A list of the displays.
112 | "display": [{
113 |
114 | // The name of the display.
115 | "name": "displayName",
116 |
117 | // The type of the display. (Optional property, default: "image")
118 | // [
119 | // "image": A textured rectangle,
120 | // "armature": A nested child armature,
121 | // "mesh": A textured mesh,
122 | // "boundingBox": A bounding box
123 | // ]
124 | "type": "image",
125 |
126 | // The resource path of the display. (Optional property, default: null)
127 | "path": null,
128 |
129 | // The name of the shared mesh. (Optional property, default: null)
130 | "share": "meshName",
131 |
132 | // Whether to inherit the deform animations of the shared mesh. (Optional property, default: true)
133 | "inheritDeform": true,
134 |
135 | // The sub type of the display.
136 | // If the display is a bounding box: (Optional property, default: "rectangle")
137 | // ["rectangle": A rectangle, "ellipse": An ellipse, "polygon": A pllygon]
138 | "subType": "rectangle",
139 |
140 | // Nonessential.
141 | "color": 0x000000,
142 |
143 | // The transform of the display relative to the slot's bone. (Optional property, default: null)
144 | "transform": {
145 | "x": 0.0, // The horizontal translate. (Optional property, default: 0.0)
146 | "y": 0.0, // The vertical translate. (Optional property, default: 0.0)
147 | "skX": 0.0, // The horizontal skew. (Optional property, default: 0.0)
148 | "skY": 0.0, // The vertical skew. (Optional property, default: 0.0)
149 | "scX": 1.0, // The horizontal scale. (Optional property, default: 1.0)
150 | "scY": 1.0 // The vertical scale. (Optional property, default: 1.0)
151 | },
152 |
153 | // The relative pivot of the display. (Optional property, default: null)
154 | "pivot": {
155 | "x": 0.5, // The horizontal translate. [0.0~1.0] (Optional property, default: 0.5)
156 | "y": 0.5, // The vertical translate. [0.0~1.0] (Optional property, default: 0.5)
157 | },
158 |
159 | // The size of display. (Valid for bounding box only)
160 | "width": 100,
161 | "height": 100,
162 |
163 | "vertices": [-64.0, -64.0, 64.0, -64.0, 64.0, 64.0, -64.0, 64.0],
164 |
165 | "uvs": [0.0, 0.0, 1.0, 0.0, 1.0, 1.0, 0.0, 1.0],
166 |
167 | "triangles": [0, 1, 2, 2, 3, 0],
168 |
169 | "weights": [1, 0, 1.0, 2, 0, 0.5, 1, 0.5],
170 |
171 | "slotPose": [1.0, 0.0, 0.0, 1.0, 0.0, 0.0],
172 |
173 | "bonePose": [0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0],
174 |
175 | // Override the default actions of the nested child armature. (Optional property, default: null)
176 | "actions": [
177 | {
178 | "gotoAndPlay": "animationName"
179 | }
180 | ]
181 | }]
182 | }]
183 | }],
184 |
185 | // A list of the IK constraints.
186 | "ik": [{
187 |
188 | // The name of the IK constraint.
189 | "name": "ikName",
190 |
191 | // The name of the bone.
192 | "bone": "boneName",
193 |
194 | // The name of the target bone.
195 | "target": "ikBoneName",
196 |
197 | // The IK constraint bend direction. (Optional property, default: true)
198 | // [true: Positive direction / Clockwise, false: Reverse Direction / Counterclockwise]
199 | "bendPositive": true,
200 |
201 | // The bone count of the bone chain in the constraint.
202 | // [0: Only the bone, N: The bone and the bone up N-level parent bones] (Optional property, default: 0)
203 | "chain": 0,
204 |
205 | // The weight of the IK constraint. [0.0~1.0] (Optional property, default: 1.0)
206 | "weight": 1.0
207 | }],
208 |
209 | // A list of the animations.
210 | "animation": [{
211 |
212 | // The name of animation.
213 | "name": "animationName",
214 |
215 | // The play times of the animation. [0: Loop play, N: Play N times] (Optional property, default: 1)
216 | "playTimes": 1,
217 |
218 | // The duration of the animation. (Optional property, default: 1)
219 | "duration": 1,
220 |
221 | // A list of the action keyframes. (Optional property, default: null)
222 | "frame": [{
223 |
224 | // The duration of the frame. (Optional property, default: 1)
225 | "duration": 1,
226 |
227 | // A list of actions. (Optional property, default: null)
228 | "actions": [{
229 |
230 | // The type of the action. (Optional property, default: 0)
231 | // [0: Play animation, 10: Frame event, 11: Frame sound event]
232 | "type": 0,
233 |
234 | // The name of the action. (The name of a animation or an event)
235 | "name": "actionName",
236 |
237 | // A bone name. (Optional property, default: null)
238 | "bone": "boneName",
239 |
240 | // A slot name. (Optional property, default: null)
241 | "slot": "slotName",
242 |
243 | // The list of custom data. (Optional property, default: null)
244 | "ints":[0, 1, 2],
245 | "floats":[0.01, 1.01, 2.01],
246 | "strings":["a", "b", "c"]
247 | }]
248 | }],
249 |
250 | // The z order timeline.
251 | "zOrder": {
252 | "frame": [{
253 |
254 | // The duration of the frame. (Optional property, default: 1)
255 | "duration": 1,
256 |
257 | // A list of slot indeices and numeric offsets. [slotIndexA, offsetA, slotIndexB, offsetB, ...]
258 | // (Optional property, default: null)
259 | "zOrder": [0, 2, 4, 1, 6, -1]
260 | }]
261 | },
262 |
263 | // A list of the bone timelines.
264 | "bone": [{
265 |
266 | // The name of the bone.
267 | "name": "boneName",
268 |
269 | // The scale of the timeline. (Optional property, default: 0.0)
270 | "scale": 1.0,
271 |
272 | // The offset of the timeline. (Optional property, default: 0.0)
273 | "offset": 0.0,
274 |
275 | // A list of the translate keyframes. (Optional property, default: null)
276 | "translateFrame": [{
277 |
278 | // The duration of the frame. (Optional property, default: 1)
279 | "duration": 1,
280 |
281 | // The tween easing of the frame. [0.0: Linear, null: No easing]. (Optional property, default: 0)
282 | "tweenEasing": 0.0,
283 |
284 | // The interpolation to use between this and the next keyframe. [x1, y1, x2, y2, ...]
285 | // (Optional property, default: null)
286 | "curve": [0.0, 0.0, 1.0, 1.0],
287 |
288 | // The horizontal translate of a bone in the keyframe. (Optional property, default: 0.0)
289 | "x": 0.0,
290 |
291 | // The vertical translate of a bone in the keyframe. (Optional property, default: 0.0)
292 | "y": 0.00,
293 | }],
294 |
295 | // A list of the rotate keyframes. (Optional property, default: null)
296 | "rotateFrame": [{
297 |
298 | // The duration of the frame. (Optional property, default: 1)
299 | "duration": 1,
300 |
301 | // The tween easing of the frame. [0.0: Linear, null: No easing]. (Optional property, default: 0)
302 | "tweenEasing": 0.0,
303 |
304 | // The interpolation to use between this and the next keyframe. [x1, y1, x2, y2, ...]
305 | // (Optional property, default: null)
306 | "curve": [0.0, 0.0, 1.0, 1.0],
307 |
308 | // The rotation behavior during a tween. (Optional property, default: 0)
309 | // [
310 | // 0: Chooses a direction of rotation that requires the least amount of turning,
311 | // 1: Rotates clockwise,
312 | // -1: Rotates counterclockwise,
313 | // N: Rotates clockwise at least N-rings,
314 | // -N: Rotates counterclockwise at least N-rings
315 | // ]
316 | "clockwise": 0,
317 |
318 | // The rotation of a bone in the keyframe. [-PI ~ PI] (Optional property, default: 0.0)
319 | "rotate": 0.0,
320 |
321 | // The skew of a bone in the keyframe. [-PI ~ PI] (Optional property, default: 0.0)
322 | "skew": 0.0
323 | }],
324 |
325 | // A list of the scale keyframes. (Optional property, default: null)
326 | "scaleFrame": [{
327 |
328 | // The duration of the frame. (Optional property, default: 1)
329 | "duration": 1,
330 |
331 | // The tween easing of the frame. [0.0: Linear, null: No easing]. (Optional property, default: 0)
332 | "tweenEasing": 0.0,
333 |
334 | // The interpolation to use between this and the next keyframe. [x1, y1, x2, y2, ...]
335 | // (Optional property, default: null)
336 | "curve": [0.0, 0.0, 1.0, 1.0],
337 |
338 | // The horizontal scale of a bone in the keyframe. (Optional property, default: 1.0)
339 | "x": 1.0,
340 |
341 | // The vertical scale of a bone in the keyframe. (Optional property, default: 1.0)
342 | "y": 1.0
343 | }]
344 | }],
345 |
346 | // A list of the slot timelines.
347 | "slot": [{
348 |
349 | // The name of the slot.
350 | "name": "slotName",
351 |
352 | // A list of the display keyframes. (Optional property, default: null)
353 | "displayFrame": [{
354 |
355 | // The duration of the frame. (Optional property, default: 1)
356 | "duration": 1,
357 |
358 | // The display index of a slot in the keyframe. (Optional property, default: 1)
359 | "value": 0,
360 |
361 | // The actions of a slot in the keyframe. (Optional property, default: null)
362 | "actions": [
363 | {
364 | "gotoAndPlay": "animationName"
365 | }
366 | ]
367 | }],
368 |
369 | // A list of the color keyframes. (Optional property, default: null)
370 | "colorFrame": [{
371 |
372 | // The duration of the frame. (Optional property, default: 1)
373 | "duration": 1,
374 |
375 | // The tween easing of the frame. [0.0: Linear, null: No easing]. (Optional property, default: 0)
376 | "tweenEasing": 0.0,
377 |
378 | // The interpolation to use between this and the next keyframe. [x1, y1, x2, y2, ...]
379 | // (Optional property, default: null)
380 | "curve": [0.0, 0.0, 1.0, 1.0],
381 |
382 | // The color transform of a slot in the frame. (Optional property, default: null)
383 | "color": {
384 | "aM": 100, // The alpha multiplier. [0~100] (Optional property, default: 100)
385 | "rM": 100, // The red multiplier. [0~100] (Optional property, default: 100)
386 | "gM": 100, // The green multiplier. [0~100] (Optional property, default: 100)
387 | "bM": 100, // The blue multiplier. [0~100] (Optional property, default: 100)
388 | "aO": 0, // The alpha offset. [-255~255] (Optional property, default: 0)
389 | "rO": 0, // The red offset. [-255~255] (Optional property, default: 0)
390 | "gO": 0, // The green offset. [-255~255] (Optional property, default: 0)
391 | "bO": 0, // The blue offset. [-255~255] (Optional property, default: 0)
392 | }
393 | }]
394 | }],
395 |
396 | // A list of the FFD timelines. (Optional property, default: null)
397 | "ffd": [{
398 |
399 | // The name of the mesh.
400 | "name": "meshName",
401 |
402 | // The name of skin.
403 | "skin": "skinName",
404 |
405 | // The name of slot.
406 | "slot": "slotName",
407 |
408 | "frame": [{
409 |
410 | // The duration of the frame. (Optional property, default: 1)
411 | "duration": 1,
412 |
413 | // The tween easing of the frame. [0.0: Linear, null: No easing]. (Optional property, default: 0)
414 | "tweenEasing": 0.0,
415 |
416 | // The interpolation to use between this and the next keyframe. [x1, y1, x2, y2, ...]
417 | // (Optional property, default: null)
418 | "curve": [0.0, 0.0, 1.0, 1.0],
419 |
420 | // The number of vertices to skip before applying vertices. (Optional property, default: 0)
421 | "offset": 0,
422 |
423 | // A list of number pairs that are the amounts to add to the setup vertex positions for the keyframe.
424 | // (Optional property, default: null)
425 | // [x0, y0, x1, y1, ...]
426 | "vertices": [0.1, 0.1]
427 | }]
428 | }],
429 |
430 | // A list of the IK constraint timelines. (Optional property, default: null)
431 | "ik": [{
432 |
433 | // The name of the IK constraint.
434 | "name": "meshName",
435 |
436 | "frame": [{
437 |
438 | // The duration of the frame. (Optional property, default: 1)
439 | "duration": 1,
440 |
441 | // The tween easing of the frame. [0.0: Linear, null: No easing]. (Optional property, default: 0)
442 | "tweenEasing": 0.0,
443 |
444 | // The interpolation to use between this and the next keyframe. [x1, y1, x2, y2, ...]
445 | // (Optional property, default: null)
446 | "curve": [0.0, 0.0, 1.0, 1.0],
447 |
448 | // The positive direction of the IK constraint in the frame. (Optional property, default: true)
449 | "bendPositive": true,
450 |
451 | // The weight of the IK constraint in the frame. (Optional property, default: 1.0)
452 | "weight": 1.0
453 | }]
454 | }]
455 | }]
456 | }]
457 | }
458 | ```
--------------------------------------------------------------------------------
/doc/如何在 Egret 中使用 Live2D 的动画资源.md:
--------------------------------------------------------------------------------
1 | # 如何在 Egret 中使用 Live2D 的动画资源
2 |
3 | * 使用[龙骨的运行库](https://github.com/DragonBones/DragonBonesJS/tree/master/Egret/4.x/out)取代 Egret 内置的龙骨运行库。
4 | * 该功能稍后会正式加入 Egret 内置库。
5 | * 使用 Live2D 2.x 版本的模型动画工具制作模型动画并导出 JSON 模型动画数据。
6 | * 使用 [DragonBones Tools](https://github.com/DragonBones/Tools) 将 Live2D 的 JSON 动画数据转换成龙骨动画数据。
7 | * 参考下面的例子使用转换后的龙骨动画数据。
8 | * [例子源码](https://github.com/DragonBones/DragonBonesJS/blob/master/Egret/Demos/src/demo/EyeTracking.ts)
9 | * [在线演示](https://dragonbones.github.io/demo/EyeTracking/index.html)
10 |
11 | * 如果在开发中对该功能有任何问题或建议,请戳[这里](https://github.com/DragonBones/DragonBonesJS/issues)提交你的问题或建议。
12 |
--------------------------------------------------------------------------------
/out/resource/helper.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
37 |
38 |
39 |
--------------------------------------------------------------------------------
/out/resource/player/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
24 |
25 |
26 |
27 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
71 |
72 |
77 |
78 |
79 |
--------------------------------------------------------------------------------
/out/resource/player/local.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
24 |
25 |
26 |
27 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
71 |
72 |
77 |
78 |
79 |
--------------------------------------------------------------------------------
/out/resource/viewer/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | DragonBones Viewer
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |

51 |
52 |
53 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |

76 |
79 |
80 |
81 |

82 |
85 |
86 |
87 |

88 |
91 |
92 |
93 |

94 |
97 |
98 |
99 |
100 |
101 |
102 |
105 |
108 |
113 |
116 |
117 |
X 1.00
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
Background Color
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
Show Grid
135 |
138 |
139 |
140 |
Show Bone
141 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
153 |
154 |
155 |
159 |
160 |
165 |
166 |
167 |
--------------------------------------------------------------------------------
/out/resource/viewer/local.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | DragonBones Viewer
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |

51 |
52 |
53 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |

76 |
79 |
80 |
81 |

82 |
85 |
86 |
87 |

88 |
91 |
92 |
93 |

94 |
97 |
98 |
99 |
100 |
101 |
102 |
105 |
108 |
113 |
116 |
117 |
X 1.00
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
Background Color
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
Show Grid
135 |
138 |
139 |
140 |
Show Bone
141 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
153 |
154 |
155 |
159 |
160 |
165 |
166 |
167 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "dragonbones-tools",
3 | "version": "0.1.02",
4 | "author": "dragonbones",
5 | "description": "Dragonbones tools.",
6 | "license": "MIT",
7 | "keywords": [
8 | "dragonbones",
9 | "convert"
10 | ],
11 | "repository": {
12 | "type": "git",
13 | "url": "https://github.com/DragonBones/Tools"
14 | },
15 | "bugs": {
16 | "url": "https://github.com/DragonBones/Tools/issues"
17 | },
18 | "scripts": {
19 | "build": "tsc"
20 | },
21 | "bin": {
22 | "db2": "./out/convertTo.js",
23 | "2db": "./out/convertFrom.js",
24 | "db-test-demos": "./out/test/testDemos.js"
25 | },
26 | "dependencies": {
27 | "@types/fs-extra": "^4.0.0",
28 | "commander": "^2.11.0",
29 | "fs-extra": "^4.0.1"
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/src/action/toFormat.ts:
--------------------------------------------------------------------------------
1 | import * as object from "../common/object";
2 | import * as geom from "../format/geom";
3 | import * as dbft from "../format/dragonBonesFormat";
4 | import * as dbftV23 from "../format/dragonBonesFormatV23";
5 | /**
6 | * Convert json string to DragonBones format.
7 | */
8 | export default function (jsonString: string, getTextureAtlases: () => dbft.TextureAtlas[]): dbft.DragonBones | null {
9 | if (!dbft.isDragonBonesString(jsonString)) {
10 | return null;
11 | }
12 |
13 | try {
14 | const json = JSON.parse(jsonString);
15 | const version = json["version"];
16 |
17 | if (dbft.DATA_VERSIONS.indexOf(version) < dbft.DATA_VERSIONS.indexOf(dbft.DATA_VERSION_4_0)) {
18 | textureAtlases = getTextureAtlases();
19 | const data = new dbftV23.DragonBones();
20 | object.copyObjectFrom(json, data, dbftV23.copyConfig);
21 |
22 | return V23ToV45(data);
23 | }
24 |
25 | const result = new dbft.DragonBones();
26 | object.copyObjectFrom(json, result, dbft.copyConfig);
27 |
28 | return result;
29 | }
30 | catch (error) {
31 | }
32 |
33 | return null;
34 | }
35 |
36 | let textureAtlases: dbft.TextureAtlas[];
37 | const helpMatrix = new geom.Matrix();
38 | const helpTransform = new geom.Transform();
39 | const helpPoint = new geom.Point();
40 | /**
41 | * Convert v2 v3 to v4 v5.
42 | */
43 | function V23ToV45(data: dbftV23.DragonBones): dbft.DragonBones | null {
44 | const result = new dbft.DragonBones();
45 |
46 | result.frameRate = data.frameRate;
47 | result.name = data.name;
48 | result.version = dbft.DATA_VERSION_4_5;
49 | result.compatibleVersion = dbft.DATA_VERSION_4_0;
50 |
51 | for (const armatureV23 of data.armature) {
52 | const armature = new dbft.Armature();
53 | armature.name = armatureV23.name;
54 | result.armature.push(armature);
55 |
56 | for (const boneV23 of armatureV23.bone) {
57 | const bone = new dbft.Bone();
58 | bone.inheritScale = false;
59 | // bone.inheritReflection = false;
60 | bone.name = boneV23.name;
61 | bone.parent = boneV23.parent;
62 | bone.transform.copyFrom(boneV23.transform);
63 | armature.bone.push(bone);
64 | }
65 |
66 | for (const skinV23 of armatureV23.skin) {
67 | const skin = new dbft.Skin();
68 | skin.name = skinV23.name;
69 | armature.skin.push(skin);
70 | skinV23.slot.sort(sortSkinSlot);
71 |
72 | for (const slotV23 of skinV23.slot) {
73 | let slot = armature.getSlot(slotV23.name);
74 | if (!slot) {
75 | slot = new dbft.Slot();
76 | slot.blendMode = slotV23.blendMode || dbft.BlendMode[dbft.BlendMode.Normal].toLowerCase();
77 | slot.displayIndex = slotV23.displayIndex;
78 | slot.name = slotV23.name;
79 | slot.parent = slotV23.parent;
80 | slot.color.copyFrom(slotV23.colorTransform);
81 | armature.slot.push(slot);
82 | }
83 |
84 | const skinSlot = new dbft.SkinSlot();
85 | skinSlot.name = slotV23.name;
86 | skin.slot.push(skinSlot);
87 |
88 | for (const displayV23 of slotV23.display) {
89 | if (displayV23.type === dbft.DisplayType[dbft.DisplayType.Image].toLowerCase()) {
90 | const display = new dbft.ImageDisplay();
91 | display.name = displayV23.name;
92 | display.transform.copyFrom(displayV23.transform);
93 | display.transform.pX = 0.0;
94 | display.transform.pY = 0.0;
95 |
96 | const texture = dbft.getTextureFormTextureAtlases(display.name, textureAtlases);
97 | if (texture) {
98 | display.transform.x += 0.5 * texture.width - displayV23.transform.pX;
99 | display.transform.y += 0.5 * texture.height - displayV23.transform.pY;
100 | }
101 |
102 | skinSlot.display.push(display);
103 | }
104 | else {
105 | const display = new dbft.ArmatureDisplay();
106 | display.name = displayV23.name;
107 | display.transform.copyFrom(displayV23.transform);
108 | skinSlot.display.push(display);
109 | }
110 | }
111 | }
112 | }
113 |
114 | for (const animationV23 of armatureV23.animation) {
115 | const animation = new dbft.Animation();
116 | animation.duration = animationV23.duration;
117 | animation.playTimes = animationV23.loop;
118 | animation.scale = animationV23.scale;
119 | animation.fadeInTime = animationV23.fadeInTime;
120 | animation.name = animationV23.name;
121 | (armature.animation as dbft.Animation[]).push(animation);
122 |
123 | for (const frameV23 of animationV23.frame) {
124 | const frame = new dbft.ActionFrame();
125 | frame.duration = frameV23.duration;
126 | frame.action = frameV23.action;
127 | frame.event = frameV23.event;
128 | frame.sound = frameV23.sound;
129 | animation.frame.push(frame);
130 | }
131 |
132 | for (const timelineV23 of animationV23.timeline) {
133 | const bone = armature.getBone(timelineV23.name);
134 | const slot = armature.getSlot(timelineV23.name);
135 | const boneAllTimeline = new dbft.BoneTimeline();
136 | const slotAllTimeline = new dbft.SlotTimeline();
137 | boneAllTimeline.scale = slotAllTimeline.scale = timelineV23.scale;
138 | boneAllTimeline.offset = slotAllTimeline.offset = timelineV23.offset;
139 | boneAllTimeline.name = slotAllTimeline.name = timelineV23.name;
140 | animation.bone.push(boneAllTimeline);
141 | animation.slot.push(slotAllTimeline);
142 |
143 | let position = 0;
144 | let prevBoneFrame: dbft.BoneAllFrame | null = null;
145 | let prevSlotFrame: dbft.SlotAllFrame | null = null;
146 | for (const frameV23 of timelineV23.frame) {
147 | const boneAllFrame = new dbft.BoneAllFrame();
148 | const slotAllFrame = new dbft.SlotAllFrame();
149 |
150 | boneAllFrame.duration = frameV23.duration;
151 | if (frameV23.tweenEasing === null) {
152 | if (animationV23.autoTween) {
153 | if (animationV23.tweenEasing === null) {
154 | boneAllFrame.tweenEasing = 0;
155 | slotAllFrame.tweenEasing = 0;
156 | }
157 | else {
158 | boneAllFrame.tweenEasing = animationV23.tweenEasing;
159 | slotAllFrame.tweenEasing = animationV23.tweenEasing;
160 | }
161 | }
162 | else {
163 | boneAllFrame.tweenEasing = NaN;
164 | slotAllFrame.tweenEasing = NaN;
165 | }
166 | }
167 | else {
168 | boneAllFrame.tweenEasing = frameV23.tweenEasing;
169 | slotAllFrame.tweenEasing = frameV23.tweenEasing;
170 | }
171 |
172 | boneAllFrame.curve = frameV23.curve;
173 | boneAllFrame.tweenRotate = frameV23.tweenRotate;
174 | boneAllFrame.transform.copyFrom(frameV23.transform);
175 | slotAllFrame.duration = frameV23.duration;
176 | slotAllFrame.curve = frameV23.curve;
177 | slotAllFrame.displayIndex = frameV23.displayIndex;
178 | slotAllFrame.color.copyFrom(frameV23.colorTransform);
179 | boneAllTimeline.frame.push(boneAllFrame);
180 | slotAllTimeline.frame.push(slotAllFrame);
181 |
182 | if (prevBoneFrame && prevSlotFrame && frameV23.displayIndex < 0) {
183 | prevBoneFrame.removeTween();
184 | prevSlotFrame.removeTween();
185 | }
186 |
187 | boneAllFrame.transform.toMatrix(helpMatrix);
188 | helpMatrix.transformPoint(frameV23.transform.pX, frameV23.transform.pY, helpPoint, true);
189 | boneAllFrame.transform.x += helpPoint.x;
190 | boneAllFrame.transform.y += helpPoint.y;
191 |
192 | if (frameV23.hide) {
193 | slotAllFrame.displayIndex = -1;
194 | }
195 |
196 | if (frameV23.action) {
197 | const action = new dbft.Action();
198 | action.type = dbft.ActionType.Play;
199 | action.name = frameV23.action;
200 | slotAllFrame.actions.push(action);
201 | }
202 |
203 | if (frameV23.event || frameV23.sound) {
204 | dbft.mergeActionToAnimation(animation, frameV23, position, bone, slot, true);
205 | }
206 |
207 | position += frameV23.duration;
208 | prevBoneFrame = boneAllFrame;
209 | prevSlotFrame = slotAllFrame;
210 | }
211 | }
212 |
213 | for (const slot of armature.slot) {
214 | let timeline = animation.getSlotTimeline(slot.name);
215 | if (!timeline) {
216 | const frame = new dbft.SlotAllFrame();
217 | frame.displayIndex = -1;
218 | timeline = new dbft.SlotTimeline();
219 | timeline.name = slot.name;
220 | timeline.frame.push(frame);
221 | animation.slot.push(timeline);
222 | }
223 | }
224 | }
225 |
226 | if (data.isGlobal) {
227 | armature.sortBones();
228 | globalToLocal(armature);
229 | }
230 | }
231 |
232 | return result;
233 | }
234 |
235 | function sortSkinSlot(a: dbftV23.Slot, b: dbftV23.Slot): number {
236 | return a.z < b.z ? -1 : 1;
237 | }
238 |
239 | function globalToLocal(armature: dbft.Armature): void {
240 | const bones = armature.bone.concat().reverse();
241 | for (const bone of bones) {
242 | const parent = armature.getBone(bone.parent);
243 | if (parent) {
244 | parent.transform.toMatrix(helpMatrix);
245 | helpMatrix.invert();
246 | helpMatrix.transformPoint(bone.transform.x, bone.transform.y, helpPoint);
247 | bone.transform.x = helpPoint.x;
248 | bone.transform.y = helpPoint.y;
249 | bone.transform.skX -= parent.transform.skY;
250 | bone.transform.skY -= parent.transform.skY;
251 | }
252 | else {
253 | bone.parent = "";
254 | }
255 |
256 | for (const animation of armature.animation as dbft.Animation[]) {
257 | const timeline = animation.getBoneTimeline(bone.name);
258 | if (!timeline) {
259 | continue;
260 | }
261 |
262 | const parentTimeline = parent ? animation.getBoneTimeline(parent.name) : null;
263 | let position = 0;
264 | for (const frame of timeline.frame) {
265 | if (parentTimeline) {
266 | getTimelineFrameMatrix(parentTimeline, position, helpTransform);
267 | helpTransform.toMatrix(helpMatrix);
268 | helpMatrix.invert();
269 | helpMatrix.transformPoint(frame.transform.x, frame.transform.y, helpPoint);
270 |
271 | frame.transform.x = helpPoint.x;
272 | frame.transform.y = helpPoint.y;
273 | frame.transform.skX -= helpTransform.skY;
274 | frame.transform.skY -= helpTransform.skY;
275 | }
276 |
277 | frame.transform.x -= bone.transform.x;
278 | frame.transform.y -= bone.transform.y;
279 | frame.transform.skX = geom.normalizeDegree(frame.transform.skX - bone.transform.skY);
280 | frame.transform.skY = geom.normalizeDegree(frame.transform.skY - bone.transform.skY);
281 | frame.transform.scX /= bone.transform.scX;
282 | frame.transform.scY /= bone.transform.scY;
283 |
284 | position += frame.duration;
285 | }
286 | }
287 | }
288 | }
289 |
290 | function getTimelineFrameMatrix(timeline: dbft.BoneTimeline, framePosition: number, transform: geom.Transform): void {
291 | let position = 0;
292 | let currentFrame: dbft.BoneAllFrame | null = null;
293 | let nextFrame: dbft.BoneAllFrame | null = null;
294 | for (const frame of timeline.frame) {
295 | if (position <= framePosition && framePosition < position + frame.duration) {
296 | currentFrame = frame;
297 | break;
298 | }
299 |
300 | position += frame.duration;
301 | }
302 |
303 | if (!currentFrame) {
304 | currentFrame = timeline.frame[timeline.frame.length - 1];
305 | }
306 |
307 | if ((!isNaN(currentFrame.tweenEasing) || currentFrame.curve.length > 0) && timeline.frame.length > 1) {
308 | let nextIndex = timeline.frame.indexOf(currentFrame) + 1;
309 | if (nextIndex >= timeline.frame.length) {
310 | nextIndex = 0;
311 | }
312 |
313 | nextFrame = timeline.frame[nextIndex];
314 | }
315 |
316 | if (!nextFrame) {
317 | transform.copyFrom(currentFrame.transform);
318 | }
319 | else {
320 | let tweenProgress = currentFrame.getTweenProgress((framePosition - position) / currentFrame.duration);
321 | transform.x = nextFrame.transform.x - currentFrame.transform.x;
322 | transform.y = nextFrame.transform.y - currentFrame.transform.y;
323 | transform.skX = geom.normalizeRadian(nextFrame.transform.skX - currentFrame.transform.skX);
324 | transform.skY = geom.normalizeRadian(nextFrame.transform.skY - currentFrame.transform.skY);
325 | transform.scX = nextFrame.transform.scX - currentFrame.transform.scX;
326 | transform.scY = nextFrame.transform.scY - currentFrame.transform.scY;
327 |
328 | transform.x = currentFrame.transform.x + transform.x * tweenProgress;
329 | transform.y = currentFrame.transform.y + transform.y * tweenProgress;
330 | transform.skX = currentFrame.transform.skX + transform.skX * tweenProgress;
331 | transform.skY = currentFrame.transform.skY + transform.skY * tweenProgress;
332 | transform.scX = currentFrame.transform.scX + transform.scX * tweenProgress;
333 | transform.scY = currentFrame.transform.scY + transform.scY * tweenProgress;
334 | }
335 | }
--------------------------------------------------------------------------------
/src/action/toNew.ts:
--------------------------------------------------------------------------------
1 | import * as geom from "../format/geom";
2 | import * as dbft from "../format/dragonBonesFormat";
3 |
4 | export default function (data: dbft.DragonBones, forRuntime: boolean): dbft.DragonBones {
5 | data.version = dbft.DATA_VERSION_5_5;
6 | data.compatibleVersion = dbft.DATA_VERSION_5_5;
7 |
8 | for (const armature of data.armature) {
9 | if (armature.type.toString().toLowerCase() === dbft.ArmatureType[dbft.ArmatureType.Stage]) {
10 | armature.type = dbft.ArmatureType[dbft.ArmatureType.MovieClip];
11 | armature.canvas = new dbft.Canvas();
12 | armature.canvas.x = armature.aabb.x;
13 | armature.canvas.y = armature.aabb.y;
14 | armature.canvas.width = armature.aabb.width;
15 | armature.canvas.height = armature.aabb.height;
16 | }
17 |
18 | for (const skin of armature.skin) {
19 | skin.name = skin.name || "default";
20 | }
21 |
22 | if (forRuntime) { // Old action to new action.
23 | if (armature.defaultActions.length > 0) {
24 | for (let i = 0, l = armature.defaultActions.length; i < l; ++i) {
25 | const action = armature.defaultActions[i];
26 | if (action instanceof dbft.OldAction) {
27 | armature.defaultActions[i] = dbft.oldActionToNewAction(action);
28 | }
29 | }
30 | }
31 | }
32 |
33 | if (forRuntime) { // Old action to new action and move action to display.
34 | for (const slot of armature.slot) {
35 | if (slot.actions.length > 0) {
36 | const defaultSkin = armature.getSkin("default");
37 | if (defaultSkin) {
38 | const skinSlot = defaultSkin.getSlot(slot.name);
39 | if (skinSlot !== null && skinSlot instanceof dbft.SkinSlot) {
40 | for (const action of slot.actions) {
41 | if (action instanceof dbft.OldAction) {
42 | const display = skinSlot.display[slot.displayIndex];
43 | if (display instanceof dbft.ArmatureDisplay) {
44 | display.actions.push(dbft.oldActionToNewAction(action));
45 | }
46 | }
47 | }
48 | }
49 | }
50 |
51 | slot.actions.length = 0;
52 | }
53 | }
54 | }
55 |
56 | for (const animation of armature.animation as dbft.Animation[]) {
57 | if (forRuntime) { // Old animation frame to new animation frame.
58 | for (const frame of animation.frame) {
59 | if (frame.event) {
60 | const action = new dbft.Action();
61 | action.type = dbft.ActionType.Frame;
62 | action.name = frame.event;
63 | frame.actions.push(action);
64 | frame.event = "";
65 | }
66 |
67 | if (frame.sound) {
68 | const action = new dbft.Action();
69 | action.type = dbft.ActionType.Sound;
70 | action.name = frame.sound;
71 | frame.actions.push(action);
72 | frame.sound = "";
73 | }
74 |
75 | if (frame.action) {
76 | const action = new dbft.Action();
77 | action.type = dbft.ActionType.Play;
78 | action.name = frame.action;
79 | frame.actions.push(action);
80 | frame.action = "";
81 | }
82 |
83 | for (const event of frame.events) {
84 | event.type = dbft.ActionType.Frame;
85 | frame.actions.push(event);
86 | }
87 |
88 | frame.events.length = 0;
89 | }
90 | }
91 | // Modify bone timelines.
92 | for (const timeline of animation.bone) {
93 | if (timeline instanceof dbft.TypeTimeline) {
94 | continue;
95 | }
96 |
97 | const bone = armature.getBone(timeline.name);
98 | if (!bone) {
99 | continue;
100 | }
101 |
102 | let position = 0;
103 | const slot = armature.getSlot(timeline.name);
104 | // Bone frame to transform frame.
105 | for (let i = 0, l = timeline.frame.length; i < l; ++i) {
106 | const frame = timeline.frame[i];
107 | const translateFrame = new dbft.DoubleValueFrame0();
108 | const rotateFrame = new dbft.BoneRotateFrame();
109 | const scaleFrame = new dbft.DoubleValueFrame1();
110 | timeline.translateFrame.push(translateFrame);
111 | timeline.rotateFrame.push(rotateFrame);
112 | timeline.scaleFrame.push(scaleFrame);
113 |
114 | translateFrame.duration = frame.duration;
115 | rotateFrame.duration = frame.duration;
116 | scaleFrame.duration = frame.duration;
117 |
118 | translateFrame.tweenEasing = frame.tweenEasing;
119 | translateFrame.curve = frame.curve.concat();
120 | rotateFrame.tweenEasing = frame.tweenEasing;
121 | rotateFrame.curve = frame.curve.concat();
122 | scaleFrame.tweenEasing = frame.tweenEasing;
123 | scaleFrame.curve = frame.curve.concat();
124 |
125 | translateFrame.x = frame.transform.x;
126 | translateFrame.y = frame.transform.y;
127 | rotateFrame.clockwise = frame.tweenRotate;
128 | rotateFrame.rotate = geom.normalizeDegree(frame.transform.skY);
129 | rotateFrame.skew = geom.normalizeDegree(frame.transform.skX - frame.transform.skY);
130 | scaleFrame.x = frame.transform.scX;
131 | scaleFrame.y = frame.transform.scY;
132 |
133 | if (frame.action && !slot) { // Error data.
134 | frame.action = "";
135 | }
136 |
137 | if (frame.event || frame.sound || frame.action) { // Merge bone action frame to action timeline.
138 | dbft.mergeActionToAnimation(animation, frame, position, bone, slot, forRuntime);
139 | frame.event = "";
140 | frame.sound = "";
141 | frame.action = "";
142 | }
143 |
144 | position += frame.duration;
145 | }
146 |
147 | timeline.frame.length = 0;
148 | }
149 | // Modify slot timelines.
150 | for (const timeline of animation.slot) {
151 | const slot = armature.getSlot(timeline.name);
152 | if (!slot) {
153 | continue;
154 | }
155 |
156 | let position = 0;
157 | // Slot frame to display frame and color frame.
158 | for (let i = 0, l = timeline.frame.length; i < l; ++i) {
159 | const frame = timeline.frame[i];
160 | const displayFrame = new dbft.SlotDisplayFrame();
161 | const colorFrame = new dbft.SlotColorFrame();
162 | timeline.displayFrame.push(displayFrame);
163 | timeline.colorFrame.push(colorFrame);
164 |
165 | displayFrame.duration = frame.duration;
166 | colorFrame.duration = frame.duration;
167 |
168 | colorFrame.tweenEasing = frame.tweenEasing;
169 | colorFrame.curve = frame.curve.concat();
170 |
171 | displayFrame.value = frame.displayIndex;
172 | colorFrame.value.copyFrom(frame.color);
173 |
174 | if (frame.actions.length > 0) {
175 | if (forRuntime) {
176 | dbft.mergeActionToAnimation(animation, frame, position, null, slot, true);
177 | }
178 | else {
179 | for (const action of frame.actions) {
180 | displayFrame.actions.push(action);
181 | }
182 | }
183 | }
184 |
185 | position += frame.duration;
186 | }
187 |
188 | timeline.frame.length = 0;
189 | // Merge slot action to action timeline.
190 | if (forRuntime) {
191 | position = 0;
192 |
193 | for (let i = 0, l = timeline.displayFrame.length; i < l; ++i) {
194 | const frame = timeline.displayFrame[i];
195 | if (frame.actions.length > 0) {
196 | dbft.mergeActionToAnimation(animation, frame, position, null, slot, true);
197 | frame.actions.length = 0;
198 | position += frame.duration;
199 | }
200 | }
201 | }
202 | }
203 | }
204 | }
205 |
206 | return data;
207 | }
--------------------------------------------------------------------------------
/src/action/toWeb.ts:
--------------------------------------------------------------------------------
1 | import * as fs from "fs";
2 | import * as path from "path";
3 | // import * as zlib from "zlib";
4 |
5 | const DATA_TAG = "data";
6 |
7 | type Input = {
8 | data: Buffer | any;
9 | textureAtlases: (Buffer | null)[];
10 | config?: {
11 | isLocal?: boolean;
12 | isAlone?: boolean;
13 | showFPS?: boolean;
14 | frameRate?: number;
15 | backgroundColor?: number;
16 | orientation?: string;
17 | scaleMode?: string;
18 | };
19 | };
20 |
21 | type ZipData = {
22 | data: string;
23 | textureAtlases: string[];
24 | }[];
25 |
26 | export default function (data: Input, isPlayer: boolean): string {
27 | const isLocal = data.config ? data.config.isLocal : false;
28 | const isAlone = data.config ? data.config.isAlone : false;
29 | const zipData = [{
30 | data: data.data instanceof Buffer ? data.data.toString("base64") : data.data,
31 | textureAtlases: data.textureAtlases.map((v) => {
32 | return v ? v.toString("base64") : "";
33 | })
34 | }] as ZipData;
35 | // const compressed = zlib.gzipSync(new Buffer(JSON.stringify(zipData))).toString("base64");
36 | // let htmlString = fs.readFileSync(path.join(__dirname, isPlayer ? "../resource/player.html" : "../resource/viewer.html"), "utf-8");
37 | // htmlString = replaceHTMLCommentTag(htmlString, DATA_TAG, `${compressed}`, false);
38 |
39 | let htmlString = fs.readFileSync(path.join(__dirname, `../resource/${isPlayer ? "player" : "viewer"}/${isLocal ? "local" : (isAlone ? "alone" : "index")}.html`), "utf-8");
40 | htmlString = replaceHTMLCommentTag(htmlString, DATA_TAG, `${JSON.stringify(zipData)}`, false);
41 |
42 | if (data.config) {
43 | if (data.config.showFPS) {
44 | htmlString = htmlString.replace(`data-show-fps="false"`, `data-show-fps="${data.config.showFPS}"`);
45 | }
46 |
47 | if (data.config.frameRate) {
48 | htmlString = htmlString.replace(`data-frame-rate="60"`, `data-frame-rate="${data.config.frameRate}"`);
49 | }
50 |
51 | if (data.config.backgroundColor || data.config.backgroundColor === 0) {
52 | if (data.config.backgroundColor >= 0) {
53 | htmlString = htmlString.replace(`background: #333333;`, `background: #${data.config.backgroundColor.toString(16)};`);
54 | }
55 | else {
56 | htmlString = htmlString.replace(`background: #333333;`, "");
57 | }
58 | }
59 |
60 | if (data.config.orientation) {
61 | htmlString = htmlString.replace(`data-orientation="auto"`, `data-orientation="${data.config.orientation}"`);
62 | }
63 |
64 | if (data.config.scaleMode) {
65 | htmlString = htmlString.replace(`data-scale-mode="fixedNarrow"`, `data-scale-mode="${data.config.scaleMode}"`);
66 | }
67 | }
68 |
69 | return htmlString;
70 | }
71 |
72 | function replaceHTMLCommentTag(htmlString: string, tag: string, string: string, keepTag: boolean): string {
73 | const startTag = "";
74 | const endTag = "";
75 | const startIndex = htmlString.indexOf(startTag);
76 | const endIndex = htmlString.indexOf(endTag);
77 |
78 | if (startIndex >= 0 && endIndex >= 0) {
79 | let replaceString: string;
80 | if (keepTag) {
81 | replaceString = htmlString.substring(startIndex + startTag.length, endIndex);
82 | }
83 | else {
84 | replaceString = htmlString.substring(startIndex, endIndex + endTag.length);
85 | }
86 |
87 | return htmlString.replace(replaceString, string);
88 | }
89 |
90 | return htmlString;
91 | }
--------------------------------------------------------------------------------
/src/common/nodeUtils.ts:
--------------------------------------------------------------------------------
1 | import * as fs from "fs";
2 | import * as path from "path";
3 | import * as os from "os";
4 | import { exec } from "child_process";
5 |
6 | export function filterFileList(folderPath: string, filter?: RegExp, maxDepth: number = 0, currentDepth: number = 0): string[] {
7 | let fileFilteredList = [] as string[];
8 |
9 | if (folderPath && fs.existsSync(folderPath)) {
10 | for (const file of fs.readdirSync(folderPath)) {
11 | const filePath = path.resolve(folderPath, file);
12 | const fileStatus = fs.lstatSync(filePath);
13 | if (fileStatus.isDirectory()) {
14 | if (maxDepth === 0 || currentDepth <= maxDepth) {
15 | fileFilteredList = fileFilteredList.concat(filterFileList(filePath, filter, currentDepth + 1));
16 | }
17 | }
18 | else if (!filter || filter.test(filePath)) {
19 | fileFilteredList.push(filePath);
20 | }
21 | }
22 | }
23 |
24 | return fileFilteredList;
25 | }
26 |
27 | export function open(target: string, appName: string | null = null, callback: ((error: any) => void) | null = null) {
28 | let command = "";
29 |
30 | switch (process.platform) {
31 | case "darwin":
32 | if (appName) {
33 | command = `open -a "${escape(appName)}"`;
34 | }
35 | else {
36 | command = `open`;
37 | }
38 | break;
39 |
40 | case "win32":
41 | // if the first parameter to start is quoted, it uses that as the title
42 | // so we pass a blank title so we can quote the file we are opening
43 | if (appName) {
44 | command = `start "" "${escape(appName)}"`;
45 | }
46 | else {
47 | command = `start ""`;
48 | }
49 | break;
50 |
51 | default:
52 | if (appName) {
53 | command = escape(appName);
54 | }
55 | else {
56 | // use Portlands xdg-open everywhere else
57 | command = path.join(__dirname, "xdg-open");
58 | }
59 | break;
60 | }
61 |
62 | const sudoUser = process.env["SUDO_USER"];
63 | if (sudoUser) {
64 | command = `sudo -u ${sudoUser} ${command}`;
65 | }
66 |
67 | command = `${command} "${escape(target)}"`;
68 |
69 | return exec(command, callback || undefined);
70 | }
71 |
72 | export function findIP(): string {
73 | const ipConfig = os.networkInterfaces();
74 | let ip = "localhost";
75 | for (const k in ipConfig) {
76 | const arr = ipConfig[k]!;
77 | for (let i = 0; i < arr.length; ++i) {
78 | const ipData = arr[i];
79 | if (!ipData.internal && ipData.family === "IPv4") {
80 | ip = ipData.address;
81 |
82 | return ip;
83 | }
84 | }
85 | }
86 |
87 | return ip;
88 | }
89 |
90 | function escape(string: string): string {
91 | return string.replace(/"/g, '\\\"');
92 | }
--------------------------------------------------------------------------------
/src/common/object.ts:
--------------------------------------------------------------------------------
1 | export function copyObjectFrom(from: any, to: any, config: any[] | null): void {
2 | let dataConfig: any = null;
3 | if (config !== null) {
4 | const index = config.indexOf(to.constructor);
5 | if (index >= 0 && index < config.length - 1) {
6 | dataConfig = config[index + 1];
7 | }
8 | }
9 |
10 | for (let k in to) {
11 | if (!(k in from)) {
12 | continue;
13 | }
14 |
15 | _copyObjectFrom(to, k, to[k], from[k], dataConfig ? dataConfig[k] : null, config);
16 | }
17 | }
18 |
19 | function _copyObjectFrom(parent: any, key: string | number, data: any, object: any, creater: any, config: any[] | null): any {
20 | const dataType = typeof data;
21 | const objectType = typeof object;
22 | if (objectType as any === "function") { //
23 | return;
24 | }
25 |
26 | if (object === null || object === undefined || objectType !== "object") {
27 | if (dataType === objectType) {
28 | parent[key] = object;
29 | }
30 | else if (dataType === "boolean") {
31 | // console.warn(`${key}: ${objectType} is not a boolean.`);
32 | switch (object) {
33 | case "0":
34 | case "NaN":
35 | case "":
36 | case "false":
37 | case "null":
38 | case "undefined":
39 | parent[key] = false;
40 | break;
41 |
42 | default:
43 | parent[key] = Boolean(object);
44 | break;
45 | }
46 | }
47 | else if (dataType === "number") {
48 | // console.warn(`${key}: ${objectType} is not a number.`);
49 | if (object === "NaN" || object === null) {
50 | parent[key] = NaN;
51 | }
52 | else {
53 | parent[key] = Number(object);
54 | }
55 | }
56 | else if (dataType === "string") {
57 | // console.warn(`${key}: ${objectType} is not a string.`);
58 | if (object || object === object) {
59 | parent[key] = String(object);
60 | }
61 | else {
62 | parent[key] = "";
63 | }
64 | }
65 | else {
66 | parent[key] = object;
67 | }
68 | }
69 | else if (object instanceof Array) {
70 | if (!(data instanceof Array)) {
71 | // console.warn(`${key}: ${dataType} is not an array.`);
72 | parent[key] = data = [];
73 | }
74 |
75 | if (data instanceof Array) {
76 | data.length = object.length;
77 | for (let i = 0, l = data.length; i < l; ++i) {
78 | _copyObjectFrom(data, i, data[i], object[i], creater, config);
79 | }
80 | }
81 | }
82 | else {
83 | if (data !== null && data !== undefined && dataType === "object") {
84 | if (creater instanceof Array) {
85 | for (let k in object) {
86 | _copyObjectFrom(data, k, data[k], object[k], creater[0], config);
87 | }
88 | }
89 | else {
90 | copyObjectFrom(object, data, config);
91 | }
92 | }
93 | else if (creater) {
94 | if (creater instanceof Array) {
95 | if (creater[1] === Function) {
96 | const clazz = creater[0](object);
97 | parent[key] = data = new clazz();
98 | copyObjectFrom(object, data, config);
99 | }
100 | else {
101 | parent[key] = data = creater[1] === Array ? [] : {};
102 | for (let k in object) {
103 | _copyObjectFrom(data, k, data[k], object[k], creater[0], config);
104 | }
105 | }
106 | }
107 | else if (creater) {
108 | parent[key] = data = new creater();
109 | copyObjectFrom(object, data, config);
110 | }
111 | else {
112 | // console.warn(`${key}: shallow copy.`);
113 | parent[key] = object;
114 | }
115 | }
116 | else {
117 | // console.warn(`${key}: shallow copy.`);
118 | parent[key] = object;
119 | }
120 | }
121 | }
122 |
123 | export function compress(data: any, config: any[]): boolean {
124 | if ((typeof data) !== "object") {
125 | return false;
126 | }
127 |
128 | if (data instanceof Array) {
129 | const array = data as any[];
130 | for (const item of array) {
131 | if (item !== null) {
132 | compress(item, config);
133 | }
134 | }
135 |
136 | if (array.length === 0) {
137 | return true;
138 | }
139 | }
140 | else {
141 | let defaultData: any = null;
142 | for (defaultData of config) {
143 | if (data.constructor === defaultData.constructor) {
144 | break;
145 | }
146 |
147 | defaultData = null;
148 | }
149 |
150 | if (defaultData !== null || typeof data === "object") {
151 | let count = 0;
152 | for (let k in data) {
153 | if (k.charAt(0) === "_") { // Pass private value.
154 | delete data[k];
155 | continue;
156 | }
157 |
158 | const value = data[k];
159 | const valueType = typeof value;
160 |
161 | if (defaultData !== null && (value === null || valueType === "undefined" || valueType === "boolean" || valueType === "number" || valueType === "string")) {
162 | const defaultValue = defaultData[k];
163 | if (value === defaultValue || (valueType === "number" && isNaN(value) && isNaN(defaultValue))) {
164 | delete data[k];
165 | continue;
166 | }
167 | }
168 | else if (valueType === "object") {
169 | if (compress(value, config)) {
170 | if ((value instanceof Array) ? !(data["_" + k]) : true) {
171 | delete data[k];
172 | }
173 |
174 | continue;
175 | }
176 | }
177 | else {
178 | continue;
179 | }
180 |
181 | count++;
182 | }
183 |
184 | return count === 0;
185 | }
186 | }
187 |
188 | return false;
189 | }
--------------------------------------------------------------------------------
/src/common/server.ts:
--------------------------------------------------------------------------------
1 | import * as fs from "fs";
2 | import * as path from "path";
3 | import * as url from "url";
4 | import * as http from "http";
5 | import * as types from "./types";
6 |
7 | export abstract class Server {
8 | public port: number;
9 | public host: string;
10 | public local: string;
11 | public readonly httpServer: http.Server = http.createServer(this._requestHandler.bind(this));
12 |
13 | protected _requestHandler(request: http.IncomingMessage, response: http.ServerResponse): boolean {
14 | // tslint:disable-next-line:no-unused-expression
15 | response;
16 | if (!request.url) {
17 | return false;
18 | }
19 |
20 | return true;
21 | }
22 |
23 | public start(host: string, port: number, local: string): void {
24 | if (this.port) {
25 | return;
26 | }
27 |
28 | this.host = host;
29 | this.port = port;
30 | this.local = local;
31 | this.httpServer.listen(this.port, () => {
32 | // console.log("Listening on: " + this.host + ":" + this.port);
33 | });
34 | // this.server.addListener("error", () => {
35 | // // process.exit(1501);
36 | // });
37 | }
38 |
39 | public stop(): void {
40 | process.exit(0);
41 | }
42 | }
43 |
44 | export enum Code {
45 | Success = 200,
46 | UnknownAction = 2000,
47 | DataError,
48 | JSONError,
49 | ActionError,
50 | }
51 |
52 | export interface Result { code: Code; message: string; data: any; }
53 |
54 | export class Gate extends Server {
55 | public readonly actions: { [k: string]: (request: http.IncomingMessage, response: http.ServerResponse) => void } = {};
56 |
57 | protected _requestHandler(request: http.IncomingMessage, response: http.ServerResponse): boolean {
58 | if (!super._requestHandler(request, response)) {
59 | return false;
60 | }
61 |
62 | const pathName = (url.parse(request.url as string).pathname as string).replace(this.local, "");
63 |
64 | if (pathName in this.actions) {
65 | const action = this.actions[pathName as any];
66 | action(request, response);
67 | }
68 | else {
69 | const localPath = path.join(__dirname, "../", pathName);
70 | let extName = path.extname(pathName);
71 | extName = extName ? extName.slice(1) : "unknown";
72 |
73 | if (fs.existsSync(localPath) && !fs.statSync(localPath).isDirectory()) {
74 | const fileResult = fs.readFileSync(localPath, "binary");
75 | response.writeHead(200, { "Content-Type": types.MineContentTypes[extName] || "text/plain" });
76 | response.write(fileResult, "binary");
77 | response.end();
78 | }
79 | else {
80 | this.responseEnd(response, Code.UnknownAction, Code[Code.UnknownAction]);
81 | }
82 | }
83 |
84 | return true;
85 | }
86 |
87 | public responseEnd(response: http.ServerResponse, code: Code, message: string, data: any = null): void {
88 | const result: Result = { code: code, message: message, data: data };
89 | response.writeHead(200, { "Content-Type": "application/json" });
90 | response.write(JSON.stringify(result));
91 | response.end();
92 | }
93 | }
--------------------------------------------------------------------------------
/src/common/types.ts:
--------------------------------------------------------------------------------
1 |
2 | export const MineContentTypes: { [key: string]: string } = {
3 | "css": "text/css",
4 | "gif": "image/gif",
5 | "html": "text/html",
6 | "ico": "image/x-icon",
7 | "jpeg": "image/jpeg",
8 | "jpg": "image/jpeg",
9 | "js": "text/javascript",
10 | "json": "application/json",
11 | "pdf": "application/pdf",
12 | "png": "image/png",
13 | "svg": "image/svg+xml",
14 | "swf": "application/x-shockwave-flash",
15 | "tiff": "image/tiff",
16 | "txt": "text/plain",
17 | "wav": "audio/x-wav",
18 | "wma": "audio/x-ms-wma",
19 | "wmv": "video/x-ms-wmv",
20 | "xml": "text/xml"
21 | };
--------------------------------------------------------------------------------
/src/common/utils.ts:
--------------------------------------------------------------------------------
1 | export function formatJSONString(string: string): string {
2 | let firstCode = string.charCodeAt(0);
3 | if (firstCode < 0x20 || firstCode > 0x7f) {
4 | string = string.substring(1); // 去除第一个字符
5 | }
6 |
7 | return string;
8 | }
9 |
10 | export function rgbaToHex(r: number, g: number, b: number, a: number): string {
11 | let result = "";
12 | let s = Math.round(r).toString(16);
13 | if (s.length < 2) (s = "0" + s);
14 | result += s;
15 |
16 | s = Math.round(g).toString(16);
17 | if (s.length < 2) (s = "0" + s);
18 | result += s;
19 |
20 | s = Math.round(b).toString(16);
21 | if (s.length < 2) (s = "0" + s);
22 | result += s;
23 |
24 | s = Math.round(a).toString(16);
25 | if (s.length < 2) (s = "0" + s);
26 | result += s;
27 |
28 | return result;
29 | }
30 |
31 | if (!String.prototype.padStart) {
32 | String.prototype.padStart = function (maxLength: number, fillString: string = ' ') {
33 | const source = this;
34 | if (source.length >= maxLength) return String(source);
35 |
36 | const fillLength = maxLength - source.length;
37 | let times = Math.ceil(fillLength / fillString.length);
38 |
39 | while (times >>= 1) {
40 | fillString += fillString;
41 | if (times === 1) {
42 | fillString += fillString;
43 | }
44 | }
45 |
46 | return fillString.slice(0, fillLength) + source;
47 | };
48 | }
49 |
50 | export function getEnumFormString(enumerator: any, type: string | number, defaultType: number = -1): number {
51 | if (typeof type === "number") {
52 | return type;
53 | }
54 |
55 | for (let k in enumerator) {
56 | if (typeof k === "string") {
57 | if (k.toLowerCase() === type.toLowerCase()) {
58 | return enumerator[k];
59 | }
60 | }
61 | }
62 |
63 | return defaultType;
64 | }
--------------------------------------------------------------------------------
/src/convertFrom.ts:
--------------------------------------------------------------------------------
1 | #! /usr/bin/env node
2 | import * as fs from "fs-extra";
3 | import * as path from "path";
4 | import * as commander from "commander";
5 | import * as object from "./common/object";
6 | import * as utils from "./common/utils";
7 | import * as nodeUtils from "./common/nodeUtils";
8 | import * as dbft from "./format/dragonBonesFormat";
9 | import * as spft from "./format/spineFormat";
10 | import * as l2ft from "./format/live2DFormat";
11 | import fromSpine from "./action/fromSpine";
12 | import fromLive2D from "./action/fromLive2D";
13 | import format from "./action/formatFormat";
14 | import * as helper from "./helper/helperRemote";
15 |
16 | function execute(): void {
17 | const commands = commander
18 | .version("0.1.0")
19 | .option("-i, --input [path]", "Input path")
20 | .option("-o, --output [path]", "Output path")
21 | .option("-t, --type [type]", "Convert from type [spine, live2d]", /^(spine|live2d)$/i, "none")
22 | .option("-f, --filter [keyword]", "Filter")
23 | .option("-d, --delete", "Delete raw files after convert complete.")
24 | .parse(process.argv);
25 |
26 | const input = path.resolve(path.normalize(commands["input"] as string || process.cwd()));
27 | const output = "output" in commands ? path.resolve(path.normalize(commands["output"])) : input;
28 | const type = commands["type"] as string || "";
29 | const filter = commands["filter"] as string || "";
30 | const deleteRaw = commands["delete"] as boolean || false;
31 | // let loadTextureAtlasToData = false;
32 | // let megreTextureAtlasToData = false;
33 |
34 | switch (type) {
35 | case "spine": {
36 | const files = nodeUtils.filterFileList(input, /\.(json)$/i);
37 | for (const file of files) {
38 | if (filter && file.indexOf(filter) < 0) {
39 | continue;
40 | }
41 |
42 | const fileString = fs.readFileSync(file, "utf-8");
43 | if (!spft.isSpineString(fileString)) {
44 | continue;
45 | }
46 |
47 | const fileName = path.basename(file, ".json");
48 | const textureAtlasFile = path.join(path.dirname(file), fileName + ".atlas");
49 | let textureAtlasString = "";
50 | if (fs.existsSync(textureAtlasFile)) {
51 | textureAtlasString = fs.readFileSync(textureAtlasFile, "utf-8");
52 | }
53 |
54 | const spine = new spft.Spine();
55 | object.copyObjectFrom(JSON.parse(fileString), spine, spft.copyConfig);
56 | const result = fromSpine({ name: fileName, data: spine, textureAtlas: textureAtlasString });
57 | const outputFile = (output ? file.replace(input, output) : file).replace(".json", "_ske.json");
58 | format(result);
59 |
60 | const textureAtlases = result.textureAtlas.concat(); // TODO
61 | result.textureAtlas.length = 0;
62 |
63 | object.compress(result, dbft.compressConfig);
64 | if (!fs.existsSync(path.dirname(outputFile))) {
65 | fs.mkdirsSync(path.dirname(outputFile));
66 | }
67 | fs.writeFileSync(
68 | outputFile,
69 | JSON.stringify(result)
70 | );
71 | console.log(outputFile);
72 |
73 | if (deleteRaw) {
74 | fs.removeSync(file);
75 | fs.removeSync(textureAtlasFile);
76 | }
77 |
78 | let index = 0;
79 | for (const textureAtlas of textureAtlases) {
80 | const pageName = `_tex${textureAtlases.length > 1 ? "_" + index++ : ""}`;
81 | const outputFile = (output ? file.replace(input, output) : file).replace(".json", pageName + ".json");
82 | const textureAtlasImage = path.join(path.dirname(file), textureAtlas.imagePath);
83 |
84 | textureAtlas.imagePath = path.basename(outputFile).replace(".json", ".png");
85 |
86 | const imageOutputFile = path.join(path.dirname(outputFile), textureAtlas.imagePath);
87 | if (!fs.existsSync(path.dirname(imageOutputFile))) {
88 | fs.mkdirsSync(path.dirname(imageOutputFile));
89 | }
90 |
91 | object.compress(textureAtlas, dbft.compressConfig);
92 | if (!fs.existsSync(path.dirname(outputFile))) {
93 | fs.mkdirsSync(path.dirname(outputFile));
94 | }
95 |
96 | fs.writeFileSync(
97 | outputFile,
98 | JSON.stringify(textureAtlas)
99 | );
100 |
101 | if (deleteRaw) {
102 | fs.moveSync(textureAtlasImage, imageOutputFile);
103 | }
104 | else {
105 | fs.copySync(textureAtlasImage, imageOutputFile);
106 | }
107 |
108 | let hasRotated = false;
109 | for (const texture of textureAtlas.SubTexture) {
110 | if (texture.rotated) {
111 | hasRotated = true;
112 | }
113 | }
114 |
115 | if (hasRotated) {
116 | const helperInput = {
117 | type: "modify_spine_textureatlas",
118 | data: {
119 | file: imageOutputFile,
120 | config: textureAtlas,
121 | texture: fs.readFileSync(imageOutputFile, "base64")
122 | }
123 | };
124 |
125 | helper.addInput(helperInput);
126 | }
127 |
128 | console.log(outputFile);
129 | console.log(imageOutputFile);
130 | }
131 | }
132 |
133 | break;
134 | }
135 |
136 | case "live2d": {
137 | const files = nodeUtils.filterFileList(input, /\.(model.json)$/i);
138 | for (const file of files) {
139 | if (filter && file.indexOf(filter) < 0) {
140 | continue;
141 | }
142 | // Parse config.
143 | const dirURL = path.dirname(file);
144 | const fileName = path.basename(file, ".model.json");
145 | const fileString = fs.readFileSync(file, "utf-8");
146 | const modelConfig = JSON.parse(fileString) as l2ft.ModelConfig;
147 | const modelURL = path.join(dirURL, modelConfig.model);
148 | const deleteFiles = [file];
149 | modelConfig.name = modelConfig.name || fileName;
150 |
151 | // Parse model.
152 | if (fs.existsSync(modelURL)) {
153 | const fileBuffer = fs.readFileSync(modelURL);
154 | const model = l2ft.parseModel(fileBuffer.buffer as ArrayBuffer);
155 | if (!model) {
156 | console.log("Model parse error.", modelURL);
157 | continue;
158 | }
159 |
160 | modelConfig.modelImpl = model;
161 | deleteFiles.push(modelURL);
162 | }
163 | else {
164 | console.log("File does not exist.", modelURL);
165 | continue;
166 | }
167 |
168 | for (let i = 0, l = modelConfig.textures.length; i < l; ++i) { // Parse textures.
169 | const textureURI = modelConfig.textures[i] as string;
170 | const textureURL = path.join(dirURL, textureURI);
171 |
172 | if (fs.existsSync(textureURL)) {
173 | const texture = { file: textureURI, width: 0, height: 0 };
174 | const textureAtlasBuffer = fs.readFileSync(textureURL);
175 | modelConfig.textures[i] = texture;
176 |
177 | if (textureAtlasBuffer.toString('ascii', 12, 16) === "CgBI") {
178 | texture.width = textureAtlasBuffer.readUInt32BE(32);
179 | texture.height = textureAtlasBuffer.readUInt32BE(36);
180 | }
181 | else {
182 | texture.width = textureAtlasBuffer.readUInt32BE(16);
183 | texture.height = textureAtlasBuffer.readUInt32BE(20);
184 | }
185 | }
186 | else {
187 | console.log("File does not exist.", textureURL);
188 | }
189 | }
190 |
191 | if (modelConfig.motions) { // Parse animation.
192 | for (const k in modelConfig.motions) {
193 | const motionConfigs = modelConfig.motions[k];
194 | for (const motionConfig of motionConfigs) {
195 | const motionURL = path.join(dirURL, motionConfig.file);
196 | if (fs.existsSync(motionURL)) {
197 | motionConfig.motion = l2ft.parseMotion(fs.readFileSync(motionURL, "utf-8"));
198 | deleteFiles.push(motionURL);
199 | }
200 | else {
201 | console.log("File does not exist.", motionURL);
202 | }
203 | }
204 | }
205 | }
206 |
207 | if (modelConfig.expressions) {
208 | for (const k in modelConfig.expressions) {
209 | const expressionConfig = modelConfig.expressions[k];
210 | const expressionURL = path.join(dirURL, expressionConfig.file);
211 | if (fs.existsSync(expressionURL)) {
212 | expressionConfig.expression = JSON.parse(utils.formatJSONString(fs.readFileSync(expressionURL, "utf-8")));
213 | deleteFiles.push(expressionURL);
214 | }
215 | else {
216 | console.log("File does not exist.", expressionURL);
217 | }
218 | }
219 | }
220 |
221 | const result = fromLive2D(modelConfig);
222 | if (result === null) {
223 | continue;
224 | }
225 |
226 | const outputDirURL = dirURL.replace(input, output);
227 | const outputURL = path.join(outputDirURL, fileName + "_ske.json");
228 | format(result);
229 | console.log(outputURL);
230 |
231 | if (!fs.existsSync(outputDirURL)) {
232 | fs.mkdirsSync(outputDirURL);
233 | }
234 |
235 | if (outputDirURL !== dirURL) {
236 | for (const textureAtlas of result.textureAtlas) {
237 | const rawImageURL = path.join(dirURL, textureAtlas.imagePath);
238 | const outputImageURL = path.join(outputDirURL, textureAtlas.imagePath);
239 | console.log(outputImageURL);
240 |
241 | if (!fs.existsSync(path.dirname(outputImageURL))) {
242 | fs.mkdirsSync(path.dirname(outputImageURL));
243 | }
244 |
245 | if (deleteRaw) {
246 | fs.moveSync(rawImageURL, outputImageURL);
247 | }
248 | else {
249 | fs.copySync(rawImageURL, outputImageURL);
250 | }
251 | }
252 | }
253 |
254 | object.compress(result, dbft.compressConfig);
255 | fs.writeFileSync(
256 | outputURL,
257 | JSON.stringify(result)
258 | );
259 |
260 | if (deleteRaw) {
261 | for (const file of deleteFiles) {
262 | fs.unlinkSync(file);
263 | }
264 | }
265 | }
266 |
267 | break;
268 | }
269 |
270 | case "cocos":
271 | // loadTextureAtlasToData = true;
272 | // megreTextureAtlasToData = true;
273 | // break;
274 |
275 | default:
276 | console.log(`Unknown type: ${type}`);
277 | return;
278 | }
279 |
280 | console.log("Convert complete.");
281 |
282 | if (helper.hasInput()) {
283 | helper.start();
284 | console.log("Waitting for helper.");
285 | }
286 | }
287 |
288 | execute();
--------------------------------------------------------------------------------
/src/convertTo.ts:
--------------------------------------------------------------------------------
1 | #! /usr/bin/env node
2 | import * as fs from "fs-extra";
3 | import * as path from "path";
4 | import * as commander from "commander";
5 | import * as object from "./common/object";
6 | import * as nodeUtils from "./common/nodeUtils";
7 | import * as dbft from "./format/dragonBonesFormat";
8 | import * as spft from "./format/spineFormat";
9 | import * as dbUtils from "./format/utils";
10 | import toFormat from "./action/toFormat";
11 | import toNew from "./action/toNew";
12 | import toBinary from "./action/toBinary";
13 | import toWeb from "./action/toWeb";
14 | import toSpine from "./action/toSpine";
15 | import format from "./action/formatFormat";
16 |
17 | function execute(): void {
18 | const commands = commander
19 | .version("0.1.0")
20 | .option("-i, --input [path]", "Input path")
21 | .option("-o, --output [path]", "Output path")
22 | .option("-t, --type [type]", "Convert to type [binary, new, v45, player, viewer, spine]", /^(binary|new|v45|player|viewer|spine|none)$/i, "none")
23 | .option("-f, --filter [keyword]", "Filter")
24 | .option("-d, --delete", "Delete raw files after convert complete")
25 | .option("-p, --parameter [parameter]", "Parameter")
26 | .parse(process.argv);
27 |
28 | const input = path.resolve(path.normalize(commands["input"] as string || process.cwd()));
29 | const output = "output" in commands ? path.resolve(path.normalize(commands["output"])) : input;
30 | const type = commands["type"] as string || "";
31 | const filter = commands["filter"] as string || "";
32 | const deleteRaw = commands["delete"] as boolean || false;
33 | const parameter = commands["parameter"] as string || "";
34 | let loadTextureAtlasToData = false;
35 | let megreTextureAtlasToData = false;
36 |
37 | switch (type) {
38 | case "binary":
39 | break;
40 |
41 | case "new":
42 | break;
43 |
44 | case "v45":
45 | break;
46 |
47 | case "player":
48 | case "viewer":
49 | loadTextureAtlasToData = true;
50 | megreTextureAtlasToData = true;
51 | break;
52 |
53 | case "spine":
54 | loadTextureAtlasToData = true;
55 | megreTextureAtlasToData = true;
56 | break;
57 |
58 | default:
59 | console.log(`Unknown type: ${type}`);
60 | return;
61 | }
62 |
63 | const files = nodeUtils.filterFileList(input, /\.(json)$/i);
64 | for (const file of files) {
65 | if (filter && file.indexOf(filter) < 0) {
66 | continue;
67 | }
68 |
69 | const dirURL = path.dirname(file);
70 | const fileName = path.basename(file, ".json");
71 | const fileString = fs.readFileSync(file, "utf-8");
72 | let textureAtlasFiles: string[] | null = null;
73 | let textureAtlasImages: string[] | null = null;
74 | let textureAtlases: dbft.TextureAtlas[] | null = null;
75 | const dragonBonesData = toFormat(fileString, () => {
76 | textureAtlasFiles = dbUtils.getTextureAtlases(file);
77 | textureAtlases = textureAtlasFiles.map((v) => {
78 | return getTextureAtlas(v);
79 | });
80 |
81 | return textureAtlases;
82 | });
83 |
84 | if (!dragonBonesData) {
85 | continue;
86 | }
87 |
88 | if (dragonBonesData.textureAtlas.length > 0) {
89 | textureAtlasFiles = null;
90 | textureAtlasImages = dragonBonesData.textureAtlas.map((v) => {
91 | return v.imagePath;
92 | });
93 | textureAtlases = dragonBonesData.textureAtlas;
94 | }
95 | else {
96 | if (!textureAtlasFiles) {
97 | textureAtlasFiles = dbUtils.getTextureAtlases(file);
98 | }
99 | textureAtlasImages = textureAtlasFiles.map((v) => {
100 | return v.replace(".json", ".png");
101 | });
102 |
103 | if (loadTextureAtlasToData && textureAtlasFiles.length > 0) {
104 | textureAtlases = textureAtlasFiles.map((v) => {
105 | return getTextureAtlas(v);
106 | });
107 | }
108 | }
109 |
110 | if (megreTextureAtlasToData && textureAtlases && dragonBonesData.textureAtlas.length === 0) {
111 | for (const textureAtals of textureAtlases) {
112 | dragonBonesData.textureAtlas.push(textureAtals);
113 | }
114 |
115 | textureAtlases = dragonBonesData.textureAtlas;
116 | }
117 |
118 | switch (type) {
119 | case "binary": {
120 | toNew(dragonBonesData, true);
121 | format(dragonBonesData);
122 |
123 | const outputDirURL = dirURL.replace(input, output);
124 | const outputURL = path.join(outputDirURL, fileName + ".dbbin");
125 | const result = toBinary(dragonBonesData);
126 |
127 | if (!fs.existsSync(outputDirURL)) {
128 | fs.mkdirsSync(outputDirURL);
129 | }
130 |
131 | fs.writeFileSync(outputURL, new Buffer(result));
132 | console.log(outputURL);
133 |
134 | if (deleteRaw) {
135 | fs.unlinkSync(file);
136 | }
137 |
138 | if (outputDirURL !== dirURL) {
139 | if (textureAtlasFiles && !megreTextureAtlasToData) {
140 | for (const textureAtlasFile of textureAtlasFiles) {
141 | const outputURL = textureAtlasFile.replace(input, output);
142 |
143 | if (deleteRaw) {
144 | fs.moveSync(textureAtlasFile, outputURL);
145 | }
146 | else {
147 | fs.copySync(textureAtlasFile, outputURL);
148 | }
149 |
150 | console.log(outputURL);
151 | }
152 | }
153 |
154 | for (const textureAtlasImage of textureAtlasImages) {
155 | const outputURL = textureAtlasImage.replace(input, output);
156 |
157 | if (deleteRaw) {
158 | fs.moveSync(textureAtlasImage, outputURL);
159 | }
160 | else {
161 | fs.copySync(textureAtlasImage, outputURL);
162 | }
163 |
164 | console.log(outputURL);
165 | }
166 | }
167 | else if (textureAtlasFiles && megreTextureAtlasToData) {
168 | for (const textureAtlasFile of textureAtlasFiles) {
169 | fs.removeSync(textureAtlasFile);
170 | }
171 | }
172 |
173 | break;
174 | }
175 |
176 | case "new": {
177 | toNew(dragonBonesData, false);
178 | format(dragonBonesData);
179 | object.compress(dragonBonesData, dbft.compressConfig);
180 |
181 | const outputDirURL = dirURL.replace(input, output);
182 | const outputURL = path.join(outputDirURL, fileName + ".json");
183 | const result = JSON.stringify(dragonBonesData);
184 |
185 | if (!fs.existsSync(outputDirURL)) {
186 | fs.mkdirsSync(outputDirURL);
187 | }
188 |
189 | fs.writeFileSync(outputURL, new Buffer(result));
190 | console.log(outputURL);
191 |
192 | if (outputDirURL !== dirURL) {
193 | if (deleteRaw) {
194 | fs.unlinkSync(file);
195 | }
196 |
197 | if (textureAtlasFiles && !megreTextureAtlasToData) {
198 | for (const textureAtlasFile of textureAtlasFiles) {
199 | const outputURL = textureAtlasFile.replace(input, output);
200 |
201 | if (deleteRaw) {
202 | fs.moveSync(textureAtlasFile, outputURL);
203 | }
204 | else {
205 | fs.copySync(textureAtlasFile, outputURL);
206 | }
207 |
208 | console.log(outputURL);
209 | }
210 | }
211 |
212 | for (const textureAtlasImage of textureAtlasImages) {
213 | const outputURL = textureAtlasImage.replace(input, output);
214 |
215 | if (deleteRaw) {
216 | fs.moveSync(textureAtlasImage, outputURL);
217 | }
218 | else {
219 | fs.copySync(textureAtlasImage, outputURL);
220 | }
221 |
222 | console.log(outputURL);
223 | }
224 | }
225 | else if (textureAtlasFiles && megreTextureAtlasToData) {
226 | for (const textureAtlasFile of textureAtlasFiles) {
227 | fs.removeSync(textureAtlasFile);
228 | }
229 | }
230 |
231 | break;
232 | }
233 |
234 | case "player":
235 | case "viewer": {
236 | toNew(dragonBonesData, true);
237 | format(dragonBonesData);
238 |
239 | const outputDirURL = dirURL.replace(input, output);
240 | const outputURL = path.join(outputDirURL, fileName + ".html");
241 | const result = toWeb({
242 | data: new Buffer(toBinary(dragonBonesData)),
243 | textureAtlases: textureAtlasImages.map((v) => {
244 | const imagePath = path.join(dirURL, v);
245 | if (fs.existsSync(imagePath)) {
246 | return fs.readFileSync(imagePath);
247 | }
248 |
249 | return null;
250 | }),
251 | config: {
252 | isLocal: parameter !== "alone",
253 | isAlone: parameter === "alone",
254 | }
255 | }, type === "player");
256 |
257 | if (!fs.existsSync(outputDirURL)) {
258 | fs.mkdirsSync(outputDirURL);
259 | }
260 |
261 | fs.writeFileSync(outputURL, new Buffer(result));
262 | console.log(outputURL);
263 |
264 | if (deleteRaw) {
265 | fs.unlinkSync(file);
266 |
267 | if (textureAtlasFiles) {
268 | for (const textureAtlasFile of textureAtlasFiles) {
269 | fs.unlinkSync(textureAtlasFile);
270 | }
271 | }
272 |
273 | for (const textureAtlasImage of textureAtlasImages) {
274 | fs.unlinkSync(textureAtlasImage);
275 | }
276 | }
277 |
278 | break;
279 | }
280 |
281 | case "spine": {
282 | toNew(dragonBonesData, true);
283 | format(dragonBonesData);
284 |
285 | const outputDirURL = dirURL.replace(input, output);
286 | const result = toSpine(dragonBonesData, "3.6.0", !output);
287 | const suffix = outputDirURL === dirURL ? "_spine" : "";
288 | dragonBonesData.name = fileName.replace("_ske", "");
289 | console.log(dragonBonesData.name);
290 |
291 | if (!fs.existsSync(outputDirURL)) {
292 | fs.mkdirsSync(outputDirURL);
293 | }
294 |
295 | for (const spine of result.spines) {
296 | object.compress(spine, spft.compressConfig);
297 | const outputURL = path.join(outputDirURL, (result.spines.length > 1 ? dragonBonesData.name + "_" + spine.skeleton.name : dragonBonesData.name) + suffix + ".json");
298 | delete spine.skeleton.name; // Delete keep name.
299 | fs.writeFileSync(outputURL, JSON.stringify(spine));
300 | console.log(outputURL);
301 | }
302 |
303 | const outputURL = path.join(outputDirURL, dragonBonesData.name + suffix + ".atlas");
304 | fs.writeFileSync(outputURL, result.textureAtlas);
305 | console.log(outputURL);
306 |
307 | if (deleteRaw) {
308 | fs.unlinkSync(file);
309 |
310 | if (textureAtlasFiles) {
311 | for (const textureAtlasFile of textureAtlasFiles) {
312 | fs.unlinkSync(textureAtlasFile);
313 | }
314 | }
315 | }
316 |
317 | if (outputDirURL !== dirURL) {
318 | let index = 0;
319 | for (const textureAtlasImage of textureAtlasImages) {
320 | const outputURL = path.join(
321 | path.dirname(textureAtlasImage.replace(input, output)),
322 | dragonBonesData.name + suffix + (textureAtlasImages.length > 1 ? "_" + index : "") + ".png"
323 | );
324 |
325 | if (fs.existsSync(textureAtlasImage)) {
326 | if (deleteRaw) {
327 | fs.moveSync(textureAtlasImage, outputURL);
328 | }
329 | else {
330 | fs.copySync(textureAtlasImage, outputURL);
331 | }
332 | }
333 |
334 | console.log(outputURL);
335 | index++;
336 | }
337 | }
338 | break;
339 | }
340 |
341 | default:
342 | break;
343 | }
344 | }
345 |
346 | console.log("Convert complete.");
347 | }
348 |
349 | function getTextureAtlas(textureAtlasFile: string): dbft.TextureAtlas {
350 | const textureAtlas = new dbft.TextureAtlas();
351 | object.copyObjectFrom(JSON.parse(fs.readFileSync(textureAtlasFile, "utf-8")), textureAtlas, dbft.copyConfig);
352 |
353 | return textureAtlas;
354 | }
355 |
356 | execute();
--------------------------------------------------------------------------------
/src/format/dragonBonesFormatV23.ts:
--------------------------------------------------------------------------------
1 | import * as utils from "../common/utils";
2 | import { Transform, ColorTransform } from "./geom";
3 | import * as dbft from "./dragonBonesFormat";
4 | /**
5 | * DragonBones format v23.
6 | */
7 | export class DragonBones {
8 | isGlobal: boolean = true;
9 | frameRate: number = 0;
10 | name: string = "";
11 | version: string = "";
12 | readonly armature: Armature[] = [];
13 | }
14 |
15 | export class Armature {
16 | name: string = "";
17 | readonly bone: Bone[] = [];
18 | readonly skin: Skin[] = [];
19 | readonly animation: Animation[] = [];
20 |
21 | getBone(name: string): Bone | null {
22 | for (const bone of this.bone) {
23 | if (bone.name === name) {
24 | return bone;
25 | }
26 | }
27 |
28 | return null;
29 | }
30 | }
31 |
32 | export class Bone {
33 | name: string = "";
34 | parent: string = "";
35 | readonly transform: Transform = new Transform();
36 | }
37 |
38 | export class Skin {
39 | name: string = "default";
40 | readonly slot: Slot[] = [];
41 | }
42 |
43 | export class Slot {
44 | blendMode: string = dbft.BlendMode[dbft.BlendMode.Normal].toLowerCase();
45 | z: number = 0;
46 | displayIndex: number = 0;
47 | name: string = "";
48 | parent: string = "";
49 | readonly colorTransform: ColorTransform = new ColorTransform();
50 | readonly display: Display[] = [];
51 | }
52 |
53 | export abstract class Display {
54 | type: string = dbft.DisplayType[dbft.DisplayType.Image].toLowerCase();
55 | name: string = "";
56 | readonly transform: Transform = new Transform();
57 | }
58 |
59 | export class ImageDisplay extends Display {
60 | constructor(isDefault: boolean = false) {
61 | super();
62 | if (!isDefault) {
63 | this.type = dbft.DisplayType[dbft.DisplayType.Image].toLowerCase();
64 | }
65 | }
66 | }
67 |
68 | export class ArmatureDisplay extends Display {
69 | constructor(isDefault: boolean = false) {
70 | super();
71 | if (!isDefault) {
72 | this.type = dbft.DisplayType[dbft.DisplayType.Armature].toLowerCase();
73 | }
74 | }
75 | }
76 |
77 | export abstract class Timeline {
78 | scale: number = 1.0;
79 | offset: number = 0.0;
80 | name: string = "";
81 | readonly frame: T[] = [];
82 | }
83 |
84 | export class Animation {
85 | autoTween: boolean = true;
86 | tweenEasing: number | null = null;
87 | duration: number = 1;
88 | loop: number = 1;
89 | scale: number = 1.0;
90 | fadeInTime: number = 0.0;
91 | name: string = "default";
92 | readonly frame: AnimationFrame[] = [];
93 | readonly timeline: AllTimeline[] = [];
94 | }
95 |
96 | export class AllTimeline extends Timeline {
97 | }
98 |
99 | export abstract class Frame {
100 | duration: number = 1;
101 | }
102 |
103 | export abstract class TweenFrame extends Frame {
104 | tweenEasing: number | null = null;
105 | readonly curve: number[] = [];
106 | }
107 |
108 | export class AnimationFrame extends Frame {
109 | action: string = "";
110 | event: string = "";
111 | sound: string = "";
112 | }
113 |
114 | export class AllFrame extends TweenFrame {
115 | hide: boolean = false;
116 | tweenRotate: number = 0;
117 | displayIndex: number = 0;
118 | action: string = "";
119 | event: string = "";
120 | sound: string = "";
121 | readonly transform: Transform = new Transform();
122 | readonly colorTransform: ColorTransform = new ColorTransform();
123 | }
124 |
125 | export const copyConfig = [
126 | DragonBones, {
127 | armature: Armature,
128 | textureAtlas: dbft.TextureAtlas
129 | },
130 | Armature, {
131 | bone: Bone,
132 | skin: Skin,
133 | animation: Animation
134 | },
135 | Bone, {
136 | transform: Transform
137 | },
138 | Slot, {
139 | display: [
140 | function (display: any): { new(): Display } | null {
141 | let type = display.type;
142 | if (type !== undefined) {
143 | if (typeof type === "string") {
144 | type = utils.getEnumFormString(dbft.DisplayType, type, dbft.DisplayType.Image);
145 | }
146 | }
147 | else {
148 | type = dbft.DisplayType.Image;
149 | }
150 |
151 | switch (type) {
152 | case dbft.DisplayType.Image:
153 | return ImageDisplay;
154 |
155 | case dbft.DisplayType.Armature:
156 | return ArmatureDisplay;
157 | }
158 |
159 | return null;
160 | },
161 | Function
162 | ]
163 | },
164 | Skin, {
165 | slot: Slot
166 | },
167 | Animation, {
168 | frame: AnimationFrame,
169 | timeline: AllTimeline
170 | },
171 | AllTimeline, {
172 | frame: AllFrame
173 | },
174 | AllFrame, {
175 | transform: Transform,
176 | colorTransform: ColorTransform
177 | },
178 | dbft.TextureAtlas, {
179 | SubTexture: dbft.Texture
180 | }
181 | ];
--------------------------------------------------------------------------------
/src/format/geom.ts:
--------------------------------------------------------------------------------
1 | export const PI_D: number = Math.PI * 2.0;
2 | export const PI_H: number = Math.PI / 2.0;
3 | export const PI_Q: number = Math.PI / 4.0;
4 | export const RAD_DEG: number = 180.0 / Math.PI;
5 | export const DEG_RAD: number = Math.PI / 180.0;
6 |
7 | export interface Position {
8 | x: number;
9 | y: number;
10 | }
11 |
12 | export function normalizeRadian(value: number): number {
13 | value = (value + Math.PI) % (PI_D);
14 | value += value > 0.0 ? -Math.PI : Math.PI;
15 |
16 | return value;
17 | }
18 |
19 | export function normalizeDegree(value: number): number {
20 | value = (value + 180.0) % (180.0 * 2.0);
21 | value += value > 0.0 ? -180.0 : 180.0;
22 |
23 | return value;
24 | }
25 |
26 | export function distance(pA: Position, pB: Position): number {
27 | const dX = pB.x - pA.x;
28 | const dY = pB.y - pA.y;
29 |
30 | return Math.sqrt(dX * dX + dY * dY);
31 | }
32 |
33 | export function multiply(pA: Position, pB: Position, pC: Position): number {
34 | return ((pA.x - pC.x) * (pB.y - pC.y) - (pB.x - pC.x) * (pA.y - pC.y));
35 | }
36 |
37 | export class Matrix {
38 | public constructor(
39 | public a: number = 1.0, public b: number = 0.0,
40 | public c: number = 0.0, public d: number = 1.0,
41 | public tx: number = 0.0, public ty: number = 0.0
42 | ) {
43 | }
44 |
45 | public copyFrom(value: Matrix): Matrix {
46 | this.a = value.a;
47 | this.b = value.b;
48 | this.c = value.c;
49 | this.d = value.d;
50 | this.tx = value.tx;
51 | this.ty = value.ty;
52 |
53 | return this;
54 | }
55 |
56 | public copyFromArray(value: number[], offset: number = 0): Matrix {
57 | this.a = value[offset];
58 | this.b = value[offset + 1];
59 | this.c = value[offset + 2];
60 | this.d = value[offset + 3];
61 | this.tx = value[offset + 4];
62 | this.ty = value[offset + 5];
63 |
64 | return this;
65 | }
66 |
67 | public identity(): Matrix {
68 | this.a = this.d = 1.0;
69 | this.b = this.c = 0.0;
70 | this.tx = this.ty = 0.0;
71 |
72 | return this;
73 | }
74 |
75 | public rotate(radian: number): Matrix {
76 | const u = Math.cos(radian);
77 | const v = Math.sin(radian);
78 | const ta = this.a;
79 | const tb = this.b;
80 | const tc = this.c;
81 | const td = this.d;
82 | const ttx = this.tx;
83 | const tty = this.ty;
84 | this.a = ta * u - tb * v;
85 | this.b = ta * v + tb * u;
86 | this.c = tc * u - td * v;
87 | this.d = tc * v + td * u;
88 | this.tx = ttx * u - tty * v;
89 | this.ty = ttx * v + tty * u;
90 |
91 | return this;
92 | }
93 |
94 | public concat(value: Matrix): Matrix {
95 | let aA = this.a * value.a;
96 | let bA = 0.0;
97 | let cA = 0.0;
98 | let dA = this.d * value.d;
99 | let txA = this.tx * value.a + value.tx;
100 | let tyA = this.ty * value.d + value.ty;
101 |
102 | if (this.b !== 0.0 || this.c !== 0.0) {
103 | aA += this.b * value.c;
104 | bA += this.b * value.d;
105 | cA += this.c * value.a;
106 | dA += this.c * value.b;
107 | }
108 |
109 | if (value.b !== 0.0 || value.c !== 0.0) {
110 | bA += this.a * value.b;
111 | cA += this.d * value.c;
112 | txA += this.ty * value.c;
113 | tyA += this.tx * value.b;
114 | }
115 |
116 | this.a = aA;
117 | this.b = bA;
118 | this.c = cA;
119 | this.d = dA;
120 | this.tx = txA;
121 | this.ty = tyA;
122 |
123 | return this;
124 | }
125 |
126 | public invert(): Matrix {
127 | let aA = this.a;
128 | let bA = this.b;
129 | let cA = this.c;
130 | let dA = this.d;
131 | const txA = this.tx;
132 | const tyA = this.ty;
133 |
134 | if (bA === 0.0 && cA === 0.0) {
135 | this.b = this.c = 0.0;
136 | if (aA === 0.0 || dA === 0.0) {
137 | this.a = this.b = this.tx = this.ty = 0.0;
138 | }
139 | else {
140 | aA = this.a = 1.0 / aA;
141 | dA = this.d = 1.0 / dA;
142 | this.tx = -aA * txA;
143 | this.ty = -dA * tyA;
144 | }
145 |
146 | return this;
147 | }
148 |
149 | let determinant = aA * dA - bA * cA;
150 | if (determinant === 0.0) {
151 | this.a = this.d = 1.0;
152 | this.b = this.c = 0.0;
153 | this.tx = this.ty = 0.0;
154 |
155 | return this;
156 | }
157 |
158 | determinant = 1.0 / determinant;
159 | let k = this.a = dA * determinant;
160 | bA = this.b = -bA * determinant;
161 | cA = this.c = -cA * determinant;
162 | dA = this.d = aA * determinant;
163 | this.tx = -(k * txA + cA * tyA);
164 | this.ty = -(bA * txA + dA * tyA);
165 |
166 | return this;
167 | }
168 |
169 | public transformPoint(x: number, y: number, result: Position, delta: boolean = false): void {
170 | result.x = this.a * x + this.c * y;
171 | result.y = this.b * x + this.d * y;
172 |
173 | if (!delta) {
174 | result.x += this.tx;
175 | result.y += this.ty;
176 | }
177 | }
178 | }
179 |
180 | export class Transform {
181 |
182 | public constructor(
183 | public x: number = 0.0,
184 | public y: number = 0.0,
185 | public skX: number = 0.0,
186 | public skY: number = 0.0,
187 | public scX: number = 1.0,
188 | public scY: number = 1.0,
189 | public pX: number = 0.0, // Deprecated.
190 | public pY: number = 0.0 // Deprecated.
191 | ) {
192 | }
193 |
194 | public toString(): string {
195 | return `${this.x}_${this.y}_${this.skX}_${this.skY}_${this.scX}_${this.scY}`;
196 | }
197 |
198 | public toFixed(): void {
199 | this.x = Number(this.x.toFixed(2));
200 | this.y = Number(this.y.toFixed(2));
201 | this.skX = Number(this.skX.toFixed(2));
202 | this.skY = Number(this.skY.toFixed(2));
203 | this.scX = Number(this.scX.toFixed(4));
204 | this.scY = Number(this.scY.toFixed(4));
205 | }
206 |
207 | public copyFrom(value: Transform): Transform {
208 | this.x = value.x;
209 | this.y = value.y;
210 | this.skX = value.skX;
211 | this.skY = value.skY;
212 | this.scX = value.scX;
213 | this.scY = value.scY;
214 |
215 | return this;
216 | }
217 |
218 | public equal(value: Transform): boolean {
219 | return this.x === value.x && this.y === value.y &&
220 | this.skX === value.skY && this.skY === value.skY &&
221 | this.scX === value.scX && this.scY === value.scY;
222 | }
223 |
224 | public identity(): Transform {
225 | this.x = this.y = this.skX = this.skY = 0.0;
226 | this.scX = this.scY = 1.0;
227 |
228 | return this;
229 | }
230 |
231 | public fromMatrix(matrix: Matrix): Transform {
232 |
233 | this.x = matrix.tx;
234 | this.y = matrix.ty;
235 | const backupScaleX = this.scX, backupScaleY = this.scY;
236 | let skX = Math.atan(-matrix.c / matrix.d);
237 | let skY = Math.atan(matrix.b / matrix.a);
238 | this.scX = (skY > -PI_Q && skY < PI_Q) ? matrix.a / Math.cos(skY) : matrix.b / Math.sin(skY);
239 | this.scY = (skX > -PI_Q && skX < PI_Q) ? matrix.d / Math.cos(skX) : -matrix.c / Math.sin(skX);
240 |
241 | if (backupScaleX >= 0.0 && this.scX < 0.0) {
242 | this.scX = -this.scX;
243 | skY = normalizeRadian(skY - Math.PI);
244 | }
245 |
246 | if (backupScaleY >= 0.0 && this.scY < 0.0) {
247 | this.scY = -this.scY;
248 | skX = normalizeRadian(skX - Math.PI);
249 | }
250 |
251 | this.skX = skX * RAD_DEG;
252 | this.skY = skY * RAD_DEG;
253 |
254 | return this;
255 | }
256 |
257 | public toMatrix(matrix: Matrix): Transform {
258 | const skX = this.skX * DEG_RAD;
259 | const skY = this.skY * DEG_RAD;
260 | matrix.a = Math.cos(skY) * this.scX;
261 | matrix.b = Math.sin(skY) * this.scX;
262 | matrix.c = -Math.sin(skX) * this.scY;
263 | matrix.d = Math.cos(skX) * this.scY;
264 | matrix.tx = this.x;
265 | matrix.ty = this.y;
266 |
267 | return this;
268 | }
269 | }
270 |
271 | export class ColorTransform {
272 | public constructor(
273 | public aM: number = 100,
274 | public rM: number = 100, public gM: number = 100, public bM: number = 100,
275 | public aO: number = 0,
276 | public rO: number = 0, public gO: number = 0, public bO: number = 0
277 | ) {
278 | }
279 |
280 | public toString(): string {
281 | return `${this.aM}_${this.rM}_${this.gM}_${this.bM}_${this.aO}_${this.rO}_${this.gO}_${this.bO}`;
282 | }
283 |
284 | public toFixed(): void {
285 | this.aM = Math.round(this.aM);
286 | this.rM = Math.round(this.rM);
287 | this.gM = Math.round(this.gM);
288 | this.bM = Math.round(this.bM);
289 | this.aO = Math.round(this.aO);
290 | this.rO = Math.round(this.rO);
291 | this.gO = Math.round(this.gO);
292 | this.bO = Math.round(this.bO);
293 | }
294 |
295 | public copyFrom(value: ColorTransform): void {
296 | this.aM = value.aM;
297 | this.rM = value.rM;
298 | this.gM = value.gM;
299 | this.bM = value.bM;
300 | this.aO = value.aO;
301 | this.rO = value.rO;
302 | this.gO = value.gO;
303 | this.bO = value.bO;
304 | }
305 |
306 | public copyFromRGBA(value: number): void {
307 | this.rM = Math.round(((0xFF000000 & value) >>> 24) / 255 * 100);
308 | this.gM = Math.round(((0x00FF0000 & value) >>> 16) / 255 * 100);
309 | this.bM = Math.round(((0x0000FF00 & value) >>> 8) / 255 * 100);
310 | this.aM = Math.round((0x000000FF & value) / 255 * 100);
311 | }
312 |
313 | public identity(): void {
314 | this.aM = this.rM = this.gM = this.bM = 100;
315 | this.aO = this.rO = this.gO = this.bO = 0;
316 | }
317 |
318 | public equal(value: ColorTransform): boolean {
319 | return this.aM === value.aM && this.rM === value.rM && this.gM === value.gM && this.bM === value.bM &&
320 | this.aO === value.aO && this.rO === value.rO && this.gO === value.gO && this.bO === value.bO;
321 | }
322 | }
323 |
324 | export class Point implements Position {
325 | public constructor(
326 | public x: number = 0.0,
327 | public y: number = 0.0
328 | ) {
329 | }
330 |
331 | public toString(): string {
332 | return "[object Point x: " + this.x + " y: " + this.y + " ]";
333 | }
334 |
335 | public clear(): void {
336 | this.x = this.y = 0.0;
337 | }
338 |
339 | public copyFrom(value: Position): this {
340 | this.x = value.x;
341 | this.y = value.y;
342 |
343 | return this;
344 | }
345 |
346 | public setTo(x: number, y: number): this {
347 | this.x = x;
348 | this.y = y;
349 |
350 | return this;
351 | }
352 |
353 | public polar(length: number, radian: number): this {
354 | this.x = length * Math.cos(radian);
355 | this.y = length * Math.sin(radian);
356 |
357 | return this;
358 | }
359 | }
360 |
361 | export class Rectangle implements Position {
362 | public constructor(
363 | public x: number = 0.0,
364 | public y: number = 0.0,
365 | public width: number = 0.0,
366 | public height: number = 0.0
367 | ) {
368 | }
369 |
370 | public toString(): string {
371 | return "[object Rectangle x: " + this.x + " y: " + this.y + " width: " + this.width + " height: " + this.height + " ]";
372 | }
373 |
374 | public toFixed(): void {
375 | this.x = Number(this.x.toFixed(2));
376 | this.y = Number(this.y.toFixed(2));
377 | this.width = Number(this.width.toFixed(2));
378 | this.height = Number(this.height.toFixed(2));
379 | }
380 |
381 | public clear(): void {
382 | this.x = this.y = this.width = this.height = 0.0;
383 | }
384 |
385 | public copyFrom(value: this): this {
386 | this.x = value.x;
387 | this.y = value.y;
388 | this.width = value.width;
389 | this.height = value.height;
390 |
391 | return this;
392 | }
393 |
394 | public setTo(x: number, y: number, width: number, height: number): this {
395 | this.x = x;
396 | this.y = y;
397 | this.width = width;
398 | this.height = height;
399 |
400 | return this;
401 | }
402 | }
403 |
404 | export const helpMatrixA: Matrix = new Matrix();
405 | export const helpMatrixB: Matrix = new Matrix();
406 | export const helpTransformA: Transform = new Transform();
407 | export const helpTransformB: Transform = new Transform();
408 | export const helpPointA: Point = new Point();
409 | export const helpPointB: Point = new Point();
--------------------------------------------------------------------------------
/src/format/resFormat.ts:
--------------------------------------------------------------------------------
1 | export enum ResourceType {
2 | JSON,
3 | BIN,
4 | Image
5 | }
6 |
7 | class ResourceGroup {
8 | public name: string = "";
9 | public keys: string = "";
10 | }
11 |
12 | class Resource {
13 | public name: string = "";
14 | public type: string = "";
15 | public url: string = "";
16 | }
17 |
18 | export class ResourceJSON {
19 | public readonly resources: Resource[] = [];
20 | public readonly groups: ResourceGroup[] = [];
21 |
22 | public clear(): void {
23 | this.resources.length = 0;
24 | this.groups.length = 0;
25 | }
26 |
27 | public getResource(name: string): Resource | null {
28 | for (let i = 0, l = this.resources.length; i < l; ++i) {
29 | const resource = this.resources[i];
30 | if (resource.name === name) {
31 | return resource;
32 | }
33 | }
34 | return null;
35 | }
36 |
37 | public getResourceGroup(name: string): ResourceGroup | null {
38 | for (let i = 0, l = this.groups.length; i < l; ++i) {
39 | const group = this.groups[i];
40 | if (group.name === name) {
41 | return group;
42 | }
43 | }
44 | return null;
45 | }
46 |
47 | public addResource(name: string, type: ResourceType, url: string, ...groupNames: string[]): void {
48 | const resource = this.getResource(name) || new Resource();
49 |
50 | resource.name = name;
51 | resource.type = ResourceType[type].toLowerCase();
52 | resource.url = url;
53 |
54 | if (this.resources.indexOf(resource) < 0) {
55 | this.resources.push(resource);
56 | }
57 |
58 | if (groupNames && groupNames.length) {
59 | for (let i = 0, l = groupNames.length; i < l; ++i) {
60 | const groupName = groupNames[i];
61 | const resourceGroup = this.getResourceGroup(groupName) || new ResourceGroup();
62 | resourceGroup.name = groupName;
63 |
64 | if (resourceGroup.keys) {
65 | const keys = resourceGroup.keys.split(",");
66 | if (keys.indexOf(name) < 0) {
67 | keys.push(name);
68 | }
69 |
70 | resourceGroup.keys = keys.join(",");
71 | }
72 | else {
73 | resourceGroup.keys = name;
74 | }
75 |
76 | if (this.groups.indexOf(resourceGroup) < 0) {
77 | this.groups.push(resourceGroup);
78 | }
79 | }
80 | }
81 | }
82 | }
--------------------------------------------------------------------------------
/src/format/spineFormat.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * Spine format.
3 | */
4 | export type TransformType = "normal" | "onlyTranslation" | "noRotationOrReflection" | "noScale" | "noScaleOrReflection";
5 | export type AttachmentType = "region" | "mesh" | "linkedmesh" | "boundingbox" | "path" | "point" | "clipping" | "skinnedmesh";
6 | export type BlendMode = "normal" | "additive" | "multiply" | "screen";
7 |
8 | type Map = {
9 | [key: string]: T;
10 | };
11 |
12 | export function isSpineString(string: string): boolean {
13 | return string.indexOf("skeleton") > 0 && string.indexOf("spine") > 0;
14 | }
15 |
16 | export class Spine {
17 | readonly skeleton: Skeleton = new Skeleton();
18 | readonly bones: Bone[] = [];
19 | readonly slots: Slot[] = [];
20 | readonly ik: IKConstraint[] = [];
21 | readonly transform: TransformConstraint[] = [];
22 | readonly path: PathConstraint[] = [];
23 | readonly skins: Map