How to create a half Cylinder in Three js? - three.js

I´m very new to three js and I want to create a half Cylinder as roof.
var geometry = new THREE.CylinderGeometry(100,100,150);
var material = new THREE.MeshNormalMaterial();
var cylinder = new THREE.Mesh( geometry, material);
scene.add(cylinder);
This is the Basic Cylinder but unfortunately I couldn´t find an specific answer to a half cylinder yet.
Is it possible to create this with vertices?

The last 2 params of CylinderGeometry constructor allows this. For example:
var geometry = new THREE.CylinderGeometry(100,100,150, 8, 1, false, 0, Math.PI);
0 is the start angle and Math.PI is the end angle; i.e. half of a circle.

Related

three.js make a cutting plane visible

In this demo:
https://threejs.org/examples/?q=clipping#webgl_clipping_advanced
if you enable the "visualize" option, you can see the 3d pyramid "cutting" the inside object.
Here:
https://threejs.org/examples/?q=clipping#webgl_clipping
there is a simple 2d plane cutting the object, but there is no such option to "see" the plane. I just started learning threejs and I am not too familiar with any 3d engine (other than fully understanding the math behind it), so I tried some basic stuff, e.g.:
localPlane.visible = true
But of course it didn't work. Any 'simple' way to make the second demo display the cutting plane?
Thank you
Here's some code to add the plane in the position you want:
const planeGeometry = new THREE.PlaneGeometry( 1.5, 1.5 );
const planeMaterial = new THREE.MeshBasicMaterial( {color: 0xffff00, side: THREE.DoubleSide} );
const plane = new THREE.Mesh( planeGeometry, planeMaterial );
plane.position.copy(localPlane.normal);
plane.position.multiplyScalar(-localPlane.constant);
plane.quaternion.setFromUnitVectors(new THREE.Vector3(0, 0, 1), localPlane.normal);
plane.material.opacity = 0.5;
plane.material.transparent = true;
scene.add( plane );
And here's what that looks like...
However, depending on what you are implementing, you may find it easier to create a Plane Mesh in the position you want, and then derive the clipping plane THREE.Plane from that.
If you want to be able to move the clipping plane around, the reasoning involved in moving a Plane Mesh Object3D is probably more straightforward than reasoning about moving a THREE.Plane.
(update)
Another alternative approach I've come across: you could simply use the built in THREE.PlaneHelper, which can be used to visualize any THREE.Plane.
https://threejs.org/docs/#api/en/helpers/PlaneHelper
The sample code offered on that page is this:
const plane = new THREE.Plane( new THREE.Vector3( 1, 1, 0.2 ), 3 );
const helper = new THREE.PlaneHelper( plane, 1, 0xffff00 );
scene.add( helper );

Why is texture not rendering on vertices in Three.js (missing UVs)

I have created a scene with THREE.js. Most of the surfaces are BoxGeometries with zero width and a texture applied. Everything works well, including transparent windows on the house. When I went to add roof, I needed to make angled flat panels and triangular spaces. I decided to use a technique borrowed from another StackOverflow page (How to create a custom mesh on THREE.JS?).
The mesh is rendering, but not the texture. I've tried with different texture images and it does change the COLOR of the rendered panel, but still no visible texture. I'm missing something. If I can get this to work, I'll start using more vertex-based meshes to fill in my building. Why isn't the texture rendering?
//texture
var texture = new THREE.ImageUtils.loadTexture("shingles.jpg");
texture["shingles"] = new THREE.MeshBasicMaterial({ map:texture, side:THREE.DoubleSide});
//roof
var v1 = new THREE.Vector3(farRight,level+height,back);
var v2 = new THREE.Vector3(farRight,level+height,front);
var v3 = new THREE.Vector3((farRight+farLeft)/2,level+(3*height/2),front-(3*width/2));
var v4 = new THREE.Vector3((farRight+farLeft)/2,level+(3*height/2),back+(3*width/2));
var geom = new THREE.Geometry();
geom.vertices.push(v1);
geom.vertices.push(v2);
geom.vertices.push(v3);
geom.vertices.push(v4);
geom.faces.push( new THREE.Face3( 0, 1, 2 ) );
geom.faces.push( new THREE.Face3( 3, 0, 2 ) );
var object = new THREE.Mesh( geom, textures["shingles"] );
scene.add(object);
----------------Updated with full answer-------------------------------
As noted in the accepted answer, I missed adding the UV vectors. After some reading I was able to figure out how they work and get the textures to map appropriately. I'm including the full solution here for future reference. First two snippets of code.
var v3 = new THREE.Vector3((farRight+farLeft)/2,level+(3*height/2),front-(3*width/2));
var v4 = new THREE.Vector3((farRight+farLeft)/2,level+(3*height/2),back+(3*width/2));
var v5 = new THREE.Vector3(farLeft,level+height,back);
var v6 = new THREE.Vector3(farLeft,level+height,front);
geom = new THREE.Geometry();
geom.vertices.push(v3);
geom.vertices.push(v4);
geom.vertices.push(v5);
geom.vertices.push(v6);
geom.faces.push( new THREE.Face3( 0, 1, 2 ) );
geom.faces.push( new THREE.Face3( 3, 0, 2 ) );
geom.faceVertexUvs[0].push([new THREE.Vector2(.3, 1),
new THREE.Vector2(.7, 1),
new THREE.Vector2(1, 0)]);
geom.faceVertexUvs[0].push([new THREE.Vector2(0, 0),
new THREE.Vector2(.3, 1),
new THREE.Vector2(1, 0)]);
object = new THREE.Mesh( geom, textures["shingles"] );
scene.add(object);
var v1 = new THREE.Vector3(farRight,level+height,back);
var v4 = new THREE.Vector3((farRight+farLeft)/2,level+(3*height/2),back+(3*width/2));
var v5 = new THREE.Vector3(farLeft,level+height,back);
geom = new THREE.Geometry();
geom.vertices.push(v1);
geom.vertices.push(v4);
geom.vertices.push(v5);
geom.faces.push( new THREE.Face3( 0, 1, 2 ) );
geom.faceVertexUvs[0].push([new THREE.Vector2(0, 0),
new THREE.Vector2(.5, 1),
new THREE.Vector2(1, 0)]);
object = new THREE.Mesh( geom, textures["shingles"] );
scene.add(object);
Now a picture and an explanation. The two geometries in the code above are highlighted in the picture. Because I got fancy, vertices 3 and 4 are slightly inward making a trapezoid shape. Likewise the UV vertices that correspond to those points are .3 and .7 inward from the corners to make a trapezoid. The triangular geometry has two points at the base and one at the top-middle.
Basically, create the vertices, add the vertices, define the faces, and (this is the part I missed) add UV vertices corresponding to the locations in the texture.
You will need to add texture coordinates as well.. (aka uv coordinates). There is a field called faceVertexUVs that has to be filled out. You will need to set 0,0 for the top left corner of each face, 1,0 for top right, 0,1 for bottom left and 1,1 for bottom right corner.

Inconsistent surface behaviour in Three.js

when I set a point light at a THREE.BoxGeometry object it looks like this:
THREE.BoxGeometry with point light
var light = new THREE.PointLight (0xffffff, 1, 100);
light.position.set (10, 10, 10);
scene.add (light);
var geometry = new THREE.BoxGeometry (1, 1, 1);
var material = new THREE.MeshPhongMaterial ();
var cube = new THREE.Mesh (geometry, material);
scene.add (cube);
When I now set a point light at a THREE.PolyhedronGeometry object it looks like this:
THREE.PolyhedronGeometry with point light
var light = new THREE.PointLight (0xffffff, 1, 100);
light.position.set (10, 10, 10);
scene.add (light);
var geometry = new THREE.PolyhedronGeometry (vertices, faces, 1, 0);
var vertices = [-1,-1,-1,1,-1,-1,1,1,-1,-1,1,-1,-1,-1,1,1,-1,1,1,1,1,-1,1,1];
var faces = [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];
var material = new THREE.MeshPhongMaterial ();
var cube = new THREE.Mesh (geometry, material);
scene.add (cube);
I want to know, where this behaviour comes from and how I can manage to make polyhedrons' faces behave as nice as boxs?
I read that it might be related to geometry.computeFaceNormals().
So I tried it out, but it doesn't make any difference.
when something is different with how light behaves on a surface, first candidates to look at are normals
this is true for the box face
boxGeometry.faces[i].normal.equals(boxGeometry.faces[i].vertexNormals[j]);//true
so box has only simple normal for each face
the polyhedron has different face normal from the vertex normals
polyhedronGeo.faces[i].normal.equals(polyhedronGeo.faces[i].vertexNormals[j]);//not true
and some of the vertex normals are not equal among each other
polyhedronGeo.faces[i].vertexNormals[j].equals(polyhedronGeo.faces[i].vertexNormals[k]);
//not true for some j,k
that is why the light looks ~shadowy - normal is interpolated for the shader from vertexNormals
to modify the polyhedron to look like box just change the vertex notmals to match the face normal
as for
geometry.computeFaceNormals();
it will only compute the face normals, not the vertexNormals
there is another function
geometry.computeVertexNormals();
but that would create vertex normals as are in polyhedron
Thanks Derte. Your reflection got me closer to the point. So with advanced keywords I found this: https://github.com/mrdoob/three.js/issues/1982
The answer to my question is this line, flattening shading for "free forms":
material.shading = THREE.FlatShading;

How to add a texture to one side of a cube without THREE.MultiMaterial to reduce draw calls

So lets say theres a cube with 2 materials.I'm using MultiMaterial but maybe thats not the correct approach because its showing 6 draw calls instead of 2. I'm worried about performance when it scales up.
http://codepen.io/glued/pen/JXmvzm
This is just an example, I know about FaceColors but would like to mix a meshBasicMaterial with another Material, say, with a texture.
var greenMaterial = new THREE.MeshBasicMaterial({ color: 0xc4f288 })
var orangeMaterial = new THREE.MeshBasicMaterial({ color: 0xf4511e })
var mats = [
orangeMaterial,
greenMaterial,
orangeMaterial,
orangeMaterial,
greenMaterial,
orangeMaterial
]
let box = new THREE.Mesh(geometry, new THREE.MultiMaterial( mats ))
If i used vertexColors: FaceColors and a texture:
new MeshBasicMaterial({ vertexColors: FaceColors, map:someTexture }))
how would i designate the texture for a specific face only?
I figured it out by creating a material with a texture and removing the UVs on the geometry faces that i'm not using
the texture is 128x256, See the codepen as i'm using a 2d canvas to generate
texture.repeat.y = 0.5
texture.offset.y = 0.5
let geometry = new THREE.BoxGeometry(50, 50, 50)
function assignUvAndColor(geo, i, color = 0x00cbff){
geo.faceVertexUvs[0][i] = new Array(3).fill(new THREE.Vector2(0, -1))
geo.faces[i].color.setHex(color)
}
const greenColor = 0xacffd3
assignUvAndColor(geometry, 3, greenColor)
assignUvAndColor(geometry, 2, greenColor)
assignUvAndColor(geometry, 0, greenColor)
assignUvAndColor(geometry, 1, greenColor)
assignUvAndColor(geometry, 4)
assignUvAndColor(geometry, 5)
assignUvAndColor(geometry, 6)
assignUvAndColor(geometry, 7)
let material = new THREE.MeshBasicMaterial({ map: texture, vertexColors: THREE.FaceColors })
let box = new THREE.Mesh(geometry, material)
http://codepen.io/glued/pen/grBEmo?editors=0010
multimaterial will always do N drawcalls where N = length of its material array
(see renderer implementation)
it does not even try to check whether some of its materials are duplicite in reference - so in your examples you have multimaterial with 6 materials = 6 drawcalls
you will have to change the geometry face material index or abandon using multimaterial and divide your geometry manually

Merge two planes with texture into one mesh

I want to merge two meshes in threejs. I want to create a geometry of two planes that are intersecting each other perpendicular. Both of the planes must have the same texture.
I've tried the following.
Currently this error occurs: THREE.Geometry.merge(): geometry not an instance of THREE.Geometry.
var texture = THREE.ImageUtils.loadTexture('/img/foo.png');
var material = new THREE.MeshPhongMaterial( { map: texture, side: THREE.DoubleSide, transparent: true });
var m_plane_1 = new THREE.PlaneGeometry( 128, 128);
var m_plane_2 = new THREE.PlaneGeometry(128, 128);
var plane_1 = new THREE.Mesh(m_plane_1, material);
var plane_2 = new THREE.Mesh(m_plane_2, material);
plane_2.rotation.y = Math.PI / 2;
var combined = new THREE.Geometry();
combined.merge(plane_1); // does not work
//combined.merge(plane_1.geometry, plane_1.matrix); // this does not work
//combined.merge(m_plane_1.geometry, m_plane_1.matrix); // this does not work
scene.add(combined);
I've tried to read the source code for merge() but could not come to any conclusion. I've read stackoverflow threads but their approach does not work for me.
How can I solve this?
You want to get the intersection of the geometries. Then you can apply whatever texture you want to that new geometry.
To get the intersection, you should use Chandler Prall's Constructive Solid Geometry code: http://evanw.github.io/csg.js/
There are several ways to merge two geometries, but probably the simplest way to achieve what you want is to use the following pattern:
var geometry = new THREE.PlaneGeometry( 128, 128 );
var geometry2 = new THREE.PlaneGeometry( 128, 128 );
geometry2.applyMatrix( new THREE.Matrix4().makeRotationY( Math.PI / 2 ) );
geometry.merge( geometry2 );
three.js r.69
Try this:
plane_1.updateMatrix();
//Now the function merge
combined.merge(plane_1.geometry, plane_1.matrix);
//combined.merge(plane_1); // does not work
//combined.merge(plane_1.geometry, plane_1.matrix); // this does not work
//combined.merge(m_plane_1.geometry, m_plane_1.matrix); // this does not work

Resources