19e12abdf8c3a23d52091ea54ebb6a04d327f9300Torne (Richard Coles)// Copyright (c) 2013 The Chromium Authors. All rights reserved.
29e12abdf8c3a23d52091ea54ebb6a04d327f9300Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
39e12abdf8c3a23d52091ea54ebb6a04d327f9300Torne (Richard Coles)// found in the LICENSE file.
49e12abdf8c3a23d52091ea54ebb6a04d327f9300Torne (Richard Coles)
59e12abdf8c3a23d52091ea54ebb6a04d327f9300Torne (Richard Coles)var container, stats;
69e12abdf8c3a23d52091ea54ebb6a04d327f9300Torne (Richard Coles)var camera, controls, scene, projector, renderer;
79e12abdf8c3a23d52091ea54ebb6a04d327f9300Torne (Richard Coles)var plane;
89e12abdf8c3a23d52091ea54ebb6a04d327f9300Torne (Richard Coles)var lastSceneDescription;
99e12abdf8c3a23d52091ea54ebb6a04d327f9300Torne (Richard Coles)var skipSceneUpdates = 0;
109e12abdf8c3a23d52091ea54ebb6a04d327f9300Torne (Richard Coles)var hold = false;
119e12abdf8c3a23d52091ea54ebb6a04d327f9300Torne (Richard Coles)var holdObjectIndex = -1;
129e12abdf8c3a23d52091ea54ebb6a04d327f9300Torne (Richard Coles)
139e12abdf8c3a23d52091ea54ebb6a04d327f9300Torne (Richard Coles)var mouse = new THREE.Vector2();
147242dc3dbeb210b5e876a3c42d1ec1a667fc621aPrimiano Tuccivar offset = new THREE.Vector3();
159e12abdf8c3a23d52091ea54ebb6a04d327f9300Torne (Richard Coles)var INTERSECTED, SELECTED;
169e12abdf8c3a23d52091ea54ebb6a04d327f9300Torne (Richard Coles)
179e12abdf8c3a23d52091ea54ebb6a04d327f9300Torne (Richard Coles)var sceneDescription = [];
189e12abdf8c3a23d52091ea54ebb6a04d327f9300Torne (Richard Coles)
199e12abdf8c3a23d52091ea54ebb6a04d327f9300Torne (Richard Coles)var shapes = {};
209e12abdf8c3a23d52091ea54ebb6a04d327f9300Torne (Richard Coles)var objects = [];
219e12abdf8c3a23d52091ea54ebb6a04d327f9300Torne (Richard Coles)
229e12abdf8c3a23d52091ea54ebb6a04d327f9300Torne (Richard Coles)function clearWorld() {
239e12abdf8c3a23d52091ea54ebb6a04d327f9300Torne (Richard Coles)  for (var i = 0; i < objects.length; i++) {
249e12abdf8c3a23d52091ea54ebb6a04d327f9300Torne (Richard Coles)    scene.remove(objects[i]);
259e12abdf8c3a23d52091ea54ebb6a04d327f9300Torne (Richard Coles)  }
269e12abdf8c3a23d52091ea54ebb6a04d327f9300Torne (Richard Coles)  objects = [];
279e12abdf8c3a23d52091ea54ebb6a04d327f9300Torne (Richard Coles)  shapes = {};
289e12abdf8c3a23d52091ea54ebb6a04d327f9300Torne (Richard Coles)  // Make sure we drop the object.
299e12abdf8c3a23d52091ea54ebb6a04d327f9300Torne (Richard Coles)  hold = false;
307242dc3dbeb210b5e876a3c42d1ec1a667fc621aPrimiano Tucci  SELECTED = undefined;
319e12abdf8c3a23d52091ea54ebb6a04d327f9300Torne (Richard Coles)  NaClAMBulletDropObject();
329e12abdf8c3a23d52091ea54ebb6a04d327f9300Torne (Richard Coles)}
339e12abdf8c3a23d52091ea54ebb6a04d327f9300Torne (Richard Coles)
349e12abdf8c3a23d52091ea54ebb6a04d327f9300Torne (Richard Coles)function loadShape(shape) {
359e12abdf8c3a23d52091ea54ebb6a04d327f9300Torne (Richard Coles)  if (shapes[shape.name] != undefined) {
369e12abdf8c3a23d52091ea54ebb6a04d327f9300Torne (Richard Coles)    return shapes[shape.name];
379e12abdf8c3a23d52091ea54ebb6a04d327f9300Torne (Richard Coles)  }
389e12abdf8c3a23d52091ea54ebb6a04d327f9300Torne (Richard Coles)
399e12abdf8c3a23d52091ea54ebb6a04d327f9300Torne (Richard Coles)  if (shape.type == "cube") {
409e12abdf8c3a23d52091ea54ebb6a04d327f9300Torne (Richard Coles)    shapes[shape.name] = new THREE.CubeGeometry(shape['wx'], shape['wy'], shape['wz']);
419e12abdf8c3a23d52091ea54ebb6a04d327f9300Torne (Richard Coles)    return shapes[shape.name];
429e12abdf8c3a23d52091ea54ebb6a04d327f9300Torne (Richard Coles)  }
439e12abdf8c3a23d52091ea54ebb6a04d327f9300Torne (Richard Coles)
449e12abdf8c3a23d52091ea54ebb6a04d327f9300Torne (Richard Coles)  if (shape.type == "convex") {
459e12abdf8c3a23d52091ea54ebb6a04d327f9300Torne (Richard Coles)    var vertices = [];
469e12abdf8c3a23d52091ea54ebb6a04d327f9300Torne (Richard Coles)    for (var i = 0; i < shape['points'].length; i++) {
479e12abdf8c3a23d52091ea54ebb6a04d327f9300Torne (Richard Coles)      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
66function 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
113function 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
140function reloadScene() {
141  if (lastSceneDescription)
142    loadWorld(lastSceneDescription);
143}
144
145function $(id) {
146  return document.getElementById(id);
147}
148
149function 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
226function 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
240function 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
261function onMouseUp(event) {
262  if (hold) {
263    hold = false;
264    SELECTED = undefined;
265    NaClAMBulletDropObject();
266    event.stopImmediatePropagation();
267  }
268}
269
270function 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
290function 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
296function render() {
297  controls.update();
298  renderer.render( scene, camera );
299}
300