1 Star 0 Fork 6

lanfeng1314/threebsp

forked from Jafey/threebsp 
加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
文件
该仓库未声明开源许可证文件(LICENSE),使用请关注具体项目描述及其代码上游依赖。
克隆/下载
index.js 20.56 KB
一键复制 编辑 原始数据 按行查看 历史
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633
module.exports = function(THREE) {
// ε,表示最大的偏差量,如果偏差小于此值,则忽略偏差
const EPSILON = 1e-5;
// 共面
const COPLANAR = 0;
// 在前方
const FRONT = 1;
// 在后方
const BACK = 2;
// 存在交集
const SPANNING = 3;
var __slice = [].slice,
__hasProp = {}.hasOwnProperty,
__extends = function(child, parent) {
for (var key in parent) {
if (__hasProp.call(parent, key)) child[key] = parent[key];
}
function ctor() {
this.constructor = child;
}
ctor.prototype = parent.prototype;
child.prototype = new ctor();
child.__super__ = parent.prototype;
return child;
};
function ThreeBSP(treeIsh, matrix) {
this.matrix = matrix;
if (this.matrix == null) {
this.matrix = new THREE.Matrix4();
}
this.tree = this.toTree(treeIsh);
}
ThreeBSP.prototype.toTree = function(treeIsh) {
if (treeIsh instanceof ThreeBSP.Node) {
return treeIsh;
}
// 看Three.js 0.126源码,各类Geometry基本都是继承或由BufferGeometry实现,基本上THREE.Geometry就废弃了,所以需要将获取点、法向量、uv信息要从以前的THREE.Geometry的faces中获取改为从THREE.BufferGeometry的attributes中获取
var polygons = [], geometry = (treeIsh === THREE.BufferGeometry || (treeIsh.type || "").endsWith("Geometry")) ? treeIsh : treeIsh.constructor === THREE.Mesh ? (treeIsh.updateMatrix(), this.matrix = treeIsh.matrix.clone(), treeIsh.geometry) : void 0;
if(geometry && geometry.attributes) {
// TODO 暂时就不对geometry.attributes中的position、 normal和uv进行非空验证了,日后有时间在说吧,正常创建的BufferGeometry这些值通常都是有的
var attributes = geometry.attributes, normal = attributes.normal, position = attributes.position, uv = attributes.uv;
// 点的数量
var pointsLength = attributes.position.array.length/attributes.position.itemSize;
// 如果索引三角形index不为空,则根据index获取面的顶点、法向量、uv信息
if(geometry.index) {
var pointsArr = [], normalsArr = [], uvsArr = [];
// 从geometry的attributes读取点、法向量、uv数据
for(var i = 0, len = pointsLength; i < len;i++) {
// 通常一个点和一个法向量的数据量(itemSize)是3,一个uv的数据量(itemSize)是2
var startIndex = 3*i
pointsArr.push(new THREE.Vector3(position.array[startIndex], position.array[startIndex + 1], position.array[startIndex + 2]));
normalsArr.push(new THREE.Vector3(normal.array[startIndex], normal.array[startIndex + 1], normal.array[startIndex + 2]));
uvsArr.push(new THREE.Vector2(uv.array[2*i], uv.array[2*i + 1]));
}
var index = geometry.index.array;
for(var i = 0, len = index.length; i < len;) {
var polygon = new ThreeBSP.Polygon();
// 将所有面都按照三角面进行处理,即三个顶点组成一个面
for(var j = 0; j < 3; j++) {
var pointIndex = index[i], point = pointsArr[pointIndex];
var vertex = new ThreeBSP.Vertex(point.x, point.y, point.z, normalsArr[pointIndex], uvsArr[pointIndex]);
vertex.applyMatrix4(this.matrix);
polygon.vertices.push(vertex);
i++;
}
polygons.push(polygon.calculateProperties());
}
} else {
// 如果索引三角形index为空,则假定每三个相邻位置(即相邻的三个点)表示一个三角形
for(var i = 0, len = pointsLength; i < len;) {
var polygon = new ThreeBSP.Polygon();
// 将所有面都按照三角面进行处理,即三个顶点组成一个面
for(var j = 0; j < 3; j++) {
var startIndex = 3*i
var vertex = new ThreeBSP.Vertex(position.array[startIndex], position.array[startIndex + 1], position.array[startIndex + 2], new THREE.Vector3(normal.array[startIndex], normal.array[startIndex + 1], normal.array[startIndex + 2]), new THREE.Vector2(uv.array[2*i], uv.array[2*i + 1]));
vertex.applyMatrix4(this.matrix);
polygon.vertices.push(vertex);
i++;
}
polygons.push(polygon.calculateProperties());
}
}
} else {
console.error("初始化ThreeBSP时为获取到几何数据信息,请检查初始化参数");
}
return new ThreeBSP.Node(polygons);
};
/**
* 转换成mesh
* @param {*} material 材质
* @param {Boolean} groupByCoplanar 是否根据共面进行分组
* @param {Boolean} uniqueMaterial 每个分组使用唯一的材质(此处将材质索引和组索引设置为一样的)
* @returns
*/
ThreeBSP.prototype.toMesh = function(material, groupByCoplanar, uniqueMaterial) {
var geometry = this.toGeometry(groupByCoplanar, uniqueMaterial);
if (material == null) {
material = new THREE.MeshNormalMaterial();
}
var mesh = new THREE.Mesh(geometry, material);
mesh.position.getPositionFromMatrix(this.matrix);
mesh.rotation.setFromRotationMatrix(this.matrix);
return mesh;
};
/**
* 转换成图形
* @param {Boolean} groupByCoplanar 是否根据共面进行分组
* @param {Boolean} uniqueMaterial 每个分组使用唯一的材质(此处将材质索引和组索引设置为一样的)
* @returns
*/
ThreeBSP.prototype.toGeometry = function(groupByCoplanar, uniqueMaterial) {
var geometry = new THREE.BufferGeometry(), matrix = new THREE.Matrix4().getInverse(this.matrix);
// verticesArr用于记录点(去重),index依次记录面中各个点对应的索引
var position = [], normal = [], uv = [], verticesArr = [], index = [];
var resolvePolygon = (polygon) => {
polygon.vertices.forEach(item => {
var vertice = item.clone().applyMatrix4(matrix), verticeIndex = null;
for(var i =0, len = verticesArr.length; i < len; i++) {
if(vertice.equals(verticesArr[i])) {
verticeIndex = i;
break;
}
}
// verticeIndex为空,表示数组中未记录当前点,进行点数据处理
if(verticeIndex == null) {
verticeIndex = verticesArr.length;
verticesArr.push(vertice);
position.push(vertice.x);
position.push(vertice.y);
position.push(vertice.z);
normal.push(vertice.normal.x);
normal.push(vertice.normal.y);
normal.push(vertice.normal.z);
uv.push(vertice.uv.x);
uv.push(vertice.uv.y);
}
// 存储点的索引
index.push(verticeIndex);
})
};
if(groupByCoplanar) {
// 将共面的面分到相同的组中
var polygonGroups = [], groups = [];
this.tree.allPolygons().forEach(polygon => {
// 归入分组标志
var flag = false;
for(var i = 0, len = polygonGroups.length; i < len; i++) {
if(COPLANAR === polygon.classifySide(polygonGroups[i][0])) {
polygonGroups[i].push(polygon);
flag = true;
break;
}
}
if(!flag) {
// 为归入到已有分组中,建立新的分组
polygonGroups.push([polygon]);
}
});
// 按照共面组进行数据的处理
var start = 0;
for(var i = 0, len = polygonGroups.length; i < len; i++) {
var polygonGroup = polygonGroups[i], count = polygonGroup.length * 3, groupItem = {
start: start,
count: count
};
if(uniqueMaterial) {
groupItem.materialIndex = i;
}
polygonGroup.forEach(resolvePolygon);
groups.push(groupItem);
start = start + count;
}
geometry.groups = groups;
} else {
this.tree.allPolygons().forEach(resolvePolygon);
}
geometry.setAttribute('position', new THREE.BufferAttribute(Float32Array.from(position), 3, false));
geometry.setAttribute('normal', new THREE.BufferAttribute(Float32Array.from(normal), 3, false));
geometry.setAttribute('uv', new THREE.BufferAttribute(Float32Array.from(uv), 2, false));
geometry.index = new THREE.Uint16BufferAttribute(new Uint16Array(index), 1, false);
return geometry;
};
/**
* 差集
* @param {*} other
* @returns
*/
ThreeBSP.prototype.subtract = function(other) {
var us = this.tree.clone(), them = other.tree.clone();
us.invert().clipTo(them);
them.clipTo(us).invert().clipTo(us).invert();
return new ThreeBSP(us.build(them.allPolygons()).invert(), this.matrix);
};
/**
* 并集
* @param {*} other
* @returns
*/
ThreeBSP.prototype.union = function(other) {
var us = this.tree.clone(), them = other.tree.clone();
us.clipTo(them);
them.clipTo(us).invert().clipTo(us).invert();
return new ThreeBSP(us.build(them.allPolygons()), this.matrix);
};
/**
* 交集
* @param {*} other
* @returns
*/
ThreeBSP.prototype.intersect = function(other) {
var us = this.tree.clone(), them = other.tree.clone();
them.clipTo(us.invert()).invert().clipTo(us.clipTo(them));
return new ThreeBSP(us.build(them.allPolygons()).invert(), this.matrix);
};
ThreeBSP.Vertex = (function(_super) {
__extends(Vertex, _super);
function Vertex(x, y, z, normal, uv) {
this.x = x;
this.y = y;
this.z = z;
this.normal = normal != null ? normal : new THREE.Vector3();
this.uv = uv != null ? uv : new THREE.Vector2();
// 此方法调用父级THREE.Vector3构造函数会报错TypeError: Class constructor Vector3 cannot be invoked without 'new',因此弃用此方式,改为显示初始化x,y,z
// Vertex.__super__.constructor.call(this, x, y, z);
}
/**
* 克隆点
* @returns
*/
Vertex.prototype.clone = function() {
return new ThreeBSP.Vertex(this.x, this.y, this.z, this.normal.clone(), this.uv.clone());
};
/**
* 插点函数
* @param {*} v
* @param {*} alpha
* @returns
*/
Vertex.prototype.lerp = function(v, alpha) {
// 对uv进行插值
this.uv.add(v.uv.clone().sub(this.uv).multiplyScalar(alpha));
// 对法向量进行插值
this.normal.lerp(v, alpha);
// 调用THREE.Vector3的lerp方法对点进行插值
return Vertex.__super__.lerp.apply(this, arguments);
};
/**
* 在两个点之间插入新的点
* @returns
*/
Vertex.prototype.interpolate = function() {
var args = 1 <= arguments.length ? __slice.call(arguments, 0) : [], cloneVertex = this.clone();
return cloneVertex.lerp.apply(cloneVertex, args);
};
/**
* 判断两个点是否相同
* @returns
*/
Vertex.prototype.equals = function(vertex) {
if(vertex) {
if(this.x === vertex.x && this.y === vertex.y && this.z === vertex.z) {
var checkUv = function(uv1, uv2) {
if(uv1 && uv2 && uv1.x === uv2.x && uv1.y === uv2.y) {
return true;
} else if(!uv1 && !uv2) {
return true;
}
return false;
}
if(this.normal && vertex.normal && this.normal.x === vertex.normal.x && this.normal.y === vertex.normal.y && this.normal.z === vertex.normal.z) {
return checkUv(this.uv, vertex.uv);
}
if(!this.normal && !vertex.normal) {
return checkUv(this.uv, vertex.uv);
}
}
}
return false;
};
return Vertex;
})(THREE.Vector3);
/**
* 多边形(或者成为网格),目前默认只有三角形
*/
ThreeBSP.Polygon = (function() {
function Polygon(vertices, normal, w) {
this.vertices = vertices != null ? vertices : [];
// 网格面的法向量
this.normal = normal;
this.w = w;
if (this.vertices.length) {
this.calculateProperties();
}
}
/**
* 计算面的一些属性
* @returns
*/
Polygon.prototype.calculateProperties = function() {
var a = this.vertices[0], b = this.vertices[1], c = this.vertices[2];
// 计算面的法向量
this.normal = b.clone().sub(a).cross(c.clone().sub(a)).normalize();
this.w = this.normal.clone().dot(a);
return this;
};
/**
* 克隆网格面
* @returns
*/
Polygon.prototype.clone = function() {
return new ThreeBSP.Polygon(this.vertices.map(v => v.clone()), this.normal.clone(), this.w);
};
/**
* 对网格面进行翻转倒置
* @returns
*/
Polygon.prototype.invert = function() {
this.normal.multiplyScalar(-1);
this.w *= -1;
this.vertices.reverse();
return this;
};
/**
* 判断点与当前网格面的关系
* @param {*} vertex
* @returns
*/
Polygon.prototype.classifyVertex = function(vertex) {
var side = this.normal.dot(vertex) - this.w;
switch (false) {
case !(side < -EPSILON):
return BACK;
case !(side > EPSILON):
return FRONT;
default:
// 共面
return COPLANAR;
}
};
/**
* 判断指定网格面polygon与当前网格面的关系(FRONT:在当前面的前方, BACK:在当前面的后方, COPLANAR:与当前面共面, SPANNING:两个面交叉)
* @param {*} polygon
* @returns
*/
Polygon.prototype.classifySide = function(polygon) {
var back = 0, front = 0;
polygon.vertices.forEach(vertice => {
switch (this.classifyVertex(vertice)) {
case FRONT:
front += 1;
break;
case BACK:
back += 1;
break;
}
})
if (front > 0 && back === 0) {
return FRONT;
}
if (front === 0 && back > 0) {
return BACK;
}
if ((front === back && back === 0)) {
// 共面
return COPLANAR;
}
return SPANNING;
};
/**
* 对两个网格面进行嵌合
* @param {*} poly
* @returns
*/
Polygon.prototype.tessellate = function(poly) {
// 如果两个面是平行的或者时共面,则直接返回无需处理
if (this.classifySide(poly) !== SPANNING) {
return [poly];
}
// 如果两个面是相交面,则进行插值处理
var frontVertices = [], backVertices = [], count = poly.vertices.length, t, ti, tj, v, vi, vj;
for (var i = 0, _len = poly.vertices.length; i < _len; i++) {
vi = poly.vertices[i];
vj = poly.vertices[(j = (i + 1) % count)];
ti = this.classifyVertex(vi),
tj = this.classifyVertex(vj);
if (ti !== BACK) {
frontVertices.push(vi);
}
if (ti !== FRONT) {
backVertices.push(vi);
}
if ((ti | tj) === SPANNING) {
t = (this.w - this.normal.dot(vi)) / this.normal.dot(vj.clone().sub(vi));
v = vi.interpolate(vj, t);
frontVertices.push(v);
backVertices.push(v);
}
}
var polys = [], frontLength = frontVertices.length, backLength = backVertices.length;
if (frontLength >= 3) {
polys.push(new ThreeBSP.Polygon(frontVertices.slice(0, 3)));
// 将多边形切割成多个三角面
if (frontLength > 3) {
var newVertices;
for(var start = 2; start < frontLength; start++) {
newVertices = [frontVertices[start], frontVertices[(start + 1)%frontLength], frontVertices[(start + 2)%frontLength]]
polys.push(new ThreeBSP.Polygon(newVertices));
}
}
}
if (backLength >= 3) {
polys.push(new ThreeBSP.Polygon(backVertices.slice(0, 3)));
// 将多边形切割成多个三角面
if (backLength > 3) {
var newVertices;
for(var start = 2; start < backLength - 1; start++) {
newVertices = [backVertices[start], backVertices[(start + 1)%backLength], backVertices[(start + 2)%backLength]]
polys.push(new ThreeBSP.Polygon(newVertices));
}
}
}
return polys;
};
/**
* 将指定面的点对于当前面的关系进行细分
* @param {*} polygon
* @param {*} coplanar_front
* @param {*} coplanar_back
* @param {*} front
* @param {*} back
* @returns
*/
Polygon.prototype.subdivide = function(polygon, coplanar_front, coplanar_back, front, back) {
var poly, side, _ref = this.tessellate(polygon), _results = [];
for (var _i = 0, _len = _ref.length; _i < _len; _i++) {
poly = _ref[_i];
side = this.classifySide(poly);
switch (side) {
case FRONT:
_results.push(front.push(poly));
break;
case BACK:
_results.push(back.push(poly));
break;
case COPLANAR:
if (this.normal.dot(poly.normal) > 0) {
_results.push(coplanar_front.push(poly));
} else {
_results.push(coplanar_back.push(poly));
}
break;
default:
throw new Error("BUG: Polygon of classification " + side + " in subdivision");
}
}
return _results;
};
return Polygon;
})();
ThreeBSP.Node = (function() {
function Node(polygons) {
this.polygons = [];
if (polygons != null && polygons.length) {
this.build(polygons);
}
}
/**
*
* @returns 克隆Node
*/
Node.prototype.clone = function() {
var node = new ThreeBSP.Node();
node.divider = this.divider != null ? this.divider.clone() : void 0;
node.polygons = this.polygons.map(item => item.clone());
node.front = this.front != null ? this.front.clone() : void 0;
node.back = this.back != null ? this.back.clone() : void 0;
return node;
};
/**
* 根据面构建Node
* @param {*} polygons
* @returns
*/
Node.prototype.build = function(polygons) {
var polys, sides = {
front: [],
back: []
};
if (this.divider == null) {
this.divider = polygons[0].clone();
}
for (var _i = 0, _len = polygons.length; _i < _len; _i++) {
this.divider.subdivide(polygons[_i], this.polygons, this.polygons, sides.front, sides.back);
}
for (var side in sides) {
if (!__hasProp.call(sides, side)) continue;
polys = sides[side];
if (polys.length) {
if (this[side] == null) {
this[side] = new ThreeBSP.Node();
}
this[side].build(polys)
}
}
return this;
};
/**
* 判断是否是凸面的
* @param {*} polys
* @returns
*/
Node.prototype.isConvex = function(polys) {
var inner, outer;
for (var _i = 0, _len = polys.length; _i < _len; _i++) {
inner = polys[_i];
for (var _j = 0, _len1 = polys.length; _j < _len1; _j++) {
outer = polys[_j];
if (inner !== outer && outer.classifySide(inner) !== BACK) {
return false;
}
}
}
return true;
};
/**
* 获取Node的所有网格面
* @returns
*/
Node.prototype.allPolygons = function() {
return this.polygons.slice().concat(this.front != null ? this.front.allPolygons() : []).concat(this.back != null ? this.back.allPolygons() : []);
};
/**
* 将Node进行倒置反转
* @returns
*/
Node.prototype.invert = function() {
// 反转网格
this.polygons.forEach(item => item.invert());
[this.divider, this.front, this.back].forEach(item => {
if (item != null) {
item.invert();
}
});
var _ref2 = [this.back, this.front];
this.front = _ref2[0];
this.back = _ref2[1]
return this;
};
/**
* 剪裁多边形
* @param {*} polygons
* @returns
*/
Node.prototype.clipPolygons = function(polygons) {
var back = [], front = [];
if (!this.divider) {
return polygons.slice();
}
polygons.forEach(poly => this.divider.subdivide(poly, front, back, front, back))
if (this.front) {
front = this.front.clipPolygons(front);
}
if (this.back) {
back = this.back.clipPolygons(back);
}
return front.concat(this.back ? back : []);
};
/**
* 用指定node对当前node进行剪裁
* @param {*} node
* @returns
*/
Node.prototype.clipTo = function(node) {
this.polygons = node.clipPolygons(this.polygons);
if (this.front != null) {
this.front.clipTo(node);
}
if (this.back != null) {
this.back.clipTo(node);
}
return this;
};
return Node;
})();
return ThreeBSP;
}
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化
1
https://gitee.com/lanfeng1314/threebsp.git
git@gitee.com:lanfeng1314/threebsp.git
lanfeng1314
threebsp
threebsp
master

搜索帮助