diff options
Diffstat (limited to 'addons/voxel-core/controls/voxel_viewer')
3 files changed, 1303 insertions, 0 deletions
diff --git a/addons/voxel-core/controls/voxel_viewer/voxel_viewer.gd b/addons/voxel-core/controls/voxel_viewer/voxel_viewer.gd new file mode 100644 index 0000000..1c3f881 --- /dev/null +++ b/addons/voxel-core/controls/voxel_viewer/voxel_viewer.gd @@ -0,0 +1,824 @@ +tool +extends Control +# 2D / 3D preview of a voxel, that allows for selection and editing of faces. + + + +## Signals +# Emitted when a voxel face has been selected +signal selected_face(face) +# Emitted when a voxel face has been unselected +signal unselected_face(face) + + + +## Enums +# View modes available +enum ViewModes { VIEW_2D, VIEW_3D } + + + +## Constants +# Default environment used +var DefaultEnv := preload("res://addons/voxel-core/controls/voxel_viewer/voxel_viewer_env.tres") + + + +## Exported Variables +# Number of uv positions that can be selected at any one time +export(int, 0, 6) var selection_max := 0 setget set_selection_max + +# Flag indicating whether edits are allowed +export var allow_edit := false setget set_allow_edit + +# Current view being shown +export(ViewModes) var view_mode := ViewModes.VIEW_3D setget set_view_mode + +# View sensitivity for the 3D view +export(int, 0, 100) var camera_sensitivity := 8 + +# ID of voxel to represented +export var voxel_id : int setget set_voxel_id + +# VoxelSet beings used +export(Resource) var voxel_set = null setget set_voxel_set + +# Environment used in 3D view +export(Environment) var environment := DefaultEnv setget set_environment + + + +## Public Variables +# UndoRedo used to commit operations +var undo_redo : UndoRedo + + + +## Private Variables +# Selected voxel ids +var _selections := [] + +# VoxelTool used for Mesh generation +var _voxel_tool := VoxelTool.new() + +# Internal flag used to know whether user is dragging in 3D view +var _is_dragging := false + +# Internal flag used to know the last face the user hovered +var _last_hovered_face := Vector3.ZERO + +# Internal value used to revert to old versions of voxel data +var _unedited_voxel := {} + +# Internal flag used to indicate the operation being committed +var _editing_action := -1 + +# Internal flag used to indicate the face being edited +var _editing_face := Vector3.ZERO + +# Internal flag used to indicate whether multiple faces are being edited +var _editing_multiple := false + + + +## OnReady Variables +onready var View2D := get_node("View2D") + +onready var View3D := get_node("View3D") + +onready var ViewPort := get_node("View3D/Viewport") + +onready var CameraPivot := get_node("View3D/Viewport/CameraPivot") + +onready var CameraRef := get_node("View3D/Viewport/CameraPivot/Camera") + +onready var VoxelPreview := get_node("View3D/Viewport/VoxelPreview") + +onready var Select := get_node("View3D/Viewport/Select") + +onready var ViewModeRef := get_node("ToolBar/ViewMode") + +onready var ViewerHint := get_node("ToolBar/Hint") + +onready var ContextMenu := get_node("ContextMenu") + +onready var ColorMenu := get_node("ColorMenu") + +onready var VoxelColor := get_node("ColorMenu/VBoxContainer/VoxelColor") + +onready var TextureMenu := get_node("TextureMenu") + +onready var VoxelTexture := get_node("TextureMenu/VBoxContainer/ScrollContainer/VoxelTexture") + +onready var MaterialMenu := get_node("MaterialMenu") + +onready var MaterialRef := get_node("MaterialMenu/VBoxContainer/VBoxContainer/HBoxContainer6/Material") + +onready var Metallic := get_node("MaterialMenu/VBoxContainer/VBoxContainer/HBoxContainer/Metallic") + +onready var Specular := get_node("MaterialMenu/VBoxContainer/VBoxContainer/HBoxContainer2/Specular") + +onready var Roughness := get_node("MaterialMenu/VBoxContainer/VBoxContainer/HBoxContainer3/Roughness") + +onready var Energy := get_node("MaterialMenu/VBoxContainer/VBoxContainer/HBoxContainer4/Energy") + +onready var EnergyColor := get_node("MaterialMenu/VBoxContainer/VBoxContainer/HBoxContainer5/EnergyColor") + +onready var EnvironmentMenu := get_node("EnvironmentMenu") + + + +## Built-In Virtual Methods +func _ready(): + set_view_mode(view_mode) + set_voxel_set(voxel_set) + load_environment() + + if not is_instance_valid(undo_redo): + undo_redo = UndoRedo.new() + + + +## Public Methods +# Saves the used environment path +func save_environment() -> void: + if environment == DefaultEnv: + var dir := Directory.new() + if dir.file_exists("res://addons/voxel-core/controls/voxel_viewer/config.var"): + dir.remove("res://addons/voxel-core/controls/voxel_viewer/config.var") + elif is_instance_valid(environment): + var file := File.new() + var opened = file.open( + "res://addons/voxel-core/controls/voxel_viewer/config.var", + File.WRITE) + if opened == OK: + file.store_string(environment.resource_path) + if file.is_open(): + file.close() + + +# Loads and sets the environment file +func load_environment() -> void: + var loaded := false + var file := File.new() + var opened = file.open( + "res://addons/voxel-core/controls/voxel_viewer/config.var", + File.READ) + if opened == OK: + var environment_path = file.get_as_text() + if file.file_exists(environment_path): + var _environment := load(environment_path) + if _environment is Environment: + set_environment(_environment) + loaded = true + + if not loaded: + set_environment(DefaultEnv) + + if file.is_open(): + file.close() + + +# Resets the environment to default +func reset_environment() -> void: + set_environment(DefaultEnv) + save_environment() + + +func set_selection_max(value : int, update := true) -> void: + selection_max = clamp(value, 0, 6) + unselect_shrink() + if update: + self.update() + + +# Sets allow_edit +func set_allow_edit(value : bool) -> void: + allow_edit = value + + +# Sets view_mode +func set_view_mode(value : int) -> void: + _last_hovered_face = Vector3.ZERO + view_mode = int(clamp(value, 0, ViewModes.size())) + + if is_instance_valid(ViewModeRef): + ViewModeRef.selected = view_mode + if is_instance_valid(View2D): + View2D.visible = view_mode == ViewModes.VIEW_2D + if is_instance_valid(View3D): + View3D.visible = view_mode == ViewModes.VIEW_3D + + +# Sets voxel_id, calls on update_view by defalut +func set_voxel_id(value : int, update := true) -> void: + voxel_id = value + if update: + update_view() + + +# Returns true if face is selected +func has_selected(face : Vector3) -> bool: + return _selections.has(face) + + +# Returns face selected at given index +func get_selected(index : int) -> Vector3: + return _selections[index] + + +# Returns array of selected faces +func get_selections() -> Array: + return _selections.duplicate() + + +# Returns number of faces selected +func get_selected_size() -> int: + return _selections.size() + + +# Sets voxel_set, and calls on update by default +func set_voxel_set(value : Resource, update := true) -> void: + if not (typeof(value) == TYPE_NIL or value is VoxelSet): + printerr("Invalid Resource given expected VoxelSet") + return + + if is_instance_valid(voxel_set): + if voxel_set.is_connected("requested_refresh", self, "update_view"): + voxel_set.disconnect("requested_refresh", self, "update_view") + + voxel_set = value + if is_instance_valid(voxel_set): + if not voxel_set.is_connected("requested_refresh", self, "update_view"): + voxel_set.connect("requested_refresh", self, "update_view") + if is_instance_valid(VoxelTexture): + VoxelTexture.voxel_set = voxel_set + + if update: + update_view() + + +func set_environment(value : Environment) -> void: + environment = value + if is_instance_valid(ViewPort): + ViewPort.transparent_bg = environment == DefaultEnv + ViewPort.world.environment = environment + + +# Return normal associated with given name +func string_to_face(string : String) -> Vector3: + string = string.to_upper() + var normal := Vector3.ZERO + match string: + "RIGHT": + normal = Vector3.RIGHT + "LEFT": + normal = Vector3.LEFT + "TOP": + normal = Vector3.UP + "BOTTOM": + normal = Vector3.DOWN + "FRONT": + normal = Vector3.FORWARD + "BACK": + normal = Vector3.BACK + return normal + +# Return name associated with given face +func face_to_string(face : Vector3) -> String: + var string := "" + match face: + Vector3.RIGHT: + string = "RIGHT" + Vector3.LEFT: + string = "LEFT" + Vector3.UP: + string = "TOP" + Vector3.DOWN: + string = "BOTTOM" + Vector3.FORWARD: + string = "FRONT" + Vector3.BACK: + string = "BACK" + return string + + +# Quick setup of voxel_set, voxel_id; calls on update_view and update_hint +func setup(voxel_set : VoxelSet, voxel_set_id : int) -> void: + set_voxel_set(voxel_set, false) + set_voxel_id(voxel_set_id, false) + update_view() + update_hint() + + +# Returns the voxel data of current voxel, returns a empty Dictionary if not set +func get_viewing_voxel() -> Dictionary: + return voxel_set.get_voxel(voxel_id) if is_instance_valid(voxel_set) else {} + + +# Returns the VoxelButton associated with face normal +func get_voxle_button(face_normal : Vector3): + return View2D.find_node(face_to_string(face_normal).capitalize()) + + +# Selects given face, and emits selected_face +func select(face : Vector3, emit := true) -> void: + if selection_max != 0: + unselect_shrink(selection_max - 1) + _selections.append(face) + var voxel_button = get_voxle_button(face) + if is_instance_valid(voxel_button): + voxel_button.pressed = true + if emit: + emit_signal("selected_face", face) + + +# Unselects given face, and emits unselected_face +func unselect(face : Vector3, emit := true) -> void: + if _selections.has(face): + _selections.erase(face) + var voxel_button = get_voxle_button(face) + if is_instance_valid(voxel_button): + voxel_button.pressed = false + if emit: + emit_signal("unselected_face", face) + + +# Unselects all the faces +func unselect_all() -> void: + while not _selections.empty(): + unselect(_selections.back()) + + +# Unselects all faces until given size is met +func unselect_shrink(size := selection_max, emit := true) -> void: + if size >= 0: + while _selections.size() > size: + unselect(_selections.back(), emit) + + +# Updates the hint message +func update_hint() -> void: + if is_instance_valid(ViewerHint): + ViewerHint.text = "" + + if not _selections.empty(): + for i in range(len(_selections)): + if i > 0: + ViewerHint.text += ", " + ViewerHint.text += face_to_string(_selections[i]).to_upper() + + if _last_hovered_face != Vector3.ZERO: + if not ViewerHint.text.empty(): + ViewerHint.text += " | " + ViewerHint.text += face_to_string(_last_hovered_face).to_upper() + + +# Updates the view +func update_view() -> void: + if not is_instance_valid(voxel_set): + return + + if is_instance_valid(View2D): + for voxel_button in View2D.get_children(): + voxel_button.setup(voxel_set, voxel_id, string_to_face(voxel_button.name)) + voxel_button.hint_tooltip = voxel_button.name + + if is_instance_valid(VoxelPreview): + _voxel_tool.begin(voxel_set, true) + for face in Voxel.Faces: + _voxel_tool.add_face( + get_viewing_voxel(), + face, -Vector3.ONE / 2) + VoxelPreview.mesh = _voxel_tool.commit() + + _voxel_tool.begin(voxel_set, true) + for selection in _selections: + _voxel_tool.add_face( + Voxel.colored(Color(0, 0, 0, 0.75)), + selection, -Vector3.ONE / 2) + Select.mesh = _voxel_tool.commit() + + +# Shows the context menu and options +func show_context_menu(global_position : Vector2, face := _last_hovered_face) -> void: + if not is_instance_valid(ContextMenu): + return + ContextMenu.clear() + if _last_hovered_face == Vector3.ZERO: + ContextMenu.add_item("Change environment", 14) + if environment != DefaultEnv: + ContextMenu.add_item("Reset environment", 15) + elif allow_edit: + _editing_face = face + _editing_multiple = false + var selected_hovered := _selections.has(_editing_face) + if is_instance_valid(voxel_set): + var voxel := get_viewing_voxel() + + if _selections.size() < 6: + ContextMenu.add_item("Select all", 13) + if _selections.size() > 0: + ContextMenu.add_item("Unselect all", 11) + + if _selections.size() == 0 or not selected_hovered: + ContextMenu.add_separator() + ContextMenu.add_item("Color side", 0) + if Voxel.has_face_color(voxel, _editing_face): + ContextMenu.add_item("Remove side color", 1) + + if voxel_set.uv_ready(): + ContextMenu.add_item("Texture side", 2) + if Voxel.has_face_uv(voxel, _editing_face): + ContextMenu.add_item("Remove side uv", 3) + + if selected_hovered and _selections.size() >= 1: + ContextMenu.add_separator() + ContextMenu.add_item("Color side(s)", 7) + if Voxel.has_face_color(voxel, _editing_face): + ContextMenu.add_item("Remove side color(s)", 8) + + if voxel_set.uv_ready(): + ContextMenu.add_item("Texture side(s)", 9) + if Voxel.has_face_uv(voxel, _editing_face): + ContextMenu.add_item("Remove side uv(s)", 10) + + ContextMenu.add_separator() + ContextMenu.add_item("Color voxel", 4) + + ContextMenu.add_item("Modify material", 12) + + if voxel_set.uv_ready(): + ContextMenu.add_item("Texture voxel", 5) + if Voxel.has_uv(voxel): + ContextMenu.add_item("Remove voxel uv", 6) + ContextMenu.set_as_minsize() + + ContextMenu.popup(Rect2( + global_position, + ContextMenu.rect_size)) + + +# Shows the color menu centered with given color +func show_color_menu(color : Color) -> void: + if is_instance_valid(ColorMenu): + VoxelColor.color = color + ColorMenu.popup_centered() + + +# Closes the color menu +func hide_color_menu() -> void: + if is_instance_valid(ColorMenu): + ColorMenu.hide() + update_view() + + +# Shows the texture menu centered with given color +func show_texture_menu(uv : Vector2) -> void: + if is_instance_valid(TextureMenu): + VoxelTexture.unselect_all() + VoxelTexture.select(uv) + TextureMenu.popup_centered() + + +# Closes the texture menu +func hide_texture_menu() -> void: + if is_instance_valid(TextureMenu): + TextureMenu.hide() + update_view() + + +# Shows the material menu with given voxel data +func show_material_menu(voxel := get_viewing_voxel()) -> void: + if is_instance_valid(MaterialMenu): + MaterialRef.value = Voxel.get_material(voxel) + MaterialRef.max_value = voxel_set.materials.size() - 1 + Metallic.value = Voxel.get_metallic(voxel) + Specular.value = Voxel.get_specular(voxel) + Roughness.value = Voxel.get_roughness(voxel) + Energy.value = Voxel.get_energy(voxel) + EnergyColor.color = Voxel.get_energy_color(voxel) + MaterialMenu.popup_centered() + + +# Closes the material menu +func hide_material_menu() -> void: + if is_instance_valid(MaterialMenu): + MaterialMenu.hide() + update_view() + + +func show_environment_change_menu() -> void: + EnvironmentMenu.popup_centered() + +func hide_environment_change_menu() -> void: + EnvironmentMenu.hide() + + + +## Private Methods +func _set_last_hovered_face(face : Vector3): + _last_hovered_face = face + + +func _voxel_backup() -> void: + _unedited_voxel = get_viewing_voxel().duplicate(true) + + +func _voxel_restore() -> void: + voxel_set.set_voxel(voxel_id, _unedited_voxel) + + +func _on_Face_gui_input(event : InputEvent, normal : Vector3) -> void: + _last_hovered_face = normal + if event is InputEventMouseButton and event.pressed: + if event.button_index == BUTTON_LEFT: + if selection_max > 0: + if _selections.has(normal): + unselect(normal) + else: + select(normal) + accept_event() + else: + get_voxle_button(normal).pressed = false + elif event.button_index == BUTTON_RIGHT: + if allow_edit: + show_context_menu(event.global_position, _last_hovered_face) + update_hint() + + +func _on_View3D_gui_input(event : InputEvent) -> void: + if event is InputEventMouse: + var from = CameraRef.project_ray_origin(event.position) + var to = from + CameraRef.project_ray_normal(event.position) * 1000 + var hit = CameraRef.get_world().direct_space_state.intersect_ray(from, to) + if hit.empty(): + _last_hovered_face = Vector3.ZERO + else: + hit["normal"] = hit["normal"].round() + _last_hovered_face = hit["normal"] + + if event is InputEventMouseMotion: + if _is_dragging: + var motion = event.relative.normalized() + CameraPivot.rotation_degrees.x += -motion.y * camera_sensitivity + CameraPivot.rotation_degrees.y += -motion.x * camera_sensitivity + elif event is InputEventMouseButton: + if event.button_index == BUTTON_LEFT: + if event.doubleclick: + if not hit.empty() and selection_max > 0: + if _selections.has(hit["normal"]): + unselect(hit["normal"]) + else: + select(hit["normal"]) + elif event.is_pressed(): + _is_dragging = true + else: + _is_dragging = false + elif event.button_index == BUTTON_RIGHT and not event.is_pressed(): + show_context_menu(event.global_position, _last_hovered_face) + + if _is_dragging: + View3D.set_default_cursor_shape(Control.CURSOR_MOVE) + elif hit: + View3D.set_default_cursor_shape(Control.CURSOR_POINTING_HAND) + else: + View3D.set_default_cursor_shape(Control.CURSOR_ARROW) + update_hint() + update_view() + + +func _on_ContextMenu_id_pressed(id : int): + _editing_action = id + _editing_multiple = false + _voxel_backup() + match id: + 0: # Color editing face + show_color_menu(Voxel.get_face_color(get_viewing_voxel(), _editing_face)) + 1: # Remove editing face color + var voxel = get_viewing_voxel() + Voxel.remove_face_color(voxel, _editing_face) + Voxel.clean(voxel) + + undo_redo.create_action("VoxelViewer : Remove voxel face color") + undo_redo.add_do_method(voxel_set, "set_voxel", voxel_id, voxel) + undo_redo.add_undo_method(voxel_set, "set_voxel", voxel_id, _unedited_voxel) + undo_redo.add_do_method(voxel_set, "request_refresh") + undo_redo.add_undo_method(voxel_set, "request_refresh") + undo_redo.commit_action() + 2: # Texture editing face + show_texture_menu(Voxel.get_face_uv(get_viewing_voxel(), _editing_face)) + 3: # Remove editing face uv + var voxel := get_viewing_voxel() + Voxel.remove_face_uv(voxel, _editing_face) + Voxel.clean(voxel) + + undo_redo.create_action("VoxelViewer : Remove voxel face uv") + undo_redo.add_do_method(voxel_set, "set_voxel", voxel_id, voxel) + undo_redo.add_undo_method(voxel_set, "set_voxel", voxel_id, _unedited_voxel) + undo_redo.add_do_method(voxel_set, "request_refresh") + undo_redo.add_undo_method(voxel_set, "request_refresh") + undo_redo.commit_action() + 7: # Color selected faces + _editing_multiple = true + show_color_menu(Voxel.get_face_color(get_viewing_voxel(), _editing_face)) + 8: # Remove selected faces color + _editing_multiple = true + var voxel = get_viewing_voxel() + for selection in _selections: + Voxel.remove_face_color(voxel, selection) + Voxel.clean(voxel) + + undo_redo.create_action("VoxelViewer : Remove voxel face(s) color") + undo_redo.add_do_method(voxel_set, "set_voxel", voxel_id, voxel) + undo_redo.add_undo_method(voxel_set, "set_voxel", voxel_id, _unedited_voxel) + undo_redo.add_do_method(voxel_set, "request_refresh") + undo_redo.add_undo_method(voxel_set, "request_refresh") + undo_redo.commit_action() + 9: # Texture selected face + _editing_multiple = true + show_texture_menu(Voxel.get_face_uv(get_viewing_voxel(), _editing_face)) + 10: # Remove selected face uv + _editing_multiple = true + var voxel := get_viewing_voxel() + for selection in _selections: + Voxel.remove_face_uv(voxel, selection) + Voxel.clean(voxel) + + undo_redo.create_action("VoxelViewer : Remove voxel face(s) uv") + undo_redo.add_do_method(voxel_set, "set_voxel", voxel_id, voxel) + undo_redo.add_undo_method(voxel_set, "set_voxel", voxel_id, _unedited_voxel) + undo_redo.add_do_method(voxel_set, "request_refresh") + undo_redo.add_undo_method(voxel_set, "request_refresh") + undo_redo.commit_action() + 4: # Set voxel color + show_color_menu(Voxel.get_color(get_viewing_voxel())) + 5: # Set voxel uv + show_texture_menu(Voxel.get_uv(get_viewing_voxel())) + 6: # Remove voxel uv + var voxel = voxel_set.get_voxel(voxel_id) + Voxel.remove_uv(voxel) + Voxel.clean(voxel) + + undo_redo.create_action("VoxelViewer : Remove voxel uv") + undo_redo.add_do_method(voxel_set, "set_voxel", voxel_id, voxel) + undo_redo.add_undo_method(voxel_set, "set_voxel", voxel_id, _unedited_voxel) + undo_redo.add_do_method(voxel_set, "request_refresh") + undo_redo.add_undo_method(voxel_set, "request_refresh") + undo_redo.commit_action() + 13: # Select all + unselect_all() + for face in Voxel.Faces: + select(face) + 11: # Unselect all + unselect_all() + 12: # Modify material + show_material_menu() + 14: + show_environment_change_menu() + 15: + reset_environment() + + +func _on_ColorPicker_color_changed(color : Color): + match _editing_action: + 0, 7: + for selection in (_selections if _editing_multiple else [_editing_face]): + Voxel.set_face_color(get_viewing_voxel(), selection, color) + 4: Voxel.set_color(get_viewing_voxel(), color) + update_view() + + +func _on_ColorMenu_Cancel_pressed(): + _voxel_restore() + + hide_color_menu() + + +func _on_ColorMenu_Confirm_pressed(): + match _editing_action: + 0, 7: + var voxel = get_viewing_voxel() + Voxel.clean(voxel) + + undo_redo.create_action("VoxelViewer : Set voxel face(s) color") + undo_redo.add_do_method(voxel_set, "set_voxel", voxel_id, voxel) + undo_redo.add_undo_method(voxel_set, "set_voxel", voxel_id, _unedited_voxel) + undo_redo.add_do_method(voxel_set, "request_refresh") + undo_redo.add_undo_method(voxel_set, "request_refresh") + undo_redo.commit_action() + 4: + var voxel = get_viewing_voxel() + Voxel.clean(voxel) + + undo_redo.create_action("VoxelViewer : Set voxel color") + undo_redo.add_do_method(voxel_set, "set_voxel", voxel_id, voxel) + undo_redo.add_undo_method(voxel_set, "set_voxel", voxel_id, _unedited_voxel) + undo_redo.add_do_method(voxel_set, "request_refresh") + undo_redo.add_undo_method(voxel_set, "request_refresh") + undo_redo.commit_action() + hide_color_menu() + + +func _on_VoxelTexture_selected_uv(uv : Vector2): + match _editing_action: + 2, 9: + for selection in (_selections if _editing_multiple else [_editing_face]): + Voxel.set_face_uv(get_viewing_voxel(), selection, uv) + 5: Voxel.set_uv(get_viewing_voxel(), uv) + update_view() + + +func _on_TextureMenu_Cancel_pressed(): + _voxel_restore() + + hide_texture_menu() + + +func _on_TextureMenu_Confirm_pressed(): + match _editing_action: + 2, 9: + var voxel = get_viewing_voxel() + Voxel.clean(voxel) + + undo_redo.create_action("VoxelViewer : Set voxel face(s) uv") + undo_redo.add_do_method(voxel_set, "set_voxel", voxel_id, voxel) + undo_redo.add_undo_method(voxel_set, "set_voxel", voxel_id, _unedited_voxel) + undo_redo.add_do_method(voxel_set, "request_refresh") + undo_redo.add_undo_method(voxel_set, "request_refresh") + undo_redo.commit_action() + 5: + var voxel = get_viewing_voxel() + Voxel.clean(voxel) + + undo_redo.create_action("VoxelViewer : Set voxel uv") + undo_redo.add_do_method(voxel_set, "set_voxel", voxel_id, voxel) + undo_redo.add_undo_method(voxel_set, "set_voxel", voxel_id, _unedited_voxel) + undo_redo.add_do_method(voxel_set, "request_refresh") + undo_redo.add_undo_method(voxel_set, "request_refresh") + undo_redo.commit_action() + hide_texture_menu() + + +func _on_Metallic_value_changed(metallic : float): + Voxel.set_metallic(get_viewing_voxel(), metallic) + update_view() + + +func _on_Specular_value_changed(specular : float): + Voxel.set_specular(get_viewing_voxel(), specular) + update_view() + + +func _on_Roughness_value_changed(roughness : float): + Voxel.set_roughness(get_viewing_voxel(), roughness) + update_view() + + +func _on_Energy_value_changed(emergy : float): + Voxel.set_energy(get_viewing_voxel(), emergy) + update_view() + + +func _on_EnergyColor_changed(color : Color): + Voxel.set_energy_color(get_viewing_voxel(), color) + update_view() + + +func _on_Material_value_changed(value : int): + Metallic.editable = value == -1 + Specular.editable = value == -1 + Roughness.editable = value == -1 + Energy.editable = value == -1 + EnergyColor.disabled = value > -1 + + Voxel.set_material(get_viewing_voxel(), value) + update_view() + + +func _on_MaterialMenu_Cancel_pressed(): + _voxel_restore() + + hide_material_menu() + + +func _on_MaterialMenu_Confirm_pressed(): + var voxel = get_viewing_voxel() + Voxel.clean(voxel) + + undo_redo.create_action("VoxelViewer : Set voxel material") + undo_redo.add_do_method(voxel_set, "set_voxel", voxel_id, voxel) + undo_redo.add_undo_method(voxel_set, "set_voxel", voxel_id, _unedited_voxel) + undo_redo.add_do_method(voxel_set, "request_refresh") + undo_redo.add_undo_method(voxel_set, "request_refresh") + undo_redo.commit_action() + + hide_material_menu() + + +func _on_EnvironmentMenu_file_selected(path): + var _environment = load(path) + if _environment is Environment: + set_environment(_environment) + property_list_changed_notify() + save_environment() diff --git a/addons/voxel-core/controls/voxel_viewer/voxel_viewer.tscn b/addons/voxel-core/controls/voxel_viewer/voxel_viewer.tscn new file mode 100644 index 0000000..5ed3914 --- /dev/null +++ b/addons/voxel-core/controls/voxel_viewer/voxel_viewer.tscn @@ -0,0 +1,475 @@ +[gd_scene load_steps=9 format=2] + +[ext_resource path="res://addons/voxel-core/controls/voxel_viewer/voxel_viewer.gd" type="Script" id=1] +[ext_resource path="res://addons/voxel-core/controls/voxel_button/voxel_button.tscn" type="PackedScene" id=2] +[ext_resource path="res://addons/voxel-core/controls/tiles_viewer/tiles_viewer.tscn" type="PackedScene" id=3] +[ext_resource path="res://addons/voxel-core/controls/voxel_viewer/voxel_viewer_env.tres" type="Environment" id=4] + +[sub_resource type="World" id=1] +environment = ExtResource( 4 ) + +[sub_resource type="CubeMesh" id=2] +size = Vector3( 0.5, 0.5, 0.5 ) + +[sub_resource type="BoxShape" id=3] +extents = Vector3( 0.25, 0.25, 0.25 ) + +[sub_resource type="SpatialMaterial" id=4] +flags_transparent = true +params_grow = true +params_grow_amount = 0.001 +albedo_color = Color( 0, 0.25, 1, 0.607843 ) + +[node name="VoxelViewer" type="Control"] +anchor_right = 1.0 +anchor_bottom = 1.0 +script = ExtResource( 1 ) +__meta__ = { +"_edit_use_anchors_": false +} +environment = ExtResource( 4 ) + +[node name="View2D" type="Control" parent="."] +visible = false +anchor_right = 1.0 +anchor_bottom = 1.0 +rect_clip_content = true +__meta__ = { +"_edit_use_anchors_": false +} + +[node name="Left" parent="View2D" instance=ExtResource( 2 )] +anchor_left = 0.176758 +anchor_top = 0.366667 +anchor_right = 0.34082 +anchor_bottom = 0.646667 +toggle_mode = true + +[node name="Front" parent="View2D" instance=ExtResource( 2 )] +anchor_left = 0.34082 +anchor_top = 0.366667 +anchor_right = 0.504883 +anchor_bottom = 0.646667 +toggle_mode = true + +[node name="Right" parent="View2D" instance=ExtResource( 2 )] +anchor_left = 0.504883 +anchor_top = 0.366666 +anchor_right = 0.668945 +anchor_bottom = 0.646666 +margin_left = -1.0 +margin_top = 0.000335693 +margin_right = -1.0 +margin_bottom = 0.000335693 +toggle_mode = true + +[node name="Back" parent="View2D" instance=ExtResource( 2 )] +anchor_left = 0.668945 +anchor_top = 0.366667 +anchor_right = 0.833008 +anchor_bottom = 0.646667 +margin_left = -0.999939 +margin_top = 1.52588e-05 +margin_right = -0.999939 +toggle_mode = true + +[node name="Top" parent="View2D" instance=ExtResource( 2 )] +anchor_left = 0.34082 +anchor_top = 0.0866667 +anchor_right = 0.504883 +anchor_bottom = 0.366666 +toggle_mode = true + +[node name="Bottom" parent="View2D" instance=ExtResource( 2 )] +anchor_left = 0.34082 +anchor_top = 0.646667 +anchor_right = 0.504883 +anchor_bottom = 0.926667 +toggle_mode = true + +[node name="View3D" type="ViewportContainer" parent="."] +anchor_right = 1.0 +anchor_bottom = 1.0 +size_flags_horizontal = 3 +size_flags_vertical = 3 +stretch = true +__meta__ = { +"_edit_use_anchors_": false +} + +[node name="Viewport" type="Viewport" parent="View3D"] +size = Vector2( 1024, 600 ) +own_world = true +world = SubResource( 1 ) +transparent_bg = true +handle_input_locally = false +render_target_update_mode = 3 +physics_object_picking = true + +[node name="CameraPivot" type="Spatial" parent="View3D/Viewport"] + +[node name="Camera" type="Camera" parent="View3D/Viewport/CameraPivot"] +transform = Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1 ) +current = true + +[node name="OmniLight" type="OmniLight" parent="View3D/Viewport/CameraPivot"] +transform = Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 0.5, 0, 0.7 ) +omni_range = 1.1122 + +[node name="VoxelPreview" type="MeshInstance" parent="View3D/Viewport"] +mesh = SubResource( 2 ) +material/0 = null + +[node name="VoxelStaticBody" type="StaticBody" parent="View3D/Viewport/VoxelPreview"] + +[node name="CollisionShape" type="CollisionShape" parent="View3D/Viewport/VoxelPreview/VoxelStaticBody"] +shape = SubResource( 3 ) + +[node name="Select" type="MeshInstance" parent="View3D/Viewport"] +material_override = SubResource( 4 ) +mesh = SubResource( 2 ) +material/0 = null + +[node name="ToolBar" type="HBoxContainer" parent="."] +margin_left = 12.0 +margin_top = 12.0 +margin_right = 244.0 +margin_bottom = 32.0 +__meta__ = { +"_edit_use_anchors_": false +} + +[node name="ViewMode" type="OptionButton" parent="ToolBar"] +margin_right = 64.0 +margin_bottom = 20.0 +rect_min_size = Vector2( 64, 20 ) +size_flags_horizontal = 0 +text = "3D" +align = 1 +items = [ "2D", null, false, 0, null, "3D", null, false, 1, null ] +selected = 1 + +[node name="Hint" type="Label" parent="ToolBar"] +margin_left = 68.0 +margin_right = 232.0 +margin_bottom = 20.0 +rect_min_size = Vector2( 164, 20 ) +size_flags_horizontal = 3 +size_flags_vertical = 3 +valign = 1 +__meta__ = { +"_edit_use_anchors_": false +} + +[node name="ContextMenu" type="PopupMenu" parent="."] +anchor_right = 0.0878906 +anchor_bottom = 0.206667 +items = [ "Color side", null, 0, false, false, 0, 0, null, "", false, "Remove side's color", null, 0, false, false, 1, 0, null, "", false, "Texture side", null, 0, false, false, 2, 0, null, "", false, "Remove side's texture", null, 0, false, false, 3, 0, null, "", false, "", null, 0, false, false, -1, 0, null, "", true, "Color voxel", null, 0, false, false, 4, 0, null, "", false, "Texture voxel", null, 0, false, false, 5, 0, null, "", false, "Remove voxel's texture", null, 0, false, false, 6, 0, null, "", false ] +__meta__ = { +"_edit_use_anchors_": true +} + +[node name="ColorMenu" type="PopupDialog" parent="."] +margin_right = 324.0 +margin_bottom = 502.0 +rect_min_size = Vector2( 340, 525 ) +popup_exclusive = true +__meta__ = { +"_edit_use_anchors_": false +} + +[node name="VBoxContainer" type="VBoxContainer" parent="ColorMenu"] +anchor_right = 1.0 +anchor_bottom = 1.0 +margin_left = 8.0 +margin_top = 8.0 +margin_right = -8.00003 +margin_bottom = -8.00003 +__meta__ = { +"_edit_use_anchors_": false +} + +[node name="VoxelColor" type="ColorPicker" parent="ColorMenu/VBoxContainer"] +margin_left = 60.0 +margin_top = 60.0 +margin_right = 384.0 +margin_bottom = 545.0 +size_flags_horizontal = 3 +size_flags_vertical = 3 +edit_alpha = false +__meta__ = { +"_edit_use_anchors_": false +} + +[node name="HBoxContainer" type="HBoxContainer" parent="ColorMenu/VBoxContainer"] +margin_top = 489.0 +margin_right = 324.0 +margin_bottom = 509.0 + +[node name="Cancel" type="Button" parent="ColorMenu/VBoxContainer/HBoxContainer"] +margin_right = 160.0 +margin_bottom = 20.0 +size_flags_horizontal = 3 +text = "Cancel" + +[node name="Confirm" type="Button" parent="ColorMenu/VBoxContainer/HBoxContainer"] +margin_left = 164.0 +margin_right = 324.0 +margin_bottom = 20.0 +size_flags_horizontal = 3 +text = "Confirm" + +[node name="TextureMenu" type="PopupDialog" parent="."] +margin_right = 324.0 +margin_bottom = 260.0 +rect_min_size = Vector2( 324, 260 ) +popup_exclusive = true + +[node name="VBoxContainer" type="VBoxContainer" parent="TextureMenu"] +anchor_right = 1.0 +anchor_bottom = 1.0 +margin_left = 8.0 +margin_top = 8.0 +margin_right = -8.00003 +margin_bottom = -8.00003 +__meta__ = { +"_edit_use_anchors_": false +} + +[node name="ScrollContainer" type="ScrollContainer" parent="TextureMenu/VBoxContainer"] +margin_right = 307.0 +margin_bottom = 219.0 +size_flags_horizontal = 3 +size_flags_vertical = 3 + +[node name="VoxelTexture" parent="TextureMenu/VBoxContainer/ScrollContainer" instance=ExtResource( 3 )] +anchor_right = 0.0 +anchor_bottom = 0.0 +margin_right = 307.0 +margin_bottom = 219.0 +size_flags_horizontal = 3 +size_flags_vertical = 3 +selection_max = 1 + +[node name="HBoxContainer" type="HBoxContainer" parent="TextureMenu/VBoxContainer"] +margin_top = 223.0 +margin_right = 307.0 +margin_bottom = 243.0 + +[node name="Cancel" type="Button" parent="TextureMenu/VBoxContainer/HBoxContainer"] +margin_right = 151.0 +margin_bottom = 20.0 +size_flags_horizontal = 3 +text = "Cancel" + +[node name="Confirm" type="Button" parent="TextureMenu/VBoxContainer/HBoxContainer"] +margin_left = 155.0 +margin_right = 307.0 +margin_bottom = 20.0 +size_flags_horizontal = 3 +text = "Confirm" + +[node name="MaterialMenu" type="PopupDialog" parent="."] +margin_right = 250.0 +margin_bottom = 325.0 +rect_min_size = Vector2( 250, 325 ) +size_flags_horizontal = 0 +size_flags_vertical = 0 +popup_exclusive = true +__meta__ = { +"_edit_use_anchors_": false +} + +[node name="VBoxContainer" type="VBoxContainer" parent="MaterialMenu"] +anchor_right = 1.0 +anchor_bottom = 1.0 +margin_left = 8.0 +margin_top = 8.0 +margin_right = -8.00003 +margin_bottom = -8.00003 +__meta__ = { +"_edit_use_anchors_": false +} + +[node name="VBoxContainer" type="VBoxContainer" parent="MaterialMenu/VBoxContainer"] +margin_right = 233.0 +margin_bottom = 284.0 +size_flags_horizontal = 3 +size_flags_vertical = 3 +custom_constants/separation = 16 +alignment = 1 + +[node name="HBoxContainer6" type="HBoxContainer" parent="MaterialMenu/VBoxContainer/VBoxContainer"] +margin_top = 22.0 +margin_right = 233.0 +margin_bottom = 46.0 + +[node name="Label" type="Label" parent="MaterialMenu/VBoxContainer/VBoxContainer/HBoxContainer6"] +margin_top = 5.0 +margin_right = 56.0 +margin_bottom = 19.0 +text = "Material:" + +[node name="Material" type="SpinBox" parent="MaterialMenu/VBoxContainer/VBoxContainer/HBoxContainer6"] +margin_left = 159.0 +margin_right = 233.0 +margin_bottom = 24.0 +size_flags_horizontal = 10 +min_value = -1.0 +rounded = true + +[node name="HSeparator" type="HSeparator" parent="MaterialMenu/VBoxContainer/VBoxContainer"] +margin_top = 62.0 +margin_right = 233.0 +margin_bottom = 66.0 + +[node name="HBoxContainer" type="HBoxContainer" parent="MaterialMenu/VBoxContainer/VBoxContainer"] +margin_top = 82.0 +margin_right = 233.0 +margin_bottom = 106.0 + +[node name="Label" type="Label" parent="MaterialMenu/VBoxContainer/VBoxContainer/HBoxContainer"] +margin_top = 5.0 +margin_right = 55.0 +margin_bottom = 19.0 +text = "Metallic:" + +[node name="Metallic" type="SpinBox" parent="MaterialMenu/VBoxContainer/VBoxContainer/HBoxContainer"] +margin_left = 159.0 +margin_right = 233.0 +margin_bottom = 24.0 +size_flags_horizontal = 10 +max_value = 1.0 +step = 0.01 + +[node name="HBoxContainer2" type="HBoxContainer" parent="MaterialMenu/VBoxContainer/VBoxContainer"] +margin_top = 122.0 +margin_right = 233.0 +margin_bottom = 146.0 + +[node name="Label" type="Label" parent="MaterialMenu/VBoxContainer/VBoxContainer/HBoxContainer2"] +margin_top = 5.0 +margin_right = 58.0 +margin_bottom = 19.0 +text = "Specular:" + +[node name="Specular" type="SpinBox" parent="MaterialMenu/VBoxContainer/VBoxContainer/HBoxContainer2"] +margin_left = 159.0 +margin_right = 233.0 +margin_bottom = 24.0 +size_flags_horizontal = 10 +max_value = 1.0 +step = 0.01 + +[node name="HBoxContainer3" type="HBoxContainer" parent="MaterialMenu/VBoxContainer/VBoxContainer"] +margin_top = 162.0 +margin_right = 233.0 +margin_bottom = 186.0 + +[node name="Label" type="Label" parent="MaterialMenu/VBoxContainer/VBoxContainer/HBoxContainer3"] +margin_top = 5.0 +margin_right = 73.0 +margin_bottom = 19.0 +text = "Roughness:" + +[node name="Roughness" type="SpinBox" parent="MaterialMenu/VBoxContainer/VBoxContainer/HBoxContainer3"] +margin_left = 159.0 +margin_right = 233.0 +margin_bottom = 24.0 +size_flags_horizontal = 10 +max_value = 1.0 +step = 0.01 + +[node name="HBoxContainer4" type="HBoxContainer" parent="MaterialMenu/VBoxContainer/VBoxContainer"] +margin_top = 202.0 +margin_right = 233.0 +margin_bottom = 226.0 + +[node name="Label" type="Label" parent="MaterialMenu/VBoxContainer/VBoxContainer/HBoxContainer4"] +margin_top = 5.0 +margin_right = 46.0 +margin_bottom = 19.0 +text = "Energy:" + +[node name="Energy" type="SpinBox" parent="MaterialMenu/VBoxContainer/VBoxContainer/HBoxContainer4"] +margin_left = 159.0 +margin_right = 233.0 +margin_bottom = 24.0 +size_flags_horizontal = 10 +max_value = 16.0 +step = 0.1 + +[node name="HBoxContainer5" type="HBoxContainer" parent="MaterialMenu/VBoxContainer/VBoxContainer"] +margin_top = 242.0 +margin_right = 233.0 +margin_bottom = 262.0 + +[node name="Label" type="Label" parent="MaterialMenu/VBoxContainer/VBoxContainer/HBoxContainer5"] +margin_top = 3.0 +margin_right = 83.0 +margin_bottom = 17.0 +text = "Energy Color:" + +[node name="EnergyColor" type="ColorPickerButton" parent="MaterialMenu/VBoxContainer/VBoxContainer/HBoxContainer5"] +margin_left = 156.0 +margin_right = 233.0 +margin_bottom = 20.0 +rect_min_size = Vector2( 77, 20 ) +size_flags_horizontal = 10 +edit_alpha = false + +[node name="HBoxContainer" type="HBoxContainer" parent="MaterialMenu/VBoxContainer"] +margin_top = 288.0 +margin_right = 233.0 +margin_bottom = 308.0 + +[node name="Cancel" type="Button" parent="MaterialMenu/VBoxContainer/HBoxContainer"] +margin_right = 114.0 +margin_bottom = 20.0 +size_flags_horizontal = 3 +text = "Cancel" + +[node name="Confirm" type="Button" parent="MaterialMenu/VBoxContainer/HBoxContainer"] +margin_left = 118.0 +margin_right = 233.0 +margin_bottom = 20.0 +size_flags_horizontal = 3 +text = "Confirm" + +[node name="EnvironmentMenu" type="FileDialog" parent="."] +margin_right = 315.0 +margin_bottom = 349.0 +rect_min_size = Vector2( 200, 350 ) +window_title = "Open a File" +mode = 0 +filters = PoolStringArray( "*.tres; Environment files" ) +[connection signal="gui_input" from="View2D/Left" to="." method="_on_Face_gui_input" binds= [ Vector3( -1, 0, 0 ) ]] +[connection signal="mouse_exited" from="View2D/Left" to="." method="_set_last_hovered_face" binds= [ Vector3( 0, 0, 0 ) ]] +[connection signal="gui_input" from="View2D/Front" to="." method="_on_Face_gui_input" binds= [ Vector3( 0, 0, -1 ) ]] +[connection signal="mouse_exited" from="View2D/Front" to="." method="_set_last_hovered_face" binds= [ Vector3( 0, 0, 0 ) ]] +[connection signal="gui_input" from="View2D/Right" to="." method="_on_Face_gui_input" binds= [ Vector3( 1, 0, 0 ) ]] +[connection signal="mouse_exited" from="View2D/Right" to="." method="_set_last_hovered_face" binds= [ Vector3( 0, 0, 0 ) ]] +[connection signal="gui_input" from="View2D/Back" to="." method="_on_Face_gui_input" binds= [ Vector3( 0, 0, 1 ) ]] +[connection signal="mouse_exited" from="View2D/Back" to="." method="_set_last_hovered_face" binds= [ Vector3( 0, 0, 0 ) ]] +[connection signal="gui_input" from="View2D/Top" to="." method="_on_Face_gui_input" binds= [ Vector3( 0, 1, 0 ) ]] +[connection signal="mouse_exited" from="View2D/Top" to="." method="_set_last_hovered_face" binds= [ Vector3( 0, 0, 0 ) ]] +[connection signal="gui_input" from="View2D/Bottom" to="." method="_on_Face_gui_input" binds= [ Vector3( 0, -1, 0 ) ]] +[connection signal="mouse_exited" from="View2D/Bottom" to="." method="_set_last_hovered_face" binds= [ Vector3( 0, 0, 0 ) ]] +[connection signal="gui_input" from="View3D" to="." method="_on_View3D_gui_input"] +[connection signal="item_selected" from="ToolBar/ViewMode" to="." method="set_view_mode"] +[connection signal="id_pressed" from="ContextMenu" to="." method="_on_ContextMenu_id_pressed"] +[connection signal="color_changed" from="ColorMenu/VBoxContainer/VoxelColor" to="." method="_on_ColorPicker_color_changed"] +[connection signal="pressed" from="ColorMenu/VBoxContainer/HBoxContainer/Cancel" to="." method="_on_ColorMenu_Cancel_pressed"] +[connection signal="pressed" from="ColorMenu/VBoxContainer/HBoxContainer/Confirm" to="." method="_on_ColorMenu_Confirm_pressed"] +[connection signal="selected_uv" from="TextureMenu/VBoxContainer/ScrollContainer/VoxelTexture" to="." method="_on_VoxelTexture_selected_uv"] +[connection signal="pressed" from="TextureMenu/VBoxContainer/HBoxContainer/Cancel" to="." method="_on_TextureMenu_Cancel_pressed"] +[connection signal="pressed" from="TextureMenu/VBoxContainer/HBoxContainer/Confirm" to="." method="_on_TextureMenu_Confirm_pressed"] +[connection signal="value_changed" from="MaterialMenu/VBoxContainer/VBoxContainer/HBoxContainer6/Material" to="." method="_on_Material_value_changed"] +[connection signal="value_changed" from="MaterialMenu/VBoxContainer/VBoxContainer/HBoxContainer/Metallic" to="." method="_on_Metallic_value_changed"] +[connection signal="value_changed" from="MaterialMenu/VBoxContainer/VBoxContainer/HBoxContainer2/Specular" to="." method="_on_Specular_value_changed"] +[connection signal="value_changed" from="MaterialMenu/VBoxContainer/VBoxContainer/HBoxContainer3/Roughness" to="." method="_on_Roughness_value_changed"] +[connection signal="value_changed" from="MaterialMenu/VBoxContainer/VBoxContainer/HBoxContainer4/Energy" to="." method="_on_Energy_value_changed"] +[connection signal="color_changed" from="MaterialMenu/VBoxContainer/VBoxContainer/HBoxContainer5/EnergyColor" to="." method="_on_EnergyColor_changed"] +[connection signal="pressed" from="MaterialMenu/VBoxContainer/HBoxContainer/Cancel" to="." method="_on_MaterialMenu_Cancel_pressed"] +[connection signal="pressed" from="MaterialMenu/VBoxContainer/HBoxContainer/Confirm" to="." method="_on_MaterialMenu_Confirm_pressed"] +[connection signal="file_selected" from="EnvironmentMenu" to="." method="_on_EnvironmentMenu_file_selected"] diff --git a/addons/voxel-core/controls/voxel_viewer/voxel_viewer_env.tres b/addons/voxel-core/controls/voxel_viewer/voxel_viewer_env.tres new file mode 100644 index 0000000..ed6528c --- /dev/null +++ b/addons/voxel-core/controls/voxel_viewer/voxel_viewer_env.tres @@ -0,0 +1,4 @@ +[gd_resource type="Environment" format=2] + +[resource] +ambient_light_color = Color( 1, 1, 1, 1 ) |