712 lines
23 KiB
GDScript
712 lines
23 KiB
GDScript
# Copyright © 2022 Kasper Arnklit Frandsen - MIT License
|
|
# See `LICENSE.md` included in the source distribution for details.
|
|
@tool
|
|
extends Node3D
|
|
|
|
const WaterHelperMethods = preload("./water_helper_methods.gd")
|
|
|
|
const FILTER_RENDERER_PATH = "res://addons/waterways/filter_renderer.tscn"
|
|
const FLOW_OFFSET_NOISE_TEXTURE_PATH = "res://addons/waterways/textures/flow_offset_noise.png"
|
|
const FOAM_NOISE_PATH = "res://addons/waterways/textures/foam_noise.png"
|
|
|
|
const MATERIAL_CATEGORIES = {
|
|
albedo_ = "Albedo",
|
|
emission_ = "Emission",
|
|
transparency_ = "Transparency",
|
|
flow_ = "Flow",
|
|
foam_ = "Foam",
|
|
custom_ = "Custom"
|
|
}
|
|
|
|
enum SHADER_TYPES {WATER, LAVA, CUSTOM}
|
|
const BUILTIN_SHADERS = [
|
|
{
|
|
name = "Water",
|
|
shader_path = "res://addons/waterways/shaders/river.gdshader",
|
|
texture_paths = [
|
|
{
|
|
name = "normal_bump_texture",
|
|
path = "res://addons/waterways/textures/water1_normal_bump.png"
|
|
}
|
|
]
|
|
},
|
|
{
|
|
name = "Lava",
|
|
shader_path = "res://addons/waterways/shaders/lava.gdshader",
|
|
texture_paths = [
|
|
{
|
|
name = "normal_bump_texture",
|
|
path = "res://addons/waterways/textures/lava_normal_bump.png"
|
|
},
|
|
{
|
|
name = "emission_texture",
|
|
path = "res://addons/waterways/textures/lava_emission.png"
|
|
}
|
|
]
|
|
}
|
|
]
|
|
|
|
const DEBUG_SHADER = {
|
|
name = "Debug",
|
|
shader_path = "res://addons/waterways/shaders/river_debug.gdshader",
|
|
texture_paths = [
|
|
{
|
|
name = "debug_pattern",
|
|
path = "res://addons/waterways/textures/debug_pattern.png"
|
|
},
|
|
{
|
|
name = "debug_arrow",
|
|
path = "res://addons/waterways/textures/debug_arrow.svg"
|
|
}
|
|
]
|
|
}
|
|
|
|
const DEFAULT_PARAMETERS = {
|
|
shape_step_length_divs = 1,
|
|
shape_step_width_divs = 1,
|
|
shape_smoothness = 0.5,
|
|
mat_shader_type = 0,
|
|
mat_custom_shader = null,
|
|
baking_resolution = 2,
|
|
baking_raycast_distance = 10.0,
|
|
baking_raycast_layers = 1,
|
|
baking_dilate = 0.6,
|
|
baking_flowmap_blur = 0.04,
|
|
baking_foam_cutoff = 0.9,
|
|
baking_foam_offset = 0.1,
|
|
baking_foam_blur = 0.02,
|
|
lod_lod0_distance = 50.0,
|
|
}
|
|
|
|
|
|
# Shape Properties
|
|
var shape_step_length_divs : int = 1: set = set_step_length_divs
|
|
var shape_step_width_divs : int = 1: set = set_step_width_divs
|
|
var shape_smoothness : float = 0.5: set = set_smoothness
|
|
|
|
# Material Properties that not handled in shader
|
|
var mat_shader_type : SHADER_TYPES: set = set_shader_type
|
|
var mat_custom_shader : Shader: set = set_custom_shader
|
|
|
|
# LOD Properties
|
|
var lod_lod0_distance : float = 50.0: set = set_lod0_distance
|
|
|
|
# Bake Properties
|
|
var baking_resolution : int = 2
|
|
var baking_raycast_distance : float = 10.0
|
|
var baking_raycast_layers : int = 1
|
|
var baking_dilate : float = 0.6
|
|
var baking_flowmap_blur : float = 0.04
|
|
var baking_foam_cutoff : float = 0.9
|
|
var baking_foam_offset : float = 0.1
|
|
var baking_foam_blur : float = 0.02
|
|
|
|
# Public variables
|
|
var curve : Curve3D
|
|
var widths := [1.0, 1.0]: set = set_widths
|
|
var valid_flowmap := false
|
|
var debug_view : int = 0: set = set_debug_view
|
|
var mesh_instance : MeshInstance3D
|
|
var flow_foam_noise : Texture2D
|
|
var dist_pressure : Texture2D
|
|
|
|
# Private variables
|
|
var _steps : int = 2
|
|
var _st : SurfaceTool
|
|
var _mdt : MeshDataTool
|
|
var _debug_material : ShaderMaterial
|
|
var _first_enter_tree := true
|
|
var _filter_renderer : PackedScene
|
|
# Serialised private variables
|
|
var _material : ShaderMaterial
|
|
var _selected_shader : int = SHADER_TYPES.WATER
|
|
var _uv2_sides : int
|
|
|
|
# river_changed used to update handles when values are changed on script side
|
|
# progress_notified used to up progress bar when baking maps
|
|
# albedo_set is needed since the gradient is a custom inspector that needs a signal to update from script side
|
|
signal river_changed
|
|
signal progress_notified
|
|
#signal albedo_set
|
|
|
|
# Internal Methods
|
|
func _get_property_list() -> Array:
|
|
var props = [
|
|
{
|
|
name = "Shape",
|
|
type = TYPE_NIL,
|
|
hint_string = "shape_",
|
|
usage = PROPERTY_USAGE_GROUP | PROPERTY_USAGE_SCRIPT_VARIABLE
|
|
},
|
|
{
|
|
name = "shape_step_length_divs",
|
|
type = TYPE_INT,
|
|
hint = PROPERTY_HINT_RANGE,
|
|
hint_string = "1, 8",
|
|
usage = PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE
|
|
},
|
|
{
|
|
name = "shape_step_width_divs",
|
|
type = TYPE_INT,
|
|
hint = PROPERTY_HINT_RANGE,
|
|
hint_string = "1, 8",
|
|
usage = PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE
|
|
},
|
|
{
|
|
name = "shape_smoothness",
|
|
type = TYPE_FLOAT,
|
|
hint = PROPERTY_HINT_RANGE,
|
|
hint_string = "0.1, 5.0",
|
|
usage = PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE
|
|
},
|
|
{
|
|
name = "Material",
|
|
type = TYPE_NIL,
|
|
hint_string = "mat_",
|
|
usage = PROPERTY_USAGE_GROUP | PROPERTY_USAGE_SCRIPT_VARIABLE
|
|
},
|
|
{
|
|
name = "mat_shader_type",
|
|
type = TYPE_INT,
|
|
hint = PROPERTY_HINT_ENUM,
|
|
hint_string = "Water, Lava, Custom",
|
|
usage = PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE
|
|
},
|
|
{
|
|
name = "mat_custom_shader",
|
|
type = TYPE_OBJECT,
|
|
hint = PROPERTY_HINT_RESOURCE_TYPE,
|
|
usage = PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE,
|
|
hint_string = "Shader"
|
|
},
|
|
]
|
|
|
|
var props2 = []
|
|
var mat_categories = MATERIAL_CATEGORIES.duplicate(true)
|
|
|
|
if _material.shader != null:
|
|
var shader_params := RenderingServer.get_shader_parameter_list(_material.shader.get_rid())
|
|
|
|
for p in shader_params:
|
|
if p.name.begins_with("i_"):
|
|
continue
|
|
var hit_category = null
|
|
for category in mat_categories:
|
|
if p.name.begins_with(category):
|
|
props2.append({
|
|
name = str("Material/", mat_categories[category]),
|
|
type = TYPE_NIL,
|
|
hint_string = str("mat_", category),
|
|
usage = PROPERTY_USAGE_GROUP | PROPERTY_USAGE_SCRIPT_VARIABLE
|
|
})
|
|
hit_category = category
|
|
break
|
|
if hit_category != null:
|
|
mat_categories.erase(hit_category)
|
|
var cp := {}
|
|
for k in p:
|
|
cp[k] = p[k]
|
|
cp.name = str("mat_", p.name)
|
|
if "curve" in cp.name:
|
|
cp.hint = PROPERTY_HINT_EXP_EASING
|
|
cp.hint_string = "EASE"
|
|
props2.append(cp)
|
|
var props3 = [
|
|
{
|
|
name = "Lod",
|
|
type = TYPE_NIL,
|
|
hint_string = "lod_",
|
|
usage = PROPERTY_USAGE_GROUP | PROPERTY_USAGE_SCRIPT_VARIABLE
|
|
},
|
|
{
|
|
name = "lod_lod0_distance",
|
|
type = TYPE_FLOAT,
|
|
hint = PROPERTY_HINT_RANGE,
|
|
hint_string = "5.0, 200.0",
|
|
usage = PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE
|
|
},
|
|
{
|
|
name = "Baking",
|
|
type = TYPE_NIL,
|
|
hint_string = "baking_",
|
|
usage = PROPERTY_USAGE_GROUP | PROPERTY_USAGE_SCRIPT_VARIABLE
|
|
},
|
|
{
|
|
name = "baking_resolution",
|
|
type = TYPE_INT,
|
|
hint = PROPERTY_HINT_ENUM,
|
|
hint_string = "64, 128, 256, 512, 1024",
|
|
usage = PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE
|
|
},
|
|
{
|
|
name = "baking_raycast_distance",
|
|
type = TYPE_FLOAT,
|
|
hint = PROPERTY_HINT_RANGE,
|
|
hint_string = "0.0, 100.0",
|
|
usage = PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE
|
|
},
|
|
{
|
|
name = "baking_raycast_layers",
|
|
type = TYPE_INT,
|
|
hint = PROPERTY_HINT_LAYERS_3D_PHYSICS,
|
|
usage = PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE
|
|
},
|
|
{
|
|
name = "baking_dilate",
|
|
type = TYPE_FLOAT,
|
|
hint = PROPERTY_HINT_RANGE,
|
|
hint_string = "0.0, 1.0",
|
|
usage = PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE
|
|
},
|
|
{
|
|
name = "baking_flowmap_blur",
|
|
type = TYPE_FLOAT,
|
|
hint = PROPERTY_HINT_RANGE,
|
|
hint_string = "0.0, 1.0",
|
|
usage = PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE
|
|
},
|
|
{
|
|
name = "baking_foam_cutoff",
|
|
type = TYPE_FLOAT,
|
|
hint = PROPERTY_HINT_RANGE,
|
|
hint_string = "0.0, 1.0",
|
|
usage = PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE
|
|
},
|
|
{
|
|
name = "baking_foam_offset",
|
|
type = TYPE_FLOAT,
|
|
hint = PROPERTY_HINT_RANGE,
|
|
hint_string = "0.0, 1.0",
|
|
usage = PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE
|
|
},
|
|
{
|
|
name = "baking_foam_blur",
|
|
type = TYPE_FLOAT,
|
|
hint = PROPERTY_HINT_RANGE,
|
|
hint_string = "0.0, 1.0",
|
|
usage = PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE
|
|
},
|
|
# Serialize these values without exposing it in the inspector
|
|
{
|
|
name = "curve",
|
|
type = TYPE_OBJECT,
|
|
usage = PROPERTY_USAGE_STORAGE
|
|
},
|
|
{
|
|
name = "widths",
|
|
type = TYPE_ARRAY,
|
|
usage = PROPERTY_USAGE_STORAGE
|
|
},
|
|
{
|
|
name = "valid_flowmap",
|
|
type = TYPE_BOOL,
|
|
usage = PROPERTY_USAGE_STORAGE
|
|
},
|
|
{
|
|
name = "flow_foam_noise",
|
|
type = TYPE_OBJECT,
|
|
usage = PROPERTY_USAGE_STORAGE
|
|
},
|
|
{
|
|
name = "dist_pressure",
|
|
type = TYPE_OBJECT,
|
|
usage = PROPERTY_USAGE_STORAGE
|
|
},
|
|
{
|
|
name = "_material",
|
|
type = TYPE_OBJECT,
|
|
hint = PROPERTY_HINT_RESOURCE_TYPE,
|
|
hint_string = "ShaderMaterial",
|
|
usage = PROPERTY_USAGE_STORAGE
|
|
},
|
|
{
|
|
name = "_selected_shader",
|
|
type = TYPE_INT,
|
|
usage = PROPERTY_USAGE_STORAGE
|
|
},
|
|
{
|
|
name = "_uv2_sides",
|
|
type = TYPE_INT,
|
|
usage = PROPERTY_USAGE_STORAGE
|
|
}
|
|
]
|
|
var combined_props = props + props2 + props3
|
|
|
|
# TODO, remember to remove this
|
|
# print(var2str(combined_props))
|
|
|
|
return combined_props
|
|
|
|
|
|
func _set(property: StringName, value) -> bool:
|
|
if str(property).begins_with("mat_"):
|
|
# TODO, is there a better way to do this, now that right() has changed?
|
|
var param_name = str(property).replace("mat_", "")
|
|
_material.set_shader_parameter(param_name, value)
|
|
return true
|
|
return false
|
|
|
|
|
|
func _get(property : StringName):
|
|
#print("in _get(), property: ", property)
|
|
if str(property).begins_with("mat_"):
|
|
var param_name = str(property).replace("mat_", "")
|
|
#print("property name in _get is: ", property)
|
|
#print("param name in _get is: ", param_name)
|
|
#print ("_material.get_shader_parameter(param_name): ", _material.get_shader_parameter(param_name))
|
|
return _material.get_shader_parameter(param_name)
|
|
|
|
|
|
# TODO - This doesn't currently work in Godot 4. https://github.com/godotengine/godot/issues/69335
|
|
#func _property_can_revert(property : StringName) -> bool:
|
|
# if str(property).begins_with("mat_"):
|
|
## if "color" in property:
|
|
## # TODO - we are disabling revert for color parameters due to this
|
|
## # bug: https://github.com/godotengine/godot/issues/45388
|
|
## return false
|
|
# var param_name = str(property).replace("mat_", "")
|
|
# return _material._property_can_revert(str("shader_param/", param_name))
|
|
#
|
|
# if not DEFAULT_PARAMETERS.has(property):
|
|
# return false
|
|
# if get(property) != DEFAULT_PARAMETERS[property]:
|
|
# return true
|
|
# return false
|
|
#
|
|
#
|
|
#func _property_get_revert(property : StringName):
|
|
# if str(property).begins_with("mat_"):
|
|
# var param_name = str(property).replace("mat_", "")
|
|
# var revert_value = _material._property_get_revert(str("shader_param/", param_name))
|
|
# return revert_value
|
|
|
|
|
|
func _init() -> void:
|
|
_st = SurfaceTool.new()
|
|
_mdt = MeshDataTool.new()
|
|
_filter_renderer = load(FILTER_RENDERER_PATH)
|
|
|
|
_debug_material = ShaderMaterial.new()
|
|
_debug_material.shader = load(DEBUG_SHADER.shader_path) as Shader
|
|
for texture in DEBUG_SHADER.texture_paths:
|
|
_debug_material.set_shader_parameter(texture.name, load(texture.path) as Texture2D)
|
|
|
|
_material = ShaderMaterial.new()
|
|
_material.shader = load(BUILTIN_SHADERS[mat_shader_type].shader_path) as Shader
|
|
for texture in BUILTIN_SHADERS[mat_shader_type].texture_paths:
|
|
_material.set_shader_parameter(texture.name, load(texture.path) as Texture2D)
|
|
# Have to manually set the color or it does not default right. Not sure how to work around this
|
|
_material.set_shader_parameter("albedo_color", Transform3D(Vector3(0.0, 0.8, 1.0), Vector3(0.15, 0.2, 0.5), Vector3.ZERO, Vector3.ZERO))
|
|
|
|
|
|
func _enter_tree() -> void:
|
|
if Engine.is_editor_hint() and _first_enter_tree:
|
|
_first_enter_tree = false
|
|
|
|
if not curve:
|
|
curve = Curve3D.new()
|
|
curve.bake_interval = 0.05
|
|
curve.add_point(Vector3(0.0, 0.0, 0.0), Vector3(0.0, 0.0, -0.25), Vector3(0.0, 0.0, 0.25))
|
|
curve.add_point(Vector3(0.0, 0.0, 1.0), Vector3(0.0, 0.0, -0.25), Vector3(0.0, 0.0, 0.25))
|
|
|
|
if get_child_count() <= 0:
|
|
## This is what happens on creating a new river
|
|
var new_mesh_instance := MeshInstance3D.new()
|
|
new_mesh_instance.name = "RiverMeshInstance"
|
|
add_child(new_mesh_instance)
|
|
mesh_instance = get_child(0) as MeshInstance3D
|
|
_generate_river()
|
|
else:
|
|
mesh_instance = get_child(0) as MeshInstance3D
|
|
_material = mesh_instance.mesh.surface_get_material(0) as ShaderMaterial
|
|
|
|
set_materials("i_valid_flowmap", valid_flowmap)
|
|
set_materials("i_uv2_sides", _uv2_sides)
|
|
set_materials("i_distmap", dist_pressure)
|
|
set_materials("i_flowmap", flow_foam_noise)
|
|
set_materials("i_texture_foam_noise", load(FOAM_NOISE_PATH) as Texture2D)
|
|
|
|
|
|
func _get_configuration_warning() -> String:
|
|
if valid_flowmap:
|
|
return ""
|
|
else:
|
|
return "No flowmap is set. Select River -> Generate Flow & Foam Map to generate and assign one."
|
|
|
|
|
|
func get_transformed_aabb() -> AABB:
|
|
return global_transform * mesh_instance.get_aabb()
|
|
|
|
|
|
# Public Methods - These should all be good to use as API from other scripts
|
|
func add_point(position : Vector3, index : int, dir : Vector3 = Vector3.ZERO, width : float = 0.0) -> void:
|
|
if index == -1:
|
|
var last_index := curve.get_point_count() - 1
|
|
var dist = position.distance_to(curve.get_point_position(last_index))
|
|
var new_dir = dir if dir != Vector3.ZERO else (position - curve.get_point_position(last_index) - curve.get_point_out(last_index) ).normalized() * 0.25 * dist
|
|
curve.add_point(position, -new_dir, new_dir, -1)
|
|
widths.append(widths[widths.size() - 1]) # If this is a new point at the end, add a width that's the same as last
|
|
else:
|
|
var dist = curve.get_point_position(index).distance_to(curve.get_point_position(index + 1))
|
|
var new_dir = dir if dir != Vector3.ZERO else (curve.get_point_position(index + 1) - curve.get_point_position(index)).normalized() * 0.25 * dist
|
|
curve.add_point(position, -new_dir, new_dir, index + 1)
|
|
var new_width = width if width != 0.0 else (widths[index] + widths[index + 1]) / 2.0
|
|
widths.insert(index + 1, new_width) # We set the width to the average of the two surrounding widths
|
|
emit_signal("river_changed")
|
|
_generate_river()
|
|
|
|
|
|
func remove_point(index : int) -> void:
|
|
# We don't allow rivers shorter than 2 points
|
|
if curve.get_point_count() <= 2:
|
|
return
|
|
curve.remove_point(index)
|
|
widths.remove_at(index)
|
|
emit_signal("river_changed")
|
|
_generate_river()
|
|
|
|
|
|
func bake_texture() -> void:
|
|
_generate_river()
|
|
_generate_flowmap(pow(2, 6 + baking_resolution))
|
|
|
|
|
|
func set_curve_point_position(index : int, position : Vector3) -> void:
|
|
curve.set_point_position(index, position)
|
|
_generate_river()
|
|
|
|
|
|
func set_curve_point_in(index : int, position : Vector3) -> void:
|
|
curve.set_point_in(index, position)
|
|
_generate_river()
|
|
|
|
|
|
func set_curve_point_out(index : int, position : Vector3) -> void:
|
|
curve.set_point_out(index, position)
|
|
_generate_river()
|
|
|
|
|
|
func set_widths(new_widths) -> void:
|
|
widths = new_widths
|
|
if _first_enter_tree:
|
|
return
|
|
_generate_river()
|
|
|
|
|
|
func set_materials(param : String, value) -> void:
|
|
_material.set_shader_parameter(param, value)
|
|
_debug_material.set_shader_parameter(param, value)
|
|
|
|
|
|
func set_debug_view(index : int) -> void:
|
|
debug_view = index
|
|
if index == 0:
|
|
mesh_instance.material_override = null
|
|
else:
|
|
_debug_material.set_shader_parameter("mode", index)
|
|
mesh_instance.material_override =_debug_material
|
|
|
|
|
|
func spawn_mesh() -> void:
|
|
if owner == null:
|
|
push_warning("Cannot create MeshInstance3D sibling when River is root.")
|
|
return
|
|
var sibling_mesh := mesh_instance.duplicate(true)
|
|
get_parent().add_child(sibling_mesh)
|
|
sibling_mesh.set_owner(get_tree().get_edited_scene_root())
|
|
sibling_mesh.position = position
|
|
sibling_mesh.material_override = null;
|
|
|
|
|
|
func get_curve_points() -> PackedVector3Array:
|
|
var points : PackedVector3Array
|
|
for p in curve.get_point_count():
|
|
points.append(curve.get_point_position(p))
|
|
|
|
return points
|
|
|
|
|
|
func get_closest_point_to(point : Vector3) -> int:
|
|
var points = []
|
|
var closest_distance := 4096.0
|
|
var closest_index
|
|
for p in curve.get_point_count():
|
|
var dist := point.distance_to(curve.get_point_position(p))
|
|
if dist < closest_distance:
|
|
closest_distance = dist
|
|
closest_index = p
|
|
|
|
return closest_index
|
|
|
|
|
|
func get_shader_parameter(param : String):
|
|
return _material.get_shader_parameter(param)
|
|
|
|
|
|
# Parameter Setters
|
|
func set_step_length_divs(value : int) -> void:
|
|
shape_step_length_divs = value
|
|
if _first_enter_tree:
|
|
return
|
|
valid_flowmap = false
|
|
set_materials("i_valid_flowmap", valid_flowmap)
|
|
_generate_river()
|
|
emit_signal("river_changed")
|
|
|
|
|
|
func set_step_width_divs(value : int) -> void:
|
|
shape_step_width_divs = value
|
|
if _first_enter_tree:
|
|
return
|
|
valid_flowmap = false
|
|
set_materials("i_valid_flowmap", valid_flowmap)
|
|
_generate_river()
|
|
emit_signal("river_changed")
|
|
|
|
|
|
func set_smoothness(value : float) -> void:
|
|
shape_smoothness = value
|
|
if _first_enter_tree:
|
|
return
|
|
valid_flowmap = false
|
|
set_materials("i_valid_flowmap", valid_flowmap)
|
|
_generate_river()
|
|
emit_signal("river_changed")
|
|
|
|
|
|
func set_shader_type(type: int):
|
|
if type == mat_shader_type:
|
|
return
|
|
mat_shader_type = type
|
|
|
|
if mat_shader_type == SHADER_TYPES.CUSTOM:
|
|
_material.shader = mat_custom_shader
|
|
else:
|
|
_material.shader = load(BUILTIN_SHADERS[mat_shader_type].shader_path)
|
|
for texture in BUILTIN_SHADERS[mat_shader_type].texture_paths:
|
|
_material.set_shader_parameter(texture.name, load(texture.path) as Texture)
|
|
|
|
notify_property_list_changed()
|
|
|
|
|
|
func set_custom_shader(shader : Shader) -> void:
|
|
if mat_custom_shader == shader:
|
|
return
|
|
mat_custom_shader = shader
|
|
if mat_custom_shader != null:
|
|
_material.shader = mat_custom_shader
|
|
|
|
if Engine.is_editor_hint:
|
|
# Ability to fork default shader
|
|
if shader.code == "":
|
|
var selected_shader = load(BUILTIN_SHADERS[mat_shader_type].shader_path) as Shader
|
|
shader.code = selected_shader.code
|
|
|
|
if shader != null:
|
|
print("shader != null - set shader type to custom")
|
|
print(shader)
|
|
set_shader_type(SHADER_TYPES.CUSTOM)
|
|
else:
|
|
set_shader_type(SHADER_TYPES.WATER)
|
|
|
|
|
|
func set_lod0_distance(value : float) -> void:
|
|
lod_lod0_distance = value
|
|
set_materials("i_lod0_distance", value)
|
|
|
|
|
|
# Private Methods
|
|
func _generate_river() -> void:
|
|
var average_width := WaterHelperMethods.sum_array(widths) / float(widths.size() / 2)
|
|
_steps = int( max(1.0, round(curve.get_baked_length() / average_width)) )
|
|
|
|
var river_width_values := WaterHelperMethods.generate_river_width_values(curve, _steps, shape_step_length_divs, shape_step_width_divs, widths)
|
|
mesh_instance.mesh = WaterHelperMethods.generate_river_mesh(curve, _steps, shape_step_length_divs, shape_step_width_divs, shape_smoothness, river_width_values)
|
|
mesh_instance.mesh.surface_set_material(0, _material)
|
|
|
|
|
|
func _generate_flowmap(flowmap_resolution : float) -> void:
|
|
WaterHelperMethods.reset_all_colliders(get_tree().root)
|
|
|
|
var image := Image.create(flowmap_resolution, flowmap_resolution, true, Image.FORMAT_RGB8)
|
|
image.fill(Color(0.0, 0.0, 0.0))
|
|
|
|
emit_signal("progress_notified", 0.0, "Calculating Collisions (" + str(flowmap_resolution) + "x" + str(flowmap_resolution) + ")")
|
|
await get_tree().process_frame
|
|
|
|
image = await WaterHelperMethods.generate_collisionmap(image, mesh_instance, baking_raycast_distance, baking_raycast_layers, _steps, shape_step_length_divs, shape_step_width_divs, self)
|
|
|
|
emit_signal("progress_notified", 0.95, "Applying filters (" + str(flowmap_resolution) + "x" + str(flowmap_resolution) + ")")
|
|
await get_tree().process_frame
|
|
|
|
# Calculate how many colums are in UV2
|
|
_uv2_sides = WaterHelperMethods.calculate_side(_steps)
|
|
|
|
var margin := int(round(float(flowmap_resolution) / float(_uv2_sides)))
|
|
|
|
image = WaterHelperMethods.add_margins(image, flowmap_resolution, margin)
|
|
|
|
var collision_with_margins := ImageTexture.create_from_image(image)
|
|
|
|
# Create correctly tiling noise for A channel
|
|
var noise_texture := load(FLOW_OFFSET_NOISE_TEXTURE_PATH) as Texture2D
|
|
var noise_with_margin_size := float(_uv2_sides + 2) * (float(noise_texture.get_width()) / float(_uv2_sides))
|
|
var noise_with_tiling := Image.create(noise_with_margin_size, noise_with_margin_size, false, Image.FORMAT_RGB8)
|
|
var slice_width := float(noise_texture.get_width()) / float(_uv2_sides)
|
|
|
|
for x in _uv2_sides:
|
|
noise_with_tiling.blend_rect(noise_texture.get_image(), Rect2(0.0, 0.0, slice_width, noise_texture.get_height()), Vector2(slice_width + float(x) * slice_width, slice_width - (noise_texture.get_width() / 2.0)))
|
|
noise_with_tiling.blend_rect(noise_texture.get_image(), Rect2(0.0, 0.0, slice_width, noise_texture.get_height()), Vector2(slice_width + float(x) * slice_width, slice_width + (noise_texture.get_width() / 2.0)))
|
|
var tiled_noise := ImageTexture.new()
|
|
tiled_noise.create_from_image(noise_with_tiling)
|
|
|
|
# Create renderer
|
|
var renderer_instance = _filter_renderer.instantiate()
|
|
|
|
self.add_child(renderer_instance)
|
|
|
|
var flow_pressure_blur_amount = 0.04 / float(_uv2_sides) * flowmap_resolution
|
|
var dilate_amount = baking_dilate / float(_uv2_sides)
|
|
var flowmap_blur_amount = baking_flowmap_blur / float(_uv2_sides) * flowmap_resolution
|
|
var foam_offset_amount = baking_foam_offset / float(_uv2_sides)
|
|
var foam_blur_amount = baking_foam_blur / float(_uv2_sides) * flowmap_resolution
|
|
|
|
var flow_pressure_map = await renderer_instance.apply_flow_pressure(collision_with_margins, flowmap_resolution, _uv2_sides + 2.0)
|
|
var blurred_flow_pressure_map = await renderer_instance.apply_vertical_blur(flow_pressure_map, flow_pressure_blur_amount, flowmap_resolution + margin * 2)
|
|
var dilated_texture = await renderer_instance.apply_dilate(collision_with_margins, dilate_amount, 0.0, flowmap_resolution + margin * 2)
|
|
var normal_map = await renderer_instance.apply_normal(dilated_texture, flowmap_resolution + margin * 2)
|
|
var flow_map = await renderer_instance.apply_normal_to_flow(normal_map, flowmap_resolution + margin * 2)
|
|
var blurred_flow_map = await renderer_instance.apply_blur(flow_map, flowmap_blur_amount, flowmap_resolution + margin * 2)
|
|
var foam_map = await renderer_instance.apply_foam(dilated_texture, foam_offset_amount, baking_foam_cutoff, flowmap_resolution + margin * 2)
|
|
var blurred_foam_map = await renderer_instance.apply_blur(foam_map, foam_blur_amount, flowmap_resolution + margin * 2)
|
|
var flow_foam_noise_img = await renderer_instance.apply_combine(blurred_flow_map, blurred_flow_map, blurred_foam_map, tiled_noise)
|
|
var dist_pressure_img = await renderer_instance.apply_combine(dilated_texture, blurred_flow_pressure_map)
|
|
|
|
# Debug texture gen
|
|
# flow_pressure_map.get_image().save_png("res://test_assets/baked_pressure_map.png")
|
|
# blurred_flow_pressure_map.get_image().save_png("res://test_assets/baked_pressure_map_blurred.png")
|
|
# dilated_texture.get_image().save_png("res://test_assets/dilated_texture.png")
|
|
# normal_map.get_image().save_png("res://test_assets/normal_map.png")
|
|
# flow_map.get_image().save_png("res://test_assets/flow_map.png")
|
|
# blurred_flow_map.get_image().save_png("res://test_assets/blurred_flow_map.png")
|
|
|
|
remove_child(renderer_instance) # cleanup
|
|
|
|
var flow_foam_noise_result = flow_foam_noise_img.get_image().get_region(Rect2(margin, margin, flowmap_resolution, flowmap_resolution))
|
|
var dist_pressure_result = dist_pressure_img.get_image().get_region(Rect2(margin, margin, flowmap_resolution, flowmap_resolution))
|
|
|
|
flow_foam_noise = flow_foam_noise_img
|
|
dist_pressure = dist_pressure_img
|
|
|
|
set_materials("i_flowmap", flow_foam_noise)
|
|
set_materials("i_distmap", dist_pressure)
|
|
set_materials("i_valid_flowmap", true)
|
|
set_materials("i_uv2_sides", _uv2_sides)
|
|
valid_flowmap = true;
|
|
emit_signal("progress_notified", 100.0, "finished")
|
|
update_configuration_warnings()
|
|
|
|
|
|
# Signal Methods
|
|
func properties_changed() -> void:
|
|
emit_signal("river_changed")
|