commit 4ab6f77279a5b98eb5306b8e15c02ac6bac7247a
Author: WhyKorp <117651228+whykorp@users.noreply.github.com>
Date: Tue Oct 17 21:54:01 2023 +0200
Initial Commit
diff --git a/css/style.css b/css/style.css
new file mode 100644
index 0000000..9de2e5d
--- /dev/null
+++ b/css/style.css
@@ -0,0 +1,21 @@
+nav.main-nav ul li.btn-nav1{
+ list-style-type: none;
+ display: inline-block;
+}
+
+nav.main-nav ul li.btn-nav1 a{
+ color: white;
+ background-color: #4C33FF;
+ text-decoration: none;
+ padding: 10px;
+ font-size: 30px;
+}
+
+nav.main-nav ul li.btn-nav1:hover a{
+ background-color: #301D74;
+ transition: 0.5s all;
+}
+
+body{
+ background-color: #101010;
+}
\ No newline at end of file
diff --git a/img/favicon.png b/img/favicon.png
new file mode 100644
index 0000000..1778a9a
Binary files /dev/null and b/img/favicon.png differ
diff --git a/index.html b/index.html
new file mode 100644
index 0000000..416a808
--- /dev/null
+++ b/index.html
@@ -0,0 +1,43 @@
+
+
+
+
+
+
+
+ Whykioh
+
+
+
+
+
+
+
+ Bienvenue sur le site de Whykioh !
+
+
+
+
\ No newline at end of file
diff --git a/swordariastudios/index.html b/swordariastudios/index.html
new file mode 100644
index 0000000..d9a8e07
--- /dev/null
+++ b/swordariastudios/index.html
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+ Swordaria Studios
+
+
+
+
+
+ Swordaria Studios
+
+
+
+
\ No newline at end of file
diff --git a/tools/fluidsim/LDR_RGB1_0.png b/tools/fluidsim/LDR_RGB1_0.png
new file mode 100644
index 0000000..685f0e5
Binary files /dev/null and b/tools/fluidsim/LDR_RGB1_0.png differ
diff --git a/tools/fluidsim/LICENSE b/tools/fluidsim/LICENSE
new file mode 100644
index 0000000..9261032
--- /dev/null
+++ b/tools/fluidsim/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2017 Pavel Dobryakov
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/tools/fluidsim/README.md b/tools/fluidsim/README.md
new file mode 100644
index 0000000..304f4f6
--- /dev/null
+++ b/tools/fluidsim/README.md
@@ -0,0 +1,14 @@
+# Colorful Fluid Animation
+
+Modified version of [PavelDoGreat](https://github.com/PavelDoGreat)'s [WebGL-Fluid-Simulation](https://github.com/PavelDoGreat/WebGL-Fluid-Simulation) to work with Wallpaper Engine.
+
+Download/Subscribe here: https://steamcommunity.com/sharedfiles/filedetails/?id=1748506393
+
+# Screenshots
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/tools/fluidsim/img/back_arrow.png b/tools/fluidsim/img/back_arrow.png
new file mode 100644
index 0000000..a371da1
Binary files /dev/null and b/tools/fluidsim/img/back_arrow.png differ
diff --git a/tools/fluidsim/img/favicon.png b/tools/fluidsim/img/favicon.png
new file mode 100644
index 0000000..ac4139c
Binary files /dev/null and b/tools/fluidsim/img/favicon.png differ
diff --git a/tools/fluidsim/index.html b/tools/fluidsim/index.html
new file mode 100644
index 0000000..c418ea0
--- /dev/null
+++ b/tools/fluidsim/index.html
@@ -0,0 +1,67 @@
+
+
+
+
+
+
+ Fluid Simulation - WhykiohTools
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/tools/fluidsim/preview.gif b/tools/fluidsim/preview.gif
new file mode 100644
index 0000000..d8df969
Binary files /dev/null and b/tools/fluidsim/preview.gif differ
diff --git a/tools/fluidsim/preview.jpg b/tools/fluidsim/preview.jpg
new file mode 100644
index 0000000..2dc27ca
Binary files /dev/null and b/tools/fluidsim/preview.jpg differ
diff --git a/tools/fluidsim/project.json b/tools/fluidsim/project.json
new file mode 100644
index 0000000..dd6ca5b
--- /dev/null
+++ b/tools/fluidsim/project.json
@@ -0,0 +1,393 @@
+{
+ "approved" : true,
+ "contentrating" : "Everyone",
+ "description" : "Source Code: https://github.com/Delivator/WebGL-Fluid-Simulation\nCredit: https://github.com/PavelDoGreat/WebGL-Fluid-Simulation\n\n[h1] If the wallpaper is not working, try resetting the wallpaper engine system settings! [/h1]\nNo idea why but my wallpaper suddenly stopped working and resetting the Wallpaper Engine settings fixed it for me.",
+ "file" : "index.html",
+ "general" :
+ {
+ "properties" :
+ {
+ "audio_responsive" :
+ {
+ "order" : 117,
+ "text" : "Audio Responsive",
+ "type" : "bool",
+ "value" : true
+ },
+ "background_color" :
+ {
+ "order" : 106,
+ "text" : "Background Color",
+ "type" : "color",
+ "value" : "0 0 0"
+ },
+ "background_image" :
+ {
+ "condition" : "use_background_image.value",
+ "fileType" : "image",
+ "order" : 103,
+ "text" : "Background Image",
+ "type" : "file",
+ "value" : ""
+ },
+ "background_image_size" :
+ {
+ "condition" : "use_background_image.value",
+ "options" :
+ [
+ {
+ "label" : "Contain",
+ "value" : "contain"
+ },
+ {
+ "label" : "Cover",
+ "value" : "Cover"
+ }
+ ],
+ "order" : 105,
+ "text" : "Background Image Size",
+ "type" : "combo",
+ "value" : "contain"
+ },
+ "bloom_intensity" :
+ {
+ "fraction" : true,
+ "max" : 2,
+ "min" : 0.10000000000000001,
+ "order" : 131,
+ "precision" : 2,
+ "step" : 0.01,
+ "text" : "Bloom Intensity",
+ "type" : "slider",
+ "value" : 0.80000000000000004
+ },
+ "bloom_threshold" :
+ {
+ "fraction" : true,
+ "max" : 1,
+ "min" : 0,
+ "order" : 132,
+ "precision" : 2,
+ "step" : 0.01,
+ "text" : "Bloom Threshold",
+ "type" : "slider",
+ "value" : 0.59999999999999998
+ },
+ "colorful" :
+ {
+ "order" : 110,
+ "text" : "Random Color",
+ "type" : "bool",
+ "value" : true
+ },
+ "density_diffusion" :
+ {
+ "fraction" : true,
+ "max" : 1,
+ "min" : 0.90000000000000002,
+ "order" : 123,
+ "precision" : 3,
+ "step" : 0.001,
+ "text" : "Density Diffusion",
+ "type" : "slider",
+ "value" : 0.96999999999999997
+ },
+ "dye_resolution" :
+ {
+ "options" :
+ [
+ {
+ "label" : "32",
+ "value" : "32"
+ },
+ {
+ "label" : "64",
+ "value" : "64"
+ },
+ {
+ "label" : "128",
+ "value" : "128"
+ },
+ {
+ "label" : "256",
+ "value" : "256"
+ },
+ {
+ "label" : "512",
+ "value" : "512"
+ },
+ {
+ "label" : "1024",
+ "value" : "1024"
+ },
+ {
+ "label" : "2048",
+ "value" : "2048"
+ },
+ {
+ "label" : "4096",
+ "value" : "4096"
+ }
+ ],
+ "order" : 122,
+ "text" : "Dye Resolution",
+ "type" : "combo",
+ "value" : "512"
+ },
+ "enable_bloom" :
+ {
+ "order" : 130,
+ "text" : "Enable Bloom",
+ "type" : "bool",
+ "value" : true
+ },
+ "frequency_range" :
+ {
+ "condition" : "audio_responsive.value",
+ "fraction" : false,
+ "max" : 62,
+ "min" : 1,
+ "order" : 119,
+ "text" : "Frequency Range",
+ "type" : "slider",
+ "value" : 8
+ },
+ "frequency_range_start" :
+ {
+ "condition" : "audio_responsive.value",
+ "fraction" : false,
+ "max" : 62,
+ "min" : 0,
+ "order" : 120,
+ "text" : "Frequency Range Start",
+ "type" : "slider",
+ "value" : 0
+ },
+ "idle_random_splats" :
+ {
+ "order" : 107,
+ "text" : "Idle Random Splats",
+ "type" : "bool",
+ "value" : false
+ },
+ "more_colors" :
+ {
+ "condition" : "!colorful.value",
+ "order" : 112,
+ "text" : "More Colors",
+ "type" : "bool",
+ "value" : false
+ },
+ "paused" :
+ {
+ "order" : 129,
+ "text" : "Paused",
+ "type" : "bool",
+ "value" : false
+ },
+ "pressure_diffusion" :
+ {
+ "fraction" : true,
+ "max" : 1,
+ "min" : 0,
+ "order" : 125,
+ "precision" : 2,
+ "step" : 0.01,
+ "text" : "Pressure Diffusion",
+ "type" : "slider",
+ "value" : 0.80000000000000004
+ },
+ "random_splat_amount" :
+ {
+ "condition" : "idle_random_splats.value",
+ "fraction" : true,
+ "max" : 100,
+ "min" : 1,
+ "order" : 109,
+ "precision" : 2,
+ "step" : 1,
+ "text" : "Random Splat Amount",
+ "type" : "slider",
+ "value" : 10
+ },
+ "random_splat_interval" :
+ {
+ "condition" : "idle_random_splats.value",
+ "fraction" : true,
+ "max" : 5,
+ "min" : 0,
+ "order" : 108,
+ "precision" : 2,
+ "step" : 0.01,
+ "text" : "Random Splat Interval",
+ "type" : "slider",
+ "value" : 1
+ },
+ "repeat_background" :
+ {
+ "condition" : "use_background_image.value",
+ "order" : 104,
+ "text" : "Repeat Background",
+ "type" : "bool",
+ "value" : false
+ },
+ "schemecolor" :
+ {
+ "order" : 0,
+ "text" : "ui_browse_properties_scheme_color",
+ "type" : "color",
+ "value" : "0 0 0"
+ },
+ "shading" :
+ {
+ "order" : 128,
+ "text" : "Shading",
+ "type" : "bool",
+ "value" : true
+ },
+ "show_mouse_movement" :
+ {
+ "order" : 100,
+ "text" : "Show Mouse Movement",
+ "type" : "bool",
+ "value" : true
+ },
+ "simulation_resolution" :
+ {
+ "options" :
+ [
+ {
+ "label" : "32",
+ "value" : "32"
+ },
+ {
+ "label" : "64",
+ "value" : "64"
+ },
+ {
+ "label" : "128",
+ "value" : "128"
+ },
+ {
+ "label" : "256",
+ "value" : "256"
+ }
+ ],
+ "order" : 121,
+ "text" : "Simulation Resolution",
+ "type" : "combo",
+ "value" : "256"
+ },
+ "sound_sensitivity" :
+ {
+ "condition" : "audio_responsive.value",
+ "fraction" : true,
+ "max" : 10,
+ "min" : 0,
+ "order" : 118,
+ "precision" : 3,
+ "step" : 0.01,
+ "text" : "Sound Sensitivity",
+ "type" : "slider",
+ "value" : 5
+ },
+ "splat_color" :
+ {
+ "condition" : "!colorful.value",
+ "order" : 111,
+ "text" : "Splat Color",
+ "type" : "color",
+ "value" : "0 1 0"
+ },
+ "splat_color_2" :
+ {
+ "condition" : "more_colors.value && !colorful.value",
+ "order" : 113,
+ "text" : "Splat Color 2",
+ "type" : "color",
+ "value" : "0 1 1"
+ },
+ "splat_color_3" :
+ {
+ "condition" : "more_colors.value && !colorful.value",
+ "order" : 114,
+ "text" : "Splat Color 3",
+ "type" : "color",
+ "value" : "0 0 1"
+ },
+ "splat_color_4" :
+ {
+ "condition" : "more_colors.value && !colorful.value",
+ "order" : 115,
+ "text" : "Splat Color 4",
+ "type" : "color",
+ "value" : "1 0 0"
+ },
+ "splat_color_5" :
+ {
+ "condition" : "more_colors.value && !colorful.value",
+ "order" : 116,
+ "text" : "Splat Color 5",
+ "type" : "color",
+ "value" : "0.9411764705882353 1 0"
+ },
+ "splat_on_click" :
+ {
+ "order" : 101,
+ "text" : "Splat on click",
+ "type" : "bool",
+ "value" : true
+ },
+ "splat_radius" :
+ {
+ "fraction" : true,
+ "max" : 1,
+ "min" : 0.01,
+ "order" : 127,
+ "precision" : 2,
+ "step" : 0.01,
+ "text" : "Splat Radius",
+ "type" : "slider",
+ "value" : 0.29999999999999999
+ },
+ "use_background_image" :
+ {
+ "order" : 102,
+ "text" : "Use Background Image",
+ "type" : "bool",
+ "value" : false
+ },
+ "velocity_diffusion" :
+ {
+ "fraction" : true,
+ "max" : 1,
+ "min" : 0.90000000000000002,
+ "order" : 124,
+ "precision" : 3,
+ "step" : 0.001,
+ "text" : "Velocity Diffusion",
+ "type" : "slider",
+ "value" : 0.97999999999999998
+ },
+ "vorticity" :
+ {
+ "fraction" : false,
+ "max" : 50,
+ "min" : 0,
+ "order" : 126,
+ "text" : "Vorticity",
+ "type" : "slider",
+ "value" : 30
+ }
+ },
+ "supportsaudioprocessing" : true
+ },
+ "preview" : "preview.gif",
+ "tags" : [ "Abstract" ],
+ "title" : "Colorful Fluid Animation [Audio Responsive]",
+ "type" : "web",
+ "version" : 3,
+ "visibility" : "public",
+ "workshopid" : "1748506393",
+ "workshopurl" : "steam://url/CommunityFilePage/1748506393"
+}
\ No newline at end of file
diff --git a/tools/fluidsim/script.js b/tools/fluidsim/script.js
new file mode 100644
index 0000000..01f0b68
--- /dev/null
+++ b/tools/fluidsim/script.js
@@ -0,0 +1,1381 @@
+'use strict';
+
+const canvas = document.getElementsByTagName('canvas')[0];
+canvas.width = canvas.clientWidth;
+canvas.height = canvas.clientHeight;
+
+Array.prototype.getRandom = function() {
+ return this[Math.floor(Math.random() * this.length)];
+};
+
+let splatColors = [{ r: 0, g: 0.15, b: 0 }];
+
+let idleSplats;
+
+function idleSplatsFunction() {
+ multipleSplats(parseInt(Math.random() * config.RANDOM_AMOUNT) + (config.RANDOM_AMOUNT / 2) + 1);
+}
+
+let config = {
+ SIM_RESOLUTION: 256,
+ DYE_RESOLUTION: 1024,
+ DENSITY_DISSIPATION: 0.97,
+ VELOCITY_DISSIPATION: 0.98,
+ PRESSURE_DISSIPATION: 0.8,
+ PRESSURE_ITERATIONS: 20,
+ CURL: 30,
+ SPLAT_RADIUS: 0.3,
+ SHADING: true,
+ COLORFUL: true,
+ PAUSED: false,
+ BACK_COLOR: { r: 0, g: 0, b: 0 },
+ TRANSPARENT: false,
+ BLOOM: true,
+ BLOOM_ITERATIONS: 8,
+ BLOOM_RESOLUTION: 256,
+ BLOOM_INTENSITY: 0.8,
+ BLOOM_THRESHOLD: 0.6,
+ BLOOM_SOFT_KNEE: 0.7,
+ POINTER_COLOR: [{ r: 0, g: 0.15, b: 0 }],
+ SOUND_SENSITIVITY: 0.25,
+ AUDIO_RESPONSIVE: true,
+ FREQ_RANGE: 8,
+ FREQ_RANGE_START: 0,
+ IDLE_SPLATS: false,
+ RANDOM_AMOUNT: 10,
+ RANDOM_INTERVAL: 1,
+ SPLAT_ON_CLICK: true,
+ SHOW_MOUSE_MOVEMENT: true
+};
+
+document.addEventListener("DOMContentLoaded", () => {
+ window.wallpaperPropertyListener = {
+ applyUserProperties: (properties) => {
+ if (properties.bloom_intensity) config.BLOOM_INTENSITY = properties.bloom_intensity.value;
+ if (properties.bloom_threshold) config.BLOOM_THRESHOLD = properties.bloom_threshold.value;
+ if (properties.colorful) config.COLORFUL = properties.colorful.value;
+ if (properties.density_diffusion) config.DENSITY_DISSIPATION = properties.density_diffusion.value;
+ if (properties.enable_bloom) config.BLOOM = properties.enable_bloom.value;
+ if (properties.paused) config.PAUSED = properties.paused.value;
+ if (properties.pressure_diffusion) config.PRESSURE_DISSIPATION = properties.pressure_diffusion.value;
+ if (properties.shading) config.SHADING = properties.shading.value;
+ if (properties.splat_radius) config.SPLAT_RADIUS = properties.splat_radius.value;
+ if (properties.velocity_diffusion) config.VELOCITY_DISSIPATION = properties.velocity_diffusion.value;
+ if (properties.vorticity) config.CURL = properties.vorticity.value;
+ if (properties.sound_sensitivity) config.SOUND_SENSITIVITY = properties.sound_sensitivity.value;
+ if (properties.audio_responsive) config.AUDIO_RESPONSIVE = properties.audio_responsive.value;
+ if (properties.simulation_resolution) {
+ config.SIM_RESOLUTION = properties.simulation_resolution.value;
+ initFramebuffers();
+ }
+ if (properties.dye_resolution) {
+ config.DYE_RESOLUTION = properties.dye_resolution.value;
+ initFramebuffers();
+ }
+ if (properties.splat_color) {
+ splatColors[0] = rgbToPointerColor(properties.splat_color.value);
+ if (!config.COLORFUL) config.POINTER_COLOR = [splatColors[0]];
+ }
+ if (properties.splat_color_2) splatColors[1] = rgbToPointerColor(properties.splat_color_2.value);
+ if (properties.splat_color_3) splatColors[2] = rgbToPointerColor(properties.splat_color_3.value);
+ if (properties.splat_color_4) splatColors[3] = rgbToPointerColor(properties.splat_color_4.value);
+ if (properties.splat_color_5) splatColors[4] = rgbToPointerColor(properties.splat_color_5.value);
+ if (properties.background_color) {
+ let c = properties.background_color.value.split(" "),
+ r = Math.floor(c[0]*255),
+ g = Math.floor(c[1]*255),
+ b = Math.floor(c[2]*255);
+ document.body.style.backgroundColor = `rgb(${r}, ${g}, ${b})`;
+ config.BACK_COLOR.r = r;
+ config.BACK_COLOR.g = g;
+ config.BACK_COLOR.b = b;
+ }
+ if (properties.more_colors && !properties.more_colors.value) {
+ config.POINTER_COLOR = [splatColors[0]];
+ } else if (properties.more_colors && properties.more_colors.value) {
+ config.POINTER_COLOR = splatColors;
+ }
+ if (properties.use_background_image) config.TRANSPARENT = properties.use_background_image.value;
+ if (properties.background_image) canvas.style.backgroundImage = `url("file:///${properties.background_image.value}")`;
+ if (properties.repeat_background) canvas.style.backgroundRepeat = properties.repeat_background.value ? "repeat" : "no-repeat";
+ if (properties.background_image_size) canvas.style.backgroundSize = properties.background_image_size.value;
+ if (properties.frequency_range) {
+ config.FREQ_RANGE = properties.frequency_range.value;
+
+ if (config.FREQ_RANGE + config.FREQ_RANGE_START > 61) {
+ config.FREQ_RANGE_START = 62 - config.FREQ_RANGE;
+ }
+ }
+ if (properties.frequency_range_start) {
+ if (config.FREQ_RANGE + properties.frequency_range_start.value > 61) {
+ config.FREQ_RANGE_START = 62 - config.FREQ_RANGE;
+ } else {
+ config.FREQ_RANGE_START = properties.frequency_range_start.value;
+ }
+ }
+ if (properties.idle_random_splats) {
+ config.IDLE_SPLATS = properties.idle_random_splats.value;
+ if (properties.idle_random_splats.value) {
+ idleSplats = setInterval(idleSplatsFunction, config.RANDOM_INTERVAL * 1000);
+ } else {
+ clearInterval(idleSplats);
+ }
+ }
+ if (properties.random_splat_interval) {
+ config.RANDOM_INTERVAL = properties.random_splat_interval.value;
+ if (config.IDLE_SPLATS) {
+ clearInterval(idleSplats);
+ idleSplats = setInterval(idleSplatsFunction, config.RANDOM_INTERVAL * 1000);
+ }
+ }
+ if (properties.random_splat_amount) {
+ config.RANDOM_AMOUNT = properties.random_splat_amount.value;
+ if (config.IDLE_SPLATS) {
+ clearInterval(idleSplats);
+ idleSplats = setInterval(idleSplatsFunction, config.RANDOM_INTERVAL * 1000);
+ }
+ }
+ if (properties.splat_on_click) config.SPLAT_ON_CLICK = properties.splat_on_click.value;
+ if (properties.show_mouse_movement) config.SHOW_MOUSE_MOVEMENT = properties.show_mouse_movement.value;
+ }
+ };
+
+ window.wallpaperRegisterAudioListener((audioArray) => {
+ if (!config.AUDIO_RESPONSIVE) return;
+ if (audioArray[0] > 5) return;
+
+ let bass = 0.0;
+ let half = Math.floor(audioArray.length / 2);
+
+ for (let i = 0; i <= config.FREQ_RANGE; i++) {
+ bass += audioArray[i + config.FREQ_RANGE_START];
+ bass += audioArray[half + (i + config.FREQ_RANGE_START)];
+ }
+ bass /= (config.FREQ_RANGE * 2);
+ multipleSplats(Math.floor((bass * config.SOUND_SENSITIVITY) * 10));
+ });
+});
+
+function indexOfMax(arr) {
+ if (arr.length === 0) {
+ return -1;
+ }
+
+ var max = arr[0];
+ var maxIndex = 0;
+
+ for (var i = 1; i < arr.length; i++) {
+ if (arr[i] > max) {
+ maxIndex = i;
+ max = arr[i];
+ }
+ }
+
+ return maxIndex;
+}
+
+class pointerPrototype {
+ constructor() {
+ this.id = -1;
+ this.x = 0;
+ this.y = 0;
+ this.dx = 0;
+ this.dy = 0;
+ this.down = false;
+ this.moved = false;
+ this.color = config.COLORFUL ? generateColor() : config.POINTER_COLOR.getRandom();
+ }
+}
+
+let pointers = [];
+let splatStack = [];
+let bloomFramebuffers = [];
+pointers.push(new pointerPrototype());
+
+const { gl, ext } = getWebGLContext(canvas);
+
+if (isMobile())
+ config.SHADING = false;
+if (!ext.supportLinearFiltering)
+{
+ config.SHADING = false;
+ config.BLOOM = false;
+}
+
+function getWebGLContext (canvas) {
+ const params = { alpha: true, depth: false, stencil: false, antialias: false, preserveDrawingBuffer: false };
+
+ let gl = canvas.getContext('webgl2', params);
+ const isWebGL2 = !!gl;
+ if (!isWebGL2)
+ gl = canvas.getContext('webgl', params) || canvas.getContext('experimental-webgl', params);
+
+ let halfFloat;
+ let supportLinearFiltering;
+ if (isWebGL2) {
+ gl.getExtension('EXT_color_buffer_float');
+ supportLinearFiltering = gl.getExtension('OES_texture_float_linear');
+ } else {
+ halfFloat = gl.getExtension('OES_texture_half_float');
+ supportLinearFiltering = gl.getExtension('OES_texture_half_float_linear');
+ }
+
+ gl.clearColor(0.0, 0.0, 0.0, 1.0);
+
+ const halfFloatTexType = isWebGL2 ? gl.HALF_FLOAT : halfFloat.HALF_FLOAT_OES;
+ let formatRGBA;
+ let formatRG;
+ let formatR;
+
+ if (isWebGL2)
+ {
+ formatRGBA = getSupportedFormat(gl, gl.RGBA16F, gl.RGBA, halfFloatTexType);
+ formatRG = getSupportedFormat(gl, gl.RG16F, gl.RG, halfFloatTexType);
+ formatR = getSupportedFormat(gl, gl.R16F, gl.RED, halfFloatTexType);
+ }
+ else
+ {
+ formatRGBA = getSupportedFormat(gl, gl.RGBA, gl.RGBA, halfFloatTexType);
+ formatRG = getSupportedFormat(gl, gl.RGBA, gl.RGBA, halfFloatTexType);
+ formatR = getSupportedFormat(gl, gl.RGBA, gl.RGBA, halfFloatTexType);
+ }
+
+ return {
+ gl,
+ ext: {
+ formatRGBA,
+ formatRG,
+ formatR,
+ halfFloatTexType,
+ supportLinearFiltering
+ }
+ };
+}
+
+function getSupportedFormat (gl, internalFormat, format, type)
+{
+ if (!supportRenderTextureFormat(gl, internalFormat, format, type))
+ {
+ switch (internalFormat)
+ {
+ case gl.R16F:
+ return getSupportedFormat(gl, gl.RG16F, gl.RG, type);
+ case gl.RG16F:
+ return getSupportedFormat(gl, gl.RGBA16F, gl.RGBA, type);
+ default:
+ return null;
+ }
+ }
+
+ return {
+ internalFormat,
+ format
+ }
+}
+
+function supportRenderTextureFormat (gl, internalFormat, format, type) {
+ let texture = gl.createTexture();
+ gl.bindTexture(gl.TEXTURE_2D, texture);
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
+ gl.texImage2D(gl.TEXTURE_2D, 0, internalFormat, 4, 4, 0, format, type, null);
+
+ let fbo = gl.createFramebuffer();
+ gl.bindFramebuffer(gl.FRAMEBUFFER, fbo);
+ gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, texture, 0);
+
+ const status = gl.checkFramebufferStatus(gl.FRAMEBUFFER);
+ if (status != gl.FRAMEBUFFER_COMPLETE)
+ return false;
+ return true;
+}
+
+function isMobile () {
+ return /Mobi|Android/i.test(navigator.userAgent);
+}
+
+class GLProgram {
+ constructor (vertexShader, fragmentShader) {
+ this.uniforms = {};
+ this.program = gl.createProgram();
+
+ gl.attachShader(this.program, vertexShader);
+ gl.attachShader(this.program, fragmentShader);
+ gl.linkProgram(this.program);
+
+ if (!gl.getProgramParameter(this.program, gl.LINK_STATUS))
+ throw gl.getProgramInfoLog(this.program);
+
+ const uniformCount = gl.getProgramParameter(this.program, gl.ACTIVE_UNIFORMS);
+ for (let i = 0; i < uniformCount; i++) {
+ const uniformName = gl.getActiveUniform(this.program, i).name;
+ this.uniforms[uniformName] = gl.getUniformLocation(this.program, uniformName);
+ }
+ }
+
+ bind () {
+ gl.useProgram(this.program);
+ }
+}
+
+function compileShader (type, source) {
+ const shader = gl.createShader(type);
+ gl.shaderSource(shader, source);
+ gl.compileShader(shader);
+
+ if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS))
+ throw gl.getShaderInfoLog(shader);
+
+ return shader;
+};
+
+const baseVertexShader = compileShader(gl.VERTEX_SHADER, `
+ precision highp float;
+
+ attribute vec2 aPosition;
+ varying vec2 vUv;
+ varying vec2 vL;
+ varying vec2 vR;
+ varying vec2 vT;
+ varying vec2 vB;
+ uniform vec2 texelSize;
+
+ void main () {
+ vUv = aPosition * 0.5 + 0.5;
+ vL = vUv - vec2(texelSize.x, 0.0);
+ vR = vUv + vec2(texelSize.x, 0.0);
+ vT = vUv + vec2(0.0, texelSize.y);
+ vB = vUv - vec2(0.0, texelSize.y);
+ gl_Position = vec4(aPosition, 0.0, 1.0);
+ }
+`);
+
+const clearShader = compileShader(gl.FRAGMENT_SHADER, `
+ precision mediump float;
+ precision mediump sampler2D;
+
+ varying highp vec2 vUv;
+ uniform sampler2D uTexture;
+ uniform float value;
+
+ void main () {
+ gl_FragColor = value * texture2D(uTexture, vUv);
+ }
+`);
+
+const colorShader = compileShader(gl.FRAGMENT_SHADER, `
+ precision mediump float;
+
+ uniform vec4 color;
+
+ void main () {
+ gl_FragColor = color;
+ }
+`);
+
+const backgroundShader = compileShader(gl.FRAGMENT_SHADER, `
+ void main () {
+ gl_FragColor = vec4(0.0, 0.0, 0.0, 0.0);
+ }
+`);
+
+const displayShader = compileShader(gl.FRAGMENT_SHADER, `
+ precision highp float;
+ precision highp sampler2D;
+
+ varying vec2 vUv;
+ uniform sampler2D uTexture;
+
+ void main () {
+ vec3 C = texture2D(uTexture, vUv).rgb;
+ float a = max(C.r, max(C.g, C.b));
+ gl_FragColor = vec4(C, a);
+ }
+`);
+
+const displayBloomShader = compileShader(gl.FRAGMENT_SHADER, `
+ precision highp float;
+ precision highp sampler2D;
+
+ varying vec2 vUv;
+ uniform sampler2D uTexture;
+ uniform sampler2D uBloom;
+ uniform sampler2D uDithering;
+ uniform vec2 ditherScale;
+
+ void main () {
+ vec3 C = texture2D(uTexture, vUv).rgb;
+ vec3 bloom = texture2D(uBloom, vUv).rgb;
+ bloom = pow(bloom.rgb, vec3(1.0 / 2.2));
+ C += bloom;
+ float a = max(C.r, max(C.g, C.b));
+ gl_FragColor = vec4(C, a);
+ }
+`);
+
+const displayShadingShader = compileShader(gl.FRAGMENT_SHADER, `
+ precision highp float;
+ precision highp sampler2D;
+
+ varying vec2 vUv;
+ varying vec2 vL;
+ varying vec2 vR;
+ varying vec2 vT;
+ varying vec2 vB;
+ uniform sampler2D uTexture;
+ uniform vec2 texelSize;
+
+ void main () {
+ vec3 L = texture2D(uTexture, vL).rgb;
+ vec3 R = texture2D(uTexture, vR).rgb;
+ vec3 T = texture2D(uTexture, vT).rgb;
+ vec3 B = texture2D(uTexture, vB).rgb;
+ vec3 C = texture2D(uTexture, vUv).rgb;
+
+ float dx = length(R) - length(L);
+ float dy = length(T) - length(B);
+
+ vec3 n = normalize(vec3(dx, dy, length(texelSize)));
+ vec3 l = vec3(0.0, 0.0, 1.0);
+
+ float diffuse = clamp(dot(n, l) + 0.7, 0.7, 1.0);
+ C.rgb *= diffuse;
+
+ float a = max(C.r, max(C.g, C.b));
+ gl_FragColor = vec4(C, a);
+ }
+`);
+
+const displayBloomShadingShader = compileShader(gl.FRAGMENT_SHADER, `
+ precision highp float;
+ precision highp sampler2D;
+
+ varying vec2 vUv;
+ varying vec2 vL;
+ varying vec2 vR;
+ varying vec2 vT;
+ varying vec2 vB;
+ uniform sampler2D uTexture;
+ uniform sampler2D uBloom;
+ uniform sampler2D uDithering;
+ uniform vec2 ditherScale;
+ uniform vec2 texelSize;
+
+ void main () {
+ vec3 L = texture2D(uTexture, vL).rgb;
+ vec3 R = texture2D(uTexture, vR).rgb;
+ vec3 T = texture2D(uTexture, vT).rgb;
+ vec3 B = texture2D(uTexture, vB).rgb;
+ vec3 C = texture2D(uTexture, vUv).rgb;
+
+ float dx = length(R) - length(L);
+ float dy = length(T) - length(B);
+
+ vec3 n = normalize(vec3(dx, dy, length(texelSize)));
+ vec3 l = vec3(0.0, 0.0, 1.0);
+
+ float diffuse = clamp(dot(n, l) + 0.7, 0.7, 1.0);
+ C *= diffuse;
+
+ vec3 bloom = texture2D(uBloom, vUv).rgb;
+ bloom = pow(bloom.rgb, vec3(1.0 / 2.2));
+ C += bloom;
+
+ float a = max(C.r, max(C.g, C.b));
+ gl_FragColor = vec4(C, a);
+ }
+`);
+
+const bloomPrefilterShader = compileShader(gl.FRAGMENT_SHADER, `
+ precision mediump float;
+ precision mediump sampler2D;
+
+ varying vec2 vUv;
+ uniform sampler2D uTexture;
+ uniform vec3 curve;
+ uniform float threshold;
+
+ void main () {
+ vec3 c = texture2D(uTexture, vUv).rgb;
+ float br = max(c.r, max(c.g, c.b));
+ float rq = clamp(br - curve.x, 0.0, curve.y);
+ rq = curve.z * rq * rq;
+ c *= max(rq, br - threshold) / max(br, 0.0001);
+ gl_FragColor = vec4(c, 0.0);
+ }
+`);
+
+const bloomBlurShader = compileShader(gl.FRAGMENT_SHADER, `
+ precision mediump float;
+ precision mediump sampler2D;
+
+ varying vec2 vL;
+ varying vec2 vR;
+ varying vec2 vT;
+ varying vec2 vB;
+ uniform sampler2D uTexture;
+
+ void main () {
+ vec4 sum = vec4(0.0);
+ sum += texture2D(uTexture, vL);
+ sum += texture2D(uTexture, vR);
+ sum += texture2D(uTexture, vT);
+ sum += texture2D(uTexture, vB);
+ sum *= 0.25;
+ gl_FragColor = sum;
+ }
+`);
+
+const bloomFinalShader = compileShader(gl.FRAGMENT_SHADER, `
+ precision mediump float;
+ precision mediump sampler2D;
+
+ varying vec2 vL;
+ varying vec2 vR;
+ varying vec2 vT;
+ varying vec2 vB;
+ uniform sampler2D uTexture;
+ uniform float intensity;
+
+ void main () {
+ vec4 sum = vec4(0.0);
+ sum += texture2D(uTexture, vL);
+ sum += texture2D(uTexture, vR);
+ sum += texture2D(uTexture, vT);
+ sum += texture2D(uTexture, vB);
+ sum *= 0.25;
+ gl_FragColor = sum * intensity;
+ }
+`);
+
+const splatShader = compileShader(gl.FRAGMENT_SHADER, `
+ precision highp float;
+ precision highp sampler2D;
+
+ varying vec2 vUv;
+ uniform sampler2D uTarget;
+ uniform float aspectRatio;
+ uniform vec3 color;
+ uniform vec2 point;
+ uniform float radius;
+
+ void main () {
+ vec2 p = vUv - point.xy;
+ p.x *= aspectRatio;
+ vec3 splat = exp(-dot(p, p) / radius) * color;
+ vec3 base = texture2D(uTarget, vUv).xyz;
+ gl_FragColor = vec4(base + splat, 1.0);
+ }
+`);
+
+const advectionManualFilteringShader = compileShader(gl.FRAGMENT_SHADER, `
+ precision highp float;
+ precision highp sampler2D;
+
+ varying vec2 vUv;
+ uniform sampler2D uVelocity;
+ uniform sampler2D uSource;
+ uniform vec2 texelSize;
+ uniform vec2 dyeTexelSize;
+ uniform float dt;
+ uniform float dissipation;
+
+ vec4 bilerp (sampler2D sam, vec2 uv, vec2 tsize) {
+ vec2 st = uv / tsize - 0.5;
+
+ vec2 iuv = floor(st);
+ vec2 fuv = fract(st);
+
+ vec4 a = texture2D(sam, (iuv + vec2(0.5, 0.5)) * tsize);
+ vec4 b = texture2D(sam, (iuv + vec2(1.5, 0.5)) * tsize);
+ vec4 c = texture2D(sam, (iuv + vec2(0.5, 1.5)) * tsize);
+ vec4 d = texture2D(sam, (iuv + vec2(1.5, 1.5)) * tsize);
+
+ return mix(mix(a, b, fuv.x), mix(c, d, fuv.x), fuv.y);
+ }
+
+ void main () {
+ vec2 coord = vUv - dt * bilerp(uVelocity, vUv, texelSize).xy * texelSize;
+ gl_FragColor = dissipation * bilerp(uSource, coord, dyeTexelSize);
+ gl_FragColor.a = 1.0;
+ }
+`);
+
+const advectionShader = compileShader(gl.FRAGMENT_SHADER, `
+ precision highp float;
+ precision highp sampler2D;
+
+ varying vec2 vUv;
+ uniform sampler2D uVelocity;
+ uniform sampler2D uSource;
+ uniform vec2 texelSize;
+ uniform float dt;
+ uniform float dissipation;
+
+ void main () {
+ vec2 coord = vUv - dt * texture2D(uVelocity, vUv).xy * texelSize;
+ gl_FragColor = dissipation * texture2D(uSource, coord);
+ gl_FragColor.a = 1.0;
+ }
+`);
+
+const divergenceShader = compileShader(gl.FRAGMENT_SHADER, `
+ precision mediump float;
+ precision mediump sampler2D;
+
+ varying highp vec2 vUv;
+ varying highp vec2 vL;
+ varying highp vec2 vR;
+ varying highp vec2 vT;
+ varying highp vec2 vB;
+ uniform sampler2D uVelocity;
+
+ void main () {
+ float L = texture2D(uVelocity, vL).x;
+ float R = texture2D(uVelocity, vR).x;
+ float T = texture2D(uVelocity, vT).y;
+ float B = texture2D(uVelocity, vB).y;
+
+ vec2 C = texture2D(uVelocity, vUv).xy;
+ if (vL.x < 0.0) { L = -C.x; }
+ if (vR.x > 1.0) { R = -C.x; }
+ if (vT.y > 1.0) { T = -C.y; }
+ if (vB.y < 0.0) { B = -C.y; }
+
+ float div = 0.5 * (R - L + T - B);
+ gl_FragColor = vec4(div, 0.0, 0.0, 1.0);
+ }
+`);
+
+const curlShader = compileShader(gl.FRAGMENT_SHADER, `
+ precision mediump float;
+ precision mediump sampler2D;
+
+ varying highp vec2 vUv;
+ varying highp vec2 vL;
+ varying highp vec2 vR;
+ varying highp vec2 vT;
+ varying highp vec2 vB;
+ uniform sampler2D uVelocity;
+
+ void main () {
+ float L = texture2D(uVelocity, vL).y;
+ float R = texture2D(uVelocity, vR).y;
+ float T = texture2D(uVelocity, vT).x;
+ float B = texture2D(uVelocity, vB).x;
+ float vorticity = R - L - T + B;
+ gl_FragColor = vec4(0.5 * vorticity, 0.0, 0.0, 1.0);
+ }
+`);
+
+const vorticityShader = compileShader(gl.FRAGMENT_SHADER, `
+ precision highp float;
+ precision highp sampler2D;
+
+ varying vec2 vUv;
+ varying vec2 vL;
+ varying vec2 vR;
+ varying vec2 vT;
+ varying vec2 vB;
+ uniform sampler2D uVelocity;
+ uniform sampler2D uCurl;
+ uniform float curl;
+ uniform float dt;
+
+ void main () {
+ float L = texture2D(uCurl, vL).x;
+ float R = texture2D(uCurl, vR).x;
+ float T = texture2D(uCurl, vT).x;
+ float B = texture2D(uCurl, vB).x;
+ float C = texture2D(uCurl, vUv).x;
+
+ vec2 force = 0.5 * vec2(abs(T) - abs(B), abs(R) - abs(L));
+ force /= length(force) + 0.0001;
+ force *= curl * C;
+ force.y *= -1.0;
+
+ vec2 vel = texture2D(uVelocity, vUv).xy;
+ gl_FragColor = vec4(vel + force * dt, 0.0, 1.0);
+ }
+`);
+
+const pressureShader = compileShader(gl.FRAGMENT_SHADER, `
+ precision mediump float;
+ precision mediump sampler2D;
+
+ varying highp vec2 vUv;
+ varying highp vec2 vL;
+ varying highp vec2 vR;
+ varying highp vec2 vT;
+ varying highp vec2 vB;
+ uniform sampler2D uPressure;
+ uniform sampler2D uDivergence;
+
+ vec2 boundary (vec2 uv) {
+ return uv;
+ // uncomment if you use wrap or repeat texture mode
+ // uv = min(max(uv, 0.0), 1.0);
+ // return uv;
+ }
+
+ void main () {
+ float L = texture2D(uPressure, boundary(vL)).x;
+ float R = texture2D(uPressure, boundary(vR)).x;
+ float T = texture2D(uPressure, boundary(vT)).x;
+ float B = texture2D(uPressure, boundary(vB)).x;
+ float C = texture2D(uPressure, vUv).x;
+ float divergence = texture2D(uDivergence, vUv).x;
+ float pressure = (L + R + B + T - divergence) * 0.25;
+ gl_FragColor = vec4(pressure, 0.0, 0.0, 1.0);
+ }
+`);
+
+const gradientSubtractShader = compileShader(gl.FRAGMENT_SHADER, `
+ precision mediump float;
+ precision mediump sampler2D;
+
+ varying highp vec2 vUv;
+ varying highp vec2 vL;
+ varying highp vec2 vR;
+ varying highp vec2 vT;
+ varying highp vec2 vB;
+ uniform sampler2D uPressure;
+ uniform sampler2D uVelocity;
+
+ vec2 boundary (vec2 uv) {
+ return uv;
+ // uv = min(max(uv, 0.0), 1.0);
+ // return uv;
+ }
+
+ void main () {
+ float L = texture2D(uPressure, boundary(vL)).x;
+ float R = texture2D(uPressure, boundary(vR)).x;
+ float T = texture2D(uPressure, boundary(vT)).x;
+ float B = texture2D(uPressure, boundary(vB)).x;
+ vec2 velocity = texture2D(uVelocity, vUv).xy;
+ velocity.xy -= vec2(R - L, T - B);
+ gl_FragColor = vec4(velocity, 0.0, 1.0);
+ }
+`);
+
+const blit = (() => {
+ gl.bindBuffer(gl.ARRAY_BUFFER, gl.createBuffer());
+ gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([-1, -1, -1, 1, 1, 1, 1, -1]), gl.STATIC_DRAW);
+ gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, gl.createBuffer());
+ gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array([0, 1, 2, 0, 2, 3]), gl.STATIC_DRAW);
+ gl.vertexAttribPointer(0, 2, gl.FLOAT, false, 0, 0);
+ gl.enableVertexAttribArray(0);
+
+ return (destination) => {
+ gl.bindFramebuffer(gl.FRAMEBUFFER, destination);
+ gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0);
+ }
+})();
+
+let simWidth;
+let simHeight;
+let dyeWidth;
+let dyeHeight;
+let density;
+let velocity;
+let divergence;
+let curl;
+let pressure;
+let bloom;
+
+let ditheringTexture = createTextureAsync('LDR_RGB1_0.png');
+
+const clearProgram = new GLProgram(baseVertexShader, clearShader);
+const colorProgram = new GLProgram(baseVertexShader, colorShader);
+const backgroundProgram = new GLProgram(baseVertexShader, backgroundShader);
+const displayProgram = new GLProgram(baseVertexShader, displayShader);
+const displayBloomProgram = new GLProgram(baseVertexShader, displayBloomShader);
+const displayShadingProgram = new GLProgram(baseVertexShader, displayShadingShader);
+const displayBloomShadingProgram = new GLProgram(baseVertexShader, displayBloomShadingShader);
+const bloomPrefilterProgram = new GLProgram(baseVertexShader, bloomPrefilterShader);
+const bloomBlurProgram = new GLProgram(baseVertexShader, bloomBlurShader);
+const bloomFinalProgram = new GLProgram(baseVertexShader, bloomFinalShader);
+const splatProgram = new GLProgram(baseVertexShader, splatShader);
+const advectionProgram = new GLProgram(baseVertexShader, ext.supportLinearFiltering ? advectionShader : advectionManualFilteringShader);
+const divergenceProgram = new GLProgram(baseVertexShader, divergenceShader);
+const curlProgram = new GLProgram(baseVertexShader, curlShader);
+const vorticityProgram = new GLProgram(baseVertexShader, vorticityShader);
+const pressureProgram = new GLProgram(baseVertexShader, pressureShader);
+const gradienSubtractProgram = new GLProgram(baseVertexShader, gradientSubtractShader);
+
+function initFramebuffers () {
+ let simRes = getResolution(config.SIM_RESOLUTION);
+ let dyeRes = getResolution(config.DYE_RESOLUTION);
+
+ simWidth = simRes.width;
+ simHeight = simRes.height;
+ dyeWidth = dyeRes.width;
+ dyeHeight = dyeRes.height;
+
+ const texType = ext.halfFloatTexType;
+ const rgba = ext.formatRGBA;
+ const rg = ext.formatRG;
+ const r = ext.formatR;
+ const filtering = ext.supportLinearFiltering ? gl.LINEAR : gl.NEAREST;
+
+ if (density == null)
+ density = createDoubleFBO(dyeWidth, dyeHeight, rgba.internalFormat, rgba.format, texType, filtering);
+ else
+ density = resizeDoubleFBO(density, dyeWidth, dyeHeight, rgba.internalFormat, rgba.format, texType, filtering);
+
+ if (velocity == null)
+ velocity = createDoubleFBO(simWidth, simHeight, rg.internalFormat, rg.format, texType, filtering);
+ else
+ velocity = resizeDoubleFBO(velocity, simWidth, simHeight, rg.internalFormat, rg.format, texType, filtering);
+
+ divergence = createFBO (simWidth, simHeight, r.internalFormat, r.format, texType, gl.NEAREST);
+ curl = createFBO (simWidth, simHeight, r.internalFormat, r.format, texType, gl.NEAREST);
+ pressure = createDoubleFBO(simWidth, simHeight, r.internalFormat, r.format, texType, gl.NEAREST);
+
+ initBloomFramebuffers();
+}
+
+function initBloomFramebuffers () {
+ let res = getResolution(config.BLOOM_RESOLUTION);
+
+ const texType = ext.halfFloatTexType;
+ const rgba = ext.formatRGBA;
+ const filtering = ext.supportLinearFiltering ? gl.LINEAR : gl.NEAREST;
+
+ bloom = createFBO(res.width, res.height, rgba.internalFormat, rgba.format, texType, filtering);
+
+ bloomFramebuffers.length = 0;
+ for (let i = 0; i < config.BLOOM_ITERATIONS; i++)
+ {
+ let width = res.width >> (i + 1);
+ let height = res.height >> (i + 1);
+
+ if (width < 2 || height < 2) break;
+
+ let fbo = createFBO(width, height, rgba.internalFormat, rgba.format, texType, filtering);
+ bloomFramebuffers.push(fbo);
+ }
+}
+
+function createFBO (w, h, internalFormat, format, type, param) {
+ gl.activeTexture(gl.TEXTURE0);
+ let texture = gl.createTexture();
+ gl.bindTexture(gl.TEXTURE_2D, texture);
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, param);
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, param);
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
+ gl.texImage2D(gl.TEXTURE_2D, 0, internalFormat, w, h, 0, format, type, null);
+
+ let fbo = gl.createFramebuffer();
+ gl.bindFramebuffer(gl.FRAMEBUFFER, fbo);
+ gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, texture, 0);
+ gl.viewport(0, 0, w, h);
+ gl.clear(gl.COLOR_BUFFER_BIT);
+
+ return {
+ texture,
+ fbo,
+ width: w,
+ height: h,
+ attach (id) {
+ gl.activeTexture(gl.TEXTURE0 + id);
+ gl.bindTexture(gl.TEXTURE_2D, texture);
+ return id;
+ }
+ };
+}
+
+function createDoubleFBO (w, h, internalFormat, format, type, param) {
+ let fbo1 = createFBO(w, h, internalFormat, format, type, param);
+ let fbo2 = createFBO(w, h, internalFormat, format, type, param);
+
+ return {
+ get read () {
+ return fbo1;
+ },
+ set read (value) {
+ fbo1 = value;
+ },
+ get write () {
+ return fbo2;
+ },
+ set write (value) {
+ fbo2 = value;
+ },
+ swap () {
+ let temp = fbo1;
+ fbo1 = fbo2;
+ fbo2 = temp;
+ }
+ }
+}
+
+function resizeFBO (target, w, h, internalFormat, format, type, param) {
+ let newFBO = createFBO(w, h, internalFormat, format, type, param);
+ clearProgram.bind();
+ gl.uniform1i(clearProgram.uniforms.uTexture, target.attach(0));
+ gl.uniform1f(clearProgram.uniforms.value, 1);
+ blit(newFBO.fbo);
+ return newFBO;
+}
+
+function resizeDoubleFBO (target, w, h, internalFormat, format, type, param) {
+ target.read = resizeFBO(target.read, w, h, internalFormat, format, type, param);
+ target.write = createFBO(w, h, internalFormat, format, type, param);
+ return target;
+}
+
+function createTextureAsync (url) {
+ let texture = gl.createTexture();
+ gl.bindTexture(gl.TEXTURE_2D, texture);
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT);
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT);
+ gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB, 1, 1, 0, gl.RGB, gl.UNSIGNED_BYTE, new Uint8Array([255, 255, 255]));
+
+ let obj = {
+ texture,
+ width: 1,
+ height: 1,
+ attach (id) {
+ gl.activeTexture(gl.TEXTURE0 + id);
+ gl.bindTexture(gl.TEXTURE_2D, texture);
+ return id;
+ }
+ };
+
+ let image = new Image();
+ image.onload = () => {
+ obj.width = image.width;
+ obj.height = image.height;
+ gl.bindTexture(gl.TEXTURE_2D, texture);
+ gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB, gl.RGB, gl.UNSIGNED_BYTE, image);
+ };
+ image.src = url;
+
+ return obj;
+}
+
+initFramebuffers();
+multipleSplats(parseInt(Math.random() * 20) + 3);
+
+let lastColorChangeTime = Date.now();
+
+update();
+
+function update () {
+ resizeCanvas();
+ input();
+ if (!config.PAUSED)
+ step(0.016);
+ render(null);
+ requestAnimationFrame(update);
+}
+
+function input () {
+ if (splatStack.length > 0)
+ multipleSplats(splatStack.pop());
+
+ for (let i = 0; i < pointers.length; i++) {
+ const p = pointers[i];
+ if (p.moved) {
+ splat(p.x, p.y, p.dx, p.dy, p.color);
+ p.moved = false;
+ }
+ }
+
+ if (lastColorChangeTime + 100 < Date.now())
+ {
+ lastColorChangeTime = Date.now();
+ for (let i = 0; i < pointers.length; i++) {
+ const p = pointers[i];
+ p.color = config.COLORFUL ? generateColor() : config.POINTER_COLOR.getRandom();
+ }
+ }
+}
+
+function step (dt) {
+ gl.disable(gl.BLEND);
+ gl.viewport(0, 0, simWidth, simHeight);
+
+ curlProgram.bind();
+ gl.uniform2f(curlProgram.uniforms.texelSize, 1.0 / simWidth, 1.0 / simHeight);
+ gl.uniform1i(curlProgram.uniforms.uVelocity, velocity.read.attach(0));
+ blit(curl.fbo);
+
+ vorticityProgram.bind();
+ gl.uniform2f(vorticityProgram.uniforms.texelSize, 1.0 / simWidth, 1.0 / simHeight);
+ gl.uniform1i(vorticityProgram.uniforms.uVelocity, velocity.read.attach(0));
+ gl.uniform1i(vorticityProgram.uniforms.uCurl, curl.attach(1));
+ gl.uniform1f(vorticityProgram.uniforms.curl, config.CURL);
+ gl.uniform1f(vorticityProgram.uniforms.dt, dt);
+ blit(velocity.write.fbo);
+ velocity.swap();
+
+ divergenceProgram.bind();
+ gl.uniform2f(divergenceProgram.uniforms.texelSize, 1.0 / simWidth, 1.0 / simHeight);
+ gl.uniform1i(divergenceProgram.uniforms.uVelocity, velocity.read.attach(0));
+ blit(divergence.fbo);
+
+ clearProgram.bind();
+ gl.uniform1i(clearProgram.uniforms.uTexture, pressure.read.attach(0));
+ gl.uniform1f(clearProgram.uniforms.value, config.PRESSURE_DISSIPATION);
+ blit(pressure.write.fbo);
+ pressure.swap();
+
+ pressureProgram.bind();
+ gl.uniform2f(pressureProgram.uniforms.texelSize, 1.0 / simWidth, 1.0 / simHeight);
+ gl.uniform1i(pressureProgram.uniforms.uDivergence, divergence.attach(0));
+ for (let i = 0; i < config.PRESSURE_ITERATIONS; i++) {
+ gl.uniform1i(pressureProgram.uniforms.uPressure, pressure.read.attach(1));
+ blit(pressure.write.fbo);
+ pressure.swap();
+ }
+
+ gradienSubtractProgram.bind();
+ gl.uniform2f(gradienSubtractProgram.uniforms.texelSize, 1.0 / simWidth, 1.0 / simHeight);
+ gl.uniform1i(gradienSubtractProgram.uniforms.uPressure, pressure.read.attach(0));
+ gl.uniform1i(gradienSubtractProgram.uniforms.uVelocity, velocity.read.attach(1));
+ blit(velocity.write.fbo);
+ velocity.swap();
+
+ advectionProgram.bind();
+ gl.uniform2f(advectionProgram.uniforms.texelSize, 1.0 / simWidth, 1.0 / simHeight);
+ if (!ext.supportLinearFiltering)
+ gl.uniform2f(advectionProgram.uniforms.dyeTexelSize, 1.0 / simWidth, 1.0 / simHeight);
+ let velocityId = velocity.read.attach(0);
+ gl.uniform1i(advectionProgram.uniforms.uVelocity, velocityId);
+ gl.uniform1i(advectionProgram.uniforms.uSource, velocityId);
+ gl.uniform1f(advectionProgram.uniforms.dt, dt);
+ gl.uniform1f(advectionProgram.uniforms.dissipation, config.VELOCITY_DISSIPATION);
+ blit(velocity.write.fbo);
+ velocity.swap();
+
+ gl.viewport(0, 0, dyeWidth, dyeHeight);
+
+ if (!ext.supportLinearFiltering)
+ gl.uniform2f(advectionProgram.uniforms.dyeTexelSize, 1.0 / dyeWidth, 1.0 / dyeHeight);
+ gl.uniform1i(advectionProgram.uniforms.uVelocity, velocity.read.attach(0));
+ gl.uniform1i(advectionProgram.uniforms.uSource, density.read.attach(1));
+ gl.uniform1f(advectionProgram.uniforms.dissipation, config.DENSITY_DISSIPATION);
+ blit(density.write.fbo);
+ density.swap();
+}
+
+function render (target) {
+ if (config.BLOOM)
+ applyBloom(density.read, bloom);
+
+ if (target == null || !config.TRANSPARENT) {
+ gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA);
+ gl.enable(gl.BLEND);
+ }
+ else {
+ gl.disable(gl.BLEND);
+ }
+
+ let width = target == null ? gl.drawingBufferWidth : dyeWidth;
+ let height = target == null ? gl.drawingBufferHeight : dyeHeight;
+
+ gl.viewport(0, 0, width, height);
+
+ if (!config.TRANSPARENT) {
+ colorProgram.bind();
+ let bc = config.BACK_COLOR;
+ gl.uniform4f(colorProgram.uniforms.color, bc.r / 255, bc.g / 255, bc.b / 255, 1);
+ blit(target);
+ }
+
+ if (target == null && config.TRANSPARENT) {
+ backgroundProgram.bind();
+ gl.uniform1f(backgroundProgram.uniforms.aspectRatio, canvas.width / canvas.height);
+ blit(null);
+ }
+
+ if (config.SHADING) {
+ let program = config.BLOOM ? displayBloomShadingProgram : displayShadingProgram;
+ program.bind();
+ gl.uniform2f(program.uniforms.texelSize, 1.0 / width, 1.0 / height);
+ gl.uniform1i(program.uniforms.uTexture, density.read.attach(0));
+ if (config.BLOOM) {
+ gl.uniform1i(program.uniforms.uBloom, bloom.attach(1));
+ gl.uniform1i(program.uniforms.uDithering, ditheringTexture.attach(2));
+ let scale = getTextureScale(ditheringTexture, width, height);
+ gl.uniform2f(program.uniforms.ditherScale, scale.x, scale.y);
+ }
+ }
+ else {
+ let program = config.BLOOM ? displayBloomProgram : displayProgram;
+ program.bind();
+ gl.uniform1i(program.uniforms.uTexture, density.read.attach(0));
+ if (config.BLOOM) {
+ gl.uniform1i(program.uniforms.uBloom, bloom.attach(1));
+ gl.uniform1i(program.uniforms.uDithering, ditheringTexture.attach(2));
+ let scale = getTextureScale(ditheringTexture, width, height);
+ gl.uniform2f(program.uniforms.ditherScale, scale.x, scale.y);
+ }
+ }
+
+ blit(target);
+}
+
+function applyBloom (source, destination) {
+ if (bloomFramebuffers.length < 2)
+ return;
+
+ let last = destination;
+
+ gl.disable(gl.BLEND);
+ bloomPrefilterProgram.bind();
+ let knee = config.BLOOM_THRESHOLD * config.BLOOM_SOFT_KNEE + 0.0001;
+ let curve0 = config.BLOOM_THRESHOLD - knee;
+ let curve1 = knee * 2;
+ let curve2 = 0.25 / knee;
+ gl.uniform3f(bloomPrefilterProgram.uniforms.curve, curve0, curve1, curve2);
+ gl.uniform1f(bloomPrefilterProgram.uniforms.threshold, config.BLOOM_THRESHOLD);
+ gl.uniform1i(bloomPrefilterProgram.uniforms.uTexture, source.attach(0));
+ gl.viewport(0, 0, last.width, last.height);
+ blit(last.fbo);
+
+ bloomBlurProgram.bind();
+ for (let i = 0; i < bloomFramebuffers.length; i++) {
+ let dest = bloomFramebuffers[i];
+ gl.uniform2f(bloomBlurProgram.uniforms.texelSize, 1.0 / last.width, 1.0 / last.height);
+ gl.uniform1i(bloomBlurProgram.uniforms.uTexture, last.attach(0));
+ gl.viewport(0, 0, dest.width, dest.height);
+ blit(dest.fbo);
+ last = dest;
+ }
+
+ gl.blendFunc(gl.ONE, gl.ONE);
+ gl.enable(gl.BLEND);
+
+ for (let i = bloomFramebuffers.length - 2; i >= 0; i--) {
+ let baseTex = bloomFramebuffers[i];
+ gl.uniform2f(bloomBlurProgram.uniforms.texelSize, 1.0 / last.width, 1.0 / last.height);
+ gl.uniform1i(bloomBlurProgram.uniforms.uTexture, last.attach(0));
+ gl.viewport(0, 0, baseTex.width, baseTex.height);
+ blit(baseTex.fbo);
+ last = baseTex;
+ }
+
+ gl.disable(gl.BLEND);
+ bloomFinalProgram.bind();
+ gl.uniform2f(bloomFinalProgram.uniforms.texelSize, 1.0 / last.width, 1.0 / last.height);
+ gl.uniform1i(bloomFinalProgram.uniforms.uTexture, last.attach(0));
+ gl.uniform1f(bloomFinalProgram.uniforms.intensity, config.BLOOM_INTENSITY);
+ gl.viewport(0, 0, destination.width, destination.height);
+ blit(destination.fbo);
+}
+
+function splat (x, y, dx, dy, color) {
+ gl.viewport(0, 0, simWidth, simHeight);
+ splatProgram.bind();
+ gl.uniform1i(splatProgram.uniforms.uTarget, velocity.read.attach(0));
+ gl.uniform1f(splatProgram.uniforms.aspectRatio, canvas.width / canvas.height);
+ gl.uniform2f(splatProgram.uniforms.point, x / canvas.width, 1.0 - y / canvas.height);
+ gl.uniform3f(splatProgram.uniforms.color, dx, -dy, 1.0);
+ gl.uniform1f(splatProgram.uniforms.radius, config.SPLAT_RADIUS / 100.0);
+ blit(velocity.write.fbo);
+ velocity.swap();
+
+ gl.viewport(0, 0, dyeWidth, dyeHeight);
+ gl.uniform1i(splatProgram.uniforms.uTarget, density.read.attach(0));
+ gl.uniform3f(splatProgram.uniforms.color, color.r, color.g, color.b);
+ blit(density.write.fbo);
+ density.swap();
+}
+
+function multipleSplats (amount) {
+ for (let i = 0; i < amount; i++) {
+ const color = config.COLORFUL ? generateColor() : Object.assign({}, config.POINTER_COLOR.getRandom());
+ color.r *= 10.0;
+ color.g *= 10.0;
+ color.b *= 10.0;
+ const x = canvas.width * Math.random();
+ const y = canvas.height * Math.random();
+ const dx = 1000 * (Math.random() - 0.5);
+ const dy = 1000 * (Math.random() - 0.5);
+ splat(x, y, dx, dy, color);
+ }
+}
+
+function resizeCanvas () {
+ if (canvas.width != canvas.clientWidth || canvas.height != canvas.clientHeight) {
+ canvas.width = canvas.clientWidth;
+ canvas.height = canvas.clientHeight;
+ initFramebuffers();
+ }
+}
+
+canvas.addEventListener('mousemove', e => {
+ if (!config.SHOW_MOUSE_MOVEMENT) return;
+ pointers[0].moved = true;
+ pointers[0].dx = (e.offsetX - pointers[0].x) * 5.0;
+ pointers[0].dy = (e.offsetY - pointers[0].y) * 5.0;
+ pointers[0].x = e.offsetX;
+ pointers[0].y = e.offsetY;
+});
+
+canvas.addEventListener('touchmove', e => {
+ e.preventDefault();
+ const touches = e.targetTouches;
+ for (let i = 0; i < touches.length; i++) {
+ let pointer = pointers[i];
+ pointer.moved = pointer.down;
+ pointer.dx = (touches[i].pageX - pointer.x) * 8.0;
+ pointer.dy = (touches[i].pageY - pointer.y) * 8.0;
+ pointer.x = touches[i].pageX;
+ pointer.y = touches[i].pageY;
+ }
+}, false);
+
+canvas.addEventListener('mouseenter', () => {
+ pointers[0].down = true;
+ pointers[0].color = config.POINTER_COLOR.getRandom();
+});
+
+canvas.addEventListener('touchstart', e => {
+ if (!config.SPLAT_ON_CLICK) return;
+ e.preventDefault();
+ const touches = e.targetTouches;
+ for (let i = 0; i < touches.length; i++) {
+ if (i >= pointers.length)
+ pointers.push(new pointerPrototype());
+
+ pointers[i].id = touches[i].identifier;
+ pointers[i].down = true;
+ pointers[i].x = touches[i].pageX;
+ pointers[i].y = touches[i].pageY;
+ pointers[i].color = config.POINTER_COLOR.getRandom();
+ }
+});
+
+canvas.addEventListener("mousedown", () => {
+ if (!config.SPLAT_ON_CLICK) return;
+ multipleSplats(parseInt(Math.random() * 20) + 5);
+});
+
+window.addEventListener('mouseleave', () => {
+ pointers[0].down = false;
+});
+
+window.addEventListener('touchend', e => {
+ const touches = e.changedTouches;
+ for (let i = 0; i < touches.length; i++)
+ for (let j = 0; j < pointers.length; j++)
+ if (touches[i].identifier == pointers[j].id)
+ pointers[j].down = false;
+});
+
+window.addEventListener('keydown', e => {
+ if (e.code === 'KeyP')
+ config.PAUSED = !config.PAUSED;
+ if (e.key === ' ')
+ splatStack.push(parseInt(Math.random() * 20) + 5);
+});
+
+function generateColor () {
+ let c = HSVtoRGB(Math.random(), 1.0, 1.0);
+ c.r *= 0.15;
+ c.g *= 0.15;
+ c.b *= 0.15;
+ return c;
+}
+
+function HSVtoRGB (h, s, v) {
+ let r, g, b, i, f, p, q, t;
+ i = Math.floor(h * 6);
+ f = h * 6 - i;
+ p = v * (1 - s);
+ q = v * (1 - f * s);
+ t = v * (1 - (1 - f) * s);
+
+ switch (i % 6) {
+ case 0: r = v, g = t, b = p; break;
+ case 1: r = q, g = v, b = p; break;
+ case 2: r = p, g = v, b = t; break;
+ case 3: r = p, g = q, b = v; break;
+ case 4: r = t, g = p, b = v; break;
+ case 5: r = v, g = p, b = q; break;
+ }
+
+ return {
+ r,
+ g,
+ b
+ };
+}
+
+function RGBToHue(r, g, b) {
+ // Find greatest and smallest channel values
+ let cmin = Math.min(r,g,b),
+ cmax = Math.max(r,g,b),
+ delta = cmax - cmin,
+ h = 0,
+ s = 0,
+ l = 0;
+
+ // Calculate hue
+ // No difference
+ if (delta == 0)
+ h = 0;
+ // Red is max
+ else if (cmax == r)
+ h = ((g - b) / delta) % 6;
+ // Green is max
+ else if (cmax == g)
+ h = (b - r) / delta + 2;
+ // Blue is max
+ else
+ h = (r - g) / delta + 4;
+
+ h = Math.round(h * 60);
+
+ // Make negative hues positive behind 360°
+ if (h < 0)
+ h += 360;
+
+ return h;
+}
+
+function getResolution (resolution) {
+ let aspectRatio = gl.drawingBufferWidth / gl.drawingBufferHeight;
+ if (aspectRatio < 1)
+ aspectRatio = 1.0 / aspectRatio;
+
+ let max = Math.round(resolution * aspectRatio);
+ let min = Math.round(resolution);
+
+ if (gl.drawingBufferWidth > gl.drawingBufferHeight)
+ return { width: max, height: min };
+ else
+ return { width: min, height: max };
+}
+
+function getTextureScale (texture, width, height) {
+ return {
+ x: width / texture.width,
+ y: height / texture.height
+ };
+}
+
+function rgbToPointerColor(color) {
+ let c = color.split(" ");
+ // let hue = RGBToHue(c[0], c[1], c[2]);
+ // let c2 = HSVtoRGB(hue/360, 1.0, 1.0);
+ // c2.r *= 0.15;
+ // c2.g *= 0.15;
+ // c2.b *= 0.15;
+ // return c2;
+ return {
+ r: c[0] * 0.15,
+ g: c[1] * 0.15,
+ b: c[2] * 0.15
+ }
+}
diff --git a/tools/index.html b/tools/index.html
new file mode 100644
index 0000000..75a5fb4
--- /dev/null
+++ b/tools/index.html
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+
+ Whykioh Tools
+
+
+
+
+
+ Voici la page des outils référencés
+
+ Fluidsim
+
+
+
\ No newline at end of file
diff --git a/youtube/index.html b/youtube/index.html
new file mode 100644
index 0000000..8d72951
--- /dev/null
+++ b/youtube/index.html
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+ Whykioh
+
+
+
+
+
+ Youtube & Twitch !
+
+
+
+
\ No newline at end of file