As quads faces are no longer supported in three.js. I am having trouble Raycast picking segments between 4 vertices points as a square as they are now made up of triangle faces.
So my picking returns triangle shape not square:
var faces = 10;
var myplane = new THREE.Mesh(
new THREE.PlaneGeometry(1000, 1000, faces, faces),
new THREE.MeshNormalMaterial({
color: 0x993333,
wireframe: false
}));
objects.push(myplane);
scene.add(myplane);
//raycast code
if ( intersects.length > 0 ) {
_controls.noRotate = true;
var face = intersects[0].face;
var temp = intersects[0].object;
var geo = temp.geometry;
geo.vertices[face.a].z -= 20;
geo.vertices[face.b].z -= 20;
geo.vertices[face.c].z -= 20;
//want other face verts below basically
geo.vertices[face.d].z -= 20;
geo.vertices[face.e].z -= 20;
geo.vertices[face.f].z -= 20;
geo.verticesNeedUpdate = true;
if(intersects[ 0 ].faceIndex % 2 == 0){
// geo.vertices[intersects[0].faceIndex + 1].z = 40;
// console.log("Even face Index: " + intersects[ 0 ].faceIndex);
}else{
// console.log("Odd face Index: " + intersects[ 0 ].faceIndex -1);
//geo.vertices[intersects[0].faceIndex - 1].z = 40, of course doest work
}
}
Below is a 2d example for a better vsual...
Currently I get the red out-come , but I'm wanting to pick like the green example:
try this code to get intersection faces
if ( intersects.length > 0 )
{
var faceIndex = intersects[0].faceIndex;
var obj = intersects[0].object;
var geom = obj.geometry;
var faces = obj.geometry.faces;
var facesIndices = ["a","b","c"];
facesIndices.forEach(function(indices){
geom.vertices[faces[faceIndex][indices]].setZ(-10);
});
if(faceIndex%2 == 0){
faceIndex = faceIndex+1;
}else{
faceIndex = faceIndex-1;
}
facesIndices.forEach(function(indices){
geom.vertices[faces[faceIndex][indices]].setZ(-10);
});
geom.verticesNeedUpdate = true;
}
In the general case, getting and changing the geometry of a raycast to mesh triangle intersection is just:
var face = intersects[0].face;
var obj = intersects[0].object;
var geom = obj.geometry;
var vertA = geom.vertices[face.a];
var vertB = geom.vertices[face.b];
var vertC = geom.vertices[face.c];
vertA.setY(100);
vertB.setY(110);
vertC.setY(105);
geom.verticesNeedUpdate = true;
No need to iterate through the faces array, or build a contrived face vertex array just to loop through it in the next step.
Related
need some help setting up UV's for THREE.js geometry. I made myself a sphere shape out of custom places points in 3D space. I know its easier to just use SphereGeometry and be done with it but I want to learn how to use custom geometry. I currently manage to apply the texture, and it sort of looks good until I noticed this line of texture:
image
At first I thought there was a mistake when generating points for geometry, but after some tweaking I cut down the problem to my UV assignment function:
function assignUVs(geometry, r) {
function cartesian2polar(position){
return({
phi:Math.acos(position.y / r),
theta:Math.atan2(position.z, position.x)
});
}
function polar2canvas(polarPoint) {
return({
y: polarPoint.phi/Math.PI,
x: (polarPoint.theta+Math.PI) / (2*Math.PI)
})
}
var polarVertices = geometry.vertices.map(cartesian2polar);
geometry.faceVertexUvs[0] = [];
geometry.faces.forEach(function(face) {
var uvs = [];
var ids = [ 'a', 'b', 'c'];
for( var i = 0; i < ids.length; i++ ) {
var vertexIndex = face[ ids[ i ] ];
var vertex = polarVertices[ vertexIndex ];
if(vertex.theta === 0 && (vertex.phi === 0 || vertex.phi === Math.PI)) {
var alignedVertice = vertex.phi === 0 ? face.b : face.a;
vertex = {
phi: vertex.phi,
theta: polarVertices[ alignedVertice ].theta
};
}
if(vertex.theta === Math.PI && cartesian2polar(face.normal).theta < Math.PI/2) {
vertex.theta = -Math.PI;
}
var canvasPoint = polar2canvas(vertex);
uvs.push( new THREE.Vector2( 1.0-canvasPoint.x, 1.0-canvasPoint.y ) );
geometry.faceVertexUvs[ 0 ].push( uvs );
});
geometry.uvsNeedUpdate = true;
}
So my question is, how to I get rid of this strip of texture?
Any help would be appreciated.
I have made a collision detection. You can place objects at the raycaster/mouse position on a floor. Therefore you need to click on a button 'Add object', then you get a object(helper) that follows the mouse to see if the new object get a collision with another object. When you click on the position you want, the new object will be placed to the world if there is no collision.
The collision detection I have made works perfectly when the object that is already placed in the world has the same size as the helper/new object.
On the next screenshot you can see a big object and a small(red) helper. The color red means that there is a collision. When I move the mouse more to the right it turns green.
Why does my collision detection work only with 2 objects that have the same size and why doesn't it not with different ones?
Here is my code in the Click event to show the big object:
var geometry = new THREE.BoxGeometry(200, 200, 300);
var bigobject = new THREE.Mesh(geometry, new THREE.MeshBasicMaterial({
color: 0xFBF5D7,
opacity: 1
}));
bigobject.position.copy(intersects[0].point);
bigobject.position.y = 100;
objects.push(bigobject);
scene.add(bigobject);
Here is my code to show the helper when the button 'Add object' is clicked:
var geometry = new THREE.BoxGeometry( 50, 50, 100 );
helper = new THREE.Mesh( geometry, new THREE.MeshBasicMaterial( { color: 0x00ff00, opacity: 1 } ) );
helper.name = 'helper';
scene.add( helper );
Here is my code in the MouseMove event to detect the collision:
if(scene.getObjectByName( 'helper' )) {
helper.position.copy( intersects[ 0 ].point );
helper.position.y = 25;
var helperWidth = helper.geometry.parameters.width;
var helperLength = helper.geometry.parameters.depth;
var validpositionObject = true;
for (var i = 0; i < objects.length; i++) {
var objectWidth = objects[i].geometry.parameters.width;
var objectLength = objects[i].geometry.parameters.depth;
// MIN X
var helperMinX = helper.position.x;
var objectMinX = objects[i].position.x;
// MAX X
var helperMaxX = helperWidth + helper.position.x;
var objectMaxX = objectWidth + objects[i].position.x;
// MIN Z
var helperMinZ = helper.position.z;
var objectMinZ = objects[i].position.z;
// MAX Z
var helperMaxZ = helperLength + helper.position.z;
var objectMaxZ = objectLength + objects[i].position.z;
if (objectMinX <= helperMaxX && objectMaxX >= helperMinX && objectMinZ <= helperMaxZ && objectMaxZ >= helperMinZ) {
validpositionObject = false;
}
}
if ( validpositionObject === true ) {
helper.material.color.setHex( 0x00ff00 );
validposition = true;
}else{
helper.material.color.setHex( 0xff0000 );
validposition = false;
}
}
What goes wrong with the position when it is a big and a small object.. Can anyone help me in the right direction? Many thanks
Use this:
function collisonXZ(o1, o2) {
if (Math.abs(o1.position.x - o2.position.x) > (o1.geometry.parameters.width + o2.geometry.parameters.width) / 2)
return false;
if (Math.abs(o1.position.z - o2.position.z) > (o1.geometry.parameters.depth + o2.geometry.parameters.depth) / 2)
return false;
return true;
}
var validpositionObject = true;
for (var i = 0; i < objects.length; i++) {
if (collisionXZ(helper, objects[i])) {
validpositionObject = false;
break;
}
}
I created a cube from BoxGeometry and created a sphere from SphereGeometry.
(those objects spin around itself)
When a number of both objects were increased, i found that sphere take more time to render than cube.
For example
500 cubes get 60 FPS
500 spheres get 45 FPS
while each object has the same 12 faces.
Why is it?
for(var i = 0; i <amount ; i++){
posX = posX + size;
if(i%20 == 0){
posX = -105;
posZ = posZ + size;
}
if(i%500 == 0){
posX = -105;
posZ = -500;
posY = posY + size;
} // To fit for view, five hundred objects were stacked per level.
sphereGeo = new THREE.SphereGeometry(radius, widthSegments, heightSegments);
sphereMat = new THREE.MeshPhongMaterial({ color: 0x219621, specular: 0xFFE7BA, shininess: 5, opacity: 1, transparent: true});
sphereMesh = new THREE.Mesh(sphereGeo, sphereMat);
sphereMesh.rotation.x =0;
sphereMesh.rotation.y =0;
sphereMesh.rotation.z =0;
sphereMesh.position.x = posX;
sphereMesh.position.y = posY;
sphereMesh.position.z = posZ;
sphereMesh.matrixAutoUpdate = false;
sphereMesh.updateMatrix();
sphereMesh.address = i;
countID = i;
objectsID[countID] = sphereMesh;
addList[sphereMesh.address] = objectsID[countID];
scene.add(objectsID[countID]);
}
function render()
{
var add_len = addList.length;
for(var i=0; i< add_len; i++ )
{
addList[i].rotation.y += 1;
addList[i].matrixAutoUpdate = false;
addList[i].updateMatrix();
}
renderer.render( scene, camera );
requestAnimationFrame( render );
}
these are pictures for
500 spheres
500 cubes
i set radius = 25, WidthSegments = 3 and heightSegments = 2
. So it has 12 faces but the output unlike the spherical shape
(3 and 2 are a minimum value which define in three.js Docs for segment)
For cubes creating, i use the same method and each cube has 12 faces.
Both testing case are run on the same environment.
i wonder why they have a difference performance.
maybe cause from a structure inside BoxGeometry and SphereGeometry?
Are the faces for both objects always pointing in the same direction (e.g. did you create a '12 face cube' out of a sphere somehow)?
If not, then normals and lighting would be more varied on the non-cubed object since the cube would have more faces that could share the same data (less calculations).
I want to extrude faces from a THREE.Geometry object, so my approach was to:
- specify the faces to extrude
- extract vertices on the outer edges
- Draw a THREE.Shape with those vertices
- Extrude it using THREE.ExtrudeGeometry (I'm actually using a modified version which is not generating front faces, as I'm cloning them by myself to keep original topology).
It seems to work having a CircleGeometry on a XY plane, so extruding on Z direction.
but if I rotate the circle a bit this is what I get. It extrudes on Z World Axes, not following its local orientation
I can I solve this? Here's the function I'm using:
function faceExtrude( geometry, face_group, amount ) {
if ( geometry.type === "undefined" ) {
console.log( "faceextrude error" );
return;
}
//convert face_group to vertex_group
var vertex_group = [];
for ( var i = 0; i < face_group.length; i++ ) {
//get vertices ID
var f = face_group[i];
var a = f.a;
var b = f.b;
var c = f.c;
//clone vertices
var v1 = geometry.vertices[a].clone();
var v2 = geometry.vertices[b].clone();
var v3 = geometry.vertices[c].clone();
//add them to an array
vertex_group.push(v1, v2, v3);
}
// Side Faces
var vertsToExtrude = [];
//Get only verts in outer edge
for ( var v = 0; v < vertex_group.length; v++ ) {
var shareCount = 0;
//count their presence in each face
for ( var f = 0; f < face_group.length; f++ ) {
if( v == face_group[ f ].a ||
v == face_group[ f ].b ||
v == face_group[ f ].c )
{
shareCount++;
}
}
//if < 4 than it's in an outer edge. Add it to an array
if ( shareCount < 4) {
vertsToExtrude.push( vertex_group[ v ].clone() );
}
}
//make a shape with them
var shape = new THREE.Shape();
shape.moveTo( vertsToExtrude[ 0 ].x, vertsToExtrude[ 0 ].y );
for ( var i = 1; i < vertsToExtrude.length; i++ ){
shape.lineTo( vertsToExtrude[ i ].x, vertsToExtrude[ i ].y );
}
shape.lineTo( vertsToExtrude[ 0 ].x, vertsToExtrude[ 0 ].y );
//extrude it
var extrudeSettings = { amount: amount, steps: 2, bevelEnabled: false };
var sideGeo = new THREE.MyExtrudeGeometry( shape, extrudeSettings );
//Front Faces
var frontGeo = new THREE.Geometry();
for ( var i = 0; i < face_group.length; i++ ) {
//get vertices ID
var f = face_group[i];
var a = f.a;
var b = f.b;
var c = f.c;
//new vertices IDs
var new_a = frontGeo.vertices.length;
var new_b = frontGeo.vertices.length + 1;
var new_c = frontGeo.vertices.length + 2;
//clone vertices
var v1 = geometry.vertices[a].clone();
var v2 = geometry.vertices[b].clone();
var v3 = geometry.vertices[c].clone();
frontGeo.vertices.push(v1, v2, v3);
//translate them depending on "amount"
v1.z = v1.z + amount;
v2.z = v2.z + amount;
v3.z = v3.z + amount;
//add them to a face
var f = new THREE.Face3(new_a, new_b, new_c);
frontGeo.faces.push(f);
}
frontGeo.computeFaceNormals();
geometry.merge( sideGeo );
geometry.merge( frontGeo );
};
I'm blind. Missed this while reading docs about ExtrudeGeometry:
"extrudePath — THREE.CurvePath. 3d spline path to extrude shape along. (creates Frames if (frames aren't defined)"
Think this is the solution I'm looking for :)
I am want to draw a plane that has an an array of points lying on it (including the origin). The three.js library draws the plane on the origin, facing the xy plane. Right now, I am having trouble of moving the it from the origin to a position such that it contains the points.
So far, I have managed to find a way to orient some planes that lie on the y-axis:
var directionalVectors = __getDirectionVectors(points);
var normal = __getNormalOfPlane(directionalVectors);
var angleXY = __getAngleBetweenPlanes( normal, new THREE.Vector3(0, 0, 1) );
plane.rotateY(- angleXY );
plane.translateY( planeDimensions.width /2.0);
plane.translateX( planeDimensions.height /2.0);
This is how I calculate the direction vectors:
var __getDirectionVectors = function( points ){
var numOfPoints = points.length, i;
var pointOne, pointTwo, directionalVectors = [], directionalVector;
for( i = 0; i < numOfPoints - 1; i++){
pointOne = points[i];
pointTwo = points[i + 1];
directionalVector = new THREE.Vector3().subVectors(pointOne, pointTwo);
directionalVectors.push(directionalVector);
}
return directionalVectors;
};
This is how I calculate the normal:
var __getNormalOfPlane = function(vectors){
var numOfVectors = vectors.length;
var vectorOne, vectorTwo, normal;
if( numOfVectors >= 2){
vectorOne = vectors[0];
vectorTwo = vectors[1];
normal = new THREE.Vector3().crossVectors(vectorOne, vectorTwo);
}
return normal;
};
This is how I calculate the angle between the plane and the XY plane:
//http://www.netcomuk.co.uk/~jenolive/vect14.html
var __getAngleBetweenPlanes = function( normalOne, normalTwo){
var dotPdt = normalOne.dot(normalTwo);
var angle = Math.acos( dotPdt / ( normalOne.length() * normalTwo.length() ) );
return angle;
}
Is there any way I could orient the plane properly for all types of planes?