Skip to content

Commit d92d59c

Browse files
author
Alexander Dürrhauer
committed
fix: render grass as tapered ribbons
1 parent 56c847f commit d92d59c

2 files changed

Lines changed: 49 additions & 4 deletions

File tree

src/grass-geometry.test.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import { describe, expect, test } from 'bun:test'
2+
import { getGrassVariant } from './grass-geometry'
3+
4+
const CASES = [
5+
{ bladeCount: 10, preset: 'meadow' as const, seed: 1 },
6+
{ bladeCount: 8, preset: 'fescue' as const, seed: 7 },
7+
{ bladeCount: 6, preset: 'reed' as const, seed: 13 },
8+
]
9+
10+
describe('grass geometry', () => {
11+
test.each(CASES)('$preset uses one flat two-triangle ribbon per blade', ({ bladeCount, preset, seed }) => {
12+
const variant = getGrassVariant({ bladeColor: '#5a8f3c', preset, seed } as never)
13+
expect(variant.subMeshes).toHaveLength(1)
14+
15+
const geometry = variant.subMeshes[0]!.geometry
16+
expect(geometry.attributes.position!.count).toBe(bladeCount * 4)
17+
expect(geometry.index?.count).toBe(bladeCount * 6)
18+
})
19+
})

src/grass-geometry.ts

Lines changed: 30 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { type BufferGeometry, ConeGeometry, DoubleSide, Group, Mesh } from 'three'
1+
import { BufferGeometry, DoubleSide, Float32BufferAttribute, Group, Mesh } from 'three'
22
import { mergeGeometries } from 'three/examples/jsm/utils/BufferGeometryUtils.js'
33
import { GRASS_PRESETS } from './grass-presets'
44
import type { GrassNode, GrassPreset } from './grass-schema'
@@ -40,9 +40,35 @@ function buildGrass(preset: GrassPreset, seed: number, bladeColor: string): Grou
4040
const blades: BufferGeometry[] = []
4141
for (let i = 0; i < spec.blades; i++) {
4242
const bh = h * (0.6 + rng() * 0.6)
43-
const blade = new ConeGeometry(0.02, bh, 3)
44-
blade.scale(1, 1, 0.3) // flatten the cone into a blade
45-
blade.translate(0, bh / 2, 0)
43+
const halfBaseWidth = 0.006
44+
const halfTipWidth = 0.0006
45+
const blade = new BufferGeometry()
46+
blade.setAttribute(
47+
'position',
48+
new Float32BufferAttribute(
49+
[
50+
-halfBaseWidth,
51+
0,
52+
0,
53+
halfBaseWidth,
54+
0,
55+
0,
56+
halfTipWidth,
57+
bh,
58+
0,
59+
-halfTipWidth,
60+
bh,
61+
0,
62+
],
63+
3,
64+
),
65+
)
66+
blade.setAttribute(
67+
'uv',
68+
new Float32BufferAttribute([0, 0, 1, 0, 0.55, 1, 0.45, 1], 2),
69+
)
70+
blade.setIndex([0, 1, 2, 0, 2, 3])
71+
blade.computeVertexNormals()
4672
blade.rotateZ((rng() - 0.5) * 0.7) // lean
4773
const angle = rng() * Math.PI * 2
4874
blade.rotateY(angle)

0 commit comments

Comments
 (0)