Alterative to merging geometries in Three.js

1k views Asked by At

I'm trying to create a randomized terrain with a square base, which will be exported as an .STL (and 3d printed).

I'm having difficulties combining each seperate geometry into a single mesh. Ostensibly, my shape looks fine, however when I try to 3d Print the .STL, it's clear that there are some gaps in each individual geometry after they are merged.

Currently, I first create the top geometry and then the left, right, front, back, and bottom and finally merge these geometries into a single mesh.

My code right now is:

<!doctype html>
<html>
    <head>
        <meta charset="utf-8" />
        <title>STL</title>
        <style>
            #container {
                background: #000;
                width: 800px;
                height: 600px;
            }
        </style>
    </head>
    <body>
        <input type="button" id="export" name="export" value="Export" />    
        <div id="container">


        </div>

    </body>

    <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.5.1/jquery.min.js"></script>
    <script src="js/three.min.js"></script>
    <script src="js/loaders/STLLoader.js"></script>
    <script src="js/exporters/STLExporter.js"></script>
    <script src="js/Detector.js"></script>
    <script src="js/libs/stats.min.js"></script>
    <script src="js/ImprovedNoise.js"></script>
    <script src="js/blob/Blob.js"></script>
    <script src="js/filesaver/FileSaver.js"></script>
    <script src="js/controls/FirstPersonControls.js"></script>

    <script>

    $(document).ready(function() {

        var mouseX, mouseY = 0;
        var WIDTH = 800,
            HEIGHT = 600;

        var VIEW_ANGLE = 45,
            ASPECT = WIDTH / HEIGHT,
            NEAR = 0.1,
            FAR = 10000;

        var container = $('#container');

        var camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 1000);
        camera.position.z = 700;

        var controls = new THREE.FirstPersonControls( camera );
        controls.movementSpeed = 1000;
        controls.lookSpeed = 0.1;



        var exporter = new THREE.STLExporter();

        var renderer = new THREE.WebGLRenderer();
        var camera = new THREE.PerspectiveCamera(  VIEW_ANGLE,
                                        ASPECT,
                                        NEAR,
                                        FAR  );
        var scene = new THREE.Scene();

        camera.position.z = 300;

        renderer.setSize(WIDTH, HEIGHT);
        var clock = new THREE.Clock();
        animate();
        container.append(renderer.domElement);

        var sphereMaterial = new THREE.MeshLambertMaterial(
        {
            color: 0xCC0000
        });

        var worldWidth = 100, worldDepth = 100,
        worldHalfWidth = worldWidth / 2, worldHalfDepth = worldDepth / 2;

        var heightData = generateHeight( worldWidth, worldDepth );
        camera.position.y = heightData[ 0] ;
        var geometry = new THREE.PlaneGeometry( 300, 300, worldWidth - 1, worldDepth - 1 );

        geometry.applyMatrix( new THREE.Matrix4().makeRotationX( - Math.PI / 2 ) );

        for ( var i = 0, l = geometry.vertices.length; i < l; i ++ ) {

            geometry.vertices[ i ].y = heightData[ i ] * 2;

        }

        var leftGeometry = new THREE.PlaneGeometry(300, 1, worldWidth - 1, worldDepth - 1 );
        for( var i = 0; i < worldWidth; i++ ) {
            for( var j = 0; j < worldDepth; j++ ) {
                var index = j * worldWidth + i;
                if ( j ==  worldDepth-1 ) {
                    leftGeometry.vertices[index].y = geometry.vertices[index].y +1;
                }
            }
        }

        var rightGeometry = new THREE.PlaneGeometry(300, 1, worldWidth - 1, worldDepth - 1 );
        for( var i = 0; i < worldWidth; i++ ) {
            for( var j = 0; j < worldDepth; j++ ) {
                var index = j * worldWidth + i;
                if ( j ==  0 ) {
                    rightGeometry.vertices[index].y = geometry.vertices[index].y + 1;
                }
            }
        }

        var topGeometry = new THREE.PlaneGeometry(1, 300, worldWidth - 1, worldDepth - 1 );

        topGeometry.applyMatrix( new THREE.Matrix4().makeRotationX( - Math.PI / 2 ) );

        for( var i = 0; i < worldWidth; i++ ) {
            for( var j = 0; j < worldDepth; j++ ) {
                var index = j * worldWidth + i;
                if ( i ==  0 ) {
                    topGeometry.vertices[index].y = geometry.vertices[index].y+1;
                }
            }
        }

        var bottomGeometry = new THREE.PlaneGeometry(1, 300, worldWidth - 1, worldDepth - 1 );

        bottomGeometry.applyMatrix( new THREE.Matrix4().makeRotationX( - Math.PI / 2 ) );

        for( var i = 0; i < worldWidth; i++ ) {
            for( var j = 0; j < worldDepth; j++ ) {
                var index = j * worldWidth + i;
                if ( i ==  worldWidth-1 ) {
                    bottomGeometry.vertices[index].y = geometry.vertices[index].y+1;
                }
            }
        }

        var baseGeometry = new THREE.PlaneGeometry(300, 300, worldWidth - 1, worldDepth - 1 );
        baseGeometry.applyMatrix( new THREE.Matrix4().makeRotationX( - Math.PI / 2 ) );

        // ------------------------------

        var combined = new THREE.Geometry();

        var meshA = new THREE.Mesh( geometry,  new THREE.MeshNormalMaterial() );
        var meshB = new THREE.Mesh( leftGeometry, new THREE.MeshNormalMaterial() );
        var meshC = new THREE.Mesh( rightGeometry, new THREE.MeshNormalMaterial() );
        var meshD = new THREE.Mesh( topGeometry, new THREE.MeshNormalMaterial() );
        var meshE = new THREE.Mesh( bottomGeometry, new THREE.MeshNormalMaterial() );
        var meshF = new THREE.Mesh( baseGeometry, new THREE.MeshNormalMaterial() );

        meshB.position.z = worldHalfWidth+100;
        meshB.position.y = 0;
        meshC.position.y = 0;
        meshC.position.z = -worldHalfWidth-100;
        meshD.position.x = -worldHalfWidth-99;
        meshE.position.x = worldHalfWidth+99;


        THREE.GeometryUtils.merge(combined, meshA);
        THREE.GeometryUtils.merge(combined, meshB);
        THREE.GeometryUtils.merge(combined, meshC);
        THREE.GeometryUtils.merge(combined, meshD);
        THREE.GeometryUtils.merge(combined, meshE);
        THREE.GeometryUtils.merge(combined, meshF);

        var out = new THREE.Mesh( combined, new THREE.MeshBasicMaterial({
            wireframe: true,
            color: 'white'
        }));

        scene.add(out);
        scene.add(camera);

        var pointLight = new THREE.PointLight( 0xFFFFFF );

        pointLight.position.x = 10;
        pointLight.position.y = 50;
        pointLight.position.z = 130;

        scene.add(pointLight);

        renderer.render(scene, camera);


        function generateHeight( width, height ) {

            var size = width * height, data = new Float32Array( size ),
            perlin = new ImprovedNoise(), quality = 1, z = Math.random() * 100;

            for ( var i = 0; i < size; i ++ ) {
                data[ i ] = 0
            }

            for ( var j = 0; j < 4; j ++ ) {
                for ( var i = 0; i < size; i ++ ) {
                    var x = i % width, y = ~~ ( i / width );
                    data[ i ] += Math.abs( perlin.noise( x / quality, y / quality, z ) * quality * 1.75 );
                }
                quality *= 5;
            }
            return data;

        }


        function animate() {

            requestAnimationFrame( animate );
            render();

        }


        function render() {

            controls.update( clock.getDelta() );
            renderer.render( scene, camera );
            camera.position.z = ( mouseX - camera.position.x ) * .6;
        }

       $("#export").click(function(){
            var out = exporter.exportScene(scene);
            var blob = new Blob([out], {type: 'text/plain'});
            saveAs(blob, 'please work.stl');
        });

        $("#container").mousemove(function(event) {
            mouseX = event.pageX; 
            mouseY = event.pageY;
        });


    });

    </script>
</html>

My questions is: Is there another way to create this type of shape, other than using the GeometryUtils.merge function? Currently, the output shape cannot be 3d printed, and I'm wondering if it's because after the shapes are merged, the final geometry contains gaps. I'm not sure how to remedy this issue.

0

There are 0 answers