add csg and change to userData
parent
470d96d961
commit
f3de6bff7d
41
src/Scene.js
41
src/Scene.js
|
@ -10,9 +10,11 @@ import Stats from './utils/stats.module.js';
|
||||||
import { add3DPoint } from './datums'
|
import { add3DPoint } from './datums'
|
||||||
import { extrude } from './extrude'
|
import { extrude } from './extrude'
|
||||||
import { onHover, onPick } from './utils/mouseEvents';
|
import { onHover, onPick } from './utils/mouseEvents';
|
||||||
import { _vec2, _vec3 } from './utils/static'
|
import { _vec2, _vec3, color } from './utils/static'
|
||||||
import { Vector3 } from 'three/src/Three';
|
import { Vector3 } from 'three/src/Three';
|
||||||
|
|
||||||
|
import CSG from "./utils/three-csg.js"
|
||||||
|
|
||||||
const eq = (a1, a2) => {
|
const eq = (a1, a2) => {
|
||||||
if (a1.length != a2.length) return false
|
if (a1.length != a2.length) return false
|
||||||
for (let i = 0; i < a1.length; i++) {
|
for (let i = 0; i < a1.length; i++) {
|
||||||
|
@ -51,10 +53,11 @@ export class Scene extends THREE.Scene {
|
||||||
const axesHelper = new THREE.AxesHelper(0.4);
|
const axesHelper = new THREE.AxesHelper(0.4);
|
||||||
helpersGroup.add(axesHelper);
|
helpersGroup.add(axesHelper);
|
||||||
|
|
||||||
|
// console.log(color)
|
||||||
const pxy = new THREE.Mesh(
|
const pxy = new THREE.Mesh(
|
||||||
new THREE.PlaneGeometry(5, 5),
|
new THREE.PlaneGeometry(5, 5),
|
||||||
new THREE.MeshBasicMaterial({
|
new THREE.MeshBasicMaterial({
|
||||||
color: 0xff0000,
|
color: color.Plane,
|
||||||
opacity: 0.2,
|
opacity: 0.2,
|
||||||
side: THREE.DoubleSide,
|
side: THREE.DoubleSide,
|
||||||
transparent: true,
|
transparent: true,
|
||||||
|
@ -77,16 +80,15 @@ export class Scene extends THREE.Scene {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const color = 0xFFFFFF;
|
|
||||||
const intensity = 1;
|
const intensity = 1;
|
||||||
const light1 = new THREE.DirectionalLight(color, intensity);
|
const light1 = new THREE.DirectionalLight(color.lighting, intensity);
|
||||||
light1.position.set(10, 10, 10);
|
light1.position.set(10, 10, 10);
|
||||||
this.add(light1);
|
this.add(light1);
|
||||||
|
|
||||||
const light2 = new THREE.DirectionalLight(color, intensity);
|
const light2 = new THREE.DirectionalLight(color.lighting, intensity);
|
||||||
light2.position.set(-10, -10, -5);
|
light2.position.set(-10, -10, -5);
|
||||||
this.add(light2);
|
this.add(light2);
|
||||||
const ambient = new THREE.AmbientLight(color, intensity);
|
const ambient = new THREE.AmbientLight(color.lighting, intensity);
|
||||||
this.add(ambient);
|
this.add(ambient);
|
||||||
|
|
||||||
|
|
||||||
|
@ -198,4 +200,31 @@ async function addSketch() {
|
||||||
}
|
}
|
||||||
|
|
||||||
window.sc = new Scene(store);
|
window.sc = new Scene(store);
|
||||||
|
window.loader = new THREE.ObjectLoader();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// const mm = []
|
||||||
|
// for (let i = 1; i <= 3; i++) {
|
||||||
|
// const obj = loader.parse(JSON.parse(localStorage.getItem(i.toString())))
|
||||||
|
// mm.push(obj)
|
||||||
|
// sc.add(mm[mm.length - 1])
|
||||||
|
// obj.visible = false
|
||||||
|
// }
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// //Create a bsp tree from each of the meshes
|
||||||
|
|
||||||
|
// let bspA = CSG.fromMesh( mm[0] )
|
||||||
|
// let bspB = CSG.fromMesh( mm[2] )
|
||||||
|
|
||||||
|
// // Subtract one bsp from the other via .subtract... other supported modes are .union and .intersect
|
||||||
|
|
||||||
|
// let bspResult = bspA.subtract(bspB)
|
||||||
|
|
||||||
|
// //Get the resulting mesh from the result bsp, and assign meshA.material to the resulting mesh
|
||||||
|
|
||||||
|
// let meshResult = CSG.toMesh( bspResult, mm[0].matrix, mm[0].material )
|
||||||
|
|
||||||
|
// sc.add(meshResult)
|
||||||
|
|
|
@ -12,7 +12,7 @@ export function extrude(sketch) {
|
||||||
|
|
||||||
function findPair(node) {
|
function findPair(node) {
|
||||||
visited.add(node)
|
visited.add(node)
|
||||||
let linkedObj = linkedObjs.get(node.l_id)
|
let linkedObj = linkedObjs.get(node.userData.l_id)
|
||||||
let arr;
|
let arr;
|
||||||
if (linkedObj[0] == 'line') {
|
if (linkedObj[0] == 'line') {
|
||||||
arr = children[objIdx.get(linkedObj[1][2])].geometry.attributes.position.array
|
arr = children[objIdx.get(linkedObj[1][2])].geometry.attributes.position.array
|
||||||
|
@ -40,7 +40,7 @@ export function extrude(sketch) {
|
||||||
|
|
||||||
|
|
||||||
function findTouching(node) {
|
function findTouching(node) {
|
||||||
for (let t of node.constraints) {
|
for (let t of node.userData.constraints) {
|
||||||
if (constraints.get(t)[0] != 'coincident') continue
|
if (constraints.get(t)[0] != 'coincident') continue
|
||||||
for (let c of constraints.get(t)[2]) {
|
for (let c of constraints.get(t)[2]) {
|
||||||
if (c == -1) continue;
|
if (c == -1) continue;
|
||||||
|
@ -64,11 +64,12 @@ export function extrude(sketch) {
|
||||||
const extrudeSettings = { depth: 8, bevelEnabled: false };
|
const extrudeSettings = { depth: 8, bevelEnabled: false };
|
||||||
const geometry = new THREE.ExtrudeGeometry(shape, extrudeSettings);
|
const geometry = new THREE.ExtrudeGeometry(shape, extrudeSettings);
|
||||||
const phong = new THREE.MeshPhongMaterial({
|
const phong = new THREE.MeshPhongMaterial({
|
||||||
color: color.extrude,
|
color: color.Extrude,
|
||||||
emissive: color.emissive,
|
emissive: color.emissive,
|
||||||
flatShading: true
|
flatShading: true
|
||||||
});
|
});
|
||||||
const mesh = new THREE.Mesh(geometry, phong)
|
const mesh = new THREE.Mesh(geometry, phong)
|
||||||
|
mesh.name = "Extrude"
|
||||||
|
|
||||||
for (let i = 0; i < offSetPts.length; i += 2) {
|
for (let i = 0; i < offSetPts.length; i += 2) {
|
||||||
if (
|
if (
|
||||||
|
|
|
@ -172,7 +172,7 @@ class Sketcher extends THREE.Group {
|
||||||
}
|
}
|
||||||
|
|
||||||
delete(obj) {
|
delete(obj) {
|
||||||
let link = this.linkedObjs.get(obj.l_id)
|
let link = this.linkedObjs.get(obj.userData.l_id)
|
||||||
if (!link) return;
|
if (!link) return;
|
||||||
link = link[1]
|
link = link[1]
|
||||||
|
|
||||||
|
@ -183,14 +183,14 @@ class Sketcher extends THREE.Group {
|
||||||
obj.geometry.dispose()
|
obj.geometry.dispose()
|
||||||
obj.material.dispose()
|
obj.material.dispose()
|
||||||
|
|
||||||
for (let c_id of obj.constraints) {
|
for (let c_id of obj.userData.constraints) {
|
||||||
this.deleteConstraints(c_id)
|
this.deleteConstraints(c_id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.children.splice(i, link.length)
|
this.children.splice(i, link.length)
|
||||||
|
|
||||||
this.linkedObjs.delete(obj.l_id)
|
this.linkedObjs.delete(obj.userData.l_id)
|
||||||
|
|
||||||
return i
|
return i
|
||||||
}
|
}
|
||||||
|
@ -201,7 +201,8 @@ class Sketcher extends THREE.Group {
|
||||||
if (idx == -1) continue
|
if (idx == -1) continue
|
||||||
const ob = this.children[this.objIdx.get(idx)]
|
const ob = this.children[this.objIdx.get(idx)]
|
||||||
if (ob) {
|
if (ob) {
|
||||||
ob.constraints.delete(c_id)
|
// ob.constraints.delete(c_id)
|
||||||
|
ob.userData.constraints.splice(ob.userData.constraints.indexOf(c_id), 1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.constraints.delete(c_id)
|
this.constraints.delete(c_id)
|
||||||
|
|
|
@ -12,9 +12,8 @@ export function addDimension(ent1, ent2, distance) {
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
ent1.constraints.add(this.c_id)
|
ent1.userData.constraints.push(this.c_id)
|
||||||
ent2.constraints.add(this.c_id)
|
ent2.userData.constraints.push(this.c_id)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -36,8 +35,8 @@ export function setCoincident() {
|
||||||
[toComb[i - 1].id, toComb[i].id, -1, -1] ///////
|
[toComb[i - 1].id, toComb[i].id, -1, -1] ///////
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
toComb[i].constraints.add(this.c_id)
|
toComb[i].userData.constraints.push(this.c_id)
|
||||||
toComb[i - 1].constraints.add(this.c_id)
|
toComb[i - 1].userData.constraints.push(this.c_id)
|
||||||
}
|
}
|
||||||
|
|
||||||
this.updateOtherBuffers()
|
this.updateOtherBuffers()
|
||||||
|
|
|
@ -18,7 +18,7 @@ export function drawOnClick1(e) {
|
||||||
|
|
||||||
this.linkedObjs.set(this.l_id, [this.mode, this.toPush.map(e=>e.id)])
|
this.linkedObjs.set(this.l_id, [this.mode, this.toPush.map(e=>e.id)])
|
||||||
for (let obj of this.toPush) {
|
for (let obj of this.toPush) {
|
||||||
obj.l_id = this.l_id
|
obj.userData.l_id = this.l_id
|
||||||
}
|
}
|
||||||
this.l_id += 1
|
this.l_id += 1
|
||||||
|
|
||||||
|
|
|
@ -10,16 +10,18 @@ export function sketchArc(mouseLoc) {
|
||||||
|
|
||||||
const p1 = ptObj(mouseLoc)
|
const p1 = ptObj(mouseLoc)
|
||||||
p1.matrixAutoUpdate = false;
|
p1.matrixAutoUpdate = false;
|
||||||
p1.constraints = new Set()
|
p1.userData.constraints = []
|
||||||
|
|
||||||
const p2 = ptObj()
|
const p2 = ptObj()
|
||||||
p2.matrixAutoUpdate = false;
|
p2.matrixAutoUpdate = false;
|
||||||
p2.constraints = new Set()
|
p2.userData.constraints = []
|
||||||
|
|
||||||
const arc = lineObj(n)
|
const arc = lineObj(n)
|
||||||
arc.frustumCulled = false;
|
arc.frustumCulled = false;
|
||||||
|
|
||||||
const p3 = ptObj()
|
const p3 = ptObj()
|
||||||
|
p3.matrixAutoUpdate = false;
|
||||||
|
p3.userData.constraints = []
|
||||||
|
|
||||||
return [p1, p2, p3, arc]
|
return [p1, p2, p3, arc]
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,16 +6,16 @@ export function sketchLine(mouseLoc) {
|
||||||
const p1 = ptObj()
|
const p1 = ptObj()
|
||||||
|
|
||||||
p1.matrixAutoUpdate = false;
|
p1.matrixAutoUpdate = false;
|
||||||
p1.constraints = new Set()
|
p1.userData.constraints = []
|
||||||
|
|
||||||
const p2 = ptObj()
|
const p2 = ptObj()
|
||||||
p2.matrixAutoUpdate = false;
|
p2.matrixAutoUpdate = false;
|
||||||
p2.constraints = new Set()
|
p2.userData.constraints = []
|
||||||
|
|
||||||
const line = lineObj()
|
const line = lineObj()
|
||||||
line.matrixAutoUpdate = false;
|
line.matrixAutoUpdate = false;
|
||||||
line.frustumCulled = false;
|
line.frustumCulled = false;
|
||||||
line.constraints = new Set()
|
line.userData.constraints = []
|
||||||
|
|
||||||
|
|
||||||
line.geometry.attributes.position.set(mouseLoc)
|
line.geometry.attributes.position.set(mouseLoc)
|
||||||
|
@ -30,8 +30,9 @@ export function sketchLine(mouseLoc) {
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
p1.constraints.add(this.c_id)
|
p1.userData.constraints.push(this.c_id)
|
||||||
this.children[this.children.length - 2].constraints.add(this.c_id)
|
console.log(this.children[this.children.length - 2].userData.constraints,'here')
|
||||||
|
this.children[this.children.length - 2].userData.constraints.push(this.c_id)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,483 @@
|
||||||
|
|
||||||
|
// ## License
|
||||||
|
//
|
||||||
|
// Copyright (c) 2011 Evan Wallace (http://madebyevan.com/), under the MIT license.
|
||||||
|
// THREE.js rework by thrax
|
||||||
|
|
||||||
|
// # class CSG
|
||||||
|
// Holds a binary space partition tree representing a 3D solid. Two solids can
|
||||||
|
// be combined using the `union()`, `subtract()`, and `intersect()` methods.
|
||||||
|
|
||||||
|
|
||||||
|
class CSG {
|
||||||
|
constructor() {
|
||||||
|
this.polygons = [];
|
||||||
|
}
|
||||||
|
clone() {
|
||||||
|
let csg = new CSG();
|
||||||
|
csg.polygons = this.polygons.map(function(p) {
|
||||||
|
return p.clone();
|
||||||
|
});
|
||||||
|
return csg;
|
||||||
|
}
|
||||||
|
|
||||||
|
toPolygons() {
|
||||||
|
return this.polygons;
|
||||||
|
}
|
||||||
|
|
||||||
|
union(csg) {
|
||||||
|
let a = new Node(this.clone().polygons);
|
||||||
|
let b = new Node(csg.clone().polygons);
|
||||||
|
a.clipTo(b);
|
||||||
|
b.clipTo(a);
|
||||||
|
b.invert();
|
||||||
|
b.clipTo(a);
|
||||||
|
b.invert();
|
||||||
|
a.build(b.allPolygons());
|
||||||
|
return CSG.fromPolygons(a.allPolygons());
|
||||||
|
}
|
||||||
|
|
||||||
|
subtract(csg) {
|
||||||
|
let a = new Node(this.clone().polygons);
|
||||||
|
let b = new Node(csg.clone().polygons);
|
||||||
|
a.invert();
|
||||||
|
a.clipTo(b);
|
||||||
|
b.clipTo(a);
|
||||||
|
b.invert();
|
||||||
|
b.clipTo(a);
|
||||||
|
b.invert();
|
||||||
|
a.build(b.allPolygons());
|
||||||
|
a.invert();
|
||||||
|
return CSG.fromPolygons(a.allPolygons());
|
||||||
|
}
|
||||||
|
|
||||||
|
intersect(csg) {
|
||||||
|
let a = new Node(this.clone().polygons);
|
||||||
|
let b = new Node(csg.clone().polygons);
|
||||||
|
a.invert();
|
||||||
|
b.clipTo(a);
|
||||||
|
b.invert();
|
||||||
|
a.clipTo(b);
|
||||||
|
b.clipTo(a);
|
||||||
|
a.build(b.allPolygons());
|
||||||
|
a.invert();
|
||||||
|
return CSG.fromPolygons(a.allPolygons());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return a new CSG solid with solid and empty space switched. This solid is
|
||||||
|
// not modified.
|
||||||
|
inverse() {
|
||||||
|
let csg = this.clone();
|
||||||
|
csg.polygons.forEach(p=>p.flip());
|
||||||
|
return csg;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Construct a CSG solid from a list of `Polygon` instances.
|
||||||
|
CSG.fromPolygons=function(polygons) {
|
||||||
|
let csg = new CSG();
|
||||||
|
csg.polygons = polygons;
|
||||||
|
return csg;
|
||||||
|
}
|
||||||
|
|
||||||
|
// # class Vector
|
||||||
|
|
||||||
|
// Represents a 3D vector.
|
||||||
|
//
|
||||||
|
// Example usage:
|
||||||
|
//
|
||||||
|
// new CSG.Vector(1, 2, 3);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class Vector {
|
||||||
|
constructor(x=0, y=0, z=0) {
|
||||||
|
this.x=x;
|
||||||
|
this.y=y;
|
||||||
|
this.z=z;
|
||||||
|
}
|
||||||
|
copy(v){
|
||||||
|
this.x=v.x;
|
||||||
|
this.y=v.y;
|
||||||
|
this.z=v.z;
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
clone() {
|
||||||
|
return new Vector(this.x,this.y,this.z)
|
||||||
|
}
|
||||||
|
negate() {
|
||||||
|
this.x*=-1;
|
||||||
|
this.y*=-1;
|
||||||
|
this.z*=-1;
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
add(a) {
|
||||||
|
this.x+=a.x
|
||||||
|
this.y+=a.y
|
||||||
|
this.z+=a.z
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
sub(a) {
|
||||||
|
this.x-=a.x
|
||||||
|
this.y-=a.y
|
||||||
|
this.z-=a.z
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
times(a) {
|
||||||
|
this.x*=a
|
||||||
|
this.y*=a
|
||||||
|
this.z*=a
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
dividedBy(a) {
|
||||||
|
this.x/=a
|
||||||
|
this.y/=a
|
||||||
|
this.z/=a
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
lerp(a, t) {
|
||||||
|
return this.add(tv0.copy(a).sub(this).times(t))
|
||||||
|
}
|
||||||
|
unit() {
|
||||||
|
return this.dividedBy(this.length())
|
||||||
|
}
|
||||||
|
length(){
|
||||||
|
return Math.sqrt((this.x**2)+(this.y**2)+(this.z**2))
|
||||||
|
}
|
||||||
|
normalize(){
|
||||||
|
return this.unit()
|
||||||
|
}
|
||||||
|
cross(b) {
|
||||||
|
let a = this;
|
||||||
|
const ax = a.x, ay = a.y, az = a.z;
|
||||||
|
const bx = b.x, by = b.y, bz = b.z;
|
||||||
|
|
||||||
|
this.x = ay * bz - az * by;
|
||||||
|
this.y = az * bx - ax * bz;
|
||||||
|
this.z = ax * by - ay * bx;
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
dot(b){
|
||||||
|
return (this.x*b.x)+(this.y*b.y)+(this.z*b.z)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//Temporaries used to avoid internal allocation..
|
||||||
|
let tv0=new Vector()
|
||||||
|
let tv1=new Vector()
|
||||||
|
|
||||||
|
|
||||||
|
// # class Vertex
|
||||||
|
|
||||||
|
// Represents a vertex of a polygon. Use your own vertex class instead of this
|
||||||
|
// one to provide additional features like texture coordinates and vertex
|
||||||
|
// colors. Custom vertex classes need to provide a `pos` property and `clone()`,
|
||||||
|
// `flip()`, and `interpolate()` methods that behave analogous to the ones
|
||||||
|
// defined by `CSG.Vertex`. This class provides `normal` so convenience
|
||||||
|
// functions like `CSG.sphere()` can return a smooth vertex normal, but `normal`
|
||||||
|
// is not used anywhere else.
|
||||||
|
|
||||||
|
class Vertex {
|
||||||
|
|
||||||
|
constructor(pos, normal, uv, color) {
|
||||||
|
this.pos = new Vector().copy(pos);
|
||||||
|
this.normal = new Vector().copy(normal);
|
||||||
|
this.uv = new Vector().copy(uv);
|
||||||
|
this.uv.z=0;
|
||||||
|
color && (this.color = new Vector().copy(color));
|
||||||
|
}
|
||||||
|
|
||||||
|
clone() {
|
||||||
|
return new Vertex(this.pos,this.normal,this.uv,this.color);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Invert all orientation-specific data (e.g. vertex normal). Called when the
|
||||||
|
// orientation of a polygon is flipped.
|
||||||
|
flip() {
|
||||||
|
this.normal.negate();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a new vertex between this vertex and `other` by linearly
|
||||||
|
// interpolating all properties using a parameter of `t`. Subclasses should
|
||||||
|
// override this to interpolate additional properties.
|
||||||
|
interpolate(other, t) {
|
||||||
|
return new Vertex(this.pos.clone().lerp(other.pos, t),this.normal.clone().lerp(other.normal, t),this.uv.clone().lerp(other.uv, t), this.color&&other.color&&this.color.clone().lerp(other.color,t))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
;
|
||||||
|
// # class Plane
|
||||||
|
|
||||||
|
// Represents a plane in 3D space.
|
||||||
|
|
||||||
|
class Plane {
|
||||||
|
constructor(normal, w) {
|
||||||
|
this.normal = normal;
|
||||||
|
this.w = w;
|
||||||
|
}
|
||||||
|
|
||||||
|
clone() {
|
||||||
|
return new Plane(this.normal.clone(),this.w);
|
||||||
|
}
|
||||||
|
|
||||||
|
flip() {
|
||||||
|
this.normal.negate();
|
||||||
|
this.w = -this.w;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Split `polygon` by this plane if needed, then put the polygon or polygon
|
||||||
|
// fragments in the appropriate lists. Coplanar polygons go into either
|
||||||
|
// `coplanarFront` or `coplanarBack` depending on their orientation with
|
||||||
|
// respect to this plane. Polygons in front or in back of this plane go into
|
||||||
|
// either `front` or `back`.
|
||||||
|
splitPolygon(polygon, coplanarFront, coplanarBack, front, back) {
|
||||||
|
const COPLANAR = 0;
|
||||||
|
const FRONT = 1;
|
||||||
|
const BACK = 2;
|
||||||
|
const SPANNING = 3;
|
||||||
|
|
||||||
|
// Classify each point as well as the entire polygon into one of the above
|
||||||
|
// four classes.
|
||||||
|
let polygonType = 0;
|
||||||
|
let types = [];
|
||||||
|
for (let i = 0; i < polygon.vertices.length; i++) {
|
||||||
|
let t = this.normal.dot(polygon.vertices[i].pos) - this.w;
|
||||||
|
let type = (t < -Plane.EPSILON) ? BACK : (t > Plane.EPSILON) ? FRONT : COPLANAR;
|
||||||
|
polygonType |= type;
|
||||||
|
types.push(type);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Put the polygon in the correct list, splitting it when necessary.
|
||||||
|
switch (polygonType) {
|
||||||
|
case COPLANAR:
|
||||||
|
(this.normal.dot(polygon.plane.normal) > 0 ? coplanarFront : coplanarBack).push(polygon);
|
||||||
|
break;
|
||||||
|
case FRONT:
|
||||||
|
front.push(polygon);
|
||||||
|
break;
|
||||||
|
case BACK:
|
||||||
|
back.push(polygon);
|
||||||
|
break;
|
||||||
|
case SPANNING:
|
||||||
|
let f = []
|
||||||
|
, b = [];
|
||||||
|
for (let i = 0; i < polygon.vertices.length; i++) {
|
||||||
|
let j = (i + 1) % polygon.vertices.length;
|
||||||
|
let ti = types[i]
|
||||||
|
, tj = types[j];
|
||||||
|
let vi = polygon.vertices[i]
|
||||||
|
, vj = polygon.vertices[j];
|
||||||
|
if (ti != BACK)
|
||||||
|
f.push(vi);
|
||||||
|
if (ti != FRONT)
|
||||||
|
b.push(ti != BACK ? vi.clone() : vi);
|
||||||
|
if ((ti | tj) == SPANNING) {
|
||||||
|
let t = (this.w - this.normal.dot(vi.pos)) / this.normal.dot(tv0.copy(vj.pos).sub(vi.pos));
|
||||||
|
let v = vi.interpolate(vj, t);
|
||||||
|
f.push(v);
|
||||||
|
b.push(v.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (f.length >= 3)
|
||||||
|
front.push(new Polygon(f,polygon.shared));
|
||||||
|
if (b.length >= 3)
|
||||||
|
back.push(new Polygon(b,polygon.shared));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// `Plane.EPSILON` is the tolerance used by `splitPolygon()` to decide if a
|
||||||
|
// point is on the plane.
|
||||||
|
Plane.EPSILON = 1e-5;
|
||||||
|
|
||||||
|
Plane.fromPoints = function(a, b, c) {
|
||||||
|
let n = tv0.copy(b).sub(a).cross(tv1.copy(c).sub(a)).normalize()
|
||||||
|
return new Plane(n.clone(),n.dot(a));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// # class Polygon
|
||||||
|
|
||||||
|
// Represents a convex polygon. The vertices used to initialize a polygon must
|
||||||
|
// be coplanar and form a convex loop. They do not have to be `Vertex`
|
||||||
|
// instances but they must behave similarly (duck typing can be used for
|
||||||
|
// customization).
|
||||||
|
//
|
||||||
|
// Each convex polygon has a `shared` property, which is shared between all
|
||||||
|
// polygons that are clones of each other or were split from the same polygon.
|
||||||
|
// This can be used to define per-polygon properties (such as surface color).
|
||||||
|
|
||||||
|
class Polygon {
|
||||||
|
constructor(vertices, shared) {
|
||||||
|
this.vertices = vertices;
|
||||||
|
this.shared = shared;
|
||||||
|
this.plane = Plane.fromPoints(vertices[0].pos, vertices[1].pos, vertices[2].pos);
|
||||||
|
}
|
||||||
|
clone() {
|
||||||
|
return new Polygon(this.vertices.map(v=>v.clone()),this.shared);
|
||||||
|
}
|
||||||
|
flip() {
|
||||||
|
this.vertices.reverse().map(v=>v.flip())
|
||||||
|
this.plane.flip();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// # class Node
|
||||||
|
|
||||||
|
// Holds a node in a BSP tree. A BSP tree is built from a collection of polygons
|
||||||
|
// by picking a polygon to split along. That polygon (and all other coplanar
|
||||||
|
// polygons) are added directly to that node and the other polygons are added to
|
||||||
|
// the front and/or back subtrees. This is not a leafy BSP tree since there is
|
||||||
|
// no distinction between internal and leaf nodes.
|
||||||
|
|
||||||
|
class Node {
|
||||||
|
constructor(polygons) {
|
||||||
|
this.plane = null;
|
||||||
|
this.front = null;
|
||||||
|
this.back = null;
|
||||||
|
this.polygons = [];
|
||||||
|
if (polygons)
|
||||||
|
this.build(polygons);
|
||||||
|
}
|
||||||
|
clone() {
|
||||||
|
let node = new Node();
|
||||||
|
node.plane = this.plane && this.plane.clone();
|
||||||
|
node.front = this.front && this.front.clone();
|
||||||
|
node.back = this.back && this.back.clone();
|
||||||
|
node.polygons = this.polygons.map(p=>p.clone());
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert solid space to empty space and empty space to solid space.
|
||||||
|
invert() {
|
||||||
|
for (let i = 0; i < this.polygons.length; i++)
|
||||||
|
this.polygons[i].flip();
|
||||||
|
|
||||||
|
this.plane && this.plane.flip();
|
||||||
|
this.front && this.front.invert();
|
||||||
|
this.back && this.back.invert();
|
||||||
|
let temp = this.front;
|
||||||
|
this.front = this.back;
|
||||||
|
this.back = temp;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Recursively remove all polygons in `polygons` that are inside this BSP
|
||||||
|
// tree.
|
||||||
|
clipPolygons(polygons) {
|
||||||
|
if (!this.plane)
|
||||||
|
return polygons.slice();
|
||||||
|
let front = []
|
||||||
|
, back = [];
|
||||||
|
for (let i = 0; i < polygons.length; i++) {
|
||||||
|
this.plane.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);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove all polygons in this BSP tree that are inside the other BSP tree
|
||||||
|
// `bsp`.
|
||||||
|
clipTo(bsp) {
|
||||||
|
this.polygons = bsp.clipPolygons(this.polygons);
|
||||||
|
if (this.front)
|
||||||
|
this.front.clipTo(bsp);
|
||||||
|
if (this.back)
|
||||||
|
this.back.clipTo(bsp);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return a list of all polygons in this BSP tree.
|
||||||
|
allPolygons() {
|
||||||
|
let polygons = this.polygons.slice();
|
||||||
|
if (this.front)
|
||||||
|
polygons = polygons.concat(this.front.allPolygons());
|
||||||
|
if (this.back)
|
||||||
|
polygons = polygons.concat(this.back.allPolygons());
|
||||||
|
return polygons;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build a BSP tree out of `polygons`. When called on an existing tree, the
|
||||||
|
// new polygons are filtered down to the bottom of the tree and become new
|
||||||
|
// nodes there. Each set of polygons is partitioned using the first polygon
|
||||||
|
// (no heuristic is used to pick a good split).
|
||||||
|
build(polygons) {
|
||||||
|
if (!polygons.length)
|
||||||
|
return;
|
||||||
|
if (!this.plane)
|
||||||
|
this.plane = polygons[0].plane.clone();
|
||||||
|
let front = []
|
||||||
|
, back = [];
|
||||||
|
for (let i = 0; i < polygons.length; i++) {
|
||||||
|
this.plane.splitPolygon(polygons[i], this.polygons, this.polygons, front, back);
|
||||||
|
}
|
||||||
|
if (front.length) {
|
||||||
|
if (!this.front)
|
||||||
|
this.front = new Node();
|
||||||
|
this.front.build(front);
|
||||||
|
}
|
||||||
|
if (back.length) {
|
||||||
|
if (!this.back)
|
||||||
|
this.back = new Node();
|
||||||
|
this.back.build(back);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
CSG.fromJSON=function(json){
|
||||||
|
return CSG.fromPolygons(json.polygons.map(p=>new Polygon(p.vertices.map(v=> new Vertex(v.pos,v.normal,v.uv)),p.shared)))
|
||||||
|
}
|
||||||
|
|
||||||
|
export {CSG,Vertex,Vector,Polygon,Plane}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// Return a new CSG solid representing space in either this solid or in the
|
||||||
|
// solid `csg`. Neither this solid nor the solid `csg` are modified.
|
||||||
|
//
|
||||||
|
// A.union(B)
|
||||||
|
//
|
||||||
|
// +-------+ +-------+
|
||||||
|
// | | | |
|
||||||
|
// | A | | |
|
||||||
|
// | +--+----+ = | +----+
|
||||||
|
// +----+--+ | +----+ |
|
||||||
|
// | B | | |
|
||||||
|
// | | | |
|
||||||
|
// +-------+ +-------+
|
||||||
|
//
|
||||||
|
// Return a new CSG solid representing space in this solid but not in the
|
||||||
|
// solid `csg`. Neither this solid nor the solid `csg` are modified.
|
||||||
|
//
|
||||||
|
// A.subtract(B)
|
||||||
|
//
|
||||||
|
// +-------+ +-------+
|
||||||
|
// | | | |
|
||||||
|
// | A | | |
|
||||||
|
// | +--+----+ = | +--+
|
||||||
|
// +----+--+ | +----+
|
||||||
|
// | B |
|
||||||
|
// | |
|
||||||
|
// +-------+
|
||||||
|
//
|
||||||
|
// Return a new CSG solid representing space both this solid and in the
|
||||||
|
// solid `csg`. Neither this solid nor the solid `csg` are modified.
|
||||||
|
//
|
||||||
|
// A.intersect(B)
|
||||||
|
//
|
||||||
|
// +-------+
|
||||||
|
// | |
|
||||||
|
// | A |
|
||||||
|
// | +--+----+ = +--+
|
||||||
|
// +----+--+ | +--+
|
||||||
|
// | B |
|
||||||
|
// | |
|
||||||
|
// +-------+
|
||||||
|
//
|
||||||
|
|
|
@ -21,7 +21,6 @@ export function onHover(e) {
|
||||||
|
|
||||||
let idx = []
|
let idx = []
|
||||||
if (hoverPts.length) {
|
if (hoverPts.length) {
|
||||||
// console.log(hoverPts)
|
|
||||||
let minDist = Infinity;
|
let minDist = Infinity;
|
||||||
for (let i = 0; i < hoverPts.length; i++) {
|
for (let i = 0; i < hoverPts.length; i++) {
|
||||||
if (!hoverPts[i].distanceToRay) continue;
|
if (!hoverPts[i].distanceToRay) continue;
|
||||||
|
@ -40,7 +39,8 @@ export function onHover(e) {
|
||||||
|
|
||||||
const obj = this.hovered[this.hovered.length - 1]
|
const obj = this.hovered[this.hovered.length - 1]
|
||||||
if (obj && !this.selected.includes(obj)) {
|
if (obj && !this.selected.includes(obj)) {
|
||||||
obj.material.color.set(0x555555)
|
// obj.material.color.set(0x555555)
|
||||||
|
obj.material.color.set(color[obj.name])
|
||||||
}
|
}
|
||||||
this.hovered = []
|
this.hovered = []
|
||||||
|
|
||||||
|
@ -58,7 +58,7 @@ export function onHover(e) {
|
||||||
|
|
||||||
const obj = this.hovered[this.hovered.length - 1]
|
const obj = this.hovered[this.hovered.length - 1]
|
||||||
if (obj && !this.selected.includes(obj)) {
|
if (obj && !this.selected.includes(obj)) {
|
||||||
obj.material.color.set(0x555555)
|
obj.material.color.set(color[obj.name])
|
||||||
}
|
}
|
||||||
this.hovered = []
|
this.hovered = []
|
||||||
|
|
||||||
|
@ -82,7 +82,8 @@ export function onPick(e) {
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
for (let obj of this.selected) {
|
for (let obj of this.selected) {
|
||||||
obj.material.color.set(0x555555)
|
// obj.material.color.set(0x555555)
|
||||||
|
obj.material.color.set(color[obj.name])
|
||||||
}
|
}
|
||||||
this.dispatchEvent({ type: 'change' })
|
this.dispatchEvent({ type: 'change' })
|
||||||
this.selected = []
|
this.selected = []
|
||||||
|
|
|
@ -8,43 +8,52 @@ const _vec3 = new THREE.Vector3()
|
||||||
|
|
||||||
const raycaster = new THREE.Raycaster();
|
const raycaster = new THREE.Raycaster();
|
||||||
raycaster.params.Line.threshold = 0.8;
|
raycaster.params.Line.threshold = 0.8;
|
||||||
raycaster.params.Points.threshold = 1.5;
|
raycaster.params.Points.threshold = 0.6;
|
||||||
|
|
||||||
|
|
||||||
const color = {
|
const color = {
|
||||||
dark1: 0x555555,
|
|
||||||
hover: 0x00ff00,
|
hover: 0x00ff00,
|
||||||
extrude: 0x156289,
|
lighting: 0xFFFFFF,
|
||||||
emissive: 0x072534
|
emissive: 0x072534,
|
||||||
|
Plane: 0xf5bc42,
|
||||||
|
Line: 0x555555,
|
||||||
|
Points: 0x555555,
|
||||||
|
Extrude: 0x156289,
|
||||||
}
|
}
|
||||||
|
|
||||||
const lineMaterial = new THREE.LineBasicMaterial({
|
const lineMaterial = new THREE.LineBasicMaterial({
|
||||||
linewidth: 2,
|
linewidth: 2,
|
||||||
color: color.dark1,
|
color: color.Line,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
const pointMaterial = new THREE.PointsMaterial({
|
const pointMaterial = new THREE.PointsMaterial({
|
||||||
color: color.dark1,
|
color: color.Points,
|
||||||
size: 4,
|
size: 4,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
|
const ptObj = (n) => {
|
||||||
|
const ret = new THREE.Points(
|
||||||
|
new THREE.BufferGeometry().setAttribute('position',
|
||||||
|
new THREE.Float32BufferAttribute(n || 3, 3)
|
||||||
|
),
|
||||||
|
pointMaterial.clone()
|
||||||
|
);
|
||||||
|
ret.name = 'Points'
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
const lineObj = (n = 1) => {
|
||||||
const ptObj = (n) => new THREE.Points(
|
const ret = new THREE.Line(
|
||||||
new THREE.BufferGeometry().setAttribute('position',
|
new THREE.BufferGeometry().setAttribute('position',
|
||||||
new THREE.Float32BufferAttribute(n || 3, 3)
|
new THREE.Float32BufferAttribute(3 * (n + 1), 3)
|
||||||
),
|
),
|
||||||
pointMaterial.clone()
|
lineMaterial.clone()
|
||||||
);
|
);
|
||||||
|
ret.name = 'Line'
|
||||||
const lineObj = (n=1) => new THREE.Line(
|
return ret
|
||||||
new THREE.BufferGeometry().setAttribute('position',
|
}
|
||||||
new THREE.Float32BufferAttribute(3 * (n+1), 3)
|
|
||||||
),
|
|
||||||
lineMaterial.clone()
|
|
||||||
);
|
|
||||||
|
|
||||||
|
|
||||||
export { lineMaterial, pointMaterial, _vec2, _vec3, raycaster, color, ptObj, lineObj }
|
export { lineMaterial, pointMaterial, _vec2, _vec3, raycaster, color, ptObj, lineObj }
|
|
@ -0,0 +1,200 @@
|
||||||
|
"use strict"
|
||||||
|
|
||||||
|
import * as THREE from '../../node_modules/three/src/Three';
|
||||||
|
import { CSG, Vertex, Polygon } from "./csg-lib.js"
|
||||||
|
|
||||||
|
|
||||||
|
CSG.fromGeometry = function (geom, objectIndex) {
|
||||||
|
if (!geom.isBufferGeometry) {
|
||||||
|
console.error("Unsupported CSG input type:" + geom.type)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
let polys = []
|
||||||
|
|
||||||
|
let posattr = geom.attributes.position
|
||||||
|
let normalattr = geom.attributes.normal
|
||||||
|
let uvattr = geom.attributes.uv
|
||||||
|
let colorattr = geom.attributes.color
|
||||||
|
let index;
|
||||||
|
if (geom.index)
|
||||||
|
index = geom.index.array;
|
||||||
|
else {
|
||||||
|
index = new Array((posattr.array.length / posattr.itemSize) | 0);
|
||||||
|
for (let i = 0; i < index.length; i++)
|
||||||
|
index[i] = i
|
||||||
|
}
|
||||||
|
let triCount = (index.length / 3) | 0
|
||||||
|
polys = new Array(triCount)
|
||||||
|
for (let i = 0, pli = 0, l = index.length; i < l; i += 3,
|
||||||
|
pli++) {
|
||||||
|
let vertices = new Array(3)
|
||||||
|
for (let j = 0; j < 3; j++) {
|
||||||
|
let vi = index[i + j]
|
||||||
|
let vp = vi * 3;
|
||||||
|
let vt = vi * 2;
|
||||||
|
let x = posattr.array[vp]
|
||||||
|
let y = posattr.array[vp + 1]
|
||||||
|
let z = posattr.array[vp + 2]
|
||||||
|
let nx = normalattr.array[vp]
|
||||||
|
let ny = normalattr.array[vp + 1]
|
||||||
|
let nz = normalattr.array[vp + 2]
|
||||||
|
let u = uvattr.array[vt]
|
||||||
|
let v = uvattr.array[vt + 1]
|
||||||
|
vertices[j] = new Vertex({
|
||||||
|
x,
|
||||||
|
y,
|
||||||
|
z
|
||||||
|
}, {
|
||||||
|
x: nx,
|
||||||
|
y: ny,
|
||||||
|
z: nz
|
||||||
|
}, {
|
||||||
|
x: u,
|
||||||
|
y: v,
|
||||||
|
z: 0
|
||||||
|
}, colorattr && { x: colorattr.array[vt], y: colorattr.array[vt + 1], z: colorattr.array[vt + 2] });
|
||||||
|
}
|
||||||
|
polys[pli] = new Polygon(vertices, objectIndex)
|
||||||
|
}
|
||||||
|
|
||||||
|
return CSG.fromPolygons(polys)
|
||||||
|
}
|
||||||
|
|
||||||
|
let ttvv0 = new THREE.Vector3()
|
||||||
|
let tmpm3 = new THREE.Matrix3();
|
||||||
|
CSG.fromMesh = function (mesh, objectIndex) {
|
||||||
|
let csg = CSG.fromGeometry(mesh.geometry, objectIndex)
|
||||||
|
tmpm3.getNormalMatrix(mesh.matrix);
|
||||||
|
for (let i = 0; i < csg.polygons.length; i++) {
|
||||||
|
let p = csg.polygons[i]
|
||||||
|
for (let j = 0; j < p.vertices.length; j++) {
|
||||||
|
let v = p.vertices[j]
|
||||||
|
v.pos.copy(ttvv0.copy(v.pos).applyMatrix4(mesh.matrix));
|
||||||
|
v.normal.copy(ttvv0.copy(v.normal).applyMatrix3(tmpm3))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return csg;
|
||||||
|
}
|
||||||
|
|
||||||
|
let nbuf3 = (ct) => {
|
||||||
|
return {
|
||||||
|
top: 0,
|
||||||
|
array: new Float32Array(ct),
|
||||||
|
write: function (v) { (this.array[this.top++] = v.x); (this.array[this.top++] = v.y); (this.array[this.top++] = v.z); }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let nbuf2 = (ct) => {
|
||||||
|
return {
|
||||||
|
top: 0,
|
||||||
|
array: new Float32Array(ct),
|
||||||
|
write: function (v) { (this.array[this.top++] = v.x); (this.array[this.top++] = v.y) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CSG.toMesh = function (csg, toMatrix, toMaterial) {
|
||||||
|
|
||||||
|
let ps = csg.polygons;
|
||||||
|
let geom;
|
||||||
|
let g2;
|
||||||
|
if (0) //Old geometry path...
|
||||||
|
{
|
||||||
|
geom = new Geometry();
|
||||||
|
let vs = geom.vertices;
|
||||||
|
let fvuv = geom.faceVertexUvs[0]
|
||||||
|
for (let i = 0; i < ps.length; i++) {
|
||||||
|
let p = ps[i]
|
||||||
|
let pvs = p.vertices;
|
||||||
|
let v0 = vs.length;
|
||||||
|
let pvlen = pvs.length
|
||||||
|
|
||||||
|
for (let j = 0; j < pvlen; j++)
|
||||||
|
vs.push(new THREE.Vector3().copy(pvs[j].pos))
|
||||||
|
|
||||||
|
for (let j = 3; j <= pvlen; j++) {
|
||||||
|
let fc = new THREE.Face3();
|
||||||
|
let fuv = []
|
||||||
|
fvuv.push(fuv)
|
||||||
|
let fnml = fc.vertexNormals;
|
||||||
|
fc.a = v0;
|
||||||
|
fc.b = v0 + j - 2;
|
||||||
|
fc.c = v0 + j - 1;
|
||||||
|
|
||||||
|
fnml.push(new THREE.Vector3().copy(pvs[0].normal))
|
||||||
|
fnml.push(new THREE.Vector3().copy(pvs[j - 2].normal))
|
||||||
|
fnml.push(new THREE.Vector3().copy(pvs[j - 1].normal))
|
||||||
|
fuv.push(new THREE.Vector3().copy(pvs[0].uv))
|
||||||
|
fuv.push(new THREE.Vector3().copy(pvs[j - 2].uv))
|
||||||
|
fuv.push(new THREE.Vector3().copy(pvs[j - 1].uv))
|
||||||
|
|
||||||
|
fc.normal = new THREE.Vector3().copy(p.plane.normal)
|
||||||
|
geom.faces.push(fc)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
geom = new THREE.BufferGeometry().fromGeometry(geom)
|
||||||
|
geom.verticesNeedUpdate = geom.elementsNeedUpdate = geom.normalsNeedUpdate = true;
|
||||||
|
}
|
||||||
|
if (1) { //BufferGeometry path
|
||||||
|
let triCount = 0;
|
||||||
|
ps.forEach(p => triCount += (p.vertices.length - 2))
|
||||||
|
geom = new THREE.BufferGeometry()
|
||||||
|
|
||||||
|
let vertices = nbuf3(triCount * 3 * 3)
|
||||||
|
let normals = nbuf3(triCount * 3 * 3)
|
||||||
|
let uvs = nbuf2(triCount * 2 * 3)
|
||||||
|
let colors;
|
||||||
|
let grps = []
|
||||||
|
ps.forEach(p => {
|
||||||
|
let pvs = p.vertices
|
||||||
|
let pvlen = pvs.length
|
||||||
|
if (p.shared !== undefined) {
|
||||||
|
if (!grps[p.shared]) grps[p.shared] = []
|
||||||
|
}
|
||||||
|
if (pvlen && pvs[0].color !== undefined) {
|
||||||
|
if (!colors) colors = nbuf3(triCount * 3 * 3);
|
||||||
|
}
|
||||||
|
for (let j = 3; j <= pvlen; j++) {
|
||||||
|
(p.shared !== undefined) && (grps[p.shared].push(vertices.top / 3, (vertices.top / 3) + 1, (vertices.top / 3) + 2));
|
||||||
|
vertices.write(pvs[0].pos)
|
||||||
|
vertices.write(pvs[j - 2].pos)
|
||||||
|
vertices.write(pvs[j - 1].pos)
|
||||||
|
normals.write(pvs[0].normal)
|
||||||
|
normals.write(pvs[j - 2].normal)
|
||||||
|
normals.write(pvs[j - 1].normal)
|
||||||
|
uvs.write(pvs[0].uv)
|
||||||
|
uvs.write(pvs[j - 2].uv)
|
||||||
|
uvs.write(pvs[j - 1].uv)
|
||||||
|
colors && (colors.write(pvs[0].color) || colors.write(pvs[j - 2].color) || colors.write(pvs[j - 1].color))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
geom.setAttribute('position', new THREE.BufferAttribute(vertices.array, 3));
|
||||||
|
geom.setAttribute('normal', new THREE.BufferAttribute(normals.array, 3));
|
||||||
|
geom.setAttribute('uv', new THREE.BufferAttribute(uvs.array, 2));
|
||||||
|
colors && geom.setAttribute('color', new THREE.BufferAttribute(colors.array, 3));
|
||||||
|
if (grps.length) {
|
||||||
|
let index = []
|
||||||
|
let gbase = 0;
|
||||||
|
for (let gi = 0; gi < grps.length; gi++) {
|
||||||
|
geom.addGroup(gbase, grps[gi].length, gi)
|
||||||
|
gbase += grps[gi].length
|
||||||
|
index = index.concat(grps[gi]);
|
||||||
|
}
|
||||||
|
geom.setIndex(index)
|
||||||
|
}
|
||||||
|
g2 = geom;
|
||||||
|
}
|
||||||
|
|
||||||
|
let inv = new THREE.Matrix4().copy(toMatrix).invert();
|
||||||
|
geom.applyMatrix4(inv);
|
||||||
|
geom.computeBoundingSphere();
|
||||||
|
geom.computeBoundingBox();
|
||||||
|
let m = new THREE.Mesh(geom, toMaterial);
|
||||||
|
m.matrix.copy(toMatrix);
|
||||||
|
m.matrix.decompose(m.position, m.quaternion, m.scale)
|
||||||
|
m.rotation.setFromQuaternion(m.quaternion)
|
||||||
|
m.updateMatrixWorld();
|
||||||
|
m.castShadow = m.receiveShadow = true;
|
||||||
|
return m
|
||||||
|
}
|
||||||
|
|
||||||
|
export default CSG
|
Loading…
Reference in New Issue