optimized zone material

This commit is contained in:
2025-09-11 11:58:12 +05:30
parent 9f386bef59
commit 6091031b9d
5 changed files with 154 additions and 138 deletions

View File

@@ -44,7 +44,7 @@ function FloorInstance({ floor }: { readonly floor: Floor }) {
textureTileScale?: [number, number];
}
> = {
"Default Material": { map: savedTheme === "dark" ? texturePathDark : texturePath, },
"Default Material": { map: savedTheme === "dark" ? texturePathDark : texturePath },
"Material 1": { map: material1 },
"Material 2": {
map: material2Map,
@@ -111,30 +111,17 @@ function FloorInstance({ floor }: { readonly floor: Floor }) {
const textureMaterialMap = [
{
textures: [
topTexture,
topNormalTexture,
topRoughnessTexture,
topMetalicTexture,
],
textures: [topTexture, topNormalTexture, topRoughnessTexture, topMetalicTexture],
materialKey: floor.topMaterial,
},
{
textures: [
sideTexture,
sideNormalTexture,
sideRoughnessTexture,
sideMetalicTexture,
],
textures: [sideTexture, sideNormalTexture, sideRoughnessTexture, sideMetalicTexture],
materialKey: floor.sideMaterial,
},
];
textureMaterialMap.forEach(({ textures, materialKey }) => {
const tileScale = materials[materialKey]?.textureTileScale ?? [
textureScale,
textureScale,
];
const tileScale = materials[materialKey]?.textureTileScale ?? [textureScale, textureScale];
textures.forEach((tex, idx) => {
if (!tex) return;
@@ -166,11 +153,7 @@ function FloorInstance({ floor }: { readonly floor: Floor }) {
geometry={geometry}
name={`Floor-${floor.floorUuid}`}
rotation={[Math.PI / 2, 0, 0]}
position={[
shapeData?.center[0] ?? 0,
!floor.isBeveled ? floor.floorDepth - 0.1 : floor.floorDepth - 0.2,
shapeData?.center[1] ?? 0,
]}
position={[shapeData?.center[0] ?? 0, !floor.isBeveled ? floor.floorDepth - 0.1 : floor.floorDepth - 0.2, shapeData?.center[1] ?? 0]}
userData={floor}
onDoubleClick={(e) => {
if (!toggleView && activeModule === "builder") {

View File

@@ -0,0 +1,59 @@
import { useMemo, useRef } from "react";
import { Color, DoubleSide, ShaderMaterial } from "three";
import { useFrame } from "@react-three/fiber";
import { useSceneStore } from "../../../store/scene/useSceneStore";
import useShaderReader from "../../../utils/scene/useShaderReader";
import zone1Vertex from "../../../assets/shaders/zone/zone1.vert.glsl";
import zone1Fragment from "../../../assets/shaders/zone/zone1.frag.glsl";
import zone2Vertex from "../../../assets/shaders/zone/zone2.vert.glsl";
import zone2Fragment from "../../../assets/shaders/zone/zone2.frag.glsl";
import zone3Vertex from "../../../assets/shaders/zone/zone3.vert.glsl";
import zone3Fragment from "../../../assets/shaders/zone/zone3.frag.glsl";
type PlaneMaterialProps = {
color: string;
variant?: "zone1" | "zone2" | "zone3";
};
function PlaneMaterial({ color, variant = "zone1", ...props }: Readonly<PlaneMaterialProps>) {
const { limitFps } = useSceneStore();
const materialRef = useRef<ShaderMaterial | null>(null);
const vertexShaderUrl = variant === "zone1" ? zone1Vertex : variant === "zone2" ? zone2Vertex : variant === "zone3" ? zone3Vertex : zone1Vertex;
const fragmentShaderUrl = variant === "zone1" ? zone1Fragment : variant === "zone2" ? zone2Fragment : variant === "zone3" ? zone3Fragment : zone1Fragment;
const vertexShader = useShaderReader(vertexShaderUrl);
const fragmentShader = useShaderReader(fragmentShaderUrl);
const material = useMemo(() => {
if (!vertexShader || !fragmentShader) return null;
return new ShaderMaterial({
side: DoubleSide,
vertexShader,
fragmentShader,
uniforms: {
uOuterColor: { value: new Color(color) },
uTime: { value: 0 },
},
transparent: true,
depthWrite: false,
...props,
});
}, [color, vertexShader, fragmentShader]);
useFrame(({ clock }) => {
if (materialRef.current && !limitFps) {
materialRef.current.uniforms.uTime.value = clock.getElapsedTime();
}
});
if (!material) return null;
return <primitive object={material} ref={materialRef} attach="material" />;
}
export default PlaneMaterial;

View File

@@ -1,75 +0,0 @@
import { useMemo } from "react";
import { Color, DoubleSide, ShaderMaterial, Shape, ShapeGeometry, Vector3 } from "three";
import useShaderReader from "../../../utils/scene/useShaderReader";
import vertexShaderUrl from "../../../assets/shaders/edge/edge-fade.vert.glsl";
import fragmentShaderUrl from "../../../assets/shaders/edge/edge-fade.frag.glsl";
type PolygonEdgeMaterialProps = {
points: { position: [number, number, number] }[];
positionY: number;
color: string;
};
function PolygonEdgeMaterial({ points, positionY, color }: Readonly<PolygonEdgeMaterialProps>) {
const vertexShader = useShaderReader(vertexShaderUrl);
const fragmentShader = useShaderReader(fragmentShaderUrl);
const geometry = useMemo(() => {
if (points.length < 3) return null;
const shape = new Shape();
points.forEach((p, i) => {
const v = new Vector3(...p.position);
if (i === 0) shape.moveTo(v.x, v.z);
else shape.lineTo(v.x, v.z);
});
shape.closePath();
return new ShapeGeometry(shape);
}, [points]);
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 material = useMemo(() => {
if (!vertexShader || !fragmentShader) 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: 2.0 },
uEdges: { value: edgeArray },
uEdgeCount: { value: edges.length },
},
transparent: true,
depthWrite: false,
depthTest: true,
polygonOffset: true,
polygonOffsetFactor: -10,
polygonOffsetUnits: -10,
});
}, [color, vertexShader, fragmentShader, edges]);
if (!geometry || !material) return null;
return (
<mesh geometry={geometry} position={[0, positionY, 0]} rotation={[Math.PI / 2, 0, 0]}>
<primitive object={material} attach="material" />
</mesh>
);
}
export default PolygonEdgeMaterial;

View File

@@ -0,0 +1,69 @@
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;

View File

@@ -1,54 +1,34 @@
import { useMemo, useState } from "react";
import { Color, DoubleSide, ShaderMaterial, Vector3 } from "three";
import { useFrame } from "@react-three/fiber";
import { useSceneStore } from "../../../../../store/scene/useSceneStore";
import useShaderReader from "../../../../../utils/scene/useShaderReader";
import { useMemo } from "react";
import { Extrude } from "@react-three/drei";
import { Shape, Vector2, Vector3 } from "three";
import CornerReference from "./cornerReference";
import PolygonEdgeMaterial from "../../../materials/polygonEdgeMaterial";
import vertexShaderUrl from "../../../../../assets/shaders/zone/zone1.vert.glsl";
import fragmentShaderUrl from "../../../../../assets/shaders/zone/zone1.frag.glsl";
// import vertexShaderUrl from "../../../../../assets/shaders/zone/zone2.vert.glsl";
// import fragmentShaderUrl from "../../../../../assets/shaders/zone/zone2.frag.glsl";
// import vertexShaderUrl from "../../../../../assets/shaders/zone/zone2.vert.glsl";
// import fragmentShaderUrl from "../../../../../assets/shaders/zone/zone2.frag.glsl";
import PlaneMaterial from "../../../materials/planeMaterial";
import PolygonMaterial from "../../../materials/polygonMaterial";
function ZoneInstance({ zone }: { readonly zone: Zone }) {
const vertexShader = useShaderReader(vertexShaderUrl);
const fragmentShader = useShaderReader(fragmentShaderUrl);
const zoneLayer = zone.points[0].layer;
const { limitFps } = useSceneStore();
const [time, setTime] = useState<number>(0);
const zoneMaterial = useMemo(() => {
if (!vertexShader || !fragmentShader) return null;
const points2D = useMemo(() => {
return zone.points.map((p) => new Vector2(parseFloat(p.position[0].toFixed(2)), parseFloat(p.position[2].toFixed(2))));
}, [zone]);
return new ShaderMaterial({
side: DoubleSide,
vertexShader,
fragmentShader,
uniforms: {
uOuterColor: { value: new Color(zone.zoneColor) },
uTime: { value: time },
},
transparent: true,
depthWrite: false,
});
}, [zone.zoneColor, time]);
useFrame(({ clock }) => {
if (!limitFps) {
setTime(clock.getElapsedTime());
const shape = useMemo(() => {
const shape = new Shape();
shape.moveTo(points2D[0].x, points2D[0].y);
for (let i = 1; i < points2D.length; i++) {
shape.lineTo(points2D[i].x, points2D[i].y);
}
});
shape.lineTo(points2D[0].x, points2D[0].y);
return shape;
}, [points2D]);
if (!zoneMaterial) return null;
if (!shape) return null;
return (
<group name={`Zone-${zone.zoneUuid}`} userData={zone}>
<PolygonEdgeMaterial points={zone.points} positionY={(zoneLayer - 1) * zone.zoneHeight} color={zone.zoneColor} />
<Extrude args={[shape, { depth: 0, bevelEnabled: false }]} rotation={[Math.PI / 2, 0, 0]}>
<PolygonMaterial points={zone.points} color={zone.zoneColor} variant="edge" />
</Extrude>
{zone.points.map((point, index: number) => {
const nextPoint = zone.points[(index + 1) % zone.points.length];
@@ -67,7 +47,7 @@ function ZoneInstance({ zone }: { readonly zone: Zone }) {
<group key={index}>
<mesh position={midpoint} rotation={[0, -angle, 0]}>
<planeGeometry args={[planeWidth, planeHeight]} />
<primitive object={zoneMaterial.clone()} attach="material" />
<PlaneMaterial color={zone.zoneColor} variant="zone1" />
</mesh>
<CornerReference showTop={false} showBottom={false} point={point} prevPoint={prevPoint} nextPoint={nextPoint} zone={zone} cornerThickness={0.25} />