diff options
Diffstat (limited to 'lib')
| -rw-r--r-- | lib/flower.js | 337 |
1 files changed, 337 insertions, 0 deletions
diff --git a/lib/flower.js b/lib/flower.js new file mode 100644 index 0000000..78b657a --- /dev/null +++ b/lib/flower.js @@ -0,0 +1,337 @@ +const internals = { + + // Constants + + kEntrypointElementId: 'entrypoint', // The id of the canvas element where we will render + kWidthRatio: 2.76, + kHeightRatio: 1, + kTargetVerticalResolution: 32, + kFieldOfView: 45, + kNearLimit: 0.1, + kFarLimit: 100, + kBackgroundColor: [0.811, 0.73, 0.48, 1.0], + kRotationValues: [Math.random(), Math.random(), Math.random()], + kFPS: 30, + + // Constants: Shaders + + kVertexShader: ` + + attribute vec4 aVertexPosition; + attribute vec4 aColor; + + varying vec4 vColor; + + uniform mat4 uModelViewMatrix; + uniform mat4 uProjectionMatrix; + + void main() { + + gl_Position = uProjectionMatrix * uModelViewMatrix * aVertexPosition; + vColor = aColor; + } + `, + + kFragmentShader: ` + + precision mediump float; + varying vec4 vColor; + + void main() { + + gl_FragColor = vColor; + } + `, + + // Properties: Animation + + currentTime: 0, + + // Runs on load, Initializes WebGL and starts the animation loop. + + onLoad() { + + + const canvas = internals.initializeCanvas(); + const {gl, programInfo, buffers} = internals.initializeWebGL(canvas); + + window.requestAnimationFrame(internals.animate.bind(this, gl, programInfo, buffers)); + }, + + // Gets a canvas element and sets up resizing events. + + initializeCanvas() { + + const canvas = document.getElementById(internals.kEntrypointElementId); + internals.resizeCanvas(canvas); + window.addEventListener('resize', () => internals.resizeCanvas(canvas)); + + return canvas; + }, + + // Given a canvas, it will initialize the webgl context and set up the shaders. + + initializeWebGL(canvas) { + + const gl = canvas.getContext('webgl', { + preserveDrawingBuffer: true + }); + const shaderProgram = internals.initializeShaderProgram(gl, internals.kVertexShader, internals.kFragmentShader); + + const programInfo = { + program: shaderProgram, + attribLocations: { + vertexPosition: gl.getAttribLocation(shaderProgram, 'aVertexPosition'), + vertexColor: gl.getAttribLocation(shaderProgram, 'aColor') + }, + uniformLocations: { + projectionMatrix: gl.getUniformLocation(shaderProgram, 'uProjectionMatrix'), + modelViewMatrix: gl.getUniformLocation(shaderProgram, 'uModelViewMatrix') + } + }; + + const buffers = internals.initializeBuffers(gl); + + return { gl, programInfo, buffers}; + }, + + // Methods: Canvas + + resizeCanvas(canvas) { + + let width = window.innerWidth; + let height = Math.round(width * internals.kHeightRatio / internals.kWidthRatio); + + if (window.innerHeight < height) { + height = window.innerHeight; + width = Math.round(height * internals.kWidthRatio / internals.kHeightRatio); + console.log(width, height); + } + + canvas.style.width = `${width}px`; + canvas.style.height = `${height}px`; + + canvas.width = internals.kTargetVerticalResolution * internals.kWidthRatio; + canvas.height = internals.kTargetVerticalResolution; + + canvas.width = width; + canvas.height = height; + }, + + // Methods: Shader / GL Related + + initializeShaderProgram(gl, vertexShaderSource, fragmentShaderSource) { + + const vertexShader = internals.loadShader(gl, gl.VERTEX_SHADER, vertexShaderSource); + const fragmentShader = internals.loadShader(gl, gl.FRAGMENT_SHADER, fragmentShaderSource); + + const shaderProgram = gl.createProgram(); + gl.attachShader(shaderProgram, vertexShader); + gl.attachShader(shaderProgram, fragmentShader); + + gl.linkProgram(shaderProgram); + + if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) { + alert(`Unable to initialize shader program: ${gl.getProgramInfoLog(shaderProgram)}`); + return null; + } + + return shaderProgram; + }, + + loadShader(gl, type, source) { + + const shader = gl.createShader(type); + gl.shaderSource(shader, source); + gl.compileShader(shader); + + if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) { + alert(`Unable to compile shader: ${gl.getShaderInfoLog(shader)}`); + gl.deleteShader(shader); + return null; + } + + return shader; + }, + + initializeBuffers(gl) { + + const positionBuffer = gl.createBuffer(); + + gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer); + + const positions = [ + -1.0, 1.0, 1.0, // Front-top-left + 1.0, 1.0, 1.0, // Front-top-right + -1.0, -1.0, 1.0, // Front-bottom-left + 1.0, -1.0, 1.0, // Front-bottom-right + 1.0, -1.0, -1.0, // Back-bottom-right + 1.0, 1.0, 1.0, // Front-top-right + 1.0, 1.0, -1.0, // Back-top-right + -1.0, 1.0, 1.0, // Front-top-left + -1.0, 1.0, -1.0, // Back-top-left + -1.0, -1.0, 1.0, // Front-bottom-left + -1.0, -1.0, -1.0, // Back-bottom-left + 1.0, -1.0, -1.0, // Back-bottom-right + -1.0, 1.0, -1.0, // Back-top-left + 1.0, 1.0, -1.0 // Back-top-right + ]; + + gl.bufferData(gl.ARRAY_BUFFER, + new Float32Array(positions), + gl.STATIC_DRAW); + + return { + position: positionBuffer + } + }, + + // Methods: Utility + + createMatrix() { + + const matrix = mat4.create(); + + mat4.translate( + matrix, + matrix, + [-0.0, 0.0, -6.0] + ); + + return matrix; + }, + + // Methods: Animation + + rotate(matrix) { + + mat4.rotate(internals.modelViewMatrix, internals.modelViewMatrix, internals.kRotationValues[0], [1, 0, 0]); + mat4.rotate(internals.modelViewMatrix, internals.modelViewMatrix, internals.kRotationValues[1], [0, 1, 0]); + mat4.rotate(internals.modelViewMatrix, internals.modelViewMatrix, internals.kRotationValues[2], [0, 0, 1]); + }, + + drawScene(gl, programInfo, buffers) { + + gl.clearColor(...internals.kBackgroundColor); + gl.clearDepth(1.0); + gl.enable(gl.DEPTH_TEST); + gl.depthFunc(gl.LEQUAL); + + const fieldOfView = internals.kFieldOfView * Math.PI / 180; + const aspectRatio = internals.kWidthRatio / internals.kHeightRatio; + const projectionMatrix = mat4.create(); + + mat4.perspective( + projectionMatrix, + fieldOfView, + aspectRatio, + internals.kNearLimit, + internals.kFarLimit + ); + + internals.modelViewMatrix = internals.modelViewMatrix || internals.createMatrix(); + + internals.rotate(internals.modelViewMatrix); + + { + const numberOfComponents = 3; + const type = gl.FLOAT; + const normalize = false; + const stride = 0; + const offset = 0; + + gl.bindBuffer(gl.ARRAY_BUFFER, buffers.position); + gl.vertexAttribPointer( + programInfo.attribLocations.vertexPosition, + numberOfComponents, + type, + normalize, + stride, + offset + ); + gl.enableVertexAttribArray(programInfo.attribLocations.vertexPosition); + } + + { + // ATTEMPT TO SEND COLOR DATA + + const colorBuffer = gl.createBuffer(); + + gl.bindBuffer(gl.ARRAY_BUFFER, colorBuffer); + + const colors = [ + 1.0, 0.0, 0.5, 1.0, // Front-top-left + 1.0, 0.3, 0.8, 1.0, // Front-top-right + 1.0, 1.0, 0.7, 1.0, // Front-bottom-left + 0.0, 0.3, 0.5, 1.0, // Front-bottom-right + 0.0, 0.3, 0.5, 1.0, // Back-bottom-right + 1.0, 0.3, 0.8, 1.0, // Front-top-right + 1.0, 0.3, 0.8, 1.0, // Back-top-right + 1.0, 0.0, 0.5, 1.0, // Front-top-left + 1.0, 0.0, 0.5, 1.0, // Back-top-left + 1.0, 1.0, 0.7, 1.0, // Front-bottom-left + 1.0, 1.0, 0.7, 1.0, // Back-bottom-left + 0.0, 0.3, 0.5, 1.0, // Back-bottom-right + 1.0, 0.0, 0.5, 1.0, // Back-top-left + 1.0, 0.3, 0.8, 1.0 // Back-top-right + ]; + + gl.bufferData(gl.ARRAY_BUFFER, + new Float32Array(colors), + gl.STATIC_DRAW); + + const numberOfComponents = 4; + const type = gl.FLOAT; + const normalize = false; + const stride = 0; + const offset = 0; + + gl.bindBuffer(gl.ARRAY_BUFFER, colorBuffer); + gl.vertexAttribPointer( + programInfo.attribLocations.vertexColor, + numberOfComponents, + type, + normalize, + stride, + offset + ); + gl.enableVertexAttribArray(programInfo.attribLocations.vertexColor); + + // END ATTEMPT + } + + gl.useProgram(programInfo.program); + + gl.uniformMatrix4fv( + programInfo.uniformLocations.projectionMatrix, + false, + projectionMatrix + ); + gl.uniformMatrix4fv( + programInfo.uniformLocations.modelViewMatrix, + false, + internals.modelViewMatrix + ); + + { + const offset = 0; + const vertexCount = 14; + gl.drawArrays(gl.TRIANGLE_STRIP, offset, vertexCount); + } + }, + + animate(gl, programInfo, buffers, time) { + + const delta = time - internals.currentTime; + const interval = 1000 / internals.kFPS; + + if (delta >= interval) { + internals.drawScene(gl, programInfo, buffers, delta); + internals.currentTime = time; + } + + window.requestAnimationFrame(internals.animate.bind(this, gl, programInfo, buffers)); + } +}; + +window.addEventListener('load', internals.onLoad); |