I'm using Three.js to display the line of a particle following a random walk. Since geometries cannot be enlarged dynamically, I'm removing the line from the scene, altering the geometry and adding a new line to the scene. Here's the relevant code:
var step = .5;
var material = new THREE.LineBasicMaterial({ color: 0x0077ff });
var geometry = new THREE.Geometry();
geometry.vertices.push( new THREE.Vector3(0,0,0) );
scene.add( new THREE.Line( geometry, material ) );
var render = function () {
requestAnimationFrame( render );
renderer.render(scene, camera);
controls.update();
}
function addStep() {
scene.remove( scene.children[1] );
last = geometry.vertices[ geometry.vertices.length - 1 ];
geometry.vertices.push( new THREE.Vector3( last.x + (2*Math.random()-1)*step,
last.y + (2*Math.random()-1)*step, last.z + (2*Math.random()-1)*step ));
scene.add( new THREE.Line( geometry.clone(), material ) );
}
setInterval( addStep, 500);
render();
This codes works, but only if the geometry is cloned at each step. If the last line of addStep() is changed to
scene.add( new THREE.Line( geometry, material ) );
then it can be called as many times as desired before rendering starts to add line segments, but if called after rendering starts then nothing appears.
Presumably I'm missing something simple about graphics buffers or variable assignments, but I would appreciate if someone could explain why the modified geometry cannot be reused without cloning.
#PaulMasson Here is a jsFiddle with an adaptation of your code:
http://jsfiddle.net/theo/u6y4n67q/
function addStep() {
vertices = geometry.vertices;
last = vertices[ vertices.length - 1 ];
vertices.push(
new THREE.Vector3(
last.x + ( 2 * Math.random() - 1 ) * step,
last.y + ( 2 * Math.random() - 1 ) * step,
last.z + ( 2 * Math.random() - 1 ) * step )
);
geometry = new THREE.Geometry();
geometry.vertices = vertices;
scene.remove( line );
line = new THREE.Line( geometry, material )
scene.add( line );
}
The only real difference is that this snippet uses new THREE.Geometry() instead of clone.
My guess is that Three.js geometry is tightly associated with with its representation in buffer geometry. Thus the only way to edit the geometry is to clone it or new it.
Related
I am trying to create an opening door when the user clicks on a sphere in three.js. I tried doing it with a model but I managed to get it to just appear in the correct position rather than rota to it through animation.
I decided to try to focus on an easier scenario and just use a rectangle I would make in three.js. After hours of trying to get it to work I managed to get it to spin and stop when at 90 degrees. However it was rotating around its y axis which was found in the center of the rectangle.
I found this tutorial : https://jsfiddle.net/uxzmsLfr/1/
var camera, scene, renderer;
var mesh, pivot;
init();
animate();
function init() {
camera = new THREE.PerspectiveCamera( 70, window.innerWidth / window.innerHeight, 0.01, 10 );
camera.position.z = 1;
scene = new THREE.Scene();
var geometry = new THREE.PlaneGeometry( 0.2, 0.5, 0.2 );
var material = new THREE.MeshNormalMaterial();
mesh = new THREE.Mesh( geometry, material );
mesh.position.set(0, -.25, 0);
scene.add( mesh );
var geo2 = geometry.clone();
geo2.rotateZ(THREE.Math.degToRad(90)); // ROTATE GEOMETRY SO IT'S IN THE CORRECT ORIENTATION
var mesh2 = new THREE.Mesh( geo2, material );
mesh2.position.set( 0, .25, 0 ); // MOVE THE GEOMOETRY UP BY HALF SO PIVOT IS AT THE BOTTOM OF THE GEO
mesh2.rotation.set(0, 0, Math.PI / 2);
mesh.add(mesh2);
pivot = new THREE.Group();
pivot.position.set( 0.0, 0.25, 0 ); // MOVE THE PIVOT BACK TO WORLD ORIGN
mesh.add( pivot ); // THIS ADDS THE PIVOT TO THE CENTRE OF THE GEOMOETRY, WHICH WAS THEN ADDING MESH2 IN THE WRONG PLACE
pivot.add( mesh2 );
// VISUALISE PIVOT
var pivotSphereGeo = new THREE.SphereGeometry( 0.01 );
var pivotSphere = new THREE.Mesh(pivotSphereGeo);
pivotSphere.position.set( 0,0,0 );
pivotSphere.position.z = 0.1;
scene.add( pivotSphere );
scene.add( new THREE.AxesHelper() );
renderer = new THREE.WebGLRenderer( { antialias: true } );
renderer.setSize( window.innerWidth, window.innerHeight );
document.body.appendChild( renderer.domElement );
}
function animate() {
requestAnimationFrame( animate );
pivot.rotation.z += 0.01;
renderer.render( scene, camera );
}
And so far I managed to do the same with my code. The only thing that I'm struggling with now is how to remove the stationary rectangle?
I would appreciate any help you can give me.
The example uses the mesh plane to offset the position of the second mesh. You can always use a group for these purposes if you don't want to render anything.
So, replace the line:
mesh = new THREE.Mesh( geometry, material );
with:
mesh = new THREE.Group();
And rename the variable accordingly to not have a confusing code.
There are two plane meshes in the code above. The first one is defined by:
mesh = new THREE.Mesh( geometry, material );
mesh.position.set(0, -.25, 0);
scene.add( mesh );
The second one (the "door") is defined by:
var mesh2 = new THREE.Mesh( geo2, material );
mesh2.position.set( 0, .25, 0 ); // MOVE THE GEOMOETRY UP BY HALF SO PIVOT IS AT THE BOTTOM OF THE GEO
mesh2.rotation.set(0, 0, Math.PI / 2);
mesh.add(mesh2);
Removing the code that defines the first one will remove the non-door rectangle. But you can't simply remove that code can call it a day, because notice the last line of the mesh2 definition:
mesh.add(mesh2);
This means mesh2 was added as a child of mesh. The pivot group is also a child of mesh. In both cases, replace mesh with scene, and you should be good to go.
I am trying to animate a cube along a path in three.js.
CODE
// Ellipse class, which extends the virtual base class Curve
var curve = new THREE.EllipseCurve(
0, 0, // ax, aY
16, 21.28, // xRadius, yRadius
0, 2 * Math.PI, // aStartAngle, aEndAngle
false, // aClockwise
0 // aRotation
);
//defines the amount of points the path will have
var path = new THREE.Path( curve.getPoints( 100 ) );
var geometrycirc = path.createPointsGeometry( 100 );
var materialcirc = new THREE.LineBasicMaterial( {
color : 0xff0000
} );
// Create the final object to add to the scene
var ellipse = new THREE.Line( geometrycirc, materialcirc );
ellipse.position.set(0,1,0);
this.scene.add( ellipse );
// add the box to the scene
this.scene.add(this.box);
I have being doing some research into how this could be done and came across this fiddle animate on path This method uses a the THREE.SplineCurve3 method to create the points for the box to use.
My question is do I need to convert my path to use the THREE.SplineCurve3 method.
Or can I use the path as it is?
Any help or pointers would be appreciated.
many thanks
Object Animating on path
Code
// GLOBALS - ALLOCATE THESE OUTSIDE OF THE RENDER LOOP - CHANGED
var cubes = [], marker, spline;
var matrix = new THREE.Matrix4();
var up = new THREE.Vector3( 0, 1, 0 );
var axis = new THREE.Vector3( );
var pt, radians, axis, tangent, path;
// the getPoint starting variable - !important - You get me ;)
var t = 0;
//This function generates the cube and chooses a random color for it
//on initial load.
function getCube(){
// cube mats and cube
var mats = [];
for (var i = 0; i < 6; i ++) {
mats.push(new
THREE.MeshBasicMaterial({color:Math.random()*0xffffff}));
}
var cube = new THREE.Mesh(
new THREE.CubeGeometry(2, 2, 2),
new THREE.MeshFaceMaterial( mats )
);
return cube
}
// Ellipse class, which extends the virtual base class Curve
function Ellipse( xRadius, yRadius ) {
THREE.Curve.call( this );
// add radius as a property
this.xRadius = xRadius;
this.yRadius = yRadius;
}
Ellipse.prototype = Object.create( THREE.Curve.prototype );
Ellipse.prototype.constructor = Ellipse;
// define the getPoint function for the subClass
Ellipse.prototype.getPoint = function ( t ) {
var radians = 2 * Math.PI * t;
return new THREE.Vector3( this.xRadius * Math.cos( radians ),
this.yRadius * Math.sin( radians ),
0 );
};
//
var mesh, renderer, scene, camera, controls;
function init() {
// renderer
renderer = new THREE.WebGLRenderer();
renderer.setSize( window.innerWidth, window.innerHeight );
document.body.appendChild( renderer.domElement );
// scene
scene = new THREE.Scene();
// camera
camera = new THREE.PerspectiveCamera( 45, window.innerWidth / window.innerHeight, 1, 1000 );
camera.position.set( 20, 20, 20 );
// controls
controls = new THREE.OrbitControls( camera, renderer.domElement );
controls.addEventListener( 'change', render ); // use if there is no animation loop
controls.minDistance = 10;
controls.maxDistance = 50;
// light
var light = new THREE.PointLight( 0xffffff, 0.7 );
camera.add( light );
scene.add( camera ); // add to scene only because the camera has a child
// axes
scene.add( new THREE.AxisHelper( 20 ) );
////////////////////////////////////////
// Create the cube //
////////////////////////////////////////
marker = getCube();
marker.position.set(0,0,0);
scene.add(marker);
////////////////////////////////////////
// Create an Extruded shape //
////////////////////////////////////////
// path
path = new Ellipse( 5, 10 );
// params
var pathSegments = 64;
var tubeRadius = 0.5;
var radiusSegments = 16;
var closed = true;
var geometry = new THREE.TubeBufferGeometry( path, pathSegments, tubeRadius, radiusSegments, closed );
// material
var material = new THREE.MeshPhongMaterial( {
color: 0x0080ff,
} );
// mesh
mesh = new THREE.Mesh( geometry, material );
scene.add( mesh );
//////////////////////////////////////////////////////////////////////////
// Create the path which is based on our shape above //
//////////////////////////////////////////////////////////////////////////
//Please note that this red ellipse was only created has a guide so that I could be certain that the square is true to the tangent and positioning.
// Ellipse class, which extends the virtual base class Curve
var curve = new THREE.EllipseCurve(
0, 0, // ax, aY
6, 11, // xRadius, yRadius
0, 2 * Math.PI, // aStartAngle, aEndAngle
false, // aClockwise
0 // aRotation
);
//defines the amount of points the path will have
var path2 = new THREE.Path( curve.getPoints( 100 ) );
geometrycirc = path2.createPointsGeometry( 100 );
var materialcirc = new THREE.LineBasicMaterial( {
color : 0xff0000
} );
// Create the final object to add to the scene
var ellipse = new THREE.Line( geometrycirc, materialcirc );
ellipse.position.set(0,0,0);
scene.add( ellipse );
}
function animate() {
requestAnimationFrame(animate);
render();
}
function render() {
// set the marker position
pt = path.getPoint( t );
// set the marker position
marker.position.set( pt.x, pt.y, pt.z );
// get the tangent to the curve
tangent = path.getTangent( t ).normalize();
// calculate the axis to rotate around
axis.crossVectors( up, tangent ).normalize();
// calcluate the angle between the up vector and the tangent
radians = Math.acos( up.dot( tangent ) );
// set the quaternion
marker.quaternion.setFromAxisAngle( axis, radians );
t = (t >= 1) ? 0 : t += 0.002;
renderer.render( scene, camera );
}
init();
animate();
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/82/three.min.js"></script>
<script src="https://threejs.org/examples/js/controls/OrbitControls.js"></script>
Conclusion
So I was very Fortunate to stumble upon the answer.
In my case it was the creation of a subclass to my object which allowed me to use it's data as points so that an object could use it as a guide.
Yes I am aware that you are thinking 'What is this guy talking about' so I have created a fiddle for you to look at and study.
Fiddle: Object Animating on path
I'm trying to UV map a cube-map texture onto a sphere. I have tried to Map a cube-map onto a cube and it was pretty easy. I had this image which was mapped onto the cube as follows:
Click here to open image
This is the final output (with a different image, of same type)[Click here to open the Output][2]
This is how I carried out the UV mapping for the aforementioned task:
var geometry = new THREE.CubeGeometry( 10, 10, 10);
var material = new THREE.MeshPhongMaterial( { map:THREE.ImageUtils.loadTexture('images/texture-atlas.jpg') } );
There are few things that I found in OpenGL, but almost nothing for Three.js
If you can come up with anything that'll help me in executing this, it'll be a great help. If possible, please show me how to map one image onto corresponding part of the sphere and I'll do it for the rest of them.
A SphereGeometry does not have vertices in the correct locations to achieve the mapping you want. However, you can easily create a suitable geometry by morphing BoxGeometry into a sphere.
// geometry
var geometry = new THREE.BoxGeometry( 10, 10, 10, 8, 8, 8 );
// morph box into a sphere
for ( var i = 0; i < geometry.vertices.length; i ++ ) {
geometry.vertices[ i ].normalize().multiplyScalar( 10 ); // or whatever size you want
}
// texture is a collage; set offset/repeat per material index
var repeat = new THREE.Vector2( 1/3, 1/2 );
var offsets = [
new THREE.Vector2( 0, 0 ),
new THREE.Vector2( 0, 1/2 ),
new THREE.Vector2( 1/3, 0 ),
new THREE.Vector2( 1/3, 1/2 ),
new THREE.Vector2( 2/3, 0 ),
new THREE.Vector2( 2/3, 1/2 )
];
// redefine vertex normals consistent with a sphere; reset UVs
for ( var i = 0; i < geometry.faces.length; i ++ ) {
var face = geometry.faces[ i ];
face.vertexNormals[ 0 ].copy( geometry.vertices[ face.a ] ).normalize();
face.vertexNormals[ 1 ].copy( geometry.vertices[ face.b ] ).normalize();
face.vertexNormals[ 2 ].copy( geometry.vertices[ face.c ] ).normalize();
var uvs = geometry.faceVertexUvs[ 0 ];
for ( var j = 0; j < 3; j ++ ) {
uvs[ i ][ j ].multiply( repeat ).add( offsets[ face.materialIndex ] );
}
// face.normal - will not be used; don't worry about it
}
var loader = new THREE.TextureLoader();
var texture = loader.load( 'texture.jpg' );
// mesh
var mesh = new THREE.Mesh( geometry, new THREE.MeshPhongMaterial( { map: texture } ) );
scene.add( mesh );
three.js r.77
I'm working on a Web Service which main funcionality resolves around displaying binary .ply files. To open and visualize the point cloud I'm using PLYLoader from Three.js. I used it to present example files from the internet and it worked. But when I'm trying to attach my .ply file it doesn't. I'm using Three.js because I need my display to be optimal, my files are at least 70mb.
Example .ply file here:
https://onedrive.live.com/redir?resid=7DDB287362EDFD5A!761&authkey=!AOHxkZk-6etH--0&ithint=file%2cply
I noticed that the .ply files that i have are a bit different from .ply files i displayed before. In my files there are no faces only points.
So basically my question is "Is it possible to display point cloud using Three.js? or should I use other library? If this is possible can someone help me with configuring my code to work properly?
Here it is my script in which I'm supporting .ply files:
var cameraControls;
var container = document.getElementById("three");
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera( 75, container.offsetWidth / container.offsetHeight, 0.1, 1000 );
var renderer = new THREE.WebGLRenderer();
renderer.setSize( container.offsetWidth, container.offsetHeight );
document.getElementById("three").appendChild( renderer.domElement );
cameraControls = new THREE.OrbitControls( camera, renderer.domElement );
cameraControls.target.set( 0, 0, 0 );
cameraControls.addEventListener( 'change', render );
var geometry = new THREE.BoxGeometry( 3, 3, 3 );
var material = new THREE.MeshPhongMaterial( { color: 0x00ff00 } );
var cube = new THREE.Mesh( geometry, material );
var loader = new THREE.PLYLoader();
loader.load( 'resources/cube.ply', function ( geometry ) {
geometry.computeFaceNormals();
var material = new THREE.MeshStandardMaterial( { color: 0x0055ff } );
var mesh = new THREE.Mesh( geometry, material );
mesh.position.y = - 0.25;
mesh.rotation.x = - Math.PI / 2;
mesh.scale.multiplyScalar( 1 );
mesh.castShadow = true;
mesh.receiveShadow = true;
scene.add( mesh );
} );
var directionalLight = new THREE.DirectionalLight( 0xffffff, 0.5 );
var light = new THREE.AmbientLight( 0x404040 ); // soft white light
scene.add( light );
directionalLight.position.set( 0, 1, 0 );
scene.add( directionalLight );
camera.position.z = 5;
function render() {
requestAnimationFrame( render );
renderer.render( scene, camera );
}
render();
Use THREE.MeshNormalMaterial. It loads the textures with MeshNormalMaterial for some odd reason
Use PointMaterials and Points
const loader = new PLYLoader();
loader.load('00000012.ply', function (geometry) {
var material = new THREE.PointsMaterial( { size: 0.005 } );
material.vertexColors = true //if has colors
var mesh = new THREE.Points(geometry, material)
scene.add(mesh)
} );
Im trying to make a three js document which shows lots of spherical objects, the quickest way to do this is by using buffergeometry. From this post here I learned that I could convert normal geometry into buffergeometry using:
var sphere = new THREE.SphereGeometry( 4, 0.05, 0.025 );
var geometry = THREE.BufferGeometryUtils.fromGeometry( sphere );
But this does not seem to work for me, the rest of the code that creates the object reads:
var positions = new Float32Array( x_GAMA.length * 3 );
for ( var i = 0; i < x_GAMA.length; i += 1 ) {
// positions
positions[ 3*i ] = x_GAMA[i]*10000;
positions[ 3*i + 1 ] = y_GAMA[i]*10000;
positions[ 3*i + 2 ] = z_GAMA[i]*10000;
}
geometry.addAttribute( 'position', new THREE.BufferAttribute( positions, 3 ) );
var material = new THREE.PointCloudMaterial( {size:1,color:0x999999} );
geometry.computeBoundingSphere();
particleSystem = new THREE.PointCloud( geometry, material );
scene.add( particleSystem );
It works fine if I use var geometry = new THREE.BufferGeometry(); but this creates squares which I do not want. Anyone have any idea why this does not seem to work? Thanks in advance.
In Three.js r71 you can create Sphere Buffer Geometry like this:
var sphereGeometry = new THREE.SphereGeometry( 4, 3, 2 );
var bufferSphereGeometry = new THREE.BufferGeometry().fromGeometry( sphereGeometry );
In r72 dev you can do ít straightforward like this:
// constructor: radius, widthSegments, heightSegments, phiStart, phiLength, thetaStart, thetaLength
var sphereGeo = new THREE.SphereBufferGeometry( 4, 3, 2 ); //r72