diff --git a/src/components/GenericShape.tsx b/src/components/GenericShape.tsx index d040fff2..73e7953c 100644 --- a/src/components/GenericShape.tsx +++ b/src/components/GenericShape.tsx @@ -2,6 +2,8 @@ import { Suspense } from "react"; import { useGLTF, useTexture } from "@react-three/drei"; import { BASE_URL, shapeTextureToUrl, shapeToUrl } from "../loaders"; import { setupColor } from "../textureUtils"; +import { filterGeometryByVertexGroups, getHullBoneIndices } from "../meshUtils"; +import { MeshStandardMaterial } from "three"; const FALLBACK_URL = `${BASE_URL}/black.png`; @@ -13,45 +15,74 @@ export function useStaticShape(shapeName: string) { return useGLTF(url); } -export function ShapeTexture({ materialName }: { materialName: string }) { - // console.log({ materialName }); - const url = shapeTextureToUrl(materialName, FALLBACK_URL); +export function ShapeTexture({ + material, +}: { + material?: MeshStandardMaterial; +}) { + const url = shapeTextureToUrl(material.name, FALLBACK_URL); const texture = useTexture(url, (texture) => setupColor(texture)); - - return ; + material.map = texture; + material.side = 2; + material.transparent = true; + return ; } export function ShapeModel({ shapeName }: { shapeName: string }) { const { nodes } = useStaticShape(shapeName); + let hullBoneIndices = new Set(); + const skeletonsFound = Object.values(nodes).filter( + (node: any) => node.skeleton + ); + + if (skeletonsFound.length > 0) { + const skeleton = (skeletonsFound[0] as any).skeleton; + hullBoneIndices = getHullBoneIndices(skeleton); + } + return ( <> {Object.entries(nodes) .filter( ([name, node]: [string, any]) => - !node.material || !node.material.name.match(/\.\d+$/) + node.material && + node.material.name !== "Unassigned" && + !node.name.match(/^Hulk/i) ) - .map(([name, node]: [string, any]) => ( - - {node.material ? ( - - } - > - {Array.isArray(node.material) ? ( - node.material.map((mat, index) => ( - - )) - ) : ( - - )} - - ) : null} - - ))} + .map(([name, node]: [string, any]) => { + const geometry = filterGeometryByVertexGroups( + node.geometry, + hullBoneIndices + ); + + return ( + + {node.material ? ( + + } + > + {Array.isArray(node.material) ? ( + node.material.map((mat, index) => ( + + )) + ) : ( + + )} + + ) : null} + + ); + })} ); } diff --git a/src/meshUtils.ts b/src/meshUtils.ts new file mode 100644 index 00000000..ea4109ab --- /dev/null +++ b/src/meshUtils.ts @@ -0,0 +1,81 @@ +/** + * Extract hull bone indices from a skeleton + * @param skeleton - The Three.js skeleton to scan + * @returns Set of bone indices for bones matching the hull pattern (starts with "Hulk") + */ +export function getHullBoneIndices(skeleton: any): Set { + const hullBoneIndices = new Set(); + + skeleton.bones.forEach((bone: any, index: number) => { + if (bone.name.match(/^Hulk/i)) { + hullBoneIndices.add(index); + } + }); + + return hullBoneIndices; +} + +/** + * Filter geometry by removing faces influenced by hull bones + * @param geometry - The Three.js geometry to filter + * @param hullBoneIndices - Set of bone indices that represent hull (collision) geometry + * @returns Filtered geometry with hull-influenced faces removed + */ +export function filterGeometryByVertexGroups( + geometry: any, + hullBoneIndices: Set +): any { + // If no hull bones or no skinning data, return original geometry + if (hullBoneIndices.size === 0 || !geometry.attributes.skinIndex) { + return geometry; + } + + const skinIndex = geometry.attributes.skinIndex; + const skinWeight = geometry.attributes.skinWeight; + const index = geometry.index; + + // Track which vertices are influenced by hull bones + const vertexHasHullInfluence = new Array(skinIndex.count).fill(false); + + // Check each vertex's bone influences + for (let i = 0; i < skinIndex.count; i++) { + for (let j = 0; j < 4; j++) { + const boneIndex = skinIndex.array[i * 4 + j]; + const weight = skinWeight.array[i * 4 + j]; + + // If this vertex has significant weight to a hull bone, mark it + if (weight > 0.01 && hullBoneIndices.has(boneIndex)) { + vertexHasHullInfluence[i] = true; + break; + } + } + } + + // Build new index array excluding faces that use hull-influenced vertices + if (index) { + const newIndices: number[] = []; + const indexArray = index.array; + + for (let i = 0; i < indexArray.length; i += 3) { + const i0 = indexArray[i]; + const i1 = indexArray[i + 1]; + const i2 = indexArray[i + 2]; + + // Only keep face if all vertices don't have hull influence + if ( + !vertexHasHullInfluence[i0] && + !vertexHasHullInfluence[i1] && + !vertexHasHullInfluence[i2] + ) { + newIndices.push(i0, i1, i2); + } + } + + // Create new geometry with filtered indices + const filteredGeometry = geometry.clone(); + filteredGeometry.setIndex(newIndices); + return filteredGeometry; + } + + return geometry; +}