src/lib/descriptors/Object/Object3DDescriptor.js
import * as THREE from 'three';
import invariant from 'fbjs/lib/invariant';
import warning from 'fbjs/lib/warning';
import PropTypes from 'react/lib/ReactPropTypes';
import THREEElementDescriptor from '../THREEElementDescriptor';
import propTypeInstanceOf from '../../utils/propTypeInstanceOf';
function _arrayMove(array, oldIndex, newIndex) {
array.splice(newIndex, 0, array.splice(oldIndex, 1)[0]);
}
function validateMatrixProp(nextProps, threeObject) {
if (nextProps && nextProps.hasOwnProperty('matrix')) {
[
'position',
'rotation',
'quaternion',
'scale',
'lookAt',
].find((unwantedProp) => {
if (nextProps.hasOwnProperty(unwantedProp)) {
warning(false, `The ${threeObject.type} should not have a` +
` '${unwantedProp}' property if it has a 'matrix' property.`);
return true;
}
return false;
});
}
}
class Object3DDescriptor extends THREEElementDescriptor {
constructor(react3Instance) {
super(react3Instance);
this.hasName();
function copyUpdate(propName) {
return (threeObject, value) => {
threeObject[propName].copy(value);
};
}
this.hasProp('position', {
type: propTypeInstanceOf(THREE.Vector3),
update(threeObject, position) {
threeObject.position.copy(position);
if (threeObject.userData._lookAt) {
threeObject.lookAt(threeObject.userData._lookAt);
}
},
default: new THREE.Vector3(),
});
this.hasProp('rotation', {
type: propTypeInstanceOf(THREE.Euler),
update(threeObject, rotation) {
threeObject.rotation.copy(rotation);
},
default: new THREE.Euler(),
});
this.hasProp('quaternion', {
type: propTypeInstanceOf(THREE.Quaternion),
update: copyUpdate('quaternion'),
default: new THREE.Quaternion(),
});
this.hasProp('scale', {
type: propTypeInstanceOf(THREE.Vector3),
update: copyUpdate('scale'),
default: new THREE.Vector3(1, 1, 1),
});
this.hasProp('lookAt', {
type: propTypeInstanceOf(THREE.Vector3),
update(threeObject, lookAt) {
threeObject.userData._lookAt = lookAt;
if (lookAt) {
threeObject.lookAt(lookAt);
}
},
default: undefined,
});
this.hasProp('matrix', {
type: propTypeInstanceOf(THREE.Matrix4),
update(threeObject, matrix) {
threeObject.matrix.copy(matrix);
threeObject.matrix.decompose(
threeObject.position,
threeObject.quaternion,
threeObject.scale);
},
default: new THREE.Matrix4(),
});
[
'frustumCulled',
'visible',
].forEach((propName) => {
this.hasProp(propName, {
type: PropTypes.bool,
simple: true,
default: true,
});
});
this.hasProp('renderOrder', {
type: PropTypes.number,
simple: true,
});
this.hasProp('castShadow', {
type: PropTypes.bool,
simple: true,
default: false,
});
this.hasProp('receiveShadow', {
type: PropTypes.bool,
updateInitial: true,
update(threeObject, receiveShadow) {
threeObject.receiveShadow = receiveShadow;
if (threeObject.material) {
threeObject.material.needsUpdate = true;
}
},
default: false,
});
}
beginPropertyUpdates(threeObject, nextProps) {
if (process.env.NODE_ENV !== 'production') {
validateMatrixProp(nextProps, threeObject);
}
}
construct() {
return new THREE.Object3D();
}
applyInitialProps(threeObject, props) {
super.applyInitialProps(threeObject, props);
if (process.env.NODE_ENV !== 'production') {
validateMatrixProp(props, threeObject);
}
if (props.matrix) {
threeObject.matrix.copy(props.matrix);
} else {
if (props.position) {
threeObject.position.copy(props.position);
}
if (props.scale) {
threeObject.scale.copy(props.scale);
}
if (props.rotation) {
threeObject.rotation.copy(props.rotation);
}
if (props.quaternion) {
threeObject.quaternion.copy(props.quaternion);
}
if (props.lookAt) {
threeObject.lookAt(props.lookAt);
}
}
if (props.lookAt) {
threeObject.userData._lookAt = props.lookAt;
}
}
/**
* @param threeObject
* @param {Array} children
*/
addChildren(threeObject, children) {
children.forEach((child) => {
threeObject.add(child);
});
}
addChild(threeObject, child, mountIndex) {
threeObject.add(child);
this.moveChild(threeObject, child, mountIndex,
threeObject.children.length - 1);
}
/**
* @param {THREE.Object3D} threeObject
* @param child
*/
removeChild(threeObject, child) {
threeObject.remove(child);
}
moveChild(threeObject, childObject, toIndex, lastIndex) { // eslint-disable-line no-unused-vars
if (process.env.NODE_ENV !== 'production') {
invariant(toIndex >= 0 && threeObject.children.length > toIndex,
'Cannot move a child to that index');
}
_arrayMove(threeObject.children, threeObject.children.indexOf(childObject), toIndex);
}
highlight(threeObject) {
threeObject.userData.events.emit('highlight', {
uuid: threeObject.uuid,
boundingBoxFunc: () => this.getBoundingBoxes(threeObject),
});
}
getBoundingBoxes(threeObject) {
const boundingBox = new THREE.Box3();
boundingBox.setFromObject(threeObject);
return [boundingBox];
}
hideHighlight(threeObject) {
threeObject.userData.events.emit('hideHighlight');
}
}
module.exports = Object3DDescriptor;