mirror of
https://github.com/OpenFOAM/ThirdParty-6.git
synced 2025-12-08 06:57:43 +00:00
1805 lines
68 KiB
JavaScript
1805 lines
68 KiB
JavaScript
/**
|
|
* vtkWeb JavaScript Library.
|
|
*
|
|
* This module extend the vtkWeb viewport to add support for WebGL rendering.
|
|
*
|
|
* @class vtkWeb.viewports.webgl
|
|
*
|
|
* Viewport Factory description:
|
|
* - Key: webgl
|
|
* - Stats:
|
|
* - webgl-fps
|
|
* - webgl-nb-objects
|
|
* - webgl-fetch-scene
|
|
* - webgl-fetch-object
|
|
*/
|
|
(function (GLOBAL, $) {
|
|
var module = {},
|
|
RENDERER_CSS = {
|
|
"position": "absolute",
|
|
"top" : "0px",
|
|
"left" : "0px",
|
|
"right" : "0px",
|
|
"bottom" : "0px",
|
|
"z-index" : "0"
|
|
},
|
|
RENDERER_CSS_2D = {
|
|
"z-index" : "1"
|
|
},
|
|
RENDERER_CSS_3D = {
|
|
"z-index" : "0"
|
|
},
|
|
DEFAULT_OPTIONS = {
|
|
keepServerInSynch: false
|
|
},
|
|
FACTORY_KEY = 'webgl',
|
|
FACTORY = {
|
|
'builder': createGeometryDeliveryRenderer,
|
|
'options': DEFAULT_OPTIONS,
|
|
'stats': {
|
|
'webgl-fps': {
|
|
label: 'Framerate',
|
|
type: 'time',
|
|
convert: function(value) {
|
|
if(value === 0) {
|
|
return 0;
|
|
}
|
|
return (1000 / value).toFixed(2);
|
|
}
|
|
},
|
|
'webgl-nb-objects': {
|
|
label: 'Number of 3D objects',
|
|
type: 'value',
|
|
convert: NoOp
|
|
},
|
|
'webgl-fetch-scene': {
|
|
label: 'Fetch scene (ms)',
|
|
type: 'time',
|
|
convert: NoOp
|
|
},
|
|
'webgl-fetch-object': {
|
|
label: 'Fetch object (ms)',
|
|
type: 'time',
|
|
convert: NoOp
|
|
}
|
|
}
|
|
},
|
|
DEFAULT_SHADERS = {},
|
|
mvMatrixStack = [],
|
|
PROGRESS_BAR_TEMPLATE =
|
|
'<div class="download-progressbar-container">' +
|
|
' <span class="progressbar-title">Download Progress</span>' +
|
|
' <div class="progressbar-content-container">' +
|
|
' <span class="progress-message-span">MESSAGE</span>' +
|
|
' <div class="progress progress-meter-container">' +
|
|
' <div class="progress-bar progress-bar-striped active progress-meter" role="progressbar" aria-valuenow="100" aria-valuemin="0" aria-valuemax="100" style="width: 100%;">' +
|
|
' </div>' +
|
|
' </div>' +
|
|
' </div>' +
|
|
'</div>';
|
|
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
function NoOp(a) {
|
|
return a;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
// Initialize the Shaders
|
|
// ----------------------------------------------------------------------
|
|
|
|
DEFAULT_SHADERS["shader-fs"] = {
|
|
type: "x-shader/x-fragment",
|
|
code: "\
|
|
#ifdef GL_ES\n\
|
|
precision highp float;\n\
|
|
#endif\n\
|
|
uniform bool uIsLine;\
|
|
varying vec4 vColor;\
|
|
varying vec4 vTransformedNormal;\
|
|
varying vec4 vPosition;\
|
|
void main(void) {\
|
|
float directionalLightWeighting1 = max(dot(normalize(vTransformedNormal.xyz), vec3(0.0, 0.0, 1.0)), 0.0); \
|
|
float directionalLightWeighting2 = max(dot(normalize(vTransformedNormal.xyz), vec3(0.0, 0.0, -1.0)), 0.0);\
|
|
vec3 lightWeighting = max(vec3(1.0, 1.0, 1.0) * directionalLightWeighting1, vec3(1.0, 1.0, 1.0) * directionalLightWeighting2);\
|
|
if (uIsLine == false){\
|
|
gl_FragColor = vec4(vColor.rgb * lightWeighting, vColor.a);\
|
|
} else {\
|
|
gl_FragColor = vColor*vec4(1.0, 1.0, 1.0, 1.0);\
|
|
}\
|
|
}"
|
|
};
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
DEFAULT_SHADERS["shader-vs"] = {
|
|
type: "x-shader/x-vertex",
|
|
code: "\
|
|
attribute vec3 aVertexPosition;\
|
|
attribute vec4 aVertexColor;\
|
|
attribute vec3 aVertexNormal;\
|
|
uniform mat4 uMVMatrix;\
|
|
uniform mat4 uPMatrix;\
|
|
uniform mat4 uNMatrix;\
|
|
varying vec4 vColor;\
|
|
varying vec4 vPosition;\
|
|
varying vec4 vTransformedNormal;\
|
|
void main(void) {\
|
|
vPosition = uMVMatrix * vec4(aVertexPosition, 1.0);\
|
|
gl_Position = uPMatrix * vPosition;\
|
|
vTransformedNormal = uNMatrix * vec4(aVertexNormal, 1.0);\
|
|
vColor = aVertexColor;\
|
|
}"
|
|
};
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
DEFAULT_SHADERS["shader-fs-Point"] = {
|
|
type: "x-shader/x-fragment",
|
|
code: "\
|
|
#ifdef GL_ES\n\
|
|
precision highp float;\n\
|
|
#endif\n\
|
|
varying vec4 vColor;\
|
|
void main(void) {\
|
|
gl_FragColor = vColor;\
|
|
}"
|
|
};
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
DEFAULT_SHADERS["shader-vs-Point"] = {
|
|
type: "x-shader/x-vertex",
|
|
code: "\
|
|
attribute vec3 aVertexPosition;\
|
|
attribute vec4 aVertexColor;\
|
|
uniform mat4 uMVMatrix;\
|
|
uniform mat4 uPMatrix;\
|
|
uniform mat4 uNMatrix;\
|
|
uniform float uPointSize;\
|
|
varying vec4 vColor;\
|
|
void main(void) {\
|
|
vec4 pos = uMVMatrix * vec4(aVertexPosition, 1.0);\
|
|
gl_Position = uPMatrix * pos;\
|
|
vColor = aVertexColor*vec4(1.0, 1.0, 1.0, 1.0);\
|
|
gl_PointSize = uPointSize;\
|
|
}"
|
|
};
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
function getShader(gl, id) {
|
|
try {
|
|
var jsonShader = DEFAULT_SHADERS[id], shader = null;
|
|
|
|
// Allocate shader
|
|
if(jsonShader.type === "x-shader/x-fragment") {
|
|
shader = gl.createShader(gl.FRAGMENT_SHADER);
|
|
} else if(jsonShader.type === "x-shader/x-vertex") {
|
|
shader = gl.createShader(gl.VERTEX_SHADER);
|
|
} else {
|
|
return null;
|
|
}
|
|
|
|
// Set code and compile
|
|
gl.shaderSource(shader, jsonShader.code);
|
|
gl.compileShader(shader);
|
|
|
|
// Check compilation
|
|
if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
|
|
alert(gl.getShaderInfoLog(shader));
|
|
return null;
|
|
}
|
|
|
|
return shader;
|
|
} catch(error) {
|
|
console.log(error);
|
|
}
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
function initializeShader(gl, shaderProgram, pointShaderProgram) {
|
|
try {
|
|
|
|
var vertexShader = getShader(gl, 'shader-vs'),
|
|
fragmentShader = getShader(gl, 'shader-fs'),
|
|
pointFragShader = getShader(gl, 'shader-fs-Point'),
|
|
pointVertShader = getShader(gl, 'shader-vs-Point');
|
|
|
|
// Initialize program
|
|
gl.attachShader(shaderProgram, vertexShader);
|
|
gl.attachShader(shaderProgram, fragmentShader);
|
|
gl.linkProgram(shaderProgram);
|
|
if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) {
|
|
alert("Could not initialise shaders");
|
|
}
|
|
|
|
gl.attachShader(pointShaderProgram, pointVertShader);
|
|
gl.attachShader(pointShaderProgram, pointFragShader);
|
|
gl.linkProgram(pointShaderProgram);
|
|
if (!gl.getProgramParameter(pointShaderProgram, gl.LINK_STATUS)) {
|
|
alert("Could not initialise the point shaders");
|
|
}
|
|
|
|
gl.useProgram(pointShaderProgram);
|
|
pointShaderProgram.vertexPositionAttribute = gl.getAttribLocation(pointShaderProgram, "aVertexPosition");
|
|
gl.enableVertexAttribArray(pointShaderProgram.vertexPositionAttribute);
|
|
pointShaderProgram.vertexColorAttribute = gl.getAttribLocation(pointShaderProgram, "aVertexColor");
|
|
gl.enableVertexAttribArray(pointShaderProgram.vertexColorAttribute);
|
|
pointShaderProgram.pMatrixUniform = gl.getUniformLocation(pointShaderProgram, "uPMatrix");
|
|
pointShaderProgram.mvMatrixUniform = gl.getUniformLocation(pointShaderProgram, "uMVMatrix");
|
|
pointShaderProgram.nMatrixUniform = gl.getUniformLocation(pointShaderProgram, "uNMatrix");
|
|
pointShaderProgram.uPointSize = gl.getUniformLocation(pointShaderProgram, "uPointSize");
|
|
|
|
gl.useProgram(shaderProgram);
|
|
shaderProgram.vertexPositionAttribute = gl.getAttribLocation(shaderProgram, "aVertexPosition");
|
|
gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute);
|
|
shaderProgram.vertexColorAttribute = gl.getAttribLocation(shaderProgram, "aVertexColor");
|
|
gl.enableVertexAttribArray(shaderProgram.vertexColorAttribute);
|
|
shaderProgram.vertexNormalAttribute = gl.getAttribLocation(shaderProgram, "aVertexNormal");
|
|
gl.enableVertexAttribArray(shaderProgram.vertexNormalAttribute);
|
|
shaderProgram.pMatrixUniform = gl.getUniformLocation(shaderProgram, "uPMatrix");
|
|
shaderProgram.mvMatrixUniform = gl.getUniformLocation(shaderProgram, "uMVMatrix");
|
|
shaderProgram.nMatrixUniform = gl.getUniformLocation(shaderProgram, "uNMatrix");
|
|
shaderProgram.uIsLine = gl.getUniformLocation(shaderProgram, "uIsLine");
|
|
} catch(error) {
|
|
console.log(error);
|
|
}
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
// GL rendering metods
|
|
// ----------------------------------------------------------------------
|
|
|
|
function setMatrixUniforms(gl, shaderProgram, projMatrix, mvMatrix) {
|
|
var mvMatrixInv = mat4.create(), normal = mat4.create();
|
|
|
|
mat4.invert(mvMatrixInv, mvMatrix);
|
|
mat4.transpose(normal, mvMatrixInv);
|
|
|
|
gl.uniformMatrix4fv(shaderProgram.pMatrixUniform, false, projMatrix);
|
|
gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, mvMatrix);
|
|
if(shaderProgram.nMatrixUniform != null) gl.uniformMatrix4fv(shaderProgram.nMatrixUniform, false, normal);
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
function renderMesh(renderingContext, camera) {
|
|
try {
|
|
var obj = this,
|
|
mvMatrix = mat4.clone(camera.getCameraMatrices()[1]),
|
|
projMatrix = mat4.clone(camera.getCameraMatrices()[0]),
|
|
objMatrix = mat4.transpose(mat4.create(), obj.matrix),
|
|
gl = renderingContext.gl,
|
|
shaderProgram = renderingContext.shaderProgram;
|
|
|
|
gl.useProgram(shaderProgram);
|
|
gl.uniform1i(shaderProgram.uIsLine, false);
|
|
|
|
mvMatrix = mat4.multiply(mvMatrix, mvMatrix, objMatrix);
|
|
|
|
gl.bindBuffer(gl.ARRAY_BUFFER, obj.vbuff);
|
|
gl.vertexAttribPointer(shaderProgram.vertexPositionAttribute, obj.vbuff.itemSize, gl.FLOAT, false, 0, 0);
|
|
gl.bindBuffer(gl.ARRAY_BUFFER, obj.nbuff);
|
|
gl.vertexAttribPointer(shaderProgram.vertexNormalAttribute, obj.nbuff.itemSize, gl.FLOAT, false, 0, 0);
|
|
gl.bindBuffer(gl.ARRAY_BUFFER, obj.cbuff);
|
|
gl.vertexAttribPointer(shaderProgram.vertexColorAttribute, obj.cbuff.itemSize, gl.FLOAT, false, 0, 0);
|
|
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, obj.ibuff);
|
|
|
|
setMatrixUniforms(gl, shaderProgram, projMatrix, mvMatrix);
|
|
|
|
gl.drawElements(gl.TRIANGLES, obj.numberOfIndex, gl.UNSIGNED_SHORT, 0);
|
|
} catch(error) {
|
|
console.log(error);
|
|
}
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
function renderLine(renderingContext, camera) {
|
|
try {
|
|
var obj = this,
|
|
mvMatrix = mat4.clone(camera.getCameraMatrices()[1]),
|
|
projMatrix = mat4.clone(camera.getCameraMatrices()[0]),
|
|
objMatrix = mat4.transpose(mat4.create(), obj.matrix),
|
|
gl = renderingContext.gl,
|
|
shaderProgram = renderingContext.shaderProgram;
|
|
|
|
gl.useProgram(shaderProgram);
|
|
|
|
gl.enable(gl.POLYGON_OFFSET_FILL); //Avoid zfighting
|
|
gl.polygonOffset(-1.0, -1.0);
|
|
|
|
gl.uniform1i(shaderProgram.uIsLine, true);
|
|
|
|
mvMatrix = mat4.multiply(mvMatrix, mvMatrix, objMatrix);
|
|
|
|
gl.bindBuffer(gl.ARRAY_BUFFER, obj.lbuff);
|
|
gl.vertexAttribPointer(shaderProgram.vertexPositionAttribute, obj.lbuff.itemSize, gl.FLOAT, false, 0, 0);
|
|
gl.bindBuffer(gl.ARRAY_BUFFER, obj.nbuff);
|
|
gl.vertexAttribPointer(shaderProgram.vertexNormalAttribute, obj.nbuff.itemSize, gl.FLOAT, false, 0, 0);
|
|
gl.bindBuffer(gl.ARRAY_BUFFER, obj.cbuff);
|
|
gl.vertexAttribPointer(shaderProgram.vertexColorAttribute, obj.cbuff.itemSize, gl.FLOAT, false, 0, 0);
|
|
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, obj.ibuff);
|
|
|
|
setMatrixUniforms(gl, shaderProgram, projMatrix, mvMatrix);
|
|
|
|
gl.drawElements(gl.LINES, obj.numberOfIndex, gl.UNSIGNED_SHORT, 0);
|
|
|
|
gl.disable(gl.POLYGON_OFFSET_FILL);
|
|
} catch(error) {
|
|
console.log(error);
|
|
}
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
function renderPoints(renderingContext, camera) {
|
|
try {
|
|
var obj = this,
|
|
mvMatrix = mat4.clone(camera.getCameraMatrices()[1]),
|
|
projMatrix = mat4.clone(camera.getCameraMatrices()[0]),
|
|
objMatrix = mat4.transpose(mat4.create(), obj.matrix),
|
|
gl = renderingContext.gl,
|
|
pointShaderProgram = renderingContext.pointShaderProgram;
|
|
|
|
gl.useProgram(pointShaderProgram);
|
|
|
|
gl.enable(gl.POLYGON_OFFSET_FILL); //Avoid zfighting
|
|
gl.polygonOffset(-1.0, -1.0);
|
|
|
|
gl.uniform1f(pointShaderProgram.uPointSize, 2.0);
|
|
|
|
mvMatrix = mat4.multiply(mvMatrix, mvMatrix, objMatrix);
|
|
|
|
gl.bindBuffer(gl.ARRAY_BUFFER, obj.pbuff);
|
|
gl.vertexAttribPointer(pointShaderProgram.vertexPositionAttribute, obj.pbuff.itemSize, gl.FLOAT, false, 0, 0);
|
|
gl.bindBuffer(gl.ARRAY_BUFFER, obj.cbuff);
|
|
gl.vertexAttribPointer(pointShaderProgram.vertexColorAttribute, obj.cbuff.itemSize, gl.FLOAT, false, 0, 0);
|
|
|
|
setMatrixUniforms(gl, pointShaderProgram, projMatrix, mvMatrix);
|
|
|
|
gl.drawArrays(gl.POINTS, 0, obj.numberOfPoints);
|
|
|
|
gl.disable(gl.POLYGON_OFFSET_FILL);
|
|
} catch(error) {
|
|
console.log(error);
|
|
}
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
function renderColorMap(renderingContext, camera) {
|
|
try {
|
|
var obj = this, ctx = renderingContext.ctx2d, range, txt, color, c, v,
|
|
size, pos, dx, dy, realSize, textSizeX, textSizeY, grad,
|
|
width = renderingContext.container.width(),
|
|
height = renderingContext.container.height();
|
|
|
|
range = [obj.colors[0][0], obj.colors[obj.colors.length-1][0]];
|
|
size = [obj.size[0]*width, obj.size[1]*height];
|
|
pos = [obj.position[0]*width, (1-obj.position[1])*height];
|
|
pos[1] = pos[1]-size[1];
|
|
dx = size[0]/size[1];
|
|
dy = size[1]/size[0];
|
|
realSize = size;
|
|
|
|
textSizeX = Math.round(height/35);
|
|
textSizeY = Math.round(height/23);
|
|
if (obj.orientation == 1){
|
|
size[0] = size[0]*dy/25;
|
|
size[1] = size[1]-(2*textSizeY);
|
|
} else {
|
|
size[0] = size[0];
|
|
size[1] = size[1]*dx/25;
|
|
}
|
|
|
|
// Draw Gradient
|
|
if(obj.orientation == 1){
|
|
pos[1] += 2*textSizeY;
|
|
grad = ctx.createLinearGradient(pos[0], pos[1], pos[0], pos[1]+size[1]);
|
|
} else {
|
|
pos[1] += 2*textSizeY;
|
|
grad = ctx.createLinearGradient(pos[0], pos[1], pos[0]+size[0], pos[1]);
|
|
}
|
|
if ((range[1]-range[0]) == 0){
|
|
color = 'rgba(' + obj.colors[0][1] + ',' + obj.colors[0][2] + ',' + obj.colors[0][3] + ',1)';
|
|
grad.addColorStop(0, color);
|
|
grad.addColorStop(1, color);
|
|
} else {
|
|
for(c=0; c<obj.colors.length; c++){
|
|
v = ((obj.colors[c][0]-range[0])/(range[1]-range[0]));
|
|
if (obj.orientation == 1) v=1-v;
|
|
color = 'rgba(' + obj.colors[c][1] + ',' + obj.colors[c][2] + ',' + obj.colors[c][3] + ',1)';
|
|
grad.addColorStop(v, color);
|
|
}
|
|
}
|
|
ctx.fillStyle = grad;
|
|
ctx.fillRect(pos[0], pos[1], size[0], size[1]);
|
|
// Draw Range Labels
|
|
range[0] = Math.round(range[0]*1000)/1000;
|
|
range[1] = Math.round(range[1]*1000)/1000;
|
|
ctx.fillStyle = 'white';
|
|
ctx.font = textSizeY + 'px sans-serif';
|
|
ctx.txtBaseline = 'ideographic';
|
|
if (obj.orientation == 1){
|
|
ctx.fillText(range[1], pos[0], pos[1]-5);
|
|
ctx.fillText(range[0], pos[0], pos[1]+size[1]+textSizeY);
|
|
} else {
|
|
ctx.fillText(range[0], pos[0], pos[1]+size[1]+textSizeY);
|
|
txt = range[1].toString();
|
|
ctx.fillText(range[1], pos[0]+size[0]-((txt.length-1)*textSizeX), pos[1]+size[1]+textSizeY);
|
|
}
|
|
// Draw Title
|
|
ctx.fillStyle = 'white';
|
|
ctx.font = textSizeY + 'px sans-serif';
|
|
ctx.txtBaseline = 'ideographic';
|
|
if (obj.orientation == 1) ctx.fillText(obj.title, pos[0]+(obj.size[0]*width)/2-(obj.title.length*textSizeX/2), pos[1]-textSizeY-5);
|
|
else ctx.fillText(obj.title, pos[0]+size[0]/2-(obj.title.length*textSizeX/2), pos[1]-textSizeY-5);
|
|
} catch(error) {
|
|
console.log(error);
|
|
}
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
function renderBackground(renderingContext, camera) {
|
|
try {
|
|
var background = this, gl = renderingContext.gl, shaderProgram = renderingContext.shaderProgram;
|
|
|
|
gl.useProgram(renderingContext.shaderProgram);
|
|
gl.uniform1i(renderingContext.shaderProgram.uIsLine, false);
|
|
|
|
var projMatrix = mat4.create();
|
|
var mvMatrix = mat4.create();
|
|
var normalMatrix = mat4.create();
|
|
|
|
// @note Not sure if this is required
|
|
mat4.translate(mvMatrix, mvMatrix, [0.0, 0.0, -1.0]);
|
|
|
|
gl.bindBuffer(gl.ARRAY_BUFFER, background.vbuff);
|
|
gl.vertexAttribPointer(shaderProgram.vertexPositionAttribute, background.vbuff.itemSize, gl.FLOAT, false, 0, 0);
|
|
gl.bindBuffer(gl.ARRAY_BUFFER, background.nbuff);
|
|
gl.vertexAttribPointer(shaderProgram.vertexNormalAttribute, background.nbuff.itemSize, gl.FLOAT, false, 0, 0);
|
|
gl.bindBuffer(gl.ARRAY_BUFFER, background.cbuff);
|
|
gl.vertexAttribPointer(shaderProgram.vertexColorAttribute, background.cbuff.itemSize, gl.FLOAT, false, 0, 0);
|
|
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, background.ibuff);
|
|
|
|
renderingContext.gl.uniformMatrix4fv(shaderProgram.pMatrixUniform, false, projMatrix);
|
|
renderingContext.gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, mvMatrix);
|
|
if(shaderProgram.nMatrixUniform != null) renderingContext.gl.uniformMatrix4fv(shaderProgram.nMatrixUniform, false, normalMatrix);
|
|
|
|
gl.drawElements(gl.TRIANGLES, background.numberOfIndex, gl.UNSIGNED_SHORT, 0);
|
|
} catch(error) {
|
|
console.log(error);
|
|
}
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
// 3D object handler
|
|
// ----------------------------------------------------------------------
|
|
|
|
function create3DObjectHandler() {
|
|
var objectIndex = {}, displayList = {}, sceneJSON;
|
|
|
|
// ------------------------------------------------------------------
|
|
|
|
function getKey(object) {
|
|
return object.id + '_' + object.md5;
|
|
}
|
|
|
|
// ------------------------------------------------------------------
|
|
|
|
function getLayerDisplayList(layer) {
|
|
var key = String(layer);
|
|
if(!displayList.hasOwnProperty(key)) {
|
|
displayList[key] = {
|
|
transparent: [],
|
|
solid: []
|
|
};
|
|
}
|
|
return displayList[key];
|
|
}
|
|
|
|
// ------------------------------------------------------------------
|
|
function render(displayList, renderingContext, camera) {
|
|
var i, k, key, array;
|
|
for(i in displayList) {
|
|
key = displayList[i];
|
|
if(objectIndex.hasOwnProperty(key)) {
|
|
array = objectIndex[key];
|
|
for(k in array) {
|
|
array[k].render(renderingContext, camera);
|
|
}
|
|
}
|
|
}
|
|
return displayList.length;
|
|
}
|
|
|
|
// ------------------------------------------------------------------
|
|
|
|
return {
|
|
clearCache: function() {
|
|
objectIndex = {};
|
|
},
|
|
|
|
// --------------------------------------------------------------
|
|
|
|
registerObject: function(object) {
|
|
var key = getKey(object), idx;
|
|
if(!objectIndex.hasOwnProperty(key)) {
|
|
objectIndex[key] = [];
|
|
} else {
|
|
// Make sure is not already in
|
|
for(idx in objectIndex[key]) {
|
|
if(objectIndex[key][idx].part === object.part) {
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Add it
|
|
objectIndex[key].push(object);
|
|
},
|
|
|
|
// --------------------------------------------------------------
|
|
|
|
isObjectRegistered: function(object) {
|
|
var key = getKey(object), idx;
|
|
if (!objectIndex.hasOwnProperty(key)) {
|
|
return false;
|
|
} else {
|
|
for (idx in objectIndex[key]) {
|
|
if (objectIndex[key][idx].part === object.part) {
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
},
|
|
|
|
// --------------------------------------------------------------
|
|
|
|
updateDisplayList: function(scene) {
|
|
// Reset displayList
|
|
displayList = {}, sceneJSON = scene;
|
|
|
|
// Create display lists
|
|
for(var idx in sceneJSON.Objects) {
|
|
var currentObject = sceneJSON.Objects[idx],
|
|
displayListKey = currentObject.hasTransparency ? 'transparent' : 'solid',
|
|
key = getKey(currentObject);
|
|
|
|
getLayerDisplayList(currentObject.layer)[displayListKey].push(key);
|
|
}
|
|
},
|
|
|
|
// --------------------------------------------------------------
|
|
|
|
renderTransparent: function(layer, renderingContext, camera) {
|
|
var displayList = getLayerDisplayList(layer).transparent;
|
|
return render(displayList, renderingContext, camera);
|
|
},
|
|
|
|
// --------------------------------------------------------------
|
|
|
|
renderSolid: function(layer, renderingContext, camera) {
|
|
var displayList = getLayerDisplayList(layer).solid;
|
|
return render(displayList, renderingContext, camera);
|
|
},
|
|
|
|
// --------------------------------------------------------------
|
|
|
|
fetchMissingObjects: function(fetchMethod) {
|
|
var fetch = fetchMethod, idx, part;
|
|
for(idx in sceneJSON.Objects) {
|
|
var currentObject = sceneJSON.Objects[idx],
|
|
key = getKey(currentObject);
|
|
if(!objectIndex.hasOwnProperty(key)) {
|
|
// Request all the pieces
|
|
for(part = 1; part <= currentObject.parts; part++) {
|
|
fetch(currentObject, part);
|
|
}
|
|
}
|
|
}
|
|
},
|
|
|
|
// --------------------------------------------------------------
|
|
|
|
garbageCollect: function() {
|
|
var refCount = {}, key, layer, array, idx;
|
|
for(key in objectIndex) {
|
|
refCount[key] = 0;
|
|
}
|
|
|
|
// Start registering display list
|
|
for(layer in displayList) {
|
|
array = displayList[layer].solid.concat(displayList[layer].transparent);
|
|
for(idx in array) {
|
|
if(refCount.hasOwnProperty(array[idx])) {
|
|
refCount[array[idx]]++;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Remove entry with no reference
|
|
for(key in refCount) {
|
|
if(refCount[key] === 0) {
|
|
delete objectIndex[key];
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
// GL object creation
|
|
// ----------------------------------------------------------------------
|
|
|
|
function get4ByteNumber(binaryArray, cursor) {
|
|
return (binaryArray[cursor++]) + (binaryArray[cursor++] << 8) + (binaryArray[cursor++] << 16) + (binaryArray[cursor++] << 24);
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
function buildBackground(gl, color1, color2) {
|
|
try {
|
|
if (typeof(gl) == "undefined") return;
|
|
|
|
var background = {
|
|
vertices: new Float32Array([-1.0, -1.0, 0.0, 1.0, -1.0, 0.0, 1.0, 1.0, 0.0, -1.0, 1.0, 0.0]),
|
|
colors: new Float32Array([
|
|
color1[0], color1[1], color1[2], 1.0,
|
|
color1[0], color1[1], color1[2], 1.0,
|
|
color2[0], color2[1], color2[2], 1.0,
|
|
color2[0], color2[1], color2[2], 1.0]),
|
|
index: new Uint16Array([0, 1, 2, 0, 2, 3]),
|
|
normals: new Float32Array([0.0, 0.0, -1.0, 0.0, 0.0, -1.0, 0.0, 0.0, -1.0, 0.0, 0.0, -1.0]),
|
|
numberOfIndex: 6
|
|
};
|
|
|
|
//Create Buffers
|
|
background.vbuff = gl.createBuffer();
|
|
gl.bindBuffer(gl.ARRAY_BUFFER, background.vbuff);
|
|
gl.bufferData(gl.ARRAY_BUFFER, background.vertices, gl.STATIC_DRAW);
|
|
background.vbuff.itemSize = 3;
|
|
background.nbuff = gl.createBuffer();
|
|
gl.bindBuffer(gl.ARRAY_BUFFER, background.nbuff);
|
|
gl.bufferData(gl.ARRAY_BUFFER, background.normals, gl.STATIC_DRAW);
|
|
background.nbuff.itemSize = 3;
|
|
background.cbuff = gl.createBuffer();
|
|
gl.bindBuffer(gl.ARRAY_BUFFER, background.cbuff);
|
|
gl.bufferData(gl.ARRAY_BUFFER, background.colors, gl.STATIC_DRAW);
|
|
background.cbuff.itemSize = 4;
|
|
background.ibuff = gl.createBuffer();
|
|
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, background.ibuff);
|
|
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, background.index, gl.STREAM_DRAW);
|
|
|
|
// bind render method
|
|
background.render = renderBackground;
|
|
|
|
return background;
|
|
} catch(error) {
|
|
console.log(error);
|
|
}
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
function processWireframe(gl, obj, binaryArray, cursor) {
|
|
try {
|
|
var tmpArray, size, i;
|
|
|
|
// Extract points
|
|
obj.numberOfPoints = get4ByteNumber(binaryArray, cursor);
|
|
cursor += 4;
|
|
|
|
// Getting Points
|
|
size = obj.numberOfPoints * 4 * 3;
|
|
tmpArray = new Int8Array(size);
|
|
for(i=0; i < size; i++) {
|
|
tmpArray[i] = binaryArray[cursor++];
|
|
}
|
|
obj.points = new Float32Array(tmpArray.buffer);
|
|
|
|
// Generating Normals
|
|
size = obj.numberOfPoints * 3;
|
|
tmpArray = new Array(size);
|
|
for(i=0; i < size; i++) {
|
|
tmpArray[i] = 0.0;
|
|
}
|
|
obj.normals = new Float32Array(tmpArray);
|
|
|
|
// Getting Colors
|
|
size = obj.numberOfPoints * 4;
|
|
tmpArray = new Array(size);
|
|
for(i=0; i < size; i++) {
|
|
tmpArray[i] = binaryArray[cursor++]/255.0;;
|
|
}
|
|
obj.colors = new Float32Array(tmpArray);
|
|
|
|
// Extract the number of index
|
|
obj.numberOfIndex = get4ByteNumber(binaryArray, cursor);
|
|
cursor += 4;
|
|
|
|
// Getting Index
|
|
size = obj.numberOfIndex * 2;
|
|
tmpArray = new Int8Array(size);
|
|
for(i=0; i < size; i++) {
|
|
tmpArray[i] = binaryArray[cursor++];
|
|
}
|
|
obj.index = new Uint16Array(tmpArray.buffer);
|
|
|
|
// Getting Matrix
|
|
size = 16 * 4;
|
|
tmpArray = new Int8Array(size);
|
|
for(i=0; i < size; i++) {
|
|
tmpArray[i] = binaryArray[cursor++];
|
|
}
|
|
obj.matrix = new Float32Array(tmpArray.buffer);
|
|
|
|
// Creating Buffers
|
|
obj.lbuff = gl.createBuffer();
|
|
gl.bindBuffer(gl.ARRAY_BUFFER, obj.lbuff);
|
|
gl.bufferData(gl.ARRAY_BUFFER, obj.points, gl.STATIC_DRAW);
|
|
obj.lbuff.itemSize = 3;
|
|
|
|
obj.nbuff = gl.createBuffer();
|
|
gl.bindBuffer(gl.ARRAY_BUFFER, obj.nbuff);
|
|
gl.bufferData(gl.ARRAY_BUFFER, obj.normals, gl.STATIC_DRAW);
|
|
obj.nbuff.itemSize = 3;
|
|
|
|
obj.cbuff = gl.createBuffer();
|
|
gl.bindBuffer(gl.ARRAY_BUFFER, obj.cbuff);
|
|
gl.bufferData(gl.ARRAY_BUFFER, obj.colors, gl.STATIC_DRAW);
|
|
obj.cbuff.itemSize = 4;
|
|
|
|
obj.ibuff = gl.createBuffer();
|
|
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, obj.ibuff);
|
|
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, obj.index, gl.STREAM_DRAW);
|
|
|
|
// Bind render method
|
|
obj.render = renderLine;
|
|
} catch(error) {
|
|
console.log(error);
|
|
}
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
function processSurfaceMesh(gl, obj, binaryArray, cursor) {
|
|
try {
|
|
var tmpArray, size, i;
|
|
|
|
// Extract number of vertices
|
|
obj.numberOfVertices = get4ByteNumber(binaryArray, cursor);
|
|
cursor += 4;
|
|
|
|
// Getting Vertices
|
|
size = obj.numberOfVertices * 4 * 3;
|
|
tmpArray = new Int8Array(size);
|
|
for(i=0; i < size; i++) {
|
|
tmpArray[i] = binaryArray[cursor++];
|
|
}
|
|
obj.vertices = new Float32Array(tmpArray.buffer);
|
|
|
|
// Getting Normals
|
|
size = obj.numberOfVertices * 4 * 3;
|
|
tmpArray = new Int8Array(size);
|
|
for(i=0; i < size; i++) {
|
|
tmpArray[i] = binaryArray[cursor++];
|
|
}
|
|
obj.normals = new Float32Array(tmpArray.buffer);
|
|
|
|
// Getting Colors
|
|
tmpArray = [];
|
|
size = obj.numberOfVertices * 4;
|
|
for(i=0; i < size; i++) {
|
|
tmpArray[i] = binaryArray[cursor++] / 255.0;
|
|
}
|
|
obj.colors = new Float32Array(tmpArray);
|
|
|
|
// Get number of index
|
|
obj.numberOfIndex = get4ByteNumber(binaryArray, cursor);
|
|
cursor += 4;
|
|
|
|
// Getting Index
|
|
size = obj.numberOfIndex * 2;
|
|
tmpArray = new Int8Array(size);
|
|
for(i=0; i < size; i++) {
|
|
tmpArray[i] = binaryArray[cursor++];
|
|
}
|
|
obj.index = new Uint16Array(tmpArray.buffer);
|
|
|
|
// Getting Matrix
|
|
size = 16 * 4;
|
|
tmpArray = new Int8Array(size);
|
|
for(i=0; i < size; i++) {
|
|
tmpArray[i] = binaryArray[cursor++];
|
|
}
|
|
obj.matrix = new Float32Array(tmpArray.buffer);
|
|
|
|
// Getting TCoord
|
|
obj.tcoord = null;
|
|
|
|
// Create Buffers
|
|
obj.vbuff = gl.createBuffer();
|
|
gl.bindBuffer(gl.ARRAY_BUFFER, obj.vbuff);
|
|
gl.bufferData(gl.ARRAY_BUFFER, obj.vertices, gl.STATIC_DRAW);
|
|
obj.vbuff.itemSize = 3;
|
|
|
|
obj.nbuff = gl.createBuffer();
|
|
gl.bindBuffer(gl.ARRAY_BUFFER, obj.nbuff);
|
|
gl.bufferData(gl.ARRAY_BUFFER, obj.normals, gl.STATIC_DRAW);
|
|
obj.nbuff.itemSize = 3;
|
|
|
|
obj.cbuff = gl.createBuffer();
|
|
gl.bindBuffer(gl.ARRAY_BUFFER, obj.cbuff);
|
|
gl.bufferData(gl.ARRAY_BUFFER, obj.colors, gl.STATIC_DRAW);
|
|
obj.cbuff.itemSize = 4;
|
|
|
|
obj.ibuff = gl.createBuffer();
|
|
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, obj.ibuff);
|
|
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, obj.index, gl.STREAM_DRAW);
|
|
|
|
// Bind render method
|
|
obj.render = renderMesh;
|
|
} catch(error) {
|
|
console.log(error);
|
|
}
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
function processColorMap(gl, obj, nbColor, binaryArray, cursor) {
|
|
try {
|
|
var tmpArray, size, xrgb, i, c;
|
|
|
|
// Set number of colors
|
|
obj.numOfColors = nbColor;
|
|
|
|
// Getting Position
|
|
size = 2 * 4;
|
|
tmpArray = new Int8Array(size);
|
|
for(i=0; i < size; i++) {
|
|
tmpArray[i] = binaryArray[cursor++];
|
|
}
|
|
obj.position = new Float32Array(tmpArray.buffer);
|
|
|
|
// Getting Size
|
|
size = 2 * 4;
|
|
tmpArray = new Int8Array(2*4);
|
|
for(i=0; i < size; i++) {
|
|
tmpArray[i] = binaryArray[cursor++];
|
|
}
|
|
obj.size = new Float32Array(tmpArray.buffer);
|
|
|
|
//Getting Colors
|
|
obj.colors = [];
|
|
for(c=0; c < obj.numOfColors; c++){
|
|
tmpArray = new Int8Array(4);
|
|
for(i=0; i < 4; i++) {
|
|
tmpArray[i] = binaryArray[cursor++];
|
|
}
|
|
xrgb = [
|
|
new Float32Array(tmpArray.buffer)[0],
|
|
binaryArray[cursor++],
|
|
binaryArray[cursor++],
|
|
binaryArray[cursor++]
|
|
];
|
|
obj.colors[c] = xrgb;
|
|
}
|
|
|
|
obj.orientation = binaryArray[cursor++];
|
|
obj.numOfLabels = binaryArray[cursor++];
|
|
obj.title = "";
|
|
while(cursor < binaryArray.length) {
|
|
obj.title += String.fromCharCode(binaryArray[cursor++]);
|
|
}
|
|
|
|
// Bind render method
|
|
obj.render = renderColorMap;
|
|
} catch(error) {
|
|
console.log(error);
|
|
}
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
function processPointSet(gl, obj, binaryArray, cursor) {
|
|
try {
|
|
var tmpArray, size, i;
|
|
|
|
// Get number of points
|
|
obj.numberOfPoints = get4ByteNumber(binaryArray, cursor);
|
|
cursor += 4;
|
|
|
|
// Getting Points
|
|
size = obj.numberOfPoints * 4 * 3;
|
|
tmpArray = new Int8Array(size);
|
|
for(i=0; i < size; i++) {
|
|
tmpArray[i] = binaryArray[cursor++];
|
|
}
|
|
obj.points = new Float32Array(tmpArray.buffer);
|
|
|
|
// Getting Colors
|
|
size = obj.numberOfPoints * 4;
|
|
tmpArray = [];
|
|
for(i=0; i < size; i++) {
|
|
tmpArray[i] = binaryArray[cursor++]/255.0;
|
|
}
|
|
obj.colors = new Float32Array(tmpArray);
|
|
|
|
// Getting Matrix
|
|
size = 16 * 4;
|
|
tmpArray = new Int8Array(size);
|
|
for(i=0; i < size; i++) {
|
|
tmpArray[i] = binaryArray[cursor++]/255.0;
|
|
}
|
|
obj.matrix = new Float32Array(tmpArray.buffer);
|
|
|
|
// Creating Buffers
|
|
obj.pbuff = gl.createBuffer();
|
|
gl.bindBuffer(gl.ARRAY_BUFFER, obj.pbuff);
|
|
gl.bufferData(gl.ARRAY_BUFFER, obj.points, gl.STATIC_DRAW);
|
|
obj.pbuff.itemSize = 3;
|
|
|
|
obj.cbuff = gl.createBuffer();
|
|
gl.bindBuffer(gl.ARRAY_BUFFER, obj.cbuff);
|
|
gl.bufferData(gl.ARRAY_BUFFER, obj.colors, gl.STATIC_DRAW);
|
|
obj.cbuff.itemSize = 4;
|
|
|
|
// Bind render method
|
|
obj.render = renderPoints;
|
|
} catch(error) {
|
|
console.log(error);
|
|
}
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
function initializeObject(gl, obj) {
|
|
try {
|
|
var binaryArray = [], cursor = 0, size, type;
|
|
|
|
// Convert char to byte
|
|
for(var i in obj.data) {
|
|
binaryArray.push(obj.data.charCodeAt(i) & 0xff);
|
|
}
|
|
|
|
// Extract size (4 bytes)
|
|
size = get4ByteNumber(binaryArray, cursor);
|
|
cursor += 4;
|
|
|
|
// Extract object type
|
|
type = String.fromCharCode(binaryArray[cursor++]);
|
|
obj.type = type;
|
|
|
|
// Extract raw data
|
|
if (type == 'L'){
|
|
processWireframe(gl, obj, binaryArray, cursor);
|
|
} else if (type == 'M'){
|
|
processSurfaceMesh(gl, obj, binaryArray, cursor);
|
|
} else if (type == 'C'){
|
|
processColorMap(gl, obj, size, binaryArray, cursor);
|
|
} else if (type == 'P'){
|
|
processPointSet(gl, obj, binaryArray, cursor);
|
|
}
|
|
} catch(error) {
|
|
console.log(error);
|
|
}
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
// Geometry Delivery renderer - factory method
|
|
// ----------------------------------------------------------------------
|
|
|
|
function createGeometryDeliveryRenderer(domElement) {
|
|
var container = $(domElement),
|
|
options = $.extend({}, DEFAULT_OPTIONS, container.data('config')),
|
|
session = options.session,
|
|
divContainer = GLOBAL.document.createElement('div'),
|
|
canvas2D = GLOBAL.document.createElement('canvas'),
|
|
canvas3D = GLOBAL.document.createElement('canvas'),
|
|
ctx2d = canvas2D.getContext('2d'),
|
|
screenImage = null,
|
|
gl = canvas3D.getContext("experimental-webgl") || canvas3D.getContext("webgl"),
|
|
shaderProgram = gl.createProgram(),
|
|
pointShaderProgram = gl.createProgram(),
|
|
renderer = $(divContainer).addClass(FACTORY_KEY).css(RENDERER_CSS).append($(canvas2D).css(RENDERER_CSS).css(RENDERER_CSS_2D)).append($(canvas3D).css(RENDERER_CSS).css(RENDERER_CSS_3D)),
|
|
sceneJSON = null,
|
|
objectHandler = create3DObjectHandler(),
|
|
cameraLayerZero = null,
|
|
otherCamera = [],
|
|
mouseHandling = {
|
|
button: null,
|
|
lastX: 0,
|
|
lastY: 0
|
|
},
|
|
renderingContext = {
|
|
container: container,
|
|
gl: gl,
|
|
ctx2d: ctx2d,
|
|
shaderProgram: shaderProgram,
|
|
pointShaderProgram: pointShaderProgram
|
|
},
|
|
background = null,
|
|
m_numberOfPartsDownloaded = 0,
|
|
m_numberOfPartsToDownload = 0;
|
|
|
|
// Helper functions -------------------------------------------------
|
|
|
|
function fetchScene() {
|
|
container.trigger({
|
|
type: 'stats',
|
|
stat_id: 'webgl-fetch-scene',
|
|
stat_value: 0
|
|
});
|
|
session.call("viewport.webgl.metadata", [Number(options.view)]).then(function(data) {
|
|
sceneJSON = JSON.parse(data);
|
|
container.trigger({
|
|
type: 'stats',
|
|
stat_id: 'webgl-fetch-scene',
|
|
stat_value: 1
|
|
});
|
|
updateScene();
|
|
});
|
|
}
|
|
|
|
// ------------------------------------------------------------------
|
|
|
|
function fetchObject(sceneObject, part) {
|
|
try {
|
|
var viewId = Number(options.view),
|
|
newObject;
|
|
|
|
container.trigger({
|
|
type: 'stats',
|
|
stat_id: 'webgl-fetch-object',
|
|
stat_value: 0
|
|
});
|
|
session.call("viewport.webgl.data", [viewId, sceneObject.id, part]).then(function(data) {
|
|
try {
|
|
// decode base64
|
|
data = atob(data);
|
|
container.trigger({
|
|
type: 'stats',
|
|
stat_id: 'webgl-fetch-object',
|
|
stat_value: 1
|
|
});
|
|
|
|
newObject = {
|
|
md5: sceneObject.md5,
|
|
part: part,
|
|
vid: viewId,
|
|
id: sceneObject.id,
|
|
data: data,
|
|
hasTransparency: sceneObject.hasTransparency,
|
|
layer: sceneObject.layer,
|
|
render: function(){}
|
|
};
|
|
|
|
// Process object
|
|
initializeObject(gl, newObject);
|
|
|
|
// Register it for rendering
|
|
objectHandler.registerObject(newObject);
|
|
|
|
// Redraw the scene
|
|
drawScene(false);
|
|
} catch(error) {
|
|
console.log(error);
|
|
}
|
|
});
|
|
} catch(error) {
|
|
console.log(error);
|
|
}
|
|
}
|
|
|
|
// ------------------------------------------------------------------
|
|
|
|
function drawScene(saveScreenOnRender) {
|
|
try {
|
|
if (sceneJSON === null || cameraLayerZero === null){
|
|
return;
|
|
}
|
|
var localRenderer, localWidth, localHeight, localX, localY,
|
|
width = renderer.width(),
|
|
height = renderer.height(),
|
|
nbObjects = 0, layer, localCamera;
|
|
|
|
// Update frame rate
|
|
container.trigger({
|
|
type: 'stats',
|
|
stat_id: 'webgl-fps',
|
|
stat_value: 0
|
|
});
|
|
|
|
// Update viewport size
|
|
ctx2d.canvas.width = width;
|
|
ctx2d.canvas.height = height;
|
|
gl.canvas.width = width;
|
|
gl.canvas.height = height;
|
|
gl.viewportWidth = width;
|
|
gl.viewportHeight = height;
|
|
|
|
// Clear 3D context
|
|
gl.viewport(0, 0, gl.viewportWidth, gl.viewportHeight);
|
|
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
|
|
|
|
// Draw background
|
|
gl.disable(gl.DEPTH_TEST);
|
|
if(background != null) {
|
|
cameraLayerZero.enableOrtho();
|
|
background.render(renderingContext, cameraLayerZero);
|
|
cameraLayerZero.enablePerspective();
|
|
}
|
|
gl.enable(gl.DEPTH_TEST);
|
|
|
|
// Clear 2D overlay canvas
|
|
ctx2d.clearRect(0, 0, width, height);
|
|
|
|
// Render each layer on top of each other (Starting with the background one)
|
|
cameraLayerZero.setViewSize(width, height);
|
|
for(layer = sceneJSON.Renderers.length - 1; layer >= 0; layer--) {
|
|
localRenderer = sceneJSON.Renderers[layer];
|
|
localWidth = localRenderer.size[0] - localRenderer.origin[0];
|
|
localHeight = localRenderer.size[1] - localRenderer.origin[1];
|
|
localCamera = localRenderer.camera;
|
|
|
|
// Convert % to pixel based
|
|
localWidth *= width;
|
|
localHeight *= height;
|
|
localX = localRenderer.origin[0] * width;
|
|
localY = localRenderer.origin[1] * height;
|
|
localX = (localX < 0) ? 0 : localX;
|
|
localY = (localY < 0) ? 0 : localY;
|
|
|
|
// Update renderer camera aspect ratio
|
|
localCamera.setViewSize(localWidth, localHeight); // FIXME maybe use the local width/height
|
|
|
|
// Setup viewport
|
|
gl.viewport(localX, localY, localWidth, localHeight);
|
|
|
|
// Render non-transparent objects for the current layer
|
|
nbObjects += objectHandler.renderSolid(layer, renderingContext, localCamera);
|
|
|
|
// Now render transparent objects
|
|
gl.enable(gl.BLEND); //Enable transparency
|
|
gl.enable(gl.POLYGON_OFFSET_FILL); //Avoid zfighting
|
|
gl.polygonOffset(-1.0, -1.0);
|
|
|
|
nbObjects += objectHandler.renderTransparent(layer, renderingContext, localCamera);
|
|
|
|
gl.disable(gl.POLYGON_OFFSET_FILL);
|
|
gl.disable(gl.BLEND);
|
|
}
|
|
|
|
if (saveScreenOnRender === true) {
|
|
screenImage = renderingContext.gl.canvas.toDataURL();
|
|
}
|
|
|
|
// Update frame rate
|
|
container.trigger({
|
|
type: 'stats',
|
|
stat_id: 'webgl-fps',
|
|
stat_value: 1
|
|
});
|
|
|
|
container.trigger({
|
|
type: 'stats',
|
|
stat_id: 'webgl-nb-objects',
|
|
stat_value: nbObjects
|
|
});
|
|
} catch(error) {
|
|
console.log(error);
|
|
}
|
|
container.trigger('done');
|
|
}
|
|
|
|
// ------------------------------------------------------------------
|
|
|
|
function pushCameraState() {
|
|
if(cameraLayerZero != null) {
|
|
var fp_ = cameraLayerZero.getFocalPoint(),
|
|
up_ = cameraLayerZero.getViewUp(),
|
|
pos_ = cameraLayerZero.getPosition(),
|
|
fp = [fp_[0], fp_[1], fp_[2]],
|
|
up = [up_[0], up_[1], up_[2]],
|
|
pos = [pos_[0], pos_[1], pos_[2]];
|
|
session.call("viewport.camera.update", [Number(options.view), fp, up, pos]);
|
|
}
|
|
}
|
|
|
|
// ------------------------------------------------------------------
|
|
|
|
function updateScene() {
|
|
try{
|
|
if(sceneJSON === null || typeof(sceneJSON) === "undefined") {
|
|
return;
|
|
}
|
|
|
|
// Local variables
|
|
var bgColor1 = [0,0,0], bgColor2 = [0,0,0], renderer;
|
|
|
|
// Create camera for each renderer + handle Background (Layer 0)
|
|
otherCamera = [];
|
|
for(var idx = 0; idx < sceneJSON.Renderers.length; idx++) {
|
|
renderer = sceneJSON.Renderers[idx];
|
|
renderer.camera = createCamera();
|
|
renderer.camera.setCenterOfRotation(sceneJSON.Center);
|
|
renderer.camera.setCameraParameters( renderer.LookAt[0],
|
|
[renderer.LookAt[7], renderer.LookAt[8], renderer.LookAt[9]],
|
|
[renderer.LookAt[1], renderer.LookAt[2], renderer.LookAt[3]],
|
|
[renderer.LookAt[4], renderer.LookAt[5], renderer.LookAt[6]]);
|
|
|
|
// Custom handling of layer 0
|
|
if(renderer.layer === 0) {
|
|
cameraLayerZero = renderer.camera;
|
|
bgColor1 = bgColor2 = renderer.Background1;
|
|
if(typeof(renderer.Background2) != "undefined") {
|
|
bgColor2 = renderer.Background2;
|
|
}
|
|
} else {
|
|
otherCamera.push(renderer.camera);
|
|
}
|
|
}
|
|
background = buildBackground(gl, bgColor1, bgColor2);
|
|
|
|
// Update the list of object to render
|
|
objectHandler.updateDisplayList(sceneJSON);
|
|
|
|
// Fetch the object that we are missing
|
|
objectHandler.fetchMissingObjects(fetchObject);
|
|
|
|
// Draw scene
|
|
drawScene(false);
|
|
} catch(error) {
|
|
console.log(error);
|
|
}
|
|
}
|
|
|
|
// ------------------------------------------------------------------
|
|
|
|
function handleDownloadProgressEvent(event) {
|
|
var status = event.status;
|
|
|
|
function updateProgressBar(element, percentProgress) {
|
|
var progressString = percentProgress + '%';
|
|
element.attr('aria-valuenow', percentProgress)
|
|
.css('width', progressString);
|
|
//.html(progressString);
|
|
}
|
|
|
|
if (status === 'create') {
|
|
var initialMessage = "Downloading metadata for all timesteps",
|
|
html = PROGRESS_BAR_TEMPLATE.replace(/MESSAGE/, initialMessage),
|
|
progressElt = $(html);
|
|
container.append(progressElt);
|
|
} else if (status === 'update') {
|
|
var progressType = event.progressType,
|
|
pbElt = $('.progress-meter');
|
|
if (progressType === 'retrieved-metadata') {
|
|
$('.progress-message-span').text('Metadata retrieved, downloading objects');
|
|
pbElt.removeClass('progress-bar-striped active');
|
|
updateProgressBar(pbElt, 0);
|
|
} else {
|
|
var numPartsThisSha = event.numParts;
|
|
m_numberOfPartsDownloaded += numPartsThisSha;
|
|
var numberRemaining = m_numberOfPartsToDownload - m_numberOfPartsDownloaded;
|
|
var percent = ((m_numberOfPartsDownloaded / m_numberOfPartsToDownload) * 100).toFixed(0);
|
|
if (numberRemaining <= 0) {
|
|
$('.download-progressbar-container').remove();
|
|
} else {
|
|
$('.progress-message-span').text('Downloading objects');
|
|
updateProgressBar(pbElt, percent);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// ------------------------------------------------------------------
|
|
|
|
function downloadAllTimesteps() {
|
|
container.trigger({
|
|
type: 'downloadProgress',
|
|
status: 'create'
|
|
});
|
|
|
|
session.call('viewport.webgl.metadata.alltimesteps', []).then(function(result){
|
|
if (result.hasOwnProperty('success') && result.success === true) {
|
|
var metaDataList = result.metaDataList;
|
|
|
|
container.trigger({
|
|
type: 'downloadProgress',
|
|
status: 'update',
|
|
progressType: 'retrieved-metadata'
|
|
});
|
|
|
|
// For progress events, I want to first know how many items to retrieve
|
|
m_numberOfPartsToDownload = 0;
|
|
for (var sha in metaDataList) {
|
|
if (metaDataList.hasOwnProperty(sha)) {
|
|
m_numberOfPartsToDownload += metaDataList[sha].numParts;
|
|
}
|
|
}
|
|
|
|
m_numberOfPartsDownloaded = 0;
|
|
|
|
setTimeout(function() {
|
|
|
|
// Now go through and download the heavy data for anythin we don't already have
|
|
for (var sha in metaDataList) {
|
|
if (metaDataList.hasOwnProperty(sha)) {
|
|
var numParts = metaDataList[sha].numParts,
|
|
objId = metaDataList[sha].id,
|
|
alreadyCached = true;
|
|
// Before I go and fetch all the parts for this object, make sure
|
|
// I don't already have them cached
|
|
for (var i = 0; i < numParts; i+=1) {
|
|
var obj = {
|
|
'id': objId,
|
|
'md5': sha,
|
|
'part': i + 1
|
|
};
|
|
if (!objectHandler.isObjectRegistered(obj)) {
|
|
alreadyCached = false;
|
|
break;
|
|
}
|
|
}
|
|
if (alreadyCached === false) {
|
|
fetchCachedObject(sha);
|
|
} else {
|
|
container.trigger({
|
|
type: 'downloadProgress',
|
|
status: 'update',
|
|
numParts: numParts
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
}, 500);
|
|
}
|
|
}, function(metaDataError) {
|
|
console.log("Error retrieving metadata for all timesteps");
|
|
console.log(metaDataError);
|
|
});
|
|
}
|
|
|
|
// ------------------------------------------------------------------
|
|
|
|
function fetchCachedObject(sha) {
|
|
var viewId = Number(options.view);
|
|
|
|
session.call('viewport.webgl.cached.data', [sha]).then(function(result) {
|
|
if (result.success === false) {
|
|
console.log("Fetching cached data for " + sha + " failed, reason:");
|
|
consolelog(result.reason);
|
|
return;
|
|
}
|
|
var dataObject = result.data;
|
|
if (dataObject.hasOwnProperty('partsList')) {
|
|
for (var dIdx = 0; dIdx < dataObject.partsList.length; dIdx += 1) {
|
|
// Create a complete scene part object and cache it
|
|
var newObject = {
|
|
md5: dataObject.md5,
|
|
part: dIdx + 1,
|
|
vid: viewId,
|
|
id: dataObject.id,
|
|
data: atob(dataObject.partsList[dIdx]),
|
|
hasTransparency: dataObject.transparency,
|
|
layer: dataObject.layer,
|
|
render: function(){}
|
|
};
|
|
|
|
// Process object
|
|
initializeObject(gl, newObject);
|
|
|
|
// Register it for rendering
|
|
objectHandler.registerObject(newObject);
|
|
}
|
|
|
|
container.trigger({
|
|
type: 'downloadProgress',
|
|
status: 'update',
|
|
numParts: dataObject.partsList.length
|
|
});
|
|
}
|
|
}, function(err) {
|
|
console.log('viewport.webgl.cached.data rpc method failed');
|
|
console.log(err);
|
|
});
|
|
}
|
|
|
|
// ------------------------------------------------------------------
|
|
// Add renderer into the DOM
|
|
container.append(renderer);
|
|
|
|
// ------------------------------------------------------------------
|
|
// Add viewport listener
|
|
container.bind('invalidateScene', function() {
|
|
if(renderer.hasClass('active')){
|
|
fetchScene();
|
|
}
|
|
}).bind('render', function(){
|
|
if(renderer.hasClass('active')){
|
|
drawScene(false);
|
|
}
|
|
}).bind('resetViewId', function(e){
|
|
options.view = -1;
|
|
}).bind('downloadAllTimesteps', function(event){
|
|
if(renderer.hasClass('active')){
|
|
downloadAllTimesteps();
|
|
}
|
|
}).bind('downloadProgress', function(event){
|
|
if(renderer.hasClass('active')){
|
|
handleDownloadProgressEvent(event);
|
|
}
|
|
}).bind('clearCache', function(event){
|
|
if(renderer.hasClass('active')){
|
|
objectHandler.clearCache();
|
|
container.trigger('invalidateScene');
|
|
}
|
|
}).bind('captureRenderedImage', function(e){
|
|
if (renderer.hasClass('active')) {
|
|
drawScene(true);
|
|
$(container).parent().trigger({
|
|
type: 'captured-screenshot-ready',
|
|
imageData: screenImage
|
|
});
|
|
}
|
|
}).bind('mouse', function(event){
|
|
if(renderer.hasClass('active')){
|
|
event.preventDefault();
|
|
|
|
if(event.action === 'down') {
|
|
mouseHandling.button = event.current_button;
|
|
mouseHandling.lastX = event.pageX;
|
|
mouseHandling.lastY = event.pageY;
|
|
} else if (event.action === 'up') {
|
|
mouseHandling.button = null;
|
|
} else if (event.action === 'move' && mouseHandling.button != null && cameraLayerZero != null) {
|
|
var newMouseX= event.pageX, newMouseY = event.pageY,
|
|
mouseDX = mouseHandling.lastX - newMouseX,
|
|
mouseDY = mouseHandling.lastY - newMouseY,
|
|
lastMouseX = mouseHandling.lastX,
|
|
lastMouseY = mouseHandling.lastY,
|
|
panD, zTrans,
|
|
focalPoint, focusWorldPt, focusDisplayPt,
|
|
displayPt1, displayPt2,
|
|
worldPt1, worldPt2,
|
|
width = renderer.width(),
|
|
height = renderer.height();
|
|
|
|
mouseHandling.lastX = newMouseX;
|
|
mouseHandling.lastY = newMouseY;
|
|
|
|
if (mouseHandling.button === 1) {
|
|
cameraLayerZero.rotate(mouseDX, mouseDY);
|
|
for(var i in otherCamera) {
|
|
otherCamera[i].rotate(mouseDX, mouseDY);
|
|
}
|
|
} else if (mouseHandling.button === 2) {
|
|
panD = cameraLayerZero.calculatePanDeltas(
|
|
width, height, newMouseX, newMouseY,
|
|
lastMouseX, lastMouseY);
|
|
cameraLayerZero.pan(-panD[0], -panD[1], -panD[2] );
|
|
} else if (mouseHandling.button === 3) {
|
|
zTrans = (newMouseY - lastMouseY) / height;
|
|
|
|
// Calculate zoom scale here
|
|
if (zTrans > 0) {
|
|
cameraLayerZero.zoom(1 - Math.abs(zTrans));
|
|
} else {
|
|
cameraLayerZero.zoom(1 + Math.abs(zTrans));
|
|
}
|
|
}
|
|
|
|
drawScene(false);
|
|
pushCameraState();
|
|
}
|
|
}
|
|
}).bind('active', function(){
|
|
if(renderer.hasClass('active')){
|
|
// Setup GL context
|
|
gl.viewportWidth = renderer.width();
|
|
gl.viewportHeight = renderer.height();
|
|
|
|
gl.clearColor(0.0, 0.0, 0.0, 1.0);
|
|
gl.clearDepth(1.0);
|
|
gl.enable(gl.DEPTH_TEST);
|
|
gl.depthFunc(gl.LEQUAL);
|
|
|
|
gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
|
|
|
|
initializeShader(gl, shaderProgram, pointShaderProgram);
|
|
|
|
// Ready to render data
|
|
fetchScene();
|
|
drawScene(false);
|
|
}
|
|
});
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
// Camera object
|
|
// ----------------------------------------------------------------------
|
|
|
|
function createCamera() {
|
|
var viewAngle = 30.0,
|
|
centerOfRotation = vec3.set(vec3.create(), 0.0,0.0,-1.0),
|
|
aspect = 1.0,
|
|
left = -1.0,
|
|
right = 1.0,
|
|
bottom = -1.0,
|
|
top = 1.0,
|
|
near = 0.01,
|
|
far = 10000.0,
|
|
position = vec4.set(vec4.create(), 0.0, 0.0, 0.0, 1.0),
|
|
focalPoint = vec4.set(vec4.create(), 0.0, 0.0, -1.0, 1.0),
|
|
viewUp = vec4.set(vec4.create(), 0.0, 1.0, 0.0, 0.0),
|
|
rightDir = vec4.set(vec4.create(), 1.0, 0.0, 0.0, 0.0),
|
|
projectionMatrix = mat4.create(),
|
|
modelViewMatrix = mat4.create(),
|
|
perspective = true,
|
|
width = 100,
|
|
height = 100,
|
|
modified = true;
|
|
|
|
directionOfProjection = vec4.fromValues(0.0, 0.0, -1.0, 0.0),
|
|
|
|
// Initialize to identity (just to be safe)
|
|
mat4.identity(modelViewMatrix);
|
|
mat4.identity(projectionMatrix);
|
|
|
|
function computeOrthogonalAxes() {
|
|
computeDirectionOfProjection();
|
|
vec3.cross(rightDir, directionOfProjection, viewUp);
|
|
vec3.normalize(rightDir, rightDir);
|
|
modified = true;
|
|
};
|
|
function computeDirectionOfProjection() {
|
|
vec3.subtract(directionOfProjection, focalPoint, position);
|
|
vec3.normalize(directionOfProjection, directionOfProjection);
|
|
modified = true;
|
|
};
|
|
|
|
function worldToDisplay(worldPt, width, height) {
|
|
var viewProjectionMatrix = mat4.create();
|
|
mat4.multiply(viewProjectionMatrix, projectionMatrix, modelViewMatrix),
|
|
result = vec4.create();
|
|
|
|
// Transform world to clipping coordinates
|
|
var clipPt = vec4.create();
|
|
vec4.transformMat4(clipPt, worldPt, viewProjectionMatrix);
|
|
|
|
if (clipPt[3] !== 0.0) {
|
|
clipPt[0] = clipPt[0] / clipPt[3];
|
|
clipPt[1] = clipPt[1] / clipPt[3];
|
|
clipPt[2] = clipPt[2] / clipPt[3];
|
|
clipPt[3] = 1.0;
|
|
}
|
|
|
|
var winX = Math.round((((clipPt[0]) + 1) / 2.0) * width);
|
|
// / We calculate -point3D.getY() because the screen Y axis is
|
|
// / oriented top->down
|
|
var winY = Math.round(((1 - clipPt[1]) / 2.0) * height);
|
|
var winZ = clipPt[2];
|
|
var winW = clipPt[3];
|
|
|
|
vec4.set(result, winX, winY, winZ, winW);
|
|
return result;
|
|
};
|
|
|
|
function displayToWorld(displayPt, width, height) {
|
|
var x = (2.0 * displayPt[0] / width) - 1;
|
|
var y = -(2.0 * displayPt[1] / height) + 1;
|
|
var z = displayPt[2];
|
|
|
|
var viewProjectionInverse = mat4.create();
|
|
mat4.multiply(viewProjectionInverse, projectionMatrix, modelViewMatrix);
|
|
mat4.invert(viewProjectionInverse, viewProjectionInverse);
|
|
|
|
var worldPt = vec4.create();
|
|
vec4.set(worldPt, x, y, z, 1);
|
|
vec4.transformMat4(worldPt, worldPt, viewProjectionInverse);
|
|
|
|
if (worldPt[3] !== 0.0) {
|
|
worldPt[0] = worldPt[0] / worldPt[3];
|
|
worldPt[1] = worldPt[1] / worldPt[3];
|
|
worldPt[2] = worldPt[2] / worldPt[3];
|
|
worldPt[3] = 1.0;
|
|
}
|
|
|
|
return worldPt;
|
|
};
|
|
|
|
return {
|
|
calculatePanDeltas: function(
|
|
width, height, newMouseX, newMouseY, lastMouseX, lastMouseY) {
|
|
|
|
var dx,dy,dz,
|
|
focusDisplayPt, displayPt1, displayPt2,
|
|
worldPt1, worldPt2,
|
|
focusWorldPt = vec4.fromValues(
|
|
focalPoint[0], focalPoint[1], focalPoint[2], 1);
|
|
|
|
focusDisplayPt =
|
|
worldToDisplay(
|
|
focusWorldPt, width, height);
|
|
|
|
displayPt1 = vec4.fromValues(
|
|
newMouseX, newMouseY, focusDisplayPt[2], 1.0);
|
|
|
|
displayPt2 = vec4.fromValues(
|
|
lastMouseX, lastMouseY, focusDisplayPt[2], 1.0);
|
|
|
|
worldPt1 = displayToWorld(
|
|
displayPt1, width, height);
|
|
|
|
worldPt2 = displayToWorld(
|
|
displayPt2, width, height);
|
|
|
|
dx = worldPt1[0] - worldPt2[0];
|
|
dy = worldPt1[1] - worldPt2[1];
|
|
dz = worldPt1[2] - worldPt2[2];
|
|
|
|
return [dx,dy,dz];
|
|
},
|
|
getFocalPoint: function() {
|
|
return focalPoint;
|
|
},
|
|
getPosition: function() {
|
|
return position;
|
|
},
|
|
getViewUp: function() {
|
|
return viewUp;
|
|
},
|
|
setCenterOfRotation: function(center) {
|
|
//console.log('[CAMERA] centerOfRotation ' + center);
|
|
vec3.set(centerOfRotation, center[0], center[1], center[2]);
|
|
},
|
|
setCameraParameters : function(angle, pos, focal, up) {
|
|
//console.log("[CAMERA] angle: " + angle + " position: " + pos + " focal: " + focal + " up: " + up );
|
|
viewAngle = angle * Math.PI / 180;
|
|
vec4.set(position, pos[0], pos[1], pos[2], 1.0);
|
|
vec4.set(focalPoint, focal[0], focal[1], focal[2], 1.0);
|
|
vec4.set(viewUp, up[0], up[1], up[2], 0.0);
|
|
modified = true;
|
|
},
|
|
setViewSize : function(w, h) {
|
|
//console.log('[CAMERA] width: ' + w + ' height: ' + h);
|
|
aspect = w/h;
|
|
width = w;
|
|
height = h;
|
|
modified = true;
|
|
},
|
|
enableOrtho : function() {
|
|
perspective = false;
|
|
modified = true;
|
|
},
|
|
enablePerspective : function() {
|
|
perspective = true;
|
|
modified = true;
|
|
},
|
|
zoom : function(d) {
|
|
if (d === 0) {
|
|
return;
|
|
}
|
|
|
|
d = d * vec3.distance(focalPoint, position);
|
|
position[0] = focalPoint[0] - d * directionOfProjection[0];
|
|
position[1] = focalPoint[1] - d * directionOfProjection[1];
|
|
position[2] = focalPoint[2] - d * directionOfProjection[2];
|
|
|
|
modified = true;
|
|
this.getCameraMatrices();
|
|
},
|
|
pan : function(dx, dy, dz) {
|
|
position[0] += dx;
|
|
position[1] += dy;
|
|
position[2] += dz;
|
|
|
|
focalPoint[0] += dx;
|
|
focalPoint[1] += dy;
|
|
focalPoint[2] += dz;
|
|
computeOrthogonalAxes();
|
|
|
|
modified = true;
|
|
},
|
|
rotate : function(dx, dy) {
|
|
dx = 0.5 * dx * (Math.PI / 180.0);
|
|
dy = 0.5 * dy * (Math.PI / 180.0);
|
|
|
|
var mat = mat4.create(),
|
|
inverseCenterOfRotation = new vec3.create();
|
|
|
|
mat4.identity(mat);
|
|
|
|
inverseCenterOfRotation[0] = -centerOfRotation[0];
|
|
inverseCenterOfRotation[1] = -centerOfRotation[1];
|
|
inverseCenterOfRotation[2] = -centerOfRotation[2];
|
|
|
|
mat4.translate(mat, mat, centerOfRotation);
|
|
mat4.rotate(mat, mat, dx, viewUp);
|
|
mat4.rotate(mat, mat, dy, rightDir);
|
|
mat4.translate(mat, mat, inverseCenterOfRotation);
|
|
|
|
vec3.transformMat4(position, position, mat);
|
|
vec3.transformMat4(focalPoint, focalPoint, mat);
|
|
|
|
// Update viewup vector
|
|
vec4.transformMat4(viewUp, viewUp, mat);
|
|
vec4.normalize(viewUp, viewUp);
|
|
|
|
computeOrthogonalAxes();
|
|
modified = true;
|
|
this.getCameraMatrices();
|
|
},
|
|
getCameraMatrices : function() {
|
|
if (modified) {
|
|
// Compute project matrix
|
|
if (perspective) {
|
|
mat4.perspective(projectionMatrix, viewAngle, aspect, near, far);
|
|
} else {
|
|
mat4.ortho(projectionMatrix, left, right, bottom, top, near, far);
|
|
}
|
|
|
|
// Compute modelview matrix
|
|
computeOrthogonalAxes();
|
|
mat4.lookAt(modelViewMatrix, position, focalPoint, viewUp);
|
|
modified = false;
|
|
};
|
|
|
|
return [projectionMatrix, modelViewMatrix];
|
|
}
|
|
|
|
};
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
// Init vtkWeb module if needed
|
|
// ----------------------------------------------------------------------
|
|
if (GLOBAL.hasOwnProperty("vtkWeb")) {
|
|
module = GLOBAL.vtkWeb || {};
|
|
} else {
|
|
GLOBAL.vtkWeb = module;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
// Extend the viewport factory - ONLY IF WEBGL IS SUPPORTED
|
|
// ----------------------------------------------------------------------
|
|
try {
|
|
if (GLOBAL.WebGLRenderingContext && typeof(vec3) != "undefined" && typeof(mat4) != "undefined") {
|
|
var canvas = GLOBAL.document.createElement('canvas'),
|
|
gl = canvas.getContext("webgl") || canvas.getContext("experimental-webgl");
|
|
if(gl) {
|
|
// WebGL is supported
|
|
if(!module.hasOwnProperty('ViewportFactory')) {
|
|
module['ViewportFactory'] = {};
|
|
}
|
|
module.ViewportFactory[FACTORY_KEY] = FACTORY;
|
|
}
|
|
}
|
|
} catch(exception) {
|
|
// nothing to do
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
// Local module registration
|
|
// ----------------------------------------------------------------------
|
|
try {
|
|
// Tests for presence of jQuery and glMatrix, then registers this module
|
|
if ($ !== undefined && module.ViewportFactory[FACTORY_KEY] !== undefined) {
|
|
module.registerModule('vtkweb-viewport-webgl');
|
|
}
|
|
} catch(err) {
|
|
console.error('jQuery or glMatrix is missing or browser does not support WebGL: ' + err.message);
|
|
}
|
|
|
|
}(window, jQuery));
|