function project3D(x, y, z, vars) {
var p, d;
x -= vars.camX;
y -= vars.camY;
z -= vars.camZ;
p = Math.atan2(x, z);
d = Math.sqrt(x * x + z * z);
x = Math.sin(p - vars.yaw) * d;
z = Math.cos(p - vars.yaw) * d;
p = Math.atan2(y, z);
d = Math.sqrt(y * y + z * z);
y = Math.sin(p - vars.pitch) * d;
z = Math.cos(p - vars.pitch) * d;
var rx1 = -9;
var ry1 = 1;
var rx2 = 9;
var ry2 = 1;
var rx3 = 0;
var ry3 = 0;
var rx4 = x;
var ry4 = z;
var uc = (ry4 - ry3) * (rx2 - rx1) - (rx4 - rx3) * (ry2 - ry1);
var ua = ((rx4 - rx3) * (ry1 - ry3) - (ry4 - ry3) * (rx1 - rx3)) / uc;
var ub = ((rx2 - rx1) * (ry1 - ry3) - (ry2 - ry1) * (rx1 - rx3)) / uc;
if (!z) z = 0.000000001;
if (ua > 0 && ua < 1 && ub > 0 && ub < 1) {
return {
x: vars.cx + (rx1 + ua * (rx2 - rx1)) * vars.scale,
y: vars.cy + y / z * vars.scale,
d: Math.sqrt(x * x + y * y + z * z)
};
} else {
return {
d: -1
};
}
}
function Vert(x, y, z) {
this.x = x;
this.y = y;
this.z = z;
}
function Poly(verts) {
this.verts = verts;
}
function elevation(x, y, z) {
var dist = Math.sqrt(x * x + y * y + z * z);
if (dist && z / dist >= -1 && z / dist <= 1) return Math.acos(z / dist);
return 0.00000001;
}
function rgb(col) {
col += 0.000001;
var r = parseInt((0.5 + Math.sin(col) * 0.5) * 256);
var g = parseInt((0.5 + Math.cos(col) * 0.5) * 256);
var b = parseInt((0.5 - Math.sin(col) * 0.5) * 256);
return "#" + ("0" + r.toString(16)).slice( - 2) + ("0" + g.toString(16)).slice( - 2) + ("0" + b.toString(16)).slice( - 2);
}
function process(vars) {
var p, d, t;
p = Math.atan2(vars.camX, vars.camZ);
d = Math.sqrt(vars.camX * vars.camX + vars.camZ * vars.camZ);
d -= Math.sin(vars.frameNo / 80) / 20;
t = Math.sin(vars.frameNo / 200) / 105;
vars.camX = Math.sin(p + t) * d;
vars.camZ = Math.cos(p + t) * d;
vars.camY = -Math.sin(vars.frameNo / 100) * 20;
vars.yaw = Math.PI + p + t;
vars.pitch = elevation(vars.camX, vars.camZ, vars.camY) - Math.PI / 2;
for (var i = 1; i < vars.shapes.length; ++i) {
rotateShape(vars.shapes[i], 0, 0, 0, 0, .02 * (i % 2 ? -1 : 1), 0);
}
}
function sortFunction(a, b) {
return b.dist - a.dist;
}
function rotateShape(shape, ox, oy, oz, pitch, yaw) {
var p, d, x, y, z;
x = shape.x - ox;
y = shape.y - oy;
z = shape.z - oz;
d = Math.sqrt(y * y + z * z);
p = Math.atan2(y, z);
y = Math.sin(p + pitch) * d;
z = Math.cos(p + pitch) * d;
d = Math.sqrt(x * x + z * z);
p = Math.atan2(x, z);
x = Math.sin(p + yaw) * d;
z = Math.cos(p + yaw) * d;
shape.x = x + ox;
shape.y = y + oy;
shape.z = z + oz;
for (var i = 0; i < shape.polys.length; ++i) {
for (var j = 0; j < shape.polys[i].verts.length; ++j) {
x = shape.polys[i].verts[j].x;
y = shape.polys[i].verts[j].y;
z = shape.polys[i].verts[j].z;
d = Math.sqrt(y * y + z * z);
p = Math.atan2(y, z);
y = Math.sin(p + pitch) * d;
z = Math.cos(p + pitch) * d;
d = Math.sqrt(x * x + z * z);
p = Math.atan2(x, z);
x = Math.sin(p + yaw) * d;
z = Math.cos(p + yaw) * d;
shape.polys[i].verts[j].x = x;
shape.polys[i].verts[j].y = y;
shape.polys[i].verts[j].z = z;
}
}
}
function draw(vars) {
vars.ctx.globalAlpha = .25;
vars.ctx.fillStyle = "#000";
vars.ctx.fillRect(0, 0, vars.canvas.width, vars.canvas.height);
var pt, x, y, z, polys = [];
for (var i = 0; i < vars.shapes.length; ++i) {
for (var j = 0; j < vars.shapes[i].polys.length; ++j) {
x = vars.shapes[i].x + vars.shapes[i].polys[j].verts[0].x;
y = vars.shapes[i].y + vars.shapes[i].polys[j].verts[0].y;
z = vars.shapes[i].z + vars.shapes[i].polys[j].verts[0].z;
pt = project3D(x, y, z, vars);
if (pt.d != -1) {
poly = {};
poly.verts = Array(vars.shapes[i].polys[j].verts.length);
poly.dist = pt.d;
poly.color = Math.PI * 2 / vars.shapes.length * i;
poly.verts[0] = {};
poly.verts[0].x = pt.x;
poly.verts[0].y = pt.y;
for (var k = 1; k < poly.verts.length; ++k) {
x = vars.shapes[i].x + vars.shapes[i].polys[j].verts[k].x;
y = vars.shapes[i].y + vars.shapes[i].polys[j].verts[k].y;
z = vars.shapes[i].z + vars.shapes[i].polys[j].verts[k].z;
pt = project3D(x, y, z, vars);
poly.dist += pt.d;
poly.verts[k] = {};
poly.verts[k].x = pt.x;
poly.verts[k].y = pt.y;
}
polys.push(poly);
}
}
}
polys.sort(sortFunction);
for (var i = 0; i < polys.length; ++i) {
x = polys[i].verts[0].x;
y = polys[i].verts[0].y;
vars.ctx.lineWidth = 100 / (1 + polys[i].dist);
vars.ctx.beginPath();
vars.ctx.moveTo(x, y);
for (var j = 1; j < polys[i].verts.length; ++j) {
x = polys[i].verts[j].x;
y = polys[i].verts[j].y;
vars.ctx.lineTo(x, y);
}
vars.ctx.fillStyle = rgb(vars.frameNo / 20 + polys[i].color);
vars.ctx.globalAlpha = .75;
vars.ctx.fill();
vars.ctx.strokeStyle = "#fff";
vars.ctx.globalAlpha = .8;
vars.ctx.stroke();
}
}
function Torus(x, y, z, radius1, radius2, stacks, slices, pitch, yaw) {
var shape = {},
x, y, z, p, x1, y1, z1, x2, y2, z2, x3, y3, z3, x4, y4, z4, p1, p2, d;
shape.x = x;
shape.y = y;
shape.z = z;
shape.polys = [];
for (var j = 0; j < slices; ++j) {
for (var i = 0; i < stacks; ++i) {
p1 = Math.PI * 2 / stacks * i;
x1 = radius2 + Math.sin(p1) * radius1;
y1 = Math.cos(p1) * radius1;
z1 = 0;
d = Math.sqrt(x1 * x1);
p2 = Math.PI * 2 / slices * j;
p1 = Math.atan2(x1, z1) + p2;
x1 = Math.sin(p1) * d;
z1 = Math.cos(p1) * d;
p1 = Math.PI * 2 / stacks * (i + 1);
x2 = radius2 + Math.sin(p1) * radius1;
y2 = Math.cos(p1) * radius1;
z2 = 0;
d = Math.sqrt(x2 * x2);
p2 = Math.PI * 2 / slices * j;
p1 = Math.atan2(x2, z2) + p2;
x2 = Math.sin(p1) * d;
z2 = Math.cos(p1) * d;
p1 = Math.PI * 2 / stacks * (i + 1);
x3 = radius2 + Math.sin(p1) * radius1;
y3 = Math.cos(p1) * radius1;
z3 = 0;
d = Math.sqrt(x3 * x3);
p2 = Math.PI * 2 / slices * (j + 1);
p1 = Math.atan2(x3, z3) + p2;
x3 = Math.sin(p1) * d;
z3 = Math.cos(p1) * d;
p1 = Math.PI * 2 / stacks * i;
x4 = radius2 + Math.sin(p1) * radius1;
y4 = Math.cos(p1) * radius1;
z4 = 0;
d = Math.sqrt(x4 * x4);
p2 = Math.PI * 2 / slices * (j + 1);
p1 = Math.atan2(x4, z4) + p2;
x4 = Math.sin(p1) * d;
z4 = Math.cos(p1) * d;
shape.polys.push(new Poly([new Vert(x1, y1, z1), new Vert(x2, y2, z2), new Vert(x3, y3, z3), new Vert(x4, y4, z4)]));
}
}
for (var i = 0; i < shape.polys.length; ++i) {
for (var j = 0; j < shape.polys[i].verts.length; ++j) {
x = shape.polys[i].verts[j].x;
y = shape.polys[i].verts[j].y;
z = shape.polys[i].verts[j].z;
d = Math.sqrt(y * y + z * z);
p = Math.atan2(y, z);
y = Math.sin(p + pitch) * d;
z = Math.cos(p + pitch) * d;
d = Math.sqrt(x * x + z * z);
p = Math.atan2(x, z);
x = Math.sin(p + yaw) * d;
z = Math.cos(p + yaw) * d;
shape.polys[i].verts[j].x = x;
shape.polys[i].verts[j].y = y;
shape.polys[i].verts[j].z = z;
}
}
return shape;
}
function loadScene(vars) {
vars.shapes = [];
var x = 0;
var y = 0;
var z = 0;
var rad1 = 2;
var rad2 = 10;
var stacks = 8;
var slices = 20;
var pitch = 0;
var yaw = 0;
vars.shapes.push(Torus(x, y, z, rad1, rad2, stacks, slices, pitch, yaw));
var sd = 3,
p;
for (var i = 0; i < sd; ++i) {
p = Math.PI * 2 / sd * i;
x = Math.sin(p) * rad2;
z = Math.cos(p) * rad2;
pitch = Math.PI / 2;
yaw = p + Math.PI / 2;
rad1b = 1;
rad2b = 3.5 * (i + 1);
stacks = 6;
slices = 8 * (i + 1);
vars.shapes.push(Torus(x, y, z, rad1b, rad2b, stacks, slices, pitch, yaw));
}
}
function loadFloor(vars) {
var p1, p2, d = 100;
vars.floorTiles = [];
for (var i = 0; i < 3500; ++i) {
vert = {};
vert.p1 = Math.PI * 2 * Math.random();
vert.p2 = Math.PI * Math.random();
vert.x = Math.sin(vert.p1) * Math.sin(vert.p2) * d;
vert.z = Math.cos(vert.p1) * Math.sin(vert.p2) * d;
vert.y = Math.cos(vert.p2) * d;
vars.floorTiles.push(vert);
}
}
function frame(vars) {
if (vars === undefined) {
var vars = {};
vars.canvas = document.createElement("canvas");
document.body.appendChild(vars.canvas);
vars.ctx = vars.canvas.getContext("2d");
vars.canvas.width = document.body.clientWidth;
vars.canvas.height = document.body.clientHeight;
window.addEventListener("resize", function() {
vars.canvas.width = document.body.clientWidth;
vars.canvas.height = document.body.clientHeight;
vars.cx = vars.canvas.width / 2;
vars.cy = vars.canvas.height / 2;
},
true);
vars.canvas.oncontextmenu = function(e) {
e.preventDefault();
};
vars.canvas.addEventListener("mousemove", function(e) {
var rect = vars.canvas.getBoundingClientRect();
vars.mx = Math.round((e.clientX - rect.left) / (rect.right - rect.left) * vars.canvas.width);
vars.my = Math.round((e.clientY - rect.top) / (rect.bottom - rect.top) * vars.canvas.height);
},
true);
vars.canvas.addEventListener("mousedown", function(e) {
switch (e.which) {
case 1:
vars.leftButton = 1;
break;
case 3:
vars.rightButton = 1;
break;
}
},
true);
vars.canvas.addEventListener("mouseup", function(e) {
switch (e.which) {
case 1:
vars.leftButton = 0;
break;
case 3:
vars.rightButton = 0;
break;
}
},
true);
vars.canvas.addEventListener("mousewheel", function(e) {
var e = window.event || e; // old IE support
vars.wheelDelta = Math.max( - 1, Math.min(1, (e.wheelDelta / 120 || -e.detail)));
},
true);
vars.canvas.addEventListener("touchstart", function(e) {
vars.leftButton = 1;
e.preventDefault();
var rect = vars.canvas.getBoundingClientRect();
vars.omx = vars.mx;
vars.omy = vars.my;
vars.mx = Math.round((e.changedTouches[0].pageX - rect.left) / (rect.right - rect.left) * vars.canvas.width);
vars.my = Math.round((e.changedTouches[0].pageY - rect.top) / (rect.bottom - rect.top) * vars.canvas.height);
},
true);
vars.canvas.addEventListener("touchend", function(e) {
vars.leftButton = 0;
},
true);
vars.canvas.addEventListener("touchmove", function(e) {
e.preventDefault();
var rect = vars.canvas.getBoundingClientRect();
vars.mx = Math.round((e.changedTouches[0].pageX - rect.left) / (rect.right - rect.left) * vars.canvas.width);
vars.my = Math.round((e.changedTouches[0].pageY - rect.top) / (rect.bottom - rect.top) * vars.canvas.height);
},
true);
vars.frameNo = 0;
vars.camX = 0;
vars.camY = 0;
vars.camZ = -30;
vars.pitch = 0;
vars.yaw = 0;
vars.cx = vars.canvas.width / 2;
vars.cy = vars.canvas.height / 2;
vars.scale = 700;
vars.seed = 0;
loadScene(vars);
loadFloor(vars);
}
vars.frameNo++;
requestAnimationFrame(function() {
frame(vars);
});
process(vars);
draw(vars);
}
frame();
html,
body {
margin: 0px;
width: 100%;
height: 100%;
overflow: hidden;
}
#canvas {
position: absolute;
width: 100%;
height: 100%;
}
console