init
This commit is contained in:
195
src/VegetationSystem.js
Normal file
195
src/VegetationSystem.js
Normal file
@@ -0,0 +1,195 @@
|
||||
import * as THREE from 'three';
|
||||
import { SimplexNoise } from './utils/SimplexNoise.js';
|
||||
|
||||
export class VegetationSystem {
|
||||
constructor(terrain, options = {}) {
|
||||
this.terrain = terrain;
|
||||
this.options = {
|
||||
grassCount: options.grassCount || 50000,
|
||||
treeCount: options.treeCount || 500,
|
||||
terrainSize: options.terrainSize || 1000,
|
||||
waterLevel: options.waterLevel || 0
|
||||
};
|
||||
|
||||
this.noise = new SimplexNoise(12345);
|
||||
this.grass = null;
|
||||
this.trees = [];
|
||||
}
|
||||
|
||||
generate() {
|
||||
this.generateGrass();
|
||||
this.generateTrees();
|
||||
|
||||
return {
|
||||
grass: this.grass,
|
||||
trees: this.trees
|
||||
};
|
||||
}
|
||||
|
||||
generateGrass() {
|
||||
const geometry = new THREE.BufferGeometry();
|
||||
const positions = [];
|
||||
const colors = [];
|
||||
const uvs = [];
|
||||
const indices = [];
|
||||
|
||||
const grassBladeHeight = 0.8;
|
||||
const grassBladeWidth = 0.1;
|
||||
|
||||
let vertexIndex = 0;
|
||||
|
||||
for (let i = 0; i < this.options.grassCount; i++) {
|
||||
const x = (Math.random() - 0.5) * this.options.terrainSize * 0.7;
|
||||
const z = (Math.random() - 0.5) * this.options.terrainSize * 0.7;
|
||||
const y = this.terrain.getHeightAt(x, z);
|
||||
|
||||
if (y < this.options.waterLevel + 0.5 || y > this.options.waterLevel + 10) continue;
|
||||
|
||||
const bendX = this.noise.noise2D(x * 0.1, z * 0.1) * 0.3;
|
||||
const bendZ = this.noise.noise2D(x * 0.1 + 100, z * 0.1) * 0.3;
|
||||
|
||||
positions.push(
|
||||
x - grassBladeWidth / 2, y, z,
|
||||
x + grassBladeWidth / 2, y, z,
|
||||
x + bendX + grassBladeWidth / 4, y + grassBladeHeight, z + bendZ
|
||||
);
|
||||
|
||||
const greenVariation = 0.7 + Math.random() * 0.3;
|
||||
const baseColor = new THREE.Color().setHSL(0.3, 0.6 * greenVariation, 0.25 * greenVariation);
|
||||
|
||||
colors.push(
|
||||
baseColor.r, baseColor.g, baseColor.b,
|
||||
baseColor.r * 0.9, baseColor.g * 0.9, baseColor.b * 0.9,
|
||||
baseColor.r * 0.8, baseColor.g * 0.8, baseColor.b * 0.8
|
||||
);
|
||||
|
||||
uvs.push(0, 0, 1, 0, 0.5, 1);
|
||||
|
||||
indices.push(
|
||||
vertexIndex, vertexIndex + 1, vertexIndex + 2,
|
||||
vertexIndex + 2, vertexIndex + 1, vertexIndex
|
||||
);
|
||||
|
||||
vertexIndex += 3;
|
||||
}
|
||||
|
||||
geometry.setAttribute('position', new THREE.Float32BufferAttribute(positions, 3));
|
||||
geometry.setAttribute('color', new THREE.Float32BufferAttribute(colors, 3));
|
||||
geometry.setAttribute('uv', new THREE.Float32BufferAttribute(uvs, 2));
|
||||
geometry.setIndex(indices);
|
||||
geometry.computeVertexNormals();
|
||||
|
||||
const material = new THREE.MeshStandardMaterial({
|
||||
vertexColors: true,
|
||||
side: THREE.DoubleSide,
|
||||
roughness: 0.9,
|
||||
metalness: 0.0
|
||||
});
|
||||
|
||||
this.grass = new THREE.Mesh(geometry, material);
|
||||
this.grass.castShadow = true;
|
||||
this.grass.receiveShadow = true;
|
||||
}
|
||||
|
||||
generateTrees() {
|
||||
const treePositions = [];
|
||||
|
||||
for (let i = 0; i < this.options.treeCount * 10; i++) {
|
||||
if (treePositions.length >= this.options.treeCount) break;
|
||||
|
||||
const x = (Math.random() - 0.5) * this.options.terrainSize * 0.6;
|
||||
const z = (Math.random() - 0.5) * this.options.terrainSize * 0.6;
|
||||
const y = this.terrain.getHeightAt(x, z);
|
||||
|
||||
if (y < this.options.waterLevel + 1 || y > this.options.waterLevel + 10) continue;
|
||||
|
||||
const densityNoise = this.noise.noise2D(x * 0.005, z * 0.005);
|
||||
if (densityNoise < 0.3) continue;
|
||||
|
||||
let tooClose = false;
|
||||
for (const pos of treePositions) {
|
||||
const dist = Math.sqrt((pos.x - x) ** 2 + (pos.z - z) ** 2);
|
||||
if (dist < 10) {
|
||||
tooClose = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!tooClose) {
|
||||
treePositions.push({ x, y, z });
|
||||
}
|
||||
}
|
||||
|
||||
for (const pos of treePositions) {
|
||||
const tree = this.createTree(pos);
|
||||
this.trees.push(tree);
|
||||
}
|
||||
}
|
||||
|
||||
createTree(pos) {
|
||||
const tree = new THREE.Group();
|
||||
|
||||
const trunkHeight = 5 + Math.random() * 5;
|
||||
const trunkRadius = 0.3 + Math.random() * 0.2;
|
||||
|
||||
const trunkGeometry = new THREE.CylinderGeometry(
|
||||
trunkRadius * 0.7,
|
||||
trunkRadius,
|
||||
trunkHeight,
|
||||
8
|
||||
);
|
||||
const trunkMaterial = new THREE.MeshStandardMaterial({
|
||||
color: 0x4a3728,
|
||||
roughness: 0.9,
|
||||
metalness: 0.0
|
||||
});
|
||||
const trunk = new THREE.Mesh(trunkGeometry, trunkMaterial);
|
||||
trunk.position.y = trunkHeight / 2;
|
||||
trunk.castShadow = true;
|
||||
trunk.receiveShadow = true;
|
||||
tree.add(trunk);
|
||||
|
||||
const foliageLayers = 3 + Math.floor(Math.random() * 2);
|
||||
let foliageY = trunkHeight;
|
||||
|
||||
for (let i = 0; i < foliageLayers; i++) {
|
||||
const foliageRadius = (2.5 - i * 0.4) + Math.random() * 0.5;
|
||||
const foliageHeight = 2 + Math.random() * 1;
|
||||
|
||||
const foliageGeometry = new THREE.ConeGeometry(
|
||||
foliageRadius,
|
||||
foliageHeight,
|
||||
8
|
||||
);
|
||||
|
||||
const greenVariation = 0.8 + Math.random() * 0.2;
|
||||
const foliageMaterial = new THREE.MeshStandardMaterial({
|
||||
color: new THREE.Color().setHSL(0.28 + Math.random() * 0.05, 0.5 * greenVariation, 0.2 * greenVariation),
|
||||
roughness: 0.8,
|
||||
metalness: 0.0
|
||||
});
|
||||
|
||||
const foliage = new THREE.Mesh(foliageGeometry, foliageMaterial);
|
||||
foliage.position.y = foliageY;
|
||||
foliage.castShadow = true;
|
||||
foliage.receiveShadow = true;
|
||||
tree.add(foliage);
|
||||
|
||||
foliageY += foliageHeight * 0.6;
|
||||
}
|
||||
|
||||
tree.position.set(pos.x, pos.y, pos.z);
|
||||
|
||||
const scale = 0.8 + Math.random() * 0.4;
|
||||
tree.scale.setScalar(scale);
|
||||
|
||||
tree.rotation.y = Math.random() * Math.PI * 2;
|
||||
|
||||
return tree;
|
||||
}
|
||||
|
||||
addToScene(scene) {
|
||||
if (this.grass) scene.add(this.grass);
|
||||
this.trees.forEach(tree => scene.add(tree));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user