diff --git a/project.godot b/project.godot
index 5dc70e2..bf5e604 100644
--- a/project.godot
+++ b/project.godot
@@ -52,6 +52,12 @@ enabled=PackedStringArray("res://addons/imgui-godot/plugin.cfg", "res://addons/i
3d_physics/layer_7="reserved-7"
3d_physics/layer_8="reserved-8"
+[physics]
+
+3d/default_gravity=0.0
+3d/default_linear_damp=0.0
+3d/default_angular_damp=0.0
+
[rendering]
lights_and_shadows/use_physical_light_units=true
diff --git a/res/scene/test.tscn b/res/scene/test.tscn
index 370779b..fd22370 100644
--- a/res/scene/test.tscn
+++ b/res/scene/test.tscn
@@ -1,7 +1,8 @@
-[gd_scene load_steps=12 format=3 uid="uid://tht1tf5iq6lw"]
+[gd_scene load_steps=15 format=3 uid="uid://tht1tf5iq6lw"]
[ext_resource type="Script" path="res://src/Craft/Krakensbane.cs" id="1_ju4qj"]
[ext_resource type="Script" path="res://src/Testing/TestThruster.cs" id="2_1t1tx"]
+[ext_resource type="Script" path="res://src/Testing/DebugUI.cs" id="3_gx3yn"]
[sub_resource type="PhysicalSkyMaterial" id="PhysicalSkyMaterial_uxbcq"]
@@ -16,6 +17,8 @@ sky = SubResource("Sky_iakm3")
[sub_resource type="Compositor" id="Compositor_tt8nt"]
+[sub_resource type="PhysicsMaterial" id="PhysicsMaterial_sjyiu"]
+
[sub_resource type="BoxShape3D" id="BoxShape3D_8al46"]
[sub_resource type="BoxMesh" id="BoxMesh_jgj3c"]
@@ -26,6 +29,8 @@ size = Vector3(16.9697, 0.0310059, 15.6934)
[sub_resource type="PlaneMesh" id="PlaneMesh_xcndr"]
size = Vector2(20, 20)
+[sub_resource type="LabelSettings" id="LabelSettings_v1h1b"]
+
[node name="root" type="Node3D"]
[node name="DirectionalLight3D" type="DirectionalLight3D" parent="."]
@@ -42,6 +47,7 @@ FocusedVessel = NodePath("test-rocket")
[node name="test-rocket" type="RigidBody3D" parent="FloatingOriginRoot"]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.815224, 0)
+physics_material_override = SubResource("PhysicsMaterial_sjyiu")
script = ExtResource("2_1t1tx")
[node name="CollisionShape3D" type="CollisionShape3D" parent="FloatingOriginRoot/test-rocket"]
@@ -63,3 +69,52 @@ shape = SubResource("BoxShape3D_xxi7g")
[node name="MeshInstance3D" type="MeshInstance3D" parent="FloatingOriginRoot/ground"]
mesh = SubResource("PlaneMesh_xcndr")
+
+[node name="DebugUI" type="Panel" parent="." node_paths=PackedStringArray("minimapContainer", "planetOutline", "vesselIndicator", "krakensbane", "displayLabel")]
+offset_left = 1508.0
+offset_top = 62.0
+offset_right = 1868.0
+offset_bottom = 542.0
+script = ExtResource("3_gx3yn")
+minimapContainer = NodePath("VBoxContainer/MinimapContainer")
+planetOutline = NodePath("VBoxContainer/MinimapContainer/Polygon2D")
+vesselIndicator = NodePath("VBoxContainer/MinimapContainer/ColorRect")
+krakensbane = NodePath("../FloatingOriginRoot")
+displayLabel = NodePath("VBoxContainer/Label")
+
+[node name="VBoxContainer" type="VBoxContainer" parent="DebugUI"]
+layout_mode = 1
+anchors_preset = 15
+anchor_right = 1.0
+anchor_bottom = 1.0
+grow_horizontal = 2
+grow_vertical = 2
+
+[node name="MinimapContainer" type="Control" parent="DebugUI/VBoxContainer"]
+custom_minimum_size = Vector2(0, 360)
+layout_mode = 2
+
+[node name="Polygon2D" type="Polygon2D" parent="DebugUI/VBoxContainer/MinimapContainer"]
+color = Color(0.287295, 0.287295, 0.287295, 1)
+polygon = PackedVector2Array(116, 83, 96, 201, 274, 248, 289, 140, 119, 85)
+
+[node name="y-axis" type="Line2D" parent="DebugUI/VBoxContainer/MinimapContainer"]
+points = PackedVector2Array(180, 180, 180, 0)
+width = 2.0
+default_color = Color(0.286275, 0.290196, 1, 1)
+
+[node name="x-axis" type="Line2D" parent="DebugUI/VBoxContainer/MinimapContainer"]
+points = PackedVector2Array(180, 180, 360, 180)
+width = 2.0
+default_color = Color(0.671919, 0.312344, 0.0459558, 1)
+
+[node name="ColorRect" type="ColorRect" parent="DebugUI/VBoxContainer/MinimapContainer"]
+layout_mode = 0
+offset_right = 10.0
+offset_bottom = 10.0
+pivot_offset = Vector2(5, 5)
+
+[node name="Label" type="Label" parent="DebugUI/VBoxContainer"]
+layout_mode = 2
+text = "Position"
+label_settings = SubResource("LabelSettings_v1h1b")
diff --git a/src/Craft/Krakensbane.cs b/src/Craft/Krakensbane.cs
index 61f5cd4..9601b31 100644
--- a/src/Craft/Krakensbane.cs
+++ b/src/Craft/Krakensbane.cs
@@ -2,6 +2,7 @@ using System.Diagnostics;
using Godot;
using ImGuiNET;
using Quadratic.Carto.MathExt;
+using Vim.Math3d;
namespace Quadratic.Carto.Craft;
@@ -20,8 +21,24 @@ public sealed partial class Krakensbane : Node3D
public Node3D? FocusedVessel { get; set; }
const float maxDistance = 500.0f;
+ const float planetRadius = 6372.0f;
- DVector3 originPosition = DVector3.Zero;
+ DVector3 originPosition = DVector3.UnitY * planetRadius;
+
+ ///
+ /// Something to manage the non-inertial frame of reference.
+ ///
+ ///
+ /// It's good to have an inertial frame of reference when you're close to
+ /// a lot of static stuff, like on the ground. However, a non-inertial frame
+ /// of reference might be more appropriate when you're in deep space.
+ ///
+ ///
+ /// This provider field is null if we're using an inertial frame of reference.
+ /// When it's not null, it should be responsible for managing the acceleration,
+ /// angular acceleration, and even positions and/or rotations of children
+ /// objects.
+ INonInertialFrameProvider? nonInertialFrameProvider;
public override void _PhysicsProcess(double delta)
{
@@ -52,6 +69,27 @@ public sealed partial class Krakensbane : Node3D
originPosition += vesselPosition.AsVim().AsDouble();
}
+
+ // Apply gravity
+ foreach (var child in GetChildren())
+ {
+ if (child is RigidBody3D rb)
+ {
+ var localPosition = rb.Transform.Origin.AsVim().AsDouble();
+ var globalPosition = originPosition + localPosition;
+ var gravityVector = -globalPosition.Normalize();
+ var floatGravityVector = gravityVector.AsSingle().AsGodot();
+ var distance = globalPosition.Length();
+ var gravity = 9.81f * planetRadius * planetRadius / (distance * distance);
+ rb.ApplyCentralForce(floatGravityVector * (float)gravity);
+ }
+ }
+ }
+
+ public DVector3 GetPositionOf(Node3D node)
+ {
+ var localPosition = node.Transform.Origin.AsVim().AsDouble();
+ return originPosition + localPosition;
}
public override void _Process(double delta)
@@ -61,3 +99,9 @@ public sealed partial class Krakensbane : Node3D
ImGui.End();
}
}
+
+public interface INonInertialFrameProvider
+{
+ public DVector3 GetCurrentAcceleration();
+ public DVector3 GetCurrentAngularAcceleration();
+}
diff --git a/src/Craft/Vessel.cs b/src/Craft/Vessel.cs
new file mode 100644
index 0000000..d213111
--- /dev/null
+++ b/src/Craft/Vessel.cs
@@ -0,0 +1,14 @@
+using Godot;
+
+namespace Quadratic.Carto.Craft;
+
+///
+/// A Vessel is the smallest unit of object in the game that is considered a
+/// whole on its own, such as a space station or an astronaut. Connections can
+/// be made between vessels, but each vessel is still a separate entity.
+///
+///
+/// Vessel is an abstract class, and is meant to be extended by concrete
+/// implementations.
+///
+public abstract class Vessel { }
diff --git a/src/Craft/VesselNode.cs b/src/Craft/VesselNode.cs
new file mode 100644
index 0000000..10d12cf
--- /dev/null
+++ b/src/Craft/VesselNode.cs
@@ -0,0 +1,9 @@
+using Godot;
+
+namespace Quadratic.Carto.Craft;
+
+///
+/// A VesselNode is the node that represents a currently
+/// loaded into the game world.
+///
+public abstract partial class VesselNode : Node3D { }
diff --git a/src/Testing/DebugUI.cs b/src/Testing/DebugUI.cs
new file mode 100644
index 0000000..1b5c108
--- /dev/null
+++ b/src/Testing/DebugUI.cs
@@ -0,0 +1,64 @@
+using System.Collections.Generic;
+using Godot;
+using Quadratic.Carto.Craft;
+using Quadratic.Carto.MathExt;
+
+namespace Quadratic.Carto.Testing;
+
+public partial class DebugUI : Control
+{
+ [Export]
+ Control minimapContainer = null!;
+
+ [Export]
+ Vector2 minimapSize = new Vector2(360, 360);
+
+ [Export]
+ float minimapSizeUnit = 6372;
+
+ [Export]
+ float minimapUnitLength = 90;
+
+ [Export]
+ Polygon2D planetOutline = null!;
+
+ [Export]
+ Control vesselIndicator = null!;
+
+ [Export]
+ Krakensbane krakensbane = null!;
+
+ [Export]
+ Label displayLabel = null!;
+
+ public override void _Ready()
+ {
+ // draw the planet outline
+ var polygon = new List();
+ const int maxPoints = 360;
+ var minimapCenter = minimapSize / 2;
+ for (int i = 0; i < maxPoints; i++)
+ {
+ var angle = (float)i / maxPoints * Mathf.Pi * 2;
+ var point = new Vector2(Mathf.Cos(angle), Mathf.Sin(angle)) * minimapUnitLength + minimapCenter;
+ polygon.Add(point);
+ }
+ planetOutline.Polygon = polygon.ToArray();
+ }
+
+ public override void _Process(double delta)
+ {
+ if (krakensbane.FocusedVessel == null)
+ {
+ return;
+ }
+
+ var vesselPosition = krakensbane.GetPositionOf(krakensbane.FocusedVessel);
+ var minimapCenter = minimapSize / 2;
+ var minimapPosition3D = (vesselPosition / minimapSizeUnit).AsSingle().AsGodot() * minimapUnitLength;
+ var minimapPosition2D = new Vector2(-minimapPosition3D.Y, minimapPosition3D.X) + minimapCenter;
+ vesselIndicator.Position = minimapPosition2D;
+
+ displayLabel.Text = $"Vessel position:\n{vesselPosition.X},\n{vesselPosition.Y},\n{vesselPosition.Z}";
+ }
+}