HT图形组件设计之道(四)

简介: 在《HT图形组件设计之道(二)》我们展示了HT在2D图形矢量的数据绑定功能,这种机制不仅可用于2D图形,HT的通用组件甚至3D引擎都具备这种数据绑定机制,此篇我们将构建一个3D飞机模型,展示如果将数据绑定机制运用于3D模型,同时会运用到HT的动画机制,以及OBJ 3D模型加载等技术细节,正巧赶上刚发布的iOS8我们终于能将基于HT for Web开发的HTML5 3D应用跑在iOS系统了。

在《HT图形组件设计之道(二)》我们展示了HT在2D图形矢量的数据绑定功能,这种机制不仅可用于2D图形,HT的通用组件甚至3D引擎都具备这种数据绑定机制,此篇我们将构建一个3D飞机模型,展示如果将数据绑定机制运用于3D模型,同时会运用到HT的动画机制,以及OBJ 3D模型加载等技术细节,正巧赶上刚发布的iOS8我们终于能将基于HT for Web开发的HTML5 3D应用跑在iOS系统了。

Screen Shot 2014-10-08 at 7.45.25 PM

首选我们需要一个飞机模型,采用HT for Web构建3D模型可采用API组合各种基础模型的方式,但今天我们将采用读入OBJ的方式,毕竟网上已有很多不错的现成模型素材,搜查了一番后我在www.turbosquid.com选择了的这款免费的飞机模型,这个飞机模型是3dsmax格式,飞机模型是一体化的,由于我还需要控制机头的螺旋桨,因此我用3dsmax做了点改造,将螺旋桨分离了机身独立作为一个材质,同时导出成HT for Web可读取的OBJ格式,接下来就没美工设计师什么事了,剩下就全靠我们程序员自己的代码手艺活了。

Screen Shot 2014-10-08 at 8.26.18 PM

读取OBJ文件一般采用AJAX的方式远程加载,这对于喜欢纯前端的程序员来说很不爽,开发或演示个例子还得启服务,我喜欢本地文件打开就能跑不受跨域安全限制,因此我们需要将OBJ的文本信息放在在HTML或者JS代码中。解决这类问题有很多种方式,例如对于WebGL开发来说vertex shader和fragment shader代码同样面临这个问题,一种方式是写成一堆的string的array然后进行join的方式,另一种方式是增加<script id=”shader-vs” type=”x-shader/x-vertex”></script>和<script id=”shader-fs” type=”x-shader/x-fragment”></script>的自定义类似script块,然后读取相应DOM元素的textContent来获取文本内容。

但这两种方式都不适合OBJ内容,因为OBJ内容太长,采用数组方式对于成千上万行的OBJ文件行行加引号是不可思议的工作量(当然你可以再写个工具干这事),而采用<script>的方式会使得页面的HTML代码太长不易阅读编辑,我喜欢采用下面代码所示的这种方式,obj和mtl文件就像普通的js文件,可分离HTML页面代码,可给多个例子复用,且没有跨域安全问题,当然代码有点tricky,将function转换成字符串再截取中间文本内容:

var flight_mtl = getRawText(function(){/*
    newmtl body
    Ns 10.0000
    Ni 1.5000
    d 1.0000
    Tr 0.0000
    Tf 1.0000 1.0000 1.0000 
    illum 2
    Ka 0.3608 0.4353 0.2549
    Kd 0.3608 0.4353 0.2549
    Ks 0.0000 0.0000 0.0000
    Ke 0.0000 0.0000 0.0000
    ...
*/});

var flight_obj = getRawText(function(){/*
    v  -21.7990 -2.5094 -157.4279
    v  -34.5972 -20.3459 -42.9317
    v  -36.7638 -6.2029 -43.0833
    ...
*/});            

function getRawText(obj){
    var text = String(obj); 
    return text.substring(14, text.length-3);
}

以下为注册飞机模型的代码,通过代码的注解可知我们对飞机模型做了调整,通过r3: [0, -Math.PI/2, 0]我将整体飞机模型沿着y轴旋转了-Math.PI弧度使之朝向右边,通过s3:[0.1, 0.1, 0.1]将飞机模型缩小了10倍。

ht.Default.loadObj(flight_obj, flight_mtl, {                    
    center: true,
    r3: [0, -Math.PI/2, 0], // make plane face right
    s3: [0.1, 0.1, 0.1], // make plane smaller
    finishFunc: function(modelMap, array, rawS3){
        if(modelMap){                            
            modelMap.propeller.r3 = {
                func: function(data){
                    return [data.a('angle'), 0, 0]; 
                }                                
            };                             
            // make propeller a litter bigger
            modelMap.propeller.s3 = [1, 1.2, 1.2]; 
            modelMap.propeller.color = 'yellow';

            // add a sphere model as an indicator light
            array.push({
                shape3d: ht.Default.createSmoothSphereModel(),
                t3: [-40, 10, 0],
                s3: [6, 6, 6],
                color: {
                    func: function(data){
                        return data.a('light') ? 'red': 'black';
                    }
                }
            });
            ht.Default.setShape3dModel('plane', array);

            createPlane(rawS3);
            createFormPane();  
        } 
    }
});

Screen Shot 2014-10-08 at 9.46.53 PM

飞机的螺旋桨模型绑定了data.a(‘angle’)属性,原始螺旋桨模型有点小,通过modelMap.propeller.s3 = [1, 1.2, 1.2];在yz面做了1.2倍的放大,通过modelMap.propeller.color = ‘yellow’;将原始模型的颜色改成更显眼的黄色,当然你也可以通过修改mtl文件实现,甚至再将该属性绑定数据模型进行动态变化。

飞机尾部原始模型并没有指示灯,我们通过ht.Default.createSmoothSphereModel()用API创建了一个模型,与OBJ的模型进行了组合,指示灯的颜色通过return data.a(‘light’) ? ‘red’: ‘black’;的函数逻辑进行数据绑定,后续我们将在飞机运行过程动态变化data.a(‘light’)参数,实现飞机飞行过程指示灯的闪烁效果。

Screen Shot 2014-10-08 at 10.01.56 PM

飞行路线是通过ht.Polyline类型构建的,上图的几个黄色球是飞行路线Polyline对象的部分控制点,通过这几个控制点我们甚至可以在飞机飞行过程动态改变飞行路线。

params = {
      delay: 1500,
      duration: 20000,
      easing: function(t){
           return (t *= 2) < 1 ? 0.5 * t * t : 0.5 * (1 - (--t) * (t - 2));                     
      },
      action: function(v, t){
           var point = getPoint(v),
                px = point.x,
                py = point.y,
                pz = point.z,
                tangent = getTangent(v),
                tx = tangent.x,
                ty = tangent.y,
                tz = tangent.z;
           plane.p3(px, py, pz);
           plane.lookAt([px + tx, py + ty, pz + tz], 'right');  

           var camera = formPane.v('Camera');
           if(camera === 'Look At'){
                g3d.setCenter(px, py, pz);
           }
           else if(camera === 'First Person'){                           
                g3d.setEye(px - tx * 400, py - ty * 400 + 30, pz - tz * 400);
                g3d.setCenter(px, py, pz);                           
           }

           plane.a('angle', v*Math.PI*120);                       
           if(this.duration * t % 1000 > 500){
                plane.a('light', false);
           }else{
                plane.a('light', true);
           }                       
      },
      finishFunc: function(){
           animation = ht.Default.startAnim(params);
           plane.a('light', false);
      }                 
 };                              

 animation = ht.Default.startAnim(params);

以上为飞行动画的相关代码,ht.Default.startAnim可启动Frame-Based和Time-Based两种方式的动画,本例中我们需要动态改变飞行的周期,同时Frame-Based的方式会导致不同硬件设备总体运行周期差异太大,因此我们采用设置Duration的Time-Based的动画方式。

动画过程主要要改变飞机的位置,以及保持机头朝向切线方向,同时在Look At的模式下,我们不断让HT的Graph3dView的eye属性盯着飞机的位置,First Person模式下我们还需要改变Graph3dView的center属性。通过if(this.duration * t % 1000 > 500)的代码逻辑,实现了半秒钟改变一次light属性的闪烁效果。

为了达到更逼真的现实效果我们定义了Easing函数,采用了easeBoth这种起始结束较慢中间过程较快的动画函数,可参考《透过WebGL 3D看动画Easing函数本质》文章,从而实现飞机逐渐加速启动启动,慢慢减速着落的效果,螺旋桨的旋转角度也在动画过程中根据Easing相关参数值设置,因此螺旋桨的旋转速度也一致的放映了这种动画效果。

Screen Shot 2014-10-08 at 10.36.35 PM

该例子综合运用了HT for Web的多种技术功能,大家能体会到HT这种数据绑定机制灵活且强大的特点,通过数据绑定机制,我们可以动态修改从2D拓扑图、到通用组件渲染,甚至到3D引擎的数据模型,所有图形元素的颜色、大小和角度等参数皆可灵活控制,并且以最直观易用的方式供程序员二次开发与实际业务数据绑定关联。

最后上段该HTML5例子在iOS、Android和Mac等多平台下的运行视频和抓图,有兴趣的同学还可对该例子做更多有意思的改造扩展。http://v.youku.com/v_show/id_XNzk5MzI3MzMy.html


Screen Shot 2014-10-08 at 7.44.54 PM

目录
相关文章
Echarts实战案例代码(19):利用visualMap视觉映射组件制作五色玫瑰工作进程图
Echarts实战案例代码(19):利用visualMap视觉映射组件制作五色玫瑰工作进程图
224 0
|
5月前
|
数据可视化 测试技术 uml
如果更好的绘制UML图
如果更好的绘制UML图
40 0
|
JSON 前端开发 数据可视化
【图形基础篇】02 # 指令式绘图系统:如何用Canvas绘制层次关系图?
【图形基础篇】02 # 指令式绘图系统:如何用Canvas绘制层次关系图?
184 0
【图形基础篇】02 # 指令式绘图系统:如何用Canvas绘制层次关系图?
|
前端开发 容器
这8张脑图几乎概括了所有的布局方案,确定不看看吗?
前端布局不管是在面试过程中还是在工作中都是非常重要的一部分,一个优秀的前端工程师可以在很快的时间内写出同一种布局的多种实现方案,练习并掌握CSS布局方案可以提高我们的页面开发速度。
132 0
|
XML 缓存 前端开发
和大家谈谈我为什么选择图形这条路(一)
前端图形 从图形的角度带你领略前端的美 59篇原创内容 公众号 图形学这个领域目前来看是很好玩也很有前景的一个方向,当我们了解它的基础知识,get到它好玩地方的时候,我们可以很轻松延伸到可视化这一领域进行拓展。本文会尽量以很通俗很详细的方式来向大家介绍,希望读者有所收获。
和大家谈谈我为什么选择图形这条路(一)
|
前端开发 数据可视化 JavaScript
和大家谈谈我为什么选择图形这条路(二)
数学基础 img 1.1 坐标系与向量之以canvas为例实现坐标系的转换 这里首先我要先从对坐标系进行转换进行讲起,那为什么我要先讲坐标系的转换问题:因为转换坐标系对于图形学绘制而言,实在太重要了,后续所有图形的绘制都要用到这个思想,具体为什么我们先从一个之前前面看到的图形讲起: 首先经过一顿坐标点换算,我们得出每个点具体的坐标(这里我用了一个Rough.js的库,绘制一个手绘风格的图像),最终算出山顶的坐标就是 (-80, 100) 和 (80, 100),山脚的坐标就是 (-180, 0)、(20, 0)、(-20, 0)、(180, 0),太阳的中心点的坐标就是 (0, 150)。 i
和大家谈谈我为什么选择图形这条路(二)
|
计算机视觉
Qt实用技巧:组合图形的比例变换
Qt实用技巧:组合图形的比例变换
Qt实用技巧:组合图形的比例变换
【D3.js 学习总结】12、D3布局-集群图
# d3.layout.cluster() 集群图是一种用于表示包含与被包含关系的图表。 #### 集群图(Cluster)的API说明 * cluster.children - 取得或者设置子节点的访问器函数。 * cluster.links - 技术树节点之间的父子连接。 * cluster.nodeSize - 为每个节点指定固定的尺寸。 * cluster.node
2497 0
|
移动开发 图形学 HTML5
数百个 HTML5 例子学习 HT 图形组件 – 3D 建模篇
http://www.hightopo.com/demo/pipeline/index.html 《数百个 HTML5 例子学习 HT 图形组件 – WebGL 3D 篇》里提到 HT 很多情况下不需要借助 3Ds Max 和 Blender 等专业 3D 建模工具也能做出很多效果,例如  http://www.
1273 0