Skip to content

Commit

Permalink
Refactor: Refactor compute model bounding box (#663)
Browse files Browse the repository at this point in the history
Fix: Fix svg flip bug
Fix: Fix mesh material bug
  • Loading branch information
meiliang1123 authored Dec 29, 2020
1 parent 953baba commit 28f46c6
Show file tree
Hide file tree
Showing 6 changed files with 93 additions and 173 deletions.
89 changes: 7 additions & 82 deletions src/app/components/SMCanvas/TransformControls.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@ import {
DoubleSide,
Float32BufferAttribute,
Vector3,
Euler,
Box3,
Quaternion,
Matrix4,
Group,
Expand All @@ -22,6 +20,8 @@ import {
} from 'three';
// import * as THREE from 'three';

import ThreeUtils from '../three-extensions/ThreeUtils';

import { ArcBufferGeometry } from './ArcGeometry';

const EVENTS = {
Expand Down Expand Up @@ -438,49 +438,12 @@ class TransformControls extends Object3D {
this.camera.updateMatrixWorld();
this.camera.matrixWorld.decompose(cameraPosition, cameraQuaternion, cameraScale);
const multiObjectPosition = this.object.position;
const maxObjectBoundingBox = new Vector3(-Number.MAX_VALUE, -Number.MAX_VALUE, -Number.MAX_VALUE);
const minObjectBoundingBox = new Vector3(Number.MAX_VALUE, Number.MAX_VALUE, Number.MAX_VALUE);
const multiObjectWidth = new Vector3();

const objectPosition = new Vector3();
const objectScale = new Vector3();
const objectQuaternion = new Quaternion();

this.object.children.forEach((child, index) => {
const objectChildrenPosition = new Vector3();
const objectChildrenScale = new Vector3();
const objectChildrenQuaternion = new Quaternion();
// object

child.matrixWorld.decompose(objectChildrenPosition, objectChildrenQuaternion, objectChildrenScale);

if (this.object.shouldUpdateBoundingbox) {
if (!this.object.boundingBox[index]) {
this.updateBoundingBox();
}
// Calculate selectedGroup's BBox in Canvas
const boundingBox = this.object.boundingBox[index];
if (boundingBox && boundingBox.min && boundingBox.max) {
if (this.object.children.length === 1) {
minObjectBoundingBox.x = Math.min(boundingBox.min.x, minObjectBoundingBox.x);
minObjectBoundingBox.y = Math.min(boundingBox.min.y, minObjectBoundingBox.y);
minObjectBoundingBox.z = Math.min(boundingBox.min.z, minObjectBoundingBox.z);

maxObjectBoundingBox.x = Math.max(boundingBox.max.x, maxObjectBoundingBox.x);
maxObjectBoundingBox.y = Math.max(boundingBox.max.y, maxObjectBoundingBox.y);
maxObjectBoundingBox.z = Math.max(boundingBox.max.z, maxObjectBoundingBox.z);
} else {
minObjectBoundingBox.x = Math.min(boundingBox.min.x + objectChildrenPosition.x - objectPosition.x, minObjectBoundingBox.x);
minObjectBoundingBox.y = Math.min(boundingBox.min.y + objectChildrenPosition.y - objectPosition.y, minObjectBoundingBox.y);
minObjectBoundingBox.z = Math.min(0, minObjectBoundingBox.z);

maxObjectBoundingBox.x = Math.max(boundingBox.max.x + objectChildrenPosition.x - objectPosition.x, maxObjectBoundingBox.x);
maxObjectBoundingBox.y = Math.max(boundingBox.max.y + objectChildrenPosition.y - objectPosition.y, maxObjectBoundingBox.y);
maxObjectBoundingBox.z = Math.max(boundingBox.max.z - boundingBox.min.z, maxObjectBoundingBox.z);
}
}
}

this.object.children.forEach((child) => {
// Update peripherals
this.translatePeripheral.visible = (this.mode === 'translate' && child.visible);
this.rotatePeripheral.visible = (this.mode === 'rotate' && child.visible);
Expand All @@ -502,6 +465,10 @@ class TransformControls extends Object3D {
const unitZ = new Vector3(0, 0, 1);

if (this.object.shouldUpdateBoundingbox) {
const boundingBox = ThreeUtils.computeBoundingBox(this.object);
const maxObjectBoundingBox = boundingBox.max;
const minObjectBoundingBox = boundingBox.min;
const multiObjectWidth = new Vector3();
multiObjectWidth.x = (maxObjectBoundingBox.x - minObjectBoundingBox.x);
multiObjectWidth.y = (maxObjectBoundingBox.y - minObjectBoundingBox.y);
multiObjectWidth.z = (maxObjectBoundingBox.z - minObjectBoundingBox.z);
Expand Down Expand Up @@ -863,48 +830,6 @@ class TransformControls extends Object3D {

// Calculate the bbox of each model in the selectedGroup
updateBoundingBox() {
this.object.updateMatrixWorld();
this.object.children.forEach((child, index) => {
// cannot get 'child.geometry.boundingBox', have to use 'child.geometry'
if (child.geometry) {
const clone = child.geometry.clone();
if (this.object.children.length === 1) {
clone.applyMatrix(child.matrixWorld);
clone.computeBoundingBox();
this.object.boundingBox[index] = clone.boundingBox;
} else {
const parentClone = this.object.clone();
const newRotation = new Euler(
child.rotation.x + this.object.rotation.x,
child.rotation.y + this.object.rotation.y,
child.rotation.z + this.object.rotation.z,
);
const newScale = new Vector3(
child.scale.x * this.object.scale.x,
child.scale.y * this.object.scale.y,
child.scale.z * this.object.scale.z,
);

parentClone.rotation.copy(newRotation);
parentClone.scale.copy(newScale);
parentClone.updateMatrix();
clone.applyMatrix(parentClone.matrix);
clone.computeBoundingBox();
const minBoundingBox = clone.boundingBox.min;
const maxBoundingBox = clone.boundingBox.max;
const centerPosition = new Vector3(
(minBoundingBox.x + maxBoundingBox.x) / 2,
(minBoundingBox.y + maxBoundingBox.y) / 2,
(minBoundingBox.z + maxBoundingBox.z) / 2,
);
const newBoundingBox = new Box3(
new Vector3(minBoundingBox.x - centerPosition.x, minBoundingBox.y - centerPosition.y, minBoundingBox.z - centerPosition.z),
new Vector3(maxBoundingBox.x - centerPosition.x, maxBoundingBox.y - centerPosition.y, maxBoundingBox.z - centerPosition.z)
);
this.object.boundingBox[index] = newBoundingBox;
}
}
});
this.object.shouldUpdateBoundingbox = true;
}
}
Expand Down
103 changes: 72 additions & 31 deletions src/app/components/three-extensions/ThreeUtils.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,18 +22,18 @@ const ThreeUtils = {
},

// get matrix for rotating v2 to v1. Applying matrix to v2 can make v2 to parallels v1.
getRotateMatrixBetweenVector3: function (v1, v2){
getRotateMatrixBetweenVector3: function (v1, v2) {
const quaternion = ThreeUtils.getQuaternionBetweenVector3(v1, v2);
const matrix4 = new THREE.Matrix4().makeRotationFromQuaternion(quaternion);
return matrix4;
},

getMouseXY: function(event, domElement) {
var rect = domElement.getBoundingClientRect();
getMouseXY: function (event, domElement) {
const rect = domElement.getBoundingClientRect();
return new THREE.Vector2(
((event.clientX - rect.left) / rect.width) * 2 - 1,
-((event.clientY - rect.top) / rect.height) * 2 + 1
)
);
},

// get world info
Expand All @@ -45,7 +45,7 @@ const ThreeUtils = {

getObjectWorldQuaternion: function (object) {
const result = new THREE.Quaternion();
object.getWorldQuaternion (result);
object.getWorldQuaternion(result);
return result;
},

Expand All @@ -56,26 +56,26 @@ const ThreeUtils = {
},

getEventWorldPosition: function (event, domElement, camera) {
var rect = domElement.getBoundingClientRect();
var tempVector3 = new THREE.Vector3();
const rect = domElement.getBoundingClientRect();
const tempVector3 = new THREE.Vector3();

// the x&y in standard thereejs coordinate
// standardX, standardY: [-1, 1]
const standardX = ((event.clientX - rect.left) / rect.width ) * 2 - 1;
const standardY = -((event.clientY - rect.top) / rect.height ) * 2 + 1;
const standardX = ((event.clientX - rect.left) / rect.width) * 2 - 1;
const standardY = -((event.clientY - rect.top) / rect.height) * 2 + 1;

tempVector3.set(standardX, standardY, 0.5);

tempVector3.unproject(camera );
tempVector3.sub(camera.position ).normalize();
var distance = -camera.position.z / tempVector3.z;
tempVector3.unproject(camera);
tempVector3.sub(camera.position).normalize();
const distance = -camera.position.z / tempVector3.z;

var result = new THREE.Vector3().copy(camera.position).add(tempVector3.multiplyScalar(distance));
const result = new THREE.Vector3().copy(camera.position).add(tempVector3.multiplyScalar(distance));
return result;
},

// set world transformation
setObjectWorldPosition: function(object, position) {
setObjectWorldPosition: function (object, position) {
const parent = object.parent;
parent.updateMatrixWorld();
const matrix = new THREE.Matrix4().getInverse(parent.matrixWorld);
Expand All @@ -84,7 +84,7 @@ const ThreeUtils = {
},

setObjectWorldScale: function (object, scale) {
var localScale = object.parent.worldToLocal(scale);
const localScale = object.parent.worldToLocal(scale);
object.scale.copy(localScale);
},

Expand All @@ -105,7 +105,7 @@ const ThreeUtils = {
const scaleY = targetSize.y / originSize2D.y;

const worldScale = new THREE.Vector3(scaleX, scaleY, 1);
ThreeUtils.setObjectWorldScale(object, worldScale );
ThreeUtils.setObjectWorldScale(object, worldScale);

const deltaX = (scaleX - originScale.x) * originSize2D.x;
const deltaY = (scaleY - originScale.y) * originSize2D.y;
Expand Down Expand Up @@ -144,13 +144,13 @@ const ThreeUtils = {
box.max.y - box.min.y,
box.max.z - box.min.z
);
if (is2D){
if (is2D) {
return new THREE.Vector2(size.x, size.y);
} else {
return size;
}
},

generateSupportBoxGeometry(width, height, topZ, bottomZ = 0) {
const depth = topZ - bottomZ;
const box = new THREE.BoxBufferGeometry(width, height, depth).toNonIndexed();
Expand All @@ -160,39 +160,80 @@ const ThreeUtils = {

removeObjectParent(obj) {
const parent = obj.parent;
if(!parent) return () =>{};
if (!parent) return () => {};

parent.updateMatrixWorld();
parent.remove(obj);
obj.applyMatrix(parent.matrixWorld);
return () => this.setObjectParent(obj, parent);
},

setObjectParent(obj, parent){
if(!parent) return;
this.removeObjectParent(obj)
setObjectParent(obj, parent) {
if (!parent) return;

this.removeObjectParent(obj);
parent.updateMatrixWorld();
obj.applyMatrix(new THREE.Matrix4().getInverse(parent.matrixWorld))
parent.add(obj)
obj.applyMatrix(new THREE.Matrix4().getInverse(parent.matrixWorld));
parent.add(obj);
},
applyObjectMatrix(obj, matrix) {
const inverse = new THREE.Matrix4().getInverse(matrix);
obj.children.forEach(child=>{
obj.children.forEach(child => {
child.applyMatrix(inverse);
})
});
obj.applyMatrix(matrix);
},
liftObjectOnlyChildMatrix(obj) {
if(obj.children.length !== 1) return;
if (obj.children.length !== 1) return;

const child = obj.children[0];
const m = child.matrix;
obj.applyMatrix(m);
child.applyMatrix(new THREE.Matrix4().getInverse(m));
}
},
computeBoundingBox: (function () {
const caches = {};
const initialBox = new THREE.Box3(
new THREE.Vector3(Infinity, Infinity, Infinity),
new THREE.Vector3(-Infinity, -Infinity, -Infinity)
);
const tmpMatrix = new THREE.Matrix4();
return function computeBoundingBox(obj) {
let cache = caches[obj.uuid];
if (!cache) {
cache = {
geometry: obj.isMesh && obj.geometry.clone(),
lastMatrix: new THREE.Matrix4(),
lastBbox: new THREE.Box3().copy(initialBox)
};
caches[obj.uuid] = cache;
}
const { geometry, lastMatrix, lastBbox } = cache;

if (obj.isMesh) {
obj.updateMatrixWorld();
if (lastBbox.equals(initialBox) || !lastMatrix.equals(obj.matrixWorld)) {
geometry.applyMatrix(obj.matrixWorld);
if(!geometry.boundingBox) {
geometry.computeBoundingBox();
}
lastBbox.copy(geometry.boundingBox);
lastMatrix.copy(obj.matrixWorld);
geometry.applyMatrix(tmpMatrix.getInverse(obj.matrixWorld));
}
} else {
lastBbox.copy(initialBox);
}

for (const child of obj.children) {
const cBBox = ThreeUtils.computeBoundingBox(child);
lastBbox.expandByPoint(cBBox.min).expandByPoint(cBBox.max);
}

return lastBbox;
};
}())

};

export default ThreeUtils;

20 changes: 2 additions & 18 deletions src/app/models/Model.js
Original file line number Diff line number Diff line change
Expand Up @@ -462,23 +462,7 @@ class Model {

computeBoundingBox() {
if (this.sourceType === '3d') {
// after operated(move/scale/rotate), model.geometry is not changed
// so need to call: geometry.applyMatrix(matrixLocal);
// then call: geometry.computeBoundingBox(); to get operated modelMesh BoundingBox
// clone this.convexGeometry then clone.computeBoundingBox() is faster.
if (this.convexGeometry) {
const clone = this.convexGeometry.clone();
this.meshObject.updateMatrixWorld();
clone.applyMatrix(this.meshObject.matrixWorld);
clone.computeBoundingBox();
this.boundingBox = clone.boundingBox;
} else {
const clone = this.meshObject.geometry.clone();
this.meshObject.updateMatrixWorld();
clone.applyMatrix(this.meshObject.matrixWorld);
clone.computeBoundingBox();
this.boundingBox = clone.boundingBox;
}
this.boundingBox = ThreeUtils.computeBoundingBox(this.meshObject);
} else {
const { width, height, rotationZ, scaleX, scaleY } = this.transformation;
const bboxWidth = (Math.abs(width * Math.cos(rotationZ)) + Math.abs(height * Math.sin(rotationZ))) * scaleX;
Expand Down Expand Up @@ -878,7 +862,7 @@ class Model {
removeVertexColors() {
const bufferGeometry = this.meshObject.geometry;
bufferGeometry.removeAttribute('color');
this.setSelected(true);
this.setSelected();
this.modelGroup && this.modelGroup.modelChanged();
}

Expand Down
Loading

0 comments on commit 28f46c6

Please sign in to comment.