Initial commit
@@ -0,0 +1,2 @@
|
|||||||
|
dist
|
||||||
|
node_modules
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"tabWidth": 4,
|
||||||
|
"useTabs": true,
|
||||||
|
"semi": false,
|
||||||
|
"printWidth": 90
|
||||||
|
}
|
||||||
@@ -0,0 +1,619 @@
|
|||||||
|
{
|
||||||
|
"name": "threejs-audio-starter",
|
||||||
|
"version": "0.0.0",
|
||||||
|
"lockfileVersion": 3,
|
||||||
|
"requires": true,
|
||||||
|
"packages": {
|
||||||
|
"": {
|
||||||
|
"name": "threejs-audio-starter",
|
||||||
|
"version": "0.0.0",
|
||||||
|
"dependencies": {
|
||||||
|
"lil-gui": "^0.17.0",
|
||||||
|
"three": "^0.148.0"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"vite": "^4.0.4"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@esbuild/android-arm": {
|
||||||
|
"version": "0.17.15",
|
||||||
|
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.17.15.tgz",
|
||||||
|
"integrity": "sha512-sRSOVlLawAktpMvDyJIkdLI/c/kdRTOqo8t6ImVxg8yT7LQDUYV5Rp2FKeEosLr6ZCja9UjYAzyRSxGteSJPYg==",
|
||||||
|
"cpu": [
|
||||||
|
"arm"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"android"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@esbuild/android-arm64": {
|
||||||
|
"version": "0.17.15",
|
||||||
|
"resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.17.15.tgz",
|
||||||
|
"integrity": "sha512-0kOB6Y7Br3KDVgHeg8PRcvfLkq+AccreK///B4Z6fNZGr/tNHX0z2VywCc7PTeWp+bPvjA5WMvNXltHw5QjAIA==",
|
||||||
|
"cpu": [
|
||||||
|
"arm64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"android"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@esbuild/android-x64": {
|
||||||
|
"version": "0.17.15",
|
||||||
|
"resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.17.15.tgz",
|
||||||
|
"integrity": "sha512-MzDqnNajQZ63YkaUWVl9uuhcWyEyh69HGpMIrf+acR4otMkfLJ4sUCxqwbCyPGicE9dVlrysI3lMcDBjGiBBcQ==",
|
||||||
|
"cpu": [
|
||||||
|
"x64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"android"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@esbuild/darwin-arm64": {
|
||||||
|
"version": "0.17.15",
|
||||||
|
"resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.17.15.tgz",
|
||||||
|
"integrity": "sha512-7siLjBc88Z4+6qkMDxPT2juf2e8SJxmsbNVKFY2ifWCDT72v5YJz9arlvBw5oB4W/e61H1+HDB/jnu8nNg0rLA==",
|
||||||
|
"cpu": [
|
||||||
|
"arm64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"darwin"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@esbuild/darwin-x64": {
|
||||||
|
"version": "0.17.15",
|
||||||
|
"resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.17.15.tgz",
|
||||||
|
"integrity": "sha512-NbImBas2rXwYI52BOKTW342Tm3LTeVlaOQ4QPZ7XuWNKiO226DisFk/RyPk3T0CKZkKMuU69yOvlapJEmax7cg==",
|
||||||
|
"cpu": [
|
||||||
|
"x64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"darwin"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@esbuild/freebsd-arm64": {
|
||||||
|
"version": "0.17.15",
|
||||||
|
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.17.15.tgz",
|
||||||
|
"integrity": "sha512-Xk9xMDjBVG6CfgoqlVczHAdJnCs0/oeFOspFap5NkYAmRCT2qTn1vJWA2f419iMtsHSLm+O8B6SLV/HlY5cYKg==",
|
||||||
|
"cpu": [
|
||||||
|
"arm64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"freebsd"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@esbuild/freebsd-x64": {
|
||||||
|
"version": "0.17.15",
|
||||||
|
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.17.15.tgz",
|
||||||
|
"integrity": "sha512-3TWAnnEOdclvb2pnfsTWtdwthPfOz7qAfcwDLcfZyGJwm1SRZIMOeB5FODVhnM93mFSPsHB9b/PmxNNbSnd0RQ==",
|
||||||
|
"cpu": [
|
||||||
|
"x64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"freebsd"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@esbuild/linux-arm": {
|
||||||
|
"version": "0.17.15",
|
||||||
|
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.17.15.tgz",
|
||||||
|
"integrity": "sha512-MLTgiXWEMAMr8nmS9Gigx43zPRmEfeBfGCwxFQEMgJ5MC53QKajaclW6XDPjwJvhbebv+RzK05TQjvH3/aM4Xw==",
|
||||||
|
"cpu": [
|
||||||
|
"arm"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@esbuild/linux-arm64": {
|
||||||
|
"version": "0.17.15",
|
||||||
|
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.17.15.tgz",
|
||||||
|
"integrity": "sha512-T0MVnYw9KT6b83/SqyznTs/3Jg2ODWrZfNccg11XjDehIved2oQfrX/wVuev9N936BpMRaTR9I1J0tdGgUgpJA==",
|
||||||
|
"cpu": [
|
||||||
|
"arm64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@esbuild/linux-ia32": {
|
||||||
|
"version": "0.17.15",
|
||||||
|
"resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.17.15.tgz",
|
||||||
|
"integrity": "sha512-wp02sHs015T23zsQtU4Cj57WiteiuASHlD7rXjKUyAGYzlOKDAjqK6bk5dMi2QEl/KVOcsjwL36kD+WW7vJt8Q==",
|
||||||
|
"cpu": [
|
||||||
|
"ia32"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@esbuild/linux-loong64": {
|
||||||
|
"version": "0.17.15",
|
||||||
|
"resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.17.15.tgz",
|
||||||
|
"integrity": "sha512-k7FsUJjGGSxwnBmMh8d7IbObWu+sF/qbwc+xKZkBe/lTAF16RqxRCnNHA7QTd3oS2AfGBAnHlXL67shV5bBThQ==",
|
||||||
|
"cpu": [
|
||||||
|
"loong64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@esbuild/linux-mips64el": {
|
||||||
|
"version": "0.17.15",
|
||||||
|
"resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.17.15.tgz",
|
||||||
|
"integrity": "sha512-ZLWk6czDdog+Q9kE/Jfbilu24vEe/iW/Sj2d8EVsmiixQ1rM2RKH2n36qfxK4e8tVcaXkvuV3mU5zTZviE+NVQ==",
|
||||||
|
"cpu": [
|
||||||
|
"mips64el"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@esbuild/linux-ppc64": {
|
||||||
|
"version": "0.17.15",
|
||||||
|
"resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.17.15.tgz",
|
||||||
|
"integrity": "sha512-mY6dPkIRAiFHRsGfOYZC8Q9rmr8vOBZBme0/j15zFUKM99d4ILY4WpOC7i/LqoY+RE7KaMaSfvY8CqjJtuO4xg==",
|
||||||
|
"cpu": [
|
||||||
|
"ppc64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@esbuild/linux-riscv64": {
|
||||||
|
"version": "0.17.15",
|
||||||
|
"resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.17.15.tgz",
|
||||||
|
"integrity": "sha512-EcyUtxffdDtWjjwIH8sKzpDRLcVtqANooMNASO59y+xmqqRYBBM7xVLQhqF7nksIbm2yHABptoioS9RAbVMWVA==",
|
||||||
|
"cpu": [
|
||||||
|
"riscv64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@esbuild/linux-s390x": {
|
||||||
|
"version": "0.17.15",
|
||||||
|
"resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.17.15.tgz",
|
||||||
|
"integrity": "sha512-BuS6Jx/ezxFuHxgsfvz7T4g4YlVrmCmg7UAwboeyNNg0OzNzKsIZXpr3Sb/ZREDXWgt48RO4UQRDBxJN3B9Rbg==",
|
||||||
|
"cpu": [
|
||||||
|
"s390x"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@esbuild/linux-x64": {
|
||||||
|
"version": "0.17.15",
|
||||||
|
"resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.17.15.tgz",
|
||||||
|
"integrity": "sha512-JsdS0EgEViwuKsw5tiJQo9UdQdUJYuB+Mf6HxtJSPN35vez1hlrNb1KajvKWF5Sa35j17+rW1ECEO9iNrIXbNg==",
|
||||||
|
"cpu": [
|
||||||
|
"x64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@esbuild/netbsd-x64": {
|
||||||
|
"version": "0.17.15",
|
||||||
|
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.17.15.tgz",
|
||||||
|
"integrity": "sha512-R6fKjtUysYGym6uXf6qyNephVUQAGtf3n2RCsOST/neIwPqRWcnc3ogcielOd6pT+J0RDR1RGcy0ZY7d3uHVLA==",
|
||||||
|
"cpu": [
|
||||||
|
"x64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"netbsd"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@esbuild/openbsd-x64": {
|
||||||
|
"version": "0.17.15",
|
||||||
|
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.17.15.tgz",
|
||||||
|
"integrity": "sha512-mVD4PGc26b8PI60QaPUltYKeSX0wxuy0AltC+WCTFwvKCq2+OgLP4+fFd+hZXzO2xW1HPKcytZBdjqL6FQFa7w==",
|
||||||
|
"cpu": [
|
||||||
|
"x64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"openbsd"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@esbuild/sunos-x64": {
|
||||||
|
"version": "0.17.15",
|
||||||
|
"resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.17.15.tgz",
|
||||||
|
"integrity": "sha512-U6tYPovOkw3459t2CBwGcFYfFRjivcJJc1WC8Q3funIwX8x4fP+R6xL/QuTPNGOblbq/EUDxj9GU+dWKX0oWlQ==",
|
||||||
|
"cpu": [
|
||||||
|
"x64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"sunos"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@esbuild/win32-arm64": {
|
||||||
|
"version": "0.17.15",
|
||||||
|
"resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.17.15.tgz",
|
||||||
|
"integrity": "sha512-W+Z5F++wgKAleDABemiyXVnzXgvRFs+GVKThSI+mGgleLWluv0D7Diz4oQpgdpNzh4i2nNDzQtWbjJiqutRp6Q==",
|
||||||
|
"cpu": [
|
||||||
|
"arm64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"win32"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@esbuild/win32-ia32": {
|
||||||
|
"version": "0.17.15",
|
||||||
|
"resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.17.15.tgz",
|
||||||
|
"integrity": "sha512-Muz/+uGgheShKGqSVS1KsHtCyEzcdOn/W/Xbh6H91Etm+wiIfwZaBn1W58MeGtfI8WA961YMHFYTthBdQs4t+w==",
|
||||||
|
"cpu": [
|
||||||
|
"ia32"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"win32"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@esbuild/win32-x64": {
|
||||||
|
"version": "0.17.15",
|
||||||
|
"resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.17.15.tgz",
|
||||||
|
"integrity": "sha512-DjDa9ywLUUmjhV2Y9wUTIF+1XsmuFGvZoCmOWkli1XcNAh5t25cc7fgsCx4Zi/Uurep3TTLyDiKATgGEg61pkA==",
|
||||||
|
"cpu": [
|
||||||
|
"x64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"win32"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/esbuild": {
|
||||||
|
"version": "0.17.15",
|
||||||
|
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.17.15.tgz",
|
||||||
|
"integrity": "sha512-LBUV2VsUIc/iD9ME75qhT4aJj0r75abCVS0jakhFzOtR7TQsqQA5w0tZ+KTKnwl3kXE0MhskNdHDh/I5aCR1Zw==",
|
||||||
|
"dev": true,
|
||||||
|
"hasInstallScript": true,
|
||||||
|
"bin": {
|
||||||
|
"esbuild": "bin/esbuild"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
},
|
||||||
|
"optionalDependencies": {
|
||||||
|
"@esbuild/android-arm": "0.17.15",
|
||||||
|
"@esbuild/android-arm64": "0.17.15",
|
||||||
|
"@esbuild/android-x64": "0.17.15",
|
||||||
|
"@esbuild/darwin-arm64": "0.17.15",
|
||||||
|
"@esbuild/darwin-x64": "0.17.15",
|
||||||
|
"@esbuild/freebsd-arm64": "0.17.15",
|
||||||
|
"@esbuild/freebsd-x64": "0.17.15",
|
||||||
|
"@esbuild/linux-arm": "0.17.15",
|
||||||
|
"@esbuild/linux-arm64": "0.17.15",
|
||||||
|
"@esbuild/linux-ia32": "0.17.15",
|
||||||
|
"@esbuild/linux-loong64": "0.17.15",
|
||||||
|
"@esbuild/linux-mips64el": "0.17.15",
|
||||||
|
"@esbuild/linux-ppc64": "0.17.15",
|
||||||
|
"@esbuild/linux-riscv64": "0.17.15",
|
||||||
|
"@esbuild/linux-s390x": "0.17.15",
|
||||||
|
"@esbuild/linux-x64": "0.17.15",
|
||||||
|
"@esbuild/netbsd-x64": "0.17.15",
|
||||||
|
"@esbuild/openbsd-x64": "0.17.15",
|
||||||
|
"@esbuild/sunos-x64": "0.17.15",
|
||||||
|
"@esbuild/win32-arm64": "0.17.15",
|
||||||
|
"@esbuild/win32-ia32": "0.17.15",
|
||||||
|
"@esbuild/win32-x64": "0.17.15"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/fsevents": {
|
||||||
|
"version": "2.3.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
|
||||||
|
"integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
|
||||||
|
"dev": true,
|
||||||
|
"hasInstallScript": true,
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"darwin"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/function-bind": {
|
||||||
|
"version": "1.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
|
||||||
|
"integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"node_modules/has": {
|
||||||
|
"version": "1.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
|
||||||
|
"integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"function-bind": "^1.1.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.4.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/is-core-module": {
|
||||||
|
"version": "2.11.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.11.0.tgz",
|
||||||
|
"integrity": "sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"has": "^1.0.3"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/lil-gui": {
|
||||||
|
"version": "0.17.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/lil-gui/-/lil-gui-0.17.0.tgz",
|
||||||
|
"integrity": "sha512-MVBHmgY+uEbmJNApAaPbtvNh1RCAeMnKym82SBjtp5rODTYKWtM+MXHCifLe2H2Ti1HuBGBtK/5SyG4ShQ3pUQ=="
|
||||||
|
},
|
||||||
|
"node_modules/nanoid": {
|
||||||
|
"version": "3.3.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.6.tgz",
|
||||||
|
"integrity": "sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==",
|
||||||
|
"dev": true,
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/ai"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"bin": {
|
||||||
|
"nanoid": "bin/nanoid.cjs"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/path-parse": {
|
||||||
|
"version": "1.0.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
|
||||||
|
"integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"node_modules/picocolors": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz",
|
||||||
|
"integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"node_modules/postcss": {
|
||||||
|
"version": "8.4.21",
|
||||||
|
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.21.tgz",
|
||||||
|
"integrity": "sha512-tP7u/Sn/dVxK2NnruI4H9BG+x+Wxz6oeZ1cJ8P6G/PZY0IKk4k/63TDsQf2kQq3+qoJeLm2kIBUNlZe3zgb4Zg==",
|
||||||
|
"dev": true,
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/postcss/"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "tidelift",
|
||||||
|
"url": "https://tidelift.com/funding/github/npm/postcss"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"dependencies": {
|
||||||
|
"nanoid": "^3.3.4",
|
||||||
|
"picocolors": "^1.0.0",
|
||||||
|
"source-map-js": "^1.0.2"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "^10 || ^12 || >=14"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/resolve": {
|
||||||
|
"version": "1.22.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.2.tgz",
|
||||||
|
"integrity": "sha512-Sb+mjNHOULsBv818T40qSPeRiuWLyaGMa5ewydRLFimneixmVy2zdivRl+AF6jaYPC8ERxGDmFSiqui6SfPd+g==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"is-core-module": "^2.11.0",
|
||||||
|
"path-parse": "^1.0.7",
|
||||||
|
"supports-preserve-symlinks-flag": "^1.0.0"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"resolve": "bin/resolve"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/rollup": {
|
||||||
|
"version": "3.20.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/rollup/-/rollup-3.20.2.tgz",
|
||||||
|
"integrity": "sha512-3zwkBQl7Ai7MFYQE0y1MeQ15+9jsi7XxfrqwTb/9EK8D9C9+//EBR4M+CuA1KODRaNbFez/lWxA5vhEGZp4MUg==",
|
||||||
|
"dev": true,
|
||||||
|
"bin": {
|
||||||
|
"rollup": "dist/bin/rollup"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=14.18.0",
|
||||||
|
"npm": ">=8.0.0"
|
||||||
|
},
|
||||||
|
"optionalDependencies": {
|
||||||
|
"fsevents": "~2.3.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/source-map-js": {
|
||||||
|
"version": "1.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz",
|
||||||
|
"integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==",
|
||||||
|
"dev": true,
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.10.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/supports-preserve-symlinks-flag": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz",
|
||||||
|
"integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==",
|
||||||
|
"dev": true,
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.4"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/three": {
|
||||||
|
"version": "0.148.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/three/-/three-0.148.0.tgz",
|
||||||
|
"integrity": "sha512-8uzVV+qhTPi0bOFs/3te3RW6hb3urL8jYEl6irjCWo/l6sr8MPNMcClFev/MMYeIxr0gmDcoXTy/8LXh/LXkfw=="
|
||||||
|
},
|
||||||
|
"node_modules/vite": {
|
||||||
|
"version": "4.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/vite/-/vite-4.2.1.tgz",
|
||||||
|
"integrity": "sha512-7MKhqdy0ISo4wnvwtqZkjke6XN4taqQ2TBaTccLIpOKv7Vp2h4Y+NpmWCnGDeSvvn45KxvWgGyb0MkHvY1vgbg==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"esbuild": "^0.17.5",
|
||||||
|
"postcss": "^8.4.21",
|
||||||
|
"resolve": "^1.22.1",
|
||||||
|
"rollup": "^3.18.0"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"vite": "bin/vite.js"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "^14.18.0 || >=16.0.0"
|
||||||
|
},
|
||||||
|
"optionalDependencies": {
|
||||||
|
"fsevents": "~2.3.2"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/node": ">= 14",
|
||||||
|
"less": "*",
|
||||||
|
"sass": "*",
|
||||||
|
"stylus": "*",
|
||||||
|
"sugarss": "*",
|
||||||
|
"terser": "^5.4.0"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/node": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"less": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"sass": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"stylus": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"sugarss": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"terser": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
{
|
||||||
|
"name": "threejs-audio-starter",
|
||||||
|
"private": true,
|
||||||
|
"version": "0.0.0",
|
||||||
|
"type": "module",
|
||||||
|
"scripts": {
|
||||||
|
"dev": "vite",
|
||||||
|
"build": "vite build"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"vite": "^4.0.4"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"lil-gui": "^0.17.0",
|
||||||
|
"three": "^0.148.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
## Setup
|
||||||
|
Download [Node.js](https://nodejs.org/en/download/).
|
||||||
|
Run this followed commands:
|
||||||
|
|
||||||
|
``` bash
|
||||||
|
# Install dependencies (only the first time)
|
||||||
|
npm install
|
||||||
|
|
||||||
|
# Run the local server at localhost:8080
|
||||||
|
npm run dev
|
||||||
|
|
||||||
|
# Build for production in the dist/ directory
|
||||||
|
npm run build
|
||||||
|
```
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
import * as THREE from "three"
|
||||||
|
import Experience from "./Experience.js"
|
||||||
|
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls.js"
|
||||||
|
|
||||||
|
export default class Camera {
|
||||||
|
constructor() {
|
||||||
|
this.experience = new Experience()
|
||||||
|
this.sizes = this.experience.sizes
|
||||||
|
this.scene = this.experience.scene
|
||||||
|
this.canvas = this.experience.canvas
|
||||||
|
|
||||||
|
this.setInstance()
|
||||||
|
// this.setControls()
|
||||||
|
}
|
||||||
|
|
||||||
|
setInstance() {
|
||||||
|
this.instance = new THREE.PerspectiveCamera(
|
||||||
|
35,
|
||||||
|
this.sizes.width / this.sizes.height,
|
||||||
|
0.1,
|
||||||
|
100
|
||||||
|
)
|
||||||
|
this.instance.position.set(0, 0, 20)
|
||||||
|
|
||||||
|
this.scene.add(this.instance)
|
||||||
|
}
|
||||||
|
|
||||||
|
setControls() {
|
||||||
|
this.controls = new OrbitControls(this.instance, this.canvas)
|
||||||
|
this.controls.enableDamping = true
|
||||||
|
}
|
||||||
|
|
||||||
|
resize() {
|
||||||
|
this.instance.aspect = this.sizes.width / this.sizes.height
|
||||||
|
this.instance.updateProjectionMatrix()
|
||||||
|
}
|
||||||
|
|
||||||
|
update() {
|
||||||
|
// this.controls.update()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,93 @@
|
|||||||
|
import * as THREE from "three"
|
||||||
|
|
||||||
|
import Debug from "./Utils/Debug.js"
|
||||||
|
import Sizes from "./Utils/Sizes.js"
|
||||||
|
import Time from "./Utils/Time.js"
|
||||||
|
import Camera from "./Camera.js"
|
||||||
|
import Renderer from "./Renderer.js"
|
||||||
|
import World from "./World/World.js"
|
||||||
|
import Resources from "./Utils/Resources.js"
|
||||||
|
import AudioAnalyser from "./Utils/AudioAnalyser.js"
|
||||||
|
import Interface from "./Interface.js"
|
||||||
|
|
||||||
|
import sources from "./sources.js"
|
||||||
|
|
||||||
|
let instance = null
|
||||||
|
|
||||||
|
export default class Experience {
|
||||||
|
constructor(_canvas) {
|
||||||
|
// Singleton
|
||||||
|
if (instance) {
|
||||||
|
return instance
|
||||||
|
}
|
||||||
|
instance = this
|
||||||
|
|
||||||
|
// Global access
|
||||||
|
window.experience = this
|
||||||
|
|
||||||
|
// Options
|
||||||
|
this.canvas = _canvas
|
||||||
|
|
||||||
|
// Setup
|
||||||
|
this.debug = new Debug()
|
||||||
|
this.sizes = new Sizes()
|
||||||
|
this.time = new Time()
|
||||||
|
this.scene = new THREE.Scene()
|
||||||
|
this.resources = new Resources(sources)
|
||||||
|
this.interface = new Interface()
|
||||||
|
this.camera = new Camera()
|
||||||
|
this.renderer = new Renderer()
|
||||||
|
this.world = new World()
|
||||||
|
this.audio = new AudioAnalyser("audio/onemoretime.mp3")
|
||||||
|
|
||||||
|
// Resize event
|
||||||
|
this.sizes.on("resize", () => {
|
||||||
|
this.resize()
|
||||||
|
})
|
||||||
|
|
||||||
|
// Time tick event
|
||||||
|
this.time.on("tick", () => {
|
||||||
|
this.update()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
resize() {
|
||||||
|
this.camera.resize()
|
||||||
|
this.renderer.resize()
|
||||||
|
}
|
||||||
|
|
||||||
|
update() {
|
||||||
|
this.camera.update()
|
||||||
|
this.world.update()
|
||||||
|
this.renderer.update()
|
||||||
|
this.audio.update()
|
||||||
|
}
|
||||||
|
|
||||||
|
destroy() {
|
||||||
|
this.sizes.off("resize")
|
||||||
|
this.time.off("tick")
|
||||||
|
|
||||||
|
// Traverse the whole scene
|
||||||
|
this.scene.traverse((child) => {
|
||||||
|
// Test if it's a mesh
|
||||||
|
if (child instanceof THREE.Mesh) {
|
||||||
|
child.geometry.dispose()
|
||||||
|
|
||||||
|
// Loop through the material properties
|
||||||
|
for (const key in child.material) {
|
||||||
|
const value = child.material[key]
|
||||||
|
|
||||||
|
// Test if there is a dispose function
|
||||||
|
if (value && typeof value.dispose === "function") {
|
||||||
|
value.dispose()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
this.camera.controls.dispose()
|
||||||
|
this.renderer.instance.dispose()
|
||||||
|
|
||||||
|
if (this.debug.active) this.debug.ui.destroy()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
import EventEmitter from './Utils/EventEmitter.js'
|
||||||
|
import Experience from './Experience.js'
|
||||||
|
|
||||||
|
export default class Interface extends EventEmitter
|
||||||
|
{
|
||||||
|
constructor()
|
||||||
|
{
|
||||||
|
super()
|
||||||
|
|
||||||
|
this.experience = new Experience()
|
||||||
|
this.resources = this.experience.resources
|
||||||
|
|
||||||
|
this.$loader = document.querySelector('.loader')
|
||||||
|
this.$startBtn = document.querySelector('.start-btn')
|
||||||
|
|
||||||
|
// Resources loaded
|
||||||
|
this.resources.on('ready', () => {
|
||||||
|
this.$loader.classList.remove('is-active')
|
||||||
|
this.$startBtn.classList.add('is-active')
|
||||||
|
|
||||||
|
this.$startBtn.addEventListener('click', () => {
|
||||||
|
this.trigger('startExperience')
|
||||||
|
this.$startBtn.classList.remove('is-active')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
import * as THREE from 'three'
|
||||||
|
import Experience from './Experience.js'
|
||||||
|
|
||||||
|
export default class Renderer
|
||||||
|
{
|
||||||
|
constructor()
|
||||||
|
{
|
||||||
|
this.experience = new Experience()
|
||||||
|
this.canvas = this.experience.canvas
|
||||||
|
this.sizes = this.experience.sizes
|
||||||
|
this.scene = this.experience.scene
|
||||||
|
this.camera = this.experience.camera
|
||||||
|
|
||||||
|
this.setInstance()
|
||||||
|
}
|
||||||
|
|
||||||
|
setInstance()
|
||||||
|
{
|
||||||
|
this.instance = new THREE.WebGLRenderer({
|
||||||
|
canvas: this.canvas,
|
||||||
|
antialias: true
|
||||||
|
})
|
||||||
|
this.instance.physicallyCorrectLights = true
|
||||||
|
this.instance.outputEncoding = THREE.sRGBEncoding
|
||||||
|
this.instance.toneMapping = THREE.CineonToneMapping
|
||||||
|
this.instance.toneMappingExposure = 1.75
|
||||||
|
this.instance.shadowMap.enabled = true
|
||||||
|
this.instance.shadowMap.type = THREE.PCFSoftShadowMap
|
||||||
|
this.instance.setClearColor('#ffffff')
|
||||||
|
this.instance.setSize(this.sizes.width, this.sizes.height)
|
||||||
|
this.instance.setPixelRatio(Math.min(this.sizes.pixelRatio, 2))
|
||||||
|
}
|
||||||
|
|
||||||
|
resize()
|
||||||
|
{
|
||||||
|
this.instance.setSize(this.sizes.width, this.sizes.height)
|
||||||
|
this.instance.setPixelRatio(Math.min(this.sizes.pixelRatio, 2))
|
||||||
|
}
|
||||||
|
|
||||||
|
update()
|
||||||
|
{
|
||||||
|
this.instance.render(this.scene, this.camera.instance)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,119 @@
|
|||||||
|
import EventEmitter from "./EventEmitter.js"
|
||||||
|
import Experience from "../Experience.js"
|
||||||
|
|
||||||
|
export default class AudioAnalyser extends EventEmitter {
|
||||||
|
constructor(audioPath, fftSize = 1024) {
|
||||||
|
super()
|
||||||
|
|
||||||
|
this.experience = new Experience()
|
||||||
|
this.scene = this.experience.scene
|
||||||
|
this.resources = this.experience.resources
|
||||||
|
this.interface = this.experience.interface
|
||||||
|
|
||||||
|
this.averageFrequencies = 0
|
||||||
|
this.averageLowFrequencies = 0
|
||||||
|
this.averageMidFrequencies = 0
|
||||||
|
this.averageHighFrequencies = 0
|
||||||
|
|
||||||
|
this.interface.on("startExperience", () => {
|
||||||
|
this.audioContext = new AudioContext()
|
||||||
|
this.analyser = this.audioContext.createAnalyser()
|
||||||
|
this.analyser.fftSize = fftSize
|
||||||
|
this.frequencyData = new Uint8Array(this.analyser.frequencyBinCount)
|
||||||
|
this.timeData = new Uint8Array(this.analyser.fftSize)
|
||||||
|
|
||||||
|
this.isPlaying = false
|
||||||
|
this.gainNode = this.audioContext.createGain()
|
||||||
|
this.gainNode.gain.value = 0.1
|
||||||
|
|
||||||
|
this.audio = new Audio(audioPath)
|
||||||
|
this.source = this.audioContext.createMediaElementSource(this.audio)
|
||||||
|
this.source.connect(this.analyser)
|
||||||
|
this.source.connect(this.gainNode)
|
||||||
|
this.gainNode.connect(this.audioContext.destination)
|
||||||
|
this.play()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
play() {
|
||||||
|
if (this.isPlaying) return
|
||||||
|
|
||||||
|
this.isPlaying = true
|
||||||
|
this.audio.play()
|
||||||
|
}
|
||||||
|
|
||||||
|
pause() {
|
||||||
|
if (!this.isPlaying) return
|
||||||
|
|
||||||
|
this.isPlaying = false
|
||||||
|
this.audio.pause()
|
||||||
|
}
|
||||||
|
|
||||||
|
update() {
|
||||||
|
if (!this.isPlaying) return
|
||||||
|
|
||||||
|
this.analyser.getByteFrequencyData(this.frequencyData)
|
||||||
|
this.analyser.getByteTimeDomainData(this.timeData)
|
||||||
|
|
||||||
|
this.setAverageLowFrequencies()
|
||||||
|
this.setAverageMediumFrequencies()
|
||||||
|
this.setAverageHighFrequencies()
|
||||||
|
this.setAverageFrequencies()
|
||||||
|
}
|
||||||
|
|
||||||
|
setVolume(gain) {
|
||||||
|
this.gainNode.gain.value = gain
|
||||||
|
}
|
||||||
|
|
||||||
|
setAverageFrequencies() {
|
||||||
|
// on add tous les éléments de l'array entre eux
|
||||||
|
const total = this.frequencyData.reduce((acc, cur) => acc + cur)
|
||||||
|
// on fait la moyenne des fréquences
|
||||||
|
const average = total / this.frequencyData.length
|
||||||
|
|
||||||
|
this.averageFrequencies = average / 255
|
||||||
|
}
|
||||||
|
|
||||||
|
setAverageLowFrequencies() {
|
||||||
|
// On divise l'array de toutes les frequences par 3 et on prend la première partie (les fréquences les plus hautes)
|
||||||
|
|
||||||
|
const third = Math.round(this.frequencyData.length / 3)
|
||||||
|
const lowFrequencies = this.frequencyData.slice(0, third)
|
||||||
|
|
||||||
|
// on add tous les éléments de l'array entre eux
|
||||||
|
const total = lowFrequencies.reduce((acc, cur) => acc + cur)
|
||||||
|
// on fait la moyenne des fréquences
|
||||||
|
const average = total / lowFrequencies.length
|
||||||
|
|
||||||
|
this.averageLowFrequencies = average / 255
|
||||||
|
}
|
||||||
|
|
||||||
|
setAverageMediumFrequencies() {
|
||||||
|
// On divise l'array de toutes les frequences par 3 et on prend la deuxieme partie (les fréquences mid)
|
||||||
|
const third = Math.round(this.frequencyData.length / 3)
|
||||||
|
const mediumFrequencies = this.frequencyData.slice(third, third * 2)
|
||||||
|
|
||||||
|
// on add tous les éléments de l'array entre eux
|
||||||
|
const total = mediumFrequencies.reduce((acc, cur) => acc + cur)
|
||||||
|
// on fait la moyenne des fréquences
|
||||||
|
const average = total / mediumFrequencies.length
|
||||||
|
|
||||||
|
this.averageMidFrequencies = average / 255
|
||||||
|
}
|
||||||
|
|
||||||
|
setAverageHighFrequencies() {
|
||||||
|
// On divise l'array de toutes les frequences par 3 et on prend la troiseme partie (les fréquences hautes)
|
||||||
|
const third = Math.round(this.frequencyData.length / 3)
|
||||||
|
const highFrequencies = this.frequencyData.slice(
|
||||||
|
third * 2,
|
||||||
|
this.frequencyData.length - 1
|
||||||
|
)
|
||||||
|
|
||||||
|
// on add tous les éléments de l'array entre eux
|
||||||
|
const total = highFrequencies.reduce((acc, cur) => acc + cur)
|
||||||
|
// on fait la moyenne des fréquences
|
||||||
|
const average = total / highFrequencies.length
|
||||||
|
|
||||||
|
this.averageHighFrequencies = average / 255
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
import * as dat from 'lil-gui'
|
||||||
|
|
||||||
|
export default class Debug
|
||||||
|
{
|
||||||
|
constructor()
|
||||||
|
{
|
||||||
|
this.active = window.location.hash === '#debug'
|
||||||
|
|
||||||
|
if(this.active)
|
||||||
|
{
|
||||||
|
this.ui = new dat.GUI()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,197 @@
|
|||||||
|
export default class EventEmitter
|
||||||
|
{
|
||||||
|
constructor()
|
||||||
|
{
|
||||||
|
this.callbacks = {}
|
||||||
|
this.callbacks.base = {}
|
||||||
|
}
|
||||||
|
|
||||||
|
on(_names, callback)
|
||||||
|
{
|
||||||
|
// 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((_name) =>
|
||||||
|
{
|
||||||
|
// Resolve name
|
||||||
|
const name = this.resolveName(_name)
|
||||||
|
|
||||||
|
// Create namespace if not exist
|
||||||
|
if(!(this.callbacks[ name.namespace ] instanceof Object))
|
||||||
|
this.callbacks[ name.namespace ] = {}
|
||||||
|
|
||||||
|
// Create callback if not exist
|
||||||
|
if(!(this.callbacks[ name.namespace ][ name.value ] instanceof Array))
|
||||||
|
this.callbacks[ name.namespace ][ name.value ] = []
|
||||||
|
|
||||||
|
// Add callback
|
||||||
|
this.callbacks[ name.namespace ][ name.value ].push(callback)
|
||||||
|
})
|
||||||
|
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
off(_names)
|
||||||
|
{
|
||||||
|
// Errors
|
||||||
|
if(typeof _names === 'undefined' || _names === '')
|
||||||
|
{
|
||||||
|
console.warn('wrong name')
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Resolve names
|
||||||
|
const names = this.resolveNames(_names)
|
||||||
|
|
||||||
|
// Each name
|
||||||
|
names.forEach((_name) =>
|
||||||
|
{
|
||||||
|
// Resolve name
|
||||||
|
const name = this.resolveName(_name)
|
||||||
|
|
||||||
|
// Remove namespace
|
||||||
|
if(name.namespace !== 'base' && name.value === '')
|
||||||
|
{
|
||||||
|
delete this.callbacks[ name.namespace ]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove specific callback in namespace
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Default
|
||||||
|
if(name.namespace === 'base')
|
||||||
|
{
|
||||||
|
// Try to remove from each namespace
|
||||||
|
for(const namespace in this.callbacks)
|
||||||
|
{
|
||||||
|
if(this.callbacks[ namespace ] instanceof Object && this.callbacks[ namespace ][ name.value ] instanceof Array)
|
||||||
|
{
|
||||||
|
delete this.callbacks[ namespace ][ name.value ]
|
||||||
|
|
||||||
|
// Remove namespace if empty
|
||||||
|
if(Object.keys(this.callbacks[ namespace ]).length === 0)
|
||||||
|
delete this.callbacks[ namespace ]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Specified namespace
|
||||||
|
else if(this.callbacks[ name.namespace ] instanceof Object && this.callbacks[ name.namespace ][ name.value ] instanceof Array)
|
||||||
|
{
|
||||||
|
delete this.callbacks[ name.namespace ][ name.value ]
|
||||||
|
|
||||||
|
// Remove namespace if empty
|
||||||
|
if(Object.keys(this.callbacks[ name.namespace ]).length === 0)
|
||||||
|
delete this.callbacks[ name.namespace ]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
trigger(_name, _args)
|
||||||
|
{
|
||||||
|
// Errors
|
||||||
|
if(typeof _name === 'undefined' || _name === '')
|
||||||
|
{
|
||||||
|
console.warn('wrong name')
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
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 this.callbacks)
|
||||||
|
{
|
||||||
|
if(this.callbacks[ namespace ] instanceof Object && this.callbacks[ namespace ][ name.value ] instanceof Array)
|
||||||
|
{
|
||||||
|
this.callbacks[ namespace ][ name.value ].forEach(function(callback)
|
||||||
|
{
|
||||||
|
result = callback.apply(this, 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
|
||||||
|
}
|
||||||
|
|
||||||
|
this.callbacks[ name.namespace ][ name.value ].forEach(function(callback)
|
||||||
|
{
|
||||||
|
result = callback.apply(this, args)
|
||||||
|
|
||||||
|
if(typeof finalResult === 'undefined')
|
||||||
|
finalResult = result
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return finalResult
|
||||||
|
}
|
||||||
|
|
||||||
|
resolveNames(_names)
|
||||||
|
{
|
||||||
|
let names = _names
|
||||||
|
names = names.replace(/[^a-zA-Z0-9 ,/.]/g, '')
|
||||||
|
names = names.replace(/[,/]+/g, ' ')
|
||||||
|
names = names.split(' ')
|
||||||
|
|
||||||
|
return names
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,89 @@
|
|||||||
|
import * as THREE from 'three'
|
||||||
|
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js'
|
||||||
|
import EventEmitter from './EventEmitter.js'
|
||||||
|
|
||||||
|
export default class Resources extends EventEmitter
|
||||||
|
{
|
||||||
|
constructor(sources)
|
||||||
|
{
|
||||||
|
super()
|
||||||
|
|
||||||
|
this.sources = sources
|
||||||
|
|
||||||
|
this.items = {}
|
||||||
|
this.toLoad = this.sources.length
|
||||||
|
this.loaded = 0
|
||||||
|
|
||||||
|
this.setLoaders()
|
||||||
|
this.startLoading()
|
||||||
|
}
|
||||||
|
|
||||||
|
setLoaders()
|
||||||
|
{
|
||||||
|
this.loaders = {}
|
||||||
|
this.loaders.audioLoader = new THREE.AudioLoader()
|
||||||
|
this.loaders.gltfLoader = new GLTFLoader()
|
||||||
|
this.loaders.textureLoader = new THREE.TextureLoader()
|
||||||
|
this.loaders.cubeTextureLoader = new THREE.CubeTextureLoader()
|
||||||
|
}
|
||||||
|
|
||||||
|
startLoading()
|
||||||
|
{
|
||||||
|
// Load each source
|
||||||
|
for(const source of this.sources)
|
||||||
|
{
|
||||||
|
if(source.type === 'gltfModel')
|
||||||
|
{
|
||||||
|
this.loaders.gltfLoader.load(
|
||||||
|
source.path,
|
||||||
|
(file) =>
|
||||||
|
{
|
||||||
|
this.sourceLoaded(source, file)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
else if (source.type === 'audio')
|
||||||
|
{
|
||||||
|
this.loaders.audioLoader.load(
|
||||||
|
source.path,
|
||||||
|
(file) =>
|
||||||
|
{
|
||||||
|
this.sourceLoaded(source, file)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
else if(source.type === 'texture')
|
||||||
|
{
|
||||||
|
this.loaders.textureLoader.load(
|
||||||
|
source.path,
|
||||||
|
(file) =>
|
||||||
|
{
|
||||||
|
this.sourceLoaded(source, file)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
else if(source.type === 'cubeTexture')
|
||||||
|
{
|
||||||
|
this.loaders.cubeTextureLoader.load(
|
||||||
|
source.path,
|
||||||
|
(file) =>
|
||||||
|
{
|
||||||
|
this.sourceLoaded(source, file)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sourceLoaded(source, file)
|
||||||
|
{
|
||||||
|
this.items[source.name] = file
|
||||||
|
|
||||||
|
this.loaded++
|
||||||
|
|
||||||
|
if(this.loaded === this.toLoad)
|
||||||
|
{
|
||||||
|
this.trigger('ready')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
import EventEmitter from './EventEmitter.js'
|
||||||
|
|
||||||
|
export default class Sizes extends EventEmitter
|
||||||
|
{
|
||||||
|
constructor()
|
||||||
|
{
|
||||||
|
super()
|
||||||
|
|
||||||
|
// Setup
|
||||||
|
this.width = window.innerWidth
|
||||||
|
this.height = window.innerHeight
|
||||||
|
this.pixelRatio = Math.min(window.devicePixelRatio, 2)
|
||||||
|
|
||||||
|
// Resize event
|
||||||
|
window.addEventListener('resize', () =>
|
||||||
|
{
|
||||||
|
this.width = window.innerWidth
|
||||||
|
this.height = window.innerHeight
|
||||||
|
this.pixelRatio = Math.min(window.devicePixelRatio, 2)
|
||||||
|
|
||||||
|
this.trigger('resize')
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
import EventEmitter from './EventEmitter.js'
|
||||||
|
|
||||||
|
export default class Time extends EventEmitter
|
||||||
|
{
|
||||||
|
constructor()
|
||||||
|
{
|
||||||
|
super()
|
||||||
|
|
||||||
|
// Setup
|
||||||
|
this.start = Date.now()
|
||||||
|
this.current = this.start
|
||||||
|
this.elapsed = 0
|
||||||
|
this.delta = 16
|
||||||
|
|
||||||
|
window.requestAnimationFrame(() =>
|
||||||
|
{
|
||||||
|
this.tick()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
tick()
|
||||||
|
{
|
||||||
|
const currentTime = Date.now()
|
||||||
|
this.delta = currentTime - this.current
|
||||||
|
this.current = currentTime
|
||||||
|
this.elapsed = this.current - this.start
|
||||||
|
|
||||||
|
this.trigger('tick')
|
||||||
|
|
||||||
|
window.requestAnimationFrame(() =>
|
||||||
|
{
|
||||||
|
this.tick()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,71 @@
|
|||||||
|
import * as THREE from "three"
|
||||||
|
import Experience from "../Experience.js"
|
||||||
|
|
||||||
|
export default class Cube {
|
||||||
|
constructor(position) {
|
||||||
|
this.experience = new Experience()
|
||||||
|
this.scene = this.experience.scene
|
||||||
|
|
||||||
|
this.initialPosition = {
|
||||||
|
x: position.x,
|
||||||
|
y: position.y,
|
||||||
|
z: position.z,
|
||||||
|
}
|
||||||
|
|
||||||
|
this.setGeometry()
|
||||||
|
this.setMaterial()
|
||||||
|
this.setMesh()
|
||||||
|
|
||||||
|
this.rotation = {
|
||||||
|
speed: {
|
||||||
|
x: Math.random() * 2 - 1,
|
||||||
|
y: Math.random() * 2 - 1,
|
||||||
|
z: Math.random() * 2 - 1,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
this.randomDirection = {
|
||||||
|
x: Math.random() * 2 - 1,
|
||||||
|
y: Math.random() * 2 - 1,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setGeometry() {
|
||||||
|
this.geometry = new THREE.BoxGeometry(0.2, 0.2, 0.2)
|
||||||
|
}
|
||||||
|
|
||||||
|
setMaterial() {
|
||||||
|
this.material = new THREE.MeshStandardMaterial({
|
||||||
|
color: new THREE.Color(
|
||||||
|
Math.round(Math.random()),
|
||||||
|
Math.round(Math.random()),
|
||||||
|
Math.round(Math.random())
|
||||||
|
),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
setMesh() {
|
||||||
|
this.mesh = new THREE.Mesh(this.geometry, this.material)
|
||||||
|
|
||||||
|
this.mesh.position.x = this.initialPosition.x
|
||||||
|
this.mesh.position.y = this.initialPosition.y
|
||||||
|
this.mesh.position.z = this.initialPosition.z
|
||||||
|
|
||||||
|
this.mesh.receiveShadow = true
|
||||||
|
this.mesh.castShadow = true
|
||||||
|
this.scene.add(this.mesh)
|
||||||
|
}
|
||||||
|
|
||||||
|
update() {
|
||||||
|
this.mesh.rotation.x += this.rotation.speed.x
|
||||||
|
this.mesh.rotation.y += this.rotation.speed.y
|
||||||
|
this.mesh.rotation.z += this.rotation.speed.z
|
||||||
|
|
||||||
|
this.mesh.position.x += this.randomDirection.x * 0.1
|
||||||
|
this.mesh.position.y += this.randomDirection.y * 0.1
|
||||||
|
}
|
||||||
|
|
||||||
|
destroy() {
|
||||||
|
this.scene.remove(this.mesh)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,94 @@
|
|||||||
|
import * as THREE from "three"
|
||||||
|
import Experience from "../Experience.js"
|
||||||
|
|
||||||
|
export default class Environment {
|
||||||
|
constructor() {
|
||||||
|
this.experience = new Experience()
|
||||||
|
this.scene = this.experience.scene
|
||||||
|
this.resources = this.experience.resources
|
||||||
|
this.debug = this.experience.debug
|
||||||
|
|
||||||
|
// Debug
|
||||||
|
if (this.debug.active) {
|
||||||
|
this.debugFolder = this.debug.ui.addFolder("environment")
|
||||||
|
}
|
||||||
|
|
||||||
|
this.setSunLight()
|
||||||
|
this.setEnvironmentMap()
|
||||||
|
}
|
||||||
|
|
||||||
|
setSunLight() {
|
||||||
|
this.sunLight = new THREE.DirectionalLight("#ffffff", 4)
|
||||||
|
this.sunLight.castShadow = true
|
||||||
|
this.sunLight.shadow.camera.far = 15
|
||||||
|
this.sunLight.shadow.mapSize.set(1024, 1024)
|
||||||
|
this.sunLight.shadow.normalBias = 0.05
|
||||||
|
this.sunLight.position.set(3.5, 2, -1.25)
|
||||||
|
this.scene.add(this.sunLight)
|
||||||
|
|
||||||
|
// Debug
|
||||||
|
if (this.debug.active) {
|
||||||
|
this.debugFolder
|
||||||
|
.add(this.sunLight, "intensity")
|
||||||
|
.name("sunLightIntensity")
|
||||||
|
.min(0)
|
||||||
|
.max(10)
|
||||||
|
.step(0.001)
|
||||||
|
|
||||||
|
this.debugFolder
|
||||||
|
.add(this.sunLight.position, "x")
|
||||||
|
.name("sunLightX")
|
||||||
|
.min(-5)
|
||||||
|
.max(5)
|
||||||
|
.step(0.001)
|
||||||
|
|
||||||
|
this.debugFolder
|
||||||
|
.add(this.sunLight.position, "y")
|
||||||
|
.name("sunLightY")
|
||||||
|
.min(-5)
|
||||||
|
.max(5)
|
||||||
|
.step(0.001)
|
||||||
|
|
||||||
|
this.debugFolder
|
||||||
|
.add(this.sunLight.position, "z")
|
||||||
|
.name("sunLightZ")
|
||||||
|
.min(-5)
|
||||||
|
.max(5)
|
||||||
|
.step(0.001)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setEnvironmentMap() {
|
||||||
|
this.environmentMap = {}
|
||||||
|
this.environmentMap.intensity = 0.4
|
||||||
|
this.environmentMap.texture = this.resources.items.environmentMapTexture
|
||||||
|
this.environmentMap.texture.encoding = THREE.sRGBEncoding
|
||||||
|
|
||||||
|
this.scene.environment = this.environmentMap.texture
|
||||||
|
|
||||||
|
this.environmentMap.updateMaterials = () => {
|
||||||
|
this.scene.traverse((child) => {
|
||||||
|
if (
|
||||||
|
child instanceof THREE.Mesh &&
|
||||||
|
child.material instanceof THREE.MeshStandardMaterial
|
||||||
|
) {
|
||||||
|
child.material.envMap = this.environmentMap.texture
|
||||||
|
child.material.envMapIntensity = this.environmentMap.intensity
|
||||||
|
child.material.needsUpdate = true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
this.environmentMap.updateMaterials()
|
||||||
|
|
||||||
|
// Debug
|
||||||
|
if (this.debug.active) {
|
||||||
|
this.debugFolder
|
||||||
|
.add(this.environmentMap, "intensity")
|
||||||
|
.name("envMapIntensity")
|
||||||
|
.min(0)
|
||||||
|
.max(4)
|
||||||
|
.step(0.001)
|
||||||
|
.onChange(this.environmentMap.updateMaterials)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
import * as THREE from "three"
|
||||||
|
import Experience from "../Experience.js"
|
||||||
|
|
||||||
|
export default class Sphere {
|
||||||
|
constructor() {
|
||||||
|
this.experience = new Experience()
|
||||||
|
this.scene = this.experience.scene
|
||||||
|
this.resources = this.experience.resources
|
||||||
|
this.debug = this.experience.debug
|
||||||
|
|
||||||
|
this.setSphere()
|
||||||
|
}
|
||||||
|
|
||||||
|
setSphere() {
|
||||||
|
this.sphere = new THREE.Mesh(
|
||||||
|
new THREE.SphereGeometry(0.1, 16, 16),
|
||||||
|
new THREE.MeshStandardMaterial({
|
||||||
|
color: "#ffffff",
|
||||||
|
})
|
||||||
|
)
|
||||||
|
this.sphere.receiveShadow = true
|
||||||
|
this.sphere.castShadow = true
|
||||||
|
this.scene.add(this.sphere)
|
||||||
|
}
|
||||||
|
|
||||||
|
update() {}
|
||||||
|
|
||||||
|
destroy() {
|
||||||
|
this.scene.remove(this.sphere)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,154 @@
|
|||||||
|
import * as THREE from "three"
|
||||||
|
import Experience from "../Experience.js"
|
||||||
|
import Environment from "./Environment.js"
|
||||||
|
import Cube from "./Cube.js"
|
||||||
|
|
||||||
|
export default class World {
|
||||||
|
constructor() {
|
||||||
|
this.experience = new Experience()
|
||||||
|
this.scene = this.experience.scene
|
||||||
|
this.interface = this.experience.interface
|
||||||
|
|
||||||
|
this.debug = this.experience.debug
|
||||||
|
|
||||||
|
this.mouseTimeout = undefined
|
||||||
|
this.cubes = []
|
||||||
|
|
||||||
|
this.colorsPick = {}
|
||||||
|
|
||||||
|
this.marioSprite = undefined
|
||||||
|
|
||||||
|
this.downVolTimout = undefined
|
||||||
|
this.upVolTimout = undefined
|
||||||
|
|
||||||
|
this.interface.on("startExperience", () => {
|
||||||
|
this.environment = new Environment()
|
||||||
|
this.scene.background = new THREE.Color(0x000000)
|
||||||
|
this.updateColorPick()
|
||||||
|
this.initMario()
|
||||||
|
|
||||||
|
document
|
||||||
|
.querySelector("body")
|
||||||
|
.addEventListener("mousemove", (e) => this.onMouseMove(e))
|
||||||
|
document
|
||||||
|
.querySelector("body")
|
||||||
|
.addEventListener("touchmove", (e) => this.onMouseMove(e))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
initMario() {
|
||||||
|
const spriteTexture = new THREE.TextureLoader().load(
|
||||||
|
`${import.meta.env.BASE_URL}/textures/mario.png`
|
||||||
|
)
|
||||||
|
const spriteMaterial = new THREE.SpriteMaterial({ map: spriteTexture })
|
||||||
|
this.marioSprite = new THREE.Sprite(spriteMaterial)
|
||||||
|
this.marioSprite.position.set(0, 0, 0)
|
||||||
|
this.scene.add(this.marioSprite)
|
||||||
|
}
|
||||||
|
|
||||||
|
onMouseMove(event) {
|
||||||
|
if (!this.experience.audio) return
|
||||||
|
|
||||||
|
const mousePosition = this.getMousePosition(event)
|
||||||
|
|
||||||
|
this.upVolume()
|
||||||
|
this.spawnEphemeralCubes(mousePosition, 200)
|
||||||
|
this.scene.background = new THREE.Color(this.getBackgroundRandomColor())
|
||||||
|
this.marioSprite.position.y =
|
||||||
|
Math.sin(this.experience.time.elapsed * 0.02) * 0.5 + 0.5
|
||||||
|
|
||||||
|
if (this.mouseTimeout) clearTimeout(this.mouseTimeout)
|
||||||
|
this.mouseTimeout = setTimeout(() => this.onMouseStop(), 50)
|
||||||
|
}
|
||||||
|
|
||||||
|
onMouseStop() {
|
||||||
|
this.downVolume()
|
||||||
|
this.scene.background = new THREE.Color(0x000000)
|
||||||
|
this.marioSprite.position.y = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
updateColorPick() {
|
||||||
|
this.colorsPick = {
|
||||||
|
r: Math.round(Math.random()),
|
||||||
|
g: Math.round(Math.random()),
|
||||||
|
b: Math.round(Math.random()),
|
||||||
|
}
|
||||||
|
setTimeout(() => this.updateColorPick(), 1000)
|
||||||
|
}
|
||||||
|
|
||||||
|
getBackgroundRandomColor() {
|
||||||
|
return new THREE.Color(
|
||||||
|
this.colorsPick.r * this.experience.audio.averageMidFrequencies,
|
||||||
|
this.colorsPick.g * this.experience.audio.averageMidFrequencies,
|
||||||
|
this.colorsPick.b * this.experience.audio.averageMidFrequencies
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
upVolume() {
|
||||||
|
if (this.experience.audio.gainNode.gain.value >= 0.8) return
|
||||||
|
clearTimeout(this.downVolTimout)
|
||||||
|
|
||||||
|
this.setVolume(
|
||||||
|
Math.round((this.experience.audio.gainNode.gain.value + 0.1) * 10) / 10
|
||||||
|
)
|
||||||
|
this.upVolTimout = setTimeout(() => {
|
||||||
|
this.upVolume()
|
||||||
|
}, 200)
|
||||||
|
}
|
||||||
|
|
||||||
|
downVolume() {
|
||||||
|
if (this.experience.audio.gainNode.gain.value <= 0.2) return
|
||||||
|
clearTimeout(this.upVolTimout)
|
||||||
|
|
||||||
|
this.setVolume(
|
||||||
|
Math.round((this.experience.audio.gainNode.gain.value - 0.1) * 10) / 10
|
||||||
|
)
|
||||||
|
this.downVolTimout = setTimeout(() => {
|
||||||
|
this.downVolume()
|
||||||
|
}, 200)
|
||||||
|
}
|
||||||
|
|
||||||
|
setVolume(volume) {
|
||||||
|
this.experience.audio.gainNode.gain.value = volume
|
||||||
|
}
|
||||||
|
|
||||||
|
getMousePosition(event) {
|
||||||
|
const client = event.touches ? event.touches[0] : event
|
||||||
|
|
||||||
|
const raycaster = new THREE.Raycaster()
|
||||||
|
const mouse = {
|
||||||
|
x: (client.clientX / window.innerWidth) * 2 - 1,
|
||||||
|
y: -(client.clientY / window.innerHeight) * 2 + 1,
|
||||||
|
z: 0,
|
||||||
|
}
|
||||||
|
raycaster.setFromCamera(mouse, window.experience.camera.instance)
|
||||||
|
return {
|
||||||
|
x: raycaster.ray.direction.x * 20,
|
||||||
|
y: raycaster.ray.direction.y * 20,
|
||||||
|
z: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
spawnEphemeralCubes(position, quantity) {
|
||||||
|
if (this.cubes.length > quantity) {
|
||||||
|
this.cubes[0].destroy()
|
||||||
|
this.cubes = this.cubes.slice(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
const cube = new Cube(position)
|
||||||
|
this.cubes.push(cube)
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
cube.destroy()
|
||||||
|
this.cubes = this.cubes.filter((c) => c !== cube)
|
||||||
|
}, 1000)
|
||||||
|
}
|
||||||
|
|
||||||
|
update() {
|
||||||
|
if (!this.environment) return
|
||||||
|
|
||||||
|
this.cubes.forEach((cube) => {
|
||||||
|
cube.update()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
export default [
|
||||||
|
{
|
||||||
|
name: 'environmentMapTexture',
|
||||||
|
type: 'cubeTexture',
|
||||||
|
path:
|
||||||
|
[
|
||||||
|
'textures/environmentMap/px.jpg',
|
||||||
|
'textures/environmentMap/nx.jpg',
|
||||||
|
'textures/environmentMap/py.jpg',
|
||||||
|
'textures/environmentMap/ny.jpg',
|
||||||
|
'textures/environmentMap/pz.jpg',
|
||||||
|
'textures/environmentMap/nz.jpg'
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'music',
|
||||||
|
type: 'audio',
|
||||||
|
path: 'audio/onemoretime.mp3'
|
||||||
|
}
|
||||||
|
]
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Audio experience</title>
|
||||||
|
<link rel="stylesheet" href="./style.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<p class="loader is-active">Loading...</p>
|
||||||
|
<p class="start-btn">Start experience</p>
|
||||||
|
<canvas class="webgl"></canvas>
|
||||||
|
<script type="module" src="./script.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
import Experience from "./Experience/Experience.js"
|
||||||
|
|
||||||
|
export const experience = new Experience(document.querySelector("canvas.webgl"))
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
*
|
||||||
|
{
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
html,
|
||||||
|
body
|
||||||
|
{
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.webgl
|
||||||
|
{
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.loader {
|
||||||
|
z-index: 3;
|
||||||
|
cursor: pointer;
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
left: 50%;
|
||||||
|
display: none;
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
|
text-transform: uppercase;
|
||||||
|
}
|
||||||
|
|
||||||
|
.loader.is-active {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.start-btn {
|
||||||
|
z-index: 2;
|
||||||
|
cursor: pointer;
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
left: 50%;
|
||||||
|
display: none;
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
|
text-transform: uppercase;
|
||||||
|
}
|
||||||
|
|
||||||
|
.start-btn.is-active {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
After Width: | Height: | Size: 191 KiB |
|
After Width: | Height: | Size: 210 KiB |
|
After Width: | Height: | Size: 247 KiB |
|
After Width: | Height: | Size: 197 KiB |
|
After Width: | Height: | Size: 201 KiB |
|
After Width: | Height: | Size: 238 KiB |
|
After Width: | Height: | Size: 25 KiB |
@@ -0,0 +1,25 @@
|
|||||||
|
import { defineConfig } from "vite"
|
||||||
|
|
||||||
|
const isCodeSandbox = "SANDBOX_URL" in process.env || "CODESANDBOX_HOST" in process.env
|
||||||
|
|
||||||
|
export default defineConfig(({ command }) => {
|
||||||
|
const baseUrl =
|
||||||
|
command === "serve" ? "https://projet.floriansylvain.fr/Creative_Coding" : ""
|
||||||
|
|
||||||
|
return {
|
||||||
|
define: {
|
||||||
|
BASE_URL: JSON.stringify(baseUrl),
|
||||||
|
},
|
||||||
|
root: "src/",
|
||||||
|
publicDir: "../static/",
|
||||||
|
base: "/Creative_Coding/",
|
||||||
|
server: {
|
||||||
|
host: true,
|
||||||
|
open: !isCodeSandbox, // Open if it's not a CodeSandbox
|
||||||
|
},
|
||||||
|
build: {
|
||||||
|
outDir: "../dist",
|
||||||
|
emptyOutDir: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
})
|
||||||