2026-03-02 15:04:15 +02:00
|
|
|
#nullable enable
|
|
|
|
|
using Godot;
|
|
|
|
|
using System;
|
|
|
|
|
using System.Globalization;
|
2026-03-03 08:51:52 +02:00
|
|
|
using System.IO;
|
2026-03-02 15:04:15 +02:00
|
|
|
using System.Linq;
|
|
|
|
|
using Godot.Collections;
|
|
|
|
|
|
|
|
|
|
public partial class Main : Control
|
|
|
|
|
{
|
2026-03-03 20:02:39 +02:00
|
|
|
public static Random Random = Random.Shared;
|
2026-03-02 15:04:15 +02:00
|
|
|
private bool _moving = false;
|
|
|
|
|
[Export]
|
|
|
|
|
private Node3D _yawNode;
|
|
|
|
|
[Export]
|
|
|
|
|
private Node3D _pitchNode;
|
|
|
|
|
[Export]
|
|
|
|
|
private Camera3D _cameraNode;
|
|
|
|
|
|
|
|
|
|
[Export] private float _moveSensitivity = 1f/500f;
|
|
|
|
|
[Export] private float _zoomSensitivity = 1f;
|
|
|
|
|
|
|
|
|
|
[Export] private MeshInstance3D _meshInstance;
|
|
|
|
|
[Export] private Node3D World;
|
|
|
|
|
[Export] private TextureRect _textureRect;
|
2026-03-03 08:51:52 +02:00
|
|
|
|
|
|
|
|
[Export] private Gradient _gradient;
|
2026-03-02 15:04:15 +02:00
|
|
|
|
|
|
|
|
private PlanetHelper.VertexData? _vertex = null;
|
|
|
|
|
private PlanetHelper.PlateData? _plate = null;
|
2026-03-03 07:34:13 +02:00
|
|
|
private int _resolution = 512;
|
2026-03-02 15:04:15 +02:00
|
|
|
|
|
|
|
|
private PlanetHelper _planetHelper;
|
2026-03-03 22:05:17 +02:00
|
|
|
[Export] private Curve _remapCurve;
|
2026-03-02 15:04:15 +02:00
|
|
|
public override void _Ready()
|
|
|
|
|
{
|
2026-03-03 22:05:17 +02:00
|
|
|
_planetHelper = new PlanetHelper(_meshInstance, _textureRect, _remapCurve);
|
2026-03-02 15:04:15 +02:00
|
|
|
UpdateStats();
|
2026-03-03 08:51:52 +02:00
|
|
|
Projector.GatherPoints(_planetHelper, int.Parse(GetNode<LineEdit>("%Resolution").Text));
|
2026-03-03 20:02:39 +02:00
|
|
|
AxialTiltChanged(GetNode<LineEdit>("%AxialTilt").Text);
|
2026-03-07 10:29:28 +02:00
|
|
|
UpdateTime();
|
2026-03-02 15:04:15 +02:00
|
|
|
}
|
|
|
|
|
|
2026-03-03 13:38:25 +02:00
|
|
|
Vector3 _pointerPosition = Vector3.Zero;
|
2026-03-02 15:04:15 +02:00
|
|
|
private const float RayLength = 1000.0f;
|
2026-03-03 20:02:39 +02:00
|
|
|
|
|
|
|
|
private bool _rotateable = false;
|
|
|
|
|
public void MouseInViewport()
|
|
|
|
|
{
|
|
|
|
|
_rotateable = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void MouseLeftViewport()
|
|
|
|
|
{
|
|
|
|
|
_rotateable = false;
|
|
|
|
|
}
|
2026-03-02 15:04:15 +02:00
|
|
|
public override void _Input(InputEvent @event)
|
|
|
|
|
{
|
|
|
|
|
if (@event is InputEventMouseButton mouseEvent)
|
|
|
|
|
{
|
|
|
|
|
if (mouseEvent.ButtonIndex == MouseButton.Left)
|
|
|
|
|
{
|
2026-03-03 20:02:39 +02:00
|
|
|
_moving = mouseEvent.Pressed && _rotateable;
|
2026-03-02 15:04:15 +02:00
|
|
|
}
|
|
|
|
|
if (mouseEvent.ButtonIndex == MouseButton.WheelUp)
|
|
|
|
|
{
|
|
|
|
|
_cameraNode.Position += new Vector3(0, 0, _zoomSensitivity);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (mouseEvent.ButtonIndex == MouseButton.WheelDown)
|
|
|
|
|
{
|
|
|
|
|
_cameraNode.Position -= new Vector3(0, 0, _zoomSensitivity);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if (@event is InputEventMouseMotion motionEvent && _moving)
|
|
|
|
|
{
|
|
|
|
|
_yawNode.RotateY(-motionEvent.ScreenRelative.X * _moveSensitivity);
|
|
|
|
|
_pitchNode.RotateX(-motionEvent.ScreenRelative.Y * _moveSensitivity);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void Tab(int tab)
|
|
|
|
|
{
|
|
|
|
|
if (tab == 1)
|
|
|
|
|
{
|
2026-03-03 07:34:13 +02:00
|
|
|
Projector.GatherPoints(_planetHelper, _resolution);
|
2026-03-02 15:04:15 +02:00
|
|
|
_textureRect.Texture = Projector.Render(_planetHelper);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public override void _Process(double delta)
|
|
|
|
|
{
|
|
|
|
|
if (Input.IsActionJustPressed("mouse_secondary"))
|
|
|
|
|
{
|
2026-03-03 13:38:25 +02:00
|
|
|
var from = _cameraNode.ProjectRayOrigin(_cameraNode.GetViewport().GetMousePosition());
|
|
|
|
|
var to = from + _cameraNode.ProjectRayNormal(_cameraNode.GetViewport().GetMousePosition()) * RayLength;
|
2026-03-02 15:04:15 +02:00
|
|
|
var result = World.GetWorld3D().DirectSpaceState.IntersectRay(PhysicsRayQueryParameters3D.Create(from, to));
|
|
|
|
|
if (result.Count > 0)
|
|
|
|
|
{
|
2026-03-03 13:38:25 +02:00
|
|
|
Vector3 pos = result["position"].AsVector3();
|
|
|
|
|
GD.Print($"Hit: '{pos}'");
|
2026-03-03 20:02:39 +02:00
|
|
|
pos *= _meshInstance.Transform.Basis.Orthonormalized();
|
2026-03-03 13:38:25 +02:00
|
|
|
var closest = _planetHelper.Octree.SearchNearest(pos)?.Id;
|
|
|
|
|
if (closest != null)
|
2026-03-02 15:04:15 +02:00
|
|
|
{
|
2026-03-03 13:38:25 +02:00
|
|
|
_vertex = _planetHelper.Vertices.Single(v => v.Id == closest);
|
|
|
|
|
if (_planetHelper.Plates.Count > 0 && _vertex.PlateId != -1)
|
|
|
|
|
_plate = _planetHelper.Plates[_vertex.PlateId];
|
|
|
|
|
else
|
|
|
|
|
_plate = null;
|
|
|
|
|
UpdateStats();
|
|
|
|
|
Vector3 vertexPos = _planetHelper.Mdt.GetVertex(_vertex.Id) * 0.01f;
|
2026-03-03 20:02:39 +02:00
|
|
|
vertexPos *= _meshInstance.Transform.Basis.Inverse().Orthonormalized();
|
2026-03-03 22:05:17 +02:00
|
|
|
_pointerPosition = (Vector3)vertexPos + (vertexPos * (_vertex.Height + 0.01f) * 0.03f); // vertexPos * 1.01f;
|
|
|
|
|
GetNode<Node3D>("%Pointer").GlobalPosition = _pointerPosition;
|
2026-03-07 10:29:28 +02:00
|
|
|
UpdateTime();
|
2026-03-02 15:04:15 +02:00
|
|
|
}
|
2026-03-03 13:38:25 +02:00
|
|
|
|
2026-03-02 15:04:15 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (Input.IsActionJustPressed("spacebar"))
|
|
|
|
|
{
|
|
|
|
|
_planetHelper.Advance = true;
|
|
|
|
|
}
|
|
|
|
|
if (Input.IsActionJustPressed("enter"))
|
|
|
|
|
{
|
|
|
|
|
_planetHelper.AutoRun = true;
|
|
|
|
|
}
|
|
|
|
|
_planetHelper.Process();
|
2026-03-03 20:02:39 +02:00
|
|
|
if (_sunRotating)
|
|
|
|
|
{
|
2026-03-07 10:29:28 +02:00
|
|
|
_sunRotation = (double)(GetNode<HSlider>("%SunAngle").Value / 365.0);
|
2026-03-03 20:02:39 +02:00
|
|
|
GetNode<Node3D>("%Sun").Rotation -= GetNode<Node3D>("%Sun").Rotation;
|
2026-03-07 10:29:28 +02:00
|
|
|
GetNode<Node3D>("%Sun").Rotation += new Vector3(0, Mathf.DegToRad((float)_sunRotation * 360f), 0);
|
|
|
|
|
UpdateTime();
|
2026-03-03 20:02:39 +02:00
|
|
|
}
|
2026-03-03 22:05:17 +02:00
|
|
|
if (_planetRotating)
|
|
|
|
|
{
|
2026-03-07 10:29:28 +02:00
|
|
|
_planetRotation = (double)(GetNode<HSlider>("%PlanetAngle").Value / 24.0);
|
2026-03-03 22:05:17 +02:00
|
|
|
_meshInstance.Rotation = Vector3.Zero;
|
2026-03-07 10:29:28 +02:00
|
|
|
_meshInstance.RotateY((float)Mathf.DegToRad(_planetRotation * 360.0));
|
2026-03-03 22:05:17 +02:00
|
|
|
_meshInstance.RotateZ(Mathf.DegToRad(_axialTilt));
|
2026-03-07 10:29:28 +02:00
|
|
|
UpdateTime();
|
2026-03-03 22:05:17 +02:00
|
|
|
}
|
2026-03-02 15:04:15 +02:00
|
|
|
}
|
2026-03-07 10:29:28 +02:00
|
|
|
|
|
|
|
|
public void UpdateTime()
|
|
|
|
|
{
|
|
|
|
|
double hours = GetLocalTime(GetNode<Node3D>("%Pointer").GlobalPosition, GetNode<Node3D>("%Sun").GlobalTransform.Basis.Z,
|
|
|
|
|
_meshInstance);
|
|
|
|
|
GetNode<Label>("%LocalTime").Text = DaysToTime(_sunRotation * 365.0, hours);
|
|
|
|
|
|
|
|
|
|
GetNode<Label>("%Date").Text = DaysToDate(_sunRotation * 365.0, _planetRotation*24.0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static double GetLocalTime(Vector3 surfacePoint, Vector3 sunPos, Node3D planet)
|
|
|
|
|
{
|
|
|
|
|
Vector3 center = planet.GlobalTransform.Origin;
|
|
|
|
|
Vector3 axis = -planet.GlobalTransform.Basis.Y.Normalized();
|
|
|
|
|
|
|
|
|
|
Vector3 surfaceDir = (surfacePoint - center).Normalized();
|
|
|
|
|
Vector3 sunDir = (sunPos - center).Normalized();
|
|
|
|
|
|
|
|
|
|
surfaceDir = (surfaceDir - axis * surfaceDir.Dot(axis)).Normalized();
|
|
|
|
|
sunDir = (sunDir - axis * sunDir.Dot(axis)).Normalized();
|
|
|
|
|
|
|
|
|
|
double angle = Mathf.Atan2(
|
|
|
|
|
axis.Dot(surfaceDir.Cross(sunDir)),
|
|
|
|
|
surfaceDir.Dot(sunDir)
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
double hours = Mathf.PosMod(angle / Mathf.Tau * 24.0 + 12.0, 24.0);
|
|
|
|
|
|
|
|
|
|
return hours;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public string DaysToDate(double days, double hours)
|
|
|
|
|
{
|
|
|
|
|
DateTime dt = new DateTime(1, 1, 1);
|
|
|
|
|
dt = dt.AddDays(days);
|
|
|
|
|
dt = dt.AddHours(hours);
|
|
|
|
|
return dt.ToString("'y'yyyy/'m'MM/'d'dd - 'h'HH':''m'mm':''s'ss");
|
|
|
|
|
}
|
|
|
|
|
public string DaysToTime(double days, double hours)
|
|
|
|
|
{
|
|
|
|
|
DateTime dt = new DateTime(1, 1, 1);
|
|
|
|
|
dt = dt.AddDays(days);
|
|
|
|
|
dt = dt.AddHours(hours);
|
|
|
|
|
return dt.ToString("'h'HH':''m'mm':''s'ss");
|
|
|
|
|
}
|
2026-03-02 15:04:15 +02:00
|
|
|
public void UpdateStats()
|
|
|
|
|
{
|
|
|
|
|
if (_vertex != null)
|
|
|
|
|
{
|
|
|
|
|
var height = -9000f * (0.5f - _vertex.Height) * 2f;
|
|
|
|
|
GetNode<Label>("%PointHeight").Text = $"{(height > 0 ? "+" : "")}{height:0000}M";
|
|
|
|
|
GetNode<Label>("%PointId").Text = $"{_vertex.Id:0000000}";
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
GetNode<Label>("%PointHeight").Text = "0000M";
|
|
|
|
|
GetNode<Label>("%PointId").Text = "0000000";
|
|
|
|
|
}
|
|
|
|
|
if (_plate != null)
|
|
|
|
|
{
|
|
|
|
|
GetNode<Label>("%PlateId").Text = $"{_plate.Id:00}";
|
|
|
|
|
GetNode<Label>("%IsLandform").Text = $"{(_plate.IsLandform ? "Y" : "N")}";
|
|
|
|
|
var area = (int)((float)_plate.Vertices.Count / _planetHelper.Vertices.Count * 100f);
|
|
|
|
|
GetNode<Label>("%Area").Text = $"{area:00}%";
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
GetNode<Label>("%PlateId").Text = "00";
|
|
|
|
|
GetNode<Label>("%IsLandform").Text = "U";
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void MakeGo()
|
|
|
|
|
{
|
2026-03-03 22:05:17 +02:00
|
|
|
_planetHelper = new PlanetHelper(_meshInstance, _textureRect, _remapCurve);
|
2026-03-02 15:04:15 +02:00
|
|
|
}
|
|
|
|
|
|
2026-03-03 07:34:13 +02:00
|
|
|
public void Advance()
|
|
|
|
|
{
|
|
|
|
|
_planetHelper.Advance = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void AutoRun()
|
|
|
|
|
{
|
|
|
|
|
_planetHelper.AutoRun = true;
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-03 08:51:52 +02:00
|
|
|
public void SaveImageValue()
|
|
|
|
|
{
|
|
|
|
|
Image img = Projector.RenderImage(_planetHelper);
|
|
|
|
|
DirectoryInfo d = new DirectoryInfo(ProjectSettings.GlobalizePath("user://"));
|
|
|
|
|
FileInfo[] files = d.GetFiles("Projection-*.png");
|
|
|
|
|
int max = 0;
|
|
|
|
|
foreach (FileInfo file in files)
|
|
|
|
|
{
|
|
|
|
|
int number = int.Parse(file.Name.Split('-')[1]);
|
|
|
|
|
max = Math.Max(max, number);
|
|
|
|
|
}
|
|
|
|
|
max += 1;
|
|
|
|
|
img.SavePng(ProjectSettings.GlobalizePath($"user://ProjectionValue-{max}-{Projector.Points.GetLength(0)}.png"));
|
|
|
|
|
}
|
|
|
|
|
public void SaveImageColor()
|
|
|
|
|
{
|
|
|
|
|
Image img = Projector.RenderImage(_planetHelper);
|
|
|
|
|
Vector2 size = img.GetSize();
|
|
|
|
|
for (int x = 0; x < size.X; x++)
|
|
|
|
|
{
|
|
|
|
|
for (int y = 0; y < size.Y; y++)
|
|
|
|
|
{
|
|
|
|
|
img.SetPixel(x,y, _gradient.Sample(img.GetPixel(x,y).R));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
DirectoryInfo d = new DirectoryInfo(ProjectSettings.GlobalizePath("user://"));
|
|
|
|
|
FileInfo[] files = d.GetFiles("ProjectionColor-*.png");
|
|
|
|
|
int max = 0;
|
|
|
|
|
foreach (FileInfo file in files)
|
|
|
|
|
{
|
|
|
|
|
int number = int.Parse(file.Name.Split('-')[1]);
|
|
|
|
|
max = Math.Max(max, number);
|
|
|
|
|
}
|
|
|
|
|
max += 1;
|
|
|
|
|
img.SavePng(ProjectSettings.GlobalizePath($"user://Projection-{max}-{Projector.Points.GetLength(0)}.png"));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void OpenFolder()
|
|
|
|
|
{
|
|
|
|
|
OS.ShellShowInFileManager(ProjectSettings.GlobalizePath("user://"));
|
|
|
|
|
}
|
2026-03-03 07:34:13 +02:00
|
|
|
public void ResolutionChange(String change)
|
|
|
|
|
{
|
|
|
|
|
change = new string(change.Where(c => char.IsDigit(c)).ToArray());
|
|
|
|
|
_resolution = Int32.Parse(change);
|
2026-03-03 22:05:17 +02:00
|
|
|
_resolution = Math.Clamp(_resolution, 4, 2048);
|
2026-03-03 07:34:13 +02:00
|
|
|
}
|
2026-03-03 20:02:39 +02:00
|
|
|
|
|
|
|
|
public void AxialTiltChanged(string value)
|
|
|
|
|
{
|
|
|
|
|
if (float.TryParse(value, out float tilt))
|
|
|
|
|
{
|
|
|
|
|
_meshInstance.RotateZ(-_meshInstance.Rotation.Z);
|
|
|
|
|
_meshInstance.RotateZ(Mathf.DegToRad(tilt));
|
|
|
|
|
_axialTilt = tilt;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private bool _sunRotating = false;
|
2026-03-07 10:29:28 +02:00
|
|
|
private double _sunRotation = 0.0;
|
2026-03-03 20:02:39 +02:00
|
|
|
private float _axialTilt = 0f;
|
|
|
|
|
public void SunAngleStart()
|
|
|
|
|
{
|
|
|
|
|
_sunRotating = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void SunAngleStop(bool changed)
|
|
|
|
|
{
|
|
|
|
|
_sunRotating = false;
|
|
|
|
|
}
|
2026-03-03 22:05:17 +02:00
|
|
|
private bool _planetRotating = false;
|
2026-03-07 10:29:28 +02:00
|
|
|
private double _planetRotation = 0.0;
|
2026-03-03 22:05:17 +02:00
|
|
|
public void PlanetAngleStart()
|
|
|
|
|
{
|
|
|
|
|
_planetRotating = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void PlanetAngleStop(bool changed)
|
|
|
|
|
{
|
|
|
|
|
_planetRotating = false;
|
|
|
|
|
}
|
2026-03-02 15:04:15 +02:00
|
|
|
}
|