UNPKG

threepipe

Version:

A modern 3D viewer framework built on top of three.js, written in TypeScript, designed to make creating high-quality, modular, and extensible 3D experiences on the web simple and enjoyable.

325 lines 15.3 kB
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; return c > 3 && r && Object.defineProperty(target, key, r), r; }; var NoiseBumpMaterialPlugin_1; import { Vector2, Vector3, Vector4 } from 'three'; import { AViewerPluginSync } from '../../viewer'; import { uiFolderContainer, uiToggle } from 'uiconfig.js'; import { serialize } from 'ts-browser-helpers'; import { updateMaterialDefines } from '../../materials'; import { shaderReplaceString, ThreeSerialization } from '../../utils'; import NoiseBumpMaterialPluginPars from './shaders/NoiseBumpMaterialPlugin.pars.glsl'; import NoiseBumpMaterialPluginPatch from './shaders/NoiseBumpMaterialPlugin.patch.glsl'; /** * NoiseBump Materials Extension * Adds a material extension to PhysicalMaterial to add support for sparkle bump / noise bump by creating procedural bump map from noise to simulate sparkle flakes. * It uses voronoise function from blender along with several additions to generate the noise for the generation. * It also adds a UI to the material to edit the settings. * It uses WEBGI_materials_noise_bump glTF extension to save the settings in glTF files. * @category Plugins */ let NoiseBumpMaterialPlugin = NoiseBumpMaterialPlugin_1 = class NoiseBumpMaterialPlugin extends AViewerPluginSync { static AddNoiseBumpMaterial(material, params) { const ud = material?.userData; if (!ud) return false; if (!ud._noiseBumpMat) { ud._noiseBumpMat = {}; } const tf = ud._noiseBumpMat; tf.hasBump = true; if (tf.bumpNoiseParams === undefined) tf.bumpNoiseParams = new Vector2(0.5, 0.5); if (tf.bumpScale === undefined) tf.bumpScale = 0.05; if (tf.flakeScale === undefined) tf.flakeScale = 0.05; if (tf.flakeClamp === undefined) tf.flakeClamp = 1; if (tf.flakeRadius === undefined) tf.flakeRadius = 0.3; if (tf.useColorFlakes === undefined) tf.useColorFlakes = false; if (tf.flakeParams === undefined) tf.flakeParams = new Vector4(0, 1, 3, 0); if (tf.flakeFallOffParams === undefined) tf.flakeFallOffParams = new Vector3(0, 1, 0); params && Object.assign(tf, params); if (material.setDirty) material.setDirty(); return true; } constructor() { super(); this.enabled = true; // private _defines: any = { // } this._uniforms = { noiseBumpParams: { value: new Vector2() }, // u scale, v scale, noiseBumpScale: { value: 0.05 }, noiseBumpFlakeScale: { value: 1000.0 }, noiseFlakeClamp: { value: 1.0 }, noiseFlakeRadius: { value: 0.5 }, flakeParams: { value: new Vector4(0, 1, 3, 0) }, flakeFallOffParams: { value: new Vector3(0, 1, 0) }, useColorFlakes: { value: false }, }; this.materialExtension = { parsFragmentSnippet: (_, material) => { if (this.isDisabled() || !material?.userData._noiseBumpMat?.hasBump) return ''; return NoiseBumpMaterialPluginPars; }, shaderExtender: (shader, material) => { if (this.isDisabled() || !material?.userData._noiseBumpMat?.hasBump) return; shader.fragmentShader = shaderReplaceString(shader.fragmentShader, '#glMarker beforeAccumulation', NoiseBumpMaterialPluginPatch, { prepend: true }); shader.defines && (shader.defines.USE_UV = ''); shader.extensionDerivatives = true; }, onObjectRender: (_, material) => { const tfUd = material.userData._noiseBumpMat; if (!tfUd?.hasBump) return; if (Array.isArray(tfUd.bumpNoiseParams)) this._uniforms.noiseBumpParams.value.fromArray(tfUd.bumpNoiseParams); else this._uniforms.noiseBumpParams.value.copy(tfUd.bumpNoiseParams); this._uniforms.noiseBumpScale.value = tfUd.bumpScale; this._uniforms.noiseBumpFlakeScale.value = tfUd.flakeScale; this._uniforms.noiseFlakeClamp.value = tfUd.flakeClamp; this._uniforms.noiseFlakeRadius.value = tfUd.flakeRadius; if (Array.isArray(tfUd.flakeParams)) this._uniforms.flakeParams.value.fromArray(tfUd.flakeParams); else this._uniforms.flakeParams.value.copy(tfUd.flakeParams); if (Array.isArray(tfUd.flakeFallOffParams)) this._uniforms.flakeFallOffParams.value.fromArray(tfUd.flakeFallOffParams); else this._uniforms.flakeFallOffParams.value.copy(tfUd.flakeFallOffParams); this._uniforms.useColorFlakes.value = tfUd.useColorFlakes; updateMaterialDefines({ // ...this._defines, ['NOISE_BUMP_MATERIAL_ENABLED']: +!this.isDisabled(), }, material); }, extraUniforms: { // ...this._uniforms, // done in constructor }, computeCacheKey: (material1) => { return (this.isDisabled() ? '0' : '1') + (material1.userData._noiseBumpMat?.hasBump ? '1' : '0'); }, isCompatible: (material1) => material1.isPhysicalMaterial, getUiConfig: material => { const viewer = this._viewer; if (material.userData._noiseBumpMat === undefined) material.userData._noiseBumpMat = {}; const state = material.userData._noiseBumpMat; const config = { type: 'folder', label: 'SparkleBump (NoiseBump)', onChange: (ev) => { if (!ev.config) return; this.setDirty(); }, children: [ { type: 'checkbox', label: 'Enabled', get value() { return state.hasBump || false; }, set value(v) { if (v === state.hasBump) return; if (v) { if (!NoiseBumpMaterialPlugin_1.AddNoiseBumpMaterial(material)) viewer.dialog.alert('Cannot add NoiseBumpMaterial.'); } else { state.hasBump = false; if (material.setDirty) material.setDirty(); } config.uiRefresh?.(true, 'postFrame'); }, }, { type: 'vec4', label: 'Bump Noise Params', bounds: [0, 1], hidden: () => !state.hasBump, property: [state, 'bumpNoiseParams'], }, { type: 'slider', label: 'Bump Scale', bounds: [0, 0.001], stepSize: 0.00001, hidden: () => !state.hasBump, property: [state, 'bumpScale'], }, { type: 'slider', label: 'Flake Scale', bounds: [100, 10000], stepSize: 0.0001, hidden: () => !state.hasBump, property: [state, 'flakeScale'], }, { type: 'slider', label: 'Flake Clamp', bounds: [0, 1], stepSize: 1, hidden: () => !state.hasBump, property: [state, 'flakeClamp'], }, { type: 'slider', label: 'Flake Radius', bounds: [0.01, 1], stepSize: 0.001, hidden: () => !state.hasBump, property: [state, 'flakeRadius'], }, { type: 'slider', label: 'Flake Roughness', bounds: [0., 1], stepSize: 0.01, hidden: () => !state.hasBump, property: [state.flakeParams, 'x'], }, { type: 'slider', label: 'Flake Metalness', bounds: [0., 1], stepSize: 0.01, hidden: () => !state.hasBump, property: [state.flakeParams, 'y'], }, { type: 'slider', label: 'Flake Strength', bounds: [0.0, 100], stepSize: 0.001, hidden: () => !state.hasBump, property: [state.flakeParams, 'z'], }, { type: 'slider', label: 'Flake Threshold', bounds: [0.1, 10], stepSize: 0.001, hidden: () => !state.hasBump, property: [state.flakeParams, 'w'], }, { type: 'slider', label: 'Falloff', stepSize: 1, bounds: [0, 1], hidden: () => !state.hasBump, property: [state.flakeFallOffParams, 'x'], }, { type: 'slider', label: 'Linear falloff factor', bounds: [0., 10], stepSize: 0.001, hidden: () => !state.hasBump, property: [state.flakeFallOffParams, 'y'], }, { type: 'slider', label: 'Quadratic falloff factor', bounds: [0., 10], stepSize: 0.001, hidden: () => !state.hasBump, property: [state.flakeFallOffParams, 'z'], }, { type: 'checkbox', label: 'Colored Flakes', hidden: () => !state.hasBump, property: [state, 'useColorFlakes'], }, ], }; return config; }, }; this.setDirty = () => { this.materialExtension.setDirty?.(); this._viewer?.setDirty(); }; Object.assign(this.materialExtension.extraUniforms, this._uniforms); } onAdded(v) { super.onAdded(v); v.assetManager.materials.registerMaterialExtension(this.materialExtension); v.assetManager.registerGltfExtension(noiseBumpMaterialGLTFExtension); } onRemove(v) { v.assetManager.materials?.unregisterMaterialExtension(this.materialExtension); v.assetManager.unregisterGltfExtension(noiseBumpMaterialGLTFExtension.name); return super.onRemove(v); } }; NoiseBumpMaterialPlugin.PluginType = 'NoiseBumpMaterialPlugin'; /** * @deprecated - use {@link noiseBumpMaterialGLTFExtension} */ NoiseBumpMaterialPlugin.NOISE_BUMP_MATERIAL_GLTF_EXTENSION = 'WEBGI_materials_noise_bump'; __decorate([ uiToggle('Enabled', (that) => ({ onChange: that.setDirty })), serialize() ], NoiseBumpMaterialPlugin.prototype, "enabled", void 0); NoiseBumpMaterialPlugin = NoiseBumpMaterialPlugin_1 = __decorate([ uiFolderContainer('Noise/Sparkle Bump (MatExt)') ], NoiseBumpMaterialPlugin); export { NoiseBumpMaterialPlugin }; /** * FragmentClipping Materials Extension * * Specification: https://threepipe.org/docs/gltf-extensions/WEBGI_materials_fragment_clipping_extension.html (todo - fix link) */ class GLTFMaterialsNoiseBumpMaterialImport { constructor(parser) { this.parser = parser; this.name = noiseBumpMaterialGLTFExtension.name; } async extendMaterialParams(materialIndex, materialParams) { const parser = this.parser; const materialDef = parser.json.materials[materialIndex]; if (!materialDef.extensions || !materialDef.extensions[this.name]) return; const extension = materialDef.extensions[this.name]; if (!materialParams.userData) materialParams.userData = {}; NoiseBumpMaterialPlugin.AddNoiseBumpMaterial(materialParams); ThreeSerialization.Deserialize(extension, materialParams.userData._noiseBumpMat); } } const glTFMaterialsNoiseBumpMaterialExport = (w) => ({ writeMaterial: (material, materialDef) => { if (!material.isMeshStandardMaterial || !material.userData._noiseBumpMat?.hasBump) return; materialDef.extensions = materialDef.extensions || {}; const extensionDef = ThreeSerialization.Serialize(material.userData._noiseBumpMat); materialDef.extensions[noiseBumpMaterialGLTFExtension.name] = extensionDef; w.extensionsUsed[noiseBumpMaterialGLTFExtension.name] = true; }, }); export const noiseBumpMaterialGLTFExtension = { name: 'WEBGI_materials_noise_bump', import: (p) => new GLTFMaterialsNoiseBumpMaterialImport(p), export: glTFMaterialsNoiseBumpMaterialExport, textures: undefined, }; //# sourceMappingURL=NoiseBumpMaterialPlugin.js.map