three-codeeditor
Version:
codeeditor for three.js
168 lines (130 loc) • 6.73 kB
JavaScript
;(function(exports) {
// "imports"
var retargetDOMEvent = THREE.CodeEditor.domevents.retargetDOMEvent;
var isFullscreen = THREE.CodeEditor.domevents.isFullscreen;
// "exports"
exports.getRelativeMouseXY = getRelativeMouseXY;
exports.getRelativeMouseXYFromEvent = getRelativeMouseXYFromEvent;
exports.domEventRaycast = domEventRaycast;
exports.pickObjFromDOMEvent = pickObjFromDOMEvent;
exports.pickingRay = pickingRay;
exports.raycastIntersectionToDomXY = raycastIntersectionToDomXY;
exports.convertToBrowserCoords = convertToBrowserCoords;
exports.convertEventPos3DtoHTML = convertEventPos3DtoHTML;
// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
function convertXYToOcculusCoords(coords) {
var x = coords.x, y = coords.y;
var domainXMin = x < 0 ? -1 : 0, domainXMax = x < 0 ? 0 : 1;
var domainYMin = -1, domainYMax = 1;
var relX = (x + (domainXMax - domainXMin)) - domainXMax;
var rangeXMin = -.9, rangeXMax = .9;
var relY = (y + (domainYMax - domainYMin)) - domainYMax;
var rangeYMin = -.8, rangeYMax = .8;
// ((rangeYMax - rangeYMin) * relY) - (rangeYMax-rangeYMin)
return {
x: ((rangeXMax - rangeXMin) * relX) - rangeXMax,
y: ((rangeYMax - rangeYMin) * relY) - (rangeYMax-rangeYMin),
z: coords.z
};
}
function getRelativeMouseXY(x, y, domElement, mapForVR) {
// Converts the browser global (page) x/y coordinates
// into relative -1/1 values. These can be used by THREE for raycasting.
var rect = domElement.getBoundingClientRect(),
relX = (x - rect.left) / rect.width,
relY = (y - rect.top) / rect.height,
coords = {
x : (relX * 2) - 1,
y : -(relY * 2) + 1,
z: 0.5
};
return mapForVR ? convertXYToOcculusCoords(coords) : coords;
}
function getRelativeMouseXYFromEvent(domEvent, mapForVR) {
return getRelativeMouseXY(domEvent.pageX, domEvent.pageY, domEvent.target || domEvent.srcElement, mapForVR);
}
// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
// see https://github.com/mrdoob/three.js/issues/5587
function pickingRay(coords, camera) {
var raycaster = new THREE.Raycaster();
// the camera is assumed _not_ to be a child of a transformed object
if ( camera instanceof THREE.PerspectiveCamera ) {
raycaster.ray.origin.copy( camera.position );
raycaster.ray.direction.set( coords.x, coords.y, 0.5 ).unproject( camera ).sub( camera.position ).normalize();
} else if ( camera instanceof THREE.OrthographicCamera ) {
raycaster.ray.origin.set( coords.x, coords.y, - 1 ).unproject( camera );
raycaster.ray.direction.set( 0, 0, - 1 ).transformDirection( camera.matrixWorld );
} else {
console.error( 'ERROR: unknown camera type.' );
}
return raycaster;
}
function domEventRaycast(domEvent, camera, mapForVR) {
var mouseCoords = getRelativeMouseXYFromEvent(domEvent, mapForVR),
vector = new THREE.Vector3(mouseCoords.x, mouseCoords.y, 0.5);
return pickingRay(vector, camera);
}
// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
// mapping of scene positions
// -=-=-=-=-=-=-=-=-=-=-=-=-=-
function pickObjFromDOMEvent(evt, camera, objsToPick,mapForVR) {
var intersects = domEventRaycast(evt,camera,mapForVR).intersectObjects(objsToPick);
return intersects[0];
}
function raycastIntersectionToDomXY(intersection, domElement) {
if (!intersection) return null;
//var localCoords = convertToBrowserCoords(intersection, intersection.object);
const mesh3D = intersection.object;
mesh3D.geometry.computeBoundingBox();
const size = mesh3D.geometry.boundingBox.getSize();
const x = size.x * intersection.uv.x;
const y = size.y * (1.0 - intersection.uv.y);
const localCoords = {x:x, y:y};
var aceCoords = {
x: domElement.offsetLeft + localCoords.x,
y: domElement.offsetTop + localCoords.y
}
return aceCoords;
}
// FIXME: SETH, this function is extremely suspect, it did not work
// for the above usage, maybe something fundamental has changed about the raycaster?
function convertToBrowserCoords(intersection, mesh3D) {
// Convert the raycast point on mesh3D into the top/left coordinate sytem
// of the DOM. The result coords are local to the mesh3D, not its scene.
if (!intersection) return null;
var cache = intersection.cachedLocalBrowserCoords || (intersection.cachedLocalBrowserCoords = {});
if (cache[mesh3D.uuid]) return cache[mesh3D.uuid];
mesh3D.geometry.computeBoundingBox()
var worldPoint = intersection.point,
size = mesh3D.geometry.boundingBox.getSize(),
worldCenter = mesh3D.position.clone().add(mesh3D.geometry.boundingBox.getCenter()),
localTopLeft = mesh3D.worldToLocal(worldCenter).add(size.multiply(new THREE.Vector3(.5,-.5,.5))),
localEvt = mesh3D.worldToLocal(worldPoint.clone()),
browserLocalTopLeft = localTopLeft.clone().add(localEvt).multiply(new THREE.Vector3(1,-1,1))
return cache[mesh3D.uuid] = browserLocalTopLeft;
}
function convertEventPos3DtoHTML(domEvent, camera, oldEventTargetEl, newEventTargetEl, sceneObject, offset, mapForVR) {
// DOM evt on 3D scene -> 2D position onto dom element acting as a hypothetical target.
// Note that `oldEventTargetEl` can be choosen by the caller, it does not
// neet to be the actual domEvent.target. We use it when getting e.g. scroll
// events from the ace editor (target is actually the ace editor element) but
// while the mouse is over the 3d canvas. We then get the targeted object and
// determine the position the event would have when we would have scrolled
// over the ace editor directly.
// ....
// Takes a domEvent sent to a canvas3d element. Does a ray cast and figures
// out if and where `sceneObject` was hit by the event (via the "intersection"
// ray cast result). Then projects the position onto
// `newEventTargetEl` and returns the local position where this object
// would have been hit if it would be the actual event target.
var offsetX = offset ? offset.x : 0,
offsetY = offset ? offset.y : 0,
intersection = pickObjFromDOMEvent(
retargetDOMEvent(domEvent,
{x: domEvent.pageX+offsetX, y: domEvent.pageY+offsetY},
oldEventTargetEl),
camera, [sceneObject],
mapForVR);
return raycastIntersectionToDomXY(intersection, newEventTargetEl);
}
})(THREE.CodeEditor.raycasting || (THREE.CodeEditor.raycasting = {}));