三维组态部件动画解决方案

简介: 三维组态部件动画解决方案

三维组态部件动画解决方案


前言


最近做了一个制冷系统的三维组态监控系统。大致的效果如下图所示:


微信图片_20220425134302.png


其中涉及到的设备有冷却塔、水泵、螺杆机、离心机 、分水器(集水器)、阀门,以及管路。


其中冷却塔,水泵,螺杆机,离心机都有停机/开机状态,开机状态下要有叶轮转动效果。


因此我们需要给这几种模型,在开机状态下做叶轮转动的动画。


分离模型发方案


我们知道一般模型的动画主要是模型的位置,旋转角度等发生变化。位置旋转角度的变化是比较容易实现的一种动画。直接调用模型的setPosition方法和setRotation方法即可。


因此要做叶轮的旋转动画,一种比较简便的模式就是把叶轮的模型,从主体模型里面分离出来,做成一个独立的模型。然后针对独立模型不断的调用setRotation方法即可。示意代码如下:


1function animate(obj){
 2  new mono.Animate({
 3      dur:1000,
 4      from:0,
 5      to:1,
 6      onUpdate(v){
 7        obj.setRotationX(v * Math.PI *2);
 8     }
 9   }).play();           
10}


分离模型的好处是动画的实现方式比较简单。但是也有缺点,增加了模型的数量。比如一个设备有多少个部件要进行动画,那就得独立出多少个模型。另外一个缺点就是独立的出来的部件的模型,需要和建模人员沟通部件在整个模型中的位置,开发人员需要进行位置的摆放,增加了开发的复杂度。


为了解决上面的缺点,我们的想法是使用整体模型。


整体模型方案


不分离模型,把所有的部件都做到一个设备模型上面,但是对部件进行分组命名。这样我们在导入整个模型之后,可以通过分组命名获取到部件子模型。然后对部件子模型进行动画操作。


但是这个里面有一个问题是此时部件子模型的建模中心点不在子模型本身的中心,而是整体设备的中心。因此我们不能通过简单的setRotation的方法来进行模型的旋转动画。而是首先需要把旋转的中心点移动到子模型真实的中心,然后再进行旋转操作。

那么问题来了如何获取子模型本身的中心点呢?


包围盒(BoundingBox)


首先想到的是通过计算模型的包围盒来计算部件的中心点。但是由于通过OBJ格式导入的模型,它的每一个部件的包围盒都是整个模型的大小。所以我们需要修改包外盒的计算逻辑。之所以部件包围盒的大小和整个模型的大小一样,是因为所有部件的顶点都是共享了一个顶点数组,该顶点数组包括了所有的部件顶点的集合。而计算部件的包围盒的时候,是通过所有顶点来进行计算的。而部件的实际的包围盒应该是只能包括自己本身的顶点,因此我们重新构建一个包围盒的计算方法。代码如下:


1mono.Node.prototype.computeRealBoundingBox = function(){
 2    if (this.realBoundingBox == null) {
 3        this.realBoundingBox = new TGL.BoundingBox();
 4    }
 5    const vertices = this.vertices,
 6    faces = this.faces,
 7    realVertices = [];
 8
 9    let indices = [],set = new Set();
10    for(let i =0; i < faces.length;i ++){
11        let {a,b,c,d} = faces[i];
12        set.add(a);
13        set.add(b);
14        set.add(c);
15        if(d != null){
16        set.add(d);
17        }
18    }
19    indices = [...set];
20
21    indices.forEach((i) =>{
22        realVertices.push(vertices[i]);
23    });
24    this.realBoundingBox.setFromPoints(realVertices);
25    return this.realBoundingBox;
26}


通过包围盒得到了部件的实际中心点之后,可以使用先平移再旋转的方法来进行旋转操作。大致代码如下:


1  function rotate(obj,axisDir){
2      let bbox = (obj._realBoundingBox = obj._realBoundingBox ||  obj.computeRealBoundingBox());
3      let center = bbox .center();
4      obj.rotateFromAxis(axisDir,center,0.2);
5  }


首先获取部件真实的包围盒。然后调用对象的rotateFromAxis方法进行旋转,该方法内部的第一个参数指定旋转的轴,第二个参数指定旋转的中心点,第三个参数指定了旋转的角度;该方法内部实际上是先进行平移操作,在进行旋转操作,具体实现上通过矩阵的变换叠加来实现。看看效果:


微信图片_20220425134310.gif微信图片_20220425134314.gif


看起来好像完美,直到遇到这个模型:


微信图片_20220425134317.gif


感觉不对了,好像没有绕着中心点旋转。这是因为通过包围盒计算模型中心点的时候,模型需要是一个基于中心点可以任意均分的形状。上面第一个张图和第二张图就是这样的(限于x轴方向),而第三张图中的模型明显不是。


那应该如何计算第三种模型的中心点呢?答案是使用包围球。


包围球(BoundingSphere)


首先,构造一个方法,计算模型的包围球体。


1        mono.Node.prototype.computeRealBoundingSphere = function() {
 2            if (this.realBoundingSphere == null) {
 3                this.realBoundingSphere = new TGL.BoundingSphere();
 4            }
 5            const vertices = this.vertices,
 6            faces = this.faces,
 7            realVertices = []
 8
 9            let indices = [],set = new Set();
10            for(let i =0; i < faces.length;i ++){
11                let {a,b,c,d} = faces[i];
12                set.add(a);
13                set.add(b);
14                set.add(c);
15                if(d != null){
16                set.add(d);
17                }
18            }
19            indices = [...set];
20
21            indices.forEach((i) =>{
22                realVertices.push(vertices[i]);
23            });
24
25            var center = new mono.Vec3(),
26            vertexNum  = realVertices.length;
27            realVertices.forEach((v) =>{
28                center.x += v.x / vertexNum;
29                center.y += v.y / vertexNum;
30                center.z += v.z / vertexNum;
31            }) 
32
33            this.realBoundingSphere.setFromCenterAndPoints(center, realVertices);
34
35            return this.realBoundingSphere;
36        }


计算出部件的包围球之后,获取中心点,后面就是旋转模型,旋转的方法和前面说的一样。


1       function rotate(obj,axisDir){
2            let bSphere = (obj._realBoundingSphere = obj._realBoundingSphere ||obj.computeRealBoundingSphere());
3            let center = bSphere.center;
4            obj.rotateFromAxis(axisDir,center,0.2);
5        }


使用包围球后的效果:

image.gif微信图片_20220425134322.gif

可以看到中心旋转的效果出来了。


整体效果


最后,上一张整体的三维组态运行效果图:

微信图片_20220425134325.gif



总结


这种整体模型下部件进行动画的技巧,减少了需要建模师的工作量,同时也提高了开发人员的开发效率和开发的整体复杂度。


同时,这种方式有利于我们在三维组态编辑器中进行模型的整体导入和动画配置。因为如果是分离的模型,我们还需要在编辑器中导入多个模型再进行模型的拼装,位置对齐等难度很大。

相关文章
Threejs实现相机视角切换,平滑过渡,点击模型切换到查看模型视角
Threejs实现相机视角切换,平滑过渡,点击模型切换到查看模型视角
2039 0
Threejs实现相机视角切换,平滑过渡,点击模型切换到查看模型视角
|
3月前
|
API
【threejs教程】场景视角切换的神器:轨道控制器
【8月更文挑战第5天】threejs教程:场景视角切换的神器,轨道控制器
155 1
【threejs教程】场景视角切换的神器:轨道控制器
|
3月前
|
算法 异构计算
第2章-图形渲染管线-2.1-架构
第2章-图形渲染管线-2.1-架构
27 0
|
6月前
|
编解码 供应链 开发工具
英飞凌采用Qt图形解决方案增强Traveo T2G MCU系列,实现智能渲染技术
在竞争激烈的全球半导体市场,制造商一直在努力缩短产品上市时间。同时,他们对流畅、高分辨率图形显示器的需求也在日益增长。
|
定位技术 内存技术
GIS空间分析 三维分析4 制作飞行动画
本文中,我们利用ArcScene软件用3种方法制作了飞行动画
286 0
|
数据可视化 物联网
Threejs物联网,养殖场3D可视化(三)模型展示,轨道控制器设置,模型沿着路线运动,模型添加边框,自定义样式显示标签,点击模型获取信息
Threejs物联网,养殖场3D可视化(三)模型展示,轨道控制器设置,模型沿着路线运动,模型添加边框,自定义样式显示标签,点击模型获取信息
937 15
Threejs物联网,养殖场3D可视化(三)模型展示,轨道控制器设置,模型沿着路线运动,模型添加边框,自定义样式显示标签,点击模型获取信息
HMI-34-【运动模式】实现运动模式的UI上电逻辑控制
今天这界面上没有实际的增加,仅仅是实把运动模式UI上电控制逻辑实现了一下,其实到这个模块的时候,就会发现之前在写舒适模式的时候的一些问题了,有好内容可以抽象出来了,不用每次都实现一遍了。不过现在还比较懒,还是先以实现为主,和面的内容放在优化里面。废话不多说,开始搞事情。不过为了吸你们开下去,现在把目前进度放在了。
|
数据可视化 JavaScript 前端开发
【视觉高级篇】18 # 如何生成简单动画让图形动起来?
【视觉高级篇】18 # 如何生成简单动画让图形动起来?
91 0
【视觉高级篇】18 # 如何生成简单动画让图形动起来?
An动画基础之元件的影片剪辑动画与传统补间
An动画基础之元件的影片剪辑动画与传统补间
337 0
An动画基础之元件的影片剪辑动画与传统补间
西门子S7-200 SMART运动控制功能,如何使用向导组态运动轴?
从今天开始我们来介绍西门子S7-200 SMART运动控制功能,本篇我们先来学习如何使用向导组态运动轴。西门子S7-200 SMART CPU内置运动轴,可以实现速度和位置的开环运动控制。
西门子S7-200 SMART运动控制功能,如何使用向导组态运动轴?