HTML5WebG的3D网络拓扑构造图-
此刻,3D 模型已经用于各种不一样的领域。在医疗行业运用它们制作器官的精准模型;电影行业将它们用于流动的人物、物体以及实际电影;视频游戏产业将它们作为盘算机与视频游戏中的资源;在科学领域将它们作为化合物的精准模型;修建业将它们用来展现提议的修建物或者光景体现;工程界将它们用于设计新设施、交通工具、构造以及其它利用领域;在比来几十年,地球科学领域开端构建三维地质模型,并且 3D 模型时常做成动画,例如,在故事片电影以及盘算机与视频游戏中批量地利用三维模型。它们可以在三维建模工具中运用或者独自运用。为了容易构成动画,平常在模型中参加一些额外的数据,例如,一些人类或者动物的三维模型中有完备的骨骼系统,这样运动时看起来会更加真实,而且可以通过关节与骨骼控制运动。
这些种种都让我们前端开发者觉得要是我们可以不消学习 unity3d 或者其他游戏开发工具就能实现 3D 结果,并且能够精确的靠代码来控制挪移或者标的目的就好了。。。于是我应用 HT For Web 中的 3D组件 来实现了一个小例子,用了 HT 中 3D组件 的大局部功能,做这个例子就是想把 3D 组件好好的把握,尽量放进一个例子中,到时候他人有需要就可以参照 了。
先来看看整体实现的结果图:
用 HT for Web,现有的 3d 模板新建三层底板不是题目,题目是要怎样将图中首先层的“电脑”和“机柜组件”放上去?我是在网上 down 下来的 obj 格局的文件,然后我应用 HT 中的 ht.Default.loadObj(objUrl, mtlUrl, params) 函数将模型加载进去,其中的 params 局部可以参照 http://www.hightopo.com/guide...,代码如下:
ht.Default.loadObj('obj/机柜组件1.obj', 'obj/机柜组件1.mtl', { //加载 obj 文件 cube: true, //可否将模型缩放到单位1的尺寸范畴内,默许为false center: true, //模型可否居中,默许为false,设定为true则会挪移模型位置使其内容居中 shape3d: 'box', //要是指定了shape3d名称,则HT将主动将加载解析后的所有材质模型构建成数组的方式,以该名称进行注册 finishFunc: function(modelMap, array, rawS3){ //用于加载后的回调处置 if(modelMap){ device2 = createNode('box', floor1); //新建一个节点,在首先层“地板”上 device2.p3([x1-120, y1+13, z1+60]); //设定这个节点坐标 device2.s3(rawS3); //设定这个节点大小 createEdge(device1, device2); //新建连线 device3 = createNode('box', floor1); device3.s3(rawS3); device3.p3([x1+120, y1+13, z1+60]); createEdge(device1, device3); } } });
其中 finishiFunc 函数中的三个参数定义如下:
modelMap:调取 ht.Default.parseObj 解析后的返回值,若加载或解析失败则返回值为空
array:所有材质模型组成的数组
rawS3:包括所有模型的原始尺寸
个别在现实利用中我们都会将图元的大小设定为模型的原始尺寸。
“电脑”上方有个红色的立体能扭转的“告诫”,是依托 ht.Default.setShape3dModel 函数(HT for Web 建模手册 http://www.hightopo.com/guide... 3d 模型,在 ht 中,封装好的建模函数有许多,比拼根基的就是球体,圆柱,立方体等等,这边我用的是结构环形的办法 createRingModel 来生成“告诫”最外面的环,感慨号的上局部就是用的 createSmoothSphereModel 结构的球体,感慨号的下局部就是用 createSmoothCylinderModel 来结构的圆柱。我一开端直接运用了 3d 模型中封装好的函数,致使后来基本不晓得函数中运用的参数是做什么用的,并且也不明确 3d 模型是怎么形成的,然后本人又从新看了前面的“模型根基”,才晓得本来 3d 模型采纳的一个面,最根基的是三角面,之后复杂的面也是由多个三角面来构成的,然后绕着一根特定的轴扭转之后构成的,固然,这个轴是你来决议的,不一样的轴可以生成不一样的外形,关于色彩等格调方面的设定可以参照 HT for Web 格调手册(http://www.hightopo.com/guide...。至于怎样让这个 3d 模型扭转起来,ht 中封装了 addScheduleTask(Task) 办法,我在第三层 Task 中调取了 ht 封装的一个扭转函数 setRotation 来设定扭转的次序和标的目的,而且指定了扭转的对象。下列是自定义“告诫”的 3d 模型的办法(注意:由于本例的模型是自定义组合的,要是要设定整体模型的色彩要用 “all.blend” style 属性):
function createAlarm(device, formPane) { var ringModel = ht.Default.createRingModel([ 8, 1, 10, 1, 10, -1, 8, -1, 8, 1 ], null, null, false, false, 100);//依据xy平面的曲线,围绕一周构成3D模型。 var sphereModel = ht.Default.createSmoothSphereModel(8, 8, 0, Math.PI*2, 0, Math.PI, 2);//构建光滑球体模型 var cylinderModel = ht.Default.createSmoothCylinderModel(8, true, true, 1, 2, 0, Math.PI*2, 8);//构建光滑圆柱体模型 var alarmArr = [//组合模型 由三个模型ringModel、sphereModel、cylinderModel组合而成 { shape3d: ringModel,//定义模型类型 r3: [Math.PI/2, 0, 0],//设定扭转角度 color: {//设定模型色彩 func: 'style@all.blend',//数据绑定style样式中的all.blend属性,可通过data.s()猎取和设定这个属性 } },{ shape3d: sphereModel, t3: [0, 4, 0], color: { func: 'style@all.blend', } },{ shape3d: cylinderModel, t3: [0, -3, 0], color: { func: 'style@all.blend', } } ]; ht.Default.setShape3dModel('alarm', {//注册自定义3D模型 shape3d: alarmArr }); var alarmTip = createNode('alarm', device);//新建shape3d为alarm的节点 alarmTip.s3([2, 2, 2]);//设定节点大小 alarmTip.p3(device.p3()[0], device.p3()[1]+60, device.p3()[2]); alarmTip.s('all.blend', 'red');//转变此属性可转变模型的色彩,由于模型新建的时候已经数据绑定了 return alarmTip; }
接下来看看怎么让这个“告警”节点“闪耀”,我是直接将这个动画跟节点绑定,这样可以直接通过节点来控制动画。所以在上面我们新建 alarm 的模型时就可以直接将动画绑在节点上:
if(formPane){ alarmNode.scaleFunc = function() {//设定大小变化动画 var size = alarmNode.s3();//猎取节点的大小 if (size[0] === 2 && size[1] === 2 && size[2] === 2) alarmNode.s3([1, 1, 1]); else alarmNode.s3([2, 2, 2]); alarmNode.scaleTimer = setTimeout(alarmNode.scaleFunc, formPane.v('scaleInterval'));//设定动画 } alarmNode.blinkFunc = function(){//设定闪耀的动画 var color = alarmNode.s('all.blend');//猎取节点的style样式 if (color === 'red') alarmNode.s({'all.blend': 'yellow'});//要是节点色彩为红色,那么设定为黄色 else alarmNode.s({'all.blend': 'red'}); alarmNode.blinkTimer = setTimeout(alarmNode.blinkFunc, formPane.v('blinkInterval')); } alarmNode.rotateFunc = function() {//设定扭转动画 alarmNode.setRotation(alarmNode.getRotation() + Math.PI/20);//猎取节点目前的扭转角度,在这个扭转角度之上增加 Math.PI/20 个角度 alarmNode.rotateTimer = setTimeout(alarmNode.rotateFunc, formPane.v('rotInterval')); } }
上面的动画我设定了可以通过 form 表单面板上的属性来控制节点闪耀的速度,以及闪耀节点的动画等等,主要说一下这个功能在 form 表单上的实现:
formPane.addRow([//向form表单面板上增加一行元素 { checkBox: {//复选框 label: 'Enable Blink',//复选框对应的文本内容 selected: true,//设定选择复选框 onValueChanged: function(){//复选框值变化时回调的函数 var data = dataModel.getDataByTag('colorAlarm');//通过tag标签猎取节点 if (this.getValue()) {//猎取复选框目前值true/false data.blinkTimer = setTimeout(data.blinkFunc, formPane.v('blinkInterval'));//直接通过设定节点的blinkTimer来设定动画 } else { clearTimeout(data.blinkTimer);//革除动画 } } } }, { id: 'blinkInterval',//form可以通过getValue(简写为v)来猎取这个item的值 slider: {//设定了该属性后HT将依据属性值主动构建ht.widget.Slider对象,并保留在element属性上 min: 0,//滑动条最小值 max: 1000,//滑动条最大值 step: 50,//滑动条步进 value: 500,//目前滑动条的值 } } ], [0.1, 0.1]);//设定这行的两个item元素的宽度小于1的值为比例
最后来说说 3D 管线上的小球活动的局部,这个功能的确非常有用,并且做出来的结果也的确不错,跟大家分享~
第一,新建一条连线连贯起始节点和完毕节点并设定这个连线的样式,用 ht.Edge 可以将连线吸附在起始节点和完毕节点上,这样挪移这两个节点中的任意一个节点连线都会跟着节点挪移的位置变化,非常利便:
var polyline = new ht.Edge(source, target);//新建连线 dataModel.add(polyline);//将连线增加进数据容器中 polyline.s({ 'edge.width': 5,//连线宽度 'edge.type': 'points',//连线类型 为points时连线走向将由edge.points属性决议,用于绘制折线 'edge.points': [//可设定类型为ht.List的{x:100, y:100}格局的点对象数组,当edge.type为points时起作用 {x: source.getPosition3d()[0]+200, y: source.getPosition3d()[2], e: source.getPosition3d()[1]}, {x: target.getPosition3d()[0]+400, y: target.getPosition3d()[2], e: target.getPosition3d()[1]} ], 'edge.segments': [1, 4],//用于描述点连贯样式,数组元素为整型值 'shape3d': 'cylinder',//圆柱 'shape3d.color': 'rgba(242, 200, 40, 0.4)', 'shape3d.resolution': 30,//微分段数,可以决议曲线的平滑度 'edge.source.t3': [20, 0, 0],//连线source端偏移,[tx, ty, tz]格局,默许为空 'edge.target.t3': [20, 0, 0]//连线target端偏移,[tx, ty, tz]格局,默许为空 });
由于我们在新建连线的时候设定的 points 仅为曲线上的两个点,所以要是要猎取曲线当前构成的点,是短少 source 和 target 两个点的,我们从新设定一个数组,将这两个点增加进去,背面猎取曲线上所有点时会用上:
var list = new ht.List(); list.push({x: source.getPosition3d()[0], y: source.getPosition3d()[2], e: source.getPosition3d()[1]});//向数组中增加source点 polyline.s('edge.points').each(function(item){//增加style属性中已设定的两个点 list.push(item); }); list.push({x: target.getPosition3d()[0], y: target.getPosition3d()[2], e: target.getPosition3d()[1]});//增加target点
然后新建一个在管线上滑动的小球节点,这是仅是设定节点,真正增加进数据容器 dataModel 中需要设定完小球的坐标时再增加,要是没有给节点设定位置就将节点增加进数据容器中,节点的初始位置就是 3D 场景的正核心 [0, 0, 0] 的位置。小球滑动的动画代码如下:
var ball = new ht.Node();//新建小球节点 ball.s({//设定小球节点的样式 'shape3d': 'sphere',//设定小球的3d模型为球形 'shape3d.color': 'rgba(40, 90, 240, 0.4)'//设定3d模型的色彩 }); var delta = 10, flag = 0; setInterval(function(){ flag++; var length = (polyline.a('total') || 0) % polyline.a('length') + delta;//小球目前走过的曲线长度 var cache = ht.Default.getLineCacheInfo(list, polyline.s('edge.segments'));//猎取曲线上的点的信息 var lineLength = ht.Default.getLineLength(cache);//猎取曲线的总长度 polyline.a('length', lineLength - 50);//由于我设定了edge的t3(相当于2d中的offset),所以线段长度现实没有那么长 var offset = ht.Default.getLineOffset(cache, length);//曲线依据曲线上点的信息的偏移量 ball.setPosition3d(offset.point.x + 10, offset.point.y, offset.point.z);//设定节点的坐标 polyline.a('total', length); if(flag === 1) dataModel.add(ball);//这时候节点已经有了坐标了,可以增加进数据容器中了 }, 10);
我们还可以看到第二层上有两个特别的多边形“平行四边形”和“梯形”,平行四边形是靠 createParallelogramModel 模型函数,这个函数比拼简略,createExtrusionModel(array, segments, top, bottom, resolution, repeatUVLength, tall, elevation),array 是你要构成的图形的坐标点,这边只是针关于 xz 轴上画的平面图形,segments 指的是怎样连贯这几个坐标点,可参照 HT for Web 外形手册(http://hightopo.com/guide/gui...),top 和 bottom 就是让你选中可否有顶部或者底部,resolution 微分段数,我们刻画一段曲线的时候可能只有确认几个一般的点然后在每两个点之间的连线上把它分成多个段,这样这条线段就会变得平滑,ht 为了会员能够轻松操纵这些线段,就封装了这一个参数,repeatUVLength 默许为空,设定值后顶部和底部的贴图将依据拟定长度值进行反复,tall 模型的高度,默许为 5,elevation 模型核心的 y 轴位置,默许值为 0,设定这个值可以使 xz 上的平面绕着 y 轴扭转。
底层的一个环形的结果是通过一个算法来实现的,环形得确认这个环形上有多少个元素,然后算每两个之间的角度,在通过 sin、cos 来盘算每一个元素的位置,得出了如下代码:
names = ['设施2', '设施3', '设施4', '设施5', '设施6', '设施7', '设施8', '设施9']; names.forEach(function(name, index) { x = 400, y = 200, angle = 45, r = 120; x = x3 + Math.sin((2 * Math.PI / 360) * angle * index) * r; y = z3 + Math.cos((2 * Math.PI / 360) * angle * index) * r; device = createRect([x, y3 + 15, y], [w * 0.1, 15, h * 0.1], '', '', floor3); createEdge(device5, device); });