First commit
This commit is contained in:
commit
4eef953ed6
26 changed files with 771 additions and 0 deletions
4
.editorconfig
Normal file
4
.editorconfig
Normal file
|
@ -0,0 +1,4 @@
|
|||
root = true
|
||||
|
||||
[*]
|
||||
charset = utf-8
|
2
.gitattributes
vendored
Normal file
2
.gitattributes
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
# Normalize EOL for all files that Git considers text files.
|
||||
* text=auto eol=lf
|
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
|
@ -0,0 +1,3 @@
|
|||
# Godot 4+ specific ignores
|
||||
.godot/
|
||||
/android/
|
21
addons/SimpleFormatOnSave/LICENSE.txt
Normal file
21
addons/SimpleFormatOnSave/LICENSE.txt
Normal file
|
@ -0,0 +1,21 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2025-present VitSoonYoung - <vitsoonyoung+simpleformatonsave@gmail.com>
|
||||
|
||||
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.
|
10
addons/SimpleFormatOnSave/formatter.gd
Normal file
10
addons/SimpleFormatOnSave/formatter.gd
Normal file
|
@ -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
|
1
addons/SimpleFormatOnSave/formatter.gd.uid
Normal file
1
addons/SimpleFormatOnSave/formatter.gd.uid
Normal file
|
@ -0,0 +1 @@
|
|||
uid://clqnxlippxlm4
|
7
addons/SimpleFormatOnSave/plugin.cfg
Normal file
7
addons/SimpleFormatOnSave/plugin.cfg
Normal file
|
@ -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"
|
65
addons/SimpleFormatOnSave/rules/blank_lines.gd
Normal file
65
addons/SimpleFormatOnSave/rules/blank_lines.gd
Normal file
|
@ -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)
|
1
addons/SimpleFormatOnSave/rules/blank_lines.gd.uid
Normal file
1
addons/SimpleFormatOnSave/rules/blank_lines.gd.uid
Normal file
|
@ -0,0 +1 @@
|
|||
uid://dr266abkfhg1o
|
155
addons/SimpleFormatOnSave/rules/spacing.gd
Normal file
155
addons/SimpleFormatOnSave/rules/spacing.gd
Normal file
|
@ -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'(?<!#)("""|\'\'\'|"|\')((?:.|\n)*?)\1')
|
||||
|
||||
var string_matches = string_regex.search_all(code)
|
||||
var string_map = {}
|
||||
|
||||
for i in range(string_matches.size()):
|
||||
var match = string_matches[i]
|
||||
var original = match.get_string()
|
||||
var placeholder = "__STRING__%d__" % i
|
||||
string_map[placeholder] = original
|
||||
code = _replace(code, original, placeholder)
|
||||
|
||||
var comment_regex = RegEx.new()
|
||||
comment_regex.compile("#.*")
|
||||
var comment_matches = comment_regex.search_all(code)
|
||||
var comment_map = {}
|
||||
|
||||
for i in range(comment_matches.size()):
|
||||
var match = comment_matches[i]
|
||||
var original = match.get_string()
|
||||
var placeholder = "__COMMENT__%d__" % i
|
||||
comment_map[placeholder] = original
|
||||
code = _replace(code, original, placeholder)
|
||||
|
||||
var ref_regex = RegEx.new()
|
||||
ref_regex.compile(r"\$[^.]*")
|
||||
var ref_matches = ref_regex.search_all(code)
|
||||
var ref_map = {}
|
||||
|
||||
for i in range(ref_matches.size()):
|
||||
var match = ref_matches[i]
|
||||
var original = match.get_string()
|
||||
var placeholder = "__REF__%d__" % i
|
||||
ref_map[placeholder] = original
|
||||
code = _replace(code, original, placeholder)
|
||||
|
||||
code = _format_operators_and_commas(code)
|
||||
|
||||
for placeholder in ref_map:
|
||||
code = code.replace(placeholder, ref_map[placeholder])
|
||||
|
||||
for placeholder in comment_map:
|
||||
code = code.replace(placeholder, comment_map[placeholder])
|
||||
|
||||
for placeholder in string_map:
|
||||
code = code.replace(placeholder, string_map[placeholder])
|
||||
|
||||
return code
|
||||
|
||||
|
||||
static func _format_operators_and_commas(code: String) -> 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
|
1
addons/SimpleFormatOnSave/rules/spacing.gd.uid
Normal file
1
addons/SimpleFormatOnSave/rules/spacing.gd.uid
Normal file
|
@ -0,0 +1 @@
|
|||
uid://wfodrj6vpfb6
|
82
addons/SimpleFormatOnSave/simple_format_on_save.gd
Normal file
82
addons/SimpleFormatOnSave/simple_format_on_save.gd
Normal file
|
@ -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]")
|
1
addons/SimpleFormatOnSave/simple_format_on_save.gd.uid
Normal file
1
addons/SimpleFormatOnSave/simple_format_on_save.gd.uid
Normal file
|
@ -0,0 +1 @@
|
|||
uid://b6d7va37k5131
|
1
icon.svg
Normal file
1
icon.svg
Normal file
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="128" height="128"><rect width="124" height="124" x="2" y="2" fill="#363d52" stroke="#212532" stroke-width="4" rx="14"/><g fill="#fff" transform="translate(12.322 12.322)scale(.101)"><path d="M105 673v33q407 354 814 0v-33z"/><path fill="#478cbf" d="m105 673 152 14q12 1 15 14l4 67 132 10 8-61q2-11 15-15h162q13 4 15 15l8 61 132-10 4-67q3-13 15-14l152-14V427q30-39 56-81-35-59-83-108-43 20-82 47-40-37-88-64 7-51 8-102-59-28-123-42-26 43-46 89-49-7-98 0-20-46-46-89-64 14-123 42 1 51 8 102-48 27-88 64-39-27-82-47-48 49-83 108 26 42 56 81zm0 33v39c0 276 813 276 814 0v-39l-134 12-5 69q-2 10-14 13l-162 11q-12 0-16-11l-10-65H446l-10 65q-4 11-16 11l-162-11q-12-3-14-13l-5-69z"/><path d="M483 600c0 34 58 34 58 0v-86c0-34-58-34-58 0z"/><circle cx="725" cy="526" r="90"/><circle cx="299" cy="526" r="90"/></g><g fill="#414042" transform="translate(12.322 12.322)scale(.101)"><circle cx="307" cy="532" r="60"/><circle cx="717" cy="532" r="60"/></g></svg>
|
After Width: | Height: | Size: 995 B |
43
icon.svg.import
Normal file
43
icon.svg.import
Normal file
|
@ -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
|
28
player/player.gd
Normal file
28
player/player.gd
Normal file
|
@ -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()
|
1
player/player.gd.uid
Normal file
1
player/player.gd.uid
Normal file
|
@ -0,0 +1 @@
|
|||
uid://b5io1eg4blrp5
|
24
project.godot
Normal file
24
project.godot
Normal file
|
@ -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
|
24
scenes/chunk_generator.gd
Normal file
24
scenes/chunk_generator.gd
Normal file
|
@ -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()
|
1
scenes/chunk_generator.gd.uid
Normal file
1
scenes/chunk_generator.gd.uid
Normal file
|
@ -0,0 +1 @@
|
|||
uid://e63lq34qepsg
|
29
scenes/world.tscn
Normal file
29
scenes/world.tscn
Normal file
|
@ -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
|
56
world_gen/main.tscn
Normal file
56
world_gen/main.tscn
Normal file
|
@ -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)
|
54
world_gen/main.tscn2944845596.tmp
Normal file
54
world_gen/main.tscn2944845596.tmp
Normal file
|
@ -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)
|
7
world_gen/new_plane_mesh.tres
Normal file
7
world_gen/new_plane_mesh.tres
Normal file
|
@ -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
|
149
world_gen/world_generator.gd
Normal file
149
world_gen/world_generator.gd
Normal file
|
@ -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)
|
1
world_gen/world_generator.gd.uid
Normal file
1
world_gen/world_generator.gd.uid
Normal file
|
@ -0,0 +1 @@
|
|||
uid://b4q4faaqbat0x
|
Loading…
Reference in a new issue