Randoloot/scripts/game/ship_shadow.gd

55 lines
1.9 KiB
GDScript

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