firtoz / react-three-renderer

Render into a three.js canvas using React.
https://toxicfork.github.com/react-three-renderer-example/
MIT License
1.49k stars 155 forks source link

BoxGeometry with pivot at top-left instead of center #215

Closed Venryx closed 6 years ago

Venryx commented 6 years ago

I'm trying to create a BoxGeometry with the pivot at its top-left instead of its center.

According to answers such as this, it can be accomplished with this code:

boxGeometry.translate(.5, .5, .5);

However, I tried using this with react-three-renderer, and it has no effect whatsoever:

class BoxGeometry_TopLeftPivot extends Component {
    geometry;
    render() {
        let {width, height, depth} = this.props;
        return (
            <boxGeometry ref={c=>this.geometry = c} width={width} height={height} depth={depth}/>
        );
    }
    componentDidMount() {
        this.geometry.translate(0.5, 0.5, 0.5);
    }
}

Any idea what could be wrong? (I tried also changing the 0.5s to 999s, and still no effect)

johnrees commented 6 years ago

Not tested this but does adding this.geometry.verticesNeedUpdate = true after the translate work? I also think the translate values might need to be half the props.width, height, depth etc.

Venryx commented 6 years ago

I tried setting the verticesNeedUpdate property (after the translate command), and it had no effect.

As for the translate values, I tried a variety of values, and it made no difference.

My "best guess" at this point is that it's caused by react-three-renderer caching the geometry data somewhere or something -- despite their being examples given in some other issues to use the componentDidMount function as used above. So it seems like the approach I'm using should work, but since it's not the only thing I can think of is some kind of caching issue.

johnrees commented 6 years ago

Seems to work for me, are you rendering the scene after the update?

https://github.com/johnrees/r3r-test/blob/master/src/R3r.jsx#L6:L17

Venryx commented 6 years ago

I came back to this, and finally see the problem now.

I was making a rectangle-dragging tool, where you hold down the mouse to start adding a rectangle, then drag over the area you want the rectangle to cover.

I had the pivot-changing code in the componentDidMount function, which only runs at the start when the dragging of the rectangle begins. Naturally, at this point, the width and height of the rectangle is 0. Meaning, that when componentDidMount runs and wants to do the translating, it isn't able to know how much to translate it for later when the rectangle has a (then unknown) size.

Now the reason I thought it would work, is because I thought the geometry translating occurred relatively, meaning that having the componentDidMount function just translate by [.5, .5, .5] would permanently change the pivot-point to be the top-left.

Looking at your code, though, and testing it successfully, showed me that the pivoting of geometry is in regular units -- so if the rectangle size is [2,2,2], you have to translate by [1,1,1] units. (not [.5, .5, .5] relative units) Therefore, you also have to update the translate amount when the rectangle size changes.

Anyway, my code used to be:

class BoxGeometry_PivotAtTopLeft extends EnhancedComponentBase<{width: number, height: number, depth: number}, {}> {
    geometry: THREE.BoxGeometry;
    render() {
        let {width, height, depth} = this.props;
        return (
            <boxGeometry ref={c=>this.geometry = c} width={width} height={height} depth={depth}/>
        );
    }
    componentDidMount() {
        this.geometry.translate(0.5, 0.5, 0.5);
    }
}

Now it's changed to:

class BoxGeometry_PivotAtTopLeft extends EnhancedComponentBase<{width: number, height: number, depth: number}, {}> {
    geometry: THREE.BoxGeometry;
    render() {
        let {width, height, depth} = this.props;
        return (
            <boxGeometry ref={c=>this.geometry = c} width={width} height={height} depth={depth}/>
        );
    }
    componentDidMountOrUpdate() {          // (custom-added convenience function)
        let {width, height, depth} = this.props;
        if (this.geometry) {
            this.geometry.translate(width / 2, height / 2, depth / 2);
        }
    }
}

And now it works as intended.