first-comit
9
.editorconfig
Normal file
@ -0,0 +1,9 @@
|
||||
root = true
|
||||
|
||||
[*]
|
||||
charset = utf-8
|
||||
indent_style = space
|
||||
indent_size = 4
|
||||
end_of_line = lf
|
||||
insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
79
.eslintrc.json
Normal file
@ -0,0 +1,79 @@
|
||||
{
|
||||
"env":
|
||||
{
|
||||
"browser": true,
|
||||
"es6": true
|
||||
},
|
||||
"extends": ["eslint:recommended"],
|
||||
"parserOptions":
|
||||
{
|
||||
"sourceType": "module",
|
||||
"ecmaFeatures":{
|
||||
"experimentalObjectRestSpread": true
|
||||
}
|
||||
},
|
||||
"globals":
|
||||
{
|
||||
},
|
||||
"rules":
|
||||
{
|
||||
"no-unused-vars": 1,
|
||||
"no-console": 0,
|
||||
"dot-notation": 1,
|
||||
"eqeqeq": 1,
|
||||
"no-else-return": 0,
|
||||
"no-new-func": 1,
|
||||
"no-param-reassign": [1, { "props": false }],
|
||||
"no-useless-concat": 1,
|
||||
"no-useless-escape": 1,
|
||||
"radix": [1, "as-needed"],
|
||||
"no-undef": 2,
|
||||
"array-bracket-spacing": [1, "never"],
|
||||
"brace-style": [1, "allman"],
|
||||
"camelcase": [1, { "properties": "never" }],
|
||||
"comma-dangle": [1, "never"],
|
||||
"comma-style": [1, "last"],
|
||||
"func-style": [1, "expression"],
|
||||
"id-length": 0,
|
||||
"indent": [1, 4, { "SwitchCase": 1 }],
|
||||
"keyword-spacing": [1, { "after": false, "before": true, "overrides": { "from": { "after": true }, "return": { "after": true }, "import": { "after": true }, "case": { "after": true } } }],
|
||||
"max-len": 0,
|
||||
"new-cap": [1, { "newIsCap": true, "newIsCapExceptions": [], "capIsNew": false, "capIsNewExceptions": ["Immutable.Map", "Immutable.Set", "Immutable.List"] }],
|
||||
"no-array-constructor": 1,
|
||||
"no-bitwise": 0,
|
||||
"no-mixed-operators": 0,
|
||||
"no-nested-ternary": 0,
|
||||
"no-new-object": 1,
|
||||
"no-plusplus": 0,
|
||||
"no-restricted-syntax": 0,
|
||||
"no-trailing-spaces": 1,
|
||||
"no-underscore-dangle": 0,
|
||||
"no-unneeded-ternary": 1,
|
||||
"no-whitespace-before-property": 1,
|
||||
"object-curly-spacing": [1, "always"],
|
||||
"one-var": [1, "never"],
|
||||
"padded-blocks": [1, "never"],
|
||||
"quote-props": [1, "as-needed"],
|
||||
"quotes": [1, "single"],
|
||||
"semi": [1, "never"],
|
||||
"space-before-blocks": [1, "always"],
|
||||
"space-before-function-paren": [1, { "anonymous": "never", "named": "never", "asyncArrow": "never" }],
|
||||
"space-in-parens": [1, "never"],
|
||||
"space-infix-ops": 1,
|
||||
"spaced-comment": [1, "always"],
|
||||
"arrow-body-style": 0,
|
||||
"arrow-parens": [1, "always"],
|
||||
"arrow-spacing": [1, { "before": true, "after": true }],
|
||||
"no-confusing-arrow": 0,
|
||||
"no-dupe-class-members": 1,
|
||||
"no-duplicate-imports": 0,
|
||||
"no-useless-constructor": 1,
|
||||
"no-var": 1,
|
||||
"object-shorthand": 0,
|
||||
"prefer-const": 1,
|
||||
"prefer-rest-params": 1,
|
||||
"prefer-spread": 1,
|
||||
"prefer-template": 0,
|
||||
"template-curly-spacing": [1, "never"]
|
||||
}
|
||||
}
|
146
.gitignore
vendored
Normal file
@ -0,0 +1,146 @@
|
||||
# Project build
|
||||
dist/
|
||||
|
||||
# Parcel cache
|
||||
.cache
|
||||
|
||||
# Created by https://www.gitignore.io/api/vim,node,macos,visualstudiocode
|
||||
# Edit at https://www.gitignore.io/?templates=vim,node,macos,visualstudiocode
|
||||
|
||||
### macOS ###
|
||||
# General
|
||||
.DS_Store
|
||||
.AppleDouble
|
||||
.LSOverride
|
||||
|
||||
# Icon must end with two \r
|
||||
Icon
|
||||
|
||||
# Thumbnails
|
||||
._*
|
||||
|
||||
# Files that might appear in the root of a volume
|
||||
.DocumentRevisions-V100
|
||||
.fseventsd
|
||||
.Spotlight-V100
|
||||
.TemporaryItems
|
||||
.Trashes
|
||||
.VolumeIcon.icns
|
||||
.com.apple.timemachine.donotpresent
|
||||
|
||||
# Directories potentially created on remote AFP share
|
||||
.AppleDB
|
||||
.AppleDesktop
|
||||
Network Trash Folder
|
||||
Temporary Items
|
||||
.apdisk
|
||||
|
||||
### Node ###
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
|
||||
# Runtime data
|
||||
pids
|
||||
*.pid
|
||||
*.seed
|
||||
*.pid.lock
|
||||
|
||||
# Directory for instrumented libs generated by jscoverage/JSCover
|
||||
lib-cov
|
||||
|
||||
# Coverage directory used by tools like istanbul
|
||||
coverage
|
||||
|
||||
# nyc test coverage
|
||||
.nyc_output
|
||||
|
||||
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
|
||||
.grunt
|
||||
|
||||
# Bower dependency directory (https://bower.io/)
|
||||
bower_components
|
||||
|
||||
# node-waf configuration
|
||||
.lock-wscript
|
||||
|
||||
# Compiled binary addons (https://nodejs.org/api/addons.html)
|
||||
build/Release
|
||||
|
||||
# Dependency directories
|
||||
node_modules/
|
||||
jspm_packages/
|
||||
|
||||
# TypeScript v1 declaration files
|
||||
typings/
|
||||
|
||||
# Optional npm cache directory
|
||||
.npm
|
||||
|
||||
# Optional eslint cache
|
||||
.eslintcache
|
||||
|
||||
# Optional REPL history
|
||||
.node_repl_history
|
||||
|
||||
# Output of 'npm pack'
|
||||
*.tgz
|
||||
|
||||
# Yarn Integrity file
|
||||
.yarn-integrity
|
||||
|
||||
# dotenv environment variables file
|
||||
.env
|
||||
|
||||
# parcel-bundler cache (https://parceljs.org/)
|
||||
.cache
|
||||
|
||||
# next.js build output
|
||||
.next
|
||||
|
||||
# nuxt.js build output
|
||||
.nuxt
|
||||
|
||||
# vuepress build output
|
||||
.vuepress/dist
|
||||
|
||||
# Serverless directories
|
||||
.serverless
|
||||
|
||||
# FuseBox cache
|
||||
.fusebox/
|
||||
|
||||
### Vim ###
|
||||
# Swap
|
||||
[._]*.s[a-v][a-z]
|
||||
[._]*.sw[a-p]
|
||||
[._]s[a-rt-v][a-z]
|
||||
[._]ss[a-gi-z]
|
||||
[._]sw[a-p]
|
||||
|
||||
# Session
|
||||
Session.vim
|
||||
|
||||
# Temporary
|
||||
.netrwhist
|
||||
*~
|
||||
# Auto-generated tag files
|
||||
tags
|
||||
# Persistent undo
|
||||
[._]*.un~
|
||||
|
||||
### VisualStudioCode ###
|
||||
.vscode/*
|
||||
!.vscode/settings.json
|
||||
!.vscode/tasks.json
|
||||
!.vscode/launch.json
|
||||
!.vscode/extensions.json
|
||||
|
||||
### VisualStudioCode Patch ###
|
||||
# Ignore all local history of files
|
||||
.history
|
||||
|
||||
# End of https://www.gitignore.io/api/vim,node,macos,visualstudiocode
|
107
bundler/webpack.common.js
Normal file
@ -0,0 +1,107 @@
|
||||
const CopyWebpackPlugin = require('copy-webpack-plugin')
|
||||
const HtmlWebpackPlugin = require('html-webpack-plugin')
|
||||
const path = require('path')
|
||||
|
||||
module.exports = {
|
||||
entry: path.resolve(__dirname, '../src/index.js'),
|
||||
output:
|
||||
{
|
||||
filename: 'bundle.[hash].js',
|
||||
path: path.resolve(__dirname, '../dist')
|
||||
},
|
||||
devtool: 'source-map',
|
||||
plugins:
|
||||
[
|
||||
new CopyWebpackPlugin([ { from: path.resolve(__dirname, '../static') } ]),
|
||||
new HtmlWebpackPlugin({
|
||||
template: path.resolve(__dirname, '../src/index.html'),
|
||||
minify: true
|
||||
})
|
||||
],
|
||||
module:
|
||||
{
|
||||
rules:
|
||||
[
|
||||
// HTML
|
||||
{
|
||||
test: /\.(html)$/,
|
||||
use: ['html-loader']
|
||||
},
|
||||
|
||||
// JS
|
||||
{
|
||||
test: /\.js$/,
|
||||
exclude: /node_modules/,
|
||||
use:
|
||||
[
|
||||
'babel-loader'
|
||||
]
|
||||
},
|
||||
|
||||
// CSS
|
||||
{
|
||||
test: /\.css$/,
|
||||
use:
|
||||
[
|
||||
'style-loader',
|
||||
'css-loader'
|
||||
]
|
||||
},
|
||||
|
||||
// Images
|
||||
{
|
||||
test: /\.(jpg|png|gif|svg)$/,
|
||||
use:
|
||||
[
|
||||
{
|
||||
loader: 'file-loader',
|
||||
options:
|
||||
{
|
||||
outputPath: 'assets/images/'
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
// Models
|
||||
{
|
||||
test: /\.(glb|gltf|fbx|obj)$/,
|
||||
use:
|
||||
[
|
||||
{
|
||||
loader: 'file-loader',
|
||||
options:
|
||||
{
|
||||
outputPath: 'assets/models/'
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
// MP3
|
||||
{
|
||||
test: /\.(mp3)$/,
|
||||
use:
|
||||
[
|
||||
{
|
||||
loader: 'file-loader',
|
||||
options:
|
||||
{
|
||||
outputPath: 'assets/audios/'
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
// Shaders
|
||||
{
|
||||
test: /\.(glsl|vs|fs|vert|frag)$/,
|
||||
exclude: /node_modules/,
|
||||
use: [
|
||||
'raw-loader',
|
||||
'glslify-loader'
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
14
bundler/webpack.dev.js
Normal file
@ -0,0 +1,14 @@
|
||||
const webpackMerge = require('webpack-merge')
|
||||
const commonConfiguration = require('./webpack.common.js')
|
||||
|
||||
module.exports = webpackMerge(
|
||||
commonConfiguration,
|
||||
{
|
||||
mode: 'development',
|
||||
devServer:
|
||||
{
|
||||
contentBase: './dist',
|
||||
open: true
|
||||
}
|
||||
}
|
||||
)
|
14
bundler/webpack.prod.js
Normal file
@ -0,0 +1,14 @@
|
||||
const webpackMerge = require('webpack-merge')
|
||||
const commonConfiguration = require('./webpack.common.js')
|
||||
const { CleanWebpackPlugin } = require('clean-webpack-plugin')
|
||||
|
||||
module.exports = webpackMerge(
|
||||
commonConfiguration,
|
||||
{
|
||||
mode: 'production',
|
||||
plugins:
|
||||
[
|
||||
new CleanWebpackPlugin()
|
||||
]
|
||||
}
|
||||
)
|
21
license.md
Normal file
@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2019 Bruno SIMON
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
BIN
model.blend
Normal file
17251
package-lock.json
generated
Normal file
48
package.json
Normal file
@ -0,0 +1,48 @@
|
||||
{
|
||||
"name": "three.js-template",
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"build": "webpack --config ./bundler/webpack.prod.js",
|
||||
"dev": "webpack-dev-server --config ./bundler/webpack.dev.js",
|
||||
"watch": "webpack --watch --config ./bundler/webpack.config.js"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/brunosimon/three.js-template.git"
|
||||
},
|
||||
"author": "brunosimon",
|
||||
"license": "MIT",
|
||||
"bugs": {
|
||||
"url": "https://github.com/brunosimon/three.js-template/issues"
|
||||
},
|
||||
"homepage": "https://github.com/brunosimon/three.js-template#readme",
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.7.7",
|
||||
"@babel/preset-env": "^7.7.7",
|
||||
"babel-loader": "^8.0.6",
|
||||
"cannon": "^0.6.2",
|
||||
"clean-webpack-plugin": "^3.0.0",
|
||||
"copy-webpack-plugin": "^5.1.1",
|
||||
"css-loader": "^3.4.0",
|
||||
"dat.gui": "^0.7.3",
|
||||
"file-loader": "^5.0.2",
|
||||
"glslify-loader": "^2.0.0",
|
||||
"gsap": "^2.1.3",
|
||||
"howler": "^2.1.2",
|
||||
"html-loader": "^0.5.5",
|
||||
"html-webpack-plugin": "^3.2.0",
|
||||
"raw-loader": "^4.0.0",
|
||||
"style-loader": "^1.1.2",
|
||||
"three": "^0.131.3",
|
||||
"webpack": "^4.41.5",
|
||||
"webpack-cli": "^3.3.10",
|
||||
"webpack-dev-server": "^3.10.1",
|
||||
"webpack-merge": "^4.2.2"
|
||||
},
|
||||
"staticFiles": {
|
||||
"staticPath": "static",
|
||||
"watcherGlob": "**"
|
||||
}
|
||||
}
|
23
readme.md
Normal file
@ -0,0 +1,23 @@
|
||||
# Folio 2019
|
||||
|
||||
## Setup
|
||||
Download [Node.js](https://nodejs.org/en/download/).
|
||||
Run this followed commands:
|
||||
|
||||
``` bash
|
||||
# Just be sure that you've got parcel js on you system
|
||||
npm install -g parcel-bundler
|
||||
|
||||
# Install dependencies (only for first time)
|
||||
npm i
|
||||
|
||||
# Serve at localhost:1234
|
||||
npm run dev
|
||||
|
||||
# Build for production in the dist/ directory
|
||||
npm run build
|
||||
```
|
||||
|
||||
```
|
||||
🥚 2021eggpvlzscw
|
||||
```
|
BIN
render.blend
Normal file
BIN
resources/3d/model.blend
Normal file
BIN
resources/3d/model.blend1
Normal file
BIN
resources/3d/portfolio.blend.zip
Normal file
BIN
resources/3d/render.blend
Normal file
BIN
resources/3d/render.blend1
Normal file
BIN
src/favicon/android-chrome-192x192.png
Normal file
After Width: | Height: | Size: 3.9 KiB |
BIN
src/favicon/android-chrome-256x256.png
Normal file
After Width: | Height: | Size: 6.0 KiB |
BIN
src/favicon/apple-touch-icon.png
Normal file
After Width: | Height: | Size: 2.7 KiB |
9
src/favicon/browserconfig.xml
Normal file
@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<browserconfig>
|
||||
<msapplication>
|
||||
<tile>
|
||||
<square150x150logo src="/mstile-150x150.png"/>
|
||||
<TileColor>#da532c</TileColor>
|
||||
</tile>
|
||||
</msapplication>
|
||||
</browserconfig>
|
BIN
src/favicon/favicon-16x16.png
Normal file
After Width: | Height: | Size: 513 B |
BIN
src/favicon/favicon-32x32.png
Normal file
After Width: | Height: | Size: 819 B |
BIN
src/favicon/favicon.ico
Normal file
After Width: | Height: | Size: 15 KiB |
BIN
src/favicon/mstile-150x150.png
Normal file
After Width: | Height: | Size: 1.2 KiB |
1
src/favicon/safari-pinned-tab.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg version="1" xmlns="http://www.w3.org/2000/svg" width="346.667" height="346.667" viewBox="0 0 260.000000 260.000000"><path d="M176.3 32.1c-19.4 3.1-33.5 16.4-37.3 35.4-1.7 8.1-.7 24.3 1.9 31.7 4.4 12.8 8.8 18 34.1 40.1 25.8 22.7 30.8 29.9 31.1 45.4.3 11.1-4.7 17.3-14.5 18.1-4.6.4-6.2 0-10-2.2-2.4-1.4-5.3-4.1-6.5-5.9-2.6-4.2-6-16.8-6.2-23.2-.1-2.8-.4-5.3-.5-5.6-.3-.5-11.9 1.5-26.9 4.5-1.1.3-2.4.5-2.9.5-.5.1-.6 3.3-.3 7.3 3 35.8 21.7 53.4 55.7 52.3 23.5-.7 40.4-14.3 45.4-36.5 2.6-11.8.5-28.3-5.1-39.5-5.7-11.2-13.1-19.6-32.3-36.5-21.7-19-24.7-22.2-27.9-29-4.7-10.4-2.9-23.3 4.2-28.4 4.2-3.1 15-3 19.4.2 4 2.9 7.1 10.8 7.9 20.5.3 3.9.7 7.4 1 7.8.2.4 2.1.3 4.1-.1 2.1-.5 5.6-1.1 7.8-1.4 2.3-.4 5.2-.9 6.5-1.2 1.4-.2 4.7-.7 7.3-1.1l4.9-.6-.7-6.6c-.4-3.6-1.8-10.1-3.1-14.4-5.5-17.2-13.7-26.2-27.8-30.2-6.7-1.9-21.4-2.6-29.3-1.4zM20.6 34c-.1.3-.2 43.9-.2 97l.1 96.5H55c36.1 0 38.6-.2 47.1-3.5 16-6.3 24.6-18.6 28.4-40.5.3-1.7.5-7.5.5-13 0-16.2-4.4-28.9-12.8-37.1-3.9-3.8-10.7-7.6-15.7-8.8l-3-.8 5.2-2.2c15.3-6.5 21.9-20.2 20.7-42.9C124 52.5 110.9 38.5 84 34.4c-5.4-.8-63-1.2-63.4-.4zm55 25.5c5.2.7 11 3.3 13.6 6.2 4.6 5.1 6.6 17.4 4.6 28.5-2.5 13.8-8.2 17.7-26.5 18.6l-11.3.5v-27c0-14.8.2-27.2.5-27.4.6-.7 13.1-.3 19.1.6zm3.8 79.6c8.1 1.9 13.5 7.9 16.1 18 1.8 6.8 1.8 21.4 0 28.4-3.4 12.8-9.5 16.6-27.7 17.3l-11.8.4v-32.5c0-29.4.2-32.6 1.6-32.9 3.4-.6 17 .2 21.8 1.3z"/></svg>
|
After Width: | Height: | Size: 1.3 KiB |
19
src/favicon/site.webmanifest
Normal file
@ -0,0 +1,19 @@
|
||||
{
|
||||
"name": "Bruno Simon",
|
||||
"short_name": "Bruno Simon",
|
||||
"icons": [
|
||||
{
|
||||
"src": "/favicon/android-chrome-192x192.png",
|
||||
"sizes": "192x192",
|
||||
"type": "image/png"
|
||||
},
|
||||
{
|
||||
"src": "/favicon/android-chrome-256x256.png",
|
||||
"sizes": "256x256",
|
||||
"type": "image/png"
|
||||
}
|
||||
],
|
||||
"theme_color": "#ffffff",
|
||||
"background_color": "#ffffff",
|
||||
"display": "standalone"
|
||||
}
|
BIN
src/images/boyHiArm.png
Normal file
After Width: | Height: | Size: 2.4 KiB |
BIN
src/images/boyHiBody.png
Normal file
After Width: | Height: | Size: 6.2 KiB |
BIN
src/images/boyShrugging.png
Normal file
After Width: | Height: | Size: 6.9 KiB |
BIN
src/images/boyYay.png
Normal file
After Width: | Height: | Size: 6.5 KiB |
4
src/images/bubbleTip.svg
Normal file
@ -0,0 +1,4 @@
|
||||
<svg width="12" height="15" viewBox="0 0 12 15" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M10.5 7.5L1 1V14L10.5 7.5Z" fill="#f8c684"/>
|
||||
<path d="M10.5 7.5L11.0647 8.32531C11.3371 8.13891 11.5 7.83009 11.5 7.5C11.5 7.16991 11.3371 6.86109 11.0647 6.67469L10.5 7.5ZM0.435316 1.82531L9.93532 8.32531L11.0647 6.67469L1.56468 0.174693L0.435316 1.82531ZM9.93532 6.67469L0.435316 13.1747L1.56468 14.8253L11.0647 8.32531L9.93532 6.67469Z" fill="white"/>
|
||||
</svg>
|
After Width: | Height: | Size: 467 B |
BIN
src/images/mobile/cross.png
Normal file
After Width: | Height: | Size: 284 B |
BIN
src/images/mobile/doubleTriangle.png
Normal file
After Width: | Height: | Size: 426 B |
BIN
src/images/mobile/triangle.png
Normal file
After Width: | Height: | Size: 401 B |
96
src/index.html
Normal file
@ -0,0 +1,96 @@
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
|
||||
<title>Bruno Simon</title>
|
||||
<meta name="description" content="Creative developer living in Paris, freelancer, former lead developer at Immersive Garden, former developer at Uzik and teacher." />
|
||||
|
||||
<meta itemprop=name content="Bruno Simon - Creative developer">
|
||||
<meta itemprop="description" content="Creative developer living in Paris, freelancer, former lead developer at Immersive Garden, former developer at Uzik and teacher.">
|
||||
<meta itemprop="image" content=https://bruno-simon.com/social/share-1200x630.png>
|
||||
|
||||
<meta name="twitter:card" content="summary_large_image">
|
||||
<meta name="twitter:title" content="Bruno Simon - Creative developer">
|
||||
<meta name="twitter:description" content="Creative developer living in Paris, freelancer, former lead developer at Immersive Garden, former developer at Uzik and teacher.">
|
||||
<meta name="twitter:image" content="https://bruno-simon.com/social/share-1200x600.png">
|
||||
|
||||
<meta property="og:site_name" content="Bruno Simon - Creative developer">
|
||||
<meta property="og:type" content="website">
|
||||
<meta property="og:url" content="https://bruno-simon.com">
|
||||
<meta property="og:title" content="Bruno Simon - Creative developer">
|
||||
<meta property="og:description" content="Creative developer living in Paris, freelancer, former lead developer at Immersive Garden, former developer at Uzik and teacher.">
|
||||
<meta property="og:image" content="https://bruno-simon.com/social/share-1200x630.png">
|
||||
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
|
||||
<link rel="apple-touch-icon" sizes="180x180" href="/favicon/apple-touch-icon.png">
|
||||
<link rel="icon" type="image/png" sizes="32x32" href="/favicon/favicon-32x32.png">
|
||||
<link rel="icon" type="image/png" sizes="16x16" href="/favicon/favicon-16x16.png">
|
||||
<link rel="manifest" href="/favicon/site.webmanifest">
|
||||
<link rel="mask-icon" href="/favicon/safari-pinned-tab.svg" color="#ff8908">
|
||||
<meta name="apple-mobile-web-app-title" content="Bruno Simon">
|
||||
<meta name="application-name" content="Bruno Simon">
|
||||
<meta name="msapplication-TileColor" content="#da532c">
|
||||
<meta name="theme-color" content="#ffffff">
|
||||
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Comic+Neue:wght@700&display=block" rel="stylesheet">
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<canvas class="canvas js-canvas"></canvas>
|
||||
|
||||
<div class="threejs-journey is-hover-none js-threejs-journey">
|
||||
<div class="message js-message">
|
||||
<div class="boy">
|
||||
<div class="variant is-hi">
|
||||
<div class="body"></div>
|
||||
<div class="arm js-boy-arm"></div>
|
||||
</div>
|
||||
<div class="variant is-yay"></div>
|
||||
<div class="variant is-shrugging"></div>
|
||||
</div>
|
||||
<div class="bubble">
|
||||
<div class="text">Hey! You seem to really enjoy my portfolio.</div>
|
||||
<div class="tip"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="message js-message">
|
||||
<div class="bubble">
|
||||
<div class="text">Would you like to learn how to create cool websites like this?</div>
|
||||
<div class="tip"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="message js-message is-answers">
|
||||
<a href="#" class="answer is-no js-no">
|
||||
<span class="background"></span>
|
||||
<span class="hover"></span>
|
||||
<span class="label">Nah, I'm good</span>
|
||||
</a>
|
||||
<a href="https://threejs-journey.com?c=p1" target="_blank" rel="noopener" class="answer is-yes js-yes">
|
||||
<span class="background"></span>
|
||||
<span class="hover"></span>
|
||||
<span class="label">Yes, teach me!</span>
|
||||
</a>
|
||||
</div>
|
||||
<div class="message js-message">
|
||||
<div class="bubble">
|
||||
<div class="text">Alright then.<br>Have fun and try not to break my car!</div>
|
||||
<div class="tip"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script async src="https://www.googletagmanager.com/gtag/js?id=UA-3966601-5"></script>
|
||||
<script>
|
||||
window.dataLayer = window.dataLayer || []
|
||||
function gtag(){dataLayer.push(arguments)}
|
||||
gtag('js', new Date())
|
||||
gtag('config', 'UA-3966601-5')
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
7
src/index.js
Normal file
@ -0,0 +1,7 @@
|
||||
import './style/main.css'
|
||||
import Application from './javascript/Application.js'
|
||||
|
||||
window.application = new Application({
|
||||
$canvas: document.querySelector('.js-canvas'),
|
||||
useComposer: true
|
||||
})
|
294
src/javascript/Application.js
Normal file
@ -0,0 +1,294 @@
|
||||
import * as THREE from 'three'
|
||||
import * as dat from 'dat.gui'
|
||||
|
||||
import Sizes from './Utils/Sizes.js'
|
||||
import Time from './Utils/Time.js'
|
||||
import World from './World/index.js'
|
||||
import Resources from './Resources.js'
|
||||
import Camera from './Camera.js'
|
||||
import ThreejsJourney from './ThreejsJourney.js'
|
||||
|
||||
import { EffectComposer } from 'three/examples/jsm/postprocessing/EffectComposer.js'
|
||||
import { ShaderPass } from 'three/examples/jsm/postprocessing/ShaderPass.js'
|
||||
import { RenderPass } from 'three/examples/jsm/postprocessing/RenderPass.js'
|
||||
import BlurPass from './Passes/Blur.js'
|
||||
import GlowsPass from './Passes/Glows.js'
|
||||
|
||||
export default class Application
|
||||
{
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
constructor(_options)
|
||||
{
|
||||
// Options
|
||||
this.$canvas = _options.$canvas
|
||||
|
||||
// Set up
|
||||
this.time = new Time()
|
||||
this.sizes = new Sizes()
|
||||
this.resources = new Resources()
|
||||
|
||||
this.setConfig()
|
||||
this.setDebug()
|
||||
this.setRenderer()
|
||||
this.setCamera()
|
||||
this.setPasses()
|
||||
this.setWorld()
|
||||
this.setTitle()
|
||||
this.setThreejsJourney()
|
||||
}
|
||||
|
||||
/**
|
||||
* Set config
|
||||
*/
|
||||
setConfig()
|
||||
{
|
||||
this.config = {}
|
||||
this.config.debug = window.location.hash === '#debug'
|
||||
this.config.cyberTruck = window.location.hash === '#cybertruck'
|
||||
this.config.touch = false
|
||||
|
||||
window.addEventListener('touchstart', () =>
|
||||
{
|
||||
this.config.touch = true
|
||||
this.world.controls.setTouch()
|
||||
|
||||
this.passes.horizontalBlurPass.strength = 1
|
||||
this.passes.horizontalBlurPass.material.uniforms.uStrength.value = new THREE.Vector2(this.passes.horizontalBlurPass.strength, 0)
|
||||
this.passes.verticalBlurPass.strength = 1
|
||||
this.passes.verticalBlurPass.material.uniforms.uStrength.value = new THREE.Vector2(0, this.passes.verticalBlurPass.strength)
|
||||
}, { once: true })
|
||||
}
|
||||
|
||||
/**
|
||||
* Set debug
|
||||
*/
|
||||
setDebug()
|
||||
{
|
||||
if(this.config.debug)
|
||||
{
|
||||
this.debug = new dat.GUI({ width: 420 })
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set renderer
|
||||
*/
|
||||
setRenderer()
|
||||
{
|
||||
// Scene
|
||||
this.scene = new THREE.Scene()
|
||||
|
||||
// Renderer
|
||||
this.renderer = new THREE.WebGLRenderer({
|
||||
canvas: this.$canvas,
|
||||
alpha: true
|
||||
})
|
||||
// this.renderer.setClearColor(0x414141, 1)
|
||||
this.renderer.setClearColor(0x000000, 1)
|
||||
// this.renderer.setPixelRatio(Math.min(Math.max(window.devicePixelRatio, 1.5), 2))
|
||||
this.renderer.setPixelRatio(2)
|
||||
this.renderer.setSize(this.sizes.viewport.width, this.sizes.viewport.height)
|
||||
this.renderer.physicallyCorrectLights = true
|
||||
this.renderer.gammaFactor = 2.2
|
||||
this.renderer.gammaOutPut = true
|
||||
this.renderer.autoClear = false
|
||||
|
||||
// Resize event
|
||||
this.sizes.on('resize', () =>
|
||||
{
|
||||
this.renderer.setSize(this.sizes.viewport.width, this.sizes.viewport.height)
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Set camera
|
||||
*/
|
||||
setCamera()
|
||||
{
|
||||
this.camera = new Camera({
|
||||
time: this.time,
|
||||
sizes: this.sizes,
|
||||
renderer: this.renderer,
|
||||
debug: this.debug,
|
||||
config: this.config
|
||||
})
|
||||
|
||||
this.scene.add(this.camera.container)
|
||||
|
||||
this.time.on('tick', () =>
|
||||
{
|
||||
if(this.world && this.world.car)
|
||||
{
|
||||
this.camera.target.x = this.world.car.chassis.object.position.x
|
||||
this.camera.target.y = this.world.car.chassis.object.position.y
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
setPasses()
|
||||
{
|
||||
this.passes = {}
|
||||
|
||||
// Debug
|
||||
if(this.debug)
|
||||
{
|
||||
this.passes.debugFolder = this.debug.addFolder('postprocess')
|
||||
// this.passes.debugFolder.open()
|
||||
}
|
||||
|
||||
this.passes.composer = new EffectComposer(this.renderer)
|
||||
|
||||
// Create passes
|
||||
this.passes.renderPass = new RenderPass(this.scene, this.camera.instance)
|
||||
|
||||
this.passes.horizontalBlurPass = new ShaderPass(BlurPass)
|
||||
this.passes.horizontalBlurPass.strength = this.config.touch ? 0 : 1
|
||||
this.passes.horizontalBlurPass.material.uniforms.uResolution.value = new THREE.Vector2(this.sizes.viewport.width, this.sizes.viewport.height)
|
||||
this.passes.horizontalBlurPass.material.uniforms.uStrength.value = new THREE.Vector2(this.passes.horizontalBlurPass.strength, 0)
|
||||
|
||||
this.passes.verticalBlurPass = new ShaderPass(BlurPass)
|
||||
this.passes.verticalBlurPass.strength = this.config.touch ? 0 : 1
|
||||
this.passes.verticalBlurPass.material.uniforms.uResolution.value = new THREE.Vector2(this.sizes.viewport.width, this.sizes.viewport.height)
|
||||
this.passes.verticalBlurPass.material.uniforms.uStrength.value = new THREE.Vector2(0, this.passes.verticalBlurPass.strength)
|
||||
|
||||
// Debug
|
||||
if(this.debug)
|
||||
{
|
||||
const folder = this.passes.debugFolder.addFolder('blur')
|
||||
folder.open()
|
||||
|
||||
folder.add(this.passes.horizontalBlurPass.material.uniforms.uStrength.value, 'x').step(0.001).min(0).max(10)
|
||||
folder.add(this.passes.verticalBlurPass.material.uniforms.uStrength.value, 'y').step(0.001).min(0).max(10)
|
||||
}
|
||||
|
||||
this.passes.glowsPass = new ShaderPass(GlowsPass)
|
||||
this.passes.glowsPass.color = '#ffcfe0'
|
||||
this.passes.glowsPass.material.uniforms.uPosition.value = new THREE.Vector2(0, 0.25)
|
||||
this.passes.glowsPass.material.uniforms.uRadius.value = 0.7
|
||||
this.passes.glowsPass.material.uniforms.uColor.value = new THREE.Color(this.passes.glowsPass.color)
|
||||
this.passes.glowsPass.material.uniforms.uAlpha.value = 0.55
|
||||
|
||||
// Debug
|
||||
if(this.debug)
|
||||
{
|
||||
const folder = this.passes.debugFolder.addFolder('glows')
|
||||
folder.open()
|
||||
|
||||
folder.add(this.passes.glowsPass.material.uniforms.uPosition.value, 'x').step(0.001).min(- 1).max(2).name('positionX')
|
||||
folder.add(this.passes.glowsPass.material.uniforms.uPosition.value, 'y').step(0.001).min(- 1).max(2).name('positionY')
|
||||
folder.add(this.passes.glowsPass.material.uniforms.uRadius, 'value').step(0.001).min(0).max(2).name('radius')
|
||||
folder.addColor(this.passes.glowsPass, 'color').name('color').onChange(() =>
|
||||
{
|
||||
this.passes.glowsPass.material.uniforms.uColor.value = new THREE.Color(this.passes.glowsPass.color)
|
||||
})
|
||||
folder.add(this.passes.glowsPass.material.uniforms.uAlpha, 'value').step(0.001).min(0).max(1).name('alpha')
|
||||
}
|
||||
|
||||
// Add passes
|
||||
this.passes.composer.addPass(this.passes.renderPass)
|
||||
this.passes.composer.addPass(this.passes.horizontalBlurPass)
|
||||
this.passes.composer.addPass(this.passes.verticalBlurPass)
|
||||
this.passes.composer.addPass(this.passes.glowsPass)
|
||||
|
||||
// Time tick
|
||||
this.time.on('tick', () =>
|
||||
{
|
||||
this.passes.horizontalBlurPass.enabled = this.passes.horizontalBlurPass.material.uniforms.uStrength.value.x > 0
|
||||
this.passes.verticalBlurPass.enabled = this.passes.verticalBlurPass.material.uniforms.uStrength.value.y > 0
|
||||
|
||||
// Renderer
|
||||
this.passes.composer.render()
|
||||
// this.renderer.domElement.style.background = 'black'
|
||||
// this.renderer.render(this.scene, this.camera.instance)
|
||||
})
|
||||
|
||||
// Resize event
|
||||
this.sizes.on('resize', () =>
|
||||
{
|
||||
this.renderer.setSize(this.sizes.viewport.width, this.sizes.viewport.height)
|
||||
this.passes.composer.setSize(this.sizes.viewport.width, this.sizes.viewport.height)
|
||||
this.passes.horizontalBlurPass.material.uniforms.uResolution.value.x = this.sizes.viewport.width
|
||||
this.passes.horizontalBlurPass.material.uniforms.uResolution.value.y = this.sizes.viewport.height
|
||||
this.passes.verticalBlurPass.material.uniforms.uResolution.value.x = this.sizes.viewport.width
|
||||
this.passes.verticalBlurPass.material.uniforms.uResolution.value.y = this.sizes.viewport.height
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Set world
|
||||
*/
|
||||
setWorld()
|
||||
{
|
||||
this.world = new World({
|
||||
config: this.config,
|
||||
debug: this.debug,
|
||||
resources: this.resources,
|
||||
time: this.time,
|
||||
sizes: this.sizes,
|
||||
camera: this.camera,
|
||||
renderer: this.renderer,
|
||||
passes: this.passes
|
||||
})
|
||||
this.scene.add(this.world.container)
|
||||
}
|
||||
|
||||
/**
|
||||
* Set title
|
||||
*/
|
||||
setTitle()
|
||||
{
|
||||
this.title = {}
|
||||
this.title.frequency = 300
|
||||
this.title.width = 20
|
||||
this.title.position = 0
|
||||
this.title.$element = document.querySelector('title')
|
||||
this.title.absolutePosition = Math.round(this.title.width * 0.25)
|
||||
|
||||
this.time.on('tick', () =>
|
||||
{
|
||||
if(this.world.physics)
|
||||
{
|
||||
this.title.absolutePosition += this.world.physics.car.forwardSpeed
|
||||
|
||||
if(this.title.absolutePosition < 0)
|
||||
{
|
||||
this.title.absolutePosition = 0
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
window.setInterval(() =>
|
||||
{
|
||||
this.title.position = Math.round(this.title.absolutePosition % this.title.width)
|
||||
|
||||
document.title = `${'_'.repeat(this.title.width - this.title.position)}🚗${'_'.repeat(this.title.position)}`
|
||||
}, this.title.frequency)
|
||||
}
|
||||
|
||||
/**
|
||||
* Set Three.js Journey
|
||||
*/
|
||||
setThreejsJourney()
|
||||
{
|
||||
this.threejsJourney = new ThreejsJourney({
|
||||
config: this.config,
|
||||
time: this.time,
|
||||
world: this.world
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Destructor
|
||||
*/
|
||||
destructor()
|
||||
{
|
||||
this.time.off('tick')
|
||||
this.sizes.off('resize')
|
||||
|
||||
this.camera.orbitControls.dispose()
|
||||
this.renderer.dispose()
|
||||
this.debug.destroy()
|
||||
}
|
||||
}
|
348
src/javascript/Camera.js
Normal file
@ -0,0 +1,348 @@
|
||||
import * as THREE from 'three'
|
||||
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js'
|
||||
import { TweenLite } from 'gsap/TweenLite'
|
||||
import { Power1 } from 'gsap/EasePack'
|
||||
|
||||
export default class Camera
|
||||
{
|
||||
constructor(_options)
|
||||
{
|
||||
// Options
|
||||
this.time = _options.time
|
||||
this.sizes = _options.sizes
|
||||
this.renderer = _options.renderer
|
||||
this.debug = _options.debug
|
||||
this.config = _options.config
|
||||
|
||||
// Set up
|
||||
this.container = new THREE.Object3D()
|
||||
this.container.matrixAutoUpdate = false
|
||||
|
||||
this.target = new THREE.Vector3(0, 0, 0)
|
||||
this.targetEased = new THREE.Vector3(0, 0, 0)
|
||||
this.easing = 0.15
|
||||
|
||||
// Debug
|
||||
if(this.debug)
|
||||
{
|
||||
this.debugFolder = this.debug.addFolder('camera')
|
||||
// this.debugFolder.open()
|
||||
}
|
||||
|
||||
this.setAngle()
|
||||
this.setInstance()
|
||||
this.setZoom()
|
||||
this.setPan()
|
||||
this.setOrbitControls()
|
||||
}
|
||||
|
||||
setAngle()
|
||||
{
|
||||
// Set up
|
||||
this.angle = {}
|
||||
|
||||
// Items
|
||||
this.angle.items = {
|
||||
default: new THREE.Vector3(1.135, - 1.45, 1.15),
|
||||
projects: new THREE.Vector3(0.38, - 1.4, 1.63)
|
||||
}
|
||||
|
||||
// Value
|
||||
this.angle.value = new THREE.Vector3()
|
||||
this.angle.value.copy(this.angle.items.default)
|
||||
|
||||
// Set method
|
||||
this.angle.set = (_name) =>
|
||||
{
|
||||
const angle = this.angle.items[_name]
|
||||
if(typeof angle !== 'undefined')
|
||||
{
|
||||
TweenLite.to(this.angle.value, 2, { ...angle, ease: Power1.easeInOut })
|
||||
}
|
||||
}
|
||||
|
||||
// Debug
|
||||
if(this.debug)
|
||||
{
|
||||
this.debugFolder.add(this, 'easing').step(0.0001).min(0).max(1).name('easing')
|
||||
this.debugFolder.add(this.angle.value, 'x').step(0.001).min(- 2).max(2).name('invertDirectionX').listen()
|
||||
this.debugFolder.add(this.angle.value, 'y').step(0.001).min(- 2).max(2).name('invertDirectionY').listen()
|
||||
this.debugFolder.add(this.angle.value, 'z').step(0.001).min(- 2).max(2).name('invertDirectionZ').listen()
|
||||
}
|
||||
}
|
||||
|
||||
setInstance()
|
||||
{
|
||||
// Set up
|
||||
this.instance = new THREE.PerspectiveCamera(40, this.sizes.viewport.width / this.sizes.viewport.height, 1, 80)
|
||||
this.instance.up.set(0, 0, 1)
|
||||
this.instance.position.copy(this.angle.value)
|
||||
this.instance.lookAt(new THREE.Vector3())
|
||||
this.container.add(this.instance)
|
||||
|
||||
// Resize event
|
||||
this.sizes.on('resize', () =>
|
||||
{
|
||||
this.instance.aspect = this.sizes.viewport.width / this.sizes.viewport.height
|
||||
this.instance.updateProjectionMatrix()
|
||||
})
|
||||
|
||||
// Time tick
|
||||
this.time.on('tick', () =>
|
||||
{
|
||||
if(!this.orbitControls.enabled)
|
||||
{
|
||||
this.targetEased.x += (this.target.x - this.targetEased.x) * this.easing
|
||||
this.targetEased.y += (this.target.y - this.targetEased.y) * this.easing
|
||||
this.targetEased.z += (this.target.z - this.targetEased.z) * this.easing
|
||||
|
||||
// Apply zoom
|
||||
this.instance.position.copy(this.targetEased).add(this.angle.value.clone().normalize().multiplyScalar(this.zoom.distance))
|
||||
|
||||
// Look at target
|
||||
this.instance.lookAt(this.targetEased)
|
||||
|
||||
// Apply pan
|
||||
this.instance.position.x += this.pan.value.x
|
||||
this.instance.position.y += this.pan.value.y
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
setZoom()
|
||||
{
|
||||
// Set up
|
||||
this.zoom = {}
|
||||
this.zoom.easing = 0.1
|
||||
this.zoom.minDistance = 14
|
||||
this.zoom.amplitude = 15
|
||||
this.zoom.value = this.config.cyberTruck ? 0.3 : 0.5
|
||||
this.zoom.targetValue = this.zoom.value
|
||||
this.zoom.distance = this.zoom.minDistance + this.zoom.amplitude * this.zoom.value
|
||||
|
||||
// Listen to mousewheel event
|
||||
document.addEventListener('mousewheel', (_event) =>
|
||||
{
|
||||
this.zoom.targetValue += _event.deltaY * 0.001
|
||||
this.zoom.targetValue = Math.min(Math.max(this.zoom.targetValue, 0), 1)
|
||||
}, { passive: true })
|
||||
|
||||
// Touch
|
||||
this.zoom.touch = {}
|
||||
this.zoom.touch.startDistance = 0
|
||||
this.zoom.touch.startValue = 0
|
||||
|
||||
this.renderer.domElement.addEventListener('touchstart', (_event) =>
|
||||
{
|
||||
if(_event.touches.length === 2)
|
||||
{
|
||||
this.zoom.touch.startDistance = Math.hypot(_event.touches[0].clientX - _event.touches[1].clientX, _event.touches[0].clientX - _event.touches[1].clientX)
|
||||
this.zoom.touch.startValue = this.zoom.targetValue
|
||||
}
|
||||
})
|
||||
|
||||
this.renderer.domElement.addEventListener('touchmove', (_event) =>
|
||||
{
|
||||
if(_event.touches.length === 2)
|
||||
{
|
||||
_event.preventDefault()
|
||||
|
||||
const distance = Math.hypot(_event.touches[0].clientX - _event.touches[1].clientX, _event.touches[0].clientX - _event.touches[1].clientX)
|
||||
const ratio = distance / this.zoom.touch.startDistance
|
||||
|
||||
this.zoom.targetValue = this.zoom.touch.startValue - (ratio - 1)
|
||||
this.zoom.targetValue = Math.min(Math.max(this.zoom.targetValue, 0), 1)
|
||||
}
|
||||
})
|
||||
|
||||
// Time tick event
|
||||
this.time.on('tick', () =>
|
||||
{
|
||||
this.zoom.value += (this.zoom.targetValue - this.zoom.value) * this.zoom.easing
|
||||
this.zoom.distance = this.zoom.minDistance + this.zoom.amplitude * this.zoom.value
|
||||
})
|
||||
}
|
||||
|
||||
setPan()
|
||||
{
|
||||
// Set up
|
||||
this.pan = {}
|
||||
this.pan.enabled = false
|
||||
this.pan.active = false
|
||||
this.pan.easing = 0.1
|
||||
this.pan.start = {}
|
||||
this.pan.start.x = 0
|
||||
this.pan.start.y = 0
|
||||
this.pan.value = {}
|
||||
this.pan.value.x = 0
|
||||
this.pan.value.y = 0
|
||||
this.pan.targetValue = {}
|
||||
this.pan.targetValue.x = this.pan.value.x
|
||||
this.pan.targetValue.y = this.pan.value.y
|
||||
this.pan.raycaster = new THREE.Raycaster()
|
||||
this.pan.mouse = new THREE.Vector2()
|
||||
this.pan.needsUpdate = false
|
||||
this.pan.hitMesh = new THREE.Mesh(
|
||||
new THREE.PlaneBufferGeometry(500, 500, 1, 1),
|
||||
new THREE.MeshBasicMaterial({ color: 0xff0000, wireframe: true, visible: false })
|
||||
)
|
||||
this.container.add(this.pan.hitMesh)
|
||||
|
||||
this.pan.reset = () =>
|
||||
{
|
||||
this.pan.targetValue.x = 0
|
||||
this.pan.targetValue.y = 0
|
||||
}
|
||||
|
||||
this.pan.enable = () =>
|
||||
{
|
||||
this.pan.enabled = true
|
||||
|
||||
// Update cursor
|
||||
this.renderer.domElement.classList.add('has-cursor-grab')
|
||||
}
|
||||
|
||||
this.pan.disable = () =>
|
||||
{
|
||||
this.pan.enabled = false
|
||||
|
||||
// Update cursor
|
||||
this.renderer.domElement.classList.remove('has-cursor-grab')
|
||||
}
|
||||
|
||||
this.pan.down = (_x, _y) =>
|
||||
{
|
||||
if(!this.pan.enabled)
|
||||
{
|
||||
return
|
||||
}
|
||||
|
||||
// Update cursor
|
||||
this.renderer.domElement.classList.add('has-cursor-grabbing')
|
||||
|
||||
// Activate
|
||||
this.pan.active = true
|
||||
|
||||
// Update mouse position
|
||||
this.pan.mouse.x = (_x / this.sizes.viewport.width) * 2 - 1
|
||||
this.pan.mouse.y = - (_y / this.sizes.viewport.height) * 2 + 1
|
||||
|
||||
// Get start position
|
||||
this.pan.raycaster.setFromCamera(this.pan.mouse, this.instance)
|
||||
|
||||
const intersects = this.pan.raycaster.intersectObjects([this.pan.hitMesh])
|
||||
|
||||
if(intersects.length)
|
||||
{
|
||||
this.pan.start.x = intersects[0].point.x
|
||||
this.pan.start.y = intersects[0].point.y
|
||||
}
|
||||
}
|
||||
|
||||
this.pan.move = (_x, _y) =>
|
||||
{
|
||||
if(!this.pan.enabled)
|
||||
{
|
||||
return
|
||||
}
|
||||
|
||||
if(!this.pan.active)
|
||||
{
|
||||
return
|
||||
}
|
||||
|
||||
this.pan.mouse.x = (_x / this.sizes.viewport.width) * 2 - 1
|
||||
this.pan.mouse.y = - (_y / this.sizes.viewport.height) * 2 + 1
|
||||
|
||||
this.pan.needsUpdate = true
|
||||
}
|
||||
|
||||
this.pan.up = () =>
|
||||
{
|
||||
// Deactivate
|
||||
this.pan.active = false
|
||||
|
||||
// Update cursor
|
||||
this.renderer.domElement.classList.remove('has-cursor-grabbing')
|
||||
}
|
||||
|
||||
// Mouse
|
||||
window.addEventListener('mousedown', (_event) =>
|
||||
{
|
||||
this.pan.down(_event.clientX, _event.clientY)
|
||||
})
|
||||
|
||||
window.addEventListener('mousemove', (_event) =>
|
||||
{
|
||||
this.pan.move(_event.clientX, _event.clientY)
|
||||
})
|
||||
|
||||
window.addEventListener('mouseup', () =>
|
||||
{
|
||||
this.pan.up()
|
||||
})
|
||||
|
||||
// Touch
|
||||
this.renderer.domElement.addEventListener('touchstart', (_event) =>
|
||||
{
|
||||
if(_event.touches.length === 1)
|
||||
{
|
||||
this.pan.down(_event.touches[0].clientX, _event.touches[0].clientY)
|
||||
}
|
||||
})
|
||||
|
||||
this.renderer.domElement.addEventListener('touchmove', (_event) =>
|
||||
{
|
||||
if(_event.touches.length === 1)
|
||||
{
|
||||
this.pan.move(_event.touches[0].clientX, _event.touches[0].clientY)
|
||||
}
|
||||
})
|
||||
|
||||
this.renderer.domElement.addEventListener('touchend', () =>
|
||||
{
|
||||
this.pan.up()
|
||||
})
|
||||
|
||||
// Time tick event
|
||||
this.time.on('tick', () =>
|
||||
{
|
||||
// If active
|
||||
if(this.pan.active && this.pan.needsUpdate)
|
||||
{
|
||||
// Update target value
|
||||
this.pan.raycaster.setFromCamera(this.pan.mouse, this.instance)
|
||||
|
||||
const intersects = this.pan.raycaster.intersectObjects([this.pan.hitMesh])
|
||||
|
||||
if(intersects.length)
|
||||
{
|
||||
this.pan.targetValue.x = - (intersects[0].point.x - this.pan.start.x)
|
||||
this.pan.targetValue.y = - (intersects[0].point.y - this.pan.start.y)
|
||||
}
|
||||
|
||||
// Update needsUpdate
|
||||
this.pan.needsUpdate = false
|
||||
}
|
||||
|
||||
// Update value and apply easing
|
||||
this.pan.value.x += (this.pan.targetValue.x - this.pan.value.x) * this.pan.easing
|
||||
this.pan.value.y += (this.pan.targetValue.y - this.pan.value.y) * this.pan.easing
|
||||
})
|
||||
}
|
||||
|
||||
setOrbitControls()
|
||||
{
|
||||
// Set up
|
||||
this.orbitControls = new OrbitControls(this.instance, this.renderer.domElement)
|
||||
this.orbitControls.enabled = false
|
||||
this.orbitControls.enableKeys = false
|
||||
this.orbitControls.zoomSpeed = 0.5
|
||||
|
||||
// Debug
|
||||
if(this.debug)
|
||||
{
|
||||
this.debugFolder.add(this.orbitControls, 'enabled').name('orbitControlsEnabled')
|
||||
}
|
||||
}
|
||||
}
|
129
src/javascript/Geometries/AreaFenceBufferGeometry.js
Normal file
@ -0,0 +1,129 @@
|
||||
import * as THREE from 'three'
|
||||
|
||||
// AreaFenceBufferGeometry
|
||||
class AreaFenceBufferGeometry
|
||||
{
|
||||
constructor(_width, _height, _depth,)
|
||||
{
|
||||
// Parameters
|
||||
this.parameters = {
|
||||
width: _width,
|
||||
height: _height,
|
||||
depth: _depth
|
||||
}
|
||||
|
||||
// Set up
|
||||
this.type = 'AreaFloorBufferGeometry'
|
||||
|
||||
// buffers
|
||||
const length = 8
|
||||
|
||||
const vertices = new Float32Array(length * 3)
|
||||
const uvs = new Uint32Array(length * 2)
|
||||
const indices = new Uint32Array(length * 6)
|
||||
|
||||
// Vertices
|
||||
vertices[0 * 3 + 0] = _width * 0.5
|
||||
vertices[0 * 3 + 1] = _height * 0.5
|
||||
vertices[0 * 3 + 2] = 0
|
||||
|
||||
vertices[1 * 3 + 0] = _width * 0.5
|
||||
vertices[1 * 3 + 1] = - _height * 0.5
|
||||
vertices[1 * 3 + 2] = 0
|
||||
|
||||
vertices[2 * 3 + 0] = - _width * 0.5
|
||||
vertices[2 * 3 + 1] = - _height * 0.5
|
||||
vertices[2 * 3 + 2] = 0
|
||||
|
||||
vertices[3 * 3 + 0] = - _width * 0.5
|
||||
vertices[3 * 3 + 1] = _height * 0.5
|
||||
vertices[3 * 3 + 2] = 0
|
||||
|
||||
vertices[4 * 3 + 0] = _width * 0.5
|
||||
vertices[4 * 3 + 1] = _height * 0.5
|
||||
vertices[4 * 3 + 2] = _depth
|
||||
|
||||
vertices[5 * 3 + 0] = _width * 0.5
|
||||
vertices[5 * 3 + 1] = - _height * 0.5
|
||||
vertices[5 * 3 + 2] = _depth
|
||||
|
||||
vertices[6 * 3 + 0] = - _width * 0.5
|
||||
vertices[6 * 3 + 1] = - _height * 0.5
|
||||
vertices[6 * 3 + 2] = _depth
|
||||
|
||||
vertices[7 * 3 + 0] = - _width * 0.5
|
||||
vertices[7 * 3 + 1] = _height * 0.5
|
||||
vertices[7 * 3 + 2] = _depth
|
||||
|
||||
// Uvs
|
||||
uvs[0 * 2 + 0] = 0
|
||||
uvs[0 * 2 + 1] = 0
|
||||
|
||||
uvs[1 * 2 + 0] = 1 / 3
|
||||
uvs[1 * 2 + 1] = 0
|
||||
|
||||
uvs[2 * 2 + 0] = 1 / 3 * 2
|
||||
uvs[2 * 2 + 1] = 0
|
||||
|
||||
uvs[3 * 2 + 0] = 1
|
||||
uvs[3 * 2 + 1] = 0
|
||||
|
||||
uvs[4 * 2 + 0] = 0
|
||||
uvs[4 * 2 + 1] = 1
|
||||
|
||||
uvs[5 * 2 + 0] = 1 / 3
|
||||
uvs[5 * 2 + 1] = 1
|
||||
|
||||
uvs[6 * 2 + 0] = 1 / 3 * 2
|
||||
uvs[6 * 2 + 1] = 1
|
||||
|
||||
uvs[7 * 2 + 0] = 1
|
||||
uvs[7 * 2 + 1] = 1
|
||||
|
||||
// Index
|
||||
indices[0 * 3 + 0] = 0
|
||||
indices[0 * 3 + 1] = 4
|
||||
indices[0 * 3 + 2] = 1
|
||||
|
||||
indices[1 * 3 + 0] = 5
|
||||
indices[1 * 3 + 1] = 1
|
||||
indices[1 * 3 + 2] = 4
|
||||
|
||||
indices[2 * 3 + 0] = 1
|
||||
indices[2 * 3 + 1] = 5
|
||||
indices[2 * 3 + 2] = 2
|
||||
|
||||
indices[3 * 3 + 0] = 6
|
||||
indices[3 * 3 + 1] = 2
|
||||
indices[3 * 3 + 2] = 5
|
||||
|
||||
indices[4 * 3 + 0] = 2
|
||||
indices[4 * 3 + 1] = 6
|
||||
indices[4 * 3 + 2] = 3
|
||||
|
||||
indices[5 * 3 + 0] = 7
|
||||
indices[5 * 3 + 1] = 3
|
||||
indices[5 * 3 + 2] = 6
|
||||
|
||||
indices[6 * 3 + 0] = 3
|
||||
indices[6 * 3 + 1] = 7
|
||||
indices[6 * 3 + 2] = 0
|
||||
|
||||
indices[7 * 3 + 0] = 4
|
||||
indices[7 * 3 + 1] = 0
|
||||
indices[7 * 3 + 2] = 7
|
||||
|
||||
const geometry = new THREE.BufferGeometry()
|
||||
|
||||
// Set indices
|
||||
geometry.setIndex(new THREE.BufferAttribute(indices, 1, false))
|
||||
|
||||
// Set attributes
|
||||
geometry.setAttribute('position', new THREE.Float32BufferAttribute(vertices, 3))
|
||||
geometry.setAttribute('uv', new THREE.Float32BufferAttribute(uvs, 2))
|
||||
|
||||
return geometry
|
||||
}
|
||||
}
|
||||
|
||||
export default AreaFenceBufferGeometry
|
107
src/javascript/Geometries/AreaFloorBorderBufferGeometry.js
Normal file
@ -0,0 +1,107 @@
|
||||
import * as THREE from 'three'
|
||||
|
||||
class AreaFloorBorderBufferGeometry
|
||||
{
|
||||
constructor(_width, _height, _thickness)
|
||||
{
|
||||
// Parameters
|
||||
this.parameters = {
|
||||
width: _width,
|
||||
height: _height,
|
||||
thickness: _thickness
|
||||
}
|
||||
|
||||
// Set up
|
||||
this.type = 'AreaFloorBufferGeometry'
|
||||
|
||||
// buffers
|
||||
const length = 8
|
||||
|
||||
const vertices = new Float32Array(length * 3)
|
||||
const indices = new Uint32Array(length * 6)
|
||||
|
||||
const outerWidth = _width
|
||||
const outerHeight = _height
|
||||
|
||||
const innerWidth = outerWidth - _thickness
|
||||
const innerHeight = outerHeight - _thickness
|
||||
|
||||
// Vertices
|
||||
vertices[0 * 3 + 0] = innerWidth * 0.5
|
||||
vertices[0 * 3 + 1] = innerHeight * 0.5
|
||||
vertices[0 * 3 + 2] = 0
|
||||
|
||||
vertices[1 * 3 + 0] = innerWidth * 0.5
|
||||
vertices[1 * 3 + 1] = - innerHeight * 0.5
|
||||
vertices[1 * 3 + 2] = 0
|
||||
|
||||
vertices[2 * 3 + 0] = - innerWidth * 0.5
|
||||
vertices[2 * 3 + 1] = - innerHeight * 0.5
|
||||
vertices[2 * 3 + 2] = 0
|
||||
|
||||
vertices[3 * 3 + 0] = - innerWidth * 0.5
|
||||
vertices[3 * 3 + 1] = innerHeight * 0.5
|
||||
vertices[3 * 3 + 2] = 0
|
||||
|
||||
vertices[4 * 3 + 0] = outerWidth * 0.5
|
||||
vertices[4 * 3 + 1] = outerHeight * 0.5
|
||||
vertices[4 * 3 + 2] = 0
|
||||
|
||||
vertices[5 * 3 + 0] = outerWidth * 0.5
|
||||
vertices[5 * 3 + 1] = - outerHeight * 0.5
|
||||
vertices[5 * 3 + 2] = 0
|
||||
|
||||
vertices[6 * 3 + 0] = - outerWidth * 0.5
|
||||
vertices[6 * 3 + 1] = - outerHeight * 0.5
|
||||
vertices[6 * 3 + 2] = 0
|
||||
|
||||
vertices[7 * 3 + 0] = - outerWidth * 0.5
|
||||
vertices[7 * 3 + 1] = outerHeight * 0.5
|
||||
vertices[7 * 3 + 2] = 0
|
||||
|
||||
// Index
|
||||
indices[0 * 3 + 0] = 4
|
||||
indices[0 * 3 + 1] = 0
|
||||
indices[0 * 3 + 2] = 1
|
||||
|
||||
indices[1 * 3 + 0] = 1
|
||||
indices[1 * 3 + 1] = 5
|
||||
indices[1 * 3 + 2] = 4
|
||||
|
||||
indices[2 * 3 + 0] = 5
|
||||
indices[2 * 3 + 1] = 1
|
||||
indices[2 * 3 + 2] = 2
|
||||
|
||||
indices[3 * 3 + 0] = 2
|
||||
indices[3 * 3 + 1] = 6
|
||||
indices[3 * 3 + 2] = 5
|
||||
|
||||
indices[4 * 3 + 0] = 6
|
||||
indices[4 * 3 + 1] = 2
|
||||
indices[4 * 3 + 2] = 3
|
||||
|
||||
indices[5 * 3 + 0] = 3
|
||||
indices[5 * 3 + 1] = 7
|
||||
indices[5 * 3 + 2] = 6
|
||||
|
||||
indices[6 * 3 + 0] = 7
|
||||
indices[6 * 3 + 1] = 3
|
||||
indices[6 * 3 + 2] = 0
|
||||
|
||||
indices[7 * 3 + 0] = 0
|
||||
indices[7 * 3 + 1] = 4
|
||||
indices[7 * 3 + 2] = 7
|
||||
|
||||
const geometry = new THREE.BufferGeometry()
|
||||
|
||||
// Set indices
|
||||
geometry.setIndex(new THREE.BufferAttribute(indices, 1, false))
|
||||
|
||||
// Set attributes
|
||||
geometry.setAttribute('position', new THREE.Float32BufferAttribute(vertices, 3))
|
||||
|
||||
return geometry
|
||||
}
|
||||
}
|
||||
|
||||
export default AreaFloorBorderBufferGeometry
|
26
src/javascript/Materials/AreaFence.js
Normal file
@ -0,0 +1,26 @@
|
||||
import * as THREE from 'three'
|
||||
|
||||
import shaderFragment from '../../shaders/areaFence/fragment.glsl'
|
||||
import shaderVertex from '../../shaders/areaFence/vertex.glsl'
|
||||
|
||||
export default function()
|
||||
{
|
||||
const uniforms = {
|
||||
uTime: { value: null },
|
||||
uBorderAlpha: { value: null },
|
||||
uStrikeAlpha: { value: null }
|
||||
}
|
||||
|
||||
const material = new THREE.ShaderMaterial({
|
||||
wireframe: false,
|
||||
transparent: true,
|
||||
side: THREE.DoubleSide,
|
||||
depthTest: true,
|
||||
depthWrite: false,
|
||||
uniforms,
|
||||
vertexShader: shaderVertex,
|
||||
fragmentShader: shaderFragment
|
||||
})
|
||||
|
||||
return material
|
||||
}
|
26
src/javascript/Materials/AreaFloorBorder.js
Normal file
@ -0,0 +1,26 @@
|
||||
import * as THREE from 'three'
|
||||
|
||||
import shaderFragment from '../../shaders/areaFloorBorder/fragment.glsl'
|
||||
import shaderVertex from '../../shaders/areaFloorBorder/vertex.glsl'
|
||||
|
||||
export default function()
|
||||
{
|
||||
const uniforms = {
|
||||
uColor: { value: null },
|
||||
uAlpha: { value: null },
|
||||
uLoadProgress: { value: null },
|
||||
uProgress: { value: null }
|
||||
}
|
||||
|
||||
const material = new THREE.ShaderMaterial({
|
||||
wireframe: false,
|
||||
transparent: true,
|
||||
depthTest: true,
|
||||
depthWrite: false,
|
||||
uniforms,
|
||||
vertexShader: shaderVertex,
|
||||
fragmentShader: shaderFragment
|
||||
})
|
||||
|
||||
return material
|
||||
}
|
21
src/javascript/Materials/Floor.js
Normal file
@ -0,0 +1,21 @@
|
||||
import * as THREE from 'three'
|
||||
|
||||
import shaderFragment from '../../shaders/floor/fragment.glsl'
|
||||
import shaderVertex from '../../shaders/floor/vertex.glsl'
|
||||
|
||||
export default function()
|
||||
{
|
||||
const uniforms = {
|
||||
tBackground: { value: null }
|
||||
}
|
||||
|
||||
const material = new THREE.ShaderMaterial({
|
||||
wireframe: false,
|
||||
transparent: false,
|
||||
uniforms,
|
||||
vertexShader: shaderVertex,
|
||||
fragmentShader: shaderFragment
|
||||
})
|
||||
|
||||
return material
|
||||
}
|
23
src/javascript/Materials/FloorShadow.js
Normal file
@ -0,0 +1,23 @@
|
||||
import * as THREE from 'three'
|
||||
|
||||
import shaderFragment from '../../shaders/floorShadow/fragment.glsl'
|
||||
import shaderVertex from '../../shaders/floorShadow/vertex.glsl'
|
||||
|
||||
export default function()
|
||||
{
|
||||
const uniforms = {
|
||||
tShadow: { value: null },
|
||||
uShadowColor: { value: null },
|
||||
uAlpha: { value: null }
|
||||
}
|
||||
|
||||
const material = new THREE.ShaderMaterial({
|
||||
wireframe: false,
|
||||
transparent: true,
|
||||
uniforms,
|
||||
vertexShader: shaderVertex,
|
||||
fragmentShader: shaderFragment
|
||||
})
|
||||
|
||||
return material
|
||||
}
|
48
src/javascript/Materials/Matcap.js
Normal file
@ -0,0 +1,48 @@
|
||||
import * as THREE from 'three'
|
||||
|
||||
import shaderFragment from '../../shaders/matcap/fragment.glsl'
|
||||
import shaderVertex from '../../shaders/matcap/vertex.glsl'
|
||||
|
||||
export default function()
|
||||
{
|
||||
const uniforms = {
|
||||
...THREE.UniformsLib.common,
|
||||
...THREE.UniformsLib.bumpmap,
|
||||
...THREE.UniformsLib.normalmap,
|
||||
...THREE.UniformsLib.displacementmap,
|
||||
...THREE.UniformsLib.fog,
|
||||
matcap: { value: null },
|
||||
uRevealProgress: { value: null },
|
||||
uIndirectDistanceAmplitude: { value: null },
|
||||
uIndirectDistanceStrength: { value: null },
|
||||
uIndirectDistancePower: { value: null },
|
||||
uIndirectAngleStrength: { value: null },
|
||||
uIndirectAngleOffset: { value: null },
|
||||
uIndirectAnglePower: { value: null },
|
||||
uIndirectColor: { value: null }
|
||||
}
|
||||
|
||||
const extensions = {
|
||||
derivatives: false,
|
||||
fragDepth: false,
|
||||
drawBuffers: false,
|
||||
shaderTextureLOD: false
|
||||
}
|
||||
|
||||
const defines = {
|
||||
MATCAP: ''
|
||||
}
|
||||
|
||||
const material = new THREE.ShaderMaterial({
|
||||
wireframe: false,
|
||||
transparent: false,
|
||||
uniforms,
|
||||
extensions,
|
||||
defines,
|
||||
lights: false,
|
||||
vertexShader: shaderVertex,
|
||||
fragmentShader: shaderFragment
|
||||
})
|
||||
|
||||
return material
|
||||
}
|
23
src/javascript/Materials/ProjectBoard.js
Normal file
@ -0,0 +1,23 @@
|
||||
import * as THREE from 'three'
|
||||
|
||||
import shaderFragment from '../../shaders/projectBoard/fragment.glsl'
|
||||
import shaderVertex from '../../shaders/projectBoard/vertex.glsl'
|
||||
|
||||
export default function()
|
||||
{
|
||||
const uniforms = {
|
||||
uTexture: { value: null },
|
||||
uTextureAlpha: { value: null },
|
||||
uColor: { value: null }
|
||||
}
|
||||
|
||||
const material = new THREE.ShaderMaterial({
|
||||
wireframe: false,
|
||||
transparent: false,
|
||||
uniforms,
|
||||
vertexShader: shaderVertex,
|
||||
fragmentShader: shaderFragment
|
||||
})
|
||||
|
||||
return material
|
||||
}
|
23
src/javascript/Materials/Shadow.js
Normal file
@ -0,0 +1,23 @@
|
||||
import * as THREE from 'three'
|
||||
|
||||
import shaderFragment from '../../shaders/shadow/fragment.glsl'
|
||||
import shaderVertex from '../../shaders/shadow/vertex.glsl'
|
||||
|
||||
export default function()
|
||||
{
|
||||
const uniforms = {
|
||||
uColor: { value: null },
|
||||
uAlpha: { value: null },
|
||||
uFadeRadius: { value: null }
|
||||
}
|
||||
|
||||
const material = new THREE.ShaderMaterial({
|
||||
wireframe: false,
|
||||
transparent: true,
|
||||
uniforms,
|
||||
vertexShader: shaderVertex,
|
||||
fragmentShader: shaderFragment
|
||||
})
|
||||
|
||||
return material
|
||||
}
|
13
src/javascript/Passes/Blur.js
Normal file
@ -0,0 +1,13 @@
|
||||
import shaderFragment from '../../shaders/blur/fragment.glsl'
|
||||
import shaderVertex from '../../shaders/blur/vertex.glsl'
|
||||
|
||||
export default {
|
||||
uniforms:
|
||||
{
|
||||
tDiffuse: { type: 't', value: null },
|
||||
uResolution: { type: 'v2', value: null },
|
||||
uStrength: { type: 'v2', value: null }
|
||||
},
|
||||
vertexShader: shaderVertex,
|
||||
fragmentShader: shaderFragment
|
||||
}
|
15
src/javascript/Passes/Glows.js
Normal file
@ -0,0 +1,15 @@
|
||||
import shaderFragment from '../../shaders/glows/fragment.glsl'
|
||||
import shaderVertex from '../../shaders/glows/vertex.glsl'
|
||||
|
||||
export default {
|
||||
uniforms:
|
||||
{
|
||||
tDiffuse: { type: 't', value: null },
|
||||
uPosition: { type: 'v2', value: null },
|
||||
uRadius: { type: 'f', value: null },
|
||||
uColor: { type: 'v3', value: null },
|
||||
uAlpha: { type: 'f', value: null }
|
||||
},
|
||||
vertexShader: shaderVertex,
|
||||
fragmentShader: shaderFragment
|
||||
}
|
456
src/javascript/Resources.js
Normal file
@ -0,0 +1,456 @@
|
||||
import * as THREE from 'three'
|
||||
|
||||
import Loader from './Utils/Loader.js'
|
||||
import EventEmitter from './Utils/EventEmitter.js'
|
||||
|
||||
// Matcaps
|
||||
import matcapBeigeSource from '../models/matcaps/beige.png'
|
||||
import matcapBlackSource from '../models/matcaps/black.png'
|
||||
import matcapOrangeSource from '../models/matcaps/orange.png'
|
||||
import matcapRedSource from '../models/matcaps/red.png'
|
||||
import matcapWhiteSource from '../models/matcaps/white.png'
|
||||
import matcapGreenSource from '../models/matcaps/green.png'
|
||||
import matcapBrownSource from '../models/matcaps/brown.png'
|
||||
import matcapGraySource from '../models/matcaps/gray.png'
|
||||
import matcapEmeraldGreenSource from '../models/matcaps/emeraldGreen.png'
|
||||
import matcapPurpleSource from '../models/matcaps/purple.png'
|
||||
import matcapBlueSource from '../models/matcaps/blue.png'
|
||||
import matcapYellowSource from '../models/matcaps/yellow.png'
|
||||
import matcapMetalSource from '../models/matcaps/metal.png'
|
||||
// import matcapGoldSource from '../models/matcaps/gold.png'
|
||||
|
||||
// Intro
|
||||
import introStaticBaseSource from '../models/intro/static/base.glb'
|
||||
import introStaticCollisionSource from '../models/intro/static/collision.glb'
|
||||
import introStaticFloorShadowSource from '../models/intro/static/floorShadow.png'
|
||||
|
||||
import introInstructionsLabelsSource from '../models/intro/instructions/labels.glb'
|
||||
import introInstructionsArrowsSource from '../models/intro/instructions/arrows.png'
|
||||
import introInstructionsControlsSource from '../models/intro/instructions/controls.png'
|
||||
import introInstructionsOtherSource from '../models/intro/instructions/other.png'
|
||||
|
||||
import introArrowKeyBaseSource from '../models/intro/arrowKey/base.glb'
|
||||
import introArrowKeyCollisionSource from '../models/intro/arrowKey/collision.glb'
|
||||
|
||||
import introBBaseSource from '../models/intro/b/base.glb'
|
||||
import introBCollisionSource from '../models/intro/b/collision.glb'
|
||||
|
||||
import introRBaseSource from '../models/intro/r/base.glb'
|
||||
import introRCollisionSource from '../models/intro/r/collision.glb'
|
||||
|
||||
import introUBaseSource from '../models/intro/u/base.glb'
|
||||
import introUCollisionSource from '../models/intro/u/collision.glb'
|
||||
|
||||
import introNBaseSource from '../models/intro/n/base.glb'
|
||||
import introNCollisionSource from '../models/intro/n/collision.glb'
|
||||
|
||||
import introOBaseSource from '../models/intro/o/base.glb'
|
||||
import introOCollisionSource from '../models/intro/o/collision.glb'
|
||||
|
||||
import introSBaseSource from '../models/intro/s/base.glb'
|
||||
import introSCollisionSource from '../models/intro/s/collision.glb'
|
||||
|
||||
import introIBaseSource from '../models/intro/i/base.glb'
|
||||
import introICollisionSource from '../models/intro/i/collision.glb'
|
||||
|
||||
import introMBaseSource from '../models/intro/m/base.glb'
|
||||
import introMCollisionSource from '../models/intro/m/collision.glb'
|
||||
|
||||
import introCreativeBaseSource from '../models/intro/creative/base.glb'
|
||||
import introCreativeCollisionSource from '../models/intro/creative/collision.glb'
|
||||
|
||||
import introDevBaseSource from '../models/intro/dev/base.glb'
|
||||
import introDevCollisionSource from '../models/intro/dev/collision.glb'
|
||||
|
||||
// Crossroads
|
||||
import crossroadsStaticFloorShadowSource from '../models/crossroads/static/floorShadow.png'
|
||||
import crossroadsStaticBaseSource from '../models/crossroads/static/base.glb'
|
||||
import crossroadsStaticCollisionSource from '../models/crossroads/static/collision.glb'
|
||||
|
||||
// Car default
|
||||
import carDefaultChassisSource from '../models/car/default/chassis.glb'
|
||||
import carDefaultWheelSource from '../models/car/default/wheel.glb'
|
||||
import carDefaultBackLightsBrakeSource from '../models/car/default/backLightsBrake.glb'
|
||||
import carDefaultBackLightsReverseSource from '../models/car/default/backLightsReverse.glb'
|
||||
import carDefaultAntenaSource from '../models/car/default/antena.glb'
|
||||
// import carDefaultBunnyEarLeftSource from '../models/car/default/bunnyEarLeft.glb'
|
||||
// import carDefaultBunnyEarRightSource from '../models/car/default/bunnyEarRight.glb'
|
||||
|
||||
// Car cyber truck
|
||||
import carCyberTruckChassisSource from '../models/car/cyberTruck/chassis.glb'
|
||||
import carCyberTruckWheelSource from '../models/car/cyberTruck/wheel.glb'
|
||||
import carCyberTruckBackLightsBrakeSource from '../models/car/cyberTruck/backLightsBrake.glb'
|
||||
import carCyberTruckBackLightsReverseSource from '../models/car/cyberTruck/backLightsReverse.glb'
|
||||
import carCyberTruckAntenaSource from '../models/car/cyberTruck/antena.glb'
|
||||
|
||||
// Projects
|
||||
import projectsBoardStructureSource from '../models/projects/board/structure.glb'
|
||||
import projectsBoardCollisionSource from '../models/projects/board/collision.glb'
|
||||
import projectsBoardStructureFloorShadowSource from '../models/projects/board/floorShadow.png'
|
||||
import projectsBoardPlaneSource from '../models/projects/board/plane.glb'
|
||||
|
||||
import projectsDistinctionsAwwwardsBaseSource from '../models/projects/distinctions/awwwards/base.glb'
|
||||
import projectsDistinctionsAwwwardsCollisionSource from '../models/projects/distinctions/awwwards/collision.glb'
|
||||
import projectsDistinctionsFWABaseSource from '../models/projects/distinctions/fwa/base.glb'
|
||||
import projectsDistinctionsFWACollisionSource from '../models/projects/distinctions/fwa/collision.glb'
|
||||
import projectsDistinctionsCSSDABaseSource from '../models/projects/distinctions/cssda/base.glb'
|
||||
import projectsDistinctionsCSSDACollisionSource from '../models/projects/distinctions/cssda/collision.glb'
|
||||
|
||||
import projectsThreejsJourneyFloorSource from '../models/projects/threejsJourney/floorTexture.png'
|
||||
import projectsMadboxFloorSource from '../models/projects/madbox/floorTexture.png'
|
||||
import projectsScoutFloorSource from '../models/projects/scout/floorTexture.png'
|
||||
import projectsChartogneFloorSource from '../models/projects/chartogne/floorTexture.png'
|
||||
import projectsZenlyFloorSource from '../models/projects/zenly/floorTexture.png'
|
||||
import projectsCitrixRedbullFloorSource from '../models/projects/citrixRedbull/floorTexture.png'
|
||||
import projectsPriorHoldingsFloorSource from '../models/projects/priorHoldings/floorTexture.png'
|
||||
import projectsOranoFloorSource from '../models/projects/orano/floorTexture.png'
|
||||
// import projectsGleecChatFloorSource from '../models/projects/gleecChat/floorTexture.png'
|
||||
import projectsKepplerFloorSource from '../models/projects/keppler/floorTexture.png'
|
||||
|
||||
// Information
|
||||
import informationStaticBaseSource from '../models/information/static/base.glb'
|
||||
import informationStaticCollisionSource from '../models/information/static/collision.glb'
|
||||
import informationStaticFloorShadowSource from '../models/information/static/floorShadow.png'
|
||||
|
||||
import informationBaguetteBaseSource from '../models/information/baguette/base.glb'
|
||||
import informationBaguetteCollisionSource from '../models/information/baguette/collision.glb'
|
||||
|
||||
import informationContactTwitterLabelSource from '../models/information/static/contactTwitterLabel.png'
|
||||
import informationContactGithubLabelSource from '../models/information/static/contactGithubLabel.png'
|
||||
import informationContactLinkedinLabelSource from '../models/information/static/contactLinkedinLabel.png'
|
||||
import informationContactMailLabelSource from '../models/information/static/contactMailLabel.png'
|
||||
|
||||
import informationActivitiesSource from '../models/information/static/activities.png'
|
||||
|
||||
// Playground
|
||||
import playgroundStaticFloorShadowSource from '../models/playground/static/floorShadow.png'
|
||||
import playgroundStaticBaseSource from '../models/playground/static/base.glb'
|
||||
import playgroundStaticCollisionSource from '../models/playground/static/collision.glb'
|
||||
|
||||
// Brick
|
||||
import brickBaseSource from '../models/brick/base.glb'
|
||||
import brickCollisionSource from '../models/brick/collision.glb'
|
||||
|
||||
// Horn
|
||||
import hornBaseSource from '../models/horn/base.glb'
|
||||
import hornCollisionSource from '../models/horn/collision.glb'
|
||||
|
||||
// // Distinction A
|
||||
// import distinctionAStaticFloorShadowSource from '../models/distinctionA/static/floorShadow.png'
|
||||
// import distinctionAStaticBaseSource from '../models/distinctionA/static/base.glb'
|
||||
// import distinctionAStaticCollisionSource from '../models/distinctionA/static/collision.glb'
|
||||
|
||||
// // Distinction B
|
||||
// import distinctionBStaticFloorShadowSource from '../models/distinctionB/static/floorShadow.png'
|
||||
// import distinctionBStaticBaseSource from '../models/distinctionB/static/base.glb'
|
||||
// import distinctionBStaticCollisionSource from '../models/distinctionB/static/collision.glb'
|
||||
|
||||
// // Distinction C
|
||||
// import distinctionCStaticFloorShadowSource from '../models/distinctionC/static/floorShadow.png'
|
||||
// import distinctionCStaticBaseSource from '../models/distinctionC/static/base.glb'
|
||||
// import distinctionCStaticCollisionSource from '../models/distinctionC/static/collision.glb'
|
||||
|
||||
// // Cone
|
||||
// import coneBaseSource from '../models/cone/base.glb'
|
||||
// import coneCollisionSource from '../models/cone/collision.glb'
|
||||
|
||||
// // Awwwards trophy
|
||||
// import awwwardsTrophyBaseSource from '../models/awwwardsTrophy/base.glb'
|
||||
// import awwwardsTrophyCollisionSource from '../models/awwwardsTrophy/collision.glb'
|
||||
|
||||
// Awwwards trophy
|
||||
import webbyTrophyBaseSource from '../models/webbyTrophy/base.glb'
|
||||
import webbyTrophyCollisionSource from '../models/webbyTrophy/collision.glb'
|
||||
|
||||
// Lemon
|
||||
import lemonBaseSource from '../models/lemon/base.glb'
|
||||
import lemonCollisionSource from '../models/lemon/collision.glb'
|
||||
|
||||
// Bowling ball
|
||||
import bowlingBallBaseSource from '../models/bowlingBall/base.glb'
|
||||
import bowlingBallCollisionSource from '../models/bowlingBall/collision.glb'
|
||||
|
||||
// Bowling pin
|
||||
import bowlingPinBaseSource from '../models/bowlingPin/base.glb'
|
||||
import bowlingPinCollisionSource from '../models/bowlingPin/collision.glb'
|
||||
|
||||
// Area
|
||||
import areaKeyEnterSource from '../models/area/keyEnter.png'
|
||||
import areaEnterSource from '../models/area/enter.png'
|
||||
import areaOpenSource from '../models/area/open.png'
|
||||
import areaResetSource from '../models/area/reset.png'
|
||||
import areaQuestionMarkSource from '../models/area/questionMark.png'
|
||||
|
||||
// Tiles
|
||||
import tilesABaseSource from '../models/tiles/a/base.glb'
|
||||
import tilesACollisionSource from '../models/tiles/a/collision.glb'
|
||||
|
||||
import tilesBBaseSource from '../models/tiles/b/base.glb'
|
||||
import tilesBCollisionSource from '../models/tiles/b/collision.glb'
|
||||
|
||||
import tilesCBaseSource from '../models/tiles/c/base.glb'
|
||||
import tilesCCollisionSource from '../models/tiles/c/collision.glb'
|
||||
|
||||
import tilesDBaseSource from '../models/tiles/d/base.glb'
|
||||
import tilesDCollisionSource from '../models/tiles/d/collision.glb'
|
||||
|
||||
import tilesEBaseSource from '../models/tiles/e/base.glb'
|
||||
import tilesECollisionSource from '../models/tiles/e/collision.glb'
|
||||
|
||||
// Konami
|
||||
import konamiLabelSource from '../models/konami/label.png'
|
||||
import konamiLabelTouchSource from '../models/konami/label-touch.png'
|
||||
|
||||
// Wigs
|
||||
import wig1Source from '../models/wigs/wig1.glb'
|
||||
import wig2Source from '../models/wigs/wig2.glb'
|
||||
import wig3Source from '../models/wigs/wig3.glb'
|
||||
import wig4Source from '../models/wigs/wig4.glb'
|
||||
|
||||
// // Egg
|
||||
// import eggBaseSource from '../models/egg/base.glb'
|
||||
// import eggCollisionSource from '../models/egg/collision.glb'
|
||||
|
||||
export default class Resources extends EventEmitter
|
||||
{
|
||||
constructor()
|
||||
{
|
||||
super()
|
||||
|
||||
this.loader = new Loader()
|
||||
this.items = {}
|
||||
|
||||
this.loader.load([
|
||||
// Matcaps
|
||||
{ name: 'matcapBeige', source: matcapBeigeSource, type: 'texture' },
|
||||
{ name: 'matcapBlack', source: matcapBlackSource, type: 'texture' },
|
||||
{ name: 'matcapOrange', source: matcapOrangeSource, type: 'texture' },
|
||||
{ name: 'matcapRed', source: matcapRedSource, type: 'texture' },
|
||||
{ name: 'matcapWhite', source: matcapWhiteSource, type: 'texture' },
|
||||
{ name: 'matcapGreen', source: matcapGreenSource, type: 'texture' },
|
||||
{ name: 'matcapBrown', source: matcapBrownSource, type: 'texture' },
|
||||
{ name: 'matcapGray', source: matcapGraySource, type: 'texture' },
|
||||
{ name: 'matcapEmeraldGreen', source: matcapEmeraldGreenSource, type: 'texture' },
|
||||
{ name: 'matcapPurple', source: matcapPurpleSource, type: 'texture' },
|
||||
{ name: 'matcapBlue', source: matcapBlueSource, type: 'texture' },
|
||||
{ name: 'matcapYellow', source: matcapYellowSource, type: 'texture' },
|
||||
{ name: 'matcapMetal', source: matcapMetalSource, type: 'texture' },
|
||||
// { name: 'matcapGold', source: matcapGoldSource, type: 'texture' },
|
||||
|
||||
// Intro
|
||||
{ name: 'introStaticBase', source: introStaticBaseSource },
|
||||
{ name: 'introStaticCollision', source: introStaticCollisionSource },
|
||||
{ name: 'introStaticFloorShadow', source: introStaticFloorShadowSource, type: 'texture' },
|
||||
|
||||
{ name: 'introInstructionsLabels', source: introInstructionsLabelsSource },
|
||||
{ name: 'introInstructionsArrows', source: introInstructionsArrowsSource, type: 'texture' },
|
||||
{ name: 'introInstructionsControls', source: introInstructionsControlsSource, type: 'texture' },
|
||||
{ name: 'introInstructionsOther', source: introInstructionsOtherSource, type: 'texture' },
|
||||
|
||||
{ name: 'introArrowKeyBase', source: introArrowKeyBaseSource },
|
||||
{ name: 'introArrowKeyCollision', source: introArrowKeyCollisionSource },
|
||||
|
||||
{ name: 'introBBase', source: introBBaseSource },
|
||||
{ name: 'introBCollision', source: introBCollisionSource },
|
||||
|
||||
{ name: 'introRBase', source: introRBaseSource },
|
||||
{ name: 'introRCollision', source: introRCollisionSource },
|
||||
|
||||
{ name: 'introUBase', source: introUBaseSource },
|
||||
{ name: 'introUCollision', source: introUCollisionSource },
|
||||
|
||||
{ name: 'introNBase', source: introNBaseSource },
|
||||
{ name: 'introNCollision', source: introNCollisionSource },
|
||||
|
||||
{ name: 'introOBase', source: introOBaseSource },
|
||||
{ name: 'introOCollision', source: introOCollisionSource },
|
||||
|
||||
{ name: 'introSBase', source: introSBaseSource },
|
||||
{ name: 'introSCollision', source: introSCollisionSource },
|
||||
|
||||
{ name: 'introIBase', source: introIBaseSource },
|
||||
{ name: 'introICollision', source: introICollisionSource },
|
||||
|
||||
{ name: 'introMBase', source: introMBaseSource },
|
||||
{ name: 'introMCollision', source: introMCollisionSource },
|
||||
|
||||
{ name: 'introCreativeBase', source: introCreativeBaseSource },
|
||||
{ name: 'introCreativeCollision', source: introCreativeCollisionSource },
|
||||
|
||||
{ name: 'introDevBase', source: introDevBaseSource },
|
||||
{ name: 'introDevCollision', source: introDevCollisionSource },
|
||||
|
||||
// Intro
|
||||
{ name: 'crossroadsStaticBase', source: crossroadsStaticBaseSource },
|
||||
{ name: 'crossroadsStaticCollision', source: crossroadsStaticCollisionSource },
|
||||
{ name: 'crossroadsStaticFloorShadow', source: crossroadsStaticFloorShadowSource, type: 'texture' },
|
||||
|
||||
// Car default
|
||||
{ name: 'carDefaultChassis', source: carDefaultChassisSource },
|
||||
{ name: 'carDefaultWheel', source: carDefaultWheelSource },
|
||||
{ name: 'carDefaultBackLightsBrake', source: carDefaultBackLightsBrakeSource },
|
||||
{ name: 'carDefaultBackLightsReverse', source: carDefaultBackLightsReverseSource },
|
||||
{ name: 'carDefaultAntena', source: carDefaultAntenaSource },
|
||||
// { name: 'carDefaultBunnyEarLeft', source: carDefaultBunnyEarLeftSource },
|
||||
// { name: 'carDefaultBunnyEarRight', source: carDefaultBunnyEarRightSource },
|
||||
|
||||
// Car default
|
||||
{ name: 'carCyberTruckChassis', source: carCyberTruckChassisSource },
|
||||
{ name: 'carCyberTruckWheel', source: carCyberTruckWheelSource },
|
||||
{ name: 'carCyberTruckBackLightsBrake', source: carCyberTruckBackLightsBrakeSource },
|
||||
{ name: 'carCyberTruckBackLightsReverse', source: carCyberTruckBackLightsReverseSource },
|
||||
{ name: 'carCyberTruckAntena', source: carCyberTruckAntenaSource },
|
||||
|
||||
// Project
|
||||
{ name: 'projectsBoardStructure', source: projectsBoardStructureSource },
|
||||
{ name: 'projectsBoardCollision', source: projectsBoardCollisionSource },
|
||||
{ name: 'projectsBoardStructureFloorShadow', source: projectsBoardStructureFloorShadowSource, type: 'texture' },
|
||||
{ name: 'projectsBoardPlane', source: projectsBoardPlaneSource },
|
||||
|
||||
{ name: 'projectsDistinctionsAwwwardsBase', source: projectsDistinctionsAwwwardsBaseSource },
|
||||
{ name: 'projectsDistinctionsAwwwardsCollision', source: projectsDistinctionsAwwwardsCollisionSource },
|
||||
{ name: 'projectsDistinctionsFWABase', source: projectsDistinctionsFWABaseSource },
|
||||
{ name: 'projectsDistinctionsFWACollision', source: projectsDistinctionsFWACollisionSource },
|
||||
{ name: 'projectsDistinctionsCSSDABase', source: projectsDistinctionsCSSDABaseSource },
|
||||
{ name: 'projectsDistinctionsCSSDACollision', source: projectsDistinctionsCSSDACollisionSource },
|
||||
|
||||
{ name: 'projectsThreejsJourneyFloor', source: projectsThreejsJourneyFloorSource, type: 'texture' },
|
||||
{ name: 'projectsMadboxFloor', source: projectsMadboxFloorSource, type: 'texture' },
|
||||
{ name: 'projectsScoutFloor', source: projectsScoutFloorSource, type: 'texture' },
|
||||
{ name: 'projectsChartogneFloor', source: projectsChartogneFloorSource, type: 'texture' },
|
||||
{ name: 'projectsZenlyFloor', source: projectsZenlyFloorSource, type: 'texture' },
|
||||
{ name: 'projectsCitrixRedbullFloor', source: projectsCitrixRedbullFloorSource, type: 'texture' },
|
||||
{ name: 'projectsPriorHoldingsFloor', source: projectsPriorHoldingsFloorSource, type: 'texture' },
|
||||
{ name: 'projectsOranoFloor', source: projectsOranoFloorSource, type: 'texture' },
|
||||
// { name: 'projectsGleecChatFloor', source: projectsGleecChatFloorSource, type: 'texture' },
|
||||
{ name: 'projectsKepplerFloor', source: projectsKepplerFloorSource, type: 'texture' },
|
||||
|
||||
// Information
|
||||
{ name: 'informationStaticBase', source: informationStaticBaseSource },
|
||||
{ name: 'informationStaticCollision', source: informationStaticCollisionSource },
|
||||
{ name: 'informationStaticFloorShadow', source: informationStaticFloorShadowSource, type: 'texture' },
|
||||
|
||||
{ name: 'informationBaguetteBase', source: informationBaguetteBaseSource },
|
||||
{ name: 'informationBaguetteCollision', source: informationBaguetteCollisionSource },
|
||||
|
||||
{ name: 'informationContactTwitterLabel', source: informationContactTwitterLabelSource, type: 'texture' },
|
||||
{ name: 'informationContactGithubLabel', source: informationContactGithubLabelSource, type: 'texture' },
|
||||
{ name: 'informationContactLinkedinLabel', source: informationContactLinkedinLabelSource, type: 'texture' },
|
||||
{ name: 'informationContactMailLabel', source: informationContactMailLabelSource, type: 'texture' },
|
||||
|
||||
{ name: 'informationActivities', source: informationActivitiesSource, type: 'texture' },
|
||||
|
||||
// Playground
|
||||
{ name: 'playgroundStaticBase', source: playgroundStaticBaseSource },
|
||||
{ name: 'playgroundStaticCollision', source: playgroundStaticCollisionSource },
|
||||
{ name: 'playgroundStaticFloorShadow', source: playgroundStaticFloorShadowSource, type: 'texture' },
|
||||
|
||||
// Brick
|
||||
{ name: 'brickBase', source: brickBaseSource },
|
||||
{ name: 'brickCollision', source: brickCollisionSource },
|
||||
|
||||
// Horn
|
||||
{ name: 'hornBase', source: hornBaseSource },
|
||||
{ name: 'hornCollision', source: hornCollisionSource },
|
||||
|
||||
// // Distinction A
|
||||
// { name: 'distinctionAStaticBase', source: distinctionAStaticBaseSource },
|
||||
// { name: 'distinctionAStaticCollision', source: distinctionAStaticCollisionSource },
|
||||
// { name: 'distinctionAStaticFloorShadow', source: distinctionAStaticFloorShadowSource, type: 'texture' },
|
||||
|
||||
// // Distinction B
|
||||
// { name: 'distinctionBStaticBase', source: distinctionBStaticBaseSource },
|
||||
// { name: 'distinctionBStaticCollision', source: distinctionBStaticCollisionSource },
|
||||
// { name: 'distinctionBStaticFloorShadow', source: distinctionBStaticFloorShadowSource, type: 'texture' },
|
||||
|
||||
// // Distinction C
|
||||
// { name: 'distinctionCStaticBase', source: distinctionCStaticBaseSource },
|
||||
// { name: 'distinctionCStaticCollision', source: distinctionCStaticCollisionSource },
|
||||
// { name: 'distinctionCStaticFloorShadow', source: distinctionCStaticFloorShadowSource, type: 'texture' },
|
||||
|
||||
// // Cone
|
||||
// { name: 'coneBase', source: coneBaseSource },
|
||||
// { name: 'coneCollision', source: coneCollisionSource },
|
||||
|
||||
// // Awwwards trophy
|
||||
// { name: 'awwwardsTrophyBase', source: awwwardsTrophyBaseSource },
|
||||
// { name: 'awwwardsTrophyCollision', source: awwwardsTrophyCollisionSource },
|
||||
|
||||
// Webby trophy
|
||||
{ name: 'webbyTrophyBase', source: webbyTrophyBaseSource },
|
||||
{ name: 'webbyTrophyCollision', source: webbyTrophyCollisionSource },
|
||||
|
||||
// Lemon
|
||||
{ name: 'lemonBase', source: lemonBaseSource },
|
||||
{ name: 'lemonCollision', source: lemonCollisionSource },
|
||||
|
||||
// Bownling ball
|
||||
{ name: 'bowlingBallBase', source: bowlingBallBaseSource },
|
||||
{ name: 'bowlingBallCollision', source: bowlingBallCollisionSource },
|
||||
|
||||
// Bownling ball
|
||||
{ name: 'bowlingPinBase', source: bowlingPinBaseSource },
|
||||
{ name: 'bowlingPinCollision', source: bowlingPinCollisionSource },
|
||||
|
||||
// Areas
|
||||
{ name: 'areaKeyEnter', source: areaKeyEnterSource, type: 'texture' },
|
||||
{ name: 'areaEnter', source: areaEnterSource, type: 'texture' },
|
||||
{ name: 'areaOpen', source: areaOpenSource, type: 'texture' },
|
||||
{ name: 'areaReset', source: areaResetSource, type: 'texture' },
|
||||
{ name: 'areaQuestionMark', source: areaQuestionMarkSource, type: 'texture' },
|
||||
|
||||
// Tiles
|
||||
{ name: 'tilesABase', source: tilesABaseSource },
|
||||
{ name: 'tilesACollision', source: tilesACollisionSource },
|
||||
|
||||
{ name: 'tilesBBase', source: tilesBBaseSource },
|
||||
{ name: 'tilesBCollision', source: tilesBCollisionSource },
|
||||
|
||||
{ name: 'tilesCBase', source: tilesCBaseSource },
|
||||
{ name: 'tilesCCollision', source: tilesCCollisionSource },
|
||||
|
||||
{ name: 'tilesDBase', source: tilesDBaseSource },
|
||||
{ name: 'tilesDCollision', source: tilesDCollisionSource },
|
||||
|
||||
{ name: 'tilesEBase', source: tilesEBaseSource },
|
||||
{ name: 'tilesECollision', source: tilesECollisionSource },
|
||||
|
||||
// Konami
|
||||
{ name: 'konamiLabel', source: konamiLabelSource, type: 'texture' },
|
||||
{ name: 'konamiLabelTouch', source: konamiLabelTouchSource, type: 'texture' },
|
||||
|
||||
// Wigs
|
||||
{ name: 'wig1', source: wig1Source },
|
||||
{ name: 'wig2', source: wig2Source },
|
||||
{ name: 'wig3', source: wig3Source },
|
||||
{ name: 'wig4', source: wig4Source },
|
||||
|
||||
// // Egg
|
||||
// { name: 'eggBase', source: eggBaseSource },
|
||||
// { name: 'eggCollision', source: eggCollisionSource },
|
||||
])
|
||||
|
||||
this.loader.on('fileEnd', (_resource, _data) =>
|
||||
{
|
||||
this.items[_resource.name] = _data
|
||||
|
||||
// Texture
|
||||
if(_resource.type === 'texture')
|
||||
{
|
||||
const texture = new THREE.Texture(_data)
|
||||
texture.needsUpdate = true
|
||||
|
||||
this.items[`${_resource.name}Texture`] = texture
|
||||
}
|
||||
|
||||
// Trigger progress
|
||||
this.trigger('progress', [this.loader.loaded / this.loader.toLoad])
|
||||
})
|
||||
|
||||
this.loader.on('end', () =>
|
||||
{
|
||||
// Trigger ready
|
||||
this.trigger('ready')
|
||||
})
|
||||
}
|
||||
}
|
219
src/javascript/ThreejsJourney.js
Normal file
@ -0,0 +1,219 @@
|
||||
import { TweenLite } from 'gsap/TweenLite'
|
||||
|
||||
export default class ThreejsJourney
|
||||
{
|
||||
constructor(_options)
|
||||
{
|
||||
// Options
|
||||
this.config = _options.config
|
||||
this.time = _options.time
|
||||
this.world = _options.world
|
||||
|
||||
// Setup
|
||||
this.$container = document.querySelector('.js-threejs-journey')
|
||||
this.$messages = [...this.$container.querySelectorAll('.js-message')]
|
||||
this.$yes = this.$container.querySelector('.js-yes')
|
||||
this.$no = this.$container.querySelector('.js-no')
|
||||
this.step = 0
|
||||
this.maxStep = this.$messages.length - 1
|
||||
this.seenCount = window.localStorage.getItem('threejsJourneySeenCount') || 0
|
||||
this.seenCount = parseInt(this.seenCount)
|
||||
this.shown = false
|
||||
this.traveledDistance = 0
|
||||
this.minTraveledDistance = (this.config.debug ? 5 : 75) * (this.seenCount + 1)
|
||||
this.prevent = !!window.localStorage.getItem('threejsJourneyPrevent')
|
||||
|
||||
if(this.config.debug)
|
||||
this.start()
|
||||
|
||||
if(this.prevent)
|
||||
return
|
||||
|
||||
this.setYesNo()
|
||||
this.setLog()
|
||||
|
||||
this.time.on('tick', () =>
|
||||
{
|
||||
if(this.world.physics)
|
||||
{
|
||||
this.traveledDistance += this.world.physics.car.forwardSpeed
|
||||
|
||||
if(!this.config.touch && !this.shown && this.traveledDistance > this.minTraveledDistance)
|
||||
{
|
||||
this.start()
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
setYesNo()
|
||||
{
|
||||
// Clicks
|
||||
this.$yes.addEventListener('click', () =>
|
||||
{
|
||||
TweenLite.delayedCall(2, () =>
|
||||
{
|
||||
this.hide()
|
||||
})
|
||||
window.localStorage.setItem('threejsJourneyPrevent', 1)
|
||||
})
|
||||
|
||||
this.$no.addEventListener('click', () =>
|
||||
{
|
||||
this.next()
|
||||
|
||||
TweenLite.delayedCall(5, () =>
|
||||
{
|
||||
this.hide()
|
||||
})
|
||||
})
|
||||
|
||||
// Hovers
|
||||
this.$yes.addEventListener('mouseenter', () =>
|
||||
{
|
||||
this.$container.classList.remove('is-hover-none')
|
||||
this.$container.classList.remove('is-hover-no')
|
||||
this.$container.classList.add('is-hover-yes')
|
||||
})
|
||||
|
||||
this.$no.addEventListener('mouseenter', () =>
|
||||
{
|
||||
this.$container.classList.remove('is-hover-none')
|
||||
this.$container.classList.add('is-hover-no')
|
||||
this.$container.classList.remove('is-hover-yes')
|
||||
})
|
||||
|
||||
this.$yes.addEventListener('mouseleave', () =>
|
||||
{
|
||||
this.$container.classList.add('is-hover-none')
|
||||
this.$container.classList.remove('is-hover-no')
|
||||
this.$container.classList.remove('is-hover-yes')
|
||||
})
|
||||
|
||||
this.$no.addEventListener('mouseleave', () =>
|
||||
{
|
||||
this.$container.classList.add('is-hover-none')
|
||||
this.$container.classList.remove('is-hover-no')
|
||||
this.$container.classList.remove('is-hover-yes')
|
||||
})
|
||||
}
|
||||
|
||||
setLog()
|
||||
{
|
||||
// console.log(
|
||||
// `%c
|
||||
// ▶
|
||||
// ▶▶▶▶
|
||||
// ▶▶▶▶▶▶▶
|
||||
// ▶▶▶▶▶▶▶▶▶▶
|
||||
// ▶▶▶▶▶▶▶▶ ▶
|
||||
// ▶▶▶▶ ▶▶▶▶▶▶▶▶
|
||||
// ▶ ▶▶▶▶▶▶▶▶▶▶▶▶▶▶▶▶
|
||||
// ▶▶▶▶▶▶▶▶▶▶▶▶▶▶▶▶▶▶▶▶▶▶
|
||||
// ▶▶▶▶▶▶▶▶▶▶▶▶▶▶▶▶
|
||||
// ▶▶ ▶▶▶▶▶▶▶▶▶▶ ▶ ▶▶▶
|
||||
// ▶▶▶▶▶▶ ▶ ▶▶▶▶▶ ▶▶▶▶▶▶
|
||||
// ▶▶▶▶▶▶▶▶▶▶▶ ▶▶▶▶▶▶▶▶ ▶▶▶▶▶▶▶▶▶
|
||||
// ▶▶▶▶▶▶▶▶▶▶▶▶▶ ▶▶▶▶▶▶▶▶▶▶ ▶▶▶▶▶▶▶
|
||||
// ▶▶▶▶▶▶▶▶▶▶▶▶▶ ▶▶▶▶▶▶▶▶▶▶ ▶▶▶▶
|
||||
// ▶▶▶▶▶▶▶▶▶▶▶▶▶ ▶▶▶▶▶▶▶▶▶▶ ▶
|
||||
// ▶▶▶▶▶▶▶▶▶▶▶▶ ▶▶▶▶▶▶▶▶▶▶
|
||||
// ▶▶▶▶▶▶▶▶ ▶▶▶▶▶▶▶
|
||||
// ▶▶▶▶ ▶▶▶▶ ▶▶▶
|
||||
// ▶▶▶▶▶▶▶ ▶
|
||||
// ▶▶▶▶▶▶▶▶▶▶
|
||||
// ▶▶▶▶▶▶▶
|
||||
// ▶▶
|
||||
// `,
|
||||
// 'color: #705df2;'
|
||||
// )
|
||||
console.log('%cWhat are you doing here?! you sneaky developer...', 'color: #32ffce');
|
||||
console.log('%cDo you want to learn how this portfolio has been made?', 'color: #32ffce');
|
||||
console.log('%cCheckout Three.js Journey 👉 https://threejs-journey.com?c=p2', 'color: #32ffce');
|
||||
console.log('%c— Bruno', 'color: #777777');
|
||||
}
|
||||
|
||||
hide()
|
||||
{
|
||||
for(const _$message of this.$messages)
|
||||
{
|
||||
_$message.classList.remove('is-visible')
|
||||
}
|
||||
|
||||
TweenLite.delayedCall(0.5, () =>
|
||||
{
|
||||
this.$container.classList.remove('is-active')
|
||||
})
|
||||
}
|
||||
|
||||
start()
|
||||
{
|
||||
this.$container.classList.add('is-active')
|
||||
|
||||
window.requestAnimationFrame(() =>
|
||||
{
|
||||
this.next()
|
||||
|
||||
TweenLite.delayedCall(4, () =>
|
||||
{
|
||||
this.next()
|
||||
})
|
||||
TweenLite.delayedCall(7, () =>
|
||||
{
|
||||
this.next()
|
||||
})
|
||||
})
|
||||
|
||||
this.shown = true
|
||||
|
||||
window.localStorage.setItem('threejsJourneySeenCount', this.seenCount + 1)
|
||||
}
|
||||
|
||||
updateMessages()
|
||||
{
|
||||
let i = 0
|
||||
|
||||
// Visibility
|
||||
for(const _$message of this.$messages)
|
||||
{
|
||||
if(i < this.step)
|
||||
_$message.classList.add('is-visible')
|
||||
|
||||
i++
|
||||
}
|
||||
|
||||
// Position
|
||||
this.$messages.reverse()
|
||||
|
||||
let height = 0
|
||||
i = this.maxStep
|
||||
for(const _$message of this.$messages)
|
||||
{
|
||||
const messageHeight = _$message.offsetHeight
|
||||
if(i < this.step)
|
||||
{
|
||||
_$message.style.transform = `translateY(${- height}px)`
|
||||
height += messageHeight + 20
|
||||
}
|
||||
else
|
||||
{
|
||||
_$message.style.transform = `translateY(${messageHeight}px)`
|
||||
}
|
||||
|
||||
i--
|
||||
}
|
||||
|
||||
|
||||
this.$messages.reverse()
|
||||
}
|
||||
|
||||
next()
|
||||
{
|
||||
if(this.step > this.maxStep)
|
||||
return
|
||||
|
||||
this.step++
|
||||
|
||||
this.updateMessages()
|
||||
}
|
||||
}
|
220
src/javascript/Utils/EventEmitter.js
Normal file
@ -0,0 +1,220 @@
|
||||
export default class
|
||||
{
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
constructor()
|
||||
{
|
||||
this.callbacks = {}
|
||||
this.callbacks.base = {}
|
||||
}
|
||||
|
||||
/**
|
||||
* On
|
||||
*/
|
||||
on(_names, callback)
|
||||
{
|
||||
const that = this
|
||||
|
||||
// Errors
|
||||
if(typeof _names === 'undefined' || _names === '')
|
||||
{
|
||||
console.warn('wrong names')
|
||||
return false
|
||||
}
|
||||
|
||||
if(typeof callback === 'undefined')
|
||||
{
|
||||
console.warn('wrong callback')
|
||||
return false
|
||||
}
|
||||
|
||||
// Resolve names
|
||||
const names = this.resolveNames(_names)
|
||||
|
||||
// Each name
|
||||
names.forEach(function(_name)
|
||||
{
|
||||
// Resolve name
|
||||
const name = that.resolveName(_name)
|
||||
|
||||
// Create namespace if not exist
|
||||
if(!(that.callbacks[ name.namespace ] instanceof Object))
|
||||
that.callbacks[ name.namespace ] = {}
|
||||
|
||||
// Create callback if not exist
|
||||
if(!(that.callbacks[ name.namespace ][ name.value ] instanceof Array))
|
||||
that.callbacks[ name.namespace ][ name.value ] = []
|
||||
|
||||
// Add callback
|
||||
that.callbacks[ name.namespace ][ name.value ].push(callback)
|
||||
})
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* Off
|
||||
*/
|
||||
off(_names)
|
||||
{
|
||||
const that = this
|
||||
|
||||
// Errors
|
||||
if(typeof _names === 'undefined' || _names === '')
|
||||
{
|
||||
console.warn('wrong name')
|
||||
return false
|
||||
}
|
||||
|
||||
// Resolve names
|
||||
const names = this.resolveNames(_names)
|
||||
|
||||
// Each name
|
||||
names.forEach(function(_name)
|
||||
{
|
||||
// Resolve name
|
||||
const name = that.resolveName(_name)
|
||||
|
||||
// Remove namespace
|
||||
if(name.namespace !== 'base' && name.value === '')
|
||||
{
|
||||
delete that.callbacks[ name.namespace ]
|
||||
}
|
||||
|
||||
// Remove specific callback in namespace
|
||||
else
|
||||
{
|
||||
// Default
|
||||
if(name.namespace === 'base')
|
||||
{
|
||||
// Try to remove from each namespace
|
||||
for(const namespace in that.callbacks)
|
||||
{
|
||||
if(that.callbacks[ namespace ] instanceof Object && that.callbacks[ namespace ][ name.value ] instanceof Array)
|
||||
{
|
||||
delete that.callbacks[ namespace ][ name.value ]
|
||||
|
||||
// Remove namespace if empty
|
||||
if(Object.keys(that.callbacks[ namespace ]).length === 0)
|
||||
delete that.callbacks[ namespace ]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Specified namespace
|
||||
else if(that.callbacks[ name.namespace ] instanceof Object && that.callbacks[ name.namespace ][ name.value ] instanceof Array)
|
||||
{
|
||||
delete that.callbacks[ name.namespace ][ name.value ]
|
||||
|
||||
// Remove namespace if empty
|
||||
if(Object.keys(that.callbacks[ name.namespace ]).length === 0)
|
||||
delete that.callbacks[ name.namespace ]
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* Trigger
|
||||
*/
|
||||
trigger(_name, _args)
|
||||
{
|
||||
// Errors
|
||||
if(typeof _name === 'undefined' || _name === '')
|
||||
{
|
||||
console.warn('wrong name')
|
||||
return false
|
||||
}
|
||||
|
||||
const that = this
|
||||
let finalResult = null
|
||||
let result = null
|
||||
|
||||
// Default args
|
||||
const args = !(_args instanceof Array) ? [] : _args
|
||||
|
||||
// Resolve names (should on have one event)
|
||||
let name = this.resolveNames(_name)
|
||||
|
||||
// Resolve name
|
||||
name = this.resolveName(name[ 0 ])
|
||||
|
||||
// Default namespace
|
||||
if(name.namespace === 'base')
|
||||
{
|
||||
// Try to find callback in each namespace
|
||||
for(const namespace in that.callbacks)
|
||||
{
|
||||
if(that.callbacks[ namespace ] instanceof Object && that.callbacks[ namespace ][ name.value ] instanceof Array)
|
||||
{
|
||||
that.callbacks[ namespace ][ name.value ].forEach(function(callback)
|
||||
{
|
||||
result = callback.apply(that, args)
|
||||
|
||||
if(typeof finalResult === 'undefined')
|
||||
{
|
||||
finalResult = result
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Specified namespace
|
||||
else if(this.callbacks[ name.namespace ] instanceof Object)
|
||||
{
|
||||
if(name.value === '')
|
||||
{
|
||||
console.warn('wrong name')
|
||||
return this
|
||||
}
|
||||
|
||||
that.callbacks[ name.namespace ][ name.value ].forEach(function(callback)
|
||||
{
|
||||
result = callback.apply(that, args)
|
||||
|
||||
if(typeof finalResult === 'undefined')
|
||||
finalResult = result
|
||||
})
|
||||
}
|
||||
|
||||
return finalResult
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve names
|
||||
*/
|
||||
resolveNames(_names)
|
||||
{
|
||||
let names = _names
|
||||
names = names.replace(/[^a-zA-Z0-9 ,/.]/g, '')
|
||||
names = names.replace(/[,/]+/g, ' ')
|
||||
names = names.split(' ')
|
||||
|
||||
return names
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve name
|
||||
*/
|
||||
resolveName(name)
|
||||
{
|
||||
const newName = {}
|
||||
const parts = name.split('.')
|
||||
|
||||
newName.original = name
|
||||
newName.value = parts[ 0 ]
|
||||
newName.namespace = 'base' // Base namespace
|
||||
|
||||
// Specified namespace
|
||||
if(parts.length > 1 && parts[ 1 ] !== '')
|
||||
{
|
||||
newName.namespace = parts[ 1 ]
|
||||
}
|
||||
|
||||
return newName
|
||||
}
|
||||
}
|
144
src/javascript/Utils/Loader.js
Normal file
@ -0,0 +1,144 @@
|
||||
import EventEmitter from './EventEmitter.js'
|
||||
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js'
|
||||
import { FBXLoader } from 'three/examples/jsm/loaders/FBXLoader.js'
|
||||
import { DRACOLoader } from 'three/examples/jsm/loaders/DRACOLoader.js'
|
||||
|
||||
export default class Resources extends EventEmitter
|
||||
{
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
constructor()
|
||||
{
|
||||
super()
|
||||
|
||||
this.setLoaders()
|
||||
|
||||
this.toLoad = 0
|
||||
this.loaded = 0
|
||||
this.items = {}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set loaders
|
||||
*/
|
||||
setLoaders()
|
||||
{
|
||||
this.loaders = []
|
||||
|
||||
// Images
|
||||
this.loaders.push({
|
||||
extensions: ['jpg', 'png'],
|
||||
action: (_resource) =>
|
||||
{
|
||||
const image = new Image()
|
||||
|
||||
image.addEventListener('load', () =>
|
||||
{
|
||||
this.fileLoadEnd(_resource, image)
|
||||
})
|
||||
|
||||
image.addEventListener('error', () =>
|
||||
{
|
||||
this.fileLoadEnd(_resource, image)
|
||||
})
|
||||
|
||||
image.src = _resource.source
|
||||
}
|
||||
})
|
||||
|
||||
// Draco
|
||||
const dracoLoader = new DRACOLoader()
|
||||
dracoLoader.setDecoderPath('draco/')
|
||||
dracoLoader.setDecoderConfig({ type: 'js' })
|
||||
|
||||
this.loaders.push({
|
||||
extensions: ['drc'],
|
||||
action: (_resource) =>
|
||||
{
|
||||
dracoLoader.load(_resource.source, (_data) =>
|
||||
{
|
||||
this.fileLoadEnd(_resource, _data)
|
||||
|
||||
DRACOLoader.releaseDecoderModule()
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
// GLTF
|
||||
const gltfLoader = new GLTFLoader()
|
||||
gltfLoader.setDRACOLoader(dracoLoader)
|
||||
|
||||
this.loaders.push({
|
||||
extensions: ['glb', 'gltf'],
|
||||
action: (_resource) =>
|
||||
{
|
||||
gltfLoader.load(_resource.source, (_data) =>
|
||||
{
|
||||
this.fileLoadEnd(_resource, _data)
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
// FBX
|
||||
const fbxLoader = new FBXLoader()
|
||||
|
||||
this.loaders.push({
|
||||
extensions: ['fbx'],
|
||||
action: (_resource) =>
|
||||
{
|
||||
fbxLoader.load(_resource.source, (_data) =>
|
||||
{
|
||||
this.fileLoadEnd(_resource, _data)
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Load
|
||||
*/
|
||||
load(_resources = [])
|
||||
{
|
||||
for(const _resource of _resources)
|
||||
{
|
||||
this.toLoad++
|
||||
const extensionMatch = _resource.source.match(/\.([a-z]+)$/)
|
||||
|
||||
if(typeof extensionMatch[1] !== 'undefined')
|
||||
{
|
||||
const extension = extensionMatch[1]
|
||||
const loader = this.loaders.find((_loader) => _loader.extensions.find((_extension) => _extension === extension))
|
||||
|
||||
if(loader)
|
||||
{
|
||||
loader.action(_resource)
|
||||
}
|
||||
else
|
||||
{
|
||||
console.warn(`Cannot found loader for ${_resource}`)
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
console.warn(`Cannot found extension of ${_resource}`)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* File load end
|
||||
*/
|
||||
fileLoadEnd(_resource, _data)
|
||||
{
|
||||
this.loaded++
|
||||
this.items[_resource.name] = _data
|
||||
|
||||
this.trigger('fileEnd', [_resource, _data])
|
||||
|
||||
if(this.loaded === this.toLoad)
|
||||
{
|
||||
this.trigger('end')
|
||||
}
|
||||
}
|
||||
}
|
44
src/javascript/Utils/Sizes.js
Normal file
@ -0,0 +1,44 @@
|
||||
import EventEmitter from './EventEmitter.js'
|
||||
|
||||
export default class Sizes extends EventEmitter
|
||||
{
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
constructor()
|
||||
{
|
||||
super()
|
||||
|
||||
// Viewport size
|
||||
this.viewport = {}
|
||||
this.$sizeViewport = document.createElement('div')
|
||||
this.$sizeViewport.style.width = '100vw'
|
||||
this.$sizeViewport.style.height = '100vh'
|
||||
this.$sizeViewport.style.position = 'absolute'
|
||||
this.$sizeViewport.style.top = 0
|
||||
this.$sizeViewport.style.left = 0
|
||||
this.$sizeViewport.style.pointerEvents = 'none'
|
||||
|
||||
// Resize event
|
||||
this.resize = this.resize.bind(this)
|
||||
window.addEventListener('resize', this.resize)
|
||||
|
||||
this.resize()
|
||||
}
|
||||
|
||||
/**
|
||||
* Resize
|
||||
*/
|
||||
resize()
|
||||
{
|
||||
document.body.appendChild(this.$sizeViewport)
|
||||
this.viewport.width = this.$sizeViewport.offsetWidth
|
||||
this.viewport.height = this.$sizeViewport.offsetHeight
|
||||
document.body.removeChild(this.$sizeViewport)
|
||||
|
||||
this.width = window.innerWidth
|
||||
this.height = window.innerHeight
|
||||
|
||||
this.trigger('resize')
|
||||
}
|
||||
}
|
49
src/javascript/Utils/Time.js
Normal file
@ -0,0 +1,49 @@
|
||||
import EventEmitter from './EventEmitter.js'
|
||||
|
||||
export default class Time extends EventEmitter
|
||||
{
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
constructor()
|
||||
{
|
||||
super()
|
||||
|
||||
this.start = Date.now()
|
||||
this.current = this.start
|
||||
this.elapsed = 0
|
||||
this.delta = 16
|
||||
|
||||
this.tick = this.tick.bind(this)
|
||||
this.tick()
|
||||
}
|
||||
|
||||
/**
|
||||
* Tick
|
||||
*/
|
||||
tick()
|
||||
{
|
||||
this.ticker = window.requestAnimationFrame(this.tick)
|
||||
|
||||
const current = Date.now()
|
||||
|
||||
this.delta = current - this.current
|
||||
this.elapsed = current - this.start
|
||||
this.current = current
|
||||
|
||||
if(this.delta > 60)
|
||||
{
|
||||
this.delta = 60
|
||||
}
|
||||
|
||||
this.trigger('tick')
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop
|
||||
*/
|
||||
stop()
|
||||
{
|
||||
window.cancelAnimationFrame(this.ticker)
|
||||
}
|
||||
}
|
309
src/javascript/World/Area.js
Normal file
@ -0,0 +1,309 @@
|
||||
import * as THREE from 'three'
|
||||
import { TweenLite } from 'gsap/TweenLite'
|
||||
import { Back } from 'gsap/EasePack'
|
||||
|
||||
import EventEmitter from '../Utils/EventEmitter.js'
|
||||
import AreaFloorBorderBufferGeometry from '../Geometries/AreaFloorBorderBufferGeometry.js'
|
||||
import AreaFenceBufferGeometry from '../Geometries/AreaFenceBufferGeometry.js'
|
||||
import AreaFenceMaterial from '../Materials/AreaFence.js'
|
||||
import AreaFloorBordereMaterial from '../Materials/AreaFloorBorder.js'
|
||||
|
||||
export default class Area extends EventEmitter
|
||||
{
|
||||
constructor(_options)
|
||||
{
|
||||
super()
|
||||
|
||||
// Options
|
||||
this.config = _options.config
|
||||
this.renderer = _options.renderer
|
||||
this.resources = _options.resources
|
||||
this.car = _options.car
|
||||
this.sounds = _options.sounds
|
||||
this.time = _options.time
|
||||
this.position = _options.position
|
||||
this.halfExtents = _options.halfExtents
|
||||
this.hasKey = _options.hasKey
|
||||
this.testCar = _options.testCar
|
||||
this.active = _options.active
|
||||
|
||||
// Set up
|
||||
this.container = new THREE.Object3D()
|
||||
this.container.position.x = this.position.x
|
||||
this.container.position.y = this.position.y
|
||||
this.container.matrixAutoUpdate = false
|
||||
this.container.updateMatrix()
|
||||
|
||||
this.initialTestCar = this.testCar
|
||||
this.isIn = false
|
||||
|
||||
this.setFloorBorder()
|
||||
this.setFence()
|
||||
this.setInteractions()
|
||||
|
||||
if(this.hasKey)
|
||||
{
|
||||
this.setKey()
|
||||
}
|
||||
}
|
||||
|
||||
activate()
|
||||
{
|
||||
this.active = true
|
||||
|
||||
if(this.isIn)
|
||||
{
|
||||
this.in()
|
||||
}
|
||||
}
|
||||
|
||||
deactivate()
|
||||
{
|
||||
this.active = false
|
||||
|
||||
if(this.isIn)
|
||||
{
|
||||
this.out()
|
||||
}
|
||||
}
|
||||
|
||||
setFloorBorder()
|
||||
{
|
||||
this.floorBorder = {}
|
||||
|
||||
this.floorBorder.geometry = new AreaFloorBorderBufferGeometry(this.halfExtents.x * 2, this.halfExtents.y * 2, 0.25)
|
||||
this.floorBorder.material = new AreaFloorBordereMaterial()
|
||||
this.floorBorder.material.uniforms.uColor.value = new THREE.Color(0xffffff)
|
||||
this.floorBorder.material.uniforms.uAlpha.value = 0.5
|
||||
this.floorBorder.material.uniforms.uLoadProgress.value = 1
|
||||
this.floorBorder.material.uniforms.uProgress.value = 1
|
||||
this.floorBorder.mesh = new THREE.Mesh(this.floorBorder.geometry, this.floorBorder.material)
|
||||
this.floorBorder.mesh.matrixAutoUpdate = false
|
||||
|
||||
this.container.add(this.floorBorder.mesh)
|
||||
}
|
||||
|
||||
setFence()
|
||||
{
|
||||
// Set up
|
||||
this.fence = {}
|
||||
this.fence.depth = 0.5
|
||||
this.fence.offset = 0.5
|
||||
|
||||
// Geometry
|
||||
this.fence.geometry = new AreaFenceBufferGeometry(this.halfExtents.x * 2, this.halfExtents.y * 2, this.fence.depth)
|
||||
|
||||
// Material
|
||||
// this.fence.material = new THREE.MeshBasicMaterial({ color: 0xffffff, wireframe: true, transparent: true, opacity: 0.5 })
|
||||
this.fence.material = new AreaFenceMaterial()
|
||||
this.fence.material.uniforms.uBorderAlpha.value = 0.5
|
||||
this.fence.material.uniforms.uStrikeAlpha.value = 0.25
|
||||
|
||||
// Mesh
|
||||
this.fence.mesh = new THREE.Mesh(this.fence.geometry, this.fence.material)
|
||||
this.fence.mesh.position.z = - this.fence.depth
|
||||
this.container.add(this.fence.mesh)
|
||||
|
||||
// Time tick
|
||||
this.time.on('tick', () =>
|
||||
{
|
||||
this.fence.material.uniforms.uTime.value = this.time.elapsed
|
||||
})
|
||||
}
|
||||
|
||||
setKey()
|
||||
{
|
||||
this.key = {}
|
||||
this.key.hiddenZ = 1.5
|
||||
this.key.shownZ = 2.5
|
||||
|
||||
// Container
|
||||
this.key.container = new THREE.Object3D()
|
||||
this.key.container.position.z = this.key.hiddenZ
|
||||
this.container.add(this.key.container)
|
||||
|
||||
// Enter
|
||||
this.key.enter = {}
|
||||
this.key.enter.size = 1.4
|
||||
this.key.enter.geometry = new THREE.PlaneBufferGeometry(this.key.enter.size, this.key.enter.size / 4, 1, 1)
|
||||
|
||||
this.key.enter.texture = this.resources.items.areaEnterTexture
|
||||
this.key.enter.texture.magFilter = THREE.NearestFilter
|
||||
this.key.enter.texture.minFilter = THREE.LinearFilter
|
||||
|
||||
this.key.enter.material = new THREE.MeshBasicMaterial({ color: 0xffffff, alphaMap: this.key.enter.texture, transparent: true, opacity: 0, depthWrite: false })
|
||||
|
||||
this.key.enter.mesh = new THREE.Mesh(this.key.enter.geometry, this.key.enter.material)
|
||||
this.key.enter.mesh.rotation.x = Math.PI * 0.5
|
||||
this.key.enter.mesh.position.x = this.key.enter.size * 0.75
|
||||
this.key.enter.mesh.matrixAutoUpdate = false
|
||||
this.key.enter.mesh.updateMatrix()
|
||||
this.key.container.add(this.key.enter.mesh)
|
||||
|
||||
// Icon
|
||||
this.key.icon = {}
|
||||
this.key.icon.size = 0.75
|
||||
this.key.icon.geometry = new THREE.PlaneBufferGeometry(this.key.icon.size, this.key.icon.size, 1, 1)
|
||||
|
||||
this.key.icon.texture = this.resources.items.areaKeyEnterTexture
|
||||
this.key.icon.texture.magFilter = THREE.NearestFilter
|
||||
this.key.icon.texture.minFilter = THREE.LinearFilter
|
||||
|
||||
this.key.icon.material = new THREE.MeshBasicMaterial({ color: 0xffffff, alphaMap: this.key.icon.texture, transparent: true, opacity: 0, depthWrite: false })
|
||||
|
||||
this.key.icon.mesh = new THREE.Mesh(this.key.icon.geometry, this.key.icon.material)
|
||||
this.key.icon.mesh.rotation.x = Math.PI * 0.5
|
||||
this.key.icon.mesh.position.x = - this.key.enter.size * 0.15
|
||||
this.key.icon.mesh.matrixAutoUpdate = false
|
||||
this.key.icon.mesh.updateMatrix()
|
||||
this.key.container.add(this.key.icon.mesh)
|
||||
}
|
||||
|
||||
interact(_showKey = true)
|
||||
{
|
||||
// Not active
|
||||
if(!this.active)
|
||||
{
|
||||
return
|
||||
}
|
||||
|
||||
// Kill tweens
|
||||
TweenLite.killTweensOf(this.fence.mesh.position)
|
||||
TweenLite.killTweensOf(this.floorBorder.material.uniforms.uAlpha)
|
||||
TweenLite.killTweensOf(this.fence.material.uniforms.uBorderAlpha)
|
||||
|
||||
if(this.hasKey)
|
||||
{
|
||||
TweenLite.killTweensOf(this.key.container.position)
|
||||
TweenLite.killTweensOf(this.key.icon.material)
|
||||
TweenLite.killTweensOf(this.key.enter.material)
|
||||
}
|
||||
|
||||
// Animate
|
||||
TweenLite.to(this.fence.mesh.position, 0.05, { z: 0, onComplete: () =>
|
||||
{
|
||||
TweenLite.to(this.fence.mesh.position, 0.25, { z: 0.5, ease: Back.easeOut.config(2) })
|
||||
TweenLite.fromTo(this.floorBorder.material.uniforms.uAlpha, 1.5, { value: 1 }, { value: 0.5 })
|
||||
TweenLite.fromTo(this.fence.material.uniforms.uBorderAlpha, 1.5, { value: 1 }, { value: 0.5 })
|
||||
} })
|
||||
|
||||
if(this.hasKey && _showKey)
|
||||
{
|
||||
this.key.container.position.z = this.key.shownZ
|
||||
TweenLite.fromTo(this.key.icon.material, 1.5, { opacity: 1 }, { opacity: 0.5 })
|
||||
TweenLite.fromTo(this.key.enter.material, 1.5, { opacity: 1 }, { opacity: 0.5 })
|
||||
}
|
||||
|
||||
// Play sound
|
||||
this.sounds.play('uiArea')
|
||||
|
||||
this.trigger('interact')
|
||||
}
|
||||
|
||||
in(_showKey = true)
|
||||
{
|
||||
this.isIn = true
|
||||
|
||||
// Not active
|
||||
if(!this.active)
|
||||
{
|
||||
return
|
||||
}
|
||||
|
||||
// Fence
|
||||
TweenLite.killTweensOf(this.fence.mesh.position)
|
||||
TweenLite.to(this.fence.mesh.position, 0.35, { z: this.fence.offset, ease: Back.easeOut.config(3) })
|
||||
|
||||
// Key
|
||||
if(this.hasKey)
|
||||
{
|
||||
TweenLite.killTweensOf(this.key.container.position)
|
||||
TweenLite.killTweensOf(this.key.icon.material)
|
||||
TweenLite.killTweensOf(this.key.enter.material)
|
||||
|
||||
// Animate
|
||||
if(_showKey)
|
||||
{
|
||||
TweenLite.to(this.key.container.position, 0.35, { z: this.key.shownZ, ease: Back.easeOut.config(3), delay: 0.1 })
|
||||
TweenLite.to(this.key.icon.material, 0.35, { opacity: 0.5, ease: Back.easeOut.config(3), delay: 0.1 })
|
||||
TweenLite.to(this.key.enter.material, 0.35, { opacity: 0.5, ease: Back.easeOut.config(3), delay: 0.1 })
|
||||
}
|
||||
}
|
||||
|
||||
// Change cursor
|
||||
if(!this.config.touch)
|
||||
{
|
||||
this.renderer.domElement.classList.add('has-cursor-pointer')
|
||||
}
|
||||
|
||||
this.trigger('in')
|
||||
}
|
||||
|
||||
out()
|
||||
{
|
||||
this.isIn = false
|
||||
|
||||
// Fence
|
||||
TweenLite.killTweensOf(this.fence.mesh.position)
|
||||
TweenLite.to(this.fence.mesh.position, 0.35, { z: - this.fence.depth, ease: Back.easeIn.config(4) })
|
||||
|
||||
// Key
|
||||
if(this.hasKey)
|
||||
{
|
||||
TweenLite.killTweensOf(this.key.container.position)
|
||||
TweenLite.killTweensOf(this.key.icon.material)
|
||||
TweenLite.killTweensOf(this.key.enter.material)
|
||||
TweenLite.to(this.key.container.position, 0.35, { z: this.key.hiddenZ, ease: Back.easeIn.config(4), delay: 0.1 })
|
||||
TweenLite.to(this.key.icon.material, 0.35, { opacity: 0, ease: Back.easeIn.config(4), delay: 0.1 })
|
||||
TweenLite.to(this.key.enter.material, 0.35, { opacity: 0, ease: Back.easeIn.config(4), delay: 0.1 })
|
||||
}
|
||||
|
||||
// Change cursor
|
||||
if(!this.config.touch)
|
||||
{
|
||||
this.renderer.domElement.classList.remove('has-cursor-pointer')
|
||||
}
|
||||
|
||||
this.trigger('out')
|
||||
}
|
||||
|
||||
setInteractions()
|
||||
{
|
||||
this.mouseMesh = new THREE.Mesh(
|
||||
new THREE.PlaneBufferGeometry(this.halfExtents.x * 2, this.halfExtents.y * 2, 1, 1),
|
||||
new THREE.MeshBasicMaterial({ transparent: true, opacity: 0 })
|
||||
)
|
||||
this.mouseMesh.position.z = - 0.01
|
||||
this.mouseMesh.matrixAutoUpdate = false
|
||||
this.mouseMesh.updateMatrix()
|
||||
this.container.add(this.mouseMesh)
|
||||
|
||||
this.time.on('tick', () =>
|
||||
{
|
||||
if(this.testCar)
|
||||
{
|
||||
const isIn = Math.abs(this.car.position.x - this.position.x) < Math.abs(this.halfExtents.x) && Math.abs(this.car.position.y - this.position.y) < Math.abs(this.halfExtents.y)
|
||||
|
||||
if(isIn !== this.isIn)
|
||||
{
|
||||
if(isIn)
|
||||
{
|
||||
this.in(!this.config.touch)
|
||||
}
|
||||
else
|
||||
{
|
||||
this.out()
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
window.addEventListener('keydown', (_event) =>
|
||||
{
|
||||
if((_event.key === 'f' || _event.key === 'e' || _event.key === 'Enter') && this.isIn)
|
||||
{
|
||||
this.interact()
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
132
src/javascript/World/Areas.js
Normal file
@ -0,0 +1,132 @@
|
||||
import * as THREE from 'three'
|
||||
|
||||
import Area from './Area.js'
|
||||
|
||||
export default class Areas
|
||||
{
|
||||
constructor(_options)
|
||||
{
|
||||
// Options
|
||||
this.config = _options.config
|
||||
this.resources = _options.resources
|
||||
this.car = _options.car
|
||||
this.sounds = _options.sounds
|
||||
this.renderer = _options.renderer
|
||||
this.camera = _options.camera
|
||||
this.time = _options.time
|
||||
this.debug = _options.debug
|
||||
|
||||
// Set up
|
||||
this.items = []
|
||||
this.container = new THREE.Object3D()
|
||||
this.container.matrixAutoUpdate = false
|
||||
|
||||
this.setMouse()
|
||||
}
|
||||
|
||||
setMouse()
|
||||
{
|
||||
// Set up
|
||||
this.mouse = {}
|
||||
this.mouse.raycaster = new THREE.Raycaster()
|
||||
this.mouse.coordinates = new THREE.Vector2()
|
||||
this.mouse.currentArea = null
|
||||
this.mouse.needsUpdate = false
|
||||
|
||||
// Mouse move event
|
||||
window.addEventListener('mousemove', (_event) =>
|
||||
{
|
||||
this.mouse.coordinates.x = (_event.clientX / window.innerWidth) * 2 - 1
|
||||
this.mouse.coordinates.y = - (_event.clientY / window.innerHeight) * 2 + 1
|
||||
|
||||
this.mouse.needsUpdate = true
|
||||
})
|
||||
|
||||
// Mouse click event
|
||||
window.addEventListener('mousedown', () =>
|
||||
{
|
||||
if(this.mouse.currentArea)
|
||||
{
|
||||
this.mouse.currentArea.interact(false)
|
||||
}
|
||||
})
|
||||
|
||||
// Touch
|
||||
this.renderer.domElement.addEventListener('touchstart', (_event) =>
|
||||
{
|
||||
this.mouse.coordinates.x = (_event.changedTouches[0].clientX / window.innerWidth) * 2 - 1
|
||||
this.mouse.coordinates.y = - (_event.changedTouches[0].clientY / window.innerHeight) * 2 + 1
|
||||
|
||||
this.mouse.needsUpdate = true
|
||||
})
|
||||
|
||||
// Time tick event
|
||||
this.time.on('tick', () =>
|
||||
{
|
||||
// Only update if needed
|
||||
if(this.mouse.needsUpdate)
|
||||
{
|
||||
this.mouse.needsUpdate = false
|
||||
|
||||
// Set up
|
||||
this.mouse.raycaster.setFromCamera(this.mouse.coordinates, this.camera.instance)
|
||||
const objects = this.items.map((_area) => _area.mouseMesh)
|
||||
const intersects = this.mouse.raycaster.intersectObjects(objects)
|
||||
|
||||
// Intersections found
|
||||
if(intersects.length)
|
||||
{
|
||||
// Find the area
|
||||
const area = this.items.find((_area) => _area.mouseMesh === intersects[0].object)
|
||||
|
||||
// Area did change
|
||||
if(area !== this.mouse.currentArea)
|
||||
{
|
||||
// Was previously over an area
|
||||
if(this.mouse.currentArea !== null)
|
||||
{
|
||||
// Play out
|
||||
this.mouse.currentArea.out()
|
||||
this.mouse.currentArea.testCar = this.mouse.currentArea.initialTestCar
|
||||
}
|
||||
|
||||
// Play in
|
||||
this.mouse.currentArea = area
|
||||
this.mouse.currentArea.in(false)
|
||||
this.mouse.currentArea.testCar = false
|
||||
}
|
||||
}
|
||||
// No intersections found but was previously over an area
|
||||
else if(this.mouse.currentArea !== null)
|
||||
{
|
||||
// Play out
|
||||
this.mouse.currentArea.out()
|
||||
this.mouse.currentArea.testCar = this.mouse.currentArea.initialTestCar
|
||||
this.mouse.currentArea = null
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
add(_options)
|
||||
{
|
||||
const area = new Area({
|
||||
config: this.config,
|
||||
renderer: this.renderer,
|
||||
resources: this.resources,
|
||||
car: this.car,
|
||||
sounds: this.sounds,
|
||||
time: this.time,
|
||||
hasKey: true,
|
||||
testCar: true,
|
||||
active: true,
|
||||
..._options
|
||||
})
|
||||
|
||||
this.container.add(area.container)
|
||||
|
||||
this.items.push(area)
|
||||
|
||||
return area
|
||||
}
|
||||
}
|
388
src/javascript/World/Car.js
Normal file
@ -0,0 +1,388 @@
|
||||
import * as THREE from 'three'
|
||||
import CANNON from 'cannon'
|
||||
import { TransformControls } from 'three/examples/jsm/controls/TransformControls.js'
|
||||
|
||||
export default class Car
|
||||
{
|
||||
constructor(_options)
|
||||
{
|
||||
// Options
|
||||
this.time = _options.time
|
||||
this.resources = _options.resources
|
||||
this.objects = _options.objects
|
||||
this.physics = _options.physics
|
||||
this.shadows = _options.shadows
|
||||
this.materials = _options.materials
|
||||
this.controls = _options.controls
|
||||
this.sounds = _options.sounds
|
||||
this.renderer = _options.renderer
|
||||
this.camera = _options.camera
|
||||
this.debug = _options.debug
|
||||
this.config = _options.config
|
||||
|
||||
// Set up
|
||||
this.container = new THREE.Object3D()
|
||||
this.position = new THREE.Vector3()
|
||||
|
||||
// Debug
|
||||
if(this.debug)
|
||||
{
|
||||
this.debugFolder = this.debug.addFolder('car')
|
||||
// this.debugFolder.open()
|
||||
}
|
||||
|
||||
this.setModels()
|
||||
this.setMovement()
|
||||
this.setChassis()
|
||||
this.setAntena()
|
||||
this.setBackLights()
|
||||
this.setWheels()
|
||||
this.setTransformControls()
|
||||
this.setShootingBall()
|
||||
this.setKlaxon()
|
||||
}
|
||||
|
||||
setModels()
|
||||
{
|
||||
this.models = {}
|
||||
|
||||
// Cyber truck
|
||||
if(this.config.cyberTruck)
|
||||
{
|
||||
this.models.chassis = this.resources.items.carCyberTruckChassis
|
||||
this.models.antena = this.resources.items.carCyberTruckAntena
|
||||
this.models.backLightsBrake = this.resources.items.carCyberTruckBackLightsBrake
|
||||
this.models.backLightsReverse = this.resources.items.carCyberTruckBackLightsReverse
|
||||
this.models.wheel = this.resources.items.carCyberTruckWheel
|
||||
}
|
||||
|
||||
// Default
|
||||
else
|
||||
{
|
||||
this.models.chassis = this.resources.items.carDefaultChassis
|
||||
this.models.antena = this.resources.items.carDefaultAntena
|
||||
// this.models.bunnyEarLeft = this.resources.items.carDefaultBunnyEarLeft
|
||||
// this.models.bunnyEarRight = this.resources.items.carDefaultBunnyEarRight
|
||||
this.models.backLightsBrake = this.resources.items.carDefaultBackLightsBrake
|
||||
this.models.backLightsReverse = this.resources.items.carDefaultBackLightsReverse
|
||||
this.models.wheel = this.resources.items.carDefaultWheel
|
||||
}
|
||||
}
|
||||
|
||||
setMovement()
|
||||
{
|
||||
this.movement = {}
|
||||
this.movement.speed = new THREE.Vector3()
|
||||
this.movement.localSpeed = new THREE.Vector3()
|
||||
this.movement.acceleration = new THREE.Vector3()
|
||||
this.movement.localAcceleration = new THREE.Vector3()
|
||||
|
||||
// Time tick
|
||||
this.time.on('tick', () =>
|
||||
{
|
||||
// Movement
|
||||
const movementSpeed = new THREE.Vector3()
|
||||
movementSpeed.copy(this.chassis.object.position).sub(this.chassis.oldPosition)
|
||||
this.movement.acceleration = movementSpeed.clone().sub(this.movement.speed)
|
||||
this.movement.speed.copy(movementSpeed)
|
||||
|
||||
this.movement.localSpeed = this.movement.speed.clone().applyAxisAngle(new THREE.Vector3(0, 0, 1), - this.chassis.object.rotation.z)
|
||||
this.movement.localAcceleration = this.movement.acceleration.clone().applyAxisAngle(new THREE.Vector3(0, 0, 1), - this.chassis.object.rotation.z)
|
||||
|
||||
// Sound
|
||||
this.sounds.engine.speed = this.movement.localSpeed.x
|
||||
this.sounds.engine.acceleration = this.controls.actions.up ? (this.controls.actions.boost ? 1 : 0.5) : 0
|
||||
|
||||
if(this.movement.localAcceleration.x > 0.01)
|
||||
{
|
||||
this.sounds.play('screech')
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
setChassis()
|
||||
{
|
||||
this.chassis = {}
|
||||
this.chassis.offset = new THREE.Vector3(0, 0, - 0.28)
|
||||
this.chassis.object = this.objects.getConvertedMesh(this.models.chassis.scene.children)
|
||||
this.chassis.object.position.copy(this.physics.car.chassis.body.position)
|
||||
this.chassis.oldPosition = this.chassis.object.position.clone()
|
||||
this.container.add(this.chassis.object)
|
||||
|
||||
this.shadows.add(this.chassis.object, { sizeX: 3, sizeY: 2, offsetZ: 0.2 })
|
||||
|
||||
// Time tick
|
||||
this.time.on('tick', () =>
|
||||
{
|
||||
// Save old position for movement calculation
|
||||
this.chassis.oldPosition = this.chassis.object.position.clone()
|
||||
|
||||
// Update if mode physics
|
||||
if(!this.transformControls.enabled)
|
||||
{
|
||||
this.chassis.object.position.copy(this.physics.car.chassis.body.position).add(this.chassis.offset)
|
||||
this.chassis.object.quaternion.copy(this.physics.car.chassis.body.quaternion)
|
||||
}
|
||||
|
||||
// Update position
|
||||
this.position.copy(this.chassis.object.position)
|
||||
})
|
||||
}
|
||||
|
||||
setAntena()
|
||||
{
|
||||
this.antena = {}
|
||||
|
||||
this.antena.speedStrength = 10
|
||||
this.antena.damping = 0.035
|
||||
this.antena.pullBackStrength = 0.02
|
||||
|
||||
this.antena.object = this.objects.getConvertedMesh(this.models.antena.scene.children)
|
||||
this.chassis.object.add(this.antena.object)
|
||||
|
||||
// this.antena.bunnyEarLeft = this.objects.getConvertedMesh(this.models.bunnyEarLeft.scene.children)
|
||||
// this.chassis.object.add(this.antena.bunnyEarLeft)
|
||||
|
||||
// this.antena.bunnyEarRight = this.objects.getConvertedMesh(this.models.bunnyEarRight.scene.children)
|
||||
// this.chassis.object.add(this.antena.bunnyEarRight)
|
||||
|
||||
this.antena.speed = new THREE.Vector2()
|
||||
this.antena.absolutePosition = new THREE.Vector2()
|
||||
this.antena.localPosition = new THREE.Vector2()
|
||||
|
||||
// Time tick
|
||||
this.time.on('tick', () =>
|
||||
{
|
||||
const max = 1
|
||||
const accelerationX = Math.min(Math.max(this.movement.acceleration.x, - max), max)
|
||||
const accelerationY = Math.min(Math.max(this.movement.acceleration.y, - max), max)
|
||||
|
||||
this.antena.speed.x -= accelerationX * this.antena.speedStrength
|
||||
this.antena.speed.y -= accelerationY * this.antena.speedStrength
|
||||
|
||||
const position = this.antena.absolutePosition.clone()
|
||||
const pullBack = position.negate().multiplyScalar(position.length() * this.antena.pullBackStrength)
|
||||
this.antena.speed.add(pullBack)
|
||||
|
||||
this.antena.speed.x *= 1 - this.antena.damping
|
||||
this.antena.speed.y *= 1 - this.antena.damping
|
||||
|
||||
this.antena.absolutePosition.add(this.antena.speed)
|
||||
|
||||
this.antena.localPosition.copy(this.antena.absolutePosition)
|
||||
this.antena.localPosition.rotateAround(new THREE.Vector2(), - this.chassis.object.rotation.z)
|
||||
|
||||
this.antena.object.rotation.y = this.antena.localPosition.x * 0.1
|
||||
this.antena.object.rotation.x = this.antena.localPosition.y * 0.1
|
||||
|
||||
// this.antena.bunnyEarLeft.rotation.y = this.antena.localPosition.x * 0.1
|
||||
// this.antena.bunnyEarLeft.rotation.x = this.antena.localPosition.y * 0.1
|
||||
|
||||
// this.antena.bunnyEarRight.rotation.y = this.antena.localPosition.x * 0.1
|
||||
// this.antena.bunnyEarRight.rotation.x = this.antena.localPosition.y * 0.1
|
||||
})
|
||||
|
||||
// Debug
|
||||
if(this.debug)
|
||||
{
|
||||
const folder = this.debugFolder.addFolder('antena')
|
||||
folder.open()
|
||||
|
||||
folder.add(this.antena, 'speedStrength').step(0.001).min(0).max(50)
|
||||
folder.add(this.antena, 'damping').step(0.0001).min(0).max(0.1)
|
||||
folder.add(this.antena, 'pullBackStrength').step(0.0001).min(0).max(0.1)
|
||||
}
|
||||
}
|
||||
|
||||
setBackLights()
|
||||
{
|
||||
this.backLightsBrake = {}
|
||||
|
||||
this.backLightsBrake.material = this.materials.pures.items.red.clone()
|
||||
this.backLightsBrake.material.transparent = true
|
||||
this.backLightsBrake.material.opacity = 0.5
|
||||
|
||||
this.backLightsBrake.object = this.objects.getConvertedMesh(this.models.backLightsBrake.scene.children)
|
||||
for(const _child of this.backLightsBrake.object.children)
|
||||
{
|
||||
_child.material = this.backLightsBrake.material
|
||||
}
|
||||
|
||||
this.chassis.object.add(this.backLightsBrake.object)
|
||||
|
||||
// Back lights brake
|
||||
this.backLightsReverse = {}
|
||||
|
||||
this.backLightsReverse.material = this.materials.pures.items.yellow.clone()
|
||||
this.backLightsReverse.material.transparent = true
|
||||
this.backLightsReverse.material.opacity = 0.5
|
||||
|
||||
this.backLightsReverse.object = this.objects.getConvertedMesh(this.models.backLightsReverse.scene.children)
|
||||
for(const _child of this.backLightsReverse.object.children)
|
||||
{
|
||||
_child.material = this.backLightsReverse.material
|
||||
}
|
||||
|
||||
this.chassis.object.add(this.backLightsReverse.object)
|
||||
|
||||
// Time tick
|
||||
this.time.on('tick', () =>
|
||||
{
|
||||
this.backLightsBrake.material.opacity = this.physics.controls.actions.brake ? 1 : 0.5
|
||||
this.backLightsReverse.material.opacity = this.physics.controls.actions.down ? 1 : 0.5
|
||||
})
|
||||
}
|
||||
|
||||
setWheels()
|
||||
{
|
||||
this.wheels = {}
|
||||
this.wheels.object = this.objects.getConvertedMesh(this.models.wheel.scene.children)
|
||||
this.wheels.items = []
|
||||
|
||||
for(let i = 0; i < 4; i++)
|
||||
{
|
||||
const object = this.wheels.object.clone()
|
||||
|
||||
this.wheels.items.push(object)
|
||||
this.container.add(object)
|
||||
}
|
||||
|
||||
// Time tick
|
||||
this.time.on('tick', () =>
|
||||
{
|
||||
if(!this.transformControls.enabled)
|
||||
{
|
||||
for(const _wheelKey in this.physics.car.wheels.bodies)
|
||||
{
|
||||
const wheelBody = this.physics.car.wheels.bodies[_wheelKey]
|
||||
const wheelObject = this.wheels.items[_wheelKey]
|
||||
|
||||
wheelObject.position.copy(wheelBody.position)
|
||||
wheelObject.quaternion.copy(wheelBody.quaternion)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
setTransformControls()
|
||||
{
|
||||
this.transformControls = new TransformControls(this.camera.instance, this.renderer.domElement)
|
||||
this.transformControls.size = 0.5
|
||||
this.transformControls.attach(this.chassis.object)
|
||||
this.transformControls.enabled = false
|
||||
this.transformControls.visible = this.transformControls.enabled
|
||||
|
||||
document.addEventListener('keydown', (_event) =>
|
||||
{
|
||||
if(this.mode === 'transformControls')
|
||||
{
|
||||
if(_event.key === 'r')
|
||||
{
|
||||
this.transformControls.setMode('rotate')
|
||||
}
|
||||
else if(_event.key === 'g')
|
||||
{
|
||||
this.transformControls.setMode('translate')
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
this.transformControls.addEventListener('dragging-changed', (_event) =>
|
||||
{
|
||||
this.camera.orbitControls.enabled = !_event.value
|
||||
})
|
||||
|
||||
this.container.add(this.transformControls)
|
||||
|
||||
if(this.debug)
|
||||
{
|
||||
const folder = this.debugFolder.addFolder('controls')
|
||||
folder.open()
|
||||
|
||||
folder.add(this.transformControls, 'enabled').onChange(() =>
|
||||
{
|
||||
this.transformControls.visible = this.transformControls.enabled
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
setShootingBall()
|
||||
{
|
||||
if(!this.config.cyberTruck)
|
||||
{
|
||||
return
|
||||
}
|
||||
|
||||
window.addEventListener('keydown', (_event) =>
|
||||
{
|
||||
if(_event.key === 'b')
|
||||
{
|
||||
const angle = Math.random() * Math.PI * 2
|
||||
const distance = 10
|
||||
const x = this.position.x + Math.cos(angle) * distance
|
||||
const y = this.position.y + Math.sin(angle) * distance
|
||||
const z = 2 + 2 * Math.random()
|
||||
const bowlingBall = this.objects.add({
|
||||
base: this.resources.items.bowlingBallBase.scene,
|
||||
collision: this.resources.items.bowlingBallCollision.scene,
|
||||
offset: new THREE.Vector3(x, y, z),
|
||||
rotation: new THREE.Euler(Math.PI * 0.5, 0, 0),
|
||||
duplicated: true,
|
||||
shadow: { sizeX: 1.5, sizeY: 1.5, offsetZ: - 0.15, alpha: 0.35 },
|
||||
mass: 5,
|
||||
soundName: 'bowlingBall',
|
||||
sleep: false
|
||||
})
|
||||
|
||||
const carPosition = new CANNON.Vec3(this.position.x, this.position.y, this.position.z + 1)
|
||||
let direction = carPosition.vsub(bowlingBall.collision.body.position)
|
||||
direction.normalize()
|
||||
direction = direction.scale(100)
|
||||
bowlingBall.collision.body.applyImpulse(direction, bowlingBall.collision.body.position)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
setKlaxon()
|
||||
{
|
||||
this.klaxon = {}
|
||||
this.klaxon.waitDuration = 150
|
||||
this.klaxon.can = true
|
||||
|
||||
window.addEventListener('keydown', (_event) =>
|
||||
{
|
||||
// Play horn sound
|
||||
if(_event.key === 'h' && this.klaxon.can)
|
||||
{
|
||||
this.klaxon.can = false
|
||||
window.setTimeout(() =>
|
||||
{
|
||||
this.klaxon.can = true
|
||||
}, this.klaxon.waitDuration)
|
||||
|
||||
this.physics.car.jump(false, 20)
|
||||
this.sounds.play(Math.random() < 0.002 ? 'carHorn2' : 'carHorn1')
|
||||
}
|
||||
|
||||
// Rain horns
|
||||
if(_event.key === 'k')
|
||||
{
|
||||
const x = this.position.x + (Math.random() - 0.5) * 3
|
||||
const y = this.position.y + (Math.random() - 0.5) * 3
|
||||
const z = 6 + 2 * Math.random()
|
||||
|
||||
this.objects.add({
|
||||
base: this.resources.items.hornBase.scene,
|
||||
collision: this.resources.items.hornCollision.scene,
|
||||
offset: new THREE.Vector3(x, y, z),
|
||||
rotation: new THREE.Euler(Math.random() * Math.PI * 2, Math.random() * Math.PI * 2, Math.random() * Math.PI * 2),
|
||||
duplicated: true,
|
||||
shadow: { sizeX: 1.5, sizeY: 1.5, offsetZ: - 0.15, alpha: 0.35 },
|
||||
mass: 5,
|
||||
soundName: 'horn',
|
||||
sleep: false
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
653
src/javascript/World/Controls.js
Normal file
@ -0,0 +1,653 @@
|
||||
import mobileDoubleTriangle from '../../images/mobile/doubleTriangle.png'
|
||||
import mobileTriangle from '../../images/mobile/triangle.png'
|
||||
import mobileCross from '../../images/mobile/cross.png'
|
||||
import EventEmitter from '../Utils/EventEmitter'
|
||||
import { TweenLite } from 'gsap/TweenLite'
|
||||
|
||||
export default class Controls extends EventEmitter
|
||||
{
|
||||
constructor(_options)
|
||||
{
|
||||
super()
|
||||
|
||||
this.config = _options.config
|
||||
this.sizes = _options.sizes
|
||||
this.time = _options.time
|
||||
this.camera = _options.camera
|
||||
this.sounds = _options.sounds
|
||||
|
||||
this.setActions()
|
||||
this.setKeyboard()
|
||||
}
|
||||
|
||||
setActions()
|
||||
{
|
||||
this.actions = {}
|
||||
this.actions.up = false
|
||||
this.actions.right = false
|
||||
this.actions.down = false
|
||||
this.actions.left = false
|
||||
this.actions.brake = false
|
||||
this.actions.boost = false
|
||||
|
||||
document.addEventListener('visibilitychange', () =>
|
||||
{
|
||||
if(!document.hidden)
|
||||
{
|
||||
this.actions.up = false
|
||||
this.actions.right = false
|
||||
this.actions.down = false
|
||||
this.actions.left = false
|
||||
this.actions.brake = false
|
||||
this.actions.boost = false
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
setKeyboard()
|
||||
{
|
||||
this.keyboard = {}
|
||||
this.keyboard.events = {}
|
||||
|
||||
this.keyboard.events.keyDown = (_event) =>
|
||||
{
|
||||
switch(_event.key)
|
||||
{
|
||||
case 'ArrowUp':
|
||||
case 'z':
|
||||
case 'w':
|
||||
this.camera.pan.reset()
|
||||
this.actions.up = true
|
||||
break
|
||||
|
||||
case 'ArrowRight':
|
||||
case 'd':
|
||||
this.actions.right = true
|
||||
break
|
||||
|
||||
case 'ArrowDown':
|
||||
case 's':
|
||||
this.camera.pan.reset()
|
||||
this.actions.down = true
|
||||
break
|
||||
|
||||
case 'ArrowLeft':
|
||||
case 'q':
|
||||
case 'a':
|
||||
this.actions.left = true
|
||||
break
|
||||
|
||||
case 'Control':
|
||||
case ' ':
|
||||
this.actions.brake = true
|
||||
break
|
||||
|
||||
case 'Shift':
|
||||
this.actions.boost = true
|
||||
break
|
||||
|
||||
// case ' ':
|
||||
// this.jump(true)
|
||||
// break
|
||||
}
|
||||
}
|
||||
|
||||
this.keyboard.events.keyUp = (_event) =>
|
||||
{
|
||||
switch(_event.key)
|
||||
{
|
||||
case 'ArrowUp':
|
||||
case 'z':
|
||||
case 'w':
|
||||
this.actions.up = false
|
||||
break
|
||||
|
||||
case 'ArrowRight':
|
||||
case 'd':
|
||||
this.actions.right = false
|
||||
break
|
||||
|
||||
case 'ArrowDown':
|
||||
case 's':
|
||||
this.actions.down = false
|
||||
break
|
||||
|
||||
case 'ArrowLeft':
|
||||
case 'q':
|
||||
case 'a':
|
||||
this.actions.left = false
|
||||
break
|
||||
|
||||
case 'Control':
|
||||
case ' ':
|
||||
this.actions.brake = false
|
||||
break
|
||||
|
||||
case 'Shift':
|
||||
this.actions.boost = false
|
||||
break
|
||||
|
||||
case 'r':
|
||||
this.trigger('action', ['reset'])
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
document.addEventListener('keydown', this.keyboard.events.keyDown)
|
||||
document.addEventListener('keyup', this.keyboard.events.keyUp)
|
||||
}
|
||||
|
||||
setTouch()
|
||||
{
|
||||
this.touch = {}
|
||||
|
||||
/**
|
||||
* Joystick
|
||||
*/
|
||||
this.touch.joystick = {}
|
||||
this.touch.joystick.active = false
|
||||
|
||||
// Element
|
||||
this.touch.joystick.$element = document.createElement('div')
|
||||
this.touch.joystick.$element.style.userSelect = 'none'
|
||||
this.touch.joystick.$element.style.position = 'fixed'
|
||||
this.touch.joystick.$element.style.bottom = '10px'
|
||||
this.touch.joystick.$element.style.left = '10px'
|
||||
this.touch.joystick.$element.style.width = '170px'
|
||||
this.touch.joystick.$element.style.height = '170px'
|
||||
this.touch.joystick.$element.style.borderRadius = '50%'
|
||||
this.touch.joystick.$element.style.transition = 'opacity 0.3s 0.0s'
|
||||
this.touch.joystick.$element.style.willChange = 'opacity'
|
||||
this.touch.joystick.$element.style.opacity = '0'
|
||||
// this.touch.joystick.$element.style.backgroundColor = '#ff0000'
|
||||
document.body.appendChild(this.touch.joystick.$element)
|
||||
|
||||
this.touch.joystick.$cursor = document.createElement('div')
|
||||
this.touch.joystick.$cursor.style.position = 'absolute'
|
||||
this.touch.joystick.$cursor.style.top = 'calc(50% - 30px)'
|
||||
this.touch.joystick.$cursor.style.left = 'calc(50% - 30px)'
|
||||
this.touch.joystick.$cursor.style.width = '60px'
|
||||
this.touch.joystick.$cursor.style.height = '60px'
|
||||
this.touch.joystick.$cursor.style.border = '2px solid #ffffff'
|
||||
this.touch.joystick.$cursor.style.borderRadius = '50%'
|
||||
this.touch.joystick.$cursor.style.boxSizing = 'border-box'
|
||||
this.touch.joystick.$cursor.style.pointerEvents = 'none'
|
||||
this.touch.joystick.$cursor.style.willChange = 'transform'
|
||||
this.touch.joystick.$element.appendChild(this.touch.joystick.$cursor)
|
||||
|
||||
this.touch.joystick.$limit = document.createElement('div')
|
||||
this.touch.joystick.$limit.style.position = 'absolute'
|
||||
this.touch.joystick.$limit.style.top = 'calc(50% - 75px)'
|
||||
this.touch.joystick.$limit.style.left = 'calc(50% - 75px)'
|
||||
this.touch.joystick.$limit.style.width = '150px'
|
||||
this.touch.joystick.$limit.style.height = '150px'
|
||||
this.touch.joystick.$limit.style.border = '2px solid #ffffff'
|
||||
this.touch.joystick.$limit.style.borderRadius = '50%'
|
||||
this.touch.joystick.$limit.style.opacity = '0.25'
|
||||
this.touch.joystick.$limit.style.pointerEvents = 'none'
|
||||
this.touch.joystick.$limit.style.boxSizing = 'border-box'
|
||||
this.touch.joystick.$element.appendChild(this.touch.joystick.$limit)
|
||||
|
||||
// Angle
|
||||
this.touch.joystick.angle = {}
|
||||
|
||||
this.touch.joystick.angle.offset = Math.PI * 0.18
|
||||
|
||||
this.touch.joystick.angle.center = {}
|
||||
this.touch.joystick.angle.center.x = 0
|
||||
this.touch.joystick.angle.center.y = 0
|
||||
|
||||
this.touch.joystick.angle.current = {}
|
||||
this.touch.joystick.angle.current.x = 0
|
||||
this.touch.joystick.angle.current.y = 0
|
||||
|
||||
this.touch.joystick.angle.originalValue = 0
|
||||
this.touch.joystick.angle.value = - Math.PI * 0.5
|
||||
|
||||
// Resize
|
||||
this.touch.joystick.resize = () =>
|
||||
{
|
||||
const boundings = this.touch.joystick.$element.getBoundingClientRect()
|
||||
|
||||
this.touch.joystick.angle.center.x = boundings.left + boundings.width * 0.5
|
||||
this.touch.joystick.angle.center.y = boundings.top + boundings.height * 0.5
|
||||
}
|
||||
|
||||
this.sizes.on('resize', this.touch.joystick.resize)
|
||||
this.touch.joystick.resize()
|
||||
|
||||
// Time tick
|
||||
this.time.on('tick', () =>
|
||||
{
|
||||
// Joystick active
|
||||
if(this.touch.joystick.active)
|
||||
{
|
||||
// Calculate joystick angle
|
||||
this.touch.joystick.angle.originalValue = - Math.atan2(
|
||||
this.touch.joystick.angle.current.y - this.touch.joystick.angle.center.y,
|
||||
this.touch.joystick.angle.current.x - this.touch.joystick.angle.center.x
|
||||
)
|
||||
this.touch.joystick.angle.value = this.touch.joystick.angle.originalValue + this.touch.joystick.angle.offset
|
||||
|
||||
// Update joystick
|
||||
const distance = Math.hypot(this.touch.joystick.angle.current.y - this.touch.joystick.angle.center.y, this.touch.joystick.angle.current.x - this.touch.joystick.angle.center.x)
|
||||
let radius = distance
|
||||
if(radius > 20)
|
||||
{
|
||||
radius = 20 + Math.log(distance - 20) * 5
|
||||
}
|
||||
if(radius > 43)
|
||||
{
|
||||
radius = 43
|
||||
}
|
||||
const cursorX = Math.sin(this.touch.joystick.angle.originalValue + Math.PI * 0.5) * radius
|
||||
const cursorY = Math.cos(this.touch.joystick.angle.originalValue + Math.PI * 0.5) * radius
|
||||
this.touch.joystick.$cursor.style.transform = `translateX(${cursorX}px) translateY(${cursorY}px)`
|
||||
}
|
||||
})
|
||||
|
||||
// Events
|
||||
this.touch.joystick.events = {}
|
||||
this.touch.joystick.touchIdentifier = null
|
||||
this.touch.joystick.events.touchstart = (_event) =>
|
||||
{
|
||||
_event.preventDefault()
|
||||
|
||||
const touch = _event.changedTouches[0]
|
||||
|
||||
if(touch)
|
||||
{
|
||||
this.touch.joystick.active = true
|
||||
|
||||
this.touch.joystick.touchIdentifier = touch.identifier
|
||||
|
||||
this.touch.joystick.angle.current.x = touch.clientX
|
||||
this.touch.joystick.angle.current.y = touch.clientY
|
||||
|
||||
this.touch.joystick.$limit.style.opacity = '0.5'
|
||||
|
||||
document.addEventListener('touchend', this.touch.joystick.events.touchend)
|
||||
document.addEventListener('touchmove', this.touch.joystick.events.touchmove, { passive: false })
|
||||
|
||||
this.trigger('joystickStart')
|
||||
}
|
||||
}
|
||||
|
||||
this.touch.joystick.events.touchmove = (_event) =>
|
||||
{
|
||||
_event.preventDefault()
|
||||
|
||||
const touches = [..._event.changedTouches]
|
||||
const touch = touches.find((_touch) => _touch.identifier === this.touch.joystick.touchIdentifier)
|
||||
|
||||
if(touch)
|
||||
{
|
||||
this.touch.joystick.angle.current.x = touch.clientX
|
||||
this.touch.joystick.angle.current.y = touch.clientY
|
||||
|
||||
this.trigger('joystickMove')
|
||||
}
|
||||
}
|
||||
|
||||
this.touch.joystick.events.touchend = (_event) =>
|
||||
{
|
||||
const touches = [..._event.changedTouches]
|
||||
const touch = touches.find((_touch) => _touch.identifier === this.touch.joystick.touchIdentifier)
|
||||
|
||||
if(touch)
|
||||
{
|
||||
this.touch.joystick.active = false
|
||||
|
||||
this.touch.joystick.$limit.style.opacity = '0.25'
|
||||
|
||||
this.touch.joystick.$cursor.style.transform = 'translateX(0px) translateY(0px)'
|
||||
|
||||
document.removeEventListener('touchend', this.touch.joystick.events.touchend)
|
||||
|
||||
this.trigger('joystickEnd')
|
||||
}
|
||||
}
|
||||
|
||||
this.touch.joystick.$element.addEventListener('touchstart', this.touch.joystick.events.touchstart, { passive: false })
|
||||
|
||||
/**
|
||||
* Boost
|
||||
*/
|
||||
this.touch.boost = {}
|
||||
|
||||
// Element
|
||||
this.touch.boost.$element = document.createElement('div')
|
||||
this.touch.boost.$element.style.userSelect = 'none'
|
||||
this.touch.boost.$element.style.position = 'fixed'
|
||||
this.touch.boost.$element.style.bottom = 'calc(70px * 3 + 15px)'
|
||||
this.touch.boost.$element.style.right = '0px'
|
||||
this.touch.boost.$element.style.width = '95px'
|
||||
this.touch.boost.$element.style.height = '70px'
|
||||
this.touch.boost.$element.style.transition = 'opacity 0.3s 0.4s'
|
||||
this.touch.boost.$element.style.willChange = 'opacity'
|
||||
this.touch.boost.$element.style.opacity = '0'
|
||||
// this.touch.boost.$element.style.backgroundColor = '#00ff00'
|
||||
document.body.appendChild(this.touch.boost.$element)
|
||||
|
||||
this.touch.boost.$border = document.createElement('div')
|
||||
this.touch.boost.$border.style.position = 'absolute'
|
||||
this.touch.boost.$border.style.top = 'calc(50% - 30px)'
|
||||
this.touch.boost.$border.style.left = 'calc(50% - 30px)'
|
||||
this.touch.boost.$border.style.width = '60px'
|
||||
this.touch.boost.$border.style.height = '60px'
|
||||
this.touch.boost.$border.style.border = '2px solid #ffffff'
|
||||
this.touch.boost.$border.style.borderRadius = '10px'
|
||||
this.touch.boost.$border.style.boxSizing = 'border-box'
|
||||
this.touch.boost.$border.style.opacity = '0.25'
|
||||
this.touch.boost.$border.style.willChange = 'opacity'
|
||||
this.touch.boost.$element.appendChild(this.touch.boost.$border)
|
||||
|
||||
this.touch.boost.$icon = document.createElement('div')
|
||||
this.touch.boost.$icon.style.position = 'absolute'
|
||||
this.touch.boost.$icon.style.top = 'calc(50% - 13px)'
|
||||
this.touch.boost.$icon.style.left = 'calc(50% - 11px)'
|
||||
this.touch.boost.$icon.style.width = '22px'
|
||||
this.touch.boost.$icon.style.height = '26px'
|
||||
this.touch.boost.$icon.style.backgroundImage = `url(${mobileDoubleTriangle})`
|
||||
this.touch.boost.$icon.style.backgroundSize = 'cover'
|
||||
this.touch.boost.$element.appendChild(this.touch.boost.$icon)
|
||||
|
||||
// Events
|
||||
this.touch.boost.events = {}
|
||||
this.touch.boost.touchIdentifier = null
|
||||
this.touch.boost.events.touchstart = (_event) =>
|
||||
{
|
||||
_event.preventDefault()
|
||||
|
||||
const touch = _event.changedTouches[0]
|
||||
|
||||
if(touch)
|
||||
{
|
||||
this.camera.pan.reset()
|
||||
|
||||
this.touch.boost.touchIdentifier = touch.identifier
|
||||
|
||||
this.actions.up = true
|
||||
this.actions.boost = true
|
||||
|
||||
this.touch.boost.$border.style.opacity = '0.5'
|
||||
|
||||
document.addEventListener('touchend', this.touch.boost.events.touchend)
|
||||
}
|
||||
}
|
||||
|
||||
this.touch.boost.events.touchend = (_event) =>
|
||||
{
|
||||
const touches = [..._event.changedTouches]
|
||||
const touch = touches.find((_touch) => _touch.identifier === this.touch.boost.touchIdentifier)
|
||||
|
||||
if(touch)
|
||||
{
|
||||
this.actions.up = false
|
||||
this.actions.boost = false
|
||||
|
||||
this.touch.boost.$border.style.opacity = '0.25'
|
||||
|
||||
document.removeEventListener('touchend', this.touch.boost.events.touchend)
|
||||
}
|
||||
}
|
||||
|
||||
this.touch.boost.$element.addEventListener('touchstart', this.touch.boost.events.touchstart)
|
||||
|
||||
/**
|
||||
* Forward
|
||||
*/
|
||||
this.touch.forward = {}
|
||||
|
||||
// Element
|
||||
this.touch.forward.$element = document.createElement('div')
|
||||
this.touch.forward.$element.style.userSelect = 'none'
|
||||
this.touch.forward.$element.style.position = 'fixed'
|
||||
this.touch.forward.$element.style.bottom = 'calc(70px * 2 + 15px)'
|
||||
this.touch.forward.$element.style.right = '0px'
|
||||
this.touch.forward.$element.style.width = '95px'
|
||||
this.touch.forward.$element.style.height = '70px'
|
||||
this.touch.forward.$element.style.transition = 'opacity 0.3s 0.3s'
|
||||
this.touch.forward.$element.style.willChange = 'opacity'
|
||||
this.touch.forward.$element.style.opacity = '0'
|
||||
// this.touch.forward.$element.style.backgroundColor = '#00ff00'
|
||||
document.body.appendChild(this.touch.forward.$element)
|
||||
|
||||
this.touch.forward.$border = document.createElement('div')
|
||||
this.touch.forward.$border.style.position = 'absolute'
|
||||
this.touch.forward.$border.style.top = 'calc(50% - 30px)'
|
||||
this.touch.forward.$border.style.left = 'calc(50% - 30px)'
|
||||
this.touch.forward.$border.style.width = '60px'
|
||||
this.touch.forward.$border.style.height = '60px'
|
||||
this.touch.forward.$border.style.border = '2px solid #ffffff'
|
||||
this.touch.forward.$border.style.borderRadius = '10px'
|
||||
this.touch.forward.$border.style.boxSizing = 'border-box'
|
||||
this.touch.forward.$border.style.opacity = '0.25'
|
||||
this.touch.forward.$border.style.willChange = 'opacity'
|
||||
this.touch.forward.$element.appendChild(this.touch.forward.$border)
|
||||
|
||||
this.touch.forward.$icon = document.createElement('div')
|
||||
this.touch.forward.$icon.style.position = 'absolute'
|
||||
this.touch.forward.$icon.style.top = 'calc(50% - 9px)'
|
||||
this.touch.forward.$icon.style.left = 'calc(50% - 11px)'
|
||||
this.touch.forward.$icon.style.width = '22px'
|
||||
this.touch.forward.$icon.style.height = '18px'
|
||||
this.touch.forward.$icon.style.backgroundImage = `url(${mobileTriangle})`
|
||||
this.touch.forward.$icon.style.backgroundSize = 'cover'
|
||||
this.touch.forward.$element.appendChild(this.touch.forward.$icon)
|
||||
|
||||
// Events
|
||||
this.touch.forward.events = {}
|
||||
this.touch.forward.touchIdentifier = null
|
||||
this.touch.forward.events.touchstart = (_event) =>
|
||||
{
|
||||
_event.preventDefault()
|
||||
|
||||
const touch = _event.changedTouches[0]
|
||||
|
||||
if(touch)
|
||||
{
|
||||
this.camera.pan.reset()
|
||||
|
||||
this.touch.forward.touchIdentifier = touch.identifier
|
||||
|
||||
this.actions.up = true
|
||||
|
||||
this.touch.forward.$border.style.opacity = '0.5'
|
||||
|
||||
document.addEventListener('touchend', this.touch.forward.events.touchend)
|
||||
}
|
||||
}
|
||||
|
||||
this.touch.forward.events.touchend = (_event) =>
|
||||
{
|
||||
const touches = [..._event.changedTouches]
|
||||
const touch = touches.find((_touch) => _touch.identifier === this.touch.forward.touchIdentifier)
|
||||
|
||||
if(touch)
|
||||
{
|
||||
this.actions.up = false
|
||||
|
||||
this.touch.forward.$border.style.opacity = '0.25'
|
||||
|
||||
document.removeEventListener('touchend', this.touch.forward.events.touchend)
|
||||
}
|
||||
}
|
||||
|
||||
this.touch.forward.$element.addEventListener('touchstart', this.touch.forward.events.touchstart)
|
||||
|
||||
/**
|
||||
* Brake
|
||||
*/
|
||||
this.touch.brake = {}
|
||||
|
||||
// Element
|
||||
this.touch.brake.$element = document.createElement('div')
|
||||
this.touch.brake.$element.style.userSelect = 'none'
|
||||
this.touch.brake.$element.style.position = 'fixed'
|
||||
this.touch.brake.$element.style.bottom = 'calc(70px + 15px)'
|
||||
this.touch.brake.$element.style.right = '0px'
|
||||
this.touch.brake.$element.style.width = '95px'
|
||||
this.touch.brake.$element.style.height = '70px'
|
||||
this.touch.brake.$element.style.transition = 'opacity 0.3s 0.2s'
|
||||
this.touch.brake.$element.style.willChange = 'opacity'
|
||||
this.touch.brake.$element.style.opacity = '0'
|
||||
// this.touch.brake.$element.style.backgroundColor = '#ff0000'
|
||||
document.body.appendChild(this.touch.brake.$element)
|
||||
|
||||
this.touch.brake.$border = document.createElement('div')
|
||||
this.touch.brake.$border.style.position = 'absolute'
|
||||
this.touch.brake.$border.style.top = 'calc(50% - 30px)'
|
||||
this.touch.brake.$border.style.left = 'calc(50% - 30px)'
|
||||
this.touch.brake.$border.style.width = '60px'
|
||||
this.touch.brake.$border.style.height = '60px'
|
||||
this.touch.brake.$border.style.border = '2px solid #ffffff'
|
||||
this.touch.brake.$border.style.borderRadius = '10px'
|
||||
this.touch.brake.$border.style.boxSizing = 'border-box'
|
||||
this.touch.brake.$border.style.opacity = '0.25'
|
||||
this.touch.brake.$border.style.willChange = 'opacity'
|
||||
this.touch.brake.$element.appendChild(this.touch.brake.$border)
|
||||
|
||||
this.touch.brake.$icon = document.createElement('div')
|
||||
this.touch.brake.$icon.style.position = 'absolute'
|
||||
this.touch.brake.$icon.style.top = 'calc(50% - 7px)'
|
||||
this.touch.brake.$icon.style.left = 'calc(50% - 7px)'
|
||||
this.touch.brake.$icon.style.width = '15px'
|
||||
this.touch.brake.$icon.style.height = '15px'
|
||||
this.touch.brake.$icon.style.backgroundImage = `url(${mobileCross})`
|
||||
this.touch.brake.$icon.style.backgroundSize = 'cover'
|
||||
this.touch.brake.$icon.style.transform = 'rotate(180deg)'
|
||||
this.touch.brake.$element.appendChild(this.touch.brake.$icon)
|
||||
|
||||
// Events
|
||||
this.touch.brake.events = {}
|
||||
this.touch.brake.touchIdentifier = null
|
||||
this.touch.brake.events.touchstart = (_event) =>
|
||||
{
|
||||
_event.preventDefault()
|
||||
|
||||
const touch = _event.changedTouches[0]
|
||||
|
||||
if(touch)
|
||||
{
|
||||
this.touch.brake.touchIdentifier = touch.identifier
|
||||
|
||||
this.actions.brake = true
|
||||
|
||||
this.touch.brake.$border.style.opacity = '0.5'
|
||||
|
||||
document.addEventListener('touchend', this.touch.brake.events.touchend)
|
||||
}
|
||||
}
|
||||
|
||||
this.touch.brake.events.touchend = (_event) =>
|
||||
{
|
||||
const touches = [..._event.changedTouches]
|
||||
const touch = touches.find((_touch) => _touch.identifier === this.touch.brake.touchIdentifier)
|
||||
|
||||
if(touch)
|
||||
{
|
||||
this.actions.brake = false
|
||||
|
||||
this.touch.brake.$border.style.opacity = '0.25'
|
||||
|
||||
document.removeEventListener('touchend', this.touch.brake.events.touchend)
|
||||
}
|
||||
}
|
||||
|
||||
this.touch.brake.$element.addEventListener('touchstart', this.touch.brake.events.touchstart)
|
||||
|
||||
/**
|
||||
* Backward
|
||||
*/
|
||||
this.touch.backward = {}
|
||||
|
||||
// Element
|
||||
this.touch.backward.$element = document.createElement('div')
|
||||
this.touch.backward.$element.style.userSelect = 'none'
|
||||
this.touch.backward.$element.style.position = 'fixed'
|
||||
this.touch.backward.$element.style.bottom = '15px'
|
||||
this.touch.backward.$element.style.right = '0px'
|
||||
this.touch.backward.$element.style.width = '95px'
|
||||
this.touch.backward.$element.style.height = '70px'
|
||||
this.touch.backward.$element.style.transition = 'opacity 0.3s 0.1s'
|
||||
this.touch.backward.$element.style.willChange = 'opacity'
|
||||
this.touch.backward.$element.style.opacity = '0'
|
||||
// this.touch.backward.$element.style.backgroundColor = '#0000ff'
|
||||
document.body.appendChild(this.touch.backward.$element)
|
||||
|
||||
this.touch.backward.$border = document.createElement('div')
|
||||
this.touch.backward.$border.style.position = 'absolute'
|
||||
this.touch.backward.$border.style.top = 'calc(50% - 30px)'
|
||||
this.touch.backward.$border.style.left = 'calc(50% - 30px)'
|
||||
this.touch.backward.$border.style.width = '60px'
|
||||
this.touch.backward.$border.style.height = '60px'
|
||||
this.touch.backward.$border.style.border = '2px solid #ffffff'
|
||||
this.touch.backward.$border.style.borderRadius = '10px'
|
||||
this.touch.backward.$border.style.boxSizing = 'border-box'
|
||||
this.touch.backward.$border.style.opacity = '0.25'
|
||||
this.touch.backward.$border.style.willChange = 'opacity'
|
||||
this.touch.backward.$element.appendChild(this.touch.backward.$border)
|
||||
|
||||
this.touch.backward.$icon = document.createElement('div')
|
||||
this.touch.backward.$icon.style.position = 'absolute'
|
||||
this.touch.backward.$icon.style.top = 'calc(50% - 9px)'
|
||||
this.touch.backward.$icon.style.left = 'calc(50% - 11px)'
|
||||
this.touch.backward.$icon.style.width = '22px'
|
||||
this.touch.backward.$icon.style.height = '18px'
|
||||
this.touch.backward.$icon.style.backgroundImage = `url(${mobileTriangle})`
|
||||
this.touch.backward.$icon.style.backgroundSize = 'cover'
|
||||
this.touch.backward.$icon.style.transform = 'rotate(180deg)'
|
||||
this.touch.backward.$element.appendChild(this.touch.backward.$icon)
|
||||
|
||||
// Events
|
||||
this.touch.backward.events = {}
|
||||
this.touch.backward.touchIdentifier = null
|
||||
this.touch.backward.events.touchstart = (_event) =>
|
||||
{
|
||||
_event.preventDefault()
|
||||
|
||||
const touch = _event.changedTouches[0]
|
||||
|
||||
if(touch)
|
||||
{
|
||||
this.camera.pan.reset()
|
||||
|
||||
this.touch.backward.touchIdentifier = touch.identifier
|
||||
|
||||
this.actions.down = true
|
||||
|
||||
this.touch.backward.$border.style.opacity = '0.5'
|
||||
|
||||
document.addEventListener('touchend', this.touch.backward.events.touchend)
|
||||
}
|
||||
}
|
||||
|
||||
this.touch.backward.events.touchend = (_event) =>
|
||||
{
|
||||
const touches = [..._event.changedTouches]
|
||||
const touch = touches.find((_touch) => _touch.identifier === this.touch.backward.touchIdentifier)
|
||||
|
||||
if(touch)
|
||||
{
|
||||
this.actions.down = false
|
||||
|
||||
this.touch.backward.$border.style.opacity = '0.25'
|
||||
|
||||
document.removeEventListener('touchend', this.touch.backward.events.touchend)
|
||||
}
|
||||
}
|
||||
|
||||
this.touch.backward.$element.addEventListener('touchstart', this.touch.backward.events.touchstart)
|
||||
|
||||
// Reveal
|
||||
this.touch.reveal = () =>
|
||||
{
|
||||
this.touch.joystick.$element.style.opacity = 1
|
||||
this.touch.backward.$element.style.opacity = 1
|
||||
this.touch.brake.$element.style.opacity = 1
|
||||
this.touch.forward.$element.style.opacity = 1
|
||||
this.touch.boost.$element.style.opacity = 1
|
||||
}
|
||||
}
|
||||
}
|
385
src/javascript/World/EasterEggs.js
Normal file
@ -0,0 +1,385 @@
|
||||
import * as THREE from 'three'
|
||||
|
||||
export default class EasterEggs
|
||||
{
|
||||
constructor(_options)
|
||||
{
|
||||
// Options
|
||||
this.resources = _options.resources
|
||||
this.car = _options.car
|
||||
this.walls = _options.walls
|
||||
this.objects = _options.objects
|
||||
this.materials = _options.materials
|
||||
this.areas = _options.areas
|
||||
this.config = _options.config
|
||||
this.physics = _options.physics
|
||||
|
||||
this.container = new THREE.Object3D()
|
||||
this.container.matrixAutoUpdate = false
|
||||
|
||||
this.setKonamiCode()
|
||||
this.setWigs()
|
||||
// this.setEggs()
|
||||
}
|
||||
|
||||
setKonamiCode()
|
||||
{
|
||||
this.konamiCode = {}
|
||||
this.konamiCode.x = - 60
|
||||
this.konamiCode.y = - 100
|
||||
this.konamiCode.sequence = ['ArrowUp', 'ArrowUp', 'ArrowDown', 'ArrowDown', 'ArrowLeft', 'ArrowRight', 'ArrowLeft', 'ArrowRight']
|
||||
|
||||
if(!this.config.touch)
|
||||
{
|
||||
this.konamiCode.sequence.push('b', 'a')
|
||||
}
|
||||
|
||||
this.konamiCode.keyIndex = 0
|
||||
this.konamiCode.latestKeys = []
|
||||
this.konamiCode.count = 0
|
||||
|
||||
// Label
|
||||
if(this.config.touch)
|
||||
{
|
||||
this.konamiCode.labelTexture = this.resources.items.konamiLabelTouchTexture
|
||||
}
|
||||
else
|
||||
{
|
||||
this.konamiCode.labelTexture = this.resources.items.konamiLabelTexture
|
||||
}
|
||||
|
||||
this.konamiCode.labelTexture.magFilter = THREE.NearestFilter
|
||||
this.konamiCode.labelTexture.minFilter = THREE.LinearFilter
|
||||
this.konamiCode.label = new THREE.Mesh(new THREE.PlaneBufferGeometry(8, 8 / 16), new THREE.MeshBasicMaterial({ transparent: true, depthWrite: false, color: 0xffffff, alphaMap: this.konamiCode.labelTexture }))
|
||||
this.konamiCode.label.position.x = this.konamiCode.x + 5
|
||||
this.konamiCode.label.position.y = this.konamiCode.y
|
||||
this.konamiCode.label.matrixAutoUpdate = false
|
||||
this.konamiCode.label.updateMatrix()
|
||||
this.container.add(this.konamiCode.label)
|
||||
|
||||
// Lemon option
|
||||
this.konamiCode.lemonOption = {
|
||||
base: this.resources.items.lemonBase.scene,
|
||||
collision: this.resources.items.lemonCollision.scene,
|
||||
offset: new THREE.Vector3(0, 0, 0),
|
||||
rotation: new THREE.Euler(Math.PI * 0.5, - Math.PI * 0.3, 0),
|
||||
duplicated: true,
|
||||
shadow: { sizeX: 1.2, sizeY: 1.8, offsetZ: - 0.15, alpha: 0.35 },
|
||||
mass: 0.5,
|
||||
sleep: true,
|
||||
soundName: 'woodHit'
|
||||
}
|
||||
|
||||
// First lemon
|
||||
this.objects.add({
|
||||
...this.konamiCode.lemonOption,
|
||||
offset: new THREE.Vector3(this.konamiCode.x, this.konamiCode.y, 0.4)
|
||||
})
|
||||
|
||||
this.konamiCode.testInput = (_input) =>
|
||||
{
|
||||
this.konamiCode.latestKeys.push(_input)
|
||||
|
||||
if(this.konamiCode.latestKeys.length > this.konamiCode.sequence.length)
|
||||
{
|
||||
this.konamiCode.latestKeys.shift()
|
||||
}
|
||||
|
||||
if(this.konamiCode.sequence.toString() === this.konamiCode.latestKeys.toString())
|
||||
{
|
||||
this.konamiCode.count++
|
||||
|
||||
for(let i = 0; i < Math.pow(3, this.konamiCode.count); i++)
|
||||
{
|
||||
window.setTimeout(() =>
|
||||
{
|
||||
const x = this.car.chassis.object.position.x + (Math.random() - 0.5) * 10
|
||||
const y = this.car.chassis.object.position.y + (Math.random() - 0.5) * 10
|
||||
|
||||
this.objects.add({
|
||||
...this.konamiCode.lemonOption,
|
||||
offset: new THREE.Vector3(x, y, 10),
|
||||
rotation: new THREE.Euler(Math.random() * Math.PI * 2, Math.random() * Math.PI * 2, Math.random() * Math.PI * 2),
|
||||
sleep: false
|
||||
})
|
||||
// this.eggs.add({
|
||||
// offset: new THREE.Vector3(x, y, 10),
|
||||
// rotation: new THREE.Euler(Math.random() * Math.PI * 2, Math.random() * Math.PI * 2, Math.random() * Math.PI * 2),
|
||||
// material: this.materials.shades.items.yellow,
|
||||
// code: 'MjAyMWVnZ2N2b3V6ZXI=',
|
||||
// sleep: false
|
||||
// })
|
||||
}, i * 50)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Keyboard handling
|
||||
*/
|
||||
window.addEventListener('keydown', (_event) =>
|
||||
{
|
||||
this.konamiCode.testInput(_event.key)
|
||||
})
|
||||
|
||||
/**
|
||||
* Touch handling
|
||||
*/
|
||||
this.konamiCode.touch = {}
|
||||
this.konamiCode.touch.x = 0
|
||||
this.konamiCode.touch.y = 0
|
||||
|
||||
this.konamiCode.touch.touchstart = (_event) =>
|
||||
{
|
||||
window.addEventListener('touchend', this.konamiCode.touch.touchend)
|
||||
|
||||
this.konamiCode.touch.x = _event.changedTouches[0].clientX
|
||||
this.konamiCode.touch.y = _event.changedTouches[0].clientY
|
||||
}
|
||||
this.konamiCode.touch.touchend = (_event) =>
|
||||
{
|
||||
window.removeEventListener('touchend', this.konamiCode.touch.touchend)
|
||||
|
||||
const endX = _event.changedTouches[0].clientX
|
||||
const endY = _event.changedTouches[0].clientY
|
||||
const deltaX = endX - this.konamiCode.touch.x
|
||||
const deltaY = endY - this.konamiCode.touch.y
|
||||
const distance = Math.hypot(deltaX, deltaY)
|
||||
|
||||
if(distance > 30)
|
||||
{
|
||||
const angle = Math.atan2(deltaY, deltaX)
|
||||
let direction = null
|
||||
|
||||
if(angle < - Math.PI * 0.75)
|
||||
{
|
||||
direction = 'ArrowLeft'
|
||||
}
|
||||
else if(angle < - Math.PI * 0.25)
|
||||
{
|
||||
direction = 'ArrowUp'
|
||||
}
|
||||
else if(angle < Math.PI * 0.25)
|
||||
{
|
||||
direction = 'ArrowRight'
|
||||
}
|
||||
else if(angle < Math.PI * 0.75)
|
||||
{
|
||||
direction = 'ArrowDown'
|
||||
}
|
||||
else
|
||||
{
|
||||
direction = 'ArrowLeft'
|
||||
}
|
||||
|
||||
this.konamiCode.testInput(direction)
|
||||
}
|
||||
}
|
||||
window.addEventListener('touchstart', this.konamiCode.touch.touchstart)
|
||||
}
|
||||
|
||||
setWigs()
|
||||
{
|
||||
this.wigs = {}
|
||||
this.wigs.currentWig = null
|
||||
|
||||
// Container
|
||||
this.wigs.container = new THREE.Object3D()
|
||||
this.wigs.container.position.x = - 0.1
|
||||
this.wigs.container.position.y = - 30
|
||||
this.wigs.container.matrixAutoUpdate = false
|
||||
this.wigs.container.updateMatrix()
|
||||
this.container.add(this.wigs.container)
|
||||
|
||||
// Materials
|
||||
this.wigs.materials = [
|
||||
this.materials.shades.items.green,
|
||||
this.materials.shades.items.red,
|
||||
this.materials.shades.items.emeraldGreen,
|
||||
this.materials.shades.items.purple,
|
||||
this.materials.shades.items.yellow,
|
||||
this.materials.shades.items.white
|
||||
]
|
||||
|
||||
// List
|
||||
this.wigs.list = [
|
||||
this.resources.items.wig1,
|
||||
this.resources.items.wig2,
|
||||
this.resources.items.wig3,
|
||||
this.resources.items.wig4
|
||||
]
|
||||
|
||||
// Items
|
||||
this.wigs.items = []
|
||||
|
||||
for(const _wig of this.wigs.list)
|
||||
{
|
||||
const container = new THREE.Object3D()
|
||||
container.visible = false
|
||||
container.matrixAutoUpdate = false
|
||||
this.wigs.container.add(container)
|
||||
|
||||
const children = [..._wig.scene.children]
|
||||
for(const _mesh of children)
|
||||
{
|
||||
_mesh.material = this.wigs.materials[0]
|
||||
container.add(_mesh)
|
||||
}
|
||||
|
||||
this.wigs.items.push(container)
|
||||
}
|
||||
|
||||
// Change
|
||||
this.wigs.change = () =>
|
||||
{
|
||||
// Hide previous wig
|
||||
if(this.wigs.currentWig)
|
||||
{
|
||||
this.wigs.currentWig.visible = false
|
||||
}
|
||||
|
||||
// Set random wig
|
||||
let randomWig = null
|
||||
do
|
||||
{
|
||||
randomWig = this.wigs.items[Math.floor(Math.random() * this.wigs.items.length)]
|
||||
} while(this.wigs.currentWig === randomWig)
|
||||
|
||||
this.wigs.currentWig = randomWig
|
||||
this.wigs.currentWig.visible = true
|
||||
|
||||
// Set random material
|
||||
const randomMaterial = this.wigs.materials[Math.floor(Math.random() * this.wigs.materials.length)]
|
||||
|
||||
for(const _mesh of this.wigs.currentWig.children)
|
||||
{
|
||||
_mesh.material = randomMaterial
|
||||
}
|
||||
|
||||
// this.eggs.add({
|
||||
// offset: new THREE.Vector3(0, 80, 10),
|
||||
// material: this.materials.shades.items.metal,
|
||||
// code: 'MjAyMWVnZ2F6ZW9jYmI=',
|
||||
// sleep: false
|
||||
// })
|
||||
}
|
||||
|
||||
// Area
|
||||
this.wigs.area = this.areas.add({
|
||||
position: new THREE.Vector2(0, 80),
|
||||
halfExtents: new THREE.Vector2(2, 2)
|
||||
})
|
||||
this.wigs.area.on('interact', this.wigs.change)
|
||||
|
||||
// Label
|
||||
this.resources.items.areaQuestionMarkTexture.magFilter = THREE.NearestFilter
|
||||
this.resources.items.areaQuestionMarkTexture.minFilter = THREE.LinearFilter
|
||||
this.wigs.areaLabel = new THREE.Mesh(new THREE.PlaneBufferGeometry(1, 1), new THREE.MeshBasicMaterial({ transparent: true, depthWrite: false, color: 0xffffff, alphaMap: this.resources.items.areaQuestionMarkTexture }))
|
||||
this.wigs.areaLabel.position.x = 0
|
||||
this.wigs.areaLabel.position.y = 80
|
||||
this.wigs.areaLabel.matrixAutoUpdate = false
|
||||
this.wigs.areaLabel.updateMatrix()
|
||||
this.container.add(this.wigs.areaLabel)
|
||||
}
|
||||
|
||||
setEggs()
|
||||
{
|
||||
this.eggs = {}
|
||||
this.eggs.items = []
|
||||
|
||||
// Console
|
||||
console.log('🥚 2021eggbvpoabe')
|
||||
|
||||
// Base eggs options
|
||||
const eggOptions = [
|
||||
{
|
||||
offset: new THREE.Vector3(- 29.80, - 18.94, 0.5),
|
||||
material: this.materials.shades.items.emeraldGreen,
|
||||
code: 'MjAyMWVnZ2Fvem5kZXo='
|
||||
},
|
||||
{
|
||||
offset: new THREE.Vector3(103.91, 128.56, 0.5),
|
||||
material: this.materials.shades.items.red,
|
||||
code: 'MjAyMWVnZ3Fxc3ZwcG8='
|
||||
},
|
||||
{
|
||||
offset: new THREE.Vector3(39.68, -23.67, 0.5),
|
||||
material: this.materials.shades.items.purple,
|
||||
code: 'MjAyMWVnZ212b2Focnc='
|
||||
},
|
||||
{
|
||||
offset: new THREE.Vector3(107.62, -155.75, 0.5),
|
||||
material: this.materials.shades.items.blue,
|
||||
code: 'MjAyMWVnZ2N1ZHBhaW4='
|
||||
},
|
||||
]
|
||||
|
||||
this.eggs.add = (_options) =>
|
||||
{
|
||||
const egg = {}
|
||||
egg.found = false
|
||||
egg.code = _options.code
|
||||
|
||||
// Create object
|
||||
egg.object = this.objects.add({
|
||||
base: this.resources.items.eggBase.scene,
|
||||
collision: this.resources.items.eggCollision.scene,
|
||||
duplicated: true,
|
||||
shadow: { sizeX: 1.2, sizeY: 1.8, offsetZ: - 0.15, alpha: 0.35 },
|
||||
mass: 0.5,
|
||||
sleep: typeof _options.sleep !== 'undefined' ? _options.sleep : true,
|
||||
soundName: 'woodHit',
|
||||
offset: _options.offset,
|
||||
rotation: typeof _options.sleep !== 'undefined' ? _options.rotation : new THREE.Euler(0, 0, 0)
|
||||
})
|
||||
|
||||
// Change material
|
||||
egg.object.container.children[0].material = _options.material
|
||||
|
||||
// Collision callback
|
||||
egg.collisionCallback = (_event) =>
|
||||
{
|
||||
// Collision with car
|
||||
if(_event.body === this.physics.car.chassis.body && !egg.found)
|
||||
{
|
||||
egg.found = true
|
||||
|
||||
// egg.object.collision.body.removeEventListener('collide', egg.collisionCallback)
|
||||
|
||||
const code = atob(egg.code)
|
||||
|
||||
window.setTimeout(() =>
|
||||
{
|
||||
if(window.confirm(`
|
||||
You find an egg!
|
||||
Here is your code for a 30% discount on https://threejs-journey.xyz
|
||||
${code}
|
||||
|
||||
Would you like to go on the subscription page?
|
||||
`))
|
||||
{
|
||||
window.open(`https://threejs-journey.xyz/subscribe/${code}`, '_blank')
|
||||
}
|
||||
|
||||
window.setTimeout(() =>
|
||||
{
|
||||
egg.found = false
|
||||
}, 1000)
|
||||
}, 600)
|
||||
}
|
||||
}
|
||||
|
||||
// Listen to collide event
|
||||
egg.object.collision.body.addEventListener('collide', egg.collisionCallback)
|
||||
|
||||
// Save
|
||||
this.eggs.items.push(egg)
|
||||
}
|
||||
|
||||
// Create base eggs
|
||||
for(const _eggOption of eggOptions)
|
||||
{
|
||||
this.eggs.add(_eggOption)
|
||||
}
|
||||
}
|
||||
}
|
70
src/javascript/World/Floor.js
Normal file
@ -0,0 +1,70 @@
|
||||
import * as THREE from 'three'
|
||||
import FloorMaterial from '../Materials/Floor.js'
|
||||
|
||||
export default class Floor
|
||||
{
|
||||
constructor(_options)
|
||||
{
|
||||
// Options
|
||||
this.debug = _options.debug
|
||||
|
||||
// Container
|
||||
this.container = new THREE.Object3D()
|
||||
this.container.matrixAutoUpdate = false
|
||||
|
||||
// Geometry
|
||||
this.geometry = new THREE.PlaneBufferGeometry(2, 2, 10, 10)
|
||||
|
||||
// Colors
|
||||
this.colors = {}
|
||||
this.colors.topLeft = '#f5883c'
|
||||
this.colors.topRight = '#ff9043'
|
||||
this.colors.bottomRight = '#fccf92'
|
||||
this.colors.bottomLeft = '#f5aa58'
|
||||
|
||||
// Material
|
||||
this.material = new FloorMaterial()
|
||||
|
||||
this.updateMaterial = () =>
|
||||
{
|
||||
const topLeft = new THREE.Color(this.colors.topLeft)
|
||||
const topRight = new THREE.Color(this.colors.topRight)
|
||||
const bottomRight = new THREE.Color(this.colors.bottomRight)
|
||||
const bottomLeft = new THREE.Color(this.colors.bottomLeft)
|
||||
|
||||
const data = new Uint8Array([
|
||||
Math.round(bottomLeft.r * 255), Math.round(bottomLeft.g * 255), Math.round(bottomLeft.b * 255),
|
||||
Math.round(bottomRight.r * 255), Math.round(bottomRight.g * 255), Math.round(bottomRight.b * 255),
|
||||
Math.round(topLeft.r * 255), Math.round(topLeft.g * 255), Math.round(topLeft.b * 255),
|
||||
Math.round(topRight.r * 255), Math.round(topRight.g * 255), Math.round(topRight.b * 255)
|
||||
])
|
||||
|
||||
this.backgroundTexture = new THREE.DataTexture(data, 2, 2, THREE.RGBFormat)
|
||||
this.backgroundTexture.magFilter = THREE.LinearFilter
|
||||
this.backgroundTexture.needsUpdate = true
|
||||
|
||||
this.material.uniforms.tBackground.value = this.backgroundTexture
|
||||
}
|
||||
|
||||
this.updateMaterial()
|
||||
|
||||
// Mesh
|
||||
this.mesh = new THREE.Mesh(this.geometry, this.material)
|
||||
this.mesh.frustumCulled = false
|
||||
this.mesh.matrixAutoUpdate = false
|
||||
this.mesh.updateMatrix()
|
||||
this.container.add(this.mesh)
|
||||
|
||||
// Debug
|
||||
if(this.debug)
|
||||
{
|
||||
const folder = this.debug.addFolder('floor')
|
||||
// folder.open()
|
||||
|
||||
folder.addColor(this.colors, 'topLeft').onChange(this.updateMaterial)
|
||||
folder.addColor(this.colors, 'topRight').onChange(this.updateMaterial)
|
||||
folder.addColor(this.colors, 'bottomRight').onChange(this.updateMaterial)
|
||||
folder.addColor(this.colors, 'bottomLeft').onChange(this.updateMaterial)
|
||||
}
|
||||
}
|
||||
}
|
216
src/javascript/World/Materials.js
Normal file
@ -0,0 +1,216 @@
|
||||
import * as THREE from 'three'
|
||||
import FloorShadowMaterial from '../Materials/FloorShadow.js'
|
||||
import MatcapMaterial from '../Materials/Matcap.js'
|
||||
|
||||
export default class Materials
|
||||
{
|
||||
constructor(_options)
|
||||
{
|
||||
// Options
|
||||
this.resources = _options.resources
|
||||
this.debug = _options.debug
|
||||
|
||||
// Debug
|
||||
if(this.debug)
|
||||
{
|
||||
this.debugFolder = this.debug.addFolder('materials')
|
||||
this.debugFolder.open()
|
||||
}
|
||||
|
||||
// Set up
|
||||
this.items = {}
|
||||
|
||||
this.setPures()
|
||||
this.setShades()
|
||||
this.setFloorShadow()
|
||||
}
|
||||
|
||||
setPures()
|
||||
{
|
||||
// Setup
|
||||
this.pures = {}
|
||||
this.pures.items = {}
|
||||
this.pures.items.red = new THREE.MeshBasicMaterial({ color: 0xff0000 })
|
||||
this.pures.items.red.name = 'pureRed'
|
||||
this.pures.items.white = new THREE.MeshBasicMaterial({ color: 0xffffff })
|
||||
this.pures.items.white.name = 'pureWhite'
|
||||
this.pures.items.yellow = new THREE.MeshBasicMaterial({ color: 0xffe889 })
|
||||
this.pures.items.yellow.name = 'pureYellow'
|
||||
}
|
||||
|
||||
setShades()
|
||||
{
|
||||
// Setup
|
||||
this.shades = {}
|
||||
this.shades.items = {}
|
||||
this.shades.indirectColor = '#d04500'
|
||||
|
||||
this.shades.uniforms = {
|
||||
uRevealProgress: 0,
|
||||
uIndirectDistanceAmplitude: 1.75,
|
||||
uIndirectDistanceStrength: 0.5,
|
||||
uIndirectDistancePower: 2.0,
|
||||
uIndirectAngleStrength: 1.5,
|
||||
uIndirectAngleOffset: 0.6,
|
||||
uIndirectAnglePower: 1.0,
|
||||
uIndirectColor: null
|
||||
}
|
||||
|
||||
// White
|
||||
this.shades.items.white = new MatcapMaterial()
|
||||
this.shades.items.white.name = 'shadeWhite'
|
||||
this.shades.items.white.uniforms.matcap.value = this.resources.items.matcapWhiteTexture
|
||||
this.items.white = this.shades.items.white
|
||||
|
||||
// Orange
|
||||
this.shades.items.orange = new MatcapMaterial()
|
||||
this.shades.items.orange.name = 'shadeOrange'
|
||||
this.shades.items.orange.uniforms.matcap.value = this.resources.items.matcapOrangeTexture
|
||||
this.items.orange = this.shades.items.orange
|
||||
|
||||
// Green
|
||||
this.shades.items.green = new MatcapMaterial()
|
||||
this.shades.items.green.name = 'shadeGreen'
|
||||
this.shades.items.green.uniforms.matcap.value = this.resources.items.matcapGreenTexture
|
||||
this.items.green = this.shades.items.green
|
||||
|
||||
// Brown
|
||||
this.shades.items.brown = new MatcapMaterial()
|
||||
this.shades.items.brown.name = 'shadeBrown'
|
||||
this.shades.items.brown.uniforms.matcap.value = this.resources.items.matcapBrownTexture
|
||||
this.items.brown = this.shades.items.brown
|
||||
|
||||
// Gray
|
||||
this.shades.items.gray = new MatcapMaterial()
|
||||
this.shades.items.gray.name = 'shadeGray'
|
||||
this.shades.items.gray.uniforms.matcap.value = this.resources.items.matcapGrayTexture
|
||||
this.items.gray = this.shades.items.gray
|
||||
|
||||
// Beige
|
||||
this.shades.items.beige = new MatcapMaterial()
|
||||
this.shades.items.beige.name = 'shadeBeige'
|
||||
this.shades.items.beige.uniforms.matcap.value = this.resources.items.matcapBeigeTexture
|
||||
this.items.beige = this.shades.items.beige
|
||||
|
||||
// Red
|
||||
this.shades.items.red = new MatcapMaterial()
|
||||
this.shades.items.red.name = 'shadeRed'
|
||||
this.shades.items.red.uniforms.matcap.value = this.resources.items.matcapRedTexture
|
||||
this.items.red = this.shades.items.red
|
||||
|
||||
// Black
|
||||
this.shades.items.black = new MatcapMaterial()
|
||||
this.shades.items.black.name = 'shadeBlack'
|
||||
this.shades.items.black.uniforms.matcap.value = this.resources.items.matcapBlackTexture
|
||||
this.items.black = this.shades.items.black
|
||||
|
||||
// Green emerald
|
||||
this.shades.items.emeraldGreen = new MatcapMaterial()
|
||||
this.shades.items.emeraldGreen.name = 'shadeEmeraldGreen'
|
||||
this.shades.items.emeraldGreen.uniforms.matcap.value = this.resources.items.matcapEmeraldGreenTexture
|
||||
this.items.emeraldGreen = this.shades.items.emeraldGreen
|
||||
|
||||
// Purple
|
||||
this.shades.items.purple = new MatcapMaterial()
|
||||
this.shades.items.purple.name = 'shadePurple'
|
||||
this.shades.items.purple.uniforms.matcap.value = this.resources.items.matcapPurpleTexture
|
||||
this.items.purple = this.shades.items.purple
|
||||
|
||||
// Blue
|
||||
this.shades.items.blue = new MatcapMaterial()
|
||||
this.shades.items.blue.name = 'shadeBlue'
|
||||
this.shades.items.blue.uniforms.matcap.value = this.resources.items.matcapBlueTexture
|
||||
this.items.blue = this.shades.items.blue
|
||||
|
||||
// Yellow
|
||||
this.shades.items.yellow = new MatcapMaterial()
|
||||
this.shades.items.yellow.name = 'shadeYellow'
|
||||
this.shades.items.yellow.uniforms.matcap.value = this.resources.items.matcapYellowTexture
|
||||
this.items.yellow = this.shades.items.yellow
|
||||
|
||||
// Metal
|
||||
this.shades.items.metal = new MatcapMaterial()
|
||||
this.shades.items.metal.name = 'shadeMetal'
|
||||
this.shades.items.metal.uniforms.matcap.value = this.resources.items.matcapMetalTexture
|
||||
this.items.metal = this.shades.items.metal
|
||||
|
||||
// // Gold
|
||||
// this.shades.items.gold = new MatcapMaterial()
|
||||
// this.shades.items.gold.name = 'shadeGold'
|
||||
// this.shades.items.gold.uniforms.matcap.value = this.resources.items.matcapGoldTexture
|
||||
// this.items.gold = this.shades.items.gold
|
||||
|
||||
// Update materials uniforms
|
||||
this.shades.updateMaterials = () =>
|
||||
{
|
||||
this.shades.uniforms.uIndirectColor = new THREE.Color(this.shades.indirectColor)
|
||||
|
||||
// Each uniform
|
||||
for(const _uniformName in this.shades.uniforms)
|
||||
{
|
||||
const _uniformValue = this.shades.uniforms[_uniformName]
|
||||
|
||||
// Each material
|
||||
for(const _materialKey in this.shades.items)
|
||||
{
|
||||
const material = this.shades.items[_materialKey]
|
||||
material.uniforms[_uniformName].value = _uniformValue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.shades.updateMaterials()
|
||||
|
||||
// Debug
|
||||
if(this.debug)
|
||||
{
|
||||
const folder = this.debugFolder.addFolder('shades')
|
||||
folder.open()
|
||||
|
||||
folder.add(this.shades.uniforms, 'uIndirectDistanceAmplitude').step(0.001).min(0).max(3).onChange(this.shades.updateMaterials)
|
||||
folder.add(this.shades.uniforms, 'uIndirectDistanceStrength').step(0.001).min(0).max(2).onChange(this.shades.updateMaterials)
|
||||
folder.add(this.shades.uniforms, 'uIndirectDistancePower').step(0.001).min(0).max(5).onChange(this.shades.updateMaterials)
|
||||
folder.add(this.shades.uniforms, 'uIndirectAngleStrength').step(0.001).min(0).max(2).onChange(this.shades.updateMaterials)
|
||||
folder.add(this.shades.uniforms, 'uIndirectAngleOffset').step(0.001).min(- 2).max(2).onChange(this.shades.updateMaterials)
|
||||
folder.add(this.shades.uniforms, 'uIndirectAnglePower').step(0.001).min(0).max(5).onChange(this.shades.updateMaterials)
|
||||
folder.addColor(this.shades, 'indirectColor').onChange(this.shades.updateMaterials)
|
||||
}
|
||||
}
|
||||
|
||||
setFloorShadow()
|
||||
{
|
||||
this.items.floorShadow = new FloorShadowMaterial()
|
||||
this.items.floorShadow.depthWrite = false
|
||||
this.items.floorShadow.shadowColor = '#d04500'
|
||||
this.items.floorShadow.uniforms.uShadowColor.value = new THREE.Color(this.items.floorShadow.shadowColor)
|
||||
this.items.floorShadow.uniforms.uAlpha.value = 0
|
||||
|
||||
this.items.floorShadow.updateMaterials = () =>
|
||||
{
|
||||
this.items.floorShadow.uniforms.uShadowColor.value = new THREE.Color(this.items.floorShadow.shadowColor)
|
||||
|
||||
for(const _item of this.objects.items)
|
||||
{
|
||||
for(const _child of _item.container.children)
|
||||
{
|
||||
if(_child.material instanceof THREE.ShaderMaterial)
|
||||
{
|
||||
if(_child.material.uniforms.uShadowColor)
|
||||
{
|
||||
_child.material.uniforms.uShadowColor.value = new THREE.Color(this.items.floorShadow.shadowColor)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Debug
|
||||
if(this.debug)
|
||||
{
|
||||
const folder = this.debugFolder.addFolder('floorShadow')
|
||||
folder.open()
|
||||
|
||||
folder.addColor(this.items.floorShadow, 'shadowColor').onChange(this.items.floorShadow.updateMaterials)
|
||||
}
|
||||
}
|
||||
}
|
353
src/javascript/World/Objects.js
Normal file
@ -0,0 +1,353 @@
|
||||
import * as THREE from 'three'
|
||||
import { BufferGeometryUtils } from 'three/examples/jsm/utils/BufferGeometryUtils.js'
|
||||
|
||||
export default class Objects
|
||||
{
|
||||
constructor(_options)
|
||||
{
|
||||
// Options
|
||||
this.time = _options.time
|
||||
this.resources = _options.resources
|
||||
this.materials = _options.materials
|
||||
this.physics = _options.physics
|
||||
this.shadows = _options.shadows
|
||||
this.sounds = _options.sounds
|
||||
this.debug = _options.debug
|
||||
|
||||
// Set up
|
||||
this.container = new THREE.Object3D()
|
||||
this.container.matrixAutoUpdate = false
|
||||
|
||||
this.items = []
|
||||
this.floorShadows = []
|
||||
|
||||
this.setParsers()
|
||||
this.setMerge()
|
||||
}
|
||||
|
||||
setParsers()
|
||||
{
|
||||
this.parsers = {}
|
||||
|
||||
this.parsers.items = [
|
||||
// Shade
|
||||
{
|
||||
regex: /^shade([a-z]+)_?[0-9]{0,3}?/i,
|
||||
apply: (_mesh, _options) =>
|
||||
{
|
||||
// Find material
|
||||
const match = _mesh.name.match(/^shade([a-z]+)_?[0-9]{0,3}?/i)
|
||||
const materialName = `${match[1].substring(0, 1).toLowerCase()}${match[1].substring(1)}` // PastalCase to camelCase
|
||||
let material = this.materials.shades.items[materialName]
|
||||
|
||||
// Default
|
||||
if(typeof material === 'undefined')
|
||||
{
|
||||
material = new THREE.MeshNormalMaterial()
|
||||
}
|
||||
|
||||
// Create clone mesh with new material
|
||||
const mesh = _options.duplicated ? _mesh.clone() : _mesh
|
||||
mesh.material = material
|
||||
|
||||
if(mesh.children.length)
|
||||
{
|
||||
for(const _child of mesh.children)
|
||||
{
|
||||
if(_child instanceof THREE.Mesh)
|
||||
{
|
||||
_child.material = material
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return mesh
|
||||
}
|
||||
},
|
||||
|
||||
// Shade
|
||||
{
|
||||
regex: /^pure([a-z]+)_?[0-9]{0,3}?/i,
|
||||
apply: (_mesh, _options) =>
|
||||
{
|
||||
// Find material
|
||||
const match = _mesh.name.match(/^pure([a-z]+)_?[0-9]{0,3}?/i)
|
||||
const materialName = match[1].toLowerCase()
|
||||
let material = this.materials.pures.items[materialName]
|
||||
|
||||
// Default
|
||||
if(typeof material === 'undefined')
|
||||
{
|
||||
material = new THREE.MeshNormalMaterial()
|
||||
}
|
||||
|
||||
// Create clone mesh with new material
|
||||
const mesh = _options.duplicated ? _mesh.clone() : _mesh
|
||||
mesh.material = material
|
||||
|
||||
return mesh
|
||||
}
|
||||
},
|
||||
|
||||
// Floor
|
||||
{
|
||||
regex: /^floor_?[0-9]{0,3}?/i,
|
||||
apply: (_mesh, _options) =>
|
||||
{
|
||||
// Create floor manually because of missing UV
|
||||
const geometry = new THREE.PlaneBufferGeometry(_mesh.scale.x, _mesh.scale.y, 10, 10)
|
||||
const material = this.materials.items.floorShadow.clone()
|
||||
|
||||
material.uniforms.tShadow.value = _options.floorShadowTexture
|
||||
material.uniforms.uShadowColor.value = new THREE.Color(this.materials.items.floorShadow.shadowColor)
|
||||
material.uniforms.uAlpha.value = 0
|
||||
|
||||
const mesh = new THREE.Mesh(geometry, material)
|
||||
mesh.matrixAutoUpdate = false
|
||||
mesh.updateMatrix()
|
||||
|
||||
this.floorShadows.push(mesh)
|
||||
|
||||
return mesh
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
// Default
|
||||
this.parsers.default = {}
|
||||
this.parsers.default.apply = (_mesh) =>
|
||||
{
|
||||
// Create clone mesh with normal material
|
||||
const mesh = _mesh.clone()
|
||||
mesh.material = this.materials.shades.items.white
|
||||
|
||||
return mesh
|
||||
}
|
||||
}
|
||||
|
||||
setMerge()
|
||||
{
|
||||
this.merge = {}
|
||||
this.merge.items = {}
|
||||
|
||||
this.merge.container = new THREE.Object3D()
|
||||
this.merge.container.matrixAutoUpdate = false
|
||||
this.container.add(this.merge.container)
|
||||
|
||||
this.merge.add = (_name, _mesh) =>
|
||||
{
|
||||
let mergeItem = this.merge.items[_name]
|
||||
|
||||
// Create merge item if not found
|
||||
if(!mergeItem)
|
||||
{
|
||||
mergeItem = {}
|
||||
|
||||
// Geometry
|
||||
mergeItem.geometry = new THREE.BufferGeometry()
|
||||
mergeItem.geometriesToMerge = []
|
||||
|
||||
// Material
|
||||
mergeItem.material = _mesh.material
|
||||
mergeItem.material.side = THREE.DoubleSide
|
||||
|
||||
// Mesh
|
||||
mergeItem.mesh = new THREE.Mesh(mergeItem.geometry, mergeItem.material)
|
||||
this.merge.container.add(mergeItem.mesh)
|
||||
|
||||
// Save
|
||||
this.merge.items[_name] = mergeItem
|
||||
}
|
||||
|
||||
// Apply the object transform to the geometry and save it for later merge
|
||||
const geometry = _mesh.geometry
|
||||
_mesh.updateMatrixWorld() // Maybe not
|
||||
geometry.applyMatrix(_mesh.matrixWorld)
|
||||
|
||||
mergeItem.geometriesToMerge.push(geometry)
|
||||
}
|
||||
|
||||
this.merge.applyMerge = () =>
|
||||
{
|
||||
for(const _mergeItemName in this.merge.items)
|
||||
{
|
||||
const mergeItem = this.merge.items[_mergeItemName]
|
||||
|
||||
mergeItem.geometry = BufferGeometryUtils.mergeBufferGeometries(mergeItem.geometriesToMerge) // Should add original geometry
|
||||
mergeItem.mesh.geometry = mergeItem.geometry
|
||||
}
|
||||
}
|
||||
|
||||
this.merge.update = () =>
|
||||
{
|
||||
for(const _object of this.items)
|
||||
{
|
||||
if(_object.shouldMerge)
|
||||
{
|
||||
const children = [..._object.container.children]
|
||||
for(const _child of children)
|
||||
{
|
||||
const materialName = _child.material.name
|
||||
if(materialName !== '')
|
||||
{
|
||||
this.merge.add(materialName, _child)
|
||||
|
||||
// Remove from parent
|
||||
_object.container.remove(_child)
|
||||
}
|
||||
}
|
||||
|
||||
// If no children, remove
|
||||
|
||||
_object.shouldMerge = false
|
||||
}
|
||||
}
|
||||
|
||||
// Apply merge
|
||||
this.merge.applyMerge()
|
||||
}
|
||||
}
|
||||
|
||||
getConvertedMesh(_children, _options = {})
|
||||
{
|
||||
const container = new THREE.Object3D()
|
||||
const center = new THREE.Vector3()
|
||||
|
||||
// Go through each base child
|
||||
const baseChildren = [..._children]
|
||||
|
||||
for(const _child of baseChildren)
|
||||
{
|
||||
// Find center
|
||||
if(_child.name.match(/^center_?[0-9]{0,3}?/i))
|
||||
{
|
||||
center.set(_child.position.x, _child.position.y, _child.position.z)
|
||||
}
|
||||
|
||||
if(_child instanceof THREE.Mesh)
|
||||
{
|
||||
// Find parser and use default if not found
|
||||
let parser = this.parsers.items.find((_item) => _child.name.match(_item.regex))
|
||||
if(typeof parser === 'undefined')
|
||||
{
|
||||
parser = this.parsers.default
|
||||
}
|
||||
|
||||
// Create mesh by applying parser
|
||||
const mesh = parser.apply(_child, _options)
|
||||
|
||||
// Add to container
|
||||
container.add(mesh)
|
||||
}
|
||||
}
|
||||
|
||||
// Recenter
|
||||
if(center.length() > 0)
|
||||
{
|
||||
for(const _child of container.children)
|
||||
{
|
||||
_child.position.sub(center)
|
||||
}
|
||||
|
||||
container.position.add(center)
|
||||
}
|
||||
|
||||
if(_options.mass && _options.mass === 0)
|
||||
{
|
||||
container.matrixAutoUpdate = false
|
||||
container.updateMatrix()
|
||||
}
|
||||
|
||||
return container
|
||||
}
|
||||
|
||||
add(_options)
|
||||
{
|
||||
const object = {}
|
||||
|
||||
object.merged = false
|
||||
object.shouldMerge = _options.mass === 0
|
||||
|
||||
// Offset
|
||||
const offset = new THREE.Vector3()
|
||||
if(_options.offset)
|
||||
{
|
||||
offset.copy(_options.offset)
|
||||
}
|
||||
|
||||
// Rotation
|
||||
const rotation = new THREE.Euler()
|
||||
if(_options.rotation)
|
||||
{
|
||||
rotation.copy(_options.rotation)
|
||||
}
|
||||
|
||||
// Sleep
|
||||
const sleep = typeof _options.sleep === 'undefined' ? true : _options.sleep
|
||||
|
||||
// Container
|
||||
object.container = this.getConvertedMesh(_options.base.children, _options)
|
||||
object.container.position.copy(offset)
|
||||
object.container.rotation.copy(rotation)
|
||||
this.container.add(object.container)
|
||||
|
||||
// Deactivate matrix auto update
|
||||
if(_options.mass === 0)
|
||||
{
|
||||
object.container.matrixAutoUpdate = false
|
||||
object.container.updateMatrix()
|
||||
|
||||
for(const _child of object.container.children)
|
||||
{
|
||||
_child.matrixAutoUpdate = false
|
||||
_child.updateMatrix()
|
||||
}
|
||||
}
|
||||
|
||||
// Create physics object
|
||||
object.collision = this.physics.addObjectFromThree({
|
||||
meshes: [..._options.collision.children],
|
||||
offset,
|
||||
rotation,
|
||||
mass: _options.mass,
|
||||
sleep
|
||||
})
|
||||
|
||||
for(const _child of object.container.children)
|
||||
{
|
||||
_child.position.sub(object.collision.center)
|
||||
}
|
||||
|
||||
// Sound
|
||||
if(_options.soundName)
|
||||
{
|
||||
object.collision.body.addEventListener('collide', (_event) =>
|
||||
{
|
||||
const relativeVelocity = _event.contact.getImpactVelocityAlongNormal()
|
||||
this.sounds.play(_options.soundName, relativeVelocity)
|
||||
})
|
||||
}
|
||||
|
||||
// Shadow
|
||||
// Add shadow
|
||||
if(_options.shadow)
|
||||
{
|
||||
this.shadows.add(object.container, _options.shadow)
|
||||
}
|
||||
|
||||
// Time tick event
|
||||
if(_options.mass > 0)
|
||||
{
|
||||
this.time.on('tick', () =>
|
||||
{
|
||||
object.container.position.copy(object.collision.body.position)
|
||||
object.container.quaternion.copy(object.collision.body.quaternion)
|
||||
})
|
||||
}
|
||||
|
||||
// Save
|
||||
this.items.push(object)
|
||||
|
||||
return object
|
||||
}
|
||||
}
|
825
src/javascript/World/Physics.js
Normal file
@ -0,0 +1,825 @@
|
||||
import CANNON from 'cannon'
|
||||
import * as THREE from 'three'
|
||||
|
||||
export default class Physics
|
||||
{
|
||||
constructor(_options)
|
||||
{
|
||||
this.config = _options.config
|
||||
this.debug = _options.debug
|
||||
this.time = _options.time
|
||||
this.sizes = _options.sizes
|
||||
this.controls = _options.controls
|
||||
this.sounds = _options.sounds
|
||||
|
||||
// Set up
|
||||
if(this.debug)
|
||||
{
|
||||
this.debugFolder = this.debug.addFolder('physics')
|
||||
// this.debugFolder.open()
|
||||
}
|
||||
|
||||
this.setWorld()
|
||||
this.setModels()
|
||||
this.setMaterials()
|
||||
this.setFloor()
|
||||
this.setCar()
|
||||
|
||||
this.time.on('tick', () =>
|
||||
{
|
||||
this.world.step(1 / 60, this.time.delta, 3)
|
||||
})
|
||||
}
|
||||
|
||||
setWorld()
|
||||
{
|
||||
this.world = new CANNON.World()
|
||||
this.world.gravity.set(0, 0, - 3.25)
|
||||
this.world.allowSleep = true
|
||||
// this.world.gravity.set(0, 0, 0)
|
||||
// this.world.broadphase = new CANNON.SAPBroadphase(this.world)
|
||||
this.world.defaultContactMaterial.friction = 0
|
||||
this.world.defaultContactMaterial.restitution = 0.2
|
||||
|
||||
// Debug
|
||||
if(this.debug)
|
||||
{
|
||||
this.debugFolder.add(this.world.gravity, 'z').step(0.001).min(- 20).max(20).name('gravity')
|
||||
}
|
||||
}
|
||||
|
||||
setModels()
|
||||
{
|
||||
this.models = {}
|
||||
this.models.container = new THREE.Object3D()
|
||||
this.models.container.visible = false
|
||||
this.models.materials = {}
|
||||
this.models.materials.static = new THREE.MeshBasicMaterial({ color: 0x0000ff, wireframe: true })
|
||||
this.models.materials.dynamic = new THREE.MeshBasicMaterial({ color: 0xff0000, wireframe: true })
|
||||
this.models.materials.dynamicSleeping = new THREE.MeshBasicMaterial({ color: 0xffff00, wireframe: true })
|
||||
|
||||
// Debug
|
||||
if(this.debug)
|
||||
{
|
||||
this.debugFolder.add(this.models.container, 'visible').name('modelsVisible')
|
||||
}
|
||||
}
|
||||
|
||||
setMaterials()
|
||||
{
|
||||
this.materials = {}
|
||||
|
||||
// All materials
|
||||
this.materials.items = {}
|
||||
this.materials.items.floor = new CANNON.Material('floorMaterial')
|
||||
this.materials.items.dummy = new CANNON.Material('dummyMaterial')
|
||||
this.materials.items.wheel = new CANNON.Material('wheelMaterial')
|
||||
|
||||
// Contact between materials
|
||||
this.materials.contacts = {}
|
||||
|
||||
this.materials.contacts.floorDummy = new CANNON.ContactMaterial(this.materials.items.floor, this.materials.items.dummy, { friction: 0.05, restitution: 0.3, contactEquationStiffness: 1000 })
|
||||
this.world.addContactMaterial(this.materials.contacts.floorDummy)
|
||||
|
||||
this.materials.contacts.dummyDummy = new CANNON.ContactMaterial(this.materials.items.dummy, this.materials.items.dummy, { friction: 0.5, restitution: 0.3, contactEquationStiffness: 1000 })
|
||||
this.world.addContactMaterial(this.materials.contacts.dummyDummy)
|
||||
|
||||
this.materials.contacts.floorWheel = new CANNON.ContactMaterial(this.materials.items.floor, this.materials.items.wheel, { friction: 0.3, restitution: 0, contactEquationStiffness: 1000 })
|
||||
this.world.addContactMaterial(this.materials.contacts.floorWheel)
|
||||
}
|
||||
|
||||
setFloor()
|
||||
{
|
||||
this.floor = {}
|
||||
this.floor.body = new CANNON.Body({
|
||||
mass: 0,
|
||||
shape: new CANNON.Plane(),
|
||||
material: this.materials.items.floor
|
||||
})
|
||||
|
||||
// this.floor.body.quaternion.setFromAxisAngle(new CANNON.Vec3(1, 0, 0), - Math.PI * 0.5)
|
||||
|
||||
this.world.addBody(this.floor.body)
|
||||
}
|
||||
|
||||
setCar()
|
||||
{
|
||||
this.car = {}
|
||||
|
||||
this.car.steering = 0
|
||||
this.car.accelerating = 0
|
||||
this.car.speed = 0
|
||||
this.car.worldForward = new CANNON.Vec3()
|
||||
this.car.angle = 0
|
||||
this.car.forwardSpeed = 0
|
||||
this.car.oldPosition = new CANNON.Vec3()
|
||||
this.car.goingForward = true
|
||||
|
||||
/**
|
||||
* Options
|
||||
*/
|
||||
this.car.options = {}
|
||||
this.car.options.chassisWidth = 1.02
|
||||
this.car.options.chassisHeight = 1.16
|
||||
this.car.options.chassisDepth = 2.03
|
||||
this.car.options.chassisOffset = new CANNON.Vec3(0, 0, 0.41)
|
||||
this.car.options.chassisMass = 20
|
||||
this.car.options.wheelFrontOffsetDepth = 0.635
|
||||
this.car.options.wheelBackOffsetDepth = - 0.475
|
||||
this.car.options.wheelOffsetWidth = 0.39
|
||||
this.car.options.wheelRadius = 0.25
|
||||
this.car.options.wheelHeight = 0.24
|
||||
this.car.options.wheelSuspensionStiffness = 25
|
||||
this.car.options.wheelSuspensionRestLength = 0.1
|
||||
this.car.options.wheelFrictionSlip = 5
|
||||
this.car.options.wheelDampingRelaxation = 1.8
|
||||
this.car.options.wheelDampingCompression = 1.5
|
||||
this.car.options.wheelMaxSuspensionForce = 100000
|
||||
this.car.options.wheelRollInfluence = 0.01
|
||||
this.car.options.wheelMaxSuspensionTravel = 0.3
|
||||
this.car.options.wheelCustomSlidingRotationalSpeed = - 30
|
||||
this.car.options.wheelMass = 5
|
||||
this.car.options.controlsSteeringSpeed = 0.005
|
||||
this.car.options.controlsSteeringMax = Math.PI * 0.17
|
||||
this.car.options.controlsSteeringQuad = false
|
||||
this.car.options.controlsAcceleratinMaxSpeed = 0.055
|
||||
this.car.options.controlsAcceleratinMaxSpeedBoost = 0.11
|
||||
this.car.options.controlsAcceleratingSpeed = 2
|
||||
this.car.options.controlsAcceleratingSpeedBoost = 3.5
|
||||
this.car.options.controlsAcceleratingQuad = true
|
||||
this.car.options.controlsBrakeStrength = 0.45
|
||||
|
||||
/**
|
||||
* Upsize down
|
||||
*/
|
||||
this.car.upsideDown = {}
|
||||
this.car.upsideDown.state = 'watching' // 'wathing' | 'pending' | 'turning'
|
||||
this.car.upsideDown.pendingTimeout = null
|
||||
this.car.upsideDown.turningTimeout = null
|
||||
|
||||
/**
|
||||
* Jump
|
||||
*/
|
||||
this.car.jump = (_toReturn = true, _strength = 60) =>
|
||||
{
|
||||
let worldPosition = this.car.chassis.body.position
|
||||
worldPosition = worldPosition.vadd(new CANNON.Vec3(_toReturn ? 0.08 : 0, 0, 0))
|
||||
this.car.chassis.body.applyImpulse(new CANNON.Vec3(0, 0, _strength), worldPosition)
|
||||
}
|
||||
|
||||
/**
|
||||
* Create method
|
||||
*/
|
||||
this.car.create = () =>
|
||||
{
|
||||
/**
|
||||
* Chassis
|
||||
*/
|
||||
this.car.chassis = {}
|
||||
|
||||
this.car.chassis.shape = new CANNON.Box(new CANNON.Vec3(this.car.options.chassisDepth * 0.5, this.car.options.chassisWidth * 0.5, this.car.options.chassisHeight * 0.5))
|
||||
|
||||
this.car.chassis.body = new CANNON.Body({ mass: this.car.options.chassisMass })
|
||||
this.car.chassis.body.allowSleep = false
|
||||
this.car.chassis.body.position.set(0, 0, 12)
|
||||
this.car.chassis.body.sleep()
|
||||
this.car.chassis.body.addShape(this.car.chassis.shape, this.car.options.chassisOffset)
|
||||
this.car.chassis.body.quaternion.setFromAxisAngle(new CANNON.Vec3(0, 0, 1), - Math.PI * 0.5)
|
||||
|
||||
/**
|
||||
* Sound
|
||||
*/
|
||||
this.car.chassis.body.addEventListener('collide', (_event) =>
|
||||
{
|
||||
if(_event.body.mass === 0)
|
||||
{
|
||||
const relativeVelocity = _event.contact.getImpactVelocityAlongNormal()
|
||||
this.sounds.play('carHit', relativeVelocity)
|
||||
}
|
||||
})
|
||||
|
||||
/**
|
||||
* Vehicle
|
||||
*/
|
||||
this.car.vehicle = new CANNON.RaycastVehicle({
|
||||
chassisBody: this.car.chassis.body
|
||||
})
|
||||
|
||||
/**
|
||||
* Wheel
|
||||
*/
|
||||
this.car.wheels = {}
|
||||
this.car.wheels.options = {
|
||||
radius: this.car.options.wheelRadius,
|
||||
height: this.car.options.wheelHeight,
|
||||
suspensionStiffness: this.car.options.wheelSuspensionStiffness,
|
||||
suspensionRestLength: this.car.options.wheelSuspensionRestLength,
|
||||
frictionSlip: this.car.options.wheelFrictionSlip,
|
||||
dampingRelaxation: this.car.options.wheelDampingRelaxation,
|
||||
dampingCompression: this.car.options.wheelDampingCompression,
|
||||
maxSuspensionForce: this.car.options.wheelMaxSuspensionForce,
|
||||
rollInfluence: this.car.options.wheelRollInfluence,
|
||||
maxSuspensionTravel: this.car.options.wheelMaxSuspensionTravel,
|
||||
customSlidingRotationalSpeed: this.car.options.wheelCustomSlidingRotationalSpeed,
|
||||
useCustomSlidingRotationalSpeed: true,
|
||||
directionLocal: new CANNON.Vec3(0, 0, - 1),
|
||||
axleLocal: new CANNON.Vec3(0, 1, 0),
|
||||
chassisConnectionPointLocal: new CANNON.Vec3(1, 1, 0) // Will be changed for each wheel
|
||||
}
|
||||
|
||||
// Front left
|
||||
this.car.wheels.options.chassisConnectionPointLocal.set(this.car.options.wheelFrontOffsetDepth, this.car.options.wheelOffsetWidth, 0)
|
||||
this.car.vehicle.addWheel(this.car.wheels.options)
|
||||
|
||||
// Front right
|
||||
this.car.wheels.options.chassisConnectionPointLocal.set(this.car.options.wheelFrontOffsetDepth, - this.car.options.wheelOffsetWidth, 0)
|
||||
this.car.vehicle.addWheel(this.car.wheels.options)
|
||||
|
||||
// Back left
|
||||
this.car.wheels.options.chassisConnectionPointLocal.set(this.car.options.wheelBackOffsetDepth, this.car.options.wheelOffsetWidth, 0)
|
||||
this.car.vehicle.addWheel(this.car.wheels.options)
|
||||
|
||||
// Back right
|
||||
this.car.wheels.options.chassisConnectionPointLocal.set(this.car.options.wheelBackOffsetDepth, - this.car.options.wheelOffsetWidth, 0)
|
||||
this.car.vehicle.addWheel(this.car.wheels.options)
|
||||
|
||||
this.car.vehicle.addToWorld(this.world)
|
||||
|
||||
this.car.wheels.indexes = {}
|
||||
|
||||
this.car.wheels.indexes.frontLeft = 0
|
||||
this.car.wheels.indexes.frontRight = 1
|
||||
this.car.wheels.indexes.backLeft = 2
|
||||
this.car.wheels.indexes.backRight = 3
|
||||
this.car.wheels.bodies = []
|
||||
|
||||
for(const _wheelInfos of this.car.vehicle.wheelInfos)
|
||||
{
|
||||
const shape = new CANNON.Cylinder(_wheelInfos.radius, _wheelInfos.radius, this.car.wheels.options.height, 20)
|
||||
const body = new CANNON.Body({ mass: this.car.options.wheelMass, material: this.materials.items.wheel })
|
||||
const quaternion = new CANNON.Quaternion()
|
||||
quaternion.setFromAxisAngle(new CANNON.Vec3(1, 0, 0), Math.PI / 2)
|
||||
|
||||
body.type = CANNON.Body.KINEMATIC
|
||||
|
||||
body.addShape(shape, new CANNON.Vec3(), quaternion)
|
||||
this.car.wheels.bodies.push(body)
|
||||
}
|
||||
|
||||
/**
|
||||
* Model
|
||||
*/
|
||||
this.car.model = {}
|
||||
this.car.model.container = new THREE.Object3D()
|
||||
this.models.container.add(this.car.model.container)
|
||||
|
||||
this.car.model.material = new THREE.MeshBasicMaterial({ color: 0xffffff, wireframe: true })
|
||||
|
||||
this.car.model.chassis = new THREE.Mesh(new THREE.BoxBufferGeometry(this.car.options.chassisDepth, this.car.options.chassisWidth, this.car.options.chassisHeight), this.car.model.material)
|
||||
this.car.model.container.add(this.car.model.chassis)
|
||||
|
||||
this.car.model.wheels = []
|
||||
|
||||
const wheelGeometry = new THREE.CylinderBufferGeometry(this.car.options.wheelRadius, this.car.options.wheelRadius, this.car.options.wheelHeight, 8, 1)
|
||||
|
||||
for(let i = 0; i < 4; i++)
|
||||
{
|
||||
const wheel = new THREE.Mesh(wheelGeometry, this.car.model.material)
|
||||
this.car.model.container.add(wheel)
|
||||
this.car.model.wheels.push(wheel)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Destroy method
|
||||
*/
|
||||
this.car.destroy = () =>
|
||||
{
|
||||
this.car.vehicle.removeFromWorld(this.world)
|
||||
this.models.container.remove(this.car.model.container)
|
||||
}
|
||||
|
||||
/**
|
||||
* Recreate method
|
||||
*/
|
||||
this.car.recreate = () =>
|
||||
{
|
||||
this.car.destroy()
|
||||
this.car.create()
|
||||
this.car.chassis.body.wakeUp()
|
||||
}
|
||||
|
||||
/**
|
||||
* Brake
|
||||
*/
|
||||
this.car.brake = () =>
|
||||
{
|
||||
this.car.vehicle.setBrake(1, 0)
|
||||
this.car.vehicle.setBrake(1, 1)
|
||||
this.car.vehicle.setBrake(1, 2)
|
||||
this.car.vehicle.setBrake(1, 3)
|
||||
}
|
||||
|
||||
/**
|
||||
* Unbrake
|
||||
*/
|
||||
this.car.unbrake = () =>
|
||||
{
|
||||
this.car.vehicle.setBrake(0, 0)
|
||||
this.car.vehicle.setBrake(0, 1)
|
||||
this.car.vehicle.setBrake(0, 2)
|
||||
this.car.vehicle.setBrake(0, 3)
|
||||
}
|
||||
|
||||
/**
|
||||
* Actions
|
||||
*/
|
||||
this.controls.on('action', (_name) =>
|
||||
{
|
||||
switch(_name)
|
||||
{
|
||||
case 'reset':
|
||||
this.car.recreate()
|
||||
break
|
||||
}
|
||||
})
|
||||
|
||||
/**
|
||||
* Cannon tick
|
||||
*/
|
||||
this.world.addEventListener('postStep', () =>
|
||||
{
|
||||
// Update speed
|
||||
let positionDelta = new CANNON.Vec3()
|
||||
positionDelta = positionDelta.copy(this.car.chassis.body.position)
|
||||
positionDelta = positionDelta.vsub(this.car.oldPosition)
|
||||
|
||||
this.car.oldPosition.copy(this.car.chassis.body.position)
|
||||
this.car.speed = positionDelta.length()
|
||||
|
||||
// Update forward
|
||||
const localForward = new CANNON.Vec3(1, 0, 0)
|
||||
this.car.chassis.body.vectorToWorldFrame(localForward, this.car.worldForward)
|
||||
this.car.angle = Math.atan2(this.car.worldForward.y, this.car.worldForward.x)
|
||||
|
||||
this.car.forwardSpeed = this.car.worldForward.dot(positionDelta)
|
||||
this.car.goingForward = this.car.forwardSpeed > 0
|
||||
|
||||
// Updise down
|
||||
const localUp = new CANNON.Vec3(0, 0, 1)
|
||||
const worldUp = new CANNON.Vec3()
|
||||
this.car.chassis.body.vectorToWorldFrame(localUp, worldUp)
|
||||
|
||||
if(worldUp.dot(localUp) < 0.5)
|
||||
{
|
||||
if(this.car.upsideDown.state === 'watching')
|
||||
{
|
||||
this.car.upsideDown.state = 'pending'
|
||||
this.car.upsideDown.pendingTimeout = window.setTimeout(() =>
|
||||
{
|
||||
this.car.upsideDown.state = 'turning'
|
||||
this.car.jump(true)
|
||||
|
||||
this.car.upsideDown.turningTimeout = window.setTimeout(() =>
|
||||
{
|
||||
this.car.upsideDown.state = 'watching'
|
||||
}, 1000)
|
||||
}, 1000)
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if(this.car.upsideDown.state === 'pending')
|
||||
{
|
||||
this.car.upsideDown.state = 'watching'
|
||||
window.clearTimeout(this.car.upsideDown.pendingTimeout)
|
||||
}
|
||||
}
|
||||
|
||||
// Update wheel bodies
|
||||
for(let i = 0; i < this.car.vehicle.wheelInfos.length; i++)
|
||||
{
|
||||
this.car.vehicle.updateWheelTransform(i)
|
||||
|
||||
const transform = this.car.vehicle.wheelInfos[i].worldTransform
|
||||
this.car.wheels.bodies[i].position.copy(transform.position)
|
||||
this.car.wheels.bodies[i].quaternion.copy(transform.quaternion)
|
||||
|
||||
// Rotate the wheels on the right
|
||||
if(i === 1 || i === 3)
|
||||
{
|
||||
const rotationQuaternion = new CANNON.Quaternion(0, 0, 0, 1)
|
||||
rotationQuaternion.setFromAxisAngle(new CANNON.Vec3(0, 0, 1), Math.PI)
|
||||
this.car.wheels.bodies[i].quaternion = this.car.wheels.bodies[i].quaternion.mult(rotationQuaternion)
|
||||
}
|
||||
}
|
||||
|
||||
// Slow down back
|
||||
if(!this.controls.actions.up && !this.controls.actions.down)
|
||||
{
|
||||
let slowDownForce = this.car.worldForward.clone()
|
||||
|
||||
if(this.car.goingForward)
|
||||
{
|
||||
slowDownForce = slowDownForce.negate()
|
||||
}
|
||||
|
||||
slowDownForce = slowDownForce.scale(this.car.chassis.body.velocity.length() * 0.1)
|
||||
|
||||
this.car.chassis.body.applyImpulse(slowDownForce, this.car.chassis.body.position)
|
||||
}
|
||||
})
|
||||
|
||||
/**
|
||||
* Time tick
|
||||
*/
|
||||
this.time.on('tick', () =>
|
||||
{
|
||||
/**
|
||||
* Body
|
||||
*/
|
||||
// Update chassis model
|
||||
this.car.model.chassis.position.copy(this.car.chassis.body.position).add(this.car.options.chassisOffset)
|
||||
this.car.model.chassis.quaternion.copy(this.car.chassis.body.quaternion)
|
||||
|
||||
// Update wheel models
|
||||
for(const _wheelKey in this.car.wheels.bodies)
|
||||
{
|
||||
const wheelBody = this.car.wheels.bodies[_wheelKey]
|
||||
const wheelMesh = this.car.model.wheels[_wheelKey]
|
||||
|
||||
wheelMesh.position.copy(wheelBody.position)
|
||||
wheelMesh.quaternion.copy(wheelBody.quaternion)
|
||||
}
|
||||
|
||||
/**
|
||||
* Steering
|
||||
*/
|
||||
if(this.controls.touch)
|
||||
{
|
||||
let deltaAngle = 0
|
||||
|
||||
if(this.controls.touch.joystick.active)
|
||||
{
|
||||
// Calculate delta between joystick and car angles
|
||||
deltaAngle = (this.controls.touch.joystick.angle.value - this.car.angle + Math.PI) % (Math.PI * 2) - Math.PI
|
||||
deltaAngle = deltaAngle < - Math.PI ? deltaAngle + Math.PI * 2 : deltaAngle
|
||||
}
|
||||
|
||||
// Update steering directly
|
||||
const goingForward = Math.abs(this.car.forwardSpeed) < 0.01 ? true : this.car.goingForward
|
||||
this.car.steering = deltaAngle * (goingForward ? - 1 : 1)
|
||||
|
||||
// Clamp steer
|
||||
if(Math.abs(this.car.steering) > this.car.options.controlsSteeringMax)
|
||||
{
|
||||
this.car.steering = Math.sign(this.car.steering) * this.car.options.controlsSteeringMax
|
||||
}
|
||||
}
|
||||
|
||||
if(!this.controls.touch || !this.controls.touch.joystick.active)
|
||||
{
|
||||
const steerStrength = this.time.delta * this.car.options.controlsSteeringSpeed
|
||||
|
||||
// Steer right
|
||||
if(this.controls.actions.right)
|
||||
{
|
||||
this.car.steering += steerStrength
|
||||
}
|
||||
// Steer left
|
||||
else if(this.controls.actions.left)
|
||||
{
|
||||
this.car.steering -= steerStrength
|
||||
}
|
||||
// Steer center
|
||||
else
|
||||
{
|
||||
if(Math.abs(this.car.steering) > steerStrength)
|
||||
{
|
||||
this.car.steering -= steerStrength * Math.sign(this.car.steering)
|
||||
}
|
||||
else
|
||||
{
|
||||
this.car.steering = 0
|
||||
}
|
||||
}
|
||||
|
||||
// Clamp steer
|
||||
if(Math.abs(this.car.steering) > this.car.options.controlsSteeringMax)
|
||||
{
|
||||
this.car.steering = Math.sign(this.car.steering) * this.car.options.controlsSteeringMax
|
||||
}
|
||||
}
|
||||
|
||||
// Update wheels
|
||||
this.car.vehicle.setSteeringValue(- this.car.steering, this.car.wheels.indexes.frontLeft)
|
||||
this.car.vehicle.setSteeringValue(- this.car.steering, this.car.wheels.indexes.frontRight)
|
||||
|
||||
if(this.car.options.controlsSteeringQuad)
|
||||
{
|
||||
this.car.vehicle.setSteeringValue(this.car.steering, this.car.wheels.indexes.backLeft)
|
||||
this.car.vehicle.setSteeringValue(this.car.steering, this.car.wheels.indexes.backRight)
|
||||
}
|
||||
|
||||
/**
|
||||
* Accelerate
|
||||
*/
|
||||
const accelerationSpeed = this.controls.actions.boost ? this.car.options.controlsAcceleratingSpeedBoost : this.car.options.controlsAcceleratingSpeed
|
||||
const accelerateStrength = this.time.delta * accelerationSpeed
|
||||
const controlsAcceleratinMaxSpeed = this.controls.actions.boost ? this.car.options.controlsAcceleratinMaxSpeedBoost : this.car.options.controlsAcceleratinMaxSpeed
|
||||
|
||||
// Accelerate up
|
||||
if(this.controls.actions.up)
|
||||
{
|
||||
if(this.car.speed < controlsAcceleratinMaxSpeed || !this.car.goingForward)
|
||||
{
|
||||
this.car.accelerating = accelerateStrength
|
||||
}
|
||||
else
|
||||
{
|
||||
this.car.accelerating = 0
|
||||
}
|
||||
}
|
||||
|
||||
// Accelerate Down
|
||||
else if(this.controls.actions.down)
|
||||
{
|
||||
if(this.car.speed < controlsAcceleratinMaxSpeed || this.car.goingForward)
|
||||
{
|
||||
this.car.accelerating = - accelerateStrength
|
||||
}
|
||||
else
|
||||
{
|
||||
this.car.accelerating = 0
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
this.car.accelerating = 0
|
||||
}
|
||||
|
||||
this.car.vehicle.applyEngineForce(- this.car.accelerating, this.car.wheels.indexes.backLeft)
|
||||
this.car.vehicle.applyEngineForce(- this.car.accelerating, this.car.wheels.indexes.backRight)
|
||||
|
||||
if(this.car.options.controlsSteeringQuad)
|
||||
{
|
||||
this.car.vehicle.applyEngineForce(- this.car.accelerating, this.car.wheels.indexes.frontLeft)
|
||||
this.car.vehicle.applyEngineForce(- this.car.accelerating, this.car.wheels.indexes.frontRight)
|
||||
}
|
||||
|
||||
/**
|
||||
* Brake
|
||||
*/
|
||||
if(this.controls.actions.brake)
|
||||
{
|
||||
this.car.vehicle.setBrake(this.car.options.controlsBrakeStrength, 0)
|
||||
this.car.vehicle.setBrake(this.car.options.controlsBrakeStrength, 1)
|
||||
this.car.vehicle.setBrake(this.car.options.controlsBrakeStrength, 2)
|
||||
this.car.vehicle.setBrake(this.car.options.controlsBrakeStrength, 3)
|
||||
}
|
||||
else
|
||||
{
|
||||
this.car.vehicle.setBrake(0, 0)
|
||||
this.car.vehicle.setBrake(0, 1)
|
||||
this.car.vehicle.setBrake(0, 2)
|
||||
this.car.vehicle.setBrake(0, 3)
|
||||
}
|
||||
})
|
||||
|
||||
// Create the initial car
|
||||
this.car.create()
|
||||
|
||||
// Debug
|
||||
if(this.debug)
|
||||
{
|
||||
this.car.debugFolder = this.debugFolder.addFolder('car')
|
||||
this.car.debugFolder.open()
|
||||
|
||||
this.car.debugFolder.add(this.car.options, 'chassisWidth').step(0.001).min(0).max(5).name('chassisWidth').onFinishChange(this.car.recreate)
|
||||
this.car.debugFolder.add(this.car.options, 'chassisHeight').step(0.001).min(0).max(5).name('chassisHeight').onFinishChange(this.car.recreate)
|
||||
this.car.debugFolder.add(this.car.options, 'chassisDepth').step(0.001).min(0).max(5).name('chassisDepth').onFinishChange(this.car.recreate)
|
||||
this.car.debugFolder.add(this.car.options.chassisOffset, 'z').step(0.001).min(0).max(5).name('chassisOffset').onFinishChange(this.car.recreate)
|
||||
this.car.debugFolder.add(this.car.options, 'chassisMass').step(0.001).min(0).max(1000).name('chassisMass').onFinishChange(this.car.recreate)
|
||||
this.car.debugFolder.add(this.car.options, 'wheelFrontOffsetDepth').step(0.001).min(0).max(5).name('wheelFrontOffsetDepth').onFinishChange(this.car.recreate)
|
||||
this.car.debugFolder.add(this.car.options, 'wheelBackOffsetDepth').step(0.001).min(- 5).max(0).name('wheelBackOffsetDepth').onFinishChange(this.car.recreate)
|
||||
this.car.debugFolder.add(this.car.options, 'wheelOffsetWidth').step(0.001).min(0).max(5).name('wheelOffsetWidth').onFinishChange(this.car.recreate)
|
||||
this.car.debugFolder.add(this.car.options, 'wheelRadius').step(0.001).min(0).max(2).name('wheelRadius').onFinishChange(this.car.recreate)
|
||||
this.car.debugFolder.add(this.car.options, 'wheelHeight').step(0.001).min(0).max(2).name('wheelHeight').onFinishChange(this.car.recreate)
|
||||
this.car.debugFolder.add(this.car.options, 'wheelSuspensionStiffness').step(0.001).min(0).max(300).name('wheelSuspensionStiffness').onFinishChange(this.car.recreate)
|
||||
this.car.debugFolder.add(this.car.options, 'wheelSuspensionRestLength').step(0.001).min(0).max(5).name('wheelSuspensionRestLength').onFinishChange(this.car.recreate)
|
||||
this.car.debugFolder.add(this.car.options, 'wheelFrictionSlip').step(0.001).min(0).max(30).name('wheelFrictionSlip').onFinishChange(this.car.recreate)
|
||||
this.car.debugFolder.add(this.car.options, 'wheelDampingRelaxation').step(0.001).min(0).max(30).name('wheelDampingRelaxation').onFinishChange(this.car.recreate)
|
||||
this.car.debugFolder.add(this.car.options, 'wheelDampingCompression').step(0.001).min(0).max(30).name('wheelDampingCompression').onFinishChange(this.car.recreate)
|
||||
this.car.debugFolder.add(this.car.options, 'wheelMaxSuspensionForce').step(0.001).min(0).max(1000000).name('wheelMaxSuspensionForce').onFinishChange(this.car.recreate)
|
||||
this.car.debugFolder.add(this.car.options, 'wheelRollInfluence').step(0.001).min(0).max(1).name('wheelRollInfluence').onFinishChange(this.car.recreate)
|
||||
this.car.debugFolder.add(this.car.options, 'wheelMaxSuspensionTravel').step(0.001).min(0).max(5).name('wheelMaxSuspensionTravel').onFinishChange(this.car.recreate)
|
||||
this.car.debugFolder.add(this.car.options, 'wheelCustomSlidingRotationalSpeed').step(0.001).min(- 45).max(45).name('wheelCustomSlidingRotationalSpeed').onFinishChange(this.car.recreate)
|
||||
this.car.debugFolder.add(this.car.options, 'wheelMass').step(0.001).min(0).max(1000).name('wheelMass').onFinishChange(this.car.recreate)
|
||||
this.car.debugFolder.add(this.car.options, 'controlsSteeringSpeed').step(0.001).min(0).max(0.1).name('controlsSteeringSpeed')
|
||||
this.car.debugFolder.add(this.car.options, 'controlsSteeringMax').step(0.001).min(0).max(Math.PI * 0.5).name('controlsSteeringMax')
|
||||
this.car.debugFolder.add(this.car.options, 'controlsSteeringQuad').name('controlsSteeringQuad')
|
||||
this.car.debugFolder.add(this.car.options, 'controlsAcceleratingSpeed').step(0.001).min(0).max(30).name('controlsAcceleratingSpeed')
|
||||
this.car.debugFolder.add(this.car.options, 'controlsAcceleratingSpeedBoost').step(0.001).min(0).max(30).name('controlsAcceleratingSpeedBoost')
|
||||
this.car.debugFolder.add(this.car.options, 'controlsAcceleratingQuad').name('controlsAcceleratingQuad')
|
||||
this.car.debugFolder.add(this.car.options, 'controlsBrakeStrength').step(0.001).min(0).max(5).name('controlsBrakeStrength')
|
||||
this.car.debugFolder.add(this.car, 'recreate')
|
||||
this.car.debugFolder.add(this.car, 'jump')
|
||||
}
|
||||
}
|
||||
|
||||
addObjectFromThree(_options)
|
||||
{
|
||||
// Set up
|
||||
const collision = {}
|
||||
|
||||
collision.model = {}
|
||||
collision.model.meshes = []
|
||||
collision.model.container = new THREE.Object3D()
|
||||
this.models.container.add(collision.model.container)
|
||||
|
||||
collision.children = []
|
||||
|
||||
// Material
|
||||
const bodyMaterial = this.materials.items.dummy
|
||||
|
||||
// Body
|
||||
collision.body = new CANNON.Body({
|
||||
position: new CANNON.Vec3(_options.offset.x, _options.offset.y, _options.offset.z),
|
||||
mass: _options.mass,
|
||||
material: bodyMaterial
|
||||
})
|
||||
collision.body.allowSleep = true
|
||||
collision.body.sleepSpeedLimit = 0.01
|
||||
if(_options.sleep)
|
||||
{
|
||||
collision.body.sleep()
|
||||
}
|
||||
|
||||
this.world.addBody(collision.body)
|
||||
|
||||
// Rotation
|
||||
if(_options.rotation)
|
||||
{
|
||||
const rotationQuaternion = new CANNON.Quaternion()
|
||||
rotationQuaternion.setFromEuler(_options.rotation.x, _options.rotation.y, _options.rotation.z, _options.rotation.order)
|
||||
collision.body.quaternion = collision.body.quaternion.mult(rotationQuaternion)
|
||||
}
|
||||
|
||||
// Center
|
||||
collision.center = new CANNON.Vec3(0, 0, 0)
|
||||
|
||||
// Shapes
|
||||
const shapes = []
|
||||
|
||||
// Each mesh
|
||||
for(let i = 0; i < _options.meshes.length; i++)
|
||||
{
|
||||
const mesh = _options.meshes[i]
|
||||
|
||||
// Define shape
|
||||
let shape = null
|
||||
|
||||
if(mesh.name.match(/^cube_?[0-9]{0,3}?|box[0-9]{0,3}?$/i))
|
||||
{
|
||||
shape = 'box'
|
||||
}
|
||||
else if(mesh.name.match(/^cylinder_?[0-9]{0,3}?$/i))
|
||||
{
|
||||
shape = 'cylinder'
|
||||
}
|
||||
else if(mesh.name.match(/^sphere_?[0-9]{0,3}?$/i))
|
||||
{
|
||||
shape = 'sphere'
|
||||
}
|
||||
else if(mesh.name.match(/^center_?[0-9]{0,3}?$/i))
|
||||
{
|
||||
shape = 'center'
|
||||
}
|
||||
|
||||
// Shape is the center
|
||||
if(shape === 'center')
|
||||
{
|
||||
collision.center.set(mesh.position.x, mesh.position.y, mesh.position.z)
|
||||
}
|
||||
|
||||
// Other shape
|
||||
else if(shape)
|
||||
{
|
||||
// Geometry
|
||||
let shapeGeometry = null
|
||||
|
||||
if(shape === 'cylinder')
|
||||
{
|
||||
shapeGeometry = new CANNON.Cylinder(mesh.scale.x, mesh.scale.x, mesh.scale.z, 8)
|
||||
}
|
||||
else if(shape === 'box')
|
||||
{
|
||||
const halfExtents = new CANNON.Vec3(mesh.scale.x * 0.5, mesh.scale.y * 0.5, mesh.scale.z * 0.5)
|
||||
shapeGeometry = new CANNON.Box(halfExtents)
|
||||
}
|
||||
else if(shape === 'sphere')
|
||||
{
|
||||
shapeGeometry = new CANNON.Sphere(mesh.scale.x)
|
||||
}
|
||||
|
||||
// Position
|
||||
const shapePosition = new CANNON.Vec3(mesh.position.x, mesh.position.y, mesh.position.z)
|
||||
|
||||
// Quaternion
|
||||
const shapeQuaternion = new CANNON.Quaternion(mesh.quaternion.x, mesh.quaternion.y, mesh.quaternion.z, mesh.quaternion.w)
|
||||
if(shape === 'cylinder')
|
||||
{
|
||||
// Rotate cylinder
|
||||
// shapeQuaternion.setFromAxisAngle(new CANNON.Vec3(1, 0, 0), - Math.PI * 0.5)
|
||||
}
|
||||
|
||||
// Save
|
||||
shapes.push({ shapeGeometry, shapePosition, shapeQuaternion })
|
||||
|
||||
// Create model object
|
||||
let modelGeometry = null
|
||||
if(shape === 'cylinder')
|
||||
{
|
||||
modelGeometry = new THREE.CylinderBufferGeometry(1, 1, 1, 8, 1)
|
||||
modelGeometry.rotateX(Math.PI * 0.5)
|
||||
}
|
||||
else if(shape === 'box')
|
||||
{
|
||||
modelGeometry = new THREE.BoxBufferGeometry(1, 1, 1)
|
||||
}
|
||||
else if(shape === 'sphere')
|
||||
{
|
||||
modelGeometry = new THREE.SphereBufferGeometry(1, 8, 8)
|
||||
}
|
||||
|
||||
const modelMesh = new THREE.Mesh(modelGeometry, this.models.materials[_options.mass === 0 ? 'static' : 'dynamic'])
|
||||
modelMesh.position.copy(mesh.position)
|
||||
modelMesh.scale.copy(mesh.scale)
|
||||
modelMesh.quaternion.copy(mesh.quaternion)
|
||||
|
||||
collision.model.meshes.push(modelMesh)
|
||||
}
|
||||
}
|
||||
|
||||
// Update meshes to match center
|
||||
for(const _mesh of collision.model.meshes)
|
||||
{
|
||||
_mesh.position.x -= collision.center.x
|
||||
_mesh.position.y -= collision.center.y
|
||||
_mesh.position.z -= collision.center.z
|
||||
|
||||
collision.model.container.add(_mesh)
|
||||
}
|
||||
|
||||
// Update shapes to match center
|
||||
for(const _shape of shapes)
|
||||
{
|
||||
// Create physic object
|
||||
_shape.shapePosition.x -= collision.center.x
|
||||
_shape.shapePosition.y -= collision.center.y
|
||||
_shape.shapePosition.z -= collision.center.z
|
||||
|
||||
collision.body.addShape(_shape.shapeGeometry, _shape.shapePosition, _shape.shapeQuaternion)
|
||||
}
|
||||
|
||||
// Update body to match center
|
||||
collision.body.position.x += collision.center.x
|
||||
collision.body.position.y += collision.center.y
|
||||
collision.body.position.z += collision.center.z
|
||||
|
||||
// Save origin
|
||||
collision.origin = {}
|
||||
collision.origin.position = collision.body.position.clone()
|
||||
collision.origin.quaternion = collision.body.quaternion.clone()
|
||||
collision.origin.sleep = _options.sleep
|
||||
|
||||
// Time tick update
|
||||
this.time.on('tick', () =>
|
||||
{
|
||||
collision.model.container.position.set(collision.body.position.x, collision.body.position.y, collision.body.position.z)
|
||||
collision.model.container.quaternion.set(collision.body.quaternion.x, collision.body.quaternion.y, collision.body.quaternion.z, collision.body.quaternion.w)
|
||||
|
||||
if(this.models.container.visible && _options.mass > 0)
|
||||
{
|
||||
for(const _mesh of collision.model.container.children)
|
||||
{
|
||||
_mesh.material = collision.body.sleepState === 2 ? this.models.materials.dynamicSleeping : this.models.materials.dynamic
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
// Reset
|
||||
collision.reset = () =>
|
||||
{
|
||||
collision.body.position.copy(collision.origin.position)
|
||||
collision.body.quaternion.copy(collision.origin.quaternion)
|
||||
|
||||
if(collision.origin.sleep)
|
||||
{
|
||||
collision.body.sleep()
|
||||
}
|
||||
}
|
||||
|
||||
return collision
|
||||
}
|
||||
}
|
56
src/javascript/World/Sections/CrossroadsSection.js
Normal file
@ -0,0 +1,56 @@
|
||||
import * as THREE from 'three'
|
||||
|
||||
export default class CrossroadsSection
|
||||
{
|
||||
constructor(_options)
|
||||
{
|
||||
// Options
|
||||
this.time = _options.time
|
||||
this.resources = _options.resources
|
||||
this.objects = _options.objects
|
||||
this.areas = _options.areas
|
||||
this.tiles = _options.tiles
|
||||
this.debug = _options.debug
|
||||
this.x = _options.x
|
||||
this.y = _options.y
|
||||
|
||||
// Set up
|
||||
this.container = new THREE.Object3D()
|
||||
this.container.matrixAutoUpdate = false
|
||||
|
||||
this.setStatic()
|
||||
this.setTiles()
|
||||
}
|
||||
|
||||
setStatic()
|
||||
{
|
||||
this.objects.add({
|
||||
base: this.resources.items.crossroadsStaticBase.scene,
|
||||
collision: this.resources.items.crossroadsStaticCollision.scene,
|
||||
floorShadowTexture: this.resources.items.crossroadsStaticFloorShadowTexture,
|
||||
offset: new THREE.Vector3(this.x, this.y, 0),
|
||||
mass: 0
|
||||
})
|
||||
}
|
||||
|
||||
setTiles()
|
||||
{
|
||||
// To intro
|
||||
this.tiles.add({
|
||||
start: new THREE.Vector2(this.x, - 10),
|
||||
delta: new THREE.Vector2(0, this.y + 14)
|
||||
})
|
||||
|
||||
// To projects
|
||||
this.tiles.add({
|
||||
start: new THREE.Vector2(this.x + 12.5, this.y),
|
||||
delta: new THREE.Vector2(7.5, 0)
|
||||
})
|
||||
|
||||
// To projects
|
||||
this.tiles.add({
|
||||
start: new THREE.Vector2(this.x - 13, this.y),
|
||||
delta: new THREE.Vector2(- 6, 0)
|
||||
})
|
||||
}
|
||||
}
|
93
src/javascript/World/Sections/DistinctionASection.js
Normal file
@ -0,0 +1,93 @@
|
||||
import * as THREE from 'three'
|
||||
|
||||
export default class DistinctionASection
|
||||
{
|
||||
constructor(_options)
|
||||
{
|
||||
// Options
|
||||
this.time = _options.time
|
||||
this.resources = _options.resources
|
||||
this.objects = _options.objects
|
||||
this.walls = _options.walls
|
||||
this.debug = _options.debug
|
||||
this.x = _options.x
|
||||
this.y = _options.y
|
||||
|
||||
// Set up
|
||||
this.container = new THREE.Object3D()
|
||||
this.container.matrixAutoUpdate = false
|
||||
|
||||
this.setStatic()
|
||||
this.setCones()
|
||||
this.setWall()
|
||||
}
|
||||
|
||||
setStatic()
|
||||
{
|
||||
this.objects.add({
|
||||
base: this.resources.items.distinctionAStaticBase.scene,
|
||||
collision: this.resources.items.distinctionAStaticCollision.scene,
|
||||
floorShadowTexture: this.resources.items.distinctionAStaticFloorShadowTexture,
|
||||
offset: new THREE.Vector3(this.x, this.y, 0),
|
||||
mass: 0
|
||||
})
|
||||
}
|
||||
|
||||
setCones()
|
||||
{
|
||||
const positions = [
|
||||
[0, 9],
|
||||
[0, 3],
|
||||
[0, - 3],
|
||||
[0, - 9]
|
||||
]
|
||||
|
||||
for(const _position of positions)
|
||||
{
|
||||
this.objects.add({
|
||||
base: this.resources.items.coneBase.scene,
|
||||
collision: this.resources.items.coneCollision.scene,
|
||||
offset: new THREE.Vector3(this.x + _position[0], this.y + _position[1], 0),
|
||||
rotation: new THREE.Euler(0, 0, 0),
|
||||
duplicated: true,
|
||||
shadow: { sizeX: 2, sizeY: 2, offsetZ: - 0.5, alpha: 0.5 },
|
||||
mass: 0.6,
|
||||
soundName: 'woodHit'
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
setWall()
|
||||
{
|
||||
// Set up
|
||||
this.wall = {}
|
||||
this.wall.x = this.x + 0
|
||||
this.wall.y = this.y - 13
|
||||
this.wall.items = []
|
||||
|
||||
this.walls.add({
|
||||
object:
|
||||
{
|
||||
base: this.resources.items.projectsDistinctionsAwwwardsBase.scene,
|
||||
collision: this.resources.items.projectsDistinctionsAwwwardsCollision.scene,
|
||||
offset: new THREE.Vector3(0, 0, 0.1),
|
||||
rotation: new THREE.Euler(0, 0, 0),
|
||||
duplicated: true,
|
||||
shadow: { sizeX: 1.2, sizeY: 1.8, offsetZ: - 0.15, alpha: 0.35 },
|
||||
mass: 0.5,
|
||||
soundName: 'brick'
|
||||
},
|
||||
shape:
|
||||
{
|
||||
type: 'brick',
|
||||
widthCount: 5,
|
||||
heightCount: 6,
|
||||
position: new THREE.Vector3(this.wall.x, this.wall.y, 0),
|
||||
offsetWidth: new THREE.Vector3(1.25, 0, 0),
|
||||
offsetHeight: new THREE.Vector3(0, 0, 0.6),
|
||||
randomOffset: new THREE.Vector3(0, 0, 0),
|
||||
randomRotation: new THREE.Vector3(0, 0, 0.4)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
98
src/javascript/World/Sections/DistinctionBSection.js
Normal file
@ -0,0 +1,98 @@
|
||||
import * as THREE from 'three'
|
||||
|
||||
export default class DistinctionBSection
|
||||
{
|
||||
constructor(_options)
|
||||
{
|
||||
// Options
|
||||
this.time = _options.time
|
||||
this.resources = _options.resources
|
||||
this.objects = _options.objects
|
||||
this.walls = _options.walls
|
||||
this.debug = _options.debug
|
||||
this.x = _options.x
|
||||
this.y = _options.y
|
||||
|
||||
// Set up
|
||||
this.container = new THREE.Object3D()
|
||||
this.container.matrixAutoUpdate = false
|
||||
|
||||
this.setStatic()
|
||||
this.setCones()
|
||||
this.setWall()
|
||||
}
|
||||
|
||||
setStatic()
|
||||
{
|
||||
this.objects.add({
|
||||
base: this.resources.items.distinctionBStaticBase.scene,
|
||||
collision: this.resources.items.distinctionBStaticCollision.scene,
|
||||
floorShadowTexture: this.resources.items.distinctionBStaticFloorShadowTexture,
|
||||
offset: new THREE.Vector3(this.x, this.y, 0),
|
||||
mass: 0
|
||||
})
|
||||
}
|
||||
|
||||
setCones()
|
||||
{
|
||||
const positions = [
|
||||
[3, 8],
|
||||
[3, 4],
|
||||
[3, 0],
|
||||
[3, - 4],
|
||||
|
||||
[- 3, 8],
|
||||
[- 3, 4],
|
||||
[- 3, 0],
|
||||
[- 3, - 4]
|
||||
]
|
||||
|
||||
for(const _position of positions)
|
||||
{
|
||||
this.objects.add({
|
||||
base: this.resources.items.coneBase.scene,
|
||||
collision: this.resources.items.coneCollision.scene,
|
||||
offset: new THREE.Vector3(this.x + _position[0], this.y + _position[1], 0),
|
||||
rotation: new THREE.Euler(0, 0, 0),
|
||||
duplicated: true,
|
||||
shadow: { sizeX: 2, sizeY: 2, offsetZ: - 0.5, alpha: 0.5 },
|
||||
mass: 0.6,
|
||||
soundName: 'woodHit'
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
setWall()
|
||||
{
|
||||
// Set up
|
||||
this.wall = {}
|
||||
this.wall.x = this.x + 0
|
||||
this.wall.y = this.y - 18
|
||||
this.wall.items = []
|
||||
|
||||
this.walls.add({
|
||||
object:
|
||||
{
|
||||
base: this.resources.items.projectsDistinctionsFWABase.scene,
|
||||
collision: this.resources.items.projectsDistinctionsFWACollision.scene,
|
||||
offset: new THREE.Vector3(0, 0, 0.1),
|
||||
rotation: new THREE.Euler(0, 0, 0),
|
||||
duplicated: true,
|
||||
shadow: { sizeX: 1.2, sizeY: 1.8, offsetZ: - 0.15, alpha: 0.35 },
|
||||
mass: 0.5,
|
||||
soundName: 'brick'
|
||||
},
|
||||
shape:
|
||||
{
|
||||
type: 'brick',
|
||||
widthCount: 4,
|
||||
heightCount: 7,
|
||||
position: new THREE.Vector3(this.wall.x, this.wall.y, 0),
|
||||
offsetWidth: new THREE.Vector3(1.7, 0, 0),
|
||||
offsetHeight: new THREE.Vector3(0, 0, 0.45),
|
||||
randomOffset: new THREE.Vector3(0, 0, 0),
|
||||
randomRotation: new THREE.Vector3(0, 0, 0.4)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
36
src/javascript/World/Sections/DistinctionCSection.js
Normal file
@ -0,0 +1,36 @@
|
||||
import * as THREE from 'three'
|
||||
|
||||
export default class DistinctionCSection
|
||||
{
|
||||
constructor(_options)
|
||||
{
|
||||
// Options
|
||||
this.time = _options.time
|
||||
this.resources = _options.resources
|
||||
this.objects = _options.objects
|
||||
this.walls = _options.walls
|
||||
this.debug = _options.debug
|
||||
this.x = _options.x
|
||||
this.y = _options.y
|
||||
|
||||
// Set up
|
||||
this.container = new THREE.Object3D()
|
||||
this.container.matrixAutoUpdate = false
|
||||
|
||||
this.setTrophy()
|
||||
}
|
||||
|
||||
setTrophy()
|
||||
{
|
||||
this.objects.add({
|
||||
base: this.resources.items.awwwardsTrophyBase.scene,
|
||||
collision: this.resources.items.awwwardsTrophyCollision.scene,
|
||||
offset: new THREE.Vector3(0, - 5, 0),
|
||||
rotation: new THREE.Euler(0, 0, 0),
|
||||
duplicated: true,
|
||||
shadow: { sizeX: 2, sizeY: 2, offsetZ: - 0.5, alpha: 0.5 },
|
||||
mass: 50,
|
||||
soundName: 'woodHit'
|
||||
})
|
||||
}
|
||||
}
|
49
src/javascript/World/Sections/DistinctionDSection.js
Normal file
@ -0,0 +1,49 @@
|
||||
import * as THREE from 'three'
|
||||
|
||||
export default class DistinctionCSection
|
||||
{
|
||||
constructor(_options)
|
||||
{
|
||||
// Options
|
||||
this.time = _options.time
|
||||
this.resources = _options.resources
|
||||
this.objects = _options.objects
|
||||
this.walls = _options.walls
|
||||
this.debug = _options.debug
|
||||
this.x = _options.x
|
||||
this.y = _options.y
|
||||
|
||||
// Set up
|
||||
this.container = new THREE.Object3D()
|
||||
this.container.matrixAutoUpdate = false
|
||||
|
||||
this.setStatic()
|
||||
this.setTrophy()
|
||||
}
|
||||
|
||||
setStatic()
|
||||
{
|
||||
this.objects.add({
|
||||
base: this.resources.items.distinctionCStaticBase.scene,
|
||||
collision: this.resources.items.distinctionCStaticCollision.scene,
|
||||
floorShadowTexture: this.resources.items.distinctionCStaticFloorShadowTexture,
|
||||
offset: new THREE.Vector3(this.x, this.y, 0),
|
||||
mass: 0
|
||||
})
|
||||
}
|
||||
|
||||
setTrophy()
|
||||
{
|
||||
this.objects.add({
|
||||
base: this.resources.items.webbyTrophyBase.scene,
|
||||
collision: this.resources.items.webbyTrophyCollision.scene,
|
||||
offset: new THREE.Vector3(0, - 2.5, 5),
|
||||
rotation: new THREE.Euler(0, 0, 0),
|
||||
duplicated: true,
|
||||
shadow: { sizeX: 4, sizeY: 4, offsetZ: - 0.5, alpha: 0.65 },
|
||||
mass: 15,
|
||||
soundName: 'woodHit',
|
||||
sleep: false
|
||||
})
|
||||
}
|
||||
}
|
184
src/javascript/World/Sections/InformationSection.js
Normal file
@ -0,0 +1,184 @@
|
||||
import * as THREE from 'three'
|
||||
|
||||
export default class InformationSection
|
||||
{
|
||||
constructor(_options)
|
||||
{
|
||||
// Options
|
||||
this.time = _options.time
|
||||
this.resources = _options.resources
|
||||
this.objects = _options.objects
|
||||
this.areas = _options.areas
|
||||
this.tiles = _options.tiles
|
||||
this.debug = _options.debug
|
||||
this.x = _options.x
|
||||
this.y = _options.y
|
||||
|
||||
// Set up
|
||||
this.container = new THREE.Object3D()
|
||||
this.container.matrixAutoUpdate = false
|
||||
|
||||
this.setStatic()
|
||||
this.setBaguettes()
|
||||
this.setLinks()
|
||||
this.setActivities()
|
||||
this.setTiles()
|
||||
}
|
||||
|
||||
setStatic()
|
||||
{
|
||||
this.objects.add({
|
||||
base: this.resources.items.informationStaticBase.scene,
|
||||
collision: this.resources.items.informationStaticCollision.scene,
|
||||
floorShadowTexture: this.resources.items.informationStaticFloorShadowTexture,
|
||||
offset: new THREE.Vector3(this.x, this.y, 0),
|
||||
mass: 0
|
||||
})
|
||||
}
|
||||
|
||||
setBaguettes()
|
||||
{
|
||||
this.baguettes = {}
|
||||
|
||||
this.baguettes.x = - 4
|
||||
this.baguettes.y = 6
|
||||
|
||||
this.baguettes.a = this.objects.add({
|
||||
base: this.resources.items.informationBaguetteBase.scene,
|
||||
collision: this.resources.items.informationBaguetteCollision.scene,
|
||||
offset: new THREE.Vector3(this.x + this.baguettes.x - 0.56, this.y + this.baguettes.y - 0.666, 0.2),
|
||||
rotation: new THREE.Euler(0, 0, - Math.PI * 37 / 180),
|
||||
duplicated: true,
|
||||
shadow: { sizeX: 0.6, sizeY: 3.5, offsetZ: - 0.15, alpha: 0.35 },
|
||||
mass: 1.5,
|
||||
// soundName: 'woodHit'
|
||||
})
|
||||
|
||||
this.baguettes.b = this.objects.add({
|
||||
base: this.resources.items.informationBaguetteBase.scene,
|
||||
collision: this.resources.items.informationBaguetteCollision.scene,
|
||||
offset: new THREE.Vector3(this.x + this.baguettes.x - 0.8, this.y + this.baguettes.y - 2, 0.5),
|
||||
rotation: new THREE.Euler(0, - 0.5, Math.PI * 60 / 180),
|
||||
duplicated: true,
|
||||
shadow: { sizeX: 0.6, sizeY: 3.5, offsetZ: - 0.15, alpha: 0.35 },
|
||||
mass: 1.5,
|
||||
sleep: false,
|
||||
// soundName: 'woodHit'
|
||||
})
|
||||
}
|
||||
|
||||
setLinks()
|
||||
{
|
||||
// Set up
|
||||
this.links = {}
|
||||
this.links.x = 1.95
|
||||
this.links.y = - 1.5
|
||||
this.links.halfExtents = {}
|
||||
this.links.halfExtents.x = 1
|
||||
this.links.halfExtents.y = 1
|
||||
this.links.distanceBetween = 2.4
|
||||
this.links.labelWidth = this.links.halfExtents.x * 2 + 1
|
||||
this.links.labelGeometry = new THREE.PlaneBufferGeometry(this.links.labelWidth, this.links.labelWidth * 0.25, 1, 1)
|
||||
this.links.labelOffset = - 1.6
|
||||
this.links.items = []
|
||||
|
||||
this.links.container = new THREE.Object3D()
|
||||
this.links.container.matrixAutoUpdate = false
|
||||
this.container.add(this.links.container)
|
||||
|
||||
// Options
|
||||
this.links.options = [
|
||||
{
|
||||
href: 'https://twitter.com/bruno_simon/',
|
||||
labelTexture: this.resources.items.informationContactTwitterLabelTexture
|
||||
},
|
||||
{
|
||||
href: 'https://github.com/brunosimon/',
|
||||
labelTexture: this.resources.items.informationContactGithubLabelTexture
|
||||
},
|
||||
{
|
||||
href: 'https://www.linkedin.com/in/simonbruno77/',
|
||||
labelTexture: this.resources.items.informationContactLinkedinLabelTexture
|
||||
},
|
||||
{
|
||||
href: 'mailto:simon.bruno.77@gmail.com',
|
||||
labelTexture: this.resources.items.informationContactMailLabelTexture
|
||||
}
|
||||
]
|
||||
|
||||
// Create each link
|
||||
let i = 0
|
||||
for(const _option of this.links.options)
|
||||
{
|
||||
// Set up
|
||||
const item = {}
|
||||
item.x = this.x + this.links.x + this.links.distanceBetween * i
|
||||
item.y = this.y + this.links.y
|
||||
item.href = _option.href
|
||||
|
||||
// Create area
|
||||
item.area = this.areas.add({
|
||||
position: new THREE.Vector2(item.x, item.y),
|
||||
halfExtents: new THREE.Vector2(this.links.halfExtents.x, this.links.halfExtents.y)
|
||||
})
|
||||
item.area.on('interact', () =>
|
||||
{
|
||||
window.open(_option.href, '_blank')
|
||||
})
|
||||
|
||||
// Texture
|
||||
item.texture = _option.labelTexture
|
||||
item.texture.magFilter = THREE.NearestFilter
|
||||
item.texture.minFilter = THREE.LinearFilter
|
||||
|
||||
// Create label
|
||||
item.labelMesh = new THREE.Mesh(this.links.labelGeometry, new THREE.MeshBasicMaterial({ wireframe: false, color: 0xffffff, alphaMap: _option.labelTexture, depthTest: true, depthWrite: false, transparent: true }))
|
||||
item.labelMesh.position.x = item.x + this.links.labelWidth * 0.5 - this.links.halfExtents.x
|
||||
item.labelMesh.position.y = item.y + this.links.labelOffset
|
||||
item.labelMesh.matrixAutoUpdate = false
|
||||
item.labelMesh.updateMatrix()
|
||||
this.links.container.add(item.labelMesh)
|
||||
|
||||
// Save
|
||||
this.links.items.push(item)
|
||||
|
||||
i++
|
||||
}
|
||||
}
|
||||
|
||||
setActivities()
|
||||
{
|
||||
// Set up
|
||||
this.activities = {}
|
||||
this.activities.x = this.x + 0
|
||||
this.activities.y = this.y - 10
|
||||
this.activities.multiplier = 5.5
|
||||
|
||||
// Geometry
|
||||
this.activities.geometry = new THREE.PlaneBufferGeometry(2 * this.activities.multiplier, 1 * this.activities.multiplier, 1, 1)
|
||||
|
||||
// Texture
|
||||
this.activities.texture = this.resources.items.informationActivitiesTexture
|
||||
this.activities.texture.magFilter = THREE.NearestFilter
|
||||
this.activities.texture.minFilter = THREE.LinearFilter
|
||||
|
||||
// Material
|
||||
this.activities.material = new THREE.MeshBasicMaterial({ wireframe: false, color: 0xffffff, alphaMap: this.activities.texture, transparent: true })
|
||||
|
||||
// Mesh
|
||||
this.activities.mesh = new THREE.Mesh(this.activities.geometry, this.activities.material)
|
||||
this.activities.mesh.position.x = this.activities.x
|
||||
this.activities.mesh.position.y = this.activities.y
|
||||
this.activities.mesh.matrixAutoUpdate = false
|
||||
this.activities.mesh.updateMatrix()
|
||||
this.container.add(this.activities.mesh)
|
||||
}
|
||||
|
||||
setTiles()
|
||||
{
|
||||
this.tiles.add({
|
||||
start: new THREE.Vector2(this.x - 1.2, this.y + 13),
|
||||
delta: new THREE.Vector2(0, - 20)
|
||||
})
|
||||
}
|
||||
}
|
463
src/javascript/World/Sections/IntroSection.js
Normal file
@ -0,0 +1,463 @@
|
||||
import * as THREE from 'three'
|
||||
|
||||
export default class IntroSection
|
||||
{
|
||||
constructor(_options)
|
||||
{
|
||||
// Options
|
||||
this.config = _options.config
|
||||
this.time = _options.time
|
||||
this.resources = _options.resources
|
||||
this.objects = _options.objects
|
||||
this.areas = _options.areas
|
||||
this.walls = _options.walls
|
||||
this.tiles = _options.tiles
|
||||
this.debug = _options.debug
|
||||
this.x = _options.x
|
||||
this.y = _options.y
|
||||
|
||||
// Set up
|
||||
this.container = new THREE.Object3D()
|
||||
this.container.matrixAutoUpdate = false
|
||||
this.container.updateMatrix()
|
||||
|
||||
this.setStatic()
|
||||
this.setInstructions()
|
||||
this.setOtherInstructions()
|
||||
this.setTitles()
|
||||
this.setTiles()
|
||||
this.setDikes()
|
||||
}
|
||||
|
||||
setStatic()
|
||||
{
|
||||
this.objects.add({
|
||||
base: this.resources.items.introStaticBase.scene,
|
||||
collision: this.resources.items.introStaticCollision.scene,
|
||||
floorShadowTexture: this.resources.items.introStaticFloorShadowTexture,
|
||||
offset: new THREE.Vector3(0, 0, 0),
|
||||
mass: 0
|
||||
})
|
||||
}
|
||||
|
||||
setInstructions()
|
||||
{
|
||||
this.instructions = {}
|
||||
|
||||
/**
|
||||
* Arrows
|
||||
*/
|
||||
this.instructions.arrows = {}
|
||||
|
||||
// Label
|
||||
this.instructions.arrows.label = {}
|
||||
|
||||
this.instructions.arrows.label.texture = this.config.touch ? this.resources.items.introInstructionsControlsTexture : this.resources.items.introInstructionsArrowsTexture
|
||||
this.instructions.arrows.label.texture.magFilter = THREE.NearestFilter
|
||||
this.instructions.arrows.label.texture.minFilter = THREE.LinearFilter
|
||||
|
||||
this.instructions.arrows.label.material = new THREE.MeshBasicMaterial({ transparent: true, alphaMap: this.instructions.arrows.label.texture, color: 0xffffff, depthWrite: false, opacity: 0 })
|
||||
|
||||
this.instructions.arrows.label.geometry = this.resources.items.introInstructionsLabels.scene.children.find((_mesh) => _mesh.name === 'arrows').geometry
|
||||
|
||||
this.instructions.arrows.label.mesh = new THREE.Mesh(this.instructions.arrows.label.geometry, this.instructions.arrows.label.material)
|
||||
this.container.add(this.instructions.arrows.label.mesh)
|
||||
|
||||
if(!this.config.touch)
|
||||
{
|
||||
// Keys
|
||||
this.instructions.arrows.up = this.objects.add({
|
||||
base: this.resources.items.introArrowKeyBase.scene,
|
||||
collision: this.resources.items.introArrowKeyCollision.scene,
|
||||
offset: new THREE.Vector3(0, 0, 0),
|
||||
rotation: new THREE.Euler(0, 0, 0),
|
||||
duplicated: true,
|
||||
shadow: { sizeX: 1, sizeY: 1, offsetZ: - 0.2, alpha: 0.5 },
|
||||
mass: 1.5,
|
||||
soundName: 'brick'
|
||||
})
|
||||
this.instructions.arrows.down = this.objects.add({
|
||||
base: this.resources.items.introArrowKeyBase.scene,
|
||||
collision: this.resources.items.introArrowKeyCollision.scene,
|
||||
offset: new THREE.Vector3(0, - 0.8, 0),
|
||||
rotation: new THREE.Euler(0, 0, Math.PI),
|
||||
duplicated: true,
|
||||
shadow: { sizeX: 1, sizeY: 1, offsetZ: - 0.2, alpha: 0.5 },
|
||||
mass: 1.5,
|
||||
soundName: 'brick'
|
||||
})
|
||||
this.instructions.arrows.left = this.objects.add({
|
||||
base: this.resources.items.introArrowKeyBase.scene,
|
||||
collision: this.resources.items.introArrowKeyCollision.scene,
|
||||
offset: new THREE.Vector3(- 0.8, - 0.8, 0),
|
||||
rotation: new THREE.Euler(0, 0, Math.PI * 0.5),
|
||||
duplicated: true,
|
||||
shadow: { sizeX: 1, sizeY: 1, offsetZ: - 0.2, alpha: 0.5 },
|
||||
mass: 1.5,
|
||||
soundName: 'brick'
|
||||
})
|
||||
this.instructions.arrows.right = this.objects.add({
|
||||
base: this.resources.items.introArrowKeyBase.scene,
|
||||
collision: this.resources.items.introArrowKeyCollision.scene,
|
||||
offset: new THREE.Vector3(0.8, - 0.8, 0),
|
||||
rotation: new THREE.Euler(0, 0, - Math.PI * 0.5),
|
||||
duplicated: true,
|
||||
shadow: { sizeX: 1, sizeY: 1, offsetZ: - 0.2, alpha: 0.5 },
|
||||
mass: 1.5,
|
||||
soundName: 'brick'
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
setOtherInstructions()
|
||||
{
|
||||
if(this.config.touch)
|
||||
{
|
||||
return
|
||||
}
|
||||
|
||||
this.otherInstructions = {}
|
||||
this.otherInstructions.x = 16
|
||||
this.otherInstructions.y = - 2
|
||||
|
||||
// Container
|
||||
this.otherInstructions.container = new THREE.Object3D()
|
||||
this.otherInstructions.container.position.x = this.otherInstructions.x
|
||||
this.otherInstructions.container.position.y = this.otherInstructions.y
|
||||
this.otherInstructions.container.matrixAutoUpdate = false
|
||||
this.otherInstructions.container.updateMatrix()
|
||||
this.container.add(this.otherInstructions.container)
|
||||
|
||||
// Label
|
||||
this.otherInstructions.label = {}
|
||||
|
||||
this.otherInstructions.label.geometry = new THREE.PlaneBufferGeometry(6, 6, 1, 1)
|
||||
|
||||
this.otherInstructions.label.texture = this.resources.items.introInstructionsOtherTexture
|
||||
this.otherInstructions.label.texture.magFilter = THREE.NearestFilter
|
||||
this.otherInstructions.label.texture.minFilter = THREE.LinearFilter
|
||||
|
||||
this.otherInstructions.label.material = new THREE.MeshBasicMaterial({ transparent: true, alphaMap: this.otherInstructions.label.texture, color: 0xffffff, depthWrite: false, opacity: 0 })
|
||||
|
||||
this.otherInstructions.label.mesh = new THREE.Mesh(this.otherInstructions.label.geometry, this.otherInstructions.label.material)
|
||||
this.otherInstructions.label.mesh.matrixAutoUpdate = false
|
||||
this.otherInstructions.container.add(this.otherInstructions.label.mesh)
|
||||
|
||||
// Horn
|
||||
this.otherInstructions.horn = this.objects.add({
|
||||
base: this.resources.items.hornBase.scene,
|
||||
collision: this.resources.items.hornCollision.scene,
|
||||
offset: new THREE.Vector3(this.otherInstructions.x + 1.25, this.otherInstructions.y - 2.75, 0.2),
|
||||
rotation: new THREE.Euler(0, 0, 0.5),
|
||||
duplicated: true,
|
||||
shadow: { sizeX: 1.65, sizeY: 0.75, offsetZ: - 0.1, alpha: 0.4 },
|
||||
mass: 1.5,
|
||||
soundName: 'horn',
|
||||
sleep: false
|
||||
})
|
||||
}
|
||||
|
||||
setTitles()
|
||||
{
|
||||
// Title
|
||||
this.objects.add({
|
||||
base: this.resources.items.introBBase.scene,
|
||||
collision: this.resources.items.introBCollision.scene,
|
||||
offset: new THREE.Vector3(0, 0, 0),
|
||||
rotation: new THREE.Euler(0, 0, 0),
|
||||
shadow: { sizeX: 1.5, sizeY: 1.5, offsetZ: - 0.6, alpha: 0.4 },
|
||||
mass: 1.5,
|
||||
soundName: 'brick'
|
||||
})
|
||||
this.objects.add({
|
||||
base: this.resources.items.introRBase.scene,
|
||||
collision: this.resources.items.introRCollision.scene,
|
||||
offset: new THREE.Vector3(0, 0, 0),
|
||||
rotation: new THREE.Euler(0, 0, 0),
|
||||
shadow: { sizeX: 1.5, sizeY: 1.5, offsetZ: - 0.6, alpha: 0.4 },
|
||||
mass: 1.5,
|
||||
soundName: 'brick'
|
||||
})
|
||||
this.objects.add({
|
||||
base: this.resources.items.introUBase.scene,
|
||||
collision: this.resources.items.introUCollision.scene,
|
||||
offset: new THREE.Vector3(0, 0, 0),
|
||||
rotation: new THREE.Euler(0, 0, 0),
|
||||
shadow: { sizeX: 1.5, sizeY: 1.5, offsetZ: - 0.6, alpha: 0.4 },
|
||||
mass: 1.5,
|
||||
soundName: 'brick'
|
||||
})
|
||||
this.objects.add({
|
||||
base: this.resources.items.introNBase.scene,
|
||||
collision: this.resources.items.introNCollision.scene,
|
||||
offset: new THREE.Vector3(0, 0, 0),
|
||||
rotation: new THREE.Euler(0, 0, 0),
|
||||
duplicated: true,
|
||||
shadow: { sizeX: 1.5, sizeY: 1.5, offsetZ: - 0.6, alpha: 0.4 },
|
||||
mass: 1.5,
|
||||
soundName: 'brick'
|
||||
})
|
||||
this.objects.add({
|
||||
base: this.resources.items.introOBase.scene,
|
||||
collision: this.resources.items.introOCollision.scene,
|
||||
offset: new THREE.Vector3(0, 0, 0),
|
||||
rotation: new THREE.Euler(0, 0, 0),
|
||||
duplicated: true,
|
||||
shadow: { sizeX: 1.5, sizeY: 1.5, offsetZ: - 0.6, alpha: 0.4 },
|
||||
mass: 1.5,
|
||||
soundName: 'brick'
|
||||
})
|
||||
this.objects.add({
|
||||
base: this.resources.items.introSBase.scene,
|
||||
collision: this.resources.items.introSCollision.scene,
|
||||
offset: new THREE.Vector3(0, 0, 0),
|
||||
rotation: new THREE.Euler(0, 0, 0),
|
||||
shadow: { sizeX: 1.5, sizeY: 1.5, offsetZ: - 0.6, alpha: 0.4 },
|
||||
mass: 1.5,
|
||||
soundName: 'brick'
|
||||
})
|
||||
this.objects.add({
|
||||
base: this.resources.items.introIBase.scene,
|
||||
collision: this.resources.items.introICollision.scene,
|
||||
offset: new THREE.Vector3(0, 0, 0),
|
||||
rotation: new THREE.Euler(0, 0, 0),
|
||||
shadow: { sizeX: 1.5, sizeY: 1.5, offsetZ: - 0.6, alpha: 0.4 },
|
||||
mass: 1.5,
|
||||
soundName: 'brick'
|
||||
})
|
||||
this.objects.add({
|
||||
base: this.resources.items.introMBase.scene,
|
||||
collision: this.resources.items.introMCollision.scene,
|
||||
offset: new THREE.Vector3(0, 0, 0),
|
||||
rotation: new THREE.Euler(0, 0, 0),
|
||||
shadow: { sizeX: 1.5, sizeY: 1.5, offsetZ: - 0.6, alpha: 0.4 },
|
||||
mass: 1.5,
|
||||
soundName: 'brick'
|
||||
})
|
||||
this.objects.add({
|
||||
base: this.resources.items.introOBase.scene,
|
||||
collision: this.resources.items.introOCollision.scene,
|
||||
offset: new THREE.Vector3(3.95, 0, 0),
|
||||
rotation: new THREE.Euler(0, 0, 0),
|
||||
duplicated: true,
|
||||
shadow: { sizeX: 1.5, sizeY: 1.5, offsetZ: - 0.6, alpha: 0.4 },
|
||||
mass: 1.5,
|
||||
soundName: 'brick'
|
||||
})
|
||||
this.objects.add({
|
||||
base: this.resources.items.introNBase.scene,
|
||||
collision: this.resources.items.introNCollision.scene,
|
||||
offset: new THREE.Vector3(5.85, 0, 0),
|
||||
rotation: new THREE.Euler(0, 0, 0),
|
||||
duplicated: true,
|
||||
shadow: { sizeX: 1.5, sizeY: 1.5, offsetZ: - 0.6, alpha: 0.4 },
|
||||
mass: 1.5,
|
||||
soundName: 'brick'
|
||||
})
|
||||
this.objects.add({
|
||||
base: this.resources.items.introCreativeBase.scene,
|
||||
collision: this.resources.items.introCreativeCollision.scene,
|
||||
offset: new THREE.Vector3(0, 0, 0),
|
||||
rotation: new THREE.Euler(0, 0, 0.25),
|
||||
shadow: { sizeX: 5, sizeY: 1.5, offsetZ: - 0.6, alpha: 0.3 },
|
||||
mass: 1.5,
|
||||
sleep: false,
|
||||
soundName: 'brick'
|
||||
})
|
||||
this.objects.add({
|
||||
base: this.resources.items.introDevBase.scene,
|
||||
collision: this.resources.items.introDevCollision.scene,
|
||||
offset: new THREE.Vector3(0, 0, 0),
|
||||
rotation: new THREE.Euler(0, 0, 0),
|
||||
shadow: { sizeX: 2.5, sizeY: 1.5, offsetZ: - 0.6, alpha: 0.3 },
|
||||
mass: 1.5,
|
||||
soundName: 'brick'
|
||||
})
|
||||
}
|
||||
|
||||
setTiles()
|
||||
{
|
||||
this.tiles.add({
|
||||
start: new THREE.Vector2(0, - 4.5),
|
||||
delta: new THREE.Vector2(0, - 4.5)
|
||||
})
|
||||
}
|
||||
|
||||
setDikes()
|
||||
{
|
||||
this.dikes = {}
|
||||
this.dikes.brickOptions = {
|
||||
base: this.resources.items.brickBase.scene,
|
||||
collision: this.resources.items.brickCollision.scene,
|
||||
offset: new THREE.Vector3(0, 0, 0.1),
|
||||
rotation: new THREE.Euler(0, 0, 0),
|
||||
duplicated: true,
|
||||
shadow: { sizeX: 1.2, sizeY: 1.8, offsetZ: - 0.15, alpha: 0.35 },
|
||||
mass: 0.5,
|
||||
soundName: 'brick'
|
||||
}
|
||||
|
||||
// this.walls.add({
|
||||
// object:
|
||||
// {
|
||||
// ...this.dikes.brickOptions,
|
||||
// rotation: new THREE.Euler(0, 0, Math.PI * 0.5)
|
||||
// },
|
||||
// shape:
|
||||
// {
|
||||
// type: 'brick',
|
||||
// equilibrateLastLine: true,
|
||||
// widthCount: 3,
|
||||
// heightCount: 2,
|
||||
// position: new THREE.Vector3(this.x + 0, this.y - 4, 0),
|
||||
// offsetWidth: new THREE.Vector3(1.05, 0, 0),
|
||||
// offsetHeight: new THREE.Vector3(0, 0, 0.45),
|
||||
// randomOffset: new THREE.Vector3(0, 0, 0),
|
||||
// randomRotation: new THREE.Vector3(0, 0, 0.2)
|
||||
// }
|
||||
// })
|
||||
|
||||
this.walls.add({
|
||||
object: this.dikes.brickOptions,
|
||||
shape:
|
||||
{
|
||||
type: 'brick',
|
||||
equilibrateLastLine: true,
|
||||
widthCount: 5,
|
||||
heightCount: 2,
|
||||
position: new THREE.Vector3(this.x - 12, this.y - 13, 0),
|
||||
offsetWidth: new THREE.Vector3(0, 1.05, 0),
|
||||
offsetHeight: new THREE.Vector3(0, 0, 0.45),
|
||||
randomOffset: new THREE.Vector3(0, 0, 0),
|
||||
randomRotation: new THREE.Vector3(0, 0, 0.2)
|
||||
}
|
||||
})
|
||||
|
||||
this.walls.add({
|
||||
object:
|
||||
{
|
||||
...this.dikes.brickOptions,
|
||||
rotation: new THREE.Euler(0, 0, Math.PI * 0.5)
|
||||
},
|
||||
shape:
|
||||
{
|
||||
type: 'brick',
|
||||
equilibrateLastLine: true,
|
||||
widthCount: 3,
|
||||
heightCount: 2,
|
||||
position: new THREE.Vector3(this.x + 8, this.y + 6, 0),
|
||||
offsetWidth: new THREE.Vector3(1.05, 0, 0),
|
||||
offsetHeight: new THREE.Vector3(0, 0, 0.45),
|
||||
randomOffset: new THREE.Vector3(0, 0, 0),
|
||||
randomRotation: new THREE.Vector3(0, 0, 0.2)
|
||||
}
|
||||
})
|
||||
|
||||
this.walls.add({
|
||||
object: this.dikes.brickOptions,
|
||||
shape:
|
||||
{
|
||||
type: 'brick',
|
||||
equilibrateLastLine: false,
|
||||
widthCount: 3,
|
||||
heightCount: 2,
|
||||
position: new THREE.Vector3(this.x + 9.9, this.y + 4.7, 0),
|
||||
offsetWidth: new THREE.Vector3(0, - 1.05, 0),
|
||||
offsetHeight: new THREE.Vector3(0, 0, 0.45),
|
||||
randomOffset: new THREE.Vector3(0, 0, 0),
|
||||
randomRotation: new THREE.Vector3(0, 0, 0.2)
|
||||
}
|
||||
})
|
||||
|
||||
this.walls.add({
|
||||
object:
|
||||
{
|
||||
...this.dikes.brickOptions,
|
||||
rotation: new THREE.Euler(0, 0, Math.PI * 0.5)
|
||||
},
|
||||
shape:
|
||||
{
|
||||
type: 'brick',
|
||||
equilibrateLastLine: true,
|
||||
widthCount: 3,
|
||||
heightCount: 2,
|
||||
position: new THREE.Vector3(this.x - 14, this.y + 2, 0),
|
||||
offsetWidth: new THREE.Vector3(1.05, 0, 0),
|
||||
offsetHeight: new THREE.Vector3(0, 0, 0.45),
|
||||
randomOffset: new THREE.Vector3(0, 0, 0),
|
||||
randomRotation: new THREE.Vector3(0, 0, 0.2)
|
||||
}
|
||||
})
|
||||
|
||||
this.walls.add({
|
||||
object: this.dikes.brickOptions,
|
||||
shape:
|
||||
{
|
||||
type: 'brick',
|
||||
equilibrateLastLine: false,
|
||||
widthCount: 3,
|
||||
heightCount: 2,
|
||||
position: new THREE.Vector3(this.x - 14.8, this.y + 0.7, 0),
|
||||
offsetWidth: new THREE.Vector3(0, - 1.05, 0),
|
||||
offsetHeight: new THREE.Vector3(0, 0, 0.45),
|
||||
randomOffset: new THREE.Vector3(0, 0, 0),
|
||||
randomRotation: new THREE.Vector3(0, 0, 0.2)
|
||||
}
|
||||
})
|
||||
|
||||
this.walls.add({
|
||||
object: this.dikes.brickOptions,
|
||||
shape:
|
||||
{
|
||||
type: 'brick',
|
||||
equilibrateLastLine: true,
|
||||
widthCount: 3,
|
||||
heightCount: 2,
|
||||
position: new THREE.Vector3(this.x - 14.8, this.y - 3.5, 0),
|
||||
offsetWidth: new THREE.Vector3(0, - 1.05, 0),
|
||||
offsetHeight: new THREE.Vector3(0, 0, 0.45),
|
||||
randomOffset: new THREE.Vector3(0, 0, 0),
|
||||
randomRotation: new THREE.Vector3(0, 0, 0.2)
|
||||
}
|
||||
})
|
||||
|
||||
if(!this.config.touch)
|
||||
{
|
||||
this.walls.add({
|
||||
object:
|
||||
{
|
||||
...this.dikes.brickOptions,
|
||||
rotation: new THREE.Euler(0, 0, Math.PI * 0.5)
|
||||
},
|
||||
shape:
|
||||
{
|
||||
type: 'brick',
|
||||
equilibrateLastLine: true,
|
||||
widthCount: 2,
|
||||
heightCount: 2,
|
||||
position: new THREE.Vector3(this.x + 18.5, this.y + 3, 0),
|
||||
offsetWidth: new THREE.Vector3(1.05, 0, 0),
|
||||
offsetHeight: new THREE.Vector3(0, 0, 0.45),
|
||||
randomOffset: new THREE.Vector3(0, 0, 0),
|
||||
randomRotation: new THREE.Vector3(0, 0, 0.2)
|
||||
}
|
||||
})
|
||||
|
||||
this.walls.add({
|
||||
object: this.dikes.brickOptions,
|
||||
shape:
|
||||
{
|
||||
type: 'brick',
|
||||
equilibrateLastLine: false,
|
||||
widthCount: 2,
|
||||
heightCount: 2,
|
||||
position: new THREE.Vector3(this.x + 19.9, this.y + 2.2, 0),
|
||||
offsetWidth: new THREE.Vector3(0, - 1.05, 0),
|
||||
offsetHeight: new THREE.Vector3(0, 0, 0.45),
|
||||
randomOffset: new THREE.Vector3(0, 0, 0),
|
||||
randomRotation: new THREE.Vector3(0, 0, 0.2)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
229
src/javascript/World/Sections/PlaygroundSection.js
Normal file
@ -0,0 +1,229 @@
|
||||
import * as THREE from 'three'
|
||||
|
||||
export default class PlaygroundSection
|
||||
{
|
||||
constructor(_options)
|
||||
{
|
||||
// Options
|
||||
this.time = _options.time
|
||||
this.resources = _options.resources
|
||||
this.objects = _options.objects
|
||||
this.areas = _options.areas
|
||||
this.walls = _options.walls
|
||||
this.tiles = _options.tiles
|
||||
this.debug = _options.debug
|
||||
this.x = _options.x
|
||||
this.y = _options.y
|
||||
|
||||
// Debug
|
||||
if(this.debug)
|
||||
{
|
||||
this.debugFolder = this.debug.addFolder('playgroundSection')
|
||||
// this.debugFolder.open()
|
||||
}
|
||||
|
||||
// Set up
|
||||
this.container = new THREE.Object3D()
|
||||
this.container.matrixAutoUpdate = false
|
||||
|
||||
this.resources.items.areaResetTexture.magFilter = THREE.NearestFilter
|
||||
this.resources.items.areaResetTexture.minFilter = THREE.LinearFilter
|
||||
|
||||
this.setStatic()
|
||||
this.setBricksWalls()
|
||||
this.setBowling()
|
||||
}
|
||||
|
||||
setStatic()
|
||||
{
|
||||
this.objects.add({
|
||||
base: this.resources.items.playgroundStaticBase.scene,
|
||||
collision: this.resources.items.playgroundStaticCollision.scene,
|
||||
floorShadowTexture: this.resources.items.playgroundStaticFloorShadowTexture,
|
||||
offset: new THREE.Vector3(this.x, this.y, 0),
|
||||
mass: 0
|
||||
})
|
||||
}
|
||||
|
||||
setBricksWalls()
|
||||
{
|
||||
// Set up
|
||||
this.brickWalls = {}
|
||||
this.brickWalls.x = this.x + 15
|
||||
this.brickWalls.y = this.y + 14
|
||||
this.brickWalls.items = []
|
||||
|
||||
// Brick options
|
||||
this.brickWalls.brickOptions = {
|
||||
base: this.resources.items.brickBase.scene,
|
||||
collision: this.resources.items.brickCollision.scene,
|
||||
offset: new THREE.Vector3(0, 0, 0.1),
|
||||
rotation: new THREE.Euler(0, 0, 0),
|
||||
duplicated: true,
|
||||
shadow: { sizeX: 1.2, sizeY: 1.8, offsetZ: - 0.15, alpha: 0.35 },
|
||||
mass: 0.5,
|
||||
soundName: 'brick'
|
||||
}
|
||||
|
||||
this.brickWalls.items.push(
|
||||
this.walls.add({
|
||||
object: this.brickWalls.brickOptions,
|
||||
shape:
|
||||
{
|
||||
type: 'rectangle',
|
||||
widthCount: 5,
|
||||
heightCount: 6,
|
||||
position: new THREE.Vector3(this.brickWalls.x - 6, this.brickWalls.y, 0),
|
||||
offsetWidth: new THREE.Vector3(0, 1.05, 0),
|
||||
offsetHeight: new THREE.Vector3(0, 0, 0.45),
|
||||
randomOffset: new THREE.Vector3(0, 0, 0),
|
||||
randomRotation: new THREE.Vector3(0, 0, 0.4)
|
||||
}
|
||||
}),
|
||||
this.walls.add({
|
||||
object: this.brickWalls.brickOptions,
|
||||
shape:
|
||||
{
|
||||
type: 'brick',
|
||||
widthCount: 5,
|
||||
heightCount: 6,
|
||||
position: new THREE.Vector3(this.brickWalls.x - 12, this.brickWalls.y, 0),
|
||||
offsetWidth: new THREE.Vector3(0, 1.05, 0),
|
||||
offsetHeight: new THREE.Vector3(0, 0, 0.45),
|
||||
randomOffset: new THREE.Vector3(0, 0, 0),
|
||||
randomRotation: new THREE.Vector3(0, 0, 0.4)
|
||||
}
|
||||
}),
|
||||
this.walls.add({
|
||||
object: this.brickWalls.brickOptions,
|
||||
shape:
|
||||
{
|
||||
type: 'triangle',
|
||||
widthCount: 6,
|
||||
position: new THREE.Vector3(this.brickWalls.x - 18, this.brickWalls.y, 0),
|
||||
offsetWidth: new THREE.Vector3(0, 1.05, 0),
|
||||
offsetHeight: new THREE.Vector3(0, 0, 0.45),
|
||||
randomOffset: new THREE.Vector3(0, 0, 0),
|
||||
randomRotation: new THREE.Vector3(0, 0, 0.4)
|
||||
}
|
||||
})
|
||||
)
|
||||
|
||||
// Reset
|
||||
this.brickWalls.reset = () =>
|
||||
{
|
||||
for(const _wall of this.brickWalls.items)
|
||||
{
|
||||
for(const _brick of _wall.items)
|
||||
{
|
||||
_brick.collision.reset()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Reset area
|
||||
this.brickWalls.resetArea = this.areas.add({
|
||||
position: new THREE.Vector2(this.brickWalls.x, this.brickWalls.y),
|
||||
halfExtents: new THREE.Vector2(2, 2)
|
||||
})
|
||||
this.brickWalls.resetArea.on('interact', () =>
|
||||
{
|
||||
this.brickWalls.reset()
|
||||
})
|
||||
|
||||
// Reset label
|
||||
this.brickWalls.areaLabelMesh = new THREE.Mesh(new THREE.PlaneBufferGeometry(2, 0.5), new THREE.MeshBasicMaterial({ transparent: true, depthWrite: false, color: 0xffffff, alphaMap: this.resources.items.areaResetTexture }))
|
||||
this.brickWalls.areaLabelMesh.position.x = this.brickWalls.x
|
||||
this.brickWalls.areaLabelMesh.position.y = this.brickWalls.y
|
||||
this.brickWalls.areaLabelMesh.matrixAutoUpdate = false
|
||||
this.brickWalls.areaLabelMesh.updateMatrix()
|
||||
this.container.add(this.brickWalls.areaLabelMesh)
|
||||
|
||||
// Debug
|
||||
if(this.debugFolder)
|
||||
{
|
||||
this.debugFolder.add(this.brickWalls, 'reset').name('brickWalls reset')
|
||||
}
|
||||
}
|
||||
|
||||
setBowling()
|
||||
{
|
||||
this.bowling = {}
|
||||
this.bowling.x = this.x + 15
|
||||
this.bowling.y = this.y + 4
|
||||
|
||||
this.bowling.pins = this.walls.add({
|
||||
object:
|
||||
{
|
||||
base: this.resources.items.bowlingPinBase.scene,
|
||||
collision: this.resources.items.bowlingPinCollision.scene,
|
||||
offset: new THREE.Vector3(0, 0, 0.1),
|
||||
rotation: new THREE.Euler(0, 0, 0),
|
||||
duplicated: true,
|
||||
shadow: { sizeX: 1.4, sizeY: 1.4, offsetZ: - 0.15, alpha: 0.35 },
|
||||
mass: 0.1,
|
||||
soundName: 'bowlingPin'
|
||||
// sleep: false
|
||||
},
|
||||
shape:
|
||||
{
|
||||
type: 'triangle',
|
||||
widthCount: 4,
|
||||
position: new THREE.Vector3(this.bowling.x - 25, this.bowling.y, 0),
|
||||
offsetWidth: new THREE.Vector3(0, 1, 0),
|
||||
offsetHeight: new THREE.Vector3(0.65, 0, 0),
|
||||
randomOffset: new THREE.Vector3(0, 0, 0),
|
||||
randomRotation: new THREE.Vector3(0, 0, 0)
|
||||
}
|
||||
})
|
||||
|
||||
this.bowling.ball = this.objects.add({
|
||||
base: this.resources.items.bowlingBallBase.scene,
|
||||
collision: this.resources.items.bowlingBallCollision.scene,
|
||||
offset: new THREE.Vector3(this.bowling.x - 5, this.bowling.y, 0),
|
||||
rotation: new THREE.Euler(Math.PI * 0.5, 0, 0),
|
||||
duplicated: true,
|
||||
shadow: { sizeX: 1.5, sizeY: 1.5, offsetZ: - 0.15, alpha: 0.35 },
|
||||
mass: 1,
|
||||
soundName: 'bowlingBall'
|
||||
// sleep: false
|
||||
})
|
||||
|
||||
// Reset
|
||||
this.bowling.reset = () =>
|
||||
{
|
||||
// Reset pins
|
||||
for(const _pin of this.bowling.pins.items)
|
||||
{
|
||||
_pin.collision.reset()
|
||||
}
|
||||
|
||||
// Reset ball
|
||||
this.bowling.ball.collision.reset()
|
||||
}
|
||||
|
||||
// Reset area
|
||||
this.bowling.resetArea = this.areas.add({
|
||||
position: new THREE.Vector2(this.bowling.x, this.bowling.y),
|
||||
halfExtents: new THREE.Vector2(2, 2)
|
||||
})
|
||||
this.bowling.resetArea.on('interact', () =>
|
||||
{
|
||||
this.bowling.reset()
|
||||
})
|
||||
|
||||
// Reset label
|
||||
this.bowling.areaLabelMesh = new THREE.Mesh(new THREE.PlaneBufferGeometry(2, 0.5), new THREE.MeshBasicMaterial({ transparent: true, depthWrite: false, color: 0xffffff, alphaMap: this.resources.items.areaResetTexture }))
|
||||
this.bowling.areaLabelMesh.position.x = this.bowling.x
|
||||
this.bowling.areaLabelMesh.position.y = this.bowling.y
|
||||
this.bowling.areaLabelMesh.matrixAutoUpdate = false
|
||||
this.bowling.areaLabelMesh.updateMatrix()
|
||||
this.container.add(this.bowling.areaLabelMesh)
|
||||
|
||||
// Debug
|
||||
if(this.debugFolder)
|
||||
{
|
||||
this.debugFolder.add(this.bowling, 'reset').name('bowling reset')
|
||||
}
|
||||
}
|
||||
}
|
208
src/javascript/World/Sections/Project.js
Normal file
@ -0,0 +1,208 @@
|
||||
import * as THREE from 'three'
|
||||
|
||||
import ProjectBoardMaterial from '../../Materials/ProjectBoard.js'
|
||||
import TweenLite from 'gsap/TweenLite'
|
||||
import { Power4 } from 'gsap/EasePack'
|
||||
|
||||
export default class Project
|
||||
{
|
||||
constructor(_options)
|
||||
{
|
||||
// Options
|
||||
this.time = _options.time
|
||||
this.resources = _options.resources
|
||||
this.objects = _options.objects
|
||||
this.areas = _options.areas
|
||||
this.name = _options.name
|
||||
this.geometries = _options.geometries
|
||||
this.meshes = _options.meshes
|
||||
this.debug = _options.debug
|
||||
this.name = _options.name
|
||||
this.x = _options.x
|
||||
this.y = _options.y
|
||||
this.imageSources = _options.imageSources
|
||||
this.floorTexture = _options.floorTexture
|
||||
this.link = _options.link
|
||||
this.distinctions = _options.distinctions
|
||||
|
||||
// Set up
|
||||
this.container = new THREE.Object3D()
|
||||
this.container.matrixAutoUpdate = false
|
||||
// this.container.updateMatrix()
|
||||
|
||||
this.setBoards()
|
||||
this.setFloor()
|
||||
}
|
||||
|
||||
setBoards()
|
||||
{
|
||||
// Set up
|
||||
this.boards = {}
|
||||
this.boards.items = []
|
||||
this.boards.xStart = - 5
|
||||
this.boards.xInter = 5
|
||||
this.boards.y = 5
|
||||
this.boards.color = '#8e7161'
|
||||
this.boards.threeColor = new THREE.Color(this.boards.color)
|
||||
|
||||
if(this.debug)
|
||||
{
|
||||
this.debug.addColor(this.boards, 'color').name('boardColor').onChange(() =>
|
||||
{
|
||||
this.boards.threeColor.set(this.boards.color)
|
||||
})
|
||||
}
|
||||
|
||||
// Create each board
|
||||
let i = 0
|
||||
|
||||
for(const _imageSource of this.imageSources)
|
||||
{
|
||||
// Set up
|
||||
const board = {}
|
||||
board.x = this.x + this.boards.xStart + i * this.boards.xInter
|
||||
board.y = this.y + this.boards.y
|
||||
|
||||
// Create structure with collision
|
||||
this.objects.add({
|
||||
base: this.resources.items.projectsBoardStructure.scene,
|
||||
collision: this.resources.items.projectsBoardCollision.scene,
|
||||
floorShadowTexture: this.resources.items.projectsBoardStructureFloorShadowTexture,
|
||||
offset: new THREE.Vector3(board.x, board.y, 0),
|
||||
rotation: new THREE.Euler(0, 0, 0),
|
||||
duplicated: true,
|
||||
mass: 0
|
||||
})
|
||||
|
||||
// Image load
|
||||
const image = new Image()
|
||||
image.addEventListener('load', () =>
|
||||
{
|
||||
board.texture = new THREE.Texture(image)
|
||||
board.texture.needsUpdate = true
|
||||
board.texture.magFilter = THREE.NearestFilter
|
||||
board.texture.minFilter = THREE.LinearFilter
|
||||
|
||||
board.planeMesh.material.uniforms.uTexture.value = board.texture
|
||||
|
||||
TweenLite.to(board.planeMesh.material.uniforms.uTextureAlpha, 1, { value: 1, ease: Power4.inOut })
|
||||
})
|
||||
|
||||
image.src = _imageSource
|
||||
|
||||
// Plane
|
||||
board.planeMesh = this.meshes.boardPlane.clone()
|
||||
board.planeMesh.position.x = board.x
|
||||
board.planeMesh.position.y = board.y
|
||||
board.planeMesh.matrixAutoUpdate = false
|
||||
board.planeMesh.updateMatrix()
|
||||
board.planeMesh.material = new ProjectBoardMaterial()
|
||||
board.planeMesh.material.uniforms.uColor.value = this.boards.threeColor
|
||||
board.planeMesh.material.uniforms.uTextureAlpha.value = 0
|
||||
this.container.add(board.planeMesh)
|
||||
|
||||
// Save
|
||||
this.boards.items.push(board)
|
||||
|
||||
i++
|
||||
}
|
||||
}
|
||||
|
||||
setFloor()
|
||||
{
|
||||
this.floor = {}
|
||||
|
||||
this.floor.x = 0
|
||||
this.floor.y = - 2
|
||||
|
||||
// Container
|
||||
this.floor.container = new THREE.Object3D()
|
||||
this.floor.container.position.x = this.x + this.floor.x
|
||||
this.floor.container.position.y = this.y + this.floor.y
|
||||
this.floor.container.matrixAutoUpdate = false
|
||||
this.floor.container.updateMatrix()
|
||||
this.container.add(this.floor.container)
|
||||
|
||||
// Texture
|
||||
this.floor.texture = this.floorTexture
|
||||
this.floor.texture.magFilter = THREE.NearestFilter
|
||||
this.floor.texture.minFilter = THREE.LinearFilter
|
||||
|
||||
// Geometry
|
||||
this.floor.geometry = this.geometries.floor
|
||||
|
||||
// Material
|
||||
this.floor.material = new THREE.MeshBasicMaterial({ transparent: true, depthWrite: false, alphaMap: this.floor.texture })
|
||||
|
||||
// Mesh
|
||||
this.floor.mesh = new THREE.Mesh(this.floor.geometry, this.floor.material)
|
||||
this.floor.mesh.matrixAutoUpdate = false
|
||||
this.floor.container.add(this.floor.mesh)
|
||||
|
||||
// Distinctions
|
||||
if(this.distinctions)
|
||||
{
|
||||
for(const _distinction of this.distinctions)
|
||||
{
|
||||
let base = null
|
||||
let collision = null
|
||||
let shadowSizeX = null
|
||||
let shadowSizeY = null
|
||||
|
||||
switch(_distinction.type)
|
||||
{
|
||||
case 'awwwards':
|
||||
base = this.resources.items.projectsDistinctionsAwwwardsBase.scene
|
||||
collision = this.resources.items.projectsDistinctionsAwwwardsCollision.scene
|
||||
shadowSizeX = 1.5
|
||||
shadowSizeY = 1.5
|
||||
break
|
||||
|
||||
case 'fwa':
|
||||
base = this.resources.items.projectsDistinctionsFWABase.scene
|
||||
collision = this.resources.items.projectsDistinctionsFWACollision.scene
|
||||
shadowSizeX = 2
|
||||
shadowSizeY = 1
|
||||
break
|
||||
|
||||
case 'cssda':
|
||||
base = this.resources.items.projectsDistinctionsCSSDABase.scene
|
||||
collision = this.resources.items.projectsDistinctionsCSSDACollision.scene
|
||||
shadowSizeX = 1.2
|
||||
shadowSizeY = 1.2
|
||||
break
|
||||
}
|
||||
|
||||
this.objects.add({
|
||||
base: base,
|
||||
collision: collision,
|
||||
offset: new THREE.Vector3(this.x + this.floor.x + _distinction.x, this.y + this.floor.y + _distinction.y, 0),
|
||||
rotation: new THREE.Euler(0, 0, 0),
|
||||
duplicated: true,
|
||||
shadow: { sizeX: shadowSizeX, sizeY: shadowSizeY, offsetZ: - 0.1, alpha: 0.5 },
|
||||
mass: 1.5,
|
||||
soundName: 'woodHit'
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Area
|
||||
this.floor.area = this.areas.add({
|
||||
position: new THREE.Vector2(this.x + this.link.x, this.y + this.floor.y + this.link.y),
|
||||
halfExtents: new THREE.Vector2(this.link.halfExtents.x, this.link.halfExtents.y)
|
||||
})
|
||||
this.floor.area.on('interact', () =>
|
||||
{
|
||||
window.open(this.link.href, '_blank')
|
||||
})
|
||||
|
||||
// Area label
|
||||
this.floor.areaLabel = this.meshes.areaLabel.clone()
|
||||
this.floor.areaLabel.position.x = this.link.x
|
||||
this.floor.areaLabel.position.y = this.link.y
|
||||
this.floor.areaLabel.position.z = 0.001
|
||||
this.floor.areaLabel.matrixAutoUpdate = false
|
||||
this.floor.areaLabel.updateMatrix()
|
||||
this.floor.container.add(this.floor.areaLabel)
|
||||
}
|
||||
}
|
445
src/javascript/World/Sections/ProjectsSection.js
Normal file
@ -0,0 +1,445 @@
|
||||
import * as THREE from 'three'
|
||||
import Project from './Project'
|
||||
import TweenLite from 'gsap/TweenLite'
|
||||
|
||||
import projectsThreejsJourneySlideASources from '../../../models/projects/threejsJourney/slideA.jpg'
|
||||
import projectsThreejsJourneySlideBSources from '../../../models/projects/threejsJourney/slideB.jpg'
|
||||
import projectsThreejsJourneySlideCSources from '../../../models/projects/threejsJourney/slideC.jpg'
|
||||
import projectsThreejsJourneySlideDSources from '../../../models/projects/threejsJourney/slideD.jpg'
|
||||
|
||||
import projectsMadboxSlideASources from '../../../models/projects/madbox/slideA.jpg'
|
||||
import projectsMadboxSlideBSources from '../../../models/projects/madbox/slideB.jpg'
|
||||
import projectsMadboxSlideCSources from '../../../models/projects/madbox/slideC.jpg'
|
||||
|
||||
import projectsScoutSlideASources from '../../../models/projects/scout/slideA.jpg'
|
||||
import projectsScoutSlideBSources from '../../../models/projects/scout/slideB.jpg'
|
||||
import projectsScoutSlideCSources from '../../../models/projects/scout/slideC.jpg'
|
||||
|
||||
import projectsChartogneSlideASources from '../../../models/projects/chartogne/slideA.jpg'
|
||||
import projectsChartogneSlideBSources from '../../../models/projects/chartogne/slideB.jpg'
|
||||
import projectsChartogneSlideCSources from '../../../models/projects/chartogne/slideC.jpg'
|
||||
|
||||
import projectsZenlySlideASources from '../../../models/projects/zenly/slideA.jpg'
|
||||
import projectsZenlySlideBSources from '../../../models/projects/zenly/slideB.jpg'
|
||||
import projectsZenlySlideCSources from '../../../models/projects/zenly/slideC.jpg'
|
||||
|
||||
import projectsCitrixRedbullSlideASources from '../../../models/projects/citrixRedbull/slideA.jpg'
|
||||
import projectsCitrixRedbullSlideBSources from '../../../models/projects/citrixRedbull/slideB.jpg'
|
||||
import projectsCitrixRedbullSlideCSources from '../../../models/projects/citrixRedbull/slideC.jpg'
|
||||
|
||||
import projectsPriorHoldingsSlideASources from '../../../models/projects/priorHoldings/slideA.jpg'
|
||||
import projectsPriorHoldingsSlideBSources from '../../../models/projects/priorHoldings/slideB.jpg'
|
||||
import projectsPriorHoldingsSlideCSources from '../../../models/projects/priorHoldings/slideC.jpg'
|
||||
|
||||
import projectsOranoSlideASources from '../../../models/projects/orano/slideA.jpg'
|
||||
import projectsOranoSlideBSources from '../../../models/projects/orano/slideB.jpg'
|
||||
import projectsOranoSlideCSources from '../../../models/projects/orano/slideC.jpg'
|
||||
|
||||
// import projectsGleecChatSlideASources from '../../../models/projects/gleecChat/slideA.jpg'
|
||||
// import projectsGleecChatSlideBSources from '../../../models/projects/gleecChat/slideB.jpg'
|
||||
// import projectsGleecChatSlideCSources from '../../../models/projects/gleecChat/slideC.jpg'
|
||||
// import projectsGleecChatSlideDSources from '../../../models/projects/gleecChat/slideD.jpg'
|
||||
|
||||
import projectsKepplerSlideASources from '../../../models/projects/keppler/slideA.jpg'
|
||||
import projectsKepplerSlideBSources from '../../../models/projects/keppler/slideB.jpg'
|
||||
import projectsKepplerSlideCSources from '../../../models/projects/keppler/slideC.jpg'
|
||||
|
||||
export default class ProjectsSection
|
||||
{
|
||||
constructor(_options)
|
||||
{
|
||||
// Options
|
||||
this.time = _options.time
|
||||
this.resources = _options.resources
|
||||
this.camera = _options.camera
|
||||
this.passes = _options.passes
|
||||
this.objects = _options.objects
|
||||
this.areas = _options.areas
|
||||
this.zones = _options.zones
|
||||
this.tiles = _options.tiles
|
||||
this.debug = _options.debug
|
||||
this.x = _options.x
|
||||
this.y = _options.y
|
||||
|
||||
// Debug
|
||||
if(this.debug)
|
||||
{
|
||||
this.debugFolder = this.debug.addFolder('projects')
|
||||
this.debugFolder.open()
|
||||
}
|
||||
|
||||
// Set up
|
||||
this.items = []
|
||||
|
||||
this.interDistance = 24
|
||||
this.positionRandomess = 5
|
||||
this.projectHalfWidth = 9
|
||||
|
||||
this.container = new THREE.Object3D()
|
||||
this.container.matrixAutoUpdate = false
|
||||
this.container.updateMatrix()
|
||||
|
||||
this.setGeometries()
|
||||
this.setMeshes()
|
||||
this.setList()
|
||||
this.setZone()
|
||||
|
||||
// Add all project from the list
|
||||
for(const _options of this.list)
|
||||
{
|
||||
this.add(_options)
|
||||
}
|
||||
}
|
||||
|
||||
setGeometries()
|
||||
{
|
||||
this.geometries = {}
|
||||
this.geometries.floor = new THREE.PlaneBufferGeometry(16, 8)
|
||||
}
|
||||
|
||||
setMeshes()
|
||||
{
|
||||
this.meshes = {}
|
||||
|
||||
// this.meshes.boardStructure = this.objects.getConvertedMesh(this.resources.items.projectsBoardStructure.scene.children, { floorShadowTexture: this.resources.items.projectsBoardStructureFloorShadowTexture })
|
||||
this.resources.items.areaOpenTexture.magFilter = THREE.NearestFilter
|
||||
this.resources.items.areaOpenTexture.minFilter = THREE.LinearFilter
|
||||
this.meshes.boardPlane = this.resources.items.projectsBoardPlane.scene.children[0]
|
||||
this.meshes.areaLabel = new THREE.Mesh(new THREE.PlaneBufferGeometry(2, 0.5), new THREE.MeshBasicMaterial({ transparent: true, depthWrite: false, color: 0xffffff, alphaMap: this.resources.items.areaOpenTexture }))
|
||||
this.meshes.areaLabel.matrixAutoUpdate = false
|
||||
}
|
||||
|
||||
setList()
|
||||
{
|
||||
this.list = [
|
||||
{
|
||||
name: 'Three.js Journey',
|
||||
imageSources:
|
||||
[
|
||||
projectsThreejsJourneySlideASources,
|
||||
projectsThreejsJourneySlideBSources,
|
||||
projectsThreejsJourneySlideCSources,
|
||||
projectsThreejsJourneySlideDSources
|
||||
],
|
||||
floorTexture: this.resources.items.projectsThreejsJourneyFloorTexture,
|
||||
link:
|
||||
{
|
||||
href: 'https://threejs-journey.com?c=p3',
|
||||
x: - 4.8,
|
||||
y: - 3,
|
||||
halfExtents:
|
||||
{
|
||||
x: 3.2,
|
||||
y: 1.5
|
||||
}
|
||||
},
|
||||
distinctions:
|
||||
[
|
||||
{ type: 'fwa', x: 3.95, y: 4.15 }
|
||||
]
|
||||
},
|
||||
{
|
||||
name: 'Madbox',
|
||||
imageSources:
|
||||
[
|
||||
projectsMadboxSlideASources,
|
||||
projectsMadboxSlideBSources,
|
||||
projectsMadboxSlideCSources
|
||||
],
|
||||
floorTexture: this.resources.items.projectsMadboxFloorTexture,
|
||||
link:
|
||||
{
|
||||
href: 'https://madbox.io',
|
||||
x: - 4.8,
|
||||
y: - 4,
|
||||
halfExtents:
|
||||
{
|
||||
x: 3.2,
|
||||
y: 1.5
|
||||
}
|
||||
},
|
||||
distinctions:
|
||||
[
|
||||
{ type: 'awwwards', x: 3.95, y: 4.15 },
|
||||
{ type: 'fwa', x: 5.6, y: 4.15 }
|
||||
]
|
||||
},
|
||||
{
|
||||
name: 'Scout',
|
||||
imageSources:
|
||||
[
|
||||
projectsScoutSlideASources,
|
||||
projectsScoutSlideBSources,
|
||||
projectsScoutSlideCSources
|
||||
],
|
||||
floorTexture: this.resources.items.projectsScoutFloorTexture,
|
||||
link:
|
||||
{
|
||||
href: 'https://fromscout.com',
|
||||
x: - 4.8,
|
||||
y: - 2,
|
||||
halfExtents:
|
||||
{
|
||||
x: 3.2,
|
||||
y: 1.5
|
||||
}
|
||||
},
|
||||
distinctions:
|
||||
[
|
||||
]
|
||||
},
|
||||
{
|
||||
name: 'Chartogne Taillet',
|
||||
imageSources:
|
||||
[
|
||||
projectsChartogneSlideASources,
|
||||
projectsChartogneSlideBSources,
|
||||
projectsChartogneSlideCSources
|
||||
],
|
||||
floorTexture: this.resources.items.projectsChartogneFloorTexture,
|
||||
link:
|
||||
{
|
||||
href: 'https://chartogne-taillet.com',
|
||||
x: - 4.8,
|
||||
y: - 3.3,
|
||||
halfExtents:
|
||||
{
|
||||
x: 3.2,
|
||||
y: 1.5
|
||||
}
|
||||
},
|
||||
distinctions:
|
||||
[
|
||||
{ type: 'awwwards', x: 3.95, y: 4.15 },
|
||||
{ type: 'fwa', x: 5.6, y: 4.15 },
|
||||
{ type: 'cssda', x: 7.2, y: 4.15 }
|
||||
]
|
||||
},
|
||||
{
|
||||
name: 'Zenly',
|
||||
imageSources:
|
||||
[
|
||||
projectsZenlySlideASources,
|
||||
projectsZenlySlideBSources,
|
||||
projectsZenlySlideCSources
|
||||
],
|
||||
floorTexture: this.resources.items.projectsZenlyFloorTexture,
|
||||
link:
|
||||
{
|
||||
href: 'https://zen.ly',
|
||||
x: - 4.8,
|
||||
y: - 4.2,
|
||||
halfExtents:
|
||||
{
|
||||
x: 3.2,
|
||||
y: 1.5
|
||||
}
|
||||
},
|
||||
distinctions:
|
||||
[
|
||||
{ type: 'awwwards', x: 3.95, y: 4.15 },
|
||||
{ type: 'fwa', x: 5.6, y: 4.15 },
|
||||
{ type: 'cssda', x: 7.2, y: 4.15 }
|
||||
]
|
||||
},
|
||||
{
|
||||
name: 'priorHoldings',
|
||||
imageSources:
|
||||
[
|
||||
projectsPriorHoldingsSlideASources,
|
||||
projectsPriorHoldingsSlideBSources,
|
||||
projectsPriorHoldingsSlideCSources
|
||||
],
|
||||
floorTexture: this.resources.items.projectsPriorHoldingsFloorTexture,
|
||||
link:
|
||||
{
|
||||
href: 'https://prior.co.jp/discover/',
|
||||
x: - 4.8,
|
||||
y: - 3,
|
||||
halfExtents:
|
||||
{
|
||||
x: 3.2,
|
||||
y: 1.5
|
||||
}
|
||||
},
|
||||
distinctions:
|
||||
[
|
||||
{ type: 'awwwards', x: 3.95, y: 4.15 },
|
||||
{ type: 'fwa', x: 5.6, y: 4.15 },
|
||||
{ type: 'cssda', x: 7.2, y: 4.15 }
|
||||
]
|
||||
},
|
||||
{
|
||||
name: 'orano',
|
||||
imageSources:
|
||||
[
|
||||
projectsOranoSlideASources,
|
||||
projectsOranoSlideBSources,
|
||||
projectsOranoSlideCSources
|
||||
],
|
||||
floorTexture: this.resources.items.projectsOranoFloorTexture,
|
||||
link:
|
||||
{
|
||||
href: 'https://orano.imm-g-prod.com/experience/innovation/en',
|
||||
x: - 4.8,
|
||||
y: - 3.4,
|
||||
halfExtents:
|
||||
{
|
||||
x: 3.2,
|
||||
y: 1.5
|
||||
}
|
||||
},
|
||||
distinctions:
|
||||
[
|
||||
{ type: 'awwwards', x: 3.95, y: 4.15 },
|
||||
{ type: 'fwa', x: 5.6, y: 4.15 },
|
||||
{ type: 'cssda', x: 7.2, y: 4.15 }
|
||||
]
|
||||
},
|
||||
{
|
||||
name: 'citrixRedbull',
|
||||
imageSources:
|
||||
[
|
||||
projectsCitrixRedbullSlideASources,
|
||||
projectsCitrixRedbullSlideBSources,
|
||||
projectsCitrixRedbullSlideCSources
|
||||
],
|
||||
floorTexture: this.resources.items.projectsCitrixRedbullFloorTexture,
|
||||
link:
|
||||
{
|
||||
href: 'https://thenewmobileworkforce.imm-g-prod.com/',
|
||||
x: - 4.8,
|
||||
y: - 4.4,
|
||||
halfExtents:
|
||||
{
|
||||
x: 3.2,
|
||||
y: 1.5
|
||||
}
|
||||
},
|
||||
distinctions:
|
||||
[
|
||||
{ type: 'awwwards', x: 3.95, y: 4.15 },
|
||||
{ type: 'fwa', x: 5.6, y: 4.15 },
|
||||
{ type: 'cssda', x: 7.2, y: 4.15 }
|
||||
]
|
||||
},
|
||||
// {
|
||||
// name: 'gleecChat',
|
||||
// imageSources:
|
||||
// [
|
||||
// projectsGleecChatSlideASources,
|
||||
// projectsGleecChatSlideBSources,
|
||||
// projectsGleecChatSlideCSources,
|
||||
// projectsGleecChatSlideDSources
|
||||
// ],
|
||||
// floorTexture: this.resources.items.projectsGleecChatFloorTexture,
|
||||
// link:
|
||||
// {
|
||||
// href: 'http://gleec.imm-g-prod.com',
|
||||
// x: - 4.8,
|
||||
// y: - 3.4,
|
||||
// halfExtents:
|
||||
// {
|
||||
// x: 3.2,
|
||||
// y: 1.5
|
||||
// }
|
||||
// },
|
||||
// distinctions:
|
||||
// [
|
||||
// { type: 'awwwards', x: 3.95, y: 4.15 },
|
||||
// { type: 'fwa', x: 5.6, y: 4.15 },
|
||||
// { type: 'cssda', x: 7.2, y: 4.15 }
|
||||
// ]
|
||||
// },
|
||||
{
|
||||
name: 'keppler',
|
||||
imageSources:
|
||||
[
|
||||
projectsKepplerSlideASources,
|
||||
projectsKepplerSlideBSources,
|
||||
projectsKepplerSlideCSources
|
||||
],
|
||||
floorTexture: this.resources.items.projectsKepplerFloorTexture,
|
||||
link:
|
||||
{
|
||||
href: 'https://brunosimon.github.io/keppler/',
|
||||
x: 2.75,
|
||||
y: - 1.1,
|
||||
halfExtents:
|
||||
{
|
||||
x: 3.2,
|
||||
y: 1.5
|
||||
}
|
||||
},
|
||||
distinctions: []
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
setZone()
|
||||
{
|
||||
const totalWidth = this.list.length * (this.interDistance / 2)
|
||||
|
||||
const zone = this.zones.add({
|
||||
position: { x: this.x + totalWidth - this.projectHalfWidth - 6, y: this.y },
|
||||
halfExtents: { x: totalWidth, y: 12 },
|
||||
data: { cameraAngle: 'projects' }
|
||||
})
|
||||
|
||||
zone.on('in', (_data) =>
|
||||
{
|
||||
this.camera.angle.set(_data.cameraAngle)
|
||||
TweenLite.to(this.passes.horizontalBlurPass.material.uniforms.uStrength.value, 2, { x: 0 })
|
||||
TweenLite.to(this.passes.verticalBlurPass.material.uniforms.uStrength.value, 2, { y: 0 })
|
||||
})
|
||||
|
||||
zone.on('out', () =>
|
||||
{
|
||||
this.camera.angle.set('default')
|
||||
TweenLite.to(this.passes.horizontalBlurPass.material.uniforms.uStrength.value, 2, { x: this.passes.horizontalBlurPass.strength })
|
||||
TweenLite.to(this.passes.verticalBlurPass.material.uniforms.uStrength.value, 2, { y: this.passes.verticalBlurPass.strength })
|
||||
})
|
||||
}
|
||||
|
||||
add(_options)
|
||||
{
|
||||
const x = this.x + this.items.length * this.interDistance
|
||||
let y = this.y
|
||||
if(this.items.length > 0)
|
||||
{
|
||||
y += (Math.random() - 0.5) * this.positionRandomess
|
||||
}
|
||||
|
||||
// Create project
|
||||
const project = new Project({
|
||||
time: this.time,
|
||||
resources: this.resources,
|
||||
objects: this.objects,
|
||||
areas: this.areas,
|
||||
geometries: this.geometries,
|
||||
meshes: this.meshes,
|
||||
debug: this.debugFolder,
|
||||
x: x,
|
||||
y: y,
|
||||
..._options
|
||||
})
|
||||
|
||||
this.container.add(project.container)
|
||||
|
||||
// Add tiles
|
||||
if(this.items.length >= 1)
|
||||
{
|
||||
const previousProject = this.items[this.items.length - 1]
|
||||
const start = new THREE.Vector2(previousProject.x + this.projectHalfWidth, previousProject.y)
|
||||
const end = new THREE.Vector2(project.x - this.projectHalfWidth, project.y)
|
||||
const delta = end.clone().sub(start)
|
||||
this.tiles.add({
|
||||
start: start,
|
||||
delta: delta
|
||||
})
|
||||
}
|
||||
|
||||
// Save
|
||||
this.items.push(project)
|
||||
}
|
||||
}
|
242
src/javascript/World/Shadows.js
Normal file
@ -0,0 +1,242 @@
|
||||
import * as THREE from 'three'
|
||||
import ShadowMaterial from '../Materials/Shadow.js'
|
||||
import { TransformControls } from 'three/examples/jsm/controls/TransformControls.js'
|
||||
|
||||
export default class Shadows
|
||||
{
|
||||
constructor(_options)
|
||||
{
|
||||
// Options
|
||||
this.time = _options.time
|
||||
this.debug = _options.debug
|
||||
this.renderer = _options.renderer
|
||||
this.camera = _options.camera
|
||||
|
||||
// Set up
|
||||
this.alpha = 0
|
||||
this.maxDistance = 3
|
||||
this.distancePower = 2
|
||||
this.zFightingDistance = 0.001
|
||||
this.color = '#d04500'
|
||||
this.wireframeVisible = false
|
||||
this.items = []
|
||||
|
||||
this.container = new THREE.Object3D()
|
||||
this.container.matrixAutoUpdate = false
|
||||
this.container.updateMatrix()
|
||||
|
||||
// Debug
|
||||
if(this.debug)
|
||||
{
|
||||
this.debugFolder = this.debug.addFolder('shadows')
|
||||
// this.debugFolder.open()
|
||||
|
||||
this.debugFolder.add(this, 'alpha').step(0.01).min(0).max(1)
|
||||
this.debugFolder.add(this, 'maxDistance').step(0.01).min(0).max(10)
|
||||
this.debugFolder.add(this, 'distancePower').step(0.01).min(1).max(5)
|
||||
this.debugFolder.add(this, 'wireframeVisible').name('wireframeVisible').onChange(() =>
|
||||
{
|
||||
for(const _shadow of this.items)
|
||||
{
|
||||
_shadow.mesh.material = this.wireframeVisible ? this.materials.wireframe : _shadow.material
|
||||
}
|
||||
})
|
||||
|
||||
this.debugFolder.addColor(this, 'color').onChange(() =>
|
||||
{
|
||||
this.materials.base.uniforms.uColor.value = new THREE.Color(this.color)
|
||||
|
||||
for(const _shadow of this.items)
|
||||
{
|
||||
_shadow.material.uniforms.uColor.value = new THREE.Color(this.color)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
this.setSun()
|
||||
this.setMaterials()
|
||||
this.setGeometry()
|
||||
this.setHelper()
|
||||
|
||||
// Time tick
|
||||
this.time.on('tick', () =>
|
||||
{
|
||||
for(const _shadow of this.items)
|
||||
{
|
||||
// Position
|
||||
const z = Math.max(_shadow.reference.position.z + _shadow.offsetZ, 0)
|
||||
const sunOffset = this.sun.vector.clone().multiplyScalar(z)
|
||||
|
||||
_shadow.mesh.position.x = _shadow.reference.position.x + sunOffset.x
|
||||
_shadow.mesh.position.y = _shadow.reference.position.y + sunOffset.y
|
||||
|
||||
// Angle
|
||||
// Project the rotation as a vector on a plane and extract the angle
|
||||
const rotationVector = new THREE.Vector3(1, 0, 0)
|
||||
rotationVector.applyQuaternion(_shadow.reference.quaternion)
|
||||
// const planeVector = new THREE.Vector3(0, 0, 1)
|
||||
// planeVector.normalize()
|
||||
const projectedRotationVector = rotationVector.clone().projectOnPlane(new THREE.Vector3(0, 0, 1))
|
||||
|
||||
let orientationAlpha = Math.abs(rotationVector.angleTo(new THREE.Vector3(0, 0, 1)) - Math.PI * 0.5) / (Math.PI * 0.5)
|
||||
orientationAlpha /= 0.5
|
||||
orientationAlpha -= 1 / 0.5
|
||||
orientationAlpha = Math.abs(orientationAlpha)
|
||||
orientationAlpha = Math.min(Math.max(orientationAlpha, 0), 1)
|
||||
|
||||
const angle = Math.atan2(projectedRotationVector.y, projectedRotationVector.x)
|
||||
_shadow.mesh.rotation.z = angle
|
||||
|
||||
// Alpha
|
||||
let alpha = (this.maxDistance - z) / this.maxDistance
|
||||
alpha = Math.min(Math.max(alpha, 0), 1)
|
||||
alpha = Math.pow(alpha, this.distancePower)
|
||||
|
||||
_shadow.material.uniforms.uAlpha.value = this.alpha * _shadow.alpha * orientationAlpha * alpha
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
setSun()
|
||||
{
|
||||
this.sun = {}
|
||||
this.sun.position = new THREE.Vector3(- 2.5, - 2.65, 3.75)
|
||||
this.sun.vector = new THREE.Vector3()
|
||||
this.sun.helper = new THREE.ArrowHelper(new THREE.Vector3(0, 0, 1), new THREE.Vector3(0, 0, 0), 1, 0xffffff, 0.1, 0.4)
|
||||
this.sun.helper.visible = false
|
||||
this.container.add(this.sun.helper)
|
||||
|
||||
this.sun.update = () =>
|
||||
{
|
||||
this.sun.vector.copy(this.sun.position).multiplyScalar(1 / this.sun.position.z).negate()
|
||||
this.sun.helper.position.copy(this.sun.position)
|
||||
|
||||
const direction = this.sun.position.clone().negate().normalize()
|
||||
|
||||
this.sun.helper.setDirection(direction)
|
||||
this.sun.helper.setLength(this.sun.helper.position.length())
|
||||
}
|
||||
|
||||
this.sun.update()
|
||||
|
||||
// Debug
|
||||
if(this.debug)
|
||||
{
|
||||
const folder = this.debugFolder.addFolder('sun')
|
||||
folder.open()
|
||||
|
||||
folder.add(this.sun.position, 'x').step(0.01).min(- 10).max(10).name('sunX').onChange(this.sun.update)
|
||||
folder.add(this.sun.position, 'y').step(0.01).min(- 10).max(10).name('sunY').onChange(this.sun.update)
|
||||
folder.add(this.sun.position, 'z').step(0.01).min(0).max(10).name('sunZ').onChange(this.sun.update)
|
||||
folder.add(this.sun.helper, 'visible').name('sunHelperVisible')
|
||||
}
|
||||
}
|
||||
|
||||
setMaterials()
|
||||
{
|
||||
// Wireframe
|
||||
this.materials = {}
|
||||
this.materials.wireframe = new THREE.MeshBasicMaterial({ color: 0xffffff, wireframe: true })
|
||||
|
||||
// Base
|
||||
this.materials.base = new ShadowMaterial()
|
||||
this.materials.base.depthWrite = false
|
||||
this.materials.base.uniforms.uColor.value = new THREE.Color(this.color)
|
||||
this.materials.base.uniforms.uAlpha.value = 0
|
||||
this.materials.base.uniforms.uFadeRadius.value = 0.35
|
||||
}
|
||||
|
||||
setGeometry()
|
||||
{
|
||||
this.geometry = new THREE.PlaneBufferGeometry(1, 1, 1, 1)
|
||||
}
|
||||
|
||||
setHelper()
|
||||
{
|
||||
if(!this.debug)
|
||||
{
|
||||
return
|
||||
}
|
||||
|
||||
this.helper = {}
|
||||
|
||||
this.helper.active = false
|
||||
|
||||
this.helper.mesh = new THREE.Mesh(new THREE.BoxBufferGeometry(3, 1, 1, 1), new THREE.MeshNormalMaterial())
|
||||
this.helper.mesh.position.z = 1.5
|
||||
this.helper.mesh.position.y = - 3
|
||||
this.helper.mesh.visible = this.helper.active
|
||||
this.container.add(this.helper.mesh)
|
||||
|
||||
this.helper.transformControls = new TransformControls(this.camera.instance, this.renderer.domElement)
|
||||
this.helper.transformControls.size = 0.5
|
||||
this.helper.transformControls.attach(this.helper.mesh)
|
||||
this.helper.transformControls.visible = this.helper.active
|
||||
this.helper.transformControls.enabled = this.helper.active
|
||||
|
||||
this.helper.shadow = this.add(this.helper.mesh, { sizeX: 6, sizeY: 2, offsetZ: - 0.35, alpha: 0.99 })
|
||||
this.helper.shadow.mesh.visible = this.helper.active
|
||||
|
||||
document.addEventListener('keydown', (_event) =>
|
||||
{
|
||||
if(_event.key === 'r')
|
||||
{
|
||||
this.helper.transformControls.setMode('rotate')
|
||||
}
|
||||
else if(_event.key === 'g')
|
||||
{
|
||||
this.helper.transformControls.setMode('translate')
|
||||
}
|
||||
})
|
||||
|
||||
this.helper.transformControls.addEventListener('dragging-changed', (_event) =>
|
||||
{
|
||||
this.camera.orbitControls.enabled = !_event.value
|
||||
})
|
||||
|
||||
this.container.add(this.helper.transformControls)
|
||||
|
||||
// Debug
|
||||
if(this.debug)
|
||||
{
|
||||
const folder = this.debugFolder.addFolder('helper')
|
||||
folder.open()
|
||||
|
||||
folder.add(this.helper, 'active').name('visible').onChange(() =>
|
||||
{
|
||||
this.helper.mesh.visible = this.helper.active
|
||||
|
||||
this.helper.transformControls.visible = this.helper.active
|
||||
this.helper.transformControls.enabled = this.helper.active
|
||||
|
||||
this.helper.shadow.mesh.visible = this.helper.active
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
add(_reference, _options = {})
|
||||
{
|
||||
const shadow = {}
|
||||
|
||||
// Options
|
||||
shadow.offsetZ = typeof _options.offsetZ === 'undefined' ? 0 : _options.offsetZ
|
||||
shadow.alpha = typeof _options.alpha === 'undefined' ? 1 : _options.alpha
|
||||
|
||||
// Reference
|
||||
shadow.reference = _reference
|
||||
|
||||
// Material
|
||||
shadow.material = this.materials.base.clone()
|
||||
|
||||
// Mesh
|
||||
shadow.mesh = new THREE.Mesh(this.geometry, this.wireframeVisible ? this.materials.wireframe : shadow.material)
|
||||
shadow.mesh.position.z = this.zFightingDistance
|
||||
shadow.mesh.scale.set(_options.sizeX, _options.sizeY, 2.4)
|
||||
|
||||
// Save
|
||||
this.container.add(shadow.mesh)
|
||||
this.items.push(shadow)
|
||||
|
||||
return shadow
|
||||
}
|
||||
}
|
368
src/javascript/World/Sounds.js
Normal file
@ -0,0 +1,368 @@
|
||||
import { Howl, Howler } from 'howler'
|
||||
|
||||
import revealSound from '../../sounds/reveal/reveal-1.mp3'
|
||||
|
||||
import engineSound from '../../sounds/engines/1/low_off.mp3'
|
||||
|
||||
import brick1Sound from '../../sounds/bricks/brick-1.mp3'
|
||||
import brick2Sound from '../../sounds/bricks/brick-2.mp3'
|
||||
// import brick3Sound from '../../sounds/bricks/brick-3.mp3'
|
||||
import brick4Sound from '../../sounds/bricks/brick-4.mp3'
|
||||
// import brick5Sound from '../../sounds/bricks/brick-5.mp3'
|
||||
import brick6Sound from '../../sounds/bricks/brick-6.mp3'
|
||||
import brick7Sound from '../../sounds/bricks/brick-7.mp3'
|
||||
import brick8Sound from '../../sounds/bricks/brick-8.mp3'
|
||||
|
||||
import bowlingPin1Sound from '../../sounds/bowling/pin-1.mp3'
|
||||
|
||||
import carHit1Sound from '../../sounds/car-hits/car-hit-1.mp3'
|
||||
// import carHit2Sound from '../../sounds/car-hits/car-hit-2.mp3'
|
||||
import carHit3Sound from '../../sounds/car-hits/car-hit-3.mp3'
|
||||
import carHit4Sound from '../../sounds/car-hits/car-hit-4.mp3'
|
||||
import carHit5Sound from '../../sounds/car-hits/car-hit-5.mp3'
|
||||
|
||||
import woodHit1Sound from '../../sounds/wood-hits/wood-hit-1.mp3'
|
||||
|
||||
import screech1Sound from '../../sounds/screeches/screech-1.mp3'
|
||||
|
||||
import uiArea1Sound from '../../sounds/ui/area-1.mp3'
|
||||
|
||||
import carHorn1Sound from '../../sounds/car-horns/car-horn-1.mp3'
|
||||
import carHorn2Sound from '../../sounds/car-horns/car-horn-2.mp3'
|
||||
|
||||
import horn1Sound from '../../sounds/horns/horn-1.mp3'
|
||||
import horn2Sound from '../../sounds/horns/horn-2.mp3'
|
||||
import horn3Sound from '../../sounds/horns/horn-3.mp3'
|
||||
|
||||
export default class Sounds
|
||||
{
|
||||
constructor(_options)
|
||||
{
|
||||
// Options
|
||||
this.time = _options.time
|
||||
this.debug = _options.debug
|
||||
|
||||
// Debug
|
||||
if(this.debug)
|
||||
{
|
||||
this.debugFolder = this.debug.addFolder('sounds')
|
||||
// this.debugFolder.open()
|
||||
}
|
||||
|
||||
// Set up
|
||||
this.items = []
|
||||
|
||||
this.setSettings()
|
||||
this.setMasterVolume()
|
||||
this.setMute()
|
||||
this.setEngine()
|
||||
}
|
||||
|
||||
setSettings()
|
||||
{
|
||||
this.settings = [
|
||||
{
|
||||
name: 'reveal',
|
||||
sounds: [revealSound],
|
||||
minDelta: 100,
|
||||
velocityMin: 0,
|
||||
velocityMultiplier: 1,
|
||||
volumeMin: 1,
|
||||
volumeMax: 1,
|
||||
rateMin: 1,
|
||||
rateMax: 1
|
||||
},
|
||||
{
|
||||
name: 'brick',
|
||||
sounds: [brick1Sound, brick2Sound, brick4Sound, brick6Sound, brick7Sound, brick8Sound],
|
||||
minDelta: 100,
|
||||
velocityMin: 1,
|
||||
velocityMultiplier: 0.75,
|
||||
volumeMin: 0.2,
|
||||
volumeMax: 0.85,
|
||||
rateMin: 0.5,
|
||||
rateMax: 0.75
|
||||
},
|
||||
{
|
||||
name: 'bowlingPin',
|
||||
sounds: [bowlingPin1Sound],
|
||||
minDelta: 0,
|
||||
velocityMin: 1,
|
||||
velocityMultiplier: 0.5,
|
||||
volumeMin: 0.35,
|
||||
volumeMax: 1,
|
||||
rateMin: 0.1,
|
||||
rateMax: 0.85
|
||||
},
|
||||
{
|
||||
name: 'bowlingBall',
|
||||
sounds: [bowlingPin1Sound, bowlingPin1Sound, bowlingPin1Sound],
|
||||
minDelta: 0,
|
||||
velocityMin: 1,
|
||||
velocityMultiplier: 0.5,
|
||||
volumeMin: 0.35,
|
||||
volumeMax: 1,
|
||||
rateMin: 0.1,
|
||||
rateMax: 0.2
|
||||
},
|
||||
{
|
||||
name: 'carHit',
|
||||
sounds: [carHit1Sound, carHit3Sound, carHit4Sound, carHit5Sound],
|
||||
minDelta: 100,
|
||||
velocityMin: 2,
|
||||
velocityMultiplier: 1,
|
||||
volumeMin: 0.2,
|
||||
volumeMax: 0.6,
|
||||
rateMin: 0.35,
|
||||
rateMax: 0.55
|
||||
},
|
||||
{
|
||||
name: 'woodHit',
|
||||
sounds: [woodHit1Sound],
|
||||
minDelta: 30,
|
||||
velocityMin: 1,
|
||||
velocityMultiplier: 1,
|
||||
volumeMin: 0.5,
|
||||
volumeMax: 1,
|
||||
rateMin: 0.75,
|
||||
rateMax: 1.5
|
||||
},
|
||||
{
|
||||
name: 'screech',
|
||||
sounds: [screech1Sound],
|
||||
minDelta: 1000,
|
||||
velocityMin: 0,
|
||||
velocityMultiplier: 1,
|
||||
volumeMin: 0.75,
|
||||
volumeMax: 1,
|
||||
rateMin: 0.9,
|
||||
rateMax: 1.1
|
||||
},
|
||||
{
|
||||
name: 'uiArea',
|
||||
sounds: [uiArea1Sound],
|
||||
minDelta: 100,
|
||||
velocityMin: 0,
|
||||
velocityMultiplier: 1,
|
||||
volumeMin: 0.75,
|
||||
volumeMax: 1,
|
||||
rateMin: 0.95,
|
||||
rateMax: 1.05
|
||||
},
|
||||
{
|
||||
name: 'carHorn1',
|
||||
sounds: [carHorn1Sound],
|
||||
minDelta: 0,
|
||||
velocityMin: 0,
|
||||
velocityMultiplier: 1,
|
||||
volumeMin: 0.95,
|
||||
volumeMax: 1,
|
||||
rateMin: 1,
|
||||
rateMax: 1
|
||||
},
|
||||
{
|
||||
name: 'carHorn2',
|
||||
sounds: [carHorn2Sound],
|
||||
minDelta: 0,
|
||||
velocityMin: 0,
|
||||
velocityMultiplier: 1,
|
||||
volumeMin: 0.95,
|
||||
volumeMax: 1,
|
||||
rateMin: 1,
|
||||
rateMax: 1
|
||||
},
|
||||
{
|
||||
name: 'horn',
|
||||
sounds: [horn1Sound, horn2Sound, horn3Sound],
|
||||
minDelta: 100,
|
||||
velocityMin: 1,
|
||||
velocityMultiplier: 0.75,
|
||||
volumeMin: 0.5,
|
||||
volumeMax: 1,
|
||||
rateMin: 0.75,
|
||||
rateMax: 1
|
||||
}
|
||||
]
|
||||
|
||||
for(const _settings of this.settings)
|
||||
{
|
||||
this.add(_settings)
|
||||
}
|
||||
}
|
||||
|
||||
setMasterVolume()
|
||||
{
|
||||
// Set up
|
||||
this.masterVolume = 0.5
|
||||
Howler.volume(this.masterVolume)
|
||||
|
||||
window.requestAnimationFrame(() =>
|
||||
{
|
||||
Howler.volume(this.masterVolume)
|
||||
})
|
||||
|
||||
// Debug
|
||||
if(this.debug)
|
||||
{
|
||||
this.debugFolder.add(this, 'masterVolume').step(0.001).min(0).max(1).onChange(() =>
|
||||
{
|
||||
Howler.volume(this.masterVolume)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
setMute()
|
||||
{
|
||||
// Set up
|
||||
this.muted = typeof this.debug !== 'undefined'
|
||||
Howler.mute(this.muted)
|
||||
|
||||
// M Key
|
||||
window.addEventListener('keydown', (_event) =>
|
||||
{
|
||||
if(_event.key === 'm')
|
||||
{
|
||||
this.muted = !this.muted
|
||||
Howler.mute(this.muted)
|
||||
}
|
||||
})
|
||||
|
||||
// Tab focus / blur
|
||||
document.addEventListener('visibilitychange', () =>
|
||||
{
|
||||
if(document.hidden)
|
||||
{
|
||||
Howler.mute(true)
|
||||
}
|
||||
else
|
||||
{
|
||||
Howler.mute(this.muted)
|
||||
}
|
||||
})
|
||||
|
||||
// Debug
|
||||
if(this.debug)
|
||||
{
|
||||
this.debugFolder.add(this, 'muted').listen().onChange(() =>
|
||||
{
|
||||
Howler.mute(this.muted)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
setEngine()
|
||||
{
|
||||
// Set up
|
||||
this.engine = {}
|
||||
|
||||
this.engine.progress = 0
|
||||
this.engine.progressEasingUp = 0.3
|
||||
this.engine.progressEasingDown = 0.15
|
||||
|
||||
this.engine.speed = 0
|
||||
this.engine.speedMultiplier = 2.5
|
||||
this.engine.acceleration = 0
|
||||
this.engine.accelerationMultiplier = 0.4
|
||||
|
||||
this.engine.rate = {}
|
||||
this.engine.rate.min = 0.4
|
||||
this.engine.rate.max = 1.4
|
||||
|
||||
this.engine.volume = {}
|
||||
this.engine.volume.min = 0.4
|
||||
this.engine.volume.max = 1
|
||||
this.engine.volume.master = 0
|
||||
|
||||
this.engine.sound = new Howl({
|
||||
src: [engineSound],
|
||||
loop: true
|
||||
})
|
||||
|
||||
this.engine.sound.play()
|
||||
|
||||
// Time tick
|
||||
this.time.on('tick', () =>
|
||||
{
|
||||
let progress = Math.abs(this.engine.speed) * this.engine.speedMultiplier + Math.max(this.engine.acceleration, 0) * this.engine.accelerationMultiplier
|
||||
progress = Math.min(Math.max(progress, 0), 1)
|
||||
|
||||
this.engine.progress += (progress - this.engine.progress) * this.engine[progress > this.engine.progress ? 'progressEasingUp' : 'progressEasingDown']
|
||||
|
||||
// Rate
|
||||
const rateAmplitude = this.engine.rate.max - this.engine.rate.min
|
||||
this.engine.sound.rate(this.engine.rate.min + rateAmplitude * this.engine.progress)
|
||||
|
||||
// Volume
|
||||
const volumeAmplitude = this.engine.volume.max - this.engine.volume.min
|
||||
this.engine.sound.volume((this.engine.volume.min + volumeAmplitude * this.engine.progress) * this.engine.volume.master)
|
||||
})
|
||||
|
||||
// Debug
|
||||
if(this.debug)
|
||||
{
|
||||
const folder = this.debugFolder.addFolder('engine')
|
||||
folder.open()
|
||||
|
||||
folder.add(this.engine, 'progressEasingUp').step(0.001).min(0).max(1).name('progressEasingUp')
|
||||
folder.add(this.engine, 'progressEasingDown').step(0.001).min(0).max(1).name('progressEasingDown')
|
||||
folder.add(this.engine.rate, 'min').step(0.001).min(0).max(4).name('rateMin')
|
||||
folder.add(this.engine.rate, 'max').step(0.001).min(0).max(4).name('rateMax')
|
||||
folder.add(this.engine, 'speedMultiplier').step(0.01).min(0).max(5).name('speedMultiplier')
|
||||
folder.add(this.engine, 'accelerationMultiplier').step(0.01).min(0).max(100).name('accelerationMultiplier')
|
||||
folder.add(this.engine, 'progress').step(0.01).min(0).max(1).name('progress').listen()
|
||||
}
|
||||
}
|
||||
|
||||
add(_options)
|
||||
{
|
||||
const item = {
|
||||
name: _options.name,
|
||||
minDelta: _options.minDelta,
|
||||
velocityMin: _options.velocityMin,
|
||||
velocityMultiplier: _options.velocityMultiplier,
|
||||
volumeMin: _options.volumeMin,
|
||||
volumeMax: _options.volumeMax,
|
||||
rateMin: _options.rateMin,
|
||||
rateMax: _options.rateMax,
|
||||
lastTime: 0,
|
||||
sounds: []
|
||||
}
|
||||
|
||||
for(const _sound of _options.sounds)
|
||||
{
|
||||
const sound = new Howl({ src: [_sound] })
|
||||
|
||||
item.sounds.push(sound)
|
||||
}
|
||||
|
||||
this.items.push(item)
|
||||
}
|
||||
|
||||
play(_name, _velocity)
|
||||
{
|
||||
const item = this.items.find((_item) => _item.name === _name)
|
||||
const time = Date.now()
|
||||
const velocity = typeof _velocity === 'undefined' ? 0 : _velocity
|
||||
|
||||
if(item && time > item.lastTime + item.minDelta && (item.velocityMin === 0 || velocity > item.velocityMin))
|
||||
{
|
||||
// Find random sound
|
||||
const sound = item.sounds[Math.floor(Math.random() * item.sounds.length)]
|
||||
|
||||
// Update volume
|
||||
let volume = Math.min(Math.max((velocity - item.velocityMin) * item.velocityMultiplier, item.volumeMin), item.volumeMax)
|
||||
volume = Math.pow(volume, 2)
|
||||
sound.volume(volume)
|
||||
|
||||
// Update rate
|
||||
const rateAmplitude = item.rateMax - item.rateMin
|
||||
sound.rate(item.rateMin + Math.random() * rateAmplitude)
|
||||
|
||||
// Play
|
||||
sound.play()
|
||||
|
||||
// Save last play time
|
||||
item.lastTime = time
|
||||
}
|
||||
}
|
||||
}
|
135
src/javascript/World/Tiles.js
Normal file
@ -0,0 +1,135 @@
|
||||
import * as THREE from 'three'
|
||||
|
||||
export default class Tiles
|
||||
{
|
||||
constructor(_options)
|
||||
{
|
||||
// Options
|
||||
this.resources = _options.resources
|
||||
this.objects = _options.objects
|
||||
this.debug = _options.debug
|
||||
|
||||
// Set up
|
||||
this.items = []
|
||||
this.interDistance = 1.5
|
||||
this.tangentDistance = 0.3
|
||||
this.positionRandomess = 0.3
|
||||
this.rotationRandomess = 0.1
|
||||
|
||||
this.setModels()
|
||||
}
|
||||
|
||||
setModels()
|
||||
{
|
||||
this.models = {}
|
||||
|
||||
this.models.items = [
|
||||
{
|
||||
base: this.resources.items.tilesABase.scene,
|
||||
collision: this.resources.items.tilesACollision.scene,
|
||||
chances: 8
|
||||
},
|
||||
{
|
||||
base: this.resources.items.tilesBBase.scene,
|
||||
collision: this.resources.items.tilesBCollision.scene,
|
||||
chances: 1
|
||||
},
|
||||
{
|
||||
base: this.resources.items.tilesCBase.scene,
|
||||
collision: this.resources.items.tilesCCollision.scene,
|
||||
chances: 2
|
||||
},
|
||||
{
|
||||
base: this.resources.items.tilesDBase.scene,
|
||||
collision: this.resources.items.tilesDCollision.scene,
|
||||
chances: 4
|
||||
},
|
||||
{
|
||||
base: this.resources.items.tilesEBase.scene,
|
||||
collision: this.resources.items.tilesECollision.scene,
|
||||
chances: 2
|
||||
}
|
||||
]
|
||||
|
||||
const totalChances = this.models.items.reduce((_totalChances, _item) => _totalChances + _item.chances, 0)
|
||||
let chances = 0
|
||||
this.models.items = this.models.items.map((_item) =>
|
||||
{
|
||||
// Update chances
|
||||
_item.minChances = chances
|
||||
|
||||
chances += _item.chances / totalChances
|
||||
_item.maxChances = chances
|
||||
|
||||
// Update rotation
|
||||
_item.rotationIndex = 0
|
||||
|
||||
return _item
|
||||
})
|
||||
|
||||
this.models.pick = () =>
|
||||
{
|
||||
const random = Math.random()
|
||||
const model = this.models.items.find((_item) => random >= _item.minChances && random <= _item.maxChances)
|
||||
model.rotationIndex++
|
||||
|
||||
if(model.rotationIndex > 3)
|
||||
{
|
||||
model.rotationIndex = 0
|
||||
}
|
||||
|
||||
return model
|
||||
}
|
||||
}
|
||||
|
||||
add(_options)
|
||||
{
|
||||
const tilePath = {}
|
||||
tilePath.start = _options.start
|
||||
tilePath.delta = _options.delta
|
||||
|
||||
tilePath.distance = tilePath.delta.length()
|
||||
tilePath.count = Math.floor(tilePath.distance / this.interDistance)
|
||||
tilePath.directionVector = tilePath.delta.clone().normalize()
|
||||
tilePath.interVector = tilePath.directionVector.clone().multiplyScalar(this.interDistance)
|
||||
tilePath.centeringVector = tilePath.delta.clone().sub(tilePath.interVector.clone().multiplyScalar(tilePath.count))
|
||||
tilePath.tangentVector = tilePath.directionVector.clone().rotateAround(new THREE.Vector2(0, 0), Math.PI * 0.5).multiplyScalar(this.tangentDistance)
|
||||
tilePath.angle = tilePath.directionVector.angle()
|
||||
|
||||
// Create tiles
|
||||
for(let i = 0; i < tilePath.count; i++)
|
||||
{
|
||||
// Model
|
||||
const model = this.models.pick()
|
||||
|
||||
// Position
|
||||
const position = tilePath.start.clone().add(tilePath.interVector.clone().multiplyScalar(i)).add(tilePath.centeringVector)
|
||||
position.x += (Math.random() - 0.5) * this.positionRandomess
|
||||
position.y += (Math.random() - 0.5) * this.positionRandomess
|
||||
|
||||
const tangent = tilePath.tangentVector
|
||||
|
||||
if(i % 1 === 0)
|
||||
{
|
||||
tangent.negate()
|
||||
}
|
||||
|
||||
position.add(tangent)
|
||||
|
||||
// Rotation
|
||||
let rotation = tilePath.angle
|
||||
rotation += (Math.random() - 0.5) * this.rotationRandomess
|
||||
rotation += model.rotationIndex / 4 * Math.PI * 2
|
||||
|
||||
// Tile
|
||||
this.objects.add({
|
||||
base: model.base,
|
||||
collision: model.collision,
|
||||
offset: new THREE.Vector3(position.x, position.y, 0),
|
||||
rotation: new THREE.Euler(0, 0, rotation),
|
||||
duplicated: true,
|
||||
mass: 0
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
122
src/javascript/World/Walls.js
Normal file
@ -0,0 +1,122 @@
|
||||
import * as THREE from 'three'
|
||||
|
||||
export default class Walls
|
||||
{
|
||||
constructor(_options)
|
||||
{
|
||||
// Options
|
||||
this.resources = _options.resources
|
||||
this.objects = _options.objects
|
||||
}
|
||||
|
||||
add(_options)
|
||||
{
|
||||
const wall = {}
|
||||
wall.coordinates = []
|
||||
wall.items = []
|
||||
|
||||
const shape = _options.shape
|
||||
let widthCount = shape.widthCount
|
||||
let heightCount = shape.heightCount
|
||||
|
||||
switch(_options.shape.type)
|
||||
{
|
||||
case 'rectangle':
|
||||
case 'brick':
|
||||
for(let i = 0; i < heightCount; i++)
|
||||
{
|
||||
const lastLine = i === heightCount - 1
|
||||
let j = 0
|
||||
let widthCountTemp = widthCount
|
||||
|
||||
if(_options.shape.type === 'brick' && lastLine && _options.shape.equilibrateLastLine)
|
||||
{
|
||||
if(i % 2 === 0)
|
||||
{
|
||||
widthCountTemp--
|
||||
}
|
||||
else
|
||||
{
|
||||
j++
|
||||
}
|
||||
}
|
||||
|
||||
for(; j < widthCountTemp; j++)
|
||||
{
|
||||
const offset = new THREE.Vector3()
|
||||
offset.add(shape.offsetWidth.clone().multiplyScalar(j - (shape.widthCount - 1) * 0.5))
|
||||
offset.add(shape.offsetHeight.clone().multiplyScalar(i))
|
||||
offset.x += (Math.random() - 0.5) * shape.randomOffset.x
|
||||
offset.y += (Math.random() - 0.5) * shape.randomOffset.y
|
||||
offset.z += (Math.random() - 0.5) * shape.randomOffset.z
|
||||
|
||||
if(_options.shape.type === 'brick' && i % 2 === 0)
|
||||
{
|
||||
offset.add(shape.offsetWidth.clone().multiplyScalar(0.5))
|
||||
}
|
||||
|
||||
const rotation = new THREE.Euler()
|
||||
rotation.x += (Math.random() - 0.5) * shape.randomRotation.x
|
||||
rotation.y += (Math.random() - 0.5) * shape.randomRotation.y
|
||||
rotation.z += (Math.random() - 0.5) * shape.randomRotation.z
|
||||
|
||||
wall.coordinates.push({
|
||||
offset,
|
||||
rotation
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
break
|
||||
|
||||
case 'triangle':
|
||||
heightCount = shape.widthCount
|
||||
for(let i = 0; i < heightCount; i++)
|
||||
{
|
||||
for(let j = 0; j < widthCount; j++)
|
||||
{
|
||||
const offset = new THREE.Vector3()
|
||||
offset.add(shape.offsetWidth.clone().multiplyScalar(j - (shape.widthCount - 1) * 0.5))
|
||||
offset.add(shape.offsetWidth.clone().multiplyScalar(i * 0.5))
|
||||
offset.add(shape.offsetHeight.clone().multiplyScalar(i))
|
||||
offset.x += (Math.random() - 0.5) * shape.randomOffset.x
|
||||
offset.y += (Math.random() - 0.5) * shape.randomOffset.y
|
||||
offset.z += (Math.random() - 0.5) * shape.randomOffset.z
|
||||
|
||||
if(_options.shape.type === 'brick' && i % 2 === 0)
|
||||
{
|
||||
offset.add(shape.offsetWidth.clone().multiplyScalar(0.5))
|
||||
}
|
||||
|
||||
const rotation = new THREE.Euler()
|
||||
rotation.x += (Math.random() - 0.5) * shape.randomRotation.x
|
||||
rotation.y += (Math.random() - 0.5) * shape.randomRotation.y
|
||||
rotation.z += (Math.random() - 0.5) * shape.randomRotation.z
|
||||
|
||||
|
||||
wall.coordinates.push({
|
||||
offset,
|
||||
rotation
|
||||
})
|
||||
}
|
||||
|
||||
widthCount--
|
||||
}
|
||||
|
||||
break
|
||||
}
|
||||
|
||||
for(const _coordinates of wall.coordinates)
|
||||
{
|
||||
const objectOptions = { ..._options.object }
|
||||
objectOptions.offset = _options.object.offset.clone().add(_coordinates.offset).add(shape.position)
|
||||
objectOptions.rotation = _options.object.rotation.clone()
|
||||
objectOptions.rotation.x += _coordinates.rotation.x
|
||||
objectOptions.rotation.y += _coordinates.rotation.y
|
||||
objectOptions.rotation.z += _coordinates.rotation.z
|
||||
wall.items.push(this.objects.add(objectOptions))
|
||||
}
|
||||
|
||||
return wall
|
||||
}
|
||||
}
|
28
src/javascript/World/Zone.js
Normal file
@ -0,0 +1,28 @@
|
||||
import * as THREE from 'three'
|
||||
|
||||
import EventEmitter from '../Utils/EventEmitter.js'
|
||||
|
||||
export default class Zone extends EventEmitter
|
||||
{
|
||||
constructor(_options)
|
||||
{
|
||||
super()
|
||||
|
||||
// Options
|
||||
this.position = _options.position
|
||||
this.halfExtents = _options.halfExtents
|
||||
this.data = _options.data
|
||||
|
||||
// Set up
|
||||
this.isIn = false
|
||||
|
||||
// Mesh
|
||||
this.mesh = new THREE.Mesh(
|
||||
new THREE.BoxBufferGeometry(_options.halfExtents.x * 2, _options.halfExtents.y * 2, 3, 1, 1, 1),
|
||||
new THREE.MeshBasicMaterial({ color: 0xff00ff, wireframe: true })
|
||||
)
|
||||
this.mesh.position.x = _options.position.x
|
||||
this.mesh.position.y = _options.position.y
|
||||
this.mesh.position.z = 1.5
|
||||
}
|
||||
}
|
80
src/javascript/World/Zones.js
Normal file
@ -0,0 +1,80 @@
|
||||
import * as THREE from 'three'
|
||||
import Zone from './Zone.js'
|
||||
|
||||
export default class Zones
|
||||
{
|
||||
constructor(_options)
|
||||
{
|
||||
// Options
|
||||
this.time = _options.time
|
||||
this.sizes = _options.sizes
|
||||
this.physics = _options.physics
|
||||
this.debug = _options.debug
|
||||
|
||||
// Set up
|
||||
this.container = new THREE.Object3D()
|
||||
this.container.visible = false
|
||||
this.container.matrixAutoUpdate = false
|
||||
|
||||
// Debug
|
||||
if(this.debug)
|
||||
{
|
||||
this.debugFolder = this.debug.addFolder('zones')
|
||||
this.debugFolder.open()
|
||||
|
||||
this.debugFolder.add(this.container, 'visible').name('visible')
|
||||
}
|
||||
|
||||
this.setTester()
|
||||
this.setItems()
|
||||
}
|
||||
|
||||
setTester()
|
||||
{
|
||||
this.tester = {}
|
||||
this.tester.x = 0
|
||||
this.tester.y = 0
|
||||
|
||||
this.time.on('tick', () =>
|
||||
{
|
||||
this.tester.x = this.physics.car.chassis.body.position.x
|
||||
this.tester.y = this.physics.car.chassis.body.position.y
|
||||
})
|
||||
}
|
||||
|
||||
setItems()
|
||||
{
|
||||
this.items = []
|
||||
|
||||
this.time.on('tick', () =>
|
||||
{
|
||||
for(const _zone of this.items)
|
||||
{
|
||||
const isIn = this.tester.x < _zone.position.x + _zone.halfExtents.x && this.tester.x > _zone.position.x - _zone.halfExtents.x && this.tester.y < _zone.position.y + _zone.halfExtents.y && this.tester.y > _zone.position.y - _zone.halfExtents.y
|
||||
|
||||
if(isIn && !_zone.isIn)
|
||||
{
|
||||
_zone.trigger('in', [_zone.data])
|
||||
}
|
||||
else if(!isIn && _zone.isIn)
|
||||
{
|
||||
_zone.trigger('out', [_zone.data])
|
||||
}
|
||||
|
||||
_zone.isIn = isIn
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
add(_settings)
|
||||
{
|
||||
// Set up
|
||||
const zone = new Zone(_settings)
|
||||
this.container.add(zone.mesh)
|
||||
|
||||
// Save
|
||||
this.items.push(zone)
|
||||
|
||||
return zone
|
||||
}
|
||||
}
|
511
src/javascript/World/index.js
Normal file
@ -0,0 +1,511 @@
|
||||
import * as THREE from 'three'
|
||||
import Materials from './Materials.js'
|
||||
import Floor from './Floor.js'
|
||||
import Shadows from './Shadows.js'
|
||||
import Physics from './Physics.js'
|
||||
import Zones from './Zones.js'
|
||||
import Objects from './Objects.js'
|
||||
import Car from './Car.js'
|
||||
import Areas from './Areas.js'
|
||||
import Tiles from './Tiles.js'
|
||||
import Walls from './Walls.js'
|
||||
import IntroSection from './Sections/IntroSection.js'
|
||||
import ProjectsSection from './Sections/ProjectsSection.js'
|
||||
import CrossroadsSection from './Sections/CrossroadsSection.js'
|
||||
import InformationSection from './Sections/InformationSection.js'
|
||||
import PlaygroundSection from './Sections/PlaygroundSection.js'
|
||||
// import DistinctionASection from './Sections/DistinctionASection.js'
|
||||
// import DistinctionBSection from './Sections/DistinctionBSection.js'
|
||||
// import DistinctionCSection from './Sections/DistinctionCSection.js'
|
||||
// import DistinctionDSection from './Sections/DistinctionDSection.js'
|
||||
import Controls from './Controls.js'
|
||||
import Sounds from './Sounds.js'
|
||||
import { TweenLite } from 'gsap/TweenLite'
|
||||
import { Power2 } from 'gsap/EasePack'
|
||||
import EasterEggs from './EasterEggs.js'
|
||||
|
||||
export default class
|
||||
{
|
||||
constructor(_options)
|
||||
{
|
||||
// Options
|
||||
this.config = _options.config
|
||||
this.debug = _options.debug
|
||||
this.resources = _options.resources
|
||||
this.time = _options.time
|
||||
this.sizes = _options.sizes
|
||||
this.camera = _options.camera
|
||||
this.renderer = _options.renderer
|
||||
this.passes = _options.passes
|
||||
|
||||
// Debug
|
||||
if(this.debug)
|
||||
{
|
||||
this.debugFolder = this.debug.addFolder('world')
|
||||
this.debugFolder.open()
|
||||
}
|
||||
|
||||
// Set up
|
||||
this.container = new THREE.Object3D()
|
||||
this.container.matrixAutoUpdate = false
|
||||
|
||||
// this.setAxes()
|
||||
this.setSounds()
|
||||
this.setControls()
|
||||
this.setFloor()
|
||||
this.setAreas()
|
||||
this.setStartingScreen()
|
||||
}
|
||||
|
||||
start()
|
||||
{
|
||||
window.setTimeout(() =>
|
||||
{
|
||||
this.camera.pan.enable()
|
||||
}, 2000)
|
||||
|
||||
this.setReveal()
|
||||
this.setMaterials()
|
||||
this.setShadows()
|
||||
this.setPhysics()
|
||||
this.setZones()
|
||||
this.setObjects()
|
||||
this.setCar()
|
||||
this.areas.car = this.car
|
||||
this.setTiles()
|
||||
this.setWalls()
|
||||
this.setSections()
|
||||
this.setEasterEggs()
|
||||
}
|
||||
|
||||
setReveal()
|
||||
{
|
||||
this.reveal = {}
|
||||
this.reveal.matcapsProgress = 0
|
||||
this.reveal.floorShadowsProgress = 0
|
||||
this.reveal.previousMatcapsProgress = null
|
||||
this.reveal.previousFloorShadowsProgress = null
|
||||
|
||||
// Go method
|
||||
this.reveal.go = () =>
|
||||
{
|
||||
TweenLite.fromTo(this.reveal, 3, { matcapsProgress: 0 }, { matcapsProgress: 1 })
|
||||
TweenLite.fromTo(this.reveal, 3, { floorShadowsProgress: 0 }, { floorShadowsProgress: 1, delay: 0.5 })
|
||||
TweenLite.fromTo(this.shadows, 3, { alpha: 0 }, { alpha: 0.5, delay: 0.5 })
|
||||
|
||||
if(this.sections.intro)
|
||||
{
|
||||
TweenLite.fromTo(this.sections.intro.instructions.arrows.label.material, 0.3, { opacity: 0 }, { opacity: 1, delay: 0.5 })
|
||||
if(this.sections.intro.otherInstructions)
|
||||
{
|
||||
TweenLite.fromTo(this.sections.intro.otherInstructions.label.material, 0.3, { opacity: 0 }, { opacity: 1, delay: 0.75 })
|
||||
}
|
||||
}
|
||||
|
||||
// Car
|
||||
this.physics.car.chassis.body.sleep()
|
||||
this.physics.car.chassis.body.position.set(0, 0, 12)
|
||||
|
||||
window.setTimeout(() =>
|
||||
{
|
||||
this.physics.car.chassis.body.wakeUp()
|
||||
}, 300)
|
||||
|
||||
// Sound
|
||||
TweenLite.fromTo(this.sounds.engine.volume, 0.5, { master: 0 }, { master: 0.7, delay: 0.3, ease: Power2.easeIn })
|
||||
window.setTimeout(() =>
|
||||
{
|
||||
this.sounds.play('reveal')
|
||||
}, 400)
|
||||
|
||||
// Controls
|
||||
if(this.controls.touch)
|
||||
{
|
||||
window.setTimeout(() =>
|
||||
{
|
||||
this.controls.touch.reveal()
|
||||
}, 400)
|
||||
}
|
||||
}
|
||||
|
||||
// Time tick
|
||||
this.time.on('tick',() =>
|
||||
{
|
||||
// Matcap progress changed
|
||||
if(this.reveal.matcapsProgress !== this.reveal.previousMatcapsProgress)
|
||||
{
|
||||
// Update each material
|
||||
for(const _materialKey in this.materials.shades.items)
|
||||
{
|
||||
const material = this.materials.shades.items[_materialKey]
|
||||
material.uniforms.uRevealProgress.value = this.reveal.matcapsProgress
|
||||
}
|
||||
|
||||
// Save
|
||||
this.reveal.previousMatcapsProgress = this.reveal.matcapsProgress
|
||||
}
|
||||
|
||||
// Matcap progress changed
|
||||
if(this.reveal.floorShadowsProgress !== this.reveal.previousFloorShadowsProgress)
|
||||
{
|
||||
// Update each floor shadow
|
||||
for(const _mesh of this.objects.floorShadows)
|
||||
{
|
||||
_mesh.material.uniforms.uAlpha.value = this.reveal.floorShadowsProgress
|
||||
}
|
||||
|
||||
// Save
|
||||
this.reveal.previousFloorShadowsProgress = this.reveal.floorShadowsProgress
|
||||
}
|
||||
})
|
||||
|
||||
// Debug
|
||||
if(this.debug)
|
||||
{
|
||||
this.debugFolder.add(this.reveal, 'matcapsProgress').step(0.0001).min(0).max(1).name('matcapsProgress')
|
||||
this.debugFolder.add(this.reveal, 'floorShadowsProgress').step(0.0001).min(0).max(1).name('floorShadowsProgress')
|
||||
this.debugFolder.add(this.reveal, 'go').name('reveal')
|
||||
}
|
||||
}
|
||||
|
||||
setStartingScreen()
|
||||
{
|
||||
this.startingScreen = {}
|
||||
|
||||
// Area
|
||||
this.startingScreen.area = this.areas.add({
|
||||
position: new THREE.Vector2(0, 0),
|
||||
halfExtents: new THREE.Vector2(2.35, 1.5),
|
||||
hasKey: false,
|
||||
testCar: false,
|
||||
active: false
|
||||
})
|
||||
|
||||
// Loading label
|
||||
this.startingScreen.loadingLabel = {}
|
||||
this.startingScreen.loadingLabel.geometry = new THREE.PlaneBufferGeometry(2.5, 2.5 / 4)
|
||||
this.startingScreen.loadingLabel.image = new Image()
|
||||
this.startingScreen.loadingLabel.image.src = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAQAAAABABAMAAAAHc7SNAAAAMFBMVEUAAAD///9ra2ucnJzR0dH09PQmJiaNjY24uLjp6end3d1CQkLFxcVYWFiqqqp9fX3nQ5qrAAAEVUlEQVRo3u3YT08TQRQA8JEtW6CATGnDdvljaTwYE2IBI/HGRrwSetGTsZh4MPFQYiQe229gE++WePFY9Oqh1cRzieEDYIgXLxjPJu5M33vbZQszW+fgoS+B7ewO836znRl2lg1jGMP4P2Okw0yFvaKsklr3I99Tvl3iPPelGbQhKqxB4eN6N/7gVcsvbEAz1F4RLn67zzl/v6/oLvejGBQ9LsNphio4UFjmEAsVJuOK/zkDtc6w+gyTcZ3LyP6IAzjBDA+pj6LkEgAjW4kANsMAC6vmOvqAMU5RgVOTskQACicCmCcA9AXjkT5gj1MswqlxWcoTgKJ6HuAQAD5guNoAu8QpMnBul1ONMGD2PCBbRgDAKYq6AEtmXvtdj3S6GhRyW1t1DvkAgM0ggG7mu1t3xWFHFzAqv3wYCi0mY1UCGgiQPU+1oWIY8LoXcAA3qeYfr+kClvHW14PJ5OfCAgHYNAoDAORBQIrDvHjqH5c0ANTbORzBacbAQgUC2IAKAzI9gCSHlWEMLmgBPJxMvyARpIICALDm4nkAbwIA71EZx5UOgO48JnLoOhQIAN9sOgKoBoAE5r0aB8ARcNhtFzrg0VQmwCp8CAMeAADGc44S5GMBsF1aCEU2LcAcAPDCvwFytBDehCaUgJxRAKeF8BNUUQJ43iiAUlqwFKoBrTCAHjiagwEgU0YM5IYWYD4KoIgPwIXQwUbVgCXzgLpIBJNeDciWTQNskVsq1ADX/6kYBdCTjse5owbMiX+IpgGWOCPSuWpA2vN/TAMm5QTYg5IC4FdbMA0YF5Nb5s2rAaLyhzBgektGZWDArrgqi0U1QHxf38OABDwUDgTAjGfyPlTVgJT/67FBACbqyGYaaoBctQwD2vI4DecVAPkgZRhQlxPQks2rAePGAbZsRlaa1QBYEQBUHRCAmaXD0QDYxgFWdye05R9cDQCrmQYkeBA6gGXTgNEeQF4DMG4S4MLjOUZRA5A0CcjADgmjqgGwSwSg9wK1GIBS74KTgTxv/EHoiaVQsTOS5RoCJuiZyosB8EIrHpyowFiYofO0i4wCjhCQwL0hq2sCaFNM22S4JXloLk0AuLDTBzCBAAt3xykeA7CHe/mDbgdTvQ9GswSAwdbqA0giYASHjQUJnhQKhQ6z/d8rDA4hAG2Dsk042ejubHMM2nV6AMf93pCkaRjhh0WsWuz+6aasl2FwiAImReEts1/CSaFfwFouAJxC4RW+I4oCThBQE1X2WbKkBFDkqYDtJ0SHaYKq3pJJwCECjjiFPoC1w+2P0gumurgeBjT6AhIIGKOelGIAngWlFnRnMZjMIYBb7gtIIsAuYU+8GICpEhYyZVgIZ2g9rYYAX1lfAKvjnxzjnWrHALDn9K1h2k2aoI1ewGd2AWAVAVMHcKdW4wDYje739pNufJXhkJohgLu9zy4CHCKAJYUge4ddCojGyPrp9kaHmYjUi9N7+2wYwxjGZfEXMKxGE0GkkfIAAAAASUVORK5CYII='
|
||||
this.startingScreen.loadingLabel.texture = new THREE.Texture(this.startingScreen.loadingLabel.image)
|
||||
this.startingScreen.loadingLabel.texture.magFilter = THREE.NearestFilter
|
||||
this.startingScreen.loadingLabel.texture.minFilter = THREE.LinearFilter
|
||||
this.startingScreen.loadingLabel.texture.needsUpdate = true
|
||||
this.startingScreen.loadingLabel.material = new THREE.MeshBasicMaterial({ transparent: true, depthWrite: false, color: 0xffffff, alphaMap: this.startingScreen.loadingLabel.texture })
|
||||
this.startingScreen.loadingLabel.mesh = new THREE.Mesh(this.startingScreen.loadingLabel.geometry, this.startingScreen.loadingLabel.material)
|
||||
this.startingScreen.loadingLabel.mesh.matrixAutoUpdate = false
|
||||
this.container.add(this.startingScreen.loadingLabel.mesh)
|
||||
|
||||
// Start label
|
||||
this.startingScreen.startLabel = {}
|
||||
this.startingScreen.startLabel.geometry = new THREE.PlaneBufferGeometry(2.5, 2.5 / 4)
|
||||
this.startingScreen.startLabel.image = new Image()
|
||||
this.startingScreen.startLabel.image.src = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAQAAAABABAMAAAAHc7SNAAAAMFBMVEUAAAD///+cnJxra2vR0dHd3d0mJib09PRYWFjp6em4uLhCQkKqqqqNjY19fX3FxcV3XeRgAAADsklEQVRo3u3YsU9TQRwH8KNgLSDQg9ZCAak1IdE4PKPu1NTEsSzOMDl3I3GpcXAxBhLjXFxNjJgQJ2ON0Rnj4uAAEyv8B/L7tV++5/VN+CM69Ldwfa+534d7d793VzeIQQzi/49c4v5lPF/1vvhFm++rjIpcyErrmrSCuz+cxng1iL/If8drPJD2Lc/Iy4VhaZWlFd4tLPfuMc6e/5LvRilJA2SkVSQA8c0OsI0uNtIAU9rsB8y1rAAZjyimAUa1mQDAeGwF+MA+9lIA69qs9AMKVoDP8vhf35A+NiMAc7YJKFSrX7tcI8BW9+k/O/kz6zSunjSnncMHiQYBcmdXrh3xCVbc2WO8N/YZZI0AxxwMArKivmwAwFKSPmV0UwBbCpj5E+C+yzUbQAaJVwUSA9SFjwFgHQ0jAMrBWgzAPCtHgFFbQAlpEwKC2zWUQgJGbAH+naSdu/fTxQAthPL5/ADD6OCpQwCAsb6LsbEGcBluOAYBmG2fkMIawHVWXEsDIGUGpZCAIRsAS93DPgDbhUmUQgKe2NUB90hfhK0YwEJYHkYpJGDbqBKiB86CGLAlzd6/S8CEvh8sACiBvrSXCshKblWEgNy2vkAMAHwGfjECcJHOu5qUQgDm6vXulshZAXJNL9GJAeg+LxeKPQBj1gzgdlnuCWAhbOi7LwaU9u0A2VWPpUgAC+GR5k0iwBtnB3Bj3qMaRYB17X0IOQhYcjYA7guxxyIAGfd1HNqchPfly7aACQUshAA2W1r5G1yG415YpgB3qIIkAHBH2D075QnQ10fHDsCl+CoGSKpiN8kMAVqIN00BsitnVgKyPIBMB4ADKU92AA5BKQIgszjKBGBLagpwB5xZBGS6pbcuizQAXMA6NAK86OCQ3okAI55BQPe7VoDxXzU/iwPASgS4GAASAiYxWgYAzvAa1loA2AkAFQIU2zEELCJtDDgIAG0CFLvp7LblC2kAtF6eTEJJ2CBAr88bAXKY4WkASbzXmwt5AvTvohHA4WSUBmj2Jt+IThQChrAOLQC13vPFMAOAQwuyTAeAKVQto3OBDOdESh2YxNZPbpYBQNbEAoBfod7e1i1BiwB0voSZWgwAOWgtAGPhD18E8ASIiRIAXNPwXJBtcqMbAFAIr5weIJMAcIx1aAAIqk0lAuycompyFwBMHAsAZlj/lgw0rsy2AkhbsgK4Q+70CUBjxeFXsUb0G1HJDJC9rketZRcCWCJwHM8DgJm7b7ch+XizXm25QQxiEOcXvwGCWOhbCZC0qAAAAABJRU5ErkJggg=='
|
||||
this.startingScreen.startLabel.texture = new THREE.Texture(this.startingScreen.startLabel.image)
|
||||
this.startingScreen.startLabel.texture.magFilter = THREE.NearestFilter
|
||||
this.startingScreen.startLabel.texture.minFilter = THREE.LinearFilter
|
||||
this.startingScreen.startLabel.texture.needsUpdate = true
|
||||
this.startingScreen.startLabel.material = new THREE.MeshBasicMaterial({ transparent: true, depthWrite: false, color: 0xffffff, alphaMap: this.startingScreen.startLabel.texture })
|
||||
this.startingScreen.startLabel.material.opacity = 0
|
||||
this.startingScreen.startLabel.mesh = new THREE.Mesh(this.startingScreen.startLabel.geometry, this.startingScreen.startLabel.material)
|
||||
this.startingScreen.startLabel.mesh.matrixAutoUpdate = false
|
||||
this.container.add(this.startingScreen.startLabel.mesh)
|
||||
|
||||
// Progress
|
||||
this.resources.on('progress', (_progress) =>
|
||||
{
|
||||
// Update area
|
||||
this.startingScreen.area.floorBorder.material.uniforms.uAlpha.value = 1
|
||||
this.startingScreen.area.floorBorder.material.uniforms.uLoadProgress.value = _progress
|
||||
})
|
||||
|
||||
// Ready
|
||||
this.resources.on('ready', () =>
|
||||
{
|
||||
window.requestAnimationFrame(() =>
|
||||
{
|
||||
this.startingScreen.area.activate()
|
||||
|
||||
TweenLite.to(this.startingScreen.area.floorBorder.material.uniforms.uAlpha, 0.3, { value: 0.3 })
|
||||
TweenLite.to(this.startingScreen.loadingLabel.material, 0.3, { opacity: 0 })
|
||||
TweenLite.to(this.startingScreen.startLabel.material, 0.3, { opacity: 1, delay: 0.3 })
|
||||
})
|
||||
})
|
||||
|
||||
// On interact, reveal
|
||||
this.startingScreen.area.on('interact', () =>
|
||||
{
|
||||
this.startingScreen.area.deactivate()
|
||||
TweenLite.to(this.startingScreen.area.floorBorder.material.uniforms.uProgress, 0.3, { value: 0, delay: 0.4 })
|
||||
|
||||
TweenLite.to(this.startingScreen.startLabel.material, 0.3, { opacity: 0, delay: 0.4 })
|
||||
|
||||
this.start()
|
||||
|
||||
window.setTimeout(() =>
|
||||
{
|
||||
this.reveal.go()
|
||||
}, 600)
|
||||
})
|
||||
}
|
||||
|
||||
setSounds()
|
||||
{
|
||||
this.sounds = new Sounds({
|
||||
debug: this.debugFolder,
|
||||
time: this.time
|
||||
})
|
||||
}
|
||||
|
||||
setAxes()
|
||||
{
|
||||
this.axis = new THREE.AxesHelper()
|
||||
this.container.add(this.axis)
|
||||
}
|
||||
|
||||
setControls()
|
||||
{
|
||||
this.controls = new Controls({
|
||||
config: this.config,
|
||||
sizes: this.sizes,
|
||||
time: this.time,
|
||||
camera: this.camera,
|
||||
sounds: this.sounds
|
||||
})
|
||||
}
|
||||
|
||||
setMaterials()
|
||||
{
|
||||
this.materials = new Materials({
|
||||
resources: this.resources,
|
||||
debug: this.debugFolder
|
||||
})
|
||||
}
|
||||
|
||||
setFloor()
|
||||
{
|
||||
this.floor = new Floor({
|
||||
debug: this.debugFolder
|
||||
})
|
||||
|
||||
this.container.add(this.floor.container)
|
||||
}
|
||||
|
||||
setShadows()
|
||||
{
|
||||
this.shadows = new Shadows({
|
||||
time: this.time,
|
||||
debug: this.debugFolder,
|
||||
renderer: this.renderer,
|
||||
camera: this.camera
|
||||
})
|
||||
this.container.add(this.shadows.container)
|
||||
}
|
||||
|
||||
setPhysics()
|
||||
{
|
||||
this.physics = new Physics({
|
||||
config: this.config,
|
||||
debug: this.debug,
|
||||
time: this.time,
|
||||
sizes: this.sizes,
|
||||
controls: this.controls,
|
||||
sounds: this.sounds
|
||||
})
|
||||
|
||||
this.container.add(this.physics.models.container)
|
||||
}
|
||||
|
||||
setZones()
|
||||
{
|
||||
this.zones = new Zones({
|
||||
time: this.time,
|
||||
physics: this.physics,
|
||||
debug: this.debugFolder
|
||||
})
|
||||
this.container.add(this.zones.container)
|
||||
}
|
||||
|
||||
setAreas()
|
||||
{
|
||||
this.areas = new Areas({
|
||||
config: this.config,
|
||||
resources: this.resources,
|
||||
debug: this.debug,
|
||||
renderer: this.renderer,
|
||||
camera: this.camera,
|
||||
car: this.car,
|
||||
sounds: this.sounds,
|
||||
time: this.time
|
||||
})
|
||||
|
||||
this.container.add(this.areas.container)
|
||||
}
|
||||
|
||||
setTiles()
|
||||
{
|
||||
this.tiles = new Tiles({
|
||||
resources: this.resources,
|
||||
objects: this.objects,
|
||||
debug: this.debug
|
||||
})
|
||||
}
|
||||
|
||||
setWalls()
|
||||
{
|
||||
this.walls = new Walls({
|
||||
resources: this.resources,
|
||||
objects: this.objects
|
||||
})
|
||||
}
|
||||
|
||||
setObjects()
|
||||
{
|
||||
this.objects = new Objects({
|
||||
time: this.time,
|
||||
resources: this.resources,
|
||||
materials: this.materials,
|
||||
physics: this.physics,
|
||||
shadows: this.shadows,
|
||||
sounds: this.sounds,
|
||||
debug: this.debugFolder
|
||||
})
|
||||
this.container.add(this.objects.container)
|
||||
|
||||
// window.requestAnimationFrame(() =>
|
||||
// {
|
||||
// this.objects.merge.update()
|
||||
// })
|
||||
}
|
||||
|
||||
setCar()
|
||||
{
|
||||
this.car = new Car({
|
||||
time: this.time,
|
||||
resources: this.resources,
|
||||
objects: this.objects,
|
||||
physics: this.physics,
|
||||
shadows: this.shadows,
|
||||
materials: this.materials,
|
||||
controls: this.controls,
|
||||
sounds: this.sounds,
|
||||
renderer: this.renderer,
|
||||
camera: this.camera,
|
||||
debug: this.debugFolder,
|
||||
config: this.config
|
||||
})
|
||||
this.container.add(this.car.container)
|
||||
}
|
||||
|
||||
setSections()
|
||||
{
|
||||
this.sections = {}
|
||||
|
||||
// Generic options
|
||||
const options = {
|
||||
config: this.config,
|
||||
time: this.time,
|
||||
resources: this.resources,
|
||||
camera: this.camera,
|
||||
passes: this.passes,
|
||||
objects: this.objects,
|
||||
areas: this.areas,
|
||||
zones: this.zones,
|
||||
walls: this.walls,
|
||||
tiles: this.tiles,
|
||||
debug: this.debugFolder
|
||||
}
|
||||
|
||||
// // Distinction A
|
||||
// this.sections.distinctionA = new DistinctionASection({
|
||||
// ...options,
|
||||
// x: 0,
|
||||
// y: - 15
|
||||
// })
|
||||
// this.container.add(this.sections.distinctionA.container)
|
||||
|
||||
// // Distinction B
|
||||
// this.sections.distinctionB = new DistinctionBSection({
|
||||
// ...options,
|
||||
// x: 0,
|
||||
// y: - 15
|
||||
// })
|
||||
// this.container.add(this.sections.distinctionB.container)
|
||||
|
||||
// // Distinction C
|
||||
// this.sections.distinctionC = new DistinctionCSection({
|
||||
// ...options,
|
||||
// x: 0,
|
||||
// y: 0
|
||||
// })
|
||||
// this.container.add(this.sections.distinctionC.container)
|
||||
|
||||
// // Distinction D
|
||||
// this.sections.distinctionD = new DistinctionDSection({
|
||||
// ...options,
|
||||
// x: 0,
|
||||
// y: 0
|
||||
// })
|
||||
// this.container.add(this.sections.distinctionD.container)
|
||||
|
||||
// Intro
|
||||
this.sections.intro = new IntroSection({
|
||||
...options,
|
||||
x: 0,
|
||||
y: 0
|
||||
})
|
||||
this.container.add(this.sections.intro.container)
|
||||
|
||||
// Crossroads
|
||||
this.sections.crossroads = new CrossroadsSection({
|
||||
...options,
|
||||
x: 0,
|
||||
y: - 30
|
||||
})
|
||||
this.container.add(this.sections.crossroads.container)
|
||||
|
||||
// Projects
|
||||
this.sections.projects = new ProjectsSection({
|
||||
...options,
|
||||
x: 30,
|
||||
y: - 30
|
||||
// x: 0,
|
||||
// y: 0
|
||||
})
|
||||
this.container.add(this.sections.projects.container)
|
||||
|
||||
// Information
|
||||
this.sections.information = new InformationSection({
|
||||
...options,
|
||||
x: 1.2,
|
||||
y: - 55
|
||||
// x: 0,
|
||||
// y: - 10
|
||||
})
|
||||
this.container.add(this.sections.information.container)
|
||||
|
||||
// Playground
|
||||
this.sections.playground = new PlaygroundSection({
|
||||
...options,
|
||||
x: - 38,
|
||||
y: - 34
|
||||
// x: - 15,
|
||||
// y: - 4
|
||||
})
|
||||
this.container.add(this.sections.playground.container)
|
||||
}
|
||||
|
||||
setEasterEggs()
|
||||
{
|
||||
this.easterEggs = new EasterEggs({
|
||||
resources: this.resources,
|
||||
car: this.car,
|
||||
walls: this.walls,
|
||||
objects: this.objects,
|
||||
materials: this.materials,
|
||||
areas: this.areas,
|
||||
config: this.config,
|
||||
physics: this.physics
|
||||
})
|
||||
this.container.add(this.easterEggs.container)
|
||||
}
|
||||
}
|
BIN
src/models/area/enter.png
Normal file
After Width: | Height: | Size: 753 B |
BIN
src/models/area/keyEnter.png
Normal file
After Width: | Height: | Size: 703 B |
BIN
src/models/area/open.png
Normal file
After Width: | Height: | Size: 809 B |
BIN
src/models/area/questionMark.png
Normal file
After Width: | Height: | Size: 512 B |
BIN
src/models/area/reset.png
Normal file
After Width: | Height: | Size: 933 B |