Combining GLTFLoader() with Raycaster

Hi together!

I try to combine an object imported with gltfloader with a raycaster to get the ability of onclick events.

My loading procedure works very well, I am also able to load my gltf file and steer single objects of it. I built another example where the raycasting works how I want it to work. But when I merge them like this, the projector variable in the code below makes problems. I think there is a conflict between the three.min.js import and the three.js import. How can I fix it?

<head>
	<title>IMPORTER</title>
	<style>
		body { margin: 0; }
		canvas { width: 100%; height: 100% }
	</style>

		<script type="text/javascript" src="js/three.min.js"></script>
		<script type="text/javascript" src="js/rStats.js"></script>
		<script type="text/javascript" src="js/Detector.js"></script>
		<script type="text/javascript" src="js/TrackballControls.js"></script>
	<script type="module" src="https://threejs.org/build/three.js"></script>
	<script type="module">
		import { GLTFLoader } from "https://threejsfundamentals.org/threejs/resources/threejs/r119/examples/jsm/loaders/GLTFLoader.js";

		//variables
    		var camera, scene, renderer;
    		var rs;
    		var mesh;
    		var mesh_scale = 0.999;
    		var helper_mesh;
    		var projector;
    		var intersect_point = undefined;
    		var last_mouse_x = Infinity, last_mouse_y = Infinity;
    		var spin_speed = 1.0;
		var unbelegt = true;

		//model	
		let model;

		//object
		var object = new THREE.Object3D();

		//testing console
		console.log('Here we go!');

		// Instantiate a loader
		var loader = new GLTFLoader();
		
		//built scene , camera , renderer
		var scene = new THREE.Scene();
		scene.background = new THREE.Color( 0xffffff );
		var camera = new THREE.PerspectiveCamera( 40, window.innerWidth / window.innerHeight, 1, 100 );
			camera.position.set( 1, 2, 18 );

		var renderer = new THREE.WebGLRenderer();
		renderer.setSize( window.innerWidth, window.innerHeight );
		renderer.outputEncoding = THREE.sRGBEncoding;
		document.body.appendChild( renderer.domElement );

		// Load a glTF resource
		loader.load(
			// resource URL
			'test4.gltf',
			// called when the resource is loaded
			function ( gltf ) {

				model = gltf.scene;
				scene.add(model);

				object = scene.getObjectByName("Cube");

				scene.add( gltf.scene );
				gltf.animations; // Array<THREE.AnimationClip>
				gltf.scene; // THREE.Group
				gltf.scenes; // Array<THREE.Group>
				gltf.cameras; // Array<THREE.Camera>
				//camera = gltf.cameras[0];
				gltf.asset; // Object

			},
			// called while loading is progressing
			function ( xhr ) {

				console.log( ( xhr.loaded / xhr.total * 100 ) + '% loaded' );

			},
			// called when loading has errors
			function ( error ) {
	
				console.error( 'An error happened:', error );

			}
		);

		//click
		
		//Problem: projector cannot be initialized
		//projector = new THREE.Projector();
		//something like mesh = object; ...

    		function onWindowResize() {
    		    camera.aspect = window.innerWidth / window.innerHeight;
    		    camera.updateProjectionMatrix();
    		    renderer.setSize(window.innerWidth, window.innerHeight);
    		}

    		function onDocumentMouseMove(event) {
    		    last_mouse_x = (event.clientX / window.innerWidth) * 2 - 1;
    		    last_mouse_y = -(event.clientY / window.innerHeight) * 2 + 1;
	            event.preventDefault();
        		    checkIntersect();
			    }

  			function checkIntersect() {
    		    camera.aspect = window.innerWidth / window.innerHeight;
    		    camera.updateProjectionMatrix();
    		    renderer.setSize(window.innerWidth, window.innerHeight);
    		    var vector = new THREE.Vector3(last_mouse_x, last_mouse_y, -1);
    		    projector.unprojectVector(vector, camera);
    		    var raycaster = new THREE.Raycaster(camera.position, vector.sub(camera.position).normalize());
    		    var intersects = raycaster.intersectObject(mesh, true);
    		    intersect_point = undefined;
    		    if (intersects.length > 0) {
    		        if (intersects[0].point !== null ) {
    		            helper_mesh.position.copy(intersects[0].point);
    		            intersect_point = intersects[0].point;
    		        } 
    		    } else {
    		        helper_mesh.position.set(Infinity, 0, 0);
    		    }
    		}

		function clicker() {
			if ( typeof intersect_point !== "undefined" && unbelegt ) {
				unbelegt = false;
				for (var i = 10; i < 36; i++) {
					setTimeout(function(){ mesh.rotation.x += 0.03; if(i==36){unbelegt = true;}}, i*50);
					}
				}
			}

		//show animation
		var animate = function () {
			requestAnimationFrame( animate );
			renderer.render( scene, camera );
			if (model) {
			    	//model.rotation.x += 0.01;
			    	//model.rotation.y += 0.01;
			    	//model.rotation.z += 0.01;
			}
			if (object) {
				object.rotation.x += 0.01;
			}
		};

		animate();

	</script>
</head>
<body>
</body>

Well, you can fix it by … not doing it :thinking:? Why do you need to import three twice (from 2 different places, which give you about 99.9% chance of loading 2 different versions of three within a week or two) ?

(Not to mention you load GLTFLoader from a separate CDN, so in the end you’ll have 3 separate versions of three code in your imports :’) )

Edit:
(1) If you are using an external CDN for loading three (not a perfect idea, but who am I to judge) - use one. Take a look at this example. Three is loaded via HTML script tags, and loaded addons populate the global THREE object.

(2) If you’re using local imports (ex. src="js/Detector.js" `), once again, use only them. Don’t use CDNs, if something is missing - download it and import locally.

(3) If you are using a bundler, just use npm install to install / add anything to your project (and don’t mix with CDNs or <script> tags. Just use import in JS.)

1 Like

jsm exports assume that three is present at “…/…/…/build/three.module.js”. they did it to make the examples work.

using script tags and copying code around manually is only going to cause you agony, and waste your time. use something like parcel or whatever bundler you like, npm install your dependencies and you are ready in seconds with a production ready environment.

1 Like

Thank you for your response! I solved the problem. But because of my limited skills, it was not so easy. I had npm already installed and imported all packages as you said. Unfortunately Projector.js is not modularized and cannot be imported. So I had to import it as text/javascript in a seperate script-tag. Because of the dependencies to three.js I had to import this before in an additional script-tag as well. Now it works! Thank you for your time :wink:

You are right. Yesterday I was a little bit desperate because nothing seemed to work. I solved it with local installations (npm) and as described below with the right import procedure.

in case you still have that one script tag with the gltfloader, always pull from examples/jsm, not js. these are all real modules. they kind of break npm convention, but it works.

I meant Projctor not Gitloader… I edited it :slight_smile: