diff --git a/src/Music.ts b/src/Music.ts index 8b5668c..e2b9284 100644 --- a/src/Music.ts +++ b/src/Music.ts @@ -5,9 +5,10 @@ import musicVert from './shaders/music.vert'; import { gl, glCat } from './globals/canvas'; import samplesOpus from './samples.opus'; import { randomTextureStatic } from './globals/randomTexture'; -import { automaton } from './globals/automaton'; -import { automatonMusicParamMap } from './automatonMusicParamMap'; -import type { AutomatonWithGUI } from '@fms-cat/automaton-with-gui'; +import { auto, automaton } from './globals/automaton'; +import { Channel } from '@fms-cat/automaton'; +import { injectCodeToShader } from './utils/injectCodeToShader'; +import { AutomatonWithGUI } from '@fms-cat/automaton-with-gui'; const discardFrag = '#version 300 es\nvoid main(){discard;}'; @@ -28,6 +29,8 @@ export class Music { private __bufferPool: Pool; private __prevBufferSource: AudioBufferSourceNode | null = null; private __samples?: GLCatTexture; + private __automatonChannelList: Channel[]; + private __automatonDefineString: string; private __textureAutomaton: GLCatTexture; constructor( glCat: GLCat, audio: AudioContext ) { @@ -59,12 +62,18 @@ export class Music { this.__transformFeedback.bindBuffer( 0, this.__bufferTransformFeedbacks[ 0 ] ); this.__transformFeedback.bindBuffer( 1, this.__bufferTransformFeedbacks[ 1 ] ); + // == automaton? =============================================================================== + this.__automatonChannelList = []; + this.__automatonDefineString = ''; + + this.__updateAutomatonChannelList(); + this.__textureAutomaton = glCat.createTexture(); this.__textureAutomaton.textureFilter( gl.NEAREST ); // == program ================================================================================== this.__program = glCat.lazyProgram( - musicVert, + injectCodeToShader( musicVert, this.__automatonDefineString ), discardFrag, { transformFeedbackVaryings: [ 'outL', 'outR' ] }, ); @@ -85,29 +94,51 @@ export class Music { this.deltaTime = 0.0; // == hot hot hot hot hot ====================================================================== - if ( process.env.DEV ) { - if ( module.hot ) { - module.hot.accept( - [ - './shaders/music.vert', - ], - async () => { - const program = await glCat.lazyProgramAsync( - musicVert, - discardFrag, - { transformFeedbackVaryings: [ 'outL', 'outR' ] }, - ).catch( ( error: any ) => { - console.error( error ); - return null; - } ); + if ( process.env.DEV && module.hot ) { + const recompileShader = async () => { + const program = await glCat.lazyProgramAsync( + injectCodeToShader( musicVert, this.__automatonDefineString ), + discardFrag, + { transformFeedbackVaryings: [ 'outL', 'outR' ] }, + ).catch( ( error: any ) => { + console.error( error ); + return null; + } ); - if ( program ) { - this.__program.dispose( true ); - this.__program = program; - } - } - ); - } + if ( program ) { + this.__program.dispose( true ); + this.__program = program; + } + }; + + module.hot.accept( + [ './shaders/music.vert' ], + () => { + recompileShader(); + } + ); + + module.hot.accept( + [ './automaton.json' ], + () => { + this.__updateAutomatonChannelList(); + recompileShader(); + } + ); + + ( automaton as AutomatonWithGUI ).on( 'createChannel', ( { name } ) => { + if ( name.startsWith( 'Music/' ) ) { + this.__updateAutomatonChannelList(); + recompileShader(); + } + } ); + + ( automaton as AutomatonWithGUI ).on( 'removeChannel', ( { name } ) => { + if ( name.startsWith( 'Music/' ) ) { + this.__updateAutomatonChannelList(); + recompileShader(); + } + } ); } } @@ -171,16 +202,24 @@ export class Music { this.__samples = texture; } + private __updateAutomatonChannelList(): void { + this.__automatonChannelList = []; + this.__automatonDefineString = ''; + + for ( const [ channelName, channel ] of automaton.mapNameToChannel.entries() ) { + if ( channelName.startsWith( 'Music/' ) ) { + const key = channelName.substring( 6 ); + const index = this.__automatonChannelList.length; + this.__automatonDefineString += `const int AUTO_${key}=${index};`; + this.__automatonChannelList.push( channel ); + } + } + } + private __updateAutomatonTexture(): void { const buffer = new Float32Array( MUSIC_BUFFER_LENGTH * 256 ); - for ( const [ iChannel, channelName ] of automatonMusicParamMap.entries() ) { - let channel = automaton.mapNameToChannel.get( channelName )!; - - if ( process.env.DEV && !channel ) { - channel = ( automaton as AutomatonWithGUI ).createChannel( channelName ); - } - + for ( const [ iChannel, channel ] of this.__automatonChannelList.entries() ) { for ( let iSample = 0; iSample < MUSIC_BUFFER_LENGTH; iSample ++ ) { const t = this.time + iSample / this.audio.sampleRate; buffer[ MUSIC_BUFFER_LENGTH * iChannel + iSample ] = channel.getValue( t ); diff --git a/src/automaton-fxs/automatonFxs.ts b/src/automaton-fxs/automatonFxs.ts index dc0ddcd..e6223f8 100644 --- a/src/automaton-fxs/automatonFxs.ts +++ b/src/automaton-fxs/automatonFxs.ts @@ -1 +1 @@ -export { hermitePatch } from '@fms-cat/automaton-fxs'; +export { sine, hermitePatch } from '@fms-cat/automaton-fxs'; diff --git a/src/automaton.json b/src/automaton.json index d39cb9e..9d547a7 100644 --- a/src/automaton.json +++ b/src/automaton.json @@ -1 +1 @@ -{"version":"4.1.1","resolution":100,"curves":[{"nodes":[[0,1,0,0,0.16140350877192983],[1,0,-0.47719298245614045]]},{"nodes":[[0,0,0,0,0.1],[0.3747555183012014,1,-0.1,0,0.1],[1.3333333333333333,0,-0.1]]},{"nodes":[[],[0.6666666666666666,1,-0.5266666666666666]]},{"nodes":[[0,0,0,0,0.2355866843161978],[1.3333333333333333,1.2000000000000002,-0.1]]},{"nodes":[[0,0,0,0,0.12284595300261082,0.061764705882352944],[0.6666666666666666,0.4,-0.1]]},{"nodes":[[0,0,0,0,0.10333333333333335],[1,1]]}],"channels":[["Glitch/amp",{"items":[{},{"time":10.666666666666666,"length":1,"curve":0},{"time":256}]}],["PixelSorter/amp",{"items":[{},{"time":15.666666666666666,"length":1.666666666666666,"curve":1}]}],["Music/neuro/t",{"items":[{"time":42.666666666666664,"length":0.6666666666666643,"curve":2},{"time":43.33333333333333,"length":1.3333333333333357,"curve":3},{"time":44.666666666666664,"length":0.6666666666666643,"curve":5,"speed":1.5000000000000056,"amp":0.5}]}],["Music/neuro/det",{"items":[{"time":42.666666666666664,"length":0.6666666666666643,"curve":4},{"time":43.33333333333333,"length":1.3333333333333357,"value":0.17000000000000004},{"time":44.666666666666664,"length":0.6666666666666643,"value":0.5}]}],["Music/neuro/radius",{"items":[]}]],"labels":{"zero":0},"guiSettings":{"snapTimeActive":true,"snapTimeInterval":0.1,"snapValueActive":true,"snapValueInterval":0.1,"snapBeatActive":true,"bpm":180,"beatOffset":0,"useBeatInGUI":true,"minimizedPrecisionTime":3,"minimizedPrecisionValue":3}} \ No newline at end of file +{"version":"4.1.1","resolution":100,"curves":[{"nodes":[[0,1,0,0,0.16140350877192983],[1,0,-0.47719298245614045]]},{"nodes":[[0,0,0,0,0.1],[0.3747555183012014,1,-0.1,0,0.1],[1.3333333333333333,0,-0.1]]},{"nodes":[[],[0.6666666666666666,1,-0.4560629631300563,-0.1852941176470587]]},{"nodes":[[0,0,0,0,0.2355866843161978],[1.3333333333333333,1.2000000000000002,-0.1]]},{"nodes":[[0,0,0,0,0.12284595300261082,0.061764705882352944],[0.6666666666666666,0.4,-0.1]]},{"nodes":[[0,0,0,0,0.10333333333333335],[1,1]]},{"nodes":[[0,0,0,0,0.1],[0.3333333333333333,1,-0.1,0,0.1],[0.6666666666666666,0,-0.1]]},{"nodes":[[0,0,0,0,0.12284595300261095,1.0980392156862744],[1.3333333333333333,0.8,-0.1]]},{"nodes":[[0,0,0,0,0.016158102050275177,0.4803921568627448],[0.02868275456174794,1,-0.017181893944471203,0.046509328639327226,0.07352361117377113,-0.1990196078431371],[0.25,0,-0.025439765782812148,0.6242474227849143]]},{"nodes":[[],[0.5,0.5,0,0,0.3072250626170559,0.32683448369273865],[1.3333333333333333,1.5]]},{"nodes":[[0,0.2,0,0,0.18674781789871728,2.609799325705191],[0.6666666666666666,0.1,-0.3546547911950566,0.44607843137254866]]},{"nodes":[[0,0.15115889256979576,0,0,0.1],[1.3333333333333333,0.7344636184844164,-0.8192982456140352,-0.01372549019607843]],"fxs":[{"def":"sine","params":{"amp":0.42000000000000004,"freq":9.43,"offset":0},"time":0.5833333333333333,"length":0.75},{"def":"hermitePatch","params":{},"time":0.5,"length":0.16666666666666663,"row":1}]}],"channels":[["Glitch/amp",{"items":[{},{"time":10.666666666666666,"length":1,"curve":0},{"time":256}]}],["PixelSorter/amp",{"items":[{},{"time":15.666666666666666,"length":1.666666666666666,"curve":1}]}],["Music/NEURO_TIME",{"items":[{"time":42.666666666666664,"length":0.6666666666666643,"curve":2},{"time":43.33333333333333,"length":1.3333333333333357,"curve":3},{"time":44.666666666666664,"length":0.6666666666666643,"curve":5,"speed":2.280000000000004,"amp":0.5},{"time":46,"length":1.3333333333333286,"curve":9,"speed":1.0000000000000109},{"time":48,"length":0.6666666666666643,"curve":2,"speed":0.6666666666666642}]}],["Music/NEURO_DETUNE",{"items":[{"time":42.666666666666664,"length":0.6666666666666643,"curve":4},{"time":43.33333333333333,"length":1.3333333333333357,"value":0.050000000000000024},{"time":44.666666666666664,"length":0.6666666666666643,"value":0.08000000000000045},{"time":46,"length":1.3333333333333286,"value":0.40000000000000024},{"time":47.33333333333333,"length":0.6666666666666643,"curve":10,"amp":0.15588235294117658},{"time":48,"length":0.6666666666666643,"value":1.0431372549019606,"curve":4}]}],["Music/NEURO_DETUNE_PHASE",{"items":[{"time":42.666666666666664,"length":0.6666666666666643,"value":1},{"time":43.33333333333333,"length":1.3333333333333357,"value":0.0299999999999997},{"time":44.666666666666664,"length":0.6666666666666643,"value":0.19000000000000006},{"time":46,"length":1.3333333333333286,"value":1},{"time":48,"length":0.6666666666666643,"value":0.0399999999999995}]}],["Music/NEURO_WUB_AMP",{"items":[{"time":42.664755294969126,"length":0.6685780383642026,"curve":6},{"time":43.33333333333333,"length":1.3333333333333357,"curve":7},{"time":44.666666666666664,"length":0.25,"curve":8},{"time":44.916666666666664,"length":0.25,"curve":8},{"time":45.166666666666664,"length":0.1666666666666643,"curve":8},{"time":46,"length":1.3333333333333286,"curve":11},{"time":48,"length":0.6685780383642026,"curve":6}]}],["Music/NEURO_WUB_FREQ",{"items":[{"time":42.666666666666664,"length":0.6666666666666643,"value":1},{"time":43.33333333333333,"length":1.3333333333333357,"value":1},{"time":44.666666666666664,"length":0.6666666666666643,"value":0.5},{"time":46,"length":1.3333333333333286,"value":2},{"time":48,"length":0.6666666666666643,"value":1}]}]],"labels":{"zero":0},"guiSettings":{"snapTimeActive":true,"snapTimeInterval":0.1,"snapValueActive":true,"snapValueInterval":0.1,"snapBeatActive":true,"bpm":180,"beatOffset":0,"useBeatInGUI":true,"minimizedPrecisionTime":3,"minimizedPrecisionValue":3}} \ No newline at end of file diff --git a/src/automatonMusicParamMap.ts b/src/automatonMusicParamMap.ts deleted file mode 100644 index ed72491..0000000 --- a/src/automatonMusicParamMap.ts +++ /dev/null @@ -1,6 +0,0 @@ -export const automatonMusicParamMap = [ - 'Music/neuro/t', - 'Music/neuro/det', - 'Music/neuro/wubIntensity', - 'Music/neuro/wubFreq', -]; diff --git a/src/heck/Material.ts b/src/heck/Material.ts index 3548663..b1f0799 100644 --- a/src/heck/Material.ts +++ b/src/heck/Material.ts @@ -1,5 +1,6 @@ import { GLCatProgram, GLCatProgramLinkOptions, GLCatProgramUniformType, GLCatTexture, GLCatTextureCubemap } from '@fms-cat/glcat-ts'; import { gl } from '../globals/canvas'; +import { injectCodeToShader } from '../utils/injectCodeToShader'; import { SHADERPOOL } from './ShaderPool'; export type MaterialTag = @@ -163,18 +164,14 @@ export class Material { } protected __withDefines( code: string ): string { - if ( !code.startsWith( '#version' ) ) { - return code; - } - - const lines = code.split( '\n' ); + let inject = ''; Object.entries( this.__defines ).map( ( [ key, value ] ) => { if ( value != null ) { - lines.splice( 1, 0, `#define ${key} ${value}` ); + inject += `#define ${key} ${value}\n`; } } ); - return lines.join( '\n' ); + return injectCodeToShader( code, inject ); } } diff --git a/src/scene.ts b/src/scene.ts index f2e1e2d..3716ca8 100644 --- a/src/scene.ts +++ b/src/scene.ts @@ -68,15 +68,15 @@ class EntityReplacer { // -- bake ----------------------------------------------------------------------------------------- const ibllut = new IBLLUT(); -dog.root.children.push( ibllut.entity ); +// dog.root.children.push( ibllut.entity ); // -- "objects" ------------------------------------------------------------------------------------ -const replacerSphereParticles = new EntityReplacer( () => new SphereParticles() ); -if ( process.env.DEV && module.hot ) { - module.hot.accept( './entities/SphereParticles', () => { - replacerSphereParticles.replace(); - } ); -} +// const replacerSphereParticles = new EntityReplacer( () => new SphereParticles() ); +// if ( process.env.DEV && module.hot ) { +// module.hot.accept( './entities/SphereParticles', () => { +// replacerSphereParticles.replace(); +// } ); +// } // const replacerTrails = new EntityReplacer( () => new Trails() ); // if ( process.env.DEV && module.hot ) { @@ -85,12 +85,12 @@ if ( process.env.DEV && module.hot ) { // } ); // } -const replacerRings = new EntityReplacer( () => new Rings() ); -if ( process.env.DEV && module.hot ) { - module.hot.accept( './entities/Rings', () => { - replacerRings.replace(); - } ); -} +// const replacerRings = new EntityReplacer( () => new Rings() ); +// if ( process.env.DEV && module.hot ) { +// module.hot.accept( './entities/Rings', () => { +// replacerRings.replace(); +// } ); +// } // const replacerCube = new EntityReplacer( () => new Cube() ); // if ( process.env.DEV && module.hot ) { @@ -99,19 +99,19 @@ if ( process.env.DEV && module.hot ) { // } ); // } -const replacerFlickyParticles = new EntityReplacer( () => new FlickyParticles() ); -if ( process.env.DEV && module.hot ) { - module.hot.accept( './entities/FlickyParticles', () => { - replacerFlickyParticles.replace(); - } ); -} +// const replacerFlickyParticles = new EntityReplacer( () => new FlickyParticles() ); +// if ( process.env.DEV && module.hot ) { +// module.hot.accept( './entities/FlickyParticles', () => { +// replacerFlickyParticles.replace(); +// } ); +// } -const replacerRaymarcher = new EntityReplacer( () => new Raymarcher() ); -if ( process.env.DEV && module.hot ) { - module.hot.accept( './entities/Raymarcher', () => { - replacerRaymarcher.replace(); - } ); -} +// const replacerRaymarcher = new EntityReplacer( () => new Raymarcher() ); +// if ( process.env.DEV && module.hot ) { +// module.hot.accept( './entities/Raymarcher', () => { +// replacerRaymarcher.replace(); +// } ); +// } // -- things that is not an "object" --------------------------------------------------------------- const swapOptions = { @@ -130,16 +130,16 @@ const swap = new Swap( } ), ); -const light = new LightEntity( { - root: dog.root, - shadowMapFov: 90.0, - shadowMapNear: 1.0, - shadowMapFar: 20.0, - namePrefix: process.env.DEV && 'light1', -} ); -light.color = [ 40.0, 40.0, 40.0 ]; -light.entity.transform.lookAt( new Vector3( [ -1.0, 2.0, 8.0 ] ) ); -dog.root.children.push( light.entity ); +// const light = new LightEntity( { +// root: dog.root, +// shadowMapFov: 90.0, +// shadowMapNear: 1.0, +// shadowMapFar: 20.0, +// namePrefix: process.env.DEV && 'light1', +// } ); +// light.color = [ 40.0, 40.0, 40.0 ]; +// light.entity.transform.lookAt( new Vector3( [ -1.0, 2.0, 8.0 ] ) ); +// dog.root.children.push( light.entity ); // const light2 = new LightEntity( { // root: dog.root, @@ -152,83 +152,83 @@ dog.root.children.push( light.entity ); // light2.entity.transform.lookAt( new Vector3( [ -4.0, -2.0, 6.0 ] ) ); // dog.root.children.push( light2.entity ); -const cubemapCamera = new CubemapCameraEntity( { - root: dog.root, - lights: [ - light, - // light2 - ], -} ); -dog.root.children.push( cubemapCamera.entity ); +// const cubemapCamera = new CubemapCameraEntity( { +// root: dog.root, +// lights: [ +// light, +// // light2 +// ], +// } ); +// dog.root.children.push( cubemapCamera.entity ); -const environmentMap = new EnvironmentMap( { - cubemap: cubemapCamera.target, -} ); -dog.root.children.push( environmentMap.entity ); +// const environmentMap = new EnvironmentMap( { +// cubemap: cubemapCamera.target, +// } ); +// dog.root.children.push( environmentMap.entity ); -const camera = new CameraEntity( { - root: dog.root, - target: swap.o, - lights: [ - light, - // light2 - ], - textureIBLLUT: ibllut.texture, - textureEnv: environmentMap.texture, -} ); -camera.camera.clear = [ 0.0, 0.0, 0.0, 0.0 ]; -camera.entity.components.unshift( new Lambda( { - onUpdate: ( event ) => { - const t1 = 0.02 * Math.sin( event.time ); - const s1 = Math.sin( t1 ); - const c1 = Math.cos( t1 ); - const t2 = 0.02 * Math.cos( event.time ); - const s2 = Math.sin( t2 ); - const c2 = Math.cos( t2 ); - const r = 5.0; +// const camera = new CameraEntity( { +// root: dog.root, +// target: swap.o, +// lights: [ +// light, +// // light2 +// ], +// textureIBLLUT: ibllut.texture, +// textureEnv: environmentMap.texture, +// } ); +// camera.camera.clear = [ 0.0, 0.0, 0.0, 0.0 ]; +// camera.entity.components.unshift( new Lambda( { +// onUpdate: ( event ) => { +// const t1 = 0.02 * Math.sin( event.time ); +// const s1 = Math.sin( t1 ); +// const c1 = Math.cos( t1 ); +// const t2 = 0.02 * Math.cos( event.time ); +// const s2 = Math.sin( t2 ); +// const c2 = Math.cos( t2 ); +// const r = 5.0; - camera.entity.transform.lookAt( new Vector3( [ - r * c1 * s2, - r * s1, - r * c1 * c2 - ] ) ); - }, - visible: false, - name: process.env.DEV && 'main/updateCamera', -} ) ); -dog.root.children.push( camera.entity ); +// camera.entity.transform.lookAt( new Vector3( [ +// r * c1 * s2, +// r * s1, +// r * c1 * c2 +// ] ) ); +// }, +// visible: false, +// name: process.env.DEV && 'main/updateCamera', +// } ) ); +// dog.root.children.push( camera.entity ); -swap.swap(); -const bloom = new Bloom( { - input: swap.i, - target: swap.o -} ); -dog.root.children.push( bloom.entity ); +// swap.swap(); +// const bloom = new Bloom( { +// input: swap.i, +// target: swap.o +// } ); +// dog.root.children.push( bloom.entity ); -swap.swap(); -const glitch = new Glitch( { - input: swap.i, - target: swap.o, -} ); -dog.root.children.push( glitch.entity ); +// swap.swap(); +// const glitch = new Glitch( { +// input: swap.i, +// target: swap.o, +// } ); +// dog.root.children.push( glitch.entity ); -swap.swap(); -const pixelSorter = new PixelSorter( { - input: swap.i, - target: swap.o, -} ); -dog.root.children.push( pixelSorter.entity ); +// swap.swap(); +// const pixelSorter = new PixelSorter( { +// input: swap.i, +// target: swap.o, +// } ); +// dog.root.children.push( pixelSorter.entity ); -swap.swap(); -const post = new Post( { - input: swap.i, - target: canvasRenderTarget -} ); -dog.root.children.push( post.entity ); +// swap.swap(); +// const post = new Post( { +// input: swap.i, +// target: canvasRenderTarget +// } ); +// dog.root.children.push( post.entity ); -if ( process.env.DEV ) { - const rtInspector = new RTInspector( { - target: canvasRenderTarget - } ); - dog.root.children.push( rtInspector.entity ); -} +// if ( process.env.DEV ) { +// const rtInspector = new RTInspector( { +// target: canvasRenderTarget +// } ); +// dog.root.children.push( rtInspector.entity ); +// } diff --git a/src/shaders/music.vert b/src/shaders/music.vert index 2543aea..04a3bc0 100644 --- a/src/shaders/music.vert +++ b/src/shaders/music.vert @@ -380,15 +380,16 @@ vec2 mainAudio( vec4 time ) { if ( inRange( time.w, SECTION_NEURO, SECTION_PORTER_FUCKING_ROBINSON ) ) { vec2 sum = vec2( 0.0 ); - float t = auto( 0 ); - float det = 0.01 * auto( 1 ); - float wubIntensity = auto( 2 ); - float wubFreq = auto( 3 ); + float t = auto( AUTO_NEURO_TIME ); + float det = 0.01 * auto( AUTO_NEURO_DETUNE ); + float detPhase = auto( AUTO_NEURO_DETUNE_PHASE ); + float wubIntensity = auto( AUTO_NEURO_WUB_AMP ); + float wubFreq = auto( AUTO_NEURO_WUB_FREQ ); for ( int i = 0; i < 5; i ++ ) { float fi = float( i ); - float tt = t + det * fi * sin( 0.7 * fi + 1.0 * t ); + float tt = t + det * fi * sin( fi * detPhase + 1.0 * t ); float radius = 0.2 + 0.1 * wubIntensity * fbm( 0.1 * vec2( tri( n2f( -36.0 ) * wubFreq * tt ) ) ).x; diff --git a/src/utils/injectCodeToShader.ts b/src/utils/injectCodeToShader.ts new file mode 100644 index 0000000..937cbde --- /dev/null +++ b/src/utils/injectCodeToShader.ts @@ -0,0 +1,10 @@ +/** + * Assuming the first line is #version... + * @param code The original shader code + * @param inject The code you want to inject + */ +export function injectCodeToShader( code: string, inject: string ): string { + const lines = code.split( '\n' ); + lines.splice( 1, 0, inject ); + return lines.join( '\n' ); +}