size: mangler hell

This commit is contained in:
FMS-Cat
2021-03-27 01:40:50 +09:00
parent 871a569f2a
commit bdf5d8ca01
29 changed files with 289 additions and 389 deletions

View File

@@ -2,9 +2,24 @@
## Build ## Build
- You will need [`shader_minifier.exe`](https://github.com/laurentlb/Shader_Minifier) in your PATH - You will need these stuff in your PATH:
- [`shader_minifier.exe`](https://github.com/laurentlb/Shader_Minifier)
- [`jsexe.exe`](https://www.pouet.net/)
```sh ```sh
yarn yarn
yarn build yarn build
``` ```
## Minification
[terser's](https://terser.org/docs/api-reference.html) mangler is spicy
you'll die instantly if you access to object properties using a string identifier
see `webpack.config.js` and `src/automaton-fxs/fxDefinitions.ts` for the funny jokes
jsexe is good
note that it cannot parse nowadays ecmascript though
shader minifier is way too spicy so I made a [separate document](./shader-minifier-tips.md)
libopus is cheating

View File

@@ -5,7 +5,10 @@
"license": "MIT", "license": "MIT",
"scripts": { "scripts": {
"dev": "webpack-cli serve --mode development", "dev": "webpack-cli serve --mode development",
"build": "webpack --mode production && ruby pnginator.rb ./dist/bundle.js ./dist/out.png.html" "build": "yarn build-js && yarn build-png",
"build-js": "webpack --mode production",
"build-png": "jsexe -cn -po ./dist/bundle.js ./dist/out.png.html",
"h": "start http://localhost:8081/out.png.html && http-server ./dist"
}, },
"devDependencies": { "devDependencies": {
"@fms-cat/automaton": "^4.1.0", "@fms-cat/automaton": "^4.1.0",

View File

@@ -1,106 +0,0 @@
#!/usr/bin/env ruby -w
# pnginator.rb: pack a .js file into a PNG image with an HTML payload;
# when saved with an .html extension and opened in a browser, the HTML extracts and executes
# the javascript.
# Usage: ruby pnginator.rb input.js output.png.html
# By Gasman <http://matt.west.co.tt/>
# from an original idea by Daeken: http://daeken.com/superpacking-js-demos
MAX_WIDTH = 4096
USE_PNGOUT = true
require 'zlib'
require 'tempfile'
input_filename, output_filename = ARGV
f = File.open(input_filename, 'rb')
js = f.read
f.close
if js.length < MAX_WIDTH
# js fits onto one pixel line
js += "\x00"
scanlines = [js]
width = js.length
height = 1
# Daeken's single-pixel-row bootstrap (requires js string to be reversed)
# (edit by Gasman: change eval to (1,eval) to force global evaluation and avoid massive slowdown)
# html = "<canvas id=q><img onload=for(p=q.width=#{width},(c=q.getContext('2d')).drawImage(this,0,e='');p;)e+=String.fromCharCode(c.getImageData(--p,0,1,1).data[0]);(1,eval)(e) src=#>"
# p01's single-pixel-row bootstrap (requires an 0x00 end marker on the js string)
# (edit by Gasman: move drawImage out of getImageData params (it returns undef, which is invalid) and change eval to (1,eval) to force global evaluation)
html = "<canvas id=c><img onload=with(c.getContext('2d'))for(p=e='';drawImage(this,p--,0),t=getImageData(0,0,1,1).data[0];)e+=String.fromCharCode(t);(1,eval)(e) src=#>"
else
js = "\x00" + js
width = MAX_WIDTH
# split js into scanlines of 'width' pixels; pad the last one with whitespace
scanlines = js.scan(/.{1,#{width}}/m).collect{|line| line.ljust(width, "\x00")}
height = scanlines.length
# p01's multiple-pixel-row bootstrap (requires a dummy first byte on the js string)
# (edit by Gasman: set explicit canvas width to support widths above 300; move drawImage out of getImageData params; change eval to (1,eval) to force global evaluation)
html = "<canvas id=c><img onload=for(w=c.width=#{width},a=c.getContext('2d'),a.drawImage(this,p=0,0),e='',d=a.getImageData(0,0,w,#{height}).data;t=d[p+=4];)e+=String.fromCharCode(t);(1,eval)(e) src=#>"
end
# prepend each scanline with 0x00 to indicate 'no filtering', then concat into one string
image_data = scanlines.collect{|line| "\x00" + line}.join
idat_chunk = Zlib::Deflate.deflate(image_data, 9) # 9 = maximum compression
def png_chunk(signature, data)
[data.length, signature, data, Zlib::crc32(signature + data)].pack("NA4A*N")
end
if USE_PNGOUT
# Create a valid (no format hacks) .png file to pass to pngout
f = Tempfile.open(['pnginator', '.png'])
begin
f.write("\x89PNG\x0d\x0a\x1a\x0a") # PNG file header
f.write(png_chunk("IHDR", [width, height, 8, 0, 0, 0, 0].pack("NNccccc")))
f.write(png_chunk("IDAT", idat_chunk))
f.write(png_chunk("IEND", ''))
f.close
system("pngout", f.path, "-c0", "-y")
# read file back and extract the IDAT chunk
f.open
f.read(8)
while !f.eof?
length, signature = f.read(8).unpack("NA4")
data = f.read(length)
crc = f.read(4)
if signature == "IDAT"
idat_chunk = data
break
end
end
ensure
f.close
f.unlink
end
end
File.open(output_filename, 'wb') do |f|
f.write("\x89PNG\x0d\x0a\x1a\x0a") # PNG file header
f.write(png_chunk("IHDR", [width, height, 8, 0, 0, 0, 0].pack("NNccccc")))
# a custom chunk containing the HTML payload; stated chunk length is 4 less than the actual length,
# leaving the final 4 bytes to take the place of the checksum
f.write([html.length - 4, "jawh", html].pack("NA4A*"))
# can safely omit the checksum of the IDAT chunk
# f.write([idat_chunk.length, "IDAT", idat_chunk, Zlib::crc32("IDAT" + idat_chunk)].pack("NA4A*N"))
f.write([idat_chunk.length, "IDAT", idat_chunk].pack("NA4A*"))
# can safely omit the IEND chunk
# f.write([0, "IEND", "", Zlib::crc32("IEND")].pack("NA4A*N"))
end

View File

@@ -1,3 +0,0 @@
export { sine, hermitePatch } from '@fms-cat/automaton-fxs';
export { repeat } from './repeat';
export { transpose } from './transpose';

View File

@@ -0,0 +1,14 @@
import { sine } from './sine';
import { repeat } from './repeat';
import { hermitePatch } from './hermitePatch';
import { transpose } from './transpose';
// quotes! prevent fx names from being mangled
const fxDefinitions = {
'sine': sine,
'repeat': repeat,
'hermitePatch': hermitePatch,
'transpose': transpose,
};
export { fxDefinitions };

View File

@@ -0,0 +1,34 @@
import type { FxDefinition } from '@fms-cat/automaton';
export const hermitePatch: FxDefinition = {
func( context ) {
if ( context.init ) {
const dt = context.deltaTime;
const v0 = context.getValue( context.t0 );
const dv0 = v0 - context.getValue( context.t0 - dt );
const v1 = context.getValue( context.t1 );
const dv1 = v1 - context.getValue( context.t1 - dt );
context.state.p0 = v0;
context.state.m0 = dv0 / dt * context.length;
context.state.p1 = v1;
context.state.m1 = dv1 / dt * context.length;
}
const { p0, m0, p1, m1 } = context.state;
const t = context.progress;
return (
( ( 2.0 * t - 3.0 ) * t * t + 1.0 ) * p0 +
( ( ( t - 2.0 ) * t + 1.0 ) * t ) * m0 +
( ( -2.0 * t + 3.0 ) * t * t ) * p1 +
( ( t - 1.0 ) * t * t ) * m1
);
}
};
if ( process.env.DEV ) {
hermitePatch.name = 'Hermite Patch';
hermitePatch.description = 'Patch a curve using hermite spline.';
}

View File

@@ -1,11 +1,6 @@
import type { FxDefinition } from '@fms-cat/automaton'; import type { FxDefinition } from '@fms-cat/automaton';
export const repeat: FxDefinition = { export const repeat: FxDefinition = {
name: 'Repeat',
description: 'Repeat a section of the curve.',
params: {
interval: { name: 'Interval', type: 'float', default: 1.0, min: 0.0 },
},
func( context ) { func( context ) {
if ( context.index === context.i1 ) { if ( context.index === context.i1 ) {
context.setShouldNotInterpolate( true ); context.setShouldNotInterpolate( true );
@@ -20,3 +15,11 @@ export const repeat: FxDefinition = {
return context.getValue( context.t0 + context.elapsed % context.params.interval ); return context.getValue( context.t0 + context.elapsed % context.params.interval );
} }
}; };
if ( process.env.DEV ) {
repeat.name = 'Repeat';
repeat.description = 'Repeat a section of the curve.';
repeat.params = {
interval: { name: 'Interval', type: 'float', default: 1.0, min: 0.0 },
};
}

21
src/automaton-fxs/sine.ts Normal file
View File

@@ -0,0 +1,21 @@
import type { FxDefinition } from '@fms-cat/automaton';
const TAU = Math.PI * 2.0;
export const sine: FxDefinition = {
func( context ) {
const v = context.value;
const p = context.elapsed * context.params.freq + context.params.offset;
return v + context.params.amp * Math.sin( p * TAU );
}
};
if ( process.env.DEV ) {
sine.name = 'Sinewave';
sine.description = 'Overlay a sinewave to the curve.';
sine.params = {
amp: { name: 'Amp', type: 'float', default: 0.1 },
freq: { name: 'Frequency', type: 'float', default: 5.0 },
offset: { name: 'Offset', type: 'float', default: 0.0, min: 0.0, max: 1.0 }
};
}

View File

@@ -1,9 +1,6 @@
import type { FxDefinition } from '@fms-cat/automaton'; import type { FxDefinition } from '@fms-cat/automaton';
export const transpose: FxDefinition = { export const transpose: FxDefinition = {
params: {
note: { name: 'Note', type: 'float', default: 0.0 }
},
func( context ) { func( context ) {
if ( context.init ) { if ( context.init ) {
context.state.v0 = context.value; context.state.v0 = context.value;
@@ -17,3 +14,11 @@ export const transpose: FxDefinition = {
return v0 + ( context.value - v0 ) * Math.pow( 2.0, context.params.note / 12.0 ); return v0 + ( context.value - v0 ) * Math.pow( 2.0, context.params.note / 12.0 );
} }
}; };
if ( process.env.DEV ) {
transpose.name = 'Transpose';
transpose.description = 'I probably don\'t need it...';
transpose.params = {
note: { name: 'Note', type: 'float', default: 0.0 },
};
}

File diff suppressed because one or more lines are too long

View File

@@ -5,5 +5,6 @@ export const
AO_RESOLUTION_RATIO = 1.0, AO_RESOLUTION_RATIO = 1.0,
RESOLUTION = [ 1280, 720 ], RESOLUTION = [ 1280, 720 ],
MUSIC_BPM = 180, MUSIC_BPM = 180,
START_POSITION = 36,
MUSIC_BUFFER_LENGTH = 4096, MUSIC_BUFFER_LENGTH = 4096,
MUSIC_AUTOMATON_TEXTURE_HEIGHT = 16; MUSIC_AUTOMATON_TEXTURE_HEIGHT = 16;

View File

@@ -97,9 +97,7 @@ export class CameraEntity extends Entity {
quadVert, quadVert,
shadingFrag, shadingFrag,
{ {
defines: { defines: iLight === 0 ? [ 'IS_FIRST_LIGHT' ] : [],
IS_FIRST_LIGHT: iLight === 0 ? 'true' : undefined
},
initOptions: { geometry: quadGeometry, target: dummyRenderTarget }, initOptions: { geometry: quadGeometry, target: dummyRenderTarget },
}, },
); );

View File

@@ -102,32 +102,34 @@ export class Condition extends Entity {
geometry.primcount = 12 * 16; geometry.primcount = 12 * 16;
// -- create materials ------------------------------------------------------------------------- // -- create materials -------------------------------------------------------------------------
const materials = { const forward = new Material(
forward: new Material( conditionVert,
conditionVert, conditionFrag,
conditionFrag, {
{ defines: [ 'FORWARD 1' ],
defines: { 'FORWARD': 'true' }, initOptions: { geometry, target: dummyRenderTarget },
initOptions: { geometry, target: dummyRenderTarget }, },
}, );
),
deferred: new Material( const deferred = new Material(
conditionVert, conditionVert,
conditionFrag, conditionFrag,
{ {
defines: { 'DEFERRED': 'true' }, defines: [ 'DEFERRED 1' ],
initOptions: { geometry, target: dummyRenderTargetFourDrawBuffers }, initOptions: { geometry, target: dummyRenderTargetFourDrawBuffers },
}, },
), );
shadow: new Material(
conditionVert, const depth = new Material(
conditionFrag, conditionVert,
{ conditionFrag,
defines: { 'SHADOW': 'true' }, {
initOptions: { geometry, target: dummyRenderTarget }, defines: [ 'SHADOW 1' ],
}, initOptions: { geometry, target: dummyRenderTarget },
), },
}; );
const materials = { forward, deferred, depth };
objectValuesMap( materials, ( material ) => { objectValuesMap( materials, ( material ) => {
material.addUniformTexture( 'samplerSvg', texture ); material.addUniformTexture( 'samplerSvg', texture );
@@ -149,9 +151,9 @@ export class Condition extends Entity {
'../shaders/condition.frag', '../shaders/condition.frag',
], ],
() => { () => {
materials.forward.replaceShader( conditionVert, conditionFrag ); forward.replaceShader( conditionVert, conditionFrag );
materials.deferred.replaceShader( conditionVert, conditionFrag ); deferred.replaceShader( conditionVert, conditionFrag );
materials.shadow.replaceShader( conditionVert, conditionFrag ); depth.replaceShader( conditionVert, conditionFrag );
}, },
); );
} }

View File

@@ -1,132 +0,0 @@
import { Mesh } from '../heck/components/Mesh';
import { Entity } from '../heck/Entity';
import { Geometry } from '../heck/Geometry';
import { Material } from '../heck/Material';
import svgVert from '../shaders/svg.vert';
import conditionCharFrag from '../shaders/condition-char.frag';
import { gl, glCat } from '../globals/canvas';
import { Vector3 } from '@fms-cat/experimental';
import { GLCatTexture } from '@fms-cat/glcat-ts';
import { auto } from '../globals/automaton';
import { dummyRenderTargetFourDrawBuffers, dummyRenderTarget } from '../globals/dummyRenderTarget';
const POINTS_MAX = 256;
interface SVGTestOptions {
table: GLCatTexture;
pos: number;
i: number;
}
export class ConditionChar extends Entity {
public constructor( { table, pos, i }: SVGTestOptions ) {
super();
this.transform.position = this.transform.position.add( new Vector3( [ 2 * pos, 0, 0 ] ) );
// -- create buffers ---------------------------------------------------------------------------
const arrayPos = [];
const arrayInd = [];
for ( let i = 0; i < POINTS_MAX; i ++ ) {
const x = i / POINTS_MAX;
arrayPos.push( x, 0, x, 1, x, 2 );
for ( let j = 0; j < 3; j ++ ) {
const j1 = ( j + 1 ) % 3;
arrayInd.push(
i * 3 + j,
i * 3 + 3 + j,
i * 3 + 3 + j1,
i * 3 + j,
i * 3 + 3 + j1,
i * 3 + j1,
);
}
}
arrayPos.push( 1, 0, 1, 1, 1, 2 );
const bufferPos = glCat.createBuffer();
bufferPos.setVertexbuffer( new Float32Array( arrayPos ) );
const bufferInd = glCat.createBuffer();
bufferInd.setIndexbuffer( new Uint16Array( arrayInd ) );
// -- create geometry --------------------------------------------------------------------------
const geometry = new Geometry();
geometry.vao.bindVertexbuffer( bufferPos, 0, 2 );
geometry.vao.bindIndexbuffer( bufferInd );
geometry.count = 18 * POINTS_MAX;
geometry.mode = gl.TRIANGLES;
geometry.indexType = gl.UNSIGNED_SHORT;
// -- create materials -------------------------------------------------------------------------
const materials = {
forward: new Material(
svgVert,
conditionCharFrag,
{
defines: { 'FORWARD': 'true' },
initOptions: { geometry, target: dummyRenderTarget },
},
),
deferred: new Material(
svgVert,
conditionCharFrag,
{
defines: { 'DEFERRED': 'true' },
initOptions: { geometry, target: dummyRenderTargetFourDrawBuffers },
},
),
shadow: new Material(
svgVert,
conditionCharFrag,
{
defines: { 'SHADOW': 'true' },
initOptions: { geometry, target: dummyRenderTarget },
},
),
};
if ( process.env.DEV ) {
if ( module.hot ) {
module.hot.accept(
[
'../shaders/svg.vert',
'../shaders/condition-char.frag',
],
() => {
materials.forward.replaceShader( svgVert, conditionCharFrag );
materials.deferred.replaceShader( svgVert, conditionCharFrag );
materials.shadow.replaceShader( svgVert, conditionCharFrag );
},
);
}
}
// -- create meshes ----------------------------------------------------------------------------
const mesh = new Mesh( {
geometry,
materials,
name: process.env.DEV && `ConditionChar/mesh${ i }`,
} );
this.components.push( mesh );
// -- material uniforms ------------------------------------------------------------------------
for ( const material of Object.values( materials ) ) {
material.addUniform( 'svgi', '1f', i );
material.addUniformTexture( 'samplerSvg', table );
auto( 'Condition/phaseOffset', ( { value } ) => {
material.addUniform( 'phaseOffset', '1f', value );
} );
auto( 'Condition/phaseWidth', ( { value } ) => {
material.addUniform( 'phaseWidth', '1f', value );
} );
}
}
}

View File

@@ -54,21 +54,22 @@ export class Cube extends Entity {
geometry.primcount = PRIMCOUNT; geometry.primcount = PRIMCOUNT;
// -- materials -------------------------------------------------------------------------------- // -- materials --------------------------------------------------------------------------------
const materials = { const deferred = new Material(
deferred: new Material(
cubeVert, cubeVert,
cubeFrag, cubeFrag,
{ {
defines: { 'DEFERRED': 'true' }, defines: [ 'DEFERRED 1' ],
initOptions: { geometry: quadGeometry, target: dummyRenderTargetFourDrawBuffers }, initOptions: { geometry: quadGeometry, target: dummyRenderTargetFourDrawBuffers },
}, },
), );
shadow: new Material(
const depth = new Material(
cubeVert, cubeVert,
depthFrag, depthFrag,
{ initOptions: { geometry: quadGeometry, target: dummyRenderTarget } }, { initOptions: { geometry: quadGeometry, target: dummyRenderTarget } },
), );
};
const materials = { deferred, depth };
if ( process.env.DEV ) { if ( process.env.DEV ) {
if ( module.hot ) { if ( module.hot ) {
@@ -78,8 +79,8 @@ export class Cube extends Entity {
'../shaders/cube.frag', '../shaders/cube.frag',
], ],
() => { () => {
materials.deferred.replaceShader( cubeVert, cubeFrag ); deferred.replaceShader( cubeVert, cubeFrag );
materials.shadow.replaceShader( cubeVert, depthFrag ); depth.replaceShader( cubeVert, depthFrag );
}, },
); );
} }

View File

@@ -11,6 +11,7 @@ import { gl, glCat } from '../globals/canvas';
import { randomTexture, randomTextureStatic } from '../globals/randomTexture'; import { randomTexture, randomTextureStatic } from '../globals/randomTexture';
import { quadGeometry } from '../globals/quadGeometry'; import { quadGeometry } from '../globals/quadGeometry';
import { dummyRenderTargetFourDrawBuffers, dummyRenderTarget } from '../globals/dummyRenderTarget'; import { dummyRenderTargetFourDrawBuffers, dummyRenderTarget } from '../globals/dummyRenderTarget';
import { objectValuesMap } from '../utils/objectEntriesMap';
const PARTICLES_SQRT = 8; const PARTICLES_SQRT = 8;
const PARTICLES = PARTICLES_SQRT * PARTICLES_SQRT; const PARTICLES = PARTICLES_SQRT * PARTICLES_SQRT;
@@ -72,7 +73,7 @@ export class FlickyParticles extends Entity {
flickyParticleRenderVert, flickyParticleRenderVert,
flickyParticleRenderFrag, flickyParticleRenderFrag,
{ {
defines: { 'FORWARD': 'true' }, defines: [ 'FORWARD 1' ],
initOptions: { geometry: geometryRender, target: dummyRenderTarget }, initOptions: { geometry: geometryRender, target: dummyRenderTarget },
}, },
); );
@@ -81,7 +82,7 @@ export class FlickyParticles extends Entity {
flickyParticleRenderVert, flickyParticleRenderVert,
flickyParticleRenderFrag, flickyParticleRenderFrag,
{ {
defines: { 'DEFERRED': 'true' }, defines: [ 'DEFERRED 1' ],
initOptions: { geometry: geometryRender, target: dummyRenderTargetFourDrawBuffers }, initOptions: { geometry: geometryRender, target: dummyRenderTargetFourDrawBuffers },
}, },
); );
@@ -90,16 +91,16 @@ export class FlickyParticles extends Entity {
flickyParticleRenderVert, flickyParticleRenderVert,
flickyParticleRenderFrag, flickyParticleRenderFrag,
{ {
defines: { 'SHADOW': 'true' }, defines: [ 'SHADOW 1' ],
initOptions: { geometry: geometryRender, target: dummyRenderTarget }, initOptions: { geometry: geometryRender, target: dummyRenderTarget },
}, },
); );
const materialsRender = { forward, deferred, shadow }; const materialsRender = { forward, deferred, shadow };
for ( const material of Object.values( materialsRender ) ) { objectValuesMap( materialsRender, ( material ) => {
material.addUniformTexture( 'samplerRandomStatic', randomTextureStatic.texture ); material.addUniformTexture( 'samplerRandomStatic', randomTextureStatic.texture );
} } );
if ( process.env.DEV ) { if ( process.env.DEV ) {
if ( module.hot ) { if ( module.hot ) {

View File

@@ -7,6 +7,7 @@ import { Mesh } from '../heck/components/Mesh';
import { Quad } from '../heck/components/Quad'; import { Quad } from '../heck/components/Quad';
import { Swap } from '@fms-cat/experimental'; import { Swap } from '@fms-cat/experimental';
import { gl } from '../globals/canvas'; import { gl } from '../globals/canvas';
import { objectValuesMap } from '../utils/objectEntriesMap';
export interface GPUParticlesOptions { export interface GPUParticlesOptions {
materialCompute: Material; materialCompute: Material;
@@ -62,14 +63,14 @@ export class GPUParticles extends Entity {
name: process.env.DEV && `${ namePrefix }/meshRender`, name: process.env.DEV && `${ namePrefix }/meshRender`,
} ); } );
for ( const material of Object.values( materialsRender ) ) { objectValuesMap( materialsRender, ( material ) => {
material?.addUniform( material?.addUniform(
'resolutionCompute', 'resolutionCompute',
'2f', '2f',
computeWidth, computeWidth,
computeHeight computeHeight
); );
} } );
// -- swapper ---------------------------------------------------------------------------------- // -- swapper ----------------------------------------------------------------------------------
this.components.push( new Lambda( { this.components.push( new Lambda( {
@@ -84,12 +85,12 @@ export class GPUParticles extends Entity {
swapCompute.i.getTexture( attachment ) swapCompute.i.getTexture( attachment )
); );
for ( const material of Object.values( materialsRender ) ) { objectValuesMap( materialsRender, ( material ) => {
material?.addUniformTexture( material?.addUniformTexture(
`samplerCompute${ i }`, `samplerCompute${ i }`,
swapCompute.o.getTexture( attachment ) swapCompute.o.getTexture( attachment )
); );
} } );
} }
quadCompute.target = swapCompute.o; quadCompute.target = swapCompute.o;

View File

@@ -58,7 +58,7 @@ export class LightEntity extends Entity {
renderTarget: swap.o, renderTarget: swap.o,
scene: this.root, scene: this.root,
name: process.env.DEV && `${ options.namePrefix }/shadowMapCamera`, name: process.env.DEV && `${ options.namePrefix }/shadowMapCamera`,
materialTag: 'shadow', materialTag: 'depth',
} ); } );
this.camera.clear = [ 1.0, 1.0, 1.0, 1.0 ]; this.camera.clear = [ 1.0, 1.0, 1.0, 1.0 ];
this.components.push( this.camera ); this.components.push( this.camera );

View File

@@ -10,6 +10,7 @@ import { randomTexture, randomTextureStatic } from '../globals/randomTexture';
import { auto } from '../globals/automaton'; import { auto } from '../globals/automaton';
import { dummyRenderTargetFourDrawBuffers, dummyRenderTarget } from '../globals/dummyRenderTarget'; import { dummyRenderTargetFourDrawBuffers, dummyRenderTarget } from '../globals/dummyRenderTarget';
import { genOctahedron } from '../geometries/genOctahedron'; import { genOctahedron } from '../geometries/genOctahedron';
import { objectValuesMap } from '../utils/objectEntriesMap';
export class Raymarcher extends Entity { export class Raymarcher extends Entity {
public constructor() { public constructor() {
@@ -31,45 +32,46 @@ export class Raymarcher extends Entity {
geometry.indexType = octahedron.indexType; geometry.indexType = octahedron.indexType;
// -- materials -------------------------------------------------------------------------------- // -- materials --------------------------------------------------------------------------------
const materials = { const deferred = new Material(
deferred: new Material( raymarchObjectVert,
raymarchObjectVert, raymarcherFrag,
raymarcherFrag, {
{ defines: [ 'DEFERRED 1' ],
defines: { 'DEFERRED': 'true' }, initOptions: { geometry, target: dummyRenderTargetFourDrawBuffers },
initOptions: { geometry, target: dummyRenderTargetFourDrawBuffers }, },
}, );
),
shadow: new Material( const depth = new Material(
raymarchObjectVert, raymarchObjectVert,
raymarcherFrag, raymarcherFrag,
{ {
defines: { 'SHADOW': 'true' }, defines: [ 'SHADOW 1' ],
initOptions: { geometry, target: dummyRenderTarget } initOptions: { geometry, target: dummyRenderTarget }
}, },
), );
};
const materials = { deferred, depth };
if ( process.env.DEV ) { if ( process.env.DEV ) {
if ( module.hot ) { if ( module.hot ) {
module.hot.accept( '../shaders/raymarcher.frag', () => { module.hot.accept( '../shaders/raymarcher.frag', () => {
materials.deferred.replaceShader( raymarchObjectVert, raymarcherFrag ); deferred.replaceShader( raymarchObjectVert, raymarcherFrag );
materials.shadow.replaceShader( raymarchObjectVert, raymarcherFrag ); depth.replaceShader( raymarchObjectVert, raymarcherFrag );
} ); } );
} }
} }
for ( const material of Object.values( materials ) ) { objectValuesMap( materials, ( material ) => {
material.addUniform( 'range', '4f', -1.0, -1.0, 1.0, 1.0 ); material.addUniform( 'range', '4f', -1.0, -1.0, 1.0, 1.0 );
material.addUniformTexture( 'samplerRandom', randomTexture.texture ); material.addUniformTexture( 'samplerRandom', randomTexture.texture );
material.addUniformTexture( 'samplerRandomStatic', randomTextureStatic.texture ); material.addUniformTexture( 'samplerRandomStatic', randomTextureStatic.texture );
} } );
// -- updater ---------------------------------------------------------------------------------- // -- updater ----------------------------------------------------------------------------------
this.components.push( new Lambda( { this.components.push( new Lambda( {
onDraw: ( event ) => { onDraw: ( event ) => {
for ( const material of Object.values( materials ) ) { objectValuesMap( materials, ( material ) => {
material.addUniform( material.addUniform(
'cameraNearFar', 'cameraNearFar',
'2f', '2f',
@@ -90,15 +92,15 @@ export class Raymarcher extends Entity {
material.addUniform( 'deformAmp', '1f', auto( 'Music/NEURO_WUB_AMP' ) ); material.addUniform( 'deformAmp', '1f', auto( 'Music/NEURO_WUB_AMP' ) );
material.addUniform( 'deformFreq', '1f', auto( 'Music/NEURO_WUB_FREQ' ) + auto( 'Music/NEURO_DETUNE' ) ); material.addUniform( 'deformFreq', '1f', auto( 'Music/NEURO_WUB_FREQ' ) + auto( 'Music/NEURO_DETUNE' ) );
material.addUniform( 'deformTime', '1f', auto( 'Music/NEURO_TIME' ) ); material.addUniform( 'deformTime', '1f', auto( 'Music/NEURO_TIME' ) );
} } );
}, },
name: process.env.DEV && 'Raymarcher/updater', name: process.env.DEV && 'Raymarcher/updater',
} ) ); } ) );
// -- mesh ------------------------------------------------------------------------------------- // -- mesh -------------------------------------------------------------------------------------
const mesh = new Mesh( { const mesh = new Mesh( {
geometry: geometry, geometry,
materials: materials, materials,
name: process.env.DEV && 'Raymarcher/mesh', name: process.env.DEV && 'Raymarcher/mesh',
} ); } );
mesh.cull = MeshCull.None; mesh.cull = MeshCull.None;

View File

@@ -47,28 +47,34 @@ export class Rings extends Entity {
geometry.primcount = PRIMCOUNT; geometry.primcount = PRIMCOUNT;
// -- materials -------------------------------------------------------------------------------- // -- materials --------------------------------------------------------------------------------
const forward = new Material(
ringsVert,
ringsFrag,
{
defines: [ 'FORWARD 1' ],
initOptions: { geometry, target: dummyRenderTarget },
},
);
const deferred = new Material(
ringsVert,
ringsFrag,
{
defines: [ 'DEFERRED 1' ],
initOptions: { geometry, target: dummyRenderTarget },
},
);
const depth = new Material(
ringsVert,
depthFrag,
{ initOptions: { geometry, target: dummyRenderTarget } },
);
const materials = { const materials = {
forward: new Material( forward,
ringsVert, deferred,
ringsFrag, depth,
{
defines: { 'FORWARD': 'true' },
initOptions: { geometry, target: dummyRenderTarget },
},
),
deferred: new Material(
ringsVert,
ringsFrag,
{
defines: { 'DEFERRED': 'true' },
initOptions: { geometry, target: dummyRenderTarget },
},
),
shadow: new Material(
ringsVert,
depthFrag,
{ initOptions: { geometry, target: dummyRenderTarget } },
),
} }
if ( process.env.DEV ) { if ( process.env.DEV ) {
@@ -79,9 +85,9 @@ export class Rings extends Entity {
'../shaders/rings.frag', '../shaders/rings.frag',
], ],
() => { () => {
materials.forward.replaceShader( ringsVert, ringsFrag ); forward.replaceShader( ringsVert, ringsFrag );
materials.deferred.replaceShader( ringsVert, ringsFrag ); deferred.replaceShader( ringsVert, ringsFrag );
materials.shadow.replaceShader( ringsVert, depthFrag ); depth.replaceShader( ringsVert, depthFrag );
}, },
); );
} }
@@ -89,8 +95,8 @@ export class Rings extends Entity {
// -- mesh ------------------------------------------------------------------------------------- // -- mesh -------------------------------------------------------------------------------------
const mesh = new Mesh( { const mesh = new Mesh( {
geometry: geometry, geometry,
materials: materials, materials,
name: process.env.DEV && 'Rings/mesh', name: process.env.DEV && 'Rings/mesh',
} ); } );
this.components.push( mesh ); this.components.push( mesh );

View File

@@ -75,21 +75,21 @@ export class SphereParticles extends Entity {
sphereParticleRenderVert, sphereParticleRenderVert,
sphereParticleRenderFrag, sphereParticleRenderFrag,
{ {
defines: { 'DEFERRED': 'true' }, defines: [ 'DEFERRED 1' ],
initOptions: { geometry: geometryRender, target: dummyRenderTargetFourDrawBuffers }, initOptions: { geometry: geometryRender, target: dummyRenderTargetFourDrawBuffers },
}, },
); );
const shadow = new Material( const depth = new Material(
sphereParticleRenderVert, sphereParticleRenderVert,
depthFrag, depthFrag,
{ initOptions: { geometry: geometryRender, target: dummyRenderTarget } }, { initOptions: { geometry: geometryRender, target: dummyRenderTarget } },
); );
const materialsRender = { deferred, shadow }; const materialsRender = { deferred, depth };
deferred.addUniformTexture( 'samplerRandomStatic', randomTextureStatic.texture ); deferred.addUniformTexture( 'samplerRandomStatic', randomTextureStatic.texture );
shadow.addUniformTexture( 'samplerRandomStatic', randomTextureStatic.texture ); depth.addUniformTexture( 'samplerRandomStatic', randomTextureStatic.texture );
if ( process.env.DEV ) { if ( process.env.DEV ) {
if ( module.hot ) { if ( module.hot ) {
@@ -100,7 +100,7 @@ export class SphereParticles extends Entity {
], ],
() => { () => {
deferred.replaceShader( sphereParticleRenderVert, sphereParticleRenderFrag ); deferred.replaceShader( sphereParticleRenderVert, sphereParticleRenderFrag );
shadow.replaceShader( sphereParticleRenderVert, depthFrag ); depth.replaceShader( sphereParticleRenderVert, depthFrag );
} }
); );
} }

View File

@@ -68,7 +68,7 @@ export class SufferTexts extends Entity {
sufferTextsRenderVert, sufferTextsRenderVert,
sufferTextsRenderFrag, sufferTextsRenderFrag,
{ {
defines: { 'DEFERRED': 'true' }, defines: [ 'DEFERRED 1' ],
initOptions: { geometry: geometryRender, target: dummyRenderTargetFourDrawBuffers }, initOptions: { geometry: geometryRender, target: dummyRenderTargetFourDrawBuffers },
}, },
); );

View File

@@ -108,20 +108,20 @@ export class Trails extends Entity {
trailsRenderVert, trailsRenderVert,
trailsRenderFrag, trailsRenderFrag,
{ {
defines: { 'DEFERRED': 'true' }, defines: [ 'DEFERRED 1' ],
initOptions: { geometry: geometryRender, target: dummyRenderTargetFourDrawBuffers }, initOptions: { geometry: geometryRender, target: dummyRenderTargetFourDrawBuffers },
}, },
); );
const shadow = new Material( const depth = new Material(
trailsRenderVert, trailsRenderVert,
depthFrag, depthFrag,
{ initOptions: { geometry: geometryRender, target: dummyRenderTarget } }, { initOptions: { geometry: geometryRender, target: dummyRenderTarget } },
); );
const materialsRender = { deferred, shadow }; const materialsRender = { deferred, depth };
deferred.addUniformTexture( 'samplerRandomStatic', randomTextureStatic.texture ); deferred.addUniformTexture( 'samplerRandomStatic', randomTextureStatic.texture );
shadow.addUniformTexture( 'samplerRandomStatic', randomTextureStatic.texture ); depth.addUniformTexture( 'samplerRandomStatic', randomTextureStatic.texture );
if ( process.env.DEV ) { if ( process.env.DEV ) {
if ( module.hot ) { if ( module.hot ) {
@@ -132,7 +132,7 @@ export class Trails extends Entity {
], ],
() => { () => {
deferred.replaceShader( trailsRenderVert, trailsRenderFrag ); deferred.replaceShader( trailsRenderVert, trailsRenderFrag );
shadow.replaceShader( trailsRenderVert, depthFrag ); depth.replaceShader( trailsRenderVert, depthFrag );
} }
); );
} }

View File

@@ -1,9 +1,9 @@
import { Automaton } from '@fms-cat/automaton'; import { Automaton } from '@fms-cat/automaton';
import { AutomatonWithGUI } from '@fms-cat/automaton-with-gui'; import { AutomatonWithGUI } from '@fms-cat/automaton-with-gui';
import automatonData from '../automaton.json'; import automatonData from '../automaton.json';
import * as automatonFxs from '../automaton-fxs/automatonFxs';
import { music } from './music'; import { music } from './music';
import { getDivAutomaton } from './dom'; import { getDivAutomaton } from './dom';
import { fxDefinitions } from '../automaton-fxs/fxDefinitions';
export const automaton = ( () => { export const automaton = ( () => {
if ( process.env.DEV ) { if ( process.env.DEV ) {
@@ -14,8 +14,8 @@ export const automaton = ( () => {
{ {
gui: getDivAutomaton(), gui: getDivAutomaton(),
isPlaying: true, isPlaying: true,
fxDefinitions: automatonFxs, fxDefinitions,
} },
); );
automatonWithGUI.on( 'play', () => { music.isPlaying = true; } ); automatonWithGUI.on( 'play', () => { music.isPlaying = true; } );
@@ -38,8 +38,8 @@ export const automaton = ( () => {
return new Automaton( return new Automaton(
automatonData, automatonData,
{ {
fxDefinitions: automatonFxs fxDefinitions,
} },
); );
} }
} )(); } )();

View File

@@ -8,7 +8,7 @@ import { SHADERPOOL } from './ShaderPool';
export type MaterialTag = export type MaterialTag =
| 'deferred' | 'deferred'
| 'forward' | 'forward'
| 'shadow'; | 'depth';
export type MaterialMap<T extends MaterialTag = MaterialTag> = { [ tag in T ]: Material }; export type MaterialMap<T extends MaterialTag = MaterialTag> = { [ tag in T ]: Material };
@@ -20,9 +20,7 @@ export interface MaterialInitOptions {
export class Material { export class Material {
protected __linkOptions: GLCatProgramLinkOptions; protected __linkOptions: GLCatProgramLinkOptions;
protected __defines: { protected __defines: string[];
[ name: string ]: ( string | undefined );
};
protected __uniforms: { protected __uniforms: {
[ name: string ]: { [ name: string ]: {
@@ -84,7 +82,7 @@ export class Material {
vert: string, vert: string,
frag: string, frag: string,
{ defines, blend, linkOptions, initOptions }: { { defines, blend, linkOptions, initOptions }: {
defines?: { [ key: string ]: ( string | undefined ) }, defines?: string[],
blend?: [ GLenum, GLenum ], blend?: [ GLenum, GLenum ],
linkOptions?: GLCatProgramLinkOptions, linkOptions?: GLCatProgramLinkOptions,
initOptions?: MaterialInitOptions, initOptions?: MaterialInitOptions,
@@ -93,7 +91,7 @@ export class Material {
this.__vert = vert; this.__vert = vert;
this.__frag = frag; this.__frag = frag;
this.__linkOptions = linkOptions ?? {}; this.__linkOptions = linkOptions ?? {};
this.__defines = defines ?? {}; this.__defines = defines ?? [];
this.blend = blend ?? [ gl.ONE, gl.ZERO ]; this.blend = blend ?? [ gl.ONE, gl.ZERO ];
if ( initOptions ) { if ( initOptions ) {
@@ -153,12 +151,12 @@ export class Material {
vert: string, vert: string,
frag: string, frag: string,
options?: { options?: {
defines?: { [ key: string ]: ( string | undefined ) }, defines?: string[],
linkOptions?: GLCatProgramLinkOptions, linkOptions?: GLCatProgramLinkOptions,
}, },
): Promise<void> { ): Promise<void> {
if ( options?.defines ) { if ( options?.defines ) {
this.__defines = { ...options.defines }; this.__defines = [ ...options.defines ];
} }
const program = await SHADERPOOL.getProgramAsync( const program = await SHADERPOOL.getProgramAsync(
@@ -194,9 +192,9 @@ export class Material {
protected __withDefines( code: string ): string { protected __withDefines( code: string ): string {
let inject = ''; let inject = '';
Object.entries( this.__defines ).map( ( [ key, value ] ) => { this.__defines.map( ( value ) => {
if ( value != null ) { if ( value != null ) {
inject += `#define ${key} ${value}\n`; inject += `#define ${value}\n`;
} }
} ); } );

View File

@@ -4,6 +4,7 @@ import { Component } from './heck/components/Component';
import { music } from './globals/music'; import { music } from './globals/music';
import { getCheckboxActive, getDivCanvasContainer } from './globals/dom'; import { getCheckboxActive, getDivCanvasContainer } from './globals/dom';
import { dog } from './scene'; import { dog } from './scene';
import { START_POSITION } from './config';
// == dom ========================================================================================== // == dom ==========================================================================================
document.body.style.margin = '0'; document.body.style.margin = '0';
@@ -20,6 +21,9 @@ if ( process.env.DEV ) {
canvas.style.margin = 'auto'; canvas.style.margin = 'auto';
canvas.style.maxWidth = '100%'; canvas.style.maxWidth = '100%';
canvas.style.maxHeight = '100%'; canvas.style.maxHeight = '100%';
music.isPlaying = true;
music.time = START_POSITION;
} else { } else {
canvas.style.position = 'fixed'; canvas.style.position = 'fixed';
canvas.style.left = '0'; canvas.style.left = '0';
@@ -34,6 +38,7 @@ if ( process.env.DEV ) {
button.onclick = () => { button.onclick = () => {
document.body.appendChild( canvas ); document.body.appendChild( canvas );
music.isPlaying = true; music.isPlaying = true;
music.time = START_POSITION;
document.body.requestFullscreen(); document.body.requestFullscreen();
}; };
} }
@@ -70,8 +75,3 @@ if ( process.env.DEV ) {
console.info( Component.nameMap ); console.info( Component.nameMap );
console.info( BufferRenderTarget.nameMap ); console.info( BufferRenderTarget.nameMap );
} }
// == music ========================================================================================
if ( process.env.DEV ) {
music.isPlaying = true;
}

View File

@@ -1,18 +1,18 @@
export const sufferList = [ export const sufferList = [
'#DEFINE DISGRACE 1', '#DEFINE DISGRACE 1',
'WELCOME TO THE MANGLER HELL',
'CTRL + ALT + DESPAIR', 'CTRL + ALT + DESPAIR',
'PUBIC CONSTRUCTOR()',
'LIBOPUS IS CHEATING', 'LIBOPUS IS CHEATING',
'PUBLIC GET FUCKED()', 'PUBLIC GET FUCKED()',
'\'RETRUN\': UNDECLARED IDENTIFIER', '\'RETRUN\': UNDECLARED IDENTIFIER',
'NOTICE ME, GARBAGE COLLECTOR', 'NOTICE ME, GARBAGE COLLECTOR',
'WEBGL HATES YOU', 'WEBGL HATES YOU',
'#DEFINE COMPROMISE 1', '#DEFINE COMPROMISE 1',
'GL.DISABLE(GL.TIMEZONE)',
'WHERE IS MY SLEEPING SCHEDULE?', 'WHERE IS MY SLEEPING SCHEDULE?',
'GL.DISABLE(GL.TIMEZONE)',
'SVG.GETPOINTATLENGTH IS CHEATING', 'SVG.GETPOINTATLENGTH IS CHEATING',
'COPY\'N\'PASTE ENGINEER', 'COPY\'N\'PASTE ENGINEER',
'ENGLISH SUCKS',
'60FPS OR DIE', '60FPS OR DIE',
'<PLACEHOLDER>',
'END MY SUFFER', 'END MY SUFFER',
]; ];

View File

@@ -0,0 +1,6 @@
export function iterateOverMap<TKey, TValue, TReturn>(
map: Map<TKey, TValue>,
func: ( value: TValue, key: TKey ) => TReturn,
): TReturn[] {
return Array.from( map.entries() ).map( ( [ key, value ] ) => func( value, key ) );
}

View File

@@ -1,11 +1,40 @@
/* eslint-env node */ /* eslint-env node */
/* eslint-disable @typescript-eslint/no-var-requires */ /* eslint-disable @typescript-eslint/no-var-requires */
const TerserPlugin = require( 'terser-webpack-plugin' );
const HtmlWebpackPlugin = require( 'html-webpack-plugin' ); const HtmlWebpackPlugin = require( 'html-webpack-plugin' );
const packageJson = require( './package.json' ); const packageJson = require( './package.json' );
const path = require( 'path' ); const path = require( 'path' );
const webpack = require( 'webpack' ); const webpack = require( 'webpack' );
/**
* @type TerserPlugin.TerserPluginOptions[ 'terserOptions' ]
*/
const terserOptions = {
compress: {
arguments: true,
booleans_as_integers: true,
drop_console: true,
keep_fargs: false,
passes: 1,
unsafe_arrows: true,
unsafe_math: true,
},
mangle: {
properties: {
regex: /.+/,
reserved: [
// material tags
'forward',
'deferred',
'depth',
]
},
},
module: true,
toplevel: true,
};
module.exports = ( env, argv ) => { module.exports = ( env, argv ) => {
const VERSION = packageJson.version; const VERSION = packageJson.version;
const DEV = argv.mode === 'development'; const DEV = argv.mode === 'development';
@@ -67,6 +96,7 @@ module.exports = ( env, argv ) => {
}, },
optimization: { optimization: {
minimize: !DEV, minimize: !DEV,
minimizer: [ new TerserPlugin( { terserOptions } ) ],
moduleIds: DEV ? 'named' : undefined, moduleIds: DEV ? 'named' : undefined,
usedExports: !DEV, usedExports: !DEV,
}, },