diff --git a/.gitignore b/.gitignore index d0650cc..ced6d86 100644 --- a/.gitignore +++ b/.gitignore @@ -16,3 +16,4 @@ mono_crash.*.json .DS_Store screenshots/.DS_Store *.tmp +*.json diff --git a/ComputeShaderStudio2D.gd b/ComputeShaderStudio2D.gd new file mode 100644 index 0000000..e08925d --- /dev/null +++ b/ComputeShaderStudio2D.gd @@ -0,0 +1,11 @@ +extends Node + + +# Called when the node enters the scene tree for the first time. +func _ready(): + pass # Replace with function body. + + +# Called every frame. 'delta' is the elapsed time since the previous frame. +func _process(delta): + pass diff --git a/examples/cells/example_cells.tscn b/examples/cells/example_cells.tscn index 6d35843..6959c31 100644 --- a/examples/cells/example_cells.tscn +++ b/examples/cells/example_cells.tscn @@ -1,6 +1,6 @@ [gd_scene load_steps=3 format=3 uid="uid://cutxgtalwsp6q"] -[ext_resource type="Script" uid="uid://bs2f5sxok3d3i" path="res://examples/cells/cells.gd" id="1_dw4h8"] +[ext_resource type="Script" path="res://examples/cells/cells.gd" id="1_dw4h8"] [ext_resource type="Texture2D" uid="uid://demftcowdd5c6" path="res://examples/icon.svg" id="2_oji6h"] [node name="Cells" type="Control" node_paths=PackedStringArray("data")] diff --git a/examples/circles/circles.tscn b/examples/circles/circles.tscn index c66e724..1916a5c 100644 --- a/examples/circles/circles.tscn +++ b/examples/circles/circles.tscn @@ -1,6 +1,6 @@ [gd_scene load_steps=3 format=3 uid="uid://c1ux4sgouixua"] -[ext_resource type="Script" uid="uid://c8esqdv0y26yp" path="res://addons/compute_shader_studio/compute_shader_studio_2d.gd" id="1_amro2"] +[ext_resource type="Script" path="res://addons/compute_shader_studio/compute_shader_studio_2d.gd" id="1_amro2"] [ext_resource type="Texture2D" uid="uid://demftcowdd5c6" path="res://examples/icon.svg" id="2_ntl1q"] [node name="Circles" type="Node2D"] diff --git a/examples/example_1.tscn b/examples/example_1.tscn index 88ce9e3..7d67949 100644 --- a/examples/example_1.tscn +++ b/examples/example_1.tscn @@ -1,6 +1,6 @@ [gd_scene load_steps=5 format=3 uid="uid://ddr6qtwy1pesd"] -[ext_resource type="Script" uid="uid://c8esqdv0y26yp" path="res://addons/compute_shader_studio/compute_shader_studio_2d.gd" id="1_6846p"] +[ext_resource type="Script" path="res://addons/compute_shader_studio/compute_shader_studio_2d.gd" id="1_6846p"] [ext_resource type="Texture2D" uid="uid://demftcowdd5c6" path="res://examples/icon.svg" id="2_s3fct"] [sub_resource type="FastNoiseLite" id="FastNoiseLite_dmk8h"] diff --git a/examples/interactive_waves/interactive_waves.tscn b/examples/interactive_waves/interactive_waves.tscn new file mode 100644 index 0000000..30e16d2 --- /dev/null +++ b/examples/interactive_waves/interactive_waves.tscn @@ -0,0 +1,339 @@ +#define MAX_CELLS 48 +#define CELL_GROWTH_RATE 0.015 +#define DIVISION_THRESHOLD 25.0 +#define MIN_RADIUS 8.0 +#define MAX_AGE 200.0 + +void main() +{ + int x = int(gl_GlobalInvocationID.x); + int y = int(gl_GlobalInvocationID.y); + int p = x + y * int(WSX); + + // At first step, initialize background and the first cell + if (step == 0) { + // Create dark blue gradient background + float gradientY = float(y) / float(WSY); + int blue = int(20 + gradientY * 40); + data_0[p] = 0xFF000000 | blue; + + // Initialize the first cell at the center + if (x == 0 && y == 0) { + // First cell in the center + data_0[0] = floatBitsToInt(WSX / 2.0); // x position + data_0[1] = floatBitsToInt(WSY / 2.0); // y position + data_0[2] = floatBitsToInt(MIN_RADIUS * 1.5);// radius + data_0[3] = floatBitsToInt(0.0); // age + data_0[4] = 1; // isAlive + data_0[5] = 0; // dividing + data_0[6] = floatBitsToInt(1.0); // health (new property) + data_0[7] = floatBitsToInt(0.0); // dx + data_0[8] = floatBitsToInt(0.0); // dy + data_0[9] = 0; // cell type (0=normal) + + // Initialize other cells as inactive + for (int i = 1; i < MAX_CELLS; i++) { + int baseIdx = i * 10; + data_0[baseIdx] = 0; // not alive + } + } + } else { + // Process cell simulation logic - only in one thread to avoid race conditions + if (x == 0 && y == 0) { + // Update cell states + for (int i = 0; i < MAX_CELLS; i++) { + int baseIdx = i * 10; + int isAlive = (i == 0) ? data_0[4] : data_0[baseIdx]; + + if (isAlive == 1) { + // Get cell properties + float cellX = (i == 0) ? intBitsToFloat(data_0[0]) : intBitsToFloat(data_0[baseIdx + 1]); + float cellY = (i == 0) ? intBitsToFloat(data_0[1]) : intBitsToFloat(data_0[baseIdx + 2]); + float radius = (i == 0) ? intBitsToFloat(data_0[2]) : intBitsToFloat(data_0[baseIdx + 3]); + float age = (i == 0) ? intBitsToFloat(data_0[3]) : intBitsToFloat(data_0[baseIdx + 4]); + int dividing = (i == 0) ? data_0[5] : data_0[baseIdx + 5]; + float health = (i == 0) ? intBitsToFloat(data_0[6]) : intBitsToFloat(data_0[baseIdx + 6]); + float dx = (i == 0) ? intBitsToFloat(data_0[7]) : intBitsToFloat(data_0[baseIdx + 7]); + float dy = (i == 0) ? intBitsToFloat(data_0[8]) : intBitsToFloat(data_0[baseIdx + 8]); + int cellType = (i == 0) ? data_0[9] : data_0[baseIdx + 9]; + + // Apply random movement based on cell type + float moveScale = (cellType == 0) ? 0.2 : 0.4; // Type 1 cells move faster + + // Create more organic movement with noise + float moveX = sin(step * 0.03 + i * 2.1) * moveScale; + float moveY = cos(step * 0.04 + i * 1.7) * moveScale; + + // Add influence from mouse position + float mouseDx = mousex - cellX; + float mouseDy = mousey - cellY; + float mouseDist = sqrt(mouseDx*mouseDx + mouseDy*mouseDy); + + if (mouseDist < 100.0) { + // Add slight attraction to mouse for type 0, repulsion for type 1 + float mouseInfluence = 0.05 / (mouseDist + 10.0); + if (cellType == 0) { + moveX += mouseInfluence * mouseDx; + moveY += mouseInfluence * mouseDy; + } else { + moveX -= mouseInfluence * mouseDx; + moveY -= mouseInfluence * mouseDy; + } + } + + // Update position with momentum + dx = dx * 0.9 + moveX; + dy = dy * 0.9 + moveY; + cellX += dx; + cellY += dy; + + // Grow cells if not dividing + if (dividing == 0) { + // Different cell types grow at different rates + float growthRate = (cellType == 0) ? CELL_GROWTH_RATE : CELL_GROWTH_RATE * 1.2; + radius += growthRate * health; + age += 0.1; + + // Cells lose health as they age + health = max(0.5, health - 0.0005); + } + + // Check for division + if (radius > DIVISION_THRESHOLD && dividing == 0 && health > 0.6) { + dividing = 1; + + // Calculate division direction with some randomness + float angle = step * 0.01 + i * 0.5 + sin(age * 0.1) * 0.3; + data_0[baseIdx + 7] = floatBitsToInt(cos(angle)); // dx for division + data_0[baseIdx + 8] = floatBitsToInt(sin(angle)); // dy for division + } + + // Process cell division + if (dividing == 1) { + // Shrink dividing cell + radius -= CELL_GROWTH_RATE * 0.7; + + // Find inactive cell to create new one + for (int j = 1; j < MAX_CELLS; j++) { + if (data_0[j * 10] == 0) { // Find inactive cell + // Get division direction + float divDx = intBitsToFloat(data_0[baseIdx + 7]); + float divDy = intBitsToFloat(data_0[baseIdx + 8]); + + // Create new cell + int newIdx = j * 10; + data_0[newIdx] = 1; // alive + data_0[newIdx + 1] = floatBitsToInt(cellX + divDx * radius * 0.8); + data_0[newIdx + 2] = floatBitsToInt(cellY + divDy * radius * 0.8); + data_0[newIdx + 3] = floatBitsToInt(radius * 0.9); + data_0[newIdx + 4] = floatBitsToInt(0.0); // new age + data_0[newIdx + 5] = 0; // not dividing + + // Cell mutation: 15% chance to change cell type during division + int newCellType = cellType; + if (sin(step * 0.1 + j * 0.3) > 0.7) { + newCellType = (cellType == 0) ? 1 : 0; + } + data_0[newIdx + 9] = newCellType; + + // New cell health is based on parent with some randomness + float newHealth = health * 0.9 + 0.1 * sin(step + j); + data_0[newIdx + 6] = floatBitsToInt(clamp(newHealth, 0.5, 1.0)); + + // Give new cell momentum in division direction + data_0[newIdx + 7] = floatBitsToInt(divDx * 0.5); + data_0[newIdx + 8] = floatBitsToInt(divDy * 0.5); + + // Move original cell in opposite direction + cellX -= divDx * radius * 0.5; + cellY -= divDy * radius * 0.5; + + // Original cell gets momentum in opposite direction + dx -= divDx * 0.5; + dy -= divDy * 0.5; + + // Reset division state + dividing = 0; + health *= 0.9; // Division costs energy + break; + } + } + } + + // Keep cells within bounds + cellX = clamp(cellX, radius, float(WSX) - radius); + cellY = clamp(cellY, radius, float(WSY) - radius); + + // Cell interaction - simplified to avoid O(n²) complexity + if (i % 3 == step % 3) { // Only process 1/3 of cells each frame + for (int j = 0; j < MAX_CELLS; j++) { + if (i != j && (j == 0 ? data_0[4] : data_0[j * 10]) == 1) { + float otherX = (j == 0) ? intBitsToFloat(data_0[0]) : intBitsToFloat(data_0[j * 10 + 1]); + float otherY = (j == 0) ? intBitsToFloat(data_0[1]) : intBitsToFloat(data_0[j * 10 + 2]); + float otherRadius = (j == 0) ? intBitsToFloat(data_0[2]) : intBitsToFloat(data_0[j * 10 + 3]); + int otherType = (j == 0) ? data_0[9] : data_0[j * 10 + 9]; + + float distX = cellX - otherX; + float distY = cellY - otherY; + float distance = sqrt(distX*distX + distY*distY); + float minDist = radius + otherRadius; + + // Simple collision response + if (distance < minDist && distance > 0.1) { + float overlap = (minDist - distance) / distance; + + // Different response based on cell types + if (cellType == otherType) { + // Same type cells gently push each other + cellX += distX * overlap * 0.2; + cellY += distY * overlap * 0.2; + } else { + // Different type cells compete - stronger push + cellX += distX * overlap * 0.3; + cellY += distY * overlap * 0.3; + + // Cells lose health when in contact with opposite type + health -= 0.001; + } + } + } + } + } + + // Cell death conditions + if ((age > MAX_AGE) || (health < 0.2)) { + if (i > 0) { // Don't let the first cell die + isAlive = 0; + } else { + // Rejuvenate the first cell to keep simulation going + age = MAX_AGE * 0.5; + health = 0.8; + } + } + + // Store updated values + if (i == 0) { + data_0[0] = floatBitsToInt(cellX); + data_0[1] = floatBitsToInt(cellY); + data_0[2] = floatBitsToInt(radius); + data_0[3] = floatBitsToInt(age); + data_0[4] = isAlive; + data_0[5] = dividing; + data_0[6] = floatBitsToInt(health); + data_0[7] = floatBitsToInt(dx); + data_0[8] = floatBitsToInt(dy); + } else { + data_0[baseIdx] = isAlive; + data_0[baseIdx + 1] = floatBitsToInt(cellX); + data_0[baseIdx + 2] = floatBitsToInt(cellY); + data_0[baseIdx + 3] = floatBitsToInt(radius); + data_0[baseIdx + 4] = floatBitsToInt(age); + data_0[baseIdx + 5] = dividing; + data_0[baseIdx + 6] = floatBitsToInt(health); + data_0[baseIdx + 7] = floatBitsToInt(dx); + data_0[baseIdx + 8] = floatBitsToInt(dy); + } + } + } + } + + // Fade background slightly + int bg_color = data_0[p]; + int blue = bg_color & 0xFF; + if (blue > 20) { + blue--; + data_0[p] = (bg_color & 0xFFFFFF00) | blue; + } + + // Visualization - draw all cells + bool pixel_drawn = false; + for (int i = 0; i < MAX_CELLS; i++) { + int baseIdx = i * 10; + int isAlive = (i == 0) ? data_0[4] : data_0[baseIdx]; + + if (isAlive == 1) { + float cellX = (i == 0) ? intBitsToFloat(data_0[0]) : intBitsToFloat(data_0[baseIdx + 1]); + float cellY = (i == 0) ? intBitsToFloat(data_0[1]) : intBitsToFloat(data_0[baseIdx + 2]); + float radius = (i == 0) ? intBitsToFloat(data_0[2]) : intBitsToFloat(data_0[baseIdx + 3]); + float age = (i == 0) ? intBitsToFloat(data_0[3]) : intBitsToFloat(data_0[baseIdx + 4]); + int dividing = (i == 0) ? data_0[5] : data_0[baseIdx + 5]; + float health = (i == 0) ? intBitsToFloat(data_0[6]) : intBitsToFloat(data_0[baseIdx + 6]); + int cellType = (i == 0) ? data_0[9] : data_0[baseIdx + 9]; + + float dx = float(x - cellX); + float dy = float(y - cellY); + float dist = sqrt(dx*dx + dy*dy); + + // Draw cell interior + if (dist < radius) { + // Age affects color - younger cells are brighter + float age_factor = clamp(1.0 - age / MAX_AGE, 0.2, 1.0); + // Health affects intensity + float health_factor = clamp(health, 0.3, 1.0); + + int r, g, b; + + // Cell type determines base color + if (cellType == 0) { + // Type 0: Cyan/Teal cells + r = int(50.0 * age_factor * health_factor); + g = int(200.0 * age_factor * health_factor); + b = int(180.0 * age_factor * health_factor); + } else { + // Type 1: Magenta/Purple cells + r = int(180.0 * age_factor * health_factor); + g = int(50.0 * age_factor * health_factor); + b = int(220.0 * age_factor * health_factor); + } + + // Dividing cells pulse + if (dividing == 1) { + float pulse = 0.7 + 0.3 * sin(step * 0.2); + r = int(float(r) * pulse); + g = int(float(g) * pulse); + b = int(float(b) * pulse); + } + + // Create a gradient from center to edge + float gradient = pow(1.0 - (dist / radius), 0.5); + r = int(float(r) * gradient); + g = int(float(g) * gradient); + b = int(float(b) * gradient); + + data_0[p] = 0xFF000000 | (r << 16) | (g << 8) | b; + pixel_drawn = true; + } + // Draw cell membrane + else if (dist < radius + 1.5 && dist > radius - 0.5 && !pixel_drawn) { + // Membrane color - white with health tint + int memR = 255; + int memG = 255; + int memB = 255; + + if (cellType == 0) { + // Type 0: slight cyan tint + memR = 200; + memG = 255; + memB = 255; + } else { + // Type 1: slight magenta tint + memR = 255; + memG = 200; + memB = 255; + } + + // Dividing cells have brighter membrane + if (dividing == 1) { + float pulse = 0.7 + 0.3 * sin(step * 0.3); + memR = min(255, int(float(memR) * pulse)); + memG = min(255, int(float(memG) * pulse)); + memB = min(255, int(float(memB) * pulse)); + } + + data_0[p] = 0xFF000000 | (memR << 16) | (memG << 8) | memB; + pixel_drawn = true; + } + } + } + } +} \ No newline at end of file diff --git a/examples/light_source/light_source.cpp b/examples/light_source/light_source.cpp new file mode 100644 index 0000000..ae9fe5a --- /dev/null +++ b/examples/light_source/light_source.cpp @@ -0,0 +1,123 @@ +// Enhanced artistic shader - Prismatic Flow +// Inspired by Danilo Guanabara with artistic extensions + + + +//AUTHORS: NASSIM & YASSER +void main() +{ + int x = int(gl_GlobalInvocationID.x); + int y = int(gl_GlobalInvocationID.y); + int idx = x + y * int(WSX); + // Normalized coordinates + vec2 fragCoord = vec2(float(x), float(y)); + vec2 resolution = vec2(float(WSX), float(WSY)); + float time = float(step) * 0.03; + + // Mouse interaction + vec2 mouse = vec2(float(mousex), float(mousey)) / resolution; + + // Calculate artistic effect + vec3 c = vec3(0.0); + float l = 0.0; + float z = time; + + // Create multiple artistic layers with different parameters + for (int i = 0; i < 3; i++) + { + vec2 uv, p; + + // Normalized coordinates (0 to 1) + p = fragCoord / resolution; + uv = p; + + // Center coordinates (-0.5 to 0.5) + p -= 0.5; + + // Fix aspect ratio + p.x *= resolution.x / resolution.y; + + // Increment z for each color channel with variation + z += 0.07 + sin(time * 0.2) * 0.02; + + // Calculate length for distortion + l = length(p); + + // Apply artistic distortion - enhanced with variations + float distortionStrength = (sin(z) + 1.0) * (1.0 + 0.3 * sin(time * 0.3)); + float wavePattern = abs(sin(l * (9.0 + sin(time * 0.2) * 3.0) - z * 2.0)); + + // Add mouse influence to distortion + float mouseDist = length(p - (mouse - 0.5) * vec2(resolution.x / resolution.y, 1.0)); + float mouseInfluence = 0.0; + if (mouseDist < 0.5) + { + mouseInfluence = (0.5 - mouseDist) * 2.0 * sin(time * 2.0); + } + + // Apply enhanced distortion + uv += p / max(0.001, l) * (distortionStrength + mouseInfluence) * wavePattern; + + // Add rotational flow + float angle = atan(p.y, p.x); + uv += (5.0f*mousex/float(WSX)) * vec2(cos(angle + time), sin(angle + time)) * sin(time + l * 5.0); + + // Calculate lightular-like pattern with enhanced effects + vec2 lightCoord = mod(uv, 1.0) - 0.5; + + // Create multiple lightular layers + float lightPattern = 0.01 / length(lightCoord); + + // Add fine details to lights + lightPattern += 0.005 / length(mod(uv * 2.0, 1.0) - 0.5) * (0.5 + 0.5 * sin(l * 20.0 + time)); + + // Store color with channel-specific effects + if (i == 0) + { + // Red channel - warmer pattern + c[i] = lightPattern * (0.9 + 0.3 * sin(time + l * 3.0)); + } + else if (i == 1) + { + // Green channel - phase shifted + c[i] = lightPattern * (0.8 + 0.3 * sin(time * 1.1 + l * 3.5)); + } + else + { + // Blue channel - different phase + c[i] = lightPattern * (0.7 + 0.4 * sin(time * 0.9 + l * 4.0)); + } + } + + // Enhance color blending + float intensity = max(l, 0.001) * (1.0 + 0.2 * sin(time)); + vec3 rawColor = c / intensity; + + // Color grading - add artistic color palette + vec3 finalColor; + + // Apply color temperature shift based on position + float temp = sin(fragCoord.x / resolution.x * 3.14159 + time * 0.5) * 0.5 + 0.5; + + // Create a vibrant color palette + finalColor.r = rawColor.r * (1.0 + 0.3 * temp); + finalColor.g = rawColor.g * (1.0 + 0.2 * (1.0 - temp)); + finalColor.b = rawColor.b * (1.0 + 0.3 * sin(temp * 3.14159)); + // Add subtle vignette effect + vec2 vignettePos = fragCoord / resolution - 0.5; + float vignette = 1.0 - dot(vignettePos, vignettePos) * 0.8; + finalColor *= vignette; + + // Add subtle pulsing + finalColor *= 0.8 + (mousey/float(WSY)) * sin(time * 0.5); + + // Enhance contrast + finalColor = pow(finalColor, vec3(0.9, 0.9, 0.9)); + + // Convert to 8-bit color + int r = int(clamp(finalColor.r * 255.0, 0.0, 255.0)); + int g = int(clamp(finalColor.g * 255.0, 0.0, 255.0)); + int b = int(clamp(finalColor.b * 255.0, 0.0, 255.0)); + // Store the final color + data_0[idx] = 0xFF000000 | (r << 16) | (g << 8) | b; +} \ No newline at end of file diff --git a/examples/light_source/light_source.tscn b/examples/light_source/light_source.tscn new file mode 100644 index 0000000..e0573cb --- /dev/null +++ b/examples/light_source/light_source.tscn @@ -0,0 +1,26 @@ +[gd_scene load_steps=3 format=3 uid="uid://dy6y2fqmvn42e"] + +[ext_resource type="Script" uid="uid://c8esqdv0y26yp" path="res://addons/compute_shader_studio/compute_shader_studio_2d.gd" id="1_bkr6h"] +[ext_resource type="Texture2D" uid="uid://demftcowdd5c6" path="res://examples/icon.svg" id="2_lvs8q"] + +[node name="LightSource" type="Node2D"] + +[node name="ComputeShaderStudio2D" type="Node" parent="." node_paths=PackedStringArray("data")] +script = ExtResource("1_bkr6h") +WSX = 800 +WSY = 600 +glsl_file = "res://examples/light_source/light_source.cpp" +data = [NodePath("../LightDisplay")] +metadata/_custom_type_script = "uid://c8esqdv0y26yp" + +[node name="LightDisplay" type="Sprite2D" parent="."] +position = Vector2(571, 324) +scale = Vector2(8.92188, 5.09375) +texture = ExtResource("2_lvs8q") + +[node name="InfoLabel" type="Label" parent="."] +offset_left = 14.0 +offset_top = 552.0 +offset_right = 415.0 +offset_bottom = 601.0 +text = "LIGHT SOURCE"