欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

基于Babylon.js编写简单的骨骼动画生成器

程序员文章站 2022-03-24 13:43:43
使用骨骼动画技术可以将网格的顶点分配给若干骨头,通过给骨头设定关键帧和父子关系,可以赋予网格高度动态并具有传递性的变形 效果。这里结合之前的相关研究在网页端使用JavaScript实现了一个简单的骨骼动画编辑和模型生成工具。 一、显示效果: 1、访问https://ljzc002.github.io ......

  使用骨骼动画技术可以将网格的顶点分配给若干骨头,通过给骨头设定关键帧和父子关系,可以赋予网格高度动态并具有传递性的变形 效果。这里结合之前的相关研究在网页端使用javascript实现了一个简单的骨骼动画编辑和模型生成工具。  

  一、显示效果:

1、访问https://ljzc002.github.io/bones/html/cstestspacecraft2.html查看测试页面:

基于Babylon.js编写简单的骨骼动画生成器

  屏幕右侧的babylon.js场景中是一个初始网格。

2、在chrome浏览器控制台输入“importmesh("","../assets/scene/","spacecraft.babylon")”,载入之前编写的一个宇宙飞船模型,关于这个模型的编写方式可以参考https://www.cnblogs.com/ljzc002/p/9473438.html

基于Babylon.js编写简单的骨骼动画生成器

3、点击“新增骨骼”按钮,会在左侧建立一个可折叠的骨头编辑区(标签的类名是div_flexible),一个编辑区分为六行,每行包括四个文本框。

基于Babylon.js编写简单的骨骼动画生成器

4、在第一行的四个文本框中输入-1、0、0、0,点击这个编辑区的刷新按钮,将在场景中建立一个朝向(-1,0,0)方向距原点距离为0的平面,所有包含平面正面(或平面上)顶点的线会被标为绿色(“正面”可以理解为从平面出发,沿着平面法线方向移动可以到达这个顶点,数学上可以说“这个顶点到平面的距离为正”)

基于Babylon.js编写简单的骨骼动画生成器

  当顶点的数量较多时,上述计算会花费一定时间,控制台里会打印出当前的查找进度。

  在编辑区的第二行输入0、1、0、-3(表示沿法线的反方向到原点的距离为3)会建立另一个平面,同时处于两个平面正面的顶点会被选中,最多可以建立6个这样的区分平面。

基于Babylon.js编写简单的骨骼动画生成器

5、选定这些顶点作为一号骨头后,点击“编辑关键帧”按钮将打开一号骨头的关键帧编辑对话框

基于Babylon.js编写简单的骨骼动画生成器

  其中父骨骼索引设为0号骨骼,0号骨骼可以理解为模型的原点,在整个动画过程中保持不变;关节点坐标由三个文本框组成,表示这一块骨头和父骨头的连接点的位置,这里一号骨头的关节点设为(0,0,0)。下面的文本框里是表示关键帧矩阵的脚本,解读规则为“帧数@矩阵对象#帧数@矩阵对象”,其中的ms.xx是简写的babylon.js矩阵构造函数,其对应关系如下:(代码位于cookbones.js文件中)

 1 //在这里写对关键帧脚本的处理和骨骼模型导出
 2     //定义一种简单的脚本简化输入
 3 var ms={}//matrixscript
 4 ms.rx=function(rad)//绕x轴旋转
 5 {
 6     return babylon.matrix.rotationx(rad);
 7 }
 8 ms.ry=function(rad)//绕y轴旋转
 9 {
10     return babylon.matrix.rotationy(rad);
11 }
12 ms.rz=function(rad)//绕z轴旋转
13 {
14     return babylon.matrix.rotationz(rad);
15 }
16 ms.m1=function(){//生成一个单位阵
17     return babylon.matrix.identity();
18 }
19 ms.sc=function(x,y,z)//缩放,因为做了矩阵标准化,在现在的场景里缩放不会起作用!!
20 {
21     return babylon.matrix.scaling(x,y,z);
22 }
23 ms.tr=function(x,y,z)//位移
24 {
25     return babylon.matrix.translation(x,y,z);
26 }
27 //0@ms.m1()#120@ms.rx(2)#240@ms.m1()
28 ms.fa=function(arr)//从数组生成矩阵
29 {
30     return babylon.matrix.fromarray(arr);
31 }
32 
33 var vs={}//vectorscript
34 vs.tr=function(vec3,matrix)//对向量进行矩阵变化
35 {
36     return babylon.vector3.transformcoordinates(vec3.clone(),matrix);
37 }
38 var pi=math.pi;

  点击“写入初始关键帧”,则关键帧设置被保存,同时编辑区上的复选框会被选中。

6、再给宇宙飞船的翅膀设置骨骼:

基于Babylon.js编写简单的骨骼动画生成器

  设置关键帧

基于Babylon.js编写简单的骨骼动画生成器

  因为是取z值小于等于-6的顶点组成骨头2,所以将关节点位置设为(0,0,-6),当然,也可以把关节点设在其他位置,如过这样做翅膀的运动方式将有所不同。

  对称的设置右侧的翅膀,生成骨头3。

基于Babylon.js编写简单的骨骼动画生成器

7、设置完成后,点击“预览模型”按钮,将在场景的x方向显示骨骼动画效果:

基于Babylon.js编写简单的骨骼动画生成器

  这里体现出当前工具的一个缺点:尚未允许同一顶点绑定多块骨头,对于重复选取的顶点,后设置的骨头会覆盖之前的设置。

  因为博客园对图片大小有限制,只能截取骨骼动画的一部分。

  点击“导出模型”则可以文本方式导出上述含有骨骼动画的模型。

8、父骨头对子骨头的影响:

可以访问https://ljzc002.github.io/bones/html/cstest2.html页面,刷新并编辑三块骨头的关键帧可以看到骨骼动画的传递效果。

  二、代码实现

1、工程结构:

基于Babylon.js编写简单的骨骼动画生成器

其中clickbutton.js里是除矩阵计算外所有和按钮响应相关的代码,computematrix.js里是所有和矩阵计算有关的代码,flex.js没用。

2、html2d网页绘制(并不是重点)。

html文件:(其中包括建立一个基础babylon.js场景的js代码)

基于Babylon.js编写简单的骨骼动画生成器
  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":"0@ms.m1()#30@ms.rz(0.5)#60@ms.m1()#90@ms.rz(-0.5)#120@ms.m1()#150@ms.rz(0.5)#180@ms.m1()#210@ms.rz(-0.5)#240@ms.m1()"}</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="lz_newland@163.com";
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>
view code

css文件:

基于Babylon.js编写简单的骨骼动画生成器
 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 }
view code

控制编辑区展缩的javascript代码:(位于clickbutton.js文件中)

基于Babylon.js编写简单的骨骼动画生成器
 1 //放和左侧伸缩菜单相关的内容
 2 var flex_current=null;//当前展开的flex
 3 function flex()//展缩一块骨骼的配置菜单
 4 {
 5     var evt=evt||window.event||arguments[0];
 6     cancelpropagation(evt);
 7     var obj=evt.currenttarget?evt.currenttarget:evt.srcelement;//obj是展缩按钮
 8     if(obj.innerhtml=="展开")
 9     {
10 
11         var divs=document.queryselectorall(".div_flexbottom");//要把其他展开状态的都关掉,同时还要改变高亮顶点(或者边线)状态
12         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="展开";
17             divs[i].parentnode.queryselectorall("button")[1].disabled="disabled";
18         }
19         obj.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];
33             if(div_comment)//如果这个平面有记录的数据
34             {
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=="收缩")
45     {
46         obj.innerhtml="展开";
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 }
view code

3、模型对象的初始化:

babylon.js格式模型的层次结构可以参考https://www.cnblogs.com/ljzc002/p/8927221.html

a、在场景中建立一个用来导出3d模型的对象:

建立这个对象的方法在newland.js文件中:

 1 //返回一个最简单的babylon.js场景格式
 2 newland.createobjscene=function()
 3 {
 4     var obj_scene=
 5     {
 6         'autoclear': true,
 7         'clearcolor': [0,0,0],
 8         'ambientcolor': [0,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 }

b、向模型中添加一个网格:

将网格对象的各种属性交给模型对象

 1 //向场景格式中加入一个网格对象
 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;//为避免出现重名材质,先不添加这个属性
 8     obj_mesh.position=[mesh.position.x,mesh.position.y,mesh.position.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;
22     }
23     if(mesh.geometry)//是有实体的网格
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 }

c、向模型中添加骨骼并向骨骼中添加骨头:

 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.push(bone)//也许应该用splice??
10 }

d、用上述方法初始化网格与模型:(在html文件里)

 1 //在这里设置一个初始的默认网格,
 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);//顶点的骨头索引
 8         mesh_origin.matricesweights=newland.repeatarr([1,0,0,0],len_pos/3);//顶点的骨头权重
 9         mesh_origin.skeletonid=0;
10         obj_scene=newland.createobjscene();
11         newland.addmesh2model(obj_scene,mesh_origin,"mesh_origin2");
12         newland.addsk2model(obj_scene,"sk_test1");//向模型中添加骨骼
13         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);// 向骨骼中添加骨头
30         arr_bone=obj_scene.skeletons[0].bones;
31         babylon.animation.allowmatricesinterpolation = true;//动画矩阵插值

  这里建立了骨头0作为所有骨骼最底层的根骨骼,它保持不变,不参加后面的各项设置。

e、添加一个编辑区(一块骨头):(在clickbutton.js文件中)

 1 function addbone()//向列表里添加一块骨骼
 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 }

 

4、导入其他模型

作为模型编辑工具不可能只处理初始模型,使用importmesh方法导入其他的babylon.js模型代替初始模型:

 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, skeletons)
10                 {//载入完成的回调函数
11                     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([1,0,0,0],len_pos/3);
24                     mesh_origin.skeletonid=0;
25                     newland.addmesh2model(obj_scene,mesh_origin,"mesh_origin2");
26                 }
27         );
28     }

5、骨骼划分:

a、点击刷新按钮时根据编辑区的输入建立平面:

 1 function clearallclip()//只清理所有的斜面,不处理突出的顶点
 2 {
 3     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=[];//保存所有的平面
17 function showclip()//先预留,否则以后要用时添加起来很麻烦
18 {
19 
20 }
21 function showclip2()//再点击刷新时根据斜面计算当前骨骼区域
22 {
23     var evt=evt||window.event||arguments[0];
24     cancelpropagation(evt);
25     var obj=evt.currenttarget?evt.currenttarget:evt.srcelement;//obj是刷新按钮
26     clearallclip();//先清空所有可能存在的平面
27     var divs=obj.parentnode.parentnode.queryselectorall(".div_flexbottom")[0].queryselectorall(".div_flexcell");
28     var str_number=obj.parentnode.parentnode.queryselectorall("span")[0].innerhtml//.getattribute("number");//这是骨骼索引编号
29     var len=6;
30     for(var i=0;i<len;i++)//遍历每个斜面设置,绘制出相应斜面
31     {
32         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;j++)//判断这个斜面是否正常设置
38         {
39             if(isnan(parsefloat(inputs[j].value)))//如果这个文本框没有内容或者内容不是数字
40             {
41                 flag=1;
42                 break;
43             }
44             else
45             {
46                 arr.push(parsefloat(inputs[j].value));
47             }
48         }
49         if(flag==0)//如果可以构成平面
50         {
51             var plane=new babylon.plane(arr[0], arr[1], arr[2], arr[3]);
52             var div_comment=div.queryselectorall(".div_comment")[0];
53             if(!div_comment)//如果以前没有这个注释内容
54             {
55                 div_comment=document.createelement("div");//建立一个隐形元素把设置持久化
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();//必须先把平面标准化,否则生成的平面网格不准确(会参考向量长度生成)
63             var mesh_plane=new babylon.meshbuilder.createplane("mesh_plane"+i
64                 ,{sourceplane:plane,sideorientation:babylon.mesh.doubleside,size:50},scene);
65             //sourceplane倾斜时sourceplane有bug!!!!??
66             mesh_plane.material=mat_alpha_yellow;//由plane生成的mesh没有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.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);//寻找属于这块骨骼的顶点
95 }

  第28行使用编辑区左上角的数字区分当前编辑的是哪一块骨头。

  接下来遍历编辑区的每一行,如果这一行的输入符合构成平面的要求,则建立一个平面对象,然后把这一行输入的内容以隐形标签的形式保存在dom文档中。

  接下来对平面进行标准化操作,在用标准化平面建立平面网格(63行),这里要注意“平面”是babylon.js中的一类数学对象,并不实际显示,平面网格才是实际显示的对象。所谓标准化指保持方向不变让平面的方向向量模为1。

  再接下来在平面网格的*建立一条线段和一个圆锥体网格代表法向量。

  最后告知浏览器在下一帧渲染时执行顶点查找计算,发现直接执行findvertex方法程序会出错,限于时间并未深入研究原因。

b、使用平面对象选择顶点:

 1 var lines_inpicked=null;
 2 function findvertex(str_number)//突出显示骨骼范围内的所有顶点
 3 {
 4     if (divfps) {
 5         // fps
 6         divfps.innerhtml = "0fps";
 7     }
 8     if(!mesh_origin||!mesh_origin.dispose)
 9     {
10         console.log("尚未加载模型");
11         return;
12     }
13     if(lines_inpicked&&lines_inpicked.dispose)
14     {
15         lines_inpicked.dispose();
16     }
17     var len=arr_plane.length;
18     if(len>0)//如果有平面,则开始遍历模型顶点
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)//对于每个顶点
31         {
32             console.log(i/3+1+"/"+len_pos/3);//显示当前操作到第几个顶点
33             if(matricesindices[i/3]==parseint(str_number))//要清空旧的设定
34             {
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;j++)//对于每一个切分平面
40             {
41                 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);//修改这个顶点的骨骼绑定
53                 //下面进行突出显示,遍历索引?
54                 for(var j=0;j<len_index;j+=3)
55                 {
56                     if(index_vertex==data_index[j])//三角形的第一个顶点
57                     {
58                         var num2=data_index[j+1]*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]);
64                     }
65                     else if(index_vertex==data_index[j+1])//三角形的第一个顶点
66                     {
67                         var num2=data_index[j]*3;
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]);
73                     }
74                     else if(index_vertex==data_index[j+2])//三角形的第一个顶点
75                     {
76                         var num2=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("没有设置符合规则的斜面");
92         return;
93     }
94 }

  对于每一个顶点,首先清空它当前的骨头索引(35行,未来考虑设置多骨头)。

  接着在41行用signeddistanceto方法获得这个顶点到平面的距离,如果顶点在平面的法线方向(正向)则距离为正,反之为负。

  如果对于所有切分平面(最多六个)这个顶点都在正向,则将这个顶点的骨头索引设为当前编辑的骨头。并且高亮显示这个顶点所在的边。(高亮显示顶点的方法可以参考https://www.cnblogs.com/ljzc002/p/9353101.html)

6、关键帧脚本解析:

  为了简化关键帧矩阵的设置,我建立了一套简单的关键帧脚本规则,它的解析方法如下:(代码位于computematrix.js文件中)

 1 function insertkey()//根据关键帧对mesh的骨骼的关键帧矩阵进行修改
 2 {
 3     var div_open=document.queryselectorall("#div_open")[0];//弹出的窗口对象
 4     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("?","");//替换掉换行符,在chrome里换行符是回车的竖折箭头,但这里被显示成了?
13     str_key.replace("\r","");
14     str_key.replace("\n","");
15     var arr_key=str_key.split("#");
16     var len=arr_key.length;
17     //计算这一块骨骼的关键帧
18     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=[];//每次点击计算时都会重写这块骨头的所有初始关键帧-》扩展关键帧要放在后面的环节!!!!
23     var div_comment=flex_current.queryselectorall(".div_comment0")[0];//注释信息从伸缩对象中提取
24     if(!div_comment)//将对于关键帧的各项设置保存在一个隐形标签里,这样下次打开对话框就不需要重新输入了
25     {
26         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++)//对于每一个关键帧
37         {
38             var key=arr_key[i];//单条关键帧的脚本代码
39             var arr=key.split("@");
40             var num_frame=parseint(arr[0]);//当前帧数
41             var script_frame=arr[1];
42             var matrix=eval(script_frame);//根据脚本计算出矩阵对象
43 
44             //var count=bone.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,vec_temp2.z)).toarray()
50             //});//推入每一条关键帧
51             bone.animation.keys.push({frame:num_frame,values:matrix.toarray()})
52 
53         }
54         flex_current.queryselectorall(".checkbone")[0].checked=true;
55     }
56     catch(e)
57     {
58         console.error(e);
59     }
60     finally
61     {//操作完毕后关闭对话框
62         delete_div('div_open');
63         delete_div("div_mask");
64     }
65 
66 }

7、预览模型

  完成前面的骨头编辑后点击预览模型,程序开始进入最关键的矩阵计算流程,首先是预览和导出模型的代码:

 1 var mesh_test=null;//必须先期声明一下,否则在importmesh的回调中会报错!!!!
 2 function exportmesh(obj_scene,flag)//这个工程专用的导出方法
 3 {
 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();//对骨骼动画的关键帧进行处理
15 
16         str_data=json.stringify(obj_scene);
17         //在现场演示环节里添加扩展关键帧的计算??
18         babylon.sceneloader.importmesh("", "", "data:"+str_data, scene
19             , function (newmeshes, particlesystems, skeletons) {//载入完成的回调函数
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);//启动骨骼动画
31                 }
32                 catch(e)//在这里拦截异常,否则异常进入babylon.js会导致浏览器卡顿!!
33                 {
34                     console.log(e);
35                 }
36 
37             });
38     }
39 }

  可以看到,只有现场演示按钮的响应里具备handlebones方法,所以在完成现场演示之后才可以点击导出模型。

  另一个需要注意的问题是在调试模式下,一旦importmesh的回调函数中出现未拦截的异常,浏览器将尝试打开babylon.js文件进行调试,并将整个模型文件的文本作为异常信息输出,这会消耗极大的cpu资源,造成浏览器卡顿甚至崩溃,所以尝试catch一下防止这类问题。

 8、关键帧扩展

  在前面的脚本里我们只设置了9个间隔30帧的关键帧,虽然babylon.js也支持自动对动画关键帧矩阵进行插值,但为了避免线性插值造成的骨骼缩放和错位(https://www.cnblogs.com/ljzc002/p/8927221.html展示了不扩展关键帧的缺点),在这里主动将9个初始关键帧扩展为241个扩展关键帧。

 1 function handlebones()//对每个骨头扩展关键帧,并建立矩阵传递
 2 {
 3     var len=arr_bone.length;
 4     var total=len*sum_frame;
 5     console.log("开始扩展非根骨头的关键帧");
 6     for(var i=1;i<len;i++)//重新扩展根骨骼之外的所有骨头
 7     {
 8         var bone=arr_bone[i];
 9         newland.extendkeys(bone,sum_frame);
10         console.log(i+"/"+(len-1));
11     }
 1 newland.extendkeys=function(bone,sum_frame)
 2 {
 3     var keys=bone.animation.keys;
 4     var keys2=[];
 5     if(keys.length==0)//如果是根骨骼一类没有初始关键帧的骨骼
 6     {
 7         for(var i=0;i<sum_frame;i++)
 8         {
 9             keys2.push({frame:i,v

                    
                
(0)
打赏 基于Babylon.js编写简单的骨骼动画生成器 微信扫一扫

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

基于Babylon.js编写简单的骨骼动画生成器
验证码: 基于Babylon.js编写简单的骨骼动画生成器