[READ-ONLY] Mirror of https://github.com/flo-bit/shapecraft. flo-bit.dev/shapecraft/
0

Configure Feed

Select the types of activity you want to include in your feed.

3.0 kB 65 lines
1import { merge } from '../../../ops' 2import { setup, blade, heightShade } from '../../../build' 3import type { Mesh } from '../../../core/mesh' 4import type { Vec3 } from '../../../core/types' 5import type { OptionSchema, OptionInput } from '../../../core/schema' 6 7export const grassSchema = { 8 seed: { type: 'integer', default: 1, min: 1, max: 100, label: 'Seed' }, 9 blades: { type: 'integer', default: 16, min: 3, max: 60, label: 'Blades' }, 10 height: { type: 'range', default: 0.6, min: 0.15, max: 1.6, step: 0.05, label: 'Height' }, 11 bladeWidth: { type: 'range', default: 0.035,min: 0.01, max: 0.1, step: 0.005, label: 'Blade Width' }, 12 spread: { type: 'range', default: 0.1, min: 0, max: 0.4, step: 0.01, label: 'Base Spread' }, 13 fan: { type: 'range', default: 0.25, min: 0, max: 0.6, step: 0.02, label: 'Tip Fan' }, 14 curve: { type: 'range', default: 0.8, min: 0, max: 1.5, step: 0.05, label: 'Curve' }, 15 segments: { type: 'integer', default: 4, min: 2, max: 8, label: 'Segments' }, 16 colors: { type: 'color-array', default: ['#3a6a1e', '#4e8a28', '#7ab33e'], min: 1, max: 6, label: 'Colors' }, 17} satisfies OptionSchema 18 19export type GrassOptions = Partial<OptionInput<typeof grassSchema>> & { preset?: string } 20 21export const grassPresets: Record<string, Partial<GrassOptions>> = { 22 default: {}, 23 tall: { height: 1.2, blades: 22, curve: 0.5 }, 24 dry: { colors: ['#7a6a28', '#9a8a3a', '#b7a64a'], curve: 1.0 }, 25 lush: { blades: 30, colors: ['#2f6a1e', '#3f8a26', '#62ad36'] }, 26} 27 28export function grass(options: GrassOptions = {}): Mesh { 29 const { o, rng } = setup(grassSchema, options, grassPresets) 30 const shapeRng = rng.stream('shape') 31 32 const blades: Mesh[] = [] 33 for (let i = 0; i < o.blades; i++) { 34 // Base position within a small disc. 35 const baseAngle = shapeRng() * Math.PI * 2 36 const baseDist = Math.sqrt(shapeRng()) * o.spread 37 const bx = Math.cos(baseAngle) * baseDist 38 const bz = Math.sin(baseAngle) * baseDist 39 40 // Each blade arcs over in a random direction, biased outward from the clump. 41 const outAngle = baseDist > 1e-4 ? baseAngle : shapeRng() * Math.PI * 2 42 const leanAngle = outAngle + (shapeRng() - 0.5) * Math.PI * o.fan 43 const ldx = Math.cos(leanAngle) 44 const ldz = Math.sin(leanAngle) 45 46 const h = o.height * (0.7 + shapeRng() * 0.5) 47 const phiMax = Math.max(0.001, o.curve * (0.6 + shapeRng() * 0.8)) 48 const R = h / phiMax 49 const w = o.bladeWidth * (0.7 + shapeRng() * 0.5) 50 51 const segs = o.segments 52 const path: Vec3[] = [] 53 for (let j = 0; j <= segs; j++) { 54 const t = j / segs 55 const phi = phiMax * t 56 const y = R * Math.sin(phi) 57 const hd = R * (1 - Math.cos(phi)) 58 path.push([bx + ldx * hd, y, bz + ldz * hd]) 59 } 60 61 blades.push(blade(path, { width: w }).vertexColor(heightShade(o.colors, h))) 62 } 63 64 return merge(...blades) 65}