mirror of
https://github.com/FMS-Cat/condition.git
synced 2025-08-08 06:56:31 +02:00
size: mangler hell
This commit is contained in:
17
README.md
17
README.md
@@ -2,9 +2,24 @@
|
||||
|
||||
## 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
|
||||
yarn
|
||||
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
|
||||
|
@@ -5,7 +5,10 @@
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
"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": {
|
||||
"@fms-cat/automaton": "^4.1.0",
|
||||
|
106
pnginator.rb
106
pnginator.rb
@@ -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
|
@@ -1,3 +0,0 @@
|
||||
export { sine, hermitePatch } from '@fms-cat/automaton-fxs';
|
||||
export { repeat } from './repeat';
|
||||
export { transpose } from './transpose';
|
14
src/automaton-fxs/fxDefinitions.ts
Normal file
14
src/automaton-fxs/fxDefinitions.ts
Normal 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 };
|
34
src/automaton-fxs/hermitePatch.ts
Normal file
34
src/automaton-fxs/hermitePatch.ts
Normal 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.';
|
||||
}
|
@@ -1,11 +1,6 @@
|
||||
import type { FxDefinition } from '@fms-cat/automaton';
|
||||
|
||||
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 ) {
|
||||
if ( context.index === context.i1 ) {
|
||||
context.setShouldNotInterpolate( true );
|
||||
@@ -20,3 +15,11 @@ export const repeat: FxDefinition = {
|
||||
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
21
src/automaton-fxs/sine.ts
Normal 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 }
|
||||
};
|
||||
}
|
@@ -1,9 +1,6 @@
|
||||
import type { FxDefinition } from '@fms-cat/automaton';
|
||||
|
||||
export const transpose: FxDefinition = {
|
||||
params: {
|
||||
note: { name: 'Note', type: 'float', default: 0.0 }
|
||||
},
|
||||
func( context ) {
|
||||
if ( context.init ) {
|
||||
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 );
|
||||
}
|
||||
};
|
||||
|
||||
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
@@ -5,5 +5,6 @@ export const
|
||||
AO_RESOLUTION_RATIO = 1.0,
|
||||
RESOLUTION = [ 1280, 720 ],
|
||||
MUSIC_BPM = 180,
|
||||
START_POSITION = 36,
|
||||
MUSIC_BUFFER_LENGTH = 4096,
|
||||
MUSIC_AUTOMATON_TEXTURE_HEIGHT = 16;
|
||||
|
@@ -97,9 +97,7 @@ export class CameraEntity extends Entity {
|
||||
quadVert,
|
||||
shadingFrag,
|
||||
{
|
||||
defines: {
|
||||
IS_FIRST_LIGHT: iLight === 0 ? 'true' : undefined
|
||||
},
|
||||
defines: iLight === 0 ? [ 'IS_FIRST_LIGHT' ] : [],
|
||||
initOptions: { geometry: quadGeometry, target: dummyRenderTarget },
|
||||
},
|
||||
);
|
||||
|
@@ -102,32 +102,34 @@ export class Condition extends Entity {
|
||||
geometry.primcount = 12 * 16;
|
||||
|
||||
// -- create materials -------------------------------------------------------------------------
|
||||
const materials = {
|
||||
forward: new Material(
|
||||
conditionVert,
|
||||
conditionFrag,
|
||||
{
|
||||
defines: { 'FORWARD': 'true' },
|
||||
initOptions: { geometry, target: dummyRenderTarget },
|
||||
},
|
||||
),
|
||||
deferred: new Material(
|
||||
conditionVert,
|
||||
conditionFrag,
|
||||
{
|
||||
defines: { 'DEFERRED': 'true' },
|
||||
initOptions: { geometry, target: dummyRenderTargetFourDrawBuffers },
|
||||
},
|
||||
),
|
||||
shadow: new Material(
|
||||
conditionVert,
|
||||
conditionFrag,
|
||||
{
|
||||
defines: { 'SHADOW': 'true' },
|
||||
initOptions: { geometry, target: dummyRenderTarget },
|
||||
},
|
||||
),
|
||||
};
|
||||
const forward = new Material(
|
||||
conditionVert,
|
||||
conditionFrag,
|
||||
{
|
||||
defines: [ 'FORWARD 1' ],
|
||||
initOptions: { geometry, target: dummyRenderTarget },
|
||||
},
|
||||
);
|
||||
|
||||
const deferred = new Material(
|
||||
conditionVert,
|
||||
conditionFrag,
|
||||
{
|
||||
defines: [ 'DEFERRED 1' ],
|
||||
initOptions: { geometry, target: dummyRenderTargetFourDrawBuffers },
|
||||
},
|
||||
);
|
||||
|
||||
const depth = new Material(
|
||||
conditionVert,
|
||||
conditionFrag,
|
||||
{
|
||||
defines: [ 'SHADOW 1' ],
|
||||
initOptions: { geometry, target: dummyRenderTarget },
|
||||
},
|
||||
);
|
||||
|
||||
const materials = { forward, deferred, depth };
|
||||
|
||||
objectValuesMap( materials, ( material ) => {
|
||||
material.addUniformTexture( 'samplerSvg', texture );
|
||||
@@ -149,9 +151,9 @@ export class Condition extends Entity {
|
||||
'../shaders/condition.frag',
|
||||
],
|
||||
() => {
|
||||
materials.forward.replaceShader( conditionVert, conditionFrag );
|
||||
materials.deferred.replaceShader( conditionVert, conditionFrag );
|
||||
materials.shadow.replaceShader( conditionVert, conditionFrag );
|
||||
forward.replaceShader( conditionVert, conditionFrag );
|
||||
deferred.replaceShader( conditionVert, conditionFrag );
|
||||
depth.replaceShader( conditionVert, conditionFrag );
|
||||
},
|
||||
);
|
||||
}
|
||||
|
@@ -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 );
|
||||
} );
|
||||
}
|
||||
}
|
||||
}
|
@@ -54,21 +54,22 @@ export class Cube extends Entity {
|
||||
geometry.primcount = PRIMCOUNT;
|
||||
|
||||
// -- materials --------------------------------------------------------------------------------
|
||||
const materials = {
|
||||
deferred: new Material(
|
||||
const deferred = new Material(
|
||||
cubeVert,
|
||||
cubeFrag,
|
||||
{
|
||||
defines: { 'DEFERRED': 'true' },
|
||||
defines: [ 'DEFERRED 1' ],
|
||||
initOptions: { geometry: quadGeometry, target: dummyRenderTargetFourDrawBuffers },
|
||||
},
|
||||
),
|
||||
shadow: new Material(
|
||||
);
|
||||
|
||||
const depth = new Material(
|
||||
cubeVert,
|
||||
depthFrag,
|
||||
{ initOptions: { geometry: quadGeometry, target: dummyRenderTarget } },
|
||||
),
|
||||
};
|
||||
);
|
||||
|
||||
const materials = { deferred, depth };
|
||||
|
||||
if ( process.env.DEV ) {
|
||||
if ( module.hot ) {
|
||||
@@ -78,8 +79,8 @@ export class Cube extends Entity {
|
||||
'../shaders/cube.frag',
|
||||
],
|
||||
() => {
|
||||
materials.deferred.replaceShader( cubeVert, cubeFrag );
|
||||
materials.shadow.replaceShader( cubeVert, depthFrag );
|
||||
deferred.replaceShader( cubeVert, cubeFrag );
|
||||
depth.replaceShader( cubeVert, depthFrag );
|
||||
},
|
||||
);
|
||||
}
|
||||
|
@@ -11,6 +11,7 @@ import { gl, glCat } from '../globals/canvas';
|
||||
import { randomTexture, randomTextureStatic } from '../globals/randomTexture';
|
||||
import { quadGeometry } from '../globals/quadGeometry';
|
||||
import { dummyRenderTargetFourDrawBuffers, dummyRenderTarget } from '../globals/dummyRenderTarget';
|
||||
import { objectValuesMap } from '../utils/objectEntriesMap';
|
||||
|
||||
const PARTICLES_SQRT = 8;
|
||||
const PARTICLES = PARTICLES_SQRT * PARTICLES_SQRT;
|
||||
@@ -72,7 +73,7 @@ export class FlickyParticles extends Entity {
|
||||
flickyParticleRenderVert,
|
||||
flickyParticleRenderFrag,
|
||||
{
|
||||
defines: { 'FORWARD': 'true' },
|
||||
defines: [ 'FORWARD 1' ],
|
||||
initOptions: { geometry: geometryRender, target: dummyRenderTarget },
|
||||
},
|
||||
);
|
||||
@@ -81,7 +82,7 @@ export class FlickyParticles extends Entity {
|
||||
flickyParticleRenderVert,
|
||||
flickyParticleRenderFrag,
|
||||
{
|
||||
defines: { 'DEFERRED': 'true' },
|
||||
defines: [ 'DEFERRED 1' ],
|
||||
initOptions: { geometry: geometryRender, target: dummyRenderTargetFourDrawBuffers },
|
||||
},
|
||||
);
|
||||
@@ -90,16 +91,16 @@ export class FlickyParticles extends Entity {
|
||||
flickyParticleRenderVert,
|
||||
flickyParticleRenderFrag,
|
||||
{
|
||||
defines: { 'SHADOW': 'true' },
|
||||
defines: [ 'SHADOW 1' ],
|
||||
initOptions: { geometry: geometryRender, target: dummyRenderTarget },
|
||||
},
|
||||
);
|
||||
|
||||
const materialsRender = { forward, deferred, shadow };
|
||||
|
||||
for ( const material of Object.values( materialsRender ) ) {
|
||||
objectValuesMap( materialsRender, ( material ) => {
|
||||
material.addUniformTexture( 'samplerRandomStatic', randomTextureStatic.texture );
|
||||
}
|
||||
} );
|
||||
|
||||
if ( process.env.DEV ) {
|
||||
if ( module.hot ) {
|
||||
|
@@ -7,6 +7,7 @@ import { Mesh } from '../heck/components/Mesh';
|
||||
import { Quad } from '../heck/components/Quad';
|
||||
import { Swap } from '@fms-cat/experimental';
|
||||
import { gl } from '../globals/canvas';
|
||||
import { objectValuesMap } from '../utils/objectEntriesMap';
|
||||
|
||||
export interface GPUParticlesOptions {
|
||||
materialCompute: Material;
|
||||
@@ -62,14 +63,14 @@ export class GPUParticles extends Entity {
|
||||
name: process.env.DEV && `${ namePrefix }/meshRender`,
|
||||
} );
|
||||
|
||||
for ( const material of Object.values( materialsRender ) ) {
|
||||
objectValuesMap( materialsRender, ( material ) => {
|
||||
material?.addUniform(
|
||||
'resolutionCompute',
|
||||
'2f',
|
||||
computeWidth,
|
||||
computeHeight
|
||||
);
|
||||
}
|
||||
} );
|
||||
|
||||
// -- swapper ----------------------------------------------------------------------------------
|
||||
this.components.push( new Lambda( {
|
||||
@@ -84,12 +85,12 @@ export class GPUParticles extends Entity {
|
||||
swapCompute.i.getTexture( attachment )
|
||||
);
|
||||
|
||||
for ( const material of Object.values( materialsRender ) ) {
|
||||
objectValuesMap( materialsRender, ( material ) => {
|
||||
material?.addUniformTexture(
|
||||
`samplerCompute${ i }`,
|
||||
swapCompute.o.getTexture( attachment )
|
||||
);
|
||||
}
|
||||
} );
|
||||
}
|
||||
|
||||
quadCompute.target = swapCompute.o;
|
||||
|
@@ -58,7 +58,7 @@ export class LightEntity extends Entity {
|
||||
renderTarget: swap.o,
|
||||
scene: this.root,
|
||||
name: process.env.DEV && `${ options.namePrefix }/shadowMapCamera`,
|
||||
materialTag: 'shadow',
|
||||
materialTag: 'depth',
|
||||
} );
|
||||
this.camera.clear = [ 1.0, 1.0, 1.0, 1.0 ];
|
||||
this.components.push( this.camera );
|
||||
|
@@ -10,6 +10,7 @@ import { randomTexture, randomTextureStatic } from '../globals/randomTexture';
|
||||
import { auto } from '../globals/automaton';
|
||||
import { dummyRenderTargetFourDrawBuffers, dummyRenderTarget } from '../globals/dummyRenderTarget';
|
||||
import { genOctahedron } from '../geometries/genOctahedron';
|
||||
import { objectValuesMap } from '../utils/objectEntriesMap';
|
||||
|
||||
export class Raymarcher extends Entity {
|
||||
public constructor() {
|
||||
@@ -31,45 +32,46 @@ export class Raymarcher extends Entity {
|
||||
geometry.indexType = octahedron.indexType;
|
||||
|
||||
// -- materials --------------------------------------------------------------------------------
|
||||
const materials = {
|
||||
deferred: new Material(
|
||||
raymarchObjectVert,
|
||||
raymarcherFrag,
|
||||
{
|
||||
defines: { 'DEFERRED': 'true' },
|
||||
initOptions: { geometry, target: dummyRenderTargetFourDrawBuffers },
|
||||
},
|
||||
),
|
||||
shadow: new Material(
|
||||
raymarchObjectVert,
|
||||
raymarcherFrag,
|
||||
{
|
||||
defines: { 'SHADOW': 'true' },
|
||||
initOptions: { geometry, target: dummyRenderTarget }
|
||||
},
|
||||
),
|
||||
};
|
||||
const deferred = new Material(
|
||||
raymarchObjectVert,
|
||||
raymarcherFrag,
|
||||
{
|
||||
defines: [ 'DEFERRED 1' ],
|
||||
initOptions: { geometry, target: dummyRenderTargetFourDrawBuffers },
|
||||
},
|
||||
);
|
||||
|
||||
const depth = new Material(
|
||||
raymarchObjectVert,
|
||||
raymarcherFrag,
|
||||
{
|
||||
defines: [ 'SHADOW 1' ],
|
||||
initOptions: { geometry, target: dummyRenderTarget }
|
||||
},
|
||||
);
|
||||
|
||||
const materials = { deferred, depth };
|
||||
|
||||
if ( process.env.DEV ) {
|
||||
if ( module.hot ) {
|
||||
module.hot.accept( '../shaders/raymarcher.frag', () => {
|
||||
materials.deferred.replaceShader( raymarchObjectVert, raymarcherFrag );
|
||||
materials.shadow.replaceShader( raymarchObjectVert, raymarcherFrag );
|
||||
deferred.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.addUniformTexture( 'samplerRandom', randomTexture.texture );
|
||||
material.addUniformTexture( 'samplerRandomStatic', randomTextureStatic.texture );
|
||||
}
|
||||
} );
|
||||
|
||||
// -- updater ----------------------------------------------------------------------------------
|
||||
this.components.push( new Lambda( {
|
||||
onDraw: ( event ) => {
|
||||
for ( const material of Object.values( materials ) ) {
|
||||
objectValuesMap( materials, ( material ) => {
|
||||
material.addUniform(
|
||||
'cameraNearFar',
|
||||
'2f',
|
||||
@@ -90,15 +92,15 @@ export class Raymarcher extends Entity {
|
||||
material.addUniform( 'deformAmp', '1f', auto( 'Music/NEURO_WUB_AMP' ) );
|
||||
material.addUniform( 'deformFreq', '1f', auto( 'Music/NEURO_WUB_FREQ' ) + auto( 'Music/NEURO_DETUNE' ) );
|
||||
material.addUniform( 'deformTime', '1f', auto( 'Music/NEURO_TIME' ) );
|
||||
}
|
||||
} );
|
||||
},
|
||||
name: process.env.DEV && 'Raymarcher/updater',
|
||||
} ) );
|
||||
|
||||
// -- mesh -------------------------------------------------------------------------------------
|
||||
const mesh = new Mesh( {
|
||||
geometry: geometry,
|
||||
materials: materials,
|
||||
geometry,
|
||||
materials,
|
||||
name: process.env.DEV && 'Raymarcher/mesh',
|
||||
} );
|
||||
mesh.cull = MeshCull.None;
|
||||
|
@@ -47,28 +47,34 @@ export class Rings extends Entity {
|
||||
geometry.primcount = PRIMCOUNT;
|
||||
|
||||
// -- 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 = {
|
||||
forward: new Material(
|
||||
ringsVert,
|
||||
ringsFrag,
|
||||
{
|
||||
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 } },
|
||||
),
|
||||
forward,
|
||||
deferred,
|
||||
depth,
|
||||
}
|
||||
|
||||
if ( process.env.DEV ) {
|
||||
@@ -79,9 +85,9 @@ export class Rings extends Entity {
|
||||
'../shaders/rings.frag',
|
||||
],
|
||||
() => {
|
||||
materials.forward.replaceShader( ringsVert, ringsFrag );
|
||||
materials.deferred.replaceShader( ringsVert, ringsFrag );
|
||||
materials.shadow.replaceShader( ringsVert, depthFrag );
|
||||
forward.replaceShader( ringsVert, ringsFrag );
|
||||
deferred.replaceShader( ringsVert, ringsFrag );
|
||||
depth.replaceShader( ringsVert, depthFrag );
|
||||
},
|
||||
);
|
||||
}
|
||||
@@ -89,8 +95,8 @@ export class Rings extends Entity {
|
||||
|
||||
// -- mesh -------------------------------------------------------------------------------------
|
||||
const mesh = new Mesh( {
|
||||
geometry: geometry,
|
||||
materials: materials,
|
||||
geometry,
|
||||
materials,
|
||||
name: process.env.DEV && 'Rings/mesh',
|
||||
} );
|
||||
this.components.push( mesh );
|
||||
|
@@ -75,21 +75,21 @@ export class SphereParticles extends Entity {
|
||||
sphereParticleRenderVert,
|
||||
sphereParticleRenderFrag,
|
||||
{
|
||||
defines: { 'DEFERRED': 'true' },
|
||||
defines: [ 'DEFERRED 1' ],
|
||||
initOptions: { geometry: geometryRender, target: dummyRenderTargetFourDrawBuffers },
|
||||
},
|
||||
);
|
||||
|
||||
const shadow = new Material(
|
||||
const depth = new Material(
|
||||
sphereParticleRenderVert,
|
||||
depthFrag,
|
||||
{ initOptions: { geometry: geometryRender, target: dummyRenderTarget } },
|
||||
);
|
||||
|
||||
const materialsRender = { deferred, shadow };
|
||||
const materialsRender = { deferred, depth };
|
||||
|
||||
deferred.addUniformTexture( 'samplerRandomStatic', randomTextureStatic.texture );
|
||||
shadow.addUniformTexture( 'samplerRandomStatic', randomTextureStatic.texture );
|
||||
depth.addUniformTexture( 'samplerRandomStatic', randomTextureStatic.texture );
|
||||
|
||||
if ( process.env.DEV ) {
|
||||
if ( module.hot ) {
|
||||
@@ -100,7 +100,7 @@ export class SphereParticles extends Entity {
|
||||
],
|
||||
() => {
|
||||
deferred.replaceShader( sphereParticleRenderVert, sphereParticleRenderFrag );
|
||||
shadow.replaceShader( sphereParticleRenderVert, depthFrag );
|
||||
depth.replaceShader( sphereParticleRenderVert, depthFrag );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
@@ -68,7 +68,7 @@ export class SufferTexts extends Entity {
|
||||
sufferTextsRenderVert,
|
||||
sufferTextsRenderFrag,
|
||||
{
|
||||
defines: { 'DEFERRED': 'true' },
|
||||
defines: [ 'DEFERRED 1' ],
|
||||
initOptions: { geometry: geometryRender, target: dummyRenderTargetFourDrawBuffers },
|
||||
},
|
||||
);
|
||||
|
@@ -108,20 +108,20 @@ export class Trails extends Entity {
|
||||
trailsRenderVert,
|
||||
trailsRenderFrag,
|
||||
{
|
||||
defines: { 'DEFERRED': 'true' },
|
||||
defines: [ 'DEFERRED 1' ],
|
||||
initOptions: { geometry: geometryRender, target: dummyRenderTargetFourDrawBuffers },
|
||||
},
|
||||
);
|
||||
const shadow = new Material(
|
||||
const depth = new Material(
|
||||
trailsRenderVert,
|
||||
depthFrag,
|
||||
{ initOptions: { geometry: geometryRender, target: dummyRenderTarget } },
|
||||
);
|
||||
|
||||
const materialsRender = { deferred, shadow };
|
||||
const materialsRender = { deferred, depth };
|
||||
|
||||
deferred.addUniformTexture( 'samplerRandomStatic', randomTextureStatic.texture );
|
||||
shadow.addUniformTexture( 'samplerRandomStatic', randomTextureStatic.texture );
|
||||
depth.addUniformTexture( 'samplerRandomStatic', randomTextureStatic.texture );
|
||||
|
||||
if ( process.env.DEV ) {
|
||||
if ( module.hot ) {
|
||||
@@ -132,7 +132,7 @@ export class Trails extends Entity {
|
||||
],
|
||||
() => {
|
||||
deferred.replaceShader( trailsRenderVert, trailsRenderFrag );
|
||||
shadow.replaceShader( trailsRenderVert, depthFrag );
|
||||
depth.replaceShader( trailsRenderVert, depthFrag );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
@@ -1,9 +1,9 @@
|
||||
import { Automaton } from '@fms-cat/automaton';
|
||||
import { AutomatonWithGUI } from '@fms-cat/automaton-with-gui';
|
||||
import automatonData from '../automaton.json';
|
||||
import * as automatonFxs from '../automaton-fxs/automatonFxs';
|
||||
import { music } from './music';
|
||||
import { getDivAutomaton } from './dom';
|
||||
import { fxDefinitions } from '../automaton-fxs/fxDefinitions';
|
||||
|
||||
export const automaton = ( () => {
|
||||
if ( process.env.DEV ) {
|
||||
@@ -14,8 +14,8 @@ export const automaton = ( () => {
|
||||
{
|
||||
gui: getDivAutomaton(),
|
||||
isPlaying: true,
|
||||
fxDefinitions: automatonFxs,
|
||||
}
|
||||
fxDefinitions,
|
||||
},
|
||||
);
|
||||
|
||||
automatonWithGUI.on( 'play', () => { music.isPlaying = true; } );
|
||||
@@ -38,8 +38,8 @@ export const automaton = ( () => {
|
||||
return new Automaton(
|
||||
automatonData,
|
||||
{
|
||||
fxDefinitions: automatonFxs
|
||||
}
|
||||
fxDefinitions,
|
||||
},
|
||||
);
|
||||
}
|
||||
} )();
|
||||
|
@@ -8,7 +8,7 @@ import { SHADERPOOL } from './ShaderPool';
|
||||
export type MaterialTag =
|
||||
| 'deferred'
|
||||
| 'forward'
|
||||
| 'shadow';
|
||||
| 'depth';
|
||||
|
||||
export type MaterialMap<T extends MaterialTag = MaterialTag> = { [ tag in T ]: Material };
|
||||
|
||||
@@ -20,9 +20,7 @@ export interface MaterialInitOptions {
|
||||
export class Material {
|
||||
protected __linkOptions: GLCatProgramLinkOptions;
|
||||
|
||||
protected __defines: {
|
||||
[ name: string ]: ( string | undefined );
|
||||
};
|
||||
protected __defines: string[];
|
||||
|
||||
protected __uniforms: {
|
||||
[ name: string ]: {
|
||||
@@ -84,7 +82,7 @@ export class Material {
|
||||
vert: string,
|
||||
frag: string,
|
||||
{ defines, blend, linkOptions, initOptions }: {
|
||||
defines?: { [ key: string ]: ( string | undefined ) },
|
||||
defines?: string[],
|
||||
blend?: [ GLenum, GLenum ],
|
||||
linkOptions?: GLCatProgramLinkOptions,
|
||||
initOptions?: MaterialInitOptions,
|
||||
@@ -93,7 +91,7 @@ export class Material {
|
||||
this.__vert = vert;
|
||||
this.__frag = frag;
|
||||
this.__linkOptions = linkOptions ?? {};
|
||||
this.__defines = defines ?? {};
|
||||
this.__defines = defines ?? [];
|
||||
this.blend = blend ?? [ gl.ONE, gl.ZERO ];
|
||||
|
||||
if ( initOptions ) {
|
||||
@@ -153,12 +151,12 @@ export class Material {
|
||||
vert: string,
|
||||
frag: string,
|
||||
options?: {
|
||||
defines?: { [ key: string ]: ( string | undefined ) },
|
||||
defines?: string[],
|
||||
linkOptions?: GLCatProgramLinkOptions,
|
||||
},
|
||||
): Promise<void> {
|
||||
if ( options?.defines ) {
|
||||
this.__defines = { ...options.defines };
|
||||
this.__defines = [ ...options.defines ];
|
||||
}
|
||||
|
||||
const program = await SHADERPOOL.getProgramAsync(
|
||||
@@ -194,9 +192,9 @@ export class Material {
|
||||
protected __withDefines( code: string ): string {
|
||||
let inject = '';
|
||||
|
||||
Object.entries( this.__defines ).map( ( [ key, value ] ) => {
|
||||
this.__defines.map( ( value ) => {
|
||||
if ( value != null ) {
|
||||
inject += `#define ${key} ${value}\n`;
|
||||
inject += `#define ${value}\n`;
|
||||
}
|
||||
} );
|
||||
|
||||
|
10
src/main.ts
10
src/main.ts
@@ -4,6 +4,7 @@ import { Component } from './heck/components/Component';
|
||||
import { music } from './globals/music';
|
||||
import { getCheckboxActive, getDivCanvasContainer } from './globals/dom';
|
||||
import { dog } from './scene';
|
||||
import { START_POSITION } from './config';
|
||||
|
||||
// == dom ==========================================================================================
|
||||
document.body.style.margin = '0';
|
||||
@@ -20,6 +21,9 @@ if ( process.env.DEV ) {
|
||||
canvas.style.margin = 'auto';
|
||||
canvas.style.maxWidth = '100%';
|
||||
canvas.style.maxHeight = '100%';
|
||||
|
||||
music.isPlaying = true;
|
||||
music.time = START_POSITION;
|
||||
} else {
|
||||
canvas.style.position = 'fixed';
|
||||
canvas.style.left = '0';
|
||||
@@ -34,6 +38,7 @@ if ( process.env.DEV ) {
|
||||
button.onclick = () => {
|
||||
document.body.appendChild( canvas );
|
||||
music.isPlaying = true;
|
||||
music.time = START_POSITION;
|
||||
document.body.requestFullscreen();
|
||||
};
|
||||
}
|
||||
@@ -70,8 +75,3 @@ if ( process.env.DEV ) {
|
||||
console.info( Component.nameMap );
|
||||
console.info( BufferRenderTarget.nameMap );
|
||||
}
|
||||
|
||||
// == music ========================================================================================
|
||||
if ( process.env.DEV ) {
|
||||
music.isPlaying = true;
|
||||
}
|
||||
|
@@ -1,18 +1,18 @@
|
||||
export const sufferList = [
|
||||
'#DEFINE DISGRACE 1',
|
||||
'WELCOME TO THE MANGLER HELL',
|
||||
'CTRL + ALT + DESPAIR',
|
||||
'PUBIC CONSTRUCTOR()',
|
||||
'LIBOPUS IS CHEATING',
|
||||
'PUBLIC GET FUCKED()',
|
||||
'\'RETRUN\': UNDECLARED IDENTIFIER',
|
||||
'NOTICE ME, GARBAGE COLLECTOR',
|
||||
'WEBGL HATES YOU',
|
||||
'#DEFINE COMPROMISE 1',
|
||||
'GL.DISABLE(GL.TIMEZONE)',
|
||||
'WHERE IS MY SLEEPING SCHEDULE?',
|
||||
'GL.DISABLE(GL.TIMEZONE)',
|
||||
'SVG.GETPOINTATLENGTH IS CHEATING',
|
||||
'COPY\'N\'PASTE ENGINEER',
|
||||
'ENGLISH SUCKS',
|
||||
'60FPS OR DIE',
|
||||
'<PLACEHOLDER>',
|
||||
'END MY SUFFER',
|
||||
];
|
||||
|
6
src/utils/iterateOverMap.ts
Normal file
6
src/utils/iterateOverMap.ts
Normal 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 ) );
|
||||
}
|
@@ -1,11 +1,40 @@
|
||||
/* eslint-env node */
|
||||
/* eslint-disable @typescript-eslint/no-var-requires */
|
||||
|
||||
const TerserPlugin = require( 'terser-webpack-plugin' );
|
||||
const HtmlWebpackPlugin = require( 'html-webpack-plugin' );
|
||||
const packageJson = require( './package.json' );
|
||||
const path = require( 'path' );
|
||||
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 ) => {
|
||||
const VERSION = packageJson.version;
|
||||
const DEV = argv.mode === 'development';
|
||||
@@ -67,6 +96,7 @@ module.exports = ( env, argv ) => {
|
||||
},
|
||||
optimization: {
|
||||
minimize: !DEV,
|
||||
minimizer: [ new TerserPlugin( { terserOptions } ) ],
|
||||
moduleIds: DEV ? 'named' : undefined,
|
||||
usedExports: !DEV,
|
||||
},
|
||||
|
Reference in New Issue
Block a user