You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
787 lines
25 KiB
787 lines
25 KiB
4 years ago
|
//Header file, contains global variable definitions,
|
||
|
// asynchronized shader loading and utility functions
|
||
|
|
||
|
var mousedx = 0,
|
||
|
mousedy = 0,
|
||
|
mousedz = 0;
|
||
|
let seldx = 0,
|
||
|
seldy = 0,
|
||
|
seldz = 0;
|
||
|
var enableSelection = false;
|
||
|
var cx = 1,
|
||
|
cy = 1,
|
||
|
sx = 0,
|
||
|
sy = 0,
|
||
|
shaders = [];
|
||
|
var mouselastX, mouselastY
|
||
|
;
|
||
|
var fl = 3;
|
||
|
let start;
|
||
|
var vs, fs;
|
||
|
var bezierMat = [-1, 3, -3, 1, 3, -6, 3, 0, -3, 3, 0, 0, 1, 0, 0, 0],
|
||
|
hermiteMat = [2, -3, 0, 1, -2, 3, 0, 0, 1, -2, 1, 0, 1, -1, 0, 0],
|
||
|
catmullRomMat = [-.5, 1, -.5, 0, 1.5, -2.5, 0, 1, -1.5, 2, .5, 0, .5, -.5, 0, 0];
|
||
|
var starColors = [0.9921, 0.5378, 0.7109,
|
||
|
0.65, 0.56, 0.992,
|
||
|
0.992, 0.7994, 0.2402,
|
||
|
0.1760, 0.5094, 0.5378,
|
||
|
.1164, .1274, .2289,
|
||
|
.9784, .71, .4482,
|
||
|
],
|
||
|
n_shapes = starColors.length / 3;
|
||
|
var editor = undefined
|
||
|
var cos = Math.cos,
|
||
|
sin = Math.sin,
|
||
|
tan = Math.tan,
|
||
|
acos = Math.acos,
|
||
|
asin = Math.asin,
|
||
|
atan = Math.atan,
|
||
|
sqrt = Math.sqrt,
|
||
|
pi = Math.PI,
|
||
|
abs = Math.abs,
|
||
|
pow = Math.pow,
|
||
|
log = Math.log;
|
||
|
var positionsupdated = true;
|
||
|
var paths = [],
|
||
|
origpath = [],
|
||
|
path_misc = [];
|
||
|
var canvas_controls = [];
|
||
|
let vsfetch = new XMLHttpRequest();
|
||
|
vsfetch.open('GET', './shader.vert');
|
||
|
vsfetch.onloadend = function () {
|
||
|
vs = vsfetch.responseText;
|
||
|
};
|
||
|
vsfetch.send();
|
||
|
//* LOADING FRAGMENT SHADER
|
||
|
let fsfetch = new XMLHttpRequest();
|
||
|
fsfetch.open('GET', './shader.frag');
|
||
|
fsfetch.onloadend = function () {
|
||
|
fs = (fsfetch.responseText);
|
||
|
//* START EVERYTHING AFTER FRAGMENT SHADER IS DOWNLOADED.
|
||
|
if (editor != undefined)
|
||
|
editor.getSession().setValue(fs);
|
||
|
};
|
||
|
fsfetch.send();
|
||
|
let pathFetch = new XMLHttpRequest();
|
||
|
pathFetch.open('GET', './paths.txt');
|
||
|
pathFetch.onloadend = function () {
|
||
|
let text = pathFetch.responseText;
|
||
|
let currX = 0,
|
||
|
currY = 0,
|
||
|
maxX = -10000,
|
||
|
maxY = -10000,
|
||
|
minX = 10000,
|
||
|
minY = 10000;
|
||
|
var currShape = [],
|
||
|
currCurve = [];
|
||
|
let i = 0;
|
||
|
let postProcess = () => {
|
||
|
if (currShape.length) {
|
||
|
let spanX = maxX - minX;
|
||
|
let spanY = maxY - minY;
|
||
|
let span = Math.max(spanX, spanY);
|
||
|
let l_total = 0;
|
||
|
for (var k = 0; k < currShape.length; ++k) {
|
||
|
let funcs = [];
|
||
|
const curve = currShape[k];
|
||
|
for (let j = 0; j < curve.length; j += 2) {
|
||
|
curve[j] = (curve[j] - minX) / span - spanX / (span * 2);
|
||
|
curve[j + 1] = (curve[j + 1] - minY) / span - spanY / (span * 2);
|
||
|
origpath.push(1, curve[j], curve[j + 1], 0, 0, 0, 1);
|
||
|
if (j % 6 == 0 && j > 5) {
|
||
|
let X = [],
|
||
|
Y = [];
|
||
|
for (let k = j - 6; k <= j + 1; k += 2) {
|
||
|
X.push(curve[k]);
|
||
|
Y.push(curve[k + 1]);
|
||
|
}
|
||
|
let l = (vec_len(minus([X[3], Y[3]], [X[0], Y[0]])) +
|
||
|
vec_len(minus([X[3], Y[3]], [X[2], Y[2]])) +
|
||
|
vec_len(minus([X[2], Y[2]], [X[1], Y[1]])) +
|
||
|
vec_len(minus([X[1], Y[1]], [X[0], Y[0]]))) / 2.;
|
||
|
l_total += l;
|
||
|
funcs.push([matrix_multiply(bezierMat, X),
|
||
|
matrix_multiply(bezierMat, Y), l
|
||
|
]);
|
||
|
}
|
||
|
|
||
|
}
|
||
|
paths.push(funcs);
|
||
|
path_misc.push([l_total, spanX / (2 * span), spanY / (2 * span)]);
|
||
|
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
let read_num = () => {
|
||
|
let num = 0,
|
||
|
sign = 1,
|
||
|
accepted = 0;
|
||
|
|
||
|
while (i < text.length && (text[i] < '0' || text[i] > '9') && text[i] != '-') ++i;
|
||
|
if (text[i] == '-') {
|
||
|
sign = -1;
|
||
|
++i;
|
||
|
}
|
||
|
while (i < text.length && text[i] >= '0' && text[i] <= '9') {
|
||
|
let n = text[i++] - '0';
|
||
|
accepted *= 10;
|
||
|
accepted += n;
|
||
|
}
|
||
|
|
||
|
num += accepted;
|
||
|
if (text[i] == '.') {
|
||
|
i++;
|
||
|
let multiplier = 0.1;
|
||
|
accepted = 0;
|
||
|
while (i < text.length && text[i] >= '0' && text[i] <= '9') {
|
||
|
let n = text[i++] - '0';
|
||
|
accepted += n * multiplier;
|
||
|
multiplier /= 10;
|
||
|
}
|
||
|
num += accepted;
|
||
|
}
|
||
|
return num * sign;
|
||
|
}
|
||
|
let cRevs = [],
|
||
|
c_idx = 0,
|
||
|
prevX = 0,
|
||
|
prevY = 0,
|
||
|
getC = () => {
|
||
|
return cRevs[c_idx--];
|
||
|
}
|
||
|
let get_next = (delta = false) => {
|
||
|
if (delta) {
|
||
|
currX = prevX + read_num();
|
||
|
currY = prevY + read_num();
|
||
|
} else {
|
||
|
currX = read_num();
|
||
|
currY = read_num();
|
||
|
}
|
||
|
maxX = currX > maxX ? currX : maxX;
|
||
|
maxY = currY > maxY ? currY : maxY;
|
||
|
minX = currX < minX ? currX : minX;
|
||
|
minY = currY < minY ? currY : minY;
|
||
|
|
||
|
currCurve.push(currX);
|
||
|
currCurve.push(currY);
|
||
|
}
|
||
|
while (i < text.length) {
|
||
|
if (text[i] == 'z') {
|
||
|
currCurve.length && currShape.push(currCurve);
|
||
|
currCurve = [];
|
||
|
++i
|
||
|
} else if (text[i] == 'N') {
|
||
|
postProcess();
|
||
|
currShape = [];
|
||
|
maxX = -1000, maxY = -1000, minX = 1000, minY = 1000;
|
||
|
++i;
|
||
|
} else if (text[i] == 'c') {
|
||
|
|
||
|
prevX = currX;
|
||
|
prevY = currY;
|
||
|
|
||
|
for (let j = 0; j < 3; ++j) {
|
||
|
get_next(true);
|
||
|
}
|
||
|
} else if (text[i] == 'C') {
|
||
|
for (let j = 0; j < 3; ++j) {
|
||
|
get_next();
|
||
|
}
|
||
|
} else if (text[i] == 'M') {
|
||
|
get_next();
|
||
|
} else ++i;
|
||
|
}
|
||
|
};
|
||
|
pathFetch.send();
|
||
|
let rtx_VFetch = new XMLHttpRequest(), rtxvshader_txt;
|
||
|
rtx_VFetch.open('GET', './RTX.vert');
|
||
|
rtx_VFetch.onloadend = function() {
|
||
|
rtxvshader_txt = rtx_VFetch.responseText;
|
||
|
};
|
||
|
rtx_VFetch.send();
|
||
|
let rtx_FFetch = new XMLHttpRequest();
|
||
|
rtx_FFetch.open('GET', './RTX.frag');
|
||
|
rtx_FFetch.onloadend = function () {
|
||
|
let rtxfs_text = rtx_FFetch.responseText;
|
||
|
|
||
|
let interval = setInterval(()=>{
|
||
|
if(buildShaders && rtxvshader_txt && canvas1.gl)
|
||
|
{
|
||
|
gl.shaders[1] = buildShaders(rtxvshader_txt, rtxfs_text);
|
||
|
clearInterval(interval);
|
||
|
}
|
||
|
}, 1);
|
||
|
}
|
||
|
rtx_FFetch.send()
|
||
|
let vec_len = v => {
|
||
|
let len = 0;
|
||
|
for (let i = 0; i < v.length; ++i)
|
||
|
len += v[i] * v[i];
|
||
|
return sqrt(len);
|
||
|
}
|
||
|
let matrix_inverse = src => {
|
||
|
let dst = [],
|
||
|
det = 0,
|
||
|
cofactor = (c, r) => {
|
||
|
let s = (i, j) => src[c + i & 3 | (r + j & 3) << 2];
|
||
|
return (c + r & 1 ? -1 : 1) * ((s(1, 1) * (s(2, 2) * s(3, 3) - s(3, 2) * s(2, 3))) -
|
||
|
(s(2, 1) * (s(1, 2) * s(3, 3) - s(3, 2) * s(1, 3))) +
|
||
|
(s(3, 1) * (s(1, 2) * s(2, 3) - s(2, 2) * s(1, 3))));
|
||
|
}
|
||
|
for (let n = 0; n < 16; n++) dst.push(cofactor(n >> 2, n & 3));
|
||
|
for (let n = 0; n < 4; n++) det += src[n] * dst[n << 2];
|
||
|
for (let n = 0; n < 16; n++) dst[n] /= det;
|
||
|
return dst;
|
||
|
}
|
||
|
|
||
|
// I HAVE IMPLEMENTED THESE FUNCTIONS FOR YOU
|
||
|
let matrix_identity = () => {
|
||
|
return [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1];
|
||
|
}
|
||
|
let matrix_translate = (x, y, z) => {
|
||
|
let m = matrix_identity();
|
||
|
m[12] = x;
|
||
|
m[13] = y;
|
||
|
m[14] = z;
|
||
|
return m;
|
||
|
}
|
||
|
let matrix_perspective = (m) => {
|
||
|
let ret = []
|
||
|
for (let i = 0; i < 16; i++)
|
||
|
ret[i] = m[i];
|
||
|
for (let i = 2; i < 15; i += 4) {
|
||
|
ret[i] = -ret[i];
|
||
|
ret[i + 1] += ret[i] / fl;
|
||
|
}
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
// YOU NEED TO PROPERLY IMPLEMENT THE FOLLOWING FIVE FUNCTIONS:
|
||
|
let matrix_rotateX = theta => {
|
||
|
let m = matrix_identity();
|
||
|
m[5] = cos(theta);
|
||
|
m[6] = sin(theta);
|
||
|
m[9] = -sin(theta);
|
||
|
m[10] = cos(theta);
|
||
|
return m;
|
||
|
}
|
||
|
|
||
|
let matrix_rotateY = theta => {
|
||
|
let m = matrix_identity();
|
||
|
m[0] = cos(theta);
|
||
|
m[2] = -sin(theta);
|
||
|
m[8] = sin(theta);
|
||
|
m[10] = cos(theta);
|
||
|
return m;
|
||
|
}
|
||
|
let matrix_rotateZ = theta => {
|
||
|
let m = matrix_identity();
|
||
|
m[0] = cos(theta);
|
||
|
m[1] = sin(theta);
|
||
|
m[4] = -sin(theta);
|
||
|
m[5] = cos(theta);
|
||
|
return m;
|
||
|
}
|
||
|
let matrix_scale = (x, y, z) => {
|
||
|
if (y === undefined)
|
||
|
y = z = x;
|
||
|
let m = matrix_identity();
|
||
|
m[0] = x;
|
||
|
m[5] = y;
|
||
|
m[10] = z;
|
||
|
return m;
|
||
|
}
|
||
|
let matrix_multiply = (a, b, m = 4, n = 4) => { //dim=mn*nm=mm
|
||
|
let res = [];
|
||
|
if (b.length < m * n) { //mat-vec multiply (i did this for my convenience)
|
||
|
for (let i = 0; i < m; ++i) {
|
||
|
res[i] = 0;
|
||
|
for (let j = 0; j < n; ++j)
|
||
|
res[i] += b[j] * a[m * j + i];
|
||
|
}
|
||
|
return res;
|
||
|
} //otherwise mm multiply
|
||
|
for (let i = 0; i < m; ++i)
|
||
|
for (let j = 0; j < m; ++j) {
|
||
|
var t = 0;
|
||
|
for (let k = 0; k < n; ++k)
|
||
|
t += a[k * m + j] * b[i * n + k];
|
||
|
res.push(t);
|
||
|
}
|
||
|
return res;
|
||
|
}
|
||
|
let const_multiply = (c, a, add = 0) => {
|
||
|
let m = [];
|
||
|
for (let i = 0; i < a.length; ++i)
|
||
|
m[i] = (a[i] + add)* c;
|
||
|
return m;
|
||
|
}
|
||
|
|
||
|
function dot(a, b) {
|
||
|
b=b?b:a;
|
||
|
let m = 0;
|
||
|
for (let i = 0; i < a.length; ++i)
|
||
|
m += a[i] * b[i];
|
||
|
return m;
|
||
|
}
|
||
|
|
||
|
function cross(a, b){
|
||
|
let m = [];
|
||
|
m[0] = a[1]*b[2] - a[2]*b[1];
|
||
|
m[1] = a[2]*b[0] - a[0]*b[2];
|
||
|
m[2] = a[0]*b[1] - a[1]*b[0];
|
||
|
return m;
|
||
|
}
|
||
|
|
||
|
function plus(a, b) {
|
||
|
let m = [];
|
||
|
for (let i = 0; i < a.length; ++i)
|
||
|
m[i] = a[i] + b[i];
|
||
|
return m;
|
||
|
}
|
||
|
|
||
|
function minus(a, b) {
|
||
|
let m = [];
|
||
|
for (let i = 0; i < a.length; ++i)
|
||
|
m[i] = a[i] - b[i];
|
||
|
return m;
|
||
|
}
|
||
|
|
||
|
function normalize(v) {
|
||
|
let res = [];
|
||
|
sum = 0;
|
||
|
for (let i = 0; i < v.length; ++i)
|
||
|
sum += v[i] * v[i];
|
||
|
sum = sqrt(sum);
|
||
|
for (let i = 0; i < v.length; ++i)
|
||
|
res[i] = v[i] / sum;
|
||
|
return res;
|
||
|
}
|
||
|
|
||
|
|
||
|
let Matrix = function () {
|
||
|
let top = 0,
|
||
|
m = [matrix_identity()];
|
||
|
this.identity = () => m[top] = matrix_identity();
|
||
|
this.translate = (x, y, z) => m[top] = matrix_multiply(m[top], matrix_translate(x, y, z));
|
||
|
this.rotateX = theta => m[top] = matrix_multiply(m[top], matrix_rotateX(theta));
|
||
|
this.rotateY = theta => m[top] = matrix_multiply(m[top], matrix_rotateY(theta));
|
||
|
this.rotateZ = theta => m[top] = matrix_multiply(m[top], matrix_rotateZ(theta));
|
||
|
this.scale = (x, y, z) => m[top] = matrix_multiply(m[top], matrix_scale(x, y, z));
|
||
|
this.apply = (m1) => m[top] = matrix_multiply(m[top], m1);
|
||
|
this.applyl = (m1) => m[top] = matrix_multiply(m1, m[top]);
|
||
|
this.value = () => m[top];
|
||
|
this.save = () => {
|
||
|
m[top + 1] = m[top].slice();
|
||
|
top++;
|
||
|
}
|
||
|
this.restore = () => --top;
|
||
|
}
|
||
|
function hitTest_sphere(ray, sphere, r) {
|
||
|
let B = dot(ray, sphere);
|
||
|
let C = dot(sphere, sphere) - r*r;
|
||
|
let D = B * B - C;
|
||
|
if (D > 0.) {
|
||
|
//console.log(D);
|
||
|
//let t = -B - sqrt(D);
|
||
|
return 1;
|
||
|
}
|
||
|
return -1;
|
||
|
}
|
||
|
function hitTest_square(pt, invMatrix, shape){
|
||
|
pt.push(1);
|
||
|
const V = dot_xyz(matrix_multiply(invMatrix, [0,0,fl,1])),
|
||
|
pos = dot_xyz(matrix_multiply(invMatrix, pt)),
|
||
|
W = minus(pos, V),
|
||
|
A = shape.slice(1,4),
|
||
|
B = shape.slice(8,11),
|
||
|
C = shape.slice(15,18),
|
||
|
AB = minus(B, A),
|
||
|
AC = minus(C, A),
|
||
|
AB_AC = cross(AB, AC),
|
||
|
VA = minus(V, A),
|
||
|
t = - dot(AB_AC, VA)/dot(W, AB_AC),
|
||
|
P = plus(V, const_multiply(t, W)),
|
||
|
AP = minus(P, A),
|
||
|
d_AP_AC = dot(AP, AC),
|
||
|
d_AP_AB = dot(AP, AB);
|
||
|
if(0 <d_AP_AC && d_AP_AC < dot(AC, AC) && 0 < d_AP_AB && d_AP_AB< dot(AB, AB))
|
||
|
return P;
|
||
|
else return -1;
|
||
|
}
|
||
|
let dot_xyz = (v) => {
|
||
|
let ret = [];
|
||
|
if(v[3])
|
||
|
for(let i = 0; i < 3; ++i)
|
||
|
ret[i] = v[i]/v[3];
|
||
|
else ret = v.slice(0,3);
|
||
|
return ret;
|
||
|
}
|
||
|
let Button = function (onclick = () => {}, shape = [], outlineTy = 'sphere') {
|
||
|
this.shape = shape;
|
||
|
this.enabled = true;
|
||
|
this.outlineTy = outlineTy;
|
||
|
this.onClick = onclick;
|
||
|
this.updateMatrix = (m) => {this.matrix = matrix_perspective(m); this.invMatrix = matrix_inverse(m);};
|
||
|
this.updateMatrix(matrix_identity());
|
||
|
this.hovering = false;
|
||
|
this.activated = false;
|
||
|
this.getShape = () => this.shape;
|
||
|
this.draw = () => {setUniform('Matrix4fv', 'uMatrix', false, this.matrix);
|
||
|
setUniform('Matrix4fv', 'invMatrix', false, this.invMatrix); drawMesh(this.shape);}
|
||
|
this.resetShape = (_sh) => {
|
||
|
if(this.shape) delete this.shape;
|
||
|
this.shape = new Float32Array(_sh);
|
||
|
let maxV = new Array(3).fill(-Infinity), minV = new Array(3).fill(Infinity);
|
||
|
for(let i = 0; i < _sh.length; i += 7){
|
||
|
const v = [_sh[i + 1], _sh[i + 2], _sh[i + 3]];
|
||
|
v.forEach((c, j) => {
|
||
|
maxV[j] = maxV[j] > c ? maxV[j] : c;
|
||
|
minV[j] = minV[j] < c ? minV[j] : c;
|
||
|
})
|
||
|
}
|
||
|
//build outline
|
||
|
switch(this.outlineTy){
|
||
|
case 'sphere':
|
||
|
this.origin = const_multiply(.5, plus(maxV, minV));
|
||
|
//this.origin.push(1);
|
||
|
this.radius = 0.6*vec_len(minus(maxV, minV))/2.;
|
||
|
break;
|
||
|
case 'square':
|
||
|
break;
|
||
|
case 'circle':
|
||
|
break;
|
||
|
}
|
||
|
switch(this.outlineTy){
|
||
|
case 'sphere':
|
||
|
this.hitTest = (pt) => {
|
||
|
pt.push(1);
|
||
|
const V = dot_xyz(matrix_multiply(this.invMatrix, [0,0,fl,1])),
|
||
|
pos = dot_xyz(matrix_multiply(this.invMatrix, pt));
|
||
|
let W = normalize((minus(pos, V)));
|
||
|
let Vp = minus(V, this.origin);
|
||
|
return hitTest_sphere(W, Vp, this.radius);
|
||
|
}
|
||
|
break;
|
||
|
case 'square':
|
||
|
this.hitTest = (pt) => {
|
||
|
this.P = hitTest_square(pt, this.invMatrix, this.shape);
|
||
|
return this.P;
|
||
|
}
|
||
|
break;
|
||
|
case 'circle':
|
||
|
break;
|
||
|
}
|
||
|
};
|
||
|
if(!shape || shape.length != 0)
|
||
|
this.resetShape(shape);
|
||
|
canvas_controls.push(this);
|
||
|
}
|
||
|
let setM = (m) => {
|
||
|
let mm = matrix_perspective(m);
|
||
|
setUniform('Matrix4fv', 'uMatrix', false, mm);
|
||
|
setUniform('Matrix4fv', 'invMatrix', false, matrix_inverse(mm));
|
||
|
}
|
||
|
//------ CREATING MESH SHAPES
|
||
|
|
||
|
// CREATE A MESH FROM A PARAMETRIC FUNCTION
|
||
|
|
||
|
let createMesh = (nu, nv, f, data, oid = 0) => {
|
||
|
let tmp = [];
|
||
|
for (let v = 0; v < 1; v += 1 / nv) {
|
||
|
for (let u = 0; u <= 1; u += 1 / nu) {
|
||
|
tmp = tmp.concat(f(u, v, oid, data));
|
||
|
tmp = tmp.concat(f(u, v + 1 / nv, oid, data));
|
||
|
}
|
||
|
tmp = tmp.concat(f(1, v, oid, data));
|
||
|
tmp = tmp.concat(f(0, v + 1 / nv, oid, data));
|
||
|
}
|
||
|
return new Float32Array(tmp);
|
||
|
}
|
||
|
//Create a Mesh from Splines
|
||
|
let createMeshFromSpline = (idx,
|
||
|
nu, nv, oid = 16, additional_offset = 0, f = () => {}) => {
|
||
|
let S = paths[idx],
|
||
|
meta = path_misc[idx];
|
||
|
const n_min = 2;
|
||
|
let ds = meta[0] / nv,
|
||
|
curr_s = 0,
|
||
|
curr_d = S[0][2] / Math.ceil(S[0][2] / ds),
|
||
|
i = 0,
|
||
|
s = 0;
|
||
|
let tmp = [],
|
||
|
ret = undefined;
|
||
|
while (s < meta[0] - 1 / 100000) {
|
||
|
|
||
|
for (let u = 0; u <= 1; u += 1 / nu) {
|
||
|
tmp = tmp.concat(getSurface(S[i][1], S[i][0], u, curr_s, idx, oid, additional_offset));
|
||
|
tmp = tmp.concat(getSurface(S[i][1], S[i][0], u, curr_s + curr_d, idx, oid, additional_offset));
|
||
|
}
|
||
|
tmp = tmp.concat(getSurface(S[i][1], S[i][0], 0, curr_s, idx, oid, additional_offset));
|
||
|
tmp = tmp.concat(getSurface(S[i][1], S[i][0], 1, curr_s + curr_d, idx, oid, additional_offset));
|
||
|
if (ret = f(i, tmp, oid)) {
|
||
|
oid = ret;
|
||
|
}
|
||
|
curr_s += curr_d;
|
||
|
if (curr_s >= 1) {
|
||
|
s += S[i][2];
|
||
|
++i;
|
||
|
if (i >= S.length)
|
||
|
break;
|
||
|
let curr_n = Math.ceil(S[i][2] / ds);
|
||
|
curr_n = curr_n < n_min ? n_min : curr_n;
|
||
|
curr_d = 1 / curr_n;
|
||
|
curr_s = 0;
|
||
|
}
|
||
|
}
|
||
|
return new Float32Array(tmp);
|
||
|
}
|
||
|
// GLUE TWO MESHES TOGETHER INTO A SINGLE MESH
|
||
|
|
||
|
let glueMeshes = (a, b) => {
|
||
|
let c = [];
|
||
|
for (let i = 0; i < a.length; i++)
|
||
|
c.push(a[i]); // a
|
||
|
for (let i = 0; i < VERTEX_SIZE; i++)
|
||
|
c.push(a[a.length - VERTEX_SIZE + i]); // + last vertex of a
|
||
|
for (let i = 0; i < VERTEX_SIZE; i++)
|
||
|
c.push(b[i]); // + first vertex of b
|
||
|
for (let i = 0; i < b.length; i++)
|
||
|
c.push(b[i]); // + b
|
||
|
return new Float32Array(c);
|
||
|
}
|
||
|
|
||
|
|
||
|
let uvToSphere = (u, v, i) => {
|
||
|
let theta = 2 * Math.PI * u;
|
||
|
let phi = Math.PI * (v - .5);
|
||
|
let x = Math.cos(theta) * Math.cos(phi);
|
||
|
let y = Math.sin(theta) * Math.cos(phi);
|
||
|
let z = Math.sin(phi);
|
||
|
return [i, x, y, z].concat(normalize([x, y, z]));
|
||
|
}
|
||
|
|
||
|
let uvToTube = (u, v, i) => {
|
||
|
let theta = 2 * Math.PI * u;
|
||
|
let x = Math.cos(theta);
|
||
|
let y = Math.sin(theta);
|
||
|
let z = 2 * v - 1;
|
||
|
return [i, x, y, z].concat(normalize([x, y, 0]));
|
||
|
}
|
||
|
|
||
|
let uvToDisk = (u, v, i, dz) => {
|
||
|
if (dz === undefined)
|
||
|
dz = 0;
|
||
|
let theta = 2 * Math.PI * u;
|
||
|
let x = Math.cos(theta) * v;
|
||
|
let y = Math.sin(theta) * v;
|
||
|
let z = dz;
|
||
|
return [i, x, y, z].concat([0, 0, Math.sign(z)]);
|
||
|
}
|
||
|
let uvToTorus = (u, v, i, r) => {
|
||
|
let theta = 2 * pi;
|
||
|
let phi = theta * v;
|
||
|
theta *= u;
|
||
|
let x = 1 + r * cos(phi);
|
||
|
let y = sin(theta) * x;
|
||
|
x *= cos(theta);
|
||
|
let z = r * sin(phi);
|
||
|
let tx = -sin(theta),
|
||
|
ty = cos(theta),
|
||
|
tsx = sin(phi),
|
||
|
tsy = tsx * tx,
|
||
|
tsz = cos(phi);
|
||
|
tsx *= -ty;
|
||
|
return [i, x, y, z].concat(normalize([ty * tsz * 0.5, -tx * tsz, tx * tsy - ty * tsx]));
|
||
|
}
|
||
|
|
||
|
let createCube = (w, h, l, id) => {
|
||
|
let mesh = [];
|
||
|
mesh = mesh.concat([id, -w / 2, -h / 2, -l / 2, 0, -1, 0]);
|
||
|
mesh = mesh.concat([id, -w / 2, -h / 2, l / 2, 0, -1, 0]);
|
||
|
mesh = mesh.concat([id, w / 2, -h / 2, -l / 2, 0, -1, 0]);
|
||
|
mesh = mesh.concat([id, w / 2, -h / 2, l / 2, 0, -1, 0]);
|
||
|
|
||
|
mesh = mesh.concat([id, w / 2, -h / 2, l / 2, 0, -1, 0]);
|
||
|
mesh = mesh.concat([id, w / 2, -h / 2, l / 2, 0, 0, 1]);
|
||
|
|
||
|
mesh = mesh.concat([id, w / 2, -h / 2, l / 2, 0, 0, 1]);
|
||
|
mesh = mesh.concat([id, w / 2, h / 2, l / 2, 0, 0, 1]);
|
||
|
mesh = mesh.concat([id, -w / 2, -h / 2, l / 2, 0, 0, 1]);
|
||
|
mesh = mesh.concat([id, -w / 2, h / 2, l / 2, 0, 0, 1]);
|
||
|
|
||
|
mesh = mesh.concat([id, -w / 2, h / 2, l / 2, 0, 0, 1]);
|
||
|
mesh = mesh.concat([id, -w / 2, h / 2, l / 2, -1, 0, 0]);
|
||
|
|
||
|
mesh = mesh.concat([id, -w / 2, h / 2, l / 2, -1, 0, 0]);
|
||
|
mesh = mesh.concat([id, -w / 2, -h / 2, l / 2, -1, 0, 0]);
|
||
|
mesh = mesh.concat([id, -w / 2, h / 2, -l / 2, -1, 0, 0]);
|
||
|
mesh = mesh.concat([id, -w / 2, -h / 2, -l / 2, -1, 0, 0]);
|
||
|
|
||
|
mesh = mesh.concat([id, -w / 2, -h / 2, -l / 2, -1, 0, 0]);
|
||
|
mesh = mesh.concat([id, -w / 2, -h / 2, -l / 2, 0, 0, -1]);
|
||
|
|
||
|
mesh = mesh.concat([id, -w / 2, -h / 2, -l / 2, 0, 0, -1]);
|
||
|
mesh = mesh.concat([id, -w / 2, h / 2, -l / 2, 0, 0, -1]);
|
||
|
mesh = mesh.concat([id, w / 2, -h / 2, -l / 2, 0, 0, -1]);
|
||
|
mesh = mesh.concat([id, w / 2, h / 2, -l / 2, 0, 0, -1]);
|
||
|
|
||
|
mesh = mesh.concat([id, w / 2, h / 2, -l / 2, 0, 0, -1]);
|
||
|
mesh = mesh.concat([id, w / 2, h / 2, -l / 2, 1, 0, 0]);
|
||
|
|
||
|
mesh = mesh.concat([id, w / 2, h / 2, -l / 2, 1, 0, 0]);
|
||
|
mesh = mesh.concat([id, w / 2, h / 2, l / 2, 1, 0, 0]);
|
||
|
mesh = mesh.concat([id, w / 2, -h / 2, -l / 2, 1, 0, 0]);
|
||
|
mesh = mesh.concat([id, w / 2, -h / 2, l / 2, 1, 0, 0]);
|
||
|
|
||
|
mesh = mesh.concat([id, w / 2, -h / 2, l / 2, 1, 0, 0]);
|
||
|
mesh = mesh.concat([id, w / 2, h / 2, l / 2, 0, 1, 0]);
|
||
|
|
||
|
mesh = mesh.concat([id, w / 2, h / 2, l / 2, 0, 1, 0]);
|
||
|
mesh = mesh.concat([id, w / 2, h / 2, -l / 2, 0, 1, 0]);
|
||
|
mesh = mesh.concat([id, -w / 2, h / 2, l / 2, 0, 1, 0]);
|
||
|
mesh = mesh.concat([id, -w / 2, h / 2, -l / 2, 0, 1, 0]);
|
||
|
return new Float32Array(mesh);
|
||
|
}
|
||
|
|
||
|
function updatePositions() {
|
||
|
let m = matrix_rotateY(-mousedx);
|
||
|
m = matrix_multiply(m, matrix_rotateX(-mousedy));
|
||
|
setUniform('3f', 'V0', m[8] * (fl + mousedz), m[9] * (fl + mousedz), m[10] * (fl + mousedz));
|
||
|
m = const_multiply((fl + 1 + mousedz) / (fl + 1), m);
|
||
|
setUniform('Matrix3fv', 'transformation', false, [m[0], m[1], m[2], m[4], m[5], m[6], m[8], m[9], m[10]]);
|
||
|
positionsupdated = false;
|
||
|
}
|
||
|
function controls_hitTest(pos, clicked = false){
|
||
|
// let iMin = -1, tMin = 10000.;
|
||
|
canvas_controls.forEach((c, i) => {
|
||
|
if(c.enabled && c.hitTest && (c.hover||clicked) ){
|
||
|
let t = c.hitTest(pos);
|
||
|
if(t != -1){
|
||
|
if(clicked)
|
||
|
c.onClick();
|
||
|
else
|
||
|
c.hover(true);
|
||
|
}
|
||
|
else
|
||
|
if(c.hovering)
|
||
|
c.hover(false);
|
||
|
}
|
||
|
});
|
||
|
//ground
|
||
|
if(ground_m && ground){
|
||
|
let invG = matrix_inverse(ground_m);
|
||
|
let P = hitTest_square(pos, invG, ground);
|
||
|
//console.log(P);
|
||
|
//let Vp = minus(matrix_multiply(invG,[0,0,fl, 1]).slice(0,3), this.origin);
|
||
|
if(P!=-1)
|
||
|
if(near_magician){
|
||
|
if(slider.dragging === true){
|
||
|
slider_dl += 10 * (P[0] - slider.lastPos);
|
||
|
if(slider_dl < 0) slider_dl = 0;
|
||
|
else if (slider_dl > 9) slider_dl = 9;
|
||
|
slider.lastPos = P[0];
|
||
|
// onUpdate
|
||
|
let id = 1 + .45*slider_dl/9;
|
||
|
changeID(id, cylinderMesh);
|
||
|
} else if(clicked) {
|
||
|
const PO = minus([slider_dl/10-.25, .7], [P[0], P[1]]);
|
||
|
const rr = PO[0]*PO[0] + PO[1] * PO[1];
|
||
|
if(rr < .003){
|
||
|
slider.dragging = true;
|
||
|
slider.lastPos = P[0];
|
||
|
}
|
||
|
}
|
||
|
} else {
|
||
|
if(clicked || running<=0)
|
||
|
{
|
||
|
direction_l = [P[0] - delta_l[0]/10, P[1] - delta_l[1]/10];
|
||
|
if(P[1] < 0.00000000000001)
|
||
|
P[1] = 0.0000000000000001;
|
||
|
figure_rot = atan(direction_l[0]/direction_l[1]);
|
||
|
if(direction_l[1] < 0){
|
||
|
figure_rot = pi + figure_rot;
|
||
|
}
|
||
|
}
|
||
|
//if(figure_rot < 0) figure_rot += pi;
|
||
|
//updateStatus([figure_rot, direction_l[0]/direction_l[1]]);
|
||
|
if(clicked)
|
||
|
{
|
||
|
dir_sdl = vec_len(direction_l)*10;
|
||
|
direction_l = normalize(direction_l);
|
||
|
rebuild = true;
|
||
|
running = 1;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
//return iMin;
|
||
|
}
|
||
|
function hitTest(pos) {
|
||
|
|
||
|
let m = matrix_rotateY(-mousedx);
|
||
|
m = matrix_multiply(m, matrix_rotateX(-mousedy));
|
||
|
let V = [m[8] * (fl + mousedz), m[9] * (fl + mousedz), m[10] * (fl + mousedz)];
|
||
|
m = const_multiply((fl + 1 + mousedz) / (fl + 1), m);
|
||
|
let trPos = matrix_multiply([m[0], m[1], m[2], m[4], m[5], m[6], m[8], m[9], m[10]], [pos[0], -pos[1], -1], 3, 3);
|
||
|
|
||
|
let W = normalize(minus(trPos, V));
|
||
|
let tMin = 10000.;
|
||
|
let iMin = -1;
|
||
|
for (let i = 0; i < cns; i++) {
|
||
|
let Vp = minus(V, matrix_multiply(SphTr[i], Sph[i]));
|
||
|
let B = dot(W, Vp);
|
||
|
let C = dot(Vp, Vp) - Sph[i][4] * Sph[i][4];
|
||
|
let D = B * B - C;
|
||
|
if (D > 0.) {
|
||
|
let t = -B - sqrt(D);
|
||
|
if (t > 0.0 && t < tMin) {
|
||
|
tMin = t; // This is an optimization, we don't have to do lighting/tex
|
||
|
iMin = i; // for objects that are occuluded, which is expensive!
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
return iMin;
|
||
|
}
|
||
|
let matrix_transform = (m, p) => {
|
||
|
let x = p[0],
|
||
|
y = p[1],
|
||
|
z = p[2],
|
||
|
w = p[3] === undefined ? 1 : p[3];
|
||
|
let q = [m[0] * x + m[4] * y + m[8] * z + m[12] * w,
|
||
|
m[1] * x + m[5] * y + m[9] * z + m[13] * w,
|
||
|
m[2] * x + m[6] * y + m[10] * z + m[14] * w,
|
||
|
m[3] * x + m[7] * y + m[11] * z + m[15] * w
|
||
|
];
|
||
|
return p[3] === undefined ? [q[0] / q[3], q[1] / q[3], q[2] / q[3]] : q;
|
||
|
}
|
||
|
let evalSpline = (h, t) => {
|
||
|
// t *= (h.length - 2) / 2;
|
||
|
// let n = 2 * Math.floor(t);
|
||
|
// t = t % 1;
|
||
|
// let C = matrix_transform(type, [h[n+0],h[n+2],h[n+1],h[n+3]]);
|
||
|
// return t*t*t*C[0] + t*t*C[1] + t*C[2] + C[3];
|
||
|
return t * t * t * h[0] + t * t * h[1] + t * h[2] + h[3];
|
||
|
}
|
||
|
let getSurface = (S0, S1, u, v, offsetidx, id = 15, additional_offset = 0) => {
|
||
|
const epsilon = .001;
|
||
|
let z0 = evalSpline(S0, v),
|
||
|
z1 = evalSpline(S0, v + epsilon),
|
||
|
|
||
|
r0 = evalSpline(S1, v) - path_misc[offsetidx][1] + additional_offset,
|
||
|
r1 = evalSpline(S1, v + epsilon),
|
||
|
|
||
|
tilt = Math.atan2(r0 - r1, z1 - z0);
|
||
|
|
||
|
let xx = cos(2 * pi * u),
|
||
|
yy = sin(2 * pi * u);
|
||
|
let x = r0 * xx, // POSITION
|
||
|
y = r0 * yy,
|
||
|
z = z0,
|
||
|
nx = cos(tilt), // NORMAL
|
||
|
ny = nx * yy,
|
||
|
nz = sin(tilt);
|
||
|
nx *= xx;
|
||
|
return [id, x, y, z, nx, ny, nz];
|
||
|
}
|
||
|
let concat = (a, b) => b.forEach(e => a.push(e));
|