extends RefCounted # Cache one simplified silhouette per texture, shared by all ships of that type. static var _silhouettes: Dictionary = {} static func attach_to(sprite: Sprite2D) -> void: if sprite.texture == null: return sprite.light_mask = 2 if sprite.has_node("ShipShadow"): return var texture := sprite.texture var key := texture.get_instance_id() if not _silhouettes.has(key): var image := texture.get_image() if image == null or image.is_empty(): return if image.is_compressed(): image.decompress() var original_size := Vector2(image.get_size()) var factor := minf(1.0, 128.0 / maxf(original_size.x, original_size.y)) image.resize(maxi(1, roundi(original_size.x * factor)), maxi(1, roundi(original_size.y * factor)), Image.INTERPOLATE_BILINEAR) var bitmap := BitMap.new() bitmap.create_from_image_alpha(image, 0.25) var polygons := bitmap.opaque_to_polygons(Rect2i(Vector2i.ZERO, image.get_size()), 1.5) var silhouette := PackedVector2Array() var largest_area := 0.0 for polygon in polygons: var area := _polygon_area(polygon) if area > largest_area: largest_area = area silhouette = polygon var image_scale := original_size / Vector2(image.get_size()) for index in silhouette.size(): silhouette[index] = silhouette[index] * image_scale - original_size * 0.5 _silhouettes[key] = silhouette var points: PackedVector2Array = _silhouettes[key] if points.size() < 3: return var polygon := OccluderPolygon2D.new() polygon.polygon = points polygon.cull_mode = OccluderPolygon2D.CULL_DISABLED var occluder := LightOccluder2D.new() occluder.name = "ShipShadow" occluder.occluder = polygon occluder.occluder_light_mask = 1 occluder.show_behind_parent = true sprite.add_child(occluder) static func _polygon_area(points: PackedVector2Array) -> float: var area := 0.0 for index in points.size(): area += points[index].cross(points[(index + 1) % points.size()]) return absf(area) * 0.5