mbt-3d 0.3.2 → 0.3.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/mbt-3d.cjs CHANGED
@@ -1,2 +1,2 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("react"),O=require("@react-three/drei"),se=require("three"),ne=require("three/examples/jsm/utils/SkeletonUtils.js"),_=require("@react-three/fiber"),oe=require("three/examples/jsm/loaders/KTX2Loader.js"),A=require("react/jsx-runtime");function Q(a){const t=Object.create(null,{[Symbol.toStringTag]:{value:"Module"}});if(a){for(const r in a)if(r!=="default"){const o=Object.getOwnPropertyDescriptor(a,r);Object.defineProperty(t,r,o.get?o:{enumerable:!0,get:()=>a[r]})}}return t.default=a,Object.freeze(t)}const C=Q(se),Y=Q(ne);function ie({position:a,controlsConfig:t}){const{camera:r}=_.useThree(),o=e.useRef(null);return e.useEffect(()=>{a&&r&&(r.position.set(a[0],a[1],a[2]),r.updateProjectionMatrix(),o.current&&(o.current.target.set(0,0,0),o.current.update()))},[a,r]),A.jsx(O.OrbitControls,{ref:o,makeDefault:!0,enabled:t.enabled,enablePan:t.enablePan,enableZoom:t.enableZoom,enableRotate:t.enableRotate,minDistance:t.minDistance,maxDistance:t.maxDistance,minPolarAngle:t.minPolarAngle,maxPolarAngle:t.maxPolarAngle,autoRotate:t.autoRotate,autoRotateSpeed:t.autoRotateSpeed})}function ce({background:a}){const t=a==null?void 0:a.startsWith("#");return a?t?A.jsx("color",{attach:"background",args:[a]}):A.jsx(ue,{url:a}):null}function ue({url:a}){const t=O.useTexture(a);return e.useMemo(()=>{t.colorSpace=C.SRGBColorSpace},[t]),A.jsx("primitive",{attach:"background",object:t})}function le({config:a}){switch(a.type){case"spot":{const{position:t=[5,10,5],intensity:r=50,castShadow:o=!0,angle:u=Math.PI/6,penumbra:d=.5,decay:M=2,distance:y=0,color:h="#ffffff"}=a;return A.jsx("spotLight",{position:t,intensity:r,castShadow:o,angle:u,penumbra:d,decay:M,distance:y,color:h})}case"point":{const{position:t=[0,5,0],intensity:r=20,color:o="#ffffff",distance:u=0,decay:d=2,castShadow:M=!1}=a;return A.jsx("pointLight",{position:t,intensity:r,color:o,distance:u,decay:d,castShadow:M})}case"directional":{const{position:t=[10,20,10],intensity:r=40,color:o="#ffffff",castShadow:u=!0}=a;return A.jsx("directionalLight",{position:t,intensity:r,color:o,castShadow:u})}case"hemisphere":{const{skyColor:t="#ffffff",groundColor:r="#444444",intensity:o=.5,position:u=[0,10,0]}=a,d=e.useMemo(()=>new C.Color(t),[t]),M=e.useMemo(()=>new C.Color(r),[r]);return A.jsx("hemisphereLight",{args:[d,M,o],position:u})}case"ambient":{const{intensity:t=.5,color:r="#ffffff"}=a;return A.jsx("ambientLight",{intensity:t,color:r})}default:return null}}function fe({children:a,camera:t={},controls:r={},background:o,shadows:u=!0,lights:d=[{type:"ambient",intensity:.5},{type:"spot",position:[5,10,5],intensity:50,castShadow:!0}],contactShadows:M=!0,style:y,className:h}){const s={position:t.position||[0,2,5],fov:t.fov||45},n={enabled:r.enabled??!0,enablePan:r.enablePan??!0,enableZoom:r.enableZoom??!0,enableRotate:r.enableRotate??!0,minDistance:r.minDistance,maxDistance:r.maxDistance,minPolarAngle:r.minPolarAngle,maxPolarAngle:r.maxPolarAngle,autoRotate:r.autoRotate??!1,autoRotateSpeed:r.autoRotateSpeed??2},c=typeof M=="object"?{position:M.position||[0,-1,0],opacity:M.opacity??.5,blur:M.blur??2}:M?{position:[0,-1,0],opacity:.5,blur:2}:null;return A.jsx("div",{style:y,className:h,children:A.jsxs(_.Canvas,{shadows:u,camera:{position:s.position,fov:s.fov},style:{width:"100%",height:"100%"},children:[A.jsx(e.Suspense,{fallback:null,children:A.jsx(ce,{background:o})}),d.map((l,b)=>A.jsx(le,{config:l},`${l.type}-${b}`)),A.jsx(e.Suspense,{fallback:null,children:a}),A.jsx(ie,{position:s.position,controlsConfig:n}),c&&A.jsx(O.ContactShadows,{position:c.position,opacity:c.opacity,blur:c.blur})]})})}function U(a,t){const r=e.useRef(new Map),o=e.useRef({}),u=e.useRef({});e.useEffect(()=>{t&&(o.current={...t})},[t]),e.useEffect(()=>{if(!a)return;const h=new Map;a.traverse(s=>{if(s.isMesh){const n=s;n.name&&h.set(n.name,n)}}),r.current=h},[a]),_.useFrame(()=>{const h=r.current;if(h.size===0)return;const s={...o.current,...u.current};for(const[n,c]of Object.entries(s)){const l=h.get(n);l&&l.visible!==c&&(l.visible=c)}});const d=e.useCallback((h,s)=>{u.current[h]=s;const n=r.current.get(h);n&&(n.visible=s)},[]),M=e.useCallback(()=>Array.from(r.current.keys()).sort(),[]),y=e.useCallback(()=>{const h={};return r.current.forEach((s,n)=>{h[n]=s.visible}),h},[]);return{setMeshVisibility:d,getMeshNames:M,getMeshVisibility:y}}function z(a,t){const r=e.useRef(new Map),o=e.useRef(new Map);e.useEffect(()=>{if(!a)return;const s=new Map;a.traverse(n=>{if(n.isMesh){const c=n;(Array.isArray(c.material)?c.material:[c.material]).forEach(b=>{b.name&&!s.has(b.name)&&s.set(b.name,b)})}}),r.current=s,t&&Object.entries(t).forEach(([n,c])=>{const l=s.get(n);if(l&&!Array.isArray(l)){const b=u(c);l.color&&l.color.copy(b),o.current.set(n,b)}})},[a,t]),_.useFrame(()=>{o.current.forEach((s,n)=>{const c=r.current.get(n);if(c&&!Array.isArray(c)){const l=c;l.color&&!l.color.equals(s)&&(l.color.copy(s),l.needsUpdate=!0)}})});const u=s=>typeof s=="string"?new C.Color(s):new C.Color(s[0],s[1],s[2]),d=e.useCallback((s,n)=>{const c=u(n);o.current.set(s,c);const l=r.current.get(s);if(l&&!Array.isArray(l)){const b=l;b.color&&(b.color.copy(c),b.needsUpdate=!0)}},[]),M=e.useCallback(()=>Array.from(r.current.keys()).sort(),[]),y=e.useCallback(s=>{const n=r.current.get(s);if(n&&!Array.isArray(n)){const c=n;if(c.color)return"#"+c.color.getHexString()}return null},[]),h=e.useCallback(()=>{const s={};return r.current.forEach((n,c)=>{if(!Array.isArray(n)){const l=n;l.color&&(s[c]="#"+l.color.getHexString())}}),s},[]);return{setMaterialColor:d,getMaterialNames:M,getMaterialColor:y,getAllMaterialColors:h}}function X(a,t){const{gl:r}=_.useThree(),o=e.useRef(new Map),u=e.useRef(new C.TextureLoader),d=e.useRef(null),M=e.useRef(new Map),y=e.useRef(new Set),h=e.useRef("");e.useEffect(()=>{if(!d.current)try{const f=typeof WebAssembly=="object"&&typeof WebAssembly.validate=="function";console.log("[useMaterialTexture] Browser features:"),console.log(` WebAssembly supported: ${f}`),console.log(` WebGL2: ${r.capabilities.isWebGL2}`);const i=new oe.KTX2Loader;i.setTranscoderPath("/basis/"),i.detectSupport(r),d.current=i,console.log("[useMaterialTexture] ✅ KTX2Loader initialized"),console.log("[useMaterialTexture] Transcoder path: /basis/"),fetch("/basis/basis_transcoder.wasm").then(m=>{m.ok?console.log("[useMaterialTexture] ✅ basis_transcoder.wasm is accessible"):console.error("[useMaterialTexture] ❌ basis_transcoder.wasm returned",m.status)}).catch(m=>{console.error("[useMaterialTexture] ❌ Failed to check basis_transcoder.wasm:",m)}),f||(console.warn("[useMaterialTexture] ⚠️ WebAssembly not supported! KTX2 will fallback to CPU decoding (slow)"),console.warn("[useMaterialTexture] ⚠️ Recommend using WebP instead of KTX2 for this browser"))}catch(f){console.error("[useMaterialTexture] ❌ Failed to initialize KTX2Loader:",f)}return()=>{d.current&&(d.current.dispose(),d.current=null)}},[r]),e.useEffect(()=>{if(!a)return;const f=new Map;a.traverse(i=>{if(i.isMesh){const m=i;(Array.isArray(m.material)?m.material:[m.material]).forEach(p=>{p.name&&!f.has(p.name)&&f.set(p.name,p)})}}),o.current=f},[a]);const s=e.useCallback(f=>f.toLowerCase().endsWith(".ktx2")?d.current?d.current:(console.warn("[useMaterialTexture] KTX2Loader not initialized, falling back to TextureLoader"),u.current):u.current,[]);e.useEffect(()=>{if(!t||!a)return;const f=o.current;if(f.size===0)return;const i=JSON.stringify(t);if(h.current===i){console.log("[useMaterialTexture] Textures unchanged, skipping reload");return}h.current=i;let m=!1;const w=new Map;Object.entries(t).forEach(([g,S])=>{w.set(g,typeof S=="string"?{map:S}:S)}),console.log("[useMaterialTexture] Starting texture load for",w.size,"materials");const p=[];w.forEach((g,S)=>{const D=f.get(S);if(!D||!D.isMeshStandardMaterial){console.warn(`[useMaterialTexture] Material "${S}" not found or not MeshStandardMaterial`);return}const x=D;x.map=null,x.normalMap=null,x.roughnessMap=null,x.metalnessMap=null,x.emissiveMap=null,x.emissive=new C.Color(0),x.emissiveIntensity=0,x.aoMap=null,x.needsUpdate=!0,Object.entries(g).forEach(([T,v])=>{if(!v)return;const E=`${S}_${T}_${v}`,j=M.current.get(E);if(j){console.log(`[useMaterialTexture] Using cached texture: ${S}.${T}`),m||n(x,T,j);return}if(y.current.has(E)){console.log(`[useMaterialTexture] Skipping already loading: ${S}.${T}`);return}p.push({materialName:S,textureType:T,url:v,material:x})})}),console.log(`[useMaterialTexture] Queued ${p.length} textures to load`);let R=0;const k=()=>{if(m||R>=p.length){console.log("[useMaterialTexture] All textures loaded");return}const g=p[R++],S=`${g.materialName}_${g.textureType}_${g.url}`;console.log(`[useMaterialTexture] Loading (${R}/${p.length}): ${g.materialName}.${g.textureType} from ${g.url}`),y.current.add(S);const D=s(g.url),x=g.url.toLowerCase().endsWith(".ktx2")?"KTX2Loader":"TextureLoader";console.log(`[useMaterialTexture] Using ${x} for ${g.url}`),D.load(g.url,T=>{var N,W,B,L,$,F;if(y.current.delete(S),m){console.log(`[useMaterialTexture] Disposed, cleaning up texture: ${g.materialName}.${g.textureType}`),T.dispose();return}const v=((N=T.image)==null?void 0:N.width)||((B=(W=T.source)==null?void 0:W.data)==null?void 0:B.width)||"unknown",E=((L=T.image)==null?void 0:L.height)||((F=($=T.source)==null?void 0:$.data)==null?void 0:F.height)||"unknown";console.log(`[useMaterialTexture] ✅ Loaded: ${g.materialName}.${g.textureType} (${v}x${E})`),T.colorSpace=g.textureType==="map"||g.textureType==="emissiveMap"?C.SRGBColorSpace:C.NoColorSpace,T.wrapS=C.RepeatWrapping,T.wrapT=C.RepeatWrapping,T.flipY=!1,M.current.set(S,T),n(g.material,g.textureType,T);const j=g.url.endsWith(".ktx2")?500:150;console.log(`[useMaterialTexture] Waiting ${j}ms before next texture...`),setTimeout(k,j)},T=>{if(T.lengthComputable){const v=Math.round(T.loaded/T.total*100);v%25===0&&console.log(`[useMaterialTexture] Progress ${g.materialName}.${g.textureType}: ${v}%`)}},T=>{y.current.delete(S),m||(console.error(`[useMaterialTexture] ❌ Failed to load ${g.materialName}.${g.textureType} from ${g.url}`),console.error("[useMaterialTexture] Error details:",T),console.error("[useMaterialTexture] Loader type:",x),g.url.endsWith(".ktx2")&&(console.error("[useMaterialTexture] KTX2 error - file may not be in Basis Universal format!"),console.error("[useMaterialTexture] Make sure files are created with: gltf-transform etc1s input.png output.ktx2"))),setTimeout(k,100)})};return k(),()=>{console.log("[useMaterialTexture] Cleanup: disposed"),m=!0}},[a,t,s]);const n=(f,i,m)=>{switch(i){case"map":f.map=m;break;case"normalMap":f.normalMap=m;break;case"roughnessMap":f.roughnessMap=m;break;case"metalnessMap":f.metalnessMap=m;break;case"emissiveMap":f.emissiveMap=m,f.emissive=new C.Color(16777215),f.emissiveIntensity=1;break;case"alphaMap":console.log(`[useMaterialTexture] Skipping alphaMap for "${f.name}" to prevent disappearing`);break;case"aoMap":f.aoMap=m;break}f.needsUpdate=!0},c=e.useCallback((f,i)=>{const m=typeof i=="string"?{map:i}:i,w=o.current.get(f);if(!w||!w.isMeshStandardMaterial){console.warn(`Material "${f}" not found or not MeshStandardMaterial`);return}const p=w;Object.entries(m).forEach(([R,k])=>{if(!k)return;const g=`${f}_${R}_${k}`,S=M.current.get(g);if(S){n(p,R,S);return}s(k).load(k,x=>{x.colorSpace=R==="map"||R==="emissiveMap"?C.SRGBColorSpace:C.NoColorSpace,x.wrapS=C.RepeatWrapping,x.wrapT=C.RepeatWrapping,M.current.set(g,x),n(p,R,x)},void 0,x=>{console.error(`Failed to load texture ${k}:`,x)})})},[s]),l=e.useCallback(()=>Array.from(o.current.keys()).sort(),[]),b=e.useCallback(f=>{const i=o.current.get(f);if(!i||!i.isMeshStandardMaterial)return;const m=i;m.map=null,m.normalMap=null,m.roughnessMap=null,m.metalnessMap=null,m.emissiveMap=null,m.alphaMap=null,m.aoMap=null,m.needsUpdate=!0},[]);return{setMaterialTextures:c,getMaterialNames:l,clearMaterialTextures:b}}function ee({url:a,position:t=[0,0,0],rotation:r=[0,0,0],scale:o=1,meshVisibility:u,materialColors:d,materialTextures:M,onLoad:y,onError:h}){const{scene:s}=O.useGLTF(a),n=e.useRef(y);n.current=y;const c=e.useMemo(()=>{const b=s.clone(),f=[],i=new Set,m=[];let w=0;return b.traverse(p=>{if(w++,p.type==="Bone"&&m.push(p.name),p.isMesh){const R=p;f.push(R.name),R.castShadow=!0,R.receiveShadow=!0,(Array.isArray(R.material)?R.material:[R.material]).forEach(g=>i.add(g.name))}}),setTimeout(()=>{var p;(p=n.current)==null||p.call(n,{meshes:f.sort(),materials:Array.from(i).sort(),bones:m.sort(),nodeCount:w})},0),b},[s]);U(c,u),z(c,d),X(c,M);const l=typeof o=="number"?[o,o,o]:o;return A.jsx("group",{position:t,rotation:r,scale:l,children:A.jsx("primitive",{object:c})})}ee.preload=a=>{O.useGLTF.preload(a)};function te(a,t,r){const{actions:o,names:u}=O.useAnimations(a,t),d=e.useRef(null),M=e.useRef(r==null?void 0:r.defaultAnimation);e.useEffect(()=>{if(u.length===0)return;const n=M.current;let c=u[0];if(n){const b=u.find(f=>f===n||f.includes(n));b&&(c=b)}const l=o[c];l&&(l.reset().fadeIn(.5).play(),d.current=l)},[o,u]);const y=e.useCallback((n,c)=>{const{loop:l=!1,crossFadeDuration:b=.2,restoreDefault:f=!0}=c||{};let i=o[n];if(!i){const w=Object.keys(o).find(p=>p.toLowerCase().includes(n.toLowerCase())||n.toLowerCase().includes(p.toLowerCase()));w&&(i=o[w])}if(!i){console.warn(`Animation "${n}" not found. Available: ${u.join(", ")}`);return}const m=d.current;if(!(m===i&&i.isRunning())&&(m&&m!==i&&m.fadeOut(b),i.reset(),i.fadeIn(b),i.setLoop(l?C.LoopRepeat:C.LoopOnce,l?1/0:1),i.clampWhenFinished=!l,i.play(),l||i.getMixer().update(0),d.current=i,f&&!l&&M.current)){const w=i.getMixer(),p=R=>{if(R.action===i){w.removeEventListener("finished",p);const k=o[M.current];k&&(i.fadeOut(b),k.reset().fadeIn(b).play(),d.current=k)}};w.addEventListener("finished",p)}},[o,u]),h=e.useCallback(()=>{var n;(n=d.current)==null||n.fadeOut(.2),d.current=null},[]),s=e.useCallback(()=>u,[u]);return{playAnimation:y,stopAnimation:h,getAnimationNames:s,actions:o}}function q(a,t){const r=e.useRef(t||{}),o=e.useRef([]),u=e.useRef([]);e.useEffect(()=>{const h=new Set,s=[];a.traverse(n=>{n instanceof C.Mesh&&n.morphTargetDictionary&&n.morphTargetInfluences&&(s.push(n),Object.keys(n.morphTargetDictionary).forEach(c=>{h.add(c)}))}),o.current=Array.from(h).sort(),u.current=s},[a]),_.useFrame(()=>{const h=r.current;u.current.forEach(s=>{!s.morphTargetDictionary||!s.morphTargetInfluences||Object.entries(h).forEach(([n,c])=>{const l=s.morphTargetDictionary[n];l!==void 0&&(s.morphTargetInfluences[l]=c)})})}),e.useEffect(()=>{t&&(r.current={...t})},[t]);const d=e.useCallback((h,s)=>{r.current[h]=Math.max(0,Math.min(1,s))},[]),M=e.useCallback(()=>o.current,[]),y=e.useCallback(()=>({...r.current}),[]);return{setMorphTarget:d,getMorphTargetNames:M,getMorphTargetValues:y}}const re=e.createContext(null);function me(){const a=e.useContext(re);if(!a)throw new Error("BoneAttachment must be used within an AnimatedModel");return a}const V=e.forwardRef(({url:a,position:t=[0,0,0],rotation:r=[0,0,0],scale:o=1,defaultAnimation:u,morphTargets:d,meshVisibility:M,materialColors:y,materialTextures:h,onLoad:s,onError:n,children:c},l)=>{const b=e.useRef(null),f=e.useRef([]),i=e.useRef(s);i.current=s;const{scene:m,animations:w}=O.useGLTF(a),p=e.useMemo(()=>Y.clone(m),[m]),{playAnimation:R,stopAnimation:k,getAnimationNames:g}=te(w,p,{defaultAnimation:u}),{setMorphTarget:S}=q(p,d),{setMeshVisibility:D,getMeshNames:x}=U(p,M),{setMaterialColor:T,getMaterialNames:v,getMaterialColor:E}=z(p,y),{setMaterialTextures:j,clearMaterialTextures:N}=X(p,h);e.useEffect(()=>{if(!p)return;const L=setTimeout(()=>{var J;const $=[],F=[],G=new Set,H=new Set;let Z=0;p.traverse(I=>{if(Z++,I.type==="Bone"&&$.push(I.name),I.isMesh){const P=I;F.push(P.name),P.castShadow=!0,P.receiveShadow=!0,(Array.isArray(P.material)?P.material:[P.material]).forEach(K=>{G.add(K.name),K.shadowSide=C.DoubleSide}),P.morphTargetDictionary&&Object.keys(P.morphTargetDictionary).forEach(K=>{H.add(K)})}}),f.current=Array.from(H).sort(),(J=i.current)==null||J.call(i,{meshes:F.sort(),materials:Array.from(G).sort(),bones:$.sort(),nodeCount:Z,animations:w.map(I=>I.name),morphTargetNames:f.current})},0);return()=>clearTimeout(L)},[p,w]),e.useImperativeHandle(l,()=>({playAnimation:R,stopAnimation:k,getAnimationNames:g,getGroup:()=>b.current,setMorphTarget:S,getMorphTargetNames:()=>f.current,setMeshVisibility:D,getMeshNames:x,setMaterialColor:T,getMaterialNames:v,getMaterialColor:E,setMaterialTextures:j,clearMaterialTextures:N}));const W=e.useMemo(()=>({scene:p,getBone:L=>p.getObjectByName(L)||null}),[p]),B=typeof o=="number"?[o,o,o]:o;return A.jsx(re.Provider,{value:W,children:A.jsxs("group",{ref:b,position:t,rotation:r,scale:B,children:[A.jsx("primitive",{object:p}),c]})})});V.displayName="AnimatedModel";V.preload=a=>{O.useGLTF.preload(a)};const ae=e.forwardRef(({url:a,position:t=[0,0,0],rotation:r=[0,0,0],scale:o=1,morphTargets:u,meshVisibility:d,materialColors:M,materialTextures:y,onMorphTargetsFound:h,onLoad:s,onError:n},c)=>{const{scene:l}=O.useGLTF(a),b=e.useRef(s);b.current=s;const f=e.useRef(h);f.current=h;const i=e.useMemo(()=>l.clone(),[l]),{setMorphTarget:m,getMorphTargetNames:w,getMorphTargetValues:p}=q(i,u),{setMeshVisibility:R,getMeshNames:k}=U(i,d),{setMaterialColor:g,getMaterialNames:S,getMaterialColor:D}=z(i,M),{setMaterialTextures:x,clearMaterialTextures:T}=X(i,y);e.useEffect(()=>{var j;const E=w();E.length>0&&((j=f.current)==null||j.call(f,E))},[i,w]),e.useEffect(()=>{var B;if(!i)return;const E=[],j=new Set,N=[];let W=0;i.traverse(L=>{if(W++,L.type==="Bone"&&N.push(L.name),L.isMesh){const $=L;E.push($.name),$.castShadow=!0,$.receiveShadow=!0,(Array.isArray($.material)?$.material:[$.material]).forEach(G=>j.add(G.name))}}),(B=b.current)==null||B.call(b,{meshes:E.sort(),materials:Array.from(j).sort(),bones:N.sort(),nodeCount:W})},[i]),e.useImperativeHandle(c,()=>({setMorphTarget:m,getMorphTargetNames:w,getMorphTargetValues:p,setMeshVisibility:R,getMeshNames:k,setMaterialColor:g,getMaterialNames:S,getMaterialColor:D,setMaterialTextures:x,clearMaterialTextures:T}));const v=typeof o=="number"?[o,o,o]:o;return A.jsx("group",{position:t,rotation:r,scale:v,children:A.jsx("primitive",{object:i})})});ae.displayName="MorphableModel";function pe({children:a,bone:t,position:r=[0,0,0],rotation:o=[0,0,0],scale:u=1}){const{getBone:d}=me(),[M,y]=e.useState(null);if(e.useEffect(()=>{const s=d(t);s?y(s):console.warn(`Bone "${t}" not found in model`)},[t,d]),!M)return null;const h=typeof u=="number"?[u,u,u]:u;return _.createPortal(A.jsx("group",{position:r,rotation:o,scale:h,children:a}),M)}function de(a,t){const{scene:r,animations:o}=O.useGLTF(a),u=e.useMemo(()=>Y.clone(r),[r]);return e.useEffect(()=>{var s;if(!u)return;const d=[],M=[],y=new Set;let h=0;u.traverse(n=>{if(h++,n.type==="Bone"&&d.push(n.name),n.isMesh){const c=n;M.push(c.name),c.castShadow=!0,c.receiveShadow=!0,(Array.isArray(c.material)?c.material:[c.material]).forEach(b=>{y.add(b.name),b.shadowSide=C.DoubleSide})}}),(s=t==null?void 0:t.onLoad)==null||s.call(t,{meshes:M.sort(),materials:Array.from(y).sort(),bones:d.sort(),nodeCount:h})},[u,t]),{scene:u,animations:o}}function he(a){O.useGLTF.preload(a)}exports.AnimatedModel=V;exports.BoneAttachment=pe;exports.Model=ee;exports.MorphableModel=ae;exports.Scene3D=fe;exports.preloadModel=he;exports.useAnimationController=te;exports.useClonedModel=de;exports.useMaterialColor=z;exports.useMaterialTexture=X;exports.useMeshVisibility=U;exports.useMorphTargets=q;
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("react"),O=require("@react-three/drei"),oe=require("three"),ae=require("three/examples/jsm/utils/SkeletonUtils.js"),B=require("@react-three/fiber"),ne=require("three/examples/jsm/loaders/KTX2Loader.js"),w=require("react/jsx-runtime");function J(s){const t=Object.create(null,{[Symbol.toStringTag]:{value:"Module"}});if(s){for(const r in s)if(r!=="default"){const l=Object.getOwnPropertyDescriptor(s,r);Object.defineProperty(t,r,l.get?l:{enumerable:!0,get:()=>s[r]})}}return t.default=s,Object.freeze(t)}const E=J(oe),Q=J(ae);function le({position:s,controlsConfig:t}){const{camera:r}=B.useThree(),l=e.useRef(null);return e.useEffect(()=>{s&&r&&(r.position.set(s[0],s[1],s[2]),r.updateProjectionMatrix(),l.current&&(l.current.target.set(0,0,0),l.current.update()))},[s,r]),w.jsx(O.OrbitControls,{ref:l,makeDefault:!0,enabled:t.enabled,enablePan:t.enablePan,enableZoom:t.enableZoom,enableRotate:t.enableRotate,minDistance:t.minDistance,maxDistance:t.maxDistance,minPolarAngle:t.minPolarAngle,maxPolarAngle:t.maxPolarAngle,autoRotate:t.autoRotate,autoRotateSpeed:t.autoRotateSpeed})}function ie({background:s}){const t=s==null?void 0:s.startsWith("#");return s?t?w.jsx("color",{attach:"background",args:[s]}):w.jsx(ce,{url:s}):null}function ce({url:s}){const t=O.useTexture(s);return e.useMemo(()=>{t.colorSpace=E.SRGBColorSpace},[t]),w.jsx("primitive",{attach:"background",object:t})}function ue({config:s}){switch(s.type){case"spot":{const{position:t=[5,10,5],intensity:r=50,castShadow:l=!0,angle:p=Math.PI/6,penumbra:g=.5,decay:T=2,distance:S=0,color:h="#ffffff"}=s;return w.jsx("spotLight",{position:t,intensity:r,castShadow:l,angle:p,penumbra:g,decay:T,distance:S,color:h})}case"point":{const{position:t=[0,5,0],intensity:r=20,color:l="#ffffff",distance:p=0,decay:g=2,castShadow:T=!1}=s;return w.jsx("pointLight",{position:t,intensity:r,color:l,distance:p,decay:g,castShadow:T})}case"directional":{const{position:t=[10,20,10],intensity:r=40,color:l="#ffffff",castShadow:p=!0}=s;return w.jsx("directionalLight",{position:t,intensity:r,color:l,castShadow:p})}case"hemisphere":{const{skyColor:t="#ffffff",groundColor:r="#444444",intensity:l=.5,position:p=[0,10,0]}=s,g=e.useMemo(()=>new E.Color(t),[t]),T=e.useMemo(()=>new E.Color(r),[r]);return w.jsx("hemisphereLight",{args:[g,T,l],position:p})}case"ambient":{const{intensity:t=.5,color:r="#ffffff"}=s;return w.jsx("ambientLight",{intensity:t,color:r})}default:return null}}function pe({children:s,camera:t={},controls:r={},background:l,shadows:p=!0,lights:g=[{type:"ambient",intensity:.5},{type:"spot",position:[5,10,5],intensity:50,castShadow:!0}],contactShadows:T=!0,style:S,className:h}){const o={position:t.position||[0,2,5],fov:t.fov||45},a={enabled:r.enabled??!0,enablePan:r.enablePan??!0,enableZoom:r.enableZoom??!0,enableRotate:r.enableRotate??!0,minDistance:r.minDistance,maxDistance:r.maxDistance,minPolarAngle:r.minPolarAngle,maxPolarAngle:r.maxPolarAngle,autoRotate:r.autoRotate??!1,autoRotateSpeed:r.autoRotateSpeed??2},c=typeof T=="object"?{position:T.position||[0,-1,0],opacity:T.opacity??.5,blur:T.blur??2}:T?{position:[0,-1,0],opacity:.5,blur:2}:null;return w.jsx("div",{style:S,className:h,children:w.jsxs(B.Canvas,{shadows:p,camera:{position:o.position,fov:o.fov},style:{width:"100%",height:"100%"},children:[w.jsx(e.Suspense,{fallback:null,children:w.jsx(ie,{background:l})}),g.map((m,b)=>w.jsx(ue,{config:m},`${m.type}-${b}`)),w.jsx(e.Suspense,{fallback:null,children:s}),w.jsx(le,{position:o.position,controlsConfig:a}),c&&w.jsx(O.ContactShadows,{position:c.position,opacity:c.opacity,blur:c.blur})]})})}function G(s,t){const r=e.useRef(new Map),l=e.useRef({}),p=e.useRef({});e.useEffect(()=>{t&&(l.current={...t})},[t]),e.useEffect(()=>{if(!s)return;const h=new Map;s.traverse(o=>{if(o.isMesh){const a=o;a.name&&h.set(a.name,a)}}),r.current=h},[s]),B.useFrame(()=>{const h=r.current;if(h.size===0)return;const o={...l.current,...p.current};for(const[a,c]of Object.entries(o)){const m=h.get(a);m&&m.visible!==c&&(m.visible=c)}});const g=e.useCallback((h,o)=>{p.current[h]=o;const a=r.current.get(h);a&&(a.visible=o)},[]),T=e.useCallback(()=>Array.from(r.current.keys()).sort(),[]),S=e.useCallback(()=>{const h={};return r.current.forEach((o,a)=>{h[a]=o.visible}),h},[]);return{setMeshVisibility:g,getMeshNames:T,getMeshVisibility:S}}function K(s,t){const r=e.useRef(new Map),l=e.useRef(new Map);e.useEffect(()=>{if(!s)return;const o=new Map;s.traverse(a=>{if(a.isMesh){const c=a;(Array.isArray(c.material)?c.material:[c.material]).forEach(b=>{b.name&&!o.has(b.name)&&o.set(b.name,b)})}}),r.current=o,t&&Object.entries(t).forEach(([a,c])=>{const m=o.get(a);if(m&&!Array.isArray(m)){const b=p(c);m.color&&m.color.copy(b),l.current.set(a,b)}})},[s,t]),B.useFrame(()=>{l.current.forEach((o,a)=>{const c=r.current.get(a);if(c&&!Array.isArray(c)){const m=c;m.color&&!m.color.equals(o)&&(m.color.copy(o),m.needsUpdate=!0)}})});const p=o=>typeof o=="string"?new E.Color(o):new E.Color(o[0],o[1],o[2]),g=e.useCallback((o,a)=>{const c=p(a);l.current.set(o,c);const m=r.current.get(o);if(m&&!Array.isArray(m)){const b=m;b.color&&(b.color.copy(c),b.needsUpdate=!0)}},[]),T=e.useCallback(()=>Array.from(r.current.keys()).sort(),[]),S=e.useCallback(o=>{const a=r.current.get(o);if(a&&!Array.isArray(a)){const c=a;if(c.color)return"#"+c.color.getHexString()}return null},[]),h=e.useCallback(()=>{const o={};return r.current.forEach((a,c)=>{if(!Array.isArray(a)){const m=a;m.color&&(o[c]="#"+m.color.getHexString())}}),o},[]);return{setMaterialColor:g,getMaterialNames:T,getMaterialColor:S,getAllMaterialColors:h}}function U(s,t){const{gl:r}=B.useThree(),l=e.useRef(new Map),p=e.useRef(new E.TextureLoader),g=e.useRef(null),T=e.useRef(new Map),S=e.useRef(new Set),h=e.useRef("");e.useEffect(()=>{if(!g.current)try{const n=typeof WebAssembly=="object"&&typeof WebAssembly.validate=="function";console.log("[useMaterialTexture] Browser features:"),console.log(` WebAssembly supported: ${n}`),console.log(` WebGL2: ${r.capabilities.isWebGL2}`);const i=new ne.KTX2Loader;i.setTranscoderPath("/basis/"),i.detectSupport(r),g.current=i,console.log("[useMaterialTexture] ✅ KTX2Loader initialized"),console.log("[useMaterialTexture] Transcoder path: /basis/"),fetch("/basis/basis_transcoder.wasm").then(f=>{f.ok?console.log("[useMaterialTexture] ✅ basis_transcoder.wasm is accessible"):console.error("[useMaterialTexture] ❌ basis_transcoder.wasm returned",f.status)}).catch(f=>{console.error("[useMaterialTexture] ❌ Failed to check basis_transcoder.wasm:",f)}),n||(console.warn("[useMaterialTexture] ⚠️ WebAssembly not supported! KTX2 will fallback to CPU decoding (slow)"),console.warn("[useMaterialTexture] ⚠️ Recommend using WebP instead of KTX2 for this browser"))}catch(n){console.error("[useMaterialTexture] ❌ Failed to initialize KTX2Loader:",n)}return()=>{g.current&&(g.current.dispose(),g.current=null)}},[r]),e.useEffect(()=>{if(!s)return;const n=new Map;s.traverse(i=>{if(i.isMesh){const f=i;(Array.isArray(f.material)?f.material:[f.material]).forEach(d=>{d.name&&!n.has(d.name)&&n.set(d.name,d)})}}),l.current=n},[s]);const o=e.useCallback(n=>n.toLowerCase().endsWith(".ktx2")?g.current?g.current:(console.warn("[useMaterialTexture] KTX2Loader not initialized, falling back to TextureLoader"),p.current):p.current,[]);e.useEffect(()=>{if(!t||!s)return;const n=l.current;if(n.size===0)return;const i=JSON.stringify(t);if(h.current===i){console.log("[useMaterialTexture] Textures unchanged, skipping reload");return}h.current=i;let f=!1;const x=new Map;Object.entries(t).forEach(([M,y])=>{x.set(M,typeof y=="string"?{map:y}:y)}),console.log("[useMaterialTexture] Starting texture load for",x.size,"materials");const d=[];x.forEach((M,y)=>{var A,k;const D=n.get(y);if(!D||!D.isMeshStandardMaterial){console.warn(`[useMaterialTexture] Material "${y}" not found or not MeshStandardMaterial`);return}const u=D;console.log(`[useMaterialTexture] 📋 Material "${y}" BEFORE reset:`),console.log(` - map: ${u.map?"EXISTS":"null"} ${u.map?`(uuid: ${u.map.uuid})`:""}`),console.log(` - normalMap: ${u.normalMap?"EXISTS":"null"}`),console.log(` - roughnessMap: ${u.roughnessMap?"EXISTS":"null"}`),console.log(` - metalnessMap: ${u.metalnessMap?"EXISTS":"null"}`),console.log(` - emissiveMap: ${u.emissiveMap?"EXISTS":"null"}`),console.log(` - aoMap: ${u.aoMap?"EXISTS":"null"}`),console.log(` - color: ${(A=u.color)==null?void 0:A.getHexString()}`),console.log(` - emissive: ${(k=u.emissive)==null?void 0:k.getHexString()}`),console.log(` - material uuid: ${u.uuid}`),u.map&&(console.log(`[useMaterialTexture] 🗑️ Disposing old map (uuid: ${u.map.uuid})`),u.map.dispose()),u.normalMap&&(console.log("[useMaterialTexture] 🗑️ Disposing old normalMap"),u.normalMap.dispose()),u.roughnessMap&&(console.log("[useMaterialTexture] 🗑️ Disposing old roughnessMap"),u.roughnessMap.dispose()),u.metalnessMap&&(console.log("[useMaterialTexture] 🗑️ Disposing old metalnessMap"),u.metalnessMap.dispose()),u.emissiveMap&&(console.log("[useMaterialTexture] 🗑️ Disposing old emissiveMap"),u.emissiveMap.dispose()),u.aoMap&&(console.log("[useMaterialTexture] 🗑️ Disposing old aoMap"),u.aoMap.dispose()),u.map=null,u.normalMap=null,u.roughnessMap=null,u.metalnessMap=null,u.emissiveMap=null,u.emissive=new E.Color(0),u.emissiveIntensity=0,u.aoMap=null,u.needsUpdate=!0,u.color=new E.Color(16777215),console.log(`[useMaterialTexture] ✅ Material "${y}" AFTER reset: all maps cleared and disposed`),Object.entries(M).forEach(([$,C])=>{if(!C)return;const j=`${y}_${$}_${C}`,X=T.current.get(j);if(X){console.log(`[useMaterialTexture] Using cached texture: ${y}.${$}`),f||a(u,$,X);return}if(S.current.has(j)){console.log(`[useMaterialTexture] Skipping already loading: ${y}.${$}`);return}d.push({materialName:y,textureType:$,url:C,material:u})})}),console.log(`[useMaterialTexture] Queued ${d.length} textures to load`);let v=0;const R=()=>{if(f||v>=d.length){console.log("[useMaterialTexture] ✅ All textures loaded"),console.log("[useMaterialTexture] 📊 FINAL material states:"),n.forEach((A,k)=>{var C,j;const $=A;console.log(` Material "${k}":`),console.log(` - map: ${$.map?"EXISTS":"null"} ${$.map?`(colorSpace: ${$.map.colorSpace})`:""}`),console.log(` - normalMap: ${$.normalMap?"EXISTS":"null"}`),console.log(` - roughnessMap: ${$.roughnessMap?"EXISTS":"null"}`),console.log(` - metalnessMap: ${$.metalnessMap?"EXISTS":"null"}`),console.log(` - emissiveMap: ${$.emissiveMap?"EXISTS":"null"}`),console.log(` - color: ${(C=$.color)==null?void 0:C.getHexString()}`),console.log(` - emissive: ${(j=$.emissive)==null?void 0:j.getHexString()}`),console.log(` - emissiveIntensity: ${$.emissiveIntensity}`),console.log(` - roughness: ${$.roughness}`),console.log(` - metalness: ${$.metalness}`)});return}const M=d[v++],y=`${M.materialName}_${M.textureType}_${M.url}`;console.log(`[useMaterialTexture] Loading (${v}/${d.length}): ${M.materialName}.${M.textureType} from ${M.url}`),S.current.add(y);const D=o(M.url),u=M.url.toLowerCase().endsWith(".ktx2")?"KTX2Loader":"TextureLoader";console.log(`[useMaterialTexture] Using ${u} for ${M.url}`),D.load(M.url,A=>{var j,X,P,I,L,W;if(S.current.delete(y),f){console.log(`[useMaterialTexture] Disposed, cleaning up texture: ${M.materialName}.${M.textureType}`),A.dispose();return}const k=((j=A.image)==null?void 0:j.width)||((P=(X=A.source)==null?void 0:X.data)==null?void 0:P.width)||"unknown",$=((I=A.image)==null?void 0:I.height)||((W=(L=A.source)==null?void 0:L.data)==null?void 0:W.height)||"unknown";console.log(`[useMaterialTexture] ✅ Loaded: ${M.materialName}.${M.textureType} (${k}x${$})`),A.colorSpace=M.textureType==="map"||M.textureType==="emissiveMap"?E.SRGBColorSpace:E.NoColorSpace,A.wrapS=E.RepeatWrapping,A.wrapT=E.RepeatWrapping,A.flipY=!1,T.current.set(y,A),a(M.material,M.textureType,A);const C=M.url.endsWith(".ktx2")?500:150;console.log(`[useMaterialTexture] Waiting111111 ${C}ms before next texture...`),setTimeout(R,0)},A=>{if(A.lengthComputable){const k=Math.round(A.loaded/A.total*100);k%25===0&&console.log(`[useMaterialTexture] Progress ${M.materialName}.${M.textureType}: ${k}%`)}},A=>{S.current.delete(y),f||(console.error(`[useMaterialTexture] ❌ Failed to load ${M.materialName}.${M.textureType} from ${M.url}`),console.error("[useMaterialTexture] Error details:",A),console.error("[useMaterialTexture] Loader type:",u),M.url.endsWith(".ktx2")&&(console.error("[useMaterialTexture] KTX2 error - file may not be in Basis Universal format!"),console.error("[useMaterialTexture] Make sure files are created with: gltf-transform etc1s input.png output.ktx2"))),setTimeout(R,100)})};return R(),()=>{console.log("[useMaterialTexture] Cleanup: disposed"),f=!0}},[s,t,o]);const a=(n,i,f)=>{var x,d;switch(console.log(`[useMaterialTexture] 🎨 Applying texture: ${n.name}.${i} (texture uuid: ${f.uuid})`),i){case"map":n.map=f;break;case"normalMap":n.normalMap=f;break;case"roughnessMap":n.roughnessMap=f;break;case"metalnessMap":n.metalnessMap=f;break;case"emissiveMap":n.emissiveMap=f,n.emissive=new E.Color(16777215),n.emissiveIntensity=1;break;case"alphaMap":console.log(`[useMaterialTexture] Skipping alphaMap for "${n.name}" to prevent disappearing`);break;case"aoMap":n.aoMap=f;break}n.needsUpdate=!0,console.log(`[useMaterialTexture] ✅ Applied ${i} to ${n.name}, needsUpdate=true`),console.log(`[useMaterialTexture] 📊 Material "${n.name}" FINAL state:`),console.log(` - map: ${n.map?"EXISTS":"null"} ${n.map?`(uuid: ${n.map.uuid}, colorSpace: ${n.map.colorSpace})`:""}`),console.log(` - normalMap: ${n.normalMap?"EXISTS":"null"}`),console.log(` - roughnessMap: ${n.roughnessMap?"EXISTS":"null"}`),console.log(` - metalnessMap: ${n.metalnessMap?"EXISTS":"null"}`),console.log(` - emissiveMap: ${n.emissiveMap?"EXISTS":"null"}`),console.log(` - aoMap: ${n.aoMap?"EXISTS":"null"}`),console.log(` - color: ${(x=n.color)==null?void 0:x.getHexString()}`),console.log(` - emissive: ${(d=n.emissive)==null?void 0:d.getHexString()}`),console.log(` - emissiveIntensity: ${n.emissiveIntensity}`),console.log(` - roughness: ${n.roughness}`),console.log(` - metalness: ${n.metalness}`)},c=e.useCallback((n,i)=>{const f=typeof i=="string"?{map:i}:i,x=l.current.get(n);if(!x||!x.isMeshStandardMaterial){console.warn(`Material "${n}" not found or not MeshStandardMaterial`);return}const d=x;Object.entries(f).forEach(([v,R])=>{if(!R)return;const M=`${n}_${v}_${R}`,y=T.current.get(M);if(y){a(d,v,y);return}o(R).load(R,u=>{u.colorSpace=v==="map"||v==="emissiveMap"?E.SRGBColorSpace:E.NoColorSpace,u.wrapS=E.RepeatWrapping,u.wrapT=E.RepeatWrapping,T.current.set(M,u),a(d,v,u)},void 0,u=>{console.error(`Failed to load texture ${R}:`,u)})})},[o]),m=e.useCallback(()=>Array.from(l.current.keys()).sort(),[]),b=e.useCallback(n=>{const i=l.current.get(n);if(!i||!i.isMeshStandardMaterial)return;const f=i;f.map=null,f.normalMap=null,f.roughnessMap=null,f.metalnessMap=null,f.emissiveMap=null,f.alphaMap=null,f.aoMap=null,f.needsUpdate=!0},[]);return{setMaterialTextures:c,getMaterialNames:m,clearMaterialTextures:b}}function ee({url:s,position:t=[0,0,0],rotation:r=[0,0,0],scale:l=1,meshVisibility:p,materialColors:g,materialTextures:T,onLoad:S,onError:h}){const{scene:o}=O.useGLTF(s),a=e.useRef(S);a.current=S;const c=e.useMemo(()=>{const b=o.clone();console.log("[Model] 🔍 Scene clone created, checking materials...");const n=[],i=new Set,f=[];let x=0;return b.traverse(d=>{if(x++,d.type==="Bone"&&f.push(d.name),d.isMesh){const v=d;n.push(v.name),v.castShadow=!0,v.receiveShadow=!0,(Array.isArray(v.material)?v.material:[v.material]).forEach(M=>{i.add(M.name),console.log(`[Model] 📦 Material found: "${M.name}" (uuid: ${M.uuid})`);const y=M;console.log(` - Has map: ${y.map?"YES":"NO"}`),console.log(` - Has normalMap: ${y.normalMap?"YES":"NO"}`),console.log(` - Has roughnessMap: ${y.roughnessMap?"YES":"NO"}`),console.log(` - Has metalnessMap: ${y.metalnessMap?"YES":"NO"}`)})}}),console.log(`[Model] 📊 Clone summary: ${n.length} meshes, ${i.size} materials`),setTimeout(()=>{var d;(d=a.current)==null||d.call(a,{meshes:n.sort(),materials:Array.from(i).sort(),bones:f.sort(),nodeCount:x})},0),b},[o]);G(c,p),K(c,g),U(c,T);const m=typeof l=="number"?[l,l,l]:l;return w.jsx("group",{position:t,rotation:r,scale:m,children:w.jsx("primitive",{object:c})})}ee.preload=s=>{O.useGLTF.preload(s)};function te(s,t,r){const{actions:l,names:p}=O.useAnimations(s,t),g=e.useRef(null),T=e.useRef(r==null?void 0:r.defaultAnimation);e.useEffect(()=>{if(p.length===0)return;const a=T.current;let c=p[0];if(a){const b=p.find(n=>n===a||n.includes(a));b&&(c=b)}const m=l[c];m&&(m.reset().fadeIn(.5).play(),g.current=m)},[l,p]);const S=e.useCallback((a,c)=>{const{loop:m=!1,crossFadeDuration:b=.2,restoreDefault:n=!0}=c||{};let i=l[a];if(!i){const x=Object.keys(l).find(d=>d.toLowerCase().includes(a.toLowerCase())||a.toLowerCase().includes(d.toLowerCase()));x&&(i=l[x])}if(!i){console.warn(`Animation "${a}" not found. Available: ${p.join(", ")}`);return}const f=g.current;if(!(f===i&&i.isRunning())&&(f&&f!==i&&f.fadeOut(b),i.reset(),i.fadeIn(b),i.setLoop(m?E.LoopRepeat:E.LoopOnce,m?1/0:1),i.clampWhenFinished=!m,i.play(),m||i.getMixer().update(0),g.current=i,n&&!m&&T.current)){const x=i.getMixer(),d=v=>{if(v.action===i){x.removeEventListener("finished",d);const R=l[T.current];R&&(i.fadeOut(b),R.reset().fadeIn(b).play(),g.current=R)}};x.addEventListener("finished",d)}},[l,p]),h=e.useCallback(()=>{var a;(a=g.current)==null||a.fadeOut(.2),g.current=null},[]),o=e.useCallback(()=>p,[p]);return{playAnimation:S,stopAnimation:h,getAnimationNames:o,actions:l}}function z(s,t){const r=e.useRef(t||{}),l=e.useRef([]),p=e.useRef([]);e.useEffect(()=>{const h=new Set,o=[];s.traverse(a=>{a instanceof E.Mesh&&a.morphTargetDictionary&&a.morphTargetInfluences&&(o.push(a),Object.keys(a.morphTargetDictionary).forEach(c=>{h.add(c)}))}),l.current=Array.from(h).sort(),p.current=o},[s]),B.useFrame(()=>{const h=r.current;p.current.forEach(o=>{!o.morphTargetDictionary||!o.morphTargetInfluences||Object.entries(h).forEach(([a,c])=>{const m=o.morphTargetDictionary[a];m!==void 0&&(o.morphTargetInfluences[m]=c)})})}),e.useEffect(()=>{t&&(r.current={...t})},[t]);const g=e.useCallback((h,o)=>{r.current[h]=Math.max(0,Math.min(1,o))},[]),T=e.useCallback(()=>l.current,[]),S=e.useCallback(()=>({...r.current}),[]);return{setMorphTarget:g,getMorphTargetNames:T,getMorphTargetValues:S}}const re=e.createContext(null);function me(){const s=e.useContext(re);if(!s)throw new Error("BoneAttachment must be used within an AnimatedModel");return s}const q=e.forwardRef(({url:s,position:t=[0,0,0],rotation:r=[0,0,0],scale:l=1,defaultAnimation:p,morphTargets:g,meshVisibility:T,materialColors:S,materialTextures:h,onLoad:o,onError:a,children:c},m)=>{const b=e.useRef(null),n=e.useRef([]),i=e.useRef(o);i.current=o;const{scene:f,animations:x}=O.useGLTF(s),d=e.useMemo(()=>Q.clone(f),[f]),{playAnimation:v,stopAnimation:R,getAnimationNames:M}=te(x,d,{defaultAnimation:p}),{setMorphTarget:y}=z(d,g),{setMeshVisibility:D,getMeshNames:u}=G(d,T),{setMaterialColor:A,getMaterialNames:k,getMaterialColor:$}=K(d,S),{setMaterialTextures:C,clearMaterialTextures:j}=U(d,h);e.useEffect(()=>{if(!d)return;const I=setTimeout(()=>{var Z;const L=[],W=[],_=new Set,V=new Set;let Y=0;d.traverse(F=>{if(Y++,F.type==="Bone"&&L.push(F.name),F.isMesh){const N=F;W.push(N.name),N.castShadow=!0,N.receiveShadow=!0,(Array.isArray(N.material)?N.material:[N.material]).forEach(H=>{_.add(H.name),H.shadowSide=E.DoubleSide}),N.morphTargetDictionary&&Object.keys(N.morphTargetDictionary).forEach(H=>{V.add(H)})}}),n.current=Array.from(V).sort(),(Z=i.current)==null||Z.call(i,{meshes:W.sort(),materials:Array.from(_).sort(),bones:L.sort(),nodeCount:Y,animations:x.map(F=>F.name),morphTargetNames:n.current})},0);return()=>clearTimeout(I)},[d,x]),e.useImperativeHandle(m,()=>({playAnimation:v,stopAnimation:R,getAnimationNames:M,getGroup:()=>b.current,setMorphTarget:y,getMorphTargetNames:()=>n.current,setMeshVisibility:D,getMeshNames:u,setMaterialColor:A,getMaterialNames:k,getMaterialColor:$,setMaterialTextures:C,clearMaterialTextures:j}));const X=e.useMemo(()=>({scene:d,getBone:I=>d.getObjectByName(I)||null}),[d]),P=typeof l=="number"?[l,l,l]:l;return w.jsx(re.Provider,{value:X,children:w.jsxs("group",{ref:b,position:t,rotation:r,scale:P,children:[w.jsx("primitive",{object:d}),c]})})});q.displayName="AnimatedModel";q.preload=s=>{O.useGLTF.preload(s)};const se=e.forwardRef(({url:s,position:t=[0,0,0],rotation:r=[0,0,0],scale:l=1,morphTargets:p,meshVisibility:g,materialColors:T,materialTextures:S,onMorphTargetsFound:h,onLoad:o,onError:a},c)=>{const{scene:m}=O.useGLTF(s),b=e.useRef(o);b.current=o;const n=e.useRef(h);n.current=h;const i=e.useMemo(()=>m.clone(),[m]),{setMorphTarget:f,getMorphTargetNames:x,getMorphTargetValues:d}=z(i,p),{setMeshVisibility:v,getMeshNames:R}=G(i,g),{setMaterialColor:M,getMaterialNames:y,getMaterialColor:D}=K(i,T),{setMaterialTextures:u,clearMaterialTextures:A}=U(i,S);e.useEffect(()=>{var C;const $=x();$.length>0&&((C=n.current)==null||C.call(n,$))},[i,x]),e.useEffect(()=>{var P;if(!i)return;const $=[],C=new Set,j=[];let X=0;i.traverse(I=>{if(X++,I.type==="Bone"&&j.push(I.name),I.isMesh){const L=I;$.push(L.name),L.castShadow=!0,L.receiveShadow=!0,(Array.isArray(L.material)?L.material:[L.material]).forEach(_=>C.add(_.name))}}),(P=b.current)==null||P.call(b,{meshes:$.sort(),materials:Array.from(C).sort(),bones:j.sort(),nodeCount:X})},[i]),e.useImperativeHandle(c,()=>({setMorphTarget:f,getMorphTargetNames:x,getMorphTargetValues:d,setMeshVisibility:v,getMeshNames:R,setMaterialColor:M,getMaterialNames:y,getMaterialColor:D,setMaterialTextures:u,clearMaterialTextures:A}));const k=typeof l=="number"?[l,l,l]:l;return w.jsx("group",{position:t,rotation:r,scale:k,children:w.jsx("primitive",{object:i})})});se.displayName="MorphableModel";function fe({children:s,bone:t,position:r=[0,0,0],rotation:l=[0,0,0],scale:p=1}){const{getBone:g}=me(),[T,S]=e.useState(null);if(e.useEffect(()=>{const o=g(t);o?S(o):console.warn(`Bone "${t}" not found in model`)},[t,g]),!T)return null;const h=typeof p=="number"?[p,p,p]:p;return B.createPortal(w.jsx("group",{position:r,rotation:l,scale:h,children:s}),T)}function de(s,t){const{scene:r,animations:l}=O.useGLTF(s),p=e.useMemo(()=>Q.clone(r),[r]);return e.useEffect(()=>{var o;if(!p)return;const g=[],T=[],S=new Set;let h=0;p.traverse(a=>{if(h++,a.type==="Bone"&&g.push(a.name),a.isMesh){const c=a;T.push(c.name),c.castShadow=!0,c.receiveShadow=!0,(Array.isArray(c.material)?c.material:[c.material]).forEach(b=>{S.add(b.name),b.shadowSide=E.DoubleSide})}}),(o=t==null?void 0:t.onLoad)==null||o.call(t,{meshes:T.sort(),materials:Array.from(S).sort(),bones:g.sort(),nodeCount:h})},[p,t]),{scene:p,animations:l}}function Me(s){O.useGLTF.preload(s)}exports.AnimatedModel=q;exports.BoneAttachment=fe;exports.Model=ee;exports.MorphableModel=se;exports.Scene3D=pe;exports.preloadModel=Me;exports.useAnimationController=te;exports.useClonedModel=de;exports.useMaterialColor=K;exports.useMaterialTexture=U;exports.useMeshVisibility=G;exports.useMorphTargets=z;
2
2
  //# sourceMappingURL=mbt-3d.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"mbt-3d.cjs","sources":["../src/lib/components/Scene3D/Scene3D.tsx","../src/lib/hooks/useMeshVisibility.ts","../src/lib/hooks/useMaterialColor.ts","../src/lib/hooks/useMaterialTexture.ts","../src/lib/components/Model/Model.tsx","../src/lib/hooks/useAnimationController.ts","../src/lib/hooks/useMorphTargets.ts","../src/lib/components/AnimatedModel/AnimatedModelContext.tsx","../src/lib/components/AnimatedModel/AnimatedModel.tsx","../src/lib/components/MorphableModel/MorphableModel.tsx","../src/lib/components/BoneAttachment/BoneAttachment.tsx","../src/lib/hooks/useClonedModel.ts"],"sourcesContent":["import { Suspense, useMemo, useRef, useEffect } from 'react';\r\nimport { Canvas, useThree } from '@react-three/fiber';\r\nimport { OrbitControls, ContactShadows, useTexture } from '@react-three/drei';\r\nimport * as THREE from 'three';\r\nimport type { Scene3DProps, LightConfigUnion } from '../../types';\r\nimport type { OrbitControls as OrbitControlsType } from 'three-stdlib';\r\n\r\n/**\r\n * CameraController - Updates camera position when props change\r\n */\r\nfunction CameraController({ \r\n position,\r\n controlsConfig \r\n}: { \r\n position: [number, number, number];\r\n controlsConfig: any;\r\n}) {\r\n const { camera } = useThree();\r\n const controlsRef = useRef<OrbitControlsType | null>(null);\r\n\r\n useEffect(() => {\r\n if (position && camera) {\r\n camera.position.set(position[0], position[1], position[2]);\r\n camera.updateProjectionMatrix();\r\n \r\n // Reset OrbitControls target to origin\r\n if (controlsRef.current) {\r\n controlsRef.current.target.set(0, 0, 0);\r\n controlsRef.current.update();\r\n }\r\n }\r\n }, [position, camera]);\r\n\r\n return (\r\n <OrbitControls\r\n ref={controlsRef}\r\n makeDefault\r\n enabled={controlsConfig.enabled}\r\n enablePan={controlsConfig.enablePan}\r\n enableZoom={controlsConfig.enableZoom}\r\n enableRotate={controlsConfig.enableRotate}\r\n minDistance={controlsConfig.minDistance}\r\n maxDistance={controlsConfig.maxDistance}\r\n minPolarAngle={controlsConfig.minPolarAngle}\r\n maxPolarAngle={controlsConfig.maxPolarAngle}\r\n autoRotate={controlsConfig.autoRotate}\r\n autoRotateSpeed={controlsConfig.autoRotateSpeed}\r\n />\r\n );\r\n}\r\n\r\n/**\r\n * Background component that handles both image URLs and solid colors\r\n */\r\nfunction Background({ background }: { background?: string }) {\r\n // Check if it's a color (starts with #) or an image URL\r\n const isColor = background?.startsWith('#');\r\n \r\n if (!background) return null;\r\n \r\n if (isColor) {\r\n return <color attach=\"background\" args={[background]} />;\r\n }\r\n \r\n // It's an image URL\r\n return <BackgroundImage url={background} />;\r\n}\r\n\r\nfunction BackgroundImage({ url }: { url: string }) {\r\n const texture = useTexture(url);\r\n \r\n // Configure texture for background\r\n useMemo(() => {\r\n texture.colorSpace = THREE.SRGBColorSpace;\r\n }, [texture]);\r\n \r\n return <primitive attach=\"background\" object={texture} />;\r\n}\r\n\r\n/**\r\n * LightRenderer - Renders different types of lights based on config\r\n */\r\nfunction LightRenderer({ config }: { config: LightConfigUnion }) {\r\n switch (config.type) {\r\n case 'spot': {\r\n const {\r\n position = [5, 10, 5],\r\n intensity = 50,\r\n castShadow = true,\r\n angle = Math.PI / 6,\r\n penumbra = 0.5,\r\n decay = 2,\r\n distance = 0,\r\n color = '#ffffff',\r\n } = config;\r\n return (\r\n <spotLight\r\n position={position}\r\n intensity={intensity}\r\n castShadow={castShadow}\r\n angle={angle}\r\n penumbra={penumbra}\r\n decay={decay}\r\n distance={distance}\r\n color={color}\r\n />\r\n );\r\n }\r\n \r\n case 'point': {\r\n const {\r\n position = [0, 5, 0],\r\n intensity = 20,\r\n color = '#ffffff',\r\n distance = 0,\r\n decay = 2,\r\n castShadow = false,\r\n } = config;\r\n return (\r\n <pointLight\r\n position={position}\r\n intensity={intensity}\r\n color={color}\r\n distance={distance}\r\n decay={decay}\r\n castShadow={castShadow}\r\n />\r\n );\r\n }\r\n \r\n case 'directional': {\r\n const {\r\n position = [10, 20, 10],\r\n intensity = 40,\r\n color = '#ffffff',\r\n castShadow = true,\r\n } = config;\r\n return (\r\n <directionalLight\r\n position={position}\r\n intensity={intensity}\r\n color={color}\r\n castShadow={castShadow}\r\n />\r\n );\r\n }\r\n \r\n case 'hemisphere': {\r\n const {\r\n skyColor = '#ffffff',\r\n groundColor = '#444444',\r\n intensity = 0.5,\r\n position = [0, 10, 0],\r\n } = config;\r\n \r\n const skyColorObj = useMemo(() => new THREE.Color(skyColor), [skyColor]);\r\n const groundColorObj = useMemo(() => new THREE.Color(groundColor), [groundColor]);\r\n \r\n return (\r\n <hemisphereLight\r\n args={[skyColorObj, groundColorObj, intensity]}\r\n position={position}\r\n />\r\n );\r\n }\r\n \r\n case 'ambient': {\r\n const {\r\n intensity = 0.5,\r\n color = '#ffffff',\r\n } = config;\r\n return (\r\n <ambientLight\r\n intensity={intensity}\r\n color={color}\r\n />\r\n );\r\n }\r\n \r\n default:\r\n return null;\r\n }\r\n}\r\n\r\n/**\r\n * Scene3D - Main 3D scene container with built-in canvas, lighting, and controls\r\n *\r\n * @remarks\r\n * This component wraps React Three Fiber's Canvas and provides:\r\n * - OrbitControls for camera rotation (preserves state between model changes)\r\n * - Configurable multi-light system (spot, point, directional, hemisphere, ambient)\r\n * - Optional background (image URL or hex color like \"#1a1a2e\")\r\n * - Optional contact shadows under models\r\n * - Shadow support\r\n *\r\n * @param children - React children to render inside the 3D scene\r\n * @param camera - Camera configuration object. Example: `{ position: [0, 2, 5], fov: 45 }`\r\n * @param camera.position - Camera position as [x, y, z]. Example: `[0, 2, 5]`. Default: `[0, 2, 5]`\r\n * @param camera.fov - Field of view in degrees. Example: `45`. Default: `45`\r\n * @param controls - OrbitControls configuration object. Example: `{ enablePan: false, minDistance: 2, maxDistance: 10 }`\r\n * @param controls.enabled - Enable/disable all controls. Default: `true`\r\n * @param controls.enablePan - Enable camera panning with middle mouse. Default: `true`\r\n * @param controls.enableZoom - Enable camera zoom with scroll wheel. Default: `true`\r\n * @param controls.enableRotate - Enable camera rotation with left mouse. Default: `true`\r\n * @param controls.minDistance - Minimum zoom distance. Example: `2`\r\n * @param controls.maxDistance - Maximum zoom distance. Example: `10`\r\n * @param controls.minPolarAngle - Minimum vertical rotation angle in radians. Example: `Math.PI / 4`\r\n * @param controls.maxPolarAngle - Maximum vertical rotation angle in radians. Example: `Math.PI / 1.8`\r\n * @param controls.autoRotate - Auto-rotate camera around the center. Default: `false`\r\n * @param controls.autoRotateSpeed - Auto-rotation speed. Default: `2`\r\n * @param background - Background: image URL (e.g., `\"/bg.jpg\"`) or hex color (e.g., `\"#1a1a2e\"`)\r\n * @param shadows - Enable shadow rendering. Default: `true`\r\n * @param lights - Array of light configurations. Supports spot, point, directional, hemisphere, and ambient lights. Default: `[{ type: 'ambient', intensity: 0.5 }, { type: 'spot', position: [5, 10, 5], intensity: 50, castShadow: true }]`\r\n * @param contactShadows - Contact shadows configuration. Can be `true` for default settings or object with custom settings. Example: `{ position: [0, -1, 0], opacity: 0.5, blur: 2 }`\r\n * @param contactShadows.position - Shadow position as [x, y, z]. Example: `[0, -1, 0]`. Default: `[0, -1, 0]`\r\n * @param contactShadows.opacity - Shadow opacity (0-1). Example: `0.5`. Default: `0.5`\r\n * @param contactShadows.blur - Shadow blur amount. Example: `2`. Default: `2`\r\n * @param style - Container style object. Example: `{ width: 400, height: 500 }`\r\n * @param className - Container CSS class name\r\n *\r\n * @example\r\n * Basic setup with default lighting:\r\n * ```tsx\r\n * <Scene3D\r\n * camera={{ position: [0, 2, 5], fov: 45 }}\r\n * background=\"#1a1a2e\"\r\n * shadows\r\n * style={{ width: 400, height: 500 }}\r\n * >\r\n * <AnimatedModel url=\"/model.glb\" position={[0, -1, 0]} />\r\n * </Scene3D>\r\n * ```\r\n *\r\n * @example\r\n * Multi-light setup for better illumination:\r\n * ```tsx\r\n * <Scene3D\r\n * camera={{ position: [0, 2, 5], fov: 45 }}\r\n * lights={[\r\n * { type: 'spot', position: [5, 10, 5], intensity: 50, castShadow: true },\r\n * { type: 'spot', position: [-5, 10, -5], intensity: 30, castShadow: false },\r\n * { type: 'point', position: [0, 5, 0], intensity: 20 },\r\n * { type: 'ambient', intensity: 0.3 }\r\n * ]}\r\n * >\r\n * <AnimatedModel url=\"/model.glb\" position={[0, -1, 0]} />\r\n * </Scene3D>\r\n * ```\r\n *\r\n * @example\r\n * Using different light types:\r\n * ```tsx\r\n * <Scene3D\r\n * lights={[\r\n * { type: 'directional', position: [10, 20, 10], intensity: 40, castShadow: true },\r\n * { type: 'hemisphere', skyColor: '#87CEEB', groundColor: '#362312', intensity: 0.5 },\r\n * { type: 'ambient', intensity: 0.2 }\r\n * ]}\r\n * >\r\n * <Model url=\"/model.glb\" />\r\n * </Scene3D>\r\n * ```\r\n */\r\nexport function Scene3D({\r\n children,\r\n camera = {},\r\n controls = {},\r\n background,\r\n shadows = true,\r\n lights = [\r\n { type: 'ambient', intensity: 0.5 },\r\n { type: 'spot', position: [5, 10, 5], intensity: 50, castShadow: true }\r\n ] as LightConfigUnion[],\r\n contactShadows = true,\r\n style,\r\n className,\r\n}: Scene3DProps) {\r\n const cameraConfig = {\r\n position: camera.position || [0, 2, 5],\r\n fov: camera.fov || 45,\r\n };\r\n\r\n const controlsConfig = {\r\n enabled: controls.enabled ?? true,\r\n enablePan: controls.enablePan ?? true,\r\n enableZoom: controls.enableZoom ?? true,\r\n enableRotate: controls.enableRotate ?? true,\r\n minDistance: controls.minDistance,\r\n maxDistance: controls.maxDistance,\r\n minPolarAngle: controls.minPolarAngle,\r\n maxPolarAngle: controls.maxPolarAngle,\r\n autoRotate: controls.autoRotate ?? false,\r\n autoRotateSpeed: controls.autoRotateSpeed ?? 2,\r\n };\r\n\r\n const contactShadowsConfig =\r\n typeof contactShadows === 'object'\r\n ? {\r\n position: contactShadows.position || [0, -1, 0],\r\n opacity: contactShadows.opacity ?? 0.5,\r\n blur: contactShadows.blur ?? 2,\r\n }\r\n : contactShadows\r\n ? { position: [0, -1, 0] as [number, number, number], opacity: 0.5, blur: 2 }\r\n : null;\r\n\r\n return (\r\n <div style={style} className={className}>\r\n <Canvas\r\n shadows={shadows}\r\n camera={{\r\n position: cameraConfig.position as [number, number, number],\r\n fov: cameraConfig.fov,\r\n }}\r\n style={{ width: '100%', height: '100%' }}\r\n >\r\n {/* Background */}\r\n <Suspense fallback={null}>\r\n <Background background={background} />\r\n </Suspense>\r\n\r\n {/* Lighting */}\r\n {lights.map((lightConfig, index) => (\r\n <LightRenderer key={`${lightConfig.type}-${index}`} config={lightConfig} />\r\n ))}\r\n\r\n {/* Content */}\r\n <Suspense fallback={null}>{children}</Suspense>\r\n\r\n {/* Controls - updates camera when props change */}\r\n <CameraController \r\n position={cameraConfig.position as [number, number, number]} \r\n controlsConfig={controlsConfig}\r\n />\r\n\r\n {/* Contact Shadows */}\r\n {contactShadowsConfig && (\r\n <ContactShadows\r\n position={contactShadowsConfig.position}\r\n opacity={contactShadowsConfig.opacity}\r\n blur={contactShadowsConfig.blur}\r\n />\r\n )}\r\n </Canvas>\r\n </div>\r\n );\r\n}\r\n","import { useEffect, useCallback, useRef } from 'react';\nimport { useFrame } from '@react-three/fiber';\nimport * as THREE from 'three';\n\n/**\n * Hook for managing mesh visibility in a 3D scene\n * \n * @param scene - The THREE.Object3D scene containing meshes\n * @param visibility - Reactive visibility state for meshes. Updates are applied every frame.\n * @returns Methods to control mesh visibility\n */\nexport function useMeshVisibility(\n scene: THREE.Object3D,\n visibility?: Record<string, boolean>\n) {\n const meshMapRef = useRef<Map<string, THREE.Mesh>>(new Map());\n // Store the latest visibility from props (kept in sync via useEffect)\n const visibilityRef = useRef<Record<string, boolean>>({});\n // Store imperative overrides set via setMeshVisibility()\n const overridesRef = useRef<Record<string, boolean>>({});\n\n // Keep visibilityRef in sync with the prop\n useEffect(() => {\n if (visibility) {\n visibilityRef.current = { ...visibility };\n }\n }, [visibility]);\n\n // Discover all meshes in the scene\n useEffect(() => {\n if (!scene) return;\n\n const meshMap = new Map<string, THREE.Mesh>();\n \n scene.traverse((child) => {\n if ((child as THREE.Mesh).isMesh) {\n const mesh = child as THREE.Mesh;\n if (mesh.name) {\n meshMap.set(mesh.name, mesh);\n }\n }\n });\n\n meshMapRef.current = meshMap;\n }, [scene]);\n\n // Apply visibility changes on every frame\n useFrame(() => {\n const meshMap = meshMapRef.current;\n if (meshMap.size === 0) return;\n\n // Merge: props take baseline, imperative overrides take priority\n const merged = { ...visibilityRef.current, ...overridesRef.current };\n\n for (const [name, visible] of Object.entries(merged)) {\n const mesh = meshMap.get(name);\n if (mesh && mesh.visible !== visible) {\n mesh.visible = visible;\n }\n }\n });\n\n /**\n * Set visibility of a specific mesh (imperative, overrides props)\n */\n const setMeshVisibility = useCallback((name: string, visible: boolean) => {\n overridesRef.current[name] = visible;\n \n // Apply immediately\n const mesh = meshMapRef.current.get(name);\n if (mesh) {\n mesh.visible = visible;\n }\n }, []);\n\n /**\n * Get all mesh names in the scene\n */\n const getMeshNames = useCallback(() => {\n return Array.from(meshMapRef.current.keys()).sort();\n }, []);\n\n /**\n * Get current visibility state of all meshes\n */\n const getMeshVisibility = useCallback(() => {\n const result: Record<string, boolean> = {};\n meshMapRef.current.forEach((mesh, name) => {\n result[name] = mesh.visible;\n });\n return result;\n }, []);\n\n return {\n setMeshVisibility,\n getMeshNames,\n getMeshVisibility,\n };\n}\n","import { useEffect, useCallback, useRef } from 'react';\r\nimport { useFrame } from '@react-three/fiber';\r\nimport * as THREE from 'three';\r\n\r\n/**\r\n * Hook for managing material colors in a 3D scene\r\n * \r\n * @param scene - The THREE.Object3D scene containing meshes with materials\r\n * @param initialColors - Initial color values for materials { materialName: \"#hexcolor\" or [r, g, b] }\r\n * @returns Methods to control material colors\r\n */\r\nexport function useMaterialColor(\r\n scene: THREE.Object3D,\r\n initialColors?: Record<string, string | [number, number, number]>\r\n) {\r\n const materialMapRef = useRef<Map<string, THREE.Material | THREE.Material[]>>(new Map());\r\n const colorMapRef = useRef<Map<string, THREE.Color>>(new Map());\r\n\r\n // Discover all materials in the scene\r\n useEffect(() => {\r\n if (!scene) return;\r\n\r\n const materialMap = new Map<string, THREE.Material | THREE.Material[]>();\r\n \r\n scene.traverse((child) => {\r\n if ((child as THREE.Mesh).isMesh) {\r\n const mesh = child as THREE.Mesh;\r\n const materials = Array.isArray(mesh.material) ? mesh.material : [mesh.material];\r\n \r\n materials.forEach((material) => {\r\n if (material.name && !materialMap.has(material.name)) {\r\n materialMap.set(material.name, material);\r\n }\r\n });\r\n }\r\n });\r\n\r\n materialMapRef.current = materialMap;\r\n\r\n // Apply initial colors\r\n if (initialColors) {\r\n Object.entries(initialColors).forEach(([materialName, color]) => {\r\n const material = materialMap.get(materialName);\r\n if (material && !Array.isArray(material)) {\r\n const threeColor = parseColor(color);\r\n if ((material as any).color) {\r\n (material as any).color.copy(threeColor);\r\n }\r\n colorMapRef.current.set(materialName, threeColor);\r\n }\r\n });\r\n }\r\n }, [scene, initialColors]);\r\n\r\n // Apply color changes on every frame (for smooth transitions)\r\n useFrame(() => {\r\n colorMapRef.current.forEach((targetColor, materialName) => {\r\n const material = materialMapRef.current.get(materialName);\r\n if (material && !Array.isArray(material)) {\r\n const mat = material as any;\r\n if (mat.color && !mat.color.equals(targetColor)) {\r\n mat.color.copy(targetColor);\r\n mat.needsUpdate = true;\r\n }\r\n }\r\n });\r\n });\r\n\r\n /**\r\n * Parse color from hex string or RGB array\r\n */\r\n const parseColor = (color: string | [number, number, number]): THREE.Color => {\r\n if (typeof color === 'string') {\r\n return new THREE.Color(color);\r\n } else {\r\n return new THREE.Color(color[0], color[1], color[2]);\r\n }\r\n };\r\n\r\n /**\r\n * Set color of a specific material\r\n * @param materialName - Material name\r\n * @param color - Hex color string (e.g., \"#ff0000\") or RGB array [r, g, b] where values are 0-1\r\n */\r\n const setMaterialColor = useCallback((\r\n materialName: string, \r\n color: string | [number, number, number]\r\n ) => {\r\n const threeColor = parseColor(color);\r\n colorMapRef.current.set(materialName, threeColor);\r\n \r\n const material = materialMapRef.current.get(materialName);\r\n if (material && !Array.isArray(material)) {\r\n const mat = material as any;\r\n if (mat.color) {\r\n mat.color.copy(threeColor);\r\n mat.needsUpdate = true;\r\n }\r\n }\r\n }, []);\r\n\r\n /**\r\n * Get all material names in the scene\r\n */\r\n const getMaterialNames = useCallback(() => {\r\n return Array.from(materialMapRef.current.keys()).sort();\r\n }, []);\r\n\r\n /**\r\n * Get current color of a material\r\n * @returns Hex color string (e.g., \"#ff0000\")\r\n */\r\n const getMaterialColor = useCallback((materialName: string): string | null => {\r\n const material = materialMapRef.current.get(materialName);\r\n if (material && !Array.isArray(material)) {\r\n const mat = material as any;\r\n if (mat.color) {\r\n return '#' + mat.color.getHexString();\r\n }\r\n }\r\n return null;\r\n }, []);\r\n\r\n /**\r\n * Get all material colors\r\n * @returns Object mapping material names to hex colors\r\n */\r\n const getAllMaterialColors = useCallback((): Record<string, string> => {\r\n const colors: Record<string, string> = {};\r\n materialMapRef.current.forEach((material, name) => {\r\n if (!Array.isArray(material)) {\r\n const mat = material as any;\r\n if (mat.color) {\r\n colors[name] = '#' + mat.color.getHexString();\r\n }\r\n }\r\n });\r\n return colors;\r\n }, []);\r\n\r\n return {\r\n setMaterialColor,\r\n getMaterialNames,\r\n getMaterialColor,\r\n getAllMaterialColors,\r\n };\r\n}\r\n","import { useEffect, useCallback, useRef } from 'react';\nimport { useThree } from '@react-three/fiber';\nimport * as THREE from 'three';\nimport { KTX2Loader } from 'three/examples/jsm/loaders/KTX2Loader.js';\n\n/**\n * Material texture maps configuration\n */\nexport interface MaterialTextures {\n /** Base/Diffuse color map */\n map?: string;\n /** Normal map */\n normalMap?: string;\n /** Roughness map */\n roughnessMap?: string;\n /** Metallic map */\n metalnessMap?: string;\n /** Emissive map */\n emissiveMap?: string;\n /** Alpha/Opacity map */\n alphaMap?: string;\n /** Ambient Occlusion map */\n aoMap?: string;\n}\n\n/**\n * Hook for managing material textures in a 3D scene\n * \n * @param scene - The THREE.Object3D scene containing meshes with materials\n * @param textures - Initial texture maps for materials { materialName: textureConfig }\n * @returns Methods to control material textures\n */\nexport function useMaterialTexture(\n scene: THREE.Object3D,\n textures?: Record<string, MaterialTextures | string>\n) {\n const { gl } = useThree();\n const materialMapRef = useRef<Map<string, THREE.Material>>(new Map());\n const textureLoaderRef = useRef<THREE.TextureLoader>(new THREE.TextureLoader());\n const ktx2LoaderRef = useRef<KTX2Loader | null>(null);\n const loadedTexturesRef = useRef<Map<string, THREE.Texture>>(new Map());\n const loadingTexturesRef = useRef<Set<string>>(new Set()); // Track textures being loaded\n const appliedTexturesRef = useRef<string>(''); // Track what was last applied\n\n // Initialize KTX2 loader\n useEffect(() => {\n if (!ktx2LoaderRef.current) {\n try {\n // Check WASM support\n const supportsWASM = typeof WebAssembly === 'object' &&\n typeof WebAssembly.validate === 'function';\n\n console.log('[useMaterialTexture] Browser features:');\n console.log(` WebAssembly supported: ${supportsWASM}`);\n console.log(` WebGL2: ${gl.capabilities.isWebGL2}`);\n\n const ktx2Loader = new KTX2Loader();\n ktx2Loader.setTranscoderPath('/basis/');\n ktx2Loader.detectSupport(gl);\n ktx2LoaderRef.current = ktx2Loader;\n console.log('[useMaterialTexture] ✅ KTX2Loader initialized');\n console.log('[useMaterialTexture] Transcoder path: /basis/');\n\n // Test if transcoder files are accessible\n fetch('/basis/basis_transcoder.wasm')\n .then(response => {\n if (response.ok) {\n console.log('[useMaterialTexture] ✅ basis_transcoder.wasm is accessible');\n } else {\n console.error('[useMaterialTexture] ❌ basis_transcoder.wasm returned', response.status);\n }\n })\n .catch(error => {\n console.error('[useMaterialTexture] ❌ Failed to check basis_transcoder.wasm:', error);\n });\n\n if (!supportsWASM) {\n console.warn('[useMaterialTexture] ⚠️ WebAssembly not supported! KTX2 will fallback to CPU decoding (slow)');\n console.warn('[useMaterialTexture] ⚠️ Recommend using WebP instead of KTX2 for this browser');\n }\n } catch (error) {\n console.error('[useMaterialTexture] ❌ Failed to initialize KTX2Loader:', error);\n }\n }\n\n return () => {\n if (ktx2LoaderRef.current) {\n ktx2LoaderRef.current.dispose();\n ktx2LoaderRef.current = null;\n }\n };\n }, [gl]);\n\n // Discover all materials in the scene\n useEffect(() => {\n if (!scene) return;\n\n const materialMap = new Map<string, THREE.Material>();\n\n scene.traverse((child) => {\n if ((child as THREE.Mesh).isMesh) {\n const mesh = child as THREE.Mesh;\n const materials = Array.isArray(mesh.material) ? mesh.material : [mesh.material];\n\n materials.forEach((material) => {\n if (material.name && !materialMap.has(material.name)) {\n materialMap.set(material.name, material);\n }\n });\n }\n });\n\n materialMapRef.current = materialMap;\n }, [scene]);\n\n // Helper function to get the appropriate loader based on file extension\n const getLoader = useCallback((url: string): THREE.TextureLoader | KTX2Loader => {\n if (url.toLowerCase().endsWith('.ktx2')) {\n if (!ktx2LoaderRef.current) {\n console.warn('[useMaterialTexture] KTX2Loader not initialized, falling back to TextureLoader');\n return textureLoaderRef.current;\n }\n return ktx2LoaderRef.current;\n }\n return textureLoaderRef.current;\n }, []);\n\n // Load and apply textures when they change\n useEffect(() => {\n if (!textures || !scene) return;\n\n const materialMap = materialMapRef.current;\n\n if (materialMap.size === 0) return;\n\n // Create a stable key for comparison\n const texturesKey = JSON.stringify(textures);\n if (appliedTexturesRef.current === texturesKey) {\n console.log('[useMaterialTexture] Textures unchanged, skipping reload');\n return;\n }\n appliedTexturesRef.current = texturesKey;\n\n let disposed = false;\n\n // Normalize textures\n const normalizedTextures = new Map<string, MaterialTextures>();\n Object.entries(textures).forEach(([name, config]) => {\n normalizedTextures.set(name, typeof config === 'string' ? { map: config } : config);\n });\n\n console.log('[useMaterialTexture] Starting texture load for', normalizedTextures.size, 'materials');\n\n // Collect all texture load tasks\n const loadTasks: Array<{\n materialName: string;\n textureType: string;\n url: string;\n material: THREE.MeshStandardMaterial;\n }> = [];\n\n normalizedTextures.forEach((textureConfig, materialName) => {\n const material = materialMap.get(materialName);\n if (!material || !(material as any).isMeshStandardMaterial) {\n console.warn(`[useMaterialTexture] Material \"${materialName}\" not found or not MeshStandardMaterial`);\n return;\n }\n\n const mat = material as THREE.MeshStandardMaterial;\n\n mat.map = null;\n mat.normalMap = null;\n mat.roughnessMap = null;\n mat.metalnessMap = null;\n mat.emissiveMap = null;\n mat.emissive = new THREE.Color(0x000000); // Reset to black\n mat.emissiveIntensity = 0;\n mat.aoMap = null;\n mat.needsUpdate = true;\n\n Object.entries(textureConfig).forEach(([textureType, url]) => {\n if (!url) return;\n\n const cacheKey = `${materialName}_${textureType}_${url}`;\n\n // Check if already loaded\n const cached = loadedTexturesRef.current.get(cacheKey);\n if (cached) {\n console.log(`[useMaterialTexture] Using cached texture: ${materialName}.${textureType}`);\n if (!disposed) {\n applyTexture(mat, textureType as keyof MaterialTextures, cached);\n }\n return;\n }\n\n // Skip if already loading\n if (loadingTexturesRef.current.has(cacheKey)) {\n console.log(`[useMaterialTexture] Skipping already loading: ${materialName}.${textureType}`);\n return;\n }\n\n loadTasks.push({ materialName, textureType, url, material: mat });\n });\n });\n\n console.log(`[useMaterialTexture] Queued ${loadTasks.length} textures to load`);\n\n // Load textures sequentially with delay to avoid overwhelming GPU\n let taskIndex = 0;\n const loadNextTexture = () => {\n if (disposed || taskIndex >= loadTasks.length) {\n console.log('[useMaterialTexture] All textures loaded');\n return;\n }\n\n const task = loadTasks[taskIndex++];\n const cacheKey = `${task.materialName}_${task.textureType}_${task.url}`;\n\n console.log(`[useMaterialTexture] Loading (${taskIndex}/${loadTasks.length}): ${task.materialName}.${task.textureType} from ${task.url}`);\n\n loadingTexturesRef.current.add(cacheKey);\n\n // Get the appropriate loader based on file extension\n const loader = getLoader(task.url);\n const loaderType = task.url.toLowerCase().endsWith('.ktx2') ? 'KTX2Loader' : 'TextureLoader';\n console.log(`[useMaterialTexture] Using ${loaderType} for ${task.url}`);\n\n loader.load(\n task.url,\n (texture) => {\n loadingTexturesRef.current.delete(cacheKey);\n\n if (disposed) {\n console.log(`[useMaterialTexture] Disposed, cleaning up texture: ${task.materialName}.${task.textureType}`);\n texture.dispose();\n return;\n }\n\n const width = texture.image?.width || texture.source?.data?.width || 'unknown';\n const height = texture.image?.height || texture.source?.data?.height || 'unknown';\n console.log(`[useMaterialTexture] ✅ Loaded: ${task.materialName}.${task.textureType} (${width}x${height})`);\n\n // Configure texture\n texture.colorSpace = task.textureType === 'map' || task.textureType === 'emissiveMap'\n ? THREE.SRGBColorSpace\n : THREE.NoColorSpace;\n texture.wrapS = THREE.RepeatWrapping;\n texture.wrapT = THREE.RepeatWrapping;\n texture.flipY = false;\n\n loadedTexturesRef.current.set(cacheKey, texture);\n applyTexture(task.material, task.textureType as keyof MaterialTextures, texture);\n\n // Load next texture after a delay (longer for KTX2 to allow GPU decompression)\n const delay = task.url.endsWith('.ktx2') ? 500 : 150;\n console.log(`[useMaterialTexture] Waiting ${delay}ms before next texture...`);\n setTimeout(loadNextTexture, delay);\n },\n (progress) => {\n // Progress callback\n if (progress.lengthComputable) {\n const percent = Math.round((progress.loaded / progress.total) * 100);\n if (percent % 25 === 0) {\n console.log(`[useMaterialTexture] Progress ${task.materialName}.${task.textureType}: ${percent}%`);\n }\n }\n },\n (error) => {\n loadingTexturesRef.current.delete(cacheKey);\n\n if (!disposed) {\n console.error(`[useMaterialTexture] ❌ Failed to load ${task.materialName}.${task.textureType} from ${task.url}`);\n console.error('[useMaterialTexture] Error details:', error);\n console.error('[useMaterialTexture] Loader type:', loaderType);\n\n // Check if this is a KTX2 format issue\n if (task.url.endsWith('.ktx2')) {\n console.error('[useMaterialTexture] KTX2 error - file may not be in Basis Universal format!');\n console.error('[useMaterialTexture] Make sure files are created with: gltf-transform etc1s input.png output.ktx2');\n }\n }\n\n // Continue to next texture even if one fails\n setTimeout(loadNextTexture, 100);\n }\n );\n };\n\n // Start loading the first texture\n loadNextTexture();\n\n return () => {\n console.log('[useMaterialTexture] Cleanup: disposed');\n disposed = true;\n };\n }, [scene, textures, getLoader]);\n\n const applyTexture = (\n material: THREE.MeshStandardMaterial,\n type: keyof MaterialTextures,\n texture: THREE.Texture\n ) => {\n switch (type) {\n case 'map':\n material.map = texture;\n break;\n case 'normalMap':\n material.normalMap = texture;\n break;\n case 'roughnessMap':\n material.roughnessMap = texture;\n break;\n case 'metalnessMap':\n material.metalnessMap = texture;\n break;\n case 'emissiveMap':\n material.emissiveMap = texture;\n material.emissive = new THREE.Color(0xffffff);\n material.emissiveIntensity = 1;\n break;\n case 'alphaMap':\n // Skip alphaMap - it causes parts to disappear incorrectly\n // Only use if material already has transparency configured\n console.log(`[useMaterialTexture] Skipping alphaMap for \"${material.name}\" to prevent disappearing`);\n break;\n case 'aoMap':\n material.aoMap = texture;\n break;\n }\n material.needsUpdate = true;\n };\n\n /**\n * Set textures for a specific material\n * @param materialName - Material name\n * @param textureConfig - Texture configuration or single map URL\n */\n const setMaterialTextures = useCallback((\n materialName: string,\n textureConfig: MaterialTextures | string\n ) => {\n const normalized = typeof textureConfig === 'string'\n ? { map: textureConfig }\n : textureConfig;\n\n const material = materialMapRef.current.get(materialName);\n if (!material || !(material as any).isMeshStandardMaterial) {\n console.warn(`Material \"${materialName}\" not found or not MeshStandardMaterial`);\n return;\n }\n\n const mat = material as THREE.MeshStandardMaterial;\n\n Object.entries(normalized).forEach(([textureType, url]) => {\n if (!url) return;\n\n const cacheKey = `${materialName}_${textureType}_${url}`;\n\n // Check cache first\n const cached = loadedTexturesRef.current.get(cacheKey);\n if (cached) {\n applyTexture(mat, textureType as keyof MaterialTextures, cached);\n return;\n }\n\n // Get the appropriate loader based on file extension\n const loader = getLoader(url);\n\n loader.load(\n url,\n (texture) => {\n texture.colorSpace = textureType === 'map' || textureType === 'emissiveMap'\n ? THREE.SRGBColorSpace\n : THREE.NoColorSpace;\n texture.wrapS = THREE.RepeatWrapping;\n texture.wrapT = THREE.RepeatWrapping;\n\n loadedTexturesRef.current.set(cacheKey, texture);\n applyTexture(mat, textureType as keyof MaterialTextures, texture);\n },\n undefined,\n (error) => {\n console.error(`Failed to load texture ${url}:`, error);\n }\n );\n });\n }, [getLoader]);\n\n /**\n * Get all material names in the scene\n */\n const getMaterialNames = useCallback(() => {\n return Array.from(materialMapRef.current.keys()).sort();\n }, []);\n\n /**\n * Clear all textures from a material\n */\n const clearMaterialTextures = useCallback((materialName: string) => {\n const material = materialMapRef.current.get(materialName);\n if (!material || !(material as any).isMeshStandardMaterial) return;\n\n const mat = material as THREE.MeshStandardMaterial;\n mat.map = null;\n mat.normalMap = null;\n mat.roughnessMap = null;\n mat.metalnessMap = null;\n mat.emissiveMap = null;\n mat.alphaMap = null;\n mat.aoMap = null;\n mat.needsUpdate = true;\n }, []);\n\n return {\n setMaterialTextures,\n getMaterialNames,\n clearMaterialTextures,\n };\n}\n","import { useMemo, useRef } from 'react';\nimport { useGLTF } from '@react-three/drei';\nimport * as THREE from 'three';\nimport { useMeshVisibility } from '../../hooks/useMeshVisibility';\nimport { useMaterialColor } from '../../hooks/useMaterialColor';\nimport { useMaterialTexture } from '../../hooks/useMaterialTexture';\nimport type { ModelProps } from '../../types';\n\n/**\n * Model - Simple 3D model component for loading and displaying GLB/GLTF files\n * \n * @remarks\n * This component:\n * - Loads GLB/GLTF models using drei's useGLTF hook\n * - Automatically clones the scene for independent instances\n * - Configures shadows on all meshes\n * - Supports position, rotation, and scale transforms\n * - Provides onLoad callback with model metadata\n * \n * @param url - URL or path to GLB/GLTF model file. Example: `\"/models/sword.glb\"`\n * @param position - Model position in 3D space as [x, y, z]. Example: `[0, 0, 0]`. Default: `[0, 0, 0]`\n * @param rotation - Model rotation in radians as [x, y, z]. Example: `[0, Math.PI/2, 0]`. Default: `[0, 0, 0]`\n * @param scale - Uniform scale (number) or per-axis scale [x, y, z]. Examples: `0.5` or `[1, 2, 1]`. Default: `1`\n * @param meshVisibility - Mesh visibility as key-value pairs. Example: `{ \"Part1\": true, \"Part2\": false }`\n * @param materialColors - Material colors as key-value pairs. Example: `{ \"MaterialName\": \"#ff0000\" }`\n * @param materialTextures - Material texture maps. Example: `{ \"Metal\": { map: \"/tex.webp\", normalMap: \"/normal.webp\" } }`\n * @param onLoad - Callback when model finishes loading. Receives ModelInfo object with metadata: `{ meshes, materials, bones, nodeCount }`\n * @param onError - Callback when model fails to load. Receives Error object\n * \n * @example\n * ```tsx\n * <Model \n * url=\"/models/sword.glb\"\n * position={[0, 0, 0]}\n * rotation={[0, Math.PI/2, 0]}\n * scale={0.5}\n * meshVisibility={{ \"Blade\": true, \"Handle\": false }}\n * onLoad={(info) => console.log('Loaded:', info)}\n * />\n * ```\n */\nexport function Model({\n url,\n position = [0, 0, 0],\n rotation = [0, 0, 0],\n scale = 1,\n meshVisibility,\n materialColors,\n materialTextures,\n onLoad,\n onError: _onError,\n}: ModelProps) {\n const { scene } = useGLTF(url);\n const onLoadRef = useRef(onLoad);\n onLoadRef.current = onLoad;\n\n // Clone scene for independent instance\n const clonedScene = useMemo(() => {\n const clone = scene.clone();\n \n // Setup shadows and collect info\n const meshes: string[] = [];\n const materials = new Set<string>();\n const bones: string[] = [];\n let nodeCount = 0;\n\n clone.traverse((child) => {\n nodeCount++;\n \n if (child.type === 'Bone') {\n bones.push(child.name);\n }\n \n if ((child as THREE.Mesh).isMesh) {\n const mesh = child as THREE.Mesh;\n meshes.push(mesh.name);\n mesh.castShadow = true;\n mesh.receiveShadow = true;\n \n const mats = Array.isArray(mesh.material) ? mesh.material : [mesh.material];\n mats.forEach((mat) => materials.add(mat.name));\n }\n });\n\n // Defer callback to avoid state update during render\n setTimeout(() => {\n onLoadRef.current?.({\n meshes: meshes.sort(),\n materials: Array.from(materials).sort(),\n bones: bones.sort(),\n nodeCount,\n });\n }, 0);\n\n return clone;\n }, [scene]);\n\n // Mesh visibility hook\n useMeshVisibility(clonedScene, meshVisibility);\n\n // Material colors hook\n useMaterialColor(clonedScene, materialColors);\n\n // Material textures hook\n useMaterialTexture(clonedScene, materialTextures);\n\n const scaleArray = typeof scale === 'number' ? [scale, scale, scale] : scale;\n\n return (\n <group position={position} rotation={rotation} scale={scaleArray as [number, number, number]}>\n <primitive object={clonedScene} />\n </group>\n );\n}\n\n// Preload helper\nModel.preload = (url: string) => {\n useGLTF.preload(url);\n};\n","import { useRef, useCallback, useEffect } from 'react';\r\nimport { useAnimations } from '@react-three/drei';\r\nimport * as THREE from 'three';\r\n\r\ninterface UseAnimationControllerOptions {\r\n defaultAnimation?: string;\r\n}\r\n\r\nexport function useAnimationController(\r\n animations: THREE.AnimationClip[],\r\n scene: THREE.Object3D,\r\n options?: UseAnimationControllerOptions\r\n) {\r\n const { actions, names } = useAnimations(animations, scene);\r\n const currentActionRef = useRef<THREE.AnimationAction | null>(null);\r\n const defaultAnimationRef = useRef<string | undefined>(options?.defaultAnimation);\r\n\r\n // Play default animation on mount\r\n useEffect(() => {\r\n if (names.length === 0) return;\r\n\r\n const defaultAnim = defaultAnimationRef.current;\r\n let animToPlay = names[0];\r\n\r\n if (defaultAnim) {\r\n const match = names.find((n) => n === defaultAnim || n.includes(defaultAnim));\r\n if (match) animToPlay = match;\r\n }\r\n\r\n const action = actions[animToPlay];\r\n if (action) {\r\n action.reset().fadeIn(0.5).play();\r\n currentActionRef.current = action;\r\n }\r\n }, [actions, names]);\r\n\r\n const playAnimation = useCallback(\r\n (\r\n name: string,\r\n opts?: {\r\n loop?: boolean;\r\n crossFadeDuration?: number;\r\n restoreDefault?: boolean;\r\n }\r\n ) => {\r\n const {\r\n loop = false,\r\n crossFadeDuration = 0.2,\r\n restoreDefault = true,\r\n } = opts || {};\r\n\r\n // Find animation (exact match or partial)\r\n let targetAction = actions[name];\r\n\r\n if (!targetAction) {\r\n const match = Object.keys(actions).find(\r\n (a) =>\r\n a.toLowerCase().includes(name.toLowerCase()) ||\r\n name.toLowerCase().includes(a.toLowerCase())\r\n );\r\n if (match) {\r\n targetAction = actions[match];\r\n }\r\n }\r\n\r\n if (!targetAction) {\r\n console.warn(`Animation \"${name}\" not found. Available: ${names.join(', ')}`);\r\n return;\r\n }\r\n\r\n // Don't restart if already playing\r\n const prev = currentActionRef.current;\r\n if (prev === targetAction && targetAction.isRunning()) {\r\n return;\r\n }\r\n\r\n // Crossfade from previous\r\n if (prev && prev !== targetAction) {\r\n prev.fadeOut(crossFadeDuration);\r\n }\r\n\r\n // Setup new action\r\n targetAction.reset();\r\n targetAction.fadeIn(crossFadeDuration);\r\n targetAction.setLoop(\r\n loop ? THREE.LoopRepeat : THREE.LoopOnce,\r\n loop ? Infinity : 1\r\n );\r\n targetAction.clampWhenFinished = !loop;\r\n targetAction.play();\r\n\r\n // Force immediate update for non-looping animations\r\n if (!loop) {\r\n targetAction.getMixer().update(0);\r\n }\r\n\r\n currentActionRef.current = targetAction;\r\n\r\n // Restore default animation after one-shot completes\r\n if (restoreDefault && !loop && defaultAnimationRef.current) {\r\n const mixer = targetAction.getMixer();\r\n const onFinished = (e: { action: THREE.AnimationAction }) => {\r\n if (e.action === targetAction) {\r\n mixer.removeEventListener('finished', onFinished);\r\n \r\n const defaultAction = actions[defaultAnimationRef.current!];\r\n if (defaultAction) {\r\n targetAction.fadeOut(crossFadeDuration);\r\n defaultAction.reset().fadeIn(crossFadeDuration).play();\r\n currentActionRef.current = defaultAction;\r\n }\r\n }\r\n };\r\n mixer.addEventListener('finished', onFinished);\r\n }\r\n },\r\n [actions, names]\r\n );\r\n\r\n const stopAnimation = useCallback(() => {\r\n currentActionRef.current?.fadeOut(0.2);\r\n currentActionRef.current = null;\r\n }, []);\r\n\r\n const getAnimationNames = useCallback(() => names, [names]);\r\n\r\n return {\r\n playAnimation,\r\n stopAnimation,\r\n getAnimationNames,\r\n actions,\r\n };\r\n}\r\n","import { useRef, useCallback, useEffect } from 'react';\r\nimport { useFrame } from '@react-three/fiber';\r\nimport * as THREE from 'three';\r\n\r\nexport function useMorphTargets(\r\n scene: THREE.Object3D,\r\n initialValues?: Record<string, number>\r\n) {\r\n const morphValuesRef = useRef<Record<string, number>>(initialValues || {});\r\n const morphNamesRef = useRef<string[]>([]);\r\n const meshesWithMorphsRef = useRef<THREE.Mesh[]>([]);\r\n\r\n // Discover morph targets\r\n useEffect(() => {\r\n const names = new Set<string>();\r\n const meshes: THREE.Mesh[] = [];\r\n\r\n scene.traverse((child) => {\r\n if (\r\n child instanceof THREE.Mesh &&\r\n child.morphTargetDictionary &&\r\n child.morphTargetInfluences\r\n ) {\r\n meshes.push(child);\r\n Object.keys(child.morphTargetDictionary).forEach((name) => {\r\n names.add(name);\r\n });\r\n }\r\n });\r\n\r\n morphNamesRef.current = Array.from(names).sort();\r\n meshesWithMorphsRef.current = meshes;\r\n }, [scene]);\r\n\r\n // Apply morph targets every frame\r\n useFrame(() => {\r\n const values = morphValuesRef.current;\r\n \r\n meshesWithMorphsRef.current.forEach((mesh) => {\r\n if (!mesh.morphTargetDictionary || !mesh.morphTargetInfluences) return;\r\n \r\n Object.entries(values).forEach(([name, value]) => {\r\n const index = mesh.morphTargetDictionary![name];\r\n if (index !== undefined) {\r\n mesh.morphTargetInfluences![index] = value;\r\n }\r\n });\r\n });\r\n });\r\n\r\n // Update values when props change\r\n useEffect(() => {\r\n if (initialValues) {\r\n morphValuesRef.current = { ...initialValues };\r\n }\r\n }, [initialValues]);\r\n\r\n const setMorphTarget = useCallback((name: string, value: number) => {\r\n morphValuesRef.current[name] = Math.max(0, Math.min(1, value));\r\n }, []);\r\n\r\n const getMorphTargetNames = useCallback(() => morphNamesRef.current, []);\r\n\r\n const getMorphTargetValues = useCallback(() => ({ ...morphValuesRef.current }), []);\r\n\r\n return {\r\n setMorphTarget,\r\n getMorphTargetNames,\r\n getMorphTargetValues,\r\n };\r\n}\r\n","import { createContext, useContext } from 'react';\r\nimport type { AnimatedModelContextValue } from '../../types';\r\n\r\nexport const AnimatedModelContext = createContext<AnimatedModelContextValue | null>(null);\r\n\r\nexport function useAnimatedModelContext() {\r\n const context = useContext(AnimatedModelContext);\r\n if (!context) {\r\n throw new Error('BoneAttachment must be used within an AnimatedModel');\r\n }\r\n return context;\r\n}\r\n","import {\n forwardRef,\n useImperativeHandle,\n useRef,\n useMemo,\n useEffect,\n} from 'react';\nimport * as THREE from 'three';\nimport { useGLTF } from '@react-three/drei';\nimport * as SkeletonUtils from 'three/examples/jsm/utils/SkeletonUtils.js';\nimport { useAnimationController } from '../../hooks/useAnimationController';\nimport { useMorphTargets } from '../../hooks/useMorphTargets';\nimport { useMeshVisibility } from '../../hooks/useMeshVisibility';\nimport { useMaterialColor } from '../../hooks/useMaterialColor';\nimport { useMaterialTexture } from '../../hooks/useMaterialTexture';\nimport { AnimatedModelContext } from './AnimatedModelContext';\nimport type { AnimatedModelProps, AnimatedModelHandle } from '../../types';\n\n/**\n * AnimatedModel - 3D model with animation support and bone attachment capability\n * \n * @remarks\n * This component:\n * - Loads GLB/GLTF models with animations using useGLTF\n * - Properly clones skinned meshes using SkeletonUtils for multiple instances\n * - Plays animations with automatic crossfading\n * - Supports morph targets for character customization\n * - Provides context for BoneAttachment children\n * - Exposes imperative API via ref for external animation control\n * \n * The ref provides these methods:\n * - `playAnimation(name, options)` - Play an animation with optional loop/restore\n * - `stopAnimation()` - Stop current animation\n * - `getAnimationNames()` - Get list of available animations\n * - `getGroup()` - Get the THREE.Group for advanced manipulation\n * - `setMorphTarget(name, value)` - Set morph target value\n * - `getMorphTargetNames()` - Get available morph target names\n * \n * @param ref - React ref for imperative API access\n * @param url - URL or path to GLB/GLTF model file with animations. Example: `\"/models/character.glb\"`\n * @param position - Model position in 3D space as [x, y, z]. Example: `[0, -1, 0]`. Default: `[0, 0, 0]`\n * @param rotation - Model rotation in radians as [x, y, z]. Example: `[0, Math.PI, 0]`. Default: `[0, 0, 0]`\n * @param scale - Uniform scale (number) or per-axis scale [x, y, z]. Examples: `0.5` or `[1, 2, 1]`. Default: `1`\n * @param defaultAnimation - Name of animation to play on load. If not specified, plays first animation. Example: `\"Idle\"`\n * @param morphTargets - Morph target values as key-value pairs where value is 0-1. Example: `{ muscular: 0.5, thin: 0.2 }`\n * @param meshVisibility - Mesh visibility as key-value pairs. Example: `{ \"Hairgirl1\": true, \"Hairgirl2\": false }`\n * @param materialColors - Material colors as key-value pairs. Example: `{ \"Hair_girl_1\": \"#ff0000\" }`\n * @param materialTextures - Material texture maps. Example: `{ \"Metal\": { map: \"/tex.webp\", normalMap: \"/normal.webp\" } }`\n * @param children - Child components, typically BoneAttachment components for attaching items\n * @param onLoad - Callback when model loads. Receives AnimatedModelInfo object: `{ meshes, materials, bones, nodeCount, animations, morphTargetNames }`\n * @param onError - Callback when model fails to load. Receives Error object\n * \n * @example\n * ```tsx\n * const modelRef = useRef<AnimatedModelHandle>(null);\n * \n * <AnimatedModel\n * ref={modelRef}\n * url=\"/models/character.glb\"\n * defaultAnimation=\"Idle\"\n * morphTargets={{ muscular: 0.5 }}\n * meshVisibility={{ \"Hairgirl1\": true, \"Hairgirl2\": false }}\n * materialColors={{ \"Hair_girl_1\": \"#ff0000\" }}\n * materialTextures={{ \"Metal\": { map: \"/metal.webp\", roughnessMap: \"/rough.webp\" } }}\n * position={[0, -1, 0]}\n * >\n * <BoneAttachment bone=\"hand_r\">\n * <Model url=\"/models/sword.glb\" />\n * </BoneAttachment>\n * </AnimatedModel>\n * \n * // Control animations from outside\n * modelRef.current?.playAnimation('Attack', { loop: false });\n * ```\n */\nexport const AnimatedModel = forwardRef<AnimatedModelHandle, AnimatedModelProps>(\n (\n {\n url,\n position = [0, 0, 0],\n rotation = [0, 0, 0],\n scale = 1,\n defaultAnimation,\n morphTargets,\n meshVisibility,\n materialColors,\n materialTextures,\n onLoad,\n onError: _onError,\n children,\n },\n ref\n ) => {\n const groupRef = useRef<THREE.Group>(null);\n const morphNamesRef = useRef<string[]>([]);\n const onLoadRef = useRef(onLoad);\n onLoadRef.current = onLoad;\n\n // Load and clone model\n const { scene: gltfScene, animations } = useGLTF(url);\n const scene = useMemo(() => SkeletonUtils.clone(gltfScene), [gltfScene]);\n\n // Animation controller\n const { playAnimation, stopAnimation, getAnimationNames } = useAnimationController(\n animations,\n scene,\n { defaultAnimation }\n );\n\n // Morph targets\n const { setMorphTarget } = useMorphTargets(\n scene,\n morphTargets\n );\n\n // Mesh visibility\n const { setMeshVisibility, getMeshNames } = useMeshVisibility(\n scene,\n meshVisibility\n );\n\n // Material colors\n const { setMaterialColor, getMaterialNames, getMaterialColor } = useMaterialColor(\n scene,\n materialColors\n );\n\n // Material textures\n const { setMaterialTextures, clearMaterialTextures } = useMaterialTexture(\n scene,\n materialTextures\n );\n\n // Discover morph targets and call onLoad\n useEffect(() => {\n if (!scene) return;\n\n const timer = setTimeout(() => {\n const bones: string[] = [];\n const meshes: string[] = [];\n const materials = new Set<string>();\n const morphNames = new Set<string>();\n let nodeCount = 0;\n\n scene.traverse((child) => {\n nodeCount++;\n \n if (child.type === 'Bone') {\n bones.push(child.name);\n }\n \n if ((child as THREE.Mesh).isMesh) {\n const mesh = child as THREE.Mesh;\n meshes.push(mesh.name);\n \n // Setup shadows\n mesh.castShadow = true;\n mesh.receiveShadow = true;\n \n const mats = Array.isArray(mesh.material) ? mesh.material : [mesh.material];\n mats.forEach((mat) => {\n materials.add(mat.name);\n mat.shadowSide = THREE.DoubleSide;\n });\n\n if (mesh.morphTargetDictionary) {\n Object.keys(mesh.morphTargetDictionary).forEach((name) => {\n morphNames.add(name);\n });\n }\n }\n });\n\n morphNamesRef.current = Array.from(morphNames).sort();\n\n onLoadRef.current?.({\n meshes: meshes.sort(),\n materials: Array.from(materials).sort(),\n bones: bones.sort(),\n nodeCount,\n animations: animations.map((a) => a.name),\n morphTargetNames: morphNamesRef.current,\n });\n }, 0);\n\n return () => clearTimeout(timer);\n }, [scene, animations]);\n\n // Expose imperative handle\n useImperativeHandle(ref, () => ({\n playAnimation,\n stopAnimation,\n getAnimationNames,\n getGroup: () => groupRef.current,\n setMorphTarget,\n getMorphTargetNames: () => morphNamesRef.current,\n setMeshVisibility,\n getMeshNames,\n setMaterialColor,\n getMaterialNames,\n getMaterialColor,\n setMaterialTextures,\n clearMaterialTextures,\n }));\n\n // Context for BoneAttachment children\n const contextValue = useMemo(\n () => ({\n scene,\n getBone: (name: string) => scene.getObjectByName(name) || null,\n }),\n [scene]\n );\n\n const scaleArray = typeof scale === 'number' ? [scale, scale, scale] : scale;\n\n return (\n <AnimatedModelContext.Provider value={contextValue}>\n <group\n ref={groupRef}\n position={position}\n rotation={rotation}\n scale={scaleArray as [number, number, number]}\n >\n <primitive object={scene} />\n {children}\n </group>\n </AnimatedModelContext.Provider>\n );\n }\n);\n\nAnimatedModel.displayName = 'AnimatedModel';\n\n// Preload helper\n(AnimatedModel as any).preload = (url: string) => {\n useGLTF.preload(url);\n};\n","import { forwardRef, useImperativeHandle, useMemo, useEffect, useRef } from 'react';\nimport { useGLTF } from '@react-three/drei';\nimport * as THREE from 'three';\nimport { useMorphTargets } from '../../hooks/useMorphTargets';\nimport { useMeshVisibility } from '../../hooks/useMeshVisibility';\nimport { useMaterialColor } from '../../hooks/useMaterialColor';\nimport { useMaterialTexture } from '../../hooks/useMaterialTexture';\nimport type { MorphableModelProps, MorphableModelHandle } from '../../types';\n\n/**\n * MorphableModel - 3D model with morph targets for shape customization\n * \n * @remarks\n * This component:\n * - Loads GLB/GLTF models with morph targets (blend shapes)\n * - Dynamically discovers available morph targets from the model\n * - Applies morph target values in real-time\n * - Provides imperative API via ref for programmatic control\n * \n * Morph targets are commonly used for:\n * - Character customization (muscular, thin, fat)\n * - Facial expressions (smile, frown, blink)\n * - Clothing fit adjustments\n * \n * @param ref - React ref for imperative API access\n * @param url - URL or path to GLB/GLTF model file with morph targets. Example: `\"/models/character.glb\"`\n * @param position - Model position in 3D space as [x, y, z]. Example: `[0, -1, 0]`. Default: `[0, 0, 0]`\n * @param rotation - Model rotation in radians as [x, y, z]. Example: `[0, Math.PI, 0]`. Default: `[0, 0, 0]`\n * @param scale - Uniform scale (number) or per-axis scale [x, y, z]. Examples: `0.5` or `[1, 2, 1]`. Default: `1`\n * @param morphTargets - Morph target values as key-value pairs where value is 0-1. Example: `{ muscular: 0.5, thin: 0.2 }`\n * @param meshVisibility - Mesh visibility as key-value pairs. Example: `{ \"Hairgirl1\": true, \"Hairgirl2\": false }`\n * @param materialColors - Material colors as key-value pairs. Example: `{ \"Hair_girl_1\": \"#ff0000\" }`\n * @param materialTextures - Material texture maps. Example: `{ \"Metal\": { map: \"/tex.webp\", normalMap: \"/normal.webp\" } }`\n * @param onMorphTargetsFound - Callback when morph targets are discovered from the model. Receives array of morph target names: `[\"muscular\", \"thin\", \"fat\"]`\n * @param onLoad - Callback when model finishes loading. Receives ModelInfo object with metadata: `{ meshes, materials, bones, nodeCount }`\n * @param onError - Callback when model fails to load. Receives Error object\n * \n * @example\n * ```tsx\n * const modelRef = useRef<MorphableModelHandle>(null);\n * const [morphs, setMorphs] = useState({ muscular: 0.5, thin: 0.2 });\n * \n * <MorphableModel\n * ref={modelRef}\n * url=\"/models/character.glb\"\n * morphTargets={morphs}\n * meshVisibility={{ \"Hairgirl1\": true, \"Hairgirl2\": false }}\n * onMorphTargetsFound={(names) => console.log('Available:', names)}\n * position={[0, -1, 0]}\n * />\n * \n * // Control programmatically\n * modelRef.current?.setMorphTarget('muscular', 0.8);\n * ```\n */\nexport const MorphableModel = forwardRef<MorphableModelHandle, MorphableModelProps>(\n (\n {\n url,\n position = [0, 0, 0],\n rotation = [0, 0, 0],\n scale = 1,\n morphTargets,\n meshVisibility,\n materialColors,\n materialTextures,\n onMorphTargetsFound,\n onLoad,\n onError: _onError,\n },\n ref\n ) => {\n const { scene } = useGLTF(url);\n const onLoadRef = useRef(onLoad);\n onLoadRef.current = onLoad;\n const onMorphTargetsFoundRef = useRef(onMorphTargetsFound);\n onMorphTargetsFoundRef.current = onMorphTargetsFound;\n\n // Clone scene\n const clonedScene = useMemo(() => scene.clone(), [scene]);\n\n // Morph targets hook\n const { setMorphTarget, getMorphTargetNames, getMorphTargetValues } = useMorphTargets(\n clonedScene,\n morphTargets\n );\n\n // Mesh visibility hook\n const { setMeshVisibility, getMeshNames } = useMeshVisibility(\n clonedScene,\n meshVisibility\n );\n\n // Material colors hook\n const { setMaterialColor, getMaterialNames, getMaterialColor } = useMaterialColor(\n clonedScene,\n materialColors\n );\n\n // Material textures hook\n const { setMaterialTextures, clearMaterialTextures } = useMaterialTexture(\n clonedScene,\n materialTextures\n );\n\n // Discover morph targets and notify\n useEffect(() => {\n const names = getMorphTargetNames();\n if (names.length > 0) {\n onMorphTargetsFoundRef.current?.(names);\n }\n }, [clonedScene, getMorphTargetNames]);\n\n // Setup shadows and call onLoad\n useEffect(() => {\n if (!clonedScene) return;\n\n const meshes: string[] = [];\n const materials = new Set<string>();\n const bones: string[] = [];\n let nodeCount = 0;\n\n clonedScene.traverse((child) => {\n nodeCount++;\n \n if (child.type === 'Bone') {\n bones.push(child.name);\n }\n \n if ((child as THREE.Mesh).isMesh) {\n const mesh = child as THREE.Mesh;\n meshes.push(mesh.name);\n mesh.castShadow = true;\n mesh.receiveShadow = true;\n \n const mats = Array.isArray(mesh.material) ? mesh.material : [mesh.material];\n mats.forEach((mat) => materials.add(mat.name));\n }\n });\n\n onLoadRef.current?.({\n meshes: meshes.sort(),\n materials: Array.from(materials).sort(),\n bones: bones.sort(),\n nodeCount,\n });\n }, [clonedScene]);\n\n // Expose handle\n useImperativeHandle(ref, () => ({\n setMorphTarget,\n getMorphTargetNames,\n getMorphTargetValues,\n setMeshVisibility,\n getMeshNames,\n setMaterialColor,\n getMaterialNames,\n getMaterialColor,\n setMaterialTextures,\n clearMaterialTextures,\n }));\n\n const scaleArray = typeof scale === 'number' ? [scale, scale, scale] : scale;\n\n return (\n <group position={position} rotation={rotation} scale={scaleArray as [number, number, number]}>\n <primitive object={clonedScene} />\n </group>\n );\n }\n);\n\nMorphableModel.displayName = 'MorphableModel';\n","import { useState, useEffect } from 'react';\r\nimport { createPortal } from '@react-three/fiber';\r\nimport * as THREE from 'three';\r\nimport { useAnimatedModelContext } from '../AnimatedModel/AnimatedModelContext';\r\nimport type { BoneAttachmentProps } from '../../types';\r\n\r\n/**\r\n * BoneAttachment - Attach models to bones of an AnimatedModel parent\r\n * \r\n * @remarks\r\n * This component:\r\n * - Must be used as a child of AnimatedModel\r\n * - Uses React Three Fiber's createPortal to attach to a bone\r\n * - Automatically follows bone transformations during animations\r\n * - Supports position/rotation/scale offsets relative to the bone\r\n * \r\n * Common bone names:\r\n * - Hand bones: \"hand_r\", \"hand_l\", \"DEF-handR\", \"DEF-handL\"\r\n * - Spine/Back: \"spine\", \"spine_upper\", \"back\"\r\n * - Head: \"head\", \"neck\"\r\n * \r\n * @param children - Child components to attach to the bone (typically Model components)\r\n * @param bone - Bone name to attach to. Must match bone name in the parent AnimatedModel. Example: `\"hand_r\"` or `\"DEF-handR\"`\r\n * @param position - Position offset relative to the bone as [x, y, z]. Example: `[0.1, 0, 0]`. Default: `[0, 0, 0]`\r\n * @param rotation - Rotation offset relative to the bone in radians as [x, y, z]. Example: `[0, Math.PI/2, 0]`. Default: `[0, 0, 0]`\r\n * @param scale - Uniform scale (number) or per-axis scale [x, y, z]. Examples: `0.7` or `[1, 2, 1]`. Default: `1`\r\n * \r\n * @example\r\n * ```tsx\r\n * <AnimatedModel url=\"/character.glb\">\r\n * // Sword in right hand\r\n * <BoneAttachment \r\n * bone=\"hand_r\"\r\n * position={[0.1, 0, 0]}\r\n * rotation={[0, Math.PI/2, 0]}\r\n * >\r\n * <Model url=\"/sword.glb\" scale={0.7} />\r\n * </BoneAttachment>\r\n * \r\n * // Shield on back\r\n * <BoneAttachment bone=\"spine_upper\">\r\n * <Model url=\"/shield.glb\" />\r\n * </BoneAttachment>\r\n * </AnimatedModel>\r\n * ```\r\n */\r\nexport function BoneAttachment({\r\n children,\r\n bone,\r\n position = [0, 0, 0],\r\n rotation = [0, 0, 0],\r\n scale = 1,\r\n}: BoneAttachmentProps) {\r\n const { getBone } = useAnimatedModelContext();\r\n const [boneObject, setBoneObject] = useState<THREE.Object3D | null>(null);\r\n\r\n useEffect(() => {\r\n const found = getBone(bone);\r\n if (found) {\r\n setBoneObject(found);\r\n } else {\r\n console.warn(`Bone \"${bone}\" not found in model`);\r\n }\r\n }, [bone, getBone]);\r\n\r\n if (!boneObject) {\r\n return null;\r\n }\r\n\r\n const scaleArray = typeof scale === 'number' ? [scale, scale, scale] : scale;\r\n\r\n return createPortal(\r\n <group\r\n position={position}\r\n rotation={rotation}\r\n scale={scaleArray as [number, number, number]}\r\n >\r\n {children}\r\n </group>,\r\n boneObject\r\n );\r\n}\r\n","import { useMemo, useEffect } from 'react';\r\nimport { useGLTF } from '@react-three/drei';\r\nimport * as THREE from 'three';\r\nimport * as SkeletonUtils from 'three/examples/jsm/utils/SkeletonUtils.js';\r\nimport type { ModelInfo } from '../types';\r\n\r\ninterface UseClonedModelOptions {\r\n onLoad?: (info: ModelInfo) => void;\r\n onError?: (error: Error) => void;\r\n}\r\n\r\nexport function useClonedModel(url: string, options?: UseClonedModelOptions) {\r\n const { scene, animations } = useGLTF(url);\r\n \r\n // Clone scene using SkeletonUtils for proper skinned mesh handling\r\n const clonedScene = useMemo(() => {\r\n return SkeletonUtils.clone(scene);\r\n }, [scene]);\r\n\r\n // Extract model info and setup shadows\r\n useEffect(() => {\r\n if (!clonedScene) return;\r\n\r\n const bones: string[] = [];\r\n const meshes: string[] = [];\r\n const materials = new Set<string>();\r\n let nodeCount = 0;\r\n\r\n clonedScene.traverse((child) => {\r\n nodeCount++;\r\n \r\n if (child.type === 'Bone') {\r\n bones.push(child.name);\r\n }\r\n \r\n if ((child as THREE.Mesh).isMesh) {\r\n const mesh = child as THREE.Mesh;\r\n meshes.push(mesh.name);\r\n \r\n // Setup shadows\r\n mesh.castShadow = true;\r\n mesh.receiveShadow = true;\r\n \r\n // Collect material names\r\n const mats = Array.isArray(mesh.material) ? mesh.material : [mesh.material];\r\n mats.forEach((mat) => {\r\n materials.add(mat.name);\r\n mat.shadowSide = THREE.DoubleSide;\r\n });\r\n }\r\n });\r\n\r\n options?.onLoad?.({\r\n meshes: meshes.sort(),\r\n materials: Array.from(materials).sort(),\r\n bones: bones.sort(),\r\n nodeCount,\r\n });\r\n }, [clonedScene, options]);\r\n\r\n return { scene: clonedScene, animations };\r\n}\r\n\r\n// Preload helper\r\nexport function preloadModel(url: string) {\r\n useGLTF.preload(url);\r\n}\r\n"],"names":["CameraController","position","controlsConfig","camera","useThree","controlsRef","useRef","useEffect","jsx","OrbitControls","Background","background","isColor","BackgroundImage","url","texture","useTexture","useMemo","THREE","LightRenderer","config","intensity","castShadow","angle","penumbra","decay","distance","color","skyColor","groundColor","skyColorObj","groundColorObj","Scene3D","children","controls","shadows","lights","contactShadows","style","className","cameraConfig","contactShadowsConfig","jsxs","Canvas","Suspense","lightConfig","index","ContactShadows","useMeshVisibility","scene","visibility","meshMapRef","visibilityRef","overridesRef","meshMap","child","mesh","useFrame","merged","name","visible","setMeshVisibility","useCallback","getMeshNames","getMeshVisibility","result","useMaterialColor","initialColors","materialMapRef","colorMapRef","materialMap","material","materialName","threeColor","parseColor","targetColor","mat","setMaterialColor","getMaterialNames","getMaterialColor","getAllMaterialColors","colors","useMaterialTexture","textures","gl","textureLoaderRef","ktx2LoaderRef","loadedTexturesRef","loadingTexturesRef","appliedTexturesRef","supportsWASM","ktx2Loader","KTX2Loader","response","error","getLoader","texturesKey","disposed","normalizedTextures","loadTasks","textureConfig","textureType","cacheKey","cached","applyTexture","taskIndex","loadNextTexture","task","loader","loaderType","width","_a","_c","_b","height","_d","_f","_e","delay","progress","percent","type","setMaterialTextures","normalized","clearMaterialTextures","Model","rotation","scale","meshVisibility","materialColors","materialTextures","onLoad","_onError","useGLTF","onLoadRef","clonedScene","clone","meshes","materials","bones","nodeCount","scaleArray","useAnimationController","animations","options","actions","names","useAnimations","currentActionRef","defaultAnimationRef","defaultAnim","animToPlay","match","n","action","playAnimation","opts","loop","crossFadeDuration","restoreDefault","targetAction","a","prev","mixer","onFinished","e","defaultAction","stopAnimation","getAnimationNames","useMorphTargets","initialValues","morphValuesRef","morphNamesRef","meshesWithMorphsRef","values","value","setMorphTarget","getMorphTargetNames","getMorphTargetValues","AnimatedModelContext","createContext","useAnimatedModelContext","context","useContext","AnimatedModel","forwardRef","defaultAnimation","morphTargets","ref","groupRef","gltfScene","SkeletonUtils","timer","morphNames","useImperativeHandle","contextValue","MorphableModel","onMorphTargetsFound","onMorphTargetsFoundRef","BoneAttachment","bone","getBone","boneObject","setBoneObject","useState","found","createPortal","useClonedModel","preloadModel"],"mappings":"6mBAUA,SAASA,GAAiB,CACxB,SAAAC,EACA,eAAAC,CACF,EAGG,CACD,KAAM,CAAE,OAAAC,CAAA,EAAWC,WAAA,EACbC,EAAcC,EAAAA,OAAiC,IAAI,EAEzDC,OAAAA,EAAAA,UAAU,IAAM,CACVN,GAAYE,IACdA,EAAO,SAAS,IAAIF,EAAS,CAAC,EAAGA,EAAS,CAAC,EAAGA,EAAS,CAAC,CAAC,EACzDE,EAAO,uBAAA,EAGHE,EAAY,UACdA,EAAY,QAAQ,OAAO,IAAI,EAAG,EAAG,CAAC,EACtCA,EAAY,QAAQ,OAAA,GAG1B,EAAG,CAACJ,EAAUE,CAAM,CAAC,EAGnBK,EAAAA,IAACC,EAAAA,cAAA,CACC,IAAKJ,EACL,YAAW,GACX,QAASH,EAAe,QACxB,UAAWA,EAAe,UAC1B,WAAYA,EAAe,WAC3B,aAAcA,EAAe,aAC7B,YAAaA,EAAe,YAC5B,YAAaA,EAAe,YAC5B,cAAeA,EAAe,cAC9B,cAAeA,EAAe,cAC9B,WAAYA,EAAe,WAC3B,gBAAiBA,EAAe,eAAA,CAAA,CAGtC,CAKA,SAASQ,GAAW,CAAE,WAAAC,GAAuC,CAE3D,MAAMC,EAAUD,GAAA,YAAAA,EAAY,WAAW,KAEvC,OAAKA,EAEDC,QACM,QAAA,CAAM,OAAO,aAAa,KAAM,CAACD,CAAU,EAAG,EAIjDH,EAAAA,IAACK,GAAA,CAAgB,IAAKF,CAAA,CAAY,EAPjB,IAQ1B,CAEA,SAASE,GAAgB,CAAE,IAAAC,GAAwB,CACjD,MAAMC,EAAUC,EAAAA,WAAWF,CAAG,EAG9BG,OAAAA,EAAAA,QAAQ,IAAM,CACZF,EAAQ,WAAaG,EAAM,cAC7B,EAAG,CAACH,CAAO,CAAC,EAELP,EAAAA,IAAC,YAAA,CAAU,OAAO,aAAa,OAAQO,EAAS,CACzD,CAKA,SAASI,GAAc,CAAE,OAAAC,GAAwC,CAC/D,OAAQA,EAAO,KAAA,CACb,IAAK,OAAQ,CACX,KAAM,CACJ,SAAAnB,EAAW,CAAC,EAAG,GAAI,CAAC,EACpB,UAAAoB,EAAY,GACZ,WAAAC,EAAa,GACb,MAAAC,EAAQ,KAAK,GAAK,EAClB,SAAAC,EAAW,GACX,MAAAC,EAAQ,EACR,SAAAC,EAAW,EACX,MAAAC,EAAQ,SAAA,EACNP,EACJ,OACEZ,EAAAA,IAAC,YAAA,CACC,SAAAP,EACA,UAAAoB,EACA,WAAAC,EACA,MAAAC,EACA,SAAAC,EACA,MAAAC,EACA,SAAAC,EACA,MAAAC,CAAA,CAAA,CAGN,CAEA,IAAK,QAAS,CACZ,KAAM,CACJ,SAAA1B,EAAW,CAAC,EAAG,EAAG,CAAC,EACnB,UAAAoB,EAAY,GACZ,MAAAM,EAAQ,UACR,SAAAD,EAAW,EACX,MAAAD,EAAQ,EACR,WAAAH,EAAa,EAAA,EACXF,EACJ,OACEZ,EAAAA,IAAC,aAAA,CACC,SAAAP,EACA,UAAAoB,EACA,MAAAM,EACA,SAAAD,EACA,MAAAD,EACA,WAAAH,CAAA,CAAA,CAGN,CAEA,IAAK,cAAe,CAClB,KAAM,CACJ,SAAArB,EAAW,CAAC,GAAI,GAAI,EAAE,EACtB,UAAAoB,EAAY,GACZ,MAAAM,EAAQ,UACR,WAAAL,EAAa,EAAA,EACXF,EACJ,OACEZ,EAAAA,IAAC,mBAAA,CACC,SAAAP,EACA,UAAAoB,EACA,MAAAM,EACA,WAAAL,CAAA,CAAA,CAGN,CAEA,IAAK,aAAc,CACjB,KAAM,CACJ,SAAAM,EAAW,UACX,YAAAC,EAAc,UACd,UAAAR,EAAY,GACZ,SAAApB,EAAW,CAAC,EAAG,GAAI,CAAC,CAAA,EAClBmB,EAEEU,EAAcb,EAAAA,QAAQ,IAAM,IAAIC,EAAM,MAAMU,CAAQ,EAAG,CAACA,CAAQ,CAAC,EACjEG,EAAiBd,EAAAA,QAAQ,IAAM,IAAIC,EAAM,MAAMW,CAAW,EAAG,CAACA,CAAW,CAAC,EAEhF,OACErB,EAAAA,IAAC,kBAAA,CACC,KAAM,CAACsB,EAAaC,EAAgBV,CAAS,EAC7C,SAAApB,CAAA,CAAA,CAGN,CAEA,IAAK,UAAW,CACd,KAAM,CACJ,UAAAoB,EAAY,GACZ,MAAAM,EAAQ,SAAA,EACNP,EACJ,OACEZ,EAAAA,IAAC,eAAA,CACC,UAAAa,EACA,MAAAM,CAAA,CAAA,CAGN,CAEA,QACE,OAAO,IAAA,CAEb,CAiFO,SAASK,GAAQ,CACtB,SAAAC,EACA,OAAA9B,EAAS,CAAA,EACT,SAAA+B,EAAW,CAAA,EACX,WAAAvB,EACA,QAAAwB,EAAU,GACV,OAAAC,EAAS,CACP,CAAE,KAAM,UAAW,UAAW,EAAA,EAC9B,CAAE,KAAM,OAAQ,SAAU,CAAC,EAAG,GAAI,CAAC,EAAG,UAAW,GAAI,WAAY,EAAA,CAAK,EAExE,eAAAC,EAAiB,GACjB,MAAAC,EACA,UAAAC,CACF,EAAiB,CACf,MAAMC,EAAe,CACnB,SAAUrC,EAAO,UAAY,CAAC,EAAG,EAAG,CAAC,EACrC,IAAKA,EAAO,KAAO,EAAA,EAGfD,EAAiB,CACrB,QAASgC,EAAS,SAAW,GAC7B,UAAWA,EAAS,WAAa,GACjC,WAAYA,EAAS,YAAc,GACnC,aAAcA,EAAS,cAAgB,GACvC,YAAaA,EAAS,YACtB,YAAaA,EAAS,YACtB,cAAeA,EAAS,cACxB,cAAeA,EAAS,cACxB,WAAYA,EAAS,YAAc,GACnC,gBAAiBA,EAAS,iBAAmB,CAAA,EAGzCO,EACJ,OAAOJ,GAAmB,SACtB,CACE,SAAUA,EAAe,UAAY,CAAC,EAAG,GAAI,CAAC,EAC9C,QAASA,EAAe,SAAW,GACnC,KAAMA,EAAe,MAAQ,CAAA,EAE/BA,EACE,CAAE,SAAU,CAAC,EAAG,GAAI,CAAC,EAA+B,QAAS,GAAK,KAAM,GACxE,KAER,OACE7B,EAAAA,IAAC,MAAA,CAAI,MAAA8B,EAAc,UAAAC,EACjB,SAAAG,EAAAA,KAACC,EAAAA,OAAA,CACC,QAAAR,EACA,OAAQ,CACN,SAAUK,EAAa,SACvB,IAAKA,EAAa,GAAA,EAEpB,MAAO,CAAE,MAAO,OAAQ,OAAQ,MAAA,EAGhC,SAAA,CAAAhC,EAAAA,IAACoC,EAAAA,UAAS,SAAU,KAClB,SAAApC,EAAAA,IAACE,GAAA,CAAW,WAAAC,EAAwB,EACtC,EAGCyB,EAAO,IAAI,CAACS,EAAaC,IACxBtC,EAAAA,IAACW,GAAA,CAAmD,OAAQ0B,CAAA,EAAxC,GAAGA,EAAY,IAAI,IAAIC,CAAK,EAAyB,CAC1E,EAGDtC,EAAAA,IAACoC,EAAAA,SAAA,CAAS,SAAU,KAAO,SAAAX,CAAA,CAAS,EAGpCzB,EAAAA,IAACR,GAAA,CACC,SAAUwC,EAAa,SACvB,eAAAtC,CAAA,CAAA,EAIDuC,GACCjC,EAAAA,IAACuC,EAAAA,eAAA,CACC,SAAUN,EAAqB,SAC/B,QAASA,EAAqB,QAC9B,KAAMA,EAAqB,IAAA,CAAA,CAC7B,CAAA,CAAA,EAGN,CAEJ,CC/UO,SAASO,EACdC,EACAC,EACA,CACA,MAAMC,EAAa7C,EAAAA,OAAgC,IAAI,GAAK,EAEtD8C,EAAgB9C,EAAAA,OAAgC,EAAE,EAElD+C,EAAe/C,EAAAA,OAAgC,EAAE,EAGvDC,EAAAA,UAAU,IAAM,CACV2C,IACFE,EAAc,QAAU,CAAE,GAAGF,CAAA,EAEjC,EAAG,CAACA,CAAU,CAAC,EAGf3C,EAAAA,UAAU,IAAM,CACd,GAAI,CAAC0C,EAAO,OAEZ,MAAMK,MAAc,IAEpBL,EAAM,SAAUM,GAAU,CACxB,GAAKA,EAAqB,OAAQ,CAChC,MAAMC,EAAOD,EACTC,EAAK,MACPF,EAAQ,IAAIE,EAAK,KAAMA,CAAI,CAE/B,CACF,CAAC,EAEDL,EAAW,QAAUG,CACvB,EAAG,CAACL,CAAK,CAAC,EAGVQ,EAAAA,SAAS,IAAM,CACb,MAAMH,EAAUH,EAAW,QAC3B,GAAIG,EAAQ,OAAS,EAAG,OAGxB,MAAMI,EAAS,CAAE,GAAGN,EAAc,QAAS,GAAGC,EAAa,OAAA,EAE3D,SAAW,CAACM,EAAMC,CAAO,IAAK,OAAO,QAAQF,CAAM,EAAG,CACpD,MAAMF,EAAOF,EAAQ,IAAIK,CAAI,EACzBH,GAAQA,EAAK,UAAYI,IAC3BJ,EAAK,QAAUI,EAEnB,CACF,CAAC,EAKD,MAAMC,EAAoBC,EAAAA,YAAY,CAACH,EAAcC,IAAqB,CACxEP,EAAa,QAAQM,CAAI,EAAIC,EAG7B,MAAMJ,EAAOL,EAAW,QAAQ,IAAIQ,CAAI,EACpCH,IACFA,EAAK,QAAUI,EAEnB,EAAG,CAAA,CAAE,EAKCG,EAAeD,EAAAA,YAAY,IACxB,MAAM,KAAKX,EAAW,QAAQ,KAAA,CAAM,EAAE,KAAA,EAC5C,CAAA,CAAE,EAKCa,EAAoBF,EAAAA,YAAY,IAAM,CAC1C,MAAMG,EAAkC,CAAA,EACxC,OAAAd,EAAW,QAAQ,QAAQ,CAACK,EAAMG,IAAS,CACzCM,EAAON,CAAI,EAAIH,EAAK,OACtB,CAAC,EACMS,CACT,EAAG,CAAA,CAAE,EAEL,MAAO,CACL,kBAAAJ,EACA,aAAAE,EACA,kBAAAC,CAAA,CAEJ,CCvFO,SAASE,EACdjB,EACAkB,EACA,CACA,MAAMC,EAAiB9D,EAAAA,OAAuD,IAAI,GAAK,EACjF+D,EAAc/D,EAAAA,OAAiC,IAAI,GAAK,EAG9DC,EAAAA,UAAU,IAAM,CACd,GAAI,CAAC0C,EAAO,OAEZ,MAAMqB,MAAkB,IAExBrB,EAAM,SAAUM,GAAU,CACxB,GAAKA,EAAqB,OAAQ,CAChC,MAAMC,EAAOD,GACK,MAAM,QAAQC,EAAK,QAAQ,EAAIA,EAAK,SAAW,CAACA,EAAK,QAAQ,GAErE,QAASe,GAAa,CAC1BA,EAAS,MAAQ,CAACD,EAAY,IAAIC,EAAS,IAAI,GACjDD,EAAY,IAAIC,EAAS,KAAMA,CAAQ,CAE3C,CAAC,CACH,CACF,CAAC,EAEDH,EAAe,QAAUE,EAGrBH,GACF,OAAO,QAAQA,CAAa,EAAE,QAAQ,CAAC,CAACK,EAAc7C,CAAK,IAAM,CAC/D,MAAM4C,EAAWD,EAAY,IAAIE,CAAY,EAC7C,GAAID,GAAY,CAAC,MAAM,QAAQA,CAAQ,EAAG,CACxC,MAAME,EAAaC,EAAW/C,CAAK,EAC9B4C,EAAiB,OACnBA,EAAiB,MAAM,KAAKE,CAAU,EAEzCJ,EAAY,QAAQ,IAAIG,EAAcC,CAAU,CAClD,CACF,CAAC,CAEL,EAAG,CAACxB,EAAOkB,CAAa,CAAC,EAGzBV,EAAAA,SAAS,IAAM,CACbY,EAAY,QAAQ,QAAQ,CAACM,EAAaH,IAAiB,CACzD,MAAMD,EAAWH,EAAe,QAAQ,IAAII,CAAY,EACxD,GAAID,GAAY,CAAC,MAAM,QAAQA,CAAQ,EAAG,CACxC,MAAMK,EAAML,EACRK,EAAI,OAAS,CAACA,EAAI,MAAM,OAAOD,CAAW,IAC5CC,EAAI,MAAM,KAAKD,CAAW,EAC1BC,EAAI,YAAc,GAEtB,CACF,CAAC,CACH,CAAC,EAKD,MAAMF,EAAc/C,GACd,OAAOA,GAAU,SACZ,IAAIT,EAAM,MAAMS,CAAK,EAErB,IAAIT,EAAM,MAAMS,EAAM,CAAC,EAAGA,EAAM,CAAC,EAAGA,EAAM,CAAC,CAAC,EASjDkD,EAAmBf,EAAAA,YAAY,CACnCU,EACA7C,IACG,CACH,MAAM8C,EAAaC,EAAW/C,CAAK,EACnC0C,EAAY,QAAQ,IAAIG,EAAcC,CAAU,EAEhD,MAAMF,EAAWH,EAAe,QAAQ,IAAII,CAAY,EACxD,GAAID,GAAY,CAAC,MAAM,QAAQA,CAAQ,EAAG,CACxC,MAAMK,EAAML,EACRK,EAAI,QACNA,EAAI,MAAM,KAAKH,CAAU,EACzBG,EAAI,YAAc,GAEtB,CACF,EAAG,CAAA,CAAE,EAKCE,EAAmBhB,EAAAA,YAAY,IAC5B,MAAM,KAAKM,EAAe,QAAQ,KAAA,CAAM,EAAE,KAAA,EAChD,CAAA,CAAE,EAMCW,EAAmBjB,cAAaU,GAAwC,CAC5E,MAAMD,EAAWH,EAAe,QAAQ,IAAII,CAAY,EACxD,GAAID,GAAY,CAAC,MAAM,QAAQA,CAAQ,EAAG,CACxC,MAAMK,EAAML,EACZ,GAAIK,EAAI,MACN,MAAO,IAAMA,EAAI,MAAM,aAAA,CAE3B,CACA,OAAO,IACT,EAAG,CAAA,CAAE,EAMCI,EAAuBlB,EAAAA,YAAY,IAA8B,CACrE,MAAMmB,EAAiC,CAAA,EACvC,OAAAb,EAAe,QAAQ,QAAQ,CAACG,EAAUZ,IAAS,CACjD,GAAI,CAAC,MAAM,QAAQY,CAAQ,EAAG,CAC5B,MAAMK,EAAML,EACRK,EAAI,QACNK,EAAOtB,CAAI,EAAI,IAAMiB,EAAI,MAAM,aAAA,EAEnC,CACF,CAAC,EACMK,CACT,EAAG,CAAA,CAAE,EAEL,MAAO,CACL,iBAAAJ,EACA,iBAAAC,EACA,iBAAAC,EACA,qBAAAC,CAAA,CAEJ,CClHO,SAASE,EACdjC,EACAkC,EACA,CACA,KAAM,CAAE,GAAAC,CAAA,EAAOhF,WAAA,EACTgE,EAAiB9D,EAAAA,OAAoC,IAAI,GAAK,EAC9D+E,EAAmB/E,EAAAA,OAA4B,IAAIY,EAAM,aAAe,EACxEoE,EAAgBhF,EAAAA,OAA0B,IAAI,EAC9CiF,EAAoBjF,EAAAA,OAAmC,IAAI,GAAK,EAChEkF,EAAqBlF,EAAAA,OAAoB,IAAI,GAAK,EAClDmF,EAAqBnF,EAAAA,OAAe,EAAE,EAG5CC,EAAAA,UAAU,IAAM,CACd,GAAI,CAAC+E,EAAc,QACjB,GAAI,CAEF,MAAMI,EAAe,OAAO,aAAgB,UAC1C,OAAO,YAAY,UAAa,WAElC,QAAQ,IAAI,wCAAwC,EACpD,QAAQ,IAAI,4BAA4BA,CAAY,EAAE,EACtD,QAAQ,IAAI,aAAaN,EAAG,aAAa,QAAQ,EAAE,EAEnD,MAAMO,EAAa,IAAIC,cACvBD,EAAW,kBAAkB,SAAS,EACtCA,EAAW,cAAcP,CAAE,EAC3BE,EAAc,QAAUK,EACxB,QAAQ,IAAI,+CAA+C,EAC3D,QAAQ,IAAI,+CAA+C,EAG3D,MAAM,8BAA8B,EACjC,KAAKE,GAAY,CACZA,EAAS,GACX,QAAQ,IAAI,4DAA4D,EAExE,QAAQ,MAAM,wDAAyDA,EAAS,MAAM,CAE1F,CAAC,EACA,MAAMC,GAAS,CACd,QAAQ,MAAM,gEAAiEA,CAAK,CACtF,CAAC,EAEEJ,IACH,QAAQ,KAAK,8FAA8F,EAC3G,QAAQ,KAAK,+EAA+E,EAEhG,OAASI,EAAO,CACd,QAAQ,MAAM,0DAA2DA,CAAK,CAChF,CAGF,MAAO,IAAM,CACPR,EAAc,UAChBA,EAAc,QAAQ,QAAA,EACtBA,EAAc,QAAU,KAE5B,CACF,EAAG,CAACF,CAAE,CAAC,EAGP7E,EAAAA,UAAU,IAAM,CACd,GAAI,CAAC0C,EAAO,OAEZ,MAAMqB,MAAkB,IAExBrB,EAAM,SAAUM,GAAU,CACxB,GAAKA,EAAqB,OAAQ,CAChC,MAAMC,EAAOD,GACK,MAAM,QAAQC,EAAK,QAAQ,EAAIA,EAAK,SAAW,CAACA,EAAK,QAAQ,GAErE,QAASe,GAAa,CAC1BA,EAAS,MAAQ,CAACD,EAAY,IAAIC,EAAS,IAAI,GACjDD,EAAY,IAAIC,EAAS,KAAMA,CAAQ,CAE3C,CAAC,CACH,CACF,CAAC,EAEDH,EAAe,QAAUE,CAC3B,EAAG,CAACrB,CAAK,CAAC,EAGV,MAAM8C,EAAYjC,cAAahD,GACzBA,EAAI,YAAA,EAAc,SAAS,OAAO,EAC/BwE,EAAc,QAIZA,EAAc,SAHnB,QAAQ,KAAK,gFAAgF,EACtFD,EAAiB,SAIrBA,EAAiB,QACvB,CAAA,CAAE,EAGL9E,EAAAA,UAAU,IAAM,CACd,GAAI,CAAC4E,GAAY,CAAClC,EAAO,OAEzB,MAAMqB,EAAcF,EAAe,QAEnC,GAAIE,EAAY,OAAS,EAAG,OAG5B,MAAM0B,EAAc,KAAK,UAAUb,CAAQ,EAC3C,GAAIM,EAAmB,UAAYO,EAAa,CAC9C,QAAQ,IAAI,0DAA0D,EACtE,MACF,CACAP,EAAmB,QAAUO,EAE7B,IAAIC,EAAW,GAGf,MAAMC,MAAyB,IAC/B,OAAO,QAAQf,CAAQ,EAAE,QAAQ,CAAC,CAACxB,EAAMvC,CAAM,IAAM,CACnD8E,EAAmB,IAAIvC,EAAM,OAAOvC,GAAW,SAAW,CAAE,IAAKA,CAAA,EAAWA,CAAM,CACpF,CAAC,EAED,QAAQ,IAAI,iDAAkD8E,EAAmB,KAAM,WAAW,EAGlG,MAAMC,EAKD,CAAA,EAELD,EAAmB,QAAQ,CAACE,EAAe5B,IAAiB,CAC1D,MAAMD,EAAWD,EAAY,IAAIE,CAAY,EAC7C,GAAI,CAACD,GAAY,CAAEA,EAAiB,uBAAwB,CAC1D,QAAQ,KAAK,kCAAkCC,CAAY,yCAAyC,EACpG,MACF,CAEA,MAAMI,EAAML,EAEZK,EAAI,IAAM,KACVA,EAAI,UAAY,KAChBA,EAAI,aAAe,KACnBA,EAAI,aAAe,KACnBA,EAAI,YAAc,KAClBA,EAAI,SAAW,IAAI1D,EAAM,MAAM,CAAQ,EACvC0D,EAAI,kBAAoB,EACxBA,EAAI,MAAQ,KACZA,EAAI,YAAc,GAElB,OAAO,QAAQwB,CAAa,EAAE,QAAQ,CAAC,CAACC,EAAavF,CAAG,IAAM,CAC5D,GAAI,CAACA,EAAK,OAEV,MAAMwF,EAAW,GAAG9B,CAAY,IAAI6B,CAAW,IAAIvF,CAAG,GAGhDyF,EAAShB,EAAkB,QAAQ,IAAIe,CAAQ,EACrD,GAAIC,EAAQ,CACV,QAAQ,IAAI,8CAA8C/B,CAAY,IAAI6B,CAAW,EAAE,EAClFJ,GACHO,EAAa5B,EAAKyB,EAAuCE,CAAM,EAEjE,MACF,CAGA,GAAIf,EAAmB,QAAQ,IAAIc,CAAQ,EAAG,CAC5C,QAAQ,IAAI,kDAAkD9B,CAAY,IAAI6B,CAAW,EAAE,EAC3F,MACF,CAEAF,EAAU,KAAK,CAAE,aAAA3B,EAAc,YAAA6B,EAAa,IAAAvF,EAAK,SAAU8D,EAAK,CAClE,CAAC,CACH,CAAC,EAED,QAAQ,IAAI,+BAA+BuB,EAAU,MAAM,mBAAmB,EAG9E,IAAIM,EAAY,EAChB,MAAMC,EAAkB,IAAM,CAC5B,GAAIT,GAAYQ,GAAaN,EAAU,OAAQ,CAC7C,QAAQ,IAAI,0CAA0C,EACtD,MACF,CAEA,MAAMQ,EAAOR,EAAUM,GAAW,EAC5BH,EAAW,GAAGK,EAAK,YAAY,IAAIA,EAAK,WAAW,IAAIA,EAAK,GAAG,GAErE,QAAQ,IAAI,iCAAiCF,CAAS,IAAIN,EAAU,MAAM,MAAMQ,EAAK,YAAY,IAAIA,EAAK,WAAW,SAASA,EAAK,GAAG,EAAE,EAExInB,EAAmB,QAAQ,IAAIc,CAAQ,EAGvC,MAAMM,EAASb,EAAUY,EAAK,GAAG,EAC3BE,EAAaF,EAAK,IAAI,YAAA,EAAc,SAAS,OAAO,EAAI,aAAe,gBAC7E,QAAQ,IAAI,8BAA8BE,CAAU,QAAQF,EAAK,GAAG,EAAE,EAEtEC,EAAO,KACLD,EAAK,IACJ5F,GAAY,iBAGX,GAFAyE,EAAmB,QAAQ,OAAOc,CAAQ,EAEtCL,EAAU,CACZ,QAAQ,IAAI,uDAAuDU,EAAK,YAAY,IAAIA,EAAK,WAAW,EAAE,EAC1G5F,EAAQ,QAAA,EACR,MACF,CAEA,MAAM+F,IAAQC,EAAAhG,EAAQ,QAAR,YAAAgG,EAAe,UAASC,GAAAC,EAAAlG,EAAQ,SAAR,YAAAkG,EAAgB,OAAhB,YAAAD,EAAsB,QAAS,UAC/DE,IAASC,EAAApG,EAAQ,QAAR,YAAAoG,EAAe,WAAUC,GAAAC,EAAAtG,EAAQ,SAAR,YAAAsG,EAAgB,OAAhB,YAAAD,EAAsB,SAAU,UACxE,QAAQ,IAAI,kCAAkCT,EAAK,YAAY,IAAIA,EAAK,WAAW,KAAKG,CAAK,IAAII,CAAM,GAAG,EAG1GnG,EAAQ,WAAa4F,EAAK,cAAgB,OAASA,EAAK,cAAgB,cACpEzF,EAAM,eACNA,EAAM,aACVH,EAAQ,MAAQG,EAAM,eACtBH,EAAQ,MAAQG,EAAM,eACtBH,EAAQ,MAAQ,GAEhBwE,EAAkB,QAAQ,IAAIe,EAAUvF,CAAO,EAC/CyF,EAAaG,EAAK,SAAUA,EAAK,YAAuC5F,CAAO,EAG/E,MAAMuG,EAAQX,EAAK,IAAI,SAAS,OAAO,EAAI,IAAM,IACjD,QAAQ,IAAI,gCAAgCW,CAAK,2BAA2B,EAC5E,WAAWZ,EAAiBY,CAAK,CACnC,EACCC,GAAa,CAEZ,GAAIA,EAAS,iBAAkB,CAC7B,MAAMC,EAAU,KAAK,MAAOD,EAAS,OAASA,EAAS,MAAS,GAAG,EAC/DC,EAAU,KAAO,GACnB,QAAQ,IAAI,iCAAiCb,EAAK,YAAY,IAAIA,EAAK,WAAW,KAAKa,CAAO,GAAG,CAErG,CACF,EACC1B,GAAU,CACTN,EAAmB,QAAQ,OAAOc,CAAQ,EAErCL,IACH,QAAQ,MAAM,yCAAyCU,EAAK,YAAY,IAAIA,EAAK,WAAW,SAASA,EAAK,GAAG,EAAE,EAC/G,QAAQ,MAAM,sCAAuCb,CAAK,EAC1D,QAAQ,MAAM,oCAAqCe,CAAU,EAGzDF,EAAK,IAAI,SAAS,OAAO,IAC3B,QAAQ,MAAM,8EAA8E,EAC5F,QAAQ,MAAM,mGAAmG,IAKrH,WAAWD,EAAiB,GAAG,CACjC,CAAA,CAEJ,EAGA,OAAAA,EAAA,EAEO,IAAM,CACX,QAAQ,IAAI,wCAAwC,EACpDT,EAAW,EACb,CACF,EAAG,CAAChD,EAAOkC,EAAUY,CAAS,CAAC,EAE/B,MAAMS,EAAe,CACnBjC,EACAkD,EACA1G,IACG,CACH,OAAQ0G,EAAA,CACN,IAAK,MACHlD,EAAS,IAAMxD,EACf,MACF,IAAK,YACHwD,EAAS,UAAYxD,EACrB,MACF,IAAK,eACHwD,EAAS,aAAexD,EACxB,MACF,IAAK,eACHwD,EAAS,aAAexD,EACxB,MACF,IAAK,cACHwD,EAAS,YAAcxD,EACvBwD,EAAS,SAAW,IAAIrD,EAAM,MAAM,QAAQ,EAC5CqD,EAAS,kBAAoB,EAC7B,MACF,IAAK,WAGH,QAAQ,IAAI,+CAA+CA,EAAS,IAAI,2BAA2B,EACnG,MACF,IAAK,QACHA,EAAS,MAAQxD,EACjB,KAAA,CAEJwD,EAAS,YAAc,EACzB,EAOMmD,EAAsB5D,EAAAA,YAAY,CACtCU,EACA4B,IACG,CACH,MAAMuB,EAAa,OAAOvB,GAAkB,SACxC,CAAE,IAAKA,GACPA,EAEE7B,EAAWH,EAAe,QAAQ,IAAII,CAAY,EACxD,GAAI,CAACD,GAAY,CAAEA,EAAiB,uBAAwB,CAC1D,QAAQ,KAAK,aAAaC,CAAY,yCAAyC,EAC/E,MACF,CAEA,MAAMI,EAAML,EAEZ,OAAO,QAAQoD,CAAU,EAAE,QAAQ,CAAC,CAACtB,EAAavF,CAAG,IAAM,CACzD,GAAI,CAACA,EAAK,OAEV,MAAMwF,EAAW,GAAG9B,CAAY,IAAI6B,CAAW,IAAIvF,CAAG,GAGhDyF,EAAShB,EAAkB,QAAQ,IAAIe,CAAQ,EACrD,GAAIC,EAAQ,CACVC,EAAa5B,EAAKyB,EAAuCE,CAAM,EAC/D,MACF,CAGeR,EAAUjF,CAAG,EAErB,KACLA,EACCC,GAAY,CACXA,EAAQ,WAAasF,IAAgB,OAASA,IAAgB,cAC1DnF,EAAM,eACNA,EAAM,aACVH,EAAQ,MAAQG,EAAM,eACtBH,EAAQ,MAAQG,EAAM,eAEtBqE,EAAkB,QAAQ,IAAIe,EAAUvF,CAAO,EAC/CyF,EAAa5B,EAAKyB,EAAuCtF,CAAO,CAClE,EACA,OACC+E,GAAU,CACT,QAAQ,MAAM,0BAA0BhF,CAAG,IAAKgF,CAAK,CACvD,CAAA,CAEJ,CAAC,CACH,EAAG,CAACC,CAAS,CAAC,EAKRjB,EAAmBhB,EAAAA,YAAY,IAC5B,MAAM,KAAKM,EAAe,QAAQ,KAAA,CAAM,EAAE,KAAA,EAChD,CAAA,CAAE,EAKCwD,EAAwB9D,cAAaU,GAAyB,CAClE,MAAMD,EAAWH,EAAe,QAAQ,IAAII,CAAY,EACxD,GAAI,CAACD,GAAY,CAAEA,EAAiB,uBAAwB,OAE5D,MAAMK,EAAML,EACZK,EAAI,IAAM,KACVA,EAAI,UAAY,KAChBA,EAAI,aAAe,KACnBA,EAAI,aAAe,KACnBA,EAAI,YAAc,KAClBA,EAAI,SAAW,KACfA,EAAI,MAAQ,KACZA,EAAI,YAAc,EACpB,EAAG,CAAA,CAAE,EAEL,MAAO,CACL,oBAAA8C,EACA,iBAAA5C,EACA,sBAAA8C,CAAA,CAEJ,CCzXO,SAASC,GAAM,CACpB,IAAA/G,EACA,SAAAb,EAAW,CAAC,EAAG,EAAG,CAAC,EACnB,SAAA6H,EAAW,CAAC,EAAG,EAAG,CAAC,EACnB,MAAAC,EAAQ,EACR,eAAAC,EACA,eAAAC,EACA,iBAAAC,EACA,OAAAC,EACA,QAASC,CACX,EAAe,CACb,KAAM,CAAE,MAAAnF,CAAA,EAAUoF,EAAAA,QAAQvH,CAAG,EACvBwH,EAAYhI,EAAAA,OAAO6H,CAAM,EAC/BG,EAAU,QAAUH,EAGpB,MAAMI,EAActH,EAAAA,QAAQ,IAAM,CAChC,MAAMuH,EAAQvF,EAAM,MAAA,EAGdwF,EAAmB,CAAA,EACnBC,MAAgB,IAChBC,EAAkB,CAAA,EACxB,IAAIC,EAAY,EAEhB,OAAAJ,EAAM,SAAUjF,GAAU,CAOxB,GANAqF,IAEIrF,EAAM,OAAS,QACjBoF,EAAM,KAAKpF,EAAM,IAAI,EAGlBA,EAAqB,OAAQ,CAChC,MAAMC,EAAOD,EACbkF,EAAO,KAAKjF,EAAK,IAAI,EACrBA,EAAK,WAAa,GAClBA,EAAK,cAAgB,IAER,MAAM,QAAQA,EAAK,QAAQ,EAAIA,EAAK,SAAW,CAACA,EAAK,QAAQ,GACrE,QAASoB,GAAQ8D,EAAU,IAAI9D,EAAI,IAAI,CAAC,CAC/C,CACF,CAAC,EAGD,WAAW,IAAM,QACfmC,EAAAuB,EAAU,UAAV,MAAAvB,EAAA,KAAAuB,EAAoB,CAClB,OAAQG,EAAO,KAAA,EACf,UAAW,MAAM,KAAKC,CAAS,EAAE,KAAA,EACjC,MAAOC,EAAM,KAAA,EACb,UAAAC,CAAA,EAEJ,EAAG,CAAC,EAEGJ,CACT,EAAG,CAACvF,CAAK,CAAC,EAGVD,EAAkBuF,EAAaP,CAAc,EAG7C9D,EAAiBqE,EAAaN,CAAc,EAG5C/C,EAAmBqD,EAAaL,CAAgB,EAEhD,MAAMW,EAAa,OAAOd,GAAU,SAAW,CAACA,EAAOA,EAAOA,CAAK,EAAIA,EAEvE,OACEvH,EAAAA,IAAC,QAAA,CAAM,SAAAP,EAAoB,SAAA6H,EAAoB,MAAOe,EACpD,SAAArI,EAAAA,IAAC,YAAA,CAAU,OAAQ+H,CAAA,CAAa,CAAA,CAClC,CAEJ,CAGAV,GAAM,QAAW/G,GAAgB,CAC/BuH,EAAAA,QAAQ,QAAQvH,CAAG,CACrB,EC9GO,SAASgI,GACdC,EACA9F,EACA+F,EACA,CACA,KAAM,CAAE,QAAAC,EAAS,MAAAC,CAAA,EAAUC,EAAAA,cAAcJ,EAAY9F,CAAK,EACpDmG,EAAmB9I,EAAAA,OAAqC,IAAI,EAC5D+I,EAAsB/I,EAAAA,OAA2B0I,GAAA,YAAAA,EAAS,gBAAgB,EAGhFzI,EAAAA,UAAU,IAAM,CACd,GAAI2I,EAAM,SAAW,EAAG,OAExB,MAAMI,EAAcD,EAAoB,QACxC,IAAIE,EAAaL,EAAM,CAAC,EAExB,GAAII,EAAa,CACf,MAAME,EAAQN,EAAM,KAAMO,GAAMA,IAAMH,GAAeG,EAAE,SAASH,CAAW,CAAC,EACxEE,IAAOD,EAAaC,EAC1B,CAEA,MAAME,EAAST,EAAQM,CAAU,EAC7BG,IACFA,EAAO,MAAA,EAAQ,OAAO,EAAG,EAAE,KAAA,EAC3BN,EAAiB,QAAUM,EAE/B,EAAG,CAACT,EAASC,CAAK,CAAC,EAEnB,MAAMS,EAAgB7F,EAAAA,YACpB,CACEH,EACAiG,IAKG,CACH,KAAM,CACJ,KAAAC,EAAO,GACP,kBAAAC,EAAoB,GACpB,eAAAC,EAAiB,EAAA,EACfH,GAAQ,CAAA,EAGZ,IAAII,EAAef,EAAQtF,CAAI,EAE/B,GAAI,CAACqG,EAAc,CACjB,MAAMR,EAAQ,OAAO,KAAKP,CAAO,EAAE,KAChCgB,GACCA,EAAE,YAAA,EAAc,SAAStG,EAAK,aAAa,GAC3CA,EAAK,YAAA,EAAc,SAASsG,EAAE,aAAa,CAAA,EAE3CT,IACFQ,EAAef,EAAQO,CAAK,EAEhC,CAEA,GAAI,CAACQ,EAAc,CACjB,QAAQ,KAAK,cAAcrG,CAAI,2BAA2BuF,EAAM,KAAK,IAAI,CAAC,EAAE,EAC5E,MACF,CAGA,MAAMgB,EAAOd,EAAiB,QAC9B,GAAI,EAAAc,IAASF,GAAgBA,EAAa,UAAA,KAKtCE,GAAQA,IAASF,GACnBE,EAAK,QAAQJ,CAAiB,EAIhCE,EAAa,MAAA,EACbA,EAAa,OAAOF,CAAiB,EACrCE,EAAa,QACXH,EAAO3I,EAAM,WAAaA,EAAM,SAChC2I,EAAO,IAAW,CAAA,EAEpBG,EAAa,kBAAoB,CAACH,EAClCG,EAAa,KAAA,EAGRH,GACHG,EAAa,SAAA,EAAW,OAAO,CAAC,EAGlCZ,EAAiB,QAAUY,EAGvBD,GAAkB,CAACF,GAAQR,EAAoB,SAAS,CAC1D,MAAMc,EAAQH,EAAa,SAAA,EACrBI,EAAcC,GAAyC,CAC3D,GAAIA,EAAE,SAAWL,EAAc,CAC7BG,EAAM,oBAAoB,WAAYC,CAAU,EAEhD,MAAME,EAAgBrB,EAAQI,EAAoB,OAAQ,EACtDiB,IACFN,EAAa,QAAQF,CAAiB,EACtCQ,EAAc,MAAA,EAAQ,OAAOR,CAAiB,EAAE,KAAA,EAChDV,EAAiB,QAAUkB,EAE/B,CACF,EACAH,EAAM,iBAAiB,WAAYC,CAAU,CAC/C,CACF,EACA,CAACnB,EAASC,CAAK,CAAA,EAGXqB,EAAgBzG,EAAAA,YAAY,IAAM,QACtCiD,EAAAqC,EAAiB,UAAjB,MAAArC,EAA0B,QAAQ,IAClCqC,EAAiB,QAAU,IAC7B,EAAG,CAAA,CAAE,EAECoB,EAAoB1G,EAAAA,YAAY,IAAMoF,EAAO,CAACA,CAAK,CAAC,EAE1D,MAAO,CACL,cAAAS,EACA,cAAAY,EACA,kBAAAC,EACA,QAAAvB,CAAA,CAEJ,CChIO,SAASwB,EACdxH,EACAyH,EACA,CACA,MAAMC,EAAiBrK,EAAAA,OAA+BoK,GAAiB,EAAE,EACnEE,EAAgBtK,EAAAA,OAAiB,EAAE,EACnCuK,EAAsBvK,EAAAA,OAAqB,EAAE,EAGnDC,EAAAA,UAAU,IAAM,CACd,MAAM2I,MAAY,IACZT,EAAuB,CAAA,EAE7BxF,EAAM,SAAUM,GAAU,CAEtBA,aAAiBrC,EAAM,MACvBqC,EAAM,uBACNA,EAAM,wBAENkF,EAAO,KAAKlF,CAAK,EACjB,OAAO,KAAKA,EAAM,qBAAqB,EAAE,QAASI,GAAS,CACzDuF,EAAM,IAAIvF,CAAI,CAChB,CAAC,EAEL,CAAC,EAEDiH,EAAc,QAAU,MAAM,KAAK1B,CAAK,EAAE,KAAA,EAC1C2B,EAAoB,QAAUpC,CAChC,EAAG,CAACxF,CAAK,CAAC,EAGVQ,EAAAA,SAAS,IAAM,CACb,MAAMqH,EAASH,EAAe,QAE9BE,EAAoB,QAAQ,QAASrH,GAAS,CACxC,CAACA,EAAK,uBAAyB,CAACA,EAAK,uBAEzC,OAAO,QAAQsH,CAAM,EAAE,QAAQ,CAAC,CAACnH,EAAMoH,CAAK,IAAM,CAChD,MAAMjI,EAAQU,EAAK,sBAAuBG,CAAI,EAC1Cb,IAAU,SACZU,EAAK,sBAAuBV,CAAK,EAAIiI,EAEzC,CAAC,CACH,CAAC,CACH,CAAC,EAGDxK,EAAAA,UAAU,IAAM,CACVmK,IACFC,EAAe,QAAU,CAAE,GAAGD,CAAA,EAElC,EAAG,CAACA,CAAa,CAAC,EAElB,MAAMM,EAAiBlH,EAAAA,YAAY,CAACH,EAAcoH,IAAkB,CAClEJ,EAAe,QAAQhH,CAAI,EAAI,KAAK,IAAI,EAAG,KAAK,IAAI,EAAGoH,CAAK,CAAC,CAC/D,EAAG,CAAA,CAAE,EAECE,EAAsBnH,EAAAA,YAAY,IAAM8G,EAAc,QAAS,CAAA,CAAE,EAEjEM,EAAuBpH,EAAAA,YAAY,KAAO,CAAE,GAAG6G,EAAe,OAAA,GAAY,EAAE,EAElF,MAAO,CACL,eAAAK,EACA,oBAAAC,EACA,qBAAAC,CAAA,CAEJ,CCnEO,MAAMC,GAAuBC,EAAAA,cAAgD,IAAI,EAEjF,SAASC,IAA0B,CACxC,MAAMC,EAAUC,EAAAA,WAAWJ,EAAoB,EAC/C,GAAI,CAACG,EACH,MAAM,IAAI,MAAM,qDAAqD,EAEvE,OAAOA,CACT,CCgEO,MAAME,EAAgBC,EAAAA,WAC3B,CACE,CACE,IAAA3K,EACA,SAAAb,EAAW,CAAC,EAAG,EAAG,CAAC,EACnB,SAAA6H,EAAW,CAAC,EAAG,EAAG,CAAC,EACnB,MAAAC,EAAQ,EACR,iBAAA2D,EACA,aAAAC,EACA,eAAA3D,EACA,eAAAC,EACA,iBAAAC,EACA,OAAAC,EACA,QAASC,EACT,SAAAnG,CAAA,EAEF2J,IACG,CACH,MAAMC,EAAWvL,EAAAA,OAAoB,IAAI,EACnCsK,EAAgBtK,EAAAA,OAAiB,EAAE,EACnCgI,EAAYhI,EAAAA,OAAO6H,CAAM,EAC/BG,EAAU,QAAUH,EAGpB,KAAM,CAAE,MAAO2D,EAAW,WAAA/C,CAAA,EAAeV,EAAAA,QAAQvH,CAAG,EAC9CmC,EAAQhC,EAAAA,QAAQ,IAAM8K,EAAc,MAAMD,CAAS,EAAG,CAACA,CAAS,CAAC,EAGjE,CAAE,cAAAnC,EAAe,cAAAY,EAAe,kBAAAC,CAAA,EAAsB1B,GAC1DC,EACA9F,EACA,CAAE,iBAAAyI,CAAA,CAAiB,EAIf,CAAE,eAAAV,GAAmBP,EACzBxH,EACA0I,CAAA,EAII,CAAE,kBAAA9H,EAAmB,aAAAE,CAAA,EAAiBf,EAC1CC,EACA+E,CAAA,EAII,CAAE,iBAAAnD,EAAkB,iBAAAC,EAAkB,iBAAAC,CAAA,EAAqBb,EAC/DjB,EACAgF,CAAA,EAII,CAAE,oBAAAP,EAAqB,sBAAAE,CAAA,EAA0B1C,EACrDjC,EACAiF,CAAA,EAIF3H,EAAAA,UAAU,IAAM,CACd,GAAI,CAAC0C,EAAO,OAEZ,MAAM+I,EAAQ,WAAW,IAAM,OAC7B,MAAMrD,EAAkB,CAAA,EAClBF,EAAmB,CAAA,EACnBC,MAAgB,IAChBuD,MAAiB,IACvB,IAAIrD,EAAY,EAEhB3F,EAAM,SAAUM,GAAU,CAOxB,GANAqF,IAEIrF,EAAM,OAAS,QACjBoF,EAAM,KAAKpF,EAAM,IAAI,EAGlBA,EAAqB,OAAQ,CAChC,MAAMC,EAAOD,EACbkF,EAAO,KAAKjF,EAAK,IAAI,EAGrBA,EAAK,WAAa,GAClBA,EAAK,cAAgB,IAER,MAAM,QAAQA,EAAK,QAAQ,EAAIA,EAAK,SAAW,CAACA,EAAK,QAAQ,GACrE,QAASoB,GAAQ,CACpB8D,EAAU,IAAI9D,EAAI,IAAI,EACtBA,EAAI,WAAa1D,EAAM,UACzB,CAAC,EAEGsC,EAAK,uBACP,OAAO,KAAKA,EAAK,qBAAqB,EAAE,QAASG,GAAS,CACxDsI,EAAW,IAAItI,CAAI,CACrB,CAAC,CAEL,CACF,CAAC,EAEDiH,EAAc,QAAU,MAAM,KAAKqB,CAAU,EAAE,KAAA,GAE/ClF,EAAAuB,EAAU,UAAV,MAAAvB,EAAA,KAAAuB,EAAoB,CAClB,OAAQG,EAAO,KAAA,EACf,UAAW,MAAM,KAAKC,CAAS,EAAE,KAAA,EACjC,MAAOC,EAAM,KAAA,EACb,UAAAC,EACA,WAAYG,EAAW,IAAKkB,GAAMA,EAAE,IAAI,EACxC,iBAAkBW,EAAc,OAAA,EAEpC,EAAG,CAAC,EAEJ,MAAO,IAAM,aAAaoB,CAAK,CACjC,EAAG,CAAC/I,EAAO8F,CAAU,CAAC,EAGtBmD,EAAAA,oBAAoBN,EAAK,KAAO,CAC9B,cAAAjC,EACA,cAAAY,EACA,kBAAAC,EACA,SAAU,IAAMqB,EAAS,QACzB,eAAAb,EACA,oBAAqB,IAAMJ,EAAc,QACzC,kBAAA/G,EACA,aAAAE,EACA,iBAAAc,EACA,iBAAAC,EACA,iBAAAC,EACA,oBAAA2C,EACA,sBAAAE,CAAA,EACA,EAGF,MAAMuE,EAAelL,EAAAA,QACnB,KAAO,CACL,MAAAgC,EACA,QAAUU,GAAiBV,EAAM,gBAAgBU,CAAI,GAAK,IAAA,GAE5D,CAACV,CAAK,CAAA,EAGF4F,EAAa,OAAOd,GAAU,SAAW,CAACA,EAAOA,EAAOA,CAAK,EAAIA,EAEvE,OACEvH,EAAAA,IAAC2K,GAAqB,SAArB,CAA8B,MAAOgB,EACpC,SAAAzJ,EAAAA,KAAC,QAAA,CACC,IAAKmJ,EACL,SAAA5L,EACA,SAAA6H,EACA,MAAOe,EAEP,SAAA,CAAArI,EAAAA,IAAC,YAAA,CAAU,OAAQyC,CAAA,CAAO,EACzBhB,CAAA,CAAA,CAAA,EAEL,CAEJ,CACF,EAEAuJ,EAAc,YAAc,gBAG3BA,EAAsB,QAAW1K,GAAgB,CAChDuH,EAAAA,QAAQ,QAAQvH,CAAG,CACrB,ECtLO,MAAMsL,GAAiBX,EAAAA,WAC5B,CACE,CACE,IAAA3K,EACA,SAAAb,EAAW,CAAC,EAAG,EAAG,CAAC,EACnB,SAAA6H,EAAW,CAAC,EAAG,EAAG,CAAC,EACnB,MAAAC,EAAQ,EACR,aAAA4D,EACA,eAAA3D,EACA,eAAAC,EACA,iBAAAC,EACA,oBAAAmE,EACA,OAAAlE,EACA,QAASC,CAAA,EAEXwD,IACG,CACH,KAAM,CAAE,MAAA3I,CAAA,EAAUoF,EAAAA,QAAQvH,CAAG,EACvBwH,EAAYhI,EAAAA,OAAO6H,CAAM,EAC/BG,EAAU,QAAUH,EACpB,MAAMmE,EAAyBhM,EAAAA,OAAO+L,CAAmB,EACzDC,EAAuB,QAAUD,EAGjC,MAAM9D,EAActH,EAAAA,QAAQ,IAAMgC,EAAM,QAAS,CAACA,CAAK,CAAC,EAGlD,CAAE,eAAA+H,EAAgB,oBAAAC,EAAqB,qBAAAC,CAAA,EAAyBT,EACpElC,EACAoD,CAAA,EAII,CAAE,kBAAA9H,EAAmB,aAAAE,CAAA,EAAiBf,EAC1CuF,EACAP,CAAA,EAII,CAAE,iBAAAnD,EAAkB,iBAAAC,EAAkB,iBAAAC,CAAA,EAAqBb,EAC/DqE,EACAN,CAAA,EAII,CAAE,oBAAAP,EAAqB,sBAAAE,CAAA,EAA0B1C,EACrDqD,EACAL,CAAA,EAIF3H,EAAAA,UAAU,IAAM,OACd,MAAM2I,EAAQ+B,EAAA,EACV/B,EAAM,OAAS,KACjBnC,EAAAuF,EAAuB,UAAvB,MAAAvF,EAAA,KAAAuF,EAAiCpD,GAErC,EAAG,CAACX,EAAa0C,CAAmB,CAAC,EAGrC1K,EAAAA,UAAU,IAAM,OACd,GAAI,CAACgI,EAAa,OAElB,MAAME,EAAmB,CAAA,EACnBC,MAAgB,IAChBC,EAAkB,CAAA,EACxB,IAAIC,EAAY,EAEhBL,EAAY,SAAUhF,GAAU,CAO9B,GANAqF,IAEIrF,EAAM,OAAS,QACjBoF,EAAM,KAAKpF,EAAM,IAAI,EAGlBA,EAAqB,OAAQ,CAChC,MAAMC,EAAOD,EACbkF,EAAO,KAAKjF,EAAK,IAAI,EACrBA,EAAK,WAAa,GAClBA,EAAK,cAAgB,IAER,MAAM,QAAQA,EAAK,QAAQ,EAAIA,EAAK,SAAW,CAACA,EAAK,QAAQ,GACrE,QAASoB,GAAQ8D,EAAU,IAAI9D,EAAI,IAAI,CAAC,CAC/C,CACF,CAAC,GAEDmC,EAAAuB,EAAU,UAAV,MAAAvB,EAAA,KAAAuB,EAAoB,CAClB,OAAQG,EAAO,KAAA,EACf,UAAW,MAAM,KAAKC,CAAS,EAAE,KAAA,EACjC,MAAOC,EAAM,KAAA,EACb,UAAAC,CAAA,EAEJ,EAAG,CAACL,CAAW,CAAC,EAGhB2D,EAAAA,oBAAoBN,EAAK,KAAO,CAC9B,eAAAZ,EACA,oBAAAC,EACA,qBAAAC,EACA,kBAAArH,EACA,aAAAE,EACA,iBAAAc,EACA,iBAAAC,EACA,iBAAAC,EACA,oBAAA2C,EACA,sBAAAE,CAAA,EACA,EAEF,MAAMiB,EAAa,OAAOd,GAAU,SAAW,CAACA,EAAOA,EAAOA,CAAK,EAAIA,EAEvE,OACEvH,EAAAA,IAAC,QAAA,CAAM,SAAAP,EAAoB,SAAA6H,EAAoB,MAAOe,EACpD,SAAArI,EAAAA,IAAC,YAAA,CAAU,OAAQ+H,CAAA,CAAa,CAAA,CAClC,CAEJ,CACF,EAEA6D,GAAe,YAAc,iBC9HtB,SAASG,GAAe,CAC7B,SAAAtK,EACA,KAAAuK,EACA,SAAAvM,EAAW,CAAC,EAAG,EAAG,CAAC,EACnB,SAAA6H,EAAW,CAAC,EAAG,EAAG,CAAC,EACnB,MAAAC,EAAQ,CACV,EAAwB,CACtB,KAAM,CAAE,QAAA0E,CAAA,EAAYpB,GAAA,EACd,CAACqB,EAAYC,CAAa,EAAIC,EAAAA,SAAgC,IAAI,EAWxE,GATArM,EAAAA,UAAU,IAAM,CACd,MAAMsM,EAAQJ,EAAQD,CAAI,EACtBK,EACFF,EAAcE,CAAK,EAEnB,QAAQ,KAAK,SAASL,CAAI,sBAAsB,CAEpD,EAAG,CAACA,EAAMC,CAAO,CAAC,EAEd,CAACC,EACH,OAAO,KAGT,MAAM7D,EAAa,OAAOd,GAAU,SAAW,CAACA,EAAOA,EAAOA,CAAK,EAAIA,EAEvE,OAAO+E,EAAAA,aACLtM,EAAAA,IAAC,QAAA,CACC,SAAAP,EACA,SAAA6H,EACA,MAAOe,EAEN,SAAA5G,CAAA,CAAA,EAEHyK,CAAA,CAEJ,CCtEO,SAASK,GAAejM,EAAakI,EAAiC,CAC3E,KAAM,CAAE,MAAA/F,EAAO,WAAA8F,GAAeV,EAAAA,QAAQvH,CAAG,EAGnCyH,EAActH,EAAAA,QAAQ,IACnB8K,EAAc,MAAM9I,CAAK,EAC/B,CAACA,CAAK,CAAC,EAGV1C,OAAAA,EAAAA,UAAU,IAAM,OACd,GAAI,CAACgI,EAAa,OAElB,MAAMI,EAAkB,CAAA,EAClBF,EAAmB,CAAA,EACnBC,MAAgB,IACtB,IAAIE,EAAY,EAEhBL,EAAY,SAAUhF,GAAU,CAO9B,GANAqF,IAEIrF,EAAM,OAAS,QACjBoF,EAAM,KAAKpF,EAAM,IAAI,EAGlBA,EAAqB,OAAQ,CAChC,MAAMC,EAAOD,EACbkF,EAAO,KAAKjF,EAAK,IAAI,EAGrBA,EAAK,WAAa,GAClBA,EAAK,cAAgB,IAGR,MAAM,QAAQA,EAAK,QAAQ,EAAIA,EAAK,SAAW,CAACA,EAAK,QAAQ,GACrE,QAASoB,GAAQ,CACpB8D,EAAU,IAAI9D,EAAI,IAAI,EACtBA,EAAI,WAAa1D,EAAM,UACzB,CAAC,CACH,CACF,CAAC,GAED6F,EAAAiC,GAAA,YAAAA,EAAS,SAAT,MAAAjC,EAAA,KAAAiC,EAAkB,CAChB,OAAQP,EAAO,KAAA,EACf,UAAW,MAAM,KAAKC,CAAS,EAAE,KAAA,EACjC,MAAOC,EAAM,KAAA,EACb,UAAAC,CAAA,EAEJ,EAAG,CAACL,EAAaS,CAAO,CAAC,EAElB,CAAE,MAAOT,EAAa,WAAAQ,CAAA,CAC/B,CAGO,SAASiE,GAAalM,EAAa,CACxCuH,EAAAA,QAAQ,QAAQvH,CAAG,CACrB"}
1
+ {"version":3,"file":"mbt-3d.cjs","sources":["../src/lib/components/Scene3D/Scene3D.tsx","../src/lib/hooks/useMeshVisibility.ts","../src/lib/hooks/useMaterialColor.ts","../src/lib/hooks/useMaterialTexture.ts","../src/lib/components/Model/Model.tsx","../src/lib/hooks/useAnimationController.ts","../src/lib/hooks/useMorphTargets.ts","../src/lib/components/AnimatedModel/AnimatedModelContext.tsx","../src/lib/components/AnimatedModel/AnimatedModel.tsx","../src/lib/components/MorphableModel/MorphableModel.tsx","../src/lib/components/BoneAttachment/BoneAttachment.tsx","../src/lib/hooks/useClonedModel.ts"],"sourcesContent":["import { Suspense, useMemo, useRef, useEffect } from 'react';\r\nimport { Canvas, useThree } from '@react-three/fiber';\r\nimport { OrbitControls, ContactShadows, useTexture } from '@react-three/drei';\r\nimport * as THREE from 'three';\r\nimport type { Scene3DProps, LightConfigUnion } from '../../types';\r\nimport type { OrbitControls as OrbitControlsType } from 'three-stdlib';\r\n\r\n/**\r\n * CameraController - Updates camera position when props change\r\n */\r\nfunction CameraController({ \r\n position,\r\n controlsConfig \r\n}: { \r\n position: [number, number, number];\r\n controlsConfig: any;\r\n}) {\r\n const { camera } = useThree();\r\n const controlsRef = useRef<OrbitControlsType | null>(null);\r\n\r\n useEffect(() => {\r\n if (position && camera) {\r\n camera.position.set(position[0], position[1], position[2]);\r\n camera.updateProjectionMatrix();\r\n \r\n // Reset OrbitControls target to origin\r\n if (controlsRef.current) {\r\n controlsRef.current.target.set(0, 0, 0);\r\n controlsRef.current.update();\r\n }\r\n }\r\n }, [position, camera]);\r\n\r\n return (\r\n <OrbitControls\r\n ref={controlsRef}\r\n makeDefault\r\n enabled={controlsConfig.enabled}\r\n enablePan={controlsConfig.enablePan}\r\n enableZoom={controlsConfig.enableZoom}\r\n enableRotate={controlsConfig.enableRotate}\r\n minDistance={controlsConfig.minDistance}\r\n maxDistance={controlsConfig.maxDistance}\r\n minPolarAngle={controlsConfig.minPolarAngle}\r\n maxPolarAngle={controlsConfig.maxPolarAngle}\r\n autoRotate={controlsConfig.autoRotate}\r\n autoRotateSpeed={controlsConfig.autoRotateSpeed}\r\n />\r\n );\r\n}\r\n\r\n/**\r\n * Background component that handles both image URLs and solid colors\r\n */\r\nfunction Background({ background }: { background?: string }) {\r\n // Check if it's a color (starts with #) or an image URL\r\n const isColor = background?.startsWith('#');\r\n \r\n if (!background) return null;\r\n \r\n if (isColor) {\r\n return <color attach=\"background\" args={[background]} />;\r\n }\r\n \r\n // It's an image URL\r\n return <BackgroundImage url={background} />;\r\n}\r\n\r\nfunction BackgroundImage({ url }: { url: string }) {\r\n const texture = useTexture(url);\r\n \r\n // Configure texture for background\r\n useMemo(() => {\r\n texture.colorSpace = THREE.SRGBColorSpace;\r\n }, [texture]);\r\n \r\n return <primitive attach=\"background\" object={texture} />;\r\n}\r\n\r\n/**\r\n * LightRenderer - Renders different types of lights based on config\r\n */\r\nfunction LightRenderer({ config }: { config: LightConfigUnion }) {\r\n switch (config.type) {\r\n case 'spot': {\r\n const {\r\n position = [5, 10, 5],\r\n intensity = 50,\r\n castShadow = true,\r\n angle = Math.PI / 6,\r\n penumbra = 0.5,\r\n decay = 2,\r\n distance = 0,\r\n color = '#ffffff',\r\n } = config;\r\n return (\r\n <spotLight\r\n position={position}\r\n intensity={intensity}\r\n castShadow={castShadow}\r\n angle={angle}\r\n penumbra={penumbra}\r\n decay={decay}\r\n distance={distance}\r\n color={color}\r\n />\r\n );\r\n }\r\n \r\n case 'point': {\r\n const {\r\n position = [0, 5, 0],\r\n intensity = 20,\r\n color = '#ffffff',\r\n distance = 0,\r\n decay = 2,\r\n castShadow = false,\r\n } = config;\r\n return (\r\n <pointLight\r\n position={position}\r\n intensity={intensity}\r\n color={color}\r\n distance={distance}\r\n decay={decay}\r\n castShadow={castShadow}\r\n />\r\n );\r\n }\r\n \r\n case 'directional': {\r\n const {\r\n position = [10, 20, 10],\r\n intensity = 40,\r\n color = '#ffffff',\r\n castShadow = true,\r\n } = config;\r\n return (\r\n <directionalLight\r\n position={position}\r\n intensity={intensity}\r\n color={color}\r\n castShadow={castShadow}\r\n />\r\n );\r\n }\r\n \r\n case 'hemisphere': {\r\n const {\r\n skyColor = '#ffffff',\r\n groundColor = '#444444',\r\n intensity = 0.5,\r\n position = [0, 10, 0],\r\n } = config;\r\n \r\n const skyColorObj = useMemo(() => new THREE.Color(skyColor), [skyColor]);\r\n const groundColorObj = useMemo(() => new THREE.Color(groundColor), [groundColor]);\r\n \r\n return (\r\n <hemisphereLight\r\n args={[skyColorObj, groundColorObj, intensity]}\r\n position={position}\r\n />\r\n );\r\n }\r\n \r\n case 'ambient': {\r\n const {\r\n intensity = 0.5,\r\n color = '#ffffff',\r\n } = config;\r\n return (\r\n <ambientLight\r\n intensity={intensity}\r\n color={color}\r\n />\r\n );\r\n }\r\n \r\n default:\r\n return null;\r\n }\r\n}\r\n\r\n/**\r\n * Scene3D - Main 3D scene container with built-in canvas, lighting, and controls\r\n *\r\n * @remarks\r\n * This component wraps React Three Fiber's Canvas and provides:\r\n * - OrbitControls for camera rotation (preserves state between model changes)\r\n * - Configurable multi-light system (spot, point, directional, hemisphere, ambient)\r\n * - Optional background (image URL or hex color like \"#1a1a2e\")\r\n * - Optional contact shadows under models\r\n * - Shadow support\r\n *\r\n * @param children - React children to render inside the 3D scene\r\n * @param camera - Camera configuration object. Example: `{ position: [0, 2, 5], fov: 45 }`\r\n * @param camera.position - Camera position as [x, y, z]. Example: `[0, 2, 5]`. Default: `[0, 2, 5]`\r\n * @param camera.fov - Field of view in degrees. Example: `45`. Default: `45`\r\n * @param controls - OrbitControls configuration object. Example: `{ enablePan: false, minDistance: 2, maxDistance: 10 }`\r\n * @param controls.enabled - Enable/disable all controls. Default: `true`\r\n * @param controls.enablePan - Enable camera panning with middle mouse. Default: `true`\r\n * @param controls.enableZoom - Enable camera zoom with scroll wheel. Default: `true`\r\n * @param controls.enableRotate - Enable camera rotation with left mouse. Default: `true`\r\n * @param controls.minDistance - Minimum zoom distance. Example: `2`\r\n * @param controls.maxDistance - Maximum zoom distance. Example: `10`\r\n * @param controls.minPolarAngle - Minimum vertical rotation angle in radians. Example: `Math.PI / 4`\r\n * @param controls.maxPolarAngle - Maximum vertical rotation angle in radians. Example: `Math.PI / 1.8`\r\n * @param controls.autoRotate - Auto-rotate camera around the center. Default: `false`\r\n * @param controls.autoRotateSpeed - Auto-rotation speed. Default: `2`\r\n * @param background - Background: image URL (e.g., `\"/bg.jpg\"`) or hex color (e.g., `\"#1a1a2e\"`)\r\n * @param shadows - Enable shadow rendering. Default: `true`\r\n * @param lights - Array of light configurations. Supports spot, point, directional, hemisphere, and ambient lights. Default: `[{ type: 'ambient', intensity: 0.5 }, { type: 'spot', position: [5, 10, 5], intensity: 50, castShadow: true }]`\r\n * @param contactShadows - Contact shadows configuration. Can be `true` for default settings or object with custom settings. Example: `{ position: [0, -1, 0], opacity: 0.5, blur: 2 }`\r\n * @param contactShadows.position - Shadow position as [x, y, z]. Example: `[0, -1, 0]`. Default: `[0, -1, 0]`\r\n * @param contactShadows.opacity - Shadow opacity (0-1). Example: `0.5`. Default: `0.5`\r\n * @param contactShadows.blur - Shadow blur amount. Example: `2`. Default: `2`\r\n * @param style - Container style object. Example: `{ width: 400, height: 500 }`\r\n * @param className - Container CSS class name\r\n *\r\n * @example\r\n * Basic setup with default lighting:\r\n * ```tsx\r\n * <Scene3D\r\n * camera={{ position: [0, 2, 5], fov: 45 }}\r\n * background=\"#1a1a2e\"\r\n * shadows\r\n * style={{ width: 400, height: 500 }}\r\n * >\r\n * <AnimatedModel url=\"/model.glb\" position={[0, -1, 0]} />\r\n * </Scene3D>\r\n * ```\r\n *\r\n * @example\r\n * Multi-light setup for better illumination:\r\n * ```tsx\r\n * <Scene3D\r\n * camera={{ position: [0, 2, 5], fov: 45 }}\r\n * lights={[\r\n * { type: 'spot', position: [5, 10, 5], intensity: 50, castShadow: true },\r\n * { type: 'spot', position: [-5, 10, -5], intensity: 30, castShadow: false },\r\n * { type: 'point', position: [0, 5, 0], intensity: 20 },\r\n * { type: 'ambient', intensity: 0.3 }\r\n * ]}\r\n * >\r\n * <AnimatedModel url=\"/model.glb\" position={[0, -1, 0]} />\r\n * </Scene3D>\r\n * ```\r\n *\r\n * @example\r\n * Using different light types:\r\n * ```tsx\r\n * <Scene3D\r\n * lights={[\r\n * { type: 'directional', position: [10, 20, 10], intensity: 40, castShadow: true },\r\n * { type: 'hemisphere', skyColor: '#87CEEB', groundColor: '#362312', intensity: 0.5 },\r\n * { type: 'ambient', intensity: 0.2 }\r\n * ]}\r\n * >\r\n * <Model url=\"/model.glb\" />\r\n * </Scene3D>\r\n * ```\r\n */\r\nexport function Scene3D({\r\n children,\r\n camera = {},\r\n controls = {},\r\n background,\r\n shadows = true,\r\n lights = [\r\n { type: 'ambient', intensity: 0.5 },\r\n { type: 'spot', position: [5, 10, 5], intensity: 50, castShadow: true }\r\n ] as LightConfigUnion[],\r\n contactShadows = true,\r\n style,\r\n className,\r\n}: Scene3DProps) {\r\n const cameraConfig = {\r\n position: camera.position || [0, 2, 5],\r\n fov: camera.fov || 45,\r\n };\r\n\r\n const controlsConfig = {\r\n enabled: controls.enabled ?? true,\r\n enablePan: controls.enablePan ?? true,\r\n enableZoom: controls.enableZoom ?? true,\r\n enableRotate: controls.enableRotate ?? true,\r\n minDistance: controls.minDistance,\r\n maxDistance: controls.maxDistance,\r\n minPolarAngle: controls.minPolarAngle,\r\n maxPolarAngle: controls.maxPolarAngle,\r\n autoRotate: controls.autoRotate ?? false,\r\n autoRotateSpeed: controls.autoRotateSpeed ?? 2,\r\n };\r\n\r\n const contactShadowsConfig =\r\n typeof contactShadows === 'object'\r\n ? {\r\n position: contactShadows.position || [0, -1, 0],\r\n opacity: contactShadows.opacity ?? 0.5,\r\n blur: contactShadows.blur ?? 2,\r\n }\r\n : contactShadows\r\n ? { position: [0, -1, 0] as [number, number, number], opacity: 0.5, blur: 2 }\r\n : null;\r\n\r\n return (\r\n <div style={style} className={className}>\r\n <Canvas\r\n shadows={shadows}\r\n camera={{\r\n position: cameraConfig.position as [number, number, number],\r\n fov: cameraConfig.fov,\r\n }}\r\n style={{ width: '100%', height: '100%' }}\r\n >\r\n {/* Background */}\r\n <Suspense fallback={null}>\r\n <Background background={background} />\r\n </Suspense>\r\n\r\n {/* Lighting */}\r\n {lights.map((lightConfig, index) => (\r\n <LightRenderer key={`${lightConfig.type}-${index}`} config={lightConfig} />\r\n ))}\r\n\r\n {/* Content */}\r\n <Suspense fallback={null}>{children}</Suspense>\r\n\r\n {/* Controls - updates camera when props change */}\r\n <CameraController \r\n position={cameraConfig.position as [number, number, number]} \r\n controlsConfig={controlsConfig}\r\n />\r\n\r\n {/* Contact Shadows */}\r\n {contactShadowsConfig && (\r\n <ContactShadows\r\n position={contactShadowsConfig.position}\r\n opacity={contactShadowsConfig.opacity}\r\n blur={contactShadowsConfig.blur}\r\n />\r\n )}\r\n </Canvas>\r\n </div>\r\n );\r\n}\r\n","import { useEffect, useCallback, useRef } from 'react';\nimport { useFrame } from '@react-three/fiber';\nimport * as THREE from 'three';\n\n/**\n * Hook for managing mesh visibility in a 3D scene\n * \n * @param scene - The THREE.Object3D scene containing meshes\n * @param visibility - Reactive visibility state for meshes. Updates are applied every frame.\n * @returns Methods to control mesh visibility\n */\nexport function useMeshVisibility(\n scene: THREE.Object3D,\n visibility?: Record<string, boolean>\n) {\n const meshMapRef = useRef<Map<string, THREE.Mesh>>(new Map());\n // Store the latest visibility from props (kept in sync via useEffect)\n const visibilityRef = useRef<Record<string, boolean>>({});\n // Store imperative overrides set via setMeshVisibility()\n const overridesRef = useRef<Record<string, boolean>>({});\n\n // Keep visibilityRef in sync with the prop\n useEffect(() => {\n if (visibility) {\n visibilityRef.current = { ...visibility };\n }\n }, [visibility]);\n\n // Discover all meshes in the scene\n useEffect(() => {\n if (!scene) return;\n\n const meshMap = new Map<string, THREE.Mesh>();\n \n scene.traverse((child) => {\n if ((child as THREE.Mesh).isMesh) {\n const mesh = child as THREE.Mesh;\n if (mesh.name) {\n meshMap.set(mesh.name, mesh);\n }\n }\n });\n\n meshMapRef.current = meshMap;\n }, [scene]);\n\n // Apply visibility changes on every frame\n useFrame(() => {\n const meshMap = meshMapRef.current;\n if (meshMap.size === 0) return;\n\n // Merge: props take baseline, imperative overrides take priority\n const merged = { ...visibilityRef.current, ...overridesRef.current };\n\n for (const [name, visible] of Object.entries(merged)) {\n const mesh = meshMap.get(name);\n if (mesh && mesh.visible !== visible) {\n mesh.visible = visible;\n }\n }\n });\n\n /**\n * Set visibility of a specific mesh (imperative, overrides props)\n */\n const setMeshVisibility = useCallback((name: string, visible: boolean) => {\n overridesRef.current[name] = visible;\n \n // Apply immediately\n const mesh = meshMapRef.current.get(name);\n if (mesh) {\n mesh.visible = visible;\n }\n }, []);\n\n /**\n * Get all mesh names in the scene\n */\n const getMeshNames = useCallback(() => {\n return Array.from(meshMapRef.current.keys()).sort();\n }, []);\n\n /**\n * Get current visibility state of all meshes\n */\n const getMeshVisibility = useCallback(() => {\n const result: Record<string, boolean> = {};\n meshMapRef.current.forEach((mesh, name) => {\n result[name] = mesh.visible;\n });\n return result;\n }, []);\n\n return {\n setMeshVisibility,\n getMeshNames,\n getMeshVisibility,\n };\n}\n","import { useEffect, useCallback, useRef } from 'react';\r\nimport { useFrame } from '@react-three/fiber';\r\nimport * as THREE from 'three';\r\n\r\n/**\r\n * Hook for managing material colors in a 3D scene\r\n * \r\n * @param scene - The THREE.Object3D scene containing meshes with materials\r\n * @param initialColors - Initial color values for materials { materialName: \"#hexcolor\" or [r, g, b] }\r\n * @returns Methods to control material colors\r\n */\r\nexport function useMaterialColor(\r\n scene: THREE.Object3D,\r\n initialColors?: Record<string, string | [number, number, number]>\r\n) {\r\n const materialMapRef = useRef<Map<string, THREE.Material | THREE.Material[]>>(new Map());\r\n const colorMapRef = useRef<Map<string, THREE.Color>>(new Map());\r\n\r\n // Discover all materials in the scene\r\n useEffect(() => {\r\n if (!scene) return;\r\n\r\n const materialMap = new Map<string, THREE.Material | THREE.Material[]>();\r\n \r\n scene.traverse((child) => {\r\n if ((child as THREE.Mesh).isMesh) {\r\n const mesh = child as THREE.Mesh;\r\n const materials = Array.isArray(mesh.material) ? mesh.material : [mesh.material];\r\n \r\n materials.forEach((material) => {\r\n if (material.name && !materialMap.has(material.name)) {\r\n materialMap.set(material.name, material);\r\n }\r\n });\r\n }\r\n });\r\n\r\n materialMapRef.current = materialMap;\r\n\r\n // Apply initial colors\r\n if (initialColors) {\r\n Object.entries(initialColors).forEach(([materialName, color]) => {\r\n const material = materialMap.get(materialName);\r\n if (material && !Array.isArray(material)) {\r\n const threeColor = parseColor(color);\r\n if ((material as any).color) {\r\n (material as any).color.copy(threeColor);\r\n }\r\n colorMapRef.current.set(materialName, threeColor);\r\n }\r\n });\r\n }\r\n }, [scene, initialColors]);\r\n\r\n // Apply color changes on every frame (for smooth transitions)\r\n useFrame(() => {\r\n colorMapRef.current.forEach((targetColor, materialName) => {\r\n const material = materialMapRef.current.get(materialName);\r\n if (material && !Array.isArray(material)) {\r\n const mat = material as any;\r\n if (mat.color && !mat.color.equals(targetColor)) {\r\n mat.color.copy(targetColor);\r\n mat.needsUpdate = true;\r\n }\r\n }\r\n });\r\n });\r\n\r\n /**\r\n * Parse color from hex string or RGB array\r\n */\r\n const parseColor = (color: string | [number, number, number]): THREE.Color => {\r\n if (typeof color === 'string') {\r\n return new THREE.Color(color);\r\n } else {\r\n return new THREE.Color(color[0], color[1], color[2]);\r\n }\r\n };\r\n\r\n /**\r\n * Set color of a specific material\r\n * @param materialName - Material name\r\n * @param color - Hex color string (e.g., \"#ff0000\") or RGB array [r, g, b] where values are 0-1\r\n */\r\n const setMaterialColor = useCallback((\r\n materialName: string, \r\n color: string | [number, number, number]\r\n ) => {\r\n const threeColor = parseColor(color);\r\n colorMapRef.current.set(materialName, threeColor);\r\n \r\n const material = materialMapRef.current.get(materialName);\r\n if (material && !Array.isArray(material)) {\r\n const mat = material as any;\r\n if (mat.color) {\r\n mat.color.copy(threeColor);\r\n mat.needsUpdate = true;\r\n }\r\n }\r\n }, []);\r\n\r\n /**\r\n * Get all material names in the scene\r\n */\r\n const getMaterialNames = useCallback(() => {\r\n return Array.from(materialMapRef.current.keys()).sort();\r\n }, []);\r\n\r\n /**\r\n * Get current color of a material\r\n * @returns Hex color string (e.g., \"#ff0000\")\r\n */\r\n const getMaterialColor = useCallback((materialName: string): string | null => {\r\n const material = materialMapRef.current.get(materialName);\r\n if (material && !Array.isArray(material)) {\r\n const mat = material as any;\r\n if (mat.color) {\r\n return '#' + mat.color.getHexString();\r\n }\r\n }\r\n return null;\r\n }, []);\r\n\r\n /**\r\n * Get all material colors\r\n * @returns Object mapping material names to hex colors\r\n */\r\n const getAllMaterialColors = useCallback((): Record<string, string> => {\r\n const colors: Record<string, string> = {};\r\n materialMapRef.current.forEach((material, name) => {\r\n if (!Array.isArray(material)) {\r\n const mat = material as any;\r\n if (mat.color) {\r\n colors[name] = '#' + mat.color.getHexString();\r\n }\r\n }\r\n });\r\n return colors;\r\n }, []);\r\n\r\n return {\r\n setMaterialColor,\r\n getMaterialNames,\r\n getMaterialColor,\r\n getAllMaterialColors,\r\n };\r\n}\r\n","import { useEffect, useCallback, useRef } from 'react';\nimport { useThree } from '@react-three/fiber';\nimport * as THREE from 'three';\nimport { KTX2Loader } from 'three/examples/jsm/loaders/KTX2Loader.js';\n\n/**\n * Material texture maps configuration\n */\nexport interface MaterialTextures {\n /** Base/Diffuse color map */\n map?: string;\n /** Normal map */\n normalMap?: string;\n /** Roughness map */\n roughnessMap?: string;\n /** Metallic map */\n metalnessMap?: string;\n /** Emissive map */\n emissiveMap?: string;\n /** Alpha/Opacity map */\n alphaMap?: string;\n /** Ambient Occlusion map */\n aoMap?: string;\n}\n\n/**\n * Hook for managing material textures in a 3D scene\n * \n * @param scene - The THREE.Object3D scene containing meshes with materials\n * @param textures - Initial texture maps for materials { materialName: textureConfig }\n * @returns Methods to control material textures\n */\nexport function useMaterialTexture(\n scene: THREE.Object3D,\n textures?: Record<string, MaterialTextures | string>\n) {\n const { gl } = useThree();\n const materialMapRef = useRef<Map<string, THREE.Material>>(new Map());\n const textureLoaderRef = useRef<THREE.TextureLoader>(new THREE.TextureLoader());\n const ktx2LoaderRef = useRef<KTX2Loader | null>(null);\n const loadedTexturesRef = useRef<Map<string, THREE.Texture>>(new Map());\n const loadingTexturesRef = useRef<Set<string>>(new Set()); // Track textures being loaded\n const appliedTexturesRef = useRef<string>(''); // Track what was last applied\n\n // Initialize KTX2 loader\n useEffect(() => {\n if (!ktx2LoaderRef.current) {\n try {\n // Check WASM support\n const supportsWASM = typeof WebAssembly === 'object' &&\n typeof WebAssembly.validate === 'function';\n\n console.log('[useMaterialTexture] Browser features:');\n console.log(` WebAssembly supported: ${supportsWASM}`);\n console.log(` WebGL2: ${gl.capabilities.isWebGL2}`);\n\n const ktx2Loader = new KTX2Loader();\n ktx2Loader.setTranscoderPath('/basis/');\n ktx2Loader.detectSupport(gl);\n ktx2LoaderRef.current = ktx2Loader;\n console.log('[useMaterialTexture] ✅ KTX2Loader initialized');\n console.log('[useMaterialTexture] Transcoder path: /basis/');\n\n // Test if transcoder files are accessible\n fetch('/basis/basis_transcoder.wasm')\n .then(response => {\n if (response.ok) {\n console.log('[useMaterialTexture] ✅ basis_transcoder.wasm is accessible');\n } else {\n console.error('[useMaterialTexture] ❌ basis_transcoder.wasm returned', response.status);\n }\n })\n .catch(error => {\n console.error('[useMaterialTexture] ❌ Failed to check basis_transcoder.wasm:', error);\n });\n\n if (!supportsWASM) {\n console.warn('[useMaterialTexture] ⚠️ WebAssembly not supported! KTX2 will fallback to CPU decoding (slow)');\n console.warn('[useMaterialTexture] ⚠️ Recommend using WebP instead of KTX2 for this browser');\n }\n } catch (error) {\n console.error('[useMaterialTexture] ❌ Failed to initialize KTX2Loader:', error);\n }\n }\n\n return () => {\n if (ktx2LoaderRef.current) {\n ktx2LoaderRef.current.dispose();\n ktx2LoaderRef.current = null;\n }\n };\n }, [gl]);\n\n // Discover all materials in the scene\n useEffect(() => {\n if (!scene) return;\n\n const materialMap = new Map<string, THREE.Material>();\n\n scene.traverse((child) => {\n if ((child as THREE.Mesh).isMesh) {\n const mesh = child as THREE.Mesh;\n const materials = Array.isArray(mesh.material) ? mesh.material : [mesh.material];\n\n materials.forEach((material) => {\n if (material.name && !materialMap.has(material.name)) {\n materialMap.set(material.name, material);\n }\n });\n }\n });\n\n materialMapRef.current = materialMap;\n }, [scene]);\n\n // Helper function to get the appropriate loader based on file extension\n const getLoader = useCallback((url: string): THREE.TextureLoader | KTX2Loader => {\n if (url.toLowerCase().endsWith('.ktx2')) {\n if (!ktx2LoaderRef.current) {\n console.warn('[useMaterialTexture] KTX2Loader not initialized, falling back to TextureLoader');\n return textureLoaderRef.current;\n }\n return ktx2LoaderRef.current;\n }\n return textureLoaderRef.current;\n }, []);\n\n // Load and apply textures when they change\n useEffect(() => {\n if (!textures || !scene) return;\n\n const materialMap = materialMapRef.current;\n\n if (materialMap.size === 0) return;\n\n // Create a stable key for comparison\n const texturesKey = JSON.stringify(textures);\n if (appliedTexturesRef.current === texturesKey) {\n console.log('[useMaterialTexture] Textures unchanged, skipping reload');\n return;\n }\n appliedTexturesRef.current = texturesKey;\n\n let disposed = false;\n\n // Normalize textures\n const normalizedTextures = new Map<string, MaterialTextures>();\n Object.entries(textures).forEach(([name, config]) => {\n normalizedTextures.set(name, typeof config === 'string' ? { map: config } : config);\n });\n\n console.log('[useMaterialTexture] Starting texture load for', normalizedTextures.size, 'materials');\n\n // Collect all texture load tasks\n const loadTasks: Array<{\n materialName: string;\n textureType: string;\n url: string;\n material: THREE.MeshStandardMaterial;\n }> = [];\n\n normalizedTextures.forEach((textureConfig, materialName) => {\n const material = materialMap.get(materialName);\n if (!material || !(material as any).isMeshStandardMaterial) {\n console.warn(`[useMaterialTexture] Material \"${materialName}\" not found or not MeshStandardMaterial`);\n return;\n }\n\n const mat = material as THREE.MeshStandardMaterial;\n\n // Log original material state before clearing\n console.log(`[useMaterialTexture] 📋 Material \"${materialName}\" BEFORE reset:`);\n console.log(` - map: ${mat.map ? 'EXISTS' : 'null'} ${mat.map ? `(uuid: ${mat.map.uuid})` : ''}`);\n console.log(` - normalMap: ${mat.normalMap ? 'EXISTS' : 'null'}`);\n console.log(` - roughnessMap: ${mat.roughnessMap ? 'EXISTS' : 'null'}`);\n console.log(` - metalnessMap: ${mat.metalnessMap ? 'EXISTS' : 'null'}`);\n console.log(` - emissiveMap: ${mat.emissiveMap ? 'EXISTS' : 'null'}`);\n console.log(` - aoMap: ${mat.aoMap ? 'EXISTS' : 'null'}`);\n console.log(` - color: ${mat.color?.getHexString()}`);\n console.log(` - emissive: ${mat.emissive?.getHexString()}`);\n console.log(` - material uuid: ${mat.uuid}`);\n\n // DISPOSE old textures to free GPU memory\n if (mat.map) {\n console.log(`[useMaterialTexture] 🗑️ Disposing old map (uuid: ${mat.map.uuid})`);\n mat.map.dispose();\n }\n if (mat.normalMap) {\n console.log(`[useMaterialTexture] 🗑️ Disposing old normalMap`);\n mat.normalMap.dispose();\n }\n if (mat.roughnessMap) {\n console.log(`[useMaterialTexture] 🗑️ Disposing old roughnessMap`);\n mat.roughnessMap.dispose();\n }\n if (mat.metalnessMap) {\n console.log(`[useMaterialTexture] 🗑️ Disposing old metalnessMap`);\n mat.metalnessMap.dispose();\n }\n if (mat.emissiveMap) {\n console.log(`[useMaterialTexture] 🗑️ Disposing old emissiveMap`);\n mat.emissiveMap.dispose();\n }\n if (mat.aoMap) {\n console.log(`[useMaterialTexture] 🗑️ Disposing old aoMap`);\n mat.aoMap.dispose();\n }\n\n mat.map = null;\n mat.normalMap = null;\n mat.roughnessMap = null;\n mat.metalnessMap = null;\n mat.emissiveMap = null;\n mat.emissive = new THREE.Color(0x000000); // Reset to black\n mat.emissiveIntensity = 0;\n mat.aoMap = null;\n mat.needsUpdate = true;\n mat.color = new THREE.Color(0xffffff);\n\n console.log(`[useMaterialTexture] ✅ Material \"${materialName}\" AFTER reset: all maps cleared and disposed`);\n\n Object.entries(textureConfig).forEach(([textureType, url]) => {\n if (!url) return;\n\n const cacheKey = `${materialName}_${textureType}_${url}`;\n\n // Check if already loaded\n const cached = loadedTexturesRef.current.get(cacheKey);\n if (cached) {\n console.log(`[useMaterialTexture] Using cached texture: ${materialName}.${textureType}`);\n if (!disposed) {\n applyTexture(mat, textureType as keyof MaterialTextures, cached);\n }\n return;\n }\n\n // Skip if already loading\n if (loadingTexturesRef.current.has(cacheKey)) {\n console.log(`[useMaterialTexture] Skipping already loading: ${materialName}.${textureType}`);\n return;\n }\n\n loadTasks.push({ materialName, textureType, url, material: mat });\n });\n });\n\n console.log(`[useMaterialTexture] Queued ${loadTasks.length} textures to load`);\n\n // Load textures sequentially with delay to avoid overwhelming GPU\n let taskIndex = 0;\n const loadNextTexture = () => {\n if (disposed || taskIndex >= loadTasks.length) {\n console.log('[useMaterialTexture] ✅ All textures loaded');\n\n // Log final state of all materials after all textures are loaded\n console.log('[useMaterialTexture] 📊 FINAL material states:');\n materialMap.forEach((mat, name) => {\n const stdMat = mat as THREE.MeshStandardMaterial;\n console.log(` Material \"${name}\":`);\n console.log(` - map: ${stdMat.map ? 'EXISTS' : 'null'} ${stdMat.map ? `(colorSpace: ${stdMat.map.colorSpace})` : ''}`);\n console.log(` - normalMap: ${stdMat.normalMap ? 'EXISTS' : 'null'}`);\n console.log(` - roughnessMap: ${stdMat.roughnessMap ? 'EXISTS' : 'null'}`);\n console.log(` - metalnessMap: ${stdMat.metalnessMap ? 'EXISTS' : 'null'}`);\n console.log(` - emissiveMap: ${stdMat.emissiveMap ? 'EXISTS' : 'null'}`);\n console.log(` - color: ${stdMat.color?.getHexString()}`);\n console.log(` - emissive: ${stdMat.emissive?.getHexString()}`);\n console.log(` - emissiveIntensity: ${stdMat.emissiveIntensity}`);\n console.log(` - roughness: ${stdMat.roughness}`);\n console.log(` - metalness: ${stdMat.metalness}`);\n });\n\n return;\n }\n\n const task = loadTasks[taskIndex++];\n const cacheKey = `${task.materialName}_${task.textureType}_${task.url}`;\n\n console.log(`[useMaterialTexture] Loading (${taskIndex}/${loadTasks.length}): ${task.materialName}.${task.textureType} from ${task.url}`);\n\n loadingTexturesRef.current.add(cacheKey);\n\n // Get the appropriate loader based on file extension\n const loader = getLoader(task.url);\n const loaderType = task.url.toLowerCase().endsWith('.ktx2') ? 'KTX2Loader' : 'TextureLoader';\n console.log(`[useMaterialTexture] Using ${loaderType} for ${task.url}`);\n\n loader.load(\n task.url,\n (texture) => {\n loadingTexturesRef.current.delete(cacheKey);\n\n if (disposed) {\n console.log(`[useMaterialTexture] Disposed, cleaning up texture: ${task.materialName}.${task.textureType}`);\n texture.dispose();\n return;\n }\n\n const width = texture.image?.width || texture.source?.data?.width || 'unknown';\n const height = texture.image?.height || texture.source?.data?.height || 'unknown';\n console.log(`[useMaterialTexture] ✅ Loaded: ${task.materialName}.${task.textureType} (${width}x${height})`);\n\n // Configure texture\n texture.colorSpace = task.textureType === 'map' || task.textureType === 'emissiveMap'\n ? THREE.SRGBColorSpace\n : THREE.NoColorSpace;\n texture.wrapS = THREE.RepeatWrapping;\n texture.wrapT = THREE.RepeatWrapping;\n texture.flipY = false;\n\n loadedTexturesRef.current.set(cacheKey, texture);\n applyTexture(task.material, task.textureType as keyof MaterialTextures, texture);\n\n // Load next texture after a delay (longer for KTX2 to allow GPU decompression)\n const delay = task.url.endsWith('.ktx2') ? 500 : 150;\n console.log(`[useMaterialTexture] Waiting111111 ${delay}ms before next texture...`);\n setTimeout(loadNextTexture, 0);\n },\n (progress) => {\n // Progress callback\n if (progress.lengthComputable) {\n const percent = Math.round((progress.loaded / progress.total) * 100);\n if (percent % 25 === 0) {\n console.log(`[useMaterialTexture] Progress ${task.materialName}.${task.textureType}: ${percent}%`);\n }\n }\n },\n (error) => {\n loadingTexturesRef.current.delete(cacheKey);\n\n if (!disposed) {\n console.error(`[useMaterialTexture] ❌ Failed to load ${task.materialName}.${task.textureType} from ${task.url}`);\n console.error('[useMaterialTexture] Error details:', error);\n console.error('[useMaterialTexture] Loader type:', loaderType);\n\n // Check if this is a KTX2 format issue\n if (task.url.endsWith('.ktx2')) {\n console.error('[useMaterialTexture] KTX2 error - file may not be in Basis Universal format!');\n console.error('[useMaterialTexture] Make sure files are created with: gltf-transform etc1s input.png output.ktx2');\n }\n }\n\n // Continue to next texture even if one fails\n setTimeout(loadNextTexture, 100);\n }\n );\n };\n\n // Start loading the first texture\n loadNextTexture();\n\n return () => {\n console.log('[useMaterialTexture] Cleanup: disposed');\n disposed = true;\n };\n }, [scene, textures, getLoader]);\n\n const applyTexture = (\n material: THREE.MeshStandardMaterial,\n type: keyof MaterialTextures,\n texture: THREE.Texture\n ) => {\n console.log(`[useMaterialTexture] 🎨 Applying texture: ${material.name}.${type} (texture uuid: ${texture.uuid})`);\n\n switch (type) {\n case 'map':\n material.map = texture;\n break;\n case 'normalMap':\n material.normalMap = texture;\n break;\n case 'roughnessMap':\n material.roughnessMap = texture;\n break;\n case 'metalnessMap':\n material.metalnessMap = texture;\n break;\n case 'emissiveMap':\n material.emissiveMap = texture;\n material.emissive = new THREE.Color(0xffffff);\n material.emissiveIntensity = 1;\n break;\n case 'alphaMap':\n // Skip alphaMap - it causes parts to disappear incorrectly\n // Only use if material already has transparency configured\n console.log(`[useMaterialTexture] Skipping alphaMap for \"${material.name}\" to prevent disappearing`);\n break;\n case 'aoMap':\n material.aoMap = texture;\n break;\n }\n material.needsUpdate = true;\n\n console.log(`[useMaterialTexture] ✅ Applied ${type} to ${material.name}, needsUpdate=true`);\n\n // Log final material state after applying texture\n console.log(`[useMaterialTexture] 📊 Material \"${material.name}\" FINAL state:`);\n console.log(` - map: ${material.map ? 'EXISTS' : 'null'} ${material.map ? `(uuid: ${material.map.uuid}, colorSpace: ${material.map.colorSpace})` : ''}`);\n console.log(` - normalMap: ${material.normalMap ? 'EXISTS' : 'null'}`);\n console.log(` - roughnessMap: ${material.roughnessMap ? 'EXISTS' : 'null'}`);\n console.log(` - metalnessMap: ${material.metalnessMap ? 'EXISTS' : 'null'}`);\n console.log(` - emissiveMap: ${material.emissiveMap ? 'EXISTS' : 'null'}`);\n console.log(` - aoMap: ${material.aoMap ? 'EXISTS' : 'null'}`);\n console.log(` - color: ${material.color?.getHexString()}`);\n console.log(` - emissive: ${material.emissive?.getHexString()}`);\n console.log(` - emissiveIntensity: ${material.emissiveIntensity}`);\n console.log(` - roughness: ${material.roughness}`);\n console.log(` - metalness: ${material.metalness}`);\n };\n\n /**\n * Set textures for a specific material\n * @param materialName - Material name\n * @param textureConfig - Texture configuration or single map URL\n */\n const setMaterialTextures = useCallback((\n materialName: string,\n textureConfig: MaterialTextures | string\n ) => {\n const normalized = typeof textureConfig === 'string'\n ? { map: textureConfig }\n : textureConfig;\n\n const material = materialMapRef.current.get(materialName);\n if (!material || !(material as any).isMeshStandardMaterial) {\n console.warn(`Material \"${materialName}\" not found or not MeshStandardMaterial`);\n return;\n }\n\n const mat = material as THREE.MeshStandardMaterial;\n\n Object.entries(normalized).forEach(([textureType, url]) => {\n if (!url) return;\n\n const cacheKey = `${materialName}_${textureType}_${url}`;\n\n // Check cache first\n const cached = loadedTexturesRef.current.get(cacheKey);\n if (cached) {\n applyTexture(mat, textureType as keyof MaterialTextures, cached);\n return;\n }\n\n // Get the appropriate loader based on file extension\n const loader = getLoader(url);\n\n loader.load(\n url,\n (texture) => {\n texture.colorSpace = textureType === 'map' || textureType === 'emissiveMap'\n ? THREE.SRGBColorSpace\n : THREE.NoColorSpace;\n texture.wrapS = THREE.RepeatWrapping;\n texture.wrapT = THREE.RepeatWrapping;\n\n loadedTexturesRef.current.set(cacheKey, texture);\n applyTexture(mat, textureType as keyof MaterialTextures, texture);\n },\n undefined,\n (error) => {\n console.error(`Failed to load texture ${url}:`, error);\n }\n );\n });\n }, [getLoader]);\n\n /**\n * Get all material names in the scene\n */\n const getMaterialNames = useCallback(() => {\n return Array.from(materialMapRef.current.keys()).sort();\n }, []);\n\n /**\n * Clear all textures from a material\n */\n const clearMaterialTextures = useCallback((materialName: string) => {\n const material = materialMapRef.current.get(materialName);\n if (!material || !(material as any).isMeshStandardMaterial) return;\n\n const mat = material as THREE.MeshStandardMaterial;\n mat.map = null;\n mat.normalMap = null;\n mat.roughnessMap = null;\n mat.metalnessMap = null;\n mat.emissiveMap = null;\n mat.alphaMap = null;\n mat.aoMap = null;\n mat.needsUpdate = true;\n }, []);\n\n return {\n setMaterialTextures,\n getMaterialNames,\n clearMaterialTextures,\n };\n}\n","import { useMemo, useRef } from 'react';\nimport { useGLTF } from '@react-three/drei';\nimport * as THREE from 'three';\nimport { useMeshVisibility } from '../../hooks/useMeshVisibility';\nimport { useMaterialColor } from '../../hooks/useMaterialColor';\nimport { useMaterialTexture } from '../../hooks/useMaterialTexture';\nimport type { ModelProps } from '../../types';\n\n/**\n * Model - Simple 3D model component for loading and displaying GLB/GLTF files\n * \n * @remarks\n * This component:\n * - Loads GLB/GLTF models using drei's useGLTF hook\n * - Automatically clones the scene for independent instances\n * - Configures shadows on all meshes\n * - Supports position, rotation, and scale transforms\n * - Provides onLoad callback with model metadata\n * \n * @param url - URL or path to GLB/GLTF model file. Example: `\"/models/sword.glb\"`\n * @param position - Model position in 3D space as [x, y, z]. Example: `[0, 0, 0]`. Default: `[0, 0, 0]`\n * @param rotation - Model rotation in radians as [x, y, z]. Example: `[0, Math.PI/2, 0]`. Default: `[0, 0, 0]`\n * @param scale - Uniform scale (number) or per-axis scale [x, y, z]. Examples: `0.5` or `[1, 2, 1]`. Default: `1`\n * @param meshVisibility - Mesh visibility as key-value pairs. Example: `{ \"Part1\": true, \"Part2\": false }`\n * @param materialColors - Material colors as key-value pairs. Example: `{ \"MaterialName\": \"#ff0000\" }`\n * @param materialTextures - Material texture maps. Example: `{ \"Metal\": { map: \"/tex.webp\", normalMap: \"/normal.webp\" } }`\n * @param onLoad - Callback when model finishes loading. Receives ModelInfo object with metadata: `{ meshes, materials, bones, nodeCount }`\n * @param onError - Callback when model fails to load. Receives Error object\n * \n * @example\n * ```tsx\n * <Model \n * url=\"/models/sword.glb\"\n * position={[0, 0, 0]}\n * rotation={[0, Math.PI/2, 0]}\n * scale={0.5}\n * meshVisibility={{ \"Blade\": true, \"Handle\": false }}\n * onLoad={(info) => console.log('Loaded:', info)}\n * />\n * ```\n */\nexport function Model({\n url,\n position = [0, 0, 0],\n rotation = [0, 0, 0],\n scale = 1,\n meshVisibility,\n materialColors,\n materialTextures,\n onLoad,\n onError: _onError,\n}: ModelProps) {\n const { scene } = useGLTF(url);\n const onLoadRef = useRef(onLoad);\n onLoadRef.current = onLoad;\n\n // Clone scene for independent instance\n const clonedScene = useMemo(() => {\n const clone = scene.clone();\n \n console.log('[Model] 🔍 Scene clone created, checking materials...');\n \n // Setup shadows and collect info\n const meshes: string[] = [];\n const materials = new Set<string>();\n const bones: string[] = [];\n let nodeCount = 0;\n\n clone.traverse((child) => {\n nodeCount++;\n \n if (child.type === 'Bone') {\n bones.push(child.name);\n }\n \n if ((child as THREE.Mesh).isMesh) {\n const mesh = child as THREE.Mesh;\n meshes.push(mesh.name);\n mesh.castShadow = true;\n mesh.receiveShadow = true;\n \n const mats = Array.isArray(mesh.material) ? mesh.material : [mesh.material];\n mats.forEach((mat) => {\n materials.add(mat.name);\n console.log(`[Model] 📦 Material found: \"${mat.name}\" (uuid: ${mat.uuid})`);\n const stdMat = mat as THREE.MeshStandardMaterial;\n console.log(` - Has map: ${stdMat.map ? 'YES' : 'NO'}`);\n console.log(` - Has normalMap: ${stdMat.normalMap ? 'YES' : 'NO'}`);\n console.log(` - Has roughnessMap: ${stdMat.roughnessMap ? 'YES' : 'NO'}`);\n console.log(` - Has metalnessMap: ${stdMat.metalnessMap ? 'YES' : 'NO'}`);\n });\n }\n });\n \n console.log(`[Model] 📊 Clone summary: ${meshes.length} meshes, ${materials.size} materials`);\n\n // Defer callback to avoid state update during render\n setTimeout(() => {\n onLoadRef.current?.({\n meshes: meshes.sort(),\n materials: Array.from(materials).sort(),\n bones: bones.sort(),\n nodeCount,\n });\n }, 0);\n\n return clone;\n }, [scene]);\n\n // Mesh visibility hook\n useMeshVisibility(clonedScene, meshVisibility);\n\n // Material colors hook\n useMaterialColor(clonedScene, materialColors);\n\n // Material textures hook\n useMaterialTexture(clonedScene, materialTextures);\n\n const scaleArray = typeof scale === 'number' ? [scale, scale, scale] : scale;\n\n return (\n <group position={position} rotation={rotation} scale={scaleArray as [number, number, number]}>\n <primitive object={clonedScene} />\n </group>\n );\n}\n\n// Preload helper\nModel.preload = (url: string) => {\n useGLTF.preload(url);\n};\n","import { useRef, useCallback, useEffect } from 'react';\r\nimport { useAnimations } from '@react-three/drei';\r\nimport * as THREE from 'three';\r\n\r\ninterface UseAnimationControllerOptions {\r\n defaultAnimation?: string;\r\n}\r\n\r\nexport function useAnimationController(\r\n animations: THREE.AnimationClip[],\r\n scene: THREE.Object3D,\r\n options?: UseAnimationControllerOptions\r\n) {\r\n const { actions, names } = useAnimations(animations, scene);\r\n const currentActionRef = useRef<THREE.AnimationAction | null>(null);\r\n const defaultAnimationRef = useRef<string | undefined>(options?.defaultAnimation);\r\n\r\n // Play default animation on mount\r\n useEffect(() => {\r\n if (names.length === 0) return;\r\n\r\n const defaultAnim = defaultAnimationRef.current;\r\n let animToPlay = names[0];\r\n\r\n if (defaultAnim) {\r\n const match = names.find((n) => n === defaultAnim || n.includes(defaultAnim));\r\n if (match) animToPlay = match;\r\n }\r\n\r\n const action = actions[animToPlay];\r\n if (action) {\r\n action.reset().fadeIn(0.5).play();\r\n currentActionRef.current = action;\r\n }\r\n }, [actions, names]);\r\n\r\n const playAnimation = useCallback(\r\n (\r\n name: string,\r\n opts?: {\r\n loop?: boolean;\r\n crossFadeDuration?: number;\r\n restoreDefault?: boolean;\r\n }\r\n ) => {\r\n const {\r\n loop = false,\r\n crossFadeDuration = 0.2,\r\n restoreDefault = true,\r\n } = opts || {};\r\n\r\n // Find animation (exact match or partial)\r\n let targetAction = actions[name];\r\n\r\n if (!targetAction) {\r\n const match = Object.keys(actions).find(\r\n (a) =>\r\n a.toLowerCase().includes(name.toLowerCase()) ||\r\n name.toLowerCase().includes(a.toLowerCase())\r\n );\r\n if (match) {\r\n targetAction = actions[match];\r\n }\r\n }\r\n\r\n if (!targetAction) {\r\n console.warn(`Animation \"${name}\" not found. Available: ${names.join(', ')}`);\r\n return;\r\n }\r\n\r\n // Don't restart if already playing\r\n const prev = currentActionRef.current;\r\n if (prev === targetAction && targetAction.isRunning()) {\r\n return;\r\n }\r\n\r\n // Crossfade from previous\r\n if (prev && prev !== targetAction) {\r\n prev.fadeOut(crossFadeDuration);\r\n }\r\n\r\n // Setup new action\r\n targetAction.reset();\r\n targetAction.fadeIn(crossFadeDuration);\r\n targetAction.setLoop(\r\n loop ? THREE.LoopRepeat : THREE.LoopOnce,\r\n loop ? Infinity : 1\r\n );\r\n targetAction.clampWhenFinished = !loop;\r\n targetAction.play();\r\n\r\n // Force immediate update for non-looping animations\r\n if (!loop) {\r\n targetAction.getMixer().update(0);\r\n }\r\n\r\n currentActionRef.current = targetAction;\r\n\r\n // Restore default animation after one-shot completes\r\n if (restoreDefault && !loop && defaultAnimationRef.current) {\r\n const mixer = targetAction.getMixer();\r\n const onFinished = (e: { action: THREE.AnimationAction }) => {\r\n if (e.action === targetAction) {\r\n mixer.removeEventListener('finished', onFinished);\r\n \r\n const defaultAction = actions[defaultAnimationRef.current!];\r\n if (defaultAction) {\r\n targetAction.fadeOut(crossFadeDuration);\r\n defaultAction.reset().fadeIn(crossFadeDuration).play();\r\n currentActionRef.current = defaultAction;\r\n }\r\n }\r\n };\r\n mixer.addEventListener('finished', onFinished);\r\n }\r\n },\r\n [actions, names]\r\n );\r\n\r\n const stopAnimation = useCallback(() => {\r\n currentActionRef.current?.fadeOut(0.2);\r\n currentActionRef.current = null;\r\n }, []);\r\n\r\n const getAnimationNames = useCallback(() => names, [names]);\r\n\r\n return {\r\n playAnimation,\r\n stopAnimation,\r\n getAnimationNames,\r\n actions,\r\n };\r\n}\r\n","import { useRef, useCallback, useEffect } from 'react';\r\nimport { useFrame } from '@react-three/fiber';\r\nimport * as THREE from 'three';\r\n\r\nexport function useMorphTargets(\r\n scene: THREE.Object3D,\r\n initialValues?: Record<string, number>\r\n) {\r\n const morphValuesRef = useRef<Record<string, number>>(initialValues || {});\r\n const morphNamesRef = useRef<string[]>([]);\r\n const meshesWithMorphsRef = useRef<THREE.Mesh[]>([]);\r\n\r\n // Discover morph targets\r\n useEffect(() => {\r\n const names = new Set<string>();\r\n const meshes: THREE.Mesh[] = [];\r\n\r\n scene.traverse((child) => {\r\n if (\r\n child instanceof THREE.Mesh &&\r\n child.morphTargetDictionary &&\r\n child.morphTargetInfluences\r\n ) {\r\n meshes.push(child);\r\n Object.keys(child.morphTargetDictionary).forEach((name) => {\r\n names.add(name);\r\n });\r\n }\r\n });\r\n\r\n morphNamesRef.current = Array.from(names).sort();\r\n meshesWithMorphsRef.current = meshes;\r\n }, [scene]);\r\n\r\n // Apply morph targets every frame\r\n useFrame(() => {\r\n const values = morphValuesRef.current;\r\n \r\n meshesWithMorphsRef.current.forEach((mesh) => {\r\n if (!mesh.morphTargetDictionary || !mesh.morphTargetInfluences) return;\r\n \r\n Object.entries(values).forEach(([name, value]) => {\r\n const index = mesh.morphTargetDictionary![name];\r\n if (index !== undefined) {\r\n mesh.morphTargetInfluences![index] = value;\r\n }\r\n });\r\n });\r\n });\r\n\r\n // Update values when props change\r\n useEffect(() => {\r\n if (initialValues) {\r\n morphValuesRef.current = { ...initialValues };\r\n }\r\n }, [initialValues]);\r\n\r\n const setMorphTarget = useCallback((name: string, value: number) => {\r\n morphValuesRef.current[name] = Math.max(0, Math.min(1, value));\r\n }, []);\r\n\r\n const getMorphTargetNames = useCallback(() => morphNamesRef.current, []);\r\n\r\n const getMorphTargetValues = useCallback(() => ({ ...morphValuesRef.current }), []);\r\n\r\n return {\r\n setMorphTarget,\r\n getMorphTargetNames,\r\n getMorphTargetValues,\r\n };\r\n}\r\n","import { createContext, useContext } from 'react';\r\nimport type { AnimatedModelContextValue } from '../../types';\r\n\r\nexport const AnimatedModelContext = createContext<AnimatedModelContextValue | null>(null);\r\n\r\nexport function useAnimatedModelContext() {\r\n const context = useContext(AnimatedModelContext);\r\n if (!context) {\r\n throw new Error('BoneAttachment must be used within an AnimatedModel');\r\n }\r\n return context;\r\n}\r\n","import {\n forwardRef,\n useImperativeHandle,\n useRef,\n useMemo,\n useEffect,\n} from 'react';\nimport * as THREE from 'three';\nimport { useGLTF } from '@react-three/drei';\nimport * as SkeletonUtils from 'three/examples/jsm/utils/SkeletonUtils.js';\nimport { useAnimationController } from '../../hooks/useAnimationController';\nimport { useMorphTargets } from '../../hooks/useMorphTargets';\nimport { useMeshVisibility } from '../../hooks/useMeshVisibility';\nimport { useMaterialColor } from '../../hooks/useMaterialColor';\nimport { useMaterialTexture } from '../../hooks/useMaterialTexture';\nimport { AnimatedModelContext } from './AnimatedModelContext';\nimport type { AnimatedModelProps, AnimatedModelHandle } from '../../types';\n\n/**\n * AnimatedModel - 3D model with animation support and bone attachment capability\n * \n * @remarks\n * This component:\n * - Loads GLB/GLTF models with animations using useGLTF\n * - Properly clones skinned meshes using SkeletonUtils for multiple instances\n * - Plays animations with automatic crossfading\n * - Supports morph targets for character customization\n * - Provides context for BoneAttachment children\n * - Exposes imperative API via ref for external animation control\n * \n * The ref provides these methods:\n * - `playAnimation(name, options)` - Play an animation with optional loop/restore\n * - `stopAnimation()` - Stop current animation\n * - `getAnimationNames()` - Get list of available animations\n * - `getGroup()` - Get the THREE.Group for advanced manipulation\n * - `setMorphTarget(name, value)` - Set morph target value\n * - `getMorphTargetNames()` - Get available morph target names\n * \n * @param ref - React ref for imperative API access\n * @param url - URL or path to GLB/GLTF model file with animations. Example: `\"/models/character.glb\"`\n * @param position - Model position in 3D space as [x, y, z]. Example: `[0, -1, 0]`. Default: `[0, 0, 0]`\n * @param rotation - Model rotation in radians as [x, y, z]. Example: `[0, Math.PI, 0]`. Default: `[0, 0, 0]`\n * @param scale - Uniform scale (number) or per-axis scale [x, y, z]. Examples: `0.5` or `[1, 2, 1]`. Default: `1`\n * @param defaultAnimation - Name of animation to play on load. If not specified, plays first animation. Example: `\"Idle\"`\n * @param morphTargets - Morph target values as key-value pairs where value is 0-1. Example: `{ muscular: 0.5, thin: 0.2 }`\n * @param meshVisibility - Mesh visibility as key-value pairs. Example: `{ \"Hairgirl1\": true, \"Hairgirl2\": false }`\n * @param materialColors - Material colors as key-value pairs. Example: `{ \"Hair_girl_1\": \"#ff0000\" }`\n * @param materialTextures - Material texture maps. Example: `{ \"Metal\": { map: \"/tex.webp\", normalMap: \"/normal.webp\" } }`\n * @param children - Child components, typically BoneAttachment components for attaching items\n * @param onLoad - Callback when model loads. Receives AnimatedModelInfo object: `{ meshes, materials, bones, nodeCount, animations, morphTargetNames }`\n * @param onError - Callback when model fails to load. Receives Error object\n * \n * @example\n * ```tsx\n * const modelRef = useRef<AnimatedModelHandle>(null);\n * \n * <AnimatedModel\n * ref={modelRef}\n * url=\"/models/character.glb\"\n * defaultAnimation=\"Idle\"\n * morphTargets={{ muscular: 0.5 }}\n * meshVisibility={{ \"Hairgirl1\": true, \"Hairgirl2\": false }}\n * materialColors={{ \"Hair_girl_1\": \"#ff0000\" }}\n * materialTextures={{ \"Metal\": { map: \"/metal.webp\", roughnessMap: \"/rough.webp\" } }}\n * position={[0, -1, 0]}\n * >\n * <BoneAttachment bone=\"hand_r\">\n * <Model url=\"/models/sword.glb\" />\n * </BoneAttachment>\n * </AnimatedModel>\n * \n * // Control animations from outside\n * modelRef.current?.playAnimation('Attack', { loop: false });\n * ```\n */\nexport const AnimatedModel = forwardRef<AnimatedModelHandle, AnimatedModelProps>(\n (\n {\n url,\n position = [0, 0, 0],\n rotation = [0, 0, 0],\n scale = 1,\n defaultAnimation,\n morphTargets,\n meshVisibility,\n materialColors,\n materialTextures,\n onLoad,\n onError: _onError,\n children,\n },\n ref\n ) => {\n const groupRef = useRef<THREE.Group>(null);\n const morphNamesRef = useRef<string[]>([]);\n const onLoadRef = useRef(onLoad);\n onLoadRef.current = onLoad;\n\n // Load and clone model\n const { scene: gltfScene, animations } = useGLTF(url);\n const scene = useMemo(() => SkeletonUtils.clone(gltfScene), [gltfScene]);\n\n // Animation controller\n const { playAnimation, stopAnimation, getAnimationNames } = useAnimationController(\n animations,\n scene,\n { defaultAnimation }\n );\n\n // Morph targets\n const { setMorphTarget } = useMorphTargets(\n scene,\n morphTargets\n );\n\n // Mesh visibility\n const { setMeshVisibility, getMeshNames } = useMeshVisibility(\n scene,\n meshVisibility\n );\n\n // Material colors\n const { setMaterialColor, getMaterialNames, getMaterialColor } = useMaterialColor(\n scene,\n materialColors\n );\n\n // Material textures\n const { setMaterialTextures, clearMaterialTextures } = useMaterialTexture(\n scene,\n materialTextures\n );\n\n // Discover morph targets and call onLoad\n useEffect(() => {\n if (!scene) return;\n\n const timer = setTimeout(() => {\n const bones: string[] = [];\n const meshes: string[] = [];\n const materials = new Set<string>();\n const morphNames = new Set<string>();\n let nodeCount = 0;\n\n scene.traverse((child) => {\n nodeCount++;\n \n if (child.type === 'Bone') {\n bones.push(child.name);\n }\n \n if ((child as THREE.Mesh).isMesh) {\n const mesh = child as THREE.Mesh;\n meshes.push(mesh.name);\n \n // Setup shadows\n mesh.castShadow = true;\n mesh.receiveShadow = true;\n \n const mats = Array.isArray(mesh.material) ? mesh.material : [mesh.material];\n mats.forEach((mat) => {\n materials.add(mat.name);\n mat.shadowSide = THREE.DoubleSide;\n });\n\n if (mesh.morphTargetDictionary) {\n Object.keys(mesh.morphTargetDictionary).forEach((name) => {\n morphNames.add(name);\n });\n }\n }\n });\n\n morphNamesRef.current = Array.from(morphNames).sort();\n\n onLoadRef.current?.({\n meshes: meshes.sort(),\n materials: Array.from(materials).sort(),\n bones: bones.sort(),\n nodeCount,\n animations: animations.map((a) => a.name),\n morphTargetNames: morphNamesRef.current,\n });\n }, 0);\n\n return () => clearTimeout(timer);\n }, [scene, animations]);\n\n // Expose imperative handle\n useImperativeHandle(ref, () => ({\n playAnimation,\n stopAnimation,\n getAnimationNames,\n getGroup: () => groupRef.current,\n setMorphTarget,\n getMorphTargetNames: () => morphNamesRef.current,\n setMeshVisibility,\n getMeshNames,\n setMaterialColor,\n getMaterialNames,\n getMaterialColor,\n setMaterialTextures,\n clearMaterialTextures,\n }));\n\n // Context for BoneAttachment children\n const contextValue = useMemo(\n () => ({\n scene,\n getBone: (name: string) => scene.getObjectByName(name) || null,\n }),\n [scene]\n );\n\n const scaleArray = typeof scale === 'number' ? [scale, scale, scale] : scale;\n\n return (\n <AnimatedModelContext.Provider value={contextValue}>\n <group\n ref={groupRef}\n position={position}\n rotation={rotation}\n scale={scaleArray as [number, number, number]}\n >\n <primitive object={scene} />\n {children}\n </group>\n </AnimatedModelContext.Provider>\n );\n }\n);\n\nAnimatedModel.displayName = 'AnimatedModel';\n\n// Preload helper\n(AnimatedModel as any).preload = (url: string) => {\n useGLTF.preload(url);\n};\n","import { forwardRef, useImperativeHandle, useMemo, useEffect, useRef } from 'react';\nimport { useGLTF } from '@react-three/drei';\nimport * as THREE from 'three';\nimport { useMorphTargets } from '../../hooks/useMorphTargets';\nimport { useMeshVisibility } from '../../hooks/useMeshVisibility';\nimport { useMaterialColor } from '../../hooks/useMaterialColor';\nimport { useMaterialTexture } from '../../hooks/useMaterialTexture';\nimport type { MorphableModelProps, MorphableModelHandle } from '../../types';\n\n/**\n * MorphableModel - 3D model with morph targets for shape customization\n * \n * @remarks\n * This component:\n * - Loads GLB/GLTF models with morph targets (blend shapes)\n * - Dynamically discovers available morph targets from the model\n * - Applies morph target values in real-time\n * - Provides imperative API via ref for programmatic control\n * \n * Morph targets are commonly used for:\n * - Character customization (muscular, thin, fat)\n * - Facial expressions (smile, frown, blink)\n * - Clothing fit adjustments\n * \n * @param ref - React ref for imperative API access\n * @param url - URL or path to GLB/GLTF model file with morph targets. Example: `\"/models/character.glb\"`\n * @param position - Model position in 3D space as [x, y, z]. Example: `[0, -1, 0]`. Default: `[0, 0, 0]`\n * @param rotation - Model rotation in radians as [x, y, z]. Example: `[0, Math.PI, 0]`. Default: `[0, 0, 0]`\n * @param scale - Uniform scale (number) or per-axis scale [x, y, z]. Examples: `0.5` or `[1, 2, 1]`. Default: `1`\n * @param morphTargets - Morph target values as key-value pairs where value is 0-1. Example: `{ muscular: 0.5, thin: 0.2 }`\n * @param meshVisibility - Mesh visibility as key-value pairs. Example: `{ \"Hairgirl1\": true, \"Hairgirl2\": false }`\n * @param materialColors - Material colors as key-value pairs. Example: `{ \"Hair_girl_1\": \"#ff0000\" }`\n * @param materialTextures - Material texture maps. Example: `{ \"Metal\": { map: \"/tex.webp\", normalMap: \"/normal.webp\" } }`\n * @param onMorphTargetsFound - Callback when morph targets are discovered from the model. Receives array of morph target names: `[\"muscular\", \"thin\", \"fat\"]`\n * @param onLoad - Callback when model finishes loading. Receives ModelInfo object with metadata: `{ meshes, materials, bones, nodeCount }`\n * @param onError - Callback when model fails to load. Receives Error object\n * \n * @example\n * ```tsx\n * const modelRef = useRef<MorphableModelHandle>(null);\n * const [morphs, setMorphs] = useState({ muscular: 0.5, thin: 0.2 });\n * \n * <MorphableModel\n * ref={modelRef}\n * url=\"/models/character.glb\"\n * morphTargets={morphs}\n * meshVisibility={{ \"Hairgirl1\": true, \"Hairgirl2\": false }}\n * onMorphTargetsFound={(names) => console.log('Available:', names)}\n * position={[0, -1, 0]}\n * />\n * \n * // Control programmatically\n * modelRef.current?.setMorphTarget('muscular', 0.8);\n * ```\n */\nexport const MorphableModel = forwardRef<MorphableModelHandle, MorphableModelProps>(\n (\n {\n url,\n position = [0, 0, 0],\n rotation = [0, 0, 0],\n scale = 1,\n morphTargets,\n meshVisibility,\n materialColors,\n materialTextures,\n onMorphTargetsFound,\n onLoad,\n onError: _onError,\n },\n ref\n ) => {\n const { scene } = useGLTF(url);\n const onLoadRef = useRef(onLoad);\n onLoadRef.current = onLoad;\n const onMorphTargetsFoundRef = useRef(onMorphTargetsFound);\n onMorphTargetsFoundRef.current = onMorphTargetsFound;\n\n // Clone scene\n const clonedScene = useMemo(() => scene.clone(), [scene]);\n\n // Morph targets hook\n const { setMorphTarget, getMorphTargetNames, getMorphTargetValues } = useMorphTargets(\n clonedScene,\n morphTargets\n );\n\n // Mesh visibility hook\n const { setMeshVisibility, getMeshNames } = useMeshVisibility(\n clonedScene,\n meshVisibility\n );\n\n // Material colors hook\n const { setMaterialColor, getMaterialNames, getMaterialColor } = useMaterialColor(\n clonedScene,\n materialColors\n );\n\n // Material textures hook\n const { setMaterialTextures, clearMaterialTextures } = useMaterialTexture(\n clonedScene,\n materialTextures\n );\n\n // Discover morph targets and notify\n useEffect(() => {\n const names = getMorphTargetNames();\n if (names.length > 0) {\n onMorphTargetsFoundRef.current?.(names);\n }\n }, [clonedScene, getMorphTargetNames]);\n\n // Setup shadows and call onLoad\n useEffect(() => {\n if (!clonedScene) return;\n\n const meshes: string[] = [];\n const materials = new Set<string>();\n const bones: string[] = [];\n let nodeCount = 0;\n\n clonedScene.traverse((child) => {\n nodeCount++;\n \n if (child.type === 'Bone') {\n bones.push(child.name);\n }\n \n if ((child as THREE.Mesh).isMesh) {\n const mesh = child as THREE.Mesh;\n meshes.push(mesh.name);\n mesh.castShadow = true;\n mesh.receiveShadow = true;\n \n const mats = Array.isArray(mesh.material) ? mesh.material : [mesh.material];\n mats.forEach((mat) => materials.add(mat.name));\n }\n });\n\n onLoadRef.current?.({\n meshes: meshes.sort(),\n materials: Array.from(materials).sort(),\n bones: bones.sort(),\n nodeCount,\n });\n }, [clonedScene]);\n\n // Expose handle\n useImperativeHandle(ref, () => ({\n setMorphTarget,\n getMorphTargetNames,\n getMorphTargetValues,\n setMeshVisibility,\n getMeshNames,\n setMaterialColor,\n getMaterialNames,\n getMaterialColor,\n setMaterialTextures,\n clearMaterialTextures,\n }));\n\n const scaleArray = typeof scale === 'number' ? [scale, scale, scale] : scale;\n\n return (\n <group position={position} rotation={rotation} scale={scaleArray as [number, number, number]}>\n <primitive object={clonedScene} />\n </group>\n );\n }\n);\n\nMorphableModel.displayName = 'MorphableModel';\n","import { useState, useEffect } from 'react';\r\nimport { createPortal } from '@react-three/fiber';\r\nimport * as THREE from 'three';\r\nimport { useAnimatedModelContext } from '../AnimatedModel/AnimatedModelContext';\r\nimport type { BoneAttachmentProps } from '../../types';\r\n\r\n/**\r\n * BoneAttachment - Attach models to bones of an AnimatedModel parent\r\n * \r\n * @remarks\r\n * This component:\r\n * - Must be used as a child of AnimatedModel\r\n * - Uses React Three Fiber's createPortal to attach to a bone\r\n * - Automatically follows bone transformations during animations\r\n * - Supports position/rotation/scale offsets relative to the bone\r\n * \r\n * Common bone names:\r\n * - Hand bones: \"hand_r\", \"hand_l\", \"DEF-handR\", \"DEF-handL\"\r\n * - Spine/Back: \"spine\", \"spine_upper\", \"back\"\r\n * - Head: \"head\", \"neck\"\r\n * \r\n * @param children - Child components to attach to the bone (typically Model components)\r\n * @param bone - Bone name to attach to. Must match bone name in the parent AnimatedModel. Example: `\"hand_r\"` or `\"DEF-handR\"`\r\n * @param position - Position offset relative to the bone as [x, y, z]. Example: `[0.1, 0, 0]`. Default: `[0, 0, 0]`\r\n * @param rotation - Rotation offset relative to the bone in radians as [x, y, z]. Example: `[0, Math.PI/2, 0]`. Default: `[0, 0, 0]`\r\n * @param scale - Uniform scale (number) or per-axis scale [x, y, z]. Examples: `0.7` or `[1, 2, 1]`. Default: `1`\r\n * \r\n * @example\r\n * ```tsx\r\n * <AnimatedModel url=\"/character.glb\">\r\n * // Sword in right hand\r\n * <BoneAttachment \r\n * bone=\"hand_r\"\r\n * position={[0.1, 0, 0]}\r\n * rotation={[0, Math.PI/2, 0]}\r\n * >\r\n * <Model url=\"/sword.glb\" scale={0.7} />\r\n * </BoneAttachment>\r\n * \r\n * // Shield on back\r\n * <BoneAttachment bone=\"spine_upper\">\r\n * <Model url=\"/shield.glb\" />\r\n * </BoneAttachment>\r\n * </AnimatedModel>\r\n * ```\r\n */\r\nexport function BoneAttachment({\r\n children,\r\n bone,\r\n position = [0, 0, 0],\r\n rotation = [0, 0, 0],\r\n scale = 1,\r\n}: BoneAttachmentProps) {\r\n const { getBone } = useAnimatedModelContext();\r\n const [boneObject, setBoneObject] = useState<THREE.Object3D | null>(null);\r\n\r\n useEffect(() => {\r\n const found = getBone(bone);\r\n if (found) {\r\n setBoneObject(found);\r\n } else {\r\n console.warn(`Bone \"${bone}\" not found in model`);\r\n }\r\n }, [bone, getBone]);\r\n\r\n if (!boneObject) {\r\n return null;\r\n }\r\n\r\n const scaleArray = typeof scale === 'number' ? [scale, scale, scale] : scale;\r\n\r\n return createPortal(\r\n <group\r\n position={position}\r\n rotation={rotation}\r\n scale={scaleArray as [number, number, number]}\r\n >\r\n {children}\r\n </group>,\r\n boneObject\r\n );\r\n}\r\n","import { useMemo, useEffect } from 'react';\r\nimport { useGLTF } from '@react-three/drei';\r\nimport * as THREE from 'three';\r\nimport * as SkeletonUtils from 'three/examples/jsm/utils/SkeletonUtils.js';\r\nimport type { ModelInfo } from '../types';\r\n\r\ninterface UseClonedModelOptions {\r\n onLoad?: (info: ModelInfo) => void;\r\n onError?: (error: Error) => void;\r\n}\r\n\r\nexport function useClonedModel(url: string, options?: UseClonedModelOptions) {\r\n const { scene, animations } = useGLTF(url);\r\n \r\n // Clone scene using SkeletonUtils for proper skinned mesh handling\r\n const clonedScene = useMemo(() => {\r\n return SkeletonUtils.clone(scene);\r\n }, [scene]);\r\n\r\n // Extract model info and setup shadows\r\n useEffect(() => {\r\n if (!clonedScene) return;\r\n\r\n const bones: string[] = [];\r\n const meshes: string[] = [];\r\n const materials = new Set<string>();\r\n let nodeCount = 0;\r\n\r\n clonedScene.traverse((child) => {\r\n nodeCount++;\r\n \r\n if (child.type === 'Bone') {\r\n bones.push(child.name);\r\n }\r\n \r\n if ((child as THREE.Mesh).isMesh) {\r\n const mesh = child as THREE.Mesh;\r\n meshes.push(mesh.name);\r\n \r\n // Setup shadows\r\n mesh.castShadow = true;\r\n mesh.receiveShadow = true;\r\n \r\n // Collect material names\r\n const mats = Array.isArray(mesh.material) ? mesh.material : [mesh.material];\r\n mats.forEach((mat) => {\r\n materials.add(mat.name);\r\n mat.shadowSide = THREE.DoubleSide;\r\n });\r\n }\r\n });\r\n\r\n options?.onLoad?.({\r\n meshes: meshes.sort(),\r\n materials: Array.from(materials).sort(),\r\n bones: bones.sort(),\r\n nodeCount,\r\n });\r\n }, [clonedScene, options]);\r\n\r\n return { scene: clonedScene, animations };\r\n}\r\n\r\n// Preload helper\r\nexport function preloadModel(url: string) {\r\n useGLTF.preload(url);\r\n}\r\n"],"names":["CameraController","position","controlsConfig","camera","useThree","controlsRef","useRef","useEffect","jsx","OrbitControls","Background","background","isColor","BackgroundImage","url","texture","useTexture","useMemo","THREE","LightRenderer","config","intensity","castShadow","angle","penumbra","decay","distance","color","skyColor","groundColor","skyColorObj","groundColorObj","Scene3D","children","controls","shadows","lights","contactShadows","style","className","cameraConfig","contactShadowsConfig","jsxs","Canvas","Suspense","lightConfig","index","ContactShadows","useMeshVisibility","scene","visibility","meshMapRef","visibilityRef","overridesRef","meshMap","child","mesh","useFrame","merged","name","visible","setMeshVisibility","useCallback","getMeshNames","getMeshVisibility","result","useMaterialColor","initialColors","materialMapRef","colorMapRef","materialMap","material","materialName","threeColor","parseColor","targetColor","mat","setMaterialColor","getMaterialNames","getMaterialColor","getAllMaterialColors","colors","useMaterialTexture","textures","gl","textureLoaderRef","ktx2LoaderRef","loadedTexturesRef","loadingTexturesRef","appliedTexturesRef","supportsWASM","ktx2Loader","KTX2Loader","response","error","getLoader","texturesKey","disposed","normalizedTextures","loadTasks","textureConfig","_a","_b","textureType","cacheKey","cached","applyTexture","taskIndex","loadNextTexture","stdMat","task","loader","loaderType","width","_c","height","_d","_f","_e","delay","progress","percent","type","setMaterialTextures","normalized","clearMaterialTextures","Model","rotation","scale","meshVisibility","materialColors","materialTextures","onLoad","_onError","useGLTF","onLoadRef","clonedScene","clone","meshes","materials","bones","nodeCount","scaleArray","useAnimationController","animations","options","actions","names","useAnimations","currentActionRef","defaultAnimationRef","defaultAnim","animToPlay","match","action","playAnimation","opts","loop","crossFadeDuration","restoreDefault","targetAction","a","prev","mixer","onFinished","e","defaultAction","stopAnimation","getAnimationNames","useMorphTargets","initialValues","morphValuesRef","morphNamesRef","meshesWithMorphsRef","values","value","setMorphTarget","getMorphTargetNames","getMorphTargetValues","AnimatedModelContext","createContext","useAnimatedModelContext","context","useContext","AnimatedModel","forwardRef","defaultAnimation","morphTargets","ref","groupRef","gltfScene","SkeletonUtils","timer","morphNames","useImperativeHandle","contextValue","MorphableModel","onMorphTargetsFound","onMorphTargetsFoundRef","BoneAttachment","bone","getBone","boneObject","setBoneObject","useState","found","createPortal","useClonedModel","preloadModel"],"mappings":"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"}