关于前端:三维组态部件动画解决方案

37次阅读

共计 3209 个字符,预计需要花费 9 分钟才能阅读完成。

前言

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

其中波及到的设施有冷却塔、水泵、螺杆机、离心机、分水器(集水器)、阀门,以及管路。
其中冷却塔,水泵,螺杆机,离心机都有停机 / 开机状态,开机状态下要有叶轮转动成果。

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

拆散模型发计划

咱们晓得个别模型的动画次要是模型的地位,旋转角度等发生变化。地位旋转角度的变动是比拟容易实现的一种动画。间接调用模型的 setPosition 办法和 setRotation 办法即可。
因而要做叶轮的旋转动画,一种比拟简便的模式就是把叶轮的模型,从主体模型外面分离出来,做成一个独立的模型。而后针对独立模型一直的调用 setRotation 办法即可。示意代码如下:

function animate(obj){
  new mono.Animate({
      dur:1000,
      from:0,
      to:1,
      onUpdate(v){obj.setRotationX(v * Math.PI *2);
     }
   }).play();}

拆散模型的益处是动画的实现形式比较简单。然而也有毛病,减少了模型的数量。比方一个设施有多少个部件要进行动画,那就得独立出多少个模型。另外一个毛病就是独立的进去的部件的模型,须要和建模人员沟通部件在整个模型中的地位,开发人员须要进行地位的摆放,减少了开发的复杂度。

为了解决下面的毛病,咱们的想法是应用整体模型。

整体模型计划

不拆散模型,把所有的部件都做到一个设施模型下面,然而对部件进行分组命名。这样咱们在导入整个模型之后,能够通过分组命名获取到部件子模型。而后对部件子模型进行动画操作。

然而这个外面有一个问题是此时部件子模型的建模中心点不在子模型自身的核心,而是整体设施的核心。因而咱们不能通过简略的 setRotation 的办法来进行模型的旋转动画。而是首先须要把旋转的中心点挪动到子模型实在的核心,而后再进行旋转操作。

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

突围盒(BoundingBox)

首先想到的是通过计算模型的突围盒来计算部件的中心点。然而因为通过 OBJ 格局导入的模型,它的每一个部件的突围盒都是整个模型的大小。所以咱们须要批改包外盒的计算逻辑。之所以部件突围盒的大小和整个模型的大小一样,是因为所有部件的顶点都是共享了一个顶点数组,该顶点数组包含了所有的部件顶点的汇合。而计算部件的突围盒的时候,是通过所有顶点来进行计算的。而部件的理论的突围盒应该是只能包含本人自身的顶点,因而咱们从新构建一个突围盒的计算方法。代码如下:

mono.Node.prototype.computeRealBoundingBox = function(){if (this.realBoundingBox == null) {this.realBoundingBox = new TGL.BoundingBox();
    }
    const vertices = this.vertices,
    faces = this.faces,
    realVertices = [];
    
    let indices = [],set = new Set();
    for(let i =0; i < faces.length;i ++){let {a,b,c,d} = faces[i];
        set.add(a);
        set.add(b);
        set.add(c);
        if(d != null){set.add(d);
        }
    }
    indices = [...set];

    indices.forEach((i) =>{realVertices.push(vertices[i]);
    });
    this.realBoundingBox.setFromPoints(realVertices);
    return this.realBoundingBox;
}

通过突围盒失去了部件的理论中心点之后,能够应用先平移再旋转的办法来进行旋转操作。大抵代码如下:

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

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

看起来如同完满,直到遇到这个模型:

感觉不对了,如同没有绕着中心点旋转。这是因为通过突围盒计算模型中心点的时候,模型须要是一个基于中心点能够任意均分的形态。下面第一个张图和第二张图就是这样的(限于 x 轴方向),而第三张图中的模型显著不是。

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

突围球(BoundingSphere)

首先,结构一个办法,计算模型的突围球体。

        mono.Node.prototype.computeRealBoundingSphere = function() {if (this.realBoundingSphere == null) {this.realBoundingSphere = new TGL.BoundingSphere();
            }
            const vertices = this.vertices,
            faces = this.faces,
            realVertices = []
            
            let indices = [],set = new Set();
            for(let i =0; i < faces.length;i ++){let {a,b,c,d} = faces[i];
                set.add(a);
                set.add(b);
                set.add(c);
                if(d != null){set.add(d);
                }
            }
            indices = [...set];

            indices.forEach((i) =>{realVertices.push(vertices[i]);
            });

            var center = new mono.Vec3(),
            vertexNum  = realVertices.length;
            realVertices.forEach((v) =>{
                center.x += v.x / vertexNum;
                center.y += v.y / vertexNum;
                center.z += v.z / vertexNum;
            }) 

            this.realBoundingSphere.setFromCenterAndPoints(center, realVertices);

            return this.realBoundingSphere;
        }

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

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

应用突围球后的成果:

能够看到核心旋转的成果进去了。

整体成果

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

总结

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

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

关注公号“ITMan 彪叔”能够及时收到更多有价值的文章。如果对可视化感兴趣,能够和我交换,微信 541002349。

正文完
 0