question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Trying to use an Expo Three Raycaster but intersects are empty or wrong

See original GitHub issue

I’m trying to intersect meshes with Three Raycaster but for some reason the default array of intersects is defaulting to 10 meshes and when I “touch” the intersects array becomes empty. Does Raycaster not work with your version of THREE? I investigated the NPM package, specifically it is loading THREE but when I look closer I don’t see an instance of THREE inside npm_modules. Here’s the code

Snack: https://snack.expo.io/@adriaanbalt/raycaster-using-expo-three

import Expo from "expo";
import React from "react";
import { Dimensions, View, Animated, PanResponder } from "react-native";
import ExpoTHREE, { THREE } from "expo-three"; // 3.0.0-alpha.4

import MeshContainer from "./MeshContainer";

export default class App extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      pan: new Animated.ValueXY(),
      mouse: new THREE.Vector2(),
    };
    THREE.suppressExpoWarnings(true);
  }

  componentWillMount() {
    this._val = { x: 0, y: 0 };
    this.state.pan.addListener(value => (this._val = value));

    this.panResponder = PanResponder.create({
      onStartShouldSetPanResponder: (e, gesture) => true,
      onPanResponderGrant: (e, gesture) => {
        this.state.pan.setOffset({
          x: this._val.x,
          y: this._val.y
        });
        this.state.pan.setValue({ x: -1, y: -1 });
      },
      onPanResponderMove: ({ nativeEvent }, gestureState) => {
        this.state.mouse.x = nativeEvent.locationX;
        this.state.mouse.y = nativeEvent.locationY;
      },
    });

  }

  render() {
    return (
      <View
        {...this.panResponder.panHandlers}
        style={[
          {
            width: Dimensions.get("window").width,
            height: Dimensions.get("window").height
          }
        ]}
      >
        <Expo.GLView
          style={{ flex: 1 }}
          onContextCreate={this._onGLContextCreate}
        />
      </View>
    );
  }

  _onGLContextCreate = async gl => {
    const scene = new THREE.Scene();

    const raycaster = new THREE.Raycaster(); // is this correct?

    const camera = new THREE.PerspectiveCamera(
      100,
      gl.drawingBufferWidth / gl.drawingBufferHeight,
      1,
      10000
    );
    camera.position.z = 2;

    const renderer = new ExpoTHREE.Renderer({ gl });
    renderer.setSize(gl.drawingBufferWidth, gl.drawingBufferHeight);

    const Container = new MeshContainer();
    const containerMesh = Container.getMesh();
    scene.add(containerMesh);

    let objects = []
    objects.push(containerMesh);

    let INTERSECTED;

    const animate = p => {
      requestAnimationFrame(animate);

      raycaster.setFromCamera( this.state.mouse, camera );
      let intersects = raycaster.intersectObjects( objects );
      // let intersects = raycaster.intersectObjects( scene.children ); // this doesn't seem to make any difference

      console.log( 'intersects length: ', intersects.length ); // defaults to 10 before touch and after touch becomes 0
      
      if ( intersects.length > 0 ) {
            if ( INTERSECTED != intersects[ 0 ].object ) {
                INTERSECTED = intersects[ 0 ].object;
            }
        } else {
            INTERSECTED = null;
        }

      renderer.render(scene, camera);

      gl.endFrameEXP();
    };
    animate();
  };
}

MeshContainer.js

import React from "react";
import ExpoTHREE, { THREE } from "expo-three"; // 3.0.0-alpha.4

export default class MeshContainer extends React.Component {

    constructor() {
        super();
        let geometry = new THREE.CircleGeometry(.25, 10);
        let material = new THREE.MeshBasicMaterial({
            color: 0xFF00FF,
            opacity: 1
        });
        this.mesh = new THREE.Mesh(geometry, material);
    }

    getMesh() {
        // this.mesh.superName = 'MovingLetter'
        return this.mesh
    }

}

Issue Analytics

  • State:closed
  • Created 5 years ago
  • Comments:7 (1 by maintainers)

github_iconTop GitHub Comments

1reaction
chira37commented, Apr 22, 2020
import Expo from "expo";
import { GLView } from "expo-gl";
import ExpoTHREE, { THREE } from "expo-three"; // 3.0.0-alpha.4
import React from "react";
import { connect } from "react-redux";
import {
	View,
	Animated,
	Text,
	Dimensions,
	PanResponder,
} from "react-native";

const { height, width } = Dimensions.get("window");

class HomeScreen extends React.Component {

	constructor(props) {
		super(props);

		this.state = {
			pan: new Animated.ValueXY(),
			mouse: new THREE.Vector2(-10, -10), // -10 is to force the start position to be OFF the screen so the user isn't automatically ontop of something
		};
		// Turn off extra warnings
		THREE.suppressExpoWarnings(true);
		// hide warnings yellow box in ios
		console.disableYellowBox = true;
	}

	componentWillMount() {
		this._val = { x: 0, y: 0 };
		this.state.pan.addListener((value) => (this._val = value));

		this.panResponder = PanResponder.create({
			onStartShouldSetPanResponder: (e, gesture) => true,
			onPanResponderGrant: (e, gesture) => {
				this.state.pan.setOffset({
					x: this._val.x,
					y: this._val.y,
				});
				this.state.pan.setValue({ x: -10, y: -10 });
			},
			onPanResponderMove: ({ nativeEvent }, gestureState) => {
				if (this.state.gl) {
					// ratio of mouse position to the width of the screen
					this.state.mouse.x =
						(nativeEvent.locationX / width) * 2 - 1;
					this.state.mouse.y =
						-(nativeEvent.locationY / height) * 2 + 1;
				}
			},
			onPanResponderRelease: ({ nativeEvent }, gestureState) => {
				this.state.mouse.x = -10;
				this.state.mouse.y = -10;
			},
		});
	}

	_onGLContextCreate = async (gl) => {
		const renderer = new ExpoTHREE.Renderer({ gl, depth: false });
		renderer.setPixelRatio(window.pixelRatio || 1);
		renderer.setSize(gl.drawingBufferWidth, gl.drawingBufferHeight);
		renderer.setClearColor(0x000000, 1.0);
		const camera = new THREE.PerspectiveCamera(
			100,
			gl.drawingBufferWidth / gl.drawingBufferHeight,
			0.1,
			20000,
		);
		camera.position.set(0, 0, 1.0).multiplyScalar(20);
		const raycaster = new THREE.Raycaster();
		const scene = new THREE.Scene();

		const yourMesh = // Your mesh
		scene.add(yourMesh);


		let intersects;

		const over = () => {
		};
		const out = () => {
		};

		const animate = (p) => {
			requestAnimationFrame(animate);

			camera.updateMatrixWorld();

			raycaster.setFromCamera(this.state.mouse, camera);
			intersects = raycaster.intersectObjects(scene.children, true);

			if (intersects.length > 0) {
				// OVER
                over()
			} else {
				// NOT OVER
                out()
			}

			renderer.render(scene, camera);

			gl.endFrameEXP();
		};

		animate();
	};

	render() {
		const { height, width } = Dimensions.get("window");
		return (
			<View
				{...this.panResponder.panHandlers}
				style={[
					{
						width,
						height,
					},
				]}>
				<GLView
					style={{ flex: 1 }}
					onContextCreate={this._onGLContextCreate}
				/>
			</View>
		);
	}

}

export default Screen

This is where I ended up, give or take @nik-sloop

I haven’t compared this to what is above, but I’ve been using the above snippet (with some other stuff) in published apps on the app store.

This is working perfectly, save lots of time Thanks👍👍👍

1reaction
EvanBaconcommented, Sep 24, 2018

?? You shouldn’t directly mutate a components state:

this.setState({ gl });

// instead of 

this.state.gl = gl;

If you want to save the variable without updating the component, then use some other variable:

this._gl = gl;
Read more comments on GitHub >

github_iconTop Results From Across the Web

Three.js raycast produces empty intersects array
I have a 3D Array of cubes which populates and displays fine but when my mouse down function is called, the intersect array...
Read more >
Three.js Picking
Probably the most common way of picking is by doing raycasting which means to cast a ray from the mouse through the frustum...
Read more >
react-three-fiber-cambrian - npm package | Snyk
This hooks gives you access to all the basic objects that are kept internally, like the default renderer, scene, camera. It also gives...
Read more >
Typeerror is not a function typescript - Seba Online
Sep 08, 2021 · But running the script throws a runtime error: TypeError: callback is not a function. register is not a function...
Read more >
Why Raycast with GLTF not working - Questions - three.js forum
This is my code i don't know why array of intersects is empty after intersectObjects and raycasting is not working import * as...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found