You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
110 lines
3.3 KiB
110 lines
3.3 KiB
[Tool] |
|
public partial class Interactable : RigidBody3D |
|
{ |
|
PackedScene _modelScene; |
|
[Export] public PackedScene ModelScene { |
|
get => _modelScene; |
|
set => _modelScene = OnModelSceneChanged(value); |
|
} |
|
|
|
Vector3I _gridSize; |
|
/// <summary> Get the size of this item in grid units. </summary> |
|
[Export] public Vector3I GridSize { |
|
get => _gridSize; |
|
set => _gridSize = OnGridSizeChanged(value); |
|
} |
|
|
|
public override void _Ready() |
|
{ |
|
} |
|
|
|
public override void _Process(double delta) |
|
{ |
|
} |
|
|
|
// FIXME: Only change this if ModelScene is actually changed, not when loaded. |
|
PackedScene OnModelSceneChanged(PackedScene value) |
|
{ |
|
if (GetNodeOrNull("_Model") is Node oldModel) { |
|
RemoveChild(oldModel); |
|
oldModel.QueueFree(); |
|
} |
|
|
|
// Remove any previously added `CollisionShape3D` nodes. |
|
foreach (var child in GetChildren(true).OfType<CollisionShape3D>()) { |
|
RemoveChild(child); |
|
child.QueueFree(); |
|
} |
|
|
|
if (value is PackedScene scene) { |
|
var model = scene.Instantiate<Node3D>(); |
|
model.Name = "_Model"; |
|
|
|
var numShapes = 0; |
|
var min = Vector3.Zero; |
|
var max = Vector3.Zero; |
|
|
|
// Find all the `StaticBody3D` nodes in the model and parent |
|
// their `CollisionShape3D` children to the this `RigidBody3D`. |
|
// Required because shapes must be immediate children of the body. |
|
// See: https://github.com/godotengine/godot-proposals/issues/535 |
|
// https://github.com/godotengine/godot/pull/77937 |
|
foreach (var body in model.FindChildren("*", "StaticBody3D").Cast<StaticBody3D>()) { |
|
body.GetParent().RemoveChild(body); |
|
body.QueueFree(); |
|
|
|
foreach (var shape in body.GetChildren().OfType<CollisionShape3D>()) { |
|
// Not unsetting the owner results in this warning: |
|
// "Adding 'CollisionShape3D' as child to 'Interactable' will make owner '...' inconsistent." |
|
shape.Owner = null; |
|
|
|
body.RemoveChild(shape); |
|
shape.Name = $"_{nameof(CollisionShape3D)}_{numShapes + 1}"; |
|
AddChild(shape, false, InternalMode.Front); |
|
// shape.Owner = this; |
|
numShapes++; |
|
|
|
// Finds the axis-aligned boundary of all collision shapes. |
|
// This assumes that the shape has an identity transformation. |
|
var vertices = (shape.Shape as ConvexPolygonShape3D)?.Points |
|
?? (shape.Shape as ConcavePolygonShape3D)?.Data |
|
?? throw new Exception("Shape must be either convex or concave"); |
|
foreach (var vert in vertices) { |
|
min = new(Min(min.X, vert.X), Min(min.Y, vert.Y), Min(min.Z, vert.Z)); |
|
max = new(Max(max.X, vert.X), Max(max.Y, vert.Y), Max(max.Z, vert.Z)); |
|
} |
|
} |
|
} |
|
|
|
AddChild(model, false, InternalMode.Front); |
|
|
|
// Set the grid size based on the boundary of all collision shapes. |
|
GridSize = (Vector3I)((max - min).Snapped(Epsilon) / Grid.StepSize).Ceil(); |
|
} else { |
|
GridSize = Vector3I.Zero; |
|
} |
|
|
|
return value; |
|
} |
|
|
|
Vector3I OnGridSizeChanged(Vector3I value) |
|
{ |
|
if (GetNodeOrNull("_GridArea") is Node oldGridArea) { |
|
RemoveChild(oldGridArea); |
|
oldGridArea.QueueFree(); |
|
} |
|
|
|
if (value.X > 0 && value.Y > 0 && value.Z > 0) { |
|
var gridArea = new Area3D(); |
|
gridArea.Name = "_GridArea"; |
|
|
|
var shape = new CollisionShape3D(); |
|
shape.Shape = new BoxShape3D { Size = (Vector3)value * Grid.StepSize }; |
|
gridArea.AddChild(shape); |
|
|
|
AddChild(gridArea, false, InternalMode.Front); |
|
} |
|
|
|
return value; |
|
} |
|
}
|
|
|