24

Can I bind two different shapes together as one shape?

For example, binding sphere and cylinder together as one?

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
BorisD
  • 2,231
  • 5
  • 25
  • 35

1 Answers1

49

Kind of, yes. There are multiple options:

  1. via hierarchy you can simply add one mesh to another using the add() function
  2. via the GeometryUtil's merge() function to merge vertices and meshes of two Geometry objects into one
  3. using a basic 3D editor that supports Boolean operations between meshes and exporting.

Method 1 is pretty straightforward:

var sphere = new THREE.Mesh(new THREE.SphereGeometry(100, 16, 12), new THREE.MeshLambertMaterial({ color: 0x2D303D, wireframe: true, shading: THREE.FlatShading }));

var cylinder = new THREE.Mesh(new THREE.CylinderGeometry(100, 100, 200, 16, 4, false), new THREE.MeshLambertMaterial({ color: 0x2D303D, wireframe: true, shading: THREE.FlatShading } ));
cylinder.position.y = -100;
scene.add(sphere);
scene.add(cylinder);

Notice that 16 is repeated, so the subdivisions level in one mesh matches the other (for a decent look).

Method 2.1 - via GeometryUtils

// Make a sphere

var sg = new THREE.SphereGeometry(100, 16, 12);
// Make a cylinder - ideally the segmentation would be similar to predictable results
var cg = new THREE.CylinderGeometry(100, 100, 200, 16, 4, false);
// Move vertices down for cylinder, so it maches half the sphere - offset pivot
for(var i = 0 ; i < cg.vertices.length; i++)
    cg.vertices[i].position.y -= 100;

// Merge meshes
THREE.GeometryUtils.merge(sg, cg);
var mesh = new THREE.Mesh(sg, new THREE.MeshLambertMaterial({ color: 0x2D303D, wireframe: true, shading: THREE.FlatShading }));
scene.add(mesh);

Method 2.2 merging a Lathe half-sphere and a cylinder:

var pts = []; // Points array

var detail = .1; // Half-circle detail - how many angle increments will be used to generate points
var radius = 100; // Radius for half_sphere
var total = Math.PI * .51;
for(var angle = 0.0; angle < total ; angle+= detail) // Loop from 0.0 radians to PI (0 - 180 degrees)
    pts.push(new THREE.Vector3(0,Math.cos(angle) * radius,Math.sin(angle) * radius)); // Angle/radius to x,z

var lathe = new THREE.LatheGeometry(pts, 16); // Create the lathe with 12 radial repetitions of the profile

// Rotate vertices in lathe geometry by 90 degrees
var rx90 = new THREE.Matrix4();
rx90.setRotationFromEuler(new THREE.Vector3(-Math.PI * .5, 0, 0));
lathe.applyMatrix(rx90);

// Make cylinder - ideally the segmentation would be similar for predictable results
var cg = new THREE.CylinderGeometry(100, 100, 200, 16, 4, false);

// Move vertices down for cylinder, so it maches half the sphere
for(var i = 0 ; i < cg.vertices.length; i++)
    cg.vertices[i].position.y -= 100;

// Merge meshes
THREE.GeometryUtils.merge(lathe, cg);
var mesh = new THREE.Mesh(lathe, new THREE.MeshLambertMaterial({ color: 0x2D303D, wireframe: true, shading: THREE.FlatShading}));
mesh.position.y = 150;
scene.add(mesh);

The one problem I can't address at the moment comes from the faces that are inside the mesh. Ideally, those would have normals flipped, so they wouldn't render, but I haven't found a quick solution for that.

The third is fairly straightforward. Most 3D packages allow Boolean operation on meshes (e.g., merging two meshes together with the ADD operation (meshA + meshB)). Try creating a cylinder and a sphere in Blender (free and opensource), which already has a Three.js exporter. Alternatively you can export an .obj file of the merged meshes from your 3D editor or choice and use the convert_obj_three script.

I've found yet another method, which might be easier/more intuitive. Remember the Boolean operations I've mentioned above?

It turns out there is an awesome JavaScript library just for that: Constructive Solid Geometry:

CSG library preview from the owner's GitHub page

Chandler Prall wrote some handy functions to connect CSG with three.js. So with the CSG library and the Three.js wrapper for it, you can simply do this:

var cylinder = THREE.CSG.toCSG(new THREE.CylinderGeometry(100, 100, 200, 16, 4, false), new THREE.Vector3(0, -100, 0));
var sphere   = THREE.CSG.toCSG(new THREE.SphereGeometry(100, 16, 12));
var geometry = cylinder.union(sphere);
var mesh     = new THREE.Mesh(THREE.CSG.fromCSG(geometry), new THREE.MeshNormalMaterial());

Which gives you a nice result (no problems with extra faces/flipping normals, etc.):

Cylinder-sphere union with CSG and Three.js

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
George Profenza
  • 50,687
  • 19
  • 144
  • 218
  • I'm trying all of those thing,thank for the answers! I asked this question since I'm trying to create 3D flower with number of shapes and textures. My purpose to bind the shapes,so I can rotate and drag the whole flower shapes and not every shape together as one shape – BorisD Dec 01 '11 at 08:32
  • Sounds a bit like [Hello Enjoy's HelloFlower app](http://helloenjoy.com/helloflower/). In this case I would say, have an empty 'container' Object3D at the top, and then `add()` the petals, pistil, etc. to that so you can manipulate the group together. Something like the 1st suggestion, but instead of adding straight to the scene, adding to a container( `scene.add(container);container.add(flowerPartA);container.add(flowerPartB);//etc.` ) – George Profenza Dec 01 '11 at 20:35
  • Wow! it's great example man...not exactly what I'm trying to build but there are some similar things.For the last couple of days I had a good progress. I hope that my final example that I will post,will help to beginner in Three.js to solve some small and bigger issues. But still there's some small things I'm still not familiar with...How can I create something like cylinder shape that starts in point X and ends in a point Y...for now the only thing I can figure out is just make a cylinder and rotate it? – BorisD Dec 04 '11 at 09:31
  • @BorisD Yes,rotating is the right approach !You need to work out the rotation, but that's easy since **Object3D** has a `lookAt()` function and so does **Matrix4** which does most of the math for you. There is a catch though: the default pivot for a cylinder is at the centre of the mesh, so you might need to also do some offsetting because of that. I spotted [this](http://stackoverflow.com/questions/8375288/three-js-cylinder-from-point-to-point) a few days back. Try to use the above mentioned. If you run into issues,try to reformulate your question and based on your trials then post again. HTH – George Profenza Dec 05 '11 at 16:37
  • That's exactly what I'm looking for, but unfortunately, it does not work with Three.js r50 :( Do you know if the three.js wrapper around CSG has been updated ? – Fabien Quatravaux Sep 14 '12 at 15:40
  • 1
    I updated a wrapper for Three.js r62. You can find it at *[csg-wrapper](https://github.com/kraag22/csg-wrapper)*. – kraag22 Nov 14 '13 at 15:33