104 lines
3.2 KiB
TypeScript
104 lines
3.2 KiB
TypeScript
import { useRef, useMemo } from 'react'
|
|
import { useFrame, useThree } from '@react-three/fiber'
|
|
import { ShaderMaterial, Vector3, DoubleSide } from 'three'
|
|
|
|
export function WorldBorder() {
|
|
const materialRef = useRef<ShaderMaterial>(null)
|
|
const { camera } = useThree()
|
|
|
|
const uniforms = useMemo(
|
|
() => ({
|
|
uPlayerPos: { value: new Vector3() },
|
|
uVisibleDistance: { value: 200.0 },
|
|
}),
|
|
[]
|
|
)
|
|
|
|
useFrame(() => {
|
|
if (materialRef.current) {
|
|
materialRef.current.uniforms.uPlayerPos.value.copy(camera.position)
|
|
}
|
|
})
|
|
|
|
const vertexShader = `
|
|
varying vec3 vWorldPos;
|
|
void main() {
|
|
vec4 worldPosition = modelMatrix * vec4(position, 1.0);
|
|
vWorldPos = worldPosition.xyz;
|
|
gl_Position = projectionMatrix * viewMatrix * worldPosition;
|
|
}
|
|
`
|
|
|
|
const fragmentShader = `
|
|
uniform vec3 uPlayerPos;
|
|
uniform float uVisibleDistance;
|
|
varying vec3 vWorldPos;
|
|
|
|
void main() {
|
|
// Calculate distance from player to this fragment
|
|
float distToPlayer = distance(uPlayerPos, vWorldPos);
|
|
|
|
// Fade based on distance
|
|
float alpha = 1.0 - smoothstep(0.0, uVisibleDistance, distToPlayer);
|
|
|
|
if (alpha <= 0.0) discard;
|
|
|
|
gl_FragColor = vec4(1.0, 0.0, 0.0, alpha * 0.8);
|
|
}
|
|
`
|
|
|
|
const mapSize = 2500
|
|
const halfSize = mapSize / 2
|
|
const wallHeight = 100
|
|
|
|
return (
|
|
<group>
|
|
{/* North Wall (-Z) */}
|
|
<mesh position={[0, wallHeight / 2, -halfSize]}>
|
|
<planeGeometry args={[mapSize, wallHeight]} />
|
|
<shaderMaterial
|
|
ref={materialRef}
|
|
vertexShader={vertexShader}
|
|
fragmentShader={fragmentShader}
|
|
uniforms={uniforms}
|
|
transparent
|
|
side={DoubleSide}
|
|
/>
|
|
</mesh>
|
|
{/* South Wall (+Z) */}
|
|
<mesh position={[0, wallHeight / 2, halfSize]}>
|
|
<planeGeometry args={[mapSize, wallHeight]} />
|
|
<shaderMaterial
|
|
vertexShader={vertexShader}
|
|
fragmentShader={fragmentShader}
|
|
uniforms={uniforms}
|
|
transparent
|
|
side={DoubleSide}
|
|
/>
|
|
</mesh>
|
|
{/* East Wall (+X) */}
|
|
<mesh position={[halfSize, wallHeight / 2, 0]} rotation={[0, -Math.PI / 2, 0]}>
|
|
<planeGeometry args={[mapSize, wallHeight]} />
|
|
<shaderMaterial
|
|
vertexShader={vertexShader}
|
|
fragmentShader={fragmentShader}
|
|
uniforms={uniforms}
|
|
transparent
|
|
side={DoubleSide}
|
|
/>
|
|
</mesh>
|
|
{/* West Wall (-X) */}
|
|
<mesh position={[-halfSize, wallHeight / 2, 0]} rotation={[0, Math.PI / 2, 0]}>
|
|
<planeGeometry args={[mapSize, wallHeight]} />
|
|
<shaderMaterial
|
|
vertexShader={vertexShader}
|
|
fragmentShader={fragmentShader}
|
|
uniforms={uniforms}
|
|
transparent
|
|
side={DoubleSide}
|
|
/>
|
|
</mesh>
|
|
</group>
|
|
)
|
|
}
|