mirror of
https://github.com/exogen/t2-mapper.git
synced 2026-07-16 00:44:59 +00:00
Hide hulls.
This commit is contained in:
parent
1c6d7effac
commit
c09fb315d5
2 changed files with 139 additions and 27 deletions
|
|
@ -2,6 +2,8 @@ import { Suspense } from "react";
|
||||||
import { useGLTF, useTexture } from "@react-three/drei";
|
import { useGLTF, useTexture } from "@react-three/drei";
|
||||||
import { BASE_URL, shapeTextureToUrl, shapeToUrl } from "../loaders";
|
import { BASE_URL, shapeTextureToUrl, shapeToUrl } from "../loaders";
|
||||||
import { setupColor } from "../textureUtils";
|
import { setupColor } from "../textureUtils";
|
||||||
|
import { filterGeometryByVertexGroups, getHullBoneIndices } from "../meshUtils";
|
||||||
|
import { MeshStandardMaterial } from "three";
|
||||||
|
|
||||||
const FALLBACK_URL = `${BASE_URL}/black.png`;
|
const FALLBACK_URL = `${BASE_URL}/black.png`;
|
||||||
|
|
||||||
|
|
@ -13,45 +15,74 @@ export function useStaticShape(shapeName: string) {
|
||||||
return useGLTF(url);
|
return useGLTF(url);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function ShapeTexture({ materialName }: { materialName: string }) {
|
export function ShapeTexture({
|
||||||
// console.log({ materialName });
|
material,
|
||||||
const url = shapeTextureToUrl(materialName, FALLBACK_URL);
|
}: {
|
||||||
|
material?: MeshStandardMaterial;
|
||||||
|
}) {
|
||||||
|
const url = shapeTextureToUrl(material.name, FALLBACK_URL);
|
||||||
const texture = useTexture(url, (texture) => setupColor(texture));
|
const texture = useTexture(url, (texture) => setupColor(texture));
|
||||||
|
material.map = texture;
|
||||||
return <meshStandardMaterial map={texture} side={2} />;
|
material.side = 2;
|
||||||
|
material.transparent = true;
|
||||||
|
return <primitive object={material} attach="material" />;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function ShapeModel({ shapeName }: { shapeName: string }) {
|
export function ShapeModel({ shapeName }: { shapeName: string }) {
|
||||||
const { nodes } = useStaticShape(shapeName);
|
const { nodes } = useStaticShape(shapeName);
|
||||||
|
|
||||||
|
let hullBoneIndices = new Set<number>();
|
||||||
|
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 (
|
return (
|
||||||
<>
|
<>
|
||||||
{Object.entries(nodes)
|
{Object.entries(nodes)
|
||||||
.filter(
|
.filter(
|
||||||
([name, node]: [string, any]) =>
|
([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]) => (
|
.map(([name, node]: [string, any]) => {
|
||||||
<mesh key={node.id} geometry={node.geometry} castShadow receiveShadow>
|
const geometry = filterGeometryByVertexGroups(
|
||||||
{node.material ? (
|
node.geometry,
|
||||||
<Suspense
|
hullBoneIndices
|
||||||
fallback={
|
);
|
||||||
// Allow the mesh to render while the texture is still loading;
|
|
||||||
// show a wireframe placeholder.
|
return (
|
||||||
<meshStandardMaterial color="gray" wireframe />
|
<mesh key={node.id} geometry={geometry} castShadow receiveShadow>
|
||||||
}
|
{node.material ? (
|
||||||
>
|
<Suspense
|
||||||
{Array.isArray(node.material) ? (
|
fallback={
|
||||||
node.material.map((mat, index) => (
|
// Allow the mesh to render while the texture is still loading;
|
||||||
<ShapeTexture key={index} materialName={mat.name} />
|
// show a wireframe placeholder.
|
||||||
))
|
<meshStandardMaterial color="gray" wireframe />
|
||||||
) : (
|
}
|
||||||
<ShapeTexture materialName={node.material.name} />
|
>
|
||||||
)}
|
{Array.isArray(node.material) ? (
|
||||||
</Suspense>
|
node.material.map((mat, index) => (
|
||||||
) : null}
|
<ShapeTexture
|
||||||
</mesh>
|
key={index}
|
||||||
))}
|
material={mat as MeshStandardMaterial}
|
||||||
|
/>
|
||||||
|
))
|
||||||
|
) : (
|
||||||
|
<ShapeTexture
|
||||||
|
material={node.material as MeshStandardMaterial}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</Suspense>
|
||||||
|
) : null}
|
||||||
|
</mesh>
|
||||||
|
);
|
||||||
|
})}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
81
src/meshUtils.ts
Normal file
81
src/meshUtils.ts
Normal file
|
|
@ -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<number> {
|
||||||
|
const hullBoneIndices = new Set<number>();
|
||||||
|
|
||||||
|
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<number>
|
||||||
|
): 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;
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue