2
0

webgl_multiple_windows.html 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732
  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <title>three.js webgl - multiple windows</title>
  5. <meta charset="utf-8">
  6. <meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
  7. <style>
  8. body, html {
  9. color: #000;
  10. font-family:Monospace;
  11. font-size:13px;
  12. text-align:center;
  13. width: 100%;
  14. height: 100%;
  15. background-color: #fff;
  16. margin: 0px;
  17. overflow: hidden;
  18. }
  19. #container {
  20. width: 100%;
  21. height: 100%;
  22. }
  23. #info {
  24. position: absolute;
  25. top: 0px; width: 100%;
  26. padding: 5px;
  27. }
  28. a {
  29. color: #0080ff;
  30. }
  31. #newview {
  32. position: absolute;
  33. top: 10px;
  34. left: 10px;
  35. background-color: rgba(0,0,0,0.5);
  36. color: white;
  37. font-weight: bold;
  38. padding: 1em;
  39. }
  40. </style>
  41. </head>
  42. <body>
  43. <div id="container"></div>
  44. <div id="info"><a href="http://threejs.org" target="_blank">three.js</a> - multiple windows - webgl</div>
  45. <div id="newview">Click for new Window</div>
  46. <script src="../build/three.js"></script>
  47. <script src="js/controls/TransformControls.js"></script>
  48. <script src="js/Detector.js"></script>
  49. <script>
  50. if ( ! Detector.webgl ) Detector.addGetWebGLMessage();
  51. var container, windowManager, viewCount = 0;
  52. var views, scene, renderer;
  53. var mesh, group1, group2, group3, light;
  54. var mouseX = 0, mouseY = 0;
  55. var windowWidth = 1, windowHeight = 1;
  56. /**
  57. * Makes one class inherit from another.
  58. * @param {!Object} subClass Class that wants to inherit.
  59. * @param {!Object} superClass Class to inherit from.
  60. */
  61. var inherit = function( subClass, superClass ) {
  62. var TmpClass = function() { };
  63. TmpClass.prototype = superClass.prototype;
  64. subClass.prototype = new TmpClass();
  65. };
  66. /**
  67. * Represents a popup window.
  68. */
  69. var ViewWindow = function( manager, id, window, container ) {
  70. var self = this;
  71. this.manager = manager;
  72. this.id = id;
  73. this.window = window;
  74. this.renderQueued = false;
  75. this.width = 300;
  76. this.height = 150;
  77. this.container = container;
  78. this.closed = false;
  79. this.devicePixelRatio = 1;//window.devicePixelRatio || 1;
  80. var document = window.document
  81. this.document = document;
  82. this.canvas = this.document.createElement( 'canvas' );
  83. var style = this.canvas.style;
  84. style.width = "100%";
  85. style.height = "100%";
  86. this.container.appendChild( this.canvas );
  87. this.ctx = this.canvas.getContext( '2d' );
  88. // TODO: should probably make the polyfill take a window?
  89. this.window.requestAnimFrame = (function() {
  90. return this.window.requestAnimationFrame ||
  91. this.window.webkitRequestAnimationFrame ||
  92. this.window.mozRequestAnimationFrame ||
  93. function( callback ){
  94. this.window.setTimeout( callback, 1000 / 60 );
  95. };
  96. })();
  97. this.window.addEventListener( 'resize', this.constructor.prototype.updateSize.bind( this ) );
  98. this.window.addEventListener( 'unload', this.constructor.prototype.close.bind( this ) );
  99. this.window.addEventListener( 'focus', this.constructor.prototype.focus.bind( this ) );
  100. var rand = function() {
  101. return Math.random() * 0.2 + 0.5;
  102. };
  103. var view = {
  104. background: new THREE.Color().setRGB( rand(), rand(), rand() ),
  105. fov: 30,
  106. };
  107. this.view = view;
  108. var camera = new THREE.PerspectiveCamera( 30, 1, 1, 10000 );
  109. var angle = 0.2;
  110. var distance = 1500;
  111. camera.position.z = Math.cos(Math.PI * angle) * distance;
  112. camera.position.x = Math.sin(Math.PI * angle) * distance;
  113. camera.position.y = distance * 0.1;
  114. camera.lookAt( new THREE.Vector3( 0, 0, 0 ) );
  115. view.camera = camera;
  116. camera.updateFromCanvas = (function( camera ) {
  117. return function( canvas ) {
  118. camera.aspect = canvas.width / canvas.height;
  119. camera.updateProjectionMatrix();
  120. };
  121. }( camera ));
  122. this.perspectiveCamera = camera;
  123. var camera = new THREE.OrthographicCamera( -1, 1, 1, -1, 1, 10000 );
  124. camera.position.z = 1500;
  125. camera.updateFromCanvas = (function( camera ) {
  126. return function ( canvas ) {
  127. var width = canvas.width;
  128. var height = canvas.height;
  129. camera.left = -width;
  130. camera.right = width;
  131. camera.top = height;
  132. camera.bottom = -height;
  133. camera.updateProjectionMatrix();
  134. };
  135. }( camera ));
  136. this.orthographicCamera = camera;
  137. this.currentCamera = this.perspectiveCamera;
  138. // Is this in three.js?
  139. var copyOrientation = function( src, dst ) {
  140. dst.up.copy( src.up );
  141. dst.position.copy( src.position );
  142. dst.scale.copy( src.scale );
  143. dst.rotation.copy( src.rotation );
  144. dst.quaternion.copy( src.quaternion );
  145. };
  146. var setCamera = function( camera ) {
  147. self.currentCamera = camera;
  148. };
  149. var buttonsElem = document.createElement( 'div' );
  150. var style = buttonsElem.style;
  151. style.position = 'absolute';
  152. style.top = '10px';
  153. style.padding = '1em';
  154. style.right = '10px';
  155. style.backgroundColor = 'rgba(0, 0, 0, 0.5)';
  156. var views = [
  157. {
  158. name: 'perspective 1',
  159. func: function() {
  160. setCamera( self.perspectiveCamera );
  161. var angle = 0.2;
  162. var distance = 1500;
  163. var x = Math.sin(Math.PI * angle) * distance;
  164. var y = distance * 0.1;
  165. var z = Math.cos(Math.PI * angle) * distance;
  166. self.currentCamera.position.set( x, y, z );
  167. self.currentCamera.lookAt( new THREE.Vector3( 0, 0, 0 ) );
  168. },
  169. },
  170. {
  171. name: 'perspective 2',
  172. func: function() {
  173. setCamera( self.perspectiveCamera );
  174. var angle = -0.4;
  175. var distance = 2500;
  176. var x = Math.sin(Math.PI * angle) * distance;
  177. var y = distance * 0.2;
  178. var z = Math.cos(Math.PI * angle) * distance;
  179. self.currentCamera.position.set( x, y, z );
  180. self.currentCamera.lookAt( new THREE.Vector3( 0, 0, 0 ) );
  181. },
  182. },
  183. {
  184. name: 'perspective 3',
  185. func: function() {
  186. setCamera( self.perspectiveCamera );
  187. var angle = 0.25;
  188. var distance = 1500;
  189. var x = Math.sin(Math.PI * angle) * distance;
  190. var y = distance * 0.6;
  191. var z = Math.cos(Math.PI * angle) * distance;
  192. self.currentCamera.position.set( x, y, z );
  193. self.currentCamera.lookAt( new THREE.Vector3( 0, 0, 0 ) );
  194. },
  195. },
  196. /*
  197. {
  198. name: 'left',
  199. func: function() {
  200. setCamera( self.orthographicCamera );
  201. self.currentCamera.position.set( -1500, 0, 0 );
  202. self.currentCamera.up.set( 0, 1, 0 );
  203. self.currentCamera.lookAt( new THREE.Vector3( 0, 0, 0 ) );
  204. },
  205. },
  206. {
  207. name: 'top',
  208. func: function() {
  209. setCamera( self.orthographicCamera );
  210. self.currentCamera.position.set( 0, 1500, 0 );
  211. self.currentCamera.up.set( 0, 0, -1 );
  212. self.currentCamera.lookAt( new THREE.Vector3( 0, 0, 0 ) );
  213. },
  214. },
  215. {
  216. name: 'front',
  217. func: function() {
  218. setCamera( self.orthographicCamera );
  219. self.currentCamera.position.set( 0, 0, 1500 );
  220. self.currentCamera.up.set( 0, 1, 0 );
  221. self.currentCamera.lookAt( new THREE.Vector3( 0, 0, 0 ) );
  222. },
  223. },
  224. */
  225. ];
  226. var updateViewHandler = function( button, view ) {
  227. if ( self.oldButton ) {
  228. self.oldButton.style.color = 'black';
  229. }
  230. view.func();
  231. self.manager.updateControlCamera( self.currentCamera );
  232. self.queueRender();
  233. self.oldButton = button,
  234. button.style.color = 'white';
  235. };
  236. var buttons = [];
  237. views.forEach(function( view ) {
  238. var button = document.createElement( 'div' );
  239. button.appendChild( document.createTextNode( view.name ) );
  240. button.addEventListener( 'click', function( button, view ) {
  241. return function() {
  242. updateViewHandler( button, view );
  243. };
  244. }( button, view ) );
  245. buttonsElem.appendChild( button );
  246. buttons.push( button );
  247. });
  248. this.container.appendChild( buttonsElem );
  249. this.updateSize();
  250. var viewNdx = (viewCount++) % buttons.length;
  251. updateViewHandler( buttons[viewNdx], views[viewNdx] );
  252. };
  253. ViewWindow.prototype.focus = function() {
  254. this.manager.makeWindowCurrent( this );
  255. };
  256. ViewWindow.prototype.updateSize = function() {
  257. var desiredWidth = this.canvas.clientWidth * this.devicePixelRatio;
  258. var desiredHeight = this.canvas.clientHeight * this.devicePixelRatio;
  259. if ( this.canvas.width != desiredWidth || this.canvas.height != desiredHeight ) {
  260. this.canvas.width = desiredWidth;
  261. this.canvas.height = desiredHeight;
  262. this.width = desiredWidth;
  263. this.height = desiredHeight;
  264. this.manager.updateMaxSize();
  265. this.queueRender();
  266. }
  267. };
  268. ViewWindow.prototype.close = function() {
  269. if ( !this.closed ) {
  270. this.manager.removeWindow( this, this.id );
  271. }
  272. };
  273. ViewWindow.prototype.closeImmediately = function() {
  274. this.closed = true;
  275. this.window.close();
  276. };
  277. ViewWindow.prototype.queueRender = function() {
  278. if ( ! this.renderQueued ) {
  279. this.renderQueued = true;
  280. this.window.requestAnimFrame( this.constructor.prototype.render.bind( this ) );
  281. }
  282. };
  283. ViewWindow.prototype.render = function() {
  284. this.renderQueued = false;
  285. var view = this.view;
  286. camera = this.currentCamera;
  287. camera.updateFromCanvas( this.canvas );
  288. this.manager.renderSceneToCanvas( this, camera, view, this.ctx );
  289. };
  290. /**
  291. * Represents the main window.
  292. */
  293. MainViewWindow = function() {
  294. ViewWindow.apply( this, arguments );
  295. };
  296. inherit( MainViewWindow, ViewWindow );
  297. MainViewWindow.prototype.close = function() {
  298. };
  299. MainViewWindow.prototype.closeImmediately = function() {
  300. };
  301. /**
  302. * Manages the windows.
  303. */
  304. var WindowManager = function( container, scene ) {
  305. this.windows = [];
  306. this.controls = [];
  307. this.maxWidth = 0;
  308. this.maxHeight = 0;
  309. this.newWindowId = 0;
  310. this.scene = scene;
  311. this.renderer = new THREE.WebGLRenderer( { antialias: true, alpha: false, devicePixelRatio: 1 } );
  312. window.addEventListener( 'unload', this.constructor.prototype.closeAllWindows.bind( this ) );
  313. // Main window has to be created last.
  314. this.windows.push( new MainViewWindow( this, 0, window, container ) );
  315. this.currentWindow = this.windows[0];
  316. };
  317. WindowManager.prototype.createWindow = function() {
  318. var id = ++this.newWindowId;
  319. var subWindow = window.open( '', 'view#' + id, 'width=400,height=400,location=no,resizable=yes' );
  320. var document = subWindow.document;
  321. var body = document.body;
  322. var style = body.style;
  323. style.width = '100%';
  324. style.height = '100%';
  325. style.padding = '0px';
  326. style.margin = '0px';
  327. style.overflow = 'hidden';
  328. style.fontFamily = 'Monospace';
  329. style.fontSize = '13px';
  330. this.windows.push( new ViewWindow( this, id, subWindow, body ) );
  331. };
  332. WindowManager.prototype.removeWindow = function( window, id ) {
  333. for ( var ii = 0; ii < this.windows.length; ++ii ) {
  334. if ( this.windows[ ii ].id == id ) {
  335. this.windows.splice( ii, 1 );
  336. break;
  337. }
  338. }
  339. };
  340. WindowManager.prototype.closeAllWindows = function() {
  341. this.windows.forEach( function( window ) {
  342. window.closeImmediately();
  343. } );
  344. this.windows = [];
  345. };
  346. WindowManager.prototype.updateMaxSize = function() {
  347. var maxWidth = 0;
  348. var maxHeight = 0;
  349. this.windows.forEach( function( window ) {
  350. maxWidth = Math.max( window.width, maxWidth );
  351. maxHeight = Math.max( window.height, maxHeight );
  352. } );
  353. if ( this.maxWidth != maxWidth || this.maxHeight != maxHeight ) {
  354. this.renderer.setSize( maxWidth, maxHeight, false );
  355. this.maxWidth = maxWidth;
  356. this.maxHeight = maxHeight;
  357. }
  358. };
  359. WindowManager.prototype.updateAllWindows = function() {
  360. this.updateMaxSize();
  361. this.controls.forEach( function( control ) {
  362. control.update();
  363. } );
  364. var renderer = this.renderer;
  365. var self = this;
  366. this.windows.forEach( function( window ) {
  367. window.render();
  368. } );
  369. };
  370. WindowManager.prototype.renderSceneToCanvas = function( window, camera, parameters, ctx ) {
  371. var renderer = this.renderer;
  372. var canvas = ctx.canvas;
  373. var scene = this.scene;
  374. var hide = window !== this.currentWindow;
  375. this.controls.forEach( function( control ) {
  376. if ( hide ) {
  377. control.hide();
  378. } else {
  379. control.setMode( control.mode );
  380. }
  381. } );
  382. renderer.setViewport( 0, 0, canvas.width, canvas.height );
  383. renderer.setClearColor( parameters.background );
  384. renderer.render( scene, camera );
  385. var srcX = 0;
  386. var srcY = this.maxHeight - canvas.height;
  387. var dstX = 0;
  388. var dstY = 0;
  389. var width = canvas.width;
  390. var height = canvas.height;
  391. if ( srcX >= 0 && srcY >= 0 && width >= 1 && height >= 1 ) {
  392. ctx.drawImage( renderer.context.canvas,
  393. srcX, srcY, width, height,
  394. dstX, dstY, width, height );
  395. }
  396. };
  397. WindowManager.prototype.makeWindowCurrent = function( window ) {
  398. if ( window !== this.currentWindow ) {
  399. this.currentWindow = window;
  400. this.attachControlsToWindow( window );
  401. this.updateAllWindows();
  402. }
  403. };
  404. WindowManager.prototype.updateControlCamera = function( camera ) {
  405. this.controls.forEach( function( control ) {
  406. control.camera = camera;
  407. control.update();
  408. } );
  409. };
  410. WindowManager.prototype.attachControlsToWindow = function( window ) {
  411. this.controls.forEach( function( control ) {
  412. var object = control.object;
  413. if ( object ) {
  414. control.detach( object );
  415. }
  416. // switch to this window
  417. control.domElement = window.container;
  418. control.document = window.document;
  419. control.camera = window.currentCamera;
  420. control.attach( object );
  421. control.update();
  422. } );
  423. };
  424. WindowManager.prototype.addTransformControl = function( object ) {
  425. var window = this.currentWindow;
  426. var camera = window.currentCamera;
  427. var container = window.container;
  428. var control = new THREE.TransformControls( camera, container, window.document );
  429. control.addEventListener( 'change', windowManager.__proto__.updateAllWindows.bind( windowManager ));
  430. control.attach( object );
  431. control.scale = 0.65;
  432. this.scene.add( control.gizmo );
  433. this.controls.push( control );
  434. };
  435. init();
  436. function init() {
  437. scene = new THREE.Scene();
  438. light = new THREE.DirectionalLight( 0xffffff );
  439. light.position.set( 0, 0, 1 );
  440. scene.add( light );
  441. var faceIndices = [ 'a', 'b', 'c', 'd' ];
  442. var color, f, f2, f3, p, n, vertexIndex,
  443. radius = 200,
  444. geometry = new THREE.IcosahedronGeometry( radius, 1 ),
  445. geometry2 = new THREE.IcosahedronGeometry( radius, 1 ),
  446. geometry3 = new THREE.IcosahedronGeometry( radius, 1 );
  447. for ( var i = 0; i < geometry.faces.length; i ++ ) {
  448. f = geometry.faces[ i ];
  449. f2 = geometry2.faces[ i ];
  450. f3 = geometry3.faces[ i ];
  451. n = ( f instanceof THREE.Face3 ) ? 3 : 4;
  452. for( var j = 0; j < n; j++ ) {
  453. vertexIndex = f[ faceIndices[ j ] ];
  454. p = geometry.vertices[ vertexIndex ];
  455. color = new THREE.Color( 0xffffff );
  456. color.setHSL( ( p.y / radius + 1 ) / 2, 1.0, 0.5 );
  457. f.vertexColors[ j ] = color;
  458. color = new THREE.Color( 0xffffff );
  459. color.setHSL( 0.0, ( p.y / radius + 1 ) / 2, 0.5 );
  460. f2.vertexColors[ j ] = color;
  461. color = new THREE.Color( 0xffffff );
  462. color.setHSL( 0.125 * vertexIndex/geometry.vertices.length, 1.0, 0.5 );
  463. f3.vertexColors[ j ] = color;
  464. }
  465. }
  466. var materials = [
  467. new THREE.MeshLambertMaterial( { color: 0xffffff, shading: THREE.FlatShading, vertexColors: THREE.VertexColors } ),
  468. new THREE.MeshBasicMaterial( { color: 0x000000, shading: THREE.FlatShading, wireframe: true, transparent: true } )
  469. ];
  470. group1 = THREE.SceneUtils.createMultiMaterialObject( geometry, materials );
  471. group1.position.x = -400;
  472. group1.rotation.x = -0;
  473. scene.add( group1 );
  474. group2 = THREE.SceneUtils.createMultiMaterialObject( geometry2, materials );
  475. group2.position.x = 400;
  476. group2.rotation.x = 0;
  477. scene.add( group2 );
  478. group3 = THREE.SceneUtils.createMultiMaterialObject( geometry3, materials );
  479. group3.position.x = 0;
  480. group3.rotation.x = 0;
  481. scene.add( group3 );
  482. container = document.getElementById( 'container' );
  483. windowManager = new WindowManager( container, scene );
  484. var newViewButton = document.getElementById( 'newview' );
  485. newViewButton.addEventListener( 'click', windowManager.__proto__.createWindow.bind( windowManager ) );
  486. windowManager.addTransformControl( group1 );
  487. windowManager.addTransformControl( group2 );
  488. windowManager.addTransformControl( group3 );
  489. windowManager.updateAllWindows();
  490. }
  491. </script>
  492. </body>
  493. </html>