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 { extrude } from './extrude'
|
||||
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 CSG from "./utils/three-csg.js"
|
||||
|
||||
const eq = (a1, a2) => {
|
||||
if (a1.length != a2.length) return false
|
||||
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);
|
||||
helpersGroup.add(axesHelper);
|
||||
|
||||
// console.log(color)
|
||||
const pxy = new THREE.Mesh(
|
||||
new THREE.PlaneGeometry(5, 5),
|
||||
new THREE.MeshBasicMaterial({
|
||||
color: 0xff0000,
|
||||
color: color.Plane,
|
||||
opacity: 0.2,
|
||||
side: THREE.DoubleSide,
|
||||
transparent: true,
|
||||
|
@ -77,16 +80,15 @@ export class Scene extends THREE.Scene {
|
|||
|
||||
|
||||
|
||||
const color = 0xFFFFFF;
|
||||
const intensity = 1;
|
||||
const light1 = new THREE.DirectionalLight(color, intensity);
|
||||
const light1 = new THREE.DirectionalLight(color.lighting, intensity);
|
||||
light1.position.set(10, 10, 10);
|
||||
this.add(light1);
|
||||
|
||||
const light2 = new THREE.DirectionalLight(color, intensity);
|
||||
const light2 = new THREE.DirectionalLight(color.lighting, intensity);
|
||||
light2.position.set(-10, -10, -5);
|
||||
this.add(light2);
|
||||
const ambient = new THREE.AmbientLight(color, intensity);
|
||||
const ambient = new THREE.AmbientLight(color.lighting, intensity);
|
||||
this.add(ambient);
|
||||
|
||||
|
||||
|
@ -198,4 +200,31 @@ async function addSketch() {
|
|||
}
|
||||
|
||||
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) {
|
||||
visited.add(node)
|
||||
let linkedObj = linkedObjs.get(node.l_id)
|
||||
let linkedObj = linkedObjs.get(node.userData.l_id)
|
||||
let arr;
|
||||
if (linkedObj[0] == 'line') {
|
||||
arr = children[objIdx.get(linkedObj[1][2])].geometry.attributes.position.array
|
||||
|
@ -40,7 +40,7 @@ export function extrude(sketch) {
|
|||
|
||||
|
||||
function findTouching(node) {
|
||||
for (let t of node.constraints) {
|
||||
for (let t of node.userData.constraints) {
|
||||
if (constraints.get(t)[0] != 'coincident') continue
|
||||
for (let c of constraints.get(t)[2]) {
|
||||
if (c == -1) continue;
|
||||
|
@ -64,11 +64,12 @@ export function extrude(sketch) {
|
|||
const extrudeSettings = { depth: 8, bevelEnabled: false };
|
||||
const geometry = new THREE.ExtrudeGeometry(shape, extrudeSettings);
|
||||
const phong = new THREE.MeshPhongMaterial({
|
||||
color: color.extrude,
|
||||
color: color.Extrude,
|
||||
emissive: color.emissive,
|
||||
flatShading: true
|
||||
});
|
||||
const mesh = new THREE.Mesh(geometry, phong)
|
||||
mesh.name = "Extrude"
|
||||
|
||||
for (let i = 0; i < offSetPts.length; i += 2) {
|
||||
if (
|
||||
|
|
|
@ -172,7 +172,7 @@ class Sketcher extends THREE.Group {
|
|||
}
|
||||
|
||||
delete(obj) {
|
||||
let link = this.linkedObjs.get(obj.l_id)
|
||||
let link = this.linkedObjs.get(obj.userData.l_id)
|
||||
if (!link) return;
|
||||
link = link[1]
|
||||
|
||||
|
@ -183,14 +183,14 @@ class Sketcher extends THREE.Group {
|
|||
obj.geometry.dispose()
|
||||
obj.material.dispose()
|
||||
|
||||
for (let c_id of obj.constraints) {
|
||||
for (let c_id of obj.userData.constraints) {
|
||||
this.deleteConstraints(c_id)
|
||||
}
|
||||
}
|
||||
|
||||
this.children.splice(i, link.length)
|
||||
|
||||
this.linkedObjs.delete(obj.l_id)
|
||||
this.linkedObjs.delete(obj.userData.l_id)
|
||||
|
||||
return i
|
||||
}
|
||||
|
@ -201,7 +201,8 @@ class Sketcher extends THREE.Group {
|
|||
if (idx == -1) continue
|
||||
const ob = this.children[this.objIdx.get(idx)]
|
||||
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)
|
||||
|
|
|
@ -12,9 +12,8 @@ export function addDimension(ent1, ent2, distance) {
|
|||
]
|
||||
)
|
||||
|
||||
ent1.constraints.add(this.c_id)
|
||||
ent2.constraints.add(this.c_id)
|
||||
|
||||
ent1.userData.constraints.push(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].constraints.add(this.c_id)
|
||||
toComb[i - 1].constraints.add(this.c_id)
|
||||
toComb[i].userData.constraints.push(this.c_id)
|
||||
toComb[i - 1].userData.constraints.push(this.c_id)
|
||||
}
|
||||
|
||||
this.updateOtherBuffers()
|
||||
|
|
|
@ -18,7 +18,7 @@ export function drawOnClick1(e) {
|
|||
|
||||
this.linkedObjs.set(this.l_id, [this.mode, this.toPush.map(e=>e.id)])
|
||||
for (let obj of this.toPush) {
|
||||
obj.l_id = this.l_id
|
||||
obj.userData.l_id = this.l_id
|
||||
}
|
||||
this.l_id += 1
|
||||
|
||||
|
|
|
@ -10,16 +10,18 @@ export function sketchArc(mouseLoc) {
|
|||
|
||||
const p1 = ptObj(mouseLoc)
|
||||
p1.matrixAutoUpdate = false;
|
||||
p1.constraints = new Set()
|
||||
p1.userData.constraints = []
|
||||
|
||||
const p2 = ptObj()
|
||||
p2.matrixAutoUpdate = false;
|
||||
p2.constraints = new Set()
|
||||
p2.userData.constraints = []
|
||||
|
||||
const arc = lineObj(n)
|
||||
arc.frustumCulled = false;
|
||||
|
||||
const p3 = ptObj()
|
||||
p3.matrixAutoUpdate = false;
|
||||
p3.userData.constraints = []
|
||||
|
||||
return [p1, p2, p3, arc]
|
||||
}
|
||||
|
|
|
@ -6,16 +6,16 @@ export function sketchLine(mouseLoc) {
|
|||
const p1 = ptObj()
|
||||
|
||||
p1.matrixAutoUpdate = false;
|
||||
p1.constraints = new Set()
|
||||
p1.userData.constraints = []
|
||||
|
||||
const p2 = ptObj()
|
||||
p2.matrixAutoUpdate = false;
|
||||
p2.constraints = new Set()
|
||||
p2.userData.constraints = []
|
||||
|
||||
const line = lineObj()
|
||||
line.matrixAutoUpdate = false;
|
||||
line.frustumCulled = false;
|
||||
line.constraints = new Set()
|
||||
line.userData.constraints = []
|
||||
|
||||
|
||||
line.geometry.attributes.position.set(mouseLoc)
|
||||
|
@ -30,8 +30,9 @@ export function sketchLine(mouseLoc) {
|
|||
]
|
||||
)
|
||||
|
||||
p1.constraints.add(this.c_id)
|
||||
this.children[this.children.length - 2].constraints.add(this.c_id)
|
||||
p1.userData.constraints.push(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 = []
|
||||
if (hoverPts.length) {
|
||||
// console.log(hoverPts)
|
||||
let minDist = Infinity;
|
||||
for (let i = 0; i < hoverPts.length; i++) {
|
||||
if (!hoverPts[i].distanceToRay) continue;
|
||||
|
@ -40,7 +39,8 @@ export function onHover(e) {
|
|||
|
||||
const obj = this.hovered[this.hovered.length - 1]
|
||||
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 = []
|
||||
|
||||
|
@ -58,7 +58,7 @@ export function onHover(e) {
|
|||
|
||||
const obj = this.hovered[this.hovered.length - 1]
|
||||
if (obj && !this.selected.includes(obj)) {
|
||||
obj.material.color.set(0x555555)
|
||||
obj.material.color.set(color[obj.name])
|
||||
}
|
||||
this.hovered = []
|
||||
|
||||
|
@ -82,7 +82,8 @@ export function onPick(e) {
|
|||
}
|
||||
} else {
|
||||
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.selected = []
|
||||
|
|
|
@ -8,43 +8,52 @@ const _vec3 = new THREE.Vector3()
|
|||
|
||||
const raycaster = new THREE.Raycaster();
|
||||
raycaster.params.Line.threshold = 0.8;
|
||||
raycaster.params.Points.threshold = 1.5;
|
||||
raycaster.params.Points.threshold = 0.6;
|
||||
|
||||
|
||||
const color = {
|
||||
dark1: 0x555555,
|
||||
hover: 0x00ff00,
|
||||
extrude: 0x156289,
|
||||
emissive: 0x072534
|
||||
lighting: 0xFFFFFF,
|
||||
emissive: 0x072534,
|
||||
Plane: 0xf5bc42,
|
||||
Line: 0x555555,
|
||||
Points: 0x555555,
|
||||
Extrude: 0x156289,
|
||||
}
|
||||
|
||||
const lineMaterial = new THREE.LineBasicMaterial({
|
||||
linewidth: 2,
|
||||
color: color.dark1,
|
||||
color: color.Line,
|
||||
})
|
||||
|
||||
|
||||
const pointMaterial = new THREE.PointsMaterial({
|
||||
color: color.dark1,
|
||||
color: color.Points,
|
||||
size: 4,
|
||||
})
|
||||
|
||||
|
||||
|
||||
|
||||
const ptObj = (n) => new THREE.Points(
|
||||
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) => new THREE.Line(
|
||||
const lineObj = (n = 1) => {
|
||||
const ret = new THREE.Line(
|
||||
new THREE.BufferGeometry().setAttribute('position',
|
||||
new THREE.Float32BufferAttribute(3 * (n+1), 3)
|
||||
new THREE.Float32BufferAttribute(3 * (n + 1), 3)
|
||||
),
|
||||
lineMaterial.clone()
|
||||
);
|
||||
);
|
||||
ret.name = 'Line'
|
||||
return ret
|
||||
}
|
||||
|
||||
|
||||
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