facebookarchive / react-360

Create amazing 360 and VR content using React
https://facebook.github.io/react-360
Other
8.73k stars 1.22k forks source link

Cannot use THREE.BufferGeometry as Native View input #409

Open wdeng opened 6 years ago

wdeng commented 6 years ago

Description

I'm currently experimenting with React VR for rendering VTK mesh. I implemented a Native View CustomMesh.js extending RCBaseMesh.

Expected behavior

I want to generate a mesh within the app like this:

<CustomMesh
   meshGeometry={this.state.geometry}
   lit={true}
   style={{...}}
/>

The CustomMesh implementation is similar to built-in elements such as Box. The meshGeometry will be the output BufferGeometry or Geometry from VTKLoader.js.

Actual behavior

When I don't THREE.Geometry as CustomMesh input, it works fine. When using Geometry as input, at runtime, the console will generate errors: cannot redefine property: 0

After searching, I think the problem is React VR has problems passing non-configurable objects into the Native rendering part.

Possible solutions

A possible solution is to pass VTK raw data into RCT Mesh and generate geometry in the RCT.

But because I want React VR to be an iframe component inside a Redux framework, I'm wondering if there is another way around.

andrewimm commented 6 years ago

Sorry, I'm still having a bit of trouble understanding the precise issue. Can you provide the React-side and Renderer-side code you wrote for your CustomMesh component?

wdeng commented 6 years ago

@andrewimm Here is the Render side code:

import VTKLoader from '../../vr/VTKLoader'

import RCTBaseMesh from 'react-vr-web/js/Views/BaseMesh'
import merge from 'react-vr-web/js/Utils/merge'

import * as OVRUI from 'ovrui';
import { ReactNativeContext} from 'react-vr-web';

// This is the port to THREE.js part, in charge of actual rendering
export default class RCTCustomMesh extends RCTBaseMesh {
    // PropTypes
    _meshGeometry: object;
    _needsUpdate: boolean;

    constructor(guiSys: GuiSys, rnctx: ReactNativeContext) {
        super(guiSys, rnctx);

        this._needsUpdate = false;
        this._meshGeometry = null//new THREE.BoxBufferGeometry(0.5, 0.5, 0.5);

        Object.defineProperty(this.props, 'meshGeometry',
            ({
                set: geo => {
                    this._meshGeometry = geo;
                    this._needsUpdate = true;
                },
            }: Object)
        );

        Object.defineProperty(this.props, 'filePath',
            ({
                set: path => {
                    const loader = new THREE.VTKLoader();
                    loader.load(path, (geo) => {
                        geo.center();
                        this._meshGeometry = geo
                        this._needsUpdate = true;
                    });
                },
            }: Object)
        );
    }

    frame() { // this occurs every frame
        if (this._needsUpdate) {
            this._needsUpdate = false;
            this._setGeometry(this._meshGeometry)
        }
    }

    static describe() {
        return merge(super.describe(), {
            // register the properties sent from react to runtime
            NativeProps: {
                meshGeometry: 'object',     // GIVES RUNTIME ERRORS
                filePath: 'string'
            },
        });
    }
}
wdeng commented 6 years ago

And the React side:

import React from 'react'
import { View } from 'react-vr'
import PropTypes from 'prop-types'
import {BoxBufferGeometry} from 'three'

const NativeMethodsMixin = require('NativeMethodsMixin');
const StyleSheetPropType = require('StyleSheetPropType');
const LayoutAndTransformColorPropTypes = require('LayoutAndTransformColorPropTypes');
const ReactNativeViewAttributes = require('ReactNativeViewAttributes');
const requireNativeComponent = require('requireNativeComponent');
const createReactClass = require('create-react-class');

const CustomMesh = createReactClass({
    mixins: [NativeMethodsMixin],

    propTypes: {
        ...View.propTypes,
        style: StyleSheetPropType(LayoutAndTransformColorPropTypes),

        lit: PropTypes.bool,

        texture: PropTypes.object, // need to make the texture to use TextureData

        materialParameters: PropTypes.object,

        wireframe: PropTypes.bool,

        meshGeometry: PropTypes.object,

        filePath: PropTypes.string,
    },

    viewConfig: {
        uiViewClassName: 'CustomMesh',
        validAttributes: {
            ...ReactNativeViewAttributes.RCTView,
            meshGeometry: true  // Will be used in animation
        },
    },

    getDefaultProps() {
        return {
            // meshGeometry: new BoxBufferGeometry(0.5, 0.5, 0.5)
            filePath: '../static_assets/InputTruthRS.vtk'
        };
    },

    render() {
        let {style, ...rest } = this.props;
        style = style || {};
        if (!style.position)
            style.position = 'absolute';
        return <RKCustomMesh style={style} {...rest} />;   // texture={texture}
    },
});

const RKCustomMesh = requireNativeComponent('CustomMesh', CustomMesh, {});

module.exports = CustomMesh;

And in index.vr.js, runtime error occur when I call: <CustomMesh meshGeometry={{new BoxBufferGeometry(0.5, 0.5, 0.5)}}/>

cannot redefine property: 0

The reason is very likely to be three.js objects are not configurable, and the input objects are modified from React-side to Render-side.

And here is a related issue: https://github.com/mrdoob/three.js/issues/11092