├── .gitignore
├── .npmignore
├── menus
└── live-2d.cson
├── package.json
├── keymaps
└── live-2d.cson
├── styles
└── atom-live2d.less
├── index.html
├── src
├── LAppDefine.js
├── utils
│ ├── MatrixStack.js
│ └── ModelSettingJson.js
├── PlatformManager.js
├── LAppLive2DManager.js
├── AtomLive2D.js
├── index.js
└── LAppModel.js
├── README.md
├── LICENSE.md
└── lib
└── live2d_framework.js
/.gitignore:
--------------------------------------------------------------------------------
1 | assets/
2 | node_modules/
3 |
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | npm-debug.log
3 | node_modules
4 |
--------------------------------------------------------------------------------
/menus/live-2d.cson:
--------------------------------------------------------------------------------
1 | # See https://atom.io/docs/latest/creating-a-package#menus for more details
2 | 'menu': [
3 | {
4 | 'label': 'Packages'
5 | 'submenu': [
6 | 'label': 'Live2D'
7 | 'submenu': [
8 | { 'label': 'Toggle', 'command': 'atom-live2d:toggle' },
9 | ]
10 | ]
11 | }
12 | ]
13 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "atom-live2d",
3 | "main": "./src/index.js",
4 | "version": "0.1.0",
5 | "description": "A package which adds live2d models to your atom!",
6 | "repository": "https://github.com/if-Team/atom-live2d",
7 | "license": "MIT",
8 | "engines": {
9 | "atom": ">1.47.0"
10 | },
11 | "dependencies": {
12 | "cron": "^1.1.0"
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/keymaps/live-2d.cson:
--------------------------------------------------------------------------------
1 | # Keybindings require three things to be fully defined: A selector that is
2 | # matched against the focused element, the keystroke and the command to
3 | # execute.
4 | #
5 | # Below is a basic keybinding which registers on all platforms by applying to
6 | # the root workspace element.
7 |
8 | # For more detailed documentation see
9 | # https://atom.io/docs/latest/advanced/keymaps
10 | 'atom-text-editor':
11 | 'ctrl-alt-k': 'atom-live2d:toggle'
12 |
--------------------------------------------------------------------------------
/styles/atom-live2d.less:
--------------------------------------------------------------------------------
1 | // The ui-variables file is provided by base themes provided by Atom.
2 | //
3 | // See https://github.com/atom/atom-dark-ui/blob/master/stylesheets/ui-variables.less
4 | // for a full listing of what's available.
5 | @import "ui-variables";
6 |
7 | .live-2d iframe#live2d {
8 | opacity: 0.3;
9 | bottom: 0;
10 | right: 0;
11 | position: absolute;
12 | z-index: 2;
13 | border: none;
14 | display: inline-block;
15 | pointer-events: none;
16 | }
17 | /*.live-2d {
18 | .item-views > :not(div) {
19 | /deep/ .editor--private:not(.mini) .scroll-view {
20 | #glcanvas {
21 | opacity: 0.3;
22 | bottom: 0;
23 | right: 0;
24 | position: absolute;
25 | z-index: 2;
26 | }
27 | }
28 | }
29 | }*/
30 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | WebGL
5 |
12 |
13 |
14 |
18 |
19 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
--------------------------------------------------------------------------------
/src/LAppDefine.js:
--------------------------------------------------------------------------------
1 | var LAppDefine = {
2 |
3 | // デバッグ。trueのときにログを表示する。
4 | DEBUG_LOG : false,
5 | DEBUG_MOUSE_LOG : false, // マウス関連の冗長なログ
6 | // DEBUG_DRAW_HIT_AREA : false, // 当たり判定の可視化
7 | // DEBUG_DRAW_ALPHA_MODEL : false, // 半透明のモデル描画を行うかどうか。
8 |
9 | // 全体の設定
10 |
11 | // 画面
12 | VIEW_MAX_SCALE : 2,
13 | VIEW_MIN_SCALE : 0.8,
14 |
15 | VIEW_LOGICAL_LEFT : -1,
16 | VIEW_LOGICAL_RIGHT : 1,
17 |
18 | VIEW_LOGICAL_MAX_LEFT : -2,
19 | VIEW_LOGICAL_MAX_RIGHT : 2,
20 | VIEW_LOGICAL_MAX_BOTTOM : -2,
21 | VIEW_LOGICAL_MAX_TOP : 2,
22 |
23 | // モーションの優先度定数
24 | PRIORITY_NONE : 0,
25 | PRIORITY_IDLE : 1,
26 | PRIORITY_NORMAL : 2,
27 | PRIORITY_FORCE : 3,
28 |
29 | // モデルの後ろにある背景の画像ファイル
30 | // モデル定義
31 |
32 | // 外部定義ファイル(json)と合わせる
33 | MOTION_GROUP_IDLE : "idle", // アイドリング
34 | MOTION_GROUP_TAP_BODY : "tap_body", // 体をタップしたとき
35 | MOTION_GROUP_FLICK_HEAD : "flick_head", // 頭を撫でた時
36 | MOTION_GROUP_PINCH_IN : "pinch_in", // 拡大した時
37 | MOTION_GROUP_PINCH_OUT : "pinch_out", // 縮小した時
38 | MOTION_GROUP_SHAKE : "shake", // シェイク
39 |
40 | // 外部定義ファイル(json)と合わせる
41 | HIT_AREA_HEAD : "head",
42 | HIT_AREA_BODY : "body"
43 |
44 | };
45 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # atom-live2d
2 | A package which adds live2d models to your atom!
3 |
4 | ## Features
5 | - [x] Shows a Live2D Model.
6 | - [x] Watches your cursor.
7 | - [x] Supports voice.
8 | - [x] Plays a motion on notification.
9 |
10 | ## How to Install atom-live2d
11 | ```bash
12 | $ apm install atom-live2d
13 | ```
14 |
15 | ## Set the Live2D model
16 | Put the `model.json` of live 2d model address in the settings.
17 |
18 | - Example : `~/assets/model_name/model_name.model.json`, type `model_name/model_name.model.json`.
19 |
20 | ### Example: [`hibiki.model.json`](http://sandwichproject.com)
21 | ```json
22 | {
23 | "version":"Sample 1.0.0",
24 | "model":"hibiki.moc",
25 | "textures":[
26 | "hibiki.1024/texture_00.png",
27 | "hibiki.1024/texture_01.png",
28 | "hibiki.1024/texture_02.png"
29 | ],
30 | "motions":{
31 | "null":[
32 | {
33 | "file":"motions/hibiki_01.mtn",
34 | "sound":"voice/hibiki_01.mp3"
35 | },
36 | {
37 | "file":"motions/hibiki_02.mtn",
38 | "sound":"voice/hibiki_02.mp3"
39 | },
40 | {
41 | "file":"motions/hibiki_03.mtn",
42 | "sound":"voice/hibiki_03.mp3"
43 | },
44 | {
45 | "file":"motions/hibiki_04.mtn",
46 | "sound":"voice/hibiki_04.mp3"
47 | },
48 | {
49 | "file":"motions/hibiki_05.mtn",
50 | "sound":"voice/hibiki_05.mp3"
51 | }
52 | ],
53 | "idle":[
54 | {
55 | "file":"motions/idle_01.mtn"
56 | },
57 | {
58 | "file":"motions/idle_02.mtn"
59 | },
60 | {
61 | "file":"motions/idle_03.mtn"
62 | },
63 | {
64 | "file":"motions/idle_04.mtn"
65 | }
66 | ]
67 | },
68 | "expressions":[
69 | {"name":"ANGRY","file":"expressions/f01.exp.json"},
70 | {"name":"DOWN","file":"expressions/f02.exp.json"},
71 | {"name":"FUN","file":"expressions/f03.exp.json"},
72 | {"name":"NOMAL.mtn","file":"expressions/f04.exp.json"},
73 | {"name":"SAD","file":"expressions/f05.exp.json"},
74 | {"name":"F_SURPRISE","file":"expressions/f06.exp.json"}
75 | ],
76 | "notification":{
77 | "angry": [
78 | {"file":"motions/hibiki_01.mtn"}
79 | ],
80 | "fun": [
81 | {"file":"motions/hibiki_02.mtn"}
82 | ],
83 | "sad": [
84 | {"file":"motions/hibiki_03.mtn"}
85 | ],
86 | "surprise": [
87 | {"file":"motions/hibiki_04.mtn"},
88 | {"file":"motions/hibiki_05.mtn"}
89 | ],
90 | "": [
91 | {"file":"motions/idle_01.mtn"},
92 | {"file":"motions/idle_02.mtn"},
93 | {"file":"motions/idle_03.mtn"},
94 | {"file":"motions/idle_04.mtn"}
95 | ],
96 | "idle": [
97 | {"file":"motions/idle_01.mtn"}
98 | ]
99 | },
100 | "physics":"hibiki.physics.json"
101 | }
102 | ```
103 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | ```
2 | Copyright (c) 2016 HelloWorld017
3 |
4 | Permission is hereby granted, free of charge, to any person obtaining
5 | a copy of this software and associated documentation files (the
6 | "Software"), to deal in the Software without restriction, including
7 | without limitation the rights to use, copy, modify, merge, publish,
8 | distribute, sublicense, and/or sell copies of the Software, and to
9 | permit persons to whom the Software is furnished to do so, subject to
10 | the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be
13 | included in all copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 | ```
23 |
24 | ----
25 | ## Open Source License:
26 |
27 | ### Forked atom-pronama-chan by hurumeki
28 | License:
29 | ```
30 | Copyright (c) 2014 hurumeki
31 |
32 | Permission is hereby granted, free of charge, to any person obtaining
33 | a copy of this software and associated documentation files (the
34 | "Software"), to deal in the Software without restriction, including
35 | without limitation the rights to use, copy, modify, merge, publish,
36 | distribute, sublicense, and/or sell copies of the Software, and to
37 | permit persons to whom the Software is furnished to do so, subject to
38 | the following conditions:
39 |
40 | The above copyright notice and this permission notice shall be
41 | included in all copies or substantial portions of the Software.
42 |
43 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
44 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
45 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
46 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
47 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
48 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
49 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
50 | ```
51 |
52 | ### Live2D WebGL
53 | Please refer to [License](http://live2d.com/sdk_license_cubism)
54 |
55 | ### Used Live2D Sample
56 | License:
57 | ```
58 | Sample.js
59 |
60 | You can modify and use this source freely
61 | only for the development of application related Live2D.
62 |
63 | (c) Live2D Inc. All rights reserved.
64 | ```
65 |
--------------------------------------------------------------------------------
/src/utils/MatrixStack.js:
--------------------------------------------------------------------------------
1 | /**
2 | *
3 | * You can modify and use this source freely
4 | * only for the development of application related Live2D.
5 | *
6 | * (c) Live2D Inc. All rights reserved.
7 | */
8 |
9 | function MatrixStack() {}
10 |
11 | // 行列スタック。4x4行列を基本とするので、16ごとの区切りの配列。
12 | MatrixStack.matrixStack = [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1];
13 |
14 | // 現在のスタックの深さ。初期は0でpushするごとに+1。
15 | MatrixStack.depth = 0;
16 |
17 | // 現在の行列
18 | MatrixStack.currentMatrix = [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1];
19 |
20 | // 計算用
21 | MatrixStack.tmp = new Array(16);
22 |
23 |
24 | /*
25 | * スタックをリセット
26 | */
27 | MatrixStack.reset = function()
28 | {
29 | this.depth = 0;
30 | }
31 |
32 |
33 | /*
34 | * 単位行列にする
35 | */
36 | MatrixStack.loadIdentity = function()
37 | {
38 | for (var i = 0; i < 16; i++)
39 | {
40 | this.currentMatrix[i] = (i % 5 == 0) ? 1 : 0;
41 | }
42 | }
43 |
44 |
45 | /*
46 | * 現在の行列を保存
47 | */
48 | MatrixStack.push = function()
49 | {
50 | var offset = this.depth * 16;
51 | var nextOffset = (this.depth + 1) * 16;
52 |
53 | if (this.matrixStack.length < nextOffset + 16)
54 | {
55 | this.matrixStack.length = nextOffset + 16;
56 | }
57 |
58 | for (var i = 0; i < 16; i++)
59 | {
60 | this.matrixStack[nextOffset + i] = this.currentMatrix[i];
61 | }
62 |
63 | this.depth++;
64 | }
65 |
66 |
67 | /*
68 | * 一つ前の行列へ
69 | */
70 | MatrixStack.pop = function()
71 | {
72 | this.depth--;
73 | if (this.depth < 0)
74 | {
75 | myError("Invalid matrix stack.");
76 | this.depth = 0;
77 | }
78 |
79 | var offset = this.depth * 16;
80 | for (var i = 0; i < 16; i++)
81 | {
82 | this.currentMatrix[i] = this.matrixStack[offset + i];
83 | }
84 | }
85 |
86 |
87 | /*
88 | * 現在の行列を取得
89 | */
90 | MatrixStack.getMatrix = function()
91 | {
92 | return this.currentMatrix;
93 | }
94 |
95 |
96 | /*
97 | * 行列を掛け合わせる
98 | */
99 | MatrixStack.multMatrix = function(matNew)
100 | {
101 | var i, j, k;
102 |
103 | for (i = 0; i < 16; i++)
104 | {
105 | this.tmp[i] = 0;
106 | }
107 |
108 | for (i = 0; i < 4; i++)
109 | {
110 | for (j = 0; j < 4; j++)
111 | {
112 | for (k = 0; k < 4; k++)
113 | {
114 | this.tmp[i + j * 4] += this.currentMatrix[i + k * 4] * matNew[k + j * 4];
115 | }
116 | }
117 | }
118 | for (i = 0; i < 16; i++)
119 | {
120 | this.currentMatrix[i] = this.tmp[i];
121 | }
122 | }
123 |
--------------------------------------------------------------------------------
/src/PlatformManager.js:
--------------------------------------------------------------------------------
1 | /**
2 | *
3 | * You can modify and use this source freely
4 | * only for the development of application related Live2D.
5 | *
6 | * (c) Live2D Inc. All rights reserved.
7 | */
8 |
9 | //============================================================
10 | //============================================================
11 | // class PlatformManager extend IPlatformManager
12 | //============================================================
13 | //============================================================
14 | function PlatformManager()
15 | {
16 |
17 | }
18 |
19 | //============================================================
20 | // PlatformManager # loadBytes()
21 | //============================================================
22 | PlatformManager.prototype.loadBytes = function(path/*String*/, callback)
23 | {
24 | var request = new XMLHttpRequest();
25 | request.open("GET", path, true);
26 | request.responseType = "arraybuffer";
27 | request.onload = function(){
28 | switch(request.status){
29 | case 200:
30 | callback(request.response);
31 | break;
32 | default:
33 | console.error("Failed to load (" + request.status + ") : " + path);
34 | break;
35 | }
36 | }
37 | request.send(null);
38 | //return request;
39 | }
40 |
41 | //============================================================
42 | // PlatformManager # loadString()
43 | //============================================================
44 | PlatformManager.prototype.loadString = function(path/*String*/)
45 | {
46 |
47 | this.loadBytes(path, function(buf) {
48 | return buf;
49 | });
50 |
51 | }
52 |
53 | //============================================================
54 | // PlatformManager # loadLive2DModel()
55 | //============================================================
56 | PlatformManager.prototype.loadLive2DModel = function(path/*String*/, callback)
57 | {
58 | var model = null;
59 |
60 | // load moc
61 | this.loadBytes(path, function(buf){
62 | model = Live2DModelWebGL.loadModel(buf);
63 | callback(model);
64 | });
65 |
66 | }
67 |
68 | //============================================================
69 | // PlatformManager # loadTexture()
70 | //============================================================
71 | PlatformManager.prototype.loadTexture = function(model/*ALive2DModel*/, no/*int*/, path/*String*/, callback)
72 | {
73 | // load textures
74 | var loadedImage = new Image();
75 | loadedImage.src = path;
76 |
77 | var thisRef = this;
78 | loadedImage.onload = function() {
79 |
80 | // create texture
81 | var canvas = document.getElementById("glcanvas");
82 | var gl = getWebGLContext(canvas, {premultipliedAlpha : true});
83 | var texture = gl.createTexture(); // テクスチャオブジェクトを作成する
84 | if (!texture){ console.error("Failed to generate gl texture name."); return -1; }
85 |
86 | if(model.isPremultipliedAlpha() == false){
87 | // 乗算済アルファテクスチャ以外の場合
88 | gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, 1);
89 | }
90 | gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, 1); // imageを上下反転
91 | gl.activeTexture(gl.TEXTURE0);
92 | gl.bindTexture(gl.TEXTURE_2D, texture);
93 | gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA,
94 | gl.UNSIGNED_BYTE, loadedImage);
95 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
96 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_NEAREST);
97 | gl.generateMipmap(gl.TEXTURE_2D);
98 |
99 |
100 | // 画像からWebGLテクスチャ化を生成し、モデルに登録
101 | model.setTexture(no, texture);// モデルにテクスチャをセット
102 |
103 | // テクスチャオブジェクトを解放
104 | texture = null;
105 |
106 | if (typeof callback == "function") callback();
107 | };
108 |
109 | loadedImage.onerror = function() {
110 | console.error("Failed to load image : " + path);
111 | }
112 | }
113 |
114 |
115 | //============================================================
116 | // PlatformManager # parseFromBytes(buf)
117 | // ArrayBuffer から JSON に変換する
118 | //============================================================
119 | PlatformManager.prototype.jsonParseFromBytes = function(buf){
120 |
121 | var jsonStr;
122 |
123 | // BOMの有無に応じて処理を分ける
124 | // UTF-8のBOMは0xEF 0xBB 0xBF(10進数:239 187 191)
125 | var bomCode = new Uint8Array(buf, 0, 3);
126 | if (bomCode[0] == 239 && bomCode[1] == 187 && bomCode[2] == 191) {
127 | jsonStr = String.fromCharCode.apply(null, new Uint8Array(buf, 3));
128 | } else {
129 | jsonStr = String.fromCharCode.apply(null, new Uint8Array(buf));
130 | }
131 |
132 | var jsonObj = JSON.parse(jsonStr);
133 |
134 | return jsonObj;
135 | };
136 |
137 |
138 | //============================================================
139 | // PlatformManager # log()
140 | //============================================================
141 | PlatformManager.prototype.log = function(txt/*String*/)
142 | {
143 | console.log(txt);
144 | }
145 |
146 |
--------------------------------------------------------------------------------
/src/LAppLive2DManager.js:
--------------------------------------------------------------------------------
1 | function LAppLive2DManager()
2 | {
3 | // console.log("--> LAppLive2DManager()");
4 |
5 | // モデルデータ
6 | this.models = []; // LAppModel
7 |
8 | // サンプル機能
9 | this.count = -1;
10 | this.reloadFlg = false; // モデル再読み込みのフラグ
11 |
12 | Live2D.init();
13 | Live2DFramework.setPlatformManager(new PlatformManager);
14 |
15 | }
16 |
17 | LAppLive2DManager.prototype.createModel = function()
18 | {
19 | // console.log("--> LAppLive2DManager.createModel()");
20 |
21 | var model = new LAppModel();
22 | this.models.push(model);
23 |
24 | return model;
25 | }
26 |
27 |
28 | LAppLive2DManager.prototype.changeModel = function(gl, model)
29 | {
30 | // console.log("--> LAppLive2DManager.update(gl)");
31 |
32 | if (this.reloadFlg)
33 | {
34 | // モデル切り替えボタンが押された時、モデルを再読み込みする
35 | this.reloadFlg = false;
36 | var no = parseInt(this.count % 5);
37 |
38 | var thisRef = this;
39 | /*switch (no)
40 | {
41 | case 0: // ハル
42 | this.releaseModel(1, gl);
43 | this.releaseModel(0, gl);
44 | // OpenGLのコンテキストをセット
45 | this.createModel();
46 | this.models[0].load(gl, LAppDefine.MODEL_HARU);
47 | break;
48 | case 1: // しずく
49 | this.releaseModel(0, gl);
50 | this.createModel();
51 | this.models[0].load(gl, LAppDefine.MODEL_SHIZUKU);
52 | break;
53 | case 2: // わんこ
54 | this.releaseModel(0, gl);
55 | this.createModel();
56 | this.models[0].load(gl, LAppDefine.MODEL_WANKO);
57 | break;
58 | case 3: // Epsilon2.1モデル
59 | this.releaseModel(0, gl);
60 | this.createModel();
61 | this.models[0].load(gl, LAppDefine.MODEL_EPSILON);
62 | break;
63 | case 4: // 複数モデル
64 | this.releaseModel(0, gl);
65 | // 一体目のモデル
66 | this.createModel();
67 | this.models[0].load(gl, LAppDefine.MODEL_HARU_A, function() {
68 | // 二体目のモデル
69 | thisRef.createModel();
70 | thisRef.models[1].load(gl, LAppDefine.MODEL_HARU_B);
71 | });
72 | break;
73 | default:
74 | break;
75 | }*/
76 |
77 | this.releaseModel(0, gl);
78 | this.createModel();
79 | this.models[0].load(gl, model);
80 | }
81 | };
82 |
83 |
84 | LAppLive2DManager.prototype.getModel = function(no)
85 | {
86 | // console.log("--> LAppLive2DManager.getModel(" + no + ")");
87 |
88 | if (no >= this.models.length) return null;
89 |
90 | return this.models[no];
91 | };
92 |
93 |
94 | /*
95 | * モデルを解放する
96 | * ないときはなにもしない
97 | */
98 | LAppLive2DManager.prototype.releaseModel = function(no, gl)
99 | {
100 | // console.log("--> LAppLive2DManager.releaseModel(" + no + ")");
101 |
102 | if (this.models.length <= no) return;
103 |
104 | this.models[no].release(gl);
105 |
106 | delete this.models[no];
107 | this.models.splice(no, 1);
108 | };
109 |
110 |
111 | /*
112 | * モデルの数
113 | */
114 | LAppLive2DManager.prototype.numModels = function()
115 | {
116 | return this.models.length;
117 | };
118 |
119 |
120 | /*
121 | * ドラッグしたとき、その方向を向く設定する
122 | */
123 | LAppLive2DManager.prototype.setDrag = function(x, y)
124 | {
125 | for (var i = 0; i < this.models.length; i++)
126 | {
127 | this.models[i].setDrag(x, y);
128 | }
129 | }
130 |
131 |
132 | /*
133 | * 画面が最大になったときのイベント
134 | */
135 | LAppLive2DManager.prototype.maxScaleEvent = function()
136 | {
137 | if (LAppDefine.DEBUG_LOG)
138 | console.log("Max scale event.");
139 | for (var i = 0; i < this.models.length; i++)
140 | {
141 | this.models[i].startRandomMotion(LAppDefine.MOTION_GROUP_PINCH_IN,
142 | LAppDefine.PRIORITY_NORMAL);
143 | }
144 | }
145 |
146 |
147 | /*
148 | * 画面が最小になったときのイベント
149 | */
150 | LAppLive2DManager.prototype.minScaleEvent = function()
151 | {
152 | if (LAppDefine.DEBUG_LOG)
153 | console.log("Min scale event.");
154 | for (var i = 0; i < this.models.length; i++)
155 | {
156 | this.models[i].startRandomMotion(LAppDefine.MOTION_GROUP_PINCH_OUT,
157 | LAppDefine.PRIORITY_NORMAL);
158 | }
159 | }
160 |
161 |
162 | /*
163 | * タップしたときのイベント
164 | */
165 | LAppLive2DManager.prototype.tapEvent = function(x, y)
166 | {
167 | if (LAppDefine.DEBUG_LOG)
168 | console.log("tapEvent view x:" + x + " y:" + y);
169 |
170 | for (var i = 0; i < this.models.length; i++)
171 | {
172 |
173 | if (this.models[i].hitTest(LAppDefine.HIT_AREA_HEAD, x, y))
174 | {
175 | // 顔をタップしたら表情切り替え
176 | if (LAppDefine.DEBUG_LOG)
177 | console.log("Tap face.");
178 |
179 | this.models[i].setRandomExpression();
180 | }
181 | else if (this.models[i].hitTest(LAppDefine.HIT_AREA_BODY, x, y))
182 | {
183 | // 体をタップしたらモーション
184 | if (LAppDefine.DEBUG_LOG)
185 | console.log("Tap body." + " models[" + i + "]");
186 |
187 | this.models[i].startRandomMotion(LAppDefine.MOTION_GROUP_TAP_BODY,
188 | LAppDefine.PRIORITY_NORMAL);
189 | }
190 | }
191 |
192 | return true;
193 | };
194 |
--------------------------------------------------------------------------------
/src/utils/ModelSettingJson.js:
--------------------------------------------------------------------------------
1 | function ModelSettingJson()
2 | {
3 | this.NAME = "name";
4 | this.ID = "id";
5 | this.MODEL = "model";
6 | this.TEXTURES = "textures";
7 | this.HIT_AREAS = "hit_areas";
8 | this.PHYSICS = "physics";
9 | this.POSE = "pose";
10 | this.EXPRESSIONS = "expressions";
11 | this.MOTION_GROUPS = "motions";
12 | this.SOUND = "sound";
13 | this.FADE_IN = "fade_in";
14 | this.FADE_OUT = "fade_out";
15 | this.LAYOUT = "layout";
16 | this.INIT_PARAM = "init_param";
17 | this.INIT_PARTS_VISIBLE = "init_parts_visible";
18 | this.VALUE = "val";
19 | this.FILE = "file";
20 |
21 | this.json = {};
22 | }
23 |
24 |
25 | ModelSettingJson.prototype.loadModelSetting = function(path, callback)
26 | {
27 | var thisRef = this;
28 | var pm = Live2DFramework.getPlatformManager();
29 | pm.loadBytes(path, function(buf) {
30 | var str = String.fromCharCode.apply(null,new Uint8Array(buf));
31 | thisRef.json = JSON.parse(str);
32 | callback();
33 | });
34 | };
35 |
36 |
37 | ModelSettingJson.prototype.getTextureFile = function(n)
38 | {
39 | if (this.json[this.TEXTURES] == null || this.json[this.TEXTURES][n] == null)
40 | return null;
41 |
42 | return this.json[this.TEXTURES][n];
43 | }
44 |
45 |
46 | ModelSettingJson.prototype.getModelFile = function()
47 | {
48 | return this.json[this.MODEL];
49 | };
50 |
51 |
52 | ModelSettingJson.prototype.getTextureNum = function()
53 | {
54 | if (this.json[this.TEXTURES] == null) return 0;
55 |
56 | return this.json[this.TEXTURES].length;
57 | }
58 |
59 |
60 | ModelSettingJson.prototype.getHitAreaNum = function()
61 | {
62 | if (this.json[this.HIT_AREAS] == null)
63 | return 0;
64 |
65 | return this.json[this.HIT_AREAS].length;
66 | }
67 |
68 |
69 | ModelSettingJson.prototype.getHitAreaID = function(n)
70 | {
71 | if (this.json[this.HIT_AREAS] == null ||
72 | this.json[this.HIT_AREAS][n] == null)
73 | return null;
74 |
75 | return this.json[this.HIT_AREAS][n][this.ID];
76 | }
77 |
78 |
79 | ModelSettingJson.prototype.getHitAreaName = function(n)
80 | {
81 | if (this.json[this.HIT_AREAS] == null ||
82 | this.json[this.HIT_AREAS][n] == null)
83 | return null;
84 |
85 | return this.json[this.HIT_AREAS][n][this.NAME];
86 | }
87 |
88 |
89 | ModelSettingJson.prototype.getPhysicsFile = function()
90 | {
91 | return this.json[this.PHYSICS];
92 | }
93 |
94 |
95 | ModelSettingJson.prototype.getPoseFile = function()
96 | {
97 | return this.json[this.POSE];
98 | }
99 |
100 |
101 | ModelSettingJson.prototype.getExpressionNum = function()
102 | {
103 | return (this.json[this.EXPRESSIONS] == null) ? 0 : this.json[this.EXPRESSIONS].length;
104 | }
105 |
106 |
107 | ModelSettingJson.prototype.getExpressionFile = function(n)
108 | {
109 | if (this.json[this.EXPRESSIONS] == null)
110 | return null;
111 | return this.json[this.EXPRESSIONS][n][this.FILE];
112 | }
113 |
114 |
115 | ModelSettingJson.prototype.getExpressionName = function(n)
116 | {
117 | if (this.json[this.EXPRESSIONS] == null)
118 | return null;
119 | return this.json[this.EXPRESSIONS][n][this.NAME];
120 | }
121 |
122 |
123 | ModelSettingJson.prototype.getLayout = function()
124 | {
125 | return this.json[this.LAYOUT];
126 | }
127 |
128 |
129 | ModelSettingJson.prototype.getInitParamNum = function()
130 | {
131 | return (this.json[this.INIT_PARAM] == null) ? 0 : this.json[this.INIT_PARAM].length;
132 | }
133 |
134 |
135 | ModelSettingJson.prototype.getMotionNum = function(name)
136 | {
137 | if (this.json[this.MOTION_GROUPS] == null ||
138 | this.json[this.MOTION_GROUPS][name] == null)
139 | return 0;
140 |
141 | return this.json[this.MOTION_GROUPS][name].length;
142 | }
143 |
144 |
145 | ModelSettingJson.prototype.getMotionFile = function(name, n)
146 | {
147 | if (this.json[this.MOTION_GROUPS] == null ||
148 | this.json[this.MOTION_GROUPS][name] == null ||
149 | this.json[this.MOTION_GROUPS][name][n] == null)
150 | return null;
151 |
152 | return this.json[this.MOTION_GROUPS][name][n][this.FILE];
153 | }
154 |
155 |
156 | ModelSettingJson.prototype.getMotionSound = function(name, n)
157 | {
158 | if (this.json[this.MOTION_GROUPS] == null ||
159 | this.json[this.MOTION_GROUPS][name] == null ||
160 | this.json[this.MOTION_GROUPS][name][n] == null ||
161 | this.json[this.MOTION_GROUPS][name][n][this.SOUND] == null)
162 | return null;
163 |
164 | return this.json[this.MOTION_GROUPS][name][n][this.SOUND];
165 | }
166 |
167 |
168 | ModelSettingJson.prototype.getMotionFadeIn = function(name, n)
169 | {
170 | if (this.json[this.MOTION_GROUPS] == null ||
171 | this.json[this.MOTION_GROUPS][name] == null ||
172 | this.json[this.MOTION_GROUPS][name][n] == null ||
173 | this.json[this.MOTION_GROUPS][name][n][this.FADE_IN] == null)
174 | return 1000;
175 |
176 | return this.json[this.MOTION_GROUPS][name][n][this.FADE_IN];
177 | }
178 |
179 |
180 | ModelSettingJson.prototype.getMotionFadeOut = function(name, n)
181 | {
182 | if (this.json[this.MOTION_GROUPS] == null ||
183 | this.json[this.MOTION_GROUPS][name] == null ||
184 | this.json[this.MOTION_GROUPS][name][n] == null ||
185 | this.json[this.MOTION_GROUPS][name][n][this.FADE_OUT] == null)
186 | return 1000;
187 |
188 | return this.json[this.MOTION_GROUPS][name][n][this.FADE_OUT];
189 | }
190 |
191 |
192 | ModelSettingJson.prototype.getInitParamID = function(n)
193 | {
194 | if (this.json[this.INIT_PARAM] == null ||
195 | this.json[this.INIT_PARAM][n] == null)
196 | return null;
197 |
198 | return this.json[this.INIT_PARAM][n][this.ID];
199 | }
200 |
201 |
202 | ModelSettingJson.prototype.getInitParamValue = function(n)
203 | {
204 | if (this.json[this.INIT_PARAM] == null || this.json[this.INIT_PARAM][n] == null)
205 | return NaN;
206 |
207 | return this.json[this.INIT_PARAM][n][this.VALUE];
208 | }
209 |
210 |
211 | ModelSettingJson.prototype.getInitPartsVisibleNum = function()
212 | {
213 | return (this.json[this.INIT_PARTS_VISIBLE] == null) ? 0 : this.json[this.INIT_PARTS_VISIBLE].length;
214 | }
215 |
216 |
217 | ModelSettingJson.prototype.getInitPartsVisibleID = function(n)
218 | {
219 | if (this.json[this.INIT_PARTS_VISIBLE] == null || this.json[this.INIT_PARTS_VISIBLE][n] == null)
220 | return null;
221 | return this.json[this.INIT_PARTS_VISIBLE][n][this.ID];
222 | }
223 |
224 |
225 | ModelSettingJson.prototype.getInitPartsVisibleValue = function(n)
226 | {
227 | if (this.json[this.INIT_PARTS_VISIBLE] == null || this.json[this.INIT_PARTS_VISIBLE][n] == null)
228 | return NaN;
229 |
230 | return this.json[this.INIT_PARTS_VISIBLE][n][this.VALUE];
231 | }
232 |
--------------------------------------------------------------------------------
/src/AtomLive2D.js:
--------------------------------------------------------------------------------
1 | var l2dthisRef = this;
2 | // JavaScriptで発生したエラーを取得
3 | const EYE_PARAM = 'PARAM_EYE_R_OPEN';
4 | let drawing = true;
5 |
6 | function atomLive2d(model)
7 | {
8 | console.log("우리의 핵심목표는 이것이다 : " + model);
9 | this.platform = window.navigator.platform.toLowerCase();
10 |
11 | this.live2DMgr = new LAppLive2DManager();
12 |
13 | this.isDrawStart = false;
14 |
15 | this.gl = null;
16 | this.canvas = null;
17 |
18 | this.dragMgr = null; /*new L2DTargetPoint();*/ // ドラッグによるアニメーションの管理
19 | this.viewMatrix = null; /*new L2DViewMatrix();*/
20 | this.projMatrix = null; /*new L2DMatrix44()*/
21 | this.deviceToScreen = null; /*new L2DMatrix44();*/
22 |
23 | this.drag = false; // ドラッグ中かどうか
24 | this.oldLen = 0; // 二本指タップした時の二点間の距離
25 |
26 | this.lastMouseX = 0;
27 | this.lastMouseY = 0;
28 |
29 | this.isModelShown = false;
30 |
31 | // モデル描画用canvasの初期化
32 | initL2dCanvas("glcanvas");
33 |
34 | // モデル用マトリクスの初期化と描画の開始
35 | init(model);
36 |
37 | window.addEventListener('message', () => {
38 | drawing = !drawing;
39 | }, false);
40 | }
41 |
42 |
43 | function initL2dCanvas(canvasId)
44 | {
45 | // canvasオブジェクトを取得
46 | this.canvas = document.getElementById(canvasId);
47 |
48 | // イベントの登録
49 | /*if(this.canvas.addEventListener) {
50 | this.canvas.addEventListener("mousewheel", mouseEvent, false);
51 | this.canvas.addEventListener("click", mouseEvent, false);
52 |
53 | this.canvas.addEventListener("mousedown", mouseEvent, false);
54 | this.canvas.addEventListener("mousemove", mouseEvent, false);
55 |
56 | this.canvas.addEventListener("mouseup", mouseEvent, false);
57 | this.canvas.addEventListener("mouseout", mouseEvent, false);
58 | this.canvas.addEventListener("contextmenu", mouseEvent, false);
59 |
60 | // タッチイベントに対応
61 | this.canvas.addEventListener("touchstart", touchEvent, false);
62 | this.canvas.addEventListener("touchend", touchEvent, false);
63 | this.canvas.addEventListener("touchmove", touchEvent, false);
64 |
65 | }*/
66 | }
67 |
68 |
69 | function init(model)
70 | {
71 | // 3Dバッファの初期化
72 | var width = this.canvas.width;
73 | var height = this.canvas.height;
74 |
75 | this.dragMgr = new L2DTargetPoint();
76 |
77 | // ビュー行列
78 | var ratio = height / width;
79 | var left = LAppDefine.VIEW_LOGICAL_LEFT;
80 | var right = LAppDefine.VIEW_LOGICAL_RIGHT;
81 | var bottom = -ratio;
82 | var top = ratio;
83 |
84 | this.viewMatrix = new L2DViewMatrix();
85 |
86 | // デバイスに対応する画面の範囲。 Xの左端, Xの右端, Yの下端, Yの上端
87 | this.viewMatrix.setScreenRect(left, right, bottom, top);
88 |
89 | // デバイスに対応する画面の範囲。 Xの左端, Xの右端, Yの下端, Yの上端
90 | this.viewMatrix.setMaxScreenRect(LAppDefine.VIEW_LOGICAL_MAX_LEFT,
91 | LAppDefine.VIEW_LOGICAL_MAX_RIGHT,
92 | LAppDefine.VIEW_LOGICAL_MAX_BOTTOM,
93 | LAppDefine.VIEW_LOGICAL_MAX_TOP);
94 |
95 | this.viewMatrix.setMaxScale(LAppDefine.VIEW_MAX_SCALE);
96 | this.viewMatrix.setMinScale(LAppDefine.VIEW_MIN_SCALE);
97 |
98 | this.projMatrix = new L2DMatrix44();
99 | this.projMatrix.multScale(1, (width / height));
100 |
101 | // マウス用スクリーン変換行列
102 | this.deviceToScreen = new L2DMatrix44();
103 | this.deviceToScreen.multTranslate(-width / 2.0, -height / 2.0);
104 | this.deviceToScreen.multScale(2 / width, -2 / width);
105 |
106 |
107 | // WebGLのコンテキストを取得する
108 | this.gl = getWebGLContext();
109 | if (!this.gl) {
110 | l2dError("Failed to create WebGL context.");
111 | return;
112 | }
113 | // OpenGLのコンテキストをセット
114 | Live2D.setGL(this.gl);
115 |
116 | // 描画エリアを白でクリア
117 | this.gl.clearColor(0.0, 0.0, 0.0, 0.0);
118 |
119 | changeModel(model);
120 |
121 | startDraw();
122 | }
123 |
124 |
125 | function startDraw() {
126 | if(!this.isDrawStart) {
127 | this.isDrawStart = true;
128 | (function tick() {
129 | if(drawing) draw(); // 1回分描画
130 |
131 | var requestAnimationFrame =
132 | window.requestAnimationFrame ||
133 | window.mozRequestAnimationFrame ||
134 | window.webkitRequestAnimationFrame ||
135 | window.msRequestAnimationFrame;
136 |
137 | // 一定時間後に自身を呼び出す
138 | requestAnimationFrame(tick ,this.canvas);
139 | })();
140 | }
141 | }
142 |
143 |
144 | function draw()
145 | {
146 | l2dLog("--> draw()");
147 |
148 | MatrixStack.reset();
149 | MatrixStack.loadIdentity();
150 |
151 | this.dragMgr.update(); // ドラッグ用パラメータの更新
152 | this.live2DMgr.setDrag(this.dragMgr.getX(), this.dragMgr.getY());
153 |
154 | // Canvasをクリアする
155 | this.gl.clear(this.gl.COLOR_BUFFER_BIT);
156 |
157 | MatrixStack.multMatrix(projMatrix.getArray());
158 | MatrixStack.multMatrix(viewMatrix.getArray());
159 | MatrixStack.push();
160 |
161 | for (var i = 0; i < this.live2DMgr.numModels(); i++)
162 | {
163 | var model = this.live2DMgr.getModel(i);
164 |
165 | if(model == null) return;
166 |
167 | if (model.initialized && !model.updating)
168 | {
169 | model.update();
170 | model.draw(this.gl);
171 |
172 | if (!this.isModelShown && i == this.live2DMgr.numModels()-1) {
173 | this.isModelShown = !this.isModelShown;
174 | }
175 | }
176 | }
177 |
178 | MatrixStack.pop();
179 | }
180 |
181 |
182 | function changeModel(model)
183 | {
184 | this.isModelShown = false;
185 |
186 | this.live2DMgr.reloadFlg = true;
187 | this.live2DMgr.count++;
188 |
189 | this.live2DMgr.changeModel(this.gl, model);
190 | }
191 |
192 | /* ********** マウスイベント ********** */
193 |
194 | /*
195 | * マウスホイールによる拡大縮小
196 | */
197 |
198 | function wink(){
199 | var waitForNextBlink = () => {
200 | if(live2DMgr.models[0].eyeBlink.nextBlinkTime >= UtSystem.getUserTimeMSec()) setTimeout(waitForNextBlink, 50);
201 | else doWinkImmediate();
202 | };
203 |
204 | waitForNextBlink();
205 | }
206 |
207 | function doWinkImmediate(){
208 | var currModel = live2DMgr.models[0].live2DModel;
209 | live2DMgr.models[0].eyeBlink.nextBlinkTime += 1000;
210 | live2DMgr.models[0].eyeBlink.eyeState = EYE_STATE.STATE_INTERVAL;
211 |
212 | var startValue = currModel.getParamFloat(EYE_PARAM);
213 | var currValue = startValue;
214 | var animateAmount = startValue / 40;
215 |
216 | var animateOpening = () => {
217 | if(currValue >= startValue) return;
218 | currValue += animateAmount;
219 | currModel.setParamFloat(EYE_PARAM, currValue);
220 | setTimeout(animateOpening, 10);
221 | };
222 |
223 | var animateClosing = () => {
224 | if(currValue <= 0) return setTimeout(animateOpening, 200);
225 | currValue -= animateAmount;
226 | currModel.setParamFloat(EYE_PARAM, currValue);
227 | setTimeout(animateClosing, 10);
228 | };
229 |
230 | animateClosing();
231 | }
232 |
233 | function loadMotionGroup(motionGroup){
234 | var model = l2dthisRef.live2DMgr.getModel(0);
235 | if(model == null) return;
236 |
237 | model.preloadMotionGroup(motionGroup);
238 | }
239 |
240 | function showExpression(exp){
241 | var model = l2dthisRef.live2DMgr.getModel(0);
242 | if(model == null) return;
243 |
244 | model.setExpression(exp);
245 | }
246 |
247 | function showRandomMotion(motionGroup){
248 | var model = l2dthisRef.live2DMgr.getModel(0);
249 | if(model == null) return;
250 |
251 | model.startRandomMotion(motionGroup, LAppDefine.PRIORITY_NORMAL);
252 | }
253 |
254 | function modelScaling(scale)
255 | {
256 | var isMaxScale = l2dthisRef.viewMatrix.isMaxScale();
257 | var isMinScale = l2dthisRef.viewMatrix.isMinScale();
258 |
259 | l2dthisRef.viewMatrix.adjustScale(0, 0, scale);
260 |
261 | // 画面が最大になったときのイベント
262 | if (!isMaxScale)
263 | {
264 | if (l2dthisRef.viewMatrix.isMaxScale())
265 | {
266 | l2dthisRef.live2DMgr.maxScaleEvent();
267 | }
268 | }
269 | // 画面が最小になったときのイベント
270 | if (!isMinScale)
271 | {
272 | if (l2dthisRef.viewMatrix.isMinScale())
273 | {
274 | l2dthisRef.live2DMgr.minScaleEvent();
275 | }
276 | }
277 | }
278 |
279 |
280 | /*
281 | * クリックされた方向を向く
282 | * タップされた場所に応じてモーションを再生
283 | */
284 | function modelTurnHead(event)
285 | {
286 | l2dthisRef.drag = true;
287 |
288 | var rect = event.target.getBoundingClientRect();
289 |
290 | var sx = transformScreenX(event.clientX - rect.left);
291 | var sy = transformScreenY(event.clientY - rect.top);
292 | var vx = transformViewX(event.clientX - rect.left);
293 | var vy = transformViewY(event.clientY - rect.top);
294 |
295 | if (LAppDefine.DEBUG_MOUSE_LOG)
296 | l2dLog("onMouseDown device( x:" + event.clientX + " y:" + event.clientY + " ) view( x:" + vx + " y:" + vy + ")");
297 |
298 | l2dthisRef.lastMouseX = sx;
299 | l2dthisRef.lastMouseY = sy;
300 |
301 | l2dthisRef.dragMgr.setPoint(vx, vy); // その方向を向く
302 |
303 | // タップした場所に応じてモーションを再生
304 | l2dthisRef.live2DMgr.tapEvent(vx, vy);
305 | }
306 |
307 |
308 | /*
309 | * マウスを動かした時のイベント
310 | */
311 | // Edited by AtomLive2D Project
312 | function followPointer(event, options)
313 | {
314 | var sx, sy, vx, vy;
315 |
316 | if (options) {
317 | vx = event.clientX - options.screenWidth + options.spriteWidth/2 + options.spriteOffsetX;
318 | sx = transformScreenX(vx);
319 | vx = vx / options.screenWidth * options.followCursorCoefficient;
320 |
321 | vy = event.clientY - options.screenHeight + options.spriteHeight/2 + options.spriteOffsetY;
322 | sy = transformScreenY(vy);
323 | vy = vy / -options.screenHeight * options.followCursorCoefficient;
324 | } else { // Fallback original method
325 | var rect = event.target.getBoundingClientRect();
326 | sx = transformScreenX(event.clientX - rect.left);
327 | sy = transformScreenY(event.clientY - rect.top);
328 | vx = transformViewX(event.clientX - rect.left);
329 | vy = transformViewY(event.clientY - rect.top);
330 | }
331 |
332 | if (LAppDefine.DEBUG_MOUSE_LOG)
333 | l2dLog("onMouseMove device( x:" + event.clientX + " y:" + event.clientY + " ) view( x:" + vx + " y:" + vy + ")");
334 |
335 | //if (l2dthisRef.drag)
336 | //{
337 | l2dthisRef.lastMouseX = sx;
338 | l2dthisRef.lastMouseY = sy;
339 |
340 | l2dthisRef.dragMgr.setPoint(vx, vy); // その方向を向く
341 | //}
342 | }
343 |
344 |
345 | /*
346 | * 正面を向く
347 | */
348 | function lookFront()
349 | {
350 | if (l2dthisRef.drag)
351 | {
352 | l2dthisRef.drag = false;
353 | }
354 |
355 | l2dthisRef.dragMgr.setPoint(0, 0);
356 | }
357 |
358 |
359 | /*function mouseEvent(e)
360 | {
361 | e.preventDefault();
362 |
363 | if (e.type == "mousedown") {
364 |
365 | // 右クリック以外なら処理を抜ける
366 | if("button" in e && e.button != 0) return;
367 |
368 | modelTurnHead(e);
369 |
370 | } else if (e.type == "mousemove") {
371 | followPointer(e);
372 |
373 | } else if (e.type == "mouseup") {
374 |
375 | // 右クリック以外なら処理を抜ける
376 | if("button" in e && e.button != 0) return;
377 |
378 | lookFront();
379 |
380 | } else if (e.type == "mouseout") {
381 |
382 | lookFront();
383 |
384 | }
385 |
386 | }
387 |
388 |
389 | function touchEvent(e)
390 | {
391 | e.preventDefault();
392 |
393 | var touch = e.touches[0];
394 |
395 | if (e.type == "touchstart") {
396 | if (e.touches.length == 1) modelTurnHead(touch);
397 | // onClick(touch);
398 |
399 | } else if (e.type == "touchmove") {
400 | followPointer(touch);
401 |
402 | if (e.touches.length == 2) {
403 | var touch1 = e.touches[0];
404 | var touch2 = e.touches[1];
405 |
406 | var len = Math.pow(touch1.pageX - touch2.pageX, 2) + Math.pow(touch1.pageY - touch2.pageY, 2);
407 | if (l2dthisRef.oldLen - len < 0) modelScaling(1.025); // 上方向スクロール 拡大
408 | else modelScaling(0.975); // 下方向スクロール 縮小
409 |
410 | thisRef.oldLen = len;
411 | }
412 |
413 | } else if (e.type == "touchend") {
414 | lookFront();
415 | }
416 | }*/
417 |
418 |
419 | /* ********** マトリックス操作 ********** */
420 |
421 | function transformViewX(deviceX)
422 | {
423 | var screenX = this.deviceToScreen.transformX(deviceX); // 論理座標変換した座標を取得。
424 | return viewMatrix.invertTransformX(screenX); // 拡大、縮小、移動後の値。
425 | }
426 |
427 |
428 | function transformViewY(deviceY)
429 | {
430 | var screenY = this.deviceToScreen.transformY(deviceY); // 論理座標変換した座標を取得。
431 | return viewMatrix.invertTransformY(screenY); // 拡大、縮小、移動後の値。
432 | }
433 |
434 |
435 | function transformScreenX(deviceX)
436 | {
437 | return this.deviceToScreen.transformX(deviceX);
438 | }
439 |
440 |
441 | function transformScreenY(deviceY)
442 | {
443 | return this.deviceToScreen.transformY(deviceY);
444 | }
445 |
446 |
447 | /*
448 | * WebGLのコンテキストを取得する
449 | */
450 | function getWebGLContext()
451 | {
452 | var NAMES = [ "webgl" , "experimental-webgl" , "webkit-3d" , "moz-webgl"];
453 |
454 | for( var i = 0; i < NAMES.length; i++ ){
455 | try{
456 | var ctx = this.canvas.getContext(NAMES[i], {premultipliedAlpha : true});
457 | if(ctx) return ctx;
458 | }
459 | catch(e){}
460 | }
461 | return null;
462 | };
463 |
464 |
465 | /*
466 | * 画面ログを出力
467 | */
468 | function l2dLog(msg) {
469 | if(!LAppDefine.DEBUG_LOG) return;
470 |
471 | console.log(msg);
472 | }
473 |
474 |
475 | /*
476 | * 画面エラーを出力
477 | */
478 | function l2dError(msg)
479 | {
480 | if(!LAppDefine.DEBUG_LOG) return;
481 |
482 | l2dLog( "" + msg + "");
483 |
484 | console.error(msg);
485 | };
486 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | const fs = require('fs');
2 | const path = require('path');
3 | const remote = require('remote');
4 | const url = require('url');
5 | const browserWindow = remote.BrowserWindow;
6 | const CronJob = require('cron').CronJob;
7 |
8 | const MOTION_REGEX = /\((motion|wink|exp):(.*)\)/;
9 |
10 | module.exports = {
11 | config: {
12 | model: {
13 | type: "string",
14 | "default": "epsilon_2.1/Epsilon2.1.model.json",
15 | description: "model.json file of your model.",
16 | order: 1
17 | },
18 | expressions: {
19 | type: "object",
20 | properties: {
21 | info: {
22 | type: "string",
23 | "default": "",
24 | description: "Expression or motion to play when info notification is showed. example: (exp:f01);(motion:idle)"
25 | },
26 |
27 | success: {
28 | type: "string",
29 | "default": "",
30 | description: "Expression or motion to play when success notification is showed."
31 | },
32 |
33 | warning: {
34 | type: "string",
35 | "default": "",
36 | description: "Expression or motion to play when warning notification is showed."
37 | },
38 |
39 | error: {
40 | type: "string",
41 | "default": "",
42 | description: "Expression or motion to play when error notification is showed."
43 | },
44 |
45 | fatal: {
46 | type: "string",
47 | "default": "",
48 | description: "Expression or motion to play when fatal notification is showed."
49 | },
50 |
51 | wink: {
52 | type: "string",
53 | "default": "",
54 | description: "Expession or motion of wink. example: (motion:fun);(wink:do)"
55 | },
56 |
57 | time: {
58 | type: "string",
59 | "default": "",
60 | description: "Expression or motion to play with timeSignal voice."
61 | }
62 | },
63 | order: 2
64 | },
65 | modelOpacity: {
66 | type: "number",
67 | minimum: 0.0,
68 | maximum: 1.0,
69 | "default": 0.7,
70 | description: "Opacity of model",
71 | order: 3
72 | },
73 | modelCursorOpacity: {
74 | type: "number",
75 | minimum: 0.0,
76 | maximum: 1.0,
77 | "default": 0.1,
78 | description: "Opacity of model with curser over",
79 | order: 4
80 | },
81 | startVoice: {
82 | type: "object",
83 | properties: {
84 | morning: {
85 | type: "string",
86 | "default": "",
87 | description: "from 6:00 to 12:00"
88 | },
89 | afternoon: {
90 | type: "string",
91 | "default": "",
92 | description: "from 12:00 to 18:00"
93 | },
94 | night: {
95 | type: "string",
96 | "default": "",
97 | description: "from 18:00 to 6:00"
98 | }
99 | },
100 | order: 5
101 | },
102 | timeSignal: {
103 | type: "array",
104 | "default": [],
105 | items: {
106 | type: "string"
107 | },
108 | description: "Time signal voice filename. Array [0 - 23]",
109 | order: 6
110 | },
111 | voiceVolume: {
112 | type: "number",
113 | "default": 0.3,
114 | minimum: 0.0,
115 | maximum: 1.0,
116 | description: "voice volume. between 0.0 and 1.0",
117 | order: 7
118 | },
119 | width: {
120 | type: "number",
121 | "default": 360,
122 | minimum: 0,
123 | description: "Width of your canvas.",
124 | order: 8
125 | },
126 | height: {
127 | type: "number",
128 | "default": 480,
129 | minimum: 0,
130 | description: "Height of your canvas.",
131 | order: 9
132 | },
133 | assetsDir: {
134 | type: "string",
135 | "default": "~/.atom/packages/atom-live2d/assets/",
136 | description: "Path to assetsdir",
137 | order: 10
138 | },
139 | followCursor: {
140 | type: "boolean",
141 | "default": true,
142 | description: "If it is true, the model will see your cursor.",
143 | order: 11
144 | },
145 | followCursorOffsetX: {
146 | type: "number",
147 | "default": 0,
148 | description: "Offset the model's X-axis field of view",
149 | order: 12
150 | },
151 | followCursorOffsetY: {
152 | type: "number",
153 | "default": 0,
154 | description: "Offset the model's Y-axis field of view",
155 | order: 13
156 | },
157 | followCursorCoefficient: {
158 | type: "number",
159 | "default": 2,
160 | description: "Focus distance coefficient (closer to greater)",
161 | order: 14
162 | }
163 | },
164 | timer: null,
165 | winkTimer: null,
166 | audio: null,
167 | activate: function(state) {
168 | var key, pJson, pkg;
169 |
170 | pJson = JSON.parse(fs.readFileSync(path.join(__dirname, '..', 'package.json')));
171 | this.loadConfig(atom.config.get("atom-live2d.model"));
172 | atom.commands.add('atom-text-editor', "atom-live2d:toggle", (function(_this) {
173 | return function() {
174 | return _this.toggle();
175 | };
176 | })(this));
177 |
178 | atom.views.getView(atom.workspace).classList.add("live-2d");
179 | atom.notifications.onDidAddNotification((function(_this) {
180 | return function(notification) {
181 | return _this.showMotion(notification.type);
182 | };
183 | })(this));
184 |
185 | atom.config.onDidChange('atom-live2d.modelOpacity', (function(_this) {
186 | return function(arg) {
187 | var newValue, oldValue;
188 | newValue = arg.newValue, oldValue = arg.oldValue;
189 | return _this.reload();
190 | };
191 | })(this));
192 |
193 | this.init();
194 | return this.startVoice(new Date);
195 | },
196 | getFileURL: function(...filePath) {
197 | return url.pathToFileURL(path.join(
198 | atom.packages.getLoadedPackage("atom-live2d").path,
199 | ...filePath
200 | )).toString();
201 | },
202 |
203 | deactivate: function() {
204 | var ref;
205 | this.audio = null;
206 | this.element.remove();
207 | this.iframe.remove();
208 | if ((ref = this.timer) != null) {
209 | ref.stop();
210 | }
211 | this.timer = null;
212 | return clearTimeout(this.winkTimer);
213 | },
214 | serialize: function() {},
215 | loadConfig: function(model) {
216 | var data;
217 | var p = path.join(this.getAssetsDirPath(), path.dirname(model), 'atom-l2d-conf.json');
218 | if (fs.existsSync(p)) data = require(p);
219 |
220 | if (data != null) {
221 | return atom.config.setDefaults("atom-live2d", data);
222 | }
223 | },
224 | init: function() {
225 | this.element = document.createElement('style');
226 | this.element.textContent = ` /*.live-2d .item-views /deep/ #glcanvas {
227 | display: inline-block;
228 | opacity: ${atom.config.get("atom-live2d.modelOpacity")};
229 | }
230 |
231 | #glcanvas {
232 | display: none;
233 | }*/
234 |
235 | iframe#live2d {
236 | display: none;
237 | }
238 |
239 | .live-2d iframe#live2d {
240 | opacity: ${atom.config.get("atom-live2d.modelOpacity")};
241 | width: ${atom.config.get("atom-live2d.width")}px;
242 | height: ${atom.config.get("atom-live2d.height")}px;
243 | transition: opacity .5s;
244 | }
245 |
246 | .live-2d iframe#live2d.cursor-over {
247 | opacity: ${atom.config.get("atom-live2d.modelCursorOpacity")};
248 | }`;
249 |
250 | /*this.glcanvas = document.createElement('canvas');
251 | this.glcanvas.id = 'glcanvas';
252 | atom.views.getView(atom.workspace).ownerDocument.querySelector('.item-views /deep/ .editor--private:not(.mini) .scroll-view').appendChild(this.glcanvas);*/
253 |
254 | const workspaceView = atom.views.getView(atom.workspace);
255 | workspaceView.appendChild(this.element);
256 |
257 | this.iframe = document.createElement('iframe');
258 | this.iframe.src = this.getFileURL('index.html');
259 | this.iframe.name = `${atom.config.get("atom-live2d.width")};${atom.config.get("atom-live2d.height")}`;
260 | this.iframe.id = 'live2d';
261 | this.iframe.onload = () => {
262 | this.loadCurrentModel();
263 | this.loadMotionGroup();
264 | this.iframe.contentWindow.document.body.appendChild(css);
265 | if(atom.config.get('atom-live2d.followCursor')) {
266 | workspaceView.addEventListener('mousemove', (e) => {
267 | if(atom.config.get('atom-live2d.followCursor')) {
268 | this.callOnWindow('followPointer', e, {
269 | screenWidth: workspaceView.clientWidth,
270 | screenHeight: workspaceView.clientHeight,
271 | spriteWidth: atom.config.get("atom-live2d.width"),
272 | spriteHeight: atom.config.get("atom-live2d.height"),
273 | spriteOffsetX: atom.config.get("atom-live2d.followCursorOffsetX"),
274 | spriteOffsetY: atom.config.get("atom-live2d.followCursorOffsetY"),
275 | followCursorCoefficient: atom.config.get("atom-live2d.followCursorCoefficient")
276 | });
277 | }
278 | });
279 | }
280 |
281 | const l2dView = workspaceView.querySelector('#live2d');
282 | atom.workspace.observeTextEditors(editor => {
283 | const callback = _ => {
284 | const spriteStartX = workspaceView.clientWidth - atom.config.get("atom-live2d.width");
285 | const spriteStartY = workspaceView.clientHeight - atom.config.get("atom-live2d.height");
286 | const spriteEndX = workspaceView.clientWidth;
287 | const spriteEndY = workspaceView.clientHeight;
288 |
289 | var detect = false;
290 | atom.views.getView(editor).querySelectorAll('.cursor').forEach(v => {
291 | const rect = v.getBoundingClientRect();
292 | if (spriteStartX < rect.left && spriteStartY < rect.top
293 | && spriteEndX > rect.left && spriteEndY > rect.top) {
294 |
295 | detect = true;
296 | }
297 | });
298 |
299 | if (detect && !l2dView.classList.contains('cursor-over')) {
300 | l2dView.classList.add('cursor-over');
301 | } else if (!detect && l2dView.classList.contains('cursor-over')) {
302 | l2dView.classList.remove('cursor-over');
303 | }
304 | };
305 | editor.onDidChangeCursorPosition(callback);
306 | editor.onDidAddCursor(callback);
307 | editor.onDidRemoveCursor(callback);
308 | });
309 | };
310 |
311 | var css = document.createElement('style');
312 | css.innerHTML = `
313 | #glcanvas {
314 | width: ${atom.config.get("atom-live2d.width")}px;
315 | height: ${atom.config.get("atom-live2d.height")}px;
316 | }
317 | `;
318 |
319 | atom.views.getView(atom.workspace).querySelector('atom-workspace-axis.vertical>atom-pane-container.panes atom-pane.pane').appendChild(this.iframe);
320 |
321 | /*[
322 | 'lib/live2d.min',
323 | 'lib/live2d_framework',
324 | 'src/utils/MatrixStack',
325 | 'src/utils/ModelSettingJSON',
326 | 'src/PlatformManager',
327 | 'src/LAppDefine',
328 | 'src/LAppModel',
329 | 'src/LAppLive2DManager',
330 | 'src/AtomLive2D'
331 | ].map((v) => path.join(atom.packages.resolvePackagePath('atom-live2d'), v + '.js')).forEach((v) => {
332 | this.evalOnWindow(fs.readFileSync(v));
333 | });*/
334 |
335 | if(atom.config.get("atom-live2d.timeSignal").length > 0) {
336 | this.timer = new CronJob('00 00 * * * *', this.timeSignal.bind(this), null, true);
337 | }
338 |
339 | if(atom.config.get("atom-live2d.expressions.wink")) {
340 | return this.winkTimer = () => {
341 | this.showMotion('wink');
342 | };
343 | }
344 | },
345 | reload: function() {
346 | this.deactivate();
347 | this.loadConfig(atom.config.get("atom-live2d.model"));
348 | return this.init();
349 | },
350 | toggle: function() {
351 | this.iframe.contentWindow.postMessage('toggle', '*');
352 | return atom.views.getView(atom.workspace).classList.toggle("live-2d");
353 | },
354 | startVoice: function(d) {
355 | var time;
356 | if (!d.getHours) {
357 | return;
358 | }
359 | time = "night";
360 | if (d.getHours() >= 6 && d.getHours() < 12) {
361 | time = "morning";
362 | } else if (d.getHours() >= 12 && d.getHours() < 18) {
363 | time = "afternoon";
364 | }
365 | return this.speak(atom.config.get("atom-live2d.startVoice." + time));
366 | },
367 | timeSignal: function() {
368 | var d;
369 | d = new Date;
370 | this.showMotion('time');
371 | return this.speak(atom.config.get("atom-live2d.timeSignal")[d.getHours()]);
372 | },
373 | speak: function(filename) {
374 | if(!filename) return;
375 | var filepath, fileurl, windows;
376 | windows = browserWindow.getAllWindows();
377 | if (windows[0].id !== atom.getCurrentWindow().id) {
378 | return;
379 | }
380 | if (!atom.views.getView(atom.workspace).classList.contains("live-2d")) {
381 | return;
382 | }
383 | filepath = path.join(this.getThemeDirPath(), filename);
384 | fileurl = this.getThemeDirUrl() + filename;
385 | if (!fs.existsSync(filepath)) {
386 | if (atom.inDevMode) {
387 | console.warn("Atom Live2D: no voice file:" + filepath);
388 | }
389 | return;
390 | }
391 | this.audio = this.audio || document.createElement("audio");
392 | this.audio.autoplay = true;
393 | this.audio.volume = atom.config.get("atom-live2d.voiceVolume");
394 | return this.audio.src = fileurl;
395 | },
396 | showMotion: function(motionString){
397 | var exp = atom.config.get("atom-live2d.expressions");
398 | var index = {};
399 | exp[motionString].split(';').forEach((v) => {
400 | var regex = v.match(MOTION_REGEX);
401 |
402 | if(regex === null) return;
403 | if(index[regex[1]] === undefined) index[regex[1]] = 0;
404 | else index[regex[1]]++;
405 |
406 | setTimeout(() => {
407 | switch(regex[1]){
408 | case 'motion':
409 | this.callOnWindow('showRandomMotion', regex[2]);
410 | break;
411 |
412 | case 'exp':
413 | this.callOnWindow('showExpression', regex[2]);
414 | break;
415 |
416 | case 'wink':
417 | this.callOnWindow('wink');
418 | break;
419 | }
420 | }, index[regex[1]]);
421 | });
422 | },
423 | loadMotionGroup: function(){
424 | var exp = atom.config.get("atom-live2d.expressions");
425 | Object.keys(exp).forEach((k) => {
426 | exp[k].split(';').forEach((v) => {
427 | var regex = v.match(MOTION_REGEX);
428 |
429 | if(regex === null) return;
430 | if(regex[1] === 'motion') this.callOnWindow('loadMotionGroup', regex[2]);
431 | });
432 | });
433 | },
434 | loadCurrentModel: function() {
435 | this.callOnWindow('atomLive2d', this.getFileURL('assets', atom.config.get('atom-live2d.model')));
436 | console.log('Evaluating init script...');
437 | },
438 | callOnWindow: function(funcname, ...args) {
439 | if(this.iframe.contentWindow[funcname] === undefined)
440 | return console.log('Skipped undefined function (plz wait for a sec!): ', funcname)
441 | //return atom.getCurrentWindow().webContents.executeJavaScript(source);
442 | return this.iframe.contentWindow[funcname](...args);
443 | },
444 | getThemeDir: function() {
445 | return path.dirname(atom.config.get('atom-live2d.model'));
446 | },
447 | getThemeDirUrl: function() {
448 | return this.trailingslash(this.getFileURL('assets', this.getThemeDir()));
449 | },
450 | getThemeDirPath: function() {
451 | return path.join(this.getAssetsDirPath(), this.getThemeDir());
452 | },
453 | getAssetsDirPath: function() {
454 | var path;
455 | path = atom.config.get("atom-live2d.assetsDir");
456 | if (path[0] === "~") {
457 | path = this.getUserHome() + path.substr(1);
458 | }
459 | return this.trailingslash(path);
460 | },
461 | getUserHome: function() {
462 | if (process.platform === 'win32') {
463 | return process.env.USERPROFILE;
464 | }
465 | return process.env.HOME;
466 | },
467 | trailingslash: function(path) {
468 | if (path[path.length - 1] === "/") {
469 | path = path.slice(0, -1);
470 | }
471 | return path + "/";
472 | }
473 | };
474 |
--------------------------------------------------------------------------------
/src/LAppModel.js:
--------------------------------------------------------------------------------
1 | //============================================================
2 | //============================================================
3 | // class LAppModel extends L2DBaseModel
4 | //============================================================
5 | //============================================================
6 | function LAppModel()
7 | {
8 | //L2DBaseModel.apply(this, arguments);
9 | L2DBaseModel.prototype.constructor.call(this);
10 |
11 | this.modelHomeDir = "";
12 | this.modelSetting = null;
13 | this.tmpMatrix = [];
14 | }
15 |
16 | LAppModel.prototype = new L2DBaseModel();
17 |
18 | /*
19 | * モデルを初期化する
20 | */
21 | LAppModel.prototype.load = function(gl, modelSettingPath, callback)
22 | {
23 | this.setUpdating(true);
24 | this.setInitialized(false);
25 |
26 | this.modelHomeDir = modelSettingPath.substring(0, modelSettingPath.lastIndexOf("/") + 1);
27 |
28 | this.modelSetting = new ModelSettingJson();
29 |
30 | var thisRef = this;
31 |
32 | this.modelSetting.loadModelSetting(modelSettingPath, function(){
33 | // モデルデータを読み込む
34 | var path = thisRef.modelHomeDir + thisRef.modelSetting.getModelFile();
35 | thisRef.loadModelData(path, function(model){
36 |
37 | for (var i = 0; i < thisRef.modelSetting.getTextureNum(); i++)
38 | {
39 | // テクスチャを読み込む
40 | var texPaths = thisRef.modelHomeDir +
41 | thisRef.modelSetting.getTextureFile(i);
42 |
43 | thisRef.loadTexture(i, texPaths, function() {
44 | // すべてのテクスチャを読み込んだ後の処理
45 | if( thisRef.isTexLoaded ) {
46 | // 表情
47 | if (thisRef.modelSetting.getExpressionNum() > 0)
48 | {
49 | // 古い表情を削除
50 | thisRef.expressions = {};
51 |
52 | for (var j = 0; j < thisRef.modelSetting.getExpressionNum(); j++)
53 | {
54 | var expName = thisRef.modelSetting.getExpressionName(j);
55 | var expFilePath = thisRef.modelHomeDir +
56 | thisRef.modelSetting.getExpressionFile(j);
57 |
58 | thisRef.loadExpression(expName, expFilePath);
59 | }
60 | }
61 | else
62 | {
63 | thisRef.expressionManager = null;
64 | thisRef.expressions = {};
65 | }
66 |
67 |
68 | // 自動目パチ
69 | if (thisRef.eyeBlink == null)
70 | {
71 | thisRef.eyeBlink = new L2DEyeBlink();
72 | }
73 |
74 | // 物理演算
75 | if (thisRef.modelSetting.getPhysicsFile() != null)
76 | {
77 | thisRef.loadPhysics(thisRef.modelHomeDir +
78 | thisRef.modelSetting.getPhysicsFile());
79 | }
80 | else
81 | {
82 | thisRef.physics = null;
83 | }
84 |
85 |
86 | // パーツ切り替え
87 | if (thisRef.modelSetting.getPoseFile() != null)
88 | {
89 | thisRef.loadPose(
90 | thisRef.modelHomeDir +
91 | thisRef.modelSetting.getPoseFile(),
92 | function() {
93 | thisRef.pose.updateParam(thisRef.live2DModel);
94 | }
95 | );
96 | }
97 | else
98 | {
99 | thisRef.pose = null;
100 | }
101 |
102 |
103 | // レイアウト
104 | if (thisRef.modelSetting.getLayout() != null)
105 | {
106 | var layout = thisRef.modelSetting.getLayout();
107 | if (layout["width"] != null)
108 | thisRef.modelMatrix.setWidth(layout["width"]);
109 | if (layout["height"] != null)
110 | thisRef.modelMatrix.setHeight(layout["height"]);
111 |
112 | if (layout["x"] != null)
113 | thisRef.modelMatrix.setX(layout["x"]);
114 | if (layout["y"] != null)
115 | thisRef.modelMatrix.setY(layout["y"]);
116 | if (layout["center_x"] != null)
117 | thisRef.modelMatrix.centerX(layout["center_x"]);
118 | if (layout["center_y"] != null)
119 | thisRef.modelMatrix.centerY(layout["center_y"]);
120 | if (layout["top"] != null)
121 | thisRef.modelMatrix.top(layout["top"]);
122 | if (layout["bottom"] != null)
123 | thisRef.modelMatrix.bottom(layout["bottom"]);
124 | if (layout["left"] != null)
125 | thisRef.modelMatrix.left(layout["left"]);
126 | if (layout["right"] != null)
127 | thisRef.modelMatrix.right(layout["right"]);
128 | }
129 |
130 | for (var j = 0; j < thisRef.modelSetting.getInitParamNum(); j++)
131 | {
132 | // パラメータを上書き
133 | thisRef.live2DModel.setParamFloat(
134 | thisRef.modelSetting.getInitParamID(j),
135 | thisRef.modelSetting.getInitParamValue(j)
136 | );
137 | }
138 |
139 | for (var j = 0; j < thisRef.modelSetting.getInitPartsVisibleNum(); j++)
140 | {
141 | // パーツの透明度を設定
142 | thisRef.live2DModel.setPartsOpacity(
143 | thisRef.modelSetting.getInitPartsVisibleID(j),
144 | thisRef.modelSetting.getInitPartsVisibleValue(j)
145 | );
146 | }
147 |
148 |
149 | // パラメータを保存。次回のloadParamで読みだされる
150 | thisRef.live2DModel.saveParam();
151 | // thisRef.live2DModel.setGL(gl);
152 |
153 | // アイドリングはあらかじめ読み込んでおく。
154 | thisRef.preloadMotionGroup(LAppDefine.MOTION_GROUP_IDLE);
155 | thisRef.mainMotionManager.stopAllMotions();
156 |
157 | thisRef.setUpdating(false); // 更新状態の完了
158 | thisRef.setInitialized(true); // 初期化完了
159 |
160 | if (typeof callback == "function") callback();
161 |
162 | }
163 | });
164 | }
165 | });
166 | });
167 | };
168 |
169 |
170 | /*
171 | * GCだけで解放されないメモリを解放
172 | */
173 | LAppModel.prototype.release = function(gl)
174 | {
175 | // this.live2DModel.deleteTextures();
176 | var pm = Live2DFramework.getPlatformManager();
177 |
178 | gl.deleteTexture(pm.texture);
179 | }
180 |
181 |
182 | /*
183 | * モーションファイルをあらかじめ読み込む
184 | */
185 | LAppModel.prototype.preloadMotionGroup = function(name)
186 | {
187 | var thisRef = this;
188 |
189 | for (var i = 0; i < this.modelSetting.getMotionNum(name); i++)
190 | {
191 | var file = this.modelSetting.getMotionFile(name, i);
192 | this.loadMotion(file, this.modelHomeDir + file, function(motion) {
193 | motion.setFadeIn(thisRef.modelSetting.getMotionFadeIn(name, i));
194 | motion.setFadeOut(thisRef.modelSetting.getMotionFadeOut(name, i));
195 | });
196 |
197 | }
198 | }
199 |
200 |
201 | LAppModel.prototype.update = function()
202 | {
203 | // console.log("--> LAppModel.update()");
204 |
205 | if(this.live2DModel == null)
206 | {
207 | if (LAppDefine.DEBUG_LOG) console.error("Failed to update.");
208 |
209 | return;
210 | }
211 |
212 | var timeMSec = UtSystem.getUserTimeMSec() - this.startTimeMSec;
213 | var timeSec = timeMSec / 1000.0;
214 | var t = timeSec * 2 * Math.PI; // 2πt
215 |
216 | // 待機モーション判定
217 | if (this.mainMotionManager.isFinished())
218 | {
219 | // モーションの再生がない場合、待機モーションの中からランダムで再生する
220 | this.startRandomMotion(LAppDefine.MOTION_GROUP_IDLE, LAppDefine.PRIORITY_IDLE);
221 | }
222 |
223 | //-----------------------------------------------------------------
224 |
225 | // 前回セーブされた状態をロード
226 | this.live2DModel.loadParam();
227 |
228 | /* インスタンスが作られていたら更新 */
229 |
230 | var update = this.mainMotionManager.updateParam(this.live2DModel); // モーションを更新
231 | if (!update) {
232 | // 目ぱち
233 | if(this.eyeBlink != null) {
234 | this.eyeBlink.updateParam(this.live2DModel);
235 | }
236 | }
237 |
238 | // 状態を保存
239 | this.live2DModel.saveParam();
240 |
241 | //-----------------------------------------------------------------
242 |
243 | // 表情でパラメータ更新(相対変化)
244 | if (this.expressionManager != null &&
245 | this.expressions != null &&
246 | !this.expressionManager.isFinished())
247 | {
248 | this.expressionManager.updateParam(this.live2DModel);
249 | }
250 |
251 | // ドラッグによる顔の向きの調整
252 | // -30から30の値を加える
253 | this.live2DModel.addToParamFloat("PARAM_ANGLE_X", this.dragX * 30, 1);
254 | this.live2DModel.addToParamFloat("PARAM_ANGLE_Y", this.dragY * 30, 1);
255 | this.live2DModel.addToParamFloat("PARAM_ANGLE_Z", (this.dragX * this.dragY) * -30, 1);
256 |
257 | // ドラッグによる体の向きの調整
258 | // -10から10の値を加える
259 | this.live2DModel.addToParamFloat("PARAM_BODY_ANGLE_X", this.dragX*10, 1);
260 |
261 | // ドラッグによる目の向きの調整
262 | // -1から1の値を加える
263 | this.live2DModel.addToParamFloat("PARAM_EYE_BALL_X", this.dragX, 1);
264 | this.live2DModel.addToParamFloat("PARAM_EYE_BALL_Y", this.dragY, 1);
265 |
266 |
267 | // 呼吸など
268 | this.live2DModel.addToParamFloat("PARAM_ANGLE_X",
269 | Number((15 * Math.sin(t / 6.5345))), 0.5);
270 | this.live2DModel.addToParamFloat("PARAM_ANGLE_Y",
271 | Number((8 * Math.sin(t / 3.5345))), 0.5);
272 | this.live2DModel.addToParamFloat("PARAM_ANGLE_Z",
273 | Number((10 * Math.sin(t / 5.5345))), 0.5);
274 | this.live2DModel.addToParamFloat("PARAM_BODY_ANGLE_X",
275 | Number((4 * Math.sin(t / 15.5345))), 0.5);
276 | this.live2DModel.setParamFloat("PARAM_BREATH",
277 | Number((0.5 + 0.5 * Math.sin(t / 3.2345))), 1);
278 |
279 | // 物理演算
280 | if (this.physics != null)
281 | {
282 | this.physics.updateParam(this.live2DModel); // 物理演算でパラメータ更新
283 | }
284 |
285 | // リップシンクの設定
286 | if (this.lipSync == null)
287 | {
288 | this.live2DModel.setParamFloat("PARAM_MOUTH_OPEN_Y",
289 | this.lipSyncValue);
290 | }
291 |
292 | // ポーズ
293 | if( this.pose != null ) {
294 | this.pose.updateParam(this.live2DModel);
295 | }
296 |
297 | this.live2DModel.update();
298 | };
299 |
300 |
301 | /*
302 | * 表情をランダムに切り替える
303 | */
304 | LAppModel.prototype.setRandomExpression = function()
305 | {
306 | var tmp = [];
307 | for (var name in this.expressions)
308 | {
309 | tmp.push(name);
310 | }
311 |
312 | var no = parseInt(Math.random() * tmp.length);
313 |
314 | this.setExpression(tmp[no]);
315 | }
316 |
317 |
318 | /*
319 | * モーションをランダムで再生する
320 | */
321 | LAppModel.prototype.startRandomMotion = function(name, priority)
322 | {
323 | var max = this.modelSetting.getMotionNum(name);
324 | var no = parseInt(Math.random() * max);
325 | this.startMotion(name, no, priority);
326 | }
327 |
328 |
329 | /*
330 | * モーションの開始。
331 | * 再生できる状態かチェックして、できなければ何もしない。
332 | * 再生出来る場合は自動でファイルを読み込んで再生。
333 | * 音声付きならそれも再生。
334 | * フェードイン、フェードアウトの情報があればここで設定。なければ初期値。
335 | */
336 | LAppModel.prototype.startMotion = function(name, no, priority)
337 | {
338 | // console.log("startMotion : " + name + " " + no + " " + priority);
339 |
340 | var motionName = this.modelSetting.getMotionFile(name, no);
341 |
342 | if (motionName == null || motionName == "")
343 | {
344 | if (LAppDefine.DEBUG_LOG)
345 | console.error("Failed to motion.");
346 | return;
347 | }
348 |
349 | if (priority == LAppDefine.PRIORITY_FORCE)
350 | {
351 | this.mainMotionManager.setReservePriority(priority);
352 | }
353 | else if (!this.mainMotionManager.reserveMotion(priority))
354 | {
355 | if (LAppDefine.DEBUG_LOG)
356 | console.log("Motion is running.")
357 | return;
358 | }
359 |
360 | var thisRef = this;
361 | var motion;
362 |
363 | if (this.motions[name] == null)
364 | {
365 | this.loadMotion(null, this.modelHomeDir + motionName, function(mtn) {
366 | motion = mtn;
367 |
368 | // フェードイン、フェードアウトの設定
369 | thisRef.setFadeInFadeOut(name, no, priority, motion);
370 |
371 | });
372 | }
373 | else
374 | {
375 | motion = this.motions[name];
376 |
377 | // フェードイン、フェードアウトの設定
378 | thisRef.setFadeInFadeOut(name, no, priority, motion);
379 | }
380 | }
381 |
382 |
383 | LAppModel.prototype.setFadeInFadeOut = function(name, no, priority, motion)
384 | {
385 | var motionName = this.modelSetting.getMotionFile(name, no);
386 |
387 | motion.setFadeIn(this.modelSetting.getMotionFadeIn(name, no));
388 | motion.setFadeOut(this.modelSetting.getMotionFadeOut(name, no));
389 |
390 |
391 | if (LAppDefine.DEBUG_LOG)
392 | console.log("Start motion : " + motionName);
393 |
394 | if (this.modelSetting.getMotionSound(name, no) == null)
395 | {
396 | this.mainMotionManager.startMotionPrio(motion, priority);
397 | }
398 | else
399 | {
400 | var soundName = this.modelSetting.getMotionSound(name, no);
401 | // var player = new Sound(this.modelHomeDir + soundName);
402 |
403 | var snd = document.createElement("audio");
404 | snd.src = this.modelHomeDir + soundName;
405 |
406 | if (LAppDefine.DEBUG_LOG)
407 | console.log("Start sound : " + soundName);
408 |
409 | snd.play();
410 | this.mainMotionManager.startMotionPrio(motion, priority);
411 | }
412 | }
413 |
414 |
415 | /*
416 | * 表情を設定する
417 | */
418 | LAppModel.prototype.setExpression = function(name)
419 | {
420 | var motion = this.expressions[name];
421 |
422 | if (LAppDefine.DEBUG_LOG)
423 | console.log("Expression : " + name);
424 |
425 | this.expressionManager.startMotion(motion, false);
426 | }
427 |
428 |
429 | /*
430 | * 描画
431 | */
432 | LAppModel.prototype.draw = function(gl)
433 | {
434 | //console.log("--> LAppModel.draw()");
435 |
436 | // if(this.live2DModel == null) return;
437 |
438 | // 通常
439 | MatrixStack.push();
440 |
441 | MatrixStack.multMatrix(this.modelMatrix.getArray());
442 |
443 | this.tmpMatrix = MatrixStack.getMatrix()
444 | this.live2DModel.setMatrix(this.tmpMatrix);
445 | this.live2DModel.draw();
446 |
447 | MatrixStack.pop();
448 |
449 | };
450 |
451 |
452 | /*
453 | * 当たり判定との簡易テスト。
454 | * 指定IDの頂点リストからそれらを含む最大の矩形を計算し、点がそこに含まれるか判定
455 | */
456 | LAppModel.prototype.hitTest = function(id, testX, testY)
457 | {
458 | var len = this.modelSetting.getHitAreaNum();
459 | for (var i = 0; i < len; i++)
460 | {
461 | if (id == this.modelSetting.getHitAreaName(i))
462 | {
463 | var drawID = this.modelSetting.getHitAreaID(i);
464 |
465 | return this.hitTestSimple(drawID, testX, testY);
466 | }
467 | }
468 |
469 | return false; // 存在しない場合はfalse
470 | }
471 |
--------------------------------------------------------------------------------
/lib/live2d_framework.js:
--------------------------------------------------------------------------------
1 | /**
2 | *
3 | * You can modify and use this source freely
4 | * only for the development of application related Live2D.
5 | *
6 | * (c) Live2D Inc. All rights reserved.
7 | */
8 | //============================================================
9 | //============================================================
10 | // class L2DBaseModel
11 | //============================================================
12 | //============================================================
13 | function L2DBaseModel()
14 | {
15 | this.live2DModel = null; // ALive2DModel
16 | this.modelMatrix = null; // L2DModelMatrix
17 | this.eyeBlink = null; // L2DEyeBlink
18 | this.physics = null; // L2DPhysics
19 | this.pose = null; // L2DPose
20 | this.debugMode = false;
21 | this.initialized = false;
22 | this.updating = false;
23 | this.alpha = 1;
24 | this.accAlpha = 0;
25 | this.lipSync = false; // リップシンクが有効かどうか
26 | this.lipSyncValue = 0; // 基本は0~1
27 | this.accelX = 0;
28 | this.accelY = 0;
29 | this.accelZ = 0;
30 | this.dragX = 0;
31 | this.dragY = 0;
32 | this.startTimeMSec = null;
33 | this.mainMotionManager = new L2DMotionManager(); //L2DMotionManager
34 | this.expressionManager = new L2DMotionManager(); //L2DMotionManager
35 | this.motions = {};
36 | this.expressions = {};
37 |
38 | this.isTexLoaded = false;
39 | }
40 |
41 | var texCounter = 0;
42 |
43 | //============================================================
44 | // L2DBaseModel # getModelMatrix()
45 | //============================================================
46 | L2DBaseModel.prototype.getModelMatrix = function()
47 | {
48 | return this.modelMatrix;
49 | }
50 |
51 | //============================================================
52 | // L2DBaseModel # setAlpha()
53 | //============================================================
54 | L2DBaseModel.prototype.setAlpha = function(a/*float*/)
55 | {
56 | if( a > 0.999 ) a = 1;
57 | if( a < 0.001 ) a = 0;
58 | this.alpha = a;
59 | }
60 |
61 | //============================================================
62 | // L2DBaseModel # getAlpha()
63 | //============================================================
64 | L2DBaseModel.prototype.getAlpha = function()
65 | {
66 | return this.alpha;
67 | }
68 |
69 | //============================================================
70 | // L2DBaseModel # isInitialized()
71 | //============================================================
72 | L2DBaseModel.prototype.isInitialized = function()
73 | {
74 | return this.initialized;
75 | }
76 |
77 | //============================================================
78 | // L2DBaseModel # setInitialized()
79 | //============================================================
80 | L2DBaseModel.prototype.setInitialized = function( v/*boolean*/)
81 | {
82 | this.initialized = v;
83 | }
84 |
85 | //============================================================
86 | // L2DBaseModel # isUpdating()
87 | //============================================================
88 | L2DBaseModel.prototype.isUpdating = function()
89 | {
90 | return this.updating;
91 | }
92 |
93 | //============================================================
94 | // L2DBaseModel # setUpdating()
95 | //============================================================
96 | L2DBaseModel.prototype.setUpdating = function(v/*boolean*/)
97 | {
98 | this.updating = v;
99 | }
100 |
101 | //============================================================
102 | // L2DBaseModel # getLive2DModel()
103 | //============================================================
104 | L2DBaseModel.prototype.getLive2DModel = function()
105 | {
106 | return this.live2DModel;
107 | }
108 |
109 | //============================================================
110 | // L2DBaseModel # setLipSync()
111 | //============================================================
112 | L2DBaseModel.prototype.setLipSync = function(v/*boolean*/)
113 | {
114 | this.lipSync = v;
115 | }
116 |
117 | //============================================================
118 | // L2DBaseModel # setLipSyncValue()
119 | //============================================================
120 | L2DBaseModel.prototype.setLipSyncValue = function(v/*float*/)
121 | {
122 | this.lipSyncValue = v;
123 | }
124 |
125 | //============================================================
126 | // L2DBaseModel # setAccel()
127 | //============================================================
128 | L2DBaseModel.prototype.setAccel = function(x/*float*/, y/*float*/, z/*float*/)
129 | {
130 | this.accelX = x;
131 | this.accelY = y;
132 | this.accelZ = z;
133 | }
134 |
135 | //============================================================
136 | // L2DBaseModel # setDrag()
137 | //============================================================
138 | L2DBaseModel.prototype.setDrag = function(x/*float*/, y/*float*/)
139 | {
140 | this.dragX = x;
141 | this.dragY = y;
142 | }
143 |
144 | //============================================================
145 | // L2DBaseModel # getMainMotionManager()
146 | //============================================================
147 | L2DBaseModel.prototype.getMainMotionManager = function()
148 | {
149 | return this.mainMotionManager;
150 | }
151 |
152 | //============================================================
153 | // L2DBaseModel # getExpressionManager()
154 | //============================================================
155 | L2DBaseModel.prototype.getExpressionManager = function()
156 | {
157 | return this.expressionManager;
158 | }
159 |
160 | //============================================================
161 | // L2DBaseModel # loadModelData()
162 | //============================================================
163 | L2DBaseModel.prototype.loadModelData = function(path/*String*/, callback)
164 | {
165 | /*
166 | if( this.live2DModel != null ) {
167 | this.live2DModel.deleteTextures();
168 | }
169 | */
170 | var pm = Live2DFramework.getPlatformManager(); //IPlatformManager
171 | if( this.debugMode ) pm.log("Load model : " + path);
172 |
173 | var thisRef = this;
174 | pm.loadLive2DModel(path, function(l2dModel) {
175 | thisRef.live2DModel = l2dModel;
176 | thisRef.live2DModel.saveParam();
177 |
178 | var _err = Live2D.getError();
179 |
180 | if( _err != 0 ) {
181 | console.error("Error : Failed to loadModelData().");
182 | return;
183 | }
184 |
185 | thisRef.modelMatrix = new L2DModelMatrix(
186 | thisRef.live2DModel.getCanvasWidth(),
187 | thisRef.live2DModel.getCanvasHeight()); //L2DModelMatrix
188 | thisRef.modelMatrix.setWidth(2);
189 | thisRef.modelMatrix.setCenterPosition(0, 0);
190 |
191 | callback(thisRef.live2DModel);
192 | });
193 | }
194 |
195 |
196 | //============================================================
197 | // L2DBaseModel # loadTexture()
198 | //============================================================
199 | L2DBaseModel.prototype.loadTexture = function(no/*int*/, path/*String*/, callback)
200 | {
201 | texCounter++;
202 |
203 | var pm = Live2DFramework.getPlatformManager(); //IPlatformManager
204 |
205 | if( this.debugMode ) pm.log("Load Texture : " + path);
206 |
207 | var thisRef = this;
208 | pm.loadTexture(this.live2DModel , no , path, function(){
209 | texCounter--;
210 | if(texCounter == 0) thisRef.isTexLoaded = true;
211 | if (typeof callback == "function") callback();
212 | });
213 |
214 | }
215 |
216 | //============================================================
217 | // L2DBaseModel # loadMotion()
218 | //============================================================
219 | L2DBaseModel.prototype.loadMotion = function(name/*String*/, path /*String*/, callback)
220 | {
221 | var pm = Live2DFramework.getPlatformManager(); //IPlatformManager
222 |
223 | if(this.debugMode) pm.log("Load Motion : " + path);
224 |
225 | var motion = null; //Live2DMotion
226 |
227 | var thisRef = this;
228 | pm.loadBytes(path, function(buf) {
229 | motion = Live2DMotion.loadMotion(buf);
230 | if( name != null ) {
231 | thisRef.motions[name] = motion;
232 | }
233 | callback(motion);
234 | });
235 |
236 | }
237 |
238 | //============================================================
239 | // L2DBaseModel # loadExpression()
240 | //============================================================
241 | L2DBaseModel.prototype.loadExpression = function(name/*String*/, path /*String*/, callback)
242 | {
243 | var pm = Live2DFramework.getPlatformManager(); //IPlatformManager
244 |
245 | if( this.debugMode ) pm.log("Load Expression : " + path);
246 |
247 | var thisRef = this;
248 | pm.loadBytes(path, function(buf) {
249 | if(name != null) {
250 | thisRef.expressions[name] = L2DExpressionMotion.loadJson(buf);
251 | }
252 | if (typeof callback == "function") callback();
253 | });
254 | }
255 |
256 | //============================================================
257 | // L2DBaseModel # loadPose()
258 | //============================================================
259 | L2DBaseModel.prototype.loadPose = function( path /*String*/, callback )
260 | {
261 | var pm = Live2DFramework.getPlatformManager(); //IPlatformManager
262 | if( this.debugMode ) pm.log("Load Pose : " + path);
263 | var thisRef = this;
264 | try {
265 | pm.loadBytes(path, function(buf) {
266 | thisRef.pose = L2DPose.load(buf);
267 | if (typeof callback == "function") callback();
268 | });
269 | }
270 | catch(e) {
271 | console.warn(e);
272 | }
273 | }
274 |
275 | //============================================================
276 | // L2DBaseModel # loadPhysics()
277 | //============================================================
278 | L2DBaseModel.prototype.loadPhysics = function(path/*String*/)
279 | {
280 | var pm = Live2DFramework.getPlatformManager(); //IPlatformManager
281 | if( this.debugMode ) pm.log("Load Physics : " + path);
282 | var thisRef = this;
283 | try {
284 | pm.loadBytes(path, function(buf) {
285 | thisRef.physics = L2DPhysics.load(buf);
286 | });
287 | }
288 | catch(e){
289 | console.warn(e);
290 | }
291 | }
292 |
293 | //============================================================
294 | // L2DBaseModel # hitTestSimple()
295 | //============================================================
296 | L2DBaseModel.prototype.hitTestSimple = function(drawID, testX, testY)
297 | {
298 | var drawIndex = this.live2DModel.getDrawDataIndex(drawID);
299 |
300 | if( drawIndex < 0 ) return false;
301 |
302 | var points = this.live2DModel.getTransformedPoints(drawIndex);
303 | var left = this.live2DModel.getCanvasWidth();
304 | var right = 0;
305 | var top = this.live2DModel.getCanvasHeight();
306 | var bottom = 0;
307 |
308 | for( var j = 0; j < points.length; j = j + 2 ) {
309 | var x = points[j];
310 | var y = points[j + 1];
311 |
312 | if( x < left ) left = x;
313 | if( x > right ) right = x;
314 | if( y < top ) top = y;
315 | if( y > bottom ) bottom = y;
316 | }
317 | var tx = this.modelMatrix.invertTransformX(testX);
318 | var ty = this.modelMatrix.invertTransformY(testY);
319 |
320 | return ( left <= tx && tx <= right && top <= ty && ty <= bottom );
321 | }
322 |
323 | /**
324 | *
325 | * You can modify and use this source freely
326 | * only for the development of application related Live2D.
327 | *
328 | * (c) Live2D Inc. All rights reserved.
329 | */
330 |
331 | //============================================================
332 | //============================================================
333 | // class L2DExpressionMotion extends AMotion
334 | //============================================================
335 | //============================================================
336 | function L2DExpressionMotion()
337 | {
338 | AMotion.prototype.constructor.call(this);
339 | this.paramList = new Array(); //ArrayList
340 | }
341 |
342 | L2DExpressionMotion.prototype = new AMotion(); // L2DExpressionMotion extends AMotion
343 |
344 | //============================================================
345 | L2DExpressionMotion.EXPRESSION_DEFAULT = "DEFAULT";
346 | L2DExpressionMotion.TYPE_SET = 0;
347 | L2DExpressionMotion.TYPE_ADD = 1;
348 | L2DExpressionMotion.TYPE_MULT = 2;
349 |
350 | //============================================================
351 | // static L2DExpressionMotion.loadJson()
352 | //============================================================
353 | L2DExpressionMotion.loadJson = function(buf)
354 | {
355 | var ret = new L2DExpressionMotion();
356 |
357 | var pm = Live2DFramework.getPlatformManager();
358 | var json = pm.jsonParseFromBytes(buf);
359 |
360 | ret.setFadeIn(parseInt(json.fade_in) > 0 ? parseInt(json.fade_in) : 1000);
361 | ret.setFadeOut(parseInt(json.fade_out) > 0 ? parseInt(json.fade_out) : 1000);
362 |
363 | if(json.params == null) {
364 | return ret;
365 | }
366 |
367 | var params = json.params;
368 | var paramNum = params.length;
369 | ret.paramList = []; //ArrayList
370 | for( var i = 0; i < paramNum; i++) {
371 | var param = params[i];
372 | var paramID = param.id.toString();
373 | var value = parseFloat(param.val);
374 | var calcTypeInt = L2DExpressionMotion.TYPE_ADD;
375 | var calc = param.calc != null ? param.calc.toString() : "add";
376 | if(calc === "add") {
377 | calcTypeInt = L2DExpressionMotion.TYPE_ADD;
378 | }
379 | else if(calc === "mult") {
380 | calcTypeInt = L2DExpressionMotion.TYPE_MULT;
381 | }
382 | else if(calc === "set") {
383 | calcTypeInt = L2DExpressionMotion.TYPE_SET;
384 | }
385 | else {
386 | calcTypeInt = L2DExpressionMotion.TYPE_ADD;
387 | }
388 | if(calcTypeInt == L2DExpressionMotion.TYPE_ADD) {
389 | var defaultValue = param.def == null ? 0 : parseFloat(param.def);
390 | value = value - defaultValue;
391 | }
392 | else if(calcTypeInt == L2DExpressionMotion.TYPE_MULT) {
393 | var defaultValue = param.def == null ? 1 : parseFloat(param.def);
394 | if(defaultValue == 0 ) defaultValue = 1;
395 | value = value / defaultValue;
396 | }
397 |
398 | var item = new L2DExpressionParam( );
399 | item.id = paramID;
400 | item.type = calcTypeInt;
401 | item.value = value;
402 |
403 | ret.paramList.push(item);
404 | }
405 |
406 | return ret;
407 | }
408 |
409 |
410 | //============================================================
411 | // L2DExpressionMotion # updateParamExe()
412 | //============================================================
413 | L2DExpressionMotion.prototype.updateParamExe = function(model /*ALive2DModel*/, timeMSec/*long*/ ,weight /*float*/ ,motionQueueEnt /*MotionQueueEnt*/)
414 | {
415 | for(var i = this.paramList.length - 1; i >= 0; --i) {
416 | var param = this.paramList[i]; //L2DExpressionParam
417 | // if (!param || !param.type) continue;
418 | if(param.type == L2DExpressionMotion.TYPE_ADD) {
419 | model.addToParamFloat(param.id, param.value, weight);
420 | }
421 | else if(param.type == L2DExpressionMotion.TYPE_MULT) {
422 | model.multParamFloat(param.id, param.value, weight);
423 | }
424 | else if(param.type == L2DExpressionMotion.TYPE_SET) {
425 | model.setParamFloat(param.id, param.value, weight);
426 | }
427 | }
428 | }
429 |
430 | //============================================================
431 | //============================================================
432 | // class L2DExpressionParam
433 | //============================================================
434 | //============================================================
435 | function L2DExpressionParam()
436 | {
437 | this.id = "";
438 | this.type = -1;
439 | this.value = null;
440 | }
441 |
442 | /**
443 | *
444 | * You can modify and use this source freely
445 | * only for the development of application related Live2D.
446 | *
447 | * (c) Live2D Inc. All rights reserved.
448 | */
449 |
450 | //============================================================
451 | //============================================================
452 | // class L2DEyeBlink
453 | //============================================================
454 | //============================================================
455 | function L2DEyeBlink()
456 | {
457 | this.nextBlinkTime = null /* TODO NOT INIT */; //
458 | this.stateStartTime = null /* TODO NOT INIT */; //
459 | this.blinkIntervalMsec = null /* TODO NOT INIT */; //
460 | this.eyeState = EYE_STATE.STATE_FIRST;
461 | this.blinkIntervalMsec = 4000;
462 | this.closingMotionMsec = 100;
463 | this.closedMotionMsec = 50;
464 | this.openingMotionMsec = 150;
465 | this.closeIfZero = true;
466 | this.eyeID_L = "PARAM_EYE_L_OPEN";
467 | this.eyeID_R = "PARAM_EYE_R_OPEN";
468 | }
469 |
470 | //============================================================
471 | // L2DEyeBlink # calcNextBlink()
472 | //============================================================
473 | L2DEyeBlink.prototype.calcNextBlink = function()
474 | {
475 | var time /*long*/ = UtSystem.getUserTimeMSec();
476 | var r /*Number*/ = Math.random();
477 | return /*(long)*/ (time + r * (2 * this.blinkIntervalMsec - 1));
478 | }
479 |
480 | //============================================================
481 | // L2DEyeBlink # setInterval()
482 | //============================================================
483 | L2DEyeBlink.prototype.setInterval = function(blinkIntervalMsec /*int*/)
484 | {
485 | this.blinkIntervalMsec = blinkIntervalMsec;
486 | }
487 |
488 | //============================================================
489 | // L2DEyeBlink # setEyeMotion()
490 | //============================================================
491 | L2DEyeBlink.prototype.setEyeMotion = function(closingMotionMsec/*int*/ , closedMotionMsec/*int*/ , openingMotionMsec/*int*/)
492 | {
493 | this.closingMotionMsec = closingMotionMsec;
494 | this.closedMotionMsec = closedMotionMsec;
495 | this.openingMotionMsec = openingMotionMsec;
496 | }
497 |
498 | //============================================================
499 | // L2DEyeBlink # updateParam()
500 | //============================================================
501 | L2DEyeBlink.prototype.updateParam = function(model/*ALive2DModel*/)
502 | {
503 | var time /*:long*/ = UtSystem.getUserTimeMSec();
504 | var eyeParamValue /*:Number*/;
505 | var t /*:Number*/ = 0;
506 | switch(this.eyeState){
507 | case EYE_STATE.STATE_CLOSING:
508 | t = (time - this.stateStartTime) / this.closingMotionMsec;
509 | if(t >= 1) {
510 | t = 1;
511 | this.eyeState = EYE_STATE.STATE_CLOSED;
512 | this.stateStartTime = time;
513 | }
514 | eyeParamValue = 1 - t;
515 | break;
516 | case EYE_STATE.STATE_CLOSED:
517 | t = (time - this.stateStartTime) / this.closedMotionMsec;
518 | if(t >= 1) {
519 | this.eyeState = EYE_STATE.STATE_OPENING;
520 | this.stateStartTime = time;
521 | }
522 | eyeParamValue = 0;
523 | break;
524 | case EYE_STATE.STATE_OPENING:
525 | t = (time - this.stateStartTime) / this.openingMotionMsec;
526 | if(t >= 1) {
527 | t = 1;
528 | this.eyeState = EYE_STATE.STATE_INTERVAL;
529 | this.nextBlinkTime = this.calcNextBlink();
530 | }
531 | eyeParamValue = t;
532 | break;
533 | case EYE_STATE.STATE_INTERVAL:
534 | if(this.nextBlinkTime < time) {
535 | this.eyeState = EYE_STATE.STATE_CLOSING;
536 | this.stateStartTime = time;
537 | }
538 | eyeParamValue = 1;
539 | break;
540 | case EYE_STATE.STATE_FIRST:
541 | default:
542 | this.eyeState = EYE_STATE.STATE_INTERVAL;
543 | this.nextBlinkTime = this.calcNextBlink();
544 | eyeParamValue = 1;
545 | break;
546 | }
547 | if(!this.closeIfZero) eyeParamValue = -eyeParamValue;
548 | model.setParamFloat(this.eyeID_L , eyeParamValue);
549 | model.setParamFloat(this.eyeID_R , eyeParamValue);
550 | }
551 |
552 | //== enum EYE_STATE ==
553 | var EYE_STATE = function(){};
554 |
555 | EYE_STATE.STATE_FIRST = "STATE_FIRST"
556 | EYE_STATE.STATE_INTERVAL = "STATE_INTERVAL"
557 | EYE_STATE.STATE_CLOSING = "STATE_CLOSING"
558 | EYE_STATE.STATE_CLOSED = "STATE_CLOSED"
559 | EYE_STATE.STATE_OPENING = "STATE_OPENING"
560 | /**
561 | *
562 | * You can modify and use this source freely
563 | * only for the development of application related Live2D.
564 | *
565 | * (c) Live2D Inc. All rights reserved.
566 | */
567 |
568 | //============================================================
569 | //============================================================
570 | // class L2DMatrix44
571 | //============================================================
572 | //============================================================
573 | function L2DMatrix44()
574 | {
575 | this.tr = new Float32Array(16); //
576 | this.identity();
577 | }
578 |
579 | //============================================================
580 | // static L2DMatrix44.mul()
581 | //============================================================
582 | L2DMatrix44.mul = function( a/*float[]*/, b/*float[]*/, dst/*float[]*/ )
583 | {
584 | var c = [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ];
585 | var n = 4;
586 | var i, j, k;
587 | for( i = 0; i < n; i++ ) {
588 | for( j = 0; j < n; j++ ) {
589 | for( k = 0; k < n; k++ ) {
590 | c[i + j * 4] += a[i + k * 4] * b[k + j * 4];
591 | }
592 | }
593 | }
594 | for( i = 0; i < 16; i++ ) {
595 | dst[i] = c[i];
596 | }
597 | }
598 |
599 | //============================================================
600 | // L2DMatrix44 # identity()
601 | //============================================================
602 | L2DMatrix44.prototype.identity = function()
603 | {
604 | for( var i/*:int*/ = 0; i < 16; i++ )
605 | this.tr[i] = ( ( i % 5 ) == 0 ) ? 1 : 0;
606 | }
607 |
608 | //============================================================
609 | // L2DMatrix44 # getArray()
610 | //============================================================
611 | L2DMatrix44.prototype.getArray = function()
612 | {
613 | return this.tr;
614 | }
615 |
616 | //============================================================
617 | // L2DMatrix44 # getCopyMatrix()
618 | //============================================================
619 | L2DMatrix44.prototype.getCopyMatrix = function()
620 | {
621 | return new Float32Array(this.tr); // this.tr.clone();
622 | }
623 |
624 | //============================================================
625 | // L2DMatrix44 # setMatrix()
626 | //============================================================
627 | L2DMatrix44.prototype.setMatrix = function( tr/*float[]*/ )
628 | {
629 | if( this.tr == null || this.tr.length != this.tr.length ) return ;
630 | for( var i/*:int*/ = 0; i < 16; i++ ) this.tr[i] = tr[i];
631 | }
632 |
633 | //============================================================
634 | // L2DMatrix44 # getScaleX()
635 | //============================================================
636 | L2DMatrix44.prototype.getScaleX = function()
637 | {
638 | return this.tr[0];
639 | }
640 |
641 | //============================================================
642 | // L2DMatrix44 # getScaleY()
643 | //============================================================
644 | L2DMatrix44.prototype.getScaleY = function()
645 | {
646 | return this.tr[5];
647 | }
648 |
649 | //============================================================
650 | // L2DMatrix44 # transformX()
651 | //============================================================
652 | L2DMatrix44.prototype.transformX = function( src/*float*/ )
653 | {
654 | return this.tr[0] * src + this.tr[12];
655 | }
656 |
657 | //============================================================
658 | // L2DMatrix44 # transformY()
659 | //============================================================
660 | L2DMatrix44.prototype.transformY = function( src/*float*/ )
661 | {
662 | return this.tr[5] * src + this.tr[13];
663 | }
664 |
665 | //============================================================
666 | // L2DMatrix44 # invertTransformX()
667 | //============================================================
668 | L2DMatrix44.prototype.invertTransformX = function( src/*float*/ )
669 | {
670 | return ( src - this.tr[12] ) / this.tr[0];
671 | }
672 |
673 | //============================================================
674 | // L2DMatrix44 # invertTransformY()
675 | //============================================================
676 | L2DMatrix44.prototype.invertTransformY = function( src/*float*/ )
677 | {
678 | return ( src - this.tr[13] ) / this.tr[5];
679 | }
680 |
681 | //============================================================
682 | // L2DMatrix44 # multTranslate()
683 | //============================================================
684 | L2DMatrix44.prototype.multTranslate = function( shiftX/*float*/, shiftY/*float*/ )
685 | {
686 | var tr1 = [ 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, shiftX, shiftY, 0, 1 ];
687 | L2DMatrix44.mul(tr1, this.tr, this.tr);
688 | }
689 |
690 | //============================================================
691 | // L2DMatrix44 # translate()
692 | //============================================================
693 | L2DMatrix44.prototype.translate = function( x/*float*/, y/*float*/ )
694 | {
695 | this.tr[12] = x;
696 | this.tr[13] = y;
697 | }
698 |
699 | //============================================================
700 | // L2DMatrix44 # translateX()
701 | //============================================================
702 | L2DMatrix44.prototype.translateX = function( x/*float*/ )
703 | {
704 | this.tr[12] = x;
705 | }
706 |
707 | //============================================================
708 | // L2DMatrix44 # translateY()
709 | //============================================================
710 | L2DMatrix44.prototype.translateY = function( y/*float*/ )
711 | {
712 | this.tr[13] = y;
713 | }
714 |
715 | //============================================================
716 | // L2DMatrix44 # multScale()
717 | //============================================================
718 | L2DMatrix44.prototype.multScale = function( scaleX/*float*/, scaleY/*float*/ )
719 | {
720 | var tr1 = [scaleX, 0, 0, 0, 0, scaleY, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1];
721 | L2DMatrix44.mul(tr1, this.tr, this.tr);
722 | }
723 |
724 | //============================================================
725 | // L2DMatrix44 # scale()
726 | //============================================================
727 | L2DMatrix44.prototype.scale = function( scaleX/*float*/, scaleY/*float*/ )
728 | {
729 | this.tr[0] = scaleX;
730 | this.tr[5] = scaleY;
731 | }
732 | /**
733 | *
734 | * You can modify and use this source freely
735 | * only for the development of application related Live2D.
736 | *
737 | * (c) Live2D Inc. All rights reserved.
738 | */
739 |
740 | //============================================================
741 | //============================================================
742 | // class L2DModelMatrix extends L2DMatrix44
743 | //============================================================
744 | //============================================================
745 | function L2DModelMatrix(w/*float*/, h/*float*/){
746 | L2DMatrix44.prototype.constructor.call(this);
747 | this.width = w;
748 | this.height = h;
749 | }
750 |
751 | //L2DModelMatrix extends L2DMatrix44
752 | L2DModelMatrix.prototype = new L2DMatrix44();
753 |
754 | //============================================================
755 | // L2DModelMatrix # setPosition()
756 | //============================================================
757 | L2DModelMatrix.prototype.setPosition = function(x/*float*/, y/*float*/)
758 | {
759 | this.translate(x, y);
760 | }
761 |
762 | //============================================================
763 | // L2DModelMatrix # setCenterPosition()
764 | //============================================================
765 | L2DModelMatrix.prototype.setCenterPosition = function(x/*float*/, y/*float*/)
766 | {
767 | var w = this.width * this.getScaleX();
768 | var h = this.height * this.getScaleY();
769 | this.translate(x - w / 2, y - h / 2);
770 | }
771 |
772 | //============================================================
773 | // L2DModelMatrix # top()
774 | //============================================================
775 | L2DModelMatrix.prototype.top = function(y/*float*/)
776 | {
777 | this.setY(y);
778 | }
779 |
780 | //============================================================
781 | // L2DModelMatrix # bottom()
782 | //============================================================
783 | L2DModelMatrix.prototype.bottom = function(y/*float*/)
784 | {
785 | var h = this.height * this.getScaleY();
786 | this.translateY(y - h);
787 | }
788 |
789 | //============================================================
790 | // L2DModelMatrix # left()
791 | //============================================================
792 | L2DModelMatrix.prototype.left = function(x/*float*/)
793 | {
794 | this.setX(x);
795 | }
796 |
797 | //============================================================
798 | // L2DModelMatrix # right()
799 | //============================================================
800 | L2DModelMatrix.prototype.right = function(x/*float*/)
801 | {
802 | var w = this.width * this.getScaleX();
803 | this.translateX(x - w);
804 | }
805 |
806 | //============================================================
807 | // L2DModelMatrix # centerX()
808 | //============================================================
809 | L2DModelMatrix.prototype.centerX = function(x/*float*/)
810 | {
811 | var w = this.width * this.getScaleX();
812 | this.translateX(x - w / 2);
813 | }
814 |
815 | //============================================================
816 | // L2DModelMatrix # centerY()
817 | //============================================================
818 | L2DModelMatrix.prototype.centerY = function(y/*float*/)
819 | {
820 | var h = this.height * this.getScaleY();
821 | this.translateY(y - h / 2);
822 | }
823 |
824 | //============================================================
825 | // L2DModelMatrix # setX()
826 | //============================================================
827 | L2DModelMatrix.prototype.setX = function(x/*float*/)
828 | {
829 | this.translateX(x);
830 | }
831 |
832 | //============================================================
833 | // L2DModelMatrix # setY()
834 | //============================================================
835 | L2DModelMatrix.prototype.setY = function(y/*float*/)
836 | {
837 | this.translateY(y);
838 | }
839 |
840 | //============================================================
841 | // L2DModelMatrix # setHeight()
842 | //============================================================
843 | L2DModelMatrix.prototype.setHeight = function(h/*float*/)
844 | {
845 | var scaleX = h / this.height;
846 | var scaleY = -scaleX;
847 | this.scale(scaleX, scaleY);
848 | }
849 |
850 | //============================================================
851 | // L2DModelMatrix # setWidth()
852 | //============================================================
853 | L2DModelMatrix.prototype.setWidth = function(w/*float*/)
854 | {
855 | var scaleX = w / this.width;
856 | var scaleY = -scaleX;
857 | this.scale(scaleX, scaleY);
858 | }
859 |
860 | /**
861 | *
862 | * You can modify and use this source freely
863 | * only for the development of application related Live2D.
864 | *
865 | * (c) Live2D Inc. All rights reserved.
866 | */
867 |
868 | //============================================================
869 | //============================================================
870 | // class L2DMotionManager extends MotionQueueManager
871 | //============================================================
872 | //============================================================
873 | function L2DMotionManager()
874 | {
875 | MotionQueueManager.prototype.constructor.call(this);
876 | this.currentPriority = null;
877 | this.reservePriority = null;
878 |
879 | this.super = MotionQueueManager.prototype;
880 | }
881 |
882 | // MotionQueueManagerクラスを継承
883 | L2DMotionManager.prototype = new MotionQueueManager();
884 |
885 | //============================================================
886 | // L2DMotionManager # getCurrentPriority()
887 | //============================================================
888 | L2DMotionManager.prototype.getCurrentPriority = function()
889 | {
890 | return this.currentPriority;
891 | }
892 |
893 | //============================================================
894 | // L2DMotionManager # getReservePriority()
895 | //============================================================
896 | L2DMotionManager.prototype.getReservePriority = function()
897 | {
898 | return this.reservePriority;
899 | }
900 |
901 | //============================================================
902 | // L2DMotionManager # reserveMotion()
903 | //============================================================
904 | L2DMotionManager.prototype.reserveMotion = function(priority/*int*/)
905 | {
906 | if(this.reservePriority >= priority) {
907 | return false;
908 | }
909 | if(this.currentPriority >= priority) {
910 | return false;
911 | }
912 |
913 | this.reservePriority = priority;
914 |
915 | return true;
916 | }
917 |
918 | //============================================================
919 | // L2DMotionManager # setReservePriority()
920 | //============================================================
921 | L2DMotionManager.prototype.setReservePriority = function(val/*int*/)
922 | {
923 | this.reservePriority = val;
924 | }
925 |
926 | //============================================================
927 | // L2DMotionManager # updateParam()
928 | //============================================================
929 | L2DMotionManager.prototype.updateParam = function(model/*ALive2DModel*/)
930 | {
931 | var updated = MotionQueueManager.prototype.updateParam.call(this, model);
932 |
933 | if(this.isFinished()) {
934 | this.currentPriority = 0;
935 | }
936 |
937 | return updated;
938 | }
939 |
940 | //============================================================
941 | // L2DMotionManager # startMotionPrio()
942 | //============================================================
943 | L2DMotionManager.prototype.startMotionPrio = function(motion/*AMotion*/, priority/*int*/)
944 | {
945 | if(priority == this.reservePriority) {
946 | this.reservePriority = 0;
947 | }
948 | this.currentPriority = priority;
949 | return this.startMotion(motion, false);
950 | }
951 |
952 | /**
953 | *
954 | * You can modify and use this source freely
955 | * only for the development of application related Live2D.
956 | *
957 | * (c) Live2D Inc. All rights reserved.
958 | */
959 |
960 | //============================================================
961 | //============================================================
962 | // class L2DPhysics
963 | //============================================================
964 | //============================================================
965 | function L2DPhysics()
966 | {
967 | this.physicsList = new Array(); //ArrayList
968 | this.startTimeMSec = UtSystem.getUserTimeMSec();
969 | }
970 |
971 | //============================================================
972 | // static L2DPhysics.load()
973 | //============================================================
974 | L2DPhysics.load = function(buf /*byte[]*/ )
975 | {
976 | var ret = new L2DPhysics(); //L2DPhysicsL2DPhysics
977 | var pm = Live2DFramework.getPlatformManager();
978 | var json = pm.jsonParseFromBytes(buf);
979 | var params = json.physics_hair;
980 | var paramNum = params.length;
981 | for(var i = 0; i < paramNum; i++) {
982 | var param = params[i]; //Value
983 | var physics = new PhysicsHair(); //PhysicsHairPhysicsHair
984 | var setup = param.setup; //Value
985 | var length = parseFloat(setup.length);
986 | var resist = parseFloat(setup.regist);
987 | var mass = parseFloat(setup.mass);
988 | physics.setup(length, resist, mass);
989 | var srcList = param.src; //Value
990 | var srcNum = srcList.length;
991 | for(var j = 0; j < srcNum; j++) {
992 | var src = srcList[j]; //Value
993 | var id = src.id; //String
994 | var type = PhysicsHair.Src.SRC_TO_X;
995 | var typeStr = src.ptype; //String
996 | if(typeStr === "x") {
997 | type = PhysicsHair.Src.SRC_TO_X;
998 | }
999 | else if(typeStr === "y") {
1000 | type = PhysicsHair.Src.SRC_TO_Y;
1001 | }
1002 | else if(typeStr === "angle") {
1003 | type = PhysicsHair.Src.SRC_TO_G_ANGLE;
1004 | }
1005 | else {
1006 | UtDebug.error("live2d", "Invalid parameter:PhysicsHair.Src");
1007 | }
1008 | var scale = parseFloat(src.scale);
1009 | var weight = parseFloat(src.weight);
1010 | physics.addSrcParam(type, id, scale, weight);
1011 | }
1012 | var targetList = param.targets; //Value
1013 | var targetNum = targetList.length;
1014 | for(var j = 0; j < targetNum; j++) {
1015 | var target = targetList[j]; //Value
1016 | var id = target.id; //String
1017 | var type = PhysicsHair.Target.TARGET_FROM_ANGLE;
1018 | var typeStr = target.ptype; //String
1019 | if(typeStr === "angle") {
1020 | type = PhysicsHair.Target.TARGET_FROM_ANGLE;
1021 | }
1022 | else if(typeStr === "angle_v") {
1023 | type = PhysicsHair.Target.TARGET_FROM_ANGLE_V;
1024 | }
1025 | else {
1026 | UtDebug.error("live2d", "Invalid parameter:PhysicsHair.Target");
1027 | }
1028 | var scale = parseFloat(target.scale);
1029 | var weight = parseFloat(target.weight);
1030 | physics.addTargetParam(type, id, scale, weight);
1031 | }
1032 | ret.physicsList.push(physics);
1033 | }
1034 | return ret;
1035 | }
1036 |
1037 | //============================================================
1038 | // L2DPhysics # updateParam()
1039 | //============================================================
1040 | L2DPhysics.prototype.updateParam = function(model/*ALive2DModel*/)
1041 | {
1042 | var timeMSec = UtSystem.getUserTimeMSec() - this.startTimeMSec;
1043 | for(var i = 0; i < this.physicsList.length; i++) {
1044 | this.physicsList[i].update(model, timeMSec);
1045 | }
1046 | }
1047 |
1048 | /**
1049 | *
1050 | * You can modify and use this source freely
1051 | * only for the development of application related Live2D.
1052 | *
1053 | * (c) Live2D Inc. All rights reserved.
1054 | */
1055 |
1056 | //============================================================
1057 | //============================================================
1058 | // class L2DPose
1059 | //============================================================
1060 | //============================================================
1061 | function L2DPose()
1062 | {
1063 | this.lastTime = 0;
1064 | this.lastModel = null; //ALive2DModel
1065 | this.partsGroups = new Array(); //ArrayList
1066 | }
1067 |
1068 |
1069 | //============================================================
1070 | // static L2DPose.load()
1071 | //============================================================
1072 | L2DPose.load = function(buf/*byte[]*/)
1073 | {
1074 | var ret = new L2DPose(); //L2DPose
1075 | var pm = Live2DFramework.getPlatformManager();
1076 | var json = pm.jsonParseFromBytes(buf);
1077 | var poseListInfo = json.parts_visible; //Value
1078 | var poseNum = poseListInfo.length;
1079 | for(var i_pose = 0; i_pose < poseNum; i_pose++) {
1080 | var poseInfo = poseListInfo[i_pose]; //Value
1081 | var idListInfo = poseInfo.group; //Value
1082 | var idNum = idListInfo.length;
1083 | var partsGroup/*L2DPartsParam*/ = new Array();
1084 | for(var i_group = 0; i_group < idNum; i_group++) {
1085 | var partsInfo = idListInfo[i_group]; //Value
1086 | var parts = new L2DPartsParam(partsInfo.id); //L2DPartsParamL2DPartsParam
1087 | partsGroup[i_group] = parts;
1088 | if(partsInfo.link == null) continue;
1089 | var linkListInfo = partsInfo.link; //Value
1090 | var linkNum = linkListInfo.length;
1091 | parts.link = new Array(); //ArrayList
1092 | for(var i_link = 0; i_link < linkNum; i_link++) {
1093 | var linkParts = new L2DPartsParam(linkListInfo[i_link]); //L2DPartsParamL2DPartsParam
1094 | parts.link.push(linkParts);
1095 | }
1096 | }
1097 | ret.partsGroups.push(partsGroup);
1098 | }
1099 |
1100 | return ret;
1101 | }
1102 |
1103 | //============================================================
1104 | // L2DPose # updateParam()
1105 | //============================================================
1106 | L2DPose.prototype.updateParam = function(model/*ALive2DModel*/)
1107 | {
1108 | if(model == null) return ;
1109 |
1110 | if(!(model == this.lastModel)) {
1111 | this.initParam(model);
1112 | }
1113 | this.lastModel = model;
1114 |
1115 | var curTime = UtSystem.getUserTimeMSec();
1116 | var deltaTimeSec = ((this.lastTime == 0) ? 0 : (curTime - this.lastTime) / 1000.0);
1117 | this.lastTime = curTime;
1118 | if(deltaTimeSec < 0) deltaTimeSec = 0;
1119 | for(var i = 0; i < this.partsGroups.length; i++) {
1120 | this.normalizePartsOpacityGroup(model, this.partsGroups[i], deltaTimeSec);
1121 | this.copyOpacityOtherParts(model, this.partsGroups[i]);
1122 | }
1123 | }
1124 |
1125 | //============================================================
1126 | // L2DPose # initParam()
1127 | //============================================================
1128 | L2DPose.prototype.initParam = function(model/*ALive2DModel*/)
1129 | {
1130 | if(model == null) return ;
1131 | for(var i = 0; i < this.partsGroups.length; i++) {
1132 | var partsGroup = this.partsGroups[i]; //L2DPartsParam
1133 | for(var j = 0; j < partsGroup.length; j++) {
1134 | partsGroup[j].initIndex(model);
1135 | var partsIndex = partsGroup[j].partsIndex;
1136 | var paramIndex = partsGroup[j].paramIndex;
1137 | if(partsIndex < 0) continue;
1138 | var v/*:Boolean*/ = (model.getParamFloat(paramIndex) != 0);
1139 | model.setPartsOpacity(partsIndex, (v ? 1.0 : 0.0));
1140 | model.setParamFloat(paramIndex, (v ? 1.0 : 0.0));
1141 | if(partsGroup[j].link == null) continue;
1142 | for(var k = 0; k < partsGroup[j].link.length; k++) {
1143 | partsGroup[j].link[k].initIndex(model);
1144 | }
1145 | }
1146 | }
1147 | }
1148 |
1149 | //============================================================
1150 | // L2DPose # normalizePartsOpacityGroup()
1151 | //============================================================
1152 | L2DPose.prototype.normalizePartsOpacityGroup = function(model/*ALive2DModel*/, partsGroup/*L2DPartsParam[]*/, deltaTimeSec/*float*/)
1153 | {
1154 | var visibleParts = -1;
1155 | var visibleOpacity = 1.0;
1156 | var CLEAR_TIME_SEC = 0.5;
1157 | var phi = 0.5;
1158 | var maxBackOpacity = 0.15;
1159 | for(var i = 0; i < partsGroup.length; i++) {
1160 | var partsIndex = partsGroup[i].partsIndex;
1161 | var paramIndex = partsGroup[i].paramIndex;
1162 | if(partsIndex < 0) continue;if(model.getParamFloat(paramIndex) != 0) {
1163 | if(visibleParts >= 0) {
1164 | break;
1165 | }
1166 | visibleParts = i;
1167 | visibleOpacity = model.getPartsOpacity(partsIndex);
1168 | visibleOpacity += deltaTimeSec / CLEAR_TIME_SEC;
1169 | if(visibleOpacity > 1) {
1170 | visibleOpacity = 1;
1171 | }
1172 | }
1173 | }
1174 | if(visibleParts < 0) {
1175 | visibleParts = 0;
1176 | visibleOpacity = 1;
1177 | }
1178 | for(var i = 0; i < partsGroup.length; i++) {
1179 | var partsIndex = partsGroup[i].partsIndex;
1180 | if(partsIndex < 0) continue;if(visibleParts == i) {
1181 | model.setPartsOpacity(partsIndex, visibleOpacity);
1182 | }
1183 | else {
1184 | var opacity = model.getPartsOpacity(partsIndex);
1185 | var a1;
1186 | if(visibleOpacity < phi) {
1187 | a1 = visibleOpacity * (phi - 1) / phi + 1;
1188 | }
1189 | else {
1190 | a1 = (1 - visibleOpacity) * phi / (1 - phi);
1191 | }
1192 | var backOp = (1 - a1) * (1 - visibleOpacity);
1193 | if(backOp > maxBackOpacity) {
1194 | a1 = 1 - maxBackOpacity / (1 - visibleOpacity);
1195 | }
1196 | if(opacity > a1) {
1197 | opacity = a1;
1198 | }
1199 | model.setPartsOpacity(partsIndex, opacity);
1200 | }
1201 | }
1202 | }
1203 |
1204 | //============================================================
1205 | // L2DPose # copyOpacityOtherParts()
1206 | //============================================================
1207 | L2DPose.prototype.copyOpacityOtherParts = function(model/*ALive2DModel*/, partsGroup/*L2DPartsParam[]*/)
1208 | {
1209 | for(var i_group = 0; i_group < partsGroup.length; i_group++) {
1210 | var partsParam = partsGroup[i_group]; //L2DPartsParam
1211 | if(partsParam.link == null) continue;
1212 | if(partsParam.partsIndex < 0) continue;
1213 | var opacity = model.getPartsOpacity(partsParam.partsIndex);
1214 | for(var i_link = 0; i_link < partsParam.link.length; i_link++) {
1215 | var linkParts = partsParam.link[i_link]; //L2DPartsParam
1216 | if(linkParts.partsIndex < 0) continue;
1217 | model.setPartsOpacity(linkParts.partsIndex, opacity);
1218 | }
1219 | }
1220 | }
1221 |
1222 | //============================================================
1223 | //============================================================
1224 | // class L2DPartsParam
1225 | //============================================================
1226 | //============================================================
1227 | function L2DPartsParam(id/*String*/){
1228 | this.paramIndex = -1;
1229 | this.partsIndex = -1;
1230 | this.link = null; // ArrayList
1231 | this.id = id;
1232 | }
1233 |
1234 | //============================================================
1235 | // L2DPartsParam # initIndex()
1236 | //============================================================
1237 | L2DPartsParam.prototype.initIndex = function(model/*ALive2DModel*/)
1238 | {
1239 | this.paramIndex = model.getParamIndex("VISIBLE:" + this.id);
1240 | this.partsIndex = model.getPartsDataIndex(PartsDataID.getID(this.id));
1241 | model.setParamFloat(this.paramIndex, 1);
1242 | }
1243 | /**
1244 | *
1245 | * You can modify and use this source freely
1246 | * only for the development of application related Live2D.
1247 | *
1248 | * (c) Live2D Inc. All rights reserved.
1249 | */
1250 |
1251 | //============================================================
1252 | //============================================================
1253 | // class L2DTargetPoint
1254 | //============================================================
1255 | //============================================================
1256 | function L2DTargetPoint()
1257 | {
1258 | this.EPSILON = 0.01; // 変化の最小値(この値以下は無視される)
1259 | this.faceTargetX = 0;
1260 | this.faceTargetY = 0;
1261 | this.faceX = 0;
1262 | this.faceY = 0;
1263 | this.faceVX = 0;
1264 | this.faceVY = 0;
1265 | this.lastTimeSec = 0;
1266 | }
1267 |
1268 | //============================================================
1269 | L2DTargetPoint.FRAME_RATE = 30;
1270 |
1271 | //============================================================
1272 | // L2DTargetPoint # set()
1273 | //============================================================
1274 | L2DTargetPoint.prototype.setPoint = function(x/*float*/, y/*float*/)
1275 | {
1276 | this.faceTargetX = x;
1277 | this.faceTargetY = y;
1278 | }
1279 |
1280 | //============================================================
1281 | // L2DTargetPoint # getX()
1282 | //============================================================
1283 | L2DTargetPoint.prototype.getX = function()
1284 | {
1285 | return this.faceX;
1286 | }
1287 |
1288 | //============================================================
1289 | // L2DTargetPoint # getY()
1290 | //============================================================
1291 | L2DTargetPoint.prototype.getY = function()
1292 | {
1293 | return this.faceY;
1294 | }
1295 |
1296 | //============================================================
1297 | // L2DTargetPoint # update()
1298 | //============================================================
1299 | L2DTargetPoint.prototype.update = function()
1300 | {
1301 | var TIME_TO_MAX_SPEED = 0.15;
1302 | var FACE_PARAM_MAX_V = 40.0 / 7.5;
1303 | var MAX_V = FACE_PARAM_MAX_V / L2DTargetPoint.FRAME_RATE;
1304 | if(this.lastTimeSec == 0) {
1305 | this.lastTimeSec = UtSystem.getUserTimeMSec();
1306 | return;
1307 | }
1308 | var curTimeSec = UtSystem.getUserTimeMSec();
1309 | var deltaTimeWeight = (curTimeSec - this.lastTimeSec) * L2DTargetPoint.FRAME_RATE / 1000.0;
1310 | this.lastTimeSec = curTimeSec;
1311 | var FRAME_TO_MAX_SPEED = TIME_TO_MAX_SPEED * L2DTargetPoint.FRAME_RATE;
1312 | var MAX_A = deltaTimeWeight * MAX_V / FRAME_TO_MAX_SPEED;
1313 | var dx = (this.faceTargetX - this.faceX);
1314 | var dy = (this.faceTargetY - this.faceY);
1315 | // if(dx == 0 && dy == 0) return;
1316 | if( Math.abs(dx) <= this.EPSILON && Math.abs(dy) <= this.EPSILON ) return;
1317 | var d = Math.sqrt(dx * dx + dy * dy);
1318 | var vx = MAX_V * dx / d;
1319 | var vy = MAX_V * dy / d;
1320 | var ax = vx - this.faceVX;
1321 | var ay = vy - this.faceVY;
1322 | var a = Math.sqrt(ax * ax + ay * ay);
1323 | if(a < -MAX_A || a > MAX_A) {
1324 | ax *= MAX_A / a;
1325 | ay *= MAX_A / a;
1326 | a = MAX_A;
1327 | }
1328 | this.faceVX += ax;
1329 | this.faceVY += ay;
1330 | {
1331 | var max_v = 0.5 * ( Math.sqrt(MAX_A * MAX_A + 16 * MAX_A * d - 8 * MAX_A * d) - MAX_A);
1332 | var cur_v = Math.sqrt(this.faceVX * this.faceVX + this.faceVY * this.faceVY);
1333 | if(cur_v > max_v) {
1334 | this.faceVX *= max_v / cur_v;
1335 | this.faceVY *= max_v / cur_v;
1336 | }
1337 | }
1338 | this.faceX += this.faceVX;
1339 | this.faceY += this.faceVY;
1340 | }
1341 | /**
1342 | *
1343 | * You can modify and use this source freely
1344 | * only for the development of application related Live2D.
1345 | *
1346 | * (c) Live2D Inc. All rights reserved.
1347 | */
1348 |
1349 | //============================================================
1350 | //============================================================
1351 | // class L2DViewMatrix extends L2DMatrix44
1352 | //============================================================
1353 | //============================================================
1354 | function L2DViewMatrix()
1355 | {
1356 | L2DMatrix44.prototype.constructor.call(this);
1357 | this.screenLeft = null;
1358 | this.screenRight = null;
1359 | this.screenTop = null;
1360 | this.screenBottom = null;
1361 | this.maxLeft = null;
1362 | this.maxRight = null;
1363 | this.maxTop = null;
1364 | this.maxBottom = null;
1365 | this.max = Number.MAX_VALUE;
1366 | this.min = 0;
1367 | }
1368 |
1369 | L2DViewMatrix.prototype = new L2DMatrix44(); //L2DViewMatrix extends L2DMatrix44
1370 |
1371 | //============================================================
1372 | // L2DViewMatrix # getMaxScale()
1373 | //============================================================
1374 | L2DViewMatrix.prototype.getMaxScale = function()
1375 | {
1376 | return this.max;
1377 | }
1378 |
1379 | //============================================================
1380 | // L2DViewMatrix # getMinScale()
1381 | //============================================================
1382 | L2DViewMatrix.prototype.getMinScale = function()
1383 | {
1384 | return this.min;
1385 | }
1386 |
1387 | //============================================================
1388 | // L2DViewMatrix # setMaxScale()
1389 | //============================================================
1390 | L2DViewMatrix.prototype.setMaxScale = function(v/*float*/)
1391 | {
1392 | this.max = v;
1393 | }
1394 |
1395 | //============================================================
1396 | // L2DViewMatrix # setMinScale()
1397 | //============================================================
1398 | L2DViewMatrix.prototype.setMinScale = function(v/*float*/)
1399 | {
1400 | this.min = v;
1401 | }
1402 |
1403 | //============================================================
1404 | // L2DViewMatrix # isMaxScale()
1405 | //============================================================
1406 | L2DViewMatrix.prototype.isMaxScale = function()
1407 | {
1408 | return this.getScaleX() == this.max;
1409 | }
1410 |
1411 | //============================================================
1412 | // L2DViewMatrix # isMinScale()
1413 | //============================================================
1414 | L2DViewMatrix.prototype.isMinScale = function()
1415 | {
1416 | return this.getScaleX() == this.min;
1417 | }
1418 |
1419 | //============================================================
1420 | // L2DViewMatrix # adjustTranslate()
1421 | //============================================================
1422 | L2DViewMatrix.prototype.adjustTranslate = function(shiftX/*float*/, shiftY/*float*/)
1423 | {
1424 | if(this.tr[0] * this.maxLeft + (this.tr[12] + shiftX) > this.screenLeft)
1425 | shiftX = this.screenLeft - this.tr[0] * this.maxLeft - this.tr[12];
1426 | if(this.tr[0] * this.maxRight + (this.tr[12] + shiftX) < this.screenRight)
1427 | shiftX = this.screenRight - this.tr[0] * this.maxRight - this.tr[12];
1428 | if(this.tr[5] * this.maxTop + (this.tr[13] + shiftY) < this.screenTop)
1429 | shiftY = this.screenTop - this.tr[5] * this.maxTop - this.tr[13];
1430 | if(this.tr[5] * this.maxBottom + (this.tr[13] + shiftY) > this.screenBottom)
1431 | shiftY = this.screenBottom - this.tr[5] * this.maxBottom - this.tr[13];
1432 |
1433 | var tr1 = [1, 0, 0, 0,
1434 | 0, 1, 0, 0,
1435 | 0, 0, 1, 0,
1436 | shiftX, shiftY, 0, 1 ];
1437 | L2DMatrix44.mul(tr1, this.tr, this.tr);
1438 | }
1439 |
1440 | //============================================================
1441 | // L2DViewMatrix # adjustScale()
1442 | //============================================================
1443 | L2DViewMatrix.prototype.adjustScale = function(cx/*float*/, cy/*float*/, scale/*float*/)
1444 | {
1445 | var targetScale = scale * this.tr[0];
1446 | if(targetScale < this.min) {
1447 | if(this.tr[0] > 0) scale = this.min / this.tr[0];
1448 | }
1449 | else if(targetScale > this.max) {
1450 | if(this.tr[0] > 0) scale = this.max / this.tr[0];
1451 | }
1452 | var tr1 = [1, 0, 0, 0,
1453 | 0, 1, 0, 0,
1454 | 0, 0, 1, 0,
1455 | cx, cy, 0, 1];
1456 | var tr2 = [scale, 0, 0, 0,
1457 | 0, scale, 0, 0,
1458 | 0, 0, 1, 0,
1459 | 0, 0, 0, 1 ];
1460 | var tr3 = [1, 0, 0, 0,
1461 | 0, 1, 0, 0,
1462 | 0, 0, 1, 0,
1463 | -cx, -cy, 0, 1 ];
1464 | L2DMatrix44.mul(tr3, this.tr, this.tr);
1465 | L2DMatrix44.mul(tr2, this.tr, this.tr);
1466 | L2DMatrix44.mul(tr1, this.tr, this.tr);
1467 | }
1468 |
1469 | //============================================================
1470 | // L2DViewMatrix # setScreenRect()
1471 | //============================================================
1472 | L2DViewMatrix.prototype.setScreenRect = function(left/*float*/, right/*float*/, bottom/*float*/, top/*float*/)
1473 | {
1474 | this.screenLeft = left;
1475 | this.screenRight = right;
1476 | this.screenTop = top;
1477 | this.screenBottom = bottom;
1478 | }
1479 |
1480 | //============================================================
1481 | // L2DViewMatrix # setMaxScreenRect()
1482 | //============================================================
1483 | L2DViewMatrix.prototype.setMaxScreenRect = function(left/*float*/, right/*float*/, bottom/*float*/, top/*float*/)
1484 | {
1485 | this.maxLeft = left;
1486 | this.maxRight = right;
1487 | this.maxTop = top;
1488 | this.maxBottom = bottom;
1489 | }
1490 |
1491 | //============================================================
1492 | // L2DViewMatrix # getScreenLeft()
1493 | //============================================================
1494 | L2DViewMatrix.prototype.getScreenLeft = function()
1495 | {
1496 | return this.screenLeft;
1497 | }
1498 |
1499 | //============================================================
1500 | // L2DViewMatrix # getScreenRight()
1501 | //============================================================
1502 | L2DViewMatrix.prototype.getScreenRight = function()
1503 | {
1504 | return this.screenRight;
1505 | }
1506 |
1507 | //============================================================
1508 | // L2DViewMatrix # getScreenBottom()
1509 | //============================================================
1510 | L2DViewMatrix.prototype.getScreenBottom = function()
1511 | {
1512 | return this.screenBottom;
1513 | }
1514 |
1515 | //============================================================
1516 | // L2DViewMatrix # getScreenTop()
1517 | //============================================================
1518 | L2DViewMatrix.prototype.getScreenTop = function()
1519 | {
1520 | return this.screenTop;
1521 | }
1522 |
1523 | //============================================================
1524 | // L2DViewMatrix # getMaxLeft()
1525 | //============================================================
1526 | L2DViewMatrix.prototype.getMaxLeft = function()
1527 | {
1528 | return this.maxLeft;
1529 | }
1530 |
1531 | //============================================================
1532 | // L2DViewMatrix # getMaxRight()
1533 | //============================================================
1534 | L2DViewMatrix.prototype.getMaxRight = function()
1535 | {
1536 | return this.maxRight;
1537 | }
1538 |
1539 | //============================================================
1540 | // L2DViewMatrix # getMaxBottom()
1541 | //============================================================
1542 | L2DViewMatrix.prototype.getMaxBottom = function()
1543 | {
1544 | return this.maxBottom;
1545 | }
1546 |
1547 | //============================================================
1548 | // L2DViewMatrix # getMaxTop()
1549 | //============================================================
1550 | L2DViewMatrix.prototype.getMaxTop = function()
1551 | {
1552 | return this.maxTop;
1553 | }
1554 |
1555 | /**
1556 | *
1557 | * You can modify and use this source freely
1558 | * only for the development of application related Live2D.
1559 | *
1560 | * (c) Live2D Inc. All rights reserved.
1561 | */
1562 |
1563 | //============================================================
1564 | //============================================================
1565 | // class Live2DFramework
1566 | //============================================================
1567 | //============================================================
1568 | function Live2DFramework()
1569 | {
1570 | }
1571 |
1572 | //============================================================
1573 | Live2DFramework.platformManager = null;
1574 |
1575 | //============================================================
1576 | // static Live2DFramework.getPlatformManager()
1577 | //============================================================
1578 | Live2DFramework.getPlatformManager = function()
1579 | {
1580 | return Live2DFramework.platformManager;
1581 | }
1582 |
1583 | //============================================================
1584 | // static Live2DFramework.setPlatformManager()
1585 | //============================================================
1586 | Live2DFramework.setPlatformManager = function( platformManager /*IPlatformManager*/ )
1587 | {
1588 | Live2DFramework.platformManager = platformManager;
1589 | }
--------------------------------------------------------------------------------