// @ts-ignore import {convert} from './objparser'; import $ from 'jquery'; import vsSource from './shaders/shader.vert'; import fsSource from './shaders/blackandwhite.frag'; import fsSource2 from './shaders/colored.frag'; import fsSource3 from './shaders/grey.frag'; import fsSource4 from './shaders/texture.frag'; import fsSource5 from './shaders/sobel.frag'; import {initBuffers, deleteBuffers} from './buffers'; import {initShaderProgram} from './shaders'; import {changeFragmentShader} from './changeshader'; import {loadTexture} from './texture'; import {initMatrices} from './matrix'; import {initVertexAttribs} from './vertexattrib'; main(); /** * The program purpose is encapsulated in a main function */ async function main() { const canvas: any = document.querySelector('#glCanvas')!; const gl = canvas.getContext('webgl'); if (gl == null) { canvas.parentNode.removeChild(canvas); document.getElementById('root')!.insertAdjacentHTML('beforeend', `

Unable to initialize WebGL. Your browser or machine may not support it.

`); } /** * Fetch an obj file * @param {string} url the url to fetch the object from * @return {string} the raw data of the obj file */ async function getObj(url: string) { const response = await fetch(url); const data = await response.text(); return data; } const data = await getObj('/static/objs/racer.obj'); const distance: any = $('#distance').val(); const distanceFine: any = $('#distancefine').val(); const instanceNumber: any = $('#instance').val(); const params: any = { distance: parseFloat(distance) + parseFloat(distanceFine), circleSize: $('#circlesize').val(), fov: $('#fov').val(), avg: { x: 0, y: 0, z: 0, }, range: 0, rot: { x: $('#rotx').val(), y: $('#roty').val(), z: $('#rotz').val(), }, rotSpeed: $('#rotspeed').val(), instanceNumber: parseInt(instanceNumber), squareRotation: 0, }; const [ positions, normals, uvs, indices, ] = convert(data); let x = 0; let y = 0; let z = 0; let maxx = positions[0]; let maxy = positions[1]; let maxz = positions[2]; let minx = positions[0]; let miny = positions[1]; let minz = positions[2]; for (let i = 0; i < positions.length; i++) { if (i % 3 == 0) { if (positions[i] > maxx) { maxx = positions[i]; } else if (positions[i] < minx) { minx = positions[i]; } x += positions[i]; } else if (i % 3 == 1) { if (positions[i] > maxy) { maxy = positions[i]; } else if (positions[i] < miny) { miny = positions[i]; } y += positions[i]; } else { if (positions[i] > maxz) { maxz = positions[i]; } else if (positions[i] < minz) { minz = positions[i]; } z += positions[i]; } } params.range = Math.max(maxx - minx, maxy - miny, maxz - minz); params.avg.x = x / (positions.length / 3); params.avg.y = y / (positions.length / 3); params.avg.z = z / (positions.length / 3); let length = indices.length; let [shaderProgram, fragmentShader]: any = initShaderProgram(gl, vsSource, fsSource4); let programInfo: any = { program: shaderProgram, attribLocations: { vertexPosition: gl.getAttribLocation(shaderProgram, 'aVertexPosition'), vertexNormal: gl.getAttribLocation(shaderProgram, 'aVertexNormal'), textureCoord: gl.getAttribLocation(shaderProgram, 'aTextureCoord'), }, uniformLocations: { projectionMatrix: gl.getUniformLocation( shaderProgram, 'uProjectionMatrix'), viewMatrix: gl.getUniformLocation( shaderProgram, 'uviewMatrix'), modelMatrix: gl.getUniformLocation( shaderProgram, 'umodelMatrix'), normalModelMatrix: gl.getUniformLocation( shaderProgram, 'unormalModelMatrix'), uSampler: gl.getUniformLocation( shaderProgram, 'uSampler'), }, }; let texture = loadTexture(gl, '/static/textures/racer.png'); let buffers = initBuffers(gl, positions, indices, normals, uvs); let then = 0; let changed = false; /** * Draws the scene repeatedly * @param {number} now the current time */ function render(now: any) { now *= 0.001; const deltaTime = now - then; params.squareRotation += deltaTime; if (now >= 1 && changed == false) { changed = true; } then = now; drawScene(gl, programInfo, buffers, length, params, texture); requestAnimationFrame(render); } /** * Pushes a new obj file to the gl buffer * @param {string} data the obj file to push */ function updateObj(data: string) { const [ positions, normals, uvs, indices, ] = convert(data); length = indices.length; let x = 0; let y = 0; let z = 0; let maxx = positions[0]; let maxy = positions[1]; let maxz = positions[2]; let minx = positions[0]; let miny = positions[1]; let minz = positions[2]; for (let i = 0; i < positions.length; i++) { if (i % 3 == 0) { if (positions[i] > maxx) { maxx = positions[i]; } else if (positions[i] < minx) { minx = positions[i]; } x += positions[i]; } else if (i % 3 == 1) { if (positions[i] > maxy) { maxy = positions[i]; } else if (positions[i] < miny) { miny = positions[i]; } y += positions[i]; } else { if (positions[i] > maxz) { maxz = positions[i]; } else if (positions[i] < minz) { minz = positions[i]; } z += positions[i]; } } params.range = Math.max(maxx - minx, maxy - miny, maxz - minz); params.avg.x = x / (positions.length / 3); params.avg.y = y / (positions.length / 3); params.avg.z = z / (positions.length / 3); deleteBuffers(gl, buffers); buffers = initBuffers(gl, positions, indices, normals, uvs); } $(function() { $('#distance').on('input', function() { const distance: any = $('#distance').val(); const distanceFine: any = $('#distancefine').val(); params.distance = parseFloat(distance) + parseFloat(distanceFine); }); $('#distancefine').on('input', function() { const distance: any = $('#distance').val(); const distanceFine: any = $('#distancefine').val(); params.distance = parseFloat(distance) + parseFloat(distanceFine); }); $('#circlesize').on('input', function() { const circleSize: any = $('#circlesize').val(); params.circleSize = parseFloat(circleSize); }); $('#instance').on('input', function() { const instance: any = $('#instance').val(); params.instanceNumber = parseInt(instance); }); $('#rotx').on('input', function() { const rotx: any = $('#rotx').val(); params.rot.x = parseFloat(rotx); }); $('#roty').on('input', function() { const roty: any = $('#roty').val(); params.rot.y = parseFloat(roty); }); $('#rotz').on('input', function() { const rotz: any = $('#rotz').val(); params.rot.z = parseFloat(rotz); }); $('#rotspeed').on('input', function() { const rotSpeed: any = $('#rotspeed').val(); params.rotSpeed = parseFloat(rotSpeed); }); $('#fov').on('input', function() { const fov: any = $('#fov').val(); params.fov = parseFloat(fov); }); $('#s_blackandwhite').on('click', function() { [programInfo, fragmentShader] = changeFragmentShader(gl, shaderProgram, fragmentShader, fsSource, vsSource); }); $('#s_color').on('click', function() { [programInfo, fragmentShader] = changeFragmentShader(gl, shaderProgram, fragmentShader, fsSource2, vsSource); }); $('#s_grey').on('click', function() { [programInfo, fragmentShader] = changeFragmentShader(gl, shaderProgram, fragmentShader, fsSource3, vsSource); }); $('#s_texture').on('click', function() { [programInfo, fragmentShader] = changeFragmentShader(gl, shaderProgram, fragmentShader, fsSource4, vsSource); }); $('#s_sobel').on('click', function() { [programInfo, fragmentShader] = changeFragmentShader(gl, shaderProgram, fragmentShader, fsSource5, vsSource); }); $('#o_sphere').on('click', async function() { const data = await getObj('/static/objs/sphere.obj'); updateObj(data); }); $('#o_teapot').on('click', async function() { const data = await getObj('/static/objs/teapot.obj'); updateObj(data); }); $('#o_fox').on('click', async function() { const data = await getObj('/static/objs/fox.obj'); updateObj(data); }); $('#o_mecha').on('click', async function() { const data = await getObj('/static/objs/mecha.obj'); updateObj(data); }); $('#o_racer').on('click', async function() { const data = await getObj('/static/objs/racer.obj'); updateObj(data); }); $('#t_wall').on('click', async function() { texture = loadTexture(gl, '/static/textures/wall.png'); }); $('#t_ice').on('click', async function() { texture = loadTexture(gl, '/static/textures/ice.png'); }); $('#t_noise').on('click', async function() { texture = loadTexture(gl, '/static/textures/noise.png'); }); $('#t_fox').on('click', async function() { texture = loadTexture(gl, '/static/textures/fox.png'); }); $('#t_racer').on('click', async function() { texture = loadTexture(gl, '/static/textures/racer.png'); }); $('#t_racer_wireframe').on('click', async function() { texture = loadTexture(gl, '/static/textures/racer_wireframe.png'); }); }); requestAnimationFrame(render); } /** * Draw a webgl scene * @param {any} gl the WebGL context * @param {any} programInfo WebGL program information * @param {any} buffers the buffers to draw * @param {number} length the index buffer length * @param {number} params various parameterss * @param {any} texture the texture to load */ function drawScene(gl: any, programInfo: any, buffers: any, length: number, params: any, texture: any) { gl.clearColor(0.0, 0.0, 0.0, 1.0); gl.clearDepth(1.0); gl.enable(gl.DEPTH_TEST); gl.depthFunc(gl.LEQUAL); gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); initVertexAttribs(gl, programInfo, buffers); gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, buffers.indices); gl.useProgram(programInfo.program); gl.activeTexture(gl.TEXTURE0); gl.bindTexture(gl.TEXTURE_2D, texture); gl.uniform1i(programInfo.uniformLocations.uSampler, 0); const [ projectionMatrix, viewMatrix, modelMatrix, normalModelMatrix, ] = initMatrices(gl, params); for (let i = 0; i < params.instanceNumber; i++) { gl.uniformMatrix4fv( programInfo.uniformLocations.projectionMatrix, false, projectionMatrix); gl.uniformMatrix4fv( programInfo.uniformLocations.viewMatrix, false, viewMatrix); gl.uniformMatrix4fv( programInfo.uniformLocations.modelMatrix, false, modelMatrix[i]); gl.uniformMatrix4fv( programInfo.uniformLocations.normalModelMatrix, false, normalModelMatrix[i]); const vertexCount = length; const type = gl.UNSIGNED_SHORT; const offset = 0; gl.drawElements(gl.TRIANGLES, vertexCount, type, offset); } }