Skip to content

Added option to keep the UV coordinates coherent when modelling - #657

Open
Micheus wants to merge 9 commits into
dgud:masterfrom
Micheus:mv/fix-uv-on-modify
Open

Micheus wants to merge 9 commits into
dgud:masterfrom
Micheus:mv/fix-uv-on-modify

Conversation

@Micheus

@Micheus Micheus commented Nov 7, 2026

Copy link
Copy Markdown
Collaborator

The commands below now update the per-corner UV attributes so the
texture follows the geometry:

  • Edges / vertices: Slide, Bevel, Crease, Bump, Extrude (edge and vertex),
    Collapse
  • Faces: Extrude, Intrude, Shell Extrude, Inset (Faces and Region/Offset),
    Lift, Bridge, Sweep Extrude
  • Others: Shift, Weld, Greebles

New preference added to handle the On/Off option:

  • New preference update_uv (default on): "Update UV coordinates when
    modeling" added to the Preferences dialog;
  • Interactive commands get a key to toggle it while dragging. The help line
    shows the current state ("Turn Off UV" / "Turn On UV"), and the key appears
    only when the selection has UVs:
    • Keys: Slide [4], Inset Faces [6] ([4] is the RMB Bump option),
      Inset/Offset Region and Sweep [5], all others [1];
  • Turning it off mid-drag restores the UVs of the start of the drag;
  • Collapse, Weld, Bridge and Greebles have no drag; they only read the preference.

NOTE: Added option to keep UV coordinates coherent when modelling. Thanks to evenlupo.

- Slide now keeps the texture attached to the surface: the UV of each
  corner of a sliding vertex follows its 3D displacement, using the
  original mapping of the adjacent faces (extrapolated past the
  neighbor vertex when "Continue past other edges" is on)
- Corners sharing the same UV are moved together, preserving the
  continuity of the mapping; across seams each side moves along its
  own island
- Added the [4] key (Adjust UVs / Keep UVs) to toggle it during the
  drag; the choice is remembered in the slide_uv preference
- Objects without UV coordinates are not affected
- Extracted slide_delta/3 from slide_fun/3 so positions and UVs use
  the same displacement
- Added wings_va:uv_move_data/2 and wings_va:uv_move/3, which compute
  and apply the UV changes for moved vertices, so other commands can
  reuse them; no existing function was changed
- Added the new help strings to wings_en.lang.
When an object has UV coordinates, Bevel (edges and faces) now moves the
UV coordinates of the new vertices together with the geometry, so the
texture stays attached to the surface instead of being stretched.

- Bevel of edges: the new vertices are cut out of the original edges.
  Their UV coordinates follow the rate (UV per unit of length) of the
  original edge in each face next to it, taken from the original object.
- Bevel of faces: the new vertices of an extruded face move in the plane
  of that face, so their corner is moved with the mapping of the face
  (same wedge calculation as Slide), and the corners of the new faces at
  the vertex follow it.
- Corners of new faces that have no UV coordinates (e.g. the faces next
  to them use a material without texture) get the mapping of a neighbor
  face, by unfolding the new face into its plane, so the texture
  continues over it.
- A new face does not become an UV island of its own; it continues the
  mapping of a neighboring face. Original faces without UV coordinates
  are never changed.
- New key [1] while dragging switches between "Adjust UVs" and "Keep
  UVs" (the old behavior). The choice is saved as the preference
  bevel_uv. The key is only available if the object has UV coordinates.
  It is temporary, until adjusting becomes the default.

wings_va: new helpers shared with the other commands that will adjust
UV coordinates: corners/2, uv_wedge/3 and uv_wedge_delta/2. Their
purpose is cohesion and reuse, not performance.

wings_en.lang: strings for the [1] key (bevel_help_uv); the old
bevel_help entry is no longer used.
When an object has UV coordinates, Inset Faces, Inset Region and Offset
Region now move the UV coordinates together with the geometry, so the
texture stays attached to the surface instead of being stretched.

Inset Faces and Inset Region:
- The vertices on the loop of the inset faces move in the plane of those
  faces, so the corner of each vertex is moved with the mapping of that
  face (same wedge calculation as Slide and Bevel). The corners of the
  new side faces at the vertex have the same UV coordinates and move with
  it, so the texture continues over the sides.
- Extrude gives the corners of the side faces at the original vertices
  the UV coordinates of the neighbor faces, which do not continue the
  texture. While the UV adjustment is on, these corners get the UV
  coordinates of the inset face. wings_extrude_face is not changed.
- Vertices inside a region only move along the normal (bump), so their UV
  coordinates are not changed.

Offset Region:
- The vertices of the outer loop of the region move in the plane of the
  faces outside the region, so the corners of those faces are moved with
  their mapping.
- The corners of the side faces at those vertices are moved with the
  mapping of the extruded face at the other end of the side edge, so the
  texture of the extruded face continues over the side faces.

General:
- Works when the neighbor faces have no UV coordinates (material without
  texture); the side faces then get the texture of the extruded face.
- New key [6] while dragging switches between "Adjust UVs" and "Keep
  UVs" (the old behavior). The choice is saved as the preference
  inset_uv and shared by the three commands. The key is only available
  if the object has UV coordinates. It is temporary, until adjusting
  becomes the default.
- collect_inset_face_data/2 (used by wpc_greebles) is not changed.

wings_va: new helpers uv_class/3, uv_class_color/1 and uv_set/3, to find
and set the corners that share the same UV coordinates at a vertex.
Their purpose is cohesion and reuse, not performance.
…s and faces

When an object has UV coordinates, Collapse of edges (including Clean
Collapse) and of faces now moves the UV coordinates of the corners of
the vertices that will be merged, so the faces around the new vertex
keep the scale of the texture instead of being stretched.

- Edges: before wings_collapse:uniform_collapse/1 moves the vertices of
  each group of edges to the center of the group, the UV coordinates of
  their corners are moved as if the vertices were moved there along the
  faces. The groups and centers are the same ones wings_collapse uses.
- Faces: the same is done for the vertices of each face before they are
  merged into the center of the face. A vertex shared by two of the
  selected faces is only moved once.
- The movement is calculated by wings_va:uv_move_data/2 and
  wings_va:uv_move/3 (the same calculation as Slide), so corners that
  share the same UV coordinates move together and UV seams are kept.
- wings_collapse is not changed, so the other users of collapse_edge/2,
  collapse_faces/2 and collapse_vertices/2 are not affected.
- Collapse of vertices is not changed: it does not move any vertex.
- The adjustment is on by default when the object has UV coordinates.
  The preference collapse_uv = false turns it off.
When an object has UV coordinates, Crease now moves the UV coordinates of
the new vertices together with the geometry, so the texture stays
attached to the surface instead of being stretched when the width of the
crease is changed. The end caps of the crease no longer damage the UV
coordinates of the faces around them.

- Width (third value of the drag): the new vertices, cut out of the
  edges next to the creased edges, slide away from the vertices of the
  creased edges along the surface. They move in the plane of the faces
  around them, so their corners are moved with the mapping of an
  original face (wings_va:uv_wedge/3), the same way as a new vertex of
  Bevel. The corners with the same UV coordinates move together, and
  UV seams are kept.
- Depth: the vertices of the creased edges only move along the normal,
  which does not change the UV coordinates. The walls of the crease get
  the texture projected from above.
- End caps: Crease collapses a new vertex into a vertex of the object, or
  moves a new vertex to the center of its neighbors, without looking at
  the UV coordinates. The corners of the vertex that keeps its position
  now get back the UV coordinates it had, and the corners of the vertex
  that moves are moved along the faces (wings_va:uv_move/3).
- Faces at the end caps that are partly on a face with UV coordinates and
  partly on a face without them (e.g. a crease on the border of a
  textured area) still have corners without UV coordinates. This is not
  changed.
- The adjustment is on by default when the object has UV coordinates.
  The preference crease_uv = false turns it off. There is no key to
  switch it while dragging.
- The body of crease/1 that handled each object is moved to
  crease_edges/3 so it can be called without a #st{}. Its logic is
  unchanged except for the UV handling.

wings_move: plus_minus/3 now also accepts the form {we,WeFuns,PlusMinus}
from the fun it is given, the same form that wings_drag:fold/4 accepts,
so a caller can add funs that change the #we{} (here, the UV
coordinates) while dragging. This avoids copying the private code that
moves the new vertices into wings_extrude_edge. Callers that return only
PlusMinus work as before.
A vertex that cuts a face edge in two has its two neighbours in line
with it, so the wedge used by uv_move_data/2 and uv_wedge/3 is
degenerate and the corner was not moved at all, leaving a distorted
area around it.

When the wedge is degenerate, use the other vertices of the same face
to build it. The mapping of the face is the same, so the result is the
one a regular corner would give. Regular corners are not affected.

Bump: keep the UV coordinates on the surface while dragging

The new vertices of Bump slide with the third drag value just like
those of Crease, while the face vertices only move along the normal
(no change of UV). Reuse the Crease code: crease_uv/4 and
crease_uv_data/5 now receive the vertices moved along the normal, so
Bump passes the vertices of its faces and Crease those of its edges.
Crease behaves as before.

Greebles: keep the UV coordinates on the surface

The vertices that Greebles moves in inset/5 now keep the texture on
the surface, with 'Inset all' and 'Inset first only'.

wpc_inset: split inset_uv_fun/3 so that the calculation is available
as inset_uv_set/3, and export it with inset_uv_data/2 for Greebles.
Add wall_uv/2: the side faces of an extrusion (not in the plane of the
extruded face) are unfolded into the plane of the face they stand on
and use its mapping, so the texture keeps its scale on them. They are
shifted by a tiny amount (?WALL_SEPARATION) so that AutoUV, which
compares the UV of both sides of an edge exactly, makes them separate
charts, to be moved and sewn by the user. Faces in the plane of the
extruded face (the rings of the inset) keep the planar projection of
the base and stay in one chart where they touch.
The UV of the corners now follows the geometry, so the texture keeps
its place and scale on the surface.

wings_va:
- uv_move_data/2 and uv_move/3 move the UV of the corners of a vertex
  as if the texture stayed in place when the vertex moves. Each corner
  uses the mapping of its own face (wedge), so seams are respected,
  and the result is absolute, so it can be used while dragging.
- wall_uv/2 unfolds the side faces of moved faces about their base
  edge into the plane of the face they stand on (flat_wall_uv/2 for
  walls in that plane). wall_uv_strips/3 does it for quads and
  triangles known by their vertices. They are shifted by
  WALL_SEPARATION to be a chart of their own for AutoUV.
- wall_uv_ring/2 and wall_uv_bridge/3 unfold a ring of walls as a
  strip, opened where the two arms meet, with exactly the same UV on
  the shared edges.
- seam_strips/2 and seam_strip_uv/2: Bevel strips across a UV seam
  (or next to a face without UV) are unfolded into the mapping of one
  of the faces they join; triangles and vertex faces follow their strip.
- lift_wall_uv/3 (walls of Lift, triangles included) and
  restore_corners/4 (corners of a face that a collapse mixed up).

Commands:
- Slide, Collapse, Shift: UV follows the moved vertices.
- Bevel, Inset (with height), Greebles: new vertices slide on the UV
  of the faces they cut; the walls use wall_uv/2.
- Crease, Bump and Extrude of edges and vertices: the new vertices
  slide with the wedge of their own UV class (the best conditioned
  face, since a corner can be degenerate). Vertices that overlap at
  the end of the drag (percent 0) are merged in a finish step:
  temp={finish,Fun,Tv} of the shape is read by
  wings_drag:normalize_fun/3, because the temp of a We is lost in
  wings_draw:join.
- Extrude of faces and regions, Intrude, Shell Extrude:
  wings_move:setup/3 adds a WeFun that updates the walls at each step
  of the drag (wings_move:walls_fun/3, also used by Lift). For free
  moves plus_minus/3 passes none instead of the position function,
  since the walls cannot be computed there. Shell Extrude unfolds its
  ring of faces as a strip, like Intrude.
- Bridge: the new faces get UV once, at the end.
- Sweep, Sweep Region, Sweep Extract: the walls follow the sweep.
- Weld (body): the bridge and the collapse overwrite the UV of the
  corners at the welded vertices. After each pass the faces that stay
  get back the UV of their corners (found by position), so the weld
  becomes a seam.
- Inset of a region that is not flat: each selected face gives its UV
  only to the side faces that touch it; vertices inside a bent region
  follow the bump; corners of other selected faces with the same UV
  are not moved with the corners of a face.
- Lift: the corners of the lifted face at the hinge are restored
  after the collapse, and the side faces follow the drag.

Models without UV are not affected. Known limits: Lift and Move by
direction deform the lifted face (its UV stays rigid); with free
directions the walls do not follow the drag; Inset "Along Edges"
with bump still stretches the walls; where the texture is continuous
across faces and a vertex moves differently in each, a seam may open.
The UV follow behaviour can be switched off with the update_uv
preference (Preferences dialog, on by default). Collapse, Weld, Bridge
and Greebles only read it.

The interactive commands also get a key to turn it on or off while
dragging; the help shows the action ("Turn Off UV"/"Turn On UV") and
the value is saved in the preference:
- Slide [4], Inset Faces [6], Inset/Offset Region [5], Sweep [5].
- Bevel, Crease, Bump, Extrude (edges, vertices, faces), Intrude,
  Shell Extrude, Lift and Shift: [1], with a new mode.

The key shows even if the option starts off. Turning it off while
dragging gives back the UV of the start of the drag, as in Wings3D
without this feature.

wings_va: any_uvs/1 accepts a #st{} too; follow_uvs/1 tells if the
option is on and a #we{} has UVs.

Some cleanup and code organization was also made.
@Micheus

Micheus commented Nov 7, 2026

Copy link
Copy Markdown
Collaborator Author

Video showing some commands in action: Wings3D: UV adjustment on mesh edit - W.I.P (2)

Preference dialog change:
image

Editing a mesh already unwrapped with the option Off:
image

Editing a mesh already unwrapped with the option On:
image

@dgud

dgud commented Nov 7, 2026

Copy link
Copy Markdown
Owner

Nice, now we need to set UV's on all start objects cube and everything.

@Micheus

Micheus commented Nov 7, 2026

Copy link
Copy Markdown
Collaborator Author

Nice, now we need to set UV's on all start objects cube and everything.

Indeed. In Discord I already talked about we be able to start from a uv mapped cube and have just a few work to unwrap any mesh when used other operations.
If the UV Pack PR be approved we have a good tool to complete the process.

But, something we need is find a way to improve the AutoUV show up time. The example in that PR has only 143 islands and it takes ~35s to show up on screen - it's an eternity.

@Micheus

Micheus commented Nov 14, 2026

Copy link
Copy Markdown
Collaborator Author

Nice, now we need to set UV's on all start objects cube and everything.

Done: Add UV maps to the built-in primitives

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.

2 participants