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}"; + } +}