├── .gitignore ├── LICENSE ├── README.md ├── babel.config.js ├── package.json ├── public ├── _redirects ├── favicon.ico ├── img │ ├── featured_projects │ │ ├── LAS Site.jpg │ │ ├── Quantum Summer Site.jpg │ │ └── WCMA Site.jpg │ └── icons │ │ ├── android-chrome-192x192.png │ │ ├── android-chrome-256x256.png │ │ ├── apple-touch-icon.png │ │ ├── browserconfig.xml │ │ ├── discord.png │ │ ├── favicon-16x16.png │ │ ├── favicon-32x32.png │ │ ├── favicon.ico │ │ ├── favicon.png │ │ ├── github.png │ │ ├── glitch.png │ │ ├── hicetnunc.png │ │ ├── msdf-left-align.png │ │ ├── mstile-150x150.png │ │ ├── p5.png │ │ ├── safari-pinned-tab.svg │ │ ├── sp-hero.png │ │ ├── sp_logo.png │ │ ├── sphere.jpg │ │ ├── threejs.png │ │ ├── touchdesigner.png │ │ └── webclip.png ├── index.html └── robots.txt ├── src ├── App.vue ├── client │ ├── codemirror │ │ ├── codemirror.css │ │ ├── glslEditor.css │ │ └── glslEditor.js │ ├── fonts │ │ ├── RegolaPro-Bold.woff2 │ │ └── RegolaPro-Book.woff2 │ ├── images │ │ ├── Heart.svg │ │ ├── Heart_filled.svg │ │ ├── close-white.svg │ │ ├── close.svg │ │ ├── favicon.png │ │ ├── glitch_logo.svg │ │ ├── msdf-left-align.png │ │ ├── msdf3.png │ │ ├── play.svg │ │ ├── share.svg │ │ ├── sp_logo.png │ │ ├── touchdesigner_logo.jpg │ │ └── webclip.png │ ├── mixins.less │ ├── normalize.css │ ├── style.css │ └── webflow.css ├── components │ ├── About.vue │ ├── ActionBar.vue │ ├── CardModal.vue │ ├── Editor.vue │ ├── Error404.vue │ ├── Examples.vue │ ├── Explore.vue │ ├── Header.vue │ ├── Home.vue │ ├── Home3D.vue │ ├── MainContainer.vue │ ├── New.vue │ ├── Profile.vue │ ├── Room.vue │ ├── Sculpture.vue │ ├── SculptureFeed.vue │ ├── ShareModal.vue │ ├── SignIn.vue │ ├── SignUp.vue │ ├── SpThreeVue.vue │ └── UniformGUI.vue ├── dbConfig.js ├── helpers │ ├── front-page-sculp1.js │ └── handelUnsavedChanges.js ├── main.js ├── registerServiceWorker.js ├── router │ └── routes.js ├── schema │ └── User.js ├── store │ └── store.js ├── sublime.js └── threejs-sculpture │ ├── SculptureN.js │ └── create-pedestal-edges.js ├── vue.config.js └── yarn.lock /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /dist 4 | 5 | # local env files 6 | .env.local 7 | .env.*.local 8 | 9 | # Log files 10 | npm-debug.log* 11 | yarn-debug.log* 12 | yarn-error.log* 13 | 14 | # Editor directories and files 15 | .idea 16 | .vscode 17 | *.suo 18 | *.ntvs* 19 | *.njsproj 20 | *.sln 21 | *.sw? 22 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 shader-park 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Shader Park 2 | 3 | [![Netlify Status](https://api.netlify.com/api/v1/badges/62b5ba66-28fd-4829-9394-b175ad113a6b/deploy-status)](https://app.netlify.com/sites/shaderpark/deploys) 4 | 5 | ## Project setup 6 | ``` 7 | yarn install 8 | ``` 9 | 10 | ### Compiles and hot-reloads for development 11 | ``` 12 | yarn serve 13 | ``` 14 | 15 | ### Compiles and minifies for production 16 | ``` 17 | yarn build 18 | ``` 19 | 20 | ### Customize configuration 21 | See [Configuration Reference](https://cli.vuejs.org/config/). 22 | 23 | ## Project Deploy 24 | ``` 25 | git checkout staging 26 | git merge master 27 | git push 28 | ``` 29 | -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [ 3 | '@vue/cli-plugin-babel/preset' 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Shader-Park-Website", 3 | "version": "0.1.0", 4 | "private": true, 5 | "author": "Peter Whidden, Torin Blankensmith", 6 | "scripts": { 7 | "serve": "vue-cli-service serve", 8 | "build": "vue-cli-service build" 9 | }, 10 | "dependencies": { 11 | "@tweenjs/tween.js": "^18.5.0", 12 | "animejs": "^3.1.0", 13 | "core-js": "^3.6.4", 14 | "dat.gui": "^0.7.6", 15 | "firebase": "^9.6.8", 16 | "intersection-observer": "^0.7.0", 17 | "lodash.throttle": "^4.1.1", 18 | "register-service-worker": "^1.6.2", 19 | "shader-park-core": "^0.2.5", 20 | "three": "^0.155.0", 21 | "v-lazy-image": "^1.3.2", 22 | "vue": "^2.6.11", 23 | "vue-codemirror": "^4.0.6", 24 | "vue-github-button": "^1.2.0", 25 | "vue-js-modal": "^1.3.33", 26 | "vue-meta": "^2.3.2", 27 | "vue-router": "^3.1.5", 28 | "vuelidate": "^0.7.5", 29 | "vuex": "^3.1.2" 30 | }, 31 | "devDependencies": { 32 | "@babel/core": "^7.17.5", 33 | "@babel/preset-env": "^7.16.11", 34 | "@vue/cli-plugin-babel": "~5.0.1", 35 | "@vue/cli-plugin-pwa": "~5.0.1", 36 | "@vue/cli-plugin-router": "~5.0.1", 37 | "@vue/cli-plugin-vuex": "~5.0.1", 38 | "@vue/cli-service": "~5.0.1", 39 | "less": "^4.1.2", 40 | "less-loader": "^10.2.0", 41 | "vue-template-compiler": "^2.6.11" 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /public/_redirects: -------------------------------------------------------------------------------- 1 | # Netlify settings for single-page application 2 | /* /index.html 200 -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shader-park/shader-park-website/e80177941cc96605aa86c7ca3de145354514c752/public/favicon.ico -------------------------------------------------------------------------------- /public/img/featured_projects/LAS Site.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shader-park/shader-park-website/e80177941cc96605aa86c7ca3de145354514c752/public/img/featured_projects/LAS Site.jpg -------------------------------------------------------------------------------- /public/img/featured_projects/Quantum Summer Site.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shader-park/shader-park-website/e80177941cc96605aa86c7ca3de145354514c752/public/img/featured_projects/Quantum Summer Site.jpg -------------------------------------------------------------------------------- /public/img/featured_projects/WCMA Site.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shader-park/shader-park-website/e80177941cc96605aa86c7ca3de145354514c752/public/img/featured_projects/WCMA Site.jpg -------------------------------------------------------------------------------- /public/img/icons/android-chrome-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shader-park/shader-park-website/e80177941cc96605aa86c7ca3de145354514c752/public/img/icons/android-chrome-192x192.png -------------------------------------------------------------------------------- /public/img/icons/android-chrome-256x256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shader-park/shader-park-website/e80177941cc96605aa86c7ca3de145354514c752/public/img/icons/android-chrome-256x256.png -------------------------------------------------------------------------------- /public/img/icons/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shader-park/shader-park-website/e80177941cc96605aa86c7ca3de145354514c752/public/img/icons/apple-touch-icon.png -------------------------------------------------------------------------------- /public/img/icons/browserconfig.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | #2b5797 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /public/img/icons/discord.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shader-park/shader-park-website/e80177941cc96605aa86c7ca3de145354514c752/public/img/icons/discord.png -------------------------------------------------------------------------------- /public/img/icons/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shader-park/shader-park-website/e80177941cc96605aa86c7ca3de145354514c752/public/img/icons/favicon-16x16.png -------------------------------------------------------------------------------- /public/img/icons/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shader-park/shader-park-website/e80177941cc96605aa86c7ca3de145354514c752/public/img/icons/favicon-32x32.png -------------------------------------------------------------------------------- /public/img/icons/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shader-park/shader-park-website/e80177941cc96605aa86c7ca3de145354514c752/public/img/icons/favicon.ico -------------------------------------------------------------------------------- /public/img/icons/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shader-park/shader-park-website/e80177941cc96605aa86c7ca3de145354514c752/public/img/icons/favicon.png -------------------------------------------------------------------------------- /public/img/icons/github.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shader-park/shader-park-website/e80177941cc96605aa86c7ca3de145354514c752/public/img/icons/github.png -------------------------------------------------------------------------------- /public/img/icons/glitch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shader-park/shader-park-website/e80177941cc96605aa86c7ca3de145354514c752/public/img/icons/glitch.png -------------------------------------------------------------------------------- /public/img/icons/hicetnunc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shader-park/shader-park-website/e80177941cc96605aa86c7ca3de145354514c752/public/img/icons/hicetnunc.png -------------------------------------------------------------------------------- /public/img/icons/msdf-left-align.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shader-park/shader-park-website/e80177941cc96605aa86c7ca3de145354514c752/public/img/icons/msdf-left-align.png -------------------------------------------------------------------------------- /public/img/icons/mstile-150x150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shader-park/shader-park-website/e80177941cc96605aa86c7ca3de145354514c752/public/img/icons/mstile-150x150.png -------------------------------------------------------------------------------- /public/img/icons/p5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shader-park/shader-park-website/e80177941cc96605aa86c7ca3de145354514c752/public/img/icons/p5.png -------------------------------------------------------------------------------- /public/img/icons/safari-pinned-tab.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 7 | 8 | Created by potrace 1.11, written by Peter Selinger 2001-2013 9 | 10 | 12 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /public/img/icons/sp-hero.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shader-park/shader-park-website/e80177941cc96605aa86c7ca3de145354514c752/public/img/icons/sp-hero.png -------------------------------------------------------------------------------- /public/img/icons/sp_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shader-park/shader-park-website/e80177941cc96605aa86c7ca3de145354514c752/public/img/icons/sp_logo.png -------------------------------------------------------------------------------- /public/img/icons/sphere.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shader-park/shader-park-website/e80177941cc96605aa86c7ca3de145354514c752/public/img/icons/sphere.jpg -------------------------------------------------------------------------------- /public/img/icons/threejs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shader-park/shader-park-website/e80177941cc96605aa86c7ca3de145354514c752/public/img/icons/threejs.png -------------------------------------------------------------------------------- /public/img/icons/touchdesigner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shader-park/shader-park-website/e80177941cc96605aa86c7ca3de145354514c752/public/img/icons/touchdesigner.png -------------------------------------------------------------------------------- /public/img/icons/webclip.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shader-park/shader-park-website/e80177941cc96605aa86c7ca3de145354514c752/public/img/icons/webclip.png -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Shader Park 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 35 | 36 | 37 | Shader Park 38 | 39 | 40 | 43 |
44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | User-agent: * 2 | Disallow: 3 | -------------------------------------------------------------------------------- /src/App.vue: -------------------------------------------------------------------------------- 1 | 21 | 22 | 133 | 134 | 178 | 179 | 352 | 353 | 354 | -------------------------------------------------------------------------------- /src/client/codemirror/codemirror.css: -------------------------------------------------------------------------------- 1 | /* BASICS */ 2 | 3 | .CodeMirror { 4 | /* Set height, width, borders, and global font properties here */ 5 | font-family: monospace; 6 | height: 300px; 7 | color: black; 8 | direction: ltr; 9 | } 10 | 11 | /* PADDING */ 12 | 13 | .CodeMirror-lines { 14 | padding: 4px 0; /* Vertical padding around content */ 15 | } 16 | .CodeMirror pre.CodeMirror-line, 17 | .CodeMirror pre.CodeMirror-line-like { 18 | padding: 0 4px; /* Horizontal padding of content */ 19 | } 20 | 21 | .CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler { 22 | background-color: white; /* The little square between H and V scrollbars */ 23 | } 24 | 25 | /* GUTTER */ 26 | 27 | .CodeMirror-gutters { 28 | border-right: 1px solid #ddd; 29 | background-color: #f7f7f7; 30 | white-space: nowrap; 31 | } 32 | .CodeMirror-linenumbers {} 33 | .CodeMirror-linenumber { 34 | padding: 0 3px 0 5px; 35 | min-width: 20px; 36 | text-align: right; 37 | color: #999; 38 | white-space: nowrap; 39 | } 40 | 41 | .CodeMirror-guttermarker { color: black; } 42 | .CodeMirror-guttermarker-subtle { color: #999; } 43 | 44 | /* CURSOR */ 45 | 46 | .CodeMirror-cursor { 47 | border-left: 1px solid black; 48 | border-right: none; 49 | width: 0; 50 | } 51 | /* Shown when moving in bi-directional text */ 52 | .CodeMirror div.CodeMirror-secondarycursor { 53 | border-left: 1px solid silver; 54 | } 55 | .cm-fat-cursor .CodeMirror-cursor { 56 | width: auto; 57 | border: 0 !important; 58 | background: #7e7; 59 | } 60 | .cm-fat-cursor div.CodeMirror-cursors { 61 | z-index: 1; 62 | } 63 | .cm-fat-cursor-mark { 64 | background-color: rgba(20, 255, 20, 0.5); 65 | -webkit-animation: blink 1.06s steps(1) infinite; 66 | -moz-animation: blink 1.06s steps(1) infinite; 67 | animation: blink 1.06s steps(1) infinite; 68 | } 69 | .cm-animate-fat-cursor { 70 | width: auto; 71 | border: 0; 72 | -webkit-animation: blink 1.06s steps(1) infinite; 73 | -moz-animation: blink 1.06s steps(1) infinite; 74 | animation: blink 1.06s steps(1) infinite; 75 | background-color: #7e7; 76 | } 77 | @-moz-keyframes blink { 78 | 0% {} 79 | 50% { background-color: transparent; } 80 | 100% {} 81 | } 82 | @-webkit-keyframes blink { 83 | 0% {} 84 | 50% { background-color: transparent; } 85 | 100% {} 86 | } 87 | @keyframes blink { 88 | 0% {} 89 | 50% { background-color: transparent; } 90 | 100% {} 91 | } 92 | 93 | /* Can style cursor different in overwrite (non-insert) mode */ 94 | .CodeMirror-overwrite .CodeMirror-cursor {} 95 | 96 | .cm-tab { display: inline-block; text-decoration: inherit; } 97 | 98 | .CodeMirror-rulers { 99 | position: absolute; 100 | left: 0; right: 0; top: -50px; bottom: 0; 101 | overflow: hidden; 102 | } 103 | .CodeMirror-ruler { 104 | border-left: 1px solid #ccc; 105 | top: 0; bottom: 0; 106 | position: absolute; 107 | } 108 | 109 | /* DEFAULT THEME */ 110 | 111 | .cm-s-default .cm-header {color: blue;} 112 | .cm-s-default .cm-quote {color: #090;} 113 | .cm-negative {color: #d44;} 114 | .cm-positive {color: #292;} 115 | .cm-header, .cm-strong {font-weight: bold;} 116 | .cm-em {font-style: italic;} 117 | .cm-link {text-decoration: underline;} 118 | .cm-strikethrough {text-decoration: line-through;} 119 | 120 | .cm-s-default .cm-keyword {color: #708;} 121 | .cm-s-default .cm-atom {color: #219;} 122 | .cm-s-default .cm-number {color: #164;} 123 | .cm-s-default .cm-def {color: #00f;} 124 | .cm-s-default .cm-variable, 125 | .cm-s-default .cm-punctuation, 126 | .cm-s-default .cm-property, 127 | .cm-s-default .cm-operator {} 128 | .cm-s-default .cm-variable-2 {color: #05a;} 129 | .cm-s-default .cm-variable-3, .cm-s-default .cm-type {color: #085;} 130 | .cm-s-default .cm-comment {color: #a50;} 131 | .cm-s-default .cm-string {color: #a11;} 132 | .cm-s-default .cm-string-2 {color: #f50;} 133 | .cm-s-default .cm-meta {color: #555;} 134 | .cm-s-default .cm-qualifier {color: #555;} 135 | .cm-s-default .cm-builtin {color: #30a;} 136 | .cm-s-default .cm-bracket {color: #997;} 137 | .cm-s-default .cm-tag {color: #170;} 138 | .cm-s-default .cm-attribute {color: #00c;} 139 | .cm-s-default .cm-hr {color: #999;} 140 | .cm-s-default .cm-link {color: #00c;} 141 | 142 | .cm-s-default .cm-error {color: #f00;} 143 | .cm-invalidchar {color: #f00;} 144 | 145 | .CodeMirror-composing { border-bottom: 2px solid; } 146 | 147 | /* Default styles for common addons */ 148 | 149 | div.CodeMirror span.CodeMirror-matchingbracket {color: #0b0;} 150 | div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #a22;} 151 | .CodeMirror-matchingtag { background: rgba(255, 150, 0, .3); } 152 | .CodeMirror-activeline-background {background: #e8f2ff;} 153 | 154 | /* STOP */ 155 | 156 | /* The rest of this file contains styles related to the mechanics of 157 | the editor. You probably shouldn't touch them. */ 158 | 159 | .CodeMirror { 160 | position: relative; 161 | overflow: hidden; 162 | background: white; 163 | } 164 | 165 | .CodeMirror-scroll { 166 | overflow: scroll !important; /* Things will break if this is overridden */ 167 | /* 30px is the magic margin used to hide the element's real scrollbars */ 168 | /* See overflow: hidden in .CodeMirror */ 169 | margin-bottom: -30px; margin-right: -30px; 170 | padding-bottom: 30px; 171 | height: 100%; 172 | outline: none; /* Prevent dragging from highlighting the element */ 173 | position: relative; 174 | } 175 | .CodeMirror-sizer { 176 | position: relative; 177 | border-right: 30px solid transparent; 178 | } 179 | 180 | /* The fake, visible scrollbars. Used to force redraw during scrolling 181 | before actual scrolling happens, thus preventing shaking and 182 | flickering artifacts. */ 183 | .CodeMirror-vscrollbar, .CodeMirror-hscrollbar, .CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler { 184 | position: absolute; 185 | z-index: 6; 186 | display: none; 187 | } 188 | .CodeMirror-vscrollbar { 189 | right: 0; top: 0; 190 | overflow-x: hidden; 191 | overflow-y: scroll; 192 | } 193 | .CodeMirror-hscrollbar { 194 | bottom: 0; left: 0; 195 | overflow-y: hidden; 196 | overflow-x: scroll; 197 | } 198 | .CodeMirror-scrollbar-filler { 199 | right: 0; bottom: 0; 200 | } 201 | .CodeMirror-gutter-filler { 202 | left: 0; bottom: 0; 203 | } 204 | 205 | .CodeMirror-gutters { 206 | position: absolute; left: 0; top: 0; 207 | min-height: 100%; 208 | z-index: 3; 209 | } 210 | .CodeMirror-gutter { 211 | white-space: normal; 212 | height: 100%; 213 | display: inline-block; 214 | vertical-align: top; 215 | margin-bottom: -30px; 216 | } 217 | .CodeMirror-gutter-wrapper { 218 | position: absolute; 219 | z-index: 4; 220 | background: none !important; 221 | border: none !important; 222 | } 223 | .CodeMirror-gutter-background { 224 | position: absolute; 225 | top: 0; bottom: 0; 226 | z-index: 4; 227 | } 228 | .CodeMirror-gutter-elt { 229 | position: absolute; 230 | cursor: default; 231 | z-index: 4; 232 | } 233 | .CodeMirror-gutter-wrapper ::selection { background-color: transparent } 234 | .CodeMirror-gutter-wrapper ::-moz-selection { background-color: transparent } 235 | 236 | .CodeMirror-lines { 237 | cursor: text; 238 | min-height: 1px; /* prevents collapsing before first draw */ 239 | } 240 | .CodeMirror pre.CodeMirror-line, 241 | .CodeMirror pre.CodeMirror-line-like { 242 | /* Reset some styles that the rest of the page might have set */ 243 | -moz-border-radius: 0; -webkit-border-radius: 0; border-radius: 0; 244 | border-width: 0; 245 | background: transparent; 246 | font-family: inherit; 247 | font-size: inherit; 248 | margin: 0; 249 | white-space: pre; 250 | word-wrap: normal; 251 | line-height: inherit; 252 | color: inherit; 253 | z-index: 2; 254 | position: relative; 255 | overflow: visible; 256 | -webkit-tap-highlight-color: transparent; 257 | -webkit-font-variant-ligatures: contextual; 258 | font-variant-ligatures: contextual; 259 | } 260 | .CodeMirror-wrap pre.CodeMirror-line, 261 | .CodeMirror-wrap pre.CodeMirror-line-like { 262 | word-wrap: break-word; 263 | white-space: pre-wrap; 264 | word-break: normal; 265 | } 266 | 267 | .CodeMirror-linebackground { 268 | position: absolute; 269 | left: 0; right: 0; top: 0; bottom: 0; 270 | z-index: 0; 271 | } 272 | 273 | .CodeMirror-linewidget { 274 | position: relative; 275 | z-index: 2; 276 | padding: 0.1px; /* Force widget margins to stay inside of the container */ 277 | } 278 | 279 | .CodeMirror-widget {} 280 | 281 | .CodeMirror-rtl pre { direction: rtl; } 282 | 283 | .CodeMirror-code { 284 | outline: none; 285 | } 286 | 287 | /* Force content-box sizing for the elements where we expect it */ 288 | .CodeMirror-scroll, 289 | .CodeMirror-sizer, 290 | .CodeMirror-gutter, 291 | .CodeMirror-gutters, 292 | .CodeMirror-linenumber { 293 | -moz-box-sizing: content-box; 294 | box-sizing: content-box; 295 | } 296 | 297 | .CodeMirror-measure { 298 | position: absolute; 299 | width: 100%; 300 | height: 0; 301 | overflow: hidden; 302 | visibility: hidden; 303 | } 304 | 305 | .CodeMirror-cursor { 306 | position: absolute; 307 | pointer-events: none; 308 | } 309 | .CodeMirror-measure pre { position: static; } 310 | 311 | div.CodeMirror-cursors { 312 | visibility: hidden; 313 | position: relative; 314 | z-index: 3; 315 | } 316 | div.CodeMirror-dragcursors { 317 | visibility: visible; 318 | } 319 | 320 | .CodeMirror-focused div.CodeMirror-cursors { 321 | visibility: visible; 322 | } 323 | 324 | .CodeMirror-selected { background: #d9d9d9; } 325 | .CodeMirror-focused .CodeMirror-selected { background: #d7d4f0; } 326 | .CodeMirror-crosshair { cursor: crosshair; } 327 | .CodeMirror-line::selection, .CodeMirror-line > span::selection, .CodeMirror-line > span > span::selection { background: #d7d4f0; } 328 | .CodeMirror-line::-moz-selection, .CodeMirror-line > span::-moz-selection, .CodeMirror-line > span > span::-moz-selection { background: #d7d4f0; } 329 | 330 | .cm-searching { 331 | background-color: #ffa; 332 | background-color: rgba(255, 255, 0, .4); 333 | } 334 | 335 | /* Used to force a border model for a node */ 336 | .cm-force-border { padding-right: .1px; } 337 | 338 | @media print { 339 | /* Hide the cursor when printing */ 340 | .CodeMirror div.CodeMirror-cursors { 341 | visibility: hidden; 342 | } 343 | } 344 | 345 | /* See issue #2901 */ 346 | .cm-tab-wrap-hack:after { content: ''; } 347 | 348 | /* Help users use markselection to safely style text background */ 349 | span.CodeMirror-selectedtext { background: none; } 350 | -------------------------------------------------------------------------------- /src/client/fonts/RegolaPro-Bold.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shader-park/shader-park-website/e80177941cc96605aa86c7ca3de145354514c752/src/client/fonts/RegolaPro-Bold.woff2 -------------------------------------------------------------------------------- /src/client/fonts/RegolaPro-Book.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shader-park/shader-park-website/e80177941cc96605aa86c7ca3de145354514c752/src/client/fonts/RegolaPro-Book.woff2 -------------------------------------------------------------------------------- /src/client/images/Heart.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/client/images/Heart_filled.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/client/images/close-white.svg: -------------------------------------------------------------------------------- 1 | close-white -------------------------------------------------------------------------------- /src/client/images/close.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /src/client/images/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shader-park/shader-park-website/e80177941cc96605aa86c7ca3de145354514c752/src/client/images/favicon.png -------------------------------------------------------------------------------- /src/client/images/glitch_logo.svg: -------------------------------------------------------------------------------- 1 | logo-day -------------------------------------------------------------------------------- /src/client/images/msdf-left-align.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shader-park/shader-park-website/e80177941cc96605aa86c7ca3de145354514c752/src/client/images/msdf-left-align.png -------------------------------------------------------------------------------- /src/client/images/msdf3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shader-park/shader-park-website/e80177941cc96605aa86c7ca3de145354514c752/src/client/images/msdf3.png -------------------------------------------------------------------------------- /src/client/images/play.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Shape 5 | Created with Sketch. 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /src/client/images/share.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /src/client/images/sp_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shader-park/shader-park-website/e80177941cc96605aa86c7ca3de145354514c752/src/client/images/sp_logo.png -------------------------------------------------------------------------------- /src/client/images/touchdesigner_logo.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shader-park/shader-park-website/e80177941cc96605aa86c7ca3de145354514c752/src/client/images/touchdesigner_logo.jpg -------------------------------------------------------------------------------- /src/client/images/webclip.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shader-park/shader-park-website/e80177941cc96605aa86c7ca3de145354514c752/src/client/images/webclip.png -------------------------------------------------------------------------------- /src/client/mixins.less: -------------------------------------------------------------------------------- 1 | .smallDesktop(@rules) { 2 | @media (max-width: 1100px) { 3 | @rules(); 4 | } 5 | } 6 | 7 | .mobile(@rules) { 8 | @media (max-width: 580px) { 9 | @rules(); 10 | } 11 | } 12 | 13 | .editor-button { 14 | .mobile({ 15 | font-size: 14 !important; 16 | padding: 3px 13px 3px 13px; 17 | }); 18 | padding: 5px 15px 5px 15px; 19 | border-radius: 50px; 20 | /* border: 1px solid lightgrey; */ 21 | box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.15); 22 | margin-bottom: 5px; 23 | transition: color 300ms ease-in-out, box-shadow 300ms ease-in-out ; 24 | color: #777; 25 | -webkit-transition: color 300ms ease-in-out, box-shadow 300ms ease-in-out ; 26 | background-repeat: no-repeat, no-repeat; 27 | &:hover { 28 | color: #000; 29 | box-shadow: 0 1px 4px 0 rgba(0, 0, 0, 0.2); 30 | } 31 | } 32 | 33 | 34 | .control-button { 35 | .mobile({ 36 | font-size: 14 !important; 37 | padding: 3px 13px 3px 13px; 38 | }); 39 | opacity: 0.55; 40 | background-repeat: no-repeat; 41 | // box-shadow: none; 42 | transition: opacity 300ms ease-in-out; 43 | -webkit-transition: opacity 300ms ease-in-out; 44 | &:hover { 45 | // box-shadow: none; 46 | opacity: 1.0; 47 | } 48 | } -------------------------------------------------------------------------------- /src/client/normalize.css: -------------------------------------------------------------------------------- 1 | /*! normalize.css v3.0.3 | MIT License | github.com/necolas/normalize.css */ 2 | /** 3 | * 1. Set default font family to sans-serif. 4 | * 2. Prevent iOS and IE text size adjust after device orientation change, 5 | * without disabling user zoom. 6 | */ 7 | html { 8 | font-family: sans-serif; 9 | /* 1 */ 10 | -ms-text-size-adjust: 100%; 11 | /* 2 */ 12 | -webkit-text-size-adjust: 100%; 13 | /* 2 */ 14 | } 15 | /** 16 | * Remove default margin. 17 | */ 18 | body { 19 | margin: 0; 20 | } 21 | /* HTML5 display definitions 22 | ========================================================================== */ 23 | /** 24 | * Correct `block` display not defined for any HTML5 element in IE 8/9. 25 | * Correct `block` display not defined for `details` or `summary` in IE 10/11 26 | * and Firefox. 27 | * Correct `block` display not defined for `main` in IE 11. 28 | */ 29 | article, 30 | aside, 31 | details, 32 | figcaption, 33 | figure, 34 | footer, 35 | header, 36 | hgroup, 37 | main, 38 | menu, 39 | nav, 40 | section, 41 | summary { 42 | display: block; 43 | } 44 | /** 45 | * 1. Correct `inline-block` display not defined in IE 8/9. 46 | * 2. Normalize vertical alignment of `progress` in Chrome, Firefox, and Opera. 47 | */ 48 | audio, 49 | canvas, 50 | progress, 51 | video { 52 | display: inline-block; 53 | /* 1 */ 54 | vertical-align: baseline; 55 | /* 2 */ 56 | } 57 | /** 58 | * Prevent modern browsers from displaying `audio` without controls. 59 | * Remove excess height in iOS 5 devices. 60 | */ 61 | audio:not([controls]) { 62 | display: none; 63 | height: 0; 64 | } 65 | /** 66 | * Address `[hidden]` styling not present in IE 8/9/10. 67 | * Hide the `template` element in IE 8/9/10/11, Safari, and Firefox < 22. 68 | */ 69 | [hidden], 70 | template { 71 | display: none; 72 | } 73 | /* Links 74 | ========================================================================== */ 75 | /** 76 | * Remove the gray background color from active links in IE 10. 77 | */ 78 | a { 79 | background-color: transparent; 80 | } 81 | /** 82 | * Improve readability of focused elements when they are also in an 83 | * active/hover state. 84 | */ 85 | a:active, 86 | a:hover { 87 | outline: 0; 88 | } 89 | /* Text-level semantics 90 | ========================================================================== */ 91 | /** 92 | * Address styling not present in IE 8/9/10/11, Safari, and Chrome. 93 | */ 94 | abbr[title] { 95 | border-bottom: 1px dotted; 96 | } 97 | /** 98 | * Address style set to `bolder` in Firefox 4+, Safari, and Chrome. 99 | */ 100 | b, 101 | strong { 102 | font-weight: bold; 103 | } 104 | /** 105 | * Address styling not present in Safari and Chrome. 106 | */ 107 | dfn { 108 | font-style: italic; 109 | } 110 | /** 111 | * Address variable `h1` font-size and margin within `section` and `article` 112 | * contexts in Firefox 4+, Safari, and Chrome. 113 | */ 114 | h1 { 115 | /* font-size: 2em; */ 116 | margin: 0.67em 0; 117 | } 118 | /** 119 | * Address styling not present in IE 8/9. 120 | */ 121 | mark { 122 | background: #ff0; 123 | color: #000; 124 | } 125 | /** 126 | * Address inconsistent and variable font size in all browsers. 127 | */ 128 | small { 129 | font-size: 80%; 130 | } 131 | /** 132 | * Prevent `sub` and `sup` affecting `line-height` in all browsers. 133 | */ 134 | sub, 135 | sup { 136 | font-size: 75%; 137 | line-height: 0; 138 | position: relative; 139 | vertical-align: baseline; 140 | } 141 | sup { 142 | top: -0.5em; 143 | } 144 | sub { 145 | bottom: -0.25em; 146 | } 147 | /* Embedded content 148 | ========================================================================== */ 149 | /** 150 | * Remove border when inside `a` element in IE 8/9/10. 151 | */ 152 | img { 153 | border: 0; 154 | } 155 | /** 156 | * Correct overflow not hidden in IE 9/10/11. 157 | */ 158 | svg:not(:root) { 159 | overflow: hidden; 160 | } 161 | /* Grouping content 162 | ========================================================================== */ 163 | /** 164 | * Address margin not present in IE 8/9 and Safari. 165 | */ 166 | figure { 167 | margin: 1em 40px; 168 | } 169 | /** 170 | * Address differences between Firefox and other browsers. 171 | */ 172 | hr { 173 | box-sizing: content-box; 174 | height: 0; 175 | } 176 | /** 177 | * Contain overflow in all browsers. 178 | */ 179 | pre { 180 | overflow: auto; 181 | } 182 | /** 183 | * Address odd `em`-unit font size rendering in all browsers. 184 | */ 185 | code, 186 | kbd, 187 | pre, 188 | samp { 189 | font-family: monospace, monospace; 190 | font-size: 1em; 191 | } 192 | /* Forms 193 | ========================================================================== */ 194 | /** 195 | * Known limitation: by default, Chrome and Safari on OS X allow very limited 196 | * styling of `select`, unless a `border` property is set. 197 | */ 198 | /** 199 | * 1. Correct color not being inherited. 200 | * Known issue: affects color of disabled elements. 201 | * 2. Correct font properties not being inherited. 202 | * 3. Address margins set differently in Firefox 4+, Safari, and Chrome. 203 | */ 204 | button, 205 | input, 206 | optgroup, 207 | select, 208 | textarea { 209 | color: inherit; 210 | /* 1 */ 211 | font: inherit; 212 | /* 2 */ 213 | margin: 0; 214 | /* 3 */ 215 | } 216 | /** 217 | * Address `overflow` set to `hidden` in IE 8/9/10/11. 218 | */ 219 | button { 220 | overflow: visible; 221 | } 222 | /** 223 | * Address inconsistent `text-transform` inheritance for `button` and `select`. 224 | * All other form control elements do not inherit `text-transform` values. 225 | * Correct `button` style inheritance in Firefox, IE 8/9/10/11, and Opera. 226 | * Correct `select` style inheritance in Firefox. 227 | */ 228 | button, 229 | select { 230 | text-transform: none; 231 | } 232 | /** 233 | * 1. Avoid the WebKit bug in Android 4.0.* where (2) destroys native `audio` 234 | * and `video` controls. 235 | * 2. Correct inability to style clickable `input` types in iOS. 236 | * 3. Improve usability and consistency of cursor style between image-type 237 | * `input` and others. 238 | * 4. CUSTOM FOR WEBFLOW: Removed the input[type="submit"] selector to reduce 239 | * specificity and defer to the .w-button selector 240 | */ 241 | button, 242 | html input[type="button"], 243 | input[type="reset"] { 244 | -webkit-appearance: button; 245 | /* 2 */ 246 | cursor: pointer; 247 | /* 3 */ 248 | } 249 | /** 250 | * Re-set default cursor for disabled elements. 251 | */ 252 | button[disabled], 253 | html input[disabled] { 254 | cursor: default; 255 | } 256 | /** 257 | * Remove inner padding and border in Firefox 4+. 258 | */ 259 | button::-moz-focus-inner, 260 | input::-moz-focus-inner { 261 | border: 0; 262 | padding: 0; 263 | } 264 | /** 265 | * Address Firefox 4+ setting `line-height` on `input` using `!important` in 266 | * the UA stylesheet. 267 | */ 268 | input { 269 | line-height: normal; 270 | } 271 | /** 272 | * It's recommended that you don't attempt to style these elements. 273 | * Firefox's implementation doesn't respect box-sizing, padding, or width. 274 | * 275 | * 1. Address box sizing set to `content-box` in IE 8/9/10. 276 | * 2. Remove excess padding in IE 8/9/10. 277 | */ 278 | input[type="checkbox"], 279 | input[type="radio"] { 280 | box-sizing: border-box; 281 | /* 1 */ 282 | padding: 0; 283 | /* 2 */ 284 | } 285 | /** 286 | * Fix the cursor style for Chrome's increment/decrement buttons. For certain 287 | * `font-size` values of the `input`, it causes the cursor style of the 288 | * decrement button to change from `default` to `text`. 289 | */ 290 | input[type="number"]::-webkit-inner-spin-button, 291 | input[type="number"]::-webkit-outer-spin-button { 292 | height: auto; 293 | } 294 | /** 295 | * 1. CUSTOM FOR WEBFLOW: changed from `textfield` to `none` to normalize iOS rounded input 296 | * 2. CUSTOM FOR WEBFLOW: box-sizing: content-box rule removed 297 | * (similar to normalize.css >=4.0.0) 298 | */ 299 | input[type="search"] { 300 | -webkit-appearance: none; 301 | /* 1 */ 302 | } 303 | /** 304 | * Remove inner padding and search cancel button in Safari and Chrome on OS X. 305 | * Safari (but not Chrome) clips the cancel button when the search input has 306 | * padding (and `textfield` appearance). 307 | */ 308 | input[type="search"]::-webkit-search-cancel-button, 309 | input[type="search"]::-webkit-search-decoration { 310 | -webkit-appearance: none; 311 | } 312 | /** 313 | * Define consistent border, margin, and padding. 314 | */ 315 | fieldset { 316 | border: 1px solid #c0c0c0; 317 | margin: 0 2px; 318 | padding: 0.35em 0.625em 0.75em; 319 | } 320 | /** 321 | * 1. Correct `color` not being inherited in IE 8/9/10/11. 322 | * 2. Remove padding so people aren't caught out if they zero out fieldsets. 323 | */ 324 | legend { 325 | border: 0; 326 | /* 1 */ 327 | padding: 0; 328 | /* 2 */ 329 | } 330 | /** 331 | * Remove default vertical scrollbar in IE 8/9/10/11. 332 | */ 333 | textarea { 334 | overflow: auto; 335 | } 336 | /** 337 | * Don't inherit the `font-weight` (applied by a rule above). 338 | * NOTE: the default cannot safely be changed in Chrome and Safari on OS X. 339 | */ 340 | optgroup { 341 | font-weight: bold; 342 | } 343 | /* Tables 344 | ========================================================================== */ 345 | /** 346 | * Remove most spacing between table cells. 347 | */ 348 | table { 349 | border-collapse: collapse; 350 | border-spacing: 0; 351 | } 352 | td, 353 | th { 354 | padding: 0; 355 | } 356 | -------------------------------------------------------------------------------- /src/client/style.css: -------------------------------------------------------------------------------- 1 | @font-face { 2 | font-family: 'Inter ui'; 3 | src: url('/fonts/Inter-UI-SemiBold.woff') format('woff'); 4 | font-weight: 600; 5 | font-style: normal; 6 | } 7 | @font-face { 8 | font-family: 'Inter ui'; 9 | src: url('/fonts/Inter-UI-Regular.woff') format('woff'); 10 | font-weight: 400; 11 | font-style: normal; 12 | } 13 | @font-face { 14 | font-family: 'Inter ui'; 15 | src: url('/fonts/Inter-UI-Medium.woff') format('woff'); 16 | font-weight: 500; 17 | font-style: normal; 18 | } 19 | @font-face { 20 | font-family: 'Inter ui'; 21 | src: url('/fonts/Inter-UI-Black.woff') format('woff'); 22 | font-weight: 900; 23 | font-style: normal; 24 | } 25 | @font-face { 26 | font-family: 'Inter ui'; 27 | src: url('/fonts/Inter-UI-Bold.woff') format('woff'); 28 | font-weight: 700; 29 | font-style: normal; 30 | } 31 | @font-face { 32 | font-family: 'Inter ui'; 33 | src: url('/fonts/Inter-UI-ExtraBold.woff') format('woff'); 34 | font-weight: 800; 35 | font-style: normal; 36 | } 37 | @font-face { 38 | font-family: 'Regolapro'; 39 | src: url('/fonts/RegolaPro-Bold.otf') format('opentype'); 40 | font-weight: 700; 41 | font-style: normal; 42 | } 43 | @font-face { 44 | font-family: 'Regolapro'; 45 | src: url('/fonts/RegolaPro-Regular.otf') format('opentype'); 46 | font-weight: 400; 47 | font-style: normal; 48 | } 49 | @font-face { 50 | font-family: 'Regolapro'; 51 | src: url('/fonts/RegolaPro-Book.otf') format('opentype'); 52 | font-weight: 300; 53 | font-style: normal; 54 | } 55 | @font-face { 56 | font-family: 'Regolapro'; 57 | src: url('/fonts/RegolaPro-Medium.otf') format('opentype'); 58 | font-weight: 500; 59 | font-style: normal; 60 | } 61 | [v-cloak] { 62 | display: none; 63 | } 64 | .logo { 65 | position: relative; 66 | text-align: center; 67 | top: 50%; 68 | } 69 | #editor { 70 | position: absolute; 71 | top: 135px; 72 | right: 8px; 73 | width: 40%; 74 | height: 80%; 75 | visibility: visible; 76 | } 77 | #editor-controls { 78 | padding-bottom: 3px; 79 | } 80 | .CodeMirror { 81 | width: 100%; 82 | height: 100%; 83 | } 84 | .error-span { 85 | background-color: red; 86 | color: white; 87 | } 88 | body { 89 | background: white; 90 | font-family: 'Regolapro', 'Poppins', sans-serif; 91 | letter-spacing: 0.1px; 92 | margin: 0; 93 | } 94 | .ge_editor { 95 | letter-spacing: 0px; 96 | } 97 | button { 98 | letter-spacing: 0.5px; 99 | } 100 | h1 { 101 | letter-spacing: 0.5px; 102 | } 103 | a { 104 | text-decoration: none; 105 | transition: color 300ms ease-in-out; 106 | color: #006492; 107 | } 108 | a:hover { 109 | color: #black; 110 | } 111 | .w-layout-grid { 112 | display: -ms-grid; 113 | display: grid; 114 | grid-auto-columns: 1fr; 115 | -ms-grid-columns: 1fr 1fr; 116 | grid-template-columns: 1fr 1fr; 117 | -ms-grid-rows: auto auto; 118 | grid-template-rows: auto auto; 119 | grid-row-gap: 16px; 120 | grid-column-gap: 16px; 121 | } 122 | input:focus, 123 | select:focus, 124 | textarea:focus, 125 | button:focus { 126 | outline: none; 127 | } 128 | -------------------------------------------------------------------------------- /src/components/About.vue: -------------------------------------------------------------------------------- 1 | 38 | 39 | 59 | 60 | 61 | 100 | -------------------------------------------------------------------------------- /src/components/ActionBar.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 106 | 107 | 108 | -------------------------------------------------------------------------------- /src/components/CardModal.vue: -------------------------------------------------------------------------------- 1 | 6 | 23 | 24 | 35 | -------------------------------------------------------------------------------- /src/components/Error404.vue: -------------------------------------------------------------------------------- 1 | 9 | -------------------------------------------------------------------------------- /src/components/Examples.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | -------------------------------------------------------------------------------- /src/components/Explore.vue: -------------------------------------------------------------------------------- 1 | 15 | 16 | 65 | 66 | 71 | -------------------------------------------------------------------------------- /src/components/Header.vue: -------------------------------------------------------------------------------- 1 | 42 | 43 | 97 | 98 | 304 | -------------------------------------------------------------------------------- /src/components/Home.vue: -------------------------------------------------------------------------------- 1 | 120 | 121 | 232 | 233 | 234 | 512 | -------------------------------------------------------------------------------- /src/components/Home3D.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | -------------------------------------------------------------------------------- /src/components/MainContainer.vue: -------------------------------------------------------------------------------- 1 | 16 | 17 | 113 | 114 | 211 | -------------------------------------------------------------------------------- /src/components/New.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 102 | 103 | 110 | -------------------------------------------------------------------------------- /src/components/Profile.vue: -------------------------------------------------------------------------------- 1 | 14 | 15 | 111 | 112 | -------------------------------------------------------------------------------- /src/components/Room.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | -------------------------------------------------------------------------------- /src/components/Sculpture.vue: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | -------------------------------------------------------------------------------- /src/components/SculptureFeed.vue: -------------------------------------------------------------------------------- 1 | 26 | 66 | 67 | 68 | 148 | -------------------------------------------------------------------------------- /src/components/ShareModal.vue: -------------------------------------------------------------------------------- 1 | 31 | 32 | 117 | 118 | 208 | -------------------------------------------------------------------------------- /src/components/SignIn.vue: -------------------------------------------------------------------------------- 1 | 14 | 15 | 73 | 80 | -------------------------------------------------------------------------------- /src/components/SignUp.vue: -------------------------------------------------------------------------------- 1 | 15 | 16 | 79 | 80 | 122 | -------------------------------------------------------------------------------- /src/components/SpThreeVue.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 206 | 207 | 208 | -------------------------------------------------------------------------------- /src/components/UniformGUI.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 131 | 132 | 200 | -------------------------------------------------------------------------------- /src/dbConfig.js: -------------------------------------------------------------------------------- 1 | export const dbConfig = { 2 | apiKey: 'AIzaSyAEc1jo0NaCuSRsdKzrxDEkm6zEDqsKHIw', 3 | authDomain: 'shader-park-core.firebaseapp.com', 4 | databaseURL: 'https://shader-park-core.firebaseio.com', 5 | projectId: 'shader-park-core', 6 | storageBucket: 'shader-park-core.appspot.com', 7 | messagingSenderId: '450026358426', 8 | appId: "1:450026358426:web:3c6e10ed94e67fd3644123", 9 | measurementId: "G-6609S7GCV5" 10 | }; -------------------------------------------------------------------------------- /src/helpers/front-page-sculp1.js: -------------------------------------------------------------------------------- 1 | export function spCode2() { 2 | return ` 3 | let hover = input(0, 0, 1); 4 | let click = input(0, 0, 1); 5 | let scroll = input(); 6 | lightDirection(getRayDirection()); 7 | metal(.4); 8 | shine(.7); 9 | 10 | let sCurve = shape((size, innerOffset) => { 11 | sphere(size); 12 | difference(); 13 | let s = getSpace(); 14 | displace(0.1, innerOffset, s.z); 15 | sphere(size-.03); 16 | expand(.00) 17 | }) 18 | 19 | 20 | let s = getSpace(); 21 | let col = vec3(0, .1, length(normal)); 22 | color(col+normal*.1) 23 | rotateX((sin(time))*.04); 24 | rotateZ((sin(time))*.04); 25 | rotateY((cos(time))*.1); 26 | 27 | shape(() => { 28 | for(let i = 0; i < 3; i++) { 29 | blend(.1); 30 | // let rot = 4 * sin(time * .2) * .5 * hover; 31 | let rot = hover; 32 | rotateX(rot); 33 | rotateY(rot); 34 | rotateZ(rot); 35 | sCurve(1*(i/3)+.3, .2); 36 | } 37 | sphere(.15); 38 | })(); 39 | mixGeo(nsin(time)*.22+.9 * 1-click); 40 | sphere(.8) 41 | `; 42 | } 43 | 44 | export function spCode() { 45 | //Put your Shader Park Code here 46 | return ` 47 | let scroll = input(); 48 | let hover = input(); 49 | let click = input(); 50 | //setMaxIterations(1) 51 | setStepSize(.3); 52 | lightDirection(mouse); 53 | 54 | 55 | rotateY(time * .2); 56 | let warpedSpace = warpSpace(getSpace()); 57 | metal(.9); 58 | shine(1); 59 | color(1 - warpedSpace); 60 | sphere(.5 + length(warpedSpace) * .2); 61 | expand(hover * .03) 62 | 63 | // inspired by https://www.shadertoy.com/view/ttlGDf 64 | function warpSpace(p) { 65 | let t = time / 4.; 66 | rotateY(getRayDirection().y * (1 - click) * 12); 67 | p = getSpace().x*8.0*(vec3(0.5, .2, .1) + p); 68 | for(let i = 1.0; i < 3.0; i += 1.0) { 69 | p.x = p.x +hover * sin(2.0 * t + i * 1.5 * p.y) + t * 0.5; 70 | p.y = p.x + hover * cos(2.0 * t + i * 1.5 * p.x); 71 | } 72 | return 0.5 + 0.5 * cos(time + vec3(p.x, p.y, p.x) + vec3(0., 2., 4.)); 73 | } 74 | ` 75 | }; 76 | 77 | export function spCode3() { 78 | // for mobile we keep iterations very low for improved performance 79 | return ` 80 | setMaxIterations(1); 81 | backgroundColor(0, 5, 20); 82 | let scroll = input(); 83 | let hover = input(); 84 | let click = input(); 85 | let t = time *.1+scroll; 86 | // let t = time *.01; 87 | 88 | let n = noise( vec3(0, 0, -1*t) + noise(getRayDirection() * 1 + t)); 89 | displace(n*.2); 90 | let col = vec3(nsin(n*2) + ncos(n*10)); 91 | color(col * 3); 92 | // metal(n); 93 | // shine(n); 94 | 95 | sphere(0.5+length(col)*.01); 96 | `; 97 | } -------------------------------------------------------------------------------- /src/helpers/handelUnsavedChanges.js: -------------------------------------------------------------------------------- 1 | 2 | export const handelUnsavedChanges = (next, self)=> { 3 | if (Object.keys(self.$store.getters.unsavedChanges).length !== 0) { 4 | self.$modal.show('dialog', { 5 | title: 'Unsaved Changes', 6 | text: 'Are you sure you want to leave and discard changes?', 7 | buttons: [ 8 | { 9 | title: 'Leave & Discard Changes', // Button title 10 | handler: () => { 11 | self.$store.commit('resetUnsavedChanges'); 12 | next(); 13 | self.$modal.hide('dialog') 14 | } 15 | }, 16 | { 17 | title: 'Cancel', 18 | default: true, 19 | handler: () => self.$modal.hide('dialog') 20 | }] 21 | }) 22 | } else { 23 | next(); 24 | } 25 | } -------------------------------------------------------------------------------- /src/registerServiceWorker.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-console */ 2 | 3 | import { register } from 'register-service-worker' 4 | 5 | if (process.env.NODE_ENV === 'production') { 6 | register(`${process.env.BASE_URL}service-worker.js`, { 7 | ready () { 8 | console.log( 9 | 'App is being served from cache by a service worker.\n' + 10 | 'For more details, visit https://goo.gl/AFskqB' 11 | ) 12 | }, 13 | registered () { 14 | console.log('Service worker has been registered.') 15 | }, 16 | cached () { 17 | console.log('Content has been cached for offline use.') 18 | }, 19 | updatefound () { 20 | console.log('New content is downloading.') 21 | }, 22 | updated () { 23 | console.log('New content is available; please refresh.'); 24 | window.location.reload(); 25 | }, 26 | offline () { 27 | console.log('No internet connection found. App is running in offline mode.') 28 | }, 29 | error (error) { 30 | console.error('Error during service worker registration:', error) 31 | } 32 | }) 33 | } 34 | -------------------------------------------------------------------------------- /src/router/routes.js: -------------------------------------------------------------------------------- 1 | import New from '../components/New.vue'; 2 | 3 | 4 | // This is where you add all your site routes 5 | // Each route is set as an obect in the array 6 | // For a the most basic route just set 7 | // the path & component to load 8 | 9 | export const routes = [ 10 | { 11 | path: '/explore', 12 | name: 'explore', 13 | component: () => import(/* webpackChunkName: "explore" */ '../components/Explore.vue'), 14 | meta: { 15 | title: 'Explore', 16 | } 17 | }, 18 | { 19 | path: '', 20 | name: 'home', 21 | component: () => import(/* webpackChunkName: "home" */ '../components/Home.vue'), 22 | meta: { 23 | title: '', 24 | } 25 | }, 26 | { 27 | path: '/examples', 28 | name: 'examples', 29 | component: () => import(/* webpackChunkName: "examples" */ '../components/Examples.vue'), 30 | props: (route) => ({embed: route.query.embed}), 31 | meta: { 32 | title: 'Examples', 33 | } 34 | }, 35 | { 36 | path: '/gallery', 37 | name: 'gallery', 38 | component: () => import(/* webpackChunkName: "examples" */ '../components/Home3D.vue'), 39 | props: (route) => ({embed: route.query.embed}), 40 | meta: { 41 | title: 'Gallery', 42 | } 43 | }, 44 | { 45 | path: '/new/:type', 46 | name: 'new', 47 | component: New, 48 | meta: { 49 | title: 'New Sculpture', 50 | selectedSculpture: true 51 | } 52 | }, 53 | { 54 | path: '/new', 55 | name: 'new', 56 | component: New, 57 | meta: { 58 | title: 'New Sculpture', 59 | selectedSculpture: true 60 | } 61 | }, 62 | { 63 | path: '/sign-in', 64 | name: 'signIn', 65 | component: () => import(/* webpackChunkName: "signin" */ '../components/SignIn.vue'), 66 | meta: { 67 | title: 'Sign In', 68 | } 69 | }, 70 | { 71 | path: '/sign-up', 72 | name: 'signUp', 73 | component: () => import(/* webpackChunkName: "signup" */ '../components/SignUp.vue'), 74 | meta: { 75 | title: 'Sign Up', 76 | } 77 | }, 78 | { 79 | path: '/profile', 80 | name: 'profile', 81 | component: () => import(/* webpackChunkName: "profile" */ '../components/Profile.vue'), 82 | meta: {title: 'Profile', requiresAuth: true} 83 | }, 84 | { 85 | path: '/featured', 86 | name: 'featured', 87 | component: () => import(/* webpackChunkName: "home" */ '../components/Explore.vue'), 88 | meta: { title: 'Featured', requiresAuth: false } 89 | }, 90 | { 91 | path: '/sculpture/:id', 92 | name: 'sculpture', 93 | component: New, 94 | props: (route) => ({ 95 | example: route.query.example, 96 | embed: route.query.embed, 97 | hideEditor: route.query.hideeditor, 98 | hidePedestal: route.query.hidepedestal, 99 | clickEnabled: route.query.clickenabled 100 | }), 101 | meta: { 102 | title: 'sculpture', 103 | selectedSculpture: true 104 | } 105 | }, 106 | { 107 | path: '/embed/:id', 108 | name: 'embed', 109 | component: New, 110 | props: (route) => ({ 111 | example: route.query.example, 112 | embed: true, 113 | hideEditor: true, 114 | hidePedestal: true, 115 | clickEnabled: false 116 | }), 117 | meta: { 118 | title: 'embed', 119 | selectedSculpture: true 120 | } 121 | }, 122 | { 123 | path: '/user/:username', 124 | name: 'user', 125 | component: () => import(/* webpackChunkName: "profile" */ '../components/Profile.vue'), 126 | meta: { 127 | title: 'user', 128 | } 129 | }, 130 | { 131 | path: '/about', 132 | name: 'about', 133 | component: () => import(/* webpackChunkName: "about" */ '../components/About.vue'), 134 | meta: { title: 'About'} 135 | }, 136 | { 137 | path: '/404', 138 | name: '404', 139 | component: import(/* webpackChunkName: "error" */ '../components/Error404.vue') 140 | }, 141 | {path: '*', redirect: '/404'} 142 | ] -------------------------------------------------------------------------------- /src/schema/User.js: -------------------------------------------------------------------------------- 1 | export const userSchema = { 2 | username : '', 3 | created : Date.now(), 4 | email : '', 5 | website : null, 6 | bio : null, 7 | "profile-picture" : null, 8 | favorites : [], 9 | sketches : [], 10 | followers : [], 11 | following : [], 12 | notifications : [], 13 | } -------------------------------------------------------------------------------- /src/store/store.js: -------------------------------------------------------------------------------- 1 | import firebase from 'firebase/compat/app'; 2 | import 'firebase/compat/auth'; 3 | 4 | import Vue from 'vue'; 5 | import Vuex from 'vuex'; 6 | 7 | Vue.use(Vuex); 8 | 9 | export const store = new Vuex.Store({ 10 | 11 | state: { 12 | user: null, 13 | userFavorites: {}, 14 | socket: null, 15 | clickEnabled: true, 16 | currentRoom: null, 17 | initialCameraPose: null, 18 | selectedSculpture: null, // vue sculpture data 19 | currSculpture: null, // vue sculpture data for currently loaded sculpure 20 | selectedObject: null, // three.js mesh 21 | objectsToUpdate: [], 22 | objectsToRaycast: [], 23 | intersectedObject: null, 24 | currentSculptures: [], 25 | loading: false, 26 | sculpturesLoaded: false, 27 | profileBadgeCount: 0, 28 | embedded: false, 29 | canvasSize: {width: 0, height: 0}, 30 | displayLogin: false, 31 | displaySignUp: false, 32 | displayCanvas: false, 33 | routeTitle: null, 34 | unsavedChanges: {}, 35 | MSDFTexture: {}, 36 | sculptureError: null, 37 | displayShareModal: false 38 | }, 39 | getters: { 40 | userFavorites: state => { 41 | return state.userFavorites; 42 | }, 43 | displayShareModal: state => { 44 | return state.displayShareModal; 45 | }, 46 | getSculptureError: state => { 47 | return state.sculptureError; 48 | }, 49 | displayCanvas: state => { 50 | return state.displayCanvas; 51 | }, 52 | getMSDFTexture: state => { 53 | return state.MSDFTexture; 54 | }, 55 | unsavedChanges: state => { 56 | return state.unsavedChanges; 57 | }, 58 | routeTitle: state => { 59 | return state.routeTitle; 60 | }, 61 | displayLogin: state => { 62 | return state.displayLogin; 63 | }, 64 | displaySignUp: state => { 65 | return state.displaySignUp; 66 | }, 67 | getUser: state => { 68 | return state.user; 69 | }, 70 | selectedSculpture: state => { 71 | return state.selectedSculpture; 72 | }, 73 | getCurrentSculptures: state => { 74 | return state.currentSculptures; 75 | }, 76 | isAdmin: state => { 77 | return state.user && state.user.uid === 'K3lAQQTKbiTiVXlwRZouH4OrWyv1'; 78 | }, 79 | getProfileBadgeCount: state => { 80 | return state.profileBadgeCount; 81 | }, 82 | getEmbedded: state => { 83 | return state.embedded; 84 | } 85 | }, 86 | mutations: { 87 | setCurrSculpture(state, payload) { 88 | state.currSculpture = {}; 89 | state.currSculpture = payload; 90 | }, 91 | setUserFavorites(state, payload) { 92 | state.userFavorites = {}; 93 | Object.assign(state.userFavorites, payload); 94 | }, 95 | displayShareModal(state, payload) { 96 | state.displayShareModal = payload; 97 | }, 98 | setSculptureError(state, payload) { 99 | state.sculptureError = payload; 100 | }, 101 | setDisplayCanvas(state, payload) { 102 | state.displayCanvas = payload; 103 | }, 104 | setMSDFTexture(state, payload) { 105 | state.MSDFTexture = payload; 106 | }, 107 | setUnsavedChanges(state, payload) { 108 | Object.keys(payload).forEach(key => { 109 | if(payload[key]){ 110 | delete state.unsavedChanges[key]; 111 | } else { 112 | state.unsavedChanges[key] = payload[key]; 113 | } 114 | }); 115 | }, 116 | resetUnsavedChanges(state) { 117 | state.unsavedChanges = {}; 118 | }, 119 | setClickEnabled(state, payload) { 120 | state.clickEnabled = payload; 121 | }, 122 | setInitialCameraPose(state, payload) { 123 | state.initialCameraPose = payload; 124 | }, 125 | setRouteTitle(state, title) { 126 | state.routeTitle = title; 127 | }, 128 | displayLogin(state, payload) { 129 | state.displayLogin = payload; 130 | if (payload) { 131 | state.displaySignUp = false; 132 | } 133 | }, 134 | displaySignUp(state, payload) { 135 | state.displaySignUp = payload; 136 | if (payload) { 137 | state.displayLogin = false; 138 | } 139 | }, 140 | setCanvasSize(state, size) { 141 | state.canvasSize = size; 142 | }, 143 | setUser: state => { 144 | state.user = firebase.auth().currentUser; 145 | }, 146 | setEmbedded(state, embedded) { 147 | state.embedded = embedded; 148 | }, 149 | setSelectedSculpture(state, sculpture) { 150 | state.selectedSculpture = sculpture; 151 | }, 152 | setCurrentSculptures(state, payload) { 153 | state.currentSculptures = payload; 154 | }, 155 | setLoading(state, payload) { 156 | state.loading = payload; 157 | }, 158 | incrementProfileBadgeCount(state) { 159 | state.profileBadgeCount += 1; 160 | }, 161 | setProfileBadgeCount(state, payload) { 162 | state.profileBadgeCount = payload; 163 | }, 164 | sculpturesLoaded(state, payload) { 165 | state.sculpturesLoaded = payload; 166 | }, 167 | updateSelectedSculpture(state, payload) { 168 | state.selectedSculpture = Object.assign(state.selectedSculpture, payload); 169 | }, 170 | updateSculptureIdInScene(state, payload) { 171 | const oldId = payload.oldId; 172 | const newId = payload.newId; 173 | const obj = window.scene.getObjectByName(oldId); 174 | if(obj) { 175 | obj.name = newId; 176 | } else { 177 | console.error('no object found when updating sculpture ID '); 178 | } 179 | state.selectedSculpture.id = newId; 180 | }, 181 | removeObjectFromUpdate(state, payload) { 182 | const index = state.objectsToUpdate.indexOf(payload); 183 | if (index != -1) { 184 | state.objectsToUpdate.splice(index, 1); 185 | } 186 | }, 187 | removeObjectFromRaycast(state, payload) { 188 | const index = state.objectsToRaycast.indexOf(payload); 189 | if (index != -1) { 190 | state.objectsToRaycast.splice(index, 1); 191 | } 192 | }, 193 | removeObjectFromSceneByName(state, name) { 194 | window.scene.remove(window.scene.getObjectByName(name)); 195 | }, 196 | joinRoom(state, roomName) { 197 | // state.socket.emit('joinRoom', roomName); 198 | state.currentRoom = roomName; 199 | }, 200 | leaveRoom(state, roomName) { 201 | // state.socket.emit('leaveRoom', roomName); 202 | state.currentRoom = null; 203 | } 204 | }, 205 | actions: { 206 | setUser: context => { 207 | context.commit('setUser'); 208 | }, 209 | getAllUserNames() { 210 | return firebase.database().ref(`usernames`).once('value') 211 | .then(data => data.val()); 212 | }, 213 | getUserIdFromUsername(context, username) { 214 | return firebase.database().ref(`usernames/${username}`).once('value') 215 | .then(data => data.val()); 216 | }, 217 | getUserName({getters}) { 218 | const user = getters.getUser; 219 | return firebase.database().ref(`users/${user.uid}`).once('value') 220 | .then(data => data.val().username); 221 | }, 222 | setDBUser(context, payload) { 223 | let uid = payload.uid; 224 | let dbUser = payload.user; 225 | firebase.database().ref('users/' + uid).set(dbUser); 226 | firebase.database().ref('usernames/' + dbUser.username).set(uid); 227 | }, 228 | async saveNewSculpture({commit, getters}, sculpture) { 229 | const user = getters.getUser; 230 | // sculpture.author = { uid: user.uid, username: user.displayName }; 231 | sculpture.uid = user.uid; 232 | sculpture.username = user.displayName; 233 | sculpture.timestamp = Date.now(); 234 | const idHistory = { 235 | oldId: sculpture.id || sculpture.vueId, 236 | newId: '' 237 | }; 238 | delete sculpture.vueId; // remove the vueId when saving to db 239 | const sculpID = await firebase.database().ref(`sculptures`).push().key; 240 | sculpture.id = sculpID; 241 | let route = sculpture.isExample && getters.isAdmin ? 'examples' : 'sculptures'; 242 | await firebase.database().ref(`${route}/${sculpID}`).update(sculpture) 243 | .catch(error => console.error(error)); 244 | 245 | //update list of sculptures in user's profile 246 | await firebase.database().ref(`users/${user.uid}/sculptures`).child(sculpID).set(sculpID) 247 | .catch(error => console.error(error)); 248 | 249 | idHistory.newId = sculpID; 250 | 251 | commit('updateSelectedSculpture', sculpture); 252 | commit('updateSculptureIdInScene', idHistory); 253 | commit('setLoading', false); 254 | commit('incrementProfileBadgeCount'); 255 | return sculpID; 256 | }, 257 | saveSculpture({commit, dispatch, getters}, sculptureObj) { 258 | return new Promise(async (resolve, reject) => { 259 | try { 260 | commit('setLoading', true); 261 | let sculpture = Object.assign({}, sculptureObj); 262 | delete sculpture.sculpture; //remove the three.js 3d object 263 | 264 | console.log('saving sculpture'); 265 | const user = getters.getUser; 266 | if(!user) { 267 | console.error('Tried to Save Sculpture when a User is not logged in'); 268 | reject('Tried to Save Sculpture when a User is not logged in'); 269 | } 270 | 271 | if (!sculpture.id || sculpture.id === sculpture.vueId) { 272 | console.log('saving new sculpture'); 273 | resolve(dispatch('saveNewSculpture', sculpture)); 274 | } else { 275 | if (sculpture.uid === user.uid || getters.isAdmin) { // update existing sculpture 276 | let route = sculpture.isExample && getters.isAdmin ? 'examples' : 'sculptures'; //must be admin to update example 277 | delete sculpture.vueId; // remove the three.js 3d object 278 | 279 | resolve(firebase.database().ref(`${route}/${sculpture.id}`).update(sculpture).then(() => { 280 | commit('setLoading', false); 281 | })); 282 | } else { // Save as a fork 283 | console.log('save as fork'); 284 | const newForkCount = sculpture.forks += 1; 285 | //update existing sculpture fork count 286 | let route = sculpture.isExample? 'examples' : 'sculptures'; //they don't have to be an admin to fork 287 | await firebase.database().ref(`${route}/${sculpture.id}/forks`).set(newForkCount); 288 | sculpture.isExample = false; //fork isn't an example 289 | Object.assign(sculpture, { 290 | forks: 0, 291 | uid: user.uid, 292 | username: user.displayName, 293 | fork: sculpture.id, 294 | forkedSculptureTitle: sculpture.title, 295 | featured: false, 296 | isExample: false, 297 | views: 0, 298 | favorites: 0, 299 | comments: 0 300 | }); 301 | // sculpture.forks = 0; 302 | // sculpture.uid = user.uid; 303 | // sculpture.author = {uid: user.uid, username: user.displayName}; //new author 304 | // sculpture.fork = sculpture.id; //save the id of the original sculpture 305 | const fork = { 306 | uid: user.uid, 307 | username: user.displayName, 308 | timestamp: Date.now(), 309 | shaderSourceHistory: sculpture.shaderSource, 310 | rootId: sculpture.id 311 | } 312 | let newId = await dispatch('saveNewSculpture', sculpture); 313 | fork['newSculptureId'] = newId; 314 | resolve(firebase.database().ref(`forks`).push(fork).then(() => { 315 | commit('setLoading', false); 316 | })); 317 | } 318 | } 319 | } catch(e) { 320 | console.error(e); 321 | reject(e); 322 | } 323 | }); 324 | }, 325 | removeSelectedSculptureFromScene({commit, getters}) { 326 | let selectedSculpture = getters.selectedSculpture; 327 | commit('removeObjectFromUpdate', selectedSculpture.sculpture); 328 | commit('removeObjectFromRaycast', selectedSculpture.sculpture.mesh); 329 | commit('removeObjectFromSceneByName', selectedSculpture.id); 330 | }, 331 | deleteSculpture({commit, getters, dispatch}, sculpture) { 332 | commit('setLoading', true); 333 | const user = getters.getUser; 334 | if (sculpture.uid === user.uid) { 335 | let route = sculpture.isExample && getters.isAdmin ? 'examples' : 'sculptures'; //must be admin to update example 336 | if(sculpture.fork) { 337 | firebase.database().ref('forks').orderByChild('newSculptureId').equalTo(sculpture.id).once('value').then(data => { 338 | let fork = data.val(); 339 | if(fork) { 340 | let forkId = Object.keys(fork)[0]; 341 | let rootSculptureId = fork[forkId].rootId; 342 | dispatch('fetchSculpture', {id: rootSculptureId, isExample: sculpture.isExample}).then((sculptureToUpdateForkCount) => { 343 | let route = sculptureToUpdateForkCount.isExample? 'examples' : 'sculptures'; //must be admin to update example 344 | sculptureToUpdateForkCount.forks -= 1; 345 | firebase.database().ref(`${route}/${sculptureToUpdateForkCount.id}`).update(sculptureToUpdateForkCount) 346 | .catch(error => console.error(error)); 347 | }); 348 | //delete fork 349 | firebase.database().ref(`forks/${forkId}`).remove().catch(error => console.error(error)); 350 | } 351 | }); 352 | } 353 | firebase.database().ref(`${route}/${sculpture.id}`).remove().catch(error => console.error(error)); 354 | firebase.database().ref(`users/${user.uid}/sculptures/${sculpture.id}`).remove().then(() => { 355 | commit('setLoading', false); 356 | }).catch(error => console.error(error)); 357 | 358 | } else { 359 | console.error('Tried to delete another user\'s sculpture'); 360 | } 361 | }, 362 | fetchUserSculptures({commit, getters}, username) { 363 | commit('setLoading', true); 364 | // const userId = uid || getters.getUser.uid; 365 | 366 | let route = 'sculptures'; 367 | if(getters.isAdmin) { 368 | route = 'examples'; 369 | } 370 | 371 | return firebase.database().ref(route).orderByChild('username').equalTo(username).once('value').then(data => { 372 | const sculptures = data.val(); 373 | if(sculptures) { 374 | return Object.keys(sculptures).map(key => sculptures[key]); 375 | } 376 | return []; 377 | }).catch(error => console.error(error)); 378 | 379 | // return firebase.database().ref(`${route}/${userId}`).once('value').then(data => { 380 | // commit('setLoading', false); 381 | // return data.val(); 382 | // }) 383 | // .catch(error => console.log(error)); 384 | }, 385 | fetchSculpture({commit, getters}, payload) { 386 | if (!payload.id) { 387 | console.error('No id passed to fetch sculpture'); 388 | return; 389 | } 390 | commit('setLoading', true); 391 | const reference = payload.example ? 'examples/': 'sculptures/'; 392 | return firebase.database().ref(`${reference}/${payload.id}`).once('value') 393 | .then(data => { 394 | commit('setLoading', false); 395 | return data.val(); 396 | }) 397 | .catch(error => console.log(error)); 398 | 399 | // return firebase.database().ref(`${reference}`).orderByChild(payload.id).limitToLast(1).once('value') 400 | // .then(data => { 401 | // commit('setLoading', true); 402 | // //ends up getting the user's entire profile 403 | // const userSculpts = data.val(); 404 | // const keys = Object.keys(userSculpts); 405 | // if (userSculpts && keys.length > 0) { 406 | // commit('setLoading', false); 407 | // return userSculpts[keys[0]][payload.id]; 408 | // } 409 | // commit('setLoading', false); 410 | // return null; 411 | // }) 412 | // .catch(error => console.log(error)); 413 | }, 414 | fetchSculptures({commit, getters}, reference) { 415 | commit('setLoading', true); 416 | const user = getters.getUser; 417 | return firebase.database().ref(reference).once('value').then(data => { 418 | const sculptures = data.val(); 419 | let output = Object.keys(sculptures).map(key => sculptures[key]); 420 | // let output = []; 421 | // Object.keys(sculptures) 422 | // .forEach(key => { 423 | // Object.keys(sculptures[key]).forEach(sculptureKey => { 424 | // const sculpture = sculptures[key][sculptureKey]; 425 | // output.push(sculpture); 426 | // }) 427 | // }); 428 | commit('setLoading', false); 429 | return output; 430 | }) 431 | .catch(error => console.log(error)); 432 | }, 433 | fetchFeaturedSculptures({commit},) { 434 | commit('setLoading', true); 435 | return firebase.database().ref('sculptures').orderByChild("featured").equalTo(true).once('value').then(data => { 436 | const sculptures = data.val(); 437 | let output = Object.keys(sculptures).map(key => sculptures[key]); 438 | commit('setLoading', false); 439 | return output; 440 | }) 441 | .catch(error => console.log(error)); 442 | }, 443 | fetchAllSculptures({dispatch}) { 444 | return dispatch('fetchSculptures', 'sculptures'); 445 | }, 446 | fetchExampleSculptures({dispatch}) { 447 | return dispatch('fetchSculptures', 'examples'); 448 | }, 449 | fetchForks() { 450 | return firebase.database().ref('/forks').once('value').then(data => data.val()); 451 | }, 452 | favorite({commit, getters}, {sculpture, favorited}) { 453 | return new Promise(async (resolve, reject) => { 454 | commit('setLoading', true); 455 | 456 | let vueXUserFavorites = getters.userFavorites; 457 | const user = getters.getUser; 458 | if(!user) { 459 | console.error('Tried to favorite Sculpture when a User is not logged in'); 460 | reject('Tried to favorite Sculpture when a User is not logged in'); 461 | } 462 | let route = sculpture.isExample? 'examples' : 'sculptures'; 463 | 464 | // Update Favorite Count 465 | await firebase.database().ref(`${route}/${sculpture.id}/favorites`).transaction((currValue) => { 466 | let newFavCount; 467 | if(favorited) { 468 | newFavCount = (currValue || 0) + 1; 469 | } else { 470 | newFavCount = Math.max((currValue || 0) - 1, 0); 471 | } 472 | sculpture.favorites = newFavCount; 473 | commit('setCurrSculpture', sculpture); 474 | return newFavCount; 475 | }).catch(error => console.error(error)); 476 | 477 | 478 | // Save, or Remove favorite 479 | let time = Date.now(); 480 | let userPath = `users/${user.uid}/favorites/${sculpture.id}`; 481 | if(favorited) { 482 | let favorite = { 483 | uid: user.uid, 484 | timestamp: time, 485 | sculptureId: sculpture.id, 486 | } 487 | const favoriteId = await firebase.database().ref(`favorites/`).push(favorite).key; 488 | await firebase.database().ref(userPath).set(favoriteId).catch(error => console.error(error)); 489 | //make sure userFavorites is a dictionary 490 | if(!(vueXUserFavorites && vueXUserFavorites.constructor === Object)) { 491 | vueXUserFavorites = {}; 492 | } 493 | vueXUserFavorites[sculpture.id] = favoriteId; 494 | 495 | store.commit('setUserFavorites', vueXUserFavorites); 496 | } else { 497 | let favoriteObj = await firebase.database().ref(userPath).once('value'); 498 | let favoriteId = favoriteObj.val(); 499 | delete vueXUserFavorites[sculpture.id]; 500 | store.commit('setUserFavorites', vueXUserFavorites); 501 | await firebase.database().ref(userPath).remove().catch(error => console.error(error)); 502 | await firebase.database().ref(`favorites/${favoriteId}`).remove().catch(error => console.error(error)); 503 | } 504 | 505 | commit('setLoading', false); 506 | resolve(); 507 | }); 508 | }, 509 | fetchUserFavorites({commit, getters}, uid) { 510 | return new Promise(async (resolve, reject) => { 511 | const user = getters.getUser; 512 | if(!user && !uid) { 513 | return; 514 | } 515 | if(user) { 516 | uid = user.uid 517 | } 518 | try { 519 | let favoritesObj = await firebase.database().ref(`users/${uid}/favorites`).once('value'); 520 | commit('setUserFavorites', favoritesObj.val()); 521 | resolve(); 522 | } catch(e) { 523 | console.error(e); 524 | reject(e); 525 | } 526 | }) 527 | } 528 | } 529 | }); -------------------------------------------------------------------------------- /src/threejs-sculpture/SculptureN.js: -------------------------------------------------------------------------------- 1 | import { BoxGeometry, MeshBasicMaterial, Color, Mesh, } from 'three'; 2 | 3 | import {createPedestalEdges} from './create-pedestal-edges.js' 4 | 5 | import {sculptToThreeJSMaterial, sculptToThreeJSMesh, glslToThreeJSMaterial, glslToThreeJSMesh} from 'shader-park-core'; 6 | import {defaultFragSourceGLSL} from 'shader-park-core'; 7 | 8 | export class Sculpture { 9 | constructor(isGlsl, source, msdfTexture) { 10 | this.uniformsToExclude = { 'sculptureCenter': 0, 'msdf': 0, 'opacity': 0, 'time': 0, 'stepSize': 0, '_scale' : 1, 'resolution': 0}; 11 | this.IsGLSL = isGlsl; 12 | this.payload = { msdfTexture} 13 | this.source = source; 14 | this.compileError = null; 15 | if (isGlsl) { 16 | try { 17 | this.mesh = glslToThreeJSMesh(source, this.payload); 18 | } catch(error) { 19 | console.error(error); 20 | this.mesh = glslToThreeJSMesh(defaultFragSourceGLSL, this.payload); 21 | this.compileError = error; 22 | // throw(e); 23 | } 24 | 25 | } else { 26 | try { 27 | this.mesh = sculptToThreeJSMesh(source, this.payload); 28 | } catch (error) { 29 | console.error(error); 30 | this.mesh = sculptToThreeJSMesh('sphere(0.5);', this.payload); 31 | this.compileError = error; 32 | 33 | } 34 | this.uniforms = this.mesh.material.uniformDescriptions; 35 | this.uniforms = this.uniforms.filter(uniform => !(uniform.name in this.uniformsToExclude)) 36 | } 37 | const pedestalGeom = new BoxGeometry(2, 1, 2); 38 | this.opacity = 0.0; 39 | this.stepSize = 0.8; 40 | const pedestalMat = new MeshBasicMaterial({ color: new Color(0.95, 0.95, 0.95), transparent: true, opacity: this.opacity }); 41 | this.pedestal = new Mesh(pedestalGeom, pedestalMat); 42 | this.sepBuffer = 0.05; // Small gap between sculpture and pedestal prevents z-fighting 43 | this.pedestal.position.set(0, -1.5-this.sepBuffer, 0); 44 | this.mesh.add(this.pedestal); 45 | this.pedestalEdges = createPedestalEdges(2, 1); 46 | this.pedestalEdges.position.set(0, -1.5-this.sepBuffer, 0); 47 | // this.mesh.add(this.pedestalEdges); 48 | this.selected = false; 49 | this.setOpacity(0.0); 50 | } 51 | 52 | setMSDFTexture(texture) { 53 | // this.MSDFTexture = texture; 54 | this.payload.msdfTexture = texture; 55 | // this.refreshMaterial(); 56 | } 57 | 58 | selectedSculpture(selected) { 59 | if(!this.pedestal.visible) { 60 | return; 61 | } 62 | this.mesh.remove(this.pedestalEdges); 63 | if (selected) { 64 | this.pedestalEdges = createPedestalEdges(2, 1, 0.015); 65 | this.pedestalEdges.position.set(0, -1.5-this.sepBuffer, 0); 66 | this.mesh.add(this.pedestalEdges); 67 | } else { 68 | this.pedestalEdges = createPedestalEdges(2, 1); 69 | this.pedestalEdges.position.set(0, -1.5-this.sepBuffer, 0); 70 | this.mesh.add(this.pedestalEdges); 71 | } 72 | this.selected = selected; 73 | } 74 | 75 | setOpacity(value) { 76 | this.opacity = value; 77 | this.mesh.visible = value !== 0.0; 78 | this.pedestal.material.opacity = this.opacity; 79 | } 80 | 81 | refreshMaterial(newSource) { 82 | if (newSource) { 83 | this.source = newSource; 84 | } 85 | if (this.IsGLSL) { 86 | try { 87 | this.mesh.material = glslToThreeJSMaterial(this.source, this.payload); 88 | } catch(e) { 89 | console.error(e); 90 | throw(e) 91 | } 92 | 93 | } else { 94 | try { 95 | this.mesh.material = sculptToThreeJSMaterial(this.source, this.payload); 96 | } catch(e) { 97 | throw(e); 98 | 99 | } 100 | this.uniforms = this.mesh.material.uniformDescriptions; 101 | this.uniforms = this.uniforms.filter(uniform => !(uniform.name in this.uniformsToExclude)) 102 | } 103 | } 104 | 105 | update(uniforms) { 106 | this.mesh.material.uniforms['opacity'].value = this.opacity; 107 | this.mesh.material.uniforms['msdf'].value = this.payload.msdfTexture; 108 | uniforms.forEach(uniform => { 109 | if(uniform && uniform.name) { 110 | this.mesh.material.uniforms[uniform.name].value = uniform.value; 111 | } 112 | }); 113 | } 114 | } 115 | 116 | -------------------------------------------------------------------------------- /src/threejs-sculpture/create-pedestal-edges.js: -------------------------------------------------------------------------------- 1 | import { BoxGeometry, MeshBasicMaterial, Color, Mesh, Group} from 'three'; 2 | 3 | /** 4 | * Creates thin edges for a pedestal given its dimensions 5 | * @param pedSize width and depth of the pedestal. The same value is 6 | * used for both because they are square 7 | * @param pedHeight height of the pedestal 8 | * @todo these could change color/size to represent selection? 9 | * Add ability to set all their colors with a single call 10 | * Could be made into class 11 | */ 12 | 13 | export function createPedestalEdges(pedSize, pedHeight, size = 0.005) { 14 | const edgeSize = pedSize * size; 15 | const longEdgeGeom = 16 | new BoxGeometry(edgeSize, edgeSize, pedSize + 1.0 * edgeSize); 17 | const shortEdgeGeomVertical = 18 | new BoxGeometry(edgeSize, pedHeight, edgeSize); 19 | const shortEdgeGeomHorizontal = 20 | new BoxGeometry(pedSize, edgeSize, edgeSize); 21 | const edgeMat = 22 | new MeshBasicMaterial({color: new Color(1.0, 1.0, 1.0)}); 23 | const edgeGroup = new Group(); 24 | 25 | const longUL = new Mesh(longEdgeGeom, edgeMat); 26 | longUL.position.x -= pedSize * 0.5; 27 | longUL.position.y += pedHeight * 0.5; 28 | edgeGroup.add(longUL); 29 | 30 | const longUR = new Mesh(longEdgeGeom, edgeMat); 31 | longUR.position.x += pedSize * 0.5; 32 | longUR.position.y += pedHeight * 0.5; 33 | edgeGroup.add(longUR); 34 | 35 | const longBL = new Mesh(longEdgeGeom, edgeMat); 36 | longBL.position.x -= pedSize * 0.5; 37 | longBL.position.y -= pedHeight * 0.5; 38 | edgeGroup.add(longBL); 39 | 40 | const longBR = new Mesh(longEdgeGeom, edgeMat); 41 | longBR.position.x += pedSize * 0.5; 42 | longBR.position.y -= pedHeight * 0.5; 43 | edgeGroup.add(longBR); 44 | 45 | const shortUF = new Mesh(shortEdgeGeomHorizontal, edgeMat); 46 | shortUF.position.z += pedSize * 0.5; 47 | shortUF.position.y += pedHeight * 0.5; 48 | edgeGroup.add(shortUF); 49 | 50 | const shortUB = new Mesh(shortEdgeGeomHorizontal, edgeMat); 51 | shortUB.position.z -= pedSize * 0.5; 52 | shortUB.position.y += pedHeight * 0.5; 53 | edgeGroup.add(shortUB); 54 | 55 | const shortLF = new Mesh(shortEdgeGeomVertical, edgeMat); 56 | shortLF.position.x -= pedSize * 0.5; 57 | shortLF.position.z += pedSize * 0.5; 58 | edgeGroup.add(shortLF); 59 | 60 | const shortRF = new Mesh(shortEdgeGeomVertical, edgeMat); 61 | shortRF.position.x += pedSize * 0.5; 62 | shortRF.position.z += pedSize * 0.5; 63 | edgeGroup.add(shortRF); 64 | 65 | const shortLB = new Mesh(shortEdgeGeomVertical, edgeMat); 66 | shortLB.position.x -= pedSize * 0.5; 67 | shortLB.position.z -= pedSize * 0.5; 68 | edgeGroup.add(shortLB); 69 | 70 | const shortRB = new Mesh(shortEdgeGeomVertical, edgeMat); 71 | shortRB.position.x += pedSize * 0.5; 72 | shortRB.position.z -= pedSize * 0.5; 73 | edgeGroup.add(shortRB); 74 | 75 | const shortBF = new Mesh(shortEdgeGeomHorizontal, edgeMat); 76 | shortBF.position.z += pedSize * 0.5; 77 | shortBF.position.y -= pedHeight * 0.5; 78 | edgeGroup.add(shortBF); 79 | 80 | const shortBB = new Mesh(shortEdgeGeomHorizontal, edgeMat); 81 | shortBB.position.z -= pedSize * 0.5; 82 | shortBB.position.y -= pedHeight * 0.5; 83 | edgeGroup.add(shortBB); 84 | 85 | return edgeGroup; 86 | } -------------------------------------------------------------------------------- /vue.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | publicPath: '/', 3 | pwa: { 4 | workboxOptions: { 5 | skipWaiting: true, 6 | clientsClaim: true, 7 | exclude: ['_redirects'], 8 | runtimeCaching: [ 9 | { 10 | // Use the NetworkFirst strategy for HTML files 11 | urlPattern: /index\.html/, 12 | handler: 'NetworkFirst', 13 | options: { 14 | networkTimeoutSeconds: 20, 15 | cacheName: 'html-cache', 16 | }, 17 | }, 18 | { 19 | // Use the CacheFirst strategy for other static assets 20 | urlPattern: /\.(?:js|css|png|jpg|jpeg|svg)$/, 21 | handler: 'CacheFirst', 22 | options: { 23 | cacheName: 'static-assets', 24 | expiration: { 25 | maxEntries: 50, 26 | maxAgeSeconds: 30 * 24 * 60 * 60, // 30 Days 27 | }, 28 | }, 29 | }, 30 | { 31 | // Don't cache requests to Firebase or other real-time APIs 32 | urlPattern: /^https:\/\/(www\.)?firebaseio\.com/, 33 | handler: 'NetworkOnly', 34 | }, 35 | ], 36 | }, 37 | }, 38 | configureWebpack: { 39 | resolve: { 40 | mainFields: ['module', 'browser', 'main'] 41 | } 42 | } 43 | }; 44 | --------------------------------------------------------------------------------