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: