├── README.md ├── assets ├── env-blur.jpg ├── env.jpg ├── normal.jpg ├── track.mp3 └── track.ogg ├── css └── styles.css ├── facebook.jpg ├── index.html ├── main.js ├── modules ├── easings.js ├── fbo.js ├── gift-box.js ├── maf.js ├── paper.js ├── paper1.js ├── paper10.js ├── paper11.js ├── paper12.js ├── paper13.js ├── paper14.js ├── paper15.js ├── paper16.js ├── paper17.js ├── paper18.js ├── paper19.js ├── paper2.js ├── paper20.js ├── paper3.js ├── paper4.js ├── paper5.js ├── paper6.js ├── paper7.js ├── paper8.js ├── paper9.js ├── post.js ├── scene.js ├── shader-pass.js ├── shader-ping-pong-pass.js └── wrapping-paper.js ├── paper.html ├── preview.jpg ├── shaders ├── blur-fs.js ├── combine-fs.js ├── depth-fs.js ├── depth-vs.js ├── dof-fs.js ├── fast-separable-gaussian-blur.js ├── final-color-fs.js ├── final-fs.js ├── fxaa.js ├── grayscale.js ├── highlight-fs.js ├── levels.js ├── ortho.js ├── overlay.js ├── random.js ├── rgb-shift.js ├── screen.js ├── smootherstep.js ├── soft-light.js └── vignette.js ├── test.js ├── third_party ├── THREE.OrbitControls.js ├── UpdatableTexture.js ├── WebXR.js ├── equirectangular-to-cubemap.js └── three.module.js └── twitter.jpg /README.md: -------------------------------------------------------------------------------- 1 | # Infinite Gift · Christmas Experiments 2018 2 | 3 | A never-ending gift in your browser. 4 | https://christmasexperiments.com/2018/24/infinite-gift/ 5 | 6 | Music by Bent Stamnes (https://twitter.com/gloom303) 7 | 8 | Code by Jaume Sanchez (https://twitter.com/thespite) 9 | 10 | Original wrapping paper generation utils by Roger Pujol (https://twitter.com/rogerpjg) 11 | 12 | Made with three.js. Based on an idea with Roger Pujol (https://twitter.com/rogerpjg) from a few years ago 13 | Supports WebVR/WebXR if available. 14 | 15 | # License to code 16 | 17 | [CC BY-SA](https://creativecommons.org/licenses/by-sa/4.0/) 18 | 19 | This license lets others remix, tweak, and build upon your work even for commercial purposes, as long as they credit you and license their new creations under the identical terms. This license is often compared to “copyleft” free and open source software licenses. All new works based on yours will carry the same license, so any derivatives will also allow commercial use. This is the license used by Wikipedia, and is recommended for materials that would benefit from incorporating content from Wikipedia and similarly licensed projects. 20 | 21 | # License to music 22 | 23 | [CC BY-NC-SA](https://creativecommons.org/licenses/by-nc-sa/4.0/) 24 | 25 | This license lets others remix, tweak, and build upon your work non-commercially, as long as they credit you and license their new creations under the identical terms. 26 | -------------------------------------------------------------------------------- /assets/env-blur.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spite/infinite-gift/eba6257d277a94d4c3d7ac702547394f09da78a5/assets/env-blur.jpg -------------------------------------------------------------------------------- /assets/env.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spite/infinite-gift/eba6257d277a94d4c3d7ac702547394f09da78a5/assets/env.jpg -------------------------------------------------------------------------------- /assets/normal.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spite/infinite-gift/eba6257d277a94d4c3d7ac702547394f09da78a5/assets/normal.jpg -------------------------------------------------------------------------------- /assets/track.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spite/infinite-gift/eba6257d277a94d4c3d7ac702547394f09da78a5/assets/track.mp3 -------------------------------------------------------------------------------- /assets/track.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spite/infinite-gift/eba6257d277a94d4c3d7ac702547394f09da78a5/assets/track.ogg -------------------------------------------------------------------------------- /css/styles.css: -------------------------------------------------------------------------------- 1 | * { 2 | margin: 0; 3 | padding: 0; 4 | box-sizing: border-box; 5 | } 6 | 7 | body { 8 | position: absolute; 9 | display: block; 10 | width: 100%; 11 | height: 100%; 12 | background: black; 13 | } 14 | 15 | #disclaimer { 16 | font-size: 13px; 17 | text-align: center; 18 | font-family: 'Roboto', sans-serif; 19 | line-height: 1.4em; 20 | position: absolute; 21 | left: 0; 22 | right: 0; 23 | bottom: 0; 24 | width: 100%; 25 | padding: 40px; 26 | color: #aaa; 27 | } 28 | 29 | #disclaimer p { 30 | white-space: nowrap; 31 | } 32 | 33 | #disclaimer b { 34 | font-weight: bold; 35 | color: white; 36 | } 37 | 38 | .render { 39 | position: absolute; 40 | left: 0; 41 | top: 0; 42 | right: 0; 43 | bottom: 0; 44 | width: 100%; 45 | height: 100%; 46 | } 47 | 48 | #options { 49 | font-size: 20px; 50 | font-family: 'Berkshire Swash', cursive; 51 | position: absolute; 52 | z-index: 10; 53 | color: white; 54 | left: 0; 55 | top: 0; 56 | right: 0; 57 | bottom: 0; 58 | width: 100%; 59 | height: 100%; 60 | transition: opacity 1s ease-out; 61 | } 62 | 63 | #options #webxr { 64 | display: none; 65 | } 66 | 67 | #presets, 68 | #loading { 69 | text-align: center; 70 | position: absolute; 71 | left: 50%; 72 | top: 50%; 73 | transform: translate3d(-50%, -50%, 0); 74 | } 75 | 76 | #options h1 { 77 | font-size: 80px; 78 | white-space: nowrap; 79 | text-align: center; 80 | position: absolute; 81 | left: 50%; 82 | top: calc(50% - 1em); 83 | transform: translate3d(-50%, -50%, 0); 84 | } 85 | 86 | #options h2 { 87 | font-size: 20px; 88 | white-space: nowrap; 89 | text-align: center; 90 | position: absolute; 91 | left: 50%; 92 | top: calc(50% - 8em); 93 | transform: translate3d(-50%, -50%, 0); 94 | } 95 | 96 | .hidden { 97 | pointer-events: none; 98 | opacity: 0; 99 | } 100 | 101 | .visible { 102 | pointer-events: auto; 103 | opacity: 1; 104 | } 105 | 106 | #presets { 107 | display: none; 108 | margin-top: 3em; 109 | } 110 | 111 | #options a { 112 | color: white; 113 | text-decoration: none; 114 | padding: 1em; 115 | display: inline-block; 116 | transition: color 250ms ease-out; 117 | } 118 | 119 | #options span { 120 | display: block; 121 | } 122 | 123 | #options a:hover { 124 | color: red; 125 | } 126 | 127 | #start { 128 | display: none; 129 | } 130 | 131 | #canvas { 132 | position: absolute; 133 | left: 0; 134 | top: 80px; 135 | transition: opacity 1s ease-out; 136 | } 137 | 138 | @media only screen and (max-width: 600px) { 139 | #options { 140 | font-size: 5vw; 141 | } 142 | 143 | #options h1 { 144 | font-size: 10vw; 145 | } 146 | 147 | #options h2 { 148 | font-size: 4vw; 149 | } 150 | 151 | #presets { 152 | margin-top: 20vw; 153 | } 154 | 155 | #options a { 156 | padding: 3vw; 157 | display: inline-block; 158 | } 159 | 160 | #options span { 161 | white-space: nowrap; 162 | } 163 | } -------------------------------------------------------------------------------- /facebook.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spite/infinite-gift/eba6257d277a94d4c3d7ac702547394f09da78a5/facebook.jpg -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Infinite Gift · Christmas Experiments 2018 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 |
25 |

spite · gloom | christmas experiments

26 |

Infinite Gift

27 |

Loading...

28 |

Low · Standard · HighVR Low · High

29 |
30 |

This is a WebGL ES6 experience. Make sure your browser is updated. Make sure WebXR is supported in order to access the VR experience. Images can cause dizziness.

31 |
32 |
33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /main.js: -------------------------------------------------------------------------------- 1 | import { 2 | renderer, 3 | setSize, 4 | animate, 5 | render, 6 | init, 7 | loadAssets 8 | } from "./modules/scene.js"; 9 | import { detectWebXR, startWebXR } from "./third_party/WebXR.js"; 10 | 11 | const padding = 80; 12 | const presets = document.getElementById("presets"); 13 | const options = document.getElementById("options"); 14 | const loading = document.getElementById("loading"); 15 | 16 | if (!Element.prototype.requestFullscreen) { 17 | Element.prototype.requestFullscreen = 18 | Element.prototype.mozRequestFullScreen || 19 | Element.prototype.webkitRequestFullScreen; 20 | } 21 | 22 | async function start() { 23 | document.body.appendChild(renderer.domElement); 24 | renderer.domElement.className = "hidden render"; 25 | renderer.domElement.id = "canvas"; 26 | resize(); 27 | 28 | let isWebXRSupported = false; 29 | /*try { 30 | await detectWebXR(renderer); 31 | isWebXRSupported = true; 32 | } catch (e) {}*/ 33 | if (isWebXRSupported) { 34 | document.getElementById("webxr").style.display = "block"; 35 | } 36 | await loadAssets(); 37 | loading.style.display = "none"; 38 | presets.style.display = "block"; 39 | const options = presets.querySelectorAll("a"); 40 | for (let option of options) { 41 | option.addEventListener("click", e => { 42 | run( 43 | option.id, 44 | option.id === "vrlow" || option.id === "vrhigh" 45 | ? isWebXRSupported 46 | : null 47 | ); 48 | }); 49 | } 50 | } 51 | 52 | async function run(preset, device) { 53 | if (device) { 54 | renderer.xr.enabled = true; 55 | } 56 | await init(preset); 57 | options.className = "hidden"; 58 | if (device) { 59 | startWebXR(device, renderer); 60 | } 61 | await renderer.domElement.requestFullscreen(); 62 | animate(); 63 | } 64 | 65 | window.addEventListener("resize", e => resize()); 66 | 67 | function resize() { 68 | let w = document.body.clientWidth; 69 | let h = document.body.clientHeight; 70 | h -= 2 * padding; 71 | setSize(w, h); 72 | } 73 | 74 | window.addEventListener("load", start); 75 | -------------------------------------------------------------------------------- /modules/easings.js: -------------------------------------------------------------------------------- 1 | const Easings = { 2 | Linear: function(t) { 3 | return t; 4 | }, 5 | InQuad: function(t) { 6 | return t * t; 7 | }, 8 | OutQuad: function(t) { 9 | return t * (2 - t); 10 | }, 11 | InOutQuad: function(t) { 12 | if ((t *= 2) < 1) { 13 | return 0.5 * t * t; 14 | } 15 | return -0.5 * (--t * (t - 2) - 1); 16 | }, 17 | InCubic: function(t) { 18 | return t * t * t; 19 | }, 20 | OutCubic: function(t) { 21 | return --t * t * t + 1; 22 | }, 23 | InOutCubic: function(t) { 24 | if ((t *= 2) < 1) { 25 | return 0.5 * t * t * t; 26 | } 27 | return 0.5 * ((t -= 2) * t * t + 2); 28 | }, 29 | InQuart: function(t) { 30 | return t * t * t * t; 31 | }, 32 | OutQuart: function(t) { 33 | return 1 - --t * t * t * t; 34 | }, 35 | InOutQuart: function(t) { 36 | if ((t *= 2) < 1) { 37 | return 0.5 * t * t * t * t; 38 | } 39 | return 0.5 * ((t -= 2) * t * t * t - 2); 40 | }, 41 | InQuint: function(t) { 42 | return t * t * t * t * t; 43 | }, 44 | OutQuint: function(t) { 45 | return --t * t * t * t * t + 1; 46 | }, 47 | InOutQuint: function(t) { 48 | if ((t *= 2) < 1) return 0.5 * t * t * t * t * t; 49 | return 0.5 * ((t -= 2) * t * t * t * t + 2); 50 | }, 51 | InSine: function(t) { 52 | return 1 - Math.cos((t * Math.PI) / 2); 53 | }, 54 | OutSine: function(t) { 55 | return Math.sin((t * Math.PI) / 2); 56 | }, 57 | InOutSine: function(t) { 58 | return 0.5 * (1 - Math.cos(Math.PI * t)); 59 | }, 60 | InBounce: function(t) { 61 | return 1 - outBounce(1 - t); 62 | }, 63 | OutBounce: function(t) { 64 | if (t < 0.36363636363636365) { 65 | return 7.5625 * t * t; 66 | } else if (t < 0.7272727272727273) { 67 | t = t - 0.5454545454545454; 68 | return 7.5625 * t * t + 0.75; 69 | } else if (t < 0.9090909090909091) { 70 | t = t - 0.8181818181818182; 71 | return 7.5625 * t * t + 0.9375; 72 | } else { 73 | t = t - 0.9545454545454546; 74 | return 7.5625 * t * t + 0.984375; 75 | } 76 | }, 77 | InOutBounce: function(t) { 78 | if (t < 0.5) { 79 | return Easings.InBounce(t * 2) * 0.5; 80 | } 81 | return Easings.OutBounce(t * 2 - 1) * 0.5 + 1 * 0.5; 82 | }, 83 | InElastic: function(t, amplitude, period) { 84 | if (typeof period == "undefined") { 85 | period = 0; 86 | } 87 | if (typeof amplitude == "undefined") { 88 | amplitude = 1; 89 | } 90 | var offset = 1.70158; 91 | 92 | if (t == 0) return 0; 93 | if (t == 1) return 1; 94 | 95 | if (!period) { 96 | period = 0.3; 97 | } 98 | 99 | if (amplitude < 1) { 100 | amplitude = 1; 101 | offset = period / 4; 102 | } else { 103 | offset = (period / (2 * Math.PI)) * Math.asin(1 / amplitude); 104 | } 105 | 106 | return -( 107 | amplitude * 108 | Math.pow(2, 10 * (t -= 1)) * 109 | Math.sin(((t - offset) * (Math.PI * 2)) / period) 110 | ); 111 | }, 112 | OutElastic: function(t, amplitude, period) { 113 | if (typeof period == "undefined") { 114 | period = 0; 115 | } 116 | if (typeof amplitude == "undefined") { 117 | amplitude = 1; 118 | } 119 | var offset = 1.70158; 120 | 121 | if (t == 0) return 0; 122 | if (t == 1) return 1; 123 | 124 | if (!period) { 125 | period = 0.3; 126 | } 127 | 128 | if (amplitude < 1) { 129 | amplitude = 1; 130 | offset = period / 4; 131 | } else { 132 | offset = (period / (2 * Math.PI)) * Math.asin(1 / amplitude); 133 | } 134 | 135 | return ( 136 | amplitude * 137 | Math.pow(2, -10 * t) * 138 | Math.sin(((t - offset) * (Math.PI * 2)) / period) + 139 | 1 140 | ); 141 | }, 142 | InOutElastic: function(t, amplitude, period) { 143 | var offset; 144 | t = t / 2 - 1; 145 | // escape early for 0 and 1 146 | if (t === 0 || t === 1) { 147 | return t; 148 | } 149 | if (!period) { 150 | period = 0.44999999999999996; 151 | } 152 | if (!amplitude) { 153 | amplitude = 1; 154 | offset = period / 4; 155 | } else { 156 | offset = (period / (Math.PI * 2.0)) * Math.asin(1 / amplitude); 157 | } 158 | return ( 159 | (amplitude * 160 | Math.pow(2, 10 * t) * 161 | Math.sin(((t - offset) * (Math.PI * 2)) / period)) / 162 | -2 163 | ); 164 | }, 165 | InExpo: function(t) { 166 | return Math.pow(2, 10 * (t - 1)); 167 | }, 168 | OutExpo: function(t) { 169 | return -Math.pow(2, -10 * t) + 1; 170 | }, 171 | InOutExpo: function(t) { 172 | if (t == 0) return 0; 173 | if (t == 1) return 1; 174 | if ((t /= 0.5) < 1) return 0.5 * Math.pow(2, 10 * (t - 1)); 175 | return 0.5 * (-Math.pow(2, -10 * --t) + 2); 176 | }, 177 | InCirc: function(t) { 178 | return -1 * (Math.sqrt(1 - t * t) - 1); 179 | }, 180 | OutCirc: function(t) { 181 | t = t - 1; 182 | return Math.sqrt(1 - t * t); 183 | }, 184 | InOutCirc: function(t) { 185 | var c = 1; 186 | if ((t /= 0.5) < 1) return -0.5 * (Math.sqrt(1 - t * t) - 1); 187 | return 0.5 * (Math.sqrt(1 - (t -= 2) * t) + 1); 188 | }, 189 | InBack: function(t, overshoot) { 190 | if (!overshoot && overshoot !== 0) { 191 | overshoot = 1.70158; 192 | } 193 | return 1 * t * t * ((overshoot + 1) * t - overshoot); 194 | }, 195 | OutBack: function(t, overshoot) { 196 | if (!overshoot && overshoot !== 0) { 197 | overshoot = 1.70158; 198 | } 199 | t = t - 1; 200 | return t * t * ((overshoot + 1) * t + overshoot) + 1; 201 | }, 202 | InOutBack: function(t, overshoot) { 203 | if (overshoot == undefined) overshoot = 1.70158; 204 | if ((t /= 0.5) < 1) 205 | return 0.5 * (t * t * (((overshoot *= 1.525) + 1) * t - overshoot)); 206 | return ( 207 | 0.5 * ((t -= 2) * t * (((overshoot *= 1.525) + 1) * t + overshoot) + 2) 208 | ); 209 | } 210 | }; 211 | 212 | const outBounce = Easings.OutBounce; 213 | 214 | export default Easings; 215 | -------------------------------------------------------------------------------- /modules/fbo.js: -------------------------------------------------------------------------------- 1 | import { 2 | WebGLRenderTarget, 3 | ClampToEdgeWrapping, 4 | LinearFilter, 5 | RGBAFormat, 6 | UnsignedByteType 7 | } from "../third_party/three.module.js"; 8 | 9 | function getFBO(w, h, options = {}) { 10 | const fbo = new WebGLRenderTarget(w, h, { 11 | wrapS: options.wrapS || ClampToEdgeWrapping, 12 | wrapT: options.wrapT || ClampToEdgeWrapping, 13 | minFilter: options.minFilter || LinearFilter, 14 | magFilter: options.magFilter || LinearFilter, 15 | format: options.format || RGBAFormat, 16 | type: options.type || UnsignedByteType, 17 | stencilBuffer: options.stencilBuffer || false, 18 | depthBuffer: options.depthBuffer || true 19 | }); 20 | return fbo; 21 | } 22 | 23 | export { getFBO }; 24 | -------------------------------------------------------------------------------- /modules/gift-box.js: -------------------------------------------------------------------------------- 1 | import { 2 | Mesh, 3 | Group, 4 | DoubleSide, 5 | PlaneGeometry, 6 | BufferAttribute, 7 | BufferGeometry, 8 | MeshBasicMaterial, 9 | Geometry, 10 | MeshNormalMaterial, 11 | MeshPhongMaterial, 12 | MeshPhysicalMaterial, 13 | Vector3, 14 | Vector2, 15 | Face3, 16 | Matrix4, 17 | RawShaderMaterial 18 | } from "../third_party/three.module.js"; 19 | import { Maf } from "./maf.js"; 20 | 21 | import { vs as depthVertexShader } from "../shaders/depth-vs.js"; 22 | import { fs as depthFragmentShader } from "../shaders/depth-fs.js"; 23 | 24 | function merge() { 25 | const total = [...arguments].reduce( 26 | (accumulator, geometry) => accumulator + geometry.attributes.position.count, 27 | 0 28 | ); 29 | const res = new BufferGeometry(); 30 | res.setAttribute( 31 | "position", 32 | new BufferAttribute(new Float32Array(total * 3), 3) 33 | ); 34 | res.setAttribute( 35 | "normal", 36 | new BufferAttribute(new Float32Array(total * 3), 3) 37 | ); 38 | res.setAttribute("uv", new BufferAttribute(new Float32Array(total * 2), 2)); 39 | let ptr = 0; 40 | [...arguments].forEach((geometry, id) => { 41 | res.merge(geometry, ptr); 42 | ptr += geometry.attributes.position.count; 43 | }); 44 | return res; 45 | } 46 | 47 | function quad( 48 | x0, 49 | y0, 50 | z0, 51 | x1, 52 | y1, 53 | z1, 54 | x2, 55 | y2, 56 | z2, 57 | x3, 58 | y3, 59 | z3, 60 | u0, 61 | v0, 62 | u1, 63 | v1, 64 | u2, 65 | v2, 66 | u3, 67 | v3, 68 | u, 69 | v 70 | ) { 71 | const geometry = new BufferGeometry().fromGeometry(new PlaneGeometry(1, 1)); 72 | const pos = geometry.attributes.position.array; 73 | pos[0] = x0; 74 | pos[1] = y0; 75 | pos[2] = z0; 76 | pos[3] = pos[9] = x1; 77 | pos[4] = pos[10] = y1; 78 | pos[5] = pos[11] = z1; 79 | pos[6] = pos[15] = x3; 80 | pos[7] = pos[16] = y3; 81 | pos[8] = pos[17] = z3; 82 | pos[12] = x2; 83 | pos[13] = y2; 84 | pos[14] = z2; 85 | geometry.computeVertexNormals(); 86 | const uvs = geometry.attributes.uv.array; 87 | const ratio = v / u; 88 | uvs[0] = u0; 89 | uvs[1] = v0 * ratio; 90 | uvs[2] = u1; 91 | uvs[3] = v1 * ratio; 92 | uvs[4] = u3; 93 | uvs[5] = v3 * ratio; 94 | uvs[6] = u1; 95 | uvs[7] = v1 * ratio; 96 | uvs[8] = u2; 97 | uvs[9] = v2 * ratio; 98 | uvs[10] = u3; 99 | uvs[11] = v3 * ratio; 100 | return geometry; 101 | } 102 | 103 | class GiftBox extends Group { 104 | constructor(material) { 105 | super(); 106 | 107 | this.padding = 0.01; 108 | 109 | this.colorMaterial = 110 | material === "phong" 111 | ? new MeshPhongMaterial({}) 112 | : new MeshPhysicalMaterial({ 113 | color: 0xffffff, 114 | roughness: 0.5, 115 | metalness: 0.5, 116 | emissive: 0x202020 117 | }); 118 | this.material = this.colorMaterial; 119 | 120 | this.rimMaterial = new MeshBasicMaterial({ color: 0xffffff }); 121 | 122 | this.depthMaterial = new RawShaderMaterial({ 123 | vertexShader: depthVertexShader, 124 | fragmentShader: depthFragmentShader 125 | }); 126 | 127 | const parts = 8; 128 | const w = ~~(Maf.randomInRange(0.75, 1) * parts) / parts; 129 | const d = ~~(Maf.randomInRange(0.75, 1) * parts) / parts; 130 | const h = ~~(Maf.randomInRange(0.5, 0.75) * 16) / 16; 131 | const p = this.padding; 132 | 133 | const { boxGeometry, rimGeometry } = this.getGeometry(w, h, d, p); 134 | boxGeometry.applyMatrix(new Matrix4().makeRotationX(Maf.PI / 2)); 135 | this.box = new Mesh(boxGeometry, this.material); 136 | this.box.castShadow = true; 137 | this.box.receiveShadow = true; 138 | this.add(this.box); 139 | 140 | this.rim = new Mesh(rimGeometry, this.rimMaterial); 141 | this.rim.castShadow = true; 142 | this.rim.receiveShadow = true; 143 | this.add(this.rim); 144 | 145 | this.pivot = new Group(); 146 | const { 147 | boxGeometry: lidBoxGeometry, 148 | rimGeometry: lidRimGeometry 149 | } = this.getGeometry(w + 2 * p, 0.25 * h, d + 2 * p, p); 150 | lidBoxGeometry.applyMatrix(new Matrix4().makeRotationX(-Maf.PI / 2)); 151 | lidBoxGeometry.applyMatrix(new Matrix4().makeTranslation(0, 0, 0.5 * h)); 152 | this.lid = new Mesh(lidBoxGeometry, this.material); 153 | this.lid.castShadow = true; 154 | this.lid.receiveShadow = true; 155 | this.lid.position.x = -1; 156 | this.pivot.add(this.lid); 157 | this.lidRim = new Mesh(lidRimGeometry, this.rimMaterial); 158 | this.lidRim.castShadow = true; 159 | this.lidRim.receiveShadow = true; 160 | this.lidRim.position.x = -1; 161 | this.pivot.add(this.lidRim); 162 | this.pivot.rotation.y = 0; 163 | this.add(this.pivot); 164 | } 165 | 166 | orientateLid() { 167 | this.pivot.position.x = 1; 168 | } 169 | 170 | refresh() { 171 | const parts = 4; 172 | const w = ~~(Maf.randomInRange(0.75, 1) * parts) / parts; 173 | const d = ~~(Maf.randomInRange(0.75, 1) * parts) / parts; 174 | const h = ~~(Maf.randomInRange(0.5, 0.75) * parts) / parts; 175 | const p = this.padding; 176 | 177 | const { boxGeometry, rimGeometry } = this.getGeometry(w, h, d, p); 178 | boxGeometry.applyMatrix(new Matrix4().makeRotationX(Maf.PI / 2)); 179 | this.box.geometry = boxGeometry; 180 | this.box.geometry.needsUpdate = true; 181 | rimGeometry.applyMatrix(new Matrix4().makeRotationX(Maf.PI / 2)); 182 | this.rim.geometry = rimGeometry; 183 | this.rim.geometry.needsUpdate = true; 184 | 185 | const { 186 | boxGeometry: lidBoxGeometry, 187 | rimGeometry: lidRimGeometry 188 | } = this.getGeometry(w + 2 * p, 0.25 * h, d + 2 * p, p); 189 | lidBoxGeometry.applyMatrix(new Matrix4().makeRotationX(-Maf.PI / 2)); 190 | lidBoxGeometry.applyMatrix(new Matrix4().makeTranslation(0, 0, 0.5 * h)); 191 | this.lid.geometry = lidBoxGeometry; 192 | this.lid.geometry.needsUpdate = true; 193 | lidRimGeometry.applyMatrix(new Matrix4().makeRotationX(-Maf.PI / 2)); 194 | lidRimGeometry.applyMatrix(new Matrix4().makeTranslation(0, 0, 0.5 * h)); 195 | this.lidRim.geometry = lidRimGeometry; 196 | this.lidRim.geometry.needsUpdate = true; 197 | } 198 | 199 | getGeometry(w, h, d, p) { 200 | const hw = 0.5 * w; 201 | const hh = 0.5 * h; 202 | const hd = 0.5 * d; 203 | 204 | const boxGeometry = merge( 205 | quad( 206 | -hw, 207 | -hh, 208 | -hd, 209 | hw, 210 | -hh, 211 | -hd, 212 | hw, 213 | -hh, 214 | hd, 215 | -hw, 216 | -hh, 217 | hd, 218 | 0, 219 | 0, 220 | 1, 221 | 0, 222 | 1, 223 | 1, 224 | 0, 225 | 1, 226 | d, 227 | w 228 | ), 229 | 230 | quad( 231 | -hw, 232 | -hh, 233 | -hd, 234 | -hw, 235 | -hh, 236 | hd, 237 | -hw, 238 | hh, 239 | hd, 240 | -hw, 241 | hh, 242 | -hd, 243 | 0, 244 | 0, 245 | 1, 246 | 0, 247 | 1, 248 | 1, 249 | 0, 250 | 1, 251 | d, 252 | h 253 | ), 254 | quad( 255 | hw, 256 | -hh, 257 | hd, 258 | hw, 259 | -hh, 260 | -hd, 261 | hw, 262 | hh, 263 | -hd, 264 | hw, 265 | hh, 266 | hd, 267 | 0, 268 | 0, 269 | 1, 270 | 0, 271 | 1, 272 | 1, 273 | 0, 274 | 1, 275 | d, 276 | h 277 | ), 278 | quad( 279 | hw, 280 | -hh, 281 | -hd, 282 | -hw, 283 | -hh, 284 | -hd, 285 | -hw, 286 | hh, 287 | -hd, 288 | hw, 289 | hh, 290 | -hd, 291 | 0, 292 | 0, 293 | 1, 294 | 0, 295 | 1, 296 | 1, 297 | 0, 298 | 1, 299 | w, 300 | h 301 | ), 302 | quad( 303 | -hw, 304 | -hh, 305 | hd, 306 | hw, 307 | -hh, 308 | hd, 309 | hw, 310 | hh, 311 | hd, 312 | -hw, 313 | hh, 314 | hd, 315 | 0, 316 | 0, 317 | 1, 318 | 0, 319 | 1, 320 | 1, 321 | 0, 322 | 1, 323 | w, 324 | h 325 | ), 326 | 327 | quad( 328 | hw - p, 329 | -(hh - p), 330 | -(hd - p), 331 | -(hw - p), 332 | -(hh - p), 333 | -(hd - p), 334 | -(hw - p), 335 | -(hh - p), 336 | hd - p, 337 | hw - p, 338 | -(hh - p), 339 | hd - p, 340 | 0, 341 | 0, 342 | 1, 343 | 0, 344 | 1, 345 | 1, 346 | 0, 347 | 1, 348 | w - 2 * p, 349 | d - 2 * p 350 | ), 351 | 352 | quad( 353 | -(hw - p), 354 | -(hh - p), 355 | hd - p, 356 | -(hw - p), 357 | -(hh - p), 358 | -(hd - p), 359 | -(hw - p), 360 | hh, 361 | -(hd - p), 362 | -(hw - p), 363 | hh, 364 | hd - p, 365 | 0, 366 | 0, 367 | 1, 368 | 0, 369 | 1, 370 | 1, 371 | 0, 372 | 1, 373 | d - 2 * p, 374 | h - 2 * p 375 | ), 376 | quad( 377 | hw - p, 378 | -(hh - p), 379 | -(hd - p), 380 | hw - p, 381 | -(hh - p), 382 | hd - p, 383 | hw - p, 384 | hh, 385 | hd - p, 386 | hw - p, 387 | hh, 388 | -(hd - p), 389 | 0, 390 | 0, 391 | 1, 392 | 0, 393 | 1, 394 | 1, 395 | 0, 396 | 1, 397 | d - 2 * p, 398 | h - 2 * p 399 | ), 400 | quad( 401 | hw - p, 402 | -(hh - p), 403 | hd - p, 404 | -(hw - p), 405 | -(hh - p), 406 | hd - p, 407 | -(hw - p), 408 | hh, 409 | hd - p, 410 | hw - p, 411 | hh, 412 | hd - p, 413 | 0, 414 | 0, 415 | 1, 416 | 0, 417 | 1, 418 | 1, 419 | 0, 420 | 1, 421 | w - 2 * p, 422 | h - 2 * p 423 | ), 424 | quad( 425 | -(hw - p), 426 | -(hh - p), 427 | -(hd - p), 428 | hw - p, 429 | -(hh - p), 430 | -(hd - p), 431 | hw - p, 432 | hh, 433 | -(hd - p), 434 | -(hw - p), 435 | hh, 436 | -(hd - p), 437 | 0, 438 | 0, 439 | 1, 440 | 0, 441 | 1, 442 | 1, 443 | 0, 444 | 1, 445 | w - 2 * p, 446 | h - 2 * p 447 | ) 448 | ); 449 | 450 | const rimGeometry = merge( 451 | quad( 452 | hw, 453 | hh, 454 | -hd, 455 | -hw, 456 | hh, 457 | -hd, 458 | -(hw - p), 459 | hh, 460 | -(hd - p), 461 | hw - p, 462 | hh, 463 | -(hd - p), 464 | 0, 465 | 0, 466 | 0, 467 | 0, 468 | 0, 469 | 0, 470 | 0, 471 | 0, 472 | w, 473 | d 474 | ), 475 | quad( 476 | -hw, 477 | hh, 478 | hd, 479 | hw, 480 | hh, 481 | hd, 482 | hw - p, 483 | hh, 484 | hd - p, 485 | -(hw - p), 486 | hh, 487 | hd - p, 488 | 0, 489 | 0, 490 | 0, 491 | 0, 492 | 0, 493 | 0, 494 | 0, 495 | 0, 496 | w, 497 | d 498 | ), 499 | quad( 500 | -hw, 501 | hh, 502 | -hd, 503 | -hw, 504 | hh, 505 | hd, 506 | -(hw - p), 507 | hh, 508 | hd - p, 509 | -(hw - p), 510 | hh, 511 | -(hd - p), 512 | 0, 513 | 0, 514 | 0, 515 | 0, 516 | 0, 517 | 0, 518 | 0, 519 | 0, 520 | w, 521 | d 522 | ), 523 | quad( 524 | hw, 525 | hh, 526 | hd, 527 | hw, 528 | hh, 529 | -hd, 530 | hw - p, 531 | hh, 532 | -(hd - p), 533 | hw - p, 534 | hh, 535 | hd - p, 536 | 0, 537 | 0, 538 | 0, 539 | 0, 540 | 0, 541 | 0, 542 | 0, 543 | 0, 544 | w, 545 | d 546 | ) 547 | ); 548 | 549 | return { boxGeometry, rimGeometry }; 550 | } 551 | } 552 | 553 | export { GiftBox }; 554 | -------------------------------------------------------------------------------- /modules/maf.js: -------------------------------------------------------------------------------- 1 | const Maf = {}; 2 | 3 | // Current version. 4 | Maf.VERSION = "1.0.0"; 5 | 6 | Maf.PI = Math.PI; 7 | Maf.TAU = 2 * Maf.PI; 8 | 9 | // https://www.opengl.org/sdk/docs/man/html/clamp.xhtml 10 | 11 | Maf.clamp = function(v, minVal, maxVal) { 12 | return Math.min(maxVal, Math.max(minVal, v)); 13 | }; 14 | 15 | // https://www.opengl.org/sdk/docs/man/html/step.xhtml 16 | 17 | Maf.step = function(edge, v) { 18 | return v < edge ? 0 : 1; 19 | }; 20 | 21 | // https://www.opengl.org/sdk/docs/man/html/smoothstep.xhtml 22 | 23 | Maf.smoothStep = function(edge0, edge1, v) { 24 | var t = Maf.clamp((v - edge0) / (edge1 - edge0), 0.0, 1.0); 25 | return t * t * (3.0 - 2.0 * t); 26 | }; 27 | 28 | // http://docs.unity3d.com/ScriptReference/Mathf.html 29 | // http://www.shaderific.com/glsl-functions/ 30 | // https://www.opengl.org/sdk/docs/man4/html/ 31 | // https://msdn.microsoft.com/en-us/library/windows/desktop/ff471376(v=vs.85).aspx 32 | // http://moutjs.com/docs/v0.11/math.html#map 33 | // https://code.google.com/p/kuda/source/browse/public/js/hemi/utils/mathUtils.js?r=8d581c02651077c4ac3f5fc4725323210b6b13cc 34 | 35 | // Converts from degrees to radians. 36 | Maf.deg2Rad = function(degrees) { 37 | return (degrees * Math.PI) / 180; 38 | }; 39 | 40 | Maf.toRadians = Maf.deg2Rad; 41 | 42 | // Converts from radians to degrees. 43 | Maf.rad2Deg = function(radians) { 44 | return (radians * 180) / Math.PI; 45 | }; 46 | 47 | Maf.toDegrees = Maf.rad2Deg; 48 | 49 | Maf.clamp01 = function(v) { 50 | return Maf.clamp(v, 0, 1); 51 | }; 52 | 53 | // https://www.opengl.org/sdk/docs/man/html/mix.xhtml 54 | 55 | Maf.mix = function(x, y, a) { 56 | if (a <= 0) return x; 57 | if (a >= 1) return y; 58 | return x + a * (y - x); 59 | }; 60 | 61 | Maf.lerp = Maf.mix; 62 | 63 | Maf.inverseMix = function(a, b, v) { 64 | return (v - a) / (b - a); 65 | }; 66 | 67 | Maf.inverseLerp = Maf.inverseMix; 68 | 69 | Maf.mixUnclamped = function(x, y, a) { 70 | if (a <= 0) return x; 71 | if (a >= 1) return y; 72 | return x + a * (y - x); 73 | }; 74 | 75 | Maf.lerpUnclamped = Maf.mixUnclamped; 76 | 77 | // https://www.opengl.org/sdk/docs/man/html/fract.xhtml 78 | 79 | Maf.fract = function(v) { 80 | return v - Math.floor(v); 81 | }; 82 | 83 | Maf.frac = Maf.fract; 84 | 85 | // http://stackoverflow.com/questions/4965301/finding-if-a-number-is-a-power-of-2 86 | 87 | Maf.isPowerOfTwo = function(v) { 88 | return ((v - 1) & v) == 0; 89 | }; 90 | 91 | // https://bocoup.com/weblog/find-the-closest-power-of-2-with-javascript 92 | 93 | Maf.closestPowerOfTwo = function(v) { 94 | return Math.pow(2, Math.round(Math.log(v) / Math.log(2))); 95 | }; 96 | 97 | Maf.nextPowerOfTwo = function(v) { 98 | return Math.pow(2, Math.ceil(Math.log(v) / Math.log(2))); 99 | }; 100 | 101 | // http://stackoverflow.com/questions/1878907/the-smallest-difference-between-2-angles 102 | 103 | //function mod(a, n) { return a - Math.floor(a/n) * n; } 104 | Maf.mod = function(a, n) { 105 | return ((a % n) + n) % n; 106 | }; 107 | 108 | Maf.deltaAngle = function(a, b) { 109 | var d = Maf.mod(b - a, 360); 110 | if (d > 180) d = Math.abs(d - 360); 111 | return d; 112 | }; 113 | 114 | Maf.deltaAngleDeg = Maf.deltaAngle; 115 | 116 | Maf.deltaAngleRad = function(a, b) { 117 | return Maf.toRadians(Maf.deltaAngle(Maf.toDegrees(a), Maf.toDegrees(b))); 118 | }; 119 | 120 | Maf.lerpAngle = function(a, b, t) { 121 | var angle = Maf.deltaAngle(a, b); 122 | return Maf.mod(a + Maf.lerp(0, angle, t), 360); 123 | }; 124 | 125 | Maf.lerpAngleDeg = Maf.lerpAngle; 126 | 127 | Maf.lerpAngleRad = function(a, b, t) { 128 | return Maf.toRadians(Maf.lerpAngleDeg(Maf.toDegrees(a), Maf.toDegrees(b), t)); 129 | }; 130 | 131 | // http://gamedev.stackexchange.com/questions/74324/gamma-space-and-linear-space-with-shader 132 | 133 | Maf.gammaToLinearSpace = function(v) { 134 | return Math.pow(v, 2.2); 135 | }; 136 | 137 | Maf.linearToGammaSpace = function(v) { 138 | return Math.pow(v, 1 / 2.2); 139 | }; 140 | 141 | Maf.map = function(from1, to1, from2, to2, v) { 142 | return from2 + ((v - from1) * (to2 - from2)) / (to1 - from1); 143 | }; 144 | 145 | Maf.scale = Maf.map; 146 | 147 | // http://www.iquilezles.org/www/articles/functions/functions.htm 148 | 149 | Maf.almostIdentity = function(x, m, n) { 150 | if (x > m) return x; 151 | 152 | var a = 2 * n - m; 153 | var b = 2 * m - 3 * n; 154 | var t = x / m; 155 | 156 | return (a * t + b) * t * t + n; 157 | }; 158 | 159 | Maf.impulse = function(k, x) { 160 | var h = k * x; 161 | return h * Math.exp(1 - h); 162 | }; 163 | 164 | Maf.cubicPulse = function(c, w, x) { 165 | x = Math.abs(x - c); 166 | if (x > w) return 0; 167 | x /= w; 168 | return 1 - x * x * (3 - 2 * x); 169 | }; 170 | 171 | Maf.expStep = function(x, k, n) { 172 | return Math.exp(-k * Math.pow(x, n)); 173 | }; 174 | 175 | Maf.parabola = function(x, k) { 176 | return Math.pow(4 * x * (1 - x), k); 177 | }; 178 | 179 | Maf.powerCurve = function(x, a, b) { 180 | var k = Math.pow(a + b, a + b) / (Math.pow(a, a) * Math.pow(b, b)); 181 | return k * Math.pow(x, a) * Math.pow(1 - x, b); 182 | }; 183 | 184 | // http://iquilezles.org/www/articles/smin/smin.htm ? 185 | 186 | Maf.latLonToCartesian = function(lat, lon) { 187 | lon += 180; 188 | lat = Maf.clamp(lat, -85, 85); 189 | var phi = Maf.toRadians(90 - lat); 190 | var theta = Maf.toRadians(180 - lon); 191 | var x = Math.sin(phi) * Math.cos(theta); 192 | var y = Math.cos(phi); 193 | var z = Math.sin(phi) * Math.sin(theta); 194 | 195 | return { x: x, y: y, z: z }; 196 | }; 197 | 198 | Maf.cartesianToLatLon = function(x, y, z) { 199 | var n = Math.sqrt(x * x + y * y + z * z); 200 | return { lat: Math.asin(z / n), lon: Math.atan2(y, x) }; 201 | }; 202 | 203 | Maf.randomInRange = function(min, max) { 204 | return min + Math.random() * (max - min); 205 | }; 206 | 207 | Maf.norm = function(v, minVal, maxVal) { 208 | return (v - minVal) / (maxVal - minVal); 209 | }; 210 | 211 | Maf.hash = function(n) { 212 | return Maf.fract((1.0 + Math.cos(n)) * 415.92653); 213 | }; 214 | 215 | Maf.noise2d = function(x, y) { 216 | var xhash = Maf.hash(x * 37.0); 217 | var yhash = Maf.hash(y * 57.0); 218 | return Maf.fract(xhash + yhash); 219 | }; 220 | 221 | // http://iquilezles.org/www/articles/smin/smin.htm 222 | 223 | Maf.smoothMin = function(a, b, k) { 224 | var res = Math.exp(-k * a) + Math.exp(-k * b); 225 | return -Math.log(res) / k; 226 | }; 227 | 228 | Maf.smoothMax = function(a, b, k) { 229 | return Math.log(Math.exp(a) + Math.exp(b)) / k; 230 | }; 231 | 232 | Maf.almost = function(a, b) { 233 | return Math.abs(a - b) < 0.0001; 234 | }; 235 | 236 | export { Maf }; 237 | -------------------------------------------------------------------------------- /modules/paper.js: -------------------------------------------------------------------------------- 1 | console.log("PAPER WORKER"); 2 | 3 | let papers; 4 | 5 | onmessage = function(e) { 6 | console.log("Message received from main script", e.data); 7 | if (e.data.fn) { 8 | const f = eval(JSON.parse(e.data.fn)); 9 | f.render(); 10 | debugger; 11 | } 12 | /*var workerResult = 'Result: ' + (e.data[0] * e.data[1]); 13 | console.log('Posting message back to main script'); 14 | postMessage(workerResult);*/ 15 | }; 16 | -------------------------------------------------------------------------------- /modules/paper1.js: -------------------------------------------------------------------------------- 1 | import { WrappingPaper } from "./wrapping-paper.js"; 2 | 3 | class Paper extends WrappingPaper { 4 | constructor(w, h) { 5 | super(w, h); 6 | 7 | var colorPalette = ["#ebe9e4", "#f56f34", "#ee4537", "#372c2c"]; 8 | 9 | const line = "#c5c5c5"; 10 | 11 | this.drawRect(0, 0, w, h, "#fff", "#000"); 12 | 13 | function d(x, y, r, c) { 14 | var paletteIndex = Math.floor(Math.random() * colorPalette.length); 15 | this.drawCircle(x, y, r, colorPalette[paletteIndex], { 16 | specularColor: "#fff" 17 | }); 18 | this.colorCtx.beginPath(); 19 | this.colorCtx.lineWidth = 0.5; 20 | this.colorCtx.strokeStyle = 0xc5c5c5; 21 | this.colorCtx.arc(x, y, r, 0, 2 * Math.PI); 22 | this.colorCtx.stroke(); 23 | } 24 | 25 | const _d = d.bind(this); 26 | const f = w / 64; 27 | 28 | for (var i = 0; i < f; i++) { 29 | for (var j = 0; j < f; j++) { 30 | var paletteIndex = Math.floor(Math.random() * colorPalette.length); 31 | var x = i * (w / f), 32 | y = j * (h / f), 33 | r = (0.5 * w) / f, 34 | m = Math.floor(Math.random() * 3); 35 | switch (m) { 36 | case 0: 37 | _d(x + (0.5 * w) / f, y + (0.5 * h) / f, r); 38 | break; 39 | case 1: 40 | var rr = r / 2; 41 | for (var ii = 0; ii < 2; ii++) { 42 | for (var jj = 0; jj < 2; jj++) { 43 | _d(x + rr + 2 * ii * rr, y + rr + jj * 2 * rr, rr); 44 | } 45 | } 46 | break; 47 | case 2: 48 | var rr = r / 3; 49 | for (var ii = 0; ii < 3; ii++) { 50 | for (var jj = 0; jj < 3; jj++) { 51 | var paletteIndex = Math.floor( 52 | Math.random() * colorPalette.length 53 | ); 54 | _d(x + rr + 2 * ii * rr, y + rr + jj * 2 * rr, rr); 55 | } 56 | } 57 | break; 58 | } 59 | } 60 | } 61 | } 62 | } 63 | 64 | export { Paper }; 65 | -------------------------------------------------------------------------------- /modules/paper10.js: -------------------------------------------------------------------------------- 1 | import { WrappingPaper } from "./wrapping-paper.js"; 2 | 3 | class Paper extends WrappingPaper { 4 | constructor(w, h) { 5 | super(w, h); 6 | 7 | const opts1 = { specularColor: "#000" }; 8 | 9 | const colorPalette1 = [ 10 | "#d88559", 11 | "#dda8c4", 12 | "#c1454d", 13 | "#8cc6dc", 14 | "#ead562", 15 | "#fefff8" 16 | ]; 17 | 18 | const colorPalette2 = [ 19 | "#f97a4d", 20 | "#4199bd", 21 | "#455b69", 22 | "#c33a4e", 23 | "#eaeaec", 24 | "#e2e5de" 25 | ]; 26 | 27 | const palettes = [colorPalette1, colorPalette2]; 28 | const colorPalette = palettes[~~(Math.random() * palettes.length)]; 29 | 30 | const width = 32; 31 | const height = 32; 32 | 33 | this.drawRect(0, 0, w, h, "#fff", opts1); 34 | for (let y = -1; y < h / height + 1; y++) { 35 | for (let x = -1; x < w / width + 1; x++) { 36 | const path1 = new Path2D(); 37 | const path2 = new Path2D(); 38 | if ((x + y) % 2) { 39 | path1.moveTo(x * width, y * height); 40 | path1.lineTo((x + 1) * width, y * height); 41 | path1.lineTo(x * width, (y + 1) * height); 42 | path2.moveTo((x + 1) * width, y * height); 43 | path2.lineTo((x + 1) * width, (y + 1) * height); 44 | path2.lineTo(x * width, (y + 1) * height); 45 | } else { 46 | path1.moveTo(x * width, y * height); 47 | path1.lineTo((x + 1) * width, y * height); 48 | path1.lineTo((x + 1) * width, (y + 1) * height); 49 | path2.moveTo(x * width, y * height); 50 | path2.lineTo(x * width, (y + 1) * height); 51 | path2.lineTo((x + 1) * width, (y + 1) * height); 52 | } 53 | 54 | const id = ~~(Math.random() * colorPalette.length); 55 | this.colorCtx.fillStyle = colorPalette[id]; 56 | this.colorCtx.fill(path1); 57 | let id2 = id; 58 | while (id2 === id) { 59 | id2 = ~~(Math.random() * colorPalette.length); 60 | } 61 | this.colorCtx.fillStyle = colorPalette[id2]; 62 | this.colorCtx.fill(path2); 63 | 64 | let c = ~~(Math.random() * 255); 65 | this.roughnessCtx.fillStyle = `rgb(${c},${c},${c})`; 66 | this.roughnessCtx.fill(path1); 67 | c = ~~(Math.random() * 255); 68 | this.roughnessCtx.fillStyle = `rgb(${c},${c},${c})`; 69 | this.roughnessCtx.fill(path2); 70 | } 71 | } 72 | } 73 | } 74 | 75 | export { Paper }; 76 | -------------------------------------------------------------------------------- /modules/paper11.js: -------------------------------------------------------------------------------- 1 | import { WrappingPaper } from "./wrapping-paper.js"; 2 | 3 | class Paper extends WrappingPaper { 4 | constructor(w, h) { 5 | super(w, h); 6 | 7 | //object that defines the specular level for the specular image 8 | const opts1 = { specularColor: "#fff" }; 9 | 10 | const colorRim = "#4f919b"; 11 | const specColorRim = "#ddd"; 12 | const colorPalette1 = ["#d24b58", "#c34e55", "#cbe2ee", "#ecf0f3"]; 13 | 14 | var colorPalette2 = ["#f6f6f6", "#ff9b00", "#ff3200", "#02827a"]; 15 | 16 | const palettes = [colorPalette1, colorPalette2]; 17 | const colorPalette = palettes[~~(Math.random() * palettes.length)]; 18 | 19 | const specPalette = ["#eee", "#aaa", "#888", "#666", "#444"]; 20 | 21 | this.drawRect(0, 0, w, h, "#340a2d", "#000"); 22 | 23 | const width = 4 * 48; 24 | const height = 4 * 32; 25 | 26 | const path = new Path2D(); 27 | path.moveTo(0, 0.5 * height); 28 | path.lineTo(0.25 * width, 0); 29 | path.lineTo(0.5 * width, 0); 30 | path.lineTo(0.75 * width, 0.5 * height); 31 | path.lineTo(0.5 * width, height); 32 | path.lineTo(0.25 * width, height); 33 | path.lineTo(0, 0.5 * height); 34 | 35 | function draw(x, y) { 36 | this.colorCtx.save(); 37 | this.colorCtx.translate(x * width, y * height); 38 | this.colorCtx.fillStyle = colorRim; 39 | this.colorCtx.fill(path); 40 | this.colorCtx.restore(); 41 | this.colorCtx.save(); 42 | this.colorCtx.translate((x + 1) * width, (y + 1) * height); 43 | this.colorCtx.fillStyle = colorRim; 44 | this.colorCtx.fill(path); 45 | this.colorCtx.restore(); 46 | const colors = colorPalette.sort((a, b) => 47 | Math.random() > 0.5 ? 1 : -1 48 | ); 49 | let inc = 0.5 * 0.25; 50 | let scale = 0.75; 51 | const aspect = width / height; 52 | for (let c of colors) { 53 | this.colorCtx.save(); 54 | this.colorCtx.translate( 55 | (x + inc) * width, 56 | (y + 0.95 * inc * aspect) * height 57 | ); 58 | this.colorCtx.scale(scale, scale); 59 | this.colorCtx.fillStyle = c; 60 | this.colorCtx.fill(path); 61 | this.colorCtx.restore(); 62 | inc += 0.75 * 0.125 * scale; 63 | scale *= 0.75; 64 | } 65 | this.roughnessCtx.save(); 66 | this.roughnessCtx.translate(x * width, y * height); 67 | this.roughnessCtx.fillStyle = specColorRim; 68 | this.roughnessCtx.fill(path); 69 | this.roughnessCtx.restore(); 70 | this.roughnessCtx.save(); 71 | this.roughnessCtx.translate((x + 1) * width, (y + 1) * height); 72 | this.roughnessCtx.fillStyle = specColorRim; 73 | this.roughnessCtx.fill(path); 74 | this.roughnessCtx.restore(); 75 | const colors2 = specPalette.sort((a, b) => 76 | Math.random() > 0.5 ? 1 : -1 77 | ); 78 | inc = 0.5 * 0.25; 79 | scale = 0.75; 80 | for (let c of colors2) { 81 | this.roughnessCtx.save(); 82 | this.roughnessCtx.translate( 83 | (x + inc) * width, 84 | (y + 0.95 * inc * aspect) * height 85 | ); 86 | this.roughnessCtx.scale(scale, scale); 87 | this.roughnessCtx.fillStyle = c; 88 | this.roughnessCtx.fill(path); 89 | this.roughnessCtx.restore(); 90 | inc += 0.75 * 0.125 * scale; 91 | scale *= 0.75; 92 | } 93 | } 94 | 95 | const _d = draw.bind(this); 96 | 97 | let id; 98 | for (let y = -1; y < h / height; y++) { 99 | for (let x = -1; x < w / width; x++) { 100 | _d(x, y); 101 | _d(x + 0.5, y + 0.5); 102 | } 103 | } 104 | } 105 | } 106 | export { Paper }; 107 | -------------------------------------------------------------------------------- /modules/paper12.js: -------------------------------------------------------------------------------- 1 | import { WrappingPaper } from "./wrapping-paper.js"; 2 | 3 | class Paper extends WrappingPaper { 4 | constructor(w, h) { 5 | super(w, h); 6 | 7 | //object that defines the specular level for the specular image 8 | const opts1 = { specularColor: "#fff" }; 9 | 10 | const colorRim = "#4f919b"; 11 | const specColorRim = "#ddd"; 12 | const colorPalette1 = [ 13 | "#e4943b", 14 | "#f1d244", 15 | "#e0cce9", 16 | "#97ccf7", 17 | "#e0e5fa", 18 | "#e24377", 19 | "#b7bb4b", 20 | "#e15c4b", 21 | "#57ab8d" 22 | ]; 23 | 24 | var colorPalette2 = ["#f6f6f6", "#ff9b00", "#ff3200", "#02827a"]; 25 | 26 | const palettes = [colorPalette1]; 27 | const colorPalette = palettes[~~(Math.random() * palettes.length)]; 28 | 29 | const specPalette = ["#eee", "#aaa", "#888", "#666", "#444"]; 30 | 31 | this.drawRect(0, 0, w, h, "#340a2d", "#000"); 32 | 33 | const width = 32; 34 | const height = 32; 35 | const p = 0.5; 36 | 37 | const path1 = new Path2D(); 38 | path1.moveTo(0.5 * width, 0 + p); 39 | path1.lineTo(width - p, height - p); 40 | path1.lineTo(0 + p, height - p); 41 | path1.lineTo(0.5 * width, 0 + p); 42 | 43 | const path2 = new Path2D(); 44 | path2.moveTo(0.5 * width, height - p); 45 | path2.lineTo(width - p, 0 + p); 46 | path2.lineTo(0 + p, 0 + p); 47 | path2.lineTo(0.5 * width, height - p); 48 | 49 | function draw(x, y) { 50 | this.colorCtx.save(); 51 | this.colorCtx.translate(x * width, y * height); 52 | this.colorCtx.fillStyle = 53 | colorPalette[~~(Math.random() * colorPalette.length)]; 54 | this.colorCtx.fill(path1); 55 | this.colorCtx.restore(); 56 | 57 | this.colorCtx.save(); 58 | this.colorCtx.translate((x + 0.5) * width, y * height); 59 | this.colorCtx.fillStyle = 60 | colorPalette[~~(Math.random() * colorPalette.length)]; 61 | this.colorCtx.fill(path2); 62 | this.colorCtx.restore(); 63 | 64 | this.roughnessCtx.save(); 65 | this.roughnessCtx.translate(x * width, y * height); 66 | this.roughnessCtx.fillStyle = 67 | specPalette[~~(Math.random() * specPalette.length)]; 68 | this.roughnessCtx.fill(path1); 69 | this.roughnessCtx.restore(); 70 | 71 | this.roughnessCtx.save(); 72 | this.roughnessCtx.translate((x + 0.5) * width, y * height); 73 | this.roughnessCtx.fillStyle = 74 | specPalette[~~(Math.random() * specPalette.length)]; 75 | this.roughnessCtx.fill(path2); 76 | this.roughnessCtx.restore(); 77 | } 78 | 79 | const _d = draw.bind(this); 80 | 81 | let id; 82 | for (let y = -1; y < h / height; y++) { 83 | for (let x = -1; x < w / width; x++) { 84 | _d(x + (y % 2 ? 0.5 : 0), y); 85 | } 86 | } 87 | } 88 | } 89 | export { Paper }; 90 | -------------------------------------------------------------------------------- /modules/paper13.js: -------------------------------------------------------------------------------- 1 | import { WrappingPaper } from "./wrapping-paper.js"; 2 | 3 | class Paper extends WrappingPaper { 4 | constructor(w, h) { 5 | super(w, h); 6 | 7 | //object that defines the specular level for the specular image 8 | const opts1 = { specularColor: "#fff" }; 9 | 10 | const backgroundColor = "#4a4041"; 11 | const specColorRim = "#ddd"; 12 | const colorPalette1 = [ 13 | "#eed33d", 14 | "#db4543", 15 | "#7cb2c1", 16 | "#6a5241", 17 | "#e07f34", 18 | "#84858a", 19 | "#e8d2be", 20 | "#dfe5cd", 21 | "#e7cc36" 22 | ]; 23 | 24 | var colorPalette2 = ["#f6f6f6", "#ff9b00", "#ff3200", "#02827a"]; 25 | 26 | const palettes = [colorPalette1]; 27 | const colorPalette = palettes[~~(Math.random() * palettes.length)]; 28 | 29 | const specPalette = ["#eee", "#aaa", "#888", "#666", "#444"]; 30 | 31 | this.colorCtx.strokeStyle = "#fff"; 32 | this.roughnessCtx.strokeStyle = "#fff"; 33 | 34 | this.drawRect(0, 0, w, h, backgroundColor, "#000"); 35 | 36 | const width = 32; 37 | const height = 32; 38 | const p = 4; 39 | 40 | const path1 = new Path2D(); 41 | path1.moveTo(0.5 * width, 0 + p); 42 | path1.lineTo(width - p, height - p); 43 | path1.lineTo(0 + p, height - p); 44 | path1.lineTo(0.5 * width, 0 + p); 45 | 46 | const path2 = new Path2D(); 47 | path2.moveTo(0.5 * width, height - p); 48 | path2.lineTo(width - p, 0 + p); 49 | path2.lineTo(0 + p, 0 + p); 50 | path2.lineTo(0.5 * width, height - p); 51 | 52 | function draw(x, y) { 53 | this.colorCtx.save(); 54 | this.colorCtx.translate(x * width, y * height + 2 * p); 55 | this.colorCtx.fillStyle = 56 | colorPalette[~~(Math.random() * colorPalette.length)]; 57 | this.colorCtx.fill(path1); 58 | this.colorCtx.stroke(path1); 59 | this.colorCtx.restore(); 60 | 61 | this.colorCtx.save(); 62 | this.colorCtx.translate((x + 0.5) * width, y * height); 63 | this.colorCtx.fillStyle = 64 | colorPalette[~~(Math.random() * colorPalette.length)]; 65 | this.colorCtx.fill(path2); 66 | this.colorCtx.stroke(path2); 67 | this.colorCtx.restore(); 68 | 69 | this.roughnessCtx.save(); 70 | this.roughnessCtx.translate(x * width, y * height + 2 * p); 71 | this.roughnessCtx.fillStyle = 72 | specPalette[~~(Math.random() * specPalette.length)]; 73 | this.roughnessCtx.fill(path1); 74 | this.roughnessCtx.stroke(path1); 75 | this.roughnessCtx.restore(); 76 | 77 | this.roughnessCtx.save(); 78 | this.roughnessCtx.translate((x + 0.5) * width, y * height); 79 | this.roughnessCtx.fillStyle = 80 | specPalette[~~(Math.random() * specPalette.length)]; 81 | this.roughnessCtx.fill(path2); 82 | this.roughnessCtx.stroke(path2); 83 | this.roughnessCtx.restore(); 84 | } 85 | 86 | const _d = draw.bind(this); 87 | 88 | let id; 89 | for (let y = -1; y < h / height; y++) { 90 | for (let x = -1; x < w / width; x++) { 91 | _d(x + (y % 2 ? 0.5 : 0), y); 92 | } 93 | } 94 | } 95 | } 96 | export { Paper }; 97 | -------------------------------------------------------------------------------- /modules/paper14.js: -------------------------------------------------------------------------------- 1 | import { WrappingPaper } from "./wrapping-paper.js"; 2 | 3 | class Paper extends WrappingPaper { 4 | constructor(w, h) { 5 | super(w, h); 6 | 7 | //object that defines the specular level for the specular image 8 | const opts1 = { specularColor: "#fff" }; 9 | 10 | const backgroundColor = "#efe8cc"; 11 | const specColorRim = "#ddd"; 12 | const colorPalette1 = [ 13 | "#a39f8a", 14 | "#5d697a", 15 | "#c0cb3c", 16 | "#fea43b", 17 | "#90d6cf", 18 | "#efe8cc" 19 | ]; 20 | 21 | const palettes = [colorPalette1]; 22 | const colorPalette = palettes[~~(Math.random() * palettes.length)]; 23 | 24 | const specPalette = ["#eee", "#aaa", "#888", "#666", "#444"]; 25 | 26 | this.roughnessCtx.strokeStyle = "#fff"; 27 | this.drawRect(0, 0, w, h, backgroundColor, backgroundColor); 28 | 29 | const width = 32; 30 | const height = 32; 31 | const p = 4; 32 | 33 | const path1 = new Path2D(); 34 | path1.moveTo(-0.5 * width, -0.5 * height); 35 | path1.lineTo(0.5 * width, -0.5 * height); 36 | path1.arcTo(0.5 * width, 0.5 * height, -0.5 * width, 0.5 * height, width); 37 | path1.lineTo(-0.5 * width, -0.5 * height); 38 | 39 | function draw(x, y) { 40 | const a = (~~(Math.random() * 4) * Math.PI) / 2; 41 | this.colorCtx.save(); 42 | this.colorCtx.translate((x + 0.5) * width, (y + 0.5) * height); 43 | this.colorCtx.rotate(a); 44 | this.colorCtx.fillStyle = 45 | colorPalette[~~(Math.random() * colorPalette.length)]; 46 | this.colorCtx.fill(path1); 47 | this.colorCtx.restore(); 48 | 49 | this.roughnessCtx.save(); 50 | this.roughnessCtx.translate((x + 0.5) * width, (y + 0.5) * height); 51 | this.roughnessCtx.rotate(a); 52 | this.roughnessCtx.fillStyle = 53 | specPalette[~~(Math.random() * specPalette.length)]; 54 | this.roughnessCtx.fill(path1); 55 | this.roughnessCtx.stroke(path1); 56 | this.roughnessCtx.restore(); 57 | } 58 | 59 | const _d = draw.bind(this); 60 | 61 | let id; 62 | for (let y = -1; y < h / height; y++) { 63 | for (let x = -1; x < w / width; x++) { 64 | _d(x, y); 65 | } 66 | } 67 | } 68 | } 69 | export { Paper }; 70 | -------------------------------------------------------------------------------- /modules/paper15.js: -------------------------------------------------------------------------------- 1 | import { WrappingPaper } from "./wrapping-paper.js"; 2 | 3 | class Paper extends WrappingPaper { 4 | constructor(w, h) { 5 | super(w, h); 6 | 7 | //object that defines the specular level for the specular image 8 | const opts1 = { specularColor: "#fff" }; 9 | 10 | const backgroundColor = "#efe8cc"; 11 | const specColorRim = "#ddd"; 12 | const colorPalette1 = [ 13 | "#eed33d", 14 | "#db4543", 15 | "#7cb2c1", 16 | "#6a5241", 17 | "#e07f34", 18 | "#84858a", 19 | "#e8d2be", 20 | "#dfe5cd", 21 | "#e7cc36" 22 | ]; 23 | 24 | const palettes = [colorPalette1]; 25 | const colorPalette = palettes[~~(Math.random() * palettes.length)]; 26 | 27 | const specPalette = ["#eee", "#aaa", "#888", "#666", "#444"]; 28 | 29 | this.roughnessCtx.strokeStyle = "#fff"; 30 | this.drawRect(0, 0, w, h, backgroundColor, backgroundColor); 31 | 32 | const width = 32; 33 | const height = 32; 34 | const p = 4; 35 | 36 | const path1 = new Path2D(); 37 | path1.moveTo(-0.5 * width, -0.5 * height); 38 | path1.lineTo(0.5 * width, -0.5 * height); 39 | path1.arcTo(0.5 * width, 0.5 * height, -0.5 * width, 0.5 * height, width); 40 | path1.lineTo(-0.5 * width, -0.5 * height); 41 | 42 | function draw(x, y) { 43 | const a = ((x % 2) * Math.PI) / 2 + (y % 2) * Math.PI; 44 | this.colorCtx.save(); 45 | this.colorCtx.translate((x + 0.5) * width, (y + 0.5) * height); 46 | this.colorCtx.rotate(a); 47 | this.colorCtx.fillStyle = 48 | colorPalette[~~(Math.random() * colorPalette.length)]; 49 | this.colorCtx.fill(path1); 50 | this.colorCtx.restore(); 51 | 52 | this.roughnessCtx.save(); 53 | this.roughnessCtx.translate((x + 0.5) * width, (y + 0.5) * height); 54 | this.roughnessCtx.rotate(a); 55 | this.roughnessCtx.fillStyle = 56 | specPalette[~~(Math.random() * specPalette.length)]; 57 | this.roughnessCtx.fill(path1); 58 | this.roughnessCtx.stroke(path1); 59 | this.roughnessCtx.restore(); 60 | } 61 | 62 | const _d = draw.bind(this); 63 | 64 | let id; 65 | for (let y = -1; y < h / height; y++) { 66 | for (let x = -1; x < w / width; x++) { 67 | _d(x, y); 68 | } 69 | } 70 | } 71 | } 72 | export { Paper }; 73 | -------------------------------------------------------------------------------- /modules/paper16.js: -------------------------------------------------------------------------------- 1 | import { WrappingPaper } from "./wrapping-paper.js"; 2 | 3 | function getWeightedRandom(inputs) { 4 | const total = inputs.reduce((ac, v) => (ac += v), 0); 5 | return function() { 6 | const r = Math.random() * total; 7 | let ac = 0; 8 | let c = 0; 9 | for (const v of inputs) { 10 | const n = ac + v; 11 | if (r > +ac && r < n) { 12 | return c; 13 | } 14 | c++; 15 | ac = n; 16 | } 17 | }; 18 | } 19 | 20 | const rnd = getWeightedRandom([1, 2, 1, 8]); 21 | 22 | class Paper extends WrappingPaper { 23 | constructor(w, h) { 24 | super(w, h); 25 | 26 | //object that defines the specular level for the specular image 27 | const opts1 = { specularColor: "#fff" }; 28 | 29 | const backgroundColor = "#bbe6fc"; 30 | const specColorRim = "#ddd"; 31 | const colorPalette1 = [ 32 | "#fef4b5", 33 | "#ffee5a", 34 | "#68cafb", 35 | "#f6ba94", 36 | "#f18547" 37 | ]; 38 | 39 | const palettes = [colorPalette1]; 40 | const colorPalette = palettes[~~(Math.random() * palettes.length)]; 41 | 42 | const specPalette = ["#eee", "#aaa", "#888", "#666", "#444"]; 43 | 44 | this.roughnessCtx.strokeStyle = "#fff"; 45 | this.drawRect(0, 0, w, h, backgroundColor, backgroundColor); 46 | 47 | const width = 32; 48 | const height = 32; 49 | const p = 4; 50 | 51 | const path1 = new Path2D(); 52 | path1.moveTo(-0.5 * width, -0.5 * height); 53 | path1.lineTo(0.5 * width, -0.5 * height); 54 | path1.arcTo(0.5 * width, 0.5 * height, -0.5 * width, 0.5 * height, width); 55 | path1.lineTo(-0.5 * width, -0.5 * height); 56 | 57 | function draw(x, y) { 58 | const type = rnd(); 59 | switch (type) { 60 | case 0: // empty 61 | break; 62 | case 1: // big circle 63 | this.colorCtx.save(); 64 | this.colorCtx.translate((x + 0.5) * width, (y + 0.5) * height); 65 | this.colorCtx.fillStyle = 66 | colorPalette[~~(Math.random() * colorPalette.length)]; 67 | this.colorCtx.beginPath(); 68 | this.colorCtx.arc(0, 0, 0.5 * width, 0, 2 * Math.PI); 69 | this.colorCtx.fill(); 70 | this.colorCtx.restore(); 71 | 72 | this.roughnessCtx.save(); 73 | this.roughnessCtx.translate((x + 0.5) * width, (y + 0.5) * height); 74 | this.roughnessCtx.fillStyle = 75 | specPalette[~~(Math.random() * specPalette.length)]; 76 | this.roughnessCtx.beginPath(); 77 | this.roughnessCtx.arc(0, 0, 0.5 * width, 0, 2 * Math.PI); 78 | this.roughnessCtx.fill(); 79 | this.roughnessCtx.restore(); 80 | break; 81 | case 2: // small circle 82 | this.colorCtx.save(); 83 | this.colorCtx.translate((x + 0.5) * width, (y + 0.5) * height); 84 | this.colorCtx.fillStyle = 85 | colorPalette[~~(Math.random() * colorPalette.length)]; 86 | this.colorCtx.beginPath(); 87 | this.colorCtx.arc(0, 0, 0.25 * width, 0, 2 * Math.PI); 88 | this.colorCtx.fill(); 89 | this.colorCtx.restore(); 90 | 91 | this.roughnessCtx.save(); 92 | this.roughnessCtx.translate((x + 0.5) * width, (y + 0.5) * height); 93 | this.roughnessCtx.fillStyle = 94 | specPalette[~~(Math.random() * specPalette.length)]; 95 | this.roughnessCtx.beginPath(); 96 | this.roughnessCtx.arc(0, 0, 0.25 * width, 0, 2 * Math.PI); 97 | this.roughnessCtx.fill(); 98 | this.roughnessCtx.restore(); 99 | break; 100 | case 3: // quarter 101 | const a = 0.5 * ~~(Math.random() * 4) * Math.PI; 102 | this.colorCtx.save(); 103 | this.colorCtx.translate((x + 0.5) * width, (y + 0.5) * height); 104 | this.colorCtx.rotate(a); 105 | this.colorCtx.fillStyle = 106 | colorPalette[~~(Math.random() * colorPalette.length)]; 107 | this.colorCtx.fill(path1); 108 | this.colorCtx.restore(); 109 | 110 | this.roughnessCtx.save(); 111 | this.roughnessCtx.translate((x + 0.5) * width, (y + 0.5) * height); 112 | this.roughnessCtx.rotate(a); 113 | this.roughnessCtx.fillStyle = 114 | specPalette[~~(Math.random() * specPalette.length)]; 115 | this.roughnessCtx.fill(path1); 116 | this.roughnessCtx.restore(); 117 | break; 118 | } 119 | } 120 | 121 | const _d = draw.bind(this); 122 | 123 | let id; 124 | for (let y = -1; y < h / height; y++) { 125 | for (let x = -1; x < w / width; x++) { 126 | _d(x, y); 127 | } 128 | } 129 | } 130 | } 131 | export { Paper }; 132 | -------------------------------------------------------------------------------- /modules/paper17.js: -------------------------------------------------------------------------------- 1 | import { WrappingPaper } from "./wrapping-paper.js"; 2 | 3 | function getWeightedRandom(inputs) { 4 | const total = inputs.reduce((ac, v) => (ac += v), 0); 5 | return function() { 6 | const r = Math.random() * total; 7 | let ac = 0; 8 | let c = 0; 9 | for (const v of inputs) { 10 | const n = ac + v; 11 | if (r > +ac && r < n) { 12 | return c; 13 | } 14 | c++; 15 | ac = n; 16 | } 17 | }; 18 | } 19 | 20 | const rnd = getWeightedRandom([1, 2, 1, 8]); 21 | 22 | class Paper extends WrappingPaper { 23 | constructor(w, h) { 24 | super(w, h); 25 | 26 | //object that defines the specular level for the specular image 27 | const opts1 = { specularColor: "#fff" }; 28 | 29 | const backgroundColor = "#ef6660"; 30 | const specColorRim = "#ddd"; 31 | const colorPalette1 = [ 32 | "#ef6660", 33 | "#f29b9b", 34 | "#68cafb", 35 | "#bbe6fc", 36 | "#fef4b5", 37 | "#ffee5a" 38 | ]; 39 | 40 | const palettes = [colorPalette1]; 41 | const colorPalette = palettes[~~(Math.random() * palettes.length)]; 42 | 43 | const specPalette = ["#eee", "#aaa", "#888", "#666", "#444"]; 44 | 45 | this.roughnessCtx.strokeStyle = "#fff"; 46 | this.drawRect(0, 0, w, h, backgroundColor, backgroundColor); 47 | 48 | const width = 32; 49 | const height = 32; 50 | const p = 4; 51 | 52 | const path1 = new Path2D(); 53 | path1.moveTo(-0.5 * width, -0.5 * height); 54 | path1.lineTo(0.5 * width, -0.5 * height); 55 | path1.arcTo(0.5 * width, 0.5 * height, -0.5 * width, 0.5 * height, width); 56 | path1.lineTo(-0.5 * width, -0.5 * height); 57 | 58 | function draw(x, y) { 59 | const type = rnd(); 60 | switch (type) { 61 | case 0: // empty 62 | break; 63 | case 1: // big circle 64 | this.colorCtx.save(); 65 | this.colorCtx.translate((x + 0.5) * width, (y + 0.5) * height); 66 | this.colorCtx.fillStyle = 67 | colorPalette[~~(Math.random() * colorPalette.length)]; 68 | this.colorCtx.beginPath(); 69 | this.colorCtx.arc(0, 0, 0.5 * width, 0, 2 * Math.PI); 70 | this.colorCtx.fill(); 71 | this.colorCtx.restore(); 72 | 73 | this.roughnessCtx.save(); 74 | this.roughnessCtx.translate((x + 0.5) * width, (y + 0.5) * height); 75 | this.roughnessCtx.fillStyle = 76 | specPalette[~~(Math.random() * specPalette.length)]; 77 | this.roughnessCtx.beginPath(); 78 | this.roughnessCtx.arc(0, 0, 0.5 * width, 0, 2 * Math.PI); 79 | this.roughnessCtx.fill(); 80 | this.roughnessCtx.restore(); 81 | break; 82 | case 2: // small circle 83 | this.colorCtx.save(); 84 | this.colorCtx.translate((x + 0.5) * width, (y + 0.5) * height); 85 | this.colorCtx.fillStyle = 86 | colorPalette[~~(Math.random() * colorPalette.length)]; 87 | this.colorCtx.beginPath(); 88 | this.colorCtx.arc(0, 0, 0.25 * width, 0, 2 * Math.PI); 89 | this.colorCtx.fill(); 90 | this.colorCtx.restore(); 91 | 92 | this.roughnessCtx.save(); 93 | this.roughnessCtx.translate((x + 0.5) * width, (y + 0.5) * height); 94 | this.roughnessCtx.fillStyle = 95 | specPalette[~~(Math.random() * specPalette.length)]; 96 | this.roughnessCtx.beginPath(); 97 | this.roughnessCtx.arc(0, 0, 0.25 * width, 0, 2 * Math.PI); 98 | this.roughnessCtx.fill(); 99 | this.roughnessCtx.restore(); 100 | break; 101 | case 3: // quarter 102 | const a = 0.5 * ~~(Math.random() * 4) * Math.PI; 103 | this.colorCtx.save(); 104 | this.colorCtx.translate((x + 0.5) * width, (y + 0.5) * height); 105 | this.colorCtx.rotate(a); 106 | this.colorCtx.fillStyle = 107 | colorPalette[~~(Math.random() * colorPalette.length)]; 108 | this.colorCtx.fill(path1); 109 | this.colorCtx.restore(); 110 | 111 | this.roughnessCtx.save(); 112 | this.roughnessCtx.translate((x + 0.5) * width, (y + 0.5) * height); 113 | this.roughnessCtx.rotate(a); 114 | this.roughnessCtx.fillStyle = 115 | specPalette[~~(Math.random() * specPalette.length)]; 116 | this.roughnessCtx.fill(path1); 117 | this.roughnessCtx.restore(); 118 | break; 119 | } 120 | } 121 | 122 | const _d = draw.bind(this); 123 | 124 | let id; 125 | for (let y = -1; y < h / height; y++) { 126 | for (let x = -1; x < w / width; x++) { 127 | _d(x, y); 128 | } 129 | } 130 | } 131 | } 132 | export { Paper }; 133 | -------------------------------------------------------------------------------- /modules/paper18.js: -------------------------------------------------------------------------------- 1 | import { WrappingPaper } from "./wrapping-paper.js"; 2 | 3 | class Paper extends WrappingPaper { 4 | constructor(w, h) { 5 | super(w, h); 6 | 7 | //object that defines the specular level for the specular image 8 | const opts1 = { specularColor: "#fff" }; 9 | 10 | const backgroundColor = "#bbe6fc"; 11 | const specColorRim = "#ddd"; 12 | const colorPalette1 = [ 13 | "#fef4b5", 14 | "#ffec5f", 15 | "#f29b9e", 16 | "#ef6660", 17 | "#6d7db8", 18 | "#162661" 19 | ]; 20 | 21 | const palettes = [colorPalette1]; 22 | const colorPalette = palettes[~~(Math.random() * palettes.length)]; 23 | 24 | const specPalette = ["#eee", "#aaa", "#888", "#666", "#444"]; 25 | 26 | this.roughnessCtx.strokeStyle = "#fff"; 27 | this.drawRect(0, 0, w, h, backgroundColor, backgroundColor); 28 | 29 | const width = 32; 30 | const height = 32; 31 | 32 | function draw(x, y) { 33 | const hasDot = Math.random() > 0.75; 34 | 35 | this.colorCtx.lineWidth = 0.25 * width; 36 | this.colorCtx.save(); 37 | this.colorCtx.translate((x + 0.5) * width, (y + 0.5) * height); 38 | this.colorCtx.fillStyle = 39 | colorPalette[~~(Math.random() * colorPalette.length)]; 40 | this.colorCtx.fillRect( 41 | -0.5 * width, 42 | -0.5 * height, 43 | 2 * width, 44 | 2 * height 45 | ); 46 | if (hasDot) { 47 | this.colorCtx.fillStyle = 48 | colorPalette[~~(Math.random() * colorPalette.length)]; 49 | this.colorCtx.beginPath(); 50 | this.colorCtx.arc(0, 0, 0.25 * width, 0, 2 * Math.PI); 51 | this.colorCtx.fill(); 52 | } 53 | this.colorCtx.restore(); 54 | 55 | this.roughnessCtx.save(); 56 | this.roughnessCtx.translate((x + 0.5) * width, (y + 0.5) * height); 57 | this.roughnessCtx.fillStyle = 58 | specPalette[~~(Math.random() * specPalette.length)]; 59 | this.roughnessCtx.fillRect( 60 | -0.5 * width, 61 | -0.5 * height, 62 | 2 * width, 63 | 2 * height 64 | ); 65 | if (hasDot) { 66 | this.roughnessCtx.fillStyle = 67 | specPalette[~~(Math.random() * colorPalette.length)]; 68 | this.roughnessCtx.beginPath(); 69 | this.roughnessCtx.arc(0, 0, 0.25 * width, 0, 2 * Math.PI); 70 | this.roughnessCtx.fill(); 71 | } 72 | this.roughnessCtx.restore(); 73 | } 74 | 75 | const _d = draw.bind(this); 76 | 77 | let id; 78 | for (let y = -1; y < h / height; y++) { 79 | for (let x = -1; x < w / width; x++) { 80 | _d(x, y); 81 | } 82 | } 83 | } 84 | } 85 | export { Paper }; 86 | -------------------------------------------------------------------------------- /modules/paper19.js: -------------------------------------------------------------------------------- 1 | import { WrappingPaper } from "./wrapping-paper.js"; 2 | 3 | class Paper extends WrappingPaper { 4 | constructor(w, h) { 5 | super(w, h); 6 | 7 | //object that defines the specular level for the specular image 8 | const opts1 = { specularColor: "#fff" }; 9 | 10 | const backgroundColor = "#bbe6fc"; 11 | const specColorRim = "#ddd"; 12 | const colorPalette1 = [ 13 | "#fef4b5", 14 | "#ffec5f", 15 | "#f29b9e", 16 | "#ef6660", 17 | "#6d7db8", 18 | "#162661" 19 | ]; 20 | 21 | const palettes = [colorPalette1]; 22 | const colorPalette = palettes[~~(Math.random() * palettes.length)]; 23 | 24 | const specPalette = ["#eee", "#aaa", "#888", "#666", "#444"]; 25 | 26 | this.roughnessCtx.strokeStyle = "#fff"; 27 | this.drawRect(0, 0, w, h, backgroundColor, backgroundColor); 28 | 29 | const width = 32; 30 | const height = 32; 31 | 32 | function draw(x, y) { 33 | const a = Math.random() * 2 * Math.PI; 34 | 35 | this.colorCtx.lineWidth = 0.25 * width; 36 | this.colorCtx.save(); 37 | this.colorCtx.translate((x + 0.5) * width, (y + 0.5) * height); 38 | this.colorCtx.rotate(a); 39 | this.colorCtx.fillStyle = 40 | colorPalette[~~(Math.random() * colorPalette.length)]; 41 | this.colorCtx.fillRect( 42 | -0.25 * width, 43 | -0.25 * height, 44 | 0.5 * width, 45 | 0.5 * height 46 | ); 47 | this.colorCtx.restore(); 48 | 49 | this.roughnessCtx.lineWidth = 0.25 * width; 50 | this.roughnessCtx.save(); 51 | this.roughnessCtx.translate((x + 0.5) * width, (y + 0.5) * height); 52 | this.roughnessCtx.rotate(a); 53 | this.roughnessCtx.fillStyle = 54 | specPalette[~~(Math.random() * specPalette.length)]; 55 | this.roughnessCtx.fillRect( 56 | -0.3 * width, 57 | -0.3 * height, 58 | 0.6 * width, 59 | 0.6 * height 60 | ); 61 | this.roughnessCtx.restore(); 62 | } 63 | 64 | const _d = draw.bind(this); 65 | 66 | let id; 67 | for (let y = -1; y < h / height; y++) { 68 | for (let x = -1; x < w / width; x++) { 69 | _d(x, y); 70 | } 71 | } 72 | } 73 | } 74 | export { Paper }; 75 | -------------------------------------------------------------------------------- /modules/paper2.js: -------------------------------------------------------------------------------- 1 | import { WrappingPaper } from "./wrapping-paper.js"; 2 | 3 | class Paper extends WrappingPaper { 4 | constructor(w, h) { 5 | super(w, h); 6 | 7 | const colorPalette = [ 8 | "#f0f0f0", 9 | "#ff8b00", 10 | "#ff2b00", 11 | "#d90a00", 12 | "#02756e", 13 | "#2c0826", 14 | "#2d0826" 15 | ]; 16 | 17 | const line = "#c5c5c5"; 18 | const f = w / 32; 19 | 20 | this.drawRect(0, 0, w, h, "#fff", "#000"); 21 | 22 | for (var i = 0; i < 8; i++) { 23 | for (var j = 0; j < 8; j++) { 24 | var x = i * (w / 8), 25 | y = j * (h / 8), 26 | rr = (0.5 * w) / 8; 27 | 28 | x += rr; 29 | y += rr; 30 | var r = Math.random() * rr; 31 | if (r < f) r = f; 32 | //while (r > 0) { 33 | var paletteIndex = Math.floor(Math.random() * colorPalette.length); 34 | this.drawCircle(x, y, r, colorPalette[paletteIndex], { 35 | specularColor: "#fff" 36 | }); 37 | //r -= 2 * f + Math.random() * 5 * f; 38 | //} 39 | } 40 | } 41 | } 42 | } 43 | 44 | export { Paper }; 45 | -------------------------------------------------------------------------------- /modules/paper20.js: -------------------------------------------------------------------------------- 1 | import { WrappingPaper } from "./wrapping-paper.js"; 2 | 3 | class Paper extends WrappingPaper { 4 | constructor(w, h) { 5 | super(w, h); 6 | 7 | //object that defines the specular level for the specular image 8 | const opts1 = { specularColor: "#fff" }; 9 | 10 | const backgroundColor = "#bbe6fc"; 11 | const specColorRim = "#ddd"; 12 | const colorPalette1 = [ 13 | "#fef4b5", 14 | "#ffec5f", 15 | "#f29b9e", 16 | "#ef6660", 17 | "#6d7db8", 18 | "#162661" 19 | ]; 20 | 21 | const palettes = [colorPalette1]; 22 | const colorPalette = palettes[~~(Math.random() * palettes.length)]; 23 | 24 | const specPalette = ["#eee", "#aaa", "#888", "#666", "#444"]; 25 | 26 | this.roughnessCtx.strokeStyle = "#fff"; 27 | this.drawRect(0, 0, w, h, backgroundColor, backgroundColor); 28 | 29 | const width = 64; 30 | const height = 64; 31 | 32 | function draw(x, y) { 33 | const count = ~~(Math.random() * 2); 34 | this.colorCtx.save(); 35 | this.colorCtx.translate((x + 0.5) * width, (y + 0.5) * height); 36 | const bkg = colorPalette[~~(Math.random() * colorPalette.length)]; 37 | this.colorCtx.fillStyle = bkg; 38 | this.colorCtx.fillRect(-0.5 * width, -0.5 * height, width, height); 39 | let c; 40 | do { 41 | c = colorPalette[~~(Math.random() * colorPalette.length)]; 42 | } while (c === bkg); 43 | this.colorCtx.strokeStyle = c; 44 | this.colorCtx.fillStyle = c; 45 | this.colorCtx.lineWidth = width / 3; 46 | if (count == 0) { 47 | this.colorCtx.beginPath(); 48 | this.colorCtx.arc( 49 | -0.5 * width, 50 | -0.5 * height, 51 | 0.5 * width, 52 | 0, 53 | Math.PI / 2 54 | ); 55 | this.colorCtx.stroke(); 56 | this.colorCtx.rotate(Math.PI); 57 | this.colorCtx.beginPath(); 58 | this.colorCtx.arc( 59 | -0.5 * width, 60 | -0.5 * height, 61 | 0.5 * width, 62 | 0, 63 | Math.PI / 2 64 | ); 65 | this.colorCtx.stroke(); 66 | } 67 | if (count == 1) { 68 | for (let a = 0; a < 4; a++) { 69 | this.colorCtx.save(); 70 | this.colorCtx.rotate((a * Math.PI) / 2); 71 | this.colorCtx.beginPath(); 72 | this.colorCtx.arc( 73 | -0.5 * width, 74 | -0.5 * height, 75 | 0.5 * width, 76 | 0, 77 | Math.PI / 2 78 | ); 79 | this.colorCtx.stroke(); 80 | this.colorCtx.fillRect( 81 | -0.1 * width, 82 | -0.1 * height, 83 | 0.2 * width, 84 | 0.2 * height 85 | ); 86 | this.colorCtx.restore(); 87 | } 88 | } 89 | this.colorCtx.restore(); 90 | 91 | this.roughnessCtx.save(); 92 | this.roughnessCtx.translate((x + 0.5) * width, (y + 0.5) * height); 93 | this.roughnessCtx.fillStyle = 94 | specPalette[~~(Math.random() * specPalette.length)]; 95 | this.roughnessCtx.fillRect(-0.5 * width, -0.5 * height, width, height); 96 | this.roughnessCtx.strokeStyle = 97 | specPalette[~~(Math.random() * specPalette.length)]; 98 | this.roughnessCtx.fillStyle = this.roughnessCtx.strokeStyle; 99 | this.roughnessCtx.lineWidth = width / 2.5; 100 | if (count == 0) { 101 | this.roughnessCtx.beginPath(); 102 | this.roughnessCtx.arc( 103 | -0.5 * width, 104 | -0.5 * height, 105 | 0.5 * width, 106 | 0, 107 | Math.PI / 2 108 | ); 109 | this.roughnessCtx.stroke(); 110 | this.roughnessCtx.rotate(Math.PI); 111 | this.roughnessCtx.beginPath(); 112 | this.roughnessCtx.arc( 113 | -0.5 * width, 114 | -0.5 * height, 115 | 0.5 * width, 116 | 0, 117 | Math.PI / 2 118 | ); 119 | this.roughnessCtx.stroke(); 120 | } 121 | if (count == 1) { 122 | for (let a = 0; a < 4; a++) { 123 | this.roughnessCtx.save(); 124 | this.roughnessCtx.rotate((a * Math.PI) / 2); 125 | this.roughnessCtx.beginPath(); 126 | this.roughnessCtx.arc( 127 | -0.5 * width, 128 | -0.5 * height, 129 | 0.5 * width, 130 | 0, 131 | Math.PI / 2 132 | ); 133 | this.roughnessCtx.stroke(); 134 | this.roughnessCtx.fillRect( 135 | -0.1 * width, 136 | -0.1 * height, 137 | 0.2 * width, 138 | 0.2 * height 139 | ); 140 | this.roughnessCtx.restore(); 141 | } 142 | } 143 | this.roughnessCtx.restore(); 144 | } 145 | 146 | const _d = draw.bind(this); 147 | 148 | let id; 149 | for (let y = -1; y < h / height; y++) { 150 | for (let x = -1; x < w / width; x++) { 151 | _d(x, y); 152 | } 153 | } 154 | } 155 | } 156 | export { Paper }; 157 | -------------------------------------------------------------------------------- /modules/paper3.js: -------------------------------------------------------------------------------- 1 | import { WrappingPaper } from "./wrapping-paper.js"; 2 | 3 | class Paper extends WrappingPaper { 4 | constructor(w, h) { 5 | super(w, h); 6 | 7 | const colorPalette = []; 8 | colorPalette.push("#f0f0f0"); 9 | colorPalette.push("#ff8b00"); 10 | colorPalette.push("#ff2b00"); 11 | colorPalette.push("#d90a00"); 12 | colorPalette.push("#02756e"); 13 | colorPalette.push("#2c0826"); 14 | colorPalette.push("#2d0826"); 15 | 16 | const specPalette = []; 17 | specPalette.push("#ddd"); 18 | specPalette.push("#ccc"); 19 | specPalette.push("#bbb"); 20 | specPalette.push("#aaa"); 21 | specPalette.push("#999"); 22 | specPalette.push("#888"); 23 | specPalette.push("#777"); 24 | 25 | this.drawRect(0, 0, w, h, "#fff", "#000"); 26 | const f = w / 512; 27 | 28 | for (let i = 0; i < 2000; i++) { 29 | const paletteIndex = Math.floor(Math.random() * colorPalette.length); 30 | const x = Math.random() * w; 31 | const y = Math.random() * h; 32 | const r = 2 * f + Math.random() * 10 * f; 33 | const op = { specularColor: specPalette[paletteIndex] }; 34 | this.drawCircle(x, y, r, colorPalette[paletteIndex], op); 35 | this.drawCircle(x + w, y, r, colorPalette[paletteIndex], op); 36 | this.drawCircle(x - w, y, r, colorPalette[paletteIndex], op); 37 | this.drawCircle(x, y + h, r, colorPalette[paletteIndex], op); 38 | this.drawCircle(x, y - h, r, colorPalette[paletteIndex], op); 39 | } 40 | } 41 | } 42 | 43 | export { Paper }; 44 | -------------------------------------------------------------------------------- /modules/paper4.js: -------------------------------------------------------------------------------- 1 | import { WrappingPaper } from "./wrapping-paper.js"; 2 | 3 | class Paper extends WrappingPaper { 4 | constructor(w, h) { 5 | super(w, h); 6 | 7 | //object that defines the specular level for the specular image 8 | const opts1 = { specularColor: "#000" }; 9 | const opts2 = { specularColor: "#fff" }; 10 | 11 | var colorPalette = []; 12 | colorPalette.push("#89d5da"); 13 | colorPalette.push("#4f4f41"); 14 | colorPalette.push("#f6bd1f"); 15 | colorPalette.push("#b1a881"); 16 | colorPalette.push("#fbd66c"); 17 | 18 | const f = 512 / w; 19 | const v = (w * 16) / 512; 20 | 21 | for (var i = 0; i < v; i++) { 22 | for (var j = 0; j < v; j++) { 23 | //Draw background 24 | var paletteIndex = Math.floor(Math.random() * colorPalette.length); 25 | this.drawRect( 26 | i * (w / v), 27 | j * (h / v), 28 | w / v, 29 | h / v, 30 | colorPalette[paletteIndex], 31 | opts1 32 | ); 33 | } 34 | } 35 | 36 | var sourceTmpCanvas = document.createElement("canvas"); 37 | var destinationTmpCanvas = document.createElement("canvas"); 38 | destinationTmpCanvas.setAttribute("width", w); 39 | destinationTmpCanvas.setAttribute("height", h); 40 | sourceTmpCanvas.setAttribute("width", w); 41 | sourceTmpCanvas.setAttribute("height", h); 42 | var srcCtx = sourceTmpCanvas.getContext("2d"); 43 | var dstCtx = destinationTmpCanvas.getContext("2d"); 44 | 45 | srcCtx.drawImage(this.colorCanvas, 0, 0); 46 | 47 | this.colorCtx.clearRect(0, 0, w, h); 48 | for (var i = 0; i <= v; i++) { 49 | for (var j = 0; j <= v; j++) { 50 | //Draw arcs inside the arcs 51 | var startAngle = Math.floor(Math.random() * 4) * (Math.PI / 2); 52 | var arcRadians = Math.ceil(Math.random() * 4) * (Math.PI / 2); 53 | //this.drawArc((i * (512 / v)) + ((512 / v / 2)), (j * (512 / v)) + ((512 / v / 2)), (512 / v / 2), startAngle, arcRadians, "#aaa", opts2); 54 | this.drawArc( 55 | i * (w / v), 56 | j * (h / v), 57 | w / v / 2, 58 | startAngle, 59 | arcRadians, 60 | "#aaa", 61 | opts2 62 | ); 63 | } 64 | } 65 | 66 | dstCtx.drawImage(this.colorCanvas, 0, 0); 67 | 68 | this.colorCtx.clearRect(0, 0, w, h); 69 | 70 | this.colorCtx.drawImage(sourceTmpCanvas, 0, 0, w, h); 71 | this.colorCtx.globalCompositeOperation = "destination-in"; 72 | this.colorCtx.drawImage(destinationTmpCanvas, 0, 0, w, h); 73 | this.colorCtx.globalCompositeOperation = "destination-over"; 74 | this.drawRect(0, 0, w, h, "#fff"); 75 | this.colorCtx.globalCompositeOperation = "source-over"; 76 | //Draw circles inside the arcs 77 | for (var i = 0; i <= v; i++) { 78 | for (var j = 0; j <= v; j++) { 79 | var paletteIndex = Math.floor(Math.random() * colorPalette.length); 80 | var circleWidth = Math.max( 81 | Math.ceil(Math.random() * (w / v / 2 - 10 * f)), 82 | 0 83 | ); 84 | this.drawCircle( 85 | i * (w / v), 86 | j * (h / v), 87 | circleWidth, 88 | colorPalette[paletteIndex], 89 | opts2 90 | ); 91 | } 92 | } 93 | } 94 | } 95 | 96 | export { Paper }; 97 | -------------------------------------------------------------------------------- /modules/paper5.js: -------------------------------------------------------------------------------- 1 | import { WrappingPaper } from "./wrapping-paper.js"; 2 | 3 | class Paper extends WrappingPaper { 4 | constructor(w, h) { 5 | super(w, h); 6 | 7 | const opts1 = { specularColor: "#000" }; 8 | 9 | const colorPalette1 = [ 10 | "#f1c12f", 11 | "#c6c6c6", 12 | "#fefefe", 13 | "#20bfd2", 14 | "#6c6c6c", 15 | "#484148" 16 | ]; 17 | const colorPalette11 = [ 18 | "#fefefe", 19 | "#c6c6c6", 20 | "#484148", 21 | "#6c6c6c", 22 | "#f1c12f", 23 | "#20bfd2" 24 | ]; 25 | const colorPalette2 = ["#d23177", "#ffffff", "#ef562d", "#ffe156"]; 26 | const colorPalette21 = ["#ffffff", "#d23177", "#ef562d", "#ffe156"]; 27 | const colorPalette3 = ["#d71e75", "#ef562d", "#98d6e1", "#f6d258"]; 28 | const colorPalette31 = ["#d71e75", "#ef562d", "#f6d258", "#98d6e1"]; 29 | 30 | const palettes = [ 31 | colorPalette1, 32 | colorPalette11, 33 | colorPalette2, 34 | colorPalette21, 35 | colorPalette3, 36 | colorPalette31 37 | ]; 38 | const colorPalette = palettes[~~(Math.random() * palettes.length)]; 39 | 40 | colorPalette.sort((a, b) => Math.random() * 2 - 1); 41 | 42 | const width = 32; 43 | const height = 32; 44 | 45 | this.drawRect(0, 0, w, h, "#fff", opts1); 46 | for (let y = -1; y < h / height + 1; y++) { 47 | for (let x = -1; x < w / width + 1; x++) { 48 | const path = new Path2D(); 49 | if (x % 2) { 50 | path.moveTo(x * width, y * height); 51 | path.lineTo((x + 1) * width, (y + 1) * height); 52 | path.lineTo((x + 1) * width, (y + 2) * height); 53 | path.lineTo(x * width, (y + 1) * height); 54 | path.lineTo(x * width, y * height); 55 | } else { 56 | path.moveTo(x * width, (y + 1) * height); 57 | path.lineTo(x * width, (y + 2) * height); 58 | path.lineTo((x + 1) * width, (y + 1) * height); 59 | path.lineTo((x + 1) * width, y * height); 60 | path.lineTo(x * width, (y + 1) * height); 61 | } 62 | 63 | this.colorCtx.fillStyle = 64 | colorPalette[((2 * (y + 1)) % colorPalette.length) + (x % 2)]; 65 | this.colorCtx.fill(path); 66 | 67 | const c = ~~(Math.random() * 255); 68 | this.roughnessCtx.fillStyle = `rgb(${c},${c},${c})`; 69 | this.roughnessCtx.fill(path); 70 | } 71 | } 72 | } 73 | } 74 | 75 | export { Paper }; 76 | -------------------------------------------------------------------------------- /modules/paper6.js: -------------------------------------------------------------------------------- 1 | import { WrappingPaper } from "./wrapping-paper.js"; 2 | 3 | class Paper extends WrappingPaper { 4 | constructor(w, h) { 5 | super(w, h); 6 | 7 | //object that defines the specular level for the specular image 8 | const opts1 = { specularColor: "#fff" }; 9 | 10 | var colorPalette = []; 11 | colorPalette.push("#edece8"); 12 | colorPalette.push("#db6644"); 13 | colorPalette.push("#b44137"); 14 | colorPalette.push("#4c7b7f"); 15 | colorPalette.push("#0a0a0f"); 16 | 17 | var specPalette = []; 18 | specPalette.push("#eee"); 19 | specPalette.push("#aaa"); 20 | specPalette.push("#888"); 21 | specPalette.push("#666"); 22 | specPalette.push("#444"); 23 | 24 | this.drawRect(0, 0, 512, 512, "#fff", "#000"); 25 | 26 | function draw(x, y, w, h, s, depth, l) { 27 | l++; 28 | 29 | for (var i = 0; i < s; i++) { 30 | for (var j = 0; j < s; j++) { 31 | var xx = x + i * (w / s), 32 | yy = y + j * (h / s), 33 | d = w / s; 34 | 35 | if (l == 1) { 36 | depth = Math.floor(Math.random() * 4); 37 | } 38 | 39 | if (l < depth) { 40 | _d(xx, yy, d, d, depth, l); 41 | } else { 42 | var paletteIndex = Math.floor(Math.random() * colorPalette.length); 43 | this.drawRect(xx, yy, d, d, colorPalette[paletteIndex], { 44 | specularColor: specPalette[paletteIndex] 45 | }); 46 | } 47 | } 48 | } 49 | } 50 | const _d = draw.bind(this); 51 | 52 | _d(0, 0, 512, 512, 15, Math.floor(Math.random() * 3), 0); 53 | } 54 | } 55 | 56 | export { Paper }; 57 | -------------------------------------------------------------------------------- /modules/paper7.js: -------------------------------------------------------------------------------- 1 | import { WrappingPaper } from "./wrapping-paper.js"; 2 | 3 | class Paper extends WrappingPaper { 4 | constructor(w, h) { 5 | super(w, h); 6 | 7 | //object that defines the specular level for the specular image 8 | const opts1 = { specularColor: "#fff" }; 9 | 10 | var colorPalette = ["#f6f6f6", "#ff9b00", "#ff3200", "#02827a", "#330a2d"]; 11 | 12 | var specPalette = ["#eee", "#aaa", "#888", "#666", "#444"]; 13 | 14 | this.drawRect(0, 0, w, h, "#340a2d", "#000"); 15 | 16 | const width = 48; 17 | const height = 32; 18 | 19 | const path = new Path2D(); 20 | path.moveTo(0, 0.5 * height); 21 | path.lineTo(0.25 * width, 0); 22 | path.lineTo(0.5 * width, 0); 23 | path.lineTo(0.75 * width, 0.5 * height); 24 | path.lineTo(0.5 * width, height); 25 | path.lineTo(0.25 * width, height); 26 | path.lineTo(0, 0.5 * height); 27 | 28 | let id; 29 | for (let y = -1; y < h / height + 1; y++) { 30 | for (let x = -1; x < w / width + 1; x++) { 31 | this.colorCtx.save(); 32 | this.colorCtx.translate(x * width, y * height); 33 | id = ~~(Math.random() * colorPalette.length); 34 | this.colorCtx.fillStyle = colorPalette[id]; 35 | this.colorCtx.fill(path); 36 | this.colorCtx.restore(); 37 | this.colorCtx.save(); 38 | this.colorCtx.translate((x + 0.5) * width, (y + 0.5) * height); 39 | id = ~~(Math.random() * colorPalette.length); 40 | this.colorCtx.fillStyle = colorPalette[id]; 41 | this.colorCtx.fill(path); 42 | this.colorCtx.restore(); 43 | this.colorCtx.save(); 44 | this.colorCtx.translate((x + 0.25 * 0.75) * width, (y + 0.25) * height); 45 | this.colorCtx.scale(0.5, 0.5); 46 | id = ~~(Math.random() * colorPalette.length); 47 | this.colorCtx.fillStyle = colorPalette[id]; 48 | this.colorCtx.fill(path); 49 | this.colorCtx.restore(); 50 | this.colorCtx.save(); 51 | this.colorCtx.translate( 52 | (x + 0.5 + 0.75 * 0.25) * width, 53 | (y + 0.75) * height 54 | ); 55 | this.colorCtx.scale(0.5, 0.5); 56 | id = ~~(Math.random() * colorPalette.length); 57 | this.colorCtx.fillStyle = colorPalette[id]; 58 | this.colorCtx.fill(path); 59 | this.colorCtx.restore(); 60 | this.roughnessCtx.save(); 61 | this.roughnessCtx.translate(x * width, y * height); 62 | id = ~~(Math.random() * specPalette.length); 63 | this.roughnessCtx.fillStyle = specPalette[id]; 64 | this.roughnessCtx.fill(path); 65 | this.roughnessCtx.restore(); 66 | this.roughnessCtx.save(); 67 | this.roughnessCtx.translate((x + 0.5) * width, (y + 0.5) * height); 68 | id = ~~(Math.random() * specPalette.length); 69 | this.roughnessCtx.fillStyle = specPalette[id]; 70 | this.roughnessCtx.fill(path); 71 | this.roughnessCtx.restore(); 72 | this.roughnessCtx.save(); 73 | this.roughnessCtx.translate( 74 | (x + 0.25 * 0.75) * width, 75 | (y + 0.25) * height 76 | ); 77 | this.roughnessCtx.scale(0.5, 0.5); 78 | id = ~~(Math.random() * specPalette.length); 79 | this.roughnessCtx.fillStyle = specPalette[id]; 80 | this.roughnessCtx.fill(path); 81 | this.roughnessCtx.restore(); 82 | this.roughnessCtx.save(); 83 | this.roughnessCtx.translate( 84 | (x + 0.5 + 0.75 * 0.25) * width, 85 | (y + 0.75) * height 86 | ); 87 | this.roughnessCtx.scale(0.5, 0.5); 88 | id = ~~(Math.random() * specPalette.length); 89 | this.roughnessCtx.fillStyle = specPalette[id]; 90 | this.roughnessCtx.fill(path); 91 | this.roughnessCtx.restore(); 92 | } 93 | } 94 | } 95 | } 96 | 97 | export { Paper }; 98 | -------------------------------------------------------------------------------- /modules/paper8.js: -------------------------------------------------------------------------------- 1 | import { WrappingPaper } from "./wrapping-paper.js"; 2 | 3 | class Paper extends WrappingPaper { 4 | constructor(w, h) { 5 | super(w, h); 6 | 7 | //object that defines the specular level for the specular image 8 | const opts1 = { specularColor: "#fff" }; 9 | 10 | var colorPalette = []; 11 | colorPalette.push("#ffffdd"); 12 | colorPalette.push("#f4c361"); 13 | colorPalette.push("#efad1c"); 14 | colorPalette.push("#badfd9"); 15 | colorPalette.push("#80cacd"); 16 | colorPalette.push("#a29c7c"); 17 | colorPalette.push("#574f40"); 18 | 19 | var specPalette = []; 20 | specPalette.push("#eee"); 21 | specPalette.push("#aaa"); 22 | specPalette.push("#888"); 23 | specPalette.push("#666"); 24 | specPalette.push("#444"); 25 | 26 | this.drawRect(0, 0, 512, 512, "#fff", "#000"); 27 | 28 | var s = 512 / 15; 29 | for (var y = 0; y < 512; y += s) { 30 | for (var x = 0; x < 512; x += s) { 31 | var steps = Math.ceil(Math.random() * 8); 32 | var dir = Math.random() > 0.5; 33 | for (var i = 0; i < s; i += s / steps) { 34 | var paletteIndex = Math.floor(Math.random() * colorPalette.length); 35 | var opts = { specularColor: specPalette[paletteIndex] }; 36 | if (dir) { 37 | this.drawRect( 38 | x + i, 39 | y, 40 | s / steps, 41 | s, 42 | colorPalette[paletteIndex], 43 | opts 44 | ); 45 | } else { 46 | this.drawRect( 47 | x, 48 | y + i, 49 | s, 50 | s / steps, 51 | colorPalette[paletteIndex], 52 | opts 53 | ); 54 | } 55 | } 56 | } 57 | } 58 | } 59 | } 60 | 61 | export { Paper }; 62 | -------------------------------------------------------------------------------- /modules/paper9.js: -------------------------------------------------------------------------------- 1 | import { WrappingPaper } from "./wrapping-paper.js"; 2 | 3 | class Paper extends WrappingPaper { 4 | constructor(w, h) { 5 | super(w, h); 6 | 7 | //object that defines the specular level for the specular image 8 | const opts1 = { specularColor: "#fff" }; 9 | 10 | var colorPalette = []; 11 | 12 | if (Math.random() > 0.5) { 13 | colorPalette.push("#dfd8d4"); 14 | colorPalette.push("#adabb7"); 15 | colorPalette.push("#d41a2e"); 16 | colorPalette.push("#230c41"); 17 | colorPalette.push("#17080e"); 18 | } else { 19 | colorPalette.push("#ffffdd"); 20 | colorPalette.push("#f4c361"); 21 | colorPalette.push("#efad1c"); 22 | colorPalette.push("#badfd9"); 23 | colorPalette.push("#80cacd"); 24 | colorPalette.push("#a29c7c"); 25 | colorPalette.push("#574f40"); 26 | } 27 | 28 | var specPalette = []; 29 | specPalette.push("#eee"); 30 | specPalette.push("#aaa"); 31 | specPalette.push("#888"); 32 | specPalette.push("#777"); 33 | specPalette.push("#666"); 34 | specPalette.push("#555"); 35 | specPalette.push("#444"); 36 | 37 | this.drawRect(0, 0, 512, 512, "#fff", "#000"); 38 | 39 | var s = 512; 40 | var steps = 5 + Math.ceil(Math.random() * 10); 41 | var dir = Math.random() > 0.5; 42 | for (var i = 0; i < s; i += s / steps) { 43 | var paletteIndex = Math.floor(Math.random() * colorPalette.length); 44 | var opts = { specularColor: specPalette[paletteIndex] }; 45 | if (dir) { 46 | this.drawRect(i, 0, s / steps, s, colorPalette[paletteIndex], opts); 47 | } else { 48 | this.drawRect(0, i, s, s / steps, colorPalette[paletteIndex], opts); 49 | } 50 | } 51 | } 52 | } 53 | 54 | export { Paper }; 55 | -------------------------------------------------------------------------------- /modules/post.js: -------------------------------------------------------------------------------- 1 | import { 2 | RawShaderMaterial, 3 | Vector2, 4 | RGBAFormat, 5 | UnsignedByteType, 6 | LinearFilter, 7 | ClampToEdgeWrapping 8 | } from "../third_party/three.module.js"; 9 | import { Maf } from "./maf.js"; 10 | 11 | import { getFBO } from "./fbo.js"; 12 | import orthoVertexShader from "../shaders/ortho.js"; 13 | import { ShaderPass } from "./shader-pass.js"; 14 | import { ShaderPingPongPass } from "./shader-ping-pong-pass.js"; 15 | 16 | import { fs as highlightFragmentShader } from "../shaders/highlight-fs.js"; 17 | import { fs as dofFragmentShader } from "../shaders/dof-fs.js"; 18 | import { fs as combineFragmentShader } from "../shaders/combine-fs.js"; 19 | import { fs as finalFragmentShader } from "../shaders/final-fs.js"; 20 | import { fs as blurFragmentShader } from "../shaders/blur-fs.js"; 21 | import { fs as finalColorFragmentShader } from "../shaders/final-color-fs.js"; 22 | 23 | function Post(renderer, params = {}) { 24 | const size = new Vector2(); 25 | renderer.getSize(size); 26 | const w = size.x; 27 | const h = size.y; 28 | 29 | const colorFBO = getFBO(w, h); 30 | const depthFBO = getFBO(w, h); 31 | const resolution = new Vector2(w, h); 32 | 33 | const dofShader = new RawShaderMaterial({ 34 | uniforms: { 35 | inputTexture: { value: colorFBO.texture }, 36 | depthTexture: { value: depthFBO.texture }, 37 | resolution: { value: resolution }, 38 | direction: { value: new Vector2(0, 1) } 39 | }, 40 | vertexShader: orthoVertexShader, 41 | fragmentShader: dofFragmentShader 42 | }); 43 | const dofPass = new ShaderPingPongPass( 44 | renderer, 45 | dofShader, 46 | w, 47 | h, 48 | RGBAFormat, 49 | UnsignedByteType, 50 | LinearFilter, 51 | LinearFilter, 52 | ClampToEdgeWrapping, 53 | ClampToEdgeWrapping 54 | ); 55 | 56 | const highlightShader = new RawShaderMaterial({ 57 | uniforms: { 58 | inputTexture: { value: dofPass.fbo.texture } 59 | }, 60 | vertexShader: orthoVertexShader, 61 | fragmentShader: highlightFragmentShader 62 | }); 63 | const highlightPass = new ShaderPass( 64 | renderer, 65 | highlightShader, 66 | w, 67 | h, 68 | RGBAFormat, 69 | UnsignedByteType, 70 | LinearFilter, 71 | LinearFilter, 72 | ClampToEdgeWrapping, 73 | ClampToEdgeWrapping 74 | ); 75 | 76 | const blurPasses = []; 77 | const levels = 5; 78 | const blurShader = new RawShaderMaterial({ 79 | uniforms: { 80 | inputTexture: { value: null }, 81 | resolution: { value: new Vector2(1, 1) }, 82 | direction: { value: new Vector2(0, 1) } 83 | }, 84 | vertexShader: orthoVertexShader, 85 | fragmentShader: blurFragmentShader 86 | }); 87 | let tw = 1; 88 | let th = 1; 89 | for (let i = 0; i < levels; i++) { 90 | const blurPass = new ShaderPingPongPass( 91 | renderer, 92 | blurShader, 93 | tw, 94 | th, 95 | RGBAFormat, 96 | UnsignedByteType, 97 | LinearFilter, 98 | LinearFilter, 99 | ClampToEdgeWrapping, 100 | ClampToEdgeWrapping 101 | ); 102 | blurPasses.push(blurPass); 103 | } 104 | 105 | const combineShader = new RawShaderMaterial({ 106 | uniforms: { 107 | resolution: { value: resolution }, 108 | inputTexture: { value: dofPass.fbo.texture }, 109 | blur1Texture: { value: blurPasses[0].fbo.texture }, 110 | blur2Texture: { value: blurPasses[1].fbo.texture }, 111 | blur3Texture: { value: blurPasses[2].fbo.texture }, 112 | blur4Texture: { value: blurPasses[3].fbo.texture }, 113 | blur5Texture: { value: blurPasses[4].fbo.texture }, 114 | time: { value: 0 } 115 | }, 116 | vertexShader: orthoVertexShader, 117 | fragmentShader: combineFragmentShader 118 | }); 119 | const combinePass = new ShaderPass( 120 | renderer, 121 | combineShader, 122 | w, 123 | h, 124 | RGBAFormat, 125 | UnsignedByteType, 126 | LinearFilter, 127 | LinearFilter, 128 | ClampToEdgeWrapping, 129 | ClampToEdgeWrapping 130 | ); 131 | 132 | const finalShader = new RawShaderMaterial({ 133 | uniforms: { 134 | resolution: { value: resolution }, 135 | vignetteBoost: { value: 0.5 }, 136 | vignetteReduction: { value: 0.75 }, 137 | inputTexture: { value: combinePass.fbo.texture } 138 | }, 139 | vertexShader: orthoVertexShader, 140 | fragmentShader: finalFragmentShader 141 | }); 142 | const finalPass = new ShaderPass( 143 | renderer, 144 | finalShader, 145 | w, 146 | h, 147 | RGBAFormat, 148 | UnsignedByteType, 149 | LinearFilter, 150 | LinearFilter, 151 | ClampToEdgeWrapping, 152 | ClampToEdgeWrapping 153 | ); 154 | 155 | const finalColorShader = new RawShaderMaterial({ 156 | uniforms: { 157 | resolution: { value: resolution }, 158 | time: { value: 0 }, 159 | inputTexture: { value: finalPass.fbo.texture } 160 | }, 161 | vertexShader: orthoVertexShader, 162 | fragmentShader: finalColorFragmentShader 163 | }); 164 | const finalColorPass = new ShaderPass( 165 | renderer, 166 | finalColorShader, 167 | w, 168 | h, 169 | RGBAFormat, 170 | UnsignedByteType, 171 | LinearFilter, 172 | LinearFilter, 173 | ClampToEdgeWrapping, 174 | ClampToEdgeWrapping 175 | ); 176 | 177 | function render(scene, camera, boxes) { 178 | renderer.setClearColor(0xffffff, 1); 179 | boxes.forEach(b => { 180 | b.mesh.box.material = b.mesh.colorMaterial; 181 | b.mesh.lid.material = b.mesh.colorMaterial; 182 | }); 183 | renderer.setRenderTarget(colorFBO); 184 | renderer.render(scene, camera); 185 | 186 | renderer.setClearColor(0, 0); 187 | boxes.forEach(b => { 188 | b.mesh.box.material = b.mesh.depthMaterial; 189 | b.mesh.lid.material = b.mesh.depthMaterial; 190 | }); 191 | renderer.setRenderTarget(depthFBO); 192 | renderer.render(scene, camera); 193 | 194 | renderer.setRenderTarget(null); 195 | dofPass.shader.uniforms.inputTexture.value = colorFBO.texture; 196 | for (let j = 0; j < 4; j++) { 197 | dofPass.shader.uniforms.direction.value.set(1, 0); 198 | dofPass.render(); 199 | dofPass.shader.uniforms.inputTexture.value = 200 | dofPass.fbos[dofPass.currentFBO].texture; 201 | dofPass.shader.uniforms.direction.value.set(0, 1); 202 | dofPass.render(); 203 | dofPass.shader.uniforms.inputTexture.value = 204 | dofPass.fbos[dofPass.currentFBO].texture; 205 | } 206 | highlightPass.render(); 207 | 208 | let offset = 1; 209 | blurShader.uniforms.inputTexture.value = highlightPass.fbo.texture; 210 | for (let j = 0; j < levels; j++) { 211 | const blurPass = blurPasses[j]; 212 | blurShader.uniforms.direction.value.set(offset, 0); 213 | blurShader.uniforms.resolution.value.set( 214 | blurPass.width * 2, 215 | blurPass.height * 2 216 | ); 217 | blurPass.render(); 218 | blurShader.uniforms.inputTexture.value = 219 | blurPass.fbos[blurPass.currentFBO].texture; 220 | blurShader.uniforms.direction.value.set(0, offset); 221 | blurPass.render(); 222 | blurShader.uniforms.inputTexture.value = 223 | blurPass.fbos[blurPass.currentFBO].texture; 224 | } 225 | 226 | combinePass.render(); 227 | finalPass.render(); 228 | finalColorPass.shader.uniforms.time.value = performance.now(); 229 | finalColorPass.render(true); 230 | } 231 | 232 | function setSize(w, h) { 233 | resolution.set(w, h); 234 | colorFBO.setSize(w, h); 235 | depthFBO.setSize(w, h); 236 | dofPass.setSize(w, h); 237 | combinePass.setSize(w, h); 238 | highlightPass.setSize(w, h); 239 | finalPass.setSize(w, h); 240 | finalColorPass.setSize(w, h); 241 | 242 | let tw = w; //Maf.nextPowerOfTwo(w) / 2; 243 | let th = h; //Maf.nextPowerOfTwo(h) / 2; 244 | blurPasses.forEach((pass, i) => { 245 | pass.shader.uniforms.resolution.value.set(tw, th); 246 | tw /= 2; 247 | th /= 2; 248 | pass.setSize(tw, th); 249 | }); 250 | } 251 | 252 | return { 253 | render, 254 | setSize 255 | }; 256 | } 257 | 258 | export { Post }; 259 | -------------------------------------------------------------------------------- /modules/scene.js: -------------------------------------------------------------------------------- 1 | import { 2 | PCFShadowMap, 3 | PCFSoftShadowMap, 4 | SpotLight, 5 | Scene, 6 | Group, 7 | Vector3, 8 | Matrix4, 9 | Object3D, 10 | Quaternion, 11 | PerspectiveCamera, 12 | WebGLRenderer, 13 | PointLight, 14 | CanvasTexture, 15 | Mesh, 16 | BoxBufferGeometry, 17 | TextureLoader, 18 | MeshBasicMaterial, 19 | FogExp2, 20 | AmbientLight, 21 | RepeatWrapping, 22 | PlaneBufferGeometry, 23 | MeshNormalMaterial, 24 | Color 25 | } from "../third_party/three.module.js"; 26 | import OrbitControls from "../third_party/THREE.OrbitControls.js"; 27 | import { EquirectangularToCubemap } from "../third_party/equirectangular-to-cubemap.js"; 28 | import { UpdatableTexture } from "../third_party/UpdatableTexture.js"; 29 | 30 | import { GiftBox } from "./gift-box.js"; 31 | import { Maf } from "./maf.js"; 32 | import easings from "./easings.js"; 33 | import { Post } from "./post.js"; 34 | 35 | import { Paper as Paper1 } from "./paper1.js"; 36 | import { Paper as Paper2 } from "./paper2.js"; 37 | import { Paper as Paper3 } from "./paper3.js"; 38 | import { Paper as Paper4 } from "./paper4.js"; 39 | import { Paper as Paper5 } from "./paper5.js"; 40 | import { Paper as Paper6 } from "./paper6.js"; 41 | import { Paper as Paper7 } from "./paper7.js"; 42 | import { Paper as Paper8 } from "./paper8.js"; 43 | import { Paper as Paper9 } from "./paper9.js"; 44 | import { Paper as Paper10 } from "./paper10.js"; 45 | import { Paper as Paper11 } from "./paper11.js"; 46 | import { Paper as Paper12 } from "./paper12.js"; 47 | import { Paper as Paper13 } from "./paper13.js"; 48 | import { Paper as Paper14 } from "./paper14.js"; 49 | import { Paper as Paper15 } from "./paper15.js"; 50 | import { Paper as Paper16 } from "./paper16.js"; 51 | import { Paper as Paper17 } from "./paper17.js"; 52 | import { Paper as Paper18 } from "./paper18.js"; 53 | import { Paper as Paper19 } from "./paper19.js"; 54 | import { Paper as Paper20 } from "./paper20.js"; 55 | 56 | const configs = { 57 | low: { 58 | textureSize: 256, 59 | tileSize: 32, 60 | pixelRatio: 0.5, 61 | shadow: PCFShadowMap, 62 | material: "phong", 63 | post: false 64 | }, 65 | std: { 66 | textureSize: 512, 67 | tileSize: 64, 68 | pixelRatio: 0.5, 69 | shadow: PCFSoftShadowMap, 70 | material: "pbr", 71 | post: true 72 | }, 73 | high: { 74 | textureSize: 512, 75 | tileSize: 64, 76 | pixelRatio: 1, 77 | shadow: PCFSoftShadowMap, 78 | material: "pbr", 79 | post: true 80 | }, 81 | vrlow: { 82 | textureSize: 512, 83 | tileSize: 32, 84 | pixelRatio: 1, 85 | shadow: PCFShadowMap, 86 | material: "phong", 87 | post: false 88 | }, 89 | vrhigh: { 90 | textureSize: 512, 91 | tileSize: 32, 92 | pixelRatio: 1, 93 | shadow: PCFShadowMap, 94 | material: "phong", 95 | post: false 96 | } 97 | }; 98 | 99 | let config; 100 | 101 | const papers = [ 102 | Paper1, 103 | Paper2, 104 | Paper3, 105 | Paper4, 106 | Paper5, 107 | Paper6, 108 | Paper7, 109 | Paper8, 110 | Paper9, 111 | Paper10, 112 | Paper11, 113 | Paper12, 114 | Paper13, 115 | Paper14, 116 | Paper15, 117 | Paper16, 118 | Paper17, 119 | Paper18, 120 | Paper19, 121 | Paper20 122 | ]; 123 | 124 | const renderer = new WebGLRenderer({ antialias: true }); 125 | renderer.setClearColor(0xffffff, 1); 126 | renderer.shadowMap.enabled = true; 127 | 128 | const post = new Post(renderer); 129 | 130 | const factor = 2; 131 | 132 | const scene = new Scene(); 133 | scene.fog = new FogExp2(0xffffff, 0.01); 134 | const camera = new PerspectiveCamera(60, 1, 0.01, 30 * factor); 135 | //camera.lookAt(scene.position); 136 | const cameraDummy = new Group(); 137 | cameraDummy.position.set(0, 0, 1.5 * factor); 138 | cameraDummy.add(camera); 139 | scene.add(cameraDummy); 140 | /*camera.position.set(0, 0, 1.6 * factor); 141 | scene.add(camera);*/ 142 | 143 | const ambient = new AmbientLight(0x808080); 144 | scene.add(ambient); 145 | 146 | //const controls = new OrbitControls(camera, renderer.domElement); 147 | //controls.screenSpacePanning = true; 148 | 149 | const cameraLight = new SpotLight( 150 | 0xffffff, 151 | 0.5, 152 | 20 * factor, 153 | Math.PI / 3, 154 | 0.5, 155 | 0.1 156 | ); 157 | cameraLight.castShadow = true; 158 | cameraLight.shadow.mapSize.width = 512; 159 | cameraLight.shadow.mapSize.height = 512; 160 | cameraLight.shadow.camera.near = 0.01 * factor; 161 | cameraLight.shadow.camera.far = 1.5 * factor; 162 | //cameraLight.shadow.bias = -.005; 163 | scene.add(cameraLight); 164 | 165 | let startTime; 166 | 167 | function animate() { 168 | renderer.domElement.className = "visible render"; 169 | audio.loop = true; 170 | audio.playbackRate = 1; 171 | audio.play(); 172 | startTime = performance.now(); 173 | renderer.setAnimationLoop(render); 174 | } 175 | 176 | function setSize(w, h) { 177 | renderer.setSize(w, h); 178 | camera.aspect = w / h; 179 | camera.updateProjectionMatrix(); 180 | const dPR = renderer.getPixelRatio(); 181 | post.setSize(w * dPR, h * dPR); 182 | } 183 | 184 | const duration = 0.5 * 2 * 7.385; 185 | let envMap; 186 | let normalMap; 187 | let audio; 188 | 189 | function loadAssets() { 190 | const texLoader = new TextureLoader(); 191 | return Promise.all([ 192 | new Promise((resolve, reject) => { 193 | audio = document.createElement("audio"); 194 | audio.addEventListener("canplay", e => { 195 | resolve(); 196 | }); 197 | if (audio.canPlayType('video/ogg; codecs="theora"')) { 198 | audio.src = "./assets/track.ogg"; 199 | } else { 200 | audio.src = "./assets/track.mp3"; 201 | } 202 | }), 203 | new Promise((resolve, reject) => { 204 | normalMap = texLoader.load("./assets/normal.jpg", res => resolve()); 205 | normalMap.wrapS = normalMap.wrapT = RepeatWrapping; 206 | }), 207 | new Promise((resolve, reject) => { 208 | texLoader.load("./assets/env.jpg", res => { 209 | const equiToCube = new EquirectangularToCubemap(renderer); 210 | envMap = equiToCube.convert(res, 512); 211 | envMap.needsUpdate = true; 212 | resolve(); 213 | }); 214 | }) 215 | ]); 216 | } 217 | 218 | const updateCanvas = document.createElement("canvas"); 219 | const updateCtx = updateCanvas.getContext("2d"); 220 | 221 | async function init(preset) { 222 | config = configs[preset]; 223 | 224 | renderer.setPixelRatio(window.devicePixelRatio * config.pixelRatio); 225 | renderer.shadowMap.type = config.shadow; 226 | 227 | paperSize = config.textureSize; 228 | tileSize = config.tileSize; 229 | updateCanvas.width = updateCanvas.height = tileSize; 230 | 231 | initScene(); 232 | updateBox(0, 0); 233 | updateWrappingPaper(0); 234 | } 235 | 236 | const target = new Vector3(); 237 | const m = new Matrix4(); 238 | const q = new Quaternion(); 239 | const group = new Group(); 240 | const boxes = []; 241 | const cards = []; 242 | const queue = []; 243 | let paperSize; 244 | let tileSize; 245 | 246 | let depth = 0; 247 | let prevTargetBox = null; 248 | 249 | function initScene() { 250 | for (let j = 0; j < 3; j++) { 251 | const card = new Mesh( 252 | new PlaneBufferGeometry(0.1, 0.2), 253 | new MeshNormalMaterial() 254 | ); 255 | //group.add(card); 256 | cards.push({ 257 | mesh: card, 258 | quaternion: new Quaternion() 259 | }); 260 | const box = new GiftBox(config.material); 261 | box.orientateLid(); 262 | box.scale.setScalar(1 / Math.exp(factor * j)); 263 | target 264 | .set( 265 | Maf.randomInRange(-1, 1), 266 | Maf.randomInRange(-1, 1), 267 | Maf.randomInRange(-1, 1) 268 | ) 269 | .normalize(); 270 | m.lookAt(box.position, target, Object3D.DefaultUp); 271 | q.setFromRotationMatrix(m); 272 | box.quaternion.copy(q); 273 | q.setFromRotationMatrix(m.getInverse(m)); 274 | group.add(box); 275 | boxes.push({ 276 | mesh: box, 277 | quaternion: q.clone() 278 | }); 279 | box.material.map = new UpdatableTexture(); 280 | box.material.map.anisotropy = renderer.capabilities.getMaxAnisotropy(); 281 | box.material.map.setRenderer(renderer); 282 | box.material.map.wrapS = box.material.map.wrapT = RepeatWrapping; 283 | if (config.material === "phong") { 284 | box.material.specularMap = new UpdatableTexture(); 285 | box.material.specularMap.anisotropy = renderer.capabilities.getMaxAnisotropy(); 286 | box.material.specularMap.setRenderer(renderer); 287 | box.material.specularMap.wrapS = box.material.specularMap.wrapT = RepeatWrapping; 288 | } else { 289 | box.material.roughnessMap = new UpdatableTexture(); 290 | box.material.map.roughnessMap = renderer.capabilities.getMaxAnisotropy(); 291 | box.material.roughnessMap.setRenderer(renderer); 292 | box.material.roughnessMap.wrapS = box.material.roughnessMap.wrapT = RepeatWrapping; 293 | box.material.metalnessMap = box.material.map; 294 | } 295 | box.material.normalMap = normalMap; 296 | box.material.normalMap.wrapS = box.material.normalMap.wrapT = RepeatWrapping; 297 | box.material.normalScale.set(0.05, 0.05); 298 | box.material.envMap = envMap; 299 | box.material.envMapIntensity = 0.8; 300 | box.material.needsUpdate = true; 301 | } 302 | scene.add(group); 303 | renderer.render(scene, camera); 304 | boxes.forEach(b => { 305 | const emptyCanvas = document.createElement("canvas"); 306 | emptyCanvas.width = emptyCanvas.height = paperSize; 307 | b.mesh.material.map.setSize(paperSize, paperSize); 308 | b.mesh.material.map.update(emptyCanvas, 0, 0); 309 | if (config.material === "phong") { 310 | b.mesh.material.specularMap.setSize(paperSize, paperSize); 311 | b.mesh.material.specularMap.update(emptyCanvas, 0, 0); 312 | } else { 313 | b.mesh.material.roughnessMap.setSize(paperSize, paperSize); 314 | b.mesh.material.roughnessMap.update(emptyCanvas, 0, 0); 315 | } 316 | }); 317 | } 318 | 319 | function updateBox(ptr, count) { 320 | const box = boxes[ptr]; 321 | box.mesh.refresh(); 322 | box.mesh.scale.setScalar(1 / Math.exp(factor * count)); 323 | target 324 | .set( 325 | Maf.randomInRange(-1, 1), 326 | Maf.randomInRange(-1, 1), 327 | Maf.randomInRange(-1, 1) 328 | ) 329 | .normalize(); 330 | m.lookAt(box.mesh.position, target, Object3D.DefaultUp); 331 | q.setFromRotationMatrix(m); 332 | box.mesh.quaternion.copy(q); 333 | q.setFromRotationMatrix(m.getInverse(m)); 334 | box.quaternion.copy(q); 335 | const card = cards[ptr]; 336 | card.mesh.scale.setScalar(1 / Math.exp(factor * count)); 337 | card.mesh.quaternion.copy(box.mesh.quaternion); 338 | } 339 | 340 | let sequence = [1]; 341 | let paperCounter = 0; 342 | 343 | function buildSequence() { 344 | const lastOne = sequence[sequence.length - 1]; 345 | sequence = []; 346 | for (let j = 0; j < papers.length; j++) { 347 | sequence.push(j); 348 | } 349 | do { 350 | sequence.sort((a, b) => Maf.randomInRange(-1, 1)); 351 | } while (lastOne === sequence[0]); 352 | } 353 | buildSequence(); 354 | 355 | function updateWrappingPaper(ptr) { 356 | const box = boxes[ptr]; 357 | const Paper = papers[sequence[paperCounter]]; 358 | const p = new Paper(paperSize, paperSize); 359 | paperCounter++; 360 | if (paperCounter > sequence.length - 1) { 361 | buildSequence(); 362 | paperCounter = 0; 363 | } 364 | for (let y = 0; y < p.colorCanvas.height; y += tileSize) { 365 | for (let x = 0; x < p.colorCanvas.width; x += tileSize) { 366 | queue.push({ 367 | target: box.mesh.material.map, 368 | source: p.colorCanvas, 369 | x, 370 | y, 371 | width: tileSize, 372 | height: tileSize 373 | }); 374 | } 375 | } 376 | for (let y = 0; y < p.roughnessCanvas.height; y += tileSize) { 377 | for (let x = 0; x < p.roughnessCanvas.width; x += tileSize) { 378 | queue.push({ 379 | target: 380 | config.material === "phong" 381 | ? box.mesh.material.specularMap 382 | : box.mesh.material.roughnessMap, 383 | source: p.roughnessCanvas, 384 | x, 385 | y, 386 | width: tileSize, 387 | height: tileSize 388 | }); 389 | } 390 | } 391 | } 392 | 393 | /*requestIdleCallback(processQueue); 394 | 395 | function processQueue(deadline) { 396 | while (deadline.timeRemaining()) { 397 | const task = queue.shift(); 398 | if (task) { 399 | updateCtx.drawImage(task.source, task.x, task.y, tileSize, tileSize, 0, 0, tileSize, tileSize); 400 | task.target.update(updateCanvas, task.x, task.y); 401 | } 402 | } 403 | requestIdleCallback(processQueue); 404 | }*/ 405 | 406 | function render() { 407 | const delta = Math.max( 408 | (1 * (performance.now() - startTime)) / (duration * 1000), 409 | 0 410 | ); 411 | const targetBox = Math.max(~~delta, 0) % boxes.length; 412 | const prevBox = Maf.mod(targetBox - 1, boxes.length); 413 | const nextBox = Maf.mod(targetBox + 1, boxes.length); 414 | if (targetBox !== prevTargetBox) { 415 | depth++; 416 | updateBox(nextBox, depth); 417 | updateWrappingPaper(Maf.mod(nextBox, boxes.length)); 418 | prevTargetBox = targetBox; 419 | } 420 | 421 | const qTo = boxes[targetBox].quaternion; 422 | const qFrom = boxes[prevBox].quaternion; 423 | 424 | boxes[prevBox].mesh.pivot.rotation.y = 425 | Maf.PI - Maf.clamp(3 * (delta % 1) * Maf.PI, 0, Maf.PI); 426 | boxes[targetBox].mesh.pivot.rotation.y = (delta % 1) * Maf.PI; 427 | boxes[nextBox].mesh.pivot.rotation.y = 0; 428 | group.quaternion.copy(qFrom).slerp(qTo, easings.InOutQuad(delta % 1)); 429 | group.scale.setScalar(Math.exp(factor * delta)); 430 | target.copy(scene.position); 431 | const t = 0.0001 * performance.now(); 432 | target.x += 0.5 * Math.cos(t); 433 | target.y += 0.5 * Math.sin(t); 434 | camera.lookAt(target); 435 | camera.rotation.z = 0.5 * delta * Maf.TAU; 436 | cameraLight.position.copy(cameraDummy.position); 437 | cameraLight.position.y += 0.5; 438 | cameraLight.position.z -= 1; 439 | 440 | if (renderer.xr.enabled || config.post === false) { 441 | renderer.render(scene, camera); 442 | } else { 443 | post.render(scene, camera, boxes); 444 | } 445 | 446 | for (let j = 0; j < 8; j++) { 447 | const task = queue.shift(); 448 | if (task) { 449 | updateCtx.drawImage( 450 | task.source, 451 | task.x, 452 | task.y, 453 | tileSize, 454 | tileSize, 455 | 0, 456 | 0, 457 | tileSize, 458 | tileSize 459 | ); 460 | task.target.update(updateCanvas, task.x, task.y); 461 | } 462 | } 463 | } 464 | 465 | export { renderer, setSize, render, animate, init, loadAssets }; 466 | -------------------------------------------------------------------------------- /modules/shader-pass.js: -------------------------------------------------------------------------------- 1 | import { 2 | Scene, 3 | WebGLRenderTarget, 4 | RepeatWrapping, 5 | LinearFilter, 6 | LinearMipMapLinearFilter, 7 | RGBAFormat, 8 | UnsignedByteType, 9 | OrthographicCamera, 10 | PlaneBufferGeometry, 11 | Mesh 12 | } from "../third_party/three.module.js"; 13 | 14 | class ShaderPass { 15 | constructor( 16 | renderer, 17 | shader, 18 | width, 19 | height, 20 | format, 21 | type, 22 | minFilter, 23 | magFilter, 24 | wrapS, 25 | wrapT 26 | ) { 27 | this.renderer = renderer; 28 | this.shader = shader; 29 | this.orthoScene = new Scene(); 30 | this.fbo = new WebGLRenderTarget(width, height, { 31 | wrapS: wrapS || RepeatWrapping, 32 | wrapT: wrapT || RepeatWrapping, 33 | minFilter: minFilter || LinearMipMapLinearFilter, 34 | magFilter: magFilter || LinearFilter, 35 | format: format || RGBAFormat, 36 | type: type || UnsignedByteType 37 | }); 38 | this.orthoCamera = new OrthographicCamera( 39 | width / -2, 40 | width / 2, 41 | height / 2, 42 | height / -2, 43 | 0.00001, 44 | 1000 45 | ); 46 | this.orthoQuad = new Mesh(new PlaneBufferGeometry(1, 1), this.shader); 47 | this.orthoQuad.scale.set(width, height, 1); 48 | this.orthoScene.add(this.orthoQuad); 49 | this.texture = this.fbo.texture; 50 | } 51 | 52 | render(final) { 53 | this.renderer.setRenderTarget(final ? null : this.fbo); 54 | this.renderer.render(this.orthoScene, this.orthoCamera); 55 | } 56 | 57 | setSize(width, height) { 58 | this.orthoQuad.scale.set(width, height, 1); 59 | 60 | this.fbo.setSize(width, height); 61 | 62 | this.orthoQuad.scale.set(width, height, 1); 63 | 64 | this.orthoCamera.left = -width / 2; 65 | this.orthoCamera.right = width / 2; 66 | this.orthoCamera.top = height / 2; 67 | this.orthoCamera.bottom = -height / 2; 68 | this.orthoCamera.updateProjectionMatrix(); 69 | } 70 | } 71 | 72 | export { ShaderPass }; 73 | -------------------------------------------------------------------------------- /modules/shader-ping-pong-pass.js: -------------------------------------------------------------------------------- 1 | import { 2 | Scene, 3 | WebGLRenderTarget, 4 | RepeatWrapping, 5 | LinearFilter, 6 | LinearMipMapLinearFilter, 7 | RGBAFormat, 8 | UnsignedByteType, 9 | OrthographicCamera, 10 | PlaneBufferGeometry, 11 | Mesh 12 | } from "../third_party/three.module.js"; 13 | 14 | class ShaderPingPongPass { 15 | constructor( 16 | renderer, 17 | shader, 18 | width, 19 | height, 20 | format, 21 | type, 22 | minFilter, 23 | magFilter, 24 | wrapS, 25 | wrapT 26 | ) { 27 | this.renderer = renderer; 28 | this.shader = shader; 29 | this.orthoScene = new Scene(); 30 | this.fbo = new WebGLRenderTarget(width, height, { 31 | wrapS: wrapS || RepeatWrapping, 32 | wrapT: wrapT || RepeatWrapping, 33 | minFilter: minFilter || LinearMipMapLinearFilter, 34 | magFilter: magFilter || LinearFilter, 35 | format: format || RGBAFormat, 36 | type: type || UnsignedByteType 37 | }); 38 | this.fbos = [this.fbo, this.fbo.clone()]; 39 | this.currentFBO = 0; 40 | this.orthoCamera = new OrthographicCamera( 41 | width / -2, 42 | width / 2, 43 | height / 2, 44 | height / -2, 45 | 0.00001, 46 | 1000 47 | ); 48 | this.orthoQuad = new Mesh(new PlaneBufferGeometry(1, 1), this.shader); 49 | this.orthoQuad.scale.set(width, height, 1); 50 | this.orthoScene.add(this.orthoQuad); 51 | this.texture = this.fbo.texture; 52 | this.width = 1; 53 | this.height = 1; 54 | } 55 | 56 | render(final) { 57 | this.renderer.setRenderTarget( 58 | final ? null : this.fbos[1 - this.currentFBO] 59 | ); 60 | this.renderer.render(this.orthoScene, this.orthoCamera); 61 | this.currentFBO = 1 - this.currentFBO; 62 | } 63 | 64 | setSize(width, height) { 65 | this.orthoQuad.scale.set(width, height, 1); 66 | 67 | this.fbos[0].setSize(width, height); 68 | this.fbos[1].setSize(width, height); 69 | 70 | this.orthoQuad.scale.set(width, height, 1); 71 | 72 | this.orthoCamera.left = -width / 2; 73 | this.orthoCamera.right = width / 2; 74 | this.orthoCamera.top = height / 2; 75 | this.orthoCamera.bottom = -height / 2; 76 | this.orthoCamera.updateProjectionMatrix(); 77 | 78 | this.width = width; 79 | this.height = height; 80 | } 81 | } 82 | 83 | export { ShaderPingPongPass }; 84 | -------------------------------------------------------------------------------- /modules/wrapping-paper.js: -------------------------------------------------------------------------------- 1 | import { Maf } from "./maf.js"; 2 | 3 | class WrappingPaper { 4 | constructor(w = 1024, h = 1024) { 5 | this.width = w; 6 | this.height = h; 7 | 8 | this.colorCanvas = document.createElement("canvas"); 9 | this.colorCanvas.width = w; 10 | this.colorCanvas.height = h; 11 | this.colorCtx = this.colorCanvas.getContext("2d"); 12 | this.roughnessCanvas = document.createElement("canvas"); 13 | this.roughnessCanvas.width = w; 14 | this.roughnessCanvas.height = h; 15 | this.roughnessCtx = this.roughnessCanvas.getContext("2d"); 16 | this.metalnessCanvas = document.createElement("canvas"); 17 | this.metalnessCanvas.width = w; 18 | this.metalnessCanvas.height = h; 19 | this.metalnessCtx = this.metalnessCanvas.getContext("2d"); 20 | 21 | // document.body.appendChild(this.colorCanvas); 22 | // document.body.appendChild(this.roughnessCanvas); 23 | 24 | this.contexts = [this.colorCtx, this.roughnessCtx, this.metalnessCtx]; 25 | } 26 | 27 | clearRect(originX, originY, width, height) { 28 | for (var i = 0; i < this.contexts.length; i++) { 29 | this.contexts[i].clearRect(originX, originY, width, height); 30 | } 31 | } 32 | 33 | drawRect(originX, originY, width, height, color, opts) { 34 | /* opts example 35 | opts = { 36 | strokeColor: "#fff", 37 | lineWidth: 10, 38 | specularColor: "#000", 39 | specularContext: ctxs 40 | }; 41 | */ 42 | var contexts = [this.colorCtx]; 43 | var colors = []; 44 | if (color) colors[0] = color; 45 | else colors[0] = "#000"; 46 | if (opts && this.roughnessCtx) { 47 | contexts[1] = this.roughnessCtx; 48 | if (opts.specularColor) colors[1] = opts.specularColor; 49 | else colors[1] = "#000"; 50 | } 51 | 52 | for (var i = 0; i < contexts.length; i++) { 53 | contexts[i].fillStyle = colors[i]; 54 | contexts[i].fillRect(originX, originY, width, height); 55 | } 56 | } 57 | 58 | drawArc(centerX, centerY, radius, start_radians, radians, color, opts) { 59 | var contexts = [this.colorCtx]; 60 | var colors = []; 61 | if (color) colors[0] = this.hexToRgb(color); 62 | else colors[0] = this.hexToRgb("#000"); 63 | var lineWidth = 10; 64 | if (radians === undefined) var radians = Math.PI / 2; 65 | if (opts && this.roughnessCtx) { 66 | contexts[1] = this.roughnessCtx; 67 | if (opts.specularColor) colors[1] = this.hexToRgb(opts.specularColor); 68 | else colors[1] = this.hexToRgb("#000"); 69 | if (opts.lineWidth) lineWidth = opts.lineWidth; 70 | else lineWidth = 10; 71 | } 72 | 73 | for (var i = 0; i < contexts.length; i++) { 74 | contexts[i].beginPath(); 75 | contexts[i].lineWidth = lineWidth; 76 | contexts[i].lineCap = "round"; 77 | contexts[i].arc(centerX, centerY, radius, start_radians, radians, false); 78 | contexts[i].strokeStyle = 79 | "rgba(" + colors[i].r + "," + colors[i].g + "," + colors[i].b + ",1)"; 80 | contexts[i].stroke(); 81 | } 82 | } 83 | 84 | drawCircle(centerX, centerY, radius, color, opts) { 85 | var colors = []; 86 | var alpha = 1.0; 87 | if (color) colors[0] = this.hexToRgb(color); 88 | else colors[0] = this.hexToRgb("#000"); 89 | const contexts = [this.colorCtx]; 90 | if (opts) { 91 | contexts.push(this.roughnessCtx); 92 | if (opts.specularColor) colors[1] = this.hexToRgb(opts.specularColor); 93 | else colors[1] = this.hexToRgb("#000"); 94 | if (opts.alpha) alpha = opts.alpha; 95 | else alpha = 1.0; 96 | } 97 | 98 | for (var i = 0; i < contexts.length; i++) { 99 | contexts[i].beginPath(); 100 | contexts[i].arc(centerX, centerY, radius, 0, 2 * Math.PI, false); 101 | contexts[i].fillStyle = 102 | "rgba(" + 103 | colors[i].r + 104 | "," + 105 | colors[i].g + 106 | "," + 107 | colors[i].b + 108 | "," + 109 | alpha + 110 | ")"; 111 | contexts[i].fill(); 112 | } 113 | } 114 | 115 | hexToRgb(hex) { 116 | // Expand shorthand form (e.g. "03F") to full form (e.g. "0033FF") 117 | var shorthandRegex = /^#?([a-f\d])([a-f\d])([a-f\d])$/i; 118 | hex = hex.replace(shorthandRegex, function(m, r, g, b) { 119 | return r + r + g + g + b + b; 120 | }); 121 | 122 | var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex); 123 | return result 124 | ? { 125 | r: parseInt(result[1], 16), 126 | g: parseInt(result[2], 16), 127 | b: parseInt(result[3], 16) 128 | } 129 | : null; 130 | } 131 | } 132 | 133 | export { WrappingPaper }; 134 | -------------------------------------------------------------------------------- /paper.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | XMasXP 2018 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /preview.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spite/infinite-gift/eba6257d277a94d4c3d7ac702547394f09da78a5/preview.jpg -------------------------------------------------------------------------------- /shaders/blur-fs.js: -------------------------------------------------------------------------------- 1 | import { blur5 } from "./fast-separable-gaussian-blur.js"; 2 | 3 | const fs = ` 4 | precision highp float; 5 | 6 | uniform vec2 resolution; 7 | uniform sampler2D inputTexture; 8 | uniform vec2 direction; 9 | 10 | varying vec2 vUv; 11 | 12 | ${blur5} 13 | 14 | void main() { 15 | gl_FragColor = blur5(inputTexture, vUv, resolution, direction); 16 | }`; 17 | 18 | export { fs }; 19 | -------------------------------------------------------------------------------- /shaders/combine-fs.js: -------------------------------------------------------------------------------- 1 | import screen from './screen.js'; 2 | import softLight from './soft-light.js'; 3 | 4 | const fs = ` 5 | precision highp float; 6 | 7 | uniform vec2 resolution; 8 | 9 | uniform sampler2D inputTexture; 10 | uniform sampler2D blur1Texture; 11 | uniform sampler2D blur2Texture; 12 | uniform sampler2D blur3Texture; 13 | uniform sampler2D blur4Texture; 14 | uniform sampler2D blur5Texture; 15 | uniform sampler2D depthTexture; 16 | 17 | varying vec2 vUv; 18 | 19 | ${screen} 20 | ${softLight} 21 | 22 | void main() { 23 | vec4 color = texture2D(inputTexture, vUv); 24 | vec4 bloom = vec4(0.); 25 | bloom += 1. *texture2D( blur1Texture, vUv ); 26 | bloom += 1.2 * texture2D( blur2Texture, vUv ); 27 | bloom += 1.4 * texture2D( blur3Texture, vUv ); 28 | bloom += 1.6 * texture2D( blur4Texture, vUv ); 29 | bloom += 1.8 * texture2D( blur5Texture, vUv ); 30 | 31 | vec4 finalColor = color; 32 | //finalColor = finalColor + .25 * bloom; 33 | finalColor = screen(finalColor,bloom,.25); 34 | gl_FragColor = finalColor; 35 | } 36 | `; 37 | 38 | export { fs }; -------------------------------------------------------------------------------- /shaders/depth-fs.js: -------------------------------------------------------------------------------- 1 | const fs = ` 2 | precision highp float; 3 | 4 | varying float vDepth; 5 | 6 | void main() { 7 | gl_FragColor = vec4(vDepth); 8 | } 9 | `; 10 | 11 | export { fs }; -------------------------------------------------------------------------------- /shaders/depth-vs.js: -------------------------------------------------------------------------------- 1 | const vs = ` 2 | precision highp float; 3 | attribute vec3 position; 4 | 5 | uniform mat4 modelViewMatrix; 6 | uniform mat4 projectionMatrix; 7 | uniform vec3 cameraPosition; 8 | 9 | varying float vDepth; 10 | 11 | void main() { 12 | vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 ); 13 | float l = length(cameraPosition); 14 | vDepth = abs(-mvPosition.z-l) / abs(l); 15 | vDepth = .1+ .9*vDepth; 16 | gl_Position = projectionMatrix * mvPosition; 17 | } 18 | `; 19 | 20 | export { vs }; -------------------------------------------------------------------------------- /shaders/dof-fs.js: -------------------------------------------------------------------------------- 1 | import { blur5 } from './fast-separable-gaussian-blur.js'; 2 | 3 | const fs = ` 4 | precision highp float; 5 | 6 | uniform sampler2D inputTexture; 7 | uniform sampler2D depthTexture; 8 | 9 | uniform vec2 resolution; 10 | uniform vec2 direction; 11 | 12 | varying vec2 vUv; 13 | 14 | ${blur5} 15 | 16 | void main() { 17 | float depth = texture2D(depthTexture, vUv).r; 18 | float r = 2. * clamp(smoothstep(.25,.75,clamp(abs(depth-.25),0.,1.)), 0., 1.); 19 | if(r>1.) { 20 | gl_FragColor = blur5(inputTexture, vUv, resolution, r * direction); 21 | } else { 22 | gl_FragColor = texture2D(inputTexture, vUv); 23 | } 24 | }`; 25 | 26 | export { fs }; -------------------------------------------------------------------------------- /shaders/fast-separable-gaussian-blur.js: -------------------------------------------------------------------------------- 1 | const blur5 = ` 2 | vec4 blur5(sampler2D image, vec2 uv, vec2 resolution, vec2 direction) { 3 | vec4 color = vec4(0.0); 4 | vec2 off1 = vec2(1.3333333333333333) * direction; 5 | color += texture2D(image, uv) * 0.29411764705882354; 6 | color += texture2D(image, uv + (off1 / resolution)) * 0.35294117647058826; 7 | color += texture2D(image, uv - (off1 / resolution)) * 0.35294117647058826; 8 | return color; 9 | }`; 10 | 11 | const blur9 = ` 12 | vec4 blur9(sampler2D image, vec2 uv, vec2 resolution, vec2 direction) { 13 | vec4 color = vec4(0.0); 14 | vec2 off1 = vec2(1.3846153846) * direction; 15 | vec2 off2 = vec2(3.2307692308) * direction; 16 | color += texture2D(image, uv) * 0.2270270270; 17 | color += texture2D(image, uv + (off1 / resolution)) * 0.3162162162; 18 | color += texture2D(image, uv - (off1 / resolution)) * 0.3162162162; 19 | color += texture2D(image, uv + (off2 / resolution)) * 0.0702702703; 20 | color += texture2D(image, uv - (off2 / resolution)) * 0.0702702703; 21 | return color; 22 | }`; 23 | 24 | const blur13 = ` 25 | vec4 blur13(sampler2D image, vec2 uv, vec2 resolution, vec2 direction) { 26 | vec4 color = vec4(0.0); 27 | vec2 off1 = vec2(1.411764705882353) * direction; 28 | vec2 off2 = vec2(3.2941176470588234) * direction; 29 | vec2 off3 = vec2(5.176470588235294) * direction; 30 | color += texture2D(image, uv) * 0.1964825501511404; 31 | color += texture2D(image, uv + (off1 / resolution)) * 0.2969069646728344; 32 | color += texture2D(image, uv - (off1 / resolution)) * 0.2969069646728344; 33 | color += texture2D(image, uv + (off2 / resolution)) * 0.09447039785044732; 34 | color += texture2D(image, uv - (off2 / resolution)) * 0.09447039785044732; 35 | color += texture2D(image, uv + (off3 / resolution)) * 0.010381362401148057; 36 | color += texture2D(image, uv - (off3 / resolution)) * 0.010381362401148057; 37 | return color; 38 | } 39 | `; 40 | 41 | export {blur5, blur9, blur13}; 42 | -------------------------------------------------------------------------------- /shaders/final-color-fs.js: -------------------------------------------------------------------------------- 1 | import fxaa from './fxaa.js'; 2 | import grayscale from './grayscale.js'; 3 | import overlay from './overlay.js'; 4 | import random from './random.js'; 5 | 6 | const fs = ` 7 | precision highp float; 8 | 9 | uniform vec2 resolution; 10 | uniform float time; 11 | 12 | uniform sampler2D inputTexture; 13 | 14 | varying vec2 vUv; 15 | ${fxaa} 16 | ${grayscale} 17 | ${overlay} 18 | ${random} 19 | 20 | void main() { 21 | vec4 color = fxaa(inputTexture, vUv); 22 | color = overlay(color,vec4(random(vec3(vUv,0.),time)),.1); 23 | gl_FragColor = color;//vec4(grayscale(color)); 24 | } 25 | `; 26 | 27 | export { fs }; -------------------------------------------------------------------------------- /shaders/final-fs.js: -------------------------------------------------------------------------------- 1 | import vignette from './vignette.js'; 2 | import softLight from './soft-light.js'; 3 | import screen from './screen.js'; 4 | import { gammaCorrect, levelRange, finalLevels } from './levels.js'; 5 | import fxaa from './fxaa.js'; 6 | import rgbShift from './rgb-shift.js'; 7 | 8 | const fs = ` 9 | precision highp float; 10 | 11 | uniform vec2 resolution; 12 | 13 | uniform sampler2D inputTexture; 14 | uniform float vignetteBoost; 15 | uniform float vignetteReduction; 16 | 17 | varying vec2 vUv; 18 | ${vignette} 19 | ${softLight} 20 | ${screen} 21 | ${gammaCorrect} 22 | ${levelRange} 23 | ${finalLevels} 24 | ${rgbShift} 25 | 26 | void main() { 27 | vec4 color = rgbShift(inputTexture, vUv, resolution/80.); 28 | vec4 v = vec4(vec3(vignette(vUv, vignetteBoost, vignetteReduction)),1.); 29 | vec4 finalColor = softLight(color, v); 30 | finalColor.rgb = finalLevels(finalColor.rgb, vec3(20.) / 255., vec3(1.), vec3(235.)/ 255.); 31 | gl_FragColor = finalColor; 32 | } 33 | `; 34 | 35 | export { fs }; -------------------------------------------------------------------------------- /shaders/fxaa.js: -------------------------------------------------------------------------------- 1 | const fxaa = ` 2 | 3 | #define FXAA_REDUCE_MIN (1.0/128.0) 4 | #define FXAA_REDUCE_MUL (1.0/8.0) 5 | #define FXAA_SPAN_MAX 8.0 6 | 7 | vec4 fxaa(sampler2D tex, vec2 uv) { 8 | 9 | vec2 res = 1. / resolution; 10 | 11 | vec3 rgbNW = texture2D( tex, ( uv.xy + vec2( -1.0, -1.0 ) * res ) ).xyz; 12 | vec3 rgbNE = texture2D( tex, ( uv.xy + vec2( 1.0, -1.0 ) * res ) ).xyz; 13 | vec3 rgbSW = texture2D( tex, ( uv.xy + vec2( -1.0, 1.0 ) * res ) ).xyz; 14 | vec3 rgbSE = texture2D( tex, ( uv.xy + vec2( 1.0, 1.0 ) * res ) ).xyz; 15 | vec4 rgbaM = texture2D( tex, uv.xy * res ); 16 | vec3 rgbM = rgbaM.xyz; 17 | vec3 luma = vec3( 0.299, 0.587, 0.114 ); 18 | 19 | float lumaNW = dot( rgbNW, luma ); 20 | float lumaNE = dot( rgbNE, luma ); 21 | float lumaSW = dot( rgbSW, luma ); 22 | float lumaSE = dot( rgbSE, luma ); 23 | float lumaM = dot( rgbM, luma ); 24 | float lumaMin = min( lumaM, min( min( lumaNW, lumaNE ), min( lumaSW, lumaSE ) ) ); 25 | float lumaMax = max( lumaM, max( max( lumaNW, lumaNE) , max( lumaSW, lumaSE ) ) ); 26 | 27 | vec2 dir; 28 | dir.x = -((lumaNW + lumaNE) - (lumaSW + lumaSE)); 29 | dir.y = ((lumaNW + lumaSW) - (lumaNE + lumaSE)); 30 | 31 | float dirReduce = max( ( lumaNW + lumaNE + lumaSW + lumaSE ) * ( 0.25 * FXAA_REDUCE_MUL ), FXAA_REDUCE_MIN ); 32 | 33 | float rcpDirMin = 1.0 / ( min( abs( dir.x ), abs( dir.y ) ) + dirReduce ); 34 | dir = min( vec2( FXAA_SPAN_MAX, FXAA_SPAN_MAX), 35 | max( vec2(-FXAA_SPAN_MAX, -FXAA_SPAN_MAX), 36 | dir * rcpDirMin)) * res; 37 | vec4 rgbA = (1.0/2.0) * ( 38 | texture2D(tex, uv.xy + dir * (1.0/3.0 - 0.5)) + 39 | texture2D(tex, uv.xy + dir * (2.0/3.0 - 0.5))); 40 | vec4 rgbB = rgbA * (1.0/2.0) + (1.0/4.0) * ( 41 | texture2D(tex, uv.xy + dir * (0.0/3.0 - 0.5)) + 42 | texture2D(tex, uv.xy + dir * (3.0/3.0 - 0.5))); 43 | float lumaB = dot(rgbB, vec4(luma, 0.0)); 44 | 45 | if ( ( lumaB < lumaMin ) || ( lumaB > lumaMax ) ) { 46 | return rgbA; 47 | } else { 48 | return rgbB; 49 | } 50 | 51 | } 52 | `; 53 | 54 | export default fxaa; 55 | -------------------------------------------------------------------------------- /shaders/grayscale.js: -------------------------------------------------------------------------------- 1 | const grayscale = ` 2 | float grayscale(vec3 color) { 3 | return dot(color.rgb, vec3(0.299, 0.587, 0.114)); 4 | } 5 | 6 | float grayscale(vec4 color) { 7 | return dot(color.rgb, vec3(0.299, 0.587, 0.114)); 8 | }`; 9 | 10 | export default grayscale; -------------------------------------------------------------------------------- /shaders/highlight-fs.js: -------------------------------------------------------------------------------- 1 | import grayscale from './grayscale.js'; 2 | 3 | const fs = ` 4 | precision highp float; 5 | 6 | uniform sampler2D inputTexture; 7 | 8 | varying vec2 vUv; 9 | 10 | ${grayscale} 11 | 12 | void main() { 13 | vec4 color = texture2D(inputTexture, vUv); 14 | gl_FragColor = vec4(smoothstep(.7, 1., grayscale(color))); 15 | } 16 | `; 17 | 18 | export { fs }; -------------------------------------------------------------------------------- /shaders/levels.js: -------------------------------------------------------------------------------- 1 | const gammaCorrect = ` 2 | vec3 gammaCorrect(vec3 color, vec3 gamma){ 3 | return pow(color, 1.0/gamma); 4 | }`; 5 | 6 | const levelRange = ` 7 | vec3 levelRange(vec3 color, vec3 minInput, vec3 maxInput){ 8 | return min(max(color - minInput, vec3(0.0)) / (maxInput - minInput), vec3(1.0)); 9 | }`; 10 | 11 | const finalLevels = ` 12 | vec3 finalLevels(vec3 color, vec3 minInput, vec3 gamma, vec3 maxInput){ 13 | return gammaCorrect(levelRange(color, minInput, maxInput), gamma); 14 | }`; 15 | 16 | export { gammaCorrect, levelRange, finalLevels }; -------------------------------------------------------------------------------- /shaders/ortho.js: -------------------------------------------------------------------------------- 1 | const turbulence = ` 2 | precision highp float; 3 | 4 | attribute vec3 position; 5 | attribute vec2 uv; 6 | 7 | uniform mat4 modelViewMatrix; 8 | uniform mat4 projectionMatrix; 9 | 10 | varying vec2 vUv; 11 | 12 | void main() { 13 | vUv = uv; 14 | gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1. ); 15 | }`; 16 | 17 | export default turbulence; 18 | -------------------------------------------------------------------------------- /shaders/overlay.js: -------------------------------------------------------------------------------- 1 | const overlay = ` 2 | float applyOverlayToChannel( float base, float blend ) { 3 | return (base < 0.5 ? (2.0 * base * blend) : (1.0 - 2.0 * (1.0 - base) * (1.0 - blend))); 4 | } 5 | 6 | vec4 overlay(vec4 base, vec4 blend, float opacity) { 7 | vec4 color = vec4( 8 | applyOverlayToChannel( base.r, blend.r ), 9 | applyOverlayToChannel( base.g, blend.g ), 10 | applyOverlayToChannel( base.b, blend.b ), 11 | applyOverlayToChannel( base.a, blend.a ) 12 | ); 13 | return color * opacity + base * ( 1. - opacity ); 14 | } 15 | 16 | `; 17 | 18 | export default overlay; 19 | -------------------------------------------------------------------------------- /shaders/random.js: -------------------------------------------------------------------------------- 1 | const random = ` 2 | float random(vec3 scale,float seed){ 3 | return fract(sin(dot(gl_FragCoord.xyz+seed,scale))*43758.5453+seed); 4 | } 5 | `; 6 | 7 | export default random; -------------------------------------------------------------------------------- /shaders/rgb-shift.js: -------------------------------------------------------------------------------- 1 | import smootherstep from './smootherstep.js'; 2 | 3 | const rgbShift = ` 4 | ${smootherstep} 5 | 6 | vec4 rgbShift(sampler2D inputTexture, vec2 uv, vec2 delta){ 7 | 8 | vec2 dir = uv - vec2( .5 ); 9 | float d = .7 * length( dir ); 10 | normalize( dir ); 11 | vec2 value = d * dir * delta; 12 | 13 | vec4 c1 = texture2D( inputTexture, uv - value / resolution.x ); 14 | vec4 c2 = texture2D( inputTexture, uv ); 15 | vec4 c3 = texture2D( inputTexture, uv + value / resolution.y ); 16 | 17 | return vec4( c1.r, c2.g, c3.b, c1.a + c2.a + c3.b ); 18 | 19 | } 20 | `; 21 | 22 | export default rgbShift; -------------------------------------------------------------------------------- /shaders/screen.js: -------------------------------------------------------------------------------- 1 | const screen = ` 2 | vec4 screen(vec4 base, vec4 blend, float opacity) { 3 | vec4 color = 1. - (1.-base) * (1.-blend); 4 | color = color * opacity + base * ( 1. - opacity ); 5 | return color; 6 | }`; 7 | 8 | export default screen; -------------------------------------------------------------------------------- /shaders/smootherstep.js: -------------------------------------------------------------------------------- 1 | const smootherstep = ` 2 | float smootherstep(float edge0, float edge1, float x) { 3 | x = clamp((x - edge0)/(edge1 - edge0), 0.0, 1.0); 4 | return x*x*x*(x*(x*6. - 15.) + 10.); 5 | } 6 | `; 7 | 8 | export default smootherstep; 9 | -------------------------------------------------------------------------------- /shaders/soft-light.js: -------------------------------------------------------------------------------- 1 | const softLight = ` 2 | float applySoftLightToChannel( float base, float blend ) { 3 | return ((blend < 0.5) ? (2.0 * base * blend + base * base * (1.0 - 2.0 * blend)) : (sqrt(base) * (2.0 * blend - 1.0) + 2.0 * base * (1.0 - blend))); 4 | } 5 | 6 | vec4 softLight(vec4 base, vec4 blend) { 7 | vec4 color = vec4( 8 | applySoftLightToChannel( base.r, blend.r ), 9 | applySoftLightToChannel( base.g, blend.g ), 10 | applySoftLightToChannel( base.b, blend.b ), 11 | applySoftLightToChannel( base.a, blend.a ) 12 | ); 13 | return color; 14 | }`; 15 | 16 | export default softLight; 17 | -------------------------------------------------------------------------------- /shaders/vignette.js: -------------------------------------------------------------------------------- 1 | const vignette = ` 2 | float vignette(vec2 uv, float boost, float reduction) { 3 | vec2 position = vUv - .5; 4 | return boost - length(position) * reduction; 5 | } 6 | `; 7 | 8 | export default vignette; 9 | -------------------------------------------------------------------------------- /test.js: -------------------------------------------------------------------------------- 1 | import { Paper } from './modules/paper15.js'; 2 | 3 | const paper = new Paper(512, 512); 4 | document.body.appendChild(paper.colorCanvas); 5 | document.body.appendChild(paper.roughnessCanvas); -------------------------------------------------------------------------------- /third_party/THREE.OrbitControls.js: -------------------------------------------------------------------------------- 1 | import * as THREE from './three.module.js'; 2 | 3 | /** 4 | * @author qiao / https://github.com/qiao 5 | * @author mrdoob / http://mrdoob.com 6 | * @author alteredq / http://alteredqualia.com/ 7 | * @author WestLangley / http://github.com/WestLangley 8 | * @author erich666 / http://erichaines.com 9 | */ 10 | 11 | // This set of controls performs orbiting, dollying (zooming), and panning. 12 | // Unlike TrackballControls, it maintains the "up" direction object.up (+Y by default). 13 | // 14 | // Orbit - left mouse / touch: one-finger move 15 | // Zoom - middle mouse, or mousewheel / touch: two-finger spread or squish 16 | // Pan - right mouse, or left mouse + ctrl/metaKey, or arrow keys / touch: two-finger move 17 | 18 | const OrbitControls = function(object, domElement) { 19 | 20 | this.object = object; 21 | 22 | this.domElement = (domElement !== undefined) ? domElement : document; 23 | 24 | // Set to false to disable this control 25 | this.enabled = true; 26 | 27 | // "target" sets the location of focus, where the object orbits around 28 | this.target = new THREE.Vector3(); 29 | 30 | // How far you can dolly in and out ( PerspectiveCamera only ) 31 | this.minDistance = 0; 32 | this.maxDistance = Infinity; 33 | 34 | // How far you can zoom in and out ( OrthographicCamera only ) 35 | this.minZoom = 0; 36 | this.maxZoom = Infinity; 37 | 38 | // How far you can orbit vertically, upper and lower limits. 39 | // Range is 0 to Math.PI radians. 40 | this.minPolarAngle = 0; // radians 41 | this.maxPolarAngle = Math.PI; // radians 42 | 43 | // How far you can orbit horizontally, upper and lower limits. 44 | // If set, must be a sub-interval of the interval [ - Math.PI, Math.PI ]. 45 | this.minAzimuthAngle = -Infinity; // radians 46 | this.maxAzimuthAngle = Infinity; // radians 47 | 48 | // Set to true to enable damping (inertia) 49 | // If damping is enabled, you must call controls.update() in your animation loop 50 | this.enableDamping = false; 51 | this.dampingFactor = 0.25; 52 | 53 | // This option actually enables dollying in and out; left as "zoom" for backwards compatibility. 54 | // Set to false to disable zooming 55 | this.enableZoom = true; 56 | this.zoomSpeed = 1.0; 57 | 58 | // Set to false to disable rotating 59 | this.enableRotate = true; 60 | this.rotateSpeed = 1.0; 61 | 62 | // Set to false to disable panning 63 | this.enablePan = true; 64 | this.panSpeed = 1.0; 65 | this.screenSpacePanning = false; // if true, pan in screen-space 66 | this.keyPanSpeed = 7.0; // pixels moved per arrow key push 67 | 68 | // Set to true to automatically rotate around the target 69 | // If auto-rotate is enabled, you must call controls.update() in your animation loop 70 | this.autoRotate = false; 71 | this.autoRotateSpeed = 2.0; // 30 seconds per round when fps is 60 72 | 73 | // Set to false to disable use of the keys 74 | this.enableKeys = true; 75 | 76 | // The four arrow keys 77 | this.keys = { LEFT: 37, UP: 38, RIGHT: 39, BOTTOM: 40 }; 78 | 79 | // Mouse buttons 80 | this.mouseButtons = { LEFT: THREE.MOUSE.LEFT, MIDDLE: THREE.MOUSE.MIDDLE, RIGHT: THREE.MOUSE.RIGHT }; 81 | 82 | // for reset 83 | this.target0 = this.target.clone(); 84 | this.position0 = this.object.position.clone(); 85 | this.zoom0 = this.object.zoom; 86 | 87 | // 88 | // public methods 89 | // 90 | 91 | this.getPolarAngle = function() { 92 | 93 | return spherical.phi; 94 | 95 | }; 96 | 97 | this.getAzimuthalAngle = function() { 98 | 99 | return spherical.theta; 100 | 101 | }; 102 | 103 | this.saveState = function() { 104 | 105 | scope.target0.copy(scope.target); 106 | scope.position0.copy(scope.object.position); 107 | scope.zoom0 = scope.object.zoom; 108 | 109 | }; 110 | 111 | this.reset = function() { 112 | 113 | scope.target.copy(scope.target0); 114 | scope.object.position.copy(scope.position0); 115 | scope.object.zoom = scope.zoom0; 116 | 117 | scope.object.updateProjectionMatrix(); 118 | scope.dispatchEvent(changeEvent); 119 | 120 | scope.update(); 121 | 122 | state = STATE.NONE; 123 | 124 | }; 125 | 126 | // this method is exposed, but perhaps it would be better if we can make it private... 127 | this.update = function() { 128 | 129 | var offset = new THREE.Vector3(); 130 | 131 | // so camera.up is the orbit axis 132 | var quat = new THREE.Quaternion().setFromUnitVectors(object.up, new THREE.Vector3(0, 1, 0)); 133 | var quatInverse = quat.clone().inverse(); 134 | 135 | var lastPosition = new THREE.Vector3(); 136 | var lastQuaternion = new THREE.Quaternion(); 137 | 138 | return function update() { 139 | 140 | var position = scope.object.position; 141 | 142 | offset.copy(position).sub(scope.target); 143 | 144 | // rotate offset to "y-axis-is-up" space 145 | offset.applyQuaternion(quat); 146 | 147 | // angle from z-axis around y-axis 148 | spherical.setFromVector3(offset); 149 | 150 | if (scope.autoRotate && state === STATE.NONE) { 151 | 152 | rotateLeft(getAutoRotationAngle()); 153 | 154 | } 155 | 156 | spherical.theta += sphericalDelta.theta; 157 | spherical.phi += sphericalDelta.phi; 158 | 159 | // restrict theta to be between desired limits 160 | spherical.theta = Math.max(scope.minAzimuthAngle, Math.min(scope.maxAzimuthAngle, spherical.theta)); 161 | 162 | // restrict phi to be between desired limits 163 | spherical.phi = Math.max(scope.minPolarAngle, Math.min(scope.maxPolarAngle, spherical.phi)); 164 | 165 | spherical.makeSafe(); 166 | 167 | 168 | spherical.radius *= scale; 169 | 170 | // restrict radius to be between desired limits 171 | spherical.radius = Math.max(scope.minDistance, Math.min(scope.maxDistance, spherical.radius)); 172 | 173 | // move target to panned location 174 | scope.target.add(panOffset); 175 | 176 | offset.setFromSpherical(spherical); 177 | 178 | // rotate offset back to "camera-up-vector-is-up" space 179 | offset.applyQuaternion(quatInverse); 180 | 181 | position.copy(scope.target).add(offset); 182 | 183 | scope.object.lookAt(scope.target); 184 | 185 | if (scope.enableDamping === true) { 186 | 187 | sphericalDelta.theta *= (1 - scope.dampingFactor); 188 | sphericalDelta.phi *= (1 - scope.dampingFactor); 189 | 190 | panOffset.multiplyScalar(1 - scope.dampingFactor); 191 | 192 | } else { 193 | 194 | sphericalDelta.set(0, 0, 0); 195 | 196 | panOffset.set(0, 0, 0); 197 | 198 | } 199 | 200 | scale = 1; 201 | 202 | // update condition is: 203 | // min(camera displacement, camera rotation in radians)^2 > EPS 204 | // using small-angle approximation cos(x/2) = 1 - x^2 / 8 205 | 206 | if (zoomChanged || 207 | lastPosition.distanceToSquared(scope.object.position) > EPS || 208 | 8 * (1 - lastQuaternion.dot(scope.object.quaternion)) > EPS) { 209 | 210 | scope.dispatchEvent(changeEvent); 211 | 212 | lastPosition.copy(scope.object.position); 213 | lastQuaternion.copy(scope.object.quaternion); 214 | zoomChanged = false; 215 | 216 | return true; 217 | 218 | } 219 | 220 | return false; 221 | 222 | }; 223 | 224 | }(); 225 | 226 | this.dispose = function() { 227 | 228 | scope.domElement.removeEventListener('contextmenu', onContextMenu, false); 229 | scope.domElement.removeEventListener('mousedown', onMouseDown, false); 230 | scope.domElement.removeEventListener('wheel', onMouseWheel, false); 231 | 232 | scope.domElement.removeEventListener('touchstart', onTouchStart, false); 233 | scope.domElement.removeEventListener('touchend', onTouchEnd, false); 234 | scope.domElement.removeEventListener('touchmove', onTouchMove, false); 235 | 236 | document.removeEventListener('mousemove', onMouseMove, false); 237 | document.removeEventListener('mouseup', onMouseUp, false); 238 | 239 | window.removeEventListener('keydown', onKeyDown, false); 240 | 241 | //scope.dispatchEvent( { type: 'dispose' } ); // should this be added here? 242 | 243 | }; 244 | 245 | // 246 | // internals 247 | // 248 | 249 | var scope = this; 250 | 251 | var changeEvent = { type: 'change' }; 252 | var startEvent = { type: 'start' }; 253 | var endEvent = { type: 'end' }; 254 | 255 | var STATE = { NONE: -1, ROTATE: 0, DOLLY: 1, PAN: 2, TOUCH_ROTATE: 3, TOUCH_DOLLY_PAN: 4 }; 256 | 257 | var state = STATE.NONE; 258 | 259 | var EPS = 0.000001; 260 | 261 | // current position in spherical coordinates 262 | var spherical = new THREE.Spherical(); 263 | var sphericalDelta = new THREE.Spherical(); 264 | 265 | var scale = 1; 266 | var panOffset = new THREE.Vector3(); 267 | var zoomChanged = false; 268 | 269 | var rotateStart = new THREE.Vector2(); 270 | var rotateEnd = new THREE.Vector2(); 271 | var rotateDelta = new THREE.Vector2(); 272 | 273 | var panStart = new THREE.Vector2(); 274 | var panEnd = new THREE.Vector2(); 275 | var panDelta = new THREE.Vector2(); 276 | 277 | var dollyStart = new THREE.Vector2(); 278 | var dollyEnd = new THREE.Vector2(); 279 | var dollyDelta = new THREE.Vector2(); 280 | 281 | function getAutoRotationAngle() { 282 | 283 | return 2 * Math.PI / 60 / 60 * scope.autoRotateSpeed; 284 | 285 | } 286 | 287 | function getZoomScale() { 288 | 289 | return Math.pow(0.95, scope.zoomSpeed); 290 | 291 | } 292 | 293 | function rotateLeft(angle) { 294 | 295 | sphericalDelta.theta -= angle; 296 | 297 | } 298 | 299 | function rotateUp(angle) { 300 | 301 | sphericalDelta.phi -= angle; 302 | 303 | } 304 | 305 | var panLeft = function() { 306 | 307 | var v = new THREE.Vector3(); 308 | 309 | return function panLeft(distance, objectMatrix) { 310 | 311 | v.setFromMatrixColumn(objectMatrix, 0); // get X column of objectMatrix 312 | v.multiplyScalar(-distance); 313 | 314 | panOffset.add(v); 315 | 316 | }; 317 | 318 | }(); 319 | 320 | var panUp = function() { 321 | 322 | var v = new THREE.Vector3(); 323 | 324 | return function panUp(distance, objectMatrix) { 325 | 326 | if (scope.screenSpacePanning === true) { 327 | 328 | v.setFromMatrixColumn(objectMatrix, 1); 329 | 330 | } else { 331 | 332 | v.setFromMatrixColumn(objectMatrix, 0); 333 | v.crossVectors(scope.object.up, v); 334 | 335 | } 336 | 337 | v.multiplyScalar(distance); 338 | 339 | panOffset.add(v); 340 | 341 | }; 342 | 343 | }(); 344 | 345 | // deltaX and deltaY are in pixels; right and down are positive 346 | var pan = function() { 347 | 348 | var offset = new THREE.Vector3(); 349 | 350 | return function pan(deltaX, deltaY) { 351 | 352 | var element = scope.domElement === document ? scope.domElement.body : scope.domElement; 353 | 354 | if (scope.object.isPerspectiveCamera) { 355 | 356 | // perspective 357 | var position = scope.object.position; 358 | offset.copy(position).sub(scope.target); 359 | var targetDistance = offset.length(); 360 | 361 | // half of the fov is center to top of screen 362 | targetDistance *= Math.tan((scope.object.fov / 2) * Math.PI / 180.0); 363 | 364 | // we use only clientHeight here so aspect ratio does not distort speed 365 | panLeft(2 * deltaX * targetDistance / element.clientHeight, scope.object.matrix); 366 | panUp(2 * deltaY * targetDistance / element.clientHeight, scope.object.matrix); 367 | 368 | } else if (scope.object.isOrthographicCamera) { 369 | 370 | // orthographic 371 | panLeft(deltaX * (scope.object.right - scope.object.left) / scope.object.zoom / element.clientWidth, scope.object.matrix); 372 | panUp(deltaY * (scope.object.top - scope.object.bottom) / scope.object.zoom / element.clientHeight, scope.object.matrix); 373 | 374 | } else { 375 | 376 | // camera neither orthographic nor perspective 377 | console.warn('WARNING: OrbitControls.js encountered an unknown camera type - pan disabled.'); 378 | scope.enablePan = false; 379 | 380 | } 381 | 382 | }; 383 | 384 | }(); 385 | 386 | function dollyIn(dollyScale) { 387 | 388 | if (scope.object.isPerspectiveCamera) { 389 | 390 | scale /= dollyScale; 391 | 392 | } else if (scope.object.isOrthographicCamera) { 393 | 394 | scope.object.zoom = Math.max(scope.minZoom, Math.min(scope.maxZoom, scope.object.zoom * dollyScale)); 395 | scope.object.updateProjectionMatrix(); 396 | zoomChanged = true; 397 | 398 | } else { 399 | 400 | console.warn('WARNING: OrbitControls.js encountered an unknown camera type - dolly/zoom disabled.'); 401 | scope.enableZoom = false; 402 | 403 | } 404 | 405 | } 406 | 407 | function dollyOut(dollyScale) { 408 | 409 | if (scope.object.isPerspectiveCamera) { 410 | 411 | scale *= dollyScale; 412 | 413 | } else if (scope.object.isOrthographicCamera) { 414 | 415 | scope.object.zoom = Math.max(scope.minZoom, Math.min(scope.maxZoom, scope.object.zoom / dollyScale)); 416 | scope.object.updateProjectionMatrix(); 417 | zoomChanged = true; 418 | 419 | } else { 420 | 421 | console.warn('WARNING: OrbitControls.js encountered an unknown camera type - dolly/zoom disabled.'); 422 | scope.enableZoom = false; 423 | 424 | } 425 | 426 | } 427 | 428 | // 429 | // event callbacks - update the object state 430 | // 431 | 432 | function handleMouseDownRotate(event) { 433 | 434 | //console.log( 'handleMouseDownRotate' ); 435 | 436 | rotateStart.set(event.clientX, event.clientY); 437 | 438 | } 439 | 440 | function handleMouseDownDolly(event) { 441 | 442 | //console.log( 'handleMouseDownDolly' ); 443 | 444 | dollyStart.set(event.clientX, event.clientY); 445 | 446 | } 447 | 448 | function handleMouseDownPan(event) { 449 | 450 | //console.log( 'handleMouseDownPan' ); 451 | 452 | panStart.set(event.clientX, event.clientY); 453 | 454 | } 455 | 456 | function handleMouseMoveRotate(event) { 457 | 458 | //console.log( 'handleMouseMoveRotate' ); 459 | 460 | rotateEnd.set(event.clientX, event.clientY); 461 | 462 | rotateDelta.subVectors(rotateEnd, rotateStart).multiplyScalar(scope.rotateSpeed); 463 | 464 | var element = scope.domElement === document ? scope.domElement.body : scope.domElement; 465 | 466 | rotateLeft(2 * Math.PI * rotateDelta.x / element.clientHeight); // yes, height 467 | 468 | rotateUp(2 * Math.PI * rotateDelta.y / element.clientHeight); 469 | 470 | rotateStart.copy(rotateEnd); 471 | 472 | scope.update(); 473 | 474 | } 475 | 476 | function handleMouseMoveDolly(event) { 477 | 478 | //console.log( 'handleMouseMoveDolly' ); 479 | 480 | dollyEnd.set(event.clientX, event.clientY); 481 | 482 | dollyDelta.subVectors(dollyEnd, dollyStart); 483 | 484 | if (dollyDelta.y > 0) { 485 | 486 | dollyIn(getZoomScale()); 487 | 488 | } else if (dollyDelta.y < 0) { 489 | 490 | dollyOut(getZoomScale()); 491 | 492 | } 493 | 494 | dollyStart.copy(dollyEnd); 495 | 496 | scope.update(); 497 | 498 | } 499 | 500 | function handleMouseMovePan(event) { 501 | 502 | //console.log( 'handleMouseMovePan' ); 503 | 504 | panEnd.set(event.clientX, event.clientY); 505 | 506 | panDelta.subVectors(panEnd, panStart).multiplyScalar(scope.panSpeed); 507 | 508 | pan(panDelta.x, panDelta.y); 509 | 510 | panStart.copy(panEnd); 511 | 512 | scope.update(); 513 | 514 | } 515 | 516 | function handleMouseUp(event) { 517 | 518 | // console.log( 'handleMouseUp' ); 519 | 520 | } 521 | 522 | function handleMouseWheel(event) { 523 | 524 | // console.log( 'handleMouseWheel' ); 525 | 526 | if (event.deltaY < 0) { 527 | 528 | dollyOut(getZoomScale()); 529 | 530 | } else if (event.deltaY > 0) { 531 | 532 | dollyIn(getZoomScale()); 533 | 534 | } 535 | 536 | scope.update(); 537 | 538 | } 539 | 540 | function handleKeyDown(event) { 541 | 542 | //console.log( 'handleKeyDown' ); 543 | 544 | switch (event.keyCode) { 545 | 546 | case scope.keys.UP: 547 | pan(0, scope.keyPanSpeed); 548 | scope.update(); 549 | break; 550 | 551 | case scope.keys.BOTTOM: 552 | pan(0, -scope.keyPanSpeed); 553 | scope.update(); 554 | break; 555 | 556 | case scope.keys.LEFT: 557 | pan(scope.keyPanSpeed, 0); 558 | scope.update(); 559 | break; 560 | 561 | case scope.keys.RIGHT: 562 | pan(-scope.keyPanSpeed, 0); 563 | scope.update(); 564 | break; 565 | 566 | } 567 | 568 | } 569 | 570 | function handleTouchStartRotate(event) { 571 | 572 | //console.log( 'handleTouchStartRotate' ); 573 | 574 | rotateStart.set(event.touches[0].pageX, event.touches[0].pageY); 575 | 576 | } 577 | 578 | function handleTouchStartDollyPan(event) { 579 | 580 | //console.log( 'handleTouchStartDollyPan' ); 581 | 582 | if (scope.enableZoom) { 583 | 584 | var dx = event.touches[0].pageX - event.touches[1].pageX; 585 | var dy = event.touches[0].pageY - event.touches[1].pageY; 586 | 587 | var distance = Math.sqrt(dx * dx + dy * dy); 588 | 589 | dollyStart.set(0, distance); 590 | 591 | } 592 | 593 | if (scope.enablePan) { 594 | 595 | var x = 0.5 * (event.touches[0].pageX + event.touches[1].pageX); 596 | var y = 0.5 * (event.touches[0].pageY + event.touches[1].pageY); 597 | 598 | panStart.set(x, y); 599 | 600 | } 601 | 602 | } 603 | 604 | function handleTouchMoveRotate(event) { 605 | 606 | //console.log( 'handleTouchMoveRotate' ); 607 | 608 | rotateEnd.set(event.touches[0].pageX, event.touches[0].pageY); 609 | 610 | rotateDelta.subVectors(rotateEnd, rotateStart).multiplyScalar(scope.rotateSpeed); 611 | 612 | var element = scope.domElement === document ? scope.domElement.body : scope.domElement; 613 | 614 | rotateLeft(2 * Math.PI * rotateDelta.x / element.clientHeight); // yes, height 615 | 616 | rotateUp(2 * Math.PI * rotateDelta.y / element.clientHeight); 617 | 618 | rotateStart.copy(rotateEnd); 619 | 620 | scope.update(); 621 | 622 | } 623 | 624 | function handleTouchMoveDollyPan(event) { 625 | 626 | //console.log( 'handleTouchMoveDollyPan' ); 627 | 628 | if (scope.enableZoom) { 629 | 630 | var dx = event.touches[0].pageX - event.touches[1].pageX; 631 | var dy = event.touches[0].pageY - event.touches[1].pageY; 632 | 633 | var distance = Math.sqrt(dx * dx + dy * dy); 634 | 635 | dollyEnd.set(0, distance); 636 | 637 | dollyDelta.set(0, Math.pow(dollyEnd.y / dollyStart.y, scope.zoomSpeed)); 638 | 639 | dollyIn(dollyDelta.y); 640 | 641 | dollyStart.copy(dollyEnd); 642 | 643 | } 644 | 645 | if (scope.enablePan) { 646 | 647 | var x = 0.5 * (event.touches[0].pageX + event.touches[1].pageX); 648 | var y = 0.5 * (event.touches[0].pageY + event.touches[1].pageY); 649 | 650 | panEnd.set(x, y); 651 | 652 | panDelta.subVectors(panEnd, panStart).multiplyScalar(scope.panSpeed); 653 | 654 | pan(panDelta.x, panDelta.y); 655 | 656 | panStart.copy(panEnd); 657 | 658 | } 659 | 660 | scope.update(); 661 | 662 | } 663 | 664 | function handleTouchEnd(event) { 665 | 666 | //console.log( 'handleTouchEnd' ); 667 | 668 | } 669 | 670 | // 671 | // event handlers - FSM: listen for events and reset state 672 | // 673 | 674 | function onMouseDown(event) { 675 | 676 | if (scope.enabled === false) return; 677 | 678 | event.preventDefault(); 679 | 680 | switch (event.button) { 681 | 682 | case scope.mouseButtons.LEFT: 683 | 684 | if (event.ctrlKey || event.metaKey) { 685 | 686 | if (scope.enablePan === false) return; 687 | 688 | handleMouseDownPan(event); 689 | 690 | state = STATE.PAN; 691 | 692 | } else { 693 | 694 | if (scope.enableRotate === false) return; 695 | 696 | handleMouseDownRotate(event); 697 | 698 | state = STATE.ROTATE; 699 | 700 | } 701 | 702 | break; 703 | 704 | case scope.mouseButtons.MIDDLE: 705 | 706 | if (scope.enableZoom === false) return; 707 | 708 | handleMouseDownDolly(event); 709 | 710 | state = STATE.DOLLY; 711 | 712 | break; 713 | 714 | case scope.mouseButtons.RIGHT: 715 | 716 | if (scope.enablePan === false) return; 717 | 718 | handleMouseDownPan(event); 719 | 720 | state = STATE.PAN; 721 | 722 | break; 723 | 724 | } 725 | 726 | if (state !== STATE.NONE) { 727 | 728 | document.addEventListener('mousemove', onMouseMove, false); 729 | document.addEventListener('mouseup', onMouseUp, false); 730 | 731 | scope.dispatchEvent(startEvent); 732 | 733 | } 734 | 735 | } 736 | 737 | function onMouseMove(event) { 738 | 739 | if (scope.enabled === false) return; 740 | 741 | event.preventDefault(); 742 | 743 | switch (state) { 744 | 745 | case STATE.ROTATE: 746 | 747 | if (scope.enableRotate === false) return; 748 | 749 | handleMouseMoveRotate(event); 750 | 751 | break; 752 | 753 | case STATE.DOLLY: 754 | 755 | if (scope.enableZoom === false) return; 756 | 757 | handleMouseMoveDolly(event); 758 | 759 | break; 760 | 761 | case STATE.PAN: 762 | 763 | if (scope.enablePan === false) return; 764 | 765 | handleMouseMovePan(event); 766 | 767 | break; 768 | 769 | } 770 | 771 | } 772 | 773 | function onMouseUp(event) { 774 | 775 | if (scope.enabled === false) return; 776 | 777 | handleMouseUp(event); 778 | 779 | document.removeEventListener('mousemove', onMouseMove, false); 780 | document.removeEventListener('mouseup', onMouseUp, false); 781 | 782 | scope.dispatchEvent(endEvent); 783 | 784 | state = STATE.NONE; 785 | 786 | } 787 | 788 | function onMouseWheel(event) { 789 | 790 | if (scope.enabled === false || scope.enableZoom === false || (state !== STATE.NONE && state !== STATE.ROTATE)) return; 791 | 792 | event.preventDefault(); 793 | event.stopPropagation(); 794 | 795 | scope.dispatchEvent(startEvent); 796 | 797 | handleMouseWheel(event); 798 | 799 | scope.dispatchEvent(endEvent); 800 | 801 | } 802 | 803 | function onKeyDown(event) { 804 | 805 | if (scope.enabled === false || scope.enableKeys === false || scope.enablePan === false) return; 806 | 807 | handleKeyDown(event); 808 | 809 | } 810 | 811 | function onTouchStart(event) { 812 | 813 | if (scope.enabled === false) return; 814 | 815 | event.preventDefault(); 816 | 817 | switch (event.touches.length) { 818 | 819 | case 1: // one-fingered touch: rotate 820 | 821 | if (scope.enableRotate === false) return; 822 | 823 | handleTouchStartRotate(event); 824 | 825 | state = STATE.TOUCH_ROTATE; 826 | 827 | break; 828 | 829 | case 2: // two-fingered touch: dolly-pan 830 | 831 | if (scope.enableZoom === false && scope.enablePan === false) return; 832 | 833 | handleTouchStartDollyPan(event); 834 | 835 | state = STATE.TOUCH_DOLLY_PAN; 836 | 837 | break; 838 | 839 | default: 840 | 841 | state = STATE.NONE; 842 | 843 | } 844 | 845 | if (state !== STATE.NONE) { 846 | 847 | scope.dispatchEvent(startEvent); 848 | 849 | } 850 | 851 | } 852 | 853 | function onTouchMove(event) { 854 | 855 | if (scope.enabled === false) return; 856 | 857 | event.preventDefault(); 858 | event.stopPropagation(); 859 | 860 | switch (event.touches.length) { 861 | 862 | case 1: // one-fingered touch: rotate 863 | 864 | if (scope.enableRotate === false) return; 865 | if (state !== STATE.TOUCH_ROTATE) return; // is this needed? 866 | 867 | handleTouchMoveRotate(event); 868 | 869 | break; 870 | 871 | case 2: // two-fingered touch: dolly-pan 872 | 873 | if (scope.enableZoom === false && scope.enablePan === false) return; 874 | if (state !== STATE.TOUCH_DOLLY_PAN) return; // is this needed? 875 | 876 | handleTouchMoveDollyPan(event); 877 | 878 | break; 879 | 880 | default: 881 | 882 | state = STATE.NONE; 883 | 884 | } 885 | 886 | } 887 | 888 | function onTouchEnd(event) { 889 | 890 | if (scope.enabled === false) return; 891 | 892 | handleTouchEnd(event); 893 | 894 | scope.dispatchEvent(endEvent); 895 | 896 | state = STATE.NONE; 897 | 898 | } 899 | 900 | function onContextMenu(event) { 901 | 902 | if (scope.enabled === false) return; 903 | 904 | event.preventDefault(); 905 | 906 | } 907 | 908 | // 909 | 910 | scope.domElement.addEventListener('contextmenu', onContextMenu, false); 911 | 912 | scope.domElement.addEventListener('mousedown', onMouseDown, false); 913 | scope.domElement.addEventListener('wheel', onMouseWheel, false); 914 | 915 | scope.domElement.addEventListener('touchstart', onTouchStart, false); 916 | scope.domElement.addEventListener('touchend', onTouchEnd, false); 917 | scope.domElement.addEventListener('touchmove', onTouchMove, false); 918 | 919 | window.addEventListener('keydown', onKeyDown, false); 920 | 921 | // force an update at start 922 | 923 | this.update(); 924 | 925 | }; 926 | 927 | OrbitControls.prototype = Object.create(THREE.EventDispatcher.prototype); 928 | OrbitControls.prototype.constructor = OrbitControls; 929 | 930 | Object.defineProperties(OrbitControls.prototype, { 931 | 932 | center: { 933 | 934 | get: function() { 935 | 936 | console.warn('OrbitControls: .center has been renamed to .target'); 937 | return this.target; 938 | 939 | } 940 | 941 | }, 942 | 943 | // backward compatibility 944 | 945 | noZoom: { 946 | 947 | get: function() { 948 | 949 | console.warn('OrbitControls: .noZoom has been deprecated. Use .enableZoom instead.'); 950 | return !this.enableZoom; 951 | 952 | }, 953 | 954 | set: function(value) { 955 | 956 | console.warn('OrbitControls: .noZoom has been deprecated. Use .enableZoom instead.'); 957 | this.enableZoom = !value; 958 | 959 | } 960 | 961 | }, 962 | 963 | noRotate: { 964 | 965 | get: function() { 966 | 967 | console.warn('OrbitControls: .noRotate has been deprecated. Use .enableRotate instead.'); 968 | return !this.enableRotate; 969 | 970 | }, 971 | 972 | set: function(value) { 973 | 974 | console.warn('OrbitControls: .noRotate has been deprecated. Use .enableRotate instead.'); 975 | this.enableRotate = !value; 976 | 977 | } 978 | 979 | }, 980 | 981 | noPan: { 982 | 983 | get: function() { 984 | 985 | console.warn('OrbitControls: .noPan has been deprecated. Use .enablePan instead.'); 986 | return !this.enablePan; 987 | 988 | }, 989 | 990 | set: function(value) { 991 | 992 | console.warn('OrbitControls: .noPan has been deprecated. Use .enablePan instead.'); 993 | this.enablePan = !value; 994 | 995 | } 996 | 997 | }, 998 | 999 | noKeys: { 1000 | 1001 | get: function() { 1002 | 1003 | console.warn('OrbitControls: .noKeys has been deprecated. Use .enableKeys instead.'); 1004 | return !this.enableKeys; 1005 | 1006 | }, 1007 | 1008 | set: function(value) { 1009 | 1010 | console.warn('OrbitControls: .noKeys has been deprecated. Use .enableKeys instead.'); 1011 | this.enableKeys = !value; 1012 | 1013 | } 1014 | 1015 | }, 1016 | 1017 | staticMoving: { 1018 | 1019 | get: function() { 1020 | 1021 | console.warn('OrbitControls: .staticMoving has been deprecated. Use .enableDamping instead.'); 1022 | return !this.enableDamping; 1023 | 1024 | }, 1025 | 1026 | set: function(value) { 1027 | 1028 | console.warn('OrbitControls: .staticMoving has been deprecated. Use .enableDamping instead.'); 1029 | this.enableDamping = !value; 1030 | 1031 | } 1032 | 1033 | }, 1034 | 1035 | dynamicDampingFactor: { 1036 | 1037 | get: function() { 1038 | 1039 | console.warn('OrbitControls: .dynamicDampingFactor has been renamed. Use .dampingFactor instead.'); 1040 | return this.dampingFactor; 1041 | 1042 | }, 1043 | 1044 | set: function(value) { 1045 | 1046 | console.warn('OrbitControls: .dynamicDampingFactor has been renamed. Use .dampingFactor instead.'); 1047 | this.dampingFactor = value; 1048 | 1049 | } 1050 | 1051 | } 1052 | 1053 | }); 1054 | 1055 | export default OrbitControls; -------------------------------------------------------------------------------- /third_party/UpdatableTexture.js: -------------------------------------------------------------------------------- 1 | import { 2 | Texture, 3 | LinearFilter, 4 | LinearMipMapLinearFilter, 5 | WebGLUtils 6 | } from "./three.module.js"; 7 | 8 | function UpdatableTexture( 9 | format, 10 | type, 11 | mapping, 12 | wrapS, 13 | wrapT, 14 | magFilter, 15 | minFilter, 16 | anisotropy, 17 | encoding 18 | ) { 19 | Texture.call( 20 | this, 21 | null, 22 | mapping, 23 | wrapS, 24 | wrapT, 25 | magFilter, 26 | minFilter, 27 | format, 28 | type, 29 | anisotropy, 30 | encoding 31 | ); 32 | 33 | var canvas = document.createElement("canvas"); 34 | canvas.width = 1; 35 | canvas.height = 1; 36 | var ctx = canvas.getContext("2d"); 37 | var imageData = ctx.createImageData(1, 1); 38 | 39 | this.image = imageData; 40 | 41 | this.magFilter = magFilter !== undefined ? magFilter : LinearFilter; 42 | this.minFilter = 43 | minFilter !== undefined ? minFilter : LinearMipMapLinearFilter; 44 | 45 | this.generateMipmaps = true; 46 | this.flipY = true; 47 | this.unpackAlignment = 1; 48 | this.needsUpdate = true; 49 | } 50 | 51 | UpdatableTexture.prototype = Object.create(Texture.prototype); 52 | UpdatableTexture.prototype.constructor = UpdatableTexture; 53 | 54 | UpdatableTexture.prototype.isUpdatableTexture = true; 55 | 56 | UpdatableTexture.prototype.setRenderer = function(renderer) { 57 | this.renderer = renderer; 58 | this.gl = this.renderer.getContext(); 59 | this.utils = WebGLUtils( 60 | this.gl, 61 | this.renderer.extensions, 62 | this.renderer.capabilities 63 | ); 64 | }; 65 | 66 | UpdatableTexture.prototype.setSize = function(width, height) { 67 | if (width === this.width && height === this.height) return; 68 | 69 | var textureProperties = this.renderer.properties.get(this); 70 | if (!textureProperties.__webglTexture) return; 71 | 72 | this.width = width; 73 | this.height = height; 74 | 75 | var activeTexture = this.gl.getParameter(this.gl.TEXTURE_BINDING_2D); 76 | this.gl.bindTexture(this.gl.TEXTURE_2D, textureProperties.__webglTexture); 77 | if (!textureProperties.__webglTexture) this.width = null; 78 | this.gl.texImage2D( 79 | this.gl.TEXTURE_2D, 80 | 0, 81 | this.utils.convert(this.format), 82 | width, 83 | height, 84 | 0, 85 | this.utils.convert(this.format), 86 | this.utils.convert(this.type), 87 | null 88 | ); 89 | this.gl.bindTexture(this.gl.TEXTURE_2D, activeTexture); 90 | }; 91 | 92 | UpdatableTexture.prototype.update = function(src, x, y) { 93 | var textureProperties = this.renderer.properties.get(this); 94 | if (!textureProperties.__webglTexture) return; 95 | 96 | var activeTexture = this.gl.getParameter(this.gl.TEXTURE_BINDING_2D); 97 | this.gl.bindTexture(this.gl.TEXTURE_2D, textureProperties.__webglTexture); 98 | this.gl.texSubImage2D( 99 | this.gl.TEXTURE_2D, 100 | 0, 101 | x, 102 | this.height - y - src.height, 103 | this.utils.convert(this.format), 104 | this.utils.convert(this.type), 105 | src 106 | ); 107 | this.gl.generateMipmap(this.gl.TEXTURE_2D); 108 | this.gl.bindTexture(this.gl.TEXTURE_2D, activeTexture); 109 | }; 110 | 111 | export { UpdatableTexture }; 112 | -------------------------------------------------------------------------------- /third_party/WebXR.js: -------------------------------------------------------------------------------- 1 | function detectWebXR() { 2 | if ("xr" in navigator) { 3 | return new Promise((resolve, reject) => { 4 | navigator.xr 5 | .isSessionSupported("immersive-vr") 6 | .then(supported => { 7 | resolve(); 8 | }) 9 | .catch(() => reject()); 10 | }); 11 | } 12 | 13 | throw new Error("No WebXR support."); 14 | } 15 | 16 | function startWebXR(device, renderer, options) { 17 | if (options && options.frameOfReferenceType) { 18 | renderer.vr.setFrameOfReferenceType(options.frameOfReferenceType); 19 | } 20 | 21 | let currentSession = null; 22 | 23 | function onSessionStarted(session) { 24 | session.addEventListener("end", onSessionEnded); 25 | renderer.vr.setSession(session); 26 | currentSession = session; 27 | } 28 | 29 | function onSessionEnded(event) { 30 | currentSession.removeEventListener("end", onSessionEnded); 31 | renderer.vr.setSession(null); 32 | currentSession = null; 33 | } 34 | 35 | var sessionInit = { optionalFeatures: ["local-floor", "bounded-floor"] }; 36 | navigator.xr 37 | .requestSession("immersive-vr", sessionInit) 38 | .then(onSessionStarted); 39 | 40 | //renderer.vr.setDevice(device); 41 | } 42 | 43 | export { detectWebXR, startWebXR }; 44 | -------------------------------------------------------------------------------- /third_party/equirectangular-to-cubemap.js: -------------------------------------------------------------------------------- 1 | import { 2 | Scene, 3 | CubeCamera, 4 | MeshBasicMaterial, 5 | Mesh, 6 | IcosahedronGeometry, 7 | BackSide 8 | } from "./three.module.js"; 9 | 10 | class EquirectangularToCubemap { 11 | constructor(renderer) { 12 | this.renderer = renderer; 13 | this.scene = new Scene(); 14 | 15 | var gl = this.renderer.getContext(); 16 | this.maxSize = gl.getParameter(gl.MAX_CUBE_MAP_TEXTURE_SIZE); 17 | 18 | this.camera = new CubeCamera(1, 100000, 1); 19 | 20 | this.material = new MeshBasicMaterial({ 21 | map: null, 22 | side: BackSide 23 | }); 24 | 25 | this.mesh = new Mesh(new IcosahedronGeometry(100, 4), this.material); 26 | this.scene.add(this.mesh); 27 | } 28 | 29 | convert(source, size) { 30 | var mapSize = Math.min(size, this.maxSize); 31 | this.camera = new CubeCamera(1, 100000, mapSize); 32 | this.material.map = source; 33 | 34 | this.camera.update(this.renderer, this.scene); 35 | 36 | return this.camera.renderTarget.texture; 37 | } 38 | } 39 | 40 | export { EquirectangularToCubemap }; 41 | -------------------------------------------------------------------------------- /twitter.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spite/infinite-gift/eba6257d277a94d4c3d7ac702547394f09da78a5/twitter.jpg --------------------------------------------------------------------------------