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

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

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


前言


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


微信图片_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



总结


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


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

相关文章
|
9月前
|
人工智能 前端开发 JavaScript
【利用AI刷面试题】50道前端基础面试题(三)
【利用AI刷面试题】50道前端基础面试题
|
3月前
|
前端开发 JavaScript API
前端界的革命性突破:掌握这些新技术,让你的作品引领潮流!
【10月更文挑战第30天】前端技术日新月异,从传统的HTML、CSS、JavaScript到现代的React、Vue、Angular等框架,以及Webpack、Sass等工具,前端开发经历了巨大变革。本文通过对比新旧技术,展示如何高效掌握这些新技术,助你作品引领潮流。
82 2
|
Java 应用服务中间件 Android开发
开发踩坑记录之四:Tomcat内存溢出问题分析
系统平台运行一段时间后,平台出现无法访问的问题,重启对应的服务后平台恢复正常。查看日志发现在凌晨两点零四分之后没有对应的日志输出,直到重启服务后才有日志的正常输出。同时发现在Tomcat的目录下存在hprof文件,即java进程的内存镜像文件。初步猜测Tomcat发生了内存溢出导致服务出现假死现象,即在任务管理器中虽然为运行状态,但是实际已不能正常对外提供服务。   对于hprof文件的分析需要借助于内存分析工具Eclipse Memory Analyzer,通过它寻找到平台发生内存泄露的根源,再根据发生内存泄露的地方以及相关的日志信息定位什么样的业务场景下导致该异常情况的发生。
开发踩坑记录之四:Tomcat内存溢出问题分析
|
9月前
|
存储 人工智能 前端开发
【利用AI刷面试题】50道前端基础面试题(一)
【利用AI刷面试题】50道前端基础面试题
|
Web App开发 数据可视化 大数据
DataV- 基础操作|学习笔记
快速学习 DataV- 基础操作
579 0
|
项目管理 PHP 前端开发
推荐5款开源的协同办公软件
国内外开源的协同办公软件
17042 0
|
9月前
|
存储 监控 前端开发
zabbix概述及简单的在centos7安装Zabbix5.0及添加监控对象
Zabbix是一种开源的企业级监控解决方案,用于实时监控网络、服务器、应用程序等各种设备和服务的性能和可用性,并提供通知和报警功能。它具有灵活可扩展、可定制化的特点,可以满足不同规模和需求的监控需求。
444 0
|
9月前
若依框架 --- echarts 封装
若依框架 --- echarts 封装
394 0
|
JavaScript
html文件里怎么引用vue组件?
html文件里怎么引用vue组件?
417 0
html文件里怎么引用vue组件?
|
9月前
|
移动开发 前端开发
ruoyi-nbcio-plus的Vue3前端升级组件后出现的问题(二)
ruoyi-nbcio-plus的Vue3前端升级组件后出现的问题(二)
184 1

热门文章

最新文章