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.
This commit is contained in:
parent
ea5006e8a2
commit
3959333534
46 changed files with 559 additions and 77 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -1,3 +1,4 @@
|
|||
# Godot 4+ specific ignores
|
||||
.godot/
|
||||
/android/
|
||||
*.tmp
|
||||
BIN
Entities/Bush/assets/bush_large.res
Normal file
BIN
Entities/Bush/assets/bush_large.res
Normal file
Binary file not shown.
BIN
Entities/Bush/assets/bush_small.res
Normal file
BIN
Entities/Bush/assets/bush_small.res
Normal file
Binary file not shown.
|
|
@ -4,12 +4,12 @@ importer="scene"
|
|||
importer_version=1
|
||||
type="PackedScene"
|
||||
uid="uid://c3objh5he4fv7"
|
||||
path="res://.godot/imported/plant_bush.glb-e9806d63c67fa60c2bf9125a46d34412.scn"
|
||||
path="res://.godot/imported/plant_bush.glb-4eaf384fbf61c7e3b82505202bc9874e.scn"
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://Entities/Tree/assets/temp/plant_bush.glb"
|
||||
dest_files=["res://.godot/imported/plant_bush.glb-e9806d63c67fa60c2bf9125a46d34412.scn"]
|
||||
source_file="res://Entities/Bush/assets/plant_bush.glb"
|
||||
dest_files=["res://.godot/imported/plant_bush.glb-4eaf384fbf61c7e3b82505202bc9874e.scn"]
|
||||
|
||||
[params]
|
||||
|
||||
|
|
@ -4,12 +4,12 @@ importer="scene"
|
|||
importer_version=1
|
||||
type="PackedScene"
|
||||
uid="uid://cefyfwcx88gn8"
|
||||
path="res://.godot/imported/plant_bushDetailed.glb-b49c41eb141c3e76fb272adf14466b5a.scn"
|
||||
path="res://.godot/imported/plant_bushDetailed.glb-8633db2639023d65ffc045b449119f56.scn"
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://Entities/Tree/assets/temp/plant_bushDetailed.glb"
|
||||
dest_files=["res://.godot/imported/plant_bushDetailed.glb-b49c41eb141c3e76fb272adf14466b5a.scn"]
|
||||
source_file="res://Entities/Bush/assets/plant_bushDetailed.glb"
|
||||
dest_files=["res://.godot/imported/plant_bushDetailed.glb-8633db2639023d65ffc045b449119f56.scn"]
|
||||
|
||||
[params]
|
||||
|
||||
|
|
@ -32,6 +32,17 @@ animation/trimming=false
|
|||
animation/remove_immutable_tracks=true
|
||||
animation/import_rest_as_RESET=false
|
||||
import_script/path=""
|
||||
_subresources={}
|
||||
_subresources={
|
||||
"meshes": {
|
||||
"plant_bushDetailed_Mesh plant_bushDetailed": {
|
||||
"generate/lightmap_uv": 0,
|
||||
"generate/lods": 0,
|
||||
"generate/shadow_meshes": 0,
|
||||
"lods/normal_merge_angle": 60.0,
|
||||
"save_to_file/enabled": true,
|
||||
"save_to_file/path": "res://Entities/Bush/assets/bush_large.res"
|
||||
}
|
||||
}
|
||||
}
|
||||
gltf/naming_version=1
|
||||
gltf/embedded_image_handling=1
|
||||
|
|
@ -4,12 +4,12 @@ importer="scene"
|
|||
importer_version=1
|
||||
type="PackedScene"
|
||||
uid="uid://bqh742nyfy67t"
|
||||
path="res://.godot/imported/plant_bushLarge.glb-444f7e2fefa42d9967360c2460aba973.scn"
|
||||
path="res://.godot/imported/plant_bushLarge.glb-034f46e010fc48537badbba708af03b9.scn"
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://Entities/Tree/assets/temp/plant_bushLarge.glb"
|
||||
dest_files=["res://.godot/imported/plant_bushLarge.glb-444f7e2fefa42d9967360c2460aba973.scn"]
|
||||
source_file="res://Entities/Bush/assets/plant_bushLarge.glb"
|
||||
dest_files=["res://.godot/imported/plant_bushLarge.glb-034f46e010fc48537badbba708af03b9.scn"]
|
||||
|
||||
[params]
|
||||
|
||||
|
|
@ -4,12 +4,12 @@ importer="scene"
|
|||
importer_version=1
|
||||
type="PackedScene"
|
||||
uid="uid://xj45kysvl047"
|
||||
path="res://.godot/imported/plant_bushLargeTriangle.glb-9e831461ae4497bcbdb95549623cbaf6.scn"
|
||||
path="res://.godot/imported/plant_bushLargeTriangle.glb-4c2a6e5fedd13bffdd9cfb0a212c89c8.scn"
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://Entities/Tree/assets/temp/plant_bushLargeTriangle.glb"
|
||||
dest_files=["res://.godot/imported/plant_bushLargeTriangle.glb-9e831461ae4497bcbdb95549623cbaf6.scn"]
|
||||
source_file="res://Entities/Bush/assets/plant_bushLargeTriangle.glb"
|
||||
dest_files=["res://.godot/imported/plant_bushLargeTriangle.glb-4c2a6e5fedd13bffdd9cfb0a212c89c8.scn"]
|
||||
|
||||
[params]
|
||||
|
||||
|
|
@ -4,12 +4,12 @@ importer="scene"
|
|||
importer_version=1
|
||||
type="PackedScene"
|
||||
uid="uid://cjoyr61xdo3hx"
|
||||
path="res://.godot/imported/plant_bushSmall.glb-636059e56114bd2f2c7902ac6a574e3a.scn"
|
||||
path="res://.godot/imported/plant_bushSmall.glb-2218874f9d8b685dac1fd79232f82be8.scn"
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://Entities/Tree/assets/temp/plant_bushSmall.glb"
|
||||
dest_files=["res://.godot/imported/plant_bushSmall.glb-636059e56114bd2f2c7902ac6a574e3a.scn"]
|
||||
source_file="res://Entities/Bush/assets/plant_bushSmall.glb"
|
||||
dest_files=["res://.godot/imported/plant_bushSmall.glb-2218874f9d8b685dac1fd79232f82be8.scn"]
|
||||
|
||||
[params]
|
||||
|
||||
|
|
@ -32,6 +32,17 @@ animation/trimming=false
|
|||
animation/remove_immutable_tracks=true
|
||||
animation/import_rest_as_RESET=false
|
||||
import_script/path=""
|
||||
_subresources={}
|
||||
_subresources={
|
||||
"meshes": {
|
||||
"plant_bushSmall_Mesh plant_bushSmall": {
|
||||
"generate/lightmap_uv": 0,
|
||||
"generate/lods": 0,
|
||||
"generate/shadow_meshes": 0,
|
||||
"lods/normal_merge_angle": 60.0,
|
||||
"save_to_file/enabled": true,
|
||||
"save_to_file/path": "res://Entities/Bush/assets/bush_small.res"
|
||||
}
|
||||
}
|
||||
}
|
||||
gltf/naming_version=1
|
||||
gltf/embedded_image_handling=1
|
||||
|
|
@ -4,12 +4,12 @@ importer="scene"
|
|||
importer_version=1
|
||||
type="PackedScene"
|
||||
uid="uid://dsch27pqxgud5"
|
||||
path="res://.godot/imported/plant_bushTriangle.glb-68d6459f20861ebce284d042b5ea0419.scn"
|
||||
path="res://.godot/imported/plant_bushTriangle.glb-518273ba96c29be659e875adddc31676.scn"
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://Entities/Tree/assets/temp/plant_bushTriangle.glb"
|
||||
dest_files=["res://.godot/imported/plant_bushTriangle.glb-68d6459f20861ebce284d042b5ea0419.scn"]
|
||||
source_file="res://Entities/Bush/assets/plant_bushTriangle.glb"
|
||||
dest_files=["res://.godot/imported/plant_bushTriangle.glb-518273ba96c29be659e875adddc31676.scn"]
|
||||
|
||||
[params]
|
||||
|
||||
|
|
@ -1,11 +1,15 @@
|
|||
[gd_scene load_steps=13 format=3 uid="uid://bwcevwwphdvq"]
|
||||
[gd_scene load_steps=17 format=3 uid="uid://bwcevwwphdvq"]
|
||||
|
||||
[ext_resource type="Script" uid="uid://bq7hia2dit80y" path="res://Entities/GroundTile/ground_tile.gd" id="1_uwxqs"]
|
||||
[ext_resource type="Script" uid="uid://bq7hia2dit80y" path="res://Entities/GroundTile/scripts/ground_tile.gd" id="1_uwxqs"]
|
||||
[ext_resource type="ArrayMesh" uid="uid://duj6747nq4qsk" path="res://Stages/Test3D/assets/stylizedGrassMeshes/grass.res" id="3_8mhad"]
|
||||
[ext_resource type="Script" uid="uid://cacp8ncwuofuj" path="res://Entities/GroundTile/scripts/grass.gd" id="3_224hx"]
|
||||
[ext_resource type="Material" uid="uid://b1miqvl8lus75" path="res://Stages/Test3D/GrassMaterialOverride.tres" id="3_f37ob"]
|
||||
[ext_resource type="Script" uid="uid://btju6b83mvgvk" path="res://Entities/GroundTile/scripts/grass_multimesh.gd" id="4_3wpcb"]
|
||||
[ext_resource type="Script" uid="uid://bt67yhdkwtqy5" path="res://Entities/GroundTile/scripts/bushes.gd" id="6_224hx"]
|
||||
[ext_resource type="Script" uid="uid://cqko4m7cbxsfb" path="res://Entities/GroundTile/scripts/trees.gd" id="7_7lc7k"]
|
||||
[ext_resource type="Script" uid="uid://dri5tubavplji" path="res://Entities/GroundTile/scripts/bush_multimesh.gd" id="7_jysav"]
|
||||
[ext_resource type="Script" uid="uid://d3s0u7rm1y7i6" path="res://Entities/GroundTile/scripts/flower_multimesh.gd" id="8_jysav"]
|
||||
[ext_resource type="Script" uid="uid://18vxtm3ua4x0" path="res://Entities/GroundTile/scripts/flowers.gd" id="8_q0r4p"]
|
||||
|
||||
[sub_resource type="ViewportTexture" id="ViewportTexture_h4g11"]
|
||||
viewport_path = NodePath("DebugText/DebugTextViewport")
|
||||
|
|
@ -67,5 +71,33 @@ script = ExtResource("4_3wpcb")
|
|||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.0111763, 0)
|
||||
mesh = SubResource("PlaneMesh_f37ob")
|
||||
|
||||
[node name="Bushes" type="Node3D" parent="."]
|
||||
script = ExtResource("6_224hx")
|
||||
|
||||
[node name="BushMultimesh" type="MultiMeshInstance3D" parent="Bushes"]
|
||||
cast_shadow = 0
|
||||
multimesh = SubResource("MultiMesh_3wpcb")
|
||||
script = ExtResource("7_jysav")
|
||||
|
||||
[node name="BushTarget" type="MeshInstance3D" parent="Bushes"]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.0111763, 0)
|
||||
mesh = SubResource("PlaneMesh_f37ob")
|
||||
|
||||
[node name="Flowers" type="Node3D" parent="."]
|
||||
script = ExtResource("8_q0r4p")
|
||||
|
||||
[node name="FlowerMultimesh" type="MultiMeshInstance3D" parent="Flowers"]
|
||||
cast_shadow = 0
|
||||
multimesh = SubResource("MultiMesh_3wpcb")
|
||||
script = ExtResource("8_jysav")
|
||||
|
||||
[node name="FloewerTarget" type="MeshInstance3D" parent="Flowers"]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.0111763, 0)
|
||||
mesh = SubResource("PlaneMesh_f37ob")
|
||||
|
||||
[node name="Trees" type="Node3D" parent="."]
|
||||
script = ExtResource("7_7lc7k")
|
||||
|
||||
[node name="Special" type="Node3D" parent="."]
|
||||
|
||||
[node name="Rocks" type="Node3D" parent="Special"]
|
||||
|
|
|
|||
56
Entities/GroundTile/scripts/bush_multimesh.gd
Normal file
56
Entities/GroundTile/scripts/bush_multimesh.gd
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
extends MultiMeshInstance3D
|
||||
var mm: MultiMesh
|
||||
var parent_node: BushController
|
||||
static var bush_mesh: Mesh = null
|
||||
var material: ShaderMaterial
|
||||
|
||||
func _ready() -> 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
|
||||
1
Entities/GroundTile/scripts/bush_multimesh.gd.uid
Normal file
1
Entities/GroundTile/scripts/bush_multimesh.gd.uid
Normal file
|
|
@ -0,0 +1 @@
|
|||
uid://dri5tubavplji
|
||||
36
Entities/GroundTile/scripts/bushes.gd
Normal file
36
Entities/GroundTile/scripts/bushes.gd
Normal file
|
|
@ -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)
|
||||
1
Entities/GroundTile/scripts/bushes.gd.uid
Normal file
1
Entities/GroundTile/scripts/bushes.gd.uid
Normal file
|
|
@ -0,0 +1 @@
|
|||
uid://bt67yhdkwtqy5
|
||||
59
Entities/GroundTile/scripts/flower_multimesh.gd
Normal file
59
Entities/GroundTile/scripts/flower_multimesh.gd
Normal file
|
|
@ -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
|
||||
1
Entities/GroundTile/scripts/flower_multimesh.gd.uid
Normal file
1
Entities/GroundTile/scripts/flower_multimesh.gd.uid
Normal file
|
|
@ -0,0 +1 @@
|
|||
uid://d3s0u7rm1y7i6
|
||||
36
Entities/GroundTile/scripts/flowers.gd
Normal file
36
Entities/GroundTile/scripts/flowers.gd
Normal file
|
|
@ -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)
|
||||
1
Entities/GroundTile/scripts/flowers.gd.uid
Normal file
1
Entities/GroundTile/scripts/flowers.gd.uid
Normal file
|
|
@ -0,0 +1 @@
|
|||
uid://18vxtm3ua4x0
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
BIN
Entities/Plant/assets/flower.res
Normal file
BIN
Entities/Plant/assets/flower.res
Normal file
Binary file not shown.
|
|
@ -4,12 +4,12 @@ importer="scene"
|
|||
importer_version=1
|
||||
type="PackedScene"
|
||||
uid="uid://c8kl0a1nstbuc"
|
||||
path="res://.godot/imported/flower_purpleB.glb-69a5b0606a51601fdd91762f296ce88b.scn"
|
||||
path="res://.godot/imported/flower_tall.glb-105aa1c03f7501793bbf8ad0e150a0ed.scn"
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://Entities/Tree/assets/temp/flower_purpleB.glb"
|
||||
dest_files=["res://.godot/imported/flower_purpleB.glb-69a5b0606a51601fdd91762f296ce88b.scn"]
|
||||
source_file="res://Entities/Plant/assets/flower_tall.glb"
|
||||
dest_files=["res://.godot/imported/flower_tall.glb-105aa1c03f7501793bbf8ad0e150a0ed.scn"]
|
||||
|
||||
[params]
|
||||
|
||||
|
|
@ -32,6 +32,17 @@ animation/trimming=false
|
|||
animation/remove_immutable_tracks=true
|
||||
animation/import_rest_as_RESET=false
|
||||
import_script/path=""
|
||||
_subresources={}
|
||||
_subresources={
|
||||
"meshes": {
|
||||
"flower_tall_Mesh flower_purpleB": {
|
||||
"generate/lightmap_uv": 0,
|
||||
"generate/lods": 0,
|
||||
"generate/shadow_meshes": 0,
|
||||
"lods/normal_merge_angle": 60.0,
|
||||
"save_to_file/enabled": true,
|
||||
"save_to_file/path": "res://Entities/Plant/assets/flower.res"
|
||||
}
|
||||
}
|
||||
}
|
||||
gltf/naming_version=1
|
||||
gltf/embedded_image_handling=1
|
||||
BIN
Entities/Plant/assets/flower_tall.res
Normal file
BIN
Entities/Plant/assets/flower_tall.res
Normal file
Binary file not shown.
|
|
@ -104,5 +104,5 @@ func update_material_in_mesh_instance(mesh_instance: MeshInstance3D, material_na
|
|||
|
||||
|
||||
func apply_material_with_queue(mesh_instance: MeshInstance3D, index: int, material: StandardMaterial3D):
|
||||
ColorData.queue_material_application(mesh_instance, index, material)
|
||||
MaterialManager.queue_material_application(mesh_instance, index, material)
|
||||
return
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ func spawn_model():
|
|||
|
||||
# Instantiate the model from the TreeDataResource
|
||||
model_instance = tree_data.model.instantiate()
|
||||
tree_data.apply_seasonal_colors(model_instance, "spring")
|
||||
tree_data.apply_seasonal_colors(model_instance, Season.current)
|
||||
add_child(model_instance)
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -3,48 +3,44 @@ extends Node
|
|||
|
||||
var tree_collection = preload("res://Entities/Tree/Resources/TreeData.tres") as TreeDataCollection
|
||||
var tree_materials: Dictionary = {}
|
||||
var grass_materials: Dictionary = {"spring": {"base": Vector3(0.196, 0.392, 0.196), "top": Vector3(0.253, 0.492, 0.253), "bottom": Vector3(0.196, 0.392, 0.196)},
|
||||
"summer": {"base": null, "top": null, "bottom": null},
|
||||
"autumn": {"base": null, "top": null, "bottom": null},
|
||||
"winter": {"base": null, "top": null, "bottom": null}}
|
||||
|
||||
|
||||
## TODO - Move this out into its own class
|
||||
var material_application_queue: Array = []
|
||||
var max_materials_per_frame: int = 10 # Adjust based on performance
|
||||
var grass_materials: Dictionary = {
|
||||
"spring": {
|
||||
"bottom": Color(0.18, 0.35, 0.12), # #2e5920 - Dark forest base
|
||||
"top": Color(0.18, 0.35, 0.12), # Color(0.35, 0.65, 0.25), # #59a640 - Bright spring green
|
||||
"base": Color(0.12, 0.25, 0.08), # #1f4014 - Deep shadow green
|
||||
"flower": Color(0.776, 0.835, 0.855) # rgb(198, 213, 218) - Spring flower color
|
||||
},
|
||||
"summer": {
|
||||
"bottom": Color(0.22, 0.45, 0.18), # #38732e - Rich summer base
|
||||
"top": Color(0.22, 0.45, 0.18), # Color(0.4, 0.75, 0.3), # #66bf4d - Sun-kissed green
|
||||
"base": Color(0.15, 0.3, 0.12), # #264d1f - Shaded summer base
|
||||
"flower": Color(0.408, 0.537, 0.886) # rgb(104, 137, 226) - Summer flower color
|
||||
},
|
||||
"autumn": {
|
||||
"bottom": Color(0.35, 0.4, 0.15), # #596626 - Yellowing grass
|
||||
"top": Color(0.42, 0.45, 0.19), # #6b7330 - Muted autumn tips
|
||||
"base": Color(0.25, 0.25, 0.1), # #40401a - Dying grass base
|
||||
"flower": Color(0.820, 0.337, 0.318) # rgb(209, 86, 81) - Autumn flower color
|
||||
},
|
||||
"winter": {
|
||||
"bottom": Color(0.25, 0.3, 0.2), # #404d33 - Dormant green-brown
|
||||
"top": Color(0.35, 0.4, 0.3), # #59664d - Frost-touched tips
|
||||
"base": Color(0.2, 0.2, 0.15), # #333326 - Winter soil color
|
||||
"flower": Color(1.0, 0.8, 0.6) # #ffd9b3 - Winter flower color
|
||||
}
|
||||
}
|
||||
var bush_materials: Dictionary = {}
|
||||
var flower_materials: Dictionary = {}
|
||||
|
||||
@export var tree_colours: Array
|
||||
|
||||
func _ready() -> 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")
|
||||
|
|
|
|||
30
Utilities/MaterialManager/MaterialManager.gd
Normal file
30
Utilities/MaterialManager/MaterialManager.gd
Normal file
|
|
@ -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)
|
||||
1
Utilities/MaterialManager/MaterialManager.gd.uid
Normal file
1
Utilities/MaterialManager/MaterialManager.gd.uid
Normal file
|
|
@ -0,0 +1 @@
|
|||
uid://hxkwqb71371n
|
||||
11
Utilities/Seasons/seasons.gd
Normal file
11
Utilities/Seasons/seasons.gd
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
class_name SeasonController
|
||||
extends Node
|
||||
|
||||
var seasons: Array = [
|
||||
"spring",
|
||||
"summer",
|
||||
"autumn",
|
||||
"winter"
|
||||
]
|
||||
|
||||
@export var current: String = "autumn"
|
||||
1
Utilities/Seasons/seasons.gd.uid
Normal file
1
Utilities/Seasons/seasons.gd.uid
Normal file
|
|
@ -0,0 +1 @@
|
|||
uid://buj63lgb7ckto
|
||||
23
leaves.gdshader
Normal file
23
leaves.gdshader
Normal file
|
|
@ -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;
|
||||
}
|
||||
1
leaves.gdshader.uid
Normal file
1
leaves.gdshader.uid
Normal file
|
|
@ -0,0 +1 @@
|
|||
uid://d0e60a0hdk02j
|
||||
|
|
@ -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]
|
||||
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
|
|
|
|||
Binary file not shown.
|
|
@ -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
|
||||
|
|
|
|||
79
stages/Test3D/particles.gd
Normal file
79
stages/Test3D/particles.gd
Normal file
|
|
@ -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
|
||||
1
stages/Test3D/particles.gd.uid
Normal file
1
stages/Test3D/particles.gd.uid
Normal file
|
|
@ -0,0 +1 @@
|
|||
uid://bjco8musjqog4
|
||||
Loading…
Add table
Add a link
Reference in a new issue