From 4eef953ed68ffea686beda115dd162938761f8fc Mon Sep 17 00:00:00 2001 From: lucastucious Date: Fri, 27 Jun 2025 14:07:21 +0200 Subject: [PATCH] First commit --- .editorconfig | 4 + .gitattributes | 2 + .gitignore | 3 + addons/SimpleFormatOnSave/LICENSE.txt | 21 +++ addons/SimpleFormatOnSave/formatter.gd | 10 ++ addons/SimpleFormatOnSave/formatter.gd.uid | 1 + addons/SimpleFormatOnSave/plugin.cfg | 7 + .../SimpleFormatOnSave/rules/blank_lines.gd | 65 ++++++++ .../rules/blank_lines.gd.uid | 1 + addons/SimpleFormatOnSave/rules/spacing.gd | 155 ++++++++++++++++++ .../SimpleFormatOnSave/rules/spacing.gd.uid | 1 + .../simple_format_on_save.gd | 82 +++++++++ .../simple_format_on_save.gd.uid | 1 + icon.svg | 1 + icon.svg.import | 43 +++++ player/player.gd | 28 ++++ player/player.gd.uid | 1 + project.godot | 24 +++ scenes/chunk_generator.gd | 24 +++ scenes/chunk_generator.gd.uid | 1 + scenes/world.tscn | 29 ++++ world_gen/main.tscn | 56 +++++++ world_gen/main.tscn2944845596.tmp | 54 ++++++ world_gen/new_plane_mesh.tres | 7 + world_gen/world_generator.gd | 149 +++++++++++++++++ world_gen/world_generator.gd.uid | 1 + 26 files changed, 771 insertions(+) create mode 100644 .editorconfig create mode 100644 .gitattributes create mode 100644 .gitignore create mode 100644 addons/SimpleFormatOnSave/LICENSE.txt create mode 100644 addons/SimpleFormatOnSave/formatter.gd create mode 100644 addons/SimpleFormatOnSave/formatter.gd.uid create mode 100644 addons/SimpleFormatOnSave/plugin.cfg create mode 100644 addons/SimpleFormatOnSave/rules/blank_lines.gd create mode 100644 addons/SimpleFormatOnSave/rules/blank_lines.gd.uid create mode 100644 addons/SimpleFormatOnSave/rules/spacing.gd create mode 100644 addons/SimpleFormatOnSave/rules/spacing.gd.uid create mode 100644 addons/SimpleFormatOnSave/simple_format_on_save.gd create mode 100644 addons/SimpleFormatOnSave/simple_format_on_save.gd.uid create mode 100644 icon.svg create mode 100644 icon.svg.import create mode 100644 player/player.gd create mode 100644 player/player.gd.uid create mode 100644 project.godot create mode 100644 scenes/chunk_generator.gd create mode 100644 scenes/chunk_generator.gd.uid create mode 100644 scenes/world.tscn create mode 100644 world_gen/main.tscn create mode 100644 world_gen/main.tscn2944845596.tmp create mode 100644 world_gen/new_plane_mesh.tres create mode 100644 world_gen/world_generator.gd create mode 100644 world_gen/world_generator.gd.uid diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..f28239b --- /dev/null +++ b/.editorconfig @@ -0,0 +1,4 @@ +root = true + +[*] +charset = utf-8 diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..8ad74f7 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,2 @@ +# Normalize EOL for all files that Git considers text files. +* text=auto eol=lf diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..0af181c --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +# Godot 4+ specific ignores +.godot/ +/android/ diff --git a/addons/SimpleFormatOnSave/LICENSE.txt b/addons/SimpleFormatOnSave/LICENSE.txt new file mode 100644 index 0000000..933cadc --- /dev/null +++ b/addons/SimpleFormatOnSave/LICENSE.txt @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2025-present VitSoonYoung - + +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. diff --git a/addons/SimpleFormatOnSave/formatter.gd b/addons/SimpleFormatOnSave/formatter.gd new file mode 100644 index 0000000..dbde166 --- /dev/null +++ b/addons/SimpleFormatOnSave/formatter.gd @@ -0,0 +1,10 @@ +class_name Formatter + +const RuleSpacing = preload("./rules/spacing.gd") +const RuleBlankLines = preload("./rules/blank_lines.gd") + + +func format_code(code: String) -> String: + code = RuleSpacing.apply(code) + code = RuleBlankLines.apply(code) + return code diff --git a/addons/SimpleFormatOnSave/formatter.gd.uid b/addons/SimpleFormatOnSave/formatter.gd.uid new file mode 100644 index 0000000..b666797 --- /dev/null +++ b/addons/SimpleFormatOnSave/formatter.gd.uid @@ -0,0 +1 @@ +uid://clqnxlippxlm4 diff --git a/addons/SimpleFormatOnSave/plugin.cfg b/addons/SimpleFormatOnSave/plugin.cfg new file mode 100644 index 0000000..d7d61a9 --- /dev/null +++ b/addons/SimpleFormatOnSave/plugin.cfg @@ -0,0 +1,7 @@ +[plugin] + +name="Simple Format On Save" +description="Combination of simple gdscript formatter & Format On Save with extra rules for beatiful formatting...maybe" +author="VitSoonYoung" +version="0.1" +script="simple_format_on_save.gd" diff --git a/addons/SimpleFormatOnSave/rules/blank_lines.gd b/addons/SimpleFormatOnSave/rules/blank_lines.gd new file mode 100644 index 0000000..4d8c521 --- /dev/null +++ b/addons/SimpleFormatOnSave/rules/blank_lines.gd @@ -0,0 +1,65 @@ +class_name RuleBlankLines + +var FORMAT_ACTION := "simple_format_on_save/format_code" +var format_key: InputEventKey + + +static func apply(code: String) -> String: + var trim1_regex = RegEx.create_from_string("\n{2,}") + code = trim1_regex.sub(code, "\n\n", true) + code = _blank_for_func_class(code) + var trim2_regex = RegEx.create_from_string("\n{3,}") + code = trim2_regex.sub(code, "\n\n\n", true) + return code + + +static func _blank_for_func_class(code: String) -> String: + var assignment_regex = RegEx.create_from_string(r".*=.*") + var statement_regex = RegEx.create_from_string(r"\s+(if|for|while|match)[\s|\(].*") + var misc_statement_regex = RegEx.create_from_string(r"\s+(else|elif|\}|\]).*") + var func_class_regex = RegEx.create_from_string(r".*(func|class) .*") + var comment_line_regex = RegEx.create_from_string(r"^\s*#") + var empty_line_regex = RegEx.create_from_string(r"^\s+$") + var lines := code.split('\n') + var modified_lines: Array[String] = [] + + for line: String in lines: + # Spaces between functions & classes + if func_class_regex.search(line): + if modified_lines.size() > 0: + var i := modified_lines.size() - 1 + + while i > 0 and comment_line_regex.search(modified_lines[i]): + i -= 1 + + if i == 0: + modified_lines.append(line) + continue + + modified_lines.insert(i + 1, "") + modified_lines.insert(i + 1, "") + + # 1 space between assignment & statement + if statement_regex.search(line): + if modified_lines.size() > 0: + var i := modified_lines.size() - 1 + + if assignment_regex.search(modified_lines[i]) and not statement_regex.search(modified_lines[i]): + modified_lines.insert(i + 1, "") + + else: + pass + + # Space after a code block (Doesn't work with spaces for now) + var indent_count := line.count("\t") + + if indent_count and not misc_statement_regex.search(line): + if modified_lines.size() > 0: + var i := modified_lines.size() - 1 + + if modified_lines[i].count("\t") > indent_count: + modified_lines.insert(i + 1, "") + + modified_lines.append(line) + + return "\n".join(modified_lines) diff --git a/addons/SimpleFormatOnSave/rules/blank_lines.gd.uid b/addons/SimpleFormatOnSave/rules/blank_lines.gd.uid new file mode 100644 index 0000000..452523a --- /dev/null +++ b/addons/SimpleFormatOnSave/rules/blank_lines.gd.uid @@ -0,0 +1 @@ +uid://dr266abkfhg1o diff --git a/addons/SimpleFormatOnSave/rules/spacing.gd b/addons/SimpleFormatOnSave/rules/spacing.gd new file mode 100644 index 0000000..915ff9f --- /dev/null +++ b/addons/SimpleFormatOnSave/rules/spacing.gd @@ -0,0 +1,155 @@ +class_name RuleSpacing + +const SYMBOLS = [ + r"\*\*=", + r"\*\*", + "<<=", + ">>=", + "<<", + ">>", + "==", + "!=", + ">=", + "<=", + "&&", + r"\|\|", + r"\+=", + "-=", + r"\*=", + "/=", + "%=", + "&=", + r"\^=", + r"\|=", + "~=", + ":=", + "->", + r"&", + r"\|", + r"\^", + "-", + r"\+", + "/", + r"\*", + ">", + "<", + "-", + "%", + "=", + ":", + ",", +]; +const KEYWORDS = [ + "and", + "is", + "or", + "not", +] + + +static func apply(code: String) -> String: + var string_regex = RegEx.new() + string_regex.compile(r'(? String: + var indent_regex = RegEx.create_from_string(r"^\s{4}") + var new_code = indent_regex.sub(code, "\t", true) + + while (code != new_code): + code = new_code + new_code = indent_regex.sub(code, "\t", true) + + var symbols_regex = "(" + ") | (".join(SYMBOLS) + ")" + symbols_regex = " * ?(" + symbols_regex + ") * " + var symbols_operator_regex = RegEx.create_from_string(symbols_regex) + code = symbols_operator_regex.sub(code, " $1 ", true) + + # ": =" => ":=" + code = RegEx.create_from_string(r": *=").sub(code, ":=", true) + + # "a(" => "a (" + code = RegEx.create_from_string(r"(?<=[\w\)\]]) *([\(:,])(?!=)").sub(code, "$1", true) + + # "( a" => "(a" + code = RegEx.create_from_string(r"([\(\{}]) *").sub(code, "$1", true) + + # "a )" => "a)" + code = RegEx.create_from_string(r" *([\)\}])").sub(code, "$1", true) + + # "if(" => "if (" + code = RegEx.create_from_string(r"\b(if|for|while|switch|match)\(").sub(code, "$1 (", true) + + var keywoisrd_regex = r"|".join(KEYWORDS) + var keyword_operator_regex = RegEx.create_from_string(r"(?<=[ \)\]])(" + keywoisrd_regex + r")(?=[ \(\[])") + code = keyword_operator_regex.sub(code, " $1 ", true) + + # tab "a\t=" => "a =" + code = RegEx.create_from_string(r"(\t*. * ?)\t * ").sub(code, "$1", true) + + #trim + code = RegEx.create_from_string("[ \t]*\n").sub(code, "\n", true) + + # " " => " " + code = RegEx.create_from_string(" +").sub(code, " ", true) + + # "= -a" => "= -a" + code = RegEx.create_from_string(r"([=,(] ?)- ").sub(code, "$1-", true) + + return code + + +static func _replace(text: String, what: String, forwhat: String) -> String: + var index := text.find(what) + + if index != -1: + text = text.substr(0, index) + forwhat + text.substr(index + what.length()) + + return text diff --git a/addons/SimpleFormatOnSave/rules/spacing.gd.uid b/addons/SimpleFormatOnSave/rules/spacing.gd.uid new file mode 100644 index 0000000..5b6c307 --- /dev/null +++ b/addons/SimpleFormatOnSave/rules/spacing.gd.uid @@ -0,0 +1 @@ +uid://wfodrj6vpfb6 diff --git a/addons/SimpleFormatOnSave/simple_format_on_save.gd b/addons/SimpleFormatOnSave/simple_format_on_save.gd new file mode 100644 index 0000000..2164754 --- /dev/null +++ b/addons/SimpleFormatOnSave/simple_format_on_save.gd @@ -0,0 +1,82 @@ +@tool +extends EditorPlugin + +var FORMAT_ACTION := "simple_format_on_save/format_code" +var format_key: InputEventKey +var formatter: Formatter + + +func _enter_tree(): + add_tool_menu_item("Format (Ctrl+Alt+L)", _on_format_code) + + if InputMap.has_action(FORMAT_ACTION): + InputMap.erase_action(FORMAT_ACTION) + + InputMap.add_action(FORMAT_ACTION) + format_key = InputEventKey.new() + format_key.keycode = KEY_L + format_key.ctrl_pressed = true + format_key.alt_pressed = true + InputMap.action_add_event(FORMAT_ACTION, format_key) + resource_saved.connect(on_resource_saved) + + +func _exit_tree(): + remove_tool_menu_item("Format (Ctrl+Alt+L)") + InputMap.erase_action(FORMAT_ACTION) + resource_saved.disconnect(on_resource_saved) + + +# Return true if formatted code != original code +func _on_format_code() -> bool: + var current_editor := EditorInterface.get_script_editor().get_current_editor() + + if not (current_editor and current_editor.is_class("ScriptTextEditor")): + return false + + var text_edit = current_editor.get_base_editor() + var code = text_edit.text + + if not formatter: + formatter = Formatter.new() + + var formatted_code = formatter.format_code(code) + + if not formatted_code: + return false + + if code.length() == formatted_code.length() and code == formatted_code: + return false + + var scroll_horizontal = text_edit.scroll_horizontal + var scroll_vertical = text_edit.scroll_vertical + var caret_column = text_edit.get_caret_column(0) + var caret_line = text_edit.get_caret_line(0) + + text_edit.text = formatted_code + text_edit.set_caret_line(caret_line) + text_edit.set_caret_column(caret_column) + text_edit.scroll_horizontal = scroll_horizontal + text_edit.scroll_vertical = scroll_vertical + + return true + + +func _shortcut_input(event: InputEvent) -> void: + if event is InputEventKey and event.is_pressed(): + if Input.is_action_pressed(FORMAT_ACTION): + _on_format_code() + + +# CALLED WHEN A SCRIPT IS SAVED +func on_resource_saved(resource: Resource): + if resource is Script: + var script: Script = resource + var current_script = get_editor_interface().get_script_editor().get_current_script() + + # Prevents other unsaved scripts from overwriting the active one + if current_script == script: + var is_modified: bool = _on_format_code() + + #if is_modified: + #print_rich("[color=#636363]Auto formatted code[/color]") diff --git a/addons/SimpleFormatOnSave/simple_format_on_save.gd.uid b/addons/SimpleFormatOnSave/simple_format_on_save.gd.uid new file mode 100644 index 0000000..a8e28ce --- /dev/null +++ b/addons/SimpleFormatOnSave/simple_format_on_save.gd.uid @@ -0,0 +1 @@ +uid://b6d7va37k5131 diff --git a/icon.svg b/icon.svg new file mode 100644 index 0000000..c6bbb7d --- /dev/null +++ b/icon.svg @@ -0,0 +1 @@ + diff --git a/icon.svg.import b/icon.svg.import new file mode 100644 index 0000000..fb31f00 --- /dev/null +++ b/icon.svg.import @@ -0,0 +1,43 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://cpufmd5631dpp" +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 diff --git a/player/player.gd b/player/player.gd new file mode 100644 index 0000000..acb7fb2 --- /dev/null +++ b/player/player.gd @@ -0,0 +1,28 @@ +extends CharacterBody3D + + +const SPEED = 5.0 +const JUMP_VELOCITY = 4.5 + + +func _physics_process(delta: float) -> void: + # Add the gravity. + if not is_on_floor(): + velocity += get_gravity() * delta + + # Handle jump. + if Input.is_action_just_pressed("ui_accept") and is_on_floor(): + velocity.y = JUMP_VELOCITY + + # Get the input direction and handle the movement/deceleration. + # As good practice, you should replace UI actions with custom gameplay actions. + var input_dir := Input.get_vector("ui_left", "ui_right", "ui_up", "ui_down") + var direction := (transform.basis * Vector3(input_dir.x, 0, input_dir.y)).normalized() + if direction: + velocity.x = direction.x * SPEED + velocity.z = direction.z * SPEED + else: + velocity.x = move_toward(velocity.x, 0, SPEED) + velocity.z = move_toward(velocity.z, 0, SPEED) + + move_and_slide() diff --git a/player/player.gd.uid b/player/player.gd.uid new file mode 100644 index 0000000..3be41de --- /dev/null +++ b/player/player.gd.uid @@ -0,0 +1 @@ +uid://b5io1eg4blrp5 diff --git a/project.godot b/project.godot new file mode 100644 index 0000000..b1e7067 --- /dev/null +++ b/project.godot @@ -0,0 +1,24 @@ +; 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="BoneForge" +run/main_scene="uid://bgofilfnbeisl" +config/features=PackedStringArray("4.5", "Forward Plus") +config/icon="res://icon.svg" + +[editor_plugins] + +enabled=PackedStringArray("res://addons/SimpleFormatOnSave/plugin.cfg") + +[network] + +limits/debugger/max_chars_per_second=99999999 diff --git a/scenes/chunk_generator.gd b/scenes/chunk_generator.gd new file mode 100644 index 0000000..076137e --- /dev/null +++ b/scenes/chunk_generator.gd @@ -0,0 +1,24 @@ +extends Node + +@export var chunks = 10 +const value = 300 + +var TerrainHtread: Thread +# Called when the node enters the scene tree for the first time. +func _ready() -> void: + TerrainHtread = Thread.new() + TerrainHtread.start(generate_chuks.bind(chunks,value,self)) + +func generate_chuks(_chunks,_value,parent): + for chunk in _chunks: + var checukn :Node3D= Node3D.new() + checukn.position = Vector3(randf_range(-_value,_value),0,randf_range(-_value,_value)) + checukn.set_script(load("res://world_generation/grok_chnk.gd")) + parent.call_deferred("add_child",checukn) + +# Called every frame. 'delta' is the elapsed time since the previous frame. +func _process(delta: float) -> void: + pass + +func _exit_tree() -> void: + TerrainHtread.wait_to_finish() diff --git a/scenes/chunk_generator.gd.uid b/scenes/chunk_generator.gd.uid new file mode 100644 index 0000000..25ab724 --- /dev/null +++ b/scenes/chunk_generator.gd.uid @@ -0,0 +1 @@ +uid://e63lq34qepsg diff --git a/scenes/world.tscn b/scenes/world.tscn new file mode 100644 index 0000000..508ec8f --- /dev/null +++ b/scenes/world.tscn @@ -0,0 +1,29 @@ +[gd_scene load_steps=5 format=3 uid="uid://bgofilfnbeisl"] + +[ext_resource type="Script" uid="uid://e63lq34qepsg" path="res://scenes/chunk_generator.gd" id="2_rwgxs"] + +[sub_resource type="ProceduralSkyMaterial" id="ProceduralSkyMaterial_nnsk1"] +sky_horizon_color = Color(0.66224277, 0.6717428, 0.6867428, 1) +ground_horizon_color = Color(0.66224277, 0.6717428, 0.6867428, 1) + +[sub_resource type="Sky" id="Sky_rwgxs"] +sky_material = SubResource("ProceduralSkyMaterial_nnsk1") + +[sub_resource type="Environment" id="Environment_4wyf3"] +background_mode = 2 +sky = SubResource("Sky_rwgxs") +tonemap_mode = 2 +glow_enabled = true + +[node name="world" type="Node3D"] + +[node name="WorldEnvironment" type="WorldEnvironment" parent="."] +environment = SubResource("Environment_4wyf3") + +[node name="DirectionalLight3D" type="DirectionalLight3D" parent="."] +transform = Transform3D(-0.8660254, -0.43301278, 0.25, 0, 0.49999997, 0.86602545, -0.50000006, 0.75, -0.43301266, 0, 0, 0) +shadow_enabled = true + +[node name="chunkGenerator" type="Node" parent="."] +script = ExtResource("2_rwgxs") +chunks = 2000 diff --git a/world_gen/main.tscn b/world_gen/main.tscn new file mode 100644 index 0000000..fa546aa --- /dev/null +++ b/world_gen/main.tscn @@ -0,0 +1,56 @@ +[gd_scene load_steps=9 format=3 uid="uid://cioop0k7xmww8"] + +[ext_resource type="Script" uid="uid://b4q4faaqbat0x" path="res://world_gen/world_generator.gd" id="1_y7qka"] +[ext_resource type="Script" uid="uid://b5io1eg4blrp5" path="res://player/player.gd" id="2_nwtrj"] + +[sub_resource type="ProceduralSkyMaterial" id="ProceduralSkyMaterial_y7qka"] +sky_horizon_color = Color(0.66224277, 0.6717428, 0.6867428, 1) +ground_horizon_color = Color(0.66224277, 0.6717428, 0.6867428, 1) + +[sub_resource type="Sky" id="Sky_nwtrj"] +sky_material = SubResource("ProceduralSkyMaterial_y7qka") + +[sub_resource type="Environment" id="Environment_vrc1b"] +background_mode = 2 +sky = SubResource("Sky_nwtrj") +tonemap_mode = 2 +volumetric_fog_enabled = true +volumetric_fog_density = 0.01 + +[sub_resource type="FastNoiseLite" id="FastNoiseLite_nwtrj"] +noise_type = 3 +seed = 280 +frequency = 0.0035 + +[sub_resource type="CapsuleShape3D" id="CapsuleShape3D_y7qka"] +resource_name = "PlayerCollision" + +[sub_resource type="CapsuleMesh" id="CapsuleMesh_nwtrj"] +resource_name = "PlayerMesh" + +[node name="main" type="Node"] + +[node name="WorldEnvironment" type="WorldEnvironment" parent="."] +environment = SubResource("Environment_vrc1b") + +[node name="DirectionalLight3D" type="DirectionalLight3D" parent="."] +transform = Transform3D(-0.8660254, -0.43301278, 0.25, 0, 0.49999997, 0.86602545, -0.50000006, 0.75, -0.43301266, 0, 0, 0) +shadow_enabled = true + +[node name="WorldGenerator" type="Node" parent="."] +script = ExtResource("1_y7qka") +noise = SubResource("FastNoiseLite_nwtrj") +height = 24.0 + +[node name="Player" type="CharacterBody3D" parent="."] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 104.43206, 0) +script = ExtResource("2_nwtrj") + +[node name="CollisionShape3D" type="CollisionShape3D" parent="Player"] +shape = SubResource("CapsuleShape3D_y7qka") + +[node name="MeshInstance3D" type="MeshInstance3D" parent="Player/CollisionShape3D"] +mesh = SubResource("CapsuleMesh_nwtrj") + +[node name="Camera3D" type="Camera3D" parent="Player"] +transform = Transform3D(1, 0, 0, 0, 0.9604348, 0.27850503, 0, -0.27850503, 0.9604348, 0, 1.8589027, 2.8465767) diff --git a/world_gen/main.tscn2944845596.tmp b/world_gen/main.tscn2944845596.tmp new file mode 100644 index 0000000..2baab03 --- /dev/null +++ b/world_gen/main.tscn2944845596.tmp @@ -0,0 +1,54 @@ +[gd_scene load_steps=9 format=3 uid="uid://cioop0k7xmww8"] + +[ext_resource type="Script" uid="uid://b4q4faaqbat0x" path="res://world_gen/world_generator.gd" id="1_y7qka"] +[ext_resource type="Script" uid="uid://b5io1eg4blrp5" path="res://player/player.gd" id="2_nwtrj"] + +[sub_resource type="ProceduralSkyMaterial" id="ProceduralSkyMaterial_y7qka"] +sky_horizon_color = Color(0.66224277, 0.6717428, 0.6867428, 1) +ground_horizon_color = Color(0.66224277, 0.6717428, 0.6867428, 1) + +[sub_resource type="Sky" id="Sky_nwtrj"] +sky_material = SubResource("ProceduralSkyMaterial_y7qka") + +[sub_resource type="Environment" id="Environment_vrc1b"] +background_mode = 2 +sky = SubResource("Sky_nwtrj") +tonemap_mode = 2 +volumetric_fog_enabled = true +volumetric_fog_density = 0.01 + +[sub_resource type="FastNoiseLite" id="FastNoiseLite_nwtrj"] +noise_type = 3 +frequency = 0.0291 + +[sub_resource type="CapsuleShape3D" id="CapsuleShape3D_y7qka"] +resource_name = "PlayerCollision" + +[sub_resource type="CapsuleMesh" id="CapsuleMesh_nwtrj"] +resource_name = "PlayerMesh" + +[node name="main" type="Node"] + +[node name="WorldEnvironment" type="WorldEnvironment" parent="."] +environment = SubResource("Environment_vrc1b") + +[node name="DirectionalLight3D" type="DirectionalLight3D" parent="."] +transform = Transform3D(-0.8660254, -0.43301278, 0.25, 0, 0.49999997, 0.86602545, -0.50000006, 0.75, -0.43301266, 0, 0, 0) +shadow_enabled = true + +[node name="WorldGenerator" type="Node" parent="."] +script = ExtResource("1_y7qka") +noise = SubResource("FastNoiseLite_nwtrj") + +[node name="Player" type="CharacterBody3D" parent="."] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 104.43206, 0) +script = ExtResource("2_nwtrj") + +[node name="CollisionShape3D" type="CollisionShape3D" parent="Player"] +shape = SubResource("CapsuleShape3D_y7qka") + +[node name="MeshInstance3D" type="MeshInstance3D" parent="Player/CollisionShape3D"] +mesh = SubResource("CapsuleMesh_nwtrj") + +[node name="Camera3D" type="Camera3D" parent="Player"] +transform = Transform3D(1, 0, 0, 0, 0.9604348, 0.27850503, 0, -0.27850503, 0.9604348, 0, 1.8589027, 2.8465767) diff --git a/world_gen/new_plane_mesh.tres b/world_gen/new_plane_mesh.tres new file mode 100644 index 0000000..6667ef6 --- /dev/null +++ b/world_gen/new_plane_mesh.tres @@ -0,0 +1,7 @@ +[gd_resource type="PlaneMesh" format=3 uid="uid://rhv7l5ya1qnf"] + +[resource] +resource_local_to_scene = true +size = Vector2(256, 256) +subdivide_width = 32 +subdivide_depth = 32 diff --git a/world_gen/world_generator.gd b/world_gen/world_generator.gd new file mode 100644 index 0000000..7971028 --- /dev/null +++ b/world_gen/world_generator.gd @@ -0,0 +1,149 @@ +@tool +extends Node +var generation_thread: Thread = Thread.new() +#### CHUNK GENERATION #### +@export_category("CHUNK GENERATION") + +const CHUNK_SIZE := 256 +const CHUNK_BOUNDS := 8 + +## How detailed the chunk should be. This could be made a const when debug phase is finished +@export_range(4,256,4) var chunk_resolution :=32: + set(new_resolution): + chunk_resolution = new_resolution + update_all_mesh() + +#### WORLD GENERATION #### +@export_category("WORLD GENERATION") +@export_tool_button("Generate Chunks") var generate_chunk_callable = generate.bind(true) +@export var noise: FastNoiseLite: + set(new_noise): + noise = new_noise + update_all_mesh() + if noise: + noise.changed.connect(update_all_mesh) #change all thes meshes after eahc changes +@export_range(4.0,128.0,4.0) var height := 64.0: + set(new_height): + height = new_height + update_all_mesh() + +var loaded_chunks := {} + + +# Called when the node enters the scene tree for the first time. ALSO IN EDITOR +func _ready() -> void: + generate(true) + + +func generate(_ForceRegen:bool=false): + print('Generation starts') + generate_chunks_around(Vector3(0,0,0), _ForceRegen) + + +# Called every frame. 'delta' is the elapsed time since the previous frame. +func _process(_delta: float) -> void: + pass + + +func generate_noise(_seed:float): + noise = FastNoiseLite.new() + + +# This function is better called in another thread, doing all the heavy process before adding it to the scene tree +func generate_chunk(position: Vector3, _ForceRegen:bool = false): + + WorkerThreadPool.add_task(func(): + var mesh_instance = MeshInstance3D.new() + update_mesh(mesh_instance) + mesh_instance.position = position + call_deferred("add_child", mesh_instance) + var chunk_coord = get_chunk_coord(position) + loaded_chunks[chunk_coord] = mesh_instance + print("chunk at : ",str(position),", coord ",str(chunk_coord), " loaded") + + ) + + +func get_height(x:float,y:float) -> float: + return noise.get_noise_2d(x,y) * height + + +func get_normal(x:float,y:float) -> Vector3: + var epsilon := CHUNK_SIZE / chunk_resolution + var normal := Vector3( + (get_height(x + epsilon,y) - get_height(x - epsilon, y))/(2.0 *epsilon), + 1.0, + (get_height(x, y + epsilon) - get_height(x, y - epsilon))/(2.0 *epsilon) + + ) + return normal.normalized() + + +func update_all_mesh(): + for chunk in loaded_chunks: + update_mesh(loaded_chunks[chunk]) + + +func update_mesh(chunk_terrain_instance:MeshInstance3D): + var plane: PlaneMesh = PlaneMesh.new() #load("res://world_gen/new_plane_mesh.tres").duplicate() + plane.subdivide_depth = chunk_resolution + plane.subdivide_width = chunk_resolution + plane.size = Vector2(CHUNK_SIZE,CHUNK_SIZE) + + var plane_arrays := plane.get_mesh_arrays() + var vertex_array: PackedVector3Array = plane_arrays[ArrayMesh.ARRAY_VERTEX] + var normal_array: PackedVector3Array = plane_arrays[ArrayMesh.ARRAY_NORMAL] + var tangent_array: PackedFloat32Array = plane_arrays[ArrayMesh.ARRAY_TANGENT] + + for i:int in vertex_array.size(): + var vertex := vertex_array[i] + # Convert local vertex coordinates to world coordinates + var world_x = chunk_terrain_instance.position.x + vertex.x + var world_z = chunk_terrain_instance.position.z + vertex.z + var normal := Vector3.UP + var tangent := Vector3.RIGHT + + if noise: + vertex.y = get_height(world_x, world_z) + normal = get_normal(world_x, world_z) + tangent = normal.cross(Vector3.UP) + + vertex_array[i] = vertex + normal_array[i] = normal + tangent_array[4*i] = tangent.x + tangent_array[4*i+1] = tangent.y + tangent_array[4*i+2] = tangent.z + tangent_array[4*i+3] = 1.0 + + var array_mesh := ArrayMesh.new() + array_mesh.add_surface_from_arrays(Mesh.PRIMITIVE_TRIANGLES,plane_arrays) + chunk_terrain_instance.mesh = array_mesh + + +## Convert position to chunk coordinates +func get_chunk_coord(position:Vector3) -> Vector2i: + var coord_chunk_x = int(floor(position.x / CHUNK_SIZE)) + var coord_chunk_z = int(floor(position.z / CHUNK_SIZE)) + return Vector2i(coord_chunk_x,coord_chunk_z) + + +func generate_chunks_around(position:Vector3,ForceRegen:bool = false): + + # Convert position to chunk coordinates + var position_to_coord := get_chunk_coord(position) + + print("closest chunk : "+str(Vector2i(position_to_coord.x,position_to_coord.y))) + + for x in range(position_to_coord.x - CHUNK_BOUNDS,position_to_coord.x + CHUNK_BOUNDS+1): + for z in range(position_to_coord.y - CHUNK_BOUNDS, position_to_coord.y + CHUNK_BOUNDS+1): + var chunk_coord := Vector2i(x,z) + var chunck_position := Vector3i(x * CHUNK_SIZE,0,z * CHUNK_SIZE) + print("trying to load chunk at coordinate : "+str(chunk_coord)) + if ForceRegen: + loaded_chunks.clear() + for child in get_children(): + child.queue_free() + + if not loaded_chunks.has(chunk_coord): + print("generate chunk at : "+str(chunck_position)) + generate_chunk(chunck_position) diff --git a/world_gen/world_generator.gd.uid b/world_gen/world_generator.gd.uid new file mode 100644 index 0000000..4ca7943 --- /dev/null +++ b/world_gen/world_generator.gd.uid @@ -0,0 +1 @@ +uid://b4q4faaqbat0x