0

I am trying to create a tee-piece which is a fitting in the plumbing domain. It consist of 2 tubes that are merged together and has 3 openings as shown in this picture.

I have written some code in threejs where I am trying to create a tube mesh1 and another tube mesh2 and then try to union them into mesh3 with the library @enable3d/three-graphics/jsm/csg - thanks to @Marquizzo. After using the function CSG.union and adding the mesh to the scene I can see that I get one tee-piece but it has also created a hole in geometry 1, which was not expected. You can see a picture of the correct holes(green) and the wrongly created hole (red) here:

this

it should instead look like this and be as one geometry.

enter image description here

Can anyone tell me how CSG works and why I am getting an extra hole on the backside of the first geometry?

import React, { Component } from 'react';
import * as THREE from 'three';
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls';
import { CSG } from '@enable3d/three-graphics/jsm/csg';

export default class TubeViewer extends Component {
    componentDidMount() {
        //Add Scene
        this.scene = new THREE.Scene();

        //Add Renderer
        this.renderer = new THREE.WebGLRenderer({ antialias: true });
        this.renderer.setClearColor('#808080');
        this.renderer.shadowMap.enabled = true;
        this.renderer.shadowMap.type = THREE.PCFSoftShadowMap;
        this.renderer.setPixelRatio(window.devicePixelRatio);
        this.renderer.setSize(window.innerWidth, window.innerHeight);
        this.mount.appendChild(this.renderer.domElement);

        //Add Camera
        const fov = 60;
        const aspect = window.innerWidth / window.innerHeight;
        const near = 1.0;
        const far = 1000.0;
        this.camera = new THREE.PerspectiveCamera(fov, aspect, near, far);
        this.camera.position.set(1, aspect, 1, 1000);


        //Tee-piece

        const curve1 = new THREE.LineCurve(new THREE.Vector3(2, 0, 0), new THREE.Vector3(2, 0, 0.1));
        const curve11 = new THREE.LineCurve(new THREE.Vector3(2.0, 0, 0.05), new THREE.Vector3(2.05, 0, 0.05));

        const geometry1 = new THREE.TubeGeometry(curve1, 20, 0.025, 8, false);
        const geometry2 = new THREE.TubeGeometry(curve2, 20, 0.025, 8, false);

        const material = new THREE.MeshBasicMaterial({ color: '#C0C0C0' });

        const mesh1 = new THREE.Mesh(geometry1, material);
        const mesh2 = new THREE.Mesh(geometry2, material);

        const mesh3 = CSG.union(mesh1, mesh2);

        this.scene.add(mesh3);


        //Add raycaster to for interactivity
        this.raycaster = new THREE.Raycaster();
        this.mouse = new THREE.Vector2();

        this.renderer.domElement.addEventListener('click', onClick.bind(this), false);

        function onClick(event) {
            event.preventDefault();

            this.mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
            this.mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;

            this.raycaster.setFromCamera(this.mouse, this.camera);

            var intersects = this.raycaster.intersectObjects(this.scene.children, true);

            if (intersects.length > 0) {
                console.log('Intersection:', intersects[0]);
                //console.log(intersects[0].object.uuid);
                // console.log(`GUID: ${intersects[0]}`);
                let object = intersects[0].object;

                object.material.color.set(Math.random() * 0xffffff);
            }
        }

        //Settings
        //Add Camera Controls
        const controls = new OrbitControls(this.camera, this.renderer.domElement);
        controls.addEventListener('change', this.render); // use if there is no animation loop
        controls.minDistance = 2;
        controls.maxDistance = 10;
        controls.target.set(0, 0, -0.2);
        controls.update();

        ///Add AMBIENT LIGHT
        let light = new THREE.DirectionalLight(0xffffff, 1.0);
        light.position.set(20, 100, 10);
        light.target.position.set(0, 0, 0);
        light.castShadow = true;
        light.shadow.bias = -0.001;
        light.shadow.mapSize.width = 2048;
        light.shadow.mapSize.height = 2048;
        light.shadow.camera.near = 0.1;
        light.shadow.camera.far = 500.0;
        light.shadow.camera.near = 0.5;
        light.shadow.camera.far = 500.0;
        light.shadow.camera.left = 100;
        light.shadow.camera.right = -100;
        light.shadow.camera.top = 100;
        light.shadow.camera.bottom = -100;
        this.scene.add(light);
        light = new THREE.AmbientLight(0xffffff, 0.7);
        this.scene.add(light);

        //Start animation
        this.start();
    }

    //Unmount when animation has stopped
    componentWillUnmount() {
        this.stop();
        this.mount.removeChild(this.renderer.domElement);
    }

    //Function to start animation
    start = () => {
        //Rotate Models
        if (!this.frameId) {
            this.frameId = requestAnimationFrame(this.animate);
        }
    };

    //Function to stop animation
    stop = () => {
        cancelAnimationFrame(this.frameId);
    };

    //Animate models here
    animate = () => {
        //ReDraw scene with camera and scene object
        if (this.cubeMesh) this.cubeMesh.rotation.y += 0.01;
        this.renderScene();
        this.frameId = window.requestAnimationFrame(this.animate);
    };

    //Render the scene
    renderScene = () => {
        if (this.renderer) this.renderer.render(this.scene, this.camera);
    };

    render() {
        return (
            <div
                style={{ width: '800px', height: '800px' }}
                ref={(mount) => {
                    this.mount = mount;
                }}
            />
        );
    }
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>

enter code here?

Ali91
  • 25
  • 5
  • 1
    I think what you're looking for is a CSG (Constructive Solid Geometry ) function, which performs sort of a boolean operation to add/subtract/multiply 2 or more geometries in 3D space. If you're using Three.js revision 124 or earlier, you could use [this tool](https://evanw.github.io/csg.js/), but if you're using revision 125 or later, you'll need to use [this newer tool](https://discourse.threejs.org/t/csg-with-buffergeometry-three-r125/23735) – M - Feb 24 '21 at 02:05
  • Hey @Marquizzo I did what you said but I got a hole on bot sides which should not happen. Something is going wrong. I can't figure out the reason. – Ali91 Feb 24 '21 at 09:32
  • This post is still unanswered. I therefore hope that someone will give a hint or help to solve the problem. – Ali91 Mar 04 '21 at 09:51

1 Answers1

0

For CSG you'll need solid bodies. These tubes are open.

I created an example using cylinders (tubes are involved to cap) so you can test it.

These cylinders are open ended, so they fail in the same way as your tubes. https://codepen.io/flatworldstudio/pen/bGBjmrP

 const geometry1 = new THREE.CylinderGeometry(0.1, 0.1, 0.5, 20, 1, true);

These are closed, and CSG works as expected. https://codepen.io/flatworldstudio/pen/VwmBRoL

const geometry1 = new THREE.CylinderGeometry(0.1, 0.1, 0.5, 20, 1, false);

(I'm using a different version of CSG, but they all seem to be built on the same code)