mirror of
https://gitlab.com/open-fpsz/open-fpsz.git
synced 2026-01-19 19:44:46 +00:00
👽 interstellar delivery
This commit is contained in:
parent
547c97bfba
commit
97c8292858
2
.gitignore
vendored
2
.gitignore
vendored
|
|
@ -19,4 +19,4 @@ addons/godot-jolt/windows/~godot-jolt_windows-x64_editor.dll
|
||||||
addons/terrain_3d/bin/~libterrain.windows.debug.x86_64.dll
|
addons/terrain_3d/bin/~libterrain.windows.debug.x86_64.dll
|
||||||
|
|
||||||
build
|
build
|
||||||
|
dist
|
||||||
|
|
|
||||||
|
|
@ -8,10 +8,9 @@ Download `Godot Engine` at https://godotengine.org/download
|
||||||
|
|
||||||
## Getting started
|
## Getting started
|
||||||
|
|
||||||
1. Clone the repository
|
1. Clone the repository and open `Godot Engine`
|
||||||
2. Open Godot Engine
|
2. Navigate to the repository and import the `project.godot`
|
||||||
3. Navigate to the repository and import the `project.godot`
|
3. Start coding or run the project!
|
||||||
4. Start coding or run the project!
|
|
||||||
|
|
||||||
## Contributing
|
## Contributing
|
||||||
|
|
||||||
|
|
@ -21,7 +20,7 @@ We welcome contributions from the community, feel free to submit pull requests,
|
||||||
|
|
||||||
## Community
|
## Community
|
||||||
|
|
||||||
Connect with fellow contributors, ask questions and stay updated on the latest developments by joining our [Discord](https://discord.gg/tdmV3MxCn5).
|
Connect, ask questions and stay updated on the latest developments by joining our [Discord](https://discord.gg/tdmV3MxCn5).
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@ See [Project Status](https://terrain3d.readthedocs.io/en/stable/docs/project_sta
|
||||||
|
|
||||||
## Getting Started
|
## Getting Started
|
||||||
|
|
||||||
1. Read through our [documentation](https://terrain3d.readthedocs.io/en/stable/index.html), starting with [Installation](https://terrain3d.readthedocs.io/en/stable/docs/installation.html).
|
1. Read the [Installation](https://terrain3d.readthedocs.io/en/stable/docs/installation.html) instructions, and the rest of the [documentation](https://terrain3d.readthedocs.io/en/stable/index.html).
|
||||||
|
|
||||||
2. For support, read [Getting Help](https://terrain3d.readthedocs.io/en/stable/docs/getting_help.html) or join our [Discord server](https://tokisan.com/discord).
|
2. For support, read [Getting Help](https://terrain3d.readthedocs.io/en/stable/docs/getting_help.html) or join our [Discord server](https://tokisan.com/discord).
|
||||||
|
|
||||||
|
|
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
|
@ -4,23 +4,36 @@ extends EditorPlugin
|
||||||
|
|
||||||
|
|
||||||
# Includes
|
# Includes
|
||||||
const UI: Script = preload("res://addons/terrain_3d/editor/components/ui.gd")
|
const UI: Script = preload("res://addons/terrain_3d/src/ui.gd")
|
||||||
const RegionGizmo: Script = preload("res://addons/terrain_3d/editor/components/region_gizmo.gd")
|
const RegionGizmo: Script = preload("res://addons/terrain_3d/src/region_gizmo.gd")
|
||||||
const TextureDock: Script = preload("res://addons/terrain_3d/editor/components/texture_dock.gd")
|
const ASSET_DOCK: String = "res://addons/terrain_3d/src/asset_dock.tscn"
|
||||||
|
const PS_DOCK_POSITION: String = "terrain3d/config/dock_position"
|
||||||
|
const PS_DOCK_PINNED: String = "terrain3d/config/dock_pinned"
|
||||||
|
|
||||||
var terrain: Terrain3D
|
var terrain: Terrain3D
|
||||||
var nav_region: NavigationRegion3D
|
var nav_region: NavigationRegion3D
|
||||||
|
|
||||||
var editor: Terrain3DEditor
|
var editor: Terrain3DEditor
|
||||||
var ui: Node # Terrain3DUI see Godot #75388
|
var ui: Node # Terrain3DUI see Godot #75388
|
||||||
var texture_dock: TextureDock
|
|
||||||
var texture_dock_container: CustomControlContainer = CONTAINER_INSPECTOR_BOTTOM
|
|
||||||
|
|
||||||
var visible: bool
|
|
||||||
var region_gizmo: RegionGizmo
|
var region_gizmo: RegionGizmo
|
||||||
|
var visible: bool
|
||||||
var current_region_position: Vector2
|
var current_region_position: Vector2
|
||||||
var mouse_global_position: Vector3 = Vector3.ZERO
|
var mouse_global_position: Vector3 = Vector3.ZERO
|
||||||
|
|
||||||
|
enum DOCK_STATE {
|
||||||
|
HIDDEN = -1,
|
||||||
|
SIDEBAR = 0,
|
||||||
|
BOTTOM = 1,
|
||||||
|
}
|
||||||
|
var asset_dock: Control
|
||||||
|
var dock_state: DOCK_STATE = -1
|
||||||
|
var dock_position: DockSlot = DOCK_SLOT_RIGHT_BL
|
||||||
|
|
||||||
|
# Track negative input (CTRL)
|
||||||
|
var _negative_input: bool = false
|
||||||
|
# Track state prior to pressing CTRL: -1 not tracked, 0 false, 1 true
|
||||||
|
var _prev_enable_state: int = -1
|
||||||
|
|
||||||
|
|
||||||
func _enter_tree() -> void:
|
func _enter_tree() -> void:
|
||||||
editor = Terrain3DEditor.new()
|
editor = Terrain3DEditor.new()
|
||||||
|
|
@ -28,27 +41,38 @@ func _enter_tree() -> void:
|
||||||
ui.plugin = self
|
ui.plugin = self
|
||||||
add_child(ui)
|
add_child(ui)
|
||||||
|
|
||||||
texture_dock = TextureDock.new()
|
|
||||||
texture_dock.hide()
|
|
||||||
texture_dock.resource_changed.connect(_on_texture_dock_resource_changed)
|
|
||||||
texture_dock.resource_inspected.connect(_on_texture_dock_resource_selected)
|
|
||||||
texture_dock.resource_selected.connect(ui._on_setting_changed)
|
|
||||||
|
|
||||||
region_gizmo = RegionGizmo.new()
|
region_gizmo = RegionGizmo.new()
|
||||||
|
|
||||||
add_control_to_container(texture_dock_container, texture_dock)
|
|
||||||
texture_dock.get_parent().visibility_changed.connect(_on_texture_dock_visibility_changed)
|
|
||||||
|
|
||||||
|
scene_changed.connect(_on_scene_changed)
|
||||||
|
|
||||||
|
if ProjectSettings.has_setting(PS_DOCK_POSITION):
|
||||||
|
dock_position = ProjectSettings.get_setting(PS_DOCK_POSITION)
|
||||||
|
asset_dock = load(ASSET_DOCK).instantiate()
|
||||||
|
await asset_dock.ready
|
||||||
|
if ProjectSettings.has_setting(PS_DOCK_PINNED):
|
||||||
|
asset_dock.placement_pin.button_pressed = ProjectSettings.get_setting(PS_DOCK_PINNED)
|
||||||
|
asset_dock.placement_pin.toggled.connect(_on_asset_dock_pin_changed)
|
||||||
|
asset_dock.placement_option.selected = dock_position
|
||||||
|
asset_dock.placement_changed.connect(_on_asset_dock_placement_changed)
|
||||||
|
asset_dock.resource_changed.connect(_on_asset_dock_resource_changed)
|
||||||
|
asset_dock.resource_inspected.connect(_on_asset_dock_resource_inspected)
|
||||||
|
asset_dock.resource_selected.connect(_on_asset_dock_resource_selected)
|
||||||
|
|
||||||
|
|
||||||
func _exit_tree() -> void:
|
func _exit_tree() -> void:
|
||||||
remove_control_from_container(texture_dock_container, texture_dock)
|
asset_dock.queue_free()
|
||||||
texture_dock.queue_free()
|
|
||||||
ui.queue_free()
|
ui.queue_free()
|
||||||
editor.free()
|
editor.free()
|
||||||
|
|
||||||
|
scene_changed.disconnect(_on_scene_changed)
|
||||||
|
|
||||||
|
|
||||||
func _handles(p_object: Object) -> bool:
|
func _handles(p_object: Object) -> bool:
|
||||||
return p_object is Terrain3D or p_object is NavigationRegion3D
|
if p_object is Terrain3D or p_object is NavigationRegion3D:
|
||||||
|
return true
|
||||||
|
if p_object is Terrain3DObjects or (p_object is Node3D and p_object.get_parent() is Terrain3DObjects):
|
||||||
|
return true
|
||||||
|
return false
|
||||||
|
|
||||||
|
|
||||||
func _edit(p_object: Object) -> void:
|
func _edit(p_object: Object) -> void:
|
||||||
|
|
@ -77,21 +101,37 @@ func _edit(p_object: Object) -> void:
|
||||||
nav_region = p_object
|
nav_region = p_object
|
||||||
else:
|
else:
|
||||||
nav_region = null
|
nav_region = null
|
||||||
|
|
||||||
|
if p_object is Terrain3DObjects:
|
||||||
|
p_object.editor_setup(self)
|
||||||
|
elif p_object is Node3D and p_object.get_parent() is Terrain3DObjects:
|
||||||
|
p_object.get_parent().editor_setup(self)
|
||||||
|
|
||||||
_update_visibility()
|
|
||||||
|
|
||||||
|
|
||||||
func _make_visible(p_visible: bool) -> void:
|
func _make_visible(p_visible: bool, p_redraw: bool = false) -> void:
|
||||||
visible = p_visible
|
visible = p_visible
|
||||||
_update_visibility()
|
|
||||||
|
|
||||||
|
|
||||||
func _update_visibility() -> void:
|
|
||||||
ui.set_visible(visible)
|
ui.set_visible(visible)
|
||||||
texture_dock.set_visible(visible and terrain)
|
update_region_grid()
|
||||||
if terrain:
|
|
||||||
update_region_grid()
|
# Manage Asset Dock position and visibility
|
||||||
region_gizmo.set_hidden(not visible or not terrain)
|
if visible and dock_state == DOCK_STATE.HIDDEN:
|
||||||
|
if dock_position < DOCK_SLOT_MAX:
|
||||||
|
add_control_to_dock(dock_position, asset_dock)
|
||||||
|
dock_state = DOCK_STATE.SIDEBAR
|
||||||
|
asset_dock.move_slider(true)
|
||||||
|
else:
|
||||||
|
add_control_to_bottom_panel(asset_dock, "Terrain3D")
|
||||||
|
make_bottom_panel_item_visible(asset_dock)
|
||||||
|
dock_state = DOCK_STATE.BOTTOM
|
||||||
|
asset_dock.move_slider(false)
|
||||||
|
elif not visible and dock_state != DOCK_STATE.HIDDEN:
|
||||||
|
var pinned: bool = false
|
||||||
|
if p_redraw or ( asset_dock.placement_pin and not asset_dock.placement_pin.button_pressed):
|
||||||
|
if dock_state == DOCK_STATE.SIDEBAR:
|
||||||
|
remove_control_from_docks(asset_dock)
|
||||||
|
else:
|
||||||
|
remove_control_from_bottom_panel(asset_dock)
|
||||||
|
dock_state = DOCK_STATE.HIDDEN
|
||||||
|
|
||||||
|
|
||||||
func _clear() -> void:
|
func _clear() -> void:
|
||||||
|
|
@ -111,21 +151,39 @@ func _forward_3d_gui_input(p_viewport_camera: Camera3D, p_event: InputEvent) ->
|
||||||
if not is_terrain_valid():
|
if not is_terrain_valid():
|
||||||
return AFTER_GUI_INPUT_PASS
|
return AFTER_GUI_INPUT_PASS
|
||||||
|
|
||||||
|
## Track negative input (CTRL)
|
||||||
|
if p_event is InputEventKey and not p_event.echo and p_event.keycode == KEY_CTRL:
|
||||||
|
if p_event.is_pressed():
|
||||||
|
_negative_input = true
|
||||||
|
_prev_enable_state = int(ui.toolbar_settings.get_setting("enable"))
|
||||||
|
ui.toolbar_settings.set_setting("enable", false)
|
||||||
|
else:
|
||||||
|
_negative_input = false
|
||||||
|
ui.toolbar_settings.set_setting("enable", bool(_prev_enable_state))
|
||||||
|
_prev_enable_state = -1
|
||||||
|
|
||||||
## Handle mouse movement
|
## Handle mouse movement
|
||||||
if p_event is InputEventMouseMotion:
|
if p_event is InputEventMouseMotion:
|
||||||
if Input.is_mouse_button_pressed(MOUSE_BUTTON_RIGHT):
|
if Input.is_mouse_button_pressed(MOUSE_BUTTON_RIGHT):
|
||||||
return AFTER_GUI_INPUT_PASS
|
return AFTER_GUI_INPUT_PASS
|
||||||
|
|
||||||
## Get mouse location on terrain
|
if _prev_enable_state >= 0 and not Input.is_key_pressed(KEY_CTRL):
|
||||||
|
_negative_input = false
|
||||||
|
ui.toolbar_settings.set_setting("enable", bool(_prev_enable_state))
|
||||||
|
_prev_enable_state = -1
|
||||||
|
|
||||||
|
## Setup for active camera & viewport
|
||||||
|
|
||||||
# Snap terrain to current camera
|
# Snap terrain to current camera
|
||||||
terrain.set_camera(p_viewport_camera)
|
terrain.set_camera(p_viewport_camera)
|
||||||
|
|
||||||
# Detect if viewport is set to half_resolution
|
# Detect if viewport is set to half_resolution
|
||||||
# Structure is: Node3DEditorViewportContainer/Node3DEditorViewport/SubViewportContainer/SubViewport/Camera3D
|
# Structure is: Node3DEditorViewportContainer/Node3DEditorViewport(4)/SubViewportContainer/SubViewport/Camera3D
|
||||||
var editor_vpc: SubViewportContainer = p_viewport_camera.get_parent().get_parent()
|
var editor_vpc: SubViewportContainer = p_viewport_camera.get_parent().get_parent()
|
||||||
var full_resolution: bool = false if editor_vpc.stretch_shrink == 2 else true
|
var full_resolution: bool = false if editor_vpc.stretch_shrink == 2 else true
|
||||||
|
|
||||||
|
## Get mouse location on terrain
|
||||||
|
|
||||||
# Project 2D mouse position to 3D position and direction
|
# Project 2D mouse position to 3D position and direction
|
||||||
var mouse_pos: Vector2 = p_event.position if full_resolution else p_event.position/2
|
var mouse_pos: Vector2 = p_event.position if full_resolution else p_event.position/2
|
||||||
var camera_pos: Vector3 = p_viewport_camera.project_ray_origin(mouse_pos)
|
var camera_pos: Vector3 = p_viewport_camera.project_ray_origin(mouse_pos)
|
||||||
|
|
@ -204,29 +262,22 @@ func _forward_3d_gui_input(p_viewport_camera: Camera3D, p_event: InputEvent) ->
|
||||||
|
|
||||||
|
|
||||||
func is_terrain_valid() -> bool:
|
func is_terrain_valid() -> bool:
|
||||||
var valid: bool = false
|
if is_instance_valid(terrain) and terrain.get_storage():
|
||||||
if is_instance_valid(terrain):
|
return true
|
||||||
valid = terrain.get_storage() != null
|
return false
|
||||||
return valid
|
|
||||||
|
|
||||||
|
|
||||||
func update_texture_dock(p_args: Array) -> void:
|
|
||||||
texture_dock.clear()
|
|
||||||
|
|
||||||
if is_terrain_valid() and terrain.texture_list:
|
|
||||||
var texture_count: int = terrain.texture_list.get_texture_count()
|
|
||||||
for i in texture_count:
|
|
||||||
var texture: Terrain3DTexture = terrain.texture_list.get_texture(i)
|
|
||||||
texture_dock.add_item(texture)
|
|
||||||
|
|
||||||
if texture_count < Terrain3DTextureList.MAX_TEXTURES:
|
|
||||||
texture_dock.add_item()
|
|
||||||
|
|
||||||
|
|
||||||
|
func _load_storage() -> void:
|
||||||
|
if terrain:
|
||||||
|
update_region_grid()
|
||||||
|
|
||||||
|
|
||||||
func update_region_grid() -> void:
|
func update_region_grid() -> void:
|
||||||
if !region_gizmo.get_node_3d():
|
if not region_gizmo:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
region_gizmo.set_hidden(not visible)
|
||||||
|
|
||||||
if is_terrain_valid():
|
if is_terrain_valid():
|
||||||
region_gizmo.show_rect = editor.get_tool() == Terrain3DEditor.REGION
|
region_gizmo.show_rect = editor.get_tool() == Terrain3DEditor.REGION
|
||||||
region_gizmo.use_secondary_color = editor.get_operation() == Terrain3DEditor.SUBTRACT
|
region_gizmo.use_secondary_color = editor.get_operation() == Terrain3DEditor.SUBTRACT
|
||||||
|
|
@ -242,41 +293,63 @@ func update_region_grid() -> void:
|
||||||
region_gizmo.grid = [Vector2i.ZERO]
|
region_gizmo.grid = [Vector2i.ZERO]
|
||||||
|
|
||||||
|
|
||||||
# Signal handlers
|
|
||||||
|
|
||||||
|
|
||||||
func _load_textures() -> void:
|
func _load_textures() -> void:
|
||||||
if terrain and terrain.texture_list:
|
if terrain and terrain.texture_list:
|
||||||
if not terrain.texture_list.textures_changed.is_connected(update_texture_dock):
|
if not terrain.texture_list.textures_changed.is_connected(update_asset_dock):
|
||||||
terrain.texture_list.textures_changed.connect(update_texture_dock)
|
terrain.texture_list.textures_changed.connect(update_asset_dock)
|
||||||
update_texture_dock(Array())
|
update_asset_dock()
|
||||||
|
|
||||||
|
|
||||||
func _load_storage() -> void:
|
func update_asset_dock(p_texture_list: Terrain3DTextureList = null) -> void:
|
||||||
if terrain:
|
asset_dock.clear()
|
||||||
update_region_grid()
|
|
||||||
|
if is_terrain_valid() and terrain.texture_list:
|
||||||
|
var texture_count: int = terrain.texture_list.get_texture_count()
|
||||||
|
for i in texture_count:
|
||||||
|
var texture: Terrain3DTexture = terrain.texture_list.get_texture(i)
|
||||||
|
asset_dock.add_item(texture)
|
||||||
|
|
||||||
|
if texture_count < Terrain3DTextureList.MAX_TEXTURES:
|
||||||
|
asset_dock.add_item()
|
||||||
|
|
||||||
|
|
||||||
func _on_texture_dock_resource_changed(texture: Resource, index: int) -> void:
|
func _on_asset_dock_pin_changed(toggled: bool) -> void:
|
||||||
|
ProjectSettings.set_setting(PS_DOCK_PINNED, toggled)
|
||||||
|
ProjectSettings.save()
|
||||||
|
|
||||||
|
|
||||||
|
func _on_asset_dock_placement_changed(index: int) -> void:
|
||||||
|
dock_position = clamp(index, 0, DOCK_SLOT_MAX)
|
||||||
|
ProjectSettings.set_setting(PS_DOCK_POSITION, dock_position)
|
||||||
|
ProjectSettings.save()
|
||||||
|
_make_visible(false, true) # Hide to redraw
|
||||||
|
_make_visible(true)
|
||||||
|
|
||||||
|
|
||||||
|
func _on_asset_dock_resource_changed(p_texture: Resource, p_index: int) -> void:
|
||||||
if is_terrain_valid():
|
if is_terrain_valid():
|
||||||
# If removing last entry and its selected, clear inspector
|
# If removing last entry and its selected, clear inspector
|
||||||
if not texture and index == texture_dock.get_selected_index() and \
|
if not p_texture and p_index == asset_dock.get_selected_index() and \
|
||||||
texture_dock.get_selected_index() == texture_dock.entries.size() - 2:
|
asset_dock.get_selected_index() == asset_dock.entries.size() - 2:
|
||||||
get_editor_interface().inspect_object(null)
|
get_editor_interface().inspect_object(null)
|
||||||
terrain.get_texture_list().set_texture(index, texture)
|
terrain.get_texture_list().set_texture(p_index, p_texture)
|
||||||
call_deferred("_load_storage")
|
|
||||||
|
|
||||||
|
|
||||||
func _on_texture_dock_resource_selected(texture) -> void:
|
func _on_asset_dock_resource_selected() -> void:
|
||||||
|
# If not on a texture painting tool, then switch to Paint
|
||||||
|
if editor.get_tool() != Terrain3DEditor.TEXTURE:
|
||||||
|
var paint_btn: Button = ui.toolbar.get_node_or_null("PaintBaseTexture")
|
||||||
|
if paint_btn:
|
||||||
|
paint_btn.set_pressed(true)
|
||||||
|
ui._on_tool_changed(Terrain3DEditor.TEXTURE, Terrain3DEditor.REPLACE)
|
||||||
|
ui._on_setting_changed()
|
||||||
|
|
||||||
|
|
||||||
|
func _on_asset_dock_resource_inspected(texture: Resource) -> void:
|
||||||
get_editor_interface().inspect_object(texture, "", true)
|
get_editor_interface().inspect_object(texture, "", true)
|
||||||
|
|
||||||
|
|
||||||
func _on_texture_dock_visibility_changed() -> void:
|
func _on_scene_changed(scene_root: Node) -> void:
|
||||||
if texture_dock.get_parent() != null:
|
if scene_root:
|
||||||
remove_control_from_container(texture_dock_container, texture_dock)
|
for node in scene_root.find_children("", "Terrain3DObjects"):
|
||||||
|
node.editor_setup(self)
|
||||||
if texture_dock.get_parent() == null:
|
|
||||||
texture_dock_container = CONTAINER_INSPECTOR_BOTTOM
|
|
||||||
if get_editor_interface().is_distraction_free_mode_enabled():
|
|
||||||
texture_dock_container = CONTAINER_SPATIAL_EDITOR_SIDE_RIGHT
|
|
||||||
add_control_to_container(texture_dock_container, texture_dock)
|
|
||||||
|
|
@ -1,460 +0,0 @@
|
||||||
extends PanelContainer
|
|
||||||
|
|
||||||
signal picking(type, callback)
|
|
||||||
signal setting_changed
|
|
||||||
|
|
||||||
enum Layout {
|
|
||||||
HORIZONTAL,
|
|
||||||
VERTICAL,
|
|
||||||
GRID,
|
|
||||||
}
|
|
||||||
|
|
||||||
enum SettingType {
|
|
||||||
CHECKBOX,
|
|
||||||
SLIDER,
|
|
||||||
DOUBLE_SLIDER,
|
|
||||||
COLOR_SELECT,
|
|
||||||
PICKER,
|
|
||||||
POINT_PICKER,
|
|
||||||
}
|
|
||||||
|
|
||||||
const PointPicker: Script = preload("res://addons/terrain_3d/editor/components/point_picker.gd")
|
|
||||||
const DEFAULT_BRUSH: String = "circle0.exr"
|
|
||||||
const BRUSH_PATH: String = "res://addons/terrain_3d/editor/brushes"
|
|
||||||
const PICKER_ICON: String = "res://addons/terrain_3d/icons/icon_picker.svg"
|
|
||||||
|
|
||||||
const NONE: int = 0x0
|
|
||||||
const ALLOW_LARGER: int = 0x1
|
|
||||||
const ALLOW_SMALLER: int = 0x2
|
|
||||||
const ALLOW_OUT_OF_BOUNDS: int = 0x3
|
|
||||||
|
|
||||||
var brush_preview_material: ShaderMaterial
|
|
||||||
|
|
||||||
var list: HBoxContainer
|
|
||||||
var advanced_list: VBoxContainer
|
|
||||||
var settings: Dictionary = {}
|
|
||||||
|
|
||||||
|
|
||||||
func _ready() -> void:
|
|
||||||
list = HBoxContainer.new()
|
|
||||||
add_child(list, true)
|
|
||||||
|
|
||||||
add_brushes(list)
|
|
||||||
|
|
||||||
add_setting(SettingType.SLIDER, "size", 50, list, "m", 4, 200, 1, ALLOW_LARGER)
|
|
||||||
add_setting(SettingType.SLIDER, "opacity", 10, list, "%", 1, 100)
|
|
||||||
add_setting(SettingType.CHECKBOX, "enable", true, list)
|
|
||||||
|
|
||||||
add_setting(SettingType.COLOR_SELECT, "color", Color.WHITE, list)
|
|
||||||
add_setting(SettingType.PICKER, "color picker", Terrain3DEditor.COLOR, list)
|
|
||||||
|
|
||||||
add_setting(SettingType.SLIDER, "roughness", 0, list, "%", -100, 100, 1)
|
|
||||||
add_setting(SettingType.PICKER, "roughness picker", Terrain3DEditor.ROUGHNESS, list)
|
|
||||||
|
|
||||||
add_setting(SettingType.SLIDER, "height", 50, list, "m", -500, 500, 0.1, ALLOW_OUT_OF_BOUNDS)
|
|
||||||
add_setting(SettingType.PICKER, "height picker", Terrain3DEditor.HEIGHT, list)
|
|
||||||
add_setting(SettingType.DOUBLE_SLIDER, "slope", 0, list, "°", 0, 180, 1)
|
|
||||||
|
|
||||||
add_setting(SettingType.POINT_PICKER, "gradient_points", Terrain3DEditor.HEIGHT, list)
|
|
||||||
add_setting(SettingType.CHECKBOX, "drawable", false, list)
|
|
||||||
|
|
||||||
settings["drawable"].toggled.connect(_on_drawable_toggled)
|
|
||||||
|
|
||||||
var spacer: Control = Control.new()
|
|
||||||
spacer.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
|
||||||
list.add_child(spacer, true)
|
|
||||||
|
|
||||||
## Advanced Settings Menu
|
|
||||||
advanced_list = create_submenu(list, "Advanced", Layout.VERTICAL)
|
|
||||||
add_setting(SettingType.CHECKBOX, "automatic_regions", true, advanced_list)
|
|
||||||
add_setting(SettingType.CHECKBOX, "align_to_view", true, advanced_list)
|
|
||||||
add_setting(SettingType.CHECKBOX, "show_cursor_while_painting", true, advanced_list)
|
|
||||||
advanced_list.add_child(HSeparator.new(), true)
|
|
||||||
add_setting(SettingType.SLIDER, "gamma", 1.0, advanced_list, "γ", 0.1, 2.0, 0.01)
|
|
||||||
add_setting(SettingType.SLIDER, "jitter", 50, advanced_list, "%", 0, 100)
|
|
||||||
|
|
||||||
|
|
||||||
func create_submenu(p_parent: Control, p_button_name: String, p_layout: Layout) -> Container:
|
|
||||||
var menu_button: Button = Button.new()
|
|
||||||
menu_button.set_text(p_button_name)
|
|
||||||
menu_button.set_toggle_mode(true)
|
|
||||||
menu_button.set_v_size_flags(SIZE_SHRINK_CENTER)
|
|
||||||
menu_button.connect("toggled", _on_show_submenu.bind(menu_button))
|
|
||||||
|
|
||||||
var submenu: PopupPanel = PopupPanel.new()
|
|
||||||
submenu.connect("popup_hide", menu_button.set_pressed_no_signal.bind(false))
|
|
||||||
submenu.set("theme_override_styles/panel", get_theme_stylebox("panel", "PopupMenu"))
|
|
||||||
|
|
||||||
var sublist: Container
|
|
||||||
match(p_layout):
|
|
||||||
Layout.GRID:
|
|
||||||
sublist = GridContainer.new()
|
|
||||||
Layout.VERTICAL:
|
|
||||||
sublist = VBoxContainer.new()
|
|
||||||
Layout.HORIZONTAL, _:
|
|
||||||
sublist = HBoxContainer.new()
|
|
||||||
|
|
||||||
p_parent.add_child(menu_button, true)
|
|
||||||
menu_button.add_child(submenu, true)
|
|
||||||
submenu.add_child(sublist, true)
|
|
||||||
|
|
||||||
return sublist
|
|
||||||
|
|
||||||
|
|
||||||
func _on_show_submenu(p_toggled: bool, p_button: Button) -> void:
|
|
||||||
var popup: PopupPanel = p_button.get_child(0)
|
|
||||||
var popup_pos: Vector2 = p_button.get_screen_transform().origin
|
|
||||||
popup.set_visible(p_toggled)
|
|
||||||
popup_pos.y -= popup.get_size().y
|
|
||||||
popup.set_position(popup_pos)
|
|
||||||
|
|
||||||
|
|
||||||
func add_brushes(p_parent: Control) -> void:
|
|
||||||
var brush_list: GridContainer = create_submenu(p_parent, "Brush", Layout.GRID)
|
|
||||||
brush_list.name = "BrushList"
|
|
||||||
|
|
||||||
var brush_button_group: ButtonGroup = ButtonGroup.new()
|
|
||||||
brush_button_group.connect("pressed", _on_setting_changed)
|
|
||||||
var default_brush_btn: Button
|
|
||||||
|
|
||||||
var dir: DirAccess = DirAccess.open(BRUSH_PATH)
|
|
||||||
if dir:
|
|
||||||
dir.list_dir_begin()
|
|
||||||
var file_name = dir.get_next()
|
|
||||||
while file_name != "":
|
|
||||||
if !dir.current_is_dir() and file_name.ends_with(".exr"):
|
|
||||||
var img: Image = Image.load_from_file(BRUSH_PATH + "/" + file_name)
|
|
||||||
_black_to_alpha(img)
|
|
||||||
var tex: ImageTexture = ImageTexture.create_from_image(img)
|
|
||||||
|
|
||||||
var btn: Button = Button.new()
|
|
||||||
btn.set_custom_minimum_size(Vector2.ONE * 100)
|
|
||||||
btn.set_button_icon(tex)
|
|
||||||
btn.set_meta("image", img)
|
|
||||||
btn.set_expand_icon(true)
|
|
||||||
btn.set_material(_get_brush_preview_material())
|
|
||||||
btn.set_toggle_mode(true)
|
|
||||||
btn.set_button_group(brush_button_group)
|
|
||||||
btn.mouse_entered.connect(_on_brush_hover.bind(true, btn))
|
|
||||||
btn.mouse_exited.connect(_on_brush_hover.bind(false, btn))
|
|
||||||
brush_list.add_child(btn, true)
|
|
||||||
if file_name == DEFAULT_BRUSH:
|
|
||||||
default_brush_btn = btn
|
|
||||||
|
|
||||||
var lbl: Label = Label.new()
|
|
||||||
btn.add_child(lbl, true)
|
|
||||||
lbl.text = file_name.get_basename()
|
|
||||||
lbl.visible = false
|
|
||||||
lbl.position.y = 70
|
|
||||||
lbl.add_theme_color_override("font_shadow_color", Color.BLACK)
|
|
||||||
lbl.add_theme_constant_override("shadow_offset_x", 1)
|
|
||||||
lbl.add_theme_constant_override("shadow_offset_y", 1)
|
|
||||||
lbl.add_theme_font_size_override("font_size", 16)
|
|
||||||
|
|
||||||
file_name = dir.get_next()
|
|
||||||
|
|
||||||
brush_list.columns = sqrt(brush_list.get_child_count()) + 2
|
|
||||||
|
|
||||||
if not default_brush_btn:
|
|
||||||
default_brush_btn = brush_button_group.get_buttons()[0]
|
|
||||||
default_brush_btn.set_pressed(true)
|
|
||||||
|
|
||||||
settings["brush"] = brush_button_group
|
|
||||||
|
|
||||||
# Optionally erase the main brush button text and replace it with the texture
|
|
||||||
# var select_brush_btn: Button = brush_list.get_parent().get_parent()
|
|
||||||
# select_brush_btn.set_button_icon(default_brush_btn.get_button_icon())
|
|
||||||
# select_brush_btn.set_custom_minimum_size(Vector2.ONE * 36)
|
|
||||||
# select_brush_btn.set_icon_alignment(HORIZONTAL_ALIGNMENT_CENTER)
|
|
||||||
# select_brush_btn.set_expand_icon(true)
|
|
||||||
|
|
||||||
|
|
||||||
func _on_brush_hover(p_hovering: bool, p_button: Button) -> void:
|
|
||||||
if p_button.get_child_count() > 0:
|
|
||||||
var child = p_button.get_child(0)
|
|
||||||
if child is Label:
|
|
||||||
if p_hovering:
|
|
||||||
child.visible = true
|
|
||||||
else:
|
|
||||||
child.visible = false
|
|
||||||
|
|
||||||
|
|
||||||
func _on_pick(p_type: Terrain3DEditor.Tool) -> void:
|
|
||||||
emit_signal("picking", p_type, _on_picked)
|
|
||||||
|
|
||||||
|
|
||||||
func _on_picked(p_type: Terrain3DEditor.Tool, p_color: Color, p_global_position: Vector3) -> void:
|
|
||||||
match p_type:
|
|
||||||
Terrain3DEditor.HEIGHT:
|
|
||||||
settings["height"].value = p_color.r
|
|
||||||
Terrain3DEditor.COLOR:
|
|
||||||
settings["color"].color = p_color
|
|
||||||
Terrain3DEditor.ROUGHNESS:
|
|
||||||
# 200... -.5 converts 0,1 to -100,100
|
|
||||||
settings["roughness"].value = round(200 * (p_color.a - 0.5))
|
|
||||||
_on_setting_changed()
|
|
||||||
|
|
||||||
|
|
||||||
func _on_point_pick(p_type: Terrain3DEditor.Tool, p_name: String) -> void:
|
|
||||||
assert(p_type == Terrain3DEditor.HEIGHT)
|
|
||||||
emit_signal("picking", p_type, _on_point_picked.bind(p_name))
|
|
||||||
|
|
||||||
|
|
||||||
func _on_point_picked(p_type: Terrain3DEditor.Tool, p_color: Color, p_global_position: Vector3, p_name: String) -> void:
|
|
||||||
assert(p_type == Terrain3DEditor.HEIGHT)
|
|
||||||
|
|
||||||
var point: Vector3 = p_global_position
|
|
||||||
point.y = p_color.r
|
|
||||||
settings[p_name].add_point(point)
|
|
||||||
_on_setting_changed()
|
|
||||||
|
|
||||||
|
|
||||||
func add_setting(p_type: SettingType, p_name: StringName, p_value: Variant, p_parent: Control,
|
|
||||||
p_suffix: String = "", p_min_value: float = 0.0, p_max_value: float = 0.0, p_step: float = 1.0,
|
|
||||||
p_flags: int = NONE) -> void:
|
|
||||||
|
|
||||||
var container: HBoxContainer = HBoxContainer.new()
|
|
||||||
var label: Label = Label.new()
|
|
||||||
var control: Control
|
|
||||||
|
|
||||||
container.set_v_size_flags(SIZE_EXPAND_FILL)
|
|
||||||
|
|
||||||
match p_type:
|
|
||||||
SettingType.SLIDER, SettingType.DOUBLE_SLIDER:
|
|
||||||
label.set_vertical_alignment(VERTICAL_ALIGNMENT_CENTER)
|
|
||||||
label.set_horizontal_alignment(HORIZONTAL_ALIGNMENT_CENTER)
|
|
||||||
label.set_custom_minimum_size(Vector2(32, 0))
|
|
||||||
label.set_v_size_flags(SIZE_SHRINK_CENTER)
|
|
||||||
label.set_text(p_name.capitalize() + ": ")
|
|
||||||
container.add_child(label, true)
|
|
||||||
|
|
||||||
var slider: Control
|
|
||||||
if p_type == SettingType.SLIDER:
|
|
||||||
control = EditorSpinSlider.new()
|
|
||||||
control.set_flat(true)
|
|
||||||
control.set_hide_slider(true)
|
|
||||||
control.connect("value_changed", _on_setting_changed)
|
|
||||||
control.set_max(p_max_value)
|
|
||||||
control.set_min(p_min_value)
|
|
||||||
control.set_step(p_step)
|
|
||||||
control.set_value(p_value)
|
|
||||||
control.set_suffix(p_suffix)
|
|
||||||
control.set_v_size_flags(SIZE_SHRINK_CENTER)
|
|
||||||
|
|
||||||
slider = HSlider.new()
|
|
||||||
slider.share(control)
|
|
||||||
if p_flags & ALLOW_LARGER:
|
|
||||||
slider.set_allow_greater(true)
|
|
||||||
if p_flags & ALLOW_SMALLER:
|
|
||||||
slider.set_allow_lesser(true)
|
|
||||||
|
|
||||||
else:
|
|
||||||
control = Label.new()
|
|
||||||
control.set_horizontal_alignment(HORIZONTAL_ALIGNMENT_CENTER)
|
|
||||||
control.set_vertical_alignment(VERTICAL_ALIGNMENT_CENTER)
|
|
||||||
slider = DoubleSlider.new()
|
|
||||||
slider.label = control
|
|
||||||
slider.suffix = p_suffix
|
|
||||||
slider.connect("value_changed", _on_setting_changed)
|
|
||||||
|
|
||||||
control.set_custom_minimum_size(Vector2(75, 0))
|
|
||||||
slider.set_max(p_max_value)
|
|
||||||
slider.set_min(p_min_value)
|
|
||||||
slider.set_step(p_step)
|
|
||||||
slider.set_value(p_value)
|
|
||||||
slider.set_v_size_flags(SIZE_SHRINK_CENTER)
|
|
||||||
slider.set_h_size_flags(SIZE_SHRINK_END | SIZE_EXPAND)
|
|
||||||
slider.set_custom_minimum_size(Vector2(100, 10))
|
|
||||||
|
|
||||||
container.add_child(slider, true)
|
|
||||||
|
|
||||||
SettingType.CHECKBOX:
|
|
||||||
control = CheckBox.new()
|
|
||||||
control.set_text(p_name.capitalize())
|
|
||||||
control.set_pressed_no_signal(p_value)
|
|
||||||
control.connect("pressed", _on_setting_changed)
|
|
||||||
|
|
||||||
SettingType.COLOR_SELECT:
|
|
||||||
control = ColorPickerButton.new()
|
|
||||||
control.set_custom_minimum_size(Vector2(100, 10))
|
|
||||||
control.color = Color.WHITE
|
|
||||||
control.edit_alpha = false
|
|
||||||
control.get_picker().set_color_mode(ColorPicker.MODE_HSV)
|
|
||||||
control.connect("color_changed", _on_setting_changed)
|
|
||||||
|
|
||||||
SettingType.PICKER:
|
|
||||||
control = Button.new()
|
|
||||||
control.icon = load(PICKER_ICON)
|
|
||||||
control.tooltip_text = "Pick value from the Terrain"
|
|
||||||
control.connect("pressed", _on_pick.bind(p_value))
|
|
||||||
|
|
||||||
SettingType.POINT_PICKER:
|
|
||||||
control = PointPicker.new()
|
|
||||||
control.connect("pressed", _on_point_pick.bind(p_value, p_name))
|
|
||||||
control.connect("value_changed", _on_setting_changed)
|
|
||||||
|
|
||||||
container.add_child(control, true)
|
|
||||||
p_parent.add_child(container, true)
|
|
||||||
|
|
||||||
settings[p_name] = control
|
|
||||||
|
|
||||||
|
|
||||||
func get_setting(p_setting: String) -> Variant:
|
|
||||||
var object: Object = settings[p_setting]
|
|
||||||
var value: Variant
|
|
||||||
if object is Range:
|
|
||||||
value = object.get_value()
|
|
||||||
elif object is DoubleSlider:
|
|
||||||
value = [object.get_min_value(), object.get_max_value()]
|
|
||||||
elif object is ButtonGroup:
|
|
||||||
var img: Image = object.get_pressed_button().get_meta("image")
|
|
||||||
var tex: Texture2D = object.get_pressed_button().get_button_icon()
|
|
||||||
value = [ img, tex ]
|
|
||||||
elif object is CheckBox:
|
|
||||||
value = object.is_pressed()
|
|
||||||
elif object is ColorPickerButton:
|
|
||||||
value = object.color
|
|
||||||
elif object is PointPicker:
|
|
||||||
value = object.get_points()
|
|
||||||
return value
|
|
||||||
|
|
||||||
|
|
||||||
func hide_settings(p_settings: PackedStringArray) -> void:
|
|
||||||
for setting in settings.keys():
|
|
||||||
var object: Object = settings[setting]
|
|
||||||
if object is Control:
|
|
||||||
object.get_parent().show()
|
|
||||||
|
|
||||||
for setting in p_settings:
|
|
||||||
if settings.has(setting):
|
|
||||||
var object: Object = settings[setting]
|
|
||||||
if object is Control:
|
|
||||||
object.get_parent().hide()
|
|
||||||
|
|
||||||
|
|
||||||
func _on_setting_changed(p_data: Variant = null) -> void:
|
|
||||||
# If a button was clicked on a submenu
|
|
||||||
if p_data is Button and p_data.get_parent().get_parent() is PopupPanel:
|
|
||||||
if p_data.get_parent().name == "BrushList":
|
|
||||||
# Optionally Set selected brush texture in main brush button
|
|
||||||
# p_data.get_parent().get_parent().get_parent().set_button_icon(p_data.get_button_icon())
|
|
||||||
# Hide popup
|
|
||||||
p_data.get_parent().get_parent().set_visible(false)
|
|
||||||
# Hide label
|
|
||||||
if p_data.get_child_count() > 0:
|
|
||||||
p_data.get_child(0).visible = false
|
|
||||||
|
|
||||||
emit_signal("setting_changed")
|
|
||||||
|
|
||||||
|
|
||||||
func _on_drawable_toggled(p_button_pressed: bool) -> void:
|
|
||||||
if not p_button_pressed:
|
|
||||||
settings["gradient_points"].clear()
|
|
||||||
|
|
||||||
|
|
||||||
func _get_brush_preview_material() -> ShaderMaterial:
|
|
||||||
if !brush_preview_material:
|
|
||||||
brush_preview_material = ShaderMaterial.new()
|
|
||||||
|
|
||||||
var shader: Shader = Shader.new()
|
|
||||||
var code: String = "shader_type canvas_item;\n"
|
|
||||||
|
|
||||||
code += "varying vec4 v_vertex_color;\n"
|
|
||||||
code += "void vertex() {\n"
|
|
||||||
code += " v_vertex_color = COLOR;\n"
|
|
||||||
code += "}\n"
|
|
||||||
code += "void fragment(){\n"
|
|
||||||
code += " vec4 tex = texture(TEXTURE, UV);\n"
|
|
||||||
code += " COLOR.a *= pow(tex.r, 0.666);\n"
|
|
||||||
code += " COLOR.rgb = v_vertex_color.rgb;\n"
|
|
||||||
code += "}\n"
|
|
||||||
|
|
||||||
shader.set_code(code)
|
|
||||||
|
|
||||||
brush_preview_material.set_shader(shader)
|
|
||||||
|
|
||||||
return brush_preview_material
|
|
||||||
|
|
||||||
|
|
||||||
func _black_to_alpha(p_image: Image) -> void:
|
|
||||||
if p_image.get_format() != Image.FORMAT_RGBAF:
|
|
||||||
p_image.convert(Image.FORMAT_RGBAF)
|
|
||||||
|
|
||||||
for y in p_image.get_height():
|
|
||||||
for x in p_image.get_width():
|
|
||||||
var color: Color = p_image.get_pixel(x,y)
|
|
||||||
color.a = color.get_luminance()
|
|
||||||
p_image.set_pixel(x, y, color)
|
|
||||||
|
|
||||||
|
|
||||||
#### Sub Class DoubleSlider
|
|
||||||
|
|
||||||
class DoubleSlider extends Range:
|
|
||||||
|
|
||||||
var label: Label
|
|
||||||
var suffix: String
|
|
||||||
var grabbed: bool = false
|
|
||||||
var _max_value: float
|
|
||||||
|
|
||||||
|
|
||||||
func _gui_input(p_event: InputEvent) -> void:
|
|
||||||
if p_event is InputEventMouseButton:
|
|
||||||
if p_event.get_button_index() == MOUSE_BUTTON_LEFT:
|
|
||||||
grabbed = p_event.is_pressed()
|
|
||||||
set_min_max(p_event.get_position().x)
|
|
||||||
|
|
||||||
if p_event is InputEventMouseMotion:
|
|
||||||
if grabbed:
|
|
||||||
set_min_max(p_event.get_position().x)
|
|
||||||
|
|
||||||
|
|
||||||
func _notification(p_what: int) -> void:
|
|
||||||
if p_what == NOTIFICATION_RESIZED:
|
|
||||||
pass
|
|
||||||
if p_what == NOTIFICATION_DRAW:
|
|
||||||
var bg: StyleBox = get_theme_stylebox("slider", "HSlider")
|
|
||||||
var bg_height: float = bg.get_minimum_size().y
|
|
||||||
draw_style_box(bg, Rect2(Vector2(0, (size.y - bg_height) / 2), Vector2(size.x, bg_height)))
|
|
||||||
|
|
||||||
var grabber: Texture2D = get_theme_icon("grabber", "HSlider")
|
|
||||||
var area: StyleBox = get_theme_stylebox("grabber_area", "HSlider")
|
|
||||||
var h: float = size.y / 2 - grabber.get_size().y / 2
|
|
||||||
|
|
||||||
var minpos: Vector2 = Vector2((min_value / _max_value) * size.x - grabber.get_size().x / 2, h)
|
|
||||||
var maxpos: Vector2 = Vector2((max_value / _max_value) * size.x - grabber.get_size().x / 2, h)
|
|
||||||
|
|
||||||
draw_style_box(area, Rect2(Vector2(minpos.x + grabber.get_size().x / 2, (size.y - bg_height) / 2), Vector2(maxpos.x - minpos.x, bg_height)))
|
|
||||||
|
|
||||||
draw_texture(grabber, minpos)
|
|
||||||
draw_texture(grabber, maxpos)
|
|
||||||
|
|
||||||
|
|
||||||
func set_max(p_value: float) -> void:
|
|
||||||
max_value = p_value
|
|
||||||
if _max_value == 0:
|
|
||||||
_max_value = max_value
|
|
||||||
update_label()
|
|
||||||
|
|
||||||
|
|
||||||
func set_min_max(p_xpos: float) -> void:
|
|
||||||
var mid_value_normalized: float = ((max_value + min_value) / 2.0) / _max_value
|
|
||||||
var mid_value: float = size.x * mid_value_normalized
|
|
||||||
var min_active: bool = p_xpos < mid_value
|
|
||||||
var xpos_ranged: float = snappedf((p_xpos / size.x) * _max_value, step)
|
|
||||||
|
|
||||||
if min_active:
|
|
||||||
min_value = xpos_ranged
|
|
||||||
else:
|
|
||||||
max_value = xpos_ranged
|
|
||||||
|
|
||||||
min_value = clamp(min_value, 0, max_value - 10)
|
|
||||||
max_value = clamp(max_value, min_value + 10, _max_value)
|
|
||||||
|
|
||||||
update_label()
|
|
||||||
emit_signal("setting_changed", value)
|
|
||||||
queue_redraw()
|
|
||||||
|
|
||||||
|
|
||||||
func update_label() -> void:
|
|
||||||
if label:
|
|
||||||
label.set_text(str(min_value) + suffix + "/" + str(max_value) + suffix)
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
// This shader is the minimum needed to allow the terrain to function.
|
// This shader is the minimum needed to allow the terrain to function, without any texturing.
|
||||||
|
|
||||||
shader_type spatial;
|
shader_type spatial;
|
||||||
render_mode blend_mix,depth_draw_opaque,cull_back,diffuse_burley,specular_schlick_ggx;
|
render_mode blend_mix,depth_draw_opaque,cull_back,diffuse_burley,specular_schlick_ggx;
|
||||||
|
|
@ -12,8 +12,19 @@ uniform int _region_map_size = 16;
|
||||||
uniform int _region_map[256];
|
uniform int _region_map[256];
|
||||||
uniform vec2 _region_offsets[256];
|
uniform vec2 _region_offsets[256];
|
||||||
uniform sampler2DArray _height_maps : repeat_disable;
|
uniform sampler2DArray _height_maps : repeat_disable;
|
||||||
|
uniform usampler2DArray _control_maps : repeat_disable;
|
||||||
|
uniform sampler2DArray _color_maps : source_color, filter_linear_mipmap_anisotropic, repeat_disable;
|
||||||
|
uniform sampler2DArray _texture_array_albedo : source_color, filter_linear_mipmap_anisotropic, repeat_enable;
|
||||||
|
uniform sampler2DArray _texture_array_normal : hint_normal, filter_linear_mipmap_anisotropic, repeat_enable;
|
||||||
|
|
||||||
varying vec3 v_vertex; // World coordinate vertex location
|
uniform float _texture_uv_scale_array[32];
|
||||||
|
uniform float _texture_uv_rotation_array[32];
|
||||||
|
uniform vec4 _texture_color_array[32];
|
||||||
|
uniform uint _background_mode = 1u; // NONE = 0, FLAT = 1, NOISE = 2
|
||||||
|
uniform uint _mouse_layer = 0x80000000u; // Layer 32
|
||||||
|
|
||||||
|
varying flat vec2 v_uv_offset;
|
||||||
|
varying flat vec2 v_uv2_offset;
|
||||||
|
|
||||||
////////////////////////
|
////////////////////////
|
||||||
// Vertex
|
// Vertex
|
||||||
|
|
@ -34,12 +45,17 @@ ivec3 get_region_uv(vec2 uv) {
|
||||||
// XY: (0 to 1) coordinates within a region
|
// XY: (0 to 1) coordinates within a region
|
||||||
// Z: layer index used for texturearrays, -1 if not in a region
|
// Z: layer index used for texturearrays, -1 if not in a region
|
||||||
vec3 get_region_uv2(vec2 uv) {
|
vec3 get_region_uv2(vec2 uv) {
|
||||||
ivec2 pos = ivec2(floor(uv)) + (_region_map_size / 2);
|
// Vertex function added half a texel to UV2, to center the UV's. vertex(), fragment() and get_height()
|
||||||
|
// call this with reclaimed versions of UV2, so to keep the last row/column within the correct
|
||||||
|
// window, take back the half pixel before the floor().
|
||||||
|
ivec2 pos = ivec2(floor(uv - vec2(_region_texel_size * 0.5))) + (_region_map_size / 2);
|
||||||
int bounds = int(pos.x>=0 && pos.x<_region_map_size && pos.y>=0 && pos.y<_region_map_size);
|
int bounds = int(pos.x>=0 && pos.x<_region_map_size && pos.y>=0 && pos.y<_region_map_size);
|
||||||
int layer_index = _region_map[ pos.y * _region_map_size + pos.x ] * bounds - 1;
|
int layer_index = _region_map[ pos.y * _region_map_size + pos.x ] * bounds - 1;
|
||||||
|
// The return value is still texel-centered.
|
||||||
return vec3(uv - _region_offsets[layer_index], float(layer_index));
|
return vec3(uv - _region_offsets[layer_index], float(layer_index));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 1 lookup
|
||||||
float get_height(vec2 uv) {
|
float get_height(vec2 uv) {
|
||||||
highp float height = 0.0;
|
highp float height = 0.0;
|
||||||
vec3 region = get_region_uv2(uv);
|
vec3 region = get_region_uv2(uv);
|
||||||
|
|
@ -51,23 +67,39 @@ float get_height(vec2 uv) {
|
||||||
|
|
||||||
void vertex() {
|
void vertex() {
|
||||||
// Get vertex of flat plane in world coordinates and set world UV
|
// Get vertex of flat plane in world coordinates and set world UV
|
||||||
v_vertex = (MODEL_MATRIX * vec4(VERTEX, 1.0)).xyz;
|
vec3 vertex = (MODEL_MATRIX * vec4(VERTEX, 1.0)).xyz;
|
||||||
|
|
||||||
// UV coordinates in world space. Values are 0 to _region_size within regions
|
// UV coordinates in world space. Values are 0 to _region_size within regions
|
||||||
UV = round(v_vertex.xz * _mesh_vertex_density);
|
UV = round(vertex.xz * _mesh_vertex_density);
|
||||||
|
|
||||||
// UV coordinates in region space + texel offset. Values are 0 to 1 within regions
|
// Discard vertices for Holes. 1 lookup
|
||||||
UV2 = (UV + vec2(0.5)) * _region_texel_size;
|
ivec3 region = get_region_uv(UV);
|
||||||
|
uint control = texelFetch(_control_maps, region, 0).r;
|
||||||
|
bool hole = bool(control >>2u & 0x1u);
|
||||||
|
// Show holes to all cameras except mouse camera (on exactly 1 layer)
|
||||||
|
if ( !(CAMERA_VISIBLE_LAYERS == _mouse_layer) &&
|
||||||
|
(hole || (_background_mode == 0u && region.z < 0)) ) {
|
||||||
|
VERTEX.x = 0./0.;
|
||||||
|
} else {
|
||||||
|
// UV coordinates in region space + texel offset. Values are 0 to 1 within regions
|
||||||
|
UV2 = (UV + vec2(0.5)) * _region_texel_size;
|
||||||
|
|
||||||
// Get final vertex location and save it
|
// Get final vertex location and save it
|
||||||
VERTEX.y = get_height(UV2);
|
VERTEX.y = get_height(UV2);
|
||||||
v_vertex = (MODEL_MATRIX * vec4(VERTEX, 1.0)).xyz;
|
}
|
||||||
|
|
||||||
|
// Transform UVs to local to avoid poor precision during varying interpolation.
|
||||||
|
v_uv_offset = MODEL_MATRIX[3].xz * _mesh_vertex_density;
|
||||||
|
UV -= v_uv_offset;
|
||||||
|
v_uv2_offset = v_uv_offset * _region_texel_size;
|
||||||
|
UV2 -= v_uv2_offset;
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////
|
////////////////////////
|
||||||
// Fragment
|
// Fragment
|
||||||
////////////////////////
|
////////////////////////
|
||||||
|
|
||||||
|
// 3 lookups
|
||||||
vec3 get_normal(vec2 uv, out vec3 tangent, out vec3 binormal) {
|
vec3 get_normal(vec2 uv, out vec3 tangent, out vec3 binormal) {
|
||||||
// Get the height of the current vertex
|
// Get the height of the current vertex
|
||||||
float height = get_height(uv);
|
float height = get_height(uv);
|
||||||
|
|
@ -97,9 +129,13 @@ vec3 get_normal(vec2 uv, out vec3 tangent, out vec3 binormal) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void fragment() {
|
void fragment() {
|
||||||
// Calculate Terrain Normals
|
// Recover UVs
|
||||||
|
vec2 uv = UV + v_uv_offset;
|
||||||
|
vec2 uv2 = UV2 + v_uv2_offset;
|
||||||
|
|
||||||
|
// Calculate Terrain Normals. 4 lookups
|
||||||
vec3 w_tangent, w_binormal;
|
vec3 w_tangent, w_binormal;
|
||||||
vec3 w_normal = get_normal(UV2, w_tangent, w_binormal);
|
vec3 w_normal = get_normal(uv2, w_tangent, w_binormal);
|
||||||
NORMAL = mat3(VIEW_MATRIX) * w_normal;
|
NORMAL = mat3(VIEW_MATRIX) * w_normal;
|
||||||
TANGENT = mat3(VIEW_MATRIX) * w_tangent;
|
TANGENT = mat3(VIEW_MATRIX) * w_tangent;
|
||||||
BINORMAL = mat3(VIEW_MATRIX) * w_binormal;
|
BINORMAL = mat3(VIEW_MATRIX) * w_binormal;
|
||||||
|
|
@ -107,3 +143,4 @@ void fragment() {
|
||||||
// Apply PBR
|
// Apply PBR
|
||||||
ALBEDO=vec3(.2);
|
ALBEDO=vec3(.2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,9 @@
|
||||||
# This script is an addon for HungryProton's Scatter https://github.com/HungryProton/scatter
|
# This script is an addon for HungryProton's Scatter https://github.com/HungryProton/scatter
|
||||||
# It allows Scatter to detect the terrain height from Terrain3D
|
# It provides a `Project on Terrain3D` modifier, which allows Scatter
|
||||||
|
# to detect the terrain height from Terrain3D without using collision.
|
||||||
# Copy this file into /addons/proton_scatter/src/modifiers
|
# Copy this file into /addons/proton_scatter/src/modifiers
|
||||||
# Then uncomment everything below
|
# Then uncomment everything below
|
||||||
# In the editor, add this modifier, then set your Terrain3D node
|
# In the editor, add this modifier to Scatter, then set your Terrain3D node
|
||||||
|
|
||||||
#@tool
|
#@tool
|
||||||
#extends "base_modifier.gd"
|
#extends "base_modifier.gd"
|
||||||
|
|
|
||||||
|
|
@ -2,9 +2,10 @@
|
||||||
|
|
||||||
importer="texture"
|
importer="texture"
|
||||||
type="CompressedTexture2D"
|
type="CompressedTexture2D"
|
||||||
uid="uid://cjrjfjmbyseol"
|
uid="uid://dbby47aoknjeb"
|
||||||
path="res://.godot/imported/icon_brush.svg-8886426485f67abe2233686de39952ce.ctex"
|
path="res://.godot/imported/icon_brush.svg-8886426485f67abe2233686de39952ce.ctex"
|
||||||
metadata={
|
metadata={
|
||||||
|
"has_editor_variant": true,
|
||||||
"vram_texture": false
|
"vram_texture": false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -33,5 +34,5 @@ process/hdr_clamp_exposure=false
|
||||||
process/size_limit=0
|
process/size_limit=0
|
||||||
detect_3d/compress_to=1
|
detect_3d/compress_to=1
|
||||||
svg/scale=1.0
|
svg/scale=1.0
|
||||||
editor/scale_with_editor_scale=false
|
editor/scale_with_editor_scale=true
|
||||||
editor/convert_colors_with_editor_theme=false
|
editor/convert_colors_with_editor_theme=false
|
||||||
|
|
|
||||||
|
|
@ -2,9 +2,10 @@
|
||||||
|
|
||||||
importer="texture"
|
importer="texture"
|
||||||
type="CompressedTexture2D"
|
type="CompressedTexture2D"
|
||||||
uid="uid://cnytnsydxqdn2"
|
uid="uid://bump6ax7j3rp1"
|
||||||
path="res://.godot/imported/icon_color.svg-b5b52e67ad2f610c27688c5daeb9cd1c.ctex"
|
path="res://.godot/imported/icon_color.svg-b5b52e67ad2f610c27688c5daeb9cd1c.ctex"
|
||||||
metadata={
|
metadata={
|
||||||
|
"has_editor_variant": true,
|
||||||
"vram_texture": false
|
"vram_texture": false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -33,5 +34,5 @@ process/hdr_clamp_exposure=false
|
||||||
process/size_limit=0
|
process/size_limit=0
|
||||||
detect_3d/compress_to=1
|
detect_3d/compress_to=1
|
||||||
svg/scale=1.0
|
svg/scale=1.0
|
||||||
editor/scale_with_editor_scale=false
|
editor/scale_with_editor_scale=true
|
||||||
editor/convert_colors_with_editor_theme=false
|
editor/convert_colors_with_editor_theme=false
|
||||||
|
|
|
||||||
|
|
@ -2,9 +2,10 @@
|
||||||
|
|
||||||
importer="texture"
|
importer="texture"
|
||||||
type="CompressedTexture2D"
|
type="CompressedTexture2D"
|
||||||
uid="uid://ca7l0radlfn5w"
|
uid="uid://8ma1rbcinto3"
|
||||||
path="res://.godot/imported/icon_height_add.svg-2928bbcb35ef4816ead056c5bcf5bdbd.ctex"
|
path="res://.godot/imported/icon_height_add.svg-2928bbcb35ef4816ead056c5bcf5bdbd.ctex"
|
||||||
metadata={
|
metadata={
|
||||||
|
"has_editor_variant": true,
|
||||||
"vram_texture": false
|
"vram_texture": false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -33,5 +34,5 @@ process/hdr_clamp_exposure=false
|
||||||
process/size_limit=0
|
process/size_limit=0
|
||||||
detect_3d/compress_to=1
|
detect_3d/compress_to=1
|
||||||
svg/scale=1.0
|
svg/scale=1.0
|
||||||
editor/scale_with_editor_scale=false
|
editor/scale_with_editor_scale=true
|
||||||
editor/convert_colors_with_editor_theme=false
|
editor/convert_colors_with_editor_theme=false
|
||||||
|
|
|
||||||
|
|
@ -2,9 +2,10 @@
|
||||||
|
|
||||||
importer="texture"
|
importer="texture"
|
||||||
type="CompressedTexture2D"
|
type="CompressedTexture2D"
|
||||||
uid="uid://cunkwpgutguvo"
|
uid="uid://5xwklmfyqaha"
|
||||||
path="res://.godot/imported/icon_height_div.svg-982f74e4859453a7d67caa2f6a71b056.ctex"
|
path="res://.godot/imported/icon_height_div.svg-982f74e4859453a7d67caa2f6a71b056.ctex"
|
||||||
metadata={
|
metadata={
|
||||||
|
"has_editor_variant": true,
|
||||||
"vram_texture": false
|
"vram_texture": false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -33,5 +34,5 @@ process/hdr_clamp_exposure=false
|
||||||
process/size_limit=0
|
process/size_limit=0
|
||||||
detect_3d/compress_to=1
|
detect_3d/compress_to=1
|
||||||
svg/scale=1.0
|
svg/scale=1.0
|
||||||
editor/scale_with_editor_scale=false
|
editor/scale_with_editor_scale=true
|
||||||
editor/convert_colors_with_editor_theme=false
|
editor/convert_colors_with_editor_theme=false
|
||||||
|
|
|
||||||
|
|
@ -2,9 +2,10 @@
|
||||||
|
|
||||||
importer="texture"
|
importer="texture"
|
||||||
type="CompressedTexture2D"
|
type="CompressedTexture2D"
|
||||||
uid="uid://cs2injw82o1il"
|
uid="uid://cpfvqnfgrw4e"
|
||||||
path="res://.godot/imported/icon_height_flat.svg-e58f6a7038e84631a5f56866c4c671e0.ctex"
|
path="res://.godot/imported/icon_height_flat.svg-e58f6a7038e84631a5f56866c4c671e0.ctex"
|
||||||
metadata={
|
metadata={
|
||||||
|
"has_editor_variant": true,
|
||||||
"vram_texture": false
|
"vram_texture": false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -33,5 +34,5 @@ process/hdr_clamp_exposure=false
|
||||||
process/size_limit=0
|
process/size_limit=0
|
||||||
detect_3d/compress_to=1
|
detect_3d/compress_to=1
|
||||||
svg/scale=1.0
|
svg/scale=1.0
|
||||||
editor/scale_with_editor_scale=false
|
editor/scale_with_editor_scale=true
|
||||||
editor/convert_colors_with_editor_theme=false
|
editor/convert_colors_with_editor_theme=false
|
||||||
|
|
|
||||||
|
|
@ -2,9 +2,10 @@
|
||||||
|
|
||||||
importer="texture"
|
importer="texture"
|
||||||
type="CompressedTexture2D"
|
type="CompressedTexture2D"
|
||||||
uid="uid://tvaw0pb2bdhu"
|
uid="uid://bkqdmfjtug1c7"
|
||||||
path="res://.godot/imported/icon_height_mul.svg-b6b666e20be820f5aa48e7410648290c.ctex"
|
path="res://.godot/imported/icon_height_mul.svg-b6b666e20be820f5aa48e7410648290c.ctex"
|
||||||
metadata={
|
metadata={
|
||||||
|
"has_editor_variant": true,
|
||||||
"vram_texture": false
|
"vram_texture": false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -33,5 +34,5 @@ process/hdr_clamp_exposure=false
|
||||||
process/size_limit=0
|
process/size_limit=0
|
||||||
detect_3d/compress_to=1
|
detect_3d/compress_to=1
|
||||||
svg/scale=1.0
|
svg/scale=1.0
|
||||||
editor/scale_with_editor_scale=false
|
editor/scale_with_editor_scale=true
|
||||||
editor/convert_colors_with_editor_theme=false
|
editor/convert_colors_with_editor_theme=false
|
||||||
|
|
|
||||||
|
|
@ -2,9 +2,10 @@
|
||||||
|
|
||||||
importer="texture"
|
importer="texture"
|
||||||
type="CompressedTexture2D"
|
type="CompressedTexture2D"
|
||||||
uid="uid://c3xbj3i7emxir"
|
uid="uid://wkm8llh72h1m"
|
||||||
path="res://.godot/imported/icon_height_slope.svg-2a8181e8d9f9b74739d6f4a9e62f040d.ctex"
|
path="res://.godot/imported/icon_height_slope.svg-2a8181e8d9f9b74739d6f4a9e62f040d.ctex"
|
||||||
metadata={
|
metadata={
|
||||||
|
"has_editor_variant": true,
|
||||||
"vram_texture": false
|
"vram_texture": false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -33,5 +34,5 @@ process/hdr_clamp_exposure=false
|
||||||
process/size_limit=0
|
process/size_limit=0
|
||||||
detect_3d/compress_to=1
|
detect_3d/compress_to=1
|
||||||
svg/scale=1.0
|
svg/scale=1.0
|
||||||
editor/scale_with_editor_scale=false
|
editor/scale_with_editor_scale=true
|
||||||
editor/convert_colors_with_editor_theme=false
|
editor/convert_colors_with_editor_theme=false
|
||||||
|
|
|
||||||
|
|
@ -2,9 +2,10 @@
|
||||||
|
|
||||||
importer="texture"
|
importer="texture"
|
||||||
type="CompressedTexture2D"
|
type="CompressedTexture2D"
|
||||||
uid="uid://ub12g3jkvivd"
|
uid="uid://fhvnyvqmygfs"
|
||||||
path="res://.godot/imported/icon_height_smooth.svg-83cfb47d64fb9579b212027a5aa50672.ctex"
|
path="res://.godot/imported/icon_height_smooth.svg-83cfb47d64fb9579b212027a5aa50672.ctex"
|
||||||
metadata={
|
metadata={
|
||||||
|
"has_editor_variant": true,
|
||||||
"vram_texture": false
|
"vram_texture": false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -33,5 +34,5 @@ process/hdr_clamp_exposure=false
|
||||||
process/size_limit=0
|
process/size_limit=0
|
||||||
detect_3d/compress_to=1
|
detect_3d/compress_to=1
|
||||||
svg/scale=1.0
|
svg/scale=1.0
|
||||||
editor/scale_with_editor_scale=false
|
editor/scale_with_editor_scale=true
|
||||||
editor/convert_colors_with_editor_theme=false
|
editor/convert_colors_with_editor_theme=false
|
||||||
|
|
|
||||||
|
|
@ -2,9 +2,10 @@
|
||||||
|
|
||||||
importer="texture"
|
importer="texture"
|
||||||
type="CompressedTexture2D"
|
type="CompressedTexture2D"
|
||||||
uid="uid://vyw5rhfpbjku"
|
uid="uid://cbtqb5fq02yx1"
|
||||||
path="res://.godot/imported/icon_height_sub.svg-f01f73a219b6c1858d4bd958d01e8130.ctex"
|
path="res://.godot/imported/icon_height_sub.svg-f01f73a219b6c1858d4bd958d01e8130.ctex"
|
||||||
metadata={
|
metadata={
|
||||||
|
"has_editor_variant": true,
|
||||||
"vram_texture": false
|
"vram_texture": false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -33,5 +34,5 @@ process/hdr_clamp_exposure=false
|
||||||
process/size_limit=0
|
process/size_limit=0
|
||||||
detect_3d/compress_to=1
|
detect_3d/compress_to=1
|
||||||
svg/scale=1.0
|
svg/scale=1.0
|
||||||
editor/scale_with_editor_scale=false
|
editor/scale_with_editor_scale=true
|
||||||
editor/convert_colors_with_editor_theme=false
|
editor/convert_colors_with_editor_theme=false
|
||||||
|
|
|
||||||
|
|
@ -2,9 +2,10 @@
|
||||||
|
|
||||||
importer="texture"
|
importer="texture"
|
||||||
type="CompressedTexture2D"
|
type="CompressedTexture2D"
|
||||||
uid="uid://bqktati6gi08q"
|
uid="uid://4qj0x1rs0a83"
|
||||||
path="res://.godot/imported/icon_holes.svg-fadd8eef4df4cdc393621d5ff25aa8e3.ctex"
|
path="res://.godot/imported/icon_holes.svg-fadd8eef4df4cdc393621d5ff25aa8e3.ctex"
|
||||||
metadata={
|
metadata={
|
||||||
|
"has_editor_variant": true,
|
||||||
"vram_texture": false
|
"vram_texture": false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -33,5 +34,5 @@ process/hdr_clamp_exposure=false
|
||||||
process/size_limit=0
|
process/size_limit=0
|
||||||
detect_3d/compress_to=1
|
detect_3d/compress_to=1
|
||||||
svg/scale=1.0
|
svg/scale=1.0
|
||||||
editor/scale_with_editor_scale=false
|
editor/scale_with_editor_scale=true
|
||||||
editor/convert_colors_with_editor_theme=false
|
editor/convert_colors_with_editor_theme=false
|
||||||
|
|
|
||||||
|
|
@ -2,9 +2,10 @@
|
||||||
|
|
||||||
importer="texture"
|
importer="texture"
|
||||||
type="CompressedTexture2D"
|
type="CompressedTexture2D"
|
||||||
uid="uid://js5u88icy324"
|
uid="uid://dkgbiictluyh8"
|
||||||
path="res://.godot/imported/icon_map_add.svg-a13cebbb261c5138d4ca5cbb5df24202.ctex"
|
path="res://.godot/imported/icon_map_add.svg-a13cebbb261c5138d4ca5cbb5df24202.ctex"
|
||||||
metadata={
|
metadata={
|
||||||
|
"has_editor_variant": true,
|
||||||
"vram_texture": false
|
"vram_texture": false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -33,5 +34,5 @@ process/hdr_clamp_exposure=false
|
||||||
process/size_limit=0
|
process/size_limit=0
|
||||||
detect_3d/compress_to=1
|
detect_3d/compress_to=1
|
||||||
svg/scale=1.0
|
svg/scale=1.0
|
||||||
editor/scale_with_editor_scale=false
|
editor/scale_with_editor_scale=true
|
||||||
editor/convert_colors_with_editor_theme=false
|
editor/convert_colors_with_editor_theme=false
|
||||||
|
|
|
||||||
|
|
@ -2,9 +2,10 @@
|
||||||
|
|
||||||
importer="texture"
|
importer="texture"
|
||||||
type="CompressedTexture2D"
|
type="CompressedTexture2D"
|
||||||
uid="uid://c1o6vv5wx3w65"
|
uid="uid://bavktgaibu05s"
|
||||||
path="res://.godot/imported/icon_map_remove.svg-bf5a269f9171f7027b6de1785cc63713.ctex"
|
path="res://.godot/imported/icon_map_remove.svg-bf5a269f9171f7027b6de1785cc63713.ctex"
|
||||||
metadata={
|
metadata={
|
||||||
|
"has_editor_variant": true,
|
||||||
"vram_texture": false
|
"vram_texture": false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -33,5 +34,5 @@ process/hdr_clamp_exposure=false
|
||||||
process/size_limit=0
|
process/size_limit=0
|
||||||
detect_3d/compress_to=1
|
detect_3d/compress_to=1
|
||||||
svg/scale=1.0
|
svg/scale=1.0
|
||||||
editor/scale_with_editor_scale=false
|
editor/scale_with_editor_scale=true
|
||||||
editor/convert_colors_with_editor_theme=false
|
editor/convert_colors_with_editor_theme=false
|
||||||
|
|
|
||||||
|
|
@ -2,9 +2,10 @@
|
||||||
|
|
||||||
importer="texture"
|
importer="texture"
|
||||||
type="CompressedTexture2D"
|
type="CompressedTexture2D"
|
||||||
uid="uid://xg3l0exmqch3"
|
uid="uid://c5004ol65cqti"
|
||||||
path="res://.godot/imported/icon_multimesh.svg-9447b6c5fe1ee9d406fd55dd3c63e196.ctex"
|
path="res://.godot/imported/icon_multimesh.svg-9447b6c5fe1ee9d406fd55dd3c63e196.ctex"
|
||||||
metadata={
|
metadata={
|
||||||
|
"has_editor_variant": true,
|
||||||
"vram_texture": false
|
"vram_texture": false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -33,5 +34,5 @@ process/hdr_clamp_exposure=false
|
||||||
process/size_limit=0
|
process/size_limit=0
|
||||||
detect_3d/compress_to=1
|
detect_3d/compress_to=1
|
||||||
svg/scale=1.0
|
svg/scale=1.0
|
||||||
editor/scale_with_editor_scale=false
|
editor/scale_with_editor_scale=true
|
||||||
editor/convert_colors_with_editor_theme=false
|
editor/convert_colors_with_editor_theme=false
|
||||||
|
|
|
||||||
|
|
@ -2,9 +2,10 @@
|
||||||
|
|
||||||
importer="texture"
|
importer="texture"
|
||||||
type="CompressedTexture2D"
|
type="CompressedTexture2D"
|
||||||
uid="uid://c0sprba3li1x4"
|
uid="uid://dewr4ro7nrg7y"
|
||||||
path="res://.godot/imported/icon_navigation.svg-35e49ee3c403c103a0079d4156b0d168.ctex"
|
path="res://.godot/imported/icon_navigation.svg-35e49ee3c403c103a0079d4156b0d168.ctex"
|
||||||
metadata={
|
metadata={
|
||||||
|
"has_editor_variant": true,
|
||||||
"vram_texture": false
|
"vram_texture": false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -33,5 +34,5 @@ process/hdr_clamp_exposure=false
|
||||||
process/size_limit=0
|
process/size_limit=0
|
||||||
detect_3d/compress_to=1
|
detect_3d/compress_to=1
|
||||||
svg/scale=1.0
|
svg/scale=1.0
|
||||||
editor/scale_with_editor_scale=false
|
editor/scale_with_editor_scale=true
|
||||||
editor/convert_colors_with_editor_theme=false
|
editor/convert_colors_with_editor_theme=false
|
||||||
|
|
|
||||||
|
|
@ -2,9 +2,10 @@
|
||||||
|
|
||||||
importer="texture"
|
importer="texture"
|
||||||
type="CompressedTexture2D"
|
type="CompressedTexture2D"
|
||||||
uid="uid://516qh3duc33g"
|
uid="uid://iiocbe4njv5s"
|
||||||
path="res://.godot/imported/icon_picker.svg-9f872a162ed3e0053283f4bf299ac645.ctex"
|
path="res://.godot/imported/icon_picker.svg-9f872a162ed3e0053283f4bf299ac645.ctex"
|
||||||
metadata={
|
metadata={
|
||||||
|
"has_editor_variant": true,
|
||||||
"vram_texture": false
|
"vram_texture": false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -33,5 +34,5 @@ process/hdr_clamp_exposure=false
|
||||||
process/size_limit=0
|
process/size_limit=0
|
||||||
detect_3d/compress_to=1
|
detect_3d/compress_to=1
|
||||||
svg/scale=1.0
|
svg/scale=1.0
|
||||||
editor/scale_with_editor_scale=false
|
editor/scale_with_editor_scale=true
|
||||||
editor/convert_colors_with_editor_theme=false
|
editor/convert_colors_with_editor_theme=false
|
||||||
|
|
|
||||||
|
|
@ -2,9 +2,10 @@
|
||||||
|
|
||||||
importer="texture"
|
importer="texture"
|
||||||
type="CompressedTexture2D"
|
type="CompressedTexture2D"
|
||||||
uid="uid://wor7v0l4vc6t"
|
uid="uid://73eyg81dnj2e"
|
||||||
path="res://.godot/imported/icon_picker_checked.svg-4e271ac1a29c979a28440c683998675e.ctex"
|
path="res://.godot/imported/icon_picker_checked.svg-4e271ac1a29c979a28440c683998675e.ctex"
|
||||||
metadata={
|
metadata={
|
||||||
|
"has_editor_variant": true,
|
||||||
"vram_texture": false
|
"vram_texture": false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -33,5 +34,5 @@ process/hdr_clamp_exposure=false
|
||||||
process/size_limit=0
|
process/size_limit=0
|
||||||
detect_3d/compress_to=1
|
detect_3d/compress_to=1
|
||||||
svg/scale=1.0
|
svg/scale=1.0
|
||||||
editor/scale_with_editor_scale=false
|
editor/scale_with_editor_scale=true
|
||||||
editor/convert_colors_with_editor_theme=false
|
editor/convert_colors_with_editor_theme=false
|
||||||
|
|
|
||||||
|
|
@ -2,9 +2,10 @@
|
||||||
|
|
||||||
importer="texture"
|
importer="texture"
|
||||||
type="CompressedTexture2D"
|
type="CompressedTexture2D"
|
||||||
uid="uid://bx2wxkso083ht"
|
uid="uid://28jdhtmo0tcm"
|
||||||
path="res://.godot/imported/icon_spray.svg-d9864c87d5d420aa9f80c0d3fdc80e87.ctex"
|
path="res://.godot/imported/icon_spray.svg-d9864c87d5d420aa9f80c0d3fdc80e87.ctex"
|
||||||
metadata={
|
metadata={
|
||||||
|
"has_editor_variant": true,
|
||||||
"vram_texture": false
|
"vram_texture": false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -33,5 +34,5 @@ process/hdr_clamp_exposure=false
|
||||||
process/size_limit=0
|
process/size_limit=0
|
||||||
detect_3d/compress_to=1
|
detect_3d/compress_to=1
|
||||||
svg/scale=1.0
|
svg/scale=1.0
|
||||||
editor/scale_with_editor_scale=false
|
editor/scale_with_editor_scale=true
|
||||||
editor/convert_colors_with_editor_theme=false
|
editor/convert_colors_with_editor_theme=false
|
||||||
|
|
|
||||||
|
|
@ -2,9 +2,10 @@
|
||||||
|
|
||||||
importer="texture"
|
importer="texture"
|
||||||
type="CompressedTexture2D"
|
type="CompressedTexture2D"
|
||||||
uid="uid://b03x10iou1m5"
|
uid="uid://cyfcx5hwray0d"
|
||||||
path="res://.godot/imported/icon_terrain3d.svg-39252bb986e607c413d93e00ee31a619.ctex"
|
path="res://.godot/imported/icon_terrain3d.svg-39252bb986e607c413d93e00ee31a619.ctex"
|
||||||
metadata={
|
metadata={
|
||||||
|
"has_editor_variant": true,
|
||||||
"vram_texture": false
|
"vram_texture": false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -33,5 +34,5 @@ process/hdr_clamp_exposure=false
|
||||||
process/size_limit=0
|
process/size_limit=0
|
||||||
detect_3d/compress_to=1
|
detect_3d/compress_to=1
|
||||||
svg/scale=1.0
|
svg/scale=1.0
|
||||||
editor/scale_with_editor_scale=false
|
editor/scale_with_editor_scale=true
|
||||||
editor/convert_colors_with_editor_theme=false
|
editor/convert_colors_with_editor_theme=false
|
||||||
|
|
|
||||||
|
|
@ -2,9 +2,10 @@
|
||||||
|
|
||||||
importer="texture"
|
importer="texture"
|
||||||
type="CompressedTexture2D"
|
type="CompressedTexture2D"
|
||||||
uid="uid://gt2l3qdaivl2"
|
uid="uid://1bsjjcq0cv08"
|
||||||
path="res://.godot/imported/icon_terrain_layer_material.svg-f3d447e84f51556fc60c5f0bd3f57443.ctex"
|
path="res://.godot/imported/icon_terrain_layer_material.svg-f3d447e84f51556fc60c5f0bd3f57443.ctex"
|
||||||
metadata={
|
metadata={
|
||||||
|
"has_editor_variant": true,
|
||||||
"vram_texture": false
|
"vram_texture": false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -33,5 +34,5 @@ process/hdr_clamp_exposure=false
|
||||||
process/size_limit=0
|
process/size_limit=0
|
||||||
detect_3d/compress_to=1
|
detect_3d/compress_to=1
|
||||||
svg/scale=1.0
|
svg/scale=1.0
|
||||||
editor/scale_with_editor_scale=false
|
editor/scale_with_editor_scale=true
|
||||||
editor/convert_colors_with_editor_theme=false
|
editor/convert_colors_with_editor_theme=false
|
||||||
|
|
|
||||||
|
|
@ -2,9 +2,10 @@
|
||||||
|
|
||||||
importer="texture"
|
importer="texture"
|
||||||
type="CompressedTexture2D"
|
type="CompressedTexture2D"
|
||||||
uid="uid://ccywk15q2ad8q"
|
uid="uid://hsoj3vbpdg6d"
|
||||||
path="res://.godot/imported/icon_terrain_material.svg-ea0cde03d29920e042a4043982714cbe.ctex"
|
path="res://.godot/imported/icon_terrain_material.svg-ea0cde03d29920e042a4043982714cbe.ctex"
|
||||||
metadata={
|
metadata={
|
||||||
|
"has_editor_variant": true,
|
||||||
"vram_texture": false
|
"vram_texture": false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -33,5 +34,5 @@ process/hdr_clamp_exposure=false
|
||||||
process/size_limit=0
|
process/size_limit=0
|
||||||
detect_3d/compress_to=1
|
detect_3d/compress_to=1
|
||||||
svg/scale=1.0
|
svg/scale=1.0
|
||||||
editor/scale_with_editor_scale=false
|
editor/scale_with_editor_scale=true
|
||||||
editor/convert_colors_with_editor_theme=false
|
editor/convert_colors_with_editor_theme=false
|
||||||
|
|
|
||||||
|
|
@ -2,9 +2,10 @@
|
||||||
|
|
||||||
importer="texture"
|
importer="texture"
|
||||||
type="CompressedTexture2D"
|
type="CompressedTexture2D"
|
||||||
uid="uid://bv4f7kijr12yr"
|
uid="uid://cidqhk7wrkcl7"
|
||||||
path="res://.godot/imported/icon_wetness.svg-6c67b1e4e9435c1aa106bee56f000e06.ctex"
|
path="res://.godot/imported/icon_wetness.svg-6c67b1e4e9435c1aa106bee56f000e06.ctex"
|
||||||
metadata={
|
metadata={
|
||||||
|
"has_editor_variant": true,
|
||||||
"vram_texture": false
|
"vram_texture": false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -33,5 +34,5 @@ process/hdr_clamp_exposure=false
|
||||||
process/size_limit=0
|
process/size_limit=0
|
||||||
detect_3d/compress_to=1
|
detect_3d/compress_to=1
|
||||||
svg/scale=1.0
|
svg/scale=1.0
|
||||||
editor/scale_with_editor_scale=false
|
editor/scale_with_editor_scale=true
|
||||||
editor/convert_colors_with_editor_theme=false
|
editor/convert_colors_with_editor_theme=false
|
||||||
|
|
|
||||||
|
|
@ -3,5 +3,5 @@
|
||||||
name="Terrain3D"
|
name="Terrain3D"
|
||||||
description="A high performance, editable terrain system for Godot 4."
|
description="A high performance, editable terrain system for Godot 4."
|
||||||
author="Cory Petkovsek & Roope Palmroos"
|
author="Cory Petkovsek & Roope Palmroos"
|
||||||
version="0.9.1"
|
version="0.9.2-dev"
|
||||||
script="editor/editor.gd"
|
script="editor.gd"
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
|
@tool
|
||||||
extends PanelContainer
|
extends PanelContainer
|
||||||
|
|
||||||
|
signal placement_changed(index: int)
|
||||||
signal resource_changed(resource: Resource, index: int)
|
signal resource_changed(resource: Resource, index: int)
|
||||||
signal resource_inspected(resource: Resource)
|
signal resource_inspected(resource: Resource)
|
||||||
signal resource_selected
|
signal resource_selected
|
||||||
|
|
@ -8,36 +9,55 @@ signal resource_selected
|
||||||
var list: ListContainer
|
var list: ListContainer
|
||||||
var entries: Array[ListEntry]
|
var entries: Array[ListEntry]
|
||||||
var selected_index: int = 0
|
var selected_index: int = 0
|
||||||
|
var focus_style: StyleBox
|
||||||
|
|
||||||
|
@onready var placement_option: OptionButton = $VBox/PlacementHBox/Options
|
||||||
|
@onready var placement_pin: Button = $VBox/PlacementHBox/Pinned
|
||||||
|
@onready var size_slider: HSlider = $VBox/SizeSlider
|
||||||
|
|
||||||
|
|
||||||
func _init() -> void:
|
func _ready() -> void:
|
||||||
|
placement_option.item_selected.connect(_on_placement_selected)
|
||||||
|
size_slider.value_changed.connect(_on_slider_changed)
|
||||||
|
|
||||||
list = ListContainer.new()
|
list = ListContainer.new()
|
||||||
|
|
||||||
var root: VBoxContainer = VBoxContainer.new()
|
|
||||||
var scroll: ScrollContainer = ScrollContainer.new()
|
|
||||||
var label: Label = Label.new()
|
|
||||||
|
|
||||||
label.set_text("Textures")
|
|
||||||
label.set_horizontal_alignment(HORIZONTAL_ALIGNMENT_CENTER)
|
|
||||||
label.set_vertical_alignment(VERTICAL_ALIGNMENT_CENTER)
|
|
||||||
scroll.set_v_size_flags(SIZE_EXPAND_FILL)
|
|
||||||
scroll.set_h_size_flags(SIZE_EXPAND_FILL)
|
|
||||||
list.set_v_size_flags(SIZE_EXPAND_FILL)
|
list.set_v_size_flags(SIZE_EXPAND_FILL)
|
||||||
list.set_h_size_flags(SIZE_EXPAND_FILL)
|
list.set_h_size_flags(SIZE_EXPAND_FILL)
|
||||||
|
$VBox/ScrollContainer.add_child(list)
|
||||||
scroll.add_child(list)
|
|
||||||
root.add_child(label)
|
|
||||||
root.add_child(scroll)
|
|
||||||
add_child(root)
|
|
||||||
|
|
||||||
set_custom_minimum_size(Vector2(256, 448))
|
|
||||||
|
|
||||||
|
# Copy theme from the editor, but since its a tool script, avoid saving icon resources in tscn
|
||||||
func _ready() -> void:
|
if EditorScript.new().get_editor_interface().get_edited_scene_root() != self:
|
||||||
get_child(0).get_child(0).set("theme_override_styles/normal", get_theme_stylebox("bg", "EditorInspectorCategory"))
|
set("theme_override_styles/panel", get_theme_stylebox("panel", "Panel"))
|
||||||
get_child(0).get_child(0).set("theme_override_fonts/font", get_theme_font("bold", "EditorFonts"))
|
$VBox/Label.set("theme_override_styles/normal", get_theme_stylebox("bg", "EditorInspectorCategory"))
|
||||||
get_child(0).get_child(0).set("theme_override_font_sizes/font_size",get_theme_font_size("bold_size", "EditorFonts"))
|
$VBox/Label.set("theme_override_fonts/font", get_theme_font("bold", "EditorFonts"))
|
||||||
set("theme_override_styles/panel", get_theme_stylebox("panel", "Panel"))
|
$VBox/Label.set("theme_override_font_sizes/font_size",get_theme_font_size("bold_size", "EditorFonts"))
|
||||||
|
placement_pin.icon = get_theme_icon("Pin", "EditorIcons")
|
||||||
|
placement_pin.text = ""
|
||||||
|
|
||||||
|
# Setup style for selected assets
|
||||||
|
focus_style = get_theme_stylebox("focus", "Button").duplicate()
|
||||||
|
focus_style.set_border_width_all(2)
|
||||||
|
focus_style.set_border_color(Color(1, 1, 1, .67))
|
||||||
|
|
||||||
|
|
||||||
|
func _on_placement_selected(index: int) -> void:
|
||||||
|
emit_signal("placement_changed", index)
|
||||||
|
|
||||||
|
|
||||||
|
func _on_slider_changed(value: float) -> void:
|
||||||
|
if list:
|
||||||
|
list.set_entry_size(value)
|
||||||
|
|
||||||
|
|
||||||
|
func move_slider(to_side: bool) -> void:
|
||||||
|
if to_side and size_slider.get_parent() != $VBox:
|
||||||
|
size_slider.reparent($VBox)
|
||||||
|
$VBox.move_child(size_slider, 2)
|
||||||
|
size_slider.custom_minimum_size = Vector2(0, 0)
|
||||||
|
elif not to_side and size_slider.get_parent() == $VBox:
|
||||||
|
size_slider.reparent($VBox/PlacementHBox)
|
||||||
|
$VBox/PlacementHBox.move_child(size_slider, 2)
|
||||||
|
size_slider.custom_minimum_size = Vector2(300, 10)
|
||||||
|
|
||||||
|
|
||||||
func clear() -> void:
|
func clear() -> void:
|
||||||
|
|
@ -49,6 +69,7 @@ func clear() -> void:
|
||||||
|
|
||||||
func add_item(p_resource: Resource = null) -> void:
|
func add_item(p_resource: Resource = null) -> void:
|
||||||
var entry: ListEntry = ListEntry.new()
|
var entry: ListEntry = ListEntry.new()
|
||||||
|
entry.focus_style = focus_style
|
||||||
var index: int = entries.size()
|
var index: int = entries.size()
|
||||||
|
|
||||||
entry.set_edited_resource(p_resource)
|
entry.set_edited_resource(p_resource)
|
||||||
|
|
@ -102,23 +123,38 @@ func notify_resource_changed(p_resource: Resource, p_index: int) -> void:
|
||||||
|
|
||||||
class ListContainer extends Container:
|
class ListContainer extends Container:
|
||||||
var height: float = 0
|
var height: float = 0
|
||||||
|
var width: float = 83
|
||||||
|
|
||||||
func _notification(p_what) -> void:
|
|
||||||
if p_what == NOTIFICATION_SORT_CHILDREN:
|
func set_entry_size(value: float) -> void:
|
||||||
height = 0
|
width = clamp(value, 56, 256)
|
||||||
var index: int = 0
|
redraw()
|
||||||
var separation: float = 4
|
|
||||||
for c in get_children():
|
|
||||||
if is_instance_valid(c):
|
func redraw() -> void:
|
||||||
var width: float = size.x / 3
|
height = 0
|
||||||
c.size = Vector2(width,width) - Vector2(separation, separation)
|
var index: int = 0
|
||||||
c.position = Vector2(index % 3, index / 3) * width + Vector2(separation/3, separation/3)
|
var separation: float = 4
|
||||||
height = max(height, c.position.y + width)
|
var columns: int = 3
|
||||||
index += 1
|
columns = clamp(size.x / width, 1, 100)
|
||||||
|
|
||||||
|
for c in get_children():
|
||||||
|
if is_instance_valid(c):
|
||||||
|
c.size = Vector2(width, width) - Vector2(separation, separation)
|
||||||
|
c.position = Vector2(index % columns, index / columns) * width + \
|
||||||
|
Vector2(separation / columns, separation / columns)
|
||||||
|
height = max(height, c.position.y + width)
|
||||||
|
index += 1
|
||||||
|
|
||||||
|
|
||||||
func _get_minimum_size() -> Vector2:
|
func _get_minimum_size() -> Vector2:
|
||||||
return Vector2(0, height)
|
return Vector2(0, height)
|
||||||
|
|
||||||
|
|
||||||
|
func _notification(p_what) -> void:
|
||||||
|
if p_what == NOTIFICATION_SORT_CHILDREN:
|
||||||
|
redraw()
|
||||||
|
|
||||||
|
|
||||||
##############################################################
|
##############################################################
|
||||||
## class ListEntry
|
## class ListEntry
|
||||||
|
|
@ -143,7 +179,7 @@ class ListEntry extends VBoxContainer:
|
||||||
@onready var clear_icon: Texture2D = get_theme_icon("Close", "EditorIcons")
|
@onready var clear_icon: Texture2D = get_theme_icon("Close", "EditorIcons")
|
||||||
@onready var edit_icon: Texture2D = get_theme_icon("Edit", "EditorIcons")
|
@onready var edit_icon: Texture2D = get_theme_icon("Edit", "EditorIcons")
|
||||||
@onready var background: StyleBox = get_theme_stylebox("pressed", "Button")
|
@onready var background: StyleBox = get_theme_stylebox("pressed", "Button")
|
||||||
@onready var focus: StyleBox = get_theme_stylebox("focus", "Button")
|
var focus_style: StyleBox
|
||||||
|
|
||||||
|
|
||||||
func _ready() -> void:
|
func _ready() -> void:
|
||||||
|
|
@ -196,11 +232,11 @@ class ListEntry extends VBoxContainer:
|
||||||
texture_filter = CanvasItem.TEXTURE_FILTER_NEAREST_WITH_MIPMAPS
|
texture_filter = CanvasItem.TEXTURE_FILTER_NEAREST_WITH_MIPMAPS
|
||||||
name_label.add_theme_font_size_override("font_size", 4 + rect.size.x/10)
|
name_label.add_theme_font_size_override("font_size", 4 + rect.size.x/10)
|
||||||
if drop_data:
|
if drop_data:
|
||||||
draw_style_box(focus, rect)
|
draw_style_box(focus_style, rect)
|
||||||
if is_hovered:
|
if is_hovered:
|
||||||
draw_rect(rect, Color(1,1,1,0.2))
|
draw_rect(rect, Color(1,1,1,0.2))
|
||||||
if is_selected:
|
if is_selected:
|
||||||
draw_style_box(focus, rect)
|
draw_style_box(focus_style, rect)
|
||||||
NOTIFICATION_MOUSE_ENTER:
|
NOTIFICATION_MOUSE_ENTER:
|
||||||
is_hovered = true
|
is_hovered = true
|
||||||
name_label.visible = true
|
name_label.visible = true
|
||||||
69
addons/terrain_3d/src/asset_dock.tscn
Normal file
69
addons/terrain_3d/src/asset_dock.tscn
Normal file
|
|
@ -0,0 +1,69 @@
|
||||||
|
[gd_scene load_steps=2 format=3 uid="uid://dkb6hii5e48m2"]
|
||||||
|
|
||||||
|
[ext_resource type="Script" path="res://addons/terrain_3d/src/asset_dock.gd" id="1_e23pg"]
|
||||||
|
|
||||||
|
[node name="Terrain3D" type="PanelContainer"]
|
||||||
|
custom_minimum_size = Vector2(256, 136)
|
||||||
|
offset_right = 256.0
|
||||||
|
offset_bottom = 128.0
|
||||||
|
script = ExtResource("1_e23pg")
|
||||||
|
|
||||||
|
[node name="VBox" type="VBoxContainer" parent="."]
|
||||||
|
layout_mode = 2
|
||||||
|
|
||||||
|
[node name="PlacementHBox" type="HBoxContainer" parent="VBox"]
|
||||||
|
layout_mode = 2
|
||||||
|
|
||||||
|
[node name="Label" type="Label" parent="VBox/PlacementHBox"]
|
||||||
|
layout_mode = 2
|
||||||
|
text = "Dock Position: "
|
||||||
|
|
||||||
|
[node name="Options" type="OptionButton" parent="VBox/PlacementHBox"]
|
||||||
|
layout_mode = 2
|
||||||
|
size_flags_horizontal = 3
|
||||||
|
item_count = 9
|
||||||
|
selected = 5
|
||||||
|
popup/item_0/text = "Left_UL"
|
||||||
|
popup/item_0/id = 0
|
||||||
|
popup/item_1/text = "Left_BL"
|
||||||
|
popup/item_1/id = 1
|
||||||
|
popup/item_2/text = "Left_UR"
|
||||||
|
popup/item_2/id = 2
|
||||||
|
popup/item_3/text = "Left_BR"
|
||||||
|
popup/item_3/id = 3
|
||||||
|
popup/item_4/text = "Right_UL"
|
||||||
|
popup/item_4/id = 4
|
||||||
|
popup/item_5/text = "Right_BL "
|
||||||
|
popup/item_5/id = 5
|
||||||
|
popup/item_6/text = "Right_UR"
|
||||||
|
popup/item_6/id = 6
|
||||||
|
popup/item_7/text = "Right_BR"
|
||||||
|
popup/item_7/id = 7
|
||||||
|
popup/item_8/text = "Bottom"
|
||||||
|
popup/item_8/id = 8
|
||||||
|
|
||||||
|
[node name="Pinned" type="Button" parent="VBox/PlacementHBox"]
|
||||||
|
layout_mode = 2
|
||||||
|
tooltip_text = "Keep panel visible even if Terrain3D is not selected. Useful for keeping dock floating."
|
||||||
|
toggle_mode = true
|
||||||
|
text = "P"
|
||||||
|
flat = true
|
||||||
|
|
||||||
|
[node name="Label" type="Label" parent="VBox"]
|
||||||
|
layout_mode = 2
|
||||||
|
theme_override_font_sizes/font_size = 16
|
||||||
|
text = "Textures"
|
||||||
|
horizontal_alignment = 1
|
||||||
|
vertical_alignment = 1
|
||||||
|
|
||||||
|
[node name="SizeSlider" type="HSlider" parent="VBox"]
|
||||||
|
custom_minimum_size = Vector2(100, 10)
|
||||||
|
layout_mode = 2
|
||||||
|
min_value = 56.0
|
||||||
|
max_value = 256.0
|
||||||
|
value = 83.0
|
||||||
|
|
||||||
|
[node name="ScrollContainer" type="ScrollContainer" parent="VBox"]
|
||||||
|
layout_mode = 2
|
||||||
|
size_flags_horizontal = 3
|
||||||
|
size_flags_vertical = 3
|
||||||
|
|
@ -1,13 +1,13 @@
|
||||||
[gd_scene load_steps=2 format=3 uid="uid://bhvrrmb8bk1bt"]
|
[gd_scene load_steps=2 format=3 uid="uid://bhvrrmb8bk1bt"]
|
||||||
|
|
||||||
[ext_resource type="Script" path="res://addons/terrain_3d/editor/components/bake_lod_dialog.gd" id="1_57670"]
|
[ext_resource type="Script" path="res://addons/terrain_3d/src/bake_lod_dialog.gd" id="1_sf76d"]
|
||||||
|
|
||||||
[node name="bake_lod_dialog" type="ConfirmationDialog"]
|
[node name="bake_lod_dialog" type="ConfirmationDialog"]
|
||||||
title = "Bake Terrain3D Mesh"
|
title = "Bake Terrain3D Mesh"
|
||||||
position = Vector2i(0, 36)
|
position = Vector2i(0, 36)
|
||||||
size = Vector2i(400, 115)
|
size = Vector2i(400, 115)
|
||||||
visible = true
|
visible = true
|
||||||
script = ExtResource("1_57670")
|
script = ExtResource("1_sf76d")
|
||||||
|
|
||||||
[node name="VBoxContainer" type="VBoxContainer" parent="."]
|
[node name="VBoxContainer" type="VBoxContainer" parent="."]
|
||||||
anchors_preset = 15
|
anchors_preset = 15
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
extends Node
|
extends Node
|
||||||
|
|
||||||
const BakeLodDialog: PackedScene = preload("res://addons/terrain_3d/editor/components/bake_lod_dialog.tscn")
|
const BakeLodDialog: PackedScene = preload("res://addons/terrain_3d/src/bake_lod_dialog.tscn")
|
||||||
const BAKE_MESH_DESCRIPTION: String = "This will create a child MeshInstance3D. LOD4+ is recommended. LOD0 is slow and dense with vertices every 1 unit. It is not an optimal mesh."
|
const BAKE_MESH_DESCRIPTION: String = "This will create a child MeshInstance3D. LOD4+ is recommended. LOD0 is slow and dense with vertices every 1 unit. It is not an optimal mesh."
|
||||||
const BAKE_OCCLUDER_DESCRIPTION: String = "This will create a child OccluderInstance3D. LOD4+ is recommended and will take 5+ seconds per region to generate. LOD0 is unnecessarily dense and slow."
|
const BAKE_OCCLUDER_DESCRIPTION: String = "This will create a child OccluderInstance3D. LOD4+ is recommended and will take 5+ seconds per region to generate. LOD0 is unnecessarily dense and slow."
|
||||||
const SET_UP_NAVIGATION_DESCRIPTION: String = "This operation will:
|
const SET_UP_NAVIGATION_DESCRIPTION: String = "This operation will:
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
extends Object
|
extends Object
|
||||||
|
|
||||||
const WINDOW_SCENE: String = "res://addons/terrain_3d/editor/components/channel_packer.tscn"
|
const WINDOW_SCENE: String = "res://addons/terrain_3d/src/channel_packer.tscn"
|
||||||
const TEMPLATE_PATH: String = "res://addons/terrain_3d/editor/components/channel_packer_import_template.txt"
|
const TEMPLATE_PATH: String = "res://addons/terrain_3d/src/channel_packer_import_template.txt"
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
IMAGE_ALBEDO,
|
IMAGE_ALBEDO,
|
||||||
|
|
@ -198,7 +198,7 @@ func _pack_textures(p_rgb_image: Image, p_a_image: Image, p_dst_path: String, p_
|
||||||
_show_error("Textures must be the same size.")
|
_show_error("Textures must be the same size.")
|
||||||
return
|
return
|
||||||
|
|
||||||
var output_image: Image = Terrain3D.pack_image(p_rgb_image, p_a_image, p_invert_green)
|
var output_image: Image = Terrain3DUtil.pack_image(p_rgb_image, p_a_image, p_invert_green)
|
||||||
|
|
||||||
if not output_image:
|
if not output_image:
|
||||||
_show_error("Failed to pack textures.")
|
_show_error("Failed to pack textures.")
|
||||||
|
|
@ -1,10 +1,10 @@
|
||||||
extends "res://addons/terrain_3d/editor/components/operation_builder.gd"
|
extends "res://addons/terrain_3d/src/operation_builder.gd"
|
||||||
|
|
||||||
|
|
||||||
const PointPicker: Script = preload("res://addons/terrain_3d/editor/components/point_picker.gd")
|
const MultiPicker: Script = preload("res://addons/terrain_3d/src/multi_picker.gd")
|
||||||
|
|
||||||
|
|
||||||
func _get_point_picker() -> PointPicker:
|
func _get_point_picker() -> MultiPicker:
|
||||||
return tool_settings.settings["gradient_points"]
|
return tool_settings.settings["gradient_points"]
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -20,10 +20,6 @@ func _init() -> void:
|
||||||
icon_picker = load(ICON_PICKER)
|
icon_picker = load(ICON_PICKER)
|
||||||
icon_picker_checked = load(ICON_PICKER_CHECKED)
|
icon_picker_checked = load(ICON_PICKER_CHECKED)
|
||||||
|
|
||||||
var label := Label.new()
|
|
||||||
label.text = "Points:"
|
|
||||||
add_child(label)
|
|
||||||
|
|
||||||
points.resize(MAX_POINTS)
|
points.resize(MAX_POINTS)
|
||||||
|
|
||||||
for i in range(MAX_POINTS):
|
for i in range(MAX_POINTS):
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
extends RefCounted
|
extends RefCounted
|
||||||
|
|
||||||
|
|
||||||
const ToolSettings: Script = preload("res://addons/terrain_3d/editor/components/tool_settings.gd")
|
const ToolSettings: Script = preload("res://addons/terrain_3d/src/tool_settings.gd")
|
||||||
|
|
||||||
|
|
||||||
var tool_settings: ToolSettings
|
var tool_settings: ToolSettings
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
extends HBoxContainer
|
extends HBoxContainer
|
||||||
|
|
||||||
|
|
||||||
const Baker: Script = preload("res://addons/terrain_3d/editor/components/baker.gd")
|
const Baker: Script = preload("res://addons/terrain_3d/src/baker.gd")
|
||||||
const Packer: Script = preload("res://addons/terrain_3d/editor/components/channel_packer.gd")
|
const Packer: Script = preload("res://addons/terrain_3d/src/channel_packer.gd")
|
||||||
|
|
||||||
var plugin: EditorPlugin
|
var plugin: EditorPlugin
|
||||||
var menu_button: MenuButton = MenuButton.new()
|
var menu_button: MenuButton = MenuButton.new()
|
||||||
645
addons/terrain_3d/src/tool_settings.gd
Normal file
645
addons/terrain_3d/src/tool_settings.gd
Normal file
|
|
@ -0,0 +1,645 @@
|
||||||
|
extends PanelContainer
|
||||||
|
|
||||||
|
signal picking(type, callback)
|
||||||
|
signal setting_changed
|
||||||
|
|
||||||
|
enum Layout {
|
||||||
|
HORIZONTAL,
|
||||||
|
VERTICAL,
|
||||||
|
GRID,
|
||||||
|
}
|
||||||
|
|
||||||
|
enum SettingType {
|
||||||
|
CHECKBOX,
|
||||||
|
COLOR_SELECT,
|
||||||
|
DOUBLE_SLIDER,
|
||||||
|
OPTION,
|
||||||
|
PICKER,
|
||||||
|
MULTI_PICKER,
|
||||||
|
SLIDER,
|
||||||
|
TYPE_MAX,
|
||||||
|
}
|
||||||
|
|
||||||
|
const MultiPicker: Script = preload("res://addons/terrain_3d/src/multi_picker.gd")
|
||||||
|
const DEFAULT_BRUSH: String = "circle0.exr"
|
||||||
|
const BRUSH_PATH: String = "res://addons/terrain_3d/brushes"
|
||||||
|
const PICKER_ICON: String = "res://addons/terrain_3d/icons/icon_picker.svg"
|
||||||
|
|
||||||
|
# Add settings flags
|
||||||
|
const NONE: int = 0x0
|
||||||
|
const ALLOW_LARGER: int = 0x1
|
||||||
|
const ALLOW_SMALLER: int = 0x2
|
||||||
|
const ALLOW_OUT_OF_BOUNDS: int = 0x3 # LARGER|SMALLER
|
||||||
|
const NO_LABEL: int = 0x4
|
||||||
|
const ADD_SEPARATOR: int = 0x8
|
||||||
|
const ADD_SPACER: int = 0x10
|
||||||
|
|
||||||
|
var brush_preview_material: ShaderMaterial
|
||||||
|
var select_brush_button: Button
|
||||||
|
|
||||||
|
var main_list: HBoxContainer
|
||||||
|
var advanced_list: VBoxContainer
|
||||||
|
var height_list: VBoxContainer
|
||||||
|
var scale_list: VBoxContainer
|
||||||
|
var rotation_list: VBoxContainer
|
||||||
|
var color_list: VBoxContainer
|
||||||
|
var settings: Dictionary = {}
|
||||||
|
|
||||||
|
|
||||||
|
func _ready() -> void:
|
||||||
|
main_list = HBoxContainer.new()
|
||||||
|
add_child(main_list, true)
|
||||||
|
|
||||||
|
## Common Settings
|
||||||
|
add_brushes(main_list)
|
||||||
|
|
||||||
|
add_setting({ "name":"size", "type":SettingType.SLIDER, "list":main_list, "default":50, "unit":"m",
|
||||||
|
"range":Vector3(2, 200, 1), "flags":ALLOW_LARGER|ADD_SPACER })
|
||||||
|
|
||||||
|
add_setting({ "name":"strength", "type":SettingType.SLIDER, "list":main_list, "default":10,
|
||||||
|
"unit":"%", "range":Vector3(1, 100, 1), "flags":ALLOW_LARGER })
|
||||||
|
|
||||||
|
add_setting({ "name":"enable", "type":SettingType.CHECKBOX, "list":main_list, "default":true })
|
||||||
|
|
||||||
|
add_setting({ "name":"height", "type":SettingType.SLIDER, "list":main_list, "default":50,
|
||||||
|
"unit":"m", "range":Vector3(-500, 500, 0.1), "flags":ALLOW_OUT_OF_BOUNDS })
|
||||||
|
add_setting({ "name":"height_picker", "type":SettingType.PICKER, "list":main_list,
|
||||||
|
"default":Terrain3DEditor.HEIGHT, "flags":NO_LABEL })
|
||||||
|
|
||||||
|
add_setting({ "name":"color", "type":SettingType.COLOR_SELECT, "list":main_list,
|
||||||
|
"default":Color.WHITE, "flags":ADD_SEPARATOR })
|
||||||
|
add_setting({ "name":"color_picker", "type":SettingType.PICKER, "list":main_list,
|
||||||
|
"default":Terrain3DEditor.COLOR, "flags":NO_LABEL })
|
||||||
|
|
||||||
|
add_setting({ "name":"roughness", "type":SettingType.SLIDER, "list":main_list, "default":0,
|
||||||
|
"unit":"%", "range":Vector3(-100, 100, 1), "flags":ADD_SEPARATOR })
|
||||||
|
add_setting({ "name":"roughness_picker", "type":SettingType.PICKER, "list":main_list,
|
||||||
|
"default":Terrain3DEditor.ROUGHNESS, "flags":NO_LABEL })
|
||||||
|
|
||||||
|
add_setting({ "name":"enable_texture", "label":"Texture", "type":SettingType.CHECKBOX,
|
||||||
|
"list":main_list, "default":true })
|
||||||
|
|
||||||
|
add_setting({ "name":"enable_angle", "label":"Angle", "type":SettingType.CHECKBOX,
|
||||||
|
"list":main_list, "default":true, "flags":ADD_SEPARATOR })
|
||||||
|
add_setting({ "name":"angle", "type":SettingType.SLIDER, "list":main_list, "default":0,
|
||||||
|
"unit":"%", "range":Vector3(0, 337.5, 22.5), "flags":NO_LABEL })
|
||||||
|
add_setting({ "name":"angle_picker", "type":SettingType.PICKER, "list":main_list,
|
||||||
|
"default":Terrain3DEditor.ANGLE, "flags":NO_LABEL })
|
||||||
|
add_setting({ "name":"dynamic_angle", "label":"Dynamic", "type":SettingType.CHECKBOX,
|
||||||
|
"list":main_list, "default":false, "flags":ADD_SPACER })
|
||||||
|
|
||||||
|
add_setting({ "name":"enable_scale", "label":"Scale ±", "type":SettingType.CHECKBOX,
|
||||||
|
"list":main_list, "default":true, "flags":ADD_SEPARATOR })
|
||||||
|
add_setting({ "name":"scale", "label":"±", "type":SettingType.SLIDER, "list":main_list, "default":0,
|
||||||
|
"unit":"%", "range":Vector3(-60, 80, 20), "flags":NO_LABEL })
|
||||||
|
add_setting({ "name":"scale_picker", "type":SettingType.PICKER, "list":main_list,
|
||||||
|
"default":Terrain3DEditor.SCALE, "flags":NO_LABEL })
|
||||||
|
|
||||||
|
## Slope
|
||||||
|
add_setting({ "name":"slope", "type":SettingType.DOUBLE_SLIDER, "list":main_list,
|
||||||
|
"default":0, "unit":"°", "range":Vector3(0, 180, 1) })
|
||||||
|
add_setting({ "name":"gradient_points", "type":SettingType.MULTI_PICKER, "label":"Points",
|
||||||
|
"list":main_list, "default":Terrain3DEditor.HEIGHT, "flags":ADD_SEPARATOR })
|
||||||
|
add_setting({ "name":"drawable", "type":SettingType.CHECKBOX, "list":main_list, "default":false,
|
||||||
|
"flags":ADD_SEPARATOR })
|
||||||
|
settings["drawable"].toggled.connect(_on_drawable_toggled)
|
||||||
|
|
||||||
|
var spacer: Control = Control.new()
|
||||||
|
spacer.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||||||
|
main_list.add_child(spacer, true)
|
||||||
|
|
||||||
|
## Advanced Settings Menu
|
||||||
|
advanced_list = create_submenu(main_list, "Advanced", Layout.VERTICAL)
|
||||||
|
add_setting({ "name":"automatic_regions", "type":SettingType.CHECKBOX, "list":advanced_list,
|
||||||
|
"default":true })
|
||||||
|
add_setting({ "name":"align_to_view", "type":SettingType.CHECKBOX, "list":advanced_list,
|
||||||
|
"default":true })
|
||||||
|
add_setting({ "name":"show_cursor_while_painting", "type":SettingType.CHECKBOX, "list":advanced_list,
|
||||||
|
"default":true })
|
||||||
|
advanced_list.add_child(HSeparator.new(), true)
|
||||||
|
add_setting({ "name":"gamma", "type":SettingType.SLIDER, "list":advanced_list, "default":1.0,
|
||||||
|
"unit":"γ", "range":Vector3(0.1, 2.0, 0.01) })
|
||||||
|
add_setting({ "name":"jitter", "type":SettingType.SLIDER, "list":advanced_list, "default":50,
|
||||||
|
"unit":"%", "range":Vector3(0, 100, 1) })
|
||||||
|
|
||||||
|
|
||||||
|
func create_submenu(p_parent: Control, p_button_name: String, p_layout: Layout) -> Container:
|
||||||
|
var menu_button: Button = Button.new()
|
||||||
|
menu_button.set_text(p_button_name)
|
||||||
|
menu_button.set_toggle_mode(true)
|
||||||
|
menu_button.set_v_size_flags(SIZE_SHRINK_CENTER)
|
||||||
|
menu_button.toggled.connect(_on_show_submenu.bind(menu_button))
|
||||||
|
|
||||||
|
var submenu: PopupPanel = PopupPanel.new()
|
||||||
|
submenu.popup_hide.connect(menu_button.set_pressed_no_signal.bind(false))
|
||||||
|
var panel_style: StyleBox = get_theme_stylebox("panel", "PopupMenu").duplicate()
|
||||||
|
panel_style.set_content_margin_all(10)
|
||||||
|
submenu.set("theme_override_styles/panel", panel_style)
|
||||||
|
|
||||||
|
# Pop up menu on hover, hide on exit
|
||||||
|
menu_button.mouse_entered.connect(_on_show_submenu.bind(true, menu_button))
|
||||||
|
menu_button.mouse_exited.connect(_on_show_submenu.bind(false, menu_button))
|
||||||
|
submenu.mouse_exited.connect(_on_show_submenu.bind(false, menu_button))
|
||||||
|
|
||||||
|
var sublist: Container
|
||||||
|
match(p_layout):
|
||||||
|
Layout.GRID:
|
||||||
|
sublist = GridContainer.new()
|
||||||
|
Layout.VERTICAL:
|
||||||
|
sublist = VBoxContainer.new()
|
||||||
|
Layout.HORIZONTAL, _:
|
||||||
|
sublist = HBoxContainer.new()
|
||||||
|
|
||||||
|
p_parent.add_child(menu_button, true)
|
||||||
|
menu_button.add_child(submenu, true)
|
||||||
|
submenu.add_child(sublist, true)
|
||||||
|
|
||||||
|
return sublist
|
||||||
|
|
||||||
|
|
||||||
|
func _on_show_submenu(p_toggled: bool, p_button: Button) -> void:
|
||||||
|
# Don't show if mouse already down (from painting)
|
||||||
|
if p_toggled and Input.is_mouse_button_pressed(MOUSE_BUTTON_LEFT):
|
||||||
|
return
|
||||||
|
|
||||||
|
# Hide menu if mouse is not in button or panel
|
||||||
|
var button_rect: Rect2 = Rect2(p_button.get_screen_transform().origin, p_button.get_global_rect().size)
|
||||||
|
var in_button: bool = button_rect.has_point(DisplayServer.mouse_get_position())
|
||||||
|
var panel: PopupPanel = p_button.get_child(0)
|
||||||
|
var panel_rect: Rect2 = Rect2(panel.position, panel.size)
|
||||||
|
var in_panel: bool = panel_rect.has_point(DisplayServer.mouse_get_position())
|
||||||
|
if not p_toggled and ( in_button or in_panel ):
|
||||||
|
return
|
||||||
|
|
||||||
|
var popup: PopupPanel = p_button.get_child(0)
|
||||||
|
var popup_pos: Vector2 = p_button.get_screen_transform().origin
|
||||||
|
popup.set_visible(p_toggled)
|
||||||
|
popup_pos.y -= popup.get_size().y
|
||||||
|
popup.set_position(popup_pos)
|
||||||
|
|
||||||
|
|
||||||
|
func add_brushes(p_parent: Control) -> void:
|
||||||
|
var brush_list: GridContainer = create_submenu(p_parent, "Brush", Layout.GRID)
|
||||||
|
brush_list.name = "BrushList"
|
||||||
|
|
||||||
|
var brush_button_group: ButtonGroup = ButtonGroup.new()
|
||||||
|
brush_button_group.pressed.connect(_on_setting_changed)
|
||||||
|
var default_brush_btn: Button
|
||||||
|
|
||||||
|
var dir: DirAccess = DirAccess.open(BRUSH_PATH)
|
||||||
|
if dir:
|
||||||
|
dir.list_dir_begin()
|
||||||
|
var file_name = dir.get_next()
|
||||||
|
while file_name != "":
|
||||||
|
if !dir.current_is_dir() and file_name.ends_with(".exr"):
|
||||||
|
var img: Image = Image.load_from_file(BRUSH_PATH + "/" + file_name)
|
||||||
|
img = Terrain3DUtil.black_to_alpha(img)
|
||||||
|
var tex: ImageTexture = ImageTexture.create_from_image(img)
|
||||||
|
|
||||||
|
var btn: Button = Button.new()
|
||||||
|
btn.set_custom_minimum_size(Vector2.ONE * 100)
|
||||||
|
btn.set_button_icon(tex)
|
||||||
|
btn.set_meta("image", img)
|
||||||
|
btn.set_expand_icon(true)
|
||||||
|
btn.set_material(_get_brush_preview_material())
|
||||||
|
btn.set_toggle_mode(true)
|
||||||
|
btn.set_button_group(brush_button_group)
|
||||||
|
btn.mouse_entered.connect(_on_brush_hover.bind(true, btn))
|
||||||
|
btn.mouse_exited.connect(_on_brush_hover.bind(false, btn))
|
||||||
|
brush_list.add_child(btn, true)
|
||||||
|
if file_name == DEFAULT_BRUSH:
|
||||||
|
default_brush_btn = btn
|
||||||
|
|
||||||
|
var lbl: Label = Label.new()
|
||||||
|
btn.name = file_name.get_basename().to_pascal_case()
|
||||||
|
btn.add_child(lbl, true)
|
||||||
|
lbl.text = btn.name
|
||||||
|
lbl.visible = false
|
||||||
|
lbl.position.y = 70
|
||||||
|
lbl.add_theme_color_override("font_shadow_color", Color.BLACK)
|
||||||
|
lbl.add_theme_constant_override("shadow_offset_x", 1)
|
||||||
|
lbl.add_theme_constant_override("shadow_offset_y", 1)
|
||||||
|
lbl.add_theme_font_size_override("font_size", 16)
|
||||||
|
|
||||||
|
file_name = dir.get_next()
|
||||||
|
|
||||||
|
brush_list.columns = sqrt(brush_list.get_child_count()) + 2
|
||||||
|
|
||||||
|
if not default_brush_btn:
|
||||||
|
default_brush_btn = brush_button_group.get_buttons()[0]
|
||||||
|
default_brush_btn.set_pressed(true)
|
||||||
|
|
||||||
|
settings["brush"] = brush_button_group
|
||||||
|
|
||||||
|
select_brush_button = brush_list.get_parent().get_parent()
|
||||||
|
# Optionally erase the main brush button text and replace it with the texture
|
||||||
|
# select_brush_button.set_button_icon(default_brush_btn.get_button_icon())
|
||||||
|
# select_brush_button.set_custom_minimum_size(Vector2.ONE * 36)
|
||||||
|
# select_brush_button.set_icon_alignment(HORIZONTAL_ALIGNMENT_CENTER)
|
||||||
|
# select_brush_button.set_expand_icon(true)
|
||||||
|
|
||||||
|
|
||||||
|
func _on_brush_hover(p_hovering: bool, p_button: Button) -> void:
|
||||||
|
if p_button.get_child_count() > 0:
|
||||||
|
var child = p_button.get_child(0)
|
||||||
|
if child is Label:
|
||||||
|
if p_hovering:
|
||||||
|
child.visible = true
|
||||||
|
else:
|
||||||
|
child.visible = false
|
||||||
|
|
||||||
|
|
||||||
|
func _on_pick(p_type: Terrain3DEditor.Tool) -> void:
|
||||||
|
emit_signal("picking", p_type, _on_picked)
|
||||||
|
|
||||||
|
|
||||||
|
func _on_picked(p_type: Terrain3DEditor.Tool, p_color: Color, p_global_position: Vector3) -> void:
|
||||||
|
match p_type:
|
||||||
|
Terrain3DEditor.HEIGHT:
|
||||||
|
settings["height"].value = p_color.r if not is_nan(p_color.r) else 0
|
||||||
|
Terrain3DEditor.COLOR:
|
||||||
|
settings["color"].color = p_color if not is_nan(p_color.r) else Color.WHITE
|
||||||
|
Terrain3DEditor.ROUGHNESS:
|
||||||
|
# 200... -.5 converts 0,1 to -100,100
|
||||||
|
settings["roughness"].value = round(200 * (p_color.a - 0.5)) if not is_nan(p_color.r) else 0.499
|
||||||
|
Terrain3DEditor.ANGLE:
|
||||||
|
settings["angle"].value = p_color.r
|
||||||
|
Terrain3DEditor.SCALE:
|
||||||
|
settings["scale"].value = p_color.r
|
||||||
|
_on_setting_changed()
|
||||||
|
|
||||||
|
|
||||||
|
func _on_point_pick(p_type: Terrain3DEditor.Tool, p_name: String) -> void:
|
||||||
|
assert(p_type == Terrain3DEditor.HEIGHT)
|
||||||
|
emit_signal("picking", p_type, _on_point_picked.bind(p_name))
|
||||||
|
|
||||||
|
|
||||||
|
func _on_point_picked(p_type: Terrain3DEditor.Tool, p_color: Color, p_global_position: Vector3, p_name: String) -> void:
|
||||||
|
assert(p_type == Terrain3DEditor.HEIGHT)
|
||||||
|
|
||||||
|
var point: Vector3 = p_global_position
|
||||||
|
point.y = p_color.r
|
||||||
|
settings[p_name].add_point(point)
|
||||||
|
_on_setting_changed()
|
||||||
|
|
||||||
|
|
||||||
|
func add_setting(p_args: Dictionary) -> void:
|
||||||
|
var p_name: StringName = p_args.get("name", "")
|
||||||
|
var p_label: String = p_args.get("label", "") # Optional replacement for name
|
||||||
|
var p_type: SettingType = p_args.get("type", SettingType.TYPE_MAX)
|
||||||
|
var p_list: Control = p_args.get("list")
|
||||||
|
var p_default: Variant = p_args.get("default")
|
||||||
|
var p_suffix: String = p_args.get("unit", "")
|
||||||
|
var p_range: Vector3 = p_args.get("range", Vector3(0, 0, 1))
|
||||||
|
var p_minimum: float = p_range.x
|
||||||
|
var p_maximum: float = p_range.y
|
||||||
|
var p_step: float = p_range.z
|
||||||
|
var p_flags: int = p_args.get("flags", NONE)
|
||||||
|
|
||||||
|
if p_name.is_empty() or p_type == SettingType.TYPE_MAX:
|
||||||
|
return
|
||||||
|
|
||||||
|
var container: HBoxContainer = HBoxContainer.new()
|
||||||
|
container.set_v_size_flags(SIZE_EXPAND_FILL)
|
||||||
|
var control: Control # Houses the setting to be saved
|
||||||
|
var pending_children: Array[Control]
|
||||||
|
|
||||||
|
match p_type:
|
||||||
|
SettingType.CHECKBOX:
|
||||||
|
var checkbox := CheckBox.new()
|
||||||
|
checkbox.set_pressed_no_signal(p_default)
|
||||||
|
checkbox.pressed.connect(_on_setting_changed)
|
||||||
|
pending_children.push_back(checkbox)
|
||||||
|
control = checkbox
|
||||||
|
|
||||||
|
SettingType.COLOR_SELECT:
|
||||||
|
var picker := ColorPickerButton.new()
|
||||||
|
picker.set_custom_minimum_size(Vector2(100, 25))
|
||||||
|
picker.color = Color.WHITE
|
||||||
|
picker.edit_alpha = false
|
||||||
|
picker.get_picker().set_color_mode(ColorPicker.MODE_HSV)
|
||||||
|
picker.color_changed.connect(_on_setting_changed)
|
||||||
|
var popup: PopupPanel = picker.get_popup()
|
||||||
|
popup.mouse_exited.connect(Callable(func(p): p.hide()).bind(popup))
|
||||||
|
pending_children.push_back(picker)
|
||||||
|
control = picker
|
||||||
|
|
||||||
|
SettingType.PICKER:
|
||||||
|
var button := Button.new()
|
||||||
|
button.icon = load(PICKER_ICON)
|
||||||
|
button.tooltip_text = "Pick value from the Terrain"
|
||||||
|
button.pressed.connect(_on_pick.bind(p_default))
|
||||||
|
pending_children.push_back(button)
|
||||||
|
control = button
|
||||||
|
|
||||||
|
SettingType.MULTI_PICKER:
|
||||||
|
var multi_picker: HBoxContainer = MultiPicker.new()
|
||||||
|
multi_picker.pressed.connect(_on_point_pick.bind(p_default, p_name))
|
||||||
|
multi_picker.value_changed.connect(_on_setting_changed)
|
||||||
|
pending_children.push_back(multi_picker)
|
||||||
|
control = multi_picker
|
||||||
|
|
||||||
|
SettingType.OPTION:
|
||||||
|
var option := OptionButton.new()
|
||||||
|
for i in int(p_maximum):
|
||||||
|
option.add_item("a", i)
|
||||||
|
option.selected = p_minimum
|
||||||
|
option.item_selected.connect(_on_setting_changed)
|
||||||
|
pending_children.push_back(option)
|
||||||
|
control = option
|
||||||
|
|
||||||
|
SettingType.SLIDER, SettingType.DOUBLE_SLIDER:
|
||||||
|
var slider: Control
|
||||||
|
if p_type == SettingType.SLIDER:
|
||||||
|
# Create an editable value box
|
||||||
|
var spin_slider := EditorSpinSlider.new()
|
||||||
|
spin_slider.set_flat(false)
|
||||||
|
spin_slider.set_hide_slider(true)
|
||||||
|
spin_slider.value_changed.connect(_on_setting_changed)
|
||||||
|
spin_slider.set_max(p_maximum)
|
||||||
|
spin_slider.set_min(p_minimum)
|
||||||
|
spin_slider.set_step(p_step)
|
||||||
|
spin_slider.set_value(p_default)
|
||||||
|
spin_slider.set_suffix(p_suffix)
|
||||||
|
spin_slider.set_h_size_flags(SIZE_SHRINK_CENTER)
|
||||||
|
spin_slider.set_v_size_flags(SIZE_SHRINK_CENTER)
|
||||||
|
spin_slider.set_custom_minimum_size(Vector2(75, 0))
|
||||||
|
|
||||||
|
# Create horizontal slider linked to the above box
|
||||||
|
slider = HSlider.new()
|
||||||
|
slider.share(spin_slider)
|
||||||
|
if p_flags & ALLOW_LARGER:
|
||||||
|
slider.set_allow_greater(true)
|
||||||
|
if p_flags & ALLOW_SMALLER:
|
||||||
|
slider.set_allow_lesser(true)
|
||||||
|
pending_children.push_back(slider)
|
||||||
|
pending_children.push_back(spin_slider)
|
||||||
|
control = spin_slider
|
||||||
|
|
||||||
|
else: # DOUBLE_SLIDER
|
||||||
|
var label := Label.new()
|
||||||
|
label.set_horizontal_alignment(HORIZONTAL_ALIGNMENT_CENTER)
|
||||||
|
label.set_vertical_alignment(VERTICAL_ALIGNMENT_CENTER)
|
||||||
|
label.set_custom_minimum_size(Vector2(75, 0))
|
||||||
|
slider = DoubleSlider.new()
|
||||||
|
slider.label = label
|
||||||
|
slider.suffix = p_suffix
|
||||||
|
slider.setting_changed.connect(_on_setting_changed)
|
||||||
|
pending_children.push_back(slider)
|
||||||
|
pending_children.push_back(label)
|
||||||
|
control = slider
|
||||||
|
|
||||||
|
slider.set_max(p_maximum)
|
||||||
|
slider.set_min(p_minimum)
|
||||||
|
slider.set_step(p_step)
|
||||||
|
slider.set_value(p_default)
|
||||||
|
slider.set_v_size_flags(SIZE_SHRINK_CENTER)
|
||||||
|
slider.set_h_size_flags(SIZE_SHRINK_END | SIZE_EXPAND)
|
||||||
|
slider.set_custom_minimum_size(Vector2(60, 10))
|
||||||
|
|
||||||
|
control.name = p_name.to_pascal_case()
|
||||||
|
settings[p_name] = control
|
||||||
|
|
||||||
|
# Setup button labels
|
||||||
|
if not (p_flags & NO_LABEL):
|
||||||
|
# Labels are actually buttons styled to look like labels
|
||||||
|
var label := Button.new()
|
||||||
|
label.set("theme_override_styles/normal", get_theme_stylebox("normal", "Label"))
|
||||||
|
label.set("theme_override_styles/hover", get_theme_stylebox("normal", "Label"))
|
||||||
|
label.set("theme_override_styles/pressed", get_theme_stylebox("normal", "Label"))
|
||||||
|
label.set("theme_override_styles/focus", get_theme_stylebox("normal", "Label"))
|
||||||
|
label.pressed.connect(_on_label_pressed.bind(p_name, p_default))
|
||||||
|
if p_label.is_empty():
|
||||||
|
label.set_text(p_name.capitalize() + ": ")
|
||||||
|
else:
|
||||||
|
label.set_text(p_label.capitalize() + ": ")
|
||||||
|
pending_children.push_front(label)
|
||||||
|
|
||||||
|
# Add separators to front
|
||||||
|
if p_flags & ADD_SEPARATOR:
|
||||||
|
pending_children.push_front(VSeparator.new())
|
||||||
|
if p_flags & ADD_SPACER:
|
||||||
|
var spacer := Control.new()
|
||||||
|
spacer.set_custom_minimum_size(Vector2(5, 0))
|
||||||
|
pending_children.push_front(spacer)
|
||||||
|
|
||||||
|
# Add all children to container and list
|
||||||
|
for child in pending_children:
|
||||||
|
container.add_child(child, true)
|
||||||
|
p_list.add_child(container, true)
|
||||||
|
|
||||||
|
|
||||||
|
# If label button is pressed, reset value to default or toggle checkbox
|
||||||
|
func _on_label_pressed(p_name: String, p_default: Variant) -> void:
|
||||||
|
var control: Control = settings.get(p_name)
|
||||||
|
if not control:
|
||||||
|
return
|
||||||
|
if control is CheckBox:
|
||||||
|
set_setting(p_name, !control.button_pressed)
|
||||||
|
elif p_default != null:
|
||||||
|
set_setting(p_name, p_default)
|
||||||
|
|
||||||
|
|
||||||
|
func get_settings() -> Dictionary:
|
||||||
|
var dict: Dictionary
|
||||||
|
for key in settings.keys():
|
||||||
|
dict[key] = get_setting(key)
|
||||||
|
return dict
|
||||||
|
|
||||||
|
|
||||||
|
func get_setting(p_setting: String) -> Variant:
|
||||||
|
var object: Object = settings.get(p_setting)
|
||||||
|
var value: Variant
|
||||||
|
if object is Range:
|
||||||
|
value = object.get_value()
|
||||||
|
# Adjust widths of all sliders on update of values
|
||||||
|
var digits: float = count_digits(value)
|
||||||
|
var width: float = clamp( (1 + count_digits(value)) * 19., 50, 80) * clamp(EditorInterface.get_editor_scale(), .9, 2)
|
||||||
|
object.set_custom_minimum_size(Vector2(width, 0))
|
||||||
|
elif object is DoubleSlider:
|
||||||
|
value = Vector2(object.get_min_value(), object.get_max_value())
|
||||||
|
elif object is ButtonGroup:
|
||||||
|
var img: Image = object.get_pressed_button().get_meta("image")
|
||||||
|
var tex: Texture2D = object.get_pressed_button().get_button_icon()
|
||||||
|
value = [ img, tex ]
|
||||||
|
elif object is CheckBox:
|
||||||
|
value = object.is_pressed()
|
||||||
|
elif object is ColorPickerButton:
|
||||||
|
value = object.color
|
||||||
|
elif object is MultiPicker:
|
||||||
|
value = object.get_points()
|
||||||
|
if value == null:
|
||||||
|
value = 0
|
||||||
|
return value
|
||||||
|
|
||||||
|
|
||||||
|
func set_setting(p_setting: String, p_value: Variant) -> void:
|
||||||
|
var object: Object = settings.get(p_setting)
|
||||||
|
if object is Range:
|
||||||
|
object.set_value(p_value)
|
||||||
|
elif object is DoubleSlider: # Expects p_value is Vector2
|
||||||
|
object.set_min_value(p_value.x)
|
||||||
|
object.set_max_value(p_value.y)
|
||||||
|
elif object is ButtonGroup: # Expects p_value is Array [ "button name", boolean ]
|
||||||
|
if p_value is Array and p_value.size() == 2:
|
||||||
|
for button in object.get_buttons():
|
||||||
|
if button.name == p_value[0]:
|
||||||
|
button.button_pressed = p_value[1]
|
||||||
|
elif object is CheckBox:
|
||||||
|
object.button_pressed = p_value
|
||||||
|
elif object is ColorPickerButton:
|
||||||
|
object.color = p_value
|
||||||
|
elif object is MultiPicker: # Expects p_value is PackedVector3Array
|
||||||
|
object.points = p_value
|
||||||
|
_on_setting_changed(object)
|
||||||
|
|
||||||
|
|
||||||
|
func show_settings(p_settings: PackedStringArray) -> void:
|
||||||
|
for setting in settings.keys():
|
||||||
|
var object: Object = settings[setting]
|
||||||
|
if object is Control:
|
||||||
|
if setting in p_settings:
|
||||||
|
object.get_parent().show()
|
||||||
|
else:
|
||||||
|
object.get_parent().hide()
|
||||||
|
if select_brush_button:
|
||||||
|
if not "brush" in p_settings:
|
||||||
|
select_brush_button.hide()
|
||||||
|
else:
|
||||||
|
select_brush_button.show()
|
||||||
|
|
||||||
|
|
||||||
|
func _on_setting_changed(p_data: Variant = null) -> void:
|
||||||
|
# If a button was clicked on a submenu
|
||||||
|
if p_data is Button and p_data.get_parent().get_parent() is PopupPanel:
|
||||||
|
if p_data.get_parent().name == "BrushList":
|
||||||
|
# Optionally Set selected brush texture in main brush button
|
||||||
|
# p_data.get_parent().get_parent().get_parent().set_button_icon(p_data.get_button_icon())
|
||||||
|
# Hide popup
|
||||||
|
p_data.get_parent().get_parent().set_visible(false)
|
||||||
|
# Hide label
|
||||||
|
if p_data.get_child_count() > 0:
|
||||||
|
p_data.get_child(0).visible = false
|
||||||
|
|
||||||
|
emit_signal("setting_changed")
|
||||||
|
|
||||||
|
|
||||||
|
func _on_drawable_toggled(p_button_pressed: bool) -> void:
|
||||||
|
if not p_button_pressed:
|
||||||
|
settings["gradient_points"].clear()
|
||||||
|
|
||||||
|
|
||||||
|
func _get_brush_preview_material() -> ShaderMaterial:
|
||||||
|
if !brush_preview_material:
|
||||||
|
brush_preview_material = ShaderMaterial.new()
|
||||||
|
var shader: Shader = Shader.new()
|
||||||
|
|
||||||
|
var code: String = "shader_type canvas_item;\n"
|
||||||
|
code += "varying vec4 v_vertex_color;\n"
|
||||||
|
code += "void vertex() {\n"
|
||||||
|
code += " v_vertex_color = COLOR;\n"
|
||||||
|
code += "}\n"
|
||||||
|
code += "void fragment(){\n"
|
||||||
|
code += " vec4 tex = texture(TEXTURE, UV);\n"
|
||||||
|
code += " COLOR.a *= pow(tex.r, 0.666);\n"
|
||||||
|
code += " COLOR.rgb = v_vertex_color.rgb;\n"
|
||||||
|
code += "}\n"
|
||||||
|
|
||||||
|
shader.set_code(code)
|
||||||
|
brush_preview_material.set_shader(shader)
|
||||||
|
return brush_preview_material
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# Counts digits of a number including negative sign, decimal points, and up to 3 decimals
|
||||||
|
func count_digits(p_value: float) -> int:
|
||||||
|
var count: int = 1
|
||||||
|
for i in range(5, 0, -1):
|
||||||
|
if abs(p_value) >= pow(10, i):
|
||||||
|
count = i+1
|
||||||
|
break
|
||||||
|
if p_value - floor(p_value) >= .1:
|
||||||
|
count += 1 # For the decimal
|
||||||
|
if p_value*10 - floor(p_value*10.) >= .1:
|
||||||
|
count += 1
|
||||||
|
if p_value*100 - floor(p_value*100.) >= .1:
|
||||||
|
count += 1
|
||||||
|
if p_value*1000 - floor(p_value*1000.) >= .1:
|
||||||
|
count += 1
|
||||||
|
# Negative sign
|
||||||
|
if p_value < 0:
|
||||||
|
count += 1
|
||||||
|
return count
|
||||||
|
|
||||||
|
|
||||||
|
#### Sub Class DoubleSlider
|
||||||
|
|
||||||
|
class DoubleSlider extends Range:
|
||||||
|
signal setting_changed(Vector2)
|
||||||
|
var label: Label
|
||||||
|
var suffix: String
|
||||||
|
var grabbed: bool = false
|
||||||
|
var _max_value: float
|
||||||
|
# TODO Needs to clamp min and max values. Currently allows max slider to go negative.
|
||||||
|
|
||||||
|
func _gui_input(p_event: InputEvent) -> void:
|
||||||
|
if p_event is InputEventMouseButton:
|
||||||
|
if p_event.get_button_index() == MOUSE_BUTTON_LEFT:
|
||||||
|
grabbed = p_event.is_pressed()
|
||||||
|
set_min_max(p_event.get_position().x)
|
||||||
|
|
||||||
|
if p_event is InputEventMouseMotion:
|
||||||
|
if grabbed:
|
||||||
|
set_min_max(p_event.get_position().x)
|
||||||
|
|
||||||
|
|
||||||
|
func _notification(p_what: int) -> void:
|
||||||
|
if p_what == NOTIFICATION_RESIZED:
|
||||||
|
pass
|
||||||
|
if p_what == NOTIFICATION_DRAW:
|
||||||
|
var bg: StyleBox = get_theme_stylebox("slider", "HSlider")
|
||||||
|
var bg_height: float = bg.get_minimum_size().y
|
||||||
|
draw_style_box(bg, Rect2(Vector2(0, (size.y - bg_height) / 2), Vector2(size.x, bg_height)))
|
||||||
|
|
||||||
|
var grabber: Texture2D = get_theme_icon("grabber", "HSlider")
|
||||||
|
var area: StyleBox = get_theme_stylebox("grabber_area", "HSlider")
|
||||||
|
var h: float = size.y / 2 - grabber.get_size().y / 2
|
||||||
|
|
||||||
|
var minpos: Vector2 = Vector2((min_value / _max_value) * size.x - grabber.get_size().x / 2, h)
|
||||||
|
var maxpos: Vector2 = Vector2((max_value / _max_value) * size.x - grabber.get_size().x / 2, h)
|
||||||
|
|
||||||
|
draw_style_box(area, Rect2(Vector2(minpos.x + grabber.get_size().x / 2, (size.y - bg_height) / 2), Vector2(maxpos.x - minpos.x, bg_height)))
|
||||||
|
|
||||||
|
draw_texture(grabber, minpos)
|
||||||
|
draw_texture(grabber, maxpos)
|
||||||
|
|
||||||
|
|
||||||
|
func set_max(p_value: float) -> void:
|
||||||
|
max_value = p_value
|
||||||
|
if _max_value == 0:
|
||||||
|
_max_value = max_value
|
||||||
|
update_label()
|
||||||
|
|
||||||
|
|
||||||
|
func set_min_max(p_xpos: float) -> void:
|
||||||
|
var mid_value_normalized: float = ((max_value + min_value) / 2.0) / _max_value
|
||||||
|
var mid_value: float = size.x * mid_value_normalized
|
||||||
|
var min_active: bool = p_xpos < mid_value
|
||||||
|
var xpos_ranged: float = snappedf((p_xpos / size.x) * _max_value, step)
|
||||||
|
|
||||||
|
if min_active:
|
||||||
|
min_value = xpos_ranged
|
||||||
|
else:
|
||||||
|
max_value = xpos_ranged
|
||||||
|
|
||||||
|
min_value = clamp(min_value, 0, max_value - 10)
|
||||||
|
max_value = clamp(max_value, min_value + 10, _max_value)
|
||||||
|
|
||||||
|
update_label()
|
||||||
|
emit_signal("setting_changed", Vector2(min_value, max_value))
|
||||||
|
queue_redraw()
|
||||||
|
|
||||||
|
|
||||||
|
func update_label() -> void:
|
||||||
|
if label:
|
||||||
|
label.set_text(str(min_value) + suffix + "/" + str(max_value) + suffix)
|
||||||
|
|
@ -24,7 +24,7 @@ var tool_group: ButtonGroup = ButtonGroup.new()
|
||||||
|
|
||||||
|
|
||||||
func _init() -> void:
|
func _init() -> void:
|
||||||
set_custom_minimum_size(Vector2(32, 0))
|
set_custom_minimum_size(Vector2(20, 0))
|
||||||
|
|
||||||
|
|
||||||
func _ready() -> void:
|
func _ready() -> void:
|
||||||
|
|
@ -59,6 +59,7 @@ func add_tool_button(p_tool: Terrain3DEditor.Tool, p_operation: Terrain3DEditor.
|
||||||
p_tip: String, p_icon: Texture2D, p_group: ButtonGroup) -> void:
|
p_tip: String, p_icon: Texture2D, p_group: ButtonGroup) -> void:
|
||||||
|
|
||||||
var button: Button = Button.new()
|
var button: Button = Button.new()
|
||||||
|
button.set_name(p_tip.to_pascal_case())
|
||||||
button.set_meta("Tool", p_tool)
|
button.set_meta("Tool", p_tool)
|
||||||
button.set_meta("Operation", p_operation)
|
button.set_meta("Operation", p_operation)
|
||||||
button.set_tooltip_text(p_tip)
|
button.set_tooltip_text(p_tip)
|
||||||
|
|
@ -3,12 +3,11 @@ extends Node
|
||||||
|
|
||||||
|
|
||||||
# Includes
|
# Includes
|
||||||
const Toolbar: Script = preload("res://addons/terrain_3d/editor/components/toolbar.gd")
|
const Toolbar: Script = preload("res://addons/terrain_3d/src/toolbar.gd")
|
||||||
const ToolSettings: Script = preload("res://addons/terrain_3d/editor/components/tool_settings.gd")
|
const ToolSettings: Script = preload("res://addons/terrain_3d/src/tool_settings.gd")
|
||||||
const TerrainTools: Script = preload("res://addons/terrain_3d/editor/components/terrain_tools.gd")
|
const TerrainTools: Script = preload("res://addons/terrain_3d/src/terrain_tools.gd")
|
||||||
const OperationBuilder: Script = preload("res://addons/terrain_3d/editor/components/operation_builder.gd")
|
const OperationBuilder: Script = preload("res://addons/terrain_3d/src/operation_builder.gd")
|
||||||
const GradientOperationBuilder: Script = preload("res://addons/terrain_3d/editor/components/gradient_operation_builder.gd")
|
const GradientOperationBuilder: Script = preload("res://addons/terrain_3d/src/gradient_operation_builder.gd")
|
||||||
const RING1: String = "res://addons/terrain_3d/editor/brushes/ring1.exr"
|
|
||||||
const COLOR_RAISE := Color.WHITE
|
const COLOR_RAISE := Color.WHITE
|
||||||
const COLOR_LOWER := Color.BLACK
|
const COLOR_LOWER := Color.BLACK
|
||||||
const COLOR_SMOOTH := Color(0.5, 0, .1)
|
const COLOR_SMOOTH := Color(0.5, 0, .1)
|
||||||
|
|
@ -26,6 +25,8 @@ const COLOR_PICK_COLOR := Color.WHITE
|
||||||
const COLOR_PICK_HEIGHT := Color.DARK_RED
|
const COLOR_PICK_HEIGHT := Color.DARK_RED
|
||||||
const COLOR_PICK_ROUGH := Color.ROYAL_BLUE
|
const COLOR_PICK_ROUGH := Color.ROYAL_BLUE
|
||||||
|
|
||||||
|
const RING1: String = "res://addons/terrain_3d/brushes/ring1.exr"
|
||||||
|
@onready var ring_texture := ImageTexture.create_from_image(Terrain3DUtil.black_to_alpha(Image.load_from_file(RING1)))
|
||||||
|
|
||||||
var plugin: EditorPlugin # Actually Terrain3DEditorPlugin, but Godot still has CRC errors
|
var plugin: EditorPlugin # Actually Terrain3DEditorPlugin, but Godot still has CRC errors
|
||||||
var toolbar: Toolbar
|
var toolbar: Toolbar
|
||||||
|
|
@ -40,14 +41,13 @@ var decal_timer: Timer
|
||||||
var gradient_decals: Array[Decal]
|
var gradient_decals: Array[Decal]
|
||||||
var brush_data: Dictionary
|
var brush_data: Dictionary
|
||||||
var operation_builder: OperationBuilder
|
var operation_builder: OperationBuilder
|
||||||
@onready var picker_texture: ImageTexture = ImageTexture.create_from_image(Image.load_from_file(RING1))
|
|
||||||
|
|
||||||
|
|
||||||
func _enter_tree() -> void:
|
func _enter_tree() -> void:
|
||||||
toolbar = Toolbar.new()
|
toolbar = Toolbar.new()
|
||||||
toolbar.hide()
|
toolbar.hide()
|
||||||
toolbar.connect("tool_changed", _on_tool_changed)
|
toolbar.connect("tool_changed", _on_tool_changed)
|
||||||
|
|
||||||
toolbar_settings = ToolSettings.new()
|
toolbar_settings = ToolSettings.new()
|
||||||
toolbar_settings.connect("setting_changed", _on_setting_changed)
|
toolbar_settings.connect("setting_changed", _on_setting_changed)
|
||||||
toolbar_settings.connect("picking", _on_picking)
|
toolbar_settings.connect("picking", _on_picking)
|
||||||
|
|
@ -61,6 +61,8 @@ func _enter_tree() -> void:
|
||||||
plugin.add_control_to_container(EditorPlugin.CONTAINER_SPATIAL_EDITOR_BOTTOM, toolbar_settings)
|
plugin.add_control_to_container(EditorPlugin.CONTAINER_SPATIAL_EDITOR_BOTTOM, toolbar_settings)
|
||||||
plugin.add_control_to_container(EditorPlugin.CONTAINER_SPATIAL_EDITOR_MENU, terrain_tools)
|
plugin.add_control_to_container(EditorPlugin.CONTAINER_SPATIAL_EDITOR_MENU, terrain_tools)
|
||||||
|
|
||||||
|
_on_tool_changed(Terrain3DEditor.REGION, Terrain3DEditor.ADD)
|
||||||
|
|
||||||
decal = Decal.new()
|
decal = Decal.new()
|
||||||
add_child(decal)
|
add_child(decal)
|
||||||
decal_timer = Timer.new()
|
decal_timer = Timer.new()
|
||||||
|
|
@ -87,126 +89,93 @@ func _exit_tree() -> void:
|
||||||
|
|
||||||
func set_visible(p_visible: bool) -> void:
|
func set_visible(p_visible: bool) -> void:
|
||||||
visible = p_visible
|
visible = p_visible
|
||||||
toolbar.set_visible(p_visible and plugin.terrain)
|
|
||||||
terrain_tools.set_visible(p_visible)
|
terrain_tools.set_visible(p_visible)
|
||||||
|
toolbar.set_visible(p_visible)
|
||||||
if p_visible and plugin.terrain:
|
toolbar_settings.set_visible(p_visible)
|
||||||
p_visible = plugin.editor.get_tool() != Terrain3DEditor.REGION
|
|
||||||
toolbar_settings.set_visible(p_visible and plugin.terrain)
|
|
||||||
update_decal()
|
update_decal()
|
||||||
|
|
||||||
|
|
||||||
func _on_tool_changed(p_tool: Terrain3DEditor.Tool, p_operation: Terrain3DEditor.Operation) -> void:
|
func _on_tool_changed(p_tool: Terrain3DEditor.Tool, p_operation: Terrain3DEditor.Operation) -> void:
|
||||||
clear_picking()
|
clear_picking()
|
||||||
|
|
||||||
if not visible or not plugin.terrain:
|
# Select which settings to show. Options in tool_settings.gd:_ready
|
||||||
return
|
var to_show: PackedStringArray = []
|
||||||
|
|
||||||
if plugin.editor:
|
match p_tool:
|
||||||
plugin.editor.set_tool(p_tool)
|
Terrain3DEditor.HEIGHT:
|
||||||
plugin.editor.set_operation(p_operation)
|
to_show.push_back("brush")
|
||||||
|
to_show.push_back("size")
|
||||||
if p_tool != Terrain3DEditor.REGION:
|
to_show.push_back("strength")
|
||||||
# Select which settings to hide. Options:
|
|
||||||
# size, opactiy, height, slope, color, roughness, (height|color|roughness) picker
|
|
||||||
var to_hide: PackedStringArray = []
|
|
||||||
|
|
||||||
if p_tool == Terrain3DEditor.HEIGHT:
|
|
||||||
to_hide.push_back("color")
|
|
||||||
to_hide.push_back("color picker")
|
|
||||||
to_hide.push_back("roughness")
|
|
||||||
to_hide.push_back("roughness picker")
|
|
||||||
to_hide.push_back("slope")
|
|
||||||
to_hide.push_back("enable")
|
|
||||||
if p_operation != Terrain3DEditor.REPLACE:
|
|
||||||
to_hide.push_back("height")
|
|
||||||
to_hide.push_back("height picker")
|
|
||||||
if p_operation != Terrain3DEditor.GRADIENT:
|
|
||||||
to_hide.push_back("gradient_points")
|
|
||||||
to_hide.push_back("drawable")
|
|
||||||
|
|
||||||
elif p_tool == Terrain3DEditor.TEXTURE:
|
|
||||||
to_hide.push_back("height")
|
|
||||||
to_hide.push_back("height picker")
|
|
||||||
to_hide.push_back("gradient_points")
|
|
||||||
to_hide.push_back("drawable")
|
|
||||||
to_hide.push_back("color")
|
|
||||||
to_hide.push_back("color picker")
|
|
||||||
to_hide.push_back("roughness")
|
|
||||||
to_hide.push_back("roughness picker")
|
|
||||||
to_hide.push_back("slope")
|
|
||||||
to_hide.push_back("enable")
|
|
||||||
if p_operation == Terrain3DEditor.REPLACE:
|
if p_operation == Terrain3DEditor.REPLACE:
|
||||||
to_hide.push_back("opacity")
|
to_show.push_back("height")
|
||||||
|
to_show.push_back("height_picker")
|
||||||
|
if p_operation == Terrain3DEditor.GRADIENT:
|
||||||
|
to_show.push_back("gradient_points")
|
||||||
|
to_show.push_back("drawable")
|
||||||
|
|
||||||
|
Terrain3DEditor.TEXTURE:
|
||||||
|
to_show.push_back("brush")
|
||||||
|
to_show.push_back("size")
|
||||||
|
to_show.push_back("enable_texture")
|
||||||
|
if p_operation == Terrain3DEditor.ADD:
|
||||||
|
to_show.push_back("strength")
|
||||||
|
to_show.push_back("enable_angle")
|
||||||
|
to_show.push_back("angle")
|
||||||
|
to_show.push_back("angle_picker")
|
||||||
|
to_show.push_back("dynamic_angle")
|
||||||
|
to_show.push_back("enable_scale")
|
||||||
|
to_show.push_back("scale")
|
||||||
|
to_show.push_back("scale_picker")
|
||||||
|
|
||||||
elif p_tool == Terrain3DEditor.COLOR:
|
Terrain3DEditor.COLOR:
|
||||||
to_hide.push_back("height")
|
to_show.push_back("brush")
|
||||||
to_hide.push_back("height picker")
|
to_show.push_back("size")
|
||||||
to_hide.push_back("gradient_points")
|
to_show.push_back("strength")
|
||||||
to_hide.push_back("drawable")
|
to_show.push_back("color")
|
||||||
to_hide.push_back("roughness")
|
to_show.push_back("color_picker")
|
||||||
to_hide.push_back("roughness picker")
|
|
||||||
to_hide.push_back("slope")
|
|
||||||
to_hide.push_back("enable")
|
|
||||||
|
|
||||||
elif p_tool == Terrain3DEditor.ROUGHNESS:
|
Terrain3DEditor.ROUGHNESS:
|
||||||
to_hide.push_back("height")
|
to_show.push_back("brush")
|
||||||
to_hide.push_back("height picker")
|
to_show.push_back("size")
|
||||||
to_hide.push_back("gradient_points")
|
to_show.push_back("strength")
|
||||||
to_hide.push_back("drawable")
|
to_show.push_back("roughness")
|
||||||
to_hide.push_back("color")
|
to_show.push_back("roughness_picker")
|
||||||
to_hide.push_back("color picker")
|
|
||||||
to_hide.push_back("slope")
|
|
||||||
to_hide.push_back("enable")
|
|
||||||
|
|
||||||
elif p_tool in [ Terrain3DEditor.AUTOSHADER, Terrain3DEditor.HOLES, Terrain3DEditor.NAVIGATION ]:
|
Terrain3DEditor.AUTOSHADER, Terrain3DEditor.HOLES, Terrain3DEditor.NAVIGATION:
|
||||||
to_hide.push_back("height")
|
to_show.push_back("brush")
|
||||||
to_hide.push_back("height picker")
|
to_show.push_back("size")
|
||||||
to_hide.push_back("gradient_points")
|
to_show.push_back("enable")
|
||||||
to_hide.push_back("drawable")
|
|
||||||
to_hide.push_back("color")
|
|
||||||
to_hide.push_back("color picker")
|
|
||||||
to_hide.push_back("roughness")
|
|
||||||
to_hide.push_back("roughness picker")
|
|
||||||
to_hide.push_back("slope")
|
|
||||||
to_hide.push_back("opacity")
|
|
||||||
|
|
||||||
toolbar_settings.hide_settings(to_hide)
|
_:
|
||||||
|
pass
|
||||||
|
|
||||||
toolbar_settings.set_visible(p_tool != Terrain3DEditor.REGION)
|
# Advanced menu settings
|
||||||
|
to_show.push_back("automatic_regions")
|
||||||
|
to_show.push_back("align_to_view")
|
||||||
|
to_show.push_back("show_cursor_while_painting")
|
||||||
|
to_show.push_back("gamma")
|
||||||
|
to_show.push_back("jitter")
|
||||||
|
toolbar_settings.show_settings(to_show)
|
||||||
|
|
||||||
operation_builder = null
|
operation_builder = null
|
||||||
if p_operation == Terrain3DEditor.GRADIENT:
|
if p_operation == Terrain3DEditor.GRADIENT:
|
||||||
operation_builder = GradientOperationBuilder.new()
|
operation_builder = GradientOperationBuilder.new()
|
||||||
operation_builder.tool_settings = toolbar_settings
|
operation_builder.tool_settings = toolbar_settings
|
||||||
|
|
||||||
|
if plugin.editor:
|
||||||
|
plugin.editor.set_tool(p_tool)
|
||||||
|
plugin.editor.set_operation(p_operation)
|
||||||
|
|
||||||
_on_setting_changed()
|
_on_setting_changed()
|
||||||
plugin.update_region_grid()
|
plugin.update_region_grid()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
func _on_setting_changed() -> void:
|
func _on_setting_changed() -> void:
|
||||||
if not visible or not plugin.terrain:
|
if not plugin.asset_dock:
|
||||||
return
|
return
|
||||||
brush_data = {
|
brush_data = toolbar_settings.get_settings()
|
||||||
"size": int(toolbar_settings.get_setting("size")),
|
brush_data["strength"] /= 100.0
|
||||||
"opacity": toolbar_settings.get_setting("opacity") / 100.0,
|
brush_data["texture_index"] = plugin.asset_dock.get_selected_index()
|
||||||
"height": toolbar_settings.get_setting("height"),
|
|
||||||
"texture_index": plugin.texture_dock.get_selected_index(),
|
|
||||||
"color": toolbar_settings.get_setting("color"),
|
|
||||||
"roughness": toolbar_settings.get_setting("roughness"),
|
|
||||||
"gradient_points": toolbar_settings.get_setting("gradient_points"),
|
|
||||||
"enable": toolbar_settings.get_setting("enable"),
|
|
||||||
"automatic_regions": toolbar_settings.get_setting("automatic_regions"),
|
|
||||||
"align_to_view": toolbar_settings.get_setting("align_to_view"),
|
|
||||||
"show_cursor_while_painting": toolbar_settings.get_setting("show_cursor_while_painting"),
|
|
||||||
"gamma": toolbar_settings.get_setting("gamma"),
|
|
||||||
"jitter": toolbar_settings.get_setting("jitter"),
|
|
||||||
}
|
|
||||||
var brush_imgs: Array = toolbar_settings.get_setting("brush")
|
|
||||||
brush_data["image"] = brush_imgs[0]
|
|
||||||
brush_data["texture"] = brush_imgs[1]
|
|
||||||
|
|
||||||
update_decal()
|
update_decal()
|
||||||
plugin.editor.set_brush_data(brush_data)
|
plugin.editor.set_brush_data(brush_data)
|
||||||
|
|
||||||
|
|
@ -226,7 +195,7 @@ func update_decal() -> void:
|
||||||
else:
|
else:
|
||||||
# Wait for cursor to recenter after right-click before revealing
|
# Wait for cursor to recenter after right-click before revealing
|
||||||
# See https://github.com/godotengine/godot/issues/70098
|
# See https://github.com/godotengine/godot/issues/70098
|
||||||
await get_tree().create_timer(.05).timeout
|
await get_tree().create_timer(.05).timeout
|
||||||
decal.visible = true
|
decal.visible = true
|
||||||
|
|
||||||
decal.size = Vector3.ONE * brush_data["size"]
|
decal.size = Vector3.ONE * brush_data["size"]
|
||||||
|
|
@ -239,7 +208,7 @@ func update_decal() -> void:
|
||||||
|
|
||||||
# Set texture and color
|
# Set texture and color
|
||||||
if picking != Terrain3DEditor.TOOL_MAX:
|
if picking != Terrain3DEditor.TOOL_MAX:
|
||||||
decal.texture_albedo = picker_texture
|
decal.texture_albedo = ring_texture
|
||||||
decal.size = Vector3.ONE * 10. * plugin.terrain.get_mesh_vertex_spacing()
|
decal.size = Vector3.ONE * 10. * plugin.terrain.get_mesh_vertex_spacing()
|
||||||
match picking:
|
match picking:
|
||||||
Terrain3DEditor.HEIGHT:
|
Terrain3DEditor.HEIGHT:
|
||||||
|
|
@ -250,7 +219,7 @@ func update_decal() -> void:
|
||||||
decal.modulate = COLOR_PICK_ROUGH
|
decal.modulate = COLOR_PICK_ROUGH
|
||||||
decal.modulate.a = 1.0
|
decal.modulate.a = 1.0
|
||||||
else:
|
else:
|
||||||
decal.texture_albedo = brush_data["texture"]
|
decal.texture_albedo = brush_data["brush"][1]
|
||||||
match plugin.editor.get_tool():
|
match plugin.editor.get_tool():
|
||||||
Terrain3DEditor.HEIGHT:
|
Terrain3DEditor.HEIGHT:
|
||||||
match plugin.editor.get_operation():
|
match plugin.editor.get_operation():
|
||||||
|
|
@ -270,7 +239,7 @@ func update_decal() -> void:
|
||||||
decal.modulate = COLOR_SLOPE
|
decal.modulate = COLOR_SLOPE
|
||||||
_:
|
_:
|
||||||
decal.modulate = Color.WHITE
|
decal.modulate = Color.WHITE
|
||||||
decal.modulate.a = max(.3, brush_data["opacity"])
|
decal.modulate.a = max(.3, brush_data["strength"])
|
||||||
Terrain3DEditor.TEXTURE:
|
Terrain3DEditor.TEXTURE:
|
||||||
match plugin.editor.get_operation():
|
match plugin.editor.get_operation():
|
||||||
Terrain3DEditor.REPLACE:
|
Terrain3DEditor.REPLACE:
|
||||||
|
|
@ -278,15 +247,15 @@ func update_decal() -> void:
|
||||||
decal.modulate.a = 1.0
|
decal.modulate.a = 1.0
|
||||||
Terrain3DEditor.ADD:
|
Terrain3DEditor.ADD:
|
||||||
decal.modulate = COLOR_SPRAY
|
decal.modulate = COLOR_SPRAY
|
||||||
decal.modulate.a = max(.3, brush_data["opacity"])
|
decal.modulate.a = max(.3, brush_data["strength"])
|
||||||
_:
|
_:
|
||||||
decal.modulate = Color.WHITE
|
decal.modulate = Color.WHITE
|
||||||
Terrain3DEditor.COLOR:
|
Terrain3DEditor.COLOR:
|
||||||
decal.modulate = brush_data["color"].srgb_to_linear()*.5
|
decal.modulate = brush_data["color"].srgb_to_linear()*.5
|
||||||
decal.modulate.a = max(.3, brush_data["opacity"])
|
decal.modulate.a = max(.3, brush_data["strength"])
|
||||||
Terrain3DEditor.ROUGHNESS:
|
Terrain3DEditor.ROUGHNESS:
|
||||||
decal.modulate = COLOR_ROUGHNESS
|
decal.modulate = COLOR_ROUGHNESS
|
||||||
decal.modulate.a = max(.3, brush_data["opacity"])
|
decal.modulate.a = max(.3, brush_data["strength"])
|
||||||
Terrain3DEditor.AUTOSHADER:
|
Terrain3DEditor.AUTOSHADER:
|
||||||
decal.modulate = COLOR_AUTOSHADER
|
decal.modulate = COLOR_AUTOSHADER
|
||||||
decal.modulate.a = 1.0
|
decal.modulate.a = 1.0
|
||||||
|
|
@ -298,15 +267,15 @@ func update_decal() -> void:
|
||||||
decal.modulate.a = 1.0
|
decal.modulate.a = 1.0
|
||||||
_:
|
_:
|
||||||
decal.modulate = Color.WHITE
|
decal.modulate = Color.WHITE
|
||||||
decal.modulate.a = max(.3, brush_data["opacity"])
|
decal.modulate.a = max(.3, brush_data["strength"])
|
||||||
decal.size.y = max(1000, decal.size.y)
|
decal.size.y = max(1000, decal.size.y)
|
||||||
decal.albedo_mix = 1.0
|
decal.albedo_mix = 1.0
|
||||||
decal.cull_mask = 1 << ( plugin.terrain.get_mouse_layer() - 1 )
|
decal.cull_mask = 1 << ( plugin.terrain.get_mouse_layer() - 1 )
|
||||||
decal_timer.start()
|
decal_timer.start()
|
||||||
|
|
||||||
for gradient_decal in gradient_decals:
|
for gradient_decal in gradient_decals:
|
||||||
gradient_decal.visible = false
|
gradient_decal.visible = false
|
||||||
|
|
||||||
if plugin.editor.get_operation() == Terrain3DEditor.GRADIENT:
|
if plugin.editor.get_operation() == Terrain3DEditor.GRADIENT:
|
||||||
var index := 0
|
var index := 0
|
||||||
for point in brush_data["gradient_points"]:
|
for point in brush_data["gradient_points"]:
|
||||||
|
|
@ -320,16 +289,16 @@ func update_decal() -> void:
|
||||||
func _get_gradient_decal(index: int) -> Decal:
|
func _get_gradient_decal(index: int) -> Decal:
|
||||||
if gradient_decals.size() > index:
|
if gradient_decals.size() > index:
|
||||||
return gradient_decals[index]
|
return gradient_decals[index]
|
||||||
|
|
||||||
var gradient_decal := Decal.new()
|
var gradient_decal := Decal.new()
|
||||||
gradient_decal = Decal.new()
|
gradient_decal = Decal.new()
|
||||||
gradient_decal.texture_albedo = picker_texture
|
gradient_decal.texture_albedo = ring_texture
|
||||||
gradient_decal.modulate = COLOR_SLOPE
|
gradient_decal.modulate = COLOR_SLOPE
|
||||||
gradient_decal.size = Vector3.ONE * 10. * plugin.terrain.get_mesh_vertex_spacing()
|
gradient_decal.size = Vector3.ONE * 10. * plugin.terrain.get_mesh_vertex_spacing()
|
||||||
gradient_decal.size.y = 1000.
|
gradient_decal.size.y = 1000.
|
||||||
gradient_decal.cull_mask = decal.cull_mask
|
gradient_decal.cull_mask = decal.cull_mask
|
||||||
add_child(gradient_decal)
|
add_child(gradient_decal)
|
||||||
|
|
||||||
gradient_decals.push_back(gradient_decal)
|
gradient_decals.push_back(gradient_decal)
|
||||||
return gradient_decal
|
return gradient_decal
|
||||||
|
|
||||||
|
|
@ -351,10 +320,10 @@ func clear_picking() -> void:
|
||||||
func is_picking() -> bool:
|
func is_picking() -> bool:
|
||||||
if picking != Terrain3DEditor.TOOL_MAX:
|
if picking != Terrain3DEditor.TOOL_MAX:
|
||||||
return true
|
return true
|
||||||
|
|
||||||
if operation_builder and operation_builder.is_picking():
|
if operation_builder and operation_builder.is_picking():
|
||||||
return true
|
return true
|
||||||
|
|
||||||
return false
|
return false
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -368,12 +337,16 @@ func pick(p_global_position: Vector3) -> void:
|
||||||
color = plugin.terrain.get_storage().get_pixel(Terrain3DStorage.TYPE_COLOR, p_global_position)
|
color = plugin.terrain.get_storage().get_pixel(Terrain3DStorage.TYPE_COLOR, p_global_position)
|
||||||
Terrain3DEditor.COLOR:
|
Terrain3DEditor.COLOR:
|
||||||
color = plugin.terrain.get_storage().get_color(p_global_position)
|
color = plugin.terrain.get_storage().get_color(p_global_position)
|
||||||
|
Terrain3DEditor.ANGLE:
|
||||||
|
color = Color(plugin.terrain.get_storage().get_angle(p_global_position), 0., 0., 1.)
|
||||||
|
Terrain3DEditor.SCALE:
|
||||||
|
color = Color(plugin.terrain.get_storage().get_scale(p_global_position), 0., 0., 1.)
|
||||||
_:
|
_:
|
||||||
push_error("Unsupported picking type: ", picking)
|
push_error("Unsupported picking type: ", picking)
|
||||||
return
|
return
|
||||||
picking_callback.call(picking, color, p_global_position)
|
picking_callback.call(picking, color, p_global_position)
|
||||||
picking = Terrain3DEditor.TOOL_MAX
|
picking = Terrain3DEditor.TOOL_MAX
|
||||||
|
|
||||||
elif operation_builder and operation_builder.is_picking():
|
elif operation_builder and operation_builder.is_picking():
|
||||||
operation_builder.pick(p_global_position, plugin.terrain)
|
operation_builder.pick(p_global_position, plugin.terrain)
|
||||||
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
[configuration]
|
[configuration]
|
||||||
|
|
||||||
entry_symbol = "terrain_3d_init"
|
entry_symbol = "terrain_3d_init"
|
||||||
compatibility_minimum = 4.1
|
compatibility_minimum = 4.2
|
||||||
|
|
||||||
[libraries]
|
[libraries]
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -54,14 +54,14 @@ func start_import(p_value: bool) -> void:
|
||||||
var min_max := Vector2(0, 1)
|
var min_max := Vector2(0, 1)
|
||||||
var img: Image
|
var img: Image
|
||||||
if height_file_name:
|
if height_file_name:
|
||||||
img = Terrain3DStorage.load_image(height_file_name, ResourceLoader.CACHE_MODE_IGNORE, r16_range, r16_size)
|
img = Terrain3DUtil.load_image(height_file_name, ResourceLoader.CACHE_MODE_IGNORE, r16_range, r16_size)
|
||||||
min_max = Terrain3D.get_min_max(img)
|
min_max = Terrain3DUtil.get_min_max(img)
|
||||||
imported_images[Terrain3DStorage.TYPE_HEIGHT] = img
|
imported_images[Terrain3DStorage.TYPE_HEIGHT] = img
|
||||||
if control_file_name:
|
if control_file_name:
|
||||||
img = Terrain3DStorage.load_image(control_file_name, ResourceLoader.CACHE_MODE_IGNORE)
|
img = Terrain3DUtil.load_image(control_file_name, ResourceLoader.CACHE_MODE_IGNORE)
|
||||||
imported_images[Terrain3DStorage.TYPE_CONTROL] = img
|
imported_images[Terrain3DStorage.TYPE_CONTROL] = img
|
||||||
if color_file_name:
|
if color_file_name:
|
||||||
img = Terrain3DStorage.load_image(color_file_name, ResourceLoader.CACHE_MODE_IGNORE)
|
img = Terrain3DUtil.load_image(color_file_name, ResourceLoader.CACHE_MODE_IGNORE)
|
||||||
imported_images[Terrain3DStorage.TYPE_COLOR] = img
|
imported_images[Terrain3DStorage.TYPE_COLOR] = img
|
||||||
if texture_list.get_texture_count() == 0:
|
if texture_list.get_texture_count() == 0:
|
||||||
material.show_checkered = false
|
material.show_checkered = false
|
||||||
|
|
|
||||||
|
|
@ -1,54 +1,16 @@
|
||||||
[gd_scene load_steps=8 format=3 uid="uid://blaieaqp413k7"]
|
[gd_scene load_steps=5 format=3 uid="uid://blaieaqp413k7"]
|
||||||
|
|
||||||
[ext_resource type="Script" path="res://addons/terrain_3d/tools/importer.gd" id="1_60b8f"]
|
[ext_resource type="Script" path="res://addons/terrain_3d/tools/importer.gd" id="1_60b8f"]
|
||||||
|
|
||||||
[sub_resource type="Terrain3DStorage" id="Terrain3DStorage_5p5ir"]
|
[sub_resource type="Terrain3DStorage" id="Terrain3DStorage_rmuvl"]
|
||||||
version = 0.842
|
|
||||||
|
|
||||||
[sub_resource type="Gradient" id="Gradient_5sc5a"]
|
[sub_resource type="Terrain3DMaterial" id="Terrain3DMaterial_cjpaa"]
|
||||||
offsets = PackedFloat32Array(0.2, 1)
|
|
||||||
colors = PackedColorArray(1, 1, 1, 1, 0, 0, 0, 1)
|
|
||||||
|
|
||||||
[sub_resource type="FastNoiseLite" id="FastNoiseLite_lp4p7"]
|
|
||||||
noise_type = 2
|
|
||||||
frequency = 0.03
|
|
||||||
cellular_jitter = 3.0
|
|
||||||
cellular_return_type = 0
|
|
||||||
domain_warp_enabled = true
|
|
||||||
domain_warp_type = 1
|
|
||||||
domain_warp_amplitude = 50.0
|
|
||||||
domain_warp_fractal_type = 2
|
|
||||||
domain_warp_fractal_lacunarity = 1.5
|
|
||||||
domain_warp_fractal_gain = 1.0
|
|
||||||
|
|
||||||
[sub_resource type="NoiseTexture2D" id="NoiseTexture2D_vyjp8"]
|
|
||||||
seamless = true
|
|
||||||
color_ramp = SubResource("Gradient_5sc5a")
|
|
||||||
noise = SubResource("FastNoiseLite_lp4p7")
|
|
||||||
|
|
||||||
[sub_resource type="Terrain3DMaterial" id="Terrain3DMaterial_f0cwi"]
|
|
||||||
_shader_parameters = {
|
|
||||||
"_mouse_layer": 2147483648,
|
|
||||||
"blend_sharpness": null,
|
|
||||||
"height_blending": null,
|
|
||||||
"macro_variation1": null,
|
|
||||||
"macro_variation2": null,
|
|
||||||
"noise1_angle": null,
|
|
||||||
"noise1_offset": null,
|
|
||||||
"noise1_scale": null,
|
|
||||||
"noise2_scale": null,
|
|
||||||
"noise3_scale": null,
|
|
||||||
"noise_texture": SubResource("NoiseTexture2D_vyjp8")
|
|
||||||
}
|
|
||||||
show_checkered = true
|
show_checkered = true
|
||||||
|
|
||||||
[sub_resource type="Terrain3DTextureList" id="Terrain3DTextureList_4yf1r"]
|
[sub_resource type="Terrain3DTextureList" id="Terrain3DTextureList_yjkn1"]
|
||||||
|
|
||||||
[node name="Importer" type="Terrain3D"]
|
[node name="Importer" type="Terrain3D"]
|
||||||
storage = SubResource("Terrain3DStorage_5p5ir")
|
storage = SubResource("Terrain3DStorage_rmuvl")
|
||||||
material = SubResource("Terrain3DMaterial_f0cwi")
|
material = SubResource("Terrain3DMaterial_cjpaa")
|
||||||
texture_list = SubResource("Terrain3DTextureList_4yf1r")
|
texture_list = SubResource("Terrain3DTextureList_yjkn1")
|
||||||
script = ExtResource("1_60b8f")
|
script = ExtResource("1_60b8f")
|
||||||
import_position = Vector3(-1024, 0, -1024)
|
|
||||||
r16_range = Vector2(0, 250)
|
|
||||||
r16_size = Vector2i(2048, 2048)
|
|
||||||
|
|
|
||||||
176
addons/terrain_3d/utils/terrain_3d_objects.gd
Normal file
176
addons/terrain_3d/utils/terrain_3d_objects.gd
Normal file
|
|
@ -0,0 +1,176 @@
|
||||||
|
@tool
|
||||||
|
extends Node3D
|
||||||
|
class_name Terrain3DObjects
|
||||||
|
|
||||||
|
const TransformChangedNotifier: Script = preload("res://addons/terrain_3d/utils/transform_changed_notifier.gd")
|
||||||
|
|
||||||
|
const CHILD_HELPER_NAME: StringName = &"TransformChangedSignaller"
|
||||||
|
const CHILD_HELPER_PATH: NodePath = ^"TransformChangedSignaller"
|
||||||
|
|
||||||
|
var _editor_interface = null
|
||||||
|
var _undo_redo = null
|
||||||
|
var _terrain_id: int
|
||||||
|
var _offsets: Dictionary # Object ID -> Vector3(X, Y offset relative to terrain height, Z)
|
||||||
|
var _ignore_transform_change: bool = false
|
||||||
|
|
||||||
|
|
||||||
|
func _exit_tree() -> void:
|
||||||
|
if not Engine.is_editor_hint() or not _editor_interface:
|
||||||
|
return
|
||||||
|
|
||||||
|
child_entered_tree.disconnect(_on_child_entered_tree)
|
||||||
|
child_exiting_tree.disconnect(_on_child_exiting_tree)
|
||||||
|
|
||||||
|
for child in get_children():
|
||||||
|
_on_child_exiting_tree(child)
|
||||||
|
|
||||||
|
|
||||||
|
func editor_setup(p_plugin) -> void:
|
||||||
|
if _editor_interface:
|
||||||
|
return
|
||||||
|
|
||||||
|
_editor_interface = p_plugin.get_editor_interface()
|
||||||
|
_undo_redo = p_plugin.get_undo_redo()
|
||||||
|
|
||||||
|
for child in get_children():
|
||||||
|
_on_child_entered_tree(child)
|
||||||
|
|
||||||
|
child_entered_tree.connect(_on_child_entered_tree)
|
||||||
|
child_exiting_tree.connect(_on_child_exiting_tree)
|
||||||
|
|
||||||
|
|
||||||
|
func get_terrain() -> Terrain3D:
|
||||||
|
var terrain := instance_from_id(_terrain_id) as Terrain3D
|
||||||
|
if not terrain or terrain.is_queued_for_deletion() or not terrain.is_inside_tree():
|
||||||
|
var terrains: Array[Node] = _editor_interface.get_edited_scene_root().find_children("", "Terrain3D")
|
||||||
|
if terrains.size() > 0:
|
||||||
|
terrain = terrains[0]
|
||||||
|
_terrain_id = terrain.get_instance_id() if terrain else 0
|
||||||
|
|
||||||
|
if terrain and terrain.storage and not terrain.storage.maps_edited.is_connected(_on_maps_edited):
|
||||||
|
terrain.storage.maps_edited.connect(_on_maps_edited)
|
||||||
|
|
||||||
|
return terrain
|
||||||
|
|
||||||
|
|
||||||
|
func _get_terrain_height(p_global_position: Vector3) -> float:
|
||||||
|
var terrain: Terrain3D = get_terrain()
|
||||||
|
if not terrain or not terrain.storage:
|
||||||
|
return 0.0
|
||||||
|
var height: float = terrain.storage.get_height(p_global_position)
|
||||||
|
if is_nan(height):
|
||||||
|
return 0.0
|
||||||
|
return height
|
||||||
|
|
||||||
|
|
||||||
|
func _on_child_entered_tree(p_node: Node) -> void:
|
||||||
|
if not (p_node is Node3D):
|
||||||
|
return
|
||||||
|
|
||||||
|
assert(p_node.get_parent() == self)
|
||||||
|
|
||||||
|
var helper: TransformChangedNotifier = p_node.get_node_or_null(CHILD_HELPER_PATH)
|
||||||
|
if not helper:
|
||||||
|
helper = TransformChangedNotifier.new()
|
||||||
|
helper.name = CHILD_HELPER_NAME
|
||||||
|
p_node.add_child(helper, true, INTERNAL_MODE_BACK)
|
||||||
|
helper.transform_changed.connect(_on_child_transform_changed.bind(p_node))
|
||||||
|
assert(p_node.has_node(CHILD_HELPER_PATH))
|
||||||
|
|
||||||
|
var id: int = p_node.get_instance_id()
|
||||||
|
if not _offsets.has(id):
|
||||||
|
_update_child_offset(p_node)
|
||||||
|
else:
|
||||||
|
_update_child_position(p_node)
|
||||||
|
|
||||||
|
|
||||||
|
func _on_child_exiting_tree(p_node: Node) -> void:
|
||||||
|
if not (p_node is Node3D) or not p_node.has_node(CHILD_HELPER_PATH):
|
||||||
|
return
|
||||||
|
|
||||||
|
var helper: TransformChangedNotifier = p_node.get_node_or_null(CHILD_HELPER_PATH)
|
||||||
|
if helper:
|
||||||
|
p_node.remove_child(helper)
|
||||||
|
helper.queue_free()
|
||||||
|
|
||||||
|
|
||||||
|
func _is_node_selected(p_node: Node) -> bool:
|
||||||
|
var editor_sel = _editor_interface.get_selection()
|
||||||
|
return editor_sel.get_transformable_selected_nodes().has(p_node)
|
||||||
|
|
||||||
|
|
||||||
|
func _on_child_transform_changed(p_node: Node3D) -> void:
|
||||||
|
if _ignore_transform_change:
|
||||||
|
return
|
||||||
|
|
||||||
|
var lmb_down := Input.is_mouse_button_pressed(MOUSE_BUTTON_LEFT)
|
||||||
|
if lmb_down and (_is_node_selected(p_node) or _is_node_selected(self)):
|
||||||
|
# The user may be moving the node using gizmos.
|
||||||
|
# We should wait until they're done before updating otherwise gizmos + this node conflict.
|
||||||
|
return
|
||||||
|
|
||||||
|
if not _offsets.has(p_node.get_instance_id()):
|
||||||
|
return
|
||||||
|
|
||||||
|
var old_offset: Vector3 = _offsets[p_node.get_instance_id()]
|
||||||
|
var old_h: float = _get_terrain_height(old_offset)
|
||||||
|
var old_position: Vector3 = old_offset + Vector3(0, old_h, 0)
|
||||||
|
var new_position: Vector3 = p_node.global_position
|
||||||
|
if old_position.is_equal_approx(new_position):
|
||||||
|
return
|
||||||
|
var new_h: float = _get_terrain_height(new_position)
|
||||||
|
var new_offset: Vector3 = new_position - Vector3(0, new_h, 0)
|
||||||
|
|
||||||
|
var translate_without_reposition: bool = Input.is_key_pressed(KEY_SHIFT)
|
||||||
|
var y_changed: bool = not is_equal_approx(old_position.y, p_node.global_position.y)
|
||||||
|
if not y_changed and not translate_without_reposition:
|
||||||
|
new_offset.y = old_offset.y
|
||||||
|
new_position = new_offset + Vector3(0, new_h, 0)
|
||||||
|
|
||||||
|
# Make sure that when the user undo's the translation, the offset change gets undone too!
|
||||||
|
_undo_redo.create_action("Translate", UndoRedo.MERGE_ALL)
|
||||||
|
_undo_redo.add_do_property(self, &"_ignore_transform_change", true)
|
||||||
|
_undo_redo.add_undo_property(self, &"_ignore_transform_change", true)
|
||||||
|
_undo_redo.add_do_property(p_node, &"global_position", new_position)
|
||||||
|
_undo_redo.add_undo_property(p_node, &"global_position", old_position)
|
||||||
|
_undo_redo.add_do_method(self, &"_set_offset", p_node.get_instance_id(), new_offset)
|
||||||
|
_undo_redo.add_undo_method(self, &"_set_offset", p_node.get_instance_id(), old_offset)
|
||||||
|
_undo_redo.add_do_property(self, &"_ignore_transform_change", false)
|
||||||
|
_undo_redo.add_undo_property(self, &"_ignore_transform_change", false)
|
||||||
|
_undo_redo.commit_action()
|
||||||
|
|
||||||
|
|
||||||
|
func _set_offset(p_id: int, p_offset: Vector3) -> void:
|
||||||
|
_offsets[p_id] = p_offset
|
||||||
|
|
||||||
|
|
||||||
|
# Overwrite current offset stored for node with its current Y position relative to the terrain
|
||||||
|
func _update_child_offset(p_node: Node3D) -> void:
|
||||||
|
var position: Vector3 = global_transform * p_node.position
|
||||||
|
var h: float = _get_terrain_height(position)
|
||||||
|
var offset: Vector3 = position - Vector3(0, h, 0)
|
||||||
|
_offsets[p_node.get_instance_id()] = offset
|
||||||
|
|
||||||
|
|
||||||
|
# Overwrite node's current position with terrain height + stored offset for this node
|
||||||
|
func _update_child_position(p_node: Node3D) -> void:
|
||||||
|
if not _offsets.has(p_node.get_instance_id()):
|
||||||
|
return
|
||||||
|
|
||||||
|
var position: Vector3 = global_transform * p_node.position
|
||||||
|
var h: float = _get_terrain_height(position)
|
||||||
|
var offset: Vector3 = _offsets[p_node.get_instance_id()]
|
||||||
|
var new_position: Vector3 = global_transform.inverse() * (offset + Vector3(0, h, 0))
|
||||||
|
if not p_node.position.is_equal_approx(new_position):
|
||||||
|
p_node.position = new_position
|
||||||
|
|
||||||
|
|
||||||
|
func _on_maps_edited(p_edited_aabb: AABB) -> void:
|
||||||
|
var edited_area: AABB = p_edited_aabb.grow(1)
|
||||||
|
edited_area.position.y = -INF
|
||||||
|
edited_area.end.y = INF
|
||||||
|
|
||||||
|
for child in get_children():
|
||||||
|
var node := child as Node3D
|
||||||
|
if node && edited_area.has_point(node.global_position):
|
||||||
|
_update_child_position(node)
|
||||||
14
addons/terrain_3d/utils/transform_changed_notifier.gd
Normal file
14
addons/terrain_3d/utils/transform_changed_notifier.gd
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
@tool
|
||||||
|
extends Node3D
|
||||||
|
|
||||||
|
signal transform_changed
|
||||||
|
|
||||||
|
|
||||||
|
func _ready() -> void:
|
||||||
|
assert(Engine.is_editor_hint())
|
||||||
|
set_notify_transform(true)
|
||||||
|
|
||||||
|
|
||||||
|
func _notification(what: int) -> void:
|
||||||
|
if what == NOTIFICATION_TRANSFORM_CHANGED:
|
||||||
|
transform_changed.emit()
|
||||||
BIN
assets/fonts/Exo2/Exo2.ttf
Normal file
BIN
assets/fonts/Exo2/Exo2.ttf
Normal file
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue