UNPKG

nanogl-gltf

Version:
163 lines (162 loc) 6.99 kB
import PbrMetallicRoughness from './PbrMetallicRoughness'; import { isWebgl2 } from 'nanogl/types'; import Gltf2 from '../types/Gltf2'; import GltfTypes from '../types/GltfTypes'; import Input from 'nanogl-pbr/Input'; import { default as BaseMaterial } from 'nanogl-pbr/Material'; import Gltf from '../Gltf'; import Flag from 'nanogl-pbr/Flag'; import { isAllZeros } from '../lib/Utils'; import { StandardPass } from 'nanogl-pbr/StandardPass'; import { MetalnessSurface } from 'nanogl-pbr/PbrSurface'; const SRC_ALPHA = 0x0302; const ONE_MINUS_SRC_ALPHA = 0x0303; /** * The GltfBaseMaterial element contains the base properties and methods for all Material classes coming from a glTF file */ export class GltfBaseMaterial { constructor() { this.gltftype = GltfTypes.MATERIAL; } get materialPass() { return this._materialPass; } /** * Create a nanogl-pbr Material object for a given Primitive, using the Pass created in setupMaterials(). * @param gltf GLTF where the Primitive comes from * @param node Parent Node of the Primitive, unused here * @param primitive Primitive to create the Material for */ createMaterialForPrimitive(gltf, node, primitive) { const gl = gltf.gl; this._materialPass.version.set(isWebgl2(gl) ? '300 es' : '100'); const material = new BaseMaterial(gl, this._materialPass.name); material.addPass(this._materialPass, 'color'); const normal = primitive.attributes.getSemantic('NORMAL'); const tangent = primitive.attributes.getSemantic('TANGENT'); material.inputs.add(new Flag('hasNormals', normal !== null)); material.inputs.add(new Flag('hasTangents', tangent !== null)); const vcColorAttrib = primitive.attributes.getSemantic('COLOR_0'); if (vcColorAttrib !== null) { // vertex color const vcInput = new Input('vertexColor', 3); vcInput.attachAttribute(vcColorAttrib.glslname); material.inputs.add(vcInput); } material.addPass(gltf.depthPass, 'depth'); return material; } /** * Parse the Material data, fill the emissiveFactor, alphaMode, alphaCutoff and doubleSided properties, * create the PbrMetallicRoughness, NormalTexture, OcclusionTexture and EmissiveTexture elements if needed, * and call the setupMaterials() method. * * Is async as it may need to wait for PbrMetallicRoughness, NormalTexture, OcclusionTexture and EmissiveTexture elements to be created. * @param gltfLoader GLTFLoader to use * @param data Data to parse */ async parse(gltfLoader, data) { var _a, _b; this.emissiveFactor = new Float32Array(data.emissiveFactor || [0, 0, 0]); this.alphaMode = data.alphaMode || Gltf2.MaterialAlphaMode.OPAQUE; this.alphaCutoff = (_a = data.alphaCutoff) !== null && _a !== void 0 ? _a : 0.5; this.doubleSided = (_b = data.doubleSided) !== null && _b !== void 0 ? _b : false; await this.parsePbrInputsData(gltfLoader, data); if (data.normalTexture !== undefined) { this.normalTexture = await gltfLoader._loadElement(data.normalTexture); } if (data.occlusionTexture !== undefined) { this.occlusionTexture = await gltfLoader._loadElement(data.occlusionTexture); } if (data.emissiveTexture !== undefined) { this.emissiveTexture = await gltfLoader._loadElement(data.emissiveTexture); } this.name = data.name; this.setupMaterials(); } /** * Parse the PbrMetallicRoughness data, filling the pbrMetallicRoughness property with a new PbrMetallicRoughness element. * @param gltfLoader GLTFLoader to use * @param data Data to parse */ async parsePbrInputsData(gltfLoader, data) { if (data.pbrMetallicRoughness !== undefined) { this.pbrInputsData = this.pbrMetallicRoughness = new PbrMetallicRoughness(); await this.pbrMetallicRoughness.parse(gltfLoader, data.pbrMetallicRoughness); } } /** * Configure PBR Surface for a given Pass with this Material's options. * If this.pbrInputsData is defined, call its setupPass() method, otherwise set a MetalnessSurface. * @param pass Pass to configure */ configurePbrSurface(pass) { if (this.pbrInputsData !== undefined) { this.pbrInputsData.setupPass(pass); } else { pass.setSurface(new MetalnessSurface()); } } /** * Configure alpha rendering mode for a given Pass with this Material's options * (depth mask, blend, blendFunc, mask, alphaMode and alphaCutoff uniform) * @param pass Pass to configure */ configureAlpha(pass) { if (this.alphaMode === Gltf2.MaterialAlphaMode.BLEND) { pass.glconfig.depthMask(false); pass.glconfig.enableBlend(); pass.glconfig.blendFunc(SRC_ALPHA, ONE_MINUS_SRC_ALPHA); pass.mask = Gltf.getRenderConfig().blendedMask; } else { pass.mask = Gltf.getRenderConfig().opaqueMask; } pass.alphaMode.set(this.alphaMode); if (this.alphaMode === Gltf2.MaterialAlphaMode.MASK) { pass.alphaCutoff.attachUniform('uAlphaCutoff').set(this.alphaCutoff); } } } /** * Basic PBR implementation of GltfBaseMaterial using a nanogl-pbr StandardPass */ export default class Material extends GltfBaseMaterial { /** * Creates a StandardPass and attach emissive, normal and occlusion textures with samplers and their strength factors. * Also configure alpha rendering mode and PBR surface. */ setupMaterials() { const pass = new StandardPass(this.name); pass.glconfig.enableDepthTest(); pass.glconfig.enableCullface(!this.doubleSided); pass.doubleSided.set(this.doubleSided); this.configureAlpha(pass); this.configurePbrSurface(pass); if (this.emissiveTexture) { const sampler = this.emissiveTexture.createSampler('emissive'); pass.emissive.attach(sampler); } if (!isAllZeros(this.emissiveFactor)) { pass.emissiveFactor.attachUniform('uEmissFactor').set(...this.emissiveFactor); } const nrm = this.normalTexture; if (nrm) { const sampler = nrm.createSampler('nrmmap'); pass.normal.attach(sampler); if (nrm.scale !== 1) { pass.normalScale.attachUniform('uNormalScale').set(nrm.scale); } } const occlu = this.occlusionTexture; if (occlu) { const sampler = occlu.createSampler('occlu'); pass.occlusion.attach(sampler); if (occlu.strength !== 1) { pass.occlusionStrength.attachUniform('uOccluStrength').set(occlu.strength); } } this._materialPass = pass; } }