Fluid Cursor Effect
An interactive React component that creates stunning fluid dynamics with WebGL shaders and smooth cursor interactions.
An interactive React component that creates stunning fluid dynamics with WebGL shaders and smooth cursor interactions.
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 pointer-events-none'>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;