#StackBounty: #javascript #reactjs #three.js #integration Three: How could we insert a locally loaded mesh into the canvas being genera…

Bounty: 50

Hello and thank you for reading this question:

I am learning Threejs, and currently I have a curious difficulty:

I have learnt how to use a loader to load local files with format NRRD, in plain HTML/JAVASCRIPT: here is the repo: https://github.com/YoneMoreno/LoadNRRDInThreeJSExample

As an example of how it looks like:

enter image description here

However, I would like to integrate the previous example with React.

I have studied how to associate React and Three using this SO thread:
Rendering three.js element in React?

And right now my code looks like:

/*global THREE */

import React from 'react';

class LoadNRRD extends React.Component {

    constructor(props) {
        super(props)

        this.start = this.start.bind(this)
        this.stop = this.stop.bind(this)
        this.animate = this.animate.bind(this)
    }

    componentDidMount() {
        const width = this.mount.clientWidth
        const height = this.mount.clientHeight

        const scene = new THREE.Scene()
        const camera = new THREE.PerspectiveCamera(
            75,
            width / height,
            0.1,
            1000
        )

        var loader = new THREE.NRRDLoader();
        loader.load("models/columnasegmentado01.nrrd", function (volume) {
            var sliceZ;


            //z plane

            var indexZ = 0;
            sliceZ = volume.extractSlice('z', Math.floor(volume.RASDimensions[2] / 4))
            console.log(sliceZ);
            scene.add(sliceZ.mesh);

        });

        const renderer = new THREE.WebGLRenderer({antialias: true})
        renderer.setPixelRatio(window.devicePixelRatio);
        renderer.setSize(width, height);


        renderer.setClearColor('#000000')


        this.scene = scene
        this.camera = camera
        this.renderer = renderer

        let controls = new THREE.TrackballControls(camera, renderer.domElement);
        controls.rotateSpeed = 0;
        controls.zoomSpeed = 5;
        controls.panSpeed = 2;
        controls.noZoom = false;
        controls.noPan = false;
        controls.staticMoving = true;
        controls.dynamicDampingFactor = 0.3;


        this.mount.appendChild(this.renderer.domElement)
        this.start()
    }

    componentWillUnmount() {
        this.stop()
        this.mount.removeChild(this.renderer.domElement)
    }

    start() {
        if (!this.frameId) {
            this.frameId = requestAnimationFrame(this.animate)
        }
    }

    stop() {
        cancelAnimationFrame(this.frameId)
    }

    animate() {


        this.renderScene()
        this.frameId = window.requestAnimationFrame(this.animate)
    }

    renderScene() {
        this.renderer.render(this.scene, this.camera)
    }

    render() {
        return (
            <div
                style={{width: '2000px', height: '2000px'}}
                ref={(mount) => {
                    this.mount = mount
                }}
            />
        )
    }
}

export default LoadNRRD;

The part I do not understand is: why it does display the canvas but without the model inside?:

enter image description here

I have checked that loader works well and model is in place because of we can see it on the console log:

THREE.VolumeSlice
axis:"z"
canvas:canvas
canvasBuffer:canvas
... more properties ...

Could you help me please? Am I missing some step? Have you experienced this issue before? How could you try to debbug this situation?

As a note, in branch ThreeIntegrate is the code I am writting about: https://github.com/PrototipoAtlas/Prototipo

If you ave any advice or suggestion please let me know it 🙃 🙂

EDIT:
Thank you @Jim Tang for your advice. I have installed Three Chrome extension and the output it gives after setting the scene in the global variab¡le window.scene as is suggested in:
https://github.com/jeromeetienne/threejs-inspector/issues/11

the result is:
enter image description here

The I tried with the standalone plain HTML/JS example and it had the model on it as:

enter image description here

So then the key question is: why is not being associated the model with the scene?‽‽

I have tried again and it looks like the model is indeed loaded however in the console when I tri to change the color of iot, or even click on it says:

THREE.MeshBasicMaterial: .shading has been removed. Use the boolean .flatShading instead.

I think, the solution could be about dicovering what is the missing part. If we see again the example which works, we have three elements:

enter image description here

I suspect that those three elements could be the ones added to the DOM:

        document.body.appendChild(container);
        container.appendChild(renderer.domElement);
...more code...
        container.appendChild(stats.dom);

In the code that does not show the model the appends are:

this.mount.appendChild(this.renderer.domElement);
this.mount.removeChild(this.renderer.domElement)

Even after deleting the uncommon element which is being appended, the stats, the plain HTML/JS example works and shows three elements !!

enter image description here

I have discovered that the missing element is the camera because if we see the working example the object has fov, near, far etc:

enter image description here

So I tried to add the camera to the scene in the code we are discussing:

const scene = new THREE.Scene();
        const camera = new THREE.PerspectiveCamera(
            75,
            width / height,
            0.1,
            1000
        );


        scene.add(camera);

And it gives a great and huge stack trace:

    TypeError: Cannot read property 'getExtension' of null
    get
    http://localhost:3001/build/three.js:20112:22
    initGLContext
    http://localhost:3001/build/three.js:21116:15
    new WebGLRenderer
    http://localhost:3001/build/three.js:21161:3
    LoadNRRD.componentDidMount
    http://localhost:3001/static/js/bundle.js:62599:28
      62596 |     scene.add(sliceZ.mesh);
      62597 | });
      62598 | 
    > 62599 | var renderer = new THREE.WebGLRenderer({ antialias: true });
            |                ^  62600 | renderer.setPixelRatio(window.devicePixelRatio);
      62601 | renderer.setSize(width, height);
      62602 | 
    View source
    ▶ 17 stack frames were collapsed.
    ./src/index.js
    C:/Users/YonePC/WebstormProjects/prototipo/src/index.js:22
      19 |     );
      20 | };
      21 | 
    > 22 | ReactDOM.render(
      23 |     <BrowserRouter>
      24 |         <App/>
      25 |     </BrowserRouter>
    View compiled
    ▶ 6 stack frames were collapsed.

TypeError: Cannot read property 'domElement' of undefined
LoadNRRD.componentWillUnmount
C:/Users/YonePC/WebstormProjects/prototipo/src/components/animals/LoadNRRD.js:73
  70 | 
  71 |    componentWillUnmount() {
  72 |        this.stop();
> 73 |        this.mount.removeChild(this.renderer.domElement)
  74 |    }
  75 | 
  76 |    start() {
View compiled
▶ 25 stack frames were collapsed.
./src/index.js
C:/Users/YonePC/WebstormProjects/prototipo/src/index.js:22
  19 |     );
  20 | };
  21 | 
> 22 | ReactDOM.render(
  23 |     <BrowserRouter>
  24 |         <App/>
  25 |     </BrowserRouter>
View compiled
▶ 6 stack frames were collapsed.

EDIT2:

I have tried again and we see:

enter image description here

The code we are talking about:

/*global THREE */

import React from 'react';

class LoadNRRD extends React.Component {

    constructor(props) {
        super(props);

        this.stop = this.stop.bind(this);
        this.start = this.start.bind(this);
        this.animate = this.animate.bind(this)
    }

    componentDidMount() {
        const width = this.mount.clientWidth;
        const height = this.mount.clientHeight;

        var scene = new THREE.Scene();
        var camera = new THREE.PerspectiveCamera(
            75,
            width / height,
            0.1,
            1000
        );


        scene.add(camera);

        const loader = new THREE.NRRDLoader();
        loader.load("models/columnasegmentado01.nrrd", function (volume) {
            let sliceZ;


            //z plane

            const indexZ = 0;
            sliceZ = volume.extractSlice('z', Math.floor(volume.RASDimensions[2] / 4));
            sliceZ.name = 'foo';
            console.log(sliceZ);
            scene.add(sliceZ.mesh);

        });

        var renderer = new THREE.WebGLRenderer({antialias: true});
        renderer.setPixelRatio(window.devicePixelRatio);
        renderer.setSize(width, height);


        renderer.setClearColor('#000000');


        this.scene = scene;
        window.scene = scene;
        this.camera = camera;
        this.renderer = renderer;

        let controls = new THREE.TrackballControls(camera, renderer.domElement);
        controls.rotateSpeed = 0;
        controls.zoomSpeed = 5;
        controls.panSpeed = 2;
        controls.noZoom = false;
        controls.noPan = false;
        controls.staticMoving = true;
        controls.dynamicDampingFactor = 0.3;


        this.mount.appendChild(this.renderer.domElement);
        this.start()
    }

    componentWillUnmount() {
        this.stop();
        this.mount.removeChild(this.renderer.domElement)
    }

    start() {
        if (!this.frameId) {
            this.frameId = requestAnimationFrame(this.animate)
        }
    }

    stop() {
        cancelAnimationFrame(this.frameId)
    }

    animate() {


        this.renderScene();
        this.frameId = window.requestAnimationFrame(this.animate)
    }

    renderScene() {
        this.renderer.render(this.scene, this.camera)
    }

    render() {
        return (
            <div
                style={{width: '2000px', height: '2000px'}}
                ref={(mount) => {
                    this.mount = mount
                }}
            />
        )
    }
}

export default LoadNRRD;

As you see I have added the name to sliceZ, and it shows on web console:
enter image description here

EDIT3: Using renderer info object to debbug:

We see that there is a geomtery an d a texture and also that the render methods is being called 217 times:

{render: {…}, memory: {…}, programs: Array(0), autoReset: false, reset: ƒ}
autoReset:false
memory:geometries:1 textures:1
__proto__:Object
programs:[WebGLProgram]
render:
calls:217
faces:434
frame:0
points:0
vertices:1302
__proto__:Object
reset:
ƒ resetInfo()
__proto__:
Object

I have debugged a working example where React is being in tegrated with Three:

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

ANd on the console we see the info Object:

autoReset: false
​
memory: Object { geometries: 1, textures: 0 }
​
programs: Array [ {…} ]
​
render: Object { frame: 485, calls: 1, vertices: 36, … }
​
__proto__: Object { … }

So a we see the calls are 1, could be the problem in the code showd that the call to render method of renderer is continously being invoked?

In addition I have uploaded the code being disscussed:

https://github.com/YoneMoreno/ThreeReactNRRDLoaderStandalone.git

Could you help me please?


Get this bounty!!!

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.