let sphereData = []; Math.HALF_PI = Math.PI / 2; Math.TWO_PI = Math.PI * 2; let sphereObject = { rotationRaw: [0, 0, 0], // degrees rotation: [0, 0, 0], //radians rotateInc: [0.0, 0.0, 0.0], //degreesInc rotateByBeat: true, translate: [0, 0, 0], total: 50, radius: 500, color: {r: 0, g: 0, b: 0}, colorByBeat: true, drawMode: 5, sphereMode: 0, lightPos: [0, 0, 0], pointSize: 2, steps: 512, dirtyMode: false, light: 0.3 } function readDataBar() { let items = sphereObject.steps; let dataArray = new Uint8Array(items); analyser.getByteFrequencyData(dataArray); let arr = []; let sum = 0; for (let i = 0; i < items; i++) { let data = dataArray[i] / 255; arr.push(data * .5); sum += dataArray[i]; } return [arr, (sum / items) / 255]; } let sphereDataVectors = [], lastTotal = 0; // avoid garbage collection each run function prepareData() { let total = sphereObject.total; if (lastTotal !== total) { sphereDataVectors = []; for (let i = 0; i <= total; i++) { sphereDataVectors[i] = []; for (let j = 0; j <= total; j++) { sphereDataVectors[i][j] = new VTVector(); } } lastTotal = total; } } function setupSphere() { sphereData = []; let data = readDataBar(), radData = data[0], map = VTUtils.map, total = sphereObject.total, cTotal = (total + 1) * (total + 1), radius = sphereObject.radius, rx = radius / c.width, ry = radius / c.height, counter = 0; prepareData(); for (let i = 0; i <= total; i++) { let lat = map(i, 0, total, 0, Math.PI, false); for (let j = 0; j <= total; j++) { let lon = map(j, 0, total, 0, Math.TWO_PI, false); let rAdd = getAddRad(counter, radData, cTotal); let realRX = rx + rAdd; let realRY = ry + rAdd; let {x, y, z} = sphereMode(lat, lon, i, counter, realRX, realRY); if (sphereObject.drawMode < 2 || sphereObject.dirtyMode) { sphereData.push(x, y, z); } else { sphereDataVectors[i][j].setXYZ(x, y, z); } counter++; } } if (sphereObject.drawMode > 1 && !sphereObject.dirtyMode) { for (let i = 0; i < total; i++) { for (let j = 0; j <= total; j++) { let cur = sphereDataVectors[i][j]; sphereData.push(cur.x, cur.y, cur.z); cur = sphereDataVectors[i + 1][j]; sphereData.push(cur.x, cur.y, cur.z); } } } return [VTUtils.peakRGB(data[1] * 2), data[1]]; } function getAddRad(counter, data, total) { let mapping, rAdd, map = VTUtils.map; let h = total / 2; if (sphereObject.sphereMode === 3) { if (counter > h) { mapping = map(counter, h, total, data.length - 1, 0); } else { mapping = map(counter, 0, h, 0, data.length - 1); } rAdd = data[Math.round(mapping)] || 0; } else if (sphereObject.sphereMode === 4) { if (counter > h) { mapping = map(counter, h, total, 0, data.length - 1); } else { mapping = map(counter, 0, h, data.length - 1, 0); } rAdd = data[Math.round(mapping)] || 0; } else { mapping = map(counter, 0, total, 0, data.length - 1); rAdd = data[Math.round(mapping)] || 0; } return rAdd; } let position, color, rotate, light, lightPos; function prepare(program) { position = gl.getAttribLocation(program, "a_position"); color = gl.getUniformLocation(program, "u_color"); light = gl.getUniformLocation(program, "u_light"); rotate = gl.getUniformLocation(program, "u_matrix"); lightPos = gl.getUniformLocation(program, "u_lightPos"); let pointSize = gl.getUniformLocation(program, "u_pointSize"); gl.uniform3fv(light, [sphereObject.light, 0, 0]); gl.uniform3fv(lightPos, sphereObject.lightPos); gl.uniform1f(pointSize, sphereObject.pointSize); } function draw() { c.width = window.innerWidth; c.height = window.innerHeight; let aspect = c.height / c.width; let d = setupSphere(); let newColor = d[0]; let program = shaderHandler.getProgram("sphere"); gl.useProgram(program); prepare(program); let matrix = [ 1 / aspect, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, sphereObject.translate[0], sphereObject.translate[1], sphereObject.translate[2], 1 ] matrix = TDUtils.multiply(matrix, TDUtils.xRotation(sphereObject.rotation[0])); matrix = TDUtils.multiply(matrix, TDUtils.yRotation(sphereObject.rotation[1])); matrix = TDUtils.multiply(matrix, TDUtils.zRotation(sphereObject.rotation[2])); gl.uniformMatrix4fv(rotate, false, matrix); let positionBuffer = gl.createBuffer(); gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer); gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(sphereData), gl.DYNAMIC_DRAW); let vao = gl.createVertexArray(); gl.bindVertexArray(vao); gl.enableVertexAttribArray(position); gl.vertexAttribPointer(position, 3, gl.FLOAT, true, 0, 0); gl.clearColor(0, 0, 0, 1); gl.enable(gl.DEPTH_TEST); gl.depthFunc(gl.LEQUAL); gl.clearDepth(2.0) gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); gl.viewport(0, 0, gl.canvas.width, gl.canvas.height); if (sphereObject.colorByBeat) { gl.uniform4f(color, newColor.r, newColor.g, 0, 1); } else { let cx = sphereObject.color; gl.uniform4f(color, cx.r, cx.g, cx.b, 1); } gl.drawArrays(sphereObject.drawMode || gl.POINTS, 0, sphereData.length / 3); if (sphereObject.rotateByBeat) { sphereObject.rotationRaw[1] += d[1]; sphereObject.rotationRaw[0] += newColor.r; } else { for (let i = 0; i < 3; i++) { sphereObject.rotationRaw[i] += sphereObject.rotateInc[i]; } } for (let i = 0; i < 3; i++) { sphereObject.rotation[i] = TDUtils.degToRad(sphereObject.rotationRaw[i]) } requestAnimationFrame(draw); } function sphereMode(lat, lon, i, counter, rx, ry) { let x, y, z, sin = Math.sin, cos = Math.cos; switch (sphereObject.sphereMode) { case 1: x = rx * sin(lon) * cos(lat); y = ry * sin(lon) * sin(lat); z = ry * cos(lat); break; case 2: x = rx * sin(lat) * cos(lat); y = ry * sin(lat) * sin(lat); z = ry * cos(lon); break; default: x = rx * sin(lat) * cos(lon); y = ry * sin(lat) * sin(lon); z = ry * cos(lat); break; } return {x: x, y: y, z: z}; }