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.