import { memo, Suspense, useMemo } from "react"; import { useGLTF, useTexture } from "@react-three/drei"; import { BASE_URL, shapeTextureToUrl, shapeToUrl } from "../loaders"; import { filterGeometryByVertexGroups, getHullBoneIndices } from "../meshUtils"; import { createAlphaAsRoughnessMaterial, setupAlphaAsRoughnessTexture, } from "../shaderMaterials"; import { MeshStandardMaterial } from "three"; import { setupColor } from "../textureUtils"; import { useDebug } from "./SettingsProvider"; import { useShapeInfo } from "./ShapeInfoProvider"; import { FloatingLabel } from "./FloatingLabel"; const FALLBACK_URL = `${BASE_URL}/black.png`; /** * Load a .glb file that was converted from a .dts, used for static shapes. */ export function useStaticShape(shapeName: string) { const url = shapeToUrl(shapeName); return useGLTF(url); } export function ShapeTexture({ material, shapeName, }: { material?: MeshStandardMaterial; shapeName?: string; }) { const url = shapeTextureToUrl(material.name, FALLBACK_URL); const isOrganic = shapeName && /borg|xorg|porg|dorg/i.test(shapeName); const texture = useTexture(url, (texture) => { if (!isOrganic) { setupAlphaAsRoughnessTexture(texture); } return setupColor(texture); }); const customMaterial = useMemo(() => { // Only use alpha-as-roughness material for borg shapes if (!isOrganic) { const shaderMaterial = createAlphaAsRoughnessMaterial(); shaderMaterial.map = texture; return shaderMaterial; } // For non-borg shapes, use the original GLTF material with updated texture const clonedMaterial = material.clone(); clonedMaterial.map = texture; clonedMaterial.transparent = true; clonedMaterial.alphaTest = 0.9; clonedMaterial.side = 2; // DoubleSide return clonedMaterial; }, [material, texture, isOrganic]); return ; } export function ShapePlaceholder({ color }: { color: string }) { return ( ); } export function DebugPlaceholder({ color }: { color: string }) { const { debugMode } = useDebug(); return debugMode ? : null; } export const ShapeModel = memo(function ShapeModel() { const { shapeName } = useShapeInfo(); const { debugMode } = useDebug(); const { nodes } = useStaticShape(shapeName); const hullBoneIndices = useMemo(() => { const skeletonsFound = Object.values(nodes).filter( (node: any) => node.skeleton, ); if (skeletonsFound.length > 0) { const skeleton = (skeletonsFound[0] as any).skeleton; return getHullBoneIndices(skeleton); } return new Set(); }, [nodes]); const processedNodes = useMemo(() => { return Object.entries(nodes) .filter( ([name, node]: [string, any]) => node.material && node.material.name !== "Unassigned" && !node.name.match(/^Hulk/i), ) .map(([name, node]: [string, any]) => { const geometry = filterGeometryByVertexGroups( node.geometry, hullBoneIndices, ); return { node, geometry }; }); }, [nodes, hullBoneIndices]); return ( {processedNodes.map(({ node, geometry }) => ( {node.material ? ( } > {Array.isArray(node.material) ? ( node.material.map((mat, index) => ( )) ) : ( )} ) : null} ))} {debugMode ? {shapeName} : null} ); });