I have vertices(x,y,z coords) of a polygon as input. How can I render a polygon having these vertices in three.js?
There is this documentation.But it seems to involve bezier. I need simple straight edged polygon.
You can create a polygon from vertices with the following code:
var geom = new THREE.Geometry();
var v1 = new THREE.Vector3(0,0,0);
var v2 = new THREE.Vector3(0,500,0);
var v3 = new THREE.Vector3(0,500,500);
geom.vertices.push(v1);
geom.vertices.push(v2);
geom.vertices.push(v3);
geom.faces.push( new THREE.Face3( 0, 1, 2 ) );
geom.computeFaceNormals();
var object = new THREE.Mesh( geom, new THREE.MeshNormalMaterial() );
scene.add(object);
Copy and paste this code in and then change x, y, and z coordinates of v1, v2, and v3 (or however many vertices you need) to the coordinates of your vertices.
Essentially, you are creating vertices using THREE.Vector3 to supply the coordinates and then pushing them to the vertices property of an empty THREE.Geometry();
Code is from this answer
THREE.Geometry is removed, try the following method
let coordinates = [
{
x : 1,
y : 1,
z: 10
},
{
x : 2,
y : 1,
z: 10
},
{
x : 2,
y : 2,
z: 10
},
{
x : 1,
y : 2,
z: 10
}
]
let polyShape = new THREE.Shape(coordinates.map((coord) => new THREE.Vector2(coord.x, coord.y)))
const polyGeometry = new THREE.ShapeGeometry(polyShape);
polyGeometry.setAttribute("position", new THREE.Float32BufferAttribute(coordinates.map(coord => [coord.x, coord.y, coord.z]).flat(), 3))
let polygon = new THREE.Mesh(polyGeometry, new THREE.MeshBasicMaterial({ color: "colorYOuWant", side: THREE.DoubleSide}))
scene.add(polygon);
Related
I'm trying to create a zoom box, so far I managed to translate the cursor positions from locale to world coordinates and create a box object around the cursor with the right uvs.
Here is the fiddle of my attempt : https://jsfiddle.net/2ynfedvk/2/
Without scaling the box is perfectly centered around the cursor, but if you toggle the scaling checkbox to set the scale zoomMesh.scale.set(1.5, 1.5, 1), the box position shift the further you move the cursor from the scene center.
Am I messing any CSS like "transform origin" for three.js to center the scale around the object, is this the right approach the get this kind of zoom effect ?
I'm new to three.js and 3d in general, so thanks for any help.
When you scale your mesh with 1.5, it means that apply transform matrix that scales values of vertices.
The issue comes from changing of vertices. Vertices are in local space of the mesh. And when you set the left-top vertex of the square, for example, to [10, 10, 0] and then apply .scale.set(1.5, 1.5, 1) to the mesh, then the coordinate of vertex became [15, 15, 0]. The same to all the other 3 vertices. And that's why the center of the square does not match at 1.5 times from the center of the picture to mouse pointer.
So, an option is not to scale a mesh, but change the size of the square.
I changed your fiddle a bit, so maybe it will be more explanatory:
const
[width, height] = [500, 300],
canvas = document.querySelector('canvas'),
scaleCheckBox = document.querySelector('input')
;
console.log(scaleCheckBox)
canvas.width = width;
canvas.height = height;
const
scene = new THREE.Scene(),
renderer = new THREE.WebGLRenderer({canvas}),
camDistance = 5,
camFov = (2 * Math.atan( height / ( 2 * camDistance ) ) * ( 180 / Math.PI )),
camera = new THREE.PerspectiveCamera(camFov, width/height, 0.1, 1000 )
;
camera.position.z = camDistance;
const
texture = new THREE.TextureLoader().load( "https://picsum.photos/500/300" ),
imageMaterial = new THREE.MeshBasicMaterial( { map: texture , side : 0 } )
;
texture.minFilter = THREE.LinearFilter;
texture.magFilter = THREE.LinearFilter;
texture.format = THREE.RGBFormat;
const
planeGeometry = new THREE.PlaneGeometry( width, height ),
planeMesh = new THREE.Mesh( planeGeometry, imageMaterial )
;
const
zoomGeometry = new THREE.BufferGeometry(),
zoomMaterial = new THREE.MeshBasicMaterial( { map: texture , side : 0 } ),
zoomMesh = new THREE.Mesh( zoomGeometry, zoomMaterial )
;
zoomMaterial.color.set(0xff0000);
zoomGeometry.setAttribute('position', new THREE.BufferAttribute(new Float32Array([
0, 0, 0,
0, 0, 0,
0, 0, 0,
0, 0, 0
]), 3));
zoomGeometry.setIndex([
0, 1, 2,
2, 1, 3
]);
scene.add( planeMesh );
scene.add( zoomMesh );
var zoom = 1.;
function setZoomBox(e){
const
size = 50 * zoom,
x = e.clientX - (size/2),
y = -(e.clientY - height) - (size/2),
coords = [
x,
y,
x + size,
y + size
]
;
const [x1, y1, x2, y2] = [
coords[0] - (width/2),
coords[1] - (height/2),
coords[2] - (width/2),
coords[3] - (height/2)
];
zoomGeometry.setAttribute('position', new THREE.BufferAttribute(new Float32Array([
x1, y1, 0,
x2, y1, 0,
x1, y2, 0,
x2, y2, 0
]), 3));
const [u1, v1, u2, v2] = [
coords[0]/width,
coords[1]/height,
coords[2]/width,
coords[3]/height
]
zoomGeometry.setAttribute('uv',
new THREE.BufferAttribute(new Float32Array([
u1, v1,
u2, v1,
u1, v2,
u2, v2,
u1, v1,
u1, v2
]), 2));
}
function setScale(e){
//zoomMesh.scale.set(...(scaleCheckBox.checked ? [1.5, 1.5, 1] : [1, 1, 1]));
zoom = scaleCheckBox.checked ? 1.5 : 1 ;
}
function render(){
renderer.render(scene, camera);
requestAnimationFrame(render);
}
render();
canvas.addEventListener('mousemove', setZoomBox);
scaleCheckBox.addEventListener('change', setScale);
html, body {
margin: 0;
height: 100%;
}
body{
background: #333;
color: #FFF;
font: bold 16px arial;
}
canvas{
}
<script src="https://threejs.org/build/three.min.js"></script>
<canvas></canvas>
<div>Toggle scale <input type="checkbox" /></div>
thanks for the answer, not quite what I was looking for (not only resize the square but also zoom in the image), but you pointed me in the right direction.
Like you said the positions coordinate are shifting with the scale, so I have to recalculate the new position relative to the scale.
Added these new lines, with new scale and offset variables :
if(scaleCheckBox.checked){
const offset = scale - 1;
zoomMesh.position.set(
-(x1 * offset) - (size*scale)/2) -(size/2),
-((y1 * offset) + (size*scale)/2) -(size/2)),
0
);
}
Here is the working fiddle : https://jsfiddle.net/dc9f5v0m/
It's a bit messy, with a lot of recalculation (Especially to center the cursor around the square), but it gets the job done and the zoom effect can be achieved with any shape not only a square.
Thanks again for your help.
I want to animate a Plane vertices to fill the screen. (Vertices as this is the effect I want, I'm hoping to animate each vertex with a short delay to then fill the screen)
As a proof of concept, I've got a vertex to animate off to a random point, using the function below -
tileClick() {
var geo = this.SELECTED.geometry;
var mat = this.SELECTED.material as THREE.MeshBasicMaterial;
TweenMax.TweenLite.to(geo.vertices[0], 0.3, {x: -5, y:5, onUpdate: () =>{
mat.needsUpdate = true;
geo.colorsNeedUpdate = true;
geo.elementsNeedUpdate = true;
}, ease: TweenMax.Elastic.easeOut.config(1, 0.5)});
}
However, now I need to work out the points of the current view of the camera. pseudo code: camera.view.getBoundingClientRect();
Plnkr of WIP - https://next.plnkr.co/edit/Jm4D2zgLtiKBGghC
I believe what you need is THREE.Vector3.unproject. With this method, you can set the vector to x, y, z in screen coordinates, and it'll return x, y, z in world coordinates:
var vector = new THREE.Vector3();
var zNearPlane = -1;
var zFarPlane = 1;
// Top left corner
vector.set( -1, 1, zNearPlane ).unproject( camera );
// Top right corner
vector.set( 1, 1, zNearPlane ).unproject( camera );
// Bottom left corner
vector.set( -1, -1, zNearPlane ).unproject( camera );
// Bottom right corner
vector.set( 1, -1, zNearPlane ).unproject( camera );
Notice that all inputs are in the [-1, 1] range:
x:-1 = left side of screen
x: 1 = right side of screen
y: 1 = top
y:-1 = bottom
z: 1 = far plane
z: -1 = near plane
I try to build molecule CH4 with threejs
But when I try to build 109.5 angle
methanum = function(x, y, z) {
molecule = new THREE.Object3D();
var startPosition = new THREE.Vector3( 0, 0, 0 );
molecule.add(atom(startPosition, "o"));
var secondPosition = new THREE.Vector3( -20, 10, 00 );
molecule.add(atom(secondPosition, "h"));
var angle = 109.5;
var matrix = new THREE.Matrix4().makeRotationAxis( new THREE.Vector3( 0, 1, 0 ), angle * ( Math.PI / 180 ));
var thirdPosition = secondPosition.applyMatrix4( matrix );
molecule.add(atom(thirdPosition, "h"));
var fourthPosition = thirdPosition.applyMatrix4( matrix );
molecule.add(atom(thirdPosition, "h"));
molecule.position.set(x, y, z);
molecule.rotation.set(x, y, z);
scene.add( molecule );
}
Demo: https://dl.dropboxusercontent.com/u/6204711/3d/ch4.html
But my atoms are not uniformly distributed as in the drawing
Some ideas?
Well there are 3 errors in your molecule code.
You place an oxygen as the center of the CH4 instead of a carbon
When you apply your fourth hydrogen, you specify the third position whereas you have created a fourthposition.
You are rotating around the wrong axis when you place your third hydrogen. My hints are the following: First of all , place your carbon, then move along the Z-axis, place your first hydrogen, rotate around the X-axis of 109.5°, place your second hydrogen, rotate around the Z-axis of 120° the position of your second hydrogen, place your third hydrogen and finally rotate once again around the Z-axis of 120° the position of your third hydrogen and place your last hydrogen.
Here is the CH4 I tried:
methanum3 = function(x, y, z) {
molecule = new THREE.Object3D();
var startPosition = new THREE.Vector3( 0, 0, 0 );
molecule.add(atom(startPosition, "c"));
var axis = new THREE.AxisHelper( 50 );
axis.position.set( 0, 0, 0 );
molecule.add( axis );
var secondPosition = new THREE.Vector3( 0, 0, -40 );
molecule.add(atom(secondPosition, "h"));
var angle = 109.5;
var matrixX = new THREE.Matrix4().makeRotationAxis( new THREE.Vector3( 1, 0, 0 ), angle * ( Math.PI / 180 ));
var thirdPosition = secondPosition.applyMatrix4( matrixX );
molecule.add(atom(thirdPosition, "h"));
var matrixZ = new THREE.Matrix4().makeRotationAxis( new THREE.Vector3( 0, 0, 1 ), 120 * ( Math.PI / 180 ));
var fourthPosition = thirdPosition.applyMatrix4( matrixZ );
molecule.add(atom(fourthPosition, "h"));
var fifthPosition = fourthPosition.applyMatrix4( matrixZ );
molecule.add(atom(fifthPosition, "h"));
molecule.position.set(x, y, z);
//molecule.rotation.set(x, y, z);
scene.add( molecule );
}
//water(0,0,0);
//water(30,60,0);
methanum3(-30,60,0);
Explanation:
Let's call H1 an hydrogen and H2 another one. The given angle of 109.5° is defined in the :
---> --->
(CH1,CH2) plane. Therefore when you look in the direction of the normal of that plane, you can see the 109.5° (Cf. the right part of the image below) BUT When you look in the direction of the normal of another plane you'll get the projection of that angle on that plane. In your case when you look in the direction of the Z-axis you can see an angle of 120°.(Cf. left part of the image below).
The two angles are different according to the direction of the camera.
Hope this helps.
I have a cube of size 1 x 1 x 2. On the larger (1 x 2) face, I would like to show one color on half of the face, and another color on the other half. What would the recommended way of implementing this? Should I use hierarchy to build this 1 x 1 x 2 cube using two 1 x 1 x 1 cubes of different face color?
Here is the pattern to follow. Adjust to your liking:
var geometry = new THREE.CubeGeometry( 10, 10, 20, 1, 1, 2 );
for ( var i = 0; i < geometry.faces.length; i ++ ) {
geometry.faces[ i ].color.setHSL( Math.random(), 0.5, 0.5 ); // pick your colors
}
var material = new THREE.MeshBasicMaterial( { vertexColors: THREE.FaceColors } );
var mesh = new THREE.Mesh( geometry, material );
If you are using CanvasRenderer, you can set material.overdraw = 0.5 to try to eliminate the diagonal lines. This is not required for WebGLRenderer.
three.js r.60
(new to stackoverflow, new to webgl/three.js, ...)
I'm using three.js r54 to plot a force-directed graph. the edges between the nodes are THREE.Lines, which is fine, but lines are not selectable with a raycaster. so my aim is to take cylinders instead(/along with) of lines(also because I can do some further stuff: using textures,...)
this is what I'm doing to place the cylinders:
// init reference vector
var upVec = new THREE.Vector3(0,1,0);
//---withhin a loop---
// get direction
var direction = startPoint.subSelf(endPoint).clone();
// half length for cylinder height
var halfLength = direction.length() * 0.5;
// get offset
var offset = endPoint.clone().addSelf(direction.clone().multiplyScalar(0.5));
// normalize direc
direction.normalize();
//newUpVec = upVec - (upVec *(dot) direction) * direction - projection of direction
var newUpVec = upVec.clone().subSelf(direction.clone().multiplyScalar(upVec.dot(direction.clone()))).normalize();
var right = newUpVec.clone().crossSelf(direction.clone());
//build rotation matrix
var rot = new THREE.Matrix4(right.x, right.y, right.z, 0,
newUpVec.x, newUpVec.y, newUpVec.z, 0,
direction.x, direction.y, direction.z,0,
0,0,0,1);
//build translation matrix
var transla = new THREE.Matrix4(1, 0, 0, offset.x,
0, 1, 0, offset.y,
0, 0, 1, offset.z,
0, 0, 0, 1);
//build transformation matrix
var transfo = new THREE.Matrix4().multiply(transla, rot);
// create geometry
var cylgeo = new THREE.CylinderGeometry(2, 2, halfLength * 2, 12, 1, false);
cylgeo.applyMatrix(transfo);
var cylMesh = new THREE.Mesh(cylgeo, new THREE.MeshLambertMaterial({color:0x000000,
wireframe: true, shading: THREE.FlatShading}));
(descripted in: http://www.fastgraph.com/makegames/3drotation/ )
So the cylinders are placed at the right offset and align in some kind of way, but not to the two points (start, end) of the edges.
any suggestion would be appreciated!
using that :
object3d-rotation-to-align-to-a-vector
given 2 Vector3 and a scene:
function drawCylinder(vstart, vend,scene){
var HALF_PI = +Math.PI * .5;
var distance = vstart.distanceTo(vend);
var position = vend.clone().addSelf(vstart).divideScalar(2);
var material = new THREE.MeshLambertMaterial({color:0x0000ff});
var cylinder = new THREE.CylinderGeometry(10,10,distance,10,10,false);
var orientation = new THREE.Matrix4();//a new orientation matrix to offset pivot
var offsetRotation = new THREE.Matrix4();//a matrix to fix pivot rotation
var offsetPosition = new THREE.Matrix4();//a matrix to fix pivot position
orientation.lookAt(vstart,vend,new THREE.Vector3(0,1,0));//look at destination
offsetRotation.rotateX(HALF_PI);//rotate 90 degs on X
orientation.multiplySelf(offsetRotation);//combine orientation with rotation transformations
cylinder.applyMatrix(orientation)
var mesh = new THREE.Mesh(cylinder,material);
mesh.position=position;
scene.add(mesh);
}
r58+ code :
function drawCylinder(vstart, vend,scene){
var HALF_PI = Math.PI * .5;
var distance = vstart.distanceTo(vend);
var position = vend.clone().add(vstart).divideScalar(2);
var material = new THREE.MeshLambertMaterial({color:0x0000ff});
var cylinder = new THREE.CylinderGeometry(10,10,distance,10,10,false);
var orientation = new THREE.Matrix4();//a new orientation matrix to offset pivot
var offsetRotation = new THREE.Matrix4();//a matrix to fix pivot rotation
var offsetPosition = new THREE.Matrix4();//a matrix to fix pivot position
orientation.lookAt(vstart,vend,new THREE.Vector3(0,1,0));//look at destination
offsetRotation.makeRotationX(HALF_PI);//rotate 90 degs on X
orientation.multiply(offsetRotation);//combine orientation with rotation transformations
cylinder.applyMatrix(orientation)
var mesh = new THREE.Mesh(cylinder,material);
mesh.position=position;
scene.add(mesh);
}
#jdregister's answer didn't quite work for me in R77, since the cylinder ended up with its center at vstart (rotation and lookAt were otherwise fine).
This modification to the second last line of the R58+ answer did the trick:
mesh.position.set(position.x, position.y, position.z);
There's a very succinct answer here: https://stackoverflow.com/a/44346439/1556416
I paraphrased it here:
function drawCylinder(vstart, vend, radius){
var cylLength = new THREE.Vector3().subVectors(vend, vstart).length();
var cylGeom = new THREE.CylinderGeometry(radius, radius, cylLength, 16);
cylGeom.translate(0, cylLength / 2, 0);
cylGeom.rotateX(Math.PI / 2);
var material = new THREE.MeshLambertMaterial({color: "blue"})
var cyl = new THREE.Mesh(cylGeom, material);
cyl.position.copy(vstart);
cyl.lookAt(vend); // and do the trick with orienation
return cyl
}
In R87 the "vend.clone().add(vstart).divideScalar(2);" is not working
You can position the item like this
mesh.position.copy(start);
mesh.position.lerp(end, 0.5);
All the others from R58 are fine :)