End Caps of TubeGeometry

Hi @hofk,

thank you so much for your guidance and resources! I can see how these examples and skills will be very valuable for my current project. For the moment, it was quicker for me to come up with a solution based on predefined basic geometries, for which this example: Tube from 3d points helped me get the end caps rotated/aligned correctly .

In the end, this is what I came up with:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Three.js Example</title>
    <style>
        body {
            margin: 0;
        }
    </style>
    <script async src="https://unpkg.com/es-module-shims@1.6.3/dist/es-module-shims.js"></script>
    <script type="importmap">
          {
            "imports": {
              "three": "https://unpkg.com/three@v0.160.0/build/three.module.js",
              "three/addons/": "https://unpkg.com/three@v0.160.0/examples/jsm/"
            }
          }
        </script>
</head>
<body>
    <script type="module">
        import * as THREE from 'three';
        import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
        import * as BufferGeometryUtils from 'three/addons/utils/BufferGeometryUtils.js';

        const scene = new THREE.Scene();
        scene.background = new THREE.Color(0x87CEEB);
        const camera = new THREE.PerspectiveCamera(70, window.innerWidth / window.innerHeight, 0.1, 1000);
        camera.position.set(0, 12, 12);
        scene.add(camera);
        const grid = new THREE.GridHelper(20, 20);
        scene.add(grid);
        scene.add(new THREE.DirectionalLight(0xffffff, 0.5));
        const renderer = new THREE.WebGLRenderer();
        renderer.setSize(window.innerWidth, window.innerHeight);
        document.body.appendChild(renderer.domElement);
        const controls = new OrbitControls(camera, renderer.domElement);

        const tubeCoordinates = [new THREE.Vector3(0, 0, 0),
        new THREE.Vector3(0, 1, 0),
        new THREE.Vector3(6, 0, 0),
        new THREE.Vector3(6, 0, 4),
        new THREE.Vector3(3, 0, 4),
        new THREE.Vector3(2, 3, 4)
        ];
        var tubeRadius = 0.5;
        var tubeColor = 0xC1C1C1;

        scene.add(new CappedTube(tubeCoordinates, tubeRadius, tubeColor));

        function CappedTube(coordinates, radius = 1, color = 0xC1C1C1) {
            const curve = new THREE.CatmullRomCurve3(coordinates);
            const tubeGeometries = [];

            const tube = new THREE.TubeGeometry(curve, 64, radius, 64);
            tubeGeometries.push(tube);

            const capA = new THREE.CircleGeometry(radius, 48)
                    .applyMatrix4(
                        new THREE.Matrix4()
                            .setPosition(curve.getPoint(0))
                            .lookAt(
                                curve.getPoint(0),
                                new THREE.Vector3(...(coordinates[1])),
                                new THREE.Vector3(1, 0, 0)
                            )
                    );
            tubeGeometries.push(capA);

            const capB = new THREE.CircleGeometry(radius, 48)
                    .applyMatrix4(
                        new THREE.Matrix4()
                            .setPosition(curve.getPoint(1))
                            .lookAt(
                                curve.getPoint(1),
                                new THREE.Vector3(...(coordinates[coordinates.length - 2])),
                                new THREE.Vector3(1, 0, 0)
                            )
                    );
            tubeGeometries.push(capB);

            const tubeMaterial = new THREE.MeshLambertMaterial({ color: color }); 
            const mergedTube = new THREE.Mesh(BufferGeometryUtils.mergeGeometries(tubeGeometries), tubeMaterial);
            return mergedTube;
        }

        function animate() {
            requestAnimationFrame(animate);
            controls.update();
            renderer.render(scene, camera);
        }

        animate();
    </script>
</body>
</html>

It is not perfect since there are some minimal gaps/space between end caps and tube, but for the moment it serves my purpose: