55 lines
2.4 KiB
GDScript
55 lines
2.4 KiB
GDScript
@tool
|
|
extends RefCounted
|
|
|
|
static func raycast(root: Node3D, camera: Camera3D, screen_position: Vector2) -> Variant:
|
|
var ray_origin := camera.project_ray_origin(screen_position)
|
|
var ray_direction := camera.project_ray_normal(screen_position).normalized()
|
|
var nearest_point: Variant = null
|
|
var nearest_distance := INF
|
|
var mesh_nodes: Array[MeshInstance3D] = []
|
|
_collect_mesh_instances(root, mesh_nodes)
|
|
for mesh_node in mesh_nodes:
|
|
var source_mesh := mesh_node.mesh
|
|
if source_mesh == null or not mesh_node.is_visible_in_tree():
|
|
continue
|
|
for surface_index in range(source_mesh.get_surface_count()):
|
|
var arrays: Array
|
|
if source_mesh is PrimitiveMesh:
|
|
if surface_index > 0:
|
|
continue
|
|
arrays = source_mesh.get_mesh_arrays()
|
|
else:
|
|
if source_mesh.surface_get_primitive_type(surface_index) != Mesh.PRIMITIVE_TRIANGLES:
|
|
continue
|
|
arrays = source_mesh.surface_get_arrays(surface_index)
|
|
if arrays.is_empty() or not arrays[Mesh.ARRAY_VERTEX] is PackedVector3Array:
|
|
continue
|
|
var vertices: PackedVector3Array = arrays[Mesh.ARRAY_VERTEX]
|
|
var indices := PackedInt32Array()
|
|
if arrays[Mesh.ARRAY_INDEX] is PackedInt32Array:
|
|
indices = arrays[Mesh.ARRAY_INDEX]
|
|
var has_indices := not indices.is_empty()
|
|
var triangle_count := indices.size() if has_indices else vertices.size()
|
|
for triangle_start in range(0, triangle_count - 2, 3):
|
|
var index_a := indices[triangle_start] if has_indices else triangle_start
|
|
var index_b := indices[triangle_start + 1] if has_indices else triangle_start + 1
|
|
var index_c := indices[triangle_start + 2] if has_indices else triangle_start + 2
|
|
if index_a < 0 or index_b < 0 or index_c < 0 or index_a >= vertices.size() or index_b >= vertices.size() or index_c >= vertices.size():
|
|
continue
|
|
var vertex_a := mesh_node.to_global(vertices[index_a])
|
|
var vertex_b := mesh_node.to_global(vertices[index_b])
|
|
var vertex_c := mesh_node.to_global(vertices[index_c])
|
|
var hit: Variant = Geometry3D.ray_intersects_triangle(ray_origin, ray_direction, vertex_a, vertex_b, vertex_c)
|
|
if hit == null:
|
|
continue
|
|
var distance := ray_origin.distance_to(hit)
|
|
if distance < nearest_distance:
|
|
nearest_distance = distance
|
|
nearest_point = hit
|
|
return nearest_point
|
|
|
|
static func _collect_mesh_instances(node: Node, result: Array[MeshInstance3D]) -> void:
|
|
if node is MeshInstance3D:
|
|
result.append(node)
|
|
for child in node.get_children():
|
|
_collect_mesh_instances(child, result)
|