95 lines
2.9 KiB
TypeScript
95 lines
2.9 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;
|
|
transparent?: boolean;
|
|
depthWrite?: boolean;
|
|
depthTest?: boolean;
|
|
opacity?: number;
|
|
polygonOffset?: boolean;
|
|
polygonOffsetFactor?: number;
|
|
polygonOffsetUnits?: number;
|
|
fadeDistance?: number;
|
|
variant?: "edge" | "none";
|
|
};
|
|
|
|
function PolygonMaterial({
|
|
points,
|
|
color = "white",
|
|
transparent = false,
|
|
depthWrite,
|
|
depthTest,
|
|
opacity = 1,
|
|
polygonOffset = false,
|
|
polygonOffsetFactor,
|
|
polygonOffsetUnits,
|
|
fadeDistance = 2.0,
|
|
variant = "none",
|
|
}: 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,
|
|
depthWrite,
|
|
depthTest,
|
|
polygonOffset,
|
|
polygonOffsetFactor,
|
|
polygonOffsetUnits,
|
|
opacity,
|
|
});
|
|
}, [color, fadeDistance, vertexShader, fragmentShader, edges]);
|
|
|
|
const basicMaterial = useMemo(() => {
|
|
if (variant !== "none") return null;
|
|
return new MeshStandardMaterial({
|
|
color: new Color(color),
|
|
side: DoubleSide,
|
|
transparent,
|
|
depthWrite,
|
|
depthTest,
|
|
polygonOffset,
|
|
polygonOffsetFactor,
|
|
polygonOffsetUnits,
|
|
opacity,
|
|
});
|
|
}, [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;
|