Search code examples
three.jsreact-three-fiber

How to merge geometries and keep one color per geometry in R3F


The idea would be to merge several geometries and keep the color for each geometry. An example with 3 merges polygones would be the following.

Goal


Solution

  • Possible solution

    • Set the color attribute for each geometry before merging them.
    • Use mesh with the merged geometry as a parameter. Give a material as a child and activate the vertexColors properties.

    EXAMPLE

    Let's say we want to merge 3 polygons with one color per polygone:

    // Polygones to display
    const zones = [{
      name:'zone1',
      coords:[[-2,0],[-2,2],[-0.4,1], [-0.8,-1.5]],
      color: 0xff0000
    },
    {
      name:'zone2',
      coords:[[-6,0],[-6,2],[-6.4,1], [-6.8,-1.5]],
      color: 0x0000ff
    },
    {
      name:'zone3',
      coords:[[4,0],[4,2],[5.6,1], [6.8,-1.5]],
      color: 0x0000ff
    },
    ]
    
    • Creating the merged geometry with the attributes would look like this:
    let geometries = [];
    zones.forEach((zone,iZone) => {
      const shape = new THREE.Shape();
      const coords = zone.coords;
      const color = new THREE.Color(zone.color);
      const startPoint = coords[0];
      // Draw the shape
      shape.moveTo(startPoint[0],startPoint[1]);
      for (let i=1;i<coords.length; i++){
        shape.lineTo(coords[i][0], coords[i][1]);
      }
      // Create the geometry thanks to the shape
      const geom = new THREE.ShapeGeometry(shape);
      // Create an attributes (same length that the indices) that will store the color state
      const indicesLength = geom.attributes.position.count;
      const aColor = new Float32Array(indicesLength * 3);
      for (let k = 0; k < aColor.length; k+=3){
        aColor[k] = color.r;
        aColor[k+1] = color.g;
        aColor[k+2] = color.b;
      }
      geom.setAttribute('color', new THREE.BufferAttribute(aColor, 3));
      geometries.push(geom);
    })
    const newMerged = BufferGeometryUtils.mergeGeometries(geometries)
    
    • And in your jsx you would have the following:
    <mesh geometry={merged}>
      {/* activate vertex color */}
      <meshBasicMaterial vertexColors/>
    </mesh>
    

    An example of the implementation can be found here: Codesandbox