|
| 1 | +@tool |
| 2 | +class_name GaeaNodeToHeight |
| 3 | +extends GaeaNodeResource |
| 4 | +## Transforms [param reference_data] into a new data grid where the height of each column is determined by [param height_offset] + ([param reference_data] * [param displacement_intensity]) |
| 5 | +## |
| 6 | +## For each cell in [param reference_data]'s [param reference_y] row, it'll get the [code]float[/code] value, |
| 7 | +## multiply it by [param displacement_intensity] and add [param height_offset] to it. This will be |
| 8 | +## the column's height, and every cell below that height (inclusive) will be full while every cell above |
| 9 | +## will be empty.[br][br] |
| 10 | +## This functions to create a heightmap, which can be used to create 2D side-view or |
| 11 | +## 3D terrain.[br][br] |
| 12 | +## [b]Note: Keep in mind the y axis in Godot is negative for up in 2D and down in 3D.[/b] |
| 13 | + |
| 14 | +enum Type { |
| 15 | + TYPE_2D, ## Referenced data will only take into account the x coordinate of the cell. |
| 16 | + TYPE_3D ## Referenced data will take into account both the x and the z coordinates of the cell. |
| 17 | +} |
| 18 | + |
| 19 | + |
| 20 | +func _get_title() -> String: |
| 21 | + return "ToHeight" |
| 22 | + |
| 23 | + |
| 24 | +func _get_description() -> String: |
| 25 | + var desc: String = "Transforms [param reference_data] into a new data grid where the height of each column is determined by\ |
| 26 | + [param height_offset] + ([param reference_data] * [param displacement_intensity]).\n" |
| 27 | + match get_enum_selection(0): |
| 28 | + Type.TYPE_2D: |
| 29 | + desc += "\nReferences all the x values of the [param reference_y] row." |
| 30 | + Type.TYPE_3D: |
| 31 | + desc += "\nReferences all the x,z values of the [param reference_y] row." |
| 32 | + return desc |
| 33 | + |
| 34 | + |
| 35 | +func _get_tree_items() -> Array[GaeaNodeResource]: |
| 36 | + var items: Array[GaeaNodeResource] |
| 37 | + for type in Type.values(): |
| 38 | + var item: GaeaNodeToHeight = get_script().new() |
| 39 | + item.set_tree_name_override(_get_title() + _get_enum_option_display_name(0, type)) |
| 40 | + item.set_default_enum_value_override(0, type) |
| 41 | + items.append(item) |
| 42 | + |
| 43 | + return items |
| 44 | + |
| 45 | + |
| 46 | +func _get_enums_count() -> int: |
| 47 | + return 1 |
| 48 | + |
| 49 | + |
| 50 | +func _get_enum_options(_enum_idx: int) -> Dictionary: |
| 51 | + return Type |
| 52 | + |
| 53 | + |
| 54 | +func _get_enum_option_display_name(_enum_idx: int, option_value: int) -> String: |
| 55 | + return Type.find_key(option_value).trim_prefix("TYPE_") |
| 56 | + |
| 57 | + |
| 58 | + |
| 59 | +func _get_arguments_list() -> Array[StringName]: |
| 60 | + return [&"reference_data", &"reference_y", |
| 61 | + &"height_offset", &"displacement_intensity", |
| 62 | + &"gradient_intensity"] |
| 63 | + |
| 64 | + |
| 65 | +func _get_argument_type(arg_name: StringName) -> GaeaValue.Type: |
| 66 | + match arg_name: |
| 67 | + &"reference_data": return GaeaValue.Type.DATA |
| 68 | + &"gradient_intensity": return GaeaValue.Type.FLOAT |
| 69 | + _: return GaeaValue.Type.INT |
| 70 | + |
| 71 | + |
| 72 | +func _get_argument_default_value(arg_name: StringName) -> Variant: |
| 73 | + match arg_name: |
| 74 | + &"displacement_intensity": return 16 |
| 75 | + &"gradient_intensity": return 1.0 |
| 76 | + return super(arg_name) |
| 77 | + |
| 78 | + |
| 79 | + |
| 80 | +func _get_output_ports_list() -> Array[StringName]: |
| 81 | + return [&"data"] |
| 82 | + |
| 83 | + |
| 84 | +func _get_output_port_type(_output_name: StringName) -> GaeaValue.Type: |
| 85 | + return GaeaValue.Type.DATA |
| 86 | + |
| 87 | + |
| 88 | +func _get_data(_output_port: StringName, area: AABB, graph: GaeaGraph) -> Dictionary: |
| 89 | + var reference_data: Dictionary = _get_arg(&"reference_data", area, graph) |
| 90 | + var row: int = _get_arg(&"reference_y", area, graph) |
| 91 | + var height_offset: int = _get_arg(&"height_offset", area, graph) |
| 92 | + var displacement: int = _get_arg(&"displacement_intensity", area, graph) |
| 93 | + var gradient_intensity: float = _get_arg(&"gradient_intensity", area, graph) |
| 94 | + var data: Dictionary[Vector3i, float] = {} |
| 95 | + var type: Type = get_enum_selection(0) as Type |
| 96 | + |
| 97 | + var remap_offset: float = 0.0 |
| 98 | + if not is_zero_approx(gradient_intensity): |
| 99 | + remap_offset = 100.0 / gradient_intensity |
| 100 | + |
| 101 | + var z_range: Array = [0] if (type == Type.TYPE_2D) else (_get_axis_range(Vector3i.AXIS_Z, area)) |
| 102 | + for x in _get_axis_range(Vector3i.AXIS_X, area): |
| 103 | + if not reference_data.has(Vector3i(x, row, 0)): |
| 104 | + continue |
| 105 | + for z in z_range: |
| 106 | + var height: int = floor(reference_data[Vector3i(x, row, z)] * displacement + height_offset) |
| 107 | + for y in _get_axis_range(Vector3i.AXIS_Y, area): |
| 108 | + if y >= -height and type == Type.TYPE_2D: |
| 109 | + data[Vector3i(x, y, z)] = 1.0 if is_zero_approx(remap_offset) else remap( |
| 110 | + y, -height + remap_offset, -height, 0, 1.0 |
| 111 | + ) |
| 112 | + elif y <= height and type == Type.TYPE_3D: |
| 113 | + data[Vector3i(x, y, z)] = 1.0 if is_zero_approx(remap_offset) else remap( |
| 114 | + y, height, height - remap_offset, 1.0, 0.0 |
| 115 | + ) |
| 116 | + return data |
0 commit comments