52 lines
1.3 KiB
TypeScript
52 lines
1.3 KiB
TypeScript
'use client'
|
|
|
|
import { useEffect, useMemo } from 'react'
|
|
import type { Material, MeshStandardMaterial } from 'three'
|
|
import { getGrassVariant } from './grass-geometry'
|
|
import type { GrassNode } from './grass-schema'
|
|
|
|
const NO_RAYCAST = () => {}
|
|
|
|
/** Translucent placement ghost for a grass tuft — clones the variant materials
|
|
* so the cursor preview is see-through without mutating the cached originals. */
|
|
export default function GrassPreview({ node }: { node: GrassNode }) {
|
|
const data = useMemo(() => getGrassVariant(node), [node])
|
|
const scale = node.height / data.naturalHeight
|
|
|
|
const ghosts = useMemo(
|
|
() =>
|
|
data.subMeshes.map((sub) => {
|
|
const base = (
|
|
Array.isArray(sub.material) ? sub.material[0] : sub.material
|
|
) as MeshStandardMaterial
|
|
const clone = base.clone()
|
|
clone.transparent = true
|
|
clone.opacity = 0.55
|
|
clone.depthWrite = false
|
|
return clone
|
|
}),
|
|
[data],
|
|
)
|
|
|
|
useEffect(
|
|
() => () => {
|
|
for (const m of ghosts as Material[]) m.dispose()
|
|
},
|
|
[ghosts],
|
|
)
|
|
|
|
return (
|
|
<group scale={scale}>
|
|
{data.subMeshes.map((sub, i) => (
|
|
<mesh
|
|
dispose={null}
|
|
geometry={sub.geometry}
|
|
key={i}
|
|
material={ghosts[i]}
|
|
raycast={NO_RAYCAST}
|
|
/>
|
|
))}
|
|
</group>
|
|
)
|
|
}
|