-
Notifications
You must be signed in to change notification settings - Fork 3
/
worldwebview.html
388 lines (345 loc) · 12.7 KB
/
worldwebview.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
<!-- an experimental interactive web UI, testing code -->
<html>
<head>
<title>naali online</title>
<!-- script src="httpreq.js" type="text/javascript"></script-->
<script type="application/x-javascript">
// todo:
// - selectable resolution for images
// - the map position should probably set and show estiameted view plane instead of camera position
// - simulate moving by compositing existing image(s) suitably?
// - map zoom in/out like in google maps
// - cache images in JS in addition to browser's own cache
// + could also image prefetching on idle time
// - label yaw,pitch and roll so that they actually mean what they do in planes
// - walking and strafing now 2d. for some worlds free 3d would make more sense.
// - figure out how to get images reasonably fast
// + lower resolution, use websockets, etc
// - automatic current view updating (update view every n seconds)
// - get knowledge about the part of the scene the image represents (distance from camera and area?)
// by default try to get screenshots from a local Tundra
var screenshot_server = "http://127.0.0.1:8886/renderimg";
//var screenshot_server = "http://www.playsign.fi:28008";
//var screenshot_server = "http://192.168.0.177:28008"; // playsign's test machine
//var screenshot_server = "http://localhost:9000"; //local opensim
//var screenshot_server = "http://grid004.3dhosting.de:9005/worldview/e4d81017-0265-4242-920f-904566c76439" //Melanie test sim
//var screenshot_server = "http://grid004.3dhosting.de:9000/worldview/c7ace13d-d23e-41c9-b3b5-f26e008260bc"
//var screenshot_server = "http://nebadon2025.homeftp.net:19500/worldview/7a4a0164-3d76-8fe2-187f-171001210001"
var canvas; // screenshot drawing canvas
var ctx; // screenshot drawing context
var map; // map drawing context
var mapelem; // map drawing canvas
var cons; // text console (a div)
var x = 0; // initial starting position
var y = 10;
var z = 60;
var w = 800; // screenshot size on page
var h = 600;
var my = h/2;
var mx = w/2;
var yaw = 0; // should turn camera left/right
var pitch = 0; // should be up/down
var roll = 0; // should rotate camera
var deg2rad = 0.0174532925; // pi/180
var load_time; // page load time
document.onmousemove = function(event) {
mx = event.pageX;
my = event.pageY;
}
//this worked with tundra1
//function euler_to_quat(yaw, pitch, roll) {
//this is changed for tundra2 compat
function euler_to_quat(pitch, yaw, roll) {
log("current angle is yaw=" + yaw + ", pitch=" + pitch + ", roll=" + roll);
yaw *= deg2rad;
pitch *= deg2rad;
roll *= deg2rad;
c1 = Math.cos(yaw/2);
c2 = Math.cos(pitch/2);
c3 = Math.cos(roll/2);
s1 = Math.sin(yaw/2);
s2 = Math.sin(pitch/2);
s3 = Math.sin(roll/2);
c1c2 = c1*c2;
s1s2 = s1*s2;
return({x: c1c2*s3 + s1s2*c3, y: s1*c2*c3 + c1*s2*s3, z: c1*s2*c3 - s1*c2*s3, w: c1c2*c3 - s1s2*s3});
}
var pict = new Image();
// fixme, should be military laser crosshair from elite
function draw_crosshair() {
var mx = w/2;
var my = h/2;
ctx.strokeStyle = "rgba(0,0,0,0.3)";
ctx.lineWidth = 2;
ctx.beginPath();
ctx.arc(mx,my,5,0, Math.PI*2, true);
ctx.stroke();
ctx.strokeStyle = "rgba(255,255,255,0.3)";
ctx.moveTo(mx-2, my-2);
ctx.lineTo(mx+2, my+2);
ctx.moveTo(mx+2, my-2);
ctx.lineTo(mx-2, my+2);
ctx.stroke();
}
var counter = 0; // frame counter
var current_image = new Image();
current_image.counter = -1;
function now_ms() {
var now = new Date();
return now.getTime();
}
function get_screenshot_indirect() { //gets first a url for the img, to allow load sharing on server
var quat = euler_to_quat(yaw, pitch, roll);
var request_uri = screenshot_server + "?posX=" + x + "&posY=" + y + "&posZ=" + z + "&ortX=" + quat.x + "&ortY=" + quat.y + "&ortZ=" + quat.z + "&ortW=" + quat.w;
log(request_uri);
starttime = now_ms();
sendRequest(request_uri, function(req) {
var imgurl = req.responseText;
log("img url: " + req + " - " + imgurl);
get_screenshot(imgurl, starttime);
})
}
function get_screenshot_direct() {
var quat = euler_to_quat(yaw, pitch, roll);
/*var dirx = quat.x / quat.w;
var diry = quat.y / quat.w;
var dirz = quat.z / quat.w;*/
var request_uri = screenshot_server + "?posX=" + x + "&posY=" + y + "&posZ=" + z
//+ "&rotX=" + dirx + "&rotY=" + diry + "&rotZ=" + dirz + "&fov=60&width=800&height=600&usetex=false";
request_uri += "&camortx=" + quat.x + "&camorty=" + quat.y + "&camortz=" + quat.z + "&camortw=" + quat.w;
//+ "&camyaw=" + yaw + "&campitch=" + pitch + "&camroll=" + roll +
starttime = now_ms();
get_screenshot(request_uri, starttime);
}
function get_screenshot(uri, starttime) {
var me = new Image();
log("loading <a href=\"" + uri + "\">frame " + counter + "</a>");
me.request_time = starttime;
me.onerror = function() {
log("<b>load error for frame " + me.counter + "</b>");
}
me.counter = counter;
counter += 1;
me.onload = function() {
var load_time = now_ms() - me.request_time;
if(me.counter > current_image.counter) { // if progress?
current_image = me;
ctx.drawImage(me, 0, 0, w, h);
draw_crosshair();
log("frame " + me.counter + " took " + load_time + "ms");
} else {
log("dropped frame " + me.counter);
}
}
me.src = uri;
}
var map_img = new Image();
function get_map() {
var map_uri = screenshot_server + "?posX=0&posY=100&posZ=0&ortX=0&ortY=0.7&ortZ=0.7&ortW=0" //+&rotX=0&rotY=0&rotZ=-0.027415567780803774&fov=60&width=256&height=256&usetex=false";
log("loading <a href=\"" + map_uri + "\">map</a>");
//use indirect loading
sendRequest(map_uri, function(req) {
var mapurl = req.responseText;
log("map url: " + req + " - " + mapurl);
map_img.src = mapurl;
map_img.onload = function() {
log("map loaded");
map.drawImage(this, 0, 0, 300, 300);
}
});
/*map_img.src = map_uri;
map_img.onerror = function() {
alert("Failed to get a map screenshot from " + screenshot_server + "\n\nCheck that the server is correct, you indeed have Naali running there, and have uncommented the webserver module in the beginning of pymodules/autoload.py.");
}*/
}
// draw a line where i'm looking towards
// fixme, should change colour when looking backwards, and be a cone
function draw_look_vector(x, y) {
var len = Math.sin(roll*deg2rad) * 20;
var dx = Math.sin(pitch*deg2rad) * len;
var dy = Math.cos(pitch*deg2rad) * len;
map.strokeStyle = "rgba(0,0,0,0.4)";
map.lineWidth = 1;
map.beginPath();
map.moveTo(x,y);
map.lineTo(x-dx,y-dy);
map.stroke();
}
function update_map() {
// draw a cirle around me
var mx = Math.floor(x/256 * 300);
var my = Math.floor(y/256 * 300);
my = 300 - my;
if(map_img.complete) {
map.drawImage(map_img, 0, 0, 300, 300);
}
map.strokeStyle = "rgba(0,0,0,0.8)";
map.lineWidth = 1;
map.beginPath();
map.arc(mx,my,2,0, Math.PI*2, true);
map.stroke();
draw_look_vector(mx, my);
}
function update() {
// mouse_turn(mx, my); // last seen mouse position
//get_screenshot_direct(); //naali main thread serves the images too - ok if you have good FPS
get_screenshot_indirect(); //when you have a separate httpd serving the images, not Naali directly
update_map();
}
// a usually fast version of remainder, just in case this will be called a lot later
function crop_angle(angle) {
if (angle >= 360)
return(crop_angle(angle - 360));
if (angle < 0)
return(crop_angle(360 + angle));
return(angle)
}
function mouse_turn(x, y) {
hh = h/2;
hw = w/2;
var yd = (y - hh)/hh; // -1 .. 1
var xd = (x - hw)/hw; // -1 .. 1
roll = crop_angle(roll - yd*23); // estimated
pitch = crop_angle(pitch - xd*27); // estimated
}
function handle_click(e) {
var x = e.clientX - canvas.offsetLeft;
var y = e.clientY - canvas.offsetTop;
log("clicked at(" + x + ", " + y + ")");
mouse_turn(x, y); // change orientation locally
update();
}
function log(msg) {
cons.innerHTML = "> " + msg + "<br>" + cons.innerHTML;
}
function handle_map_click(e) {
var s = 256; // map coords (0 .. 256)
var mx = e.pageX - mapelem.offsetLeft;
var my = e.pageY - mapelem.offsetTop;
var w = 300; // map width
var h = 300; // map height
log("map clicked at(" + mx + ", " + my + ")");
var wx = mx/w; // 0..1
var wy = my/h;
x = (s * wx);
z = s - (s * wy);
x -= 128; //XXX TODO: this is still not right for Tundra2
z -= 128;
log("moved to x=" + x + ", y=" + y);
update();
}
function strafe(speed) { // speed = int
var dy = Math.sin(pitch*deg2rad) * speed;
var dx = Math.cos(pitch*deg2rad) * speed;
log("strafing " + speed);
x = x + dx;
y = y + dy;
update();
}
function walk(speed) { // speed = int
var dx = Math.sin(pitch*deg2rad) * speed;
var dy = Math.cos(pitch*deg2rad) * speed;
log("walking " + speed);
x = x - dx;
z = z + dy;
update();
}
var s = 10;
document.onkeydown = function(event) {
var keyCode;
if(event == null)
key = window.event.keyCode;
else
key = event.keyCode;
//log("key " + key);
switch(key) {
// up/down = walk along the direction vector
case 40: walk(15); break; // up/down
case 38: walk(-15); break;
// left/right = strafe correspondingly
case 37: strafe(-10); break; // up/down
case 39: strafe(10); break;
// space/c move up/down
case 32: y += 4; log("height " + z); update(); break; // space moves up (as in naali)
case 67: y -= 4; log("height " + z); update(); break; // c moves down (as in naali)
default:
log("unmapped key " + key);
}
}
// update view later if it's been a while since the last one
function beatnik() {
log("dumdidum");
}
function init() {
load_time = now_ms();
canvas = document.getElementById('canvas');
ctx = canvas.getContext('2d');
mapelem = document.getElementById('map');
cons = document.getElementById('consoleview');
log("cons is " + cons);
map = mapelem.getContext('2d');
canvas.addEventListener("click", handle_click, false);
mapelem.addEventListener("click", handle_map_click, true);
setInterval(beatnik, 100000);
get_map();
update();
}
//from http://www.quirksmode.org/js/xmlhttp.html
function sendRequest(url,callback,postData) {
var req = createXMLHTTPObject();
if (!req) return;
var method = (postData) ? "POST" : "GET";
req.open(method,url,true);
req.setRequestHeader('User-Agent','XMLHTTP/1.0');
if (postData)
req.setRequestHeader('Content-type','application/x-www-form-urlencoded');
req.onreadystatechange = function () {
if (req.readyState != 4) return;
if (req.status != 200 && req.status != 304) {
//alert('HTTP error ' + req.status);
log('ERROR: ' + url + ", " + req.status);
return;
}
callback(req);
}
if (req.readyState == 4) return;
req.send(postData);
}
var XMLHttpFactories = [
function () {return new XMLHttpRequest()},
function () {return new ActiveXObject("Msxml2.XMLHTTP")},
function () {return new ActiveXObject("Msxml3.XMLHTTP")},
function () {return new ActiveXObject("Microsoft.XMLHTTP")}
];
function createXMLHTTPObject() {
var xmlhttp = false;
for (var i=0;i<XMLHttpFactories.length;i++) {
try {
xmlhttp = XMLHttpFactories[i]();
}
catch (e) {
continue;
}
break;
}
return xmlhttp;
}
</script>
<style type="text/css">
body { margin: 0px; padding:0px; background-color: black; color: #aaa;}
div.consoleview { border: 1px dotted #aa0; background-color: gray; color: white; }
canvas { border: 1px dotted #aaa; margin-top: 20px; }
a { color: #c80; text-decoration: none; }
a:hover { color: #ff0; text-decoration: underline; }
</style>
</head>
<body onload="init();">
<div>
<canvas id="canvas" width="800" height="600"></canvas>
<div style="float: left">
<canvas id="map" width="300" height="300"></canvas><br>
<div style="overflow: auto; border: none; width:300px; height:300px; overflow: auto" id="consoleview" width="300" height="300">Starting web UI.</div>
</div>
</div>
</body>
</html>