3

I'm adding objects to a scene for each entry in a database. I had a cube appearing in the scene for entry, but when I tried to add raycasting to click on objects it doesnt work, the objects dont appear and the console reads "Expression unavailable".. I got parts of the code from the three.js website for Raycasting so not sure what I'm doing wrong.

Here is the JS code:

var renderer, scene, container, camera;
var geometry, material;
var controls, group;

var raycaster = new THREE.Raycaster();
var mouse = new THREE.Vector2();


init()

function onMouseMove( event ) {

    // calculate mouse position in normalized device coordinates
    // (-1 to +1) for both components

    mouse.x = ( event.clientX / window.innerWidth ) * 2 - 1;
    mouse.y = - ( event.clientY / window.innerHeight ) * 2 + 1;

}

function init() {
    // init renderer
    renderer = new THREE.WebGLRenderer( { antialias: true } );
    renderer.setPixelRatio( window.devicePixelRatio );
    renderer.setSize( window.innerWidth, window.innerHeight );
    // document.body.appendChild( renderer.domElement );

    container = document.getElementById('container');
    container.appendChild( renderer.domElement );

    // init scene
    scene = new THREE.Scene();
    scene.background = new THREE.Color( 0xffffff );

    group = new THREE.Group();
    scene.add( group )

    //fetch data from database and add object for each entry
    getData()
    async function getData() {
        var response = await fetch('/api/indexvr');
        var data = await response.json();
        console.log(data) 

        for (var i=0; i<data.length; i++) {
            cube = new THREE.Mesh( geometry, material );
            cube.position.x = i;
            scene.add(cube);
            //group.add(data)
        }
    }

    // init camera
    camera = new THREE.PerspectiveCamera( 50, window.innerWidth / window.innerHeight, 1, 1000 );
    camera.position.set( 15, 15, 15 ); //camera.position.set( 5, 0, 10 );
    camera.lookAt( scene.position );
    // controls = new OrbitControls( camera, renderer.domElement );
    // controls.enableRotate = true;
}

function render() {

    // update the picking ray with the camera and mouse position
    raycaster.setFromCamera( mouse, camera );

    // calculate objects intersecting the picking ray
    var intersects = raycaster.intersectObjects( scene.children );

    for ( var i = 0; i < intersects.length; i++ ) {

        intersects[ i ].object.material.color.set( 0xff0000 );

    }

    renderer.render( scene, camera );

}

window.addEventListener( 'mousemove', onMouseMove, false );

window.requestAnimationFrame(render);

The HTML just has a div called "container" and this tag:

<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/108/three.js"></script>

It doesn't produce any error it only says this in the console: enter image description here

So it is fetching the data but can't render the scene

var renderer, scene, container, camera;
var geometry, material;
var controls, group;

var raycaster = new THREE.Raycaster();
var mouse = new THREE.Vector2();


init()

function onMouseMove(event) {

  // calculate mouse position in normalized device coordinates
  // (-1 to +1) for both components

  mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
  mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;

}

function init() {
  // init renderer
  renderer = new THREE.WebGLRenderer({
    antialias: true
  });
  renderer.setPixelRatio(window.devicePixelRatio);
  renderer.setSize(window.innerWidth, window.innerHeight);
  // document.body.appendChild( renderer.domElement );

  container = document.getElementById('container');
  container.appendChild(renderer.domElement);

  // init scene
  scene = new THREE.Scene();
  scene.background = new THREE.Color(0xffffff);


  group = new THREE.Group();
  scene.add(group)

  //fetch data from database and add object for each entry
  getData()
  async function getData() {
    /**
     * @author TheJim01
     * Replacing DB call with fake data to make it work here.
     * Nancy: Please feel free to add appropriate data.
     */
    // var response = await fetch('/api/indexvr');
    // var data = await response.json();
    var data = [{}, {}, {}, {}, {}]
    console.log(data)

    for (var i = 0; i < data.length; i++) {
      cube = new THREE.Mesh(geometry, material);
      cube.position.x = i;
      scene.add(cube);
      //group.add(data)
    }
  }

  // init camera
  camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 1, 1000);
  camera.position.set(15, 15, 15); //camera.position.set( 5, 0, 10 );
  camera.lookAt(scene.position);
  // controls = new OrbitControls( camera, renderer.domElement );
  // controls.enableRotate = true;
}

function render() {

  // update the picking ray with the camera and mouse position
  raycaster.setFromCamera(mouse, camera);

  // calculate objects intersecting the picking ray
  var intersects = raycaster.intersectObjects(scene.children);

  for (var i = 0; i < intersects.length; i++) {

    intersects[i].object.material.color.set(0xff0000);

  }

  renderer.render(scene, camera);

}

window.addEventListener('mousemove', onMouseMove, false);

window.requestAnimationFrame(render);
<script src="//threejs.org/build/three.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/108/three.js"></script>
<script src="https://unpkg.com/spritejs/dist/spritejs.min.js"></script>

<div id="container"></div>
Nancy Collins
  • 197
  • 4
  • 13
  • Please post the full text of the error message. – TheJim01 Sep 27 '19 at 13:51
  • There actually isn't any error message, I've added a screenshot of the console message. – Nancy Collins Sep 27 '19 at 13:56
  • When I comment out the window methods like so: ``` // window.addEventListener( 'mousemove', onMouseMove, false ); // window.requestAnimationFrame(render); ``` The background of the scene renders but not the cubes and the console produces exactly the same message.. – Nancy Collins Sep 27 '19 at 13:57
  • 1
    I'm not seeing the "Expressions: not available" message in my browser when I run the snippet. You may have some kind of `watch` enabled in your debugger? Also, just a note, You only need one ` – TheJim01 Sep 27 '19 at 14:08
  • Oh good to know thanks. Unusual that the message is exclusive to my browser, i dont have any debugger running.. – Nancy Collins Sep 27 '19 at 14:22
  • Is raycasting code actually functioning for you when you run the snippet? – Nancy Collins Sep 27 '19 at 14:24

2 Answers2

3

There are several things wrong that I can see. Whether that's because you omitted sections of your code, I can't say.

First, the code you provided does not define geometry, nor a material. You implied you're drawing a cube for each DB result, so I'll make an assumption and use BoxBufferGeometry. You also don't have any lights defined, so I'll just use MeshBasicMaterial which doesn't need lights.

With those out of the way, it looks like you're halfway to setting up a render loop using window.requestAnimationFrame, but you still only call render once, even though your DB fetching is asynchronous. In other words, the render might occur before you even get a response from your DB, so you would see nothing. I've added some boilerplate code to set up a render loop, similar to how three.js does in its examples.

Interestingly enough, that was all it took. The raycaster started working, and I was able to console log the results out. I did get some false-positives when the scene first started rendering, but that's because there hadn't been any mouse input yet, so it was raycasting from the middle of the screen (where the first cube exists).

Normally, you wouldn't want to raycast for every frame, but I understand the VR situation might be different (darn fidgety humans).

Finally, one last change I made was to give each cube its own material (well, clones of the original). This was necessary to ensure you could raycast against each one individually.

// Need to create geometry and material
var geometry = new THREE.BoxBufferGeometry(0.5, 0.5, 0.5);
var material = new THREE.MeshBasicMaterial({
  color: "green"
});

var renderer, scene, container, camera;
var controls, group;

var raycaster = new THREE.Raycaster();
var mouse = new THREE.Vector2();

init()

function onMouseMove(event) {

  // calculate mouse position in normalized device coordinates
  // (-1 to +1) for both components

  mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
  mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;

}

function init() {
  // init renderer
  renderer = new THREE.WebGLRenderer({
    antialias: true
  });
  renderer.setPixelRatio(window.devicePixelRatio);
  renderer.setSize(window.innerWidth, window.innerHeight);
  // document.body.appendChild( renderer.domElement );

  container = document.getElementById('container');
  container.appendChild(renderer.domElement);

  // init scene
  scene = new THREE.Scene();
  scene.background = new THREE.Color(0xffffff);


  group = new THREE.Group();
  scene.add(group)

  //fetch data from database and add object for each entry
  getData()
  async function getData() {
    /**
     * @author TheJim01
     * Replacing DB call with fake data to make it work here.
     * Nancy: Please feel free to add appropriate data.
     */
    // var response = await fetch('/api/indexvr');
    // var data = await response.json();
    var data = [{}, {}, {}, {}, {}]
    //console.log(data)

    for (var i = 0; i < data.length; i++) {
      cube = new THREE.Mesh(geometry, material.clone());
      cube.position.x = i;
      scene.add(cube);
      //group.add(data)
    }
  }

  // init camera
  camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 1, 1000);
  camera.position.set(15, 15, 15); //camera.position.set( 5, 0, 10 );
  camera.lookAt(scene.position);
  // controls = new OrbitControls( camera, renderer.domElement );
  // controls.enableRotate = true;
}

function render() {

  // update the picking ray with the camera and mouse position
  raycaster.setFromCamera(mouse, camera);

  // calculate objects intersecting the picking ray
  var intersects = raycaster.intersectObjects(scene.children);
  if (intersects.length > 0) {
    console.log(intersects);
  }

  for (var i = 0; i < intersects.length; i++) {

  intersects[i].object.material.color.set(0xff0000);
  

  }

  renderer.render(scene, camera);

}

window.addEventListener('mousemove', onMouseMove, false);

// Here's the bbasic render loop implementation
function animate() {
  requestAnimationFrame(animate);
  render();
}
animate();
html,
body {
  width: 100%;
  height: 100%;
  margin: 0;
  padding: 0;
  overflow: hidden;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/108/three.min.js"></script>

<div id="container"></div>
TheJim01
  • 8,411
  • 1
  • 30
  • 54
1

I think you never add three-orbitcontrols module. According to the three.js documentation we need to add orbit controls in a file separately.

https://threejs.org/docs/#examples/en/controls/OrbitControls

https://www.npmjs.com/package/three-orbitcontrols

Have you the OrbitControls.js file in your project?

datmau
  • 59
  • 4
  • Please provide detailed answer and then references. – Sachith Muhandiram Sep 27 '19 at 14:38
  • 1
    Hi Alexander. Welcome to Stack Overflow. Thank you for taking the initiative to take the tour. Regarding your answer, it is not a requirement to use any type of mouse controller in the three.js project, and Nancy has commented-out the lines referencing `OrbitControls`. For these reasons, including the `OrbitControls.js` file is not necessary. – TheJim01 Sep 27 '19 at 14:48