add csg and change to userData

master
howard 2021-03-31 01:07:34 -07:00
parent 470d96d961
commit f3de6bff7d
11 changed files with 776 additions and 50 deletions

View File

@ -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)

View File

@ -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 (

View File

@ -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)

View File

@ -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()

View File

@ -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

View File

@ -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]
} }

View File

@ -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)
} }

483
src/utils/csg-lib.js Normal file
View File

@ -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 |
// | |
// +-------+
//

View File

@ -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 = []

View File

@ -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 }

200
src/utils/three-csg.js Normal file
View File

@ -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