├── .circleci
└── config.yml
├── .eslintignore
├── .eslintrc.js
├── .github
├── ISSUE_TEMPLATE
│ ├── bug_report.md
│ └── feature_request.md
└── pull_request_template.md
├── .gitignore
├── .nvmrc
├── CHANGELOG.md
├── LICENSE.md
├── README.md
├── babel.config.js
├── build
└── index.js
├── demo
└── src
│ ├── App.js
│ └── index.js
├── docs
├── 52f4fcf052d40d121e7ad596cd758164.jpg
├── 813d08cc533261b5422bbac3b67deb19.jpg
├── index.bundle.js
└── index.html
├── jest.config.js
├── jest
├── __mocks__
│ └── file-mock.js
├── preprocess.js
└── setup.js
├── package-lock.json
├── package.json
├── public
├── assets
│ └── images
│ │ └── screenshot.png
├── favicon.ico
└── index.html
├── scripts
├── deploy_demo.sh
├── getPackageVersion.js
├── publish.sh
├── tag_exists.sh
└── tag_release.sh
├── server
├── jormungandr-config.yaml
├── package.json
├── restart.sh
└── server.js
├── src
├── assets
│ ├── globeBW.jpg
│ └── globeBWe.jpg
├── components
│ ├── Config.js
│ ├── Main.css
│ ├── Main.js
│ └── classes
│ │ ├── AmbientLightClass.js
│ │ ├── BaseClass.js
│ │ ├── CameraClass.js
│ │ ├── ControlsClass.js
│ │ ├── FBOClass.js
│ │ ├── GlobeClass.js
│ │ ├── GlobeSceneClass.js
│ │ ├── IcosaSceneClass.js
│ │ ├── IcosahedronClass.js
│ │ ├── MarkersClass.js
│ │ ├── MouseClass.js
│ │ ├── ParticlesClass.js
│ │ ├── PathsClass.js
│ │ ├── PickerSceneClass.js
│ │ ├── PickersClass.js
│ │ ├── PointLightClass.js
│ │ ├── QuadCameraClass.js
│ │ ├── RendererClass.js
│ │ └── TouchClass.js
├── data
│ └── test.js
├── helpers
│ ├── TextureHelper.js
│ ├── math.js
│ └── utility.js
├── index.js
├── libs
│ ├── Detector.js
│ └── post
│ │ ├── CopyShader.js
│ │ ├── EffectComposer.js
│ │ └── Vignette.js
├── post
│ ├── BlendLighten.js
│ ├── BrightnessContrast.js
│ ├── CopyShader.js
│ ├── EffectComposer.js
│ ├── FXAAShader.js
│ ├── Film.js
│ └── Vignette.js
└── shaders
│ ├── applyQuaternionToVector.glsl
│ ├── blur.frag
│ ├── curlNoise.glsl
│ ├── edgeDetect.frag
│ ├── empty.frag
│ ├── markers.frag
│ ├── markers.vert
│ ├── mousePos.frag
│ ├── particles.frag
│ ├── particles.vert
│ ├── passThrough.frag
│ ├── passThrough.vert
│ ├── paths.frag
│ ├── paths.vert
│ ├── pickers.frag
│ ├── pickers.vert
│ ├── position.frag
│ └── rotationMatrix.glsl
├── webpack.prod.config.js
├── webpack_demo.config.js
└── webpack_demo.prod.config.js
/.circleci/config.yml:
--------------------------------------------------------------------------------
1 | version: 2.1
2 | executors:
3 | main:
4 | docker:
5 | - image: 'circleci/node:10'
6 |
7 | jobs:
8 | install_dependencies:
9 | executor: main
10 | steps:
11 | - checkout
12 | - restore_cache:
13 | keys:
14 | - node-v1-{{ .Branch }}-{{ checksum "package-lock.json" }}
15 | - node-v1-{{ .Branch }}-
16 | - node-v1-
17 | - run: npm ci
18 | - save_cache:
19 | paths:
20 | - ./node_modules
21 | key: node-v1-{{ .Branch }}-{{ checksum "package-lock.json" }}
22 | build:
23 | executor: main
24 | steps:
25 | - checkout
26 | - restore_cache:
27 | keys:
28 | - node-v1-{{ .Branch }}-{{ checksum "package-lock.json" }}
29 | - run:
30 | name: Building src
31 | command: npm run build
32 | - persist_to_workspace:
33 | root: build
34 | paths:
35 | - "*"
36 | deploy:
37 | executor: main
38 | steps:
39 | - checkout
40 | - restore_cache:
41 | keys:
42 | - node-v1-{{ .Branch }}-{{ checksum "package-lock.json" }}
43 | - run:
44 | name: Publish to NPM
45 | command: ./scripts/publish.sh
46 | release:
47 | executor: main
48 | steps:
49 | - checkout
50 | - add_ssh_keys:
51 | fingerprints:
52 | - "3c:17:52:8b:7b:72:62:10:86:bf:ec:ab:fc:63:7b:0e"
53 | - run:
54 | name: Setup git config
55 | command: |
56 | git config user.email "$GIT_EMAIL"
57 | git config user.name "$GIT_USERNAME"
58 | - run:
59 | name: Tag release
60 | command: ./scripts/tag_release.sh
61 | test:
62 | executor: main
63 | steps:
64 | - checkout
65 | - restore_cache:
66 | keys:
67 | - node-v1-{{ .Branch }}-{{ checksum "package-lock.json" }}
68 | - run:
69 | name: Linting javascript
70 | command: npm run lint
71 | build_demo:
72 | executor: main
73 | steps:
74 | - checkout
75 | - restore_cache:
76 | keys:
77 | - node-v1-{{ .Branch }}-{{ checksum "package-lock.json" }}
78 | - attach_workspace:
79 | at: build
80 | - run:
81 | name: Building demo
82 | command: npm run build:demo
83 | - persist_to_workspace:
84 | root: public/build
85 | paths:
86 | - "*"
87 | deploy_demo:
88 | executor: main
89 | steps:
90 | - checkout
91 | - restore_cache:
92 | keys:
93 | - node-v1-{{ .Branch }}-{{ checksum "package-lock.json" }}
94 | - attach_workspace:
95 | at: public/build
96 | - add_ssh_keys:
97 | fingerprints:
98 | - "3c:17:52:8b:7b:72:62:10:86:bf:ec:ab:fc:63:7b:0e"
99 | - run:
100 | name: Setup git config
101 | command: |
102 | git config user.email "$GIT_EMAIL"
103 | git config user.name "$GIT_USERNAME"
104 | - run:
105 | name: Deploying to GitHub pages
106 | command: npm run deploy:demo
107 |
108 | workflows:
109 | version: 2
110 | main:
111 | jobs:
112 | - install_dependencies
113 | - test:
114 | requires:
115 | - install_dependencies
116 | - build:
117 | requires:
118 | - install_dependencies
119 | - build_demo:
120 | requires:
121 | - build
122 | - deploy:
123 | requires:
124 | - build
125 | filters:
126 | branches:
127 | only: master
128 | - release:
129 | requires:
130 | - deploy
131 | filters:
132 | branches:
133 | only: master
134 | - deploy_demo:
135 | requires:
136 | - build_demo
137 | filters:
138 | branches:
139 | only: master
140 |
--------------------------------------------------------------------------------
/.eslintignore:
--------------------------------------------------------------------------------
1 | webpack*.js
2 | scripts/*
3 | jest/*
4 |
--------------------------------------------------------------------------------
/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | parser: 'babel-eslint', // Specifies the ESLint parser
3 | extends: [
4 | 'standard',
5 | 'eslint:recommended',
6 | 'plugin:import/errors',
7 | 'plugin:import/warnings',
8 | 'plugin:react/recommended' // Uses the recommended rules from @eslint-plugin-react
9 | ],
10 | parserOptions: {
11 | ecmaVersion: 2018,
12 | sourceType: 'module', // Allows for the use of imports
13 | ecmaFeatures: {
14 | jsx: true, // Allows for the parsing of JSX
15 | }
16 | },
17 | rules: {
18 | indent: 'off',
19 | 'import/no-unresolved': 'off',
20 | 'import/named': 'error',
21 | 'import/namespace': 'error',
22 | 'import/default': 'error',
23 | 'import/export': 'error'
24 | // Place to specify ESLint rules. Can be used to overwrite rules specified from the extended configs
25 | // e.g. "@typescript-eslint/explicit-function-return-type": "off",
26 | },
27 | settings: {
28 | react: {
29 | version: 'detect', // Tells eslint-plugin-react to automatically detect the version of React to use
30 | },
31 | 'import/ignore': [ 'node_modules/*' ]
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug_report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Bug report
3 | about: Create a report to help us improve
4 | title: ''
5 | labels: ''
6 | assignees: ''
7 |
8 | ---
9 |
10 | **Describe the bug**
11 | A clear and concise description of what the bug is.
12 |
13 | **To Reproduce**
14 | Steps to reproduce the behavior:
15 | 1. Go to '...'
16 | 2. Click on '....'
17 | 3. Scroll down to '....'
18 | 4. See error
19 |
20 | **Expected behavior**
21 | A clear and concise description of what you expected to happen.
22 |
23 | **Screenshots**
24 | If applicable, add screenshots to help explain your problem.
25 |
26 | **Desktop (please complete the following information):**
27 | - OS: [e.g. iOS]
28 | - Browser [e.g. chrome, safari]
29 | - Version [e.g. 22]
30 |
31 | **Smartphone (please complete the following information):**
32 | - Device: [e.g. iPhone6]
33 | - OS: [e.g. iOS8.1]
34 | - Browser [e.g. stock browser, safari]
35 | - Version [e.g. 22]
36 |
37 | **Additional context**
38 | Add any other context about the problem here.
39 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature_request.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Feature request
3 | about: Suggest an idea for this project
4 | title: ''
5 | labels: ''
6 | assignees: ''
7 |
8 | ---
9 |
10 | **Is your feature request related to a problem? Please describe.**
11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
12 |
13 | **Describe the solution you'd like**
14 | A clear and concise description of what you want to happen.
15 |
16 | **Describe alternatives you've considered**
17 | A clear and concise description of any alternative solutions or features you've considered.
18 |
19 | **Additional context**
20 | Add any other context or screenshots about the feature request here.
21 |
--------------------------------------------------------------------------------
/.github/pull_request_template.md:
--------------------------------------------------------------------------------
1 | * **Please check if the PR fulfills these requirements**
2 | - [ ] Commit messages are concise and descriptive
3 | - [ ] Tests for the changes have been added (for bug fixes / features)
4 | - [ ] Docs have been added / updated (for bug fixes / features)
5 | - [ ] Demos have been added / updated (for bug fixes / features)
6 | - [ ] Removed any tech debt where applicable
7 |
8 |
9 | * **What kind of change does this PR introduce?** (Bug fix, feature, docs update, ...)
10 |
11 |
12 |
13 | * **What is the current behavior?** (You can also link to an open issue here)
14 |
15 |
16 |
17 | * **What is the new behavior (if this is a feature change)?**
18 |
19 |
20 |
21 | * **Does this PR introduce a breaking change?** (What changes might users need to make in their application due to this PR?)
22 |
23 |
24 |
25 | * **Other information**:
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 |
8 | # Runtime data
9 | pids
10 | *.pid
11 | *.seed
12 | *.pid.lock
13 |
14 | # Directory for instrumented libs generated by jscoverage/JSCover
15 | lib-cov
16 |
17 | # Coverage directory used by tools like istanbul
18 | coverage
19 |
20 | # nyc test coverage
21 | .nyc_output
22 |
23 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
24 | .grunt
25 |
26 | # Bower dependency directory (https://bower.io/)
27 | bower_components
28 |
29 | # node-waf configuration
30 | .lock-wscript
31 |
32 | # Compiled binary addons (http://nodejs.org/api/addons.html)
33 | build/Release
34 |
35 | # Dependency directories
36 | node_modules/
37 | jspm_packages/
38 |
39 | # Typescript v1 declaration files
40 | typings/
41 |
42 | # Optional npm cache directory
43 | .npm
44 |
45 | # Optional eslint cache
46 | .eslintcache
47 |
48 | # Optional REPL history
49 | .node_repl_history
50 |
51 | # Output of 'npm pack'
52 | *.tgz
53 |
54 | # dotenv environment variables file
55 | .env
56 |
57 | # Mac files
58 | .DS_Store
59 |
60 | # Yarn
61 | yarn-error.log
62 | .pnp/
63 | .pnp.js
64 | yarn.lock
65 | # Yarn Integrity file
66 | .yarn-integrity
67 |
68 | .vscode
69 |
70 | # Build
71 | .cache/
72 |
73 | # firebase auth
74 | server/auth
--------------------------------------------------------------------------------
/.nvmrc:
--------------------------------------------------------------------------------
1 | 10
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Changelog
2 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019 Scott Darby
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 |
2 | Cardano Shelley Node Map
3 |
4 |
5 | 
6 |
7 |
--------------------------------------------------------------------------------
/babel.config.js:
--------------------------------------------------------------------------------
1 | const presets = [
2 | '@babel/env',
3 | '@babel/preset-react'
4 | ]
5 |
6 | module.exports = {
7 | presets
8 | }
9 |
--------------------------------------------------------------------------------
/demo/src/App.js:
--------------------------------------------------------------------------------
1 |
2 | import React, { Component } from 'react'
3 | import Main from '../../src/components/Main'
4 |
5 | class App extends Component {
6 | render () {
7 | return (
8 | // es-lint
9 | )
10 | }
11 | }
12 |
13 | export default App
14 |
--------------------------------------------------------------------------------
/demo/src/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import ReactDOM from 'react-dom'
3 | import App from './App'
4 |
5 | ReactDOM.render((
6 |
7 | ), document.getElementById('root'))
8 |
--------------------------------------------------------------------------------
/docs/52f4fcf052d40d121e7ad596cd758164.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/input-output-hk/shelley-node-map/069e9e464f422530a998090abe57ab6f44c283e2/docs/52f4fcf052d40d121e7ad596cd758164.jpg
--------------------------------------------------------------------------------
/docs/813d08cc533261b5422bbac3b67deb19.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/input-output-hk/shelley-node-map/069e9e464f422530a998090abe57ab6f44c283e2/docs/813d08cc533261b5422bbac3b67deb19.jpg
--------------------------------------------------------------------------------
/docs/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Shelley Node Map
6 |
7 |
8 |
9 |
10 |
11 |
39 |
40 |
41 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/jest.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | transform: {
3 | '^.+\\.(t|j)sx?$': `/jest/preprocess.js`
4 | },
5 | moduleNameMapper: {
6 | '.+\\.(css|styl|less|sass|scss)$': `identity-obj-proxy`,
7 | '.+\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$': `/jest/__mocks__/file-mock.js`
8 | },
9 | testPathIgnorePatterns: [`node_modules`, `.cache`],
10 | transformIgnorePatterns: [`node_modules/(?!(gatsby)/)`],
11 | globals: {
12 | __PATH_PREFIX__: ``
13 | },
14 | testURL: 'https://www.something.com/',
15 | setupFilesAfterEnv: ['/jest/setup.js'],
16 | snapshotSerializers: ['enzyme-to-json/serializer']
17 | }
18 |
--------------------------------------------------------------------------------
/jest/__mocks__/file-mock.js:
--------------------------------------------------------------------------------
1 | module.exports = 'test-file-stub'
2 |
--------------------------------------------------------------------------------
/jest/preprocess.js:
--------------------------------------------------------------------------------
1 | const babelConfig = require('../babel.config.js')
2 | module.exports = require('babel-jest').createTransformer(babelConfig)
3 |
--------------------------------------------------------------------------------
/jest/setup.js:
--------------------------------------------------------------------------------
1 | import Enzyme from 'enzyme'
2 | import Adapter from 'enzyme-adapter-react-16'
3 | import 'jest-styled-components'
4 |
5 | Enzyme.configure({ adapter: new Adapter() })
6 |
7 | global.windowEventListeners = {}
8 | global.triggerWindowEvent = (name, event) => global.windowEventListeners[name].forEach(listener => listener(event))
9 |
10 | global.window = window
11 | global.window.innerWidth = 1920
12 | global.window.addEventListener = (name, listener) => {
13 | windowEventListeners[name] = windowEventListeners[name] || []
14 | if (windowEventListeners[name].includes(listener)) return
15 | windowEventListeners[name].push(listener)
16 | }
17 | global.window.removeEventListener = (name, listener) => {
18 | if (!windowEventListeners[name]) return
19 | windowEventListeners[name] = windowEventListeners[name].splice(windowEventListeners[name].indexOf(listener), 1)
20 | if (windowEventListeners[name].length < 1) delete windowEventListeners[name]
21 | }
22 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "shelley-node-map",
3 | "version": "0.0.1",
4 | "description": "Shelley Node Map",
5 | "main": "build/index.js",
6 | "files": [
7 | "/build",
8 | "/src"
9 | ],
10 | "repository": {
11 | "type": "git"
12 | },
13 | "browserify": {
14 | "transform": [
15 | "glslify"
16 | ]
17 | },
18 | "scripts": {
19 | "build": "npm run build:js",
20 | "build:demo": "webpack --config webpack_demo.prod.config.js",
21 | "build:js": "webpack --config webpack.prod.config.js",
22 | "deploy:demo": "./scripts/deploy_demo.sh",
23 | "lint": "eslint src/**/*.js demo/src/**/*.js",
24 | "prepublish": "npm run build",
25 | "prepublishOnly": "npm run lint",
26 | "dev:demo": "webpack-dev-server --config webpack_demo.config.js --open",
27 | "watch:js": "npm run build:js -- --watch"
28 | },
29 | "peerDependencies": {
30 | "react": "^16.8"
31 | },
32 | "devDependencies": {
33 | "@babel/cli": "^7.4.4",
34 | "@babel/core": "^7.4.5",
35 | "@babel/preset-env": "^7.4.5",
36 | "@babel/preset-react": "^7.0.0",
37 | "@tweenjs/tween.js": "^17.4.0",
38 | "@types/jest": "^24.0.15",
39 | "@types/react": "^16.8.22",
40 | "@types/styled-components": "^4.1.16",
41 | "babel-eslint": "^10.0.2",
42 | "babel-jest": "^24.8.0",
43 | "babel-loader": "^8.0.6",
44 | "babel-plugin-react-css-modules": "^5.2.6",
45 | "babel-plugin-styled-components": "^1.10.0",
46 | "coveralls": "^3.0.4",
47 | "css-loader": "^3.0.0",
48 | "dat.gui": "^0.7.6",
49 | "enzyme": "^3.9.0",
50 | "enzyme-adapter-react-16": "^1.12.1",
51 | "enzyme-to-json": "^3.3.5",
52 | "eslint": "^5.16.0",
53 | "eslint-config-standard": "^12.0.0",
54 | "eslint-plugin-import": "^2.18.0",
55 | "eslint-plugin-node": "^9.1.0",
56 | "eslint-plugin-promise": "^4.2.1",
57 | "eslint-plugin-react": "^7.14.2",
58 | "eslint-plugin-standard": "^4.0.0",
59 | "file-loader": "^4.2.0",
60 | "gh-pages": "^2.0.1",
61 | "html-loader": "^0.5.5",
62 | "html-webpack-plugin": "^3.2.0",
63 | "identity-obj-proxy": "^3.0.0",
64 | "jest": "^24.8.0",
65 | "jest-styled-components": "^6.3.3",
66 | "markdown-loader": "^5.0.0",
67 | "node-sass": "^4.12.0",
68 | "prop-types": "^15.7.2",
69 | "react": "^16.8.6",
70 | "react-dom": "^16.8.6",
71 | "sass-loader": "^7.1.0",
72 | "standard": "^12.0.1",
73 | "style-loader": "^0.23.1",
74 | "styled-components": "^4.3.2",
75 | "three": "^0.112.1",
76 | "three-orbitcontrols": "^2.102.2",
77 | "url-loader": "^2.2.0",
78 | "webpack": "^4.35.2",
79 | "webpack-cli": "^3.3.5",
80 | "webpack-dev-server": "^3.8.0"
81 | },
82 | "author": "Scott Darby ",
83 | "license": "MIT",
84 | "dependencies": {
85 | "@input-output-hk/react-preloader": "^1.0.5",
86 | "d3-geo": "^1.11.9",
87 | "deep-assign": "^3.0.0",
88 | "firebase": "^7.5.0",
89 | "glsl-blend": "^1.0.3",
90 | "glsl-edge-detection": "^1.1.0",
91 | "glsl-noise": "^0.0.0",
92 | "glslify-loader": "^2.0.0",
93 | "mixin": "^0.2.0",
94 | "raw-loader": "^3.1.0",
95 | "tween.js": "^16.6.0"
96 | }
97 | }
98 |
--------------------------------------------------------------------------------
/public/assets/images/screenshot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/input-output-hk/shelley-node-map/069e9e464f422530a998090abe57ab6f44c283e2/public/assets/images/screenshot.png
--------------------------------------------------------------------------------
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/input-output-hk/shelley-node-map/069e9e464f422530a998090abe57ab6f44c283e2/public/favicon.ico
--------------------------------------------------------------------------------
/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Shelley Node Map
6 |
7 |
8 |
9 |
10 |
11 |
39 |
40 |
41 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/scripts/deploy_demo.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | RED='\033[0;31m'
4 | NC='\033[0m'
5 | if [ "$CI" == "true" ]; then
6 | echo "On CI, continuing with deployment"
7 | ./node_modules/.bin/gh-pages -d public -b gh-pages -m "[skip ci] Updated demo"
8 | else
9 | echo -e "${RED}---------------------------------"
10 | echo "------------ ERROR ------------"
11 | echo "---------------------------------"
12 | echo ""
13 | echo "Can only run deploy script on CI"
14 | echo ""
15 | echo -e "---------------------------------${NC}"
16 | exit 1
17 | fi
18 |
--------------------------------------------------------------------------------
/scripts/getPackageVersion.js:
--------------------------------------------------------------------------------
1 | const package = require('../package.json')
2 | console.log(package.version)
3 |
--------------------------------------------------------------------------------
/scripts/publish.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | if [ "$CI" != "true" ]; then
4 | echo ""
5 | echo "Can only use the publish script on CI"
6 | echo ""
7 | exit 1
8 | fi
9 |
10 | PACKAGE_VERSION=$(node ./scripts/getPackageVersion.js)
11 | TAG_EXISTS=$(./scripts/tag_exists.sh v$PACKAGE_VERSION)
12 | if [[ $TAG_EXISTS == "false" ]]; then
13 | echo '//registry.npmjs.org/:_authToken=${NPM_TOKEN}' > ./.npmrc
14 | npm publish --access public
15 | fi
16 |
--------------------------------------------------------------------------------
/scripts/tag_exists.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | TAG_FOUND=$(git tag | grep "$1")
4 | if [[ $TAG_FOUND == $1 ]]; then
5 | echo "true"
6 | else
7 | echo "false"
8 | fi
9 |
--------------------------------------------------------------------------------
/scripts/tag_release.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | if [ "$CI" != "true" ]; then
4 | echo ""
5 | echo "Can only use the tag release script on CI"
6 | echo ""
7 | exit 1
8 | fi
9 |
10 | PACKAGE_VERSION=$(node ./scripts/getPackageVersion.js)
11 | TAG_EXISTS=$(./scripts/tag_exists.sh v$PACKAGE_VERSION)
12 | if [[ $TAG_EXISTS == "false" ]]; then
13 | git tag v$PACKAGE_VERSION
14 | git push origin --tags
15 | fi
16 |
--------------------------------------------------------------------------------
/server/jormungandr-config.yaml:
--------------------------------------------------------------------------------
1 | {
2 | "log": [
3 | {
4 | "format": "json",
5 | "level": "info",
6 | "output": "stdout"
7 | }
8 | ],
9 | "p2p": {
10 | "topics_of_interest": {
11 | "blocks": "high",
12 | "messages": "low"
13 | },
14 | "trusted_peers": [
15 | {
16 | "address": "/ip4/13.230.137.72/tcp/3000",
17 | "id": "fe3332044877b2034c8632a08f08ee47f3fbea6c64165b3b"
18 | },
19 | {
20 | "address": "/ip4/13.230.48.191/tcp/3000",
21 | "id": "c38aabb936944776ef15bbe4b5b02454c46a8a80d871f873"
22 | },
23 | {
24 | "address": "/ip4/18.196.168.220/tcp/3000",
25 | "id": "7e2222179e4f3622b31037ede70949d232536fdc244ca3d9"
26 | },
27 | {
28 | "address": "/ip4/3.124.132.123/tcp/3000",
29 | "id": "9085fa5caeb39eace748a7613438bd2a62c8c8ee00040b71"
30 | },
31 | {
32 | "address": "/ip4/18.184.181.30/tcp/3000",
33 | "id": "f131b71d65c49116f3c23c8f1dd7ceaa98f5962979133404"
34 | },
35 | {
36 | "address": "/ip4/184.169.162.15/tcp/3000",
37 | "id": "fdb88d08c7c759b5d30e854492cb96f8203c2d875f6f3e00"
38 | },
39 | {
40 | "address": "/ip4/52.52.67.33/tcp/3000",
41 | "id": "3d1f8891bf53eb2946a18fb46cf99309649f0163b4f71b34"
42 | }
43 | ]
44 | },
45 | "rest": {
46 | "listen": "127.0.0.1:3100"
47 | },
48 | "storage": "./storage"
49 | }
--------------------------------------------------------------------------------
/server/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "shelley-node-map-server",
3 | "version": "0.0.1",
4 | "description": "Parse Cardano Shelley node log file and update firebase",
5 | "author": "Scott Darby ",
6 | "license": "ISC",
7 | "scripts": {
8 | "server": "nodemon server.js",
9 | "sim": "nodemon sim.js"
10 | },
11 | "dependencies": {
12 | "express": "^4.16.2",
13 | "firebase-admin": "8.8.0",
14 | "node-fetch": "^2.2.0",
15 | "node-watch": "^0.6.3",
16 | "nodemon": "^2.0.1",
17 | "read-last-lines": "^1.7.1",
18 | "ws": "^7.2.0"
19 | },
20 | "devDependencies": {
21 | "concurrently": "^4.0.1",
22 | "standard": "^11.0.1"
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/server/restart.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | # kill and restart jormungandr node (run on cron to ensure logs don't get too large and we get a new selection of peers)
3 |
4 | HOME_DIR="/home/scott/"
5 | SELF_NODE_DIR="/home/scott/self-node/"
6 |
7 | killall -q "jormungandr"
8 | rm ${SELF_NODE_DIR}blocks.log
9 | ${HOME_DIR}.cargo/bin/jormungandr --config ${SELF_NODE_DIR}config.yaml --genesis-block-hash 65a9b15f82619fffd5a7571fdbf973a18480e9acf1d2fddeb606ebb53ecca839 --secret ${SELF_NODE_DIR}pool-secret1.yaml | grep --line-buffered -i "block_events" | tee -a ${SELF_NODE_DIR}blocks.log
--------------------------------------------------------------------------------
/server/server.js:
--------------------------------------------------------------------------------
1 | const readLastLines = require('read-last-lines')
2 | const watch = require('node-watch')
3 | // const express = require('express')
4 | const fetch = require('node-fetch')
5 |
6 | // const app = express()
7 | // const port = process.env.PORT || 5000
8 |
9 | const config = {
10 | FBFilename: 'webgl-gource-1da99-firebase-adminsdk-2gak7-c2e824de64.json',
11 | collection: 'shelley-node-log'
12 | }
13 |
14 | // firebase
15 | const admin = require('firebase-admin')
16 | const serviceAccount = require('./auth/' + config.FBFilename)
17 |
18 | admin.initializeApp({
19 | credential: admin.credential.cert(serviceAccount)
20 | })
21 |
22 | const firebaseDB = admin.firestore()
23 |
24 | const filePath = '/home/scott/self-node/blocks.log'
25 | const geoLocationURL = 'https://api.ipdata.co/'
26 | const geoLocationConfig = require('./auth/ipdata.json')
27 |
28 | // app.get('/test', (req, res) => {
29 | watch(filePath, { recursive: true }, function (evt, name) {
30 | readLastLines.read(filePath, 1)
31 | .then(async (lines) => {
32 | try {
33 | const msg = JSON.parse(lines)
34 | if (typeof msg.peer_addr !== 'undefined') {
35 | let url = new URL('http://' + msg.peer_addr)
36 | url.port = ''
37 |
38 | const ipAddress = url.host
39 |
40 | // check if geodata is in db
41 | let docRef = firebaseDB.collection(config.collection).doc(ipAddress)
42 | let snapshot = await docRef.get().catch(err => {
43 | console.log(err)
44 | })
45 |
46 | if (!snapshot.exists) {
47 | // get lat/long for ip
48 | fetch(geoLocationURL + url.host + '?api-key=' + geoLocationConfig.APIKey, {
49 | method: 'GET'
50 | })
51 | .then(res => res.text())
52 | .then((body) => {
53 | let geoData = JSON.parse(body)
54 |
55 | if (typeof geoData.latitude !== 'undefined') {
56 | // save geolocation in db
57 | let saveData = {
58 | ip: ipAddress,
59 | lat: geoData.latitude,
60 | long: geoData.longitude,
61 | region: geoData.region,
62 | country: geoData.country_name,
63 | city: geoData.city,
64 | timestamp: admin.firestore.Timestamp.fromDate(new Date())
65 | }
66 |
67 | docRef.set(saveData, { merge: true }).then(() => {
68 | console.log('Geolocation data saved for IP: ' + ipAddress)
69 | })
70 | } else {
71 | let saveData = {
72 | ip: ipAddress,
73 | timestamp: admin.firestore.Timestamp.fromDate(new Date())
74 | }
75 |
76 | docRef.set(saveData, { merge: true }).then(() => {
77 | console.log('Could not get geolocation data for: ' + ipAddress)
78 | })
79 | }
80 | })
81 | .catch(error => console.error(error))
82 | } else {
83 | // just store updated time
84 | let saveData = {
85 | timestamp: admin.firestore.Timestamp.fromDate(new Date())
86 | }
87 |
88 | docRef.set(saveData, { merge: true }).then(() => {
89 | console.log('Updated node timestamp for IP: ' + ipAddress)
90 | })
91 | .catch(error => {
92 | console.log(error)
93 | })
94 | }
95 | }
96 | } catch (error) {
97 | console.log(error)
98 | }
99 | })
100 | })
101 |
102 | // res.send({ express: 'Check console' })
103 | // })
104 |
105 | // app.listen(port, () => console.log(`Listening on port ${port}`))
106 |
--------------------------------------------------------------------------------
/src/assets/globeBW.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/input-output-hk/shelley-node-map/069e9e464f422530a998090abe57ab6f44c283e2/src/assets/globeBW.jpg
--------------------------------------------------------------------------------
/src/assets/globeBWe.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/input-output-hk/shelley-node-map/069e9e464f422530a998090abe57ab6f44c283e2/src/assets/globeBWe.jpg
--------------------------------------------------------------------------------
/src/components/Config.js:
--------------------------------------------------------------------------------
1 | import {
2 | HalfFloatType,
3 | FloatType,
4 | Color
5 | } from 'three'
6 |
7 | import Detector from '../libs/Detector'
8 |
9 | class Config {
10 | constructor () {
11 | if (!Config.instance) {
12 | this.init()
13 | Config.instance = this
14 | }
15 |
16 | return Config.instance
17 | }
18 |
19 | init () {
20 | this.data = {
21 | curveMinAltitude: 0,
22 | curveMaxAltitude: 1.8,
23 | curveSegments: 32,
24 | particleScene: {
25 | width: 2100,
26 | height: 2100,
27 | downScaleFactor: Detector.isMobile ? 0.4 : 0.5
28 | },
29 | scene: {
30 | lowBandwidth: false,
31 | fullScreen: true,
32 | width: window.innerWidth,
33 | height: window.innerHeight,
34 | bgColor: new Color(0x000000),
35 | canvasID: 'stage', // ID of webgl canvas element
36 | ambientLightColor: 0xffffff,
37 | ambientLightIntensity: 1.0,
38 | sphereRadius: 2,
39 | globeRadius: 2.1,
40 | particleLifeMax: 1000,
41 | showAnnotations: true
42 | },
43 | post: {
44 | enabled: false,
45 | vignette: true,
46 | blendLighten: true,
47 | tranparentBackground: false,
48 | blendColor: new Color(0x000000) // 121326
49 | },
50 | camera: {
51 | fov: 60,
52 | initPos: { x: 0, y: 0, z: 7 },
53 | near: 0.1,
54 | far: 20,
55 | enableZoom: true // enable camera zoom on mousewheel/pinch gesture
56 | },
57 | dev: {
58 | debugPicker: false
59 | },
60 | fireBase: {
61 | collection: 'shelley-node-log-ITN',
62 | apiKey: 'AIzaSyCwfdzrjQ5GRqyz-napBM29T7Zel_6KIUY',
63 | authDomain: 'webgl-gource-1da99.firebaseapp.com',
64 | databaseURL: 'https://webgl-gource-1da99.firebaseio.com',
65 | projectId: 'webgl-gource-1da99',
66 | storageBucket: 'webgl-gource-1da99.appspot.com',
67 | messagingSenderId: '532264380396'
68 | },
69 | detector: Detector,
70 | floatType: Detector.isIOS ? HalfFloatType : FloatType
71 | }
72 |
73 | this.data.particleScene.width *= this.data.particleScene.downScaleFactor
74 | this.data.particleScene.height *= this.data.particleScene.downScaleFactor
75 | }
76 |
77 | get (id) {
78 | return this.data[id]
79 | }
80 | }
81 |
82 | const instance = new Config()
83 | Object.freeze(instance)
84 |
85 | export default Config
86 |
--------------------------------------------------------------------------------
/src/components/Main.css:
--------------------------------------------------------------------------------
1 | :local(.container) {
2 | font-family: 'Open Sans', sans-serif;
3 | height: 100vh;
4 | display: flex;
5 | align-items: center;
6 | justify-content: center;
7 | }
8 |
9 | :local(.tooltip) {
10 | text-transform: uppercase;
11 | font-size: 12px;
12 | text-shadow: 0px 0px 3px #000;
13 | position: fixed;
14 | top: 0;
15 | left: 0;
16 | color: #fff;
17 | opacity: 1.0;
18 | transition: opacity 0.3s;
19 | padding-left: 5px;
20 | margin-top: -5px;
21 | pointer-events: none;
22 | -webkit-touch-callout: none;
23 | -webkit-user-select: none;
24 | -ms-user-select: none;
25 | user-select: none;
26 | visibility: visible;
27 | }
28 |
29 | :local(.tooltipHide) {
30 | transition: opacity 0.3s, visibility 0.3s;
31 | visibility: hidden;
32 | composes: tooltip;
33 | opacity: 0.0;
34 | }
35 |
36 |
--------------------------------------------------------------------------------
/src/components/Main.js:
--------------------------------------------------------------------------------
1 | /* ------------------------------------------
2 | 3rd Party
3 | ------------------------------------------ */
4 | import React, { Component } from 'react'
5 | import {
6 | Clock,
7 | Vector2,
8 | Color
9 | } from 'three'
10 |
11 | import EventEmitter from 'eventemitter3'
12 | import mixin from 'mixin'
13 | import TWEEN from 'tween.js'
14 | import firebase from 'firebase/app'
15 | import 'firebase/firestore'
16 | import 'firebase/auth'
17 | import { getUrlParameter } from '../helpers/utility'
18 |
19 | /* ------------------------------------------
20 | Config
21 | ------------------------------------------ */
22 | import Config from './Config'
23 |
24 | /* ------------------------------------------
25 | Classes
26 | ------------------------------------------ */
27 | import RendererClass from './classes/RendererClass'
28 | import GlobeSceneClass from './classes/GlobeSceneClass'
29 | import IcosaSceneClass from './classes/IcosaSceneClass'
30 | import PickerSceneClass from './classes/PickerSceneClass'
31 | import FBOClass from './classes/FBOClass'
32 | import CameraClass from './classes/CameraClass'
33 | import ControlsClass from './classes/ControlsClass'
34 | import MouseClass from './classes/MouseClass'
35 | import TouchClass from './classes/TouchClass'
36 | import IcosahedronClass from './classes/IcosahedronClass'
37 | import AmbientLightClass from './classes/AmbientLightClass'
38 | import PointLightClass from './classes/PointLightClass'
39 | import ParticlesClass from './classes/ParticlesClass'
40 | import GlobeClass from './classes/GlobeClass'
41 | import MarkersClass from './classes/MarkersClass'
42 | import PickersClass from './classes/PickersClass'
43 | import PathsClass from './classes/PathsClass'
44 |
45 | /* ------------------------------------------
46 | Styles
47 | ------------------------------------------ */
48 | import styles from './Main.css'
49 |
50 | class Main extends mixin(EventEmitter, Component) {
51 | constructor (props) {
52 | super(props)
53 |
54 | this.config = new Config().data
55 | this.clock = new Clock()
56 | this.modifiedQueue = []
57 | this.processingQueue = false
58 | this.data = []
59 |
60 | this.state = {
61 | tooltipPos: new Vector2(),
62 | tooltipCountry: null,
63 | tooltipCity: null,
64 | tooltipHide: true,
65 | loading: true
66 | }
67 | }
68 |
69 | componentDidMount () {
70 | this.initStage()
71 | }
72 |
73 | initFireBase () {
74 | return new Promise((resolve, reject) => {
75 | try {
76 | firebase.initializeApp(this.config.fireBase)
77 | firebase.firestore()
78 | this.firebaseDB = firebase.firestore()
79 | } catch (error) {
80 | console.log(error)
81 | }
82 | this.docRef = this.firebaseDB.collection(this.config.fireBase.collection)
83 |
84 | let coords = []
85 |
86 | let that = this
87 |
88 | firebase.auth().signInAnonymously()
89 | .then(() => {
90 | // setup live data listener
91 | this.docRef.onSnapshot(function (querySnapshot) {
92 | querySnapshot.docChanges().forEach(function (change) {
93 | if (change.type === 'added') {
94 | that.emit('added', change.doc.data())
95 | }
96 | if (change.type === 'modified') {
97 | that.emit('modified', change.doc.data())
98 | }
99 | if (change.type === 'removed') {
100 | that.emit('removed', change.doc.data())
101 | }
102 | })
103 | })
104 |
105 | this.docRef.orderBy('timestamp', 'desc').get().then(function (querySnapshot) {
106 | querySnapshot.forEach(function (doc) {
107 | coords.push(doc.data())
108 | })
109 | resolve(coords)
110 | })
111 | })
112 | .catch(function (error) {
113 | console.log(error.code)
114 | console.log(error.message)
115 | })
116 | })
117 | }
118 |
119 | setConfigFromURLParams () {
120 | const blendColor = parseInt(getUrlParameter('blendColor'))
121 | if (!isNaN(blendColor)) {
122 | this.config.post.blendColor = new Color(blendColor)
123 | }
124 |
125 | const showAnnotations = parseInt(getUrlParameter('showAnnotations'))
126 | if (!isNaN(showAnnotations)) {
127 | this.config.scene.showAnnotations = !!showAnnotations
128 | }
129 |
130 | const transparentBackground = parseInt(getUrlParameter('transparentBackground'))
131 | if (!isNaN(transparentBackground)) {
132 | this.config.post.transparentBackground = !!transparentBackground
133 | }
134 |
135 | const lowBandwidth = parseInt(getUrlParameter('lowBandwidth'))
136 | if (!isNaN(lowBandwidth)) {
137 | this.config.scene.lowBandwidth = !!lowBandwidth
138 | }
139 | }
140 |
141 | initStage () {
142 | this.setConfigFromURLParams()
143 |
144 | GlobeSceneClass.getInstance().init()
145 | IcosaSceneClass.getInstance().init()
146 | PickerSceneClass.getInstance().init()
147 | CameraClass.getInstance().init()
148 |
149 | RendererClass.getInstance().init()
150 |
151 | const numPoints = this.config.particleScene.width * this.config.scene.height
152 | ParticlesClass.getInstance().init(numPoints)
153 |
154 | FBOClass.getInstance().init({
155 | width: this.config.particleScene.width,
156 | height: this.config.scene.height,
157 | transparentBackground: this.config.post.transparentBackground
158 | })
159 | ControlsClass.getInstance().init()
160 | MouseClass.getInstance().init()
161 | TouchClass.getInstance().init()
162 | IcosahedronClass.getInstance().init()
163 | GlobeClass.getInstance().init()
164 | AmbientLightClass.getInstance().init()
165 | PointLightClass.getInstance().init()
166 |
167 | this.initFireBase().then((data) => {
168 | this.data = data
169 |
170 | MarkersClass.getInstance().init(data)
171 | PickersClass.getInstance().init(data)
172 | PathsClass.getInstance().init(data)
173 |
174 | this.buildScene()
175 | this.addEvents()
176 | this.animate()
177 |
178 | this.setState({
179 | loading: false
180 | })
181 |
182 | this.highlightLatestNode()
183 | })
184 | }
185 |
186 | highlightLatestNode () {
187 | this.emit('modified', this.data[0])
188 | }
189 |
190 | buildScene () {
191 | IcosaSceneClass.getInstance().scene.add(IcosahedronClass.getInstance().mesh)
192 | IcosaSceneClass.getInstance().scene.add(IcosahedronClass.getInstance().mesh2)
193 | IcosaSceneClass.getInstance().scene.add(AmbientLightClass.getInstance().light)
194 | IcosaSceneClass.getInstance().scene.add(PointLightClass.getInstance().light)
195 | IcosaSceneClass.getInstance().scene.add(MarkersClass.getInstance().mesh)
196 | IcosaSceneClass.getInstance().scene.add(PathsClass.getInstance().mesh)
197 |
198 | PickerSceneClass.getInstance().scene.add(PickersClass.getInstance().mesh)
199 |
200 | GlobeSceneClass.getInstance().scene.add(GlobeClass.getInstance().mesh)
201 | }
202 |
203 | animate () {
204 | window.requestAnimationFrame(this.animate.bind(this))
205 | this.renderFrame()
206 | }
207 |
208 | renderFrame () {
209 | const dt = this.clock.getDelta()
210 |
211 | TWEEN.update()
212 |
213 | this.setState({
214 | tooltipPos: MarkersClass.getInstance().selectedNodePosScreen
215 | })
216 |
217 | MouseClass.getInstance().renderFrame({ dt: dt })
218 | TouchClass.getInstance().renderFrame({ dt: dt })
219 | ControlsClass.getInstance().renderFrame({ dt: dt })
220 | MarkersClass.getInstance().renderFrame({ dt: dt })
221 | PickersClass.getInstance().renderFrame({ dt: dt })
222 | PathsClass.getInstance().renderFrame({ dt: dt })
223 | ParticlesClass.getInstance().renderFrame({ dt: dt })
224 | FBOClass.getInstance().renderFrame({ dt: dt })
225 | }
226 |
227 | addNewNode (data) {
228 | this.data.push(data)
229 | this.addToModifiedQueue(data)
230 | MarkersClass.getInstance().addNode(data)
231 | PickersClass.getInstance().addNode(data)
232 | PathsClass.getInstance().addNode(data)
233 | }
234 |
235 | addEvents () {
236 | window.addEventListener('resize', this.resize.bind(this), false)
237 | this.resize()
238 |
239 | RendererClass.getInstance().renderer.domElement.addEventListener('mousemove', (e) => {
240 | MouseClass.getInstance().onMouseMove(e)
241 | }, false)
242 |
243 | RendererClass.getInstance().renderer.domElement.addEventListener('touchmove', (e) => {
244 | TouchClass.getInstance().onTouchMove(e)
245 | }, false)
246 |
247 | RendererClass.getInstance().renderer.domElement.addEventListener('wheel', () => {
248 | MarkersClass.getInstance().stopUpdateCamPos()
249 | })
250 |
251 | RendererClass.getInstance().renderer.domElement.addEventListener('mousedown', () => {
252 | MarkersClass.getInstance().stopUpdateCamPos()
253 |
254 | // const data = {
255 | // city: 'Ashburn',
256 | // country: 'United States',
257 | // ip: '54.242.227.95',
258 | // lat: 0.0,
259 | // long: 0.0,
260 | // region: 'Virginia',
261 | // timestamp: { seconds: 1575282866, nanoseconds: 504000000 }
262 | // }
263 |
264 | // this.addNewNode(data)
265 | })
266 |
267 | PickersClass.getInstance().on('nodeMouseOver', (data) => {
268 | clearTimeout(this.hoverTimeout)
269 | this.showGeoData(data)
270 | })
271 |
272 | PickersClass.getInstance().on('nodeMouseOut', () => {
273 | this.setState({
274 | tooltipHide: true
275 | })
276 | })
277 |
278 | // on node data changes
279 | this.on('modified', (data) => {
280 | this.addToModifiedQueue(data)
281 | this.processModifiedQueue()
282 | })
283 |
284 | this.on('added', (data) => {
285 | this.addNewNode(data)
286 |
287 | this.processModifiedQueue()
288 |
289 | // console.log('Added: ', data)
290 | })
291 |
292 | this.on('removed', (data) => {
293 | // console.log('Removed: ', data)
294 | })
295 | }
296 |
297 | addToModifiedQueue (data) {
298 | this.modifiedQueue.push(data)
299 | }
300 |
301 | showGeoData (data) {
302 | if (typeof data === 'undefined') {
303 | return
304 | }
305 |
306 | if (data.city === null && data.country === null) {
307 | data.country = 'Unknown'
308 | }
309 |
310 | this.hoverTimeout = setTimeout(() => {
311 | this.setState({
312 | tooltipHide: true
313 | })
314 | }, 4000)
315 |
316 | this.setState({
317 | tooltipCountry: data.country,
318 | tooltipCity: data.city,
319 | tooltipHide: false,
320 | tooltipLastBlockTime: new Intl.DateTimeFormat('default', {
321 | year: '2-digit',
322 | month: 'numeric',
323 | day: 'numeric',
324 | hour: 'numeric',
325 | minute: 'numeric',
326 | second: 'numeric',
327 | hour12: false
328 | }).format(data.timestamp.toDate())
329 | })
330 | }
331 |
332 | processModifiedQueue () {
333 | if (PickersClass.getInstance().isHovering) {
334 | return
335 | }
336 |
337 | if (this.modifiedQueue.length === 0) {
338 | return
339 | }
340 |
341 | if (this.processingQueue) {
342 | return
343 | }
344 |
345 | this.processingQueue = true
346 |
347 | const data = this.modifiedQueue.shift()
348 |
349 | if (!PickersClass.getInstance().isHovering) {
350 | this.showGeoData(data)
351 | }
352 |
353 | MarkersClass.getInstance().highlight(data)
354 | .then(() => {
355 | // console.log('Updated: ', data)
356 | this.processingQueue = false
357 | this.processModifiedQueue()
358 | })
359 | }
360 |
361 | resize () {
362 | this.width = window.innerWidth
363 | this.height = window.innerHeight
364 |
365 | CameraClass.getInstance().resize(this.width, this.height)
366 | RendererClass.getInstance().resize(this.width, this.height)
367 | FBOClass.getInstance().resize(this.width, this.height)
368 | ParticlesClass.getInstance().resize(this.width, this.height)
369 | MarkersClass.getInstance().resize(this.width, this.height)
370 | PickersClass.getInstance().resize(this.width, this.height)
371 |
372 | if (this.config.post.enabled) {
373 | this.composer.setSize(this.width, this.height)
374 | }
375 | }
376 |
377 | destroy () {
378 | RendererClass.getInstance().dispose()
379 | GlobeSceneClass.getInstance().destroy()
380 | ControlsClass.getInstance().destroy()
381 | FBOClass.getInstance().destroy()
382 |
383 | if (this.composer) {
384 | delete this.composer
385 | }
386 |
387 | window.cancelAnimationFrame(this.animate)
388 | this.running = false
389 | }
390 |
391 | render () {
392 | var tooltipStyle = {
393 | left: this.state.tooltipPos.x,
394 | top: this.state.tooltipPos.y
395 | }
396 |
397 | let className = styles.tooltip
398 |
399 | if (this.state.tooltipHide) {
400 | className = styles.tooltipHide
401 | }
402 |
403 | if (this.config.scene.showAnnotations === false) {
404 | className = styles.tooltipHide
405 | }
406 |
407 | return (
408 |
409 |
410 |
411 |
{this.state.tooltipCity}
412 |
{this.state.tooltipCountry}
413 |
Last Block Time: {this.state.tooltipLastBlockTime}
414 |
415 |
416 | )
417 | }
418 | }
419 |
420 | export default Main
421 |
--------------------------------------------------------------------------------
/src/components/classes/AmbientLightClass.js:
--------------------------------------------------------------------------------
1 | import {
2 | AmbientLight
3 | } from 'three'
4 |
5 | import BaseClass from './BaseClass'
6 |
7 | class AmbientLightClass extends BaseClass {
8 | init () {
9 | this.light = new AmbientLight(this.config.scene.ambientLightColor, this.config.scene.ambientLightIntensity)
10 |
11 | super.init()
12 | }
13 | }
14 |
15 | export default AmbientLightClass
16 |
--------------------------------------------------------------------------------
/src/components/classes/BaseClass.js:
--------------------------------------------------------------------------------
1 | import EventEmitter from 'eventemitter3'
2 |
3 | import Config from '../Config'
4 |
5 | /**
6 | * Base Singleton Class
7 | */
8 | class BaseClass extends EventEmitter {
9 | /**
10 | * Create new singleton class instance
11 | */
12 | constructor () {
13 | super()
14 | this.config = new Config().data
15 |
16 | if (!this.constructor.instance) {
17 | this.constructor.instance = this
18 | }
19 |
20 | return this.constructor.instance
21 | }
22 |
23 | /**
24 | * Return singleton instance
25 | */
26 | static getInstance () {
27 | return new this()
28 | }
29 |
30 | /**
31 | * Initialize class properties
32 | */
33 | init () {
34 | const instance = this.constructor.getInstance()
35 | Object.freeze(instance)
36 | }
37 |
38 | /**
39 | * Run on window resize
40 | *
41 | * @param {int} width
42 | * @param {int} height
43 | */
44 | resize (width, height) {}
45 |
46 | /**
47 | * Run on each renderered frame
48 | */
49 | renderFrame () {}
50 |
51 | /**
52 | * Tear down
53 | */
54 | destroy () {}
55 |
56 | /**
57 | * On mouse move
58 | */
59 | onMouseMove () {}
60 | }
61 |
62 | export default BaseClass
63 |
--------------------------------------------------------------------------------
/src/components/classes/CameraClass.js:
--------------------------------------------------------------------------------
1 | import {
2 | PerspectiveCamera
3 | } from 'three'
4 |
5 | import BaseClass from './BaseClass'
6 |
7 | class CameraClass extends BaseClass {
8 | init () {
9 | this.camera = new PerspectiveCamera(
10 | this.config.camera.fov,
11 | window.innerWidth / window.innerHeight,
12 | this.config.camera.near,
13 | this.config.camera.far
14 | )
15 | this.camera.position.x = this.config.camera.initPos.x
16 | this.camera.position.y = this.config.camera.initPos.y
17 | this.camera.position.z = this.config.camera.initPos.z
18 |
19 | this.camera.updateMatrixWorld()
20 | }
21 |
22 | resize (width, height) {
23 | this.camera.aspect = width / height
24 |
25 | this.camera.updateProjectionMatrix()
26 | super.resize()
27 | }
28 | }
29 |
30 | export default CameraClass
31 |
--------------------------------------------------------------------------------
/src/components/classes/ControlsClass.js:
--------------------------------------------------------------------------------
1 | import OrbitControls from 'three-orbitcontrols'
2 |
3 | import CameraClass from './CameraClass'
4 | import RendererClass from './RendererClass'
5 | import BaseClass from './BaseClass'
6 |
7 | class ControlsClass extends BaseClass {
8 | init () {
9 | this.controls = new OrbitControls(CameraClass.getInstance().camera, RendererClass.getInstance().renderer.domElement.parentNode)
10 | this.controls.minDistance = 2.4
11 | this.controls.maxDistance = 8
12 | this.controls.enablePan = false
13 | this.controls.enableZoom = this.config.camera.enableZoom
14 | this.controls.zoomSpeed = 0.7
15 | this.controls.rotateSpeed = 0.25
16 | this.controls.autoRotateSpeed = 0.3
17 | this.controls.autoRotate = false
18 | this.controls.enableDamping = false
19 | this.controls.dampingFactor = 0.01
20 | super.init()
21 | }
22 |
23 | destroy () {
24 | this.controls.dispose()
25 | super.destroy()
26 | }
27 |
28 | renderFrame () {
29 | this.controls.update()
30 | super.renderFrame()
31 | }
32 | }
33 |
34 | export default ControlsClass
35 |
--------------------------------------------------------------------------------
/src/components/classes/FBOClass.js:
--------------------------------------------------------------------------------
1 |
2 | /* ------------------------------------------
3 | 3rd Party
4 | ------------------------------------------ */
5 | import {
6 | WebGLRenderTarget,
7 | LinearFilter,
8 | RGBAFormat,
9 | PlaneBufferGeometry,
10 | Mesh,
11 | ShaderMaterial,
12 | Scene,
13 | PerspectiveCamera,
14 | Vector2,
15 | OrthographicCamera
16 | } from 'three'
17 |
18 | /* ------------------------------------------
19 | Post
20 | ------------------------------------------ */
21 | import { EffectComposer, ShaderPass, RenderPass, UnrealBloomPass } from '../../post/EffectComposer'
22 | import BrightnessContrastShader from '../../post/BrightnessContrast'
23 | import FXAAShader from '../../post/FXAAShader'
24 | import BlendShader from '../../post/BlendLighten'
25 | import VignetteShader from '../../post/Vignette'
26 |
27 | /* ------------------------------------------
28 | Classes
29 | ------------------------------------------ */
30 | import BaseClass from './BaseClass'
31 | import GlobeSceneClass from './GlobeSceneClass'
32 | import IcosaSceneClass from './IcosaSceneClass'
33 | import RendererClass from './RendererClass'
34 | import CameraClass from './CameraClass'
35 | import ParticlesClass from './ParticlesClass'
36 |
37 | /* ------------------------------------------
38 | Shaders
39 | ------------------------------------------ */
40 | import PassThroughVert from '../../shaders/passThrough.vert'
41 | import MousePosFrag from '../../shaders/mousePos.frag'
42 | import MouseClass from './MouseClass'
43 |
44 | import TouchClass from './TouchClass'
45 |
46 | class FBOClass extends BaseClass {
47 | init ({
48 | width,
49 | height,
50 | transparentBackground
51 | } = {}) {
52 | this.frame = 0
53 | this.width = width
54 | this.height = height
55 |
56 | this.initRenderTargets()
57 | this.initMousePos()
58 | this.addMesh()
59 |
60 | this.composer = new EffectComposer(RendererClass.getInstance().renderer)
61 |
62 | this.renderPassMain = new RenderPass(IcosaSceneClass.getInstance().scene, CameraClass.getInstance().camera)
63 | this.composer.addPass(this.renderPassMain)
64 |
65 | this.renderPassParticles = new RenderPass(this.particleScene, this.particleCamera)
66 | this.renderPassParticles.clear = false
67 | this.renderPassParticles.alpha = true
68 | this.renderPassParticles.transparent = true
69 | this.composer.addPass(this.renderPassParticles)
70 |
71 | this.BrightnessContrastPass = new ShaderPass(BrightnessContrastShader)
72 | this.composer.addPass(this.BrightnessContrastPass)
73 |
74 | const alphaSum = transparentBackground ? 0.0 : 1.0
75 |
76 | this.bloomPass = new UnrealBloomPass(new Vector2(this.width, this.height), 0.8, 2, 0.1, alphaSum) // 1.0, 9, 0.5, 512);
77 | this.composer.addPass(this.bloomPass)
78 |
79 | if (this.config.post.vignette) {
80 | this.VignettePass = new ShaderPass(VignetteShader)
81 | this.composer.addPass(this.VignettePass)
82 | }
83 |
84 | if (this.config.post.blendLighten && !transparentBackground) {
85 | this.BlendPass = new ShaderPass(BlendShader)
86 | this.BlendPass.material.uniforms['blendColor'].value = this.config.post.blendColor
87 | this.composer.addPass(this.BlendPass)
88 | }
89 |
90 | this.FXAAPass = new ShaderPass(FXAAShader)
91 | this.FXAAPass.material.uniforms['resolution'].value.x = 1 / (window.innerWidth)
92 | this.FXAAPass.material.uniforms['resolution'].value.y = 1 / (window.innerHeight)
93 | this.FXAAPass.renderToScreen = true
94 | this.composer.addPass(this.FXAAPass)
95 | }
96 |
97 | initRenderTargets () {
98 | this.RTGlobe = new WebGLRenderTarget(
99 | this.width,
100 | this.height,
101 | {
102 | minFilter: LinearFilter,
103 | magFilter: LinearFilter,
104 | format: RGBAFormat,
105 | type: this.config.floatType,
106 | depthWrite: false,
107 | depthBuffer: false,
108 | stencilBuffer: false
109 | }
110 | )
111 |
112 | this.RTParticles = this.RTGlobe.clone()
113 | this.rt3 = this.RTGlobe.clone()
114 | this.rt4 = this.RTGlobe.clone()
115 | }
116 |
117 | addMesh () {
118 | this.particleScene = new Scene()
119 | this.particleScene.add(ParticlesClass.getInstance().mesh)
120 |
121 | this.particleCamera = new PerspectiveCamera(
122 | this.config.camera.fov,
123 | 1.0,
124 | this.config.camera.near,
125 | this.config.camera.far
126 | )
127 | this.particleCamera.position.x = 0
128 | this.particleCamera.position.y = 0
129 | this.particleCamera.position.z = 0.9
130 | this.particleCamera.updateMatrixWorld()
131 | }
132 |
133 | initMousePos () {
134 | this.mousePosRT1 = new WebGLRenderTarget(
135 | this.width,
136 | this.height,
137 | {
138 | minFilter: LinearFilter,
139 | magFilter: LinearFilter,
140 | format: RGBAFormat,
141 | type: this.config.floatType,
142 | depthWrite: false,
143 | depthBuffer: false,
144 | stencilBuffer: false
145 | })
146 |
147 | this.mousePosRT2 = this.mousePosRT1.clone()
148 |
149 | this.mousePosScene = new Scene()
150 | this.mousePosMaterial = new ShaderMaterial({
151 | uniforms: {
152 | uMousePosTexture: {
153 | type: 't',
154 | value: null
155 | },
156 | uMousePos: {
157 | type: 'v2',
158 | value: new Vector2(0, 0)
159 | },
160 | uPrevMousePos: {
161 | type: 'v2',
162 | value: new Vector2(0, 0)
163 | },
164 | uDir: {
165 | type: 'v2',
166 | value: new Vector2(0, 0)
167 | },
168 | uAspect: {
169 | type: 'f',
170 | value: 1.0
171 | }
172 | },
173 | vertexShader: PassThroughVert,
174 | fragmentShader: MousePosFrag
175 | })
176 | this.mousePosMesh = new Mesh(new PlaneBufferGeometry(2, 2), this.mousePosMaterial)
177 | this.mousePosMesh.frustumCulled = false
178 | this.mousePosScene.add(this.mousePosMesh)
179 |
180 | this.mousePosCamera = new OrthographicCamera()
181 | this.mousePosCamera.position.z = 1
182 | this.mousePosCamera.updateMatrixWorld()
183 |
184 | this.mousePosTexture = null
185 | }
186 |
187 | resize (width, height) {
188 | this.RTGlobe.setSize(width, height)
189 | this.RTParticles.setSize(width, height)
190 | this.composer.setSize(width, height)
191 | this.bloomPass.setSize(width, height)
192 | this.FXAAPass.material.uniforms[ 'resolution' ].value.x = 1 / (width)
193 | this.FXAAPass.material.uniforms[ 'resolution' ].value.y = 1 / (height)
194 | this.mousePosMaterial.uniforms.uAspect.value = CameraClass.getInstance().camera.aspect
195 |
196 | super.resize()
197 | }
198 |
199 | renderFrame (args) {
200 | this.frame++
201 |
202 | // this.FilmPass.uniforms[ 'time' ].value += args.dt * 0.1
203 |
204 | // standard scene
205 | RendererClass.getInstance().renderer.setRenderTarget(this.RTGlobe)
206 | // RendererClass.getInstance().renderer.autoClear = false
207 | RendererClass.getInstance().renderer.render(GlobeSceneClass.getInstance().scene, CameraClass.getInstance().camera)
208 |
209 | // particles scene
210 | ParticlesClass.getInstance().mesh.material.uniforms.uTexture.value = this.RTGlobe.texture
211 |
212 | // debug picker
213 | // RendererClass.getInstance().renderer.setRenderTarget(null)
214 | // RendererClass.getInstance().renderer.render(PickerSceneClass.getInstance().scene, CameraClass.getInstance().camera)
215 |
216 | this.composer.render()
217 |
218 | // mouse position
219 | if (this.config.detector.isMobile) {
220 | this.mousePosMaterial.uniforms.uMousePos.value = TouchClass.getInstance().normalizedTouchPos
221 | this.mousePosMaterial.uniforms.uPrevMousePos.value = TouchClass.getInstance().prevNormalizedTouchPos
222 | } else {
223 | this.mousePosMaterial.uniforms.uMousePos.value = MouseClass.getInstance().normalizedMousePos
224 | this.mousePosMaterial.uniforms.uPrevMousePos.value = MouseClass.getInstance().prevNormalizedMousePos
225 | }
226 |
227 | let inputPositionRenderTarget = this.mousePosRT1
228 | this.outputPositionRenderTarget = this.mousePosRT2
229 | if (this.frame % 2 === 0) {
230 | inputPositionRenderTarget = this.mousePosRT2
231 | this.outputPositionRenderTarget = this.mousePosRT1
232 | }
233 |
234 | this.mousePosMaterial.uniforms.uMousePosTexture.value = inputPositionRenderTarget.texture
235 |
236 | RendererClass.getInstance().renderer.setRenderTarget(this.outputPositionRenderTarget)
237 | RendererClass.getInstance().renderer.render(this.mousePosScene, this.mousePosCamera)
238 |
239 | this.mousePosTexture = this.outputPositionRenderTarget.texture
240 |
241 | // RendererClass.getInstance().renderer.setRenderTarget(null)
242 | // RendererClass.getInstance().renderer.render(this.mousePosScene, this.mousePosCamera)
243 |
244 | super.renderFrame()
245 | }
246 | }
247 |
248 | export default FBOClass
249 |
--------------------------------------------------------------------------------
/src/components/classes/GlobeClass.js:
--------------------------------------------------------------------------------
1 | import {
2 | Mesh,
3 | SphereBufferGeometry,
4 | MeshBasicMaterial,
5 | Color,
6 | TextureLoader
7 | } from 'three'
8 |
9 | import BaseClass from './BaseClass'
10 |
11 | // textures
12 | import map from '../../assets/globeBW.jpg'
13 | import mapE from '../../assets/globeBWe.jpg'
14 |
15 | class GlobeClass extends BaseClass {
16 | init () {
17 | if (this.config.scene.lowBandwidth) {
18 | this.map = new TextureLoader().load(mapE)
19 | } else {
20 | this.map = new TextureLoader().load(map)
21 | }
22 |
23 | this.geometry = new SphereBufferGeometry(this.config.scene.sphereRadius, 32, 32)
24 | this.material = new MeshBasicMaterial({
25 | color: new Color(0xffffff),
26 | opacity: 1.0,
27 | map: this.map
28 | })
29 | this.mesh = new Mesh(this.geometry, this.material)
30 |
31 | super.init()
32 | }
33 | }
34 |
35 | export default GlobeClass
36 |
--------------------------------------------------------------------------------
/src/components/classes/GlobeSceneClass.js:
--------------------------------------------------------------------------------
1 | import {
2 | Scene
3 | } from 'three'
4 |
5 | import BaseClass from './BaseClass'
6 |
7 | class GlobeClass extends BaseClass {
8 | init () {
9 | this.scene = new Scene()
10 | // this.scene.background = this.config.scene.bgColor
11 |
12 | super.init()
13 | }
14 |
15 | destroy () {
16 | this.scene.traverse(function (object) {
17 | if (object.geometry) {
18 | object.geometry.dispose()
19 | }
20 | if (object.material) {
21 | object.material.dispose()
22 | }
23 | })
24 |
25 | super.destroy()
26 | }
27 | }
28 |
29 | export default GlobeClass
30 |
--------------------------------------------------------------------------------
/src/components/classes/IcosaSceneClass.js:
--------------------------------------------------------------------------------
1 | import {
2 | Scene
3 | } from 'three'
4 |
5 | import BaseClass from './BaseClass'
6 |
7 | class IcosaSceneClass extends BaseClass {
8 | init () {
9 | this.scene = new Scene()
10 |
11 | super.init()
12 | }
13 |
14 | destroy () {
15 | this.scene.traverse(function (object) {
16 | if (object.geometry) {
17 | object.geometry.dispose()
18 | }
19 | if (object.material) {
20 | object.material.dispose()
21 | }
22 | })
23 |
24 | super.destroy()
25 | }
26 | }
27 |
28 | export default IcosaSceneClass
29 |
--------------------------------------------------------------------------------
/src/components/classes/IcosahedronClass.js:
--------------------------------------------------------------------------------
1 | import {
2 | Mesh,
3 | IcosahedronBufferGeometry,
4 | MeshBasicMaterial,
5 | Color,
6 | AdditiveBlending,
7 | FrontSide,
8 | BackSide
9 | } from 'three'
10 |
11 | import BaseClass from './BaseClass'
12 |
13 | class IcosahedronClass extends BaseClass {
14 | init () {
15 | this.geometry = new IcosahedronBufferGeometry(this.config.scene.sphereRadius * 1.08, 1)
16 | this.material = new MeshBasicMaterial({
17 | color: new Color(0x711111),
18 | wireframe: true,
19 | opacity: 0.1,
20 | transparent: true,
21 | blending: AdditiveBlending,
22 | depthWrite: false,
23 | side: FrontSide
24 | })
25 | this.mesh = new Mesh(this.geometry, this.material)
26 |
27 | this.mesh.frustumCulled = false
28 |
29 | this.mesh2 = this.mesh.clone()
30 | this.mesh2.scale.set(0.98, 0.98, 0.98)
31 | this.mesh2.material = new MeshBasicMaterial({
32 | color: new Color(0x000000),
33 | side: BackSide,
34 | opacity: 0
35 | })
36 |
37 | super.init()
38 | }
39 | }
40 |
41 | export default IcosahedronClass
42 |
--------------------------------------------------------------------------------
/src/components/classes/MarkersClass.js:
--------------------------------------------------------------------------------
1 | import {
2 | Mesh,
3 | BufferGeometry,
4 | CylinderGeometry,
5 | InstancedBufferGeometry,
6 | InstancedBufferAttribute,
7 | ShaderLib,
8 | Color,
9 | Object3D,
10 | Vector3,
11 | Vector2,
12 | MeshBasicMaterial
13 | } from 'three'
14 |
15 | import TWEEN from 'tween.js'
16 |
17 | import BaseClass from './BaseClass'
18 |
19 | import { latLongToCartesian } from '../../helpers/math'
20 |
21 | // import { coords } from '../../data/test'
22 |
23 | // shaders
24 | import fragmentShader from '../../shaders/markers.frag'
25 | import vertexShader from '../../shaders/markers.vert'
26 | import CameraClass from './CameraClass'
27 | import IcosaSceneClass from './IcosaSceneClass'
28 |
29 | class MarkersClass extends BaseClass {
30 | init (data) {
31 | let coords = data
32 |
33 | this.camTween = null
34 | this.updateCamPos = true
35 | this.selectionRef = new Object3D()
36 | this.selectionRefPos = new Vector3()
37 | this.selectedNodePosScreen = new Vector3()
38 | IcosaSceneClass.getInstance().scene.add(this.selectionRef)
39 |
40 | this.nodeCount = coords.length
41 | this.instanceTotal = 1000 // max number of instances
42 |
43 | this.material = new MarkersMaterial({
44 | color: new Color(0x888888),
45 | flatShading: true,
46 | wireframe: true
47 | })
48 |
49 | const tubeGeo = new CylinderGeometry(0.0, 0.005, 0.06, 3)
50 | const tubeBufferGeo = new BufferGeometry().fromGeometry(tubeGeo)
51 | this.geometry = new InstancedBufferGeometry().copy(tubeBufferGeo)
52 | this.geometry.rotateX(Math.PI / 2)
53 |
54 | this.ipMap = []
55 | this.offsetsAttr = new InstancedBufferAttribute(new Float32Array(this.instanceTotal * 3).fill(99999), 3)
56 | this.idAttr = new InstancedBufferAttribute(new Float32Array(this.instanceTotal), 1)
57 | this.scalesAttr = new InstancedBufferAttribute(new Float32Array(this.instanceTotal), 1)
58 | this.quaternionsAttr = new InstancedBufferAttribute(new Float32Array(this.instanceTotal * 4), 4)
59 | this.isHoveredAttr = new InstancedBufferAttribute(new Float32Array(this.instanceTotal), 1)
60 | this.isSelectedAttr = new InstancedBufferAttribute(new Float32Array(this.instanceTotal), 1)
61 |
62 | this.geometry.setAttribute('offset', this.offsetsAttr)
63 | this.geometry.setAttribute('scale', this.scalesAttr)
64 | this.geometry.setAttribute('quaternion', this.quaternionsAttr)
65 | this.geometry.setAttribute('isHovered', this.isHoveredAttr)
66 | this.geometry.setAttribute('isSelected', this.isSelectedAttr)
67 | this.geometry.setAttribute('id', this.idAttr)
68 |
69 | for (let index = 0; index < this.nodeCount; index++) {
70 | if (typeof coords[index] !== 'undefined') {
71 | this.addNodeGeoData(coords[index], index)
72 | }
73 | }
74 |
75 | this.mesh = new Mesh(this.geometry, this.material)
76 | this.mesh.frustumCulled = false
77 | }
78 |
79 | addNodeGeoData (data, index) {
80 | const pos = latLongToCartesian(data.lat, data.long, this.config.scene.sphereRadius * 1.05)
81 |
82 | const x = this.offsetsAttr.array[index * 3 + 0] = pos.x
83 | const y = this.offsetsAttr.array[index * 3 + 1] = pos.y
84 | const z = this.offsetsAttr.array[index * 3 + 2] = pos.z
85 |
86 | const dummyObject = new Object3D()
87 | dummyObject.position.set(x, y, z)
88 | dummyObject.lookAt(0, 0, 0)
89 |
90 | this.quaternionsAttr.array[index * 4 + 0] = dummyObject.quaternion.x
91 | this.quaternionsAttr.array[index * 4 + 1] = dummyObject.quaternion.y
92 | this.quaternionsAttr.array[index * 4 + 2] = dummyObject.quaternion.z
93 | this.quaternionsAttr.array[index * 4 + 3] = dummyObject.quaternion.w
94 |
95 | this.scalesAttr.array[index] = 1.0
96 |
97 | this.idAttr.array[index] = index
98 |
99 | data.pos = pos
100 | this.ipMap[index] = data
101 |
102 | this.geometry.attributes.offset.needsUpdate = true
103 | this.geometry.attributes.scale.needsUpdate = true
104 | this.geometry.attributes.quaternion.needsUpdate = true
105 | this.geometry.attributes.isHovered.needsUpdate = true
106 | this.geometry.attributes.isSelected.needsUpdate = true
107 | this.geometry.attributes.id.needsUpdate = true
108 | }
109 |
110 | addNode (data) {
111 | this.nodeCount += 1
112 | this.addNodeGeoData(data, this.nodeCount)
113 | }
114 |
115 | getArcFromCoords (camPos, endPos, steps) {
116 | // get normal of both points
117 | let cb = new Vector3()
118 | let ab = new Vector3()
119 | let normal = new Vector3()
120 | cb.subVectors(new Vector3(), endPos)
121 | ab.subVectors(camPos, endPos)
122 | cb.cross(ab)
123 | normal.copy(cb).normalize()
124 |
125 | const angle = camPos.angleTo(endPos) // get the angle between vectors
126 | const angleDelta = angle / (steps)
127 |
128 | let points = []
129 | for (var i = 0; i <= steps; i++) {
130 | points.push(camPos.clone().applyAxisAngle(normal, angleDelta * i))
131 | }
132 |
133 | return points
134 | }
135 |
136 | stopUpdateCamPos () {
137 | this.updateCamPos = false
138 | }
139 |
140 | setNodePosInScreenSpace () {
141 | this.selectionRefPos.setFromMatrixPosition(this.selectionRef.matrixWorld)
142 | this.selectionRefPos.project(CameraClass.getInstance().camera)
143 |
144 | this.selectedNodePosScreen = new Vector2(
145 | (this.selectionRefPos.x + 1) * this.width * 0.5,
146 | (1 - this.selectionRefPos.y) * this.height * 0.5
147 | )
148 | }
149 |
150 | setSelectionRef (nodePos) {
151 | this.selectionRef.position.set(nodePos.x, nodePos.y, nodePos.z)
152 | }
153 |
154 | highlight (data) {
155 | return new Promise((resolve, reject) => {
156 | let that = this
157 |
158 | this.ipMap.forEach((nodeData, index) => {
159 | if (nodeData.ip === data.ip) {
160 | if (that.camTween) {
161 | that.camTween.stop()
162 | }
163 |
164 | const nodePos = new Vector3(
165 | that.offsetsAttr.array[index * 3 + 0],
166 | that.offsetsAttr.array[index * 3 + 1],
167 | that.offsetsAttr.array[index * 3 + 2]
168 | )
169 |
170 | this.setSelectionRef(nodePos)
171 |
172 | const steps = 25
173 | let points = this.getArcFromCoords(CameraClass.getInstance().camera.position, nodePos, steps)
174 |
175 | that.camTween = new TWEEN.Tween({ step: 0 })
176 | .to({ step: steps }, 3000)
177 | .onUpdate(function () {
178 | if (!that.updateCamPos) {
179 | return
180 | }
181 |
182 | // lerp between points on arc
183 | const pos1 = points[Math.floor(this.step)]
184 | const pos2 = points[Math.floor(this.step + 1)]
185 | if (typeof pos2 !== 'undefined') {
186 | const pos = pos1.clone().lerp(pos2, this.step % 1)
187 | CameraClass.getInstance().camera.position.set(pos.x, pos.y, pos.z)
188 | }
189 | })
190 | .onComplete(() => {
191 | that.updateCamPos = true
192 | const properties = { scale: 5.0 }
193 | new TWEEN.Tween(properties)
194 | .to({ scale: 1.0 }, 2000)
195 | .onUpdate(function () {
196 | that.scalesAttr.array[index] = properties.scale
197 | that.scalesAttr.needsUpdate = true
198 | })
199 | .onComplete(() => {
200 | resolve()
201 | })
202 | .easing(TWEEN.Easing.Quadratic.InOut)
203 | .start()
204 | })
205 | .easing(TWEEN.Easing.Quadratic.InOut)
206 | .start()
207 | }
208 | })
209 | })
210 | }
211 |
212 | resize (width, height) {
213 | this.width = width
214 | this.height = height
215 | }
216 |
217 | renderFrame (args) {
218 | this.material.uniforms.uTime.value += args.dt
219 | this.material.uniforms.uDTime.value = args.dt
220 |
221 | this.setNodePosInScreenSpace()
222 |
223 | super.renderFrame()
224 | }
225 | }
226 |
227 | class MarkersMaterial extends MeshBasicMaterial {
228 | constructor (config) {
229 | super(config)
230 | this.type = 'ShaderMaterial'
231 |
232 | this.uniforms = ShaderLib.basic.uniforms
233 |
234 | this.uniforms.uTime = {
235 | type: 'f',
236 | value: 0.0
237 | }
238 |
239 | this.uniforms.uDTime = {
240 | type: 'f',
241 | value: 0.0
242 | }
243 |
244 | this.vertexShader = vertexShader
245 | this.fragmentShader = fragmentShader
246 | this.lights = true
247 | }
248 | }
249 |
250 | export default MarkersClass
251 |
--------------------------------------------------------------------------------
/src/components/classes/MouseClass.js:
--------------------------------------------------------------------------------
1 | // 3rd party
2 | import {
3 | Vector2
4 | } from 'three'
5 |
6 | // classes
7 | import BaseClass from './BaseClass'
8 | import RendererClass from './RendererClass'
9 |
10 | class MouseClass extends BaseClass {
11 | init () {
12 | this.prevMousePos = new Vector2(0, 0)
13 | this.mouseDelta = new Vector2(0, 0)
14 | this.movement = new Vector2()
15 | this.mousePos = new Vector2()
16 | this.normalizedMousePos = new Vector2()
17 | this.prevNormalizedMousePos = new Vector2()
18 | }
19 |
20 | onMouseMove (e) {
21 | this.prevNormalizedMousePos.x = this.normalizedMousePos.x
22 | this.prevNormalizedMousePos.y = this.normalizedMousePos.y
23 |
24 | this.prevMousePos.x = this.mousePos.x
25 | this.prevMousePos.y = this.mousePos.y
26 |
27 | this.mousePos.x = e.clientX - RendererClass.getInstance().renderer.domElement.offsetLeft
28 | this.mousePos.y = e.clientY - RendererClass.getInstance().renderer.domElement.offsetTop
29 |
30 | this.mouseDelta = this.mousePos.clone().sub(this.prevMousePos)
31 |
32 | this.movement.x = e.movementX
33 | this.movement.y = e.movementY
34 |
35 | const x = e.clientX - RendererClass.getInstance().renderer.domElement.offsetLeft
36 | const y = e.clientY - RendererClass.getInstance().renderer.domElement.offsetTop
37 |
38 | this.normalizedMousePos.x = x / RendererClass.getInstance().renderer.domElement.width
39 | this.normalizedMousePos.y = 1 - y / RendererClass.getInstance().renderer.domElement.height
40 |
41 | super.onMouseMove()
42 | }
43 |
44 | renderFrame ({ dt } = {}) {
45 | super.renderFrame()
46 | }
47 | }
48 |
49 | export default MouseClass
50 |
--------------------------------------------------------------------------------
/src/components/classes/ParticlesClass.js:
--------------------------------------------------------------------------------
1 | // 3rd party
2 | import {
3 | Vector2,
4 | InstancedBufferGeometry,
5 | InstancedBufferAttribute,
6 | Mesh,
7 | ShaderLib,
8 | ShaderMaterial,
9 | PlaneBufferGeometry,
10 | AdditiveBlending,
11 | Scene,
12 | WebGLRenderTarget,
13 | ClampToEdgeWrapping,
14 | NearestFilter,
15 | RGBAFormat,
16 | OrthographicCamera,
17 | Vector3
18 | } from 'three'
19 |
20 | import BaseClass from './BaseClass'
21 | import MouseClass from './MouseClass'
22 | import TouchClass from './TouchClass'
23 |
24 | // shaders
25 | import fragmentShader from '../../shaders/particles.frag'
26 | import vertexShader from '../../shaders/particles.vert'
27 | import PassThroughVert from '../../shaders/passThrough.vert'
28 | import PositionFrag from '../../shaders/position.frag'
29 | import PassThroughFrag from '../../shaders/passThrough.frag'
30 |
31 | // classes
32 | import TextureHelper from '../../helpers/TextureHelper'
33 | import RendererClass from './RendererClass'
34 | import FBOClass from './FBOClass'
35 | import CameraClass from './CameraClass'
36 |
37 | class ParticlesClass extends BaseClass {
38 | init (numPoints) {
39 | this.mouseMoved = 1
40 | this.frame = 0
41 | let step = 4
42 |
43 | this.particleCount = Math.round(numPoints / (step * step))
44 |
45 | this.textureHelper = new TextureHelper({
46 | config: this.config
47 | })
48 | this.textureHelper.setTextureSize(this.particleCount)
49 |
50 | this.material = new ParticlesMaterial({
51 | transparent: true,
52 | blending: AdditiveBlending
53 | })
54 | this.material.uniforms.uTextureSize = { value: new Vector2(this.config.particleScene.width, this.config.particleScene.height) }
55 | this.material.uniforms.uAspect = { value: CameraClass.getInstance().camera.aspect }
56 |
57 | this.geometry = new InstancedBufferGeometry()
58 | const refGeo = new PlaneBufferGeometry(1, 1)
59 | this.geometry.setAttribute('position', refGeo.attributes.position)
60 |
61 | this.geometry.setAttribute('uv', refGeo.attributes.uv)
62 | this.geometry.setIndex(refGeo.index)
63 |
64 | this.offsets = new Float32Array(this.particleCount * 2)
65 |
66 | for (let i = 0; i < this.particleCount; i++) {
67 | this.offsets[i * 2 + 0] = (i % (this.config.particleScene.width / step)) * step
68 | this.offsets[i * 2 + 1] = Math.floor(i / (this.config.particleScene.width / step)) * step
69 | }
70 |
71 | this.geometry.setAttribute('offset', new InstancedBufferAttribute(this.offsets, 2, false))
72 |
73 | const positionArray = new Float32Array(this.particleCount * 3)
74 |
75 | this.setTextureLocations(
76 | this.particleCount,
77 | positionArray
78 | )
79 |
80 | const tPosition = new InstancedBufferAttribute(positionArray, 3)
81 | this.geometry.setAttribute('tPosition', tPosition)
82 |
83 | this.mesh = new Mesh(this.geometry, this.material)
84 |
85 | this.mesh.position.z = 0.1
86 |
87 | this.positionMaterial = new ShaderMaterial({
88 | uniforms: {
89 | positionTexture: {
90 | type: 't',
91 | value: null
92 | },
93 | defaultPositionTexture: {
94 | type: 't',
95 | value: null
96 | },
97 | initialPositionTexture: {
98 | type: 't',
99 | value: null
100 | },
101 | uNoiseMix: {
102 | type: 'f',
103 | value: 1.0
104 | },
105 | uFrame: {
106 | type: 'f',
107 | value: 0.0
108 | },
109 | uMousePos: {
110 | type: 'v2',
111 | value: new Vector2(0, 0)
112 | },
113 | uPrevMousePos: {
114 | type: 'v2',
115 | value: new Vector2(0, 0)
116 | }
117 | },
118 | vertexShader: PassThroughVert,
119 | fragmentShader: PositionFrag
120 | })
121 |
122 | this.initCamera()
123 | this.initPassThrough()
124 | this.initRenderTargets()
125 | this.initPositions()
126 | }
127 |
128 | initPassThrough () {
129 | this.passThroughScene = new Scene()
130 | this.passThroughMaterial = new ShaderMaterial({
131 | uniforms: {
132 | texture: {
133 | type: 't',
134 | value: null
135 | }
136 | },
137 | vertexShader: PassThroughVert,
138 | fragmentShader: PassThroughFrag
139 | })
140 | const mesh = new Mesh(new PlaneBufferGeometry(2, 2), this.passThroughMaterial)
141 | mesh.frustumCulled = false
142 | this.passThroughScene.add(mesh)
143 | }
144 |
145 | initRenderTargets () {
146 | this.positionRenderTarget1 = new WebGLRenderTarget(this.textureHelper.textureWidth, this.textureHelper.textureHeight, {
147 | wrapS: ClampToEdgeWrapping,
148 | wrapT: ClampToEdgeWrapping,
149 | minFilter: NearestFilter,
150 | magFilter: NearestFilter,
151 | format: RGBAFormat,
152 | type: this.config.floatType,
153 | depthWrite: false,
154 | depthBuffer: false,
155 | stencilBuffer: false
156 | })
157 |
158 | this.positionRenderTarget2 = this.positionRenderTarget1.clone()
159 |
160 | this.outputPositionRenderTarget = this.positionRenderTarget1
161 | }
162 |
163 | initPositions () {
164 | this.renderer = RendererClass.getInstance().renderer
165 |
166 | const positionData = this.textureHelper.createPositionTexture()
167 | this.defaultPositionTexture = positionData.positionTexture
168 | this.initialPositionTexture = positionData.initialPositionTexture
169 |
170 | this.positionMaterial.uniforms.defaultPositionTexture.value = this.defaultPositionTexture
171 | this.material.uniforms.defaultPositionTexture.value = this.defaultPositionTexture
172 |
173 | this.positionMaterial.uniforms.initialPositionTexture.value = this.initialPositionTexture
174 | this.material.uniforms.initialPositionTexture.value = this.initialPositionTexture
175 |
176 | this.positionScene = new Scene()
177 |
178 | this.positionMesh = new Mesh(new PlaneBufferGeometry(2, 2), this.positionMaterial)
179 | this.positionMesh.frustumCulled = false
180 | this.positionScene.add(this.positionMesh)
181 | }
182 |
183 | initCamera () {
184 | this.quadCamera = new OrthographicCamera()
185 | this.quadCamera.position.z = 1
186 | }
187 |
188 | passThroughTexture (input, output) {
189 | this.passThroughMaterial.uniforms.texture.value = input
190 | this.renderer.setRenderTarget(output)
191 | this.renderer.render(this.passThroughScene, this.quadCamera)
192 | }
193 |
194 | updatePositions () {
195 | let inputPositionRenderTarget = this.positionRenderTarget1
196 | this.outputPositionRenderTarget = this.positionRenderTarget2
197 | if (this.frame % 2 === 0) {
198 | inputPositionRenderTarget = this.positionRenderTarget2
199 | this.outputPositionRenderTarget = this.positionRenderTarget1
200 | }
201 | this.positionMaterial.uniforms.positionTexture.value = inputPositionRenderTarget.texture
202 |
203 | this.renderer.setRenderTarget(this.outputPositionRenderTarget)
204 | this.renderer.render(this.positionScene, this.quadCamera)
205 |
206 | this.material.uniforms.positionTexture.value = this.outputPositionRenderTarget.texture
207 | }
208 |
209 | setTextureLocations (
210 | nodeCount,
211 | positionArray
212 | ) {
213 | for (let i = 0; i < nodeCount; i++) {
214 | const textureLocation = this.textureHelper.getNodeTextureLocation(i)
215 | positionArray[i * 3 + 0] = textureLocation.x
216 | positionArray[i * 3 + 1] = textureLocation.y
217 | }
218 | }
219 |
220 | resize (width, height) {
221 | this.material.uniforms.uAspect = { value: CameraClass.getInstance().camera.aspect }
222 | this.material.uniforms.uTextureSize = { value: new Vector2(width * this.config.particleScene.downScaleFactor, height * this.config.particleScene.downScaleFactor) }
223 |
224 | this.mesh.scale.set(1.0, CameraClass.getInstance().camera.aspect, 1.0)
225 | }
226 |
227 | renderFrame (args) {
228 | this.frame++
229 |
230 | this.positionMaterial.uniforms.uFrame.value = this.frame
231 | this.positionMaterial.uniforms.uMousePos.value = MouseClass.getInstance().normalizedMousePos
232 | this.positionMaterial.uniforms.uPrevMousePos.value = MouseClass.getInstance().prevNormalizedMousePos
233 |
234 | this.material.uniforms.uTime.value += args.dt
235 | this.material.uniforms.uMousePos.value = MouseClass.getInstance().normalizedMousePos
236 | this.material.uniforms.uPrevMousePos.value = MouseClass.getInstance().prevNormalizedMousePos
237 | this.material.uniforms.uMousePosTexture.value = FBOClass.getInstance().mousePosTexture
238 | this.material.uniforms.uCamPos.value = CameraClass.getInstance().camera.position
239 | this.material.uniforms.uIsMobile.value = this.config.detector.isMobile
240 |
241 | this.updatePositions()
242 |
243 | if (this.config.detector.isMobile) {
244 | if (Math.abs(TouchClass.getInstance().touchDelta.x) + Math.abs(TouchClass.getInstance().touchDelta.y) > 1.0) {
245 | this.mouseMoved = 1.0
246 | }
247 | } else {
248 | if (Math.abs(MouseClass.getInstance().mouseDelta.x) + Math.abs(MouseClass.getInstance().mouseDelta.y) > 1.0) {
249 | this.mouseMoved = 1.0
250 | }
251 | }
252 |
253 | if (this.mouseMoved > 0) {
254 | this.mouseMoved -= args.dt * 0.7
255 | }
256 | if (this.mouseMoved < 0) {
257 | this.mouseMoved = 0
258 | }
259 |
260 | this.material.uniforms.uNoiseMix.value = this.mouseMoved
261 | this.positionMaterial.uniforms.uNoiseMix.value = this.mouseMoved
262 |
263 | super.renderFrame()
264 | }
265 | }
266 |
267 | class ParticlesMaterial extends ShaderMaterial {
268 | constructor (config) {
269 | super(config)
270 |
271 | this.type = 'ShaderMaterial'
272 |
273 | this.uniforms = ShaderLib.standard.uniforms
274 |
275 | this.uniforms.uTexture = {
276 | type: 't',
277 | value: null
278 | }
279 |
280 | this.uniforms.uMousePosTexture = {
281 | type: 't',
282 | value: null
283 | }
284 |
285 | this.uniforms.uTime = {
286 | type: 'f',
287 | value: 0.0
288 | }
289 |
290 | this.uniforms.positionTexture = {
291 | type: 't',
292 | value: null
293 | }
294 |
295 | this.uniforms.initialPositionTexture = {
296 | type: 't',
297 | value: null
298 | }
299 |
300 | this.uniforms.defaultPositionTexture = {
301 | type: 't',
302 | value: null
303 | }
304 |
305 | this.uniforms.uMousePos = {
306 | type: 'v2',
307 | value: new Vector2(0, 0)
308 | }
309 |
310 | this.uniforms.uPrevMousePos = {
311 | type: 'v2',
312 | value: new Vector2(0, 0)
313 | }
314 |
315 | this.uniforms.uNoiseMix = {
316 | type: 'f',
317 | value: 0.0
318 | }
319 |
320 | this.uniforms.uAspect = {
321 | type: 'f',
322 | value: 1.0
323 | }
324 |
325 | this.uniforms.uCamPos = {
326 | type: 'v3',
327 | value: new Vector3(0, 0, 0)
328 | }
329 |
330 | this.uniforms.uIsMobile = {
331 | type: 'f',
332 | value: 0.0
333 | }
334 |
335 | this.vertexShader = vertexShader
336 | this.fragmentShader = fragmentShader
337 | this.lights = true
338 | }
339 | }
340 |
341 | export default ParticlesClass
342 |
--------------------------------------------------------------------------------
/src/components/classes/PathsClass.js:
--------------------------------------------------------------------------------
1 | import {
2 | Mesh,
3 | BufferGeometry,
4 | Color,
5 | MeshBasicMaterial,
6 | CubicBezierCurve3,
7 | BufferAttribute,
8 | AdditiveBlending,
9 | Line
10 | } from 'three'
11 |
12 | import { geoInterpolate } from 'd3-geo'
13 |
14 | import BaseClass from './BaseClass'
15 |
16 | import { latLongToCartesian, clamp } from '../../helpers/math'
17 |
18 | // test data
19 | // import { coords } from '../../data/test'
20 |
21 | class PathsClass extends BaseClass {
22 | getSplineFromCoords (coords) {
23 | const startLat = coords[0]
24 | const startLng = coords[1]
25 | const endLat = coords[2]
26 | const endLng = coords[3]
27 |
28 | // start and end points
29 | const start = latLongToCartesian(startLat, startLng, this.config.scene.globeRadius)
30 | const end = latLongToCartesian(endLat, endLng, this.config.scene.globeRadius)
31 |
32 | // altitude
33 | const altitude = clamp(start.distanceTo(end) * 0.75, this.config.curveMinAltitude, this.config.curveMaxAltitude)
34 |
35 | // 2 control points
36 | const interpolate = geoInterpolate([startLng, startLat], [endLng, endLat])
37 | const midCoord1 = interpolate(0.25 + (Math.random() * 0.1))
38 | const midCoord2 = interpolate(0.75 + (Math.random() * 0.1))
39 | const mid1 = latLongToCartesian(midCoord1[1], midCoord1[0], this.config.scene.globeRadius + altitude)
40 | const mid2 = latLongToCartesian(midCoord2[1], midCoord2[0], this.config.scene.globeRadius + altitude)
41 |
42 | return {
43 | start,
44 | end,
45 | spline: new CubicBezierCurve3(start, mid1, mid2, end)
46 | }
47 | }
48 |
49 | init (data) {
50 | this.coords = data
51 | this.material = new MeshBasicMaterial({
52 | blending: AdditiveBlending,
53 | opacity: 0.4,
54 | transparent: true,
55 | depthWrite: false,
56 | color: new Color(0x003a62)
57 | })
58 |
59 | this.mesh = new Mesh()
60 |
61 | this.lineCount = 700
62 |
63 | this.counters = []
64 |
65 | for (let index = 0; index < this.lineCount; index++) {
66 | const randIndex1 = Math.floor(Math.random() * this.coords.length)
67 | const randIndex2 = Math.floor(Math.random() * this.coords.length)
68 | this.addLine(randIndex1, randIndex2)
69 | }
70 |
71 | super.init()
72 | }
73 |
74 | addLine (index1, index2) {
75 | this.counters.push(Math.floor(Math.random() * this.config.curveSegments))
76 |
77 | const start = this.coords[index1]
78 | const end = this.coords[index2]
79 |
80 | if (typeof start === 'undefined' || typeof end === 'undefined') {
81 | return
82 | }
83 |
84 | const { spline } = this.getSplineFromCoords([
85 | start.lat,
86 | start.long,
87 | end.lat,
88 | end.long
89 | ])
90 |
91 | // add curve geometry
92 | const curveGeometry = new BufferGeometry()
93 | const points = new Float32Array(this.config.curveSegments * 3)
94 | const vertices = spline.getPoints(this.config.curveSegments - 1)
95 |
96 | for (let i = 0, j = 0; i < vertices.length; i++) {
97 | const vertex = vertices[i]
98 | points[j++] = vertex.x
99 | points[j++] = vertex.y
100 | points[j++] = vertex.z
101 | }
102 |
103 | curveGeometry.setAttribute('position', new BufferAttribute(points, 3))
104 | curveGeometry.setDrawRange(0, 0)
105 |
106 | let mesh = new Line(curveGeometry, this.material)
107 |
108 | this.mesh.add(mesh)
109 | }
110 |
111 | addNode (data) {
112 | this.coords.push(data)
113 | for (let index = 0; index < 10; index++) {
114 | this.addLine(this.coords.length - 1, Math.floor(Math.random() * this.coords.length))
115 | }
116 | }
117 |
118 | renderFrame (args) {
119 | this.mesh.children.forEach((line, index) => {
120 | this.counters[index] += (args.dt * 30.0)
121 |
122 | if (this.counters[index] > this.config.curveSegments) {
123 | this.counters[index] = Math.floor(Math.random() * this.config.curveSegments)
124 | }
125 |
126 | line.geometry.setDrawRange(0, this.counters[index])
127 | })
128 |
129 | super.renderFrame()
130 | }
131 | }
132 |
133 | export default PathsClass
134 |
--------------------------------------------------------------------------------
/src/components/classes/PickerSceneClass.js:
--------------------------------------------------------------------------------
1 | import {
2 | Scene
3 | } from 'three'
4 |
5 | import BaseClass from './BaseClass'
6 |
7 | class PickerSceneClass extends BaseClass {
8 | init () {
9 | this.scene = new Scene()
10 |
11 | super.init()
12 | }
13 |
14 | destroy () {
15 | this.scene.traverse(function (object) {
16 | if (object.geometry) {
17 | object.geometry.dispose()
18 | }
19 | if (object.material) {
20 | object.material.dispose()
21 | }
22 | })
23 |
24 | super.destroy()
25 | }
26 | }
27 |
28 | export default PickerSceneClass
29 |
--------------------------------------------------------------------------------
/src/components/classes/PickersClass.js:
--------------------------------------------------------------------------------
1 | import {
2 | Mesh,
3 | BufferGeometry,
4 | CylinderGeometry,
5 | InstancedBufferGeometry,
6 | InstancedBufferAttribute,
7 | Color,
8 | Object3D,
9 | ShaderMaterial,
10 | WebGLRenderTarget,
11 | LinearFilter
12 | } from 'three'
13 |
14 | import BaseClass from './BaseClass'
15 |
16 | import { latLongToCartesian } from '../../helpers/math'
17 |
18 | // shaders
19 | import fragmentShader from '../../shaders/pickers.frag'
20 | import vertexShader from '../../shaders/pickers.vert'
21 |
22 | import RendererClass from './RendererClass'
23 | import CameraClass from './CameraClass'
24 | import PickerSceneClass from './PickerSceneClass'
25 | import MouseClass from './MouseClass'
26 | import MarkersClass from './MarkersClass'
27 |
28 | class PickersClass extends BaseClass {
29 | init (data) {
30 | this.lastHoveredID = -1
31 | this.lastSelectedID = -1
32 | this.hoveredIP = ''
33 | this.isHovering = false
34 |
35 | this.pickingTexture = new WebGLRenderTarget(window.innerWidth, window.innerHeight)
36 | this.pickingTexture.texture.minFilter = LinearFilter
37 | this.pickingTexture.texture.generateMipmaps = false
38 |
39 | let coords = data
40 |
41 | this.nodeCount = coords.length
42 | this.instanceTotal = 1000 // max number of instances
43 |
44 | this.material = new PickersMaterial({})
45 |
46 | const tubeGeo = new CylinderGeometry(0.0, 0.005, 0.06, 3)
47 | const tubeBufferGeo = new BufferGeometry().fromGeometry(tubeGeo)
48 | this.geometry = new InstancedBufferGeometry().copy(tubeBufferGeo)
49 | this.geometry.rotateX(Math.PI / 2)
50 |
51 | this.offsetsAttr = new InstancedBufferAttribute(new Float32Array(this.instanceTotal * 3).fill(99999), 3)
52 | this.idAttr = new InstancedBufferAttribute(new Float32Array(this.instanceTotal), 1)
53 | this.scalesAttr = new InstancedBufferAttribute(new Float32Array(this.instanceTotal), 1)
54 | this.quaternionsAttr = new InstancedBufferAttribute(new Float32Array(this.instanceTotal * 4), 4)
55 | this.pickingColorsAttr = new InstancedBufferAttribute(new Float32Array(this.instanceTotal * 3), 3)
56 |
57 | this.geometry.setAttribute('offset', this.offsetsAttr)
58 | this.geometry.setAttribute('scale', this.scalesAttr)
59 | this.geometry.setAttribute('quaternion', this.quaternionsAttr)
60 | this.geometry.setAttribute('id', this.idAttr)
61 | this.geometry.setAttribute('pickerColor', this.pickingColorsAttr)
62 |
63 | this.pickColor = new Color(0x999999)
64 |
65 | for (let index = 0; index < this.nodeCount; index++) {
66 | if (typeof coords[index] !== 'undefined') {
67 | this.addNodeGeoData(coords[index], index)
68 | }
69 | }
70 |
71 | this.mesh = new Mesh(this.geometry, this.material)
72 | this.mesh.frustumCulled = false
73 | }
74 |
75 | addNodeGeoData (data, index) {
76 | this.pickColor.setHex(index + 1)
77 | this.pickingColorsAttr.array[index * 3 + 0] = this.pickColor.r
78 | this.pickingColorsAttr.array[index * 3 + 1] = this.pickColor.g
79 | this.pickingColorsAttr.array[index * 3 + 2] = this.pickColor.b
80 |
81 | const pos = latLongToCartesian(data.lat, data.long, this.config.scene.sphereRadius * 1.05)
82 |
83 | const x = this.offsetsAttr.array[index * 3 + 0] = pos.x
84 | const y = this.offsetsAttr.array[index * 3 + 1] = pos.y
85 | const z = this.offsetsAttr.array[index * 3 + 2] = pos.z
86 |
87 | const dummyObject = new Object3D()
88 | dummyObject.position.set(x, y, z)
89 | dummyObject.lookAt(0, 0, 0)
90 |
91 | this.quaternionsAttr.array[index * 4 + 0] = dummyObject.quaternion.x
92 | this.quaternionsAttr.array[index * 4 + 1] = dummyObject.quaternion.y
93 | this.quaternionsAttr.array[index * 4 + 2] = dummyObject.quaternion.z
94 | this.quaternionsAttr.array[index * 4 + 3] = dummyObject.quaternion.w
95 |
96 | this.scalesAttr.array[index] = 2.0
97 |
98 | this.idAttr.array[index] = index
99 |
100 | this.geometry.attributes.offset.needsUpdate = true
101 | this.geometry.attributes.scale.needsUpdate = true
102 | this.geometry.attributes.quaternion.needsUpdate = true
103 | this.geometry.attributes.id.needsUpdate = true
104 | this.geometry.attributes.pickerColor.needsUpdate = true
105 | }
106 |
107 | addNode (data) {
108 | this.nodeCount += 1
109 | this.addNodeGeoData(data, this.nodeCount)
110 | }
111 |
112 | resize (width, height) {
113 | this.width = width
114 | this.height = height
115 |
116 | this.pickingTexture.setSize(this.width, this.height)
117 | }
118 |
119 | renderFrame () {
120 | RendererClass.getInstance().renderer.autoClear = true
121 | RendererClass.getInstance().renderer.setRenderTarget(this.pickingTexture)
122 | RendererClass.getInstance().renderer.render(PickerSceneClass.getInstance().scene, CameraClass.getInstance().camera)
123 |
124 | const pixelBuffer = new Uint8Array(4)
125 | pixelBuffer[3] = 255
126 |
127 | RendererClass.getInstance().renderer.readRenderTargetPixels(
128 | this.pickingTexture,
129 | MouseClass.getInstance().mousePos.x,
130 | this.pickingTexture.height - (MouseClass.getInstance().mousePos.y),
131 | 1,
132 | 1,
133 | pixelBuffer
134 | )
135 |
136 | const id = (pixelBuffer[0] << 16) | (pixelBuffer[1] << 8) | (pixelBuffer[2] - 1)
137 |
138 | if (this.lastHoveredID !== id) {
139 | this.lastHoveredID = id
140 |
141 | if (typeof MarkersClass.getInstance().ipMap[id] !== 'undefined') {
142 | this.hoveredData = MarkersClass.getInstance().ipMap[id]
143 | MarkersClass.getInstance().setSelectionRef(this.hoveredData.pos)
144 |
145 | this.emit('nodeMouseOver', this.hoveredData)
146 |
147 | this.isHovering = true
148 |
149 | document.body.style.cursor = 'pointer'
150 | } else {
151 | this.isHovering = false
152 |
153 | this.emit('nodeMouseOut', {})
154 |
155 | // MarkersClass.getInstance().setSelectionRef(new Vector3(0, 0, 0))
156 |
157 | document.body.style.cursor = 'default'
158 | }
159 | }
160 |
161 | super.renderFrame()
162 | }
163 | }
164 |
165 | class PickersMaterial extends ShaderMaterial {
166 | constructor (config) {
167 | super(config)
168 | this.type = 'ShaderMaterial'
169 |
170 | this.uniforms = {}
171 |
172 | this.vertexShader = vertexShader
173 | this.fragmentShader = fragmentShader
174 | }
175 | }
176 |
177 | export default PickersClass
178 |
--------------------------------------------------------------------------------
/src/components/classes/PointLightClass.js:
--------------------------------------------------------------------------------
1 | import {
2 | PointLight
3 | } from 'three'
4 |
5 | import BaseClass from './BaseClass'
6 |
7 | class PointLightClass extends BaseClass {
8 | init () {
9 | this.light = new PointLight(0xffffff, 3.0)
10 | this.light.position.set(0, 5, 0)
11 |
12 | super.init()
13 | }
14 | }
15 |
16 | export default PointLightClass
17 |
--------------------------------------------------------------------------------
/src/components/classes/QuadCameraClass.js:
--------------------------------------------------------------------------------
1 | import {
2 | OrthographicCamera
3 | } from 'three'
4 |
5 | import BaseClass from './BaseClass'
6 |
7 | class QuadCameraClass extends BaseClass {
8 | init () {
9 | this.camera = new OrthographicCamera()
10 | this.camera.position.z = 1
11 |
12 | super.init()
13 | }
14 |
15 | resize (width, height) {
16 | this.camera.aspect = width / height
17 | this.camera.updateProjectionMatrix()
18 | super.resize()
19 | }
20 | }
21 |
22 | export default QuadCameraClass
23 |
--------------------------------------------------------------------------------
/src/components/classes/RendererClass.js:
--------------------------------------------------------------------------------
1 | import {
2 | WebGLRenderer
3 | } from 'three'
4 |
5 | import BaseClass from './BaseClass'
6 | import CameraClass from './CameraClass'
7 | import GlobeSceneClass from './GlobeSceneClass'
8 |
9 | class RendererClass extends BaseClass {
10 | init () {
11 | this.canvas = document.querySelector('#' + this.config.scene.canvasID)
12 | this.renderer = new WebGLRenderer({
13 | antialias: false,
14 | canvas: this.canvas,
15 | powerPreference: 'high-performance',
16 | alpha: true
17 | })
18 |
19 | this.renderer.setClearColor(0xffffff, 0)
20 |
21 | super.init()
22 | }
23 |
24 | resize (width, height) {
25 | this.renderer.setSize(width, height, false)
26 |
27 | super.resize()
28 | }
29 |
30 | renderFrame ({
31 | renderTarget = null,
32 | scene = GlobeSceneClass.getInstance().scene,
33 | camera = CameraClass.getInstance().camera
34 | } = {}) {
35 | this.renderer.setRenderTarget(renderTarget)
36 | this.renderer.render(scene, camera)
37 |
38 | super.renderFrame()
39 | }
40 |
41 | destroy () {
42 | this.renderer.dispose()
43 |
44 | super.destroy()
45 | }
46 | }
47 |
48 | export default RendererClass
49 |
--------------------------------------------------------------------------------
/src/components/classes/TouchClass.js:
--------------------------------------------------------------------------------
1 | import {
2 | Vector2
3 | } from 'three'
4 |
5 | import BaseClass from './BaseClass'
6 | import RendererClass from './RendererClass'
7 |
8 | class TouchClass extends BaseClass {
9 | init () {
10 | this.touchPos = new Vector2()
11 | this.prevTouchPos = new Vector2(0, 0)
12 | this.normalizedTouchPos = new Vector2()
13 |
14 | this.touchDelta = new Vector2(0, 0)
15 | this.movement = new Vector2()
16 | this.touchPos = new Vector2()
17 | this.normalizedTouchPos = new Vector2()
18 | this.prevNormalizedTouchPos = new Vector2()
19 | }
20 |
21 | onTouchMove (e) {
22 | if (typeof e.touches[0] === 'undefined') {
23 | return
24 | } else {
25 | e = e.touches[0]
26 | }
27 |
28 | this.prevNormalizedTouchPos.x = this.normalizedTouchPos.x
29 | this.prevNormalizedTouchPos.y = this.normalizedTouchPos.y
30 |
31 | this.prevTouchPos.x = this.touchPos.x
32 | this.prevTouchPos.y = this.touchPos.y
33 |
34 | this.touchPos.x = e.clientX - RendererClass.getInstance().renderer.domElement.offsetLeft
35 | this.touchPos.y = e.clientY - RendererClass.getInstance().renderer.domElement.offsetTop
36 |
37 | this.touchDelta = this.touchPos.clone().sub(this.prevTouchPos)
38 |
39 | this.movement.x = this.touchDelta.x
40 | this.movement.y = this.touchDelta.y
41 |
42 | const x = e.clientX - RendererClass.getInstance().renderer.domElement.offsetLeft
43 | const y = e.clientY - RendererClass.getInstance().renderer.domElement.offsetTop
44 |
45 | this.normalizedTouchPos.x = x / RendererClass.getInstance().renderer.domElement.width
46 | this.normalizedTouchPos.y = 1 - y / RendererClass.getInstance().renderer.domElement.height
47 | }
48 |
49 | renderFrame ({ dt } = {}) {
50 | super.renderFrame()
51 | }
52 | }
53 |
54 | export default TouchClass
55 |
--------------------------------------------------------------------------------
/src/data/test.js:
--------------------------------------------------------------------------------
1 | export const coords = [
2 | { lat: 51.229424, long: -2.322441 },
3 | { lat: 31.0449, long: 121.4012 },
4 | { lat: 38.6583, long: -77.2481 },
5 | { lat: 37.5112, long: 126.9741 },
6 | { lat: 32.3485, long: -97.3311 },
7 | { lat: 50.1188, long: 8.6843 },
8 | { lat: 40.4143, long: -3.7016 },
9 | { lat: 51.2993, long: 9.491 },
10 | { lat: 35.1731, long: 149.107 },
11 | { lat: -27.4833, long: 153.0053 },
12 | { lat: 35.6882, long: 139.7532 },
13 | { lat: 35.6882, long: 139.7532 },
14 | { lat: 37.3388, long: -121.8914 },
15 | { lat: 37.3388, long: -121.8914 },
16 | { lat: 39.9653, long: -83.0235 },
17 | { lat: 34.8224, long: 135.4301 },
18 | { lat: 52.3902, long: 4.6568 },
19 | { lat: 38.7095, long: -78.1539 },
20 | { lat: 53.3338, long: -6.2488 },
21 | { lat: 56.752, long: -111.4398 },
22 | { lat: 43.6547, long: -79.3623 },
23 | { lat: -27.4737, long: 153.0169 },
24 | { lat: -33.8591, long: 151.2002 },
25 | { lat: 37.3387, long: -121.8914 },
26 | { lat: 51.5353, long: -0.6658 },
27 | { lat: 51.2993, long: 9.491 },
28 | { lat: 49.405, long: 11.1617 },
29 | { lat: 49.405, long: 11.1617 },
30 | { lat: 37.751, long: -97.822 },
31 | { lat: 51.2993, long: 9.491 },
32 | { lat: 49.405, long: 11.1617 },
33 | { lat: 49.405, long: 11.1617 },
34 | { lat: 37.751, long: -97.822 },
35 | { lat: 51.2993, long: 9.491 },
36 | { lat: 35.7965, long: -78.7981 },
37 | { lat: 45.4995, long: -73.5848 },
38 | { lat: 52.2777, long: -0.8284 },
39 | { lat: 56.1174, long: -3.5345 },
40 | { lat: 37.751, long: -97.822 },
41 | { lat: 50.4671, long: -104.541 },
42 | { lat: 48.8582, long: 2.3387 },
43 | { lat: 37.3417, long: -121.9753 },
44 | { lat: 40.8364, long: -74.1403 },
45 | { lat: 50.1084, long: 8.6841 },
46 | { lat: 49.405, long: 11.1617 },
47 | { lat: 17.3846, long: 78.4574 },
48 | { lat: 51.7047, long: -0.4213 },
49 | { lat: 52.5258, long: 5.7235 },
50 | { lat: 47.3848, long: 9.9014 },
51 | { lat: 61.4545, long: 5.8395 },
52 | { lat: 47.3664, long: 8.5546 },
53 | { lat: -3.1145, long: -60.0278 },
54 | { lat: 1.2929, long: 103.8547 },
55 | { lat: 35.6882, long: 139.7532 },
56 | { lat: 50.1188, long: 8.6843 },
57 | { lat: 50.1188, long: 8.6843 },
58 | { lat: 50.1188, long: 8.6843 },
59 | { lat: 50.1188, long: 8.6843 },
60 | { lat: 53.3338, long: -6.2488 },
61 | { lat: 39.9653, long: -83.0235 },
62 | { lat: 37.3388, long: -121.8914 },
63 | { lat: 25.2925, long: 51.5321 },
64 | { lat: 46.6682, long: 11.1595 },
65 | { lat: 46.6682, long: 11.1595 },
66 | { lat: 42.7, long: 23.3333 },
67 | { lat: 52.4855, long: 13.4392 },
68 | { lat: 51.2993, long: 9.491 },
69 | { lat: 59.9308, long: 30.1918 },
70 | { lat: 55.7386, long: 37.6068 },
71 | { lat: 55.7386, long: 37.6068 },
72 | { lat: 55.6059, long: 13.0007 },
73 | { lat: -32.4833, long: -58.2283 },
74 | { lat: -31.4015, long: -64.1803 },
75 | { lat: 19.4065, long: -99.0837 },
76 | { lat: -20.3857, long: -40.3206 },
77 | { lat: 39.0448, long: -77.6042 },
78 | { lat: 36.1682, long: -115.2166 },
79 | { lat: 45.4168, long: -73.4992 },
80 | { lat: 47.8008, long: 0.0443 },
81 | { lat: 49.405, long: 11.1617 },
82 | { lat: -33.9165, long: 18.4155 },
83 | { lat: 32.7787, long: -96.8217 },
84 | { lat: 47.6859, long: -122.2994 },
85 | { lat: 52.3824, long: 4.8995 },
86 | { lat: 48.8582, long: 2.3387 },
87 | { lat: 37.3417, long: -121.9753 },
88 | { lat: 49.405, long: 11.1617 },
89 | { lat: 43.9982, long: -79.4625 },
90 | { lat: 56.1663, long: 10.1987 },
91 | { lat: 47.4925, long: 19.0514 },
92 | { lat: 47.3664, long: 8.5546 },
93 | { lat: 43.2001, long: -79.5663 },
94 | { lat: 59.3307, long: 18.0718 },
95 | { lat: 52.3534, long: 4.9087 },
96 | { lat: 64.7805, long: -147.3694 },
97 | { lat: 36.0861, long: -115.2541 },
98 | { lat: 45.4978, long: -73.5485 },
99 | { lat: 35.6882, long: 139.7532 },
100 | { lat: 50.1188, long: 8.6843 },
101 | { lat: 50.1188, long: 8.6843 },
102 | { lat: 50.1188, long: 8.6843 },
103 | { lat: 50.1188, long: 8.6843 },
104 | { lat: 50.1188, long: 8.6843 },
105 | { lat: 50.1188, long: 8.6843 },
106 | { lat: 50.1188, long: 8.6843 },
107 | { lat: 50.1188, long: 8.6843 },
108 | { lat: 50.1188, long: 8.6843 },
109 | { lat: 39.9653, long: -83.0235 },
110 | { lat: 39.0481, long: -77.4728 },
111 | { lat: 46.3682, long: 17.7945 },
112 | { lat: 53.3338, long: -6.2488 },
113 | { lat: 38.6583, long: -77.2481 },
114 | { lat: 37.751, long: -97.822 },
115 | { lat: 37.751, long: -97.822 },
116 | { lat: 37.751, long: -97.822 },
117 | { lat: 37.751, long: -97.822 },
118 | { lat: 27.8485, long: -82.7944 },
119 | { lat: 38.6583, long: -77.2481 },
120 | { lat: 35, long: 105 },
121 | { lat: 50.941, long: 5.8016 },
122 | { lat: 48.8582, long: 2.3387 },
123 | { lat: 41.3891, long: 2.1611 },
124 | { lat: 37.3388, long: -121.8914 },
125 | { lat: -26.2309, long: 28.0583 },
126 | { lat: 41.6006, long: -93.6112 },
127 | { lat: 41.6006, long: -93.6112 },
128 | { lat: 52.3534, long: 4.9087 },
129 | { lat: 45.8491, long: -119.7143 },
130 | { lat: 45.8491, long: -119.7143 },
131 | { lat: 60.1756, long: 24.9342 },
132 | { lat: 42.7, long: 23.3333 },
133 | { lat: 42.7, long: 23.3333 },
134 |
135 | { lat: 42.7, long: 23.3333 },
136 | { lat: 42.7, long: 23.3333 },
137 | { lat: 32.7787, long: -96.8217 },
138 | { lat: 40.5511, long: -74.4606 },
139 | { lat: 49.05, long: 14.4333 },
140 | { lat: 33.5631, long: -117.2738 },
141 | { lat: 37.5112, long: 126.9741 },
142 | { lat: 33.7485, long: -84.3871 },
143 | { lat: 50.9266, long: -113.9726 },
144 | { lat: 51.5132, long: -0.0961 },
145 | { lat: 48.8582, long: 2.3387 },
146 | { lat: 52.3615, long: 4.6419 },
147 | { lat: 52.3824, long: 4.8995 },
148 | { lat: 48.8607, long: 2.3281 },
149 | { lat: 48.8607, long: 2.3281 },
150 | { lat: 43.6319, long: -79.3716 },
151 | { lat: 48.8582, long: 2.3387 },
152 | { lat: 35.6882, long: 139.7532 },
153 | { lat: 35.6882, long: 139.7532 },
154 | { lat: 1.2929, long: 103.8547 },
155 | { lat: 50.1188, long: 8.6843 },
156 | { lat: 37.3388, long: -121.8914 },
157 | { lat: 37.3388, long: -121.8914 },
158 | { lat: 50.1188, long: 8.6843 },
159 | { lat: 35.6882, long: 139.7532 },
160 | { lat: 37.3388, long: -121.8914 },
161 | { lat: 37.3388, long: -121.8914 },
162 | { lat: 37.3388, long: -121.8914 },
163 | { lat: 37.3388, long: -121.8914 },
164 | { lat: 37.3388, long: -121.8914 },
165 | { lat: 37.3388, long: -121.8914 },
166 | { lat: 39.0481, long: -77.4728 },
167 | { lat: 45.5063, long: -73.5794 },
168 | { lat: 39.0481, long: -77.4728 },
169 | { lat: 35.6882, long: 139.7532 },
170 | { lat: 35.69, long: 139.69 },
171 | { lat: 35.7138, long: 0.7484 },
172 | { lat: -33.8591, long: 151.2002 },
173 | { lat: 51.4476, long: 7.0122 },
174 | { lat: 45.4017, long: -74.0335 },
175 | { lat: 53.2158, long: -6.6669 },
176 | { lat: 40.2342, long: -111.6442 },
177 | { lat: 41.7579, long: -88.2934 },
178 | { lat: 40.8364, long: -74.1403 },
179 | { lat: 44.1035, long: -102.0159 },
180 | { lat: 40.739, long: -74.1697 },
181 | { lat: 37.045, long: -76.4067 },
182 | { lat: 34.185, long: -118.7669 },
183 | { lat: 50.8696, long: 3.8105 },
184 | { lat: 51.2993, long: 9.491 },
185 | { lat: 52.5739, long: 13.3224 },
186 | { lat: 51.2993, long: 9.491 },
187 | { lat: 51.2993, long: 9.491 },
188 | { lat: 51.2993, long: 9.491 },
189 | { lat: 51.2993, long: 9.491 },
190 | { lat: 51.2993, long: 9.491 },
191 | { lat: 38.9942, long: -1.8564 },
192 | { lat: 52.2524, long: 6.1595 },
193 | { lat: 49.85, long: 18.3667 },
194 | { lat: 49.85, long: 18.3667 },
195 | { lat: 52.2859, long: 4.8667 },
196 | { lat: 48.8543, long: 2.3527 },
197 | { lat: 45.7537, long: 21.2257 },
198 | { lat: 41.3891, long: 2.1611 },
199 | { lat: 52.0626, long: 5.1191 },
200 | { lat: 50.8168, long: 5.1865 },
201 | { lat: 52.5174, long: 13.3985 },
202 | { lat: 46.5189, long: 6.636 },
203 | { lat: 51.2993, long: 9.491 },
204 | { lat: 51.4476, long: 7.0122 },
205 | { lat: 51.6004, long: -0.1168 },
206 | { lat: 51.6, long: -0.2167 },
207 | { lat: 51.5167, long: -0.0333 },
208 | { lat: 52.4854, long: 6.1145 },
209 | { lat: 51.6573, long: 4.8688 },
210 | { lat: 51.2993, long: 9.491 },
211 | { lat: 42.5499, long: -3.3232 },
212 | { lat: 66.9122, long: 13.6296 },
213 | { lat: 46.0503, long: 14.5046 },
214 | { lat: 48.7662, long: 9.1833 },
215 | { lat: 44.1392, long: 4.8079 },
216 | { lat: 51.197, long: 3.1789 },
217 | { lat: 34.6874, long: 33.0366 },
218 | { lat: 43.5611, long: -5.9191 },
219 | { lat: 45.5534, long: 8.9792 },
220 | { lat: 51.0955, long: 4.509 },
221 | { lat: 60.1708, long: 24.9375 },
222 | { lat: 60.1708, long: 24.9375 },
223 | { lat: 60.1708, long: 24.9375 },
224 | { lat: 60.1708, long: 24.9375 },
225 | { lat: 60.1708, long: 24.9375 },
226 | { lat: 60.1708, long: 24.9375 },
227 | { lat: 60.1708, long: 24.9375 },
228 | { lat: 32.8137, long: -96.8704 },
229 | { lat: 35.3869, long: -119.1745 },
230 | { lat: 40.76, long: -73.5318 },
231 | { lat: 43.0351, long: -108.2024 },
232 | { lat: 21.3133, long: -157.823 }
233 |
234 | ]
235 |
--------------------------------------------------------------------------------
/src/helpers/TextureHelper.js:
--------------------------------------------------------------------------------
1 | import {
2 | Vector3,
3 | DataTexture,
4 | RGBAFormat,
5 | FloatType,
6 | NearestFilter
7 | } from 'three'
8 |
9 | export default class TextureHelper {
10 | constructor (args) {
11 | this.config = args.config
12 | }
13 |
14 | setPointCount (pointCount) {
15 | this.pointCount = pointCount
16 | }
17 |
18 | setTextureSize (pointCount) {
19 | this.setPointCount(pointCount)
20 |
21 | let width = 1
22 | let height = 1
23 |
24 | while (height * width < this.pointCount) {
25 | width *= 2
26 | if (height * width >= this.pointCount) {
27 | break
28 | }
29 | height *= 2
30 | }
31 |
32 | this.textureWidth = width
33 | this.textureHeight = height
34 | }
35 |
36 | getNodeTextureLocation (nodeID) {
37 | return {
38 | x: (nodeID % this.textureWidth) * (1 / this.textureWidth) + (1 / (this.textureWidth * 2)),
39 | y: Math.floor(nodeID / this.textureWidth) * (1 / this.textureHeight) + (1 / (this.textureHeight * 2))
40 | }
41 | }
42 |
43 | createPositionTexture ({
44 | defaultPositions = new Float32Array()
45 | } = {}) {
46 | let initialTextureArray = new Float32Array(this.textureWidth * this.textureHeight * 4)
47 | let textureArray = new Float32Array(this.textureWidth * this.textureHeight * 4)
48 | let lifeArray = []
49 | let step = 4
50 |
51 | for (let i = 0; i < this.pointCount; i++) {
52 | let location = new Vector3(
53 | ((i) % (this.config.particleScene.width / step)) * step,
54 | (Math.floor((i) / (this.config.particleScene.width / step))) * step,
55 | 0
56 | )
57 |
58 | let lifeDuration = Math.ceil(Math.random() * this.config.scene.particleLifeMax)
59 |
60 | textureArray[i * 4 + 0] = location.x
61 | textureArray[i * 4 + 1] = location.y
62 | textureArray[i * 4 + 2] = location.z
63 | textureArray[i * 4 + 3] = lifeDuration
64 |
65 | initialTextureArray[i * 4 + 0] = Math.random() * this.config.particleScene.width
66 | initialTextureArray[i * 4 + 1] = Math.random() * this.config.particleScene.height
67 | initialTextureArray[i * 4 + 2] = Math.random() * this.config.particleScene.height
68 | initialTextureArray[i * 4 + 3] = 0
69 | }
70 |
71 | let positionTexture = new DataTexture(
72 | textureArray,
73 | this.textureWidth,
74 | this.textureHeight,
75 | RGBAFormat,
76 | FloatType
77 | )
78 | positionTexture.minFilter = NearestFilter
79 | positionTexture.magFilter = NearestFilter
80 | positionTexture.generateMipmaps = false
81 | positionTexture.needsUpdate = true
82 |
83 | let initialPositionTexture = new DataTexture(
84 | initialTextureArray,
85 | this.textureWidth,
86 | this.textureHeight,
87 | RGBAFormat,
88 | FloatType
89 | )
90 | initialPositionTexture.minFilter = NearestFilter
91 | initialPositionTexture.magFilter = NearestFilter
92 | initialPositionTexture.generateMipmaps = false
93 | initialPositionTexture.needsUpdate = true
94 |
95 | return {
96 | positionTexture: positionTexture,
97 | initialPositionTexture: initialPositionTexture,
98 | lifeArray: lifeArray
99 | }
100 | }
101 | }
102 |
--------------------------------------------------------------------------------
/src/helpers/math.js:
--------------------------------------------------------------------------------
1 | import { Vector3 } from 'three'
2 |
3 | export function lerp (v0, v1, t) {
4 | return v0 * (1 - t) + v1 * t
5 | }
6 |
7 | export function latLongToCartesian (lat, long, radius) {
8 | const phi = (90 - lat) * (Math.PI / 180)
9 | const theta = (long + 180) * (Math.PI / 180)
10 | const x = radius * Math.sin(phi) * Math.cos(theta) * -1
11 | const z = radius * Math.sin(phi) * Math.sin(theta)
12 | const y = radius * Math.cos(phi)
13 | return new Vector3(x, y, z)
14 | }
15 |
16 | export function clamp (num, min, max) {
17 | return num <= min ? min : (num >= max ? max : num)
18 | }
19 |
--------------------------------------------------------------------------------
/src/helpers/utility.js:
--------------------------------------------------------------------------------
1 | export function getUrlParameter (name) {
2 | name = name.replace(/[\[]/, '\\[').replace(/[\]]/, '\\]')
3 | var regex = new RegExp('[\\?&]' + name + '=([^]*)')
4 | var results = regex.exec(window.location.search)
5 | return results === null ? '' : decodeURIComponent(results[1].replace(/\+/g, ' '))
6 | };
7 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | import Main from './components/Main'
2 |
3 | export {
4 | Main
5 | }
6 |
--------------------------------------------------------------------------------
/src/libs/Detector.js:
--------------------------------------------------------------------------------
1 | const ua = (navigator.userAgent || navigator.vendor || window.opera).toLowerCase()
2 |
3 | const Detector = {
4 | isRetina: window.devicePixelRatio && window.devicePixelRatio >= 1.5,
5 | isChrome: ua.indexOf('chrome') > -1,
6 | isFirefox: ua.indexOf('firefox') > -1,
7 | isSafari: ua.indexOf('safari') > -1,
8 | isEdge: ua.indexOf('edge') > -1,
9 | isIE: ua.indexOf('msie') > -1,
10 | isMobile: /(iPad|iPhone|Android)/i.test(ua),
11 | isIOS: /(iPad|iPhone)/i.test(ua)
12 | }
13 |
14 | export default Detector
15 |
--------------------------------------------------------------------------------
/src/libs/post/CopyShader.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author alteredq / http://alteredqualia.com/
3 | *
4 | * Full-screen textured quad shader
5 | */
6 |
7 | const CopyShader = {
8 |
9 | uniforms: {
10 |
11 | 'tDiffuse': { value: null },
12 | 'opacity': { value: 1.0 }
13 |
14 | },
15 |
16 | vertexShader: [
17 |
18 | 'varying vec2 vUv;',
19 |
20 | 'void main() {',
21 |
22 | 'vUv = uv;',
23 | 'gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );',
24 |
25 | '}'
26 |
27 | ].join('\n'),
28 |
29 | fragmentShader: [
30 |
31 | 'uniform float opacity;',
32 |
33 | 'uniform sampler2D tDiffuse;',
34 |
35 | 'varying vec2 vUv;',
36 |
37 | 'void main() {',
38 |
39 | 'vec4 texel = texture2D( tDiffuse, vUv );',
40 | 'gl_FragColor = opacity * texel;',
41 |
42 | '}'
43 |
44 | ].join('\n')
45 |
46 | }
47 |
48 | export default CopyShader
49 |
--------------------------------------------------------------------------------
/src/libs/post/EffectComposer.js:
--------------------------------------------------------------------------------
1 |
2 | import {
3 | ShaderMaterial,
4 | LinearFilter,
5 | WebGLRenderTarget,
6 | RGBAFormat,
7 | UniformsUtils,
8 | OrthographicCamera,
9 | Scene,
10 | Mesh,
11 | PlaneBufferGeometry,
12 | Vector2
13 | } from 'three'
14 |
15 | import CopyShader from './CopyShader'
16 |
17 | /**
18 | * @author alteredq / http://alteredqualia.com/
19 | */
20 |
21 | const EffectComposer = function (renderer, renderTarget) {
22 | this.renderer = renderer
23 |
24 | if (renderTarget === undefined) {
25 | var parameters = {
26 | minFilter: LinearFilter,
27 | magFilter: LinearFilter,
28 | format: RGBAFormat,
29 | stencilBuffer: false
30 | }
31 |
32 | var size = renderer.getDrawingBufferSize(new Vector2())
33 | renderTarget = new WebGLRenderTarget(size.width, size.height, parameters)
34 | renderTarget.texture.name = 'EffectComposer.rt1'
35 | }
36 |
37 | this.renderTarget1 = renderTarget
38 | this.renderTarget2 = renderTarget.clone()
39 | this.renderTarget2.texture.name = 'EffectComposer.rt2'
40 |
41 | this.writeBuffer = this.renderTarget1
42 | this.readBuffer = this.renderTarget2
43 |
44 | this.passes = []
45 |
46 | // dependencies
47 |
48 | if (CopyShader === undefined) {
49 | console.error('EffectComposer relies on CopyShader')
50 | }
51 |
52 | if (ShaderPass === undefined) {
53 | console.error('EffectComposer relies on ShaderPass')
54 | }
55 |
56 | this.copyPass = new ShaderPass(CopyShader)
57 | }
58 |
59 | Object.assign(EffectComposer.prototype, {
60 |
61 | swapBuffers: function () {
62 | var tmp = this.readBuffer
63 | this.readBuffer = this.writeBuffer
64 | this.writeBuffer = tmp
65 | },
66 |
67 | addPass: function (pass) {
68 | this.passes.push(pass)
69 |
70 | var size = this.renderer.getDrawingBufferSize(new Vector2())
71 | pass.setSize(size.width, size.height)
72 | },
73 |
74 | insertPass: function (pass, index) {
75 | this.passes.splice(index, 0, pass)
76 | },
77 |
78 | render: function (delta) {
79 | var maskActive = false
80 |
81 | var pass
82 | var i
83 | var il = this.passes.length
84 |
85 | for (i = 0; i < il; i++) {
86 | pass = this.passes[i]
87 |
88 | if (pass.enabled === false) continue
89 |
90 | pass.render(this.renderer, this.writeBuffer, this.readBuffer, delta, maskActive)
91 |
92 | if (pass.needsSwap) {
93 | if (maskActive) {
94 | var context = this.renderer.context
95 |
96 | context.stencilFunc(context.NOTEQUAL, 1, 0xffffffff)
97 |
98 | this.copyPass.render(this.renderer, this.writeBuffer, this.readBuffer, delta)
99 |
100 | context.stencilFunc(context.EQUAL, 1, 0xffffffff)
101 | }
102 |
103 | this.swapBuffers()
104 | }
105 |
106 | /* if (MaskPass !== undefined) {
107 | if (pass instanceof MaskPass) {
108 | maskActive = true
109 | } else if (pass instanceof ClearMaskPass) {
110 | maskActive = false
111 | }
112 | } */
113 | }
114 | },
115 |
116 | reset: function (renderTarget) {
117 | if (renderTarget === undefined) {
118 | var size = this.renderer.getDrawingBufferSize(new Vector2())
119 |
120 | renderTarget = this.renderTarget1.clone()
121 | renderTarget.setSize(size.width, size.height)
122 | }
123 |
124 | this.renderTarget1.dispose()
125 | this.renderTarget2.dispose()
126 | this.renderTarget1 = renderTarget
127 | this.renderTarget2 = renderTarget.clone()
128 |
129 | this.writeBuffer = this.renderTarget1
130 | this.readBuffer = this.renderTarget2
131 | },
132 |
133 | setSize: function (width, height) {
134 | this.renderTarget1.setSize(width, height)
135 | this.renderTarget2.setSize(width, height)
136 |
137 | for (var i = 0; i < this.passes.length; i++) {
138 | this.passes[i].setSize(width, height)
139 | }
140 | }
141 |
142 | })
143 |
144 | const Pass = function () {
145 | // if set to true, the pass is processed by the composer
146 | this.enabled = true
147 |
148 | // if set to true, the pass indicates to swap read and write buffer after rendering
149 | this.needsSwap = true
150 |
151 | // if set to true, the pass clears its buffer before rendering
152 | this.clear = false
153 |
154 | // if set to true, the result of the pass is rendered to screen
155 | this.renderToScreen = false
156 | }
157 |
158 | Object.assign(Pass.prototype, {
159 |
160 | setSize: function (width, height) {},
161 |
162 | render: function (renderer, writeBuffer, readBuffer, delta, maskActive) {
163 | console.error('Pass: .render() must be implemented in derived pass.')
164 | }
165 |
166 | })
167 |
168 | const ShaderPass = function (shader, textureID) {
169 | Pass.call(this)
170 |
171 | this.textureID = (textureID !== undefined) ? textureID : 'tDiffuse'
172 |
173 | if (shader instanceof ShaderMaterial) {
174 | this.uniforms = shader.uniforms
175 |
176 | this.material = shader
177 | } else if (shader) {
178 | this.uniforms = UniformsUtils.clone(shader.uniforms)
179 |
180 | this.material = new ShaderMaterial({
181 |
182 | defines: shader.defines || {},
183 | uniforms: this.uniforms,
184 | vertexShader: shader.vertexShader,
185 | fragmentShader: shader.fragmentShader
186 |
187 | })
188 | }
189 |
190 | this.camera = new OrthographicCamera(-1, 1, 1, -1, 0, 1)
191 | this.scene = new Scene()
192 |
193 | this.quad = new Mesh(new PlaneBufferGeometry(2, 2), null)
194 | this.quad.frustumCulled = false // Avoid getting clipped
195 | this.scene.add(this.quad)
196 | }
197 |
198 | ShaderPass.prototype = Object.assign(Object.create(Pass.prototype), {
199 |
200 | constructor: ShaderPass,
201 |
202 | render: function (renderer, writeBuffer, readBuffer, delta, maskActive) {
203 | if (this.uniforms[this.textureID]) {
204 | this.uniforms[this.textureID].value = readBuffer.texture
205 | }
206 |
207 | this.quad.material = this.material
208 |
209 | if (this.renderToScreen) {
210 | renderer.render(this.scene, this.camera)
211 | } else {
212 | renderer.render(this.scene, this.camera, writeBuffer, this.clear)
213 | }
214 | }
215 |
216 | })
217 |
218 | /**
219 | * @author alteredq / http://alteredqualia.com/
220 | */
221 |
222 | const RenderPass = function (scene, camera, overrideMaterial, clearColor, clearAlpha) {
223 | Pass.call(this)
224 |
225 | this.scene = scene
226 | this.camera = camera
227 |
228 | this.overrideMaterial = overrideMaterial
229 |
230 | this.clearColor = clearColor
231 | this.clearAlpha = (clearAlpha !== undefined) ? clearAlpha : 0
232 |
233 | this.clear = true
234 | this.clearDepth = false
235 | this.needsSwap = false
236 | }
237 |
238 | RenderPass.prototype = Object.assign(Object.create(Pass.prototype), {
239 |
240 | constructor: RenderPass,
241 |
242 | render: function (renderer, writeBuffer, readBuffer, delta, maskActive) {
243 | var oldAutoClear = renderer.autoClear
244 | renderer.autoClear = false
245 |
246 | this.scene.overrideMaterial = this.overrideMaterial
247 |
248 | var oldClearColor, oldClearAlpha
249 |
250 | if (this.clearColor) {
251 | oldClearColor = renderer.getClearColor().getHex()
252 | oldClearAlpha = renderer.getClearAlpha()
253 |
254 | renderer.setClearColor(this.clearColor, this.clearAlpha)
255 | }
256 |
257 | if (this.clearDepth) {
258 | renderer.clearDepth()
259 | }
260 |
261 | renderer.render(this.scene, this.camera, this.renderToScreen ? null : readBuffer, this.clear)
262 |
263 | if (this.clearColor) {
264 | renderer.setClearColor(oldClearColor, oldClearAlpha)
265 | }
266 |
267 | this.scene.overrideMaterial = null
268 | renderer.autoClear = oldAutoClear
269 | }
270 |
271 | })
272 |
273 | export { EffectComposer, ShaderPass, RenderPass }
274 |
--------------------------------------------------------------------------------
/src/libs/post/Vignette.js:
--------------------------------------------------------------------------------
1 | const Vignette = {
2 |
3 | uniforms: {
4 | tDiffuse: { value: null },
5 | offset: { value: 2.0 },
6 | bgColor: { value: null }
7 | },
8 |
9 | vertexShader: `
10 |
11 | varying vec2 vUv;
12 |
13 | void main() {
14 | vUv = uv;
15 | gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
16 | }
17 |
18 | `,
19 |
20 | fragmentShader: `
21 |
22 | uniform vec3 bgColor;
23 | uniform float offset;
24 | uniform sampler2D tDiffuse;
25 |
26 | varying vec2 vUv;
27 |
28 | void main() {
29 |
30 | vec4 texel = texture2D( tDiffuse, vUv );
31 | vec2 uv = ( vUv - vec2( 0.5 ) ) * vec2( offset );
32 |
33 | vec3 result = mix(
34 | texel.rgb,
35 | bgColor,
36 | dot( uv, uv )
37 | );
38 |
39 | gl_FragColor = vec4(
40 | clamp(
41 | result,
42 | bgColor,
43 | vec3( 1. )
44 | ),
45 | texel.a
46 | );
47 |
48 | }
49 |
50 | `
51 |
52 | }
53 |
54 | export default Vignette
55 |
--------------------------------------------------------------------------------
/src/post/BlendLighten.js:
--------------------------------------------------------------------------------
1 | import {
2 | Color
3 | } from 'three'
4 |
5 | const BlendLighten = {
6 | uniforms: {
7 | 'tDiffuse': { value: null },
8 | 'brightness': { value: 0.18 },
9 | 'contrast': { value: 0.35 },
10 | 'blendColor': { value: new Color(0x000000) }
11 | },
12 |
13 | vertexShader: `
14 | varying vec2 vUv;
15 | void main() {
16 |
17 | vUv = uv;
18 | gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
19 | }
20 | `,
21 |
22 | fragmentShader: `
23 |
24 | float blendLighten(float base, float blend) {
25 | return max(blend,base);
26 | }
27 |
28 | vec3 blendLighten(vec3 base, vec3 blend) {
29 | return vec3(blendLighten(base.r,blend.r),blendLighten(base.g,blend.g),blendLighten(base.b,blend.b));
30 | }
31 |
32 | vec3 blendLighten(vec3 base, vec3 blend, float opacity) {
33 | return (blendLighten(base, blend) * opacity + base * (1.0 - opacity));
34 | }
35 |
36 | uniform sampler2D tDiffuse;
37 | uniform float brightness;
38 | uniform float contrast;
39 | uniform vec3 blendColor;
40 |
41 | varying vec2 vUv;
42 |
43 | void main() {
44 |
45 | //vec3 bgColor = vec3(18. / 255., 19. / 255., 38. / 255.);
46 | vec3 bgColor = blendColor;
47 |
48 | gl_FragColor = texture2D( tDiffuse, vUv );
49 |
50 | gl_FragColor.rgb += brightness;
51 |
52 | if (contrast > 0.0) {
53 | gl_FragColor.rgb = (gl_FragColor.rgb - 0.5) / (1.0 - contrast) + 0.5;
54 | } else {
55 | gl_FragColor.rgb = (gl_FragColor.rgb - 0.5) * (1.0 + contrast) + 0.5;
56 | }
57 |
58 | vec3 color = blendLighten(bgColor.rgb, gl_FragColor.rgb);
59 |
60 | gl_FragColor.rgb = color;
61 |
62 | }
63 |
64 | `
65 |
66 | }
67 |
68 | export default BlendLighten
69 |
--------------------------------------------------------------------------------
/src/post/BrightnessContrast.js:
--------------------------------------------------------------------------------
1 | const BrightnessContrast = {
2 | uniforms: {
3 | 'tDiffuse': { value: null },
4 | 'brightness': { value: 0.18 },
5 | 'contrast': { value: 0.35 }
6 | },
7 |
8 | vertexShader: `
9 | varying vec2 vUv;
10 | void main() {
11 |
12 | vUv = uv;
13 | gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
14 | }
15 | `,
16 |
17 | fragmentShader: `
18 |
19 | uniform sampler2D tDiffuse;
20 | uniform float brightness;
21 | uniform float contrast;
22 |
23 | varying vec2 vUv;
24 |
25 | void main() {
26 |
27 | gl_FragColor = texture2D( tDiffuse, vUv );
28 |
29 | gl_FragColor.rgb += brightness;
30 |
31 | if (contrast > 0.0) {
32 | gl_FragColor.rgb = (gl_FragColor.rgb - 0.5) / (1.0 - contrast) + 0.5;
33 | } else {
34 | gl_FragColor.rgb = (gl_FragColor.rgb - 0.5) * (1.0 + contrast) + 0.5;
35 | }
36 |
37 | }
38 |
39 | `
40 |
41 | }
42 |
43 | export default BrightnessContrast
44 |
--------------------------------------------------------------------------------
/src/post/CopyShader.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author alteredq / http://alteredqualia.com/
3 | *
4 | * Full-screen textured quad shader
5 | */
6 |
7 | const CopyShader = {
8 |
9 | uniforms: {
10 |
11 | 'tDiffuse': { value: null },
12 | 'opacity': { value: 1.0 }
13 |
14 | },
15 |
16 | vertexShader: [
17 |
18 | 'varying vec2 vUv;',
19 |
20 | 'void main() {',
21 |
22 | 'vUv = uv;',
23 | 'gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );',
24 |
25 | '}'
26 |
27 | ].join('\n'),
28 |
29 | fragmentShader: [
30 |
31 | 'uniform float opacity;',
32 |
33 | 'uniform sampler2D tDiffuse;',
34 |
35 | 'varying vec2 vUv;',
36 |
37 | 'void main() {',
38 |
39 | 'vec4 texel = texture2D( tDiffuse, vUv );',
40 | 'gl_FragColor = opacity * texel;',
41 |
42 | '}'
43 |
44 | ].join('\n')
45 |
46 | }
47 |
48 | export default CopyShader
49 |
--------------------------------------------------------------------------------
/src/post/EffectComposer.js:
--------------------------------------------------------------------------------
1 | import {
2 | Vector2,
3 | LinearFilter,
4 | RGBAFormat,
5 | WebGLRenderTarget,
6 | Clock,
7 | OrthographicCamera,
8 | PlaneBufferGeometry,
9 | Mesh,
10 | ShaderMaterial,
11 | UniformsUtils,
12 | Color,
13 | Vector3,
14 | AdditiveBlending,
15 | MeshBasicMaterial
16 | } from 'three'
17 | import CopyShader from './CopyShader'
18 |
19 | /**
20 | * @author alteredq / http://alteredqualia.com/
21 | */
22 |
23 | const EffectComposer = function (renderer, renderTarget) {
24 | this.renderer = renderer
25 |
26 | if (renderTarget === undefined) {
27 | var parameters = {
28 | minFilter: LinearFilter,
29 | magFilter: LinearFilter,
30 | format: RGBAFormat,
31 | stencilBuffer: false
32 | }
33 |
34 | var size = renderer.getSize(new Vector2())
35 | this._pixelRatio = renderer.getPixelRatio()
36 | this._width = size.width
37 | this._height = size.height
38 |
39 | renderTarget = new WebGLRenderTarget(this._width * this._pixelRatio, this._height * this._pixelRatio, parameters)
40 | renderTarget.texture.name = 'EffectComposer.rt1'
41 | } else {
42 | this._pixelRatio = 1
43 | this._width = renderTarget.width
44 | this._height = renderTarget.height
45 | }
46 |
47 | this.renderTarget1 = renderTarget
48 | this.renderTarget2 = renderTarget.clone()
49 | this.renderTarget2.texture.name = 'EffectComposer.rt2'
50 |
51 | this.writeBuffer = this.renderTarget1
52 | this.readBuffer = this.renderTarget2
53 |
54 | this.renderToScreen = true
55 |
56 | this.passes = []
57 |
58 | // dependencies
59 |
60 | if (CopyShader === undefined) {
61 | console.error('EffectComposer relies on CopyShader')
62 | }
63 |
64 | if (ShaderPass === undefined) {
65 | console.error('EffectComposer relies on ShaderPass')
66 | }
67 |
68 | this.copyPass = new ShaderPass(CopyShader)
69 |
70 | this.clock = new Clock()
71 | }
72 |
73 | Object.assign(EffectComposer.prototype, {
74 |
75 | swapBuffers: function () {
76 | var tmp = this.readBuffer
77 | this.readBuffer = this.writeBuffer
78 | this.writeBuffer = tmp
79 | },
80 |
81 | addPass: function (pass) {
82 | this.passes.push(pass)
83 | pass.setSize(this._width * this._pixelRatio, this._height * this._pixelRatio)
84 | },
85 |
86 | insertPass: function (pass, index) {
87 | this.passes.splice(index, 0, pass)
88 | },
89 |
90 | isLastEnabledPass: function (passIndex) {
91 | for (var i = passIndex + 1; i < this.passes.length; i++) {
92 | if (this.passes[ i ].enabled) {
93 | return false
94 | }
95 | }
96 |
97 | return true
98 | },
99 |
100 | render: function (deltaTime) {
101 | // deltaTime value is in seconds
102 |
103 | if (deltaTime === undefined) {
104 | deltaTime = this.clock.getDelta()
105 | }
106 |
107 | var currentRenderTarget = this.renderer.getRenderTarget()
108 |
109 | var maskActive = false
110 |
111 | var pass; var i; var il = this.passes.length
112 |
113 | for (i = 0; i < il; i++) {
114 | pass = this.passes[ i ]
115 |
116 | if (pass.enabled === false) continue
117 |
118 | pass.renderToScreen = (this.renderToScreen && this.isLastEnabledPass(i))
119 | pass.render(this.renderer, this.writeBuffer, this.readBuffer, deltaTime, maskActive)
120 |
121 | if (pass.needsSwap) {
122 | if (maskActive) {
123 | var context = this.renderer.getContext()
124 | var stencil = this.renderer.state.buffers.stencil
125 |
126 | // context.stencilFunc( context.NOTEQUAL, 1, 0xffffffff );
127 | stencil.setFunc(context.NOTEQUAL, 1, 0xffffffff)
128 |
129 | this.copyPass.render(this.renderer, this.writeBuffer, this.readBuffer, deltaTime)
130 |
131 | // context.stencilFunc( context.EQUAL, 1, 0xffffffff );
132 | stencil.setFunc(context.EQUAL, 1, 0xffffffff)
133 | }
134 |
135 | this.swapBuffers()
136 | }
137 |
138 | if (MaskPass !== undefined) {
139 | if (pass instanceof MaskPass) {
140 | maskActive = true
141 | } else if (pass instanceof ClearMaskPass) {
142 | maskActive = false
143 | }
144 | }
145 | }
146 |
147 | this.renderer.setRenderTarget(currentRenderTarget)
148 | },
149 |
150 | reset: function (renderTarget) {
151 | if (renderTarget === undefined) {
152 | var size = this.renderer.getSize(new Vector2())
153 | this._pixelRatio = this.renderer.getPixelRatio()
154 | this._width = size.width
155 | this._height = size.height
156 |
157 | renderTarget = this.renderTarget1.clone()
158 | renderTarget.setSize(this._width * this._pixelRatio, this._height * this._pixelRatio)
159 | }
160 |
161 | this.renderTarget1.dispose()
162 | this.renderTarget2.dispose()
163 | this.renderTarget1 = renderTarget
164 | this.renderTarget2 = renderTarget.clone()
165 |
166 | this.writeBuffer = this.renderTarget1
167 | this.readBuffer = this.renderTarget2
168 | },
169 |
170 | setSize: function (width, height) {
171 | this._width = width
172 | this._height = height
173 |
174 | var effectiveWidth = this._width * this._pixelRatio
175 | var effectiveHeight = this._height * this._pixelRatio
176 |
177 | this.renderTarget1.setSize(effectiveWidth, effectiveHeight)
178 | this.renderTarget2.setSize(effectiveWidth, effectiveHeight)
179 |
180 | for (var i = 0; i < this.passes.length; i++) {
181 | this.passes[ i ].setSize(effectiveWidth, effectiveHeight)
182 | }
183 | },
184 |
185 | setPixelRatio: function (pixelRatio) {
186 | this._pixelRatio = pixelRatio
187 |
188 | this.setSize(this._width, this._height)
189 | }
190 |
191 | })
192 |
193 | const Pass = function () {
194 | // if set to true, the pass is processed by the composer
195 | this.enabled = true
196 |
197 | // if set to true, the pass indicates to swap read and write buffer after rendering
198 | this.needsSwap = true
199 |
200 | // if set to true, the pass clears its buffer before rendering
201 | this.clear = false
202 |
203 | // if set to true, the result of the pass is rendered to screen. This is set automatically by EffectComposer.
204 | this.renderToScreen = false
205 | }
206 |
207 | Object.assign(Pass.prototype, {
208 |
209 | setSize: function (/* width, height */) {},
210 |
211 | render: function (/* renderer, writeBuffer, readBuffer, deltaTime, maskActive */) {
212 | console.error('Pass: .render() must be implemented in derived pass.')
213 | }
214 |
215 | })
216 |
217 | // Helper for passes that need to fill the viewport with a single quad.
218 | Pass.FullScreenQuad = (function () {
219 | var camera = new OrthographicCamera(-1, 1, 1, -1, 0, 1)
220 | var geometry = new PlaneBufferGeometry(2, 2)
221 |
222 | var FullScreenQuad = function (material) {
223 | this._mesh = new Mesh(geometry, material)
224 | }
225 |
226 | Object.defineProperty(FullScreenQuad.prototype, 'material', {
227 |
228 | get: function () {
229 | return this._mesh.material
230 | },
231 |
232 | set: function (value) {
233 | this._mesh.material = value
234 | }
235 |
236 | })
237 |
238 | Object.assign(FullScreenQuad.prototype, {
239 |
240 | dispose: function () {
241 | this._mesh.geometry.dispose()
242 | },
243 |
244 | render: function (renderer) {
245 | renderer.render(this._mesh, camera)
246 | }
247 |
248 | })
249 |
250 | return FullScreenQuad
251 | })()
252 |
253 | /**
254 | * @author alteredq / http://alteredqualia.com/
255 | */
256 |
257 | const ShaderPass = function (shader, textureID) {
258 | Pass.call(this)
259 |
260 | this.textureID = (textureID !== undefined) ? textureID : 'tDiffuse'
261 |
262 | if (shader instanceof ShaderMaterial) {
263 | this.uniforms = shader.uniforms
264 |
265 | this.material = shader
266 | } else if (shader) {
267 | this.uniforms = UniformsUtils.clone(shader.uniforms)
268 |
269 | this.material = new ShaderMaterial({
270 |
271 | defines: Object.assign({}, shader.defines),
272 | uniforms: this.uniforms,
273 | vertexShader: shader.vertexShader,
274 | fragmentShader: shader.fragmentShader
275 |
276 | })
277 | }
278 |
279 | this.fsQuad = new Pass.FullScreenQuad(this.material)
280 | }
281 |
282 | ShaderPass.prototype = Object.assign(Object.create(Pass.prototype), {
283 |
284 | constructor: ShaderPass,
285 |
286 | render: function (renderer, writeBuffer, readBuffer /*, deltaTime, maskActive */) {
287 | if (this.uniforms[ this.textureID ]) {
288 | this.uniforms[ this.textureID ].value = readBuffer.texture
289 | }
290 |
291 | this.fsQuad.material = this.material
292 |
293 | if (this.renderToScreen) {
294 | renderer.setRenderTarget(null)
295 | this.fsQuad.render(renderer)
296 | } else {
297 | renderer.setRenderTarget(writeBuffer)
298 | // TODO: Avoid using autoClear properties, see https://github.com/mrdoob/js/pull/15571#issuecomment-465669600
299 | if (this.clear) renderer.clear(renderer.autoClearColor, renderer.autoClearDepth, renderer.autoClearStencil)
300 | this.fsQuad.render(renderer)
301 | }
302 | }
303 |
304 | })
305 |
306 | /**
307 | * @author alteredq / http://alteredqualia.com/
308 | */
309 |
310 | const MaskPass = function (scene, camera) {
311 | Pass.call(this)
312 |
313 | this.scene = scene
314 | this.camera = camera
315 |
316 | this.clear = true
317 | this.needsSwap = false
318 |
319 | this.inverse = false
320 | }
321 |
322 | MaskPass.prototype = Object.assign(Object.create(Pass.prototype), {
323 |
324 | constructor: MaskPass,
325 |
326 | render: function (renderer, writeBuffer, readBuffer /*, deltaTime, maskActive */) {
327 | var context = renderer.getContext()
328 | var state = renderer.state
329 |
330 | // don't update color or depth
331 |
332 | state.buffers.color.setMask(false)
333 | state.buffers.depth.setMask(false)
334 |
335 | // lock buffers
336 |
337 | state.buffers.color.setLocked(true)
338 | state.buffers.depth.setLocked(true)
339 |
340 | // set up stencil
341 |
342 | var writeValue, clearValue
343 |
344 | if (this.inverse) {
345 | writeValue = 0
346 | clearValue = 1
347 | } else {
348 | writeValue = 1
349 | clearValue = 0
350 | }
351 |
352 | state.buffers.stencil.setTest(true)
353 | state.buffers.stencil.setOp(context.REPLACE, context.REPLACE, context.REPLACE)
354 | state.buffers.stencil.setFunc(context.ALWAYS, writeValue, 0xffffffff)
355 | state.buffers.stencil.setClear(clearValue)
356 | state.buffers.stencil.setLocked(true)
357 |
358 | // draw into the stencil buffer
359 |
360 | renderer.setRenderTarget(readBuffer)
361 | if (this.clear) renderer.clear()
362 | renderer.render(this.scene, this.camera)
363 |
364 | renderer.setRenderTarget(writeBuffer)
365 | if (this.clear) renderer.clear()
366 | renderer.render(this.scene, this.camera)
367 |
368 | // unlock color and depth buffer for subsequent rendering
369 |
370 | state.buffers.color.setLocked(false)
371 | state.buffers.depth.setLocked(false)
372 |
373 | // only render where stencil is set to 1
374 |
375 | state.buffers.stencil.setLocked(false)
376 | state.buffers.stencil.setFunc(context.EQUAL, 1, 0xffffffff) // draw if == 1
377 | state.buffers.stencil.setOp(context.KEEP, context.KEEP, context.KEEP)
378 | state.buffers.stencil.setLocked(true)
379 | }
380 |
381 | })
382 |
383 | const ClearMaskPass = function () {
384 | Pass.call(this)
385 |
386 | this.needsSwap = false
387 | }
388 |
389 | ClearMaskPass.prototype = Object.create(Pass.prototype)
390 |
391 | Object.assign(ClearMaskPass.prototype, {
392 |
393 | render: function (renderer /*, writeBuffer, readBuffer, deltaTime, maskActive */) {
394 | renderer.state.buffers.stencil.setLocked(false)
395 | renderer.state.buffers.stencil.setTest(false)
396 | }
397 |
398 | })
399 |
400 | /**
401 | * @author alteredq / http://alteredqualia.com/
402 | */
403 |
404 | const RenderPass = function (scene, camera, overrideMaterial, clearColor, clearAlpha) {
405 | Pass.call(this)
406 |
407 | this.scene = scene
408 | this.camera = camera
409 |
410 | this.overrideMaterial = overrideMaterial
411 |
412 | this.clearColor = clearColor
413 | this.clearAlpha = (clearAlpha !== undefined) ? clearAlpha : 0
414 |
415 | this.clear = true
416 | this.clearDepth = false
417 | this.needsSwap = false
418 | }
419 |
420 | RenderPass.prototype = Object.assign(Object.create(Pass.prototype), {
421 |
422 | constructor: RenderPass,
423 |
424 | render: function (renderer, writeBuffer, readBuffer /*, deltaTime, maskActive */) {
425 | var oldAutoClear = renderer.autoClear
426 | renderer.autoClear = false
427 |
428 | this.scene.overrideMaterial = this.overrideMaterial
429 |
430 | var oldClearColor, oldClearAlpha
431 |
432 | if (this.clearColor) {
433 | oldClearColor = renderer.getClearColor().getHex()
434 | oldClearAlpha = renderer.getClearAlpha()
435 |
436 | renderer.setClearColor(this.clearColor, this.clearAlpha)
437 | }
438 |
439 | if (this.clearDepth) {
440 | renderer.clearDepth()
441 | }
442 |
443 | renderer.setRenderTarget(this.renderToScreen ? null : readBuffer)
444 |
445 | // TODO: Avoid using autoClear properties, see https://github.com/mrdoob/js/pull/15571#issuecomment-465669600
446 | if (this.clear) renderer.clear(renderer.autoClearColor, renderer.autoClearDepth, renderer.autoClearStencil)
447 | renderer.render(this.scene, this.camera)
448 |
449 | if (this.clearColor) {
450 | renderer.setClearColor(oldClearColor, oldClearAlpha)
451 | }
452 |
453 | this.scene.overrideMaterial = null
454 | renderer.autoClear = oldAutoClear
455 | }
456 |
457 | })
458 |
459 | /**
460 | * @author alteredq / http://alteredqualia.com/
461 | */
462 |
463 | const TexturePass = function (map, opacity) {
464 | Pass.call(this)
465 |
466 | if (CopyShader === undefined) { console.error('TexturePass relies on CopyShader') }
467 |
468 | var shader = CopyShader
469 |
470 | this.map = map
471 | this.opacity = (opacity !== undefined) ? opacity : 1.0
472 |
473 | this.uniforms = UniformsUtils.clone(shader.uniforms)
474 |
475 | this.material = new ShaderMaterial({
476 |
477 | uniforms: this.uniforms,
478 | vertexShader: shader.vertexShader,
479 | fragmentShader: shader.fragmentShader,
480 | depthTest: false,
481 | depthWrite: false
482 |
483 | })
484 |
485 | this.needsSwap = false
486 |
487 | this.fsQuad = new Pass.FullScreenQuad(null)
488 | }
489 |
490 | TexturePass.prototype = Object.assign(Object.create(Pass.prototype), {
491 |
492 | constructor: TexturePass,
493 |
494 | render: function (renderer, writeBuffer, readBuffer /*, deltaTime, maskActive */) {
495 | var oldAutoClear = renderer.autoClear
496 | renderer.autoClear = false
497 |
498 | this.fsQuad.material = this.material
499 |
500 | this.uniforms[ 'opacity' ].value = this.opacity
501 | this.uniforms[ 'tDiffuse' ].value = this.map
502 | this.material.transparent = (this.opacity < 1.0)
503 |
504 | renderer.setRenderTarget(this.renderToScreen ? null : readBuffer)
505 | if (this.clear) renderer.clear()
506 | this.fsQuad.render(renderer)
507 |
508 | renderer.autoClear = oldAutoClear
509 | }
510 |
511 | })
512 |
513 | /**
514 | * @author bhouston / http://clara.io/
515 | *
516 | * Luminosity
517 | * http://en.wikipedia.org/wiki/Luminosity
518 | */
519 |
520 | const LuminosityHighPassShader = {
521 |
522 | shaderID: 'luminosityHighPass',
523 |
524 | uniforms: {
525 |
526 | 'tDiffuse': { value: null },
527 | 'luminosityThreshold': { value: 1.0 },
528 | 'smoothWidth': { value: 1.0 },
529 | 'defaultColor': { value: new Color(0x000000) },
530 | 'defaultOpacity': { value: 0.0 }
531 |
532 | },
533 |
534 | vertexShader: [
535 |
536 | 'varying vec2 vUv;',
537 |
538 | 'void main() {',
539 |
540 | ' vUv = uv;',
541 |
542 | ' gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );',
543 |
544 | '}'
545 |
546 | ].join('\n'),
547 |
548 | fragmentShader: [
549 |
550 | 'uniform sampler2D tDiffuse;',
551 | 'uniform vec3 defaultColor;',
552 | 'uniform float defaultOpacity;',
553 | 'uniform float luminosityThreshold;',
554 | 'uniform float smoothWidth;',
555 |
556 | 'varying vec2 vUv;',
557 |
558 | 'void main() {',
559 |
560 | ' vec4 texel = texture2D( tDiffuse, vUv );',
561 |
562 | ' vec3 luma = vec3( 0.299, 0.587, 0.114 );',
563 |
564 | ' float v = dot( texel.xyz, luma );',
565 |
566 | ' vec4 outputColor = vec4( defaultColor.rgb, defaultOpacity );',
567 |
568 | ' float alpha = smoothstep( luminosityThreshold, luminosityThreshold + smoothWidth, v );',
569 |
570 | ' gl_FragColor = mix( outputColor, texel, alpha );',
571 |
572 | '}'
573 |
574 | ].join('\n')
575 |
576 | }
577 |
578 | /**
579 | * @author spidersharma / http://eduperiment.com/
580 | *
581 | * Inspired from Unreal Engine
582 | * https://docs.unrealengine.com/latest/INT/Engine/Rendering/PostProcessEffects/Bloom/
583 | */
584 |
585 | const UnrealBloomPass = function (resolution, strength, radius, threshold, alphaSum) {
586 | Pass.call(this)
587 |
588 | this.strength = (strength !== undefined) ? strength : 1
589 | this.radius = radius
590 | this.threshold = threshold
591 | this.resolution = (resolution !== undefined) ? new Vector2(resolution.x, resolution.y) : new Vector2(256, 256)
592 |
593 | // create color only once here, reuse it later inside the render function
594 | this.clearColor = new Color(0, 0, 0)
595 |
596 | // render targets
597 | var pars = { minFilter: LinearFilter, magFilter: LinearFilter, format: RGBAFormat }
598 | this.renderTargetsHorizontal = []
599 | this.renderTargetsVertical = []
600 | this.nMips = 5
601 | var resx = Math.round(this.resolution.x / 2)
602 | var resy = Math.round(this.resolution.y / 2)
603 |
604 | this.renderTargetBright = new WebGLRenderTarget(resx, resy, pars)
605 | this.renderTargetBright.texture.name = 'UnrealBloomPass.bright'
606 | this.renderTargetBright.texture.generateMipmaps = false
607 |
608 | for (var i = 0; i < this.nMips; i++) {
609 | var renderTargetHorizonal = new WebGLRenderTarget(resx, resy, pars)
610 |
611 | renderTargetHorizonal.texture.name = 'UnrealBloomPass.h' + i
612 | renderTargetHorizonal.texture.generateMipmaps = false
613 |
614 | this.renderTargetsHorizontal.push(renderTargetHorizonal)
615 |
616 | var renderTargetVertical = new WebGLRenderTarget(resx, resy, pars)
617 |
618 | renderTargetVertical.texture.name = 'UnrealBloomPass.v' + i
619 | renderTargetVertical.texture.generateMipmaps = false
620 |
621 | this.renderTargetsVertical.push(renderTargetVertical)
622 |
623 | resx = Math.round(resx / 2)
624 |
625 | resy = Math.round(resy / 2)
626 | }
627 |
628 | // luminosity high pass material
629 |
630 | if (LuminosityHighPassShader === undefined) { console.error('UnrealBloomPass relies on LuminosityHighPassShader') }
631 |
632 | var highPassShader = LuminosityHighPassShader
633 | this.highPassUniforms = UniformsUtils.clone(highPassShader.uniforms)
634 |
635 | this.highPassUniforms[ 'luminosityThreshold' ].value = threshold
636 | this.highPassUniforms[ 'smoothWidth' ].value = 0.01
637 |
638 | this.materialHighPassFilter = new ShaderMaterial({
639 | uniforms: this.highPassUniforms,
640 | vertexShader: highPassShader.vertexShader,
641 | fragmentShader: highPassShader.fragmentShader,
642 | defines: {}
643 | })
644 |
645 | // Gaussian Blur Materials
646 | this.separableBlurMaterials = []
647 | var kernelSizeArray = [ 3, 5, 7, 9, 11 ]
648 | var resx = Math.round(this.resolution.x / 2)
649 | var resy = Math.round(this.resolution.y / 2)
650 |
651 | for (var i = 0; i < this.nMips; i++) {
652 | this.separableBlurMaterials.push(this.getSeperableBlurMaterial(kernelSizeArray[ i ], alphaSum))
653 |
654 | this.separableBlurMaterials[ i ].uniforms[ 'texSize' ].value = new Vector2(resx, resy)
655 |
656 | resx = Math.round(resx / 2)
657 |
658 | resy = Math.round(resy / 2)
659 | }
660 |
661 | // Composite material
662 | this.compositeMaterial = this.getCompositeMaterial(this.nMips)
663 | this.compositeMaterial.uniforms[ 'blurTexture1' ].value = this.renderTargetsVertical[ 0 ].texture
664 | this.compositeMaterial.uniforms[ 'blurTexture2' ].value = this.renderTargetsVertical[ 1 ].texture
665 | this.compositeMaterial.uniforms[ 'blurTexture3' ].value = this.renderTargetsVertical[ 2 ].texture
666 | this.compositeMaterial.uniforms[ 'blurTexture4' ].value = this.renderTargetsVertical[ 3 ].texture
667 | this.compositeMaterial.uniforms[ 'blurTexture5' ].value = this.renderTargetsVertical[ 4 ].texture
668 | this.compositeMaterial.uniforms[ 'bloomStrength' ].value = strength
669 | this.compositeMaterial.uniforms[ 'bloomRadius' ].value = 0.1
670 | this.compositeMaterial.needsUpdate = true
671 |
672 | var bloomFactors = [ 1.0, 0.8, 0.6, 0.4, 0.2 ]
673 | this.compositeMaterial.uniforms[ 'bloomFactors' ].value = bloomFactors
674 | this.bloomTintColors = [ new Vector3(1, 1, 1), new Vector3(1, 1, 1), new Vector3(1, 1, 1),
675 | new Vector3(1, 1, 1), new Vector3(1, 1, 1) ]
676 | this.compositeMaterial.uniforms[ 'bloomTintColors' ].value = this.bloomTintColors
677 |
678 | // copy material
679 | if (CopyShader === undefined) {
680 | console.error('UnrealBloomPass relies on CopyShader')
681 | }
682 |
683 | var copyShader = CopyShader
684 |
685 | this.copyUniforms = UniformsUtils.clone(copyShader.uniforms)
686 | this.copyUniforms[ 'opacity' ].value = 1.0
687 |
688 | this.materialCopy = new ShaderMaterial({
689 | uniforms: this.copyUniforms,
690 | vertexShader: copyShader.vertexShader,
691 | fragmentShader: copyShader.fragmentShader,
692 | blending: AdditiveBlending,
693 | depthTest: false,
694 | depthWrite: false,
695 | transparent: true
696 | })
697 |
698 | this.enabled = true
699 | this.needsSwap = false
700 |
701 | this.oldClearColor = new Color()
702 | this.oldClearAlpha = 1
703 |
704 | this.basic = new MeshBasicMaterial()
705 |
706 | this.fsQuad = new Pass.FullScreenQuad(null)
707 | }
708 |
709 | UnrealBloomPass.prototype = Object.assign(Object.create(Pass.prototype), {
710 |
711 | constructor: UnrealBloomPass,
712 |
713 | dispose: function () {
714 | for (var i = 0; i < this.renderTargetsHorizontal.length; i++) {
715 | this.renderTargetsHorizontal[ i ].dispose()
716 | }
717 |
718 | for (var i = 0; i < this.renderTargetsVertical.length; i++) {
719 | this.renderTargetsVertical[ i ].dispose()
720 | }
721 |
722 | this.renderTargetBright.dispose()
723 | },
724 |
725 | setSize: function (width, height) {
726 | var resx = Math.round(width / 2)
727 | var resy = Math.round(height / 2)
728 |
729 | this.renderTargetBright.setSize(resx, resy)
730 |
731 | for (var i = 0; i < this.nMips; i++) {
732 | this.renderTargetsHorizontal[ i ].setSize(resx, resy)
733 | this.renderTargetsVertical[ i ].setSize(resx, resy)
734 |
735 | this.separableBlurMaterials[ i ].uniforms[ 'texSize' ].value = new Vector2(resx, resy)
736 |
737 | resx = Math.round(resx / 2)
738 | resy = Math.round(resy / 2)
739 | }
740 | },
741 |
742 | render: function (renderer, writeBuffer, readBuffer, deltaTime, maskActive) {
743 | this.oldClearColor.copy(renderer.getClearColor())
744 | this.oldClearAlpha = renderer.getClearAlpha()
745 | var oldAutoClear = renderer.autoClear
746 | renderer.autoClear = false
747 |
748 | renderer.setClearColor(this.clearColor, 0)
749 |
750 | if (maskActive) renderer.state.buffers.stencil.setTest(false)
751 |
752 | // Render input to screen
753 |
754 | if (this.renderToScreen) {
755 | this.fsQuad.material = this.basic
756 | this.basic.map = readBuffer.texture
757 |
758 | renderer.setRenderTarget(null)
759 | renderer.clear()
760 | this.fsQuad.render(renderer)
761 | }
762 |
763 | // 1. Extract Bright Areas
764 |
765 | this.highPassUniforms[ 'tDiffuse' ].value = readBuffer.texture
766 | this.highPassUniforms[ 'luminosityThreshold' ].value = this.threshold
767 | this.fsQuad.material = this.materialHighPassFilter
768 |
769 | renderer.setRenderTarget(this.renderTargetBright)
770 | renderer.clear()
771 | this.fsQuad.render(renderer)
772 |
773 | // 2. Blur All the mips progressively
774 |
775 | var inputRenderTarget = this.renderTargetBright
776 |
777 | for (var i = 0; i < this.nMips; i++) {
778 | this.fsQuad.material = this.separableBlurMaterials[ i ]
779 |
780 | this.separableBlurMaterials[ i ].uniforms[ 'colorTexture' ].value = inputRenderTarget.texture
781 | this.separableBlurMaterials[ i ].uniforms[ 'direction' ].value = UnrealBloomPass.BlurDirectionX
782 | renderer.setRenderTarget(this.renderTargetsHorizontal[ i ])
783 | renderer.clear()
784 | this.fsQuad.render(renderer)
785 |
786 | this.separableBlurMaterials[ i ].uniforms[ 'colorTexture' ].value = this.renderTargetsHorizontal[ i ].texture
787 | this.separableBlurMaterials[ i ].uniforms[ 'direction' ].value = UnrealBloomPass.BlurDirectionY
788 | renderer.setRenderTarget(this.renderTargetsVertical[ i ])
789 | renderer.clear()
790 | this.fsQuad.render(renderer)
791 |
792 | inputRenderTarget = this.renderTargetsVertical[ i ]
793 | }
794 |
795 | // Composite All the mips
796 |
797 | this.fsQuad.material = this.compositeMaterial
798 | this.compositeMaterial.uniforms[ 'bloomStrength' ].value = this.strength
799 | this.compositeMaterial.uniforms[ 'bloomRadius' ].value = this.radius
800 | this.compositeMaterial.uniforms[ 'bloomTintColors' ].value = this.bloomTintColors
801 |
802 | renderer.setRenderTarget(this.renderTargetsHorizontal[ 0 ])
803 | renderer.clear()
804 | this.fsQuad.render(renderer)
805 |
806 | // Blend it additively over the input texture
807 |
808 | this.fsQuad.material = this.materialCopy
809 | this.copyUniforms[ 'tDiffuse' ].value = this.renderTargetsHorizontal[ 0 ].texture
810 |
811 | if (maskActive) renderer.state.buffers.stencil.setTest(true)
812 |
813 | if (this.renderToScreen) {
814 | renderer.setRenderTarget(null)
815 | this.fsQuad.render(renderer)
816 | } else {
817 | renderer.setRenderTarget(readBuffer)
818 | this.fsQuad.render(renderer)
819 | }
820 |
821 | // Restore renderer settings
822 |
823 | renderer.setClearColor(this.oldClearColor, this.oldClearAlpha)
824 | renderer.autoClear = oldAutoClear
825 | },
826 |
827 | getSeperableBlurMaterial: function (kernelRadius, alphaSum) {
828 | return new ShaderMaterial({
829 |
830 | defines: {
831 | 'KERNEL_RADIUS': kernelRadius,
832 | 'SIGMA': kernelRadius
833 | },
834 |
835 | uniforms: {
836 | 'colorTexture': { value: null },
837 | 'texSize': { value: new Vector2(0.5, 0.5) },
838 | 'direction': { value: new Vector2(0.5, 0.5) },
839 | 'alphaSum': { value: alphaSum }
840 | },
841 |
842 | vertexShader:
843 | 'varying vec2 vUv;\n\
844 | void main() {\n\
845 | vUv = uv;\n\
846 | gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );\n\
847 | }',
848 |
849 | fragmentShader:
850 | '#include \
851 | varying vec2 vUv;\n\
852 | uniform sampler2D colorTexture;\n\
853 | uniform vec2 texSize;\
854 | uniform vec2 direction;\
855 | uniform float alphaSum;\
856 | \
857 | float gaussianPdf(in float x, in float sigma) {\
858 | return 0.39894 * exp( -0.5 * x * x/( sigma * sigma))/sigma;\
859 | }\
860 | void main() {\n\
861 | vec2 invSize = 1.0 / texSize;\
862 | float fSigma = float(SIGMA);\
863 | float weightSum = gaussianPdf(0.0, fSigma);\
864 | float alphaSum = alphaSum;\
865 | vec3 diffuseSum = texture2D( colorTexture, vUv).rgb * weightSum;\
866 | for( int i = 1; i < KERNEL_RADIUS; i ++ ) {\
867 | float x = float(i);\
868 | float w = gaussianPdf(x, fSigma);\
869 | vec2 uvOffset = direction * invSize * x;\
870 | vec4 sample1 = texture2D( colorTexture, vUv + uvOffset);\
871 | vec4 sample2 = texture2D( colorTexture, vUv - uvOffset);\
872 | diffuseSum += (sample1.rgb + sample2.rgb) * w;\
873 | alphaSum += (sample1.a + sample2.a) * w;\
874 | weightSum += 2.0 * w;\
875 | }\
876 | gl_FragColor = vec4(diffuseSum/weightSum, alphaSum/weightSum);\n\
877 | }'
878 | })
879 | },
880 |
881 | getCompositeMaterial: function (nMips) {
882 | return new ShaderMaterial({
883 |
884 | defines: {
885 | 'NUM_MIPS': nMips
886 | },
887 |
888 | uniforms: {
889 | 'blurTexture1': { value: null },
890 | 'blurTexture2': { value: null },
891 | 'blurTexture3': { value: null },
892 | 'blurTexture4': { value: null },
893 | 'blurTexture5': { value: null },
894 | 'dirtTexture': { value: null },
895 | 'bloomStrength': { value: 1.0 },
896 | 'bloomFactors': { value: null },
897 | 'bloomTintColors': { value: null },
898 | 'bloomRadius': { value: 0.0 }
899 | },
900 |
901 | vertexShader:
902 | 'varying vec2 vUv;\n\
903 | void main() {\n\
904 | vUv = uv;\n\
905 | gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );\n\
906 | }',
907 |
908 | fragmentShader:
909 | 'varying vec2 vUv;\
910 | uniform sampler2D blurTexture1;\
911 | uniform sampler2D blurTexture2;\
912 | uniform sampler2D blurTexture3;\
913 | uniform sampler2D blurTexture4;\
914 | uniform sampler2D blurTexture5;\
915 | uniform sampler2D dirtTexture;\
916 | uniform float bloomStrength;\
917 | uniform float bloomRadius;\
918 | uniform float bloomFactors[NUM_MIPS];\
919 | uniform vec3 bloomTintColors[NUM_MIPS];\
920 | \
921 | float lerpBloomFactor(const in float factor) { \
922 | float mirrorFactor = 1.2 - factor;\
923 | return mix(factor, mirrorFactor, bloomRadius);\
924 | }\
925 | \
926 | void main() {\
927 | gl_FragColor = bloomStrength * ( lerpBloomFactor(bloomFactors[0]) * vec4(bloomTintColors[0], 1.0) * texture2D(blurTexture1, vUv) + \
928 | lerpBloomFactor(bloomFactors[1]) * vec4(bloomTintColors[1], 1.0) * texture2D(blurTexture2, vUv) + \
929 | lerpBloomFactor(bloomFactors[2]) * vec4(bloomTintColors[2], 1.0) * texture2D(blurTexture3, vUv) + \
930 | lerpBloomFactor(bloomFactors[3]) * vec4(bloomTintColors[3], 1.0) * texture2D(blurTexture4, vUv) + \
931 | lerpBloomFactor(bloomFactors[4]) * vec4(bloomTintColors[4], 1.0) * texture2D(blurTexture5, vUv) );\
932 | }'
933 | })
934 | }
935 |
936 | })
937 |
938 | UnrealBloomPass.BlurDirectionX = new Vector2(1.0, 0.0)
939 | UnrealBloomPass.BlurDirectionY = new Vector2(0.0, 1.0)
940 |
941 | export { EffectComposer, ShaderPass, RenderPass, UnrealBloomPass, TexturePass }
942 |
--------------------------------------------------------------------------------
/src/post/Film.js:
--------------------------------------------------------------------------------
1 | const Film = {
2 |
3 | uniforms: {
4 |
5 | tDiffuse: { value: null },
6 | time: { value: 0.0 },
7 | nIntensity: { value: 0.4 },
8 | sIntensity: { value: 0.0 },
9 | sCount: { value: 0 },
10 | grayscale: { value: 0 }
11 |
12 | },
13 |
14 | vertexShader: `
15 |
16 | varying vec2 vUv;
17 |
18 | void main() {
19 |
20 | vUv = uv;
21 | gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
22 |
23 | }
24 |
25 | `,
26 |
27 | fragmentShader: `
28 |
29 | #include
30 |
31 | // control parameter
32 | uniform float time;
33 |
34 | uniform bool grayscale;
35 |
36 | // noise effect intensity value (0 = no effect, 1 = full effect)
37 | uniform float nIntensity;
38 |
39 | // scanlines effect intensity value (0 = no effect, 1 = full effect)
40 | uniform float sIntensity;
41 |
42 | // scanlines effect count value (0 = no effect, 4096 = full effect)
43 | uniform float sCount;
44 |
45 | uniform sampler2D tDiffuse;
46 |
47 | varying vec2 vUv;
48 |
49 | void main() {
50 |
51 | // sample the source
52 | vec4 cTextureScreen = texture2D( tDiffuse, vUv );
53 |
54 | // make some noise
55 | float dx = rand( vUv + time );
56 |
57 | // add noise
58 | vec3 cResult = cTextureScreen.rgb + cTextureScreen.rgb * clamp( 0.1 + dx, 0.0, 1.0 );
59 |
60 | // get us a sine and cosine
61 | vec2 sc = vec2( sin( vUv.y * sCount ), cos( vUv.y * sCount ) );
62 |
63 | // add scanlines
64 | cResult += cTextureScreen.rgb * vec3( sc.x, sc.y, sc.x ) * sIntensity;
65 |
66 | // interpolate between source and result by intensity
67 | cResult = cTextureScreen.rgb + clamp( nIntensity, 0.0,1.0 ) * ( cResult - cTextureScreen.rgb );
68 |
69 | // convert to grayscale if desired
70 | if( grayscale ) {
71 |
72 | cResult = vec3( cResult.r * 0.3 + cResult.g * 0.59 + cResult.b * 0.11 );
73 |
74 | }
75 |
76 | gl_FragColor = vec4( cResult, cTextureScreen.a );
77 |
78 | }
79 |
80 | `
81 |
82 | }
83 |
84 | export default Film
85 |
--------------------------------------------------------------------------------
/src/post/Vignette.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author alteredq / http://alteredqualia.com/
3 | *
4 | * Vignette shader
5 | * based on PaintEffect postprocess from ro.me
6 | * http://code.google.com/p/3-dreams-of-black/source/browse/deploy/js/effects/PaintEffect.js
7 | */
8 |
9 | const Vignette = {
10 |
11 | uniforms: {
12 |
13 | tDiffuse: { value: null },
14 | offset: { value: 1.0 },
15 | darkness: { value: 1.3 }
16 |
17 | },
18 |
19 | vertexShader: `
20 |
21 | varying vec2 vUv;
22 |
23 | void main() {
24 |
25 | vUv = uv;
26 | gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
27 |
28 | }
29 |
30 | `,
31 |
32 | fragmentShader: `
33 |
34 | uniform float offset;
35 | uniform float darkness;
36 |
37 | uniform sampler2D tDiffuse;
38 |
39 | varying vec2 vUv;
40 |
41 | float random(vec2 co){
42 | return fract(sin(dot(co.xy, vec2(12.9898,78.233))) * 43758.5453);
43 | }
44 |
45 | // based on https://www.shadertoy.com/view/MslGR8
46 | vec3 dithering( vec3 color ) {
47 | //Calculate grid position
48 | float grid_position = random( gl_FragCoord.xy );
49 | //Shift the individual colors differently, thus making it even harder to see the dithering pattern
50 | vec3 dither_shift_RGB = vec3( 0.25 / 255.0, -0.25 / 255.0, 0.25 / 255.0 );
51 | //modify shift acording to grid position.
52 | dither_shift_RGB = mix( 2.0 * dither_shift_RGB, -2.0 * dither_shift_RGB, grid_position );
53 | //shift the color by dither_shift
54 | return color + dither_shift_RGB;
55 | }
56 |
57 | void main() {
58 |
59 | // Eskils vignette
60 |
61 | vec4 texel = texture2D( tDiffuse, vUv );
62 | vec2 uv = ( vUv - vec2( 0.5 ) ) * vec2( offset );
63 | gl_FragColor = vec4( dithering( mix( texel.rgb, vec3( 1.0 - darkness ), dot( uv, uv ) ) ), texel.a );
64 |
65 | /*
66 | // alternative version from glfx.js
67 | // this one makes more "dusty" look (as opposed to "burned")
68 |
69 | "vec4 color = texture2D( tDiffuse, vUv );",
70 | "float dist = distance( vUv, vec2( 0.5 ) );",
71 | "color.rgb *= smoothstep( 0.8, offset * 0.799, dist *( darkness + offset ) );",
72 | "gl_FragColor = color;",
73 | */
74 |
75 | }
76 |
77 | `
78 |
79 | }
80 |
81 | export default Vignette
82 |
--------------------------------------------------------------------------------
/src/shaders/applyQuaternionToVector.glsl:
--------------------------------------------------------------------------------
1 | vec3 applyQuaternionToVector( vec4 q, vec3 v ){
2 | return v + 2.0 * cross( q.xyz, cross( q.xyz, v ) + q.w * v );
3 | }
4 |
5 |
6 | #pragma glslify: export(applyQuaternionToVector)
7 |
--------------------------------------------------------------------------------
/src/shaders/blur.frag:
--------------------------------------------------------------------------------
1 | // https://github.com/Jam3/glsl-fast-gaussian-blur/blob/master/9.glsl
2 | vec4 blur9(sampler2D image, vec2 uv, vec2 resolution, vec2 direction) {
3 | vec4 color = vec4(0.0);
4 | vec2 off1 = vec2(1.3846153846) * direction;
5 | vec2 off2 = vec2(3.2307692308) * direction;
6 | color += texture2D(image, uv) * 0.2270270270;
7 | color += texture2D(image, uv + (off1 / resolution)) * 0.3162162162;
8 | color += texture2D(image, uv - (off1 / resolution)) * 0.3162162162;
9 | color += texture2D(image, uv + (off2 / resolution)) * 0.0702702703;
10 | color += texture2D(image, uv - (off2 / resolution)) * 0.0702702703;
11 | return color;
12 | }
13 |
14 | vec4 blur13(sampler2D image, vec2 uv, vec2 resolution, vec2 direction) {
15 | vec4 color = vec4(0.0);
16 | vec2 off1 = vec2(1.411764705882353) * direction;
17 | vec2 off2 = vec2(3.2941176470588234) * direction;
18 | vec2 off3 = vec2(5.176470588235294) * direction;
19 | color += texture2D(image, uv) * 0.1964825501511404;
20 | color += texture2D(image, uv + (off1 / resolution)) * 0.2969069646728344;
21 | color += texture2D(image, uv - (off1 / resolution)) * 0.2969069646728344;
22 | color += texture2D(image, uv + (off2 / resolution)) * 0.09447039785044732;
23 | color += texture2D(image, uv - (off2 / resolution)) * 0.09447039785044732;
24 | color += texture2D(image, uv + (off3 / resolution)) * 0.010381362401148057;
25 | color += texture2D(image, uv - (off3 / resolution)) * 0.010381362401148057;
26 | return color;
27 | }
28 |
29 | uniform sampler2D uTexture;
30 | varying vec2 vUv;
31 |
32 | void main() {
33 |
34 | vec2 res = vec2(1280.0, 720.0);
35 |
36 | vec4 blurH = blur9(uTexture, vUv, res, vec2(1.0, 0.0));
37 | vec4 blurV = blur9(uTexture, vUv, res, vec2(0.0, 1.0));
38 |
39 | gl_FragColor = mix(blurH, blurV, 0.5);
40 |
41 | }
--------------------------------------------------------------------------------
/src/shaders/curlNoise.glsl:
--------------------------------------------------------------------------------
1 | // https://github.com/fazeaction/webgl-001/blob/master/src/shaders/curl.glsl
2 |
3 | #pragma glslify: snoise = require(glsl-noise/simplex/3d)
4 | // Using @eddietree implementation from
5 | // His brilliant demo 'Artifacts'
6 |
7 | vec3 snoiseVec3( vec3 x ){
8 |
9 | float s = snoise(vec3( x ));
10 | float s1 = snoise(vec3( x.y - 19.1 , x.z + 33.4 , x.x + 47.2 ));
11 | float s2 = snoise(vec3( x.z + 74.2 , x.x - 124.5 , x.y + 99.4 ));
12 | vec3 c = vec3( s , s1 , s2 );
13 | return c;
14 |
15 | }
16 |
17 |
18 | vec3 curlNoise( vec3 p ){
19 |
20 | const float e = 1e-1;
21 | vec3 dx = vec3( e , 0.0 , 0.0 );
22 | vec3 dy = vec3( 0.0 , e , 0.0 );
23 | vec3 dz = vec3( 0.0 , 0.0 , e );
24 |
25 | vec3 p_x0 = snoiseVec3( p - dx );
26 | vec3 p_x1 = snoiseVec3( p + dx );
27 | vec3 p_y0 = snoiseVec3( p - dy );
28 | vec3 p_y1 = snoiseVec3( p + dy );
29 | vec3 p_z0 = snoiseVec3( p - dz );
30 | vec3 p_z1 = snoiseVec3( p + dz );
31 |
32 | float x = p_y1.z - p_y0.z - p_z1.y + p_z0.y;
33 | float y = p_z1.x - p_z0.x - p_x1.z + p_x0.z;
34 | float z = p_x1.y - p_x0.y - p_y1.x + p_y0.x;
35 |
36 | const float divisor = 1.0 / ( 2.0 * e );
37 | vec3 result = normalize( vec3( x , y , z ) * divisor );
38 | // return vec3(
39 | // floor(result.x + 0.5),
40 | // floor(result.y + 0.5),
41 | // floor(result.z + 0.5)
42 | // );
43 | return result;
44 |
45 | }
46 |
47 | #pragma glslify: export(curlNoise)
--------------------------------------------------------------------------------
/src/shaders/edgeDetect.frag:
--------------------------------------------------------------------------------
1 | #extension GL_OES_standard_derivatives : enable
2 |
3 | // #pragma glslify: blend = require(glsl-blend/overlay)
4 | // #pragma glslify: edgeDetect = require(glsl-edge-detection)
5 |
6 | uniform sampler2D uTexture;
7 | varying vec2 vUv;
8 |
9 | void main() {
10 |
11 | // fast edge detection https://www.shadertoy.com/view/MdGGRt
12 | vec4 o = vec4(1.0);
13 |
14 | float edgeThreshold = 50.0;
15 |
16 | o -= o - length(fwidth(texture2D(uTexture, vUv))) * edgeThreshold;
17 |
18 | gl_FragColor = 1.0 - o;
19 | }
--------------------------------------------------------------------------------
/src/shaders/empty.frag:
--------------------------------------------------------------------------------
1 | void main() {
2 | gl_FragColor = vec4(0.0);
3 | }
--------------------------------------------------------------------------------
/src/shaders/markers.frag:
--------------------------------------------------------------------------------
1 | uniform vec3 diffuse;
2 | uniform float opacity;
3 |
4 | #ifndef FLAT_SHADED
5 |
6 | varying vec3 vNormal;
7 |
8 | #endif
9 |
10 | #include
11 | #include
12 | #include
13 | #include
14 | #include
15 | #include
16 | #include
17 | #include
18 | #include
19 | #include
20 | #include
21 | #include
22 | #include
23 | #include
24 |
25 | void main() {
26 |
27 | #include
28 |
29 | vec4 diffuseColor = vec4( diffuse, opacity );
30 |
31 | #include
32 | #include
33 | #include
34 | #include
35 | #include
36 | #include
37 |
38 | ReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) );
39 |
40 | // accumulation (baked indirect lighting only)
41 | #ifdef USE_LIGHTMAP
42 |
43 | reflectedLight.indirectDiffuse += texture2D( lightMap, vUv2 ).xyz * lightMapIntensity;
44 |
45 | #else
46 |
47 | reflectedLight.indirectDiffuse += vec3( 1.0 );
48 |
49 | #endif
50 |
51 | // modulation
52 | #include
53 |
54 | reflectedLight.indirectDiffuse *= diffuseColor.rgb;
55 |
56 | vec3 outgoingLight = reflectedLight.indirectDiffuse;
57 |
58 | #include
59 |
60 | gl_FragColor = vec4( outgoingLight, diffuseColor.a );
61 |
62 | #include
63 | #include
64 | #include
65 | #include
66 |
67 | }
68 |
--------------------------------------------------------------------------------
/src/shaders/markers.vert:
--------------------------------------------------------------------------------
1 | #pragma glslify: applyQuaternionToVector = require('./applyQuaternionToVector');
2 | #pragma glslify: rotationMatrix = require('./rotationMatrix');
3 |
4 | uniform float uTime;
5 |
6 | attribute vec3 offset;
7 | attribute float scale;
8 | attribute vec4 quaternion;
9 | attribute float id;
10 | attribute float isSelected;
11 |
12 | #include
13 | #include
14 | #include
15 | #include
16 | #include
17 | #include
18 | #include
19 | #include
20 | #include
21 | #include
22 |
23 | void main() {
24 |
25 | #include
26 | #include
27 | #include
28 | #include
29 |
30 | #ifdef USE_ENVMAP
31 |
32 | #include
33 | #include
34 | #include
35 | #include
36 |
37 | #endif
38 |
39 | #include
40 |
41 | // scale
42 | transformed.xyz *= scale;
43 |
44 | mat4 rotation = rotationMatrix(offset.xyz * vec3(0.0, 0.0, 1.0), (id + uTime * 1.5));
45 | vec4 newPos = rotation * vec4( transformed, 1.0 );
46 |
47 | transformed.xyz = newPos.xyz;
48 | transformed.xyz = applyQuaternionToVector( quaternion, transformed.xyz );
49 | transformed.xyz += offset.xyz;
50 |
51 | #include
52 | #include
53 | #include
54 | #include
55 |
56 | #include
57 | #include
58 | #include
59 | #include
60 |
61 | }
62 |
--------------------------------------------------------------------------------
/src/shaders/mousePos.frag:
--------------------------------------------------------------------------------
1 | varying vec2 vUv;
2 |
3 | uniform sampler2D uMousePosTexture;
4 | uniform vec2 uMousePos;
5 | uniform vec2 uPrevMousePos;
6 | uniform float uAspect;
7 |
8 | float distToSegment( vec2 x1, vec2 x2, vec2 p ) {
9 |
10 | vec2 v = x2 - x1;
11 | vec2 w = p - x1;
12 |
13 | float c1 = dot(w,v);
14 | float c2 = dot(v,v);
15 |
16 | float div = mix( c2, c1, step( c2, c1 ) );
17 |
18 | float mult = step( 0.0, c1 );
19 |
20 | float b = c1 * mult / div;
21 | vec2 pb = x1 + b*v;
22 |
23 | return distance( p, pb );
24 |
25 | }
26 |
27 | void main() {
28 |
29 | vec4 mousePosTexture = texture2D(uMousePosTexture, vUv);
30 |
31 | float mouseRadius = 0.35;
32 |
33 | float decay = 0.96;
34 | vec4 color = vec4(0.0, 0.0, 0.0, 1.0);
35 |
36 | // vec2 correctedMousePos = uMousePos * vec2(uAspect, 1.0);
37 |
38 | float dist = distToSegment(uPrevMousePos * vec2(uAspect, 1.0), uMousePos * vec2(uAspect, 1.0), vUv * vec2(uAspect, 1.0));
39 |
40 | if (dist < mouseRadius) {
41 | float mouse = pow(1.0-abs(dist) * 1.0, 125.0);
42 | color.b = mouse;
43 |
44 | vec2 dir = vec2((uMousePos * vec2(uAspect, 1.0)) - (uPrevMousePos * vec2(uAspect, 1.0) ) );
45 | color.xy = dir*0.5;
46 | }
47 |
48 | color += mousePosTexture * decay;
49 |
50 | gl_FragColor = color;
51 | }
--------------------------------------------------------------------------------
/src/shaders/particles.frag:
--------------------------------------------------------------------------------
1 |
2 | #pragma glslify: snoise3 = require(glsl-noise/simplex/3d)
3 |
4 | uniform sampler2D uTexture;
5 | uniform vec2 uTextureSize;
6 | uniform float uTime;
7 | uniform float uAspect;
8 | uniform float uIsMobile;
9 |
10 | varying vec2 vPUv;
11 | varying vec2 vUv;
12 | varying vec4 vMousePosTexture;
13 | varying float vCamDist;
14 |
15 | void main() {
16 |
17 | vec2 uv = vUv * vec2(uAspect, 1.0);
18 | vec2 puv = vPUv;
19 |
20 | // pixel color
21 | vec4 color = texture2D(uTexture, puv);
22 |
23 | if (color.r + color.g + color.b < 0.01) {
24 | discard;
25 | }
26 |
27 | // greyscale
28 | float grey = color.r * 0.21 + color.g * 0.71 + color.b * 0.07;
29 | color = vec4(grey * 1.0, grey * 0.2, grey * 0.2, 1.0);
30 |
31 | //float noiseVal = snoise3(vec3(puv * 2.0, uTime * 0.2 ));
32 |
33 | // color.r *= max( 0.4, noiseVal );
34 | // color += noiseVal * 0.02;
35 |
36 | float border = 1.0;
37 | float radius = 0.5;
38 | if (uIsMobile == 1.0) {
39 | border = 1.0;
40 | radius = 0.6;
41 | }
42 |
43 | // float radius = 0.5 + (vCamDist * 0.005);
44 | // float radius = 0.5 + (1.0-(uTextureSize.x+uTextureSize.y) * 0.001);
45 | float dist = radius - distance(uv, vec2(0.5));
46 | float t = smoothstep(0.0, border, dist);
47 | if (uIsMobile == 1.0) {
48 | t *= 2.0;
49 | }
50 |
51 | color.a *= t;
52 |
53 | gl_FragColor = color;
54 |
55 | }
--------------------------------------------------------------------------------
/src/shaders/particles.vert:
--------------------------------------------------------------------------------
1 | uniform vec2 uTextureSize;
2 | uniform sampler2D uTexture;
3 | uniform sampler2D uMousePosTexture;
4 | uniform sampler2D positionTexture;
5 | uniform sampler2D defaultPositionTexture;
6 | uniform sampler2D initialPositionTexture;
7 | uniform vec2 uMousePos;
8 | uniform vec2 uPrevMousePos;
9 | uniform float uNoiseMix;
10 | uniform float uTime;
11 | uniform float uAspect;
12 | uniform vec3 uCamPos;
13 |
14 | attribute vec2 offset;
15 | attribute vec3 tPosition;
16 |
17 | varying vec2 vUv;
18 | varying vec2 vPUv;
19 | varying vec4 vMousePosTexture;
20 | varying float vCamDist;
21 |
22 | void main() {
23 |
24 | vCamDist = dot(uCamPos, uCamPos);
25 |
26 | vUv = uv;
27 |
28 | // particle uv
29 | vec2 puv = offset.xy / uTextureSize;
30 | vPUv = puv;
31 |
32 | vec4 color = texture2D(uTexture, puv);
33 |
34 | if (color.r + color.g + color.b > 0.01) {
35 |
36 | #include
37 |
38 | vec4 noisePositionData = (texture2D(positionTexture, tPosition.xy) / (uTextureSize.x ));
39 | vec4 defaultPosition = (texture2D(defaultPositionTexture, tPosition.xy) /(uTextureSize.x ));
40 | vec4 initialPosition = (texture2D(initialPositionTexture, tPosition.xy) / (uTextureSize.x ));
41 |
42 | vec4 mousePosTexture = texture2D(uMousePosTexture, puv);
43 | vMousePosTexture = mousePosTexture;
44 |
45 | vec2 dir = mousePosTexture.xy;
46 |
47 | defaultPosition.xyz = mix(initialPosition.xyz, defaultPosition.xyz, clamp(0.5 + uTime * 0.3, 0.0, 1.0));
48 |
49 | transformed.xyz = mix(defaultPosition.xyz, noisePositionData.xyz, clamp(mousePosTexture.b, 0.0, 1.0 ) );
50 | transformed.z = 0.0;
51 |
52 | float puvToMouse = distance(uMousePos * vec2(uAspect, 1.0), puv * vec2(uAspect, 1.0));
53 | if (puvToMouse < 0.4) {
54 | transformed.xy += ( clamp(dir * 2.0, -1.0, 1.0) * clamp( pow(1.0-puvToMouse * 2.0, 10.0) , 0.0, 1.0 ) ) * (uNoiseMix * 0.1);
55 | }
56 |
57 | #include
58 |
59 | vec4 newPos = vec4(position, 0.);
60 |
61 | float scale = 0.006;
62 | newPos.xy *= scale;
63 |
64 | mvPosition.xyz += newPos.xyz;
65 | mvPosition.xy -= 0.5;
66 |
67 | gl_Position = projectionMatrix * mvPosition;
68 |
69 | } else {
70 |
71 | gl_Position = vec4(99999.9);
72 |
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/src/shaders/passThrough.frag:
--------------------------------------------------------------------------------
1 | uniform sampler2D texture;
2 | varying vec2 vUv;
3 |
4 | void main() {
5 | gl_FragColor = texture2D(texture, vUv);
6 | }
--------------------------------------------------------------------------------
/src/shaders/passThrough.vert:
--------------------------------------------------------------------------------
1 | varying vec2 vUv;
2 |
3 | void main() {
4 | vUv = uv;
5 | gl_Position = vec4(position, 1.);
6 | }
--------------------------------------------------------------------------------
/src/shaders/paths.frag:
--------------------------------------------------------------------------------
1 | #define STANDARD
2 |
3 | #ifdef PHYSICAL
4 | #define REFLECTIVITY
5 | #define CLEARCOAT
6 | #define TRANSPARENCY
7 | #endif
8 |
9 | uniform vec3 diffuse;
10 | uniform vec3 emissive;
11 | uniform float roughness;
12 | uniform float metalness;
13 | uniform float opacity;
14 |
15 | #ifdef TRANSPARENCY
16 | uniform float transparency;
17 | #endif
18 |
19 | #ifdef REFLECTIVITY
20 | uniform float reflectivity;
21 | #endif
22 |
23 | #ifdef CLEARCOAT
24 | uniform float clearcoat;
25 | uniform float clearcoatRoughness;
26 | #endif
27 |
28 | #ifdef USE_SHEEN
29 | uniform vec3 sheen;
30 | #endif
31 |
32 | varying vec3 vViewPosition;
33 |
34 | #ifndef FLAT_SHADED
35 |
36 | varying vec3 vNormal;
37 |
38 | #ifdef USE_TANGENT
39 |
40 | varying vec3 vTangent;
41 | varying vec3 vBitangent;
42 |
43 | #endif
44 |
45 | #endif
46 |
47 | #include
48 | #include
49 | #include
50 | #include
51 | #include
52 | #include
53 | #include
54 | #include
55 | #include
56 | #include
57 | #include
58 | #include
59 | #include
60 | #include
61 | #include
62 | #include
63 | #include
64 | #include
65 | #include
66 | #include
67 | #include
68 | #include
69 | #include
70 | #include
71 | #include
72 | #include
73 |
74 | void main() {
75 |
76 | #include
77 |
78 | vec4 diffuseColor = vec4( diffuse, opacity );
79 | ReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) );
80 | vec3 totalEmissiveRadiance = emissive;
81 |
82 | #include
83 | #include
84 | #include
85 | #include
86 | #include
87 | #include
88 | #include
89 | #include
90 | #include
91 | #include
92 | #include
93 | #include
94 |
95 | // accumulation
96 | #include
97 | #include
98 | #include
99 | #include
100 |
101 | // modulation
102 | #include
103 |
104 | vec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + reflectedLight.directSpecular + reflectedLight.indirectSpecular + totalEmissiveRadiance;
105 |
106 | // this is a stub for the transparency model
107 | #ifdef TRANSPARENCY
108 | diffuseColor.a *= saturate( 1. - transparency + linearToRelativeLuminance( reflectedLight.directSpecular + reflectedLight.indirectSpecular ) );
109 | #endif
110 |
111 | gl_FragColor = vec4( outgoingLight, diffuseColor.a );
112 |
113 | #include
114 | #include
115 | #include
116 | #include
117 | #include
118 |
119 | }
120 |
--------------------------------------------------------------------------------
/src/shaders/paths.vert:
--------------------------------------------------------------------------------
1 | #define STANDARD
2 |
3 | vec3 applyQuaternionToVector( vec4 q, vec3 v ){
4 | return v + 2.0 * cross( q.xyz, cross( q.xyz, v ) + q.w * v );
5 | }
6 |
7 | uniform float uTime;
8 |
9 | attribute vec3 offset;
10 | attribute float scale;
11 | attribute vec4 quaternion;
12 |
13 | varying vec3 vViewPosition;
14 |
15 | #ifndef FLAT_SHADED
16 |
17 | varying vec3 vNormal;
18 |
19 | #ifdef USE_TANGENT
20 |
21 | varying vec3 vTangent;
22 | varying vec3 vBitangent;
23 |
24 | #endif
25 |
26 | #endif
27 |
28 | #include
29 | #include
30 | #include
31 | #include
32 | #include
33 | #include
34 | #include
35 | #include
36 | #include
37 | #include
38 | #include
39 |
40 | void main() {
41 |
42 | #include
43 | #include
44 | #include
45 |
46 | #include
47 | #include
48 | #include
49 | #include
50 | #include
51 |
52 | #ifndef FLAT_SHADED // Normal computed with derivatives when FLAT_SHADED
53 |
54 | vNormal = normalize( transformedNormal );
55 |
56 | #ifdef USE_TANGENT
57 |
58 | vTangent = normalize( transformedTangent );
59 | vBitangent = normalize( cross( vNormal, vTangent ) * tangent.w );
60 |
61 | #endif
62 |
63 | #endif
64 |
65 | #include
66 |
67 | transformed.xyz = applyQuaternionToVector( quaternion, transformed.xyz );
68 | transformed.xyz += offset.xyz;
69 |
70 | #include
71 | #include
72 | #include
73 | #include
74 | #include
75 | #include
76 |
77 | vViewPosition = - mvPosition.xyz;
78 |
79 | #include
80 | #include
81 | #include
82 |
83 | }
84 |
--------------------------------------------------------------------------------
/src/shaders/pickers.frag:
--------------------------------------------------------------------------------
1 | varying vec3 vPickerColor;
2 |
3 | void main() {
4 | gl_FragColor = vec4(vPickerColor.rgb, 1.0);
5 | }
6 |
--------------------------------------------------------------------------------
/src/shaders/pickers.vert:
--------------------------------------------------------------------------------
1 | #pragma glslify: applyQuaternionToVector = require('./applyQuaternionToVector');
2 | // #pragma glslify: rotationMatrix = require('./rotationMatrix');
3 |
4 | uniform float uTime;
5 |
6 | attribute vec3 pickerColor;
7 | attribute vec3 offset;
8 | attribute float scale;
9 | attribute vec4 quaternion;
10 | attribute float id;
11 | attribute float isSelected;
12 |
13 | varying vec3 vPickerColor;
14 |
15 | void main() {
16 |
17 | vPickerColor = pickerColor;
18 |
19 | #include
20 |
21 | // scale
22 | transformed.xyz *= scale;
23 | transformed.xyz = applyQuaternionToVector( quaternion, transformed.xyz );
24 | transformed.xyz += offset.xyz;
25 |
26 |
27 | #include
28 |
29 | }
--------------------------------------------------------------------------------
/src/shaders/position.frag:
--------------------------------------------------------------------------------
1 | #pragma glslify: curlNoise = require('./curlNoise');
2 |
3 | varying vec2 vUv;
4 |
5 | uniform sampler2D positionTexture;
6 | uniform sampler2D defaultPositionTexture;
7 | uniform float uFrame;
8 | uniform float uNoiseMix;
9 |
10 | void main() {
11 | vec4 defaultPosition = texture2D(defaultPositionTexture, vUv);
12 | vec4 currentPosition = texture2D(positionTexture, vUv);
13 |
14 | float kernelSize = 0.03;
15 | vec3 scaledPosition = vec3(currentPosition.x, currentPosition.y, sin(uFrame * 0.001) * 100.0) * kernelSize;
16 |
17 | float noiseSpeed = 0.4;
18 |
19 | currentPosition.xyz = currentPosition.xyz + (curlNoise(scaledPosition) ) * noiseSpeed;
20 |
21 | currentPosition = mix(defaultPosition, currentPosition, uNoiseMix * 0.95);
22 |
23 | gl_FragColor = currentPosition;
24 |
25 | }
--------------------------------------------------------------------------------
/src/shaders/rotationMatrix.glsl:
--------------------------------------------------------------------------------
1 | mat4 rotationMatrix(vec3 axis, float angle) {
2 | axis = normalize(axis);
3 | float s = sin(angle);
4 | float c = cos(angle);
5 | float oc = 1.0 - c;
6 |
7 | return mat4(oc * axis.x * axis.x + c, oc * axis.x * axis.y - axis.z * s, oc * axis.z * axis.x + axis.y * s, 0.0,
8 | oc * axis.x * axis.y + axis.z * s, oc * axis.y * axis.y + c, oc * axis.y * axis.z - axis.x * s, 0.0,
9 | oc * axis.z * axis.x - axis.y * s, oc * axis.y * axis.z + axis.x * s, oc * axis.z * axis.z + c, 0.0,
10 | 0.0, 0.0, 0.0, 1.0);
11 | }
12 |
13 | #pragma glslify: export(rotationMatrix)
14 |
--------------------------------------------------------------------------------
/webpack.prod.config.js:
--------------------------------------------------------------------------------
1 | const path = require('path')
2 |
3 | module.exports = {
4 | entry: {
5 | index: path.join(__dirname, './src/index.js')
6 | },
7 | output: {
8 | path: path.join(__dirname, './build'),
9 | filename: 'index.js',
10 | library: 'Main',
11 | libraryTarget: 'umd'
12 | },
13 | module: {
14 | rules: [
15 | {
16 | test: /\.(js|jsx|mjs)$/,
17 | loader: require.resolve('babel-loader'),
18 | options: {
19 | compact: false
20 | }
21 | },
22 | {
23 | test: /\.(glsl|frag|vert)$/,
24 | use: [
25 | require.resolve('raw-loader'),
26 | require.resolve('glslify-loader')
27 | ]
28 | },
29 | {
30 | test: /\.(glb)$/,
31 | use: [
32 | require.resolve('url-loader')
33 | ]
34 | },
35 | {
36 | test: /\.md$/,
37 | exclude: /node_modules/,
38 | use: [
39 | {
40 | loader: 'html-loader'
41 | },
42 | {
43 | loader: 'markdown-loader'
44 | }
45 | ]
46 | },
47 | // "url" loader works like "file" loader except that it embeds assets
48 | // smaller than specified limit in bytes as data URLs to avoid requests.
49 | // A missing `test` is equivalent to a match.
50 | {
51 | test: [/\.bmp$/, /\.gif$/, /\.jpe?g$/, /\.png$/, /\.svg$/],
52 | loader: require.resolve('url-loader')
53 | },
54 | {
55 | test: /\.css$/i,
56 | use: [
57 | {
58 | loader: 'style-loader'
59 | },
60 | {
61 | loader: 'css-loader',
62 | options: {
63 | modules: true
64 | }
65 | },
66 | {
67 | loader: 'sass-loader'
68 | }
69 | ]
70 | }
71 | ]
72 | },
73 | resolve: {
74 | extensions: ['.js']
75 | },
76 | mode: 'production'
77 | }
78 |
--------------------------------------------------------------------------------
/webpack_demo.config.js:
--------------------------------------------------------------------------------
1 | const path = require('path')
2 | const HtmlWebpackPlugin = require('html-webpack-plugin')
3 |
4 | module.exports = {
5 | entry: {
6 | index: path.join(__dirname, 'demo/src/index.js')
7 | },
8 | output: {
9 | path: path.join(__dirname, 'public/build'),
10 | filename: '[name].bundle.js',
11 | publicPath: '/'
12 | },
13 | module: {
14 | rules: [
15 | {
16 | test: /\.jsx?$/,
17 | exclude: /node_modules/,
18 | use: {
19 | loader: 'babel-loader'
20 | }
21 | },
22 | {
23 | test: /\.(glsl|frag|vert)$/,
24 | use: [
25 | require.resolve('raw-loader'),
26 | require.resolve('glslify-loader')
27 | ]
28 | },
29 | {
30 | test: /\.md$/,
31 | exclude: /node_modules/,
32 | use: [
33 | {
34 | loader: 'html-loader'
35 | },
36 | {
37 | loader: 'markdown-loader'
38 | }
39 | ]
40 | },
41 | // "url" loader works like "file" loader except that it embeds assets
42 | // smaller than specified limit in bytes as data URLs to avoid requests.
43 | // A missing `test` is equivalent to a match.
44 | {
45 | test: [/\.bmp$/, /\.gif$/, /\.jpe?g$/, /\.png$/, /\.svg$/],
46 | loader: require.resolve('file-loader')
47 | // options: {
48 | // limit: 1000
49 | // }
50 | },
51 | {
52 | test: /\.s?css$/,
53 | // exclude: /node_modules/,
54 | use: [
55 | {
56 | loader: 'style-loader'
57 | },
58 | {
59 | loader: 'css-loader',
60 | options: {
61 | modules: true
62 | }
63 | },
64 | {
65 | loader: 'sass-loader'
66 | }
67 | ]
68 | }
69 | ]
70 | },
71 | plugins: [
72 | new HtmlWebpackPlugin({
73 | template: './public/index.html'
74 | })
75 | ],
76 | devtool: 'inline-source-map',
77 | mode: 'development'
78 | }
79 |
--------------------------------------------------------------------------------
/webpack_demo.prod.config.js:
--------------------------------------------------------------------------------
1 | const path = require('path')
2 | const HtmlWebpackPlugin = require('html-webpack-plugin')
3 |
4 | module.exports = {
5 | entry: {
6 | index: path.join(__dirname, 'demo/src/index.js')
7 | },
8 | output: {
9 | path: path.join(__dirname, 'docs'),
10 | filename: '[name].bundle.js'
11 | },
12 | module: {
13 | rules: [
14 | {
15 | test: /\.(js|jsx|mjs)$/,
16 | loader: require.resolve('babel-loader'),
17 | options: {
18 | compact: false
19 | }
20 | },
21 | {
22 | test: /\.(glsl|frag|vert)$/,
23 | use: [
24 | require.resolve('raw-loader'),
25 | require.resolve('glslify-loader')
26 | ]
27 | },
28 | {
29 | test: /\.md$/,
30 | exclude: /node_modules/,
31 | use: [
32 | {
33 | loader: 'html-loader'
34 | },
35 | {
36 | loader: 'markdown-loader'
37 | }
38 | ]
39 | },
40 | // "url" loader works like "file" loader except that it embeds assets
41 | // smaller than specified limit in bytes as data URLs to avoid requests.
42 | // A missing `test` is equivalent to a match.
43 | {
44 | test: [/\.bmp$/, /\.gif$/, /\.jpe?g$/, /\.png$/, /\.svg$/],
45 | loader: require.resolve('file-loader'),
46 | options: {
47 | limit: 10000
48 | }
49 | },
50 | {
51 | test: /\.s?css$/,
52 | // exclude: /node_modules/,
53 | use: [
54 | {
55 | loader: 'style-loader'
56 | },
57 | {
58 | loader: 'css-loader',
59 | options: {
60 | modules: true
61 | }
62 | },
63 | {
64 | loader: 'sass-loader'
65 | }
66 | ]
67 |
68 | }
69 | ]
70 | },
71 | plugins: [
72 | new HtmlWebpackPlugin({
73 | template: './public/index.html'
74 | })
75 | ],
76 | mode: 'production'
77 | }
78 |
--------------------------------------------------------------------------------