Initial Commit

This commit is contained in:
Aada 2026-03-02 15:04:15 +02:00
commit cf76e5c9b2
43 changed files with 1779 additions and 0 deletions

4
.editorconfig Normal file
View file

@ -0,0 +1,4 @@
root = true
[*]
charset = utf-8

42
.gitattributes vendored Normal file
View file

@ -0,0 +1,42 @@
# Normalize EOL for all files that Git considers text files.
* text=auto eol=lf
# Git LFS Tracking (Assets)
# 3D Models
*.fbx filter=lfs diff=lfs merge=lfs -text
*.gltf filter=lfs diff=lfs merge=lfs -text
*.glb filter=lfs diff=lfs merge=lfs -text
*.blend filter=lfs diff=lfs merge=lfs -text
*.obj filter=lfs diff=lfs merge=lfs -text
# Images
*.png filter=lfs diff=lfs merge=lfs -text
*.svg filter=lfs diff=lfs merge=lfs -text
*.jpg filter=lfs diff=lfs merge=lfs -text
*.jpeg filter=lfs diff=lfs merge=lfs -text
*.gif filter=lfs diff=lfs merge=lfs -text
*.tga filter=lfs diff=lfs merge=lfs -text
*.webp filter=lfs diff=lfs merge=lfs -text
*.exr filter=lfs diff=lfs merge=lfs -text
*.hdr filter=lfs diff=lfs merge=lfs -text
*.dds filter=lfs diff=lfs merge=lfs -text
# Audio
*.mp3 filter=lfs diff=lfs merge=lfs -text
*.wav filter=lfs diff=lfs merge=lfs -text
*.ogg filter=lfs diff=lfs merge=lfs -text
# Font & Icon
*.ttf filter=lfs diff=lfs merge=lfs -text
*.otf filter=lfs diff=lfs merge=lfs -text
*.ico filter=lfs diff=lfs merge=lfs -text
# Godot LFS Specific
*.scn filter=lfs diff=lfs merge=lfs -text
*.res filter=lfs diff=lfs merge=lfs -text
*.material filter=lfs diff=lfs merge=lfs -text
*.anim filter=lfs diff=lfs merge=lfs -text
*.mesh filter=lfs diff=lfs merge=lfs -text
*.lmbake filter=lfs diff=lfs merge=lfs -text

3
.gitignore vendored Normal file
View file

@ -0,0 +1,3 @@
# Godot 4+ specific ignores
.godot/
/android/

13
.idea/.idea.adatonic/.idea/.gitignore generated vendored Normal file
View file

@ -0,0 +1,13 @@
# Default ignored files
/shelf/
/workspace.xml
# Rider ignored files
/modules.xml
/projectSettingsUpdater.xml
/contentModel.xml
/.idea.adatonic.iml
# Editor-based HTTP Client requests
/httpRequests/
# Datasource local storage ignored files
/dataSources/
/dataSources.local.xml

View file

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Encoding" addBOMForNewFiles="with BOM under Windows, with no BOM otherwise" />
</project>

View file

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="UserContentModel">
<attachedFolders />
<explicitIncludes />
<explicitExcludes />
</component>
</project>

6
.idea/.idea.adatonic/.idea/vcs.xml generated Normal file
View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git" />
</component>
</project>

7
LICENSE.md Normal file
View file

@ -0,0 +1,7 @@
Copyright (c) 2026 Aada Tikkanen
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

2
PlanetLow.mtl Normal file
View file

@ -0,0 +1,2 @@
# Blender 5.0.1 MTL File: 'None'
# www.blender.org

45
README.md Normal file
View file

@ -0,0 +1,45 @@
# Adatonics
Planet generation with simple plate tectonics.
## Description
TODO
## Getting Started
### Dependencies
* Godot v4.6.1
### Installing
* Open project in Godot
* TODO: Binaries
### Executing program
* TODO
## Help
* TODO
## Authors
Aada Tikkanen (Ade9)
## Version History
* 0.0.0
* Initial Commit
## License
This project is licensed under the MIT License - see the LICENSE.md file for details
## Acknowledgments
* [Fractal Philosophy's video](https://www.youtube.com/watch?v=7xL0udlhnqI)
* [Devote's video](https://www.youtube.com/watch?v=CeJz8tsgCPw)
* [DomPizzie's readme template](https://gist.github.com/DomPizzie/7a5ff55ffa9081f2de27c315f5018afc)

7
adatonic.csproj Normal file
View file

@ -0,0 +1,7 @@
<Project Sdk="Godot.NET.Sdk/4.6.1">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<TargetFramework Condition=" '$(GodotTargetPlatform)' == 'android' ">net9.0</TargetFramework>
<EnableDynamicLoading>true</EnableDynamicLoading>
</PropertyGroup>
</Project>

19
adatonic.sln Normal file
View file

@ -0,0 +1,19 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 2012
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "adatonic", "adatonic.csproj", "{ADF8C75F-73BD-49E2-AFF0-C630B5D45619}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
ExportDebug|Any CPU = ExportDebug|Any CPU
ExportRelease|Any CPU = ExportRelease|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{ADF8C75F-73BD-49E2-AFF0-C630B5D45619}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{ADF8C75F-73BD-49E2-AFF0-C630B5D45619}.Debug|Any CPU.Build.0 = Debug|Any CPU
{ADF8C75F-73BD-49E2-AFF0-C630B5D45619}.ExportDebug|Any CPU.ActiveCfg = ExportDebug|Any CPU
{ADF8C75F-73BD-49E2-AFF0-C630B5D45619}.ExportDebug|Any CPU.Build.0 = ExportDebug|Any CPU
{ADF8C75F-73BD-49E2-AFF0-C630B5D45619}.ExportRelease|Any CPU.ActiveCfg = ExportRelease|Any CPU
{ADF8C75F-73BD-49E2-AFF0-C630B5D45619}.ExportRelease|Any CPU.Build.0 = ExportRelease|Any CPU
EndGlobalSection
EndGlobal

View file

@ -0,0 +1,6 @@
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AMain_005FScriptMethods_002Egenerated_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003F_002E_002E_003Fhome_003Fade9_003F_002Econfig_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FSourcesCache_003Fb637bee5badc9b536d43ca23ca5744325c67bb7_003FMain_005FScriptMethods_002Egenerated_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ARandom_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003F_002E_002E_003Fhome_003Fade9_003F_002Econfig_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F2e87203df15b4d60b9c670dd76fb5ed8dba400_003F3a_003F5188629f_003FRandom_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AThrowHelpers_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003F_002E_002E_003Fhome_003Fade9_003F_002Econfig_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F2e87203df15b4d60b9c670dd76fb5ed8dba400_003F61_003Fd1d5ae77_003FThrowHelpers_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AThrowHelper_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003F_002E_002E_003Fhome_003Fade9_003F_002Econfig_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F2e87203df15b4d60b9c670dd76fb5ed8dba400_003F8c_003F9b15e004_003FThrowHelper_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AThrowHelper_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003F_002E_002E_003Fhome_003Fade9_003F_002Econfig_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fa7769c42125c450287f439cf7637d25b83800_003F36_003F88fa510a_003FThrowHelper_002Ecs_002Fz_003A2_002D1/@EntryIndexedValue">ForceIncluded</s:String></wpf:ResourceDictionary>

BIN
assets/PlanetBase.glb (Stored with Git LFS) Normal file

Binary file not shown.

View file

@ -0,0 +1,54 @@
[remap]
importer="scene"
importer_version=1
type="PackedScene"
uid="uid://cdtbfiae03msw"
path="res://.godot/imported/PlanetBase.glb-cb9772b7baab7be7a9066dd45b99a7d7.scn"
[deps]
source_file="res://assets/PlanetBase.glb"
dest_files=["res://.godot/imported/PlanetBase.glb-cb9772b7baab7be7a9066dd45b99a7d7.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": false,
"save_to_file/fallback_path": "res://sphere.tres",
"save_to_file/path": "uid://2kh0vgcigdse"
}
}
}
gltf/naming_version=2
gltf/embedded_image_handling=1

BIN
assets/PlanetBase.obj (Stored with Git LFS) Normal file

Binary file not shown.

View file

@ -0,0 +1,25 @@
[remap]
importer="wavefront_obj"
importer_version=1
type="Mesh"
uid="uid://65modei4jwaj"
path="res://.godot/imported/PlanetBase.obj-e7dd6f84cf7385297b2403f29d44bb02.mesh"
[deps]
files=["res://.godot/imported/PlanetBase.obj-e7dd6f84cf7385297b2403f29d44bb02.mesh"]
source_file="res://assets/PlanetBase.obj"
dest_files=["res://.godot/imported/PlanetBase.obj-e7dd6f84cf7385297b2403f29d44bb02.mesh", "res://.godot/imported/PlanetBase.obj-e7dd6f84cf7385297b2403f29d44bb02.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
assets/PlanetLow.obj (Stored with Git LFS) Normal file

Binary file not shown.

View file

@ -0,0 +1,25 @@
[remap]
importer="wavefront_obj"
importer_version=1
type="Mesh"
uid="uid://i15y020lbl21"
path="res://.godot/imported/PlanetLow.obj-04144c5140c8d3a6a9a4499e7542afe4.mesh"
[deps]
files=["res://.godot/imported/PlanetLow.obj-04144c5140c8d3a6a9a4499e7542afe4.mesh"]
source_file="res://assets/PlanetLow.obj"
dest_files=["res://.godot/imported/PlanetLow.obj-04144c5140c8d3a6a9a4499e7542afe4.mesh", "res://.godot/imported/PlanetLow.obj-04144c5140c8d3a6a9a4499e7542afe4.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
assets/PlanetMed.obj (Stored with Git LFS) Normal file

Binary file not shown.

View file

@ -0,0 +1,25 @@
[remap]
importer="wavefront_obj"
importer_version=1
type="Mesh"
uid="uid://cxpivgqm7x7ky"
path="res://.godot/imported/PlanetMed.obj-a5753ae94b99c04c1b3bfb2440cece04.mesh"
[deps]
files=["res://.godot/imported/PlanetMed.obj-a5753ae94b99c04c1b3bfb2440cece04.mesh"]
source_file="res://assets/PlanetMed.obj"
dest_files=["res://.godot/imported/PlanetMed.obj-a5753ae94b99c04c1b3bfb2440cece04.mesh", "res://.godot/imported/PlanetMed.obj-a5753ae94b99c04c1b3bfb2440cece04.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
assets/icosphere.obj (Stored with Git LFS) Normal file

Binary file not shown.

View file

@ -0,0 +1,25 @@
[remap]
importer="wavefront_obj"
importer_version=1
type="Mesh"
uid="uid://bpdmuywq6qd6d"
path="res://.godot/imported/icosphere.obj-18fc8dc1aa0ca7202b62526d6632660d.mesh"
[deps]
files=["res://.godot/imported/icosphere.obj-18fc8dc1aa0ca7202b62526d6632660d.mesh"]
source_file="res://assets/icosphere.obj"
dest_files=["res://.godot/imported/icosphere.obj-18fc8dc1aa0ca7202b62526d6632660d.mesh", "res://.godot/imported/icosphere.obj-18fc8dc1aa0ca7202b62526d6632660d.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
icon.svg (Stored with Git LFS) Normal file

Binary file not shown.

43
icon.svg.import Normal file
View file

@ -0,0 +1,43 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://n56csi5ekat"
path="res://.godot/imported/icon.svg-218a8f2b3041327d8a5756f3a245f83b.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://icon.svg"
dest_files=["res://.godot/imported/icon.svg-218a8f2b3041327d8a5756f3a245f83b.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
compress/rdo_quality_loss=0.0
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/channel_remap/red=0
process/channel_remap/green=1
process/channel_remap/blue=2
process/channel_remap/alpha=3
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1
svg/scale=1.0
editor/scale_with_editor_scale=false
editor/convert_colors_with_editor_theme=false

53
project.godot Normal file
View file

@ -0,0 +1,53 @@
; Engine configuration file.
; It's best edited using the editor UI and not directly,
; since the parameters that go here are not all obvious.
;
; Format:
; [section] ; section goes between []
; param=value ; assign values to parameters
config_version=5
[application]
config/name="adatonic"
run/main_scene="uid://csfh7ptgerpm2"
config/features=PackedStringArray("4.6", "C#", "GL Compatibility")
config/icon="res://icon.svg"
[dotnet]
project/assembly_name="adatonic"
[input]
spacebar={
"deadzone": 0.2,
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":32,"key_label":0,"unicode":32,"location":0,"echo":false,"script":null)
]
}
enter={
"deadzone": 0.2,
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194309,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null)
]
}
mouse_primary={
"deadzone": 0.2,
"events": [Object(InputEventMouseButton,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"button_mask":1,"position":Vector2(343.16016, 11.21875),"global_position":Vector2(352.16016, 59.21875),"factor":1.0,"button_index":1,"canceled":false,"pressed":true,"double_click":false,"script":null)
]
}
mouse_secondary={
"deadzone": 0.2,
"events": [Object(InputEventMouseButton,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"button_mask":2,"position":Vector2(448.19922, 12.3359375),"global_position":Vector2(457.19922, 60.335938),"factor":1.0,"button_index":2,"canceled":false,"pressed":true,"double_click":false,"script":null)
]
}
[physics]
3d/physics_engine="Jolt Physics"
[rendering]
rendering_device/driver.windows="d3d12"
renderer/rendering_method="gl_compatibility"
renderer/rendering_method.mobile="gl_compatibility"

View file

@ -0,0 +1,5 @@
[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)

View file

@ -0,0 +1,5 @@
[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)

199
scenes/MainScene.tscn Normal file
View file

@ -0,0 +1,199 @@
[gd_scene format=3 uid="uid://csfh7ptgerpm2"]
[ext_resource type="Script" uid="uid://bhpic251bgvgk" path="res://src/Main.cs" id="1_611at"]
[ext_resource type="ArrayMesh" uid="uid://65modei4jwaj" path="res://assets/PlanetBase.obj" id="2_k24pf"]
[ext_resource type="Material" uid="uid://c55st036tapeo" path="res://shaders/planet.tres" id="3_ygjfp"]
[ext_resource type="Material" uid="uid://k3teblrpopsb" path="res://shaders/map.tres" id="4_1wiy7"]
[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_611at")
_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_k24pf")
surface_material_override/0 = ExtResource("3_ygjfp")
[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_1wiy7")
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"]

23
shaders/map.gdshader Normal file
View file

@ -0,0 +1,23 @@
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
shaders/map.gdshader.uid Normal file
View file

@ -0,0 +1 @@
uid://b5n8mkb7vo6of

12
shaders/map.tres Normal file
View file

@ -0,0 +1,12 @@
[gd_resource type="ShaderMaterial" format=3 uid="uid://k3teblrpopsb"]
[ext_resource type="Shader" uid="uid://b5n8mkb7vo6of" path="res://shaders/map.gdshader" id="1_l44ik"]
[ext_resource type="Gradient" uid="uid://b5l44rktieewe" path="res://resources/planet_gradient.tres" id="2_1qhyp"]
[sub_resource type="GradientTexture1D" id="GradientTexture1D_e6qn0"]
gradient = ExtResource("2_1qhyp")
[resource]
shader = ExtResource("1_l44ik")
shader_parameter/mode = 1
shader_parameter/gradient = SubResource("GradientTexture1D_e6qn0")

32
shaders/planet.gdshader Normal file
View file

@ -0,0 +1,32 @@
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.
//}

View file

@ -0,0 +1 @@
uid://bi1msxvmhvcqf

13
shaders/planet.tres Normal file
View file

@ -0,0 +1,13 @@
[gd_resource type="ShaderMaterial" format=3 uid="uid://c55st036tapeo"]
[ext_resource type="Shader" uid="uid://bi1msxvmhvcqf" path="res://shaders/planet.gdshader" id="1_p4gwj"]
[ext_resource type="Gradient" uid="uid://b5l44rktieewe" path="res://resources/planet_gradient.tres" id="2_tjikr"]
[sub_resource type="GradientTexture1D" id="GradientTexture1D_5ojgt"]
gradient = ExtResource("2_tjikr")
[resource]
render_priority = 0
shader = ExtResource("1_p4gwj")
shader_parameter/mode = 1
shader_parameter/gradient = SubResource("GradientTexture1D_5ojgt")

139
src/Main.cs Normal file
View file

@ -0,0 +1,139 @@
#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
src/Main.cs.uid Normal file
View file

@ -0,0 +1 @@
uid://bhpic251bgvgk

144
src/Oct.cs Normal file
View file

@ -0,0 +1,144 @@
#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)
{
if (Node != null)
{
float dist = position.DistanceSquaredTo(Node.Position);
if (dist < bestDist)
{
bestDist = dist;
best = Node;
}
}
if (Trees == null)
return;
int first = WhichSubOct(position);
Trees[first]?.SearchNearest(position, ref best, ref bestDist);
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
src/Oct.cs.uid Normal file
View file

@ -0,0 +1 @@
uid://c4pay0n8iktfa

703
src/PlanetHelper.cs Normal file
View file

@ -0,0 +1,703 @@
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,
Compression,
Shear
}
public class StrainAnalysis
{
public float Magnitude;
public StrainType Type;
public float NormalRate;
public float ShearRate;
}
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)
{
Vector3 normal = pointOnSphere.Normalized();
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)
);
Vector3 tangent = randomVec.Cross(normal);
if (tangent.Dot(tangent) < 1e-6f)
{
randomVec = new Vector3(0, 1, 0);
tangent = randomVec.Cross(normal);
}
Vector3 normalizedTangent = tangent.Normalized();
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();
Vector3 edge = p2 - p1;
float edgeLength = edge.Length();
if (edgeLength < float.Epsilon)
{
result.Magnitude = 0;
result.Type = StrainType.Shear; // Default
return result;
}
Vector3 relVelocity = v2 - v1;
float relVelMag = relVelocity.Length();
float dot = relVelocity.Dot(edge);
result.NormalRate = dot / edgeLength;
float normalRateSq = result.NormalRate * result.NormalRate;
float shearRateSq = relVelMag * relVelMag - normalRateSq;
result.ShearRate = (shearRateSq > 0) ? (float)Math.Sqrt(shearRateSq) : 0;
result.Magnitude = (float)Math.Sqrt(normalRateSq + shearRateSq);
float absNormal = Math.Abs(result.NormalRate);
float absShear = Math.Abs(result.ShearRate);
if (absNormal > absShear)
{
result.Type = result.NormalRate > 0
? StrainType.Tension
: StrainType.Compression;
}
else
{
result.Type = StrainType.Shear;
}
return result;
}
public static StrainAnalysis AverageStrainList(List<StrainAnalysis> strains)
{
if (strains == null || strains.Count == 0)
{
return new StrainAnalysis();
}
int count = strains.Count;
float sumMagnitude = 0;
float sumNormalRate = 0;
float sumShearRate = 0;
int tensionCount = 0;
int compressionCount = 0;
int shearCount = 0;
foreach (var s in strains)
{
sumMagnitude += s.Magnitude;
sumNormalRate += s.NormalRate;
sumShearRate += s.ShearRate;
switch (s.Type)
{
case StrainType.Tension:
tensionCount++;
break;
case StrainType.Compression:
compressionCount++;
break;
case StrainType.Shear:
shearCount++;
break;
}
}
float avgMagnitude = sumMagnitude / count;
float avgNormalRate = sumNormalRate / count;
float avgShearRate = sumShearRate / count;
StrainType averageType = StrainType.Shear;
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
src/PlanetHelper.cs.uid Normal file
View file

@ -0,0 +1 @@
uid://d2661qrqttvva

64
src/Projector.cs Normal file
View file

@ -0,0 +1,64 @@
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
src/Projector.cs.uid Normal file
View file

@ -0,0 +1 @@
uid://xjfkn5o2lo8l