webgl 3d模型,webgl3d模型浏览
前言
WebGL渲染的3D机房现在已经不是什么新鲜事了。这篇文章的主要目的是说明3D机房中的眼和中心问题恰好被用在了项目中。我想了很多,最后觉得这个例子最符合我的要求,就把它作为记录。
效果图
这个3D机房的Demo还不错,很漂亮,基本的交互都满足。我们来看看如何实现。
代码生成
定义类
首先,从index.html调用的js路径序列逐个打开对应的js,以及一个自定义编辑器。server.js中的服务器类是由ht创建的。由HT封装的Default.def函数(注意,编辑器在创建的类名编辑器前面。服务器不能被E)替换:
Ht.default.def (editor.server ,object,{//第一个参数是类名,如果是字符串,自动注册到Ht的classMap中;第二个参数是该类要继承的父类;第三个参数是方法和变量的声明:addToDataModel: function(dm) {//将节点添加到数据容器dm.add(this。_ node);//HT中的预定义函数,通过add方法}向数据容器中添加节点,setHost: function() {//设置吸附this。_ node.sethost.apply (this。_ node,arguments);},s3: function() {//设置节点this的大小。_ node.s3.apply (this。_ node,arguments);},setElevation: function() {//控制节点图元中心所在的3D坐标系的Y轴位置。这个。_ node.setelevation.apply (this。_ node,arguments);}});创建编辑器。服务器类别
这个类可以创建一个ht。节点Node,设置节点的颜色和正面贴图:
var=e . Server=function(obj){//服务器组件var color=obj.color,frontier mg=obj.frontier mgvar node=this。_node=新ht。node();//创建节点node.s({//将节点的样式s设置为setStyle all.color 的缩写:color,//设置节点 front.image 的六边颜色:frontier//设置节点正面的图片});};这样我就可以在需要创建服务器组件的位置直接创建一个新的服务器组件对象,也可以直接调用上面声明的setHost等函数,很快就会用到。
接下来,创建编辑器。Cabinet cabinet类,类似编辑器的定义方法。以上服务器类别:
总远视Default.def(编辑器。Cabinet ,Object,{ addToDataModel:function(DM){ DM . add(this。_ door);dm.add(这个。_ node);这个。_ server list . foreach(function){ s . addtodatamodel(DM);});},p3: function() { this。_node.p3.apply(this。_node,arguments);//设置节点的三维坐标} });创建编辑器。橱柜类
这个类比前一个编辑器稍微复杂一点。服务器服务器组件类。该类在机柜内创建机柜主体、机柜门和服务器组件:
var C=E . Cabinet=function(obj){ var color=obj . color,doorFrontImg=obj.doorFrontImg,doorBackImg=obj.doorBackImg,S3=obj . S3;var node=this。_node=新ht。node();//Cabinet node . S3(S3);//将节点的大小设置为setSize3d node.a(cabinet ,this);//自定义cabinet属性node.s({//将节点的样式设置为setStyle all.color: color,//设置节点的六个面的颜色 front.visible: false//设置节点的正面是否可见});if(math . random()0.5){ node . addstyleicon( alarm ),{//给节点添加图标名称:[icon温度计],//包含多个字符串的数组,每个字符串对应一个图片或向量(通过ht注册。Default.setImage)。face: top ,//默认值为front,3D中图标的方向可以取为left right top bottom front back center position:17,//指定图标自动旋转的位置: y ,//默认值为false,图标是否自动面向3D中眼睛的方向t3: [0,16,0],//默认值为undefined,3D中图标的偏移量, 格式为[x,y,z] width: 37,//指定每个图标的宽度,默认情况下,注册图片时根据width height: 32,//指定每个图标的高度。 //默认是基于高度textureScale: 4,//注册图片时。//默认值为2,表示内存生成的实际映射的倍数。它不应该设置得太大,否则会影响性能。可见:{func: function () {return!e .报警可视;} }//表示是否显示该组图片});} var门=这个。_door=新ht。door window();//cabinet door . set width(S3[0]);//设置3D拓扑中图元在X轴方向的长度door . Set height(1);//设置3D拓扑中图元的Z轴长度door . settall(S3[1]);//控制Y轴door.setElevation(0)上节点图元的长度;//设置图元中心在3D坐标系中的Y坐标door . sety(S3[2]* 0.5);//设置节点在Y轴上的位置door . sethost(node);//设置snap door.s({//设置节点样式setStyle all.color: color,//设置节点 front.image 的六面颜色:doorFrontImg,//设置节点 front.transparent 的正面图片:true,//设置节点正面是否透明 back.image: doorBackImg,//设置节点背面的图片 back . uv :[1,0,1,1,0,1,0,1,0],//自定义节点后面的uv贴图,如果为空,则取默认var serverList=this。_ server list=[];var max=6,list=E.randomList(max,math . floor(math . random()*(max-2))2);//在//global.js中声明的获取随机数的函数varserver,h=S3[0]/4;list . foreach(function(r){ var server=new e . server({//服务器组件颜色: RGB (51,49,49),frontier:服务器组件精细 });server.s3(s3[0] - 2,h,S3[2]-4);//设置节点大小server . Set elevation((r-max * 0.5)*(H2));//设置Y轴上节点中心点的坐标server . Set host(node);//设置节点的吸附serverList.push(服务器);//将服务器节点添加到server list });};上面代码中唯一没有提到的是Editor.randomList函数,它在global.js文件中声明如下:
var E=窗口。Editor={ leftWidth: 0,topHeight: 40,randomList: function(max,size) { var list=[],ranwhile(list . length size){ ran=math . floor(math . random()* max);if (list.indexOf(ran)=0)继续;list . push(ran);}返回列表;}};好了,场景各部分的类都创建好了,接下来就要创建场景,把这些图元堆进去!
场景创建
如果熟悉的话,应该知道用HT创建3D场景只需要创建一个3D组件,然后通过addToDOM函数将这个场景添加到几何体中:
var g3d=e . main=new ht . graph 3d . graph 3d view();//3D场景的main.js文件主要处理3D场景中的一些必要元素,比如墙壁、地板、门、空调、所有橱柜的产生和排放位置,以及非常重要的交互部分。
墙面、地板、门、空调、橱柜的创建代码我就不贴了。有兴趣的请自己查代码。这里主要讲双击机柜以及任何与机柜相关的对象(柜门、服务器设备)。在3D中,相机的视线会移动到双击的柜子前面的某个位置,这个移动非常流畅。我之前的技术不好,导致对这部分想了很久。最后我参考了这个Demo的实现方法。
为了重复setEye和center,设置这两个参数对应的内容被封装成setEye和setCenter方法。setCenter方法类似于seteye方法,因此这里不再重复:
//设置眼位置var set eye=function (eye,finish) {if(!眼)返回;Var=g3d.geteye()。slice (0),//获取当前眼的值dx=eye [0]-e [0],dy=eye [1]-e [1],dz=eye[2]-e[2];//启动500 ms动画over ht . default . startanim({ duration:500,easing: easing,//animation easing func:finish function(){ },//function action: function(v,T) {//设置动画V代表easing(t)函数计算的值,T代表当前动画的进度[0~1]。一般属性更改由G3D执行。根据V参数设置eye([//将3D场景中eye eyes的值设置为数组,分别对应x,y,z轴的值e [0] dx * v,e。} });};我没有反复声明setCenter函数不代表它不重要。相反,它在“视线”移动的过程中起着决定性的作用。上面的setEye函数相当于我想去我的目标位置的前面(至少我定义的时候是这样),而sCenter的定义是把我的视线移动到目标位置(比如我可以站在我现在的位置,看着我身后的物体向右,也可以去我的右后方,站在那里。
双击事件很简单,只需监听HT封装的事件,判断事件类型,做出相应的动作:
g3d . mi(function(e){//addinteractorlistener事件侦听器函数if (e.kind!==doubleClickData) //确定事件类型为双击节点返回;var数据=e.data,P3;If (data.a(柜))//机身P3=data . P3();else { host=data . get host();//获取被点击节点的吸附对象If(host host . a( cabinet ){//如果吸附对象是cabinet P3=host . P3();} }如果(!p3)退货;setCenter(P3);//设置要移动到机柜位置的中心目标的setEye([p3[0],211,P3[2]247]);//设置eye的眼睛将移动到的位置});顶部导航栏
当初看到这个例子的时候,我就想,这个人这么厉害。用了这么久的ht,还没能用HT的ht.widget.Toolbar做出这么美的效果。看着看着,发现原来是用form form做的,好强大,我好傻。
var form=e . top=new ht . widget . form pane();//顶级表单组件form . setrowheight(e . top height);//设置行高form . setvgap(-e . top height);//将form组件的水平间距设置为行高负值,可以使同一行中的多行form . setvpadding(0);//设置表单的顶部以及顶部与组件内容之间的距离form.addRow([null,{//在表单中添加一行组件。第一个参数是元素数组,可以是字符串、json格式描述的组件参数信息、html元素或空图像:{icon:。/symbols/inputbg.json ,stretch: centeruniform}。//第二个参数是每个元素的宽度信息数组。宽度值大于1表示固定的绝对值,小于等于1表示相对值。也可以是80 0.3的组合form . addrow([null,null,{ID: searchinput ,textfield: {}},{element:机房可视化管理系统,Color: white ,font: 18px arial,sans-serif},null,{button: {//label:查看开关,icon:。/symbols/viewchange.json ,background: null,selectBackground: rgb(128,128,128),borderColor: rgba(0,0,0,0),on clicked:function(){ e . focus to();} }},null,{button: {//label: alarm ,icon:。/symbols/alarm.json ,toggle: true,selected: false,background: null,select background: RGB (128,128,128),borderColor: rgba(0,0,0,0),on clicked:function(e){ e . setalarmvisible(this . is selected());} }},null],[40,42,218,300,0.1,50,10,50,10]);以上都是只能实现,并没有真正添加到html标签中,也就是说现在界面上什么都没有!不要忘记在页面加载时将3D场景添加到正文中,同时,不要忘记将form form添加到正文中。当设置窗口大小改变事件时,表单form也需要实时更新:
window.addEventListener(load ,function(){ g3d . addtodom();//将3D场景添加到body document . body . appendchild(e . top . getview())中;//将窗体组件的底部div添加到正文window . addevent listener( resize ,function(){//窗口大小变化事件监听e . top . iv();//更新form form}的底部div});在这里解释addToDOM函数对于理解HT的机制非常重要。HT的组件一般嵌入在BorderPane、SplitView、TabView等容器中,而最外层的HT组件需要用户手动将getView()返回的底层div元素添加到页面的DOM元素中。这里需要注意的是,当父容器的大小发生变化时,如果父容器是HT预定义的容器组件如BorderPane、SplitView,HT的容器会自动递归调用子组件的invalidate函数通知更新。但是,如果父容器是一个本地html元素,HT组件就不知道它需要更新。所以最外层HT组件一般需要监听窗口的窗口大小变化事件,调用最外层组件invalidate函数来更新。
为了最外层组件加载填充窗口的方便,HT的所有组件都有addToDOM函数,其实现逻辑如下,其中iv是invalid的缩写:
addToDOM=function(){ var self=this,view=self.getView(),style=view.styledocument.body.appendChild(视图);//将场景的底部div添加到body style.left= 0//HT默认情况下,将所有组件的底层div的位置设置为absolute style.right= 0style . top=“0”;style . bottom=“0”;window . addevent listener( resize ,function(){ self . iv();},假);//窗口大小变化时监听事件,通知组件变化和更新}这样所有代码都完成了,可以右键自己“检查”了。相应的json文件可以在网络上获得。
这就是本文的全部内容。希望对大家的学习和支持有帮助。
郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。