From 3959333534ceec106286d9820b0ecf1d2f3c7924 Mon Sep 17 00:00:00 2001 From: Dan Baker Date: Sun, 29 Jun 2025 10:58:18 +0100 Subject: [PATCH] Adds bushes and flowers to ground tiles Implements bush and flower spawning on ground tiles based on vegetation density. Adds new assets for bushes and flowers, and introduces multi-mesh rendering for optimized performance. Introduces seasonal color variations for vegetation using a shader for bushes and materials for flowers and grass. Refactors material application into a MaterialManager to handle material assignments over multiple frames. Moves ground tile scripts into a subfolder. Adds floating particles to test scene. --- .gitignore | 1 + Entities/Bush/assets/bush_large.res | Bin 0 -> 9037 bytes Entities/Bush/assets/bush_small.res | Bin 0 -> 4857 bytes .../temp => Bush/assets}/plant_bush.glb | Bin .../assets}/plant_bush.glb.import | 6 +- .../assets}/plant_bushDetailed.glb | Bin .../assets}/plant_bushDetailed.glb.import | 19 ++- .../temp => Bush/assets}/plant_bushLarge.glb | Bin .../assets}/plant_bushLarge.glb.import | 6 +- .../assets}/plant_bushLargeTriangle.glb | Bin .../plant_bushLargeTriangle.glb.import | 6 +- .../temp => Bush/assets}/plant_bushSmall.glb | Bin .../assets}/plant_bushSmall.glb.import | 19 ++- .../assets}/plant_bushTriangle.glb | Bin .../assets}/plant_bushTriangle.glb.import | 6 +- Entities/GroundTile/GroundTile.tscn | 36 +++++- Entities/GroundTile/scripts/bush_multimesh.gd | 56 +++++++++ .../GroundTile/scripts/bush_multimesh.gd.uid | 1 + Entities/GroundTile/scripts/bushes.gd | 36 ++++++ Entities/GroundTile/scripts/bushes.gd.uid | 1 + .../GroundTile/scripts/flower_multimesh.gd | 59 +++++++++ .../scripts/flower_multimesh.gd.uid | 1 + Entities/GroundTile/scripts/flowers.gd | 36 ++++++ Entities/GroundTile/scripts/flowers.gd.uid | 1 + .../GroundTile/scripts/grass_multimesh.gd | 17 +++ .../GroundTile/{ => scripts}/ground_tile.gd | 14 ++- .../{ => scripts}/ground_tile.gd.uid | 0 Entities/Plant/assets/flower.res | Bin 0 -> 9978 bytes .../assets/flower_tall.glb} | Bin .../assets/flower_tall.glb.import} | 19 ++- Entities/Plant/assets/flower_tall.res | Bin 0 -> 9978 bytes Entities/Tree/scripts/color_default.gd | 2 +- Entities/Tree/scripts/tree.gd | 2 +- Utilities/ColorStorage/ColorStorage.gd | 118 ++++++++++++------ Utilities/MaterialManager/MaterialManager.gd | 30 +++++ .../MaterialManager/MaterialManager.gd.uid | 1 + Utilities/Seasons/seasons.gd | 11 ++ Utilities/Seasons/seasons.gd.uid | 1 + leaves.gdshader | 23 ++++ leaves.gdshader.uid | 1 + project.godot | 2 + stages/Test3D/Test3d.tscn | 14 ++- .../assets/stylizedGrassMeshes/grass.res | Bin 3879 -> 4148 bytes stages/Test3D/new_environment.tres | 11 +- stages/Test3D/particles.gd | 79 ++++++++++++ stages/Test3D/particles.gd.uid | 1 + 46 files changed, 559 insertions(+), 77 deletions(-) create mode 100644 Entities/Bush/assets/bush_large.res create mode 100644 Entities/Bush/assets/bush_small.res rename Entities/{Tree/assets/temp => Bush/assets}/plant_bush.glb (100%) rename Entities/{Tree/assets/temp => Bush/assets}/plant_bush.glb.import (75%) rename Entities/{Tree/assets/temp => Bush/assets}/plant_bushDetailed.glb (100%) rename Entities/{Tree/assets/temp => Bush/assets}/plant_bushDetailed.glb.import (56%) rename Entities/{Tree/assets/temp => Bush/assets}/plant_bushLarge.glb (100%) rename Entities/{Tree/assets/temp => Bush/assets}/plant_bushLarge.glb.import (74%) rename Entities/{Tree/assets/temp => Bush/assets}/plant_bushLargeTriangle.glb (100%) rename Entities/{Tree/assets/temp => Bush/assets}/plant_bushLargeTriangle.glb.import (72%) rename Entities/{Tree/assets/temp => Bush/assets}/plant_bushSmall.glb (100%) rename Entities/{Tree/assets/temp => Bush/assets}/plant_bushSmall.glb.import (56%) rename Entities/{Tree/assets/temp => Bush/assets}/plant_bushTriangle.glb (100%) rename Entities/{Tree/assets/temp => Bush/assets}/plant_bushTriangle.glb.import (73%) create mode 100644 Entities/GroundTile/scripts/bush_multimesh.gd create mode 100644 Entities/GroundTile/scripts/bush_multimesh.gd.uid create mode 100644 Entities/GroundTile/scripts/bushes.gd create mode 100644 Entities/GroundTile/scripts/bushes.gd.uid create mode 100644 Entities/GroundTile/scripts/flower_multimesh.gd create mode 100644 Entities/GroundTile/scripts/flower_multimesh.gd.uid create mode 100644 Entities/GroundTile/scripts/flowers.gd create mode 100644 Entities/GroundTile/scripts/flowers.gd.uid rename Entities/GroundTile/{ => scripts}/ground_tile.gd (75%) rename Entities/GroundTile/{ => scripts}/ground_tile.gd.uid (100%) create mode 100644 Entities/Plant/assets/flower.res rename Entities/{Tree/assets/temp/flower_purpleB.glb => Plant/assets/flower_tall.glb} (100%) rename Entities/{Tree/assets/temp/flower_purpleB.glb.import => Plant/assets/flower_tall.glb.import} (57%) create mode 100644 Entities/Plant/assets/flower_tall.res create mode 100644 Utilities/MaterialManager/MaterialManager.gd create mode 100644 Utilities/MaterialManager/MaterialManager.gd.uid create mode 100644 Utilities/Seasons/seasons.gd create mode 100644 Utilities/Seasons/seasons.gd.uid create mode 100644 leaves.gdshader create mode 100644 leaves.gdshader.uid create mode 100644 stages/Test3D/particles.gd create mode 100644 stages/Test3D/particles.gd.uid diff --git a/.gitignore b/.gitignore index 0af181c..1856418 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ # Godot 4+ specific ignores .godot/ /android/ +*.tmp \ No newline at end of file diff --git a/Entities/Bush/assets/bush_large.res b/Entities/Bush/assets/bush_large.res new file mode 100644 index 0000000000000000000000000000000000000000..dcb20f28213ee913fc862235424b58b15f36bb9b GIT binary patch literal 9037 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 I;6Kv*FM}LdT>t<8 literal 0 HcmV?d00001 diff --git a/Entities/Bush/assets/bush_small.res b/Entities/Bush/assets/bush_small.res new file mode 100644 index 0000000000000000000000000000000000000000..dbab79302aa4694835a4d08abaaaa93e469ed743 GIT binary patch literal 4857 zcmb_gZ-`w-6`$;OlQwQPF>TUpjcskZrfO~4ja5;yX*FL2YiZU$&{}5a-8=8S+r973 zb?&^~eOgfbp!G||Xu+BfE2$7H1ra|5nxddk$d@3ED2RwCLXn6HQa`P~-^`u+-oD+i zQoPH#bI+MG=bSln&YAP}?Bdx^iu$<;BVjjSFW^yY)!;E*b@3gZNesuH{>HxFzkKmn zr2Q{rA52F8R#&EP6D@O-s9ZWDt3(%C%#6+zs;9*PPbpGuWoa{GGdJ)y6?)CdQdO1M z2M%XdQI*Q-A{mHxfY8cyk; zd7dTW;0Pk)9>qIHsZrbPHbj|pRZ-~NJKmapr(0+o6Qwqoi~?xx7HT7bI(09rs>~Ep z7j$jvZH+|l%7}nN+s;7t`nk)>Jk!=IZKh4Bt#Zctm>NCuc^!L5p{GR~TE(1>Ju49Q}QtDT$Go+^D3Dbp-s+5q+OELFR$6Bk*cI+_X= zoD}`(#hQ4|&53fCUCpe3l1Mt3B5@zt z(w9<}0nV)&o2Mgdf{OV~D&{w*$h7$Q}Nlr1Xex2T}oqAS~3j)d?9Fp{h;wF*g? zX5lKdI?>q*?jKyIW;NUc;hWdKDOJKNopM`Qp0|v$^}Wq&a7bEa0SLaE!bHcKRe!JY z1J|wTBlqdlcDANdS!G`im6^w3UQLD{Nx!qcxbu&sJP5X#Y1OC z=BCM%>B$b7ox?mUJlpfMJL*Fx#aDP6LL2?!{@F8;_jR4 zDYBqb2_{rk`EDf3i_{htma@pI=XV*^j16Q>PyVbhE<=kx{)DKsQ&>&WEgxF7x!uOCh=^F9rO{(I9VnHqe3lESbC%=#!Ud$K2}? zkMmcK20GU>_Sdh6>{!1Z@C#D`AKDw_(H>^Y(mJ!~-1(*TfKSf`e0ny{hwS1!Gi+%+ z;BlTK>2VyfueK*-DeWQO`t^X*9@-o2x-j*vXMXfy!h?gC?tSLZ<#EEDf4Oq-$xl7^ zCgCZ7x%j>F4LtDu%g=s1{S9H`7oYj=sXv|lCP)4Gub+wVlfQ{iM4(&(WfG{1It=6x zxSsIhsc%0K;gz=*S`nh2FFtkRKM}5+`{NHHFd(WtkrjVSR;N8GOopa zU--vQKLmKV{}$r8<8M43$0$4EaomkU zxANOJ9_Ktq;*po*jpn&`!rLpRPyXRok39eCcOIo5_)yXfis=zls#!4H;hTkB;wZ|^ zP2%0CZPU13kpH-$?-9q)!_VQD!EuzPP void: + parent_node = get_parent() as BushController + if parent_node == null: + Log.pr("Error: Parent node is not a BushController!") + + # Load mesh once and reuse + if bush_mesh == null: + bush_mesh = load("res://Entities/Bush/assets/bush_large.res") + +func setup_multimesh() -> void: + if parent_node == null: + return + + if bush_mesh == null: + Log.pr("Error: Could not load bush mesh") + return + + # Reuse existing MultiMesh if possible, or create new one + if mm == null: + mm = MultiMesh.new() + mm.transform_format = MultiMesh.TRANSFORM_3D + mm.mesh = bush_mesh + + material = ColorData.get_random_bush_material(Season.current) + mm.mesh.surface_set_material(0, material) + + # Configure instance count + mm.instance_count = parent_node.bush_instance_range + + # Get shared RNG from GroundTile + var rng = parent_node.parent_node.get_rng() + + # Generate positions using shared RNG + for i in range(mm.instance_count): + var random_pos = Vector3( + rng.randf_range(-1.0, 1.0), + 0.0, + rng.randf_range(-1.0, 1.0) + ) + + var random_rotation = rng.randf_range(0.0, TAU) + var basis = Basis(Vector3.UP, random_rotation) + + var random_scale = rng.randf_range(0.3, 0.9) + basis = basis.scaled(Vector3(random_scale, random_scale, random_scale)) + + var tx = Transform3D(basis, random_pos) + mm.set_instance_transform(i, tx) + + multimesh = mm diff --git a/Entities/GroundTile/scripts/bush_multimesh.gd.uid b/Entities/GroundTile/scripts/bush_multimesh.gd.uid new file mode 100644 index 0000000..d832095 --- /dev/null +++ b/Entities/GroundTile/scripts/bush_multimesh.gd.uid @@ -0,0 +1 @@ +uid://dri5tubavplji diff --git a/Entities/GroundTile/scripts/bushes.gd b/Entities/GroundTile/scripts/bushes.gd new file mode 100644 index 0000000..6cbdb58 --- /dev/null +++ b/Entities/GroundTile/scripts/bushes.gd @@ -0,0 +1,36 @@ +# GrassController.gd +extends Node3D +class_name BushController + +@onready var bush_multimesh: MultiMeshInstance3D = $BushMultimesh +var parent_node: GroundTile = null +var bush_density: float = 0.5 +var bush_instance_range: int = 10 + +func _ready() -> void: + parent_node = get_parent() as GroundTile + +func spawn_bushes_for_cell(value): + if value == null: + return + + bush_density = value.vegetation_density + update_bush_density() + + if bush_multimesh and bush_multimesh.has_method("setup_multimesh"): + bush_multimesh.setup_multimesh() + +func update_bush_density() -> void: + if parent_node == null: + return + + var rng = parent_node.get_rng() + + if bush_density > 0.8: + bush_instance_range = rng.randi_range(5, 10) + elif bush_density > 0.6: + bush_instance_range = rng.randi_range(2, 7) + elif bush_density > 0.3: + bush_instance_range = rng.randi_range(0, 1) + else: + bush_instance_range = rng.randi_range(0, 0) diff --git a/Entities/GroundTile/scripts/bushes.gd.uid b/Entities/GroundTile/scripts/bushes.gd.uid new file mode 100644 index 0000000..905ac13 --- /dev/null +++ b/Entities/GroundTile/scripts/bushes.gd.uid @@ -0,0 +1 @@ +uid://bt67yhdkwtqy5 diff --git a/Entities/GroundTile/scripts/flower_multimesh.gd b/Entities/GroundTile/scripts/flower_multimesh.gd new file mode 100644 index 0000000..6ca351a --- /dev/null +++ b/Entities/GroundTile/scripts/flower_multimesh.gd @@ -0,0 +1,59 @@ +extends MultiMeshInstance3D + +var mm: MultiMesh +var parent_node: FlowerController +static var flower_mesh: Mesh = null +var material: StandardMaterial3D + +func _ready() -> void: + parent_node = get_parent() as FlowerController + if parent_node == null: + Log.pr("Error: Parent node is not a FlowerController!") + + # Load mesh once and reuse + if flower_mesh == null: + flower_mesh = load("res://Entities/Plant/assets/flower_tall.res") + +func setup_multimesh() -> void: + if parent_node == null: + return + + if flower_mesh == null: + Log.pr("Error: Could not load flower mesh") + return + + # Reuse existing MultiMesh if possible, or create new one + if mm == null: + mm = MultiMesh.new() + mm.transform_format = MultiMesh.TRANSFORM_3D + mm.mesh = flower_mesh + + var flower_material = ColorData.flower_materials[Season.current]["flower"] + var stem_material = ColorData.flower_materials[Season.current]["stem"] + mm.mesh.surface_set_material(0, stem_material) + mm.mesh.surface_set_material(1, flower_material) + + # Configure instance count + mm.instance_count = parent_node.flower_instance_range + + # Get shared RNG from GroundTile + var rng = parent_node.parent_node.get_rng() + + # Generate positions using shared RNG + for i in range(mm.instance_count): + var random_pos = Vector3( + rng.randf_range(-1.0, 1.0), + 0.0, + rng.randf_range(-1.0, 1.0) + ) + + var random_rotation = rng.randf_range(0.0, TAU) + var basis = Basis(Vector3.UP, random_rotation) + + var random_scale = rng.randf_range(0.2, 0.4) + basis = basis.scaled(Vector3(random_scale, random_scale, random_scale)) + + var tx = Transform3D(basis, random_pos) + mm.set_instance_transform(i, tx) + + multimesh = mm diff --git a/Entities/GroundTile/scripts/flower_multimesh.gd.uid b/Entities/GroundTile/scripts/flower_multimesh.gd.uid new file mode 100644 index 0000000..0f711a3 --- /dev/null +++ b/Entities/GroundTile/scripts/flower_multimesh.gd.uid @@ -0,0 +1 @@ +uid://d3s0u7rm1y7i6 diff --git a/Entities/GroundTile/scripts/flowers.gd b/Entities/GroundTile/scripts/flowers.gd new file mode 100644 index 0000000..f66aa62 --- /dev/null +++ b/Entities/GroundTile/scripts/flowers.gd @@ -0,0 +1,36 @@ +# GrassController.gd +extends Node3D +class_name FlowerController + +@onready var flower_multimesh: MultiMeshInstance3D = $FlowerMultimesh +var parent_node: GroundTile = null +var flower_density: float = 0.5 +var flower_instance_range: int = 10 + +func _ready() -> void: + parent_node = get_parent() as GroundTile + +func spawn_flowers_for_cell(value): + if value == null: + return + + flower_density = value.vegetation_density + update_flower_density() + + if flower_multimesh and flower_multimesh.has_method("setup_multimesh"): + flower_multimesh.setup_multimesh() + +func update_flower_density() -> void: + if parent_node == null: + return + + var rng = parent_node.get_rng() + + if flower_density > 0.8: + flower_instance_range = rng.randi_range(1, 3) + elif flower_density > 0.6: + flower_instance_range = rng.randi_range(3, 4) + elif flower_density > 0.3: + flower_instance_range = rng.randi_range(4, 7) + else: + flower_instance_range = rng.randi_range(5, 10) diff --git a/Entities/GroundTile/scripts/flowers.gd.uid b/Entities/GroundTile/scripts/flowers.gd.uid new file mode 100644 index 0000000..bb5f79f --- /dev/null +++ b/Entities/GroundTile/scripts/flowers.gd.uid @@ -0,0 +1 @@ +uid://18vxtm3ua4x0 diff --git a/Entities/GroundTile/scripts/grass_multimesh.gd b/Entities/GroundTile/scripts/grass_multimesh.gd index e05c4f4..a289661 100644 --- a/Entities/GroundTile/scripts/grass_multimesh.gd +++ b/Entities/GroundTile/scripts/grass_multimesh.gd @@ -25,6 +25,8 @@ func setup_multimesh() -> void: mm = MultiMesh.new() mm.transform_format = MultiMesh.TRANSFORM_3D mm.mesh = grass_mesh + update_shader_parameter('top_color', ColorData.grass_materials[Season.current]['top']) + update_shader_parameter('bottom_color', ColorData.grass_materials[Season.current]['base']) # Configure instance count mm.instance_count = parent_node.grass_instance_range @@ -51,3 +53,18 @@ func setup_multimesh() -> void: multimesh = mm cast_shadow = GeometryInstance3D.SHADOW_CASTING_SETTING_OFF + + +func update_shader_parameter(param_name: String, value) -> void: + if material_override == null: + Log.pr("Error: No material override found") + return + + # Check if it's a ShaderMaterial + if material_override is ShaderMaterial: + var shader_material = material_override as ShaderMaterial + shader_material.set_shader_parameter(param_name, value) + else: + Log.pr("Error: Material override is not a ShaderMaterial") + + return diff --git a/Entities/GroundTile/ground_tile.gd b/Entities/GroundTile/scripts/ground_tile.gd similarity index 75% rename from Entities/GroundTile/ground_tile.gd rename to Entities/GroundTile/scripts/ground_tile.gd index 86f29b5..639ab11 100644 --- a/Entities/GroundTile/ground_tile.gd +++ b/Entities/GroundTile/scripts/ground_tile.gd @@ -3,6 +3,9 @@ extends Node3D @onready var debug_text: Label = $DebugText/DebugTextViewport/DebugTextLabel @onready var tree_spawner = $Trees @onready var grass_spawner = $Grass +@onready var bush_spawner = $Bushes +@onready var flower_spawner = $Flowers +@onready var ground = $Ground var grid_x: int var grid_z: int var cell_info: CellDataResource = null @@ -16,8 +19,10 @@ func _ready() -> void: spawners_ready = true if cell_info != null: - spawn_content() - update_text_label() + spawn_content() + update_text_label() + + ground.material_override.albedo_color = ColorData.grass_materials[Season.current]['base'] func get_rng() -> RandomClass: if cached_rng == null and cell_info: @@ -44,6 +49,11 @@ func spawn_content(): tree_spawner.spawn_trees_for_cell(cell_info) if grass_spawner: grass_spawner.spawn_grass_for_cell(cell_info) + if bush_spawner: + bush_spawner.spawn_bushes_for_cell(cell_info) + if flower_spawner: + flower_spawner.spawn_flowers_for_cell(cell_info) + func update_text_label() -> void: debug_text.text = str(cell_info.vegetation_density) diff --git a/Entities/GroundTile/ground_tile.gd.uid b/Entities/GroundTile/scripts/ground_tile.gd.uid similarity index 100% rename from Entities/GroundTile/ground_tile.gd.uid rename to Entities/GroundTile/scripts/ground_tile.gd.uid diff --git a/Entities/Plant/assets/flower.res b/Entities/Plant/assets/flower.res new file mode 100644 index 0000000000000000000000000000000000000000..869b4f6f428389e2e4041b485cec09d02467b796 GIT binary patch literal 9978 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 void: populate_tree_colors() Log.pr("Tree colors populated: %d trees" % tree_materials.size()) - -func _process(_delta): - # Process material applications gradually over multiple frames - process_material_queue() - -func process_material_queue(): - var processed = 0 - while material_application_queue.size() > 0 and processed < max_materials_per_frame: - var task = material_application_queue.pop_front() - - # Check if the mesh instance is still valid before applying - if is_instance_valid(task.mesh_instance) and task.mesh_instance != null: - apply_material_immediately(task.mesh_instance, task.surface_index, task.material) - # If invalid, just skip this task (object was freed) - - processed += 1 - -func queue_material_application(mesh_instance: MeshInstance3D, surface_index: int, material: StandardMaterial3D): - material_application_queue.append({ - "mesh_instance": mesh_instance, - "surface_index": surface_index, - "material": material - }) - -func apply_material_immediately(mesh_instance: MeshInstance3D, surface_index: int, material: StandardMaterial3D): - if is_instance_valid(mesh_instance): - mesh_instance.set_surface_override_material(surface_index, material) + populate_bush_materials() + Log.pr("Bush materials populated: %d seasons" % bush_materials.size()) + populate_flower_materials() + Log.pr("Flower materials populated: %d seasons" % flower_materials.size()) # Create materials once at startup func populate_tree_colors() -> void: @@ -117,3 +113,53 @@ func get_random_trunk_material(tree_name: String, season: String) -> StandardMat return trunk_materials[random_key] return create_material(Color(0.6, 0.4, 0.2), "fallback_trunk") + +func populate_bush_materials() -> void: + for season in Season.seasons: + bush_materials[season] = { + "1": create_bush_material(grass_materials[season]["base"], grass_materials[season]["flower"].lightened(0.1), "bush_base_%s" % season) + } + Log.pr("Bush materials populated for seasons: %s" % bush_materials.keys()) + +func create_bush_material(color: Color, variation: Color, material_name: String) -> ShaderMaterial: + var material = ShaderMaterial.new() + + var shader = load("res://leaves.gdshader") + + material.shader = shader + material.resource_name = material_name + + material.set_shader_parameter("base_color", color) + material.set_shader_parameter("variation_color", variation) + + return material + +func get_random_bush_material(season: String) -> ShaderMaterial: + if bush_materials.has(season): + var bush_materials_season = bush_materials[season] + var material_keys = bush_materials_season.keys() + if material_keys.size() > 0: + var random_key = material_keys[randi() % material_keys.size()] + return bush_materials_season[random_key] + + Log.pr("No bush materials found for season: %s, returning fallback." % season) + return create_bush_material(Color.GREEN, Color.GREEN, "fallback_bush") + +func populate_flower_materials() -> void: + for season in Season.seasons: + flower_materials[season] = { + "flower": create_material(grass_materials[season]['flower'], "flower_base_%s" % season), + "stem": create_material(grass_materials[season]["top"], "flower_stem_%s" % season), + } + Log.pr("Flower materials populated for seasons: %s" % flower_materials.keys()) + +func get_random_flower_material(season: String) -> StandardMaterial3D: + if flower_materials.has(season): + var flower_materials_season = flower_materials[season] + var material_keys = flower_materials_season.keys() + if material_keys.size() > 0: + var random_key = material_keys[randi() % material_keys.size()] + return flower_materials_season[random_key] + + Log.pr("No flower materials found for season: %s, returning fallback." % season) + return create_material(Color(1.0, 1.0, 0.5), "fallback_flower") diff --git a/Utilities/MaterialManager/MaterialManager.gd b/Utilities/MaterialManager/MaterialManager.gd new file mode 100644 index 0000000..9a9f562 --- /dev/null +++ b/Utilities/MaterialManager/MaterialManager.gd @@ -0,0 +1,30 @@ +class_name MaterialManagerClass +extends Node + +var material_application_queue: Array = [] +var max_materials_per_frame: int = 20 # Adjust based on performance + +func _process(_delta): + # Process material applications gradually over multiple frames + process_material_queue() + +func process_material_queue(): + var processed = 0 + while material_application_queue.size() > 0 and processed < max_materials_per_frame: + var task = material_application_queue.pop_front() + + if is_instance_valid(task.mesh_instance) and task.mesh_instance != null: + apply_material_immediately(task.mesh_instance, task.surface_index, task.material) + + processed += 1 + +func queue_material_application(mesh_instance: MeshInstance3D, surface_index: int, material: StandardMaterial3D): + material_application_queue.append({ + "mesh_instance": mesh_instance, + "surface_index": surface_index, + "material": material + }) + +func apply_material_immediately(mesh_instance: MeshInstance3D, surface_index: int, material: StandardMaterial3D): + if is_instance_valid(mesh_instance): + mesh_instance.set_surface_override_material(surface_index, material) diff --git a/Utilities/MaterialManager/MaterialManager.gd.uid b/Utilities/MaterialManager/MaterialManager.gd.uid new file mode 100644 index 0000000..1c7917f --- /dev/null +++ b/Utilities/MaterialManager/MaterialManager.gd.uid @@ -0,0 +1 @@ +uid://hxkwqb71371n diff --git a/Utilities/Seasons/seasons.gd b/Utilities/Seasons/seasons.gd new file mode 100644 index 0000000..8cd6f8b --- /dev/null +++ b/Utilities/Seasons/seasons.gd @@ -0,0 +1,11 @@ +class_name SeasonController +extends Node + +var seasons: Array = [ + "spring", + "summer", + "autumn", + "winter" +] + +@export var current: String = "autumn" \ No newline at end of file diff --git a/Utilities/Seasons/seasons.gd.uid b/Utilities/Seasons/seasons.gd.uid new file mode 100644 index 0000000..f759b7d --- /dev/null +++ b/Utilities/Seasons/seasons.gd.uid @@ -0,0 +1 @@ +uid://buj63lgb7ckto diff --git a/leaves.gdshader b/leaves.gdshader new file mode 100644 index 0000000..4ba6f4e --- /dev/null +++ b/leaves.gdshader @@ -0,0 +1,23 @@ +shader_type spatial; +uniform vec4 base_color : source_color = vec4(0.2, 0.6, 0.1, 1.0); +uniform vec4 variation_color : source_color = vec4(0.4, 0.8, 0.2, 1.0); +uniform float gradient_height : hint_range(0.1, 5.0) = 2.0; +uniform float gradient_offset : hint_range(-2.0, 2.0) = 0.0; + +varying vec3 world_position; + +void vertex() { + world_position = (MODEL_MATRIX * vec4(VERTEX, 1.0)).xyz; +} + +void fragment() { + // Use world Y position instead of UV + float gradient_factor = (world_position.y + gradient_offset) / gradient_height; + gradient_factor = clamp(gradient_factor, 0.0, 1.0); + + vec3 final_color = mix(base_color.rgb, variation_color.rgb, gradient_factor); + + ALBEDO = final_color; + ROUGHNESS = 0.8; + METALLIC = 0.0; +} \ No newline at end of file diff --git a/leaves.gdshader.uid b/leaves.gdshader.uid new file mode 100644 index 0000000..8d6b26d --- /dev/null +++ b/leaves.gdshader.uid @@ -0,0 +1 @@ +uid://d0e60a0hdk02j diff --git a/project.godot b/project.godot index b41fddb..fe1e629 100644 --- a/project.godot +++ b/project.godot @@ -25,6 +25,8 @@ BiomeData="*res://Utilities/BiomeGeneration/BiomeData.gd" MapData="*res://Utilities/MapData/MapData.gd" ColorData="*res://Utilities/ColorStorage/ColorStorage.gd" MapPopulation="*res://Utilities/MapData/MapPopulation.gd" +MaterialManager="*res://Utilities/MaterialManager/MaterialManager.gd" +Season="*res://Utilities/Seasons/seasons.gd" [editor_plugins] diff --git a/stages/Test3D/Test3d.tscn b/stages/Test3D/Test3d.tscn index 518349c..05f23fe 100644 --- a/stages/Test3D/Test3d.tscn +++ b/stages/Test3D/Test3d.tscn @@ -1,10 +1,11 @@ -[gd_scene load_steps=45 format=4 uid="uid://bwsugg4p50fjr"] +[gd_scene load_steps=46 format=4 uid="uid://bwsugg4p50fjr"] [ext_resource type="Environment" uid="uid://cm77bbr0io118" path="res://Stages/Test3D/new_environment.tres" id="1_8ph61"] [ext_resource type="Script" uid="uid://bwed2dwogfmxv" path="res://Entities/Player/scripts/player.gd" id="1_d602n"] [ext_resource type="AnimationLibrary" uid="uid://bwnn7vpd0dqds" path="res://Common/animations/basic-movement.res" id="1_tfa5t"] [ext_resource type="Script" uid="uid://bbjv6a7yg7m02" path="res://Stages/Test3D/camera_pivot.gd" id="2_sdmks"] [ext_resource type="Shader" uid="uid://bsemnmdracd4m" path="res://Common/shaders/outline.gdshader" id="4_feu7y"] +[ext_resource type="Script" uid="uid://bjco8musjqog4" path="res://Stages/Test3D/particles.gd" id="9_oiyue"] [ext_resource type="Texture2D" uid="uid://c78jcjh8fjndd" path="res://Stages/Test3D/assets/3d/particles/flamelet_smooth.png" id="21_xvexm"] [ext_resource type="Script" uid="uid://dglvt140rhg00" path="res://Stages/Test3D/omni_light_3d.gd" id="22_ukp6m"] [ext_resource type="PackedScene" uid="uid://mdxkaqaoybjv" path="res://Stages/Test3D/assets/tent-canvas.glb" id="23_5r2bu"] @@ -737,11 +738,11 @@ transform = Transform3D(0.707107, -0.408607, 0.577096, 0, 0.816138, 0.577857, -0 script = ExtResource("2_sdmks") [node name="Camera3D" type="Camera3D" parent="SubViewportContainer/SubViewport/Player/CameraPivot"] -transform = Transform3D(1, 0, 0, 0, 1, -1.49012e-07, 0, 1.19209e-07, 1, 0.0410548, 0.237644, 3.45114) +transform = Transform3D(1, 0, 0, 0, 1, 1.78814e-07, 0, -3.27826e-07, 1, 0, 0, 7) projection = 1 current = true size = 3.0 -near = 0.005 +near = 0.001 far = 100.0 [node name="PostProcessing" type="MeshInstance3D" parent="SubViewportContainer/SubViewport/Player/CameraPivot/Camera3D"] @@ -759,10 +760,10 @@ mesh = SubResource("QuadMesh_tfa5t") shape = SubResource("BoxShape3D_tfa5t") [node name="VFX" type="Node3D" parent="SubViewportContainer/SubViewport"] -visible = false [node name="Fire" type="GPUParticles3D" parent="SubViewportContainer/SubViewport/VFX"] transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 1.08083, 0.0837402, 0.501403) +visible = false amount = 50 lifetime = 0.4 speed_scale = 0.4 @@ -772,7 +773,7 @@ draw_pass_1 = SubResource("QuadMesh_hvb1l") [node name="OmniLight3D" type="OmniLight3D" parent="SubViewportContainer/SubViewport/VFX/Fire"] transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -0.000509977, 0.121094, -0.00151992) light_color = Color(0.89, 0.461613, 0.2136, 1) -light_energy = 0.802091 +light_energy = 0.590552 light_indirect_energy = 1.084 light_volumetric_fog_energy = 3.764 light_size = 0.105 @@ -794,6 +795,9 @@ trail_lifetime = 0.1 process_material = SubResource("ParticleProcessMaterial_p5fn2") draw_pass_1 = SubResource("RibbonTrailMesh_5r2bu") +[node name="FloatingParticles" type="GPUParticles3D" parent="SubViewportContainer/SubViewport/VFX"] +script = ExtResource("9_oiyue") + [node name="TileGround" type="Node3D" parent="SubViewportContainer/SubViewport"] unique_name_in_owner = true script = ExtResource("24_vyi1v") diff --git a/stages/Test3D/assets/stylizedGrassMeshes/grass.res b/stages/Test3D/assets/stylizedGrassMeshes/grass.res index 598ca867466e4a751740cb9eb8ef22125d36b744..416568c99ce222d5909e413627092a9243d64ef8 100644 GIT binary patch literal 4148 zcmV-45XPX2><}W1^@skwJ-f(01s_S0GfPw95o;;>(BMe zYHDe=9FhurqUSSf?M5wdXl*CSNGT_gBzvd5Cw%JLzGkPiseCX=Hq7u#|t9jh|2>{`}; z)=k$0SD&*=&%3Hz)9*~ZGxaK0x&~D4^z3>z_e59Ee(rnA`ktz7`qifswQKsmc67OR zbY)Z5O`Uy#>-H6{`DDG?*OkL5(Xay0-l1_k+yZla2Ll&}IlSZ4*^%nn)EUouW0` zOWwE!)kcfXtFZTAgO;4X8%8ZEOIgvHTkw5QWvQ@nMekLITeQtQy;^o1~nv-sGGgWd8XwtIwsrC6F`M zSI&7Xf2M<+vyW`n!K|-bZ9{VgkPT(Gd$MMk+g+D?70Z5?s&?NkbANYTUZ&>ZyPZav z+na75k#la7lXP`(EUM-~<*DzVvn@SeuKdp^D`sYIcEWF?{W$}XOpU$CY8Kp=~y~;+2_)(?4I|w`~KCGd3JVo zc6PN?JxkYm4(FntuUu_Y|9^LxOt!<^(+7fY77$hXYs{Uk=l1scN^80gOq^W^_P$^k z5_N*q!Pe`MUj=3IG!y`MyAT4};9qXjat`F!i%vKUR#76NYmf-C67V#qFh68&Lzaew2jLd5^@gNc;*C@b z{$K&51RdS5D)0?YA7H#8C^RBN6i!Ntdlhw29sQVL1beJzu$&t{?FdZPU?QIpm{1YN zhLK>z+m<1UvBH4tC|^C^z$6!}r-{~X!H59N!wOKWI zN+P6Dk86!y6l9Xl0>jfJ;xyTBZzeRJMP9=>!ee9EOK!3hZ@P09+lKLxhN~{=Jp;Bj zmI&|8Fh@g=(u7+LpWU_r=xT4>o!K#gO#3d$C4I@t%~rFRV&hnJWfv%1cTA&h2g%^J zx#~7-LP4jtkp_5n7Xwk7&`aMgl0wB6)AHJJqLj@9J#jn?YCG_g!5TCO*?SnaY(8UQ zFj^slt)-ONM*OGXW7_V$3o$ky1#++&3Q1ebC=VNPi3wgoW!zpQ#qC5{4&XHdZMK(5 zXm%p6I2f7b%?1o}+lodIf>B^KGV;mzODo*-2HsG(c+_+SNsF4Y1@9?QcuZns!hB*K`k57Mr;NZDgq8uiN?+IpTtkfYmc07Kqjj@!2vg=qWipz8uqq#f?+#VBPBUq+iNR<0;r)W zLWqV&k`xrA(g9Hf1ktF3EbkIvbT!G63?W4{Be7MhInRmhK(eZV>~#!sw8u^s0_tJE zb8dh{acFtcD)!7v2q7dnZOHnMyDf^|&|0o+hbZ+g=RtkHiSDBmk*v zQPg!N#IRZ=!6r-pNll5$<{)EK~J=m{enNxy9v)6S6to?$#+Qs+~k<BRgLJw(k_{#WXk$7;Bm6&xrOgoD2U)QJ|FSnQX@PwwUHnA=|2kST`^AqY;3<`5RP%841fn560wNIYZR zBGK33}UmOdB%oNtq z>Wv%S)5RvfS*#lwv6)8E}W#Ylt5%6!S@|$hHDXl?KbrLgJ=HL}X>JaVgyO zytR#IaZsvABCdf)F|Crv4$vwCSC=)m{vjld20Q3B9kJvtNWlMTxwVs@KaX~@d^6g<7-*HLf?CQEvr}d-FtlB>T7*8_r%#|1d$btT&8yV zM$*%AkB(N#f|HX+1xM#01-JJ3R^k}PKQQH@Rhu9c%v|MHyEG;0uI9AJ* z2<`?=d@Y(vIBZ`P%XW}eqdRqjjLi~bi<^dKkPCK&4ZDU>nJdk~#?4@7hL!xX>jyj7Yp=g}>&7J4?u3DzLWi9n5! z6Du(X+hRzMCJIPSEEP4w_`@Csaois|%wZ6FfctF@cYyBp+i>g~b{qQK`G(n@JAd9h!)$A~ z?F<;cu!h;zw(x~9R9I~buc4i`+7&2#cG_qGfTXDL93xIBj))`$1%*nR0U!gJp-DOt zfYeF`jWHBbG$W~kf})aAWtjsUz!d>#8dQ znE^#a#FU``q~-)bp(ZAU2oR}f0HBx|S;$omC`lC@i6S)A!PgPtK&W&Z%&RowzJ`6u zJxuFN!iE$x`>#|3XeDN1v6`g{?jUF98d-S^w+I8y>`s8A{U?7K0Z^al*{w|w&vZv5 zjwU;`q#yQM^8-N{#%99v*kT4C3*|Uwr|m+*s@|p99ms|HITz6#Lu-VlUED6NL29y! yF+@D>K{68$5A|kSp_pu*fs2`eyqJOZRa-{>W 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%0APAE_85w;~eW42i`T#@eLc( zxW;%gzMIB2Vm#x!Wx&|(8OPXeyJ7s|y6JAQi|be{y~T7ZZkNtN?z!7nq*%ps%b_@K z6{k4vxKRvuboRGT9Ch2oZ?kQ)ZDO~y*Jis7ZMD}qHU}nN+So=unY6d@Go^jb+JEK^?bi4Go(^eLqqQ@iA(?h diff --git a/stages/Test3D/new_environment.tres b/stages/Test3D/new_environment.tres index 970049f..0cfe14c 100644 --- a/stages/Test3D/new_environment.tres +++ b/stages/Test3D/new_environment.tres @@ -2,7 +2,6 @@ [sub_resource type="ProceduralSkyMaterial" id="ProceduralSkyMaterial_lg8b7"] sky_horizon_color = Color(0.67451, 0.682353, 0.698039, 1) -sky_curve = 0.0175 ground_bottom_color = Color(1, 1, 1, 1) ground_curve = 0.171484 @@ -10,7 +9,7 @@ ground_curve = 0.171484 sky_material = SubResource("ProceduralSkyMaterial_lg8b7") [resource] -background_mode = 1 +background_mode = 2 background_color = Color(0.752941, 0.776471, 0.827451, 1) sky = SubResource("Sky_7bk1c") ambient_light_source = 2 @@ -18,12 +17,12 @@ ambient_light_color = Color(0.662745, 0.694118, 0.772549, 1) ambient_light_energy = 0.5 reflected_light_source = 2 tonemap_mode = 2 -ssao_enabled = true -ssao_radius = 0.3 -ssao_intensity = 0.5 -ssao_power = 15.0 ssil_enabled = true +sdfgi_use_occlusion = true glow_levels/2 = 0.6 glow_levels/3 = 0.6 glow_levels/5 = 0.0 glow_intensity = 2.0 +fog_density = 0.0635 +volumetric_fog_emission = Color(0.821925, 0.333878, 0.419207, 1) +volumetric_fog_ambient_inject = 16.0 diff --git a/stages/Test3D/particles.gd b/stages/Test3D/particles.gd new file mode 100644 index 0000000..3046ca8 --- /dev/null +++ b/stages/Test3D/particles.gd @@ -0,0 +1,79 @@ +extends GPUParticles3D + +@onready var player: Node3D = get_node("%Player") +var last_player_position: Vector3 +var update_distance: float = 1 + +func _ready(): + setup_floating_particles() + +func _process(delta): + if player: + var current_player_pos = player.global_position + + var target_pos = Vector3( + current_player_pos.x, + 1.0, + current_player_pos.z + ) + + global_position = global_position.lerp(target_pos, delta * 3.0) + +func setup_floating_particles(): + # Basic particle setup + emitting = true + amount = 100 + lifetime = 8.0 + visibility_aabb = AABB(Vector3(-20, 0, -20), Vector3(40, 10, 40)) + + # Create material + var material = ParticleProcessMaterial.new() + + # Emission + material.emission_shape = ParticleProcessMaterial.EMISSION_SHAPE_BOX + material.emission_box_extents = Vector3(5, 2, 5) + + # Movement + material.direction = Vector3(0.1, 0.8, 0.1) + material.initial_velocity_min = 0.2 + material.initial_velocity_max = 0.8 + material.gravity = Vector3(0, -0.3, 0) + + # Floating motion + material.orbit_velocity_min = 0.1 + material.orbit_velocity_max = 0.3 + material.radial_velocity_min = -0.2 + material.radial_velocity_max = 0.2 + + # Size and fade + material.scale_min = 0.01 + material.scale_max = 0.03 + material.scale_over_velocity_min = 0.0 + material.scale_over_velocity_max = 2.0 + + # Color (golden dust particles) + var gradient = Gradient.new() + gradient.add_point(0.0, Color(1.0, 0.9, 0.6, 0.0)) # Fade in + gradient.add_point(0.2, Color(1.0, 0.9, 0.6, 0.5)) # Full opacity + gradient.add_point(0.8, Color(1.0, 0.8, 0.4, 0.3)) # Slight color shift + gradient.add_point(1.0, Color(1.0, 0.7, 0.3, 0.0)) # Fade out + + var gradient_texture = GradientTexture1D.new() + gradient_texture.gradient = gradient + material.color_ramp = gradient_texture + + # Assign the material to the particle system + process_material = material + + # Create and assign visual mesh (small billboard) + var quad_mesh = QuadMesh.new() + quad_mesh.size = Vector2(0.01, 0.01) + draw_pass_1 = quad_mesh + + # Create a basic material for the particles to be visible + var particle_material = StandardMaterial3D.new() + particle_material.albedo_color = ColorData.grass_materials[Season.current]['top'] + particle_material.flags_transparent = true + particle_material.flags_unshaded = true + particle_material.billboard_mode = BaseMaterial3D.BILLBOARD_ENABLED + material_override = particle_material diff --git a/stages/Test3D/particles.gd.uid b/stages/Test3D/particles.gd.uid new file mode 100644 index 0000000..469c48b --- /dev/null +++ b/stages/Test3D/particles.gd.uid @@ -0,0 +1 @@ +uid://bjco8musjqog4