mirror of
https://github.com/FMS-Cat/condition.git
synced 2025-08-14 09:43:58 +02:00
feature: SufferTexts
This commit is contained in:
144
src/entities/SufferTexts.ts
Normal file
144
src/entities/SufferTexts.ts
Normal file
@@ -0,0 +1,144 @@
|
|||||||
|
import { Entity } from '../heck/Entity';
|
||||||
|
import { GPUParticles } from './GPUParticles';
|
||||||
|
import { Geometry } from '../heck/Geometry';
|
||||||
|
import { InstancedGeometry } from '../heck/InstancedGeometry';
|
||||||
|
import { Material, MaterialMap } from '../heck/Material';
|
||||||
|
import quadVert from '../shaders/quad.vert';
|
||||||
|
import sufferTextsComputeFrag from '../shaders/suffer-texts-compute.frag';
|
||||||
|
import sufferTextsRenderFrag from '../shaders/suffer-texts-render.frag';
|
||||||
|
import sufferTextsRenderVert from '../shaders/suffer-texts-render.vert';
|
||||||
|
import { TRIANGLE_STRIP_QUAD } from '@fms-cat/experimental';
|
||||||
|
import { gl, glCat } from '../globals/canvas';
|
||||||
|
import { randomTextureStatic } from '../globals/randomTexture';
|
||||||
|
import { tinyCharTexture } from '../globals/tinyCharTexture';
|
||||||
|
import { Lambda } from '../heck/components/Lambda';
|
||||||
|
import { auto } from '../globals/automaton';
|
||||||
|
import { sufferList } from '../sufferList';
|
||||||
|
|
||||||
|
const PARTICLES = 256;
|
||||||
|
|
||||||
|
export class SufferTexts {
|
||||||
|
public get entity(): Entity {
|
||||||
|
return this.gpuParticles.entity;
|
||||||
|
}
|
||||||
|
|
||||||
|
public gpuParticles: GPUParticles;
|
||||||
|
public queue: number[][];
|
||||||
|
public particleIndex: number;
|
||||||
|
|
||||||
|
public constructor() {
|
||||||
|
this.gpuParticles = new GPUParticles( {
|
||||||
|
materialCompute: this.__createMaterialCompute(),
|
||||||
|
geometryRender: this.__createGeometryRender(),
|
||||||
|
materialsRender: this.__createMaterialsRender(),
|
||||||
|
computeWidth: PARTICLES,
|
||||||
|
computeHeight: 1,
|
||||||
|
computeNumBuffers: 1,
|
||||||
|
namePrefix: process.env.DEV && 'SufferTexts',
|
||||||
|
} );
|
||||||
|
|
||||||
|
this.queue = [];
|
||||||
|
|
||||||
|
this.particleIndex = 0;
|
||||||
|
|
||||||
|
auto( 'sufferText/push', ( { value } ) => {
|
||||||
|
const suffer = sufferList[ Math.floor( value ) ].split( '\n' );
|
||||||
|
suffer.forEach( ( line, iLine ) => {
|
||||||
|
const chars = [ ...line ];
|
||||||
|
this.queue.push( ...chars.map( ( char, iChar ) => [
|
||||||
|
iChar - 0.5 * ( chars.length - 1 ),
|
||||||
|
iLine - 0.5 * ( suffer.length - 1 ),
|
||||||
|
char.charCodeAt( 0 ),
|
||||||
|
] ) );
|
||||||
|
} );
|
||||||
|
} );
|
||||||
|
|
||||||
|
this.entity.components.push( new Lambda( {
|
||||||
|
onUpdate: () => {
|
||||||
|
const val = this.queue.shift();
|
||||||
|
if ( val != null ) {
|
||||||
|
const x = ( this.particleIndex + 0.5 ) / PARTICLES;
|
||||||
|
this.gpuParticles.materialCompute.addUniform( 'logInit', '4f', ...val, x );
|
||||||
|
this.particleIndex = ( this.particleIndex + 1 ) % PARTICLES;
|
||||||
|
} else {
|
||||||
|
this.gpuParticles.materialCompute.addUniform( 'logInit', '4f', 0.0, 0.0, 0.0, 0.0 );
|
||||||
|
}
|
||||||
|
},
|
||||||
|
} ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
private __createMaterialCompute(): Material {
|
||||||
|
const material = new Material( quadVert, sufferTextsComputeFrag );
|
||||||
|
if ( process.env.DEV ) {
|
||||||
|
if ( module.hot ) {
|
||||||
|
module.hot.accept( '../shaders/suffer-texts-compute.frag', () => {
|
||||||
|
material.replaceShader( quadVert, sufferTextsComputeFrag );
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return material;
|
||||||
|
}
|
||||||
|
|
||||||
|
private __createGeometryRender(): Geometry {
|
||||||
|
const geometry = new InstancedGeometry();
|
||||||
|
|
||||||
|
const bufferP = glCat.createBuffer();
|
||||||
|
bufferP.setVertexbuffer( new Float32Array( TRIANGLE_STRIP_QUAD ) );
|
||||||
|
|
||||||
|
geometry.addAttribute( 'position', {
|
||||||
|
buffer: bufferP,
|
||||||
|
size: 2,
|
||||||
|
type: gl.FLOAT,
|
||||||
|
} );
|
||||||
|
|
||||||
|
const bufferComputeUV = glCat.createBuffer();
|
||||||
|
bufferComputeUV.setVertexbuffer( ( () => {
|
||||||
|
const ret = new Float32Array( PARTICLES );
|
||||||
|
for ( let ix = 0; ix < PARTICLES; ix ++ ) {
|
||||||
|
const s = ( ix + 0.5 ) / PARTICLES;
|
||||||
|
ret[ ix ] = s;
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
} )() );
|
||||||
|
|
||||||
|
geometry.addAttribute( 'computeX', {
|
||||||
|
buffer: bufferComputeUV,
|
||||||
|
size: 1,
|
||||||
|
divisor: 1,
|
||||||
|
type: gl.FLOAT
|
||||||
|
} );
|
||||||
|
|
||||||
|
geometry.count = 4;
|
||||||
|
geometry.mode = gl.TRIANGLE_STRIP;
|
||||||
|
geometry.primcount = PARTICLES;
|
||||||
|
|
||||||
|
return geometry;
|
||||||
|
}
|
||||||
|
|
||||||
|
private __createMaterialsRender(): MaterialMap<'deferred'> {
|
||||||
|
const deferred = new Material(
|
||||||
|
sufferTextsRenderVert,
|
||||||
|
sufferTextsRenderFrag,
|
||||||
|
{ defines: { 'DEFERRED': 'true' } },
|
||||||
|
);
|
||||||
|
deferred.addUniformTexture( 'samplerRandomStatic', randomTextureStatic.texture );
|
||||||
|
deferred.addUniformTexture( 'samplerTinyChar', tinyCharTexture );
|
||||||
|
|
||||||
|
if ( process.env.DEV ) {
|
||||||
|
if ( module.hot ) {
|
||||||
|
module.hot.accept(
|
||||||
|
[
|
||||||
|
'../shaders/suffer-texts-render.vert',
|
||||||
|
'../shaders/suffer-texts-render.frag',
|
||||||
|
],
|
||||||
|
() => {
|
||||||
|
deferred.replaceShader( sufferTextsRenderVert, sufferTextsRenderFrag );
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return { deferred };
|
||||||
|
}
|
||||||
|
}
|
11
src/globals/tinyCharTexture.ts
Normal file
11
src/globals/tinyCharTexture.ts
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
import { gl, glCat } from './canvas';
|
||||||
|
import char5x5Png from '../images/char5x5.png';
|
||||||
|
|
||||||
|
export const tinyCharTexture = glCat.createTexture();
|
||||||
|
|
||||||
|
const image = new Image();
|
||||||
|
image.onload = () => {
|
||||||
|
tinyCharTexture.setTexture( image );
|
||||||
|
tinyCharTexture.textureFilter( gl.NEAREST );
|
||||||
|
};
|
||||||
|
image.src = char5x5Png;
|
BIN
src/images/char5x5.png
Normal file
BIN
src/images/char5x5.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 895 B |
4
src/png.d.ts
vendored
Normal file
4
src/png.d.ts
vendored
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
declare module '*.png' {
|
||||||
|
const uri: string;
|
||||||
|
export default uri;
|
||||||
|
}
|
11
src/scene.ts
11
src/scene.ts
@@ -14,6 +14,7 @@ import { Raymarcher } from './entities/Raymarcher';
|
|||||||
import { Rings } from './entities/Rings';
|
import { Rings } from './entities/Rings';
|
||||||
import { RTInspector } from './entities/RTInspector';
|
import { RTInspector } from './entities/RTInspector';
|
||||||
import { SphereParticles } from './entities/SphereParticles';
|
import { SphereParticles } from './entities/SphereParticles';
|
||||||
|
import { SufferTexts } from './entities/SufferTexts';
|
||||||
import { Trails } from './entities/Trails';
|
import { Trails } from './entities/Trails';
|
||||||
import { auto, automaton } from './globals/automaton';
|
import { auto, automaton } from './globals/automaton';
|
||||||
import { music } from './globals/music';
|
import { music } from './globals/music';
|
||||||
@@ -133,6 +134,16 @@ if ( process.env.DEV && module.hot ) {
|
|||||||
} );
|
} );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const replacerSufferTexts = new EntityReplacer(
|
||||||
|
() => new SufferTexts(),
|
||||||
|
'SufferTexts',
|
||||||
|
);
|
||||||
|
if ( process.env.DEV && module.hot ) {
|
||||||
|
module.hot.accept( './entities/SufferTexts', () => {
|
||||||
|
replacerSufferTexts.replace();
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
|
||||||
const replacerRaymarcher = new EntityReplacer( () => new Raymarcher(), 'Raymarcher' );
|
const replacerRaymarcher = new EntityReplacer( () => new Raymarcher(), 'Raymarcher' );
|
||||||
if ( process.env.DEV && module.hot ) {
|
if ( process.env.DEV && module.hot ) {
|
||||||
module.hot.accept( './entities/Raymarcher', () => {
|
module.hot.accept( './entities/Raymarcher', () => {
|
||||||
|
24
src/shaders/suffer-texts-compute.frag
Normal file
24
src/shaders/suffer-texts-compute.frag
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
#version 300 es
|
||||||
|
|
||||||
|
precision highp float;
|
||||||
|
|
||||||
|
out vec4 fragCompute0;
|
||||||
|
|
||||||
|
uniform float time;
|
||||||
|
uniform float deltaTime;
|
||||||
|
uniform vec2 resolution;
|
||||||
|
uniform vec4 logInit;
|
||||||
|
uniform sampler2D samplerCompute0;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
vec2 uv = gl_FragCoord.xy / resolution;
|
||||||
|
|
||||||
|
fragCompute0 = texture( samplerCompute0, uv );
|
||||||
|
|
||||||
|
if ( logInit.w == uv.x ) {
|
||||||
|
fragCompute0 = logInit;
|
||||||
|
fragCompute0.w = 0.0; // life
|
||||||
|
}
|
||||||
|
|
||||||
|
fragCompute0.w += deltaTime; // life
|
||||||
|
}
|
40
src/shaders/suffer-texts-render.frag
Normal file
40
src/shaders/suffer-texts-render.frag
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
#version 300 es
|
||||||
|
|
||||||
|
precision highp float;
|
||||||
|
|
||||||
|
const int MTL_UNLIT = 1;
|
||||||
|
|
||||||
|
// == varings / uniforms ===========================================================================
|
||||||
|
in float vLife;
|
||||||
|
in float vMode;
|
||||||
|
in vec2 vUv;
|
||||||
|
in vec2 vSize;
|
||||||
|
in vec3 vNormal;
|
||||||
|
in vec4 vPosition;
|
||||||
|
in vec4 vDice;
|
||||||
|
|
||||||
|
uniform float time;
|
||||||
|
uniform sampler2D samplerRandomStatic;
|
||||||
|
uniform sampler2D samplerTinyChar;
|
||||||
|
|
||||||
|
#ifdef DEFERRED
|
||||||
|
layout (location = 0) out vec4 fragPosition;
|
||||||
|
layout (location = 1) out vec4 fragNormal;
|
||||||
|
layout (location = 2) out vec4 fragColor;
|
||||||
|
layout (location = 3) out vec4 fragWTF;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// == main procedure ===============================================================================
|
||||||
|
void main() {
|
||||||
|
if ( vLife > 1.0 ) { discard; }
|
||||||
|
|
||||||
|
float tex = texture( samplerTinyChar, vUv ).x;
|
||||||
|
if ( tex < 0.5 ) { discard; }
|
||||||
|
|
||||||
|
#ifdef DEFERRED
|
||||||
|
fragPosition = vPosition;
|
||||||
|
fragNormal = vec4( vNormal, 1.0 );
|
||||||
|
fragColor = vec4( 1.0 );
|
||||||
|
fragWTF = vec4( vec3( 0.0 ), MTL_UNLIT );
|
||||||
|
#endif
|
||||||
|
}
|
66
src/shaders/suffer-texts-render.vert
Normal file
66
src/shaders/suffer-texts-render.vert
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
#version 300 es
|
||||||
|
|
||||||
|
#define fs(i) (fract(sin((i)*114.514)*1919.810))
|
||||||
|
#define saturate(i) clamp(i,0.,1.)
|
||||||
|
#define lofi(i,m) (floor((i)/(m))*(m))
|
||||||
|
#define lofir(i,m) (floor((i+0.5)/(m))*(m))
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
in float computeX;
|
||||||
|
in vec2 position;
|
||||||
|
|
||||||
|
out float vLife;
|
||||||
|
out vec2 vUv;
|
||||||
|
out vec2 vSize;
|
||||||
|
out vec3 vNormal;
|
||||||
|
out vec4 vPosition;
|
||||||
|
|
||||||
|
uniform vec2 resolution;
|
||||||
|
uniform vec2 resolutionCompute;
|
||||||
|
uniform mat4 projectionMatrix;
|
||||||
|
uniform mat4 viewMatrix;
|
||||||
|
uniform mat4 modelMatrix;
|
||||||
|
uniform mat4 normalMatrix;
|
||||||
|
uniform sampler2D samplerCompute0;
|
||||||
|
uniform sampler2D samplerRandomStatic;
|
||||||
|
|
||||||
|
// == utils ========================================================================================
|
||||||
|
vec2 yflip( vec2 uv ) {
|
||||||
|
return vec2( 0.0, 1.0 ) + vec2( 1.0, -1.0 ) * uv;
|
||||||
|
}
|
||||||
|
|
||||||
|
// == main procedure ===============================================================================
|
||||||
|
void main() {
|
||||||
|
vec2 computeUV = vec2( computeX, 0.5 );
|
||||||
|
vec4 tex0 = texture( samplerCompute0, computeUV );
|
||||||
|
|
||||||
|
// == assign varying variables ===================================================================
|
||||||
|
vLife = tex0.w;
|
||||||
|
|
||||||
|
float char = tex0.z;
|
||||||
|
char += 64.0 * ( min( vLife, 0.3 ) + max( vLife, 0.7 ) - 1.0 );
|
||||||
|
|
||||||
|
vUv = yflip( 0.5 + 0.499 * position );
|
||||||
|
vUv = ( vUv + floor( mod( vec2( char / vec2( 1.0, 16.0 ) ), 16.0 ) ) ) / 16.0;
|
||||||
|
|
||||||
|
vNormal = normalize( ( normalMatrix * vec4( 0.0, 0.0, 1.0, 1.0 ) ).xyz );
|
||||||
|
|
||||||
|
// == compute size ===============================================================================
|
||||||
|
float scale = 0.0625;
|
||||||
|
|
||||||
|
vPosition = vec4( scale * 1.2 * tex0.xy, 0.0, 1.0 );
|
||||||
|
vPosition.y = -vPosition.y;
|
||||||
|
|
||||||
|
vec2 shape = position * scale * 0.5;
|
||||||
|
|
||||||
|
vPosition.xy += shape;
|
||||||
|
|
||||||
|
// == send the vertex position ===================================================================
|
||||||
|
vPosition = vPosition;
|
||||||
|
vec4 outPos = vPosition;
|
||||||
|
outPos.x *= resolution.y / resolution.x;
|
||||||
|
gl_Position = outPos;
|
||||||
|
|
||||||
|
vPosition.w = outPos.z / outPos.w;
|
||||||
|
}
|
18
src/sufferList.ts
Normal file
18
src/sufferList.ts
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
export const sufferList = [
|
||||||
|
'#DEFINE DISGRACE 1',
|
||||||
|
'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?',
|
||||||
|
'SVG.GETPOINTATLENGTH IS CHEATING',
|
||||||
|
'COPY\'N\'PASTE ENGINEER',
|
||||||
|
'ENGLISH SUCKS',
|
||||||
|
'60FPS OR DIE',
|
||||||
|
'END MY SUFFER',
|
||||||
|
];
|
@@ -53,7 +53,7 @@ module.exports = ( env, argv ) => {
|
|||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
test: /\.opus$/,
|
test: /\.(opus|png)$/,
|
||||||
type: 'asset/inline',
|
type: 'asset/inline',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
Reference in New Issue
Block a user