12

Update 2019

Since r89 three.js will also adjust the faces and normals. To flip/mirror an object simply use:

object3D.applyMatrix(new THREE.Matrix4().makeScale(-1, 1, 1));

No other adjustments as shown in the original solution are required. Link to the ticket: Support reflection matrices. #12787

Original question

I'm trying to create an utility that would flip any object in Three.js scene. The flipping itself is the easy bit:

object3D.applyMatrix(new THREE.Matrix4().makeScale(-1, 1, 1));

What is proving difficult is fixing faces and normals after the flip. The result looks quite messed up. Images of the source and flipped object: https://dl.dropboxusercontent.com/u/20925853/Flipped.png

I have seen a number of threads where this and similar issues were discussed but did not find anything usable. Does anybody know what I'm missing there? - Thanks!

Sample code on Js fiddle: https://jsfiddle.net/7dwh084w/

var renderer;
var scene;
var camera;

function render() {

    renderer.render(scene, camera);
};

function load(callback) {

    new THREE.ColladaLoader().load("https://dl.dropboxusercontent.com/u/20925853/Kitchen.dae", function (result) {

        var mesh = result.scene.children[0].children[0].clone();
        if (callback) callback(mesh);
    });
}

function init() {

    scene = new THREE.Scene();
    camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 10000);
    camera.position.z = 1000;

    var light = new THREE.DirectionalLight(0xffffff);
    light.position.set(2, 1, 1).normalize();
    scene.add(light);
    var ambient = new THREE.AmbientLight(0x777777);
    scene.add(ambient);

    var controls = new THREE.OrbitControls(camera);
    controls.damping = 0.2;
    controls.addEventListener('change', render);

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

    load(function (mesh) {

        flipMesh(mesh);

        scene.add(mesh);
        render();
    });

    render();
}

function flipMesh(object3D) {

    object3D.applyMatrix(new THREE.Matrix4().makeScale(-1, 1, 1));
    reverseWindingOrder(object3D);
}

function reverseWindingOrder(object3D) {

    // TODO: Something is missing, the objects are flipped alright but the light reflection on them is somehow broken

    if (object3D.type === "Mesh") {

        var geometry = object3D.geometry;

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

            var face = geometry.faces[i];
            var temp = face.a;
            face.a = face.c;
            face.c = temp;

        }

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

            var vector2 = faceVertexUvs[i][0];
            faceVertexUvs[i][0] = faceVertexUvs[i][2];
            faceVertexUvs[i][2] = vector2;
        }

        geometry.computeFaceNormals();
        geometry.computeVertexNormals();
    }

    if (object3D.children) {

        for (var j = 0, jl = object3D.children.length; j < jl; j++) {

            reverseWindingOrder(object3D.children[j]);
        }
    }
}

init();
Community
  • 1
  • 1
Immugio
  • 496
  • 1
  • 6
  • 13
  • What about looping through all facenormals and doing a face.normal.negate()? – Flux Feb 20 '15 at 19:14
  • Hi @Flux, unfortunately it does not help – Immugio Feb 22 '15 at 09:34
  • Did you ever get this working? – 2pha Jan 13 '16 at 02:31
  • @2pha The code above works beautifully for objects loaded from Collada (dae) files. It does not work correctly for some objects I create in code. I'm using it with threejs r71. The code required slight modifications, these two lines need to be removed: geometry.computeFaceNormals(); geometry.computeVertexNormals(); – Immugio Jan 14 '16 at 18:57
  • Thanks, indeed it does work. my geometry was a bufferGeometry so I had to convert it to Geometry and then back. Thanks – 2pha Jan 15 '16 at 02:16
  • I'm having the same problem with loading a file through OBJLoader. `terrain.scale.set(1, 1, -1);` flips the terrain upside down. – majidarif Nov 07 '16 at 23:58
  • @majidarif you could try terrain.scale.set(1, -1, 1) or terrain.scale.set(-1, 1, 1) to mirror the other axes – Immugio Dec 06 '16 at 20:06
  • See https://stackoverflow.com/questions/16824650/three-js-how-to-flip-normals-after-negative-scale – Jérémie Boulay Mar 16 '18 at 10:37
  • I'm using `object.scale.x *= -1;` to flip horizontally and works well in my case where the scale is other than 1. – Miro Jun 11 '19 at 09:25
  • How could we achieve this? https://stackoverflow.com/questions/62764844/drawing-mirror-object-in-threejs – user2780638 Jul 11 '20 at 22:16

1 Answers1

6

I use this code in my project to flip objects:

    const scale = new THREE.Vector3(1, 1, 1);
    if (flipX) {
        scale.x *= -1;
    }
    if (flipY) {
        scale.z *= -1;
    }
    object.scale.multiply(scale);

Hope this helps.

teh.fonsi
  • 3,040
  • 2
  • 27
  • 22
  • How could we achieve this? https://stackoverflow.com/questions/62764844/drawing-mirror-object-in-threejs – user2780638 Jul 11 '20 at 22:17
  • this works fine when the model is oriented in the direction of the world axis, but when you rotate the model in any way it will flip the model local coordinates but because of the rotation the transformation will not result in a proper mirrored model – sjjk001 Apr 30 '21 at 09:31