Three.js Pinch Zoom - javascript

I want to add Pinch Zoom to my three.js panorama player.
I've had a look around and it seems TrackBallControls.js might have this build in?
I've tried implementing it, however I'm getting a
'Uncaught TypeError: undefined is not a function'
on
var controls = new THREE.TrackballControls( camera );
Now because of the system I'm having to load all Three.js scripts dynamically using Sid.js, would this be a reason why it can't seem to find THREE.TrackballControls?
Is this even the right solution for pinch and zoom on mobile in Three?

TrackBallControls.js does have touch zoom built in, relevant code below; however the files is not a part of the Three.js library. It only exists in the example projects. You can find the source code here.
function touchstart( event ) {
if ( _this.enabled === false ) return;
switch ( event.touches.length ) {
case 1:
_state = STATE.TOUCH_ROTATE;
_rotateStart.copy( getMouseProjectionOnBall( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY ) );
_rotateEnd.copy( _rotateStart );
break;
case 2:
_state = STATE.TOUCH_ZOOM_PAN;
var dx = event.touches[ 0 ].pageX - event.touches[ 1 ].pageX;
var dy = event.touches[ 0 ].pageY - event.touches[ 1 ].pageY;
_touchZoomDistanceEnd = _touchZoomDistanceStart = Math.sqrt( dx * dx + dy * dy );
var x = ( event.touches[ 0 ].pageX + event.touches[ 1 ].pageX ) / 2;
var y = ( event.touches[ 0 ].pageY + event.touches[ 1 ].pageY ) / 2;
_panStart.copy( getMouseOnScreen( x, y ) );
_panEnd.copy( _panStart );
break;
default:
_state = STATE.NONE;
}
_this.dispatchEvent( startEvent );
}
function touchmove( event ) {
if ( _this.enabled === false ) return;
event.preventDefault();
event.stopPropagation();
switch ( event.touches.length ) {
case 1:
_rotateEnd.copy( getMouseProjectionOnBall( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY ) );
break;
case 2:
var dx = event.touches[ 0 ].pageX - event.touches[ 1 ].pageX;
var dy = event.touches[ 0 ].pageY - event.touches[ 1 ].pageY;
_touchZoomDistanceEnd = Math.sqrt( dx * dx + dy * dy );
var x = ( event.touches[ 0 ].pageX + event.touches[ 1 ].pageX ) / 2;
var y = ( event.touches[ 0 ].pageY + event.touches[ 1 ].pageY ) / 2;
_panEnd.copy( getMouseOnScreen( x, y ) );
break;
default:
_state = STATE.NONE;
}
}
function touchend( event ) {
if ( _this.enabled === false ) return;
switch ( event.touches.length ) {
case 1:
_rotateEnd.copy( getMouseProjectionOnBall( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY ) );
_rotateStart.copy( _rotateEnd );
break;
case 2:
_touchZoomDistanceStart = _touchZoomDistanceEnd = 0;
var x = ( event.touches[ 0 ].pageX + event.touches[ 1 ].pageX ) / 2;
var y = ( event.touches[ 0 ].pageY + event.touches[ 1 ].pageY ) / 2;
_panEnd.copy( getMouseOnScreen( x, y ) );
_panStart.copy( _panEnd );
break;
}
_state = STATE.NONE;
_this.dispatchEvent( endEvent );
}

If anyone else needs the code I modified it a bit
function onDocumentTouchStart(event) {
if (event.touches.length == 1) {
console.log('1');
event.preventDefault();
onPointerDownPointerX = event.touches[0].pageX;
onPointerDownPointerY = event.touches[0].pageY;
onPointerDownLon = lon;
onPointerDownLat = lat;
detectHotspotClick();
}
if (event.touches.length == 2) {
console.log('2');
_state = STATE.TOUCH_ZOOM_PAN;
var dx = event.touches[ 0 ].pageX - event.touches[ 1 ].pageX;
var dy = event.touches[ 0 ].pageY - event.touches[ 1 ].pageY;
_touchZoomDistanceEnd = _touchZoomDistanceStart = Math.sqrt( dx * dx + dy * dy );
}
}
function onDocumentTouchMove(event) {
if (event.touches.length == 1) {
event.preventDefault();
lon = (onPointerDownPointerX - event.touches[0].pageX) * 0.1 + onPointerDownLon;
lat = (event.touches[0].pageY - onPointerDownPointerY) * 0.1 + onPointerDownLat;
}
if (event.touches.length == 2) {
var dx = event.touches[ 0 ].pageX - event.touches[ 1 ].pageX;
var dy = event.touches[ 0 ].pageY - event.touches[ 1 ].pageY;
_touchZoomDistanceEnd = Math.sqrt( dx * dx + dy * dy );
var factor = _touchZoomDistanceStart / _touchZoomDistanceEnd;
_touchZoomDistanceStart = _touchZoomDistanceEnd;
setZoom(camera.fov * factor);
}
}
function onDocumentTouchEnd( event ) {
_touchZoomDistanceStart = _touchZoomDistanceEnd = 0;
}
function setZoom(fov){
camera.fov = fov;
if(camera.fov < 30) camera.fov = 30;
if(camera.fov > 100) camera.fov = 100;
camera.updateProjectionMatrix();
}

Related

Can't line ellipse with the coordinates of a vector

I'm trying to line up some ellipse with the endpoint of a line that moves and rotates, and the end goal is to be able to calculate the coordinates of said endpoint. I can calculate the initial position, but as soon as I move the car all of the ellipses go in the opposite direction. I'm not great at geometry and I can't figure out why it's doing this or how to fix it.
What I have so far:
sketch.js
// Declares global variables
const h = window.innerHeight;
const w = window.innerWidth;
var car;
var borders = [];
var pos = {
x: w / 2,
y: h / 2,
angle: 0
}
function setup () {
// Creates the canvas
background ( '#000000' );
createCanvas ( w, h );
angleMode ( DEGREES );
rectMode ( CENTER );
stroke ( 255 );
// Creates some borders
borders.push ( new Border ( 0, height / 2, width, height / 2 ) );
// Creates the car
car = new Car();
}
function draw () {
// Clears the canvas
background ( '#000000' );
fill("white");
// Displays the borders
for ( var border of borders ) {
border.show();
}
// Moves the car
if ( keyIsDown ( UP_ARROW ) ) {
car.forward();
}
if ( keyIsDown ( DOWN_ARROW ) ) {
car.backward();
}
// Checks if the car is intersecting
const intersection = car.intersects ( borders );
car.emit(borders);
if ( intersection ) {
fill("red");
}
// Displays the car on the canvas
translate(pos.x, pos.y);
rotate(pos.angle);
car.show();
}
car.js
class Car {
// Defines the class
constructor ( ) {
this.pos = createVector ( 0, 0 );
this.width = 20;
this.length = 40;
this.speed = 0;
this.rays = [];
// Creates the car's rays
for ( var x = 0; x < 360; x += 20 ) {
this.rays.push ( new Ray ( createVector ( this.pos.x, this.pos.y ), radians ( x ) ) );
}
}
// Displays the car on the canvas
show () {
// Displays the car
rect ( this.pos.x, this.pos.y, this.width, this.length );
// Displays the rays
for ( var ray of this.rays ) {
ray.show();
}
}
// Checks if any rays are intersecting a border
emit ( borders ) {
// Loops through all the rays
for ( var ray of this.rays ) {
// Loops through all the borders
for ( var border of borders ) {
const intersection = ray.intersects ( border );
if ( intersection ) {
console.log(intersection);
}
}
}
}
// Moves forward
forward () {
pos.y -= cos(pos.angle) * 4;
pos.x += sin(pos.angle) * 4;
// Steers left
if ( keyIsDown ( LEFT_ARROW ) ) {
pos.angle -= 3;
}
// Steers right
if ( keyIsDown ( RIGHT_ARROW ) ) {
pos.angle += 3;
}
}
// Moves backward
backward () {
pos.y += cos(pos.angle) * 4;
pos.x -= sin(pos.angle) * 4;
// Steers left
if ( keyIsDown ( LEFT_ARROW ) ) {
pos.angle -= 3;
}
// Steers right
if ( keyIsDown ( RIGHT_ARROW ) ) {
pos.angle += 3;
}
}
// Checks if the car is intersecting any borders
intersects ( borders ) {
// Calculates the car's corners
var corners = [
createVector ( pos.x - ( this.width / 2 ), pos.y - ( this.length / 2 ) ),
createVector ( pos.x + ( this.width / 2 ), pos.y - ( this.length / 2 ) ),
createVector ( pos.x + ( this.width / 2 ), pos.y + ( this.length / 2 ) ),
createVector ( pos.x - ( this.width / 2 ), pos.y + ( this.length / 2 ) )
];
var sides = [
[
corners[0],
corners[1]
],
[
corners[1],
corners[2]
],
[
corners[2],
corners[3]
],
[
corners[3],
corners[0]
]
];
// Loops through each side
for ( var side of sides ) {
// Loops through each border
for ( var border of borders ) {
var x1 = side[0].x;
var y1 = side[0].y;
var x2 = side[1].x;
var y2 = side[1].y;
const x3 = border.x.x;
const y3 = border.x.y;
const x4 = border.y.x;
const y4 = border.y.y;
// Rotates the corners relative to the car
var tempX1 = x1 - pos.x;
var tempY1 = y1 - pos.y;
var tempX2 = x2 - pos.x;
var tempY2 = y2 - pos.y;
var rotatedX1 = tempX1 * cos ( pos.angle ) - tempY1 * sin ( pos.angle );
var rotatedY1 = tempX1 * sin ( pos.angle ) + tempY1 * cos ( pos.angle );
var rotatedX2 = tempX2 * cos ( pos.angle ) - tempY2 * sin ( pos.angle );
var rotatedY2 = tempX2 * sin ( pos.angle ) + tempY2 * cos ( pos.angle );
x1 = rotatedX1 + pos.x;
y1 = rotatedY1 + pos.y;
x2 = rotatedX2 + pos.x;
y2 = rotatedY2 + pos.y;
// Checks if the car is intersecting
const d = ( x1 - x2 ) * ( y3 - y4 ) - ( y1 - y2 ) * ( x3 - x4 );
const t = ( x1 - x3 ) * ( y3 - y4 ) - ( y1 - y3 ) * ( x3 - x4 ) / d;
if ( 0 <= t && t <= 1 ) {
return createVector ( x1 + t * ( x2 - x1 ), y1 + t * ( y2 - y1 ) );
}
}
}
}
}
ray.js
class Ray {
// Defines the class
constructor ( position, angle ) {
this.pos = position;
this.angle = angle;
this.dir = p5.Vector.fromAngle ( angle );
}
// Checks if the ray is intersecting a border
intersects ( border ) {
// const endpoints = [
// ];
var x1 = pos.x;
var y1 = pos.y;
var x2 = this.dir.x * 100;
var y2 = this.dir.y * 100;
const x3 = border.x.x;
const y3 = border.x.y;
const x4 = border.y.x;
const y4 = border.y.y;
// Rotates the corners relative to the car
var tempX2 = x2 - pos.x;
var tempY2 = y2 - pos.y;
var rotatedX2 = tempX2 * cos ( pos.angle ) - tempY2 * sin ( pos.angle );
var rotatedY2 = tempX2 * sin ( pos.angle ) + tempY2 * cos ( pos.angle );
x2 = rotatedX2 + pos.x;
y2 = rotatedY2 + pos.y;
/* This is where I'm having difficulty */
push();
translate(pos.x, pos.y);
ellipse(x2, y2, 10);
pop();
// const den = ( ( x1 - x2 ) * ( y3 - y4 ) - ( y1 - y2 ) * ( x3 - x4 ) );
// if ( den == 0 ) {
// console.log(1);
// return;
// }
// var t = ( ( x1 - x3 ) * ( y3 - y4 ) - ( y1 - y3 ) * ( x3 - x4 ) ) / den;
// var u = ( ( x2 - x1 ) * ( y1 - y3 ) - ( y2 - y1 ) * ( x1 - x3 ) ) / den;
// if ( 0 <= t && t <= 1 ) {
// const x = x1 + t * ( x2 - x1 );
// const y = y1 + t * ( y2 - y1 );
// // return createVector ( x, y );
// ellipse ( x, y, 10 );
// }
// return;
}
// Displays the ray on the canvas
show () {
push();
translate(this.pos.x, this.pos.y);
line(0, 0, this.dir.x * 100, this.dir.y * 100);
pop();
}
}
border.js
class Border {
// Defines the class
constructor ( x1, y1, x2, y2 ) {
this.x = createVector ( x1, y1 );
this.y = createVector ( x2, y2 );
}
show () {
stroke ( 255 );
line ( this.x.x, this.x.y, this.y.x, this.y.y );
}
}
My interpretation is that in lines 31-36 of ray.js, you're trying to rotate the point x2, y2 by pos.angle. You actually don't need to do any trig in order to do that.
In the trig, you subtracted off pos from this.dir. But this.dir was already a vector whose base was at the position of the car, so the coordinates of this.dir were already relative to the position of the car. Here is one solution, where x2 and y2 are initialized with the rotation:
var x2 = this.dir.x * 100 * cos ( pos.angle ) -
this.dir.y * 100 * sin ( pos.angle );
var y2 = this.dir.x * 100 * sin ( pos.angle ) +
this.dir.y * 100 * cos ( pos.angle );
As I said above, though, you don't have to do any trig. You can make use of the .rotate() method:
var x2 = this.dir.copy().rotate(radians(pos.angle)).x * 100;
var y2 = this.dir.copy().rotate(radians(pos.angle)).y * 100;
NOTE: you need to convert to radians when using .rotate() and other vector methods because angleMode() does not affect vectors.
In both of these solutions, you should get rid of lines 31-36.
I think you might run into problems, however, when you try to apply this. Since x2 and y2 are relative to the car's position, they are not the actual endpoints of the lines drawn on the screen. Rather, they are in a circle of points up by the top left corner. If you were to try to test whether the line segment between x1, y1 and x2, y2 intersects with the line segment between x3, y3 and x4, y4, you would get that it only intersects when the border is between pos and the top left corner.
I think what you're actually looking for is
var x2 = this.dir.copy().rotate(radians(pos.angle)).x + pos.x;
var y2 = this.dir.copy().rotate(radians(pos.angle)).y + pos.y;
NOTE: you would also have to get rid of the translation when you display the ellipses in order for this to look right.
When I uncomment your other code in ray.js, it behaves better in this last version.
Additionally, you seem to multiply this.dir.x and this.dir.y by 100 every time you refer to them. You can omit this if you make this.dir 100 pixels long, which is built into the .fromAngle() method:
this.dir = p5.Vector.fromAngle(angle, 100);
will give you a vector in the proper direction with length 100.

How to simulate the wave effect to the content with threejs wave background

I need to simulate the wave effect to the content on top of the wave background using three.js
In my case, the wave effect is working as my expectation but I need the contents will be up and down with the background effect. Like boats in sea waves
Mostly, you can achieve that by adding the wave effects to the background of the scene.
This link may help you
https://codepen.io/mweslander/pen/JreWPa
const SEPARATION = 100, AMOUNTX = 50, AMOUNTY = 50;
let container, stats;
let camera, scene, renderer;
let particles, particle, count = 0;
let mouseX = 0, mouseY = 0;
let windowHalfX = window.innerWidth / 2;
let windowHalfY = window.innerHeight / 2;
function init() {
container = document.createElement( 'div' );
document.body.appendChild(container);
camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 1, 10000 );
camera.position.z = 1000; // Good var to change
scene = new THREE.Scene();
particles = new Array();
var PI2 = Math.PI * 2;
var geometry = new THREE.Geometry();
var material = new THREE.SpriteCanvasMaterial({
color: 0xffffff,
program: function ( context ) {
context.beginPath();
context.arc( 0, 0, 0.4, 0, PI2, true );
context.fill();
}
});
var i = 0;
for ( var ix = 0; ix < AMOUNTX; ix ++ ) {
for ( var iy = 0; iy < AMOUNTY; iy ++ ) {
particle = particles[ i ++ ] = new THREE.Sprite( material );
particle.position.x = ix * SEPARATION - ( ( AMOUNTX * SEPARATION ) / 2 );
particle.position.z = iy * SEPARATION - ( ( AMOUNTY * SEPARATION ) / 2 );
scene.add(particle);
if (i > 0) {
geometry.vertices.push( particle.position );
}
}
}
renderer = new THREE.CanvasRenderer();
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);
container.appendChild(renderer.domElement);
stats = new Stats();
container.appendChild( stats.dom );
document.addEventListener( 'mousemove', onDocumentMouseMove, false );
document.addEventListener( 'touchstart', onDocumentTouchStart, false );
document.addEventListener( 'touchmove', onDocumentTouchMove, false );
//
window.addEventListener( 'resize', onWindowResize, false );
}
function onWindowResize() {
windowHalfX = window.innerWidth / 2;
windowHalfY = window.innerHeight / 2;
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize( window.innerWidth, window.innerHeight );
}
function onDocumentMouseMove(event) {
mouseX = event.clientX - windowHalfX;
mouseY = event.clientY - windowHalfY;
}
function onDocumentTouchStart(event) {
if (event.touches.length === 1) {
event.preventDefault();
mouseX = event.touches[ 0 ].pageX - windowHalfX;
mouseY = event.touches[ 0 ].pageY - windowHalfY;
}
}
function onDocumentTouchMove( event ) {
if (event.touches.length === 1) {
event.preventDefault();
mouseX = event.touches[ 0 ].pageX - windowHalfX;
mouseY = event.touches[ 0 ].pageY - windowHalfY;
}
}
function animate() {
requestAnimationFrame( animate );
render();
stats.update();
}
function render() {
renderer.setClearColor( 0x07074e, 1);
camera.position.x += ( mouseX - camera.position.x ) * .05;
camera.position.y += ( - mouseY - camera.position.y ) * .05;
camera.lookAt( scene.position );
var i = 0;
for (var ix = 0; ix < AMOUNTX; ix++) {
for (var iy = 0; iy < AMOUNTY; iy++) {
particle = particles[i++];
particle.position.y = (Math.sin((ix + count) * 0.3) * 50) + (Math.sin((iy + count) * 0.5) * 50);
particle.scale.x = particle.scale.y = (Math.sin((ix + count) * 0.3) + 1) * 4 + (Math.sin((iy + count) * 0.5) + 1) * 4;
}
}
renderer.render(scene, camera);
count += 0.1;
}
init();
animate();

Optimize hundreds of dynamic lines in Three.js

I'm new to Three.js, I'm just having fun with it. I'm trying to achieve a simple dynamic background fullscreen on a page, you get the example here:
function createHexagon( vertices, color ) {
var geometry = new THREE.BufferGeometry();
geometry.addAttribute( 'position', new THREE.BufferAttribute( vertices, 3 ) );
var material = new THREE.LineBasicMaterial( { color: color, opacity: Math.min((Math.random() / 5), 0.1), transparent: true } );
var hexagon = new THREE.Line( geometry, material );
return hexagon;
}
function initMatrix() {
var color = defaultColor.getHex();
var vertices;
var x = ( width / -2 ) - 90;
var y = height / -2;
var deltaX = 120;
var deltaY = 60;
var time = 5.0;
while( y < height / 2 ) {
while( x < width / 2 ) {
vertices = new Float32Array([
0, 30, 0,
20, 0, 0
]);
var hexagon = createHexagon( vertices, color );
scene.add( hexagon );
hexagon.position.set( x, y, 0 );
vertices = new Float32Array([
20, 0, 0,
60, 0, 0
]);
var hexagon = createHexagon( vertices, color );
scene.add( hexagon );
hexagon.position.set( x, y, 0 );
vertices = new Float32Array([
60, 0, 0,
80, 30, 0
]);
var hexagon = createHexagon( vertices, color );
scene.add( hexagon );
hexagon.position.set( x, y, 0 );
x += deltaX;
}
x = ( width / -2 ) - 90;
y += deltaY;
}
x = ( width / -2 ) - 30;
y = ( height / -2 ) - 30;
deltaX = 120;
deltaY = 60;
while( y < height / 2 ) {
while( x < width / 2 ) {
vertices = new Float32Array([
0, 30, 0,
20, 0, 0
]);
var hexagon = createHexagon( vertices, color );
scene.add( hexagon );
hexagon.position.set( x, y, 0 );
vertices = new Float32Array([
20, 0, 0,
60, 0, 0
]);
var hexagon = createHexagon( vertices, color );
scene.add( hexagon );
hexagon.position.set( x, y, 0 );
vertices = new Float32Array([
60, 0, 0,
80, 30, 0
]);
var hexagon = createHexagon( vertices, color );
scene.add( hexagon );
hexagon.position.set( x, y, 0 );
x += deltaX;
}
x = ( width / -2 ) - 30;
y += deltaY;
}
}
Those are single bufferGeometry lines (as you can see in the above functions to create the background) that randomly rotate and change opacity on mouse hover with raycaster and TweenLite. It works pretty fine. What you can notice is that CPU usage goes to almost 100%.
I know that if I group lines into the same geometry it'll be better on performance, but then I'm not able to animate single lines with raycaster, especially the opacity.
I searched a lot of discussions and tried so many things. The best result is this way, rendering single lines separately. Can you suggest some tips about it?
(Posted on behalf of the question author).
I found a solution. I made a whole geometry and I indexed vertices and respective colors. The only problem is that I can't mange opacity this way, but it works fine and CPU is around 20%.
document.addEventListener( 'DOMContentLoaded', main );
var width = screen.width;
var height = screen.height;
var camera, scene, renderer, raycaster, mouse;
var rayColor = new THREE.Color( 0x0640C2 );
var rayColor2 = new THREE.Color( 0xCC311B );
var colors;
var linesPositions, originalPositions;
var linesMesh;
var geometry;
var intersects;
var stats;
function initMatrix() {
var AlinesPositions = [ ];
var x = ( width / - 2 ) - 80;
var y = ( height / - 2 ) - 60;
var deltaX = 120;
var deltaY = 60;
while( y <= ( height / 2 ) ) {
while( x <= ( width / 2 ) ) {
AlinesPositions.push( x, y, 0 /*Math.abs( ( 100 / ( width / 2 ) * x ) + ( 100 / ( height / 2 ) * y ) )*/ );
AlinesPositions.push( x + 80, y, 0 /*Math.abs( ( 100 / ( width / 2 ) * x ) + ( 100 / ( height / 2 ) * y ) )*/ );
AlinesPositions.push( x + 80, y, 0 /*Math.abs( ( 100 / ( width / 2 ) * x ) + ( 100 / ( height / 2 ) * y ) )*/ );
AlinesPositions.push( x + 80, y + 60, 0 /*Math.abs( ( 100 / ( width / 2 ) * x ) + ( 100 / ( height / 2 ) * y ) )*/ );
AlinesPositions.push( x + 80, y + 60, 0 /*Math.abs( ( 100 / ( width / 2 ) * x ) + ( 100 / ( height / 2 ) * y ) )*/ );
AlinesPositions.push( x, y + 60, 0 /*Math.abs( ( 100 / ( width / 2 ) * x ) + ( 100 / ( height / 2 ) * y ) )*/ );
AlinesPositions.push( x, y + 60, 0 /*Math.abs( ( 100 / ( width / 2 ) * x ) + ( 100 / ( height / 2 ) * y ) )*/ );
AlinesPositions.push( x, y, 0 /*Math.abs( ( 100 / ( width / 2 ) * x ) + ( 100 / ( height / 2 ) * y ) )*/ );
x += deltaX;
}
x = ( width / -2 ) - 80;
y += deltaY;
}
x = ( width / - 2 );
y = ( height / - 2 ) - 60;
while( y <= ( height / 2 ) ) {
while( x <= ( width / 2 ) ) {
AlinesPositions.push( x, y, 0 /*Math.abs( ( 100 / ( width / 2 ) * x ) + ( 100 / ( height / 2 ) * y ) )*/ );
AlinesPositions.push( x + 80, y, 0 /*Math.abs( ( 100 / ( width / 2 ) * x ) + ( 100 / ( height / 2 ) * y ) )*/ );
AlinesPositions.push( x + 80, y, 0 /*Math.abs( ( 100 / ( width / 2 ) * x ) + ( 100 / ( height / 2 ) * y ) )*/ );
AlinesPositions.push( x + 80, y + 60, 0 /*Math.abs( ( 100 / ( width / 2 ) * x ) + ( 100 / ( height / 2 ) * y ) )*/ );
AlinesPositions.push( x + 80, y + 60, 0 /*Math.abs( ( 100 / ( width / 2 ) * x ) + ( 100 / ( height / 2 ) * y ) )*/ );
AlinesPositions.push( x, y + 60, 0 /*Math.abs( ( 100 / ( width / 2 ) * x ) + ( 100 / ( height / 2 ) * y ) )*/ );
AlinesPositions.push( x, y + 60, 0 /*Math.abs( ( 100 / ( width / 2 ) * x ) + ( 100 / ( height / 2 ) * y ) )*/ );
AlinesPositions.push( x, y, 0 /*Math.abs( ( 100 / ( width / 2 ) * x ) + ( 100 / ( height / 2 ) * y ) )*/ );
x += deltaX;
}
x = ( width / -2 );
y += deltaY;
}
linesPositions = new Float32Array( AlinesPositions );
var Acolors = [ ];
for( var i = 0; i < AlinesPositions.length; i++ ) {
var fact = Math.random() * 20.0;
var ran = Math.min( Math.max( ( Math.random() / fact ), 0.01 ), 0.5 );
Acolors[i] = ran;
Acolors[i+1] = ran;
Acolors[i+2] = ran;
i += 2;
}
colors = new Float32Array( Acolors );
geometry = new THREE.BufferGeometry();
geometry.addAttribute( 'position', new THREE.BufferAttribute( linesPositions, 3 ).setDynamic( true ) );
geometry.addAttribute( 'color', new THREE.BufferAttribute( colors, 3 ).setDynamic( true ) );
var material = new THREE.LineBasicMaterial( {
vertexColors: THREE.VertexColors,
blending: THREE.AdditiveBlending,
transparent: true
} );
linesMesh = new THREE.LineSegments( geometry, material );
scene.add( linesMesh );
originalPositions = new THREE.BufferAttribute( linesPositions, 3 );
originalPositions.copy( linesMesh.geometry.attributes.position );
}
function init() {
scene = new THREE.Scene();
//camera = new THREE.OrthographicCamera( width / - 2, width / 2, height / 2, height / - 2, 1, 100 );
camera = new THREE.PerspectiveCamera( 120, width / height, 1, 1000 );
camera.position.set( 0, 0, 200 );
camera.lookAt( 0, 0, 0 );
// Create matrix
initMatrix();
raycaster = new THREE.Raycaster();
raycaster.linePrecision = 20;
mouse = new THREE.Vector2();
renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize( width, height );
document.body.appendChild( renderer.domElement );
}
function main() {
init();
animate();
}
function animate() {
requestAnimationFrame( animate );
render();
}
var intersected = [];
function removeX( i ) { intersected.splice( i, 1 ); }
function alterate( index ) {
var randColor = ( Math.random() <= 0.49 ) ? rayColor : rayColor2;
// Change color
var currentC = new THREE.Color( linesMesh.geometry.attributes.color.getX( index ), linesMesh.geometry.attributes.color.getY( index ), linesMesh.geometry.attributes.color.getZ( index ));
var tweenC = TweenLite.to( currentC, 1.0, {
r: randColor.r,
g: randColor.g,
b: randColor.b,
immediateRender: true,
lazy: false,
onUpdate: function() {
linesMesh.geometry.attributes.color.setXYZ( index, currentC.r, currentC.g, currentC.b );
linesMesh.geometry.attributes.color.needsUpdate = true;
},
onUpdateParams: [ index, currentC ]
});
// Change coordinates
var currentXY = { x: originalPositions.getX( index ), y: originalPositions.getY( index ), z: originalPositions.getZ( index ) };
var goal = [ ( currentXY.x + ( Math.random() * 50 ) * ( ( Math.random() < 0.5 ) ? 1 : -1 ) ), ( currentXY.y + ( Math.random() * 50 ) * ( ( Math.random() < 0.5 ) ? 1 : -1 ) ), ( currentXY.z + ( Math.random() * 50 ) * ( ( Math.random() < 0.5 ) ? 1 : -1 ) ) ];
var tweenXY = TweenLite.to( currentXY, 1.0, {
x: goal[ 0 ],
y: goal[ 1 ],
z: goal[ 2 ],
immediateRender: true,
lazy: false,
onUpdate: function() {
linesMesh.geometry.attributes.position.setXYZ( index, currentXY.x, currentXY.y, currentXY.z );
linesMesh.geometry.attributes.position.needsUpdate = true;
},
onUpdateParams: [ index, currentXY ]
});
}
function render() {
// update the picking ray with the camera and mouse position
raycaster.setFromCamera( mouse, camera );
intersects = raycaster.intersectObject( linesMesh );
if( intersects.length ) {
if( !intersected.includes( intersects[ 0 ].index ) ) {
for( var x = 0; x < intersects.length; x++ ) {
if( !intersected.includes( intersects[ x ].index ) ) {
var index = intersects[ x ].index;
// Save index
intersected.push( index );
alterate( index );
}
}
var present = 0;
for( var y = 0; y < intersected.length; y++ ) {
for( var j = 0; j < intersects.length; j++ ) {
if( intersects[ j ].index == intersected[ y ] ) { present = 1; break; }
}
if( !present ) {
( function( y ) {
var randC = Math.min( Math.max( ( Math.random() / ( Math.random() * 20.0 ) ), 0.01 ), 0.5 );
// Current item old coordinates
let index = intersected[ y ];
var indexX = originalPositions.getX( index );
var indexY = originalPositions.getY( index );
var indexZ = originalPositions.getZ( index );
removeX( y );
// Reset color
var currentCreset = new THREE.Color( linesMesh.geometry.attributes.color.getX( index ), linesMesh.geometry.attributes.color.getY( index ), linesMesh.geometry.attributes.color.getZ( index ));
var tweenCreset = TweenLite.to( currentCreset, 1.5, {
r: randC,
g: randC,
b: randC,
immediateRender: true,
lazy: false,
onUpdate: function() {
linesMesh.geometry.attributes.color.setXYZ( index, currentCreset.r, currentCreset.g, currentCreset.b );
linesMesh.geometry.attributes.color.needsUpdate = true;
},
onUpdateParams: [ index, currentCreset ],
onComplete: function() { }
} );
// Reset coordinates
var currentXYreset = { x: linesMesh.geometry.attributes.position.getX( index ), y: linesMesh.geometry.attributes.position.getY( index ), z: linesMesh.geometry.attributes.position.getZ( index ) };
var tweenXYreset = TweenLite.to( currentXYreset, 1.5, {
x: indexX,
y: indexY,
z: indexZ,
immediateRender: true,
lazy: false,
onUpdate: function() {
linesMesh.geometry.attributes.position.setXYZ( index, currentXYreset.x, currentXYreset.y, currentXYreset.z );
linesMesh.geometry.attributes.position.needsUpdate = true;
},
onUpdateParams: [ index, currentXYreset ]
});
}) ( y );
}
}
}
}
renderer.render( scene, camera );
}
function onMouseMove( event ) {
// calculate mouse position in normalized device coordinates
// (-1 to +1) for both components
event.preventDefault();
mouse.x = ( event.clientX / window.innerWidth ) * 2 - 1;
mouse.y = - ( event.clientY / window.innerHeight ) * 2 + 1;
var normX = event.clientX - ( window.innerWidth / 2 );
var normY = event.clientY - ( window.innerHeight / 2 );
camera.position.set( 0 + ( normY / 50 ), 0 + ( normX / 50 ), 200 );
camera.lookAt( 0 + ( normY / 50 ), 0 + ( normX / 50 ), 0 );
}
window.addEventListener( 'click', function() {
if( intersected.length ) {
for( var x = 0; x < intersected.length; x++ ) {
var index = intersected[ x ];
alterate( index );
}
}
});
window.addEventListener( 'mousemove', onMouseMove, false );
// Set new dimensions for scene when resize window
window.addEventListener( 'resize', function() {
var wWidth = window.innerWidth;
var wHeight = window.innerHeight;
/*camera.left = wWidth / -2;
camera.right = wWidth / 2;
camera.top = wHeight / 2;
camera.bottom = wHeight / -2;*/
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
raycaster.setFromCamera( mouse, camera );
renderer.setSize( window.innerWidth, window.innerHeight );
} );
Here is a Codepen link.
I found a solution. I made a whole geometry and I indexed vertices and respective colors. The only problem is that I can't mange opacity this way, but it works fine and CPU is around 20%.
document.addEventListener( 'DOMContentLoaded', main );
var width = screen.width;
var height = screen.height;
var camera, scene, renderer, raycaster, mouse;
var rayColor = new THREE.Color( 0x0640C2 );
var rayColor2 = new THREE.Color( 0xCC311B );
var colors;
var linesPositions, originalPositions;
var linesMesh;
var geometry;
var intersects;
var stats;
function initMatrix() {
var AlinesPositions = [ ];
var x = ( width / - 2 ) - 80;
var y = ( height / - 2 ) - 60;
var deltaX = 120;
var deltaY = 60;
while( y <= ( height / 2 ) ) {
while( x <= ( width / 2 ) ) {
AlinesPositions.push( x, y, 0 /*Math.abs( ( 100 / ( width / 2 ) * x ) + ( 100 / ( height / 2 ) * y ) )*/ );
AlinesPositions.push( x + 80, y, 0 /*Math.abs( ( 100 / ( width / 2 ) * x ) + ( 100 / ( height / 2 ) * y ) )*/ );
AlinesPositions.push( x + 80, y, 0 /*Math.abs( ( 100 / ( width / 2 ) * x ) + ( 100 / ( height / 2 ) * y ) )*/ );
AlinesPositions.push( x + 80, y + 60, 0 /*Math.abs( ( 100 / ( width / 2 ) * x ) + ( 100 / ( height / 2 ) * y ) )*/ );
AlinesPositions.push( x + 80, y + 60, 0 /*Math.abs( ( 100 / ( width / 2 ) * x ) + ( 100 / ( height / 2 ) * y ) )*/ );
AlinesPositions.push( x, y + 60, 0 /*Math.abs( ( 100 / ( width / 2 ) * x ) + ( 100 / ( height / 2 ) * y ) )*/ );
AlinesPositions.push( x, y + 60, 0 /*Math.abs( ( 100 / ( width / 2 ) * x ) + ( 100 / ( height / 2 ) * y ) )*/ );
AlinesPositions.push( x, y, 0 /*Math.abs( ( 100 / ( width / 2 ) * x ) + ( 100 / ( height / 2 ) * y ) )*/ );
x += deltaX;
}
x = ( width / -2 ) - 80;
y += deltaY;
}
x = ( width / - 2 );
y = ( height / - 2 ) - 60;
while( y <= ( height / 2 ) ) {
while( x <= ( width / 2 ) ) {
AlinesPositions.push( x, y, 0 /*Math.abs( ( 100 / ( width / 2 ) * x ) + ( 100 / ( height / 2 ) * y ) )*/ );
AlinesPositions.push( x + 80, y, 0 /*Math.abs( ( 100 / ( width / 2 ) * x ) + ( 100 / ( height / 2 ) * y ) )*/ );
AlinesPositions.push( x + 80, y, 0 /*Math.abs( ( 100 / ( width / 2 ) * x ) + ( 100 / ( height / 2 ) * y ) )*/ );
AlinesPositions.push( x + 80, y + 60, 0 /*Math.abs( ( 100 / ( width / 2 ) * x ) + ( 100 / ( height / 2 ) * y ) )*/ );
AlinesPositions.push( x + 80, y + 60, 0 /*Math.abs( ( 100 / ( width / 2 ) * x ) + ( 100 / ( height / 2 ) * y ) )*/ );
AlinesPositions.push( x, y + 60, 0 /*Math.abs( ( 100 / ( width / 2 ) * x ) + ( 100 / ( height / 2 ) * y ) )*/ );
AlinesPositions.push( x, y + 60, 0 /*Math.abs( ( 100 / ( width / 2 ) * x ) + ( 100 / ( height / 2 ) * y ) )*/ );
AlinesPositions.push( x, y, 0 /*Math.abs( ( 100 / ( width / 2 ) * x ) + ( 100 / ( height / 2 ) * y ) )*/ );
x += deltaX;
}
x = ( width / -2 );
y += deltaY;
}
linesPositions = new Float32Array( AlinesPositions );
var Acolors = [ ];
for( var i = 0; i < AlinesPositions.length; i++ ) {
var fact = Math.random() * 20.0;
var ran = Math.min( Math.max( ( Math.random() / fact ), 0.01 ), 0.5 );
Acolors[i] = ran;
Acolors[i+1] = ran;
Acolors[i+2] = ran;
i += 2;
}
colors = new Float32Array( Acolors );
geometry = new THREE.BufferGeometry();
geometry.addAttribute( 'position', new THREE.BufferAttribute( linesPositions, 3 ).setDynamic( true ) );
geometry.addAttribute( 'color', new THREE.BufferAttribute( colors, 3 ).setDynamic( true ) );
var material = new THREE.LineBasicMaterial( {
vertexColors: THREE.VertexColors,
blending: THREE.AdditiveBlending,
transparent: true
} );
linesMesh = new THREE.LineSegments( geometry, material );
scene.add( linesMesh );
originalPositions = new THREE.BufferAttribute( linesPositions, 3 );
originalPositions.copy( linesMesh.geometry.attributes.position );
}
function init() {
scene = new THREE.Scene();
//camera = new THREE.OrthographicCamera( width / - 2, width / 2, height / 2, height / - 2, 1, 100 );
camera = new THREE.PerspectiveCamera( 120, width / height, 1, 1000 );
camera.position.set( 0, 0, 200 );
camera.lookAt( 0, 0, 0 );
// Create matrix
initMatrix();
raycaster = new THREE.Raycaster();
raycaster.linePrecision = 20;
mouse = new THREE.Vector2();
renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize( width, height );
document.body.appendChild( renderer.domElement );
}
function main() {
init();
animate();
}
function animate() {
requestAnimationFrame( animate );
render();
}
var intersected = [];
function removeX( i ) { intersected.splice( i, 1 ); }
function alterate( index ) {
var randColor = ( Math.random() <= 0.49 ) ? rayColor : rayColor2;
// Change color
var currentC = new THREE.Color( linesMesh.geometry.attributes.color.getX( index ), linesMesh.geometry.attributes.color.getY( index ), linesMesh.geometry.attributes.color.getZ( index ));
var tweenC = TweenLite.to( currentC, 1.0, {
r: randColor.r,
g: randColor.g,
b: randColor.b,
immediateRender: true,
lazy: false,
onUpdate: function() {
linesMesh.geometry.attributes.color.setXYZ( index, currentC.r, currentC.g, currentC.b );
linesMesh.geometry.attributes.color.needsUpdate = true;
},
onUpdateParams: [ index, currentC ]
});
// Change coordinates
var currentXY = { x: originalPositions.getX( index ), y: originalPositions.getY( index ), z: originalPositions.getZ( index ) };
var goal = [ ( currentXY.x + ( Math.random() * 50 ) * ( ( Math.random() < 0.5 ) ? 1 : -1 ) ), ( currentXY.y + ( Math.random() * 50 ) * ( ( Math.random() < 0.5 ) ? 1 : -1 ) ), ( currentXY.z + ( Math.random() * 50 ) * ( ( Math.random() < 0.5 ) ? 1 : -1 ) ) ];
var tweenXY = TweenLite.to( currentXY, 1.0, {
x: goal[ 0 ],
y: goal[ 1 ],
z: goal[ 2 ],
immediateRender: true,
lazy: false,
onUpdate: function() {
linesMesh.geometry.attributes.position.setXYZ( index, currentXY.x, currentXY.y, currentXY.z );
linesMesh.geometry.attributes.position.needsUpdate = true;
},
onUpdateParams: [ index, currentXY ]
});
}
function render() {
// update the picking ray with the camera and mouse position
raycaster.setFromCamera( mouse, camera );
intersects = raycaster.intersectObject( linesMesh );
if( intersects.length ) {
if( !intersected.includes( intersects[ 0 ].index ) ) {
for( var x = 0; x < intersects.length; x++ ) {
if( !intersected.includes( intersects[ x ].index ) ) {
var index = intersects[ x ].index;
// Save index
intersected.push( index );
alterate( index );
}
}
var present = 0;
for( var y = 0; y < intersected.length; y++ ) {
for( var j = 0; j < intersects.length; j++ ) {
if( intersects[ j ].index == intersected[ y ] ) { present = 1; break; }
}
if( !present ) {
( function( y ) {
var randC = Math.min( Math.max( ( Math.random() / ( Math.random() * 20.0 ) ), 0.01 ), 0.5 );
// Current item old coordinates
let index = intersected[ y ];
var indexX = originalPositions.getX( index );
var indexY = originalPositions.getY( index );
var indexZ = originalPositions.getZ( index );
removeX( y );
// Reset color
var currentCreset = new THREE.Color( linesMesh.geometry.attributes.color.getX( index ), linesMesh.geometry.attributes.color.getY( index ), linesMesh.geometry.attributes.color.getZ( index ));
var tweenCreset = TweenLite.to( currentCreset, 1.5, {
r: randC,
g: randC,
b: randC,
immediateRender: true,
lazy: false,
onUpdate: function() {
linesMesh.geometry.attributes.color.setXYZ( index, currentCreset.r, currentCreset.g, currentCreset.b );
linesMesh.geometry.attributes.color.needsUpdate = true;
},
onUpdateParams: [ index, currentCreset ],
onComplete: function() { }
} );
// Reset coordinates
var currentXYreset = { x: linesMesh.geometry.attributes.position.getX( index ), y: linesMesh.geometry.attributes.position.getY( index ), z: linesMesh.geometry.attributes.position.getZ( index ) };
var tweenXYreset = TweenLite.to( currentXYreset, 1.5, {
x: indexX,
y: indexY,
z: indexZ,
immediateRender: true,
lazy: false,
onUpdate: function() {
linesMesh.geometry.attributes.position.setXYZ( index, currentXYreset.x, currentXYreset.y, currentXYreset.z );
linesMesh.geometry.attributes.position.needsUpdate = true;
},
onUpdateParams: [ index, currentXYreset ]
});
}) ( y );
}
}
}
}
renderer.render( scene, camera );
}
function onMouseMove( event ) {
// calculate mouse position in normalized device coordinates
// (-1 to +1) for both components
event.preventDefault();
mouse.x = ( event.clientX / window.innerWidth ) * 2 - 1;
mouse.y = - ( event.clientY / window.innerHeight ) * 2 + 1;
var normX = event.clientX - ( window.innerWidth / 2 );
var normY = event.clientY - ( window.innerHeight / 2 );
camera.position.set( 0 + ( normY / 50 ), 0 + ( normX / 50 ), 200 );
camera.lookAt( 0 + ( normY / 50 ), 0 + ( normX / 50 ), 0 );
}
window.addEventListener( 'click', function() {
if( intersected.length ) {
for( var x = 0; x < intersected.length; x++ ) {
var index = intersected[ x ];
alterate( index );
}
}
});
window.addEventListener( 'mousemove', onMouseMove, false );
// Set new dimensions for scene when resize window
window.addEventListener( 'resize', function() {
var wWidth = window.innerWidth;
var wHeight = window.innerHeight;
/*camera.left = wWidth / -2;
camera.right = wWidth / 2;
camera.top = wHeight / 2;
camera.bottom = wHeight / -2;*/
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
raycaster.setFromCamera( mouse, camera );
renderer.setSize( window.innerWidth, window.innerHeight );
} );
Here is a Codepen link: Example

pivot point lost after element move whit orbitcontrols

I have a problem whit three.js and OrbitControls.js., i have an sphere that's make a little tween from center to left,
this is the fiddle: http://jsfiddle.net/mirko_roma89/4ruLy96g/1/
var scene, renderer, camera, controls;
init();
animate();
function init()
{
renderer = new THREE.WebGLRenderer( {antialias:true} );
var width = window.innerWidth;
var height = window.innerHeight;
renderer.setSize (width, height);
renderer.setClearColor( 0x222244, 1);
document.body.appendChild (renderer.domElement);
scene = new THREE.Scene();
camera = new THREE.PerspectiveCamera (45, width/height, 1, 10000);
camera.position.y = 16;
camera.position.z = 250;
controls = new THREE.OrbitControls (camera, renderer.domElement);
controls.enableDamping = true;
controls.dampingFactor = 0.25;
var gridXZ = new THREE.GridHelper(100, 10);
gridXZ.setColors( new THREE.Color(0xff0000), new THREE.Color(0xffffff) );
scene.add(gridXZ);
var loader = new THREE.TextureLoader();
loader.crossOrigin = "";
var earthTexture = loader.load( "http://api.usno.navy.mil/imagery/earth.png?view=full&date=11/24/2015&time=4:45", callback );
// globe
var radius = 100;
var sphere = new THREE.Mesh( new THREE.SphereGeometry( radius, 16,16), new THREE.MeshBasicMaterial( { map: earthTexture } ) );
scene.add( sphere );
var latitude = 38.43;
var longitude = -9.1;
var marker = new THREE.Mesh( new THREE.SphereGeometry( 1, 16, 16 ), new THREE.MeshBasicMaterial( { color: 0xFF0000 } ) );
sphere.add( marker );
var verticalOffset = 0.1;
function callback() {
new TWEEN.Tween( sphere.position )
.to( { x:-30 , y: 0 }, 2000 )
.start();
}
}
function animate()
{
requestAnimationFrame ( animate );
TWEEN.update();
controls.update();
renderer.render (scene, camera);
}
<script>
/**
* #author qiao / https://github.com/qiao
* #author mrdoob / http://mrdoob.com
* #author alteredq / http://alteredqualia.com/
* #author WestLangley / http://github.com/WestLangley
* #author erich666 / http://erichaines.com
*/
// This set of controls performs orbiting, dollying (zooming), and panning.
// Unlike TrackballControls, it maintains the "up" direction object.up (+Y by default).
//
// Orbit - left mouse / touch: one finger move
// Zoom - middle mouse, or mousewheel / touch: two finger spread or squish
// Pan - right mouse, or arrow keys / touch: three finter swipe
THREE.OrbitControls = function ( object, domElement ) {
this.object = object;
this.domElement = ( domElement !== undefined ) ? domElement : document;
// Set to false to disable this control
this.enabled = true;
// "target" sets the location of focus, where the object orbits around
this.target = new THREE.Vector3();
// How far you can dolly in and out ( PerspectiveCamera only )
this.minDistance = 0;
this.maxDistance = Infinity;
// How far you can zoom in and out ( OrthographicCamera only )
this.minZoom = 0;
this.maxZoom = Infinity;
// How far you can orbit vertically, upper and lower limits.
// Range is 0 to Math.PI radians.
this.minPolarAngle = 0; // radians
this.maxPolarAngle = Math.PI; // radians
// How far you can orbit horizontally, upper and lower limits.
// If set, must be a sub-interval of the interval [ - Math.PI, Math.PI ].
this.minAzimuthAngle = - Infinity; // radians
this.maxAzimuthAngle = Infinity; // radians
// Set to true to enable damping (inertia)
// If damping is enabled, you must call controls.update() in your animation loop
this.enableDamping = false;
this.dampingFactor = 0.25;
// This option actually enables dollying in and out; left as "zoom" for backwards compatibility.
// Set to false to disable zooming
this.enableZoom = true;
this.zoomSpeed = 1.0;
// Set to false to disable rotating
this.enableRotate = true;
this.rotateSpeed = 1.0;
// Set to false to disable panning
this.enablePan = true;
this.keyPanSpeed = 7.0; // pixels moved per arrow key push
// Set to true to automatically rotate around the target
// If auto-rotate is enabled, you must call controls.update() in your animation loop
this.autoRotate = false;
this.autoRotateSpeed = 2.0; // 30 seconds per round when fps is 60
// Set to false to disable use of the keys
this.enableKeys = true;
// The four arrow keys
this.keys = { LEFT: 37, UP: 38, RIGHT: 39, BOTTOM: 40 };
// Mouse buttons
this.mouseButtons = { ORBIT: THREE.MOUSE.LEFT, ZOOM: THREE.MOUSE.MIDDLE, PAN: THREE.MOUSE.RIGHT };
// for reset
this.target0 = this.target.clone();
this.position0 = this.object.position.clone();
this.zoom0 = this.object.zoom;
//
// public methods
//
this.getPolarAngle = function () {
return phi;
};
this.getAzimuthalAngle = function () {
return theta;
};
this.reset = function () {
scope.target.copy( scope.target0 );
scope.object.position.copy( scope.position0 );
scope.object.zoom = scope.zoom0;
scope.object.updateProjectionMatrix();
scope.dispatchEvent( changeEvent );
scope.update();
state = STATE.NONE;
};
// this method is exposed, but perhaps it would be better if we can make it private...
this.update = function() {
var offset = new THREE.Vector3();
// so camera.up is the orbit axis
var quat = new THREE.Quaternion().setFromUnitVectors( object.up, new THREE.Vector3( 0, 1, 0 ) );
var quatInverse = quat.clone().inverse();
var lastPosition = new THREE.Vector3();
var lastQuaternion = new THREE.Quaternion();
return function () {
var position = scope.object.position;
offset.copy( position ).sub( scope.target );
// rotate offset to "y-axis-is-up" space
offset.applyQuaternion( quat );
// angle from z-axis around y-axis
theta = Math.atan2( offset.x, offset.z );
// angle from y-axis
phi = Math.atan2( Math.sqrt( offset.x * offset.x + offset.z * offset.z ), offset.y );
if ( scope.autoRotate && state === STATE.NONE ) {
rotateLeft( getAutoRotationAngle() );
}
theta += thetaDelta;
phi += phiDelta;
// restrict theta to be between desired limits
theta = Math.max( scope.minAzimuthAngle, Math.min( scope.maxAzimuthAngle, theta ) );
// restrict phi to be between desired limits
phi = Math.max( scope.minPolarAngle, Math.min( scope.maxPolarAngle, phi ) );
// restrict phi to be betwee EPS and PI-EPS
phi = Math.max( EPS, Math.min( Math.PI - EPS, phi ) );
var radius = offset.length() * scale;
// restrict radius to be between desired limits
radius = Math.max( scope.minDistance, Math.min( scope.maxDistance, radius ) );
// move target to panned location
scope.target.add( panOffset );
offset.x = radius * Math.sin( phi ) * Math.sin( theta );
offset.y = radius * Math.cos( phi );
offset.z = radius * Math.sin( phi ) * Math.cos( theta );
// rotate offset back to "camera-up-vector-is-up" space
offset.applyQuaternion( quatInverse );
position.copy( scope.target ).add( offset );
scope.object.lookAt( scope.target );
if ( scope.enableDamping === true ) {
thetaDelta *= ( 1 - scope.dampingFactor );
phiDelta *= ( 1 - scope.dampingFactor );
} else {
thetaDelta = 0;
phiDelta = 0;
}
scale = 1;
panOffset.set( 0, 0, 0 );
// update condition is:
// min(camera displacement, camera rotation in radians)^2 > EPS
// using small-angle approximation cos(x/2) = 1 - x^2 / 8
if ( zoomChanged ||
lastPosition.distanceToSquared( scope.object.position ) > EPS ||
8 * ( 1 - lastQuaternion.dot( scope.object.quaternion ) ) > EPS ) {
scope.dispatchEvent( changeEvent );
lastPosition.copy( scope.object.position );
lastQuaternion.copy( scope.object.quaternion );
zoomChanged = false;
return true;
}
return false;
};
}();
this.dispose = function() {
scope.domElement.removeEventListener( 'contextmenu', onContextMenu, false );
scope.domElement.removeEventListener( 'mousedown', onMouseDown, false );
scope.domElement.removeEventListener( 'mousewheel', onMouseWheel, false );
scope.domElement.removeEventListener( 'MozMousePixelScroll', onMouseWheel, false ); // firefox
scope.domElement.removeEventListener( 'touchstart', onTouchStart, false );
scope.domElement.removeEventListener( 'touchend', onTouchEnd, false );
scope.domElement.removeEventListener( 'touchmove', onTouchMove, false );
document.removeEventListener( 'mousemove', onMouseMove, false );
document.removeEventListener( 'mouseup', onMouseUp, false );
document.removeEventListener( 'mouseout', onMouseUp, false );
window.removeEventListener( 'keydown', onKeyDown, false );
//scope.dispatchEvent( { type: 'dispose' } ); // should this be added here?
};
//
// internals
//
var scope = this;
var changeEvent = { type: 'change' };
var startEvent = { type: 'start' };
var endEvent = { type: 'end' };
var STATE = { NONE : - 1, ROTATE : 0, DOLLY : 1, PAN : 2, TOUCH_ROTATE : 3, TOUCH_DOLLY : 4, TOUCH_PAN : 5 };
var state = STATE.NONE;
var EPS = 0.000001;
// current position in spherical coordinates
var theta;
var phi;
var phiDelta = 0;
var thetaDelta = 0;
var scale = 1;
var panOffset = new THREE.Vector3();
var zoomChanged = false;
var rotateStart = new THREE.Vector2();
var rotateEnd = new THREE.Vector2();
var rotateDelta = new THREE.Vector2();
var panStart = new THREE.Vector2();
var panEnd = new THREE.Vector2();
var panDelta = new THREE.Vector2();
var dollyStart = new THREE.Vector2();
var dollyEnd = new THREE.Vector2();
var dollyDelta = new THREE.Vector2();
function getAutoRotationAngle() {
return 2 * Math.PI / 60 / 60 * scope.autoRotateSpeed;
}
function getZoomScale() {
return Math.pow( 0.95, scope.zoomSpeed );
}
function rotateLeft( angle ) {
thetaDelta -= angle;
}
function rotateUp( angle ) {
phiDelta -= angle;
}
var panLeft = function() {
var v = new THREE.Vector3();
return function panLeft( distance, objectMatrix ) {
var te = objectMatrix.elements;
// get X column of objectMatrix
v.set( te[ 0 ], te[ 1 ], te[ 2 ] );
v.multiplyScalar( - distance );
panOffset.add( v );
};
}();
var panUp = function() {
var v = new THREE.Vector3();
return function panUp( distance, objectMatrix ) {
var te = objectMatrix.elements;
// get Y column of objectMatrix
v.set( te[ 4 ], te[ 5 ], te[ 6 ] );
v.multiplyScalar( distance );
panOffset.add( v );
};
}();
// deltaX and deltaY are in pixels; right and down are positive
var pan = function() {
var offset = new THREE.Vector3();
return function( deltaX, deltaY ) {
var element = scope.domElement === document ? scope.domElement.body : scope.domElement;
if ( scope.object instanceof THREE.PerspectiveCamera ) {
// perspective
var position = scope.object.position;
offset.copy( position ).sub( scope.target );
var targetDistance = offset.length();
// half of the fov is center to top of screen
targetDistance *= Math.tan( ( scope.object.fov / 2 ) * Math.PI / 180.0 );
// we actually don't use screenWidth, since perspective camera is fixed to screen height
panLeft( 2 * deltaX * targetDistance / element.clientHeight, scope.object.matrix );
panUp( 2 * deltaY * targetDistance / element.clientHeight, scope.object.matrix );
} else if ( scope.object instanceof THREE.OrthographicCamera ) {
// orthographic
panLeft( deltaX * ( scope.object.right - scope.object.left ) / element.clientWidth, scope.object.matrix );
panUp( deltaY * ( scope.object.top - scope.object.bottom ) / element.clientHeight, scope.object.matrix );
} else {
// camera neither orthographic nor perspective
console.warn( 'WARNING: OrbitControls.js encountered an unknown camera type - pan disabled.' );
scope.enablePan = false;
}
};
}();
function dollyIn( dollyScale ) {
if ( scope.object instanceof THREE.PerspectiveCamera ) {
scale /= dollyScale;
} else if ( scope.object instanceof THREE.OrthographicCamera ) {
scope.object.zoom = Math.max( scope.minZoom, Math.min( scope.maxZoom, scope.object.zoom * dollyScale ) );
scope.object.updateProjectionMatrix();
zoomChanged = true;
} else {
console.warn( 'WARNING: OrbitControls.js encountered an unknown camera type - dolly/zoom disabled.' );
scope.enableZoom = false;
}
}
function dollyOut( dollyScale ) {
if ( scope.object instanceof THREE.PerspectiveCamera ) {
scale *= dollyScale;
} else if ( scope.object instanceof THREE.OrthographicCamera ) {
scope.object.zoom = Math.max( scope.minZoom, Math.min( scope.maxZoom, scope.object.zoom / dollyScale ) );
scope.object.updateProjectionMatrix();
zoomChanged = true;
} else {
console.warn( 'WARNING: OrbitControls.js encountered an unknown camera type - dolly/zoom disabled.' );
scope.enableZoom = false;
}
}
//
// event callbacks - update the object state
//
function handleMouseDownRotate( event ) {
//console.log( 'handleMouseDownRotate' );
rotateStart.set( event.clientX, event.clientY );
}
function handleMouseDownDolly( event ) {
//console.log( 'handleMouseDownDolly' );
dollyStart.set( event.clientX, event.clientY );
}
function handleMouseDownPan( event ) {
//console.log( 'handleMouseDownPan' );
panStart.set( event.clientX, event.clientY );
}
function handleMouseMoveRotate( event ) {
//console.log( 'handleMouseMoveRotate' );
rotateEnd.set( event.clientX, event.clientY );
rotateDelta.subVectors( rotateEnd, rotateStart );
var element = scope.domElement === document ? scope.domElement.body : scope.domElement;
// rotating across whole screen goes 360 degrees around
rotateLeft( 2 * Math.PI * rotateDelta.x / element.clientWidth * scope.rotateSpeed );
// rotating up and down along whole screen attempts to go 360, but limited to 180
rotateUp( 2 * Math.PI * rotateDelta.y / element.clientHeight * scope.rotateSpeed );
rotateStart.copy( rotateEnd );
scope.update();
}
function handleMouseMoveDolly( event ) {
//console.log( 'handleMouseMoveDolly' );
dollyEnd.set( event.clientX, event.clientY );
dollyDelta.subVectors( dollyEnd, dollyStart );
if ( dollyDelta.y > 0 ) {
dollyIn( getZoomScale() );
} else if ( dollyDelta.y < 0 ) {
dollyOut( getZoomScale() );
}
dollyStart.copy( dollyEnd );
scope.update();
}
function handleMouseMovePan( event ) {
//console.log( 'handleMouseMovePan' );
panEnd.set( event.clientX, event.clientY );
panDelta.subVectors( panEnd, panStart );
pan( panDelta.x, panDelta.y );
panStart.copy( panEnd );
scope.update();
}
function handleMouseUp( event ) {
//console.log( 'handleMouseUp' );
}
function handleMouseWheel( event ) {
//console.log( 'handleMouseWheel' );
var delta = 0;
if ( event.wheelDelta !== undefined ) {
// WebKit / Opera / Explorer 9
delta = event.wheelDelta;
} else if ( event.detail !== undefined ) {
// Firefox
delta = - event.detail;
}
if ( delta > 0 ) {
dollyOut( getZoomScale() );
} else if ( delta < 0 ) {
dollyIn( getZoomScale() );
}
scope.update();
}
function handleKeyDown( event ) {
//console.log( 'handleKeyDown' );
switch ( event.keyCode ) {
case scope.keys.UP:
pan( 0, scope.keyPanSpeed );
scope.update();
break;
case scope.keys.BOTTOM:
pan( 0, - scope.keyPanSpeed );
scope.update();
break;
case scope.keys.LEFT:
pan( scope.keyPanSpeed, 0 );
scope.update();
break;
case scope.keys.RIGHT:
pan( - scope.keyPanSpeed, 0 );
scope.update();
break;
}
}
function handleTouchStartRotate( event ) {
//console.log( 'handleTouchStartRotate' );
rotateStart.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY );
}
function handleTouchStartDolly( event ) {
//console.log( 'handleTouchStartDolly' );
var dx = event.touches[ 0 ].pageX - event.touches[ 1 ].pageX;
var dy = event.touches[ 0 ].pageY - event.touches[ 1 ].pageY;
var distance = Math.sqrt( dx * dx + dy * dy );
dollyStart.set( 0, distance );
}
function handleTouchStartPan( event ) {
//console.log( 'handleTouchStartPan' );
panStart.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY );
}
function handleTouchMoveRotate( event ) {
//console.log( 'handleTouchMoveRotate' );
rotateEnd.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY );
rotateDelta.subVectors( rotateEnd, rotateStart );
var element = scope.domElement === document ? scope.domElement.body : scope.domElement;
// rotating across whole screen goes 360 degrees around
rotateLeft( 2 * Math.PI * rotateDelta.x / element.clientWidth * scope.rotateSpeed );
// rotating up and down along whole screen attempts to go 360, but limited to 180
rotateUp( 2 * Math.PI * rotateDelta.y / element.clientHeight * scope.rotateSpeed );
rotateStart.copy( rotateEnd );
scope.update();
}
function handleTouchMoveDolly( event ) {
//console.log( 'handleTouchMoveDolly' );
var dx = event.touches[ 0 ].pageX - event.touches[ 1 ].pageX;
var dy = event.touches[ 0 ].pageY - event.touches[ 1 ].pageY;
var distance = Math.sqrt( dx * dx + dy * dy );
dollyEnd.set( 0, distance );
dollyDelta.subVectors( dollyEnd, dollyStart );
if ( dollyDelta.y > 0 ) {
dollyOut( getZoomScale() );
} else if ( dollyDelta.y < 0 ) {
dollyIn( getZoomScale() );
}
dollyStart.copy( dollyEnd );
scope.update();
}
function handleTouchMovePan( event ) {
//console.log( 'handleTouchMovePan' );
panEnd.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY );
panDelta.subVectors( panEnd, panStart );
pan( panDelta.x, panDelta.y );
panStart.copy( panEnd );
scope.update();
}
function handleTouchEnd( event ) {
//console.log( 'handleTouchEnd' );
}
//
// event handlers - FSM: listen for events and reset state
//
function onMouseDown( event ) {
if ( scope.enabled === false ) return;
event.preventDefault();
if ( event.button === scope.mouseButtons.ORBIT ) {
if ( scope.enableRotate === false ) return;
handleMouseDownRotate( event );
state = STATE.ROTATE;
} else if ( event.button === scope.mouseButtons.ZOOM ) {
if ( scope.enableZoom === false ) return;
handleMouseDownDolly( event );
state = STATE.DOLLY;
} else if ( event.button === scope.mouseButtons.PAN ) {
if ( scope.enablePan === false ) return;
handleMouseDownPan( event );
state = STATE.PAN;
}
if ( state !== STATE.NONE ) {
document.addEventListener( 'mousemove', onMouseMove, false );
document.addEventListener( 'mouseup', onMouseUp, false );
document.addEventListener( 'mouseout', onMouseUp, false );
scope.dispatchEvent( startEvent );
}
}
function onMouseMove( event ) {
if ( scope.enabled === false ) return;
event.preventDefault();
if ( state === STATE.ROTATE ) {
if ( scope.enableRotate === false ) return;
handleMouseMoveRotate( event );
} else if ( state === STATE.DOLLY ) {
if ( scope.enableZoom === false ) return;
handleMouseMoveDolly( event );
} else if ( state === STATE.PAN ) {
if ( scope.enablePan === false ) return;
handleMouseMovePan( event );
}
}
function onMouseUp( event ) {
if ( scope.enabled === false ) return;
handleMouseUp( event );
document.removeEventListener( 'mousemove', onMouseMove, false );
document.removeEventListener( 'mouseup', onMouseUp, false );
document.removeEventListener( 'mouseout', onMouseUp, false );
scope.dispatchEvent( endEvent );
state = STATE.NONE;
}
function onMouseWheel( event ) {
if ( scope.enabled === false || scope.enableZoom === false || state !== STATE.NONE ) return;
event.preventDefault();
event.stopPropagation();
handleMouseWheel( event );
scope.dispatchEvent( startEvent ); // not sure why these are here...
scope.dispatchEvent( endEvent );
}
function onKeyDown( event ) {
if ( scope.enabled === false || scope.enableKeys === false || scope.enablePan === false ) return;
handleKeyDown( event );
}
function onTouchStart( event ) {
if ( scope.enabled === false ) return;
switch ( event.touches.length ) {
case 1: // one-fingered touch: rotate
if ( scope.enableRotate === false ) return;
handleTouchStartRotate( event );
state = STATE.TOUCH_ROTATE;
break;
case 2: // two-fingered touch: dolly
if ( scope.enableZoom === false ) return;
handleTouchStartDolly( event );
state = STATE.TOUCH_DOLLY;
break;
case 3: // three-fingered touch: pan
if ( scope.enablePan === false ) return;
handleTouchStartPan( event );
state = STATE.TOUCH_PAN;
break;
default:
state = STATE.NONE;
}
if ( state !== STATE.NONE ) {
scope.dispatchEvent( startEvent );
}
}
function onTouchMove( event ) {
if ( scope.enabled === false ) return;
event.preventDefault();
event.stopPropagation();
switch ( event.touches.length ) {
case 1: // one-fingered touch: rotate
if ( scope.enableRotate === false ) return;
if ( state !== STATE.TOUCH_ROTATE ) return; // is this needed?...
handleTouchMoveRotate( event );
break;
case 2: // two-fingered touch: dolly
if ( scope.enableZoom === false ) return;
if ( state !== STATE.TOUCH_DOLLY ) return; // is this needed?...
handleTouchMoveDolly( event );
break;
case 3: // three-fingered touch: pan
if ( scope.enablePan === false ) return;
if ( state !== STATE.TOUCH_PAN ) return; // is this needed?...
handleTouchMovePan( event );
break;
default:
state = STATE.NONE;
}
}
function onTouchEnd( event ) {
if ( scope.enabled === false ) return;
handleTouchEnd( event );
scope.dispatchEvent( endEvent );
state = STATE.NONE;
}
function onContextMenu( event ) {
event.preventDefault();
}
//
scope.domElement.addEventListener( 'contextmenu', onContextMenu, false );
scope.domElement.addEventListener( 'mousedown', onMouseDown, false );
scope.domElement.addEventListener( 'mousewheel', onMouseWheel, false );
scope.domElement.addEventListener( 'MozMousePixelScroll', onMouseWheel, false ); // firefox
scope.domElement.addEventListener( 'touchstart', onTouchStart, false );
scope.domElement.addEventListener( 'touchend', onTouchEnd, false );
scope.domElement.addEventListener( 'touchmove', onTouchMove, false );
window.addEventListener( 'keydown', onKeyDown, false );
// force an update at start
this.update();
};
THREE.OrbitControls.prototype = Object.create( THREE.EventDispatcher.prototype );
THREE.OrbitControls.prototype.constructor = THREE.OrbitControls;
Object.defineProperties( THREE.OrbitControls.prototype, {
center: {
get: function () {
console.warn( 'THREE.OrbitControls: .center has been renamed to .target' );
return this.target;
}
},
// backward compatibility
noZoom: {
get: function () {
console.warn( 'THREE.OrbitControls: .noZoom has been deprecated. Use .enableZoom instead.' );
return ! this.enableZoom;
},
set: function ( value ) {
console.warn( 'THREE.OrbitControls: .noZoom has been deprecated. Use .enableZoom instead.' );
this.enableZoom = ! value;
}
},
noRotate: {
get: function () {
console.warn( 'THREE.OrbitControls: .noRotate has been deprecated. Use .enableRotate instead.' );
return ! this.enableRotate;
},
set: function ( value ) {
console.warn( 'THREE.OrbitControls: .noRotate has been deprecated. Use .enableRotate instead.' );
this.enableRotate = ! value;
}
},
noPan: {
get: function () {
console.warn( 'THREE.OrbitControls: .noPan has been deprecated. Use .enablePan instead.' );
return ! this.enablePan;
},
set: function ( value ) {
console.warn( 'THREE.OrbitControls: .noPan has been deprecated. Use .enablePan instead.' );
this.enablePan = ! value;
}
},
noKeys: {
get: function () {
console.warn( 'THREE.OrbitControls: .noKeys has been deprecated. Use .enableKeys instead.' );
return ! this.enableKeys;
},
set: function ( value ) {
console.warn( 'THREE.OrbitControls: .noKeys has been deprecated. Use .enableKeys instead.' );
this.enableKeys = ! value;
}
},
staticMoving : {
get: function () {
console.warn( 'THREE.OrbitControls: .staticMoving has been deprecated. Use .enableDamping instead.' );
return ! this.constraint.enableDamping;
},
set: function ( value ) {
console.warn( 'THREE.OrbitControls: .staticMoving has been deprecated. Use .enableDamping instead.' );
this.constraint.enableDamping = ! value;
}
},
dynamicDampingFactor : {
get: function () {
console.warn( 'THREE.OrbitControls: .dynamicDampingFactor has been renamed. Use .dampingFactor instead.' );
return this.constraint.dampingFactor;
},
set: function ( value ) {
console.warn( 'THREE.OrbitControls: .dynamicDampingFactor has been renamed. Use .dampingFactor instead.' );
this.constraint.dampingFactor = value;
}
}
} );
</script>
When the tween is completed and the controls of camera return enabled the rotation of camera not respect the pivot center of element but follow to take as center the old element position....
Anybody can help me?
Thank's a lot!
enter code here

Three.js Raycaster works with mouse not with touch

I have an annoying problem where my raycasting is fine with the mouse but not with touch.
I setup events as such
$(document).bind('mousedown', onDocumentMouseDown);
$(document).bind('mousewheel', onDocumentMouseWheel);
$(document).bind('touchstart', onDocumentTouchStart);
$(document).bind('touchmove', onDocumentTouchMove);
My mouse event
function onDocumentMouseDown(e) {
e.preventDefault();
detectHotspotClick(e.pageX,e.pageY);
}
My Touch event
function onDocumentTouchStart(e) {
e.preventDefault();
var event = e.originalEvent;
if (event.touches.length == 1) {
var t=event.touches[0];
draggingY = t.pageY;
detectHotspotClick(t.pageX,t.pageY);
}
if (event.touches.length == 2) {
var dx = event.touches[ 0 ].pageX - event.touches[ 1 ].pageX;
var dy = event.touches[ 0 ].pageY - event.touches[ 1 ].pageY;
_touchZoomDistanceEnd = _touchZoomDistanceStart = Math.sqrt( dx * dx + dy * dy );
}
}
My detection
function detectHotspotClick(x,y){
console.log('detectHotspotClick('+ x + "," + y+ ")" );
var raycaster = new THREE.Raycaster();
var mouse = new THREE.Vector2();
mouse.x = ( x / renderer.domElement.width ) * 2 - 1;
mouse.y = - ( y / renderer.domElement.height ) * 2 + 1;
raycaster.setFromCamera( mouse, camera );
var intersects = raycaster.intersectObjects( targetList, true );
if ( intersects.length > 0)
hotspotClick(intersects[ 0 ].object);
}
This works fine with mouse, but I notice on Touch, the mouse.x value always seems to be negetative and I'm not sure that's right.
Is there any other way of doing this I should consider?
Not sure why.. but it works with detectHotspotClick(t.pageX*2,t.pageY*2) on the touchevent, which must report different figures to the mouse event.

Categories