Marak / buddypond

Cloud OS and Instant Messenger
https://buddypond.com
Other
151 stars 17 forks source link

Add `Desktop.ButterChurn` audio visualization App #26

Open Marak opened 2 years ago

Marak commented 2 years ago

Butterchurn is really good viz library for doing audio visualizations in browser. We should add this as an App so we can have visualization in window. Butterchurn will work well with our existing audio sources and DJ playlists.

see: https://github.com/jberg/butterchurn

tom-space-churn

cc: @jberg come check out https://buddypond.com if you want to say hello and follow progress. Cheers 🥂

Marak commented 2 years ago

With updating desktop.ui and jquery.desktop.x windowing APIs it's pretty easy to place this into an iframe.

We went ahead and did that with Butterchurn being the default visualization for App.Visuals.

Good start.

Next, we'll want to expose the Butternchurn configurations to the UI. Would also like to see more integration such as having Butterchurn as a background wallpaper.

Marak commented 2 years ago
Screen Shot 2022-04-12 at 1 45 00 PM

Just noticed there is no error message if App.Visuals starts and doesn't have access to microphone ( such as when testing over non-https ). We'll want an alert to happen there.

We also still need the UX and HTML forms for Butternchurn exposed to the Buddies. It should be simple, it's all there and connected. Just needs some UX love.

Bounty has been set for 777.77 Good Buddy Points. We are in #Alpha room on Buddy Pond.

Marak commented 2 years ago

Would also like to see more integration such as having Butterchurn as a background wallpaper.

This is now possible through >Window Menu with ability to set any Desktop Window as Wallpaper.

Screen Shot 2022-05-08 at 6 10 06 PM
Lyzon0 commented 10 months ago

For a possible implementation of the web app this could be used { "name": "butterchurn", "version": "3.0.0-beta.3", "description": "Butterchurn is a WebGL implementation of the Milkdrop Visualizer", "main": "dist/butterchurn.js", "unpkg": "dist/butterchurn.js", "files": [ "dist/butterchurn.js", "dist/butterchurn.min.js", "dist/isSupported.min.js" ], "scripts": { "build": "webpack --config config/webpack.config.js --env prod", "dev": "webpack --config config/webpack.config.js --progress --colors --watch --env dev", "dev-build": "webpack --config config/webpack.config.js --progress --colors --env dev" }, "repository": { "type": "git", "url": "git+https://github.com/jberg/butterchurn.git" }, "keywords": [ "music visualization", "visualizer", "webgl", "webaudio" ], "author": "Jordan Berg jordannealberg@gmail.com", "license": "MIT", "homepage": "https://butterchurnviz.com", "dependencies": { "@assemblyscript/loader": "^0.17.11", "@babel/runtime": "^7.11.2", "ecma-proposal-math-extensions": "0.0.2", "eel-wasm": "^0.0.15" }, "devDependencies": { "@babel/core": "^7.11.6", "@babel/plugin-transform-runtime": "^7.11.5", "@babel/preset-env": "^7.11.5", "assemblyscript": "^0.17.11", "babel-eslint": "^10.1.0", "babel-loader": "^8.1.0", "eslint": "^7.9.0", "eslint-loader": "^4.0.2", "eslint-plugin-prettier": "^3.1.4", "prettier": "^2.1.2", "webpack": "^4.44.2", "webpack-cli": "^3.3.12", "yargs": "^16.0.3" }, "browserslist": "supports audio-api and supports webgl2 and last 3 years" }

Lyzon0 commented 10 months ago

Archive.zip Use this zip file to access put the code into the sec file

Lyzon0 commented 10 months ago

Audio levels


export default class AudioLevels {
  constructor(audio) {
    this.audio = audio;

    let sampleRate;
    if (this.audio.audioContext) {
      sampleRate = this.audio.audioContext.sampleRate;
    } else {
      sampleRate = 44100;
    }

    const bucketHz = sampleRate / this.audio.fftSize;

    const bassLow = Math.clamp(
      Math.round(20 / bucketHz) - 1,
      0,
      this.audio.numSamps - 1
    );
    const bassHigh = Math.clamp(
      Math.round(320 / bucketHz) - 1,
      0,
      this.audio.numSamps - 1
    );
    const midHigh = Math.clamp(
      Math.round(2800 / bucketHz) - 1,
      0,
      this.audio.numSamps - 1
    );
    const trebHigh = Math.clamp(
      Math.round(11025 / bucketHz) - 1,
      0,
      this.audio.numSamps - 1
    );

    this.starts = [bassLow, bassHigh, midHigh];
    this.stops = [bassHigh, midHigh, trebHigh];

    this.val = new Float32Array(3);
    this.imm = new Float32Array(3);
    this.att = new Float32Array(3);
    this.avg = new Float32Array(3);
    this.longAvg = new Float32Array(3);

    this.att.fill(1);
    this.avg.fill(1);
    this.longAvg.fill(1);
  }

  /* eslint-disable camelcase */
  get bass() {
    return this.val[0];
  }

  get bass_att() {
    return this.att[0];
  }

  get mid() {
    return this.val[1];
  }

  get mid_att() {
    return this.att[1];
  }

  get treb() {
    return this.val[2];
  }

  get treb_att() {
    return this.att[2];
  }
  /* eslint-enable camelcase */

  static isFiniteNumber(num) {
    return Number.isFinite(num) && !Number.isNaN(num);
  }

  static adjustRateToFPS(rate, baseFPS, FPS) {
    return rate ** (baseFPS / FPS);
  }

  updateAudioLevels(fps, frame) {
    if (this.audio.freqArray.length > 0) {
      let effectiveFPS = fps;
      if (!AudioLevels.isFiniteNumber(effectiveFPS) || effectiveFPS < 15) {
        effectiveFPS = 15;
      } else if (effectiveFPS > 144) {
        effectiveFPS = 144;
      }

      // Clear for next loop
      this.imm.fill(0);
      for (let i = 0; i < 3; i++) {
        for (let j = this.starts[i]; j < this.stops[i]; j++) {
          this.imm[i] += this.audio.freqArray[j];
        }
      }

      for (let i = 0; i < 3; i++) {
        let rate;
        if (this.imm[i] > this.avg[i]) {
          rate = 0.2;
        } else {
          rate = 0.5;
        }
        rate = AudioLevels.adjustRateToFPS(rate, 30.0, effectiveFPS);
        this.avg[i] = this.avg[i] * rate + this.imm[i] * (1 - rate);

        if (frame < 50) {
          rate = 0.9;
        } else {
          rate = 0.992;
        }
        rate = AudioLevels.adjustRateToFPS(rate, 30.0, effectiveFPS);
        this.longAvg[i] = this.longAvg[i] * rate + this.imm[i] * (1 - rate);

        if (this.longAvg[i] < 0.001) {
          this.val[i] = 1.0;
          this.att[i] = 1.0;
        } else {
          this.val[i] = this.imm[i] / this.longAvg[i];
          this.att[i] = this.avg[i] / this.longAvg[i];
        }
      }
    }
  }
}
Lyzon0 commented 10 months ago

Audio processor


import FFT from "./fft";

export default class AudioProcessor {
  constructor(context) {
    this.numSamps = 512;
    this.fftSize = this.numSamps * 2;

    this.fft = new FFT(this.fftSize, 512, true);

    if (context) {
      this.audioContext = context;
      this.audible = context.createDelay();

      this.analyser = context.createAnalyser();
      this.analyser.smoothingTimeConstant = 0.0;
      this.analyser.fftSize = this.fftSize;

      this.audible.connect(this.analyser);

      // Split channels
      this.analyserL = context.createAnalyser();
      this.analyserL.smoothingTimeConstant = 0.0;
      this.analyserL.fftSize = this.fftSize;

      this.analyserR = context.createAnalyser();
      this.analyserR.smoothingTimeConstant = 0.0;
      this.analyserR.fftSize = this.fftSize;

      this.splitter = context.createChannelSplitter(2);

      this.audible.connect(this.splitter);
      this.splitter.connect(this.analyserL, 0);
      this.splitter.connect(this.analyserR, 1);
    }

    // Initialised once as typed arrays
    // Used for webaudio API raw (time domain) samples. 0 -> 255
    this.timeByteArray = new Uint8Array(this.fftSize);
    this.timeByteArrayL = new Uint8Array(this.fftSize);
    this.timeByteArrayR = new Uint8Array(this.fftSize);

    // Signed raw samples shifted to -128 -> 127
    this.timeArray = new Int8Array(this.fftSize);
    this.timeByteArraySignedL = new Int8Array(this.fftSize);
    this.timeByteArraySignedR = new Int8Array(this.fftSize);

    // Temporary array for smoothing
    this.tempTimeArrayL = new Int8Array(this.fftSize);
    this.tempTimeArrayR = new Int8Array(this.fftSize);

    // Undersampled from this.fftSize to this.numSamps
    this.timeArrayL = new Int8Array(this.numSamps);
    this.timeArrayR = new Int8Array(this.numSamps);
  }

  sampleAudio() {
    this.analyser.getByteTimeDomainData(this.timeByteArray);
    this.analyserL.getByteTimeDomainData(this.timeByteArrayL);
    this.analyserR.getByteTimeDomainData(this.timeByteArrayR);
    this.processAudio();
  }
  updateAudio(timeByteArray, timeByteArrayL, timeByteArrayR) {
    this.timeByteArray.set(timeByteArray);
    this.timeByteArrayL.set(timeByteArrayL);
    this.timeByteArrayR.set(timeByteArrayR);
    this.processAudio();
  }
  /* eslint-disable no-bitwise */
  processAudio() {
    for (let i = 0, j = 0, lastIdx = 0; i < this.fftSize; i++) {
      // Shift Unsigned to Signed about 0
      this.timeArray[i] = this.timeByteArray[i] - 128;
      this.timeByteArraySignedL[i] = this.timeByteArrayL[i] - 128;
      this.timeByteArraySignedR[i] = this.timeByteArrayR[i] - 128;

      this.tempTimeArrayL[i] =
        0.5 *
        (this.timeByteArraySignedL[i] + this.timeByteArraySignedL[lastIdx]);
      this.tempTimeArrayR[i] =
        0.5 *
        (this.timeByteArraySignedR[i] + this.timeByteArraySignedR[lastIdx]);

      // Undersampled
      if (i % 2 === 0) {
        this.timeArrayL[j] = this.tempTimeArrayL[i];
        this.timeArrayR[j] = this.tempTimeArrayR[i];
        j += 1;
      }

      lastIdx = i;
    }

    // Use full width samples for the FFT
    this.freqArray = this.fft.timeToFrequencyDomain(this.timeArray);
    this.freqArrayL = this.fft.timeToFrequencyDomain(this.timeByteArraySignedL);
    this.freqArrayR = this.fft.timeToFrequencyDomain(this.timeByteArraySignedR);
  }

  connectAudio(audionode) {
    audionode.connect(this.audible);
  }

  disconnectAudio(audionode) {
    audionode.disconnect(this.audible);
  }
  /* eslint-enable no-bitwise */
}
Lyzon0 commented 10 months ago

Basic waveform


import ShaderUtils from "../shaders/shaderUtils";
import WaveUtils from "./waveUtils";

export default class BasicWaveform {
  constructor(gl, opts = {}) {
    this.gl = gl;

    const numAudioSamples = 512;
    this.positions = new Float32Array(numAudioSamples * 3);
    this.positions2 = new Float32Array(numAudioSamples * 3);
    this.oldPositions = new Float32Array(numAudioSamples * 3);
    this.oldPositions2 = new Float32Array(numAudioSamples * 3);
    this.smoothedPositions = new Float32Array((numAudioSamples * 2 - 1) * 3);
    this.smoothedPositions2 = new Float32Array((numAudioSamples * 2 - 1) * 3);
    this.color = [0, 0, 0, 1];

    this.texsizeX = opts.texsizeX;
    this.texsizeY = opts.texsizeY;
    this.aspectx = opts.aspectx;
    this.aspecty = opts.aspecty;
    this.invAspectx = 1.0 / this.aspectx;
    this.invAspecty = 1.0 / this.aspecty;

    this.floatPrecision = ShaderUtils.getFragmentFloatPrecision(this.gl);
    this.createShader();

    this.vertexBuf = this.gl.createBuffer();
  }

  updateGlobals(opts) {
    this.texsizeX = opts.texsizeX;
    this.texsizeY = opts.texsizeY;
    this.aspectx = opts.aspectx;
    this.aspecty = opts.aspecty;
    this.invAspectx = 1.0 / this.aspectx;
    this.invAspecty = 1.0 / this.aspecty;
  }

  createShader() {
    this.shaderProgram = this.gl.createProgram();

    const vertShader = this.gl.createShader(this.gl.VERTEX_SHADER);
    this.gl.shaderSource(
      vertShader,
      `
      #version 300 es
      in vec3 aPos;
      uniform vec2 thickOffset;
      void main(void) {
        gl_Position = vec4(aPos + vec3(thickOffset, 0.0), 1.0);
      }
      `.trim()
    );
    this.gl.compileShader(vertShader);

    const fragShader = this.gl.createShader(this.gl.FRAGMENT_SHADER);
    this.gl.shaderSource(
      fragShader,
      `
      #version 300 es
      precision ${this.floatPrecision} float;
      precision highp int;
      precision mediump sampler2D;
      out vec4 fragColor;
      uniform vec4 u_color;
      void main(void) {
        fragColor = u_color;
      }
      `.trim()
    );
    this.gl.compileShader(fragShader);

    this.gl.attachShader(this.shaderProgram, vertShader);
    this.gl.attachShader(this.shaderProgram, fragShader);
    this.gl.linkProgram(this.shaderProgram);

    this.aPosLoc = this.gl.getAttribLocation(this.shaderProgram, "aPos");

    this.colorLoc = this.gl.getUniformLocation(this.shaderProgram, "u_color");
    this.thickOffsetLoc = this.gl.getUniformLocation(
      this.shaderProgram,
      "thickOffset"
    );
  }

  static processWaveform(timeArray, mdVSFrame) {
    const waveform = [];

    const scale = mdVSFrame.wave_scale / 128.0;
    const smooth = mdVSFrame.wave_smoothing;
    const smooth2 = scale * (1.0 - smooth);

    waveform.push(timeArray[0] * scale);
    for (let i = 1; i < timeArray.length; i++) {
      waveform.push(timeArray[i] * smooth2 + waveform[i - 1] * smooth);
    }

    return waveform;
  }

  generateWaveform(blending, blendProgress, timeArrayL, timeArrayR, mdVSFrame) {
    let alpha = mdVSFrame.wave_a;
    const vol = (mdVSFrame.bass + mdVSFrame.mid + mdVSFrame.treb) / 3.0;

    if (vol > -0.01 && alpha > 0.001 && timeArrayL.length > 0) {
      const waveL = BasicWaveform.processWaveform(timeArrayL, mdVSFrame);
      const waveR = BasicWaveform.processWaveform(timeArrayR, mdVSFrame);

      const newWaveMode = Math.floor(mdVSFrame.wave_mode) % 8;
      const oldWaveMode = Math.floor(mdVSFrame.old_wave_mode) % 8;

      const wavePosX = mdVSFrame.wave_x * 2.0 - 1.0;
      const wavePosY = mdVSFrame.wave_y * 2.0 - 1.0;

      this.numVert = 0;
      this.oldNumVert = 0;

      const its = blending && newWaveMode !== oldWaveMode ? 2 : 1;
      for (let it = 0; it < its; it++) {
        const waveMode = it === 0 ? newWaveMode : oldWaveMode;

        let fWaveParam2 = mdVSFrame.wave_mystery;
        if (
          (waveMode === 0 || waveMode === 1 || waveMode === 4) &&
          (fWaveParam2 < -1 || fWaveParam2 > 1)
        ) {
          fWaveParam2 = fWaveParam2 * 0.5 + 0.5;
          fWaveParam2 -= Math.floor(fWaveParam2);
          fWaveParam2 = Math.abs(fWaveParam2);
          fWaveParam2 = fWaveParam2 * 2 - 1;
        }

        let numVert;
        let positions;
        let positions2;
        if (it === 0) {
          positions = this.positions;
          positions2 = this.positions2;
        } else {
          positions = this.oldPositions;
          positions2 = this.oldPositions2;
        }

        alpha = mdVSFrame.wave_a;
        if (waveMode === 0) {
          if (mdVSFrame.modwavealphabyvolume > 0) {
            const alphaDiff =
              mdVSFrame.modwavealphaend - mdVSFrame.modwavealphastart;
            alpha *= (vol - mdVSFrame.modwavealphastart) / alphaDiff;
          }
          alpha = Math.clamp(alpha, 0, 1);

          numVert = Math.floor(waveL.length / 2) + 1;
          const numVertInv = 1.0 / (numVert - 1);
          const sampleOffset = Math.floor((waveL.length - numVert) / 2);
          for (let i = 0; i < numVert - 1; i++) {
            let rad = 0.5 + 0.4 * waveR[i + sampleOffset] + fWaveParam2;
            const ang = i * numVertInv * 2 * Math.PI + mdVSFrame.time * 0.2;

            if (i < numVert / 10) {
              let mix = i / (numVert * 0.1);
              mix = 0.5 - 0.5 * Math.cos(mix * Math.PI);
              const rad2 =
                0.5 + 0.4 * waveR[i + numVert + sampleOffset] + fWaveParam2;
              rad = (1.0 - mix) * rad2 + rad * mix;
            }

            positions[i * 3 + 0] =
              rad * Math.cos(ang) * this.aspecty + wavePosX;
            positions[i * 3 + 1] =
              rad * Math.sin(ang) * this.aspectx + wavePosY;
            positions[i * 3 + 2] = 0;
          }

          // connect the loop
          positions[(numVert - 1) * 3 + 0] = positions[0];
          positions[(numVert - 1) * 3 + 1] = positions[1];
          positions[(numVert - 1) * 3 + 2] = 0;
        } else if (waveMode === 1) {
          alpha *= 1.25;
          if (mdVSFrame.modwavealphabyvolume > 0) {
            const alphaDiff =
              mdVSFrame.modwavealphaend - mdVSFrame.modwavealphastart;
            alpha *= (vol - mdVSFrame.modwavealphastart) / alphaDiff;
          }
          alpha = Math.clamp(alpha, 0, 1);

          numVert = Math.floor(waveL.length / 2);
          for (let i = 0; i < numVert; i++) {
            const rad = 0.53 + 0.43 * waveR[i] + fWaveParam2;
            const ang = waveL[i + 32] * 0.5 * Math.PI + mdVSFrame.time * 2.3;

            positions[i * 3 + 0] =
              rad * Math.cos(ang) * this.aspecty + wavePosX;
            positions[i * 3 + 1] =
              rad * Math.sin(ang) * this.aspectx + wavePosY;
            positions[i * 3 + 2] = 0;
          }
        } else if (waveMode === 2) {
          if (this.texsizeX < 1024) {
            alpha *= 0.09;
          } else if (this.texsizeX >= 1024 && this.texsizeX < 2048) {
            alpha *= 0.11;
          } else {
            alpha *= 0.13;
          }

          if (mdVSFrame.modwavealphabyvolume > 0) {
            const alphaDiff =
              mdVSFrame.modwavealphaend - mdVSFrame.modwavealphastart;
            alpha *= (vol - mdVSFrame.modwavealphastart) / alphaDiff;
          }
          alpha = Math.clamp(alpha, 0, 1);

          numVert = waveL.length;
          for (let i = 0; i < waveL.length; i++) {
            positions[i * 3 + 0] = waveR[i] * this.aspecty + wavePosX;
            positions[i * 3 + 1] =
              waveL[(i + 32) % waveL.length] * this.aspectx + wavePosY;
            positions[i * 3 + 2] = 0;
          }
        } else if (waveMode === 3) {
          if (this.texsizeX < 1024) {
            alpha *= 0.15;
          } else if (this.texsizeX >= 1024 && this.texsizeX < 2048) {
            alpha *= 0.22;
          } else {
            alpha *= 0.33;
          }

          alpha *= 1.3;
          alpha *= mdVSFrame.treb * mdVSFrame.treb; // should be treb_imm

          if (mdVSFrame.modwavealphabyvolume > 0) {
            const alphaDiff =
              mdVSFrame.modwavealphaend - mdVSFrame.modwavealphastart;
            alpha *= (vol - mdVSFrame.modwavealphastart) / alphaDiff;
          }
          alpha = Math.clamp(alpha, 0, 1);

          numVert = waveL.length;
          for (let i = 0; i < waveL.length; i++) {
            positions[i * 3 + 0] = waveR[i] * this.aspecty + wavePosX;
            positions[i * 3 + 1] =
              waveL[(i + 32) % waveL.length] * this.aspectx + wavePosY;
            positions[i * 3 + 2] = 0;
          }
        } else if (waveMode === 4) {
          if (mdVSFrame.modwavealphabyvolume > 0) {
            const alphaDiff =
              mdVSFrame.modwavealphaend - mdVSFrame.modwavealphastart;
            alpha *= (vol - mdVSFrame.modwavealphastart) / alphaDiff;
          }
          alpha = Math.clamp(alpha, 0, 1);

          numVert = waveL.length;
          if (numVert > this.texsizeX / 3) {
            numVert = Math.floor(this.texsizeX / 3);
          }
          const numVertInv = 1.0 / numVert;
          const sampleOffset = Math.floor((waveL.length - numVert) / 2);

          const w1 = 0.45 + 0.5 * (fWaveParam2 * 0.5 + 0.5);
          const w2 = 1.0 - w1;
          for (let i = 0; i < numVert; i++) {
            let x =
              2.0 * i * numVertInv +
              (wavePosX - 1) +
              waveR[(i + 25 + sampleOffset) % waveL.length] * 0.44;
            let y = waveL[i + sampleOffset] * 0.47 + wavePosY;

            // momentum
            if (i > 1) {
              x =
                x * w2 +
                w1 *
                  (positions[(i - 1) * 3 + 0] * 2.0 -
                    positions[(i - 2) * 3 + 0]);
              y =
                y * w2 +
                w1 *
                  (positions[(i - 1) * 3 + 1] * 2.0 -
                    positions[(i - 2) * 3 + 1]);
            }

            positions[i * 3 + 0] = x;
            positions[i * 3 + 1] = y;
            positions[i * 3 + 2] = 0;
          }
        } else if (waveMode === 5) {
          if (this.texsizeX < 1024) {
            alpha *= 0.09;
          } else if (this.texsizeX >= 1024 && this.texsizeX < 2048) {
            alpha *= 0.11;
          } else {
            alpha *= 0.13;
          }

          if (mdVSFrame.modwavealphabyvolume > 0) {
            const alphaDiff =
              mdVSFrame.modwavealphaend - mdVSFrame.modwavealphastart;
            alpha *= (vol - mdVSFrame.modwavealphastart) / alphaDiff;
          }
          alpha = Math.clamp(alpha, 0, 1);

          const cosRot = Math.cos(mdVSFrame.time * 0.3);
          const sinRot = Math.sin(mdVSFrame.time * 0.3);

          numVert = waveL.length;
          for (let i = 0; i < waveL.length; i++) {
            const ioff = (i + 32) % waveL.length;
            const x0 = waveR[i] * waveL[ioff] + waveL[i] * waveR[ioff];
            const y0 = waveR[i] * waveR[i] - waveL[ioff] * waveL[ioff];

            positions[i * 3 + 0] =
              (x0 * cosRot - y0 * sinRot) * (this.aspecty + wavePosX);
            positions[i * 3 + 1] =
              (x0 * sinRot + y0 * cosRot) * (this.aspectx + wavePosY);
            positions[i * 3 + 2] = 0;
          }
        } else if (waveMode === 6 || waveMode === 7) {
          if (mdVSFrame.modwavealphabyvolume > 0) {
            const alphaDiff =
              mdVSFrame.modwavealphaend - mdVSFrame.modwavealphastart;
            alpha *= (vol - mdVSFrame.modwavealphastart) / alphaDiff;
          }
          alpha = Math.clamp(alpha, 0, 1);

          numVert = Math.floor(waveL.length / 2);
          if (numVert > this.texsizeX / 3) {
            numVert = Math.floor(this.texsizeX / 3);
          }
          const sampleOffset = Math.floor((waveL.length - numVert) / 2);

          const ang = Math.PI * 0.5 * fWaveParam2;
          let dx = Math.cos(ang);
          let dy = Math.sin(ang);

          const edgex = [
            wavePosX * Math.cos(ang + Math.PI * 0.5) - dx * 3.0,
            wavePosX * Math.cos(ang + Math.PI * 0.5) + dx * 3.0,
          ];
          const edgey = [
            wavePosX * Math.sin(ang + Math.PI * 0.5) - dy * 3.0,
            wavePosX * Math.sin(ang + Math.PI * 0.5) + dy * 3.0,
          ];

          for (let i = 0; i < 2; i++) {
            for (let j = 0; j < 4; j++) {
              let t;
              let bClip = false;

              switch (j) {
                case 0:
                  if (edgex[i] > 1.1) {
                    t = (1.1 - edgex[1 - i]) / (edgex[i] - edgex[1 - i]);
                    bClip = true;
                  }
                  break;
                case 1:
                  if (edgex[i] < -1.1) {
                    t = (-1.1 - edgex[1 - i]) / (edgex[i] - edgex[1 - i]);
                    bClip = true;
                  }
                  break;
                case 2:
                  if (edgey[i] > 1.1) {
                    t = (1.1 - edgey[1 - i]) / (edgey[i] - edgey[1 - i]);
                    bClip = true;
                  }
                  break;
                case 3:
                  if (edgey[i] < -1.1) {
                    t = (-1.1 - edgey[1 - i]) / (edgey[i] - edgey[1 - i]);
                    bClip = true;
                  }
                  break;
                default:
              }

              if (bClip) {
                const dxi = edgex[i] - edgex[1 - i];
                const dyi = edgey[i] - edgey[1 - i];
                edgex[i] = edgex[1 - i] + dxi * t;
                edgey[i] = edgey[1 - i] + dyi * t;
              }
            }
          }

          dx = (edgex[1] - edgex[0]) / numVert;
          dy = (edgey[1] - edgey[0]) / numVert;

          const ang2 = Math.atan2(dy, dx);
          const perpDx = Math.cos(ang2 + Math.PI * 0.5);
          const perpDy = Math.sin(ang2 + Math.PI * 0.5);

          if (waveMode === 6) {
            for (let i = 0; i < numVert; i++) {
              const sample = waveL[i + sampleOffset];
              positions[i * 3 + 0] = edgex[0] + dx * i + perpDx * 0.25 * sample;
              positions[i * 3 + 1] = edgey[0] + dy * i + perpDy * 0.25 * sample;
              positions[i * 3 + 2] = 0;
            }
          } else if (waveMode === 7) {
            const sep = (wavePosY * 0.5 + 0.5) ** 2;
            for (let i = 0; i < numVert; i++) {
              const sample = waveL[i + sampleOffset];
              positions[i * 3 + 0] =
                edgex[0] + dx * i + perpDx * (0.25 * sample + sep);
              positions[i * 3 + 1] =
                edgey[0] + dy * i + perpDy * (0.25 * sample + sep);
              positions[i * 3 + 2] = 0;
            }

            for (let i = 0; i < numVert; i++) {
              const sample = waveR[i + sampleOffset];
              positions2[i * 3 + 0] =
                edgex[0] + dx * i + perpDx * (0.25 * sample - sep);
              positions2[i * 3 + 1] =
                edgey[0] + dy * i + perpDy * (0.25 * sample - sep);
              positions2[i * 3 + 2] = 0;
            }
          }
        }

        if (it === 0) {
          this.positions = positions;
          this.positions2 = positions2;
          this.numVert = numVert;
          this.alpha = alpha;
        } else {
          this.oldPositions = positions;
          this.oldPositions2 = positions2;
          this.oldNumVert = numVert;
          this.oldAlpha = alpha;
        }
      }

      const mix = 0.5 - 0.5 * Math.cos(blendProgress * Math.PI);
      const mix2 = 1 - mix;

      if (this.oldNumVert > 0) {
        alpha = mix * this.alpha + mix2 * this.oldAlpha;
      }

      let r = Math.clamp(mdVSFrame.wave_r, 0, 1);
      let g = Math.clamp(mdVSFrame.wave_g, 0, 1);
      let b = Math.clamp(mdVSFrame.wave_b, 0, 1);

      if (mdVSFrame.wave_brighten !== 0) {
        const maxc = Math.max(r, g, b);
        if (maxc > 0.01) {
          r /= maxc;
          g /= maxc;
          b /= maxc;
        }
      }

      this.color = [r, g, b, alpha];

      if (this.oldNumVert > 0) {
        if (newWaveMode === 7) {
          const m = (this.oldNumVert - 1) / (this.numVert * 2);
          for (let i = 0; i < this.numVert; i++) {
            const fIdx = i * m;
            const nIdx = Math.floor(fIdx);
            const t = fIdx - nIdx;

            const x =
              this.oldPositions[nIdx * 3 + 0] * (1 - t) +
              this.oldPositions[(nIdx + 1) * 3 + 0] * t;
            const y =
              this.oldPositions[nIdx * 3 + 1] * (1 - t) +
              this.oldPositions[(nIdx + 1) * 3 + 1] * t;

            this.positions[i * 3 + 0] =
              this.positions[i * 3 + 0] * mix + x * mix2;
            this.positions[i * 3 + 1] =
              this.positions[i * 3 + 1] * mix + y * mix2;
            this.positions[i * 3 + 2] = 0;
          }

          for (let i = 0; i < this.numVert; i++) {
            const fIdx = (i + this.numVert) * m;
            const nIdx = Math.floor(fIdx);
            const t = fIdx - nIdx;

            const x =
              this.oldPositions[nIdx * 3 + 0] * (1 - t) +
              this.oldPositions[(nIdx + 1) * 3 + 0] * t;
            const y =
              this.oldPositions[nIdx * 3 + 1] * (1 - t) +
              this.oldPositions[(nIdx + 1) * 3 + 1] * t;

            this.positions2[i * 3 + 0] =
              this.positions2[i * 3 + 0] * mix + x * mix2;
            this.positions2[i * 3 + 1] =
              this.positions2[i * 3 + 1] * mix + y * mix2;
            this.positions2[i * 3 + 2] = 0;
          }
        } else if (oldWaveMode === 7) {
          const halfNumVert = this.numVert / 2;
          const m = (this.oldNumVert - 1) / halfNumVert;
          for (let i = 0; i < halfNumVert; i++) {
            const fIdx = i * m;
            const nIdx = Math.floor(fIdx);
            const t = fIdx - nIdx;

            const x =
              this.oldPositions[nIdx * 3 + 0] * (1 - t) +
              this.oldPositions[(nIdx + 1) * 3 + 0] * t;
            const y =
              this.oldPositions[nIdx * 3 + 1] * (1 - t) +
              this.oldPositions[(nIdx + 1) * 3 + 1] * t;

            this.positions[i * 3 + 0] =
              this.positions[i * 3 + 0] * mix + x * mix2;
            this.positions[i * 3 + 1] =
              this.positions[i * 3 + 1] * mix + y * mix2;
            this.positions[i * 3 + 2] = 0;
          }

          for (let i = 0; i < halfNumVert; i++) {
            const fIdx = i * m;
            const nIdx = Math.floor(fIdx);
            const t = fIdx - nIdx;

            const x =
              this.oldPositions2[nIdx * 3 + 0] * (1 - t) +
              this.oldPositions2[(nIdx + 1) * 3 + 0] * t;
            const y =
              this.oldPositions2[nIdx * 3 + 1] * (1 - t) +
              this.oldPositions2[(nIdx + 1) * 3 + 1] * t;

            this.positions2[i * 3 + 0] =
              this.positions[(i + halfNumVert) * 3 + 0] * mix + x * mix2;
            this.positions2[i * 3 + 1] =
              this.positions[(i + halfNumVert) * 3 + 1] * mix + y * mix2;
            this.positions2[i * 3 + 2] = 0;
          }
        } else {
          const m = (this.oldNumVert - 1) / this.numVert;
          for (let i = 0; i < this.numVert; i++) {
            const fIdx = i * m;
            const nIdx = Math.floor(fIdx);
            const t = fIdx - nIdx;

            const x =
              this.oldPositions[nIdx * 3 + 0] * (1 - t) +
              this.oldPositions[(nIdx + 1) * 3 + 0] * t;
            const y =
              this.oldPositions[nIdx * 3 + 1] * (1 - t) +
              this.oldPositions[(nIdx + 1) * 3 + 1] * t;

            this.positions[i * 3 + 0] =
              this.positions[i * 3 + 0] * mix + x * mix2;
            this.positions[i * 3 + 1] =
              this.positions[i * 3 + 1] * mix + y * mix2;
            this.positions[i * 3 + 2] = 0;
          }
        }
      }

      for (let i = 0; i < this.numVert; i++) {
        this.positions[i * 3 + 1] = -this.positions[i * 3 + 1];
      }

      this.smoothedNumVert = this.numVert * 2 - 1;
      WaveUtils.smoothWave(
        this.positions,
        this.smoothedPositions,
        this.numVert
      );

      if (newWaveMode === 7 || oldWaveMode === 7) {
        for (let i = 0; i < this.numVert; i++) {
          this.positions2[i * 3 + 1] = -this.positions2[i * 3 + 1];
        }

        WaveUtils.smoothWave(
          this.positions2,
          this.smoothedPositions2,
          this.numVert
        );
      }

      return true;
    }

    return false;
  }

  drawBasicWaveform(
    blending,
    blendProgress,
    timeArrayL,
    timeArrayR,
    mdVSFrame
  ) {
    if (
      this.generateWaveform(
        blending,
        blendProgress,
        timeArrayL,
        timeArrayR,
        mdVSFrame
      )
    ) {
      this.gl.useProgram(this.shaderProgram);

      this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.vertexBuf);
      this.gl.bufferData(
        this.gl.ARRAY_BUFFER,
        this.smoothedPositions,
        this.gl.STATIC_DRAW
      );

      this.gl.vertexAttribPointer(this.aPosLoc, 3, this.gl.FLOAT, false, 0, 0);
      this.gl.enableVertexAttribArray(this.aPosLoc);

      this.gl.uniform4fv(this.colorLoc, this.color);

      let instances = 1;
      if (mdVSFrame.wave_thick !== 0 || mdVSFrame.wave_dots !== 0) {
        instances = 4;
      }

      if (mdVSFrame.additivewave !== 0) {
        this.gl.blendFunc(this.gl.SRC_ALPHA, this.gl.ONE);
      } else {
        this.gl.blendFunc(this.gl.SRC_ALPHA, this.gl.ONE_MINUS_SRC_ALPHA);
      }

      const drawMode =
        mdVSFrame.wave_dots !== 0 ? this.gl.POINTS : this.gl.LINE_STRIP;

      // TODO: use drawArraysInstanced
      for (let i = 0; i < instances; i++) {
        const offset = 2;
        if (i === 0) {
          this.gl.uniform2fv(this.thickOffsetLoc, [0, 0]);
        } else if (i === 1) {
          this.gl.uniform2fv(this.thickOffsetLoc, [offset / this.texsizeX, 0]);
        } else if (i === 2) {
          this.gl.uniform2fv(this.thickOffsetLoc, [0, offset / this.texsizeY]);
        } else if (i === 3) {
          this.gl.uniform2fv(this.thickOffsetLoc, [
            offset / this.texsizeX,
            offset / this.texsizeY,
          ]);
        }

        this.gl.drawArrays(drawMode, 0, this.smoothedNumVert);
      }

      const waveMode = Math.floor(mdVSFrame.wave_mode) % 8;
      if (waveMode === 7) {
        this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.vertexBuf);
        this.gl.bufferData(
          this.gl.ARRAY_BUFFER,
          this.smoothedPositions2,
          this.gl.STATIC_DRAW
        );

        this.gl.vertexAttribPointer(
          this.aPosLoc,
          3,
          this.gl.FLOAT,
          false,
          0,
          0
        );
        this.gl.enableVertexAttribArray(this.aPosLoc);

        for (let i = 0; i < instances; i++) {
          const offset = 2;
          if (i === 0) {
            this.gl.uniform2fv(this.thickOffsetLoc, [0, 0]);
          } else if (i === 1) {
            this.gl.uniform2fv(this.thickOffsetLoc, [
              offset / this.texsizeX,
              0,
            ]);
          } else if (i === 2) {
            this.gl.uniform2fv(this.thickOffsetLoc, [
              0,
              offset / this.texsizeY,
            ]);
          } else if (i === 3) {
            this.gl.uniform2fv(this.thickOffsetLoc, [
              offset / this.texsizeX,
              offset / this.texsizeY,
            ]);
          }

          this.gl.drawArrays(drawMode, 0, this.smoothedNumVert);
        }
      }
    }
  }
}
Lyzon0 commented 10 months ago

Blank preset


/* eslint-disable */
define([], function () {
  "use strict;";

  var pmap = {
    baseVals: {
      gammaadj: 1.25,
      wave_g: 0.5,
      mv_x: 12.0,
      warpscale: 1.0,
      brighten: 0.0,
      mv_y: 9.0,
      wave_scale: 1.0,
      echo_alpha: 0.0,
      additivewave: 0.0,
      sx: 1.0,
      sy: 1.0,
      warp: 0.01,
      red_blue: 0.0,
      wave_mode: 0.0,
      wave_brighten: 0.0,
      wrap: 0.0,
      zoomexp: 1.0,
      fshader: 0.0,
      wave_r: 0.5,
      echo_zoom: 1.0,
      wave_smoothing: 0.75,
      warpanimspeed: 1.0,
      wave_dots: 0.0,
      wave_x: 0.5,
      wave_y: 0.5,
      zoom: 1.0,
      solarize: 0.0,
      modwavealphabyvolume: 0.0,
      dx: 0.0,
      cx: 0.5,
      dy: 0.0,
      darken_center: 0.0,
      cy: 0.5,
      invert: 0.0,
      bmotionvectorson: 0.0,
      rot: 0.0,
      modwavealphaend: 0.95,
      wave_mystery: -0.2,
      decay: 0.9,
      wave_a: 1.0,
      wave_b: 0.5,
      rating: 5.0,
      modwavealphastart: 0.75,
      darken: 0.0,
      echo_orient: 0.0,
      ib_r: 0.5,
      ib_g: 0.5,
      ib_b: 0.5,
      ib_a: 0.0,
      ib_size: 0.0,
      ob_r: 0.5,
      ob_g: 0.5,
      ob_b: 0.5,
      ob_a: 0.0,
      ob_size: 0.0,
      mv_dx: 0.0,
      mv_dy: 0.0,
      mv_a: 0.0,
      mv_r: 0.5,
      mv_g: 0.5,
      mv_b: 0.5,
      mv_l: 0.0,
    },
    init_eqs: function () {
      var m = {};
      return m;
    },
    frame_eqs: function (m) {
      m.rkeys = ["warp"];
      m.zoom = 1.01 + 0.02 * m.treb_att;
      m.warp = 0.15 + 0.25 * m.bass_att;
      return m;
    },
    pixel_eqs: function (m) {
      m.warp = m.warp + m.rad * 0.15;
      return m;
    },
    waves: [
      {
        baseVals: {
          a: 1.0,
          enabled: 0.0,
          b: 1.0,
          g: 1.0,
          scaling: 1.0,
          samples: 512.0,
          additive: 0.0,
          usedots: 0.0,
          spectrum: 0.0,
          r: 1.0,
          smoothing: 0.5,
          thick: 0.0,
          sep: 0.0,
        },
        init_eqs: function (m) {
          m.rkeys = [];
          return m;
        },
        frame_eqs: function (m) {
          return m;
        },
        point_eqs: "",
      },
      {
        baseVals: {
          a: 1.0,
          enabled: 0.0,
          b: 1.0,
          g: 1.0,
          scaling: 1.0,
          samples: 512.0,
          additive: 0.0,
          usedots: 0.0,
          spectrum: 0.0,
          r: 1.0,
          smoothing: 0.5,
          thick: 0.0,
          sep: 0.0,
        },
        init_eqs: function (m) {
          m.rkeys = [];
          return m;
        },
        frame_eqs: function (m) {
          return m;
        },
        point_eqs: "",
      },
      {
        baseVals: {
          a: 1.0,
          enabled: 0.0,
          b: 1.0,
          g: 1.0,
          scaling: 1.0,
          samples: 512.0,
          additive: 0.0,
          usedots: 0.0,
          spectrum: 0.0,
          r: 1.0,
          smoothing: 0.5,
          thick: 0.0,
          sep: 0.0,
        },
        init_eqs: function (m) {
          m.rkeys = [];
          return m;
        },
        frame_eqs: function (m) {
          return m;
        },
        point_eqs: "",
      },
      {
        baseVals: {
          a: 1.0,
          enabled: 0.0,
          b: 1.0,
          g: 1.0,
          scaling: 1.0,
          samples: 512.0,
          additive: 0.0,
          usedots: 0.0,
          spectrum: 0.0,
          r: 1.0,
          smoothing: 0.5,
          thick: 0.0,
          sep: 0.0,
        },
        init_eqs: function (m) {
          m.rkeys = [];
          return m;
        },
        frame_eqs: function (m) {
          return m;
        },
        point_eqs: "",
      },
    ],
    shapes: [
      {
        baseVals: {
          r2: 0.0,
          a: 1.0,
          enabled: 0.0,
          b: 0.0,
          tex_ang: 0.0,
          thickoutline: 0.0,
          g: 0.0,
          textured: 0.0,
          g2: 1.0,
          tex_zoom: 1.0,
          additive: 0.0,
          border_a: 0.1,
          border_b: 1.0,
          b2: 0.0,
          a2: 0.0,
          r: 1.0,
          border_g: 1.0,
          rad: 0.1,
          x: 0.5,
          y: 0.5,
          ang: 0.0,
          sides: 4.0,
          border_r: 1.0,
        },
        init_eqs: function (m) {
          m.rkeys = [];
          return m;
        },
        frame_eqs: function (m) {
          return m;
        },
      },
      {
        baseVals: {
          r2: 0.0,
          a: 1.0,
          enabled: 0.0,
          b: 0.0,
          tex_ang: 0.0,
          thickoutline: 0.0,
          g: 0.0,
          textured: 0.0,
          g2: 1.0,
          tex_zoom: 1.0,
          additive: 0.0,
          border_a: 0.1,
          border_b: 1.0,
          b2: 0.0,
          a2: 0.0,
          r: 1.0,
          border_g: 1.0,
          rad: 0.1,
          x: 0.5,
          y: 0.5,
          ang: 0.0,
          sides: 4.0,
          border_r: 1.0,
        },
        init_eqs: function (m) {
          m.rkeys = [];
          return m;
        },
        frame_eqs: function (m) {
          return m;
        },
      },
      {
        baseVals: {
          r2: 0.0,
          a: 1.0,
          enabled: 0.0,
          b: 0.0,
          tex_ang: 0.0,
          thickoutline: 0.0,
          g: 0.0,
          textured: 0.0,
          g2: 1.0,
          tex_zoom: 1.0,
          additive: 0.0,
          border_a: 0.1,
          border_b: 1.0,
          b2: 0.0,
          a2: 0.0,
          r: 1.0,
          border_g: 1.0,
          rad: 0.1,
          x: 0.5,
          y: 0.5,
          ang: 0.0,
          sides: 4.0,
          border_r: 1.0,
        },
        init_eqs: function (m) {
          m.rkeys = [];
          return m;
        },
        frame_eqs: function (m) {
          return m;
        },
      },
      {
        baseVals: {
          r2: 0.0,
          a: 1.0,
          enabled: 0.0,
          b: 0.0,
          tex_ang: 0.0,
          thickoutline: 0.0,
          g: 0.0,
          textured: 0.0,
          g2: 1.0,
          tex_zoom: 1.0,
          additive: 0.0,
          border_a: 0.1,
          border_b: 1.0,
          b2: 0.0,
          a2: 0.0,
          r: 1.0,
          border_g: 1.0,
          rad: 0.1,
          x: 0.5,
          y: 0.5,
          ang: 0.0,
          sides: 4.0,
          border_r: 1.0,
        },
        init_eqs: function (m) {
          m.rkeys = [];
          return m;
        },
        frame_eqs: function (m) {
          return m;
        },
      },
    ],
    warp:
      "shader_body {\nret = texture2D(sampler_main, uv).rgb;\nret -= 0.004;\n}\n",
    comp:
      "shader_body {\nret = texture2D(sampler_main, uv).rgb;\nret *= hue_shader;\n}\n",
  };

  return pmap;
});
/* eslint-enable */

Blend pattern


export default class BlendPattern {
  constructor(opts) {
    this.mesh_width = opts.mesh_width;
    this.mesh_height = opts.mesh_height;
    this.aspectx = opts.aspectx;
    this.aspecty = opts.aspecty;

    this.vertInfoA = new Float32Array(
      (this.mesh_width + 1) * (this.mesh_height + 1)
    );
    this.vertInfoC = new Float32Array(
      (this.mesh_width + 1) * (this.mesh_height + 1)
    );

    this.createBlendPattern();
  }

  static resizeMatrixValues(oldMat, oldWidth, oldHeight, newWidth, newHeight) {
    const newMat = new Float32Array((newWidth + 1) * (newHeight + 1));
    let nVert = 0;
    for (let j = 0; j < newHeight + 1; j++) {
      for (let i = 0; i < newWidth + 1; i++) {
        let x = i / newHeight;
        let y = j / newWidth;

        x *= oldWidth + 1;
        y *= oldHeight + 1;
        x = Math.clamp(x, 0, oldWidth - 1);
        y = Math.clamp(y, 0, oldHeight - 1);
        const nx = Math.floor(x);
        const ny = Math.floor(y);
        const dx = x - nx;
        const dy = y - ny;
        const val00 = oldMat[ny * (oldWidth + 1) + nx];
        const val01 = oldMat[ny * (oldWidth + 1) + (nx + 1)];
        const val10 = oldMat[(ny + 1) * (oldWidth + 1) + nx];
        const val11 = oldMat[(ny + 1) * (oldWidth + 1) + (nx + 1)];
        newMat[nVert] =
          val00 * (1 - dx) * (1 - dy) +
          val01 * dx * (1 - dy) +
          val10 * (1 - dx) * dy +
          val11 * dx * dy;

        nVert += 1;
      }
    }

    return newMat;
  }

  updateGlobals(opts) {
    const oldMeshWidth = this.mesh_width;
    const oldMeshHeight = this.mesh_height;

    this.mesh_width = opts.mesh_width;
    this.mesh_height = opts.mesh_height;
    this.aspectx = opts.aspectx;
    this.aspecty = opts.aspecty;

    if (
      this.mesh_width !== oldMeshWidth ||
      this.mesh_height !== oldMeshHeight
    ) {
      this.vertInfoA = BlendPattern.resizeMatrixValues(
        this.vertInfoA,
        oldMeshWidth,
        oldMeshHeight,
        this.mesh_width,
        this.mesh_height
      );
      this.vertInfoC = BlendPattern.resizeMatrixValues(
        this.vertInfoC,
        oldMeshWidth,
        oldMeshHeight,
        this.mesh_width,
        this.mesh_height
      );
    }
  }

  genPlasma(x0, x1, y0, y1, dt) {
    const midx = Math.floor((x0 + x1) / 2);
    const midy = Math.floor((y0 + y1) / 2);
    let t00 = this.vertInfoC[y0 * (this.mesh_width + 1) + x0];
    let t01 = this.vertInfoC[y0 * (this.mesh_width + 1) + x1];
    let t10 = this.vertInfoC[y1 * (this.mesh_width + 1) + x0];
    let t11 = this.vertInfoC[y1 * (this.mesh_width + 1) + x1];

    if (y1 - y0 >= 2) {
      if (x0 === 0) {
        this.vertInfoC[midy * (this.mesh_width + 1) + x0] =
          0.5 * (t00 + t10) + (Math.random() * 2 - 1) * dt * this.aspecty;
      }
      this.vertInfoC[midy * (this.mesh_width + 1) + x1] =
        0.5 * (t01 + t11) + (Math.random() * 2 - 1) * dt * this.aspecty;
    }
    if (x1 - x0 >= 2) {
      if (y0 === 0) {
        this.vertInfoC[y0 * (this.mesh_width + 1) + midx] =
          0.5 * (t00 + t01) + (Math.random() * 2 - 1) * dt * this.aspectx;
      }
      this.vertInfoC[y1 * (this.mesh_width + 1) + midx] =
        0.5 * (t10 + t11) + (Math.random() * 2 - 1) * dt * this.aspectx;
    }

    if (y1 - y0 >= 2 && x1 - x0 >= 2) {
      t00 = this.vertInfoC[midy * (this.mesh_width + 1) + x0];
      t01 = this.vertInfoC[midy * (this.mesh_width + 1) + x1];
      t10 = this.vertInfoC[y0 * (this.mesh_width + 1) + midx];
      t11 = this.vertInfoC[y1 * (this.mesh_width + 1) + midx];
      this.vertInfoC[midy * (this.mesh_width + 1) + midx] =
        0.25 * (t10 + t11 + t00 + t01) + (Math.random() * 2 - 1) * dt;

      this.genPlasma(x0, midx, y0, midy, dt * 0.5);
      this.genPlasma(midx, x1, y0, midy, dt * 0.5);
      this.genPlasma(x0, midx, midy, y1, dt * 0.5);
      this.genPlasma(midx, x1, midy, y1, dt * 0.5);
    }
  }

  createBlendPattern() {
    const mixType = 1 + Math.floor(Math.random() * 3);
    if (mixType === 0) {
      // not currently used
      let nVert = 0;
      for (let y = 0; y <= this.mesh_height; y++) {
        for (let x = 0; x <= this.mesh_width; x++) {
          this.vertInfoA[nVert] = 1;
          this.vertInfoC[nVert] = 0;
          nVert += 1;
        }
      }
    } else if (mixType === 1) {
      const ang = Math.random() * 6.28;
      const vx = Math.cos(ang);
      const vy = Math.sin(ang);
      const band = 0.1 + 0.2 * Math.random();
      const invBand = 1.0 / band;

      let nVert = 0;
      for (let y = 0; y <= this.mesh_height; y++) {
        const fy = (y / this.mesh_height) * this.aspecty;
        for (let x = 0; x <= this.mesh_width; x++) {
          const fx = (x / this.mesh_width) * this.aspectx;

          let t = (fx - 0.5) * vx + (fy - 0.5) * vy + 0.5;
          t = (t - 0.5) / Math.sqrt(2) + 0.5;

          this.vertInfoA[nVert] = invBand * (1 + band);
          this.vertInfoC[nVert] = -invBand + invBand * t;
          nVert += 1;
        }
      }
    } else if (mixType === 2) {
      const band = 0.12 + 0.13 * Math.random();
      const invBand = 1.0 / band;

      this.vertInfoC[0] = Math.random();
      this.vertInfoC[this.mesh_width] = Math.random();
      this.vertInfoC[this.mesh_height * (this.mesh_width + 1)] = Math.random();
      this.vertInfoC[
        this.mesh_height * (this.mesh_width + 1) + this.mesh_width
      ] = Math.random();
      this.genPlasma(0, this.mesh_width, 0, this.mesh_height, 0.25);

      let minc = this.vertInfoC[0];
      let maxc = this.vertInfoC[0];

      let nVert = 0;
      for (let y = 0; y <= this.mesh_height; y++) {
        for (let x = 0; x <= this.mesh_width; x++) {
          if (minc > this.vertInfoC[nVert]) {
            minc = this.vertInfoC[nVert];
          }
          if (maxc < this.vertInfoC[nVert]) {
            maxc = this.vertInfoC[nVert];
          }
          nVert += 1;
        }
      }

      const mult = 1.0 / (maxc - minc);
      nVert = 0;
      for (let y = 0; y <= this.mesh_height; y++) {
        for (let x = 0; x <= this.mesh_width; x++) {
          const t = (this.vertInfoC[nVert] - minc) * mult;
          this.vertInfoA[nVert] = invBand * (1 + band);
          this.vertInfoC[nVert] = -invBand + invBand * t;
          nVert += 1;
        }
      }
    } else if (mixType === 3) {
      const band = 0.02 + 0.14 * Math.random() + 0.34 * Math.random();
      const invBand = 1.0 / band;
      const dir = Math.floor(Math.random() * 2) * 2 - 1;

      let nVert = 0;
      for (let y = 0; y <= this.mesh_height; y++) {
        const dy = (y / this.mesh_height - 0.5) * this.aspecty;
        for (let x = 0; x <= this.mesh_width; x++) {
          const dx = (x / this.mesh_width - 0.5) * this.aspectx;
          let t = Math.sqrt(dx * dx + dy * dy) * 1.41421;
          if (dir === -1) {
            t = 1 - t;
          }

          this.vertInfoA[nVert] = invBand * (1 + band);
          this.vertInfoC[nVert] = -invBand + invBand * t;
          nVert += 1;
        }
      }
    }
  }
}
Lyzon0 commented 10 months ago

import BlurVertical from "./blurVertical";
import BlurHorizontal from "./blurHorizontal";

export default class BlurShader {
  constructor(blurLevel, blurRatios, gl, opts = {}) {
    this.blurLevel = blurLevel;
    this.blurRatios = blurRatios;
    this.gl = gl;

    this.texsizeX = opts.texsizeX;
    this.texsizeY = opts.texsizeY;

    this.anisoExt =
      this.gl.getExtension("EXT_texture_filter_anisotropic") ||
      this.gl.getExtension("MOZ_EXT_texture_filter_anisotropic") ||
      this.gl.getExtension("WEBKIT_EXT_texture_filter_anisotropic");

    this.blurHorizontalFrameBuffer = this.gl.createFramebuffer();
    this.blurVerticalFrameBuffer = this.gl.createFramebuffer();

    this.blurHorizontalTexture = this.gl.createTexture();
    this.blurVerticalTexture = this.gl.createTexture();

    this.setupFrameBufferTextures();

    this.blurHorizontal = new BlurHorizontal(gl, this.blurLevel, opts);
    this.blurVertical = new BlurVertical(gl, this.blurLevel, opts);
  }

  updateGlobals(opts) {
    this.texsizeX = opts.texsizeX;
    this.texsizeY = opts.texsizeY;

    this.setupFrameBufferTextures();
  }

  getTextureSize(sizeRatio) {
    let sizeX = Math.max(this.texsizeX * sizeRatio, 16);
    sizeX = Math.floor((sizeX + 3) / 16) * 16;
    let sizeY = Math.max(this.texsizeY * sizeRatio, 16);
    sizeY = Math.floor((sizeY + 3) / 4) * 4;
    return [sizeX, sizeY];
  }

  setupFrameBufferTextures() {
    const srcBlurRatios =
      this.blurLevel > 0 ? this.blurRatios[this.blurLevel - 1] : [1, 1];
    const dstBlurRatios = this.blurRatios[this.blurLevel];
    const srcTexsizeHorizontal = this.getTextureSize(srcBlurRatios[1]);
    const dstTexsizeHorizontal = this.getTextureSize(dstBlurRatios[0]);
    this.bindFrameBufferTexture(
      this.blurHorizontalFrameBuffer,
      this.blurHorizontalTexture,
      dstTexsizeHorizontal
    );

    const srcTexsizeVertical = dstTexsizeHorizontal;
    const dstTexsizeVertical = this.getTextureSize(dstBlurRatios[1]);
    this.bindFrameBufferTexture(
      this.blurVerticalFrameBuffer,
      this.blurVerticalTexture,
      dstTexsizeVertical
    );

    this.horizontalTexsizes = [srcTexsizeHorizontal, dstTexsizeHorizontal];
    this.verticalTexsizes = [srcTexsizeVertical, dstTexsizeVertical];
  }

  bindFrambufferAndSetViewport(fb, texsize) {
    this.gl.bindFramebuffer(this.gl.FRAMEBUFFER, fb);
    this.gl.viewport(0, 0, texsize[0], texsize[1]);
  }

  bindFrameBufferTexture(targetFrameBuffer, targetTexture, texsize) {
    this.gl.bindTexture(this.gl.TEXTURE_2D, targetTexture);

    this.gl.pixelStorei(this.gl.UNPACK_ALIGNMENT, 1);
    this.gl.texImage2D(
      this.gl.TEXTURE_2D,
      0,
      this.gl.RGBA,
      texsize[0],
      texsize[1],
      0,
      this.gl.RGBA,
      this.gl.UNSIGNED_BYTE,
      new Uint8Array(texsize[0] * texsize[1] * 4)
    );

    this.gl.generateMipmap(this.gl.TEXTURE_2D);

    this.gl.texParameteri(
      this.gl.TEXTURE_2D,
      this.gl.TEXTURE_WRAP_S,
      this.gl.CLAMP_TO_EDGE
    );
    this.gl.texParameteri(
      this.gl.TEXTURE_2D,
      this.gl.TEXTURE_WRAP_T,
      this.gl.CLAMP_TO_EDGE
    );
    this.gl.texParameteri(
      this.gl.TEXTURE_2D,
      this.gl.TEXTURE_MIN_FILTER,
      this.gl.LINEAR_MIPMAP_LINEAR
    );
    this.gl.texParameteri(
      this.gl.TEXTURE_2D,
      this.gl.TEXTURE_MAG_FILTER,
      this.gl.LINEAR
    );
    if (this.anisoExt) {
      const max = this.gl.getParameter(
        this.anisoExt.MAX_TEXTURE_MAX_ANISOTROPY_EXT
      );
      this.gl.texParameterf(
        this.gl.TEXTURE_2D,
        this.anisoExt.TEXTURE_MAX_ANISOTROPY_EXT,
        max
      );
    }

    this.gl.bindFramebuffer(this.gl.FRAMEBUFFER, targetFrameBuffer);
    this.gl.framebufferTexture2D(
      this.gl.FRAMEBUFFER,
      this.gl.COLOR_ATTACHMENT0,
      this.gl.TEXTURE_2D,
      targetTexture,
      0
    );
  }

  renderBlurTexture(prevTexture, mdVSFrame, blurMins, blurMaxs) {
    this.bindFrambufferAndSetViewport(
      this.blurHorizontalFrameBuffer,
      this.horizontalTexsizes[1]
    );
    this.blurHorizontal.renderQuadTexture(
      prevTexture,
      mdVSFrame,
      blurMins,
      blurMaxs,
      this.horizontalTexsizes[0]
    );

    this.gl.bindTexture(this.gl.TEXTURE_2D, this.blurHorizontalTexture);
    this.gl.generateMipmap(this.gl.TEXTURE_2D);

    this.bindFrambufferAndSetViewport(
      this.blurVerticalFrameBuffer,
      this.verticalTexsizes[1]
    );
    this.blurVertical.renderQuadTexture(
      this.blurHorizontalTexture,
      mdVSFrame,
      this.verticalTexsizes[0]
    );

    this.gl.bindTexture(this.gl.TEXTURE_2D, this.blurVerticalTexture);
    this.gl.generateMipmap(this.gl.TEXTURE_2D);
  }
}
Lyzon0 commented 10 months ago

Blur horizontal


import ShaderUtils from "../shaderUtils";

export default class BlurHorizontal {
  constructor(gl, blurLevel) {
    this.gl = gl;
    this.blurLevel = blurLevel;

    const w = [4.0, 3.8, 3.5, 2.9, 1.9, 1.2, 0.7, 0.3];
    const w1H = w[0] + w[1];
    const w2H = w[2] + w[3];
    const w3H = w[4] + w[5];
    const w4H = w[6] + w[7];
    const d1H = 0 + (2 * w[1]) / w1H;
    const d2H = 2 + (2 * w[3]) / w2H;
    const d3H = 4 + (2 * w[5]) / w3H;
    const d4H = 6 + (2 * w[7]) / w4H;

    this.ws = new Float32Array([w1H, w2H, w3H, w4H]);
    this.ds = new Float32Array([d1H, d2H, d3H, d4H]);
    this.wDiv = 0.5 / (w1H + w2H + w3H + w4H);

    this.positions = new Float32Array([-1, -1, 1, -1, -1, 1, 1, 1]);

    this.vertexBuf = this.gl.createBuffer();

    this.floatPrecision = ShaderUtils.getFragmentFloatPrecision(this.gl);
    this.createShader();
  }

  createShader() {
    this.shaderProgram = this.gl.createProgram();

    const vertShader = this.gl.createShader(this.gl.VERTEX_SHADER);
    this.gl.shaderSource(
      vertShader,
      `
      #version 300 es
      const vec2 halfmad = vec2(0.5);
      in vec2 aPos;
      out vec2 uv;
      void main(void) {
        gl_Position = vec4(aPos, 0.0, 1.0);
        uv = aPos * halfmad + halfmad;
      }
      `.trim()
    );
    this.gl.compileShader(vertShader);

    const fragShader = this.gl.createShader(this.gl.FRAGMENT_SHADER);
    this.gl.shaderSource(
      fragShader,
      `#version 300 es
       precision ${this.floatPrecision} float;
       precision highp int;
       precision mediump sampler2D;

       in vec2 uv;
       out vec4 fragColor;
       uniform sampler2D uTexture;
       uniform vec4 texsize;
       uniform float scale;
       uniform float bias;
       uniform vec4 ws;
       uniform vec4 ds;
       uniform float wdiv;

       void main(void) {
         float w1 = ws[0];
         float w2 = ws[1];
         float w3 = ws[2];
         float w4 = ws[3];
         float d1 = ds[0];
         float d2 = ds[1];
         float d3 = ds[2];
         float d4 = ds[3];

         vec2 uv2 = uv.xy;

         vec3 blur =
           ( texture(uTexture, uv2 + vec2( d1 * texsize.z,0.0) ).xyz
           + texture(uTexture, uv2 + vec2(-d1 * texsize.z,0.0) ).xyz) * w1 +
           ( texture(uTexture, uv2 + vec2( d2 * texsize.z,0.0) ).xyz
           + texture(uTexture, uv2 + vec2(-d2 * texsize.z,0.0) ).xyz) * w2 +
           ( texture(uTexture, uv2 + vec2( d3 * texsize.z,0.0) ).xyz
           + texture(uTexture, uv2 + vec2(-d3 * texsize.z,0.0) ).xyz) * w3 +
           ( texture(uTexture, uv2 + vec2( d4 * texsize.z,0.0) ).xyz
           + texture(uTexture, uv2 + vec2(-d4 * texsize.z,0.0) ).xyz) * w4;

         blur.xyz *= wdiv;
         blur.xyz = blur.xyz * scale + bias;

         fragColor = vec4(blur, 1.0);
       }`
    );
    this.gl.compileShader(fragShader);

    this.gl.attachShader(this.shaderProgram, vertShader);
    this.gl.attachShader(this.shaderProgram, fragShader);
    this.gl.linkProgram(this.shaderProgram);

    this.positionLocation = this.gl.getAttribLocation(
      this.shaderProgram,
      "aPos"
    );
    this.textureLoc = this.gl.getUniformLocation(
      this.shaderProgram,
      "uTexture"
    );
    this.texsizeLocation = this.gl.getUniformLocation(
      this.shaderProgram,
      "texsize"
    );
    this.scaleLoc = this.gl.getUniformLocation(this.shaderProgram, "scale");
    this.biasLoc = this.gl.getUniformLocation(this.shaderProgram, "bias");
    this.wsLoc = this.gl.getUniformLocation(this.shaderProgram, "ws");
    this.dsLocation = this.gl.getUniformLocation(this.shaderProgram, "ds");
    this.wdivLoc = this.gl.getUniformLocation(this.shaderProgram, "wdiv");
  }

  getScaleAndBias(blurMins, blurMaxs) {
    const scale = [1, 1, 1];
    const bias = [0, 0, 0];

    let tempMin;
    let tempMax;
    scale[0] = 1.0 / (blurMaxs[0] - blurMins[0]);
    bias[0] = -blurMins[0] * scale[0];
    tempMin = (blurMins[1] - blurMins[0]) / (blurMaxs[0] - blurMins[0]);
    tempMax = (blurMaxs[1] - blurMins[0]) / (blurMaxs[0] - blurMins[0]);
    scale[1] = 1.0 / (tempMax - tempMin);
    bias[1] = -tempMin * scale[1];
    tempMin = (blurMins[2] - blurMins[1]) / (blurMaxs[1] - blurMins[1]);
    tempMax = (blurMaxs[2] - blurMins[1]) / (blurMaxs[1] - blurMins[1]);
    scale[2] = 1.0 / (tempMax - tempMin);
    bias[2] = -tempMin * scale[2];

    return {
      scale: scale[this.blurLevel],
      bias: bias[this.blurLevel],
    };
  }

  renderQuadTexture(texture, mdVSFrame, blurMins, blurMaxs, srcTexsize) {
    this.gl.useProgram(this.shaderProgram);

    this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.vertexBuf);
    this.gl.bufferData(
      this.gl.ARRAY_BUFFER,
      this.positions,
      this.gl.STATIC_DRAW
    );

    this.gl.vertexAttribPointer(
      this.positionLocation,
      2,
      this.gl.FLOAT,
      false,
      0,
      0
    );
    this.gl.enableVertexAttribArray(this.positionLocation);

    this.gl.activeTexture(this.gl.TEXTURE0);
    this.gl.bindTexture(this.gl.TEXTURE_2D, texture);

    this.gl.uniform1i(this.textureLoc, 0);

    const { scale, bias } = this.getScaleAndBias(blurMins, blurMaxs);

    this.gl.uniform4fv(this.texsizeLocation, [
      srcTexsize[0],
      srcTexsize[1],
      1.0 / srcTexsize[0],
      1.0 / srcTexsize[1],
    ]);
    this.gl.uniform1f(this.scaleLoc, scale);
    this.gl.uniform1f(this.biasLoc, bias);
    this.gl.uniform4fv(this.wsLoc, this.ws);
    this.gl.uniform4fv(this.dsLocation, this.ds);
    this.gl.uniform1f(this.wdivLoc, this.wDiv);

    this.gl.blendFunc(this.gl.SRC_ALPHA, this.gl.ONE_MINUS_SRC_ALPHA);

    this.gl.drawArrays(this.gl.TRIANGLE_STRIP, 0, 4);
  }
}
Lyzon0 commented 10 months ago

import ShaderUtils from "../shaderUtils";

export default class BlurVertical {
  constructor(gl, blurLevel) {
    this.gl = gl;
    this.blurLevel = blurLevel;

    const w = [4.0, 3.8, 3.5, 2.9, 1.9, 1.2, 0.7, 0.3];
    const w1V = w[0] + w[1] + w[2] + w[3];
    const w2V = w[4] + w[5] + w[6] + w[7];
    const d1V = 0 + 2 * ((w[2] + w[3]) / w1V);
    const d2V = 2 + 2 * ((w[6] + w[7]) / w2V);

    this.wds = new Float32Array([w1V, w2V, d1V, d2V]);
    this.wDiv = 1.0 / ((w1V + w2V) * 2);

    this.positions = new Float32Array([-1, -1, 1, -1, -1, 1, 1, 1]);

    this.vertexBuf = this.gl.createBuffer();

    this.floatPrecision = ShaderUtils.getFragmentFloatPrecision(this.gl);
    this.createShader();
  }

  createShader() {
    this.shaderProgram = this.gl.createProgram();

    const vertShader = this.gl.createShader(this.gl.VERTEX_SHADER);
    this.gl.shaderSource(
      vertShader,
      `
      #version 300 es
      const vec2 halfmad = vec2(0.5);
      in vec2 aPos;
      out vec2 uv;
      void main(void) {
        gl_Position = vec4(aPos, 0.0, 1.0);
        uv = aPos * halfmad + halfmad;
      }
      `.trim()
    );
    this.gl.compileShader(vertShader);

    const fragShader = this.gl.createShader(this.gl.FRAGMENT_SHADER);
    this.gl.shaderSource(
      fragShader,
      `#version 300 es
       precision ${this.floatPrecision} float;
       precision highp int;
       precision mediump sampler2D;

       in vec2 uv;
       out vec4 fragColor;
       uniform sampler2D uTexture;
       uniform vec4 texsize;
       uniform float ed1;
       uniform float ed2;
       uniform float ed3;
       uniform vec4 wds;
       uniform float wdiv;

       void main(void) {
         float w1 = wds[0];
         float w2 = wds[1];
         float d1 = wds[2];
         float d2 = wds[3];

         vec2 uv2 = uv.xy;

         vec3 blur =
           ( texture(uTexture, uv2 + vec2(0.0, d1 * texsize.w) ).xyz
           + texture(uTexture, uv2 + vec2(0.0,-d1 * texsize.w) ).xyz) * w1 +
           ( texture(uTexture, uv2 + vec2(0.0, d2 * texsize.w) ).xyz
           + texture(uTexture, uv2 + vec2(0.0,-d2 * texsize.w) ).xyz) * w2;

         blur.xyz *= wdiv;

         float t = min(min(uv.x, uv.y), 1.0 - max(uv.x, uv.y));
         t = sqrt(t);
         t = ed1 + ed2 * clamp(t * ed3, 0.0, 1.0);
         blur.xyz *= t;

         fragColor = vec4(blur, 1.0);
       }`
    );
    this.gl.compileShader(fragShader);

    this.gl.attachShader(this.shaderProgram, vertShader);
    this.gl.attachShader(this.shaderProgram, fragShader);
    this.gl.linkProgram(this.shaderProgram);

    this.positionLocation = this.gl.getAttribLocation(
      this.shaderProgram,
      "aPos"
    );
    this.textureLoc = this.gl.getUniformLocation(
      this.shaderProgram,
      "uTexture"
    );
    this.texsizeLocation = this.gl.getUniformLocation(
      this.shaderProgram,
      "texsize"
    );
    this.ed1Loc = this.gl.getUniformLocation(this.shaderProgram, "ed1");
    this.ed2Loc = this.gl.getUniformLocation(this.shaderProgram, "ed2");
    this.ed3Loc = this.gl.getUniformLocation(this.shaderProgram, "ed3");
    this.wdsLocation = this.gl.getUniformLocation(this.shaderProgram, "wds");
    this.wdivLoc = this.gl.getUniformLocation(this.shaderProgram, "wdiv");
  }

  renderQuadTexture(texture, mdVSFrame, srcTexsize) {
    this.gl.useProgram(this.shaderProgram);

    this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.vertexBuf);
    this.gl.bufferData(
      this.gl.ARRAY_BUFFER,
      this.positions,
      this.gl.STATIC_DRAW
    );

    this.gl.vertexAttribPointer(
      this.positionLocation,
      2,
      this.gl.FLOAT,
      false,
      0,
      0
    );
    this.gl.enableVertexAttribArray(this.positionLocation);

    this.gl.activeTexture(this.gl.TEXTURE0);
    this.gl.bindTexture(this.gl.TEXTURE_2D, texture);

    this.gl.uniform1i(this.textureLoc, 0);

    const b1ed = this.blurLevel === 0 ? mdVSFrame.b1ed : 0.0;

    this.gl.uniform4fv(this.texsizeLocation, [
      srcTexsize[0],
      srcTexsize[1],
      1.0 / srcTexsize[0],
      1.0 / srcTexsize[1],
    ]);
    this.gl.uniform1f(this.ed1Loc, 1.0 - b1ed);
    this.gl.uniform1f(this.ed2Loc, b1ed);
    this.gl.uniform1f(this.ed3Loc, 5.0);
    this.gl.uniform4fv(this.wdsLocation, this.wds);
    this.gl.uniform1f(this.wdivLoc, this.wDiv);

    this.gl.blendFunc(this.gl.SRC_ALPHA, this.gl.ONE_MINUS_SRC_ALPHA);

    this.gl.drawArrays(this.gl.TRIANGLE_STRIP, 0, 4);
  }
}
Lyzon0 commented 10 months ago

Border


import ShaderUtils from "../shaders/shaderUtils";

export default class Border {
  constructor(gl, opts = {}) {
    this.gl = gl;

    this.positions = new Float32Array(72);

    this.aspectx = opts.aspectx;
    this.aspecty = opts.aspecty;
    this.invAspectx = 1.0 / this.aspectx;
    this.invAspecty = 1.0 / this.aspecty;

    this.floatPrecision = ShaderUtils.getFragmentFloatPrecision(this.gl);
    this.createShader();

    this.vertexBuf = this.gl.createBuffer();
  }

  updateGlobals(opts) {
    this.aspectx = opts.aspectx;
    this.aspecty = opts.aspecty;
    this.invAspectx = 1.0 / this.aspectx;
    this.invAspecty = 1.0 / this.aspecty;
  }

  createShader() {
    this.shaderProgram = this.gl.createProgram();

    const vertShader = this.gl.createShader(this.gl.VERTEX_SHADER);
    this.gl.shaderSource(
      vertShader,
      `
      #version 300 es
      in vec3 aPos;
      void main(void) {
        gl_Position = vec4(aPos, 1.0);
      }
      `.trim()
    );
    this.gl.compileShader(vertShader);

    const fragShader = this.gl.createShader(this.gl.FRAGMENT_SHADER);
    this.gl.shaderSource(
      fragShader,
      `
      #version 300 es
      precision ${this.floatPrecision} float;
      precision highp int;
      precision mediump sampler2D;
      out vec4 fragColor;
      uniform vec4 u_color;
      void main(void) {
        fragColor = u_color;
      }
      `.trim()
    );
    this.gl.compileShader(fragShader);

    this.gl.attachShader(this.shaderProgram, vertShader);
    this.gl.attachShader(this.shaderProgram, fragShader);
    this.gl.linkProgram(this.shaderProgram);

    this.aPosLoc = this.gl.getAttribLocation(this.shaderProgram, "aPos");

    this.colorLoc = this.gl.getUniformLocation(this.shaderProgram, "u_color");
  }

  addTriangle(offset, point1, point2, point3) {
    this.positions[offset + 0] = point1[0];
    this.positions[offset + 1] = point1[1];
    this.positions[offset + 2] = point1[2];

    this.positions[offset + 3] = point2[0];
    this.positions[offset + 4] = point2[1];
    this.positions[offset + 5] = point2[2];

    this.positions[offset + 6] = point3[0];
    this.positions[offset + 7] = point3[1];
    this.positions[offset + 8] = point3[2];
  }

  // based on https://github.com/mrdoob/three.js/blob/master/src/geometries/PlaneGeometry.js
  generateBorder(borderColor, borderSize, prevBorderSize) {
    if (borderSize > 0 && borderColor[3] > 0) {
      const width = 2;
      const height = 2;

      const widthHalf = width / 2;
      const heightHalf = height / 2;

      const prevBorderWidth = prevBorderSize / 2;
      const borderWidth = borderSize / 2 + prevBorderWidth;

      const prevBorderWidthWidth = prevBorderWidth * width;
      const prevBorderWidthHeight = prevBorderWidth * height;
      const borderWidthWidth = borderWidth * width;
      const borderWidthHeight = borderWidth * height;

      // 1st side
      let point1 = [
        -widthHalf + prevBorderWidthWidth,
        -heightHalf + borderWidthHeight,
        0,
      ];
      let point2 = [
        -widthHalf + prevBorderWidthWidth,
        heightHalf - borderWidthHeight,
        0,
      ];
      let point3 = [
        -widthHalf + borderWidthWidth,
        heightHalf - borderWidthHeight,
        0,
      ];
      let point4 = [
        -widthHalf + borderWidthWidth,
        -heightHalf + borderWidthHeight,
        0,
      ];

      this.addTriangle(0, point4, point2, point1);
      this.addTriangle(9, point4, point3, point2);

      // 2nd side
      point1 = [
        widthHalf - prevBorderWidthWidth,
        -heightHalf + borderWidthHeight,
        0,
      ];
      point2 = [
        widthHalf - prevBorderWidthWidth,
        heightHalf - borderWidthHeight,
        0,
      ];
      point3 = [
        widthHalf - borderWidthWidth,
        heightHalf - borderWidthHeight,
        0,
      ];
      point4 = [
        widthHalf - borderWidthWidth,
        -heightHalf + borderWidthHeight,
        0,
      ];

      this.addTriangle(18, point1, point2, point4);
      this.addTriangle(27, point2, point3, point4);

      // Top
      point1 = [
        -widthHalf + prevBorderWidthWidth,
        -heightHalf + prevBorderWidthHeight,
        0,
      ];
      point2 = [
        -widthHalf + prevBorderWidthWidth,
        borderWidthHeight - heightHalf,
        0,
      ];
      point3 = [
        widthHalf - prevBorderWidthWidth,
        borderWidthHeight - heightHalf,
        0,
      ];
      point4 = [
        widthHalf - prevBorderWidthWidth,
        -heightHalf + prevBorderWidthHeight,
        0,
      ];

      this.addTriangle(36, point4, point2, point1);
      this.addTriangle(45, point4, point3, point2);

      // Bottom
      point1 = [
        -widthHalf + prevBorderWidthWidth,
        heightHalf - prevBorderWidthHeight,
        0,
      ];
      point2 = [
        -widthHalf + prevBorderWidthWidth,
        heightHalf - borderWidthHeight,
        0,
      ];
      point3 = [
        widthHalf - prevBorderWidthWidth,
        heightHalf - borderWidthHeight,
        0,
      ];
      point4 = [
        widthHalf - prevBorderWidthWidth,
        heightHalf - prevBorderWidthHeight,
        0,
      ];

      this.addTriangle(54, point1, point2, point4);
      this.addTriangle(63, point2, point3, point4);

      return true;
    }

    return false;
  }

  drawBorder(borderColor, borderSize, prevBorderSize) {
    if (this.generateBorder(borderColor, borderSize, prevBorderSize)) {
      this.gl.useProgram(this.shaderProgram);

      this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.vertexBuf);
      this.gl.bufferData(
        this.gl.ARRAY_BUFFER,
        this.positions,
        this.gl.STATIC_DRAW
      );

      this.gl.vertexAttribPointer(this.aPosLoc, 3, this.gl.FLOAT, false, 0, 0);
      this.gl.enableVertexAttribArray(this.aPosLoc);

      this.gl.uniform4fv(this.colorLoc, borderColor);

      this.gl.blendFunc(this.gl.SRC_ALPHA, this.gl.ONE_MINUS_SRC_ALPHA);

      this.gl.drawArrays(this.gl.TRIANGLES, 0, this.positions.length / 3);
    }
  }
}
Lyzon0 commented 10 months ago

Comp


import ShaderUtils from "./shaderUtils";

export default class CompShader {
  constructor(gl, noise, image, opts = {}) {
    this.gl = gl;
    this.noise = noise;
    this.image = image;

    this.mesh_width = opts.mesh_width;
    this.mesh_height = opts.mesh_height;
    this.texsizeX = opts.texsizeX;
    this.texsizeY = opts.texsizeY;
    this.aspectx = opts.aspectx;
    this.aspecty = opts.aspecty;
    this.invAspectx = 1.0 / this.aspectx;
    this.invAspecty = 1.0 / this.aspecty;

    this.compWidth = 32;
    this.compHeight = 24;

    this.buildPositions();

    this.indexBuf = gl.createBuffer();
    this.positionVertexBuf = this.gl.createBuffer();
    this.compColorVertexBuf = this.gl.createBuffer();

    this.floatPrecision = ShaderUtils.getFragmentFloatPrecision(this.gl);
    this.createShader();

    this.mainSampler = this.gl.createSampler();
    this.mainSamplerFW = this.gl.createSampler();
    this.mainSamplerFC = this.gl.createSampler();
    this.mainSamplerPW = this.gl.createSampler();
    this.mainSamplerPC = this.gl.createSampler();

    gl.samplerParameteri(
      this.mainSampler,
      gl.TEXTURE_MIN_FILTER,
      gl.LINEAR_MIPMAP_LINEAR
    );
    gl.samplerParameteri(this.mainSampler, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
    gl.samplerParameteri(this.mainSampler, gl.TEXTURE_WRAP_S, gl.REPEAT);
    gl.samplerParameteri(this.mainSampler, gl.TEXTURE_WRAP_T, gl.REPEAT);

    gl.samplerParameteri(
      this.mainSamplerFW,
      gl.TEXTURE_MIN_FILTER,
      gl.LINEAR_MIPMAP_LINEAR
    );
    gl.samplerParameteri(this.mainSamplerFW, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
    gl.samplerParameteri(this.mainSamplerFW, gl.TEXTURE_WRAP_S, gl.REPEAT);
    gl.samplerParameteri(this.mainSamplerFW, gl.TEXTURE_WRAP_T, gl.REPEAT);

    gl.samplerParameteri(
      this.mainSamplerFC,
      gl.TEXTURE_MIN_FILTER,
      gl.LINEAR_MIPMAP_LINEAR
    );
    gl.samplerParameteri(this.mainSamplerFC, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
    gl.samplerParameteri(
      this.mainSamplerFC,
      gl.TEXTURE_WRAP_S,
      gl.CLAMP_TO_EDGE
    );
    gl.samplerParameteri(
      this.mainSamplerFC,
      gl.TEXTURE_WRAP_T,
      gl.CLAMP_TO_EDGE
    );

    gl.samplerParameteri(
      this.mainSamplerPW,
      gl.TEXTURE_MIN_FILTER,
      gl.NEAREST_MIPMAP_NEAREST
    );
    gl.samplerParameteri(this.mainSamplerPW, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
    gl.samplerParameteri(this.mainSamplerPW, gl.TEXTURE_WRAP_S, gl.REPEAT);
    gl.samplerParameteri(this.mainSamplerPW, gl.TEXTURE_WRAP_T, gl.REPEAT);

    gl.samplerParameteri(
      this.mainSamplerPC,
      gl.TEXTURE_MIN_FILTER,
      gl.NEAREST_MIPMAP_NEAREST
    );
    gl.samplerParameteri(this.mainSamplerPC, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
    gl.samplerParameteri(
      this.mainSamplerPC,
      gl.TEXTURE_WRAP_S,
      gl.CLAMP_TO_EDGE
    );
    gl.samplerParameteri(
      this.mainSamplerPC,
      gl.TEXTURE_WRAP_T,
      gl.CLAMP_TO_EDGE
    );
  }

  // based on https://github.com/mrdoob/three.js/blob/master/src/geometries/PlaneGeometry.js
  buildPositions() {
    const width = 2;
    const height = 2;

    const widthHalf = width / 2;
    const heightHalf = height / 2;

    const gridX = this.compWidth;
    const gridY = this.compHeight;

    const gridX1 = gridX + 1;
    const gridY1 = gridY + 1;

    const segmentWidth = width / gridX;
    const segmentHeight = height / gridY;

    const vertices = [];
    for (let iy = 0; iy < gridY1; iy++) {
      const y = iy * segmentHeight - heightHalf;
      for (let ix = 0; ix < gridX1; ix++) {
        const x = ix * segmentWidth - widthHalf;

        vertices.push(x, -y, 0);
      }
    }

    const indices = [];
    for (let iy = 0; iy < gridY; iy++) {
      for (let ix = 0; ix < gridX; ix++) {
        const a = ix + gridX1 * iy;
        const b = ix + gridX1 * (iy + 1);
        const c = ix + 1 + gridX1 * (iy + 1);
        const d = ix + 1 + gridX1 * iy;

        indices.push(a, b, d);
        indices.push(b, c, d);
      }
    }

    this.vertices = new Float32Array(vertices);
    this.indices = new Uint16Array(indices);
  }

  updateGlobals(opts) {
    this.mesh_width = opts.mesh_width;
    this.mesh_height = opts.mesh_height;
    this.texsizeX = opts.texsizeX;
    this.texsizeY = opts.texsizeY;
    this.aspectx = opts.aspectx;
    this.aspecty = opts.aspecty;
    this.invAspectx = 1.0 / this.aspectx;
    this.invAspecty = 1.0 / this.aspecty;

    this.buildPositions();
  }

  createShader(shaderText = "") {
    let fragShaderText;
    let fragShaderHeaderText;
    if (shaderText.length === 0) {
      fragShaderText = `float orient_horiz = mod(echo_orientation, 2.0);
                        float orient_x = (orient_horiz != 0.0) ? -1.0 : 1.0;
                        float orient_y = (echo_orientation >= 2.0) ? -1.0 : 1.0;
                        vec2 uv_echo = ((uv - 0.5) *
                                        (1.0 / echo_zoom) *
                                        vec2(orient_x, orient_y)) + 0.5;

                        ret = mix(texture(sampler_main, uv).rgb,
                                  texture(sampler_main, uv_echo).rgb,
                                  echo_alpha);

                        ret *= gammaAdj;

                        if(fShader >= 1.0) {
                          ret *= hue_shader;
                        } else if(fShader > 0.001) {
                          ret *= (1.0 - fShader) + (fShader * hue_shader);
                        }

                        if(brighten != 0) ret = sqrt(ret);
                        if(darken != 0) ret = ret*ret;
                        if(solarize != 0) ret = ret * (1.0 - ret) * 4.0;
                        if(invert != 0) ret = 1.0 - ret;`;
      fragShaderHeaderText = "";
    } else {
      const shaderParts = ShaderUtils.getShaderParts(shaderText);
      fragShaderHeaderText = shaderParts[0];
      fragShaderText = shaderParts[1];
    }

    fragShaderText = fragShaderText.replace(/texture2D/g, "texture");
    fragShaderText = fragShaderText.replace(/texture3D/g, "texture");

    this.userTextures = ShaderUtils.getUserSamplers(fragShaderHeaderText);

    this.shaderProgram = this.gl.createProgram();

    const vertShader = this.gl.createShader(this.gl.VERTEX_SHADER);
    this.gl.shaderSource(
      vertShader,
      `
      #version 300 es
      const vec2 halfmad = vec2(0.5);
      in vec2 aPos;
      in vec4 aCompColor;
      out vec2 vUv;
      out vec4 vColor;
      void main(void) {
        gl_Position = vec4(aPos, 0.0, 1.0);
        vUv = aPos * halfmad + halfmad;
        vColor = aCompColor;
      }
      `.trim()
    );
    this.gl.compileShader(vertShader);

    const fragShader = this.gl.createShader(this.gl.FRAGMENT_SHADER);
    this.gl.shaderSource(
      fragShader,
      `
      #version 300 es
      precision ${this.floatPrecision} float;
      precision highp int;
      precision mediump sampler2D;
      precision mediump sampler3D;

      vec3 lum(vec3 v){
          return vec3(dot(v, vec3(0.32,0.49,0.29)));
      }

      in vec2 vUv;
      in vec4 vColor;
      out vec4 fragColor;
      uniform sampler2D sampler_main;
      uniform sampler2D sampler_fw_main;
      uniform sampler2D sampler_fc_main;
      uniform sampler2D sampler_pw_main;
      uniform sampler2D sampler_pc_main;
      uniform sampler2D sampler_blur1;
      uniform sampler2D sampler_blur2;
      uniform sampler2D sampler_blur3;
      uniform sampler2D sampler_noise_lq;
      uniform sampler2D sampler_noise_lq_lite;
      uniform sampler2D sampler_noise_mq;
      uniform sampler2D sampler_noise_hq;
      uniform sampler2D sampler_pw_noise_lq;
      uniform sampler3D sampler_noisevol_lq;
      uniform sampler3D sampler_noisevol_hq;

      uniform float time;
      uniform float gammaAdj;
      uniform float echo_zoom;
      uniform float echo_alpha;
      uniform float echo_orientation;
      uniform int invert;
      uniform int brighten;
      uniform int darken;
      uniform int solarize;
      uniform vec2 resolution;
      uniform vec4 aspect;
      uniform vec4 texsize;
      uniform vec4 texsize_noise_lq;
      uniform vec4 texsize_noise_mq;
      uniform vec4 texsize_noise_hq;
      uniform vec4 texsize_noise_lq_lite;
      uniform vec4 texsize_noisevol_lq;
      uniform vec4 texsize_noisevol_hq;

      uniform float bass;
      uniform float mid;
      uniform float treb;
      uniform float vol;
      uniform float bass_att;
      uniform float mid_att;
      uniform float treb_att;
      uniform float vol_att;

      uniform float frame;
      uniform float fps;

      uniform vec4 _qa;
      uniform vec4 _qb;
      uniform vec4 _qc;
      uniform vec4 _qd;
      uniform vec4 _qe;
      uniform vec4 _qf;
      uniform vec4 _qg;
      uniform vec4 _qh;

      #define q1 _qa.x
      #define q2 _qa.y
      #define q3 _qa.z
      #define q4 _qa.w
      #define q5 _qb.x
      #define q6 _qb.y
      #define q7 _qb.z
      #define q8 _qb.w
      #define q9 _qc.x
      #define q10 _qc.y
      #define q11 _qc.z
      #define q12 _qc.w
      #define q13 _qd.x
      #define q14 _qd.y
      #define q15 _qd.z
      #define q16 _qd.w
      #define q17 _qe.x
      #define q18 _qe.y
      #define q19 _qe.z
      #define q20 _qe.w
      #define q21 _qf.x
      #define q22 _qf.y
      #define q23 _qf.z
      #define q24 _qf.w
      #define q25 _qg.x
      #define q26 _qg.y
      #define q27 _qg.z
      #define q28 _qg.w
      #define q29 _qh.x
      #define q30 _qh.y
      #define q31 _qh.z
      #define q32 _qh.w

      uniform vec4 slow_roam_cos;
      uniform vec4 roam_cos;
      uniform vec4 slow_roam_sin;
      uniform vec4 roam_sin;

      uniform float blur1_min;
      uniform float blur1_max;
      uniform float blur2_min;
      uniform float blur2_max;
      uniform float blur3_min;
      uniform float blur3_max;

      uniform float scale1;
      uniform float scale2;
      uniform float scale3;
      uniform float bias1;
      uniform float bias2;
      uniform float bias3;

      uniform vec4 rand_frame;
      uniform vec4 rand_preset;

      uniform float fShader;

      float PI = ${Math.PI};

      ${fragShaderHeaderText}

      void main(void) {
        vec3 ret;
        vec2 uv = vUv;
        vec2 uv_orig = vUv;
        uv.y = 1.0 - uv.y;
        uv_orig.y = 1.0 - uv_orig.y;
        float rad = length(uv - 0.5);
        float ang = atan(uv.x - 0.5, uv.y - 0.5);
        vec3 hue_shader = vColor.rgb;

        ${fragShaderText}

        fragColor = vec4(ret, vColor.a);
      }
      `.trim()
    );
    this.gl.compileShader(fragShader);

    this.gl.attachShader(this.shaderProgram, vertShader);
    this.gl.attachShader(this.shaderProgram, fragShader);
    this.gl.linkProgram(this.shaderProgram);

    this.positionLocation = this.gl.getAttribLocation(
      this.shaderProgram,
      "aPos"
    );
    this.compColorLocation = this.gl.getAttribLocation(
      this.shaderProgram,
      "aCompColor"
    );
    this.textureLoc = this.gl.getUniformLocation(
      this.shaderProgram,
      "sampler_main"
    );
    this.textureFWLoc = this.gl.getUniformLocation(
      this.shaderProgram,
      "sampler_fw_main"
    );
    this.textureFCLoc = this.gl.getUniformLocation(
      this.shaderProgram,
      "sampler_fc_main"
    );
    this.texturePWLoc = this.gl.getUniformLocation(
      this.shaderProgram,
      "sampler_pw_main"
    );
    this.texturePCLoc = this.gl.getUniformLocation(
      this.shaderProgram,
      "sampler_pc_main"
    );
    this.blurTexture1Loc = this.gl.getUniformLocation(
      this.shaderProgram,
      "sampler_blur1"
    );
    this.blurTexture2Loc = this.gl.getUniformLocation(
      this.shaderProgram,
      "sampler_blur2"
    );
    this.blurTexture3Loc = this.gl.getUniformLocation(
      this.shaderProgram,
      "sampler_blur3"
    );
    this.noiseLQLoc = this.gl.getUniformLocation(
      this.shaderProgram,
      "sampler_noise_lq"
    );
    this.noiseMQLoc = this.gl.getUniformLocation(
      this.shaderProgram,
      "sampler_noise_mq"
    );
    this.noiseHQLoc = this.gl.getUniformLocation(
      this.shaderProgram,
      "sampler_noise_hq"
    );
    this.noiseLQLiteLoc = this.gl.getUniformLocation(
      this.shaderProgram,
      "sampler_noise_lq_lite"
    );
    this.noisePointLQLoc = this.gl.getUniformLocation(
      this.shaderProgram,
      "sampler_pw_noise_lq"
    );
    this.noiseVolLQLoc = this.gl.getUniformLocation(
      this.shaderProgram,
      "sampler_noisevol_lq"
    );
    this.noiseVolHQLoc = this.gl.getUniformLocation(
      this.shaderProgram,
      "sampler_noisevol_hq"
    );
    this.timeLoc = this.gl.getUniformLocation(this.shaderProgram, "time");
    this.gammaAdjLoc = this.gl.getUniformLocation(
      this.shaderProgram,
      "gammaAdj"
    );
    this.echoZoomLoc = this.gl.getUniformLocation(
      this.shaderProgram,
      "echo_zoom"
    );
    this.echoAlphaLoc = this.gl.getUniformLocation(
      this.shaderProgram,
      "echo_alpha"
    );
    this.echoOrientationLoc = this.gl.getUniformLocation(
      this.shaderProgram,
      "echo_orientation"
    );
    this.invertLoc = this.gl.getUniformLocation(this.shaderProgram, "invert");
    this.brightenLoc = this.gl.getUniformLocation(
      this.shaderProgram,
      "brighten"
    );
    this.darkenLoc = this.gl.getUniformLocation(this.shaderProgram, "darken");
    this.solarizeLoc = this.gl.getUniformLocation(
      this.shaderProgram,
      "solarize"
    );
    this.texsizeLoc = this.gl.getUniformLocation(this.shaderProgram, "texsize");
    this.texsizeNoiseLQLoc = this.gl.getUniformLocation(
      this.shaderProgram,
      "texsize_noise_lq"
    );
    this.texsizeNoiseMQLoc = this.gl.getUniformLocation(
      this.shaderProgram,
      "texsize_noise_mq"
    );
    this.texsizeNoiseHQLoc = this.gl.getUniformLocation(
      this.shaderProgram,
      "texsize_noise_hq"
    );
    this.texsizeNoiseLQLiteLoc = this.gl.getUniformLocation(
      this.shaderProgram,
      "texsize_noise_lq_lite"
    );
    this.texsizeNoiseVolLQLoc = this.gl.getUniformLocation(
      this.shaderProgram,
      "texsize_noisevol_lq"
    );
    this.texsizeNoiseVolHQLoc = this.gl.getUniformLocation(
      this.shaderProgram,
      "texsize_noisevol_hq"
    );
    this.resolutionLoc = this.gl.getUniformLocation(
      this.shaderProgram,
      "resolution"
    );
    this.aspectLoc = this.gl.getUniformLocation(this.shaderProgram, "aspect");
    this.bassLoc = this.gl.getUniformLocation(this.shaderProgram, "bass");
    this.midLoc = this.gl.getUniformLocation(this.shaderProgram, "mid");
    this.trebLoc = this.gl.getUniformLocation(this.shaderProgram, "treb");
    this.volLoc = this.gl.getUniformLocation(this.shaderProgram, "vol");
    this.bassAttLoc = this.gl.getUniformLocation(
      this.shaderProgram,
      "bass_att"
    );
    this.midAttLoc = this.gl.getUniformLocation(this.shaderProgram, "mid_att");
    this.trebAttLoc = this.gl.getUniformLocation(
      this.shaderProgram,
      "treb_att"
    );
    this.volAttLoc = this.gl.getUniformLocation(this.shaderProgram, "vol_att");
    this.frameLoc = this.gl.getUniformLocation(this.shaderProgram, "frame");
    this.fpsLoc = this.gl.getUniformLocation(this.shaderProgram, "fps");
    this.blur1MinLoc = this.gl.getUniformLocation(
      this.shaderProgram,
      "blur1_min"
    );
    this.blur1MaxLoc = this.gl.getUniformLocation(
      this.shaderProgram,
      "blur1_max"
    );
    this.blur2MinLoc = this.gl.getUniformLocation(
      this.shaderProgram,
      "blur2_min"
    );
    this.blur2MaxLoc = this.gl.getUniformLocation(
      this.shaderProgram,
      "blur2_max"
    );
    this.blur3MinLoc = this.gl.getUniformLocation(
      this.shaderProgram,
      "blur3_min"
    );
    this.blur3MaxLoc = this.gl.getUniformLocation(
      this.shaderProgram,
      "blur3_max"
    );
    this.scale1Loc = this.gl.getUniformLocation(this.shaderProgram, "scale1");
    this.scale2Loc = this.gl.getUniformLocation(this.shaderProgram, "scale2");
    this.scale3Loc = this.gl.getUniformLocation(this.shaderProgram, "scale3");
    this.bias1Loc = this.gl.getUniformLocation(this.shaderProgram, "bias1");
    this.bias2Loc = this.gl.getUniformLocation(this.shaderProgram, "bias2");
    this.bias3Loc = this.gl.getUniformLocation(this.shaderProgram, "bias3");
    this.randPresetLoc = this.gl.getUniformLocation(
      this.shaderProgram,
      "rand_preset"
    );
    this.randFrameLoc = this.gl.getUniformLocation(
      this.shaderProgram,
      "rand_frame"
    );
    this.fShaderLoc = this.gl.getUniformLocation(this.shaderProgram, "fShader");

    this.qaLoc = this.gl.getUniformLocation(this.shaderProgram, "_qa");
    this.qbLoc = this.gl.getUniformLocation(this.shaderProgram, "_qb");
    this.qcLoc = this.gl.getUniformLocation(this.shaderProgram, "_qc");
    this.qdLoc = this.gl.getUniformLocation(this.shaderProgram, "_qd");
    this.qeLoc = this.gl.getUniformLocation(this.shaderProgram, "_qe");
    this.qfLoc = this.gl.getUniformLocation(this.shaderProgram, "_qf");
    this.qgLoc = this.gl.getUniformLocation(this.shaderProgram, "_qg");
    this.qhLoc = this.gl.getUniformLocation(this.shaderProgram, "_qh");

    this.slowRoamCosLoc = this.gl.getUniformLocation(
      this.shaderProgram,
      "slow_roam_cos"
    );
    this.roamCosLoc = this.gl.getUniformLocation(
      this.shaderProgram,
      "roam_cos"
    );
    this.slowRoamSinLoc = this.gl.getUniformLocation(
      this.shaderProgram,
      "slow_roam_sin"
    );
    this.roamSinLoc = this.gl.getUniformLocation(
      this.shaderProgram,
      "roam_sin"
    );

    for (let i = 0; i < this.userTextures.length; i++) {
      const userTexture = this.userTextures[i];
      userTexture.textureLoc = this.gl.getUniformLocation(
        this.shaderProgram,
        `sampler_${userTexture.sampler}`
      );
    }
  }

  updateShader(shaderText) {
    this.createShader(shaderText);
  }

  bindBlurVals(blurMins, blurMaxs) {
    const blurMin1 = blurMins[0];
    const blurMin2 = blurMins[1];
    const blurMin3 = blurMins[2];
    const blurMax1 = blurMaxs[0];
    const blurMax2 = blurMaxs[1];
    const blurMax3 = blurMaxs[2];

    const scale1 = blurMax1 - blurMin1;
    const bias1 = blurMin1;

    const scale2 = blurMax2 - blurMin2;
    const bias2 = blurMin2;

    const scale3 = blurMax3 - blurMin3;
    const bias3 = blurMin3;

    this.gl.uniform1f(this.blur1MinLoc, blurMin1);
    this.gl.uniform1f(this.blur1MaxLoc, blurMax1);
    this.gl.uniform1f(this.blur2MinLoc, blurMin2);
    this.gl.uniform1f(this.blur2MaxLoc, blurMax2);
    this.gl.uniform1f(this.blur3MinLoc, blurMin3);
    this.gl.uniform1f(this.blur3MaxLoc, blurMax3);
    this.gl.uniform1f(this.scale1Loc, scale1);
    this.gl.uniform1f(this.scale2Loc, scale2);
    this.gl.uniform1f(this.scale3Loc, scale3);
    this.gl.uniform1f(this.bias1Loc, bias1);
    this.gl.uniform1f(this.bias2Loc, bias2);
    this.gl.uniform1f(this.bias3Loc, bias3);
  }

  static generateHueBase(mdVSFrame) {
    const hueBase = new Float32Array([1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]);

    /* eslint-disable max-len */
    for (let i = 0; i < 4; i++) {
      hueBase[i * 3 + 0] =
        0.6 +
        0.3 *
          Math.sin(
            mdVSFrame.time * 30.0 * 0.0143 +
              3 +
              i * 21 +
              mdVSFrame.rand_start[3]
          );
      hueBase[i * 3 + 1] =
        0.6 +
        0.3 *
          Math.sin(
            mdVSFrame.time * 30.0 * 0.0107 +
              1 +
              i * 13 +
              mdVSFrame.rand_start[1]
          );
      hueBase[i * 3 + 2] =
        0.6 +
        0.3 *
          Math.sin(
            mdVSFrame.time * 30.0 * 0.0129 + 6 + i * 9 + mdVSFrame.rand_start[2]
          );
      const maxshade = Math.max(
        hueBase[i * 3],
        hueBase[i * 3 + 1],
        hueBase[i * 3 + 2]
      );
      for (let k = 0; k < 3; k++) {
        hueBase[i * 3 + k] = hueBase[i * 3 + k] / maxshade;
        hueBase[i * 3 + k] = 0.5 + 0.5 * hueBase[i * 3 + k];
      }
    }
    /* eslint-enable max-len */

    return hueBase;
  }

  generateCompColors(blending, mdVSFrame, warpColor) {
    const hueBase = CompShader.generateHueBase(mdVSFrame);
    const gridX1 = this.compWidth + 1;
    const gridY1 = this.compHeight + 1;
    const compColor = new Float32Array(gridX1 * gridY1 * 4);

    let offsetColor = 0;
    for (let j = 0; j < gridY1; j++) {
      for (let i = 0; i < gridX1; i++) {
        let x = i / this.compWidth;
        let y = j / this.compHeight;

        const col = [1, 1, 1];
        for (let c = 0; c < 3; c++) {
          col[c] =
            hueBase[0 + c] * x * y +
            hueBase[3 + c] * (1 - x) * y +
            hueBase[6 + c] * x * (1 - y) +
            hueBase[9 + c] * (1 - x) * (1 - y);
        }

        let alpha = 1;
        if (blending) {
          x *= this.mesh_width + 1;
          y *= this.mesh_height + 1;
          x = Math.clamp(x, 0, this.mesh_width - 1);
          y = Math.clamp(y, 0, this.mesh_height - 1);
          const nx = Math.floor(x);
          const ny = Math.floor(y);
          const dx = x - nx;
          const dy = y - ny;
          const alpha00 = warpColor[(ny * (this.mesh_width + 1) + nx) * 4 + 3];
          const alpha01 =
            warpColor[(ny * (this.mesh_width + 1) + (nx + 1)) * 4 + 3];
          const alpha10 =
            warpColor[((ny + 1) * (this.mesh_width + 1) + nx) * 4 + 3];
          const alpha11 =
            warpColor[((ny + 1) * (this.mesh_width + 1) + (nx + 1)) * 4 + 3];
          alpha =
            alpha00 * (1 - dx) * (1 - dy) +
            alpha01 * dx * (1 - dy) +
            alpha10 * (1 - dx) * dy +
            alpha11 * dx * dy;
        }

        compColor[offsetColor + 0] = col[0];
        compColor[offsetColor + 1] = col[1];
        compColor[offsetColor + 2] = col[2];
        compColor[offsetColor + 3] = alpha;

        offsetColor += 4;
      }
    }

    return compColor;
  }

  renderQuadTexture(
    blending,
    texture,
    blurTexture1,
    blurTexture2,
    blurTexture3,
    blurMins,
    blurMaxs,
    mdVSFrame,
    mdVSQs,
    warpColor
  ) {
    const compColors = this.generateCompColors(blending, mdVSFrame, warpColor);

    this.gl.useProgram(this.shaderProgram);

    this.gl.bindBuffer(this.gl.ELEMENT_ARRAY_BUFFER, this.indexBuf);
    this.gl.bufferData(
      this.gl.ELEMENT_ARRAY_BUFFER,
      this.indices,
      this.gl.STATIC_DRAW
    );

    this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.positionVertexBuf);
    this.gl.bufferData(
      this.gl.ARRAY_BUFFER,
      this.vertices,
      this.gl.STATIC_DRAW
    );

    this.gl.vertexAttribPointer(
      this.positionLocation,
      3,
      this.gl.FLOAT,
      false,
      0,
      0
    );
    this.gl.enableVertexAttribArray(this.positionLocation);

    this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.compColorVertexBuf);
    this.gl.bufferData(this.gl.ARRAY_BUFFER, compColors, this.gl.STATIC_DRAW);

    this.gl.vertexAttribPointer(
      this.compColorLocation,
      4,
      this.gl.FLOAT,
      false,
      0,
      0
    );
    this.gl.enableVertexAttribArray(this.compColorLocation);

    const wrapping =
      mdVSFrame.wrap !== 0 ? this.gl.REPEAT : this.gl.CLAMP_TO_EDGE;
    this.gl.samplerParameteri(
      this.mainSampler,
      this.gl.TEXTURE_WRAP_S,
      wrapping
    );
    this.gl.samplerParameteri(
      this.mainSampler,
      this.gl.TEXTURE_WRAP_T,
      wrapping
    );

    this.gl.activeTexture(this.gl.TEXTURE0);
    this.gl.bindTexture(this.gl.TEXTURE_2D, texture);
    this.gl.bindSampler(0, this.mainSampler);
    this.gl.uniform1i(this.textureLoc, 0);

    this.gl.activeTexture(this.gl.TEXTURE1);
    this.gl.bindTexture(this.gl.TEXTURE_2D, texture);
    this.gl.bindSampler(1, this.mainSamplerFW);
    this.gl.uniform1i(this.textureFWLoc, 1);

    this.gl.activeTexture(this.gl.TEXTURE2);
    this.gl.bindTexture(this.gl.TEXTURE_2D, texture);
    this.gl.bindSampler(2, this.mainSamplerFC);
    this.gl.uniform1i(this.textureFCLoc, 2);

    this.gl.activeTexture(this.gl.TEXTURE3);
    this.gl.bindTexture(this.gl.TEXTURE_2D, texture);
    this.gl.bindSampler(3, this.mainSamplerPW);
    this.gl.uniform1i(this.texturePWLoc, 3);

    this.gl.activeTexture(this.gl.TEXTURE4);
    this.gl.bindTexture(this.gl.TEXTURE_2D, texture);
    this.gl.bindSampler(4, this.mainSamplerPC);
    this.gl.uniform1i(this.texturePCLoc, 4);

    this.gl.activeTexture(this.gl.TEXTURE5);
    this.gl.bindTexture(this.gl.TEXTURE_2D, blurTexture1);
    this.gl.uniform1i(this.blurTexture1Loc, 5);

    this.gl.activeTexture(this.gl.TEXTURE6);
    this.gl.bindTexture(this.gl.TEXTURE_2D, blurTexture2);
    this.gl.uniform1i(this.blurTexture2Loc, 6);

    this.gl.activeTexture(this.gl.TEXTURE7);
    this.gl.bindTexture(this.gl.TEXTURE_2D, blurTexture3);
    this.gl.uniform1i(this.blurTexture3Loc, 7);

    this.gl.activeTexture(this.gl.TEXTURE8);
    this.gl.bindTexture(this.gl.TEXTURE_2D, this.noise.noiseTexLQ);
    this.gl.uniform1i(this.noiseLQLoc, 8);

    this.gl.activeTexture(this.gl.TEXTURE9);
    this.gl.bindTexture(this.gl.TEXTURE_2D, this.noise.noiseTexMQ);
    this.gl.uniform1i(this.noiseMQLoc, 9);

    this.gl.activeTexture(this.gl.TEXTURE10);
    this.gl.bindTexture(this.gl.TEXTURE_2D, this.noise.noiseTexHQ);
    this.gl.uniform1i(this.noiseHQLoc, 10);

    this.gl.activeTexture(this.gl.TEXTURE11);
    this.gl.bindTexture(this.gl.TEXTURE_2D, this.noise.noiseTexLQLite);
    this.gl.uniform1i(this.noiseLQLiteLoc, 11);

    this.gl.activeTexture(this.gl.TEXTURE12);
    this.gl.bindTexture(this.gl.TEXTURE_2D, this.noise.noiseTexLQ);
    this.gl.bindSampler(12, this.noise.noiseTexPointLQ);
    this.gl.uniform1i(this.noisePointLQLoc, 12);

    this.gl.activeTexture(this.gl.TEXTURE13);
    this.gl.bindTexture(this.gl.TEXTURE_3D, this.noise.noiseTexVolLQ);
    this.gl.uniform1i(this.noiseVolLQLoc, 13);

    this.gl.activeTexture(this.gl.TEXTURE14);
    this.gl.bindTexture(this.gl.TEXTURE_3D, this.noise.noiseTexVolHQ);
    this.gl.uniform1i(this.noiseVolHQLoc, 14);

    for (let i = 0; i < this.userTextures.length; i++) {
      const userTexture = this.userTextures[i];
      this.gl.activeTexture(this.gl.TEXTURE15 + i);
      this.gl.bindTexture(
        this.gl.TEXTURE_2D,
        this.image.getTexture(userTexture.sampler)
      );
      this.gl.uniform1i(userTexture.textureLoc, 15 + i);
    }

    this.gl.uniform1f(this.timeLoc, mdVSFrame.time);
    this.gl.uniform1f(this.gammaAdjLoc, mdVSFrame.gammaadj);
    this.gl.uniform1f(this.echoZoomLoc, mdVSFrame.echo_zoom);
    this.gl.uniform1f(this.echoAlphaLoc, mdVSFrame.echo_alpha);
    this.gl.uniform1f(this.echoOrientationLoc, mdVSFrame.echo_orient);
    this.gl.uniform1i(this.invertLoc, mdVSFrame.invert);
    this.gl.uniform1i(this.brightenLoc, mdVSFrame.brighten);
    this.gl.uniform1i(this.darkenLoc, mdVSFrame.darken);
    this.gl.uniform1i(this.solarizeLoc, mdVSFrame.solarize);
    this.gl.uniform2fv(this.resolutionLoc, [this.texsizeX, this.texsizeY]);
    this.gl.uniform4fv(this.aspectLoc, [
      this.aspectx,
      this.aspecty,
      this.invAspectx,
      this.invAspecty,
    ]);
    this.gl.uniform4fv(
      this.texsizeLoc,
      new Float32Array([
        this.texsizeX,
        this.texsizeY,
        1.0 / this.texsizeX,
        1.0 / this.texsizeY,
      ])
    );
    this.gl.uniform4fv(this.texsizeNoiseLQLoc, [256, 256, 1 / 256, 1 / 256]);
    this.gl.uniform4fv(this.texsizeNoiseMQLoc, [256, 256, 1 / 256, 1 / 256]);
    this.gl.uniform4fv(this.texsizeNoiseHQLoc, [256, 256, 1 / 256, 1 / 256]);
    this.gl.uniform4fv(this.texsizeNoiseLQLiteLoc, [32, 32, 1 / 32, 1 / 32]);
    this.gl.uniform4fv(this.texsizeNoiseVolLQLoc, [32, 32, 1 / 32, 1 / 32]);
    this.gl.uniform4fv(this.texsizeNoiseVolHQLoc, [32, 32, 1 / 32, 1 / 32]);
    this.gl.uniform1f(this.bassLoc, mdVSFrame.bass);
    this.gl.uniform1f(this.midLoc, mdVSFrame.mid);
    this.gl.uniform1f(this.trebLoc, mdVSFrame.treb);
    this.gl.uniform1f(
      this.volLoc,
      (mdVSFrame.bass + mdVSFrame.mid + mdVSFrame.treb) / 3
    );
    this.gl.uniform1f(this.bassAttLoc, mdVSFrame.bass_att);
    this.gl.uniform1f(this.midAttLoc, mdVSFrame.mid_att);
    this.gl.uniform1f(this.trebAttLoc, mdVSFrame.treb_att);
    this.gl.uniform1f(
      this.volAttLoc,
      (mdVSFrame.bass_att + mdVSFrame.mid_att + mdVSFrame.treb_att) / 3
    );
    this.gl.uniform1f(this.frameLoc, mdVSFrame.frame);
    this.gl.uniform1f(this.fpsLoc, mdVSFrame.fps);
    this.gl.uniform4fv(this.randPresetLoc, mdVSFrame.rand_preset);
    this.gl.uniform4fv(
      this.randFrameLoc,
      new Float32Array([
        Math.random(),
        Math.random(),
        Math.random(),
        Math.random(),
      ])
    );
    this.gl.uniform1f(this.fShaderLoc, mdVSFrame.fshader);

    this.gl.uniform4fv(
      this.qaLoc,
      new Float32Array([
        mdVSQs.q1 || 0,
        mdVSQs.q2 || 0,
        mdVSQs.q3 || 0,
        mdVSQs.q4 || 0,
      ])
    );
    this.gl.uniform4fv(
      this.qbLoc,
      new Float32Array([
        mdVSQs.q5 || 0,
        mdVSQs.q6 || 0,
        mdVSQs.q7 || 0,
        mdVSQs.q8 || 0,
      ])
    );
    this.gl.uniform4fv(
      this.qcLoc,
      new Float32Array([
        mdVSQs.q9 || 0,
        mdVSQs.q10 || 0,
        mdVSQs.q11 || 0,
        mdVSQs.q12 || 0,
      ])
    );
    this.gl.uniform4fv(
      this.qdLoc,
      new Float32Array([
        mdVSQs.q13 || 0,
        mdVSQs.q14 || 0,
        mdVSQs.q15 || 0,
        mdVSQs.q16 || 0,
      ])
    );
    this.gl.uniform4fv(
      this.qeLoc,
      new Float32Array([
        mdVSQs.q17 || 0,
        mdVSQs.q18 || 0,
        mdVSQs.q19 || 0,
        mdVSQs.q20 || 0,
      ])
    );
    this.gl.uniform4fv(
      this.qfLoc,
      new Float32Array([
        mdVSQs.q21 || 0,
        mdVSQs.q22 || 0,
        mdVSQs.q23 || 0,
        mdVSQs.q24 || 0,
      ])
    );
    this.gl.uniform4fv(
      this.qgLoc,
      new Float32Array([
        mdVSQs.q25 || 0,
        mdVSQs.q26 || 0,
        mdVSQs.q27 || 0,
        mdVSQs.q28 || 0,
      ])
    );
    this.gl.uniform4fv(
      this.qhLoc,
      new Float32Array([
        mdVSQs.q29 || 0,
        mdVSQs.q30 || 0,
        mdVSQs.q31 || 0,
        mdVSQs.q32 || 0,
      ])
    );
    this.gl.uniform4fv(this.slowRoamCosLoc, [
      0.5 + 0.5 * Math.cos(mdVSFrame.time * 0.005),
      0.5 + 0.5 * Math.cos(mdVSFrame.time * 0.008),
      0.5 + 0.5 * Math.cos(mdVSFrame.time * 0.013),
      0.5 + 0.5 * Math.cos(mdVSFrame.time * 0.022),
    ]);
    this.gl.uniform4fv(this.roamCosLoc, [
      0.5 + 0.5 * Math.cos(mdVSFrame.time * 0.3),
      0.5 + 0.5 * Math.cos(mdVSFrame.time * 1.3),
      0.5 + 0.5 * Math.cos(mdVSFrame.time * 5.0),
      0.5 + 0.5 * Math.cos(mdVSFrame.time * 20.0),
    ]);
    this.gl.uniform4fv(this.slowRoamSinLoc, [
      0.5 + 0.5 * Math.sin(mdVSFrame.time * 0.005),
      0.5 + 0.5 * Math.sin(mdVSFrame.time * 0.008),
      0.5 + 0.5 * Math.sin(mdVSFrame.time * 0.013),
      0.5 + 0.5 * Math.sin(mdVSFrame.time * 0.022),
    ]);
    this.gl.uniform4fv(this.roamSinLoc, [
      0.5 + 0.5 * Math.sin(mdVSFrame.time * 0.3),
      0.5 + 0.5 * Math.sin(mdVSFrame.time * 1.3),
      0.5 + 0.5 * Math.sin(mdVSFrame.time * 5.0),
      0.5 + 0.5 * Math.sin(mdVSFrame.time * 20.0),
    ]);

    this.bindBlurVals(blurMins, blurMaxs);

    if (blending) {
      this.gl.blendFunc(this.gl.SRC_ALPHA, this.gl.ONE_MINUS_SRC_ALPHA);
    } else {
      this.gl.disable(this.gl.BLEND);
    }

    this.gl.drawElements(
      this.gl.TRIANGLES,
      this.indices.length,
      this.gl.UNSIGNED_SHORT,
      0
    );

    if (!blending) {
      this.gl.enable(this.gl.BLEND);
    }
  }
}
Lyzon0 commented 10 months ago

Custom shape


import Utils from "../../utils";
import ShaderUtils from "../shaders/shaderUtils";

export default class CustomShape {
  constructor(index, gl, opts) {
    this.index = index;
    this.gl = gl;

    const maxSides = 101;
    this.positions = new Float32Array((maxSides + 2) * 3);
    this.colors = new Float32Array((maxSides + 2) * 4);
    this.uvs = new Float32Array((maxSides + 2) * 2);
    this.borderPositions = new Float32Array((maxSides + 1) * 3);

    this.texsizeX = opts.texsizeX;
    this.texsizeY = opts.texsizeY;
    this.mesh_width = opts.mesh_width;
    this.mesh_height = opts.mesh_height;
    this.aspectx = opts.aspectx;
    this.aspecty = opts.aspecty;
    this.invAspectx = 1.0 / this.aspectx;
    this.invAspecty = 1.0 / this.aspecty;

    this.positionVertexBuf = this.gl.createBuffer();
    this.colorVertexBuf = this.gl.createBuffer();
    this.uvVertexBuf = this.gl.createBuffer();
    this.borderPositionVertexBuf = this.gl.createBuffer();

    this.floatPrecision = ShaderUtils.getFragmentFloatPrecision(this.gl);
    this.createShader();
    this.createBorderShader();

    this.mainSampler = this.gl.createSampler();

    gl.samplerParameteri(
      this.mainSampler,
      gl.TEXTURE_MIN_FILTER,
      gl.LINEAR_MIPMAP_LINEAR
    );
    gl.samplerParameteri(this.mainSampler, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
    gl.samplerParameteri(this.mainSampler, gl.TEXTURE_WRAP_S, gl.REPEAT);
    gl.samplerParameteri(this.mainSampler, gl.TEXTURE_WRAP_T, gl.REPEAT);
  }

  updateGlobals(opts) {
    this.texsizeX = opts.texsizeX;
    this.texsizeY = opts.texsizeY;
    this.mesh_width = opts.mesh_width;
    this.mesh_height = opts.mesh_height;
    this.aspectx = opts.aspectx;
    this.aspecty = opts.aspecty;
    this.invAspectx = 1.0 / this.aspectx;
    this.invAspecty = 1.0 / this.aspecty;
  }

  createShader() {
    this.shaderProgram = this.gl.createProgram();

    const vertShader = this.gl.createShader(this.gl.VERTEX_SHADER);
    this.gl.shaderSource(
      vertShader,
      `
      #version 300 es
      in vec3 aPos;
      in vec4 aColor;
      in vec2 aUv;
      out vec4 vColor;
      out vec2 vUv;
      void main(void) {
        vColor = aColor;
        vUv = aUv;
        gl_Position = vec4(aPos, 1.0);
      }
      `.trim()
    );
    this.gl.compileShader(vertShader);

    const fragShader = this.gl.createShader(this.gl.FRAGMENT_SHADER);
    this.gl.shaderSource(
      fragShader,
      `
      #version 300 es
      precision ${this.floatPrecision} float;
      precision highp int;
      precision mediump sampler2D;
      uniform sampler2D uTexture;
      uniform float uTextured;
      in vec4 vColor;
      in vec2 vUv;
      out vec4 fragColor;
      void main(void) {
        if (uTextured != 0.0) {
          fragColor = texture(uTexture, vUv) * vColor;
        } else {
          fragColor = vColor;
        }
      }
      `.trim()
    );
    this.gl.compileShader(fragShader);

    this.gl.attachShader(this.shaderProgram, vertShader);
    this.gl.attachShader(this.shaderProgram, fragShader);
    this.gl.linkProgram(this.shaderProgram);

    this.aPosLocation = this.gl.getAttribLocation(this.shaderProgram, "aPos");
    this.aColorLocation = this.gl.getAttribLocation(
      this.shaderProgram,
      "aColor"
    );
    this.aUvLocation = this.gl.getAttribLocation(this.shaderProgram, "aUv");

    this.texturedLoc = this.gl.getUniformLocation(
      this.shaderProgram,
      "uTextured"
    );
    this.textureLoc = this.gl.getUniformLocation(
      this.shaderProgram,
      "uTexture"
    );
  }

  createBorderShader() {
    this.borderShaderProgram = this.gl.createProgram();

    const vertShader = this.gl.createShader(this.gl.VERTEX_SHADER);
    this.gl.shaderSource(
      vertShader,
      `
      #version 300 es
      in vec3 aBorderPos;
      uniform vec2 thickOffset;
      void main(void) {
        gl_Position = vec4(aBorderPos +
                            vec3(thickOffset, 0.0), 1.0);
      }
      `.trim()
    );
    this.gl.compileShader(vertShader);

    const fragShader = this.gl.createShader(this.gl.FRAGMENT_SHADER);
    this.gl.shaderSource(
      fragShader,
      `
      #version 300 es
      precision ${this.floatPrecision} float;
      precision highp int;
      precision mediump sampler2D;
      out vec4 fragColor;
      uniform vec4 uBorderColor;
      void main(void) {
        fragColor = uBorderColor;
      }
      `.trim()
    );
    this.gl.compileShader(fragShader);

    this.gl.attachShader(this.borderShaderProgram, vertShader);
    this.gl.attachShader(this.borderShaderProgram, fragShader);
    this.gl.linkProgram(this.borderShaderProgram);

    this.aBorderPosLoc = this.gl.getAttribLocation(
      this.borderShaderProgram,
      "aBorderPos"
    );

    this.uBorderColorLoc = this.gl.getUniformLocation(
      this.borderShaderProgram,
      "uBorderColor"
    );
    this.thickOffsetLoc = this.gl.getUniformLocation(
      this.shaderProgram,
      "thickOffset"
    );
  }

  drawCustomShape(
    blendProgress,
    globalVars,
    presetEquationRunner,
    shapeEqs,
    prevTexture
  ) {
    if (shapeEqs.baseVals.enabled !== 0) {
      if (!presetEquationRunner.preset.useWASM) {
        this.setupShapeBuffers(presetEquationRunner.mdVSFrame.wrap);

        let mdVSShape = Object.assign(
          {},
          presetEquationRunner.mdVSShapes[this.index],
          presetEquationRunner.mdVSFrameMapShapes[this.index],
          globalVars
        );

        // If we aren't setting these every instance, set them initially
        if (
          presetEquationRunner.preset.shapes[this.index].frame_eqs_str === ""
        ) {
          mdVSShape = Object.assign(
            mdVSShape,
            presetEquationRunner.mdVSQAfterFrame,
            presetEquationRunner.mdVSTShapeInits[this.index]
          );
        }

        const baseVals =
          presetEquationRunner.preset.shapes[this.index].baseVals;

        const numInst = Math.clamp(baseVals.num_inst, 1, 1024);
        for (let j = 0; j < numInst; j++) {
          mdVSShape.instance = j;
          mdVSShape.x = baseVals.x;
          mdVSShape.y = baseVals.y;
          mdVSShape.rad = baseVals.rad;
          mdVSShape.ang = baseVals.ang;
          mdVSShape.r = baseVals.r;
          mdVSShape.g = baseVals.g;
          mdVSShape.b = baseVals.b;
          mdVSShape.a = baseVals.a;
          mdVSShape.r2 = baseVals.r2;
          mdVSShape.g2 = baseVals.g2;
          mdVSShape.b2 = baseVals.b2;
          mdVSShape.a2 = baseVals.a2;
          mdVSShape.border_r = baseVals.border_r;
          mdVSShape.border_g = baseVals.border_g;
          mdVSShape.border_b = baseVals.border_b;
          mdVSShape.border_a = baseVals.border_a;
          mdVSShape.thickoutline = baseVals.thickoutline;
          mdVSShape.textured = baseVals.textured;
          mdVSShape.tex_zoom = baseVals.tex_zoom;
          mdVSShape.tex_ang = baseVals.tex_ang;
          mdVSShape.additive = baseVals.additive;

          let mdVSShapeFrame;
          if (
            presetEquationRunner.preset.shapes[this.index].frame_eqs_str !== ""
          ) {
            mdVSShape = Object.assign(
              mdVSShape,
              presetEquationRunner.mdVSQAfterFrame,
              presetEquationRunner.mdVSTShapeInits[this.index]
            );

            mdVSShapeFrame = presetEquationRunner.runShapeFrameEquations(
              this.index,
              mdVSShape
            );
          } else {
            mdVSShapeFrame = mdVSShape;
          }

          let sides = mdVSShapeFrame.sides;
          sides = Math.clamp(sides, 3, 100);
          sides = Math.floor(sides);

          const rad = mdVSShapeFrame.rad;
          const ang = mdVSShapeFrame.ang;

          const x = mdVSShapeFrame.x * 2 - 1;
          const y = mdVSShapeFrame.y * -2 + 1;

          const r = mdVSShapeFrame.r;
          const g = mdVSShapeFrame.g;
          const b = mdVSShapeFrame.b;
          const a = mdVSShapeFrame.a;
          const r2 = mdVSShapeFrame.r2;
          const g2 = mdVSShapeFrame.g2;
          const b2 = mdVSShapeFrame.b2;
          const a2 = mdVSShapeFrame.a2;

          const borderR = mdVSShapeFrame.border_r;
          const borderG = mdVSShapeFrame.border_g;
          const borderB = mdVSShapeFrame.border_b;
          const borderA = mdVSShapeFrame.border_a;
          this.borderColor = [
            borderR,
            borderG,
            borderB,
            borderA * blendProgress,
          ];

          const thickoutline = mdVSShapeFrame.thickoutline;

          const textured = mdVSShapeFrame.textured;
          const texZoom = mdVSShapeFrame.tex_zoom;
          const texAng = mdVSShapeFrame.tex_ang;

          const additive = mdVSShapeFrame.additive;

          const hasBorder = this.borderColor[3] > 0;
          const isTextured = Math.abs(textured) >= 1;
          const isBorderThick = Math.abs(thickoutline) >= 1;
          const isAdditive = Math.abs(additive) >= 1;

          this.positions[0] = x;
          this.positions[1] = y;
          this.positions[2] = 0;

          this.colors[0] = r;
          this.colors[1] = g;
          this.colors[2] = b;
          this.colors[3] = a * blendProgress;

          if (isTextured) {
            this.uvs[0] = 0.5;
            this.uvs[1] = 0.5;
          }

          const quarterPi = Math.PI * 0.25;
          for (let k = 1; k <= sides + 1; k++) {
            const p = (k - 1) / sides;
            const pTwoPi = p * 2 * Math.PI;

            const angSum = pTwoPi + ang + quarterPi;
            this.positions[k * 3 + 0] =
              x + rad * Math.cos(angSum) * this.aspecty;
            this.positions[k * 3 + 1] = y + rad * Math.sin(angSum);
            this.positions[k * 3 + 2] = 0;

            this.colors[k * 4 + 0] = r2;
            this.colors[k * 4 + 1] = g2;
            this.colors[k * 4 + 2] = b2;
            this.colors[k * 4 + 3] = a2 * blendProgress;

            if (isTextured) {
              const texAngSum = pTwoPi + texAng + quarterPi;
              this.uvs[k * 2 + 0] =
                0.5 + ((0.5 * Math.cos(texAngSum)) / texZoom) * this.aspecty;
              this.uvs[k * 2 + 1] = 0.5 + (0.5 * Math.sin(texAngSum)) / texZoom;
            }

            if (hasBorder) {
              this.borderPositions[(k - 1) * 3 + 0] = this.positions[k * 3 + 0];
              this.borderPositions[(k - 1) * 3 + 1] = this.positions[k * 3 + 1];
              this.borderPositions[(k - 1) * 3 + 2] = this.positions[k * 3 + 2];
            }
          }

          this.mdVSShapeFrame = mdVSShapeFrame;

          this.drawCustomShapeInstance(
            prevTexture,
            sides,
            isTextured,
            hasBorder,
            isBorderThick,
            isAdditive
          );
        }

        const mdVSUserKeysShape =
          presetEquationRunner.mdVSUserKeysShapes[this.index];
        const mdVSNewFrameMapShape = Utils.pick(
          this.mdVSShapeFrame,
          mdVSUserKeysShape
        );

        // eslint-disable-next-line no-param-reassign
        presetEquationRunner.mdVSFrameMapShapes[
          this.index
        ] = mdVSNewFrameMapShape;
      } else {
        // eslint-disable-next-line max-len
        this.setupShapeBuffers(
          presetEquationRunner.preset.globalPools.perFrame.wrap.value
        );

        const baseVals =
          presetEquationRunner.preset.shapes[this.index].baseVals;
        const varPool =
          presetEquationRunner.preset.globalPools[`shapePerFrame${this.index}`];
        Utils.setWasm(varPool, globalVars, presetEquationRunner.globalKeys);

        // If we aren't setting these every instance, set them initially
        if (!presetEquationRunner.preset.shapes[this.index].frame_eqs) {
          presetEquationRunner.preset.restore_qs();
        }

        Utils.setWasm(
          varPool,
          presetEquationRunner.mdVSTShapeInits[this.index],
          presetEquationRunner.ts
        );
        presetEquationRunner.preset.save_ts();

        varPool.x.value = baseVals.x;
        varPool.y.value = baseVals.y;
        varPool.rad.value = baseVals.rad;
        varPool.ang.value = baseVals.ang;
        varPool.r.value = baseVals.r;
        varPool.g.value = baseVals.g;
        varPool.b.value = baseVals.b;
        varPool.a.value = baseVals.a;
        varPool.r2.value = baseVals.r2;
        varPool.g2.value = baseVals.g2;
        varPool.b2.value = baseVals.b2;
        varPool.a2.value = baseVals.a2;
        varPool.border_r.value = baseVals.border_r;
        varPool.border_g.value = baseVals.border_g;
        varPool.border_b.value = baseVals.border_b;
        varPool.border_a.value = baseVals.border_a;
        varPool.thickoutline.value = baseVals.thickoutline;
        varPool.textured.value = baseVals.textured;
        varPool.tex_zoom.value = baseVals.tex_zoom;
        varPool.tex_ang.value = baseVals.tex_ang;
        varPool.additive.value = baseVals.additive;
        presetEquationRunner.preset.shapes[this.index].frame_eqs_save();

        const numInst = Math.clamp(baseVals.num_inst, 1, 1024);
        for (let j = 0; j < numInst; j++) {
          varPool.instance.value = j;

          // this condition should check the JS equations because of comments
          if (presetEquationRunner.preset.shapes[this.index].frame_eqs) {
            presetEquationRunner.preset.shapes[this.index].frame_eqs_restore();
            presetEquationRunner.preset.restore_qs();
            presetEquationRunner.preset.restore_ts();
            presetEquationRunner.preset.shapes[this.index].frame_eqs();
          }

          let sides = varPool.sides.value;
          sides = Math.clamp(sides, 3, 100);
          sides = Math.floor(sides);

          const rad = varPool.rad.value;
          const ang = varPool.ang.value;

          const x = varPool.x.value * 2 - 1;
          const y = varPool.y.value * -2 + 1;

          const r = varPool.r.value;
          const g = varPool.g.value;
          const b = varPool.b.value;
          const a = varPool.a.value;
          const r2 = varPool.r2.value;
          const g2 = varPool.g2.value;
          const b2 = varPool.b2.value;
          const a2 = varPool.a2.value;

          const borderR = varPool.border_r.value;
          const borderG = varPool.border_g.value;
          const borderB = varPool.border_b.value;
          const borderA = varPool.border_a.value;
          this.borderColor = [
            borderR,
            borderG,
            borderB,
            borderA * blendProgress,
          ];

          const thickoutline = varPool.thickoutline.value;

          const textured = varPool.textured.value;
          const texZoom = varPool.tex_zoom.value;
          const texAng = varPool.tex_ang.value;

          const additive = varPool.additive.value;

          const hasBorder = this.borderColor[3] > 0;
          const isTextured = Math.abs(textured) >= 1;
          const isBorderThick = Math.abs(thickoutline) >= 1;
          const isAdditive = Math.abs(additive) >= 1;

          this.positions[0] = x;
          this.positions[1] = y;
          this.positions[2] = 0;

          this.colors[0] = r;
          this.colors[1] = g;
          this.colors[2] = b;
          this.colors[3] = a * blendProgress;

          if (isTextured) {
            this.uvs[0] = 0.5;
            this.uvs[1] = 0.5;
          }

          const quarterPi = Math.PI * 0.25;
          for (let k = 1; k <= sides + 1; k++) {
            const p = (k - 1) / sides;
            const pTwoPi = p * 2 * Math.PI;

            const angSum = pTwoPi + ang + quarterPi;
            this.positions[k * 3 + 0] =
              x + rad * Math.cos(angSum) * this.aspecty;
            this.positions[k * 3 + 1] = y + rad * Math.sin(angSum);
            this.positions[k * 3 + 2] = 0;

            this.colors[k * 4 + 0] = r2;
            this.colors[k * 4 + 1] = g2;
            this.colors[k * 4 + 2] = b2;
            this.colors[k * 4 + 3] = a2 * blendProgress;

            if (isTextured) {
              const texAngSum = pTwoPi + texAng + quarterPi;
              this.uvs[k * 2 + 0] =
                0.5 + ((0.5 * Math.cos(texAngSum)) / texZoom) * this.aspecty;
              this.uvs[k * 2 + 1] = 0.5 + (0.5 * Math.sin(texAngSum)) / texZoom;
            }

            if (hasBorder) {
              this.borderPositions[(k - 1) * 3 + 0] = this.positions[k * 3 + 0];
              this.borderPositions[(k - 1) * 3 + 1] = this.positions[k * 3 + 1];
              this.borderPositions[(k - 1) * 3 + 2] = this.positions[k * 3 + 2];
            }
          }

          this.drawCustomShapeInstance(
            prevTexture,
            sides,
            isTextured,
            hasBorder,
            isBorderThick,
            isAdditive
          );
        }
      }
    }
  }

  setupShapeBuffers(wrap) {
    this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.positionVertexBuf);
    this.gl.bufferData(
      this.gl.ARRAY_BUFFER,
      this.positions,
      this.gl.DYNAMIC_DRAW
    );

    this.gl.vertexAttribPointer(
      this.aPosLocation,
      3,
      this.gl.FLOAT,
      false,
      0,
      0
    );
    this.gl.enableVertexAttribArray(this.aPosLocation);

    this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.colorVertexBuf);
    this.gl.bufferData(this.gl.ARRAY_BUFFER, this.colors, this.gl.DYNAMIC_DRAW);

    this.gl.vertexAttribPointer(
      this.aColorLocation,
      4,
      this.gl.FLOAT,
      false,
      0,
      0
    );
    this.gl.enableVertexAttribArray(this.aColorLocation);

    this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.uvVertexBuf);
    this.gl.bufferData(this.gl.ARRAY_BUFFER, this.uvs, this.gl.DYNAMIC_DRAW);

    this.gl.vertexAttribPointer(
      this.aUvLocation,
      2,
      this.gl.FLOAT,
      false,
      0,
      0
    );
    this.gl.enableVertexAttribArray(this.aUvLocation);

    this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.borderPositionVertexBuf);
    this.gl.bufferData(
      this.gl.ARRAY_BUFFER,
      this.borderPositions,
      this.gl.DYNAMIC_DRAW
    );

    this.gl.vertexAttribPointer(
      this.aBorderPosLoc,
      3,
      this.gl.FLOAT,
      false,
      0,
      0
    );
    this.gl.enableVertexAttribArray(this.aBorderPosLoc);

    const wrapping = wrap !== 0 ? this.gl.REPEAT : this.gl.CLAMP_TO_EDGE;
    this.gl.samplerParameteri(
      this.mainSampler,
      this.gl.TEXTURE_WRAP_S,
      wrapping
    );
    this.gl.samplerParameteri(
      this.mainSampler,
      this.gl.TEXTURE_WRAP_T,
      wrapping
    );
  }

  drawCustomShapeInstance(
    prevTexture,
    sides,
    isTextured,
    hasBorder,
    isBorderThick,
    isAdditive
  ) {
    this.gl.useProgram(this.shaderProgram);

    this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.positionVertexBuf);
    this.gl.bufferSubData(
      this.gl.ARRAY_BUFFER,
      0,
      this.positions,
      0,
      (sides + 2) * 3
    );

    this.gl.vertexAttribPointer(
      this.aPosLocation,
      3,
      this.gl.FLOAT,
      false,
      0,
      0
    );
    this.gl.enableVertexAttribArray(this.aPosLocation);

    this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.colorVertexBuf);
    this.gl.bufferSubData(
      this.gl.ARRAY_BUFFER,
      0,
      this.colors,
      0,
      (sides + 2) * 4
    );

    this.gl.vertexAttribPointer(
      this.aColorLocation,
      4,
      this.gl.FLOAT,
      false,
      0,
      0
    );
    this.gl.enableVertexAttribArray(this.aColorLocation);

    if (isTextured) {
      this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.uvVertexBuf);
      this.gl.bufferSubData(
        this.gl.ARRAY_BUFFER,
        0,
        this.uvs,
        0,
        (sides + 2) * 2
      );

      this.gl.vertexAttribPointer(
        this.aUvLocation,
        2,
        this.gl.FLOAT,
        false,
        0,
        0
      );
      this.gl.enableVertexAttribArray(this.aUvLocation);
    }

    this.gl.uniform1f(this.texturedLoc, isTextured ? 1 : 0);

    this.gl.activeTexture(this.gl.TEXTURE0);
    this.gl.bindTexture(this.gl.TEXTURE_2D, prevTexture);
    this.gl.bindSampler(0, this.mainSampler);
    this.gl.uniform1i(this.textureLoc, 0);

    if (isAdditive) {
      this.gl.blendFunc(this.gl.SRC_ALPHA, this.gl.ONE);
    } else {
      this.gl.blendFunc(this.gl.SRC_ALPHA, this.gl.ONE_MINUS_SRC_ALPHA);
    }

    this.gl.drawArrays(this.gl.TRIANGLE_FAN, 0, sides + 2);

    if (hasBorder) {
      this.gl.useProgram(this.borderShaderProgram);

      this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.borderPositionVertexBuf);
      this.gl.bufferSubData(
        this.gl.ARRAY_BUFFER,
        0,
        this.borderPositions,
        0,
        (sides + 1) * 3
      );

      this.gl.vertexAttribPointer(
        this.aBorderPosLoc,
        3,
        this.gl.FLOAT,
        false,
        0,
        0
      );
      this.gl.enableVertexAttribArray(this.aBorderPosLoc);

      this.gl.uniform4fv(this.uBorderColorLoc, this.borderColor);

      // TODO: use drawArraysInstanced
      const instances = isBorderThick ? 4 : 1;
      for (let i = 0; i < instances; i++) {
        const offset = 2;
        if (i === 0) {
          this.gl.uniform2fv(this.thickOffsetLoc, [0, 0]);
        } else if (i === 1) {
          this.gl.uniform2fv(this.thickOffsetLoc, [offset / this.texsizeX, 0]);
        } else if (i === 2) {
          this.gl.uniform2fv(this.thickOffsetLoc, [0, offset / this.texsizeY]);
        } else if (i === 3) {
          this.gl.uniform2fv(this.thickOffsetLoc, [
            offset / this.texsizeX,
            offset / this.texsizeY,
          ]);
        }

        this.gl.drawArrays(this.gl.LINE_STRIP, 0, sides + 1);
      }
    }
  }
}
Lyzon0 commented 10 months ago

Custom waveform


import Utils from "../../utils";
import ShaderUtils from "../shaders/shaderUtils";
import WaveUtils from "./waveUtils";

export default class CustomWaveform {
  constructor(index, gl, opts) {
    this.index = index;
    this.gl = gl;

    const maxSamples = 512;
    this.pointsData = [
      new Float32Array(maxSamples),
      new Float32Array(maxSamples),
    ];
    this.positions = new Float32Array(maxSamples * 3);
    this.colors = new Float32Array(maxSamples * 4);
    this.smoothedPositions = new Float32Array((maxSamples * 2 - 1) * 3);
    this.smoothedColors = new Float32Array((maxSamples * 2 - 1) * 4);

    this.texsizeX = opts.texsizeX;
    this.texsizeY = opts.texsizeY;
    this.mesh_width = opts.mesh_width;
    this.mesh_height = opts.mesh_height;
    this.aspectx = opts.aspectx;
    this.aspecty = opts.aspecty;
    this.invAspectx = 1.0 / this.aspectx;
    this.invAspecty = 1.0 / this.aspecty;

    this.positionVertexBuf = this.gl.createBuffer();
    this.colorVertexBuf = this.gl.createBuffer();

    this.floatPrecision = ShaderUtils.getFragmentFloatPrecision(this.gl);
    this.createShader();
  }

  updateGlobals(opts) {
    this.texsizeX = opts.texsizeX;
    this.texsizeY = opts.texsizeY;
    this.mesh_width = opts.mesh_width;
    this.mesh_height = opts.mesh_height;
    this.aspectx = opts.aspectx;
    this.aspecty = opts.aspecty;
    this.invAspectx = 1.0 / this.aspectx;
    this.invAspecty = 1.0 / this.aspecty;
  }

  createShader() {
    this.shaderProgram = this.gl.createProgram();

    const vertShader = this.gl.createShader(this.gl.VERTEX_SHADER);
    this.gl.shaderSource(
      vertShader,
      `
      #version 300 es
      uniform float uSize;
      uniform vec2 thickOffset;
      in vec3 aPos;
      in vec4 aColor;
      out vec4 vColor;
      void main(void) {
        vColor = aColor;
        gl_PointSize = uSize;
        gl_Position = vec4(aPos + vec3(thickOffset, 0.0), 1.0);
      }
      `.trim()
    );
    this.gl.compileShader(vertShader);

    const fragShader = this.gl.createShader(this.gl.FRAGMENT_SHADER);
    this.gl.shaderSource(
      fragShader,
      `
      #version 300 es
      precision ${this.floatPrecision} float;
      precision highp int;
      precision mediump sampler2D;
      in vec4 vColor;
      out vec4 fragColor;
      void main(void) {
        fragColor = vColor;
      }
      `.trim()
    );
    this.gl.compileShader(fragShader);

    this.gl.attachShader(this.shaderProgram, vertShader);
    this.gl.attachShader(this.shaderProgram, fragShader);
    this.gl.linkProgram(this.shaderProgram);

    this.aPosLocation = this.gl.getAttribLocation(this.shaderProgram, "aPos");
    this.aColorLocation = this.gl.getAttribLocation(
      this.shaderProgram,
      "aColor"
    );

    this.sizeLoc = this.gl.getUniformLocation(this.shaderProgram, "uSize");
    this.thickOffsetLoc = this.gl.getUniformLocation(
      this.shaderProgram,
      "thickOffset"
    );
  }

  generateWaveform(
    timeArrayL,
    timeArrayR,
    freqArrayL,
    freqArrayR,
    globalVars,
    presetEquationRunner,
    waveEqs,
    alphaMult
  ) {
    if (waveEqs.baseVals.enabled !== 0 && timeArrayL.length > 0) {
      let mdVSWaveFrame;
      if (presetEquationRunner.preset.useWASM) {
        mdVSWaveFrame = presetEquationRunner.runWaveFrameEquations(
          this.index,
          globalVars
        );
      } else {
        const mdVSWave = Object.assign(
          {},
          presetEquationRunner.mdVSWaves[this.index],
          presetEquationRunner.mdVSFrameMapWaves[this.index],
          presetEquationRunner.mdVSQAfterFrame,
          presetEquationRunner.mdVSTWaveInits[this.index],
          globalVars
        );

        mdVSWaveFrame = presetEquationRunner.runWaveFrameEquations(
          this.index,
          mdVSWave
        );
      }

      const maxSamples = 512;
      if (Object.prototype.hasOwnProperty.call(mdVSWaveFrame, "samples")) {
        this.samples = mdVSWaveFrame.samples;
      } else {
        this.samples = maxSamples;
      }

      if (this.samples > maxSamples) {
        this.samples = maxSamples;
      }
      this.samples = Math.floor(this.samples);

      const baseVals = presetEquationRunner.preset.waves[this.index].baseVals;

      const sep = Math.floor(mdVSWaveFrame.sep);
      const scaling = mdVSWaveFrame.scaling;
      const spectrum = mdVSWaveFrame.spectrum;
      const smoothing = mdVSWaveFrame.smoothing;
      const usedots = baseVals.usedots;

      const frameR = mdVSWaveFrame.r;
      const frameG = mdVSWaveFrame.g;
      const frameB = mdVSWaveFrame.b;
      const frameA = mdVSWaveFrame.a;

      const waveScale = presetEquationRunner.preset.baseVals.wave_scale;

      this.samples -= sep;

      if (this.samples >= 2 || (usedots !== 0 && this.samples >= 1)) {
        const useSpectrum = spectrum !== 0;
        const scale = (useSpectrum ? 0.15 : 0.004) * scaling * waveScale;
        const pointsLeft = useSpectrum ? freqArrayL : timeArrayL;
        const pointsRight = useSpectrum ? freqArrayR : timeArrayR;

        const j0 = useSpectrum
          ? 0
          : Math.floor((maxSamples - this.samples) / 2 - sep / 2);
        const j1 = useSpectrum
          ? 0
          : Math.floor((maxSamples - this.samples) / 2 + sep / 2);
        const t = useSpectrum ? (maxSamples - sep) / this.samples : 1;
        const mix1 = (smoothing * 0.98) ** 0.5;
        const mix2 = 1 - mix1;

        // Milkdrop smooths waveform forward, backward and then scales
        this.pointsData[0][0] = pointsLeft[j0];
        this.pointsData[1][0] = pointsRight[j1];
        for (let j = 1; j < this.samples; j++) {
          const left = pointsLeft[Math.floor(j * t + j0)];
          const right = pointsRight[Math.floor(j * t + j1)];
          this.pointsData[0][j] =
            left * mix2 + this.pointsData[0][j - 1] * mix1;
          this.pointsData[1][j] =
            right * mix2 + this.pointsData[1][j - 1] * mix1;
        }
        for (let j = this.samples - 2; j >= 0; j--) {
          this.pointsData[0][j] =
            this.pointsData[0][j] * mix2 + this.pointsData[0][j + 1] * mix1;
          this.pointsData[1][j] =
            this.pointsData[1][j] * mix2 + this.pointsData[1][j + 1] * mix1;
        }
        for (let j = 0; j < this.samples; j++) {
          this.pointsData[0][j] *= scale;
          this.pointsData[1][j] *= scale;
        }

        if (!presetEquationRunner.preset.useWASM) {
          for (let j = 0; j < this.samples; j++) {
            const value1 = this.pointsData[0][j];
            const value2 = this.pointsData[1][j];

            mdVSWaveFrame.sample = j / (this.samples - 1);
            mdVSWaveFrame.value1 = value1;
            mdVSWaveFrame.value2 = value2;
            mdVSWaveFrame.x = 0.5 + value1;
            mdVSWaveFrame.y = 0.5 + value2;
            mdVSWaveFrame.r = frameR;
            mdVSWaveFrame.g = frameG;
            mdVSWaveFrame.b = frameB;
            mdVSWaveFrame.a = frameA;

            if (waveEqs.point_eqs !== "") {
              mdVSWaveFrame = presetEquationRunner.runWavePointEquations(
                this.index,
                mdVSWaveFrame
              );
            }

            const x = (mdVSWaveFrame.x * 2 - 1) * this.invAspectx;
            const y = (mdVSWaveFrame.y * -2 + 1) * this.invAspecty;
            const r = mdVSWaveFrame.r;
            const g = mdVSWaveFrame.g;
            const b = mdVSWaveFrame.b;
            const a = mdVSWaveFrame.a;

            this.positions[j * 3 + 0] = x;
            this.positions[j * 3 + 1] = y;
            this.positions[j * 3 + 2] = 0;

            this.colors[j * 4 + 0] = r;
            this.colors[j * 4 + 1] = g;
            this.colors[j * 4 + 2] = b;
            this.colors[j * 4 + 3] = a * alphaMult;
          }
        } else {
          const varPool =
            presetEquationRunner.preset.globalPools[
              `wavePerFrame${this.index}`
            ];
          for (let j = 0; j < this.samples; j++) {
            const value1 = this.pointsData[0][j];
            const value2 = this.pointsData[1][j];

            varPool.sample.value = j / (this.samples - 1);
            varPool.value1.value = value1;
            varPool.value2.value = value2;
            varPool.x.value = 0.5 + value1;
            varPool.y.value = 0.5 + value2;
            varPool.r.value = frameR;
            varPool.g.value = frameG;
            varPool.b.value = frameB;
            varPool.a.value = frameA;

            if (waveEqs.point_eqs) {
              presetEquationRunner.preset.waves[this.index].point_eqs();
            }

            const x = (varPool.x.value * 2 - 1) * this.invAspectx;
            const y = (varPool.y.value * -2 + 1) * this.invAspecty;
            const r = varPool.r.value;
            const g = varPool.g.value;
            const b = varPool.b.value;
            const a = varPool.a.value;

            this.positions[j * 3 + 0] = x;
            this.positions[j * 3 + 1] = y;
            this.positions[j * 3 + 2] = 0;

            this.colors[j * 4 + 0] = r;
            this.colors[j * 4 + 1] = g;
            this.colors[j * 4 + 2] = b;
            this.colors[j * 4 + 3] = a * alphaMult;
          }
        }

        // this needs to be after per point (check fishbrain - witchcraft)
        if (!presetEquationRunner.preset.useWASM) {
          const mdvsUserKeysWave =
            presetEquationRunner.mdVSUserKeysWaves[this.index];
          const mdVSNewFrameMapWave = Utils.pick(
            mdVSWaveFrame,
            mdvsUserKeysWave
          );

          // eslint-disable-next-line no-param-reassign
          presetEquationRunner.mdVSFrameMapWaves[
            this.index
          ] = mdVSNewFrameMapWave;
        } else {
          mdVSWaveFrame.usedots = usedots;
          mdVSWaveFrame.thick = baseVals.thick;
          mdVSWaveFrame.additive = baseVals.additive;
        }

        this.mdVSWaveFrame = mdVSWaveFrame;

        if (usedots === 0) {
          WaveUtils.smoothWaveAndColor(
            this.positions,
            this.colors,
            this.smoothedPositions,
            this.smoothedColors,
            this.samples
          );
        }

        return true;
      }
    }

    return false;
  }

  drawCustomWaveform(
    blendProgress,
    timeArrayL,
    timeArrayR,
    freqArrayL,
    freqArrayR,
    globalVars,
    presetEquationRunner,
    waveEqs
  ) {
    if (
      waveEqs &&
      this.generateWaveform(
        timeArrayL,
        timeArrayR,
        freqArrayL,
        freqArrayR,
        globalVars,
        presetEquationRunner,
        waveEqs,
        blendProgress
      )
    ) {
      this.gl.useProgram(this.shaderProgram);

      const waveUseDots = this.mdVSWaveFrame.usedots !== 0;
      const waveThick = this.mdVSWaveFrame.thick !== 0;
      const waveAdditive = this.mdVSWaveFrame.additive !== 0;

      let positions;
      let colors;
      let numVerts;
      if (!waveUseDots) {
        positions = this.smoothedPositions;
        colors = this.smoothedColors;
        numVerts = this.samples * 2 - 1;
      } else {
        positions = this.positions;
        colors = this.colors;
        numVerts = this.samples;
      }

      this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.positionVertexBuf);
      this.gl.bufferData(this.gl.ARRAY_BUFFER, positions, this.gl.STATIC_DRAW);

      this.gl.vertexAttribPointer(
        this.aPosLocation,
        3,
        this.gl.FLOAT,
        false,
        0,
        0
      );
      this.gl.enableVertexAttribArray(this.aPosLocation);

      this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.colorVertexBuf);
      this.gl.bufferData(this.gl.ARRAY_BUFFER, colors, this.gl.STATIC_DRAW);

      this.gl.vertexAttribPointer(
        this.aColorLocation,
        4,
        this.gl.FLOAT,
        false,
        0,
        0
      );
      this.gl.enableVertexAttribArray(this.aColorLocation);

      let instances = 1;
      if (waveUseDots) {
        if (waveThick) {
          this.gl.uniform1f(this.sizeLoc, 2 + (this.texsizeX >= 1024 ? 1 : 0));
        } else {
          this.gl.uniform1f(this.sizeLoc, 1 + (this.texsizeX >= 1024 ? 1 : 0));
        }
      } else {
        this.gl.uniform1f(this.sizeLoc, 1);
        if (waveThick) {
          instances = 4;
        }
      }

      if (waveAdditive) {
        this.gl.blendFunc(this.gl.SRC_ALPHA, this.gl.ONE);
      } else {
        this.gl.blendFunc(this.gl.SRC_ALPHA, this.gl.ONE_MINUS_SRC_ALPHA);
      }

      const drawMode = waveUseDots ? this.gl.POINTS : this.gl.LINE_STRIP;

      // TODO: use drawArraysInstanced
      for (let i = 0; i < instances; i++) {
        const offset = 2;
        if (i === 0) {
          this.gl.uniform2fv(this.thickOffsetLoc, [0, 0]);
        } else if (i === 1) {
          this.gl.uniform2fv(this.thickOffsetLoc, [offset / this.texsizeX, 0]);
        } else if (i === 2) {
          this.gl.uniform2fv(this.thickOffsetLoc, [0, offset / this.texsizeY]);
        } else if (i === 3) {
          this.gl.uniform2fv(this.thickOffsetLoc, [
            offset / this.texsizeX,
            offset / this.texsizeY,
          ]);
        }

        this.gl.drawArrays(drawMode, 0, numVerts);
      }
    }
  }
}
Lyzon0 commented 10 months ago

Darken center


import ShaderUtils from "../shaders/shaderUtils";

export default class CustomShape {
  constructor(gl, opts) {
    this.gl = gl;

    this.aspectx = opts.aspectx;
    this.aspecty = opts.aspecty;
    this.invAspectx = 1.0 / this.aspectx;
    this.invAspecty = 1.0 / this.aspecty;

    this.generatePositions();

    this.colors = new Float32Array([
      0,
      0,
      0,
      3 / 32,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
    ]);

    this.positionVertexBuf = this.gl.createBuffer();
    this.colorVertexBuf = this.gl.createBuffer();

    this.floatPrecision = ShaderUtils.getFragmentFloatPrecision(this.gl);
    this.createShader();
  }

  updateGlobals(opts) {
    this.aspectx = opts.aspectx;
    this.aspecty = opts.aspecty;
    this.invAspectx = 1.0 / this.aspectx;
    this.invAspecty = 1.0 / this.aspecty;

    this.generatePositions();
  }

  generatePositions() {
    const halfSize = 0.05;
    this.positions = new Float32Array([
      0,
      0,
      0,
      -halfSize * this.aspecty,
      0,
      0,
      0,
      -halfSize,
      0,
      halfSize * this.aspecty,
      0,
      0,
      0,
      halfSize,
      0,
      -halfSize * this.aspecty,
      0,
      0,
    ]);
  }

  createShader() {
    this.shaderProgram = this.gl.createProgram();

    const vertShader = this.gl.createShader(this.gl.VERTEX_SHADER);
    this.gl.shaderSource(
      vertShader,
      `
      #version 300 es
      in vec3 aPos;
      in vec4 aColor;
      out vec4 vColor;
      void main(void) {
        vColor = aColor;
        gl_Position = vec4(aPos, 1.0);
      }
      `.trim()
    );
    this.gl.compileShader(vertShader);

    const fragShader = this.gl.createShader(this.gl.FRAGMENT_SHADER);
    this.gl.shaderSource(
      fragShader,
      `
      #version 300 es
      precision ${this.floatPrecision} float;
      precision highp int;
      precision mediump sampler2D;
      in vec4 vColor;
      out vec4 fragColor;
      void main(void) {
        fragColor = vColor;
      }
      `.trim()
    );
    this.gl.compileShader(fragShader);

    this.gl.attachShader(this.shaderProgram, vertShader);
    this.gl.attachShader(this.shaderProgram, fragShader);
    this.gl.linkProgram(this.shaderProgram);

    this.aPosLocation = this.gl.getAttribLocation(this.shaderProgram, "aPos");
    this.aColorLocation = this.gl.getAttribLocation(
      this.shaderProgram,
      "aColor"
    );
  }

  drawDarkenCenter(mdVSFrame) {
    if (mdVSFrame.darken_center !== 0) {
      this.gl.useProgram(this.shaderProgram);

      this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.positionVertexBuf);
      this.gl.bufferData(
        this.gl.ARRAY_BUFFER,
        this.positions,
        this.gl.STATIC_DRAW
      );

      this.gl.vertexAttribPointer(
        this.aPosLocation,
        3,
        this.gl.FLOAT,
        false,
        0,
        0
      );
      this.gl.enableVertexAttribArray(this.aPosLocation);

      this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.colorVertexBuf);
      this.gl.bufferData(
        this.gl.ARRAY_BUFFER,
        this.colors,
        this.gl.STATIC_DRAW
      );

      this.gl.vertexAttribPointer(
        this.aColorLocation,
        4,
        this.gl.FLOAT,
        false,
        0,
        0
      );
      this.gl.enableVertexAttribArray(this.aColorLocation);

      this.gl.blendFunc(this.gl.SRC_ALPHA, this.gl.ONE_MINUS_SRC_ALPHA);

      this.gl.drawArrays(this.gl.TRIANGLE_FAN, 0, this.positions.length / 3);
    }
  }
}
Lyzon0 commented 10 months ago

Fft


export default class FFT {
  constructor(samplesIn, samplesOut, equalize = false) {
    this.samplesIn = samplesIn;
    this.samplesOut = samplesOut;
    this.equalize = equalize;
    this.NFREQ = samplesOut * 2;

    if (this.equalize) {
      this.initEqualizeTable();
    }
    this.initBitRevTable();
    this.initCosSinTable();
  }

  initEqualizeTable() {
    this.equalizeArr = new Float32Array(this.samplesOut);
    const invHalfNFREQ = 1.0 / this.samplesOut;
    for (let i = 0; i < this.samplesOut; i++) {
      this.equalizeArr[i] =
        -0.02 * Math.log((this.samplesOut - i) * invHalfNFREQ);
    }
  }

  /* eslint-disable no-bitwise */
  initBitRevTable() {
    this.bitrevtable = new Uint16Array(this.NFREQ);

    for (let i = 0; i < this.NFREQ; i++) {
      this.bitrevtable[i] = i;
    }

    let j = 0;
    for (let i = 0; i < this.NFREQ; i++) {
      if (j > i) {
        const temp = this.bitrevtable[i];
        this.bitrevtable[i] = this.bitrevtable[j];
        this.bitrevtable[j] = temp;
      }

      let m = this.NFREQ >> 1;

      while (m >= 1 && j >= m) {
        j -= m;
        m >>= 1;
      }

      j += m;
    }
  }

  initCosSinTable() {
    let dftsize = 2;
    let tabsize = 0;
    while (dftsize <= this.NFREQ) {
      tabsize += 1;
      dftsize <<= 1;
    }

    this.cossintable = [new Float32Array(tabsize), new Float32Array(tabsize)];

    dftsize = 2;
    let i = 0;
    while (dftsize <= this.NFREQ) {
      const theta = (-2.0 * Math.PI) / dftsize;
      this.cossintable[0][i] = Math.cos(theta);
      this.cossintable[1][i] = Math.sin(theta);
      i += 1;
      dftsize <<= 1;
    }
  }

  timeToFrequencyDomain(waveDataIn) {
    const real = new Float32Array(this.NFREQ);
    const imag = new Float32Array(this.NFREQ);

    for (let i = 0; i < this.NFREQ; i++) {
      const idx = this.bitrevtable[i];
      if (idx < this.samplesIn) {
        real[i] = waveDataIn[idx];
      } else {
        real[i] = 0;
      }
      imag[i] = 0;
    }

    let dftsize = 2;
    let t = 0;
    while (dftsize <= this.NFREQ) {
      const wpr = this.cossintable[0][t];
      const wpi = this.cossintable[1][t];
      let wr = 1.0;
      let wi = 0.0;
      const hdftsize = dftsize >> 1;

      for (let m = 0; m < hdftsize; m++) {
        for (let i = m; i < this.NFREQ; i += dftsize) {
          const j = i + hdftsize;
          const tempr = wr * real[j] - wi * imag[j];
          const tempi = wr * imag[j] + wi * real[j];
          real[j] = real[i] - tempr;
          imag[j] = imag[i] - tempi;
          real[i] += tempr;
          imag[i] += tempi;
        }

        const wtemp = wr;
        wr = wtemp * wpr - wi * wpi;
        wi = wi * wpr + wtemp * wpi;
      }

      dftsize <<= 1;
      t += 1;
    }

    const spectralDataOut = new Float32Array(this.samplesOut);
    if (this.equalize) {
      for (let i = 0; i < this.samplesOut; i++) {
        spectralDataOut[i] =
          this.equalizeArr[i] *
          Math.sqrt(real[i] * real[i] + imag[i] * imag[i]);
      }
    } else {
      for (let i = 0; i < this.samplesOut; i++) {
        spectralDataOut[i] = Math.sqrt(real[i] * real[i] + imag[i] * imag[i]);
      }
    }

    return spectralDataOut;
  }
  /* eslint-enable no-bitwise */
}
Lyzon0 commented 10 months ago

Image textures


export default class ImageTextures {
  constructor(gl) {
    this.gl = gl;

    this.anisoExt =
      this.gl.getExtension("EXT_texture_filter_anisotropic") ||
      this.gl.getExtension("MOZ_EXT_texture_filter_anisotropic") ||
      this.gl.getExtension("WEBKIT_EXT_texture_filter_anisotropic");

    this.samplers = {};

    /* eslint-disable max-len */
    this.clouds2Image = new Image();
    this.clouds2Image.onload = () => {
      this.samplers.clouds2 = this.gl.createTexture();
      this.bindTexture(this.samplers.clouds2, this.clouds2Image, 128, 128);
    };
    this.clouds2Image.src =
      "data:image/jpeg;base64,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";

    this.emptyImage = new Image();
    this.emptyImage.onload = () => {
      this.samplers.empty = this.gl.createTexture();
      this.bindTexture(this.samplers.empty, this.emptyImage, 1, 1);
    };
    this.emptyImage.src =
      "data:image/gif;base64,R0lGODlhAQABAIAAAAUEBAAAACwAAAAAAQABAAACAkQBADs=";
    /* eslint-enable max-len */
  }

  bindTexture(texture, data, width, height) {
    this.gl.bindTexture(this.gl.TEXTURE_2D, texture);

    this.gl.pixelStorei(this.gl.UNPACK_ALIGNMENT, 1);
    this.gl.texImage2D(
      this.gl.TEXTURE_2D,
      0,
      this.gl.RGBA,
      width,
      height,
      0,
      this.gl.RGBA,
      this.gl.UNSIGNED_BYTE,
      data
    );

    this.gl.generateMipmap(this.gl.TEXTURE_2D);

    this.gl.texParameteri(
      this.gl.TEXTURE_2D,
      this.gl.TEXTURE_WRAP_S,
      this.gl.REPEAT
    );
    this.gl.texParameteri(
      this.gl.TEXTURE_2D,
      this.gl.TEXTURE_WRAP_T,
      this.gl.REPEAT
    );
    this.gl.texParameteri(
      this.gl.TEXTURE_2D,
      this.gl.TEXTURE_MIN_FILTER,
      this.gl.LINEAR_MIPMAP_LINEAR
    );
    this.gl.texParameteri(
      this.gl.TEXTURE_2D,
      this.gl.TEXTURE_MAG_FILTER,
      this.gl.LINEAR
    );
    if (this.anisoExt) {
      const max = this.gl.getParameter(
        this.anisoExt.MAX_TEXTURE_MAX_ANISOTROPY_EXT
      );
      this.gl.texParameterf(
        this.gl.TEXTURE_2D,
        this.anisoExt.TEXTURE_MAX_ANISOTROPY_EXT,
        max
      );
    }
  }

  loadExtraImages(imageData) {
    Object.keys(imageData).forEach((imageName) => {
      const { data, width, height } = imageData[imageName];
      if (!this.samplers[imageName]) {
        const image = new Image();
        image.onload = () => {
          this.samplers[imageName] = this.gl.createTexture();
          this.bindTexture(this.samplers[imageName], image, width, height);
        };
        image.src = data;
      }
    });
  }

  getTexture(sampler) {
    const tex = this.samplers[sampler];
    if (tex) {
      return tex;
    }

    return this.samplers.clouds2;
  }
}
Lyzon0 commented 10 months ago

Index


import "ecma-proposal-math-extensions";
import "./presetBase";
import Visualizer from "./visualizer";

export default class Butterchurn {
  static createVisualizer(context, canvas, opts) {
    return new Visualizer(context, canvas, opts);
  }
}
Lyzon0 commented 10 months ago

Is that good

Marak commented 10 months ago

No, this is not good. Why did you copy and paste all this code here for no reason? Don't do that.