Interaction improvements and minor fixes
This commit is contained in:
parent
fb772db717
commit
27c835b8e5
16 changed files with 262 additions and 95 deletions
|
|
@ -25,7 +25,7 @@
|
|||
"scripts": {
|
||||
"build": "webpack --config ./webpack.config.js --mode=production",
|
||||
"dev": "webpack serve --config ./webpack.config.js --mode=development",
|
||||
"gh-pages": "push-dir --dir=build --branch=gh-pages"
|
||||
"gh-pages": "npm run build && push-dir --dir=build --branch=gh-pages"
|
||||
},
|
||||
"author": "",
|
||||
"license": "MIT"
|
||||
|
|
|
|||
14
src/App.ts
14
src/App.ts
|
|
@ -6,6 +6,7 @@ import AntsDiscretizeScene from "./scenes/AntsDiscretizeScene";
|
|||
import WorldBlurScene from "./scenes/WorldBlurScene";
|
||||
import Config from "./Config";
|
||||
import GUI from "./GUI";
|
||||
import DrawScene from "./scenes/DrawScene";
|
||||
|
||||
export interface SceneCollection {
|
||||
ants: AntsComputeScene;
|
||||
|
|
@ -13,6 +14,7 @@ export interface SceneCollection {
|
|||
worldBlur: WorldBlurScene;
|
||||
discretize: AntsDiscretizeScene;
|
||||
screen: ScreenScene;
|
||||
draw: DrawScene;
|
||||
}
|
||||
|
||||
export default new class App {
|
||||
|
|
@ -22,6 +24,7 @@ export default new class App {
|
|||
private renderLoop = (deltaTime: number): void => this.render(deltaTime);
|
||||
private simInterval: NodeJS.Timer;
|
||||
private simulationStepsPerSecond: number = 0;
|
||||
private simStarted: boolean = false;
|
||||
|
||||
constructor() {
|
||||
this.initScenes();
|
||||
|
|
@ -47,6 +50,8 @@ export default new class App {
|
|||
}
|
||||
|
||||
this.simulationStep();
|
||||
|
||||
this.simStarted = true;
|
||||
}, 1000 / this.simulationStepsPerSecond);
|
||||
}
|
||||
|
||||
|
|
@ -57,12 +62,13 @@ export default new class App {
|
|||
worldBlur: new WorldBlurScene(this.renderer),
|
||||
discretize: new AntsDiscretizeScene(this.renderer),
|
||||
screen: new ScreenScene(this.renderer),
|
||||
draw: new DrawScene(this.renderer)
|
||||
};
|
||||
}
|
||||
|
||||
private resize() {
|
||||
const width = window.innerWidth;
|
||||
const height = window.innerHeight;
|
||||
const width = window.innerWidth * window.devicePixelRatio;
|
||||
const height = window.innerHeight * window.devicePixelRatio;
|
||||
|
||||
this.renderer.resizeCanvas(width, height);
|
||||
|
||||
|
|
@ -82,6 +88,10 @@ export default new class App {
|
|||
private render(deltaTime: number) {
|
||||
requestAnimationFrame(this.renderLoop);
|
||||
|
||||
if (!this.simStarted) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.renderer.renderToScreen(this.scenes);
|
||||
}
|
||||
}
|
||||
|
|
@ -8,5 +8,6 @@ export default {
|
|||
scentMaxStorage: 1e6,
|
||||
scentPerMarker: 1000,
|
||||
antSpeed: 1,
|
||||
antRotationAngle: Math.PI / 50
|
||||
antRotationAngle: Math.PI / 50,
|
||||
brushRadius: 20,
|
||||
};
|
||||
|
|
@ -15,6 +15,8 @@ export default class GUI {
|
|||
|
||||
const controlsFolder = this.gui.addFolder('Controls');
|
||||
|
||||
controlsFolder.add(Config, 'brushRadius', 1, 100);
|
||||
|
||||
simFolder.open();
|
||||
controlsFolder.open();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import Config from "./Config";
|
|||
|
||||
interface Resources {
|
||||
worldRenderTarget: THREE.WebGLRenderTarget;
|
||||
worldRenderTargetCopy: THREE.WebGLRenderTarget;
|
||||
worldBlurredRenderTarget: THREE.WebGLRenderTarget;
|
||||
antsDataRenderTarget0: THREE.WebGLRenderTarget;
|
||||
antsDataRenderTarget1: THREE.WebGLRenderTarget;
|
||||
|
|
@ -31,12 +32,19 @@ export default class Renderer {
|
|||
magFilter: THREE.LinearFilter,
|
||||
minFilter: THREE.LinearFilter,
|
||||
}),
|
||||
worldRenderTargetCopy: new THREE.WebGLRenderTarget(Config.worldSize, Config.worldSize, {
|
||||
format: THREE.RGBAFormat,
|
||||
type: THREE.FloatType,
|
||||
depthBuffer: false,
|
||||
magFilter: THREE.NearestFilter,
|
||||
minFilter: THREE.LinearFilter,
|
||||
}),
|
||||
worldBlurredRenderTarget: new THREE.WebGLRenderTarget(Config.worldSize, Config.worldSize, {
|
||||
format: THREE.RGBAFormat,
|
||||
type: THREE.FloatType,
|
||||
depthBuffer: false,
|
||||
magFilter: THREE.LinearFilter,
|
||||
minFilter: THREE.LinearFilter,
|
||||
magFilter: THREE.NearestFilter,
|
||||
minFilter: THREE.NearestFilter,
|
||||
}),
|
||||
antsDataRenderTarget0: new THREE.WebGLRenderTarget(antTextureSize, antTextureSize, {
|
||||
format: THREE.RGBAFormat,
|
||||
|
|
@ -65,39 +73,47 @@ export default class Renderer {
|
|||
public renderSimulation(scenes: SceneCollection) {
|
||||
const [antsComputeSource, antsComputeTarget] = scenes.ants.getRenderTargets();
|
||||
|
||||
this.renderer.setViewport(0, 0, scenes.worldBlur.renderWidth, scenes.worldBlur.renderHeight);
|
||||
|
||||
this.renderer.setRenderTarget(this.resources.worldBlurredRenderTarget);
|
||||
scenes.worldBlur.material.uniforms.tWorld.value = this.resources.worldRenderTarget.texture;
|
||||
this.renderer.render(scenes.worldBlur, scenes.worldBlur.camera);
|
||||
|
||||
this.renderer.setViewport(0, 0, scenes.ants.renderWidth, scenes.ants.renderHeight);
|
||||
|
||||
this.renderer.setRenderTarget(antsComputeTarget);
|
||||
scenes.ants.material.uniforms.tLastState.value = antsComputeSource.texture;
|
||||
scenes.ants.material.uniforms.tWorld.value = scenes.worldBlur.getRenderTarget().texture;
|
||||
scenes.ants.material.uniforms.tWorld.value = this.resources.worldBlurredRenderTarget.texture;
|
||||
this.renderer.render(scenes.ants, scenes.ants.camera);
|
||||
|
||||
this.renderer.setViewport(0, 0, scenes.discretize.renderWidth, scenes.discretize.renderHeight);
|
||||
|
||||
this.renderer.setRenderTarget(scenes.discretize.getRenderTarget());
|
||||
this.renderer.setRenderTarget(this.resources.antsDiscreteRenderTarget);
|
||||
scenes.discretize.material.uniforms.tDataCurrent.value = antsComputeTarget.texture;
|
||||
scenes.discretize.material.uniforms.tDataLast.value = antsComputeSource.texture;
|
||||
this.renderer.render(scenes.discretize, scenes.discretize.camera);
|
||||
|
||||
this.renderer.setViewport(0, 0, scenes.world.renderWidth, scenes.world.renderHeight);
|
||||
|
||||
this.renderer.setRenderTarget(scenes.world.getRenderTarget());
|
||||
scenes.world.material.uniforms.tLastState.value = scenes.worldBlur.getRenderTarget().texture;
|
||||
scenes.world.material.uniforms.tDiscreteAnts.value = scenes.discretize.getRenderTarget().texture;
|
||||
scenes.world.material.uniforms.pointerData.value = scenes.screen.getPointerData();
|
||||
this.renderer.setRenderTarget(this.resources.worldRenderTarget);
|
||||
scenes.world.material.uniforms.tLastState.value = this.resources.worldBlurredRenderTarget.texture;
|
||||
scenes.world.material.uniforms.tDiscreteAnts.value = this.resources.antsDiscreteRenderTarget.texture;
|
||||
this.renderer.render(scenes.world, scenes.world.camera);
|
||||
|
||||
this.renderer.setViewport(0, 0, scenes.worldBlur.renderWidth, scenes.worldBlur.renderHeight);
|
||||
|
||||
this.renderer.setRenderTarget(scenes.worldBlur.getRenderTarget());
|
||||
scenes.worldBlur.material.uniforms.tWorld.value = scenes.world.getRenderTarget().texture;
|
||||
this.renderer.render(scenes.worldBlur, scenes.worldBlur.camera);
|
||||
|
||||
scenes.screen.material.uniforms.tData.value = antsComputeTarget.texture;
|
||||
scenes.screen.groundMaterial.uniforms.map.value = scenes.worldBlur.getRenderTarget().texture;
|
||||
scenes.screen.groundMaterial.uniforms.map.value = this.resources.worldRenderTargetCopy.texture;
|
||||
}
|
||||
|
||||
public renderToScreen(scenes: SceneCollection) {
|
||||
this.renderer.setViewport(0, 0, scenes.draw.renderWidth, scenes.draw.renderHeight);
|
||||
this.renderer.setRenderTarget(this.resources.worldRenderTargetCopy);
|
||||
scenes.draw.material.uniforms.tWorld.value = this.resources.worldRenderTarget.texture;
|
||||
scenes.draw.material.uniforms.pointerPosition.value = scenes.screen.pointerPosition;
|
||||
scenes.draw.material.uniforms.drawMode.value = scenes.screen.drawMode;
|
||||
scenes.draw.material.uniforms.brushRadius.value = Config.brushRadius;
|
||||
this.renderer.render(scenes.draw, scenes.draw.camera);
|
||||
this.renderer.copyFramebufferToTexture(new THREE.Vector2(), this.resources.worldRenderTarget.texture);
|
||||
|
||||
this.renderer.setViewport(0, 0, scenes.screen.renderWidth, scenes.screen.renderHeight);
|
||||
this.renderer.setRenderTarget(null);
|
||||
this.renderer.render(scenes.screen, scenes.screen.camera);
|
||||
|
|
|
|||
49
src/scenes/DrawScene.ts
Normal file
49
src/scenes/DrawScene.ts
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
import * as THREE from 'three';
|
||||
import Renderer from "../Renderer";
|
||||
import AbstractScene from "./AbstractScene";
|
||||
import FullScreenTriangleGeometry from "../utils/FullScreenTriangleGeometry";
|
||||
import fragmentShader from '../shaders/draw.frag';
|
||||
import vertexShader from '../shaders/draw.vert';
|
||||
import {WebGLRenderTarget} from "three";
|
||||
|
||||
export default class DrawScene extends AbstractScene {
|
||||
public readonly camera: THREE.OrthographicCamera = new THREE.OrthographicCamera();
|
||||
public readonly material: THREE.RawShaderMaterial;
|
||||
|
||||
constructor(renderer: Renderer) {
|
||||
super(renderer);
|
||||
|
||||
const geometry = new FullScreenTriangleGeometry();
|
||||
const material = new THREE.RawShaderMaterial({
|
||||
uniforms: {
|
||||
tWorld: {value: null},
|
||||
pointerPosition: {value: new THREE.Vector2()},
|
||||
drawMode: {value: 0},
|
||||
brushRadius: {value: 0},
|
||||
},
|
||||
vertexShader,
|
||||
fragmentShader,
|
||||
defines: this.renderer.getCommonMaterialDefines(),
|
||||
glslVersion: THREE.GLSL3
|
||||
});
|
||||
const mesh = new THREE.Mesh(geometry, material);
|
||||
this.add(mesh);
|
||||
|
||||
this.material = material;
|
||||
|
||||
this.renderWidth = this.renderer.resources.worldRenderTarget.width;
|
||||
this.renderHeight = this.renderer.resources.worldRenderTarget.height;
|
||||
}
|
||||
|
||||
public getRenderTarget(): WebGLRenderTarget {
|
||||
return this.renderer.resources.worldRenderTarget;
|
||||
}
|
||||
|
||||
public resize(width: number, height: number) {
|
||||
|
||||
}
|
||||
|
||||
public update(deltaTime: number) {
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -8,25 +8,29 @@ import fragmentShaderGround from "../shaders/screenWorld.frag";
|
|||
|
||||
enum PointerState {
|
||||
None,
|
||||
LMB,
|
||||
RMB
|
||||
Food,
|
||||
Home,
|
||||
Obstacle
|
||||
}
|
||||
|
||||
export default class ScreenScene extends AbstractScene {
|
||||
public readonly camera: THREE.PerspectiveCamera;
|
||||
public readonly camera: THREE.OrthographicCamera;
|
||||
public readonly material: THREE.ShaderMaterial;
|
||||
public readonly groundMaterial: THREE.ShaderMaterial;
|
||||
public readonly pointerPosition: THREE.Vector2 = new THREE.Vector2();
|
||||
public pointerState: PointerState = PointerState.None;
|
||||
public drawMode: PointerState = PointerState.None;
|
||||
private cameraZoomLinear: number = 0;
|
||||
private isPointerDown: boolean = false;
|
||||
|
||||
constructor(renderer: Renderer) {
|
||||
super(renderer);
|
||||
|
||||
const ground = new THREE.Mesh(
|
||||
new THREE.PlaneBufferGeometry(10, 10),
|
||||
new THREE.PlaneBufferGeometry(1, 1),
|
||||
new THREE.ShaderMaterial({
|
||||
uniforms: {
|
||||
map: {value: this.renderer.resources.worldRenderTarget.texture},
|
||||
tDiscreteAnts: {value: this.renderer.resources.antsDiscreteRenderTarget.texture},
|
||||
},
|
||||
vertexShader: vertexShaderGround,
|
||||
fragmentShader: fragmentShaderGround,
|
||||
|
|
@ -37,8 +41,8 @@ export default class ScreenScene extends AbstractScene {
|
|||
|
||||
this.groundMaterial = ground.material;
|
||||
|
||||
ground.position.x += 5;
|
||||
ground.position.y += 5;
|
||||
//ground.position.x = 0.5;
|
||||
//ground.position.y = 0.5;
|
||||
|
||||
this.add(ground);
|
||||
|
||||
|
|
@ -60,48 +64,31 @@ export default class ScreenScene extends AbstractScene {
|
|||
});
|
||||
|
||||
const ants = new THREE.InstancedMesh(
|
||||
new THREE.PlaneBufferGeometry(0.15, 0.15),
|
||||
new THREE.PlaneBufferGeometry(0.015, 0.015),
|
||||
this.material,
|
||||
this.renderer.resources.antsDataRenderTarget0.width * this.renderer.resources.antsDataRenderTarget0.height
|
||||
)
|
||||
|
||||
ants.position.x = ants.position.y = -0.5;
|
||||
|
||||
this.add(ants);
|
||||
|
||||
this.camera = new THREE.PerspectiveCamera(50, 1, 0.1, 10000);
|
||||
this.camera = new THREE.OrthographicCamera(-0.5, 0.5, 0.5, -0.5);
|
||||
|
||||
this.add(this.camera);
|
||||
|
||||
this.camera.position.z = 12;
|
||||
this.camera.position.x = this.camera.position.y = 5;
|
||||
|
||||
const raycastVector = new THREE.Vector2(0, 0);
|
||||
const raycaster = new THREE.Raycaster();
|
||||
|
||||
this.renderer.canvas.addEventListener('contextmenu', (e) => {
|
||||
this.renderer.canvas.addEventListener('contextmenu', e => {
|
||||
e.preventDefault();
|
||||
});
|
||||
|
||||
this.renderer.canvas.addEventListener('pointerdown', (e) => {
|
||||
raycastVector.x = (e.clientX / window.innerWidth) * 2 - 1;
|
||||
raycastVector.y = -(e.clientY / window.innerHeight) * 2 + 1;
|
||||
this.renderer.canvas.addEventListener('pointerdown', e => {
|
||||
this.isPointerDown = true;
|
||||
|
||||
raycaster.setFromCamera(raycastVector, this.camera);
|
||||
|
||||
const intersects = raycaster.intersectObjects([ground]);
|
||||
|
||||
if (intersects.length > 0) {
|
||||
const uv = intersects[0].uv;
|
||||
this.pointerPosition.copy(uv);
|
||||
|
||||
if (e.button === 0) {
|
||||
this.pointerState = PointerState.LMB;
|
||||
} else if (e.button === 2) {
|
||||
this.pointerState = PointerState.RMB;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
this.renderer.canvas.addEventListener('pointermove', (e) => {
|
||||
raycastVector.x = (e.clientX / window.innerWidth) * 2 - 1;
|
||||
raycastVector.y = -(e.clientY / window.innerHeight) * 2 + 1;
|
||||
|
||||
|
|
@ -115,22 +102,77 @@ export default class ScreenScene extends AbstractScene {
|
|||
}
|
||||
});
|
||||
|
||||
this.renderer.canvas.addEventListener('pointerup', (e) => {
|
||||
this.pointerState = PointerState.None;
|
||||
this.renderer.canvas.addEventListener('pointermove', e => {
|
||||
if (this.isPointerDown) {
|
||||
const dx = e.movementX;
|
||||
const dy = e.movementY;
|
||||
|
||||
this.camera.position.x -= dx / window.innerHeight / this.camera.zoom;
|
||||
this.camera.position.y += dy / window.innerHeight / this.camera.zoom;
|
||||
}
|
||||
|
||||
raycastVector.x = (e.clientX / window.innerWidth) * 2 - 1;
|
||||
raycastVector.y = -(e.clientY / window.innerHeight) * 2 + 1;
|
||||
|
||||
raycaster.setFromCamera(raycastVector, this.camera);
|
||||
|
||||
const intersects = raycaster.intersectObjects([ground]);
|
||||
|
||||
if (intersects.length > 0) {
|
||||
const uv = intersects[0].uv;
|
||||
this.pointerPosition.copy(uv);
|
||||
}
|
||||
});
|
||||
|
||||
this.renderer.canvas.addEventListener('pointerup', e => {
|
||||
this.isPointerDown = false;
|
||||
});
|
||||
|
||||
this.renderer.canvas.addEventListener('pointerleave', e => {
|
||||
this.isPointerDown = false;
|
||||
});
|
||||
|
||||
this.renderer.canvas.addEventListener('wheel', e => {
|
||||
this.cameraZoomLinear -= e.deltaY * 0.001;
|
||||
|
||||
this.updateCameraZoom();
|
||||
});
|
||||
|
||||
window.addEventListener('keydown', e => {
|
||||
switch (e.code) {
|
||||
case 'KeyQ': {
|
||||
this.drawMode = PointerState.Home;
|
||||
break;
|
||||
}
|
||||
case 'KeyW': {
|
||||
this.drawMode = PointerState.Food;
|
||||
break;
|
||||
}
|
||||
case 'KeyE': {
|
||||
this.drawMode = PointerState.Obstacle;
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
window.addEventListener('keyup', e => {
|
||||
this.drawMode = PointerState.None;
|
||||
});
|
||||
}
|
||||
|
||||
public getPointerData(): THREE.Vector4 {
|
||||
return new THREE.Vector4(
|
||||
+(this.pointerState === PointerState.LMB),
|
||||
+(this.pointerState === PointerState.RMB),
|
||||
this.pointerPosition.x,
|
||||
this.pointerPosition.y
|
||||
);
|
||||
private updateCameraZoom() {
|
||||
this.camera.zoom = 2 ** this.cameraZoomLinear;
|
||||
this.camera.updateProjectionMatrix();
|
||||
}
|
||||
|
||||
public resize(width: number, height: number) {
|
||||
this.camera.aspect = width / height;
|
||||
const aspect = width / height;
|
||||
|
||||
this.camera.left = -0.5 * aspect;
|
||||
this.camera.right = 0.5 * aspect;
|
||||
this.camera.top = 0.5;
|
||||
this.camera.bottom = -0.5;
|
||||
|
||||
this.camera.updateProjectionMatrix();
|
||||
|
||||
this.renderWidth = width;
|
||||
|
|
|
|||
|
|
@ -16,9 +16,8 @@ export default class WorldComputeScene extends AbstractScene {
|
|||
const geometry = new FullScreenTriangleGeometry();
|
||||
const material = new THREE.RawShaderMaterial({
|
||||
uniforms: {
|
||||
tLastState: {value: this.renderer.resources.worldRenderTarget.texture},
|
||||
tDiscreteAnts: {value: this.renderer.resources.antsDiscreteRenderTarget.texture},
|
||||
pointerData: {value: new THREE.Vector4()},
|
||||
tLastState: {value: null},
|
||||
tDiscreteAnts: {value: null}
|
||||
},
|
||||
vertexShader,
|
||||
fragmentShader,
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ void main() {
|
|||
|
||||
vec4 dataSample = texture(tData, vec2(sampleX, sampleY) / dataTextureSize);
|
||||
|
||||
vec2 offset = dataSample.xy * 10.;
|
||||
vec2 offset = dataSample.xy;
|
||||
vec2 rotatedPosition = rotate(position.xy, -dataSample.z + PI * 0.5);
|
||||
|
||||
vIsCarryingFood = float(int(dataSample.w) & 1);
|
||||
|
|
|
|||
|
|
@ -77,21 +77,6 @@ void main() {
|
|||
storage = 0.;
|
||||
}
|
||||
|
||||
if (tryGetFood(pos) && isCarrying == 0.) {
|
||||
isCarrying = 1.;
|
||||
angle += PI;
|
||||
storage = getMaxScentStorage(vUv);
|
||||
}
|
||||
|
||||
if (tryDropFood(pos)) {
|
||||
storage = getMaxScentStorage(vUv);
|
||||
|
||||
if (isCarrying == 1.) {
|
||||
isCarrying = 0.;
|
||||
angle += PI;
|
||||
}
|
||||
}
|
||||
|
||||
if (isCarrying == 0.) {
|
||||
if (noise < 0.33) {
|
||||
vec2 offset = vec2(cos(angle), sin(angle)) * sampleDistance;
|
||||
|
|
@ -181,6 +166,21 @@ void main() {
|
|||
angle += PI * (noise - 0.5);
|
||||
}
|
||||
|
||||
if (tryGetFood(pos) && isCarrying == 0.) {
|
||||
isCarrying = 1.;
|
||||
angle += PI;
|
||||
storage = getMaxScentStorage(vUv);
|
||||
}
|
||||
|
||||
if (tryDropFood(pos)) {
|
||||
storage = getMaxScentStorage(vUv);
|
||||
|
||||
if (isCarrying == 1.) {
|
||||
isCarrying = 0.;
|
||||
angle += PI;
|
||||
}
|
||||
}
|
||||
|
||||
FragColor = vec4(
|
||||
pos.x,
|
||||
pos.y,
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@ void main() {
|
|||
vIsCellCleared = isCellCleared;
|
||||
|
||||
gl_Position = vec4(
|
||||
(position.xy * cellSize * 0.1 + floor(offset * WORLD_SIZE) / WORLD_SIZE + cellSize * 0.5) * 2. - 1.,
|
||||
(position.xy * cellSize * 0.01 + floor(offset * WORLD_SIZE) / WORLD_SIZE + cellSize * 0.5) * 2. - 1.,
|
||||
0,
|
||||
1
|
||||
);
|
||||
|
|
|
|||
28
src/shaders/draw.frag
Normal file
28
src/shaders/draw.frag
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
precision highp float;
|
||||
precision highp int;
|
||||
|
||||
in vec2 vUv;
|
||||
|
||||
out vec4 FragColor;
|
||||
|
||||
uniform sampler2D tWorld;
|
||||
uniform vec2 pointerPosition;
|
||||
uniform float drawMode;
|
||||
uniform float brushRadius;
|
||||
|
||||
void main() {
|
||||
vec4 lastState = texture(tWorld, vUv);
|
||||
|
||||
float isFood = lastState.x;
|
||||
float isHome = lastState.y;
|
||||
|
||||
if (distance(pointerPosition, vUv) < brushRadius / WORLD_SIZE) {
|
||||
if (drawMode == 1.) {
|
||||
isFood = 1.;
|
||||
} else if (drawMode == 2.) {
|
||||
isHome = 1.;
|
||||
}
|
||||
}
|
||||
|
||||
FragColor = vec4(isFood, isHome, lastState.zw);
|
||||
}
|
||||
12
src/shaders/draw.vert
Normal file
12
src/shaders/draw.vert
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
precision highp float;
|
||||
precision highp int;
|
||||
|
||||
in vec3 position;
|
||||
in vec2 uv;
|
||||
|
||||
out vec2 vUv;
|
||||
|
||||
void main() {
|
||||
vUv = uv;
|
||||
gl_Position = vec4(position, 1.0);
|
||||
}
|
||||
|
|
@ -6,21 +6,35 @@ in vec2 vUv;
|
|||
out vec4 FragColor;
|
||||
|
||||
uniform sampler2D map;
|
||||
uniform sampler2D tDiscreteAnts;
|
||||
|
||||
void main() {
|
||||
vec4 value = clamp(texture(map, vUv), 0., 1.);
|
||||
vec3 bg = vec3(0.9);
|
||||
|
||||
bg = mix(bg, vec3(0.2, 0.2, 0.8), clamp(value.a, 0., 1.));
|
||||
bg = mix(bg, vec3(0.8, 0.2, 0.2), clamp(value.b, 0., 1.));
|
||||
float isFood = value.r;
|
||||
float isHome = value.g;
|
||||
float toFood = value.b;
|
||||
float toHome = value.a;
|
||||
|
||||
if (value.r == 1.) {
|
||||
bg = vec3(1, 0.2, 0.2);
|
||||
// The part below doen't seem right.
|
||||
// I could figure out a better way to make pheromone colors blend properly on white background :(
|
||||
|
||||
vec3 t = vec3(0.95, 0.2, 0.2) * toFood + vec3(0.2, 0.2, 0.95) * toHome;
|
||||
float a = clamp(toHome + toFood, 0., 1.);
|
||||
|
||||
t /= a;
|
||||
|
||||
if (a == 0.) t = vec3(0);
|
||||
|
||||
vec3 color = mix(vec3(1, 1, 1), t, a * 0.7);
|
||||
|
||||
if (isFood == 1.) {
|
||||
color = vec3(1, 0.1, 0.1);
|
||||
}
|
||||
|
||||
if (value.g == 1.) {
|
||||
bg = vec3(0.2, 0.2, 1);
|
||||
if (isHome == 1.) {
|
||||
color = vec3(0.1, 0.1, 1);
|
||||
}
|
||||
|
||||
FragColor = vec4(bg, 1);
|
||||
FragColor = vec4(color, 1);
|
||||
}
|
||||
|
|
@ -7,7 +7,6 @@ out vec4 FragColor;
|
|||
|
||||
uniform sampler2D tLastState;
|
||||
uniform sampler2D tDiscreteAnts;
|
||||
uniform vec4 pointerData;
|
||||
|
||||
void main() {
|
||||
vec4 lastState = texture(tLastState, vUv);
|
||||
|
|
@ -15,17 +14,12 @@ void main() {
|
|||
|
||||
float isFood = lastState.x;
|
||||
float isHome = lastState.y;
|
||||
float scentToHome = lastState.z + discreteAnts.x * 2.;
|
||||
float scentToFood = lastState.w + discreteAnts.y * 2.;
|
||||
float scentToHome = min(10., lastState.z + discreteAnts.x * 2.);
|
||||
float scentToFood = min(10., lastState.w + discreteAnts.y * 2.);
|
||||
|
||||
if (discreteAnts.z == 1.) {
|
||||
isFood = 0.;
|
||||
}
|
||||
|
||||
if (distance(pointerData.zw, vUv) < 0.02) {
|
||||
isFood = max(isFood, pointerData.x);
|
||||
isHome = max(isHome, pointerData.y);
|
||||
}
|
||||
|
||||
FragColor = vec4(isFood, isHome, scentToHome, scentToFood);
|
||||
}
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 229 B After Width: | Height: | Size: 229 B |
Loading…
Reference in a new issue