mirror of
https://github.com/Doodle3D/Doodle3D-Core.git
synced 2024-12-22 11:03:48 +01:00
fix 3d holes
This commit is contained in:
parent
7a93b3e5c8
commit
6a54f540eb
103
src/d3/ShapeMesh.js
vendored
103
src/d3/ShapeMesh.js
vendored
@ -16,11 +16,11 @@ const MAX_HEIGHT_BASE = 5;
|
|||||||
const isValidNumber = (num) => typeof num === 'number' && !isNaN(num);
|
const isValidNumber = (num) => typeof num === 'number' && !isNaN(num);
|
||||||
|
|
||||||
class ShapeMesh extends THREE.Object3D {
|
class ShapeMesh extends THREE.Object3D {
|
||||||
constructor(shapeData, holes, toonShader, active) {
|
constructor(shapeData, active, toonShader) {
|
||||||
super();
|
super();
|
||||||
this.name = shapeData.UID;
|
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;
|
let material;
|
||||||
if (toonShader) {
|
if (toonShader) {
|
||||||
@ -40,10 +40,6 @@ class ShapeMesh extends THREE.Object3D {
|
|||||||
this._mesh.name = shapeData.UID;
|
this._mesh.name = shapeData.UID;
|
||||||
this.add(this._mesh);
|
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._toonShader = toonShader;
|
||||||
|
|
||||||
this._shapes = [];
|
this._shapes = [];
|
||||||
@ -61,10 +57,13 @@ class ShapeMesh extends THREE.Object3D {
|
|||||||
this._shapeData = shapeData;
|
this._shapeData = shapeData;
|
||||||
this._color = color;
|
this._color = color;
|
||||||
this._fill = fill;
|
this._fill = fill;
|
||||||
|
|
||||||
this.updatePoints(shapeData);
|
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) {
|
add(object) {
|
||||||
@ -74,52 +73,29 @@ class ShapeMesh extends THREE.Object3D {
|
|||||||
if (this.children.includes(object)) super.remove(object);
|
if (this.children.includes(object)) super.remove(object);
|
||||||
}
|
}
|
||||||
|
|
||||||
_checkHoles(holes, active) {
|
updateHoleGeometry(holes) {
|
||||||
let changed = false;
|
if (!this._solid || !this._fill) return false;
|
||||||
|
if (holes === this._holes && !this._changedGeometry) return false;
|
||||||
|
|
||||||
const visible = this._shapeData.solid || active;
|
this._holeMesh.geometry.dispose();
|
||||||
if (visible !== this.visible) {
|
|
||||||
this.visible = visible;
|
if (holes === null) {
|
||||||
changed = true;
|
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);
|
const objectGeometry = new THREE.Geometry().fromBufferGeometry(this._mesh.geometry);
|
||||||
let objectBSP = new THREE_BSP(objectGeometry);
|
let objectBSP = new THREE_BSP(objectGeometry);
|
||||||
objectGeometry.dispose();
|
objectGeometry.dispose();
|
||||||
for (const hole of holes) {
|
objectBSP = objectBSP.subtract(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();
|
|
||||||
this._holeMesh.geometry = objectBSP.toMesh().geometry;
|
this._holeMesh.geometry = objectBSP.toMesh().geometry;
|
||||||
|
|
||||||
this.add(this._holeMesh);
|
this._holes = holes;
|
||||||
this.remove(this._mesh);
|
this._changedGeometry = false;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
update(shapeData, holes, active) {
|
update(shapeData, active) {
|
||||||
let changed = false;
|
let changed = false;
|
||||||
|
|
||||||
if (shapeChanged(this._shapeData, shapeData)) {
|
if (shapeChanged(this._shapeData, shapeData)) {
|
||||||
@ -152,28 +128,20 @@ class ShapeMesh extends THREE.Object3D {
|
|||||||
changed = true;
|
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;
|
changed = true;
|
||||||
|
solidChanged = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
this._shapeData = shapeData;
|
this._shapeData = shapeData;
|
||||||
return changed;
|
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() {
|
dispose() {
|
||||||
this._mesh.geometry.dispose();
|
this._mesh.geometry.dispose();
|
||||||
this._meshHole.geometry.dispose();
|
this._holeMesh.geometry.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
updatePoints(shapeData) {
|
updatePoints(shapeData) {
|
||||||
@ -252,6 +220,23 @@ class ShapeMesh extends THREE.Object3D {
|
|||||||
this._color = color;
|
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) {
|
_getPoint(point, heightStep, center) {
|
||||||
const { scale, pos: y } = this._heightSteps[heightStep];
|
const { scale, pos: y } = this._heightSteps[heightStep];
|
||||||
|
|
||||||
@ -326,6 +311,8 @@ class ShapeMesh extends THREE.Object3D {
|
|||||||
this._mesh.geometry.boundingSphere = null;
|
this._mesh.geometry.boundingSphere = null;
|
||||||
this._mesh.geometry.computeFaceNormals();
|
this._mesh.geometry.computeFaceNormals();
|
||||||
this._mesh.geometry.computeVertexNormals();
|
this._mesh.geometry.computeVertexNormals();
|
||||||
|
|
||||||
|
this._changedGeometry = true;
|
||||||
}
|
}
|
||||||
_updateSide() {
|
_updateSide() {
|
||||||
// TODO use higher precision for export mesh
|
// TODO use higher precision for export mesh
|
||||||
@ -362,6 +349,8 @@ class ShapeMesh extends THREE.Object3D {
|
|||||||
this._heightSteps = heightSteps;
|
this._heightSteps = heightSteps;
|
||||||
|
|
||||||
if (heightStepsChanged) this._updateFaces();
|
if (heightStepsChanged) this._updateFaces();
|
||||||
|
|
||||||
|
this._changedGeometry = true;
|
||||||
}
|
}
|
||||||
_updateFaces() {
|
_updateFaces() {
|
||||||
// TODO
|
// TODO
|
||||||
@ -483,6 +472,8 @@ class ShapeMesh extends THREE.Object3D {
|
|||||||
this._vertices = new Float32Array(vertexBufferLength);
|
this._vertices = new Float32Array(vertexBufferLength);
|
||||||
this._vertexBuffer = new THREE.BufferAttribute(this._vertices, 3);
|
this._vertexBuffer = new THREE.BufferAttribute(this._vertices, 3);
|
||||||
this._mesh.geometry.addAttribute('position', this._vertexBuffer);
|
this._mesh.geometry.addAttribute('position', this._vertexBuffer);
|
||||||
|
|
||||||
|
this._changedGeometry = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
76
src/d3/ShapesManager.js
vendored
76
src/d3/ShapesManager.js
vendored
@ -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 { SHAPE_TYPE_PROPERTIES } from '../constants/shapeTypeProperties.js';
|
||||||
import * as THREE from 'three';
|
import * as THREE from 'three';
|
||||||
import ShapeMesh from './ShapeMesh.js';
|
import ShapeMesh from './ShapeMesh.js';
|
||||||
|
import ThreeBSP from 'three-js-csg';
|
||||||
|
|
||||||
|
const THREE_BSP = ThreeBSP(THREE);
|
||||||
|
|
||||||
export default class ShapesManager extends THREE.Object3D {
|
export default class ShapesManager extends THREE.Object3D {
|
||||||
constructor({ toonShader }) {
|
constructor({ toonShader }) {
|
||||||
@ -12,6 +15,9 @@ export default class ShapesManager extends THREE.Object3D {
|
|||||||
this._meshes = {};
|
this._meshes = {};
|
||||||
this._spaces = {};
|
this._spaces = {};
|
||||||
this.name = 'shapes-manager';
|
this.name = 'shapes-manager';
|
||||||
|
|
||||||
|
this._holes = new THREE_BSP(new THREE.Geometry());
|
||||||
|
|
||||||
// this._edges = {};
|
// this._edges = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -36,27 +42,22 @@ export default class ShapesManager extends THREE.Object3D {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let holesChanged = false;
|
||||||
|
|
||||||
// Remove removed shapes
|
// Remove removed shapes
|
||||||
if (this._state) {
|
if (this._state) {
|
||||||
for (const id in this._state.objectsById) {
|
for (const id in this._state.objectsById) {
|
||||||
if (!state.objectsById[id]) {
|
if (!state.objectsById[id]) {
|
||||||
|
if (!this._meshes[id].mesh._shapeData.solid) holesChanged = true;
|
||||||
this._handleShapeRemove(id);
|
this._handleShapeRemove(id);
|
||||||
render = true;
|
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 ++) {
|
for (let i = 0; i < ids.length; i ++) {
|
||||||
const id = ids[i];
|
const id = ids[i];
|
||||||
const newShapeData = state.objectsById[id];
|
const newShapeData = state.objectsById[id];
|
||||||
@ -65,33 +66,52 @@ export default class ShapesManager extends THREE.Object3D {
|
|||||||
if (!SHAPE_TYPE_PROPERTIES[newShapeData.type].D3Visible) continue;
|
if (!SHAPE_TYPE_PROPERTIES[newShapeData.type].D3Visible) continue;
|
||||||
// add new shapes
|
// add new shapes
|
||||||
if (!this._state || !this._state.objectsById[id]) {
|
if (!this._state || !this._state.objectsById[id]) {
|
||||||
this._handleShapeAdded(newShapeData, holes, active);
|
this._handleShapeAdded(newShapeData, active);
|
||||||
render = true;
|
render = true;
|
||||||
|
if (!newShapeData.solid) holesChanged = true;
|
||||||
} else {
|
} else {
|
||||||
const { mesh } = this._meshes[id];
|
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;
|
render = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!newShapeData.solid && !active) holes.push(this._meshes[id]);
|
|
||||||
}
|
}
|
||||||
this._state = state;
|
|
||||||
|
|
||||||
|
this._state = state;
|
||||||
return render;
|
return render;
|
||||||
}
|
}
|
||||||
|
|
||||||
updateTransparent(selectedUIDs) {
|
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) {
|
_handleShapeRemove(id) {
|
||||||
@ -103,10 +123,10 @@ export default class ShapesManager extends THREE.Object3D {
|
|||||||
this._spaces[space].remove(mesh);
|
this._spaces[space].remove(mesh);
|
||||||
}
|
}
|
||||||
|
|
||||||
_handleShapeAdded(shapeData, holes) {
|
_handleShapeAdded(shapeData, active) {
|
||||||
if (!SHAPE_TYPE_PROPERTIES[shapeData.type].D3Visible) return;
|
if (!SHAPE_TYPE_PROPERTIES[shapeData.type].D3Visible) return;
|
||||||
const { space } = shapeData;
|
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._meshes[shapeData.UID] = { mesh, space };
|
||||||
|
|
||||||
this._spaces[space].add(mesh);
|
this._spaces[space].add(mesh);
|
||||||
|
@ -93,3 +93,18 @@ export const determineActiveShape = (state) => {
|
|||||||
}
|
}
|
||||||
return activeShapes;
|
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;
|
||||||
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user