The GLTF file is loaded and looks nice, but I want to click on it and see if it has any information for example an uuid. The code below trickers a bug, saying TypeError: Cannot set property 'x' of undefined, when I click on the viewer. Can anybody tell me why this.mouse.x gives me an error and how I can click on the loaded GLTF objects and receive some piece of information from it? I have added the code below and a copied the error below as well. Hope that someone can help me out here.
TypeError: Cannot set property 'x' of undefined
HTMLCanvasElement.onClick C:/Users/alikuc/Desktop/codingProjects/IFCtoFSO/server-react/client/src/components/Viewer.js:65
62 | function onClick(event) {
63 | event.preventDefault();
64 |
65 | this.mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
| ^
| this.mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
67 |
68 | this.raycaster.setFromCamera(this.mouse, this.camera);
import React, { Component } from 'react';
import * as THREE from 'three';
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls';
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader';
import filePath from '../assets/02.00.04_test8.gltf';
export default class Viewer extends Component {
componentDidMount() {
//Add Scene
this.scene = new THREE.Scene();
//Add Renderer
this.renderer = new THREE.WebGLRenderer({ antialias: true });
this.renderer.setClearColor('#808080');
this.renderer.shadowMap.enabled = true;
this.renderer.shadowMap.type = THREE.PCFSoftShadowMap;
this.renderer.setPixelRatio(window.devicePixelRatio);
this.renderer.setSize(window.innerWidth, window.innerHeight);
this.mount.appendChild(this.renderer.domElement);
//Add Camera
const fov = 60;
const aspect = window.innerWidth / window.innerHeight;
const near = 1.0;
const far = 1000.0;
this.camera = new THREE.PerspectiveCamera(fov, aspect, near, far);
this.camera.position.set(45, aspect, 1, 1000);
// //Add Geometry and material
// const cube = new Mesh(new THREE.BoxGeometry(5, 5, 5), new THREE.MeshBasicMaterial({ color: '#0F0' }));
// // //Add mesh which is also the model
// this.scene.add(cube);
// Load GLTF file
// Instantiate a loader
const loader = new GLTFLoader();
/// Load a glTF resource
loader.load(
filePath,
(gltf) => {
this.scene.add(gltf.scene);
},
(xhr) => {
console.log((xhr.loaded / xhr.total) * 100 + '% loaded');
},
(error) => {
console.log('An error happened');
console.log(error);
}
);
//Add raycaster to for interactivity
this.raycaster = new THREE.Raycaster();
this.mouse = new THREE.Vector2();
this.renderer.domElement.addEventListener('click', onClick, false);
function onClick(event) {
event.preventDefault();
this.mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
this.mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
this.raycaster.setFromCamera(this.mouse, this.camera);
var intersects = this.raycaster.intersectObjects(this.scene.children, true);
if (intersects.length > 0) {
console.log('Intersection:', intersects[0]);
}
}
//Settings
//Add Camera Controls
const controls = new OrbitControls(this.camera, this.renderer.domElement);
controls.addEventListener('change', this.render); // use if there is no animation loop
controls.minDistance = 2;
controls.maxDistance = 10;
controls.target.set(0, 0, -0.2);
controls.update();
///Add AMBIENT LIGHT
let light = new THREE.DirectionalLight(0xffffff, 1.0);
light.position.set(20, 100, 10);
light.target.position.set(0, 0, 0);
light.castShadow = true;
light.shadow.bias = -0.001;
light.shadow.mapSize.width = 2048;
light.shadow.mapSize.height = 2048;
light.shadow.camera.near = 0.1;
light.shadow.camera.far = 500.0;
light.shadow.camera.near = 0.5;
light.shadow.camera.far = 500.0;
light.shadow.camera.left = 100;
light.shadow.camera.right = -100;
light.shadow.camera.top = 100;
light.shadow.camera.bottom = -100;
this.scene.add(light);
light = new THREE.AmbientLight(0xffffff, 0.7);
this.scene.add(light);
//Start animation
this.start();
}
//Unmount when animation has stopped
componentWillUnmount() {
this.stop();
this.mount.removeChild(this.renderer.domElement);
}
//Function to start animation
start = () => {
//Rotate Models
if (!this.frameId) {
this.frameId = requestAnimationFrame(this.animate);
}
};
//Function to stop animation
stop = () => {
cancelAnimationFrame(this.frameId);
};
//Animate models here
animate = () => {
//ReDraw scene with camera and scene object
//if (this.cubeMesh) this.cubeMesh.rotation.y += 0.01;
this.renderScene();
this.frameId = window.requestAnimationFrame(this.animate);
};
//Render the scene
renderScene = () => {
if (this.renderer) this.renderer.render(this.scene, this.camera);
};
render() {
return (
<div
style={{ width: '800px', height: '800px' }}
ref={(mount) => {
this.mount = mount;
}}
/>
);
}
}
That's because the scope of this
changes when you're inside the onClick
function.
Try adding this
to that function's scope by adding .bind(this)
this.renderer.domElement.addEventListener('click', onClick.bind(this), false);