Bone animation techniques can be used to assign the vertices of the mesh to several bones, and by assigning key frames and parent-child relationships to the bones, the mesh can be given a highly dynamic and transitive deformation effect. In this paper, a simple skeleton animation editing and model generation tool is realized by using JavaScript on the web based on previous research.
First, display effect:
1. Visit https://ljzc002.github.io/Bones/HTML/CstestSpaceCraft2.html to check the test page:
The babybabl.js scene on the right of the screen shows an initial grid.
2. On the Chrome console, enter ImportMesh(“”,”.. / ASSETS/SCENE/SpaceCraft. “, “Babylon”), “load before writing a SpaceCraft model, way of writing about this model can refer to https://www.cnblogs.com/ljzc002/p/9473438.html
3. Click “Add bone” button, a foldable bone editing area will be created on the left side (the class name of the label is DIV_flexible). One editing area is divided into six lines, each line contains four text boxes.
4, in the first line of the four text box input 1, 0, 0, 0, click the refresh button of the editing area, will establish a in the scene (0, 1) direction plane distance from the origin to 0, all include planar front vertex of the line (or plane) would be marked as green (” positive “can be understood as starting from the plane, If you move along the normal line of the plane to get to this vertex, you can say mathematically, “This vertex is a positive distance from the plane.”)
When the number of vertices is large, the above calculation will take some time and the console will print out the current search progress.
Entering 0, 1, 0, -3 in the second line of the editing area (indicating a distance of 3 from the origin in the opposite direction of the normal) will create another plane. Vertices facing both planes at the same time will be selected. Up to six such planes can be created.
5. After selecting these vertices as the first bone, click “Edit Key Frame” button to open the key frame editing dialog box of the first bone
The index of the parent skeleton is set as no. 0 skeleton, which can be understood as the origin of the model and remains unchanged throughout the animation process. The coordinate of the joint node is composed of three text boxes, representing the position of the connection point between this bone and the parent bone. Here, the joint node of the first bone is set as (0,0,0). In the text box below is the script for representing the keyframe matrix, read as “frames @ matrix object # frames @ matrix object”, where Ms. Xx is shorthand for the Babylon. Js matrix constructor, which corresponds as follows :(the code is in the CookBones.
3 var ms={}//MatrixScript 4 Ms. Rx =function(rad)// Rotate around the x axis 5 {6 return BABYLON.Matrix.RotationX(rad); 7} 8 ms. Ry =function(rad) {8 return baby.matrix.RotationY(rad); 5} 6 ms. Rz =function(rad) {7 return baby.matrix.rotationz (rad); 5} 16 ms.m1=function(){return bobabylon.matrix.identity (); 16} 19 Ms. Sc =function(x,y,z)// 20 { 21 return BABYLON.Matrix.Scaling(x,y,z); 22} 23 Ms. Tr = function (x, y, z) / / displacement 24 {25 return BABYLON. Matrix. Translation (x, y, z); 26} 27 //[email protected]()#[email protected](2)#[email protected]() 28 Ms. Fa =function(arr)// Generate matrix from array 29 {30 return BABYLON.Matrix.FromArray(arr); Function (VectorScript) {//VectorScript =function(VectorScript) {//VectorScript =function(VectorScript) {//VectorScript =function(VectorScript) {//VectorScript =function(VectorScript BABYLON.Vector3.TransformCoordinates(vec3.clone(),matrix); 37 } 38 var pi=Math.PI;Copy the code
Click “Write Initial Keyframe”, the keyframe Settings are saved, and the check box on the edit area is selected.
6. Then set the skeleton for the spacecraft’s wings:
Set keyframes
Since bone 2 is composed of vertices whose z value is less than or equal to -6, the key point position is set as (0,0,-6). Of course, the key point can also be set in other positions, such as this, the way of wing movement will be different.
Set the right wing symmetrically, generating bone 3.
7. After setting, click the “Preview model” button to display the bone animation effect in the X direction of the scene:
This shows a weakness of the current tool: it does not yet allow multiple bones to be bound to the same vertex, and for repeated vertices, the later bones will override the previous ones.
Because the blogosphere has a limited image size, only part of the skeleton animation can be captured.
Click “Export Model” to export the above model with bone animation in text mode.
8. The influence of father bone on child bone:
Can visit https://ljzc002.github.io/Bones/HTML/Cstest2.html page, refresh and edit three bones of key frames can see transfer of skeletal animation effects.
Two, code implementation
1. Engineering structure:
Clickbutton. js contains all button response-related code except matrix computematrix. js contains all matrix response-related code, while flex.js does not.
2. Html2D web page rendering (not the focus).
HTML file :(this includes the js code to set up a base babyl.js scene)
1 <!DOCTYPE html>
2 <html lang="en">
3 <head>
4 <meta charset="UTF-8">
5 <title>载入宇宙飞船模型进行测试</title>
6 <link href="../CSS/newland.css" rel="stylesheet">
7 <link href="../CSS/stat.css" rel="stylesheet">
8 <script src="../JS/LIB/babylon.40v.all.max.js"></script>
9 <script src="../JS/LIB/stat.js"></script>
10 <script src="../JS/MYLIB/Events.js"></script>
11 <script src="../JS/MYLIB/FileText.js"></script>
12 <script src="../JS/MYLIB/newland.js"></script>
13 <script src="../JS/MYLIB/View.js"></script>
14 <script src="../JS/MYLIB/Att7.js"></script>
15 <!--script src="../JS/MYLIB/ExportBabylonBones2.js"></script-->
16 <script src="../JS/PAGE/ClickButton.js"></script>
17 <script src="../JS/PAGE/ComputeMatrix.js"></script>
18 <script src="../JS/PAGE/CookBones.js"></script>
19 <style>
20 .div_flexible{float:left;width:100%; }
21 .div_flextop{width:100%;height:36px;background-color: #15428B;float: left}
22 .floatleft{float:left;margin-left: 10px;margin-top:6px}
23 .div_flexbottom{width:270px;margin-left:5px;height: 300px;display: none;overflow: hidden;border:1px solid #15428B;float: left}
24 .div_flexcell{float:left;height:50px;width:100%}
25 .div_key{}
26 </style>
27 </head>
28 <body oncontextmenu="return false;">
29 <!--https://ljzc002.github.io/Bones/HTML/Cstest2.html-->
30 <div style="position:absolute;top: 0px;left:0px;width:300px;height: 100%;overflow: hidden;">
31 <button style="float: left;margin-top:10px;margin-left: 10px" onclick="addBone()">新增骨骼</button>
32 <button style="float: left;margin-top:10px;margin-left: 10px" onclick="ExportMesh(obj_scene,0)">预览模型</button>
33 <button style="float: left;margin-top:10px;margin-left: 10px" onclick="ExportMesh(obj_scene,1)">导出模型</button>
34 <div style="position:absolute;top:50px;overflow-y: scroll;width:300px;height:500px" id="div_flexcontainer">
35 <!--这以内的内容都是可复制粘贴的-->
36 <div class="div_flexible" number="1">
37 <div class="div_flextop">
38 <span class="floatleft str_flexlen" style="color:darkgoldenrod;font-size: 16px;width:32px">1</span>
39 <button class="floatleft" onclick="flex()">收缩</button>
40 <button class="floatleft" onclick="OpenDivKey()">编辑关键帧</button>
41 <input class="floatleft checkbone" style="width: 20px;height: 20px;" type="checkbox">
42 <!--勾选表示导出骨骼时包含这一块,否则不导出它-》考虑到可能存在的复杂层级关系,决定导出所有骨头-->
43 <button class="floatleft" onclick="ShowClip2()">刷新</button>
44 </div>
45 <div class="div_flexbottom" style="display: block;">
46 <!--在这里设置最多六个切割平面-->
47 <div class="div_flexcell" number="1">
48 <input class="floatleft" style="width:50px" onchange="ShowClip()">
49 <input class="floatleft" style="width:50px" onchange="ShowClip()">
50 <input class="floatleft" style="width:50px" onchange="ShowClip()">
51 <input class="floatleft" style="width:50px" onchange="ShowClip()">
52 <div class="div_comment" style="display: none;">[-1,0,0,0]</div></div>
53 <div class="div_flexcell" number="2">
54 <input class="floatleft" style="width:50px" onchange="ShowClip()">
55 <input class="floatleft" style="width:50px" onchange="ShowClip()">
56 <input class="floatleft" style="width:50px" onchange="ShowClip()">
57 <input class="floatleft" style="width:50px" onchange="ShowClip()">
58 </div>
59 <div class="div_flexcell" number="3">
60 <input class="floatleft" style="width:50px" onchange="ShowClip()">
61 <input class="floatleft" style="width:50px" onchange="ShowClip()">
62 <input class="floatleft" style="width:50px" onchange="ShowClip()">
63 <input class="floatleft" style="width:50px" onchange="ShowClip()">
64 </div>
65 <div class="div_flexcell" number="4">
66 <input class="floatleft" style="width:50px" onchange="ShowClip()">
67 <input class="floatleft" style="width:50px" onchange="ShowClip()">
68 <input class="floatleft" style="width:50px" onchange="ShowClip()">
69 <input class="floatleft" style="width:50px" onchange="ShowClip()">
70 </div>
71 <div class="div_flexcell" number="5">
72 <input class="floatleft" style="width:50px" onchange="ShowClip()">
73 <input class="floatleft" style="width:50px" onchange="ShowClip()">
74 <input class="floatleft" style="width:50px" onchange="ShowClip()">
75 <input class="floatleft" style="width:50px" onchange="ShowClip()">
76 </div>
77 <div class="div_flexcell" number="6">
78 <input class="floatleft" style="width:50px" onchange="ShowClip()">
79 <input class="floatleft" style="width:50px" onchange="ShowClip()">
80 <input class="floatleft" style="width:50px" onchange="ShowClip()">
81 <input class="floatleft" style="width:50px" onchange="ShowClip()">
82 </div>
83 </div>
84 <div class="div_comment0" style="display: none;">{"str_indexp":0,"str_posjx":0,"str_posjy":0,"str_posjz":0,"text_key":"[email protected]()#[email protected](0.5)#[email protected]()#[email protected](-0.5)#[email protected]()#[email protected](0.5)#[email protected]()#[email protected](-0.5)#[email protected]()"}</div></div>
85 <!--复制粘贴的截止线-->
86 </div>
87 </div>
88 <div id="div_allbase" style="position:absolute;top: 0px;right: 0px;left:301px;height: 100%">
89 <canvas id="renderCanvas"></canvas>
90 <div id="fps" style="z-index: 301;"></div>
91 </div>
92 <div id="div_hiden" style="display: none">
93 <div class="div_hidecell">
94 <div class="div_flexible" number="1">
95 <div class="div_flextop">
96 <span class="floatleft str_flexlen" style="color:darkgoldenrod;font-size: 16px;width:32px">1</span>
97 <button class="floatleft" onclick="flex()">展开</button>
98 <button class="floatleft" onclick="OpenDivKey()" disabled="disabled">编辑关键帧</button>
99 <input class="floatleft checkbone" style="width: 20px;height: 20px;" type="checkbox">
100 <!--勾选表示导出骨骼时包含这一块,否则不导出它-》考虑到可能存在的复杂层级关系,决定导出所有骨头-->
101 <button class="floatleft" onclick="ShowClip2()">刷新</button>
102 </div>
103 <div class="div_flexbottom">
104 <!--在这里设置最多六个切割平面-->
105 <div class="div_flexcell" number="1">
106 <input class="floatleft" style="width:50px" onchange="ShowClip()">
107 <input class="floatleft" style="width:50px" onchange="ShowClip()">
108 <input class="floatleft" style="width:50px" onchange="ShowClip()">
109 <input class="floatleft" style="width:50px" onchange="ShowClip()">
110 </div>
111 <div class="div_flexcell" number="2">
112 <input class="floatleft" style="width:50px" onchange="ShowClip()">
113 <input class="floatleft" style="width:50px" onchange="ShowClip()">
114 <input class="floatleft" style="width:50px" onchange="ShowClip()">
115 <input class="floatleft" style="width:50px" onchange="ShowClip()">
116 </div>
117 <div class="div_flexcell" number="3">
118 <input class="floatleft" style="width:50px" onchange="ShowClip()">
119 <input class="floatleft" style="width:50px" onchange="ShowClip()">
120 <input class="floatleft" style="width:50px" onchange="ShowClip()">
121 <input class="floatleft" style="width:50px" onchange="ShowClip()">
122 </div>
123 <div class="div_flexcell" number="4">
124 <input class="floatleft" style="width:50px" onchange="ShowClip()">
125 <input class="floatleft" style="width:50px" onchange="ShowClip()">
126 <input class="floatleft" style="width:50px" onchange="ShowClip()">
127 <input class="floatleft" style="width:50px" onchange="ShowClip()">
128 </div>
129 <div class="div_flexcell" number="5">
130 <input class="floatleft" style="width:50px" onchange="ShowClip()">
131 <input class="floatleft" style="width:50px" onchange="ShowClip()">
132 <input class="floatleft" style="width:50px" onchange="ShowClip()">
133 <input class="floatleft" style="width:50px" onchange="ShowClip()">
134 </div>
135 <div class="div_flexcell" number="6">
136 <input class="floatleft" style="width:50px" onchange="ShowClip()">
137 <input class="floatleft" style="width:50px" onchange="ShowClip()">
138 <input class="floatleft" style="width:50px" onchange="ShowClip()">
139 <input class="floatleft" style="width:50px" onchange="ShowClip()">
140 </div>
141 </div>
142 </div>
143 </div>
144 <div class="div_hidecell">
145 <div class="div_key"><!--它的样式由open_div设定了-->
146 <span class="floatleft"></span><br>
147 <span class="floatleft ">父骨骼索引:</span><input class="floatleft str_indexp" value="0">
148 <!--span class="floatleft ">每秒帧数:</span><input class="floatleft str_fps" value="30"--><br>
149 <span class="floatleft ">关节点坐标:</span><input class="floatleft str_posjx" value="0"><input class="floatleft str_posjy" value="0"><input class="floatleft str_posjz" value="0">
150 <button class="floatleft" onclick="InsertKey()">写入初始关键帧</button>
151 <!--在这里使用一种格式化的文本,体现关键帧与矩阵,num_key@matrix#-->
152 <textarea class="floatleft text_key" style="width:90%;top:40px;height: 250px;"></textarea>
153 <button class="floatleft" onclick="delete_div('div_open');delete_div('div_mask');">取消</button>
154 </div>
155 </div>
156 </div>
157 </body>
158 <script>
159 var VERSION=1.0,AUTHOR="[email protected]";
160 var machine,canvas,engine,scene,gl,MyGame={};
161 canvas = document.getElementById("renderCanvas");
162 engine = new BABYLON.Engine(canvas, true);
163 engine.displayLoadingUI();
164 gl=engine._gl;//决定在这里结合使用原生OpenGL和Babylon.js;
165 scene = new BABYLON.Scene(engine);
166 var divFps = document.getElementById("fps");
167 window.onload=beforewebGL;
168 function beforewebGL()
169 {
170 if(engine._webGLVersion==2.0)//输出ES版本
171 {
172 console.log("ES3.0");
173 }
174 else{
175 console.log("ES2.0");
176 }
177 //MyGame=new Game(0,"first_pick","","http://127.0.0.1:8082/");
178 /*0-startWebGL
179 * */
180 webGLStart();
181 }
182 //从下面开始分成简单测试和对象框架两种架构
183 //全局对象
184 var light0//全局光源
185 ,camera0//主相机
186 ,arr_bone;//除了根骨骼之外所有骨骼的集合
187 var obj_scene=null;
188 var num_fps=30;//一开始设置好动画帧数和总帧数
189 var sum_frame=241;//其实是包括0到240的241帧
190 var mesh_origin=null;
191 function webGLStart()
192 {
193 window.addEventListener("resize", function () {
194 engine.resize();
195 });
196 if(true)
197 {
198 camera0 =new BABYLON.FreeCamera("FreeCamera", new BABYLON.Vector3(0, 0, -80), scene);
199 camera0.attachControl(canvas, true);
200 camera0.speed=0.5;
201 camera0.minZ=0.01;//问题出在这里!!设置的过小,会导致鼠标pick失败!!!!
202 light0 = new BABYLON.HemisphericLight("Hemi0", new BABYLON.Vector3(0, 1, 0), scene);
203 light0.groundColor=new BABYLON.Color3(0.5,0.5,0.5);
204 light0.specular = new BABYLON.Color3(1, 1, 1);
205 light0.diffuse = new BABYLON.Color3(1, 1, 1);
206 var advancedTexture=BABYLON.GUI.AdvancedDynamicTexture.CreateFullscreenUI("ui1");
207 var mat_green = new BABYLON.StandardMaterial("mat_green", scene);
208 mat_green.diffuseColor = new BABYLON.Color3(0, 1, 0);
209 mat_green.backFaceCulling=false;
210 var mesh_base=new BABYLON.MeshBuilder.CreateSphere("mesh_base",{diameter:1},scene);
211 mesh_base.material=mat_green;
212 mesh_base.position.x=0;
213 //mesh_base.layerMask=2;
214 var mesh_base1=new BABYLON.MeshBuilder.CreateSphere("mesh_base1",{diameter:1},scene);
215 mesh_base1.position.y=10;
216 mesh_base1.position.x=0;
217 mesh_base1.material=mat_green;
218 //mesh_base1.layerMask=2;
219 var mesh_base2=new BABYLON.MeshBuilder.CreateSphere("mesh_base2",{diameter:1},scene);
220 mesh_base2.position.y=-10;
221 mesh_base2.position.x=0;
222 mesh_base2.material=mat_green;
223 //mesh_base2.layerMask=2;
224
225 mat_frame = new BABYLON.StandardMaterial("mat_frame", scene);
226 mat_frame.wireframe = true;
227 mat_frame.freeze();
228
229 mat_alpha_yellow=new BABYLON.StandardMaterial("mat_alpha_yellow", scene);
230 mat_alpha_yellow.diffuseColor = new BABYLON.Color3(1,1,0);
231 mat_alpha_yellow.alpha=0.2;//不透明度
232 mat_alpha_yellow.freeze();
233
234 mat_red=new BABYLON.StandardMaterial("mat_red", scene);
235 mat_red.diffuseColor = new BABYLON.Color3(1,0,0);
236 mat_red.wireframe=true;
237 mat_red.freeze();
238 }
239
240 //在这里设置一个初始的默认网格,
241 mesh_origin=new BABYLON.MeshBuilder.CreateSphere("mesh_origin",{diameter:8,diameterY:64,segments:16},scene);
242 mesh_origin.material=mat_frame;
243 var vb=mesh_origin.geometry._vertexBuffers;
244 var data_pos=vb.position._buffer._data;
245 var len_pos=data_pos.length;
246 mesh_origin.matricesIndices=newland.repeatArr([0],len_pos/3);
247 mesh_origin.matricesWeights=newland.repeatArr([1,0,0,0],len_pos/3);
248 mesh_origin.skeletonId=0;
249 obj_scene=newland.CreateObjScene();
250 newland.AddMesh2Model(obj_scene,mesh_origin,"mesh_origin2");
251 newland.AddSK2Model(obj_scene,"sk_test1");//向模型中添加骨骼
252 var bone={
253 'animation':{
254 dataType:3,
255 framePerSecond:num_fps,
256 keys:[],
257 loopBehavior:1,
258 name:'_bone'+0+'Animation',
259 property:'_matrix'
260 },
261 'index':0,
262 'matrix':BABYLON.Matrix.Identity().toArray(),
263 'name':'_bone'+0,
264 'parentBoneIndex':-1
265 };
266 //bone.
267 newland.ExtendKeys(bone,sum_frame);//初始扩展根骨骼的关键帧,认为根骨骼是一直保持不变的
268 newland.AddBone2SK(obj_scene,0,bone);// 向骨骼中添加骨头
269 arr_bone=obj_scene.skeletons[0].bones;
270 BABYLON.Animation.AllowMatricesInterpolation = true;//动画矩阵插值
271 //建立两个gui显示进度,但是这样是不行的,因为此时主线程已经阻塞了,gui是不会刷新的!!
272 /*if(true)
273 {
274 var UiPanel2 = new BABYLON.GUI.StackPanel();
275 UiPanel2.width = "220px";
276 UiPanel2.height="30px";
277 UiPanel2.fontSize = "14px";
278 UiPanel2.horizontalAlignment = BABYLON.GUI.Control.HORIZONTAL_ALIGNMENT_LEFT;
279 UiPanel2.verticalAlignment = BABYLON.GUI.Control.VERTICAL_ALIGNMENT_TOP
280 UiPanel2.color = "white";
281 UiPanel2.background = "green";
282 advancedTexture.addControl(UiPanel2);
283 text1 = new BABYLON.GUI.TextBlock();
284 text1.text = "0";
285 text1.background = "blue";
286 text1.color="white";
287 text1.height="30px";
288 text1.width="30px";
289 text1.left="0px";
290 UiPanel2.addControl(text1);
291 text2 = new BABYLON.GUI.TextBlock();
292 text2.color="white";
293 text2.text = "/1";
294 text2.height="30px";
295 UiPanel2.addControl(text2);
296 }*/
297
298 reInit();//如果dom内容都是粘贴过来的,需要重新初始化一下arr_bone,相当于重新执行addbone
299 ImportMesh("","../ASSETS/SCENE/","SpaceCraft.babylon")
300 MyBeforeRender();
301 }
302 function MyBeforeRender()
303 {
304 scene.registerBeforeRender(function() {
305 if(scene.isReady())
306 {
307 }
308 });
309 engine.runRenderLoop(function () {
310 engine.hideLoadingUI();
311 if (divFps) {
312 // Fps
313 divFps.innerHTML = engine.getFps().toFixed() + " fps";
314 }
315 scene.render();
316 });
317 }
318 /*
319 * ImportMesh("","../../ASSETS/SCENE/","10.babylon")
320 * ImportMesh("","../ASSETS/SCENE/","SpaceCraft.babylon")
321 * */
322 function ImportMesh(objname,filepath,filename)
323 {
324
325 BABYLON.SceneLoader.ImportMesh(objname, filepath, filename, scene
326 , function (newMeshes, particleSystems, skeletons)
327 {//载入完成的回调函数
328 newland.ClearMeshinModel(obj_scene);
329 if(mesh_origin&&mesh_origin.dispose)
330 {
331 mesh_origin.dispose();
332 }
333 mesh_origin=newMeshes[0];
334 mesh_origin.material=mat_frame;
335 //mesh_origin.layerMask=2;
336 var vb=mesh_origin.geometry._vertexBuffers;
337 var data_pos=vb.position._buffer._data;
338 var len_pos=data_pos.length;
339 mesh_origin.matricesIndices=newland.repeatArr([0],len_pos/3);
340 mesh_origin.matricesWeights=newland.repeatArr([1,0,0,0],len_pos/3);
341 mesh_origin.skeletonId=0;
342 newland.AddMesh2Model(obj_scene,mesh_origin,"mesh_origin2");
343 }
344 );
345 }
346 </script>
347 </html>
Copy the code
View Code
The CSS file:
1 /*通用属性*/
2 body{ margin: 0; padding: 0; border: 0; text-align: center; overflow: hidden;width: 100%;
3 height: 100%;position: fixed; font-family: verdana,arial,sans-serif; touch-action: none;
4 -ms-touch-action: none;font-size: 12px;min-width: 600px;}
5 ul { list-style: none; margin: 0; padding: 0;}
6 li{ list-style: none; margin: 0; padding: 0;}
7 ul li { float: left;}
8 button{ cursor: pointer; height: 23px;}
9 a:link{ text-decoration: none;}
10
11 /*顶层属性*/
12
13 #div_control{height: 100%;width:100%;background-color: transparent;z-index: 100;position: absolute;top: 0px;left: 0px;pointer-events: none;}
14 #renderCanvas { width: 100%; height: 100%; outline: none;}
15 .div_col{width: 80px;height: 26px;padding: 5px;overflow: visible;position: relative;pointer-events: none;}
16 .to_left{float: left;text-align: left}
17 .to_right{float: right;text-align: right}
18 .btn_first{text-align: center;width: 60px;pointer-events:auto}
19 .btn_second{text-align: center;overflow: visible;overflow-wrap: normal;display: block;position: absolute;pointer-events:auto}
20 .hidden{display: none}
21 .btn_third{text-align: center;display: block;pointer-events:auto}
22 .div_mask{ height: 100%; width: 100%; display:block; z-index: 400; background: #cccccc; position: absolute;
23 left: 0px; top: 0px; filter: alpha(opacity=40); opacity: 0.40; overflow: hidden;}
24
25 /*弹出层的一些属性*/
26 .div_cook2{ margin-top: 5px; margin-left: 0px; margin-right: auto; width: 100%; height: 35px; font-family: 宋体; font-size: 12px; float: left;}
27 .div_cook2 ul{float: left;margin-top: 5px;margin-bottom: 15px}
28 .div_cook2 li{ float: left; /*margin-top: 10px;*/ margin-left: 5px; margin-right: 10px; /*color:darkred;*/ color:#020202;}
29 .div_cook2 span{ width: 120px; float: left; text-align: right; /*color: darkred;*/ color: #15428b; padding-right: 5px; font-weight: bold;}
30 .div_cook2 button{ float: left; margin-right: 5px; margin-left: 5px; width: 52px; height: 20px; text-align: center; background-color: #d6e7ef; border: solid 1px #020202;}
31 .btn_close{ float:right;position:static; width: 14px;height: 14px; margin: 0;margin-top: 2px;margin-right:2px;padding: 0
32 ;background: url(../ASSETS/IMAGE/close.png) no-repeat;border: 0px;vertical-align:top;z-index: 101;}
33 .str_number{ border: solid 1px #020202; width: 60px;}
34 .str_normal{ border: solid 1px #020202; width: 120px;}
35 .str_date{ width: 120px;}
36 .str_text{ border: solid 1px #020202; width:220px ; }
37 #fps { position: absolute; right: 20px; top: 5em; font-size: 20px; color: white;/*帧数显示*/
38 text-shadow: 2px 2px 0 black;}
39
40
41 /*表格的属性*/
42 #all_base{min-height: 400px;min-width: 250px;height: 100%;width:100%;position: relative;overflow-x:hidden;overflow-y: hidden;}
43 td input{ height: 100%; width: 100%; border:0; text-align: center; background-color: inherit;}
44 .div_tab{float: left;position: relative;width:4000px;overflow-x: hidden;overflow-y: scroll}
45 .div_tab td{ text-align: center; /*border: solid 1px #15428B;*/ border-right:solid 1px #15428B; border-bottom: solid 1px #15428B;
46 line-height: 16px; font-size: 13px; height: 24px; padding: 1px; background-color: inherit; word-break: keep-all;
47 /*display: inline-block*/}
48 .div_tab th{ text-align: center; /*border: solid 1px #15428B;*/ line-height: 16px; font-size: 13px; height: 36px;
49 padding: 1px; text-align: center; border-right: solid 1px #15428B; border-bottom: solid 1px #15428B; word-break: keep-all;
50 white-space:nowrap; overflow: hidden; text-overflow: ellipsis;/*display: inline-block*/}
51 .div_tab table{ float: left; width: auto; border-right-width:0px; border: solid 1px #15428B; table-layout: fixed;}
52 .div_tab tr{ width: auto; vertical-align: middle; /*border: solid 1px #15428B;*/ padding: 1px;}
53 td a{ cursor: pointer;}
54 td button{ cursor: pointer;}
55 .div_mask2{ display:block; left: 0px; top: 0px; /*filter: alpha(opacity=50); opacity: 0.50;*/ overflow: hidden;/*锁定的表头表列*/
56 position: absolute; float: left; overflow-x: hidden}
57 table{ border-spacing:0;}
58 .div_mask2 td{ text-align: center; /*border: solid 1px #15428B;*/ border-right:solid 1px #15428B; border-bottom: solid 1px #15428B;
59 line-height: 16px; font-size: 13px; height: 24px; padding: 1px; background-color: inherit; word-break: keep-all;}
60 .div_mask2 th{ text-align: center; /*border: solid 1px #15428B;*/ line-height: 16px; font-size: 13px; height: 36px;
61 padding: 1px; text-align: center; border-right: solid 1px #15428B; border-bottom: solid 1px #15428B; word-break: keep-all;
62 white-space:nowrap; overflow: hidden; text-overflow: ellipsis;}
63 .div_mask2 table{ float: left; width: auto; border-right-width:0px; border: solid 1px #15428B; table-layout: fixed;
64 position: absolute;}
65 .div_mask2 tr{ width: auto; vertical-align: middle; /*border: solid 1px #15428B;*/ padding: 1px;}
66 .combo-panel li{ float:none;}
67 .btn_limlen{ /*float: left;*/ height: 20px; width: 20px; border: 1px solid; /*margin-top: 6px;*/ /*margin-left: 4px;*/
68 background: url(../ASSETS/IMAGE/play.png) no-repeat; position: absolute; -moz-border-radius: 3px; /* Gecko browsers圆角 */
69 -webkit-border-radius: 3px; /* Webkit browsers */ border-radius:3px; /* W3C syntax */ position: absolute;
70 top: 6px; right: 4px;}
71
72 /*帧数显示*/
73 #fps {
74 position: absolute;
75 right: 20px;
76 top: 5px;
77 font-size: 20px;
78 color: white;
79 text-shadow: 2px 2px 0 black;
80 }
81
82 #stats {
83 position: absolute;
84 right: 20px;
85 top: 11em;
86 font-size: 14px;
87 color: white;
88 text-align: right;
89 text-shadow: 2px 2px 0 black;
90 }
91
92 #status {
93 position: absolute;
94 left: 20px;
95 bottom: 20px;
96 font-size: 14px;
97 color: white;
98 text-shadow: 2px 2px 0 black;
99 }
Copy the code
View Code
The JavaScript code that controls the zoom of the edit area :(located in the clickbutton.js file)
Var flex_current=null; / / current flex 3 function flex () / / companding a bone configuration menu 4 {5 var evt = evt | | window. The event | | the arguments [0]. 6 cancelPropagation(evt); 7 var obj=evt.currentTarget? evt.currentTarget:evt.srcElement; / / obj is 8 if companding button (obj. InnerHTML = = "on") {9 10 11 var divs = document. QuerySelectorAll (". Div_flexbottom "); Var len=divs. Length; 13 for(var i=0; i<len; i++) 14 { 15 divs[i].style.display="none"; 16 divs [I]. ParentNode. QuerySelectorAll (" button ") [0]. InnerHTML = "on"; 17 divs[i].parentNode.querySelectorAll("button")[1].disabled="disabled"; 19} 19 obj.innerHTML=" innerHTML "; 20 obj.parentNode.parentNode.querySelectorAll(".div_flexbottom")[0].style.display="block"; 21 obj.parentNode.querySelectorAll("button")[1].disabled=null; 22 ClearAllClip(); 23 if(lines_inpicked&&lines_inpicked.dispose) 24 { 25 lines_inpicked.dispose(); 26 } 27 flex_current=obj.parentNode.parentNode; 28 var divs=flex_current.querySelectorAll(".div_flexcell"); 29 var len2=divs.length; 30 for(var i=0; i<len2; i++) 31 { 32 var div_comment=divs[i].querySelectorAll(".div_comment")[0]; Parse (div_comment.innerhtml) {35 var arr= json. parse(div_comment.innerhtml); 36 var inputs=divs[i].querySelectorAll("input"); 37 inputs[0].value=arr[0]; 38 inputs[1].value=arr[1]; 39 inputs[2].value=arr[2]; 40 inputs[3].value=arr[3]; 41} 42} 43} 44 else if(obj.innerHTML==" shrink ") 45 {46 obj.innerHTML=" expand "; 47 obj.parentNode.parentNode.querySelectorAll(".div_flexbottom")[0].style.display="none"; 48 obj.parentNode.querySelectorAll("button")[1].disabled="disabled"; 49 ClearAllClip(); 50 if(lines_inpicked&&lines_inpicked.dispose) 51 { 52 lines_inpicked.dispose(); 53} 54} 55}Copy the code
View Code
3. Initialization of model objects:
Babylon. Format of js model hierarchy can refer to https://www.cnblogs.com/ljzc002/p/8927221.html
A. Create an object in the scene to export the 3D model:
The method for creating this object is in the newland.js file:
1 // Return the simplest format of the babyl.js scene 2 newland.createObjScene =function() 3 {4 var obj_scene= 5 {6 'autoClear': True, 7 'clearColor: 0, 0 and 3, 8' ambientColor ': 0, 0, 9' gravity ': [0, 9.81, 0], 10' cameras' : [], 11 'activeCamera' : null, 12 'lights':[], 13 'materials':[], 14 'geometries': {}, 15 'meshes': [], 16 'multiMaterials': [], 17 'shadowGenerators': [], 18 'skeletons': [], 19 'sounds': [] 20 }; 21 return obj_scene; 22}Copy the code
B. Add a grid to the model:
Pass the various properties of the grid object to the model object
2 newland.AddMesh2Model=function(obj_scene,mesh,name) 3 {4 var obj_mesh={}; 5 obj_mesh.name=name?name:mesh.name; 6 obj_mesh. Id =name? name:mesh.id; 7 //obj_mesh.materialId=mat.id; / / to avoid a moniker is qualitative, not to add this attribute 8 obj_mesh. Position = [mesh. Position. X, mesh. The position. The y, mesh. The position. The z]; 9 obj_mesh.rotation=[mesh.rotation.x,mesh.rotation.y,mesh.rotation.z]; 10 obj_mesh.scaling=[mesh.scaling.x,mesh.scaling.y,mesh.scaling.z]; 11 obj_mesh.isVisible=true; 12 obj_mesh.isEnabled=true; 13 obj_mesh.checkCollisions=false; 14 obj_mesh.billboardMode=0; 15 obj_mesh.receiveShadows=true; 16 obj_mesh.metadata=mesh.metadata; 17 if(mesh.matricesIndices) 18 { 19 obj_mesh.matricesIndices=mesh.matricesIndices; 20 obj_mesh.matricesWeights=mesh.matricesWeights; 21 obj_mesh.skeletonId=mesh.skeletonId; 24 {25 var vb=mesh.geometry._vertexBuffers; 26 obj_mesh.positions=newland.BuffertoArray2(vb.position._buffer._data); 27 obj_mesh.normals=newland.BuffertoArray2(vb.normal._buffer._data); 28 obj_mesh.uvs= newland.BuffertoArray2(vb.uv._buffer._data); 29 obj_mesh.indices=newland.BuffertoArray2(mesh.geometry._indices); 30 obj_mesh.subMeshes=[{ 31 'materialIndex': 0, 32 'verticesStart': 0, 33 'verticesCount': mesh.geometry._vertexBuffers.position._buffer._data.length,//mesh.geometry._totalVertices, 34 'indexStart': 0, 35 'indexCount': mesh.geometry._indices.length 36 }]; 37 obj_mesh.parentId=mesh.parent? mesh.parent.id:null; 38 } 39 else 40 { 41 obj_mesh.positions=[]; 42 obj_mesh.normals=[]; 43 obj_mesh.uvs=[]; 44 obj_mesh.indices=[]; 45 obj_mesh.subMeshes=[{ 46 'materialIndex': 0, 47 'verticesStart': 0, 48 'verticesCount': 0, 49 'indexStart': 0, 50 'indexCount': 0 51 }]; 52 obj_mesh.parentId=null; 53 } 54 obj_scene.meshes.push(obj_mesh); 55}Copy the code
C. Add bones to the model and add bones to the skeleton:
1 newland.AddSK2Model=function(obj_scene,skname) 2 { 3 var obj_sk={id:obj_scene.skeletons.length,name:skname,bones:[],ranges:[] 4 ,needInitialSkinMatrix:false} 5 obj_scene.skeletons.push(obj_sk); 6} 7 newland.addbone2sk =function(obj_scene, I,bone) 8 {9 obj_scene. Skeletons [I].bones. 10}Copy the code
D. Initialize mesh and model using the above method :(in HTML file)
1 // Set an initial default grid here, 2 mesh_origin=new BABYLON.MeshBuilder.CreateSphere("mesh_origin",{diameter:8,diameterY:64,segments:16},scene); 3 mesh_origin.material=mat_frame; 4 var vb=mesh_origin.geometry._vertexBuffers; 5 var data_pos=vb.position._buffer._data; 6 var len_pos=data_pos.length; 7 mesh_origin.matricesIndices=newland.repeatArr([0],len_pos/3); Mesh_origin. MatricesWeights = newland.repeatarr ([1,0,0,0],len_pos/3); Mesh_origine.skeletonid =0; 10 obj_scene=newland.CreateObjScene(); 11 newland.AddMesh2Model(obj_scene,mesh_origin,"mesh_origin2"); 12 newland.AddSK2Model(obj_scene,"sk_test1"); Var bone={14 'animation':{15 dataType:3, 16 framePerSecond:num_fps, 17 keys:[], 18 loopBehavior:1, 19 name:'_bone'+0+'Animation', 20 property:'_matrix' 21 }, 22 'index':0, 23 'matrix':BABYLON.Matrix.Identity().toArray(), 24 'name':'_bone'+0, 25 'parentBoneIndex':-1 26 }; 27 //bone. 28 newland.ExtendKeys(bone,sum_frame); 29 newland.AddBone2SK(obj_scene,0,bone); 29 newland.AddBone2SK(obj_scene,0,bone); // Add bones to the skeletons. 31 BABYLON.Animation.AllowMatricesInterpolation = true; // Animate matrix interpolationCopy the code
Bone 0 is established as the lowest root bone of all bones, which remains unchanged and does not participate in subsequent Settings.
E, add an edit area (a bone) :(in clickbutton.js)
2 {3 var container= document.getelementById (" div_flexContainer "); 4 container.appendChild(document.querySelectorAll("#div_hiden .div_flexible")[0].cloneNode(true)); 5 var divs=container.querySelectorAll(".div_flexible"); 6 var len=divs.length; 7 divs[len-1].number=len; 8 divs[len-1]. QuerySelectorAll (".str_flexLen ")[0]. InnerHTML =len+""; 9 var bone={ 10 'animation':{ 11 dataType:3, 12 framePerSecond:num_fps, 13 keys:[], 14 loopBehavior:1, 15 name:'_bone'+len+'Animation', 16 property:'_matrix' 17 }, 18 'index':len, 19 'matrix':BABYLON.Matrix.Identity().toArray(), 20 'name':'_bone'+len, 21 'parentBoneIndex':0 22 } 23 newland.AddBone2SK(obj_scene,0,bone); 24}Copy the code
4. Import other models
As a model editing tool, it is not possible to only work with the initial model. Use the ImportMesh method to import the babybabe.js model instead of the initial model:
1 /* 2 * ImportMesh("",".. /ASSETS/SCENE/","10.babylon") 3 * ImportMesh("",".. /ASSETS/SCENE/","SpaceCraft.babylon") 4 * */ 5 function ImportMesh(objname,filepath,filename) 6 { 7 8 BABYLON.SceneLoader.ImportMesh(objname, filepath, filename, scene 9 , function (newMeshes, particleSystems, Newland.clearmeshinmodel (obj_scene); newland.clearmeshinModel (obj_scene); 12 if(mesh_origin&&mesh_origin.dispose) 13 { 14 mesh_origin.dispose(); 15 } 16 mesh_origin=newMeshes[0]; 17 mesh_origin.material=mat_frame; 18 //mesh_origin.layerMask=2; 19 var vb=mesh_origin.geometry._vertexBuffers; 20 var data_pos=vb.position._buffer._data; 21 var len_pos=data_pos.length; 22 mesh_origin.matricesIndices=newland.repeatArr([0],len_pos/3); 23 mesh_origin. MatricesWeights = newland. RepeatArr (,0,0,0 [1], len_pos / 3); 24 mesh_origin.skeletonId=0; 25 newland.AddMesh2Model(obj_scene,mesh_origin,"mesh_origin2"); 26} 27); 28}Copy the code
5. Bone division:
A. Create a plane according to the input in the edit area when clicking the refresh button:
1 function ClearAllClip() {2 var len=arr_plane.length; 4 for(var i=0; i<len; i++) 5 { 6 var plane=arr_plane[i]; 7 plane.cylinder.dispose(); 8 plane.mesh.dispose(); 9 plane.lines_normal.dispose(); 10 11 plane=null; 12 } 13 arr_plane=[]; 14 } 15 16 var arr_plane=[]; Function ShowClip() function ShowClip() Otherwise later took a great trouble to add 18 19 20} {21 function ShowClip2 () / / clicking refresh calculated according to the current slope bone area 22 {23 var evt = evt | | window. The event | | the arguments [0]. 24 cancelPropagation(evt); 25 var obj=evt.currentTarget? evt.currentTarget:evt.srcElement; //obj is the refresh button 26 ClearAllClip(); // Clear all possible planes divs=obj.parentNode.parentNode.querySelectorAll(".div_flexbottom")[0].querySelectorAll(".div_flexcell"); 28 var str_number=obj.parentNode.parentNode.querySelectorAll("span")[0].innerHTML//.getAttribute("number"); Var len=6; 30 for(var i=0; i<len; Var div=divs[I]; 33 var inputs=div.querySelectorAll("input"); 34 var len2=4; 35 var flag=0; 36 var arr=[]; 37 for(var j=0; j<len2; If (isNaN(inputs parseFloat(inputs[j].value)))// If (isNaN(inputs parseFloat [j].value)) 40 {41 flag=1; 42 break; 43 } 44 else 45 { 46 arr.push(parseFloat(inputs[j].value)); 5 {5 var plane=new BABYLON.Plane(arr[0], ARr [1], ARr [2], arr[3]); 5 {5 var plane=new BABYLON.Plane(arr[0], ARr [1], ARr [2], arr[3]); 52 var div_comment=div.querySelectorAll(".div_comment")[0]; 53 if(! 54 {55 div_comment=document.createElement("div"); // create a hidden element to persist the Settings 56 div_comment.style.display="none"; 57 div_comment.className="div_comment"; 58 div.appendChild(div_comment); 59 } 60 div_comment.innerHTML=JSON.stringify(arr); 61 62 plane.normalize(); // We must first standardize the plane, Inaccurate or generate planar grid reference (vector length generated) 63 var mesh_plane = new BABYLON. MeshBuilder. CreatePlane (" mesh_plane "+ I 64 ,{sourcePlane:plane,sideOrientation:BABYLON.Mesh.DOUBLESIDE,size:50},scene); 65 //sourcePlane Has a Bug in sourcePlane when tilting !!!! ?? 66 mesh_plane.material=mat_alpha_yellow; // The mesh generated by plane has no rotation?? 67 var pos1=mesh_plane.position.clone(); 68 var vec_nomal=plane.normal.clone().normalize(); 69 var pos2=pos1.add(vec_nomal); 70 var lines=[[pos1,pos2]]; 71 var lines_normal=new BABYLON.MeshBuilder.CreateLineSystem("lines_normal"+i,{lines:lines,updatable:false},scene); 72 lines_normal.color=new BABYLON.Color3(1, 0, 0); 73 var cylinder = BABYLON. MeshBuilder. CreateCylinder (" cylinder "+ I, {height: 1, diameterTop: 0, diameterBottom: 0.2}, scene). 74 cylinder.parent=mesh_plane; 75 cylinder.rotation.x-=Math.PI/2; - 76 cylinder. The position. Z = 1.5; 77 cylinder.material=mat_red; 78 79 plane.mesh=mesh_plane; 80 plane.lines_normal=lines_normal; 81 plane.cylinder=cylinder; 82 arr_plane.push(plane); 83 } 84 else 85 { 86 var div_comment=div.querySelectorAll("div_comment")[0]; 87 if(div_comment) 88 {89 delete_div(div_comment); 90 } 91 } 92 } 93 requestAnimFrame(function(){FindVertex(str_number); }); 94 //FindVertex(str_number); // Find the vertex belonging to this skeletonCopy the code
Line 28 uses the number in the upper left corner of the edit area to distinguish which bone is being edited.
The next step is to iterate over each line of the edit area, create a flat object if the line’s input meets the requirements for a flat, and then store the line’s input in a DOM document as an invisible label.
The next step is to normalize the plane by using the normalized plane to create a plane grid (line 63). Note here that “plane” is a mathematical object in Babylu.js and is not actually displayed; the plane grid is actually displayed. Normalization means keeping the direction constant so that the magnitude of the plane’s direction vector is equal to one.
Then a line segment and a cone grid are constructed in the center of the plane grid to represent the normal vector.
Finally, the browser was informed to perform vertex search calculation in the next frame rendering, and it was found that directly executing FindVertex method program would cause errors. The reason was not thoroughly studied due to time limitation.
B, select vertex with plane object:
1 var lines_inpicked=null; 1 function FindVertex(str_number) {2 if (divFps) {5 if (divFps) {5 if (divFps) 6 divFps. InnerHTML = "0fps"; 7 } 8 if(! mesh_origin||! Dispose) 9 {10 console.log(" model not loaded yet "); 11 return; 12 } 13 if(lines_inpicked&&lines_inpicked.dispose) 14 { 15 lines_inpicked.dispose(); 16 } 17 var len=arr_plane.length; 19 {20 var mesh=mesh_origin; 21 var vb=mesh.geometry._vertexBuffers; 22 var data_pos=vb.position._buffer._data; 23 var len_pos=data_pos.length; 24 var data_index=mesh.geometry._indices; 25 var len_index=data_index.length; 26 var lines=[]; 27 var matricesIndices=mesh_origin.matricesIndices; 28 var matricesWeights=mesh_origin.matricesWeights; 29 30 for(var i=0; i<len_pos; I + = 3) / / for each vertex {31 32. The console log (I / 3 + 1 + "/" + len_pos / 3); If (matricesIndices[I /3]==parseInt(str_number)) {35 matricesIndices[I /3]=0; 36 } 37 var pos=new BABYLON.Vector3(data_pos[i],data_pos[i+1],data_pos[i+2]); 38 var flag=0; 39 for(var j=0; j<len; Var num=arr_plane[j]. SignedDistanceTo (pos); 42 if(num<0) 43 { 44 flag=1; 45 break; 46 } 47 } 48 if(flag==0) 49 { 50 var index_vertex=i/3; 51 var vec=pos; 52 matricesIndices[index_vertex]=parseInt(str_number); // Change the skeleton binding for this vertex. 54 for(var j=0; j<len_index; If (index_vertex==data_index[j]) {if(index_vertex==data_index[j]) {var num2=data_index[j]*3; 59 var num3=data_index[j+2]*3; 60 var vec2=new BABYLON.Vector3(data_pos[num2],data_pos[num2+1],data_pos[num2+2]); 61 var vec3=new BABYLON.Vector3(data_pos[num3],data_pos[num3+1],data_pos[num3+2]); 62 lines.push([vec,vec2]); 63 lines.push([vec,vec3]); If (index_vertex==data_index[j] +1]) else if(index_vertex==data_index[j]* 2; 68 var num3=data_index[j+2]*3; 69 var vec2=new BABYLON.Vector3(data_pos[num2],data_pos[num2+1],data_pos[num2+2]); 70 var vec3=new BABYLON.Vector3(data_pos[num3],data_pos[num3+1],data_pos[num3+2]); 71 lines.push([vec,vec2]); 72 lines.push([vec,vec3]); If (index_vertex==data_index[j]) else if(index_vertex==data_index[j]*3; 77 var num3=data_index[j+1]*3; 78 var vec2=new BABYLON.Vector3(data_pos[num2],data_pos[num2+1],data_pos[num2+2]); 79 var vec3=new BABYLON.Vector3(data_pos[num3],data_pos[num3+1],data_pos[num3+2]); 80 lines.push([vec,vec2]); 81 lines.push([vec,vec3]); 82 } 83 } 84 } 85 } 86 lines_inpicked=new BABYLON.MeshBuilder.CreateLineSystem("lines_inpicked",{lines:lines,updatable:false},scene); 87 lines_inpicked.color=new BABYLON.Color3(0, 1, 0); 88} 89 else 90 {91 console.log(" No matching bevels "); 92 return; 94 93}}Copy the code
For each vertex, first clear its current bone index (35 rows, consider setting multiple bones in the future).
We then use the signedDistanceTo method at line 41 to get the distance from this vertex to the plane, positive if the vertex is in the normal direction of the plane (positive) and negative if the vertex is in the plane (positive).
If this vertex is in the forward direction for all the splicing planes (up to six), then the bone index of this vertex is set to the currently edited bone. And highlight the edge of the vertex. (highlighted vertex method can refer to https://www.cnblogs.com/ljzc002/p/9353101.html)
6. Key frame script parsing:
To simplify the setup of the keyframe matrix, I have established a simple set of keyframe scripting rules, which are resolved as follows :(the code is located in the computematrix.js file)
1 function InsertKey () / / according to the key frames of mesh skeleton key frames of matrix modified 2 {3 var div_open = document. QuerySelectorAll (" # div_open ") [0]; Var obj={}; 5 obj.str_indexp=parseInt(div_open.querySelectorAll(".str_indexp")[0].value); 6 //obj.str_fps=div_open.querySelectorAll(".str_fps")[0].value; 7 obj.str_posjx=parseInt(div_open.querySelectorAll(".str_posjx")[0].value); 8 obj.str_posjy=parseInt(div_open.querySelectorAll(".str_posjy")[0].value); 9 obj.str_posjz=parseInt(div_open.querySelectorAll(".str_posjz")[0].value); 10 obj.text_key=div_open.querySelectorAll(".text_key")[0].value; 11 var str_key=obj.text_key; 12 str_key.replace("?" , ""); // Replace the newline character. In Chrome, the newline character is the vertical arrow of the carriage return, but here it is displayed as? 13 str_key.replace("\r",""); 14 str_key.replace("\n",""); 15 var arr_key=str_key.split("#"); 16 var len=arr_key.length; Var bone=arr_bone[flex_current. QuerySelectorAll ("span")[0].innerhtml]//.getAttribute("number")]; 19 //var inputs=document.querySelectorAll("#div_open input"); 20 //bone.animation.framePerSecond=parseInt(inputs[1].value); 21 bone.parentBoneIndex=obj.str_indexp; 22 bone.animation.keys=[]; // All initial keyframes of this bone will be overwritten every time you click the calculation - "Extension keyframes should be placed in the following link !!!! 23 var div_comment=flex_current.querySelectorAll(".div_comment0")[0]; // Comment information to extract from the flex object 24 if(! Div_comment =document.createElement("div"); div_comment=document.createElement("div"); 27 div_comment.style.display="none"; 28 div_comment.className="div_comment0"; 29 flex_current.appendChild(div_comment); 30 } 31 div_comment.innerHTML=JSON.stringify(obj); 32 try 33 { 34 var pos_gj=new BABYLON.Vector3(obj.str_posjx,obj.str_posjy,obj.str_posjz); 35 bone.pos_gj=pos_gj; 36 for(var I =0; i<len; I ++)// For each keyframe 37 {38 var key=arr_key[I]; Var arr=key.split("@"); var arr=key.split("@"); 40 var num_frame=parseInt(arr[0]); Var script_frame=arr[1]; 42 var matrix=eval(script_frame); / / object according to the script calculates the matrix 43 and 44 / / var count = ipads. Animation. Keys. Length; 45 //var matrix2=LoadParent4ThisKey(bone,count,true); 46 47 //var vec_temp2=BABYLON.Vector3.TransformCoordinates(pos_gj,matrix2) 48 // .subtract(BABYLON.Vector3.TransformCoordinates(pos_gj,matrix.multiply(matrix2))); 49 //bone.animation.keys.push({frame:num_frame,values:matrix.multiply(BABYLON.Matrix.Translation(vec_temp2.x,vec_temp2.y,ve c_temp2.z)).toArray() 50 //}); / / push 51 ipads. Each key frame animation. Keys. Push ({frame: num_frame, values: matrix. The toArray ()}) 52 and 53} 54 flex_current.querySelectorAll(".checkbone")[0].checked=true; 55 } 56 catch(e) 57 { 58 console.error(e); 59} 60 finally 61 {// close dialog box 62 delete_div('div_open'); 63 delete_div("div_mask"); 64} 65 66}Copy the code
7. Preview the model
Click preview model after finishing the previous bone editing, and the program begins to enter the most critical matrix calculation process. The first step is to preview and export the code of the model:
1 var mesh_test=null; // This must be declared in advance, otherwise !!!! will be reported in the ImportMesh callback 2 function ExportMesh(obj_scene,flag) {4 5 if(flag==1) 6 {7 var str_data= json.stringify (obj_scene); 8 DownloadText(MakeDateStr()+"testscene",str_data,".babylon"); 9} 10 else if(flag==0) 11 {12 var str_data=""; 13 //var bones=obj_scene.skeletons[0].bones; 14 HandleBones(); 16 16 str_data= json.stringify (obj_scene); 17 // Add extended keyframe calculation in live demo session?? 18 BABYLON.SceneLoader.ImportMesh("", "", "data:"+str_data, scene 19 , function (newMeshes, particleSystems, // Finish the callback? 20 try{21 if(mesh_test) 22 {23 mesh_test.dispose(); 24 } 25 mesh_test=newMeshes[0]; 26 mesh_test.position.x=50; 27 mesh_test.material=mat_frame; 28 //var totalFrame=skeletons[0]._scene._activeSkeletons.data.length; 29 skeleton=skeletons[0]; 30 scene. BeginAnimation (skeleton, 0, sum_frame, true, 0.5); // Start skeleton animation 31} 32 catch(e) 33 { 34 console.log(e); 35} 36 37}); 38}} 39Copy the code
As you can see, only the HandleBones method is available in the response of the live demo button, so you can click export model after the live demo is complete.
Another problem that needs to be noted is that in debug mode, if there is an unintercepted exception in the ImportMesh callback function, the browser will try to open the Babylon. Js file for debugging and output the text of the entire model file as an exception information. This will consume a lot of CPU resources and cause the browser to stall or even crash. So try catching to prevent this kind of problem.
8. Keyframe extension
We only set up in the script in front of the nine 30 frames between keyframes, though Babylon. Js also support for animation key frames matrix interpolation automatically, but in order to avoid the bone scaling and dislocation by linear interpolation (www.cnblogs.com/ljzc002/p/8…
Var len=arr_bone.length; var len=arr_bone.length; var len=arr_bone.length; 4 var total=len*sum_frame; 5 console.log(" Start extending keyframes for non-root bones "); 6 for(var i=1; i<len; 7 {8 var bone=arr_bone[I]; 9 newland.ExtendKeys(bone,sum_frame); 10 console.log(i+"/"+(len-1)); 11}Copy the code
1 newland.ExtendKeys=function(bone,sum_frame) 2 { 3 var keys=bone.animation.keys; 4 var keys2=[]; If (keys.length==0)// If (keys.length==0)// If (keys.length==0) i<sum_frame; i++) 8 { 9 keys2.push({frame:i,values:BABYLON.Matrix.Identity().toArray()}); 10 } 11 } 12 else 13 { 14 var count_frame=0; For (var I =0; i<keys.length; I ++)// For each initial keyframe 16 {17 var key1=keys[I]; 18 if(i<(keys.length-1)) 19 { 20 var key2=keys[i+1]; 21 var frame_between=key2.frame-key1.frame; 22 var j=0; 23 var m1=BABYLON.Matrix.FromArray(key1.values); 24 var m2=BABYLON.Matrix.FromArray(key2.values); 25 while(count_frame<=key2.frame) 26 { 27 var rate=j/frame_between; 28 var m_lerp=BABYLON.Matrix.Lerp(m1,m2,rate); 29 newland.NormalizeMatrix(m_lerp); 30 keys2.push({frame:count_frame,values:m_lerp.toArray()}); 31 count_frame++; 32 j++; 33 } 34 } 35 else 36 { 37 while(count_frame<sum_frame) 38 { 39 keys2.push({frame:count_frame,values:key1.values}); // The values should be an object, but just exporting JSON does not seem to clone the new object. 41 } 42 } 43 } 44 } 45 bone.animation.keys=keys2; 46 return keys2; 47}Copy the code
ExtendKeys method through bone has set the initial key frames, if there is no set initial key frames, use do not contain any change fill 241 unit matrix extension of key frames, such as bone, if set the initial key frames, first use linear interpolation for extended key frames, and to expand the key frames for standardized operation.
Matrix standardization avoids the scaling of bone size caused by linear interpolation, but at the cost of making the user unable to set the scaling of bone animation. Consider adding another variable to maintain the size change, and scale the normalized matrix again.
Another thing to note is that the matrix (matrix.toarray ()) is stored as a one-dimensional array in the ibb.js bone object, whereas the actual matrix transformation is performed using a matrix-like object (matrix.fromarray (arr);). .
After the above calculation, all the bones were expanded to 241 keyframes.
9. Matrix transformation inherited from parent skeleton:
Because I am not good at mathematics in college, there is not enough theoretical basis for the algorithm in this section, which is mainly obtained by reasoning and trial and error based on high school mathematics knowledge :(the second half of HandleBones)
1 var joints=[]; 2 //var joint0=arr_bone[1].pos_gj; 3 console.log(" start adjusting key frames for non-root bones ") 4 for(var I =1; i<len; Var bone=arr_bone[I]; var bone=arr_bone[I]; 7 joints=[]; 5 var sum_j=new BABYLON.Vector3(0,0,0); 9 var bone_now=bone; 12 var parent=arr_bone[bone_now.parentBoneIndex]; If (parent. Index! =0)// 15 {16 joints. Unshift ({pos_gj:bone_now.pos_gj.clone().subtract(parent.pos_gj),index:parent.index}); 17 sum_j=sum_j.add(bone_now.pos_gj.clone().subtract(parent.pos_gj)); 18 bone_now=parent; 19 } 20 else 21 { 22 joints.unshift({pos_gj:bone_now.pos_gj,index:0}); 23 sum_j=sum_j.add(bone_now.pos_gj); 24 break; 25 } 26 } 27 var keys=bone.animation.keys; 28 var keys2=[]; 29 bone.keys2=keys2; 30 bone.vec_adjusts=[]; 31 bone.temp1=[]; / / the father. Multiply the son 32 ipads. Temp1b = []; / / child. Multiply the father 33 ipads. Sum_j = sum_j; 34 bone.temp3=[]; 35 bone.joint=joints[joints.length-1].pos_gj.clone(); For (var j=0; j<sum_frame; Var matrix=ms. Fa (keys[j]. Values); Var parent=arr_bone[bone.parentboneindex]; var parent=arr_bone[bone.parentboneindex]; 40 if(bone.parentBoneIndex==0)// 1 {42 bone.temp1. Push (matrix); 43 bone.temp1b.push(matrix); 44 bone.vec_adjusts.push(bone.pos_gj.clone().subtract(vs.tr(sum_j.clone(),matrix))); 45 bone.temp3.push(bone.pos_gj.clone()); Multiply (matrix) = matrix.multiply (matrix); multiply(matrix) = matrix.multiply (matrix); multiply(matrix) = matrix.multiply (matrix); multiply(matrix) = matrix.multiply (matrix); 51 bone.temp1.push(matrix2); 52 var matrix2b=(matrix.clone()).multiply(parent.temp1b[j].clone()); 53 bone.temp1b.push(matrix2b); 54 var vec=vs.tr(bone.joint.clone(),parent.temp1b[j].clone()); 55 bone.temp3.push(parent.temp3[j].clone().add(vec)); 56 bone.vec_adjusts.push((parent.temp3[j].clone().add(vec).subtract(vs.tr(bone.sum_j,matrix2)))); 57 } 58 var vec_adjust=bone.vec_adjusts[j].clone(); 59 if(bone.parentBoneIndex! =0) 60 { 61 vec_adjust=vs.tr(vec_adjust.subtract(parent.vec_adjusts[j]),parent.temp1b[j].clone().invert()); Multiply (Ms. Tr (vec_adjuste.x, vec_adjuste.y, vec_adjuste.z))// Matrix_adjusted =matrix.multiply(Ms. Tr (vec_adjuste.x, vec_adjuste.y, vec_adjuste.z) keys2.push({frame:j,values:matrix_adjusted.toArray()}); 65 //var vec_adjust1=joint0.clone(),vec_adjust2=vs.tr(sum_j.clone(),matrix); Var len2= signals.length; var len2= signals.length; var len2= signals.length; /*for(var k=1; k<len2; K ++)// for each upstream joint 69 {// This is used to enforce all calculations. 71 for(var l=1; l<=k; l++) 72 { 73 vec1=vs.tr(vec1,ms.fa(arr_bone[joints[l].index].animation.keys[j].values)); 74 } 75 vec_adjust1=vec_adjust1.add(vec1); Adjust2 vs.adjust2, Ms.fa (ARR_bone [k].index].animation.keys[j].values) /*for(var k=0; k<len2; 27 var adjust_adjust =vec_adjust1. Subtract (Vec_adjust2); 86 var matrix_adjusted=matrix.multiply(Ms. Tr (vec_adjuste.x, vec_adjuste.y, vec_adjuste.z) keys2.push({frame:j,values:matrix_adjusted.toArray()}); 87 bone.vec_adjusts.push(vec_adjust); */ 88} 89 console.log(I +"/"+(len-1)); 90 } 91 for(var i=1; i<len; I ++)// Replace keys 92 {93 var bone=arr_bone[I]; 94 //var temp=bone.animation.keys; 95 bone.animation.keys=bone.keys2; //bone.keys2=temp; 97 delete bone.keys2; // Save file size 98 delete bone.temp1; 99 delete bone.vec_adjusts; 101 100}}Copy the code
A. The algorithms in the above code are described in the following figures:
Assume that all bone animations are rotated around the X-axis, ABC represents the turnable Angle, j0, j1 and j2 represent the vectors between the two turnable points, and j0·a represents the matrix transformation of turning Angle A applied to vector j0.
Figure 1 shows that there is only one non-root bone and the joint position of the bone is not at the origin. We call the fixed root bone “bone 0” and the part rotating around point A “bone 1”. We hope that the bone 1 point A rotate Angle to reach A solid position shown in the figure, but if we only put 1 key frames of matrix is set to A bone, in the actual execution of animation as bone 1 will with the origin as A rotation axis in the dotted line in the figure position, we will vector translation from A dotted line to the solid line is defined as “vec_adjust” (fixed vector). Applying this correction vector to the matrix of each frame of the bone can adjust the key frame to its destination position.
In the case of Figure 1, VEC_adjust =j0-j0·a.
In FIG. 2, there are two non-root bones (limited camera conditions, let’s see), and the correction vector is j0+ J1 ·a-(j0+ J1)·a·b.
There are three non-root bones in Figure 3. In order to simplify thinking, point A is made to coincide with the origin, and the correction vector is J1 · A + J2 · A ·b-(j1+ J2)· A · B · C.
Based on the above three situations, the calculation rule of the correction vector is predicted as follows:
Vec_adjust = j0 + a + j1, j2 a. b – (j0 + j1 + j2) a. b. c
After the actual test, the rule should be:
Vec_adjust = j0 + j1, a + j2, b. a. (j0 + j1 + j2) a. b. c
But we don’t know how it works.
It can be observed that the correction vector of the child bone contains the component of the correction vector of the parent bone. In order to save calculation cost, j0+ J1 ·a=temp3, A ·b = temp1, B · A = temp1b, and j0+ J1 correspond to sum_j of the joint vector.
So for 241 frames of this bone, the VEC_adjust for each frame was calculated.
B. Next, the calculated correction vector will be processed in two steps
First, the child bone will inherit the matrix transformation of the parent bone in the same frame, which also includes the parent bone’s correction vector, so the child bone’s correction vector should be changed to the difference between the child bone’s correction vector and the parent bone’s correction vector. (Maybe we can simplify the calculation here.)
In addition, the correction vector we just calculated is in the world coordinate system, and we need to transform it into the local coordinate system of the bone to be affected by the parent bone along with the bone.
Line 63 of the above code completes both steps.
C. Apply the matrix changes represented by the correction vector to the key frame matrix, and clear the redundant attributes in bone objects to reduce the size of the generated model file.
Thus the matrix calculation of the skeleton model is completed.
10. Save and load skeleton Settings
Just use the console to copy the contents of the div_flexContainer tag and paste it into a copied HTML page.
The reInit method reads the previous Settings when the page loads:
1 function reInit() 2 { 3 var flexs=document.querySelectorAll("#div_flexcontainer")[0].querySelectorAll(".div_flexible"); 4 var len=flexs.length; 5 for(var i=0; i<len; Var flex=flexs[I]; 8 var bone={ 9 'animation':{ 10 dataType:3, 11 framePerSecond:num_fps, 12 keys:[], 13 loopBehavior:1, 14 name:'_bone'+(i+1)+'Animation', 15 property:'_matrix' 16 }, 17 'index':(i+1), 18 'matrix':BABYLON.Matrix.Identity().toArray(), 19 'name':'_bone'+(i+1), 20 'parentBoneIndex':0 21 } 22 newland.AddBone2SK(obj_scene,0,bone); 23 24 var divs=flex.querySelectorAll(".div_flexcell"); // Initialize the text box according to the initial values that may exist, but you still need to manually click the refresh button for each skeleton. 26 for(var j=0; j<len2; Var div_comment=divs[j]. QuerySelectorAll (".div_comment")[0]; Parse (div_comment.innerhtml) {31 var arr= json. parse(div_comment.innerhtml); 32 var inputs=divs[j].querySelectorAll("input"); 33 inputs[0].value=arr[0]; 34 inputs[1].value=arr[1]; 35 inputs[2].value=arr[2]; 36 inputs[3].value=arr[3]; 37 } 38 } 39 40 var div_bottom=flex.querySelectorAll(".div_flexbottom")[0]; 41 if(div_bottom.style.display=="block") 42 { 43 flex_current=flex; 44} 45} 46}Copy the code
Third, summary
The function of the current skeleton editor is very primitive, and it does not support multi-skeleton binding or zooming skeleton animation. The algorithm has not been rigorously tested, so there may be various problems. Welcome to help test and point out the problems.