/**
|
* @author arodic / https://github.com/arodic
|
*/
|
|
(function () {
|
|
'use strict';
|
|
var GizmoMaterial = function (parameters) {
|
|
THREE.MeshBasicMaterial.call(this);
|
|
this.depthTest = false;
|
this.depthWrite = false;
|
this.fog = false;
|
this.side = THREE.FrontSide;
|
this.transparent = true;
|
|
this.setValues(parameters);
|
|
this.oldColor = this.color.clone();
|
this.oldOpacity = this.opacity;
|
|
this.highlight = function (highlighted) {
|
|
if (highlighted) {
|
|
this.color.setRGB(1, 1, 0);
|
this.opacity = 1;
|
|
} else {
|
|
this.color.copy(this.oldColor);
|
this.opacity = this.oldOpacity;
|
|
}
|
|
};
|
|
};
|
|
GizmoMaterial.prototype = Object.create(THREE.MeshBasicMaterial.prototype);
|
GizmoMaterial.prototype.constructor = GizmoMaterial;
|
|
|
var GizmoLineMaterial = function (parameters) {
|
|
THREE.LineBasicMaterial.call(this);
|
|
this.depthTest = false;
|
this.depthWrite = false;
|
this.fog = false;
|
this.transparent = true;
|
this.linewidth = 1;
|
|
this.setValues(parameters);
|
|
this.oldColor = this.color.clone();
|
this.oldOpacity = this.opacity;
|
|
this.highlight = function (highlighted) {
|
|
if (highlighted) {
|
|
this.color.setRGB(1, 1, 0);
|
this.opacity = 1;
|
|
} else {
|
|
this.color.copy(this.oldColor);
|
this.opacity = this.oldOpacity;
|
|
}
|
|
};
|
|
};
|
|
GizmoLineMaterial.prototype = Object.create(THREE.LineBasicMaterial.prototype);
|
GizmoLineMaterial.prototype.constructor = GizmoLineMaterial;
|
|
|
var pickerMaterial = new GizmoMaterial({ visible: false, transparent: false });
|
|
|
THREE.TransformGizmo = function () {
|
|
this.init = function () {
|
|
THREE.Object3D.call(this);
|
|
this.handles = new THREE.Object3D();
|
this.pickers = new THREE.Object3D();
|
this.planes = new THREE.Object3D();
|
|
this.add(this.handles);
|
this.add(this.pickers);
|
this.add(this.planes);
|
|
//// PLANES
|
|
var planeGeometry = new THREE.PlaneBufferGeometry(50, 50, 2, 2);
|
var planeMaterial = new THREE.MeshBasicMaterial({ visible: false, side: THREE.DoubleSide });
|
|
var planes = {
|
"XY": new THREE.Mesh(planeGeometry, planeMaterial),
|
"YZ": new THREE.Mesh(planeGeometry, planeMaterial),
|
"XZ": new THREE.Mesh(planeGeometry, planeMaterial),
|
"XYZE": new THREE.Mesh(planeGeometry, planeMaterial)
|
};
|
|
this.activePlane = planes["XYZE"];
|
|
planes["YZ"].rotation.set(0, Math.PI / 2, 0);
|
planes["XZ"].rotation.set(-Math.PI / 2, 0, 0);
|
|
for (var i in planes) {
|
|
planes[i].name = i;
|
this.planes.add(planes[i]);
|
this.planes[i] = planes[i];
|
|
}
|
|
//// HANDLES AND PICKERS
|
|
var setupGizmos = function (gizmoMap, parent) {
|
|
for (var name in gizmoMap) {
|
|
for (i = gizmoMap[name].length; i--;) {
|
|
var object = gizmoMap[name][i][0];
|
var position = gizmoMap[name][i][1];
|
var rotation = gizmoMap[name][i][2];
|
|
object.name = name;
|
|
object.renderOrder = Infinity; // avoid being hidden by other transparent objects
|
|
if (position) object.position.set(position[0], position[1], position[2]);
|
if (rotation) object.rotation.set(rotation[0], rotation[1], rotation[2]);
|
|
parent.add(object);
|
|
}
|
|
}
|
|
};
|
|
setupGizmos(this.handleGizmos, this.handles);
|
setupGizmos(this.pickerGizmos, this.pickers);
|
|
// reset Transformations
|
|
this.traverse(function (child) {
|
|
if (child instanceof THREE.Mesh) {
|
|
child.updateMatrix();
|
|
var tempGeometry = child.geometry.clone();
|
tempGeometry.applyMatrix(child.matrix);
|
child.geometry = tempGeometry;
|
|
child.position.set(0, 0, 0);
|
child.rotation.set(0, 0, 0);
|
child.scale.set(1, 1, 1);
|
|
}
|
|
});
|
|
};
|
|
this.highlight = function (axis) {
|
|
this.traverse(function (child) {
|
|
if (child.material && child.material.highlight) {
|
|
if (child.name === axis) {
|
|
child.material.highlight(true);
|
|
} else {
|
|
child.material.highlight(false);
|
|
}
|
|
}
|
|
});
|
|
};
|
|
};
|
|
THREE.TransformGizmo.prototype = Object.create(THREE.Object3D.prototype);
|
THREE.TransformGizmo.prototype.constructor = THREE.TransformGizmo;
|
|
THREE.TransformGizmo.prototype.update = function (rotation, eye) {
|
|
var vec1 = new THREE.Vector3(0, 0, 0);
|
var vec2 = new THREE.Vector3(0, 1, 0);
|
var lookAtMatrix = new THREE.Matrix4();
|
|
this.traverse(function (child) {
|
|
if (child.name.search("E") !== -1) {
|
|
child.quaternion.setFromRotationMatrix(lookAtMatrix.lookAt(eye, vec1, vec2));
|
|
} else if (child.name.search("X") !== -1 || child.name.search("Y") !== -1 || child.name.search("Z") !== -1) {
|
|
child.quaternion.setFromEuler(rotation);
|
|
}
|
|
});
|
|
};
|
|
THREE.TransformGizmoTranslate = function () {
|
|
THREE.TransformGizmo.call(this);
|
|
var arrowGeometry = new THREE.ConeBufferGeometry(0.05, 0.2, 12, 1, false);
|
arrowGeometry.translate(0, 0.5, 0);
|
|
var lineXGeometry = new THREE.BufferGeometry();
|
lineXGeometry.addAttribute('position', new THREE.Float32BufferAttribute([0, 0, 0, 1, 0, 0], 3));
|
|
var lineYGeometry = new THREE.BufferGeometry();
|
lineYGeometry.addAttribute('position', new THREE.Float32BufferAttribute([0, 0, 0, 0, 1, 0], 3));
|
|
var lineZGeometry = new THREE.BufferGeometry();
|
lineZGeometry.addAttribute('position', new THREE.Float32BufferAttribute([0, 0, 0, 0, 0, 1], 3));
|
|
this.handleGizmos = {
|
|
X: [
|
[new THREE.Mesh(arrowGeometry, new GizmoMaterial({ color: 0xff0000 })), [0.5, 0, 0], [0, 0, -Math.PI / 2]],
|
[new THREE.Line(lineXGeometry, new GizmoLineMaterial({ color: 0xff0000 }))]
|
],
|
|
Y: [
|
[new THREE.Mesh(arrowGeometry, new GizmoMaterial({ color: 0x00ff00 })), [0, 0.5, 0]],
|
[new THREE.Line(lineYGeometry, new GizmoLineMaterial({ color: 0x00ff00 }))]
|
],
|
|
Z: [
|
[new THREE.Mesh(arrowGeometry, new GizmoMaterial({ color: 0x0000ff })), [0, 0, 0.5], [Math.PI / 2, 0, 0]],
|
[new THREE.Line(lineZGeometry, new GizmoLineMaterial({ color: 0x0000ff }))]
|
],
|
|
XYZ: [
|
[new THREE.Mesh(new THREE.OctahedronGeometry(0.1, 0), new GizmoMaterial({ color: 0xffffff, opacity: 0.25 })), [0, 0, 0], [0, 0, 0]]
|
],
|
|
XY: [
|
[new THREE.Mesh(new THREE.PlaneBufferGeometry(0.29, 0.29), new GizmoMaterial({ color: 0xffff00, opacity: 0.25 })), [0.15, 0.15, 0]]
|
],
|
|
YZ: [
|
[new THREE.Mesh(new THREE.PlaneBufferGeometry(0.29, 0.29), new GizmoMaterial({ color: 0x00ffff, opacity: 0.25 })), [0, 0.15, 0.15], [0, Math.PI / 2, 0]]
|
],
|
|
XZ: [
|
[new THREE.Mesh(new THREE.PlaneBufferGeometry(0.29, 0.29), new GizmoMaterial({ color: 0xff00ff, opacity: 0.25 })), [0.15, 0, 0.15], [-Math.PI / 2, 0, 0]]
|
]
|
|
};
|
|
this.pickerGizmos = {
|
|
X: [
|
[new THREE.Mesh(new THREE.CylinderBufferGeometry(0.2, 0, 1, 4, 1, false), pickerMaterial), [0.6, 0, 0], [0, 0, -Math.PI / 2]]
|
],
|
|
Y: [
|
[new THREE.Mesh(new THREE.CylinderBufferGeometry(0.2, 0, 1, 4, 1, false), pickerMaterial), [0, 0.6, 0]]
|
],
|
|
Z: [
|
[new THREE.Mesh(new THREE.CylinderBufferGeometry(0.2, 0, 1, 4, 1, false), pickerMaterial), [0, 0, 0.6], [Math.PI / 2, 0, 0]]
|
],
|
|
XYZ: [
|
[new THREE.Mesh(new THREE.OctahedronGeometry(0.2, 0), pickerMaterial)]
|
],
|
|
XY: [
|
[new THREE.Mesh(new THREE.PlaneBufferGeometry(0.4, 0.4), pickerMaterial), [0.2, 0.2, 0]]
|
],
|
|
YZ: [
|
[new THREE.Mesh(new THREE.PlaneBufferGeometry(0.4, 0.4), pickerMaterial), [0, 0.2, 0.2], [0, Math.PI / 2, 0]]
|
],
|
|
XZ: [
|
[new THREE.Mesh(new THREE.PlaneBufferGeometry(0.4, 0.4), pickerMaterial), [0.2, 0, 0.2], [-Math.PI / 2, 0, 0]]
|
]
|
|
};
|
|
this.setActivePlane = function (axis, eye) {
|
|
var tempMatrix = new THREE.Matrix4();
|
eye.applyMatrix4(tempMatrix.getInverse(tempMatrix.extractRotation(this.planes["XY"].matrixWorld)));
|
|
if (axis === "X") {
|
|
this.activePlane = this.planes["XY"];
|
|
if (Math.abs(eye.y) > Math.abs(eye.z)) this.activePlane = this.planes["XZ"];
|
|
}
|
|
if (axis === "Y") {
|
|
this.activePlane = this.planes["XY"];
|
|
if (Math.abs(eye.x) > Math.abs(eye.z)) this.activePlane = this.planes["YZ"];
|
|
}
|
|
if (axis === "Z") {
|
|
this.activePlane = this.planes["XZ"];
|
|
if (Math.abs(eye.x) > Math.abs(eye.y)) this.activePlane = this.planes["YZ"];
|
|
}
|
|
if (axis === "XYZ") this.activePlane = this.planes["XYZE"];
|
|
if (axis === "XY") this.activePlane = this.planes["XY"];
|
|
if (axis === "YZ") this.activePlane = this.planes["YZ"];
|
|
if (axis === "XZ") this.activePlane = this.planes["XZ"];
|
|
};
|
|
this.init();
|
|
};
|
|
THREE.TransformGizmoTranslate.prototype = Object.create(THREE.TransformGizmo.prototype);
|
THREE.TransformGizmoTranslate.prototype.constructor = THREE.TransformGizmoTranslate;
|
|
THREE.TransformGizmoRotate = function () {
|
|
THREE.TransformGizmo.call(this);
|
|
var CircleGeometry = function (radius, facing, arc) {
|
|
var geometry = new THREE.BufferGeometry();
|
var vertices = [];
|
arc = arc ? arc : 1;
|
|
for (var i = 0; i <= 64 * arc; ++i) {
|
|
if (facing === 'x') vertices.push(0, Math.cos(i / 32 * Math.PI) * radius, Math.sin(i / 32 * Math.PI) * radius);
|
if (facing === 'y') vertices.push(Math.cos(i / 32 * Math.PI) * radius, 0, Math.sin(i / 32 * Math.PI) * radius);
|
if (facing === 'z') vertices.push(Math.sin(i / 32 * Math.PI) * radius, Math.cos(i / 32 * Math.PI) * radius, 0);
|
|
}
|
|
geometry.addAttribute('position', new THREE.Float32BufferAttribute(vertices, 3));
|
return geometry;
|
|
};
|
|
this.handleGizmos = {
|
|
X: [
|
[new THREE.Line(new CircleGeometry(1, 'x', 0.5), new GizmoLineMaterial({ color: 0xff0000 }))]
|
],
|
|
Y: [
|
[new THREE.Line(new CircleGeometry(1, 'y', 0.5), new GizmoLineMaterial({ color: 0x00ff00 }))]
|
],
|
|
Z: [
|
[new THREE.Line(new CircleGeometry(1, 'z', 0.5), new GizmoLineMaterial({ color: 0x0000ff }))]
|
],
|
|
E: [
|
[new THREE.Line(new CircleGeometry(1.25, 'z', 1), new GizmoLineMaterial({ color: 0xcccc00 }))]
|
],
|
|
XYZE: [
|
[new THREE.Line(new CircleGeometry(1, 'z', 1), new GizmoLineMaterial({ color: 0x787878 }))]
|
]
|
|
};
|
|
this.pickerGizmos = {
|
|
X: [
|
[new THREE.Mesh(new THREE.TorusBufferGeometry(1, 0.12, 4, 12, Math.PI), pickerMaterial), [0, 0, 0], [0, -Math.PI / 2, -Math.PI / 2]]
|
],
|
|
Y: [
|
[new THREE.Mesh(new THREE.TorusBufferGeometry(1, 0.12, 4, 12, Math.PI), pickerMaterial), [0, 0, 0], [Math.PI / 2, 0, 0]]
|
],
|
|
Z: [
|
[new THREE.Mesh(new THREE.TorusBufferGeometry(1, 0.12, 4, 12, Math.PI), pickerMaterial), [0, 0, 0], [0, 0, -Math.PI / 2]]
|
],
|
|
E: [
|
[new THREE.Mesh(new THREE.TorusBufferGeometry(1.25, 0.12, 2, 24), pickerMaterial)]
|
],
|
|
XYZE: [
|
[new THREE.Mesh(new THREE.TorusBufferGeometry(1, 0.12, 2, 24), pickerMaterial)]
|
]
|
|
};
|
|
this.pickerGizmos.XYZE[0][0].visible = false; // disable XYZE picker gizmo
|
|
this.setActivePlane = function (axis) {
|
|
if (axis === "E") this.activePlane = this.planes["XYZE"];
|
|
if (axis === "X") this.activePlane = this.planes["YZ"];
|
|
if (axis === "Y") this.activePlane = this.planes["XZ"];
|
|
if (axis === "Z") this.activePlane = this.planes["XY"];
|
|
};
|
|
this.update = function (rotation, eye2) {
|
|
THREE.TransformGizmo.prototype.update.apply(this, arguments);
|
|
var tempMatrix = new THREE.Matrix4();
|
var worldRotation = new THREE.Euler(0, 0, 1);
|
var tempQuaternion = new THREE.Quaternion();
|
var unitX = new THREE.Vector3(1, 0, 0);
|
var unitY = new THREE.Vector3(0, 1, 0);
|
var unitZ = new THREE.Vector3(0, 0, 1);
|
var quaternionX = new THREE.Quaternion();
|
var quaternionY = new THREE.Quaternion();
|
var quaternionZ = new THREE.Quaternion();
|
var eye = eye2.clone();
|
|
worldRotation.copy(this.planes["XY"].rotation);
|
tempQuaternion.setFromEuler(worldRotation);
|
|
tempMatrix.makeRotationFromQuaternion(tempQuaternion).getInverse(tempMatrix);
|
eye.applyMatrix4(tempMatrix);
|
|
this.traverse(function (child) {
|
|
tempQuaternion.setFromEuler(worldRotation);
|
|
if (child.name === "X") {
|
|
quaternionX.setFromAxisAngle(unitX, Math.atan2(-eye.y, eye.z));
|
tempQuaternion.multiplyQuaternions(tempQuaternion, quaternionX);
|
child.quaternion.copy(tempQuaternion);
|
|
}
|
|
if (child.name === "Y") {
|
|
quaternionY.setFromAxisAngle(unitY, Math.atan2(eye.x, eye.z));
|
tempQuaternion.multiplyQuaternions(tempQuaternion, quaternionY);
|
child.quaternion.copy(tempQuaternion);
|
|
}
|
|
if (child.name === "Z") {
|
|
quaternionZ.setFromAxisAngle(unitZ, Math.atan2(eye.y, eye.x));
|
tempQuaternion.multiplyQuaternions(tempQuaternion, quaternionZ);
|
child.quaternion.copy(tempQuaternion);
|
|
}
|
|
});
|
|
};
|
|
this.init();
|
|
};
|
|
THREE.TransformGizmoRotate.prototype = Object.create(THREE.TransformGizmo.prototype);
|
THREE.TransformGizmoRotate.prototype.constructor = THREE.TransformGizmoRotate;
|
|
THREE.TransformGizmoScale = function () {
|
|
THREE.TransformGizmo.call(this);
|
|
var arrowGeometry = new THREE.BoxBufferGeometry(0.125, 0.125, 0.125);
|
arrowGeometry.translate(0, 0.5, 0);
|
|
var lineXGeometry = new THREE.BufferGeometry();
|
lineXGeometry.addAttribute('position', new THREE.Float32BufferAttribute([0, 0, 0, 1, 0, 0], 3));
|
|
var lineYGeometry = new THREE.BufferGeometry();
|
lineYGeometry.addAttribute('position', new THREE.Float32BufferAttribute([0, 0, 0, 0, 1, 0], 3));
|
|
var lineZGeometry = new THREE.BufferGeometry();
|
lineZGeometry.addAttribute('position', new THREE.Float32BufferAttribute([0, 0, 0, 0, 0, 1], 3));
|
|
this.handleGizmos = {
|
|
X: [
|
[new THREE.Mesh(arrowGeometry, new GizmoMaterial({ color: 0xff0000 })), [0.5, 0, 0], [0, 0, -Math.PI / 2]],
|
[new THREE.Line(lineXGeometry, new GizmoLineMaterial({ color: 0xff0000 }))]
|
],
|
|
Y: [
|
[new THREE.Mesh(arrowGeometry, new GizmoMaterial({ color: 0x00ff00 })), [0, 0.5, 0]],
|
[new THREE.Line(lineYGeometry, new GizmoLineMaterial({ color: 0x00ff00 }))]
|
],
|
|
Z: [
|
[new THREE.Mesh(arrowGeometry, new GizmoMaterial({ color: 0x0000ff })), [0, 0, 0.5], [Math.PI / 2, 0, 0]],
|
[new THREE.Line(lineZGeometry, new GizmoLineMaterial({ color: 0x0000ff }))]
|
],
|
|
XYZ: [
|
[new THREE.Mesh(new THREE.BoxBufferGeometry(0.125, 0.125, 0.125), new GizmoMaterial({ color: 0xffffff, opacity: 0.25 }))]
|
]
|
|
};
|
|
this.pickerGizmos = {
|
|
X: [
|
[new THREE.Mesh(new THREE.CylinderBufferGeometry(0.2, 0, 1, 4, 1, false), pickerMaterial), [0.6, 0, 0], [0, 0, -Math.PI / 2]]
|
],
|
|
Y: [
|
[new THREE.Mesh(new THREE.CylinderBufferGeometry(0.2, 0, 1, 4, 1, false), pickerMaterial), [0, 0.6, 0]]
|
],
|
|
Z: [
|
[new THREE.Mesh(new THREE.CylinderBufferGeometry(0.2, 0, 1, 4, 1, false), pickerMaterial), [0, 0, 0.6], [Math.PI / 2, 0, 0]]
|
],
|
|
XYZ: [
|
[new THREE.Mesh(new THREE.BoxBufferGeometry(0.4, 0.4, 0.4), pickerMaterial)]
|
]
|
|
};
|
|
this.setActivePlane = function (axis, eye) {
|
|
var tempMatrix = new THREE.Matrix4();
|
eye.applyMatrix4(tempMatrix.getInverse(tempMatrix.extractRotation(this.planes["XY"].matrixWorld)));
|
|
if (axis === "X") {
|
|
this.activePlane = this.planes["XY"];
|
if (Math.abs(eye.y) > Math.abs(eye.z)) this.activePlane = this.planes["XZ"];
|
|
}
|
|
if (axis === "Y") {
|
|
this.activePlane = this.planes["XY"];
|
if (Math.abs(eye.x) > Math.abs(eye.z)) this.activePlane = this.planes["YZ"];
|
|
}
|
|
if (axis === "Z") {
|
|
this.activePlane = this.planes["XZ"];
|
if (Math.abs(eye.x) > Math.abs(eye.y)) this.activePlane = this.planes["YZ"];
|
|
}
|
|
if (axis === "XYZ") this.activePlane = this.planes["XYZE"];
|
|
};
|
|
this.init();
|
|
};
|
|
THREE.TransformGizmoScale.prototype = Object.create(THREE.TransformGizmo.prototype);
|
THREE.TransformGizmoScale.prototype.constructor = THREE.TransformGizmoScale;
|
|
THREE.TransformControls = function (camera, domElement) {
|
|
// TODO: Make non-uniform scale and rotate play nice in hierarchies
|
// TODO: ADD RXYZ contol
|
|
THREE.Object3D.call(this);
|
|
domElement = (domElement !== undefined) ? domElement : document;
|
|
this.object = undefined;
|
this.visible = false;
|
this.translationSnap = null;
|
this.rotationSnap = null;
|
this.space = "world";
|
this.size = 1;
|
this.axis = null;
|
|
var scope = this;
|
|
var _mode = "translate";
|
var _dragging = false;
|
var _gizmo = {
|
|
"translate": new THREE.TransformGizmoTranslate(),
|
"rotate": new THREE.TransformGizmoRotate(),
|
"scale": new THREE.TransformGizmoScale()
|
};
|
|
for (var type in _gizmo) {
|
|
var gizmoObj = _gizmo[type];
|
|
gizmoObj.visible = (type === _mode);
|
this.add(gizmoObj);
|
|
}
|
|
var changeEvent = { type: "change" };
|
var mouseDownEvent = { type: "mouseDown" };
|
var mouseUpEvent = { type: "mouseUp", mode: _mode };
|
var objectChangeEvent = { type: "objectChange" };
|
|
var ray = new THREE.Raycaster();
|
var pointerVector = new THREE.Vector2();
|
|
var point = new THREE.Vector3();
|
var offset = new THREE.Vector3();
|
|
var rotation = new THREE.Vector3();
|
var offsetRotation = new THREE.Vector3();
|
var scale = 1;
|
|
var lookAtMatrix = new THREE.Matrix4();
|
var eye = new THREE.Vector3();
|
|
var tempMatrix = new THREE.Matrix4();
|
var tempVector = new THREE.Vector3();
|
var tempQuaternion = new THREE.Quaternion();
|
var unitX = new THREE.Vector3(1, 0, 0);
|
var unitY = new THREE.Vector3(0, 1, 0);
|
var unitZ = new THREE.Vector3(0, 0, 1);
|
|
var quaternionXYZ = new THREE.Quaternion();
|
var quaternionX = new THREE.Quaternion();
|
var quaternionY = new THREE.Quaternion();
|
var quaternionZ = new THREE.Quaternion();
|
var quaternionE = new THREE.Quaternion();
|
|
var oldPosition = new THREE.Vector3();
|
var oldScale = new THREE.Vector3();
|
var oldRotationMatrix = new THREE.Matrix4();
|
|
var parentRotationMatrix = new THREE.Matrix4();
|
var parentScale = new THREE.Vector3();
|
|
var worldPosition = new THREE.Vector3();
|
var worldRotation = new THREE.Euler();
|
var worldRotationMatrix = new THREE.Matrix4();
|
var camPosition = new THREE.Vector3();
|
var camRotation = new THREE.Euler();
|
|
domElement.addEventListener("mousedown", onPointerDown, false);
|
domElement.addEventListener("touchstart", onPointerDown, false);
|
|
domElement.addEventListener("mousemove", onPointerHover, false);
|
domElement.addEventListener("touchmove", onPointerHover, false);
|
|
domElement.addEventListener("mousemove", onPointerMove, false);
|
domElement.addEventListener("touchmove", onPointerMove, false);
|
|
domElement.addEventListener("mouseup", onPointerUp, false);
|
domElement.addEventListener("mouseout", onPointerUp, false);
|
domElement.addEventListener("touchend", onPointerUp, false);
|
domElement.addEventListener("touchcancel", onPointerUp, false);
|
domElement.addEventListener("touchleave", onPointerUp, false);
|
|
this.dispose = function () {
|
|
domElement.removeEventListener("mousedown", onPointerDown);
|
domElement.removeEventListener("touchstart", onPointerDown);
|
|
domElement.removeEventListener("mousemove", onPointerHover);
|
domElement.removeEventListener("touchmove", onPointerHover);
|
|
domElement.removeEventListener("mousemove", onPointerMove);
|
domElement.removeEventListener("touchmove", onPointerMove);
|
|
domElement.removeEventListener("mouseup", onPointerUp);
|
domElement.removeEventListener("mouseout", onPointerUp);
|
domElement.removeEventListener("touchend", onPointerUp);
|
domElement.removeEventListener("touchcancel", onPointerUp);
|
domElement.removeEventListener("touchleave", onPointerUp);
|
|
};
|
|
this.attach = function (object) {
|
|
this.object = object;
|
this.visible = true;
|
this.update();
|
|
};
|
|
this.detach = function () {
|
|
this.object = undefined;
|
this.visible = false;
|
this.axis = null;
|
|
};
|
|
this.getMode = function () {
|
|
return _mode;
|
|
};
|
|
this.setMode = function (mode) {
|
|
_mode = mode ? mode : _mode;
|
|
if (_mode === "scale") scope.space = "local";
|
|
for (var type in _gizmo) _gizmo[type].visible = (type === _mode);
|
|
this.update();
|
scope.dispatchEvent(changeEvent);
|
|
};
|
|
this.setTranslationSnap = function (translationSnap) {
|
|
scope.translationSnap = translationSnap;
|
|
};
|
|
this.setRotationSnap = function (rotationSnap) {
|
|
scope.rotationSnap = rotationSnap;
|
|
};
|
|
this.setSize = function (size) {
|
|
scope.size = size;
|
this.update();
|
scope.dispatchEvent(changeEvent);
|
|
};
|
|
this.setSpace = function (space) {
|
|
scope.space = space;
|
this.update();
|
scope.dispatchEvent(changeEvent);
|
|
};
|
|
this.update = function () {
|
|
if (scope.object === undefined) return;
|
|
scope.object.updateMatrixWorld();
|
worldPosition.setFromMatrixPosition(scope.object.matrixWorld);
|
worldRotation.setFromRotationMatrix(tempMatrix.extractRotation(scope.object.matrixWorld));
|
|
camera.updateMatrixWorld();
|
camPosition.setFromMatrixPosition(camera.matrixWorld);
|
camRotation.setFromRotationMatrix(tempMatrix.extractRotation(camera.matrixWorld));
|
|
scale = worldPosition.distanceTo(camPosition) / 6 * scope.size;
|
this.position.copy(worldPosition);
|
this.scale.set(scale, scale, scale);
|
|
if (camera instanceof THREE.PerspectiveCamera) {
|
|
eye.copy(camPosition).sub(worldPosition).normalize();
|
|
} else if (camera instanceof THREE.OrthographicCamera) {
|
|
eye.copy(camPosition).normalize();
|
|
}
|
|
if (scope.space === "local") {
|
|
_gizmo[_mode].update(worldRotation, eye);
|
|
} else if (scope.space === "world") {
|
|
_gizmo[_mode].update(new THREE.Euler(), eye);
|
|
}
|
|
_gizmo[_mode].highlight(scope.axis);
|
|
};
|
|
function onPointerHover(event) {
|
|
if (scope.object === undefined || _dragging === true || (event.button !== undefined && event.button !== 0)) return;
|
|
var pointer = event.changedTouches ? event.changedTouches[0] : event;
|
|
var intersect = intersectObjects(pointer, _gizmo[_mode].pickers.children);
|
|
var axis = null;
|
|
if (intersect) {
|
|
axis = intersect.object.name;
|
|
event.preventDefault();
|
|
}
|
|
if (scope.axis !== axis) {
|
|
scope.axis = axis;
|
scope.update();
|
scope.dispatchEvent(changeEvent);
|
|
}
|
|
}
|
|
function onPointerDown(event) {
|
|
if (scope.object === undefined || _dragging === true || (event.button !== undefined && event.button !== 0)) return;
|
|
var pointer = event.changedTouches ? event.changedTouches[0] : event;
|
|
if (pointer.button === 0 || pointer.button === undefined) {
|
|
var intersect = intersectObjects(pointer, _gizmo[_mode].pickers.children);
|
|
if (intersect) {
|
|
event.preventDefault();
|
event.stopPropagation();
|
|
scope.axis = intersect.object.name;
|
|
scope.dispatchEvent(mouseDownEvent);
|
|
scope.update();
|
|
eye.copy(camPosition).sub(worldPosition).normalize();
|
|
_gizmo[_mode].setActivePlane(scope.axis, eye);
|
|
var planeIntersect = intersectObjects(pointer, [_gizmo[_mode].activePlane]);
|
|
if (planeIntersect) {
|
|
oldPosition.copy(scope.object.position);
|
oldScale.copy(scope.object.scale);
|
|
oldRotationMatrix.extractRotation(scope.object.matrix);
|
worldRotationMatrix.extractRotation(scope.object.matrixWorld);
|
|
parentRotationMatrix.extractRotation(scope.object.parent.matrixWorld);
|
parentScale.setFromMatrixScale(tempMatrix.getInverse(scope.object.parent.matrixWorld));
|
|
offset.copy(planeIntersect.point);
|
|
}
|
|
}
|
|
}
|
|
_dragging = true;
|
|
}
|
|
function onPointerMove(event) {
|
|
if (scope.object === undefined || scope.axis === null || _dragging === false || (event.button !== undefined && event.button !== 0)) return;
|
|
var pointer = event.changedTouches ? event.changedTouches[0] : event;
|
|
var planeIntersect = intersectObjects(pointer, [_gizmo[_mode].activePlane]);
|
|
if (planeIntersect === false) return;
|
|
event.preventDefault();
|
event.stopPropagation();
|
|
point.copy(planeIntersect.point);
|
|
if (_mode === "translate") {
|
|
point.sub(offset);
|
point.multiply(parentScale);
|
|
if (scope.space === "local") {
|
|
point.applyMatrix4(tempMatrix.getInverse(worldRotationMatrix));
|
|
if (scope.axis.search("X") === -1) point.x = 0;
|
if (scope.axis.search("Y") === -1) point.y = 0;
|
if (scope.axis.search("Z") === -1) point.z = 0;
|
|
point.applyMatrix4(oldRotationMatrix);
|
|
scope.object.position.copy(oldPosition);
|
scope.object.position.add(point);
|
|
}
|
|
if (scope.space === "world" || scope.axis.search("XYZ") !== -1) {
|
|
if (scope.axis.search("X") === -1) point.x = 0;
|
if (scope.axis.search("Y") === -1) point.y = 0;
|
if (scope.axis.search("Z") === -1) point.z = 0;
|
|
point.applyMatrix4(tempMatrix.getInverse(parentRotationMatrix));
|
|
scope.object.position.copy(oldPosition);
|
scope.object.position.add(point);
|
|
}
|
|
if (scope.translationSnap !== null) {
|
|
if (scope.space === "local") {
|
|
scope.object.position.applyMatrix4(tempMatrix.getInverse(worldRotationMatrix));
|
|
}
|
|
if (scope.axis.search("X") !== -1) scope.object.position.x = Math.round(scope.object.position.x / scope.translationSnap) * scope.translationSnap;
|
if (scope.axis.search("Y") !== -1) scope.object.position.y = Math.round(scope.object.position.y / scope.translationSnap) * scope.translationSnap;
|
if (scope.axis.search("Z") !== -1) scope.object.position.z = Math.round(scope.object.position.z / scope.translationSnap) * scope.translationSnap;
|
|
if (scope.space === "local") {
|
|
scope.object.position.applyMatrix4(worldRotationMatrix);
|
|
}
|
|
}
|
|
} else if (_mode === "scale") {
|
|
point.sub(offset);
|
point.multiply(parentScale);
|
|
if (scope.space === "local") {
|
|
if (scope.axis === "XYZ") {
|
|
scale = 1 + ((point.y) / Math.max(oldScale.x, oldScale.y, oldScale.z));
|
|
scope.object.scale.x = oldScale.x * scale;
|
scope.object.scale.y = oldScale.y * scale;
|
scope.object.scale.z = oldScale.z * scale;
|
|
} else {
|
|
point.applyMatrix4(tempMatrix.getInverse(worldRotationMatrix));
|
|
if (scope.axis === "X") scope.object.scale.x = oldScale.x * (1 + point.x / oldScale.x);
|
if (scope.axis === "Y") scope.object.scale.y = oldScale.y * (1 + point.y / oldScale.y);
|
if (scope.axis === "Z") scope.object.scale.z = oldScale.z * (1 + point.z / oldScale.z);
|
|
}
|
|
}
|
|
} else if (_mode === "rotate") {
|
|
point.sub(worldPosition);
|
point.multiply(parentScale);
|
tempVector.copy(offset).sub(worldPosition);
|
tempVector.multiply(parentScale);
|
|
if (scope.axis === "E") {
|
|
point.applyMatrix4(tempMatrix.getInverse(lookAtMatrix));
|
tempVector.applyMatrix4(tempMatrix.getInverse(lookAtMatrix));
|
|
rotation.set(Math.atan2(point.z, point.y), Math.atan2(point.x, point.z), Math.atan2(point.y, point.x));
|
offsetRotation.set(Math.atan2(tempVector.z, tempVector.y), Math.atan2(tempVector.x, tempVector.z), Math.atan2(tempVector.y, tempVector.x));
|
|
tempQuaternion.setFromRotationMatrix(tempMatrix.getInverse(parentRotationMatrix));
|
|
quaternionE.setFromAxisAngle(eye, rotation.z - offsetRotation.z);
|
quaternionXYZ.setFromRotationMatrix(worldRotationMatrix);
|
|
tempQuaternion.multiplyQuaternions(tempQuaternion, quaternionE);
|
tempQuaternion.multiplyQuaternions(tempQuaternion, quaternionXYZ);
|
|
scope.object.quaternion.copy(tempQuaternion);
|
|
} else if (scope.axis === "XYZE") {
|
|
quaternionE.setFromEuler(point.clone().cross(tempVector).normalize()); // rotation axis
|
|
tempQuaternion.setFromRotationMatrix(tempMatrix.getInverse(parentRotationMatrix));
|
quaternionX.setFromAxisAngle(quaternionE, -point.clone().angleTo(tempVector));
|
quaternionXYZ.setFromRotationMatrix(worldRotationMatrix);
|
|
tempQuaternion.multiplyQuaternions(tempQuaternion, quaternionX);
|
tempQuaternion.multiplyQuaternions(tempQuaternion, quaternionXYZ);
|
|
scope.object.quaternion.copy(tempQuaternion);
|
|
} else if (scope.space === "local") {
|
|
point.applyMatrix4(tempMatrix.getInverse(worldRotationMatrix));
|
|
tempVector.applyMatrix4(tempMatrix.getInverse(worldRotationMatrix));
|
|
rotation.set(Math.atan2(point.z, point.y), Math.atan2(point.x, point.z), Math.atan2(point.y, point.x));
|
offsetRotation.set(Math.atan2(tempVector.z, tempVector.y), Math.atan2(tempVector.x, tempVector.z), Math.atan2(tempVector.y, tempVector.x));
|
|
quaternionXYZ.setFromRotationMatrix(oldRotationMatrix);
|
|
if (scope.rotationSnap !== null) {
|
|
quaternionX.setFromAxisAngle(unitX, Math.round((rotation.x - offsetRotation.x) / scope.rotationSnap) * scope.rotationSnap);
|
quaternionY.setFromAxisAngle(unitY, Math.round((rotation.y - offsetRotation.y) / scope.rotationSnap) * scope.rotationSnap);
|
quaternionZ.setFromAxisAngle(unitZ, Math.round((rotation.z - offsetRotation.z) / scope.rotationSnap) * scope.rotationSnap);
|
|
} else {
|
|
quaternionX.setFromAxisAngle(unitX, rotation.x - offsetRotation.x);
|
quaternionY.setFromAxisAngle(unitY, rotation.y - offsetRotation.y);
|
quaternionZ.setFromAxisAngle(unitZ, rotation.z - offsetRotation.z);
|
|
}
|
|
if (scope.axis === "X") quaternionXYZ.multiplyQuaternions(quaternionXYZ, quaternionX);
|
if (scope.axis === "Y") quaternionXYZ.multiplyQuaternions(quaternionXYZ, quaternionY);
|
if (scope.axis === "Z") quaternionXYZ.multiplyQuaternions(quaternionXYZ, quaternionZ);
|
|
scope.object.quaternion.copy(quaternionXYZ);
|
|
} else if (scope.space === "world") {
|
|
rotation.set(Math.atan2(point.z, point.y), Math.atan2(point.x, point.z), Math.atan2(point.y, point.x));
|
offsetRotation.set(Math.atan2(tempVector.z, tempVector.y), Math.atan2(tempVector.x, tempVector.z), Math.atan2(tempVector.y, tempVector.x));
|
|
tempQuaternion.setFromRotationMatrix(tempMatrix.getInverse(parentRotationMatrix));
|
|
if (scope.rotationSnap !== null) {
|
|
quaternionX.setFromAxisAngle(unitX, Math.round((rotation.x - offsetRotation.x) / scope.rotationSnap) * scope.rotationSnap);
|
quaternionY.setFromAxisAngle(unitY, Math.round((rotation.y - offsetRotation.y) / scope.rotationSnap) * scope.rotationSnap);
|
quaternionZ.setFromAxisAngle(unitZ, Math.round((rotation.z - offsetRotation.z) / scope.rotationSnap) * scope.rotationSnap);
|
|
} else {
|
|
quaternionX.setFromAxisAngle(unitX, rotation.x - offsetRotation.x);
|
quaternionY.setFromAxisAngle(unitY, rotation.y - offsetRotation.y);
|
quaternionZ.setFromAxisAngle(unitZ, rotation.z - offsetRotation.z);
|
|
}
|
|
quaternionXYZ.setFromRotationMatrix(worldRotationMatrix);
|
|
if (scope.axis === "X") tempQuaternion.multiplyQuaternions(tempQuaternion, quaternionX);
|
if (scope.axis === "Y") tempQuaternion.multiplyQuaternions(tempQuaternion, quaternionY);
|
if (scope.axis === "Z") tempQuaternion.multiplyQuaternions(tempQuaternion, quaternionZ);
|
|
tempQuaternion.multiplyQuaternions(tempQuaternion, quaternionXYZ);
|
|
scope.object.quaternion.copy(tempQuaternion);
|
|
}
|
|
}
|
|
scope.update();
|
scope.dispatchEvent(changeEvent);
|
scope.dispatchEvent(objectChangeEvent);
|
|
}
|
|
function onPointerUp(event) {
|
|
event.preventDefault(); // Prevent MouseEvent on mobile
|
|
if (event.button !== undefined && event.button !== 0) return;
|
|
if (_dragging && (scope.axis !== null)) {
|
|
mouseUpEvent.mode = _mode;
|
scope.dispatchEvent(mouseUpEvent);
|
|
}
|
|
_dragging = false;
|
|
if ('TouchEvent' in window && event instanceof TouchEvent) {
|
|
// Force "rollover"
|
|
scope.axis = null;
|
scope.update();
|
scope.dispatchEvent(changeEvent);
|
|
} else {
|
|
onPointerHover(event);
|
|
}
|
|
}
|
|
function intersectObjects(pointer, objects) {
|
|
var rect = domElement.getBoundingClientRect();
|
var x = (pointer.clientX - rect.left) / rect.width;
|
var y = (pointer.clientY - rect.top) / rect.height;
|
|
pointerVector.set((x * 2) - 1, -(y * 2) + 1);
|
ray.setFromCamera(pointerVector, camera);
|
|
var intersections = ray.intersectObjects(objects, true);
|
return intersections[0] ? intersections[0] : false;
|
|
}
|
|
};
|
|
THREE.TransformControls.prototype = Object.create(THREE.Object3D.prototype);
|
THREE.TransformControls.prototype.constructor = THREE.TransformControls;
|
|
}());
|