]> git.plutz.net Git - serve0/blob - stereoview.js
Merge commit 'b931bbd0c30907b9cc956d3707b26b449bf41f76'
[serve0] / stereoview.js
1 /*  Copyright 2018, 2023 Paul Hänsch
2    
3     This file is part of Serve0
4     
5     Serve0 is free software: you can redistribute it and/or modify
6     it under the terms of the GNU Affero General Public License as published by
7     the Free Software Foundation, either version 3 of the License, or
8     (at your option) any later version.
9     
10     Serve0 is distributed in the hope that it will be useful,
11     but WITHOUT ANY WARRANTY; without even the implied warranty of
12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13     GNU Affero General Public License for more details.
14     
15     You should have received a copy of the GNU Affero General Public License
16     along with Serve0  If not, see <http://www.gnu.org/licenses/>. 
17 */
18
19 var contLeft  = document.createElement("div");
20 var contRight = document.createElement("div");
21 var lv = document.createElement("canvas");
22 var rv = document.createElement("canvas");
23 var debug = document.createElement("p");
24
25  contLeft.setAttribute("style", "position: fixed; top: 0; left:  0; width: 50%; height: 100%; overflow: hidden; z-index: 100; background-color: #000;");
26 contRight.setAttribute("style", "position: fixed; top: 0; right: 0; width: 50%; height: 100%; overflow: hidden; z-index: 100; background-color: #000;");
27        lv.setAttribute("style", "position: absolute; top: 50%; left: calc(100% - 32mm); max-width: unset; max-height: unset;");
28        rv.setAttribute("style", "position: absolute; top: 50%; left:             32mm;  max-width: unset; max-height: unset;");
29     debug.setAttribute("style", "display: none; position: fixed; top: 0; left: 0; z-index: 101; background: #000;");
30
31
32 function stereoview(layout, video) {
33   this.layout = layout; this.video = video;
34
35   let w = video.videoWidth; h = video.videoHeight;
36   let render, controlTimeout = 0;
37   let pitch = 0, roll = 0, yaw = 0;
38   let scale, vsf = 1, fov = 90, dist = 32;
39
40   document.body.appendChild(contLeft ).appendChild( lv );
41   document.body.appendChild(contRight).appendChild( rv );
42   document.body.appendChild( debug );
43
44   if ( layout == "180") {
45     lv.width = rv.width = w / 2; lv.height = rv.height = h;
46     scale = contLeft.offsetHeight / h * 2 * fov / 90;
47   } else {
48     lv.width = rv.width = w; lv.height = rv.height = h / 2;
49     scale = contLeft.offsetHeight / h * 4 * fov / 90;
50   }
51
52   lc = lv.getContext("2d"); rc = rv.getContext("2d");
53
54   function draw() {
55     if ( layout == "180" ) {
56       // scale = contLeft.offsetHeight / h * 2 * fov / 90;
57       lc.drawImage(video,      0, 0);
58       rc.drawImage(video, -w / 2, 0);
59       lv.style.transform = rv.style.transform = 
60         "translate(" + (yaw / 180 * -w / 2 - w / 4) + "px, " +
61                        (pitch / 90 * -h / 2 * scale - h / 2) + "px)" +
62         "rotate(" + roll + "deg) " + "scale(" + (scale / vsf) + ", " + scale + ")";
63     } else {
64       // scale = contLeft.offsetHeight / h * 4 * fov / 90;
65       lc.drawImage(video, yaw / 180 * -w / 2, 0);
66       lc.drawImage(video, yaw / 180 * -w / 2 + ((yaw>0) ? w : -w), 0);
67       rc.drawImage(video, yaw / 180 * -w / 2, -h/2);
68       rc.drawImage(video, yaw / 180 * -w / 2 + ((yaw>0) ? w : -w), -h/2);
69       lv.style.transform = rv.style.transform = 
70         "translate(" + (- w / 2) + "px, " + (pitch /  90 * -h / 2 * (scale / 2) - h / 4) + "px) " +
71         "rotate(" + roll + "deg) " + "scale(" + (scale / vsf) + ", " + scale + ")";
72     }
73
74     // debug.textContent = "" + video.currentTime + " " + controlTimeout + " " + tx + " " + ty + " " + tz;
75     debug.textContent = "Pitch: " + pitch.toFixed(2) + " | Yaw: " + yaw.toFixed(2) + " | Roll: " + roll.toFixed(2) +
76                         " | FOV: " + fov + " | Eye Distance: " + (2 * dist) + "mm";
77   
78     requestAnimationFrame(draw);
79
80     gpinput();
81   };
82
83   function gpinput() {
84     let gp = navigator.getGamepads()[0];
85     let date = new Date();
86
87     debug.innerHTML += "<br/><br/>GamePad: " + gp.axes[0].toFixed(3) + " | " + gp.axes[1].toFixed(3)
88     for (cnt = 0; cnt < 16; cnt++) {
89       gp.buttons[cnt].pressed ? debug.textContent += " | B" + cnt + " 1" : debug.textContent += " | B" + cnt + " 0";
90     }
91
92     if ( gp && Date.now() < controlTimeout ) {
93       true;
94     } else if (gp.buttons[0].pressed && gp.buttons[5].pressed) {
95       this.layout = layout = "180";
96       document.body.appendChild(contLeft ).appendChild( lv );
97       document.body.appendChild(contRight).appendChild( rv );
98       lv.width = rv.width = w / 2; lv.height = rv.height = h;
99       scale = contLeft.offsetHeight / h * 2 * fov / 90;
100       video.play();
101       controlTimeout = Date.now() + 600;
102     } else if (gp.buttons[0].pressed && gp.buttons[4].pressed) {
103       this.layout = layout = "360";
104       document.body.appendChild(contLeft ).appendChild( lv );
105       document.body.appendChild(contRight).appendChild( rv );
106       lv.width = rv.width = w; lv.height = rv.height = h / 2;
107       scale = contLeft.offsetHeight / h * 4 * fov / 90;
108       video.play();
109       controlTimeout = Date.now() + 600;
110     } else if (gp.buttons[3].pressed && gp.buttons[4].pressed) {
111       vsf = (vsf == 1) ? 2 : 1;
112       controlTimeout = Date.now() + 300;
113     } else if (gp.buttons[0].pressed) {
114       ( contLeft.parentElement)? contLeft.parentElement.removeChild(contLeft ):{};
115       (contRight.parentElement)?contRight.parentElement.removeChild(contRight):{};
116       video.pause();
117       controlTimeout = Date.now() + 300;
118     } else if (gp.buttons[1].pressed) {
119       (debug.style.display == "block") ? debug.style.display = "none" : debug.style.display = "block";
120       controlTimeout = Date.now() + 300;
121     } else if (gp.buttons[5].pressed) {
122       video.paused ? video.play() : video.pause();
123       controlTimeout = Date.now() + 300;
124     } else if (gp.buttons[4].pressed) {
125       video.currentTime += 1 / 30;
126       controlTimeout = Date.now() + 300;
127     } else if (gp.buttons[3].pressed && gp.axes[0] < -.3) {
128       dist -= .5;
129       lv.style.left = "calc(100% - " + dist + "mm)";
130       rv.style.left = ""             + dist + "mm";
131       date.setTime(date.getTime() + 3 * 365 * 86400 * 1000)
132       document.cookie = "StereoDist=" + dist + "; expires=" + date.toUTCString() + "; path=/";
133       controlTimeout = Date.now() + 300;
134     } else if (gp.buttons[3].pressed && gp.axes[0] >  .3) {
135       dist += .5;
136       lv.style.left = "calc(100% - " + dist + "mm)";
137       rv.style.left = ""             + dist + "mm";
138       date.setTime(date.getTime() + 3 * 365 * 86400 * 1000)
139       document.cookie = "StereoDist=" + dist + "; expires=" + date.toUTCString() + "; path=/";
140       controlTimeout = Date.now() + 300;
141     } else if (gp.buttons[3].pressed && gp.axes[1] < -.3) {
142       fov -= 10;
143       date.setTime(date.getTime() + 3 * 365 * 86400 * 1000)
144       document.cookie = "StereoFOV=" + fov + "; expires=" + date.toUTCString() + "; path=/";
145       controlTimeout = Date.now() + 300;
146     } else if (gp.buttons[3].pressed && gp.axes[1] >  .3) {
147       fov += 10;
148       date.setTime(date.getTime() + 3 * 365 * 86400 * 1000)
149       document.cookie = "StereoFOV=" + fov + "; expires=" + date.toUTCString() + "; path=/";
150       controlTimeout = Date.now() + 300;
151     } else if (gp.buttons[2].pressed && gp.axes[0] < -.3) {
152       yaw -= pitch ? ( Math.cos(pitch * Math.PI / 180) % 360 ) : 1;
153       if ( yaw >  180) yaw -= 360;
154       if ( yaw < -180) yaw += 360;
155       controlTimeout = Date.now() + 30;
156     } else if (gp.buttons[2].pressed && gp.axes[0] >  .3) {
157       yaw += pitch ? ( Math.cos(pitch * Math.PI / 180) % 360 ) : 1;
158       if ( yaw >  180) yaw -= 360;
159       if ( yaw < -180) yaw += 360;
160       controlTimeout = Date.now() + 30;
161     } else if (gp.buttons[2].pressed && gp.axes[1] < -.3) {
162       video.volume += .05;
163       controlTimeout = Date.now() + 300;
164     } else if (gp.buttons[2].pressed && gp.axes[1] >  .3) {
165       video.volume -= .05;
166       controlTimeout = Date.now() + 300;
167     } else if ( gp.axes[0] >  .3) {
168       video.currentTime += 10;
169       controlTimeout = Date.now() + 200;
170     } else if ( gp.axes[0] < -.3) { 
171       video.currentTime -= 10;
172       controlTimeout = Date.now() + 200;
173     } else if ( gp.axes[1] >  .3) {
174       video.currentTime -= 60;
175       controlTimeout = Date.now() + 300;
176     } else if ( gp.axes[1] < -.3) {
177       video.currentTime += 60;
178       controlTimeout = Date.now() + 300;
179     }
180   }
181   
182   // mpuevent = new EventSource("http://localhost:314");
183   // var x = [], y = [], z = [], cnt = -1, inertia = 6;
184
185   // mpuevent.addEventListener("bearing", function(e) {
186   //   bearing = e.data.split(" ");
187   //   yaw = -parseFloat(bearing[0]);
188   // }, false);
189   // mpuevent.addEventListener("motion", function(e) {
190   //   motion = e.data.split(" ");
191
192   //   cnt = (cnt + 1) % inertia;
193   //   x[cnt] = parseFloat(motion[0]);
194   //   y[cnt] = parseFloat(motion[1]);
195   //   z[cnt] = parseFloat(motion[2]);
196
197   //   // tx = 0; x.forEach( function(n, i){ tx += n; } ); tx /= inertia;
198   //   ty = 0; y.forEach( function(n, i){ ty += n; } ); ty /= inertia;
199   //   tz = 0; z.forEach( function(n, i){ tz += n; } ); tz /= inertia;
200
201   //   pitch =   Math.asin((tz / 9.81 > 1)?1:(tz/9.81)) / Math.PI * 180 + 22.5;
202   //   roll  = - Math.asin((ty / 9.81 > 1)?1:(ty/9.81)) / Math.PI * 180;
203   //   // yaw   = (yaw + ty) % 360;
204   // }, false );
205
206   window.addEventListener("devicemotion", (function() {
207     let x = [], y = [], z = [], cnt = -1, inertia = 6;
208     return event => {
209       cnt = (cnt + 1) % inertia;
210
211       x[cnt] = event.accelerationIncludingGravity.x;
212       y[cnt] = event.accelerationIncludingGravity.y;
213       z[cnt] = event.accelerationIncludingGravity.z;
214
215       tx = 0; x.forEach( function(n, i){ tx += n; } ); tx /= inertia;
216       ty = 0; y.forEach( function(n, i){ ty += n; } ); ty /= inertia;
217       tz = 0; z.forEach( function(n, i){ tz += n; } ); tz /= inertia;
218
219       pitch =   Math.asin((tz / 9.81 > 1)?1:(tz/9.81)) / Math.PI * 180;
220       roll  = - Math.asin((ty / 9.81 > 1)?1:(ty/9.81)) / Math.PI * 180;
221       yaw   = pitch ? ( yaw
222               - event.rotationRate.alpha / 1000 * event.interval * Math.cos(pitch * Math.PI / 180)
223               - event.rotationRate.gamma / 1000 * event.interval * Math.sin(pitch * Math.PI / 180)
224             ) % 360 : yaw;
225       if ( yaw >  180) yaw -= 360;
226       if ( yaw < -180) yaw += 360;
227
228       // pitch = (pitch + event.rotationRate.beta  / 1000 * event.interval) % 360;
229       // roll  = (roll  + event.rotationRate.gamma / 1000 * event.interval) % 360;
230       // yaw   = (yaw + ty) % 360;
231     };
232   })());
233
234   window.addEventListener("click", function(event) {
235     ( contLeft.parentElement)? contLeft.parentElement.removeChild(contLeft ):{};
236     (contRight.parentElement)?contRight.parentElement.removeChild(contRight):{};
237     // video.style.display = "block";
238     video.pause();
239   }, true);
240
241   dist = dist ? dist : parseInt(document.getElementById("StereoDist").getAttribute("value"));
242   fov  =  fov ?  fov : parseInt(document.getElementById("StereoFOV" ).getAttribute("value"));
243
244   video.play();
245   // video.style.display = "none";
246   draw();
247 };