/*
mki3d_view version 4
THIS SCRIPT SHOULD BE SAVED IN THE FOLDER WITH HTML PAGES EXPORTED
FROM THE MKI 3D RAPID MODELLER ( https://github.com/mki1967/mki3d ).
IT IS USED BY THE EXPORTED PAGES FOR RENDERING AND USER INTERACTION.
Copyright (C) 2015 Marcin Kik mki1967@gmail.com
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
*/
/** from mki3d_constants.js **/
/* Constants used by mki3d */
/* sizes of elementary components */
const MKI3D_VERTEX_POSITION_SIZE = 3; // 3 floats: x,y,z
const MKI3D_VERTEX_COLOR_SIZE = 3; // 3 floats: r,g,b
/* upper limit for absoloute value of clipping coordinate */
const MKI3D_MAX_CLIP_ABS = 1e+20;
/* upper and lower bounds on the scale */
const MKI3D_MAX_SCALE = 1024.0;
const MKI3D_MIN_SCALE = 1/ MKI3D_MAX_SCALE;
/* Initial view parameters */
const MKI3D_SCREEN_Z = 60;
/* Initial projection parameters */
const MKI3D_PROJECTION_Z_NEAR = 0.25;
const MKI3D_PROJECTION_Z_FAR = 300;
const MKI3D_PROJECTION_ZOOM_Y = 4.0;
/** from mki3d.js **/
var mki3d = {};
window.onload= function(){
mki3d.html.initObjects();
mki3d.gl.initGL( mki3d.html.canvas );
mki3d.dataReset();
window.onresize= mki3d.callback.onWindowResize;
mki3d.loadExported();
mki3d.callback.onWindowResize();
mki3d.redraw();
}
/** from mki3d_algebra.js **/
/** vector and matrix operations **/
/** Vector is an array [x,y,z] **/
mki3d.vectorClone= function (v){
return [v[0],v[1],v[2]];
};
/* lexicographic ordering */
mki3d.vectorCompare = function( v1, v2 ){
var cmp=0;
cmp= v1[0]-v2[0];
if(cmp != 0) return cmp;
cmp= v1[1]-v2[1];
if(cmp != 0) return cmp;
cmp= v1[2]-v2[2];
return cmp;
};
mki3d.vectorProductOrdered = function( v, w){
return v[0]<=w[0] && v[1]<=w[1] && v[2]<=w[2];
}
/* Set elements of the existing vector v */
mki3d.vectorSet = function(v, x,y,z ) {
v[0]=x;
v[1]=y;
v[2]=z;
};
mki3d.vectorMove = function(v, dx, dy, dz ) {
v[0]+= dx;
v[1]+= dy;
v[2]+= dz;
};
mki3d.vectorScale = function(v, sx, sy, sz ) {
v[0]*= sx;
v[1]*= sy;
v[2]*= sz;
};
mki3d.scalarProduct= function( v, w ) {
return v[0]*w[0]+v[1]*w[1]+v[2]*w[2];
};
mki3d.distanceSquare = function(v, w) {
var a=[ v[0]-w[0], v[1]-w[1], v[2]-w[2] ];
return mki3d.scalarProduct( a,a);
}
mki3d.vectorProduct= function( a, b ) { // cross product
return [ a[1]*b[2]-a[2]*b[1],
a[2]*b[0]-a[0]*b[2],
a[0]*b[1]-a[1]*b[0] ];
};
mki3d.vectorLength = function (a) {
return Math.sqrt(mki3d.scalarProduct(a,a));
};
mki3d.vectorNormalized = function (v) {
var len= mki3d.vectorLength(v);
if(len==0) return [0,0,0]; // normalized zero vector :-(
var vn= mki3d.vectorClone(v);
var s =1/len;
mki3d.vectorScale(vn, s,s,s);
return vn;
};
mki3d.normalToPlane = function ( a, b, c ) { // a,b,c are three points of the plane
var v1 = [ b[0]-a[0], b[1]-a[1], b[2]-a[2] ];
var v2 = [ c[0]-a[0], c[1]-a[1], c[2]-a[2] ];
return mki3d.vectorNormalized( mki3d.vectorProduct( v1, v2 ) );
};
/* Matrix is an array of three vectors (rows of the matrix) */
/* returns new Identity matrix */
mki3d.newIdMatrix = function () {
return [ [ 1, 0, 0],
[ 0, 1, 0],
[ 0, 0, 1] ];
};
mki3d.matrixClone = function( m ) {
return [ mki3d.vectorClone( m[0] ),
mki3d.vectorClone( m[1] ),
mki3d.vectorClone( m[2] ) ];
};
mki3d.matrixScale = function( m, s ) {
mki3d.vectorScale( m[0], s,s,s );
mki3d.vectorScale( m[1], s,s,s );
mki3d.vectorScale( m[2], s,s,s );
};
mki3d.matrixDeterminant = function(m)
{
return m[0][2]*( m[1][0]*m[2][1]-m[2][0]*m[1][1] )
-m[1][2]*( m[0][0]*m[2][1]-m[0][1]*m[2][0] )
+m[2][2]*( m[0][0]*m[1][1]-m[0][1]*m[1][0] );
}
mki3d.matrixInverse= function( m ){
var det = mki3d.matrixDeterminant(m);
if(det == 0) {
// console.log(m);
Throw ("mki3d.matrixInverse: non-invertible matrix");
}
var s= 1/det;
var r= [
mki3d.vectorProduct( mki3d.matrixColumn( m, 1), mki3d.matrixColumn( m, 2) ),
mki3d.vectorProduct( mki3d.matrixColumn( m, 2), mki3d.matrixColumn( m, 0) ),
mki3d.vectorProduct( mki3d.matrixColumn( m, 0), mki3d.matrixColumn( m, 1) )
];
mki3d.vectorScale( r[0], s, s, s );
mki3d.vectorScale( r[1], s, s,s );
mki3d.vectorScale( r[2], s, s, s );
return r;
}
mki3d.matrixColumn = function ( matrix, i ){
return [ matrix[0][i], matrix[1][i], matrix[2][i] ];
};
mki3d.matrixTransposed = function ( matrix ){
return [ mki3d.matrixColumn(matrix, 0),
mki3d.matrixColumn(matrix, 1),
mki3d.matrixColumn(matrix, 2) ];
};
mki3d.matrixVectorProduct = function ( m, v ) {
var sp = mki3d.scalarProduct;
return [ sp(m[0],v), sp(m[1],v), sp(m[2],v)];
};
mki3d.matrixProduct = function( m1, m2){
var sp = mki3d.scalarProduct;
var col = mki3d.matrixColumn;
return [ [ sp(m1[0], col(m2, 0)) , sp(m1[0], col(m2, 1)), sp(m1[0], col(m2, 2)) ],
[ sp(m1[1], col(m2, 0)) , sp(m1[1], col(m2, 1)), sp(m1[1], col(m2, 2)) ],
[ sp(m1[2], col(m2, 0)) , sp(m1[2], col(m2, 1)), sp(m1[2], col(m2, 2)) ] ];
};
mki3d.matrixRotatedXY= function(matrix, alpha ){
var c = Math.cos( alpha );
var s = Math.sin( alpha );
var rot = [ [ c, -s, 0 ],
[ s, c, 0 ],
[ 0, 0, 1 ] ];
return mki3d.matrixProduct( rot, matrix );
};
mki3d.matrixRotatedXZ= function(matrix, alpha ){
var c = Math.cos( alpha );
var s = Math.sin( alpha );
var rot = [ [ c, 0, -s ],
[ 0, 1, 0 ],
[ s, 0, c ] ];
return mki3d.matrixProduct( rot, matrix );
};
mki3d.matrixRotatedYZ= function(matrix, alpha ){
var c = Math.cos( alpha );
var s = Math.sin( alpha );
var rot = [ [ 1, 0, 0 ],
[ 0, c, -s ],
[ 0, s, c ] ];
return mki3d.matrixProduct( rot, matrix );
};
/** 4-dimmensional vectors and matrices **/
mki3d.scalarProduct4= function( v, w ) {
return v[0]*w[0]+v[1]*w[1]+v[2]*w[2]+v[3]*w[3];
};
/* extend 3d matrix to 4d matrix */
mki3d.matrix3to4= function( m ) {
return [
[ m[0][0], m[0][1], m[0][2], 0 ],
[ m[1][0], m[1][1], m[1][2], 0 ],
[ m[2][0], m[2][1], m[2][2], 0 ],
[ 0, 0, 0, 1 ]
];
};
mki3d.matrix4Column = function ( matrix, i ){
return [ matrix[0][i], matrix[1][i], matrix[2][i], matrix[3][i] ];
};
mki3d.matrix4Product = function( m1, m2){
var sp = mki3d.scalarProduct4;
var col = mki3d.matrix4Column;
return [
[ sp(m1[0], col(m2, 0)) , sp(m1[0], col(m2, 1)), sp(m1[0], col(m2, 2)), sp(m1[0], col(m2, 3)) ],
[ sp(m1[1], col(m2, 0)) , sp(m1[1], col(m2, 1)), sp(m1[1], col(m2, 2)), sp(m1[1], col(m2, 3)) ],
[ sp(m1[2], col(m2, 0)) , sp(m1[2], col(m2, 1)), sp(m1[2], col(m2, 2)), sp(m1[2], col(m2, 3)) ],
[ sp(m1[3], col(m2, 0)) , sp(m1[3], col(m2, 1)), sp(m1[3], col(m2, 2)), sp(m1[3], col(m2, 3)) ]
];
};
/** from mki3d_stereo.js **/
mki3d.stereoProjection= function(eyeShift){
var d=eyeShift;
var shift1 = [
[ 1, 0, 0, -d],
[ 0, 1, 0, 0],
[ 0, 0, 1, 0],
[ 0, 0, 0, 1]
];
var screenZ = mki3d.data.view.screenShift[2];
var projection = mki3d.data.projection;
var gl = mki3d.gl.context;
var dx = d* projection.zoomY / screenZ * gl.viewportHeight/gl.viewportWidth;
var shift2 = [
[ 1, 0, 0, dx],
[ 0, 1, 0, 0],
[ 0, 0, 1, 0],
[ 0, 0, 0, 1]
];
var m= mki3d.matrix4Product( mki3d.projectionMatrix(), shift1 );
return mki3d.matrix4Product( shift2, m );
}
mki3d.setProjectionGLMatrices= function(){
mki3d.monoProjectionGL= mki3d.gl.matrix4toGL(mki3d.projectionMatrix());
if( mki3d.stereo.mode ) {
mki3d.stereo.leftProjectionGL= mki3d.gl.matrix4toGL(mki3d.stereoProjection( -mki3d.stereo.eyeShift ));
mki3d.stereo.rightProjectionGL= mki3d.gl.matrix4toGL(mki3d.stereoProjection( mki3d.stereo.eyeShift ));
}
}
/** from mki3d_html.js **/
/*
mki3d.html -- the references to the relevant objects form html page DOM
*/
mki3d.html = {};
mki3d.html.divsArray= []; /* array of
objects */
mki3d.html.directionButtonArray= []; /* array of direction buttons */
mki3d.html.actionButtonArray= []; /* array of action buttons */
mki3d.html.registerInArray = function( selectorString, array ) {
htmlObject = document.querySelector(selectorString);
array.push(htmlObject);
return htmlObject;
}
mki3d.html.hideAllDivs = function() {
var i=0;
for(i=0; i= Math.abs(dy) ) {
mki3d.action.viewRotateRight(dx/mki3d.html.canvas.width*2*Math.PI);
} else {
mki3d.action.viewRotateUp(-dy/mki3d.html.canvas.height*2*Math.PI);
}
break;
case mki3d.MOVE_ACTION_IDX:
var c=2*mki3d.data.view.screenShift[2]/mki3d.data.projection.zoomY;
mki3d.moveFocusPoint([-dx/mki3d.html.canvas.height*c, dy/mki3d.html.canvas.height*c,0]);
break;
}
mki3d.redraw();
}
mki3d.lastMouse={};
mki3d.onMouseMove= function(e){
var dx= e.clientX-mki3d.lastMouse.clientX;
var dy= e.clientY-mki3d.lastMouse.clientY;
mki3d.lastMouse.clientX=e.clientX;
mki3d.lastMouse.clientY=e.clientY;
mki3d.deltaAction( dx, dy );
}
// touch callbacks
mki3d.lastTouch={};
mki3d.onTouchMove=function(e){
e.preventDefault();
var dx= e.touches[0].clientX-mki3d.lastTouch.clientX;
var dy= e.touches[0].clientY-mki3d.lastTouch.clientY;
mki3d.lastTouch.clientX=e.touches[0].clientX;
mki3d.lastTouch.clientY=e.touches[0].clientY;
mki3d.deltaAction( dx, dy );
}
mki3d.html.initObjects= function() {
mki3d.html.html=document.querySelector('#htmlId');
// mki3d.html.html.style.overflowY="";
mki3d.html.divCanvas= mki3d.html.registerInArray('#divCanvas', mki3d.html.divsArray);
var idx;
/* Register direction buttons */
mki3d.html.leftButton= mki3d.html.registerInArray('#leftButton', mki3d.html.directionButtonArray);
idx = mki3d.html.directionButtonArray.length-1,
mki3d.html.directionButtonArray[idx].onclick = mki3d.directionSetOnClickClosure(idx, mki3d.directionActions.left)
mki3d.html.rightButton= mki3d.html.registerInArray('#rightButton', mki3d.html.directionButtonArray);
idx = mki3d.html.directionButtonArray.length-1,
mki3d.html.directionButtonArray[idx].onclick = mki3d.directionSetOnClickClosure(idx, mki3d.directionActions.right)
mki3d.html.upButton= mki3d.html.registerInArray('#upButton', mki3d.html.directionButtonArray);
idx = mki3d.html.directionButtonArray.length-1,
mki3d.html.directionButtonArray[idx].onclick = mki3d.directionSetOnClickClosure(idx, mki3d.directionActions.up)
mki3d.html.downButton= mki3d.html.registerInArray('#downButton', mki3d.html.directionButtonArray);
idx = mki3d.html.directionButtonArray.length-1,
mki3d.html.directionButtonArray[idx].onclick = mki3d.directionSetOnClickClosure(idx, mki3d.directionActions.down)
mki3d.html.backButton= mki3d.html.registerInArray('#backButton', mki3d.html.directionButtonArray);
idx = mki3d.html.directionButtonArray.length-1,
mki3d.html.directionButtonArray[idx].onclick = mki3d.directionSetOnClickClosure(idx, mki3d.directionActions.back)
mki3d.html.forwardButton= mki3d.html.registerInArray('#forwardButton', mki3d.html.directionButtonArray);
idx = mki3d.html.directionButtonArray.length-1,
mki3d.html.directionButtonArray[idx].onclick = mki3d.directionSetOnClickClosure(idx, mki3d.directionActions.forward)
/* Register action buttons */
mki3d.html.rotateButton= mki3d.html.registerInArray('#rotateButton', mki3d.html.actionButtonArray);
idx = mki3d.html.actionButtonArray.length-1,
mki3d.html.actionButtonArray[idx].onclick = mki3d.actionSetOnClickClosure(idx,
mki3d.buttonRotateCallback
);
mki3d.html.moveButton= mki3d.html.registerInArray('#moveButton', mki3d.html.actionButtonArray);
idx = mki3d.html.actionButtonArray.length-1,
mki3d.html.actionButtonArray[idx].onclick = mki3d.actionSetOnClickClosure(idx,
mki3d.buttonMoveCallback
);
mki3d.html.scaleUpButton= mki3d.html.registerInArray('#scaleUpButton', mki3d.html.actionButtonArray);
idx = mki3d.html.actionButtonArray.length-1,
mki3d.html.actionButtonArray[idx].onclick = mki3d.actionSetOnClickClosure(idx,
mki3d.scaleUpCallback
);
mki3d.html.scaleDownButton= mki3d.html.registerInArray('#scaleDownButton', mki3d.html.actionButtonArray);
idx = mki3d.html.actionButtonArray.length-1,
mki3d.html.actionButtonArray[idx].onclick = mki3d.actionSetOnClickClosure(idx,
mki3d.scaleDownCallback
);
mki3d.html.alignButton= mki3d.html.registerInArray('#alignButton', mki3d.html.actionButtonArray);
idx = mki3d.html.actionButtonArray.length-1,
mki3d.html.actionButtonArray[idx].onclick = mki3d.actionSetOnClickClosure(idx,
mki3d.action.viewAlignRotation
);
mki3d.html.resetButton= mki3d.html.registerInArray('#resetButton', mki3d.html.actionButtonArray);
idx = mki3d.html.actionButtonArray.length-1,
mki3d.html.actionButtonArray[idx].onclick = function() {
mki3d.stopIntervalAction();
mki3d.dataReset();
mki3d.redraw();
};
mki3d.html.divUpperMessage= document.querySelector('#divUpperMessage');
mki3d.html.hideAllDivs();
mki3d.html.showDiv(mki3d.html.divCanvas);
mki3d.html.canvas= document.querySelector("#canvasId");
/* touch callbacks */
function onTouchDown(evt){
evt.preventDefault();
mki3d.stopIntervalAction();
mki3d.lastTouch.clientX=evt.touches[0].clientX;
mki3d.lastTouch.clientY=evt.touches[0].clientY;
mki3d.html.canvas.addEventListener("touchmove", mki3d.onTouchMove, false);
}
function onTouchUp(evt){
evt.preventDefault();
mki3d.html.canvas.removeEventListener("touchmove", mki3d.onTouchMove);
}
mki3d.html.canvas.addEventListener("touchstart", onTouchDown, false);
mki3d.html.canvas.addEventListener("touchend", onTouchUp, false);
/* mouse callbacs */
mki3d.html.canvas.onmousedown= function (e){
mki3d.stopIntervalAction();
mki3d.lastMouse.clientX=e.clientX;
mki3d.lastMouse.clientY=e.clientY;
mki3d.html.canvas.onmousemove=mki3d.onMouseMove;
}
mki3d.html.canvas.onmouseup= function (e){
mki3d.html.canvas.onmousemove=null;
}
mki3d.html.canvas.onwheel= function (e){
if(!e.deltaY) return;
if(e.deltaY==0) return;
var sign= e.deltaY/Math.abs(e.deltaY);
switch(mki3d.actionIdx) {
case mki3d.ROTATE_ACTION_IDX:
mki3d.action.viewRotateForward(-sign*Math.PI/10);
break;
case mki3d.MOVE_ACTION_IDX:
mki3d.moveFocusPoint([0,0, sign*1]);
break;
}
mki3d.redraw();
}
}
/* scaling callbacks */
mki3d.MAX_SCALE= 128; // power of two
mki3d.MIN_SCALE= 1/mki3d.MAX_SCALE;
mki3d.scaleUpCallback= function( buttonIdx ) {
var view=mki3d.data.view;
view.scale= Math.min(MKI3D_MAX_SCALE, 2*view.scale);
mki3d.redraw();
}
mki3d.scaleDownCallback= function( buttonIdx ) {
var view=mki3d.data.view;
view.scale= Math.max(MKI3D_MIN_SCALE, view.scale/2);
mki3d.redraw();
}
/** from mki3d_gl.js **/
mki3d.gl = {};
/*jshint multistr: true */
mki3d.gl.vertexShaderSource = " \
attribute vec3 aVertexPosition; \
attribute vec4 aVertexColor; \
uniform mat4 uMVMatrix; \
uniform mat4 uPMatrix; \
varying vec4 vColor; \
varying vec3 vPosition;\
void main(void) { \
gl_Position = uPMatrix*uMVMatrix*vec4(aVertexPosition, 1.0); \
vColor = aVertexColor; \
vPosition = aVertexPosition; \
}";
mki3d.gl.fragmentShaderSource = " \
precision mediump float; \
uniform vec3 uClipMax; \
uniform vec3 uClipMin; \
varying vec4 vColor; \
varying vec3 vPosition;\
void main(void) { \
if( vPosition.x > uClipMax.x ) discard; \
if( vPosition.y > uClipMax.y ) discard; \
if( vPosition.z > uClipMax.z ) discard; \
if( vPosition.x < uClipMin.x ) discard; \
if( vPosition.y < uClipMin.y ) discard; \
if( vPosition.z < uClipMin.z ) discard; \
gl_FragColor = vColor; \
}";
mki3d.gl.initGL= function(canvas) {
var gl = canvas.getContext("experimental-webgl");
gl.viewportWidth = canvas.width;
gl.viewportHeight = canvas.height;
mki3d.gl.context = gl;
mki3d.gl.buffers.model = mki3d.gl.newBuffers();
mki3d.gl.initShaderProgram();
}
/* object for storing ids of GL buffers */
mki3d.gl.buffers = {};
mki3d.gl.newBuffers= function () {
var gl = mki3d.gl.context;
var buf = {};
buf.segments = gl.createBuffer();
buf.segmentsColors = gl.createBuffer();
buf.triangles = gl.createBuffer();
buf.trianglesColors = gl.createBuffer();
buf.nrOfSegments = 0;
buf.nrOfTriangles = 0;
return buf;
}
// SHADER PROGRAM
mki3d.gl.initShaderProgram= function(){
var gl=mki3d.gl.context;
var fragmentShader =gl.createShader(gl.FRAGMENT_SHADER);
gl.shaderSource(fragmentShader, mki3d.gl.fragmentShaderSource);
gl.compileShader(fragmentShader);
if (!gl.getShaderParameter(fragmentShader, gl.COMPILE_STATUS)) {
console.log(gl.getShaderInfoLog(fragmentShader));
return;
}
var vertexShader = gl.createShader(gl.VERTEX_SHADER);
gl.shaderSource(vertexShader, mki3d.gl.vertexShaderSource);
gl.compileShader(vertexShader);
if (!gl.getShaderParameter(vertexShader, gl.COMPILE_STATUS)) {
console.log(gl.getShaderInfoLog(vertexShader));
return;
}
var shaderProgram = gl.createProgram();
gl.attachShader(shaderProgram, vertexShader);
gl.attachShader(shaderProgram, fragmentShader);
gl.linkProgram(shaderProgram);
if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) {
console.log("Could not initialise shaders");
return;
}
gl.useProgram(shaderProgram);
shaderProgram.aVertexPosition = gl.getAttribLocation(shaderProgram, "aVertexPosition");
gl.enableVertexAttribArray(shaderProgram.aVertexPosition);
shaderProgram.aVertexColor = gl.getAttribLocation(shaderProgram, "aVertexColor");
gl.enableVertexAttribArray(shaderProgram.aVertexColor);
shaderProgram.uPMatrix = gl.getUniformLocation(shaderProgram, "uPMatrix");
shaderProgram.uMVMatrix = gl.getUniformLocation(shaderProgram, "uMVMatrix");
shaderProgram.uClipMax = gl.getUniformLocation(shaderProgram, "uClipMax");
shaderProgram.uClipMin = gl.getUniformLocation(shaderProgram, "uClipMin");
mki3d.gl.shaderProgram = shaderProgram;
}
mki3d.gl.vector3 = function ( x,y,z ){
return new Float32Array(x,y,z);
}
mki3d.gl.matrix4 = function ( xx, yx, zx, wx,
xy, yy, zy, wy,
xz, yz, zz, wz,
xw, yw, zw, ww )
{
// sequence of concatenated columns
return new Float32Array( [ xx, xy, xz, xw,
yx, yy, yz, yw,
zx, zy, zz, zw,
wx, wy, wz, ww ] );
}
mki3d.gl.setClipMax= function ( x, y, z) {
mki3d.gl.context.uniform3f(mki3d.gl.shaderProgram.uClipMax, x,y,z );
}
mki3d.gl.setClipMin= function ( x, y, z) {
mki3d.gl.context.uniform3f(mki3d.gl.shaderProgram.uClipMin, x,y,z );
}
/** from mki3d_callback.js **/
/* event callbacks */
mki3d.callback = {};
mki3d.callback.onWindowResize = function () {
var wth = parseInt(window.innerWidth)-10;
var hth = parseInt(window.innerHeight)-10;
var canvas = mki3d.html.canvas;
var gl = mki3d.gl.context;
canvas.setAttribute("width", ''+wth);
canvas.setAttribute("height", ''+hth);
gl.viewportWidth = wth;
gl.viewportHeight = hth;
gl.viewport(0,0,wth,hth);
// mki3d.setProjectionMatrix();
mki3d.setProjectionGLMatrices()
mki3d.setModelViewMatrix();
mki3d.redraw();
};
/** from mki3d_studio.js **/
mki3d.drawGraph = function (graph) {
// console.log(graph); // test
var gl= mki3d.gl.context;
var shaderProgram = mki3d.gl.shaderProgram;
if(graph.triangles && graph.nrOfTriangles>0 ){
/* draw triangles */
gl.bindBuffer(gl.ARRAY_BUFFER, graph.triangles );
gl.vertexAttribPointer(shaderProgram.aVertexPosition, MKI3D_VERTEX_POSITION_SIZE, gl.FLOAT, false, 0, 0);
gl.bindBuffer(gl.ARRAY_BUFFER, graph.trianglesColors);
gl.vertexAttribPointer(shaderProgram.aVertexColor, MKI3D_VERTEX_COLOR_SIZE, gl.FLOAT, false, 0, 0);
gl.drawArrays(gl.TRIANGLES, 0, 3*graph.nrOfTriangles);
}
if(graph.segments && graph.nrOfSegments>0 ){
/* draw lines - after triangles */
gl.bindBuffer(gl.ARRAY_BUFFER, graph.segments );
gl.vertexAttribPointer(shaderProgram.aVertexPosition, MKI3D_VERTEX_POSITION_SIZE, gl.FLOAT, false, 0, 0);
gl.bindBuffer(gl.ARRAY_BUFFER, graph.segmentsColors);
gl.vertexAttribPointer(shaderProgram.aVertexColor, MKI3D_VERTEX_COLOR_SIZE, gl.FLOAT, false, 0, 0);
gl.drawArrays(gl.LINES, 0, 2*graph.nrOfSegments);
}
}
/* 4d matrix to GL format */
mki3d.gl.matrix4toGL = function ( m )
{
// sequence of concatenated columns
return new Float32Array( [
m[0][0], m[1][0], m[2][0], m[3][0],
m[0][1], m[1][1], m[2][1], m[3][1],
m[0][2], m[1][2], m[2][2], m[3][2],
m[0][3], m[1][3], m[2][3], m[3][3]
] );
}
/* compute projection matrix */
mki3d.projectionMatrix = function(){
var projection = mki3d.data.projection;
var gl = mki3d.gl.context;
var xx= projection.zoomY*gl.viewportHeight/gl.viewportWidth;
var yy= projection.zoomY;
var zz= (projection.zFar+projection.zNear)/(projection.zFar-projection.zNear);
var zw= 1;
var wz= -2*projection.zFar*projection.zNear/(projection.zFar-projection.zNear);
var pMatrix = [
[xx, 0, 0, 0],
[ 0, yy, 0, 0],
[ 0, 0, zz, wz],
[ 0, 0, zw, 0]
];
return pMatrix;
}
/* load model view to GL uMVMatrix */
mki3d.setModelViewMatrix = function () {
var gl = mki3d.gl.context;
var mov = mki3d.vectorClone( mki3d.data.view.focusPoint);
mki3d.vectorScale( mov, -1, -1, -1);
var rot = mki3d.matrixClone( mki3d.data.view.rotationMatrix);
var scale= mki3d.data.view.scale;
mki3d.vectorScale( rot[0], scale, scale, scale);
mki3d.vectorScale( rot[1], scale, scale, scale);
mki3d.vectorScale( rot[2], scale, scale, scale);
var scrSh= mki3d.data.view.screenShift;
var mvMatrix = mki3d.gl.matrix4(
rot[0][0], rot[0][1], rot[0][2], mki3d.scalarProduct(rot[0],mov)+scrSh[0],
rot[1][0], rot[1][1], rot[1][2], mki3d.scalarProduct(rot[1],mov)+scrSh[1],
rot[2][0], rot[2][1], rot[2][2], mki3d.scalarProduct(rot[2],mov)+scrSh[2],
0, 0, 0, 1 );
gl.uniformMatrix4fv(mki3d.gl.shaderProgram.uMVMatrix, false, mvMatrix);
}
mki3d.setDataClipping= function (){
var v= mki3d.data.clipMaxVector;
mki3d.gl.setClipMax(v[0], v[1], v[2]);
v= mki3d.data.clipMinVector;
mki3d.gl.setClipMin(v[0], v[1], v[2]);
}
mki3d.unsetClipping= function () {
mki3d.gl.setClipMax(MKI3D_MAX_CLIP_ABS, MKI3D_MAX_CLIP_ABS, MKI3D_MAX_CLIP_ABS);
mki3d.gl.setClipMin(-MKI3D_MAX_CLIP_ABS, -MKI3D_MAX_CLIP_ABS, -MKI3D_MAX_CLIP_ABS);
}
/* loadf exported data to the GL buffers */
mki3d.loadExported= function() {
var gl = mki3d.gl.context;
var buf = mki3d.gl.buffers.model;
// load segments and colors to GL buffers
var elements=mki3d.exported.segments;
var elementsColors = mki3d.exported.segmentsColors;
gl.bindBuffer(gl.ARRAY_BUFFER, buf.segments);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array( elements ), gl.STATIC_DRAW );
gl.bindBuffer(gl.ARRAY_BUFFER, buf.segmentsColors);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array( elementsColors ), gl.STATIC_DRAW );
buf.nrOfSegments = elements.length/(2*MKI3D_VERTEX_POSITION_SIZE); // + ... markers
// load triangles and colors to GL buffers
elements=mki3d.exported.triangles;
elementsColors = mki3d.exported.trianglesColors;
gl.bindBuffer(gl.ARRAY_BUFFER, buf.triangles);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array( elements ), gl.STATIC_DRAW );
gl.bindBuffer(gl.ARRAY_BUFFER, buf.trianglesColors);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array( elementsColors ), gl.STATIC_DRAW );
buf.nrOfTriangles = elements.length/(3*MKI3D_VERTEX_POSITION_SIZE);
}
/* general redraw function */
mki3d.redraw = function() {
var gl = mki3d.gl.context;
var bg = mki3d.data.backgroundColor;
gl.enable(gl.DEPTH_TEST);
gl.depthFunc(gl.LEQUAL);
mki3d.setModelViewMatrix();
/// mki3d.stereo.mode=true // test stereo mode
if(mki3d.stereo.mode){ /// test stereo version for white colors only ;-)
gl.clearColor(0.0, 0.0, 0.0, 1.0); // stereo background
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); // clear everything
/* LEFT */
gl.colorMask(true, false, false, true ); /// red filter
mki3d.redrawProjection( mki3d.stereo.leftProjectionGL); // monoscopic view
/* RIGHT */
gl.clear(gl.DEPTH_BUFFER_BIT); // clear depth buffer only
gl.colorMask(false, false, true, true ); /// blue filter
mki3d.redrawProjection( mki3d.stereo.rightProjectionGL); // monoscopic view
gl.colorMask(true, true, true, true ); /// reset filter
} else {
gl.clearColor(bg[0], bg[1], bg[2], 1.0);
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
mki3d.redrawProjection(mki3d.monoProjectionGL); // monoscopic view
}
}
mki3d.redrawProjection = function( projectionMatrixGL ) {
mki3d.gl.context.useProgram( mki3d.gl.shaderProgram ); // use the default shader program
mki3d.gl.context.uniformMatrix4fv(mki3d.gl.shaderProgram.uPMatrix, false, projectionMatrixGL ); // projectionMatrixGL
mki3d.setDataClipping()
mki3d.drawGraph( mki3d.gl.buffers.model );
mki3d.unsetClipping();
}
/** from mki3d_data.js **/
mki3d.data = {};
mki3d.dataReset= function(){
mki3d.data = {};
mki3d.data.view = {};
mki3d.data.view.focusPoint = [0,0,0];
mki3d.data.view.rotationMatrix = mki3d.newIdMatrix();
mki3d.data.view.scale = 1;
mki3d.data.view.screenShift = [0,0, MKI3D_SCREEN_Z];
mki3d.data.projection = {};
mki3d.data.projection.zNear = MKI3D_PROJECTION_Z_NEAR;
mki3d.data.projection.zFar = MKI3D_PROJECTION_Z_FAR;
mki3d.data.projection.zoomY = MKI3D_PROJECTION_ZOOM_Y;
mki3d.data.backgroundColor = [0,0,0]; // black
mki3d.data.clipMaxVector = [MKI3D_MAX_CLIP_ABS, MKI3D_MAX_CLIP_ABS, MKI3D_MAX_CLIP_ABS];
mki3d.data.clipMinVector = [-MKI3D_MAX_CLIP_ABS, -MKI3D_MAX_CLIP_ABS, -MKI3D_MAX_CLIP_ABS];
if(mki3d.exported.view) mki3d.data.view = JSON.parse(JSON.stringify(mki3d.exported.view)); // clone
if(mki3d.exported.projection) mki3d.data.projection = JSON.parse(JSON.stringify(mki3d.exported.projection)); // clone
if(mki3d.exported.backgroundColor) mki3d.data.backgroundColor = JSON.parse(JSON.stringify(mki3d.exported.backgroundColor)); // clone
mki3d.stereo= { mode: false };
if(mki3d.exported.stereo) mki3d.stereo = JSON.parse(JSON.stringify(mki3d.exported.stereo)); // clone
// recompute matrices
mki3d.setProjectionGLMatrices();
mki3d.setModelViewMatrix();
}
/** from mki3d_action.js **/
/* ROTATE */
mki3d.action = {};
/* rotation step */
mki3d.action.rotationStep = Math.PI / 36; // 5 degrees
mki3d.action.upRotate = function(){
mki3d.action.viewRotateUp( mki3d.action.rotationStep);
}
mki3d.action.downRotate = function(){
mki3d.action.viewRotateUp( -mki3d.action.rotationStep);
}
mki3d.action.rightRotate = function(){
mki3d.action.viewRotateRight( mki3d.action.rotationStep);
}
mki3d.action.leftRotate = function(){
mki3d.action.viewRotateRight( -mki3d.action.rotationStep);
}
mki3d.action.forwardRotate = function(){
mki3d.action.viewRotateForward( mki3d.action.rotationStep);
}
mki3d.action.backRotate = function(){
mki3d.action.viewRotateForward( -mki3d.action.rotationStep);
}
/* view manipulations */
mki3d.action.viewRotateUp = function(alpha) {
view = mki3d.data.view;
view.rotationMatrix= mki3d.matrixRotatedYZ(view.rotationMatrix, alpha );
mki3d.setModelViewMatrix();
mki3d.redraw();
}
mki3d.action.viewRotateRight = function(alpha) {
view = mki3d.data.view;
view.rotationMatrix= mki3d.matrixRotatedXZ(view.rotationMatrix, alpha );
mki3d.setModelViewMatrix();
mki3d.redraw();
}
mki3d.action.viewRotateForward = function(alpha) {
view = mki3d.data.view;
view.rotationMatrix= mki3d.matrixRotatedXY(view.rotationMatrix, -alpha );
mki3d.setModelViewMatrix();
mki3d.redraw();
}
mki3d.action.viewAlignRotation = function() {
view = mki3d.data.view;
mki3d.tmpRefreshVersorsMatrix();
view.rotationMatrix= mki3d.matrixTransposed( mki3d.tmp.versorsMatrix );
mki3d.setModelViewMatrix();
mki3d.redraw();
}
/* moving focus point in obervers coordinates */
mki3d.moveFocusPoint= function( vector ){
var view= mki3d.data.view;
var rs=1/view.scale;
mki3d.vectorScale(vector, rs, rs, rs);
var matrix= mki3d.matrixInverse(view.rotationMatrix);
var v= mki3d.matrixVectorProduct(matrix, vector);
mki3d.vectorMove(view.focusPoint, v[0], v[1], v[2]);
mki3d.setModelViewMatrix();
mki3d.redraw();
}
/* direction callbacks for MOVE */
mki3d.MOVE_STEP=0.5;
mki3d.moveLeft = function() {
mki3d.moveFocusPoint([mki3d.MOVE_STEP,0,0]);
}
mki3d.moveRight = function() {
mki3d.moveFocusPoint([-mki3d.MOVE_STEP,0,0]);
}
mki3d.moveUp = function() {
mki3d.moveFocusPoint([0,-mki3d.MOVE_STEP,0]);
}
mki3d.moveDown = function() {
mki3d.moveFocusPoint([0,mki3d.MOVE_STEP,0]);
}
mki3d.moveForward = function() {
mki3d.moveFocusPoint([0,0,-mki3d.MOVE_STEP]);
}
mki3d.moveBack = function() {
mki3d.moveFocusPoint([0,0,mki3d.MOVE_STEP]);
}
mki3d.directionActions = {
left: [mki3d.action.leftRotate, mki3d.moveLeft],
right: [mki3d.action.rightRotate, mki3d.moveRight],
up: [mki3d.action.upRotate, mki3d.moveUp],
down: [mki3d.action.downRotate, mki3d.moveDown],
back: [mki3d.action.backRotate, mki3d.moveBack],
forward: [mki3d.action.forwardRotate, mki3d.moveForward]
}
window.onkeydown = function (e){
// var code=e.keyCode? e.keyCode : e.charCode;
const rotStep = Math.PI / 36; // 5 degrees
var code= e.which || e.keyCode;
switch(code)
{
/* direction actions */
case 38: // up
case 73: // I
mki3d.directionActions.up[mki3d.actionIdx]();
break;
case 40: // down
case 75: // K
mki3d.directionActions.down[mki3d.actionIdx]();
break;
case 37: // left
case 74:// J
mki3d.directionActions.left[mki3d.actionIdx]();
break;
case 39:// right
case 76: // L
mki3d.directionActions.right[mki3d.actionIdx]();
break;
case 70: // F
mki3d.directionActions.forward[mki3d.actionIdx]();
break;
case 66: // B
case 86: // V
mki3d.directionActions.back[mki3d.actionIdx]();
break;
/* action buttons */
case 82: // R
mki3d.html.rotateButton.onclick();
break;
case 77: // M
mki3d.html.moveButton.onclick();
break;
case 85: // U
mki3d.html.scaleUpButton.onclick();
break;
case 68: // D
mki3d.html.scaleDownButton.onclick();
break;
case 13: // enter
case 27: // escape
case 81: // Q
mki3d.html.resetButton.onclick();
break;
case 32: // space
mki3d.html.alignButton.onclick();
break;
}
};
/** from mki3d_tmp.js **/
mki3d.tmp={};
/* (re)creation of tmp data */
mki3d.tmpMakeVersorsMatrix = function() {
var rot = mki3d.data.view.rotationMatrix;
var imageXYZRows = [ { img : mki3d.matrixColumn(rot, 0), idx : 0 , row: [1,0,0] },
{ img : mki3d.matrixColumn(rot, 1), idx : 1 , row: [0,1,0] },
{ img : mki3d.matrixColumn(rot, 2), idx : 2 , row: [0,0,1] }];
var spMaxAbs, spNext, tmp;
/* Move best image for Right key to imageXYZRows[0] */
spMaxAbs = mki3d.scalarProduct( imageXYZRows[0].img, [1,0,0] );
spNext = mki3d.scalarProduct( imageXYZRows[1].img, [1,0,0] );
if( Math.abs(spMaxAbs) < Math.abs(spNext) ) { // swap
tmp = imageXYZRows[0];
imageXYZRows[0] = imageXYZRows[1];
imageXYZRows[1] = tmp;
spMaxAbs=spNext; // new record
}
spNext = mki3d.scalarProduct( imageXYZRows[2].img, [1,0,0] );
if( Math.abs(spMaxAbs) < Math.abs(spNext) ) { // swap
tmp = imageXYZRows[0];
imageXYZRows[0] = imageXYZRows[2];
imageXYZRows[2] = tmp;
spMaxAbs=spNext; // new record
}
/* set direction */
if(spMaxAbs < 0 ) mki3d.vectorScale( imageXYZRows[0].row, -1, -1, -1);
/* Move best image for Up key to imageXYZRows[1] */
spMaxAbs = mki3d.scalarProduct( imageXYZRows[1].img, [0,1,0] );
spNext = mki3d.scalarProduct( imageXYZRows[2].img, [0,1,0] );
if( Math.abs(spMaxAbs) < Math.abs(spNext) ) { // swap
tmp = imageXYZRows[1];
imageXYZRows[1] = imageXYZRows[2];
imageXYZRows[2] = tmp;
spMaxAbs=spNext; // new record
}
/* set direction */
if(spMaxAbs < 0 ) mki3d.vectorScale( imageXYZRows[1].row, -1, -1, -1);
/* set direction of the last versor */
if(mki3d.scalarProduct( imageXYZRows[2].img, [0,0,1] )<0) mki3d.vectorScale( imageXYZRows[2].row, -1, -1, -1);
/* set the versorsMatrix */
var alignedMatrix = [ imageXYZRows[0].row,
imageXYZRows[1].row,
imageXYZRows[2].row ];
mki3d.tmp.versorsMatrix = mki3d.matrixTransposed(alignedMatrix); // reverse of the alignedMatrix
mki3d.tmp.versorsMatrix.input = rot;
};
mki3d.tmpInvalidVersorsMatrix= function(){
if(!mki3d.tmp.versorsMatrix) return true;
return mki3d.tmp.versorsMatrix.input !== mki3d.data.view.rotationMatrix;
};
mki3d.tmpRefreshVersorsMatrix= function(){
if( mki3d.tmpInvalidVersorsMatrix() ) mki3d.tmpMakeVersorsMatrix();
};