<!doctype html> <html> <head> <title>Mouse-picking Collada models with Three.js</title> <meta charset="utf-8"> <style> body { background-color: #f0f0f0; margin: 0; overflow: hidden; font-family: monospace; font-size: 13px; text-align: center; font-weight: bold; } a { color: #0078ff; } #info { color: #000000; position: absolute; top: 0; width: 100%; padding: 5px; z-index: 100; } #stats { position: absolute; right: 10px; top: 5px; color: #fff; text-align: left; background: rgba(0, 0, 0, 0.5); padding: 10px; width: 200px; height: 60px; border: solid 1px black; border-radius: 5px; } </style> </head> <body> <div id="info"> - mouse-picking COLLADA models with <a href="http://github.com/mrdoob/three.js" target="_blank">three.js</a> -<br> crate model by <a href="http://www.turbosquid.com/FullPreview/Index.cfm/ID/631645" target="_blank">DeYogbar</a><br> Move around using WASD and click on objects. </div> <div id="stats"></div> <script src="threejs_r62/build/three.min.js"></script> <script src="threejs_r62/ColladaLoader.js"></script> <script src="threejs_r62/FirstPersonControls.js"></script> <script src="three.js/js/Detector.js"></script> <script src="three.js/js/Stats.js"></script> <script> if (!Detector.webgl) Detector.addGetWebGLMessage(); var container, stats; var camera, controls, scene, renderer; var clock = new THREE.Clock(); var raycaster = new THREE.Raycaster(); var projector = new THREE.Projector(); var directionVector = new THREE.Vector3(); var SCREEN_HEIGHT = window.innerHeight; var SCREEN_WIDTH = window.innerWidth; var clickInfo = { x: 0, y: 0, userHasClicked: false }; var statsNode = document.getElementById('stats'); var marker; init(); animate(); function init () { container = document.createElement('div'); document.body.appendChild(container); container.addEventListener('click', function (evt) { // The user has clicked; let's note this event // and the click's coordinates so that we can // react to it in the render loop clickInfo.userHasClicked = true; clickInfo.x = evt.clientX; clickInfo.y = evt.clientY; }, false); // we just do the following to hide the event from controls // and disable moving via mouse buttons var stopEvent = function (evt) { evt.preventDefault(); evt.stopPropagation(); }; container.addEventListener('mousedown', stopEvent, false); container.addEventListener('mouseup', stopEvent, false); /* Scene & Camera */ scene = new THREE.Scene(); camera = new THREE.PerspectiveCamera(25, SCREEN_WIDTH / SCREEN_HEIGHT); scene.add(camera); /* Controls */ controls = new THREE.FirstPersonControls(camera); controls.constrainVertical = true; controls.movementSpeed = 60; controls.lookSpeed = 0.05; /* Renderer */ renderer = new THREE.WebGLRenderer({ antialias: true, preserveDrawingBuffer: true }); renderer.setSize(SCREEN_WIDTH, SCREEN_HEIGHT); container.appendChild(renderer.domElement); /* Lights */ var ambientLight = new THREE.AreaLight(0xffffff); scene.add(ambientLight); var directionalLight = new THREE.DirectionalLight(0xe0e0e0); directionalLight.position.set(1, 1, 0.5).normalize(); scene.add(directionalLight); directionalLight = new THREE.DirectionalLight(0xe0e0e0); directionalLight.position.set(-1, 1, 0.5).normalize(); scene.add(directionalLight); /* Ground */ var plane = new THREE.Mesh(new THREE.PlaneGeometry(1000, 1000, 10, 10), new THREE.MeshBasicMaterial({ color: 0x808080, wireframe: true })); plane.rotation.x = -Math.PI / 2; plane.name = 'Ground'; scene.add(plane); /* Collada Objects */ createBoxes(); /* hit point marker */ marker = new THREE.Mesh(new THREE.SphereGeometry(1), new THREE.MeshLambertMaterial({ color: 0xff0000 })); scene.add(marker); } function createBoxes () { var boxCount = 20; var loader = new THREE.ColladaLoader(); loader.options.convertUpAxis = true; loader.load('models/WoodenBox02/WoodenBox02.dae', function (collada) { var objectProto = collada.scene; for (var i = 0; i < boxCount; i++) { var object = objectProto.clone(); object.name = 'Box #' + i; object.position.x = Math.random() * 800 - 400; object.position.y = 5; object.position.z = Math.random() * 800 - 400; object.rotation.y = ( Math.random() * 360 ) * Math.PI / 180; object.scale.x = 10; object.scale.y = 10; object.scale.z = 10; scene.add(object); } }); } function animate () { var delta = clock.getDelta(); requestAnimationFrame(animate); render(delta); } function render (delta) { if (clickInfo.userHasClicked) { clickInfo.userHasClicked = false; statsNode.innerHTML = ''; // The following will translate the mouse coordinates into a number // ranging from -1 to 1, where // x == -1 && y == -1 means top-left, and // x == 1 && y == 1 means bottom right var x = ( clickInfo.x / SCREEN_WIDTH ) * 2 - 1; var y = -( clickInfo.y / SCREEN_HEIGHT ) * 2 + 1; // Now we set our direction vector to those initial values directionVector.set(x, y, 1); // Unproject the vector projector.unprojectVector(directionVector, camera); // Substract the vector representing the camera position directionVector.sub(camera.position); // Normalize the vector, to avoid large numbers from the // projection and substraction directionVector.normalize(); // Now our direction vector holds the right numbers! raycaster.set(camera.position, directionVector); // Ask the raycaster for intersects with all objects in the scene: // (The second arguments means "recursive") var intersects = raycaster.intersectObjects(scene.children, true); if (intersects.length) { // intersections are, by default, ordered by distance, // so we only care for the first one. The intersection // object holds the intersection point, the face that's // been "hit" by the ray, and the object to which that // face belongs. We only care for the object itself. var target = intersects[0].object; statsNode.innerHTML = 'Name: ' + target.name + '<br>' + 'ID: ' + target.id; // let's move the marker to the hit point marker.position.x = intersects[0].point.x; marker.position.y = intersects[0].point.y; marker.position.z = intersects[0].point.z; } } controls.update(delta); camera.position.y = 20; renderer.render(scene, camera); } </script> <script type="text/javascript"> var _gaq = _gaq || []; _gaq.push(['_setAccount', 'UA-10931011-2']); _gaq.push(['_trackPageview']); (function () { var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true; ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js'; var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s); })(); </script> </body> </html>