feat: author discoverable forest edge
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
class_name RiverbankResourceCluster
|
||||
extends Node3D
|
||||
extends TerrainResourceCluster
|
||||
|
||||
const FERN_OFFSETS := [
|
||||
Vector3(-0.8, 0.0, -10.8),
|
||||
@@ -36,75 +36,6 @@ const STONE_OFFSETS := [
|
||||
Vector3(2.7, 0.0, 11.2),
|
||||
]
|
||||
|
||||
@export var terrain_path := NodePath("../../../TerrainRoot/Terrain3D")
|
||||
|
||||
var terrain: Terrain3D
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
call_deferred("_initialize_cluster")
|
||||
|
||||
|
||||
func get_resource_anchors() -> Array[ResourceNode]:
|
||||
var anchors: Array[ResourceNode] = []
|
||||
for child in $ResourceAnchors.get_children():
|
||||
if child is ResourceNode:
|
||||
anchors.append(child)
|
||||
return anchors
|
||||
|
||||
|
||||
func get_presentation_stats() -> Dictionary:
|
||||
var understory_instances := 0
|
||||
var multimesh_batches := 0
|
||||
for child in $DecorativeInstances/Understory.get_children():
|
||||
var instance := child as MultiMeshInstance3D
|
||||
if instance == null or instance.multimesh == null:
|
||||
continue
|
||||
understory_instances += instance.multimesh.instance_count
|
||||
multimesh_batches += 1
|
||||
return {
|
||||
"understory_instance_count": understory_instances,
|
||||
"multimesh_batch_count": multimesh_batches,
|
||||
"decorative_tree_count": $DecorativeInstances/Trees.get_child_count(),
|
||||
"resource_anchor_count": get_resource_anchors().size(),
|
||||
}
|
||||
|
||||
|
||||
func _initialize_cluster() -> void:
|
||||
terrain = get_node_or_null(terrain_path) as Terrain3D
|
||||
if terrain == null:
|
||||
push_warning("RiverbankResourceCluster: Terrain3D is unavailable")
|
||||
return
|
||||
_snap_authored_children($DecorativeInstances/Trees)
|
||||
for anchor in get_resource_anchors():
|
||||
_snap_resource_anchor(anchor)
|
||||
_build_understory()
|
||||
|
||||
|
||||
func _snap_authored_children(parent: Node) -> void:
|
||||
for child in parent.get_children():
|
||||
if child is Node3D:
|
||||
_snap_to_terrain(child)
|
||||
|
||||
|
||||
func _snap_to_terrain(node: Node3D) -> void:
|
||||
var position := node.global_position
|
||||
var height := terrain.data.get_height(position)
|
||||
if not is_nan(height):
|
||||
node.global_position.y = height
|
||||
|
||||
|
||||
func _snap_resource_anchor(anchor: ResourceNode) -> void:
|
||||
var anchor_position := anchor.global_position
|
||||
var anchor_height := terrain.data.get_height(anchor_position)
|
||||
if is_nan(anchor_height):
|
||||
return
|
||||
var interaction_position := anchor.global_transform * anchor.interaction_point.position
|
||||
var interaction_height := terrain.data.get_height(interaction_position)
|
||||
if not is_nan(interaction_height):
|
||||
anchor.interaction_point.position.y = interaction_height - anchor_height + 0.05
|
||||
anchor.global_position.y = anchor_height
|
||||
|
||||
|
||||
func _build_understory() -> void:
|
||||
var fern_material := StandardMaterial3D.new()
|
||||
@@ -148,63 +79,3 @@ func _build_understory() -> void:
|
||||
Vector3(0.62, 0.24, 0.44),
|
||||
0.22
|
||||
)
|
||||
|
||||
|
||||
func _assign_multimesh(
|
||||
target: MultiMeshInstance3D,
|
||||
mesh: Mesh,
|
||||
offsets: Array,
|
||||
base_scale: Vector3,
|
||||
vertical_offset: float
|
||||
) -> void:
|
||||
var multimesh := MultiMesh.new()
|
||||
multimesh.transform_format = MultiMesh.TRANSFORM_3D
|
||||
multimesh.mesh = mesh
|
||||
multimesh.instance_count = offsets.size()
|
||||
for index in offsets.size():
|
||||
var offset: Vector3 = offsets[index]
|
||||
var world_sample := to_global(offset)
|
||||
var height := terrain.data.get_height(world_sample)
|
||||
if is_nan(height):
|
||||
height = global_position.y
|
||||
var local_position := to_local(Vector3(world_sample.x, height, world_sample.z))
|
||||
local_position.y += vertical_offset
|
||||
var scale_variation := 0.84 + float((index * 37) % 5) * 0.07
|
||||
var rotation := deg_to_rad(float((index * 53) % 360))
|
||||
var basis := Basis(Vector3.UP, rotation).scaled(base_scale * scale_variation)
|
||||
multimesh.set_instance_transform(index, Transform3D(basis, local_position))
|
||||
target.multimesh = multimesh
|
||||
|
||||
|
||||
func _create_fern_mesh(material: Material) -> ArrayMesh:
|
||||
var vertices := PackedVector3Array()
|
||||
var normals := PackedVector3Array()
|
||||
var uvs := PackedVector2Array()
|
||||
for frond_index in 5:
|
||||
var angle := float(frond_index) / 5.0 * TAU + 0.3
|
||||
var direction := Vector3(cos(angle), 0.0, sin(angle))
|
||||
var side := Vector3(-direction.z, 0.0, direction.x) * 0.16
|
||||
var base := Vector3(0.0, 0.02, 0.0)
|
||||
var middle := direction * 0.38 + Vector3.UP * 0.14
|
||||
var tip := direction * 0.82 + Vector3.UP * 0.24
|
||||
vertices.append_array([base, middle + side, tip, base, tip, middle - side])
|
||||
for _vertex in 6:
|
||||
normals.append(Vector3.UP)
|
||||
for uv in [
|
||||
Vector2(0.5, 0.0),
|
||||
Vector2(1.0, 0.5),
|
||||
Vector2(0.5, 1.0),
|
||||
Vector2(0.5, 0.0),
|
||||
Vector2(0.5, 1.0),
|
||||
Vector2(0.0, 0.5),
|
||||
]:
|
||||
uvs.append(uv)
|
||||
var arrays := []
|
||||
arrays.resize(Mesh.ARRAY_MAX)
|
||||
arrays[Mesh.ARRAY_VERTEX] = vertices
|
||||
arrays[Mesh.ARRAY_NORMAL] = normals
|
||||
arrays[Mesh.ARRAY_TEX_UV] = uvs
|
||||
var mesh := ArrayMesh.new()
|
||||
mesh.add_surface_from_arrays(Mesh.PRIMITIVE_TRIANGLES, arrays)
|
||||
mesh.surface_set_material(0, material)
|
||||
return mesh
|
||||
|
||||
Reference in New Issue
Block a user