ShapePath auto closing - three.js

I'm having an issue with ShapePath where the shape is automatically being closed when converting to geometry?
var path = new THREE.ShapePath();
path.moveTo(0, 0);
path.lineTo(0,50);
path.lineTo(50,50);
var shapes = path.toShapes(true);
for ( var j = 0; j < shapes.length; j ++ ) {
var shape = shapes[ j ];
console.log(shape);
var material = new THREE.MeshLambertMaterial( {
color: Math.random() * 0xffffff
});
var geometry = new THREE.ShapeBufferGeometry( shape );
var mesh = new THREE.Mesh( geometry, material );
scene.add(mesh);
}
JS Fiddle
What I'm expecting to have is a L style shape, instead i'm getting a triangle being rendered.
I really just want to extrude the path to a solid L shape.
Any ideas on the best approach?

You can draw your intended result if you create an enclosing contour of the L shape like shown in the following example: https://jsfiddle.net/zbeLk6jw/1/
var path = new THREE.ShapePath();
path.moveTo(0, 0);
path.lineTo(0,50);
path.lineTo(10,50);
path.lineTo(10,10);
path.lineTo(25,10);
path.lineTo(25,0);

Related

THREE.js Many instances of the same simple mesh killing framerate

I have a basic scene in which I'm using each loops to add multiple meshes ( hundreds of simple cylinders ) to a group (for each line), and grouping the lines to cover the surfaces. The result is this:
This is the code to add these cylinders:
var base_material = new THREE.MeshPhongMaterial( {
color: 0x666666,
side: THREE.FrontSide,
});
var cylinderGeometry = new THREE.CylinderGeometry( 1, 1, 1, 4 );
var floor_geometry = new THREE.PlaneGeometry( 68, 10000, 10 );
var floor = new THREE.Mesh( floor_geometry, base_material );
floor.receiveShadow = true;
scene.add( floor );
floor.position.set(0,-15,-530);
floor.rotation.x = -Math.PI / 2;
// Add Floor Studs
for ( var i = 0; i < 15; i++ ) {
var lineGroup = new THREE.Group();
for ( var n = 0; n < 1000; n++ ) {
var cylinder = new THREE.Mesh( cylinderGeometry, base_material );
// cylinder.castShadow = true;
// cylinder.receiveShadow = true;
lineGroup.add( cylinder );
posZ = 0 - n*6;
cylinder.position.set(0,0, posZ);
}
scene.add( lineGroup );
posX = -28.4 + i*4.1;
lineGroup.position.set(posX,-14.7,0);
}
When I animate the camera to traverse through the scene the framerate is dire. Potential approaches I've come across include merging the geometry, possibly rendering out and loading in a single GLTF model with all of these cylinders, or duplicating them somehow. As you can see the geometry and material is created once and reused, however the mesh is recreated each time which I suspect is the culprit..
My question is, what is the most optimum of these approaches to do this, is there a standard best practice method?
Thanks in advance!

THREE.js planeGeometry clipped inside the walls

I want to make a 3D building using Three.js. For example, I made 6 walls and a floor by checkerboard texture. I used clippingPlanes for wall1 and wall4:
floor1.material.clippingPlanes = [plane1,plane4];
I made my planes(plane1 and plane4) by my walls(wall1 and wall4). For example, my wall4 planeGeometry and plane4 code is here:
var wallGeometry4 = new THREE.PlaneGeometry(40, Floor_Height, 1, 1);
var wall4 = createMesh(wallGeometry4, "brick_diffuse.jpg", THREE.DoubleSide, 1024, 1024);
unit1.add(wall4);
wall4.position.x = -10;
wall4.position.y = 0;
wall4.position.z = -20;
wall4.rotation.y = 1.5 * Math.PI;
wall4.add(new THREE.EdgesHelper(wall4, 0x000000));
var plane4 = new THREE.Plane();
var normal4 = new THREE.Vector3();
var point4 = new THREE.Vector3();
normal4.set(0, 0, -1).applyQuaternion(wall4.quaternion);
point4.copy(wall4.position);
plane4.setFromNormalAndCoplanarPoint(normal4, point4);
But I see an empty area between wall5 and wall6, because plane4(that used for clipping the floor) isn't the same size of wall4. I think Plane4 is whole of the scene. How to change size of my plane to clip correctly? Or Is there any way to make floor bounded in walls?
One way to achieve this as suggested is to use ShapeGeometry.
When you are creating your walls you can save the x and z co-ordinate of their starting and ending points in an array to form a loop of points of Vector2. Then you can create a new custom shape from these points using shapeGeometry.
points = [{x:0,y:0},{x:0,y:10},{x:10,y:10},{x:10,y:0},{x:0,y:0}]
function getShapeFromPoints(points){
const shape = new THREE.Shape();
shape.moveTo(points[0].x, points[0].y);
for (let i = 1; i < points.length; i++) {
shape.lineTo(points[i].x, points[i].y);
}
return shape;
}
function createPlaneFromPoints(points) {
const planeMaterial = getPlaneMaterial();
const shape = getShapeFromPoints(points);
const geometry = new THREE.ShapeBufferGeometry(shape);
geometry.rotateX(degreeToRadians(-90));
const mesh = new THREE.Mesh(geometry, planeMaterial);
return mesh;
}
Hope that helps you!

Merge geometries, but use a single material

Somewhat new to Three.js and 3d libraries in general.
I merged two geometries (a quarter cylinder and a plane) using this code:
var planeGeo = new THREE.PlaneGeometry(planeW, planeD / 2, 199, 399);
var planeMesh = new THREE.Mesh(planeGeo);
planeMesh.updateMatrix();
var cylinderGeo = new THREE.CylinderGeometry(100, 100, planeW, 199, 399, true, 0, Math.PI / 2);
cylinderGeo.rotateZ(Math.PI / 2).translate(0, 200, -100);
var cylinderMesh = new THREE.Mesh(cylinderGeo);
cylinderMesh.updateMatrix();
var singleGeometry = new THREE.Geometry();
singleGeometry.merge(planeMesh.geometry, planeMesh.matrix);
singleGeometry.merge(cylinderMesh.geometry, cylinderMesh.matrix);
var testmaterial = new THREE.MeshPhongMaterial({ color: 0x666666 });
mesh = new THREE.Mesh(singleGeometry, testmaterial);
scene.add(mesh);
I then would like to use a single material (png) over the entire thing. This code doesn't work:
textureLoader.load('data/test.png', function (texture) {
material = new THREE.MeshLambertMaterial({
map: texture
});
});
Later in the block with the merging...
mesh = new THREE.Mesh(singleGeometry, material);
scene.add(mesh);
This results in:
I would like the end result to be a single draped png over the entire merged geometry, but I can't find anything that suggests this is a normal thing to do. Is there a better way to achieve that result than merging geometries? Or am I just looking in the wrong places?
A poor-mans solution to achieve this, using the shape supplied in your post, is the following:
https://jsfiddle.net/87wg5z27/44/
Using code from this answer: https://stackoverflow.com/a/20774922/4977165
It sets the UVs based on the bounding box of the geometry, leaving out the z-coordinate (=0). Thats why the texture is a little bit stretched at the top, you can correct that manually or maybe its sufficent for you.
geometry.computeBoundingBox();
var max = geometry.boundingBox.max,
min = geometry.boundingBox.min;
var offset = new THREE.Vector2(0 - min.x, 0 - min.y);
var range = new THREE.Vector2(max.x - min.x, max.y - min.y);
var faces = geometry.faces;
geometry.faceVertexUvs[0] = [];
for (var i = 0; i < faces.length ; i++) {
var v1 = geometry.vertices[faces[i].a],
v2 = geometry.vertices[faces[i].b],
v3 = geometry.vertices[faces[i].c];
geometry.faceVertexUvs[0].push([
new THREE.Vector2((v1.x + offset.x)/range.x ,(v1.y + offset.y)/range.y),
new THREE.Vector2((v2.x + offset.x)/range.x ,(v2.y + offset.y)/range.y),
new THREE.Vector2((v3.x + offset.x)/range.x ,(v3.y + offset.y)/range.y)
]);
}
geometry.uvsNeedUpdate = true;

Threejs | How to tween Radius (RingGeometry)

How can I tween the innerRadius attribute of THREE.RingGeometry() in three.js using tween.js. I don't want to scale the ring, I want to update geometry.
You will need to look at morphing the vertices, This website has great examples for different situations:
https://stemkoski.github.io/Three.js/Graphulus-Surface.html
https://stemkoski.github.io/Three.js/
Have look through the morphing samples aswell..
May be an answer if it can give idea to help.
1 - Give a name to the ring,
2 - Create a function to find, remove and redraw the ring
3 - and with Tween.js or setInterval use the function to animate.
Something like :
var rStart = 100;
var rStep = 10;
var ep = 50;
//create circle
var geometry = new THREE.RingGeometry( rStart, rStart + ep, 32,3,0, Math.PI * 2 );
var material = new THREE.MeshBasicMaterial( { color: 0xff0000, side: THREE.DoubleSide } );
var ring = new THREE.Mesh( geometry, material );
ring.name = 'the_ring';
scene.add( ring );
// function to find ring, remove and redraw
function grow(i,rStart,rStep,ep){
var ringToRemove = 'the_ring';
var ringToRemoveSelected = scene.getObjectByName(ringToRemove);
scene.remove(ringToRemoveSelected);
var newRadius = rStart + ( rStep * i);
var geometry = new THREE.RingGeometry( newRadius , newRadius + ep , 32,3,0, Math.PI * 2);
var material = new THREE.MeshBasicMaterial( { color: 0xff0000, side: THREE.DoubleSide } );
var ring = new THREE.Mesh( geometry, material );
ring.name = 'the_ring';
scene.add( ring );
}
//and animate
var i = 0;
setInterval(function () {
i++;
if(i < 100){
grow(i,rStart,rStep,ep);
}
}, 100);

THREE.JS UV Mapping on ExtrudeGeometry

I need to apply a texture on a ExtrudeGeometry object.
The shape is a circle and the extrude path is composed of 2 vectors :
One for the top.
One for the bottom.
I didn't choose cylinderGeometry because I need to place top/bottom sections of my geometry at precise positions and because the geometry created will not be always purely vertical (like a oblique cylinder for example).
Here is a picture of a section (one top vector, one bottom vector and a shape extruded between these 2 vectors).
and a picture of the texture I'm trying to apply.
All I want to do is to wrap this picture on the vertical sides of my object just one time.
Here is my code :
var biVectors = [ new THREE.Vector3( this.startVector.x, this.startVector.y, this.startVector.z ) , new THREE.Vector3( this.endVector.x, this.endVector.y, this.endVector.z ) ];
var wellSpline = new THREE.SplineCurve3(biVectors);
var extrudeSettings = {
steps : 1,
material: 0,
extrudeMaterial: 1,
extrudePath : wellSpline
};
var pts = [];
for (var i = 0; i <= this.segments; i++) {
var theta = (i / this.segments) * Math.PI * 2;
pts.push( new THREE.Vector3(Math.cos(theta) * this.diameter , Math.sin(theta) * this.diameter, 0) );
}
var shape = new THREE.Shape( pts );
var geometry = new THREE.ExtrudeGeometry( shape, extrudeSettings );
var texture = THREE.ImageUtils.loadTexture( 'textures/sampleTexture2.jpg' );
texture.wrapS = texture.wrapT = THREE.RepeatWrapping;
texture.flipY = false;
var material = new THREE.MeshBasicMaterial( { map: texture } );
var slice = new THREE.Mesh( geometry, material );
var faceNormals = new THREE.FaceNormalsHelper( slice );
console.log("face normals: ", faceNormals);
myCanvas.scene.add( faceNormals );
slice.parentObject = this;
myCanvas.scene.add( slice );
this.object3D = slice;
}
Now, as you can see, the mapping is not correct at all.
I've read a lot of information about this problem the last 3 days. But I'm running out of options as I'm new to THREE.JS.
I think I have to redefine the UV coordinates but I have no clue how to do this.
It seems that wrapping a texture on a cylinder like object is anything but easy in THREE.JS.
Can someone please help me on this issue ?

Resources