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

Why react-three-renderer ? #229

Open yiakwy opened 6 years ago

yiakwy commented 6 years ago

Hello I find this when I am exploring integration with electron and three.js-editor.

I developed a project using react, mobx, mobx-react and three (es6, webpack). For me, three provides scene graph to manage three objects. And react interacts with doms.

I cannot see clearly why we need to develop react-three renderer

Here is what I have done

src/ app.js (animation routine): defines "app" to to store global objects store/states.js: defines dom-data bidirectional binding and notify RENDERERE to use new to data in udpate renderer: three renderer Singleton in es6 and export interfaces to modify inner data managed by scene graph ui/: react components, like editor, gui and so on

The only thing you need is to trigger data initialization of renderer after React component mounted

Why react-three-renderer? I am seriously.

MaartenBreeedveld commented 6 years ago

Hi yiakwy, I'm really curious at this. Is all going well with memory management (i.e. is the THREE script also decently unmounting)?

Would you mind posting a uber-basic working example?

Cheers,

yiakwy commented 6 years ago

Hi @MaartenBreeedveld Here are some excerpts from my codes (I am currently writing codes for company not for myself)

var app = {

}
window.app = app

import RENDERER from "renderer/renderer"
import * as ReactDOM from "react-dom"
import Editor from "scene/editor.js"
import { Provider } from "mobx-react"
import React from "react"
import observableSceneStore from "mobx-stores/state"
import runtime from "serviceworker-webpack-plugin/lib/runtime"
import registerEvents from "serviceworker-webpack-plugin/lib/browser/registerEvents"

// initalize your dom and workers first

// dom
ReactDOM.render(<Provider store={ observableSceneStore }><Editor /></Provider>, document.getElementById("wrapper"))

function animate() {
  requrestAnimationFrame(aniamte)
  // tween, udpate control, anything else inside here
   app.update(RENDERER)
}

// Now the Dom is ok, let us run main loop and begin to  @handlel events
 animate()

Hope this helps you.

For the dom part that is easy

// scene.js

import React from "react"
import {observer, inject} from "mobx-react"

import RENDERER from "renderer/renderer"
import Viewport from "UICom/viewport"

const ca = ".canvas"

@inject("store") @observer 
export default class Scene extends React.Component {
    constructor(props) {
        super(props)
    }

    componentDidMount() {
        RENDERER.init(ca)
    }

    render() {
        return (
            <div className="scene row">
                <Viewport />
            </div>
        )

    }

}

I use combination of quadtree and kdtree for active memory management, If I want to find something quickly. If a block of memory is not active, I will post it to local storage using worker aysnc api. You can treat it like redis hot key.

/*
 * @author Lei, (yiak.wy@gmail.com), asynchronous 
 */ 
let extend = function(array_) {
    Array.prototype.push.apply(this, array_)
    return this 
}
Array.prototype.extend = extend 

export function inject_scope(...dep) {
    var fn = arguments[0],
        extended_args

    console.log(`inject_scope variables: ${dep}`)
    extended_args = Array.prototype.slice.apply(arguments).slice(1, arguments.length)

    return function wrapped(...inp) {
        console.log(`wrapped func inp: ${inp}`)
        var args_ = Array.prototype.slice.apply(inp)
        args_.extend(extended_args)
        return fn.apply(null, args_)
    }

}

export function channel_msg(resolve, reject, worker, msg) {
    return new Promise(function(resolve, reject) {
        let channel = new MessageChannel()
        channel.port1.onmessage = function(evt) {
            if (evt.data.error) {
                reject(evt.data.error)
            } else {
                resolve(evt.data)
            }
        }

        worker.postMessage(msg, [channel.port2])
    }).then(resolve).catch(reject)
}

export async function send(worker, msg, resolve, reject) {
    // let promise = new Promise(inject_scope(channel_msg, worker, msg)).then(resolve).catch(reject),
    let promise = channel_msg(resolve, reject, worker, msg),
        data 
    data = await promise
    return data 
}

I am not using high resolution shaders for rendering. THREE provides a convenient way to interact with gpu clip queue. I think it is ok for the moment.

MaartenBreeedveld commented 6 years ago

Hi yiakwy,

Thank you very much for this example. It does look crazy simple indeed! I'll have a go at it and try to get it working. Cheers!

johnrees commented 6 years ago

I can only speak as a user of the library (and I haven’t used it for quite some time- plan to again soon!), but I think that one of the headline features it provides is a declarative way to construct and manipulate the contents of a THREE.Scene.

yiakwy commented 6 years ago

@johnrees I think the difference between us is that for me THREE.Scene is a just scene graph to manage objects and is responsible for canvas rendering. I don't think it has any relationship with dom. While Dom will use observers and signals to manage complex interaction. That is not what three is good at.