import * as THREE from 'three' var math = { getBaseLog(x, y) { //返回以 x 为底 y 的对数(即 logx y) . Math.log 返回一个数的自然对数 return Math.log(y) / Math.log(x) }, convertVisionVector: function (e) { return new THREE.Vector3(e.x, e.z, -e.y) }, invertVisionVector: function (e) { //反转给算法部 return new THREE.Vector3(e.x, -e.z, e.y) }, convertVisionQuaternion: function (e) { return new THREE.Quaternion(e.x, e.z, -e.y, e.w).multiply(new THREE.Quaternion().setFromAxisAngle(new THREE.Vector3(0, 1, 0), THREE.MathUtils.degToRad(90))) }, invertVisionQuaternion: function (e) { //反转给算法部 var a = e.clone().multiply(new THREE.Quaternion().setFromAxisAngle(new THREE.Vector3(0, 1, 0), THREE.MathUtils.degToRad(-90))) return new THREE.Quaternion(a.x, -a.z, a.y, a.w) }, convertWorkshopVector: function (e) { return new THREE.Vector3(-e.x, e.y, e.z) }, convertWorkshopQuaternion: function (e) { return new THREE.Quaternion(-e.x, e.y, e.z, -e.w).multiply(new THREE.Quaternion(Math.sqrt(2) / 2, Math.sqrt(2) / 2, 0, 0)) }, convertWorkshopPanoramaQuaternion: function (e) { return new THREE.Quaternion(e.x, -e.y, -e.z, e.w).normalize().multiply(new THREE.Quaternion().setFromAxisAngle(new THREE.Vector3(0, 1, 0), THREE.MathUtils.degToRad(270))) }, convertWorkshopOrthoZoom: function (e, dom) { //xzw return e === -1 ? -1 : e * (dom.clientHeight / dom.clientHeight) }, getVec2Angle: function (dir1, dir2) { return Math.acos(THREE.MathUtils.clamp(this.getVec2Cos(dir1, dir2), -1, 1)) }, getVec2Cos: function (dir1, dir2) { return dir1.dot(dir2) / dir1.length() / dir2.length() }, closeTo: function (a, b, num) { if (num != void 0) return Math.abs(a - b) < num return Math.abs(a - b) < 1e-6 }, toPrecision: function (e, t) { //xzw change 保留小数 var f = function (e, t) { var i = Math.pow(10, t) return Math.round(e * i) / i } if (e instanceof Array) { for (var s = 0; s < e.length; s++) { e[s] = f(e[s], t) } return e } else if (e instanceof Object) { for (var s in e) { e[s] = f(e[s], t) } return e } else return f(e, t) }, isEmptyQuaternion: function (e) { return 0 === Math.abs(e.x) && 0 === Math.abs(e.y) && 0 === Math.abs(e.z) && 0 === Math.abs(e.w) }, projectPositionToCanvas: function (e, t, i, domE) { ;(i = i || new THREE.Vector3()), i.copy(e) var r = 0.5 * domE.clientWidth, o = 0.5 * domE.clientHeight return i.project(t), (i.x = i.x * r + r), (i.y = -(i.y * o) + o), i }, convertScreenPositionToNDC: function (e, t, i, domE) { /* return i = i || new THREE.Vector2, i.x = e / window.innerWidth * 2 - 1, i.y = 2 * -(t / window.innerHeight) + 1, i */ return (i = i || new n.Vector2()), (i.x = (e / domE.clientWidth) * 2 - 1), (i.y = 2 * -(t / domE.clientHeight) + 1), i }, handelPadding: (function () { //去除player左边和上面的宽高,因为pc的player左上有其他element 许钟文 var pads = new Map() //记录下来避免反复计算 return function (x, y, domE) { let pad let padInfo = pads.get(domE) if (padInfo) { if (domE.clientWidth == padInfo.width && domE.clientHeight == padInfo.height) { pad = padInfo.pad } } if (!pad) { pad = { x: this.getOffset('left', domE), y: this.getOffset('top', domE), } pads.set(domE, { width: domE.clientWidth, height: domE.clientHeight, pad, }) } return { x: x - pad.x, y: y - pad.y, } } })(), getOffset: function (type, element, parent) { //获取元素的边距 许钟文 var offset = type == 'left' ? element.offsetLeft : element.offsetTop if (!parent) parent = document.body while ((element = element.offsetParent)) { if (element == parent) break offset += type == 'left' ? element.offsetLeft : element.offsetTop } return offset }, constrainedTurn: function (e) { var t = e % (2 * Math.PI) return (t = t > Math.PI ? (t -= 2 * Math.PI) : t < -Math.PI ? (t += 2 * Math.PI) : t) }, getFOVDotThreshold: function (e) { return Math.cos(THREE.MathUtils.degToRad(e / 2)) }, transform2DForwardVectorByCubeFace: function (e, t, i, n) { switch (e) { case GLCubeFaces.GL_TEXTURE_CUBE_MAP_POSITIVE_X: i.set(1, t.y, t.x) break case GLCubeFaces.GL_TEXTURE_CUBE_MAP_NEGATIVE_X: i.set(-1, t.y, -t.x) break case GLCubeFaces.GL_TEXTURE_CUBE_MAP_POSITIVE_Y: i.set(-t.x, 1, -t.y) break case GLCubeFaces.GL_TEXTURE_CUBE_MAP_NEGATIVE_Y: i.set(-t.x, -1, t.y) break case GLCubeFaces.GL_TEXTURE_CUBE_MAP_POSITIVE_Z: i.set(-t.x, t.y, 1) break case GLCubeFaces.GL_TEXTURE_CUBE_MAP_NEGATIVE_Z: i.set(t.x, t.y, -1) } n && i.normalize() }, getFootPoint: function (oldPos, p1, p2, restricInline) { //找oldPos在线段p1, p2上的垂足 /* if(isWorld){//输出全局坐标 需要考虑meshGroup.position p1 = p1.clone(); p2 = p2.clone(); p1.y += mainDesign.meshGroup.position.y; p2.y += mainDesign.meshGroup.position.y; } */ var op1 = oldPos.clone().sub(p1) var p1p2 = p1.clone().sub(p2) var p1p2Len = p1p2.length() var leftLen = op1.dot(p1p2) / p1p2Len var pos = p1.clone().add(p1p2.multiplyScalar(leftLen / p1p2Len)) if (restricInline && pos.clone().sub(p1).dot(pos.clone().sub(p2)) > 0) { //foot不在线段上 if (pos.distanceTo(p1) < pos.distanceTo(p2)) pos = p1.clone() else pos = p2.clone() } return pos }, /** * 计算多边形的重心 * @param {*} points */ getCenterOfGravityPoint: function (mPoints) { var area = 0.0 //多边形面积 var Gx = 0.0, Gy = 0.0 // 重心的x、y for (var i = 1; i <= mPoints.length; i++) { var ix = mPoints[i % mPoints.length].x var iy = mPoints[i % mPoints.length].y var nx = mPoints[i - 1].x var ny = mPoints[i - 1].y var temp = (ix * ny - iy * nx) / 2.0 area += temp Gx += (temp * (ix + nx)) / 3.0 Gy += (temp * (iy + ny)) / 3.0 } Gx = Gx / area Gy = Gy / area return { x: Gx, y: Gy } }, getBound: function (ring) { var bound = new THREE.Box2() for (var j = 0, len = ring.length; j < len; j++) { bound.expandByPoint(ring[j]) } return bound }, isPointInArea: function (ring, point, ifAtLine) { //判断点是否在某个环内 var bound = this.getBound(ring) if (point.x < bound.min.x || point.x > bound.max.x || point.y < bound.min.y || point.y > bound.max.y) return false var inside = false var x = point.x, y = point.y for (var i = 0, j = ring.length - 1; i < ring.length; j = i++) { var xi = ring[i].x, yi = ring[i].y var xj = ring[j].x, yj = ring[j].y if ( (xi - x) * (yj - y) == (xi - x) * (yi - y) && x >= Math.min(xi, xj) && x <= Math.max(xi, xj) && //xzw add y >= Math.min(yi, yj) && y <= Math.max(yi, yj) ) { return !!ifAtLine //在线段上,则判断为…… (默认在外) } if (yi > y != yj > y && x < ((xj - xi) * (y - yi)) / (yj - yi) + xi) { inside = !inside } } return inside }, getArea: function (ring) { //求面积 顺时针为正 来自three shape for (var t = ring.length, i = 0, n = t - 1, r = 0; r < t; n = r++) i += ring[n].x * ring[r].y - ring[r].x * ring[n].y return -0.5 * i }, isInBetween: function (a, b, c, precision) { // 如果b几乎等于a或c,返回false.为了避免浮点运行时两值几乎相等,但存在相差0.00000...0001的这种情况出现使用下面方式进行避免 /* if (Math.abs(a - b) < 0.000001 || Math.abs(b - c) < 0.000001) { return false; } return (a <= b && b <= c) || (c <= b && b <= a);*/ //更改:如果b和a或c中一个接近 就算在a和c之间 return (a <= b && b <= c) || (c <= b && b <= a) || this.closeTo(a, b, precision) || this.closeTo(b, c, precision) }, ifPointAtLineBound: function (point, linePoints, precision) { //待验证 横线和竖线比较特殊 return math.isInBetween(linePoints[0].x, point.x, linePoints[1].x, precision) && math.isInBetween(linePoints[0].y, point.y, linePoints[1].y, precision) }, isLineIntersect: function (line1, line2, notSegment) { //线段和线段是否有交点. notSegment代表是直线而不是线段 var a1 = line1[1].y - line1[0].y var b1 = line1[0].x - line1[1].x var c1 = a1 * line1[0].x + b1 * line1[0].y //转换成一般式: Ax+By = C var a2 = line2[1].y - line2[0].y var b2 = line2[0].x - line2[1].x var c2 = a2 * line2[0].x + b2 * line2[0].y // 计算交点 var d = a1 * b2 - a2 * b1 // 当d==0时,两线平行 if (d == 0) { return false } else { var x = (b2 * c1 - b1 * c2) / d var y = (a1 * c2 - a2 * c1) / d // 检测交点是否在两条线段上 /* if (notSegment || (isInBetween(line1[0].x, x, line1[1].x) || isInBetween(line1[0].y, y, line1[1].y)) && (isInBetween(line2[0].x, x, line2[1].x) || isInBetween(line2[0].y, y, line2[1].y))) { return {x,y}; } */ if (notSegment || (math.ifPointAtLineBound({ x, y }, line1) && math.ifPointAtLineBound({ x, y }, line2))) { return { x, y } } } }, getNormal: function (line2d) { //获取二维法向量 方向向内 var x, y //要求的向量 //line2d的向量 var x1 = line2d.points[1].x - line2d.points[0].x var y1 = line2d.points[1].y - line2d.points[0].y //假设法向量的x或y固定为1或-1 if (y1 != 0) { x = 1 y = -(x1 * x) / y1 } else if (x1 != 0) { //y如果为0,正常情况x不会是0 y = 1 x = -(y1 * y) / x1 } else { console.log('两个点一样') return null } //判断方向里或者外: var vNormal = new THREE.Vector3(x, 0, y) var vLine = new THREE.Vector3(x1, 0, y1) var vDir = vNormal.cross(vLine) if (vDir.y > 0) { x *= -1 y *= -1 } return new THREE.Vector2(x, y).normalize() }, getQuaBetween2Vector: function (oriVec, newVec, upVec) { //获取从oriVec旋转到newVec可以应用的quaternion var angle = oriVec.angleTo(newVec) var axis = oriVec.clone().cross(newVec).normalize() //两个up之间 if (axis.length() == 0) { //当夹角为180 或 0 度时,得到的axis为(0,0,0),故使用备用的指定upVec return new THREE.Quaternion().setFromAxisAngle(upVec, angle) } return new THREE.Quaternion().setFromAxisAngle(axis, angle) }, getScaleForConstantSize: (function () { //获得规定二维大小的mesh的scale值。可以避免因camera的projection造成的mesh视觉大小改变。 来源:tag.updateDisc var w var i = new THREE.Vector3(), o = new THREE.Vector3(), l = new THREE.Vector3(), c = new THREE.Vector3(), h = new THREE.Vector3() return function (op = {}) { if (op.width2d) w = op.width2d //如果恒定二维宽度 else { var currentDis if (op.camera.type == 'OrthographicCamera') { //floorplan要直接使用activeControl.camera,主要用到projectionMatrix currentDis = (op.camera.right - op.camera.left) / op.camera.zoom / 3 } else { currentDis = op.position.distanceTo(op.camera.position) //dollhouse要直接使用player.camera, 因为activeControl.camera没有更新matrixWorld } if ((op.nearBound == void 0 && op.farBound != void 0) || (op.nearBound != void 0 && op.farBound == void 0)) { //仅限制最大或最小的话,不判断像素大小,直接限制mesh的scale //这个判断也可以写到getScaleForConstantSize里,可以更严谨控制像素宽度,这里只简单计算大小 let scale if (op.farBound == void 0 && currentDis < op.nearBound) { scale = (op.scale * currentDis) / op.nearBound } else if (op.nearBound == void 0 && currentDis > op.farBound) { scale = (op.scale * currentDis) / op.farBound } else { scale = op.scale } return scale } w = op.maxSize - (op.maxSize - op.minSize) * THREE.MathUtils.smoothstep(currentDis, op.nearBound, op.farBound) //maxSize : mesh要表现的最大像素宽度; nearBound: 最近距离,若比nearBound近,则使用maxSize } i.copy(op.position).project(op.camera), //tag中心在屏幕上的二维坐标 o.set(op.dom.clientWidth / 2, op.dom.clientHeight / 2, 1).multiply(i), //转化成px -w/2 到 w/2的范围 l.set(w / 2, 0, 0).add(o), //加上tag宽度的一半 c.set(2 / op.dom.clientWidth, 2 / op.dom.clientHeight, 1).multiply(l), //再转回 -1 到 1的范围 h.copy(c).unproject(op.camera) //再转成三维坐标,求得tag边缘的位置 var g = h.distanceTo(op.position) //就能得到tag的三维半径 return g } })(), //W , H, left, top分别是rect的宽、高、左、上 getCrossPointAtRect: function (p1, aim, W, H, left, top) { //求射线p1-aim在rect边界上的交点,其中aim在rect范围内,p1则不一定(交点在aim这边的延长线上) var x, y, borderX var r = (aim.x - p1.x) / (aim.y - p1.y) //根据相似三角形原理先求出这个比值 var getX = function (y) { return r * (y - p1.y) + p1.x } var getY = function (x) { return (1 / r) * (x - p1.x) + p1.y } if (aim.x >= p1.x) { borderX = W + left } else { borderX = left } x = borderX y = getY(x) if (y < top || y > top + H) { if (y < top) { y = top } else { y = top + H } x = getX(y) } return new THREE.Vector2(x, y) }, getDirFromUV: function (uv) { //获取dir 反向计算 - - 二维转三维比较麻烦 var dirB //所求 单位向量 uv.x %= 1 if (uv.x < 0) uv.x += 1 //调整为0-1 var y = Math.cos(uv.y * Math.PI) //uv中纵向可以直接确定y, 根据上面getUVfromDir的反向计算 var angle = 2 * Math.PI * uv.x - Math.PI //x/z代表的是角度 var axisX, axisZ //axis为1代表是正,-1是负数 if (-Math.PI <= angle && angle < 0) { axisX = -1 //下半圆 } else { axisX = 1 //上半圆 } if (-Math.PI / 2 <= angle && angle < Math.PI / 2) { axisZ = 1 //右半圆 } else { axisZ = -1 //左半圆 } var XDivideZ = Math.tan(angle) var z = Math.sqrt((1 - y * y) / (1 + XDivideZ * XDivideZ)) var x = XDivideZ * z if (z * axisZ < 0) { //异号 z *= -1 x *= -1 if (x * axisX < 0) { // console.log("wrong!!!!!??????????") } } x *= -1 //计算完成后这里不能漏掉 *= -1 dirB = new THREE.Vector3(x, y, z) //理想状态下x和z和anotherDir相同 return dirB }, getUVfromDir: function (dir) { //获取UV 同shader里的计算 var dir = dir.clone() dir.x *= -1 //计算前这里不能漏掉 *= -1 见shader var tx = Math.atan2(dir.x, dir.z) / (Math.PI * 2.0) + 0.5 //atan2(y,x) 返回从 X 轴正向逆时针旋转到点 (x,y) 时经过的角度。区间是-PI 到 PI 之间的值 var ty = Math.acos(dir.y) / Math.PI return { x: tx, y: ty } //理想状态下tx相同 }, crossRight: function (vec3, matrix) { //向量右乘矩阵,不能用向量的applyMatrix4(左乘) var e = matrix.elements var v = new THREE.Vector3() v.x = e[0] * vec3.x + e[1] * vec3.y + e[2] * vec3.z + e[3] v.y = e[4] * vec3.x + e[5] * vec3.y + e[6] * vec3.z + e[7] v.z = e[8] * vec3.x + e[9] * vec3.y + e[10] * vec3.z + e[11] //v.w不要 return v }, getNormalDir: function (point, supportsTiles, currentPano) { //获取A单位法线 /* console.log("lookVector:") console.log(objects.player.cameraControls.activeControl.lookVector) */ var dir = point.clone().sub(currentPano.position) //OA /* console.log("A的dir(无matrix转化):") console.log(dir.clone().normalize()); */ if (supportsTiles) { var matrixWorld = currentPano.rot90Matrix.clone() //因为热点求点时所右乘的matrix必须是单张全景照片时用的转90度的matrix才行 } else { var matrixWorld = currentPano.matrixWorld.clone() } dir = this.crossRight(dir, matrixWorld) //右乘matrixWorld 得matrix转化的向量 dir.normalize() /* var b = player.currentPano.skyboxMesh.matrixWorld.clone().getInverse(player.currentPano.skyboxMesh.matrixWorld) console.log(crossRight(dir,b).normalize()) */ return dir }, getDirByLonLat: function (lon, lat) { var dir = new THREE.Vector3() var phi = THREE.MathUtils.degToRad(90 - lat) var theta = THREE.MathUtils.degToRad(lon) dir.x = Math.sin(phi) * Math.cos(theta) dir.y = Math.cos(phi) dir.z = Math.sin(phi) * Math.sin(theta) return dir }, //0,0 => (1,0,0) 270=>(0,0,-1) getLineIntersect2(o = {}) { //得两条直线在其向量构成的面的法线方向的交点,投影在线上点的中点。 if (o.A != void 0) { // Ap1为一条线,Bp2为一条线 var A = o.A var B = o.B var p1 = o.p1 var p2 = o.p2 var dir0 = o.dir0 || new THREE.Vector3().subVectors(p1, A) var dir1 = o.dir1 || new THREE.Vector3().subVectors(p2, B) } if (A.equals(B)) return { pos3d: p1.clone() } //寻找两个向量所在的面 let normal = dir0.clone().cross(dir1) //面的法线 //先把整体旋转到使在xz平面上,求完交点再转回来 var qua = math.getQuaBetween2Vector(normal, new THREE.Vector3(0, 1, 0), new THREE.Vector3(0, 1, 0)) let newPoints = [A, B, p1, p2].map(e => { return e.clone().applyQuaternion(qua) }) var pos2d = math.isLineIntersect( [ { x: newPoints[0].x, y: newPoints[0].z }, { x: newPoints[2].x, y: newPoints[2].z }, ], [ { x: newPoints[1].x, y: newPoints[1].z }, { x: newPoints[3].x, y: newPoints[3].z }, ], true ) var quaInverse = qua.clone().invert() let pos3d = new THREE.Vector3(pos2d.x, 0, pos2d.y) let pos3d1 = pos3d.clone().setY(newPoints[0].y) let pos3d2 = pos3d.clone().setY(newPoints[1].y) pos3d1.applyQuaternion(quaInverse) pos3d2.applyQuaternion(quaInverse) pos3d = new THREE.Vector3().addVectors(pos3d1, pos3d2).multiplyScalar(0.5) return { pos3d, mid1: pos3d1, mid2: pos3d2 } }, getLineIntersect(o) { //两条三维直线相交 //取两线最短线段中心点 并且不能超出起点 o = o || {} if (o.A != void 0) { // Ap1为一条线,Bp2为一条线 var A = o.A var B = o.B var p1 = o.p1 var p2 = o.p2 } if (A.equals(B)) return { pos3d: p1.clone() } /* console.log("v1:") console.log(A.clone().sub(p1).normalize()) console.log("v2:") console.log(B.clone().sub(p2).normalize()) */ //调试热点夹角 var line1 = p1.clone().sub(A).normalize() var line2 = p2.clone().sub(B).normalize() var angle = line1.angleTo(line2) //var pano = player.model.panos.index[player.posGets.list[1]] //console.log('真实两线夹角: ', THREE.MathUtils.radToDeg(angle)) /*+ "旧夹角min: "+getAngle(pano.recentAngleScore)+"("+pano.recentAngleScore+")" */ //---------- var compute = function () { var pos3d var ux = p1.x - A.x var uy = p1.y - A.y var uz = p1.z - A.z var vx = p2.x - B.x var vy = p2.y - B.y var vz = p2.z - B.z var wx = A.x - B.x var wy = A.y - B.y var wz = A.z - B.z var a = ux * ux + uy * uy + uz * uz //u*u var b = ux * vx + uy * vy + uz * vz //u*v var c = vx * vx + vy * vy + vz * vz //v*v var d = ux * wx + uy * wy + uz * wz //u*w var e = vx * wx + vy * wy + vz * wz //v*w var dt = a * c - b * b var sd = dt var td = dt var sn = 0.0 //sn = be-cd var tn = 0.0 //tn = ae-bd var behind = function (index) { //在后方交点的话,直接其中一个点 不用两posget点中心点是因为可能从不同方位 距离很大 pos3d = (index == 1 ? p1 : p2).clone() //console.log(pos3d , ' 在后方交点,使用点' + index) }.bind(this) if (math.closeTo(dt, 0.0)) { //两直线平行 sn = 0.0 //在s上指定取s0 sd = 1.0 //防止计算时除0错误 tn = e //按(公式3)求tc td = c } else { sn = b * e - c * d tn = a * e - b * d if (sn < 0.0) { //最近点在s起点以外,同平行条件 behind(1) return { pos3d, behind: true } sn = 0.0 tn = e td = c } else if (sn > sd) { //超出终点不限制 /* //最近点在s终点以外(即sc>1,则取sc=1) sn = sd; tn = e + b; //按(公式3)计算 td = c; */ } } if (tn < 0.0) { //最近点在t起点以外 behind(2) return { pos3d, behind: true } tn = 0.0 if (-d < 0.0) sn = 0.0 //按(公式2)计算,如果等号右边小于0,则sc也小于零,取sc=0 else if (-d > a) sn = sd //按(公式2)计算,如果sc大于1,取sc=1 else { sn = -d sd = a } } /* else if (tn > td){ //超出终点不限制 tn = td; if ((-d + b) < 0.0) sn = 0.0; else if ((-d + b) > a) sn = sd; else { sn = (-d + b); sd = a; } } */ var sc = 0.0 var tc = 0.0 if (math.closeTo(sn, 0.0)) sc = 0.0 else sc = sn / sd if (math.closeTo(tn, 0.0)) tc = 0.0 else tc = tn / td //两个最近点 var mid1 = new THREE.Vector3(A.x + sc * ux, A.y + sc * uy, A.z + sc * uz) var mid2 = new THREE.Vector3(B.x + tc * vx, B.y + tc * vy, B.z + tc * vz) /* console.log("v11:") console.log(A.clone().sub(mid1).normalize()) console.log("v22:") console.log(B.clone().sub(mid2).normalize()) */ //console.log('另一个结果', math.getLineIntersect2(o)) //结果一样的,只是没有限制后方交点 let r = { pos3d: mid1.clone().add(mid2).multiplyScalar(0.5), mid1, mid2 } return r } return compute() //https://blog.csdn.net/u011511587/article/details/52063663 三维空间两直线/线段最短距离、线段计算算法 }, getShapeGeo: function (points, holes) { //获取任意形状(多边形或弧形)的形状面 //quadraticCurveTo() 这是弧形的含函数 var shape = new THREE.Shape() shape.moveTo(points[0].x, points[0].y) for (var i = 1, len = points.length; i < len; i++) { shape.lineTo(points[i].x, points[i].y) } /* var holePath = new THREE.Path() .moveTo( 20, 10 ) .absarc( 10, 10, 10, 0, Math.PI * 2, true ) arcShape.holes.push( holePath ); */ if (holes) { //挖空 holes.forEach(points => { var holePath = new THREE.Path() holePath.moveTo(points[0].x, points[0].y) for (var i = 1, len = points.length; i < len; i++) { holePath.lineTo(points[i].x, points[i].y) } shape.holes.push(holePath) }) } var geometry = new THREE.ShapeBufferGeometry(shape) //ShapeGeometry return geometry }, getUnPosPlaneGeo: (function () { //获取还没有赋值位置的plane geometry var e = new Uint16Array([0, 1, 2, 0, 2, 3]), // , t = new Float32Array([-.5, -.5, 0, .5, -.5, 0, .5, .5, 0, -.5, .5, 0]) i = new Float32Array([0, 0, 1, 0, 1, 1, 0, 1]), g = new THREE.BufferGeometry() g.setIndex(new THREE.BufferAttribute(e, 1)), //g.setAttribute("position", new n.BufferAttribute(t, 3)), g.setAttribute('uv', new THREE.BufferAttribute(i, 2)) return function () { return g } })(), getPlaneGeo: function (A, B, C, D) { var geo = this.getUnPosPlaneGeo().clone() var pos = new Float32Array([A.x, A.y, A.z, B.x, B.y, B.z, C.x, C.y, C.z, D.x, D.y, D.z]) geo.setAttribute('position', new THREE.BufferAttribute(pos, 3)) geo.computeVertexNormals() geo.computeBoundingSphere() //for raycaster return geo }, drawPlane: function (A, B, C, D, material) { var wall = new THREE.Mesh(this.getPlaneGeo(A, B, C, D), material) return wall }, movePlane: function (mesh, A, B, C, D) { var pos = new Float32Array([A.x, A.y, A.z, B.x, B.y, B.z, C.x, C.y, C.z, D.x, D.y, D.z]) mesh.geometry.setAttribute('position', new THREE.BufferAttribute(pos, 3)) mesh.geometry.computeBoundingSphere() //for checkIntersect }, getAngle(vec1, vec2, axis) { var angle = vec1.angleTo(vec2) var axis_ = vec1.clone().cross(vec2) if (axis_[axis] < 0) { angle *= -1 } return angle }, linearClamp(value, x1, x2, y1, y2) { //x为bound.min, bound.max value = THREE.MathUtils.clamp(value, x1, x2) return y1 + ((y2 - y1) * (value - x1)) / (x2 - x1) }, isInsideFrustum(bounding, camera) { // bounding是否在视野范围内有可见部分(视野就是一个锥状box) let frustumMatrix = new THREE.Matrix4() frustumMatrix.multiplyMatrices(camera.projectionMatrix, camera.matrixWorldInverse) let frustum = new THREE.Frustum() frustum.setFromProjectionMatrix(frustumMatrix) if (bounding instanceof THREE.Sphere) { return frustum.intersectsSphere(bounding) } else { return frustum.intersectsBox(bounding) } }, getStandardYaw(yaw1, yaw2) { //使yaw1过渡到yaw2时朝角度差小的那边走。如果差距大于半个圆,就要反个方向转(把大的那个数字减去360度) if (Math.abs(yaw1 - yaw2) > Math.PI) { yaw1 > yaw2 ? (yaw1 -= Math.PI * 2) : (yaw2 -= Math.PI * 2) } return [yaw1, yaw2] }, } export default math