135 lines
4.4 KiB
GDScript
135 lines
4.4 KiB
GDScript
@tool
|
|
extends Node3D
|
|
## Hollow planet collision. Attach to Moon or Earth.
|
|
## SphereShape3D is a SOLID ball — anything inside the radius gets ejected.
|
|
## This builds a concave surface (triangles only) with an optional station hole.
|
|
## Visual mesh stays on SurfaceHigh. Collision matches that radius.
|
|
|
|
@export var surface_mesh_path: NodePath = ^"SurfaceHigh"
|
|
@export var old_body_path: NodePath = ^"MoonBody"
|
|
@export var hole_anchor_path: NodePath = ^"LovellAnchor"
|
|
@export var hole_radius_m: float = 205.0
|
|
@export var segments: int = 48
|
|
@export var rings: int = 32
|
|
@export var rebuild_shell: bool = false:
|
|
set(v):
|
|
rebuild_shell = false
|
|
if v:
|
|
build()
|
|
|
|
|
|
func _ready() -> void:
|
|
build()
|
|
|
|
|
|
func build() -> void:
|
|
var radius: float = _visual_radius()
|
|
if radius < 10.0:
|
|
push_warning("PlanetShell: no radius from SurfaceHigh")
|
|
return
|
|
_disable_solid_body()
|
|
var hole_dir := Vector3.ZERO
|
|
var hole_cos := 1.0
|
|
var anchor := get_node_or_null(hole_anchor_path) as Node3D
|
|
if anchor and hole_radius_m > 1.0:
|
|
var to_a: Vector3 = anchor.global_position - global_position
|
|
if to_a.length() > 1.0:
|
|
hole_dir = global_transform.basis.inverse() * to_a.normalized()
|
|
hole_dir = hole_dir.normalized()
|
|
var ang: float = atan(hole_radius_m / radius)
|
|
hole_cos = cos(ang)
|
|
var faces := _make_faces(radius, hole_dir, hole_cos)
|
|
var body := get_node_or_null("ShellBody") as StaticBody3D
|
|
if body == null:
|
|
body = StaticBody3D.new()
|
|
body.name = "ShellBody"
|
|
add_child(body)
|
|
body.owner = _scene_root()
|
|
body.transform = Transform3D()
|
|
body.collision_layer = 1
|
|
body.collision_mask = 1
|
|
var col := body.get_node_or_null("CollisionShape3D") as CollisionShape3D
|
|
if col == null:
|
|
col = CollisionShape3D.new()
|
|
col.name = "CollisionShape3D"
|
|
body.add_child(col)
|
|
col.owner = body.owner
|
|
var shape := ConcavePolygonShape3D.new()
|
|
shape.set_faces(faces)
|
|
col.shape = shape
|
|
col.transform = Transform3D()
|
|
print("PlanetShell ", name, " radius=", radius, " faces=", faces.size() / 3, " hole=", hole_dir != Vector3.ZERO, " anchor=", anchor)
|
|
|
|
|
|
func _visual_radius() -> float:
|
|
var high := get_node_or_null(surface_mesh_path) as MeshInstance3D
|
|
if high and high.mesh is SphereMesh:
|
|
var sm := high.mesh as SphereMesh
|
|
var rad: float = sm.radius
|
|
if sm.height > rad * 1.5:
|
|
rad = sm.height * 0.5
|
|
var scl: float = maxf(high.scale.x, maxf(high.scale.y, high.scale.z))
|
|
rad *= scl
|
|
if rad > 100.0:
|
|
return rad
|
|
return 173700.0
|
|
|
|
|
|
func _disable_solid_body() -> void:
|
|
var old := get_node_or_null(old_body_path)
|
|
if old == null:
|
|
return
|
|
var col := old.get_node_or_null("CollisionShape3D") as CollisionShape3D
|
|
if col:
|
|
col.disabled = true
|
|
if old is CollisionObject3D:
|
|
(old as CollisionObject3D).collision_layer = 0
|
|
(old as CollisionObject3D).collision_mask = 0
|
|
print("PlanetShell: MoonBody DISABLED (solid sphere ejects)")
|
|
|
|
|
|
func _make_faces(radius: float, hole_dir: Vector3, hole_cos: float) -> PackedVector3Array:
|
|
var faces := PackedVector3Array()
|
|
var segs: int = maxi(12, segments)
|
|
var rgs: int = maxi(8, rings)
|
|
var use_hole: bool = hole_dir.length_squared() > 0.5
|
|
for lat in range(rgs):
|
|
var t0: float = float(lat) / float(rgs) * PI - PI * 0.5
|
|
var t1: float = float(lat + 1) / float(rgs) * PI - PI * 0.5
|
|
var y0: float = sin(t0)
|
|
var y1: float = sin(t1)
|
|
var r0: float = cos(t0)
|
|
var r1: float = cos(t1)
|
|
for lon in range(segs):
|
|
var a0: float = float(lon) / float(segs) * TAU
|
|
var a1: float = float(lon + 1) / float(segs) * TAU
|
|
var p00 := Vector3(r0 * cos(a0), y0, r0 * sin(a0)) * radius
|
|
var p10 := Vector3(r0 * cos(a1), y0, r0 * sin(a1)) * radius
|
|
var p01 := Vector3(r1 * cos(a0), y1, r1 * sin(a0)) * radius
|
|
var p11 := Vector3(r1 * cos(a1), y1, r1 * sin(a1)) * radius
|
|
_add_tri(faces, p00, p01, p11, hole_dir, hole_cos, use_hole)
|
|
_add_tri(faces, p00, p11, p10, hole_dir, hole_cos, use_hole)
|
|
return faces
|
|
|
|
|
|
func _add_tri(faces: PackedVector3Array, a: Vector3, b: Vector3, c: Vector3, hole_dir: Vector3, hole_cos: float, use_hole: bool) -> void:
|
|
if use_hole:
|
|
var mid: Vector3 = (a + b + c) / 3.0
|
|
if mid.length_squared() < 0.001:
|
|
return
|
|
if mid.normalized().dot(hole_dir) >= hole_cos:
|
|
return
|
|
# Both windings. Concave shells are one-sided; a flipped face is a hole you fall through.
|
|
faces.append(a)
|
|
faces.append(b)
|
|
faces.append(c)
|
|
faces.append(a)
|
|
faces.append(c)
|
|
faces.append(b)
|
|
|
|
|
|
func _scene_root() -> Node:
|
|
if Engine.is_editor_hint() and get_tree() and get_tree().edited_scene_root:
|
|
return get_tree().edited_scene_root
|
|
return owner if owner else self
|