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,535 @@
|
||||
class_name StudioMain
|
||||
extends Control
|
||||
## Standalone ModBison Studio Application Shell.
|
||||
## Implements multi-panel docking layout, top menu bar with keyboard shortcuts,
|
||||
## 3D interactive viewport with gizmos, hierarchy, inspector, collections dock,
|
||||
## and simulation harness.
|
||||
|
||||
const MBWorldProjectIOClass = preload("res://scripts/studio/mbworld_project_io.gd")
|
||||
const StudioViewportCameraClass = preload("res://scripts/studio/studio_viewport_camera.gd")
|
||||
const TransformGizmo3DClass = preload("res://scripts/studio/gizmos/transform_gizmo_3d.gd")
|
||||
const StudioHierarchyDockClass = preload("res://scripts/studio/studio_hierarchy_dock.gd")
|
||||
const StudioInspectorDockClass = preload("res://scripts/studio/studio_inspector_dock.gd")
|
||||
const StudioCollectionsDockClass = preload("res://scripts/studio/studio_collections_dock.gd")
|
||||
const StudioSimulationManagerClass = preload("res://scripts/studio/studio_simulation_manager.gd")
|
||||
const StaticObjectNodeClass = preload("res://scripts/static_object_node.gd")
|
||||
const WorldObjectClass = preload("res://scripts/world_object.gd")
|
||||
|
||||
# Core state
|
||||
var core: EditorCore = null
|
||||
var current_project_path: String = ""
|
||||
var project_title: String = "Untitled World"
|
||||
|
||||
# Top Menu & Toolbar
|
||||
var _menu_bar: MenuBar
|
||||
var _toolbar: HBoxContainer
|
||||
var _btn_select: Button
|
||||
var _btn_move: Button
|
||||
var _btn_rotate: Button
|
||||
var _btn_scale: Button
|
||||
var _btn_play: Button
|
||||
var _btn_stop: Button
|
||||
var _snap_spin: SpinBox
|
||||
|
||||
# Main Viewport & 3D Elements
|
||||
var _sub_viewport: SubViewport
|
||||
var _camera_3d: Camera3D
|
||||
var _world_container: Node3D
|
||||
var _gizmo_3d: Node3D
|
||||
|
||||
# Docks
|
||||
var _hierarchy_dock = null
|
||||
var _inspector_dock = null
|
||||
var _collections_dock = null
|
||||
var _simulation_mgr = null
|
||||
var _status_bar_label: Label
|
||||
var _is_built: bool = false
|
||||
|
||||
|
||||
func _init() -> void:
|
||||
name = "StudioMain"
|
||||
if not _is_built:
|
||||
_init_core_model()
|
||||
_build_studio_layout()
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
if not _is_built:
|
||||
_init_core_model()
|
||||
_build_studio_layout()
|
||||
_setup_shortcuts()
|
||||
_parse_cmdline_arguments()
|
||||
|
||||
|
||||
func _init_core_model() -> void:
|
||||
var payload := ChunkDataDecoder.ChunkPayload.new()
|
||||
var layer := ChunkDataDecoder.TileLayerData.new()
|
||||
layer.name = "ground"
|
||||
layer.layer_id = 0
|
||||
layer.width = 32
|
||||
layer.height = 32
|
||||
layer.depth = 1
|
||||
layer.tiles = PackedInt32Array()
|
||||
layer.tiles.resize(32 * 32)
|
||||
payload.tile_layers.append(layer)
|
||||
|
||||
var model := ChunkEditorModel.new(payload)
|
||||
var catalog := EditorObjectCatalog.new()
|
||||
catalog.load_catalog()
|
||||
core = EditorCore.new(model, catalog)
|
||||
|
||||
|
||||
func _build_studio_layout() -> void:
|
||||
if _is_built:
|
||||
return
|
||||
_is_built = true
|
||||
set_anchors_preset(Control.PRESET_FULL_RECT)
|
||||
|
||||
var bg := ColorRect.new()
|
||||
bg.set_anchors_preset(Control.PRESET_FULL_RECT)
|
||||
bg.color = Color(0.14, 0.15, 0.18)
|
||||
add_child(bg)
|
||||
|
||||
var main_vbox := VBoxContainer.new()
|
||||
main_vbox.set_anchors_preset(Control.PRESET_FULL_RECT)
|
||||
add_child(main_vbox)
|
||||
|
||||
# 1. Top Menu Bar
|
||||
_build_top_menu_bar(main_vbox)
|
||||
|
||||
# 2. Main Action Toolbar
|
||||
_build_toolbar(main_vbox)
|
||||
|
||||
# 3. Center Workspace (Horizontal Split: Left Dock | Center Viewport | Right Docks)
|
||||
var workspace_hbox := HBoxContainer.new()
|
||||
workspace_hbox.size_flags_vertical = Control.SIZE_EXPAND_FILL
|
||||
main_vbox.add_child(workspace_hbox)
|
||||
|
||||
# Left Dock: Hierarchy
|
||||
var left_panel := PanelContainer.new()
|
||||
left_panel.custom_minimum_size = Vector2(240, 0)
|
||||
left_panel.size_flags_vertical = Control.SIZE_EXPAND_FILL
|
||||
_hierarchy_dock = StudioHierarchyDockClass.new()
|
||||
_hierarchy_dock.setup(core)
|
||||
left_panel.add_child(_hierarchy_dock)
|
||||
workspace_hbox.add_child(left_panel)
|
||||
|
||||
# Center: 3D Viewport
|
||||
var vp_container := SubViewportContainer.new()
|
||||
vp_container.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||||
vp_container.size_flags_vertical = Control.SIZE_EXPAND_FILL
|
||||
vp_container.stretch = true
|
||||
workspace_hbox.add_child(vp_container)
|
||||
|
||||
_sub_viewport = SubViewport.new()
|
||||
_sub_viewport.handle_input_locally = true
|
||||
vp_container.add_child(_sub_viewport)
|
||||
|
||||
_setup_3d_viewport_scene(_sub_viewport)
|
||||
|
||||
# Right Dock: TabContainer with Inspector & Collections
|
||||
var right_panel := PanelContainer.new()
|
||||
right_panel.custom_minimum_size = Vector2(280, 0)
|
||||
right_panel.size_flags_vertical = Control.SIZE_EXPAND_FILL
|
||||
var tabs := TabContainer.new()
|
||||
tabs.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||||
tabs.size_flags_vertical = Control.SIZE_EXPAND_FILL
|
||||
|
||||
_inspector_dock = StudioInspectorDockClass.new()
|
||||
_inspector_dock.setup(core)
|
||||
tabs.add_child(_inspector_dock)
|
||||
tabs.set_tab_title(0, "Inspector")
|
||||
|
||||
_collections_dock = StudioCollectionsDockClass.new()
|
||||
_collections_dock.model_selected.connect(_on_collection_model_selected)
|
||||
tabs.add_child(_collections_dock)
|
||||
tabs.set_tab_title(1, "Collections")
|
||||
|
||||
right_panel.add_child(tabs)
|
||||
workspace_hbox.add_child(right_panel)
|
||||
|
||||
# 4. Bottom Status Bar
|
||||
var status_panel := PanelContainer.new()
|
||||
status_panel.custom_minimum_size = Vector2(0, 24)
|
||||
_status_bar_label = Label.new()
|
||||
_status_bar_label.text = "ModBison Studio Ready | Grid: 1.0m | Mode: Select"
|
||||
_status_bar_label.add_theme_font_size_override("font_size", 10)
|
||||
status_panel.add_child(_status_bar_label)
|
||||
main_vbox.add_child(status_panel)
|
||||
|
||||
# Simulation Manager
|
||||
_simulation_mgr = StudioSimulationManagerClass.new()
|
||||
_simulation_mgr.setup(core, _world_container)
|
||||
_simulation_mgr.simulation_started.connect(func(_m): _update_simulation_ui(true))
|
||||
_simulation_mgr.simulation_stopped.connect(func(): _update_simulation_ui(false))
|
||||
add_child(_simulation_mgr)
|
||||
|
||||
|
||||
func _build_top_menu_bar(parent: Control) -> void:
|
||||
_menu_bar = MenuBar.new()
|
||||
_menu_bar.custom_minimum_size = Vector2(0, 28)
|
||||
|
||||
# File Menu
|
||||
var file_menu := PopupMenu.new()
|
||||
file_menu.name = "File"
|
||||
file_menu.add_item("New Project...", 0, KEY_MASK_CTRL | KEY_N)
|
||||
file_menu.add_item("Open Project...", 1, KEY_MASK_CTRL | KEY_O)
|
||||
file_menu.add_separator()
|
||||
file_menu.add_item("Save (Ctrl+S)", 2, KEY_MASK_CTRL | KEY_S)
|
||||
file_menu.add_item("Save As...", 3)
|
||||
file_menu.add_separator()
|
||||
file_menu.add_item("Publish to Cloud...", 4)
|
||||
file_menu.add_separator()
|
||||
file_menu.add_item("Exit Studio", 5)
|
||||
file_menu.id_pressed.connect(_on_file_menu_pressed)
|
||||
_menu_bar.add_child(file_menu)
|
||||
|
||||
# Edit Menu
|
||||
var edit_menu := PopupMenu.new()
|
||||
edit_menu.name = "Edit"
|
||||
edit_menu.add_item("Undo", 0, KEY_MASK_CTRL | KEY_Z)
|
||||
edit_menu.add_item("Redo", 1, KEY_MASK_CTRL | KEY_Y)
|
||||
edit_menu.add_separator()
|
||||
edit_menu.add_item("Duplicate", 2, KEY_MASK_CTRL | KEY_D)
|
||||
edit_menu.add_item("Delete", 3, KEY_DELETE)
|
||||
edit_menu.id_pressed.connect(_on_edit_menu_pressed)
|
||||
_menu_bar.add_child(edit_menu)
|
||||
|
||||
# View Menu
|
||||
var view_menu := PopupMenu.new()
|
||||
view_menu.name = "View"
|
||||
view_menu.add_item("Perspective", 0, KEY_P)
|
||||
view_menu.add_item("Top View", 1, KEY_7)
|
||||
view_menu.add_item("Front View", 2, KEY_1)
|
||||
view_menu.add_item("Side View", 3, KEY_3)
|
||||
view_menu.add_separator()
|
||||
view_menu.add_item("Focus Selection", 4, KEY_F)
|
||||
view_menu.id_pressed.connect(_on_view_menu_pressed)
|
||||
_menu_bar.add_child(view_menu)
|
||||
|
||||
# Model Menu
|
||||
var model_menu := PopupMenu.new()
|
||||
model_menu.name = "Model"
|
||||
model_menu.add_item("Add Crate Prop", 0)
|
||||
model_menu.add_item("Add Lamp Post", 1)
|
||||
model_menu.add_item("Add Pine Tree", 2)
|
||||
model_menu.add_item("Add ModBison", 3)
|
||||
model_menu.id_pressed.connect(_on_model_menu_pressed)
|
||||
_menu_bar.add_child(model_menu)
|
||||
|
||||
# Test Menu
|
||||
var test_menu := PopupMenu.new()
|
||||
test_menu.name = "Test"
|
||||
test_menu.add_item("Play Solo", 0, KEY_F5)
|
||||
test_menu.add_item("Run Simulation", 1, KEY_F6)
|
||||
test_menu.add_item("Stop Playtest", 2, KEY_MASK_SHIFT | KEY_F5)
|
||||
test_menu.id_pressed.connect(_on_test_menu_pressed)
|
||||
_menu_bar.add_child(test_menu)
|
||||
|
||||
# Publish Menu
|
||||
var pub_menu := PopupMenu.new()
|
||||
pub_menu.name = "Publish"
|
||||
pub_menu.add_item("Export .mbworld Package", 0)
|
||||
pub_menu.add_item("Sync to Game Server", 1)
|
||||
pub_menu.id_pressed.connect(_on_publish_menu_pressed)
|
||||
_menu_bar.add_child(pub_menu)
|
||||
|
||||
parent.add_child(_menu_bar)
|
||||
|
||||
|
||||
func _build_toolbar(parent: Control) -> void:
|
||||
_toolbar = HBoxContainer.new()
|
||||
_toolbar.custom_minimum_size = Vector2(0, 36)
|
||||
_toolbar.add_theme_constant_override("separation", 6)
|
||||
|
||||
_btn_select = _add_tool_btn("↖ Select", func(): _set_gizmo_mode(TransformGizmo3DClass.Mode.NONE))
|
||||
_btn_move = _add_tool_btn("✛ Move", func(): _set_gizmo_mode(TransformGizmo3DClass.Mode.TRANSLATE))
|
||||
_btn_rotate = _add_tool_btn("↻ Rotate", func(): _set_gizmo_mode(TransformGizmo3DClass.Mode.ROTATE))
|
||||
_btn_scale = _add_tool_btn("⤢ Scale", func(): _set_gizmo_mode(TransformGizmo3DClass.Mode.SCALE))
|
||||
|
||||
var vsep := VSeparator.new()
|
||||
_toolbar.add_child(vsep)
|
||||
|
||||
var snap_lbl := Label.new()
|
||||
snap_lbl.text = "Snap:"
|
||||
snap_lbl.add_theme_font_size_override("font_size", 10)
|
||||
_toolbar.add_child(snap_lbl)
|
||||
|
||||
_snap_spin = SpinBox.new()
|
||||
_snap_spin.min_value = 0.1
|
||||
_snap_spin.max_value = 10.0
|
||||
_snap_spin.step = 0.5
|
||||
_snap_spin.value = 1.0
|
||||
_snap_spin.value_changed.connect(func(v):
|
||||
if _gizmo_3d != null:
|
||||
_gizmo_3d.snap_translate = v
|
||||
)
|
||||
_toolbar.add_child(_snap_spin)
|
||||
|
||||
var spacer := Control.new()
|
||||
spacer.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||||
_toolbar.add_child(spacer)
|
||||
|
||||
_btn_play = Button.new()
|
||||
_btn_play.text = "▶ Play (F5)"
|
||||
_btn_play.add_theme_color_override("font_color", Color(0.3, 0.9, 0.4))
|
||||
_btn_play.pressed.connect(func(): _simulation_mgr.start_play(true))
|
||||
_toolbar.add_child(_btn_play)
|
||||
|
||||
_btn_stop = Button.new()
|
||||
_btn_stop.text = "⏹ Stop"
|
||||
_btn_stop.add_theme_color_override("font_color", Color(1.0, 0.4, 0.4))
|
||||
_btn_stop.disabled = true
|
||||
_btn_stop.pressed.connect(func(): _simulation_mgr.stop_simulation())
|
||||
_toolbar.add_child(_btn_stop)
|
||||
|
||||
parent.add_child(_toolbar)
|
||||
|
||||
|
||||
func _add_tool_btn(text: String, callback: Callable) -> Button:
|
||||
var btn := Button.new()
|
||||
btn.text = text
|
||||
btn.pressed.connect(callback)
|
||||
_toolbar.add_child(btn)
|
||||
return btn
|
||||
|
||||
|
||||
func _setup_3d_viewport_scene(vp: SubViewport) -> void:
|
||||
# World Environment
|
||||
var env := WorldEnvironment.new()
|
||||
var environment := Environment.new()
|
||||
environment.background_mode = Environment.BG_COLOR
|
||||
environment.background_color = Color(0.18, 0.22, 0.28)
|
||||
environment.ambient_light_source = Environment.AMBIENT_SOURCE_COLOR
|
||||
environment.ambient_light_color = Color(0.6, 0.6, 0.65)
|
||||
env.environment = environment
|
||||
vp.add_child(env)
|
||||
|
||||
# Directional Sun Light
|
||||
var sun := DirectionalLight3D.new()
|
||||
sun.rotation_degrees = Vector3(-45, 30, 0)
|
||||
sun.shadow_enabled = true
|
||||
vp.add_child(sun)
|
||||
|
||||
# 3D Grid floor
|
||||
var grid := _create_3d_grid(32, 1.0)
|
||||
vp.add_child(grid)
|
||||
|
||||
# Camera
|
||||
_camera_3d = StudioViewportCameraClass.new()
|
||||
vp.add_child(_camera_3d)
|
||||
|
||||
# World Object Container
|
||||
_world_container = Node3D.new()
|
||||
_world_container.name = "WorldContainer"
|
||||
vp.add_child(_world_container)
|
||||
|
||||
# Interactive Gizmo
|
||||
_gizmo_3d = TransformGizmo3DClass.new()
|
||||
_gizmo_3d.name = "TransformGizmo3D"
|
||||
vp.add_child(_gizmo_3d)
|
||||
|
||||
core.selection.selection_changed.connect(_on_selection_changed)
|
||||
|
||||
|
||||
func _create_3d_grid(size: int, step: float) -> Node3D:
|
||||
var mi := MeshInstance3D.new()
|
||||
var imm := ImmediateMesh.new()
|
||||
var mat := StandardMaterial3D.new()
|
||||
mat.albedo_color = Color(0.35, 0.4, 0.45, 0.5)
|
||||
mat.shading_mode = BaseMaterial3D.SHADING_MODE_UNSHADED
|
||||
|
||||
imm.surface_begin(Mesh.PRIMITIVE_LINES, mat)
|
||||
var half := (size * step) / 2.0
|
||||
for i in range(-size / 2, size / 2 + 1):
|
||||
var p := i * step
|
||||
imm.surface_add_vertex(Vector3(p, 0, -half))
|
||||
imm.surface_add_vertex(Vector3(p, 0, half))
|
||||
imm.surface_add_vertex(Vector3(-half, 0, p))
|
||||
imm.surface_add_vertex(Vector3(half, 0, p))
|
||||
imm.surface_end()
|
||||
mi.mesh = imm
|
||||
return mi
|
||||
|
||||
|
||||
func _setup_shortcuts() -> void:
|
||||
set_process_unhandled_key_input(true)
|
||||
|
||||
|
||||
func _unhandled_key_input(event: InputEvent) -> void:
|
||||
var k := event as InputEventKey
|
||||
if not k.pressed or k.echo:
|
||||
return
|
||||
|
||||
if k.is_command_or_control_pressed():
|
||||
if k.keycode == KEY_Z:
|
||||
core.undo()
|
||||
_refresh_scene_visuals()
|
||||
elif k.keycode == KEY_Y:
|
||||
core.redo()
|
||||
_refresh_scene_visuals()
|
||||
elif k.keycode == KEY_S:
|
||||
save_current_project()
|
||||
elif k.keycode == KEY_D:
|
||||
_hierarchy_dock._on_duplicate_pressed()
|
||||
elif k.keycode == KEY_F5:
|
||||
if _simulation_mgr.is_playing():
|
||||
_simulation_mgr.stop_simulation()
|
||||
else:
|
||||
_simulation_mgr.start_play(true)
|
||||
|
||||
|
||||
func _set_gizmo_mode(mode: TransformGizmo3DClass.Mode) -> void:
|
||||
if _gizmo_3d != null:
|
||||
_gizmo_3d.set_mode(mode)
|
||||
_status_bar_label.text = "Gizmo Mode: %s" % str(mode)
|
||||
|
||||
|
||||
func _on_selection_changed(obj_id: String) -> void:
|
||||
if obj_id == "":
|
||||
_gizmo_3d.attach_to_node(null)
|
||||
return
|
||||
|
||||
var obj_node = _world_container.get_node_or_null(obj_id)
|
||||
if obj_node is Node3D:
|
||||
_gizmo_3d.attach_to_node(obj_node)
|
||||
_camera_3d.focus_on(obj_node.global_position)
|
||||
|
||||
|
||||
func _on_collection_model_selected(model_data: Dictionary) -> void:
|
||||
var cat_id := str(model_data.get("catalog_id", "crate_small"))
|
||||
var new_pos := Vector3(0, 0, 0)
|
||||
var new_id := core.place_object(cat_id, new_pos, Vector4(0, 0, 0, 1), Vector3.ONE, {
|
||||
"display_name": model_data.get("name", cat_id),
|
||||
"catalog_id": cat_id,
|
||||
"asset_id": model_data.get("asset_id", "")
|
||||
})
|
||||
if new_id != "":
|
||||
core.select_object(new_id)
|
||||
_spawn_visual_node(new_id)
|
||||
_hierarchy_dock.refresh_tree()
|
||||
|
||||
|
||||
func _spawn_visual_node(obj_id: String) -> void:
|
||||
var obj = core.model.find_object(obj_id)
|
||||
if obj == null:
|
||||
return
|
||||
var wo := WorldObjectClass.new()
|
||||
wo.id = obj.id
|
||||
wo.type = obj.type
|
||||
wo.asset_id = obj.asset_id
|
||||
wo.position = obj.position
|
||||
wo.properties = obj.properties.duplicate(true)
|
||||
|
||||
var node = StaticObjectNodeClass.new()
|
||||
node.name = obj.id
|
||||
node.world_object = wo
|
||||
node.position = Vector3(obj.position.x, obj.position.z, obj.position.y)
|
||||
_world_container.add_child(node)
|
||||
|
||||
|
||||
func _refresh_scene_visuals() -> void:
|
||||
for c in _world_container.get_children():
|
||||
c.queue_free()
|
||||
for obj in core.model.payload.objects:
|
||||
_spawn_visual_node(obj.id)
|
||||
_hierarchy_dock.refresh_tree()
|
||||
|
||||
|
||||
func load_template(template_name: String) -> void:
|
||||
var data := MBWorldProjectIOClass.create_template(template_name)
|
||||
project_title = data.get("title", "Untitled World")
|
||||
for obj in data.get("objects", []):
|
||||
var p: Array = obj.get("position", [0, 0, 0])
|
||||
var r: Array = obj.get("rotation", [0, 0, 0, 1])
|
||||
var s: Array = obj.get("scale", [1, 1, 1])
|
||||
core.place_object(
|
||||
obj.get("catalog_id", "crate_small"),
|
||||
Vector3(p[0], p[1], p[2]),
|
||||
Vector4(r[0], r[1], r[2], r[3]),
|
||||
Vector3(s[0], s[1], s[2]),
|
||||
obj.get("properties", {})
|
||||
)
|
||||
_refresh_scene_visuals()
|
||||
|
||||
|
||||
func save_current_project(path: String = "") -> bool:
|
||||
if path == "":
|
||||
path = "user://saved_world.mbworld"
|
||||
var data := {
|
||||
"title": project_title,
|
||||
"objects": []
|
||||
}
|
||||
for obj in core.model.payload.objects:
|
||||
data["objects"].append({
|
||||
"id": obj.id,
|
||||
"catalog_id": obj.properties.get("catalog_id", "crate_small"),
|
||||
"asset_id": obj.asset_id,
|
||||
"position": [obj.position.x, obj.position.y, obj.position.z],
|
||||
"rotation": [obj.rotation.x, obj.rotation.y, obj.rotation.z, obj.rotation.w],
|
||||
"scale": [obj.scale.x, obj.scale.y, obj.scale.z],
|
||||
"properties": obj.properties
|
||||
})
|
||||
var ok := MBWorldProjectIOClass.save_project(path, data)
|
||||
if ok:
|
||||
_status_bar_label.text = "Saved world project to %s" % path
|
||||
return ok
|
||||
|
||||
|
||||
func _update_simulation_ui(playing: bool) -> void:
|
||||
_btn_play.disabled = playing
|
||||
_btn_stop.disabled = not playing
|
||||
_status_bar_label.text = "Simulation: PLAYING (F5 to Stop)" if playing else "Simulation: STOPPED (Edit mode)"
|
||||
|
||||
|
||||
func _on_file_menu_pressed(id: int) -> void:
|
||||
match id:
|
||||
0: load_template("baseplate")
|
||||
2: save_current_project()
|
||||
5: get_tree().quit(0)
|
||||
|
||||
|
||||
func _on_edit_menu_pressed(id: int) -> void:
|
||||
match id:
|
||||
0: core.undo(); _refresh_scene_visuals()
|
||||
1: core.redo(); _refresh_scene_visuals()
|
||||
2: _hierarchy_dock._on_duplicate_pressed()
|
||||
3: _hierarchy_dock._on_delete_pressed()
|
||||
|
||||
|
||||
func _on_view_menu_pressed(id: int) -> void:
|
||||
match id:
|
||||
0: _camera_3d.set_view_preset(StudioViewportCameraClass.ViewPreset.PERSPECTIVE)
|
||||
1: _camera_3d.set_view_preset(StudioViewportCameraClass.ViewPreset.TOP)
|
||||
2: _camera_3d.set_view_preset(StudioViewportCameraClass.ViewPreset.FRONT)
|
||||
3: _camera_3d.set_view_preset(StudioViewportCameraClass.ViewPreset.SIDE)
|
||||
|
||||
|
||||
func _on_model_menu_pressed(id: int) -> void:
|
||||
var cat_ids := ["crate_small", "lamp_post", "tree_pine", "bison"]
|
||||
if id >= 0 and id < cat_ids.size():
|
||||
_on_collection_model_selected({"catalog_id": cat_ids[id], "name": cat_ids[id].capitalize()})
|
||||
|
||||
|
||||
func _on_test_menu_pressed(id: int) -> void:
|
||||
match id:
|
||||
0: _simulation_mgr.start_play(true)
|
||||
1: _simulation_mgr.start_play(false)
|
||||
2: _simulation_mgr.stop_simulation()
|
||||
|
||||
|
||||
func _on_publish_menu_pressed(id: int) -> void:
|
||||
if id == 0:
|
||||
save_current_project("user://exported_world.mbworld")
|
||||
|
||||
|
||||
func _parse_cmdline_arguments() -> void:
|
||||
var args := OS.get_cmdline_args()
|
||||
for i in range(args.size()):
|
||||
var arg := args[i]
|
||||
if arg == "--template" and i + 1 < args.size():
|
||||
load_template(args[i + 1])
|
||||
elif arg.ends_with(".mbworld") and FileAccess.file_exists(arg):
|
||||
var d := MBWorldProjectIOClass.load_project(arg)
|
||||
if not d.is_empty():
|
||||
project_title = d.get("title", "Loaded World")
|
||||
Reference in New Issue
Block a user