From c3e4c6e5a1a38b09eca51cd55ef1e37347b0231d Mon Sep 17 00:00:00 2001 From: FMS-Cat Date: Thu, 25 Mar 2021 02:57:55 +0900 Subject: [PATCH] feature: instanced condition --- src/entities/Condition.ts | 178 +++++++++++++++--- .../{condition-char.frag => condition.frag} | 10 +- src/shaders/{svg.vert => condition.vert} | 8 +- src/utils/createSVGTableTexture.ts | 43 +++-- 4 files changed, 186 insertions(+), 53 deletions(-) rename src/shaders/{condition-char.frag => condition.frag} (82%) rename src/shaders/{svg.vert => condition.vert} (80%) diff --git a/src/entities/Condition.ts b/src/entities/Condition.ts index 74de0d1..3b95a1e 100644 --- a/src/entities/Condition.ts +++ b/src/entities/Condition.ts @@ -1,39 +1,167 @@ import { Vector3 } from '@fms-cat/experimental'; -import { GLCatTexture } from '@fms-cat/glcat-ts'; +import { gl, glCat } from '../globals/canvas'; import { Entity } from '../heck/Entity'; +import { InstancedGeometry } from '../heck/InstancedGeometry'; import { createSVGTableTexture } from '../utils/createSVGTableTexture'; -import { ConditionChar } from './ConditionChar'; +import conditionVert from '../shaders/condition.vert'; +import conditionFrag from '../shaders/condition.frag'; +import { Material } from '../heck/Material'; +import { dummyRenderTargetFourDrawBuffers, dummyRenderTargetOneDrawBuffers } from '../globals/dummyRenderTarget'; +import { Mesh } from '../heck/components/Mesh'; +import { auto } from '../globals/automaton'; + +const POINTS_MAX = 256; export class Condition extends Entity { public constructor() { super(); this.transform.scale = new Vector3( [ 0.05, 0.05, 0.05 ] ); - const pathC = createSVGTableTexture( 'M5,5l-9,0l-1,-1l0,-8l1,-1l9,0l0,2l-8,0l0,6l8,0Z' ); - const pathO = createSVGTableTexture( 'M5,4l0,-8l-1,-1l-8,0l-1,1l0,8l1,1l8,0Z' ); - const pathOi = createSVGTableTexture( 'M3,3l0,-6l-6,0l0,6Z' ); - const pathN = createSVGTableTexture( 'M5,4l0,-9l-2,0l0,8l-6,0l0,-8l-2,0l0,9l1,1l8,0Z' ); - const pathD = createSVGTableTexture( 'M5,4l0,-8l-1,-1l-9,0l0,10l9,0Z' ); - const pathI = createSVGTableTexture( 'M1,5l0,-10l-2,0l0,10Z' ); - const pathT = createSVGTableTexture( 'M5,5l0,-2l-4,0l0,-8l-2,0l0,8l-4,0l0,2Z' ); + // -- paths ------------------------------------------------------------------------------------ + const texture = createSVGTableTexture( [ + 'M5,5l-9,0l-1,-1l0,-8l1,-1l9,0l0,2l-8,0l0,6l8,0Z', + 'M5,4l0,-8l-1,-1l-8,0l-1,1l0,8l1,1l8,0Z', + 'M3,3l0,-6l-6,0l0,6Z', + 'M5,4l0,-9l-2,0l0,8l-6,0l0,-8l-2,0l0,9l1,1l8,0Z', + 'M5,4l0,-8l-1,-1l-9,0l0,10l9,0Z', + 'M1,5l0,-10l-2,0l0,10Z', + 'M5,5l0,-2l-4,0l0,-8l-2,0l0,8l-4,0l0,2Z', + ] ); - const tableAndPos: [ GLCatTexture, number ][] = [ - [ pathC, -20 ], - [ pathO, -14 ], - [ pathOi, -14 ], - [ pathN, -8 ], - [ pathD, -2 ], - [ pathOi, -2 ], - [ pathI, 2 ], - [ pathT, 6 ], - [ pathI, 10 ], - [ pathO, 14 ], - [ pathOi, 14 ], - [ pathN, 20 ], + const tablePos: number[] = [ + 0, -40, + 1, -28, + 2, -28, + 3, -16, + 4, -4, + 2, -4, + 5, 4, + 6, 12, + 5, 20, + 1, 28, + 2, 28, + 3, 40, ]; - tableAndPos.forEach( ( [ table, pos ], i ) => { - const svgEntity = new ConditionChar( { table, pos, i } ); - this.children.push( svgEntity ); + + // -- 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 ) ); + + const arrayIter: number[] = []; + + for ( let i = 0; i < 16; i ++ ) { + for ( let j = 0; j < 12; j ++ ) { + arrayIter.push( + ( tablePos[ j * 2 + 0 ] + 0.5 ) / 7.0, + tablePos[ j * 2 + 1 ], + i, + j, + ); + } + } + + const bufferIter = glCat.createBuffer(); + bufferIter.setVertexbuffer( new Float32Array( arrayIter ) ); + + // -- create geometry -------------------------------------------------------------------------- + const geometry = new InstancedGeometry(); + + geometry.vao.bindVertexbuffer( bufferPos, 0, 2 ); + geometry.vao.bindIndexbuffer( bufferInd ); + geometry.vao.bindVertexbuffer( bufferIter, 1, 4, 1 ); + + geometry.count = 18 * POINTS_MAX; + geometry.mode = gl.TRIANGLES; + geometry.indexType = gl.UNSIGNED_SHORT; + geometry.primcount = 12 * 16; + + // -- create materials ------------------------------------------------------------------------- + const materials = { + forward: new Material( + conditionVert, + conditionFrag, + { + defines: { 'FORWARD': 'true' }, + initOptions: { geometry, target: dummyRenderTargetOneDrawBuffers }, + }, + ), + deferred: new Material( + conditionVert, + conditionFrag, + { + defines: { 'DEFERRED': 'true' }, + initOptions: { geometry, target: dummyRenderTargetFourDrawBuffers }, + }, + ), + shadow: new Material( + conditionVert, + conditionFrag, + { + defines: { 'SHADOW': 'true' }, + initOptions: { geometry, target: dummyRenderTargetOneDrawBuffers }, + }, + ), + }; + + for ( const material of Object.values( materials ) ) { + material.addUniformTexture( 'samplerSvg', texture ); + + auto( 'Condition/phaseOffset', ( { value } ) => { + material.addUniform( 'phaseOffset', '1f', value ); + } ); + + auto( 'Condition/phaseWidth', ( { value } ) => { + material.addUniform( 'phaseWidth', '1f', value ); + } ); + } + + if ( process.env.DEV ) { + if ( module.hot ) { + module.hot.accept( + [ + '../shaders/condition.vert', + '../shaders/condition.frag', + ], + () => { + materials.forward.replaceShader( conditionVert, conditionFrag ); + materials.deferred.replaceShader( conditionVert, conditionFrag ); + materials.shadow.replaceShader( conditionVert, conditionFrag ); + }, + ); + } + } + + // -- create meshes ---------------------------------------------------------------------------- + const mesh = new Mesh( { + geometry, + materials, + name: process.env.DEV && `Condition/mesh`, } ); + this.components.push( mesh ); } } diff --git a/src/shaders/condition-char.frag b/src/shaders/condition.frag similarity index 82% rename from src/shaders/condition-char.frag rename to src/shaders/condition.frag index dee2c3d..af50b32 100644 --- a/src/shaders/condition-char.frag +++ b/src/shaders/condition.frag @@ -10,6 +10,7 @@ const int MTL_UNLIT = 1; in float vPhase; in vec3 vNormal; in vec4 vPosition; +in vec4 vHuh; #ifdef FORWARD out vec4 fragColor; @@ -30,22 +31,23 @@ in vec4 vPosition; #endif uniform float time; -uniform float svgi; uniform float phaseOffset; uniform float phaseWidth; void main() { - float phase = fract( 2.0 * vPhase + 0.1 * time + 0.1 * svgi + phaseOffset ); + float phase = fract( 2.0 * vPhase + 0.01 * vHuh.z + 0.1 * time + 0.1 * vHuh.y + phaseOffset ); if ( phase > phaseWidth ) { discard; } + vec3 color = 2.0 * vec3( exp( -0.2 * vHuh.z ) ); + #ifdef FORWARD - fragColor = vec4( 1.0 ); + fragColor = vec4( color, 1.0 ); #endif #ifdef DEFERRED fragPosition = vPosition; fragNormal = vec4( vNormal, 1.0 ); - fragColor = vec4( 1.0 ); + fragColor = vec4( color, 1.0 ); fragWTF = vec4( vec3( 0.0, 0.0, 0.0 ), MTL_UNLIT ); #endif diff --git a/src/shaders/svg.vert b/src/shaders/condition.vert similarity index 80% rename from src/shaders/svg.vert rename to src/shaders/condition.vert index ff1b09f..91a6166 100644 --- a/src/shaders/svg.vert +++ b/src/shaders/condition.vert @@ -3,10 +3,12 @@ const float TAU = 6.283185307; layout (location = 0) in vec2 what; +layout (location = 1) in vec4 huh; out float vPhase; out vec3 vNormal; out vec4 vPosition; +out vec4 vHuh; uniform float time; uniform vec2 resolution; @@ -20,9 +22,11 @@ uniform sampler2D samplerSvg; void main() { vPhase = what.x; - vec4 tex = texture( samplerSvg, vec2( what.x, 0.5 ) ); + vHuh = huh; - vPosition = vec4( tex.xy, 0.0, 1.0 ); + vec4 tex = texture( samplerSvg, vec2( what.x, huh.x ) ); + + vPosition = vec4( tex.xy + vec2( huh.y, 0.0 ), 6.0 - 12.0 * huh.z, 1.0 ); mat3 basis = orthBasis( vec3( tex.zw, 0.0 ) ); float theta = what.y / 3.0 * TAU; diff --git a/src/utils/createSVGTableTexture.ts b/src/utils/createSVGTableTexture.ts index 088a9ac..a2219c6 100644 --- a/src/utils/createSVGTableTexture.ts +++ b/src/utils/createSVGTableTexture.ts @@ -1,39 +1,38 @@ import { GLCatTexture } from '@fms-cat/glcat-ts'; import { gl, glCat } from '../globals/canvas'; -export function createSVGTableTexture( pathStr: string, width = 1024 ): GLCatTexture { - const svgNamespaceURI = 'http://www.w3.org/2000/svg'; - const path = document.createElementNS( svgNamespaceURI, 'path' ); - path.setAttribute( 'd', pathStr ); +export function createSVGTableTexture( pathStrs: string[], width = 1024 ): GLCatTexture { + const table: number[] = []; // x, y, dx, dy - const pathLength = path.getTotalLength(); - const eps = 0.25 / width * pathLength; + pathStrs.map( ( pathStr ) => { + const svgNamespaceURI = 'http://www.w3.org/2000/svg'; + const path = document.createElementNS( svgNamespaceURI, 'path' ); + path.setAttribute( 'd', pathStr ); - const table = new Float32Array( 4 * width ); // x, y, dx, dy + const pathLength = path.getTotalLength(); + const eps = 0.25 / width * pathLength; - for ( let i = 0; i < width; i ++ ) { - const phase = ( ( i + 0.5 ) / width ) * pathLength; + for ( let i = 0; i < width; i ++ ) { + const phase = ( ( i + 0.5 ) / width ) * pathLength; - const point = path.getPointAtLength( phase ); + const point = path.getPointAtLength( phase ); - const pointdn = path.getPointAtLength( phase - eps ); - const pointdp = path.getPointAtLength( phase + eps ); + const pointdn = path.getPointAtLength( phase - eps ); + const pointdp = path.getPointAtLength( phase + eps ); - const dx = pointdp.x - pointdn.x; - const dy = pointdp.y - pointdn.y; - const dl = Math.sqrt( dx * dx + dy * dy ); + const dx = pointdp.x - pointdn.x; + const dy = pointdp.y - pointdn.y; + const dl = Math.sqrt( dx * dx + dy * dy ); - table[ 4 * i + 0 ] = point.x; - table[ 4 * i + 1 ] = point.y; - table[ 4 * i + 2 ] = dx / dl; - table[ 4 * i + 3 ] = dy / dl; - } + table.push( point.x, point.y, dx / dl, dy / dl ); + } + } ); const texture = glCat.createTexture(); texture.setTextureFromArray( width, - 1, - table, + pathStrs.length, + new Float32Array( table ), { internalformat: gl.RGBA32F, format: gl.RGBA, type: gl.FLOAT }, ); texture.textureWrap( gl.REPEAT );