Three.js creating a custom Mesh fails - three.js

I'm looking to create a custom mesh of a d10 in three.js. I think I've got most of it set up correctly (creating vertices, tying vertices to faces), but when I attempt to computeFaceNormals() I'm greeted with an uncaught type error (cannot read property 'x' of undefined). Code is below:
function newDie10(){
var geom = new THREE.Geometry();
//Add the top & bottom vertices of the die
geom.vertices.push(new THREE.Vector3(0,0,1));
geom.vertices.push(new THREE.Vector3(0,0,-1));
var z = .1;
//Add the outer rim of vertices
//half above the midline and half below
for(var angle=0; angle <360; angle+=36){
var vert = new THREE.Vector3(Math.cos(angle),Math.sin(angle),z);
geom.vertices.push(vert);
console.log(vert.x," ",vert.y," ",vert.z);
z = z*-1;
}
//Each face is split into two triangles
//final, combined face is diamond-shaped
geom.faces.push(new THREE.Face3(0,2,4)); //1
geom.faces.push(new THREE.Face3(2,3,4)); //1
geom.faces.push(new THREE.Face3(0,4,6)); //2
geom.faces.push(new THREE.Face3(4,5,6)); //2
// Some similar code omitted for readability
geom.faces.push(new THREE.Face3(1,9,11)); //9
geom.faces.push(new THREE.Face3(9,10,11)); //9
geom.faces.push(new THREE.Face3(1,11,3)); //0
geom.faces.push(new THREE.Face3(11,12,3)); //0
//The error occurs here
geom.computeFaceNormals();
return new Physijs.ConvexMesh(geom, new Physijs.createMaterial(new THREE.MeshPhongMaterial({color: 0x005588}), .5, .3), 1);
}

You are making two mistakes:
First, Math.cos and Math.sin takes radians as argument, not 360-degrees angles. The solution is to convert angle to radians:
var angleInRadians = angle / 180 * Math.PI;
var vert = new THREE.Vector3(Math.cos(angleInRadians),
Math.sin(angleInRadians), z);
Also, the vertex indices are wrong in the last face. The index 12 does not exist as the vertex indices are zero based.
geom.faces.push(new THREE.Face3(11,12,3)); // These are wrong
I have tested your code, and these are the right indices:
geom.faces.push(new THREE.Face3(11,2,3)); // These are right

Related

How fill a loaded STL mesh ( NOT SIMPLE SHAPES LIKE CUBE ETC) with random particles and animate with this geometry bound in three.js

How I can fill a loaded STL mesh ( like suzane NOT SIMPLE SHAPES LIKE CUBE etc) with random particles and animate it inside this geometry bounds with three.js ?
I see many examples but all of it for simple shapes with geometrical bounds like cube or sphere with limit by coordinates around center
https://threejs.org/examples/?q=points#webgl_custom_attributes_points3
TNX
A concept, using a ray, that counts intersections of the ray with faces of a mesh, and if the number is odd, it means that the point is inside of the mesh:
Codepen
function fillWithPoints(geometry, count) {
var ray = new THREE.Ray()
var size = new THREE.Vector3();
geometry.computeBoundingBox();
let bbox = geometry.boundingBox;
let points = [];
var dir = new THREE.Vector3(1, 1, 1).normalize();
for (let i = 0; i < count; i++) {
let p = setRandomVector(bbox.min, bbox.max);
points.push(p);
}
function setRandomVector(min, max){
let v = new THREE.Vector3(
THREE.Math.randFloat(min.x, max.x),
THREE.Math.randFloat(min.y, max.y),
THREE.Math.randFloat(min.z, max.z)
);
if (!isInside(v)){return setRandomVector(min, max);}
return v;
}
function isInside(v){
ray.set(v, dir);
let counter = 0;
let pos = geometry.attributes.position;
let faces = pos.count / 3;
let vA = new THREE.Vector3(), vB = new THREE.Vector3(), vC = new THREE.Vector3();
for(let i = 0; i < faces; i++){
vA.fromBufferAttribute(pos, i * 3 + 0);
vB.fromBufferAttribute(pos, i * 3 + 1);
vC.fromBufferAttribute(pos, i * 3 + 2);
if (ray.intersectTriangle(vA, vB, vC)) counter++;
}
return counter % 2 == 1;
}
return new THREE.BufferGeometry().setFromPoints(points);
}
The concepts from the previous answer is very good, but it has some performance limitations:
the whole geometry is tested with every ray
the recursion on points outside can lead to stack overflow
Moreover, it's incompatible with indexed geometry.
It can be improved by creating a spatial hashmap storing the geometry triangles and limiting the intersection test to only some part of the mesh.
Demonstration

CircleGeometry to Shape

I would like to convert a THREE.CircleGeometry to a THREE.Shape, but I haven't found a way yet. I want to extrude this circle, but the ExtrudeGeometry() only works with shapes.
I know I can use curves to draw a circle but I want to keep the topology the CircleGeometry() gives.
If that's not possible, is there a work around to draw a circle shape which is made of a number of triangular segments that are oriented around a central point?
This will generate a shape as circle geometry does.
Here is fiddle: http://jsfiddle.net/tcLr7eg3/
var circleTri = new THREE.Shape();
radius = 30;
segments = 32;
var theta_next, x_next, y_next, j;
for (var i = 0; i < segments; i++) {
theta = ((i + 1) / segments) * Math.PI * 2.0;
x = radius * Math.cos(theta);
y = radius * Math.sin(theta);
j = i + 2;
if( (j - 1) === segments ) j = 1;
theta_next = (j / segments) * Math.PI * 2.0;
x_next = radius * Math.cos(theta_next);
y_next = radius * Math.sin(theta_next);
circleTri.moveTo(0, 0);
circleTri.lineTo(x, y);
circleTri.lineTo(x_next, y_next);
circleTri.lineTo(0, 0);
}
For anyone new to three.js looking to do this, you should try the SphereGeometry shape. You can create a perfectly round sphere this way without the hassle of creating your own shape or trying to extrude a CircleGeometry shape. The code example on the docs should give you a good starting point.
const geometry = new THREE.SphereGeometry( 5, 32, 32 );
const material = new THREE.MeshBasicMaterial( {color: 0xffff00} );
const sphere = new THREE.Mesh( geometry, material );
scene.add( sphere );

Drawing lines between the Icosahedron vertices without wireframe material and with some line width using WEBGLRenderer

I'm new to threejs
I need to draw a sphere connected with triangles. I use Icosahedron to construct the sphere in the following way
var material = new THREE.MeshPhongMaterial({
emissive : 0xffffff,
transparent: true,
opacity : 0.5,
wireframe : true
});
var icogeo = new THREE.IcosahedronGeometry(80,2);
var mesh = new THREE.Mesh(icogeo, material);
scean.add(mesh);
But i need the width of the line to be more but line width won't show up in windows so i taught of looping through the vertices and draw a cylinder/tube between the vertices. (I can't draw lines because the LineBasicMaterial was not responding to Light.)
for(i=0;i<icogeo.faces.length;i++){
var face = icogeo.faces[i];
//get vertices from face and draw cylinder/tube between the three vertices
}
Can some one please help on drawing the tube/cylinder between two vector3 vertices?
**the problem i'm facing with wireframe was it was not smooth and i can't increase width of it in windows.
If you really want to create a cylinder between two points one way to do is to create it in a unit space and then transform it to your line. But that is very mathy.
An intuitive way to create it is to think about how would you do it in a unit space? A circle around the z axis (in x,y) and another one a bit down z.
Creating a circle in 2d is easy: for ( angle(0,360,360/numsteps) ) (x,y)=(sin(angle),cos(angle))*radius. (see for example Calculating the position of points in a circle).
Now the two butt ends of your cylinder are not in x,y! But If you have two vectors dx,dy you can just multiply your x,y with them and get a 3d position!
So how to get dx, dy? One way is http://en.wikipedia.org/wiki/Gram%E2%80%93Schmidt_process
which reads way more scary than it is. You start with your forward direction, which is your line. forward = normalize(end-start). Then you just pick a direction "up". Usually (0,1,0). Unless forward is already close to up, then pick another one like (1,0,0). Take their cross product. This gives you "left". Then take the cross product between "left" and "forward" to get "right". Now "left" and "right" are you dx and dy!
That way you can make two circles at the two ends of your line. Add triangles in between and you have a cylinder!
Even though I do believe it is an overkill for what you are trying to achieve, here is code that draws a capsule (cylinder with spheres at the end) between two endpoints.
/**
* Returns a THREE.Object3D cylinder and spheres going from top to bottom positions
* #param radius - the radius of the capsule's cylinder
* #param top, bottom - THREE.Vector3, top and bottom positions of cone
* #param radiusSegments - tessellation around equator
* #param openTop, openBottom - whether the end is given a sphere; true means they are not
* #param material - THREE.Material
*/
function createCapsule (radius, top, bottom, radiusSegments, openTop, openBottom, material)
{
radiusSegments = (radiusSegments === undefined) ? 32 : radiusSegments;
openTop = (openTop === undefined) ? false : openTop;
openBottom = (openBottom === undefined) ? false : openBottom;
var capsule = new THREE.Object3D();
var cylinderAxis = new THREE.Vector3();
cylinderAxis.subVectors (top, bottom); // get cylinder height
var cylinderGeom = new THREE.CylinderGeometry (radius, radius, cylinderAxis.length(), radiusSegments, 1, true); // open-ended
var cylinderMesh = new THREE.Mesh (cylinderGeom, material);
// get cylinder center for translation
var center = new THREE.Vector3();
center.addVectors (top, bottom);
center.divideScalar (2.0);
// pass in the cylinder itself, its desired axis, and the place to move the center.
makeLengthAngleAxisTransform (cylinderMesh, cylinderAxis, center);
capsule.add (cylinderMesh);
if (! openTop || ! openBottom)
{
// instance geometry
var hemisphGeom = new THREE.SphereGeometry (radius, radiusSegments, radiusSegments/2, 0, 2*Math.PI, 0, Math.PI/2);
// make a cap instance of hemisphGeom around 'center', looking into some 'direction'
var makeHemiCapMesh = function (direction, center)
{
var cap = new THREE.Mesh (hemisphGeom, material);
makeLengthAngleAxisTransform (cap, direction, center);
return cap;
};
// ================================================================================
if (! openTop)
capsule.add (makeHemiCapMesh (cylinderAxis, top));
// reverse the axis so that the hemiCaps would look the other way
cylinderAxis.negate();
if (! openBottom)
capsule.add (makeHemiCapMesh (cylinderAxis, bottom));
}
return capsule;
}
// Transform object to align with given axis and then move to center
function makeLengthAngleAxisTransform (obj, align_axis, center)
{
obj.matrixAutoUpdate = false;
// From left to right using frames: translate, then rotate; TR.
// So translate is first.
obj.matrix.makeTranslation (center.x, center.y, center.z);
// take cross product of axis and up vector to get axis of rotation
var yAxis = new THREE.Vector3 (0, 1, 0);
// Needed later for dot product, just do it now;
var axis = new THREE.Vector3();
axis.copy (align_axis);
axis.normalize();
var rotationAxis = new THREE.Vector3();
rotationAxis.crossVectors (axis, yAxis);
if (rotationAxis.length() < 0.000001)
{
// Special case: if rotationAxis is just about zero, set to X axis,
// so that the angle can be given as 0 or PI. This works ONLY
// because we know one of the two axes is +Y.
rotationAxis.set (1, 0, 0);
}
rotationAxis.normalize();
// take dot product of axis and up vector to get cosine of angle of rotation
var theta = -Math.acos (axis.dot (yAxis));
// obj.matrix.makeRotationAxis (rotationAxis, theta);
var rotMatrix = new THREE.Matrix4();
rotMatrix.makeRotationAxis (rotationAxis, theta);
obj.matrix.multiply (rotMatrix);
}

How to get UV and texture coordinate from a face or 3d point?

I have a sphere geometry with a basic material which mapped by a texture:
var geometry = new THREE.SphereGeometry(500, 60, 40);
var material = new THREE.MeshBasicMaterial({
map: THREE.ImageUtils.loadTexture('textures/equirectangular.jpg'),
overdraw: 0.5
});
var mesh = new THREE.Mesh(geometry, material);
And on mouse click:
mouse.x = (event.clientX / renderer.domElement.width) * 2 - 1;
mouse.y = -(event.clientY / renderer.domElement.height) * 2 + 1;
raycaster.setFromCamera(mouse, camera);
var intersects = raycaster.intersectObject(mesh);
Which gives me an array of intersected objects.
Now how can i find the UV coordinate out of the intersected point and then get the texture pixel(pixels range) of the sphere texture?
With more googling and seeing this question, i've got this code which gives me the right texture coordinate:
var p = intersects[0].point;
var x = (p.x - sphere.position.x) / (-1 * radius);
var y = (p.y - sphere.position.y) / radius;
var z = (p.z - sphere.position.z) / radius;
var u = 1 - (Math.atan2(z, x) / (2 * Math.PI) + 0.5);
var v = 1 - ((Math.asin(y) / Math.PI) + 0.5);
console.log("u,v:", u, v);
var x = u * textureWidth;
var y = v * textureHeight;
console.log(x, y);
Note: camera is inside the sphere at (0, 0, 0) and i apply a scale matrix to the sphere geometry like this:
geometry.applyMatrix(new THREE.Matrix4().makeScale(-1, 1, 1));
But i'm looking for more complete answer which applies for geometry without negative scale too or other basic geometries like plane or cube.
Also this code returns inaccurate texture X coordinates for a loaded sphere from blender. generally getting the right x coordinates is more problematic for me. i guess the geometry/mesh rotation and scale should take into account in order to get right x,y texture coordinates, but i'm not good at math!

three.js - Set the rotation of an object in relation to its own axes

I'm trying to make a static 3D prism out of point clouds with specific numbers of particles in each. I've got the the corner coordinates of each side of the prism based on the angle of turn, and tried spawning the particles in the area bound by these coordinates. Instead, the resulting point clouds have kept only the bottom left coordinate.
Screenshot: http://i.stack.imgur.com/uQ7Q8.png
I've tried to set the rotation of each cloud object such that their edges meet, but they will rotate only around the world centre. I gather this is something to do with rotation matrices and Euler angles, but, having been trying to work them out for 3 solid days, I've despaired. (I'm a sociologist, not a dev, and haven't touched graphics before this project.)
Please help? How do I set the rotation on each face of the prism? Or maybe there is a more sensible way to get the particles to spawn in the correct area in the first place?
The code:
// draw the particles
var n = 0;
do {
var geom = new THREE.Geometry();
var material = new THREE.PointCloudMaterial({size: 1, vertexColors: true, color: 0xffffff});
for (i = 0; i < group[n]; i++) {
if (geom.vertices.length < group[n]){
var particle = new THREE.Vector3(
Math.random() * screens[n].bottomrightback.x + screens[n].bottomleftfront.x,
Math.random() * screens[n].toprightback.y + screens[n].bottomleftfront.y,
Math.random() * screens[n].bottomrightfront.z + screens[n].bottomleftfront.z);
geom.vertices.push(particle);
geom.colors.push(new THREE.Color(Math.random() * 0x00ffff));
}
}
var system = new THREE.PointCloud(geom, material);
scene.add(system);
**// something something matrix Euler something?**
n++
}
while (n < numGroups);
I've tried to set the rotation of each cloud object such that their
edges meet, but they will rotate only around the world centre.
It is true they only rotate around 0,0,0. The simple solution then is to move the object to the center, rotate it, and then move it back to its original position.
For example (Code not tested so might take a bit of tweaking):
var m = new THREE.Matrix4();
var movetocenter = new THREE.Matrix4();
movetocenter.makeTranslation(-x, -y, -z);
var rotate = new THREE.Matrix4();
rotate.makeRotationFromEuler(); //Build your rotation here
var moveback = new THREE.Matrix4();
moveback .makeTranslation(x, y, z);
m.multiply(movetocenter);
m.multiply(rotate);
m.multiply(moveback);
//Now you can use geometry.applyMatrix(m)

Resources