1 // Copyright (c) 2013 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 var container, stats; 6 var camera, controls, scene, projector, renderer; 7 var plane; 8 var lastSceneDescription; 9 var skipSceneUpdates = 0; 10 var hold = false; 11 var holdObjectIndex = -1; 12 13 var mouse = new THREE.Vector2(); 14 var offset = new THREE.Vector3(); 15 var INTERSECTED, SELECTED; 16 17 var sceneDescription = []; 18 19 var shapes = {}; 20 var objects = []; 21 22 function clearWorld() { 23 for (var i = 0; i < objects.length; i++) { 24 scene.remove(objects[i]); 25 } 26 objects = []; 27 shapes = {}; 28 // Make sure we drop the object. 29 hold = false; 30 SELECTED = undefined; 31 NaClAMBulletDropObject(); 32 } 33 34 function loadShape(shape) { 35 if (shapes[shape.name] != undefined) { 36 return shapes[shape.name]; 37 } 38 39 if (shape.type == "cube") { 40 shapes[shape.name] = new THREE.CubeGeometry(shape['wx'], shape['wy'], shape['wz']); 41 return shapes[shape.name]; 42 } 43 44 if (shape.type == "convex") { 45 var vertices = []; 46 for (var i = 0; i < shape['points'].length; i++) { 47 vertices.push(new THREE.Vector3(shape['points'][i][0], shape['points'][i][1], shape['points'][i][2])); 48 } 49 shapes[shape.name] = new THREE.ConvexGeometry(vertices); 50 return shapes[shape.name]; 51 } 52 53 if (shape.type == "cylinder") { 54 shapes[shape.name] = new THREE.CylinderGeometry(shape['radius'], shape['radius'], shape['height']) 55 return shapes[shape.name]; 56 } 57 58 if (shape.type == "sphere") { 59 shapes[shape.name] = new THREE.SphereGeometry(shape['radius']); 60 return shapes[shape.name]; 61 } 62 63 return undefined; 64 } 65 66 function loadBody(body) { 67 var shape = shapes[body.shape]; 68 if (shape == undefined) { 69 return shape; 70 } 71 72 var object = new THREE.Mesh( shape, new THREE.MeshLambertMaterial( { color: Math.random() * 0xffffff } ) ); 73 74 object.material.ambient = object.material.color; 75 76 object.position.x = body.position.x; 77 object.position.y = body.position.y; 78 object.position.z = body.position.z; 79 80 object.rotation.x = body.rotation.x; 81 object.rotation.y = body.rotation.y; 82 object.rotation.z = body.rotation.z; 83 84 object.updateMatrixWorld(true); 85 var T = [object.matrixWorld.elements[0], 86 object.matrixWorld.elements[1], 87 object.matrixWorld.elements[2], 88 object.matrixWorld.elements[3], 89 object.matrixWorld.elements[4], 90 object.matrixWorld.elements[5], 91 object.matrixWorld.elements[6], 92 object.matrixWorld.elements[7], 93 object.matrixWorld.elements[8], 94 object.matrixWorld.elements[9], 95 object.matrixWorld.elements[10], 96 object.matrixWorld.elements[11], 97 object.matrixWorld.elements[12], 98 object.matrixWorld.elements[13], 99 object.matrixWorld.elements[14], 100 object.matrixWorld.elements[15]]; 101 body.transform = T; 102 103 object.castShadow = false; 104 object.receiveShadow = false; 105 object.matrixAutoUpdate = false; 106 object.objectTableIndex = objects.length; 107 scene.add(object); 108 objects.push(object); 109 110 return object; 111 } 112 113 function loadWorld(worldDescription) { 114 clearWorld(); 115 var i; 116 var shapes = worldDescription['shapes']; 117 var bodies = worldDescription['bodies']; 118 for (i = 0; i < shapes.length; i++) { 119 if (loadShape(shapes[i]) == undefined) { 120 console.log('Could not load shape ' + shapes[i].name); 121 } 122 } 123 124 for (i = 0; i < bodies.length; i++) { 125 if (loadBody(bodies[i]) == undefined) { 126 console.log('Could not make body.'); 127 } 128 } 129 130 var r = verifyWorldDescription(worldDescription); 131 if (r == false) { 132 alert('Invalid scene description. See console.'); 133 return; 134 } 135 skipSceneUpdates = 4; 136 NaClAMBulletLoadScene(worldDescription); 137 lastSceneDescription = worldDescription; 138 } 139 140 function reloadScene() { 141 if (lastSceneDescription) 142 loadWorld(lastSceneDescription); 143 } 144 145 function $(id) { 146 return document.getElementById(id); 147 } 148 149 function init() { 150 var rendererContainer = $('rendererContainer'); 151 var rcW = rendererContainer.clientWidth; 152 var rcH = rendererContainer.clientHeight; 153 154 camera = new THREE.PerspectiveCamera( 155 70, 156 rcW / rcH, 1, 10000); 157 camera.position.y = 20.0; 158 camera.position.z = 40; 159 160 scene = new THREE.Scene(); 161 162 scene.add( new THREE.AmbientLight( 0x505050 ) ); 163 164 var light = new THREE.SpotLight( 0xffffff, 1.5 ); 165 light.position.set( 0, 500, 2000 ); 166 light.castShadow = true; 167 168 light.shadowCameraNear = 200; 169 light.shadowCameraFar = camera.far; 170 light.shadowCameraFov = 50; 171 172 light.shadowBias = -0.00022; 173 light.shadowDarkness = 0.5; 174 175 light.shadowMapWidth = 2048; 176 light.shadowMapHeight = 2048; 177 178 scene.add( light ); 179 180 plane = new THREE.Mesh( new THREE.PlaneGeometry( 200, 200, 100, 100), new THREE.MeshBasicMaterial( { color: 0x000000, opacity: 0.25, transparent: true, wireframe: true } ) ); 181 plane.rotation.x = Math.PI * 0.5; 182 plane.visible = true; 183 scene.add( plane ); 184 projector = new THREE.Projector(); 185 186 renderer = new THREE.WebGLRenderer( { antialias: true } ); 187 renderer.sortObjects = false; 188 renderer.setSize( rcW, rcH ); 189 lastRendererWidth = rcW; 190 lastRendererWidth = rcH; 191 192 renderer.shadowMapEnabled = true; 193 renderer.shadowMapSoft = true; 194 195 rendererContainer.appendChild(renderer.domElement); 196 197 var idFuncHash = { 198 jenga10: loadJenga10, 199 jenga20: loadJenga20, 200 randomShapes: loadRandomShapes, 201 randomCube250: load250RandomCubes, 202 randomCylinder500: load500RandomCylinders, 203 randomCube1000: load1000RandomCubes, 204 randomCube2000: load2000RandomCubes 205 }; 206 207 for (var id in idFuncHash) { 208 var func = idFuncHash[id]; 209 $(id).addEventListener('click', func, false); 210 } 211 212 $('reload').addEventListener('click', reloadScene, false); 213 214 rendererContainer.addEventListener('mousedown', onMouseDown, false); 215 rendererContainer.addEventListener('mouseup', onMouseUp, false); 216 rendererContainer.addEventListener('mouseleave', onMouseUp, false); 217 renderer.domElement.addEventListener('mousemove', onMouseMove, false); 218 219 // Add the OrbitControls after our own listeners -- that way we can prevent 220 // the camera rotation when dragging an object. 221 controls = new THREE.OrbitControls(camera, rendererContainer); 222 223 window.setInterval(pollForRendererResize, 10); 224 } 225 226 function pollForRendererResize() { 227 var rendererContainer = $('rendererContainer'); 228 var w = rendererContainer.clientWidth; 229 var h = rendererContainer.clientHeight; 230 if (w == lastRendererWidth && h == lastRendererHeight) 231 return; 232 233 camera.aspect = w / h; 234 camera.updateProjectionMatrix(); 235 renderer.setSize( w, h ); 236 lastRendererWidth = w; 237 lastRendererHeight = h; 238 } 239 240 function onMouseDown(event) { 241 event.preventDefault(); 242 243 var vector = new THREE.Vector3( mouse.x, mouse.y, 0.5 ); 244 projector.unprojectVector( vector, camera ); 245 var ray = new THREE.Ray( camera.position, vector.subSelf( camera.position ).normalize() ); 246 var intersects = ray.intersectObjects( objects ); 247 if (intersects.length > 0) { 248 if (intersects[0].object != plane) { 249 hold = true; 250 SELECTED = intersects[0].object; 251 //console.log(SELECTED.objectTableIndex); 252 NaClAMBulletPickObject(SELECTED.objectTableIndex, camera.position, intersects[0].point); 253 // stopImmediatePropagation() will prevent other event listeners on the 254 // same element from firing -- in this case, the OrbitControls camera 255 // rotation. 256 event.stopImmediatePropagation(); 257 } 258 } 259 } 260 261 function onMouseUp(event) { 262 if (hold) { 263 hold = false; 264 SELECTED = undefined; 265 NaClAMBulletDropObject(); 266 event.stopImmediatePropagation(); 267 } 268 } 269 270 function onMouseMove( event ) { 271 event.preventDefault(); 272 273 var clientRect = $('rendererContainer').getClientRects()[0]; 274 var x = event.clientX - clientRect.left; 275 var y = event.clientY - clientRect.top; 276 var w = clientRect.width; 277 var h = clientRect.height; 278 279 mouse.x = ( x / w ) * 2 - 1; 280 mouse.y = -( y / h ) * 2 + 1; 281 var vector = new THREE.Vector3( mouse.x, mouse.y, 0.5 ); 282 projector.unprojectVector( vector, camera ); 283 offset.x = vector.x; 284 offset.y = vector.y; 285 offset.z = vector.z; 286 } 287 288 // 289 290 function animate() { 291 window.requestAnimationFrame(animate); 292 aM.sendMessage('stepscene', {rayFrom: [camera.position.x, camera.position.y, camera.position.z], rayTo: [offset.x, offset.y, offset.z]}); 293 render(); 294 } 295 296 function render() { 297 controls.update(); 298 renderer.render( scene, camera ); 299 } 300