1

After making a stand alone three js app I wanted to make this three js app a component in my React application.

I needed multiple scenes overlayed so I made a threeApp function that instantiates a scene, camera, renderer and adds it to the DOM. So I tried to make a lot of stuff reusable.

After following this answer I managed to get some a three js scene with a spinning cube in my react app. Success! Since I already made a functioning OOP (?) implementation without react so I dove right in.

Right now I have a rendered canvas in my app but the objects in it are not getting rendered. However I can see the scene in three js inspector extension. If I console log at the animate/update functions they all fire.

This is the react component copied from this SO answer which originally worked.

import React, { Component } from 'react'
import * as THREE from 'three'

import testCube from "./viz/testCube"
import VizHandler from './VizHandler'


class Visualizer extends 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 vizHandler = this.vizHandler = new VizHandler();
    vizHandler.init(this.mount);
    this.start()
  }

  componentWillUnmount() {
    this.stop()
    // this.mount.removeChild(threeApps['vizholder'].getRenderer().domElement)
    // vizHandler.unmount(this.mount);
  }

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

  stop() {
    cancelAnimationFrame(this.frameId)
  }

  animate() {
    this.vizHandler.update(); // update / animate stuff in here

    this.vizHandler.render()
    this.frameId = window.requestAnimationFrame(this.animate)
  }

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

export default Visualizer

I handle multiple threejs scenes/canvases here in the VizHandler function/class

import * as THREE from "three";
import threeApp from "./threeApp";
import WhiteRing from "./viz/WhiteRing";
import testCube from "./viz/testCube";
import Bars from "./viz/Bars";


const VizHandler = function () {

  var threeApps = [];
  var rings = new WhiteRing();
  var bars = new Bars();
  var cube = new testCube();

  function init(mount) {


      threeApps['vizholder'] = new threeApp();
      // threeApps['overlay'] = new threeApp();
      threeApps['vizholder'].init(mount, 'VizHolder') ;
      // threeApps['overlay'].init(mount, 'Overlay');

      window.scene = threeApps['vizholder'].getScene();
      window.THREE = THREE;

      const vizHolder = threeApps['vizholder'].getVizHolder();
      rings.init( vizHolder );
      bars.init( vizHolder );
      cube.init( vizHolder );

  }

  function update() {
    rings.update();
    bars.update();
    cube.update();
  }

  function render() {
    threeApps["vizholder"].render();
  }


  return {
      init: init,
      update: update,
      render: render,
      getVizHolder: function( name ) {
          if ( name ) return threeApps[name].getVizHolder();
          else return threeApps['vizholder'].getVizHolder();
      },
      getRenderer: function( name ) {
          if ( name ) return threeApps[name].getRenderer();
          else return threeApps['vizholder'].getRenderer();
      },
      getThreeApps: function() {
          return threeApps
      },
  };

};

And this is the most basic cube to that I'm trying to render.

import * as THREE from 'three';

const testCube = function () {

  let cube;

  function init (vizHolder) {

      const groupHolder = new THREE.Object3D();
      groupHolder.name = "TemplateViz";
      vizHolder.add(groupHolder);

      const geometry = new THREE.BoxGeometry(1, 1, 1)
      const material = new THREE.MeshBasicMaterial({ color: '#ffffff' })
      cube = new THREE.Mesh(geometry, material)
      cube.name = "TESTCUBE";
      groupHolder.add(cube);
      vizHolder.add(groupHolder);

  }

  function update() {
    cube.rotation.x += 0.01
    cube.rotation.y += 0.01
  }

  return {
    init: init,
    update: update,
  }

};

export default testCube;

And the result of all this is a renderer element in the dom that does not render my elements to the scene. I can change the renderer color and update the objects in the scene but I cannot make them appear.

EDIT: After solving the problem in my more simplified answer here, I forgot to post 1 important file where the error eventually occurred. The threeApp class where I make scenes, renderers and camera's. The code below was the non working code.

import * as THREE from "three";

function threeApp() {

  let camera, scene, renderer;
  let groundLight, skyLight, hemiLight;
  let vizHolder;
  let objName;
  let renderColor = '#909009';

  function init(mount, name) {

      objName = name;
      this.holderName = name;

      renderer = new THREE.WebGLRenderer({
          // antialias: true,
          // alpha: true
      });
      renderer.setSize(800, 600);

      renderer.domElement.className = "renderer";
      mount.appendChild(renderer.domElement);

      //3D SCENE
      camera = new THREE.PerspectiveCamera(70, 800 / 600, 1, 3000);
      camera.position.z = 1000;
      camera.name = "camera";


      scene = new THREE.Scene();
      // window.THREE = THREE;
      scene.name = name + " scene";
      console.log( 'name', name );
      if ( name == "VizHolder") {
          window.scene = scene;
      }
      camera.lookAt(scene);
      scene.fog = new THREE.Fog(0xffd8e5, 1000, 3000);
      scene.add(camera);

      //INIT VIZ
      vizHolder = new THREE.Object3D();
      vizHolder.name = name;

      scene.add(vizHolder);
  }


  function update () {
        render();
  }

  function render() {
    renderer.render(scene, camera);
  }

  function onResize () {

      var renderW = window.innerWidth;
      var renderH = window.innerHeight;
      camera.aspect = renderW / renderH;
      camera.updateProjectionMatrix();
      renderer.setSize(renderW, renderH);
  }

  return {
    init: init,
    render: render,
    onResize: onResize,
    getVizHolder: function() {
        return vizHolder;
    },
    getCamera: function() {
        return camera;
    },
    getScene: function() {
        return scene;
    },
    getRenderer: function() {
        return renderer;
    },
    setRenderColor: setRenderColor
}

};

export default threeApp
HermanChan
  • 65
  • 1
  • 8
  • Ok so as I said I could see the objects in the scene via the chrome three js extension. As soon as I tweak the camera settings, the scene starts getting rendered with the animations. This only happens with the camera, no other object starts the rendering. Maybe something with asynchronicity going on here? – HermanChan Jan 12 '18 at 19:24

0 Answers0