mrdoob / three.js

JavaScript 3D Library.
https://threejs.org/
MIT License
101.97k stars 35.33k forks source link

[feature request] configurable handedness (direction of rotation) #15442

Closed trusktr closed 5 years ago

trusktr commented 5 years ago
Description of the problem

It'd be nice to configure the default direction of rotation (handedness) in Three.js.

Maybe it can be a static property on Object3D or Matrix4?

For example:

Object3D.HANDEDNESS_X = 'left'
Object3D.HANDEDNESS_Y = 'right'
Object3D.HANDEDNESS_Z = 'left'

or maybe on an instance-per-instance basis? F.e.:

Object3D.handednessX = 'left'
Object3D.handednessY = 'right'
Object3D.handednessZ = 'left'

I think perhaps on an object-per-object would be nicer and more flexible.

Ah, but if it is on an object-per-object basis, then we have access to the matrix, so it would be cleaner for it to be an instance variable on Matrix4:

Object3D.matrix.handednessX = 'left'
Object3D.matrix.handednessY = 'right'
Object3D.matrix.handednessZ = 'left'
Three.js version
Browser
OS
trusktr commented 5 years ago

Here's a codepen example, where I've hacked CSS3DRenderer in order to keep the DOM structure nested in the same shape as the Three.js scene tree, and I'm simply copying over local matrices to the DOM elements instead of world matrices, and as you can see the X rotation direction in the DOM is inverse from Three.js rotation (notice the slightly-visible mesh rotates on X the opposite direction as the DOM element):

https://codepen.io/anon/pen/ZVOKwG

(see line 468 where X rotation is applied, and line 123 is the function that converts the local object's matrix into a CSS transform)

If we could simply invert the handedness for that axis, then it'd make the end-user code cleaner, instead of the user having to calculate opposite rotation and having to abstract it at a higher level.

Notice that the Y rotation direction is the same between DOM and Three.js.

mrdoob commented 5 years ago

I think such a feature will make the math classes ~9 times more complicated...

In 7 years we were not able to export from Blender with the right handedness. And that was a case of single handedness to single handedness. In short, handedness is complicated.

Feel free to give it a go, but I suspect the complexity this brings would scare contributors.

mrdoob commented 5 years ago

https://codepen.io/anon/pen/ZVOKwG

For some reason this page enters a infinite reloading loop in Chrome.

looeee commented 5 years ago

For some reason this page enters a infinite reloading loop in Chrome

Does it work in incognito? I've very intermittently been having that problem with codepen for about a year now but incognito mode usually fixes it. I think it's a problem with some extension but I haven't tracked down which one yet.

mrdoob commented 5 years ago

@looeee it does yeah! 🤔

trusktr commented 5 years ago

The way I was thinking of the implementation it wouldn't touch the Math classes, just Object3D rotation.xyz API: basically simply negate or don't negate the values based on handedness.

Someone doing their own matrix math would be on their own and would have to ensure handedness of their own math based on default handedness of current Math classes.

I'll make a PR for discussion if I can get a chance.

handedness and axis direction, Three.js vs DOM - Three.js: - X: right (X goes right) - Y: right (Y goes up) - Z: right (Z goes towards camera) - DOM: - X: left (X goes right) - Y: left (Y goes down) - Z: left (Z goes towards camera)
WestLangley commented 5 years ago

See https://www.evl.uic.edu/ralph/508S98/coordinates.html.

There is no way I would support making per-axis handedness configurable.

I recommend closing this issue.

trusktr commented 5 years ago

It seems at least reasonable to be able to configure "direction of rotation" for all axes together, if not individually.

I'd enjoy to simply toggle a flag and have Three.js align with CSS3D transforms. This google search shows I'm not the only one that would like more configurability over the axes.


As example, suppose an existing app uses regular CSS transforms (not CSS3DRenderer) to make some cool rotating DOM stuffs, and we'd like to simply add some shine or shadow to the DOM elements while they rotate.

Having this feature would solve half of the requirements (there's still some work involved in making the camera emulate CSS perspective, which can be learned from CSS3DRenderer.

trusktr commented 5 years ago

There is no way I would support making per-axis handedness configurable.

I've explained (with working example) why I want the feature. Can you explain why you don't, and how having the optional feature would impact your use of Three?

Mugen87 commented 5 years ago

Engines like Unity also don't offer such an option. For good reasons.

This google search shows I'm not the only one that would like more configurability over the axes.

People who are searching for this are obviously not familiar with the complexity behind such a feature.

trusktr commented 5 years ago

How much complexity would it add? (Just curious, not implying anything about the complexity.)

trusktr commented 5 years ago

If I'm not relying on matrices directly (i.e. relying the renderer calling updateMatrixWorld to compose matrices from position, quaternion, and scale), then I can do the following to simply switch to left handedness:

const LeftHandedEuler = (function() {

    const Super = Object.getOwnPropertyDescriptors(THREE.Euler.prototype)

    return class LeftHandedEuler extends THREE.Euler {

        get x() { return -Super.x.get.call(this) }
        set x(value) { Super.x.set.call(this, -value) }

        get y() { return -Super.y.get.call(this) }
        set y(value) { Super.y.set.call(this, -value) }

        get z() { return -Super.z.get.call(this) }
        set z(value) { Super.z.set.call(this, -value) }

    }

}())

function switchToLeftHanded(obj) {

    // cleanup the old handler
    obj.rotation.onChange( function() {} )

    Object.defineProperty(obj, 'rotation', {

        configurable: true,
        enumerable: true,
        value: new LeftHandedEuler,

    })

    function onRotationChange() {

        obj.quaternion.setFromEuler( obj.rotation, false );

    }

    function onQuaternionChange() {

        obj.rotation.setFromQuaternion( quaternion, undefined, false );

    }

    obj.rotation.onChange( onRotationChange );
    obj.quaternion.onChange( onQuaternionChange );

}

// new box (cube) geometry
var geometry = new THREE.BoxGeometry(120, 120, 120);
var material = new THREE.MeshLambertMaterial();
var mesh = new THREE.Mesh(geometry, material);

// switch it!
switchToLeftHanded(mesh);

codepen link

mrdoob commented 5 years ago

How much complexity would it add? (Just curious, not implying anything about the complexity.)

You may want to do some studying on the subject over the holidays 😉