style: format with prettier

This commit is contained in:
neru
2026-01-02 00:40:01 -03:00
parent 76209cfcd7
commit 8b1706b8fe
+153 -52
View File
@@ -4,7 +4,17 @@ import { OrbitControls, PerspectiveCamera } from '@react-three/drei';
import { Canvas, useLoader, useFrame } from '@react-three/fiber'; import { Canvas, useLoader, useFrame } from '@react-three/fiber';
import { forwardRef, useLayoutEffect, useMemo, useRef } from 'react'; import { forwardRef, useLayoutEffect, useMemo, useRef } from 'react';
import { BufferAttribute, BufferGeometry, Mesh, Object3D, InstancedMesh, DoubleSide, TextureLoader, Color, MeshStandardMaterial } from 'three'; import {
BufferAttribute,
BufferGeometry,
Mesh,
Object3D,
InstancedMesh,
DoubleSide,
TextureLoader,
Color,
MeshStandardMaterial
} from 'three';
import './page.css'; import './page.css';
import { createNoise2D } from 'simplex-noise'; import { createNoise2D } from 'simplex-noise';
@@ -12,7 +22,12 @@ import { useControls } from 'leva';
import grassVert from './shaders/grass.vert'; import grassVert from './shaders/grass.vert';
import grassFrag from './shaders/grass.frag'; import grassFrag from './shaders/grass.frag';
import { Bloom, EffectComposer, Noise, Pixelation } from '@react-three/postprocessing'; import {
Bloom,
EffectComposer,
Noise,
Pixelation
} from '@react-three/postprocessing';
interface Shader { interface Shader {
uniforms: { [key: string]: { value: unknown } }; uniforms: { [key: string]: { value: unknown } };
@@ -35,8 +50,10 @@ function getTerrainHeight(
const worldX = (worldXBase + localX) * 0.1; const worldX = (worldXBase + localX) * 0.1;
const worldZ = (worldZBase + localZ) * 0.1; const worldZ = (worldZBase + localZ) * 0.1;
const noiseHill = noise2D(worldX * hillScale, worldZ * hillScale) * hillHeight; const noiseHill =
const noiseDetail = noise2D(worldX * detailScale, worldZ * detailScale) * detailHeight; noise2D(worldX * hillScale, worldZ * hillScale) * hillHeight;
const noiseDetail =
noise2D(worldX * detailScale, worldZ * detailScale) * detailHeight;
return (noiseHill + noiseDetail) * scale; return (noiseHill + noiseDetail) * scale;
} }
@@ -56,17 +73,39 @@ interface GrassProps {
enableShadows?: boolean; enableShadows?: boolean;
} }
function Grass({ x, y, size, count, grassSize, scale, hillScale, hillHeight, detailScale, detailHeight, noise2D }: GrassProps) { function Grass({
x,
y,
size,
count,
grassSize,
scale,
hillScale,
hillHeight,
detailScale,
detailHeight,
noise2D
}: GrassProps) {
const meshRef = useRef<InstancedMesh>(null); const meshRef = useRef<InstancedMesh>(null);
const dummyRef = useRef<Object3D>(new Object3D()); const dummyRef = useRef<Object3D>(new Object3D());
const [alphaMap, normalMap] = useLoader(TextureLoader, ['/img/grass_alpha.png', '/img/grass_normal.png']); const [alphaMap, normalMap] = useLoader(TextureLoader, [
'/img/grass_alpha.png',
'/img/grass_normal.png'
]);
const materialRef = useRef<MeshStandardMaterial & { userData: { shader: Shader } }>(null); const materialRef = useRef<
MeshStandardMaterial & { userData: { shader: Shader } }
>(null);
useFrame((state) => { useFrame((state) => {
if (materialRef.current && materialRef.current.userData && materialRef.current.userData.shader) { if (
(materialRef.current.userData.shader as Shader).uniforms.uTime.value = state.clock.getElapsedTime(); materialRef.current &&
materialRef.current.userData &&
materialRef.current.userData.shader
) {
(materialRef.current.userData.shader as Shader).uniforms.uTime.value =
state.clock.getElapsedTime();
} }
}); });
@@ -77,21 +116,40 @@ function Grass({ x, y, size, count, grassSize, scale, hillScale, hillHeight, det
const h = 2; const h = 2;
const positions = [ const positions = [
-w, 0, 0, w, 0, 0, -w, h, 0, w, h, 0, -w,
0, 0, w, 0, 0, -w, 0, h, w, 0, h, -w 0,
0,
w,
0,
0,
-w,
h,
0,
w,
h,
0,
0,
0,
w,
0,
0,
-w,
0,
h,
w,
0,
h,
-w
]; ];
const uvs = [ const uvs = [0, 0, 1, 0, 0, 1, 1, 1, 0, 0, 1, 0, 0, 1, 1, 1];
0, 0, 1, 0, 0, 1, 1, 1,
0, 0, 1, 0, 0, 1, 1, 1
];
const indices = [ const indices = [0, 1, 2, 2, 1, 3, 4, 5, 6, 6, 5, 7];
0, 1, 2, 2, 1, 3,
4, 5, 6, 6, 5, 7
];
geo.setAttribute('position', new BufferAttribute(new Float32Array(positions), 3)); geo.setAttribute(
'position',
new BufferAttribute(new Float32Array(positions), 3)
);
geo.setAttribute('uv', new BufferAttribute(new Float32Array(uvs), 2)); geo.setAttribute('uv', new BufferAttribute(new Float32Array(uvs), 2));
geo.setIndex(indices); geo.setIndex(indices);
geo.computeVertexNormals(); geo.computeVertexNormals();
@@ -133,7 +191,7 @@ function Grass({ x, y, size, count, grassSize, scale, hillScale, hillHeight, det
dummy.rotation.x = (Math.random() - 0.5) * 0.2; dummy.rotation.x = (Math.random() - 0.5) * 0.2;
dummy.rotation.z = (Math.random() - 0.5) * 0.2; dummy.rotation.z = (Math.random() - 0.5) * 0.2;
const baseScale = grassSize + (Math.random() * grassSize * 0.5); const baseScale = grassSize + Math.random() * grassSize * 0.5;
const heightMult = 0.5 + Math.random() * 1.0; const heightMult = 0.5 + Math.random() * 1.0;
dummy.scale.set(baseScale, baseScale * heightMult, baseScale); dummy.scale.set(baseScale, baseScale * heightMult, baseScale);
@@ -154,10 +212,24 @@ function Grass({ x, y, size, count, grassSize, scale, hillScale, hillHeight, det
meshRef.current.setColorAt(i, color); meshRef.current.setColorAt(i, color);
} }
meshRef.current.instanceMatrix.needsUpdate = true; meshRef.current.instanceMatrix.needsUpdate = true;
if (meshRef.current.instanceColor) meshRef.current.instanceColor.needsUpdate = true; if (meshRef.current.instanceColor)
}, [x, y, size, count, grassSize, scale, hillScale, hillHeight, detailScale, detailHeight, noise2D]); meshRef.current.instanceColor.needsUpdate = true;
}, [
x,
y,
size,
count,
grassSize,
scale,
hillScale,
hillHeight,
detailScale,
detailHeight,
noise2D
]);
const onBeforeCompile = useMemo(() => (shader: Shader) => { const onBeforeCompile = useMemo(
() => (shader: Shader) => {
shader.uniforms.uTime = { value: 0 }; shader.uniforms.uTime = { value: 0 };
shader.vertexShader = ` shader.vertexShader = `
@@ -222,7 +294,9 @@ function Grass({ x, y, size, count, grassSize, scale, hillScale, hillHeight, det
if (materialRef.current) { if (materialRef.current) {
materialRef.current.userData.shader = shader; materialRef.current.userData.shader = shader;
} }
}, []); },
[]
);
return ( return (
<instancedMesh <instancedMesh
@@ -233,7 +307,7 @@ function Grass({ x, y, size, count, grassSize, scale, hillScale, hillHeight, det
> >
<meshStandardMaterial <meshStandardMaterial
ref={materialRef} ref={materialRef}
color="#ffffff" color='#ffffff'
side={DoubleSide} side={DoubleSide}
alphaMap={alphaMap} alphaMap={alphaMap}
alphaTest={0.5} alphaTest={0.5}
@@ -247,22 +321,37 @@ function Grass({ x, y, size, count, grassSize, scale, hillScale, hillHeight, det
} }
interface TerrainChunkProps { interface TerrainChunkProps {
x: number, x: number;
y: number, y: number;
size: number, size: number;
resolution: number, resolution: number;
scale: number, scale: number;
hillScale: number, hillScale: number;
hillHeight: number, hillHeight: number;
detailScale: number, detailScale: number;
detailHeight: number, detailHeight: number;
noise2D: (x: number, y: number) => number; noise2D: (x: number, y: number) => number;
wireframe?: boolean; wireframe?: boolean;
grassCount: number; grassCount: number;
grassSize: number; grassSize: number;
grassLOD: number; grassLOD: number;
} }
function TerrainChunk({ x, y, size, resolution, scale, hillScale, hillHeight, detailScale, detailHeight, noise2D, wireframe = false, grassCount, grassSize, grassLOD }: TerrainChunkProps) { function TerrainChunk({
x,
y,
size,
resolution,
scale,
hillScale,
hillHeight,
detailScale,
detailHeight,
noise2D,
wireframe = false,
grassCount,
grassSize,
grassLOD
}: TerrainChunkProps) {
const distance = Math.sqrt((x * size) ** 2 + (y * size) ** 2); const distance = Math.sqrt((x * size) ** 2 + (y * size) ** 2);
let adjustedGrassCount = grassCount; let adjustedGrassCount = grassCount;
@@ -271,7 +360,7 @@ function TerrainChunk({ x, y, size, resolution, scale, hillScale, hillHeight, de
} else if (distance > grassLOD * 0.6) { } else if (distance > grassLOD * 0.6) {
const fadeStart = grassLOD * 0.6; const fadeStart = grassLOD * 0.6;
const fadeRange = grassLOD * 0.4; const fadeRange = grassLOD * 0.4;
const fadeFactor = 1.0 - ((distance - fadeStart) / fadeRange); const fadeFactor = 1.0 - (distance - fadeStart) / fadeRange;
adjustedGrassCount = Math.floor(grassCount * fadeFactor * fadeFactor); adjustedGrassCount = Math.floor(grassCount * fadeFactor * fadeFactor);
} }
const meshRef = useRef<Mesh>(null); const meshRef = useRef<Mesh>(null);
@@ -326,7 +415,10 @@ function TerrainChunk({ x, y, size, resolution, scale, hillScale, hillHeight, de
indices.push(topRight, bottomLeft, bottomRight); indices.push(topRight, bottomLeft, bottomRight);
} }
} }
geo.setAttribute('position', new BufferAttribute(new Float32Array(vertices), 3)); geo.setAttribute(
'position',
new BufferAttribute(new Float32Array(vertices), 3)
);
const colors: Array<number> = []; const colors: Array<number> = [];
for (let iz = 0; iz < resolution; iz++) { for (let iz = 0; iz < resolution; iz++) {
@@ -354,7 +446,18 @@ function TerrainChunk({ x, y, size, resolution, scale, hillScale, hillHeight, de
geo.computeVertexNormals(); geo.computeVertexNormals();
return geo; return geo;
}, [x, y, size, resolution, scale, hillScale, hillHeight, detailScale, detailHeight, noise2D]); }, [
x,
y,
size,
resolution,
scale,
hillScale,
hillHeight,
detailScale,
detailHeight,
noise2D
]);
return ( return (
<group> <group>
@@ -388,7 +491,7 @@ function TerrainChunk({ x, y, size, resolution, scale, hillScale, hillHeight, de
/> />
)} )}
</group> </group>
) );
} }
interface TerrainProps { interface TerrainProps {
@@ -396,10 +499,10 @@ interface TerrainProps {
chunkSize?: number; chunkSize?: number;
resolution?: number; resolution?: number;
scale?: number; scale?: number;
hillScale: number, hillScale: number;
hillHeight: number, hillHeight: number;
detailScale: number, detailScale: number;
detailHeight: number, detailHeight: number;
wireframe?: boolean; wireframe?: boolean;
grassCount?: number; grassCount?: number;
grassSize?: number; grassSize?: number;
@@ -425,8 +528,7 @@ function Terrain({
const chunkPositions = useMemo(() => { const chunkPositions = useMemo(() => {
const positions: [number, number][] = []; const positions: [number, number][] = [];
for (let x = 0; x < chunks; x++) for (let x = 0; x < chunks; x++)
for (let y = 0; y < chunks; y++) for (let y = 0; y < chunks; y++) positions.push([x + offset, y + offset]);
positions.push([x + offset, y + offset]);
return positions; return positions;
}, [chunks, offset]); }, [chunks, offset]);
@@ -502,17 +604,16 @@ export default function Home() {
> >
<EffectComposer> <EffectComposer>
<Noise opacity={0.1} /> <Noise opacity={0.1} />
<Bloom intensity={2} <Bloom
intensity={2}
luminanceThreshold={0.5} luminanceThreshold={0.5}
luminanceSmoothing={0.1} /> luminanceSmoothing={0.1}
/>
<Pixelation /> <Pixelation />
</EffectComposer> </EffectComposer>
<ambientLight intensity={0.5} /> <ambientLight intensity={0.5} />
<directionalLight <directionalLight position={[10, 20, 5]} intensity={1} />
position={[10, 20, 5]}
intensity={1}
/>
<Terrain <Terrain
chunks={8.0} chunks={8.0}
chunkSize={10.0} chunkSize={10.0}