Compare commits

...

3 Commits

Author SHA1 Message Date
copygirl 32caf91234 Improve grid and placement code 4 months ago
copygirl 8cc15ae68e Add small box object 4 months ago
copygirl e5aa2387a0 Change table height from 1m to 80cm 4 months ago
  1. BIN
      assets/models/small_box.blend
  2. 50
      assets/models/small_box.blend.import
  3. 50
      objects/Grid.cs
  4. 5
      objects/grid.tscn
  5. 15
      objects/small_box.tscn
  6. 82
      player/PickupController.cs
  7. 32
      scenes/workshop.tscn

Binary file not shown.

@ -0,0 +1,50 @@
[remap]
importer="scene"
importer_version=1
type="PackedScene"
uid="uid://c2u217co6u3fk"
path="res://.godot/imported/small_box.blend-b4d6c3d1ff6b310ab306e6f2c5a46167.scn"
[deps]
source_file="res://assets/models/small_box.blend"
dest_files=["res://.godot/imported/small_box.blend-b4d6c3d1ff6b310ab306e6f2c5a46167.scn"]
[params]
nodes/root_type=""
nodes/root_name=""
nodes/apply_root_scale=true
nodes/root_scale=1.0
meshes/ensure_tangents=true
meshes/generate_lods=true
meshes/create_shadow_meshes=true
meshes/light_baking=1
meshes/lightmap_texel_size=0.2
meshes/force_disable_compression=false
skins/use_named_skins=true
animation/import=true
animation/fps=30
animation/trimming=false
animation/remove_immutable_tracks=true
import_script/path=""
_subresources={}
gltf/naming_version=1
gltf/embedded_image_handling=1
blender/nodes/visible=0
blender/nodes/punctual_lights=true
blender/nodes/cameras=true
blender/nodes/custom_properties=true
blender/nodes/modifiers=1
blender/meshes/colors=false
blender/meshes/uvs=true
blender/meshes/normals=true
blender/meshes/tangents=true
blender/meshes/skins=2
blender/meshes/export_bones_deforming_mesh_only=false
blender/materials/unpack_enabled=true
blender/materials/export_materials=1
blender/animation/limit_playback=true
blender/animation/always_sample=true
blender/animation/group_tracks=true

@ -1,5 +1,6 @@
[Tool]
public partial class Grid : Area3D
public partial class Grid : Node3D
{
public const float StepSize = 0.05f; // 5cm
@ -19,19 +20,13 @@ public partial class Grid : Area3D
_gridSize = value;
// Helper value for converting grid pos to local pos and back.
_halfGridActualSize = new Vector3(GridSize.X, 0, GridSize.Y) * (StepSize / 2.0f);
Update();
UpdateImmediateMesh();
}
}
public override void _Ready()
=> Update();
void Update()
{
UpdateCollisionShape();
UpdateImmediateMesh();
}
=> UpdateImmediateMesh();
/// <summary> Returns whether the specified item is contained in this or any nested grids. </summary>
@ -44,6 +39,21 @@ public partial class Grid : Area3D
return false;
}
/// <summary>
/// Recursively adds all items included in this grid to the specified
/// collection. The collection must be of a valid super-type of Item.
/// </summary>
public void AddItemsRecursively<T>(ICollection<T> collection)
{
if (!typeof(T).IsAssignableFrom(typeof(Item)))
throw new ArgumentException($"Type '{typeof(T)}' is not a super-type of Item", nameof(T));
foreach (var child in GetChildren().OfType<Item>()) {
collection.Add((T)(object)child);
child.GetNodeOrNull<Grid>(nameof(Grid))?.AddItemsRecursively(collection);
}
}
public Vector3I LocalToGrid(Vector3 pos)
=> (Vector3I)((pos + _halfGridActualSize) / StepSize);
@ -108,28 +118,6 @@ public partial class Grid : Area3D
static StandardMaterial3D GetOrCreateMaterial()
=> _material ??= new() { VertexColorUseAsAlbedo = true };
ConvexPolygonShape3D _shape;
void UpdateCollisionShape()
{
if (Engine.IsEditorHint()) return;
if (_shape == null) {
_shape = new ConvexPolygonShape3D();
AddChild(new CollisionShape3D { Shape = _shape }, true);
}
const float Offset = 0.001f;
var x = GridSize.X * StepSize / 2;
var y = GridSize.Y * StepSize / 2;
_shape.Points = [
new(-x, Offset, -y),
new( x, Offset, -y),
new( x, Offset, y),
new(-x, Offset, y),
];
}
ImmediateMesh _mesh;
void UpdateImmediateMesh()
{

@ -2,8 +2,5 @@
[ext_resource type="Script" path="res://objects/Grid.cs" id="1_7yhbt"]
[node name="Grid" type="Area3D"]
collision_layer = 512
collision_mask = 0
monitoring = false
[node name="Grid" type="Node3D"]
script = ExtResource("1_7yhbt")

@ -0,0 +1,15 @@
[gd_scene load_steps=4 format=3 uid="uid://c7bmopoyrldwt"]
[ext_resource type="Script" path="res://objects/Item.cs" id="1_jx3xq"]
[ext_resource type="PackedScene" uid="uid://c2u217co6u3fk" path="res://assets/models/small_box.blend" id="2_mqs4f"]
[ext_resource type="PackedScene" uid="uid://54575e3ygpxl" path="res://objects/grid.tscn" id="3_114ae"]
[node name="Small Box" type="RigidBody3D"]
script = ExtResource("1_jx3xq")
Size = Vector3i(10, 4, 6)
[node name="Model" parent="." instance=ExtResource("2_mqs4f")]
[node name="Grid" parent="." instance=ExtResource("3_114ae")]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, -0.05, 0)
GridSize = Vector2i(8, 4)

@ -61,39 +61,17 @@ public partial class PickupController : Node3D
if (!_player.IsLocal) return;
if (HasItemsHeld) {
// This ray will be blocked by static and dynamic objects.
const PhysicsLayer Mask = PhysicsLayer.Place | PhysicsLayer.Static | PhysicsLayer.Dynamic;
if ((RayToMouseCursor(Mask) is RayResult ray) && (ray.Collider is Grid grid)
// Not pointing at item's own grid, or one of its nested grids.
&& (grid.GetParent() != HeldItem) && !grid.ContainsItem(HeldItem))
{
var inverseTransform = grid.GlobalTransform.AffineInverse();
var inverseBasis = grid.GlobalBasis.Inverse();
var pos = inverseTransform * ray.Position;
var normal = inverseBasis * ray.Normal;
if (grid.CanPlaceAgainst(HeldItem, pos, normal)) {
var transform = new Transform3D(inverseBasis * HeldItem.GlobalBasis, pos);
transform = grid.Snap(transform, normal, HeldItem);
var canPlace = grid.CanPlaceAt(HeldItem, transform);
var outlineColor = canPlace ? OutlineYesPlace : OutlineNoPlace;
_outlineMaterial.SetShaderParameter("line_color", outlineColor);
_preview.GlobalTransform = grid.GlobalTransform * transform;
_preview.Visible = true;
_targetedGrid = canPlace ? grid : null;
} else {
_preview.Visible = false;
_targetedGrid = null;
}
if (GetValidPlacement(HeldItem) is var (grid, transform, isFree)) {
var outlineColor = isFree ? OutlineYesPlace : OutlineNoPlace;
_outlineMaterial.SetShaderParameter("line_color", outlineColor);
_preview.GlobalTransform = grid.GlobalTransform * transform;
_preview.Visible = true;
_targetedGrid = isFree ? grid : null;
} else {
_preview.Visible = false;
_targetedGrid = null;
}
} else {
var interactable = RayToMouseCursor(PhysicsLayer.Pickup)?.Collider;
@ -142,8 +120,48 @@ public partial class PickupController : Node3D
return preview;
}
(Grid Grid, Transform3D Target, bool IsFree)? GetValidPlacement(Item itemToPlace)
{
// This ray will be blocked by static and dynamic objects.
const PhysicsLayer Mask = PhysicsLayer.Static | PhysicsLayer.Dynamic | PhysicsLayer.Item;
// FIXME: Remove .Place and .Pickup physics layers?
// TODO: We need a separate physics layers for:
// - The physical item collider used for physics calculations (simplified).
// - The placement collider which should match the item's appearance.
// - The general space / size an item takes up, as a cuboid.
// TODO: Probably just overhaul the physics layers altogether.
// It would be better to have a "collides with player" and "player collides with it" layer, etc.
var excludeSet = new HashSet<CollisionObject3D> { HeldItem };
var heldItemGrid = HeldItem.GetNodeOrNull<Grid>(nameof(Grid));
heldItemGrid?.AddItemsRecursively(excludeSet);
// Cast a ray and make sure it hit something.
if (RayToMouseCursor(Mask, excludeSet) is not RayResult ray) return null;
// Find a grid to place against, which will be either the grid belonging
// to the item the ray intersected, or the grid said item is placed upon.
var grid = ray.Collider.GetNodeOrNull<Grid>(nameof(Grid))
?? ray.Collider.GetParentOrNull<Grid>();
if (grid == null) return null; // No suitable grid found.
var inverseTransform = grid.GlobalTransform.AffineInverse();
var inverseBasis = grid.GlobalBasis.Inverse();
var pos = inverseTransform * ray.Position;
var normal = inverseBasis * ray.Normal;
if (!grid.CanPlaceAgainst(itemToPlace, pos, normal)) return null;
var transform = new Transform3D(inverseBasis * itemToPlace.GlobalBasis, pos);
transform = grid.Snap(transform, normal, itemToPlace);
var isFree = grid.CanPlaceAt(itemToPlace, transform);
return (grid, transform, isFree);
}
record class RayResult(CollisionObject3D Collider, Vector3 Position, Vector3 Normal);
RayResult RayToMouseCursor(PhysicsLayer collisionMask)
RayResult RayToMouseCursor(PhysicsLayer collisionMask, IEnumerable<CollisionObject3D> excluded = null)
{
var camera = _player.Camera.Camera;
var mouse = GetViewport().GetMousePosition();
@ -153,12 +171,10 @@ public partial class PickupController : Node3D
var query = PhysicsRayQueryParameters3D.Create(from, to);
query.CollisionMask = (uint)collisionMask;
query.CollideWithAreas = true;
// Exclude the `CurrentItem` from collision checking if it's being held.
query.Exclude = HasItemsHeld ? [ HeldItem.GetRid() ] : [];
query.Exclude = new((excluded ?? []).Select(obj => obj.GetRid()));
var result = GetWorld3D().DirectSpaceState.IntersectRay(query);
return (result.Count > 0) ? new(
// FIXME: Unable to cast object of type 'ReplacePalette' to type 'Godot.CollisionObject3D'.
result["collider"].As<CollisionObject3D>(),
(Vector3)result["position"],
(Vector3)result["normal"]

@ -1,4 +1,4 @@
[gd_scene load_steps=16 format=3 uid="uid://bwfuet1irfi17"]
[gd_scene load_steps=17 format=3 uid="uid://bwfuet1irfi17"]
[ext_resource type="Script" path="res://scenes/Workshop.cs" id="1_i8qyc"]
[ext_resource type="Script" path="res://scenes/ItemManager.cs" id="1_l6hw6"]
@ -9,6 +9,7 @@
[ext_resource type="PackedScene" uid="uid://dmehew21mh3ee" path="res://objects/cutting_board.tscn" id="5_hj6pf"]
[ext_resource type="PackedScene" uid="uid://bjgfm5x7a0dab" path="res://objects/bolt.tscn" id="5_r6ljd"]
[ext_resource type="PackedScene" uid="uid://54575e3ygpxl" path="res://objects/grid.tscn" id="6_okibm"]
[ext_resource type="PackedScene" uid="uid://c7bmopoyrldwt" path="res://objects/small_box.tscn" id="10_kgm3d"]
[sub_resource type="BoxShape3D" id="BoxShape3D_q481w"]
size = Vector3(15, 5, 15)
@ -25,7 +26,7 @@ size = Vector3(2, 0.1, 1)
size = Vector3(2, 0.1, 1)
[sub_resource type="BoxMesh" id="BoxMesh_efbik"]
size = Vector3(0.1, 0.9, 0.1)
size = Vector3(0.1, 0.7, 0.1)
[node name="Workshop" type="Area3D" node_paths=PackedStringArray("Objects")]
collision_layer = 1024
@ -84,7 +85,7 @@ collision_layer = 2
collision_mask = 0
[node name="Grid" parent="Objects/Table" instance=ExtResource("6_okibm")]
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, 0.8, 0)
GridSize = Vector2i(38, 18)
[node name="Nail" parent="Objects/Table/Grid" instance=ExtResource("4_6l6v6")]
@ -103,50 +104,53 @@ transform = Transform3D(1.31134e-07, 1, 4.37114e-08, 0, -4.37114e-08, 1, 1, -1.3
transform = Transform3D(-4.37114e-08, -1, -4.37114e-08, 0, -4.37114e-08, 1, -1, 4.37114e-08, 1.91069e-15, -0.75, 0.025, 0.0250001)
[node name="Plank" parent="Objects/Table/Grid" instance=ExtResource("4_kupiv")]
transform = Transform3D(-4.37114e-08, 0, -1, 0, 1, 0, 1, 0, -4.37114e-08, 0.775, 0.025, -0.05)
transform = Transform3D(-4.37114e-08, 0, -1, 0, 1, 0, 1, 0, -4.37114e-08, 0.775, 0.025, -0.0500002)
[node name="Cutting Board" parent="Objects/Table/Grid" instance=ExtResource("5_hj6pf")]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.025, 0.2)
[node name="Bolt" parent="Objects/Table/Grid/Cutting Board/Grid" index="1" instance=ExtResource("5_r6ljd")]
[node name="Bolt" parent="Objects/Table/Grid/Cutting Board/Grid" index="0" instance=ExtResource("5_r6ljd")]
transform = Transform3D(1, 0, 0, 0, -4.37114e-08, 1, 0, -1, -4.37114e-08, 0.1, 0.025, -0.0250001)
[node name="Bolt2" parent="Objects/Table/Grid/Cutting Board/Grid" index="2" instance=ExtResource("5_r6ljd")]
[node name="Bolt2" parent="Objects/Table/Grid/Cutting Board/Grid" index="1" instance=ExtResource("5_r6ljd")]
transform = Transform3D(-1, 8.74228e-08, 3.82137e-15, 0, -4.37114e-08, 1, 8.74228e-08, 1, 4.37114e-08, 0.15, 0.025, -0.0250001)
[node name="Bolt3" parent="Objects/Table/Grid/Cutting Board/Grid" index="3" instance=ExtResource("5_r6ljd")]
[node name="Bolt3" parent="Objects/Table/Grid/Cutting Board/Grid" index="2" instance=ExtResource("5_r6ljd")]
transform = Transform3D(1, 0, 0, 0, -4.37114e-08, 1, 0, -1, -4.37114e-08, 0.2, 0.025, -0.0250001)
[node name="Small Box" parent="Objects/Table/Grid" instance=ExtResource("10_kgm3d")]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0.2, 0.1, -0.2)
[node name="CollisionShape3D" type="CollisionShape3D" parent="Objects/Table"]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.95, 0)
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.75, 0)
shape = SubResource("BoxShape3D_vkl3b")
[node name="MeshInstance3D" type="MeshInstance3D" parent="Objects/Table"]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.95, 0)
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.75, 0)
mesh = SubResource("BoxMesh_548mk")
[node name="Leg1" type="MeshInstance3D" parent="Objects/Table"]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0.9, 0.45, 0.4)
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0.9, 0.35, 0.4)
mesh = SubResource("BoxMesh_efbik")
skeleton = NodePath("../MeshInstance3D")
[node name="Leg2" type="MeshInstance3D" parent="Objects/Table"]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -0.9, 0.45, 0.4)
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -0.9, 0.35, 0.4)
mesh = SubResource("BoxMesh_efbik")
skeleton = NodePath("../MeshInstance3D")
[node name="Leg3" type="MeshInstance3D" parent="Objects/Table"]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0.9, 0.45, -0.4)
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0.9, 0.35, -0.4)
mesh = SubResource("BoxMesh_efbik")
skeleton = NodePath("../MeshInstance3D")
[node name="Leg4" type="MeshInstance3D" parent="Objects/Table"]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -0.9, 0.45, -0.4)
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -0.9, 0.35, -0.4)
mesh = SubResource("BoxMesh_efbik")
skeleton = NodePath("../MeshInstance3D")
[node name="Table2" type="StaticBody3D" parent="Objects"]
transform = Transform3D(1, 0, 0, 0, -4.37114e-08, -1, 0, 1, -4.37114e-08, 0, 1.5, -4.6)
transform = Transform3D(1, 0, 0, 0, -4.37114e-08, -1, 0, 1, -4.37114e-08, 0, 1.3, -4.6)
collision_layer = 2
collision_mask = 0

Loading…
Cancel
Save