Can someone help! I am simulating a cloth attached to their 4 corners. I am trying to re-locate the 4 pins 0, 10, 88, 98 of the Cloth with an 10x10 array. I want to be able to place each Pin at a different position in x,y,z.
For this simulation I am using Three.js and Cloth.js.
Something similar to this example:
[https://threejs.org/examples/#webgl_animation_cloth][1]
Here is my Code and also the Cloth code I am using.
var pinsFormation = [];
pinsFormation.push( pins );
pins = [ 0, 10, 88, 98 ];
var container, stats;
var camera, scene, renderer, clothGeometry, object;
init();
animate();
function init() {
container = document.createElement( 'div' );
document.body.appendChild( container );
scene = new THREE.Scene();
scene.background = new THREE.Color( 0xFFFFFF );
camera = new THREE.PerspectiveCamera( 30, window.innerWidth / window.innerHeight, 1, 10000 );
camera.position.set( 1000, 50, 1000 );
// cloth
var material_wire = new THREE.MeshBasicMaterial( { color : 0x000000, side: THREE.DoubleSide, wireframe: true } );
clothGeometry = new THREE.ParametricGeometry( clothFunction, cloth.w, cloth.h );
object = new THREE.Mesh( clothGeometry, material_wire ); // clothMaterial
object.position.set( 0, 0, 0 );
scene.add( object );
// renderer
renderer = new THREE.WebGLRenderer( { antialias: true } );
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize( window.innerWidth, window.innerHeight );
container.appendChild( renderer.domElement );
var controls = new THREE.OrbitControls( camera, renderer.domElement );
controls.maxPolarAngle = Math.PI * 1.5;
window.addEventListener( 'resize', onWindowResize, false );
}
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize( window.innerWidth, window.innerHeight );
}
function animate() {
requestAnimationFrame( animate );
var time = Date.now();
var windStrength = Math.cos( time / 7000 ) * 20 + 40;
windForce.set( Math.sin( time / 2000 ), Math.cos( time / 3000 ), Math.sin( time / 1000 ) )
windForce.normalize()
windForce.multiplyScalar( windStrength );
simulate( time );
render();
}
function render() {
var p = cloth.particles;
for ( var i = 0, il = p.length; i < il; i ++ ) {
clothGeometry.vertices[ i ].copy( p[ i ].position );
}
clothGeometry.verticesNeedUpdate = true;
clothGeometry.computeFaceNormals();
clothGeometry.computeVertexNormals();
renderer.render( scene, camera );
}
// cloth.js
var DAMPING = 0.03;
var DRAG = 1 - DAMPING;
var MASS = 0.1;
var restDistance = 25;
var xSegs = 10;
var ySegs = 10;
var clothFunction = plane( restDistance * xSegs, restDistance * ySegs );
var cloth = new Cloth( xSegs, ySegs );
var GRAVITY = 981 * 1.4;
var gravity = new THREE.Vector3( 0, - GRAVITY, 0 ).multiplyScalar( MASS );
var TIMESTEP = 18 / 1000;
var TIMESTEP_SQ = TIMESTEP * TIMESTEP;
var pins = [];
var wind = true;
var windStrength = 2;
var windForce = new THREE.Vector3( 0, 0, 0 );
var tmpForce = new THREE.Vector3();
var lastTime;
function plane( width, height ) {
return function( u, v ) {
var x = ( u - 0.5 ) * width;
var y = ( v - 0.1 ) * height;
var z = 0;
return new THREE.Vector3( x, y, z );
};
}
function Particle( x, y, z, mass ) {
this.position = clothFunction( x, y ); // position
this.previous = clothFunction( x, y ); // previous
this.original = clothFunction( x, y );
this.a = new THREE.Vector3( 0, 0, 0 ); // acceleration
this.mass = mass;
this.invMass = 1 / mass;
this.tmp = new THREE.Vector3();
this.tmp2 = new THREE.Vector3();
}
// Force -> Acceleration
Particle.prototype.addForce = function( force ) {
this.a.add(
this.tmp2.copy( force ).multiplyScalar( this.invMass )
);
};
// Performs Verlet integration
Particle.prototype.integrate = function( timesq ) {
var newPos = this.tmp.subVectors( this.position, this.previous );
newPos.multiplyScalar( DRAG ).add( this.position );
newPos.add( this.a.multiplyScalar( timesq ) );
this.tmp = this.previous;
this.previous = this.position;
this.position = newPos;
this.a.set( 0, 0, 0 );
};
var diff = new THREE.Vector3();
function satisfyConstraints( p1, p2, distance ) {
diff.subVectors( p2.position, p1.position );
var currentDist = diff.length();
if ( currentDist === 0 ) return;
var correction = diff.multiplyScalar( 1 - distance / currentDist );
var correctionHalf = correction.multiplyScalar( 0.5 );
p1.position.add( correctionHalf );
p2.position.sub( correctionHalf );
}
function Cloth( w, h ) {
w = w || 20;
h = h || 20;
this.w = w;
this.h = h;
var particles = [];
var constraints = [];
var u, v;
// Create particles
for ( v = 0; v <= h; v ++ ) {
for ( u = 0; u <= w; u ++ ) {
particles.push(
new Particle( u / w, v / h, 0, MASS )
);
}
}
// Structural
for ( v = 0; v < h; v ++ ) {
for ( u = 0; u < w; u ++ ) {
constraints.push( [
particles[ index( u, v ) ],
particles[ index( u, v + 1 ) ],
restDistance
] );
constraints.push( [
particles[ index( u, v ) ],
particles[ index( u + 1, v ) ],
restDistance
] );
}
}
for ( u = w, v = 0; v < h; v ++ ) {
constraints.push( [
particles[ index( u, v ) ],
particles[ index( u, v + 1 ) ],
restDistance
] );
}
for ( v = h, u = 0; u < w; u ++ ) {
constraints.push( [
particles[ index( u, v ) ],
particles[ index( u + 1, v ) ],
restDistance
] );
}
this.particles = particles;
this.constraints = constraints;
function index( u, v ) {
return u + v * ( w + 1 );
}
this.index = index;
}
function simulate( time ) {
if ( ! lastTime ) {
lastTime = time;
return;
}
var i, il, particles, particle, pt, constraints, constraint;
// Aerodynamics forces
if ( wind ) {
var face, faces = clothGeometry.faces, normal;
particles = cloth.particles;
for ( i = 0, il = faces.length; i < il; i ++ ) {
face = faces[ i ];
normal = face.normal;
tmpForce.copy( normal ).normalize().multiplyScalar( normal.dot( windForce ) );
particles[ face.a ].addForce( tmpForce );
particles[ face.b ].addForce( tmpForce );
particles[ face.c ].addForce( tmpForce );
}
}
for ( particles = cloth.particles, i = 0, il = particles.length; i < il; i ++ ) {
particle = particles[ i ];
particle.addForce( gravity );
particle.integrate( TIMESTEP_SQ );
}
// Start Constraints
constraints = cloth.constraints;
il = constraints.length;
for ( i = 0; i < il; i ++ ) {
constraint = constraints[ i ];
satisfyConstraints( constraint[ 0 ], constraint[ 1 ], constraint[ 2 ] );
}
// Pin Constraints
for ( i = 0, il = pins.length; i < il; i ++ ) {
var xy = pins[ i ];
var p = particles[ xy ];
p.position.copy( particles.original );
p.previous.copy( particles.original );
}
}
The "pin" is just the index of one of the vertices.. so what you'll have to do is identify the vertex corresponding to the spot you want to pin.. you can get that from a raycast when the user clicks the mesh, or figure it our analytically.
Related
I'm in the process of tinkering together a solar system simulator and it is all going well with the exception of one part: when a moon or a planet goes behind its parent body, you can still see it!
There are no transparent objects in the scene, and I have not made any modifications to the order in which objects are rendered, so I'm at a loss as to why objects appear when they are behind another object.
I'm using three.js r71 (problem still appears in r84, though).
To see where it goes wrong: check out http:mrhuffman.net/projects/gp and pick the Martian system; you will see how Phobos or Deimos are still visible even though they are behind Mars.
Here's the code for the scene. It's not as tidy as it could be as I just started working on it, so if you need clarifications or have questions, shoot!
import THREE from '../vendor/three';
import OrbitControls from '../vendor/OrbitControlsES6';
import ColladaLoader from '../vendor/ColladaLoaderES6';
import nBodyProblem from '../algorithms/nBodyProblem';
const scene = ( function () {
//Full screen action
let w = window.innerWidth;
let h = window.innerHeight;
let requestAnimationFrameId = null;
let scene = null;
let camera = null;
let controls = null;
let renderer = null;
let system = null;
let dae = null;
let cameraControlsWrapper = null;
let orbitButton = null;
let view3DButton = null;
let pathCanvas = document.createElement( 'canvas' );
pathCanvas.style.display = 'none';
pathCanvas.style.backgroundImage = 'url(misc/starfield.jpg)';
let ctxPath = pathCanvas.getContext( '2d' );
let massCanvas = document.createElement( 'canvas' );
massCanvas.style.display = 'none';
massCanvas.style.position = 'absolute';
massCanvas.style.zIndex = 2;
massCanvas.style.top = 0;
massCanvas.style.bottom = 0;
let ctxMass = massCanvas.getContext( '2d' );
pathCanvas.width = w;
pathCanvas.height = h;
massCanvas.width = w;
massCanvas.height = h;
ctxPath.translate( w / 2, h / 2 );
ctxMass.translate( w / 2, h / 2 );
function showOrbits() {
orbitButton.style.display = 'none';
cameraControlsWrapper.style.display = 'none';
pathCanvas.style.display = 'block';
massCanvas.style.display = 'block';
view3DButton.style.display = 'block';
}
function hideOrbits() {
view3DButton.style.display = 'none';
pathCanvas.style.display = 'none';
massCanvas.style.display = 'none';
orbitButton.style.display = 'block';
cameraControlsWrapper.style.display = 'block';
}
function createBody( radius, name, type ) {
let segments = type !== 'asteroid' ? 32 : 6;
let geometry = new THREE.SphereGeometry( radius, segments, segments );
let map;
let bumpMap;
switch( type ) {
case 'asteroid':
map = THREE.ImageUtils.loadTexture('textures/Phobos.jpg');
bumpMap = THREE.ImageUtils.loadTexture('textures/PhobosBump.jpg');
break;
case 'custom':
map = THREE.ImageUtils.loadTexture('textures/Acid.jpg');
break;
case 'star':
map = THREE.ImageUtils.loadTexture('textures/Sun.jpg');
break;
case 'spacecraft':
scene.add( dae );
return dae;
default:
map = THREE.ImageUtils.loadTexture('textures/' + name + '.jpg');
bumpMap = THREE.ImageUtils.loadTexture('textures/Phobos.jpg');
}
let material = new THREE.MeshPhongMaterial( { map: map, bumpMap: bumpMap, bumpScale: 0.02 } );
let mesh = new THREE.Mesh( geometry, material );
mesh.rotation.x = 1.5;
scene.add( mesh );
return mesh;
}
function sceneSetup( callback, scenario, container, camPos, camFocus, viewOrbits, view3D, cameraControls ) {
if ( scenario.model === undefined ) {
callback( scenario, container, camPos, camFocus, viewOrbits, view3D, cameraControls );
return;
}
let loader = new ColladaLoader();
loader.options.convertUpAxis = true;
loader.load( './models/' + scenario.model + '/' + scenario.model + '.dae', ( collada ) => {
dae = collada.scene;
dae.scale.setScalar( 1 / 800 );
callback( scenario, container, camPos, camFocus, viewOrbits, view3D, cameraControls );
});
}
function initSimulation( scenario, container, camPos, camFocus, viewOrbits, view3D, cameraControls ) {
cameraControlsWrapper = cameraControls;
orbitButton = viewOrbits;
orbitButton.addEventListener( 'click', showOrbits, false );
view3DButton = view3D;
view3DButton.addEventListener( 'click', hideOrbits, false );
hideOrbits();
scene = new THREE.Scene();
camera = new THREE.PerspectiveCamera( 45, w / h, 0.000001, 1500 );
//Prevent rolling of the camera when you view a body from another
camera.up.set( 0, 0, 1 );
camera.position.set( 0, 90, 150 );
var light = new THREE.AmbientLight(0x404040);
scene.add(light);
renderer = new THREE.WebGLRenderer({
antialias: true,
alpha: true
});
renderer.setSize(w, h);
renderer.setClearColor(0x000000);
container.appendChild( pathCanvas );
container.appendChild( massCanvas );
container.appendChild( renderer.domElement );
controls = new OrbitControls( camera, renderer.domElement );
//Create a new n-body problem from the selected scenario
system = new nBodyProblem( {
g: scenario.g,
law: scenario.law,
dt: scenario.dt,
masses: scenario.masses
});
//Create visual manifestations of the planets, asteroids and star(s)
for (let i = 0; i < system.masses.length; i++) {
let mass = system.masses[ i ];
mass.manifestation = createBody( mass.radius, mass.name, mass.type );
}
const render = function () {
requestAnimationFrameId = requestAnimationFrame( render );
//Update state vectors system.updatePositionVectors().updateVelocityVectors().updateBarycenter().calculateElapsedTime();
//Check if a rocket should be fired and if so check if it should be fired in this iteration; should that be the case, fire!
if ( scenario.rocketBurn === true ) {
if ( system.elapsedTime === scenario.rocketBurnTime ) {
for ( let i = 0; i < system.masses.length; i++ ) {
if ( system.masses[ i ].type === 'spacecraft' ) {
system.masses[ i ].vx = scenario.afterRocketBurnVelocity.vx;
system.masses[ i ].vy = scenario.afterRocketBurnVelocity.vy;
system.masses[ i ].vz = scenario.afterRocketBurnVelocity.vz;
}
}
}
}
ctxMass.clearRect( -0.5 * w, -0.5 * h, w, h );
//Put all the masses in their new positions and set camera position and focus
for (let i = 0; i < system.masses.length; i++) {
let mass = system.masses[ i ];
let x = mass.x * scenario.scale;
let y = mass.y * scenario.scale;
let z = mass.z * scenario.scale;
mass.manifestation.position.set( x, y, z );
let camR = camPos.value;
let name = mass.name;
if ( camR === name ) {
camera.position.set( x, y, z + ( mass.radius * 1.2 ) );
controls.enabled = false;
} else if ( camR === 'free' ) {
controls.enabled = true;
}
if ( camFocus.value === name ) {
camera.lookAt( new THREE.Vector3( x, y, z ) );
//If the camera mode is free, the user can pan, orbit and have fun
if ( camPos === 'free' ) controls.target = new THREE.Vector3( x, y, z );
}
ctxPath.fillStyle = mass.color;
ctxPath.fillRect( x, y, 1, 1 );
ctxMass.beginPath();
ctxMass.fillStyle = mass.color;
ctxMass.arc( x, y, 6, 0, 2 * Math.PI );
ctxMass.fill();
ctxMass.font = "14px Arial";
ctxMass.fillText( mass.name, x + 8, y );
}
//Put the barycenter of the system in its new position
let barycenterX = system.x * scenario.scale;
let barycenterY = system.y * scenario.scale;
ctxMass.strokeStyle = 'limegreen';
ctxMass.lineWidth = 2;
ctxMass.beginPath();
ctxMass.moveTo( barycenterX - 20, barycenterY );
ctxMass.lineTo( barycenterX + 20, barycenterY );
ctxMass.moveTo( barycenterX, barycenterY - 20 );
ctxMass.lineTo( barycenterX, barycenterY + 20 );
ctxMass.stroke();
ctxMass.fillStyle = 'limegreen';
ctxMass.font = "14px Arial";
ctxMass.fillText( 'Barycenter', barycenterX, barycenterY - 25 );
renderer.render( scene, camera );
};
//Makes the scene responsive
//Note that traces are cleared when the size of the viewport changes
window.addEventListener( 'resize', onWindowResize, false );
function onWindowResize() {
w = window.innerWidth;
h = window.innerHeight;
camera.aspect = w / h;
camera.updateProjectionMatrix();
renderer.setSize( w, h );
pathCanvas.width = w;
pathCanvas.height = h;
massCanvas.width = w;
massCanvas.height = h;
ctxPath.clearRect( -0.5 * w, -0.5 * h, w, h );
ctxMass.clearRect( -0.5 * w, -0.5 * h, w, h );
ctxPath.translate( w / 2, h / 2 );
ctxMass.translate( w / 2, h / 2 );
}
render();
}
//Tidy up
function resetSimulation() {
orbitButton.removeEventListener( 'click', showOrbits );
view3DButton.removeEventListener( 'click', hideOrbits );
cancelAnimationFrame( requestAnimationFrameId );
ctxPath.clearRect( -0.5 * w, -0.5 * h, w, h );
renderer.domElement.parentNode.removeChild( renderer.domElement );
pathCanvas.parentNode.removeChild( pathCanvas );
massCanvas.parentNode.removeChild( massCanvas );
}
//API
return {
startSimulation: ( scenario, container, camPos, camFocus, viewOrbits, view3D, cameraControls ) => {
sceneSetup( initSimulation, scenario, container, camPos, camFocus, viewOrbits, view3D, cameraControls );
},
resetSimulation: resetSimulation
};
}());
export default scene;
I want to use three.js bird sample in my WordPress website in top of a page. In my header I placed
<script type="text/javascript" src="http://somesite.com/wp-content/themes/theme/assets/js/three.js"></script>
<script type="text/javascript" src="http://somesite.com/wp-content/themes/theme/assets/js/Projector.js"></script>
<script type="text/javascript" src="http://somesite.com/wp-content/themes/theme/assets/js/CanvasRenderer.js"></script>
<script type="text/javascript" src="http://somesite.com/wp-content/themes/theme/assets/js/stats.min.js"></script>
<script type="text/javascript" src="http://somesite.com/wp-content/themes/theme/assets/js/Bird.js"></script>
and when I use visual composer js block to display
<script>
// Based on http://www.openprocessing.org/visuals/?visualID=6910
var Boid = function() {
var vector = new THREE.Vector3(),
_acceleration, _width = 500, _height = 500, _depth = 200, _goal, _neighborhoodRadius = 100,
_maxSpeed = 4, _maxSteerForce = 0.1, _avoidWalls = false;
this.position = new THREE.Vector3();
this.velocity = new THREE.Vector3();
_acceleration = new THREE.Vector3();
this.setGoal = function ( target ) {
_goal = target;
};
this.setAvoidWalls = function ( value ) {
_avoidWalls = value;
};
this.setWorldSize = function ( width, height, depth ) {
_width = width;
_height = height;
_depth = depth;
};
this.run = function ( boids ) {
if ( _avoidWalls ) {
vector.set( - _width, this.position.y, this.position.z );
vector = this.avoid( vector );
vector.multiplyScalar( 5 );
_acceleration.add( vector );
vector.set( _width, this.position.y, this.position.z );
vector = this.avoid( vector );
vector.multiplyScalar( 5 );
_acceleration.add( vector );
vector.set( this.position.x, - _height, this.position.z );
vector = this.avoid( vector );
vector.multiplyScalar( 5 );
_acceleration.add( vector );
vector.set( this.position.x, _height, this.position.z );
vector = this.avoid( vector );
vector.multiplyScalar( 5 );
_acceleration.add( vector );
vector.set( this.position.x, this.position.y, - _depth );
vector = this.avoid( vector );
vector.multiplyScalar( 5 );
_acceleration.add( vector );
vector.set( this.position.x, this.position.y, _depth );
vector = this.avoid( vector );
vector.multiplyScalar( 5 );
_acceleration.add( vector );
}/* else {
this.checkBounds();
}
*/
if ( Math.random() > 0.5 ) {
this.flock( boids );
}
this.move();
};
this.flock = function ( boids ) {
if ( _goal ) {
_acceleration.add( this.reach( _goal, 0.005 ) );
}
_acceleration.add( this.alignment( boids ) );
_acceleration.add( this.cohesion( boids ) );
_acceleration.add( this.separation( boids ) );
};
this.move = function () {
this.velocity.add( _acceleration );
var l = this.velocity.length();
if ( l > _maxSpeed ) {
this.velocity.divideScalar( l / _maxSpeed );
}
this.position.add( this.velocity );
_acceleration.set( 0, 0, 0 );
};
this.checkBounds = function () {
if ( this.position.x > _width ) this.position.x = - _width;
if ( this.position.x < - _width ) this.position.x = _width;
if ( this.position.y > _height ) this.position.y = - _height;
if ( this.position.y < - _height ) this.position.y = _height;
if ( this.position.z > _depth ) this.position.z = - _depth;
if ( this.position.z < - _depth ) this.position.z = _depth;
};
//
this.avoid = function ( target ) {
var steer = new THREE.Vector3();
steer.copy( this.position );
steer.sub( target );
steer.multiplyScalar( 1 / this.position.distanceToSquared( target ) );
return steer;
};
this.repulse = function ( target ) {
var distance = this.position.distanceTo( target );
if ( distance < 150 ) {
var steer = new THREE.Vector3();
steer.subVectors( this.position, target );
steer.multiplyScalar( 0.5 / distance );
_acceleration.add( steer );
}
};
this.reach = function ( target, amount ) {
var steer = new THREE.Vector3();
steer.subVectors( target, this.position );
steer.multiplyScalar( amount );
return steer;
};
this.alignment = function ( boids ) {
var boid, velSum = new THREE.Vector3(),
count = 0;
for ( var i = 0, il = boids.length; i < il; i++ ) {
if ( Math.random() > 0.6 ) continue;
boid = boids[ i ];
distance = boid.position.distanceTo( this.position );
if ( distance > 0 && distance <= _neighborhoodRadius ) {
velSum.add( boid.velocity );
count++;
}
}
if ( count > 0 ) {
velSum.divideScalar( count );
var l = velSum.length();
if ( l > _maxSteerForce ) {
velSum.divideScalar( l / _maxSteerForce );
}
}
return velSum;
};
this.cohesion = function ( boids ) {
var boid, distance,
posSum = new THREE.Vector3(),
steer = new THREE.Vector3(),
count = 0;
for ( var i = 0, il = boids.length; i < il; i ++ ) {
if ( Math.random() > 0.6 ) continue;
boid = boids[ i ];
distance = boid.position.distanceTo( this.position );
if ( distance > 0 && distance <= _neighborhoodRadius ) {
posSum.add( boid.position );
count++;
}
}
if ( count > 0 ) {
posSum.divideScalar( count );
}
steer.subVectors( posSum, this.position );
var l = steer.length();
if ( l > _maxSteerForce ) {
steer.divideScalar( l / _maxSteerForce );
}
return steer;
};
this.separation = function ( boids ) {
var boid, distance,
posSum = new THREE.Vector3(),
repulse = new THREE.Vector3();
for ( var i = 0, il = boids.length; i < il; i ++ ) {
if ( Math.random() > 0.6 ) continue;
boid = boids[ i ];
distance = boid.position.distanceTo( this.position );
if ( distance > 0 && distance <= _neighborhoodRadius ) {
repulse.subVectors( this.position, boid.position );
repulse.normalize();
repulse.divideScalar( distance );
posSum.add( repulse );
}
}
return posSum;
}
}
</script>
<script>
var SCREEN_WIDTH = window.innerWidth,
SCREEN_HEIGHT = window.innerHeight,
SCREEN_WIDTH_HALF = SCREEN_WIDTH / 2,
SCREEN_HEIGHT_HALF = SCREEN_HEIGHT / 2;
var camera, scene, renderer,
birds, bird;
var boid, boids;
var stats;
init();
animate();
function init() {
camera = new THREE.PerspectiveCamera( 75, SCREEN_WIDTH / SCREEN_HEIGHT, 1, 10000 );
camera.position.z = 450;
scene = new THREE.Scene();
birds = [];
boids = [];
for ( var i = 0; i < 200; i ++ ) {
boid = boids[ i ] = new Boid();
boid.position.x = Math.random() * 400 - 200;
boid.position.y = Math.random() * 400 - 200;
boid.position.z = Math.random() * 400 - 200;
boid.velocity.x = Math.random() * 2 - 1;
boid.velocity.y = Math.random() * 2 - 1;
boid.velocity.z = Math.random() * 2 - 1;
boid.setAvoidWalls( true );
boid.setWorldSize( 500, 500, 400 );
bird = birds[ i ] = new THREE.Mesh( new Bird(), new THREE.MeshBasicMaterial( { color:Math.random() * 0xffffff, side: THREE.DoubleSide } ) );
bird.phase = Math.floor( Math.random() * 62.83 );
scene.add( bird );
}
renderer = new THREE.CanvasRenderer();
renderer.setClearColor( 0xffffff );
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize( SCREEN_WIDTH, SCREEN_HEIGHT );
document.addEventListener( 'mousemove', onDocumentMouseMove, false );
document.body.appendChild( renderer.domElement );
stats = new Stats();
document.getElementById( 'container' ).appendChild(stats.dom);
//
window.addEventListener( 'resize', onWindowResize, false );
}
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize( window.innerWidth, window.innerHeight );
}
function onDocumentMouseMove( event ) {
var vector = new THREE.Vector3( event.clientX - SCREEN_WIDTH_HALF, - event.clientY + SCREEN_HEIGHT_HALF, 0 );
for ( var i = 0, il = boids.length; i < il; i++ ) {
boid = boids[ i ];
vector.z = boid.position.z;
boid.repulse( vector );
}
}
//
function animate() {
requestAnimationFrame( animate );
stats.begin();
render();
stats.end();
}
function render() {
for ( var i = 0, il = birds.length; i < il; i++ ) {
boid = boids[ i ];
boid.run( boids );
bird = birds[ i ];
bird.position.copy( boids[ i ].position );
color = bird.material.color;
color.r = color.g = color.b = ( 500 - bird.position.z ) / 1000;
bird.rotation.y = Math.atan2( - boid.velocity.z, boid.velocity.x );
bird.rotation.z = Math.asin( boid.velocity.y / boid.velocity.length() );
bird.phase = ( bird.phase + ( Math.max( 0, bird.rotation.z ) + 0.1 ) ) % 62.83;
bird.geometry.vertices[ 5 ].y = bird.geometry.vertices[ 4 ].y = Math.sin( bird.phase ) * 5;
}
renderer.render( scene, camera );
}
</script>
nothing is showing up. I tested in html on local machine, and it was working fine. Can anyone help me?
I'm trying to add an offset to the camera after deviceControls.update(); command. I used DeviceOrientationControls as shown in this first example.
The offset will be the result of a drag gesture, as presents in this example.
When I multiply the 2 quaternions (I have tried a x b and b x a), the final result is not correct.
Here is my operation :
const m1 = new THREE.Matrix4();
m1.lookAt(new THREE.Vector3(), camera.target, THREE.Object3D.DefaultUp.clone());
const quater = new THREE.Quaternion();
quater.setFromRotationMatrix(m1);
const finalQuater = new THREE.Quaternion();
finalQuater.multiplyQuaternions(quater, camera.quaternion);
camera.quaternion.copy(finalQuater);
camera.target is my final drag target (Vector3), and camera.quaternion has been set by deviceControls.update() and is equals to the camera orientation, according to the device gyroscope.
Thanks for your help
Update : I have tried to changer rotate order, same problem. I think it is due to the origin change after the device orientation update, but can't find how to solve.
DeviceOrientationControls now has a property alphaOffsetAngle, and a method
controls.updateAlphaOffsetAngle( angle ); // angle is in radians
that will rotate the scene around the three.js 'Y' axis.
three.js r.77
var rotY = 0;
var rotX = 0;
function setObjectQuaternion(quaternion, alpha, beta, gamma, orient) {
var zee = new THREE.Vector3( 0, 0, 1 );
var euler = new THREE.Euler();
var q0 = new THREE.Quaternion();
var q1 = new THREE.Quaternion( -Math.sqrt( 0.5 ), 0, 0, Math.sqrt( 0.5 ) ); // - PI/2 around the x-axis
if (screenOrientation == 0) {
var vectorFingerY = new THREE.Vector3( 1, 0, 0 );
var fingerQY = new THREE.Quaternion();
fingerQY.setFromAxisAngle ( vectorFingerY, -rotX );
}else if (screenOrientation == 180) {
var vectorFingerY = new THREE.Vector3( 1, 0, 0 );
var fingerQY = new THREE.Quaternion();
fingerQY.setFromAxisAngle ( vectorFingerY, rotX );
}else if (screenOrientation == 90) {
var vectorFingerY = new THREE.Vector3( 0, 1, 0 );
var fingerQY = new THREE.Quaternion();
fingerQY.setFromAxisAngle ( vectorFingerY, rotX );
}else if (screenOrientation == -90) {
var vectorFingerY = new THREE.Vector3( 0, 1, 0 );
var fingerQY = new THREE.Quaternion();
fingerQY.setFromAxisAngle ( vectorFingerY, -rotX );
}
q1.multiply( fingerQY );
euler.set( beta, alpha, - gamma, 'YXZ' ); // 'ZXY' for the device, but 'YXZ' for us
quaternion.setFromEuler( euler ); // orient the device
quaternion.multiply( q1 ); // camera looks out the back of the device, not the top
quaternion.multiply( q0.setFromAxisAngle( zee, - orient ) ); // adjust for screen orientation
};
function update(camera) {
if (window.orientation !== undefined && window.orientation !== null) screenOrientation = window.orientation;
var alpha = deviceOrientation.alpha ? THREE.Math.degToRad( deviceOrientation.alpha ) : 0; // Z
var beta = deviceOrientation.beta ? THREE.Math.degToRad( deviceOrientation.beta ) : 0; // X'
var gamma = deviceOrientation.gamma ? THREE.Math.degToRad( deviceOrientation.gamma ) : 0; // Y''
var orient = screenOrientation ? THREE.Math.degToRad( screenOrientation ) : 0; // O
setObjectQuaternion( camera.quaternion, alpha, beta, gamma, orient );
};
add this to your init
container.appendChild( renderer.domElement );
renderer.domElement.addEventListener( 'touchstart', function (e) {
if (controls) {
e.preventDefault();
e.stopPropagation();
tempX = e.touches[ 0 ].pageX;
tempY = e.touches[ 0 ].pageY;
}
}, false );
renderer.domElement.addEventListener( 'touchmove', function (e) {
if (controls) {
e.preventDefault();
e.stopPropagation();
rotY += THREE.Math.degToRad((tempX - e.touches[ 0 ].pageX)/4);
rotX += THREE.Math.degToRad((tempY - e.touches[ 0 ].pageY)/4);
mesh.quaternion.copy(MeshStartQY);
var vectorFingerY = new THREE.Vector3( 0, 1, 0 );
var fingerQY = new THREE.Quaternion();
fingerQY.setFromAxisAngle ( vectorFingerY, rotY );
mesh.quaternion.multiply(fingerQY);
tempX = e.touches[ 0 ].pageX;
tempY = e.touches[ 0 ].pageY;
}
}, false );
I have a bird script taken from the project "three.js". Now, there's the issue: There are so many birds flying around, it's not realistic anymore. I have tried to adjust many options but I can't seem to find the right one. Note that I'm a noob in this code.
var Boid = function() {
var vector = new THREE.Vector3(),
_acceleration, _width = 500, _height = 500, _depth = 200, _goal, _neighborhoodRadius = 50,
_maxSpeed = 4, _maxSteerForce = 0.1, _avoidWalls = false;
this.position = new THREE.Vector3();
this.velocity = new THREE.Vector3();
_acceleration = new THREE.Vector3();
this.setGoal = function ( target ) {
_goal = target;
}
this.setAvoidWalls = function ( value ) {
_avoidWalls = value;
}
this.setWorldSize = function ( width, height, depth ) {
_width = width;
_height = height;vector
_depth = depth;
}
this.run = function ( boids ) {
if ( _avoidWalls ) {
vector.set( - _width, this.position.y, this.position.z );
vector = this.avoid( vector );
vector.multiplyScalar( 5 );
_acceleration.addSelf( vector );
vector.set( _width, this.position.y, this.position.z );
vector = this.avoid( vector );
vector.multiplyScalar( 5 );
_acceleration.addSelf( vector );
vector.set( this.position.x, - _height, this.position.z );
vector = this.avoid( vector );
vector.multiplyScalar( 5 );
_acceleration.addSelf( vector );
vector.set( this.position.x, _height, this.position.z );
vector = this.avoid( vector );
vector.multiplyScalar( 5 );
_acceleration.addSelf( vector );
vector.set( this.position.x, this.position.y, - _depth );
vector = this.avoid( vector );
vector.multiplyScalar( 5 );
_acceleration.addSelf( vector );
vector.set( this.position.x, this.position.y, _depth );
vector = this.avoid( vector );
vector.multiplyScalar( 5 );
_acceleration.addSelf( vector );
}/* else {
this.checkBounds();
}
*/
if ( Math.random() > 0.5 ) {
this.flock( boids );
}
this.move();
}
this.flock = function ( boids ) {
if ( _goal ) {
_acceleration.addSelf( this.reach( _goal, 0.005 ) );
}
_acceleration.addSelf( this.alignment( boids ) );
_acceleration.addSelf( this.cohesion( boids ) );
_acceleration.addSelf( this.separation( boids ) );
}
this.move = function () {
this.velocity.addSelf( _acceleration );
var l = this.velocity.length();
if ( l > _maxSpeed ) {
this.velocity.divideScalar( l / _maxSpeed );
}
this.position.addSelf( this.velocity );
_acceleration.set( 0, 0, 0 );
}
this.checkBounds = function () {
if ( this.position.x > _width ) this.position.x = - _width;
if ( this.position.x < - _width ) this.position.x = _width;
if ( this.position.y > _height ) this.position.y = - _height;
if ( this.position.y < - _height ) this.position.y = _height;
if ( this.position.z > _depth ) this.position.z = - _depth;
if ( this.position.z < - _depth ) this.position.z = _depth;
}
//
this.avoid = function ( target ) {
var steer = new THREE.Vector3();
steer.copy( this.position );
steer.subSelf( target );
steer.multiplyScalar( 1 / this.position.distanceToSquared( target ) );
return steer;
}
this.repulse = function ( target ) {
var distance = this.position.distanceTo( target );
if ( distance < 150 ) {
var steer = new THREE.Vector3();
steer.sub( this.position, target );
steer.multiplyScalar( 0.5 / distance );
_acceleration.addSelf( steer );
}
}
this.reach = function ( target, amount ) {
var steer = new THREE.Vector3();
steer.sub( target, this.position );
steer.multiplyScalar( amount );
return steer;
}
this.alignment = function ( boids ) {
var boid, velSum = new THREE.Vector3(),
count = 0;
for ( var i = 0, il = boids.length; i < il; i++ ) {
if ( Math.random() > 0.6 ) continue;
boid = boids[ i ];
distance = boid.position.distanceTo( this.position );
if ( distance > 0 && distance <= _neighborhoodRadius ) {
velSum.addSelf( boid.velocity );
count++;
}
}
if ( count > 0 ) {
velSum.divideScalar( count );
var l = velSum.length();
if ( l > _maxSteerForce ) {
velSum.divideScalar( l / _maxSteerForce );
}
}
return velSum;
}
this.cohesion = function ( boids ) {
var boid, distance,
posSum = new THREE.Vector3(),
steer = new THREE.Vector3(),
count = 0;
for ( var i = 0, il = boids.length; i < il; i ++ ) {
if ( Math.random() > 0.6 ) continue;
boid = boids[ i ];
distance = boid.position.distanceTo( this.position );
if ( distance > 0 && distance <= _neighborhoodRadius ) {
posSum.addSelf( boid.position );
count++;
}
}
if ( count > 0 ) {
posSum.divideScalar( count );
}
steer.sub( posSum, this.position );
var l = steer.length();
if ( l > _maxSteerForce ) {
steer.divideScalar( l / _maxSteerForce );
}
return steer;
}
this.separation = function ( boids ) {
var boid, distance,
posSum = new THREE.Vector3(),
repulse = new THREE.Vector3();
for ( var i = 0, il = boids.length; i < il; i ++ ) {
if ( Math.random() > 0.6 ) continue;
boid = boids[ i ];
distance = boid.position.distanceTo( this.position );
if ( distance > 0 && distance <= _neighborhoodRadius ) {
repulse.sub( this.position, boid.position );
repulse.normalize();
repulse.divideScalar( distance );
posSum.addSelf( repulse );
}
}
return posSum;
}
}
</script>
<script>
var SCREEN_WIDTH = window.innerWidth,
SCREEN_HEIGHT = window.innerHeight,
SCREEN_WIDTH_HALF = SCREEN_WIDTH / 2,
SCREEN_HEIGHT_HALF = SCREEN_HEIGHT / 2;
var camera, scene, renderer,
birds, bird;
var boid, boids;
init();
animate();
function init() {
camera = new THREE.PerspectiveCamera( 75, SCREEN_WIDTH / SCREEN_HEIGHT, 1, 10000 );
camera.position.z = 450;
scene = new THREE.Scene();
birds = [];
boids = [];
for ( var i = 0; i < 200; i ++ ) {
boid = boids[ i ] = new Boid();
boid.position.x = Math.random() * 400 - 200;
boid.position.y = Math.random() * 400 - 200;
boid.position.z = Math.random() * 400 - 200;
boid.velocity.x = Math.random() * 2 - 1;
boid.velocity.y = Math.random() * 2 - 1;
boid.velocity.z = Math.random() * 2 - 1;
boid.setAvoidWalls( true );
boid.setWorldSize( 500, 500, 400 );
bird = birds[ i ] = new THREE.Mesh( new Bird(), new THREE.MeshBasicMaterial( { color:Math.random() * 0xffffff, side: THREE.DoubleSide } ) );
bird.phase = Math.floor( Math.random() * 62.83 );
bird.position = boids[ i ].position;
scene.add( bird );
}
renderer = new THREE.CanvasRenderer();
// renderer.autoClear = false;
renderer.setSize( SCREEN_WIDTH, SCREEN_HEIGHT );
document.addEventListener( 'mousemove', onDocumentMouseMove, false );
document.body.appendChild( renderer.domElement );
//
window.addEventListener( 'resize', onWindowResize, false );
}
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize( window.innerWidth, window.innerHeight );
}
function onDocumentMouseMove( event ) {
var vector = new THREE.Vector3( event.clientX - SCREEN_WIDTH_HALF, - event.clientY + SCREEN_HEIGHT_HALF, 0 );
for ( var i = 0, il = boids.length; i < il; i++ ) {
boid = boids[ i ];
vector.z = boid.position.z;
boid.repulse( vector );
}
}
//
function animate() {
requestAnimationFrame( animate );
render();
stats.update();
}
function render() {
for ( var i = 0, il = birds.length; i < il; i++ ) {
boid = boids[ i ];
boid.run( boids );
bird = birds[ i ];
color = bird.material.color;
color.r = color.g = color.b = ( 500 - bird.position.z ) / 1000;
bird.rotation.y = Math.atan2( - boid.velocity.z, boid.velocity.x );
bird.rotation.z = Math.asin( boid.velocity.y / boid.velocity.length() );
bird.phase = ( bird.phase + ( Math.max( 0, bird.rotation.z ) + 0.1 ) ) % 62.83;
bird.geometry.vertices[ 5 ].y = bird.geometry.vertices[ 4 ].y = Math.sin( bird.phase ) * 5;
}
renderer.render( scene, camera );
}
My guess is the for loop in the init()
for ( var i = 0; i < 200; i ++ ) { <-- reduce the 200
this is my situation:
I want to show twitter tweets like this: http://threejs.org/examples/css3d_periodictable.html
This is what I have now:http://nielsvroman.be/twitter/root/index.php
The boxes are tweets from the user: #BachelorGDM.
What I would like now is that there is always one selected and that you can navigate through them with the arrow keys on the keyboard.
This is my javascript code:
// THE TWEETS
var data = loadTweets()
// FUNCTION SHOW THE TWEETS
function ShowTweets(){
// VARS TABLE WITH TWEETS / PLACE IN COLUMN / PLACE IN ROW
var table = [];
var column = 1;
var row = 1;
// LOOP THROUGH DATA AND CREATE TABLE WITH TWEET DETAILS + PLACE
$.each(data, function(i) {
// DETAILS TWEET
var idstring = data[i].id_str;
var screenname = data[i].user.screen_name;
var imageurl = data[i].user.profile_image_url;
// 9 TWEETS NEXT TO EACH OTHER
if(column % 9 == 0)
{
row++
column = 1;
}
var array = [imageurl, idstring, screenname, column, row ]
column++;
table.push(array);
});
// VARIABLES THREE JS
var camera, scene, renderer;
var controls;
var objects = [];
var targets = { table: [], sphere: [], helix: [], grid: [] };
init(); // CALL INIT FUNCTION
animate(); // CALL ANIMATE FUNCTION
function init() {
// INITIALIZE CAMERA
camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 1, 5000 );
camera.position.z = 1800;
// INITIALIZE SCENE
scene = new THREE.Scene();
// LOOP THROUGH TABLE ARRAY (ARRAY WITH ARRAYS IN IT)
for ( var i = 0; i < table.length; i ++ ) {
var item = table[i]; // ITEM IS ARRAY WITH [imageurl, idstring, screenname, column, row]
var element = document.createElement( 'div' );
element.className = 'element';
element.id = item[1]; // ITEM IDSTRING
element.style.backgroundColor = 'rgba(0,127,127,' + ( Math.random() * 0.5 + 0.25 ) + ')'; // BG COLOR + OPACITY FROM FRAME
var number = document.createElement( 'div' );
number.className = 'number';
number.textContent = i + 1; // NUMBER IN THE RIGHT TOP
element.appendChild( number );
var symbol = document.createElement( 'div' );
symbol.className = 'symbol';
var img = document.createElement('img');
img.src = item[0]; // IMAGE SOURCE IS LINK TO IMAGE
symbol.appendChild(img);
element.appendChild( symbol );
var details = document.createElement( 'div' );
details.className = 'details';
details.innerHTML = "" + '<br>' + item[2]; // SCREENNAME
element.appendChild( details );
var object = new THREE.CSS3DObject( element );
// POSITION OBJECTS AGAINST EACH OTHER
object.position.x = Math.random() * 4000 - 2000;
object.position.y = Math.random() * 4000 - 2000;
object.position.z = Math.random() * 4000 - 2000;
// ADD OBJECTS TO SCENE
scene.add(object);
// ADD OBJECT TO OBJECTS ARRAY
objects.push(object);
}
// TABLE VIEW
for ( var i = 0; i < objects.length; i ++ ) {
var item = table[i]; // ITEM IS ARRAY WITH [imageurl, idstring, screenname, column, row]
var object = new THREE.Object3D();
object.position.x = ( item[3] * 160 ) - 1540; // X-POSITION (COLUMN)
object.position.y = - ( item[4] * 200 ) + 1100; // Y-POSITION (ROW)
// targets = { table: [], sphere: [], helix: [], grid: [] };
targets.table.push(object); // PUSH OBJECT IN TABLE ARRAY (IN TARGETS ARRAY)
}
// SPHERE VIEW
var vector = new THREE.Vector3();
for ( var i = 0, l = objects.length; i < l; i ++ ) {
var phi = Math.acos( -1 + ( 2 * i ) / l );
var theta = Math.sqrt( l * Math.PI ) * phi;
var object = new THREE.Object3D();
object.position.x = 1000 * Math.cos( theta ) * Math.sin( phi );
object.position.y = 1000 * Math.sin( theta ) * Math.sin( phi );
object.position.z = 1000 * Math.cos( phi );
vector.copy( object.position ).multiplyScalar( 2 );
object.lookAt( vector );
// targets = { table: [], sphere: [], helix: [], grid: [] };
targets.sphere.push( object ); // PUSH OBJECT IN SPHERES ARRAY (IN TARGETS ARRAY)
}
// HELIX VIEW
var vector = new THREE.Vector3();
for ( var i = 0, l = objects.length; i < l; i ++ ) {
var phi = i * 0.175 + Math.PI;
var object = new THREE.Object3D();
object.position.x = 1100 * Math.sin( phi );
object.position.y = - ( i * 8 ) + 450;
object.position.z = 1100 * Math.cos( phi );
vector.copy( object.position );
vector.x *= 2;
vector.z *= 2;
object.lookAt( vector );
// targets = { table: [], sphere: [], helix: [], grid: [] };
targets.helix.push( object ); // PUSH OBJECT IN HELIX ARRAY (IN TARGETS ARRAY)
}
// GRID VIEW
for ( var i = 0; i < objects.length; i ++ ) {
var object = new THREE.Object3D();
object.position.x = ( ( i % 5 ) * 400 ) - 800;
object.position.y = ( - ( Math.floor( i / 5 ) % 5 ) * 400 ) + 800;
object.position.z = ( Math.floor( i / 25 ) ) * 1000 - 2000;
// targets = { table: [], sphere: [], helix: [], grid: [] };
targets.grid.push( object ); // PUSH OBJECT IN GRID ARRAY (IN TARGETS ARRAY)
}
renderer = new THREE.CSS3DRenderer();
renderer.setSize( window.innerWidth, window.innerHeight );
renderer.domElement.style.position = 'absolute';
// ADD RENDERER TO CONTAINER
document.getElementById( 'container' ).appendChild( renderer.domElement );
// TRACKBALLCONTROLS => WHEN YOU HOLD DOWN MOUSECLICK
controls = new THREE.TrackballControls( camera, renderer.domElement );
controls.rotateSpeed = 0.5;
//controls.minDistance = 500; // MAX ZOOM IN => MIN DISTANCE
controls.maxDistance = 2500; // MAX ZOOM OUT => MAX DISTANCE
controls.zoomSpeed = 1; // STANDARD IS 1.2
controls.keys = [ 37 /*LEFT*/, 38 /*UP*/, 39 /*RIGHT*/, 40 /*DOWN*/ ];
controls.addEventListener( 'change', render ); // RENDER ON CHANGE
var button = document.getElementById( 'table' );
button.addEventListener( 'click', function ( event ) {
transform( targets.table, 2000 );
}, false );
var button = document.getElementById( 'sphere' );
button.addEventListener( 'click', function ( event ) {
transform( targets.sphere, 2000 );
}, false );
var button = document.getElementById( 'helix' );
button.addEventListener( 'click', function ( event ) {
transform( targets.helix, 2000 );
}, false );
var button = document.getElementById( 'grid' );
button.addEventListener( 'click', function ( event ) {
transform( targets.grid, 2000 );
}, false );
transform( targets.table, 5000 );
//
window.addEventListener( 'resize', onWindowResize, false );
// WHEN PRESSED ON KEY
window.addEventListener( 'keydown', keydown, false );
function keydown( event ) {
};
}
function transform( targets, duration ) {
TWEEN.removeAll();
for ( var i = 0; i < objects.length; i ++ ) {
var object = objects[ i ];
var target = targets[ i ];
new TWEEN.Tween( object.position )
.to( { x: target.position.x, y: target.position.y, z: target.position.z }, Math.random() * duration + duration )
.easing( TWEEN.Easing.Exponential.InOut )
.start();
new TWEEN.Tween( object.rotation )
.to( { x: target.rotation.x, y: target.rotation.y, z: target.rotation.z }, Math.random() * duration + duration )
.easing( TWEEN.Easing.Exponential.InOut )
.start();
}
new TWEEN.Tween( this )
.to( {}, duration * 2 )
.onUpdate( render )
.start();
}
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize( window.innerWidth, window.innerHeight );
}
function animate() {
requestAnimationFrame( animate );
TWEEN.update();
controls.update();
}
// RENDER SCENE/CAMERA
function render() {
renderer.render( scene, camera );
}
}
My tweets are initially in the variable data.
I now have added this to my code:
controls.keys = [ 37 /*LEFT*/, 38 /*UP*/, 39 /*RIGHT*/, 40 /*DOWN*/ ];
window.addEventListener( 'keydown', keydown, false );
function keydown( event ) {};
But now how can I navigate through the tweets (boxes) with my arrows on the keyboard?
I want to change the css of the selected box and open an overlay of the associated tweet. (You can see this when you click on a box on the link)
I have no clue on how to start with this. Can somebody help me?
You could make a stylesheet with a "selected" class, which when you click one of the elements, it removes all other instances of the class "selected" and adds ".selected" to that specific element that was clicked. Then with keyboard input, you do the same thing, but move to the next element. It could be simplified a bit with jQuery, though done without as well.
.element .selected {
background-color: #cccccc;
}
javascript:
document.addEventListener("click", function(e) {
if (e.target === *element you want*)
// remove other selected classes code here.
document.addClass += " " + ".selected"