UNPKG

cannon

Version:

A lightweight 3D physics engine written in JavaScript.

339 lines (276 loc) 11.8 kB
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>cannon.js mouse pick demo</title> <style> html, body { width: 100%; height: 100%; margin:0; padding:0; overflow: hidden; } </style> </head> <body> <script src="../libs/Three.js"></script> <script src="../libs/Detector.js"></script> <script src="../build/cannon.js"></script> <script> var world; var dt = 1 / 60; var constraintDown = false; var camera, scene, renderer, gplane=false, clickMarker=false; var geometry, material, mesh; var controls,time = Date.now(); var jointBody, constrainedBody, mouseConstraint; var N = 1; var container, camera, scene, renderer, projector; // To be synced var meshes=[], bodies=[]; // Initialize Three.js if ( ! Detector.webgl ) Detector.addGetWebGLMessage(); initCannon(); init(); animate(); function init() { projector = new THREE.Projector(); container = document.createElement( 'div' ); document.body.appendChild( container ); // scene scene = new THREE.Scene(); scene.fog = new THREE.Fog( 0x000000, 500, 10000 ); // camera camera = new THREE.PerspectiveCamera( 30, window.innerWidth / window.innerHeight, 0.5, 10000 ); camera.position.set(10, 2, 0); camera.quaternion.setFromAxisAngle(new THREE.Vector3(0,1,0), Math.PI/2); scene.add(camera); // lights var light, materials; scene.add( new THREE.AmbientLight( 0x666666 ) ); light = new THREE.DirectionalLight( 0xffffff, 1.75 ); var d = 20; light.position.set( d, d, d ); light.castShadow = true; //light.shadowCameraVisible = true; light.shadowMapWidth = 1024; light.shadowMapHeight = 1024; light.shadowCameraLeft = -d; light.shadowCameraRight = d; light.shadowCameraTop = d; light.shadowCameraBottom = -d; light.shadowCameraFar = 3*d; light.shadowCameraNear = d; light.shadowDarkness = 0.5; scene.add( light ); // floor geometry = new THREE.PlaneGeometry( 100, 100, 1, 1 ); //geometry.applyMatrix( new THREE.Matrix4().makeRotationX( -Math.PI / 2 ) ); material = new THREE.MeshLambertMaterial( { color: 0x777777 } ); markerMaterial = new THREE.MeshLambertMaterial( { color: 0xff0000 } ); //THREE.ColorUtils.adjustHSV( material.color, 0, 0, 0.9 ); mesh = new THREE.Mesh( geometry, material ); mesh.castShadow = true; mesh.quaternion.setFromAxisAngle(new THREE.Vector3(1,0,0), -Math.PI / 2); mesh.receiveShadow = true; scene.add(mesh); // cubes var cubeGeo = new THREE.BoxGeometry( 1, 1, 1, 10, 10 ); var cubeMaterial = new THREE.MeshPhongMaterial( { color: 0x888888 } ); for(var i=0; i<N; i++){ cubeMesh = new THREE.Mesh(cubeGeo, cubeMaterial); cubeMesh.castShadow = true; meshes.push(cubeMesh); scene.add(cubeMesh); } renderer = new THREE.WebGLRenderer( { antialias: true } ); renderer.setSize( window.innerWidth, window.innerHeight ); renderer.setClearColor( scene.fog.color ); container.appendChild( renderer.domElement ); renderer.gammaInput = true; renderer.gammaOutput = true; renderer.shadowMapEnabled = true; window.addEventListener( 'resize', onWindowResize, false ); window.addEventListener("mousemove", onMouseMove, false ); window.addEventListener("mousedown", onMouseDown, false ); window.addEventListener("mouseup", onMouseUp, false ); } function setClickMarker(x,y,z) { if(!clickMarker){ var shape = new THREE.SphereGeometry(0.2, 8, 8); clickMarker = new THREE.Mesh(shape, markerMaterial); scene.add(clickMarker); } clickMarker.visible = true; clickMarker.position.set(x,y,z); } function removeClickMarker(){ clickMarker.visible = false; } function onMouseMove(e){ // Move and project on the plane if (gplane && mouseConstraint) { var pos = projectOntoPlane(e.clientX,e.clientY,gplane,camera); if(pos){ setClickMarker(pos.x,pos.y,pos.z,scene); moveJointToPoint(pos.x,pos.y,pos.z); } } } function onMouseDown(e){ // Find mesh from a ray var entity = findNearestIntersectingObject(e.clientX,e.clientY,camera,meshes); var pos = entity.point; if(pos && entity.object.geometry instanceof THREE.BoxGeometry){ constraintDown = true; // Set marker on contact point setClickMarker(pos.x,pos.y,pos.z,scene); // Set the movement plane setScreenPerpCenter(pos,camera); var idx = meshes.indexOf(entity.object); if(idx !== -1){ addMouseConstraint(pos.x,pos.y,pos.z,bodies[idx]); } } } // This function creates a virtual movement plane for the mouseJoint to move in function setScreenPerpCenter(point, camera) { // If it does not exist, create a new one if(!gplane) { var planeGeo = new THREE.PlaneGeometry(100,100); var plane = gplane = new THREE.Mesh(planeGeo,material); plane.visible = false; // Hide it.. scene.add(gplane); } // Center at mouse position gplane.position.copy(point); // Make it face toward the camera gplane.quaternion.copy(camera.quaternion); } function onMouseUp(e) { constraintDown = false; // remove the marker removeClickMarker(); // Send the remove mouse joint to server removeJointConstraint(); } var lastx,lasty,last; function projectOntoPlane(screenX,screenY,thePlane,camera) { var x = screenX; var y = screenY; var now = new Date().getTime(); // project mouse to that plane var hit = findNearestIntersectingObject(screenX,screenY,camera,[thePlane]); lastx = x; lasty = y; last = now; if(hit) return hit.point; return false; } function findNearestIntersectingObject(clientX,clientY,camera,objects) { // Get the picking ray from the point var raycaster = getRayCasterFromScreenCoord(clientX, clientY, camera, projector); // Find the closest intersecting object // Now, cast the ray all render objects in the scene to see if they collide. Take the closest one. var hits = raycaster.intersectObjects(objects); var closest = false; if (hits.length > 0) { closest = hits[0]; } return closest; } // Function that returns a raycaster to use to find intersecting objects // in a scene given screen pos and a camera, and a projector function getRayCasterFromScreenCoord (screenX, screenY, camera, projector) { var mouse3D = new THREE.Vector3(); // Get 3D point form the client x y mouse3D.x = (screenX / window.innerWidth) * 2 - 1; mouse3D.y = -(screenY / window.innerHeight) * 2 + 1; mouse3D.z = 0.5; return projector.pickingRay(mouse3D, camera); } function onWindowResize() { camera.aspect = window.innerWidth / window.innerHeight; camera.updateProjectionMatrix(); //controls.handleResize(); renderer.setSize( window.innerWidth, window.innerHeight ); } function animate() { requestAnimationFrame( animate ); //controls.update(); updatePhysics(); render(); } function updatePhysics(){ world.step(dt); for(var i=0; i !== meshes.length; i++){ meshes[i].position.copy(bodies[i].position); meshes[i].quaternion.copy(bodies[i].quaternion); } } function render() { renderer.render(scene, camera); } function initCannon(){ // Setup our world world = new CANNON.World(); world.quatNormalizeSkip = 0; world.quatNormalizeFast = false; world.gravity.set(0,-10,0); world.broadphase = new CANNON.NaiveBroadphase(); // Create boxes var mass = 5, radius = 1.3; boxShape = new CANNON.Box(new CANNON.Vec3(0.5,0.5,0.5)); for(var i=0; i<N; i++){ boxBody = new CANNON.Body({ mass: mass }); boxBody.addShape(boxShape); boxBody.position.set(0,5,0); world.add(boxBody); bodies.push(boxBody); } // Create a plane var groundShape = new CANNON.Plane(); var groundBody = new CANNON.Body({ mass: 0 }); groundBody.addShape(groundShape); groundBody.quaternion.setFromAxisAngle(new CANNON.Vec3(1,0,0),-Math.PI/2); world.add(groundBody); // Joint body var shape = new CANNON.Sphere(0.1); jointBody = new CANNON.Body({ mass: 0 }); jointBody.addShape(shape); jointBody.collisionFilterGroup = 0; jointBody.collisionFilterMask = 0; world.add(jointBody) } function addMouseConstraint(x,y,z,body) { // The cannon body constrained by the mouse joint constrainedBody = body; // Vector to the clicked point, relative to the body var v1 = new CANNON.Vec3(x,y,z).vsub(constrainedBody.position); // Apply anti-quaternion to vector to tranform it into the local body coordinate system var antiRot = constrainedBody.quaternion.inverse(); pivot = antiRot.vmult(v1); // pivot is not in local body coordinates // Move the cannon click marker particle to the click position jointBody.position.set(x,y,z); // Create a new constraint // The pivot for the jointBody is zero mouseConstraint = new CANNON.PointToPointConstraint(constrainedBody, pivot, jointBody, new CANNON.Vec3(0,0,0)); // Add the constriant to world world.addConstraint(mouseConstraint); } // This functions moves the transparent joint body to a new postion in space function moveJointToPoint(x,y,z) { // Move the joint body to a new position jointBody.position.set(x,y,z); mouseConstraint.update(); } function removeJointConstraint(){ // Remove constriant from world world.removeConstraint(mouseConstraint); mouseConstraint = false; } </script> </body> </html>