From 6a54f540eb3f9c9c6dfe89df6012cb49a90df660 Mon Sep 17 00:00:00 2001 From: casperlamboo Date: Wed, 22 Nov 2017 12:25:48 +0100 Subject: [PATCH] fix 3d holes --- src/d3/ShapeMesh.js | 103 ++++++++++++++++-------------------- src/d3/ShapesManager.js | 76 ++++++++++++++++---------- src/shape/shapeDataUtils.js | 15 ++++++ 3 files changed, 110 insertions(+), 84 deletions(-) diff --git a/src/d3/ShapeMesh.js b/src/d3/ShapeMesh.js index f098606..91cee54 100644 --- a/src/d3/ShapeMesh.js +++ b/src/d3/ShapeMesh.js @@ -16,11 +16,11 @@ const MAX_HEIGHT_BASE = 5; const isValidNumber = (num) => typeof num === 'number' && !isNaN(num); class ShapeMesh extends THREE.Object3D { - constructor(shapeData, holes, toonShader, active) { + constructor(shapeData, active, toonShader) { super(); this.name = shapeData.UID; - const { sculpt, rotate, twist, height, type, transform, z, color, fill } = shapeData; + const { sculpt, rotate, twist, height, type, transform, z, color, fill, solid } = shapeData; let material; if (toonShader) { @@ -40,10 +40,6 @@ class ShapeMesh extends THREE.Object3D { this._mesh.name = shapeData.UID; this.add(this._mesh); - this._holeMesh = new THREE.Mesh(new THREE.Geometry, material); - this._holeMesh.name = shapeData.UID; - this.add(this._holeMesh); - this._toonShader = toonShader; this._shapes = []; @@ -61,10 +57,13 @@ class ShapeMesh extends THREE.Object3D { this._shapeData = shapeData; this._color = color; this._fill = fill; - this.updatePoints(shapeData); - this._checkHoles(holes, active); + this._holeMesh = new THREE.Mesh(new THREE.Geometry().fromBufferGeometry(this._mesh.geometry), material); + this._holeMesh.name = shapeData.UID; + this.add(this._holeMesh); + + this.updateSolid(solid, active); } add(object) { @@ -74,52 +73,29 @@ class ShapeMesh extends THREE.Object3D { if (this.children.includes(object)) super.remove(object); } - _checkHoles(holes, active) { - let changed = false; + updateHoleGeometry(holes) { + if (!this._solid || !this._fill) return false; + if (holes === this._holes && !this._changedGeometry) return false; - const visible = this._shapeData.solid || active; - if (visible !== this.visible) { - this.visible = visible; - changed = true; + this._holeMesh.geometry.dispose(); + + if (holes === null) { + this._holeMesh.geometry = new THREE.Geometry().fromBufferGeometry(this._mesh.geometry); + return true; } - if (active) { - this.remove(this._holeMesh); - this.add(this._mesh); - } - - holes = holes.map(hole => hole.mesh._mesh); - const objectBoundingBox = new THREE.Box3().setFromObject(this._mesh); - holes = holes.filter(hole => { - const holeBoundingBox = new THREE.Box3().setFromObject(hole); - return holeBoundingBox.intersectsBox(objectBoundingBox); - }); - - if (holes.length === 0) { - this.remove(this._holeMesh); - this.add(this._mesh); - return changed; - } - // is coliding with holes - const objectGeometry = new THREE.Geometry().fromBufferGeometry(this._mesh.geometry); let objectBSP = new THREE_BSP(objectGeometry); objectGeometry.dispose(); - for (const hole of holes) { - const holeGeometry = new THREE.Geometry().fromBufferGeometry(hole.geometry); - const holeBSP = new THREE_BSP(holeGeometry); - holeGeometry.dispose(); - objectBSP = objectBSP.subtract(holeBSP) - } - this._holeMesh.geometry.dispose(); + objectBSP = objectBSP.subtract(holes); this._holeMesh.geometry = objectBSP.toMesh().geometry; - this.add(this._holeMesh); - this.remove(this._mesh); + this._holes = holes; + this._changedGeometry = false; return true; } - update(shapeData, holes, active) { + update(shapeData, active) { let changed = false; if (shapeChanged(this._shapeData, shapeData)) { @@ -152,28 +128,20 @@ class ShapeMesh extends THREE.Object3D { changed = true; } - if (this._checkHoles(holes, active)) { + let solidChanged = false; + if (shapeData.solid !== this._solid || active !== this._active) { + this.updateSolid(shapeData.solid, active); changed = true; + solidChanged = true; } this._shapeData = shapeData; return changed; } - setOpaque(opaque) { - let opacity; - if (this._shapeData.solid) { - opacity = opaque ? 1.0 : DESELECT_TRANSPARENCY; - } else { - opacity = 0.0; - } - this._mesh.material.opacity = opacity; - this._mesh.material.transparent = opacity !== 1; - } - dispose() { this._mesh.geometry.dispose(); - this._meshHole.geometry.dispose(); + this._holeMesh.geometry.dispose(); } updatePoints(shapeData) { @@ -252,6 +220,23 @@ class ShapeMesh extends THREE.Object3D { this._color = color; } + updateSolid(solid, active) { + this._mesh.material.opacity = solid ? 1.0 : 0.0; + this._mesh.material.transparent = !solid; + this.visible = solid || active; + + if (active || !solid) { + this.add(this._mesh); + this.remove(this._holeMesh); + } else { + this.add(this._holeMesh); + this.remove(this._mesh); + } + + this._solid = solid; + this._active = active; + } + _getPoint(point, heightStep, center) { const { scale, pos: y } = this._heightSteps[heightStep]; @@ -326,6 +311,8 @@ class ShapeMesh extends THREE.Object3D { this._mesh.geometry.boundingSphere = null; this._mesh.geometry.computeFaceNormals(); this._mesh.geometry.computeVertexNormals(); + + this._changedGeometry = true; } _updateSide() { // TODO use higher precision for export mesh @@ -362,6 +349,8 @@ class ShapeMesh extends THREE.Object3D { this._heightSteps = heightSteps; if (heightStepsChanged) this._updateFaces(); + + this._changedGeometry = true; } _updateFaces() { // TODO @@ -483,6 +472,8 @@ class ShapeMesh extends THREE.Object3D { this._vertices = new Float32Array(vertexBufferLength); this._vertexBuffer = new THREE.BufferAttribute(this._vertices, 3); this._mesh.geometry.addAttribute('position', this._vertexBuffer); + + this._changedGeometry = true; } } diff --git a/src/d3/ShapesManager.js b/src/d3/ShapesManager.js index a7e3eaa..20e30e0 100644 --- a/src/d3/ShapesManager.js +++ b/src/d3/ShapesManager.js @@ -1,7 +1,10 @@ -import { determineActiveShape } from '../shape/shapeDataUtils.js'; +import { determineActiveShape3d } from '../shape/shapeDataUtils.js'; import { SHAPE_TYPE_PROPERTIES } from '../constants/shapeTypeProperties.js'; import * as THREE from 'three'; import ShapeMesh from './ShapeMesh.js'; +import ThreeBSP from 'three-js-csg'; + +const THREE_BSP = ThreeBSP(THREE); export default class ShapesManager extends THREE.Object3D { constructor({ toonShader }) { @@ -12,6 +15,9 @@ export default class ShapesManager extends THREE.Object3D { this._meshes = {}; this._spaces = {}; this.name = 'shapes-manager'; + + this._holes = new THREE_BSP(new THREE.Geometry()); + // this._edges = {}; } @@ -36,27 +42,22 @@ export default class ShapesManager extends THREE.Object3D { } } + let holesChanged = false; + // Remove removed shapes if (this._state) { for (const id in this._state.objectsById) { if (!state.objectsById[id]) { + if (!this._meshes[id].mesh._shapeData.solid) holesChanged = true; this._handleShapeRemove(id); render = true; } } } - const activeShapes = determineActiveShape(state); + const ids = Object.keys(state.objectsById); + const activeShapes = determineActiveShape3d(state); - const ids = Object.keys(state.objectsById).sort((a, b) => { - const solidA = state.objectsById[a].solid; - const solidB = state.objectsById[b].solid; - - if (solidA === solidB) return 0; - return solidA ? 1 : -1; - }); - - const holes = []; for (let i = 0; i < ids.length; i ++) { const id = ids[i]; const newShapeData = state.objectsById[id]; @@ -65,33 +66,52 @@ export default class ShapesManager extends THREE.Object3D { if (!SHAPE_TYPE_PROPERTIES[newShapeData.type].D3Visible) continue; // add new shapes if (!this._state || !this._state.objectsById[id]) { - this._handleShapeAdded(newShapeData, holes, active); + this._handleShapeAdded(newShapeData, active); render = true; + if (!newShapeData.solid) holesChanged = true; } else { const { mesh } = this._meshes[id]; - if (mesh.update(newShapeData, holes, active)) { + if (mesh.update(newShapeData, active)) { + render = true; + if (!newShapeData.solid || !this._state.objectsById[id].solid) holesChanged = true; + } + } + } + + if (holesChanged) { + this._holes = null; + for (let i = 0; i < ids.length; i ++) { + const id = ids[i]; + if (!state.objectsById[id].solid) { + const hole = this._meshes[id].mesh._mesh; + const holeGeometry = new THREE.Geometry().fromBufferGeometry(hole.geometry); + const holeBSP = new THREE_BSP(holeGeometry); + if (!this._holes) { + this._holes = holeBSP; + } else { + this._holes = this._holes.union(holeBSP); + } + holeGeometry.dispose(); + } + } + } + + for (let i = 0; i < ids.length; i ++) { + const id = ids[i]; + const active = activeShapes[id]; + if (!active && state.objectsById[id].solid) { + const shape = this._meshes[id].mesh; + if (shape.updateHoleGeometry(this._holes)) { render = true; } } - if (!newShapeData.solid && !active) holes.push(this._meshes[id]); } - this._state = state; + this._state = state; return render; } updateTransparent(selectedUIDs) { - for (const UID in this._meshes) { - const { mesh } = this._meshes[UID]; - const selected = selectedUIDs.indexOf(UID) !== -1; - const opaque = selected || selectedUIDs.length === 0; - - mesh.setOpaque(opaque); - } - } - - getMesh(id) { - return this._meshes[id].mesh; } _handleShapeRemove(id) { @@ -103,10 +123,10 @@ export default class ShapesManager extends THREE.Object3D { this._spaces[space].remove(mesh); } - _handleShapeAdded(shapeData, holes) { + _handleShapeAdded(shapeData, active) { if (!SHAPE_TYPE_PROPERTIES[shapeData.type].D3Visible) return; const { space } = shapeData; - const mesh = new ShapeMesh(shapeData, holes, this._toonShader); + const mesh = new ShapeMesh(shapeData, active, this._toonShader); this._meshes[shapeData.UID] = { mesh, space }; this._spaces[space].add(mesh); diff --git a/src/shape/shapeDataUtils.js b/src/shape/shapeDataUtils.js index eb113af..1c1cd32 100644 --- a/src/shape/shapeDataUtils.js +++ b/src/shape/shapeDataUtils.js @@ -93,3 +93,18 @@ export const determineActiveShape = (state) => { } return activeShapes; }; + +export const determineActiveShape3d = (state) => { + const activeTransformer = state.d2.eraser.active || + (state.d2.transform.active && state.d2.transform.handle !== 'dragselect') || + state.d3.height.active || + state.d3.sculpt.activeHandle !== null || + state.d3.twist.active; + + const selectedObjects = state.selection.objects.map(({ id }) => id); + const activeShapes = {}; + for (const id in state.objectsById) { + activeShapes[id] = activeTransformer; + } + return activeShapes; +};