UNPKG

threepipe

Version:

A 3D viewer framework built on top of three.js in TypeScript with a focus on quality rendering, modularity and extensibility.

106 lines 4.46 kB
/** * Materials variants extension * Modified from https://github.com/takahirox/three-gltf-extensions/blob/main/exporters/KHR_materials_variants/KHR_materials_variants_exporter.js * * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_materials_variants */ import { khrMaterialsVariantsGLTF } from './GLTFMaterialsVariantsExtensionImport'; /** * @param object {THREE.Object3D} * @return {boolean} */ const compatibleObject = (object) => { return object.material !== undefined && // easier than (!object.isMesh && !object.isLine && !object.isPoints) object.userData && // just in case object.userData._variantMaterials && !!Object.values(object.userData._variantMaterials).filter(m => compatibleMaterial(m?.material)); }; /** * @param material {THREE.Material} * @return {boolean} */ const compatibleMaterial = (material) => { // @TODO: support multi materials? return material && material.isMaterial && !Array.isArray(material); }; export class GLTFExporterMaterialsVariantsExtensionExport { constructor(writer) { this.writer = writer; this.name = khrMaterialsVariantsGLTF; this.variantNames = []; } beforeParse(objects) { // Find all variant names and store them to the table const variantNameTable = new Set(); for (const object of objects) { object.traverse(o => { if (!compatibleObject(o)) { return; } const variantMaterials = o.userData._variantMaterials; for (const variantName in variantMaterials) { const variantMaterial = variantMaterials[variantName]; // Ignore unloaded variant materials if (compatibleMaterial(variantMaterial.material)) { variantNameTable.add(variantName); } } }); } // We may want to sort? variantNameTable.forEach(name => this.variantNames.push(name)); } writeMesh(mesh, meshDef) { if (!compatibleObject(mesh)) { return; } const userData = mesh.userData; const variantMaterials = userData._variantMaterials; const mappingTable = {}; for (const variantName in variantMaterials) { const variantMaterialInstance = variantMaterials[variantName].material; if (!compatibleMaterial(variantMaterialInstance)) { continue; } const variantIndex = this.variantNames.indexOf(variantName); // Shouldn't be -1 const materialIndex = this.writer.processMaterial(variantMaterialInstance); if (!mappingTable[materialIndex]) { mappingTable[materialIndex] = { material: materialIndex, variants: [], }; } mappingTable[materialIndex].variants.push(variantIndex); } const mappingsDef = Object.values(mappingTable) .map(m => { return m.variants.sort((a, b) => a - b) && m; }) .sort((a, b) => a.material - b.material); if (mappingsDef.length === 0) { return; } const originalMaterialIndex = compatibleMaterial(userData._originalMaterial) ? this.writer.processMaterial(userData._originalMaterial) ?? -1 : -1; for (const primitiveDef of meshDef.primitives) { // Override primitiveDef.material with original material. if (originalMaterialIndex >= 0) { primitiveDef.material = originalMaterialIndex; } primitiveDef.extensions = primitiveDef.extensions || {}; primitiveDef.extensions[this.name] = { mappings: mappingsDef }; } } afterParse(_input) { if (this.variantNames.length === 0) { return; } const root = this.writer.json; root.extensions = root.extensions || {}; const variantsDef = this.variantNames.map(n => { return { name: n }; }); root.extensions[this.name] = { variants: variantsDef }; this.writer.extensionsUsed[this.name] = true; } } export function gltfExporterMaterialsVariantsExtensionExport(writer) { return new GLTFExporterMaterialsVariantsExtensionExport(writer); } //# sourceMappingURL=GLTFMaterialsVariantsExtensionExport.js.map