From 4a3495faee13d03daff1a9408a9133b4b9e28ac3 Mon Sep 17 00:00:00 2001 From: copygirl Date: Wed, 27 Dec 2023 20:53:58 +0100 Subject: [PATCH] Snap correctly to non-world-aligned grid - Support non-even item sizes - Add wall test grid and items of various sizes --- player/PickupController.cs | 23 ++-- scenes/workshop.tscn | 183 +++++++++++++++++++++++++++--- scripts/globals/MathExtensions.cs | 5 + 3 files changed, 183 insertions(+), 28 deletions(-) create mode 100644 scripts/globals/MathExtensions.cs diff --git a/player/PickupController.cs b/player/PickupController.cs index e16844a..4771930 100644 --- a/player/PickupController.cs +++ b/player/PickupController.cs @@ -72,20 +72,23 @@ public partial class PickupController : Node3D EnsureCurrentItemValid(); if (HasItemsHeld) { - if ((RayToMouseCursor() is RayResult ray) && (ray.Collider is Grid)) { + if ((RayToMouseCursor() is RayResult ray) && (ray.Collider is Grid grid)) { // Snao rotation to nearest axis. - // FIXME: This needs to snap relative to the grid rotation. - var globalRot = CurrentItem.GlobalRotation; - globalRot.X = Snapped(globalRot.X, Tau / 4); - globalRot.Y = Snapped(globalRot.Y, Tau / 4); - globalRot.Z = Snapped(globalRot.Z, Tau / 4); - _placementPreview.GlobalRotation = globalRot; + var localBasis = grid.GlobalBasis.Inverse() * CurrentItem.GlobalBasis; + localBasis = Basis.FromEuler(localBasis.GetEuler().Snapped(Tau / 4)); + _placementPreview.GlobalBasis = grid.GlobalBasis * localBasis; // Snap the position to the grid. var halfSize = (Vector3)CurrentItem.Size * Grid.StepSize / 2; - var pos = ray.Position + halfSize * (ray.Normal * _placementPreview.GlobalTransform.Basis); - pos = pos.Snapped(Grid.StepSize * Vector3.One); // FIXME: This does global snapping only - _placementPreview.GlobalPosition = pos; + var localPos = ray.Position * grid.GlobalTransform; // Get grid-local ray position. + var localNormal = ray.Normal * grid.GlobalBasis; // Get grid-local ray normal. (Pointing away from surface hit.) + var off = localBasis * halfSize.PosMod(1.0f); // Calculate an offset for grid snapping. + off[(int)localNormal.Abs().MaxAxisIndex()] = 0; // Do not include offset in the normal direction. + localPos = off + (localPos - off).Snapped(Grid.StepSize); // Snap `localPos` to nearest grid value. + var axis = (localNormal * localBasis).Abs().MaxAxisIndex(); // Find object-local axis that the normal is pointing towards. + localPos += halfSize[(int)axis] * localNormal; // Offset (push out) object by half its size. + _placementPreview.GlobalPosition = grid.GlobalTransform * localPos; + _placementPreview.Visible = true; } else { _placementPreview.Visible = false; diff --git a/scenes/workshop.tscn b/scenes/workshop.tscn index 27a446a..d2710c7 100644 --- a/scenes/workshop.tscn +++ b/scenes/workshop.tscn @@ -1,4 +1,4 @@ -[gd_scene load_steps=13 format=3 uid="uid://bwfuet1irfi17"] +[gd_scene load_steps=17 format=3 uid="uid://bwfuet1irfi17"] [ext_resource type="Script" path="res://objects/Grid.cs" id="2_gstd0"] [ext_resource type="PackedScene" uid="uid://yvy5vvaqgxy8" path="res://objects/crate.tscn" id="2_j6a20"] @@ -23,6 +23,20 @@ size = Vector3(2, 0.1, 1) [sub_resource type="BoxMesh" id="BoxMesh_efbik"] size = Vector3(0.1, 0.9, 0.1) +[sub_resource type="BoxShape3D" id="BoxShape3D_dvxhj"] +size = Vector3(0.063, 0.063, 0.25) + +[sub_resource type="BoxMesh" id="BoxMesh_hlein"] +material = SubResource("StandardMaterial3D_foa6h") +size = Vector3(0.063, 0.063, 0.25) + +[sub_resource type="BoxShape3D" id="BoxShape3D_wh54f"] +size = Vector3(0.188, 0.063, 0.75) + +[sub_resource type="BoxMesh" id="BoxMesh_pejsc"] +material = SubResource("StandardMaterial3D_foa6h") +size = Vector3(0.188, 0.063, 0.75) + [sub_resource type="WorldBoundaryShape3D" id="WorldBoundaryShape3D_5erfn"] [sub_resource type="PlaneMesh" id="PlaneMesh_tg4vq"] @@ -62,45 +76,45 @@ transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, -4) collision_layer = 2 collision_mask = 0 -[node name="SmallBox" type="StaticBody3D" parent="Table"] -transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -0.43938, 1.06737, 0) +[node name="Grid" type="Area3D" parent="Table"] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0) +collision_layer = 8 +collision_mask = 0 +script = ExtResource("2_gstd0") +GridSize = Vector2i(30, 14) + +[node name="SmallBox" type="StaticBody3D" parent="Table/Grid"] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -0.43938, 0.0673701, 0) collision_layer = 9 collision_mask = 0 script = ExtResource("3_01pgc") Size = Vector3i(2, 2, 4) -[node name="CollisionShape3D" type="CollisionShape3D" parent="Table/SmallBox"] +[node name="CollisionShape3D" type="CollisionShape3D" parent="Table/Grid/SmallBox"] shape = SubResource("BoxShape3D_hkc0l") -[node name="Model" type="Node3D" parent="Table/SmallBox"] +[node name="Model" type="Node3D" parent="Table/Grid/SmallBox"] -[node name="MeshInstance3D" type="MeshInstance3D" parent="Table/SmallBox/Model"] +[node name="MeshInstance3D" type="MeshInstance3D" parent="Table/Grid/SmallBox/Model"] mesh = SubResource("BoxMesh_3qwx3") skeleton = NodePath("../..") -[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) +[node name="SmallBox2" type="StaticBody3D" parent="Table/Grid"] +transform = Transform3D(-4.37114e-08, 0, -1, 0, 1, 0, 1, 0, -4.37114e-08, 0.251316, 0.0673701, 0.127615) collision_layer = 9 collision_mask = 0 script = ExtResource("3_01pgc") Size = Vector3i(2, 2, 4) -[node name="CollisionShape3D" type="CollisionShape3D" parent="Table/SmallBox2"] +[node name="CollisionShape3D" type="CollisionShape3D" parent="Table/Grid/SmallBox2"] shape = SubResource("BoxShape3D_hkc0l") -[node name="Model" type="Node3D" parent="Table/SmallBox2"] +[node name="Model" type="Node3D" parent="Table/Grid/SmallBox2"] -[node name="MeshInstance3D" type="MeshInstance3D" parent="Table/SmallBox2/Model"] +[node name="MeshInstance3D" type="MeshInstance3D" parent="Table/Grid/SmallBox2/Model"] mesh = SubResource("BoxMesh_3qwx3") skeleton = NodePath("../..") -[node name="Grid" type="Area3D" parent="Table"] -transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0) -collision_layer = 8 -collision_mask = 0 -script = ExtResource("2_gstd0") -GridSize = Vector2i(30, 14) - [node name="CollisionShape3D" type="CollisionShape3D" parent="Table"] transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.95, 0) shape = SubResource("BoxShape3D_vkl3b") @@ -129,6 +143,139 @@ transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -0.9, 0.45, -0.4) mesh = SubResource("BoxMesh_efbik") skeleton = NodePath("../MeshInstance3D") +[node name="Table2" type="StaticBody3D" parent="."] +transform = Transform3D(1, 0, 0, 0, -4.37114e-08, -1, 0, 1, -4.37114e-08, 0, 1.5, -4.6) +collision_layer = 2 +collision_mask = 0 + +[node name="Grid" type="Area3D" parent="Table2"] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.0999999, 0) +collision_layer = 8 +collision_mask = 0 +script = ExtResource("2_gstd0") +GridSize = Vector2i(30, 14) + +[node name="SmallBox3" type="StaticBody3D" parent="Table2/Grid"] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -0.717982, 0.0335231, -0.120977) +collision_layer = 9 +collision_mask = 0 +script = ExtResource("3_01pgc") +Size = Vector3i(1, 1, 4) + +[node name="CollisionShape3D" type="CollisionShape3D" parent="Table2/Grid/SmallBox3"] +shape = SubResource("BoxShape3D_dvxhj") + +[node name="Model" type="Node3D" parent="Table2/Grid/SmallBox3"] + +[node name="MeshInstance3D" type="MeshInstance3D" parent="Table2/Grid/SmallBox3/Model"] +mesh = SubResource("BoxMesh_hlein") +skeleton = NodePath("../..") + +[node name="SmallBox4" type="StaticBody3D" parent="Table2/Grid"] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -0.591133, 0.0335231, -0.120977) +collision_layer = 9 +collision_mask = 0 +script = ExtResource("3_01pgc") +Size = Vector3i(1, 1, 4) + +[node name="CollisionShape3D" type="CollisionShape3D" parent="Table2/Grid/SmallBox4"] +shape = SubResource("BoxShape3D_dvxhj") + +[node name="Model" type="Node3D" parent="Table2/Grid/SmallBox4"] + +[node name="MeshInstance3D" type="MeshInstance3D" parent="Table2/Grid/SmallBox4/Model"] +mesh = SubResource("BoxMesh_hlein") +skeleton = NodePath("../..") + +[node name="SmallBox5" type="StaticBody3D" parent="Table2/Grid"] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -0.466087, 0.0335231, -0.120977) +collision_layer = 9 +collision_mask = 0 +script = ExtResource("3_01pgc") +Size = Vector3i(1, 1, 4) + +[node name="CollisionShape3D" type="CollisionShape3D" parent="Table2/Grid/SmallBox5"] +shape = SubResource("BoxShape3D_dvxhj") + +[node name="Model" type="Node3D" parent="Table2/Grid/SmallBox5"] + +[node name="MeshInstance3D" type="MeshInstance3D" parent="Table2/Grid/SmallBox5/Model"] +mesh = SubResource("BoxMesh_hlein") +skeleton = NodePath("../..") + +[node name="SmallBox6" type="StaticBody3D" parent="Table2/Grid"] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -0.342431, 0.0335231, -0.120977) +collision_layer = 9 +collision_mask = 0 +script = ExtResource("3_01pgc") +Size = Vector3i(1, 1, 4) + +[node name="CollisionShape3D" type="CollisionShape3D" parent="Table2/Grid/SmallBox6"] +shape = SubResource("BoxShape3D_dvxhj") + +[node name="Model" type="Node3D" parent="Table2/Grid/SmallBox6"] + +[node name="MeshInstance3D" type="MeshInstance3D" parent="Table2/Grid/SmallBox6/Model"] +mesh = SubResource("BoxMesh_hlein") +skeleton = NodePath("../..") + +[node name="SmallBox7" type="StaticBody3D" parent="Table2/Grid"] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -0.217693, 0.0335231, -0.120977) +collision_layer = 9 +collision_mask = 0 +script = ExtResource("3_01pgc") +Size = Vector3i(1, 1, 4) + +[node name="CollisionShape3D" type="CollisionShape3D" parent="Table2/Grid/SmallBox7"] +shape = SubResource("BoxShape3D_dvxhj") + +[node name="Model" type="Node3D" parent="Table2/Grid/SmallBox7"] + +[node name="MeshInstance3D" type="MeshInstance3D" parent="Table2/Grid/SmallBox7/Model"] +mesh = SubResource("BoxMesh_hlein") +skeleton = NodePath("../..") + +[node name="SmallBox8" type="StaticBody3D" parent="Table2/Grid"] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -0.0920233, 0.0335231, -0.120977) +collision_layer = 9 +collision_mask = 0 +script = ExtResource("3_01pgc") +Size = Vector3i(1, 1, 4) + +[node name="CollisionShape3D" type="CollisionShape3D" parent="Table2/Grid/SmallBox8"] +shape = SubResource("BoxShape3D_dvxhj") + +[node name="Model" type="Node3D" parent="Table2/Grid/SmallBox8"] + +[node name="MeshInstance3D" type="MeshInstance3D" parent="Table2/Grid/SmallBox8/Model"] +mesh = SubResource("BoxMesh_hlein") +skeleton = NodePath("../..") + +[node name="SmallBox9" type="StaticBody3D" parent="Table2/Grid"] +transform = Transform3D(-4.37114e-08, 0, -1, 1, -4.37114e-08, -4.37114e-08, -4.37114e-08, -1, 1.91069e-15, 0.314445, 0.0927377, 0.155108) +collision_layer = 9 +collision_mask = 0 +script = ExtResource("3_01pgc") +Size = Vector3i(3, 1, 12) + +[node name="CollisionShape3D" type="CollisionShape3D" parent="Table2/Grid/SmallBox9"] +shape = SubResource("BoxShape3D_wh54f") + +[node name="Model" type="Node3D" parent="Table2/Grid/SmallBox9"] + +[node name="MeshInstance3D" type="MeshInstance3D" parent="Table2/Grid/SmallBox9/Model"] +transform = Transform3D(1, 0, -8.35187e-23, 0, 1, 0, -8.35187e-23, 0, 1, 0, 0, 0) +mesh = SubResource("BoxMesh_pejsc") +skeleton = NodePath("../..") + +[node name="CollisionShape3D" type="CollisionShape3D" parent="Table2"] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.0499997, 0) +shape = SubResource("BoxShape3D_vkl3b") + +[node name="MeshInstance3D" type="MeshInstance3D" parent="Table2"] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.0499997, 0) +mesh = SubResource("BoxMesh_548mk") + [node name="Floor" type="StaticBody3D" parent="."] collision_mask = 0 diff --git a/scripts/globals/MathExtensions.cs b/scripts/globals/MathExtensions.cs new file mode 100644 index 0000000..caaa05a --- /dev/null +++ b/scripts/globals/MathExtensions.cs @@ -0,0 +1,5 @@ +public static class MathExtensions +{ + public static Vector3 Snapped(this Vector3 self, float value) + => self.Snapped(Vector3.One * value); +}