Generate shape in three.js from multiple elements - three.js

Is there a way in three.js to create a poly from multiple individual elements, rectangle for example.
I have attached an example.
I am using:
for(i = 0; i<5; i++){
var rand = Math.floor(Math.random() * 50)+1000;
var material = new THREE.MeshBasicMaterial({
color : "#ff"+i+ rand,
side : THREE.DoubleSide,
transparent : true,
opacity : 1
});
var mesh = new THREE.Mesh( geometry, material );
if(angle) mesh.rotation.y = angle;
mesh.position.set( loop+1, 4,4);
scene.add( mesh );
}
When I apply roatation mesh.rotation.y = angle; it doesn't come up with my below design, I rather get a cross + because the panel rotates on it's y from center, not from corner...
Thank you
The

There are 3 ways to achieve what you're trying to do. The problem you are facing stems from transform origin, as you noted, origin defaults to position [0,0,0]. So, your options are:
build a transform matrix using a different transform offset for rotation, this is probably an overkill for simple use-cases.
translate geometry to not be centered on [0,0,0], for example you can move the whole quad (your geometry) right so that the left edge of the quad aligns with [0,0,0], then, when you rotate, left edge will stay put.
embed Mesh inside a Group, rotate the Mesh and translate (position.set(....)) the Group.
no matter which route you take - you will still have to deal with the some trigonometry as you will need to compute the position for the next segment to align with the edge of the previous one.
One more way around that is to build the following type of structure
Group[
Mesh 1,
Mesh 2,
Mesh 3,
Group [
Mesh 4,
Mesh 5,
Mesh 6,
Group [
Mesh 7
]
]
]
Last group is unnecessary, it's there purely for consistency.
As far as the trigonometry that I mentioned - it's simple Sin and Cos stuff, so it should be quite simple. Here is some pseudo-code that you'll need:
prevPosition, prevAngle //position and angle of previous segment
// Compute next segment transform
nextPosition.x = Math.cos(prevAngle)*segmentSize + prevPosition.x;
nextPosition.z = Math.sin(prevAngle)*segmentSize + prevPosition.z;

Related

Why is three.js inconsistent about gouraud interpolation?

I want to shade a THREE.BoxBufferGeometry using a simple THREE.MeshLambertMaterial. The material is supposed to use a Lambert illumination model to pick the colors for each vertex (and it does), and then use Gouraud shading to produce smooth gradients on each face.
The Gouraud part is not happening. Instead, the cube's faces are each shaded with one single, solid color.
I have tried various other BufferGeometrys, and gotten inconsistent results.
For example, if instead I make an IcosahedronBufferGeometry, I get the same problem: each face is one single, solid color.
geometry = new THREE.IcosahedronBufferGeometry(2, 0); // no Gouraud shading.
geometry = new THREE.IcosahedronBufferGeometry(2, 2); // no Gouraud shading.
On the other hand, if I make a SphereBufferGeometry, the Gouraud is present.
geometry = new THREE.SphereBufferGeometry(2, 3, 2); // yes Gouraud shading.
geometry = new THREE.SphereBufferGeometry(2, 16, 16); // yes Gouraud shading.
But then if I make a cube using a PolyhedronBufferGeometry, the Gouraud shading doesn't appear unless I set the detail to something other than 0.
const verticesOfCube = [
-1,-1,-1, 1,-1,-1, 1, 1,-1, -1, 1,-1,
-1,-1, 1, 1,-1, 1, 1, 1, 1, -1, 1, 1,
];
const indicesOfFaces = [
2,1,0, 0,3,2,
0,4,7, 7,3,0,
0,1,5, 5,4,0,
1,2,6, 6,5,1,
2,3,7, 7,6,2,
4,5,6, 6,7,4
];
const geometry = new THREE.PolyhedronBufferGeometry(verticesOfCube, indicesOfFaces, 1, 1); // no Gouraud shading
geometry = new THREE.PolyhedronBufferGeometry(verticesOfCube, indicesOfFaces, 1, 1); // yes Gouraud shading
I am aware of the existence of the BufferGeometry methods computeFaceNormals() and computeVertexNormals(). Normals are emphatically important here, as they are used to determine the colors for each face and vertice, respectively. But while they help with the Icosahedron, they have no effect on the Box, no matter whether they are present, only one is present, or both are present in both possible orders.
Here is the code I expect to work:
const geometry = new THREE.BoxBufferGeometry(2, 2, 2);
geometry.computeFaceNormals();
geometry.computeVertexNormals();
const material = new THREE.MeshLambertMaterial({
color: 0xBE6E37
});
const mesh = new THREE.Mesh(geometry, material);
I should be getting a cube whose faces (the real, triangular ones) are shaded with a gradient. First, the face normals should be computed, and then the vertex normals by averaging the normals of the faces formed by them. Here is a triangular bipyramid on which correct Gouraud shading is being applied:
But the code above produces this instead:
At no point does three.js log any errors or warnings to the console.
So what is it that's going on here? The only explanation I can think of is that the Box is actually comprised of 24 vertices, three at each corner of the cube, and that they form faces such that each vertex's computed normal is an average of at most two faces pointing in the same direction. But I can't find that written down anywhere, and that explanation doesn't fly for the Polyhedron where vertices and faces were explicitly specified in code.

Find relative position of axes of cube after rotation

I am rotating a cube through a series of 90 degree rotations using quaternions and I want to be able to get relative positions of the quaternions after a rotation compared with its original position that I have stored.
IE I'd like to know which axis is now equivalent to the original x axis (and if it is inverted), and so on...
I'm using threejs, but I'm sure that's not necessary for answering.
Use this pattern to determine the direction the x-axis is pointing after applying a series of rotations.
var dir = new THREE.Vector3( 1, 0, 0 );
dir.applyQuaternion( q1 );
dir.applyQuaternion( q2 ); // etc...
To see it visually, you can add axes as a child of your cube mesh. The axes will be rotated automatically.
var axes = new THREE.AxisHelper( 100 );
mesh.add( axes );
three.js r.71

How to list me vertex normals to the specified tops?

I do animation at which the part of tops moves. Thus lighting starts working incorrectly. For correct lighting it is necessary to change face.vertexNormals. At first I thought that it is enough
geometry.computeVertexNormals();
But it appeared, it does not absolutely that.
How to list me topmost vertex normals to the specified tops?
OR
How to list me the faces containing specified tops?
example here
Here example. But I need not only to see them, and to list and change in the program.
if (d<50) { var dist = 15 * Math.cos( d/20 - t );
geometry.vertices[i].z = dist; }
How to list me vertex normals for these tops?
Your plane is upside-down, which causes your vertex normals to point downward.
Set plane.rotation.x = -Math.PI/2;.
To see the normals, add
vnh = new THREE.VertexNormalsHelper( plane, 20, 0xff0000, 2 );
scene.add( vnh );
to your init() function, and in your animation loop call:
vnh.update();
three.js r.68

Three.js: change the pivot point of a sprite

I've created a 3D map and I'm labelling points on this map through Sprites. This in itself works fine, except for the positioning of the sprite labels.
Because I'm creating a map the camera can tilt from 0 to 90 degrees, while ideally the label always stays some distance directly above the item it is labelling on the screen. But unfortunately, as sprites are always centred around their origin and that overlaps the item, I have to move the sprite up on the Y world axis and with that the centre location of the sprite changes as the camera is tilted. This looks weird if the item looked at is off centre, and doesn't work too well when the camera is looking straight down.
No jsfiddle handy, but my application at http://leeft.eu/starcitizen/ should give a good impression of what it looks like.
The code of THREE.SpritePlugin suggests to me it should be possible to use "matrixWorld" to shift the sprite some distance up on the screen's Y axis while rendering, but I can't work out how to use that, nor am I entirely sure that's what I need to use in the first place.
Is it possible to shift the sprites up on the screen while rendering, or perhaps change their origin? Or is there maybe some other way I can achieve the same effect?
Three.js r.67
As suggested by WestLangley, I've created a workable solution by changing the sprite position based on the viewing angle though it took me hours to work out the few lines of code needed to get the math working. I've updated my application too, so see that for a live demo.
With the tilt angle phi and the heading angle theta as computed from the camera in OrbitControls.js the following code computes a sprite offset that does exactly what I want it to:
// Given:
// phi = tilt; 0 = top down view, 1.48 = 85 degrees (almost head on)
// theta = heading; 0 = north, < 0 looking east, > 0 looking west
// Compute an "opposite" angle; note the 'YXZ' axis order is important
var euler = new THREE.Euler( phi + Math.PI / 2, theta, 0, 'YXZ' );
// Labels are positioned 5.5 units up the Y axis relative to its parent
var spriteOffset = new THREE.Vector3( 0, -5.5, 0 );
// Rotate the offset vector to be opposite to the camera
spriteOffset.applyMatrix4( new THREE.Matrix4().makeRotationFromEuler( euler ) );
scene.traverse( function ( object ) {
if ( ( object instanceof THREE.Sprite ) && object.userData.isLabel ) {
object.position.copy( spriteOffset );
}
} );
Note for anyone using this code: that the sprite labels are children of the object group they're referring to, and this only sets a local offset from that parent object.
I had a similar problem, but with flat sprites; I put trees on a map and wanted them to rotate in such a way that they'd rotate around their base, rather than their center. To do that, i simply edited the image files of the trees to be twice as tall, with the bottom as just a transparency:
http://imgur.com/ogFxyFw
if you turn the first image into a sprite, it'll rotate around the tree's center when the camera rotates. The second tree will rotate around it's base when the camera rotates.
For your application, if you resize the textbox in such a way that the center of it would be coincide with the star; perhaps by adding a few newlines or editing the height of the sprite
This is very much a hack, but if you will only use sprites in this way, and could tolerate a global change to how sprites were rendered, you could change the following line in the compiled three.js script:
Find (ctrl+F) THREE.SpritePlugin = function, and you'll see:
this.init = function ( renderer ) {
_gl = renderer.context;
_renderer = renderer;
vertices = new Float32Array( [
- 0.5, - 0.5, 0, 0,
0.5, - 0.5, 1, 0,
0.5, 0.5, 1, 1,
- 0.5, 0.5, 0, 1
] );
I changed the definition of the array to the following:
var vertices = new Float32Array( [
- 0.5, - 0.0, 0, 0,
0.5, - 0.0, 1, 0,
0.5, 1.0, 1, 1,
- 0.5, 1.0, 0, 1
] );
And now all my sprites render with the rotation origin at the bottom.
If you use the minified version, search for THREE.SpritePlugin=function and move the cursor right until you find the Float32Array defined, and make the same changes there.
Note: this changes how things render only when using WebGL. For the canvas renderer you'll have to play a function called renderSprite() in the THREE.CanvasRenderer. I suspect playing with these lines will do it:
var dist = 0.5 * Math.sqrt( scaleX * scaleX + scaleY * scaleY ); // allow for rotated sprite
_elemBox.min.set( v1.x - dist, v1.y - dist );
_elemBox.max.set( v1.x + dist, v1.y + dist );
This function will also be a lot more difficult to find in the minified version, since renderSprite() is not an outward facing function, it'll likely be renamed to something obscure and small.
Note 2: I did try making these modifications with "polyfills" (or rather, redefining the SpritePlugin after Three is defined), but it caused major problems with things not being properly defined for some reason. Scoping is also an issue with the "polyfill" method.
Note 3: My version of three.js is r69. So there may be differences above.

Rotate a plane on its edge in three.js

I wanted to rotate a plane, but I can't figure out how to set the rotation axis. I'd like to rotate a plane around its edge.
I've seen solutions suggesting matrix transformations but they lacked explanation so I couldn't apply them.
Ok I figured it out. What you have to do is create a parent 3D object and add the plane to it. Once, added, you have to translate it by 50% and start rotating the parent object.
var object = THREE.SceneUtils.createMultiMaterialObject( new THREE.PlaneGeometry( 200, 50, 4, 4 ), [material] );
var parent = new THREE.Object3D();
object.applyMatrix( new THREE.Matrix4().makeTranslation( 0, 25, 0 ) );
parent.add(object);
parent.rotation.x = 1;
scene.add(parent)

Resources