generated from krampus/template-godot4
Compare commits
No commits in common. "358d7c2eb44a6c12e43c7b5a6dc891293b1d0218" and "d762bfc8f1f8f6e41afb0af3b1ba59badd7d26ba" have entirely different histories.
358d7c2eb4
...
d762bfc8f1
|
@ -1,6 +1,6 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2024 Cory Petkovsek, Roope Palmroos, and Contributors.
|
||||
Copyright (c) 2023 Cory Petkovsek, Roope Palmroos, and Contributors.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
![Terrain3D Logo](/doc/docs/images/terrain3d.jpg)
|
||||
<img src="doc/docs/images/terrain3d.png">
|
||||
|
||||
# Terrain3D
|
||||
A high performance, editable terrain system for Godot 4.
|
||||
|
@ -7,7 +7,7 @@ A high performance, editable terrain system for Godot 4.
|
|||
* Written in C++ as a GDExtension addon, which works with official engine builds
|
||||
* Can be accessed by GDScript, C#, and any language Godot supports
|
||||
* Geometric Clipmap Mesh Terrain, as used in The Witcher 3. See [System Architecture](https://terrain3d.readthedocs.io/en/stable/docs/system_architecture.html)
|
||||
* Terrains as small as 64x64m up to 65.5x65.5km (4295km^2) in variable sized regions
|
||||
* Up to 16k x 16k in 1k regions (imagine multiple islands without paying for 16k^2 vram)
|
||||
* Up to 32 textures
|
||||
* Up to 10 levels of detail
|
||||
* Foliage instancing
|
||||
|
|
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.
|
@ -7,36 +7,29 @@ extends EditorPlugin
|
|||
const UI: Script = preload("res://addons/terrain_3d/src/ui.gd")
|
||||
const RegionGizmo: Script = preload("res://addons/terrain_3d/src/region_gizmo.gd")
|
||||
const ASSET_DOCK: String = "res://addons/terrain_3d/src/asset_dock.tscn"
|
||||
|
||||
var modifier_ctrl: bool
|
||||
var modifier_alt: bool
|
||||
var modifier_shift: bool
|
||||
var _last_modifiers: int = 0
|
||||
var _input_mode: int = 0 # -1: camera move, 0: none, 1: operating
|
||||
const PS_DOCK_POSITION: String = "terrain3d/config/dock_position"
|
||||
const PS_DOCK_PINNED: String = "terrain3d/config/dock_pinned"
|
||||
|
||||
var terrain: Terrain3D
|
||||
var _last_terrain: Terrain3D
|
||||
var nav_region: NavigationRegion3D
|
||||
|
||||
var editor: Terrain3DEditor
|
||||
var editor_settings: EditorSettings
|
||||
var ui: Node # Terrain3DUI see Godot #75388
|
||||
var asset_dock: PanelContainer
|
||||
var region_gizmo: RegionGizmo
|
||||
var visible: bool
|
||||
var current_region_position: Vector2
|
||||
var mouse_global_position: Vector3 = Vector3.ZERO
|
||||
var godot_editor_window: Window # The Godot Editor window
|
||||
|
||||
|
||||
func _init() -> void:
|
||||
# Get the Godot Editor window. Structure is root:Window/EditorNode/Base Control
|
||||
godot_editor_window = EditorInterface.get_base_control().get_parent().get_parent()
|
||||
godot_editor_window.focus_entered.connect(_on_godot_focus_entered)
|
||||
# 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:
|
||||
editor = Terrain3DEditor.new()
|
||||
setup_editor_settings()
|
||||
ui = UI.new()
|
||||
ui.plugin = self
|
||||
add_child(ui)
|
||||
|
@ -56,46 +49,25 @@ func _exit_tree() -> void:
|
|||
editor.free()
|
||||
|
||||
scene_changed.disconnect(_on_scene_changed)
|
||||
godot_editor_window.focus_entered.disconnect(_on_godot_focus_entered)
|
||||
|
||||
|
||||
func _on_godot_focus_entered() -> void:
|
||||
_read_input()
|
||||
ui.update_decal()
|
||||
|
||||
|
||||
## EditorPlugin selection function call chain isn't consistent. Here's the map of calls:
|
||||
## Assume we handle Terrain3D and NavigationRegion3D
|
||||
# Click Terrain3D: _handles(Terrain3D), _make_visible(true), _edit(Terrain3D)
|
||||
# Deselect: _make_visible(false), _edit(null)
|
||||
# Click other node: _handles(OtherNode)
|
||||
# Click NavRegion3D: _handles(NavReg3D), _make_visible(true), _edit(NavReg3D)
|
||||
# Click NavRegion3D, Terrain3D: _handles(Terrain3D), _edit(Terrain3D)
|
||||
# Click Terrain3D, NavRegion3D: _handles(NavReg3D), _edit(NavReg3D)
|
||||
func _handles(p_object: Object) -> bool:
|
||||
if p_object is Terrain3D:
|
||||
return true
|
||||
elif p_object is NavigationRegion3D and is_instance_valid(_last_terrain):
|
||||
return true
|
||||
|
||||
# Terrain3DObjects requires access to EditorUndoRedoManager. The only way to make sure it
|
||||
# always has it, is to pass it in here. _edit is NOT called if the node is cut and pasted.
|
||||
elif p_object is Terrain3DObjects:
|
||||
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)
|
||||
|
||||
if is_instance_valid(_last_terrain) and _last_terrain.is_inside_tree() and p_object is NavigationRegion3D:
|
||||
return true
|
||||
|
||||
return false
|
||||
|
||||
|
||||
func _make_visible(p_visible: bool, p_redraw: bool = false) -> void:
|
||||
if p_visible and is_selected():
|
||||
ui.set_visible(true)
|
||||
asset_dock.update_dock()
|
||||
else:
|
||||
ui.set_visible(false)
|
||||
|
||||
|
||||
func _edit(p_object: Object) -> void:
|
||||
if !p_object:
|
||||
_clear()
|
||||
|
@ -105,41 +77,39 @@ func _edit(p_object: Object) -> void:
|
|||
return
|
||||
terrain = p_object
|
||||
_last_terrain = terrain
|
||||
terrain.set_plugin(self)
|
||||
terrain.set_editor(editor)
|
||||
editor.set_terrain(terrain)
|
||||
region_gizmo.set_node_3d(terrain)
|
||||
terrain.add_gizmo(region_gizmo)
|
||||
ui.set_visible(true)
|
||||
terrain.set_meta("_edit_lock_", true)
|
||||
terrain.set_plugin(self)
|
||||
|
||||
# Deprecated 0.9.3 - Remove 1.0
|
||||
if terrain.storage:
|
||||
ui.terrain_menu.directory_setup.directory_setup_popup()
|
||||
|
||||
# Get alerted when a new asset list is loaded
|
||||
# Connect to new Assets resource
|
||||
if not terrain.assets_changed.is_connected(asset_dock.update_assets):
|
||||
terrain.assets_changed.connect(asset_dock.update_assets)
|
||||
asset_dock.update_assets()
|
||||
# Get alerted when the region map changes
|
||||
if not terrain.data.region_map_changed.is_connected(update_region_grid):
|
||||
terrain.data.region_map_changed.connect(update_region_grid)
|
||||
update_region_grid()
|
||||
# Connect to new Storage resource
|
||||
if not terrain.storage_changed.is_connected(_load_storage):
|
||||
terrain.storage_changed.connect(_load_storage)
|
||||
_load_storage()
|
||||
else:
|
||||
_clear()
|
||||
|
||||
if is_terrain_valid(_last_terrain):
|
||||
if is_instance_valid(_last_terrain) and _last_terrain.is_inside_tree():
|
||||
if p_object is NavigationRegion3D:
|
||||
ui.set_visible(true, true)
|
||||
nav_region = p_object
|
||||
else:
|
||||
nav_region = null
|
||||
|
||||
|
||||
func _make_visible(p_visible: bool, p_redraw: bool = false) -> void:
|
||||
visible = p_visible
|
||||
ui.set_visible(visible)
|
||||
update_region_grid()
|
||||
asset_dock.update_dock(visible)
|
||||
|
||||
|
||||
func _clear() -> void:
|
||||
if is_terrain_valid():
|
||||
if terrain.data.region_map_changed.is_connected(update_region_grid):
|
||||
terrain.data.region_map_changed.disconnect(update_region_grid)
|
||||
terrain.storage_changed.disconnect(_load_storage)
|
||||
|
||||
terrain.clear_gizmos()
|
||||
terrain = null
|
||||
|
@ -154,11 +124,28 @@ func _forward_3d_gui_input(p_viewport_camera: Camera3D, p_event: InputEvent) ->
|
|||
if not is_terrain_valid():
|
||||
return AFTER_GUI_INPUT_PASS
|
||||
|
||||
_read_input(p_event)
|
||||
## 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
|
||||
if p_event is InputEventMouseMotion:
|
||||
## Setup active camera & viewport
|
||||
if Input.is_mouse_button_pressed(MOUSE_BUTTON_RIGHT):
|
||||
return AFTER_GUI_INPUT_PASS
|
||||
|
||||
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
|
||||
terrain.set_camera(p_viewport_camera)
|
||||
|
@ -182,131 +169,88 @@ func _forward_3d_gui_input(p_viewport_camera: Camera3D, p_event: InputEvent) ->
|
|||
else:
|
||||
# Else look for intersection with terrain
|
||||
var intersection_point: Vector3 = terrain.get_intersection(camera_pos, camera_dir)
|
||||
if intersection_point.z > 3.4e38 or is_nan(intersection_point.y): # max double or nan
|
||||
if intersection_point.z > 3.4e38 or is_nan(intersection_point.z): # max double or nan
|
||||
return AFTER_GUI_INPUT_STOP
|
||||
mouse_global_position = intersection_point
|
||||
|
||||
ui.update_decal()
|
||||
## Update decal
|
||||
ui.decal.global_position = mouse_global_position
|
||||
ui.decal.albedo_mix = 1.0
|
||||
if ui.decal_timer.is_stopped():
|
||||
ui.update_decal()
|
||||
else:
|
||||
ui.decal_timer.start()
|
||||
|
||||
if _input_mode != -1: # Not cam rotation
|
||||
## Update region highlight
|
||||
var region_position: Vector2 = ( Vector2(mouse_global_position.x, mouse_global_position.z) \
|
||||
/ (terrain.get_region_size() * terrain.get_vertex_spacing()) ).floor()
|
||||
if current_region_position != region_position:
|
||||
current_region_position = region_position
|
||||
update_region_grid()
|
||||
## Update region highlight
|
||||
var region_size = terrain.get_storage().get_region_size()
|
||||
var region_position: Vector2 = ( Vector2(mouse_global_position.x, mouse_global_position.z) \
|
||||
/ (region_size * terrain.get_mesh_vertex_spacing()) ).floor()
|
||||
if current_region_position != region_position:
|
||||
current_region_position = region_position
|
||||
update_region_grid()
|
||||
|
||||
if _input_mode > 0 and editor.is_operating():
|
||||
# Inject pressure - Relies on C++ set_brush_data() using same dictionary instance
|
||||
ui.brush_data["mouse_pressure"] = p_event.pressure
|
||||
|
||||
editor.operate(mouse_global_position, p_viewport_camera.rotation.y)
|
||||
return AFTER_GUI_INPUT_STOP
|
||||
|
||||
return AFTER_GUI_INPUT_PASS
|
||||
|
||||
ui.update_decal()
|
||||
|
||||
if p_event is InputEventMouseButton and _input_mode > 0:
|
||||
if p_event.is_pressed():
|
||||
# If picking
|
||||
if ui.is_picking():
|
||||
ui.pick(mouse_global_position)
|
||||
if not ui.operation_builder or not ui.operation_builder.is_ready():
|
||||
return AFTER_GUI_INPUT_STOP
|
||||
|
||||
# If adjusting regions
|
||||
if editor.get_tool() == Terrain3DEditor.REGION:
|
||||
# Skip regions that already exist or don't
|
||||
var has_region: bool = terrain.data.has_regionp(mouse_global_position)
|
||||
var op: int = editor.get_operation()
|
||||
if ( has_region and op == Terrain3DEditor.ADD) or \
|
||||
( not has_region and op == Terrain3DEditor.SUBTRACT ):
|
||||
return AFTER_GUI_INPUT_STOP
|
||||
|
||||
# If an automatic operation is ready to go (e.g. gradient)
|
||||
if ui.operation_builder and ui.operation_builder.is_ready():
|
||||
ui.operation_builder.apply_operation(editor, mouse_global_position, p_viewport_camera.rotation.y)
|
||||
return AFTER_GUI_INPUT_STOP
|
||||
|
||||
# Mouse clicked, start editing
|
||||
editor.start_operation(mouse_global_position)
|
||||
if Input.is_mouse_button_pressed(MOUSE_BUTTON_LEFT) and editor.is_operating():
|
||||
editor.operate(mouse_global_position, p_viewport_camera.rotation.y)
|
||||
return AFTER_GUI_INPUT_STOP
|
||||
|
||||
# _input_apply released, save undo data
|
||||
elif editor.is_operating():
|
||||
editor.stop_operation()
|
||||
return AFTER_GUI_INPUT_STOP
|
||||
elif p_event is InputEventMouseButton:
|
||||
ui.update_decal()
|
||||
|
||||
if p_event.get_button_index() == MOUSE_BUTTON_LEFT:
|
||||
if p_event.is_pressed():
|
||||
if Input.is_mouse_button_pressed(MOUSE_BUTTON_RIGHT):
|
||||
return AFTER_GUI_INPUT_STOP
|
||||
|
||||
# If picking
|
||||
if ui.is_picking():
|
||||
ui.pick(mouse_global_position)
|
||||
if not ui.operation_builder or not ui.operation_builder.is_ready():
|
||||
return AFTER_GUI_INPUT_STOP
|
||||
|
||||
# If adjusting regions
|
||||
if editor.get_tool() == Terrain3DEditor.REGION:
|
||||
# Skip regions that already exist or don't
|
||||
var has_region: bool = terrain.get_storage().has_region(mouse_global_position)
|
||||
var op: int = editor.get_operation()
|
||||
if ( has_region and op == Terrain3DEditor.ADD) or \
|
||||
( not has_region and op == Terrain3DEditor.SUBTRACT ):
|
||||
return AFTER_GUI_INPUT_STOP
|
||||
|
||||
# If an automatic operation is ready to go (e.g. gradient)
|
||||
if ui.operation_builder and ui.operation_builder.is_ready():
|
||||
ui.operation_builder.apply_operation(editor, mouse_global_position, p_viewport_camera.rotation.y)
|
||||
return AFTER_GUI_INPUT_STOP
|
||||
|
||||
# Mouse clicked, start editing
|
||||
editor.start_operation(mouse_global_position)
|
||||
editor.operate(mouse_global_position, p_viewport_camera.rotation.y)
|
||||
return AFTER_GUI_INPUT_STOP
|
||||
|
||||
elif editor.is_operating():
|
||||
# Mouse released, save undo data
|
||||
editor.stop_operation()
|
||||
return AFTER_GUI_INPUT_STOP
|
||||
|
||||
return AFTER_GUI_INPUT_PASS
|
||||
|
||||
|
||||
func _read_input(p_event: InputEvent = null) -> void:
|
||||
## Determine if user is moving camera or applying
|
||||
if Input.is_mouse_button_pressed(MOUSE_BUTTON_LEFT) or \
|
||||
p_event is InputEventMouseButton and p_event.is_released() and \
|
||||
p_event.get_button_index() == MOUSE_BUTTON_LEFT:
|
||||
_input_mode = 1
|
||||
else:
|
||||
_input_mode = 0
|
||||
|
||||
match get_setting("editors/3d/navigation/navigation_scheme", 0):
|
||||
2, 1: # Modo, Maya
|
||||
if Input.is_mouse_button_pressed(MOUSE_BUTTON_RIGHT) or \
|
||||
( Input.is_key_pressed(KEY_ALT) and Input.is_mouse_button_pressed(MOUSE_BUTTON_LEFT) ):
|
||||
_input_mode = -1
|
||||
if p_event is InputEventMouseButton and p_event.is_released() and \
|
||||
( p_event.get_button_index() == MOUSE_BUTTON_RIGHT or \
|
||||
( Input.is_key_pressed(KEY_ALT) and p_event.get_button_index() == MOUSE_BUTTON_LEFT )):
|
||||
ui.last_rmb_time = Time.get_ticks_msec()
|
||||
0, _: # Godot
|
||||
if Input.is_mouse_button_pressed(MOUSE_BUTTON_RIGHT) or \
|
||||
Input.is_mouse_button_pressed(MOUSE_BUTTON_MIDDLE):
|
||||
_input_mode = -1
|
||||
if p_event is InputEventMouseButton and p_event.is_released() and \
|
||||
( p_event.get_button_index() == MOUSE_BUTTON_RIGHT or \
|
||||
p_event.get_button_index() == MOUSE_BUTTON_MIDDLE ):
|
||||
ui.last_rmb_time = Time.get_ticks_msec()
|
||||
if _input_mode < 0:
|
||||
return
|
||||
|
||||
## Determine modifiers pressed
|
||||
modifier_shift = Input.is_key_pressed(KEY_SHIFT)
|
||||
modifier_ctrl = Input.is_key_pressed(KEY_CTRL)
|
||||
# Keybind enum: Alt,Space,Meta,Capslock
|
||||
var alt_key: int
|
||||
match get_setting("terrain3d/config/alt_key_bind", 0):
|
||||
3: alt_key = KEY_CAPSLOCK
|
||||
2: alt_key = KEY_META
|
||||
1: alt_key = KEY_SPACE
|
||||
0, _: alt_key = KEY_ALT
|
||||
modifier_alt = Input.is_key_pressed(alt_key)
|
||||
|
||||
# Return if modifiers haven't changed AND brush_data has them;
|
||||
# modifiers disappear from brush_data when clicking asset_dock (Why?)
|
||||
var current_mods: int = int(modifier_shift) | int(modifier_ctrl) << 1 | int(modifier_alt) << 2
|
||||
if _last_modifiers == current_mods and ui.brush_data.has("modifier_shift"):
|
||||
return
|
||||
|
||||
_last_modifiers = current_mods
|
||||
ui.brush_data["modifier_shift"] = modifier_shift
|
||||
ui.brush_data["modifier_ctrl"] = modifier_ctrl
|
||||
ui.brush_data["modifier_alt"] = modifier_alt
|
||||
ui.update_modifiers()
|
||||
func _load_storage() -> void:
|
||||
if terrain:
|
||||
update_region_grid()
|
||||
|
||||
|
||||
func update_region_grid() -> void:
|
||||
if not region_gizmo:
|
||||
return
|
||||
region_gizmo.set_hidden(not ui.visible)
|
||||
|
||||
region_gizmo.set_hidden(not visible)
|
||||
|
||||
if is_terrain_valid():
|
||||
region_gizmo.show_rect = editor.get_tool() == Terrain3DEditor.REGION
|
||||
region_gizmo.use_secondary_color = editor.get_operation() == Terrain3DEditor.SUBTRACT
|
||||
region_gizmo.region_position = current_region_position
|
||||
region_gizmo.region_size = terrain.get_region_size() * terrain.get_vertex_spacing()
|
||||
region_gizmo.grid = terrain.get_data().get_region_locations()
|
||||
region_gizmo.region_size = terrain.get_storage().get_region_size() * terrain.get_mesh_vertex_spacing()
|
||||
region_gizmo.grid = terrain.get_storage().get_region_offsets()
|
||||
|
||||
terrain.update_gizmos()
|
||||
return
|
||||
|
@ -334,80 +278,22 @@ func is_terrain_valid(p_terrain: Terrain3D = null) -> bool:
|
|||
t = p_terrain
|
||||
else:
|
||||
t = terrain
|
||||
if is_instance_valid(t) and t.is_inside_tree() and t.data:
|
||||
if is_instance_valid(t) and t.is_inside_tree() and t.get_storage():
|
||||
return true
|
||||
return false
|
||||
|
||||
|
||||
func is_selected() -> bool:
|
||||
var selected: Array[Node] = EditorInterface.get_selection().get_selected_nodes()
|
||||
var selected: Array[Node] = get_editor_interface().get_selection().get_selected_nodes()
|
||||
for node in selected:
|
||||
if ( is_instance_valid(_last_terrain) and node.get_instance_id() == _last_terrain.get_instance_id() ) or \
|
||||
node is Terrain3D:
|
||||
return true
|
||||
if node.get_instance_id() == _last_terrain.get_instance_id():
|
||||
return true
|
||||
|
||||
return false
|
||||
|
||||
|
||||
func select_terrain() -> void:
|
||||
if is_instance_valid(_last_terrain) and is_terrain_valid(_last_terrain) and not is_selected():
|
||||
var es: EditorSelection = EditorInterface.get_selection()
|
||||
var es: EditorSelection = get_editor_interface().get_selection()
|
||||
es.clear()
|
||||
es.add_node(_last_terrain)
|
||||
|
||||
|
||||
## Editor Settings
|
||||
|
||||
|
||||
func setup_editor_settings() -> void:
|
||||
editor_settings = EditorInterface.get_editor_settings()
|
||||
if not editor_settings.has_setting("terrain3d/config/alt_key_bind"):
|
||||
editor_settings.set("terrain3d/config/alt_key_bind", 0)
|
||||
var property_info = {
|
||||
"name": "terrain3d/config/alt_key_bind",
|
||||
"type": TYPE_INT,
|
||||
"hint": PROPERTY_HINT_ENUM,
|
||||
"hint_string": "Alt,Space,Meta,Capslock"
|
||||
}
|
||||
editor_settings.add_property_info(property_info)
|
||||
|
||||
_cleanup_old_settings()
|
||||
|
||||
|
||||
# Remove or rename old settings
|
||||
func _cleanup_old_settings() -> void:
|
||||
# Rename deprecated settings - Remove in 1.0
|
||||
var value: Variant
|
||||
var rename_arr := [ "terrain3d/config/dock_slot", "terrain3d/config/dock_tile_size",
|
||||
"terrain3d/config/dock_floating", "terrain3d/config/dock_always_on_top",
|
||||
"terrain3d/config/dock_window_size", "terrain3d/config/dock_window_position", ]
|
||||
for es: String in rename_arr:
|
||||
if editor_settings.has_setting(es):
|
||||
value = editor_settings.get_setting(es)
|
||||
editor_settings.erase(es)
|
||||
editor_settings.set_setting(es.replace("/config/dock_", "/dock/"), value)
|
||||
|
||||
# Special handling
|
||||
var es: String = "terrain3d/tool_settings/slope"
|
||||
if editor_settings.has_setting(es):
|
||||
value = editor_settings.get_setting(es)
|
||||
if typeof(value) == TYPE_FLOAT:
|
||||
editor_settings.erase(es)
|
||||
|
||||
|
||||
func set_setting(p_str: String, p_value: Variant) -> void:
|
||||
editor_settings.set_setting(p_str, p_value)
|
||||
|
||||
|
||||
func get_setting(p_str: String, p_default: Variant) -> Variant:
|
||||
if editor_settings.has_setting(p_str):
|
||||
return editor_settings.get_setting(p_str)
|
||||
else:
|
||||
return p_default
|
||||
|
||||
|
||||
func has_setting(p_str: String) -> bool:
|
||||
return editor_settings.has_setting(p_str)
|
||||
|
||||
|
||||
func erase_setting(p_str: String) -> void:
|
||||
editor_settings.erase(p_str)
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
# 1. Click import. The output window and console will report when finished.
|
||||
# 1. Clear the script from your Terrain3D node, and save your scene.
|
||||
#
|
||||
# The instance transforms are now stored in your region files.
|
||||
# The instance transforms are now stored in your Storage resource.
|
||||
#
|
||||
# Use clear_instances to erase all instances that match the assign_mesh_id.
|
||||
#
|
||||
|
|
|
@ -1,41 +1,30 @@
|
|||
// This shader is the minimum needed to allow the terrain to function, without any texturing.
|
||||
|
||||
shader_type spatial;
|
||||
render_mode blend_mix,depth_draw_opaque,cull_back,diffuse_burley,specular_schlick_ggx,skip_vertex_transform;
|
||||
render_mode blend_mix,depth_draw_opaque,cull_back,diffuse_burley,specular_schlick_ggx;
|
||||
|
||||
// Private uniforms
|
||||
uniform float _region_size = 1024.0;
|
||||
uniform float _region_texel_size = 0.0009765625; // = 1/1024
|
||||
uniform float _vertex_spacing = 1.0;
|
||||
uniform float _vertex_density = 1.0; // = 1/_vertex_spacing
|
||||
uniform int _region_map_size = 32;
|
||||
uniform int _region_map[1024];
|
||||
uniform vec2 _region_locations[1024];
|
||||
uniform float _mesh_vertex_spacing = 1.0;
|
||||
uniform float _mesh_vertex_density = 1.0; // = 1/_mesh_vertex_spacing
|
||||
uniform int _region_map_size = 16;
|
||||
uniform int _region_map[256];
|
||||
uniform vec2 _region_offsets[256];
|
||||
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;
|
||||
uniform sampler2D noise_texture : source_color, filter_linear_mipmap_anisotropic, repeat_enable;
|
||||
|
||||
uniform float _texture_uv_scale_array[32];
|
||||
uniform float _texture_detile_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
|
||||
|
||||
// Public uniforms
|
||||
uniform float vertex_normals_distance : hint_range(0, 1024) = 128.0;
|
||||
|
||||
// Varyings & Types
|
||||
varying flat vec3 v_vertex; // World coordinate vertex location
|
||||
varying flat vec3 v_camera_pos;
|
||||
varying float v_vertex_xz_dist;
|
||||
varying flat ivec3 v_region;
|
||||
varying flat vec2 v_uv_offset;
|
||||
varying flat vec2 v_uv2_offset;
|
||||
varying vec3 v_normal;
|
||||
varying float v_region_border_mask;
|
||||
|
||||
////////////////////////
|
||||
// Vertex
|
||||
|
@ -44,22 +33,26 @@ varying float v_region_border_mask;
|
|||
// Takes in UV world space coordinates, returns ivec3 with:
|
||||
// XY: (0 to _region_size) coordinates within a region
|
||||
// Z: layer index used for texturearrays, -1 if not in a region
|
||||
ivec3 get_region_uv(const vec2 uv) {
|
||||
ivec2 pos = ivec2(floor(uv * _region_texel_size)) + (_region_map_size / 2);
|
||||
int bounds = int(uint(pos.x | pos.y) < uint(_region_map_size));
|
||||
ivec3 get_region_uv(vec2 uv) {
|
||||
uv *= _region_texel_size;
|
||||
ivec2 pos = ivec2(floor(uv)) + (_region_map_size / 2);
|
||||
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;
|
||||
return ivec3(ivec2(mod(uv,_region_size)), layer_index);
|
||||
return ivec3(ivec2((uv - _region_offsets[layer_index]) * _region_size), layer_index);
|
||||
}
|
||||
|
||||
// Takes in UV2 region space coordinates, returns vec3 with:
|
||||
// XY: (0 to 1) coordinates within a region
|
||||
// Z: layer index used for texturearrays, -1 if not in a region
|
||||
vec3 get_region_uv2(const vec2 uv2) {
|
||||
// Remove Texel Offset to ensure correct region index.
|
||||
ivec2 pos = ivec2(floor(uv2 - vec2(_region_texel_size * 0.5))) + (_region_map_size / 2);
|
||||
int bounds = int(uint(pos.x | pos.y) < uint(_region_map_size));
|
||||
vec3 get_region_uv2(vec2 uv) {
|
||||
// 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 layer_index = _region_map[ pos.y * _region_map_size + pos.x ] * bounds - 1;
|
||||
return vec3(uv2 - _region_locations[layer_index], float(layer_index));
|
||||
// The return value is still texel-centered.
|
||||
return vec3(uv - _region_offsets[layer_index], float(layer_index));
|
||||
}
|
||||
|
||||
// 1 lookup
|
||||
|
@ -73,75 +66,63 @@ float get_height(vec2 uv) {
|
|||
}
|
||||
|
||||
void vertex() {
|
||||
// Get camera pos in world vertex coords
|
||||
v_camera_pos = INV_VIEW_MATRIX[3].xyz;
|
||||
|
||||
// Get vertex of flat plane in world coordinates and set world UV
|
||||
v_vertex = (MODEL_MATRIX * vec4(VERTEX, 1.0)).xyz;
|
||||
|
||||
// Camera distance to vertex on flat plane
|
||||
v_vertex_xz_dist = length(v_vertex.xz - v_camera_pos.xz);
|
||||
vec3 vertex = (MODEL_MATRIX * vec4(VERTEX, 1.0)).xyz;
|
||||
|
||||
// UV coordinates in world space. Values are 0 to _region_size within regions
|
||||
UV = round(v_vertex.xz * _vertex_density);
|
||||
|
||||
// UV coordinates in region space + texel offset. Values are 0 to 1 within regions
|
||||
UV2 = fma(UV, vec2(_region_texel_size), vec2(0.5 * _region_texel_size));
|
||||
UV = round(vertex.xz * _mesh_vertex_density);
|
||||
|
||||
// Discard vertices for Holes. 1 lookup
|
||||
v_region = get_region_uv(UV);
|
||||
uint control = texelFetch(_control_maps, v_region, 0).r;
|
||||
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 && (get_region_uv(UV - _region_texel_size) & v_region).z < 0))) {
|
||||
VERTEX.x = 0. / 0.;
|
||||
(hole || (_background_mode == 0u && region.z < 0)) ) {
|
||||
VERTEX.x = 0./0.;
|
||||
} else {
|
||||
// Set final vertex height & calculate vertex normals. 3 lookups.
|
||||
// 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
|
||||
VERTEX.y = get_height(UV2);
|
||||
v_vertex.y = VERTEX.y;
|
||||
v_normal = vec3(
|
||||
v_vertex.y - get_height(UV2 + vec2(_region_texel_size, 0)),
|
||||
_vertex_spacing,
|
||||
v_vertex.y - get_height(UV2 + vec2(0, _region_texel_size))
|
||||
);
|
||||
// Due to a bug caused by the GPUs linear interpolation across edges of region maps,
|
||||
// mask region edges and use vertex normals only across region boundaries.
|
||||
v_region_border_mask = mod(UV.x + 2.5, _region_size) - fract(UV.x) < 5.0 || mod(UV.y + 2.5, _region_size) - fract(UV.y) < 5.0 ? 1. : 0.;
|
||||
}
|
||||
|
||||
// Transform UVs to local to avoid poor precision during varying interpolation.
|
||||
v_uv_offset = MODEL_MATRIX[3].xz * _vertex_density;
|
||||
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;
|
||||
|
||||
// Convert model space to view space w/ skip_vertex_transform render mode
|
||||
VERTEX = (MODEL_MATRIX * vec4(VERTEX, 1.0)).xyz;
|
||||
VERTEX = (VIEW_MATRIX * vec4(VERTEX, 1.0)).xyz;
|
||||
NORMAL = normalize((MODELVIEW_MATRIX * vec4(NORMAL, 0.0)).xyz);
|
||||
BINORMAL = normalize((MODELVIEW_MATRIX * vec4(BINORMAL, 0.0)).xyz);
|
||||
TANGENT = normalize((MODELVIEW_MATRIX * vec4(TANGENT, 0.0)).xyz);
|
||||
}
|
||||
|
||||
////////////////////////
|
||||
// Fragment
|
||||
////////////////////////
|
||||
|
||||
// 0 - 3 lookups
|
||||
// 3 lookups
|
||||
vec3 get_normal(vec2 uv, out vec3 tangent, out vec3 binormal) {
|
||||
float u, v, height;
|
||||
vec3 normal;
|
||||
// Use vertex normals within radius of vertex_normals_distance, and along region borders.
|
||||
if (v_region_border_mask > 0.5 || v_vertex_xz_dist < vertex_normals_distance) {
|
||||
normal = normalize(v_normal);
|
||||
// Get the height of the current vertex
|
||||
float height = get_height(uv);
|
||||
|
||||
// Get the heights to the right and in front, but because of hardware
|
||||
// interpolation on the edges of the heightmaps, the values are off
|
||||
// causing the normal map to look weird. So, near the edges of the map
|
||||
// get the heights to the left or behind instead. Hacky solution that
|
||||
// reduces the artifact, but doesn't fix it entirely. See #185.
|
||||
float u, v;
|
||||
if(mod(uv.y*_region_size, _region_size) > _region_size-2.) {
|
||||
v = get_height(uv + vec2(0, -_region_texel_size)) - height;
|
||||
} else {
|
||||
height = get_height(uv);
|
||||
u = height - get_height(uv + vec2(_region_texel_size, 0));
|
||||
v = height - get_height(uv + vec2(0, _region_texel_size));
|
||||
normal = normalize(vec3(u, _vertex_spacing, v));
|
||||
}
|
||||
if(mod(uv.x*_region_size, _region_size) > _region_size-2.) {
|
||||
u = get_height(uv + vec2(-_region_texel_size, 0)) - height;
|
||||
} else {
|
||||
u = height - get_height(uv + vec2(_region_texel_size, 0));
|
||||
}
|
||||
|
||||
vec3 normal = vec3(u, _mesh_vertex_spacing, v);
|
||||
normal = normalize(normal);
|
||||
tangent = cross(normal, vec3(0, 0, 1));
|
||||
binormal = cross(normal, tangent);
|
||||
return normal;
|
||||
|
@ -162,3 +143,4 @@ void fragment() {
|
|||
// Apply PBR
|
||||
ALBEDO=vec3(.2);
|
||||
}
|
||||
|
||||
|
|
|
@ -61,8 +61,8 @@
|
|||
#warning += """No Terrain3D node found"""
|
||||
#return
|
||||
#
|
||||
#if not _terrain.data:
|
||||
#warning += """Terrain3DData is not initialized"""
|
||||
#if not _terrain.storage:
|
||||
#warning += """Terrain3D storage is not initialized"""
|
||||
#return
|
||||
#
|
||||
## Get global transform
|
||||
|
@ -70,8 +70,8 @@
|
|||
#var gt_inverse := gt.affine_inverse()
|
||||
#for i in transforms.list.size():
|
||||
#var location: Vector3 = (gt * transforms.list[i]).origin
|
||||
#var height: float = _terrain.data.get_height(location)
|
||||
#var normal: Vector3 = _terrain.data.get_normal(location)
|
||||
#var height: float = _terrain.storage.get_height(location)
|
||||
#var normal: Vector3 = _terrain.storage.get_normal(location)
|
||||
#
|
||||
#if align_with_collision_normal and not is_nan(normal.x):
|
||||
#transforms.list[i].basis.y = normal
|
||||
|
|
Binary file not shown.
|
@ -0,0 +1,38 @@
|
|||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://c11ip32w7ln4v"
|
||||
path="res://.godot/imported/picker.svg-0ed48f8d7e66014d2aac4b303bc65df6.ctex"
|
||||
metadata={
|
||||
"has_editor_variant": true,
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://addons/terrain_3d/icons/picker.svg"
|
||||
dest_files=["res://.godot/imported/picker.svg-0ed48f8d7e66014d2aac4b303bc65df6.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
compress/mode=0
|
||||
compress/high_quality=false
|
||||
compress/lossy_quality=0.7
|
||||
compress/hdr_compression=1
|
||||
compress/normal_map=0
|
||||
compress/channel_pack=0
|
||||
mipmaps/generate=false
|
||||
mipmaps/limit=-1
|
||||
roughness/mode=0
|
||||
roughness/src_normal=""
|
||||
process/fix_alpha_border=true
|
||||
process/premult_alpha=false
|
||||
process/normal_map_invert_y=false
|
||||
process/hdr_as_srgb=false
|
||||
process/hdr_clamp_exposure=false
|
||||
process/size_limit=0
|
||||
detect_3d/compress_to=1
|
||||
svg/scale=1.0
|
||||
editor/scale_with_editor_scale=true
|
||||
editor/convert_colors_with_editor_theme=false
|
|
@ -3,5 +3,5 @@
|
|||
name="Terrain3D"
|
||||
description="A high performance, editable terrain system for Godot 4."
|
||||
author="Cory Petkovsek & Roope Palmroos"
|
||||
version="0.9.3a"
|
||||
version="0.9.2"
|
||||
script="editor.gd"
|
||||
|
|
|
@ -6,13 +6,12 @@ signal confirmation_closed
|
|||
signal confirmation_confirmed
|
||||
signal confirmation_canceled
|
||||
|
||||
const ES_DOCK_SLOT: String = "terrain3d/dock/slot"
|
||||
const ES_DOCK_TILE_SIZE: String = "terrain3d/dock/tile_size"
|
||||
const ES_DOCK_FLOATING: String = "terrain3d/dock/floating"
|
||||
const ES_DOCK_PINNED: String = "terrain3d/dock/always_on_top"
|
||||
const ES_DOCK_WINDOW_POSITION: String = "terrain3d/dock/window_position"
|
||||
const ES_DOCK_WINDOW_SIZE: String = "terrain3d/dock/window_size"
|
||||
const ES_DOCK_TAB: String = "terrain3d/dock/tab"
|
||||
const PS_DOCK_SLOT: String = "terrain3d/config/dock_slot"
|
||||
const PS_DOCK_TILE_SIZE: String = "terrain3d/config/dock_tile_size"
|
||||
const PS_DOCK_FLOATING: String = "terrain3d/config/dock_floating"
|
||||
const PS_DOCK_PINNED: String = "terrain3d/config/dock_always_on_top"
|
||||
const PS_DOCK_WINDOW_POSITION: String = "terrain3d/config/dock_window_position"
|
||||
const PS_DOCK_WINDOW_SIZE: String = "terrain3d/config/dock_window_size"
|
||||
|
||||
var texture_list: ListContainer
|
||||
var mesh_list: ListContainer
|
||||
|
@ -41,6 +40,10 @@ enum {
|
|||
}
|
||||
var state: int = HIDDEN
|
||||
|
||||
var window: Window
|
||||
var _godot_editor_window: Window # The main Godot Editor window
|
||||
var _godot_last_state: Window.Mode = Window.MODE_FULLSCREEN
|
||||
|
||||
enum {
|
||||
POS_LEFT_UL = 0,
|
||||
POS_LEFT_BL = 1,
|
||||
|
@ -56,15 +59,17 @@ enum {
|
|||
var slot: int = POS_RIGHT_BR
|
||||
var _initialized: bool = false
|
||||
var plugin: EditorPlugin
|
||||
var window: Window
|
||||
var _godot_last_state: Window.Mode = Window.MODE_FULLSCREEN
|
||||
var editor_settings: EditorSettings
|
||||
|
||||
|
||||
func initialize(p_plugin: EditorPlugin) -> void:
|
||||
if p_plugin:
|
||||
plugin = p_plugin
|
||||
|
||||
_godot_last_state = plugin.godot_editor_window.mode
|
||||
# Get editor window. Structure is root:Window/EditorNode/Base Control
|
||||
_godot_editor_window = plugin.get_editor_interface().get_base_control().get_parent().get_parent()
|
||||
_godot_last_state = _godot_editor_window.mode
|
||||
|
||||
placement_opt = $Box/Buttons/PlacementOpt
|
||||
pinned_btn = $Box/Buttons/Pinned
|
||||
floating_btn = $Box/Buttons/Floating
|
||||
|
@ -88,6 +93,7 @@ func initialize(p_plugin: EditorPlugin) -> void:
|
|||
asset_container.add_child(mesh_list)
|
||||
_current_list = texture_list
|
||||
|
||||
editor_settings = EditorInterface.get_editor_settings()
|
||||
load_editor_settings()
|
||||
|
||||
# Connect signals
|
||||
|
@ -97,7 +103,7 @@ func initialize(p_plugin: EditorPlugin) -> void:
|
|||
placement_opt.item_selected.connect(set_slot)
|
||||
floating_btn.pressed.connect(make_dock_float)
|
||||
pinned_btn.toggled.connect(_on_pin_changed)
|
||||
pinned_btn.visible = ( window != null )
|
||||
pinned_btn.visible = false
|
||||
size_slider.value_changed.connect(_on_slider_changed)
|
||||
plugin.ui.toolbar.tool_changed.connect(_on_tool_changed)
|
||||
|
||||
|
@ -105,7 +111,7 @@ func initialize(p_plugin: EditorPlugin) -> void:
|
|||
textures_btn.add_theme_font_size_override("font_size", 16 * EditorInterface.get_editor_scale())
|
||||
|
||||
_initialized = true
|
||||
update_dock()
|
||||
update_dock(plugin.visible)
|
||||
update_layout()
|
||||
|
||||
|
||||
|
@ -116,7 +122,7 @@ func _ready() -> void:
|
|||
# Setup styles
|
||||
set("theme_override_styles/panel", get_theme_stylebox("panel", "Panel"))
|
||||
# Avoid saving icon resources in tscn when editing w/ a tool script
|
||||
if EditorInterface.get_edited_scene_root() != self:
|
||||
if plugin.get_editor_interface().get_edited_scene_root() != self:
|
||||
pinned_btn.icon = get_theme_icon("Pin", "EditorIcons")
|
||||
pinned_btn.text = ""
|
||||
floating_btn.icon = get_theme_icon("MakeFloating", "EditorIcons")
|
||||
|
@ -148,7 +154,7 @@ func set_slot(p_slot: int) -> void:
|
|||
placement_opt.selected = slot
|
||||
save_editor_settings()
|
||||
plugin.select_terrain()
|
||||
update_dock()
|
||||
update_dock(plugin.visible)
|
||||
|
||||
|
||||
func remove_dock(p_force: bool = false) -> void:
|
||||
|
@ -161,41 +167,49 @@ func remove_dock(p_force: bool = false) -> void:
|
|||
state = HIDDEN
|
||||
|
||||
# If windowed and destination is not window or final exit, otherwise leave
|
||||
elif state == WINDOWED and p_force and window:
|
||||
elif state == WINDOWED and p_force:
|
||||
if not window:
|
||||
return
|
||||
var parent: Node = get_parent()
|
||||
if parent:
|
||||
parent.remove_child(self)
|
||||
plugin.godot_editor_window.mouse_entered.disconnect(_on_godot_window_entered)
|
||||
plugin.godot_editor_window.focus_entered.disconnect(_on_godot_focus_entered)
|
||||
plugin.godot_editor_window.focus_exited.disconnect(_on_godot_focus_exited)
|
||||
window.hide()
|
||||
window.queue_free()
|
||||
window = null
|
||||
_godot_editor_window.mouse_entered.disconnect(_on_godot_window_entered)
|
||||
_godot_editor_window.focus_entered.disconnect(_on_godot_focus_entered)
|
||||
_godot_editor_window.focus_exited.disconnect(_on_godot_focus_exited)
|
||||
window.hide()
|
||||
window.queue_free()
|
||||
window = null
|
||||
floating_btn.button_pressed = false
|
||||
floating_btn.visible = true
|
||||
pinned_btn.visible = false
|
||||
placement_opt.visible = true
|
||||
state = HIDDEN
|
||||
update_dock() # return window to side/bottom
|
||||
update_dock(plugin.visible) # return window to side/bottom
|
||||
|
||||
|
||||
func update_dock() -> void:
|
||||
if not _initialized or window:
|
||||
func update_dock(p_visible: bool) -> void:
|
||||
update_assets()
|
||||
if not _initialized:
|
||||
return
|
||||
|
||||
update_assets()
|
||||
if window:
|
||||
return
|
||||
elif floating_btn.button_pressed:
|
||||
# No window, but floating button pressed, occurs when from editor settings
|
||||
make_dock_float()
|
||||
return
|
||||
|
||||
# Move dock to new destination
|
||||
remove_dock()
|
||||
# Add dock to new destination
|
||||
# Sidebar
|
||||
if slot < POS_BOTTOM:
|
||||
state = SIDEBAR
|
||||
plugin.add_control_to_dock(slot, self)
|
||||
# Bottom
|
||||
elif slot == POS_BOTTOM:
|
||||
state = BOTTOM
|
||||
plugin.add_control_to_bottom_panel(self, "Terrain3D")
|
||||
plugin.make_bottom_panel_item_visible(self)
|
||||
if p_visible:
|
||||
plugin.make_bottom_panel_item_visible(self)
|
||||
|
||||
|
||||
func update_layout() -> void:
|
||||
|
@ -243,8 +257,6 @@ func update_thumbnails() -> void:
|
|||
_last_thumb_update_time = Time.get_ticks_msec()
|
||||
for mesh_asset in mesh_list.entries:
|
||||
mesh_asset.queue_redraw()
|
||||
|
||||
|
||||
## Dock Button handlers
|
||||
|
||||
|
||||
|
@ -270,9 +282,7 @@ func _on_textures_pressed() -> void:
|
|||
textures_btn.button_pressed = true
|
||||
meshes_btn.button_pressed = false
|
||||
texture_list.set_selected_id(texture_list.selected_id)
|
||||
if plugin.is_terrain_valid():
|
||||
EditorInterface.edit_node(plugin.terrain)
|
||||
save_editor_settings()
|
||||
plugin.get_editor_interface().edit_node(plugin.terrain)
|
||||
|
||||
|
||||
func _on_meshes_pressed() -> void:
|
||||
|
@ -283,16 +293,14 @@ func _on_meshes_pressed() -> void:
|
|||
meshes_btn.button_pressed = true
|
||||
textures_btn.button_pressed = false
|
||||
mesh_list.set_selected_id(mesh_list.selected_id)
|
||||
if plugin.is_terrain_valid():
|
||||
EditorInterface.edit_node(plugin.terrain)
|
||||
plugin.get_editor_interface().edit_node(plugin.terrain)
|
||||
update_thumbnails()
|
||||
save_editor_settings()
|
||||
|
||||
|
||||
func _on_tool_changed(p_tool: Terrain3DEditor.Tool, p_operation: Terrain3DEditor.Operation) -> void:
|
||||
if p_tool == Terrain3DEditor.INSTANCER:
|
||||
_on_meshes_pressed()
|
||||
elif p_tool in [ Terrain3DEditor.TEXTURE, Terrain3DEditor.COLOR, Terrain3DEditor.ROUGHNESS ]:
|
||||
elif p_tool == Terrain3DEditor.TEXTURE:
|
||||
_on_textures_pressed()
|
||||
|
||||
|
||||
|
@ -312,34 +320,24 @@ func update_assets() -> void:
|
|||
|
||||
_current_list.update_asset_list()
|
||||
|
||||
|
||||
## Window Management
|
||||
|
||||
|
||||
func make_dock_float() -> void:
|
||||
# If not already created (eg from editor panel 'Make Floating' button)
|
||||
# If already created (eg from editor Make Floating)
|
||||
if not window:
|
||||
remove_dock()
|
||||
create_window()
|
||||
|
||||
state = WINDOWED
|
||||
visible = true # Asset dock contents are hidden when popping out of the bottom!
|
||||
pinned_btn.visible = true
|
||||
floating_btn.visible = false
|
||||
placement_opt.visible = false
|
||||
window.title = "Terrain3D Asset Dock"
|
||||
window.always_on_top = pinned_btn.button_pressed
|
||||
window.close_requested.connect(remove_dock.bind(true))
|
||||
window.window_input.connect(_on_window_input)
|
||||
window.focus_exited.connect(save_editor_settings)
|
||||
window.mouse_exited.connect(save_editor_settings)
|
||||
window.size_changed.connect(save_editor_settings)
|
||||
plugin.godot_editor_window.mouse_entered.connect(_on_godot_window_entered)
|
||||
plugin.godot_editor_window.focus_entered.connect(_on_godot_focus_entered)
|
||||
plugin.godot_editor_window.focus_exited.connect(_on_godot_focus_exited)
|
||||
plugin.godot_editor_window.grab_focus()
|
||||
update_assets()
|
||||
save_editor_settings()
|
||||
visible = true # Is hidden when pops off of bottom. ??
|
||||
_godot_editor_window.grab_focus()
|
||||
|
||||
|
||||
func create_window() -> void:
|
||||
|
@ -350,80 +348,79 @@ func create_window() -> void:
|
|||
mc.add_child(self)
|
||||
window.add_child(mc)
|
||||
window.set_transient(false)
|
||||
window.set_size(plugin.get_setting(ES_DOCK_WINDOW_SIZE, Vector2i(512, 512)))
|
||||
window.set_position(plugin.get_setting(ES_DOCK_WINDOW_POSITION, Vector2i(704, 284)))
|
||||
window.set_size(get_setting(PS_DOCK_WINDOW_SIZE, Vector2i(512, 512)))
|
||||
window.set_position(get_setting(PS_DOCK_WINDOW_POSITION, Vector2i(704, 284)))
|
||||
plugin.add_child(window)
|
||||
window.show()
|
||||
|
||||
|
||||
func clamp_window_position() -> void:
|
||||
if window and window.visible:
|
||||
var bounds: Vector2i
|
||||
if EditorInterface.get_editor_settings().get_setting("interface/editor/single_window_mode"):
|
||||
bounds = EditorInterface.get_base_control().size
|
||||
else:
|
||||
bounds = DisplayServer.screen_get_position(window.current_screen)
|
||||
bounds += DisplayServer.screen_get_size(window.current_screen)
|
||||
var margin: int = 40
|
||||
window.position.x = clamp(window.position.x, -window.size.x + 2*margin, bounds.x - margin)
|
||||
window.position.y = clamp(window.position.y, 25, bounds.y - margin)
|
||||
window.window_input.connect(_on_window_input)
|
||||
window.focus_exited.connect(_on_window_focus_exited)
|
||||
_godot_editor_window.mouse_entered.connect(_on_godot_window_entered)
|
||||
_godot_editor_window.focus_entered.connect(_on_godot_focus_entered)
|
||||
_godot_editor_window.focus_exited.connect(_on_godot_focus_exited)
|
||||
|
||||
|
||||
func _on_window_input(event: InputEvent) -> void:
|
||||
# Capture CTRL+S when doc focused to save scene
|
||||
# Capture CTRL+S when doc focused to save scene)
|
||||
if event is InputEventKey and event.keycode == KEY_S and event.pressed and event.is_command_or_control_pressed():
|
||||
save_editor_settings()
|
||||
EditorInterface.save_scene()
|
||||
plugin.get_editor_interface().save_scene()
|
||||
|
||||
|
||||
func _on_window_focus_exited() -> void:
|
||||
# Capture window position w/o other changes
|
||||
save_editor_settings()
|
||||
|
||||
|
||||
func _on_godot_window_entered() -> void:
|
||||
if is_instance_valid(window) and window.has_focus():
|
||||
plugin.godot_editor_window.grab_focus()
|
||||
_godot_editor_window.grab_focus()
|
||||
|
||||
|
||||
func _on_godot_focus_entered() -> void:
|
||||
# If asset dock is windowed, and Godot was minimized, and now is not, restore asset dock window
|
||||
if is_instance_valid(window):
|
||||
if _godot_last_state == Window.MODE_MINIMIZED and plugin.godot_editor_window.mode != Window.MODE_MINIMIZED:
|
||||
if _godot_last_state == Window.MODE_MINIMIZED and _godot_editor_window.mode != Window.MODE_MINIMIZED:
|
||||
window.show()
|
||||
_godot_last_state = plugin.godot_editor_window.mode
|
||||
plugin.godot_editor_window.grab_focus()
|
||||
_godot_last_state = _godot_editor_window.mode
|
||||
_godot_editor_window.grab_focus()
|
||||
|
||||
|
||||
func _on_godot_focus_exited() -> void:
|
||||
if is_instance_valid(window) and plugin.godot_editor_window.mode == Window.MODE_MINIMIZED:
|
||||
if is_instance_valid(window) and _godot_editor_window.mode == Window.MODE_MINIMIZED:
|
||||
window.hide()
|
||||
_godot_last_state = plugin.godot_editor_window.mode
|
||||
_godot_last_state = _godot_editor_window.mode
|
||||
|
||||
|
||||
## Manage Editor Settings
|
||||
|
||||
|
||||
func get_setting(p_str: String, p_default: Variant) -> Variant:
|
||||
if editor_settings.has_setting(p_str):
|
||||
return editor_settings.get_setting(p_str)
|
||||
else:
|
||||
return p_default
|
||||
|
||||
|
||||
func load_editor_settings() -> void:
|
||||
floating_btn.button_pressed = plugin.get_setting(ES_DOCK_FLOATING, false)
|
||||
pinned_btn.button_pressed = plugin.get_setting(ES_DOCK_PINNED, true)
|
||||
size_slider.value = plugin.get_setting(ES_DOCK_TILE_SIZE, 83)
|
||||
floating_btn.button_pressed = get_setting(PS_DOCK_FLOATING, false)
|
||||
pinned_btn.button_pressed = get_setting(PS_DOCK_PINNED, true)
|
||||
size_slider.value = get_setting(PS_DOCK_TILE_SIZE, 83)
|
||||
set_slot(get_setting(PS_DOCK_SLOT, POS_BOTTOM))
|
||||
_on_slider_changed(size_slider.value)
|
||||
set_slot(plugin.get_setting(ES_DOCK_SLOT, POS_BOTTOM))
|
||||
if floating_btn.button_pressed:
|
||||
make_dock_float()
|
||||
# TODO Don't save tab until thumbnail generation more reliable
|
||||
#if plugin.get_setting(ES_DOCK_TAB, 0) == 1:
|
||||
# _on_meshes_pressed()
|
||||
# Window pos/size set on window creation in update_dock
|
||||
update_dock(plugin.visible)
|
||||
|
||||
|
||||
func save_editor_settings() -> void:
|
||||
if not _initialized:
|
||||
return
|
||||
clamp_window_position()
|
||||
plugin.set_setting(ES_DOCK_SLOT, slot)
|
||||
plugin.set_setting(ES_DOCK_TILE_SIZE, size_slider.value)
|
||||
plugin.set_setting(ES_DOCK_FLOATING, floating_btn.button_pressed)
|
||||
plugin.set_setting(ES_DOCK_PINNED, pinned_btn.button_pressed)
|
||||
# TODO Don't save tab until thumbnail generation more reliable
|
||||
# plugin.set_setting(ES_DOCK_TAB, 0 if _current_list == texture_list else 1)
|
||||
editor_settings.set_setting(PS_DOCK_SLOT, slot)
|
||||
editor_settings.set_setting(PS_DOCK_TILE_SIZE, size_slider.value)
|
||||
editor_settings.set_setting(PS_DOCK_FLOATING, floating_btn.button_pressed)
|
||||
editor_settings.set_setting(PS_DOCK_PINNED, pinned_btn.button_pressed)
|
||||
if window:
|
||||
plugin.set_setting(ES_DOCK_WINDOW_SIZE, window.size)
|
||||
plugin.set_setting(ES_DOCK_WINDOW_POSITION, window.position)
|
||||
editor_settings.set_setting(PS_DOCK_WINDOW_SIZE, window.size)
|
||||
editor_settings.set_setting(PS_DOCK_WINDOW_POSITION, window.position)
|
||||
|
||||
|
||||
##############################################################
|
||||
|
@ -530,8 +527,7 @@ class ListContainer extends Container:
|
|||
plugin.select_terrain()
|
||||
|
||||
# Select Paint tool if clicking a texture
|
||||
if type == Terrain3DAssets.TYPE_TEXTURE and \
|
||||
not plugin.editor.get_tool() in [ Terrain3DEditor.TEXTURE, Terrain3DEditor.COLOR, Terrain3DEditor.ROUGHNESS ]:
|
||||
if type == Terrain3DAssets.TYPE_TEXTURE and plugin.editor.get_tool() != Terrain3DEditor.TEXTURE:
|
||||
var paint_btn: Button = plugin.ui.toolbar.get_node_or_null("PaintBaseTexture")
|
||||
if paint_btn:
|
||||
paint_btn.set_pressed(true)
|
||||
|
@ -549,7 +545,7 @@ class ListContainer extends Container:
|
|||
|
||||
func _on_resource_inspected(p_resource: Resource) -> void:
|
||||
await get_tree().create_timer(.01).timeout
|
||||
EditorInterface.edit_resource(p_resource)
|
||||
plugin.get_editor_interface().edit_resource(p_resource)
|
||||
|
||||
|
||||
func _on_resource_changed(p_resource: Resource, p_id: int) -> void:
|
||||
|
@ -579,7 +575,7 @@ class ListContainer extends Container:
|
|||
|
||||
# If removing an entry, clear inspector
|
||||
if not p_resource:
|
||||
EditorInterface.inspect_object(null)
|
||||
plugin.get_editor_interface().inspect_object(null)
|
||||
|
||||
# If null resource, remove last
|
||||
if not p_resource:
|
||||
|
|
|
@ -5,39 +5,37 @@
|
|||
[node name="bake_lod_dialog" type="ConfirmationDialog"]
|
||||
title = "Bake Terrain3D Mesh"
|
||||
position = Vector2i(0, 36)
|
||||
size = Vector2i(400, 155)
|
||||
size = Vector2i(400, 115)
|
||||
visible = true
|
||||
script = ExtResource("1_sf76d")
|
||||
|
||||
[node name="MarginContainer" type="MarginContainer" parent="."]
|
||||
[node name="VBoxContainer" type="VBoxContainer" parent="."]
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
offset_left = 8.0
|
||||
offset_top = 8.0
|
||||
offset_right = 392.0
|
||||
offset_bottom = 106.0
|
||||
theme_override_constants/margin_left = 10
|
||||
theme_override_constants/margin_top = 10
|
||||
theme_override_constants/margin_right = 10
|
||||
theme_override_constants/margin_bottom = 10
|
||||
offset_right = -8.0
|
||||
offset_bottom = -49.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
|
||||
[node name="VBoxContainer" type="VBoxContainer" parent="MarginContainer"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="HBoxContainer" type="HBoxContainer" parent="MarginContainer/VBoxContainer"]
|
||||
[node name="HBoxContainer" type="HBoxContainer" parent="VBoxContainer"]
|
||||
layout_mode = 2
|
||||
theme_override_constants/separation = 20
|
||||
|
||||
[node name="Label" type="Label" parent="MarginContainer/VBoxContainer/HBoxContainer"]
|
||||
[node name="Label" type="Label" parent="VBoxContainer/HBoxContainer"]
|
||||
layout_mode = 2
|
||||
text = "LOD:"
|
||||
|
||||
[node name="LodBox" type="SpinBox" parent="MarginContainer/VBoxContainer/HBoxContainer"]
|
||||
[node name="LodBox" type="SpinBox" parent="VBoxContainer/HBoxContainer"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
max_value = 8.0
|
||||
value = 4.0
|
||||
|
||||
[node name="DescriptionLabel" type="Label" parent="MarginContainer/VBoxContainer"]
|
||||
[node name="DescriptionLabel" type="Label" parent="VBoxContainer"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
autowrap_mode = 2
|
||||
|
|
|
@ -44,14 +44,11 @@ func bake_mesh_popup() -> void:
|
|||
if plugin.terrain:
|
||||
bake_method = _bake_mesh
|
||||
bake_lod_dialog.description = BAKE_MESH_DESCRIPTION
|
||||
EditorInterface.popup_dialog_centered(bake_lod_dialog)
|
||||
plugin.get_editor_interface().popup_dialog_centered(bake_lod_dialog)
|
||||
|
||||
|
||||
func _bake_mesh() -> void:
|
||||
if plugin.terrain.data.get_region_count() == 0:
|
||||
push_error("Terrain3D has no active regions to bake")
|
||||
return
|
||||
var mesh: Mesh = plugin.terrain.bake_mesh(bake_lod_dialog.lod, Terrain3DData.HEIGHT_FILTER_NEAREST)
|
||||
var mesh: Mesh = plugin.terrain.bake_mesh(bake_lod_dialog.lod, Terrain3DStorage.HEIGHT_FILTER_NEAREST)
|
||||
if !mesh:
|
||||
push_error("Failed to bake mesh from Terrain3D")
|
||||
return
|
||||
|
@ -68,7 +65,7 @@ func _bake_mesh() -> void:
|
|||
|
||||
undo.add_do_method(plugin.terrain, &"add_child", mesh_instance, true)
|
||||
undo.add_undo_method(plugin.terrain, &"remove_child", mesh_instance)
|
||||
undo.add_do_property(mesh_instance, &"owner", EditorInterface.get_edited_scene_root())
|
||||
undo.add_do_property(mesh_instance, &"owner", plugin.terrain.owner)
|
||||
undo.add_do_reference(mesh_instance)
|
||||
|
||||
else:
|
||||
|
@ -89,14 +86,11 @@ func bake_occluder_popup() -> void:
|
|||
if plugin.terrain:
|
||||
bake_method = _bake_occluder
|
||||
bake_lod_dialog.description = BAKE_OCCLUDER_DESCRIPTION
|
||||
EditorInterface.popup_dialog_centered(bake_lod_dialog)
|
||||
plugin.get_editor_interface().popup_dialog_centered(bake_lod_dialog)
|
||||
|
||||
|
||||
func _bake_occluder() -> void:
|
||||
if plugin.terrain.data.get_region_count() == 0:
|
||||
push_error("Terrain3D has no active regions to bake")
|
||||
return
|
||||
var mesh: Mesh = plugin.terrain.bake_mesh(bake_lod_dialog.lod, Terrain3DData.HEIGHT_FILTER_MINIMUM)
|
||||
var mesh: Mesh = plugin.terrain.bake_mesh(bake_lod_dialog.lod, Terrain3DStorage.HEIGHT_FILTER_MINIMUM)
|
||||
if !mesh:
|
||||
push_error("Failed to bake mesh from Terrain3D")
|
||||
return
|
||||
|
@ -119,7 +113,7 @@ func _bake_occluder() -> void:
|
|||
|
||||
undo.add_do_method(plugin.terrain, &"add_child", occluder_instance, true)
|
||||
undo.add_undo_method(plugin.terrain, &"remove_child", occluder_instance)
|
||||
undo.add_do_property(occluder_instance, &"owner", EditorInterface.get_edited_scene_root())
|
||||
undo.add_do_property(occluder_instance, &"owner", plugin.terrain.owner)
|
||||
undo.add_do_reference(occluder_instance)
|
||||
|
||||
else:
|
||||
|
@ -159,7 +153,7 @@ func find_nav_region_terrains(p_nav_region: NavigationRegion3D) -> Array[Terrain
|
|||
|
||||
func find_terrain_nav_regions(p_terrain: Terrain3D) -> Array[NavigationRegion3D]:
|
||||
var result: Array[NavigationRegion3D] = []
|
||||
var root: Node = EditorInterface.get_edited_scene_root()
|
||||
var root: Node = plugin.get_editor_interface().get_edited_scene_root()
|
||||
if not root:
|
||||
return result
|
||||
for nav_region in root.find_children("", "NavigationRegion3D", true, true):
|
||||
|
@ -175,9 +169,6 @@ func bake_nav_mesh() -> void:
|
|||
print("Terrain3DNavigation: Finished baking 1 NavigationMesh.")
|
||||
|
||||
elif plugin.terrain:
|
||||
if plugin.terrain.data.get_region_count() == 0:
|
||||
push_error("Terrain3D has no active regions to bake")
|
||||
return
|
||||
# A Terrain3D is selected. There are potentially multiple navmeshes to bake and we need to
|
||||
# find them all. (The multiple navmesh use-case is likely on very large scenes with lots of
|
||||
# geometry. Each navmesh in this case would define its own, non-overlapping, baking AABB, to
|
||||
|
@ -338,17 +329,11 @@ func set_up_navigation_popup() -> void:
|
|||
if plugin.terrain:
|
||||
bake_method = _set_up_navigation
|
||||
confirm_dialog.dialog_text = SET_UP_NAVIGATION_DESCRIPTION
|
||||
EditorInterface.popup_dialog_centered(confirm_dialog)
|
||||
plugin.get_editor_interface().popup_dialog_centered(confirm_dialog)
|
||||
|
||||
|
||||
func _set_up_navigation() -> void:
|
||||
assert(plugin.terrain)
|
||||
if plugin.terrain == EditorInterface.get_edited_scene_root():
|
||||
push_error("Terrain3D Navigation setup not possible if Terrain3D node is scene root")
|
||||
return
|
||||
if plugin.terrain.data.get_region_count() == 0:
|
||||
push_error("Terrain3D has no active regions")
|
||||
return
|
||||
var terrain: Terrain3D = plugin.terrain
|
||||
|
||||
var nav_region := NavigationRegion3D.new()
|
||||
|
@ -363,7 +348,7 @@ func _set_up_navigation() -> void:
|
|||
undo_redo.add_do_reference(nav_region)
|
||||
undo_redo.commit_action()
|
||||
|
||||
EditorInterface.inspect_object(nav_region)
|
||||
plugin.get_editor_interface().inspect_object(nav_region)
|
||||
assert(plugin.nav_region == nav_region)
|
||||
|
||||
bake_nav_mesh()
|
||||
|
|
|
@ -1,38 +1,21 @@
|
|||
extends RefCounted
|
||||
extends Object
|
||||
|
||||
const WINDOW_SCENE: String = "res://addons/terrain_3d/src/channel_packer.tscn"
|
||||
const TEMPLATE_PATH: String = "res://addons/terrain_3d/src/channel_packer_import_template.txt"
|
||||
const DRAG_DROP_SCRIPT: String = "res://addons/terrain_3d/src/channel_packer_dragdrop.gd"
|
||||
enum {
|
||||
INFO,
|
||||
WARN,
|
||||
ERROR,
|
||||
}
|
||||
|
||||
enum {
|
||||
IMAGE_ALBEDO,
|
||||
IMAGE_HEIGHT,
|
||||
IMAGE_NORMAL,
|
||||
IMAGE_ROUGHNESS
|
||||
IMAGE_ALBEDO,
|
||||
IMAGE_HEIGHT,
|
||||
IMAGE_NORMAL,
|
||||
IMAGE_ROUGHNESS,
|
||||
}
|
||||
|
||||
var plugin: EditorPlugin
|
||||
var window: Window
|
||||
var save_file_dialog: EditorFileDialog
|
||||
var open_file_dialog: EditorFileDialog
|
||||
var editor_interface: EditorInterface
|
||||
var dialog: AcceptDialog
|
||||
var save_file_dialog: FileDialog
|
||||
var open_file_dialog: FileDialog
|
||||
var invert_green_checkbox: CheckBox
|
||||
var invert_smooth_checkbox: CheckBox
|
||||
var invert_height_checkbox: CheckBox
|
||||
var lumin_height_button: Button
|
||||
var generate_mipmaps_checkbox: CheckBox
|
||||
var high_quality_checkbox: CheckBox
|
||||
var align_normals_checkbox: CheckBox
|
||||
var resize_toggle_checkbox: CheckBox
|
||||
var resize_option_box: SpinBox
|
||||
var height_channel: Array[Button]
|
||||
var height_channel_selected: int = 0
|
||||
var roughness_channel: Array[Button]
|
||||
var roughness_channel_selected: int = 0
|
||||
var last_opened_directory: String
|
||||
var last_saved_directory: String
|
||||
var packing_albedo: bool = false
|
||||
|
@ -41,184 +24,60 @@ var images: Array[Image] = [null, null, null, null]
|
|||
var status_label: Label
|
||||
var no_op: Callable = func(): pass
|
||||
var last_file_selected_fn: Callable = no_op
|
||||
var normal_vector: Vector3
|
||||
|
||||
|
||||
func pack_textures_popup() -> void:
|
||||
if window != null:
|
||||
window.show()
|
||||
window.move_to_foreground()
|
||||
window.move_to_center()
|
||||
if dialog != null:
|
||||
print("Terrain3DChannelPacker: Cannot open pack tool, dialog already open.")
|
||||
return
|
||||
window = (load(WINDOW_SCENE) as PackedScene).instantiate()
|
||||
window.close_requested.connect(_on_close_requested)
|
||||
window.window_input.connect(func(event:InputEvent):
|
||||
if event is InputEventKey:
|
||||
if event.pressed and event.keycode == KEY_ESCAPE:
|
||||
_on_close_requested()
|
||||
)
|
||||
window.find_child("CloseButton").pressed.connect(_on_close_requested)
|
||||
|
||||
status_label = window.find_child("StatusLabel") as Label
|
||||
invert_green_checkbox = window.find_child("InvertGreenChannelCheckBox") as CheckBox
|
||||
invert_smooth_checkbox = window.find_child("InvertSmoothCheckBox") as CheckBox
|
||||
invert_height_checkbox = window.find_child("ConvertDepthToHeight") as CheckBox
|
||||
lumin_height_button = window.find_child("LuminanceAsHeightButton") as Button
|
||||
generate_mipmaps_checkbox = window.find_child("GenerateMipmapsCheckBox") as CheckBox
|
||||
high_quality_checkbox = window.find_child("HighQualityCheckBox") as CheckBox
|
||||
align_normals_checkbox = window.find_child("AlignNormalsCheckBox") as CheckBox
|
||||
resize_toggle_checkbox = window.find_child("ResizeToggle") as CheckBox
|
||||
resize_option_box = window.find_child("ResizeOptionButton") as SpinBox
|
||||
height_channel = [
|
||||
window.find_child("HeightChannelR") as Button,
|
||||
window.find_child("HeightChannelG") as Button,
|
||||
window.find_child("HeightChannelB") as Button,
|
||||
window.find_child("HeightChannelA") as Button
|
||||
]
|
||||
roughness_channel = [
|
||||
window.find_child("RoughnessChannelR") as Button,
|
||||
window.find_child("RoughnessChannelG") as Button,
|
||||
window.find_child("RoughnessChannelB") as Button,
|
||||
window.find_child("RoughnessChannelA") as Button
|
||||
]
|
||||
dialog = (load(WINDOW_SCENE) as PackedScene).instantiate()
|
||||
dialog.confirmed.connect(_on_close_requested)
|
||||
dialog.canceled.connect(_on_close_requested)
|
||||
status_label = dialog.find_child("StatusLabel")
|
||||
invert_green_checkbox = dialog.find_child("InvertGreenChannelCheckBox")
|
||||
|
||||
height_channel[0].pressed.connect(func() -> void: height_channel_selected = 0)
|
||||
height_channel[1].pressed.connect(func() -> void: height_channel_selected = 1)
|
||||
height_channel[2].pressed.connect(func() -> void: height_channel_selected = 2)
|
||||
height_channel[3].pressed.connect(func() -> void: height_channel_selected = 3)
|
||||
|
||||
roughness_channel[0].pressed.connect(func() -> void: roughness_channel_selected = 0)
|
||||
roughness_channel[1].pressed.connect(func() -> void: roughness_channel_selected = 1)
|
||||
roughness_channel[2].pressed.connect(func() -> void: roughness_channel_selected = 2)
|
||||
roughness_channel[3].pressed.connect(func() -> void: roughness_channel_selected = 3)
|
||||
|
||||
plugin.add_child(window)
|
||||
editor_interface = plugin.get_editor_interface()
|
||||
_init_file_dialogs()
|
||||
editor_interface.popup_dialog_centered(dialog)
|
||||
|
||||
# the dialog disables the parent window "on top" so, restore it after 1 frame to alow the dialog to clear.
|
||||
var set_on_top_fn: Callable = func(_file: String = "") -> void:
|
||||
await RenderingServer.frame_post_draw
|
||||
window.always_on_top = true
|
||||
save_file_dialog.file_selected.connect(set_on_top_fn)
|
||||
save_file_dialog.canceled.connect(set_on_top_fn)
|
||||
open_file_dialog.file_selected.connect(set_on_top_fn)
|
||||
open_file_dialog.canceled.connect(set_on_top_fn)
|
||||
|
||||
_init_texture_picker(window.find_child("AlbedoVBox"), IMAGE_ALBEDO)
|
||||
_init_texture_picker(window.find_child("HeightVBox"), IMAGE_HEIGHT)
|
||||
_init_texture_picker(window.find_child("NormalVBox"), IMAGE_NORMAL)
|
||||
_init_texture_picker(window.find_child("RoughnessVBox"), IMAGE_ROUGHNESS)
|
||||
|
||||
_init_texture_picker(dialog.find_child("AlbedoVBox"), IMAGE_ALBEDO)
|
||||
_init_texture_picker(dialog.find_child("HeightVBox"), IMAGE_HEIGHT)
|
||||
_init_texture_picker(dialog.find_child("NormalVBox"), IMAGE_NORMAL)
|
||||
_init_texture_picker(dialog.find_child("RoughnessVBox"), IMAGE_ROUGHNESS)
|
||||
var pack_button_path: String = "Panel/MarginContainer/VBoxContainer/PackButton"
|
||||
(window.get_node(pack_button_path) as Button).pressed.connect(_on_pack_button_pressed)
|
||||
(dialog.get_node(pack_button_path) as Button).pressed.connect(_on_pack_button_pressed)
|
||||
|
||||
|
||||
func _on_close_requested() -> void:
|
||||
last_file_selected_fn = no_op
|
||||
images = [null, null, null, null]
|
||||
window.queue_free()
|
||||
window = null
|
||||
dialog.queue_free()
|
||||
dialog = null
|
||||
|
||||
|
||||
func _init_file_dialogs() -> void:
|
||||
save_file_dialog = EditorFileDialog.new()
|
||||
save_file_dialog = FileDialog.new()
|
||||
save_file_dialog.set_filters(PackedStringArray(["*.png"]))
|
||||
save_file_dialog.set_file_mode(EditorFileDialog.FILE_MODE_SAVE_FILE)
|
||||
save_file_dialog.access = EditorFileDialog.ACCESS_FILESYSTEM
|
||||
save_file_dialog.set_file_mode(FileDialog.FILE_MODE_SAVE_FILE)
|
||||
save_file_dialog.access = FileDialog.ACCESS_FILESYSTEM
|
||||
save_file_dialog.file_selected.connect(_on_save_file_selected)
|
||||
save_file_dialog.ok_button_text = "Save"
|
||||
save_file_dialog.size = Vector2i(550, 550)
|
||||
#save_file_dialog.transient = false
|
||||
#save_file_dialog.exclusive = false
|
||||
#save_file_dialog.popup_window = true
|
||||
|
||||
open_file_dialog = EditorFileDialog.new()
|
||||
open_file_dialog.set_filters(PackedStringArray(
|
||||
["*.png", "*.bmp", "*.exr", "*.hdr", "*.jpg", "*.jpeg", "*.tga", "*.svg", "*.webp", "*.ktx", "*.dds"]))
|
||||
open_file_dialog.set_file_mode(EditorFileDialog.FILE_MODE_OPEN_FILE)
|
||||
open_file_dialog.access = EditorFileDialog.ACCESS_FILESYSTEM
|
||||
open_file_dialog.ok_button_text = "Open"
|
||||
open_file_dialog.size = Vector2i(550, 550)
|
||||
#open_file_dialog.transient = false
|
||||
#open_file_dialog.exclusive = false
|
||||
#open_file_dialog.popup_window = true
|
||||
open_file_dialog = FileDialog.new()
|
||||
open_file_dialog.set_filters(PackedStringArray(["*.png", "*.bmp", "*.exr", "*.hdr", "*.jpg", "*.jpeg", "*.tga", "*.svg", "*.webp", ".ktx"]))
|
||||
open_file_dialog.set_file_mode(FileDialog.FILE_MODE_OPEN_FILE)
|
||||
open_file_dialog.access = FileDialog.ACCESS_FILESYSTEM
|
||||
|
||||
window.add_child(save_file_dialog)
|
||||
window.add_child(open_file_dialog)
|
||||
dialog.add_child(save_file_dialog)
|
||||
dialog.add_child(open_file_dialog)
|
||||
|
||||
|
||||
func _init_texture_picker(p_parent: Node, p_image_index: int) -> void:
|
||||
var line_edit: LineEdit = p_parent.find_child("LineEdit") as LineEdit
|
||||
var file_pick_button: Button = p_parent.find_child("PickButton") as Button
|
||||
var clear_button: Button = p_parent.find_child("ClearButton") as Button
|
||||
var texture_rect: TextureRect = p_parent.find_child("TextureRect") as TextureRect
|
||||
var texture_button: Button = p_parent.find_child("TextureButton") as Button
|
||||
texture_button.set_script(load(DRAG_DROP_SCRIPT) as GDScript)
|
||||
|
||||
var set_channel_fn: Callable = func(used_channels: int) -> void:
|
||||
var channel_count: int = 4
|
||||
# enum Image.UsedChannels
|
||||
match used_channels:
|
||||
Image.USED_CHANNELS_L, Image.USED_CHANNELS_R: channel_count = 1
|
||||
Image.USED_CHANNELS_LA, Image.USED_CHANNELS_RG: channel_count = 2
|
||||
Image.USED_CHANNELS_RGB: channel_count = 3
|
||||
Image.USED_CHANNELS_RGBA: channel_count = 4
|
||||
if p_image_index == IMAGE_HEIGHT:
|
||||
for i in 4:
|
||||
height_channel[i].visible = i < channel_count
|
||||
height_channel[0].button_pressed = true
|
||||
height_channel[0].pressed.emit()
|
||||
elif p_image_index == IMAGE_ROUGHNESS:
|
||||
for i in 4:
|
||||
roughness_channel[i].visible = i < channel_count
|
||||
roughness_channel[0].button_pressed = true
|
||||
roughness_channel[0].pressed.emit()
|
||||
|
||||
var load_image_fn: Callable = func(path: String):
|
||||
var image: Image = Image.new()
|
||||
var error: int = OK
|
||||
# Special case for dds files
|
||||
if path.get_extension() == "dds":
|
||||
image = ResourceLoader.load(path).get_image()
|
||||
if not image.is_empty():
|
||||
# if the dds file is loaded, we must clear any mipmaps and
|
||||
# decompress if needed in order to do per pixel operations.
|
||||
image.clear_mipmaps()
|
||||
image.decompress()
|
||||
else:
|
||||
error = FAILED
|
||||
else:
|
||||
error = image.load(path)
|
||||
if error != OK:
|
||||
_show_message(ERROR, "Failed to load texture '" + path + "'")
|
||||
texture_rect.texture = null
|
||||
images[p_image_index] = null
|
||||
else:
|
||||
_show_message(INFO, "Loaded texture '" + path + "'")
|
||||
texture_rect.texture = ImageTexture.create_from_image(image)
|
||||
images[p_image_index] = image
|
||||
_set_wh_labels(p_image_index, image.get_width(), image.get_height())
|
||||
if p_image_index == IMAGE_NORMAL:
|
||||
_set_normal_vector(image)
|
||||
if p_image_index == IMAGE_HEIGHT or p_image_index == IMAGE_ROUGHNESS:
|
||||
set_channel_fn.call(image.detect_used_channels())
|
||||
|
||||
var os_drop_fn: Callable = func(files: PackedStringArray) -> void:
|
||||
# OS drag drop holds mouse focus until released,
|
||||
# Get mouse pos and check directly if inside texture_rect
|
||||
var rect = texture_button.get_global_rect()
|
||||
var mouse_position = texture_button.get_global_mouse_position()
|
||||
if rect.has_point(mouse_position):
|
||||
if files.size() != 1:
|
||||
_show_message(ERROR, "Cannot load multiple files")
|
||||
else:
|
||||
line_edit.text = files[0]
|
||||
load_image_fn.call(files[0])
|
||||
|
||||
var godot_drop_fn: Callable = func(path: String) -> void:
|
||||
path = ProjectSettings.globalize_path(path)
|
||||
line_edit.text = path
|
||||
load_image_fn.call(path)
|
||||
var line_edit: LineEdit = p_parent.find_child("LineEdit")
|
||||
var file_pick_button: Button = p_parent.find_child("PickButton")
|
||||
var clear_button: Button = p_parent.find_child("ClearButton")
|
||||
var texture_rect: TextureRect = p_parent.find_child("TextureRect")
|
||||
var texture_button: Button = p_parent.find_child("TextureButton")
|
||||
|
||||
var open_fn: Callable = func() -> void:
|
||||
open_file_dialog.current_path = last_opened_directory
|
||||
|
@ -226,98 +85,72 @@ func _init_texture_picker(p_parent: Node, p_image_index: int) -> void:
|
|||
open_file_dialog.file_selected.disconnect(last_file_selected_fn)
|
||||
last_file_selected_fn = func(path: String) -> void:
|
||||
line_edit.text = path
|
||||
load_image_fn.call(path)
|
||||
line_edit.caret_column = path.length()
|
||||
last_opened_directory = path.get_base_dir() + "/"
|
||||
var image: Image = Image.new()
|
||||
var code: int = image.load(path)
|
||||
if code != OK:
|
||||
_show_error("Failed to load texture '" + path + "'")
|
||||
texture_rect.texture = null
|
||||
images[p_image_index] = null
|
||||
else:
|
||||
_show_success("Loaded texture '" + path + "'")
|
||||
texture_rect.texture = ImageTexture.create_from_image(image)
|
||||
images[p_image_index] = image
|
||||
open_file_dialog.file_selected.connect(last_file_selected_fn)
|
||||
open_file_dialog.popup_centered_ratio()
|
||||
|
||||
var line_edit_submit_fn: Callable = func(path: String) -> void:
|
||||
line_edit.text = path
|
||||
load_image_fn.call(path)
|
||||
|
||||
var clear_fn: Callable = func() -> void:
|
||||
line_edit.text = ""
|
||||
texture_rect.texture = null
|
||||
images[p_image_index] = null
|
||||
_set_wh_labels(p_image_index, -1, -1)
|
||||
|
||||
# allow user to edit textbox and press enter because Godot's file picker doesn't work 100% of the time
|
||||
var line_edit_submit_fn: Callable = func(path: String) -> void:
|
||||
var image: Image = Image.new()
|
||||
var code: int = image.load(path)
|
||||
if code != OK:
|
||||
_show_error("Failed to load texture '" + path + "'")
|
||||
texture_rect.texture = null
|
||||
images[p_image_index] = null
|
||||
else:
|
||||
texture_rect.texture = ImageTexture.create_from_image(image)
|
||||
images[p_image_index] = image
|
||||
|
||||
line_edit.text_submitted.connect(line_edit_submit_fn)
|
||||
file_pick_button.pressed.connect(open_fn)
|
||||
texture_button.pressed.connect(open_fn)
|
||||
clear_button.pressed.connect(clear_fn)
|
||||
texture_button.dropped.connect(godot_drop_fn)
|
||||
window.files_dropped.connect(os_drop_fn)
|
||||
|
||||
if p_image_index == IMAGE_HEIGHT:
|
||||
var lumin_fn: Callable = func() -> void:
|
||||
if !images[IMAGE_ALBEDO]:
|
||||
_show_message(ERROR, "Albedo Image Required for Operation")
|
||||
else:
|
||||
line_edit.text = "Generated Height"
|
||||
var height_texture: Image = Terrain3DUtil.luminance_to_height(images[IMAGE_ALBEDO])
|
||||
if height_texture.is_empty():
|
||||
_show_message(ERROR, "Height Texture Generation error")
|
||||
# blur the image by resizing down and back..
|
||||
var w: int = height_texture.get_width()
|
||||
var h: int = height_texture.get_height()
|
||||
height_texture.resize(w / 4, h / 4)
|
||||
height_texture.resize(w, h, Image.INTERPOLATE_CUBIC)
|
||||
# "Load" the height texture
|
||||
images[IMAGE_HEIGHT] = height_texture
|
||||
texture_rect.texture = ImageTexture.create_from_image(images[IMAGE_HEIGHT])
|
||||
_set_wh_labels(IMAGE_HEIGHT, height_texture.get_width(), height_texture.get_height())
|
||||
set_channel_fn.call(Image.USED_CHANNELS_R)
|
||||
_show_message(INFO, "Height Texture generated sucsessfully")
|
||||
lumin_height_button.pressed.connect(lumin_fn)
|
||||
plugin.ui.set_button_editor_icon(file_pick_button, "Folder")
|
||||
plugin.ui.set_button_editor_icon(clear_button, "Remove")
|
||||
_set_button_icon(file_pick_button, "Folder")
|
||||
_set_button_icon(clear_button, "Remove")
|
||||
|
||||
|
||||
func _set_wh_labels(p_image_index: int, width: int, height: int) -> void:
|
||||
var w: String = ""
|
||||
var h: String = ""
|
||||
if width > 0 and height > 0:
|
||||
w = "w: " + str(width)
|
||||
h = "h: " + str(height)
|
||||
match p_image_index:
|
||||
0:
|
||||
window.find_child("AlbedoW").text = w
|
||||
window.find_child("AlbedoH").text = h
|
||||
1:
|
||||
window.find_child("HeightW").text = w
|
||||
window.find_child("HeightH").text = h
|
||||
2:
|
||||
window.find_child("NormalW").text = w
|
||||
window.find_child("NormalH").text = h
|
||||
3:
|
||||
window.find_child("RoughnessW").text = w
|
||||
window.find_child("RoughnessH").text = h
|
||||
func _set_button_icon(p_button: Button, p_icon_name: String) -> void:
|
||||
var editor_base: Control = editor_interface.get_base_control()
|
||||
var icon: Texture2D = editor_base.get_theme_icon(p_icon_name, "EditorIcons")
|
||||
p_button.icon = icon
|
||||
|
||||
|
||||
func _show_message(p_level: int, p_text: String) -> void:
|
||||
func _show_error(p_text: String) -> void:
|
||||
push_error("Terrain3DChannelPacker: " + p_text)
|
||||
status_label.text = p_text
|
||||
match p_level:
|
||||
INFO:
|
||||
print("Terrain3DChannelPacker: " + p_text)
|
||||
status_label.add_theme_color_override("font_color", Color(0, 0.82, 0.14))
|
||||
WARN:
|
||||
push_warning("Terrain3DChannelPacker: " + p_text)
|
||||
status_label.add_theme_color_override("font_color", Color(0.9, 0.9, 0))
|
||||
ERROR,_:
|
||||
push_error("Terrain3DChannelPacker: " + p_text)
|
||||
status_label.add_theme_color_override("font_color", Color(0.9, 0, 0))
|
||||
status_label.add_theme_color_override("font_color", Color(0.9, 0, 0))
|
||||
|
||||
|
||||
func _show_success(p_text: String) -> void:
|
||||
print("Terrain3DChannelPacker: " + p_text)
|
||||
status_label.text = p_text
|
||||
status_label.add_theme_color_override("font_color", Color(0, 0.82, 0.14))
|
||||
|
||||
|
||||
func _create_import_file(png_path: String) -> void:
|
||||
var dst_import_path: String = png_path + ".import"
|
||||
|
||||
var file: FileAccess = FileAccess.open(TEMPLATE_PATH, FileAccess.READ)
|
||||
var template_content: String = file.get_as_text()
|
||||
file.close()
|
||||
template_content = template_content.replace(
|
||||
"$SOURCE_FILE", png_path).replace(
|
||||
"$HIGH_QUALITY", str(high_quality_checkbox.button_pressed)).replace(
|
||||
"$GENERATE_MIPMAPS", str(generate_mipmaps_checkbox.button_pressed)
|
||||
)
|
||||
var import_content: String = template_content
|
||||
|
||||
var import_content: String = template_content.replace("$SOURCE_FILE", png_path)
|
||||
file = FileAccess.open(dst_import_path, FileAccess.WRITE)
|
||||
file.store_string(import_content)
|
||||
file.close()
|
||||
|
@ -328,8 +161,9 @@ func _on_pack_button_pressed() -> void:
|
|||
var packing_normal_roughness: bool = images[IMAGE_NORMAL] != null and images[IMAGE_ROUGHNESS] != null
|
||||
|
||||
if not packing_albedo and not packing_normal_roughness:
|
||||
_show_message(WARN, "Please select an albedo and height texture or a normal and roughness texture")
|
||||
_show_error("Please select an albedo and height texture or a normal and roughness texture.")
|
||||
return
|
||||
|
||||
if packing_albedo:
|
||||
save_file_dialog.current_path = last_saved_directory + "packed_albedo_height"
|
||||
save_file_dialog.title = "Save Packed Albedo/Height Texture"
|
||||
|
@ -345,119 +179,34 @@ func _on_pack_button_pressed() -> void:
|
|||
|
||||
func _on_save_file_selected(p_dst_path) -> void:
|
||||
last_saved_directory = p_dst_path.get_base_dir() + "/"
|
||||
var error: int
|
||||
if packing_albedo:
|
||||
error = _pack_textures(images[IMAGE_ALBEDO], images[IMAGE_HEIGHT], p_dst_path, false,
|
||||
invert_height_checkbox.button_pressed, false, height_channel_selected)
|
||||
_pack_textures(images[IMAGE_ALBEDO], images[IMAGE_HEIGHT], p_dst_path, false)
|
||||
else:
|
||||
error = _pack_textures(images[IMAGE_NORMAL], images[IMAGE_ROUGHNESS], p_dst_path,
|
||||
invert_green_checkbox.button_pressed, invert_smooth_checkbox.button_pressed,
|
||||
align_normals_checkbox.button_pressed, roughness_channel_selected)
|
||||
|
||||
if error == OK:
|
||||
EditorInterface.get_resource_filesystem().scan()
|
||||
if window.visible:
|
||||
window.hide()
|
||||
await EditorInterface.get_resource_filesystem().resources_reimported
|
||||
# wait 1 extra frame, to ensure the UI is responsive.
|
||||
await RenderingServer.frame_post_draw
|
||||
window.show()
|
||||
_pack_textures(images[IMAGE_NORMAL], images[IMAGE_ROUGHNESS], p_dst_path, invert_green_checkbox.button_pressed)
|
||||
|
||||
if queue_pack_normal_roughness:
|
||||
queue_pack_normal_roughness = false
|
||||
packing_albedo = false
|
||||
save_file_dialog.current_path = last_saved_directory + "packed_normal_roughness"
|
||||
save_file_dialog.title = "Save Packed Normal/Roughness Texture"
|
||||
|
||||
save_file_dialog.call_deferred("popup_centered_ratio")
|
||||
save_file_dialog.call_deferred("move_to_foreground")
|
||||
|
||||
|
||||
func _alignment_basis(normal: Vector3) -> Basis:
|
||||
var up: Vector3 = Vector3(0, 0, 1)
|
||||
var v: Vector3 = normal.cross(up)
|
||||
var c: float = normal.dot(up)
|
||||
var k: float = 1.0 / (1.0 + c)
|
||||
|
||||
var vxy: float = v.x * v.y * k
|
||||
var vxz: float = v.x * v.z * k
|
||||
var vyz: float = v.y * v.z * k
|
||||
|
||||
return Basis(Vector3(v.x * v.x * k + c, vxy - v.z, vxz + v.y),
|
||||
Vector3(vxy + v.z, v.y * v.y * k + c, vyz - v.x),
|
||||
Vector3(vxz - v.y, vyz + v.x, v.z * v.z * k + c)
|
||||
)
|
||||
|
||||
|
||||
func _set_normal_vector(source: Image, quiet: bool = false) -> void:
|
||||
# Calculate texture normal sum direction
|
||||
var normal: Image = source
|
||||
var sum: Color = Color(0.0, 0.0, 0.0, 0.0)
|
||||
for x in normal.get_height():
|
||||
for y in normal.get_width():
|
||||
sum += normal.get_pixel(x, y)
|
||||
var div: float = normal.get_height() * normal.get_width()
|
||||
sum /= Color(div, div, div)
|
||||
sum *= 2.0
|
||||
sum -= Color(1.0, 1.0, 1.0)
|
||||
normal_vector = Vector3(sum.r, sum.g, sum.b).normalized()
|
||||
if normal_vector.dot(Vector3(0.0, 0.0, 1.0)) < 0.999 && !quiet:
|
||||
_show_message(WARN, "Normal Texture Not Orthoganol to UV plane.\nFor Compatability with Detiling and Rotation, Select Orthoganolize Normals")
|
||||
|
||||
|
||||
func _align_normals(source: Image, iteration: int = 0) -> void:
|
||||
# generate matrix to re-align the normalmap
|
||||
var mat3: Basis = _alignment_basis(normal_vector)
|
||||
# re-align the normal map pixels
|
||||
for x in source.get_height():
|
||||
for y in source.get_width():
|
||||
var old_pixel: Color = source.get_pixel(x, y)
|
||||
var vector_pixel: Vector3 = Vector3(old_pixel.r, old_pixel.g, old_pixel.b)
|
||||
vector_pixel *= 2.0
|
||||
vector_pixel -= Vector3.ONE
|
||||
vector_pixel = vector_pixel.normalized()
|
||||
vector_pixel = vector_pixel * mat3
|
||||
vector_pixel += Vector3.ONE
|
||||
vector_pixel *= 0.5
|
||||
var new_pixel: Color = Color(vector_pixel.x, vector_pixel.y, vector_pixel.z, old_pixel.a)
|
||||
source.set_pixel(x, y, new_pixel)
|
||||
_set_normal_vector(source, true)
|
||||
if normal_vector.dot(Vector3(0.0, 0.0, 1.0)) < 0.999 && iteration < 3:
|
||||
++iteration
|
||||
_align_normals(source, iteration)
|
||||
|
||||
|
||||
func _pack_textures(p_rgb_image: Image, p_a_image: Image, p_dst_path: String, p_invert_green: bool,
|
||||
p_invert_smooth: bool, p_align_normals : bool, p_alpha_channel: int) -> Error:
|
||||
func _pack_textures(p_rgb_image: Image, p_a_image: Image, p_dst_path: String, p_invert_green: bool) -> void:
|
||||
if p_rgb_image and p_a_image:
|
||||
if p_rgb_image.get_size() != p_a_image.get_size() and !resize_toggle_checkbox.button_pressed:
|
||||
_show_message(ERROR, "Textures must be the same size.\nEnable resize to override image dimensions")
|
||||
return FAILED
|
||||
if p_rgb_image.get_size() != p_a_image.get_size():
|
||||
_show_error("Textures must be the same size.")
|
||||
return
|
||||
|
||||
if resize_toggle_checkbox.button_pressed:
|
||||
var size: int = max(128, resize_option_box.value)
|
||||
p_rgb_image.resize(size, size, Image.INTERPOLATE_CUBIC)
|
||||
p_a_image.resize(size, size, Image.INTERPOLATE_CUBIC)
|
||||
|
||||
if p_align_normals and normal_vector.dot(Vector3(0.0, 0.0, 1.0)) < 0.999:
|
||||
_align_normals(p_rgb_image)
|
||||
elif p_align_normals:
|
||||
_show_message(INFO, "Alignment OK, skipping Normal Orthogonalization")
|
||||
|
||||
var output_image: Image = Terrain3DUtil.pack_image(p_rgb_image, p_a_image,
|
||||
p_invert_green, p_invert_smooth, p_alpha_channel)
|
||||
var output_image: Image = Terrain3DUtil.pack_image(p_rgb_image, p_a_image, p_invert_green)
|
||||
|
||||
if not output_image:
|
||||
_show_message(ERROR, "Failed to pack textures")
|
||||
return FAILED
|
||||
if output_image.detect_used_channels() != 5:
|
||||
_show_message(ERROR, "Packing Error, Alpha Channel empty")
|
||||
return FAILED
|
||||
_show_error("Failed to pack textures.")
|
||||
return
|
||||
|
||||
output_image.save_png(p_dst_path)
|
||||
editor_interface.get_resource_filesystem().scan_sources()
|
||||
_create_import_file(p_dst_path)
|
||||
_show_message(INFO, "Packed to " + p_dst_path + ".")
|
||||
return OK
|
||||
_show_success("Packed to " + p_dst_path + ".")
|
||||
else:
|
||||
_show_message(ERROR, "Failed to load one or more textures")
|
||||
return FAILED
|
||||
_show_error("Failed to load one or more textures.")
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
[gd_scene load_steps=7 format=3 uid="uid://nud6dwjcnj5v"]
|
||||
[gd_scene load_steps=5 format=3 uid="uid://nud6dwjcnj5v"]
|
||||
|
||||
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_ysabf"]
|
||||
bg_color = Color(0.211765, 0.239216, 0.290196, 1)
|
||||
|
@ -30,21 +30,21 @@ corner_radius_bottom_left = 5
|
|||
|
||||
[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_7qdas"]
|
||||
|
||||
[sub_resource type="ButtonGroup" id="ButtonGroup_wnxik"]
|
||||
|
||||
[sub_resource type="ButtonGroup" id="ButtonGroup_bs6ki"]
|
||||
|
||||
[node name="Window" type="Window"]
|
||||
[node name="AcceptDialog" type="AcceptDialog"]
|
||||
title = "Terrain3D Channel Packer"
|
||||
initial_position = 1
|
||||
size = Vector2i(680, 835)
|
||||
unresizable = true
|
||||
always_on_top = true
|
||||
size = Vector2i(660, 900)
|
||||
visible = true
|
||||
ok_button_text = "Close"
|
||||
|
||||
[node name="Panel" type="Panel" parent="."]
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
offset_left = 8.0
|
||||
offset_top = 8.0
|
||||
offset_right = -8.0
|
||||
offset_bottom = -49.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
theme_override_styles/panel = SubResource("StyleBoxFlat_ysabf")
|
||||
|
@ -54,10 +54,10 @@ layout_mode = 1
|
|||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
offset_left = 5.0
|
||||
offset_top = 5.0
|
||||
offset_right = -5.0
|
||||
offset_bottom = 5.0
|
||||
offset_left = 4.0
|
||||
offset_top = 4.0
|
||||
offset_right = -1.0
|
||||
offset_bottom = -53.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
theme_override_constants/margin_left = 5
|
||||
|
@ -67,12 +67,12 @@ theme_override_constants/margin_bottom = 5
|
|||
|
||||
[node name="VBoxContainer" type="VBoxContainer" parent="Panel/MarginContainer"]
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 0
|
||||
theme_override_constants/separation = 10
|
||||
|
||||
[node name="AlbedoHeightPanel" type="Panel" parent="Panel/MarginContainer/VBoxContainer"]
|
||||
custom_minimum_size = Vector2(0, 290)
|
||||
custom_minimum_size = Vector2(0, 250)
|
||||
layout_mode = 2
|
||||
mouse_filter = 1
|
||||
theme_override_styles/panel = SubResource("StyleBoxFlat_lcvna")
|
||||
|
||||
[node name="MarginContainer" type="MarginContainer" parent="Panel/MarginContainer/VBoxContainer/AlbedoHeightPanel"]
|
||||
|
@ -147,27 +147,6 @@ grow_horizontal = 2
|
|||
grow_vertical = 2
|
||||
theme_override_styles/normal = SubResource("StyleBoxEmpty_7qdas")
|
||||
|
||||
[node name="AlbedoWHHBox" type="HBoxContainer" parent="Panel/MarginContainer/VBoxContainer/AlbedoHeightPanel/MarginContainer/HBoxContainer/AlbedoVBox"]
|
||||
layout_mode = 2
|
||||
alignment = 1
|
||||
|
||||
[node name="AlbedoW" type="Label" parent="Panel/MarginContainer/VBoxContainer/AlbedoHeightPanel/MarginContainer/HBoxContainer/AlbedoVBox/AlbedoWHHBox"]
|
||||
layout_mode = 2
|
||||
horizontal_alignment = 1
|
||||
|
||||
[node name="AlbedoH" type="Label" parent="Panel/MarginContainer/VBoxContainer/AlbedoHeightPanel/MarginContainer/HBoxContainer/AlbedoVBox/AlbedoWHHBox"]
|
||||
layout_mode = 2
|
||||
horizontal_alignment = 1
|
||||
|
||||
[node name="HBoxContainer2" type="HBoxContainer" parent="Panel/MarginContainer/VBoxContainer/AlbedoHeightPanel/MarginContainer/HBoxContainer/AlbedoVBox"]
|
||||
layout_mode = 2
|
||||
alignment = 1
|
||||
|
||||
[node name="LuminanceAsHeightButton" type="Button" parent="Panel/MarginContainer/VBoxContainer/AlbedoHeightPanel/MarginContainer/HBoxContainer/AlbedoVBox/HBoxContainer2"]
|
||||
layout_mode = 2
|
||||
text = " Generate Height from Luminance"
|
||||
icon_alignment = 2
|
||||
|
||||
[node name="HeightVBox" type="VBoxContainer" parent="Panel/MarginContainer/VBoxContainer/AlbedoHeightPanel/MarginContainer/HBoxContainer"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
|
@ -225,63 +204,8 @@ grow_horizontal = 2
|
|||
grow_vertical = 2
|
||||
theme_override_styles/normal = SubResource("StyleBoxEmpty_7qdas")
|
||||
|
||||
[node name="HeightWHHBox" type="HBoxContainer" parent="Panel/MarginContainer/VBoxContainer/AlbedoHeightPanel/MarginContainer/HBoxContainer/HeightVBox"]
|
||||
layout_mode = 2
|
||||
alignment = 1
|
||||
|
||||
[node name="HeightW" type="Label" parent="Panel/MarginContainer/VBoxContainer/AlbedoHeightPanel/MarginContainer/HBoxContainer/HeightVBox/HeightWHHBox"]
|
||||
layout_mode = 2
|
||||
horizontal_alignment = 1
|
||||
|
||||
[node name="HeightH" type="Label" parent="Panel/MarginContainer/VBoxContainer/AlbedoHeightPanel/MarginContainer/HBoxContainer/HeightVBox/HeightWHHBox"]
|
||||
layout_mode = 2
|
||||
horizontal_alignment = 1
|
||||
|
||||
[node name="HBoxContainer2" type="HBoxContainer" parent="Panel/MarginContainer/VBoxContainer/AlbedoHeightPanel/MarginContainer/HBoxContainer/HeightVBox"]
|
||||
layout_mode = 2
|
||||
alignment = 1
|
||||
|
||||
[node name="ConvertDepthToHeight" type="CheckBox" parent="Panel/MarginContainer/VBoxContainer/AlbedoHeightPanel/MarginContainer/HBoxContainer/HeightVBox/HBoxContainer2"]
|
||||
layout_mode = 2
|
||||
text = " Convert Depth to Height"
|
||||
icon_alignment = 2
|
||||
|
||||
[node name="HBoxContainer" type="HBoxContainer" parent="Panel/MarginContainer/VBoxContainer/AlbedoHeightPanel/MarginContainer/HBoxContainer/HeightVBox"]
|
||||
layout_mode = 2
|
||||
alignment = 1
|
||||
|
||||
[node name="HeightChannelLabel" type="Label" parent="Panel/MarginContainer/VBoxContainer/AlbedoHeightPanel/MarginContainer/HBoxContainer/HeightVBox/HBoxContainer"]
|
||||
layout_mode = 2
|
||||
text = " Source Channel: "
|
||||
horizontal_alignment = 2
|
||||
|
||||
[node name="HeightChannelR" type="Button" parent="Panel/MarginContainer/VBoxContainer/AlbedoHeightPanel/MarginContainer/HBoxContainer/HeightVBox/HBoxContainer"]
|
||||
layout_mode = 2
|
||||
toggle_mode = true
|
||||
button_pressed = true
|
||||
button_group = SubResource("ButtonGroup_wnxik")
|
||||
text = "R"
|
||||
|
||||
[node name="HeightChannelB" type="Button" parent="Panel/MarginContainer/VBoxContainer/AlbedoHeightPanel/MarginContainer/HBoxContainer/HeightVBox/HBoxContainer"]
|
||||
layout_mode = 2
|
||||
toggle_mode = true
|
||||
button_group = SubResource("ButtonGroup_wnxik")
|
||||
text = "G"
|
||||
|
||||
[node name="HeightChannelG" type="Button" parent="Panel/MarginContainer/VBoxContainer/AlbedoHeightPanel/MarginContainer/HBoxContainer/HeightVBox/HBoxContainer"]
|
||||
layout_mode = 2
|
||||
toggle_mode = true
|
||||
button_group = SubResource("ButtonGroup_wnxik")
|
||||
text = "B"
|
||||
|
||||
[node name="HeightChannelA" type="Button" parent="Panel/MarginContainer/VBoxContainer/AlbedoHeightPanel/MarginContainer/HBoxContainer/HeightVBox/HBoxContainer"]
|
||||
layout_mode = 2
|
||||
toggle_mode = true
|
||||
button_group = SubResource("ButtonGroup_wnxik")
|
||||
text = "A"
|
||||
|
||||
[node name="NormalRoughnessPanel" type="Panel" parent="Panel/MarginContainer/VBoxContainer"]
|
||||
custom_minimum_size = Vector2(0, 290)
|
||||
custom_minimum_size = Vector2(0, 280)
|
||||
layout_mode = 2
|
||||
theme_override_styles/panel = SubResource("StyleBoxFlat_lcvna")
|
||||
|
||||
|
@ -357,33 +281,9 @@ grow_horizontal = 2
|
|||
grow_vertical = 2
|
||||
theme_override_styles/normal = SubResource("StyleBoxEmpty_7qdas")
|
||||
|
||||
[node name="NormalWHHBox" type="HBoxContainer" parent="Panel/MarginContainer/VBoxContainer/NormalRoughnessPanel/MarginContainer/HBoxContainer/NormalVBox"]
|
||||
[node name="InvertGreenChannelCheckBox" type="CheckBox" parent="Panel/MarginContainer/VBoxContainer/NormalRoughnessPanel/MarginContainer/HBoxContainer/NormalVBox"]
|
||||
layout_mode = 2
|
||||
alignment = 1
|
||||
|
||||
[node name="NormalW" type="Label" parent="Panel/MarginContainer/VBoxContainer/NormalRoughnessPanel/MarginContainer/HBoxContainer/NormalVBox/NormalWHHBox"]
|
||||
layout_mode = 2
|
||||
horizontal_alignment = 1
|
||||
|
||||
[node name="NormalH" type="Label" parent="Panel/MarginContainer/VBoxContainer/NormalRoughnessPanel/MarginContainer/HBoxContainer/NormalVBox/NormalWHHBox"]
|
||||
layout_mode = 2
|
||||
horizontal_alignment = 1
|
||||
|
||||
[node name="HBoxContainer" type="HBoxContainer" parent="Panel/MarginContainer/VBoxContainer/NormalRoughnessPanel/MarginContainer/HBoxContainer/NormalVBox"]
|
||||
layout_mode = 2
|
||||
alignment = 1
|
||||
|
||||
[node name="InvertGreenChannelCheckBox" type="CheckBox" parent="Panel/MarginContainer/VBoxContainer/NormalRoughnessPanel/MarginContainer/HBoxContainer/NormalVBox/HBoxContainer"]
|
||||
layout_mode = 2
|
||||
text = " Convert DirectX to OpenGL"
|
||||
|
||||
[node name="HBoxContainer2" type="HBoxContainer" parent="Panel/MarginContainer/VBoxContainer/NormalRoughnessPanel/MarginContainer/HBoxContainer/NormalVBox"]
|
||||
layout_mode = 2
|
||||
alignment = 1
|
||||
|
||||
[node name="AlignNormalsCheckBox" type="CheckBox" parent="Panel/MarginContainer/VBoxContainer/NormalRoughnessPanel/MarginContainer/HBoxContainer/NormalVBox/HBoxContainer2"]
|
||||
layout_mode = 2
|
||||
text = " Orthoganolise Normals"
|
||||
text = "Convert DirectX to OpenGL"
|
||||
|
||||
[node name="RoughnessVBox" type="VBoxContainer" parent="Panel/MarginContainer/VBoxContainer/NormalRoughnessPanel/MarginContainer/HBoxContainer"]
|
||||
layout_mode = 2
|
||||
|
@ -442,112 +342,18 @@ grow_horizontal = 2
|
|||
grow_vertical = 2
|
||||
theme_override_styles/normal = SubResource("StyleBoxEmpty_7qdas")
|
||||
|
||||
[node name="RoughnessWHHBox" type="HBoxContainer" parent="Panel/MarginContainer/VBoxContainer/NormalRoughnessPanel/MarginContainer/HBoxContainer/RoughnessVBox"]
|
||||
[node name="NormalRoughnessHBox" type="HBoxContainer" parent="Panel/MarginContainer/VBoxContainer"]
|
||||
layout_mode = 2
|
||||
alignment = 1
|
||||
|
||||
[node name="RoughnessW" type="Label" parent="Panel/MarginContainer/VBoxContainer/NormalRoughnessPanel/MarginContainer/HBoxContainer/RoughnessVBox/RoughnessWHHBox"]
|
||||
layout_mode = 2
|
||||
horizontal_alignment = 1
|
||||
|
||||
[node name="RoughnessH" type="Label" parent="Panel/MarginContainer/VBoxContainer/NormalRoughnessPanel/MarginContainer/HBoxContainer/RoughnessVBox/RoughnessWHHBox"]
|
||||
layout_mode = 2
|
||||
horizontal_alignment = 1
|
||||
|
||||
[node name="HBoxContainer2" type="HBoxContainer" parent="Panel/MarginContainer/VBoxContainer/NormalRoughnessPanel/MarginContainer/HBoxContainer/RoughnessVBox"]
|
||||
layout_mode = 2
|
||||
alignment = 1
|
||||
|
||||
[node name="InvertSmoothCheckBox" type="CheckBox" parent="Panel/MarginContainer/VBoxContainer/NormalRoughnessPanel/MarginContainer/HBoxContainer/RoughnessVBox/HBoxContainer2"]
|
||||
layout_mode = 2
|
||||
text = " Convert Smoothness to Roughness"
|
||||
|
||||
[node name="HBoxContainer" type="HBoxContainer" parent="Panel/MarginContainer/VBoxContainer/NormalRoughnessPanel/MarginContainer/HBoxContainer/RoughnessVBox"]
|
||||
layout_mode = 2
|
||||
alignment = 1
|
||||
|
||||
[node name="RoughnessChannelLabel" type="Label" parent="Panel/MarginContainer/VBoxContainer/NormalRoughnessPanel/MarginContainer/HBoxContainer/RoughnessVBox/HBoxContainer"]
|
||||
layout_mode = 2
|
||||
text = " Source Channel: "
|
||||
horizontal_alignment = 2
|
||||
|
||||
[node name="RoughnessChannelR" type="Button" parent="Panel/MarginContainer/VBoxContainer/NormalRoughnessPanel/MarginContainer/HBoxContainer/RoughnessVBox/HBoxContainer"]
|
||||
layout_mode = 2
|
||||
toggle_mode = true
|
||||
button_pressed = true
|
||||
button_group = SubResource("ButtonGroup_bs6ki")
|
||||
text = "R"
|
||||
|
||||
[node name="RoughnessChannelG" type="Button" parent="Panel/MarginContainer/VBoxContainer/NormalRoughnessPanel/MarginContainer/HBoxContainer/RoughnessVBox/HBoxContainer"]
|
||||
layout_mode = 2
|
||||
toggle_mode = true
|
||||
button_group = SubResource("ButtonGroup_bs6ki")
|
||||
text = "G"
|
||||
|
||||
[node name="RoughnessChannelB" type="Button" parent="Panel/MarginContainer/VBoxContainer/NormalRoughnessPanel/MarginContainer/HBoxContainer/RoughnessVBox/HBoxContainer"]
|
||||
layout_mode = 2
|
||||
toggle_mode = true
|
||||
button_group = SubResource("ButtonGroup_bs6ki")
|
||||
text = "B"
|
||||
|
||||
[node name="RoughnessChannelA" type="Button" parent="Panel/MarginContainer/VBoxContainer/NormalRoughnessPanel/MarginContainer/HBoxContainer/RoughnessVBox/HBoxContainer"]
|
||||
layout_mode = 2
|
||||
toggle_mode = true
|
||||
button_group = SubResource("ButtonGroup_bs6ki")
|
||||
text = "A"
|
||||
|
||||
[node name="GeneralOptionsLabel" type="Label" parent="Panel/MarginContainer/VBoxContainer"]
|
||||
layout_mode = 2
|
||||
text = "General Options"
|
||||
horizontal_alignment = 1
|
||||
vertical_alignment = 1
|
||||
|
||||
[node name="GeneralOptionsHBox" type="HBoxContainer" parent="Panel/MarginContainer/VBoxContainer"]
|
||||
custom_minimum_size = Vector2(0, 35)
|
||||
layout_mode = 2
|
||||
alignment = 1
|
||||
|
||||
[node name="ResizeToggle" type="CheckBox" parent="Panel/MarginContainer/VBoxContainer/GeneralOptionsHBox"]
|
||||
layout_mode = 2
|
||||
text = " Resize Packed Image"
|
||||
|
||||
[node name="ResizeOptionButton" type="SpinBox" parent="Panel/MarginContainer/VBoxContainer/GeneralOptionsHBox"]
|
||||
visible = false
|
||||
layout_mode = 2
|
||||
tooltip_text = "A value of 0 disables resizing."
|
||||
min_value = 128.0
|
||||
max_value = 4096.0
|
||||
step = 128.0
|
||||
value = 1024.0
|
||||
|
||||
[node name="VSeparator" type="VSeparator" parent="Panel/MarginContainer/VBoxContainer/GeneralOptionsHBox"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="GenerateMipmapsCheckBox" type="CheckBox" parent="Panel/MarginContainer/VBoxContainer/GeneralOptionsHBox"]
|
||||
layout_mode = 2
|
||||
button_pressed = true
|
||||
text = "Generate Mipmaps"
|
||||
|
||||
[node name="HighQualityCheckBox" type="CheckBox" parent="Panel/MarginContainer/VBoxContainer/GeneralOptionsHBox"]
|
||||
layout_mode = 2
|
||||
text = "Import High Quality"
|
||||
|
||||
[node name="PackButton" type="Button" parent="Panel/MarginContainer/VBoxContainer"]
|
||||
layout_mode = 2
|
||||
text = "Pack textures as..."
|
||||
|
||||
[node name="StatusLabel" type="Label" parent="Panel/MarginContainer/VBoxContainer"]
|
||||
custom_minimum_size = Vector2(0, 60)
|
||||
custom_minimum_size = Vector2(0, 10)
|
||||
layout_mode = 2
|
||||
horizontal_alignment = 1
|
||||
autowrap_mode = 3
|
||||
theme_override_colors/font_color = Color(1, 1, 1, 1)
|
||||
text = "Use this to create a packed Albedo + Height texture and/or a packed Normal + Roughness texture.
|
||||
|
||||
[node name="HBoxContainer" type="HBoxContainer" parent="Panel/MarginContainer/VBoxContainer"]
|
||||
layout_mode = 2
|
||||
alignment = 1
|
||||
|
||||
[node name="CloseButton" type="Button" parent="Panel/MarginContainer/VBoxContainer/HBoxContainer"]
|
||||
layout_mode = 2
|
||||
text = "Close"
|
||||
|
||||
[connection signal="toggled" from="Panel/MarginContainer/VBoxContainer/GeneralOptionsHBox/ResizeToggle" to="Panel/MarginContainer/VBoxContainer/GeneralOptionsHBox/ResizeOptionButton" method="set_visible"]
|
||||
You can then use these textures with Terrain3D."
|
||||
autowrap_mode = 2
|
||||
|
|
|
@ -1,15 +0,0 @@
|
|||
@tool
|
||||
extends Button
|
||||
|
||||
signal dropped
|
||||
|
||||
func _can_drop_data(p_position, p_data) -> bool:
|
||||
if typeof(p_data) == TYPE_DICTIONARY:
|
||||
if p_data.files.size() == 1:
|
||||
match p_data.files[0].get_extension():
|
||||
"png", "bmp", "exr", "hdr", "jpg", "jpeg", "tga", "svg", "webp", "ktx", "dds":
|
||||
return true
|
||||
return false
|
||||
|
||||
func _drop_data(p_position, p_data) -> void:
|
||||
dropped.emit(p_data.files[0])
|
|
@ -14,12 +14,12 @@ source_file="$SOURCE_FILE"
|
|||
[params]
|
||||
|
||||
compress/mode=2
|
||||
compress/high_quality=$HIGH_QUALITY
|
||||
compress/high_quality=false
|
||||
compress/lossy_quality=0.7
|
||||
compress/hdr_compression=1
|
||||
compress/normal_map=2
|
||||
compress/channel_pack=0
|
||||
mipmaps/generate=$GENERATE_MIPMAPS
|
||||
mipmaps/generate=true
|
||||
mipmaps/limit=-1
|
||||
roughness/mode=0
|
||||
roughness/src_normal=""
|
||||
|
|
|
@ -1,116 +0,0 @@
|
|||
extends Node
|
||||
|
||||
const DIRECTORY_SETUP: String = "res://addons/terrain_3d/src/directory_setup.tscn"
|
||||
|
||||
var plugin: EditorPlugin
|
||||
var dialog: ConfirmationDialog
|
||||
var select_dir_btn: Button
|
||||
var selected_dir_le: LineEdit
|
||||
var select_upg_btn: Button
|
||||
var upgrade_file_le: LineEdit
|
||||
var editor_file_dialog: EditorFileDialog
|
||||
|
||||
|
||||
func _init() -> void:
|
||||
editor_file_dialog = EditorFileDialog.new()
|
||||
editor_file_dialog.set_filters(PackedStringArray(["*.res"]))
|
||||
editor_file_dialog.set_file_mode(EditorFileDialog.FILE_MODE_SAVE_FILE)
|
||||
editor_file_dialog.access = EditorFileDialog.ACCESS_RESOURCES
|
||||
editor_file_dialog.ok_button_text = "Open"
|
||||
editor_file_dialog.title = "Open a folder or file"
|
||||
editor_file_dialog.file_selected.connect(_on_file_selected)
|
||||
editor_file_dialog.dir_selected.connect(_on_dir_selected)
|
||||
editor_file_dialog.size = Vector2i(850, 550)
|
||||
editor_file_dialog.transient = false
|
||||
editor_file_dialog.exclusive = false
|
||||
editor_file_dialog.popup_window = true
|
||||
add_child(editor_file_dialog)
|
||||
|
||||
|
||||
func directory_setup_popup() -> void:
|
||||
dialog = load(DIRECTORY_SETUP).instantiate()
|
||||
dialog.hide()
|
||||
|
||||
# Nodes
|
||||
select_dir_btn = dialog.get_node("Margin/VBox/DirHBox/SelectDir")
|
||||
selected_dir_le = dialog.get_node("Margin/VBox/DirHBox/LineEdit")
|
||||
select_upg_btn = dialog.get_node("Margin/VBox/UpgradeHBox/SelectResFile")
|
||||
upgrade_file_le = dialog.get_node("Margin/VBox/UpgradeHBox/LineEdit")
|
||||
upgrade_file_le.text = ""
|
||||
|
||||
if plugin.terrain.data_directory:
|
||||
selected_dir_le.text = plugin.terrain.data_directory
|
||||
|
||||
if plugin.terrain.storage:
|
||||
upgrade_file_le.text = plugin.terrain.storage.get_path()
|
||||
|
||||
# Icons
|
||||
plugin.ui.set_button_editor_icon(select_upg_btn, "Folder")
|
||||
plugin.ui.set_button_editor_icon(select_dir_btn, "Folder")
|
||||
|
||||
#Signals
|
||||
select_upg_btn.pressed.connect(_on_select_file_pressed.bind(EditorFileDialog.FILE_MODE_OPEN_FILE))
|
||||
select_dir_btn.pressed.connect(_on_select_file_pressed.bind(EditorFileDialog.FILE_MODE_OPEN_DIR))
|
||||
dialog.confirmed.connect(_on_close_requested)
|
||||
dialog.canceled.connect(_on_close_requested)
|
||||
dialog.get_ok_button().pressed.connect(_on_ok_pressed)
|
||||
|
||||
# Popup
|
||||
EditorInterface.popup_dialog_centered(dialog)
|
||||
|
||||
|
||||
func _on_close_requested() -> void:
|
||||
dialog.queue_free()
|
||||
dialog = null
|
||||
|
||||
|
||||
func _on_select_file_pressed(file_mode: EditorFileDialog.FileMode) -> void:
|
||||
editor_file_dialog.file_mode = file_mode
|
||||
editor_file_dialog.popup_centered()
|
||||
|
||||
|
||||
func _on_dir_selected(path: String) -> void:
|
||||
selected_dir_le.text = path
|
||||
|
||||
|
||||
func _on_file_selected(path: String) -> void:
|
||||
upgrade_file_le.text = path
|
||||
|
||||
|
||||
func _on_ok_pressed() -> void:
|
||||
if not plugin.terrain:
|
||||
push_error("Not connected terrain. Click the Terrain3D node first")
|
||||
return
|
||||
if selected_dir_le.text.is_empty():
|
||||
push_error("No data directory specified")
|
||||
return
|
||||
if not DirAccess.dir_exists_absolute(selected_dir_le.text):
|
||||
push_error("Directory doesn't exist: ", selected_dir_le.text)
|
||||
return
|
||||
# Check if directory empty of terrain files
|
||||
var data_found: bool = false
|
||||
var files: Array = DirAccess.get_files_at(selected_dir_le.text)
|
||||
for file in files:
|
||||
if file.begins_with("terrain3d") || file.ends_with(".res"):
|
||||
data_found = true
|
||||
break
|
||||
|
||||
print("Setting terrain directory: ", selected_dir_le.text)
|
||||
plugin.terrain.data_directory = selected_dir_le.text
|
||||
|
||||
if not upgrade_file_le.text.is_empty():
|
||||
if data_found:
|
||||
push_warning("Target directory already has terrain data. Specify an empty directory to upgrade")
|
||||
return
|
||||
if not FileAccess.file_exists(upgrade_file_le.text):
|
||||
push_error("File doesn't exist: ", upgrade_file_le.text)
|
||||
return
|
||||
|
||||
if not plugin.terrain.storage or \
|
||||
( plugin.terrain.storage and plugin.terrain.storage.get_path() != upgrade_file_le.text):
|
||||
print("Loading storage file: ", upgrade_file_le.text)
|
||||
plugin.terrain.set_storage(load(upgrade_file_le.text))
|
||||
|
||||
if plugin.terrain.storage:
|
||||
print("Begining upgrade of: ", upgrade_file_le.text)
|
||||
plugin.terrain.split_storage()
|
|
@ -1,66 +0,0 @@
|
|||
[gd_scene format=3 uid="uid://by3kr2nqbqr67"]
|
||||
|
||||
[node name="DirectorySetup" type="ConfirmationDialog"]
|
||||
title = "Terrain3D Data Directory Setup"
|
||||
position = Vector2i(0, 36)
|
||||
size = Vector2i(750, 574)
|
||||
visible = true
|
||||
|
||||
[node name="Margin" type="MarginContainer" parent="."]
|
||||
offset_left = 8.0
|
||||
offset_top = 8.0
|
||||
offset_right = 742.0
|
||||
offset_bottom = 525.0
|
||||
theme_override_constants/margin_left = 20
|
||||
theme_override_constants/margin_top = 20
|
||||
theme_override_constants/margin_right = 20
|
||||
theme_override_constants/margin_bottom = 20
|
||||
|
||||
[node name="VBox" type="VBoxContainer" parent="Margin"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="Instructions" type="Label" parent="Margin/VBox"]
|
||||
custom_minimum_size = Vector2(400, 0)
|
||||
layout_mode = 2
|
||||
text = "Terrain3D now stores data in a directory instead of a single file. Each region is stored in a separate file named `terrain3d[-_]##[-_]##.res`. For instance, the region at location (-1, 1) would be named `terrain3d-01_01.res`. Enable Terrain3D / Debug / Show Region Labels for a visual display."
|
||||
autowrap_mode = 3
|
||||
|
||||
[node name="DirectoryLabel" type="Label" parent="Margin/VBox"]
|
||||
custom_minimum_size = Vector2(400, 0)
|
||||
layout_mode = 2
|
||||
text = "
|
||||
Specify the directory to store your data. Any existing region files will be loaded."
|
||||
autowrap_mode = 3
|
||||
|
||||
[node name="DirHBox" type="HBoxContainer" parent="Margin/VBox"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="LineEdit" type="LineEdit" parent="Margin/VBox/DirHBox"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
placeholder_text = "Data directory"
|
||||
|
||||
[node name="SelectDir" type="Button" parent="Margin/VBox/DirHBox"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="UpgradeLabel" type="Label" parent="Margin/VBox"]
|
||||
custom_minimum_size = Vector2(400, 0)
|
||||
layout_mode = 2
|
||||
text = "
|
||||
If you wish to upgrade a storage file from v0.8.4 - v0.9.2, specify it below. Data will be stored in the directory above upon save. The original file will not be touched."
|
||||
autowrap_mode = 3
|
||||
|
||||
[node name="UpgradeHBox" type="HBoxContainer" parent="Margin/VBox"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="LineEdit" type="LineEdit" parent="Margin/VBox/UpgradeHBox"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
placeholder_text = "Storage .res to upgrade"
|
||||
|
||||
[node name="SelectResFile" type="Button" parent="Margin/VBox/UpgradeHBox"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="Spacer" type="Control" parent="Margin/VBox"]
|
||||
custom_minimum_size = Vector2(0, 40)
|
||||
layout_mode = 2
|
|
@ -1,124 +0,0 @@
|
|||
@tool
|
||||
class_name DoubleSlider
|
||||
extends Control
|
||||
|
||||
signal value_changed(Vector2)
|
||||
var label: Label
|
||||
var suffix: String
|
||||
var grabbed_handle: int = 0 # -1 left, 0 none, 1 right
|
||||
var min_value: float = 0.0
|
||||
var max_value: float = 100.0
|
||||
var step: float = 1.0
|
||||
var range := Vector2(0, 100)
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
update_label()
|
||||
|
||||
|
||||
func set_min(p_value: float) -> void:
|
||||
min_value = p_value
|
||||
if range.x <= min_value:
|
||||
range.x = min_value
|
||||
set_value(range)
|
||||
update_label()
|
||||
|
||||
|
||||
func get_min() -> float:
|
||||
return min_value
|
||||
|
||||
|
||||
func set_max(p_value: float) -> void:
|
||||
max_value = p_value
|
||||
if range.y == 0 or range.y >= max_value:
|
||||
range.y = max_value
|
||||
set_value(range)
|
||||
update_label()
|
||||
|
||||
|
||||
func get_max() -> float:
|
||||
return max_value
|
||||
|
||||
|
||||
func set_step(p_step: float) -> void:
|
||||
step = p_step
|
||||
|
||||
|
||||
func get_step() -> float:
|
||||
return step
|
||||
|
||||
|
||||
func set_value(p_range: Vector2) -> void:
|
||||
range.x = clamp(p_range.x, min_value, max_value)
|
||||
range.y = clamp(p_range.y, min_value, max_value)
|
||||
if range.y < range.x:
|
||||
var tmp: float = range.x
|
||||
range.x = range.y
|
||||
range.y = tmp
|
||||
|
||||
update_label()
|
||||
emit_signal("value_changed", Vector2(range.x, range.y))
|
||||
queue_redraw()
|
||||
|
||||
|
||||
func get_value() -> Vector2:
|
||||
return range
|
||||
|
||||
|
||||
func update_label() -> void:
|
||||
if label:
|
||||
label.set_text(str(range.x) + suffix + "/" + str(range.y) + suffix)
|
||||
|
||||
|
||||
func _gui_input(p_event: InputEvent) -> void:
|
||||
if p_event is InputEventMouseButton:
|
||||
if p_event.get_button_index() == MOUSE_BUTTON_LEFT:
|
||||
if p_event.is_pressed():
|
||||
var mid_point = (range.x + range.y) / 2.0
|
||||
var xpos: float = p_event.get_position().x * 2.0
|
||||
if xpos >= mid_point:
|
||||
grabbed_handle = 1
|
||||
else:
|
||||
grabbed_handle = -1
|
||||
set_slider(p_event.get_position().x)
|
||||
else:
|
||||
grabbed_handle = 0
|
||||
|
||||
if p_event is InputEventMouseMotion:
|
||||
if grabbed_handle != 0:
|
||||
set_slider(p_event.get_position().x)
|
||||
|
||||
|
||||
func set_slider(p_xpos: float) -> void:
|
||||
if grabbed_handle == 0:
|
||||
return
|
||||
var xpos_step: float = clamp(snappedf((p_xpos / size.x) * max_value, step), min_value, max_value)
|
||||
if(grabbed_handle < 0):
|
||||
range.x = xpos_step
|
||||
else:
|
||||
range.y = xpos_step
|
||||
set_value(range)
|
||||
|
||||
|
||||
func _notification(p_what: int) -> void:
|
||||
if p_what == NOTIFICATION_DRAW:
|
||||
# Draw background bar
|
||||
var bg: StyleBox = get_theme_stylebox("slider", "HSlider")
|
||||
var bg_height: float = bg.get_minimum_size().y
|
||||
var mid_y: float = (size.y - bg_height) / 2.0
|
||||
draw_style_box(bg, Rect2(Vector2(0, mid_y), Vector2(size.x, bg_height)))
|
||||
|
||||
# Draw foreground bar
|
||||
var handle: Texture2D = get_theme_icon("grabber", "HSlider")
|
||||
var area: StyleBox = get_theme_stylebox("grabber_area", "HSlider")
|
||||
var h: float = size.y / 2 - handle.get_size().y / 2
|
||||
var startx: float = (range.x / max_value) * size.x
|
||||
var endx: float = (range.y / max_value) * size.x #- startx
|
||||
draw_style_box(area, Rect2(Vector2(startx, mid_y), Vector2(endx - startx, bg_height)))
|
||||
|
||||
# Draw handles, slightly in so they don't get on the outside edges
|
||||
var handle_pos: Vector2
|
||||
handle_pos.x = clamp(startx - handle.get_size().x/2, -5, size.x)
|
||||
handle_pos.y = clamp(endx - handle.get_size().x/2, 0, size.x - 10)
|
||||
draw_texture(handle, Vector2(handle_pos.x, -mid_y))
|
||||
draw_texture(handle, Vector2(handle_pos.y, -mid_y))
|
|
@ -5,6 +5,7 @@ signal pressed
|
|||
signal value_changed
|
||||
|
||||
|
||||
const ICON_PICKER: String = "res://addons/terrain_3d/icons/picker.svg"
|
||||
const ICON_PICKER_CHECKED: String = "res://addons/terrain_3d/icons/picker_checked.svg"
|
||||
const MAX_POINTS: int = 2
|
||||
|
||||
|
@ -15,8 +16,8 @@ var points: PackedVector3Array
|
|||
var picking_index: int = -1
|
||||
|
||||
|
||||
func _enter_tree() -> void:
|
||||
icon_picker = get_theme_icon("ColorPick", "EditorIcons")
|
||||
func _init() -> void:
|
||||
icon_picker = load(ICON_PICKER)
|
||||
icon_picker_checked = load(ICON_PICKER_CHECKED)
|
||||
|
||||
points.resize(MAX_POINTS)
|
||||
|
|
|
@ -32,7 +32,7 @@ func _redraw() -> void:
|
|||
|
||||
if show_rect:
|
||||
var modulate: Color = main_color if !use_secondary_color else secondary_color
|
||||
if abs(region_position.x) > Terrain3DData.REGION_MAP_SIZE*.5 or abs(region_position.y) > Terrain3DData.REGION_MAP_SIZE*.5:
|
||||
if abs(region_position.x) > 8 or abs(region_position.y) > 8:
|
||||
modulate = Color.GRAY
|
||||
draw_rect(Vector2(region_size,region_size)*.5 + rect_position, region_size, selection_material, modulate)
|
||||
|
||||
|
@ -44,7 +44,7 @@ func _redraw() -> void:
|
|||
|
||||
draw_rect(Vector2(region_size,region_size)*.5 + grid_tile_position, region_size, material, grid_color)
|
||||
|
||||
draw_rect(Vector2.ZERO, region_size * Terrain3DData.REGION_MAP_SIZE, material, border_color)
|
||||
draw_rect(Vector2.ZERO, region_size * 16.0, material, border_color)
|
||||
|
||||
|
||||
func draw_rect(p_pos: Vector2, p_size: float, p_material: StandardMaterial3D, p_modulate: Color) -> void:
|
||||
|
|
|
@ -1,72 +1,68 @@
|
|||
extends HBoxContainer
|
||||
|
||||
|
||||
const DirectoryWizard: Script = preload("res://addons/terrain_3d/src/directory_setup.gd")
|
||||
const Packer: Script = preload("res://addons/terrain_3d/src/channel_packer.gd")
|
||||
const Baker: Script = preload("res://addons/terrain_3d/src/baker.gd")
|
||||
const Packer: Script = preload("res://addons/terrain_3d/src/channel_packer.gd")
|
||||
|
||||
var plugin: EditorPlugin
|
||||
var menu_button: MenuButton = MenuButton.new()
|
||||
var directory_setup: DirectoryWizard = DirectoryWizard.new()
|
||||
var packer: Packer = Packer.new()
|
||||
var baker: Baker = Baker.new()
|
||||
var packer: Packer = Packer.new()
|
||||
|
||||
# These are IDs and order must be consistent with add_item and set_disabled IDs
|
||||
enum {
|
||||
MENU_DIRECTORY_SETUP,
|
||||
MENU_PACK_TEXTURES,
|
||||
MENU_SEPARATOR,
|
||||
MENU_BAKE_ARRAY_MESH,
|
||||
MENU_BAKE_OCCLUDER,
|
||||
MENU_SEPARATOR2,
|
||||
MENU_SET_UP_NAVIGATION,
|
||||
MENU_BAKE_NAV_MESH,
|
||||
MENU_SEPARATOR,
|
||||
MENU_SET_UP_NAVIGATION,
|
||||
MENU_PACK_TEXTURES,
|
||||
}
|
||||
|
||||
|
||||
func _enter_tree() -> void:
|
||||
directory_setup.plugin = plugin
|
||||
packer.plugin = plugin
|
||||
baker.plugin = plugin
|
||||
add_child(directory_setup)
|
||||
packer.plugin = plugin
|
||||
|
||||
add_child(baker)
|
||||
|
||||
menu_button.text = "Terrain3D Tools"
|
||||
menu_button.get_popup().add_item("Directory Setup...", MENU_DIRECTORY_SETUP)
|
||||
menu_button.get_popup().add_item("Pack Textures...", MENU_PACK_TEXTURES)
|
||||
menu_button.get_popup().add_item("Bake ArrayMesh", MENU_BAKE_ARRAY_MESH)
|
||||
menu_button.get_popup().add_item("Bake Occluder3D", MENU_BAKE_OCCLUDER)
|
||||
menu_button.get_popup().add_item("Bake NavMesh", MENU_BAKE_NAV_MESH)
|
||||
menu_button.get_popup().add_separator("", MENU_SEPARATOR)
|
||||
menu_button.get_popup().add_item("Bake ArrayMesh...", MENU_BAKE_ARRAY_MESH)
|
||||
menu_button.get_popup().add_item("Bake Occluder3D...", MENU_BAKE_OCCLUDER)
|
||||
menu_button.get_popup().add_separator("", MENU_SEPARATOR2)
|
||||
menu_button.get_popup().add_item("Set up Navigation...", MENU_SET_UP_NAVIGATION)
|
||||
menu_button.get_popup().add_item("Bake NavMesh...", MENU_BAKE_NAV_MESH)
|
||||
menu_button.get_popup().add_item("Set up Navigation", MENU_SET_UP_NAVIGATION)
|
||||
menu_button.get_popup().add_separator("", MENU_SEPARATOR)
|
||||
menu_button.get_popup().add_item("Pack Textures", MENU_PACK_TEXTURES)
|
||||
|
||||
menu_button.get_popup().id_pressed.connect(_on_menu_pressed)
|
||||
menu_button.about_to_popup.connect(_on_menu_about_to_popup)
|
||||
add_child(menu_button)
|
||||
|
||||
|
||||
func _exit_tree() -> void:
|
||||
# TODO: If packer isn't freed, Godot complains about ObjectDB instances leaked and
|
||||
# resources still in use at exit. Figure out why.
|
||||
packer.free()
|
||||
|
||||
|
||||
func _on_menu_pressed(p_id: int) -> void:
|
||||
match p_id:
|
||||
MENU_DIRECTORY_SETUP:
|
||||
directory_setup.directory_setup_popup()
|
||||
MENU_PACK_TEXTURES:
|
||||
packer.pack_textures_popup()
|
||||
MENU_BAKE_ARRAY_MESH:
|
||||
baker.bake_mesh_popup()
|
||||
MENU_BAKE_OCCLUDER:
|
||||
baker.bake_occluder_popup()
|
||||
MENU_SET_UP_NAVIGATION:
|
||||
baker.set_up_navigation_popup()
|
||||
MENU_BAKE_NAV_MESH:
|
||||
baker.bake_nav_mesh()
|
||||
MENU_SET_UP_NAVIGATION:
|
||||
baker.set_up_navigation_popup()
|
||||
MENU_PACK_TEXTURES:
|
||||
packer.pack_textures_popup()
|
||||
|
||||
|
||||
func _on_menu_about_to_popup() -> void:
|
||||
menu_button.get_popup().set_item_disabled(MENU_DIRECTORY_SETUP, not plugin.terrain)
|
||||
menu_button.get_popup().set_item_disabled(MENU_PACK_TEXTURES, not plugin.terrain)
|
||||
menu_button.get_popup().set_item_disabled(MENU_BAKE_ARRAY_MESH, not plugin.terrain)
|
||||
menu_button.get_popup().set_item_disabled(MENU_BAKE_OCCLUDER, not plugin.terrain)
|
||||
menu_button.get_popup().set_item_disabled(MENU_PACK_TEXTURES, not plugin.terrain)
|
||||
|
||||
if plugin.terrain:
|
||||
var nav_regions: Array[NavigationRegion3D] = baker.find_terrain_nav_regions(plugin.terrain)
|
|
@ -17,14 +17,13 @@ enum SettingType {
|
|||
PICKER,
|
||||
MULTI_PICKER,
|
||||
SLIDER,
|
||||
LABEL,
|
||||
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 ES_TOOL_SETTINGS: String = "terrain3d/tool_settings/"
|
||||
const PICKER_ICON: String = "res://addons/terrain_3d/icons/picker.svg"
|
||||
|
||||
# Add settings flags
|
||||
const NONE: int = 0x0
|
||||
|
@ -32,14 +31,13 @@ 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 # Add a vertical line before this entry
|
||||
const ADD_SPACER: int = 0x10 # Add a space before this entry
|
||||
const NO_SAVE: int = 0x20 # Don't save this in EditorSettings
|
||||
const ADD_SEPARATOR: int = 0x8
|
||||
const ADD_SPACER: int = 0x10
|
||||
|
||||
var plugin: EditorPlugin # Actually Terrain3DEditorPlugin, but Godot still has CRC errors
|
||||
var brush_preview_material: ShaderMaterial
|
||||
var select_brush_button: Button
|
||||
var main_list: HFlowContainer
|
||||
|
||||
var main_list: HBoxContainer
|
||||
var advanced_list: VBoxContainer
|
||||
var height_list: VBoxContainer
|
||||
var scale_list: VBoxContainer
|
||||
|
@ -49,26 +47,21 @@ var settings: Dictionary = {}
|
|||
|
||||
|
||||
func _ready() -> void:
|
||||
# Remove old editor settings
|
||||
for setting in ["lift_floor", "flatten_peaks", "lift_flatten", "automatic_regions"]:
|
||||
plugin.erase_setting(ES_TOOL_SETTINGS + setting)
|
||||
|
||||
# Setup buttons
|
||||
main_list = HFlowContainer.new()
|
||||
main_list = HBoxContainer.new()
|
||||
add_child(main_list, true)
|
||||
|
||||
## Common Settings
|
||||
add_brushes(main_list)
|
||||
|
||||
add_setting({ "name":"instructions", "label":"Click the terrain to add a region. CTRL+Click to remove. Or select another tool on the left.",
|
||||
"type":SettingType.LABEL, "list":main_list, "flags":NO_LABEL|NO_SAVE })
|
||||
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":"size", "type":SettingType.SLIDER, "list":main_list, "default":20, "unit":"m",
|
||||
"range":Vector3(0.1, 200, 1), "flags":ALLOW_LARGER|ADD_SPACER })
|
||||
|
||||
add_setting({ "name":"strength", "type":SettingType.SLIDER, "list":main_list, "default":33,
|
||||
add_setting({ "name":"strength", "type":SettingType.SLIDER, "list":main_list, "default":10,
|
||||
"unit":"%", "range":Vector3(1, 100, 1), "flags":ALLOW_LARGER })
|
||||
|
||||
add_setting({ "name":"height", "type":SettingType.SLIDER, "list":main_list, "default":20,
|
||||
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 })
|
||||
|
@ -78,20 +71,13 @@ func _ready() -> void:
|
|||
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":-65,
|
||||
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, "flags":ADD_SEPARATOR })
|
||||
|
||||
add_setting({ "name":"margin", "type":SettingType.SLIDER, "list":main_list, "default":0,
|
||||
"unit":"", "range":Vector3(-50, 50, 1), "flags":ALLOW_OUT_OF_BOUNDS })
|
||||
|
||||
# Slope painting filter
|
||||
add_setting({ "name":"slope", "type":SettingType.DOUBLE_SLIDER, "list":main_list, "default":Vector2(0, 90),
|
||||
"unit":"°", "range":Vector3(0, 90, 1), "flags":ADD_SEPARATOR })
|
||||
"list":main_list, "default":true })
|
||||
|
||||
add_setting({ "name":"enable_angle", "label":"Angle", "type":SettingType.CHECKBOX,
|
||||
"list":main_list, "default":true, "flags":ADD_SEPARATOR })
|
||||
|
@ -102,16 +88,18 @@ func _ready() -> void:
|
|||
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,
|
||||
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 sculpting brush
|
||||
## 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.SCULPT, "flags":ADD_SEPARATOR })
|
||||
"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)
|
||||
|
@ -135,9 +123,9 @@ func _ready() -> void:
|
|||
"default":0, "unit":"°", "range":Vector3(0, 360, 1) })
|
||||
add_setting({ "name":"random_spin", "type":SettingType.SLIDER, "list":rotation_list, "default":360,
|
||||
"unit":"°", "range":Vector3(0, 360, 1) })
|
||||
add_setting({ "name":"fixed_tilt", "label":"Fixed Tilt", "type":SettingType.SLIDER, "list":rotation_list,
|
||||
add_setting({ "name":"fixed_angle", "label":"Fixed Angle (From Y)", "type":SettingType.SLIDER, "list":rotation_list,
|
||||
"default":0, "unit":"°", "range":Vector3(-85, 85, 1), "flags":ALLOW_OUT_OF_BOUNDS })
|
||||
add_setting({ "name":"random_tilt", "label":"Random Tilt ±", "type":SettingType.SLIDER, "list":rotation_list,
|
||||
add_setting({ "name":"random_angle", "label":"Random Angle ±", "type":SettingType.SLIDER, "list":rotation_list,
|
||||
"default":10, "unit":"°", "range":Vector3(0, 85, 1), "flags":ALLOW_OUT_OF_BOUNDS })
|
||||
add_setting({ "name":"align_to_normal", "type":SettingType.CHECKBOX, "list":rotation_list, "default":false })
|
||||
|
||||
|
@ -151,17 +139,14 @@ func _ready() -> void:
|
|||
#add_setting({ "name":"blend_mode", "type":SettingType.OPTION, "list":color_list, "default":0,
|
||||
#"range":Vector3(0, 3, 1) })
|
||||
|
||||
if DisplayServer.is_touchscreen_available():
|
||||
add_setting({ "name":"remove", "label":"Invert", "type":SettingType.CHECKBOX, "list":main_list, "default":false, "flags":ADD_SEPARATOR })
|
||||
|
||||
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, "", Layout.VERTICAL, false)
|
||||
add_setting({ "name":"auto_regions", "label":"Add regions while sculpting", "type":SettingType.CHECKBOX,
|
||||
"list":advanced_list, "default":true })
|
||||
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,
|
||||
|
@ -173,44 +158,23 @@ func _ready() -> void:
|
|||
"unit":"%", "range":Vector3(0, 100, 1) })
|
||||
|
||||
|
||||
func create_submenu(p_parent: Control, p_button_name: String, p_layout: Layout, p_hover_pop: bool = true) -> Container:
|
||||
func create_submenu(p_parent: Control, p_button_name: String, p_layout: Layout) -> Container:
|
||||
var menu_button: Button = Button.new()
|
||||
if p_button_name.is_empty():
|
||||
menu_button.icon = get_theme_icon("GuiTabMenuHl", "EditorIcons")
|
||||
else:
|
||||
menu_button.set_text(p_button_name)
|
||||
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.bind(false))
|
||||
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)
|
||||
submenu.add_to_group("terrain3d_submenus")
|
||||
|
||||
# Pop up menu on hover, hide on exit
|
||||
if p_hover_pop:
|
||||
menu_button.mouse_entered.connect(_on_show_submenu.bind(true, menu_button))
|
||||
|
||||
submenu.mouse_entered.connect(func(): submenu.set_meta("mouse_entered", true))
|
||||
|
||||
submenu.mouse_exited.connect(func():
|
||||
# On mouse_exit, hide popup unless LineEdit focused
|
||||
var focused_element: Control = submenu.gui_get_focus_owner()
|
||||
if not focused_element is LineEdit:
|
||||
_on_show_submenu(false, menu_button)
|
||||
submenu.set_meta("mouse_entered", false)
|
||||
return
|
||||
|
||||
focused_element.focus_exited.connect(func():
|
||||
# Close submenu once lineedit loses focus
|
||||
if not submenu.get_meta("mouse_entered"):
|
||||
_on_show_submenu(false, menu_button)
|
||||
submenu.set_meta("mouse_entered", false)
|
||||
)
|
||||
)
|
||||
menu_button.mouse_entered.connect(_on_show_submenu.bind(true, menu_button))
|
||||
submenu.mouse_exited.connect(_on_show_submenu.bind(false, menu_button))
|
||||
|
||||
var sublist: Container
|
||||
match(p_layout):
|
||||
|
@ -236,19 +200,18 @@ func _on_show_submenu(p_toggled: bool, p_button: Button) -> void:
|
|||
# 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 popup: PopupPanel = p_button.get_child(0)
|
||||
var popup_rect: Rect2 = Rect2(popup.position, popup.size)
|
||||
var in_popup: bool = popup_rect.has_point(DisplayServer.mouse_get_position())
|
||||
if not p_toggled and ( in_button or in_popup ):
|
||||
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
|
||||
|
||||
# Hide all submenus before possibly enabling the current one
|
||||
get_tree().call_group("terrain3d_submenus", "set_visible", false)
|
||||
popup.set_visible(p_toggled)
|
||||
var popup: PopupPanel = p_button.get_child(0)
|
||||
var popup_pos: Vector2 = p_button.get_screen_transform().origin
|
||||
popup_pos.y -= popup.size.y
|
||||
if popup.get_child_count()>0 and popup.get_child(0) == advanced_list:
|
||||
popup_pos.x -= popup.size.x - p_button.size.x
|
||||
popup.set_visible(p_toggled)
|
||||
popup_pos.y -= popup.get_size().y
|
||||
popup.set_position(popup_pos)
|
||||
|
||||
|
||||
|
@ -268,8 +231,6 @@ func add_brushes(p_parent: Control) -> void:
|
|||
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)
|
||||
if img.get_width() < 1024 and img.get_height() < 1024:
|
||||
img.resize(1024, 1024, Image.INTERPOLATE_CUBIC)
|
||||
var tex: ImageTexture = ImageTexture.create_from_image(img)
|
||||
|
||||
var btn: Button = Button.new()
|
||||
|
@ -309,11 +270,10 @@ func add_brushes(p_parent: Control) -> void:
|
|||
|
||||
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_text("")
|
||||
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)
|
||||
# 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:
|
||||
|
@ -347,12 +307,13 @@ func _on_picked(p_type: Terrain3DEditor.Tool, p_color: Color, p_global_position:
|
|||
|
||||
|
||||
func _on_point_pick(p_type: Terrain3DEditor.Tool, p_name: String) -> void:
|
||||
assert(p_type == Terrain3DEditor.SCULPT)
|
||||
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.SCULPT)
|
||||
assert(p_type == Terrain3DEditor.HEIGHT)
|
||||
|
||||
var point: Vector3 = p_global_position
|
||||
point.y = p_color.r
|
||||
settings[p_name].add_point(point)
|
||||
|
@ -381,22 +342,9 @@ func add_setting(p_args: Dictionary) -> void:
|
|||
var pending_children: Array[Control]
|
||||
|
||||
match p_type:
|
||||
SettingType.LABEL:
|
||||
var label := Label.new()
|
||||
label.set_text(p_label)
|
||||
pending_children.push_back(label)
|
||||
control = label
|
||||
|
||||
SettingType.CHECKBOX:
|
||||
var checkbox := CheckBox.new()
|
||||
if !(p_flags & NO_SAVE):
|
||||
checkbox.set_pressed_no_signal(plugin.get_setting(ES_TOOL_SETTINGS + p_name, p_default))
|
||||
checkbox.toggled.connect( (
|
||||
func(value, path):
|
||||
plugin.set_setting(path, value)
|
||||
).bind(ES_TOOL_SETTINGS + p_name) )
|
||||
else:
|
||||
checkbox.set_pressed_no_signal(p_default)
|
||||
checkbox.set_pressed_no_signal(p_default)
|
||||
checkbox.pressed.connect(_on_setting_changed)
|
||||
pending_children.push_back(checkbox)
|
||||
control = checkbox
|
||||
|
@ -404,24 +352,19 @@ func add_setting(p_args: Dictionary) -> void:
|
|||
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)
|
||||
if !(p_flags & NO_SAVE):
|
||||
picker.set_pick_color(plugin.get_setting(ES_TOOL_SETTINGS + p_name, p_default))
|
||||
picker.color_changed.connect( (
|
||||
func(value, path):
|
||||
plugin.set_setting(path, value)
|
||||
).bind(ES_TOOL_SETTINGS + p_name) )
|
||||
else:
|
||||
picker.set_pick_color(p_default)
|
||||
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.set_v_size_flags(SIZE_SHRINK_CENTER)
|
||||
button.icon = get_theme_icon("ColorPick", "EditorIcons")
|
||||
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)
|
||||
|
@ -454,9 +397,10 @@ func add_setting(p_args: Dictionary) -> void:
|
|||
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_v_size_flags(SIZE_SHRINK_CENTER)
|
||||
spin_slider.set_custom_minimum_size(Vector2(65, 0))
|
||||
spin_slider.set_custom_minimum_size(Vector2(75, 0))
|
||||
|
||||
# Create horizontal slider linked to the above box
|
||||
slider = HSlider.new()
|
||||
|
@ -465,38 +409,27 @@ func add_setting(p_args: Dictionary) -> void:
|
|||
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_custom_minimum_size(Vector2(60, 0))
|
||||
label.set_horizontal_alignment(HORIZONTAL_ALIGNMENT_RIGHT)
|
||||
label.set_custom_minimum_size(Vector2(75, 0))
|
||||
slider = DoubleSlider.new()
|
||||
slider.label = label
|
||||
slider.suffix = p_suffix
|
||||
slider.value_changed.connect(_on_setting_changed)
|
||||
slider.setting_changed.connect(_on_setting_changed)
|
||||
pending_children.push_back(slider)
|
||||
pending_children.push_back(label)
|
||||
control = slider
|
||||
|
||||
slider.set_min(p_minimum)
|
||||
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_custom_minimum_size(Vector2(50, 10))
|
||||
|
||||
if !(p_flags & NO_SAVE):
|
||||
slider.set_value(plugin.get_setting(ES_TOOL_SETTINGS + p_name, p_default))
|
||||
slider.value_changed.connect( (
|
||||
func(value, path):
|
||||
plugin.set_setting(path, value)
|
||||
).bind(ES_TOOL_SETTINGS + p_name) )
|
||||
else:
|
||||
slider.set_value(p_default)
|
||||
slider.set_custom_minimum_size(Vector2(60, 10))
|
||||
|
||||
control.name = p_name.to_pascal_case()
|
||||
settings[p_name] = control
|
||||
|
@ -558,8 +491,8 @@ func get_setting(p_setting: String) -> Variant:
|
|||
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 = object.get_value()
|
||||
elif object is ButtonGroup: # "brush"
|
||||
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 ]
|
||||
|
@ -576,10 +509,11 @@ func get_setting(p_setting: String) -> Variant:
|
|||
|
||||
func set_setting(p_setting: String, p_value: Variant) -> void:
|
||||
var object: Object = settings.get(p_setting)
|
||||
if object is DoubleSlider: # Expects p_value is Vector2
|
||||
object.set_value(p_value)
|
||||
elif object is Range:
|
||||
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():
|
||||
|
@ -589,7 +523,6 @@ func set_setting(p_setting: String, p_value: Variant) -> void:
|
|||
object.button_pressed = p_value
|
||||
elif object is ColorPickerButton:
|
||||
object.color = p_value
|
||||
plugin.set_setting(ES_TOOL_SETTINGS + p_setting, p_value) # Signal doesn't fire on CPB
|
||||
elif object is MultiPicker: # Expects p_value is PackedVector3Array
|
||||
object.points = p_value
|
||||
_on_setting_changed(object)
|
||||
|
@ -615,12 +548,13 @@ func _on_setting_changed(p_data: Variant = null) -> void:
|
|||
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())
|
||||
# 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")
|
||||
|
||||
|
||||
|
@ -633,6 +567,7 @@ 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"
|
||||
|
@ -643,11 +578,13 @@ func _get_brush_preview_material() -> ShaderMaterial:
|
|||
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
|
||||
|
@ -668,3 +605,75 @@ func count_digits(p_value: float) -> int:
|
|||
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)
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
extends VFlowContainer
|
||||
extends VBoxContainer
|
||||
|
||||
|
||||
signal tool_changed(p_tool: Terrain3DEditor.Tool, p_operation: Terrain3DEditor.Operation)
|
||||
|
||||
|
@ -6,6 +7,8 @@ const ICON_REGION_ADD: String = "res://addons/terrain_3d/icons/region_add.svg"
|
|||
const ICON_REGION_REMOVE: String = "res://addons/terrain_3d/icons/region_remove.svg"
|
||||
const ICON_HEIGHT_ADD: String = "res://addons/terrain_3d/icons/height_add.svg"
|
||||
const ICON_HEIGHT_SUB: String = "res://addons/terrain_3d/icons/height_sub.svg"
|
||||
const ICON_HEIGHT_MUL: String = "res://addons/terrain_3d/icons/height_mul.svg"
|
||||
const ICON_HEIGHT_DIV: String = "res://addons/terrain_3d/icons/height_div.svg"
|
||||
const ICON_HEIGHT_FLAT: String = "res://addons/terrain_3d/icons/height_flat.svg"
|
||||
const ICON_HEIGHT_SLOPE: String = "res://addons/terrain_3d/icons/height_slope.svg"
|
||||
const ICON_HEIGHT_SMOOTH: String = "res://addons/terrain_3d/icons/height_smooth.svg"
|
||||
|
@ -18,126 +21,57 @@ const ICON_HOLES: String = "res://addons/terrain_3d/icons/holes.svg"
|
|||
const ICON_NAVIGATION: String = "res://addons/terrain_3d/icons/navigation.svg"
|
||||
const ICON_INSTANCER: String = "res://addons/terrain_3d/icons/multimesh.svg"
|
||||
|
||||
var add_tool_group: ButtonGroup = ButtonGroup.new()
|
||||
var sub_tool_group: ButtonGroup = ButtonGroup.new()
|
||||
var tool_group: ButtonGroup = ButtonGroup.new()
|
||||
|
||||
|
||||
func _init() -> void:
|
||||
set_custom_minimum_size(Vector2(20, 0))
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
add_tool_group.connect("pressed", _on_tool_selected)
|
||||
sub_tool_group.connect("pressed", _on_tool_selected)
|
||||
|
||||
add_tool_button({ "tool":Terrain3DEditor.REGION,
|
||||
"add_text":"Add Region", "add_op":Terrain3DEditor.ADD, "add_icon":ICON_REGION_ADD,
|
||||
"sub_text":"Remove Region", "sub_op":Terrain3DEditor.SUBTRACT, "sub_icon":ICON_REGION_REMOVE })
|
||||
tool_group.connect("pressed", _on_tool_selected)
|
||||
|
||||
add_tool_button(Terrain3DEditor.REGION, Terrain3DEditor.ADD, "Add Region", load(ICON_REGION_ADD), tool_group)
|
||||
add_tool_button(Terrain3DEditor.REGION, Terrain3DEditor.SUBTRACT, "Remove Region", load(ICON_REGION_REMOVE), tool_group)
|
||||
add_child(HSeparator.new())
|
||||
|
||||
add_tool_button({ "tool":Terrain3DEditor.SCULPT,
|
||||
"add_text":"Raise", "add_op":Terrain3DEditor.ADD, "add_icon":ICON_HEIGHT_ADD,
|
||||
"sub_text":"Lower", "sub_op":Terrain3DEditor.SUBTRACT, "sub_icon":ICON_HEIGHT_SUB })
|
||||
|
||||
add_tool_button({ "tool":Terrain3DEditor.SCULPT,
|
||||
"add_text":"Smooth", "add_op":Terrain3DEditor.AVERAGE, "add_icon":ICON_HEIGHT_SMOOTH })
|
||||
|
||||
add_tool_button({ "tool":Terrain3DEditor.HEIGHT,
|
||||
"add_text":"Height", "add_op":Terrain3DEditor.ADD, "add_icon":ICON_HEIGHT_FLAT,
|
||||
"sub_text":"Zero", "sub_op":Terrain3DEditor.SUBTRACT, "sub_icon":ICON_HEIGHT_FLAT })
|
||||
|
||||
add_tool_button({ "tool":Terrain3DEditor.SCULPT,
|
||||
"add_text":"Slope", "add_op":Terrain3DEditor.GRADIENT, "add_icon":ICON_HEIGHT_SLOPE })
|
||||
|
||||
add_tool_button(Terrain3DEditor.HEIGHT, Terrain3DEditor.ADD, "Raise", load(ICON_HEIGHT_ADD), tool_group)
|
||||
add_tool_button(Terrain3DEditor.HEIGHT, Terrain3DEditor.SUBTRACT, "Lower", load(ICON_HEIGHT_SUB), tool_group)
|
||||
add_tool_button(Terrain3DEditor.HEIGHT, Terrain3DEditor.MULTIPLY, "Expand (Away from 0)", load(ICON_HEIGHT_MUL), tool_group)
|
||||
add_tool_button(Terrain3DEditor.HEIGHT, Terrain3DEditor.DIVIDE, "Reduce (Towards 0)", load(ICON_HEIGHT_DIV), tool_group)
|
||||
add_tool_button(Terrain3DEditor.HEIGHT, Terrain3DEditor.REPLACE, "Flatten", load(ICON_HEIGHT_FLAT), tool_group)
|
||||
add_tool_button(Terrain3DEditor.HEIGHT, Terrain3DEditor.GRADIENT, "Slope", load(ICON_HEIGHT_SLOPE), tool_group)
|
||||
add_tool_button(Terrain3DEditor.HEIGHT, Terrain3DEditor.AVERAGE, "Smooth", load(ICON_HEIGHT_SMOOTH), tool_group)
|
||||
add_child(HSeparator.new())
|
||||
|
||||
add_tool_button({ "tool":Terrain3DEditor.TEXTURE,
|
||||
"add_text":"Paint Base Texture", "add_op":Terrain3DEditor.REPLACE, "add_icon":ICON_PAINT_TEXTURE })
|
||||
|
||||
add_tool_button({ "tool":Terrain3DEditor.TEXTURE,
|
||||
"add_text":"Spray Overlay Texture", "add_op":Terrain3DEditor.ADD, "add_icon":ICON_SPRAY_TEXTURE })
|
||||
|
||||
add_tool_button({ "tool":Terrain3DEditor.AUTOSHADER,
|
||||
"add_text":"Enable Autoshader", "add_op":Terrain3DEditor.ADD, "add_icon":ICON_AUTOSHADER,
|
||||
"sub_text":"Disable Autoshader", "sub_op":Terrain3DEditor.SUBTRACT })
|
||||
|
||||
add_tool_button(Terrain3DEditor.TEXTURE, Terrain3DEditor.REPLACE, "Paint Base Texture", load(ICON_PAINT_TEXTURE), tool_group)
|
||||
add_tool_button(Terrain3DEditor.TEXTURE, Terrain3DEditor.ADD, "Spray Overlay Texture", load(ICON_SPRAY_TEXTURE), tool_group)
|
||||
add_tool_button(Terrain3DEditor.AUTOSHADER, Terrain3DEditor.REPLACE, "Autoshader", load(ICON_AUTOSHADER), tool_group)
|
||||
add_child(HSeparator.new())
|
||||
|
||||
add_tool_button({ "tool":Terrain3DEditor.COLOR,
|
||||
"add_text":"Paint Color", "add_op":Terrain3DEditor.ADD, "add_icon":ICON_COLOR,
|
||||
"sub_text":"Remove Color", "sub_op":Terrain3DEditor.SUBTRACT })
|
||||
|
||||
add_tool_button({ "tool":Terrain3DEditor.ROUGHNESS,
|
||||
"add_text":"Paint Wetness", "add_op":Terrain3DEditor.ADD, "add_icon":ICON_WETNESS,
|
||||
"sub_text":"Remove Wetness", "sub_op":Terrain3DEditor.SUBTRACT })
|
||||
|
||||
add_tool_button(Terrain3DEditor.COLOR, Terrain3DEditor.REPLACE, "Paint Color", load(ICON_COLOR), tool_group)
|
||||
add_tool_button(Terrain3DEditor.ROUGHNESS, Terrain3DEditor.REPLACE, "Paint Wetness", load(ICON_WETNESS), tool_group)
|
||||
add_child(HSeparator.new())
|
||||
add_tool_button(Terrain3DEditor.HOLES, Terrain3DEditor.REPLACE, "Create Holes", load(ICON_HOLES), tool_group)
|
||||
add_tool_button(Terrain3DEditor.NAVIGATION, Terrain3DEditor.REPLACE, "Paint Navigable Area", load(ICON_NAVIGATION), tool_group)
|
||||
add_tool_button(Terrain3DEditor.INSTANCER, Terrain3DEditor.ADD, "Instance Meshes", load(ICON_INSTANCER), tool_group)
|
||||
|
||||
add_tool_button({ "tool":Terrain3DEditor.HOLES,
|
||||
"add_text":"Add Holes", "add_op":Terrain3DEditor.ADD, "add_icon":ICON_HOLES,
|
||||
"sub_text":"Remove Holes", "sub_op":Terrain3DEditor.SUBTRACT })
|
||||
|
||||
add_tool_button({ "tool":Terrain3DEditor.NAVIGATION,
|
||||
"add_text":"Paint Navigable Area", "add_op":Terrain3DEditor.ADD, "add_icon":ICON_NAVIGATION,
|
||||
"sub_text":"Remove Navigable Area", "sub_op":Terrain3DEditor.SUBTRACT })
|
||||
|
||||
add_tool_button({ "tool":Terrain3DEditor.INSTANCER,
|
||||
"add_text":"Instance Meshes", "add_op":Terrain3DEditor.ADD, "add_icon":ICON_INSTANCER,
|
||||
"sub_text":"Remove Meshes", "sub_op":Terrain3DEditor.SUBTRACT })
|
||||
|
||||
# Select first button
|
||||
var buttons: Array[BaseButton] = add_tool_group.get_buttons()
|
||||
var buttons: Array[BaseButton] = tool_group.get_buttons()
|
||||
buttons[0].set_pressed(true)
|
||||
show_add_buttons(true)
|
||||
|
||||
|
||||
func add_tool_button(p_params: Dictionary) -> void:
|
||||
# Additive button
|
||||
var button := Button.new()
|
||||
button.set_name(p_params.get("add_text", "blank").to_pascal_case())
|
||||
button.set_meta("Tool", p_params.get("tool", 0))
|
||||
button.set_meta("Operation", p_params.get("add_op", 0))
|
||||
button.set_meta("ID", add_tool_group.get_buttons().size() + 1)
|
||||
button.set_tooltip_text(p_params.get("add_text", "blank"))
|
||||
button.set_button_icon(load(p_params.get("add_icon")))
|
||||
func add_tool_button(p_tool: Terrain3DEditor.Tool, p_operation: Terrain3DEditor.Operation,
|
||||
p_tip: String, p_icon: Texture2D, p_group: ButtonGroup) -> void:
|
||||
|
||||
var button: Button = Button.new()
|
||||
button.set_name(p_tip.to_pascal_case())
|
||||
button.set_meta("Tool", p_tool)
|
||||
button.set_meta("Operation", p_operation)
|
||||
button.set_tooltip_text(p_tip)
|
||||
button.set_button_icon(p_icon)
|
||||
button.set_button_group(p_group)
|
||||
button.set_flat(true)
|
||||
button.set_toggle_mode(true)
|
||||
button.set_h_size_flags(SIZE_SHRINK_END)
|
||||
button.set_button_group(p_params.get("group", add_tool_group))
|
||||
add_child(button, true)
|
||||
|
||||
# Subtractive button
|
||||
var button2: Button
|
||||
if p_params.has("sub_text"):
|
||||
button2 = Button.new()
|
||||
button2.set_name(p_params.get("sub_text", "blank").to_pascal_case())
|
||||
button2.set_meta("Tool", p_params.get("tool", 0))
|
||||
button2.set_meta("Operation", p_params.get("sub_op", 0))
|
||||
button2.set_meta("ID", button.get_meta("ID"))
|
||||
button2.set_tooltip_text(p_params.get("sub_text", "blank"))
|
||||
button2.set_button_icon(load(p_params.get("sub_icon", p_params.get("add_icon"))))
|
||||
button2.set_flat(true)
|
||||
button2.set_toggle_mode(true)
|
||||
button2.set_h_size_flags(SIZE_SHRINK_END)
|
||||
else:
|
||||
button2 = button.duplicate()
|
||||
button2.set_button_group(p_params.get("group", sub_tool_group))
|
||||
add_child(button2, true)
|
||||
|
||||
|
||||
func show_add_buttons(p_enable: bool) -> void:
|
||||
for button in add_tool_group.get_buttons():
|
||||
button.visible = p_enable
|
||||
for button in sub_tool_group.get_buttons():
|
||||
button.visible = !p_enable
|
||||
add_child(button)
|
||||
|
||||
|
||||
func _on_tool_selected(p_button: BaseButton) -> void:
|
||||
# Select same tool on negative bar
|
||||
var group: ButtonGroup = p_button.get_button_group()
|
||||
var change_group: ButtonGroup = add_tool_group if group == sub_tool_group else sub_tool_group
|
||||
var id: int = p_button.get_meta("ID", -2)
|
||||
for button in change_group.get_buttons():
|
||||
button.set_pressed_no_signal(button.get_meta("ID", -1) == id)
|
||||
|
||||
emit_signal("tool_changed", p_button.get_meta("Tool", Terrain3DEditor.TOOL_MAX), p_button.get_meta("Operation", Terrain3DEditor.OP_MAX))
|
||||
emit_signal("tool_changed", p_button.get_meta("Tool", -1), p_button.get_meta("Operation", -1))
|
||||
|
|
|
@ -5,38 +5,34 @@ extends Node
|
|||
# Includes
|
||||
const Toolbar: Script = preload("res://addons/terrain_3d/src/toolbar.gd")
|
||||
const ToolSettings: Script = preload("res://addons/terrain_3d/src/tool_settings.gd")
|
||||
const TerrainMenu: Script = preload("res://addons/terrain_3d/src/terrain_menu.gd")
|
||||
const TerrainTools: Script = preload("res://addons/terrain_3d/src/terrain_tools.gd")
|
||||
const OperationBuilder: Script = preload("res://addons/terrain_3d/src/operation_builder.gd")
|
||||
const GradientOperationBuilder: Script = preload("res://addons/terrain_3d/src/gradient_operation_builder.gd")
|
||||
const COLOR_RAISE := Color.WHITE
|
||||
const COLOR_LOWER := Color(.02, .02, .02)
|
||||
const COLOR_LOWER := Color.BLACK
|
||||
const COLOR_SMOOTH := Color(0.5, 0, .1)
|
||||
const COLOR_LIFT := Color.ORANGE
|
||||
const COLOR_FLATTEN := Color.BLUE_VIOLET
|
||||
const COLOR_HEIGHT := Color(0., 0.32, .4)
|
||||
const COLOR_EXPAND := Color.ORANGE
|
||||
const COLOR_REDUCE := Color.BLUE_VIOLET
|
||||
const COLOR_FLATTEN := Color(0., 0.32, .4)
|
||||
const COLOR_SLOPE := Color.YELLOW
|
||||
const COLOR_PAINT := Color.DARK_GREEN
|
||||
const COLOR_SPRAY := Color.PALE_GREEN
|
||||
const COLOR_PAINT := Color.FOREST_GREEN
|
||||
const COLOR_SPRAY := Color.SEA_GREEN
|
||||
const COLOR_ROUGHNESS := Color.ROYAL_BLUE
|
||||
const COLOR_AUTOSHADER := Color.DODGER_BLUE
|
||||
const COLOR_HOLES := Color.BLACK
|
||||
const COLOR_NAVIGATION := Color(.15, .0, .255)
|
||||
const COLOR_NAVIGATION := Color.REBECCA_PURPLE
|
||||
const COLOR_INSTANCER := Color.CRIMSON
|
||||
const COLOR_PICK_COLOR := Color.WHITE
|
||||
const COLOR_PICK_HEIGHT := Color.DARK_RED
|
||||
const COLOR_PICK_ROUGH := Color.ROYAL_BLUE
|
||||
|
||||
const OP_NONE: int = 0x0
|
||||
const OP_POSITIVE_ONLY: int = 0x01
|
||||
const OP_NEGATIVE_ONLY: int = 0x02
|
||||
|
||||
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 toolbar: Toolbar
|
||||
var tool_settings: ToolSettings
|
||||
var terrain_menu: TerrainMenu
|
||||
var toolbar_settings: ToolSettings
|
||||
var terrain_tools: TerrainTools
|
||||
var setting_has_changed: bool = false
|
||||
var visible: bool = false
|
||||
var picking: int = Terrain3DEditor.TOOL_MAX
|
||||
|
@ -46,16 +42,6 @@ var decal_timer: Timer
|
|||
var gradient_decals: Array[Decal]
|
||||
var brush_data: Dictionary
|
||||
var operation_builder: OperationBuilder
|
||||
var last_tool: Terrain3DEditor.Tool
|
||||
var last_operation: Terrain3DEditor.Operation
|
||||
var last_rmb_time: int = 0 # Set in editor.gd
|
||||
|
||||
# Compatibility decals, indices; 0 = main brush, 1 = slope point A, 2 = slope point B
|
||||
var editor_decal_position: Array[Vector2]
|
||||
var editor_decal_rotation: Array[float]
|
||||
var editor_decal_size: Array[float]
|
||||
var editor_decal_color: Array[Color]
|
||||
var editor_decal_visible: Array[bool]
|
||||
|
||||
|
||||
func _enter_tree() -> void:
|
||||
|
@ -63,19 +49,18 @@ func _enter_tree() -> void:
|
|||
toolbar.hide()
|
||||
toolbar.connect("tool_changed", _on_tool_changed)
|
||||
|
||||
tool_settings = ToolSettings.new()
|
||||
tool_settings.connect("setting_changed", _on_setting_changed)
|
||||
tool_settings.connect("picking", _on_picking)
|
||||
tool_settings.plugin = plugin
|
||||
tool_settings.hide()
|
||||
toolbar_settings = ToolSettings.new()
|
||||
toolbar_settings.connect("setting_changed", _on_setting_changed)
|
||||
toolbar_settings.connect("picking", _on_picking)
|
||||
toolbar_settings.hide()
|
||||
|
||||
terrain_menu = TerrainMenu.new()
|
||||
terrain_menu.plugin = plugin
|
||||
terrain_menu.hide()
|
||||
terrain_tools = TerrainTools.new()
|
||||
terrain_tools.plugin = plugin
|
||||
terrain_tools.hide()
|
||||
|
||||
plugin.add_control_to_container(EditorPlugin.CONTAINER_SPATIAL_EDITOR_SIDE_LEFT, toolbar)
|
||||
plugin.add_control_to_container(EditorPlugin.CONTAINER_SPATIAL_EDITOR_BOTTOM, tool_settings)
|
||||
plugin.add_control_to_container(EditorPlugin.CONTAINER_SPATIAL_EDITOR_MENU, terrain_menu)
|
||||
plugin.add_control_to_container(EditorPlugin.CONTAINER_SPATIAL_EDITOR_BOTTOM, toolbar_settings)
|
||||
plugin.add_control_to_container(EditorPlugin.CONTAINER_SPATIAL_EDITOR_MENU, terrain_tools)
|
||||
|
||||
_on_tool_changed(Terrain3DEditor.REGION, Terrain3DEditor.ADD)
|
||||
|
||||
|
@ -92,10 +77,10 @@ func _enter_tree() -> void:
|
|||
|
||||
func _exit_tree() -> void:
|
||||
plugin.remove_control_from_container(EditorPlugin.CONTAINER_SPATIAL_EDITOR_SIDE_LEFT, toolbar)
|
||||
plugin.remove_control_from_container(EditorPlugin.CONTAINER_SPATIAL_EDITOR_BOTTOM, tool_settings)
|
||||
plugin.remove_control_from_container(EditorPlugin.CONTAINER_SPATIAL_EDITOR_BOTTOM, toolbar_settings)
|
||||
toolbar.queue_free()
|
||||
tool_settings.queue_free()
|
||||
terrain_menu.queue_free()
|
||||
toolbar_settings.queue_free()
|
||||
terrain_tools.queue_free()
|
||||
decal.queue_free()
|
||||
decal_timer.queue_free()
|
||||
for gradient_decal in gradient_decals:
|
||||
|
@ -103,25 +88,12 @@ func _exit_tree() -> void:
|
|||
gradient_decals.clear()
|
||||
|
||||
|
||||
func set_visible(p_visible: bool, p_menu_only: bool = false) -> void:
|
||||
terrain_menu.set_visible(p_visible)
|
||||
|
||||
if p_menu_only:
|
||||
toolbar.set_visible(false)
|
||||
tool_settings.set_visible(false)
|
||||
else:
|
||||
visible = p_visible
|
||||
toolbar.set_visible(p_visible)
|
||||
tool_settings.set_visible(p_visible)
|
||||
update_decal()
|
||||
|
||||
if(plugin.editor):
|
||||
if(p_visible):
|
||||
await get_tree().create_timer(.01).timeout # Won't work, otherwise.
|
||||
_on_tool_changed(last_tool, last_operation)
|
||||
else:
|
||||
plugin.editor.set_tool(Terrain3DEditor.TOOL_MAX)
|
||||
plugin.editor.set_operation(Terrain3DEditor.OP_MAX)
|
||||
func set_visible(p_visible: bool) -> void:
|
||||
visible = p_visible
|
||||
terrain_tools.set_visible(p_visible)
|
||||
toolbar.set_visible(p_visible)
|
||||
toolbar_settings.set_visible(p_visible)
|
||||
update_decal()
|
||||
|
||||
|
||||
func set_menu_visibility(p_list: Control, p_visible: bool) -> void:
|
||||
|
@ -131,37 +103,26 @@ func set_menu_visibility(p_list: Control, p_visible: bool) -> void:
|
|||
|
||||
func _on_tool_changed(p_tool: Terrain3DEditor.Tool, p_operation: Terrain3DEditor.Operation) -> void:
|
||||
clear_picking()
|
||||
set_menu_visibility(tool_settings.advanced_list, true)
|
||||
set_menu_visibility(tool_settings.scale_list, false)
|
||||
set_menu_visibility(tool_settings.rotation_list, false)
|
||||
set_menu_visibility(tool_settings.height_list, false)
|
||||
set_menu_visibility(tool_settings.color_list, false)
|
||||
set_menu_visibility(toolbar_settings.advanced_list, true)
|
||||
set_menu_visibility(toolbar_settings.scale_list, false)
|
||||
set_menu_visibility(toolbar_settings.rotation_list, false)
|
||||
set_menu_visibility(toolbar_settings.height_list, false)
|
||||
set_menu_visibility(toolbar_settings.color_list, false)
|
||||
|
||||
# Select which settings to show. Options in tool_settings.gd:_ready
|
||||
var to_show: PackedStringArray = []
|
||||
|
||||
match p_tool:
|
||||
Terrain3DEditor.REGION:
|
||||
to_show.push_back("instructions")
|
||||
to_show.push_back("remove")
|
||||
set_menu_visibility(tool_settings.advanced_list, false)
|
||||
|
||||
Terrain3DEditor.SCULPT:
|
||||
to_show.push_back("brush")
|
||||
to_show.push_back("size")
|
||||
to_show.push_back("strength")
|
||||
if p_operation in [Terrain3DEditor.ADD, Terrain3DEditor.SUBTRACT]:
|
||||
to_show.push_back("remove")
|
||||
elif p_operation == Terrain3DEditor.GRADIENT:
|
||||
to_show.push_back("gradient_points")
|
||||
to_show.push_back("drawable")
|
||||
|
||||
Terrain3DEditor.HEIGHT:
|
||||
to_show.push_back("brush")
|
||||
to_show.push_back("size")
|
||||
to_show.push_back("strength")
|
||||
to_show.push_back("height")
|
||||
to_show.push_back("height_picker")
|
||||
if p_operation == Terrain3DEditor.REPLACE:
|
||||
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")
|
||||
|
@ -169,7 +130,6 @@ func _on_tool_changed(p_tool: Terrain3DEditor.Tool, p_operation: Terrain3DEditor
|
|||
to_show.push_back("enable_texture")
|
||||
if p_operation == Terrain3DEditor.ADD:
|
||||
to_show.push_back("strength")
|
||||
to_show.push_back("slope")
|
||||
to_show.push_back("enable_angle")
|
||||
to_show.push_back("angle")
|
||||
to_show.push_back("angle_picker")
|
||||
|
@ -184,10 +144,6 @@ func _on_tool_changed(p_tool: Terrain3DEditor.Tool, p_operation: Terrain3DEditor
|
|||
to_show.push_back("strength")
|
||||
to_show.push_back("color")
|
||||
to_show.push_back("color_picker")
|
||||
to_show.push_back("slope")
|
||||
to_show.push_back("enable_texture")
|
||||
to_show.push_back("margin")
|
||||
to_show.push_back("remove")
|
||||
|
||||
Terrain3DEditor.ROUGHNESS:
|
||||
to_show.push_back("brush")
|
||||
|
@ -195,59 +151,52 @@ func _on_tool_changed(p_tool: Terrain3DEditor.Tool, p_operation: Terrain3DEditor
|
|||
to_show.push_back("strength")
|
||||
to_show.push_back("roughness")
|
||||
to_show.push_back("roughness_picker")
|
||||
to_show.push_back("slope")
|
||||
to_show.push_back("enable_texture")
|
||||
to_show.push_back("margin")
|
||||
to_show.push_back("remove")
|
||||
|
||||
Terrain3DEditor.AUTOSHADER, Terrain3DEditor.HOLES, Terrain3DEditor.NAVIGATION:
|
||||
to_show.push_back("brush")
|
||||
to_show.push_back("size")
|
||||
to_show.push_back("remove")
|
||||
to_show.push_back("enable")
|
||||
|
||||
Terrain3DEditor.INSTANCER:
|
||||
to_show.push_back("size")
|
||||
to_show.push_back("strength")
|
||||
to_show.push_back("slope")
|
||||
set_menu_visibility(tool_settings.height_list, true)
|
||||
to_show.push_back("enable")
|
||||
set_menu_visibility(toolbar_settings.height_list, true)
|
||||
to_show.push_back("height_offset")
|
||||
to_show.push_back("random_height")
|
||||
set_menu_visibility(tool_settings.scale_list, true)
|
||||
set_menu_visibility(toolbar_settings.scale_list, true)
|
||||
to_show.push_back("fixed_scale")
|
||||
to_show.push_back("random_scale")
|
||||
set_menu_visibility(tool_settings.rotation_list, true)
|
||||
set_menu_visibility(toolbar_settings.rotation_list, true)
|
||||
to_show.push_back("fixed_spin")
|
||||
to_show.push_back("random_spin")
|
||||
to_show.push_back("fixed_tilt")
|
||||
to_show.push_back("random_tilt")
|
||||
to_show.push_back("fixed_angle")
|
||||
to_show.push_back("random_angle")
|
||||
to_show.push_back("align_to_normal")
|
||||
set_menu_visibility(tool_settings.color_list, true)
|
||||
set_menu_visibility(toolbar_settings.color_list, true)
|
||||
to_show.push_back("vertex_color")
|
||||
to_show.push_back("random_darken")
|
||||
to_show.push_back("random_hue")
|
||||
to_show.push_back("remove")
|
||||
|
||||
_:
|
||||
pass
|
||||
|
||||
# Advanced menu settings
|
||||
to_show.push_back("auto_regions")
|
||||
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")
|
||||
tool_settings.show_settings(to_show)
|
||||
toolbar_settings.show_settings(to_show)
|
||||
|
||||
operation_builder = null
|
||||
if p_operation == Terrain3DEditor.GRADIENT:
|
||||
operation_builder = GradientOperationBuilder.new()
|
||||
operation_builder.tool_settings = tool_settings
|
||||
operation_builder.tool_settings = toolbar_settings
|
||||
|
||||
if plugin.editor:
|
||||
plugin.editor.set_tool(p_tool)
|
||||
plugin.editor.set_operation(_modify_operation(p_operation))
|
||||
last_tool = p_tool
|
||||
last_operation = p_operation
|
||||
plugin.editor.set_operation(p_operation)
|
||||
|
||||
_on_setting_changed()
|
||||
plugin.update_region_grid()
|
||||
|
@ -256,67 +205,31 @@ func _on_tool_changed(p_tool: Terrain3DEditor.Tool, p_operation: Terrain3DEditor
|
|||
func _on_setting_changed() -> void:
|
||||
if not plugin.asset_dock:
|
||||
return
|
||||
brush_data = tool_settings.get_settings()
|
||||
brush_data = toolbar_settings.get_settings()
|
||||
brush_data["asset_id"] = plugin.asset_dock.get_current_list().get_selected_id()
|
||||
update_decal()
|
||||
plugin.editor.set_brush_data(brush_data)
|
||||
plugin.editor.set_operation(_modify_operation(plugin.editor.get_operation()))
|
||||
|
||||
|
||||
func update_modifiers() -> void:
|
||||
toolbar.show_add_buttons(not plugin.modifier_ctrl)
|
||||
|
||||
if plugin.modifier_shift and not plugin.modifier_ctrl:
|
||||
plugin.editor.set_tool(Terrain3DEditor.SCULPT)
|
||||
plugin.editor.set_operation(Terrain3DEditor.AVERAGE)
|
||||
else:
|
||||
plugin.editor.set_tool(last_tool)
|
||||
if plugin.modifier_ctrl:
|
||||
plugin.editor.set_operation(_modify_operation(last_operation))
|
||||
else:
|
||||
plugin.editor.set_operation(last_operation)
|
||||
|
||||
|
||||
func _modify_operation(p_operation: Terrain3DEditor.Operation) -> Terrain3DEditor.Operation:
|
||||
var remove_checked: bool = false
|
||||
if DisplayServer.is_touchscreen_available():
|
||||
var removable_tools := [Terrain3DEditor.REGION, Terrain3DEditor.SCULPT, Terrain3DEditor.HEIGHT, Terrain3DEditor.AUTOSHADER,
|
||||
Terrain3DEditor.HOLES, Terrain3DEditor.INSTANCER, Terrain3DEditor.NAVIGATION,
|
||||
Terrain3DEditor.COLOR, Terrain3DEditor.ROUGHNESS]
|
||||
remove_checked = brush_data.get("remove", false) && plugin.editor.get_tool() in removable_tools
|
||||
|
||||
if plugin.modifier_ctrl or remove_checked:
|
||||
return _invert_operation(p_operation, OP_NEGATIVE_ONLY)
|
||||
return _invert_operation(p_operation, OP_POSITIVE_ONLY)
|
||||
|
||||
|
||||
func _invert_operation(p_operation: Terrain3DEditor.Operation, flags: int = OP_NONE) -> Terrain3DEditor.Operation:
|
||||
if p_operation == Terrain3DEditor.ADD and ! (flags & OP_POSITIVE_ONLY):
|
||||
return Terrain3DEditor.SUBTRACT
|
||||
elif p_operation == Terrain3DEditor.SUBTRACT and ! (flags & OP_NEGATIVE_ONLY):
|
||||
return Terrain3DEditor.ADD
|
||||
return p_operation
|
||||
|
||||
|
||||
func update_decal() -> void:
|
||||
# If not a state that should show the decal, hide everything and return
|
||||
var mouse_buttons: int = Input.get_mouse_button_mask()
|
||||
if not visible or \
|
||||
not plugin.terrain or \
|
||||
plugin._input_mode < 0 or \
|
||||
# Wait for cursor to recenter after moving camera before revealing
|
||||
not plugin.terrain or \
|
||||
brush_data.is_empty() or \
|
||||
mouse_buttons & MOUSE_BUTTON_RIGHT or \
|
||||
(mouse_buttons & MOUSE_BUTTON_LEFT and not brush_data["show_cursor_while_painting"]) or \
|
||||
plugin.editor.get_tool() == Terrain3DEditor.REGION:
|
||||
decal.visible = false
|
||||
for gradient_decal in gradient_decals:
|
||||
gradient_decal.visible = false
|
||||
return
|
||||
else:
|
||||
# Wait for cursor to recenter after right-click before revealing
|
||||
# See https://github.com/godotengine/godot/issues/70098
|
||||
Time.get_ticks_msec() - last_rmb_time <= 30 or \
|
||||
brush_data.is_empty() or \
|
||||
plugin.editor.get_tool() == Terrain3DEditor.REGION or \
|
||||
(plugin._input_mode > 0 and not brush_data["show_cursor_while_painting"]):
|
||||
decal.visible = false
|
||||
for gradient_decal in gradient_decals:
|
||||
gradient_decal.visible = false
|
||||
return
|
||||
await get_tree().create_timer(.05).timeout
|
||||
decal.visible = true
|
||||
|
||||
decal.position = plugin.mouse_global_position
|
||||
decal.visible = true
|
||||
decal.size = Vector3.ONE * maxf(brush_data["size"], .5)
|
||||
decal.size = Vector3.ONE * brush_data["size"]
|
||||
if brush_data["align_to_view"]:
|
||||
var cam: Camera3D = plugin.terrain.get_camera();
|
||||
if (cam):
|
||||
|
@ -327,7 +240,7 @@ func update_decal() -> void:
|
|||
# Set texture and color
|
||||
if picking != Terrain3DEditor.TOOL_MAX:
|
||||
decal.texture_albedo = ring_texture
|
||||
decal.size = Vector3.ONE * 10. * plugin.terrain.get_vertex_spacing()
|
||||
decal.size = Vector3.ONE * 10. * plugin.terrain.get_mesh_vertex_spacing()
|
||||
match picking:
|
||||
Terrain3DEditor.HEIGHT:
|
||||
decal.modulate = COLOR_PICK_HEIGHT
|
||||
|
@ -339,61 +252,57 @@ func update_decal() -> void:
|
|||
else:
|
||||
decal.texture_albedo = brush_data["brush"][1]
|
||||
match plugin.editor.get_tool():
|
||||
Terrain3DEditor.SCULPT:
|
||||
Terrain3DEditor.HEIGHT:
|
||||
match plugin.editor.get_operation():
|
||||
Terrain3DEditor.ADD:
|
||||
if plugin.modifier_alt:
|
||||
decal.modulate = COLOR_LIFT
|
||||
decal.modulate.a = clamp(brush_data["strength"], .2, .5)
|
||||
else:
|
||||
decal.modulate = COLOR_RAISE
|
||||
decal.modulate.a = clamp(brush_data["strength"], .2, .5)
|
||||
decal.modulate = COLOR_RAISE
|
||||
Terrain3DEditor.SUBTRACT:
|
||||
if plugin.modifier_alt:
|
||||
decal.modulate = COLOR_FLATTEN
|
||||
decal.modulate.a = clamp(brush_data["strength"], .2, .5)
|
||||
else:
|
||||
decal.modulate = COLOR_LOWER
|
||||
decal.modulate.a = clamp(brush_data["strength"], .2, .5) + .5
|
||||
decal.modulate = COLOR_LOWER
|
||||
Terrain3DEditor.MULTIPLY:
|
||||
decal.modulate = COLOR_EXPAND
|
||||
Terrain3DEditor.DIVIDE:
|
||||
decal.modulate = COLOR_REDUCE
|
||||
Terrain3DEditor.REPLACE:
|
||||
decal.modulate = COLOR_FLATTEN
|
||||
Terrain3DEditor.AVERAGE:
|
||||
decal.modulate = COLOR_SMOOTH
|
||||
decal.modulate.a = clamp(brush_data["strength"], .2, .5) + .2
|
||||
Terrain3DEditor.GRADIENT:
|
||||
decal.modulate = COLOR_SLOPE
|
||||
decal.modulate.a = clamp(brush_data["strength"], .2, .5)
|
||||
Terrain3DEditor.HEIGHT:
|
||||
decal.modulate = COLOR_HEIGHT
|
||||
decal.modulate.a = clamp(brush_data["strength"], .2, .5)
|
||||
_:
|
||||
decal.modulate = Color.WHITE
|
||||
decal.modulate.a = max(.3, brush_data["strength"] * .01)
|
||||
Terrain3DEditor.TEXTURE:
|
||||
match plugin.editor.get_operation():
|
||||
Terrain3DEditor.REPLACE:
|
||||
decal.modulate = COLOR_PAINT
|
||||
decal.modulate.a = .7
|
||||
Terrain3DEditor.SUBTRACT:
|
||||
decal.modulate = COLOR_PAINT
|
||||
decal.modulate.a = clamp(brush_data["strength"], .2, .5)
|
||||
decal.modulate.a = 1.0
|
||||
Terrain3DEditor.ADD:
|
||||
decal.modulate = COLOR_SPRAY
|
||||
decal.modulate.a = clamp(brush_data["strength"], .2, .5)
|
||||
decal.modulate.a = max(.3, brush_data["strength"] * .01)
|
||||
_:
|
||||
decal.modulate = Color.WHITE
|
||||
Terrain3DEditor.COLOR:
|
||||
decal.modulate = brush_data["color"].srgb_to_linear()
|
||||
decal.modulate.a *= clamp(brush_data["strength"], .2, .5)
|
||||
decal.modulate = brush_data["color"].srgb_to_linear()*.5
|
||||
decal.modulate.a = max(.3, brush_data["strength"] * .01)
|
||||
Terrain3DEditor.ROUGHNESS:
|
||||
decal.modulate = COLOR_ROUGHNESS
|
||||
decal.modulate.a = clamp(brush_data["strength"], .2, .5)
|
||||
decal.modulate.a = max(.3, brush_data["strength"] * .01)
|
||||
Terrain3DEditor.AUTOSHADER:
|
||||
decal.modulate = COLOR_AUTOSHADER
|
||||
decal.modulate.a = .7
|
||||
decal.modulate.a = 1.0
|
||||
Terrain3DEditor.HOLES:
|
||||
decal.modulate = COLOR_HOLES
|
||||
decal.modulate.a = .85
|
||||
decal.modulate.a = 1.0
|
||||
Terrain3DEditor.NAVIGATION:
|
||||
decal.modulate = COLOR_NAVIGATION
|
||||
decal.modulate.a = .85
|
||||
decal.modulate.a = 1.0
|
||||
Terrain3DEditor.INSTANCER:
|
||||
decal.texture_albedo = ring_texture
|
||||
decal.modulate = COLOR_INSTANCER
|
||||
decal.modulate.a = 1.0
|
||||
_:
|
||||
decal.modulate = Color.WHITE
|
||||
decal.modulate.a = max(.3, brush_data["strength"] * .01)
|
||||
decal.size.y = max(1000, decal.size.y)
|
||||
decal.albedo_mix = 1.0
|
||||
decal.cull_mask = 1 << ( plugin.terrain.get_mouse_layer() - 1 )
|
||||
|
@ -411,8 +320,6 @@ func update_decal() -> void:
|
|||
point_decal.position = point
|
||||
index += 1
|
||||
|
||||
update_compatibility_decal()
|
||||
|
||||
|
||||
func _get_gradient_decal(index: int) -> Decal:
|
||||
if gradient_decals.size() > index:
|
||||
|
@ -422,7 +329,7 @@ func _get_gradient_decal(index: int) -> Decal:
|
|||
gradient_decal = Decal.new()
|
||||
gradient_decal.texture_albedo = ring_texture
|
||||
gradient_decal.modulate = COLOR_SLOPE
|
||||
gradient_decal.size = Vector3.ONE * 10. * plugin.terrain.get_vertex_spacing()
|
||||
gradient_decal.size = Vector3.ONE * 10. * plugin.terrain.get_mesh_vertex_spacing()
|
||||
gradient_decal.size.y = 1000.
|
||||
gradient_decal.cull_mask = decal.cull_mask
|
||||
add_child(gradient_decal)
|
||||
|
@ -431,61 +338,6 @@ func _get_gradient_decal(index: int) -> Decal:
|
|||
return gradient_decal
|
||||
|
||||
|
||||
func update_compatibility_decal() -> void:
|
||||
if not plugin.terrain.is_compatibility_mode():
|
||||
return
|
||||
|
||||
# Verify setup
|
||||
if editor_decal_position.size() != 3:
|
||||
editor_decal_position.resize(3)
|
||||
editor_decal_rotation.resize(3)
|
||||
editor_decal_size.resize(3)
|
||||
editor_decal_color.resize(3)
|
||||
editor_decal_visible.resize(3)
|
||||
decal_timer.timeout.connect(func():
|
||||
var mat_rid: RID = plugin.terrain.material.get_material_rid()
|
||||
editor_decal_visible[0] = false
|
||||
RenderingServer.material_set_param(mat_rid, "_editor_decal_visible", editor_decal_visible)
|
||||
)
|
||||
|
||||
# Update compatibility decal
|
||||
var mat_rid: RID = plugin.terrain.material.get_material_rid()
|
||||
if decal.visible:
|
||||
editor_decal_position[0] = Vector2(decal.global_position.x, decal.global_position.z)
|
||||
editor_decal_rotation[0] = decal.rotation.y
|
||||
editor_decal_size[0] = brush_data.get("size")
|
||||
editor_decal_color[0] = decal.modulate
|
||||
editor_decal_visible[0] = decal.visible
|
||||
RenderingServer.material_set_param(
|
||||
mat_rid, "_editor_decal_0", decal.texture_albedo.get_rid()
|
||||
)
|
||||
if gradient_decals.size() >= 1:
|
||||
editor_decal_position[1] = Vector2(gradient_decals[0].global_position.x,
|
||||
gradient_decals[0].global_position.z)
|
||||
editor_decal_rotation[1] = gradient_decals[0].rotation.y
|
||||
editor_decal_size[1] = 10.0
|
||||
editor_decal_color[1] = gradient_decals[0].modulate
|
||||
editor_decal_visible[1] = gradient_decals[0].visible
|
||||
RenderingServer.material_set_param(
|
||||
mat_rid, "_editor_decal_1", gradient_decals[0].texture_albedo.get_rid()
|
||||
)
|
||||
if gradient_decals.size() >= 2:
|
||||
editor_decal_position[2] = Vector2(gradient_decals[1].global_position.x,
|
||||
gradient_decals[1].global_position.z)
|
||||
editor_decal_rotation[2] = gradient_decals[1].rotation.y
|
||||
editor_decal_size[2] = 10.0
|
||||
editor_decal_color[2] = gradient_decals[1].modulate
|
||||
editor_decal_visible[2] = gradient_decals[1].visible
|
||||
RenderingServer.material_set_param(
|
||||
mat_rid, "_editor_decal_2", gradient_decals[1].texture_albedo.get_rid()
|
||||
)
|
||||
RenderingServer.material_set_param(mat_rid, "_editor_decal_position", editor_decal_position)
|
||||
RenderingServer.material_set_param(mat_rid, "_editor_decal_rotation", editor_decal_rotation)
|
||||
RenderingServer.material_set_param(mat_rid, "_editor_decal_size", editor_decal_size)
|
||||
RenderingServer.material_set_param(mat_rid, "_editor_decal_color", editor_decal_color)
|
||||
RenderingServer.material_set_param(mat_rid, "_editor_decal_visible", editor_decal_visible)
|
||||
|
||||
|
||||
func set_decal_rotation(p_rot: float) -> void:
|
||||
decal.rotation.y = p_rot
|
||||
|
||||
|
@ -514,16 +366,16 @@ func pick(p_global_position: Vector3) -> void:
|
|||
if picking != Terrain3DEditor.TOOL_MAX:
|
||||
var color: Color
|
||||
match picking:
|
||||
Terrain3DEditor.HEIGHT, Terrain3DEditor.SCULPT:
|
||||
color = plugin.terrain.data.get_pixel(Terrain3DRegion.TYPE_HEIGHT, p_global_position)
|
||||
Terrain3DEditor.HEIGHT:
|
||||
color = plugin.terrain.get_storage().get_pixel(Terrain3DStorage.TYPE_HEIGHT, p_global_position)
|
||||
Terrain3DEditor.ROUGHNESS:
|
||||
color = plugin.terrain.data.get_pixel(Terrain3DRegion.TYPE_COLOR, p_global_position)
|
||||
color = plugin.terrain.get_storage().get_pixel(Terrain3DStorage.TYPE_COLOR, p_global_position)
|
||||
Terrain3DEditor.COLOR:
|
||||
color = plugin.terrain.data.get_color(p_global_position)
|
||||
color = plugin.terrain.get_storage().get_color(p_global_position)
|
||||
Terrain3DEditor.ANGLE:
|
||||
color = Color(plugin.terrain.data.get_control_angle(p_global_position), 0., 0., 1.)
|
||||
color = Color(plugin.terrain.get_storage().get_angle(p_global_position), 0., 0., 1.)
|
||||
Terrain3DEditor.SCALE:
|
||||
color = Color(plugin.terrain.data.get_control_scale(p_global_position), 0., 0., 1.)
|
||||
color = Color(plugin.terrain.get_storage().get_scale(p_global_position), 0., 0., 1.)
|
||||
_:
|
||||
push_error("Unsupported picking type: ", picking)
|
||||
return
|
||||
|
@ -533,6 +385,3 @@ func pick(p_global_position: Vector3) -> void:
|
|||
elif operation_builder and operation_builder.is_picking():
|
||||
operation_builder.pick(p_global_position, plugin.terrain)
|
||||
|
||||
|
||||
func set_button_editor_icon(p_button: Button, p_icon_name: String) -> void:
|
||||
p_button.icon = EditorInterface.get_base_control().get_theme_icon(p_icon_name, "EditorIcons")
|
||||
|
|
|
@ -12,83 +12,64 @@ func reset_settings(p_value) -> void:
|
|||
height_file_name = ""
|
||||
control_file_name = ""
|
||||
color_file_name = ""
|
||||
destination_directory = ""
|
||||
import_position = Vector2i.ZERO
|
||||
height_offset = 0.0
|
||||
import_position = Vector3.ZERO
|
||||
import_offset = 0.0
|
||||
import_scale = 1.0
|
||||
r16_range = Vector2(0, 1)
|
||||
r16_size = Vector2i(1024, 1024)
|
||||
storage = null
|
||||
material = null
|
||||
assets = null
|
||||
|
||||
|
||||
func reset_terrain(p_value) -> void:
|
||||
data_directory = ""
|
||||
if p_value:
|
||||
storage = null
|
||||
|
||||
|
||||
func update_heights(p_value) -> void:
|
||||
if p_value and data:
|
||||
data.update_height_range()
|
||||
if p_value and storage:
|
||||
storage.update_height_range()
|
||||
|
||||
|
||||
@export_group("Import File")
|
||||
@export_global_file var height_file_name: String = ""
|
||||
@export_global_file var control_file_name: String = ""
|
||||
@export_global_file var color_file_name: String = ""
|
||||
@export var import_position: Vector2i = Vector2i(0, 0) : set = set_import_position
|
||||
@export var import_position: Vector3 = Vector3.ZERO
|
||||
@export var import_scale: float = 1.0
|
||||
@export var height_offset: float = 0.0
|
||||
@export var import_offset: float = 0.0
|
||||
@export var r16_range: Vector2 = Vector2(0, 1)
|
||||
@export var r16_size: Vector2i = Vector2i(1024, 1024) : set = set_r16_size
|
||||
@export var r16_size: Vector2i = Vector2i(1024, 1024)
|
||||
@export var run_import: bool = false : set = start_import
|
||||
|
||||
@export_dir var destination_directory: String = ""
|
||||
@export var save_to_disk: bool = false : set = save_data
|
||||
|
||||
|
||||
func set_import_position(p_value: Vector2i) -> void:
|
||||
import_position.x = clamp(p_value.x, -8192, 8192)
|
||||
import_position.y = clamp(p_value.y, -8192, 8192)
|
||||
|
||||
|
||||
func set_r16_size(p_value: Vector2i) -> void:
|
||||
r16_size.x = clamp(p_value.x, 0, 16384)
|
||||
r16_size.y = clamp(p_value.y, 0, 16384)
|
||||
|
||||
|
||||
func start_import(p_value: bool) -> void:
|
||||
if p_value:
|
||||
print("Terrain3DImporter: Importing files:\n\t%s\n\t%s\n\t%s" % [ height_file_name, control_file_name, color_file_name])
|
||||
if not storage:
|
||||
storage = Terrain3DStorage.new()
|
||||
|
||||
var imported_images: Array[Image]
|
||||
imported_images.resize(Terrain3DRegion.TYPE_MAX)
|
||||
imported_images.resize(Terrain3DStorage.TYPE_MAX)
|
||||
var min_max := Vector2(0, 1)
|
||||
var img: Image
|
||||
if height_file_name:
|
||||
img = Terrain3DUtil.load_image(height_file_name, ResourceLoader.CACHE_MODE_IGNORE, r16_range, r16_size)
|
||||
min_max = Terrain3DUtil.get_min_max(img)
|
||||
imported_images[Terrain3DRegion.TYPE_HEIGHT] = img
|
||||
imported_images[Terrain3DStorage.TYPE_HEIGHT] = img
|
||||
if control_file_name:
|
||||
img = Terrain3DUtil.load_image(control_file_name, ResourceLoader.CACHE_MODE_IGNORE)
|
||||
imported_images[Terrain3DRegion.TYPE_CONTROL] = img
|
||||
imported_images[Terrain3DStorage.TYPE_CONTROL] = img
|
||||
if color_file_name:
|
||||
img = Terrain3DUtil.load_image(color_file_name, ResourceLoader.CACHE_MODE_IGNORE)
|
||||
imported_images[Terrain3DRegion.TYPE_COLOR] = img
|
||||
imported_images[Terrain3DStorage.TYPE_COLOR] = img
|
||||
if assets.get_texture_count() == 0:
|
||||
material.show_checkered = false
|
||||
material.show_colormap = true
|
||||
var pos := Vector3(import_position.x, 0, import_position.y)
|
||||
data.import_images(imported_images, pos, height_offset, import_scale)
|
||||
storage.import_images(imported_images, import_position, import_offset, import_scale)
|
||||
print("Terrain3DImporter: Import finished")
|
||||
|
||||
|
||||
func save_data(p_value: bool) -> void:
|
||||
if destination_directory.is_empty():
|
||||
push_error("Set destination directory first")
|
||||
return
|
||||
data.save_directory(destination_directory)
|
||||
|
||||
|
||||
@export_group("Export File")
|
||||
enum { TYPE_HEIGHT, TYPE_CONTROL, TYPE_COLOR }
|
||||
@export_enum("Height:0", "Control:1", "Color:2") var map_type: int = TYPE_HEIGHT
|
||||
|
@ -96,6 +77,6 @@ enum { TYPE_HEIGHT, TYPE_CONTROL, TYPE_COLOR }
|
|||
@export var run_export: bool = false : set = start_export
|
||||
|
||||
func start_export(p_value: bool) -> void:
|
||||
var err: int = data.export_image(file_name_out, map_type)
|
||||
var err: int = storage.export_image(file_name_out, map_type)
|
||||
print("Terrain3DImporter: Export error status: ", err, " ", error_string(err))
|
||||
|
||||
|
|
|
@ -1,63 +1,16 @@
|
|||
[gd_scene load_steps=9 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"]
|
||||
|
||||
[sub_resource type="Gradient" id="Gradient_88f3t"]
|
||||
offsets = PackedFloat32Array(0.2, 1)
|
||||
colors = PackedColorArray(1, 1, 1, 1, 0, 0, 0, 1)
|
||||
[sub_resource type="Terrain3DStorage" id="Terrain3DStorage_rmuvl"]
|
||||
|
||||
[sub_resource type="FastNoiseLite" id="FastNoiseLite_muvel"]
|
||||
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_ve0yk"]
|
||||
seamless = true
|
||||
color_ramp = SubResource("Gradient_88f3t")
|
||||
noise = SubResource("FastNoiseLite_muvel")
|
||||
|
||||
[sub_resource type="Terrain3DMaterial" id="Terrain3DMaterial_p55u0"]
|
||||
_shader_parameters = {
|
||||
"blend_sharpness": 0.87,
|
||||
"height_blending": true,
|
||||
"macro_variation1": Color(1, 1, 1, 1),
|
||||
"macro_variation2": Color(1, 1, 1, 1),
|
||||
"noise1_angle": 0.0,
|
||||
"noise1_offset": Vector2(0.5, 0.5),
|
||||
"noise1_scale": 0.04,
|
||||
"noise2_scale": 0.076,
|
||||
"noise3_scale": 0.225,
|
||||
"noise_texture": SubResource("NoiseTexture2D_ve0yk"),
|
||||
"vertex_normals_distance": 128.0
|
||||
}
|
||||
[sub_resource type="Terrain3DMaterial" id="Terrain3DMaterial_cjpaa"]
|
||||
show_checkered = true
|
||||
|
||||
[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_8rvqy"]
|
||||
cull_mode = 2
|
||||
vertex_color_use_as_albedo = true
|
||||
backlight_enabled = true
|
||||
backlight = Color(0.5, 0.5, 0.5, 1)
|
||||
|
||||
[sub_resource type="Terrain3DMeshAsset" id="Terrain3DMeshAsset_7je72"]
|
||||
height_offset = 0.5
|
||||
density = 10.0
|
||||
material_override = SubResource("StandardMaterial3D_8rvqy")
|
||||
generated_type = 1
|
||||
|
||||
[sub_resource type="Terrain3DAssets" id="Terrain3DAssets_op32e"]
|
||||
mesh_list = Array[Terrain3DMeshAsset]([SubResource("Terrain3DMeshAsset_7je72")])
|
||||
[sub_resource type="Terrain3DAssets" id="Terrain3DAssets_gbxcd"]
|
||||
|
||||
[node name="Importer" type="Terrain3D"]
|
||||
material = SubResource("Terrain3DMaterial_p55u0")
|
||||
assets = SubResource("Terrain3DAssets_op32e")
|
||||
mesh_lods = 8
|
||||
top_level = true
|
||||
storage = SubResource("Terrain3DStorage_rmuvl")
|
||||
material = SubResource("Terrain3DMaterial_cjpaa")
|
||||
assets = SubResource("Terrain3DAssets_gbxcd")
|
||||
script = ExtResource("1_60b8f")
|
||||
metadata/_edit_lock_ = true
|
||||
|
|
|
@ -42,22 +42,22 @@ func editor_setup(p_plugin) -> void:
|
|||
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] = Engine.get_singleton(&"EditorInterface").get_edited_scene_root().find_children("", "Terrain3D")
|
||||
var terrains: Array[Node] = EditorInterface.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.data and not terrain.data.maps_edited.is_connected(_on_maps_edited):
|
||||
terrain.data.maps_edited.connect(_on_maps_edited)
|
||||
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.data:
|
||||
if not terrain or not terrain.storage:
|
||||
return 0.0
|
||||
var height: float = terrain.data.get_height(p_global_position)
|
||||
var height: float = terrain.storage.get_height(p_global_position)
|
||||
if is_nan(height):
|
||||
return 0.0
|
||||
return height
|
||||
|
@ -105,7 +105,7 @@ func _on_child_exiting_tree(p_node: Node) -> void:
|
|||
|
||||
|
||||
func _is_node_selected(p_node: Node) -> bool:
|
||||
var editor_sel = Engine.get_singleton(&"EditorInterface").get_selection()
|
||||
var editor_sel = EditorInterface.get_selection()
|
||||
return editor_sel.get_transformable_selected_nodes().has(p_node)
|
||||
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
[gd_scene load_steps=54 format=3 uid="uid://bm2o3mex10v11"]
|
||||
[gd_scene load_steps=56 format=3 uid="uid://bm2o3mex10v11"]
|
||||
|
||||
[ext_resource type="Terrain3DStorage" uid="uid://bu1gewgsgc5hm" path="res://levels/debug_level/terrain_3d_storage.res" id="1_nlsu2"]
|
||||
[ext_resource type="Texture2D" path="res://assets/textures/grass_fairway/grass_fairway_albedo.dds" id="2_e4m27"]
|
||||
[ext_resource type="Texture2D" path="res://assets/textures/grass_rough/grass_rough_albedo.dds" id="4_p0awb"]
|
||||
[ext_resource type="Texture2D" path="res://assets/textures/grass_rough/grass_rough_normal.dds" id="4_yhjhv"]
|
||||
|
@ -9,6 +10,7 @@
|
|||
[ext_resource type="Texture2D" path="res://assets/textures/sand_trap/sand_trap_albedo.dds" id="6_nlea7"]
|
||||
[ext_resource type="Texture2D" path="res://assets/textures/sand_trap/sand_trap_normal.dds" id="7_f25dn"]
|
||||
[ext_resource type="Texture2D" uid="uid://con5a36t6n6sq" path="res://assets/textures/clear_sea_water_2048x2048.png" id="7_t86h2"]
|
||||
[ext_resource type="PackedScene" uid="uid://cy7t2tc4y3b4" path="res://src/player/shot_setup/shot_setup.tscn" id="8_h44v5"]
|
||||
[ext_resource type="Texture2D" path="res://assets/textures/mountain_rock/mountain_rock_albedo.dds" id="8_ujbjy"]
|
||||
[ext_resource type="Script" path="res://src/world/ball_zone/ball_zone.gd" id="9_jwlau"]
|
||||
[ext_resource type="Texture2D" path="res://assets/textures/mountain_rock/mountain_rock_normal.dds" id="9_rrguh"]
|
||||
|
@ -49,32 +51,30 @@ _shader_parameters = {
|
|||
"noise3_scale": 0.021,
|
||||
"noise_texture": SubResource("NoiseTexture2D_sb318"),
|
||||
"vertex_normals_distance": 128.0,
|
||||
"world_noise_blend_far": 1.0,
|
||||
"world_noise_blend_near": 0.48,
|
||||
"world_noise_height": 74.0,
|
||||
"world_noise_lod_distance": 7500.0,
|
||||
"world_noise_lod_distance": 16384.0,
|
||||
"world_noise_max_octaves": 8,
|
||||
"world_noise_min_octaves": 2,
|
||||
"world_noise_offset": Vector3(0, -8, 0),
|
||||
"world_noise_region_blend": 0.55,
|
||||
"world_noise_scale": 5.0
|
||||
}
|
||||
world_background = 2
|
||||
texture_filtering = 1
|
||||
auto_shader = true
|
||||
|
||||
[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_l1sla"]
|
||||
transparency = 4
|
||||
[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_2u1w6"]
|
||||
cull_mode = 2
|
||||
vertex_color_use_as_albedo = true
|
||||
backlight_enabled = true
|
||||
backlight = Color(0.5, 0.5, 0.5, 1)
|
||||
distance_fade_mode = 1
|
||||
distance_fade_min_distance = 960.0
|
||||
distance_fade_max_distance = 480.0
|
||||
|
||||
[sub_resource type="Terrain3DMeshAsset" id="Terrain3DMeshAsset_iaq2t"]
|
||||
[sub_resource type="Terrain3DMeshAsset" id="Terrain3DMeshAsset_8g62o"]
|
||||
name = "palm_tree"
|
||||
height_offset = 0.5
|
||||
density = 10.0
|
||||
material_override = SubResource("StandardMaterial3D_l1sla")
|
||||
material_override = SubResource("StandardMaterial3D_2u1w6")
|
||||
generated_type = 1
|
||||
|
||||
[sub_resource type="Terrain3DTextureAsset" id="Terrain3DTextureAsset_kocfk"]
|
||||
|
@ -105,7 +105,7 @@ normal_texture = ExtResource("9_rrguh")
|
|||
uv_scale = 0.02
|
||||
|
||||
[sub_resource type="Terrain3DAssets" id="Terrain3DAssets_eq5uw"]
|
||||
mesh_list = Array[Terrain3DMeshAsset]([SubResource("Terrain3DMeshAsset_iaq2t")])
|
||||
mesh_list = Array[Terrain3DMeshAsset]([SubResource("Terrain3DMeshAsset_8g62o")])
|
||||
texture_list = Array[Terrain3DTextureAsset]([SubResource("Terrain3DTextureAsset_kocfk"), SubResource("Terrain3DTextureAsset_vmhlw"), SubResource("Terrain3DTextureAsset_k6h8c"), SubResource("Terrain3DTextureAsset_13d2a")])
|
||||
|
||||
[sub_resource type="PanoramaSkyMaterial" id="PanoramaSkyMaterial_h8tes"]
|
||||
|
@ -252,12 +252,9 @@ point_count = 5
|
|||
[node name="TestLevel" type="Node3D"]
|
||||
|
||||
[node name="Terrain3D" type="Terrain3D" parent="."]
|
||||
data_directory = "res://levels/debug_level/terrain_3d_data"
|
||||
storage = ExtResource("1_nlsu2")
|
||||
material = SubResource("Terrain3DMaterial_woy2k")
|
||||
assets = SubResource("Terrain3DAssets_eq5uw")
|
||||
mesh_size = 64
|
||||
top_level = true
|
||||
metadata/_edit_lock_ = true
|
||||
|
||||
[node name="WorldEnvironment" type="WorldEnvironment" parent="."]
|
||||
environment = SubResource("Environment_k6wwx")
|
||||
|
@ -305,17 +302,8 @@ shape = SubResource("BoxShape3D_x3wvm")
|
|||
[node name="Flag" parent="Course" instance=ExtResource("13_6jtao")]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 540, 4, 452)
|
||||
|
||||
[node name="PlayerSpawn1" type="Marker3D" parent="Course" groups=["PlayerSpawn"]]
|
||||
transform = Transform3D(-0.842697, 0, -0.538388, 0, 1, 0, 0.538388, 0, -0.842697, 212.717, 4, 294.073)
|
||||
|
||||
[node name="PlayerSpawn2" type="Marker3D" parent="Course" groups=["PlayerSpawn"]]
|
||||
transform = Transform3D(-0.842697, 0, -0.538388, 0, 1, 0, 0.538388, 0, -0.842697, 217.046, 4, 292.261)
|
||||
|
||||
[node name="PlayerSpawn3" type="Marker3D" parent="Course" groups=["PlayerSpawn"]]
|
||||
transform = Transform3D(-0.842697, 0, -0.538388, 0, 1, 0, 0.538388, 0, -0.842697, 218.01, 4, 289.245)
|
||||
|
||||
[node name="PlayerSpawn4" type="Marker3D" parent="Course" groups=["PlayerSpawn"]]
|
||||
transform = Transform3D(-0.842697, 0, -0.538388, 0, 1, 0, 0.538388, 0, -0.842697, 214.256, 4, 290.91)
|
||||
[node name="ShotSetup" parent="Course" instance=ExtResource("8_h44v5")]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 212.717, 4, 294.073)
|
||||
|
||||
[node name="GuardRail" parent="Course" instance=ExtResource("16_xtxyj")]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 211.416, 4, 288.708)
|
||||
|
|
BIN
levels/debug_level/terrain_3d_data/terrain3d-01-01.res (Stored with Git LFS)
BIN
levels/debug_level/terrain_3d_data/terrain3d-01-01.res (Stored with Git LFS)
Binary file not shown.
BIN
levels/debug_level/terrain_3d_data/terrain3d-01_00.res (Stored with Git LFS)
BIN
levels/debug_level/terrain_3d_data/terrain3d-01_00.res (Stored with Git LFS)
Binary file not shown.
BIN
levels/debug_level/terrain_3d_data/terrain3d_00-01.res (Stored with Git LFS)
BIN
levels/debug_level/terrain_3d_data/terrain3d_00-01.res (Stored with Git LFS)
Binary file not shown.
BIN
levels/debug_level/terrain_3d_data/terrain3d_00_00.res (Stored with Git LFS)
BIN
levels/debug_level/terrain_3d_data/terrain3d_00_00.res (Stored with Git LFS)
Binary file not shown.
Binary file not shown.
|
@ -12,7 +12,7 @@ config_version=5
|
|||
|
||||
config/name="GFOLF 2"
|
||||
config/description="GFOLF: Combat Golf Action"
|
||||
run/main_scene="res://src/world/world.tscn"
|
||||
run/main_scene="res://levels/debug_level/debug_level.tscn"
|
||||
config/features=PackedStringArray("4.3", "Forward Plus")
|
||||
run/max_fps=60
|
||||
|
||||
|
@ -56,10 +56,6 @@ config/controls/camera/x_axis_acceleration=30.0
|
|||
config/controls/camera/y_axis_acceleration=30.0
|
||||
config/controls/camera/invert_pitch=false
|
||||
|
||||
[global_group]
|
||||
|
||||
WorldGroup="Singleton group for the active world instance, if any."
|
||||
|
||||
[gui]
|
||||
|
||||
theme/custom="res://src/ui/main_theme.tres"
|
||||
|
|
|
@ -169,7 +169,7 @@ bones/23/scale = Vector3(1, 1, 1)
|
|||
visible = false
|
||||
|
||||
[node name="BoneAttachment3D" type="BoneAttachment3D" parent="Armature/Skeleton3D" index="6"]
|
||||
transform = Transform3D(-0.947388, 0.307308, -0.089546, -0.319114, -0.928605, 0.189367, -0.0249588, 0.207979, 0.977815, -0.157835, 3.16355, 0.865627)
|
||||
transform = Transform3D(-0.944824, 0.316851, -0.0831417, -0.326395, -0.932135, 0.156815, -0.027812, 0.1753, 0.984122, -0.1687, 3.13445, 0.86185)
|
||||
bone_name = "Hand.R"
|
||||
bone_idx = 11
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
class_name Club extends Resource
|
||||
class_name Club extends Node
|
||||
## Base type for gfolf clubs
|
||||
|
||||
enum Type {
|
||||
|
@ -10,7 +10,6 @@ enum Type {
|
|||
SPECIAL,
|
||||
}
|
||||
|
||||
@export var name: String
|
||||
@export var type: Club.Type
|
||||
@export var _model: PackedScene
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
class_name ClubCatalogType extends Node
|
||||
## Provides access to Club equipment
|
||||
|
||||
@export var debug_driver: Club = preload("res://src/equipment/clubs/drivers/debug_driver.tres")
|
||||
@export var debug_iron: Club = preload("res://src/equipment/clubs/irons/debug_iron.tres")
|
||||
@export var debug_wedge: Club = preload("res://src/equipment/clubs/wedges/debug_wedge.tres")
|
||||
@export var debug_putter: Club = preload("res://src/equipment/clubs/putters/debug_putter.tres")
|
||||
@onready var debug_driver: Club = %"DEBUG Driver"
|
||||
@onready var debug_iron: Club = %"DEBUG Iron"
|
||||
@onready var debug_wedge: Club = %"DEBUG Wedge"
|
||||
@onready var debug_putter: Club = %"DEBUG Putter"
|
||||
|
|
|
@ -1,6 +1,35 @@
|
|||
[gd_scene load_steps=2 format=3 uid="uid://brn02ebnp248v"]
|
||||
[gd_scene load_steps=7 format=3 uid="uid://f5qw4losi1ag"]
|
||||
|
||||
[ext_resource type="Script" path="res://src/equipment/clubs/club_catalog.gd" id="1_omf4k"]
|
||||
[ext_resource type="Script" path="res://src/equipment/clubs/club.gd" id="2_hl4eh"]
|
||||
[ext_resource type="PackedScene" uid="uid://dekwor3a7kqio" path="res://assets/models/clubs/driver.gltf" id="3_gocak"]
|
||||
[ext_resource type="PackedScene" uid="uid://btpn1j6jwv4pr" path="res://assets/models/clubs/iron.gltf" id="4_ile8w"]
|
||||
[ext_resource type="PackedScene" uid="uid://d4bnvf34xenya" path="res://assets/models/clubs/wedge.gltf" id="5_qa3by"]
|
||||
[ext_resource type="PackedScene" uid="uid://bo41mkkc70fcl" path="res://assets/models/clubs/putter.gltf" id="6_8bavt"]
|
||||
|
||||
[node name="ClubCatalog" type="Node"]
|
||||
script = ExtResource("1_omf4k")
|
||||
|
||||
[node name="DEBUG Driver" type="Node" parent="."]
|
||||
unique_name_in_owner = true
|
||||
script = ExtResource("2_hl4eh")
|
||||
type = 1
|
||||
_model = ExtResource("3_gocak")
|
||||
|
||||
[node name="DEBUG Iron" type="Node" parent="."]
|
||||
unique_name_in_owner = true
|
||||
script = ExtResource("2_hl4eh")
|
||||
type = 2
|
||||
_model = ExtResource("4_ile8w")
|
||||
|
||||
[node name="DEBUG Wedge" type="Node" parent="."]
|
||||
unique_name_in_owner = true
|
||||
script = ExtResource("2_hl4eh")
|
||||
type = 3
|
||||
_model = ExtResource("5_qa3by")
|
||||
|
||||
[node name="DEBUG Putter" type="Node" parent="."]
|
||||
unique_name_in_owner = true
|
||||
script = ExtResource("2_hl4eh")
|
||||
type = 4
|
||||
_model = ExtResource("6_8bavt")
|
||||
|
|
|
@ -1,10 +0,0 @@
|
|||
[gd_resource type="Resource" script_class="Club" load_steps=3 format=3 uid="uid://c1pnqsddvey3m"]
|
||||
|
||||
[ext_resource type="Script" path="res://src/equipment/clubs/club.gd" id="1_7mcyu"]
|
||||
[ext_resource type="PackedScene" uid="uid://dekwor3a7kqio" path="res://assets/models/clubs/driver.gltf" id="1_ug675"]
|
||||
|
||||
[resource]
|
||||
script = ExtResource("1_7mcyu")
|
||||
name = "DEBUG Driver"
|
||||
type = 1
|
||||
_model = ExtResource("1_ug675")
|
|
@ -1,10 +0,0 @@
|
|||
[gd_resource type="Resource" script_class="Club" load_steps=3 format=3 uid="uid://ck17u5yn6k0bi"]
|
||||
|
||||
[ext_resource type="PackedScene" uid="uid://btpn1j6jwv4pr" path="res://assets/models/clubs/iron.gltf" id="1_5wgil"]
|
||||
[ext_resource type="Script" path="res://src/equipment/clubs/club.gd" id="1_6ksva"]
|
||||
|
||||
[resource]
|
||||
script = ExtResource("1_6ksva")
|
||||
name = "DEBUG Iron"
|
||||
type = 2
|
||||
_model = ExtResource("1_5wgil")
|
|
@ -1,10 +0,0 @@
|
|||
[gd_resource type="Resource" script_class="Club" load_steps=3 format=3 uid="uid://dagld0q5krapu"]
|
||||
|
||||
[ext_resource type="PackedScene" uid="uid://bo41mkkc70fcl" path="res://assets/models/clubs/putter.gltf" id="1_ghpah"]
|
||||
[ext_resource type="Script" path="res://src/equipment/clubs/club.gd" id="1_qlirk"]
|
||||
|
||||
[resource]
|
||||
script = ExtResource("1_qlirk")
|
||||
name = "DEBUG Putter"
|
||||
type = 4
|
||||
_model = ExtResource("1_ghpah")
|
|
@ -1,10 +0,0 @@
|
|||
[gd_resource type="Resource" script_class="Club" load_steps=3 format=3 uid="uid://dthtc1no2c4wy"]
|
||||
|
||||
[ext_resource type="Script" path="res://src/equipment/clubs/club.gd" id="1_gy3nk"]
|
||||
[ext_resource type="PackedScene" uid="uid://d4bnvf34xenya" path="res://assets/models/clubs/wedge.gltf" id="1_voves"]
|
||||
|
||||
[resource]
|
||||
script = ExtResource("1_gy3nk")
|
||||
name = "DEBUG Wedge"
|
||||
type = 3
|
||||
_model = ExtResource("1_voves")
|
|
@ -1,15 +0,0 @@
|
|||
[gd_resource type="Resource" script_class="WorldPlayer" load_steps=6 format=3 uid="uid://crock3revdn73"]
|
||||
|
||||
[ext_resource type="Resource" uid="uid://c1pnqsddvey3m" path="res://src/equipment/clubs/drivers/debug_driver.tres" id="1_sn8fd"]
|
||||
[ext_resource type="Resource" uid="uid://ck17u5yn6k0bi" path="res://src/equipment/clubs/irons/debug_iron.tres" id="2_piku2"]
|
||||
[ext_resource type="Resource" uid="uid://dagld0q5krapu" path="res://src/equipment/clubs/putters/debug_putter.tres" id="3_tytwr"]
|
||||
[ext_resource type="Script" path="res://src/player/world_player.gd" id="4_8ybyj"]
|
||||
[ext_resource type="Resource" uid="uid://dthtc1no2c4wy" path="res://src/equipment/clubs/wedges/debug_wedge.tres" id="5_wm4ae"]
|
||||
|
||||
[resource]
|
||||
script = ExtResource("4_8ybyj")
|
||||
name = "DEBUG Gfolfer"
|
||||
driver = ExtResource("1_sn8fd")
|
||||
iron = ExtResource("2_piku2")
|
||||
wedge = ExtResource("5_wm4ae")
|
||||
putter = ExtResource("3_tytwr")
|
|
@ -22,7 +22,7 @@ var invert_pitch: bool = ProjectSettings.get_setting("game/config/controls/camer
|
|||
|
||||
@onready var _target := Vector2(rotation.x, rotation.y)
|
||||
|
||||
static var scene := preload("res://src/ui/camera/free_camera/free_camera.tscn")
|
||||
static var scene := preload("res://src/player/free_camera/free_camera.tscn")
|
||||
|
||||
|
||||
func _unhandled_input(event: InputEvent) -> void:
|
|
@ -1,6 +1,6 @@
|
|||
[gd_scene load_steps=3 format=3 uid="uid://dd17ce110sw6p"]
|
||||
|
||||
[ext_resource type="Script" path="res://src/ui/camera/free_camera/free_camera.gd" id="1_3gm3q"]
|
||||
[ext_resource type="Script" path="res://src/player/free_camera/free_camera.gd" id="1_3gm3q"]
|
||||
|
||||
[sub_resource type="SphereShape3D" id="SphereShape3D_wmusx"]
|
||||
|
|
@ -9,7 +9,7 @@ const POSITION_ACCELERATION := 4.0
|
|||
|
||||
@onready var camera: Camera3D = %Camera
|
||||
|
||||
static var scene := preload("res://src/ui/camera/orbital_camera/orbital_camera.tscn")
|
||||
static var scene := preload("res://src/player/orbital_camera/orbital_camera.tscn")
|
||||
|
||||
|
||||
func _physics_process(delta: float) -> void:
|
|
@ -1,6 +1,6 @@
|
|||
[gd_scene load_steps=2 format=3 uid="uid://de5onfk2tyr1d"]
|
||||
|
||||
[ext_resource type="Script" path="res://src/ui/camera/orbital_camera/orbital_camera.gd" id="1_nvlic"]
|
||||
[ext_resource type="Script" path="res://src/player/orbital_camera/orbital_camera.gd" id="1_nvlic"]
|
||||
|
||||
[node name="OrbitalCamera" type="Node3D"]
|
||||
script = ExtResource("1_nvlic")
|
|
@ -24,12 +24,10 @@ const IRON_DAMPING := 9999.0
|
|||
|
||||
var _zones: Array[BallZone] = []
|
||||
|
||||
@onready var normal_physics: PhysicsMaterial = preload(
|
||||
"res://src/equipment/balls/physics_ball/normal_physics.tres"
|
||||
)
|
||||
@onready var iron_physics: PhysicsMaterial = preload(
|
||||
"res://src/equipment/balls/physics_ball/iron_physics.tres"
|
||||
)
|
||||
@onready
|
||||
var normal_physics: PhysicsMaterial = preload("res://src/player/physics_ball/normal_physics.tres")
|
||||
@onready
|
||||
var iron_physics: PhysicsMaterial = preload("res://src/player/physics_ball/iron_physics.tres")
|
||||
|
||||
|
||||
## Called by a water area when this ball enters it
|
|
@ -1,7 +1,7 @@
|
|||
[gd_scene load_steps=8 format=3 uid="uid://dfttci386ohip"]
|
||||
|
||||
[ext_resource type="Script" path="res://src/equipment/balls/physics_ball/game_ball.gd" id="1_iwh2u"]
|
||||
[ext_resource type="PhysicsMaterial" uid="uid://3bih72l068ic" path="res://src/equipment/balls/physics_ball/normal_physics.tres" id="1_l23pw"]
|
||||
[ext_resource type="Script" path="res://src/player/physics_ball/game_ball.gd" id="1_iwh2u"]
|
||||
[ext_resource type="PhysicsMaterial" uid="uid://3bih72l068ic" path="res://src/player/physics_ball/normal_physics.tres" id="1_l23pw"]
|
||||
[ext_resource type="Texture2D" uid="uid://ckhiq6rfjv63u" path="res://assets/textures/gfolfball/gfolfball.png" id="1_y3q5j"]
|
||||
[ext_resource type="Texture2D" uid="uid://btntjs7mbdigu" path="res://assets/textures/gfolfball/gfolfball_normal.png" id="2_mx7l6"]
|
||||
|
|
@ -20,20 +20,11 @@ const CLUB_ROTATION := {
|
|||
|
||||
@onready var club_hub: Control = %ClubHub
|
||||
|
||||
@onready var driver_label: ClubSelectorLabel = %DriverLabel
|
||||
@onready var iron_label: ClubSelectorLabel = %IronLabel
|
||||
@onready var wedge_label: ClubSelectorLabel = %WedgeLabel
|
||||
@onready var special_label: ClubSelectorLabel = %SpecialLabel
|
||||
@onready var putter_label: ClubSelectorLabel = %PutterLabel
|
||||
|
||||
|
||||
## Enable/disable labels based on the player's available equipment
|
||||
func set_state_for_player(player: WorldPlayer) -> void:
|
||||
driver_label.set_enabled(player.driver != null)
|
||||
iron_label.set_enabled(player.iron != null)
|
||||
wedge_label.set_enabled(player.wedge != null)
|
||||
special_label.set_enabled(player.special != null)
|
||||
putter_label.set_enabled(player.putter != null)
|
||||
@onready var driver_label: Label = %DriverLabel
|
||||
@onready var iron_label: Label = %IronLabel
|
||||
@onready var wedge_label: Label = %WedgeLabel
|
||||
@onready var special_label: Label = %SpecialLabel
|
||||
@onready var putter_label: Label = %PutterLabel
|
||||
|
||||
|
||||
func _get_club_label(club: Club.Type) -> Label:
|
|
@ -1,9 +1,8 @@
|
|||
[gd_scene load_steps=8 format=3 uid="uid://445qd7m4qe2j"]
|
||||
[gd_scene load_steps=7 format=3 uid="uid://445qd7m4qe2j"]
|
||||
|
||||
[ext_resource type="Script" path="res://src/ui/shot_hud/club_selector/club_selector.gd" id="1_cttgc"]
|
||||
[ext_resource type="Script" path="res://src/player/shot_setup/club_selector/club_selector.gd" id="1_cttgc"]
|
||||
[ext_resource type="Script" path="res://src/ui/decorations/arc.gd" id="2_vijl7"]
|
||||
[ext_resource type="Script" path="res://src/ui/decorations/double_rotator.gd" id="3_0ptmq"]
|
||||
[ext_resource type="Script" path="res://src/ui/shot_hud/club_selector/club_selector_label.gd" id="4_j7p7f"]
|
||||
[ext_resource type="Script" path="res://src/ui/double_rotator.gd" id="3_0ptmq"]
|
||||
|
||||
[sub_resource type="Animation" id="Animation_crn6i"]
|
||||
length = 0.001
|
||||
|
@ -108,7 +107,6 @@ scale = Vector2(0.618, 0.618)
|
|||
pivot_offset = Vector2(0, 20)
|
||||
theme_type_variation = &"ClubSelectLabel"
|
||||
text = "DRIVER"
|
||||
script = ExtResource("4_j7p7f")
|
||||
|
||||
[node name="IronLabel" type="Label" parent="ClubHub"]
|
||||
unique_name_in_owner = true
|
||||
|
@ -128,7 +126,6 @@ scale = Vector2(0.618, 0.618)
|
|||
pivot_offset = Vector2(0, 20)
|
||||
theme_type_variation = &"ClubSelectLabel"
|
||||
text = "IRON"
|
||||
script = ExtResource("4_j7p7f")
|
||||
|
||||
[node name="WedgeLabel" type="Label" parent="ClubHub"]
|
||||
unique_name_in_owner = true
|
||||
|
@ -148,7 +145,6 @@ scale = Vector2(0.618, 0.618)
|
|||
pivot_offset = Vector2(0, 20)
|
||||
theme_type_variation = &"ClubSelectLabel"
|
||||
text = "WEDGE"
|
||||
script = ExtResource("4_j7p7f")
|
||||
|
||||
[node name="SpecialLabel" type="Label" parent="ClubHub"]
|
||||
unique_name_in_owner = true
|
||||
|
@ -168,7 +164,6 @@ scale = Vector2(0.618, 0.618)
|
|||
pivot_offset = Vector2(0, 20)
|
||||
theme_type_variation = &"ClubSelectLabel"
|
||||
text = "SPECIAL"
|
||||
script = ExtResource("4_j7p7f")
|
||||
|
||||
[node name="PutterLabel" type="Label" parent="ClubHub"]
|
||||
unique_name_in_owner = true
|
||||
|
@ -188,4 +183,3 @@ scale = Vector2(0.618, 0.618)
|
|||
pivot_offset = Vector2(0, 20)
|
||||
theme_type_variation = &"ClubSelectLabel"
|
||||
text = "PUTTER"
|
||||
script = ExtResource("4_j7p7f")
|
|
@ -1,7 +1,5 @@
|
|||
class_name ShotSetup extends Node3D
|
||||
|
||||
signal finished(source: ShotSetup)
|
||||
|
||||
enum Phase {
|
||||
AIM,
|
||||
POWER_ADJUST,
|
||||
|
@ -26,8 +24,6 @@ const FREE_CAM_RETURN_TIME := 0.618
|
|||
const BALL_RETURN_TIME := 0.618
|
||||
const CAMERA_SNAP_TIME := 0.3
|
||||
|
||||
const WASTED_BALL_RETURN_DELAY := 3.5
|
||||
|
||||
## In Driving Range mode, the ball can be retrieved in the shot phase.
|
||||
@export var driving_range := false
|
||||
|
||||
|
@ -46,8 +42,6 @@ const WASTED_BALL_RETURN_DELAY := 3.5
|
|||
## Keep projection visible
|
||||
@export var keep_projection := false
|
||||
|
||||
var player: WorldPlayer
|
||||
|
||||
var base_speed: float = ProjectSettings.get_setting("game/config/controls/camera/free_camera_speed")
|
||||
|
||||
var x_sensitivity: float = ProjectSettings.get_setting(
|
||||
|
@ -67,16 +61,12 @@ var invert_pitch: bool = ProjectSettings.get_setting("game/config/controls/camer
|
|||
|
||||
var control_disabled := false
|
||||
|
||||
var phase: Phase = Phase.FINISHED:
|
||||
var phase: Phase:
|
||||
set(value):
|
||||
if value != phase:
|
||||
_on_phase_change(value)
|
||||
phase = value
|
||||
|
||||
var hud: ShotHUD:
|
||||
get:
|
||||
return world.ui.shot_hud if world and world.ui else null
|
||||
|
||||
var club: Club.Type:
|
||||
set(value):
|
||||
if value != club:
|
||||
|
@ -85,21 +75,8 @@ var club: Club.Type:
|
|||
|
||||
var shot_ref: Node3D
|
||||
|
||||
var shot_power: float:
|
||||
set(value):
|
||||
hud.power_bar.value = value
|
||||
get:
|
||||
return hud.power_bar.value
|
||||
|
||||
var shot_curve: float:
|
||||
set(value):
|
||||
hud.curve_bar.value = value
|
||||
get:
|
||||
return hud.power_bar.value
|
||||
|
||||
var _free_camera: FreeCamera
|
||||
var _returning_free_camera := false
|
||||
var _restart_queued := false
|
||||
|
||||
var _tracking_camera: OrbitalCamera
|
||||
|
||||
|
@ -120,6 +97,12 @@ var _tracking_camera: OrbitalCamera
|
|||
@onready var arrow_animation: AnimationPlayer = %ArrowAnimation
|
||||
@onready var shot_projection: ProjectileArc = %ShotProjection
|
||||
|
||||
@onready var power_bar: ProgressBar = %PowerBar
|
||||
@onready var power_animation: AnimationPlayer = %PowerAnimation
|
||||
|
||||
@onready var curve_bar: ProgressBar = %CurveBar
|
||||
@onready var curve_animation: AnimationPlayer = %CurveAnimation
|
||||
|
||||
@onready var ball_point: Node3D = %BallPoint
|
||||
@onready var physics_ball: GameBall = %PhysicsBall
|
||||
|
||||
|
@ -132,29 +115,27 @@ var _tracking_camera: OrbitalCamera
|
|||
@onready var putt_ref: RayCast3D = %PuttRef
|
||||
@onready var putt_arrow: Node3D = %PuttArrow
|
||||
|
||||
@onready var ball_return_timer: Timer = %BallReturnTimer
|
||||
@onready var club_selector: ClubSelector = %ClubSelector
|
||||
|
||||
@onready var nice_animation: AnimationPlayer = %NiceAnimation
|
||||
@onready var wasted_animation: AnimationPlayer = %WastedAnimation
|
||||
|
||||
@onready var hud_state_machine: AnimationTree = %HUDStateMachine
|
||||
@onready var hud_state: AnimationNodeStateMachinePlayback = hud_state_machine["parameters/playback"]
|
||||
|
||||
@onready var ball_impulse_debug: Node3D = %BallImpulseDebug
|
||||
|
||||
@onready var camera_distance := zoom.position.z:
|
||||
set = _set_camera_distance
|
||||
|
||||
@onready var phys_ball_scene := preload("res://src/equipment/balls/physics_ball/physics_ball.tscn")
|
||||
|
||||
@onready var world: World = get_tree().get_first_node_in_group(World.group)
|
||||
@onready var phys_ball_scene := preload("res://src/player/physics_ball/physics_ball.tscn")
|
||||
|
||||
@onready var _target_rotation := Vector2(pitch.rotation.x, direction.rotation.y)
|
||||
|
||||
static var scene := preload("res://src/player/shot_setup/shot_setup.tscn")
|
||||
|
||||
|
||||
func _init_deferred() -> void:
|
||||
# Set up HUD
|
||||
club = initial_club
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
call_deferred("_init_deferred")
|
||||
_on_phase_change(phase)
|
||||
club = initial_club
|
||||
|
||||
|
||||
func _set_camera_distance(value: float) -> void:
|
||||
|
@ -188,15 +169,6 @@ func camera_motion(motion: Vector2) -> void:
|
|||
)
|
||||
|
||||
|
||||
## Return this instance to the AIM phase the next time we process while FINISHED.
|
||||
func queue_restart() -> void:
|
||||
_restart_queued = true
|
||||
|
||||
|
||||
func is_active() -> bool:
|
||||
return phase != Phase.FINISHED
|
||||
|
||||
|
||||
func finish_downswing() -> void:
|
||||
phase = Phase.SHOT
|
||||
|
||||
|
@ -206,9 +178,9 @@ func get_shot_impulse(meter_pct: float) -> Vector3:
|
|||
|
||||
|
||||
func take_shot() -> void:
|
||||
print_debug("WHACK!\nPower: ", shot_power, "\nCurve: ", shot_curve)
|
||||
print_debug("WHACK!\nPower: ", power_bar.value, "\nCurve: ", curve_bar.value)
|
||||
|
||||
var impulse := get_shot_impulse(shot_power)
|
||||
var impulse := get_shot_impulse(power_bar.value)
|
||||
print_debug("Shot impulse: ", impulse, "; ", impulse.length(), " N*s")
|
||||
|
||||
ball_impulse_debug.transform = (
|
||||
|
@ -234,7 +206,7 @@ func _show_shot_projection() -> void:
|
|||
func insert_free_cam() -> void:
|
||||
arrow_animation.play("hide")
|
||||
_show_shot_projection()
|
||||
hud.hide_hud()
|
||||
hud_state.travel("hidden")
|
||||
_free_camera = FreeCamera.create(camera)
|
||||
add_sibling(_free_camera)
|
||||
control_disabled = true
|
||||
|
@ -248,7 +220,7 @@ func return_free_cam() -> void:
|
|||
if not keep_projection:
|
||||
shot_projection.hide()
|
||||
|
||||
hud.show_hud()
|
||||
hud_state.travel("visible")
|
||||
_free_camera.queue_free()
|
||||
_free_camera = null
|
||||
control_disabled = false
|
||||
|
@ -295,13 +267,7 @@ func end_shot_track() -> void:
|
|||
|
||||
|
||||
## Called immediately before `club` is mutated.
|
||||
func _on_club_change(new_club_type: Club.Type) -> void:
|
||||
var new_club := player.get_club(new_club_type)
|
||||
if not new_club:
|
||||
# `new_club` will be null if player has no club in the given slot
|
||||
# TODO play bonk
|
||||
return
|
||||
|
||||
func _on_club_change(new_club: Club.Type) -> void:
|
||||
# Hide all arrows
|
||||
# TODO animate?
|
||||
drive_arrow.hide()
|
||||
|
@ -309,74 +275,77 @@ func _on_club_change(new_club_type: Club.Type) -> void:
|
|||
iron_arrow.hide()
|
||||
putt_arrow.hide()
|
||||
physics_ball.iron_ball = false
|
||||
hud.club_selector.value = new_club_type
|
||||
# TODO club change animation
|
||||
character.hold_right(new_club.get_model())
|
||||
match new_club_type:
|
||||
club_selector.value = new_club
|
||||
match new_club:
|
||||
Club.Type.DRIVER:
|
||||
shot_ref = drive_ref
|
||||
drive_arrow.show()
|
||||
# TODO get this from player loadout
|
||||
# TODO club change animation
|
||||
character.hold_right(Club.catalog.debug_driver.get_model())
|
||||
Club.Type.PUTTER:
|
||||
shot_ref = putt_ref
|
||||
putt_arrow.show()
|
||||
character.hold_right(Club.catalog.debug_putter.get_model())
|
||||
Club.Type.WEDGE:
|
||||
# TODO
|
||||
shot_ref = wedge_ref
|
||||
wedge_arrow.show()
|
||||
character.hold_right(Club.catalog.debug_wedge.get_model())
|
||||
Club.Type.IRON:
|
||||
# TODO
|
||||
shot_ref = iron_ref
|
||||
iron_arrow.show()
|
||||
physics_ball.iron_ball = true
|
||||
character.hold_right(Club.catalog.debug_iron.get_model())
|
||||
Club.Type.SPECIAL:
|
||||
# TODO figure this out
|
||||
shot_ref = drive_ref
|
||||
# TODO
|
||||
print_debug("TODO: implement special clubs!")
|
||||
_:
|
||||
print_debug("Not sure how to equip club type: ", new_club)
|
||||
|
||||
|
||||
## Called immediately before `phase` is mutated.
|
||||
func _on_phase_change(new_phase: Phase) -> void:
|
||||
print_debug("Player ", player.name, ": change to ", Phase.keys()[new_phase])
|
||||
match new_phase:
|
||||
Phase.AIM:
|
||||
hud.set_state_for_player(player)
|
||||
hud.show_hud()
|
||||
hud_state.travel("visible")
|
||||
if not arrow.visible:
|
||||
arrow_animation.play("show")
|
||||
camera.make_current()
|
||||
hud.power_bar.hide()
|
||||
hud.curve_bar.hide()
|
||||
power_bar.hide()
|
||||
curve_bar.hide()
|
||||
|
||||
character.reset()
|
||||
Phase.POWER_ADJUST:
|
||||
hud.curve_bar.hide()
|
||||
curve_bar.hide()
|
||||
|
||||
hud.power_bar.show()
|
||||
hud.reset_power_bar() # Reset if needed
|
||||
power_bar.show()
|
||||
power_animation.stop() # Reset if needed
|
||||
Phase.CURVE_ADJUST:
|
||||
hud.curve_bar.show()
|
||||
hud.start_curve_bar()
|
||||
curve_bar.show()
|
||||
curve_animation.play("fill")
|
||||
Phase.DOWNSWING:
|
||||
hud.power_bar.hide()
|
||||
hud.curve_bar.hide()
|
||||
power_bar.hide()
|
||||
curve_bar.hide()
|
||||
character.downswing()
|
||||
shot_animation.play("swing_delay") # calls `take_shot`
|
||||
Phase.SHOT:
|
||||
hud.power_bar.hide()
|
||||
hud.curve_bar.hide()
|
||||
power_bar.hide()
|
||||
curve_bar.hide()
|
||||
|
||||
hud.play_nice_animation()
|
||||
nice_animation.play("display")
|
||||
|
||||
if not driving_range:
|
||||
shot_animation.play("shoot")
|
||||
arrow_animation.play("hide")
|
||||
hud.hide_hud()
|
||||
hud_state.travel("hidden")
|
||||
|
||||
take_shot()
|
||||
Phase.FINISHED:
|
||||
hud.power_bar.hide()
|
||||
hud.curve_bar.hide()
|
||||
power_bar.hide()
|
||||
curve_bar.hide()
|
||||
travel_to_ball()
|
||||
finished.emit(self)
|
||||
|
||||
|
||||
func _process(delta: float) -> void:
|
||||
|
@ -451,28 +420,27 @@ func _process(delta: float) -> void:
|
|||
if Input.is_action_just_pressed("shot_accept"):
|
||||
# TODO set power gauge parameters if needed
|
||||
character.start_upswing()
|
||||
hud.start_power_bar()
|
||||
power_animation.play("fill")
|
||||
if Input.is_action_just_pressed("ui_cancel"):
|
||||
hud.reset_power_bar()
|
||||
power_animation.stop()
|
||||
phase = Phase.AIM
|
||||
if Input.is_action_just_released("shot_accept") and shot_power > 0:
|
||||
hud.stop_power_bar()
|
||||
if Input.is_action_just_released("shot_accept") and power_bar.value > 0:
|
||||
power_animation.pause()
|
||||
phase = Phase.CURVE_ADJUST
|
||||
Phase.CURVE_ADJUST:
|
||||
if Input.is_action_just_pressed("ui_cancel"):
|
||||
hud.reset_curve_bar()
|
||||
curve_animation.stop()
|
||||
phase = Phase.POWER_ADJUST
|
||||
if Input.is_action_just_pressed("shot_accept"):
|
||||
hud.stop_curve_bar()
|
||||
curve_animation.pause()
|
||||
phase = Phase.DOWNSWING
|
||||
Phase.SHOT:
|
||||
if driving_range and Input.is_action_just_pressed("shot_accept"):
|
||||
phase = Phase.AIM
|
||||
return_ball()
|
||||
Phase.FINISHED:
|
||||
if _restart_queued:
|
||||
_restart_queued = false
|
||||
phase = Phase.AIM
|
||||
# TODO: hand over to next player where appropriate
|
||||
phase = Phase.AIM
|
||||
|
||||
|
||||
func _on_physics_ball_sleeping_state_changed() -> void:
|
||||
|
@ -484,8 +452,7 @@ func _on_ball_entered_water() -> void:
|
|||
# Should only be possible during SHOT phase, but let's check just to be sure...
|
||||
if phase == Phase.SHOT:
|
||||
physics_ball.freeze = true
|
||||
hud.play_wasted_animation()
|
||||
ball_return_timer.start(WASTED_BALL_RETURN_DELAY)
|
||||
wasted_animation.play("display")
|
||||
|
||||
|
||||
func _on_physics_ball_body_entered(_body: Node) -> void:
|
||||
|
@ -499,14 +466,3 @@ func _on_physics_ball_body_entered(_body: Node) -> void:
|
|||
add_sibling(_free_camera)
|
||||
control_disabled = true
|
||||
camera.current = false
|
||||
|
||||
|
||||
func _on_ball_return_timer_timeout() -> void:
|
||||
return_ball()
|
||||
|
||||
|
||||
## Create a new instance for the given player.
|
||||
static func create(_player: WorldPlayer) -> ShotSetup:
|
||||
var instance: ShotSetup = ShotSetup.scene.instantiate()
|
||||
instance.player = _player
|
||||
return instance
|
||||
|
|
|
@ -1,11 +1,13 @@
|
|||
[gd_scene load_steps=17 format=3 uid="uid://cy7t2tc4y3b4"]
|
||||
[gd_scene load_steps=42 format=3 uid="uid://cy7t2tc4y3b4"]
|
||||
|
||||
[ext_resource type="Script" path="res://src/player/shot_setup/shot_setup.gd" id="1_r6ei4"]
|
||||
[ext_resource type="PackedScene" uid="uid://dfttci386ohip" path="res://src/equipment/balls/physics_ball/physics_ball.tscn" id="2_1i5j5"]
|
||||
[ext_resource type="PackedScene" uid="uid://c2k88ns0h5ie1" path="res://src/ui/3d/arrow/arrow.tscn" id="2_s70wl"]
|
||||
[ext_resource type="PackedScene" uid="uid://dfttci386ohip" path="res://src/player/physics_ball/physics_ball.tscn" id="2_1i5j5"]
|
||||
[ext_resource type="PackedScene" uid="uid://c2k88ns0h5ie1" path="res://src/ui/arrow/arrow.tscn" id="2_s70wl"]
|
||||
[ext_resource type="PackedScene" uid="uid://1s3gywmoi20e" path="res://src/characters/player_characters/gfolf_girl/gfolf_girl.tscn" id="3_e4aur"]
|
||||
[ext_resource type="PackedScene" uid="uid://fht6j87o8ecr" path="res://src/ui/3d/projectile_arc/projectile_arc.tscn" id="4_ry2ho"]
|
||||
[ext_resource type="PackedScene" uid="uid://dbdul15c4oblg" path="res://src/ui/3d/projected_target.tscn" id="6_mynqj"]
|
||||
[ext_resource type="PackedScene" uid="uid://445qd7m4qe2j" path="res://src/player/shot_setup/club_selector/club_selector.tscn" id="4_56ape"]
|
||||
[ext_resource type="PackedScene" uid="uid://fht6j87o8ecr" path="res://src/ui/projectile_arc/projectile_arc.tscn" id="4_ry2ho"]
|
||||
[ext_resource type="PackedScene" uid="uid://dbdul15c4oblg" path="res://src/ui/projected_target.tscn" id="6_mynqj"]
|
||||
[ext_resource type="Shader" path="res://src/shaders/canvas_retro.gdshader" id="7_h6c4m"]
|
||||
|
||||
[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_lnol1"]
|
||||
albedo_color = Color(0, 0.537255, 1, 1)
|
||||
|
@ -203,6 +205,441 @@ _data = {
|
|||
"swing_delay": SubResource("Animation_u8k07")
|
||||
}
|
||||
|
||||
[sub_resource type="Animation" id="Animation_3xds6"]
|
||||
resource_name = "RESET"
|
||||
length = 0.001
|
||||
tracks/0/type = "value"
|
||||
tracks/0/imported = false
|
||||
tracks/0/enabled = true
|
||||
tracks/0/path = NodePath(".:visible")
|
||||
tracks/0/interp = 1
|
||||
tracks/0/loop_wrap = true
|
||||
tracks/0/keys = {
|
||||
"times": PackedFloat32Array(0),
|
||||
"transitions": PackedFloat32Array(1),
|
||||
"update": 1,
|
||||
"values": [false]
|
||||
}
|
||||
tracks/1/type = "value"
|
||||
tracks/1/imported = false
|
||||
tracks/1/enabled = true
|
||||
tracks/1/path = NodePath(".:visible_ratio")
|
||||
tracks/1/interp = 1
|
||||
tracks/1/loop_wrap = true
|
||||
tracks/1/keys = {
|
||||
"times": PackedFloat32Array(0),
|
||||
"transitions": PackedFloat32Array(1),
|
||||
"update": 0,
|
||||
"values": [1.0]
|
||||
}
|
||||
tracks/2/type = "value"
|
||||
tracks/2/imported = false
|
||||
tracks/2/enabled = true
|
||||
tracks/2/path = NodePath(".:rotation")
|
||||
tracks/2/interp = 3
|
||||
tracks/2/loop_wrap = true
|
||||
tracks/2/keys = {
|
||||
"times": PackedFloat32Array(0),
|
||||
"transitions": PackedFloat32Array(1),
|
||||
"update": 0,
|
||||
"values": [7.31612e-10]
|
||||
}
|
||||
tracks/3/type = "bezier"
|
||||
tracks/3/imported = false
|
||||
tracks/3/enabled = true
|
||||
tracks/3/path = NodePath(".:anchor_top")
|
||||
tracks/3/interp = 1
|
||||
tracks/3/loop_wrap = true
|
||||
tracks/3/keys = {
|
||||
"handle_modes": PackedInt32Array(0),
|
||||
"points": PackedFloat32Array(0.5, -0.25, 0, 0.25, 0),
|
||||
"times": PackedFloat32Array(0)
|
||||
}
|
||||
tracks/4/type = "bezier"
|
||||
tracks/4/imported = false
|
||||
tracks/4/enabled = true
|
||||
tracks/4/path = NodePath(".:anchor_bottom")
|
||||
tracks/4/interp = 1
|
||||
tracks/4/loop_wrap = true
|
||||
tracks/4/keys = {
|
||||
"handle_modes": PackedInt32Array(0),
|
||||
"points": PackedFloat32Array(0.5, -0.25, 0, 0.25, 0),
|
||||
"times": PackedFloat32Array(0)
|
||||
}
|
||||
|
||||
[sub_resource type="Animation" id="Animation_cwotn"]
|
||||
resource_name = "display"
|
||||
length = 2.4
|
||||
step = 0.1
|
||||
tracks/0/type = "value"
|
||||
tracks/0/imported = false
|
||||
tracks/0/enabled = true
|
||||
tracks/0/path = NodePath(".:visible")
|
||||
tracks/0/interp = 1
|
||||
tracks/0/loop_wrap = true
|
||||
tracks/0/keys = {
|
||||
"times": PackedFloat32Array(0, 2.4),
|
||||
"transitions": PackedFloat32Array(1, 1),
|
||||
"update": 1,
|
||||
"values": [true, false]
|
||||
}
|
||||
tracks/1/type = "value"
|
||||
tracks/1/imported = false
|
||||
tracks/1/enabled = true
|
||||
tracks/1/path = NodePath(".:visible_ratio")
|
||||
tracks/1/interp = 1
|
||||
tracks/1/loop_wrap = true
|
||||
tracks/1/keys = {
|
||||
"times": PackedFloat32Array(0, 0.2),
|
||||
"transitions": PackedFloat32Array(1, 1),
|
||||
"update": 0,
|
||||
"values": [0.0, 1.0]
|
||||
}
|
||||
tracks/2/type = "value"
|
||||
tracks/2/imported = false
|
||||
tracks/2/enabled = true
|
||||
tracks/2/path = NodePath(".:rotation")
|
||||
tracks/2/interp = 3
|
||||
tracks/2/loop_wrap = true
|
||||
tracks/2/keys = {
|
||||
"times": PackedFloat32Array(0, 2, 2.4),
|
||||
"transitions": PackedFloat32Array(1, 1, 1),
|
||||
"update": 0,
|
||||
"values": [7.31612e-10, 7.31612e-10, 0.174533]
|
||||
}
|
||||
tracks/3/type = "bezier"
|
||||
tracks/3/imported = false
|
||||
tracks/3/enabled = true
|
||||
tracks/3/path = NodePath(".:anchor_top")
|
||||
tracks/3/interp = 1
|
||||
tracks/3/loop_wrap = true
|
||||
tracks/3/keys = {
|
||||
"handle_modes": PackedInt32Array(0, 0, 0),
|
||||
"points": PackedFloat32Array(0.5, -0.25, 0, 0.5, -0.0548477, 0.5, -0.4, 0.0593877, 0.233401, -0.456136, 1.25, -0.25, 0, 0.25, 0),
|
||||
"times": PackedFloat32Array(0, 2, 2.4)
|
||||
}
|
||||
tracks/4/type = "bezier"
|
||||
tracks/4/imported = false
|
||||
tracks/4/enabled = true
|
||||
tracks/4/path = NodePath(".:anchor_bottom")
|
||||
tracks/4/interp = 1
|
||||
tracks/4/loop_wrap = true
|
||||
tracks/4/keys = {
|
||||
"handle_modes": PackedInt32Array(0, 0, 0),
|
||||
"points": PackedFloat32Array(0.5, -0.25, 0, 0.5, -0.055, 0.5, -0.4, 0.059, 0.233, -0.456, 1.25, -0.25, 0, 0.25, 0),
|
||||
"times": PackedFloat32Array(0, 2, 2.4)
|
||||
}
|
||||
|
||||
[sub_resource type="AnimationLibrary" id="AnimationLibrary_2a0gn"]
|
||||
_data = {
|
||||
"RESET": SubResource("Animation_3xds6"),
|
||||
"display": SubResource("Animation_cwotn")
|
||||
}
|
||||
|
||||
[sub_resource type="Animation" id="Animation_2gt87"]
|
||||
resource_name = "RESET"
|
||||
length = 0.001
|
||||
tracks/0/type = "value"
|
||||
tracks/0/imported = false
|
||||
tracks/0/enabled = true
|
||||
tracks/0/path = NodePath(".:visible")
|
||||
tracks/0/interp = 1
|
||||
tracks/0/loop_wrap = true
|
||||
tracks/0/keys = {
|
||||
"times": PackedFloat32Array(0),
|
||||
"transitions": PackedFloat32Array(1),
|
||||
"update": 1,
|
||||
"values": [false]
|
||||
}
|
||||
tracks/1/type = "value"
|
||||
tracks/1/imported = false
|
||||
tracks/1/enabled = true
|
||||
tracks/1/path = NodePath(".:visible_ratio")
|
||||
tracks/1/interp = 1
|
||||
tracks/1/loop_wrap = true
|
||||
tracks/1/keys = {
|
||||
"times": PackedFloat32Array(0),
|
||||
"transitions": PackedFloat32Array(1),
|
||||
"update": 0,
|
||||
"values": [1.0]
|
||||
}
|
||||
tracks/2/type = "value"
|
||||
tracks/2/imported = false
|
||||
tracks/2/enabled = true
|
||||
tracks/2/path = NodePath(".:rotation")
|
||||
tracks/2/interp = 3
|
||||
tracks/2/loop_wrap = true
|
||||
tracks/2/keys = {
|
||||
"times": PackedFloat32Array(0),
|
||||
"transitions": PackedFloat32Array(1),
|
||||
"update": 0,
|
||||
"values": [7.31612e-10]
|
||||
}
|
||||
tracks/3/type = "bezier"
|
||||
tracks/3/imported = false
|
||||
tracks/3/enabled = true
|
||||
tracks/3/path = NodePath(".:anchor_top")
|
||||
tracks/3/interp = 1
|
||||
tracks/3/loop_wrap = true
|
||||
tracks/3/keys = {
|
||||
"handle_modes": PackedInt32Array(0),
|
||||
"points": PackedFloat32Array(0.5, -0.25, 0, 0.25, 0),
|
||||
"times": PackedFloat32Array(0)
|
||||
}
|
||||
tracks/4/type = "bezier"
|
||||
tracks/4/imported = false
|
||||
tracks/4/enabled = true
|
||||
tracks/4/path = NodePath(".:anchor_bottom")
|
||||
tracks/4/interp = 1
|
||||
tracks/4/loop_wrap = true
|
||||
tracks/4/keys = {
|
||||
"handle_modes": PackedInt32Array(0),
|
||||
"points": PackedFloat32Array(0.5, -0.25, 0, 0.25, 0),
|
||||
"times": PackedFloat32Array(0)
|
||||
}
|
||||
|
||||
[sub_resource type="Animation" id="Animation_g52q7"]
|
||||
resource_name = "display"
|
||||
length = 3.4
|
||||
step = 0.1
|
||||
tracks/0/type = "value"
|
||||
tracks/0/imported = false
|
||||
tracks/0/enabled = true
|
||||
tracks/0/path = NodePath(".:visible")
|
||||
tracks/0/interp = 1
|
||||
tracks/0/loop_wrap = true
|
||||
tracks/0/keys = {
|
||||
"times": PackedFloat32Array(0, 3.4),
|
||||
"transitions": PackedFloat32Array(1, 1),
|
||||
"update": 1,
|
||||
"values": [true, false]
|
||||
}
|
||||
tracks/1/type = "value"
|
||||
tracks/1/imported = false
|
||||
tracks/1/enabled = true
|
||||
tracks/1/path = NodePath(".:visible_ratio")
|
||||
tracks/1/interp = 1
|
||||
tracks/1/loop_wrap = true
|
||||
tracks/1/keys = {
|
||||
"times": PackedFloat32Array(0, 0.4),
|
||||
"transitions": PackedFloat32Array(1, 1),
|
||||
"update": 0,
|
||||
"values": [0.0, 1.0]
|
||||
}
|
||||
tracks/2/type = "value"
|
||||
tracks/2/imported = false
|
||||
tracks/2/enabled = true
|
||||
tracks/2/path = NodePath(".:rotation")
|
||||
tracks/2/interp = 3
|
||||
tracks/2/loop_wrap = true
|
||||
tracks/2/keys = {
|
||||
"times": PackedFloat32Array(0, 3, 3.4),
|
||||
"transitions": PackedFloat32Array(1, 0.618, 1),
|
||||
"update": 0,
|
||||
"values": [7.31612e-10, 7.31612e-10, -2.96706]
|
||||
}
|
||||
tracks/3/type = "bezier"
|
||||
tracks/3/imported = false
|
||||
tracks/3/enabled = true
|
||||
tracks/3/path = NodePath(".:anchor_top")
|
||||
tracks/3/interp = 1
|
||||
tracks/3/loop_wrap = true
|
||||
tracks/3/keys = {
|
||||
"handle_modes": PackedInt32Array(0, 0, 0),
|
||||
"points": PackedFloat32Array(0.5, -0.25, 0, 0.5, -0.0548477, 0.5, -0.4, 0.0593877, 0.233401, -0.456136, 1.25, -0.25, 0, 0.25, 0),
|
||||
"times": PackedFloat32Array(0, 3, 3.4)
|
||||
}
|
||||
tracks/4/type = "bezier"
|
||||
tracks/4/imported = false
|
||||
tracks/4/enabled = true
|
||||
tracks/4/path = NodePath(".:anchor_bottom")
|
||||
tracks/4/interp = 1
|
||||
tracks/4/loop_wrap = true
|
||||
tracks/4/keys = {
|
||||
"handle_modes": PackedInt32Array(0, 0, 0),
|
||||
"points": PackedFloat32Array(0.5, -0.25, 0, 0.5, -0.055, 0.5, -0.4, 0.059, 0.233, -0.456, 1.25, -0.25, 0, 0.25, 0),
|
||||
"times": PackedFloat32Array(0, 3, 3.4)
|
||||
}
|
||||
tracks/5/type = "method"
|
||||
tracks/5/imported = false
|
||||
tracks/5/enabled = true
|
||||
tracks/5/path = NodePath("../..")
|
||||
tracks/5/interp = 1
|
||||
tracks/5/loop_wrap = true
|
||||
tracks/5/keys = {
|
||||
"times": PackedFloat32Array(3.4),
|
||||
"transitions": PackedFloat32Array(1),
|
||||
"values": [{
|
||||
"args": [],
|
||||
"method": &"return_ball"
|
||||
}]
|
||||
}
|
||||
|
||||
[sub_resource type="AnimationLibrary" id="AnimationLibrary_gbnnr"]
|
||||
_data = {
|
||||
"RESET": SubResource("Animation_2gt87"),
|
||||
"display": SubResource("Animation_g52q7")
|
||||
}
|
||||
|
||||
[sub_resource type="ShaderMaterial" id="ShaderMaterial_afsun"]
|
||||
shader = ExtResource("7_h6c4m")
|
||||
shader_parameter/change_color_depth = true
|
||||
shader_parameter/target_color_depth = 3
|
||||
shader_parameter/dithering = true
|
||||
shader_parameter/scale_resolution = true
|
||||
shader_parameter/target_resolution_scale = 3
|
||||
shader_parameter/enable_recolor = false
|
||||
|
||||
[sub_resource type="Animation" id="Animation_pk1s7"]
|
||||
length = 0.001
|
||||
tracks/0/type = "bezier"
|
||||
tracks/0/imported = false
|
||||
tracks/0/enabled = true
|
||||
tracks/0/path = NodePath(".:value")
|
||||
tracks/0/interp = 1
|
||||
tracks/0/loop_wrap = true
|
||||
tracks/0/keys = {
|
||||
"handle_modes": PackedInt32Array(0),
|
||||
"points": PackedFloat32Array(0, -0.25, 0, 0.25, 0),
|
||||
"times": PackedFloat32Array(0)
|
||||
}
|
||||
|
||||
[sub_resource type="Animation" id="Animation_67gmp"]
|
||||
resource_name = "fill"
|
||||
length = 1.618
|
||||
tracks/0/type = "bezier"
|
||||
tracks/0/imported = false
|
||||
tracks/0/enabled = true
|
||||
tracks/0/path = NodePath(".:value")
|
||||
tracks/0/interp = 1
|
||||
tracks/0/loop_wrap = true
|
||||
tracks/0/keys = {
|
||||
"handle_modes": PackedInt32Array(0, 0),
|
||||
"points": PackedFloat32Array(0, -0.25, 0, 0.233333, 0.0884774, 1, -0.267469, -0.483539, 0.25, 0),
|
||||
"times": PackedFloat32Array(0, 1.618)
|
||||
}
|
||||
|
||||
[sub_resource type="AnimationLibrary" id="AnimationLibrary_coah5"]
|
||||
_data = {
|
||||
"RESET": SubResource("Animation_pk1s7"),
|
||||
"fill": SubResource("Animation_67gmp")
|
||||
}
|
||||
|
||||
[sub_resource type="Animation" id="Animation_noa0w"]
|
||||
length = 0.001
|
||||
tracks/0/type = "bezier"
|
||||
tracks/0/imported = false
|
||||
tracks/0/enabled = true
|
||||
tracks/0/path = NodePath("%CurveBar:value")
|
||||
tracks/0/interp = 1
|
||||
tracks/0/loop_wrap = true
|
||||
tracks/0/keys = {
|
||||
"handle_modes": PackedInt32Array(0),
|
||||
"points": PackedFloat32Array(0, -0.25, 0, 0.25, 0),
|
||||
"times": PackedFloat32Array(0)
|
||||
}
|
||||
|
||||
[sub_resource type="Animation" id="Animation_uo6s7"]
|
||||
resource_name = "fill"
|
||||
length = 0.618
|
||||
loop_mode = 2
|
||||
tracks/0/type = "bezier"
|
||||
tracks/0/imported = false
|
||||
tracks/0/enabled = true
|
||||
tracks/0/path = NodePath("%CurveBar:value")
|
||||
tracks/0/interp = 1
|
||||
tracks/0/loop_wrap = true
|
||||
tracks/0/keys = {
|
||||
"handle_modes": PackedInt32Array(0, 0),
|
||||
"points": PackedFloat32Array(-1, -0.25, 0, 0.3, 0, 1, -0.3, 0, 0.25, 0),
|
||||
"times": PackedFloat32Array(0, 0.618)
|
||||
}
|
||||
|
||||
[sub_resource type="AnimationLibrary" id="AnimationLibrary_dicse"]
|
||||
_data = {
|
||||
"RESET": SubResource("Animation_noa0w"),
|
||||
"fill": SubResource("Animation_uo6s7")
|
||||
}
|
||||
|
||||
[sub_resource type="Animation" id="Animation_3cn2c"]
|
||||
length = 0.001
|
||||
tracks/0/type = "value"
|
||||
tracks/0/imported = false
|
||||
tracks/0/enabled = true
|
||||
tracks/0/path = NodePath("%ClubSelector:rotation")
|
||||
tracks/0/interp = 4
|
||||
tracks/0/loop_wrap = true
|
||||
tracks/0/keys = {
|
||||
"times": PackedFloat32Array(0),
|
||||
"transitions": PackedFloat32Array(1),
|
||||
"update": 0,
|
||||
"values": [0.0]
|
||||
}
|
||||
|
||||
[sub_resource type="Animation" id="Animation_dt1yq"]
|
||||
resource_name = "hide"
|
||||
length = 0.4
|
||||
step = 0.02
|
||||
tracks/0/type = "value"
|
||||
tracks/0/imported = false
|
||||
tracks/0/enabled = true
|
||||
tracks/0/path = NodePath("%ClubSelector:rotation")
|
||||
tracks/0/interp = 4
|
||||
tracks/0/loop_wrap = true
|
||||
tracks/0/keys = {
|
||||
"times": PackedFloat32Array(0, 0.1, 0.4),
|
||||
"transitions": PackedFloat32Array(1.618, 1.618, 1),
|
||||
"update": 0,
|
||||
"values": [0.0, 0.0872665, -1.5708]
|
||||
}
|
||||
|
||||
[sub_resource type="Animation" id="Animation_0maif"]
|
||||
resource_name = "show"
|
||||
length = 0.4
|
||||
step = 0.02
|
||||
tracks/0/type = "value"
|
||||
tracks/0/imported = false
|
||||
tracks/0/enabled = true
|
||||
tracks/0/path = NodePath("%ClubSelector:rotation")
|
||||
tracks/0/interp = 4
|
||||
tracks/0/loop_wrap = true
|
||||
tracks/0/keys = {
|
||||
"times": PackedFloat32Array(0, 0.34, 0.4),
|
||||
"transitions": PackedFloat32Array(1.618, 1.618, 1),
|
||||
"update": 0,
|
||||
"values": [-1.5708, 0.0872665, 0.0]
|
||||
}
|
||||
|
||||
[sub_resource type="AnimationLibrary" id="AnimationLibrary_c3i4w"]
|
||||
_data = {
|
||||
"RESET": SubResource("Animation_3cn2c"),
|
||||
"hide": SubResource("Animation_dt1yq"),
|
||||
"show": SubResource("Animation_0maif")
|
||||
}
|
||||
|
||||
[sub_resource type="AnimationNodeAnimation" id="AnimationNodeAnimation_8uxnp"]
|
||||
animation = &"hide"
|
||||
|
||||
[sub_resource type="AnimationNodeAnimation" id="AnimationNodeAnimation_v05eu"]
|
||||
animation = &"show"
|
||||
|
||||
[sub_resource type="AnimationNodeStateMachineTransition" id="AnimationNodeStateMachineTransition_28a4x"]
|
||||
advance_mode = 2
|
||||
|
||||
[sub_resource type="AnimationNodeStateMachineTransition" id="AnimationNodeStateMachineTransition_bj7v0"]
|
||||
switch_mode = 2
|
||||
|
||||
[sub_resource type="AnimationNodeStateMachineTransition" id="AnimationNodeStateMachineTransition_xpwgd"]
|
||||
switch_mode = 2
|
||||
|
||||
[sub_resource type="AnimationNodeStateMachine" id="AnimationNodeStateMachine_osrbp"]
|
||||
states/hidden/node = SubResource("AnimationNodeAnimation_8uxnp")
|
||||
states/hidden/position = Vector2(592, 100)
|
||||
states/visible/node = SubResource("AnimationNodeAnimation_v05eu")
|
||||
states/visible/position = Vector2(364, 100)
|
||||
transitions = ["Start", "visible", SubResource("AnimationNodeStateMachineTransition_28a4x"), "visible", "hidden", SubResource("AnimationNodeStateMachineTransition_bj7v0"), "hidden", "visible", SubResource("AnimationNodeStateMachineTransition_xpwgd")]
|
||||
graph_offset = Vector2(-309, -132)
|
||||
|
||||
[node name="ShotSetup" type="Node3D"]
|
||||
script = ExtResource("1_r6ei4")
|
||||
|
||||
|
@ -356,11 +793,187 @@ libraries = {
|
|||
"": SubResource("AnimationLibrary_u78hq")
|
||||
}
|
||||
|
||||
[node name="BallReturnTimer" type="Timer" parent="."]
|
||||
[node name="ShotUI" type="Control" parent="."]
|
||||
top_level = true
|
||||
layout_mode = 3
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
mouse_filter = 1
|
||||
|
||||
[node name="WoahNiceFeedback" type="RichTextLabel" parent="ShotUI"]
|
||||
visible = false
|
||||
custom_minimum_size = Vector2(1200, 0)
|
||||
layout_mode = 1
|
||||
anchors_preset = -1
|
||||
anchor_left = 0.5
|
||||
anchor_top = 0.5
|
||||
anchor_right = 0.5
|
||||
anchor_bottom = 0.5
|
||||
offset_left = -350.0
|
||||
offset_top = -66.0
|
||||
offset_right = 350.0
|
||||
offset_bottom = 66.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
mouse_filter = 2
|
||||
theme_type_variation = &"ShotFeedback"
|
||||
bbcode_enabled = true
|
||||
text = "[center][wave amp=64][rainbow]woah nice[/rainbow][/wave][/center]"
|
||||
fit_content = true
|
||||
autowrap_mode = 0
|
||||
visible_characters_behavior = 1
|
||||
|
||||
[node name="NiceAnimation" type="AnimationPlayer" parent="ShotUI/WoahNiceFeedback"]
|
||||
unique_name_in_owner = true
|
||||
one_shot = true
|
||||
libraries = {
|
||||
"": SubResource("AnimationLibrary_2a0gn")
|
||||
}
|
||||
|
||||
[node name="WastedFeedback" type="RichTextLabel" parent="ShotUI"]
|
||||
visible = false
|
||||
custom_minimum_size = Vector2(1400, 0)
|
||||
layout_mode = 1
|
||||
anchors_preset = -1
|
||||
anchor_left = 0.5
|
||||
anchor_top = 0.5
|
||||
anchor_right = 0.5
|
||||
anchor_bottom = 0.5
|
||||
offset_left = -350.0
|
||||
offset_top = -66.0
|
||||
offset_right = 350.0
|
||||
offset_bottom = 66.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
pivot_offset = Vector2(100, 115)
|
||||
mouse_filter = 2
|
||||
theme_type_variation = &"ShotFeedback"
|
||||
bbcode_enabled = true
|
||||
text = "[center][tornado radius=16][color=powder_blue]wasted[/color][/tornado][/center]"
|
||||
fit_content = true
|
||||
autowrap_mode = 0
|
||||
visible_characters_behavior = 1
|
||||
|
||||
[node name="WastedAnimation" type="AnimationPlayer" parent="ShotUI/WastedFeedback"]
|
||||
unique_name_in_owner = true
|
||||
libraries = {
|
||||
"": SubResource("AnimationLibrary_gbnnr")
|
||||
}
|
||||
|
||||
[node name="ColorRect" type="ColorRect" parent="ShotUI"]
|
||||
visible = false
|
||||
material = SubResource("ShaderMaterial_afsun")
|
||||
layout_mode = 1
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
mouse_filter = 2
|
||||
|
||||
[node name="ShotGauges" type="Control" parent="ShotUI"]
|
||||
layout_mode = 1
|
||||
anchor_left = 0.4
|
||||
anchor_top = 0.3
|
||||
anchor_right = 0.6
|
||||
anchor_bottom = 0.85
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
mouse_filter = 1
|
||||
|
||||
[node name="PowerGauge" type="Control" parent="ShotUI/ShotGauges"]
|
||||
layout_mode = 1
|
||||
anchor_left = 0.5
|
||||
anchor_top = 0.382
|
||||
anchor_right = 0.5
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
mouse_filter = 1
|
||||
|
||||
[node name="PowerBar" type="ProgressBar" parent="ShotUI/ShotGauges/PowerGauge"]
|
||||
unique_name_in_owner = true
|
||||
visible = false
|
||||
custom_minimum_size = Vector2(30, 0)
|
||||
layout_mode = 1
|
||||
anchors_preset = 13
|
||||
anchor_left = 0.5
|
||||
anchor_right = 0.5
|
||||
anchor_bottom = 1.0
|
||||
offset_left = -2.0
|
||||
offset_right = 2.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
mouse_filter = 1
|
||||
max_value = 1.0
|
||||
fill_mode = 2
|
||||
|
||||
[node name="PowerAnimation" type="AnimationPlayer" parent="ShotUI/ShotGauges/PowerGauge"]
|
||||
unique_name_in_owner = true
|
||||
root_node = NodePath("../PowerBar")
|
||||
libraries = {
|
||||
"": SubResource("AnimationLibrary_coah5")
|
||||
}
|
||||
|
||||
[node name="CurveGauge" type="Control" parent="ShotUI/ShotGauges"]
|
||||
layout_mode = 1
|
||||
anchor_top = 0.25
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 0.25
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
mouse_filter = 1
|
||||
|
||||
[node name="CurveBar" type="ProgressBar" parent="ShotUI/ShotGauges/CurveGauge"]
|
||||
unique_name_in_owner = true
|
||||
visible = false
|
||||
layout_mode = 1
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
offset_top = -13.5
|
||||
offset_bottom = 13.5
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
min_value = -1.0
|
||||
max_value = 1.0
|
||||
|
||||
[node name="CurveAnimation" type="AnimationPlayer" parent="ShotUI/ShotGauges/CurveGauge"]
|
||||
unique_name_in_owner = true
|
||||
libraries = {
|
||||
"": SubResource("AnimationLibrary_dicse")
|
||||
}
|
||||
|
||||
[node name="GaugeFlasher" type="AnimationPlayer" parent="ShotUI/ShotGauges"]
|
||||
unique_name_in_owner = true
|
||||
|
||||
[node name="ClubSelector" parent="ShotUI" instance=ExtResource("4_56ape")]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 1
|
||||
anchors_preset = 0
|
||||
anchor_right = 0.0
|
||||
anchor_bottom = 0.0
|
||||
offset_left = 50.0
|
||||
offset_top = 50.0
|
||||
offset_right = 50.0
|
||||
offset_bottom = 50.0
|
||||
grow_horizontal = 1
|
||||
grow_vertical = 1
|
||||
pivot_offset = Vector2(-400, 0)
|
||||
|
||||
[node name="HUDAnimation" type="AnimationPlayer" parent="ShotUI"]
|
||||
libraries = {
|
||||
"": SubResource("AnimationLibrary_c3i4w")
|
||||
}
|
||||
|
||||
[node name="HUDStateMachine" type="AnimationTree" parent="ShotUI"]
|
||||
unique_name_in_owner = true
|
||||
root_node = NodePath("%HUDStateMachine/..")
|
||||
tree_root = SubResource("AnimationNodeStateMachine_osrbp")
|
||||
anim_player = NodePath("../HUDAnimation")
|
||||
|
||||
[connection signal="body_entered" from="BallPoint/PhysicsBall" to="." method="_on_physics_ball_body_entered"]
|
||||
[connection signal="entered_water" from="BallPoint/PhysicsBall" to="." method="_on_ball_entered_water"]
|
||||
[connection signal="sleeping_state_changed" from="BallPoint/PhysicsBall" to="." method="_on_physics_ball_sleeping_state_changed"]
|
||||
[connection signal="timeout" from="BallReturnTimer" to="." method="_on_ball_return_timer_timeout"]
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[gd_scene load_steps=2 format=3 uid="uid://ceu57tasi270s"]
|
||||
|
||||
[ext_resource type="Script" path="res://src/ui/camera/tracking_camera/tracking_camera.gd" id="1_352mq"]
|
||||
[ext_resource type="Script" path="res://src/player/tracking_camera/tracking_camera.gd" id="1_352mq"]
|
||||
|
||||
[node name="TrackingCamera" type="Camera3D"]
|
||||
script = ExtResource("1_352mq")
|
|
@ -1,49 +0,0 @@
|
|||
class_name WorldPlayer extends Resource
|
||||
## Container for the state of the player within the world.
|
||||
|
||||
# TODO character select
|
||||
@export var name: String = "Gfolfer"
|
||||
|
||||
@export_category("Equipment")
|
||||
@export var driver: Club
|
||||
@export var iron: Club
|
||||
@export var wedge: Club
|
||||
@export var putter: Club
|
||||
@export var special: Club
|
||||
|
||||
# TODO balls, pickups, etc
|
||||
|
||||
var shot_setup: ShotSetup:
|
||||
get:
|
||||
if not shot_setup:
|
||||
shot_setup = ShotSetup.create(self)
|
||||
return shot_setup
|
||||
|
||||
|
||||
## Get the club equipped in the given slot.
|
||||
##
|
||||
## Returns `null` if the player has no club equipped in the given slot
|
||||
func get_club(type: Club.Type) -> Club:
|
||||
match type:
|
||||
Club.Type.DRIVER:
|
||||
return driver
|
||||
Club.Type.IRON:
|
||||
return iron
|
||||
Club.Type.WEDGE:
|
||||
return wedge
|
||||
Club.Type.PUTTER:
|
||||
return putter
|
||||
Club.Type.SPECIAL:
|
||||
return special
|
||||
return null
|
||||
|
||||
|
||||
## Create a debug player instance
|
||||
static func create_debug() -> WorldPlayer:
|
||||
var instance := WorldPlayer.new()
|
||||
instance.driver = Club.catalog.debug_driver
|
||||
instance.iron = Club.catalog.debug_iron
|
||||
instance.wedge = Club.catalog.debug_wedge
|
||||
instance.putter = Club.catalog.debug_wedge
|
||||
|
||||
return instance
|
|
@ -1,14 +1,14 @@
|
|||
@tool
|
||||
extends Node3D
|
||||
|
||||
const COLOR_PARAMETER := "albedo"
|
||||
|
||||
enum Loop {
|
||||
RESET,
|
||||
BOB_TIP,
|
||||
BOB_TAIL,
|
||||
}
|
||||
|
||||
const COLOR_PARAMETER := "albedo"
|
||||
|
||||
@export var loop_animation: Loop:
|
||||
set(value):
|
||||
if is_instance_valid(animation_player):
|
|
@ -1,7 +1,7 @@
|
|||
[gd_scene load_steps=9 format=3 uid="uid://c2k88ns0h5ie1"]
|
||||
|
||||
[ext_resource type="PackedScene" uid="uid://bw54bi35myvpg" path="res://assets/models/ui/arrow/arrow.glb" id="1_xifl6"]
|
||||
[ext_resource type="Script" path="res://src/ui/3d/arrow/arrow.gd" id="2_0viam"]
|
||||
[ext_resource type="Script" path="res://src/ui/arrow/arrow.gd" id="2_0viam"]
|
||||
[ext_resource type="Material" uid="uid://d3ka0yjhxu5ks" path="res://assets/materials/gummy_material.tres" id="3_g7vwe"]
|
||||
|
||||
[sub_resource type="Animation" id="Animation_hsqkd"]
|
|
@ -13,10 +13,6 @@ ClubSelectLabel/constants/shadow_offset_x = 6
|
|||
ClubSelectLabel/constants/shadow_offset_y = 0
|
||||
ClubSelectLabel/font_sizes/font_size = 84
|
||||
ClubSelectLabel/fonts/font = ExtResource("1_3rv2b")
|
||||
ClubSelectLabelDisabled/base_type = &"ClubSelectLabel"
|
||||
ClubSelectLabelDisabled/colors/font_color = Color(0.4, 0.3848, 0.376, 1)
|
||||
ClubSelectLabelDisabled/colors/font_outline_color = Color(0.2, 0.2, 0.2, 1)
|
||||
ClubSelectLabelDisabled/colors/font_shadow_color = Color(0.2, 0.2, 0.2, 1)
|
||||
ShotFeedback/base_type = &"RichTextLabel"
|
||||
ShotFeedback/colors/font_shadow_color = Color(0, 0, 0, 1)
|
||||
ShotFeedback/constants/shadow_offset_x = 6
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[gd_scene load_steps=9 format=3 uid="uid://fht6j87o8ecr"]
|
||||
|
||||
[ext_resource type="Script" path="res://src/ui/3d/projectile_arc/projectile_arc.gd" id="1_vafqi"]
|
||||
[ext_resource type="Script" path="res://src/ui/projectile_arc/projectile_arc.gd" id="1_vafqi"]
|
||||
[ext_resource type="Shader" path="res://src/shaders/plasma.gdshader" id="2_pi36v"]
|
||||
|
||||
[sub_resource type="Curve3D" id="Curve3D_rfv3d"]
|
|
@ -1,8 +0,0 @@
|
|||
class_name ClubSelectorLabel extends Label
|
||||
|
||||
const ENABLED_THEME_TYPE := "ClubSelectLabel"
|
||||
const DISABLED_THEME_TYPE := "ClubSelectLabelDisabled"
|
||||
|
||||
|
||||
func set_enabled(is_enabled: bool) -> void:
|
||||
theme_type_variation = ENABLED_THEME_TYPE if is_enabled else DISABLED_THEME_TYPE
|
|
@ -1,66 +0,0 @@
|
|||
class_name ShotHUD extends Control
|
||||
## HUD for main gameplay loop
|
||||
|
||||
@onready var power_bar: ProgressBar = %PowerBar
|
||||
@onready var curve_bar: ProgressBar = %CurveBar
|
||||
|
||||
@onready var club_selector: ClubSelector = %ClubSelector
|
||||
|
||||
@onready var hud_state_machine: AnimationTree = %HUDStateMachine
|
||||
|
||||
@onready var _curve_animation: AnimationPlayer = %CurveAnimation
|
||||
@onready var _power_animation: AnimationPlayer = %PowerAnimation
|
||||
|
||||
@onready var _nice_animation: AnimationPlayer = %NiceAnimation
|
||||
@onready var _wasted_animation: AnimationPlayer = %WastedAnimation
|
||||
|
||||
@onready var _state: AnimationNodeStateMachinePlayback = hud_state_machine["parameters/playback"]
|
||||
|
||||
|
||||
## Set any HUD state specific to the player.
|
||||
func set_state_for_player(player: WorldPlayer) -> void:
|
||||
print_debug("Setting HUD for player ", player.name)
|
||||
club_selector.set_state_for_player(player)
|
||||
# TODO life
|
||||
# TODO special equipment
|
||||
# TODO abilities
|
||||
|
||||
|
||||
func show_hud() -> void:
|
||||
_state.travel("visible")
|
||||
|
||||
|
||||
func hide_hud() -> void:
|
||||
_state.travel("hidden")
|
||||
|
||||
|
||||
func start_power_bar() -> void:
|
||||
_power_animation.play("fill")
|
||||
|
||||
|
||||
func stop_power_bar() -> void:
|
||||
_power_animation.pause()
|
||||
|
||||
|
||||
func reset_power_bar() -> void:
|
||||
_power_animation.stop()
|
||||
|
||||
|
||||
func start_curve_bar() -> void:
|
||||
_curve_animation.play("fill")
|
||||
|
||||
|
||||
func stop_curve_bar() -> void:
|
||||
_curve_animation.pause()
|
||||
|
||||
|
||||
func reset_curve_bar() -> void:
|
||||
_curve_animation.stop()
|
||||
|
||||
|
||||
func play_nice_animation() -> void:
|
||||
_nice_animation.play("display")
|
||||
|
||||
|
||||
func play_wasted_animation() -> void:
|
||||
_wasted_animation.play("display")
|
|
@ -1,605 +0,0 @@
|
|||
[gd_scene load_steps=27 format=3 uid="uid://c4ifdiohng830"]
|
||||
|
||||
[ext_resource type="Script" path="res://src/ui/shot_hud/shot_hud.gd" id="1_x5b4c"]
|
||||
[ext_resource type="Shader" path="res://src/shaders/canvas_retro.gdshader" id="1_ybxxp"]
|
||||
[ext_resource type="PackedScene" uid="uid://445qd7m4qe2j" path="res://src/ui/shot_hud/club_selector/club_selector.tscn" id="2_1hdub"]
|
||||
|
||||
[sub_resource type="Animation" id="Animation_3xds6"]
|
||||
resource_name = "RESET"
|
||||
length = 0.001
|
||||
tracks/0/type = "value"
|
||||
tracks/0/imported = false
|
||||
tracks/0/enabled = true
|
||||
tracks/0/path = NodePath(".:visible")
|
||||
tracks/0/interp = 1
|
||||
tracks/0/loop_wrap = true
|
||||
tracks/0/keys = {
|
||||
"times": PackedFloat32Array(0),
|
||||
"transitions": PackedFloat32Array(1),
|
||||
"update": 1,
|
||||
"values": [false]
|
||||
}
|
||||
tracks/1/type = "value"
|
||||
tracks/1/imported = false
|
||||
tracks/1/enabled = true
|
||||
tracks/1/path = NodePath(".:visible_ratio")
|
||||
tracks/1/interp = 1
|
||||
tracks/1/loop_wrap = true
|
||||
tracks/1/keys = {
|
||||
"times": PackedFloat32Array(0),
|
||||
"transitions": PackedFloat32Array(1),
|
||||
"update": 0,
|
||||
"values": [1.0]
|
||||
}
|
||||
tracks/2/type = "value"
|
||||
tracks/2/imported = false
|
||||
tracks/2/enabled = true
|
||||
tracks/2/path = NodePath(".:rotation")
|
||||
tracks/2/interp = 3
|
||||
tracks/2/loop_wrap = true
|
||||
tracks/2/keys = {
|
||||
"times": PackedFloat32Array(0),
|
||||
"transitions": PackedFloat32Array(1),
|
||||
"update": 0,
|
||||
"values": [7.31612e-10]
|
||||
}
|
||||
tracks/3/type = "bezier"
|
||||
tracks/3/imported = false
|
||||
tracks/3/enabled = true
|
||||
tracks/3/path = NodePath(".:anchor_top")
|
||||
tracks/3/interp = 1
|
||||
tracks/3/loop_wrap = true
|
||||
tracks/3/keys = {
|
||||
"handle_modes": PackedInt32Array(0),
|
||||
"points": PackedFloat32Array(0.5, -0.25, 0, 0.25, 0),
|
||||
"times": PackedFloat32Array(0)
|
||||
}
|
||||
tracks/4/type = "bezier"
|
||||
tracks/4/imported = false
|
||||
tracks/4/enabled = true
|
||||
tracks/4/path = NodePath(".:anchor_bottom")
|
||||
tracks/4/interp = 1
|
||||
tracks/4/loop_wrap = true
|
||||
tracks/4/keys = {
|
||||
"handle_modes": PackedInt32Array(0),
|
||||
"points": PackedFloat32Array(0.5, -0.25, 0, 0.25, 0),
|
||||
"times": PackedFloat32Array(0)
|
||||
}
|
||||
|
||||
[sub_resource type="Animation" id="Animation_cwotn"]
|
||||
resource_name = "display"
|
||||
length = 2.4
|
||||
step = 0.1
|
||||
tracks/0/type = "value"
|
||||
tracks/0/imported = false
|
||||
tracks/0/enabled = true
|
||||
tracks/0/path = NodePath(".:visible")
|
||||
tracks/0/interp = 1
|
||||
tracks/0/loop_wrap = true
|
||||
tracks/0/keys = {
|
||||
"times": PackedFloat32Array(0, 2.4),
|
||||
"transitions": PackedFloat32Array(1, 1),
|
||||
"update": 1,
|
||||
"values": [true, false]
|
||||
}
|
||||
tracks/1/type = "value"
|
||||
tracks/1/imported = false
|
||||
tracks/1/enabled = true
|
||||
tracks/1/path = NodePath(".:visible_ratio")
|
||||
tracks/1/interp = 1
|
||||
tracks/1/loop_wrap = true
|
||||
tracks/1/keys = {
|
||||
"times": PackedFloat32Array(0, 0.2),
|
||||
"transitions": PackedFloat32Array(1, 1),
|
||||
"update": 0,
|
||||
"values": [0.0, 1.0]
|
||||
}
|
||||
tracks/2/type = "value"
|
||||
tracks/2/imported = false
|
||||
tracks/2/enabled = true
|
||||
tracks/2/path = NodePath(".:rotation")
|
||||
tracks/2/interp = 3
|
||||
tracks/2/loop_wrap = true
|
||||
tracks/2/keys = {
|
||||
"times": PackedFloat32Array(0, 2, 2.4),
|
||||
"transitions": PackedFloat32Array(1, 1, 1),
|
||||
"update": 0,
|
||||
"values": [7.31612e-10, 7.31612e-10, 0.174533]
|
||||
}
|
||||
tracks/3/type = "bezier"
|
||||
tracks/3/imported = false
|
||||
tracks/3/enabled = true
|
||||
tracks/3/path = NodePath(".:anchor_top")
|
||||
tracks/3/interp = 1
|
||||
tracks/3/loop_wrap = true
|
||||
tracks/3/keys = {
|
||||
"handle_modes": PackedInt32Array(0, 0, 0),
|
||||
"points": PackedFloat32Array(0.5, -0.25, 0, 0.5, -0.0548477, 0.5, -0.4, 0.0593877, 0.233401, -0.456136, 1.25, -0.25, 0, 0.25, 0),
|
||||
"times": PackedFloat32Array(0, 2, 2.4)
|
||||
}
|
||||
tracks/4/type = "bezier"
|
||||
tracks/4/imported = false
|
||||
tracks/4/enabled = true
|
||||
tracks/4/path = NodePath(".:anchor_bottom")
|
||||
tracks/4/interp = 1
|
||||
tracks/4/loop_wrap = true
|
||||
tracks/4/keys = {
|
||||
"handle_modes": PackedInt32Array(0, 0, 0),
|
||||
"points": PackedFloat32Array(0.5, -0.25, 0, 0.5, -0.055, 0.5, -0.4, 0.059, 0.233, -0.456, 1.25, -0.25, 0, 0.25, 0),
|
||||
"times": PackedFloat32Array(0, 2, 2.4)
|
||||
}
|
||||
|
||||
[sub_resource type="AnimationLibrary" id="AnimationLibrary_2a0gn"]
|
||||
_data = {
|
||||
"RESET": SubResource("Animation_3xds6"),
|
||||
"display": SubResource("Animation_cwotn")
|
||||
}
|
||||
|
||||
[sub_resource type="Animation" id="Animation_2gt87"]
|
||||
resource_name = "RESET"
|
||||
length = 0.001
|
||||
tracks/0/type = "value"
|
||||
tracks/0/imported = false
|
||||
tracks/0/enabled = true
|
||||
tracks/0/path = NodePath(".:visible")
|
||||
tracks/0/interp = 1
|
||||
tracks/0/loop_wrap = true
|
||||
tracks/0/keys = {
|
||||
"times": PackedFloat32Array(0),
|
||||
"transitions": PackedFloat32Array(1),
|
||||
"update": 1,
|
||||
"values": [false]
|
||||
}
|
||||
tracks/1/type = "value"
|
||||
tracks/1/imported = false
|
||||
tracks/1/enabled = true
|
||||
tracks/1/path = NodePath(".:visible_ratio")
|
||||
tracks/1/interp = 1
|
||||
tracks/1/loop_wrap = true
|
||||
tracks/1/keys = {
|
||||
"times": PackedFloat32Array(0),
|
||||
"transitions": PackedFloat32Array(1),
|
||||
"update": 0,
|
||||
"values": [1.0]
|
||||
}
|
||||
tracks/2/type = "value"
|
||||
tracks/2/imported = false
|
||||
tracks/2/enabled = true
|
||||
tracks/2/path = NodePath(".:rotation")
|
||||
tracks/2/interp = 3
|
||||
tracks/2/loop_wrap = true
|
||||
tracks/2/keys = {
|
||||
"times": PackedFloat32Array(0),
|
||||
"transitions": PackedFloat32Array(1),
|
||||
"update": 0,
|
||||
"values": [7.31612e-10]
|
||||
}
|
||||
tracks/3/type = "bezier"
|
||||
tracks/3/imported = false
|
||||
tracks/3/enabled = true
|
||||
tracks/3/path = NodePath(".:anchor_top")
|
||||
tracks/3/interp = 1
|
||||
tracks/3/loop_wrap = true
|
||||
tracks/3/keys = {
|
||||
"handle_modes": PackedInt32Array(0),
|
||||
"points": PackedFloat32Array(0.5, -0.25, 0, 0.25, 0),
|
||||
"times": PackedFloat32Array(0)
|
||||
}
|
||||
tracks/4/type = "bezier"
|
||||
tracks/4/imported = false
|
||||
tracks/4/enabled = true
|
||||
tracks/4/path = NodePath(".:anchor_bottom")
|
||||
tracks/4/interp = 1
|
||||
tracks/4/loop_wrap = true
|
||||
tracks/4/keys = {
|
||||
"handle_modes": PackedInt32Array(0),
|
||||
"points": PackedFloat32Array(0.5, -0.25, 0, 0.25, 0),
|
||||
"times": PackedFloat32Array(0)
|
||||
}
|
||||
|
||||
[sub_resource type="Animation" id="Animation_g52q7"]
|
||||
resource_name = "display"
|
||||
length = 3.4
|
||||
step = 0.1
|
||||
tracks/0/type = "value"
|
||||
tracks/0/imported = false
|
||||
tracks/0/enabled = true
|
||||
tracks/0/path = NodePath(".:visible")
|
||||
tracks/0/interp = 1
|
||||
tracks/0/loop_wrap = true
|
||||
tracks/0/keys = {
|
||||
"times": PackedFloat32Array(0, 3.4),
|
||||
"transitions": PackedFloat32Array(1, 1),
|
||||
"update": 1,
|
||||
"values": [true, false]
|
||||
}
|
||||
tracks/1/type = "value"
|
||||
tracks/1/imported = false
|
||||
tracks/1/enabled = true
|
||||
tracks/1/path = NodePath(".:visible_ratio")
|
||||
tracks/1/interp = 1
|
||||
tracks/1/loop_wrap = true
|
||||
tracks/1/keys = {
|
||||
"times": PackedFloat32Array(0, 0.4),
|
||||
"transitions": PackedFloat32Array(1, 1),
|
||||
"update": 0,
|
||||
"values": [0.0, 1.0]
|
||||
}
|
||||
tracks/2/type = "value"
|
||||
tracks/2/imported = false
|
||||
tracks/2/enabled = true
|
||||
tracks/2/path = NodePath(".:rotation")
|
||||
tracks/2/interp = 3
|
||||
tracks/2/loop_wrap = true
|
||||
tracks/2/keys = {
|
||||
"times": PackedFloat32Array(0, 3, 3.4),
|
||||
"transitions": PackedFloat32Array(1, 0.618, 1),
|
||||
"update": 0,
|
||||
"values": [7.31612e-10, 7.31612e-10, -2.96706]
|
||||
}
|
||||
tracks/3/type = "bezier"
|
||||
tracks/3/imported = false
|
||||
tracks/3/enabled = true
|
||||
tracks/3/path = NodePath(".:anchor_top")
|
||||
tracks/3/interp = 1
|
||||
tracks/3/loop_wrap = true
|
||||
tracks/3/keys = {
|
||||
"handle_modes": PackedInt32Array(0, 0, 0),
|
||||
"points": PackedFloat32Array(0.5, -0.25, 0, 0.5, -0.0548477, 0.5, -0.4, 0.0593877, 0.233401, -0.456136, 1.25, -0.25, 0, 0.25, 0),
|
||||
"times": PackedFloat32Array(0, 3, 3.4)
|
||||
}
|
||||
tracks/4/type = "bezier"
|
||||
tracks/4/imported = false
|
||||
tracks/4/enabled = true
|
||||
tracks/4/path = NodePath(".:anchor_bottom")
|
||||
tracks/4/interp = 1
|
||||
tracks/4/loop_wrap = true
|
||||
tracks/4/keys = {
|
||||
"handle_modes": PackedInt32Array(0, 0, 0),
|
||||
"points": PackedFloat32Array(0.5, -0.25, 0, 0.5, -0.055, 0.5, -0.4, 0.059, 0.233, -0.456, 1.25, -0.25, 0, 0.25, 0),
|
||||
"times": PackedFloat32Array(0, 3, 3.4)
|
||||
}
|
||||
|
||||
[sub_resource type="AnimationLibrary" id="AnimationLibrary_gbnnr"]
|
||||
_data = {
|
||||
"RESET": SubResource("Animation_2gt87"),
|
||||
"display": SubResource("Animation_g52q7")
|
||||
}
|
||||
|
||||
[sub_resource type="ShaderMaterial" id="ShaderMaterial_afsun"]
|
||||
shader = ExtResource("1_ybxxp")
|
||||
shader_parameter/change_color_depth = true
|
||||
shader_parameter/target_color_depth = 3
|
||||
shader_parameter/dithering = true
|
||||
shader_parameter/scale_resolution = true
|
||||
shader_parameter/target_resolution_scale = 3
|
||||
shader_parameter/enable_recolor = false
|
||||
|
||||
[sub_resource type="Animation" id="Animation_pk1s7"]
|
||||
length = 0.001
|
||||
tracks/0/type = "bezier"
|
||||
tracks/0/imported = false
|
||||
tracks/0/enabled = true
|
||||
tracks/0/path = NodePath(".:value")
|
||||
tracks/0/interp = 1
|
||||
tracks/0/loop_wrap = true
|
||||
tracks/0/keys = {
|
||||
"handle_modes": PackedInt32Array(0),
|
||||
"points": PackedFloat32Array(0, -0.25, 0, 0.25, 0),
|
||||
"times": PackedFloat32Array(0)
|
||||
}
|
||||
|
||||
[sub_resource type="Animation" id="Animation_67gmp"]
|
||||
resource_name = "fill"
|
||||
length = 1.618
|
||||
tracks/0/type = "bezier"
|
||||
tracks/0/imported = false
|
||||
tracks/0/enabled = true
|
||||
tracks/0/path = NodePath(".:value")
|
||||
tracks/0/interp = 1
|
||||
tracks/0/loop_wrap = true
|
||||
tracks/0/keys = {
|
||||
"handle_modes": PackedInt32Array(0, 0),
|
||||
"points": PackedFloat32Array(0, -0.25, 0, 0.233333, 0.0884774, 1, -0.267469, -0.483539, 0.25, 0),
|
||||
"times": PackedFloat32Array(0, 1.618)
|
||||
}
|
||||
|
||||
[sub_resource type="AnimationLibrary" id="AnimationLibrary_coah5"]
|
||||
_data = {
|
||||
"RESET": SubResource("Animation_pk1s7"),
|
||||
"fill": SubResource("Animation_67gmp")
|
||||
}
|
||||
|
||||
[sub_resource type="Animation" id="Animation_noa0w"]
|
||||
length = 0.001
|
||||
tracks/0/type = "bezier"
|
||||
tracks/0/imported = false
|
||||
tracks/0/enabled = true
|
||||
tracks/0/path = NodePath("CurveBar:value")
|
||||
tracks/0/interp = 1
|
||||
tracks/0/loop_wrap = true
|
||||
tracks/0/keys = {
|
||||
"handle_modes": PackedInt32Array(0),
|
||||
"points": PackedFloat32Array(0, -0.25, 0, 0.25, 0),
|
||||
"times": PackedFloat32Array(0)
|
||||
}
|
||||
|
||||
[sub_resource type="Animation" id="Animation_uo6s7"]
|
||||
resource_name = "fill"
|
||||
length = 0.618
|
||||
loop_mode = 2
|
||||
tracks/0/type = "bezier"
|
||||
tracks/0/imported = false
|
||||
tracks/0/enabled = true
|
||||
tracks/0/path = NodePath("CurveBar:value")
|
||||
tracks/0/interp = 1
|
||||
tracks/0/loop_wrap = true
|
||||
tracks/0/keys = {
|
||||
"handle_modes": PackedInt32Array(0, 0),
|
||||
"points": PackedFloat32Array(-1, -0.25, 0, 0.3, 0, 1, -0.3, 0, 0.25, 0),
|
||||
"times": PackedFloat32Array(0, 0.618)
|
||||
}
|
||||
|
||||
[sub_resource type="AnimationLibrary" id="AnimationLibrary_dicse"]
|
||||
_data = {
|
||||
"RESET": SubResource("Animation_noa0w"),
|
||||
"fill": SubResource("Animation_uo6s7")
|
||||
}
|
||||
|
||||
[sub_resource type="Animation" id="Animation_3cn2c"]
|
||||
length = 0.001
|
||||
tracks/0/type = "value"
|
||||
tracks/0/imported = false
|
||||
tracks/0/enabled = true
|
||||
tracks/0/path = NodePath("ClubSelector:rotation")
|
||||
tracks/0/interp = 4
|
||||
tracks/0/loop_wrap = true
|
||||
tracks/0/keys = {
|
||||
"times": PackedFloat32Array(0),
|
||||
"transitions": PackedFloat32Array(1),
|
||||
"update": 0,
|
||||
"values": [0.0]
|
||||
}
|
||||
|
||||
[sub_resource type="Animation" id="Animation_dt1yq"]
|
||||
resource_name = "hide"
|
||||
length = 0.4
|
||||
step = 0.02
|
||||
tracks/0/type = "value"
|
||||
tracks/0/imported = false
|
||||
tracks/0/enabled = true
|
||||
tracks/0/path = NodePath("ClubSelector:rotation")
|
||||
tracks/0/interp = 4
|
||||
tracks/0/loop_wrap = true
|
||||
tracks/0/keys = {
|
||||
"times": PackedFloat32Array(0, 0.1, 0.4),
|
||||
"transitions": PackedFloat32Array(1.618, 1.618, 1),
|
||||
"update": 0,
|
||||
"values": [0.0, 0.0872665, -1.5708]
|
||||
}
|
||||
|
||||
[sub_resource type="Animation" id="Animation_0maif"]
|
||||
resource_name = "show"
|
||||
length = 0.4
|
||||
step = 0.02
|
||||
tracks/0/type = "value"
|
||||
tracks/0/imported = false
|
||||
tracks/0/enabled = true
|
||||
tracks/0/path = NodePath("ClubSelector:rotation")
|
||||
tracks/0/interp = 4
|
||||
tracks/0/loop_wrap = true
|
||||
tracks/0/keys = {
|
||||
"times": PackedFloat32Array(0, 0.34, 0.4),
|
||||
"transitions": PackedFloat32Array(1.618, 1.618, 1),
|
||||
"update": 0,
|
||||
"values": [-1.5708, 0.0872665, 0.0]
|
||||
}
|
||||
|
||||
[sub_resource type="AnimationLibrary" id="AnimationLibrary_c3i4w"]
|
||||
_data = {
|
||||
"RESET": SubResource("Animation_3cn2c"),
|
||||
"hide": SubResource("Animation_dt1yq"),
|
||||
"show": SubResource("Animation_0maif")
|
||||
}
|
||||
|
||||
[sub_resource type="AnimationNodeAnimation" id="AnimationNodeAnimation_8uxnp"]
|
||||
animation = &"hide"
|
||||
|
||||
[sub_resource type="AnimationNodeAnimation" id="AnimationNodeAnimation_v05eu"]
|
||||
animation = &"show"
|
||||
|
||||
[sub_resource type="AnimationNodeStateMachineTransition" id="AnimationNodeStateMachineTransition_28a4x"]
|
||||
advance_mode = 2
|
||||
|
||||
[sub_resource type="AnimationNodeStateMachineTransition" id="AnimationNodeStateMachineTransition_bj7v0"]
|
||||
switch_mode = 2
|
||||
|
||||
[sub_resource type="AnimationNodeStateMachineTransition" id="AnimationNodeStateMachineTransition_xpwgd"]
|
||||
switch_mode = 2
|
||||
|
||||
[sub_resource type="AnimationNodeStateMachine" id="AnimationNodeStateMachine_osrbp"]
|
||||
states/hidden/node = SubResource("AnimationNodeAnimation_8uxnp")
|
||||
states/hidden/position = Vector2(592, 100)
|
||||
states/visible/node = SubResource("AnimationNodeAnimation_v05eu")
|
||||
states/visible/position = Vector2(364, 100)
|
||||
transitions = ["Start", "visible", SubResource("AnimationNodeStateMachineTransition_28a4x"), "visible", "hidden", SubResource("AnimationNodeStateMachineTransition_bj7v0"), "hidden", "visible", SubResource("AnimationNodeStateMachineTransition_xpwgd")]
|
||||
graph_offset = Vector2(-309, -132)
|
||||
|
||||
[node name="ShotHUD" type="Control"]
|
||||
top_level = true
|
||||
layout_mode = 3
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
mouse_filter = 1
|
||||
script = ExtResource("1_x5b4c")
|
||||
|
||||
[node name="WoahNiceFeedback" type="RichTextLabel" parent="."]
|
||||
visible = false
|
||||
custom_minimum_size = Vector2(1200, 0)
|
||||
layout_mode = 1
|
||||
anchors_preset = -1
|
||||
anchor_left = 0.5
|
||||
anchor_top = 0.5
|
||||
anchor_right = 0.5
|
||||
anchor_bottom = 0.5
|
||||
offset_left = -350.0
|
||||
offset_top = -66.0
|
||||
offset_right = 350.0
|
||||
offset_bottom = 66.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
mouse_filter = 2
|
||||
theme_type_variation = &"ShotFeedback"
|
||||
bbcode_enabled = true
|
||||
text = "[center][wave amp=64][rainbow]woah nice[/rainbow][/wave][/center]"
|
||||
fit_content = true
|
||||
autowrap_mode = 0
|
||||
visible_characters_behavior = 1
|
||||
|
||||
[node name="NiceAnimation" type="AnimationPlayer" parent="WoahNiceFeedback"]
|
||||
unique_name_in_owner = true
|
||||
libraries = {
|
||||
"": SubResource("AnimationLibrary_2a0gn")
|
||||
}
|
||||
|
||||
[node name="WastedFeedback" type="RichTextLabel" parent="."]
|
||||
visible = false
|
||||
custom_minimum_size = Vector2(1400, 0)
|
||||
layout_mode = 1
|
||||
anchors_preset = -1
|
||||
anchor_left = 0.5
|
||||
anchor_top = 0.5
|
||||
anchor_right = 0.5
|
||||
anchor_bottom = 0.5
|
||||
offset_left = -350.0
|
||||
offset_top = -66.0
|
||||
offset_right = 350.0
|
||||
offset_bottom = 66.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
pivot_offset = Vector2(100, 115)
|
||||
mouse_filter = 2
|
||||
theme_type_variation = &"ShotFeedback"
|
||||
bbcode_enabled = true
|
||||
text = "[center][tornado radius=16][color=powder_blue]wasted[/color][/tornado][/center]"
|
||||
fit_content = true
|
||||
autowrap_mode = 0
|
||||
visible_characters_behavior = 1
|
||||
|
||||
[node name="WastedAnimation" type="AnimationPlayer" parent="WastedFeedback"]
|
||||
unique_name_in_owner = true
|
||||
libraries = {
|
||||
"": SubResource("AnimationLibrary_gbnnr")
|
||||
}
|
||||
|
||||
[node name="ColorRect" type="ColorRect" parent="."]
|
||||
visible = false
|
||||
material = SubResource("ShaderMaterial_afsun")
|
||||
layout_mode = 1
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
mouse_filter = 2
|
||||
|
||||
[node name="ShotGauges" type="Control" parent="."]
|
||||
layout_mode = 1
|
||||
anchor_left = 0.4
|
||||
anchor_top = 0.3
|
||||
anchor_right = 0.6
|
||||
anchor_bottom = 0.85
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
mouse_filter = 1
|
||||
|
||||
[node name="PowerGauge" type="Control" parent="ShotGauges"]
|
||||
layout_mode = 1
|
||||
anchor_left = 0.5
|
||||
anchor_top = 0.382
|
||||
anchor_right = 0.5
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
mouse_filter = 1
|
||||
|
||||
[node name="PowerBar" type="ProgressBar" parent="ShotGauges/PowerGauge"]
|
||||
unique_name_in_owner = true
|
||||
visible = false
|
||||
custom_minimum_size = Vector2(30, 0)
|
||||
layout_mode = 1
|
||||
anchors_preset = 13
|
||||
anchor_left = 0.5
|
||||
anchor_right = 0.5
|
||||
anchor_bottom = 1.0
|
||||
offset_left = -2.0
|
||||
offset_right = 2.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
mouse_filter = 1
|
||||
max_value = 1.0
|
||||
fill_mode = 2
|
||||
|
||||
[node name="PowerAnimation" type="AnimationPlayer" parent="ShotGauges/PowerGauge"]
|
||||
unique_name_in_owner = true
|
||||
root_node = NodePath("../PowerBar")
|
||||
libraries = {
|
||||
"": SubResource("AnimationLibrary_coah5")
|
||||
}
|
||||
|
||||
[node name="CurveGauge" type="Control" parent="ShotGauges"]
|
||||
layout_mode = 1
|
||||
anchor_top = 0.25
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 0.25
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
mouse_filter = 1
|
||||
|
||||
[node name="CurveBar" type="ProgressBar" parent="ShotGauges/CurveGauge"]
|
||||
unique_name_in_owner = true
|
||||
visible = false
|
||||
layout_mode = 1
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
offset_top = -13.5
|
||||
offset_bottom = 13.5
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
min_value = -1.0
|
||||
max_value = 1.0
|
||||
|
||||
[node name="CurveAnimation" type="AnimationPlayer" parent="ShotGauges/CurveGauge"]
|
||||
unique_name_in_owner = true
|
||||
libraries = {
|
||||
"": SubResource("AnimationLibrary_dicse")
|
||||
}
|
||||
|
||||
[node name="ClubSelector" parent="." instance=ExtResource("2_1hdub")]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 1
|
||||
anchors_preset = 0
|
||||
anchor_right = 0.0
|
||||
anchor_bottom = 0.0
|
||||
offset_left = 50.0
|
||||
offset_top = 50.0
|
||||
offset_right = 50.0
|
||||
offset_bottom = 50.0
|
||||
grow_horizontal = 1
|
||||
grow_vertical = 1
|
||||
pivot_offset = Vector2(-400, 0)
|
||||
|
||||
[node name="HUDAnimation" type="AnimationPlayer" parent="."]
|
||||
libraries = {
|
||||
"": SubResource("AnimationLibrary_c3i4w")
|
||||
}
|
||||
|
||||
[node name="HUDStateMachine" type="AnimationTree" parent="."]
|
||||
unique_name_in_owner = true
|
||||
root_node = NodePath("%HUDStateMachine/..")
|
||||
tree_root = SubResource("AnimationNodeStateMachine_osrbp")
|
||||
anim_player = NodePath("../HUDAnimation")
|
|
@ -1,4 +0,0 @@
|
|||
class_name WorldUI extends Control
|
||||
## Container & accessor for the world UI.
|
||||
|
||||
@onready var shot_hud: ShotHUD = %ShotHUD
|
|
@ -1,23 +0,0 @@
|
|||
class_name PlayManager extends Resource
|
||||
## Abstract base type for strategies to manage the flow of gameplay
|
||||
|
||||
## List of game player instances
|
||||
@export var players: Array[WorldPlayer] = []
|
||||
|
||||
|
||||
func initialize() -> void:
|
||||
for player: WorldPlayer in players:
|
||||
player.shot_setup.finished.connect(_on_turn_finished)
|
||||
on_initialization()
|
||||
|
||||
|
||||
func on_initialization() -> void:
|
||||
pass # Implemented in derived type
|
||||
|
||||
|
||||
func on_turn_finished(_shot_setup: ShotSetup) -> void:
|
||||
pass # Implemented in derived type
|
||||
|
||||
|
||||
func _on_turn_finished(shot_setup: ShotSetup) -> void:
|
||||
on_turn_finished(shot_setup)
|
|
@ -1,14 +0,0 @@
|
|||
class_name RoundRobinManager extends PlayManager
|
||||
## Players take turns one after the other, looping back to the first player at the end.
|
||||
|
||||
|
||||
func on_initialization() -> void:
|
||||
print("IN ON_INITIALIZATION")
|
||||
# Set first player as active
|
||||
players[0].shot_setup.phase = ShotSetup.Phase.AIM
|
||||
|
||||
|
||||
func on_turn_finished(source: ShotSetup) -> void:
|
||||
print_debug("Shot finished for ", source.player.name)
|
||||
players.push_back(players.pop_front())
|
||||
players[0].shot_setup.queue_restart()
|
|
@ -1,61 +0,0 @@
|
|||
class_name World extends Node
|
||||
## Container scene for a game world instance.
|
||||
##
|
||||
## A world contains player(s) and the active level, manages player states,
|
||||
## and transitions between active levels.
|
||||
|
||||
@export var initial_level: PackedScene = load("res://levels/debug_level/debug_level.tscn")
|
||||
|
||||
@export var manager: PlayManager
|
||||
|
||||
@export var spawn_group := "PlayerSpawn"
|
||||
|
||||
var _spawns_available: Array[Node3D] = []
|
||||
|
||||
@onready var level: Node3D = %Level
|
||||
@onready var ui: WorldUI = %UI
|
||||
|
||||
static var group := "WorldGroup"
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
load_level(initial_level)
|
||||
|
||||
# Spawn players in the level
|
||||
for player: WorldPlayer in manager.players:
|
||||
_spawn_player(player)
|
||||
|
||||
manager.initialize()
|
||||
if not manager.players:
|
||||
push_warning("Warning: Starting game world with no players!")
|
||||
|
||||
|
||||
func _random_spawn() -> Node3D:
|
||||
# Get random spawn point
|
||||
if not _spawns_available:
|
||||
_spawns_available.assign(get_tree().get_nodes_in_group(spawn_group))
|
||||
@warning_ignore("unsafe_cast")
|
||||
var spawn_point := _spawns_available.pick_random() as Node3D
|
||||
assert(spawn_point, "No nodes in spawn group `%s` found!" % spawn_group)
|
||||
_spawns_available.erase(spawn_point)
|
||||
return spawn_point
|
||||
|
||||
|
||||
func _spawn_player(player: WorldPlayer) -> void:
|
||||
var spawn_point := _random_spawn()
|
||||
var shot_setup := player.shot_setup
|
||||
shot_setup.global_transform = spawn_point.global_transform
|
||||
spawn_point.add_sibling(shot_setup)
|
||||
|
||||
|
||||
## Instantiate and mantle the given level scene.
|
||||
##
|
||||
## This will free any currently-loaded level!
|
||||
func load_level(level_scene: PackedScene) -> void:
|
||||
# Remove any currently-loaded level assets
|
||||
for c: Node in level.get_children():
|
||||
c.queue_free()
|
||||
|
||||
# Load the level
|
||||
var instance: Node3D = level_scene.instantiate()
|
||||
level.add_child(instance)
|
|
@ -1,36 +1,33 @@
|
|||
[gd_scene load_steps=9 format=3 uid="uid://cwnwcd8kushl3"]
|
||||
[gd_scene load_steps=4 format=3 uid="uid://cwnwcd8kushl3"]
|
||||
|
||||
[ext_resource type="Script" path="res://src/world/world.gd" id="1_ybjyx"]
|
||||
[ext_resource type="PackedScene" uid="uid://bm2o3mex10v11" path="res://levels/debug_level/debug_level.tscn" id="2_0xu5a"]
|
||||
[ext_resource type="PackedScene" uid="uid://c4ifdiohng830" path="res://src/ui/shot_hud/shot_hud.tscn" id="2_5b7qb"]
|
||||
[ext_resource type="Script" path="res://src/player/world_player.gd" id="2_e743i"]
|
||||
[ext_resource type="Script" path="res://src/ui/world_ui.gd" id="2_imewa"]
|
||||
[ext_resource type="Resource" uid="uid://crock3revdn73" path="res://src/player/debug_player.tres" id="3_pyw81"]
|
||||
[ext_resource type="Script" path="res://src/world/play_manager/round_robin_manager.gd" id="5_h6mje"]
|
||||
[ext_resource type="Shader" path="res://src/shaders/retro_postprocessing.gdshader" id="1_3gv54"]
|
||||
[ext_resource type="PackedScene" uid="uid://bm2o3mex10v11" path="res://levels/debug_level/debug_level.tscn" id="1_pge3b"]
|
||||
|
||||
[sub_resource type="Resource" id="Resource_rdjhi"]
|
||||
script = ExtResource("5_h6mje")
|
||||
players = Array[ExtResource("2_e743i")]([ExtResource("3_pyw81")])
|
||||
[sub_resource type="ShaderMaterial" id="ShaderMaterial_sc4r2"]
|
||||
shader = ExtResource("1_3gv54")
|
||||
shader_parameter/change_color_depth = true
|
||||
shader_parameter/target_color_depth = 8
|
||||
shader_parameter/dithering = true
|
||||
shader_parameter/scale_resolution = true
|
||||
shader_parameter/target_resolution_scale = 3
|
||||
shader_parameter/enable_recolor = false
|
||||
|
||||
[node name="World" type="Node" groups=["WorldGroup"]]
|
||||
script = ExtResource("1_ybjyx")
|
||||
initial_level = ExtResource("2_0xu5a")
|
||||
manager = SubResource("Resource_rdjhi")
|
||||
[node name="World" type="Node"]
|
||||
|
||||
[node name="Level" type="Node3D" parent="."]
|
||||
unique_name_in_owner = true
|
||||
|
||||
[node name="UI" type="Control" parent="."]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 3
|
||||
[node name="SubViewportContainer" type="SubViewportContainer" parent="."]
|
||||
material = SubResource("ShaderMaterial_sc4r2")
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
mouse_filter = 1
|
||||
script = ExtResource("2_imewa")
|
||||
stretch = true
|
||||
|
||||
[node name="ShotHUD" parent="UI" instance=ExtResource("2_5b7qb")]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 1
|
||||
[node name="SubViewport" type="SubViewport" parent="SubViewportContainer"]
|
||||
handle_input_locally = false
|
||||
size = Vector2i(1280, 720)
|
||||
render_target_update_mode = 4
|
||||
|
||||
[node name="Level" type="Node3D" parent="SubViewportContainer/SubViewport"]
|
||||
|
||||
[node name="TestLevel" parent="SubViewportContainer/SubViewport/Level" instance=ExtResource("1_pge3b")]
|
||||
|
|
Loading…
Reference in New Issue