Created
October 2, 2024 13:40
-
-
Save DennisSmolek/7d75f9e7d4476beffd3c5b4db1a581be to your computer and use it in GitHub Desktop.
useVariants() draft
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // this hook wraps a gltf and allows the use of the khr variants extension | |
| import { useMemo, useState, useEffect, useCallback } from "react"; | |
| import type { GLTF } from "../../types/gltf"; | |
| import type { Material, Mesh } from "three"; | |
| /* What I want to return is an object like { variants, activeVariant, setVariant, materials}*/ | |
| export const useVariants = (model: GLTF, inVariant: string) => { | |
| // states | |
| const [activeVariant, setActiveVariant] = useState<string | null>(null); | |
| const [materials, setMaterials] = useState<Material[]>([]); | |
| // On load handler | |
| useEffect(() => { | |
| // get the materials from the model | |
| model.parser.getDependencies("material").then((materials) => { | |
| setMaterials(materials); | |
| }); | |
| }, [model]); | |
| const variants = useMemo(() => { | |
| return model.userData.gltfExtensions?.KHR_materials_variants?.variants; | |
| }, [model]); | |
| // use the parser to apply the variant to the model | |
| const applyVariant = useCallback((variantIndex) => { | |
| console.log("applying variant", variantIndex); | |
| model.scene.traverse(async (object) => { | |
| if (!('isMesh' in object) || !object.userData.gltfExtensions) return; | |
| const meshObject = object as Mesh; | |
| const meshVariantDef = | |
| meshObject.userData.gltfExtensions.KHR_materials_variants; | |
| if (!meshVariantDef) return; | |
| // when this first runs we want to set the original material | |
| if (!meshObject.userData.originalMaterial) | |
| meshObject.userData.originalMaterial = meshObject.material; | |
| const mapping = meshVariantDef.mappings.find((mapping) => | |
| mapping.variants.includes(variantIndex) | |
| ); | |
| if (mapping) { | |
| meshObject.material = await model.parser.getDependency( | |
| "material", | |
| mapping.material | |
| ); | |
| model.parser.assignFinalMaterial(meshObject); | |
| } else meshObject.material = meshObject.userData.originalMaterial; | |
| // mark the material for update | |
| if (Array.isArray(meshObject.material)) | |
| for (const mat of meshObject.material) mat.needsUpdate = true; | |
| else meshObject.material.needsUpdate = true; | |
| }); | |
| }, [model]); | |
| // set the active variant, block if non existent | |
| const setVariant = useCallback((variantName: string | null) => { | |
| // check if the variant is in the variants array and set it accordingly | |
| if (!variants) return; | |
| const newVariant = variantName && variants.some(v => v.name === variantName) ? variantName : null; | |
| setActiveVariant(newVariant); | |
| }, [variants]); | |
| // if the variants or active variant change, apply the variant | |
| useEffect(() => { | |
| if (!variants) return; | |
| const variantIndex = variants.findIndex((v) => v.name === activeVariant); | |
| applyVariant(variantIndex > -1 ? variantIndex : 0); | |
| }, [variants, activeVariant, applyVariant]); | |
| // take in variant as an option and set it accordingly | |
| useEffect(() => { | |
| setVariant(inVariant); | |
| }, [inVariant, setVariant]); | |
| return { variants, activeVariant, setVariant, materials }; | |
| }; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment