import { useMemo } from "react"; import { Color, DoubleSide, ShaderMaterial, Vector3, MeshStandardMaterial } from "three"; import useShaderReader from "../../../utils/scene/useShaderReader"; import edgeVertex from "../../../assets/shaders/edge/edge-fade.vert.glsl"; import edgeFragment from "../../../assets/shaders/edge/edge-fade.frag.glsl"; type PolygonMaterialProps = { points: { position: [number, number, number] }[]; color: string; fadeDistance?: number; variant?: "edge" | "none"; }; function PolygonMaterial({ points, color, fadeDistance = 2.0, variant = "edge", ...props }: Readonly) { const vertexShader = useShaderReader(edgeVertex); const fragmentShader = useShaderReader(edgeFragment); const edges = useMemo(() => { return points.map((p, i) => { const next = points[(i + 1) % points.length]; return [new Vector3(...p.position), new Vector3(...next.position)]; }); }, [points]); const edgeMaterial = useMemo(() => { if (!vertexShader || !fragmentShader || variant !== "edge") return null; const edgeArray: number[] = []; edges.forEach(([a, b]) => { edgeArray.push(a.x, a.z, b.x, b.z); }); return new ShaderMaterial({ side: DoubleSide, vertexShader, fragmentShader, uniforms: { uColor: { value: new Color(color) }, uFadeDistance: { value: fadeDistance }, uEdges: { value: edgeArray }, uEdgeCount: { value: edges.length }, }, transparent: true, depthWrite: false, depthTest: true, polygonOffset: true, polygonOffsetFactor: -10, polygonOffsetUnits: -10, ...props, }); }, [color, fadeDistance, vertexShader, fragmentShader, edges]); const basicMaterial = useMemo(() => { if (variant !== "none") return null; return new MeshStandardMaterial({ color: new Color(color), side: DoubleSide, transparent: true, }); }, [color]); if (variant === "none" && basicMaterial) return ; if (variant === "edge" && edgeMaterial) return ; return null; } export default PolygonMaterial;