Threejs - How to pick all objects in area? - three.js

I'm using Three.js and I wonder how to get all objects in a given area?
For example, get all objects that found in the green-square:
Solution:
getEntitiesInSelection: function(x, z, width, height, inGroup) {
var self = this,
entitiesMap = [],
color = 0,
colors = [],
ids = [],
pickingGeometry = new THREE.Geometry(),
pickingMaterial = new THREE.MeshBasicMaterial( { vertexColors: THREE.VertexColors } ),
pickingScene = new THREE.Scene(),
pickingTexture = new THREE.WebGLRenderTarget( this._renderer.domElement.width, this._renderer.domElement.height),
cloneMesh,
entities = inGroup ?
engine.getObjectsByGroup(inGroup) : engine.getRegisteredEntities();
pickingTexture.generateMipmaps = false;
//Go over each entity, change its color into its ID
_.forEach(entities, function(entity) {
if(undefined == entity.threeRenderable) {
return ;
}
//Clone entity
cloneMesh = entity.threeRenderable.mesh().clone();
cloneMesh.material = entity.threeRenderable.mesh().material.clone();
cloneMesh.material.map = null;
cloneMesh.material.vertexColors = THREE.VertexColors;
cloneMesh.geometry = entity.threeRenderable.mesh().geometry.clone();
cloneMesh.position.copy( entity.threeRenderable.mesh().position );
cloneMesh.rotation.copy( entity.threeRenderable.mesh().rotation );
cloneMesh.scale.copy( entity.threeRenderable.mesh().scale );
//Cancel shadow
cloneMesh.castShadow = false;
cloneMesh.receiveShadow = false;
//Set color as entity ID
entitiesMap[color] = entity.id();
self._applyVertexColors(cloneMesh.geometry, new THREE.Color( color ) );
color++;
THREE.GeometryUtils.merge( pickingGeometry, cloneMesh);
});
pickingScene.add( new THREE.Mesh( pickingGeometry, pickingMaterial ) );
//render the picking scene off-screen
this._renderer.render(pickingScene, this._objs[this._mainCamera], pickingTexture );
var gl = this._renderer.getContext();
//read the pixel under the mouse from the texture
var pixelBuffer = new Uint8Array( 4 * width * height );
gl.readPixels( x, this._renderer.domElement.height - z, width, height, gl.RGBA, gl.UNSIGNED_BYTE, pixelBuffer );
//Convert RGB in the selected area back to color
for(var i=0; i<pixelBuffer.length; i+=4) {
if( 0 == pixelBuffer[i] && 0 == pixelBuffer[i+1] && 0 == pixelBuffer[i+2] && 0 == pixelBuffer[i+3] ) {
continue;
}
color = ( pixelBuffer[i] << 16 ) | ( pixelBuffer[i+1] << 8 ) | ( pixelBuffer[i+2] );
colors.push(color);
}
colors = _.unique(colors);
//Convert colors to ids
_.forEach(colors, function(color) {
ids.push(entitiesMap[color]);
});
return ids;
}
The line engine.getObjectsByGroup(inGroup) : engine.getRegisteredEntities();
just return an array of entities, which in turn, I iterate over the entities:
_.forEach(entities, function(entity) { ...
Only entities that have the 'threeRenderable' property (object) are visible, therefore, I ignore those that doesn't have it:
if(undefined == entity.threeRenderable) {
return ;
}
then I merge the entity's cloned mesh with with the pickingGeometry:
THREE.GeometryUtils.merge( pickingGeometry, cloneMesh);
eventually, I add the pickingGeometry to the pickingScene:
pickingScene.add( new THREE.Mesh( pickingGeometry, pickingMaterial ) );
Then I read the colors of the selected area, and return an array of IDs.
You can checkout the Node.js game engine I wrote back then.

I've wanted to implement something like this and I choose a very different method - maybe much worse, I don't really know - but much easier to do IMO, so I put it here in case someone wants it.
Basically, I used only 2 raycasts to know the first and last points of the selection rectangle, projected on my ground plane, and iterate over my objects to know which ones are in.
Some very basic code:
function onDocumentMouseDown(event) {
// usual Raycaster stuff ...
// get the ground intersection
var intersects = raycaster.intersectObject(ground);
GlobalGroundSelection = {
screen: { x: event.clientX, y: event.clientY },
ground: intersects[0].point
};
}
function onDocumentMouseUp(event) {
// ends a ground selection
if (GlobalGroundSelection) {
// usual Raycaster stuff ...
// get the ground intersection
var intersects = raycaster.intersectObjects(ground);
var selection = {
begins: GlobalGroundSelection.ground,
ends: intersects[0].point
};
GlobalGroundSelection = null;
selectCharactersInZone(selection.begins, selection.ends);
}
}
function onDocumentMouseMove(event) {
if (GlobalGroundSelection) {
// in a selection, draw a rectangle
var p1 = GlobalGroundSelection.screen,
p2 = { x: event.clientX, y: event.clientY };
/* with these coordinates
left: p1.x > p2.x ? p2.x : p1.x,
top: p1.y > p2.y ? p2.y : p1.y,
width: Math.abs(p1.x - p2.x),
height: Math.abs(p1.y - p2.y)
*/
}
}
Here is my select function:
function selectCharactersInZone (start, end) {
var selected = _.filter( SELECTABLE_OBJECTS , function(object) {
// warning: this ignore the Y elevation value
var itsin = object.position.x > start.x
&& object.position.z > start.z
&& object.position.x < end.x
&& object.position.z < end.z;
return itsin;
});
return selected;
}
Some warnings: as far as I know, this technique is only usable when you don't care about Y positions AND your selection is a basic rectangle.
My 2c

Related

How to get corners of shapes in threejs

I am developing a shape drawing tool in threejs. I have drawn line, square, circle, rectangle, triangle, ellipse etc. on mouse down + drag successfully. Now i want editing of these shapes, such as scale, move and rotate. How do I get the corners and put points at corners where user can mouse+drag to scale , move points etc.
function onMouseMove(event) {
if(edit) {
event.preventDefault();
if ( selectedObject ) {
selectedObject.material.color.set( '#49BFFE' );
selectedObject = null;
}
var intersects = getIntersects( event );
if ( intersects.length > 0 ) {
var res = intersects.filter( function ( res ) {
return res && res.object;
} )[ 0 ];
if ( res && res.object ) {
selectedObject = res.object;
selectedObject.material.color.set( '#f00' );
console.log(selectedObject)
}
}
}
}
Code to create shapes is as follows:
this.onMouseDown = function( relative ) {
var geometry = new THREE.PlaneBufferGeometry( 1, 1);
var material = new THREE.MeshBasicMaterial({ color: 0x49BFFE, wireframe: false });
square = new THREE.Mesh(geometry, material);
scope.group.add(square);
}
this.onDrag = function( clientX, clientY ) {
var scale = Math.abs(clientX) + Math.abs(clientY);
if(scale == 0) scale = scale + 0.1;
square.scale.set(scale, scale, 0.1);
}

ThreeJS - How do I scale down intersected object on "mouseout"

I have n+1 hexshapes in a honeycomb grid. The objects are stacked close together. With this code:
// Get intersected objects, a.k.a objects "hit" by mouse, a.k.a objects that are mouse-overed
const intersects = raycaster.intersectObjects(hexObjects);
// If there is one (or more) intersections
let scaleTween = null;
if (intersects.length > 0) {
// If mouse is not currently over an object
// Set cursor to pointer so that the user can see that the object is clickable
document.body.style.cursor = 'pointer';
// Get the last intersected object, it's most likely that object we are currently hovering
const is = intersects.length > 0 ? intersects.length - 1 : 0;
// Is the object hovered over for the first time?
if (INTERSECTED === null) {
// Save current hovered object
INTERSECTED = intersects[is].object;
// HIGHLIGHT
// Save current color
INTERSECTED.currentHex = INTERSECTED.material.color.getHex();
// Set highlight color
INTERSECTED.material.color.setHex(COLOR_HIGHLIGHT);
// SCALE UP
// Try to stop the current tween, if any, in progress, so we can proceed with the next, if any, tween
try {
scaleTween.stop();
} catch (e) {}
// Create tween, save it so we can try to stop it, if needed
scaleTween = scale_tween(
INTERSECTED,
INTERSECTED.scale.clone(),
{
x: 1.5,
y: 1.5
},
100
);
scaleTween.start();
// SET Z-INDEX
INTERSECTED.position.z = 10;
} else {
// If the mouse is over an object
// Do we have a previous hovered item?
if (INTERSECTED !== null) {
// Revert color
INTERSECTED.material.color.setHex(INTERSECTED.currentHex);
// SCALE DOWN
// Try to stop the current tween, if any, in progress, so we can proceed with the next, if any, tween
try {
scaleTween.stop();
} catch (e) {}
// Create tween, save it so we can try to stop it, if needed
scaleTween = scale_tween(
INTERSECTED,
INTERSECTED.scale.clone(),
{
x: 1,
y: 1
},
100
);
scaleTween.start();
// REVERT Z-INDEX
INTERSECTED.position.z = 1;
}
// Save current intersected object
INTERSECTED = intersects[is].object;
// HIGHLIGHT
// Save current color
INTERSECTED.currentHex = INTERSECTED.material.color.getHex();
// Set highlight color
INTERSECTED.material.color.setHex(COLOR_HIGHLIGHT);
// SCALE UP
// Try to stop the current tween, if any, in progress, so we can proceed with the next, if any, tween
try {
scaleTween.stop();
} catch (e) {}
// Create tween, save it so we can try to stop it, if needed
scaleTween = scale_tween(
INTERSECTED,
INTERSECTED.scale.clone(),
{
x: 1.5,
y: 1.5
},
100
);
scaleTween.start();
// SET Z-INDEX
INTERSECTED.position.z = 10;
}
} else {
// If there are no intersections
// Reset cursor
document.body.style.cursor = 'default';
// Restore previous intersection object (if it exists) to its original color
if (INTERSECTED !== null) {
// REVERT COLOR
INTERSECTED.material.color.setHex(INTERSECTED.currentHex);
// SCALE DOWN
// Try to stop the current tween, if any, in progress, so we can proceed with the next, if any, tween
try {
scaleTween.stop();
} catch (e) {}
// Create tween, save it so we can try to stop it, if needed
scaleTween = scale_tween(
INTERSECTED,
INTERSECTED.scale.clone(),
{
x: 1,
y: 1
},
100
);
scaleTween.start();
// REVERT "Z-INDEX"
INTERSECTED.position.z = 1;
}
// Remove previous intersection object reference by setting current intersection object to "nothing"
INTERSECTED = null;
}
I've managed to highlight the object and scale it up with a tween quite nicely, but when I move the mouse out of the object onto the next object (the scaled object is scaled over the next object a bit), the highlight is gone, but the scale persists. How do I manage to scale the object down? And preferably with a tween?
A pen for this code can be found here: https://codepen.io/phun-ky/pen/erBZZy, the relevant part is at about line 1284 or search for INTERSECTED.
I wrote my own one. It's hell imperfect, but, at least, it scales up and down the hexagons:
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 1, 1000);
camera.position.set(0, 0, 10);
var renderer = new THREE.WebGLRenderer({
antialias: true
});
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setClearColor(0x101010);
document.body.appendChild(renderer.domElement);
var hexes = [];
var colCount = 5;
var rowCount = 4;
var hexDiameter = 3;
var xStart = -(colCount) * hexDiameter * 0.5;
var rowSpace = Math.sqrt(3) * hexDiameter * 0.5;
var yStart = (rowCount - 1) * rowSpace * 0.5;
var hexGeom = new THREE.CylinderGeometry(hexDiameter * 0.5, hexDiameter * 0.5, 0.0625, 6, 1);
hexGeom.rotateX(Math.PI * 0.5);
for (let j = 0; j < rowCount; j++) {
for (let i = 0; i < colCount + (j % 2 === 0 ? 0 : 1); i++) {
let hex = new THREE.Mesh(hexGeom, new THREE.MeshBasicMaterial({
color: Math.random() * 0x7e7e7e + 0x7e7e7e,
wireframe: false
}));
hex.position.set(xStart + i * hexDiameter + (j % 2 === 0 ? 0.5 * hexDiameter : 0), yStart - j * rowSpace, 0);
hex.userData.scaleUp = function(h) {
if (h.userData.scaleDownTween) h.userData.scaleDownTween.stop();
let initScale = h.scale.clone();
let finalScale = new THREE.Vector3().setScalar(2);
h.userData.scaleUpTween = new TWEEN.Tween(initScale).to(finalScale, 500).onUpdate(function(obj) {
h.scale.copy(obj)
}).start();
}
hex.userData.scaleDown = function(h) {
if (h.userData.scaleUpTween) h.userData.scaleUpTween.stop();
let initScale = h.scale.clone();
let finalScale = new THREE.Vector3().setScalar(1);
h.userData.scaleUpTween = new TWEEN.Tween(initScale).to(finalScale, 500).onUpdate(function(obj) {
h.scale.copy(obj)
}).start();
}
scene.add(hex);
hexes.push(hex);
}
}
window.addEventListener("mousemove", onMouseMove, false);
var raycaster = new THREE.Raycaster();
var mouse = new THREE.Vector2();
var intersects = [];
var intersected;
function onMouseMove(event) {
mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
raycaster.setFromCamera(mouse, camera);
intersects = raycaster.intersectObjects(hexes);
if (intersects.length > 0) {
if (intersected != intersects[0].object) {
if (intersected) intersected.userData.scaleDown(intersected);
intersected = intersects[0].object;
intersected.userData.scaleUp(intersected);
}
} else {
if (intersected) intersected.userData.scaleDown(intersected);
intersected = null;
}
}
render();
function render() {
requestAnimationFrame(render);
TWEEN.update();
renderer.render(scene, camera);
}
body {
overflow: hidden;
margin: 0;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/92/three.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/tween.js/17.2.0/Tween.min.js"></script>

Object rotating while travelling along a curve path

I am creating a flight animation, I have objects (airplanes) moving along a curve path. My problem is some of my objects are rotating while travelling along the curve path.
Screenshot
function createPlane(image) {
return new THREE.Mesh(
new THREE.PlaneGeometry(5, 5),
new THREE.MeshPhongMaterial({ map: new THREE.TextureLoader().load('textures/planes/' + image), side: THREE.DoubleSide, transparent: true })
);
}
// this is how we get the line.vertices
// var curve = new THREE.Curve();
// line.vertices = curve.getPoints(50);
function animatePlane(plane, line, key) {
if (key > 1) {
key = 1;
} else {
key = Math.round(key * 100) / 100;
}
var curve = new THREE.CatmullRomCurve3(line.vertices), up = new THREE.Vector3(0, 1, 0), axis = new THREE.Vector3();
var angle, duration = 300, position = curve.getPointAt(key), tangent = curve.getTangentAt(key).normalize();
// rotation
axis.crossVectors(up, tangent).normalize();
angle = Math.acos(up.dot(tangent));
plane.quaternion.setFromAxisAngle(axis, angle);
// position
new TWEEN.Tween(plane.position)
.to({ x: position.x, y: position.y, z: position.z }, duration)
.onUpdate(function() {
plane.position.set(this.x, this.y, this.z);
})
.onComplete(function() {
if (key < 1) {
key += 0.02;
} else {
key = 0;
}
animatePlane(plane, line, key);
})
.start();
}
// lines
var point = latLongToVector3(14.512274, 121.016508, radius, 0), // Philippines
line1 = createCurveLine(createSphereArc(point, latLongToVector3(2.745364, 101.707079, radius, 0), 0.3), green, 'MALAYSIA');
animatePlane(createPlane('airplane.png'), line1, 0);
The above code is how I create and what moves my objects along the curve path.
Note that curve paths were dynamically created, from the screenshot above it is from Philippines going to other countries but it could be from other countries.
Curve paths may lie in different part of the earth, for example, from Australia (which is at the bottom part of the earth) going to other countries.
Is there anything I can do to always keep my airplane upright as it is moving along the curve path?
I got it working now by using Frenet–Serret formulas (TNB Frame).
I used Tangent, Normal, and Binormal to create a matrix (Matrix4) then used that matrix to set the rotation of planes by using .setRotationFromMatrix().
This is now the code to animate the planes:
function animatePlane(plane, line, key) {
if (key > 1) {
key = 1;
} else {
key = Math.round(key * 100) / 100;
}
var curve = new THREE.CatmullRomCurve3(line.vertices), matrix = new THREE.Matrix4();
var duration = 300, position = curve.getPointAt(key);
if ((key > 0) && (key < 1)) {
var D = curve.getPointAt(1).normalize(), X = plane.position.clone().normalize();
var T = position.clone().normalize(), B = curve.getTangentAt(key), N = new THREE.Vector3();
N.crossVectors(D, T).normalize();
matrix.set(
N.x, B.x, T.x, X.x,
N.y, B.y, T.y, X.y,
N.z, B.z, T.z, X.z,
0, 0, 0, 1
);
plane.setRotationFromMatrix(matrix);
}
new TWEEN.Tween(plane.position)
.to({ x: position.x, y: position.y, z: position.z }, duration)
.onUpdate(function() {
plane.position.set(this.x, this.y, this.z);
})
.onComplete(function() {
if (key < 1) {
key += 0.02;
} else {
key = 0;
}
animatePlane(plane, line, key);
})
.start();
}
and added this to make the plane above the curve path:
var plane1 = createPlane('airplane.png');
plane1.renderOrder = 1;
plane1.onBeforeRender = function(renderer) {
renderer.clearDepth();
}
animatePlane(plane1, line1, 0);

Why is OrbitControls not working as expected ? (Three.js)

I am using THREE.OrbitControls in my experimental project and have something very similar to this example.
I have different radio buttons at the top and I want to only enable THREE.OrbitControls if the rotate radio button is active.
I have replaced the code inside the if statement form the code Pen example:
function doMouseMove(x,y,evt,prevX,prevY) {
if (mouseAction == ROTATE) {
var dx = x - prevX;
world.rotateY( dx/200 );
render();
}
with:
function doMouseMove(x,y,evt,prevX,prevY) {
if (mouseAction == ROTATE) {
controls = new THREE.OrbitControls(camera, canvas);
controls.rotateSpeed = 0.1;
controls.zoomSpeed = 1;
controls.update();
}
This works perfectly, however once I go back from the rotate button to the drag button (or any other button), the OrbitControls is still active, and the camera moves with the object being dragged/added/removed.
This was not the case with the original example (as can be seen) and so I was wondering if I have to add further functionality to disable the OrbitControls.
I have tried:
controls.reset();
However, the orbitControls is still active even after the rotate radio button is not pressed!
I would like to add that the orbitControls is not active (as expected) when the page is reloaded on the drag button (or any other button). However once the rotate button has been pressed, it remains active throughout the session regardless of which input is pressed.
Any pointers on how I can solve this functionality?
The following is the full code outline from the example (excluding HTML file with references) of the code:
var canvas, scene, renderer, camera, controls;
var raycaster; // A THREE.Raycaster for user mouse input.
var ground; // A square base on which the cylinders stand.
var cylinder; // A cylinder that will be cloned to make the visible
cylinders.
var world;
var ROTATE = 1,
DRAG = 2,
ADD = 3,
DELETE = 4; // Possible mouse actions
var mouseAction; // currently selected mouse action
var dragItem; // the cylinder that is being dragged, during a drag operation
var intersects; //the objects intersected
var targetForDragging; // An invisible object that is used as the target for
raycasting while
// call functions to initialise trackballcontrols
init();
// animate();
function init() {
canvas = document.getElementById("maincanvas");
renderer = new THREE.WebGLRenderer({
canvas: canvas,
antialias: true
});
document.getElementById("mouseDrag").checked = true;
mouseAction = DRAG;
document.getElementById("mouseRotate").onchange = doChangeMouseAction;
document.getElementById("mouseDrag").onchange = doChangeMouseAction;
document.getElementById("mouseAdd").onchange = doChangeMouseAction;
document.getElementById("mouseDelete").onchange = doChangeMouseAction;
createWorld();
setUpMouseHander(canvas, doMouseDown, doMouseMove);
setUpTouchHander(canvas, doMouseDown, doMouseMove);
raycaster = new THREE.Raycaster();
render();
}
// loop that causes the renderer to draw the scene 60 times per second.
function render() {
renderer.render(scene, camera);
}
function createWorld() {
renderer.setClearColor(0x222222);
// First parameter is FOV in degrees. Second: Aspect ratio. Third/Fourth:
Near/Far clipping plane
camera = new THREE.PerspectiveCamera(37, canvas.width / canvas.height, 1,
10000);
camera.position.z = 5;
camera.position.y = 60;
/**Creating the scene */
scene = new THREE.Scene();
camera.lookAt(new THREE.Vector3(0, 1, 0));
camera.add(new THREE.PointLight(0xffffff, 0.7)); // point light at camera
position
scene.add(camera);
scene.add(new THREE.DirectionalLight(0xffffff, 0.5)); // light shining from
above.
world = new THREE.Object3D();
ground = new THREE.Mesh(
new THREE.BoxGeometry(40, 1, 40),
new THREE.MeshLambertMaterial({ color: "gray" })
);
ground.position.y = -0.5; // top of base lies in the plane y = -5;
world.add(ground);
targetForDragging = new THREE.Mesh(
new THREE.BoxGeometry(1000, 0.01, 1000),
new THREE.MeshBasicMaterial()
);
targetForDragging.material.visible = false;
cylinder = new THREE.Mesh(
new THREE.CylinderGeometry(1, 2, 6, 16, 32),
new THREE.MeshLambertMaterial({ color: "yellow" })
);
cylinder.position.y = 3; // places base at y = 0;
addCylinder(10, 10);
addCylinder(0, 15);
addCylinder(-15, -7);
addCylinder(-8, 5);
addCylinder(5, -12);
}
function addCylinder(x, z) {
var obj = cylinder.clone();
obj.position.x = x;
obj.position.z = z;
world.add(obj);
}
function doMouseDown(x, y) {
//enable rotate
if (mouseAction == ROTATE) {
return true;
}
if (mouseAction != ROTATE) {
controls = 0;
controls.enabled = false;
}
// Affecting drag function
if (targetForDragging.parent == world) {
world.remove(targetForDragging); // I don't want to check for hits on
targetForDragging
}
var a = 2 * x / canvas.width - 1;
var b = 1 - 2 * y / canvas.height;
raycaster.setFromCamera(new THREE.Vector2(a, b), camera);
intersects = raycaster.intersectObjects(world.children); // no need for
recusion since all objects are top-level
if (intersects.length == 0) {
return false;
}
var item = intersects[0];
var objectHit = item.object;
switch (mouseAction) {
case DRAG:
if (objectHit == ground) {
return false;
} else {
dragItem = objectHit;
world.add(targetForDragging);
targetForDragging.position.set(0, item.point.y, 0);
render();
return true;
}
case ADD:
if (objectHit == ground) {
var locationX = item.point.x; // Gives the point of intersection
in world coords
var locationZ = item.point.z;
var coords = new THREE.Vector3(locationX, 0, locationZ);
world.worldToLocal(coords); // to add cylider in correct
position, neew local coords for the world object
addCylinder(coords.x, coords.z);
render();
}
return false;
default: // DELETE
if (objectHit != ground) {
world.remove(objectHit);
render();
}
return false;
}
}
//this function is used when dragging OR rotating
function doMouseMove(x, y, evt, prevX, prevY) {
if (mouseAction == ROTATE) {
controls = new THREE.OrbitControls(camera, canvas);
controls.rotateSpeed = 0.1;
controls.zoomSpeed = 1;
controls.addEventListener('change', render, renderer.domElement);
controls.update();
} else { // drag
var a = 2 * x / canvas.width - 1;
var b = 1 - 2 * y / canvas.height;
raycaster.setFromCamera(new THREE.Vector2(a, b), camera);
intersects = raycaster.intersectObject(targetForDragging);
if (intersects.length == 0) {
return;
}
var locationX = intersects[0].point.x;
var locationZ = intersects[0].point.z;
var coords = new THREE.Vector3(locationX, 0, locationZ);
world.worldToLocal(coords);
a = Math.min(19, Math.max(-19, coords.x)); // clamp coords to the range
-19 to 19, so object stays on ground
b = Math.min(19, Math.max(-19, coords.z));
dragItem.position.set(a, 3, b);
render();
}
}
function doChangeMouseAction() {
if (document.getElementById("mouseRotate").checked) {
mouseAction = ROTATE;
} else if (document.getElementById("mouseDrag").checked) {
mouseAction = DRAG;
} else if (document.getElementById("mouseAdd").checked) {
mouseAction = ADD;
} else {
mouseAction = DELETE;
}
}
window.requestAnimationFrame =
window.requestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.msRequestAnimationFrame ||
window.oRequestAnimationFrame ||
function(callback) {
setTimeout(function() {
callback(Date.now());
}, 1000 / 60);
};
function setUpMouseHander(element, mouseDownFunc, mouseDragFunc,
mouseUpFunc) {
if (!element || !mouseDownFunc || !(typeof mouseDownFunc == "function")) {
throw "Illegal arguments in setUpMouseHander";
}
if (typeof element == "string") {
element = document.getElementById(element);
}
if (!element || !element.addEventListener) {
throw "first argument in setUpMouseHander is not a valid element";
}
var dragging = false;
var startX, startY;
var prevX, prevY;
function doMouseDown(evt) {
if (dragging) {
return;
}
var r = element.getBoundingClientRect();
var x = evt.clientX - r.left;
var y = evt.clientY - r.top;
prevX = startX = x;
prevY = startY = y;
dragging = mouseDownFunc(x, y, evt);
if (dragging) {
document.addEventListener("mousemove", doMouseMove);
document.addEventListener("mouseup", doMouseUp);
}
}
function doMouseMove(evt) {
if (dragging) {
if (mouseDragFunc) {
var r = element.getBoundingClientRect();
var x = evt.clientX - r.left;
var y = evt.clientY - r.top;
mouseDragFunc(x, y, evt, prevX, prevY, startX, startY);
}
prevX = x;
prevY = y;
}
}
function doMouseUp(evt) {
if (dragging) {
document.removeEventListener("mousemove", doMouseMove);
document.removeEventListener("mouseup", doMouseUp);
if (mouseUpFunc) {
var r = element.getBoundingClientRect();
var x = evt.clientX - r.left;
var y = evt.clientY - r.top;
mouseUpFunc(x, y, evt, prevX, prevY, startX, startY);
}
dragging = false;
}
}
element.addEventListener("mousedown", doMouseDown);
}
function setUpTouchHander(element, touchStartFunc, touchMoveFunc, t
touchEndFunc, touchCancelFunc) {
if (!element || !touchStartFunc || !(typeof touchStartFunc == "function")) {
throw "Illegal arguments in setUpTouchHander";
}
if (typeof element == "string") {
element = document.getElementById(element);
}
if (!element || !element.addEventListener) {
throw "first argument in setUpTouchHander is not a valid element";
}
var dragging = false;
var startX, startY;
var prevX, prevY;
function doTouchStart(evt) {
if (evt.touches.length != 1) {
doTouchEnd(evt);
return;
}
evt.preventDefault();
if (dragging) {
doTouchEnd();
}
var r = element.getBoundingClientRect();
var x = evt.touches[0].clientX - r.left;
var y = evt.touches[0].clientY - r.top;
prevX = startX = x;
prevY = startY = y;
dragging = touchStartFunc(x, y, evt);
if (dragging) {
element.addEventListener("touchmove", doTouchMove);
element.addEventListener("touchend", doTouchEnd);
element.addEventListener("touchcancel", doTouchCancel);
}
}
function doTouchMove(evt) {
if (dragging) {
if (evt.touches.length != 1) {
doTouchEnd(evt);
return;
}
evt.preventDefault();
if (touchMoveFunc) {
var r = element.getBoundingClientRect();
var x = evt.touches[0].clientX - r.left;
var y = evt.touches[0].clientY - r.top;
touchMoveFunc(x, y, evt, prevX, prevY, startX, startY);
}
prevX = x;
prevY = y;
}
}
function doTouchCancel() {
if (touchCancelFunc) {
touchCancelFunc();
}
}
function doTouchEnd(evt) {
if (dragging) {
dragging = false;
element.removeEventListener("touchmove", doTouchMove);
element.removeEventListener("touchend", doTouchEnd);
element.removeEventListener("touchcancel", doTouchCancel);
if (touchEndFunc) {
touchEndFunc(evt, prevX, prevY, startX, startY);
}
}
}
element.addEventListener("touchstart", doTouchStart);
}
You can instantiate controls once:
controls = new THREE.OrbitControls(camera, canvas);
controls.enableZoom = false;
controls.enablePan = false;
controls.enableRotate = false;
and then just switch controls.enableRotate between true and false. For example, in the doChangeMouseAction() function. Creativity is up to you.

Clickable mesh with THREE.lod

I for now work on a 3D engine for an amateur video game, and i want to add to him tons of features to get the best performances & gameplay. But i've got serious problems to get an "on click event" with lod meshes.
For the "lod" part, no problem for now, he's integrated, but i found no solution to apply that exemple with him :
https://stackoverflow.com/a/12808987/3379444
Did i must push the "lod" object, or every mesh individually ?
a part of my code :
var i, j, mesh, lod;
for ( j = 0; j <= 42; j++) {
lod = new THREE.LOD();
//Here, it's just var for place my mesh into a circle
rayonSysteme = Math.floor(Math.random() * 10000) + 2500;
angleSysteme = Math.random() * 360;
for ( i = 0; i < geometry.length; i ++ ) {
mesh = new THREE.Mesh( geometry[ i ][ 0 ], material );
mesh.scale.set( 1.5, 1.5, 1.5 );
mesh.updateMatrix();
mesh.matrixAutoUpdate = false;
//Callback here ?
mesh.callback = function() { console.log( this.name ); }
lod.addLevel( mesh, geometry[ i ][ 1 ] );
}
lod.position.x = rayonSysteme * Math.cos(angleSysteme);
lod.position.y = Math.floor(Math.random() * 1000)-500;
lod.position.z = rayonSysteme * Math.sin(angleSysteme);
//Or here ?
lod.updateMatrix();
lod.matrixAutoUpdate = false;
gravity = drawCircle("XZ", 64, 500, 360);
gravity.position.x = lod.position.x;
gravity.position.y = lod.position.y;
gravity.position.z = lod.position.z;
scene.add( lod );
//objects.push( lod );
scene.add( gravity );
};
Raycaster will test the mesh that should be used based on the distance to the ray origin.
This is the relevant code inside Raycaster:
} else if ( object instanceof THREE.LOD ) {
matrixPosition.setFromMatrixPosition( object.matrixWorld );
var distance = raycaster.ray.origin.distanceTo( matrixPosition );
intersectObject( object.getObjectForDistance( distance ), raycaster, intersects );
}

Resources