0

This code should produce a mesh of a torus in three js. I'm pretty sure the maths are correct. However it renders only a piece of torus, or stranger things if I change some parameters. Is there something bad with my practice of THREE.Mesh ?

// the vertices of the mesh and the vertex normals ----------------
        var nx = 64;
        var ny = 32;
        var R = 10; var r = 3;
        var Vertices = new Array(nx);
        var Normals = new Array(nx);
        for (var i = 0; i < nx; i++) {
            Vertices[i] = new Array(ny);
            Normals[i] = new Array(ny);
            var u = i / nx * 2 * Math.PI;
            var cos_u = Math.cos(u);
            var sin_u = Math.sin(u);
            var cx = R * cos_u;
            var cy = R * sin_u;
            for (var j = 0; j < ny; j++) {
                var v = j / ny * 2 * Math.PI;
                var rcos_v = r * Math.cos(v);
                var rsin_v = r * Math.sin(v);
                Vertices[i][j] = new THREE.Vector3(
                    cx + rcos_v * cos_u,
                    cy + rcos_v * sin_u,
                    rsin_v
                );
                Normals[i][j] = new THREE.Vector3(
                    rcos_v * cos_u,
                    rcos_v * sin_u,
                    rsin_v
                );
            }
        }

// vertices as a dot cloud ----------------------------------------
        var dotGeometry = new THREE.Geometry();
        for (var i = 0; i < nx; i++) {
            for (var j = 0; j < ny; j++) {
                dotGeometry.vertices.push(Vertices[i][j]);
            }
        }
        var dotMaterial =
            new THREE.PointsMaterial({ size: 1, sizeAttenuation: false });
        var cloud = new THREE.Points(dotGeometry, dotMaterial);

// mesh -----------------------------------------------------------
        var geom = new THREE.Geometry();
        for (var i = 0; i < nx; i++) {
            var ip1 = (i == nx - 1 ? 0 : i + 1);
            for (var j = 0; j < ny; j++) {
                var jp1 = (j == ny - 1 ? 0 : j + 1);
                geom.vertices.push(Vertices[i][j]);
                geom.vertices.push(Vertices[i][jp1]);
                geom.vertices.push(Vertices[ip1][j]);
                var vnormals1 = 
                    [Normals[i][j], Normals[i][jp1], Normals[ip1][j]];
                geom.faces.push(new THREE.Face3(
                    i * ny + j,
                    i * ny + jp1,
                    ip1 * ny + j,
                    vnormals1
                ));
                geom.vertices.push(Vertices[i][jp1]);
                geom.vertices.push(Vertices[ip1][jp1]);
                geom.vertices.push(Vertices[ip1][j]);
                var vnormals2 = 
                    [Normals[i][jp1], Normals[ip1][jp1], Normals[ip1][j]];
                geom.faces.push(new THREE.Face3(
                    i * ny + jp1,
                    ip1 * ny + jp1,
                    ip1 * ny + j,
                    vnormals2
                ));
            }
        }

        var torusMesh = new THREE.Mesh(
            geom, 
            new THREE.MeshNormalMaterial({ wireframe: false }));

// three js scene -------------------------------------------------
        var scene = new THREE.Scene();

        var aspect = window.innerWidth / window.innerHeight;
        var camera = new THREE.PerspectiveCamera(50, aspect, 1, 10000);
        camera.position.z = 30;
        scene.add(camera);

        var renderer = new THREE.WebGLRenderer();
        renderer.setSize(window.innerWidth, window.innerHeight);
        document.body.appendChild(renderer.domElement);

        var object = new THREE.Object3D();
        object.add(torusMesh);
        object.add(cloud);
        scene.add(object);
        renderer.render(scene, camera);

// animation ---------------------------------------------------------
        var isDragging = false;
        var previousMousePosition = {
            x: 0,
            y: 0
        };
        $(renderer.domElement).on('mousedown', function (e) {
            isDragging = true;
        }).on('mousemove', function (e) {
            var deltaMove = {
                x: e.offsetX - previousMousePosition.x,
                y: e.offsetY - previousMousePosition.y
            };
            if (isDragging) {
                var deltaRotationQuaternion = new THREE.Quaternion()
                    .setFromEuler(new THREE.Euler(
                        Math.PI / 180 * (deltaMove.y * 1),
                        Math.PI / 180 * (deltaMove.x * 1),
                        0,
                        'XYZ'
                    ));
                object.quaternion.multiplyQuaternions(deltaRotationQuaternion,
                    object.quaternion);
            }
            previousMousePosition = {
                x: e.offsetX,
                y: e.offsetY
            };
        });

        $(document).on('mouseup', function (e) {
            isDragging = false;
        });

        window.requestAnimFrame = (function () {
            return window.requestAnimationFrame ||
                window.webkitRequestAnimationFrame ||
                window.mozRequestAnimationFrame ||
                function (callback) {
                    window.setTimeout(callback, 1000 / 60);
                };
        })();

        function render() {
            renderer.render(scene, camera);
            requestAnimFrame(render);
        }

        render();
canvas {
            width: 100%;
            height: 100%
        }
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/98/three.js"></script>

1 Answer 1

2

I think there were two problems with your code:

  • When using Geometry, you define faces just by adding objects of type Face3 to the faces array. The vertices of the geometry are defined just once. In your case, you can just do this:

    geom.vertices = dotGeometry.vertices;
    
  • Besides, the winding order of your faces was not correct. You have to switch the first and third index.

// the vertices of the mesh and the vertex normals ----------------
        var nx = 64;
        var ny = 32;
        var R = 10; var r = 3;
        var Vertices = new Array(nx);
        var Normals = new Array(nx);
        for (var i = 0; i < nx; i++) {
            Vertices[i] = new Array(ny);
            Normals[i] = new Array(ny);
            var u = i / nx * 2 * Math.PI;
            var cos_u = Math.cos(u);
            var sin_u = Math.sin(u);
            var cx = R * cos_u;
            var cy = R * sin_u;
            for (var j = 0; j < ny; j++) {
                var v = j / ny * 2 * Math.PI;
                var rcos_v = r * Math.cos(v);
                var rsin_v = r * Math.sin(v);
                Vertices[i][j] = new THREE.Vector3(
                    cx + rcos_v * cos_u,
                    cy + rcos_v * sin_u,
                    rsin_v
                );
                Normals[i][j] = new THREE.Vector3(
                    rcos_v * cos_u,
                    rcos_v * sin_u,
                    rsin_v
                );
            }
        }

// vertices as a dot cloud ----------------------------------------
        var dotGeometry = new THREE.Geometry();
        for (var i = 0; i < nx; i++) {
            for (var j = 0; j < ny; j++) {
                dotGeometry.vertices.push(Vertices[i][j]);
            }
        }
        var dotMaterial =
            new THREE.PointsMaterial({ size: 1, sizeAttenuation: false });
        var cloud = new THREE.Points(dotGeometry, dotMaterial);

// mesh -----------------------------------------------------------
        var geom = new THREE.Geometry();
        geom.vertices = dotGeometry.vertices;
        for (var i = 0; i < nx; i++) {
            var ip1 = (i == nx - 1 ? 0 : i + 1);
            for (var j = 0; j < ny; j++) {
                var jp1 = (j == ny - 1 ? 0 : j + 1);

                var vnormals1 = 
                    [Normals[i][j], Normals[i][jp1], Normals[ip1][j]];
                geom.faces.push(new THREE.Face3(
                    ip1 * ny + j,
                    i * ny + jp1,
                    i * ny + j,
                    vnormals1
                ));
 
                var vnormals2 = 
                    [Normals[i][jp1], Normals[ip1][jp1], Normals[ip1][j]];
                geom.faces.push(new THREE.Face3(
                    ip1 * ny + j,
                    ip1 * ny + jp1,
                    i * ny + jp1,
                    vnormals2
                ));
            }
        }

        var torusMesh = new THREE.Mesh(
            geom, 
            new THREE.MeshNormalMaterial({ wireframe: false }));

// three js scene -------------------------------------------------
        var scene = new THREE.Scene();

        var aspect = window.innerWidth / window.innerHeight;
        var camera = new THREE.PerspectiveCamera(50, aspect, 1, 10000);
        camera.position.z = 30;
        scene.add(camera);

        var renderer = new THREE.WebGLRenderer();
        renderer.setSize(window.innerWidth, window.innerHeight);
        document.body.appendChild(renderer.domElement);

        var object = new THREE.Object3D();
        object.add(torusMesh);
        object.add(cloud);
        scene.add(object);
        renderer.render(scene, camera);

// animation ---------------------------------------------------------
        var isDragging = false;
        var previousMousePosition = {
            x: 0,
            y: 0
        };
        $(renderer.domElement).on('mousedown', function (e) {
            isDragging = true;
        }).on('mousemove', function (e) {
            var deltaMove = {
                x: e.offsetX - previousMousePosition.x,
                y: e.offsetY - previousMousePosition.y
            };
            if (isDragging) {
                var deltaRotationQuaternion = new THREE.Quaternion()
                    .setFromEuler(new THREE.Euler(
                        Math.PI / 180 * (deltaMove.y * 1),
                        Math.PI / 180 * (deltaMove.x * 1),
                        0,
                        'XYZ'
                    ));
                object.quaternion.multiplyQuaternions(deltaRotationQuaternion,
                    object.quaternion);
            }
            previousMousePosition = {
                x: e.offsetX,
                y: e.offsetY
            };
        });

        $(document).on('mouseup', function (e) {
            isDragging = false;
        });

        window.requestAnimFrame = (function () {
            return window.requestAnimationFrame ||
                window.webkitRequestAnimationFrame ||
                window.mozRequestAnimationFrame ||
                function (callback) {
                    window.setTimeout(callback, 1000 / 60);
                };
        })();

        function render() {
            renderer.render(scene, camera);
            requestAnimFrame(render);
        }

        render();
canvas {
            width: 100%;
            height: 100%
        }
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/98/three.js"></script>
<script
  src="https://code.jquery.com/jquery-3.3.1.slim.min.js"
  integrity="sha256-3edrmyuQ0w65f8gfBsqowzjJe2iM6n0nKciPUp8y+7E="
  crossorigin="anonymous"></script>

Besides, consider to use the approach of TorusBufferGeometry. Moreover, it's much faster to generate a geometry with BufferGeometry than with Geometry.

Sign up to request clarification or add additional context in comments.

4 Comments

Thank you! I knew that the torus is avalaible in three js. This is just an exercise and also a preliminary step for something more complicated.
That still doesn't work for me :( And I still get this warning by the way: [.WebGL-0x55bf7783fad0]GL ERROR :GL_INVALID_ENUM : glGetIntegerv: pname was GL_MAX_SAMPLES_ANGLE
The torus appears complete in your snipet. Look at this one: fiddle. For me, only a portion of the torus appears. The code is correct, I have reversed the triangles.
Are you on Linux? If so, you can ignore the mentioned warning. It has nothing to do with your particular torus implementation.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.