feat(studio,stacking,animations,collections): M16 ModBison Studio, object stacking, model animations, and poly collections for MVP 2
checkpoint / checkpoint (push) Successful in 6m32s
checkpoint / checkpoint (push) Successful in 6m32s
This commit is contained in:
@@ -0,0 +1,185 @@
|
||||
class_name StudioInspectorDock
|
||||
extends VBoxContainer
|
||||
## Property Inspector Dock for ModBison Studio.
|
||||
## Renders editable properties for selected world object(s) with undo/redo support.
|
||||
|
||||
signal property_changed(object_id: String, key: String, value: Variant)
|
||||
signal delete_requested(object_id: String)
|
||||
|
||||
var _core: EditorCore = null
|
||||
var _selected_id: String = ""
|
||||
var _updating: bool = false
|
||||
|
||||
var _title_label: Label
|
||||
var _empty_label: Label
|
||||
var _scroll: ScrollContainer
|
||||
var _content_vbox: VBoxContainer
|
||||
|
||||
# Transform fields
|
||||
var _pos_x: SpinBox
|
||||
var _pos_y: SpinBox
|
||||
var _pos_z: SpinBox
|
||||
var _rot_y: SpinBox
|
||||
var _scale_x: SpinBox
|
||||
var _scale_y: SpinBox
|
||||
var _scale_z: SpinBox
|
||||
|
||||
# Metadata
|
||||
var _name_edit: LineEdit
|
||||
var _asset_label: Label
|
||||
|
||||
|
||||
func _init() -> void:
|
||||
name = "StudioInspectorDock"
|
||||
custom_minimum_size = Vector2(240, 300)
|
||||
size_flags_vertical = Control.SIZE_EXPAND_FILL
|
||||
_build_ui()
|
||||
|
||||
|
||||
func setup(core: EditorCore) -> void:
|
||||
_core = core
|
||||
if _core != null and _core.selection != null:
|
||||
_core.selection.selection_changed.connect(_on_selection_changed)
|
||||
|
||||
|
||||
func _build_ui() -> void:
|
||||
_title_label = Label.new()
|
||||
_title_label.text = "Properties"
|
||||
_title_label.add_theme_font_size_override("font_size", 13)
|
||||
_title_label.add_theme_color_override("font_color", Color(1.0, 0.9, 0.4))
|
||||
add_child(_title_label)
|
||||
|
||||
_empty_label = Label.new()
|
||||
_empty_label.text = "(No selection)"
|
||||
_empty_label.add_theme_font_size_override("font_size", 11)
|
||||
_empty_label.add_theme_color_override("font_color", Color(0.6, 0.6, 0.6))
|
||||
add_child(_empty_label)
|
||||
|
||||
_scroll = ScrollContainer.new()
|
||||
_scroll.size_flags_vertical = Control.SIZE_EXPAND_FILL
|
||||
_scroll.horizontal_scroll_mode = ScrollContainer.SCROLL_MODE_DISABLED
|
||||
add_child(_scroll)
|
||||
|
||||
_content_vbox = VBoxContainer.new()
|
||||
_content_vbox.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||||
_content_vbox.visible = false
|
||||
_scroll.add_child(_content_vbox)
|
||||
|
||||
# Name field
|
||||
var name_lbl := Label.new()
|
||||
name_lbl.text = "Display Name:"
|
||||
name_lbl.add_theme_font_size_override("font_size", 10)
|
||||
_content_vbox.add_child(name_lbl)
|
||||
_name_edit = LineEdit.new()
|
||||
_name_edit.text_submitted.connect(_on_name_changed)
|
||||
_content_vbox.add_child(_name_edit)
|
||||
|
||||
_asset_label = Label.new()
|
||||
_asset_label.text = "Asset: None"
|
||||
_asset_label.add_theme_font_size_override("font_size", 10)
|
||||
_asset_label.add_theme_color_override("font_color", Color(0.7, 0.7, 0.7))
|
||||
_content_vbox.add_child(_asset_label)
|
||||
|
||||
# Transform header
|
||||
var t_header := Label.new()
|
||||
t_header.text = "Transform"
|
||||
t_header.add_theme_font_size_override("font_size", 11)
|
||||
t_header.add_theme_color_override("font_color", Color(0.35, 0.85, 1.0))
|
||||
_content_vbox.add_child(t_header)
|
||||
|
||||
_pos_x = _add_spin("Pos X", _on_pos_changed)
|
||||
_pos_y = _add_spin("Pos Y", _on_pos_changed)
|
||||
_pos_z = _add_spin("Pos Z", _on_pos_changed)
|
||||
|
||||
_rot_y = _add_spin("Yaw (°)", _on_rot_changed, -360.0, 360.0, 1.0)
|
||||
|
||||
_scale_x = _add_spin("Scale X", _on_scale_changed, 0.05, 50.0, 0.1)
|
||||
_scale_y = _add_spin("Scale Y", _on_scale_changed, 0.05, 50.0, 0.1)
|
||||
_scale_z = _add_spin("Scale Z", _on_scale_changed, 0.05, 50.0, 0.1)
|
||||
|
||||
|
||||
func _add_spin(label: String, callback: Callable, min_val: float = -500.0, max_val: float = 500.0, step: float = 0.5) -> SpinBox:
|
||||
var row := HBoxContainer.new()
|
||||
var lbl := Label.new()
|
||||
lbl.text = label + ":"
|
||||
lbl.custom_minimum_size = Vector2(70, 0)
|
||||
lbl.add_theme_font_size_override("font_size", 10)
|
||||
row.add_child(lbl)
|
||||
|
||||
var spin := SpinBox.new()
|
||||
spin.min_value = min_val
|
||||
spin.max_value = max_val
|
||||
spin.step = step
|
||||
spin.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||||
spin.value_changed.connect(callback)
|
||||
row.add_child(spin)
|
||||
_content_vbox.add_child(row)
|
||||
return spin
|
||||
|
||||
|
||||
func _on_selection_changed(obj_id: String) -> void:
|
||||
_selected_id = obj_id
|
||||
if _selected_id == "" or _core == null:
|
||||
_empty_label.visible = true
|
||||
_content_vbox.visible = false
|
||||
return
|
||||
|
||||
var obj = _core.model.find_object(_selected_id)
|
||||
if obj == null:
|
||||
_empty_label.visible = true
|
||||
_content_vbox.visible = false
|
||||
return
|
||||
|
||||
_updating = true
|
||||
_empty_label.visible = false
|
||||
_content_vbox.visible = true
|
||||
|
||||
var dname: String = str(obj.id)
|
||||
if obj.properties is Dictionary and obj.properties.has("display_name"):
|
||||
dname = str(obj.properties["display_name"])
|
||||
_name_edit.text = dname
|
||||
_asset_label.text = "Asset: %s" % obj.asset_id
|
||||
|
||||
_pos_x.value = obj.position.x
|
||||
_pos_y.value = obj.position.y
|
||||
_pos_z.value = obj.position.z
|
||||
|
||||
var euler = Quaternion(obj.rotation.x, obj.rotation.y, obj.rotation.z, obj.rotation.w).get_euler()
|
||||
_rot_y.value = rad_to_deg(euler.y)
|
||||
|
||||
_scale_x.value = obj.scale.x
|
||||
_scale_y.value = obj.scale.y
|
||||
_scale_z.value = obj.scale.z
|
||||
_updating = false
|
||||
|
||||
|
||||
func _on_name_changed(new_name: String) -> void:
|
||||
if _updating or _core == null or _selected_id == "":
|
||||
return
|
||||
_core.set_object_property(_selected_id, "display_name", new_name)
|
||||
property_changed.emit(_selected_id, "display_name", new_name)
|
||||
|
||||
|
||||
func _on_pos_changed(_val: float) -> void:
|
||||
if _updating or _core == null or _selected_id == "":
|
||||
return
|
||||
var new_pos := Vector3(_pos_x.value, _pos_y.value, _pos_z.value)
|
||||
_core.move_object(_selected_id, new_pos)
|
||||
property_changed.emit(_selected_id, "position", new_pos)
|
||||
|
||||
|
||||
func _on_rot_changed(_val: float) -> void:
|
||||
if _updating or _core == null or _selected_id == "":
|
||||
return
|
||||
var quat := Quaternion.from_euler(Vector3(0, deg_to_rad(_rot_y.value), 0))
|
||||
var rot_vec := Vector4(quat.x, quat.y, quat.z, quat.w)
|
||||
_core.rotate_object(_selected_id, rot_vec)
|
||||
property_changed.emit(_selected_id, "rotation", rot_vec)
|
||||
|
||||
|
||||
func _on_scale_changed(_val: float) -> void:
|
||||
if _updating or _core == null or _selected_id == "":
|
||||
return
|
||||
var new_scale := Vector3(_scale_x.value, _scale_y.value, _scale_z.value)
|
||||
_core.scale_object(_selected_id, new_scale)
|
||||
property_changed.emit(_selected_id, "scale", new_scale)
|
||||
Reference in New Issue
Block a user