mirror of
https://github.com/Facepunch/sbox-public.git
synced 2025-12-23 22:48:07 -05:00
Mapping tools resize mode (#3630)
This commit is contained in:
@@ -28,7 +28,7 @@ public sealed class MeshComponent : Collider, ExecuteInEditor, ITintable, IMater
|
||||
|
||||
field = value;
|
||||
|
||||
Update();
|
||||
RebuildMesh();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -146,7 +146,7 @@ public sealed class MeshComponent : Collider, ExecuteInEditor, ITintable, IMater
|
||||
internal override void OnEnabledInternal()
|
||||
{
|
||||
// Mesh needs to build before collider.
|
||||
RebuildMesh();
|
||||
RebuildRenderMesh();
|
||||
|
||||
base.OnEnabledInternal();
|
||||
}
|
||||
@@ -171,17 +171,17 @@ public sealed class MeshComponent : Collider, ExecuteInEditor, ITintable, IMater
|
||||
{
|
||||
base.OnUpdate();
|
||||
|
||||
Update();
|
||||
RebuildMesh();
|
||||
}
|
||||
|
||||
void Update()
|
||||
public void RebuildMesh()
|
||||
{
|
||||
// Only rebuild dirty meshes in editor.
|
||||
if ( !Active ) return;
|
||||
if ( !Scene.IsEditor ) return;
|
||||
if ( Mesh is null || !Mesh.IsDirty ) return;
|
||||
|
||||
RebuildMesh();
|
||||
RebuildRenderMesh();
|
||||
RebuildImmediately();
|
||||
}
|
||||
|
||||
@@ -253,7 +253,7 @@ public sealed class MeshComponent : Collider, ExecuteInEditor, ITintable, IMater
|
||||
}
|
||||
}
|
||||
|
||||
public void RebuildMesh()
|
||||
void RebuildRenderMesh()
|
||||
{
|
||||
if ( !Active ) return;
|
||||
if ( Mesh is null ) return;
|
||||
|
||||
@@ -165,6 +165,14 @@ public sealed partial class PolygonMesh : IJsonConvert
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set transform without computing texture parameters from coordinates.
|
||||
/// </summary>
|
||||
public void SetTransform( Transform transform )
|
||||
{
|
||||
_transform = transform;
|
||||
}
|
||||
|
||||
public PolygonMesh()
|
||||
{
|
||||
Positions = Topology.CreateVertexData<Vector3>( nameof( Positions ) );
|
||||
@@ -2569,6 +2577,12 @@ public sealed partial class PolygonMesh : IJsonConvert
|
||||
ComputeFaceTextureCoordinatesFromParameters( FaceHandles );
|
||||
}
|
||||
|
||||
public void ComputeFaceTextureCoordinatesFromParameters( Transform transform )
|
||||
{
|
||||
var textureSizes = Materials.Select( CalculateTextureSize ).ToArray();
|
||||
ComputeFaceTextureCoordinatesFromParameters( FaceHandles, transform, textureSizes, 0.25f );
|
||||
}
|
||||
|
||||
public void ComputeFaceTextureCoordinatesFromParameters( IEnumerable<FaceHandle> faces )
|
||||
{
|
||||
var textureSizes = Materials.Select( CalculateTextureSize ).ToArray();
|
||||
|
||||
102
game/addons/tools/Code/Scene/Mesh/MoveModes/ResizeMode.cs
Normal file
102
game/addons/tools/Code/Scene/Mesh/MoveModes/ResizeMode.cs
Normal file
@@ -0,0 +1,102 @@
|
||||
|
||||
namespace Editor.MeshEditor;
|
||||
|
||||
/// <summary>
|
||||
/// Resize everything in the selection using box resize handles.
|
||||
/// </summary>
|
||||
[Title( "Resize" )]
|
||||
[Icon( "device_hub" )]
|
||||
[Alias( "mesh.resize.mode" )]
|
||||
[Order( 4 )]
|
||||
public sealed class ResizeMode : MoveMode
|
||||
{
|
||||
private BBox _startBox;
|
||||
private BBox _deltaBox;
|
||||
private BBox _box;
|
||||
|
||||
protected override void OnUpdate( SelectionTool tool )
|
||||
{
|
||||
if ( !Gizmo.Pressed.Any )
|
||||
{
|
||||
tool.EndDrag();
|
||||
|
||||
_startBox = tool.CalculateSelectionBounds();
|
||||
_deltaBox = default;
|
||||
_box = _startBox;
|
||||
}
|
||||
|
||||
var size = _startBox.Size;
|
||||
if ( size.x.AlmostEqual( 0.0f ) ) return;
|
||||
if ( size.y.AlmostEqual( 0.0f ) ) return;
|
||||
if ( size.z.AlmostEqual( 0.0f ) ) return;
|
||||
|
||||
using ( Gizmo.Scope( "box" ) )
|
||||
{
|
||||
Gizmo.Hitbox.DepthBias = 0.01f;
|
||||
|
||||
if ( Gizmo.Control.BoundingBox( "resize", _box, out var outBox ) )
|
||||
{
|
||||
_deltaBox.Maxs += outBox.Maxs - _box.Maxs;
|
||||
_deltaBox.Mins += outBox.Mins - _box.Mins;
|
||||
|
||||
_box = Snap( _startBox, _deltaBox );
|
||||
|
||||
tool.StartDrag();
|
||||
|
||||
ResizeBBox( tool, _startBox, _box, Rotation.Identity );
|
||||
|
||||
tool.UpdateDrag();
|
||||
|
||||
tool.Pivot = tool.CalculateSelectionOrigin();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static BBox Snap( BBox startBox, BBox movement )
|
||||
{
|
||||
var mins = startBox.Mins + movement.Mins;
|
||||
var maxs = startBox.Maxs + movement.Maxs;
|
||||
|
||||
var snap = Gizmo.Settings.SnapToGrid != Gizmo.IsCtrlPressed;
|
||||
|
||||
if ( snap )
|
||||
{
|
||||
mins = Gizmo.Snap( mins, movement.Mins );
|
||||
maxs = Gizmo.Snap( maxs, movement.Maxs );
|
||||
}
|
||||
|
||||
var spacing = 1.0f;
|
||||
|
||||
mins.x = MathF.Min( mins.x, startBox.Maxs.x - spacing );
|
||||
mins.y = MathF.Min( mins.y, startBox.Maxs.y - spacing );
|
||||
mins.z = MathF.Min( mins.z, startBox.Maxs.z - spacing );
|
||||
|
||||
maxs.x = MathF.Max( maxs.x, startBox.Mins.x + spacing );
|
||||
maxs.y = MathF.Max( maxs.y, startBox.Mins.y + spacing );
|
||||
maxs.z = MathF.Max( maxs.z, startBox.Mins.z + spacing );
|
||||
|
||||
return new BBox( mins, maxs );
|
||||
}
|
||||
|
||||
static void ResizeBBox( SelectionTool tool, BBox prevBox, BBox newBox, Rotation basis )
|
||||
{
|
||||
var prevSize = prevBox.Size;
|
||||
|
||||
var scale = newBox.Size / prevSize;
|
||||
var dMin = newBox.Mins - prevBox.Mins;
|
||||
var dMax = newBox.Maxs - prevBox.Maxs;
|
||||
|
||||
var origin = prevBox.Center;
|
||||
|
||||
if ( MathF.Abs( dMax.x ) > MathF.Abs( dMin.x ) ) origin.x = prevBox.Mins.x;
|
||||
else if ( MathF.Abs( dMin.x ) > MathF.Abs( dMax.x ) ) origin.x = prevBox.Maxs.x;
|
||||
|
||||
if ( MathF.Abs( dMax.y ) > MathF.Abs( dMin.y ) ) origin.y = prevBox.Mins.y;
|
||||
else if ( MathF.Abs( dMin.y ) > MathF.Abs( dMax.y ) ) origin.y = prevBox.Maxs.y;
|
||||
|
||||
if ( MathF.Abs( dMax.z ) > MathF.Abs( dMin.z ) ) origin.z = prevBox.Mins.z;
|
||||
else if ( MathF.Abs( dMin.z ) > MathF.Abs( dMax.z ) ) origin.z = prevBox.Maxs.z;
|
||||
|
||||
tool.Resize( origin, basis, scale );
|
||||
}
|
||||
}
|
||||
@@ -62,6 +62,7 @@ public sealed class BlockEditor( PrimitiveTool tool ) : PrimitiveEditor( tool )
|
||||
if ( !_dragStarted )
|
||||
{
|
||||
EditorToolManager.SetSubTool( nameof( MeshSelection ) );
|
||||
Tool.MeshTool.SetMoveMode<ResizeMode>();
|
||||
}
|
||||
|
||||
_box = null;
|
||||
|
||||
@@ -192,8 +192,8 @@ partial class MeshSelection
|
||||
|
||||
var world = meshComponent.WorldTransform;
|
||||
var localCenter = world.PointToLocal( origin );
|
||||
meshComponent.WorldPosition = origin;
|
||||
meshComponent.Mesh.ApplyTransform( new Transform( -localCenter ) );
|
||||
meshComponent.WorldPosition = origin;
|
||||
meshComponent.RebuildMesh();
|
||||
}
|
||||
|
||||
|
||||
@@ -12,7 +12,8 @@ public sealed partial class MeshSelection( MeshTool tool ) : SelectionTool
|
||||
{
|
||||
public MeshTool Tool { get; private init; } = tool;
|
||||
|
||||
readonly Dictionary<GameObject, Transform> _startPoints = [];
|
||||
readonly Dictionary<MeshComponent, Transform> _startPoints = [];
|
||||
readonly Dictionary<MeshVertex, Vector3> _transformVertices = [];
|
||||
IDisposable _undoScope;
|
||||
|
||||
MeshComponent[] _meshes = [];
|
||||
@@ -27,6 +28,7 @@ public sealed partial class MeshSelection( MeshTool tool ) : SelectionTool
|
||||
{
|
||||
_undoScope ??= SceneEditorSession.Active.UndoScope( "Duplicate Object(s)" )
|
||||
.WithGameObjectCreations()
|
||||
.WithComponentChanges( _meshes )
|
||||
.Push();
|
||||
|
||||
DuplicateSelection();
|
||||
@@ -36,12 +38,19 @@ public sealed partial class MeshSelection( MeshTool tool ) : SelectionTool
|
||||
{
|
||||
_undoScope ??= SceneEditorSession.Active.UndoScope( "Transform Object(s)" )
|
||||
.WithGameObjectChanges( _meshes.Select( x => x.GameObject ), GameObjectUndoFlags.Properties )
|
||||
.WithComponentChanges( _meshes )
|
||||
.Push();
|
||||
}
|
||||
|
||||
foreach ( var mesh in _meshes )
|
||||
{
|
||||
_startPoints[mesh.GameObject] = mesh.WorldTransform;
|
||||
_startPoints[mesh] = mesh.WorldTransform;
|
||||
|
||||
foreach ( var vertex in mesh.Mesh.VertexHandles )
|
||||
{
|
||||
var v = new MeshVertex( mesh, vertex );
|
||||
_transformVertices[v] = mesh.WorldTransform.PointToWorld( mesh.Mesh.GetVertexPosition( vertex ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -95,6 +104,40 @@ public sealed partial class MeshSelection( MeshTool tool ) : SelectionTool
|
||||
}
|
||||
}
|
||||
|
||||
public override void Resize( Vector3 origin, Rotation basis, Vector3 scale )
|
||||
{
|
||||
foreach ( var startPoint in _startPoints )
|
||||
{
|
||||
var position = (startPoint.Value.Position - origin) * basis.Inverse;
|
||||
position *= scale;
|
||||
position *= basis;
|
||||
position += origin;
|
||||
|
||||
var component = startPoint.Key;
|
||||
var transform = component.WorldTransform.WithPosition( position );
|
||||
component.Mesh.SetTransform( transform );
|
||||
}
|
||||
|
||||
foreach ( var entry in _transformVertices )
|
||||
{
|
||||
var position = (entry.Value - origin) * basis.Inverse;
|
||||
position *= scale;
|
||||
position *= basis;
|
||||
position += origin;
|
||||
|
||||
var transform = entry.Key.Component.Mesh.Transform;
|
||||
entry.Key.Component.Mesh.SetVertexPosition( entry.Key.Handle, transform.PointToLocal( position ) );
|
||||
}
|
||||
|
||||
foreach ( var startPoint in _startPoints )
|
||||
{
|
||||
var component = startPoint.Key;
|
||||
component.Mesh.ComputeFaceTextureCoordinatesFromParameters();
|
||||
component.WorldTransform = component.Mesh.Transform;
|
||||
component.RebuildMesh();
|
||||
}
|
||||
}
|
||||
|
||||
public override BBox CalculateLocalBounds()
|
||||
{
|
||||
return CalculateSelectionBounds();
|
||||
@@ -147,10 +190,17 @@ public sealed partial class MeshSelection( MeshTool tool ) : SelectionTool
|
||||
Tool.MoveMode.Update( this );
|
||||
}
|
||||
|
||||
BBox CalculateSelectionBounds()
|
||||
public override Vector3 CalculateSelectionOrigin()
|
||||
{
|
||||
var meshes = _meshes.Where( x => x.IsValid() && x.Model.IsValid() );
|
||||
return BBox.FromBoxes( meshes.Select( x => x.Model.Bounds.Transform( x.WorldTransform ) ) );
|
||||
var mesh = _meshes.FirstOrDefault();
|
||||
return mesh.IsValid() ? mesh.WorldPosition : default;
|
||||
}
|
||||
|
||||
public override BBox CalculateSelectionBounds()
|
||||
{
|
||||
return BBox.FromPoints( _transformVertices
|
||||
.Where( x => x.Key.IsValid() )
|
||||
.Select( x => x.Key.Transform.PointToWorld( x.Key.Component.Mesh.GetVertexPosition( x.Key.Handle ) ) ) );
|
||||
}
|
||||
|
||||
public override void OnSelectionChanged()
|
||||
@@ -160,6 +210,17 @@ public sealed partial class MeshSelection( MeshTool tool ) : SelectionTool
|
||||
.Where( x => x.IsValid() )
|
||||
.ToArray();
|
||||
|
||||
_transformVertices.Clear();
|
||||
|
||||
foreach ( var mesh in _meshes )
|
||||
{
|
||||
foreach ( var vertex in mesh.Mesh.VertexHandles )
|
||||
{
|
||||
var v = new MeshVertex( mesh, vertex );
|
||||
_transformVertices[v] = mesh.WorldTransform.PointToWorld( mesh.Mesh.GetVertexPosition( vertex ) );
|
||||
}
|
||||
}
|
||||
|
||||
ClearPivot();
|
||||
}
|
||||
|
||||
@@ -354,8 +415,7 @@ public sealed partial class MeshSelection( MeshTool tool ) : SelectionTool
|
||||
|
||||
public void ClearPivot()
|
||||
{
|
||||
var mesh = _meshes.FirstOrDefault();
|
||||
Pivot = mesh.IsValid() ? mesh.WorldPosition : default;
|
||||
Pivot = CalculateSelectionOrigin();
|
||||
_pivotIndex = 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -14,6 +14,12 @@ public partial class MeshTool : EditorTool
|
||||
|
||||
public MoveMode MoveMode { get; set; }
|
||||
|
||||
public void SetMoveMode<T>() where T : MoveMode
|
||||
{
|
||||
if ( MoveMode?.GetType() == typeof( T ) ) return;
|
||||
MoveMode = EditorTypeLibrary.Create<MoveMode>( typeof( T ) );
|
||||
}
|
||||
|
||||
public override IEnumerable<EditorTool> GetSubtools()
|
||||
{
|
||||
yield return new PrimitiveTool( this );
|
||||
@@ -33,7 +39,7 @@ public partial class MeshTool : EditorTool
|
||||
|
||||
Selection.Clear();
|
||||
|
||||
MoveMode = EditorTypeLibrary.Create<MoveMode>( "PositionMode" );
|
||||
SetMoveMode<PositionMode>();
|
||||
}
|
||||
|
||||
public override void OnSelectionChanged()
|
||||
|
||||
@@ -35,6 +35,9 @@ class MoveModeToolBar : Widget
|
||||
|
||||
[Shortcut( "tools.pivot-tool", "t", typeof( SceneDock ) )]
|
||||
public void ActivatePivotMode() => SetMode( "mesh.pivot.mode" );
|
||||
|
||||
[Shortcut( "tools.resize-tool", "y", typeof( SceneDock ) )]
|
||||
public void ActivateResizeMode() => SetMode( "mesh.resize.mode" );
|
||||
}
|
||||
|
||||
file class MoveModeButton : Widget
|
||||
|
||||
@@ -8,9 +8,11 @@ namespace Editor.MeshEditor;
|
||||
[Alias( "tools.primitive-tool" )]
|
||||
public partial class PrimitiveTool( MeshTool tool ) : EditorTool
|
||||
{
|
||||
public MeshTool MeshTool { get; private init; } = tool;
|
||||
|
||||
public PrimitiveEditor Editor { get; private set; }
|
||||
|
||||
public Material ActiveMaterial => tool.ActiveMaterial;
|
||||
public Material ActiveMaterial => MeshTool.ActiveMaterial;
|
||||
|
||||
public override void OnEnabled()
|
||||
{
|
||||
|
||||
@@ -5,11 +5,21 @@ public abstract class SelectionTool : EditorTool
|
||||
{
|
||||
public Vector3 Pivot { get; set; }
|
||||
|
||||
public virtual Vector3 CalculateSelectionOrigin()
|
||||
{
|
||||
return default;
|
||||
}
|
||||
|
||||
public virtual Rotation CalculateSelectionBasis()
|
||||
{
|
||||
return Rotation.Identity;
|
||||
}
|
||||
|
||||
public virtual BBox CalculateSelectionBounds()
|
||||
{
|
||||
return default;
|
||||
}
|
||||
|
||||
public virtual BBox CalculateLocalBounds()
|
||||
{
|
||||
return default;
|
||||
@@ -38,6 +48,11 @@ public abstract class SelectionTool : EditorTool
|
||||
public virtual void Scale( Vector3 origin, Rotation basis, Vector3 scale )
|
||||
{
|
||||
}
|
||||
|
||||
public virtual void Resize( Vector3 origin, Rotation basis, Vector3 scale )
|
||||
{
|
||||
Scale( origin, basis, scale );
|
||||
}
|
||||
}
|
||||
|
||||
public abstract class SelectionTool<T>( MeshTool tool ) : SelectionTool where T : IMeshElement
|
||||
@@ -332,14 +347,14 @@ public abstract class SelectionTool<T>( MeshTool tool ) : SelectionTool where T
|
||||
_nudge = true;
|
||||
}
|
||||
|
||||
public BBox CalculateSelectionBounds()
|
||||
public override BBox CalculateSelectionBounds()
|
||||
{
|
||||
return BBox.FromPoints( _vertexSelection
|
||||
.Where( x => x.IsValid() )
|
||||
.Select( x => x.Transform.PointToWorld( x.Component.Mesh.GetVertexPosition( x.Handle ) ) ) );
|
||||
}
|
||||
|
||||
public virtual Vector3 CalculateSelectionOrigin()
|
||||
public override Vector3 CalculateSelectionOrigin()
|
||||
{
|
||||
var bounds = CalculateSelectionBounds();
|
||||
return bounds.Center;
|
||||
|
||||
Reference in New Issue
Block a user