Add placement preview

main
copygirl 11 months ago
parent 29f4eb35cd
commit 585768e48e
  1. 4
      objects/Item.cs
  2. 2
      objects/crate.tscn
  3. 62
      player/PickupController.cs
  4. 2
      player/player.tscn
  5. 10
      scenes/workshop.tscn

@ -2,6 +2,6 @@ public partial class Item : StaticBody3D
{ {
[Export] public Vector3I Size { get; set; } [Export] public Vector3I Size { get; set; }
public virtual MeshInstance3D Mesh public virtual Node3D Model
=> GetNode<MeshInstance3D>("MeshInstance3D"); => GetNode<Node3D>("Model");
} }

@ -15,5 +15,5 @@ Palette = ExtResource("2_n415g")
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, -0.125, 0) transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, -0.125, 0)
shape = SubResource("BoxShape3D_to3fn") shape = SubResource("BoxShape3D_to3fn")
[node name="blockbench_export" parent="." instance=ExtResource("3_tncd6")] [node name="Model" parent="." instance=ExtResource("3_tncd6")]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, -0.5, 0) transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, -0.5, 0)

@ -2,7 +2,7 @@ public partial class PickupController : Node3D
{ {
public Item CurrentItem { get; private set; } public Item CurrentItem { get; private set; }
public bool HasItemsHeld => GetChildCount() > 0; public bool HasItemsHeld => GetChildCount() > 0;
MeshInstance3D _placementPreview; Node3D _placementPreview;
[Export] public Camera3D Camera { get; set; } [Export] public Camera3D Camera { get; set; }
[Export] public float PickupDistance { get; set; } = 2.0f; [Export] public float PickupDistance { get; set; } = 2.0f;
@ -24,11 +24,19 @@ public partial class PickupController : Node3D
if (@event.IsActionPressed("interact_pickup")) { if (@event.IsActionPressed("interact_pickup")) {
if (!HasItemsHeld) { if (!HasItemsHeld) {
// Create clone of the item's model, use it as placement preview.
_placementPreview = (Node3D)CurrentItem.Model.Duplicate(0);
_placementPreview.Name = "PlacementPreview";
_placementPreview.TopLevel = true;
_placementPreview.Visible = false;
SetMeshLayerOutline(_placementPreview, OutlineMode.Exclusive);
AddChild(_placementPreview);
// Parent item to the `PickupController`. // Parent item to the `PickupController`.
var prevRot = CurrentItem.GlobalRotation; var prevRot = CurrentItem.GlobalRotation;
CurrentItem.GetParent().RemoveChild(CurrentItem); CurrentItem.GetParent().RemoveChild(CurrentItem);
AddChild(CurrentItem); AddChild(CurrentItem);
CurrentItem.Mesh.Layers &= (uint)~RenderLayer.Outline; SetMeshLayerOutline(CurrentItem.Model, OutlineMode.Disable);
CurrentItem.Position = Vector3.Zero; CurrentItem.Position = Vector3.Zero;
CurrentItem.GlobalRotation = prevRot; CurrentItem.GlobalRotation = prevRot;
CurrentItem.CollisionLayer &= (uint)~PhysicsLayer.Static; CurrentItem.CollisionLayer &= (uint)~PhysicsLayer.Static;
@ -44,7 +52,14 @@ public partial class PickupController : Node3D
// CurrentItem.Freeze = false; // CurrentItem.Freeze = false;
RemoveChild(CurrentItem); RemoveChild(CurrentItem);
_world.AddChild(CurrentItem); _world.AddChild(CurrentItem);
CurrentItem.GlobalTransform = prevTransform;
CurrentItem.GlobalTransform = _placementPreview.Visible
? _placementPreview.GlobalTransform
: prevTransform;
RemoveChild(_placementPreview);
_placementPreview.QueueFree();
_placementPreview = null;
GetViewport().SetInputAsHandled(); GetViewport().SetInputAsHandled();
} }
@ -59,28 +74,31 @@ public partial class PickupController : Node3D
if (HasItemsHeld) { if (HasItemsHeld) {
if ((RayToMouseCursor() is RayResult ray) && (ray.Collider is Grid)) { if ((RayToMouseCursor() is RayResult ray) && (ray.Collider is Grid)) {
// Snao rotation to nearest axis. // Snao rotation to nearest axis.
// FIXME: This needs to snap to the // FIXME: This needs to snap relative to the grid rotation.
// var globalRot = CurrentItem.GlobalRotation; var globalRot = CurrentItem.GlobalRotation;
// globalRot.X = Snapped(globalRot.X, Tau / 4); globalRot.X = Snapped(globalRot.X, Tau / 4);
// globalRot.Y = Snapped(globalRot.Y, Tau / 4); globalRot.Y = Snapped(globalRot.Y, Tau / 4);
// globalRot.Z = Snapped(globalRot.Z, Tau / 4); globalRot.Z = Snapped(globalRot.Z, Tau / 4);
// CurrentItem.GlobalRotation = globalRot; _placementPreview.GlobalRotation = globalRot;
// Snap the position to the grid. // Snap the position to the grid.
var halfSize = (Vector3)CurrentItem.Size * Grid.StepSize / 2; var halfSize = (Vector3)CurrentItem.Size * Grid.StepSize / 2;
var pos = ray.Position + halfSize * (ray.Normal * CurrentItem.GlobalTransform.Basis); var pos = ray.Position + halfSize * (ray.Normal * _placementPreview.GlobalTransform.Basis);
pos = pos.Snapped(Grid.StepSize * Vector3.One); // FIXME: This does global snapping only pos = pos.Snapped(Grid.StepSize * Vector3.One); // FIXME: This does global snapping only
CurrentItem.GlobalPosition = pos; _placementPreview.GlobalPosition = pos;
_placementPreview.Visible = true;
} else {
_placementPreview.Visible = false;
} }
} else { } else {
var interactable = RayToMouseCursor()?.Collider; var interactable = RayToMouseCursor()?.Collider;
// Remove the outline from the previously looked-at item. // Remove the outline from the previously looked-at item.
if (CurrentItem != null) CurrentItem.Mesh.Layers &= (uint)~RenderLayer.Outline; if (CurrentItem != null) SetMeshLayerOutline(CurrentItem.Model, OutlineMode.Disable);
// If the ray hits anything and the object hit is an item, set it as current. // If the ray hits anything and the object hit is an item, set it as current.
CurrentItem = interactable as Item; CurrentItem = interactable as Item;
// Add the outline to the currently looked-at item. // Add the outline to the currently looked-at item.
if (CurrentItem != null) CurrentItem.Mesh.Layers |= (uint)RenderLayer.Outline; if (CurrentItem != null) SetMeshLayerOutline(CurrentItem.Model, OutlineMode.Enable);
} }
} }
@ -89,7 +107,11 @@ public partial class PickupController : Node3D
if (CurrentItem == null) return; if (CurrentItem == null) return;
if (!IsInstanceValid(CurrentItem)) { if (!IsInstanceValid(CurrentItem)) {
CurrentItem = null; CurrentItem = null;
_placementPreview?.QueueFree(); if (_placementPreview != null) {
RemoveChild(_placementPreview);
_placementPreview.QueueFree();
_placementPreview = null;
}
} }
} }
@ -115,4 +137,16 @@ public partial class PickupController : Node3D
public Vector3 Position { get; } = (Vector3)dict["position"]; public Vector3 Position { get; } = (Vector3)dict["position"];
public Vector3 Normal { get; } = (Vector3)dict["normal"]; public Vector3 Normal { get; } = (Vector3)dict["normal"];
} }
enum OutlineMode { Disable, Enable, Exclusive }
static void SetMeshLayerOutline(Node3D parent, OutlineMode mode)
{
var children = parent.FindChildren("*", "MeshInstance3D", owned: false);
foreach (var mesh in children.Cast<MeshInstance3D>())
switch (mode) {
case OutlineMode.Disable: mesh.Layers &= ~(uint)RenderLayer.Outline; break;
case OutlineMode.Enable: mesh.Layers |= (uint)RenderLayer.Outline; break;
case OutlineMode.Exclusive: mesh.Layers = (uint)RenderLayer.Outline; break;
}
}
} }

@ -1115,6 +1115,6 @@ use_external_skeleton = true
external_skeleton = NodePath("../../../../../../../Model/Skeleton/Skeleton3D") external_skeleton = NodePath("../../../../../../../Model/Skeleton/Skeleton3D")
[node name="PickupController" type="Node3D" parent="." node_paths=PackedStringArray("Camera")] [node name="PickupController" type="Node3D" parent="." node_paths=PackedStringArray("Camera")]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.35, -0.45) transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.4, -0.45)
script = ExtResource("2_ns2pe") script = ExtResource("2_ns2pe")
Camera = NodePath("../AnimationController/Root/LowerBody/UpperBody/Neck/Head/Camera") Camera = NodePath("../AnimationController/Root/LowerBody/UpperBody/Neck/Head/Camera")

@ -72,8 +72,11 @@ Size = Vector3i(2, 2, 4)
[node name="CollisionShape3D" type="CollisionShape3D" parent="Table/SmallBox"] [node name="CollisionShape3D" type="CollisionShape3D" parent="Table/SmallBox"]
shape = SubResource("BoxShape3D_hkc0l") shape = SubResource("BoxShape3D_hkc0l")
[node name="MeshInstance3D" type="MeshInstance3D" parent="Table/SmallBox"] [node name="Model" type="Node3D" parent="Table/SmallBox"]
[node name="MeshInstance3D" type="MeshInstance3D" parent="Table/SmallBox/Model"]
mesh = SubResource("BoxMesh_3qwx3") mesh = SubResource("BoxMesh_3qwx3")
skeleton = NodePath("../..")
[node name="SmallBox2" type="StaticBody3D" parent="Table"] [node name="SmallBox2" type="StaticBody3D" parent="Table"]
transform = Transform3D(-4.37114e-08, 0, -1, 0, 1, 0, 1, 0, -4.37114e-08, 0.251316, 1.06737, 0.127615) transform = Transform3D(-4.37114e-08, 0, -1, 0, 1, 0, 1, 0, -4.37114e-08, 0.251316, 1.06737, 0.127615)
@ -85,8 +88,11 @@ Size = Vector3i(2, 2, 4)
[node name="CollisionShape3D" type="CollisionShape3D" parent="Table/SmallBox2"] [node name="CollisionShape3D" type="CollisionShape3D" parent="Table/SmallBox2"]
shape = SubResource("BoxShape3D_hkc0l") shape = SubResource("BoxShape3D_hkc0l")
[node name="MeshInstance3D" type="MeshInstance3D" parent="Table/SmallBox2"] [node name="Model" type="Node3D" parent="Table/SmallBox2"]
[node name="MeshInstance3D" type="MeshInstance3D" parent="Table/SmallBox2/Model"]
mesh = SubResource("BoxMesh_3qwx3") mesh = SubResource("BoxMesh_3qwx3")
skeleton = NodePath("../..")
[node name="Grid" type="Area3D" parent="Table"] [node name="Grid" type="Area3D" parent="Table"]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0) transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0)

Loading…
Cancel
Save