Skip to content

Replace drag-point editing with Unity splines - #567

Merged
freezy merged 12 commits into
masterfrom
refactor/splines
Aug 11, 2026
Merged

Replace drag-point editing with Unity splines#567
freezy merged 12 commits into
masterfrom
refactor/splines

Conversation

@freezy

@freezy freezy commented Jul 13, 2026

Copy link
Copy Markdown
Owner

Summary

  • convert VPX drag points losslessly to Unity Splines while preserving per-point metadata
  • migrate supported table components and editor tooling to native spline knot workflows
  • exclude generated spline helpers from table packages and reconcile missing or duplicate helpers deterministically

This PR fixes #541.

Tests

  • Unity EditMode: DragPointSplineComponentTests (11 passed)
  • Unity EditMode: RubberTests.ShouldKeepGeneratedSplinesOutOfPackageRoundTrips (passed)

freezy added 6 commits July 10, 2026 21:45
Record byte-exact goldens for every drag-point geometry consumer, including
surface, ramp, rubber, wire guide, trigger-outline, and light-insert output.
The fixtures include vertex positions, normals, UVs, indices, and control-point
metadata so later storage and editor changes cannot alter generated geometry.

Exercise open and closed curves across smooth flags and accuracy levels in both
two and three dimensions. A VPX export/reload matrix also compares every BIFF
field for all six Unity-backed item types, separating serialized semantics from
the runtime-only IDs and calculated ramp heights.
Depend on Unity Splines 2.8.2 and expose it to the runtime, editor, and test
assemblies. Convert raw VPX knot positions to broken Bezier knots with tangents
ported from VPE's centripetal Catmull-Rom coefficients, preserving the exact
curve while keeping the source floats out of coordinate-space round trips.

Store every semantic and runtime drag-point field in serializable per-knot
metadata, and rebuild derived tangents after knot edits. Edit-mode tests cover
bit-identical data round trips, sampled Catmull-Rom/Bezier equivalence for open
and closed curves, and rejection of authored tangent changes.
Move Rubber, Trigger, Surface, MetalWireGuide, LightInsertMesh, and Ramp onto a
shared child SplineContainer with index-aligned VPE metadata. Legacy arrays now
upgrade lazily, while mesh, collider, packable, and VPX export consumers keep
reading computed DragPointData arrays and therefore retain their existing APIs.

Place spline children under the exact VPX-to-Unity basis so knot floats remain
verbatim in serialized data. Centralize insert/remove metadata alignment,
derived-tangent restoration, planar constraints, Undo/prefab recording, and
mesh invalidation; retain Ramp's endpoint Z-to-height behavior and cover the
migration with edit-mode component tests.
Route all six drag-point inspectors through Unity's spline tool context and
expose VPE metadata for the selected knots. Smoothness remains VPE-authored,
while slingshot and texture fields stay aligned with native knot selection.

Keep the established flip, reverse, and center-origin operations, including
closed-loop orientation and slingshot rotation. Draw segment ownership and
knot indices over the native gizmo, and cover the subtle transforms with
editor tests.
Delete the custom control-point selection, curve traveller, context menus, and
scene-view renderer now that Unity Splines owns knot editing. Remove the old
inspector contract and its drag-point-only handle utility as well.

Keep the remaining flip-axis detail local to the spline inspector so the new
editor path no longer references any part of the retired handle stack.
@greptile-apps

greptile-apps Bot commented Jul 13, 2026

Copy link
Copy Markdown

Greptile Summary

This PR replaces legacy drag-point editing with Unity spline workflows. The main changes are:

  • Conversion between VPX drag points and Unity spline knots.
  • Generated spline components for supported table objects.
  • Editor handles and inspectors for spline-based point editing.
  • Packaging logic to keep generated spline helpers out of table packages.
  • Geometry and round-trip tests for drag-point preservation.

Confidence Score: 5/5

This looks safe to merge.

  • No blocking issues found in the changed code.

Important Files Changed

Filename Overview
VisualPinball.Unity/VisualPinball.Unity/DragPoint/DragPointSplineConverter.cs Adds drag-point to spline conversion and recalculates derived tangents without emitting extra spline change events.
VisualPinball.Unity/VisualPinball.Unity/DragPoint/DragPointSplineComponent.cs Adds spline ownership, metadata alignment, change handling, and mesh rebuild coordination for drag-point spline objects.
VisualPinball.Unity/VisualPinball.Unity.Test/DragPointSplineComponentTests.cs Adds tests for spline editing, metadata alignment, generated child behavior, and single-event knot edits.

Reviews (3): Last reviewed commit: "splines(editor): add/remove drag points ..." | Re-trigger Greptile

Comment thread VisualPinball.Unity/VisualPinball.Unity/DragPoint/DragPointSplineConverter.cs Outdated
@freezy

freezy commented Jul 13, 2026

Copy link
Copy Markdown
Owner Author

Follow-up: tangent notifications are fixed in be7e501. Inserted points remain unlocked to preserve legacy insertion behavior; ramp calculated heights are derived again during the required mesh rebuild.

freezy added 5 commits July 26, 2026 18:34
Owners null their migration copy of the drag points once a spline exists, but
Unity deserializes a null array as an empty one. After any serialization round
trip - entering play mode, a domain reload - an already-migrated owner therefore
handed GetOrCreate an empty list rather than null, which passed the null check
and applied itself over the spline it was asked to return. Both the knots and
the metadata were dropped, so the item silently lost its shape. Reproducible
with create trigger, start game, stop game.

Treat an empty migration list as nothing to migrate, and make the surrounding
code resilient to the same class of failure:

- prefer the child holding the most knots when resolving a spline, so a blank
  created while the hierarchy was incomplete heals instead of winning
- never destroy a generated child that still carries knots
- log an error instead of silently creating a blank spline with no drag points

Also fix the surface and ramp pivot, which relied on DragPoints aliasing the
incoming data. The spline-backed getter returns fresh instances, so recentring
no longer reached the data that SetReferencedData builds the child meshes from,
and every new wall came out offset by its own centre. Rebase the data
explicitly rather than restoring the aliasing.

UnityObjectId handed out ids from a static counter, which is wiped on every
domain reload, so serialized ids stopped matching and every procedural mesh
concluded it had been duplicated. Derive the id from the object's EntityId.
Defer the resulting rebuild out of Awake, where the hierarchy is still being
restored.

Guard the degenerate cases the above could produce: a rubber whose central
curve collapses, and polygon colliders built from fewer than three vertices.
Both indexed into empty arrays and threw from OnDrawGizmosSelected.

Finally, suppress the tangent handles on drag-point splines. Drag points have
no tangents - the converter derives them and reads back only positions - so
dragging one changed the drawn curve but never the mesh or the colliders. The
knots keep the package's own handles, backed by a sizeable ring that stays
legible on a textured playfield.
`Position` is relative to the immediate parent, which only matches the
playfield for kickers parented directly to it. Kickers grouped under
sub-assemblies with their own rotation or offset therefore reported a
wrong position to anything living in playfield space.

Add `PositionInPlayfield`, which resolves the position through the whole
parent chain, and use it for ball creation and for the trough's exit
gizmo.
Double clicking a segment inserts a knot at the clicked position,
double clicking an existing knot removes it. Picking is done in
screen space so it matches what is drawn at any viewing angle.
Removal keeps the minimum knot count (3 closed, 2 open) and warns
instead.

Also stop clearing SplineToolContext.useCustomSplineHandles when a
knot placement tool owns the handles, which previously drew the
default handles on top of the tool's own.
@freezy
freezy marked this pull request as ready for review August 11, 2026 22:06
@freezy
freezy merged commit ab37da7 into master Aug 11, 2026
15 checks passed
@freezy
freezy deleted the refactor/splines branch August 11, 2026 22:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Rewrite Drag Point Tooling Using Splines

1 participant