From c4f3c7515448abbd0eb98782afbc3e43fc64d844 Mon Sep 17 00:00:00 2001 From: gbrochar Date: Thu, 26 Nov 2020 14:07:15 +0100 Subject: [PATCH] refacto : refacto ui and add env variable context --- config/webpack.client.dev.js | 2 +- src/client/changeshader.ts | 53 +++---- src/client/main.ts | 262 +++++++---------------------------- src/client/objutils.ts | 80 +++++++++++ src/client/shaders.ts | 29 ++-- src/client/texture.ts | 1 - src/client/uijquery.ts | 152 ++++++++++++++++++++ 7 files changed, 324 insertions(+), 255 deletions(-) create mode 100644 src/client/objutils.ts create mode 100644 src/client/uijquery.ts diff --git a/config/webpack.client.dev.js b/config/webpack.client.dev.js index aa3fc2f..fb15c9c 100644 --- a/config/webpack.client.dev.js +++ b/config/webpack.client.dev.js @@ -3,7 +3,7 @@ const { merge } = require('webpack-merge'); const common = require('./webpack.client.common.js'); module.exports = merge(common, { - devtool: 'eval-cheap-module-source-map', + devtool: 'source-map', mode: 'development', watch: true }); diff --git a/src/client/changeshader.ts b/src/client/changeshader.ts index b89e935..931cfa4 100644 --- a/src/client/changeshader.ts +++ b/src/client/changeshader.ts @@ -3,48 +3,41 @@ import {initShaderProgram, /** * - * @param {any} gl the gl context - * @param {any} shaderProgram the shader program - * @param {any} fragmentShader the current fragment shader + * @param {any} context the program context * @param {string} fsSource new fragment shader source * @param {string} vsSource current vsSource - * @return {any} the program info object */ -export function changeFragmentShader(gl: any, - shaderProgram: any, - fragmentShader: any, +export function changeFragmentShader(context: any, fsSource: string, vsSource: string) { - [shaderProgram, fragmentShader] = unlinkShaderProgram(gl, - fragmentShader, - shaderProgram); - [shaderProgram, fragmentShader] = initShaderProgram(gl, - vsSource, - fsSource)!; - const programInfo: any = { - program: shaderProgram, + [context.shaderProgram, context.fragmentShader] = + unlinkShaderProgram(context.gl, + context.fragmentShader, + context.shaderProgram); + initShaderProgram(context, vsSource, fsSource); + context.programInfo = { + program: context.shaderProgram, attribLocations: { - vertexPosition: gl.getAttribLocation(shaderProgram, + vertexPosition: context.gl.getAttribLocation(context.shaderProgram, 'aVertexPosition'), - vertexColor: gl.getAttribLocation(shaderProgram, + vertexColor: context.gl.getAttribLocation(context.shaderProgram, 'aVertexColor'), - vertexNormal: gl.getAttribLocation(shaderProgram, + vertexNormal: context.gl.getAttribLocation(context.shaderProgram, 'aVertexNormal'), - textureCoord: gl.getAttribLocation(shaderProgram, + textureCoord: context.gl.getAttribLocation(context.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'), + projectionMatrix: context.gl.getUniformLocation( + context.shaderProgram, 'uProjectionMatrix'), + viewMatrix: context.gl.getUniformLocation( + context.shaderProgram, 'uviewMatrix'), + modelMatrix: context.gl.getUniformLocation( + context.shaderProgram, 'umodelMatrix'), + normalModelMatrix: context.gl.getUniformLocation( + context.shaderProgram, 'unormalModelMatrix'), + uSampler: context.gl.getUniformLocation( + context.shaderProgram, 'uSampler'), }, }; - return [programInfo, fragmentShader]; } diff --git a/src/client/main.ts b/src/client/main.ts index 6ac0173..bce21bb 100644 --- a/src/client/main.ts +++ b/src/client/main.ts @@ -1,55 +1,54 @@ -// @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 {fetchObj, updateObj} from './objutils'; import {initShaderProgram} from './shaders'; -import {changeFragmentShader} from './changeshader'; import {loadTexture} from './texture'; import {initMatrices} from './matrix'; import {initVertexAttribs} from './vertexattrib'; +import {uiUpdateParams, + uiUpdateTexture, + uiUpdateObject, + uiUpdateShader} from './uijquery'; + main(); /** * The program purpose is encapsulated in a main function */ async function main() { + const context: any = { + gl: null, + texture: null, + params: null, + buffers: null, + programInfo: null, + shaderProgram: null, + fragmentShader: null, + vertexShader: null, + }; const canvas: any = document.querySelector('#glCanvas')!; - const gl = canvas.getContext('webgl'); + context.gl = canvas.getContext('webgl'); - if (gl == null) { + if (context.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 data = await fetchObj('/static/objs/racer.obj'); const distance: any = $('#distance').val(); const distanceFine: any = $('#distancefine').val(); const instanceNumber: any = $('#instance').val(); - const params: any = { + context.params = { distance: parseFloat(distance) + parseFloat(distanceFine), circleSize: $('#circlesize').val(), fov: $('#fov').val(), + length: 0, avg: { x: 0, y: 0, @@ -65,38 +64,35 @@ async function main() { instanceNumber: parseInt(instanceNumber), squareRotation: 0, }; - let length: number = 0; - let buffers: any; - updateObj(data, true); + [context.buffers, context.params] = updateObj( + context.gl, data, context.buffers, context.params, true); - let [shaderProgram, fragmentShader]: any = initShaderProgram(gl, - vsSource, - fsSource4); - let programInfo: any = { - program: shaderProgram, + initShaderProgram(context, vsSource, fsSource4); + context.programInfo = { + program: context.shaderProgram, attribLocations: { - vertexPosition: gl.getAttribLocation(shaderProgram, + vertexPosition: context.gl.getAttribLocation(context.shaderProgram, 'aVertexPosition'), - vertexNormal: gl.getAttribLocation(shaderProgram, + vertexNormal: context.gl.getAttribLocation(context.shaderProgram, 'aVertexNormal'), - textureCoord: gl.getAttribLocation(shaderProgram, + textureCoord: context.gl.getAttribLocation(context.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'), + projectionMatrix: context.gl.getUniformLocation( + context.shaderProgram, 'uProjectionMatrix'), + viewMatrix: context.gl.getUniformLocation( + context.shaderProgram, 'uviewMatrix'), + modelMatrix: context.gl.getUniformLocation( + context.shaderProgram, 'umodelMatrix'), + normalModelMatrix: context.gl.getUniformLocation( + context.shaderProgram, 'unormalModelMatrix'), + uSampler: context.gl.getUniformLocation( + context.shaderProgram, 'uSampler'), }, }; - let texture = loadTexture(gl, '/static/textures/racer.png'); + context.texture = loadTexture(context.gl, '/static/textures/racer.png'); let then = 0; let changed = false; @@ -107,174 +103,22 @@ async function main() { function render(now: any) { now *= 0.001; const deltaTime = now - then; - params.squareRotation += deltaTime; + context.params.squareRotation += deltaTime * context.params.rotSpeed; if (now >= 1 && changed == false) { changed = true; } then = now; - drawScene(gl, - programInfo, - buffers, - length, - params, - texture); + drawScene(context.gl, + context.programInfo, + context.buffers, + context.params, + context.texture); requestAnimationFrame(render); } - - /** - * Pushes a new obj file to the gl buffer - * @param {string} data the obj file to push - * @param {boolean} firstCall is it first object updated ? - */ - function updateObj(data: string, firstCall: boolean = false) { - 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); - if (!firstCall) { - 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'); - }); - }); + uiUpdateParams(context.params); + uiUpdateTexture(context); + uiUpdateObject(context); + uiUpdateShader(context); requestAnimationFrame(render); } @@ -283,14 +127,12 @@ async function main() { * @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); @@ -330,7 +172,7 @@ function drawScene(gl: any, programInfo.uniformLocations.normalModelMatrix, false, normalModelMatrix[i]); - const vertexCount = length; + const vertexCount = params.length; const type = gl.UNSIGNED_SHORT; const offset = 0; gl.drawElements(gl.TRIANGLES, vertexCount, type, offset); diff --git a/src/client/objutils.ts b/src/client/objutils.ts new file mode 100644 index 0000000..ceaeb22 --- /dev/null +++ b/src/client/objutils.ts @@ -0,0 +1,80 @@ +// @ts-ignore +import {convert} from './objparser'; +import {initBuffers, deleteBuffers} from './buffers'; + +/** + * Fetch an obj file + * @param {string} url the url to fetch the object from + * @return {string} the raw data of the obj file + */ +export async function fetchObj(url: string) { + const response = await fetch(url); + const data = await response.text(); + return data; +} + +/** + * Pushes a new obj file to the gl buffer + * @param {any} gl the WebGL context + * @param {string} data the obj file to push + * @param {any} buffers the buffers to be updated + * @param {any} params the params to be updated + * @param {boolean} firstCall is it first object updated ? + * @return {Array} the updated buffers and params + */ +export function updateObj( + gl: any, + data: string, + buffers: any, + params: any, + firstCall: boolean = false) { + const [ + positions, + normals, + uvs, + indices, + ] = convert(data); + params.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); + if (!firstCall) { + deleteBuffers(gl, buffers); + } + buffers = initBuffers(gl, positions, indices, normals, uvs); + return [buffers, params]; +} diff --git a/src/client/shaders.ts b/src/client/shaders.ts index ae6ebf2..f02eac1 100644 --- a/src/client/shaders.ts +++ b/src/client/shaders.ts @@ -13,28 +13,31 @@ export function unlinkShaderProgram(gl: any, shader: any, shaderProgram: any) { /** * Initialize a shader program, so WebGL knows how to draw our data - * @param {any} gl the WebGL context + * @param {any} context the program context * @param {string} vsSource the vertex shader source * @param {string} fsSource the fragment shader source - * @return {any} the shader program */ -export function initShaderProgram(gl: any, vsSource: string, fsSource: string) { - const vertexShader = loadShader(gl, gl.VERTEX_SHADER, vsSource); - const fragmentShader = loadShader(gl, gl.FRAGMENT_SHADER, fsSource); +export function initShaderProgram(context: any, + vsSource: string, + fsSource: string) { + context.vertexShader = loadShader(context.gl, + context.gl.VERTEX_SHADER, vsSource); + context.fragmentShader = loadShader(context.gl, + context.gl.FRAGMENT_SHADER, fsSource); // Create the shader program - const shaderProgram = gl.createProgram(); - gl.attachShader(shaderProgram, vertexShader); - gl.attachShader(shaderProgram, fragmentShader); - gl.linkProgram(shaderProgram); + context.shaderProgram = context.gl.createProgram(); + context.gl.attachShader(context.shaderProgram, context.vertexShader); + context.gl.attachShader(context.shaderProgram, context.fragmentShader); + context.gl.linkProgram(context.shaderProgram); // If creating the shader program failed, alert - if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) { + if (!context.gl.getProgramParameter(context.shaderProgram, + context.gl.LINK_STATUS)) { alert('Unable to initialize the shader program: ' + - gl.getProgramInfoLog(shaderProgram)); - return null; + context.gl.getProgramInfoLog(context.shaderProgram)); + context.shaderProgram = null; } - return [shaderProgram, fragmentShader]; } /** diff --git a/src/client/texture.ts b/src/client/texture.ts index 5d592ac..1a08670 100644 --- a/src/client/texture.ts +++ b/src/client/texture.ts @@ -24,7 +24,6 @@ export function loadTexture(gl: any, url: any) { gl.texImage2D(gl.TEXTURE_2D, level, internalFormat, width, height, border, srcFormat, srcType, pixel); - const image = new Image(); image.onload = function() { gl.bindTexture(gl.TEXTURE_2D, texture); diff --git a/src/client/uijquery.ts b/src/client/uijquery.ts new file mode 100644 index 0000000..f4d2c75 --- /dev/null +++ b/src/client/uijquery.ts @@ -0,0 +1,152 @@ +import $ from 'jquery'; +import {loadTexture} from './texture'; +import {fetchObj, updateObj} from './objutils'; +import {changeFragmentShader} from './changeshader'; + +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'; + +/** + * UI block for updating parameters + * @param {any} params the parameters to be tweaked + */ +export function uiUpdateParams(params: any) { + $('#distance').on('input', () => { + const distance: any = $('#distance').val(); + const distanceFine: any = $('#distancefine').val(); + params.distance = parseFloat(distance) + parseFloat(distanceFine); + }); + $('#distancefine').on('input', () => { + const distance: any = $('#distance').val(); + const distanceFine: any = $('#distancefine').val(); + params.distance = parseFloat(distance) + parseFloat(distanceFine); + }); + $('#circlesize').on('input', () => { + const circleSize: any = $('#circlesize').val(); + params.circleSize = parseFloat(circleSize); + }); + $('#instance').on('input', () => { + const instance: any = $('#instance').val(); + params.instanceNumber = parseInt(instance); + }); + $('#rotx').on('input', () => { + const rotx: any = $('#rotx').val(); + params.rot.x = parseFloat(rotx); + }); + $('#roty').on('input', () => { + const roty: any = $('#roty').val(); + params.rot.y = parseFloat(roty); + }); + $('#rotz').on('input', () => { + const rotz: any = $('#rotz').val(); + params.rot.z = parseFloat(rotz); + }); + $('#rotspeed').on('input', () => { + console.log('salut'); + const rotSpeed: any = $('#rotspeed').val(); + params.rotSpeed = parseFloat(rotSpeed); + }); + $('#fov').on('input', () => { + const fov: any = $('#fov').val(); + params.fov = parseFloat(fov); + }); +} + +/** + * UI block to update texture + * @param {any} context the program context + */ +export function uiUpdateTexture(context: any) { + $('#t_wall').on('click', () => { + context.texture = loadTexture(context.gl, + '/static/textures/wall.png'); + }); + $('#t_ice').on('click', () => { + context.texture = loadTexture(context.gl, + '/static/textures/ice.png'); + }); + $('#t_noise').on('click', () => { + context.texture = loadTexture(context.gl, + '/static/textures/noise.png'); + }); + $('#t_fox').on('click', () => { + context.texture = loadTexture(context.gl, + '/static/textures/fox.png'); + }); + $('#t_racer').on('click', () => { + context.texture = loadTexture(context.gl, + '/static/textures/racer.png'); + }); + $('#t_racer_wireframe').on('click', () => { + context.texture = loadTexture(context.gl, + '/static/textures/racer_wireframe.png'); + }); +} + +/** + * UI block to update object + * @param {any} context the program context + */ +export function uiUpdateObject(context: any) { + $('#o_sphere').on('click', async function() { + const data = await fetchObj('/static/objs/sphere.obj'); + [context.buffers, context.params] = updateObj(context.gl, + data, + context.buffers, + context.params); + }); + $('#o_teapot').on('click', async function() { + const data = await fetchObj('/static/objs/teapot.obj'); + [context.buffers, context.params] = updateObj(context.gl, + data, + context.buffers, + context.params); + }); + $('#o_fox').on('click', async function() { + const data = await fetchObj('/static/objs/fox.obj'); + [context.buffers, context.params] = updateObj(context.gl, + data, + context.buffers, + context.params); + }); + $('#o_mecha').on('click', async function() { + const data = await fetchObj('/static/objs/mecha.obj'); + [context.buffers, context.params] = updateObj(context.gl, + data, + context.buffers, + context.params); + }); + $('#o_racer').on('click', async function() { + const data = await fetchObj('/static/objs/racer.obj'); + [context.buffers, context.params] = updateObj(context.gl, + data, + context.buffers, + context.params); + }); +} + +/** + * UI block to update shader + * @param {any} context the program context + */ +export function uiUpdateShader(context: any) { + $('#s_blackandwhite').on('click', function() { + changeFragmentShader(context, fsSource, vsSource); + }); + $('#s_color').on('click', function() { + changeFragmentShader(context, fsSource2, vsSource); + }); + $('#s_grey').on('click', function() { + changeFragmentShader(context, fsSource3, vsSource); + }); + $('#s_texture').on('click', function() { + changeFragmentShader(context, fsSource4, vsSource); + }); + $('#s_sobel').on('click', function() { + changeFragmentShader(context, fsSource5, vsSource); + }); +}