UNPKG

lume

Version:

Build next-level interactive web applications.

82 lines (70 loc) 2.48 kB
import 'element-behaviors' import {untrack} from 'solid-js' import {attribute, stringAttribute} from '@lume/element' import {ShaderMaterial} from 'three/src/materials/ShaderMaterial.js' // @ts-ignore, no type def import default_vertex from 'three/src/renderers/shaders/ShaderChunk/default_vertex.glsl.js' // @ts-ignore, no type def import default_fragment from 'three/src/renderers/shaders/ShaderChunk/default_fragment.glsl.js' import {behavior} from '../../Behavior.js' import {receiver} from '../../PropReceiver.js' import {MaterialBehavior, type MaterialBehaviorAttributes} from './MaterialBehavior.js' export type ShaderMaterialBehaviorAttributes = | MaterialBehaviorAttributes | 'uniforms' | 'vertexShader' | 'fragmentShader' export @behavior class ShaderMaterialBehavior extends MaterialBehavior { // TODO: Perhaps instead of accepting string objects for HTML attributes, // we can create specific uniform-foo attributes for each uniform, and have // specific data handling and type definitions for each one. This would // make it easier to animate particular uniforms instead of replacing the // whole object each time. @attribute @receiver get uniforms(): Record<string, any> { return this.#uniforms } @attribute set uniforms(u: string | Record<string, any> | null) { if (!u) { this.#uniforms = {} return } if (typeof u === 'string') { try { this.#uniforms = JSON.parse(u) } catch (e) { console.warn('Unparsable uniform value:', u) } } else { this.#uniforms = u } } #uniforms: Record<string, any> = {} @stringAttribute @receiver vertexShader = default_vertex @stringAttribute @receiver fragmentShader = default_fragment override _createComponent() { // untrack, we subsequently update the properties using an effect. return untrack(() => { return new ShaderMaterial({ uniforms: this.uniforms, vertexShader: this.vertexShader, fragmentShader: this.fragmentShader, }) }) } override connectedCallback() { this.createEffect(() => { const mat = this.meshComponent if (!mat) return mat.uniforms = this.uniforms mat.vertexShader = this.vertexShader || default_vertex mat.fragmentShader = this.fragmentShader || default_fragment mat.needsUpdate = true this.element.needsUpdate() }) super.connectedCallback() } } if (globalThis.window?.document && !elementBehaviors.has('shader-material')) elementBehaviors.define('shader-material', ShaderMaterialBehavior)