Initial commit
This commit is contained in:
@@ -0,0 +1,134 @@
|
||||
@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
|
||||
Reference in New Issue
Block a user