Initial commit

This commit is contained in:
Florian Sylvain
2023-04-12 12:18:18 +02:00
commit 2d21a06262
40 changed files with 1803 additions and 0 deletions
BIN
View File
Binary file not shown.
+119
View File
@@ -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
}
}
+14
View File
@@ -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()
}
}
}
+197
View File
@@ -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
}
}
+89
View File
@@ -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')
}
}
}
+24
View File
@@ -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')
})
}
}
+35
View File
@@ -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()
})
}
}