three.js draw multiple faces and performance - three.js

I would like draw multiple faces (e.g. 100000x) with beast performance. I am trying to use "for" cycle, but there is a lot scene.children.
lastz=0;nextz=0;
for (i1=0; i1<100000; i1++) {
var geometry = new THREE.Geometry();
var material = defaultshellmaterial.clone();
nextz=Math.random();
geometry.vertices.push(new THREE.Vector3(0+i1,0,lastz));
geometry.vertices.push(new THREE.Vector3(1+i1,0,nextz));
geometry.vertices.push(new THREE.Vector3(1+i1,1,nextz));
geometry.vertices.push(new THREE.Vector3(0+i1,1,lastz));
geometry.faces.push(new THREE.Face3(0, 1, 2));
geometry.faces.push(new THREE.Face3(0, 3, 2));
geometry.computeFaceNormals();
lastz=nextz;
var mesh= new THREE.Mesh( geometry, material);
scene.add(mesh);
}
Is this the beast way? Exist the possibility draw the mesh as one scene child (like method which i am using for multiple lines or points)? And will be then posible select each face by mouse? My basic idea below does not work...
lastz=0;nextz=0;
var geometry = new THREE.Geometry();
var material = defaultshellmaterial.clone();
var mesh= new THREE.Mesh( geometry, material);
for (i1=0; i1<10000; i1++) {
nextz=Math.random();
geometry.vertices.push(new THREE.Vector3(0+i1,0,lastz));
geometry.vertices.push(new THREE.Vector3(1+i1,0,nextz));
geometry.vertices.push(new THREE.Vector3(1+i1,1,nextz));
geometry.vertices.push(new THREE.Vector3(0+i1,1,lastz));
geometry.faces.push(new THREE.Face3(0, 1, 2));
geometry.faces.push(new THREE.Face3(0, 3, 2));
geometry.computeFaceNormals();
lastz=nextz;
}
scene.add(mesh);

Thank you all for your help and inspiration! I found also one similar example on the three.js sites https://threejs.org/examples/#webgl_buffergeometry_uint The trick is in usage the buffergeometry which must use the float32array. Only problem is than the float32array has not the changable length and is necessary to create standard arrays for vertices, normals and colors and than tranform then it to the float32array. The longer but more powerfull solution how to draw a lot triangles could be:
var geometry = new THREE.BufferGeometry(); // buffered geometry
var positions = []; // standard arrays which allow push next number on the end (changeble length)
var normals = [];
var colors = [];
var pA = new THREE.Vector3(); //auxiliary vectors
var pB = new THREE.Vector3();
var pC = new THREE.Vector3();
var cb = new THREE.Vector3();
var ab = new THREE.Vector3();
var color = new THREE.Color();
for (i=0; i<1000000; i++) {
add=i*10;
ax=0+add;ay=0;az=0;
bx=10+add;by=0;bz=0;
cx=0+add;cy=10;cz=0;
positions.push( ax, ay, az ); // push new vertices
positions.push( bx, by, bz );
positions.push( cx, cy, cz );
// flat face normals (just compute normals with auxiliary vectors)
pA.set( ax, ay, az );
pB.set( bx, by, bz );
pC.set( cx, cy, cz );
cb.subVectors( pC, pB );
ab.subVectors( pA, pB );
cb.cross( ab );
cb.normalize();
var nx = cb.x;
var ny = cb.y;
var nz = cb.z;
normals.push( nx, ny, nz ); // push normals
normals.push( nx, ny, nz );
normals.push( nx, ny, nz );
// set random HSL color for each triangle
color.setHSL(Math.random(), 1, 0.5); // barvy duhy?
colors.push( color.r, color.g, color.b );
colors.push( color.r, color.g, color.b );
colors.push( color.r, color.g, color.b );
}
function disposeArray() { this.array = null; }// auxiliary function set inital null array
geometry.addAttribute( 'position', new THREE.Float32BufferAttribute( positions, 3 ).onUpload( disposeArray ) ); // transfor standard array which allows the ".push" to the Float32array suitable for 3D graphics
geometry.addAttribute( 'normal', new THREE.Float32BufferAttribute( normals, 3 ).onUpload( disposeArray ) );
geometry.addAttribute( 'color', new THREE.Float32BufferAttribute( colors, 3 ).onUpload( disposeArray ) );
geometry.computeBoundingSphere(); // sometimes is usseful to define new boundary for new entities
var material = new THREE.MeshPhongMaterial( {
color: 0xaaaaaa, specular: 0xffffff, shininess: 250,
side: THREE.DoubleSide, vertexColors: THREE.VertexColors
} );
mesh = new THREE.Mesh( geometry, material ); // all triangles will be in the one mesh what is powerfull
scene.add( mesh );

Related

Get world position of plane

I'm very new to three.js so I'm sure I'm miss-understanding something here.
I've created a plane in the following way:
var planeGeom = new THREE.PlaneGeometry(0.2, 0.2);
planeGeom.rotateX(-Math.PI / 2);
var plane = new THREE.Mesh(planeGeom, new THREE.MeshBasicMaterial({color: 0xffff00, side: THREE.DoubleSide}));
plane.position.set(0, 0.1, 0);
scene.add(plane);
var mathPlane = new THREE.Plane();
planePointA.copy(plane.geometry.vertices[plane.geometry.faces[0].a]);
planePointB.copy(plane.geometry.vertices[plane.geometry.faces[0].b]);
planePointC.copy(plane.geometry.vertices[plane.geometry.faces[0].c]);
plane.localToWorld(planePointA);
plane.localToWorld(planePointB);
plane.localToWorld(planePointC);
mathPlane.setFromCoplanarPoints(planePointA, planePointB, planePointC);
var helper = new THREE.PlaneHelper( mathPlane, 1, 0xffff00 );
scene.add( helper );
Why is my PlaneGeometry object and Plane positioned differently? Why doesn't .localToWorld() get the world position of the plane?
https://jsfiddle.net/sek0yzLp/
Use .updateMatrixWorld() on the plane after setting its position:
plane.position.set(0, 0.1, 0);
plane.updateMatrixWorld();
scene.add(plane);

Ray does not accurately determine the intersection

I want to find point of intersection curve and line. Created raycast, but it doesn't work well. The point of the ray is far from the actual intersection.
Webgl 1, threejs 0.109
var sartPoint = new THREE.Vector3( -30, -50, 0 );
var endPoint = new THREE.Vector3( 50, 80, 0 );
var geometry = new THREE.Geometry();
geometry.vertices.push(sartPoint);
geometry.vertices.push(endPoint);
var materialTmp = new THREE.LineBasicMaterial( { color: 0xffffff, linewidth: 5 } );
var itemTmp = new THREE.Line( geometry, materialTmp );
_this.add( itemTmp, 'lines' );
scene.updateMatrixWorld()
var curve = new THREE.EllipseCurve(
0, 0, // ax, aY
10, 10, // xRadius, yRadius
0, 2 * Math.PI, // aStartAngle, aEndAngle
false, // aClockwise
0 // aRotation
);
var points = curve.getPoints( 10 );
var geometry = new THREE.BufferGeometry().setFromPoints( points );
var material = new THREE.LineBasicMaterial( { color : 0xff00ff } );
var ellipse = new THREE.Line( geometry, material );
scene.add( ellipse );
var raycaster = new THREE.Raycaster(sartPoint, endPoint.clone().normalize());
var intersects = raycaster.intersectObject( ellipse );
console.log(intersects);
if(intersects.length > 0){
// FIRST dot of intersect
var dotGeometry2 = new THREE.Geometry();
dotGeometry2.vertices.push(intersects[0].point);
var dotMaterial2 = new THREE.PointsMaterial( { size: 5, color: 0x00ff00 } );
var dot2 = new THREE.Points( dotGeometry2, dotMaterial2 );
_this.add( dot2, 'points' );
}
The second argument to the Raycaster constructor is a direction vector. Instead of:
endPoint.clone().normalize()
I think you want:
endPoint.clone().sub(startPoint).normalize()
It is work if
curve.getPoints( 10 );
When
curve.getPoints( 100 );
That doesn't work.

Default Object.rotation's axis of rotation (Three.js)

Does changing the value of an object's rotation property rotate the object about the world axes or the object's axes? For eg
object.rotation.x += 2
Will this rotate the object about the world X axis or the object's X axis?
I tried the following example :
var materials = [];
materials.push( [ new THREE.MeshBasicMaterial( { color: 0xff0000 } ) ] );
materials.push( [ new THREE.MeshBasicMaterial( { color: 0xff0000 } ) ] );
materials.push( [ new THREE.MeshBasicMaterial( { color: 0x00ff00 } ) ] );
materials.push( [ new THREE.MeshBasicMaterial( { color: 0x00ff00 } ) ] );
materials.push( [ new THREE.MeshBasicMaterial( { color: 0x0000ff } ) ] );
materials.push( [ new THREE.MeshBasicMaterial( { color: 0x0000ff } ) ] );
object = new THREE.Mesh( new THREE.CubeGeometry( 20, 20, 20, 1, 1, 1, materials ), new THREE.MeshFaceMaterial() );
object.position.x = 0;
object.position.y = 0;
object.position.z = 0;
object.rotation.x = 0;
object.rotation.y = 0;
object.rotation.z = 0;
object.scale.x = 1;
object.scale.y = 1;
object.scale.z = 1;
scene.add( object );
After i have added the object to the scene i tried the following -
object.rotation.x += Math.PI/4;
object.rotation.y += Math.PI/4;
object.rotation.z += Math.PI/4;
Until now the object rotates with respect to its own X,Y and Z axes.
but doing:
object.rotation.x += Math.Pi/4;
now rotates the object 45 degrees about the World X axis instead of the object's X axis.
The rotations are in local space. However, they are evaluated in a specific order which you can set yourself. The object.rotation property is an Euler, and as you can read in the description of the order-property:
The order in which to apply rotations. Default is 'XYZ', which means
that the object will first be rotated around its X axis, then its Y
axis and finally its Z axis. Other possibilities are: 'YZX', 'ZXY',
'XZY', 'YXZ' and 'ZYX'. These must be in upper case.
Three.js uses intrinsic Tait-Bryan angles. This means that rotations
are performed with respect to the local coordinate system. That is,
for order 'XYZ', the rotation is first around the local-X axis (which
is the same as the world-X axis), then around local-Y (which may now
be different from the world Y-axis), then local-Z (which may be
different from the world Z-axis).
(Emphasis added by me).
Consider the following example, every cube is updated with the same y-axis rotation. In every case the rotation is happening around the y-axis in local space, but:
The first cube has not been rotated in any other axis, making its
local space axis the same as the world space axis.
The second cube has been rotated around its x-axis, which means that
the y-axis is also rotated.
The third cube has also been rotated, but the order of rotation is
changed so that the y-axis rotation happens first, while it is still the same as the world y-axis, and the rotation around x happens afterward.
const canvas = document.getElementById("canvas");
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(10, 2, 20, 40);
const renderer = new THREE.WebGLRenderer({ canvas });
const light = new THREE.PointLight();
light.position.set(0, 5, 10)
scene.add(light);
camera.position.set(0, 0, 25);
const material = new THREE.MeshLambertMaterial();
const geometry = new THREE.BoxBufferGeometry( 1, 1, 1 );
const cube1 = new THREE.Mesh(geometry, material);
const cube2 = new THREE.Mesh(geometry, material);
const cube3 = new THREE.Mesh(geometry, material);
cube1.position.set(-2, 0, 0);
cube2.position.set( 0, 0, 0);
cube3.position.set( 2, 0, 0);
scene.add(cube1);
scene.add(cube2);
scene.add(cube3);
//The first cube is not rotated
cube1.rotation.set(0, 0, 0);
//The second cube is rotated pi/4 rad around X
cube2.rotation.set(Math.PI/4, 0, 0);
//The third cube is rotated pi/4 rad around X
//AND the rotation order is set to YXZ to make the y-rotation happen first
cube3.rotation.set(Math.PI/4, 0, 0);
cube3.rotation.order = "YXZ";
let t0 = performance.now();
function update(){
const t1 = performance.now();
const rotation = 0.001 * (t1 - t0);
cube1.rotation.y += rotation;
cube2.rotation.y += rotation;
cube3.rotation.y += rotation;
renderer.render(scene, camera);
t0 = t1;
requestAnimationFrame(update);
}
update();
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/92/three.js"></script>
<canvas id="canvas" width="400" height="200"></canvas>

Three.js marker on mesh

I'm working on a visualization that uses a height map to create a 3d terrain, and want to add some markers for locations of cities on the map. I'm guessing some sort of raycaster and intersectObject might do it, but I'm lost as to how to place a marker directly on the surface of the terrain. For instance, I know that a city is at (640, 480) in the pixel units of my texture, so I'm doing something like:
// This is the terrain, loaded from a PNG of the digital elevation model
var planeGeo = new THREE.PlaneGeometry( bumpTexture.image.width, bumpTexture.image.height, 600, 450 );
var plane = new THREE.Mesh( planeGeo, customMaterial );
plane.rotation.x = -Math.PI / 2;
plane.position.y = 0;
scene.add( plane );
// At (640, 480) of the image, there should be a marker directly on top of the mesh.
var cube = new THREE.Mesh( new THREE.CubeGeometry( 20, 20, 20 ), new THREE.MeshNormalMaterial() );
cube.position.x = 640;
cube.position.y = 0;
cube.position.z = 480;
scene.add(cube);
raycaster.setFromCamera(cube, camera );
var intersects = raycaster.intersectObject(plane);
console.log(raycaster, intersects);
// no intersects???
Any suggestions, Threejs wizards?

Concentric circles texture on RingGeometry

I am trying to create a flat ring in three.js with a concentric circles texture, like Saturn's rings. I cannot manage to do anything but lines that radiate from the center (like a bicycle wheel), no matter what I put in the image. It seems that textures are applied to RingGeometry in a very different fashion than CircleGeometry.
I could easily apply a concentric circles texture to a CircleGeometry, but a ring (with a hole in the middle) is really what I need. Is anybody aware of a way to have textures on rings do something else than radiate?
I did not find a way in Three.js documentation, nor on the web, to do what I want, as it seems that rings are seldom used by anybody...
Thank you
Go here http://jsfiddle.net/theo/VsWb9/ and replace
geometry = new THREE.CubeGeometry(200, 200, 200);
material = new THREE.MeshNormalMaterial();
mesh = new THREE.Mesh(geometry, material);
with
geometry = new THREE.TorusGeometry( 100, .5 , 50 ,50);
material = new THREE.MeshNormalMaterial();
mesh = new THREE.Mesh( geometry, material );
If you want to change the ring color to say black for instance
change
material = new THREE.MeshNormalMaterial();
to
material = new THREE.MeshBasicMaterial({color:0x000});
Stick any other material changes in that array input argument to the constructor
function THREE.MeshBasicMaterial({arguments here})
I found this for making the geometry. It creates a disk of theataSeegmens triangles
this.RingGeometry = function ( innerRadius, outerRadius, thetaSegments) {
THREE.Geometry.call( this )
innerRadius = innerRadius || 0
outerRadius = outerRadius || 50
thetaSegments = thetaSegments || 8
innerRadius*=Obj.Size*100;
outerRadius*=Obj.Size*100;
var normal = new THREE.Vector3( 0, 0, 1 )
for(var i = 0; i < thetaSegments; i++ ){
var angleLo = (i / thetaSegments) *Math.PI*2
var angleHi = ((i+1) / thetaSegments) *Math.PI*2
var vertex1 = new THREE.Vector3(innerRadius * Math.cos(angleLo), innerRadius * Math.sin(angleLo), 0);
var vertex2 = new THREE.Vector3(outerRadius * Math.cos(angleLo), outerRadius * Math.sin(angleLo), 0);
var vertex3 = new THREE.Vector3(innerRadius * Math.cos(angleHi), innerRadius * Math.sin(angleHi), 0);
var vertex4 = new THREE.Vector3(outerRadius * Math.cos(angleHi), outerRadius * Math.sin(angleHi), 0);
this.vertices.push( vertex1 );
this.vertices.push( vertex2 );
this.vertices.push( vertex3 );
this.vertices.push( vertex4 );
var vertexIdx = i * 4;
// Create the first triangle
var face = new THREE.Face3(vertexIdx + 0, vertexIdx + 1, vertexIdx + 2, normal);
var uvs = []
var uv = new THREE.Vector2(0, 0)
uvs.push(uv)
var uv = new THREE.Vector2(1, 0)
uvs.push(uv)
var uv = new THREE.Vector2(0, 1)
uvs.push(uv)
this.faces.push(face);
this.faceVertexUvs[0].push(uvs);
// Create the second triangle
var face = new THREE.Face3(vertexIdx + 2, vertexIdx + 1, vertexIdx + 3, normal);
var uvs = []
var uv = new THREE.Vector2(0, 1)
uvs.push(uv)
var uv = new THREE.Vector2(1, 0)
uvs.push(uv)
var uv = new THREE.Vector2(1, 1)
uvs.push(uv)
this.faces.push(face);
this.faceVertexUvs[0].push(uvs);
}
//this.computeCentroids();
//this.computeFaceNormals();
this.boundingSphere = new THREE.Sphere( new THREE.Vector3(), outerRadius );
};
this.RingGeometry.prototype = Object.create( THREE.Geometry.prototype );
The above line is important to get it to work.
Here is a sugestion how to set the ring material.assuming that you have two pictures (just a sqare section) that can be used for alphamap and for
the actual ring.
var ringMaterial = new THREE.MeshPhongMaterial(
{
map: SaturnRingColor,
alphaMap:SaturnRingPattern,
color: 0xffffff,
specular: 0x555555,
shininess: 3,
emissive:10,
side: THREE.DoubleSide,
castshadow:true,
transparent : true,
opacity : 0.9,
} );
this.ringMesh = new THREE.Mesh( this.RingGeometry , RingMaterial );

Resources