Files
tsullivan13andClaude Opus 5.5 d9bac524db Park editor: saved object sets; duplicate selections; group local axes
Sets: save a selection under a name and place it again anywhere, on any map.

- The library gets an OBJECTS / SETS switch. SETS saves the selected
  objects as a set and lists the saved sets to place or delete (Delete asks
  for a second press).
- A set is a park file in <profile>/object_sets/<name>.park.json, so it
  shares the park format, its 2 MB limit and its name rules. Positions are
  relative to the set's anchor: the middle of its footprint, at its lowest
  origin.
- Placing follows the cursor as one wire cube per object, on surfaces or
  the grid plane; the mouse wheel turns it by the angle step. A click
  pastes the whole set: one undo step, under the paste path's object and
  session limits. Sets with objects missing from the catalog cannot be
  placed.
- A placed or pasted group is then selected with the move gizmo.

Editing groups:

- Local axes work for multi-selections. A placed set's axes are the
  rotation it was placed with, plus every later turn; any other group uses
  its last selected object's rotation. Applying a moved group does not snap
  its own rotation to the angle grid.
- Duplicate (button or Ctrl+D) copies every selected object at its saved
  position, rotation and scale, one grid step along X, as one paste.
- On a server with object scaling off, pastes (sets, duplicates, Ctrl+V)
  are made at 1x.

Tests cover set storage (anchor, round trip, replace, names, damaged files,
delete), saving and placing a set through the UI (surface, wheel, one
paste, no live preview), selecting it with its own axes, moving it along
them, and duplicating with and without scaling.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 09:02:41 -07:00

71 lines
3.6 KiB
C++

#pragma once
#include "Extension/Objects/ParkEditor/editor_math.h"
#include "overlay.h"
#include <imgui.h>
#include <optional>
namespace dingosdk::overlay {
enum class ParkTransformMode { move, rotate, scale };
struct ParkEditorUI {
std::uint64_t generation{}, selected{}, draft_revision{}, queued_revision{};
std::array<char, 128> search{};
// PARK PROJECT dialog: the selected mod folder ("" = a new mod) and the
// form, filled from the selection whenever it changes.
bool project_open{};
std::string project_selected, project_fields_for;
std::array<char, 65> mod_title{}, mod_author{};
std::array<char, 33> mod_version{};
std::array<char, 513> mod_description{};
std::string category, placing, feedback, queued_status;
editor::Vec3 position{}, angles{}, drag_start{}, drag_axis{};
editor::Quat drag_rotation{0, 0, 0, 1};
bool scaling_allowed{true}; // off: the server shares objects at their own size, so none is resized here
float scale{1}, grid{.5f}, angle_snap{15}, scale_snap{.1f}, plane_height{}, distance{10},
drag_parameter{}, drag_angle{}, drag_scale{1}, drag_gizmo_scale{1};
ParkTransformMode mode{ParkTransformMode::move};
bool snapping{true}, local_axes{}, pending{}, inspector_dirty{}, initialized{}, flying{},
exit_pending{};
int dragging{-1};
double queued_at{}, exit_at{}, feedback_until{};
EditorSurfaceRequest surface_request;
bool surface_mode{true}, surface_confirm{}, surface_valid{};
float surface_offset{};
double next_surface_request{};
std::vector<std::uint64_t> selection;
std::vector<EditorTransform> drag_objects;
editor::Vec3 drag_pivot{}, free_offset{};
editor::Quat drag_orientation{0, 0, 0, 1};
std::uint64_t preview_token{}, preview_sequence{}, preview_revision{}, next_preview_token{};
double preview_heartbeat{};
editor::Vec3 preview_position{}, preview_angles{};
float preview_scale{1};
bool preview_place{}, asset_drag{}, pick_confirm{}, pick_shift{}, free_anchor{}, free_release{};
double selection_heartbeat{};
std::vector<std::uint64_t> highlighted;
std::vector<EditorObject> clipboard, paste_expected;
std::vector<std::uint64_t> paste_before;
unsigned paste_serial{};
// Saved object sets: the library lists them in place of objects. `placing_set` follows the
// cursor at ui.position / ui.angles / ui.scale until a click pastes it; `set_delete` is
// the set whose Delete was pressed once and waits for the confirming press.
bool library_sets{};
std::string placing_set, set_delete;
// A multi-selection's own orientation, for its local axes and its Rotation field: a placed set
// starts at the rotation it was placed with and keeps every turn given to it. Any other group
// uses its active (last selected) object's rotation. `group_angles` is what the field showed.
std::vector<std::uint64_t> group_ids; // sorted
editor::Quat group_rotation{0, 0, 0, 1};
std::optional<editor::Quat> paste_rotation; // the set being pasted
editor::Vec3 group_angles{};
std::array<char, 65> set_name{};
// Look shared with the ReSkate menu, set by the overlay before drawing.
// Null fonts fall back to the current font (the UI tests pass none).
ImFont *title_font{}, *bold_font{}, *heading_font{};
float ui_scale{1};
std::string close_key{"INS"};
};
// Render thread only. Returns whether the viewport currently owns flight input.
bool draw_park_editor(ParkEditorUI &, const Model &, const CallbacksV3 &, bool console_open,
bool exit_requested, bool &menu_visible);
} // namespace dingosdk::overlay