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

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

读取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();
}
}
});

飞机的螺旋桨模型绑定了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’)参数,实现飞机飞行过程指示灯的闪烁效果。

飞行路线是通过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相关参数值设置,因此螺旋桨的旋转速度也一致的放映了这种动画效果。

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

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

HT图形组件设计之道(四)的更多相关文章

  1. HT图形组件设计之道(三)

    上篇我们通过定制了CPU和内存展示界面,体验了HT for Web通过定义矢量实现图形绘制与业务数据的代码解耦及绑定联动,这类案例后续文章还会继续以便大家掌握更多的矢量应用场景,本篇我们先切换个话题, ...

  2. HT图形组件设计之道(一)

    HT for Web简称HT提供了涵盖通用组件.2D拓扑图形组件以及3D引擎的一站式解决方案,正如Hightopo官网所表达的我们希望提供:Everything you need to create ...

  3. HT图形组件设计之道(二)

    上一篇我们自定义CPU和内存的展示界面效果,这篇我们将继续采用HT完成一个新任务:实现一个能进行展开和合并切换动作的刀闸控件.对于电力SCADA和工业控制等领域的人机交互界面常需要预定义一堆的行业标准 ...

  4. HTML5拓扑图形组件设计之道(一)

    HT for Web(http://www.hightopo.com/guide/readme.html)提供了涵盖通用组件.2D拓扑图形组件以及3D引擎的一站式解决方案,正如Hightopo官网所表 ...

  5. 数百个 HTML5 例子学习 HT 图形组件 – 拓扑图篇

    HT 是啥:Everything you need to create cutting-edge 2D and 3D visualization. 这口号是当年心目中的产品方向,接着就朝这个方向慢慢打 ...

  6. HTML5 例子学习 HT 图形组件

    HTML5 例子学习 HT 图形组件 HT 是啥:Everything you need to create cutting-edge 2D and 3D visualization. 这口号是当年心 ...

  7. 数百个 HTML5 例子学习 HT 图形组件 – WebGL 3D 篇

    <数百个 HTML5 例子学习 HT 图形组件 – 拓扑图篇>一文让读者了解了 HT的 2D 拓扑图组件使用,本文将对 HT 的 3D 功能做个综合性的介绍,以便初学者可快速上手使用 HT ...

  8. 数百个 HTML5 例子学习 HT 图形组件 – 3D建模篇

    http://www.hightopo.com/demo/pipeline/index.html <数百个 HTML5 例子学习 HT 图形组件 – WebGL 3D 篇>里提到 HT 很 ...

  9. 数百个 HTML5 例子学习 HT 图形组件 – 3D 建模篇

    http://www.hightopo.com/demo/pipeline/index.html <数百个 HTML5 例子学习 HT 图形组件 – WebGL 3D 篇>里提到 HT 很 ...

随机推荐

  1. Revit中如何添加水平仰视平面视图

    在Revit平面视图中视角是俯视视角,但是在一些特殊的情况下,我们可能需要创建仰视视角的平面视图,例如我们需要向上看天花板的灯具布置的时候,下面举例说明添加仰视平面视图的方法. 如图在模型中有一楼板跟 ...

  2. [leetcode]Maximum Product Subarray @ Python

    原题地址:https://oj.leetcode.com/problems/maximum-product-subarray/ 解题思路:主要需要考虑负负得正这种情况,比如之前的最小值是一个负数,再乘 ...

  3. 【linux】如何将Vim打造成一个成熟的IDE

    如果你稍微写过一点代码,就能知道“集成开发环境”(IDE)是多么的便利.不管是Java.C还是Python,当IDE会帮你检查语法.后台编译,或者自动导入你需要的库时,写代码就变得容易许多.另外,如果 ...

  4. nginx做反向代理负载均衡 Java怎么获取后端服务器获取用户IP

    nginx做反向负载均衡,后端服务器获取真实客户端ip   首先,在前端nginx上需要做如下配置: location / proxy_set_hearder host                 ...

  5. asp.net中TreeView的大数据加载速度优化

    由于数据量太大,加载树时间很长,所以进行了优化 前台 .aspx <asp:Panel ID="Panel2" runat="server" Height ...

  6. Transaction recovery: lock conflict caught and ignored

    Transaction recovery: lock conflict caught and ignored环境:RAC 4节点.oracle 11.2.0.4.redhat 5.9 64bit 问题 ...

  7. jenkins svn E175002错误

    JENKINS_JAVA_OPTIONS="-Djava.awt.headless=true -Dsvnkit.http.sslProtocols='SSLv3' -Dsvnkit.http ...

  8. Selenium实战脚本集(3)--抓取infoq里的测试新闻

    描述 打开infoq页面,抓取最新的一些测试文章 需要抓取文章的标题和内容 如果你有个人blog的话,可以将这些文章转载到自己的blog 要求 不要在新窗口打开文章 自行了解最新的测试思潮与实践

  9. Ecshop文章列表页显示内容摘要

    本教程中讲到的“内容摘要”指的是文章内容的前 60个字符(当然也可以是前40个,前50个等等) 下面以 2.7.2版 + 官方默认模板 为例进行讲解: 1).修改 includes/lib_artic ...

  10. josephus Problem 中级(使用数组模拟链表,提升效率)

    问题描写叙述: 在<josephus Problem 0基础(使用数组)>中.我们提出了一种最简单直接的解决方式. 可是,细致审视代码之后.发现此种方案的效率并不高,详细体如今.当有人出局 ...