├── .gitignore
├── src
├── assets
│ └── photo.jpg
├── js
│ ├── bidello
│ │ ├── index.js
│ │ ├── viewport.js
│ │ ├── raf.js
│ │ └── pointer.js
│ ├── postfx
│ │ ├── postfx.vert
│ │ ├── postfx.js
│ │ └── postfx.frag
│ ├── utils
│ │ ├── deferred.js
│ │ ├── triangle.js
│ │ ├── trail.js
│ │ └── fbo.js
│ ├── scene.js
│ ├── renderer.js
│ ├── main.js
│ ├── settings.js
│ ├── camera.js
│ ├── assets.js
│ └── cube
│ │ └── cube.js
└── index.html
├── README.md
└── package.json
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | dist
3 | .DS_Store
4 | .cache
5 | package-lock.json
--------------------------------------------------------------------------------
/src/assets/photo.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/luruke/antipasto/HEAD/src/assets/photo.jpg
--------------------------------------------------------------------------------
/src/js/bidello/index.js:
--------------------------------------------------------------------------------
1 | export * from './raf';
2 | export * from './viewport';
3 | export * from './pointer';
--------------------------------------------------------------------------------
/src/js/postfx/postfx.vert:
--------------------------------------------------------------------------------
1 | precision highp float;
2 | attribute vec2 position;
3 |
4 | void main() {
5 | gl_Position = vec4(position, 1.0, 1.0);
6 | }
--------------------------------------------------------------------------------
/src/js/utils/deferred.js:
--------------------------------------------------------------------------------
1 | export default () => {
2 | let _resolve = undefined;
3 | let _reject = undefined;
4 | const promise = new Promise((resolve, reject) => {
5 | _resolve = resolve;
6 | _reject = reject;
7 | });
8 |
9 | promise.resolve = _resolve;
10 | promise.reject = _reject;
11 |
12 | return promise;
13 | };
14 |
--------------------------------------------------------------------------------
/src/js/utils/triangle.js:
--------------------------------------------------------------------------------
1 | import {
2 | BufferGeometry,
3 | BufferAttribute,
4 | } from 'three';
5 |
6 | const vertices = new Float32Array([
7 | -1.0, -1.0,
8 | 3.0, -1.0,
9 | -1.0, 3.0
10 | ]);
11 |
12 | const geometry = new BufferGeometry();
13 | geometry.setAttribute('position', new BufferAttribute(vertices, 2));
14 |
15 | export default geometry;
--------------------------------------------------------------------------------
/src/js/scene.js:
--------------------------------------------------------------------------------
1 | // import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader';
2 | import { Scene } from 'three';
3 | import { component } from 'bidello';
4 | import Cube from './cube/cube';
5 | import camera from './camera';
6 |
7 | class Stage extends component(Scene) {
8 | init() {
9 | this.add(new Cube());
10 | this.add(camera);
11 | }
12 | }
13 |
14 | export default new Stage();
--------------------------------------------------------------------------------
/src/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | antipasto
4 |
5 |
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/src/js/renderer.js:
--------------------------------------------------------------------------------
1 | import { WebGLRenderer } from 'three';
2 | import { component } from 'bidello';
3 | import settings from './settings';
4 |
5 | class Renderer extends component(WebGLRenderer) {
6 | constructor() {
7 | super({
8 | powerPreference: 'high-performance',
9 | antialiasing: false,
10 | });
11 |
12 | // this.gammaFactor = 2.2;
13 | // this.gammaInput = true;
14 | // this.gammaOutput = true;
15 |
16 | this.setPixelRatio(settings.dpr);
17 | }
18 |
19 | onResize({ width, height }) {
20 | this.setSize(width, height);
21 | }
22 | }
23 |
24 | export default new Renderer();
25 |
--------------------------------------------------------------------------------
/src/js/main.js:
--------------------------------------------------------------------------------
1 | import * as helpers from './bidello';
2 | import renderer from './renderer';
3 | import camera from './camera';
4 | import scene from './scene';
5 | import { component } from 'bidello';
6 | import settings from './settings';
7 | import postfx from './postfx/postfx';
8 | import assets from './assets';
9 |
10 | class Site extends component() {
11 | init() {
12 | assets.load();
13 | document.body.appendChild(renderer.domElement);
14 | }
15 |
16 | onRaf() {
17 | // renderer.render(scene, camera);
18 | postfx.render(scene, camera);
19 | }
20 |
21 | onLoadEnd() {
22 | console.log('finished loader!');
23 | }
24 | }
25 |
26 | new Site();
--------------------------------------------------------------------------------
/src/js/settings.js:
--------------------------------------------------------------------------------
1 | import { getGPUTier } from 'detect-gpu';
2 |
3 | const tier = getGPUTier({
4 | mobileBenchmarkPercentages: [10, 40, 30, 20], // (Default) [TIER_0, TIER_1, TIER_2, TIER_3]
5 | desktopBenchmarkPercentages: [10, 40, 30, 20], // (Default) [TIER_0, TIER_1, TIER_2, TIER_3]
6 | // forceRendererString: 'Apple A11 GPU', // (Development) Force a certain renderer string
7 | // forceMobile: true, // (Development) Force the use of mobile benchmarking scores
8 | });
9 |
10 | const dpr = Math.min(1.5, window.devicePixelRatio || 1);
11 |
12 | const settings = {
13 | tier,
14 | dpr,
15 | fxaa: true,
16 | };
17 |
18 | console.log(`⚙️ settings`, settings);
19 |
20 | export default settings;
21 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # 🍽 antipasto
2 |
3 | > ⚠️ probably won't work with modern version of threejs, last time I tested was with r114
4 | > pr is welcome
5 |
6 | ### Boilerplate for three.js, using some juicy stuff:
7 |
8 | - [bidello](https://github.com/luruke/bidello)
9 | - [magicshader](https://github.com/luruke/magicshader)
10 | - [postfx](https://medium.com/@luruke/simple-postprocessing-in-three-js-91936ecadfb7)
11 | - [detect-gpu](https://github.com/TimvanScherpenzeel/detect-gpu)
12 | - [parcel-bundler](https://parceljs.org/)
13 | - [resource-loader](https://github.com/englercj/resource-loader)
14 | - [glsl-fxaa](https://github.com/mattdesl/glsl-fxaa)
15 | - [orbit-control-es6](https://github.com/silviopaganini/orbit-controls-es6)
16 | - [GPGPU/FBO utility](https://github.com/luruke/antipasto/blob/master/src/js/utils/fbo.js)
17 |
18 |
19 | ```
20 | npm install
21 | npm run dev
22 | ```
23 |
--------------------------------------------------------------------------------
/src/js/bidello/viewport.js:
--------------------------------------------------------------------------------
1 | import bidello from 'bidello';
2 |
3 | class Viewport {
4 | constructor() {
5 | this.width = this.calculateWidth();
6 | this.height = this.calculateHeight();
7 | this.ratio = this.width / this.height;
8 |
9 | this.onResize = this.onResize.bind(this);
10 | this.onResize();
11 |
12 | window.addEventListener('resize', this.onResize);
13 | }
14 |
15 | calculateWidth() {
16 | return window.innerWidth;
17 | }
18 |
19 | calculateHeight() {
20 | return window.innerHeight;
21 | }
22 |
23 | onResize() {
24 | this.width = this.calculateWidth();
25 | this.height = this.calculateHeight();
26 | this.ratio = this.width / this.height;
27 |
28 | bidello.trigger({ name: 'resize', fireAtStart: true }, {
29 | width: this.width,
30 | height: this.height,
31 | ratio: this.ratio,
32 | })
33 | }
34 | }
35 |
36 | export const viewport = new Viewport();
37 |
--------------------------------------------------------------------------------
/src/js/bidello/raf.js:
--------------------------------------------------------------------------------
1 | import bidello from 'bidello';
2 |
3 | class Raf {
4 | constructor() {
5 | this.time = window.performance.now();
6 |
7 | this.start = this.start.bind(this);
8 | this.pause = this.pause.bind(this);
9 | this.onTick = this.onTick.bind(this);
10 | this.start();
11 | }
12 |
13 | start() {
14 | this.startTime = window.performance.now();
15 | this.oldTime = this.startTime;
16 | this.isPaused = false;
17 |
18 | this.onTick(this.startTime);
19 | }
20 |
21 | pause() {
22 | this.isPaused = true;
23 | }
24 |
25 | onTick(now) {
26 | this.time = now;
27 |
28 | if (!this.isPaused) {
29 | this.delta = (now - this.oldTime) / 1000;
30 | this.oldTime = now;
31 |
32 | bidello.trigger({ name: 'raf' }, {
33 | delta: this.delta,
34 | now
35 | });
36 | }
37 |
38 | window.requestAnimationFrame(this.onTick);
39 | }
40 | }
41 |
42 | export const raf = new Raf();
43 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "antipasto",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": "index.js",
6 | "scripts": {
7 | "test": "echo \"Error: no test specified\" && exit 1",
8 | "dev": "parcel src/index.html --no-autoinstall --open",
9 | "build": "rm -rf ./docs; parcel build src/index.html -d docs --public-url '.' --no-source-maps"
10 | },
11 | "repository": {
12 | "type": "git",
13 | "url": "git+https://github.com/luruke/antipasto.git"
14 | },
15 | "author": "",
16 | "license": "ISC",
17 | "bugs": {
18 | "url": "https://github.com/luruke/antipasto/issues"
19 | },
20 | "homepage": "https://github.com/luruke/antipasto#readme",
21 | "devDependencies": {
22 | "bidello": "1.0.1",
23 | "detect-gpu": "^1.1.4",
24 | "glsl-fxaa": "^3.0.0",
25 | "glslify-bundle": "^5.1.1",
26 | "glslify-deps": "^1.3.1",
27 | "magicshader": "^0.1.4",
28 | "orbit-controls-es6": "^2.0.1",
29 | "parcel-bundler": "^1.12.4",
30 | "resource-loader": "^3.0.1",
31 | "math-toolbox": "^1.12.0",
32 | "three": "^0.111.0"
33 | },
34 | "dependencies": {}
35 | }
36 |
--------------------------------------------------------------------------------
/src/js/camera.js:
--------------------------------------------------------------------------------
1 | import { PerspectiveCamera, Vector3 } from 'three';
2 | import { component } from 'bidello';
3 | import OrbitControls from 'orbit-controls-es6';
4 | import renderer from '/js/renderer';
5 |
6 | class Camera extends component(PerspectiveCamera) {
7 | constructor() {
8 | super(35, 0, 0.1, 500);
9 | }
10 |
11 | init() {
12 | this.position.set(0, 0, 10);
13 | this.lookAt(new Vector3(0, 0, 0));
14 | this.initOrbitControl();
15 | }
16 |
17 | initOrbitControl() {
18 | const controls = new OrbitControls(this, renderer.domElement);
19 |
20 | controls.enabled = true;
21 | controls.maxDistance = 1500;
22 | controls.minDistance = 0;
23 | }
24 |
25 | calculateUnitSize(distance = this.position.z) {
26 | const vFov = this.fov * Math.PI / 180;
27 | const height = 2 * Math.tan(vFov / 2) * distance;
28 | const width = height * this.aspect;
29 |
30 | return {
31 | width,
32 | height
33 | };
34 | }
35 |
36 | onResize({ ratio }) {
37 | this.aspect = ratio;
38 | this.unit = this.calculateUnitSize();
39 | this.updateProjectionMatrix();
40 | }
41 | }
42 |
43 | export default new Camera();
44 |
--------------------------------------------------------------------------------
/src/js/assets.js:
--------------------------------------------------------------------------------
1 | import { Loader } from 'resource-loader';
2 | import bidello from 'bidello';
3 | import deferred from '/js/utils/deferred';
4 |
5 | const RESOURCES = [
6 | {
7 | name: 'photo',
8 | url: require('/assets/photo.jpg')
9 | },
10 |
11 | // {
12 | // name: 'photo',
13 | // url: require('/assets/photo.glb'),
14 | // loadType: Resource.LOAD_TYPE.XHR,
15 | // xhrType: Resource.XHR_RESPONSE_TYPE.BLOB,
16 | // },
17 | ];
18 |
19 | /*
20 | assets.resources.photo.loading.then(res => {
21 | console.log(res.meta.data);
22 | });
23 | */
24 |
25 | class Assets {
26 | constructor() {
27 | this.resources = {};
28 |
29 | RESOURCES.forEach(entry => {
30 | this.resources[entry.name] = entry;
31 | this.resources[entry.name].loading = deferred();
32 | });
33 | }
34 |
35 | load() {
36 | this.deferred = deferred();
37 | this.loader = new Loader();
38 |
39 | bidello.trigger({ name: 'loadStart' });
40 |
41 | RESOURCES.forEach(res => {
42 | this.loader.add(res);
43 | });
44 |
45 | this.loader.onProgress.add(this.onProgress.bind(this));
46 | this.loader.load(this.finish.bind(this));
47 |
48 | return deferred;
49 | }
50 |
51 | onProgress(loader, meta) {
52 | bidello.trigger({ name: 'loadProgress' }, { progress: this.loader.progress });
53 | const res = this.resources[meta.name];
54 | res.meta = meta;
55 | res.loading.resolve(res);
56 | }
57 |
58 | finish() {
59 | this.deferred.resolve();
60 | bidello.trigger({ name: 'loadEnd' }, { resources: this.resources });
61 | }
62 | }
63 |
64 | export default new Assets();
65 |
--------------------------------------------------------------------------------
/src/js/utils/trail.js:
--------------------------------------------------------------------------------
1 | import { component } from 'bidello';
2 | import FBO from './fbo';
3 | import { Vector2, LinearFilter } from 'three';
4 |
5 | const shader = `
6 | precision highp float;
7 |
8 | uniform sampler2D texture;
9 | uniform vec2 uPointer;
10 |
11 | float circle(vec2 uv, vec2 disc_center, float disc_radius, float border_size) {
12 | uv -= disc_center;
13 | float dist = sqrt(dot(uv, uv));
14 | return smoothstep(disc_radius+border_size, disc_radius-border_size, dist);
15 | }
16 |
17 | void main() {
18 | vec2 uv = gl_FragCoord.xy / RESOLUTION.xy;
19 | vec4 color = texture2D(texture, uv);
20 |
21 | color.rgb += circle(uv, uPointer, 0.0, 0.1);
22 | color.rgb = mix(color.rgb, vec3(0.0), .009);
23 | color.rgb = clamp(color.rgb, vec3(0.0), vec3(1.0));
24 | color.a = 1.0;
25 |
26 | gl_FragColor = color;
27 | }
28 | `;
29 |
30 | class Trail extends component() {
31 | init() {
32 | this.fbo = new FBO({
33 | width: 256,
34 | height: 256,
35 | name: 'trail',
36 | shader,
37 | uniforms: {
38 | uPointer: { value: new Vector2() }
39 | },
40 | rtOptions: {
41 | minFilter: LinearFilter,
42 | magFilter: LinearFilter,
43 | },
44 | // debug: true,
45 | });
46 |
47 | this.pointerTarget = new Vector2();
48 | }
49 |
50 | onPointerMove({ pointer }) {
51 | this.pointerTarget.set(
52 | pointer.x / window.innerWidth,
53 | 1 - (pointer.y / window.innerHeight)
54 | );
55 | }
56 |
57 | onRaf({ delta }) {
58 | // this.fbo.uniforms.uPointer.value.lerp(this.pointerTarget, 7 * delta);
59 | this.fbo.uniforms.uPointer.value.copy(this.pointerTarget);
60 | this.fbo.update();
61 | }
62 | }
63 |
64 | export default new Trail();
--------------------------------------------------------------------------------
/src/js/postfx/postfx.js:
--------------------------------------------------------------------------------
1 | import {
2 | WebGLRenderTarget,
3 | Camera,
4 | RGBFormat,
5 | Mesh,
6 | Scene,
7 | RawShaderMaterial,
8 | Vector2,
9 | } from 'three';
10 |
11 | import { component } from 'bidello';
12 | import renderer from '/js/renderer';
13 | import settings from '/js/settings';
14 | import triangle from '/js/utils/triangle';
15 | import vertexShader from './postfx.vert';
16 | import fragmentShader from './postfx.frag';
17 |
18 | class PostFX extends component() {
19 | init() {
20 | this.renderer = renderer;
21 | this.scene = new Scene();
22 | this.dummyCamera = new Camera();
23 | this.resolution = new Vector2();
24 | this.renderer.getDrawingBufferSize(this.resolution);
25 |
26 | this.target = new WebGLRenderTarget(this.resolution.x, this.resolution.y, {
27 | format: RGBFormat,
28 | stencilBuffer: false,
29 | depthBuffer: true,
30 | });
31 |
32 | const defines = {};
33 |
34 | settings.fxaa && (defines.FXAA = true);
35 |
36 | this.material = new RawShaderMaterial({
37 | defines,
38 | fragmentShader,
39 | vertexShader,
40 | uniforms: {
41 | uScene: { value: this.target.texture },
42 | uResolution: { value: this.resolution },
43 | },
44 | });
45 |
46 | this.triangle = new Mesh(triangle, this.material);
47 | this.triangle.frustumCulled = false;
48 | this.scene.add(this.triangle);
49 | }
50 |
51 | onResize() {
52 | this.renderer.getDrawingBufferSize(this.resolution);
53 | this.target.setSize(this.resolution.x, this.resolution.y);
54 | }
55 |
56 | render(scene, camera) {
57 | this.renderer.setRenderTarget(this.target);
58 | this.renderer.render(scene, camera);
59 | this.renderer.setRenderTarget(null);
60 | this.renderer.render(this.scene, this.dummyCamera);
61 | }
62 | }
63 |
64 | export default new PostFX();
--------------------------------------------------------------------------------
/src/js/cube/cube.js:
--------------------------------------------------------------------------------
1 | import {
2 | Object3D,
3 | BoxBufferGeometry,
4 | Mesh
5 | } from 'three';
6 |
7 | import { component } from 'bidello';
8 | import MagicShader from 'magicshader';
9 | import trail from '/js/utils/trail';
10 |
11 | export default class extends component(Object3D) {
12 | init() {
13 | this.geometry = new BoxBufferGeometry(3, 3, 3, 18, 18, 18);
14 | this.material = new MagicShader({
15 | wireframe: true,
16 | name: 'Cube',
17 | vertexShader: `
18 | precision highp float;
19 |
20 | attribute vec3 normal;
21 | attribute vec3 position;
22 |
23 | uniform sampler2D uTrail;
24 | uniform mat3 normalMatrix;
25 | uniform mat4 modelMatrix;
26 | uniform mat4 viewMatrix;
27 | uniform mat4 modelViewMatrix;
28 | uniform mat4 projectionMatrix;
29 |
30 | varying float vForce;
31 |
32 | float quarticOut(float t) {
33 | return pow(t - 1.0, 3.0) * (1.0 - t) + 1.0;
34 | }
35 |
36 | void main() {
37 | vec4 clipSpace = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
38 |
39 | vec2 uv = ((clipSpace.xy / clipSpace.w) + 1.0) / 2.0;
40 | float pointer = texture2D(uTrail, uv).r;
41 | vec4 pos = modelMatrix * vec4(position, 1.0);
42 |
43 | float force = quarticOut(pointer);
44 | vForce = force;
45 |
46 | vec3 norm = normalMatrix * normal;
47 | pos.rgb += (norm * force) * .5;
48 |
49 | gl_Position = projectionMatrix * viewMatrix * pos;
50 | }
51 | `,
52 | fragmentShader: `
53 | precision highp float;
54 |
55 | uniform vec3 colorA; // ms({ value: '#ff0000' })
56 | uniform vec3 colorB; // ms({ value: '#00ff00' })
57 | varying float vForce;
58 |
59 | void main() {
60 | gl_FragColor = vec4(mix(colorA, colorB, vForce), 1.0);
61 | }
62 | `,
63 | uniforms: {
64 | uTrail: { value: trail.fbo.target }
65 | }
66 | });
67 |
68 | this.mesh = new Mesh(this.geometry, this.material);
69 |
70 | this.add(this.mesh);
71 | }
72 |
73 | onRaf({ delta }) {
74 | this.mesh.rotation.x += 0.3 * delta;
75 | this.mesh.rotation.y += 0.3 * delta;
76 | // this.material.uniforms.uTrail.value = trail.target;
77 | }
78 | }
--------------------------------------------------------------------------------
/src/js/postfx/postfx.frag:
--------------------------------------------------------------------------------
1 | precision highp float;
2 | uniform sampler2D uScene;
3 | uniform vec2 uResolution;
4 |
5 | #ifdef FXAA
6 | #pragma glslify: fxaa = require(glsl-fxaa)
7 | #endif
8 |
9 | const float gamma = 2.2;
10 |
11 | vec3 linearToneMapping(vec3 color) {
12 | float exposure = 1.;
13 | color = clamp(exposure * color, 0., 1.);
14 | color = pow(color, vec3(1. / gamma));
15 | return color;
16 | }
17 |
18 | vec3 simpleReinhardToneMapping(vec3 color) {
19 | float exposure = 1.5;
20 | color *= exposure/(1. + color / exposure);
21 | color = pow(color, vec3(1. / gamma));
22 | return color;
23 | }
24 |
25 | vec3 lumaBasedReinhardToneMapping(vec3 color) {
26 | float luma = dot(color, vec3(0.2126, 0.7152, 0.0722));
27 | float toneMappedLuma = luma / (1. + luma);
28 | color *= toneMappedLuma / luma;
29 | color = pow(color, vec3(1. / gamma));
30 | return color;
31 | }
32 |
33 | vec3 whitePreservingLumaBasedReinhardToneMapping(vec3 color) {
34 | float white = 2.;
35 | float luma = dot(color, vec3(0.2126, 0.7152, 0.0722));
36 | float toneMappedLuma = luma * (1. + luma / (white*white)) / (1. + luma);
37 | color *= toneMappedLuma / luma;
38 | color = pow(color, vec3(1. / gamma));
39 | return color;
40 | }
41 |
42 | vec3 RomBinDaHouseToneMapping(vec3 color) {
43 | color = exp( -1.0 / ( 2.72*color + 0.15 ) );
44 | color = pow(color, vec3(1. / gamma));
45 | return color;
46 | }
47 |
48 | vec3 filmicToneMapping(vec3 color) {
49 | color = max(vec3(0.), color - vec3(0.004));
50 | color = (color * (6.2 * color + .5)) / (color * (6.2 * color + 1.7) + 0.06);
51 | return color;
52 | }
53 |
54 | vec3 Uncharted2ToneMapping(vec3 color) {
55 | float A = 0.15;
56 | float B = 0.50;
57 | float C = 0.10;
58 | float D = 0.20;
59 | float E = 0.02;
60 | float F = 0.30;
61 | float W = 11.2;
62 | float exposure = 2.;
63 | color *= exposure;
64 | color = ((color * (A * color + C * B) + D * E) / (color * (A * color + B) + D * F)) - E / F;
65 | float white = ((W * (A * W + C * B) + D * E) / (W * (A * W + B) + D * F)) - E / F;
66 | color /= white;
67 | color = pow(color, vec3(1. / gamma));
68 | return color;
69 | }
70 |
71 | void main() {
72 | #ifdef FXAA
73 | vec3 color = fxaa(uScene, gl_FragCoord.xy, uResolution).rgb;
74 | #else
75 | vec2 uv = gl_FragCoord.xy / uResolution.xy;
76 | vec3 color = texture2D(uScene, uv).rgb;
77 | #endif
78 |
79 | color = linearToneMapping(color);
80 |
81 | gl_FragColor = vec4(color, 1.0);
82 | }
--------------------------------------------------------------------------------
/src/js/bidello/pointer.js:
--------------------------------------------------------------------------------
1 | import bidello from 'bidello';
2 | import { Vector2, Vector3} from 'three';
3 | import { clamp } from 'math-toolbox';
4 | import camera from '/js/camera';
5 | import { viewport } from './viewport';
6 |
7 | class Pointer {
8 | constructor() {
9 | this.x = 0;
10 | this.y = 0;
11 | this.isTouching = true;
12 | this.distance = 0;
13 |
14 | this.hold = new Vector2();
15 | this.last = new Vector2();
16 | this.delta = new Vector2();
17 | this.move = new Vector2();
18 | this.world = new Vector3();
19 | this.normalized = new Vector2();
20 | this._tmp = new Vector3();
21 |
22 | this.bind();
23 | }
24 |
25 | bind() {
26 | const container = window;
27 |
28 | container.addEventListener('touchstart', this.onStart.bind(this), {
29 | passive: false
30 | });
31 | container.addEventListener('touchmove', this.onMove.bind(this), {
32 | passive: false
33 | });
34 | container.addEventListener('touchend', this.onEnd.bind(this), {
35 | passive: false
36 | });
37 | container.addEventListener('touchcancel', this.onEnd.bind(this), {
38 | passive: false
39 | });
40 |
41 | container.addEventListener('mousedown', this.onStart.bind(this));
42 | container.addEventListener('mousemove', this.onMove.bind(this));
43 | container.addEventListener('mouseup', this.onEnd.bind(this));
44 | container.addEventListener('contextmenu', this.onEnd.bind(this));
45 | }
46 |
47 | convertEvent(e) {
48 | e.preventDefault();
49 | e.stopPropagation();
50 |
51 | const t = {
52 | x: 0,
53 | y: 0,
54 | };
55 |
56 | if (!e) {
57 | return t;
58 | }
59 |
60 | if (e.windowsPointer) {
61 | return e;
62 | }
63 |
64 | if (e.touches || e.changedTouches) {
65 | if (e.touches.length) {
66 | t.x = e.touches[0].pageX;
67 | t.y = e.touches[0].pageY;
68 | } else {
69 | t.x = e.changedTouches[0].pageX;
70 | t.y = e.changedTouches[0].pageY;
71 | }
72 | } else {
73 | t.x = e.pageX;
74 | t.y = e.pageY;
75 | }
76 |
77 | t.x = clamp(0, viewport.width, t.x);
78 | t.y = clamp(0, viewport.height, t.y);
79 |
80 | return t;
81 | }
82 |
83 | onStart(event) {
84 | const e = this.convertEvent(event);
85 |
86 | this.isTouching = true;
87 | this.x = e.x;
88 | this.y = e.y;
89 |
90 | this.hold.set(e.x, e.y);
91 | this.last.set(e.x, e.y);
92 | this.delta.set(0, 0);
93 | this.move.set(0, 0);
94 |
95 | this.normalized.x = ((this.x / viewport.width) * 2) - 1;
96 | this.normalized.y = (-(this.y / viewport.height) * 2) + 1;
97 | this.distance = 0;
98 |
99 | bidello.trigger({
100 | name: 'pointerStart'
101 | }, {
102 | pointer: this,
103 | });
104 | }
105 |
106 | onMove(event) {
107 | const e = this.convertEvent(event);
108 |
109 | if (this.isTouching) {
110 | this.move.x = e.x - this.hold.x;
111 | this.move.y = e.y - this.hold.y;
112 | }
113 |
114 | // if (this.last.x !== e.x || this.last.y !== e.y) {
115 | // this.last.set(this.x, this.y);
116 | // }
117 |
118 | this.x = e.x;
119 | this.y = e.y;
120 | this.delta.x = e.x - this.last.x;
121 | this.delta.y = e.y - this.last.y;
122 |
123 | this.distance += this.delta.length();
124 |
125 | this.normalized.x = ((this.x / viewport.width) * 2) - 1;
126 | this.normalized.y = (-(this.y / viewport.height) * 2) + 1;
127 |
128 | this._tmp.x = this.normalized.x;
129 | this._tmp.y = this.normalized.y;
130 | this._tmp.z = 0.5;
131 | this._tmp.unproject(camera);
132 | const dir = this._tmp.sub(camera.position).normalize();
133 | const dist = -camera.position.z / dir.z;
134 | this.world.copy(camera.position).add(dir.multiplyScalar(dist));
135 |
136 | bidello.trigger({
137 | name: 'pointerMove'
138 | }, {
139 | pointer: this,
140 | });
141 |
142 | if (this.isTouching) {
143 | bidello.trigger({
144 | name: 'pointerDrag'
145 | }, {
146 | pointer: this,
147 | });
148 | }
149 | }
150 |
151 | onEnd() {
152 | this.isTouching = false;
153 | this.move.set(0, 0);
154 |
155 | bidello.trigger({
156 | name: 'pointerEnd'
157 | }, {
158 | pointer: this,
159 | });
160 | }
161 | }
162 |
163 | export const pointer = new Pointer();
--------------------------------------------------------------------------------
/src/js/utils/fbo.js:
--------------------------------------------------------------------------------
1 | /*
2 | this.position = new FBO({
3 | width: 128,
4 | height: 128,
5 | name: 'position',
6 | shader: require('./position.frag'),
7 | uniforms: {
8 | uTime: {
9 | value: 0
10 | },
11 | },
12 | });
13 |
14 | this.position.target
15 | this.position.update()
16 | */
17 |
18 | import {
19 | WebGLRenderTarget,
20 | NearestFilter,
21 | DataTexture,
22 | RGBAFormat,
23 | FloatType,
24 | HalfFloatType,
25 | Camera,
26 | Scene,
27 | Mesh,
28 | PlaneBufferGeometry,
29 | MeshBasicMaterial,
30 | } from 'three';
31 | import MagicShader from 'magicshader';
32 | import renderer from '/js/renderer';
33 | import triangle from './triangle';
34 | import camera from '/js/camera';
35 |
36 | export const isAvailable = (() => {
37 | const gl = renderer.getContext();
38 |
39 | if (!gl.getExtension('OES_texture_float')) {
40 | return false;
41 | }
42 |
43 | if (gl.getParameter(gl.MAX_VERTEX_TEXTURE_IMAGE_UNITS) === 0) {
44 | return false;
45 | }
46 |
47 | return true;
48 | })();
49 |
50 | const iOS = /(iPad|iPhone|iPod)/g.test(navigator.userAgent);
51 | const type = iOS ? HalfFloatType : FloatType;
52 |
53 | export default class FBO {
54 | constructor({
55 | width,
56 | height,
57 | data,
58 | name,
59 | shader,
60 | texture,
61 | uniforms = {},
62 | rtOptions = {},
63 | debug = false
64 | }) {
65 | this.options = arguments[0];
66 | this.renderer = renderer;
67 | this.camera = new Camera();
68 | this.scene = new Scene();
69 | this.index = 0;
70 | this.copyData = true;
71 | this.texture = texture || new DataTexture(
72 | data || new Float32Array((width * height * 4)),
73 | width,
74 | height,
75 | RGBAFormat,
76 | FloatType,
77 | // type,
78 | );
79 | this.texture.needsUpdate = true;
80 |
81 | this.rt = [this.createRT(), this.createRT()];
82 |
83 | this.material = new MagicShader({
84 | name: name || 'FBO',
85 | defines: {
86 | RESOLUTION: `vec2(${width.toFixed(1)}, ${height.toFixed(1)})`
87 | },
88 | uniforms: {
89 | ...uniforms,
90 | texture: {
91 | value: this.texture
92 | }
93 | },
94 | vertexShader: `
95 | precision highp float;
96 | attribute vec3 position;
97 |
98 | void main() {
99 | gl_Position = vec4(position, 1.0);
100 | }
101 | `,
102 | fragmentShader: shader || `
103 | precision highp float;
104 | uniform sampler2D texture;
105 |
106 | void main() {
107 | vec2 uv = gl_FragCoord.xy / RESOLUTION.xy;
108 | gl_FragColor = texture2D(texture, uv);
109 | }
110 | `,
111 | });
112 |
113 | this.mesh = new Mesh(triangle, this.material);
114 | this.mesh.frustumCulled = false;
115 | this.scene.add(this.mesh);
116 |
117 | if (this.options.debug) {
118 | this.initDebug();
119 | }
120 | }
121 |
122 | initDebug() {
123 | this.debugGeometry = new PlaneBufferGeometry(2, 2);
124 | this.debugMaterial = new MeshBasicMaterial({
125 | map: this.target,
126 | });
127 |
128 | this.debugMesh = new Mesh(this.debugGeometry, this.debugMaterial);
129 | this.debugMesh.position.set(0, 0, -5);
130 |
131 | camera.add(this.debugMesh);
132 | }
133 |
134 | createRT() {
135 | return new WebGLRenderTarget(this.options.width, this.options.height, Object.assign({
136 | minFilter: NearestFilter,
137 | magFilter: NearestFilter,
138 | stencilBuffer: false,
139 | depthBuffer: false,
140 | depthWrite: false,
141 | depthTest: false,
142 | type,
143 | }, this.options.rtOptions));
144 | }
145 |
146 | get target() {
147 | return this.rt[this.index].texture;
148 | }
149 |
150 | get uniforms() {
151 | return this.material.uniforms;
152 | }
153 |
154 | // TODO: test...
155 | resize(width, height) {
156 | this.material.defines.RESOLUTION = `vec2(${width.toFixed(1)}, ${height.toFixed(1)})`;
157 | this.options.width = width;
158 | this.options.height = height;
159 |
160 | this.rt.forEach(rt => {
161 | rt.setSize(width, height);
162 | });
163 | }
164 |
165 | update(switchBack = true) {
166 | const destIndex = this.index === 0 ? 1 : 0;
167 | const old = this.rt[this.index];
168 | const dest = this.rt[destIndex];
169 |
170 | this.material.uniforms.texture.value = this.copyData ? this.texture : old.texture;
171 |
172 | const oldMainTarget = this.renderer.getRenderTarget();
173 | this.renderer.setRenderTarget(dest);
174 | this.renderer.render(this.scene, this.camera);
175 | switchBack && this.renderer.setRenderTarget(oldMainTarget);
176 |
177 | this.index = destIndex;
178 | this.copyData = false;
179 | }
180 | }
181 |
--------------------------------------------------------------------------------