• 基于webgl(threejs)的路面编辑


    楔子

    在很多应用中,特别是一些园区类的应用。 都需要对园区的地面 环境进行展示,路面就是地面的一部分。
    通常的做法是,都是建模的时候把相关的元素都建好,然后导入到展示系统中进行展示。
    不过有些情况下,可能建模并不太方便,所以三维编辑器可以直接进行简单的路面编辑显得挺有必要。

    路面对象扩展

    简单的路面希望能够通过一个路径来生成。 我们知道在threejs中有通过路径生成管路的对象,参考文章 WebGL管网展示(及TubeGeometry优化) 管路的横截面是一个圆形。 道路的横截面期望是一个矩形,因此,我们可以仿照管路的思路制作一个类似的对象PathRectGeometry,只是计算顶点的时候,横截面不再使用圆形,而是使用一个矩形,代码如下:

     let points = [new Vec3(-width/2,-height/2,0),new Vec3(-width/2,height/2,0),
            new Vec3(width/2,height/2,0),new Vec3(width/2,-height/2,0)] 
    			if(!scope.clockwise) {
    				points = [new Vec3(-width/2,-height/2,0),new Vec3(width/2,-height/2,0),
    					new Vec3(width/2,height/2,0),new Vec3(-width/2,height/2,0)];
    			}
          for( let j = 0;j <= points.length;j ++) {
            let jj = j == points.length ? 0 : j; 
            let point = points[jj];
            let radius = Math.hypot(point.x,point.y);
            const sin = point.y / radius;
            const cos = point.x / radius;
    
          	normal.x = ( cos * N.x + sin * B.x );
    				normal.y = ( cos * N.y + sin * B.y );
    				normal.z = ( cos * N.z + sin * B.z );
    				normal.normalize();
    
    				normals.push( 0,1,0 );
    			// vertex
    				vertex.x = P.x + radius * normal.x;
    				vertex.y = P.y + radius * normal.y;
    				vertex.z = P.z + radius * normal.z;
    
    				vertices.push( vertex.x, vertex.y, vertex.z );
          }
    

    通过PathRectGeometry创建对象的效果如下图所示:

    image.png

    路面编辑

    通过在平面上面打点来构建直线和贝塞尔曲线,然后通过构建得线条了生成路径,通过路径就可以生成路面效果,

     graph.getView().addEventListener("click", (event) => {
            let now = new Date().getTime();
            if (t != 0 && now - t < 500) {
              return;
            }
            t = now;
            if (path) {
              let pos = graph.getPositionOnPlaneByEvent(event, plane);
              constraintsHorizontalOrVertical(path, pos);
              path.lineTo(pos.x, pos.y, pos.z);
              tempPath = path.clone(tempPath);
              tempRoad.geometry.path = tempPath;
            }
          })
    

    大概得过程如下所示:

    animate.gif
    在生成得路径上,会有很多控制点,拖动控制点可以二次修改路径:

    animate2.gif

    生成连接处

    两条路得连接处会有斑马线之类得,点击生成斑马线,可以通过算法自动计算斑马线,

    // 找到road1 到road2的joint
        function createJointShape(road1, road2) {
          let path = road1.geometry.path;
          let path2 = road2.geometry.path;
          let lastPoint = path.points.at(-1);
          let lastCurve = path.curves.at(-1);
          let curves = path2.curves;
          console.log(curves);
          let minCurve, minDist = Infinity,
            minPoint
          for (let i = 0; i < curves.length; i++) {
            let curve = curves[i];
            if (curve.type == "LineCurve3") {
              let {
                dist,
                point
              } = findClosestPoint(lastPoint, curve.v1, curve.v2);
              if (dist < minDist) {
                minDist = dist;
                minPoint = point;
                minCurve = curve;
              }
            }
          }
          console.log(minCurve, minDist, minPoint);
    
          let v1 = lastCurve.v1,
            v2 = lastCurve.v2;
          let tagent = new dt.Vec3().subVectors(v2, v1);
          let up = new dt.Vec3(0, 1, 0);
          let cross = new dt.Vec3().cross(up, tagent);
          cross.normalize();
          let halfRoadWidth = 50;
          cross.multiplyScalar(halfRoadWidth);
          let cross2 = cross.clone().multiplyScalar(3.0);
    
          let p1 = lastPoint.clone().add(cross),
            p2 = lastPoint.clone().sub(cross);
    
          let sub = new dt.Vec3().subVectors(minPoint, lastPoint);
          console.log(sub.length(), minDist, halfRoadWidth)
          sub.setLength(minDist - halfRoadWidth);
    
          let joinPoint = new dt.Vec3().addVectors(lastPoint, sub);
    
          let halfSub = sub.clone().multiplyScalar(0.75);
          let p3Center = p1.clone().add(halfSub);
          let p4Center = p2.clone().add(halfSub);
    
          let p3 = joinPoint.clone().add(cross2);
          let p4 = joinPoint.clone().sub(cross2)
    
          let newPath = new dt.ShapePath();
    
          newPath.moveTo(p2.x, p2.z);
          newPath.quadraticCurveTo(p4Center.x, p4Center.z, p4.x, p4.z);
          newPath.lineTo(p3.x, p3.z);
          newPath.quadraticCurveTo(p3Center.x, p3Center.z, p1.x, p1.z);
          // newPath.closePath();
          // let geo = new dt.PathTubeGeometry(newPath, 64, 2);
          // let tube = new dt.Mesh(geo);
    
          let shapePath = newPath;
    
          const simpleShapes = shapePath.toShapes(true);
    
          var texture = graph.loadTexture("./road/001.jpg", {
            wrapT: dt.RepeatWrapping,
            wrapS: dt.RepeatWrapping,
          });
          texture.repeat.set(1 / 100, 1 / 100);
          texture.anisotropy = 16;
    
          let m1 = new dt.BasicMaterial({
            //  flatShading:true,
            map: texture,
            // envMap:envMap,
            // reflectivity:0.4,
            color: 0xffffff,
            toneMapped: false,
          });
    
          var geometry = new dt.ExtrudeGeometry(simpleShapes, {
            depth: 1,
            bevelEnabled: false,
            vertical: true,
          });
          var mesh = new dt.Mesh(geometry, m1);
          window.graph.getDataManager().add(mesh);
    
          road1.add(mesh);
        }
    

    如下图所示:

    animate3.gif

    结语

    本文所示只是一个demo级别得尝试,如果要做一个强度得路面编辑器系统,可能要考虑得还有很多,比如多车道效果,更重得衔接形状等等。这在后续得产品中会持续强化相关功能。

    如果你有好的思路,也欢迎和我交流。关注公号“ITMan彪叔” 可以添加作者微信进行交流,及时收到更多有价值的文章。

  • 相关阅读:
    摩根看好的前智能硬件头部品牌双11交易数据极度异常!——是模式创新还是饮鸩止渴?
    PyTorch入门学习(八):神经网络-卷积层
    【电商】电商供应链产品介绍
    HTML网页设计:爱护动物题材——保护动物大象(6页) HTML网页设计结课作业 web课程设计网页规划与设计 网页设计成品DW静态网页
    Golang企业面试题
    【CSS】min 和 max 函数(设置最大最小值)
    程序化交易行情接口有哪些特点?
    Docker搭建MusicBrainz
    java spring cloud 工程企业管理软件-综合型项目管理软件-工程系统源码
    基于树莓派的嵌入式Linux之简单入门代码
  • 原文地址:https://www.cnblogs.com/flyfox1982/p/16576984.html