Forêt en MultiMesh

Quatre mille arbres dessinés par deux nœuds MultiMeshInstance3D, avec une couleur par instance sur les frondaisons.

L'éditeur ci-dessus fait tourner ce projet. Changez une ligne et il se recharge.

Classes Godot utilisées

MultiMeshInstance3DMultiMeshCylinderMesh

Le code

scripts/main.gd
extends Node

const TREES: int = 4000
const FIELD: float = 48.0

var camera: Camera3D
var angle: float = 0.0


func _ready() -> void:
	_add_environment()
	_add_light()
	_add_ground()

	var rng: RandomNumberGenerator = RandomNumberGenerator.new()
	rng.seed = 20260814

	var spots: Array[Transform3D] = []
	var tint: Array[Color] = []

	for i in TREES:
		var turn: float = rng.randf() * TAU
		var radius: float = sqrt(rng.randf()) * FIELD
		var size: float = rng.randf_range(0.7, 1.6)
		var basis: Basis = Basis(Vector3.UP, rng.randf() * TAU).scaled(Vector3(size, size, size))

		spots.append(Transform3D(basis, Vector3(cos(turn), 0, sin(turn)) * radius))
		tint.append(Color('#8eef97').darkened(rng.randf_range(0.15, 0.6)))

	_add_layer(_trunk_mesh(), spots, 0.6, [])
	_add_layer(_crown_mesh(), spots, 2.4, tint)
	_add_counter()

	camera = Camera3D.new()
	camera.fov = 55.0
	add_child(camera)
	camera.current = true
	_place_camera()

	print('two MultiMeshInstance3D draw %d trees, and the camera keeps circling them' % TREES)


func _process(delta: float) -> void:
	angle += delta * 0.1
	_place_camera()


func _place_camera() -> void:
	camera.position = Vector3(sin(angle), 0, cos(angle)) * 56.0 + Vector3(0, 24, 0)
	camera.look_at(Vector3.ZERO, Vector3.UP)


func _add_layer(mesh: Mesh, spots: Array[Transform3D], lift: float, tint: Array[Color]) -> void:
	# use_colors and transform_format only take effect while instance_count is still zero
	var multimesh: MultiMesh = MultiMesh.new()
	multimesh.transform_format = MultiMesh.TRANSFORM_3D
	multimesh.use_colors = not tint.is_empty()
	multimesh.mesh = mesh
	multimesh.instance_count = spots.size()

	for i in spots.size():
		multimesh.set_instance_transform(i, spots[i].translated_local(Vector3(0, lift, 0)))

		if not tint.is_empty():
			multimesh.set_instance_color(i, tint[i])

	var material: StandardMaterial3D = StandardMaterial3D.new()
	material.albedo_color = Color('#8eef97') if not tint.is_empty() else Color('#4a3a2e')
	material.vertex_color_use_as_albedo = not tint.is_empty()
	material.roughness = 0.85

	var instance: MultiMeshInstance3D = MultiMeshInstance3D.new()
	instance.multimesh = multimesh
	instance.material_override = material
	add_child(instance)


func _trunk_mesh() -> CylinderMesh:
	var mesh: CylinderMesh = CylinderMesh.new()
	mesh.top_radius = 0.13
	mesh.bottom_radius = 0.19
	mesh.height = 1.2
	mesh.radial_segments = 6
	mesh.rings = 1

	return mesh


func _crown_mesh() -> CylinderMesh:
	var mesh: CylinderMesh = CylinderMesh.new()
	mesh.top_radius = 0.0
	mesh.bottom_radius = 1.0
	mesh.height = 2.2
	mesh.radial_segments = 7
	mesh.rings = 1

	return mesh


func _add_counter() -> void:
	var layer: CanvasLayer = CanvasLayer.new()
	add_child(layer)

	var counter: Label = Label.new()
	counter.text = '%d trees\n%d MultiMesh instances\n2 MeshInstance3D nodes' % [TREES, TREES * 2]
	counter.position = Vector2(28, 24)
	counter.add_theme_font_size_override('font_size', 24)
	counter.add_theme_color_override('font_color', Color('#f3d48d'))
	layer.add_child(counter)


func _add_ground() -> void:
	var mesh: PlaneMesh = PlaneMesh.new()
	mesh.size = Vector2(400, 400)

	var material: StandardMaterial3D = StandardMaterial3D.new()
	material.albedo_color = Color('#2a2f3d')
	material.roughness = 0.95

	var ground: MeshInstance3D = MeshInstance3D.new()
	ground.mesh = mesh
	ground.material_override = material
	add_child(ground)


func _add_environment() -> void:
	var sky_material: ProceduralSkyMaterial = ProceduralSkyMaterial.new()
	sky_material.sky_top_color = Color(0.08, 0.11, 0.22)
	sky_material.sky_horizon_color = Color(0.3, 0.34, 0.46)
	sky_material.ground_bottom_color = Color(0.06, 0.07, 0.11)
	sky_material.ground_horizon_color = Color(0.22, 0.25, 0.34)

	var sky: Sky = Sky.new()
	sky.sky_material = sky_material

	var env: Environment = Environment.new()
	env.background_mode = Environment.BG_SKY
	env.sky = sky
	env.ambient_light_source = Environment.AMBIENT_SOURCE_SKY
	env.ambient_light_energy = 0.8

	var world: WorldEnvironment = WorldEnvironment.new()
	world.environment = env
	add_child(world)


func _add_light() -> void:
	var light: DirectionalLight3D = DirectionalLight3D.new()
	light.rotation_degrees = Vector3(-52, -35, 0)
	light.light_energy = 1.3
	add_child(light)

Plus d'exemples de Scène 3D