70 lines
2.4 KiB
TypeScript
70 lines
2.4 KiB
TypeScript
|
|
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<PolygonMaterialProps>) {
|
||
|
|
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 <primitive object={basicMaterial} attach="material" />;
|
||
|
|
if (variant === "edge" && edgeMaterial) return <primitive object={edgeMaterial} attach="material" />;
|
||
|
|
|
||
|
|
return null;
|
||
|
|
}
|
||
|
|
|
||
|
|
export default PolygonMaterial;
|