FullDomeRenderer.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372
  1. /* The FullDomeRenderer code presented here is adapted from:
  2. *
  3. * https://github.com/spite/THREE.CubemapToEquirectangular
  4. */
  5. import {initPlaylist} from '../../js/playlist.js'
  6. const inchesToMeters = 0.0254;
  7. const feetToMeters = 0.3048;
  8. const degreesToRadians = Math.PI / 180;
  9. let domRender;
  10. let RendererConfig = {
  11. camera: {
  12. /* The cube camera takes in the 360 view of the scene. The
  13. starting position is at average eye height for a standing
  14. person. */
  15. startingPosition: new THREE.Vector3(0, 5 * feetToMeters, 0),
  16. /* The cameraRig member carries the camera. Move this around to
  17. animate the viewpoint */
  18. rig: null,
  19. near: .01, // .1,
  20. far: 1000,
  21. cubeMapSize: 1024
  22. },
  23. dome: {
  24. radius: 35.0 * feetToMeters / 2,
  25. inclination: 20 * degreesToRadians,
  26. fullSphere: false
  27. },
  28. };
  29. // Load defaults from session storage
  30. // var val = sessionStorage.getItem('defaultCubeMapResolution');
  31. // if(val) {
  32. // console.log("cubeMapSize from session storage", val);
  33. // RendererConfig.camera.cubeMapSize = parseInt(val);
  34. // }
  35. /* Trick for inline strings for GLSL code:
  36. http://stackoverflow.com/questions/805107/creating-multiline-strings-in-javascript
  37. */
  38. Function.prototype.getComment = function() {
  39. var startComment = "/*!";
  40. var endComment = "*/";
  41. var str = this.toString();
  42. var start = str.indexOf(startComment);
  43. var end = str.lastIndexOf(endComment);
  44. return str.slice(start + startComment.length, -(str.length - end));
  45. };
  46. FullDomeRenderer.vertexShader = function() {/*!
  47. attribute vec3 position;
  48. attribute vec2 uv;
  49. uniform mat4 projectionMatrix;
  50. uniform mat4 modelViewMatrix;
  51. varying vec2 vUv;
  52. #define M_PI 3.1415926535897932384626433832795
  53. void main() {
  54. vUv = vec2(uv.x - 0.5, uv.y - 0.5);
  55. gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
  56. }
  57. */}.getComment();
  58. FullDomeRenderer.fragmentShader = function() {/*!
  59. precision highp float;
  60. uniform samplerCube map;
  61. varying vec2 vUv;
  62. #define M_PI 3.1415926535897932384626433832795
  63. void main() {
  64. float l = length(vUv);
  65. float latitude = l * M_PI;
  66. float sinOfLatitude = sin( latitude );
  67. float cosOfLatitude = cos( latitude );
  68. vec3 dir = vec3(
  69. vUv.x/l * sinOfLatitude,
  70. cosOfLatitude,
  71. vUv.y/l * sinOfLatitude
  72. );
  73. gl_FragColor = textureCube( map, dir );
  74. }
  75. */}.getComment();
  76. function getDomeGeometry(radius, fullSphere) {
  77. var geometry = new THREE.SphereBufferGeometry(
  78. radius, 64, 64,
  79. 0, Math.PI*2,
  80. 0, fullSphere ? Math.PI : Math.PI/2
  81. );
  82. /* If rendering a half sphere, the UV texture coordinates
  83. * need to be halved in the y direction. */
  84. if(!fullSphere) {
  85. var uv = geometry.attributes.uv.array;
  86. for(var i = 1; i < uv.length; i += 2) {
  87. uv[i] = uv[i]/2 + 0.5;
  88. }
  89. }
  90. return geometry;
  91. }
  92. function FullDomeRenderer( renderer ) {
  93. this.renderer = renderer;
  94. /* For doing the warpping, we use a scene that consists of
  95. * a quad that covers the entire canvas and paint it with a
  96. * fragment shader */
  97. this.material = new THREE.RawShaderMaterial( {
  98. uniforms: {
  99. map: { type: 't', value: null }
  100. },
  101. vertexShader: FullDomeRenderer.vertexShader,
  102. fragmentShader: FullDomeRenderer.fragmentShader,
  103. side: THREE.DoubleSide
  104. } );
  105. this.scene = new THREE.Scene();
  106. this.quad = new THREE.Mesh(
  107. new THREE.PlaneBufferGeometry(1, 1),
  108. this.material
  109. );
  110. this.scene.add(this.quad);
  111. this.camera = new THREE.OrthographicCamera(
  112. 1 / -2, // Camera frustum left plane
  113. 1 / 2, // Camera frustum right plane
  114. 1 / 2, // Camera frustum top plane
  115. 1 / -2, // Camera frustum bottom plane
  116. -10000, // Camera frustum near plane
  117. 10000 // Camera frustum far plane
  118. );
  119. /* The cube camera snaps a 360 picture of the user's scene into a cube texture */
  120. var gl = this.renderer.getContext();
  121. var maxSize = gl.getParameter( gl.MAX_CUBE_MAP_TEXTURE_SIZE );
  122. var cameraRig = new THREE.Object3D();
  123. this.cubeCamera = new THREE.CubeCamera(
  124. RendererConfig.camera.near,
  125. RendererConfig.camera.far,
  126. Math.min(maxSize, RendererConfig.camera.cubeMapSize)
  127. );
  128. cameraRig.add(this.cubeCamera);
  129. this.cubeCamera.rotation.x = -RendererConfig.dome.inclination;
  130. cameraRig.position.copy(RendererConfig.camera.startingPosition);
  131. RendererConfig.camera.rig = cameraRig;
  132. /* If we have a half-dome, obstructing the lower half of
  133. * the cube camera with a hemisphere improves performance on
  134. * complex scenes. */
  135. if(!RendererConfig.dome.fullSphere) {
  136. var domeGeometry = getDomeGeometry(RendererConfig.dome.radius, RendererConfig.dome.fullSphere);
  137. var shield = new THREE.Mesh(domeGeometry, new THREE.MeshBasicMaterial({
  138. color: 0x000000,
  139. side: THREE.BackSide
  140. }));
  141. shield.rotation.x = Math.PI;
  142. // shield.scale.set(0.10, 0.10, 0.10);
  143. shield.scale.set(0.01, 0.01, 0.01);
  144. this.cubeCamera.add(shield);
  145. }
  146. }
  147. FullDomeRenderer.prototype.setSize = function( width, height ) {
  148. this.width = width;
  149. this.height = height;
  150. this.quad.scale.set( this.width, this.height, 1 );
  151. this.camera.left = this.width / - 2;
  152. this.camera.right = this.width / 2;
  153. this.camera.top = this.height / 2;
  154. this.camera.bottom = this.height / - 2;
  155. this.camera.updateProjectionMatrix();
  156. }
  157. FullDomeRenderer.prototype.render = function( scene ) {
  158. /* Step 1: Render the user's scene to the CubeCamera's texture */
  159. var autoClear = this.renderer.autoClear;
  160. this.renderer.autoClear = true;
  161. this.cubeCamera.updateCubeMap( this.renderer, scene );
  162. this.renderer.autoClear = autoClear;
  163. /* Step 2: Assign the CubeCamera's texture to the quad's fragment shader */
  164. this.quad.material.uniforms.map.value = this.cubeCamera.renderTarget.texture;
  165. /* Step 3: Paints the quad to the screen using the fragment shader to
  166. * perform the equirectangular distortion. */
  167. this.renderer.render(this.scene, this.camera);
  168. }
  169. /* Main function that kickstarts the animation loop */
  170. // SETUP VIDEO PLAYER
  171. function setupScene(scene) {
  172. var geometry = new THREE.SphereBufferGeometry( RendererConfig.dome.radius, 60, 40 );
  173. geometry.scale( -1, 1, 1 );
  174. window.videoPlayer = document.createElement('video');
  175. window.videoPlayer.id = "window.videoPlayerplayer"
  176. window.videoPlayer.width = 1024;
  177. window.videoPlayer.height = 640;
  178. window.videoPlayer.loop = false;
  179. window.videoPlayer.loopPlaylist = true
  180. window.videoPlayer.muted = true;
  181. window.videoPlayer.src = "./assets/preview.mp4";
  182. window.videoPlayer.setAttribute( 'webkit-playsinline', 'webkit-playsinline' );
  183. window.videoPlayer.setAttribute( 'playsinline', 'playsinline' );
  184. window.videoPlayer.setAttribute( 'crossorigin', 'anonymous' );
  185. window.videoPlayer.play();
  186. window.curVideo = 0;
  187. window.videoPlayer.onended = function () {
  188. let playlistVideos = window.playlist.el.getElementsByTagName("li")
  189. if (playlistVideos.length !== 0) {
  190. window.curVideo++;
  191. // go to next video in the playlist
  192. if (window.curVideo < playlistVideos.length) {
  193. // console.log("try to switch no next video", playlistVideos[window.curVideo]);
  194. window.videoPlayer.src = playlistVideos[window.curVideo].getAttribute('path');
  195. window.videoPlayer.play()
  196. }
  197. // last video of the playlist loop back to the begining.
  198. else if (window.curVideo == playlistVideos.length && window.videoPlayer.loopPlaylist){
  199. window.curVideo = 0;
  200. window.videoPlayer.src = playlistVideos[window.curVideo].getAttribute('path');
  201. window.videoPlayer.play()
  202. }
  203. } else {
  204. window.curVideo = 0
  205. window.videoPlayer.play()
  206. }
  207. }
  208. // iOS require the video to start muted in order for it to autoplay, so we use a click
  209. // to enable the sound.
  210. document.getElementsByTagName("canvas")[0].addEventListener("click",
  211. function() {window.videoPlayer.muted = false;}
  212. );
  213. var texture = new THREE.VideoTexture( window.videoPlayer );
  214. texture.minFilter = THREE.LinearFilter;
  215. texture.format = THREE.RGBFormat;
  216. var material = new THREE.MeshBasicMaterial( { map : texture } );
  217. let mesh = new THREE.Mesh( geometry, material );
  218. mesh.rotation.y = -Math.PI/2;
  219. scene.add( mesh );
  220. }
  221. function startAnimation() {
  222. var clock = new THREE.Clock();
  223. var renderer = new THREE.WebGLRenderer();
  224. domRender = new FullDomeRenderer(renderer, true);
  225. const container = document.getElementById('wrap');
  226. const canvas = renderer.domElement;
  227. canvas.id = "mainVideoCanvas"
  228. container.appendChild(canvas);
  229. // Setup Initial position
  230. // localStorage.setItem('playerTop', 'Tom');
  231. // localStorage.setItem('playerLeft', 'Tom');
  232. let playerTop = localStorage.getItem('playerTop');
  233. let playerLeft = localStorage.getItem('playerLeft');
  234. let playerWidth = localStorage.getItem('playerWidth');
  235. let playerHeight = localStorage.getItem('playerHeight');
  236. console.log("playerTop", playerTop, "playerLeft", playerLeft)
  237. if (!playerLeft && !playerTop) {
  238. container.style.left = `calc(50vw - ${container.offsetWidth / 2}px)`
  239. container.style.top = `calc(50vh - ${container.offsetHeight / 2}px)`
  240. } else {
  241. container.style.left = playerTop
  242. container.style.top = playerLeft
  243. }
  244. if (playerWidth && playerHeight) {
  245. container.style.width = playerWidth
  246. container.style.height = playerHeight
  247. }
  248. const canvasNode = document.getElementById('mainVideoCanvas');
  249. // Call the user routine to setup the scene
  250. var scene = new THREE.Scene();
  251. setupScene(scene);
  252. scene.add(RendererConfig.camera.rig);
  253. const progress = document.getElementById("progressBar");
  254. // The animation routine
  255. function animate() {
  256. progress.setAttribute("value", Math.round((window.videoPlayer.currentTime / window.videoPlayer.duration) * 100))
  257. var dt = clock.getDelta();
  258. var t = clock.getElapsedTime();
  259. if(RendererConfig.animationCallback) {
  260. RendererConfig.animationCallback(t, dt);
  261. }
  262. canvasNode.width = container.clientWidth;
  263. canvasNode.height = container.clientHeight;
  264. domRender.render(scene);
  265. requestAnimationFrame( animate );
  266. }
  267. // The resize handler
  268. window.onWindowResize = function(bool) {
  269. let width = container.clientWidth;
  270. let height = container.clientHeight;
  271. canvasNode.width = width;
  272. canvasNode.height = height;
  273. domRender.setSize(width, height);
  274. renderer.setViewport(0, 0, width, height);
  275. // Show canvas size while resizing
  276. const resizeInfoTime = 3000;
  277. if(!this.resizeInfo) {
  278. this.resizeInfo = document.createElement("div");
  279. this.resizeInfo.style.position = "absolute";
  280. this.resizeInfo.style.top = "0px";
  281. this.resizeInfo.style.left = "0px";
  282. this.resizeInfo.style.color = "white";
  283. this.resizeInfo.innerText = width + "x" + height;
  284. document.body.appendChild(this.resizeInfo);
  285. }
  286. function checkStillResizing() {
  287. if(this.stillResizing) {
  288. this.stillResizing = false;
  289. window.setTimeout(checkStillResizing, resizeInfoTime);
  290. } else {
  291. this.resizeInfo.style.display = "none";
  292. }
  293. }
  294. if(!this.stillResizing) {
  295. window.setTimeout(checkStillResizing, resizeInfoTime);
  296. this.resizeInfo.style.display = "block";
  297. }
  298. this.stillResizing = bool;
  299. this.resizeInfo.innerText = width + " x " + height;
  300. }
  301. // Begin the animation
  302. animate();
  303. }
  304. if(window.onRendererReady) {
  305. window.onRendererReady();
  306. }
  307. window.onload = function () {
  308. initPlaylist()
  309. startAnimation()
  310. window.onWindowResize(true)
  311. };