fragment-shader
Advanced tools
Comparing version 3.0.4 to 4.0.0
{ | ||
"name": "fragment-shader", | ||
"version": "3.0.4", | ||
"version": "4.0.0", | ||
"type": "module", | ||
@@ -5,0 +5,0 @@ "main": "src/index.js", |
@@ -0,23 +1,31 @@ | ||
import { EditorState } from '@codemirror/state'; | ||
import { ViewUpdate } from '@codemirror/view'; | ||
import { EditorView } from 'codemirror'; | ||
import { EditorState } from '@codemirror/state'; | ||
import { html } from '@codemirror/lang-html'; | ||
import { glsl, getBaseExtensions } from '../util/codemirror'; | ||
import '../style.scss'; | ||
import { type SupportedLanguage, type EditorConfig } from '../types/editor'; | ||
import { type Uniform } from '../types/uniforms'; | ||
export default class Editor { | ||
constructor(config = {}) { | ||
private language: SupportedLanguage; | ||
private doc: string; | ||
private uniforms: Uniform[]; | ||
private state: EditorState; | ||
private onUpdate: (a: string) => unknown; | ||
private parent: HTMLElement; | ||
private view: EditorView; | ||
constructor(config: EditorConfig = { parent: '#app' }) { | ||
const parent: HTMLElement = | ||
typeof config.parent === 'string' | ||
? document.querySelector(config.parent) | ||
: config.parent || (document.body as HTMLElement); | ||
typeof config.parent === 'string' ? document.querySelector(config.parent) || document.body : config.parent || document.body; | ||
const { doc, uniforms, onUpdate, language } = { | ||
doc: '', | ||
uniforms: [], | ||
onUpdate: (e: any) => e, | ||
language: 'GLSL', | ||
uniforms: [] as Uniform[], | ||
onUpdate: (s: string) => s, | ||
language: 'GLSL' as SupportedLanguage, | ||
...config, | ||
}; | ||
this.language = language; | ||
this.language = language as SupportedLanguage; | ||
this.onUpdate = onUpdate.bind(this); | ||
@@ -44,13 +52,12 @@ this.parent = parent; | ||
createState() { | ||
return EditorState.create({ | ||
doc: this.doc.trim(), | ||
extensions: [ | ||
...getBaseExtensions({ parent: this.parent }), | ||
this.getLanguage(), | ||
EditorView.updateListener.of(this.update.bind(this)), | ||
], | ||
}); | ||
const extensions = [ | ||
...getBaseExtensions({ parent: this.parent }), | ||
this.getLanguage(), | ||
EditorView.updateListener.of(this.update.bind(this)), | ||
]; | ||
return EditorState.create({ doc: this.doc.trim(), extensions }); | ||
} | ||
update(e) { | ||
update(e: ViewUpdate) { | ||
if (typeof this.onUpdate !== 'function') return; | ||
@@ -61,2 +68,6 @@ const { state, docChanged } = e; | ||
} | ||
destroy() { | ||
this.view.destroy(); | ||
} | ||
} |
import { Layer } from '../types/layers'; | ||
import Shader, { type ShaderProps } from './Shader'; | ||
import { Uniform } from '../types/uniforms'; | ||
import Shader from './Shader'; | ||
import { type Uniform } from '../types/uniforms'; | ||
import { type ShaderConfig } from '../types/shader'; | ||
import { log } from '../util/log'; | ||
import { getParentFromConfig } from '../util/dom'; | ||
@@ -9,12 +11,7 @@ export default class Renderer { | ||
private shader: Shader; | ||
public config: ShaderProps; | ||
public config: ShaderConfig; | ||
constructor(layers: Layer[], config: ShaderProps) { | ||
const parent: HTMLElement = | ||
typeof config.parent === 'string' | ||
? document.querySelector(config.parent) | ||
: config.parent || (document.body as HTMLElement); | ||
constructor(layers: Layer[], config: ShaderConfig) { | ||
this.config = config; | ||
this.config.parent = parent; | ||
this.config.parent = getParentFromConfig(config); | ||
this.layers = layers; | ||
@@ -44,3 +41,3 @@ this.shader = new Shader({ | ||
get fragmentShader() { | ||
let shader = /*glsl*/ ` | ||
let shader = ` | ||
${this.layerDeclarations} | ||
@@ -47,0 +44,0 @@ |
import { Shader, Editor } from './'; | ||
import { DEFAULT_FRAGMENT_SHADER } from './../constants/glsl-defaults'; | ||
import { type Uniform } from '../types/uniforms'; | ||
import { type ShaderConfig } from './Shader'; | ||
import '../style.scss'; | ||
import { type ShaderConfig } from '../types/shader'; | ||
import { EditorConfig } from '../types/editor'; | ||
import { getParentFromConfig } from '../util/dom'; | ||
export default class Sandbox { | ||
public shader: string; | ||
public uniforms: Uniform[]; | ||
public parent: HTMLElement | null; | ||
public instances: { | ||
private shader: string; | ||
private uniforms: Uniform[]; | ||
private parent: HTMLElement | null; | ||
private animate: boolean; | ||
private instances: { | ||
shader: Shader; | ||
editor: Editor; | ||
}; | ||
private raf: any; | ||
public animate: boolean; | ||
constructor(config: ShaderConfig = {}) { | ||
const parent: HTMLElement = ( | ||
typeof config.parent === 'string' | ||
? document.querySelector(config.parent) | ||
: config.parent || (document.body as HTMLElement) | ||
) as HTMLElement; | ||
this.parent = getParentFromConfig(config); | ||
@@ -32,6 +28,4 @@ const { shader, uniforms, animate } = { | ||
this.raf; | ||
this.shader = shader; | ||
this.uniforms = uniforms; | ||
this.parent = parent; | ||
this.animate = animate; | ||
@@ -48,6 +42,6 @@ | ||
const editorConfig = { | ||
const editorConfig: EditorConfig = { | ||
doc: this.shader, | ||
uniforms: this.uniforms, | ||
onUpdate: this.onUpdate.bind(this), | ||
onUpdate: this.onUpdate.bind(this) as any, | ||
}; | ||
@@ -61,9 +55,9 @@ | ||
onUpdate(fragmentShader: string) { | ||
onUpdate(fragmentShader: string): void { | ||
this.instances.shader.rebuild({ fragmentShader, uniforms: this.uniforms }); | ||
} | ||
set uniform(val) { | ||
set uniform(val: Uniform) { | ||
this.instances.shader.uniform = val; | ||
} | ||
} |
@@ -1,6 +0,4 @@ | ||
import { Plane, Uniform } from '.'; | ||
import { createCanvas, sizeCanvas, createResizeObserver } from '../util/dom'; | ||
import Plane from './Plane'; | ||
import { createCanvas, sizeCanvas, createResizeObserver, getParentFromConfig } from '../util/dom'; | ||
import { createWebGL2Context, createWebGL2Program } from '../util/webgl'; | ||
import { type Artboard } from '../types/artboard'; | ||
import { Uniform as UniformType } from '../types/uniforms'; | ||
import { clone } from '../util/clone'; | ||
@@ -17,24 +15,5 @@ import { log } from '../util/log'; | ||
} from './../constants/glsl-defaults'; | ||
import { type Uniform } from '../types/uniforms'; | ||
import { type ShaderConfig, type ShaderState } from '../types/shader'; | ||
export type ShaderConfig = { | ||
parent?: HTMLElement | string | undefined; | ||
dpr?: number; | ||
onResize?: ({ width, height, dpr }: Artboard) => unknown; | ||
debug?: boolean; | ||
vertexShader?: string; | ||
fragmentShader?: string; | ||
uniforms?: UniformType[]; | ||
animate?: boolean; | ||
shader?: string; | ||
shaderpad?: boolean; | ||
}; | ||
interface ShaderState extends Artboard { | ||
uniforms?: Record<string, Uniform>; | ||
volume: number; | ||
internalUniforms: UniformType[]; | ||
stream: number; | ||
animate: boolean; | ||
} | ||
export default class Shader { | ||
@@ -47,13 +26,12 @@ public config: ShaderConfig; | ||
private plane: Plane; | ||
private program: WebGLProgram; | ||
private cleanup: () => void; | ||
private animate: boolean | undefined; | ||
private shaderpad: boolean; | ||
constructor(config: ShaderConfig) { | ||
const parent: HTMLElement = | ||
typeof config.parent === 'string' | ||
? document.querySelector(config.parent) | ||
: config.parent || (document.body as HTMLElement); | ||
const parent = getParentFromConfig(config); | ||
const { canvas, observer, ctx } = this.initContext(parent); | ||
this.canvas = canvas; | ||
this.observer = observer; | ||
this.ctx = ctx; | ||
this.config = { | ||
@@ -64,13 +42,10 @@ debug: true, | ||
uniforms: DEFUALT_UNIFORMS, | ||
animate: false, | ||
animate: !!config.animate, | ||
shaderpad: false, | ||
...(config || {}), | ||
parent, | ||
parent: parent as HTMLElement, | ||
}; | ||
if (typeof config?.shader === 'string') { | ||
this.config.fragmentShader = config.shader as string; | ||
} | ||
this.state = { | ||
uniforms: {}, | ||
width: 0, | ||
@@ -82,16 +57,14 @@ height: 0, | ||
internalUniforms: clone(INTERNAL_UNIFORMS), | ||
animate: this.config.animate, | ||
animate: !!this.config.animate, | ||
}; | ||
const { canvas, observer, ctx } = this.initContext(); | ||
const { plane, uniforms, cleanup } = this.initProgram(); | ||
this.canvas = canvas; | ||
this.observer = observer; | ||
this.ctx = ctx; | ||
this.state.uniforms = uniforms; | ||
this.plane = plane; | ||
const { plane, program, uniforms, cleanup } = this.initProgram(); | ||
if (typeof config?.shader === 'string') { | ||
this.config.fragmentShader = config.shader as string; | ||
} | ||
this.plane = plane; | ||
this.program = program; | ||
this.state.uniforms = uniforms; | ||
this.cleanup = cleanup; | ||
@@ -104,3 +77,3 @@ this.tick = this.tick.bind(this); | ||
get dpr() { | ||
return this.config.dpr || window.devicePixelRatio; | ||
return this.config?.dpr || window.devicePixelRatio; | ||
} | ||
@@ -126,6 +99,3 @@ | ||
sizeCanvas(this.canvas, size); | ||
this.state.internalUniforms[0][2] = [ | ||
size.width * size.dpr, | ||
size.height * size.dpr, | ||
]; | ||
this.state.internalUniforms[0][2] = [size.width * size.dpr, size.height * size.dpr]; | ||
this.ctx.viewport(0, 0, size.width * size.dpr, size.height * size.dpr); | ||
@@ -138,3 +108,3 @@ this.config?.onResize?.(size); | ||
const shader = ` | ||
${this.config.vertexShader} | ||
${this.config?.vertexShader || ''} | ||
`; | ||
@@ -156,3 +126,3 @@ | ||
${ | ||
this.config.shaderpad | ||
this.config?.shaderpad | ||
? `void main () { | ||
@@ -177,11 +147,6 @@ vec2 uv = k_uv(gl_FragCoord); | ||
get uniforms() { | ||
const uniforms = [ | ||
...this.state.internalUniforms, | ||
...(this.config?.uniforms || []), | ||
]; | ||
return uniforms; | ||
return [...(this.state?.internalUniforms || []), ...(this.config?.uniforms || [])]; | ||
} | ||
set uniform(uniform: UniformType) { | ||
set uniform(uniform: Uniform) { | ||
this.config.uniforms?.forEach(u => { | ||
@@ -194,3 +159,3 @@ if (u[0] === uniform[0]) u[2] = uniform[2]; | ||
return this.uniforms.reduce((acc, [name, type]) => { | ||
acc += `uniform ${SHADER_TYPE_MAP[type]} ${name};\n`; | ||
acc += `uniform ${SHADER_TYPE_MAP[type as 0 | 1 | 2 | 3 | 4]} ${name};\n`; | ||
return acc; | ||
@@ -200,9 +165,6 @@ }, ''); | ||
initContext() { | ||
const canvas = createCanvas({ | ||
parent: this.config.parent, | ||
dpr: this.dpr, | ||
}); | ||
initContext(parent: HTMLElement) { | ||
const canvas = createCanvas({ parent, dpr: this.dpr }); | ||
const observer = createResizeObserver(this.config.parent, size => { | ||
const observer = createResizeObserver(parent, size => { | ||
this.size = size; | ||
@@ -217,3 +179,3 @@ }); | ||
initProgram() { | ||
const { plane, program, uniforms, cleanup } = createWebGL2Program({ | ||
const { plane, uniforms, cleanup } = createWebGL2Program({ | ||
ctx: this.ctx, | ||
@@ -227,3 +189,3 @@ width: this.ctx.canvas.width, | ||
return { plane, program, uniforms, cleanup }; | ||
return { plane, uniforms, cleanup }; | ||
} | ||
@@ -253,9 +215,3 @@ | ||
rebuild({ | ||
fragmentShader, | ||
uniforms, | ||
}: { | ||
fragmentShader: string; | ||
uniforms?: UniformType[]; | ||
}) { | ||
rebuild({ fragmentShader, uniforms }: { fragmentShader: string; uniforms?: Uniform[] }) { | ||
this.cleanup(); | ||
@@ -266,11 +222,5 @@ | ||
const { | ||
plane, | ||
program, | ||
uniforms: uniformState, | ||
cleanup, | ||
} = this.initProgram(); | ||
const { plane, uniforms: uniformState, cleanup } = this.initProgram(); | ||
this.plane = plane; | ||
this.program = program; | ||
this.state.uniforms = uniformState; | ||
@@ -277,0 +227,0 @@ this.cleanup = cleanup; |
@@ -9,7 +9,3 @@ import { Uniform as UniformValue } from './../types/uniforms'; | ||
constructor( | ||
ctx: WebGLRenderingContext, | ||
program: WebGLProgram, | ||
uniform: UniformValue | ||
) { | ||
constructor(ctx: WebGLRenderingContext, program: WebGLProgram, uniform: UniformValue) { | ||
this.ctx = ctx; | ||
@@ -26,3 +22,3 @@ this.suffix = WEBGL_TYPE_MAP[uniform[1]]; | ||
try { | ||
ctx?.[method].apply(ctx, args); | ||
(ctx?.[method] as any).apply(ctx, args); | ||
} catch (e) { | ||
@@ -29,0 +25,0 @@ console.log(e); |
import { Uniform } from '../types/uniforms'; | ||
import { | ||
k_hue, | ||
k_kale, | ||
k_orb, | ||
k_rainbow, | ||
k_rotate2d, | ||
k_swap, | ||
k_uv, | ||
} from './glsl-util'; | ||
import { k_hue, k_kale, k_orb, k_rainbow, k_rotate2d, k_swap, k_uv } from './glsl-util'; | ||
@@ -35,2 +27,3 @@ export const DEFAULT_VERTEX_SHADER = /*glsl*/ `attribute vec2 position; | ||
['volume', 0, 1], | ||
['zoom', 0, 1], | ||
] as const; | ||
@@ -54,8 +47,3 @@ | ||
export const DEFAULT_DEFS = [ | ||
'precision mediump float;', | ||
'', | ||
'#define PI 3.14159265359', | ||
'#define TWO_PI 2. * PI', | ||
].join('\n') as any; | ||
export const DEFAULT_DEFS = ['precision mediump float;', '', '#define PI 3.14159265359', '#define TWO_PI 2. * PI'].join('\n'); | ||
@@ -74,16 +62,10 @@ export const RAW_UTILS = { | ||
export const GLSL_UTILS = Object.keys(RAW_UTILS).reduce( | ||
(acc: string, key: string) => { | ||
acc += `${RAW_UTILS[key]}`; | ||
return acc; | ||
}, | ||
'\n' | ||
) as any; | ||
export const GLSL_UTILS = Object.keys(RAW_UTILS).reduce((acc: string, key: string) => { | ||
acc += `${RAW_UTILS[key]}`; | ||
return acc; | ||
}, '\n') as any; | ||
export const SHADER_UTILS = Object.keys(GLSL_UTILS).reduce( | ||
(acc: any, key: any) => { | ||
acc += `${(GLSL_UTILS as any)[key]}`; | ||
return acc; | ||
}, | ||
'\n' | ||
) as any; | ||
export const SHADER_UTILS = Object.keys(GLSL_UTILS).reduce((acc: any, key: any) => { | ||
acc += `${(GLSL_UTILS as any)[key]}`; | ||
return acc; | ||
}, '\n') as any; |
@@ -6,7 +6,2 @@ export type FloatUniform = [string, 0, number]; | ||
export type Vec4Uniform = [string, 4, [number, number, number, number]]; | ||
export type Uniform = | ||
| FloatUniform | ||
| BooleanUniform | ||
| Vec2Uniform | ||
| Vec3Uniform | ||
| Vec4Uniform; | ||
export type Uniform = FloatUniform | BooleanUniform | Vec2Uniform | Vec3Uniform | Vec4Uniform; |
import { clike } from '@codemirror/legacy-modes/mode/clike'; | ||
import { BLOCKS, MATH, KEYWORDS, TYPES } from './../constants/glsl-lang'; | ||
import { StreamLanguage } from '@codemirror/language'; | ||
import { autocompletion, closeBrackets } from '@codemirror/autocomplete'; | ||
import { bracketMatching, indentOnInput } from '@codemirror/language'; | ||
import { | ||
defaultKeymap, | ||
indentWithTab, | ||
history, | ||
historyKeymap, | ||
} from '@codemirror/commands'; | ||
import { | ||
keymap, | ||
lineNumbers, | ||
highlightActiveLine, | ||
highlightActiveLineGutter, | ||
dropCursor, | ||
tooltips, | ||
} from '@codemirror/view'; | ||
import { | ||
RAW_UTIL_KEYS, | ||
INTERNAL_UNIFORMS, | ||
DEFUALT_UNIFORMS, | ||
} from './../constants/glsl-defaults'; | ||
import { defaultKeymap, indentWithTab, history, historyKeymap } from '@codemirror/commands'; | ||
import { keymap, lineNumbers, highlightActiveLine, highlightActiveLineGutter, dropCursor, tooltips } from '@codemirror/view'; | ||
import * as themes from 'thememirror'; | ||
import { BLOCKS, MATH, KEYWORDS, TYPES } from './../constants/glsl-lang'; | ||
import { RAW_UTIL_KEYS, INTERNAL_UNIFORMS, DEFUALT_UNIFORMS } from './../constants/glsl-defaults'; | ||
import { Uniform } from '../types/uniforms'; | ||
function toKeyObject(arr: any): any { | ||
return arr.reduce( | ||
(acc: any, key: any) => ({ ...acc, [key]: true }), | ||
{} as any | ||
); | ||
return arr.reduce((acc: any, key: any) => ({ ...acc, [key]: true }), {} as any); | ||
} | ||
export function glsl(uniforms: any[]) { | ||
export function glsl(uniforms: Uniform[]) { | ||
return StreamLanguage.define( | ||
@@ -42,8 +24,3 @@ clike({ | ||
types: toKeyObject(TYPES), | ||
atoms: toKeyObject([ | ||
...RAW_UTIL_KEYS, | ||
...[...INTERNAL_UNIFORMS, ...DEFUALT_UNIFORMS, ...uniforms].map( | ||
u => u[0] | ||
), | ||
]), | ||
atoms: toKeyObject([...RAW_UTIL_KEYS, ...[...INTERNAL_UNIFORMS, ...DEFUALT_UNIFORMS, ...uniforms].map(u => u[0])]), | ||
}) | ||
@@ -53,3 +30,3 @@ ); | ||
export function getBaseExtensions({ parent }) { | ||
export function getBaseExtensions({ parent }: { parent: HTMLElement }) { | ||
return [ | ||
@@ -56,0 +33,0 @@ lineNumbers(), |
@@ -1,8 +0,5 @@ | ||
export function createCanvas({ | ||
parent, | ||
dpr, | ||
}: { | ||
parent: HTMLElement; | ||
dpr: number; | ||
}): HTMLCanvasElement { | ||
import { EditorConfig } from '../types/editor'; | ||
import { ShaderConfig } from '../types/shader'; | ||
export function createCanvas({ parent, dpr }: { parent: HTMLElement; dpr: number }): HTMLCanvasElement { | ||
const canvas = document.createElement('canvas'); | ||
@@ -15,6 +12,3 @@ const { width, height } = parent.getBoundingClientRect(); | ||
export function sizeCanvas( | ||
canvas: HTMLCanvasElement, | ||
{ width, height, dpr }: { width: number; height: number; dpr: number } | ||
) { | ||
export function sizeCanvas(canvas: HTMLCanvasElement, { width, height, dpr }: { width: number; height: number; dpr: number }) { | ||
canvas.width = width * dpr; | ||
@@ -26,6 +20,3 @@ canvas.height = height * dpr; | ||
export function createResizeObserver( | ||
element: HTMLElement, | ||
callback: ({ width, height }: { width: number; height: number }) => unknown | ||
) { | ||
export function createResizeObserver(element: HTMLElement, callback: ({ width, height }: { width: number; height: number }) => unknown) { | ||
const observer = new ResizeObserver(([entry]) => { | ||
@@ -40,1 +31,7 @@ const { width, height } = entry.contentRect; | ||
} | ||
export function getParentFromConfig(config: ShaderConfig | EditorConfig): HTMLElement { | ||
return typeof config.parent === 'string' | ||
? (document.querySelector(config.parent) as HTMLElement) | ||
: config.parent || (document.body as HTMLElement); | ||
} |
@@ -52,9 +52,6 @@ import { Plane, Uniform } from '../classes'; | ||
}) { | ||
return uniforms.reduce( | ||
(acc: Record<string, Uniform>, uniform: UniformValue) => { | ||
acc[uniform[0]] = new Uniform(ctx, program, uniform); | ||
return acc; | ||
}, | ||
{} | ||
); | ||
return uniforms.reduce((acc: Record<string, Uniform>, uniform: UniformValue) => { | ||
acc[uniform[0]] = new Uniform(ctx, program, uniform); | ||
return acc; | ||
}, {}); | ||
} | ||
@@ -99,10 +96,3 @@ | ||
ctx.enableVertexAttribArray(0); | ||
ctx.vertexAttribPointer( | ||
ctx.getAttribLocation(program, 'position'), | ||
2, | ||
ctx.FLOAT, | ||
false, | ||
0, | ||
0 | ||
); | ||
ctx.vertexAttribPointer(ctx.getAttribLocation(program, 'position'), 2, ctx.FLOAT, false, 0, 0); | ||
@@ -121,7 +111,7 @@ return { | ||
export function selectVariant(uniforms, i) { | ||
return clone(uniforms).map(u => { | ||
u[2] = u[1] === 0 ? u[2][i][0] : u[2][i]; | ||
export function selectVariant(uniforms: UniformValue[], i: number) { | ||
return clone(uniforms).map((u: UniformValue) => { | ||
u[2] = u[1] === 0 ? (u as any)[2][i][0] : (u as any)[2][i]; | ||
return u; | ||
}); | ||
} |
41159
26
1324