1 效果如下

2 代码如下

基本思路:

(1) 创建两个mesh,一个墙体,一个窗户

(2) 然后取墙体和窗户的差集,将差集转换成几何体

(3) 根据几何体新建mesh,并贴纹理

依赖库有三个:

import * as THREE from 'three';
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls";
import _ThreeBSP  from '../utils/ThreeBSP.js'
// 创建挖墙function initObject(){// 1 定义墙面var cubeGeometry = new THREE.BoxGeometry(1, 10, 30);var cube = new THREE.Mesh(cubeGeometry); // 2 定义窗户var door = new THREE.BoxGeometry(1, 8, 15);var doorMesh = new THREE.Mesh(door);doorMesh.position.z = 0// 3 定义两个bsp对象var cubeBSP = new ThreeBSP(cube);var doorBSP = new ThreeBSP(doorMesh);// 4 取交集let resultBSP = cubeBSP.subtract(doorBSP); // 墙体挖窗户// 5 将相交的部分转换成Meshlet result = resultBSP.toMesh();// 6 转成geometryvar cubeGeometry = result.geometryvar cubeMaterial = new THREE.MeshBasicMaterial({map:THREE.ImageUtils.loadTexture('./statics/imgs/wall2.jpg')})// 7 重新生成一个mesh let wallMesh = new THREE.Mesh(cubeGeometry,cubeMaterial);scene.add(wallMesh);
}

本文依赖ThreeBSP这个库;但这个库没有在threejs中集成;所以单独下载下来,然后改成 了ES5的方式;方便我们导入使用;

ThreeBSP.js

'use strict';var ThreeBSP, EPSILON = 1e-5,COPLANAR = 0,FRONT = 1,BACK = 2,SPANNING = 3;export default  function( THREE ) {var ThreeBSP = function( geometry ) {// Convert THREE.Geometry to ThreeBSPvar i, _length_i,face, vertex, faceVertexUvs, uvs,polygon,polygons = [],tree;if ( geometry instanceof THREE.Geometry ) {this.matrix = new THREE.Matrix4;} else if ( geometry instanceof THREE.Mesh ) {// #todo: add hierarchy supportgeometry.updateMatrix();this.matrix = geometry.matrix.clone();geometry = geometry.geometry;} else if ( geometry instanceof ThreeBSP.Node ) {this.tree = geometry;this.matrix = new THREE.Matrix4;return this;} else {throw 'ThreeBSP: Given geometry is unsupported';}for ( i = 0, _length_i = geometry.faces.length; i < _length_i; i++ ) {face = geometry.faces[i];faceVertexUvs = geometry.faceVertexUvs[0][i];polygon = new ThreeBSP.Polygon;if ( face instanceof THREE.Face3 ) {vertex = geometry.vertices[ face.a ];uvs = faceVertexUvs ? new THREE.Vector2( faceVertexUvs[0].x, faceVertexUvs[0].y ) : null;vertex = new ThreeBSP.Vertex( vertex.x, vertex.y, vertex.z, face.vertexNormals[0], uvs );vertex.applyMatrix4(this.matrix);polygon.vertices.push( vertex );vertex = geometry.vertices[ face.b ];uvs = faceVertexUvs ? new THREE.Vector2( faceVertexUvs[1].x, faceVertexUvs[1].y ) : null;vertex = new ThreeBSP.Vertex( vertex.x, vertex.y, vertex.z, face.vertexNormals[2], uvs );vertex.applyMatrix4(this.matrix);polygon.vertices.push( vertex );vertex = geometry.vertices[ face.c ];uvs = faceVertexUvs ? new THREE.Vector2( faceVertexUvs[2].x, faceVertexUvs[2].y ) : null;vertex = new ThreeBSP.Vertex( vertex.x, vertex.y, vertex.z, face.vertexNormals[2], uvs );vertex.applyMatrix4(this.matrix);polygon.vertices.push( vertex );} else if ( typeof THREE.Face4 ) {vertex = geometry.vertices[ face.a ];uvs = faceVertexUvs ? new THREE.Vector2( faceVertexUvs[0].x, faceVertexUvs[0].y ) : null;vertex = new ThreeBSP.Vertex( vertex.x, vertex.y, vertex.z, face.vertexNormals[0], uvs );vertex.applyMatrix4(this.matrix);polygon.vertices.push( vertex );vertex = geometry.vertices[ face.b ];uvs = faceVertexUvs ? new THREE.Vector2( faceVertexUvs[1].x, faceVertexUvs[1].y ) : null;vertex = new ThreeBSP.Vertex( vertex.x, vertex.y, vertex.z, face.vertexNormals[1], uvs );vertex.applyMatrix4(this.matrix);polygon.vertices.push( vertex );vertex = geometry.vertices[ face.c ];uvs = faceVertexUvs ? new THREE.Vector2( faceVertexUvs[2].x, faceVertexUvs[2].y ) : null;vertex = new ThreeBSP.Vertex( vertex.x, vertex.y, vertex.z, face.vertexNormals[2], uvs );vertex.applyMatrix4(this.matrix);polygon.vertices.push( vertex );vertex = geometry.vertices[ face.d ];uvs = faceVertexUvs ? new THREE.Vector2( faceVertexUvs[3].x, faceVertexUvs[3].y ) : null;vertex = new ThreeBSP.Vertex( vertex.x, vertex.y, vertex.z, face.vertexNormals[3], uvs );vertex.applyMatrix4(this.matrix);polygon.vertices.push( vertex );} else {throw 'Invalid face type at index ' + i;}polygon.calculateProperties();polygons.push( polygon );};this.tree = new ThreeBSP.Node( polygons );};ThreeBSP.prototype.subtract = function( other_tree ) {var a = this.tree.clone(),b = other_tree.tree.clone();a.invert();a.clipTo( b );b.clipTo( a );b.invert();b.clipTo( a );b.invert();a.build( b.allPolygons() );a.invert();a = new ThreeBSP( a );a.matrix = this.matrix;return a;};ThreeBSP.prototype.union = function( other_tree ) {var a = this.tree.clone(),b = other_tree.tree.clone();a.clipTo( b );b.clipTo( a );b.invert();b.clipTo( a );b.invert();a.build( b.allPolygons() );a = new ThreeBSP( a );a.matrix = this.matrix;return a;};ThreeBSP.prototype.intersect = function( other_tree ) {var a = this.tree.clone(),b = other_tree.tree.clone();a.invert();b.clipTo( a );b.invert();a.clipTo( b );b.clipTo( a );a.build( b.allPolygons() );a.invert();a = new ThreeBSP( a );a.matrix = this.matrix;return a;};ThreeBSP.prototype.toGeometry = function() {var i, j,matrix = new THREE.Matrix4().getInverse( this.matrix ),geometry = new THREE.Geometry(),polygons = this.tree.allPolygons(),polygon_count = polygons.length,polygon, polygon_vertice_count,vertice_dict = {},vertex_idx_a, vertex_idx_b, vertex_idx_c,vertex, face,verticeUvs;for ( i = 0; i < polygon_count; i++ ) {polygon = polygons[i];polygon_vertice_count = polygon.vertices.length;for ( j = 2; j < polygon_vertice_count; j++ ) {verticeUvs = [];vertex = polygon.vertices[0];verticeUvs.push( new THREE.Vector2( vertex.uv.x, vertex.uv.y ) );vertex = new THREE.Vector3( vertex.x, vertex.y, vertex.z );vertex.applyMatrix4(matrix);if ( typeof vertice_dict[ vertex.x + ',' + vertex.y + ',' + vertex.z ] !== 'undefined' ) {vertex_idx_a = vertice_dict[ vertex.x + ',' + vertex.y + ',' + vertex.z ];} else {geometry.vertices.push( vertex );vertex_idx_a = vertice_dict[ vertex.x + ',' + vertex.y + ',' + vertex.z ] = geometry.vertices.length - 1;}vertex = polygon.vertices[j-1];verticeUvs.push( new THREE.Vector2( vertex.uv.x, vertex.uv.y ) );vertex = new THREE.Vector3( vertex.x, vertex.y, vertex.z );vertex.applyMatrix4(matrix);if ( typeof vertice_dict[ vertex.x + ',' + vertex.y + ',' + vertex.z ] !== 'undefined' ) {vertex_idx_b = vertice_dict[ vertex.x + ',' + vertex.y + ',' + vertex.z ];} else {geometry.vertices.push( vertex );vertex_idx_b = vertice_dict[ vertex.x + ',' + vertex.y + ',' + vertex.z ] = geometry.vertices.length - 1;}vertex = polygon.vertices[j];verticeUvs.push( new THREE.Vector2( vertex.uv.x, vertex.uv.y ) );vertex = new THREE.Vector3( vertex.x, vertex.y, vertex.z );vertex.applyMatrix4(matrix);if ( typeof vertice_dict[ vertex.x + ',' + vertex.y + ',' + vertex.z ] !== 'undefined' ) {vertex_idx_c = vertice_dict[ vertex.x + ',' + vertex.y + ',' + vertex.z ];} else {geometry.vertices.push( vertex );vertex_idx_c = vertice_dict[ vertex.x + ',' + vertex.y + ',' + vertex.z ] = geometry.vertices.length - 1;}face = new THREE.Face3(vertex_idx_a,vertex_idx_b,vertex_idx_c,new THREE.Vector3( polygon.normal.x, polygon.normal.y, polygon.normal.z ));geometry.faces.push( face );geometry.faceVertexUvs[0].push( verticeUvs );}}return geometry;};ThreeBSP.prototype.toMesh = function( material ) {var geometry = this.toGeometry(),mesh = new THREE.Mesh( geometry, material );mesh.position.setFromMatrixPosition( this.matrix );mesh.rotation.setFromRotationMatrix( this.matrix );return mesh;};ThreeBSP.Polygon = function( vertices, normal, w ) {if ( !( vertices instanceof Array ) ) {vertices = [];}this.vertices = vertices;if ( vertices.length > 0 ) {this.calculateProperties();} else {this.normal = this.w = undefined;}};ThreeBSP.Polygon.prototype.calculateProperties = function() {var a = this.vertices[0],b = this.vertices[1],c = this.vertices[2];this.normal = b.clone().subtract( a ).cross(c.clone().subtract( a )).normalize();this.w = this.normal.clone().dot( a );return this;};ThreeBSP.Polygon.prototype.clone = function() {var i, vertice_count,polygon = new ThreeBSP.Polygon;for ( i = 0, vertice_count = this.vertices.length; i < vertice_count; i++ ) {polygon.vertices.push( this.vertices[i].clone() );};polygon.calculateProperties();return polygon;};ThreeBSP.Polygon.prototype.flip = function() {var i, vertices = [];this.normal.multiplyScalar( -1 );this.w *= -1;for ( i = this.vertices.length - 1; i >= 0; i-- ) {vertices.push( this.vertices[i] );};this.vertices = vertices;return this;};ThreeBSP.Polygon.prototype.classifyVertex = function( vertex ) {  var side_value = this.normal.dot( vertex ) - this.w;if ( side_value < -EPSILON ) {return BACK;} else if ( side_value > EPSILON ) {return FRONT;} else {return COPLANAR;}};ThreeBSP.Polygon.prototype.classifySide = function( polygon ) {var i, vertex, classification,num_positive = 0,num_negative = 0,vertice_count = polygon.vertices.length;for ( i = 0; i < vertice_count; i++ ) {vertex = polygon.vertices[i];classification = this.classifyVertex( vertex );if ( classification === FRONT ) {num_positive++;} else if ( classification === BACK ) {num_negative++;}}if ( num_positive > 0 && num_negative === 0 ) {return FRONT;} else if ( num_positive === 0 && num_negative > 0 ) {return BACK;} else if ( num_positive === 0 && num_negative === 0 ) {return COPLANAR;} else {return SPANNING;}};ThreeBSP.Polygon.prototype.splitPolygon = function( polygon, coplanar_front, coplanar_back, front, back ) {var classification = this.classifySide( polygon );if ( classification === COPLANAR ) {( this.normal.dot( polygon.normal ) > 0 ? coplanar_front : coplanar_back ).push( polygon );} else if ( classification === FRONT ) {front.push( polygon );} else if ( classification === BACK ) {back.push( polygon );} else {var vertice_count,i, j, ti, tj, vi, vj,t, v,f = [],b = [];for ( i = 0, vertice_count = polygon.vertices.length; i < vertice_count; i++ ) {j = (i + 1) % vertice_count;vi = polygon.vertices[i];vj = polygon.vertices[j];ti = this.classifyVertex( vi );tj = this.classifyVertex( vj );if ( ti != BACK ) f.push( vi );if ( ti != FRONT ) b.push( vi );if ( (ti | tj) === SPANNING ) {t = ( this.w - this.normal.dot( vi ) ) / this.normal.dot( vj.clone().subtract( vi ) );v = vi.interpolate( vj, t );f.push( v );b.push( v );}}if ( f.length >= 3 ) front.push( new ThreeBSP.Polygon( f ).calculateProperties() );if ( b.length >= 3 ) back.push( new ThreeBSP.Polygon( b ).calculateProperties() );}};ThreeBSP.Vertex = function( x, y, z, normal, uv ) {this.x = x;this.y = y;this.z = z;this.normal = normal || new THREE.Vector3;this.uv = uv || new THREE.Vector2;};ThreeBSP.Vertex.prototype.clone = function() {return new ThreeBSP.Vertex( this.x, this.y, this.z, this.normal.clone(), this.uv.clone() );};ThreeBSP.Vertex.prototype.add = function( vertex ) {this.x += vertex.x;this.y += vertex.y;this.z += vertex.z;return this;};ThreeBSP.Vertex.prototype.subtract = function( vertex ) {this.x -= vertex.x;this.y -= vertex.y;this.z -= vertex.z;return this;};ThreeBSP.Vertex.prototype.multiplyScalar = function( scalar ) {this.x *= scalar;this.y *= scalar;this.z *= scalar;return this;};ThreeBSP.Vertex.prototype.cross = function( vertex ) {var x = this.x,y = this.y,z = this.z;this.x = y * vertex.z - z * vertex.y;this.y = z * vertex.x - x * vertex.z;this.z = x * vertex.y - y * vertex.x;return this;};ThreeBSP.Vertex.prototype.normalize = function() {var length = Math.sqrt( this.x * this.x + this.y * this.y + this.z * this.z );this.x /= length;this.y /= length;this.z /= length;return this;};ThreeBSP.Vertex.prototype.dot = function( vertex ) {return this.x * vertex.x + this.y * vertex.y + this.z * vertex.z;};ThreeBSP.Vertex.prototype.lerp = function( a, t ) {this.add(a.clone().subtract( this ).multiplyScalar( t ));this.normal.add(a.normal.clone().sub( this.normal ).multiplyScalar( t ));this.uv.add(a.uv.clone().sub( this.uv ).multiplyScalar( t ));return this;};ThreeBSP.Vertex.prototype.interpolate = function( other, t ) {return this.clone().lerp( other, t );};ThreeBSP.Vertex.prototype.applyMatrix4 = function ( m ) {// input: THREE.Matrix4 affine matrixvar x = this.x, y = this.y, z = this.z;var e = m.elements;this.x = e[0] * x + e[4] * y + e[8]  * z + e[12];this.y = e[1] * x + e[5] * y + e[9]  * z + e[13];this.z = e[2] * x + e[6] * y + e[10] * z + e[14];return this;}ThreeBSP.Node = function( polygons ) {var i, polygon_count,front = [],back = [];this.polygons = [];this.front = this.back = undefined;if ( !(polygons instanceof Array) || polygons.length === 0 ) return;this.divider = polygons[0].clone();for ( i = 0, polygon_count = polygons.length; i < polygon_count; i++ ) {this.divider.splitPolygon( polygons[i], this.polygons, this.polygons, front, back );}   if ( front.length > 0 ) {this.front = new ThreeBSP.Node( front );}if ( back.length > 0 ) {this.back = new ThreeBSP.Node( back );}};ThreeBSP.Node.isConvex = function( polygons ) {var i, j;for ( i = 0; i < polygons.length; i++ ) {for ( j = 0; j < polygons.length; j++ ) {if ( i !== j && polygons[i].classifySide( polygons[j] ) !== BACK ) {return false;}}}return true;};ThreeBSP.Node.prototype.build = function( polygons ) {var i, polygon_count,front = [],back = [];if ( !this.divider ) {this.divider = polygons[0].clone();}for ( i = 0, polygon_count = polygons.length; i < polygon_count; i++ ) {this.divider.splitPolygon( polygons[i], this.polygons, this.polygons, front, back );}   if ( front.length > 0 ) {if ( !this.front ) this.front = new ThreeBSP.Node();this.front.build( front );}if ( back.length > 0 ) {if ( !this.back ) this.back = new ThreeBSP.Node();this.back.build( back );}};ThreeBSP.Node.prototype.allPolygons = function() {var polygons = this.polygons.slice();if ( this.front ) polygons = polygons.concat( this.front.allPolygons() );if ( this.back ) polygons = polygons.concat( this.back.allPolygons() );return polygons;};ThreeBSP.Node.prototype.clone = function() {var node = new ThreeBSP.Node();node.divider = this.divider.clone();node.polygons = this.polygons.map( function( polygon ) { return polygon.clone(); } );node.front = this.front && this.front.clone();node.back = this.back && this.back.clone();return node;};ThreeBSP.Node.prototype.invert = function() {var i, polygon_count, temp;for ( i = 0, polygon_count = this.polygons.length; i < polygon_count; i++ ) {this.polygons[i].flip();}this.divider.flip();if ( this.front ) this.front.invert();if ( this.back ) this.back.invert();temp = this.front;this.front = this.back;this.back = temp;return this;};ThreeBSP.Node.prototype.clipPolygons = function( polygons ) {var i, polygon_count,front, back;if ( !this.divider ) return polygons.slice();front = [], back = [];for ( i = 0, polygon_count = polygons.length; i < polygon_count; i++ ) {this.divider.splitPolygon( polygons[i], front, back, front, back );}if ( this.front ) front = this.front.clipPolygons( front );if ( this.back ) back = this.back.clipPolygons( back );else back = [];return front.concat( back );};ThreeBSP.Node.prototype.clipTo = function( node ) {this.polygons = node.clipPolygons( this.polygons );if ( this.front ) this.front.clipTo( node );if ( this.back ) this.back.clipTo( node );};return ThreeBSP;}

全部完整代码可以移步 【Threejs效果:挖空几何体】ThreeBSP实现墙体挖洞

【Threejs效果:挖空几何体】ThreeBSP实现墙体挖洞相关推荐

  1. BearSkill之UIView挖空处理

    扫描二维码的界面或者特定的View需要做挖空处理,在StackOverFlow上找了一番,发现了一些有用的方法,并且整理了一下. 挖空效果图 核心代码 Demo地址: https://github.c ...

  2. PPT2007中图片挖空效果的实现

    本案例将利用自选图形以及图片工具,轻松实现不同形状的图片挖空效果.图片挖空之后,就像玻璃一样,具有"透视"功能.下面,写篇教程给大家.< ppt模板大全/p> ①启动P ...

  3. 使用python读取txt坐标文件生成挖空矿山_探矿批量

    # -*-coding:utf-8-*- import arcpy import fileinput import os # 探矿权坐标格式举例 # 111.0846,31.1530 # 111.10 ...

  4. python读取坐标文本文件_使用python读取txt坐标文件生成挖空矿山_探矿批量

    # -*-coding:utf-8-*- import arcpy import fileinput import os # 探矿权坐标格式举例 # 111.0846,31.1530 # 111.10 ...

  5. AltiumDesigner覆铜挖空技巧总结

    问题提出 在进行PCB绘制后,大部分情况下,我们需要对板子反正面进行覆铜操作.同样,大多数情况下,我们的板子上都有定位孔,如果覆铜边界和定位孔边界距离过近,当拧螺丝时,螺丝会压在覆铜上,甚至在螺丝旋转 ...

  6. AD中板内挖空的方法

    一.先使用禁止布线层将板框确定 使用的快捷键:D----->S------>D 二.再绘制一个挖空区域 使用的快捷键:T----->V------>B 三.挖空后的效果

  7. threejs进阶,管道几何体高级应用,geometry几何体应用,可拖拽改变形状的管道

    threejs进阶,管道几何体高级应用,geometry几何体应用,可拖拽改变形状的管道 一.threejs是什么? 二.geometry几何体使用步骤 1.引入库 2.如果使用谷歌浏览器打不开报错, ...

  8. Python实现语文老师默写挖空助手

    上一期博客,我为大家介绍了我自己写的一套自动古诗文查询系统.当我们把我们想要的古诗文名字或者其中某句话输入系统时,它会自动将古文的全文打印出来. 今天我所写的这个功能就是基于上一期古诗文查询系统的. ...

  9. POW矿池挖空块原理和解决方案

    第0章 引言 比特币的挖矿收益包含两部分,第一是区块奖励:第二是交易手续费.如果打空块,就收不到交易手续费,为什么有矿池会不要手续费来打空块呢? 第1章POW挖矿原理 工作量证明POW挖矿就是区块链生 ...

最新文章

  1. 【数据库】sqlite3数据库备份、导出方法汇总
  2. 认真了解一下javascript
  3. 09-spring学习-资源访问接口
  4. mysql还原数据报错:
  5. Java—集合框架List
  6. 3.过滤——比较过滤结果、2D中的移动平均线_2
  7. java的handleback类,CallbackHandler
  8. 从 Storm 到 Flink,汽车之家基于 Flink 的实时 SQL 平台设计思路与实践
  9. java动作触发声音_关于鼠标动作的声音如何添加
  10. ERP进销存管理系统,建立企业信息化管理平台
  11. 队列DID:以知识青年“上山下乡”为例
  12. 【开发日志-已归档】2021-08
  13. 【职业规划】该如何选择职业方向?性能?自动化?测开?学习选择python、java?
  14. AR软件开发一个要多少钱?分享AR内容制作市价
  15. 人工智能是当前最好的计算机研究方向吗?
  16. Android画图方式
  17. 企业微信集成EAS流程助手
  18. python电话号码_Python有效电话号码
  19. 关于pycharm中html在页面访问的记录(授权问题)
  20. 【南大科院】高级网络服务工程训练

热门文章

  1. uniapp px转rpx
  2. android x86 鼠标指针,x86中断完全版
  3. 上界与下界-- 视图界定--协变与逆变
  4. 文件服务器 tmp文件夹,Linux管理临时文件tmpfiles
  5. 如何使用Pixelmator Pro处理图片?mac pixelmator使用教程
  6. zypper in 安装下载不了_强大音频制作软件 Cubase Pro 10.5 中文版(附安装教程)
  7. Android 分屏模式-多窗口支持
  8. 【经验】面对感冒引起的发烧怎么办,总结一些有用的经验
  9. 微信小程序的后台在哪里云开发控制台操作方法
  10. python写小程序后台——学习(小白)