blah
This commit is contained in:
parent
0c5085a7fa
commit
e28d0cd923
35 changed files with 0 additions and 2676 deletions
139
Main.cs
139
Main.cs
|
|
@ -1,139 +0,0 @@
|
|||
#nullable enable
|
||||
using Godot;
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using Godot.Collections;
|
||||
|
||||
public partial class Main : Control
|
||||
{
|
||||
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;
|
||||
|
||||
private PlanetHelper.VertexData? _vertex = null;
|
||||
private PlanetHelper.PlateData? _plate = null;
|
||||
|
||||
private PlanetHelper _planetHelper;
|
||||
public override void _Ready()
|
||||
{
|
||||
_planetHelper = new PlanetHelper(_meshInstance, _textureRect);
|
||||
UpdateStats();
|
||||
}
|
||||
|
||||
private const float RayLength = 1000.0f;
|
||||
public override void _Input(InputEvent @event)
|
||||
{
|
||||
if (@event is InputEventMouseButton mouseEvent)
|
||||
{
|
||||
if (mouseEvent.ButtonIndex == MouseButton.Left)
|
||||
{
|
||||
_moving = mouseEvent.Pressed;
|
||||
}
|
||||
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)
|
||||
{
|
||||
Projector.GatherPoints(_planetHelper);
|
||||
_textureRect.Texture = Projector.Render(_planetHelper);
|
||||
}
|
||||
}
|
||||
|
||||
public override void _Process(double delta)
|
||||
{
|
||||
if (Input.IsActionJustPressed("mouse_secondary"))
|
||||
{
|
||||
var from = _cameraNode.ProjectRayOrigin(GetViewport().GetMousePosition());
|
||||
var to = from + _cameraNode.ProjectRayNormal(GetViewport().GetMousePosition()) * RayLength;
|
||||
var result = World.GetWorld3D().DirectSpaceState.IntersectRay(PhysicsRayQueryParameters3D.Create(from, to));
|
||||
if (result.Count > 0)
|
||||
{
|
||||
Vector3? pos = result["position"].AsVector3();
|
||||
if (pos != null)
|
||||
{
|
||||
GD.Print($"Hit: '{pos}'");
|
||||
var closest = _planetHelper.Octree.SearchNearest(pos ?? Vector3.Zero)?.Id;
|
||||
if (closest != null)
|
||||
{
|
||||
_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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (Input.IsActionJustPressed("spacebar"))
|
||||
{
|
||||
_planetHelper.Advance = true;
|
||||
}
|
||||
if (Input.IsActionJustPressed("enter"))
|
||||
{
|
||||
_planetHelper.AutoRun = true;
|
||||
}
|
||||
_planetHelper.Process();
|
||||
}
|
||||
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()
|
||||
{
|
||||
_planetHelper = new PlanetHelper(_meshInstance, _textureRect);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1 +0,0 @@
|
|||
uid://bhpic251bgvgk
|
||||
199
MainScene.tscn
199
MainScene.tscn
|
|
@ -1,199 +0,0 @@
|
|||
[gd_scene format=3 uid="uid://csfh7ptgerpm2"]
|
||||
|
||||
[ext_resource type="Script" uid="uid://bhpic251bgvgk" path="res://Main.cs" id="1_rpqi1"]
|
||||
[ext_resource type="ArrayMesh" uid="uid://65modei4jwaj" path="res://PlanetBase.obj" id="2_rpqi1"]
|
||||
[ext_resource type="Material" uid="uid://k3teblrpopsb" path="res://map.tres" id="4_rf2cd"]
|
||||
[ext_resource type="Material" uid="uid://c55st036tapeo" path="res://planet.tres" id="4_uxrcv"]
|
||||
|
||||
[sub_resource type="ProceduralSkyMaterial" id="ProceduralSkyMaterial_uxrcv"]
|
||||
|
||||
[sub_resource type="Sky" id="Sky_tlwt5"]
|
||||
sky_material = SubResource("ProceduralSkyMaterial_uxrcv")
|
||||
|
||||
[sub_resource type="Environment" id="Environment_rf2cd"]
|
||||
background_mode = 2
|
||||
sky = SubResource("Sky_tlwt5")
|
||||
ambient_light_source = 3
|
||||
reflected_light_source = 2
|
||||
|
||||
[sub_resource type="CameraAttributesPractical" id="CameraAttributesPractical_a814b"]
|
||||
|
||||
[sub_resource type="SphereShape3D" id="SphereShape3D_rpqi1"]
|
||||
radius = 1.0
|
||||
|
||||
[node name="MainScene" type="Control" unique_id=1070682561 node_paths=PackedStringArray("_yawNode", "_pitchNode", "_cameraNode", "_meshInstance", "World", "_textureRect")]
|
||||
layout_mode = 3
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
script = ExtResource("1_rpqi1")
|
||||
_yawNode = NodePath("TabContainer/Planet/SubViewportContainer/SubViewport/ZaWarudo/Yaw")
|
||||
_pitchNode = NodePath("TabContainer/Planet/SubViewportContainer/SubViewport/ZaWarudo/Yaw/Pitch")
|
||||
_cameraNode = NodePath("TabContainer/Planet/SubViewportContainer/SubViewport/ZaWarudo/Yaw/Pitch/Camera3D")
|
||||
_meshInstance = NodePath("TabContainer/Planet/SubViewportContainer/SubViewport/ZaWarudo/Icosphere")
|
||||
World = NodePath("TabContainer/Planet/SubViewportContainer/SubViewport/ZaWarudo")
|
||||
_textureRect = NodePath("TabContainer/Projection")
|
||||
|
||||
[node name="TabContainer" type="TabContainer" parent="." unique_id=1586027287]
|
||||
layout_mode = 1
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
current_tab = 0
|
||||
|
||||
[node name="Planet" type="HBoxContainer" parent="TabContainer" unique_id=1786930306]
|
||||
layout_mode = 2
|
||||
metadata/_tab_index = 0
|
||||
|
||||
[node name="SubViewportContainer" type="SubViewportContainer" parent="TabContainer/Planet" unique_id=58474785]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
size_flags_stretch_ratio = 1.44
|
||||
stretch = true
|
||||
|
||||
[node name="SubViewport" type="SubViewport" parent="TabContainer/Planet/SubViewportContainer" unique_id=1257998631]
|
||||
handle_input_locally = false
|
||||
size = Vector2i(677, 617)
|
||||
render_target_update_mode = 4
|
||||
|
||||
[node name="ZaWarudo" type="Node3D" parent="TabContainer/Planet/SubViewportContainer/SubViewport" unique_id=937719505]
|
||||
|
||||
[node name="WorldEnvironment" type="WorldEnvironment" parent="TabContainer/Planet/SubViewportContainer/SubViewport/ZaWarudo" unique_id=610642146]
|
||||
environment = SubResource("Environment_rf2cd")
|
||||
camera_attributes = SubResource("CameraAttributesPractical_a814b")
|
||||
|
||||
[node name="DirectionalLight3D" type="DirectionalLight3D" parent="TabContainer/Planet/SubViewportContainer/SubViewport/ZaWarudo" unique_id=899426195]
|
||||
transform = Transform3D(0.88874525, -0.29675773, 0.3493804, 0, 0.7621714, 0.6473753, -0.45840138, -0.5753517, 0.6773762, 0, 0, 0)
|
||||
|
||||
[node name="Icosphere" type="MeshInstance3D" parent="TabContainer/Planet/SubViewportContainer/SubViewport/ZaWarudo" unique_id=793654005]
|
||||
transform = Transform3D(0.01, 0, 0, 0, 0.01, 0, 0, 0, 0.01, 0, 0, 0)
|
||||
mesh = ExtResource("2_rpqi1")
|
||||
surface_material_override/0 = ExtResource("4_uxrcv")
|
||||
|
||||
[node name="Yaw" type="Node3D" parent="TabContainer/Planet/SubViewportContainer/SubViewport/ZaWarudo" unique_id=2073443785]
|
||||
|
||||
[node name="Pitch" type="Node3D" parent="TabContainer/Planet/SubViewportContainer/SubViewport/ZaWarudo/Yaw" unique_id=98015529]
|
||||
|
||||
[node name="Camera3D" type="Camera3D" parent="TabContainer/Planet/SubViewportContainer/SubViewport/ZaWarudo/Yaw/Pitch" unique_id=1654616370]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 30)
|
||||
fov = 5.0
|
||||
|
||||
[node name="StaticBody3D" type="StaticBody3D" parent="TabContainer/Planet/SubViewportContainer/SubViewport/ZaWarudo" unique_id=330930041]
|
||||
|
||||
[node name="CollisionShape3D" type="CollisionShape3D" parent="TabContainer/Planet/SubViewportContainer/SubViewport/ZaWarudo/StaticBody3D" unique_id=299495076]
|
||||
shape = SubResource("SphereShape3D_rpqi1")
|
||||
|
||||
[node name="PanelContainer" type="PanelContainer" parent="TabContainer/Planet" unique_id=716838825]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
|
||||
[node name="MarginContainer" type="MarginContainer" parent="TabContainer/Planet/PanelContainer" unique_id=249859922]
|
||||
layout_mode = 2
|
||||
theme_override_constants/margin_left = 12
|
||||
theme_override_constants/margin_top = 12
|
||||
theme_override_constants/margin_right = 12
|
||||
theme_override_constants/margin_bottom = 12
|
||||
|
||||
[node name="VBoxContainer" type="VBoxContainer" parent="TabContainer/Planet/PanelContainer/MarginContainer" unique_id=654818970]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="Generate" type="Button" parent="TabContainer/Planet/PanelContainer/MarginContainer/VBoxContainer" unique_id=662365522]
|
||||
layout_mode = 2
|
||||
text = "Generate"
|
||||
|
||||
[node name="HSeparator" type="HSeparator" parent="TabContainer/Planet/PanelContainer/MarginContainer/VBoxContainer" unique_id=1862016318]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="Label2" type="Label" parent="TabContainer/Planet/PanelContainer/MarginContainer/VBoxContainer" unique_id=186838891]
|
||||
layout_mode = 2
|
||||
text = "- Point -"
|
||||
|
||||
[node name="HBoxContainer2" type="HBoxContainer" parent="TabContainer/Planet/PanelContainer/MarginContainer/VBoxContainer" unique_id=1345806516]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="Margin" type="VSeparator" parent="TabContainer/Planet/PanelContainer/MarginContainer/VBoxContainer/HBoxContainer2" unique_id=1465747985]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="Label" type="Label" parent="TabContainer/Planet/PanelContainer/MarginContainer/VBoxContainer/HBoxContainer2" unique_id=1300757041]
|
||||
layout_mode = 2
|
||||
theme_override_colors/font_color = Color(0.4922884, 0.49228835, 0.49228835, 1)
|
||||
text = "Point ID: "
|
||||
|
||||
[node name="PointId" type="Label" parent="TabContainer/Planet/PanelContainer/MarginContainer/VBoxContainer/HBoxContainer2" unique_id=1976947082]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
text = "-1"
|
||||
|
||||
[node name="VSeparator1" type="VSeparator" parent="TabContainer/Planet/PanelContainer/MarginContainer/VBoxContainer/HBoxContainer2" unique_id=746067660]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="Label1" type="Label" parent="TabContainer/Planet/PanelContainer/MarginContainer/VBoxContainer/HBoxContainer2" unique_id=1690535936]
|
||||
layout_mode = 2
|
||||
theme_override_colors/font_color = Color(0.4922884, 0.49228835, 0.49228835, 1)
|
||||
text = "Height:"
|
||||
|
||||
[node name="PointHeight" type="Label" parent="TabContainer/Planet/PanelContainer/MarginContainer/VBoxContainer/HBoxContainer2" unique_id=2115494397]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
text = "-1"
|
||||
|
||||
[node name="Label3" type="Label" parent="TabContainer/Planet/PanelContainer/MarginContainer/VBoxContainer" unique_id=1412781677]
|
||||
layout_mode = 2
|
||||
text = " - Plate -"
|
||||
|
||||
[node name="HBoxContainer" type="HBoxContainer" parent="TabContainer/Planet/PanelContainer/MarginContainer/VBoxContainer" unique_id=471041646]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="Margin" type="VSeparator" parent="TabContainer/Planet/PanelContainer/MarginContainer/VBoxContainer/HBoxContainer" unique_id=1514412102]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="Label1" type="Label" parent="TabContainer/Planet/PanelContainer/MarginContainer/VBoxContainer/HBoxContainer" unique_id=1070896445]
|
||||
layout_mode = 2
|
||||
theme_override_colors/font_color = Color(0.4922884, 0.49228835, 0.49228835, 1)
|
||||
text = "Plate ID:"
|
||||
|
||||
[node name="PlateId" type="Label" parent="TabContainer/Planet/PanelContainer/MarginContainer/VBoxContainer/HBoxContainer" unique_id=37820014]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
text = "-1"
|
||||
|
||||
[node name="VSeparator1" type="VSeparator" parent="TabContainer/Planet/PanelContainer/MarginContainer/VBoxContainer/HBoxContainer" unique_id=1699626450]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="Label2" type="Label" parent="TabContainer/Planet/PanelContainer/MarginContainer/VBoxContainer/HBoxContainer" unique_id=288487629]
|
||||
layout_mode = 2
|
||||
theme_override_colors/font_color = Color(0.4922884, 0.49228835, 0.49228835, 1)
|
||||
text = "Is Landform:"
|
||||
|
||||
[node name="IsLandform" type="Label" parent="TabContainer/Planet/PanelContainer/MarginContainer/VBoxContainer/HBoxContainer" unique_id=2089133484]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
text = "unknown"
|
||||
|
||||
[node name="VSeparator2" type="VSeparator" parent="TabContainer/Planet/PanelContainer/MarginContainer/VBoxContainer/HBoxContainer" unique_id=75061841]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="Label3" type="Label" parent="TabContainer/Planet/PanelContainer/MarginContainer/VBoxContainer/HBoxContainer" unique_id=1444405231]
|
||||
layout_mode = 2
|
||||
theme_override_colors/font_color = Color(0.4922884, 0.49228835, 0.49228835, 1)
|
||||
text = "Area:"
|
||||
|
||||
[node name="Area" type="Label" parent="TabContainer/Planet/PanelContainer/MarginContainer/VBoxContainer/HBoxContainer" unique_id=849006497]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
text = "0%"
|
||||
|
||||
[node name="Projection" type="TextureRect" parent="TabContainer" unique_id=155743781]
|
||||
visible = false
|
||||
material = ExtResource("4_rf2cd")
|
||||
layout_mode = 2
|
||||
expand_mode = 1
|
||||
stretch_mode = 5
|
||||
metadata/_tab_index = 1
|
||||
|
||||
[connection signal="tab_changed" from="TabContainer" to="." method="Tab"]
|
||||
[connection signal="pressed" from="TabContainer/Planet/PanelContainer/MarginContainer/VBoxContainer/Generate" to="." method="MakeGo"]
|
||||
147
Oct.cs
147
Oct.cs
|
|
@ -1,147 +0,0 @@
|
|||
#nullable enable
|
||||
using System.Collections.Generic;
|
||||
using Godot;
|
||||
|
||||
namespace adatonic;
|
||||
|
||||
public class Node
|
||||
{
|
||||
public Node()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public Node(int id, Vector3 pos)
|
||||
{
|
||||
Id = id;
|
||||
Position = pos;
|
||||
}
|
||||
public int Id;
|
||||
public Vector3 Position;
|
||||
}
|
||||
|
||||
public class Oct
|
||||
{
|
||||
private Vector3 Start;
|
||||
private Vector3 Extent;
|
||||
|
||||
Oct?[]? Trees = null;
|
||||
private Node? Node = null;
|
||||
public Oct()
|
||||
{
|
||||
Start = -Vector3.One;
|
||||
Extent = Vector3.One * 2f;
|
||||
}
|
||||
public Oct(Vector3 start, Vector3 extent)
|
||||
{
|
||||
Start = start;
|
||||
Extent = extent;
|
||||
}
|
||||
|
||||
public void Insert(Node node)
|
||||
{
|
||||
if (!IsInside(node.Position))
|
||||
{
|
||||
GD.Print($"Failed to insert to Octree - Point out of bounds!");
|
||||
return;
|
||||
}
|
||||
|
||||
if (Node == null && Trees == null)
|
||||
{
|
||||
Node = node;
|
||||
return;
|
||||
}
|
||||
Trees ??= new Oct?[8];
|
||||
if (Node != null)
|
||||
{
|
||||
int octOld = WhichSubOct(Node.Position);
|
||||
|
||||
Trees[octOld] ??= new Oct(GetSubStart(octOld), Extent * 0.5f);
|
||||
Trees[octOld]?.Insert(Node);
|
||||
Node = null;
|
||||
}
|
||||
|
||||
int oct = WhichSubOct(node.Position);
|
||||
Trees[oct] ??= new Oct(GetSubStart(oct), Extent * 0.5f);
|
||||
Trees[oct]?.Insert(node);
|
||||
}
|
||||
public Node? SearchNearest(Vector3 position)
|
||||
{
|
||||
Node? best = null;
|
||||
float bestDist = float.MaxValue;
|
||||
|
||||
SearchNearest(position, ref best, ref bestDist);
|
||||
return best;
|
||||
}
|
||||
private void SearchNearest(Vector3 position, ref Node? best, ref float bestDist)
|
||||
{
|
||||
// Check stored node
|
||||
if (Node != null)
|
||||
{
|
||||
float dist = position.DistanceSquaredTo(Node.Position);
|
||||
if (dist < bestDist)
|
||||
{
|
||||
bestDist = dist;
|
||||
best = Node;
|
||||
}
|
||||
}
|
||||
|
||||
if (Trees == null)
|
||||
return;
|
||||
|
||||
int first = WhichSubOct(position);
|
||||
|
||||
// Search containing octant first
|
||||
Trees[first]?.SearchNearest(position, ref best, ref bestDist);
|
||||
|
||||
// Check other octants only if their bounds could contain closer points
|
||||
for (int i = 0; i < 8; i++)
|
||||
{
|
||||
if (i == first || Trees[i] == null)
|
||||
continue;
|
||||
|
||||
float boxDist = Trees[i]!.DistanceToBox(position);
|
||||
|
||||
if (boxDist < bestDist)
|
||||
{
|
||||
Trees[i]!.SearchNearest(position, ref best, ref bestDist);
|
||||
}
|
||||
}
|
||||
}
|
||||
private float DistanceToBox(Vector3 p)
|
||||
{
|
||||
Vector3 min = Start;
|
||||
Vector3 max = Start + Extent;
|
||||
|
||||
float dx = Mathf.Max(Mathf.Max(min.X - p.X, 0), p.X - max.X);
|
||||
float dy = Mathf.Max(Mathf.Max(min.Y - p.Y, 0), p.Y - max.Y);
|
||||
float dz = Mathf.Max(Mathf.Max(min.Z - p.Z, 0), p.Z - max.Z);
|
||||
|
||||
return dx * dx + dy * dy + dz * dz;
|
||||
}
|
||||
|
||||
public int WhichSubOct(Vector3 position)
|
||||
{
|
||||
bool left = position.X < Start.X + Extent.X * 0.5f;
|
||||
bool bottom = position.Y < Start.Y + Extent.Y * 0.5f;
|
||||
bool near = position.Z < Start.Z + Extent.Z * 0.5f;
|
||||
|
||||
return (left ? 1 : 0) + (bottom ? 2 : 0) + (near ? 4 : 0);
|
||||
}
|
||||
|
||||
public Vector3 GetSubStart(int oct)
|
||||
{
|
||||
Vector3 start = Vector3.Zero;
|
||||
bool left = (oct & (1 << 0)) != 0;
|
||||
bool bottom = (oct & (1 << 1)) != 0;
|
||||
bool near = (oct & (1 << 2)) != 0;
|
||||
start.X += left ? Start.X : Start.X + Extent.X * 0.5f;
|
||||
start.Y += bottom ? Start.Y : Start.Y + Extent.Y * 0.5f;
|
||||
start.Z += near ? Start.Z : Start.Z + Extent.Z * 0.5f;
|
||||
return start;
|
||||
}
|
||||
public bool IsInside(Vector3 position)
|
||||
{
|
||||
return position > Start && position < Start + Extent;
|
||||
}
|
||||
}
|
||||
|
|
@ -1 +0,0 @@
|
|||
uid://c4pay0n8iktfa
|
||||
BIN
PlanetBase.glb
(Stored with Git LFS)
BIN
PlanetBase.glb
(Stored with Git LFS)
Binary file not shown.
|
|
@ -1,54 +0,0 @@
|
|||
[remap]
|
||||
|
||||
importer="scene"
|
||||
importer_version=1
|
||||
type="PackedScene"
|
||||
uid="uid://cdtbfiae03msw"
|
||||
path="res://.godot/imported/PlanetBase.glb-3483dcc96f97564a1e6a70b40e920696.scn"
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://PlanetBase.glb"
|
||||
dest_files=["res://.godot/imported/PlanetBase.glb-3483dcc96f97564a1e6a70b40e920696.scn"]
|
||||
|
||||
[params]
|
||||
|
||||
nodes/root_type=""
|
||||
nodes/root_name=""
|
||||
nodes/root_script=null
|
||||
nodes/apply_root_scale=true
|
||||
nodes/root_scale=1.0
|
||||
nodes/import_as_skeleton_bones=false
|
||||
nodes/use_name_suffixes=true
|
||||
nodes/use_node_type_suffixes=true
|
||||
meshes/ensure_tangents=true
|
||||
meshes/generate_lods=false
|
||||
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
|
||||
animation/import_rest_as_RESET=false
|
||||
import_script/path=""
|
||||
materials/extract=0
|
||||
materials/extract_format=0
|
||||
materials/extract_path=""
|
||||
_subresources={
|
||||
"meshes": {
|
||||
"PlanetBase_Icosphere": {
|
||||
"generate/lightmap_uv": 2,
|
||||
"generate/lods": 2,
|
||||
"generate/shadow_meshes": 2,
|
||||
"lods/normal_merge_angle": 20.0,
|
||||
"save_to_file/enabled": true,
|
||||
"save_to_file/fallback_path": "res://sphere.tres",
|
||||
"save_to_file/path": "uid://2kh0vgcigdse"
|
||||
}
|
||||
}
|
||||
}
|
||||
gltf/naming_version=2
|
||||
gltf/embedded_image_handling=1
|
||||
BIN
PlanetBase.obj
(Stored with Git LFS)
BIN
PlanetBase.obj
(Stored with Git LFS)
Binary file not shown.
|
|
@ -1,25 +0,0 @@
|
|||
[remap]
|
||||
|
||||
importer="wavefront_obj"
|
||||
importer_version=1
|
||||
type="Mesh"
|
||||
uid="uid://65modei4jwaj"
|
||||
path="res://.godot/imported/PlanetBase.obj-358ab826c2ee2335ab43b222bdbd88b9.mesh"
|
||||
|
||||
[deps]
|
||||
|
||||
files=["res://.godot/imported/PlanetBase.obj-358ab826c2ee2335ab43b222bdbd88b9.mesh"]
|
||||
|
||||
source_file="res://PlanetBase.obj"
|
||||
dest_files=["res://.godot/imported/PlanetBase.obj-358ab826c2ee2335ab43b222bdbd88b9.mesh", "res://.godot/imported/PlanetBase.obj-358ab826c2ee2335ab43b222bdbd88b9.mesh"]
|
||||
|
||||
[params]
|
||||
|
||||
generate_tangents=true
|
||||
generate_lods=true
|
||||
generate_shadow_mesh=true
|
||||
generate_lightmap_uv2=false
|
||||
generate_lightmap_uv2_texel_size=0.2
|
||||
scale_mesh=Vector3(1, 1, 1)
|
||||
offset_mesh=Vector3(0, 0, 0)
|
||||
force_disable_mesh_compression=false
|
||||
676
PlanetFormer.cs
676
PlanetFormer.cs
|
|
@ -1,676 +0,0 @@
|
|||
using Godot;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
public partial class PlanetFormer : MeshInstance3D
|
||||
{
|
||||
[Export] private FastNoiseLite ContinentalNoise;
|
||||
[Export] private FastNoiseLite MountainNoise;
|
||||
[Export] private FastNoiseLite HFNoise;
|
||||
[Export] private int _plateCount = 14;
|
||||
[Export] private float _landRatio = 0.4f;
|
||||
|
||||
MeshDataTool mdt = new MeshDataTool();
|
||||
public enum GenerationStage
|
||||
{
|
||||
NotStarted,
|
||||
Initialization,
|
||||
PlateExpansion,
|
||||
BorderSearch,
|
||||
CalculateEdgeDistance,
|
||||
CalculateEdgeStress,
|
||||
BorderExpansion,
|
||||
HeightCalc,
|
||||
Completed,
|
||||
}
|
||||
|
||||
public GenerationStage StopAt = GenerationStage.Completed;
|
||||
public class PlateData(int Id = 0, Color Color = new(), bool IsLandform = false, List<int> Vertices = null)
|
||||
{
|
||||
public int Id { get; set; } = Id;
|
||||
public Color Color { get; set; } = Color;
|
||||
public bool IsLandform { get; set; } = IsLandform;
|
||||
public List<int> Vertices { get; set; } = Vertices;
|
||||
|
||||
public int CenterVertexId = -1;
|
||||
public float PlateExpansion { get; set; } = RandF(0.5f, 2f);
|
||||
public Vector3 Dir { get; set; } = Vector3.Zero;
|
||||
}
|
||||
|
||||
public class VertexData(int Id = 0, int PlateId = 0, List<int> Neighbours = null, bool StageComplete = false)
|
||||
{
|
||||
public int Id { get; set; } = Id;
|
||||
public int PlateId { get; set; } = PlateId;
|
||||
|
||||
public List<StrainAnalysis> StrainSamples { get; set; } = new();
|
||||
public List<int> Neighbours { get; set; } = Neighbours;
|
||||
public bool StageComplete { get; set; } = StageComplete;
|
||||
public bool IsEdge = false;
|
||||
public bool IsTypeEdge = false;
|
||||
public float EdgeDistance = -1f;
|
||||
public float Height = 0f;
|
||||
}
|
||||
public enum StrainType
|
||||
{
|
||||
Tension, // Pulling apart
|
||||
Compression, // Pushing together
|
||||
Shear // Sliding past each other
|
||||
}
|
||||
public class StrainAnalysis
|
||||
{
|
||||
public float Magnitude; // Total magnitude of the strain
|
||||
public StrainType Type; // The dominant type of force
|
||||
public float NormalRate; // Rate of convergence/divergence
|
||||
public float ShearRate; // Rate of sliding
|
||||
}
|
||||
public List<PlateData> Plates = new List<PlateData>();
|
||||
public List<VertexData> Vertices = new List<VertexData>();
|
||||
|
||||
|
||||
private MeshInstance3D MeshInstance;
|
||||
private ArrayMesh arrayMesh;
|
||||
private GenerationStage Stage = GenerationStage.NotStarted;
|
||||
|
||||
Stopwatch _generationStopwatch = new Stopwatch();
|
||||
public static float RandF(float min, float max)
|
||||
{
|
||||
return min + (max - min) * Random.Shared.NextSingle();
|
||||
}
|
||||
|
||||
public override void _Ready()
|
||||
{
|
||||
MeshInstance = this;
|
||||
if (MeshInstance.Mesh is ArrayMesh mesh)
|
||||
{
|
||||
arrayMesh = mesh;
|
||||
}
|
||||
if (MeshInstance.GetSurfaceOverrideMaterial(0) is ShaderMaterial shaderMaterial)
|
||||
{
|
||||
shaderMaterial.SetShaderParameter("mode", 1);
|
||||
}
|
||||
}
|
||||
|
||||
public void InitializeGeneration()
|
||||
{
|
||||
GD.Print("Starting Generation!");
|
||||
Plates = new();
|
||||
Vertices = new();
|
||||
ChangeStage(GenerationStage.Initialization);
|
||||
|
||||
mdt.CreateFromSurface(arrayMesh, 0);
|
||||
|
||||
for (int i = 0; i < mdt.GetVertexCount(); i++)
|
||||
{
|
||||
// Init to black
|
||||
mdt.SetVertexColor(i, Colors.Black);
|
||||
|
||||
Vertices.Add(new VertexData(i, -1,GetNeighboringVertices(i, false).OrderBy(v => Guid.NewGuid()).ToList()));
|
||||
}
|
||||
|
||||
// Initialize Plates
|
||||
for (int i = 0; i < _plateCount; i++)
|
||||
{
|
||||
// Get a random un-assigned vertex.
|
||||
VertexData vertex = Vertices.Where(v => v.PlateId == -1).OrderBy(v => Guid.NewGuid()).First();
|
||||
vertex.PlateId = i;
|
||||
var color = new Color(RandF(0f, 1f), RandF(0f, 1f), RandF(0f, 1f));
|
||||
ColorVertex(vertex.Id, color);
|
||||
PlateData plate = new PlateData(i, color, false, [vertex.Id]);
|
||||
|
||||
plate.Dir = GetRandomTangentialVelocity(mdt.GetVertex(vertex.Id), RandF(0f, 1f));
|
||||
Plates.Add(plate);
|
||||
}
|
||||
ChangeStage(GenerationStage.PlateExpansion);
|
||||
|
||||
}
|
||||
public void AssignOceanPlates(List<PlateData> areas)
|
||||
{
|
||||
int n = areas.Count;
|
||||
double totalArea = areas.Sum(a => a.Vertices.Count * a.PlateExpansion);
|
||||
double targetOcean = totalArea * _landRatio;
|
||||
|
||||
double bestDiff = double.MaxValue;
|
||||
int bestMask = 0;
|
||||
|
||||
int combinations = 1 << n;
|
||||
|
||||
for (int mask = 0; mask < combinations; mask++)
|
||||
{
|
||||
int oceanArea = 0;
|
||||
|
||||
for (int i = 0; i < n; i++)
|
||||
{
|
||||
if ((mask & (1 << i)) != 0)
|
||||
oceanArea += (int)(areas[i].Vertices.Count * areas[i].PlateExpansion);
|
||||
}
|
||||
|
||||
double diff = Math.Abs(oceanArea - targetOcean);
|
||||
|
||||
if (diff < bestDiff)
|
||||
{
|
||||
bestDiff = diff;
|
||||
bestMask = mask;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < n; i++)
|
||||
{
|
||||
areas[i].IsLandform = (bestMask & (1 << i)) != 0;
|
||||
Color color = GetInitialColor(areas[i].IsLandform);
|
||||
areas[i].Color = color;
|
||||
foreach (int v in areas[i].Vertices)
|
||||
{
|
||||
ColorVertex(v, color);
|
||||
}
|
||||
}
|
||||
}
|
||||
public override void _Process(double delta)
|
||||
{
|
||||
if (Input.IsActionJustPressed("spacebar"))
|
||||
{
|
||||
InitializeGeneration();
|
||||
if (MeshInstance.GetSurfaceOverrideMaterial(0) is ShaderMaterial shaderMaterial)
|
||||
{
|
||||
shaderMaterial.SetShaderParameter("mode", 1);
|
||||
}
|
||||
}
|
||||
|
||||
if (Stage is GenerationStage.Completed or GenerationStage.NotStarted)
|
||||
return;
|
||||
List<VertexData> availableVerts;
|
||||
switch (Stage)
|
||||
{
|
||||
default:
|
||||
case GenerationStage.Initialization:
|
||||
break;
|
||||
case GenerationStage.PlateExpansion:
|
||||
availableVerts = Vertices.Where(d => d.StageComplete == false && d.PlateId != -1).OrderBy(v => Guid.NewGuid()).ToList();
|
||||
foreach (PlateData plateData in Plates)
|
||||
{
|
||||
var plateVerts = availableVerts.Where(d => d.PlateId == plateData.Id);
|
||||
foreach (VertexData vertexData in plateVerts.Take((int)((5 + plateVerts.Count() / 4) * plateData.PlateExpansion)))
|
||||
{
|
||||
int expandTo = GetFreeNeighbourIndex(vertexData);
|
||||
if (expandTo != -1)
|
||||
{
|
||||
Vertices[expandTo].PlateId = plateData.Id;
|
||||
plateData.Vertices.Add(expandTo);
|
||||
ColorVertex(expandTo, plateData.Color);
|
||||
}
|
||||
else
|
||||
{
|
||||
vertexData.StageComplete = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!availableVerts.Any())
|
||||
{
|
||||
foreach (VertexData vertexData in Vertices)
|
||||
vertexData.StageComplete = false;
|
||||
AssignOceanPlates(Plates);
|
||||
ChangeStage(GenerationStage.BorderSearch);
|
||||
}
|
||||
break;
|
||||
case GenerationStage.BorderSearch:
|
||||
availableVerts = Vertices.Where(d => d.StageComplete == false).Take(2500).ToList();
|
||||
foreach (VertexData vertexData in availableVerts)
|
||||
{
|
||||
// Do we have any neighbours of another plate?
|
||||
var neighbours = GetNeighboringVertices(vertexData.Id, false).ToList();
|
||||
if (neighbours
|
||||
.Any(v => Vertices[v].PlateId != vertexData.PlateId))
|
||||
{
|
||||
vertexData.IsEdge = true;
|
||||
vertexData.IsTypeEdge = neighbours.Any(n => Plates[Vertices[n].PlateId].IsLandform != Plates[vertexData.PlateId].IsLandform);
|
||||
if (vertexData.IsTypeEdge)
|
||||
vertexData.EdgeDistance = 1f;
|
||||
ColorVertex(vertexData.Id, vertexData.IsTypeEdge ? Colors.White : Colors.Black);
|
||||
}
|
||||
else
|
||||
{
|
||||
ColorVertex(vertexData.Id, Plates[vertexData.PlateId].Color);
|
||||
}
|
||||
vertexData.StageComplete = true;
|
||||
}
|
||||
|
||||
if (!availableVerts.Any())
|
||||
{
|
||||
foreach (VertexData vertexData in Vertices)
|
||||
vertexData.StageComplete = false;
|
||||
ChangeStage(GenerationStage.CalculateEdgeDistance);
|
||||
}
|
||||
break;
|
||||
case GenerationStage.CalculateEdgeDistance:
|
||||
availableVerts = Vertices.Where(d => d.StageComplete == false && d.EdgeDistance > 0f).OrderBy(v => v.EdgeDistance).Take(2500).ToList();
|
||||
foreach (VertexData vertexData in availableVerts)
|
||||
{
|
||||
var neighbours = GetNeighboringVertices(vertexData.Id, false).ToList();
|
||||
foreach (int neighbour in neighbours)
|
||||
{
|
||||
if (Vertices[neighbour].EdgeDistance > 0f && Vertices[neighbour].EdgeDistance < vertexData.EdgeDistance + 1f)
|
||||
continue;
|
||||
VertexData neighbourVert = Vertices[neighbour];
|
||||
neighbourVert.EdgeDistance = vertexData.EdgeDistance + 1f;
|
||||
ColorVertex(neighbourVert.Id, Plates[vertexData.PlateId].Color * 0.8f);
|
||||
}
|
||||
vertexData.StageComplete = true;
|
||||
}
|
||||
|
||||
if (!availableVerts.Any())
|
||||
{
|
||||
float maxDistance = Vertices.Max(v => v.EdgeDistance);
|
||||
foreach (VertexData vertexData in Vertices)
|
||||
{
|
||||
vertexData.EdgeDistance /= maxDistance;
|
||||
}
|
||||
|
||||
foreach (PlateData plateData in Plates)
|
||||
{
|
||||
plateData.CenterVertexId =
|
||||
Vertices.Where(v => v.PlateId == plateData.Id).MaxBy(v => v.EdgeDistance).Id;
|
||||
}
|
||||
foreach (VertexData vertexData in Vertices)
|
||||
vertexData.StageComplete = false;
|
||||
ChangeStage(GenerationStage.CalculateEdgeStress);
|
||||
}
|
||||
break;
|
||||
case GenerationStage.CalculateEdgeStress:
|
||||
availableVerts = Vertices.Where(d => d.StageComplete == false && d.IsEdge).Take(2500).ToList();
|
||||
foreach (VertexData vertexData in availableVerts)
|
||||
{
|
||||
var neighbours = GetNeighboringVertices(vertexData.Id, false).ToList();
|
||||
foreach (int neighbour in neighbours)
|
||||
{
|
||||
if (!Vertices[neighbour].IsEdge)
|
||||
continue;
|
||||
if (Vertices[neighbour].PlateId == vertexData.PlateId)
|
||||
continue;
|
||||
PlateData plateA = Plates[vertexData.PlateId];
|
||||
PlateData plateB = Plates[Vertices[neighbour].PlateId];
|
||||
VertexData centerA = Vertices[plateA.CenterVertexId];
|
||||
VertexData centerB = Vertices[plateB.CenterVertexId];
|
||||
Vector3 p1, p2;
|
||||
p1 = mdt.GetVertex(vertexData.Id).Cross(mdt.GetVertex(centerA.Id));
|
||||
p2 = mdt.GetVertex(neighbour).Cross(mdt.GetVertex(centerB.Id));
|
||||
vertexData.StrainSamples.Add(CalculateStrainMagnitude(p1, p2, plateA.Dir, plateB.Dir));
|
||||
}
|
||||
vertexData.StageComplete = true;
|
||||
var majorStrain = AverageStrainList(vertexData.StrainSamples);
|
||||
switch (majorStrain.Type)
|
||||
{
|
||||
case StrainType.Compression:
|
||||
ColorVertex(vertexData.Id, Colors.Red * majorStrain.Magnitude);
|
||||
break;
|
||||
case StrainType.Shear:
|
||||
ColorVertex(vertexData.Id, Colors.Yellow * majorStrain.Magnitude);
|
||||
break;
|
||||
case StrainType.Tension:
|
||||
ColorVertex(vertexData.Id, Colors.Blue * majorStrain.Magnitude);
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (!availableVerts.Any())
|
||||
{
|
||||
foreach (VertexData vertexData in Vertices)
|
||||
vertexData.StageComplete = false;
|
||||
ChangeStage(GenerationStage.BorderExpansion);
|
||||
}
|
||||
break;
|
||||
case GenerationStage.BorderExpansion:
|
||||
availableVerts = Vertices.Where(d => d.StageComplete == false && d.IsEdge && d.StrainSamples.Any()).OrderBy(d => Mathf.Abs(d.StrainSamples.Max(s => s.Magnitude))).Take(2500).ToList();
|
||||
foreach (VertexData vertexData in availableVerts)
|
||||
{
|
||||
var neighbours = GetNeighboringVertices(vertexData.Id, false).ToList();
|
||||
var majorStrain = AverageStrainList(vertexData.StrainSamples);
|
||||
foreach (int neighbour in neighbours)
|
||||
{
|
||||
VertexData neighbourVert = Vertices[neighbour];
|
||||
neighbourVert.IsEdge = true;
|
||||
|
||||
var newStrain = new StrainAnalysis();
|
||||
newStrain.Magnitude = majorStrain.Magnitude * 0.9f;
|
||||
newStrain.Type = majorStrain.Type;
|
||||
newStrain.NormalRate = majorStrain.NormalRate * 0.9f;
|
||||
newStrain.ShearRate = majorStrain.ShearRate * 0.9f;
|
||||
neighbourVert.StrainSamples.Add(newStrain);
|
||||
var newAverage = AverageStrainList(neighbourVert.StrainSamples);;
|
||||
switch (majorStrain.Type)
|
||||
{
|
||||
case StrainType.Compression:
|
||||
ColorVertex(neighbourVert.Id, Colors.Red * newAverage.Magnitude);
|
||||
break;
|
||||
case StrainType.Shear:
|
||||
ColorVertex(neighbourVert.Id, Colors.Yellow * newAverage.Magnitude);
|
||||
break;
|
||||
case StrainType.Tension:
|
||||
ColorVertex(neighbourVert.Id, Colors.Blue * newAverage.Magnitude);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (neighbours.All(n => Vertices[n].IsEdge))
|
||||
{
|
||||
vertexData.StageComplete = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!availableVerts.Any())
|
||||
{
|
||||
foreach (VertexData vertexData in Vertices)
|
||||
{
|
||||
vertexData.StageComplete = false;
|
||||
ColorVertex(vertexData.Id, Colors.White * vertexData.Height);
|
||||
}
|
||||
ChangeStage(GenerationStage.HeightCalc);
|
||||
}
|
||||
break;
|
||||
case GenerationStage.HeightCalc:
|
||||
availableVerts = Vertices.Where(d => d.StageComplete == false).Take(2500).ToList();
|
||||
foreach (VertexData vertexData in availableVerts)
|
||||
{
|
||||
PlateData plate = Plates[vertexData.PlateId];
|
||||
float continentalNoise = ContinentalNoise.GetNoise3Dv(GetVertexPosition(vertexData.Id));
|
||||
float mountainNoise = (1.0f + MountainNoise.GetNoise3Dv(GetVertexPosition(vertexData.Id))) * 0.5f;
|
||||
float hfNoise = HFNoise.GetNoise3Dv(GetVertexPosition(vertexData.Id));
|
||||
var majorStrain = AverageStrainList(vertexData.StrainSamples);
|
||||
var normalRate = -majorStrain.NormalRate * majorStrain.Magnitude * (plate.IsLandform ? 1f : 0.5f);
|
||||
var edgeDistance = vertexData.EdgeDistance * (plate.IsLandform ? 1f : -1f);
|
||||
float height = 0.5f;
|
||||
//height *= plate.PlateExpansion;
|
||||
float mult = 2f;
|
||||
height += hfNoise;
|
||||
height = (height + 0.5f * mult) / (1f + mult);
|
||||
height += continentalNoise;
|
||||
height = (height + 0.5f * mult) / (1f + mult);
|
||||
height += edgeDistance * 0.25f;
|
||||
height = (height + 0.5f * mult) / (1f + mult);
|
||||
height += normalRate * 0.35f;
|
||||
height = Mathf.Clamp(height, 0.01f, 0.99f);
|
||||
ColorVertex(vertexData.Id, Colors.White * height);
|
||||
vertexData.StageComplete = true;
|
||||
vertexData.Height = height;
|
||||
}
|
||||
|
||||
if (!availableVerts.Any())
|
||||
{
|
||||
GD.Print($"Heights - min:'{Vertices.Min(v => v.Height)}' - max:'{Vertices.Max(v => v.Height)}' - average:'{Vertices.Average(v => v.Height)}'");
|
||||
ScaleValues(Vertices);
|
||||
foreach (VertexData vertexData in Vertices)
|
||||
ColorVertex(vertexData.Id, Colors.White * vertexData.Height);
|
||||
float oceanPercentage = Vertices.Count(v => v.Height < 0.5f) / (float)Vertices.Count;
|
||||
GD.Print($"Ocean Percentage:'{oceanPercentage}'");
|
||||
ChangeStage(GenerationStage.Completed);
|
||||
if (MeshInstance.GetSurfaceOverrideMaterial(0) is ShaderMaterial shaderMaterial)
|
||||
{
|
||||
shaderMaterial.SetShaderParameter("mode", 2);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
UpdateMesh();
|
||||
}
|
||||
|
||||
public Vector3 GetRandomTangentialVelocity(Vector3 pointOnSphere, float speed)
|
||||
{
|
||||
// 1. Normalize the input point to ensure it represents the normal vector
|
||||
// (If your sphere is not at 0,0,0, use the vector from center to point)
|
||||
Vector3 normal = pointOnSphere.Normalized();
|
||||
|
||||
// 2. Generate a random vector
|
||||
Random rand = new Random();
|
||||
Vector3 randomVec = new Vector3(
|
||||
(float)(rand.NextDouble() - 0.5), // Range -0.5 to 0.5
|
||||
(float)(rand.NextDouble() - 0.5),
|
||||
(float)(rand.NextDouble() - 0.5)
|
||||
);
|
||||
|
||||
// 3. Calculate the tangent using Cross Product
|
||||
// Cross Product of (randomVec, normal) gives a vector perpendicular to both.
|
||||
// Since it is perpendicular to the normal, it is tangential to the sphere.
|
||||
Vector3 tangent = randomVec.Cross(normal);
|
||||
|
||||
// 4. Edge Case Handling
|
||||
// If the random vector happens to be parallel to the normal,
|
||||
// the cross product will result in a zero vector.
|
||||
if (tangent.Dot(tangent) < 1e-6f)
|
||||
{
|
||||
// If parallel, force a non-zero vector by modifying the input slightly
|
||||
// or simply recurse (try again). Here we force a modification for safety.
|
||||
randomVec = new Vector3(0, 1, 0);
|
||||
tangent = randomVec.Cross(normal);
|
||||
}
|
||||
|
||||
// 5. Normalize to ensure unit length
|
||||
Vector3 normalizedTangent = tangent.Normalized();
|
||||
|
||||
// 6. Scale by the desired speed
|
||||
return normalizedTangent * speed;
|
||||
}
|
||||
public void ScaleValues(List<VertexData> values)
|
||||
{
|
||||
float maxDistance = Vertices.Max(s => Mathf.Abs(s.Height - 0.5f));
|
||||
float scale = 0.5f/maxDistance;
|
||||
|
||||
values.ForEach(v => v.Height = Mathf.Clamp(0.5f + (v.Height - 0.5f) * scale, 0.01f, 0.99f));
|
||||
GD.Print($"Heights Post Scaling - min:'{Vertices.Min(v => v.Height)}' - max:'{Vertices.Max(v => v.Height)}' - average:'{Vertices.Average(v => v.Height)}'");
|
||||
}
|
||||
public void Normalize()
|
||||
{
|
||||
float min = Vertices.Min(s => s.Height);
|
||||
float max = Vertices.Max(s => s.Height);
|
||||
float mult = 0f;
|
||||
if (1f - max > min)
|
||||
{
|
||||
// closer to max
|
||||
mult = 1f / max;
|
||||
}
|
||||
else
|
||||
{
|
||||
// closer to min
|
||||
mult = 1f / 1f + min;
|
||||
}
|
||||
foreach (var v in Vertices)
|
||||
{
|
||||
v.Height *= mult;
|
||||
}
|
||||
GD.Print($"Heights Post Normalization - min:'{Vertices.Min(v => v.Height)}' - max:'{Vertices.Max(v => v.Height)}' - average:'{Vertices.Average(v => v.Height)}'");
|
||||
}
|
||||
public StrainAnalysis CalculateStrainMagnitude(Vector3 p1, Vector3 p2, Vector3 v1, Vector3 v2)
|
||||
{
|
||||
StrainAnalysis result = new StrainAnalysis();
|
||||
|
||||
// 1. Geometry and Relative Velocity
|
||||
Vector3 edge = p2 - p1;
|
||||
float edgeLength = edge.Length();
|
||||
|
||||
// If points are identical, strain is zero
|
||||
if (edgeLength < float.Epsilon)
|
||||
{
|
||||
result.Magnitude = 0;
|
||||
result.Type = StrainType.Shear; // Default
|
||||
return result;
|
||||
}
|
||||
|
||||
Vector3 relVelocity = v2 - v1;
|
||||
float relVelMag = relVelocity.Length();
|
||||
|
||||
// 2. Calculate Components
|
||||
// Normal component: How much they are moving parallel to the edge (Pulling apart/Pushing together)
|
||||
// We project relVelocity onto edge vector
|
||||
float dot = relVelocity.Dot(edge);
|
||||
result.NormalRate = dot / edgeLength;
|
||||
|
||||
// Shear component: How much they are moving perpendicular to the edge (Sliding)
|
||||
// Formula: v_tangent = sqrt(|v|^2 - (v_normal)^2)
|
||||
// Note: We use the magnitude of the relVelocity for the subtraction to avoid float errors
|
||||
float normalRateSq = result.NormalRate * result.NormalRate;
|
||||
float shearRateSq = relVelMag * relVelMag - normalRateSq;
|
||||
result.ShearRate = (shearRateSq > 0) ? (float)Math.Sqrt(shearRateSq) : 0;
|
||||
|
||||
// 3. Determine Magnitude
|
||||
// Total strain = sqrt(normal^2 + shear^2)
|
||||
result.Magnitude = (float)Math.Sqrt(normalRateSq + shearRateSq);
|
||||
|
||||
// 4. Classification Logic
|
||||
// Compare the absolute values to see which force is "dominant"
|
||||
float absNormal = Math.Abs(result.NormalRate);
|
||||
float absShear = Math.Abs(result.ShearRate);
|
||||
|
||||
if (absNormal > absShear)
|
||||
{
|
||||
// Dominant force is edge-wise
|
||||
result.Type = result.NormalRate > 0
|
||||
? StrainType.Tension // Moving apart
|
||||
: StrainType.Compression; // Moving together
|
||||
}
|
||||
else
|
||||
{
|
||||
// Dominant force is lateral (sliding)
|
||||
result.Type = StrainType.Shear;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public static StrainAnalysis AverageStrainList(List<StrainAnalysis> strains)
|
||||
{
|
||||
if (strains == null || strains.Count == 0)
|
||||
{
|
||||
return new StrainAnalysis(); // Return default if empty
|
||||
}
|
||||
|
||||
int count = strains.Count;
|
||||
|
||||
float sumMagnitude = 0;
|
||||
float sumNormalRate = 0;
|
||||
float sumShearRate = 0;
|
||||
|
||||
// Counters for the Type
|
||||
int tensionCount = 0;
|
||||
int compressionCount = 0;
|
||||
int shearCount = 0;
|
||||
|
||||
foreach (var s in strains)
|
||||
{
|
||||
sumMagnitude += s.Magnitude;
|
||||
sumNormalRate += s.NormalRate;
|
||||
sumShearRate += s.ShearRate;
|
||||
|
||||
// Count occurrences of each type
|
||||
switch (s.Type)
|
||||
{
|
||||
case StrainType.Tension:
|
||||
tensionCount++;
|
||||
break;
|
||||
case StrainType.Compression:
|
||||
compressionCount++;
|
||||
break;
|
||||
case StrainType.Shear:
|
||||
shearCount++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Calculate numerical averages
|
||||
float avgMagnitude = sumMagnitude / count;
|
||||
float avgNormalRate = sumNormalRate / count;
|
||||
float avgShearRate = sumShearRate / count;
|
||||
|
||||
// Determine the most common StrainType (Mode)
|
||||
StrainType averageType = StrainType.Shear; // Default
|
||||
int maxCount = 0;
|
||||
|
||||
if (tensionCount > maxCount) { maxCount = tensionCount; averageType = StrainType.Tension; }
|
||||
if (compressionCount > maxCount) { maxCount = compressionCount; averageType = StrainType.Compression; }
|
||||
if (shearCount > maxCount) { maxCount = shearCount; averageType = StrainType.Shear; }
|
||||
|
||||
return new StrainAnalysis
|
||||
{
|
||||
Magnitude = avgMagnitude,
|
||||
Type = averageType,
|
||||
NormalRate = avgNormalRate,
|
||||
ShearRate = avgShearRate
|
||||
};
|
||||
}
|
||||
public Vector3 GetVertexPosition(int vertexId)
|
||||
{
|
||||
return mdt.GetVertex(vertexId);
|
||||
}
|
||||
public void ColorVertex(int vertexId, Color color)
|
||||
{
|
||||
mdt.SetVertexColor(vertexId, color);
|
||||
}
|
||||
|
||||
public int GetRandomVertexId()
|
||||
{
|
||||
return Random.Shared.Next(0, Vertices.Count);
|
||||
}
|
||||
|
||||
public Color GetInitialColor(bool isLand)
|
||||
{
|
||||
var color = isLand ? new Color(
|
||||
0.2f,
|
||||
1f,
|
||||
0.2f
|
||||
) : new Color(
|
||||
0.2f,
|
||||
0.2f,
|
||||
1f
|
||||
);
|
||||
color.ToHsv(out float h, out float s, out float v);
|
||||
h += RandF(-0.05f, 0.05f);
|
||||
s += RandF(-0.2f, 0.2f);
|
||||
v += RandF(-0.3f, 0.3f);
|
||||
color = Color.FromHsv(h, s, v);
|
||||
return color;
|
||||
}
|
||||
public void ChangeStage(GenerationStage stage)
|
||||
{
|
||||
if (stage != Stage)
|
||||
GD.Print($"'{Stage.ToString()}' took '{_generationStopwatch.Elapsed}'");
|
||||
|
||||
Stage = Stage == StopAt ? GenerationStage.Completed : stage;
|
||||
if (stage == GenerationStage.Completed)
|
||||
_generationStopwatch.Stop();
|
||||
else
|
||||
_generationStopwatch.Restart();
|
||||
GD.Print($"Stage Started: '{Stage.ToString()}'");
|
||||
}
|
||||
|
||||
public int GetFreeNeighbourIndex(VertexData vertexData)
|
||||
{
|
||||
foreach (int neighbour in vertexData.Neighbours)
|
||||
{
|
||||
if (Vertices[neighbour].PlateId == -1)
|
||||
return neighbour;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
public IEnumerable<int> GetNeighboringVertices(int vertexId, bool blackOnly = true)
|
||||
{
|
||||
if (Stage != GenerationStage.Initialization)
|
||||
{
|
||||
if (blackOnly)
|
||||
return Vertices[vertexId].Neighbours.Where(n => Vertices[n].PlateId == -1);
|
||||
return Vertices[vertexId].Neighbours;
|
||||
}
|
||||
var verts = mdt.GetVertexEdges(vertexId).AsEnumerable().SelectMany<int, int>(edge => [mdt.GetEdgeVertex(edge, 0), mdt.GetEdgeVertex(edge, 1)]).Distinct().Where(v => v != vertexId);
|
||||
if (!blackOnly)
|
||||
return verts.Except([vertexId]);
|
||||
return verts.Where(v => mdt.GetVertexColor(v) == Colors.Black).Except([vertexId]);
|
||||
}
|
||||
|
||||
public void UpdateMesh()
|
||||
{
|
||||
arrayMesh.ClearSurfaces();
|
||||
mdt.CommitToSurface(arrayMesh);
|
||||
}
|
||||
}
|
||||
|
|
@ -1 +0,0 @@
|
|||
uid://l6f3v32agqs4
|
||||
|
|
@ -1,317 +0,0 @@
|
|||
using Godot;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
public partial class PlanetGenerator : Node3D
|
||||
{
|
||||
[Export] FastNoiseLite noise;
|
||||
[Export] FastNoiseLite hfnoise;
|
||||
[Export] private int _plateCount = 55;
|
||||
[Export] private float oceanPercent = 0.6f;
|
||||
|
||||
Dictionary<int, List<int>> plateVerticeCandidates = new Dictionary<int, List<int>>();
|
||||
Dictionary<int, PlateDataOld> plates = new Dictionary<int, PlateDataOld>();
|
||||
private int stage = -1;
|
||||
|
||||
MeshDataTool mdt = new MeshDataTool();
|
||||
|
||||
public class PlateDataOld(List<VertexDataOld> vertices = null, bool isLand = false, Vector3? dir = null, Color color = new())
|
||||
{
|
||||
public List<VertexDataOld> Vertices = vertices ?? new List<VertexDataOld>();
|
||||
public Color Color = color;
|
||||
public bool IsLand = isLand;
|
||||
public Vector3? Dir = dir;
|
||||
}
|
||||
|
||||
public class VertexDataOld(int index = 0, float stress = 0f, bool isEdge = false)
|
||||
{
|
||||
public int Index = index;
|
||||
public float Stress = stress;
|
||||
public bool IsEdge = isEdge;
|
||||
public bool StageProcessed = false;
|
||||
public float Height = 0f;
|
||||
}
|
||||
public override void _Ready()
|
||||
{
|
||||
var MeshInstance = GetNode<MeshInstance3D>("./Icosphere");
|
||||
if (MeshInstance.GetSurfaceOverrideMaterial(0) is ShaderMaterial shaderMaterial)
|
||||
{
|
||||
shaderMaterial.SetShaderParameter("mode", 1);
|
||||
}
|
||||
}
|
||||
|
||||
public void InitializeGeneration()
|
||||
{
|
||||
plateVerticeCandidates = new();
|
||||
plates = new();
|
||||
var MeshInstance = GetNode<MeshInstance3D>("./Icosphere");
|
||||
if (MeshInstance.Mesh is ArrayMesh mesh)
|
||||
{
|
||||
mdt.CreateFromSurface(mesh, 0);
|
||||
GD.Print($"MDTVertices: '{mdt.GetVertexCount()}', MDTFaces: '{mdt.GetFaceCount()}', MESHFaces: '{MeshInstance.Mesh.GetFaces().Length}'");
|
||||
for (int i = 0; i < mdt.GetVertexCount(); i++)
|
||||
{
|
||||
mdt.SetVertexColor(i, Colors.Black);
|
||||
}
|
||||
List<bool> isLandList = new List<bool>();
|
||||
isLandList.AddRange(Enumerable.Repeat(true, (int)(_plateCount * oceanPercent)));
|
||||
isLandList.AddRange(Enumerable.Repeat(false, (int)(_plateCount * 1f - oceanPercent)));
|
||||
isLandList = isLandList.OrderBy(l => Guid.NewGuid()).ToList();
|
||||
for (int i = 0; i < _plateCount; i++)
|
||||
{
|
||||
bool isLand = isLandList[i];
|
||||
var vertexIndex = Random.Shared.Next(0, mdt.GetVertexCount());
|
||||
|
||||
var color = isLand ? new Color(
|
||||
0.2f,
|
||||
1f,
|
||||
0.2f
|
||||
) : new Color(
|
||||
0.2f,
|
||||
0.2f,
|
||||
1f
|
||||
);
|
||||
color.ToHsv(out float h, out float s, out float v);
|
||||
h += RandF(-0.05f, 0.05f);
|
||||
s += RandF(-0.2f, 0.2f);
|
||||
v += RandF(-0.3f, 0.3f);
|
||||
color = Color.FromHsv(h, s, v);
|
||||
plateVerticeCandidates.Add(i, GetNeighboringVertices(vertexIndex).ToList());
|
||||
var plateVel = new Vector3(Random.Shared.NextSingle() * 2f - 1f ,Random.Shared.NextSingle() * 2f - 1f ,Random.Shared.NextSingle() * 2f - 1f);
|
||||
plateVel = plateVel.Normalized() * Random.Shared.NextSingle();
|
||||
plates.Add(i, new PlateDataOld([new VertexDataOld(vertexIndex)], isLand, plateVel, color));
|
||||
mdt.SetVertexColor(vertexIndex, color);
|
||||
}
|
||||
|
||||
mesh.ClearSurfaces();
|
||||
mdt.CommitToSurface(mesh);
|
||||
}
|
||||
}
|
||||
|
||||
private float RandF(float min, float max)
|
||||
{
|
||||
return min + (max - min) * Random.Shared.NextSingle();
|
||||
}
|
||||
|
||||
private int iterations = 1;
|
||||
public override void _Process(double delta)
|
||||
{
|
||||
if (Input.IsActionJustPressed("spacebar"))
|
||||
{
|
||||
InitializeGeneration();
|
||||
stage = 0;
|
||||
var MeshInstance = GetNode<MeshInstance3D>("./Icosphere");
|
||||
if (MeshInstance.GetSurfaceOverrideMaterial(0) is ShaderMaterial shaderMaterial)
|
||||
{
|
||||
shaderMaterial.SetShaderParameter("mode", 1);
|
||||
}
|
||||
}
|
||||
|
||||
if (GetNode<MeshInstance3D>("./Icosphere").Mesh is ArrayMesh mesh)
|
||||
{
|
||||
switch (stage)
|
||||
{
|
||||
case 0: // Gen initial tectonic plates
|
||||
if (plateVerticeCandidates.Count == 0)
|
||||
stage = 1;
|
||||
|
||||
|
||||
List<int> toRemove = [];
|
||||
foreach ((int plateIndex, List<int> verticeCanditates) in plateVerticeCandidates)
|
||||
{
|
||||
if (verticeCanditates.Count == 0)
|
||||
toRemove.Add(plateIndex);
|
||||
int max = Random.Shared.Next(1, (int)(2 + verticeCanditates.Count / 4f));
|
||||
for (int i = 0; i < max; i++)
|
||||
{
|
||||
iterations++;
|
||||
ExpandRandomly(verticeCanditates, plateIndex);
|
||||
}
|
||||
|
||||
plateVerticeCandidates[plateIndex] = verticeCanditates.Distinct().ToList();
|
||||
}
|
||||
|
||||
foreach (int index in toRemove)
|
||||
plateVerticeCandidates.Remove(index);
|
||||
mesh.ClearSurfaces();
|
||||
mdt.CommitToSurface(mesh);
|
||||
|
||||
break;
|
||||
case 1:
|
||||
foreach ((int plateIndex, PlateDataOld data) in plates)
|
||||
{
|
||||
foreach (VertexDataOld vertex in data.Vertices.Where(v => !v.StageProcessed).Take(50))
|
||||
{
|
||||
if (IsEdge(vertex.Index, plateIndex))
|
||||
{
|
||||
vertex.IsEdge = true;
|
||||
}
|
||||
vertex.StageProcessed = true;
|
||||
mdt.SetVertexColor(vertex.Index, vertex.IsEdge ? Colors.Black : Colors.White);
|
||||
}
|
||||
}
|
||||
mesh.ClearSurfaces();
|
||||
mdt.CommitToSurface(mesh);
|
||||
if (plates.Values.SelectMany(val => val.Vertices).All(v => v.StageProcessed))
|
||||
{
|
||||
foreach ((int plateIndex, PlateDataOld data) in plates)
|
||||
{
|
||||
foreach (VertexDataOld vertex in data.Vertices)
|
||||
{
|
||||
vertex.StageProcessed = false;
|
||||
}
|
||||
}
|
||||
stage = 2;
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
foreach ((int plateIndex, PlateDataOld data) in plates)
|
||||
{
|
||||
foreach (VertexDataOld vertex in data.Vertices.Where(v => !v.StageProcessed && v.IsEdge).Take(10))
|
||||
{
|
||||
vertex.Stress = GetStress(vertex.Index, plateIndex);
|
||||
vertex.StageProcessed = true;
|
||||
mdt.SetVertexColor(vertex.Index, vertex.Stress > 0f ? Colors.Red : Colors.Blue);
|
||||
}
|
||||
}
|
||||
mesh.ClearSurfaces();
|
||||
mdt.CommitToSurface(mesh);
|
||||
if (plates.Values.SelectMany(val => val.Vertices).Where(v => v.IsEdge).All(v => v.StageProcessed))
|
||||
{
|
||||
foreach ((int plateIndex, PlateDataOld data) in plates)
|
||||
{
|
||||
foreach (VertexDataOld vertex in data.Vertices)
|
||||
{
|
||||
vertex.StageProcessed = false;
|
||||
}
|
||||
}
|
||||
stage = 3;
|
||||
var MeshInstance = GetNode<MeshInstance3D>("./Icosphere");
|
||||
if (MeshInstance.GetSurfaceOverrideMaterial(0) is ShaderMaterial shaderMaterial)
|
||||
{
|
||||
shaderMaterial.SetShaderParameter("mode", 2);
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
case 3:
|
||||
foreach ((int plateIndex, PlateDataOld data) in plates)
|
||||
{
|
||||
foreach (VertexDataOld vertex in data.Vertices.Where(v => !v.StageProcessed).Take(10))
|
||||
{
|
||||
vertex.Height = GetHeight(vertex, plateIndex);
|
||||
vertex.StageProcessed = true;
|
||||
mdt.SetVertexColor(vertex.Index, new Color(vertex.Height, vertex.Height, vertex.Height));
|
||||
}
|
||||
}
|
||||
mesh.ClearSurfaces();
|
||||
mdt.CommitToSurface(mesh);
|
||||
if (plates.Values.SelectMany(val => val.Vertices).All(v => v.StageProcessed))
|
||||
{
|
||||
foreach ((int plateIndex, PlateDataOld data) in plates)
|
||||
{
|
||||
foreach (VertexDataOld vertex in data.Vertices)
|
||||
{
|
||||
vertex.StageProcessed = false;
|
||||
}
|
||||
}
|
||||
stage = 4;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void ColorFace(int faceIndex, Color color)
|
||||
{
|
||||
mdt.SetVertexColor(mdt.GetFaceVertex(faceIndex, 0), color);
|
||||
mdt.SetVertexColor(mdt.GetFaceVertex(faceIndex, 1), color);
|
||||
mdt.SetVertexColor(mdt.GetFaceVertex(faceIndex, 2), color);
|
||||
}
|
||||
|
||||
public bool IsEdge(int vertex, int plateIndex)
|
||||
{
|
||||
var neighbours = GetNeighboringVertices(vertex, false);
|
||||
foreach (var neighbour in neighbours)
|
||||
{
|
||||
if (plates[plateIndex].Vertices.All(v => v.Index != neighbour))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
public float GetStress(int vertex, int plateIndex)
|
||||
{
|
||||
PlateDataOld plate = plates[plateIndex];
|
||||
var neighbours = GetNeighboringVertices(vertex, false);
|
||||
float stress = 0f;
|
||||
|
||||
foreach ((int otherPlateIndex, PlateDataOld plateData) in plates.Where(p => p.Key != plateIndex))
|
||||
{
|
||||
if (plateData.Vertices.Where(v => v.IsEdge).Any(v => neighbours.Contains(v.Index)))
|
||||
{
|
||||
var a = plate.Dir ?? Vector3.Zero;
|
||||
var b = plateData.Dir ?? Vector3.Zero;
|
||||
stress += a.Dot(b);
|
||||
}
|
||||
}
|
||||
return stress;
|
||||
}
|
||||
|
||||
public float GetHeight(VertexDataOld vertex, int plateIndex)
|
||||
{
|
||||
PlateDataOld plate = plates[plateIndex];
|
||||
float height = 0.5f;
|
||||
|
||||
if (plate.IsLand)
|
||||
{
|
||||
height += Mathf.Abs(vertex.Stress * 0.25f);
|
||||
height += noise.GetNoise3Dv(mdt.GetVertex(vertex.Index)) * 0.15f;
|
||||
height += hfnoise.GetNoise3Dv(mdt.GetVertex(vertex.Index)) * 0.05f;
|
||||
}
|
||||
else
|
||||
{
|
||||
height -= Mathf.Abs(vertex.Stress * 0.25f);
|
||||
height += noise.GetNoise3Dv(mdt.GetVertex(vertex.Index)) * 0.15f;
|
||||
height += hfnoise.GetNoise3Dv(mdt.GetVertex(vertex.Index)) * 0.05f;
|
||||
}
|
||||
|
||||
return height;
|
||||
}
|
||||
public void ExpandRandomly(List<int> vertexCandidate, int plateIndex)
|
||||
{
|
||||
bool looping = true;
|
||||
var index = 0;
|
||||
while (looping)
|
||||
{
|
||||
if (vertexCandidate.Count == 0)
|
||||
{
|
||||
looping = false;
|
||||
continue;
|
||||
}
|
||||
index = Random.Shared.Next(vertexCandidate.Count);
|
||||
var vertex = vertexCandidate[index];
|
||||
if (mdt.GetVertexColor(vertex) != Colors.Black)
|
||||
{
|
||||
vertexCandidate.RemoveAt(index);
|
||||
continue;
|
||||
}
|
||||
mdt.SetVertexColor(vertex, plates[plateIndex].Color);
|
||||
plates[plateIndex].Vertices.Add(new VertexDataOld(vertex));
|
||||
vertexCandidate.RemoveAt(index);
|
||||
var neighbours = GetNeighboringVertices(vertex).ToList();
|
||||
vertexCandidate.AddRange(neighbours);
|
||||
looping = false;
|
||||
}
|
||||
}
|
||||
|
||||
public List<int> GetNeighboringVertices(int vertex, bool blackOnly = true)
|
||||
{
|
||||
var verts = mdt.GetVertexEdges(vertex).AsEnumerable().SelectMany<int, int>(edge => [mdt.GetEdgeVertex(edge, 0), mdt.GetEdgeVertex(edge, 1)]).Distinct().Where(v => v != vertex);
|
||||
if (!blackOnly)
|
||||
return verts.ToList();
|
||||
return verts.Where(v => mdt.GetVertexColor(v) == Colors.Black).ToList();
|
||||
}
|
||||
}
|
||||
|
|
@ -1 +0,0 @@
|
|||
uid://brdlktm67budp
|
||||
734
PlanetHelper.cs
734
PlanetHelper.cs
|
|
@ -1,734 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Godot;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using adatonic;
|
||||
using Node = adatonic.Node;
|
||||
using Timer = System.Timers.Timer;
|
||||
|
||||
public class PlanetHelper
|
||||
{
|
||||
public static float RandF(float min, float max)
|
||||
{
|
||||
return min + (max - min) * Random.Shared.NextSingle();
|
||||
}
|
||||
public class PlateData(int Id = 0, Color Color = new(), bool IsLandform = false, List<int> Vertices = null)
|
||||
{
|
||||
public int Id { get; set; } = Id;
|
||||
public Color Color { get; set; } = Color;
|
||||
public bool IsLandform { get; set; } = IsLandform;
|
||||
public List<int> Vertices { get; set; } = Vertices;
|
||||
|
||||
public int CenterVertexId = -1;
|
||||
public float PlateExpansion { get; set; } = RandF(0.5f, 2f);
|
||||
public Vector3 Dir { get; set; } = Vector3.Zero;
|
||||
}
|
||||
|
||||
public class VertexData(int Id = 0, int PlateId = 0, List<int> Neighbours = null, bool StageComplete = false)
|
||||
{
|
||||
public int Id { get; set; } = Id;
|
||||
public int PlateId { get; set; } = PlateId;
|
||||
|
||||
public List<StrainAnalysis> StrainSamples { get; set; } = new();
|
||||
public List<int> Neighbours { get; set; } = Neighbours;
|
||||
public bool StageComplete { get; set; } = StageComplete;
|
||||
public bool IsEdge = false;
|
||||
public bool IsTypeEdge = false;
|
||||
public float EdgeDistance = -1f;
|
||||
public float Height = 0f;
|
||||
}
|
||||
public enum StrainType
|
||||
{
|
||||
Tension, // Pulling apart
|
||||
Compression, // Pushing together
|
||||
Shear // Sliding past each other
|
||||
}
|
||||
public class StrainAnalysis
|
||||
{
|
||||
public float Magnitude; // Total magnitude of the strain
|
||||
public StrainType Type; // The dominant type of force
|
||||
public float NormalRate; // Rate of convergence/divergence
|
||||
public float ShearRate; // Rate of sliding
|
||||
}
|
||||
|
||||
private bool StageComplete = true;
|
||||
private int _plateCount = 14;
|
||||
private float _landRatio = 0.4f;
|
||||
|
||||
public List<PlateData> Plates = new List<PlateData>();
|
||||
public List<VertexData> Vertices = new List<VertexData>();
|
||||
|
||||
public double StageHangTime = 1.0;
|
||||
public bool AutoRun = false;
|
||||
public bool Advance = false;
|
||||
|
||||
public int TesselationLevel = 4;
|
||||
|
||||
Stopwatch _generationStopwatch = new Stopwatch();
|
||||
|
||||
private FastNoiseLite _continentalNoise;
|
||||
private FastNoiseLite _mountainNoise;
|
||||
private FastNoiseLite _hfNoise;
|
||||
|
||||
public enum GenerationStage
|
||||
{
|
||||
NotStarted,
|
||||
Initialization,
|
||||
PlateGeneration,
|
||||
BorderSearch,
|
||||
EdgeDistanceCalculation,
|
||||
EdgeStressCalculation,
|
||||
SpreadStress,
|
||||
HeightCalculation,
|
||||
|
||||
Completed,
|
||||
}
|
||||
|
||||
private bool _waiting = false;
|
||||
public GenerationStage Stage = GenerationStage.NotStarted;
|
||||
public GenerationStage StopStage = GenerationStage.Completed;
|
||||
|
||||
private MeshInstance3D _meshInstance;
|
||||
private TextureRect _textureRect;
|
||||
private ArrayMesh _arrayMesh;
|
||||
|
||||
public MeshDataTool Mdt;
|
||||
|
||||
public Oct Octree = new Oct();
|
||||
|
||||
public PlanetHelper(MeshInstance3D meshInstance, TextureRect textureRect)
|
||||
{
|
||||
_meshInstance = meshInstance;
|
||||
_arrayMesh = meshInstance.Mesh as ArrayMesh;
|
||||
_textureRect = textureRect;
|
||||
|
||||
_continentalNoise = new FastNoiseLite();
|
||||
_mountainNoise = new FastNoiseLite();
|
||||
_hfNoise = new FastNoiseLite();
|
||||
|
||||
Mdt = new MeshDataTool();
|
||||
Mdt.CreateFromSurface(_arrayMesh, 0);
|
||||
|
||||
for (int i = 0; i < Mdt.GetVertexCount(); i++)
|
||||
{
|
||||
Octree.Insert(new Node(i, Mdt.GetVertex(i) * 0.001f));
|
||||
Mdt.SetVertexColor(i, Colors.Black);
|
||||
}
|
||||
}
|
||||
|
||||
public void InitializeGeneration()
|
||||
{
|
||||
Plates = new();
|
||||
Vertices = new();
|
||||
|
||||
Mdt.CreateFromSurface(_arrayMesh, 0);
|
||||
|
||||
for (int i = 0; i < Mdt.GetVertexCount(); i++)
|
||||
{
|
||||
// Init to black
|
||||
Mdt.SetVertexColor(i, Colors.Black);
|
||||
|
||||
Vertices.Add(new VertexData(i, -1,GetNeighboringVertices(i, false).OrderBy(v => Guid.NewGuid()).ToList()));
|
||||
}
|
||||
|
||||
// Initialize Plates
|
||||
for (int i = 0; i < _plateCount; i++)
|
||||
{
|
||||
// Get a random un-assigned vertex.
|
||||
VertexData vertex = Vertices.Where(v => v.PlateId == -1).OrderBy(v => Guid.NewGuid()).First();
|
||||
vertex.PlateId = i;
|
||||
var color = new Color(RandF(0f, 1f), RandF(0f, 1f), RandF(0f, 1f));
|
||||
ColorVertex(vertex.Id, color);
|
||||
PlateData plate = new PlateData(i, color, false, [vertex.Id]);
|
||||
|
||||
plate.Dir = GetRandomTangentialVelocity(Mdt.GetVertex(vertex.Id), RandF(0f, 1f));
|
||||
Plates.Add(plate);
|
||||
}
|
||||
|
||||
CompleteStage();
|
||||
}
|
||||
|
||||
public IEnumerable<int> GetNeighboringVertices(int vertexId, bool blackOnly = true)
|
||||
{
|
||||
if (Stage != GenerationStage.Initialization)
|
||||
{
|
||||
if (blackOnly)
|
||||
return Vertices[vertexId].Neighbours.Where(n => Vertices[n].PlateId == -1);
|
||||
return Vertices[vertexId].Neighbours;
|
||||
}
|
||||
var verts = Mdt.GetVertexEdges(vertexId).AsEnumerable().SelectMany<int, int>(edge => [Mdt.GetEdgeVertex(edge, 0), Mdt.GetEdgeVertex(edge, 1)]).Distinct().Where(v => v != vertexId);
|
||||
if (!blackOnly)
|
||||
return verts.Except([vertexId]);
|
||||
return verts.Where(v => Mdt.GetVertexColor(v) == Colors.Black).Except([vertexId]);
|
||||
}
|
||||
|
||||
public Vector3 GetRandomTangentialVelocity(Vector3 pointOnSphere, float speed)
|
||||
{
|
||||
// 1. Normalize the input point to ensure it represents the normal vector
|
||||
// (If your sphere is not at 0,0,0, use the vector from center to point)
|
||||
Vector3 normal = pointOnSphere.Normalized();
|
||||
|
||||
// 2. Generate a random vector
|
||||
Random rand = new Random();
|
||||
Vector3 randomVec = new Vector3(
|
||||
(float)(rand.NextDouble() - 0.5), // Range -0.5 to 0.5
|
||||
(float)(rand.NextDouble() - 0.5),
|
||||
(float)(rand.NextDouble() - 0.5)
|
||||
);
|
||||
|
||||
// 3. Calculate the tangent using Cross Product
|
||||
// Cross Product of (randomVec, normal) gives a vector perpendicular to both.
|
||||
// Since it is perpendicular to the normal, it is tangential to the sphere.
|
||||
Vector3 tangent = randomVec.Cross(normal);
|
||||
|
||||
// 4. Edge Case Handling
|
||||
// If the random vector happens to be parallel to the normal,
|
||||
// the cross product will result in a zero vector.
|
||||
if (tangent.Dot(tangent) < 1e-6f)
|
||||
{
|
||||
// If parallel, force a non-zero vector by modifying the input slightly
|
||||
// or simply recurse (try again). Here we force a modification for safety.
|
||||
randomVec = new Vector3(0, 1, 0);
|
||||
tangent = randomVec.Cross(normal);
|
||||
}
|
||||
|
||||
// 5. Normalize to ensure unit length
|
||||
Vector3 normalizedTangent = tangent.Normalized();
|
||||
|
||||
// 6. Scale by the desired speed
|
||||
return normalizedTangent * speed;
|
||||
}
|
||||
|
||||
public void ToggleAutoRun()
|
||||
{
|
||||
AutoRun = !AutoRun;
|
||||
}
|
||||
|
||||
public void ToggleAdvance()
|
||||
{
|
||||
Advance = !Advance;
|
||||
}
|
||||
|
||||
public void AdvanceStage()
|
||||
{
|
||||
Advance = false;
|
||||
if (_waiting)
|
||||
return;
|
||||
|
||||
|
||||
Timer timer = new(Mathf.Clamp(StageHangTime, 0.1, 10.0));
|
||||
timer.Elapsed += (o, e) =>
|
||||
{
|
||||
GenerationStage stage = Stage + 1;
|
||||
Stage = Stage == StopStage ? GenerationStage.Completed : stage;
|
||||
if (stage == GenerationStage.Completed)
|
||||
_generationStopwatch.Stop();
|
||||
else
|
||||
_generationStopwatch.Restart();
|
||||
GD.Print($"Stage Started: '{Stage.ToString()}'");
|
||||
_waiting = false;
|
||||
StageComplete = false;
|
||||
};
|
||||
timer.AutoReset = false;
|
||||
timer.Start();
|
||||
_waiting = true;
|
||||
}
|
||||
public void Process()
|
||||
{
|
||||
if (!StageComplete)
|
||||
{
|
||||
switch (Stage)
|
||||
{
|
||||
default:
|
||||
case GenerationStage.NotStarted:
|
||||
break;
|
||||
case GenerationStage.Completed:
|
||||
break;
|
||||
case GenerationStage.Initialization:
|
||||
InitializeGeneration();
|
||||
break;
|
||||
case GenerationStage.PlateGeneration:
|
||||
PlateGeneration();
|
||||
break;
|
||||
case GenerationStage.BorderSearch:
|
||||
BorderSearch();
|
||||
break;
|
||||
case GenerationStage.EdgeDistanceCalculation:
|
||||
EdgeDistanceCalculation();
|
||||
break;
|
||||
case GenerationStage.EdgeStressCalculation:
|
||||
EdgeStressCalculation();
|
||||
break;
|
||||
case GenerationStage.SpreadStress:
|
||||
SpreadStress();
|
||||
break;
|
||||
case GenerationStage.HeightCalculation:
|
||||
HeightCalculation();
|
||||
break;
|
||||
}
|
||||
|
||||
UpdateMesh();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (AutoRun || Advance)
|
||||
AdvanceStage();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void PlateGeneration()
|
||||
{
|
||||
var availableVerts = Vertices.Where(d => d.StageComplete == false && d.PlateId != -1).OrderBy(v => Guid.NewGuid()).ToList();
|
||||
foreach (PlateData plateData in Plates)
|
||||
{
|
||||
var plateVerts = availableVerts.Where(d => d.PlateId == plateData.Id);
|
||||
foreach (VertexData vertexData in plateVerts.Take((int)((5 + plateVerts.Count() / 4) * plateData.PlateExpansion)))
|
||||
{
|
||||
int expandTo = GetFreeNeighbourIndex(vertexData);
|
||||
if (expandTo != -1)
|
||||
{
|
||||
Vertices[expandTo].PlateId = plateData.Id;
|
||||
plateData.Vertices.Add(expandTo);
|
||||
ColorVertex(expandTo, plateData.Color);
|
||||
}
|
||||
else
|
||||
{
|
||||
vertexData.StageComplete = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!availableVerts.Any())
|
||||
{
|
||||
foreach (VertexData vertexData in Vertices)
|
||||
vertexData.StageComplete = false;
|
||||
AssignOceanPlates(Plates);
|
||||
CompleteStage();
|
||||
}
|
||||
}
|
||||
public void BorderSearch()
|
||||
{
|
||||
var availableVerts = Vertices.Where(d => d.StageComplete == false).Take(2500).ToList();
|
||||
foreach (VertexData vertexData in availableVerts)
|
||||
{
|
||||
// Do we have any neighbours of another plate?
|
||||
var neighbours = GetNeighboringVertices(vertexData.Id, false).ToList();
|
||||
if (neighbours
|
||||
.Any(v => Vertices[v].PlateId != vertexData.PlateId))
|
||||
{
|
||||
vertexData.IsEdge = true;
|
||||
vertexData.IsTypeEdge = neighbours.Any(n => Plates[Vertices[n].PlateId].IsLandform != Plates[vertexData.PlateId].IsLandform);
|
||||
if (vertexData.IsTypeEdge)
|
||||
vertexData.EdgeDistance = 1f;
|
||||
ColorVertex(vertexData.Id, vertexData.IsTypeEdge ? Colors.White : Colors.Black);
|
||||
}
|
||||
else
|
||||
{
|
||||
ColorVertex(vertexData.Id, Plates[vertexData.PlateId].Color);
|
||||
}
|
||||
vertexData.StageComplete = true;
|
||||
}
|
||||
|
||||
if (!availableVerts.Any())
|
||||
{
|
||||
foreach (VertexData vertexData in Vertices)
|
||||
vertexData.StageComplete = false;
|
||||
CompleteStage();
|
||||
}
|
||||
}
|
||||
public void EdgeDistanceCalculation()
|
||||
{
|
||||
var availableVerts = Vertices.Where(d => d.StageComplete == false && d.EdgeDistance > 0f).OrderBy(v => v.EdgeDistance).Take(2500).ToList();
|
||||
foreach (VertexData vertexData in availableVerts)
|
||||
{
|
||||
var neighbours = GetNeighboringVertices(vertexData.Id, false).ToList();
|
||||
foreach (int neighbour in neighbours)
|
||||
{
|
||||
if (Vertices[neighbour].EdgeDistance > 0f && Vertices[neighbour].EdgeDistance < vertexData.EdgeDistance + 1f)
|
||||
continue;
|
||||
VertexData neighbourVert = Vertices[neighbour];
|
||||
neighbourVert.EdgeDistance = vertexData.EdgeDistance + 1f;
|
||||
ColorVertex(neighbourVert.Id, Plates[vertexData.PlateId].Color * 0.8f);
|
||||
}
|
||||
vertexData.StageComplete = true;
|
||||
}
|
||||
|
||||
if (!availableVerts.Any())
|
||||
{
|
||||
float maxDistance = Vertices.Max(v => v.EdgeDistance);
|
||||
foreach (VertexData vertexData in Vertices)
|
||||
{
|
||||
vertexData.EdgeDistance /= maxDistance;
|
||||
}
|
||||
|
||||
foreach (PlateData plateData in Plates)
|
||||
{
|
||||
plateData.CenterVertexId =
|
||||
Vertices.Where(v => v.PlateId == plateData.Id).MaxBy(v => v.EdgeDistance).Id;
|
||||
}
|
||||
foreach (VertexData vertexData in Vertices)
|
||||
vertexData.StageComplete = false;
|
||||
CompleteStage();
|
||||
}
|
||||
}
|
||||
public void EdgeStressCalculation()
|
||||
{
|
||||
var availableVerts = Vertices.Where(d => d.StageComplete == false && d.IsEdge).Take(2500).ToList();
|
||||
foreach (VertexData vertexData in availableVerts)
|
||||
{
|
||||
var neighbours = GetNeighboringVertices(vertexData.Id, false).ToList();
|
||||
foreach (int neighbour in neighbours)
|
||||
{
|
||||
if (!Vertices[neighbour].IsEdge)
|
||||
continue;
|
||||
if (Vertices[neighbour].PlateId == vertexData.PlateId)
|
||||
continue;
|
||||
PlateData plateA = Plates[vertexData.PlateId];
|
||||
PlateData plateB = Plates[Vertices[neighbour].PlateId];
|
||||
VertexData centerA = Vertices[plateA.CenterVertexId];
|
||||
VertexData centerB = Vertices[plateB.CenterVertexId];
|
||||
Vector3 p1, p2;
|
||||
p1 = Mdt.GetVertex(vertexData.Id).Cross(Mdt.GetVertex(centerA.Id));
|
||||
p2 = Mdt.GetVertex(neighbour).Cross(Mdt.GetVertex(centerB.Id));
|
||||
vertexData.StrainSamples.Add(CalculateStrainMagnitude(p1, p2, plateA.Dir, plateB.Dir));
|
||||
}
|
||||
vertexData.StageComplete = true;
|
||||
var majorStrain = AverageStrainList(vertexData.StrainSamples);
|
||||
switch (majorStrain.Type)
|
||||
{
|
||||
case StrainType.Compression:
|
||||
ColorVertex(vertexData.Id, Colors.Red * majorStrain.Magnitude);
|
||||
break;
|
||||
case StrainType.Shear:
|
||||
ColorVertex(vertexData.Id, Colors.Yellow * majorStrain.Magnitude);
|
||||
break;
|
||||
case StrainType.Tension:
|
||||
ColorVertex(vertexData.Id, Colors.Blue * majorStrain.Magnitude);
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (!availableVerts.Any())
|
||||
{
|
||||
foreach (VertexData vertexData in Vertices)
|
||||
vertexData.StageComplete = false;
|
||||
CompleteStage();
|
||||
}
|
||||
}
|
||||
public void SpreadStress()
|
||||
{
|
||||
var availableVerts = Vertices.Where(d => d.StageComplete == false && d.IsEdge && d.StrainSamples.Any()).OrderBy(d => Mathf.Abs(d.StrainSamples.Max(s => s.Magnitude))).Take(2500).ToList();
|
||||
foreach (VertexData vertexData in availableVerts)
|
||||
{
|
||||
var neighbours = GetNeighboringVertices(vertexData.Id, false).ToList();
|
||||
var majorStrain = AverageStrainList(vertexData.StrainSamples);
|
||||
foreach (int neighbour in neighbours)
|
||||
{
|
||||
VertexData neighbourVert = Vertices[neighbour];
|
||||
neighbourVert.IsEdge = true;
|
||||
|
||||
var newStrain = new StrainAnalysis();
|
||||
newStrain.Magnitude = majorStrain.Magnitude * 0.9f;
|
||||
newStrain.Type = majorStrain.Type;
|
||||
newStrain.NormalRate = majorStrain.NormalRate * 0.9f;
|
||||
newStrain.ShearRate = majorStrain.ShearRate * 0.9f;
|
||||
neighbourVert.StrainSamples.Add(newStrain);
|
||||
var newAverage = AverageStrainList(neighbourVert.StrainSamples);;
|
||||
switch (majorStrain.Type)
|
||||
{
|
||||
case StrainType.Compression:
|
||||
ColorVertex(neighbourVert.Id, Colors.Red * newAverage.Magnitude);
|
||||
break;
|
||||
case StrainType.Shear:
|
||||
ColorVertex(neighbourVert.Id, Colors.Yellow * newAverage.Magnitude);
|
||||
break;
|
||||
case StrainType.Tension:
|
||||
ColorVertex(neighbourVert.Id, Colors.Blue * newAverage.Magnitude);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (neighbours.All(n => Vertices[n].IsEdge))
|
||||
{
|
||||
vertexData.StageComplete = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!availableVerts.Any())
|
||||
{
|
||||
foreach (VertexData vertexData in Vertices)
|
||||
{
|
||||
vertexData.StageComplete = false;
|
||||
}
|
||||
CompleteStage();
|
||||
}
|
||||
}
|
||||
|
||||
public void HeightCalculation()
|
||||
{
|
||||
var availableVerts = Vertices.Where(d => d.StageComplete == false).Take(2500).ToList();
|
||||
foreach (VertexData vertexData in availableVerts)
|
||||
{
|
||||
PlateData plate = Plates[vertexData.PlateId];
|
||||
float continentalNoise = _continentalNoise.GetNoise3Dv(GetVertexPosition(vertexData.Id));
|
||||
float mountainNoise = (1.0f + _mountainNoise.GetNoise3Dv(GetVertexPosition(vertexData.Id))) * 0.5f;
|
||||
float hfNoise = _hfNoise.GetNoise3Dv(GetVertexPosition(vertexData.Id));
|
||||
var majorStrain = AverageStrainList(vertexData.StrainSamples);
|
||||
var normalRate = -majorStrain.NormalRate * majorStrain.Magnitude * (plate.IsLandform ? 1f : 0.5f);
|
||||
var edgeDistance = vertexData.EdgeDistance * (plate.IsLandform ? 1f : -1f);
|
||||
float height = 0.5f;
|
||||
//height *= plate.PlateExpansion;
|
||||
float mult = 2f;
|
||||
height += hfNoise;
|
||||
height = (height + 0.5f * mult) / (1f + mult);
|
||||
height += continentalNoise;
|
||||
height = (height + 0.5f * mult) / (1f + mult);
|
||||
height += edgeDistance * 0.25f;
|
||||
height = (height + 0.5f * mult) / (1f + mult);
|
||||
height += normalRate * 0.35f;
|
||||
height = Mathf.Clamp(height, 0.01f, 0.99f);
|
||||
ColorVertex(vertexData.Id, Colors.White * height);
|
||||
vertexData.StageComplete = true;
|
||||
vertexData.Height = height;
|
||||
}
|
||||
|
||||
if (!availableVerts.Any())
|
||||
{
|
||||
GD.Print($"Heights - min:'{Vertices.Min(v => v.Height)}' - max:'{Vertices.Max(v => v.Height)}' - average:'{Vertices.Average(v => v.Height)}'");
|
||||
ScaleValues(Vertices);
|
||||
foreach (VertexData vertexData in Vertices)
|
||||
ColorVertex(vertexData.Id, Colors.White * vertexData.Height);
|
||||
float oceanPercentage = Vertices.Count(v => v.Height < 0.5f) / (float)Vertices.Count;
|
||||
GD.Print($"Ocean Percentage:'{oceanPercentage}'");
|
||||
CompleteStage();
|
||||
if (_meshInstance.GetSurfaceOverrideMaterial(0) is ShaderMaterial shaderMaterial)
|
||||
{
|
||||
shaderMaterial.SetShaderParameter("mode", 2);
|
||||
}
|
||||
if (_textureRect.Material is ShaderMaterial textureShaderMaterial)
|
||||
{
|
||||
textureShaderMaterial.SetShaderParameter("mode", 2);
|
||||
}
|
||||
}
|
||||
}
|
||||
public void ScaleValues(List<VertexData> values)
|
||||
{
|
||||
float maxDistance = Vertices.Max(s => Mathf.Abs(s.Height - 0.5f));
|
||||
float scale = 0.5f/maxDistance;
|
||||
|
||||
values.ForEach(v => v.Height = Mathf.Clamp(0.5f + (v.Height - 0.5f) * scale, 0.01f, 0.99f));
|
||||
GD.Print($"Heights Post Scaling - min:'{Vertices.Min(v => v.Height)}' - max:'{Vertices.Max(v => v.Height)}' - average:'{Vertices.Average(v => v.Height)}'");
|
||||
}
|
||||
public StrainAnalysis CalculateStrainMagnitude(Vector3 p1, Vector3 p2, Vector3 v1, Vector3 v2)
|
||||
{
|
||||
StrainAnalysis result = new StrainAnalysis();
|
||||
|
||||
// 1. Geometry and Relative Velocity
|
||||
Vector3 edge = p2 - p1;
|
||||
float edgeLength = edge.Length();
|
||||
|
||||
// If points are identical, strain is zero
|
||||
if (edgeLength < float.Epsilon)
|
||||
{
|
||||
result.Magnitude = 0;
|
||||
result.Type = StrainType.Shear; // Default
|
||||
return result;
|
||||
}
|
||||
|
||||
Vector3 relVelocity = v2 - v1;
|
||||
float relVelMag = relVelocity.Length();
|
||||
|
||||
// 2. Calculate Components
|
||||
// Normal component: How much they are moving parallel to the edge (Pulling apart/Pushing together)
|
||||
// We project relVelocity onto edge vector
|
||||
float dot = relVelocity.Dot(edge);
|
||||
result.NormalRate = dot / edgeLength;
|
||||
|
||||
// Shear component: How much they are moving perpendicular to the edge (Sliding)
|
||||
// Formula: v_tangent = sqrt(|v|^2 - (v_normal)^2)
|
||||
// Note: We use the magnitude of the relVelocity for the subtraction to avoid float errors
|
||||
float normalRateSq = result.NormalRate * result.NormalRate;
|
||||
float shearRateSq = relVelMag * relVelMag - normalRateSq;
|
||||
result.ShearRate = (shearRateSq > 0) ? (float)Math.Sqrt(shearRateSq) : 0;
|
||||
|
||||
// 3. Determine Magnitude
|
||||
// Total strain = sqrt(normal^2 + shear^2)
|
||||
result.Magnitude = (float)Math.Sqrt(normalRateSq + shearRateSq);
|
||||
|
||||
// 4. Classification Logic
|
||||
// Compare the absolute values to see which force is "dominant"
|
||||
float absNormal = Math.Abs(result.NormalRate);
|
||||
float absShear = Math.Abs(result.ShearRate);
|
||||
|
||||
if (absNormal > absShear)
|
||||
{
|
||||
// Dominant force is edge-wise
|
||||
result.Type = result.NormalRate > 0
|
||||
? StrainType.Tension // Moving apart
|
||||
: StrainType.Compression; // Moving together
|
||||
}
|
||||
else
|
||||
{
|
||||
// Dominant force is lateral (sliding)
|
||||
result.Type = StrainType.Shear;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public static StrainAnalysis AverageStrainList(List<StrainAnalysis> strains)
|
||||
{
|
||||
if (strains == null || strains.Count == 0)
|
||||
{
|
||||
return new StrainAnalysis(); // Return default if empty
|
||||
}
|
||||
|
||||
int count = strains.Count;
|
||||
|
||||
float sumMagnitude = 0;
|
||||
float sumNormalRate = 0;
|
||||
float sumShearRate = 0;
|
||||
|
||||
// Counters for the Type
|
||||
int tensionCount = 0;
|
||||
int compressionCount = 0;
|
||||
int shearCount = 0;
|
||||
|
||||
foreach (var s in strains)
|
||||
{
|
||||
sumMagnitude += s.Magnitude;
|
||||
sumNormalRate += s.NormalRate;
|
||||
sumShearRate += s.ShearRate;
|
||||
|
||||
// Count occurrences of each type
|
||||
switch (s.Type)
|
||||
{
|
||||
case StrainType.Tension:
|
||||
tensionCount++;
|
||||
break;
|
||||
case StrainType.Compression:
|
||||
compressionCount++;
|
||||
break;
|
||||
case StrainType.Shear:
|
||||
shearCount++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Calculate numerical averages
|
||||
float avgMagnitude = sumMagnitude / count;
|
||||
float avgNormalRate = sumNormalRate / count;
|
||||
float avgShearRate = sumShearRate / count;
|
||||
|
||||
// Determine the most common StrainType (Mode)
|
||||
StrainType averageType = StrainType.Shear; // Default
|
||||
int maxCount = 0;
|
||||
|
||||
if (tensionCount > maxCount) { maxCount = tensionCount; averageType = StrainType.Tension; }
|
||||
if (compressionCount > maxCount) { maxCount = compressionCount; averageType = StrainType.Compression; }
|
||||
if (shearCount > maxCount) { maxCount = shearCount; averageType = StrainType.Shear; }
|
||||
|
||||
return new StrainAnalysis
|
||||
{
|
||||
Magnitude = avgMagnitude,
|
||||
Type = averageType,
|
||||
NormalRate = avgNormalRate,
|
||||
ShearRate = avgShearRate
|
||||
};
|
||||
}
|
||||
public void AssignOceanPlates(List<PlateData> areas)
|
||||
{
|
||||
int n = areas.Count;
|
||||
double totalArea = areas.Sum(a => a.Vertices.Count * a.PlateExpansion);
|
||||
double targetOcean = totalArea * _landRatio;
|
||||
|
||||
double bestDiff = double.MaxValue;
|
||||
int bestMask = 0;
|
||||
|
||||
int combinations = 1 << n;
|
||||
|
||||
for (int mask = 0; mask < combinations; mask++)
|
||||
{
|
||||
int oceanArea = 0;
|
||||
|
||||
for (int i = 0; i < n; i++)
|
||||
{
|
||||
if ((mask & (1 << i)) != 0)
|
||||
oceanArea += (int)(areas[i].Vertices.Count * areas[i].PlateExpansion);
|
||||
}
|
||||
|
||||
double diff = Math.Abs(oceanArea - targetOcean);
|
||||
|
||||
if (diff < bestDiff)
|
||||
{
|
||||
bestDiff = diff;
|
||||
bestMask = mask;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < n; i++)
|
||||
{
|
||||
areas[i].IsLandform = (bestMask & (1 << i)) != 0;
|
||||
Color color = GetInitialColor(areas[i].IsLandform);
|
||||
areas[i].Color = color;
|
||||
foreach (int v in areas[i].Vertices)
|
||||
{
|
||||
ColorVertex(v, color);
|
||||
}
|
||||
}
|
||||
}
|
||||
public int GetFreeNeighbourIndex(VertexData vertexData)
|
||||
{
|
||||
foreach (int neighbour in vertexData.Neighbours)
|
||||
{
|
||||
if (Vertices[neighbour].PlateId == -1)
|
||||
return neighbour;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
public Color GetInitialColor(bool isLand)
|
||||
{
|
||||
var color = isLand ? new Color(
|
||||
0.2f,
|
||||
1f,
|
||||
0.2f
|
||||
) : new Color(
|
||||
0.2f,
|
||||
0.2f,
|
||||
1f
|
||||
);
|
||||
color.ToHsv(out float h, out float s, out float v);
|
||||
h += RandF(-0.05f, 0.05f);
|
||||
s += RandF(-0.2f, 0.2f);
|
||||
v += RandF(-0.3f, 0.3f);
|
||||
color = Color.FromHsv(h, s, v);
|
||||
return color;
|
||||
}
|
||||
public Vector3 GetVertexPosition(int vertexId)
|
||||
{
|
||||
return Mdt.GetVertex(vertexId);
|
||||
}
|
||||
public void CompleteStage()
|
||||
{
|
||||
StageComplete = true;
|
||||
_generationStopwatch.Stop();
|
||||
if (Stage != GenerationStage.NotStarted)
|
||||
GD.Print($"'{Stage.ToString()}' took '{_generationStopwatch.Elapsed}'");
|
||||
}
|
||||
|
||||
public void ColorVertex(int id, Color color)
|
||||
{
|
||||
Mdt.SetVertexColor(id, color);
|
||||
}
|
||||
|
||||
public void UpdateMesh()
|
||||
{
|
||||
_arrayMesh.ClearSurfaces();
|
||||
Mdt.CommitToSurface(_arrayMesh);
|
||||
}
|
||||
}
|
||||
|
|
@ -1 +0,0 @@
|
|||
uid://d2661qrqttvva
|
||||
BIN
PlanetLow.obj
(Stored with Git LFS)
BIN
PlanetLow.obj
(Stored with Git LFS)
Binary file not shown.
|
|
@ -1,25 +0,0 @@
|
|||
[remap]
|
||||
|
||||
importer="wavefront_obj"
|
||||
importer_version=1
|
||||
type="Mesh"
|
||||
uid="uid://i15y020lbl21"
|
||||
path="res://.godot/imported/PlanetLow.obj-bc74423b35159de14972c57f3a38df3a.mesh"
|
||||
|
||||
[deps]
|
||||
|
||||
files=["res://.godot/imported/PlanetLow.obj-bc74423b35159de14972c57f3a38df3a.mesh"]
|
||||
|
||||
source_file="res://PlanetLow.obj"
|
||||
dest_files=["res://.godot/imported/PlanetLow.obj-bc74423b35159de14972c57f3a38df3a.mesh", "res://.godot/imported/PlanetLow.obj-bc74423b35159de14972c57f3a38df3a.mesh"]
|
||||
|
||||
[params]
|
||||
|
||||
generate_tangents=true
|
||||
generate_lods=true
|
||||
generate_shadow_mesh=true
|
||||
generate_lightmap_uv2=false
|
||||
generate_lightmap_uv2_texel_size=0.2
|
||||
scale_mesh=Vector3(1, 1, 1)
|
||||
offset_mesh=Vector3(0, 0, 0)
|
||||
force_disable_mesh_compression=false
|
||||
BIN
PlanetMed.obj
(Stored with Git LFS)
BIN
PlanetMed.obj
(Stored with Git LFS)
Binary file not shown.
|
|
@ -1,25 +0,0 @@
|
|||
[remap]
|
||||
|
||||
importer="wavefront_obj"
|
||||
importer_version=1
|
||||
type="Mesh"
|
||||
uid="uid://cxpivgqm7x7ky"
|
||||
path="res://.godot/imported/PlanetMed.obj-dd89aa4f98d2b699cf11182b799d5596.mesh"
|
||||
|
||||
[deps]
|
||||
|
||||
files=["res://.godot/imported/PlanetMed.obj-dd89aa4f98d2b699cf11182b799d5596.mesh"]
|
||||
|
||||
source_file="res://PlanetMed.obj"
|
||||
dest_files=["res://.godot/imported/PlanetMed.obj-dd89aa4f98d2b699cf11182b799d5596.mesh", "res://.godot/imported/PlanetMed.obj-dd89aa4f98d2b699cf11182b799d5596.mesh"]
|
||||
|
||||
[params]
|
||||
|
||||
generate_tangents=true
|
||||
generate_lods=true
|
||||
generate_shadow_mesh=true
|
||||
generate_lightmap_uv2=false
|
||||
generate_lightmap_uv2_texel_size=0.2
|
||||
scale_mesh=Vector3(1, 1, 1)
|
||||
offset_mesh=Vector3(0, 0, 0)
|
||||
force_disable_mesh_compression=false
|
||||
64
Projector.cs
64
Projector.cs
|
|
@ -1,64 +0,0 @@
|
|||
using Godot;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
public static class Projector
|
||||
{
|
||||
public static int[,] Points = new int[1024,512];
|
||||
private static bool _gathered = false;
|
||||
public static void GatherPoints(PlanetHelper helper, int resolutionH = 2048, bool regather = false)
|
||||
{
|
||||
if (!regather && _gathered)
|
||||
return;
|
||||
Points = new int[resolutionH,resolutionH / 2];
|
||||
string filename = $"user://points-{resolutionH}-{resolutionH / 2}.dat";
|
||||
if (FileAccess.FileExists(filename))
|
||||
{
|
||||
var readfile = FileAccess.Open(filename, FileAccess.ModeFlags.Read);
|
||||
for (int x = 0; x < Points.GetLength(0); x++)
|
||||
{
|
||||
for (int y = 0; y < Points.GetLength(1); y++)
|
||||
{
|
||||
Points[x, y] = (int)readfile.Get32();
|
||||
}
|
||||
}
|
||||
readfile.Close();
|
||||
return;
|
||||
}
|
||||
|
||||
Parallel.ForEach(Enumerable.Range(0, Points.GetLength(0)), x =>
|
||||
{
|
||||
for (int y = 0; y < Points.GetLength(1); y++)
|
||||
{
|
||||
float yaw = (float)x / Points.GetLength(0) * 360f;
|
||||
float pitch = (float)y / Points.GetLength(1) * 180f;
|
||||
Vector3 point = Vector3.Up;
|
||||
point = point.Rotated(Vector3.Forward, Mathf.DegToRad(pitch));
|
||||
point = point.Rotated(Vector3.Up, Mathf.DegToRad(yaw));
|
||||
int index = helper.Octree.SearchNearest(point)?.Id ?? -1;
|
||||
Points[x,y] = index;
|
||||
}
|
||||
});
|
||||
var file = FileAccess.Open(filename, FileAccess.ModeFlags.Write);
|
||||
for (int x = 0; x < Points.GetLength(0); x++)
|
||||
for (int y = 0; y < Points.GetLength(1); y++)
|
||||
file.Store32((uint)Points[x,y]);
|
||||
_gathered = true;
|
||||
file.Close();
|
||||
}
|
||||
|
||||
public static ImageTexture Render(PlanetHelper helper)
|
||||
{
|
||||
var image = Image.CreateEmpty(Points.GetLength(0) + 1, Points.GetLength(1) + 1, false, Image.Format.Rgb8);;
|
||||
|
||||
for (int x = 0; x < Points.GetLength(0); x++)
|
||||
{
|
||||
for (int y = 0; y < Points.GetLength(1); y++)
|
||||
{
|
||||
image.SetPixel(x,y, helper.Mdt.GetVertexColor(Points[x,y]));
|
||||
}
|
||||
}
|
||||
return ImageTexture.CreateFromImage(image);
|
||||
}
|
||||
}
|
||||
|
|
@ -1 +0,0 @@
|
|||
uid://xjfkn5o2lo8l
|
||||
65
World.cs
65
World.cs
|
|
@ -1,65 +0,0 @@
|
|||
using Godot;
|
||||
using System;
|
||||
|
||||
public partial class World : Node3D
|
||||
{
|
||||
private bool _moving = false;
|
||||
[Export]
|
||||
private Node3D _yawNode;
|
||||
[Export]
|
||||
private Node3D _pitchNode;
|
||||
[Export]
|
||||
private Node3D _cameraNode;
|
||||
|
||||
[Export] private float _moveSensitivity = 1f/500f;
|
||||
[Export] private float _zoomSensitivity = 55f;
|
||||
|
||||
[Export] private MeshInstance3D _planet;
|
||||
|
||||
private PlanetHelper _planetHelper;
|
||||
|
||||
public override void _Ready()
|
||||
{
|
||||
base._Ready();
|
||||
}
|
||||
|
||||
public override void _Input(InputEvent @event)
|
||||
{
|
||||
if (@event is InputEventMouseButton mouseEvent)
|
||||
{
|
||||
if (mouseEvent.ButtonIndex == MouseButton.Left)
|
||||
{
|
||||
_moving = mouseEvent.Pressed;
|
||||
Input.MouseMode = _moving ? Input.MouseModeEnum.Captured : Input.MouseModeEnum.Visible;
|
||||
}
|
||||
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 override void _Process(double delta)
|
||||
{
|
||||
if (Input.IsActionJustPressed("enter"))
|
||||
{
|
||||
_planetHelper.ToggleAutoRun();
|
||||
}
|
||||
if (Input.IsActionJustPressed("spacebar"))
|
||||
{
|
||||
_planetHelper.ToggleAdvance();
|
||||
}
|
||||
|
||||
_planetHelper.Process();
|
||||
}
|
||||
}
|
||||
|
|
@ -1 +0,0 @@
|
|||
uid://cqbbtco508idv
|
||||
BIN
icosphere.obj
(Stored with Git LFS)
BIN
icosphere.obj
(Stored with Git LFS)
Binary file not shown.
|
|
@ -1,25 +0,0 @@
|
|||
[remap]
|
||||
|
||||
importer="wavefront_obj"
|
||||
importer_version=1
|
||||
type="Mesh"
|
||||
uid="uid://bpdmuywq6qd6d"
|
||||
path="res://.godot/imported/icosphere.obj-93d235c6f5a056cf3dbc8b25a3e41937.mesh"
|
||||
|
||||
[deps]
|
||||
|
||||
files=["res://.godot/imported/icosphere.obj-93d235c6f5a056cf3dbc8b25a3e41937.mesh"]
|
||||
|
||||
source_file="res://icosphere.obj"
|
||||
dest_files=["res://.godot/imported/icosphere.obj-93d235c6f5a056cf3dbc8b25a3e41937.mesh", "res://.godot/imported/icosphere.obj-93d235c6f5a056cf3dbc8b25a3e41937.mesh"]
|
||||
|
||||
[params]
|
||||
|
||||
generate_tangents=true
|
||||
generate_lods=true
|
||||
generate_shadow_mesh=true
|
||||
generate_lightmap_uv2=false
|
||||
generate_lightmap_uv2_texel_size=0.2
|
||||
scale_mesh=Vector3(1, 1, 1)
|
||||
offset_mesh=Vector3(0, 0, 0)
|
||||
force_disable_mesh_compression=false
|
||||
23
map.gdshader
23
map.gdshader
|
|
@ -1,23 +0,0 @@
|
|||
shader_type canvas_item;
|
||||
|
||||
uniform int mode = 1;
|
||||
uniform sampler2D gradient;
|
||||
void vertex() {
|
||||
// Called for every vertex the material is visible on.
|
||||
}
|
||||
|
||||
void fragment() {
|
||||
if (mode == 1) {
|
||||
COLOR = COLOR;
|
||||
}
|
||||
if (mode == 2)
|
||||
{
|
||||
vec4 ree = texture(gradient, vec2(COLOR.r));
|
||||
COLOR = ree;
|
||||
}
|
||||
}
|
||||
|
||||
//void light() {
|
||||
// // Called for every pixel for every light affecting the material.
|
||||
// // Uncomment to replace the default light processing function with this one.
|
||||
//}
|
||||
|
|
@ -1 +0,0 @@
|
|||
uid://b5n8mkb7vo6of
|
||||
12
map.tres
12
map.tres
|
|
@ -1,12 +0,0 @@
|
|||
[gd_resource type="ShaderMaterial" format=3 uid="uid://k3teblrpopsb"]
|
||||
|
||||
[ext_resource type="Shader" uid="uid://b5n8mkb7vo6of" path="res://map.gdshader" id="1_l44ik"]
|
||||
[ext_resource type="Gradient" uid="uid://b5l44rktieewe" path="res://planet_gradient.tres" id="2_dobfw"]
|
||||
|
||||
[sub_resource type="GradientTexture1D" id="GradientTexture1D_e6qn0"]
|
||||
gradient = ExtResource("2_dobfw")
|
||||
|
||||
[resource]
|
||||
shader = ExtResource("1_l44ik")
|
||||
shader_parameter/mode = 1
|
||||
shader_parameter/gradient = SubResource("GradientTexture1D_e6qn0")
|
||||
|
|
@ -1,32 +0,0 @@
|
|||
shader_type spatial;
|
||||
render_mode unshaded;
|
||||
|
||||
uniform int mode = 1;
|
||||
uniform sampler2D gradient;
|
||||
varying vec3 world_position;
|
||||
varying flat float color;
|
||||
void vertex() {
|
||||
// Called for every vertex the material is visible on.
|
||||
world_position = VERTEX;
|
||||
color = COLOR.r;
|
||||
}
|
||||
|
||||
void fragment() {
|
||||
// Called for every pixel the material is visible on.
|
||||
float temp = sin((world_position.y-95.0) / 60.0);
|
||||
|
||||
temp = clamp(temp, 0.0, 1)*28.0;
|
||||
if (mode == 1) {
|
||||
ALBEDO = vec3(COLOR.r, COLOR.g, COLOR.b);
|
||||
}
|
||||
if (mode == 2)
|
||||
{
|
||||
vec4 ree = texture(gradient, vec2(color));
|
||||
ALBEDO = vec3(ree.x, ree.y, ree.z);
|
||||
}
|
||||
}
|
||||
|
||||
//void light() {
|
||||
// // Called for every pixel for every light affecting the material.
|
||||
// // Uncomment to replace the default light processing function with this one.
|
||||
//}
|
||||
|
|
@ -1 +0,0 @@
|
|||
uid://bi1msxvmhvcqf
|
||||
13
planet.tres
13
planet.tres
|
|
@ -1,13 +0,0 @@
|
|||
[gd_resource type="ShaderMaterial" format=3 uid="uid://c55st036tapeo"]
|
||||
|
||||
[ext_resource type="Shader" uid="uid://bi1msxvmhvcqf" path="res://planet.gdshader" id="1_p4gwj"]
|
||||
[ext_resource type="Gradient" uid="uid://b5l44rktieewe" path="res://planet_gradient.tres" id="2_rbkur"]
|
||||
|
||||
[sub_resource type="GradientTexture1D" id="GradientTexture1D_5ojgt"]
|
||||
gradient = ExtResource("2_rbkur")
|
||||
|
||||
[resource]
|
||||
render_priority = 0
|
||||
shader = ExtResource("1_p4gwj")
|
||||
shader_parameter/mode = 1
|
||||
shader_parameter/gradient = SubResource("GradientTexture1D_5ojgt")
|
||||
|
|
@ -1,5 +0,0 @@
|
|||
[gd_resource type="Gradient" format=3 uid="uid://b5l44rktieewe"]
|
||||
|
||||
[resource]
|
||||
offsets = PackedFloat32Array(0.020102732, 0.21077614, 0.4361486, 0.4835682, 0.49508524, 0.49909908, 0.52932715, 0.5585736, 0.6065243, 0.65517426, 1)
|
||||
colors = PackedColorArray(2.5268645e-07, 3.128499e-07, 0.274243, 1, 8.904189e-07, 2.4065375e-07, 0.42930406, 1, 0.20772403, 0.34871656, 0.66512626, 1, 0.3622311, 0.6195445, 0.94423914, 1, 0.10825918, 0.3611443, 0.64106447, 1, 0.61873, 0.8132518, 0.40761396, 1, 0.35594854, 0.5452644, 0.23284999, 1, 0.77351433, 0.7971149, 0.5667908, 1, 0.7397217, 0.59494364, 0.46294695, 1, 0.3225033, 0.42213485, 0.47578907, 1, 1, 1, 1, 1)
|
||||
|
|
@ -1,5 +0,0 @@
|
|||
[gd_resource type="Gradient" format=3 uid="uid://cq6xba4c8m2j8"]
|
||||
|
||||
[resource]
|
||||
offsets = PackedFloat32Array(0, 0.499, 0.501, 1)
|
||||
colors = PackedColorArray(0, 0, 0, 1, 0.17078295, 0.40157902, 0.9999513, 1, 0.4204993, 0.7579306, 0.19122374, 1, 1, 1, 1, 1)
|
||||
18
sphere.tres
18
sphere.tres
File diff suppressed because one or more lines are too long
49
world.tscn
49
world.tscn
|
|
@ -1,49 +0,0 @@
|
|||
[gd_scene format=3 uid="uid://bnecr4011unaf"]
|
||||
|
||||
[ext_resource type="Script" uid="uid://cqbbtco508idv" path="res://World.cs" id="1_tlwt5"]
|
||||
[ext_resource type="ArrayMesh" uid="uid://65modei4jwaj" path="res://PlanetBase.obj" id="4_aqk2v"]
|
||||
|
||||
[sub_resource type="ProceduralSkyMaterial" id="ProceduralSkyMaterial_fj7yv"]
|
||||
|
||||
[sub_resource type="Sky" id="Sky_tlwt5"]
|
||||
sky_material = SubResource("ProceduralSkyMaterial_fj7yv")
|
||||
|
||||
[sub_resource type="Environment" id="Environment_aqk2v"]
|
||||
background_mode = 2
|
||||
sky = SubResource("Sky_tlwt5")
|
||||
ambient_light_source = 3
|
||||
reflected_light_source = 2
|
||||
|
||||
[sub_resource type="CameraAttributesPractical" id="CameraAttributesPractical_036b0"]
|
||||
|
||||
[node name="World" type="Node3D" unique_id=1246998319 node_paths=PackedStringArray("_yawNode", "_pitchNode", "_cameraNode", "_planet")]
|
||||
script = ExtResource("1_tlwt5")
|
||||
_yawNode = NodePath("Yaw")
|
||||
_pitchNode = NodePath("Yaw/Pitch")
|
||||
_cameraNode = NodePath("Yaw/Pitch/Camera3D")
|
||||
_planet = NodePath("Icosphere")
|
||||
|
||||
[node name="WorldEnvironment" type="WorldEnvironment" parent="." unique_id=1682967023]
|
||||
environment = SubResource("Environment_aqk2v")
|
||||
camera_attributes = SubResource("CameraAttributesPractical_036b0")
|
||||
|
||||
[node name="DirectionalLight3D" type="DirectionalLight3D" parent="." unique_id=547620692]
|
||||
transform = Transform3D(0.88874525, -0.29675773, 0.3493804, 0, 0.7621714, 0.6473753, -0.45840138, -0.5753517, 0.6773762, 0, 0, 0)
|
||||
|
||||
[node name="Icosphere" type="MeshInstance3D" parent="." unique_id=1909179361]
|
||||
transform = Transform3D(0.01, 0, 0, 0, 0.01, 0, 0, 0, 0.01, 0, 0, 0)
|
||||
mesh = ExtResource("4_aqk2v")
|
||||
|
||||
[node name="Yaw" type="Node3D" parent="." unique_id=743949193]
|
||||
|
||||
[node name="Pitch" type="Node3D" parent="Yaw" unique_id=190965746]
|
||||
|
||||
[node name="Camera3D" type="Camera3D" parent="Yaw/Pitch" unique_id=1928139403]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 29.95575)
|
||||
fov = 5.0
|
||||
|
||||
[node name="UI" type="Control" parent="." unique_id=242647974]
|
||||
layout_mode = 3
|
||||
anchors_preset = 0
|
||||
offset_right = 40.0
|
||||
offset_bottom = 40.0
|
||||
Loading…
Add table
Add a link
Reference in a new issue