|
let points = [ |
|
// x,z dist |
|
0.0,0, 0.0, |
|
0.3,0, 0.3, |
|
0.3,1, 1.3, |
|
0.5,1, 1.5, |
|
0.5,0, 2.5, |
|
0.6,0, 2.6, |
|
0.6,2, 4.6, |
|
0.8,2, 4.8, |
|
0.8,1, 5.8, |
|
0.9,1, 5.9, |
|
0.9,0, 6.9, |
|
1.0,0, 7.0 |
|
]; |
|
|
|
const vertexShader = `#version 100 |
|
attribute vec2 position; |
|
attribute float dist; |
|
|
|
uniform float progress; |
|
uniform float totalDist; |
|
uniform mat4 projModelMat; |
|
|
|
varying float v_dist; |
|
varying float scaledDist; |
|
|
|
void main() { |
|
gl_Position = projModelMat * vec4(position.x,0.,position.y,1.); |
|
v_dist = dist; |
|
scaledDist = totalDist * progress; |
|
} |
|
`; |
|
const fragmentShader = `#version 100 |
|
precision mediump float; |
|
|
|
varying float scaledDist; |
|
varying float v_dist; |
|
|
|
void main() { |
|
float alpha = 1. - step(scaledDist, v_dist); |
|
gl_FragColor = vec4(mix( |
|
vec3(.5,.5,.5), |
|
vec3(0.,1.,0.), |
|
alpha |
|
),1.); |
|
} |
|
`; |
|
|
|
let gl; |
|
let vbo; |
|
let shader; |
|
|
|
let projMat = mat4.create(); |
|
let modelMat = mat4.create(); |
|
let projModelMat = mat4.create(); |
|
|
|
let totalLength = 0; |
|
let start = 0; |
|
let lastFrame = 0; |
|
|
|
let maxTime = 10.0; |
|
let elapsedTime = 0.0; |
|
let isAnimating = false; |
|
|
|
let cameraAngle = 0.25; |
|
let isFixed = false; |
|
|
|
function init() { |
|
let canvas = document.querySelector("#c"); |
|
let mouseDown = false; |
|
let lastMouseX = 0; |
|
canvas.onmousedown = (e) => { mouseDown = true; lastMouseX = e.clientX; }; |
|
canvas.onmouseup = () => { mouseDown = false }; |
|
canvas.onmousemove = (e) => { |
|
if(mouseDown) { cameraAngle = ((e.clientX - lastMouseX) / 50) % (Math.PI * 2) } |
|
}; |
|
gl = canvas.getContext("webgl") || canvas.getContext("experimental-webgl"); |
|
|
|
// setup camera matrices |
|
let aspect = canvas.width / canvas.height; |
|
let scl = 3; |
|
mat4.ortho(projMat, -scl * aspect, scl * aspect, -scl, scl, -10, 10); |
|
mat4.identity(modelMat); |
|
|
|
// setup state |
|
gl.clearColor(0,0,0,0); |
|
gl.enable(gl.DEPTH_TEST); |
|
gl.cullFace(gl.BACK); |
|
gl.lineWidth(Math.min(5,gl.getParameter(gl.ALIASED_LINE_WIDTH_RANGE))); |
|
|
|
// load shader and get uniform/attribute locations |
|
initShader(); |
|
shader.uniforms = {}; |
|
shader.attributes = {}; |
|
|
|
gl.useProgram(shader); |
|
shader.uniforms["progress"] = gl.getUniformLocation(shader, "progress"); |
|
shader.uniforms["totalDist"] = gl.getUniformLocation(shader, "totalDist"); |
|
shader.uniforms["projModelMat"] = gl.getUniformLocation(shader, "projModelMat"); |
|
shader.attributes["position"] = gl.getAttribLocation(shader, "position"); |
|
shader.attributes["dist"] = gl.getAttribLocation(shader, "dist"); |
|
|
|
// calculate length |
|
totalLength = points[points.length-1]; |
|
gl.uniform1f(shader.uniforms["totalDist"], totalLength); |
|
|
|
// load to vbo |
|
vbo = gl.createBuffer(); |
|
gl.bindBuffer(gl.ARRAY_BUFFER, vbo); |
|
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(points), gl.STATIC_DRAW); |
|
gl.vertexAttribPointer(shader.attributes["position"], 2, gl.FLOAT, false, 3 * 4, 0); |
|
gl.vertexAttribPointer(shader.attributes["dist"], 1, gl.FLOAT, false, 3 * 4, 2 * 4); |
|
gl.bindBuffer(gl.ARRAY_BUFFER, null); |
|
|
|
// calculate start time and draws frame |
|
start = Date.now(); |
|
lastFrame = (Date.now() - start) / 1000; |
|
requestAnimationFrame(draw); |
|
} |
|
|
|
function draw() { |
|
// calculate delta |
|
let time = (Date.now() - start) / 1000; |
|
let delta = time - lastFrame; |
|
lastFrame = time; |
|
|
|
// update model matrix |
|
if(!isFixed) { |
|
let cameraPos = [Math.cos(cameraAngle)*2.5,1,Math.sin(cameraAngle)*2.5]; |
|
mat4.lookAt(modelMat, cameraPos, [2,0,0], [0,1,0]); |
|
} else { |
|
mat4.identity(modelMat); |
|
mat4.translate(modelMat, modelMat, [-4,0,4]); |
|
} |
|
mat4.scale(modelMat, modelMat, [4,1,1]); |
|
|
|
// update progress |
|
if(isAnimating) { |
|
elapsedTime += delta; |
|
if(elapsedTime >= maxTime) { |
|
elapsedTime = maxTime; |
|
isAnimating = false; |
|
} |
|
} |
|
gl.uniform1f(shader.uniforms["progress"], elapsedTime / maxTime); |
|
|
|
// calculate projModelMat |
|
mat4.mul(projModelMat, projMat, modelMat); |
|
gl.uniformMatrix4fv(shader.uniforms["projModelMat"], false, projModelMat); |
|
|
|
// draw progress bar |
|
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); |
|
gl.bindBuffer(gl.ARRAY_BUFFER, vbo); |
|
gl.enableVertexAttribArray(shader.attributes["position"]); |
|
gl.enableVertexAttribArray(shader.attributes["dist"]); |
|
gl.drawArrays(gl.LINE_STRIP, 0, points.length / 3); |
|
gl.disableVertexAttribArray(shader.attributes["position"]); |
|
gl.disableVertexAttribArray(shader.attributes["dist"]); |
|
gl.bindBuffer(gl.ARRAY_BUFFER, null); |
|
|
|
// redraw frame |
|
requestAnimationFrame(draw); |
|
} |
|
|
|
function initShader() { |
|
shader = gl.createProgram(); |
|
vs = gl.createShader(gl.VERTEX_SHADER); |
|
fs = gl.createShader(gl.FRAGMENT_SHADER); |
|
|
|
// load vertex shader |
|
gl.shaderSource(vs, vertexShader); |
|
gl.compileShader(vs); |
|
if(!gl.getShaderParameter(vs, gl.COMPILE_STATUS)) { |
|
console.error("vertex:", gl.getShaderInfoLog(vs)); |
|
} |
|
|
|
// load fragment shader |
|
gl.shaderSource(fs, fragmentShader); |
|
gl.compileShader(fs); |
|
if(!gl.getShaderParameter(fs, gl.COMPILE_STATUS)) { |
|
console.error("fragment:", gl.getShaderInfoLog(fs)); |
|
} |
|
|
|
// link program |
|
gl.attachShader(shader, vs); |
|
gl.attachShader(shader, fs); |
|
gl.linkProgram(shader); |
|
if(!gl.getProgramParameter(shader, gl.LINK_STATUS)) { |
|
console.error("linking:", gl.getProgramInfoLog(shader)); |
|
} |
|
|
|
// delete shaders |
|
gl.detachShader(shader, vs); |
|
gl.detachShader(shader, fs); |
|
gl.deleteShader(vs); |
|
gl.deleteShader(fs); |
|
} |
|
|
|
document.getElementById("title").onclick = () => { |
|
this.innerText = "Loading, please wait..."; |
|
isAnimating = true; |
|
elapsedTime = 0.0; |
|
}; |
|
|
|
function toggleView() { |
|
isFixed = !isFixed; |
|
} |
|
|
|
init(); |