diff --git a/src/index.ts b/src/index.ts index b76fb2d..ac0c43a 100644 --- a/src/index.ts +++ b/src/index.ts @@ -6,6 +6,8 @@ import { CollapsiblePanelAnimator } from './page/collapsible-panel-animator'; import { FullScreenHandler } from './page/full-screen-handler'; import { MenuHider } from './page/menu-hider'; import { setUpSettingsPage } from './page/set-up-settings-page'; +import { SettingsSlider } from './page/settings-slider'; +import { resetSettings } from './settings'; import { applyArrayPlugins } from './utils/array'; import { DeltaTimeCalculator } from './utils/delta-time-calculator'; import { ErrorHandler, Severity } from './utils/error-handler'; @@ -29,11 +31,13 @@ declare global { } } -const getElements = () => ({ +const elements = { aside: document.querySelector('aside') as HTMLDivElement, infoButton: document.querySelector('button.info') as HTMLButtonElement, infoElement: document.querySelector('.info-page') as HTMLDivElement, settingsPage: document.querySelector('.settings-page') as HTMLDivElement, + settingsContent: document.querySelector('.settings-content') as HTMLDivElement, + applyDefaults: document.querySelector('#apply-defaults') as HTMLButtonElement, minimizeFullScreenButton: document.querySelector( 'button.minimize-full-screen' ) as HTMLButtonElement, @@ -46,25 +50,23 @@ const getElements = () => ({ canvasContainer: document.querySelector('main.canvas-container') as HTMLCanvasElement, errorContainer: document.querySelector('.errors-container') as HTMLDivElement, counters: document.querySelector('.counters > pre') as HTMLPreElement, -}); +}; const main = async () => { - const elements = getElements(); - - let shouldStop = false; - let game: GameLoop | null = null; - - ErrorHandler.addOnErrorListener((error, _metadata) => { - elements.errorContainer.innerHTML += ` -
${error.message}
- `;
- game?.destroy();
- shouldStop = true;
- });
-
try {
+ let shouldStop = false;
+ let game: GameLoop | null = null;
+
applyArrayPlugins();
+ ErrorHandler.addOnErrorListener((error, _metadata) => {
+ elements.errorContainer.innerHTML += `
+ ${error.message}
+ `;
+ game?.destroy();
+ shouldStop = true;
+ });
+
const infoPageHandler = new CollapsiblePanelAnimator(
elements.infoButton,
elements.infoElement,
@@ -98,7 +100,12 @@ const main = async () => {
const deltaTimeCalculator = new DeltaTimeCalculator();
const gameRules = new GameRules(performance.now() / 1000);
- let isSettingsPageSetUp = false;
+ let sliders: Array> = [];
+
+ elements.applyDefaults.addEventListener('click', () => {
+ resetSettings();
+ sliders.forEach((slider) => slider.updateSliderValueBasedOnSource());
+ });
const updateCounters = () => {
elements.counters.innerHTML = `FPS: ${deltaTimeCalculator.fps.toFixed(2)}
@@ -110,9 +117,9 @@ next gen: ${formatNumber(game?.aliveAgentCounts.nextGenerationCount ?? 0)}`;
while (!shouldStop) {
game = new GameLoop(elements.canvas, gpu, deltaTimeCalculator, gameRules);
- if (!isSettingsPageSetUp) {
- isSettingsPageSetUp = true;
- setUpSettingsPage(elements.settingsPage, game.maxAgentCount);
+
+ if (sliders.length === 0) {
+ sliders = setUpSettingsPage(elements.settingsContent, game.maxAgentCount);
}
await game.start();
diff --git a/src/page/set-up-settings-page.ts b/src/page/set-up-settings-page.ts
index 4eb3c10..b669546 100644
--- a/src/page/set-up-settings-page.ts
+++ b/src/page/set-up-settings-page.ts
@@ -4,129 +4,123 @@ import { SettingsSlider, ValueScaling } from './settings-slider';
export const setUpSettingsPage = (
settingsPage: HTMLDivElement,
maxAgentCount: number
-) => {
+): Array> => {
const sliders = [
- [
- new SettingsSlider(settings, 'agentCount', {
- min: 1,
- max: maxAgentCount,
- scaling: ValueScaling.Logarithmic,
- rounding: Math.round,
- }),
+ new SettingsSlider(settings, 'renderSpeed', {
+ min: 1,
+ max: 10,
+ rounding: Math.round,
+ }),
- new SettingsSlider(settings, 'currentGenerationAggression', {
- min: -20,
- max: 20,
- }),
+ new SettingsSlider(settings, 'agentCount', {
+ min: 1,
+ max: maxAgentCount,
+ scaling: ValueScaling.Logarithmic,
+ rounding: Math.round,
+ }),
- new SettingsSlider(settings, 'nextGenerationAggression', {
- min: -20,
- max: 20,
- }),
+ new SettingsSlider(settings, 'currentGenerationAggression', {
+ min: -5,
+ max: 5,
+ }),
- new SettingsSlider(settings, 'moveSpeed', {
- min: 10,
- max: 500,
- scaling: ValueScaling.Quadratic,
- rounding: Math.round,
- }),
+ new SettingsSlider(settings, 'nextGenerationAggression', {
+ min: -5,
+ max: 5,
+ }),
- new SettingsSlider(settings, 'turnSpeed', {
- min: 10,
- max: 1000,
- scaling: ValueScaling.Quadratic,
- rounding: Math.round,
- }),
+ new SettingsSlider(settings, 'moveSpeed', {
+ min: 10,
+ max: 500,
+ scaling: ValueScaling.Quadratic,
+ rounding: Math.round,
+ }),
- new SettingsSlider(settings, 'sensorOffsetAngle', {
- min: 0,
- max: 90,
- step: 1,
- }),
+ new SettingsSlider(settings, 'turnSpeed', {
+ min: 1,
+ max: 200,
+ scaling: ValueScaling.Quadratic,
+ rounding: Math.round,
+ }),
- new SettingsSlider(settings, 'sensorOffsetDistance', {
- min: 0,
- max: 200,
- scaling: ValueScaling.Quadratic,
- rounding: Math.round,
- }),
+ new SettingsSlider(settings, 'sensorOffsetAngle', {
+ min: 0,
+ max: 90,
+ step: 1,
+ }),
- new SettingsSlider(settings, 'turnWhenLost', {
- min: 0,
- max: 1,
- }),
+ new SettingsSlider(settings, 'sensorOffsetDistance', {
+ min: 0,
+ max: 200,
+ scaling: ValueScaling.Quadratic,
+ rounding: Math.round,
+ }),
- new SettingsSlider(settings, 'turnWhenGoingInTheRightDirection', {
- min: 0,
- max: 1,
- }),
+ new SettingsSlider(settings, 'turnWhenLost', {
+ min: 0,
+ max: 1,
+ }),
- new SettingsSlider(settings, 'deinfectionProbability', {
- min: 0,
- max: 1,
- scaling: ValueScaling.Quadratic,
- }),
+ new SettingsSlider(settings, 'deinfectionProbability', {
+ min: 0,
+ max: 1,
+ scaling: ValueScaling.Quadratic,
+ }),
- new SettingsSlider(settings, 'brushTrailWeight', {
- min: 0,
- max: 10,
- }),
+ new SettingsSlider(settings, 'individualTrailWeight', {
+ min: 0,
+ max: 1,
+ }),
- new SettingsSlider(settings, 'individualTrailWeight', {
- min: 0,
- max: 1,
- }),
+ new SettingsSlider(settings, 'diffusionRateTrails', {
+ min: 0,
+ max: 2,
+ }),
- new SettingsSlider(settings, 'diffusionRateTrails', {
- min: 0,
- max: 10,
- }),
+ new SettingsSlider(settings, 'decayRateTrails', {
+ min: 0.1,
+ max: 1000,
+ }),
- new SettingsSlider(settings, 'decayRateTrails', {
- min: 0,
- max: 10,
- }),
+ new SettingsSlider(settings, 'diffusionRateBrush', {
+ min: 0.001,
+ max: 1,
+ }),
- new SettingsSlider(settings, 'diffusionRateBrush', {
- min: 0,
- max: 10,
- }),
+ new SettingsSlider(settings, 'decayRateBrush', {
+ min: 0.1,
+ max: 100,
+ }),
- new SettingsSlider(settings, 'decayRateBrush', {
- min: 0,
- max: 10,
- }),
+ new SettingsSlider(settings, 'spawnRadius', {
+ min: 0,
+ max: 1000,
+ }),
- new SettingsSlider(settings, 'spawnRadius', {
- min: 0,
- max: 1000,
- }),
+ new SettingsSlider(settings, 'spawnInterval', {
+ min: 0.1,
+ max: 600,
+ scaling: ValueScaling.Quadratic,
+ }),
- new SettingsSlider(settings, 'spawnInterval', {
- min: 0.1,
- max: 600,
- }),
+ new SettingsSlider(settings, 'clarity', {
+ min: 0,
+ max: 0.5,
+ }),
- new SettingsSlider(settings, 'clarity', {
- min: 0.5,
- max: 8,
- step: 0.1,
- }),
-
- new SettingsSlider(settings, 'brushSize', {
- min: 1,
- max: 60,
- }),
- ],
+ new SettingsSlider(settings, 'brushSize', {
+ min: 1,
+ max: 30,
+ }),
];
- sliders.forEach((sliderContainer) => {
- const sliderContainerElement = document.createElement('div');
+ const sliderContainerElement = document.createElement('div');
- sliderContainer.forEach((slider) => {
- sliderContainerElement.appendChild(slider.element);
- });
-
- settingsPage.querySelector('section').appendChild(sliderContainerElement);
+ sliders.forEach((slider) => {
+ sliderContainerElement.appendChild(slider.element);
});
+
+ settingsPage.appendChild(sliderContainerElement);
+
+ return sliders;
};
diff --git a/src/page/settings-slider.ts b/src/page/settings-slider.ts
index 40204eb..d7ad26b 100644
--- a/src/page/settings-slider.ts
+++ b/src/page/settings-slider.ts
@@ -97,6 +97,11 @@ export class SettingsSlider> {
);
}
+ public updateSliderValueBasedOnSource() {
+ this.slider.value = this.scaling(this.settings[this.settingName]).toString();
+ this.onChange();
+ }
+
public updateConfig(config: Partial) {
Object.assign(this.config, config);
diff --git a/src/pipelines/agents/agent-generation/agent-first-generation.wgsl b/src/pipelines/agents/agent-generation/agent-first-generation.wgsl
index a47bcd3..091297e 100644
--- a/src/pipelines/agents/agent-generation/agent-first-generation.wgsl
+++ b/src/pipelines/agents/agent-generation/agent-first-generation.wgsl
@@ -9,20 +9,26 @@ fn main(
return;
}
+ let clusterId = f32(id % 1000);
+
let random = textureSampleLevel(
noise,
noiseSampler,
vec2(f32(id % 1999) / 2000, f32(id) / 1999 / 2000),
0
);
+
+ let randomPosition = textureSampleLevel(
+ noise,
+ noiseSampler,
+ vec2(clusterId / 2000, clusterId / 2000),
+ 0
+ );
- let position = random.xy * state.size;
- let center = state.size / 2.0;
- let direction = position - center;
agents[id] = Agent(
- state.size / 2.0,
- atan2(direction.y, direction.x),
+ randomPosition.xz * state.size,
+ random.r * 3.14 * 2,
0,
);
}
diff --git a/src/pipelines/agents/agent-pipeline.ts b/src/pipelines/agents/agent-pipeline.ts
index 9a2a863..3b3d7dd 100644
--- a/src/pipelines/agents/agent-pipeline.ts
+++ b/src/pipelines/agents/agent-pipeline.ts
@@ -9,7 +9,7 @@ import { vec2 } from 'gl-matrix';
export class AgentPipeline {
private static readonly WORKGROUP_SIZE = 64;
- private static readonly UNIFORM_COUNT = 17;
+ private static readonly UNIFORM_COUNT = 16;
private readonly bindGroupLayout: GPUBindGroupLayout;
private readonly pipeline: GPUComputePipeline;
@@ -54,7 +54,6 @@ export class AgentPipeline {
isNextGenerationOdd,
center,
radius,
- turnWhenGoingInTheRightDirection,
turnWhenLost,
individualTrailWeight,
deinfectionProbability,
@@ -72,20 +71,24 @@ export class AgentPipeline {
this.uniforms,
0,
new Float32Array([
+ ...center,
+ radius,
+
brushTrailWeight,
moveSpeed,
turnSpeed,
+
(sensorOffsetAngle * Math.PI) / 180,
sensorOffsetDistance,
+
currentGenerationAggression,
nextGenerationAggression,
isNextGenerationOdd,
- ...center,
- radius,
- turnWhenGoingInTheRightDirection,
+
turnWhenLost,
individualTrailWeight,
deinfectionProbability,
+
agentCount,
])
);
diff --git a/src/pipelines/agents/agent-settings.ts b/src/pipelines/agents/agent-settings.ts
index 57d2706..c6115df 100644
--- a/src/pipelines/agents/agent-settings.ts
+++ b/src/pipelines/agents/agent-settings.ts
@@ -4,7 +4,6 @@ export interface AgentSettings {
turnSpeed: number;
sensorOffsetAngle: number;
sensorOffsetDistance: number;
- turnWhenGoingInTheRightDirection: number;
turnWhenLost: number;
individualTrailWeight: number;
deinfectionProbability: number;
diff --git a/src/pipelines/agents/agent.wgsl b/src/pipelines/agents/agent.wgsl
index ec4dc2c..bbe902d 100644
--- a/src/pipelines/agents/agent.wgsl
+++ b/src/pipelines/agents/agent.wgsl
@@ -1,4 +1,7 @@
struct Settings {
+ center: vec2,
+ radius: f32,
+
brushTrailWeight: f32,
moveRate: f32,
turnRate: f32,
@@ -10,10 +13,6 @@ struct Settings {
nextGenerationAggression: f32,
isNextGenerationOdd: f32,
- center: vec2,
- radius: f32,
-
- turnWhenGoingInTheRightDirection: f32,
turnWhenLost: f32,
individualTrailWeight: f32,
deinfectionProbability: f32,
@@ -47,10 +46,12 @@ fn main(
let random = textureSampleLevel(
noise,
noiseSampler,
- vec2(f32(id) % 23647 / 2000,
- state.time % 6294 / 2000),
+ vec2(
+ f32(id) % 23647 / 2000,
+ agent.angle / 10
+ ) + agent.position / state.size,
0
- ).a;
+ );
let isFromCurrentGeneration = abs(agent.generation - settings.isNextGenerationOdd);
let isFromOddGeneration = agent.generation == 1.0;
@@ -59,9 +60,10 @@ fn main(
let trailLeft = sense(agent.position, agent.angle, settings.sensorOffset, settings.sensorAngle);
let trailRight = sense(agent.position, agent.angle, settings.sensorOffset, -settings.sensorAngle);
- var weightForward: f32 = isFromCurrentGeneration * trailForward.a * settings.brushTrailWeight;
- var weightLeft: f32 = isFromCurrentGeneration * trailLeft.a * settings.brushTrailWeight;
- var weightRight: f32 = isFromCurrentGeneration * trailRight.a * settings.brushTrailWeight;
+ let brushWeight = isFromCurrentGeneration * settings.brushTrailWeight - (1 - isFromCurrentGeneration) * settings.brushTrailWeight;
+ var weightForward: f32 = brushWeight * trailForward.a;
+ var weightLeft: f32 = brushWeight * trailLeft.a;
+ var weightRight: f32 = brushWeight * trailRight.a;
let agression = isFromCurrentGeneration * settings.currentGenerationAggression + (1.0 - isFromCurrentGeneration) * settings.nextGenerationAggression;
if (isFromOddGeneration) {
@@ -75,32 +77,34 @@ fn main(
}
var rotation: f32 = 0;
- if weightForward > weightLeft && weightForward > weightRight {
- rotation = (random - 0.5) * settings.turnWhenGoingInTheRightDirection * settings.turnRate * state.deltaTime;
+ if weightForward >= weightLeft && weightForward >= weightRight {
+ rotation = (random.r - 0.5) * 0.0 * settings.turnRate * state.deltaTime;
} else if weightLeft < weightRight {
- rotation = -min(settings.sensorAngle, settings.turnRate * state.deltaTime);
+ rotation = -settings.turnRate * state.deltaTime;
} else if weightRight < weightLeft {
- rotation = min(settings.sensorAngle, settings.turnRate * state.deltaTime);
+ rotation = settings.turnRate * state.deltaTime;
} else {
- rotation = (random - 0.5) * settings.turnWhenLost * settings.turnRate * state.deltaTime;
+ rotation = (random.r - 0.5) * settings.turnWhenLost * settings.turnRate * state.deltaTime;
}
let direction = vec2(cos(agent.angle), sin(agent.angle));
var nextPosition = agent.position + direction * settings.moveRate * state.deltaTime;
nextPosition = clamp(nextPosition, vec2(0, 0), state.size);
if nextPosition.x == 0 || nextPosition.x == state.size.x || nextPosition.y == 0 || nextPosition.y == state.size.y {
- rotation = 3.14159265359 + random - 0.5;
+ rotation = 3.14159265359 + random.a - 0.5;
}
var trail = vec4(settings.individualTrailWeight, 0, 0, 0);
if isFromOddGeneration {
- trail = vec4(0, settings.individualTrailWeight, 0, 0);
+ trail = vec4(0, settings.individualTrailWeight, 0, 0);
}
- var trailBelow = textureLoad(trailMapIn, vec2(agent.position), 0);
+ agent.position = nextPosition;
+ agent.angle += rotation;
+ var trailBelow = textureLoad(trailMapIn, vec2(nextPosition), 0);
if settings.radius > 0 && length(settings.center - agent.position) < settings.radius {
- agents[id].generation = settings.isNextGenerationOdd;
+ agent.generation = settings.isNextGenerationOdd;
// clear trail map below so the agent won't die immediately
if (settings.isNextGenerationOdd == 1.0) {
@@ -111,25 +115,21 @@ fn main(
trailBelow.g = 0;
}
- textureStore(trailMapOut, vec2(agent.position), trailBelow);
- return;
- }
-
- let next = vec4(trail.rgb + trailBelow.rgb, trailBelow.a);
- textureStore(trailMapOut, vec2(nextPosition), next);
-
- if isFromOddGeneration {
- if next.g < next.r && (isFromCurrentGeneration == 1.0 || (isFromCurrentGeneration == 0.0 && random < settings.deinfectionProbability)) {
- agent.generation = 0;
- }
+ textureStore(trailMapOut, vec2(nextPosition), trailBelow);
} else {
- if next.r < next.g && (isFromCurrentGeneration == 1.0 || (isFromCurrentGeneration == 0.0 && random < settings.deinfectionProbability)) {
- agent.generation = 1;
+ textureStore(trailMapOut, vec2(nextPosition), trail + trailBelow);
+
+ if isFromOddGeneration {
+ if trailBelow.g < trailBelow.r && (isFromCurrentGeneration == 1.0 || (isFromCurrentGeneration == 0.0 && random.a < settings.deinfectionProbability)) {
+ agent.generation = 0;
+ }
+ } else {
+ if trailBelow.r < trailBelow.g && (isFromCurrentGeneration == 1.0 || (isFromCurrentGeneration == 0.0 && random.a < settings.deinfectionProbability)) {
+ agent.generation = 1;
+ }
}
}
- agent.position = nextPosition;
- agent.angle += rotation;
agents[id] = agent;
}
diff --git a/src/pipelines/diffusion/diffuse.wgsl b/src/pipelines/diffusion/diffuse.wgsl
index 94734b9..f0dc3df 100644
--- a/src/pipelines/diffusion/diffuse.wgsl
+++ b/src/pipelines/diffusion/diffuse.wgsl
@@ -1,40 +1,48 @@
struct Settings {
- diffusionRateTrails: f32,
+ inverseDiffusionRateTrails: f32,
decayRateTrails: f32,
- diffusionRateBrush: f32,
+ inverseDiffusionRateBrush: f32,
decayRateBrush: f32,
};
+
@group(1) @binding(0) var settings: Settings;
@group(1) @binding(1) var Sampler: sampler;
@group(1) @binding(2) var trailMap: texture_2d;
+
@fragment
fn fragment(@location(0) uv: vec2) -> @location(0) vec4 {
var current = textureSample(trailMap, Sampler, uv);
- var change = vec4(0);
- for (var x: i32 = -1; x <= 1; x++) {
- for (var y: i32 = -1; y <= 1; y++) {
- if (x != 0 || y != 0) {
- let offset = vec2(f32(x), f32(y));
- let neighbour = textureSample(trailMap, Sampler, uv + offset / state.size);
- let random = textureSample(noise, noiseSampler, uv + offset / state.size * 0.5).r;
-
- let difference = clamp(neighbour - current, vec4(0), vec4(1));
- change += vec4(
- length(neighbour.rgb) * pow(random, settings.diffusionRateTrails) * difference.rgb,
- min(1.0, length(neighbour.a)) * pow(random, settings.diffusionRateBrush) * difference.a
- );
- }
- }
- }
- current += change / 4;
+ current += (
+ propagate(uv, vec2(-1.0, -1.0), current)
+ + propagate(uv, vec2(-1.0, 1.0), current)
+ + propagate(uv, vec2(1.0, -1.0), current)
+ + propagate(uv, vec2(1.0, 1.0), current)
+
+ + propagate(uv, vec2(-1.0, 0.0), current)
+ + propagate(uv, vec2(0.0, -1.0), current)
+ + propagate(uv, vec2(1.0, 0.0), current)
+ + propagate(uv, vec2(0.0, 1.0), current)
+ ) / 8;
let decayed = clamp(vec4(
- current.rgb * settings.decayRateTrails,
- max(0, current.a - settings.decayRateBrush)
+ current.rgb - settings.decayRateTrails,
+ max(0, current.a + (current.a - 1.001) * settings.decayRateBrush)
), vec4(0), vec4(1));
return decayed;
}
+
+
+fn propagate(uv: vec2, offset: vec2, currentColor: vec4) -> vec4 {
+ let neighbour = textureSample(trailMap, Sampler, uv + offset / state.size);
+ var random = textureSample(noise, noiseSampler, uv + offset / state.size * 0.5).r;
+ let difference = clamp(neighbour - currentColor, vec4(0), vec4(1));
+
+ return vec4(
+ vec3(length(neighbour.rgb) * pow(random, settings.inverseDiffusionRateTrails)),
+ length(neighbour.a) * pow(random, settings.inverseDiffusionRateBrush)
+ ) * difference;
+}
diff --git a/src/pipelines/diffusion/diffusion-pipeline.ts b/src/pipelines/diffusion/diffusion-pipeline.ts
index e3685d7..15f2fda 100644
--- a/src/pipelines/diffusion/diffusion-pipeline.ts
+++ b/src/pipelines/diffusion/diffusion-pipeline.ts
@@ -62,10 +62,10 @@ export class DiffusionPipeline {
this.uniforms,
0,
new Float32Array([
- diffusionRateTrails,
- decayRateTrails,
- diffusionRateBrush,
- decayRateBrush,
+ 1 / diffusionRateTrails,
+ decayRateTrails / 1000,
+ 1 / diffusionRateBrush,
+ decayRateBrush / 1000,
])
);
}
diff --git a/src/pipelines/render/render.wgsl b/src/pipelines/render/render.wgsl
index 28c5105..ba2b7d0 100644
--- a/src/pipelines/render/render.wgsl
+++ b/src/pipelines/render/render.wgsl
@@ -16,8 +16,8 @@ fn fragment(@location(0) uv: vec2) -> @location(0) vec4 {
let backgroundColor = vec3(0.9) + 0.075 * random.r;
- let evenGenerationStrength = pow(traces.r, settings.clarity);
- let oddGenerationStrength = pow(traces.g, settings.clarity);
+ let evenGenerationStrength = clarity(traces.r);
+ let oddGenerationStrength = clarity(traces.g);
let brushStrength = traces.a;
let color = max(
@@ -32,3 +32,7 @@ fn fragment(@location(0) uv: vec2) -> @location(0) vec4 {
return vec4(mix(backgroundColor, color, strength), 1);
}
+
+fn clarity(strength: f32) -> f32 {
+ return pow(strength, 5) - strength * settings.clarity + sign(strength) * settings.clarity;
+}
diff --git a/src/settings.ts b/src/settings.ts
index 027822a..1a34208 100644
--- a/src/settings.ts
+++ b/src/settings.ts
@@ -5,37 +5,38 @@ import { DiffusionSettings } from './pipelines/diffusion/diffusion-settings';
import { RenderSettings } from './pipelines/render/render-settings';
import { persist } from './utils/persist';
-export const settings: { [key: string]: number } & GameLoopSettings &
+const initialValues: GameLoopSettings &
AgentSettings &
BrushSettings &
DiffusionSettings &
- RenderSettings = persist({
- agentCount: 1_000_000,
+ RenderSettings = {
+ agentCount: 1_001_500,
- currentGenerationAggression: 0.1,
- nextGenerationAggression: 10,
+ currentGenerationAggression: -5,
+ nextGenerationAggression: 0.5,
- moveSpeed: 70,
- turnSpeed: 345,
- sensorOffsetAngle: 32,
- sensorOffsetDistance: 23,
- turnWhenGoingInTheRightDirection: 0,
- turnWhenLost: 0.2,
- deinfectionProbability: 0.001,
+ moveSpeed: 90,
+ turnSpeed: 78,
+ sensorOffsetAngle: 41,
+ sensorOffsetDistance: 45,
+ turnWhenLost: 0.43,
+ deinfectionProbability: 1,
- brushTrailWeight: 5,
- individualTrailWeight: 0.5,
- diffusionRateTrails: 2, // inverse
- decayRateTrails: 0.9, // inverse
- diffusionRateBrush: 4, // inverse
- decayRateBrush: 0.003,
+ brushTrailWeight: 500,
+ individualTrailWeight: 0.2,
- spawnRadius: 5,
- spawnInterval: 600,
+ diffusionRateTrails: 0.29,
+ decayRateTrails: 21.95,
+ diffusionRateBrush: 0.25,
+ decayRateBrush: 15,
- clarity: 2,
+ spawnRadius: 8,
+ spawnInterval: 8,
+
+ clarity: 0,
brushSize: 12,
- brushSizeVariation: 0.5,
+
+ brushSizeVariation: 0.5, // hidden on the UI
startColorHue: 200,
@@ -44,4 +45,14 @@ export const settings: { [key: string]: number } & GameLoopSettings &
// debug options
renderSpeed: 1,
simulatedDelayMs: 0,
-});
+};
+
+export const settings: { [key: string]: number } & GameLoopSettings &
+ AgentSettings &
+ BrushSettings &
+ DiffusionSettings &
+ RenderSettings = persist({ ...initialValues });
+
+export const resetSettings = () => {
+ Object.assign(settings, initialValues);
+};
diff --git a/src/utils/format-number.ts b/src/utils/format-number.ts
index 8375628..a57812e 100644
--- a/src/utils/format-number.ts
+++ b/src/utils/format-number.ts
@@ -7,5 +7,5 @@ export const formatNumber = (value: number, unit = ''): string => {
return `${(value / 1e3).toFixed(1)} thousand ${unit}`;
}
- return `${value === Math.floor(value) ? value : value.toFixed(2)}${unit}`;
+ return `${value === Math.floor(value) ? value : value.toFixed(2)} ${unit}`;
};
diff --git a/src/utils/graphics/full-screen-quad.ts b/src/utils/graphics/full-screen-quad.ts
index 3790f75..3e9dbec 100644
--- a/src/utils/graphics/full-screen-quad.ts
+++ b/src/utils/graphics/full-screen-quad.ts
@@ -1,4 +1,3 @@
-import shader from './full-screen-quad.wgsl';
import { smartCompile } from './smart-compile';
export const setUpFullScreenQuad = (
diff --git a/src/utils/graphics/noise.ts b/src/utils/graphics/noise.ts
index 004de5b..920ddeb 100644
--- a/src/utils/graphics/noise.ts
+++ b/src/utils/graphics/noise.ts
@@ -5,12 +5,12 @@ const textureCache = new Map();
export const generateNoise = ({
device,
- width = 1024,
- height = 1024,
+ width,
+ height,
}: {
device: GPUDevice;
- width?: number;
- height?: number;
+ width: number;
+ height: number;
}): GPUTextureView => {
const cacheKey = `${width}x${height}`;
if (!textureCache.has(cacheKey)) {
@@ -31,10 +31,10 @@ export const generateNoise = ({
@fragment
fn fragment(@location(0) uv: vec2) -> @location(0) vec4 {
return vec4(
+ random_with_seed(uv, 0),
random_with_seed(uv, 1),
random_with_seed(uv, 2),
random_with_seed(uv, 3),
- random_with_seed(uv, 4),
);
}`
),
diff --git a/src/utils/graphics/resizable-texture.ts b/src/utils/graphics/resizable-texture.ts
index 4fe25c7..58aa531 100644
--- a/src/utils/graphics/resizable-texture.ts
+++ b/src/utils/graphics/resizable-texture.ts
@@ -20,12 +20,9 @@ export class ResizableTexture {
const newTexture = this.device.createTexture({
format: 'rgba16float',
- dimension: '2d',
- mipLevelCount: 1,
size: {
width: size.x,
height: size.y,
- depthOrArrayLayers: 1,
},
usage:
GPUTextureUsage.STORAGE_BINDING |