Fluid Cursor Effect
An interactive React component that adds a dynamic bubble effect, visually tracking cursor movement in real time.
An interactive React component that adds a dynamic bubble effect, visually tracking cursor movement in real time.
1'use client';2import { useEffect } from 'react';34import fluidCursor from '@/hooks/use-FluidCursor';56const FluidCursor = () => {7useEffect(() => {8fluidCursor();9}, []);1011return (12<div className='fixed top-0 left-0 z-2'>13<canvas id='fluid' className='w-screen h-screen' />14</div>15);16};17export default FluidCursor;18
1// @ts-nocheck2const useFluidCursor = () => {3const canvas = document.getElementById('fluid');4resizeCanvas();56//try to adjust settings78let config = {9SIM_RESOLUTION: 128,10DYE_RESOLUTION: 1440,11CAPTURE_RESOLUTION: 512,12DENSITY_DISSIPATION: 3.5,13VELOCITY_DISSIPATION: 2,14PRESSURE: 0.1,15PRESSURE_ITERATIONS: 20,16CURL: 3,17SPLAT_RADIUS: 0.2,18SPLAT_FORCE: 6000,19SHADING: true,20COLOR_UPDATE_SPEED: 10,21PAUSED: false,22BACK_COLOR: { r: 0.5, g: 0, b: 0 },23TRANSPARENT: true,24};2526function pointerPrototype() {27this.id = -1;28this.texcoordX = 0;29this.texcoordY = 0;30this.prevTexcoordX = 0;31this.prevTexcoordY = 0;32this.deltaX = 0;33this.deltaY = 0;34this.down = false;35this.moved = false;36this.color = [0, 0, 0];37}3839const pointers = [];40pointers.push(new pointerPrototype());4142const { gl, ext } = getWebGLContext(canvas);4344if (!ext.supportLinearFiltering) {45config.DYE_RESOLUTION = 256;46config.SHADING = false;47}4849function getWebGLContext(canvas) {50const params = {51alpha: true,52depth: false,53stencil: false,54antialias: false,55preserveDrawingBuffer: false,56};5758let gl = canvas.getContext('webgl2', params);59const isWebGL2 = !!gl;60if (!isWebGL2)61gl =62canvas.getContext('webgl', params) ||63canvas.getContext('experimental-webgl', params);6465let halfFloat;66let supportLinearFiltering;67if (isWebGL2) {68gl.getExtension('EXT_color_buffer_float');69supportLinearFiltering = gl.getExtension('OES_texture_float_linear');70} else {71halfFloat = gl.getExtension('OES_texture_half_float');72supportLinearFiltering = gl.getExtension('OES_texture_half_float_linear');73}7475gl.clearColor(0.0, 0.0, 0.0, 1.0);7677const halfFloatTexType = isWebGL278? gl.HALF_FLOAT79: halfFloat.HALF_FLOAT_OES;80let formatRGBA;81let formatRG;82let formatR;8384if (isWebGL2) {85formatRGBA = getSupportedFormat(86gl,87gl.RGBA16F,88gl.RGBA,89halfFloatTexType90);91formatRG = getSupportedFormat(gl, gl.RG16F, gl.RG, halfFloatTexType);92formatR = getSupportedFormat(gl, gl.R16F, gl.RED, halfFloatTexType);93} else {94formatRGBA = getSupportedFormat(gl, gl.RGBA, gl.RGBA, halfFloatTexType);95formatRG = getSupportedFormat(gl, gl.RGBA, gl.RGBA, halfFloatTexType);96formatR = getSupportedFormat(gl, gl.RGBA, gl.RGBA, halfFloatTexType);97}9899return {100gl,101ext: {102formatRGBA,103formatRG,104formatR,105halfFloatTexType,106supportLinearFiltering,107},108};109}110111function getSupportedFormat(gl, internalFormat, format, type) {112if (!supportRenderTextureFormat(gl, internalFormat, format, type)) {113switch (internalFormat) {114case gl.R16F:115return getSupportedFormat(gl, gl.RG16F, gl.RG, type);116case gl.RG16F:117return getSupportedFormat(gl, gl.RGBA16F, gl.RGBA, type);118default:119return null;120}121}122123return {124internalFormat,125format,126};127}128129function supportRenderTextureFormat(gl, internalFormat, format, type) {130const texture = gl.createTexture();131gl.bindTexture(gl.TEXTURE_2D, texture);132gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);133gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);134gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);135gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);136gl.texImage2D(137gl.TEXTURE_2D,1380,139internalFormat,1404,1414,1420,143format,144type,145null146);147148const fbo = gl.createFramebuffer();149gl.bindFramebuffer(gl.FRAMEBUFFER, fbo);150gl.framebufferTexture2D(151gl.FRAMEBUFFER,152gl.COLOR_ATTACHMENT0,153gl.TEXTURE_2D,154texture,1550156);157158const status = gl.checkFramebufferStatus(gl.FRAMEBUFFER);159return status == gl.FRAMEBUFFER_COMPLETE;160}161162class Material {163constructor(vertexShader, fragmentShaderSource) {164this.vertexShader = vertexShader;165this.fragmentShaderSource = fragmentShaderSource;166this.programs = [];167this.activeProgram = null;168this.uniforms = [];169}170171setKeywords(keywords) {172let hash = 0;173for (let i = 0; i < keywords.length; i++) hash += hashCode(keywords[i]);174175let program = this.programs[hash];176if (program == null) {177let fragmentShader = compileShader(178gl.FRAGMENT_SHADER,179this.fragmentShaderSource,180keywords181);182program = createProgram(this.vertexShader, fragmentShader);183this.programs[hash] = program;184}185186if (program == this.activeProgram) return;187188this.uniforms = getUniforms(program);189this.activeProgram = program;190}191192bind() {193gl.useProgram(this.activeProgram);194}195}196197class Program {198constructor(vertexShader, fragmentShader) {199this.uniforms = {};200this.program = createProgram(vertexShader, fragmentShader);201this.uniforms = getUniforms(this.program);202}203204bind() {205gl.useProgram(this.program);206}207}208209function createProgram(vertexShader, fragmentShader) {210let program = gl.createProgram();211gl.attachShader(program, vertexShader);212gl.attachShader(program, fragmentShader);213gl.linkProgram(program);214215if (!gl.getProgramParameter(program, gl.LINK_STATUS))216console.trace(gl.getProgramInfoLog(program));217218return program;219}220221function getUniforms(program) {222let uniforms = [];223let uniformCount = gl.getProgramParameter(program, gl.ACTIVE_UNIFORMS);224for (let i = 0; i < uniformCount; i++) {225let uniformName = gl.getActiveUniform(program, i).name;226uniforms[uniformName] = gl.getUniformLocation(program, uniformName);227}228return uniforms;229}230231function compileShader(type, source, keywords) {232source = addKeywords(source, keywords);233234const shader = gl.createShader(type);235gl.shaderSource(shader, source);236gl.compileShader(shader);237238if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS))239console.trace(gl.getShaderInfoLog(shader));240241return shader;242}243244function addKeywords(source, keywords) {245if (keywords == null) return source;246let keywordsString = '';247keywords.forEach((keyword) => {248keywordsString += '#define ' + keyword + '\n';249});250251return keywordsString + source;252}253254const baseVertexShader = compileShader(255gl.VERTEX_SHADER,256`257precision highp float;258259attribute vec2 aPosition;260varying vec2 vUv;261varying vec2 vL;262varying vec2 vR;263varying vec2 vT;264varying vec2 vB;265uniform vec2 texelSize;266267void main () {268vUv = aPosition * 0.5 + 0.5;269vL = vUv - vec2(texelSize.x, 0.0);270vR = vUv + vec2(texelSize.x, 0.0);271vT = vUv + vec2(0.0, texelSize.y);272vB = vUv - vec2(0.0, texelSize.y);273gl_Position = vec4(aPosition, 0.0, 1.0);274}275`276);277278const blurVertexShader = compileShader(279gl.VERTEX_SHADER,280`281precision highp float;282283attribute vec2 aPosition;284varying vec2 vUv;285varying vec2 vL;286varying vec2 vR;287uniform vec2 texelSize;288289void main () {290vUv = aPosition * 0.5 + 0.5;291float offset = 1.33333333;292vL = vUv - texelSize * offset;293vR = vUv + texelSize * offset;294gl_Position = vec4(aPosition, 0.0, 1.0);295}296`297);298299const blurShader = compileShader(300gl.FRAGMENT_SHADER,301`302precision mediump float;303precision mediump sampler2D;304305varying vec2 vUv;306varying vec2 vL;307varying vec2 vR;308uniform sampler2D uTexture;309310void main () {311vec4 sum = texture2D(uTexture, vUv) * 0.29411764;312sum += texture2D(uTexture, vL) * 0.35294117;313sum += texture2D(uTexture, vR) * 0.35294117;314gl_FragColor = sum;315}316`317);318319const copyShader = compileShader(320gl.FRAGMENT_SHADER,321`322precision mediump float;323precision mediump sampler2D;324325varying highp vec2 vUv;326uniform sampler2D uTexture;327328void main () {329gl_FragColor = texture2D(uTexture, vUv);330}331`332);333334const clearShader = compileShader(335gl.FRAGMENT_SHADER,336`337precision mediump float;338precision mediump sampler2D;339340varying highp vec2 vUv;341uniform sampler2D uTexture;342uniform float value;343344void main () {345gl_FragColor = value * texture2D(uTexture, vUv);346}347`348);349350const colorShader = compileShader(351gl.FRAGMENT_SHADER,352`353precision mediump float;354355uniform vec4 color;356357void main () {358gl_FragColor = color;359}360`361);362363const displayShaderSource = `364precision highp float;365precision highp sampler2D;366367varying vec2 vUv;368varying vec2 vL;369varying vec2 vR;370varying vec2 vT;371varying vec2 vB;372uniform sampler2D uTexture;373uniform sampler2D uDithering;374uniform vec2 ditherScale;375uniform vec2 texelSize;376377vec3 linearToGamma (vec3 color) {378color = max(color, vec3(0));379return max(1.055 * pow(color, vec3(0.416666667)) - 0.055, vec3(0));380}381382void main () {383vec3 c = texture2D(uTexture, vUv).rgb;384385#ifdef SHADING386vec3 lc = texture2D(uTexture, vL).rgb;387vec3 rc = texture2D(uTexture, vR).rgb;388vec3 tc = texture2D(uTexture, vT).rgb;389vec3 bc = texture2D(uTexture, vB).rgb;390391float dx = length(rc) - length(lc);392float dy = length(tc) - length(bc);393394vec3 n = normalize(vec3(dx, dy, length(texelSize)));395vec3 l = vec3(0.0, 0.0, 1.0);396397float diffuse = clamp(dot(n, l) + 0.7, 0.7, 1.0);398c *= diffuse;399#endif400401float a = max(c.r, max(c.g, c.b));402gl_FragColor = vec4(c, a);403}404`;405406const splatShader = compileShader(407gl.FRAGMENT_SHADER,408`409precision highp float;410precision highp sampler2D;411412varying vec2 vUv;413uniform sampler2D uTarget;414uniform float aspectRatio;415uniform vec3 color;416uniform vec2 point;417uniform float radius;418419void main () {420vec2 p = vUv - point.xy;421p.x *= aspectRatio;422vec3 splat = exp(-dot(p, p) / radius) * color;423vec3 base = texture2D(uTarget, vUv).xyz;424gl_FragColor = vec4(base + splat, 1.0);425}426`427);428429const advectionShader = compileShader(430gl.FRAGMENT_SHADER,431`432precision highp float;433precision highp sampler2D;434435varying vec2 vUv;436uniform sampler2D uVelocity;437uniform sampler2D uSource;438uniform vec2 texelSize;439uniform vec2 dyeTexelSize;440uniform float dt;441uniform float dissipation;442443vec4 bilerp (sampler2D sam, vec2 uv, vec2 tsize) {444vec2 st = uv / tsize - 0.5;445446vec2 iuv = floor(st);447vec2 fuv = fract(st);448449vec4 a = texture2D(sam, (iuv + vec2(0.5, 0.5)) * tsize);450vec4 b = texture2D(sam, (iuv + vec2(1.5, 0.5)) * tsize);451vec4 c = texture2D(sam, (iuv + vec2(0.5, 1.5)) * tsize);452vec4 d = texture2D(sam, (iuv + vec2(1.5, 1.5)) * tsize);453454return mix(mix(a, b, fuv.x), mix(c, d, fuv.x), fuv.y);455}456457void main () {458#ifdef MANUAL_FILTERING459vec2 coord = vUv - dt * bilerp(uVelocity, vUv, texelSize).xy * texelSize;460vec4 result = bilerp(uSource, coord, dyeTexelSize);461#else462vec2 coord = vUv - dt * texture2D(uVelocity, vUv).xy * texelSize;463vec4 result = texture2D(uSource, coord);464#endif465float decay = 1.0 + dissipation * dt;466gl_FragColor = result / decay;467}`,468ext.supportLinearFiltering ? null : ['MANUAL_FILTERING']469);470471const divergenceShader = compileShader(472gl.FRAGMENT_SHADER,473`474precision mediump float;475precision mediump sampler2D;476477varying highp vec2 vUv;478varying highp vec2 vL;479varying highp vec2 vR;480varying highp vec2 vT;481varying highp vec2 vB;482uniform sampler2D uVelocity;483484void main () {485float L = texture2D(uVelocity, vL).x;486float R = texture2D(uVelocity, vR).x;487float T = texture2D(uVelocity, vT).y;488float B = texture2D(uVelocity, vB).y;489490vec2 C = texture2D(uVelocity, vUv).xy;491if (vL.x < 0.0) { L = -C.x; }492if (vR.x > 1.0) { R = -C.x; }493if (vT.y > 1.0) { T = -C.y; }494if (vB.y < 0.0) { B = -C.y; }495496float div = 0.5 * (R - L + T - B);497gl_FragColor = vec4(div, 0.0, 0.0, 1.0);498}499`500);501502const curlShader = compileShader(503gl.FRAGMENT_SHADER,504`505precision mediump float;506precision mediump sampler2D;507508varying highp vec2 vUv;509varying highp vec2 vL;510varying highp vec2 vR;511varying highp vec2 vT;512varying highp vec2 vB;513uniform sampler2D uVelocity;514515void main () {516float L = texture2D(uVelocity, vL).y;517float R = texture2D(uVelocity, vR).y;518float T = texture2D(uVelocity, vT).x;519float B = texture2D(uVelocity, vB).x;520float vorticity = R - L - T + B;521gl_FragColor = vec4(0.5 * vorticity, 0.0, 0.0, 1.0);522}523`524);525526const vorticityShader = compileShader(527gl.FRAGMENT_SHADER,528`529precision highp float;530precision highp sampler2D;531532varying vec2 vUv;533varying vec2 vL;534varying vec2 vR;535varying vec2 vT;536varying vec2 vB;537uniform sampler2D uVelocity;538uniform sampler2D uCurl;539uniform float curl;540uniform float dt;541542void main () {543float L = texture2D(uCurl, vL).x;544float R = texture2D(uCurl, vR).x;545float T = texture2D(uCurl, vT).x;546float B = texture2D(uCurl, vB).x;547float C = texture2D(uCurl, vUv).x;548549vec2 force = 0.5 * vec2(abs(T) - abs(B), abs(R) - abs(L));550force /= length(force) + 0.0001;551force *= curl * C;552force.y *= -1.0;553554vec2 velocity = texture2D(uVelocity, vUv).xy;555velocity += force * dt;556velocity = min(max(velocity, -1000.0), 1000.0);557gl_FragColor = vec4(velocity, 0.0, 1.0);558}559`560);561562const pressureShader = compileShader(563gl.FRAGMENT_SHADER,564`565precision mediump float;566precision mediump sampler2D;567568varying highp vec2 vUv;569varying highp vec2 vL;570varying highp vec2 vR;571varying highp vec2 vT;572varying highp vec2 vB;573uniform sampler2D uPressure;574uniform sampler2D uDivergence;575576void main () {577float L = texture2D(uPressure, vL).x;578float R = texture2D(uPressure, vR).x;579float T = texture2D(uPressure, vT).x;580float B = texture2D(uPressure, vB).x;581float C = texture2D(uPressure, vUv).x;582float divergence = texture2D(uDivergence, vUv).x;583float pressure = (L + R + B + T - divergence) * 0.25;584gl_FragColor = vec4(pressure, 0.0, 0.0, 1.0);585}586`587);588589const gradientSubtractShader = compileShader(590gl.FRAGMENT_SHADER,591`592precision mediump float;593precision mediump sampler2D;594595varying highp vec2 vUv;596varying highp vec2 vL;597varying highp vec2 vR;598varying highp vec2 vT;599varying highp vec2 vB;600uniform sampler2D uPressure;601uniform sampler2D uVelocity;602603void main () {604float L = texture2D(uPressure, vL).x;605float R = texture2D(uPressure, vR).x;606float T = texture2D(uPressure, vT).x;607float B = texture2D(uPressure, vB).x;608vec2 velocity = texture2D(uVelocity, vUv).xy;609velocity.xy -= vec2(R - L, T - B);610gl_FragColor = vec4(velocity, 0.0, 1.0);611}612`613);614615const blit = (() => {616gl.bindBuffer(gl.ARRAY_BUFFER, gl.createBuffer());617gl.bufferData(618gl.ARRAY_BUFFER,619new Float32Array([-1, -1, -1, 1, 1, 1, 1, -1]),620gl.STATIC_DRAW621);622gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, gl.createBuffer());623gl.bufferData(624gl.ELEMENT_ARRAY_BUFFER,625new Uint16Array([0, 1, 2, 0, 2, 3]),626gl.STATIC_DRAW627);628gl.vertexAttribPointer(0, 2, gl.FLOAT, false, 0, 0);629gl.enableVertexAttribArray(0);630631return (target, clear = false) => {632if (target == null) {633gl.viewport(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight);634gl.bindFramebuffer(gl.FRAMEBUFFER, null);635} else {636gl.viewport(0, 0, target.width, target.height);637gl.bindFramebuffer(gl.FRAMEBUFFER, target.fbo);638}639if (clear) {640gl.clearColor(0.0, 0.0, 0.0, 1.0);641gl.clear(gl.COLOR_BUFFER_BIT);642}643gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0);644};645})();646647let dye;648let velocity;649let divergence;650let curl;651let pressure;652653const copyProgram = new Program(baseVertexShader, copyShader);654const clearProgram = new Program(baseVertexShader, clearShader);655const splatProgram = new Program(baseVertexShader, splatShader);656const advectionProgram = new Program(baseVertexShader, advectionShader);657const divergenceProgram = new Program(baseVertexShader, divergenceShader);658const curlProgram = new Program(baseVertexShader, curlShader);659const vorticityProgram = new Program(baseVertexShader, vorticityShader);660const pressureProgram = new Program(baseVertexShader, pressureShader);661const gradienSubtractProgram = new Program(662baseVertexShader,663gradientSubtractShader664);665666const displayMaterial = new Material(baseVertexShader, displayShaderSource);667668function initFramebuffers() {669let simRes = getResolution(config.SIM_RESOLUTION);670let dyeRes = getResolution(config.DYE_RESOLUTION);671672const texType = ext.halfFloatTexType;673const rgba = ext.formatRGBA;674const rg = ext.formatRG;675const r = ext.formatR;676const filtering = ext.supportLinearFiltering ? gl.LINEAR : gl.NEAREST;677678gl.disable(gl.BLEND);679680if (dye == null)681dye = createDoubleFBO(682dyeRes.width,683dyeRes.height,684rgba.internalFormat,685rgba.format,686texType,687filtering688);689else690dye = resizeDoubleFBO(691dye,692dyeRes.width,693dyeRes.height,694rgba.internalFormat,695rgba.format,696texType,697filtering698);699700if (velocity == null)701velocity = createDoubleFBO(702simRes.width,703simRes.height,704rg.internalFormat,705rg.format,706texType,707filtering708);709else710velocity = resizeDoubleFBO(711velocity,712simRes.width,713simRes.height,714rg.internalFormat,715rg.format,716texType,717filtering718);719720divergence = createFBO(721simRes.width,722simRes.height,723r.internalFormat,724r.format,725texType,726gl.NEAREST727);728curl = createFBO(729simRes.width,730simRes.height,731r.internalFormat,732r.format,733texType,734gl.NEAREST735);736pressure = createDoubleFBO(737simRes.width,738simRes.height,739r.internalFormat,740r.format,741texType,742gl.NEAREST743);744}745746function createFBO(w, h, internalFormat, format, type, param) {747gl.activeTexture(gl.TEXTURE0);748let texture = gl.createTexture();749gl.bindTexture(gl.TEXTURE_2D, texture);750gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, param);751gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, param);752gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);753gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);754gl.texImage2D(755gl.TEXTURE_2D,7560,757internalFormat,758w,759h,7600,761format,762type,763null764);765766let fbo = gl.createFramebuffer();767gl.bindFramebuffer(gl.FRAMEBUFFER, fbo);768gl.framebufferTexture2D(769gl.FRAMEBUFFER,770gl.COLOR_ATTACHMENT0,771gl.TEXTURE_2D,772texture,7730774);775gl.viewport(0, 0, w, h);776gl.clear(gl.COLOR_BUFFER_BIT);777778let texelSizeX = 1.0 / w;779let texelSizeY = 1.0 / h;780781return {782texture,783fbo,784width: w,785height: h,786texelSizeX,787texelSizeY,788attach(id) {789gl.activeTexture(gl.TEXTURE0 + id);790gl.bindTexture(gl.TEXTURE_2D, texture);791return id;792},793};794}795796function createDoubleFBO(w, h, internalFormat, format, type, param) {797let fbo1 = createFBO(w, h, internalFormat, format, type, param);798let fbo2 = createFBO(w, h, internalFormat, format, type, param);799800return {801width: w,802height: h,803texelSizeX: fbo1.texelSizeX,804texelSizeY: fbo1.texelSizeY,805get read() {806return fbo1;807},808set read(value) {809fbo1 = value;810},811get write() {812return fbo2;813},814set write(value) {815fbo2 = value;816},817swap() {818let temp = fbo1;819fbo1 = fbo2;820fbo2 = temp;821},822};823}824825function resizeFBO(target, w, h, internalFormat, format, type, param) {826let newFBO = createFBO(w, h, internalFormat, format, type, param);827copyProgram.bind();828gl.uniform1i(copyProgram.uniforms.uTexture, target.attach(0));829blit(newFBO);830return newFBO;831}832833function resizeDoubleFBO(target, w, h, internalFormat, format, type, param) {834if (target.width == w && target.height == h) return target;835target.read = resizeFBO(836target.read,837w,838h,839internalFormat,840format,841type,842param843);844target.write = createFBO(w, h, internalFormat, format, type, param);845target.width = w;846target.height = h;847target.texelSizeX = 1.0 / w;848target.texelSizeY = 1.0 / h;849return target;850}851852function createTextureAsync(url) {853let texture = gl.createTexture();854gl.bindTexture(gl.TEXTURE_2D, texture);855gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);856gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);857gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT);858gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT);859gl.texImage2D(860gl.TEXTURE_2D,8610,862gl.RGB,8631,8641,8650,866gl.RGB,867gl.UNSIGNED_BYTE,868new Uint8Array([255, 255, 255])869);870871let obj = {872texture,873width: 1,874height: 1,875attach(id) {876gl.activeTexture(gl.TEXTURE0 + id);877gl.bindTexture(gl.TEXTURE_2D, texture);878return id;879},880};881882let image = new Image();883image.onload = () => {884obj.width = image.width;885obj.height = image.height;886gl.bindTexture(gl.TEXTURE_2D, texture);887gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB, gl.RGB, gl.UNSIGNED_BYTE, image);888};889image.src = url;890891return obj;892}893894function updateKeywords() {895let displayKeywords = [];896if (config.SHADING) displayKeywords.push('SHADING');897displayMaterial.setKeywords(displayKeywords);898}899900updateKeywords();901initFramebuffers();902903let lastUpdateTime = Date.now();904let colorUpdateTimer = 0.0;905906function update() {907const dt = calcDeltaTime();908// console.log(dt)909if (resizeCanvas()) initFramebuffers();910updateColors(dt);911applyInputs();912step(dt);913render(null);914requestAnimationFrame(update);915}916917function calcDeltaTime() {918let now = Date.now();919let dt = (now - lastUpdateTime) / 1000;920dt = Math.min(dt, 0.016666);921lastUpdateTime = now;922return dt;923}924925function resizeCanvas() {926let width = scaleByPixelRatio(canvas.clientWidth);927let height = scaleByPixelRatio(canvas.clientHeight);928if (canvas.width != width || canvas.height != height) {929canvas.width = width;930canvas.height = height;931return true;932}933return false;934}935936function updateColors(dt) {937colorUpdateTimer += dt * config.COLOR_UPDATE_SPEED;938if (colorUpdateTimer >= 1) {939colorUpdateTimer = wrap(colorUpdateTimer, 0, 1);940pointers.forEach((p) => {941p.color = generateColor();942});943}944}945946function applyInputs() {947pointers.forEach((p) => {948if (p.moved) {949p.moved = false;950splatPointer(p);951}952});953}954955function step(dt) {956gl.disable(gl.BLEND);957958curlProgram.bind();959gl.uniform2f(960curlProgram.uniforms.texelSize,961velocity.texelSizeX,962velocity.texelSizeY963);964gl.uniform1i(curlProgram.uniforms.uVelocity, velocity.read.attach(0));965blit(curl);966967vorticityProgram.bind();968gl.uniform2f(969vorticityProgram.uniforms.texelSize,970velocity.texelSizeX,971velocity.texelSizeY972);973gl.uniform1i(vorticityProgram.uniforms.uVelocity, velocity.read.attach(0));974gl.uniform1i(vorticityProgram.uniforms.uCurl, curl.attach(1));975gl.uniform1f(vorticityProgram.uniforms.curl, config.CURL);976gl.uniform1f(vorticityProgram.uniforms.dt, dt);977blit(velocity.write);978velocity.swap();979980divergenceProgram.bind();981gl.uniform2f(982divergenceProgram.uniforms.texelSize,983velocity.texelSizeX,984velocity.texelSizeY985);986gl.uniform1i(divergenceProgram.uniforms.uVelocity, velocity.read.attach(0));987blit(divergence);988989clearProgram.bind();990gl.uniform1i(clearProgram.uniforms.uTexture, pressure.read.attach(0));991gl.uniform1f(clearProgram.uniforms.value, config.PRESSURE);992blit(pressure.write);993pressure.swap();994995pressureProgram.bind();996gl.uniform2f(997pressureProgram.uniforms.texelSize,998velocity.texelSizeX,999velocity.texelSizeY1000);1001gl.uniform1i(pressureProgram.uniforms.uDivergence, divergence.attach(0));1002for (let i = 0; i < config.PRESSURE_ITERATIONS; i++) {1003gl.uniform1i(pressureProgram.uniforms.uPressure, pressure.read.attach(1));1004blit(pressure.write);1005pressure.swap();1006}10071008gradienSubtractProgram.bind();1009gl.uniform2f(1010gradienSubtractProgram.uniforms.texelSize,1011velocity.texelSizeX,1012velocity.texelSizeY1013);1014gl.uniform1i(1015gradienSubtractProgram.uniforms.uPressure,1016pressure.read.attach(0)1017);1018gl.uniform1i(1019gradienSubtractProgram.uniforms.uVelocity,1020velocity.read.attach(1)1021);1022blit(velocity.write);1023velocity.swap();10241025advectionProgram.bind();1026gl.uniform2f(1027advectionProgram.uniforms.texelSize,1028velocity.texelSizeX,1029velocity.texelSizeY1030);1031if (!ext.supportLinearFiltering)1032gl.uniform2f(1033advectionProgram.uniforms.dyeTexelSize,1034velocity.texelSizeX,1035velocity.texelSizeY1036);1037let velocityId = velocity.read.attach(0);1038gl.uniform1i(advectionProgram.uniforms.uVelocity, velocityId);1039gl.uniform1i(advectionProgram.uniforms.uSource, velocityId);1040gl.uniform1f(advectionProgram.uniforms.dt, dt);1041gl.uniform1f(1042advectionProgram.uniforms.dissipation,1043config.VELOCITY_DISSIPATION1044);1045blit(velocity.write);1046velocity.swap();10471048if (!ext.supportLinearFiltering)1049gl.uniform2f(1050advectionProgram.uniforms.dyeTexelSize,1051dye.texelSizeX,1052dye.texelSizeY1053);1054gl.uniform1i(advectionProgram.uniforms.uVelocity, velocity.read.attach(0));1055gl.uniform1i(advectionProgram.uniforms.uSource, dye.read.attach(1));1056gl.uniform1f(1057advectionProgram.uniforms.dissipation,1058config.DENSITY_DISSIPATION1059);1060blit(dye.write);1061dye.swap();1062}10631064function render(target) {1065gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA);1066gl.enable(gl.BLEND);1067drawDisplay(target);1068}10691070function drawDisplay(target) {1071let width = target == null ? gl.drawingBufferWidth : target.width;1072let height = target == null ? gl.drawingBufferHeight : target.height;10731074displayMaterial.bind();1075if (config.SHADING)1076gl.uniform2f(1077displayMaterial.uniforms.texelSize,10781.0 / width,10791.0 / height1080);1081gl.uniform1i(displayMaterial.uniforms.uTexture, dye.read.attach(0));1082blit(target);1083}10841085function splatPointer(pointer) {1086let dx = pointer.deltaX * config.SPLAT_FORCE;1087let dy = pointer.deltaY * config.SPLAT_FORCE;1088splat(pointer.texcoordX, pointer.texcoordY, dx, dy, pointer.color);1089}10901091function clickSplat(pointer) {1092const color = generateColor();1093color.r *= 10.0;1094color.g *= 10.0;1095color.b *= 10.0;1096let dx = 10 * (Math.random() - 0.5);1097let dy = 30 * (Math.random() - 0.5);1098splat(pointer.texcoordX, pointer.texcoordY, dx, dy, color);1099}11001101function splat(x, y, dx, dy, color) {1102splatProgram.bind();1103gl.uniform1i(splatProgram.uniforms.uTarget, velocity.read.attach(0));1104gl.uniform1f(1105splatProgram.uniforms.aspectRatio,1106canvas.width / canvas.height1107);1108gl.uniform2f(splatProgram.uniforms.point, x, y);1109gl.uniform3f(splatProgram.uniforms.color, dx, dy, 0.0);1110gl.uniform1f(1111splatProgram.uniforms.radius,1112correctRadius(config.SPLAT_RADIUS / 100.0)1113);1114blit(velocity.write);1115velocity.swap();11161117gl.uniform1i(splatProgram.uniforms.uTarget, dye.read.attach(0));1118gl.uniform3f(splatProgram.uniforms.color, color.r, color.g, color.b);1119blit(dye.write);1120dye.swap();1121}11221123function correctRadius(radius) {1124let aspectRatio = canvas.width / canvas.height;1125if (aspectRatio > 1) radius *= aspectRatio;1126return radius;1127}11281129window.addEventListener('mousedown', (e) => {1130let pointer = pointers[0];1131let posX = scaleByPixelRatio(e.clientX);1132let posY = scaleByPixelRatio(e.clientY);1133updatePointerDownData(pointer, -1, posX, posY);1134clickSplat(pointer);1135});11361137document.body.addEventListener('mousemove', function handleFirstMouseMove(e) {1138let pointer = pointers[0];1139let posX = scaleByPixelRatio(e.clientX);1140let posY = scaleByPixelRatio(e.clientY);1141let color = generateColor();11421143update();1144updatePointerMoveData(pointer, posX, posY, color);11451146// Remove this event listener after the first mousemove event1147document.body.removeEventListener('mousemove', handleFirstMouseMove);1148});11491150window.addEventListener('mousemove', (e) => {1151let pointer = pointers[0];1152let posX = scaleByPixelRatio(e.clientX);1153let posY = scaleByPixelRatio(e.clientY);1154let color = pointer.color;11551156updatePointerMoveData(pointer, posX, posY, color);1157});11581159document.body.addEventListener(1160'touchstart',1161function handleFirstTouchStart(e) {1162const touches = e.targetTouches;1163let pointer = pointers[0];11641165for (let i = 0; i < touches.length; i++) {1166let posX = scaleByPixelRatio(touches[i].clientX);1167let posY = scaleByPixelRatio(touches[i].clientY);11681169update();1170updatePointerDownData(pointer, touches[i].identifier, posX, posY);1171}11721173// Remove this event listener after the first touchstart event1174document.body.removeEventListener('touchstart', handleFirstTouchStart);1175}1176);11771178window.addEventListener('touchstart', (e) => {1179const touches = e.targetTouches;1180let pointer = pointers[0];1181for (let i = 0; i < touches.length; i++) {1182let posX = scaleByPixelRatio(touches[i].clientX);1183let posY = scaleByPixelRatio(touches[i].clientY);1184updatePointerDownData(pointer, touches[i].identifier, posX, posY);1185}1186});11871188window.addEventListener(1189'touchmove',1190(e) => {1191const touches = e.targetTouches;1192let pointer = pointers[0];1193for (let i = 0; i < touches.length; i++) {1194let posX = scaleByPixelRatio(touches[i].clientX);1195let posY = scaleByPixelRatio(touches[i].clientY);1196updatePointerMoveData(pointer, posX, posY, pointer.color);1197}1198},1199false1200);12011202window.addEventListener('touchend', (e) => {1203const touches = e.changedTouches;1204let pointer = pointers[0];12051206for (let i = 0; i < touches.length; i++) {1207updatePointerUpData(pointer);1208}1209});12101211function updatePointerDownData(pointer, id, posX, posY) {1212pointer.id = id;1213pointer.down = true;1214pointer.moved = false;1215pointer.texcoordX = posX / canvas.width;1216pointer.texcoordY = 1.0 - posY / canvas.height;1217pointer.prevTexcoordX = pointer.texcoordX;1218pointer.prevTexcoordY = pointer.texcoordY;1219pointer.deltaX = 0;1220pointer.deltaY = 0;1221pointer.color = generateColor();1222}12231224function updatePointerMoveData(pointer, posX, posY, color) {1225// pointer.down = false;1226pointer.prevTexcoordX = pointer.texcoordX;1227pointer.prevTexcoordY = pointer.texcoordY;1228pointer.texcoordX = posX / canvas.width;1229pointer.texcoordY = 1.0 - posY / canvas.height;1230pointer.deltaX = correctDeltaX(pointer.texcoordX - pointer.prevTexcoordX);1231pointer.deltaY = correctDeltaY(pointer.texcoordY - pointer.prevTexcoordY);1232pointer.moved =1233Math.abs(pointer.deltaX) > 0 || Math.abs(pointer.deltaY) > 0;1234pointer.color = color;1235}12361237function updatePointerUpData(pointer) {1238pointer.down = false;1239}12401241function correctDeltaX(delta) {1242let aspectRatio = canvas.width / canvas.height;1243if (aspectRatio < 1) delta *= aspectRatio;1244return delta;1245}12461247function correctDeltaY(delta) {1248let aspectRatio = canvas.width / canvas.height;1249if (aspectRatio > 1) delta /= aspectRatio;1250return delta;1251}12521253function generateColor() {1254let c = HSVtoRGB(Math.random(), 1.0, 1.0);1255c.r *= 0.15;1256c.g *= 0.15;1257c.b *= 0.15;1258return c;1259}12601261function HSVtoRGB(h, s, v) {1262let r, g, b, i, f, p, q, t;1263i = Math.floor(h * 6);1264f = h * 6 - i;1265p = v * (1 - s);1266q = v * (1 - f * s);1267t = v * (1 - (1 - f) * s);12681269switch (i % 6) {1270case 0:1271(r = v), (g = t), (b = p);1272break;1273case 1:1274(r = q), (g = v), (b = p);1275break;1276case 2:1277(r = p), (g = v), (b = t);1278break;1279case 3:1280(r = p), (g = q), (b = v);1281break;1282case 4:1283(r = t), (g = p), (b = v);1284break;1285case 5:1286(r = v), (g = p), (b = q);1287break;1288}12891290return {1291r,1292g,1293b,1294};1295}12961297function wrap(value, min, max) {1298const range = max - min;1299if (range == 0) return min;1300return ((value - min) % range) + min;1301}13021303function getResolution(resolution) {1304let aspectRatio = gl.drawingBufferWidth / gl.drawingBufferHeight;1305if (aspectRatio < 1) aspectRatio = 1.0 / aspectRatio;13061307const min = Math.round(resolution);1308const max = Math.round(resolution * aspectRatio);13091310if (gl.drawingBufferWidth > gl.drawingBufferHeight)1311return { width: max, height: min };1312else return { width: min, height: max };1313}13141315function scaleByPixelRatio(input) {1316const pixelRatio = window.devicePixelRatio || 1;1317return Math.floor(input * pixelRatio);1318}13191320function hashCode(s) {1321if (s.length == 0) return 0;1322let hash = 0;1323for (let i = 0; i < s.length; i++) {1324hash = (hash << 5) - hash + s.charCodeAt(i);1325hash |= 0; // Convert to 32bit integer1326}1327return hash;1328}1329};13301331export default useFluidCursor;