lavavu-osmesa 1.9.9__cp313-cp313-manylinux_2_28_x86_64.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- lavavu/LavaVuPython.py +561 -0
- lavavu/_LavaVuPython.cpython-313-x86_64-linux-gnu.so +0 -0
- lavavu/__init__.py +15 -0
- lavavu/__main__.py +12 -0
- lavavu/amalgamate.py +15 -0
- lavavu/aserver.py +359 -0
- lavavu/control.py +1731 -0
- lavavu/convert.py +888 -0
- lavavu/dict.json +2528 -0
- lavavu/font.bin +0 -0
- lavavu/html/LavaVu-amalgamated.css +282 -0
- lavavu/html/OK-min.js +99 -0
- lavavu/html/baseviewer.js +307 -0
- lavavu/html/control.css +104 -0
- lavavu/html/control.js +340 -0
- lavavu/html/dat-gui-light-theme.css +68 -0
- lavavu/html/dat.gui.min.js +2 -0
- lavavu/html/draw.js +2259 -0
- lavavu/html/drawbox.js +1039 -0
- lavavu/html/emscripten-template.js +184 -0
- lavavu/html/emscripten.css +92 -0
- lavavu/html/favicon.ico +0 -0
- lavavu/html/gl-matrix-min.js +47 -0
- lavavu/html/gui.css +25 -0
- lavavu/html/menu.js +615 -0
- lavavu/html/server.js +226 -0
- lavavu/html/stats.min.js +5 -0
- lavavu/html/styles.css +58 -0
- lavavu/html/webview-template.html +43 -0
- lavavu/html/webview.html +43 -0
- lavavu/lavavu.py +6200 -0
- lavavu/osmesa/LavaVuPython.py +561 -0
- lavavu/osmesa/_LavaVuPython.cpython-313-x86_64-linux-gnu.so +0 -0
- lavavu/osmesa/__init__.py +0 -0
- lavavu/points.py +191 -0
- lavavu/server.py +343 -0
- lavavu/shaders/default.frag +14 -0
- lavavu/shaders/default.vert +17 -0
- lavavu/shaders/fontShader.frag +20 -0
- lavavu/shaders/fontShader.vert +18 -0
- lavavu/shaders/lineShader.frag +39 -0
- lavavu/shaders/lineShader.vert +26 -0
- lavavu/shaders/pointShader.frag +127 -0
- lavavu/shaders/pointShader.vert +53 -0
- lavavu/shaders/triShader.frag +153 -0
- lavavu/shaders/triShader.vert +49 -0
- lavavu/shaders/volumeShader.frag +400 -0
- lavavu/shaders/volumeShader.vert +5 -0
- lavavu/tracers.py +207 -0
- lavavu/vutils.py +211 -0
- lavavu_osmesa-1.9.9.dist-info/METADATA +323 -0
- lavavu_osmesa-1.9.9.dist-info/RECORD +65 -0
- lavavu_osmesa-1.9.9.dist-info/WHEEL +5 -0
- lavavu_osmesa-1.9.9.dist-info/entry_points.txt +2 -0
- lavavu_osmesa-1.9.9.dist-info/licenses/LICENSE.md +179 -0
- lavavu_osmesa-1.9.9.dist-info/top_level.txt +1 -0
- lavavu_osmesa.libs/libLLVM-17-51492e70.so +0 -0
- lavavu_osmesa.libs/libOSMesa-f6a8f160.so.8.0.0 +0 -0
- lavavu_osmesa.libs/libdrm-b0291a67.so.2.4.0 +0 -0
- lavavu_osmesa.libs/libffi-3a37023a.so.6.0.2 +0 -0
- lavavu_osmesa.libs/libglapi-520b284c.so.0.0.0 +0 -0
- lavavu_osmesa.libs/libpcre2-8-516f4c9d.so.0.7.1 +0 -0
- lavavu_osmesa.libs/libselinux-d0805dcb.so.1 +0 -0
- lavavu_osmesa.libs/libtinfo-3a2cb85b.so.6.1 +0 -0
- lavavu_osmesa.libs/libzstd-76b78bac.so.1.4.4 +0 -0
lavavu/html/draw.js
ADDED
@@ -0,0 +1,2259 @@
|
|
1
|
+
//WebGL viewer, Owen Kaluza (c) Monash University 2012-18
|
2
|
+
// License: LGPLv3 for now
|
3
|
+
var server = false;
|
4
|
+
var types = ["triangles", "points", "lines", "volume"]
|
5
|
+
var MAXIDX = 2047;
|
6
|
+
/** @const */
|
7
|
+
var DEBUG = false;
|
8
|
+
|
9
|
+
function initPage(elid, menu, src) {
|
10
|
+
//Load from the data tag if available, otherwise URL
|
11
|
+
var dtag = document.getElementById('data');
|
12
|
+
if (!src && dtag && dtag.innerHTML.length > 100)
|
13
|
+
src = dtag.innerHTML;
|
14
|
+
|
15
|
+
var urlq = decodeURI(window.location.href);
|
16
|
+
if (!src && urlq.indexOf("?") > 0) {
|
17
|
+
var parts = urlq.split("?"); //whole querystring before and after ?
|
18
|
+
var query = parts[1];
|
19
|
+
|
20
|
+
if (query.indexOf(".json") > 0) {
|
21
|
+
//Passed a json(p) file on URL
|
22
|
+
if (query.indexOf(".jsonp") > 0) {
|
23
|
+
//Load jsonp file as a script, useful when opening page as file://
|
24
|
+
//only way to get around security issues in chrome,
|
25
|
+
//script calls the loadData function providing the content
|
26
|
+
var script = document.createElement('script');
|
27
|
+
script.id = 'script';
|
28
|
+
script.style.display = 'none';
|
29
|
+
script.src = query;
|
30
|
+
document.body.appendChild(script);
|
31
|
+
} else {
|
32
|
+
var el = document.getElementById('fileupload');
|
33
|
+
if (el) el.style.display = "none";
|
34
|
+
progress("Downloading model data from server...");
|
35
|
+
ajaxReadFile(query, initPage, false, updateProgress);
|
36
|
+
}
|
37
|
+
return;
|
38
|
+
}
|
39
|
+
} else if (!src && urlq.indexOf("#") > 0) {
|
40
|
+
//IPython strips out ? args so have to check for this instead
|
41
|
+
var parts = urlq.split("#"); //whole querystring before and after #
|
42
|
+
if (parts[1].indexOf(".json") > 0) {
|
43
|
+
//Load filename from url
|
44
|
+
ajaxReadFile(parts[1], initPage, false);
|
45
|
+
return;
|
46
|
+
}
|
47
|
+
|
48
|
+
//Load base64 encoded data from url
|
49
|
+
window.location.hash = ""
|
50
|
+
src = window.atob(parts[1]);
|
51
|
+
}
|
52
|
+
|
53
|
+
progress();
|
54
|
+
|
55
|
+
var container;
|
56
|
+
if (elid)
|
57
|
+
container = document.getElementById(elid);
|
58
|
+
else
|
59
|
+
container = document.body;
|
60
|
+
var canvas = document.createElement("canvas");
|
61
|
+
canvas.id = "canvas_" + container.id;
|
62
|
+
container.appendChild(canvas);
|
63
|
+
var viewer = new Viewer(canvas);
|
64
|
+
viewer.menu = menu; //GUI menu flag
|
65
|
+
if (canvas) {
|
66
|
+
//this.canvas = document.createElement("canvas");
|
67
|
+
//this.canvas.style.cssText = "width: 100%; height: 100%; z-index: 0; margin: 0px; padding: 0px; background: black; border: none; display:block;";
|
68
|
+
//if (!parentEl) parentEl = document.body;
|
69
|
+
//parentEl.appendChild(this.canvas);
|
70
|
+
|
71
|
+
//Canvas event handling
|
72
|
+
var handler = new MouseEventHandler(canvasMouseClick, canvasMouseWheel, canvasMouseMove, canvasMouseDown, null, null, canvasMousePinch);
|
73
|
+
canvas.mouse = new Mouse(canvas, handler);
|
74
|
+
//Following two settings should probably be defaults?
|
75
|
+
canvas.mouse.moveUpdate = true; //Continual update of deltaX/Y
|
76
|
+
//canvas.mouse.setDefault();
|
77
|
+
|
78
|
+
canvas.mouse.wheelTimer = true; //Accumulate wheel scroll (prevents too many events backing up)
|
79
|
+
defaultMouse = document.mouse = canvas.mouse;
|
80
|
+
|
81
|
+
//Reference to viewer object on canvas for event handling
|
82
|
+
canvas.viewer = viewer;
|
83
|
+
window.viewer = viewer; //Only most recent stored on window
|
84
|
+
|
85
|
+
//Enable auto resize for full screen view
|
86
|
+
if (canvas.parentElement == document.body) {
|
87
|
+
window.onresize = function() {viewer.drawTimed();};
|
88
|
+
//No border
|
89
|
+
canvas.style.cssText = "width: 100%; height: 100%; z-index: 0; margin: 0px; padding: 0px; background: black; border: none; display:block;";
|
90
|
+
} else {
|
91
|
+
//Light border
|
92
|
+
canvas.style.cssText = "width: 100%; height: 100%; z-index: 0; margin: 0px; padding: 0px; background: black; border: 1px solid #aaa; display:block;";
|
93
|
+
}
|
94
|
+
}
|
95
|
+
|
96
|
+
//Data dict and colourmap names stored in globals
|
97
|
+
viewer.dict = window.dictionary;
|
98
|
+
viewer.defaultcolourmaps = window.defaultcolourmaps;
|
99
|
+
|
100
|
+
if (query && query.indexOf("server") >= 0) {
|
101
|
+
//Switch to image frame
|
102
|
+
setAll('', 'server');
|
103
|
+
setAll('none', 'client');
|
104
|
+
|
105
|
+
server = true;
|
106
|
+
|
107
|
+
//if (!viewer.gl) {
|
108
|
+
//img = document.getElementById('frame');
|
109
|
+
//Image canvas event handling
|
110
|
+
//img.mouse = new Mouse(img, new MouseEventHandler(serverMouseClick, serverMouseWheel, serverMouseMove, serverMouseDown));
|
111
|
+
//}
|
112
|
+
|
113
|
+
//Initiate the server update
|
114
|
+
requestData('/connect', parseRequest);
|
115
|
+
//requestData('/objects', parseObjects);
|
116
|
+
|
117
|
+
//Enable to forward key presses to server directly
|
118
|
+
//document.onkeypress = keyPress;
|
119
|
+
window.onbeforeunload = function() {if (client_id >= 0) requestData("/disconnect=" + client_id, null, true); client_id = -1;};
|
120
|
+
|
121
|
+
//Set viewer window to match ours?
|
122
|
+
//resizeToWindow();
|
123
|
+
|
124
|
+
} else {
|
125
|
+
setAll('none', 'server');
|
126
|
+
setAll('', 'client');
|
127
|
+
var frame = document.getElementById('frame');
|
128
|
+
if (frame)
|
129
|
+
frame.style.display = 'none';
|
130
|
+
}
|
131
|
+
|
132
|
+
if (src) {
|
133
|
+
viewer.loadFile(src);
|
134
|
+
} else {
|
135
|
+
var source = getSourceFromElement('source');
|
136
|
+
if (source) {
|
137
|
+
//Preloaded data
|
138
|
+
var el = document.getElementById('fileupload');
|
139
|
+
if (el) el.style.display = "none";
|
140
|
+
viewer.loadFile(source);
|
141
|
+
} else {
|
142
|
+
//Demo objects
|
143
|
+
//demoData();
|
144
|
+
viewer.draw();
|
145
|
+
}
|
146
|
+
}
|
147
|
+
|
148
|
+
//VR setup
|
149
|
+
if (navigator.getVRDisplays)
|
150
|
+
setup_VR(viewer);
|
151
|
+
}
|
152
|
+
|
153
|
+
function loadData(data) {
|
154
|
+
initPage(data);
|
155
|
+
}
|
156
|
+
|
157
|
+
function progress(text) {
|
158
|
+
var el = document.getElementById('progress');
|
159
|
+
if (!el) return;
|
160
|
+
if (el.style.display == 'block' || text == undefined)
|
161
|
+
//el.style.display = 'none';
|
162
|
+
setTimeout("document.getElementById('progress').style.display = 'none';", 150);
|
163
|
+
else {
|
164
|
+
document.getElementById('progressmessage').innerHTML = text;
|
165
|
+
document.getElementById('progressstatus').innerHTML = "";
|
166
|
+
document.getElementById('progressbar').style.width = 0;
|
167
|
+
el.style.display = 'block';
|
168
|
+
}
|
169
|
+
}
|
170
|
+
|
171
|
+
function canvasMouseClick(event, mouse) {
|
172
|
+
if (server) {
|
173
|
+
if (mouse.element.viewer.rotating)
|
174
|
+
sendCommand('' + mouse.element.viewer.getRotationString());
|
175
|
+
else
|
176
|
+
sendCommand('' + mouse.element.viewer.getTranslationString());
|
177
|
+
}
|
178
|
+
|
179
|
+
//if (server) serverMouseClick(event, mouse); //Pass to server handler
|
180
|
+
if (mouse.element.viewer.rotating) {
|
181
|
+
mouse.element.viewer.rotating = false;
|
182
|
+
//mouse.element.viewer.reload = true;
|
183
|
+
sortTimer(false, mouse.element.viewer);
|
184
|
+
return false;
|
185
|
+
}
|
186
|
+
|
187
|
+
mouse.element.viewer.draw();
|
188
|
+
return false;
|
189
|
+
}
|
190
|
+
|
191
|
+
function sortTimer(ifexists, viewer) {
|
192
|
+
if (viewer.vis.sortenabled == false) {
|
193
|
+
//Sorting disabled
|
194
|
+
viewer.rotated = false;
|
195
|
+
viewer.draw();
|
196
|
+
return;
|
197
|
+
}
|
198
|
+
if (viewer.vis.immediatesort == true) {
|
199
|
+
//No timers
|
200
|
+
viewer.rotated = true;
|
201
|
+
viewer.draw();
|
202
|
+
return;
|
203
|
+
}
|
204
|
+
//Set a timer to apply the sort function in 2 seconds
|
205
|
+
if (viewer.timer) {
|
206
|
+
clearTimeout(viewer.timer);
|
207
|
+
} else if (ifexists) {
|
208
|
+
//No existing timer? Don't start a new one
|
209
|
+
return;
|
210
|
+
}
|
211
|
+
|
212
|
+
viewer.timer = setTimeout(function() {viewer.rotated = true; viewer.draw();}, 50);
|
213
|
+
}
|
214
|
+
|
215
|
+
function canvasMouseDown(event, mouse) {
|
216
|
+
//if (server) serverMouseDown(event, mouse); //Pass to server handler
|
217
|
+
return false;
|
218
|
+
}
|
219
|
+
|
220
|
+
var hideTimer;
|
221
|
+
|
222
|
+
function canvasMouseMove(event, mouse) {
|
223
|
+
//if (server) serverMouseMove(event, mouse); //Pass to server handler
|
224
|
+
|
225
|
+
//GUI elements to show on mouseover
|
226
|
+
if (mouse.element && mouse.element.viewer) {
|
227
|
+
var gui = mouse.element.viewer.gui;
|
228
|
+
var rect = mouse.element.getBoundingClientRect();
|
229
|
+
x = event.clientX-rect.left;
|
230
|
+
y = event.clientY-rect.top;
|
231
|
+
if (x >= 0 && y >= 0 && x < rect.width && y < rect.height) {
|
232
|
+
if (!gui && mouse.element.imgtarget)
|
233
|
+
mouse.element.imgtarget.nextElementSibling.style.display = "block";
|
234
|
+
|
235
|
+
if (gui) {
|
236
|
+
if (mouse.element.imgtarget) mouse.element.imgtarget.nextElementSibling.style.display = "none";
|
237
|
+
gui.domElement.style.display = "block";
|
238
|
+
}
|
239
|
+
|
240
|
+
if (hideTimer)
|
241
|
+
clearTimeout(hideTimer);
|
242
|
+
|
243
|
+
hideTimer = setTimeout(function () { hideMenu(mouse.element, gui);}, 1000 );
|
244
|
+
}
|
245
|
+
}
|
246
|
+
|
247
|
+
if (!mouse.isdown || !mouse.element.viewer) return true;
|
248
|
+
mouse.element.viewer.rotating = false;
|
249
|
+
|
250
|
+
//Switch buttons for translate/rotate
|
251
|
+
var button = mouse.button;
|
252
|
+
if (mouse.element.viewer.mode == "Translate") {
|
253
|
+
//Swap rotate/translate buttons
|
254
|
+
if (button == 0)
|
255
|
+
button = 2
|
256
|
+
else if (button == 2)
|
257
|
+
button = 0;
|
258
|
+
} else if (button==0 && mouse.element.viewer.mode == "Zoom") {
|
259
|
+
button = 100;
|
260
|
+
}
|
261
|
+
|
262
|
+
//console.log(mouse.deltaX + "," + mouse.deltaY);
|
263
|
+
switch (button)
|
264
|
+
{
|
265
|
+
case 0:
|
266
|
+
mouse.element.viewer.rotateY(mouse.deltaX/5);
|
267
|
+
mouse.element.viewer.rotateX(mouse.deltaY/5);
|
268
|
+
mouse.element.viewer.rotating = true;
|
269
|
+
sortTimer(true, mouse.element.viewer); //Delay sort if queued
|
270
|
+
break;
|
271
|
+
case 1:
|
272
|
+
mouse.element.viewer.rotateZ(Math.sqrt(mouse.deltaX*mouse.deltaX + mouse.deltaY*mouse.deltaY)/5);
|
273
|
+
mouse.element.viewer.rotating = true;
|
274
|
+
sortTimer(true, mouse.element.viewer); //Delay sort if queued
|
275
|
+
break;
|
276
|
+
case 2:
|
277
|
+
var adjust = mouse.element.viewer.modelsize / 1000; //1/1000th of size
|
278
|
+
mouse.element.viewer.translate[0] += mouse.deltaX * adjust;
|
279
|
+
mouse.element.viewer.translate[1] -= mouse.deltaY * adjust;
|
280
|
+
break;
|
281
|
+
case 100:
|
282
|
+
var adjust = mouse.element.viewer.modelsize / 1000; //1/1000th of size
|
283
|
+
mouse.element.viewer.translate[2] += mouse.deltaX * adjust;
|
284
|
+
break;
|
285
|
+
}
|
286
|
+
|
287
|
+
//Always draw border while interacting in server mode?
|
288
|
+
if (server)
|
289
|
+
mouse.element.viewer.draw(true);
|
290
|
+
//Draw border while interacting (automatically on for models > 500K vertices)
|
291
|
+
//Hold shift to switch from default behaviour
|
292
|
+
else if (mouse.element.viewer.vis.interactive)
|
293
|
+
mouse.element.viewer.draw();
|
294
|
+
else
|
295
|
+
mouse.element.viewer.draw(!event.shiftKey);
|
296
|
+
return false;
|
297
|
+
}
|
298
|
+
|
299
|
+
var zoomTimer;
|
300
|
+
var zoomClipTimer;
|
301
|
+
var zoomSpin = 0;
|
302
|
+
|
303
|
+
function canvasMouseWheel(event, mouse) {
|
304
|
+
if (event.shiftKey) {
|
305
|
+
var factor = event.spin * 0.01;
|
306
|
+
if (zoomClipTimer) clearTimeout(zoomClipTimer);
|
307
|
+
zoomClipTimer = setTimeout(function () {mouse.element.viewer.zoomClip(factor);}, 100 );
|
308
|
+
} else {
|
309
|
+
if (zoomTimer)
|
310
|
+
clearTimeout(zoomTimer);
|
311
|
+
zoomSpin += event.spin;
|
312
|
+
zoomTimer = setTimeout(function () {mouse.element.viewer.zoom(zoomSpin*0.01); zoomSpin = 0;}, 100 );
|
313
|
+
//Clear the box after a second
|
314
|
+
//setTimeout(function() {mouse.element.viewer.clear();}, 1000);
|
315
|
+
}
|
316
|
+
return false; //Prevent default
|
317
|
+
}
|
318
|
+
|
319
|
+
function canvasMousePinch(event, mouse) {
|
320
|
+
if (event.distance != 0) {
|
321
|
+
var factor = event.distance * 0.0001;
|
322
|
+
mouse.element.viewer.zoom(factor);
|
323
|
+
//Clear the box after a second
|
324
|
+
//setTimeout(function() {mouse.element.viewer.clear();}, 1000);
|
325
|
+
}
|
326
|
+
return false; //Prevent default
|
327
|
+
}
|
328
|
+
|
329
|
+
function getImageDataURL(img) {
|
330
|
+
var canvas = document.createElement("canvas");
|
331
|
+
canvas.width = img.width;
|
332
|
+
canvas.height = img.height;
|
333
|
+
var ctx = canvas.getContext("2d");
|
334
|
+
ctx.drawImage(img, 0, 0);
|
335
|
+
var dataURL = canvas.toDataURL("image/png");
|
336
|
+
return dataURL;
|
337
|
+
}
|
338
|
+
|
339
|
+
function loadColourMaps(vis) {
|
340
|
+
//Load colourmaps
|
341
|
+
if (!vis.colourmaps) return;
|
342
|
+
|
343
|
+
var canvas = document.getElementById('palette');
|
344
|
+
for (var i=0; i<vis.colourmaps.length; i++) {
|
345
|
+
var palette = new Palette(vis.colourmaps[i].colours);
|
346
|
+
vis.colourmaps[i].palette = palette;
|
347
|
+
paletteLoad(palette);
|
348
|
+
}
|
349
|
+
|
350
|
+
//if (viewer) viewer.setColourMap(sel);
|
351
|
+
}
|
352
|
+
|
353
|
+
function safeLog10(val) {return val < Number.MIN_VALUE ? Math.log10(Number.MIN_VALUE) : Math.log10(val); }
|
354
|
+
|
355
|
+
function vertexColour(colour, opacity, colourmap, data, idx) {
|
356
|
+
//Default to object colour property
|
357
|
+
if (data.values) {
|
358
|
+
var colrange = data.vertices.data.length / (3*data.values.data.length);
|
359
|
+
idx = Math.floor(idx/colrange);
|
360
|
+
if (colourmap) {
|
361
|
+
var min = 0;
|
362
|
+
var max = 1;
|
363
|
+
if (data.values.minimum != undefined) min = data.values.minimum;
|
364
|
+
if (data.values.maximum != undefined) max = data.values.maximum;
|
365
|
+
//Use a colourmap
|
366
|
+
if (colourmap.range != undefined) {
|
367
|
+
rmin = parseFloat(colourmap.range[0]);
|
368
|
+
rmax = parseFloat(colourmap.range[1]);
|
369
|
+
if (rmin < rmax) {
|
370
|
+
min = rmin;
|
371
|
+
max = rmax;
|
372
|
+
}
|
373
|
+
}
|
374
|
+
//Get nearest pixel on the canvas
|
375
|
+
var pos = MAXIDX*0.5; //If rubbish data, return centre
|
376
|
+
//Allows single value for entire object
|
377
|
+
if (idx >= data.values.data.length) idx = data.values.data.length-1;
|
378
|
+
var val = data.values.data[idx];
|
379
|
+
if (val < min)
|
380
|
+
pos = 0;
|
381
|
+
else if (val > max)
|
382
|
+
pos = MAXIDX;
|
383
|
+
else if (max > min) {
|
384
|
+
var scaled;
|
385
|
+
if (colourmap.logscale) {
|
386
|
+
val = safeLog10(val);
|
387
|
+
min = safeLog10(min);
|
388
|
+
max = safeLog10(max);
|
389
|
+
}
|
390
|
+
//Scale to range [0,1]
|
391
|
+
scaled = (val - min) / (max - min);
|
392
|
+
//Get colour pos [0-2047)
|
393
|
+
pos = Math.round(MAXIDX * scaled);
|
394
|
+
}
|
395
|
+
colour = colourmap.palette.cache[pos];
|
396
|
+
//if (idx % 100) console.log(" : " + val + " min " + min + " max " + max + " pos = " + pos + " colour: " + colour);
|
397
|
+
} else if (data.values.type == 'integer') {
|
398
|
+
//Integer data values, treat as colours
|
399
|
+
colour = data.values.data[idx];
|
400
|
+
}
|
401
|
+
}
|
402
|
+
//RGBA colour values
|
403
|
+
if (data.colours) {
|
404
|
+
var colrange = data.vertices.data.length / (3*data.colours.data.length);
|
405
|
+
idx = Math.floor(idx/colrange);
|
406
|
+
if (data.colours.data.length == 1) idx = 0; //Single colour only provided
|
407
|
+
if (idx >= data.colours.data.length) idx = data.colours.data.length-1;
|
408
|
+
colour = data.colours.data[idx];
|
409
|
+
}
|
410
|
+
|
411
|
+
//Apply opacity per object setting
|
412
|
+
var C = new Colour(colour);
|
413
|
+
if (opacity < 1.0)
|
414
|
+
C.alpha *= opacity;
|
415
|
+
//Return final integer value
|
416
|
+
return C.toInt();
|
417
|
+
}
|
418
|
+
|
419
|
+
//Get eye pos vector z by multiplying vertex by modelview matrix
|
420
|
+
function eyeDistance(M2,P) {
|
421
|
+
return -(M2[0] * P[0] + M2[1] * P[1] + M2[2] * P[2] + M2[3]);
|
422
|
+
}
|
423
|
+
|
424
|
+
function newFilledArray(length, val) {
|
425
|
+
var array = [];
|
426
|
+
for (var i = 0; i < length; i++) {
|
427
|
+
array[i] = val;
|
428
|
+
}
|
429
|
+
return array;
|
430
|
+
}
|
431
|
+
|
432
|
+
function radix(nbyte, source, dest, N)
|
433
|
+
//void radix(char byte, char size, long N, unsigned char *src, unsigned char *dst)
|
434
|
+
{
|
435
|
+
// Radix counting sort of 1 byte, 8 bits = 256 bins
|
436
|
+
var count = newFilledArray(256,0);
|
437
|
+
var index = [];
|
438
|
+
var i;
|
439
|
+
//unsigned char* dst = (unsigned char*)dest;
|
440
|
+
|
441
|
+
//Create histogram, count occurences of each possible byte value 0-255
|
442
|
+
var mask = 0xff;// << (byte*8);
|
443
|
+
for (i=0; i<N; i++) {
|
444
|
+
//if (byte > 0) alert(source[i].key + " : " + (source[i].key >> (byte*8) & mask) + " " + mask);
|
445
|
+
count[source[i].key >> (nbyte*8) & mask]++;
|
446
|
+
}
|
447
|
+
|
448
|
+
//Calculate number of elements less than each value (running total through array)
|
449
|
+
//This becomes the offset index into the sorted array
|
450
|
+
//(eg: there are 5 smaller values so place me in 6th position = 5)
|
451
|
+
index[0]=0;
|
452
|
+
for (i=1; i<256; i++) index[i] = index[i-1] + count[i-1];
|
453
|
+
|
454
|
+
//Finally, re-arrange data by index positions
|
455
|
+
for (i=0; i<N; i++ )
|
456
|
+
{
|
457
|
+
var val = source[i].key >> (nbyte*8) & mask; //Get value
|
458
|
+
//memcpy(&dest[index[val]], &source[i], size);
|
459
|
+
dest[index[val]] = source[i];
|
460
|
+
//memcpy(&dst[index[val]*size], &src[i*size], size);
|
461
|
+
index[val]++; //Increment index to push next element with same value forward one
|
462
|
+
}
|
463
|
+
}
|
464
|
+
|
465
|
+
|
466
|
+
function radix_sort(source, swap, bytes)
|
467
|
+
{
|
468
|
+
//assert(bytes % 2 == 0);
|
469
|
+
//DEBUG && console.log("Radix X sort: %d items %d bytes. Byte: ", N, size);
|
470
|
+
// Sort bytes from least to most significant
|
471
|
+
var N = source.length;
|
472
|
+
for (var x = 0; x < bytes; x += 2)
|
473
|
+
{
|
474
|
+
radix(x, source, swap, N);
|
475
|
+
radix(x+1, swap, source, N);
|
476
|
+
}
|
477
|
+
}
|
478
|
+
|
479
|
+
|
480
|
+
//http://stackoverflow.com/questions/7936923/assist-with-implementing-radix-sort-in-javascript
|
481
|
+
//arr: array to be sorted
|
482
|
+
//begin: 0
|
483
|
+
//end: length of array
|
484
|
+
//bit: maximum number of bits required to represent numbers in arr
|
485
|
+
function msb_radix_sort(arr, begin, end, bit) {
|
486
|
+
var i, j, mask, tmp;
|
487
|
+
i = begin;
|
488
|
+
j = end;
|
489
|
+
mask = 1 << bit;
|
490
|
+
while(i < j) {
|
491
|
+
while(i < j && !(arr[i].key & mask)) ++i;
|
492
|
+
while(i < j && (arr[j - 1].key & mask)) --j;
|
493
|
+
if(i < j) {
|
494
|
+
j--;
|
495
|
+
tmp = arr[i]; //Swap
|
496
|
+
arr[i] = arr[j];
|
497
|
+
arr[j] = tmp;
|
498
|
+
i++;
|
499
|
+
}
|
500
|
+
}
|
501
|
+
if(bit && i > begin)
|
502
|
+
msb_radix_sort(arr, begin, i, bit - 1);
|
503
|
+
if(bit && i < end)
|
504
|
+
msb_radix_sort(arr, i, end, bit - 1);
|
505
|
+
}
|
506
|
+
|
507
|
+
//This object encapsulates a vertex buffer and shader set
|
508
|
+
function Renderer(viewer, type, colour, border) {
|
509
|
+
this.viewer = viewer;
|
510
|
+
this.gl = viewer.gl;
|
511
|
+
this.type = type;
|
512
|
+
this.border = border;
|
513
|
+
if (colour) this.colour = new Colour(colour);
|
514
|
+
|
515
|
+
//Only two options for now, points and triangles
|
516
|
+
if (type == "points") {
|
517
|
+
//Particle renderer
|
518
|
+
//Vertex3, Colour, Size, Type
|
519
|
+
this.attribSizes = [3 * Float32Array.BYTES_PER_ELEMENT,
|
520
|
+
Int32Array.BYTES_PER_ELEMENT,
|
521
|
+
Float32Array.BYTES_PER_ELEMENT,
|
522
|
+
Float32Array.BYTES_PER_ELEMENT];
|
523
|
+
} else if (type == "triangles") {
|
524
|
+
//Triangle renderer
|
525
|
+
//Vertex3, Normal3, Colour, TexCoord2
|
526
|
+
this.attribSizes = [3 * Float32Array.BYTES_PER_ELEMENT,
|
527
|
+
3 * Float32Array.BYTES_PER_ELEMENT,
|
528
|
+
Int32Array.BYTES_PER_ELEMENT,
|
529
|
+
2 * Float32Array.BYTES_PER_ELEMENT];
|
530
|
+
} else if (type == "lines") {
|
531
|
+
//Line renderer
|
532
|
+
//Vertex3, Colour
|
533
|
+
this.attribSizes = [3 * Float32Array.BYTES_PER_ELEMENT,
|
534
|
+
Int32Array.BYTES_PER_ELEMENT];
|
535
|
+
} else if (type == "volume") {
|
536
|
+
//Volume renderer
|
537
|
+
//Vertex3
|
538
|
+
this.attribSizes = [3 * Float32Array.BYTES_PER_ELEMENT];
|
539
|
+
}
|
540
|
+
|
541
|
+
this.elementSize = 0;
|
542
|
+
for (var i=0; i<this.attribSizes.length; i++)
|
543
|
+
this.elementSize += this.attribSizes[i];
|
544
|
+
}
|
545
|
+
|
546
|
+
Renderer.prototype.init = function() {
|
547
|
+
if (this.type == "triangles" && !this.viewer.hasTriangles) return false;
|
548
|
+
if (this.type == "points" && !this.viewer.hasPoints) return false;
|
549
|
+
var fs = this.type + '-fs';
|
550
|
+
var vs = this.type + '-vs';
|
551
|
+
var vis = this.viewer.vis;
|
552
|
+
|
553
|
+
//User defined shaders if provided...
|
554
|
+
if (vis.shaders) {
|
555
|
+
if (vis.shaders[this.type]) {
|
556
|
+
fs = vis.shaders[this.type].fragment || fs;
|
557
|
+
vs = vis.shaders[this.type].vertex || vs;
|
558
|
+
}
|
559
|
+
}
|
560
|
+
|
561
|
+
var fdefines = "#extension GL_OES_standard_derivatives : enable\nprecision highp float;\n#define WEBGL\n";
|
562
|
+
var vdefines = "precision highp float;\n#define WEBGL\n";
|
563
|
+
|
564
|
+
if (this.type == "volume" && this.id && this.image) {
|
565
|
+
//Setup two-triangle rendering
|
566
|
+
this.viewer.webgl.init2dBuffers(this.gl.TEXTURE1); //Use 2nd texture unit
|
567
|
+
|
568
|
+
//Override texture params set in previous call
|
569
|
+
this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_WRAP_S, this.gl.CLAMP_TO_EDGE);
|
570
|
+
this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_WRAP_T, this.gl.CLAMP_TO_EDGE);
|
571
|
+
|
572
|
+
//Load the volume texture image
|
573
|
+
this.viewer.webgl.loadTexture(this.image, this.gl.LINEAR); //this.gl.LUMINANCE, true
|
574
|
+
|
575
|
+
//Calculated scaling
|
576
|
+
this.res = vis.objects[this.id].volume["res"];
|
577
|
+
this.scaling = vis.objects[this.id].volume["scale"];
|
578
|
+
/*/Auto compensate for differences in resolution..
|
579
|
+
if (vis.objects[this.id].volume.autoscale) {
|
580
|
+
//Divide all by the highest res
|
581
|
+
var maxn = Math.max.apply(null, this.res);
|
582
|
+
this.scaling = [this.res[0] / maxn * vis.objects[this.id].volume["scale"][0],
|
583
|
+
this.res[1] / maxn * vis.objects[this.id].volume["scale"][1],
|
584
|
+
this.res[2] / maxn * vis.objects[this.id].volume["scale"][2]];
|
585
|
+
}*/
|
586
|
+
this.tiles = [this.image.width / this.res[0],
|
587
|
+
this.image.height / this.res[1]];
|
588
|
+
this.iscale = [1.0 / this.scaling[0], 1.0 / this.scaling[1], 1.0 / this.scaling[2]]
|
589
|
+
|
590
|
+
var maxSamples = 1024; //interactive ? 1024 : 256;
|
591
|
+
fdefines += "const highp vec2 slices = vec2(" + this.tiles[0] + "," + this.tiles[1] + ");\n";
|
592
|
+
fdefines += "const int maxSamples = " + maxSamples + ";\n";
|
593
|
+
|
594
|
+
if (vis.objects[this.id].tricubicfilter)
|
595
|
+
fdefines += "#define ENABLE_TRICUBIC\n";
|
596
|
+
}
|
597
|
+
|
598
|
+
//vs = vdefines + getSourceFromElement(vs).replace(/^out /gm, 'varying ').replace(/^in /gm, 'attribute ');
|
599
|
+
//fs = fdefines + getSourceFromElement(fs).replace(/^in /gm, 'varying ');
|
600
|
+
vs = vdefines + shaders[vs].replace(/^in /gm, 'attribute ').replace(/^out /gm, 'varying ');
|
601
|
+
fs = fdefines + shaders[fs].replace(/^in /gm, 'varying ');
|
602
|
+
|
603
|
+
try {
|
604
|
+
//Compile the shaders
|
605
|
+
this.program = new WebGLProgram(this.gl, vs, fs);
|
606
|
+
if (this.program.errors) DEBUG && console.log(this.program.errors);
|
607
|
+
//Setup attribs/uniforms (flag set to skip enabling attribs)
|
608
|
+
this.program.setup(undefined, undefined, true);
|
609
|
+
} catch(e) {
|
610
|
+
console.log("FRAGMENT SHADER:\n" + fs);
|
611
|
+
console.log("VERTEX SHADER:\n" + vs);
|
612
|
+
console.error(e);
|
613
|
+
return false;
|
614
|
+
}
|
615
|
+
return true;
|
616
|
+
}
|
617
|
+
|
618
|
+
function SortIdx(idx, key) {
|
619
|
+
this.idx = idx;
|
620
|
+
this.key = key;
|
621
|
+
}
|
622
|
+
|
623
|
+
Renderer.prototype.loadElements = function() {
|
624
|
+
if (this.border) return;
|
625
|
+
DEBUG && console.log("Loading " + this.type + " elements...");
|
626
|
+
var start = new Date();
|
627
|
+
var distances = [];
|
628
|
+
var indices = [];
|
629
|
+
var vis = this.viewer.vis;
|
630
|
+
//Only update the positions array when sorting due to update
|
631
|
+
if (!this.positions || !this.viewer.rotated || this.type == 'lines') {
|
632
|
+
this.positions = [];
|
633
|
+
//Add visible element positions
|
634
|
+
for (var id in vis.objects) {
|
635
|
+
var name = vis.objects[id].name;
|
636
|
+
var skip = !vis.objects[id].visible;
|
637
|
+
|
638
|
+
if (this.type == "points") {
|
639
|
+
if (vis.objects[id].points) {
|
640
|
+
for (var e in vis.objects[id].points) {
|
641
|
+
var pdat = vis.objects[id].points[e];
|
642
|
+
var count = pdat.vertices.data.length;
|
643
|
+
//DEBUG && console.log(name + " " + skip + " : " + count);
|
644
|
+
for (var i=0; i<count; i += 3)
|
645
|
+
this.positions.push(skip ? null : [pdat.vertices.data[i], pdat.vertices.data[i+1], pdat.vertices.data[i+2]]);
|
646
|
+
}
|
647
|
+
}
|
648
|
+
} else if (this.type == "triangles") {
|
649
|
+
if (vis.objects[id].triangles) {
|
650
|
+
for (var e in vis.objects[id].triangles) {
|
651
|
+
var tdat = vis.objects[id].triangles[e];
|
652
|
+
var count = tdat.indices.data.length/3;
|
653
|
+
|
654
|
+
//console.log(name + " " + skip + " : " + count + " - " + tdat.centroids.length);
|
655
|
+
for (var i=0; i<count; i++) {
|
656
|
+
//this.positions.push(skip ? null : tdat.centroids[i]);
|
657
|
+
if (skip || !tdat.centroids)
|
658
|
+
this.positions.push(null);
|
659
|
+
else if (tdat.centroids.length == 1)
|
660
|
+
this.positions.push(tdat.centroids[0]);
|
661
|
+
else
|
662
|
+
this.positions.push(tdat.centroids[i]);
|
663
|
+
}
|
664
|
+
}
|
665
|
+
}
|
666
|
+
} else if (this.type == "lines") {
|
667
|
+
//Write lines directly to indices buffer, no depth sort necessary
|
668
|
+
if (skip) continue;
|
669
|
+
if (vis.objects[id].lines) {
|
670
|
+
for (var e in vis.objects[id].lines) {
|
671
|
+
var ldat = vis.objects[id].lines[e];
|
672
|
+
var count = ldat.indices.data.length;
|
673
|
+
//DEBUG && console.log(name + " " + skip + " : " + count);
|
674
|
+
for (var i=0; i<count; i++)
|
675
|
+
indices.push(ldat.indices.data[i]);
|
676
|
+
}
|
677
|
+
}
|
678
|
+
}
|
679
|
+
}
|
680
|
+
|
681
|
+
var time = (new Date() - start) / 1000.0;
|
682
|
+
DEBUG && console.log(this.type + " : " + time + " seconds to update positions " + this.positions.length);
|
683
|
+
start = new Date();
|
684
|
+
}
|
685
|
+
|
686
|
+
//Depth sorting and create index buffer for objects that require it...
|
687
|
+
if (indices.length == 0) {
|
688
|
+
var distance;
|
689
|
+
//Calculate min/max distances from view plane
|
690
|
+
var minmax = minMaxDist();
|
691
|
+
var mindist = minmax[0];
|
692
|
+
var maxdist = minmax[1];
|
693
|
+
|
694
|
+
//Update eye distances, clamping int distance to integer between 0 and 65535
|
695
|
+
var multiplier = 65534.0 / (maxdist - mindist);
|
696
|
+
var M2 = [this.viewer.webgl.modelView.matrix[2],
|
697
|
+
this.viewer.webgl.modelView.matrix[6],
|
698
|
+
this.viewer.webgl.modelView.matrix[10],
|
699
|
+
this.viewer.webgl.modelView.matrix[14]];
|
700
|
+
|
701
|
+
//Add visible element distances to sorting array
|
702
|
+
for (var i=0; i<this.positions.length; i++) {
|
703
|
+
if (this.positions[i]) {
|
704
|
+
if (this.positions[i].length == 0) {
|
705
|
+
//No position data, draw last
|
706
|
+
distances.push(new SortIdx(i, 65535));
|
707
|
+
} else {
|
708
|
+
//Distance from viewing plane is -eyeZ
|
709
|
+
distance = multiplier * (-(M2[0] * this.positions[i][0] + M2[1] * this.positions[i][1] + M2[2] * this.positions[i][2] + M2[3]) - mindist);
|
710
|
+
//distance = (-(M2[0] * this.positions[i][0] + M2[1] * this.positions[i][1] + M2[2] * this.positions[i][2] + M2[3]) - mindist);
|
711
|
+
// if (i%100==0 && this.positions[i].length == 4) console.log(distance + " - " + this.positions[i][3]);
|
712
|
+
//if (this.positions[i].length == 4) distance -= this.positions[i][3];
|
713
|
+
//distance *= multiplier;
|
714
|
+
//if (distance < 0) distance = 0;
|
715
|
+
if (distance > 65535) distance = 65535;
|
716
|
+
distances.push(new SortIdx(i, 65535 - Math.round(distance)));
|
717
|
+
}
|
718
|
+
}
|
719
|
+
}
|
720
|
+
|
721
|
+
var time = (new Date() - start) / 1000.0;
|
722
|
+
DEBUG && console.log(this.type + " : " + time + " seconds to update distances " + distances.length);
|
723
|
+
|
724
|
+
if (distances.length > 0) {
|
725
|
+
//Sort
|
726
|
+
start = new Date();
|
727
|
+
//distances.sort(function(a,b){return a.key - b.key});
|
728
|
+
//This is about 10 times faster than above:
|
729
|
+
if (this.viewer.view.is3d)
|
730
|
+
msb_radix_sort(distances, 0, distances.length, 16);
|
731
|
+
//Pretty sure msb is still fastest...
|
732
|
+
//if (!this.swap) this.swap = [];
|
733
|
+
//radix_sort(distances, this.swap, 2);
|
734
|
+
time = (new Date() - start) / 1000.0;
|
735
|
+
DEBUG && console.log(time + " seconds to sort");
|
736
|
+
|
737
|
+
start = new Date();
|
738
|
+
//Reload index buffer
|
739
|
+
if (this.type == "points") {
|
740
|
+
//Process points
|
741
|
+
for (var i = 0; i < distances.length; ++i)
|
742
|
+
indices.push(distances[i].idx);
|
743
|
+
//if (distances[i].idx > this.elements) alert("ERROR: " + i + " - " + distances[i].idx + " > " + this.elements);
|
744
|
+
} else if (this.type == "triangles") {
|
745
|
+
//Process triangles
|
746
|
+
for (var i = 0; i < distances.length; ++i) {
|
747
|
+
var i3 = distances[i].idx*3;
|
748
|
+
indices.push(i3);
|
749
|
+
indices.push(i3+1);
|
750
|
+
indices.push(i3+2);
|
751
|
+
//if (i3+2 > this.elements) alert("ERROR: " + i + " - " + (i3+2) + " > " + this.elements);
|
752
|
+
}
|
753
|
+
}
|
754
|
+
time = (new Date() - start) / 1000.0;
|
755
|
+
DEBUG && console.log(time + " seconds to load index buffers");
|
756
|
+
}
|
757
|
+
}
|
758
|
+
|
759
|
+
start = new Date();
|
760
|
+
if (indices.length > 0) {
|
761
|
+
//this.gl.bufferData(this.gl.ELEMENT_ARRAY_BUFFER, new Uint32Array(indices), this.gl.STATIC_DRAW);
|
762
|
+
this.gl.bufferData(this.gl.ELEMENT_ARRAY_BUFFER, new Uint32Array(indices), this.gl.DYNAMIC_DRAW);
|
763
|
+
//this.gl.bufferData(this.gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(indices), this.gl.DYNAMIC_DRAW);
|
764
|
+
|
765
|
+
time = (new Date() - start) / 1000.0;
|
766
|
+
DEBUG && console.log(this.type + " : " + time + " seconds to update index buffer object " + indices.length);
|
767
|
+
}
|
768
|
+
//Update count to visible elements...
|
769
|
+
this.elements = indices.length;
|
770
|
+
}
|
771
|
+
|
772
|
+
var texcoords = [[[0,0], [0,0], [0,0]], [[0,0], [0,255], [255,0]], [[255,255], [0,0], [0,255]]];
|
773
|
+
|
774
|
+
function VertexBuffer(elements, size) {
|
775
|
+
this.size = size;
|
776
|
+
this.vertexSizeInFloats = size / Float32Array.BYTES_PER_ELEMENT;
|
777
|
+
this.array = new ArrayBuffer(elements * size);
|
778
|
+
// Map this buffer to a Float32Array to access the positions/normals/sizes
|
779
|
+
this.floats = new Float32Array(this.array);
|
780
|
+
// Map the same buffer to an Int32Array to access the color
|
781
|
+
this.ints = new Int32Array(this.array);
|
782
|
+
this.bytes = new Uint8Array(this.array);
|
783
|
+
this.offset = 0;
|
784
|
+
DEBUG && console.log(elements + " - " + size);
|
785
|
+
DEBUG && console.log("Created vertex buffer");
|
786
|
+
}
|
787
|
+
|
788
|
+
VertexBuffer.prototype.loadPoints = function(object, viewer) {
|
789
|
+
for (var p in object.points) {
|
790
|
+
var pdat = object.points[p];
|
791
|
+
|
792
|
+
/*console.log("loadPoints " + p);
|
793
|
+
if (pdat.values)
|
794
|
+
console.log(object.name + " : " + pdat.values.minimum + " -> " + pdat.values.maximum);
|
795
|
+
if (object.colourmap >= 0)
|
796
|
+
console.log(object.name + " :: " + vis.colourmaps[object.colourmap].range[0] + " -> " + vis.colourmaps[object.colourmap].range[1]);
|
797
|
+
else
|
798
|
+
console.log(object.colourmap);*/
|
799
|
+
|
800
|
+
var map = viewer.lookupMap(object.colourmap);
|
801
|
+
|
802
|
+
var psize = object.pointsize ? object.pointsize : viewer.vis.properties.pointsize;
|
803
|
+
if (!psize) psize = 1.0;
|
804
|
+
psize = psize * (object.scaling || 1.0);
|
805
|
+
//console.log("POINTSIZE: " + psize);
|
806
|
+
|
807
|
+
for (var i=0; i<pdat.vertices.data.length/3; i++) {
|
808
|
+
var i3 = i*3;
|
809
|
+
var vert = [pdat.vertices.data[i3], pdat.vertices.data[i3+1], pdat.vertices.data[i3+2]];
|
810
|
+
this.floats[this.offset] = vert[0];
|
811
|
+
this.floats[this.offset+1] = vert[1];
|
812
|
+
this.floats[this.offset+2] = vert[2];
|
813
|
+
this.ints[this.offset+3] = vertexColour(object.colour, object.opacity, map, pdat, i)
|
814
|
+
this.floats[this.offset+4] = pdat.sizes ? pdat.sizes.data[i] * psize : psize;
|
815
|
+
this.floats[this.offset+5] = object.pointtype >= 0 ? object.pointtype : -1;
|
816
|
+
this.offset += this.vertexSizeInFloats;
|
817
|
+
}
|
818
|
+
}
|
819
|
+
}
|
820
|
+
|
821
|
+
VertexBuffer.prototype.loadTriangles = function(object, id, viewer) {
|
822
|
+
//Process triangles
|
823
|
+
if (!this.byteOffset) this.byteOffset = 9 * Float32Array.BYTES_PER_ELEMENT;
|
824
|
+
var T = 0;
|
825
|
+
if (object.wireframe) T = 1;
|
826
|
+
for (var t in object.triangles) {
|
827
|
+
var tdat = object.triangles[t];
|
828
|
+
var calcCentroids = false;
|
829
|
+
if (!tdat.centroids) {
|
830
|
+
calcCentroids = true;
|
831
|
+
tdat.centroids = [];
|
832
|
+
}
|
833
|
+
|
834
|
+
//if (tdat.values)
|
835
|
+
// console.log(object.name + " : " + tdat.values.minimum + " -> " + tdat.values.maximum);
|
836
|
+
// console.log("OPACITY: " + object.name + " : " + object.opacity);
|
837
|
+
//if (object.colourmap >= 0)
|
838
|
+
// console.log(object.name + " :: " + viewer.vis.colourmaps[object.colourmap].range[0] + " -> " + viewer.vis.colourmaps[object.colourmap].range[1]);
|
839
|
+
|
840
|
+
var map = viewer.lookupMap(object.colourmap);
|
841
|
+
|
842
|
+
for (var i=0; i<tdat.indices.data.length/3; i++) {
|
843
|
+
if (i%10000 == 0) console.log(i);
|
844
|
+
//Indices holds references to vertices and other data
|
845
|
+
var i3 = i * 3;
|
846
|
+
var ids = [tdat.indices.data[i3], tdat.indices.data[i3+1], tdat.indices.data[i3+2]];
|
847
|
+
var ids3 = [ids[0]*3, ids[1]*3, ids[2]*3];
|
848
|
+
|
849
|
+
for (var j=0; j<3; j++) {
|
850
|
+
this.floats[this.offset] = tdat.vertices.data[ids3[j]];
|
851
|
+
this.floats[this.offset+1] = tdat.vertices.data[ids3[j]+1];
|
852
|
+
this.floats[this.offset+2] = tdat.vertices.data[ids3[j]+2];
|
853
|
+
if (tdat.normals && tdat.normals.data.length == 3) {
|
854
|
+
//Single surface normal
|
855
|
+
this.floats[this.offset+3] = tdat.normals.data[0];
|
856
|
+
this.floats[this.offset+4] = tdat.normals.data[1];
|
857
|
+
this.floats[this.offset+5] = tdat.normals.data[2];
|
858
|
+
} else if (tdat.normals) {
|
859
|
+
this.floats[this.offset+3] = tdat.normals.data[ids3[j]];
|
860
|
+
this.floats[this.offset+4] = tdat.normals.data[ids3[j]+1];
|
861
|
+
this.floats[this.offset+5] = tdat.normals.data[ids3[j]+2];
|
862
|
+
} else {
|
863
|
+
this.floats[this.offset+3] = 0.0;
|
864
|
+
this.floats[this.offset+4] = 0.0;
|
865
|
+
this.floats[this.offset+5] = 0.0;
|
866
|
+
}
|
867
|
+
if (!object.tex)
|
868
|
+
this.ints[this.offset+6] = vertexColour(object.colour, object.opacity, map, tdat, ids[j])
|
869
|
+
else {
|
870
|
+
if (tdat.texcoords) {
|
871
|
+
this.floats[this.offset+7] = tdat.texcoords.data[ids[j]*2];
|
872
|
+
this.floats[this.offset+8] = tdat.texcoords.data[ids[j]*2+1];
|
873
|
+
} else {
|
874
|
+
this.floats[this.offset+7] = texcoords[(i%2+1)*T][j][0];
|
875
|
+
this.floats[this.offset+8] = texcoords[(i%2+1)*T][j][1];
|
876
|
+
}
|
877
|
+
}
|
878
|
+
this.offset += this.vertexSizeInFloats;
|
879
|
+
this.byteOffset += this.size;
|
880
|
+
}
|
881
|
+
|
882
|
+
//Calc centroids (only required if vertices changed)
|
883
|
+
if (calcCentroids) {
|
884
|
+
if (tdat.width) //indices.data.length == 6)
|
885
|
+
//Cross-sections, null centroid - always drawn last
|
886
|
+
tdat.centroids.push([]);
|
887
|
+
else {
|
888
|
+
//(x1+x2+x3, y1+y2+y3, z1+z2+z3)
|
889
|
+
var verts = tdat.vertices.data;
|
890
|
+
/*/Side lengths: A-B, A-C, B-C
|
891
|
+
var AB = vec3.createFrom(verts[ids3[0]] - verts[ids3[1]], verts[ids3[0] + 1] - verts[ids3[1] + 1], verts[ids3[0] + 2] - verts[ids3[1] + 2]);
|
892
|
+
var AC = vec3.createFrom(verts[ids3[0]] - verts[ids3[2]], verts[ids3[0] + 1] - verts[ids3[2] + 1], verts[ids3[0] + 2] - verts[ids3[2] + 2]);
|
893
|
+
var BC = vec3.createFrom(verts[ids3[1]] - verts[ids3[2]], verts[ids3[1] + 1] - verts[ids3[2] + 1], verts[ids3[1] + 2] - verts[ids3[2] + 2]);
|
894
|
+
var lengths = [vec3.length(AB), vec3.length(AC), vec3.length(BC)];
|
895
|
+
//Size weighting shift
|
896
|
+
var adj = (lengths[0] + lengths[1] + lengths[2]) / 9.0;
|
897
|
+
//if (i%100==0) console.log(verts[ids3[0]] + "," + verts[ids3[0] + 1] + "," + verts[ids3[0] + 2] + " " + adj);*/
|
898
|
+
tdat.centroids.push([(verts[ids3[0]] + verts[ids3[1]] + verts[ids3[2]]) / 3,
|
899
|
+
(verts[ids3[0] + 1] + verts[ids3[1] + 1] + verts[ids3[2] + 1]) / 3,
|
900
|
+
(verts[ids3[0] + 2] + verts[ids3[1] + 2] + verts[ids3[2] + 2]) / 3]);
|
901
|
+
}
|
902
|
+
}
|
903
|
+
}
|
904
|
+
}
|
905
|
+
}
|
906
|
+
|
907
|
+
VertexBuffer.prototype.loadLines = function(object) {
|
908
|
+
for (var l in object.lines) {
|
909
|
+
var ldat = object.lines[l];
|
910
|
+
var map = viewer.lookupMap(object.colourmap);
|
911
|
+
for (var i=0; i<ldat.vertices.data.length/3; i++) {
|
912
|
+
var i3 = i*3;
|
913
|
+
var vert = [ldat.vertices.data[i3], ldat.vertices.data[i3+1], ldat.vertices.data[i3+2]];
|
914
|
+
this.floats[this.offset] = vert[0];
|
915
|
+
this.floats[this.offset+1] = vert[1];
|
916
|
+
this.floats[this.offset+2] = vert[2];
|
917
|
+
this.ints[this.offset+3] = vertexColour(object.colour, object.opacity, map, ldat, i)
|
918
|
+
this.offset += this.vertexSizeInFloats;
|
919
|
+
}
|
920
|
+
}
|
921
|
+
}
|
922
|
+
|
923
|
+
VertexBuffer.prototype.update = function(gl) {
|
924
|
+
start = new Date();
|
925
|
+
//gl.bufferData(gl.ARRAY_BUFFER, this.array, gl.STATIC_DRAW);
|
926
|
+
gl.bufferData(gl.ARRAY_BUFFER, this.array, gl.DYNAMIC_DRAW);
|
927
|
+
//gl.bufferData(gl.ARRAY_BUFFER, this.vertices * this.elementSize, gl.DYNAMIC_DRAW);
|
928
|
+
//gl.bufferSubData(gl.ARRAY_BUFFER, 0, buffer);
|
929
|
+
|
930
|
+
time = (new Date() - start) / 1000.0;
|
931
|
+
DEBUG && console.log(time + " seconds to update vertex buffer object");
|
932
|
+
}
|
933
|
+
|
934
|
+
Renderer.prototype.updateBuffers = function() {
|
935
|
+
var vis = viewer.vis;
|
936
|
+
if (this.border) {
|
937
|
+
this.box(viewer.view.min, viewer.view.max);
|
938
|
+
this.elements = 24;
|
939
|
+
return;
|
940
|
+
}
|
941
|
+
|
942
|
+
//Count vertices
|
943
|
+
this.elements = 0;
|
944
|
+
for (var id in vis.objects) {
|
945
|
+
if (this.type == "triangles" && vis.objects[id].triangles) {
|
946
|
+
for (var t in vis.objects[id].triangles)
|
947
|
+
this.elements += vis.objects[id].triangles[t].indices.data.length;
|
948
|
+
} else if (this.type == "points" && vis.objects[id].points) {
|
949
|
+
for (var t in vis.objects[id].points)
|
950
|
+
this.elements += vis.objects[id].points[t].vertices.data.length/3;
|
951
|
+
} else if (this.type == "lines" && vis.objects[id].lines) {
|
952
|
+
for (var t in vis.objects[id].lines)
|
953
|
+
this.elements += vis.objects[id].lines[t].indices.data.length;
|
954
|
+
}
|
955
|
+
}
|
956
|
+
|
957
|
+
if (this.elements == 0) return;
|
958
|
+
DEBUG && console.log("Updating " + this.type + " data... (" + this.elements + " elements)");
|
959
|
+
console.log("Updating " + this.type + " data... (" + this.elements + " elements)");
|
960
|
+
|
961
|
+
//Load vertices and attributes into buffers
|
962
|
+
var start = new Date();
|
963
|
+
|
964
|
+
//Copy data to VBOs
|
965
|
+
var buffer = new VertexBuffer(this.elements, this.elementSize);
|
966
|
+
|
967
|
+
//Reload vertex buffers
|
968
|
+
if (this.type == "points") {
|
969
|
+
//Process points
|
970
|
+
|
971
|
+
/*/Auto 50% subsample when > 1M particles
|
972
|
+
var subsample = 1;
|
973
|
+
if (document.getElementById("subsample").checked == true && newParticles > 1000000) {
|
974
|
+
subsample = Math.round(newParticles/1000000 + 0.5);
|
975
|
+
DEBUG && console.log("Subsampling at " + (100/subsample) + "% (" + subsample + ") to " + Math.floor(newParticles / subsample));
|
976
|
+
}*/
|
977
|
+
//Random subsampling
|
978
|
+
//if (subsample > 1 && Math.random() > 1.0/subsample) continue;
|
979
|
+
|
980
|
+
for (var id in vis.objects)
|
981
|
+
if (vis.objects[id].points)
|
982
|
+
buffer.loadPoints(vis.objects[id], this.viewer);
|
983
|
+
|
984
|
+
} else if (this.type == "lines") {
|
985
|
+
//Process lines
|
986
|
+
for (var id in vis.objects)
|
987
|
+
if (vis.objects[id].lines)
|
988
|
+
buffer.loadLines(vis.objects[id]);
|
989
|
+
|
990
|
+
} else if (this.type == "triangles") {
|
991
|
+
//Process triangles
|
992
|
+
for (var id in vis.objects)
|
993
|
+
if (vis.objects[id].triangles)
|
994
|
+
buffer.loadTriangles(vis.objects[id], id, this.viewer);
|
995
|
+
}
|
996
|
+
|
997
|
+
var time = (new Date() - start) / 1000.0;
|
998
|
+
DEBUG && console.log(time + " seconds to load buffers... (elements: " + this.elements + " bytes: " + buffer.bytes.length + ")");
|
999
|
+
console.log(time + " seconds to load buffers... (elements: " + this.elements + " bytes: " + buffer.bytes.length + ")");
|
1000
|
+
|
1001
|
+
buffer.update(this.gl);
|
1002
|
+
}
|
1003
|
+
|
1004
|
+
Renderer.prototype.box = function(min, max) {
|
1005
|
+
var vertices = new Float32Array(
|
1006
|
+
[
|
1007
|
+
min[0], min[1], max[2],
|
1008
|
+
min[0], max[1], max[2],
|
1009
|
+
max[0], max[1], max[2],
|
1010
|
+
max[0], min[1], max[2],
|
1011
|
+
min[0], min[1], min[2],
|
1012
|
+
min[0], max[1], min[2],
|
1013
|
+
max[0], max[1], min[2],
|
1014
|
+
max[0], min[1], min[2]
|
1015
|
+
]);
|
1016
|
+
|
1017
|
+
var indices = new Uint16Array(
|
1018
|
+
[
|
1019
|
+
0, 1, 1, 2, 2, 3, 3, 0,
|
1020
|
+
4, 5, 5, 6, 6, 7, 7, 4,
|
1021
|
+
0, 4, 3, 7, 1, 5, 2, 6
|
1022
|
+
]
|
1023
|
+
);
|
1024
|
+
this.gl.bufferData(this.gl.ARRAY_BUFFER, vertices, this.gl.STATIC_DRAW);
|
1025
|
+
this.gl.bufferData(this.gl.ELEMENT_ARRAY_BUFFER, indices, this.gl.STATIC_DRAW);
|
1026
|
+
}
|
1027
|
+
|
1028
|
+
Renderer.prototype.getglobal = function(prop) {
|
1029
|
+
//Lookup global prop, then use default if not found
|
1030
|
+
if (this.viewer.vis.properties[prop] != undefined)
|
1031
|
+
return this.viewer.vis.properties[prop];
|
1032
|
+
return this.viewer.dict[prop].default;
|
1033
|
+
}
|
1034
|
+
|
1035
|
+
Renderer.prototype.getprop = function(prop, id) {
|
1036
|
+
//Lookup prop on object (or renderer if not set) first, then global, then use default
|
1037
|
+
//console.log(prop + " getprop " + this.id);
|
1038
|
+
var object = this;
|
1039
|
+
if (id == undefined) id = this.id;
|
1040
|
+
if (id != undefined) object = this.viewer.vis.objects[id];
|
1041
|
+
if (object[prop] != undefined)
|
1042
|
+
return object[prop];
|
1043
|
+
return this.getglobal(prop);
|
1044
|
+
}
|
1045
|
+
|
1046
|
+
Renderer.prototype.draw = function() {
|
1047
|
+
var vis = this.viewer.vis;
|
1048
|
+
if (!vis.objects || !vis.objects.length) return;
|
1049
|
+
var start = new Date();
|
1050
|
+
var desc = "";
|
1051
|
+
//console.log(" ----- " + this.type + " --------------------------------------------------------------------");
|
1052
|
+
|
1053
|
+
//Create buffer if not yet allocated
|
1054
|
+
if (this.vertexBuffer == undefined) {
|
1055
|
+
//Init shaders etc...
|
1056
|
+
//(This is done here now so we can skip triangle shaders if not required,
|
1057
|
+
//due to really slow rendering when doing triangles and points... still to be looked into)
|
1058
|
+
if (!this.init()) return;
|
1059
|
+
DEBUG && console.log("Creating " + this.type + " buffers...");
|
1060
|
+
this.vertexBuffer = this.gl.createBuffer();
|
1061
|
+
this.indexBuffer = this.gl.createBuffer();
|
1062
|
+
//this.viewer.reload = true;
|
1063
|
+
}
|
1064
|
+
|
1065
|
+
if (this.program.attributes["aVertexPosition"] == undefined) return; //Require vertex buffer
|
1066
|
+
|
1067
|
+
this.viewer.webgl.use(this.program);
|
1068
|
+
this.viewer.webgl.setMatrices();
|
1069
|
+
|
1070
|
+
//Bind buffers
|
1071
|
+
this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.vertexBuffer);
|
1072
|
+
this.gl.bindBuffer(this.gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer);
|
1073
|
+
|
1074
|
+
//Reload geometry if required
|
1075
|
+
this.viewer.canvas.mouse.disabled = true;
|
1076
|
+
if (this.reload) this.updateBuffers();
|
1077
|
+
if (this.sort || this.viewer.rotated) this.loadElements();
|
1078
|
+
this.reload = this.sort = false;
|
1079
|
+
this.viewer.canvas.mouse.disabled = false;
|
1080
|
+
|
1081
|
+
if (this.elements == 0 && this.type != "volume") return;
|
1082
|
+
|
1083
|
+
//Enable attributes
|
1084
|
+
for (var key in this.program.attributes)
|
1085
|
+
this.gl.enableVertexAttribArray(this.program.attributes[key]);
|
1086
|
+
|
1087
|
+
//General uniform vars
|
1088
|
+
//TODO: fix this? can't us getprop() as draw is not per object - support per object renderers as in library?
|
1089
|
+
this.gl.uniform1i(this.program.uniforms["uLighting"], this.getglobal('lit'));
|
1090
|
+
this.gl.uniform1f(this.program.uniforms["uOpacity"], this.getglobal('opacity')); //Global opacity
|
1091
|
+
this.gl.uniform1f(this.program.uniforms["uBrightness"], this.getglobal('brightness'));
|
1092
|
+
this.gl.uniform1f(this.program.uniforms["uContrast"], this.getglobal('contrast'));
|
1093
|
+
this.gl.uniform1f(this.program.uniforms["uAmbient"], this.getglobal('ambient'));
|
1094
|
+
this.gl.uniform1f(this.program.uniforms["uSaturation"], this.getglobal('saturation'));
|
1095
|
+
this.gl.uniform1f(this.program.uniforms["uAmbient"], this.getglobal('ambient'));
|
1096
|
+
this.gl.uniform1f(this.program.uniforms["uDiffuse"], this.getglobal('diffuse'));
|
1097
|
+
this.gl.uniform1f(this.program.uniforms["uSpecular"], this.getglobal('specular'));
|
1098
|
+
this.gl.uniform1f(this.program.uniforms["uShininess"], this.getglobal('shininess'));
|
1099
|
+
this.gl.uniform4fv(this.program.uniforms["uLightPos"], new Float32Array(this.getglobal('lightpos')));
|
1100
|
+
this.gl.uniform4fv(this.program.uniforms["uLight"], new Float32Array(this.getglobal('light')));
|
1101
|
+
var colour = this.getglobal('colour');
|
1102
|
+
//if (this.colour)
|
1103
|
+
if (colour)
|
1104
|
+
this.gl.uniform4f(this.program.uniforms["uColour"], colour.red/255.0, colour.green/255.0, colour.blue/255.0, colour.alpha);
|
1105
|
+
|
1106
|
+
var cmin = [vis.properties.xmin || -Infinity,
|
1107
|
+
vis.properties.ymin || -Infinity,
|
1108
|
+
this.viewer.view.is3d ? (vis.properties.zmin || -Infinity) : -Infinity];
|
1109
|
+
var cmax = [vis.properties.xmax || Infinity,
|
1110
|
+
vis.properties.ymax || Infinity,
|
1111
|
+
this.viewer.view.is3d ? (vis.properties.zmax || Infinity) : Infinity];
|
1112
|
+
|
1113
|
+
if (vis.properties.clipmap == undefined || vis.properties.clipmap) {
|
1114
|
+
cmin = [this.viewer.view.min[0] + this.viewer.dims[0] * cmin[0],
|
1115
|
+
this.viewer.view.min[1] + this.viewer.dims[1] * cmin[1],
|
1116
|
+
this.viewer.view.min[2] + this.viewer.dims[2] * cmin[2]];
|
1117
|
+
cmax = [this.viewer.view.min[0] + this.viewer.dims[0] * cmax[0],
|
1118
|
+
this.viewer.view.min[1] + this.viewer.dims[1] * cmax[1],
|
1119
|
+
this.viewer.view.min[2] + this.viewer.dims[2] * cmax[2]];
|
1120
|
+
}
|
1121
|
+
this.gl.uniform3fv(this.program.uniforms["uClipMin"], new Float32Array(cmin));
|
1122
|
+
this.gl.uniform3fv(this.program.uniforms["uClipMax"], new Float32Array(cmax));
|
1123
|
+
|
1124
|
+
if (this.type == "points") {
|
1125
|
+
|
1126
|
+
this.gl.vertexAttribPointer(this.program.attributes["aVertexPosition"], 3, this.gl.FLOAT, false, this.elementSize, 0);
|
1127
|
+
this.gl.vertexAttribPointer(this.program.attributes["aVertexColour"], 4, this.gl.UNSIGNED_BYTE, true, this.elementSize, this.attribSizes[0]);
|
1128
|
+
this.gl.vertexAttribPointer(this.program.attributes["aSize"], 1, this.gl.FLOAT, false, this.elementSize, this.attribSizes[0]+this.attribSizes[1]);
|
1129
|
+
this.gl.vertexAttribPointer(this.program.attributes["aPointType"], 1, this.gl.FLOAT, false, this.elementSize, this.attribSizes[0]+this.attribSizes[1]+this.attribSizes[2]);
|
1130
|
+
|
1131
|
+
//Set uniforms...
|
1132
|
+
this.gl.uniform1i(this.program.uniforms["uPointType"], this.viewer.pointType >= 0 ? this.viewer.pointType : 1);
|
1133
|
+
this.gl.uniform1f(this.program.uniforms["uPointScale"], this.viewer.pointScale*this.viewer.modelsize);
|
1134
|
+
|
1135
|
+
//Draw points
|
1136
|
+
this.gl.drawElements(this.gl.POINTS, this.elements, this.gl.UNSIGNED_INT, 0);
|
1137
|
+
//this.gl.drawElements(this.gl.POINTS, this.positions.length, this.gl.UNSIGNED_SHORT, 0);
|
1138
|
+
//this.gl.drawArrays(this.gl.POINTS, 0, this.positions.length);
|
1139
|
+
desc = this.elements + " points";
|
1140
|
+
|
1141
|
+
} else if (this.type == "triangles") {
|
1142
|
+
|
1143
|
+
this.gl.vertexAttribPointer(this.program.attributes["aVertexPosition"], 3, this.gl.FLOAT, false, this.elementSize, 0);
|
1144
|
+
this.gl.vertexAttribPointer(this.program.attributes["aVertexNormal"], 3, this.gl.FLOAT, false, this.elementSize, this.attribSizes[0]);
|
1145
|
+
this.gl.vertexAttribPointer(this.program.attributes["aVertexColour"], 4, this.gl.UNSIGNED_BYTE, true, this.elementSize, this.attribSizes[0]+this.attribSizes[1]);
|
1146
|
+
this.gl.vertexAttribPointer(this.program.attributes["aVertexTexCoord"], 2, this.gl.FLOAT, true, this.elementSize, this.attribSizes[0]+this.attribSizes[1]+this.attribSizes[2]);
|
1147
|
+
|
1148
|
+
//Set uniforms...
|
1149
|
+
//this.gl.enable(this.gl.CULL_FACE);
|
1150
|
+
//this.gl.cullFace(this.gl.BACK_FACE);
|
1151
|
+
|
1152
|
+
//Texture -- TODO: Switch per object!
|
1153
|
+
//this.gl.bindTexture(this.gl.TEXTURE_2D, this.viewer.webgl.textures[0]);
|
1154
|
+
if (vis.objects[0].tex) {
|
1155
|
+
this.gl.activeTexture(this.gl.TEXTURE0);
|
1156
|
+
this.gl.bindTexture(this.gl.TEXTURE_2D, vis.objects[0].tex);
|
1157
|
+
this.gl.uniform1i(this.program.uniforms["uTexture"], 0);
|
1158
|
+
this.gl.uniform1i(this.program.uniforms["uTextured"], 1); //Disabled unless texture attached!
|
1159
|
+
} else {
|
1160
|
+
this.gl.uniform1i(this.program.uniforms["uTextured"], 0); //Disabled unless texture attached!
|
1161
|
+
}
|
1162
|
+
|
1163
|
+
this.gl.uniform1i(this.program.uniforms["uCalcNormal"], 0);
|
1164
|
+
|
1165
|
+
//Draw triangles
|
1166
|
+
this.gl.drawElements(this.gl.TRIANGLES, this.elements, this.gl.UNSIGNED_INT, 0);
|
1167
|
+
//this.gl.drawElements(this.gl.TRIANGLES, this.positions.length*3, this.gl.UNSIGNED_SHORT, 0);
|
1168
|
+
//this.gl.drawArrays(this.gl.TRIANGLES, 0, this.positions.length*3);
|
1169
|
+
desc = (this.elements / 3) + " triangles";
|
1170
|
+
|
1171
|
+
} else if (this.border) {
|
1172
|
+
this.gl.lineWidth(vis.properties.border >= 0 ? vis.properties.border : 1.0);
|
1173
|
+
|
1174
|
+
this.gl.vertexAttribPointer(this.program.attributes["aVertexPosition"], 3, this.gl.FLOAT, false, 0, 0);
|
1175
|
+
this.gl.vertexAttribPointer(this.program.attributes["aVertexColour"], 4, this.gl.UNSIGNED_BYTE, true, 0, 0);
|
1176
|
+
this.gl.drawElements(this.gl.LINES, this.elements, this.gl.UNSIGNED_SHORT, 0);
|
1177
|
+
desc = "border";
|
1178
|
+
|
1179
|
+
} else if (this.type == "lines") {
|
1180
|
+
|
1181
|
+
//Default line width (TODO: per object settings, using first object only here)
|
1182
|
+
this.gl.lineWidth(vis.objects[0].linewidth || 1.0);
|
1183
|
+
|
1184
|
+
this.gl.vertexAttribPointer(this.program.attributes["aVertexPosition"], 3, this.gl.FLOAT, false, this.elementSize, 0);
|
1185
|
+
this.gl.vertexAttribPointer(this.program.attributes["aVertexColour"], 4, this.gl.UNSIGNED_BYTE, true, this.elementSize, this.attribSizes[0]);
|
1186
|
+
|
1187
|
+
//Draw lines
|
1188
|
+
this.gl.drawElements(this.gl.LINES, this.elements, this.gl.UNSIGNED_INT, 0);
|
1189
|
+
desc = (this.elements / 2) + " lines";
|
1190
|
+
//this.gl.drawArrays(this.gl.LINES, 0, this.positions.length);
|
1191
|
+
|
1192
|
+
} else if (this.type == "volume") {
|
1193
|
+
|
1194
|
+
//Volume render
|
1195
|
+
this.viewer.webgl.initDraw2d(); //Prep for two-triangle render
|
1196
|
+
|
1197
|
+
//Premultiplied alpha blending
|
1198
|
+
this.gl.blendFunc(this.gl.ONE, this.gl.ONE_MINUS_SRC_ALPHA);
|
1199
|
+
|
1200
|
+
if (this.viewer.gradient.mapid != vis.objects[this.id].colourmap) {
|
1201
|
+
//Map selected has changed, so update texture (used on initial render only)
|
1202
|
+
var obj = {};
|
1203
|
+
obj.viewer = this.viewer;
|
1204
|
+
paletteUpdate(obj, vis.objects[this.id].colourmap);
|
1205
|
+
}
|
1206
|
+
|
1207
|
+
//Setup volume camera
|
1208
|
+
this.viewer.webgl.modelView.push();
|
1209
|
+
this.viewer.webgl.projection.push();
|
1210
|
+
{
|
1211
|
+
var props = vis.objects[this.id];
|
1212
|
+
var scaling = vis.objects[this.id].volume["scale"];
|
1213
|
+
/*/Apply translation to origin, any rotation and scaling
|
1214
|
+
this.viewer.webgl.modelView.identity()
|
1215
|
+
this.viewer.webgl.modelView.translate(this.viewer.translate)
|
1216
|
+
|
1217
|
+
// rotate model
|
1218
|
+
var rotmat = quat4.toMat4(this.viewer.rotate);
|
1219
|
+
this.viewer.webgl.modelView.mult(rotmat);*/
|
1220
|
+
|
1221
|
+
//TODO: get object rotation & translation and apply them (pass to apply?)
|
1222
|
+
//Apply the default camera
|
1223
|
+
//this.viewer.webgl.apply(viewer, rot, trans);
|
1224
|
+
|
1225
|
+
//console.log("DIMS: " + (scaling) + " TRANS: " + [-scaling[0]*0.5, -scaling[1]*0.5, -scaling[2]*0.5] + " SCALE: " + [1.0/scaling[0], 1.0/scaling[1], 1.0/scaling[2]]);
|
1226
|
+
//For a volume cube other than [0,0,0] - [1,1,1], need to translate/scale here...
|
1227
|
+
//this.viewer.webgl.modelView.translate([-scaling[0]*0.5, -scaling[1]*0.5, -scaling[2]*0.5]); //Translate to origin
|
1228
|
+
//this.viewer.webgl.modelView.translate([-0.5, -0.5, -0.5]); //Translate to origin
|
1229
|
+
|
1230
|
+
//Apply any corner offset translation here
|
1231
|
+
var minv = vis.objects[this.id].min;
|
1232
|
+
this.viewer.webgl.modelView.translate(minv); //Translate to origin
|
1233
|
+
//this.viewer.webgl.modelView.translate([-minv[0], -minv[1], -minv[2]]); //Translate to origin
|
1234
|
+
|
1235
|
+
//Get the mvMatrix scaled by volume size
|
1236
|
+
//(used for depth calculations)
|
1237
|
+
var matrix = mat4.create(this.viewer.webgl.modelView.matrix);
|
1238
|
+
mat4.scale(matrix, [scaling[0], scaling[1], scaling[2]]);
|
1239
|
+
|
1240
|
+
//Inverse of scaling
|
1241
|
+
this.viewer.webgl.modelView.scale([this.iscale[0], this.iscale[1], this.iscale[2]]);
|
1242
|
+
|
1243
|
+
//Send the normal matrix now
|
1244
|
+
this.viewer.webgl.setNormalMatrix();
|
1245
|
+
|
1246
|
+
//Perspective matrix
|
1247
|
+
//this.viewer.webgl.setPerspective(this.viewer.fov, this.viewer.webgl.viewport.width / this.viewer.webgl.viewport.height, this.viewer.near_clip, this.viewer.far_clip);
|
1248
|
+
//Apply model scaling, inverse squared
|
1249
|
+
this.viewer.webgl.modelView.scale([1.0/(this.viewer.scale[0]*this.viewer.scale[0]), 1.0/(this.viewer.scale[1]*this.viewer.scale[1]), 1.0/(this.viewer.scale[2]*this.viewer.scale[2])]);
|
1250
|
+
|
1251
|
+
//Send default matrices now
|
1252
|
+
//this.viewer.webgl.setMatrices(); //(can't use this as will overwrite our normal matrix)
|
1253
|
+
//Model view matrix
|
1254
|
+
this.gl.uniformMatrix4fv(this.viewer.webgl.program.mvMatrixUniform, false, this.viewer.webgl.modelView.matrix);
|
1255
|
+
//Perspective projection matrix
|
1256
|
+
this.gl.uniformMatrix4fv(this.viewer.webgl.program.pMatrixUniform, false, this.viewer.webgl.projection.matrix);
|
1257
|
+
|
1258
|
+
//Combined ModelView * Projection
|
1259
|
+
var MVPMatrix = mat4.create(this.viewer.webgl.projection.matrix);
|
1260
|
+
mat4.multiply(MVPMatrix, matrix);
|
1261
|
+
|
1262
|
+
//Transpose of model view
|
1263
|
+
var tMVMatrix = mat4.create(this.viewer.webgl.modelView.matrix);
|
1264
|
+
mat4.transpose(tMVMatrix);
|
1265
|
+
|
1266
|
+
//Combined InverseProjection * InverseModelView
|
1267
|
+
var invPMatrix = mat4.create(this.viewer.webgl.projection.matrix);
|
1268
|
+
mat4.inverse(invPMatrix);
|
1269
|
+
var invMVPMatrix = mat4.create(this.viewer.webgl.modelView.matrix);
|
1270
|
+
mat4.transpose(invMVPMatrix);
|
1271
|
+
mat4.multiply(invMVPMatrix, invPMatrix);
|
1272
|
+
}
|
1273
|
+
this.viewer.webgl.modelView.pop();
|
1274
|
+
|
1275
|
+
this.gl.activeTexture(this.gl.TEXTURE0);
|
1276
|
+
this.gl.bindTexture(this.gl.TEXTURE_2D, this.viewer.webgl.textures[0]);
|
1277
|
+
|
1278
|
+
this.gl.activeTexture(this.gl.TEXTURE1);
|
1279
|
+
this.gl.bindTexture(this.gl.TEXTURE_2D, this.viewer.webgl.gradientTexture);
|
1280
|
+
|
1281
|
+
//Only render full quality when not interacting
|
1282
|
+
//this.gl.uniform1i(this.program.uniforms["uSamples"], this.samples);
|
1283
|
+
//TODO: better default handling here - get default values from the properties dict, needs to be exported to a json file
|
1284
|
+
this.gl.uniform1i(this.program.uniforms["uSamples"], props.samples || 256);
|
1285
|
+
this.gl.uniform1i(this.program.uniforms["uVolume"], 0);
|
1286
|
+
this.gl.uniform1i(this.program.uniforms["uTransferFunction"], 1);
|
1287
|
+
this.gl.uniform1i(this.program.uniforms["uEnableColour"], props.usecolourmap || 1);
|
1288
|
+
this.gl.uniform1i(this.program.uniforms["uFilter"], props.tricubicfilter || 0);
|
1289
|
+
this.gl.uniform4fv(this.program.uniforms["uViewport"], this.viewer.webgl.viewport.array);
|
1290
|
+
|
1291
|
+
var bbmin = [props.xmin || 0.0, props.ymin || 0.0, props.zmin || 0.0];
|
1292
|
+
var bbmax = [props.xmax || 1.0, props.ymax || 1.0, props.zmax || 1.0];
|
1293
|
+
this.gl.uniform3fv(this.program.uniforms["uBBMin"], new Float32Array(bbmin));
|
1294
|
+
this.gl.uniform3fv(this.program.uniforms["uBBMax"], new Float32Array(bbmax));
|
1295
|
+
this.gl.uniform3fv(this.program.uniforms["uResolution"], new Float32Array(vis.objects[this.id].volume["res"]));
|
1296
|
+
|
1297
|
+
this.gl.uniform1f(this.program.uniforms["uDensityFactor"], (props.density != undefined ? props.density : 5));
|
1298
|
+
//Per object settings
|
1299
|
+
this.gl.uniform1f(this.program.uniforms["uOpacity"], this.getprop('opacity')); //TODO: fix per object settings
|
1300
|
+
// brightness and contrast
|
1301
|
+
this.gl.uniform1f(this.program.uniforms["uSaturation"], props.saturation || 1.0);
|
1302
|
+
this.gl.uniform1f(this.program.uniforms["uBrightness"], props.brightness || 0.0);
|
1303
|
+
this.gl.uniform1f(this.program.uniforms["uContrast"], props.contrast || 1.0);
|
1304
|
+
this.gl.uniform1f(this.program.uniforms["uPower"], props.power || 1.0);
|
1305
|
+
this.gl.uniform1f(this.program.uniforms["uBloom"], props.bloom || 0.0);
|
1306
|
+
//Density clip range
|
1307
|
+
var dclip = [vis.objects[this.id].minclip || 0.0, vis.objects[this.id].maxclip || 1.0];
|
1308
|
+
this.gl.uniform2fv(this.program.uniforms["uDenMinMax"], new Float32Array(dclip));
|
1309
|
+
|
1310
|
+
//Data value range for isoValue etc
|
1311
|
+
var range = new Float32Array([0.0, 1.0]);
|
1312
|
+
var isovalue = props.isovalue;
|
1313
|
+
var isoalpha = props.isoalpha;
|
1314
|
+
if (isoalpha == undefined) isoalpha = 1.0;
|
1315
|
+
if (props.isovalue != undefined) {
|
1316
|
+
if (vis.objects[this.id]["volume"].minimum != undefined && vis.objects[this.id]["volume"].maximum != undefined) {
|
1317
|
+
var r = [vis.objects[this.id]["volume"].minimum, vis.objects[this.id]["volume"].maximum];
|
1318
|
+
//Normalise isovalue to range [0,1] to match data (non-float textures always used in WebGL)
|
1319
|
+
isovalue = (isovalue - r[0]) / (r[1] - r[0]);
|
1320
|
+
//This is used in shader to normalize data to [0,1] when using float textures
|
1321
|
+
//range[0] = r[0];
|
1322
|
+
//range[1] = r[1];
|
1323
|
+
}
|
1324
|
+
} else {
|
1325
|
+
isoalpha = 0.0;
|
1326
|
+
}
|
1327
|
+
|
1328
|
+
this.gl.uniform2fv(this.program.uniforms["uRange"], range);
|
1329
|
+
this.gl.uniform1f(this.program.uniforms["uIsoValue"], isovalue);
|
1330
|
+
|
1331
|
+
var colour = new Colour(props.colour);
|
1332
|
+
colour.alpha = isoalpha;
|
1333
|
+
this.gl.uniform4fv(this.program.uniforms["uIsoColour"], colour.rgbaGL());
|
1334
|
+
|
1335
|
+
this.gl.uniform1f(this.program.uniforms["uIsoSmooth"], props.isosmooth || 0.1);
|
1336
|
+
this.gl.uniform1i(this.program.uniforms["uIsoWalls"], props.isowalls != undefined ? props.isowalls : 1.0);
|
1337
|
+
|
1338
|
+
//Draw two triangles
|
1339
|
+
this.gl.uniformMatrix4fv(this.program.uniforms["uTMVMatrix"], false, tMVMatrix);
|
1340
|
+
this.gl.uniformMatrix4fv(this.program.uniforms["uInvMVPMatrix"], false, invMVPMatrix);
|
1341
|
+
this.gl.uniformMatrix4fv(this.program.uniforms["uMVPMatrix"], false, MVPMatrix);
|
1342
|
+
this.gl.drawArrays(this.gl.TRIANGLE_STRIP, 0, this.viewer.webgl.vertexPositionBuffer.numItems);
|
1343
|
+
|
1344
|
+
//Restore default blending
|
1345
|
+
this.gl.blendFuncSeparate(this.gl.SRC_ALPHA, this.gl.ONE_MINUS_SRC_ALPHA, this.gl.ONE, this.gl.ONE_MINUS_SRC_ALPHA);
|
1346
|
+
}
|
1347
|
+
|
1348
|
+
//Disable attribs
|
1349
|
+
for (var key in this.program.attributes)
|
1350
|
+
this.gl.disableVertexAttribArray(this.program.attributes[key]);
|
1351
|
+
|
1352
|
+
this.gl.bindBuffer(this.gl.ARRAY_BUFFER, null);
|
1353
|
+
this.gl.bindBuffer(this.gl.ELEMENT_ARRAY_BUFFER, null);
|
1354
|
+
this.gl.useProgram(null);
|
1355
|
+
|
1356
|
+
var time = (new Date() - start) / 1000.0;
|
1357
|
+
if (time > 0.01) DEBUG && console.log(time + " seconds to draw " + desc);
|
1358
|
+
}
|
1359
|
+
|
1360
|
+
function minMaxDist()
|
1361
|
+
{
|
1362
|
+
//Calculate min/max distances from view plane
|
1363
|
+
var M2 = [this.viewer.webgl.modelView.matrix[0*4+2],
|
1364
|
+
this.viewer.webgl.modelView.matrix[1*4+2],
|
1365
|
+
this.viewer.webgl.modelView.matrix[2*4+2],
|
1366
|
+
this.viewer.webgl.modelView.matrix[3*4+2]];
|
1367
|
+
var maxdist = -Number.MAX_VALUE, mindist = Number.MAX_VALUE;
|
1368
|
+
for (i=0; i<2; i++)
|
1369
|
+
{
|
1370
|
+
var x = i==0 ? this.viewer.view.min[0] : this.viewer.view.max[0];
|
1371
|
+
for (var j=0; j<2; j++)
|
1372
|
+
{
|
1373
|
+
var y = j==0 ? this.viewer.view.min[1] : this.viewer.view.max[1];
|
1374
|
+
for (var k=0; k<2; k++)
|
1375
|
+
{
|
1376
|
+
var z = k==0 ? this.viewer.view.min[2] : this.viewer.view.max[2];
|
1377
|
+
var dist = eyeDistance(M2, [x, y, z]);
|
1378
|
+
if (dist < mindist) mindist = dist;
|
1379
|
+
if (dist > maxdist) maxdist = dist;
|
1380
|
+
}
|
1381
|
+
}
|
1382
|
+
}
|
1383
|
+
//alert(mindist + " --> " + maxdist);
|
1384
|
+
if (maxdist == mindist) maxdist += 0.0000001;
|
1385
|
+
return [mindist, maxdist];
|
1386
|
+
}
|
1387
|
+
|
1388
|
+
//This object holds the viewer details and calls the renderers
|
1389
|
+
function Viewer(canvas) {
|
1390
|
+
this.vis = {};
|
1391
|
+
this.canvas = canvas;
|
1392
|
+
if (canvas)
|
1393
|
+
{
|
1394
|
+
try {
|
1395
|
+
this.webgl = new WebGL(this.canvas, {antialias: true, premultipliedAlpha: false});
|
1396
|
+
this.gl = this.webgl.gl;
|
1397
|
+
this.ext = (
|
1398
|
+
this.gl.getExtension('OES_element_index_uint') ||
|
1399
|
+
this.gl.getExtension('MOZ_OES_element_index_uint') ||
|
1400
|
+
this.gl.getExtension('WEBKIT_OES_element_index_uint')
|
1401
|
+
);
|
1402
|
+
this.gl.getExtension('OES_standard_derivatives');
|
1403
|
+
} catch(e) {
|
1404
|
+
//No WebGL
|
1405
|
+
DEBUG && console.log(e);
|
1406
|
+
if (!this.webgl) document.getElementById('canvas').style.display = 'none';
|
1407
|
+
}
|
1408
|
+
}
|
1409
|
+
|
1410
|
+
//Default colour editor
|
1411
|
+
this.gradient = new GradientEditor(document.getElementById('palette'), paletteUpdate);
|
1412
|
+
this.gradient.viewer = this;
|
1413
|
+
|
1414
|
+
this.width = 0; //Auto resize
|
1415
|
+
this.height = 0;
|
1416
|
+
|
1417
|
+
this.rotating = false;
|
1418
|
+
this.translate = [0,0,0];
|
1419
|
+
this.rotate = quat4.create();
|
1420
|
+
quat4.identity(this.rotate);
|
1421
|
+
this.focus = [0,0,0];
|
1422
|
+
this.centre = [0,0,0];
|
1423
|
+
this.fov = 45.0;
|
1424
|
+
this.near_clip = this.far_clip = 0.0;
|
1425
|
+
this.modelsize = 1;
|
1426
|
+
this.scale = [1, 1, 1];
|
1427
|
+
this.orientation = 1.0; //1.0 for RH, -1.0 for LH
|
1428
|
+
this.background = new Colour(0xff404040);
|
1429
|
+
this.canvas.parentElement.style.background = this.background.html();
|
1430
|
+
this.pointScale = 1.0;
|
1431
|
+
this.pointType = 0;
|
1432
|
+
|
1433
|
+
//Non-persistant settings
|
1434
|
+
this.border = 0;
|
1435
|
+
this.mode = 'Rotate';
|
1436
|
+
|
1437
|
+
//Create the renderers
|
1438
|
+
this.renderers = [];
|
1439
|
+
if (this.gl) {
|
1440
|
+
this.points = new Renderer(this, 'points');
|
1441
|
+
this.triangles = new Renderer(this, 'triangles');
|
1442
|
+
this.lines = new Renderer(this, 'lines');
|
1443
|
+
this.border = new Renderer(this, 'lines', 0xffffffff, true);
|
1444
|
+
|
1445
|
+
//Defines draw order
|
1446
|
+
this.renderers.push(this.triangles);
|
1447
|
+
this.renderers.push(this.points);
|
1448
|
+
this.renderers.push(this.lines);
|
1449
|
+
this.renderers.push(this.border);
|
1450
|
+
|
1451
|
+
this.gl.enable(this.gl.DEPTH_TEST);
|
1452
|
+
this.gl.depthFunc(this.gl.LEQUAL);
|
1453
|
+
//this.gl.depthMask(this.gl.FALSE);
|
1454
|
+
this.gl.enable(this.gl.BLEND);
|
1455
|
+
//this.gl.blendFunc(this.gl.SRC_ALPHA, this.gl.ONE_MINUS_SRC_ALPHA);
|
1456
|
+
//this.gl.blendFuncSeparate(this.gl.SRC_ALPHA, this.gl.ONE_MINUS_SRC_ALPHA, this.gl.ZERO, this.gl.ONE);
|
1457
|
+
this.gl.blendFuncSeparate(this.gl.SRC_ALPHA, this.gl.ONE_MINUS_SRC_ALPHA, this.gl.ONE, this.gl.ONE_MINUS_SRC_ALPHA);
|
1458
|
+
}
|
1459
|
+
}
|
1460
|
+
|
1461
|
+
Viewer.prototype.loadFile = function(source) {
|
1462
|
+
//Skip update to rotate/translate etc if in process of updating
|
1463
|
+
//console.log(source);
|
1464
|
+
if (document.mouse && document.mouse.isdown) return;
|
1465
|
+
var start = new Date();
|
1466
|
+
var updated = true;
|
1467
|
+
try {
|
1468
|
+
if (typeof source != 'object') {
|
1469
|
+
if (source.indexOf("{") < 0) {
|
1470
|
+
if (server) {
|
1471
|
+
//Not a json string, assume a command script
|
1472
|
+
var lines = source.split("\n");
|
1473
|
+
for (var line in lines) {
|
1474
|
+
sendCommand('' + lines[line]);
|
1475
|
+
}
|
1476
|
+
return;
|
1477
|
+
} else {
|
1478
|
+
console.log(source);
|
1479
|
+
alert("Invalid data");
|
1480
|
+
return;
|
1481
|
+
}
|
1482
|
+
} else if (source.substr(8) == "loadData") {
|
1483
|
+
//jsonp, strip function call when loaded from FileReader
|
1484
|
+
source = source.substring(10, source.lastIndexOf("\n"));
|
1485
|
+
}
|
1486
|
+
source = JSON.parse(source);
|
1487
|
+
}
|
1488
|
+
} catch(e) {
|
1489
|
+
console.log(source);
|
1490
|
+
alert(e);
|
1491
|
+
return;
|
1492
|
+
}
|
1493
|
+
var time = (new Date() - start) / 1000.0;
|
1494
|
+
DEBUG && console.log(time + " seconds to parse data");
|
1495
|
+
|
1496
|
+
if (source.exported) {
|
1497
|
+
if (!this.vis.views && !this.view) {DEBUG && console.log("Exported settings require loaded model"); return;}
|
1498
|
+
var old = this.toString();
|
1499
|
+
//Copy, overwriting if exists in source
|
1500
|
+
if (source.views[0].rotate) this.vis.views[0].rotate = source.views[0].rotate;
|
1501
|
+
//Merge keys - preserves original objects for gui access
|
1502
|
+
Merge(this.vis, source);
|
1503
|
+
if (!source.reload) updated = false; //No reload necessary
|
1504
|
+
} else {
|
1505
|
+
//Clear geometry
|
1506
|
+
for (var r in this.renderers)
|
1507
|
+
this.renderers[r].elements = 0;
|
1508
|
+
|
1509
|
+
//Replace
|
1510
|
+
//Merge keys - preserves original objects for gui access
|
1511
|
+
Merge(this.vis, source);
|
1512
|
+
//this.vis = source;
|
1513
|
+
|
1514
|
+
if (!this.vis.colourmaps) this.vis.colourmaps = [];
|
1515
|
+
}
|
1516
|
+
|
1517
|
+
var vis = this.vis;
|
1518
|
+
if (!vis.properties) vis.properties = {}; //Ensure valid object even if no properties
|
1519
|
+
|
1520
|
+
//Set active view (always first for now)
|
1521
|
+
this.view = vis.views[0];
|
1522
|
+
|
1523
|
+
//Always set a bounding box, get from objects if not in view
|
1524
|
+
var objbb = false;
|
1525
|
+
if (!this.view.min || !this.view.max) {
|
1526
|
+
this.view.min = [Number.MAX_VALUE, Number.MAX_VALUE, Number.MAX_VALUE];
|
1527
|
+
this.view.max = [-Number.MAX_VALUE, -Number.MAX_VALUE, -Number.MAX_VALUE];
|
1528
|
+
objbb = true;
|
1529
|
+
}
|
1530
|
+
//console.log(JSON.stringify(this.view));
|
1531
|
+
|
1532
|
+
//Load some user options...
|
1533
|
+
loadColourMaps(this.vis);
|
1534
|
+
|
1535
|
+
if (this.view) {
|
1536
|
+
this.fov = this.view.fov || 45;
|
1537
|
+
this.near_clip = this.view.near || 0;
|
1538
|
+
this.far_clip = this.view.far || 0;
|
1539
|
+
this.orientation = this.view.orientation || 1;
|
1540
|
+
//this.axes = this.view.axis == undefined ? true : this.view.axis;
|
1541
|
+
}
|
1542
|
+
|
1543
|
+
this.pointScale = vis.properties.scalepoints || 1.0;
|
1544
|
+
this.pointType = vis.properties.pointtype; // > 0 ? vis.properties.pointtype : 0;
|
1545
|
+
|
1546
|
+
this.applyBackground(vis.properties.background);
|
1547
|
+
|
1548
|
+
if (this.canvas && vis.properties.resolution && vis.properties.resolution[0] && vis.properties.resolution[1]) {
|
1549
|
+
this.width = vis.properties.resolution[0];
|
1550
|
+
this.height = vis.properties.resolution[1];
|
1551
|
+
this.canvas.style.width = "";
|
1552
|
+
this.canvas.style.height = "";
|
1553
|
+
}
|
1554
|
+
|
1555
|
+
//Decode into Float buffers and replace original base64 data
|
1556
|
+
//-Colour lookups: do in shader with a texture?
|
1557
|
+
//-Sizes don't change at per-particle level here, per-object? send id and calc size in shader? (short:id + short:size = 4 bytes)
|
1558
|
+
|
1559
|
+
//min = [Number.MAX_VALUE, Number.MAX_VALUE, Number.MAX_VALUE];
|
1560
|
+
//max = [-Number.MAX_VALUE, -Number.MAX_VALUE, -Number.MAX_VALUE];
|
1561
|
+
|
1562
|
+
//Process object data and convert base64 to Float32Array
|
1563
|
+
if (!source.exported) this.vertexCount = 0;
|
1564
|
+
for (var id in vis.objects) {
|
1565
|
+
//Opacity bug fix hack
|
1566
|
+
if (!vis.objects[id]['opacity']) vis.objects[id]['opacity'] = 1.0;
|
1567
|
+
var name = vis.objects[id].name;
|
1568
|
+
//Process points/triangles
|
1569
|
+
if (!source.exported) {
|
1570
|
+
//Set default colour if not yet set
|
1571
|
+
if (!vis.objects[id].colour) {
|
1572
|
+
if ("volume" in vis.objects[id]) //Use a different default for volume (isosurface) colour
|
1573
|
+
vis.objects[id].colour = [220, 220, 200, 255];
|
1574
|
+
else
|
1575
|
+
vis.objects[id].colour = this.dict["colour"].default;
|
1576
|
+
}
|
1577
|
+
|
1578
|
+
for (var type in vis.objects[id]) {
|
1579
|
+
if (["triangles", "points", "lines", "volume"].indexOf(type) < 0) continue;
|
1580
|
+
if (type == "triangles") this.hasTriangles = true;
|
1581
|
+
if (type == "points") this.hasPoints = true;
|
1582
|
+
if (type == "lines") this.hasLines = true;
|
1583
|
+
|
1584
|
+
if (type == "volume" && vis.objects[id][type].url) {
|
1585
|
+
//Single volume per object only, each volume needs own renderer
|
1586
|
+
this.hasVolumes = true;
|
1587
|
+
var vren = new Renderer(this, 'volume');
|
1588
|
+
vren.id = id;
|
1589
|
+
this.renderers.push(vren);
|
1590
|
+
vren.image = new Image();
|
1591
|
+
vren.image.src = vis.objects[id][type].url;
|
1592
|
+
var viewer = this;
|
1593
|
+
vren.image.onload = function(){ viewer.drawFrame(); viewer.draw(); };
|
1594
|
+
}
|
1595
|
+
|
1596
|
+
//Apply object bounding box
|
1597
|
+
if (objbb && vis.objects[id].min)
|
1598
|
+
this.checkPointMinMax(vis.objects[id].min);
|
1599
|
+
if (objbb && vis.objects[id].max)
|
1600
|
+
this.checkPointMinMax(vis.objects[id].max);
|
1601
|
+
|
1602
|
+
//Read vertices, values, normals, sizes, etc...
|
1603
|
+
for (var idx in vis.objects[id][type]) {
|
1604
|
+
//Only support following data types for now
|
1605
|
+
decodeBase64(vis.objects[id][type][idx], 'vertices');
|
1606
|
+
decodeBase64(vis.objects[id][type][idx], 'values');
|
1607
|
+
decodeBase64(vis.objects[id][type][idx], 'normals');
|
1608
|
+
decodeBase64(vis.objects[id][type][idx], 'texcoords');
|
1609
|
+
decodeBase64(vis.objects[id][type][idx], 'colours', 'integer');
|
1610
|
+
decodeBase64(vis.objects[id][type][idx], 'sizes');
|
1611
|
+
decodeBase64(vis.objects[id][type][idx], 'indices', 'integer');
|
1612
|
+
|
1613
|
+
if (vis.objects[id][type][idx].vertices) {
|
1614
|
+
DEBUG && console.log("Loaded " + vis.objects[id][type][idx].vertices.data.length/3 + " vertices from " + name);
|
1615
|
+
this.vertexCount += vis.objects[id][type][idx].vertices.data.length/3;
|
1616
|
+
}
|
1617
|
+
|
1618
|
+
//Create indices for cross-sections & lines
|
1619
|
+
if (type == 'lines' && !vis.objects[id][type][idx].indices) {
|
1620
|
+
//Collect indices
|
1621
|
+
var verts = vis.objects[id][type][idx].vertices.data;
|
1622
|
+
var buf = new Uint32Array(verts.length);
|
1623
|
+
for (var j=0; j < verts.length; j++) //Iterate over h-1 strips
|
1624
|
+
buf[j] = j;
|
1625
|
+
vis.objects[id][type][idx].indices = {"data" : buf, "type" : "integer"};
|
1626
|
+
}
|
1627
|
+
if (type == 'triangles' && !vis.objects[id][type][idx].indices) {
|
1628
|
+
//Collect indices
|
1629
|
+
var h = vis.objects[id][type][idx].height;
|
1630
|
+
var w = vis.objects[id][type][idx].width;
|
1631
|
+
DEBUG && console.log("GRID w : " + w + " , h : " + h);
|
1632
|
+
var buf = new Uint32Array((w-1)*(h-1)*6);
|
1633
|
+
var i = 0;
|
1634
|
+
for (var j=0; j < h-1; j++) //Iterate over h-1 strips
|
1635
|
+
{
|
1636
|
+
var offset0 = j * w;
|
1637
|
+
var offset1 = (j+1) * w;
|
1638
|
+
for (var k=0; k < w-1; k++) //Iterate width of strips, w vertices
|
1639
|
+
{
|
1640
|
+
//Tri 1
|
1641
|
+
buf[i++] = offset0 + k;
|
1642
|
+
buf[i++] = offset1 + k;
|
1643
|
+
buf[i++] = offset0 + k + 1;
|
1644
|
+
//Tri 2
|
1645
|
+
buf[i++] = offset1 + k;
|
1646
|
+
buf[i++] = offset0 + k + 1;
|
1647
|
+
buf[i++] = offset1 + k + 1;
|
1648
|
+
}
|
1649
|
+
}
|
1650
|
+
|
1651
|
+
vis.objects[id][type][idx].indices = {"data" : buf, "type" : "integer"};
|
1652
|
+
/*/Get center point for depth sort...
|
1653
|
+
var verts = vis.objects[id][type][idx].vertices.data;
|
1654
|
+
vis.objects[id][type][idx].centroids = [null];
|
1655
|
+
vis.objects[id][type][idx].centroids =
|
1656
|
+
[[
|
1657
|
+
(verts[0]+verts[verts.length-3])/2,
|
1658
|
+
(verts[1]+verts[verts.length-2])/2,
|
1659
|
+
(verts[2]+verts[verts.length-1])/2
|
1660
|
+
]];*/
|
1661
|
+
}
|
1662
|
+
}
|
1663
|
+
}
|
1664
|
+
}
|
1665
|
+
|
1666
|
+
this.updateDims(this.view);
|
1667
|
+
}
|
1668
|
+
var time = (new Date() - start) / 1000.0;
|
1669
|
+
DEBUG && console.log(time + " seconds to import data");
|
1670
|
+
|
1671
|
+
//Load texture images
|
1672
|
+
this.loadTextures();
|
1673
|
+
|
1674
|
+
//Defaults for interaction flags
|
1675
|
+
//(Default to interactive render if vertex count < 0.5 M)
|
1676
|
+
if (vis.interactive == undefined) vis.interactive = (this.vertexCount <= 500000);
|
1677
|
+
if (vis.immediatesort == undefined) vis.immediatesort = false;
|
1678
|
+
if (vis.sortenabled == undefined) vis.sortenabled = true;
|
1679
|
+
|
1680
|
+
//TODO: loaded custom shader is not replaced by default when new model loaded...
|
1681
|
+
for (var r in this.renderers) {
|
1682
|
+
var ren = this.renderers[r];
|
1683
|
+
//Custom shader load
|
1684
|
+
if (vis.shaders && vis.shaders[ren.type])
|
1685
|
+
ren.init();
|
1686
|
+
//Set update flags
|
1687
|
+
ren.sort = ren.reload = updated;
|
1688
|
+
}
|
1689
|
+
|
1690
|
+
//Defer drawing until textures loaded if necessary
|
1691
|
+
if (!this.hasVolumes && !this.hasTexture) {
|
1692
|
+
this.drawFrame();
|
1693
|
+
this.draw();
|
1694
|
+
}
|
1695
|
+
|
1696
|
+
//Create UI - disable by omitting dat.gui.min.js
|
1697
|
+
var viewer = this;
|
1698
|
+
var changefn = function(value) {
|
1699
|
+
viewer.reload();
|
1700
|
+
viewer.draw();
|
1701
|
+
};
|
1702
|
+
|
1703
|
+
//Create GUI menu if enabled
|
1704
|
+
if (this.menu)
|
1705
|
+
createMenu(this, changefn, 1);
|
1706
|
+
}
|
1707
|
+
|
1708
|
+
Viewer.prototype.loadTextures = function() {
|
1709
|
+
//Load/reload textures for all objects
|
1710
|
+
var vis = this.vis;
|
1711
|
+
for (var id in vis.objects) {
|
1712
|
+
//Texure loading
|
1713
|
+
if (vis.objects[id].texture) {
|
1714
|
+
this.hasTexture = true;
|
1715
|
+
vis.objects[id].image = new Image();
|
1716
|
+
vis.objects[id].image.crossOrigin = "anonymous";
|
1717
|
+
vis.objects[id].image.src = vis.objects[id].texture + '?_' + new Date().getTime(); //Prevent caching
|
1718
|
+
var viewer = this;
|
1719
|
+
var i = id; //Variable for closure
|
1720
|
+
vis.objects[id].image.onload = function() {
|
1721
|
+
// Flip the image's Y axis to match the WebGL texture coordinate space.
|
1722
|
+
viewer.webgl.gl.activeTexture(viewer.webgl.gl.TEXTURE0);
|
1723
|
+
//Flip jpegs
|
1724
|
+
var flip = vis.objects[i].image.src.slice(0,1024).toLowerCase().indexOf('png') < 0;
|
1725
|
+
vis.objects[i].tex = viewer.webgl.loadTexture(vis.objects[i].image, viewer.gl.LINEAR, viewer.gl.RGBA, flip);
|
1726
|
+
viewer.drawFrame();
|
1727
|
+
viewer.draw();
|
1728
|
+
};
|
1729
|
+
}
|
1730
|
+
}
|
1731
|
+
}
|
1732
|
+
|
1733
|
+
Viewer.prototype.reload = function() {
|
1734
|
+
for (var r in this.renderers) {
|
1735
|
+
//Set update flags
|
1736
|
+
var ren = this.renderers[r];
|
1737
|
+
ren.sort = ren.reload = true;
|
1738
|
+
}
|
1739
|
+
|
1740
|
+
this.applyBackground(this.vis.properties.background);
|
1741
|
+
}
|
1742
|
+
|
1743
|
+
Viewer.prototype.checkPointMinMax = function(coord) {
|
1744
|
+
for (var i=0; i<3; i++) {
|
1745
|
+
this.view.min[i] = Math.min(coord[i], this.view.min[i]);
|
1746
|
+
this.view.max[i] = Math.max(coord[i], this.view.max[i]);
|
1747
|
+
}
|
1748
|
+
//console.log(JSON.stringify(this.view.min) + " -- " + JSON.stringify(this.view.max));
|
1749
|
+
}
|
1750
|
+
|
1751
|
+
function Merge(obj1, obj2) {
|
1752
|
+
for (var p in obj2) {
|
1753
|
+
try {
|
1754
|
+
//alert(p + " ==> " + obj2[p].constructor);
|
1755
|
+
// Property in destination object set; update its value.
|
1756
|
+
if (obj2[p].constructor == Object || obj2[p].constructor == Array) {
|
1757
|
+
obj1[p] = Merge(obj1[p], obj2[p]);
|
1758
|
+
} else {
|
1759
|
+
obj1[p] = obj2[p];
|
1760
|
+
}
|
1761
|
+
} catch(e) {
|
1762
|
+
// Property in destination object not set; create it and set its value.
|
1763
|
+
obj1[p] = obj2[p];
|
1764
|
+
}
|
1765
|
+
}
|
1766
|
+
return obj1;
|
1767
|
+
}
|
1768
|
+
|
1769
|
+
Viewer.prototype.toString = function(nocam, reload) {
|
1770
|
+
var exp = {"objects" : this.exportObjects(),
|
1771
|
+
"colourmaps" : this.exportColourMaps(),
|
1772
|
+
"views" : this.exportView(nocam),
|
1773
|
+
"properties" : this.vis.properties};
|
1774
|
+
|
1775
|
+
exp.exported = true;
|
1776
|
+
exp.reload = reload ? true : false;
|
1777
|
+
|
1778
|
+
if (nocam) return JSON.stringify(exp);
|
1779
|
+
//Export with 2 space indentation
|
1780
|
+
return JSON.stringify(exp, undefined, 2);
|
1781
|
+
}
|
1782
|
+
|
1783
|
+
Viewer.prototype.exportView = function(nocam) {
|
1784
|
+
//Update camera settings of current view
|
1785
|
+
if (nocam)
|
1786
|
+
this.view = {};
|
1787
|
+
else {
|
1788
|
+
this.view.rotate = this.getRotation();
|
1789
|
+
this.view.focus = this.focus;
|
1790
|
+
this.view.translate = this.translate;
|
1791
|
+
this.view.scale = this.scale;
|
1792
|
+
}
|
1793
|
+
this.view.fov = this.fov;
|
1794
|
+
this.view.near = this.near_clip;
|
1795
|
+
this.view.far = this.far_clip;
|
1796
|
+
//this.properties.border = this.border ? 1 : 0;
|
1797
|
+
//this.properties.axis = this.axes;
|
1798
|
+
//this.view.background = this.background.toString();
|
1799
|
+
|
1800
|
+
return [this.view];
|
1801
|
+
}
|
1802
|
+
|
1803
|
+
Viewer.prototype.exportObjects = function() {
|
1804
|
+
objs = [];
|
1805
|
+
for (var id in this.vis.objects) {
|
1806
|
+
objs[id] = {};
|
1807
|
+
//Skip geometry data
|
1808
|
+
for (var type in this.vis.objects[id]) {
|
1809
|
+
if (type != "triangles" && type != "points" && type != 'lines' && type != "volume") {
|
1810
|
+
objs[id][type] = this.vis.objects[id][type];
|
1811
|
+
}
|
1812
|
+
}
|
1813
|
+
}
|
1814
|
+
return objs;
|
1815
|
+
}
|
1816
|
+
|
1817
|
+
Viewer.prototype.exportColourMaps = function() {
|
1818
|
+
cmaps = [];
|
1819
|
+
if (this.vis.colourmaps) {
|
1820
|
+
for (var i=0; i<this.vis.colourmaps.length; i++) {
|
1821
|
+
cmaps[i] = this.vis.colourmaps[i].palette.get();
|
1822
|
+
//Copy additional properties
|
1823
|
+
for (var type in this.vis.colourmaps[i]) {
|
1824
|
+
if (type != "palette" && type != "colours")
|
1825
|
+
cmaps[i][type] = this.vis.colourmaps[i][type];
|
1826
|
+
}
|
1827
|
+
}
|
1828
|
+
}
|
1829
|
+
return cmaps;
|
1830
|
+
}
|
1831
|
+
|
1832
|
+
Viewer.prototype.exportFile = function() {
|
1833
|
+
window.open('data:text/plain;base64,' + window.btoa(this.toString(false, true)));
|
1834
|
+
}
|
1835
|
+
|
1836
|
+
Viewer.prototype.applyBackground = function(bg) {
|
1837
|
+
if (!bg) return;
|
1838
|
+
this.background = new Colour(bg);
|
1839
|
+
var hsv = this.background.HSV();
|
1840
|
+
if (this.border) this.border.colour = hsv.V > 50 ? new Colour(0xff444444) : new Colour(0xffbbbbbb);
|
1841
|
+
this.canvas.parentElement.style.background = this.background.html();
|
1842
|
+
}
|
1843
|
+
|
1844
|
+
Viewer.prototype.addColourMap = function() {
|
1845
|
+
var properties = this.vis.properties;
|
1846
|
+
if (properties.id == undefined) return;
|
1847
|
+
var name = prompt("Enter colourmap name:");
|
1848
|
+
//New colourmap on active object
|
1849
|
+
if (server)
|
1850
|
+
sendCommand("colourmap " + properties.id + " add");
|
1851
|
+
|
1852
|
+
var newmap = {
|
1853
|
+
"name": name,
|
1854
|
+
"range": [0, 1], "logscale": 0,
|
1855
|
+
"colours": [{"position": 0, "colour": "rgba(0,0,0,0)"},{"position": 1,"colour": "rgba(255,255,255,1)"}
|
1856
|
+
]
|
1857
|
+
}
|
1858
|
+
this.vis.colourmaps.push(newmap);
|
1859
|
+
|
1860
|
+
loadColourMaps(this.vis);
|
1861
|
+
}
|
1862
|
+
|
1863
|
+
Viewer.prototype.setColourMap = function(id) {
|
1864
|
+
var properties = this.vis.properties;
|
1865
|
+
if (properties.id == undefined) return;
|
1866
|
+
this.vis.objects[properties.id].colourmap = parseInt(id);
|
1867
|
+
if (id === undefined) id = -1;
|
1868
|
+
//Set new colourmap on active object
|
1869
|
+
if (id < 0) {
|
1870
|
+
document.getElementById('palette').style.display = 'none';
|
1871
|
+
document.getElementById('log').style.display = 'none';
|
1872
|
+
} else {
|
1873
|
+
//Draw palette UI
|
1874
|
+
document.getElementById('logscale').checked = this.vis.colourmaps[id].logscale;
|
1875
|
+
document.getElementById('log').style.display = 'block';
|
1876
|
+
document.getElementById('palette').style.display = 'block';
|
1877
|
+
this.gradient.palette = this.vis.colourmaps[id].palette;
|
1878
|
+
this.gradient.mapid = id; //Save the id
|
1879
|
+
this.gradient.update();
|
1880
|
+
}
|
1881
|
+
}
|
1882
|
+
|
1883
|
+
function decodeBase64(obj, datatype, format) {
|
1884
|
+
if (!format) format = 'float';
|
1885
|
+
if (!obj[datatype]) return null;
|
1886
|
+
var buf;
|
1887
|
+
if (typeof obj[datatype].data == 'string') {
|
1888
|
+
//Base64 encoded string
|
1889
|
+
var decoded = atob(obj[datatype].data);
|
1890
|
+
var buffer = new ArrayBuffer(decoded.length);
|
1891
|
+
var bufView = new Uint8Array(buffer);
|
1892
|
+
for (var i=0, strLen=decoded.length; i<strLen; i++) {
|
1893
|
+
bufView[i] = decoded.charCodeAt(i);
|
1894
|
+
}
|
1895
|
+
if (format == 'float')
|
1896
|
+
buf = new Float32Array(buffer);
|
1897
|
+
else
|
1898
|
+
buf = new Uint32Array(buffer);
|
1899
|
+
} else {
|
1900
|
+
//Literal array
|
1901
|
+
if (format == 'float')
|
1902
|
+
buf = new Float32Array(obj[datatype].data);
|
1903
|
+
else
|
1904
|
+
buf = new Uint32Array(obj[datatype].data);
|
1905
|
+
}
|
1906
|
+
|
1907
|
+
obj[datatype].data = buf;
|
1908
|
+
obj[datatype].type = format;
|
1909
|
+
|
1910
|
+
if (datatype == 'values') {
|
1911
|
+
if (!obj.values.minimum ||
|
1912
|
+
!obj.values.maximum) {
|
1913
|
+
//Value field max min
|
1914
|
+
var minval = Number.MAX_VALUE, maxval = -Number.MAX_VALUE;
|
1915
|
+
for (var i=0; i<buf.length; i++) {
|
1916
|
+
if (buf[i] > maxval)
|
1917
|
+
maxval = buf[i];
|
1918
|
+
if (buf[i] < minval)
|
1919
|
+
minval = buf[i];
|
1920
|
+
}
|
1921
|
+
|
1922
|
+
//Set from data where none provided...
|
1923
|
+
if (!obj.values.minimum)
|
1924
|
+
obj.values.minimum = minval;
|
1925
|
+
if (!obj.values.maximum)
|
1926
|
+
obj.values.maximum = maxval;
|
1927
|
+
}
|
1928
|
+
}
|
1929
|
+
}
|
1930
|
+
|
1931
|
+
function removeChildren(element) {
|
1932
|
+
if (element.hasChildNodes()) {
|
1933
|
+
while (element.childNodes.length > 0 )
|
1934
|
+
element.removeChild(element.firstChild);
|
1935
|
+
}
|
1936
|
+
}
|
1937
|
+
|
1938
|
+
Viewer.prototype.lookupMapId = function(id) {
|
1939
|
+
if (typeof(id) == 'string') {
|
1940
|
+
for (var i = 0; i < this.vis.colourmaps.length; i++) {
|
1941
|
+
if (this.vis.colourmaps[i].name == id) {
|
1942
|
+
return i;
|
1943
|
+
break;
|
1944
|
+
}
|
1945
|
+
}
|
1946
|
+
}
|
1947
|
+
return id;
|
1948
|
+
}
|
1949
|
+
|
1950
|
+
Viewer.prototype.lookupMap = function(id) {
|
1951
|
+
var mapid = this.lookupMapId(id);
|
1952
|
+
if (mapid >= 0)
|
1953
|
+
return this.vis.colourmaps[mapid];
|
1954
|
+
return null;
|
1955
|
+
}
|
1956
|
+
|
1957
|
+
paletteUpdate = function(obj, id) {
|
1958
|
+
//Convert name to index for now
|
1959
|
+
//console.log("paletteUpdate " + id + " : " + obj.name);
|
1960
|
+
viewer = obj.viewer;
|
1961
|
+
id = viewer.lookupMapId(id);
|
1962
|
+
if (id != undefined) viewer.gradient.mapid = id;
|
1963
|
+
|
1964
|
+
//Load colourmap change
|
1965
|
+
if (viewer.gradient.mapid < 0) return;
|
1966
|
+
var cmap = viewer.vis.colourmaps[viewer.gradient.mapid];
|
1967
|
+
if (!cmap) return;
|
1968
|
+
|
1969
|
+
paletteLoad(cmap.palette);
|
1970
|
+
|
1971
|
+
//Update colour data
|
1972
|
+
cmap.colours = cmap.palette.colours;
|
1973
|
+
|
1974
|
+
if (viewer.webgl) {
|
1975
|
+
var gradient = document.getElementById('gradient');
|
1976
|
+
viewer.webgl.updateTexture(viewer.webgl.gradientTexture, gradient, viewer.gl.TEXTURE1); //Use 2nd texture unit
|
1977
|
+
//window.open(gradient.toDataURL());
|
1978
|
+
}
|
1979
|
+
}
|
1980
|
+
|
1981
|
+
paletteLoad = function(palette) {
|
1982
|
+
//Update colours and cache
|
1983
|
+
var canvas = document.getElementById('palette');
|
1984
|
+
var gradient = document.getElementById('gradient');
|
1985
|
+
var context = gradient.getContext('2d');
|
1986
|
+
if (!context) alert("getContext failed");
|
1987
|
+
|
1988
|
+
//Get colour data and store in array
|
1989
|
+
//Redraw without UI elements
|
1990
|
+
//palette.draw(canvas, false);
|
1991
|
+
palette.draw(gradient, false);
|
1992
|
+
//Cache colour values
|
1993
|
+
var pixels = context.getImageData(0, 0, MAXIDX+1, 1).data;
|
1994
|
+
|
1995
|
+
palette.cache = [];
|
1996
|
+
for (var c=0; c<=MAXIDX; c++)
|
1997
|
+
palette.cache[c] = pixels[c*4] + (pixels[c*4+1] << 8) + (pixels[c*4+2] << 16) + (pixels[c*4+3] << 24);
|
1998
|
+
|
1999
|
+
//Redraw UI
|
2000
|
+
palette.draw(canvas, true);
|
2001
|
+
}
|
2002
|
+
|
2003
|
+
Viewer.prototype.drawTimed = function() {
|
2004
|
+
if (this.drawTimer)
|
2005
|
+
clearTimeout(this.drawTimer);
|
2006
|
+
var viewer = this;
|
2007
|
+
this.drawTimer = setTimeout(function () {viewer.drawFrame();}, 100 );
|
2008
|
+
}
|
2009
|
+
|
2010
|
+
Viewer.prototype.draw = function(borderOnly) {
|
2011
|
+
//If requested draw border only (used while interacting)
|
2012
|
+
//Draw the full model on a timer
|
2013
|
+
this.drawFrame(borderOnly);
|
2014
|
+
if (borderOnly && !server) this.drawTimed();
|
2015
|
+
}
|
2016
|
+
|
2017
|
+
Viewer.prototype.drawFrame = function(borderOnly) {
|
2018
|
+
if (!this.canvas || this.inVR) return;
|
2019
|
+
|
2020
|
+
if (server && !document.mouse.isdown && this.gl) {
|
2021
|
+
//Don't draw in server mode unless interacting (border view)
|
2022
|
+
this.gl.clear(this.gl.COLOR_BUFFER_BIT | this.gl.DEPTH_BUFFER_BIT);
|
2023
|
+
return;
|
2024
|
+
}
|
2025
|
+
|
2026
|
+
/*/Show screenshot while interacting or if using server
|
2027
|
+
//if (server || borderOnly)
|
2028
|
+
var frame = document.getElementById('frame');
|
2029
|
+
if (frame) {
|
2030
|
+
if (server) {
|
2031
|
+
frame.style.display = 'block';
|
2032
|
+
this.width = frame.offsetWidth;
|
2033
|
+
this.height = frame.offsetHeight;
|
2034
|
+
this.canvas.style.width = this.width + "px";
|
2035
|
+
this.canvas.style.height = this.height + "px";
|
2036
|
+
} else {
|
2037
|
+
frame.style.display = 'none';
|
2038
|
+
}
|
2039
|
+
}*/
|
2040
|
+
|
2041
|
+
if (!this.gl) return;
|
2042
|
+
|
2043
|
+
if (this.width == 0 || this.height == 0) {
|
2044
|
+
//Get size from window/container
|
2045
|
+
if (this.canvas.parentElement != document.body) {
|
2046
|
+
//Get from container
|
2047
|
+
this.width = this.canvas.parentElement.offsetWidth;
|
2048
|
+
this.height = this.canvas.parentElement.offsetHeight;
|
2049
|
+
//alert(this.canvas.parentElement.id + " ==> " + this.canvas.parentElement.offsetWidth + " : " + this.canvas.parentElement.clientWidth + " : " + this.canvas.parentElement.style.width);
|
2050
|
+
} else {
|
2051
|
+
this.width = window.innerWidth;
|
2052
|
+
this.height = window.innerHeight;
|
2053
|
+
}
|
2054
|
+
}
|
2055
|
+
|
2056
|
+
if (this.width != this.canvas.width || this.height != this.canvas.height) {
|
2057
|
+
this.canvas.width = this.canvas.clientWidth = this.width;
|
2058
|
+
this.canvas.height = this.canvas.clientHeight = this.height;
|
2059
|
+
this.canvas.setAttribute("width", this.width);
|
2060
|
+
this.canvas.setAttribute("height", this.height);
|
2061
|
+
this.webgl.viewport = new Viewport(0, 0, this.width, this.height);
|
2062
|
+
}
|
2063
|
+
this.width = this.height = 0;
|
2064
|
+
|
2065
|
+
var start = new Date();
|
2066
|
+
|
2067
|
+
this.gl.viewport(this.webgl.viewport.x, this.webgl.viewport.y, this.webgl.viewport.width, this.webgl.viewport.height);
|
2068
|
+
// console.log(JSON.stringify(this.webgl.viewport));
|
2069
|
+
//this.gl.clearColor(this.background.red/255, this.background.green/255, this.background.blue/255, server || document.mouse.isdown ? 0 : 1);
|
2070
|
+
this.gl.clearColor(this.background.red/255, this.background.green/255, this.background.blue/255, server ? 0 : 1);
|
2071
|
+
//this.gl.clearColor(this.background.red/255, this.background.green/255, this.background.blue/255, 0);
|
2072
|
+
//this.gl.clearColor(1, 1, 1, 0);
|
2073
|
+
this.gl.clear(this.gl.COLOR_BUFFER_BIT | this.gl.DEPTH_BUFFER_BIT);
|
2074
|
+
|
2075
|
+
//Apply the camera
|
2076
|
+
this.webgl.view(this);
|
2077
|
+
|
2078
|
+
//Render objects
|
2079
|
+
for (var r in this.renderers) {
|
2080
|
+
if (this.renderers[r].border) {
|
2081
|
+
if (!borderOnly && !this.vis.border) continue;
|
2082
|
+
} else {
|
2083
|
+
if (borderOnly) continue;
|
2084
|
+
}
|
2085
|
+
|
2086
|
+
this.renderers[r].draw();
|
2087
|
+
//console.log("Draw: " + r + " : " + this.renderers[r].type);
|
2088
|
+
}
|
2089
|
+
|
2090
|
+
/*/Save canvas image to background for display while interacting
|
2091
|
+
if (!borderOnly && !server) {
|
2092
|
+
if (document.mouse.isdown || !("frame").src) {
|
2093
|
+
//document.getElementById("frame").src = document.getElementById('canvas').toDataURL("image/png");
|
2094
|
+
//document.getElementById('canvas').toBlob(function (blob) {document.getElementById("frame").src = URL.createObjectURL(blob);});
|
2095
|
+
this.border.draw();
|
2096
|
+
}
|
2097
|
+
}*/
|
2098
|
+
|
2099
|
+
this.rotated = false; //Clear rotation flag
|
2100
|
+
}
|
2101
|
+
|
2102
|
+
Viewer.prototype.syncRotation = function() {
|
2103
|
+
this.rotated = true;
|
2104
|
+
this.draw();
|
2105
|
+
//if (this.command)
|
2106
|
+
// this.command('' + this.getRotationString());
|
2107
|
+
}
|
2108
|
+
|
2109
|
+
Viewer.prototype.rotateX = function(deg) {
|
2110
|
+
this.rotation(deg, [1,0,0]);
|
2111
|
+
}
|
2112
|
+
|
2113
|
+
Viewer.prototype.rotateY = function(deg) {
|
2114
|
+
this.rotation(deg, [0,1,0]);
|
2115
|
+
}
|
2116
|
+
|
2117
|
+
Viewer.prototype.rotateZ = function(deg) {
|
2118
|
+
this.rotation(deg, [0,0,1]);
|
2119
|
+
}
|
2120
|
+
|
2121
|
+
Viewer.prototype.rotation = function(deg, axis) {
|
2122
|
+
//Quaterion rotate
|
2123
|
+
var arad = deg * Math.PI / 180.0;
|
2124
|
+
var rotation = quat4.fromAngleAxis(arad, axis);
|
2125
|
+
rotation = quat4.normalize(rotation);
|
2126
|
+
this.rotate = quat4.multiply(rotation, this.rotate);
|
2127
|
+
}
|
2128
|
+
|
2129
|
+
Viewer.prototype.getRotation = function() {
|
2130
|
+
return [this.rotate[0], this.rotate[1], this.rotate[2], this.rotate[3]];
|
2131
|
+
}
|
2132
|
+
|
2133
|
+
Viewer.prototype.getRotationString = function() {
|
2134
|
+
//Return current rotation quaternion as string
|
2135
|
+
var q = this.getRotation();
|
2136
|
+
return 'rotation ' + q[0] + ' ' + q[1] + ' ' + q[2] + ' ' + q[3];
|
2137
|
+
}
|
2138
|
+
|
2139
|
+
Viewer.prototype.getTranslationString = function() {
|
2140
|
+
return 'translation ' + this.translate[0] + ' ' + this.translate[1] + ' ' + this.translate[2];
|
2141
|
+
}
|
2142
|
+
|
2143
|
+
Viewer.prototype.reset = function() {
|
2144
|
+
if (this.gl) {
|
2145
|
+
this.updateDims(this.view);
|
2146
|
+
this.draw();
|
2147
|
+
}
|
2148
|
+
|
2149
|
+
if (server) {
|
2150
|
+
//sendCommand('' + this.getRotationString());
|
2151
|
+
//sendCommand('' + this.getTranslationString());
|
2152
|
+
sendCommand('reset');
|
2153
|
+
}
|
2154
|
+
}
|
2155
|
+
|
2156
|
+
Viewer.prototype.zoom = function(factor) {
|
2157
|
+
if (this.gl) {
|
2158
|
+
var adj = factor * this.modelsize;
|
2159
|
+
this.translate[2] += adj;
|
2160
|
+
this.draw();
|
2161
|
+
}
|
2162
|
+
|
2163
|
+
if (server)
|
2164
|
+
sendCommand('' + this.getTranslationString());
|
2165
|
+
//sendCommand('zoom ' + factor);
|
2166
|
+
}
|
2167
|
+
|
2168
|
+
Viewer.prototype.zoomClip = function(factor) {
|
2169
|
+
if (this.gl) {
|
2170
|
+
var near_clip = this.near_clip + factor * this.modelsize;
|
2171
|
+
if (near_clip >= this.modelsize * 0.001)
|
2172
|
+
this.near_clip = near_clip;
|
2173
|
+
this.draw();
|
2174
|
+
}
|
2175
|
+
if (server)
|
2176
|
+
sendCommand('zoomclip ' + factor);
|
2177
|
+
}
|
2178
|
+
|
2179
|
+
Viewer.prototype.updateDims = function(view) {
|
2180
|
+
if (!view) return;
|
2181
|
+
var oldsize = this.modelsize;
|
2182
|
+
this.dims = [view.max[0] - view.min[0], view.max[1] - view.min[1], view.max[2] - view.min[2]];
|
2183
|
+
this.modelsize = Math.sqrt(this.dims[0]*this.dims[0] + this.dims[1]*this.dims[1] + this.dims[2]*this.dims[2]);
|
2184
|
+
|
2185
|
+
this.focus = [view.min[0] + 0.5*this.dims[0], view.min[1] + 0.5*this.dims[1], view.min[2] + 0.5*this.dims[2]];
|
2186
|
+
this.centre = [this.focus[0],this.focus[1],this.focus[2]];
|
2187
|
+
|
2188
|
+
this.translate = [0,0,0];
|
2189
|
+
if (this.modelsize != oldsize) this.translate[2] = -this.modelsize*1.25;
|
2190
|
+
|
2191
|
+
if (this.near_clip == 0.0) this.near_clip = this.modelsize / 10.0;
|
2192
|
+
if (this.far_clip == 0.0) this.far_clip = this.modelsize * 10.0;
|
2193
|
+
|
2194
|
+
quat4.identity(this.rotate);
|
2195
|
+
this.rotated = true;
|
2196
|
+
|
2197
|
+
if (view) {
|
2198
|
+
//Initial rotation
|
2199
|
+
if (view.rotate) {
|
2200
|
+
if (view.rotate.length == 3) {
|
2201
|
+
this.rotateZ(-view.rotate[2]);
|
2202
|
+
this.rotateY(-view.rotate[1]);
|
2203
|
+
this.rotateX(-view.rotate[0]);
|
2204
|
+
} else if (view.rotate.length == 4) {
|
2205
|
+
this.rotate = quat4.create(view.rotate);
|
2206
|
+
}
|
2207
|
+
}
|
2208
|
+
|
2209
|
+
//Translate
|
2210
|
+
if (view.translate) {
|
2211
|
+
this.translate[0] = view.translate[0];
|
2212
|
+
this.translate[1] = view.translate[1];
|
2213
|
+
this.translate[2] = view.translate[2];
|
2214
|
+
}
|
2215
|
+
|
2216
|
+
//Scale
|
2217
|
+
if (view.scale) {
|
2218
|
+
this.scale[0] = view.scale[0];
|
2219
|
+
this.scale[1] = view.scale[1];
|
2220
|
+
this.scale[2] = view.scale[2];
|
2221
|
+
}
|
2222
|
+
|
2223
|
+
//Focal point
|
2224
|
+
if (view.focus) {
|
2225
|
+
this.focus[0] = this.centre[0] = view.focus[0];
|
2226
|
+
this.focus[1] = this.centre[1] = view.focus[1];
|
2227
|
+
this.focus[2] = this.centre[2] = view.focus[2];
|
2228
|
+
}
|
2229
|
+
|
2230
|
+
}
|
2231
|
+
|
2232
|
+
/*
|
2233
|
+
console.log("DIMS: " + this.dims[0] + "," + this.dims[1] + "," + this.dims[2]);
|
2234
|
+
console.log("New model size: " + this.modelsize + ", Focal point: " + this.focus[0] + "," + this.focus[1] + "," + this.focus[2]);
|
2235
|
+
console.log("Translate: " + this.translate[0] + "," + this.translate[1] + "," + this.translate[2]);
|
2236
|
+
console.log("Rotate: " + this.rotate[0] + "," + this.rotate[1] + "," + this.rotate[2] + "," + this.rotate[3]);
|
2237
|
+
console.log(JSON.stringify(view));
|
2238
|
+
*/
|
2239
|
+
}
|
2240
|
+
|
2241
|
+
/*
|
2242
|
+
function resizeToWindow() {
|
2243
|
+
//var canvas = document.getElementById('canvas');
|
2244
|
+
//if (canvas.width < window.innerWidth || canvas.height < window.innerHeight)
|
2245
|
+
sendCommand('resize ' + window.innerWidth + " " + window.innerHeight);
|
2246
|
+
var frame = document.getElementById('frame');
|
2247
|
+
if (frame) {
|
2248
|
+
canvas.style.width = frame.style.width = "100%";
|
2249
|
+
canvas.style.height = frame.style.height = "100%";
|
2250
|
+
}
|
2251
|
+
}
|
2252
|
+
|
2253
|
+
function connectWindow() {
|
2254
|
+
requestData('/render');
|
2255
|
+
window.location.reload();
|
2256
|
+
}
|
2257
|
+
*/
|
2258
|
+
|
2259
|
+
|