SurfaceTool 实体

同一个实体用 SurfaceTool 提交两次,一个硬边一个平滑。区别只在平滑组。

上方的编辑器正在运行这个项目。改一行代码,它就会重新加载。

用到的 Godot 类

SurfaceToolArrayMeshLabel3D

代码

scripts/main.gd
extends Node

const SEGMENTS: int = 14
const TOP: Vector3 = Vector3(0, 1.5, 0)
const BOTTOM: Vector3 = Vector3(0, -1.5, 0)
const UPPER: float = 0.55
const LOWER: float = -0.2

var solids: Array[MeshInstance3D] = []


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

	for entry in [{smooth = false, at = -1.7, label = 'set_smooth_group(-1)'}, {smooth = true, at = 1.7, label = 'set_smooth_group(0)'}]:
		var solid: MeshInstance3D = MeshInstance3D.new()
		solid.mesh = _make_gem(entry.smooth)
		solid.position = Vector3(entry.at, 0, 0)
		add_child(solid)
		solids.append(solid)

		var caption: Label3D = Label3D.new()
		caption.text = entry.label
		caption.pixel_size = 0.009
		caption.modulate = Color('#dfe6ff')
		caption.position = Vector3(entry.at, -2.1, 0)
		add_child(caption)

	var camera: Camera3D = Camera3D.new()
	camera.position = Vector3(0, 1.5, 5.6)
	add_child(camera)
	camera.look_at(Vector3(0, -0.1, 0), Vector3.UP)
	camera.current = true

	print('same vertices, same generate_normals call, only the smooth group changes')


func _process(delta: float) -> void:
	for solid in solids:
		solid.rotate_y(delta * 0.5)


func _make_gem(smooth: bool) -> ArrayMesh:
	var tool: SurfaceTool = SurfaceTool.new()
	tool.begin(Mesh.PRIMITIVE_TRIANGLES)
	tool.set_smooth_group(0 if smooth else -1)

	for i in SEGMENTS:
		var upper_a: Vector3 = _ring(i, 0.95, UPPER)
		var upper_b: Vector3 = _ring(i + 1, 0.95, UPPER)
		var lower_a: Vector3 = _ring(i, 1.2, LOWER)
		var lower_b: Vector3 = _ring(i + 1, 1.2, LOWER)

		for vertex in [TOP, upper_a, upper_b, upper_a, lower_a, lower_b, upper_a, lower_b, upper_b, BOTTOM, lower_b, lower_a]:
			tool.add_vertex(vertex)

	tool.generate_normals()
	tool.set_material(_make_material())

	return tool.commit()


func _ring(index: int, radius: float, height: float) -> Vector3:
	var angle: float = TAU * index / SEGMENTS

	return Vector3(cos(angle) * radius, height, sin(angle) * radius)


func _make_material() -> StandardMaterial3D:
	var material: StandardMaterial3D = StandardMaterial3D.new()
	material.albedo_color = Color('#8da5f3')
	material.metallic = 0.35
	material.roughness = 0.28

	return material


func _add_environment() -> void:
	var sky_material: ProceduralSkyMaterial = ProceduralSkyMaterial.new()
	sky_material.sky_top_color = Color(0.04, 0.05, 0.1)
	sky_material.sky_horizon_color = Color(0.12, 0.14, 0.22)
	sky_material.ground_bottom_color = Color(0.02, 0.02, 0.04)
	sky_material.ground_horizon_color = Color(0.09, 0.1, 0.15)

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

	var env: Environment = Environment.new()
	env.background_mode = Environment.BG_SKY
	env.sky = sky
	# a sky ambient leaves the faces under the gem unlit against a black ground
	env.ambient_light_source = Environment.AMBIENT_SOURCE_COLOR
	env.ambient_light_color = Color('#3b4560')
	env.ambient_light_energy = 1.1

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


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

更多程序化生成示例