diff --git a/BepInEx/Plugin.cs b/BepInEx/Plugin.cs index b5bd2ab..eb6f543 100644 --- a/BepInEx/Plugin.cs +++ b/BepInEx/Plugin.cs @@ -14,7 +14,7 @@ namespace LineTool /// /// BepInEx plugin to substitute for IMod support. /// - [BepInPlugin(GUID, "Line Tool Lite", "1.0.10")] + [BepInPlugin(GUID, "Line Tool Lite", "1.1.0")] [HarmonyPatch] public class Plugin : BaseUnityPlugin { diff --git a/BepInEx/manifest.json b/BepInEx/manifest.json index 4473da4..db136be 100644 --- a/BepInEx/manifest.json +++ b/BepInEx/manifest.json @@ -1,6 +1,6 @@ { "name": "Line_Tool_Lite", - "version_number": "1.0.10", + "version_number": "1.1.0", "website_url": "https://github.com/algernon-A/LineToolLite", "description": "Place objects in lines, curves, or circles. A variety of options and controls are availalbe to specify and fine-tune results.", "dependencies": [ diff --git a/Changelog.txt b/Changelog.txt index 353c81f..28e9872 100644 --- a/Changelog.txt +++ b/Changelog.txt @@ -1,3 +1,6 @@ +1.1.0 +- Add dragging of line control points in fixed preview mode. + 1.0.10 - Add fixed-length even spacing mode (will evenly space out objects along the full length of the line with spacing as close as possible to the specified distance). diff --git a/Code/LineModes/Circle.cs b/Code/LineModes/Circle.cs index d2cc9a1..6cce5d2 100644 --- a/Code/LineModes/Circle.cs +++ b/Code/LineModes/Circle.cs @@ -82,6 +82,9 @@ public override void CalculatePoints(float3 currentPos, SpacingMode spacingMode, // Add point to list. pointList.Add(new PointData { Position = thisPoint, Rotation = quaternion.Euler(0f, math.radians(rotation) - i, 0f), }); } + + // Record end position for overlays. + m_endPos = currentPos; } /// diff --git a/Code/LineModes/LineBase.cs b/Code/LineModes/LineBase.cs index 8bc2789..9508ba1 100644 --- a/Code/LineModes/LineBase.cs +++ b/Code/LineModes/LineBase.cs @@ -11,6 +11,7 @@ namespace LineTool using Unity.Mathematics; using UnityEngine; using static Game.Rendering.GuideLinesSystem; + using static LineToolSystem; /// /// Line placement mode. @@ -18,6 +19,11 @@ namespace LineTool [System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1401:Fields should be private", Justification = "Protected fields")] public abstract class LineBase { + /// + /// Selection radius of points. + /// + protected const float PointRadius = 8f; + /// /// Indicates whether a valid starting position has been recorded. /// @@ -28,6 +34,11 @@ public abstract class LineBase /// protected float3 m_startPos; + /// + /// Records the current selection end position. + /// + protected float3 m_endPos; + /// /// Initializes a new instance of the class. /// @@ -167,23 +178,38 @@ public virtual void CalculatePoints(float3 currentPos, SpacingMode spacingMode, // Add point to list. pointList.Add(new PointData { Position = thisPoint, Rotation = qRotation, }); } + + // Record end position for overlays. + m_endPos = currentPos; } /// /// Draws any applicable overlay. /// - /// Current cursor world position. /// Overlay buffer. /// Tooltip list. - public virtual void DrawOverlay(float3 currentPos, OverlayRenderSystem.Buffer overlayBuffer, NativeList tooltips) + public virtual void DrawOverlay(OverlayRenderSystem.Buffer overlayBuffer, NativeList tooltips) { // Don't draw overlay if we don't have a valid start. if (m_validStart) { - DrawDashedLine(m_startPos, currentPos, new Line3.Segment(m_startPos, currentPos), overlayBuffer, tooltips); + DrawDashedLine(m_startPos, m_endPos, new Line3.Segment(m_startPos, m_endPos), overlayBuffer, tooltips); } } + /// + /// Draws point overlays. + /// + /// Overlay buffer. + public virtual void DrawPointOverlays(OverlayRenderSystem.Buffer overlayBuffer) + { + Color softCyan = Color.cyan; + softCyan.a *= 0.1f; + + overlayBuffer.DrawCircle(Color.cyan, softCyan, 0.3f, 0, new float2(0f, 1f), m_startPos, PointRadius * 2f); + overlayBuffer.DrawCircle(Color.cyan, softCyan, 0.3f, 0, new float2(0f, 1f), m_endPos, PointRadius * 2f); + } + /// /// Clears the current selection. /// @@ -192,6 +218,42 @@ public virtual void Reset() m_validStart = false; } + /// + /// Checks to see if a click should initiate point dragging. + /// + /// Click position in world space. + /// Drag mode. + internal virtual DragMode CheckDragHit(float3 position) + { + if (math.distancesq(position, m_startPos) < (PointRadius * PointRadius)) + { + // Start point. + return DragMode.StartPos; + } + else if (math.distancesq(position, m_endPos) < (PointRadius * PointRadius)) + { + // End point. + return DragMode.EndPos; + } + + // No hit. + return DragMode.None; + } + + /// + /// Handles dragging action. + /// + /// Dragging mode. + /// New position. + internal virtual void HandleDrag(DragMode dragMode, float3 position) + { + // Drag start point. + if (dragMode == DragMode.StartPos) + { + m_startPos = position; + } + } + /// /// Draws a dashed line overlay between the two given points. /// diff --git a/Code/LineModes/SimpleCurve.cs b/Code/LineModes/SimpleCurve.cs index b8b0310..8ee1d95 100644 --- a/Code/LineModes/SimpleCurve.cs +++ b/Code/LineModes/SimpleCurve.cs @@ -12,6 +12,7 @@ namespace LineTool using Unity.Mathematics; using UnityEngine; using static Game.Rendering.GuideLinesSystem; + using static LineToolSystem; /// /// Simple curve placement mode. @@ -105,7 +106,8 @@ public override void CalculatePoints(float3 currentPos, SpacingMode spacingMode, if (!_validElbow) { // Constrain as required. - base.CalculatePoints(ConstrainPos(currentPos), spacingMode, spacing, randomSpacing, randomOffset, rotation, zBounds, pointList, ref heightData); + m_endPos = ConstrainPos(currentPos); + base.CalculatePoints(m_endPos, spacingMode, spacing, randomSpacing, randomOffset, rotation, zBounds, pointList, ref heightData); return; } @@ -176,15 +178,17 @@ public override void CalculatePoints(float3 currentPos, SpacingMode spacingMode, // Add point to list. pointList.Add(new PointData { Position = thisPoint, Rotation = qRotation, }); } + + // Record end position for overlays. + m_endPos = currentPos; } /// /// Draws any applicable overlay. /// - /// Current cursor world position. /// Overlay buffer. /// Tooltip list. - public override void DrawOverlay(float3 currentPos, OverlayRenderSystem.Buffer overlayBuffer, NativeList tooltips) + public override void DrawOverlay(OverlayRenderSystem.Buffer overlayBuffer, NativeList tooltips) { if (m_validStart) { @@ -193,11 +197,11 @@ public override void DrawOverlay(float3 currentPos, OverlayRenderSystem.Buffer o { // Calculate lines. Line3.Segment line1 = new (m_startPos, _elbowPoint); - Line3.Segment line2 = new (_elbowPoint, currentPos); + Line3.Segment line2 = new (_elbowPoint, m_endPos); // Draw lines. DrawDashedLine(m_startPos, _elbowPoint, line1, overlayBuffer, tooltips); - DrawDashedLine(_elbowPoint, currentPos, line2, overlayBuffer, tooltips); + DrawDashedLine(_elbowPoint, m_endPos, line2, overlayBuffer, tooltips); // Draw angle. DrawAngleIndicator(line1, line2, 8f, 8f, overlayBuffer, tooltips); @@ -205,11 +209,28 @@ public override void DrawOverlay(float3 currentPos, OverlayRenderSystem.Buffer o else { // Initial position only; just draw a straight line (constrained if required). - base.DrawOverlay(ConstrainPos(currentPos), overlayBuffer, tooltips); + base.DrawOverlay(overlayBuffer, tooltips); } } } + /// + /// Draws point overlays. + /// + /// Overlay buffer. + public override void DrawPointOverlays(OverlayRenderSystem.Buffer overlayBuffer) + { + base.DrawPointOverlays(overlayBuffer); + + // Draw elbow point. + if (_validElbow) + { + Color softCyan = Color.cyan; + softCyan.a *= 0.1f; + overlayBuffer.DrawCircle(Color.cyan, softCyan, 0.3f, 0, new float2(0f, 1f), _elbowPoint, PointRadius * 2f); + } + } + /// /// Clears the current selection. /// @@ -228,6 +249,44 @@ public override void Reset() } } + /// + /// Checks to see if a click should initiate point dragging. + /// + /// Click position in world space. + /// Drag mode. + internal override DragMode CheckDragHit(float3 position) + { + // Start and end points. + DragMode mode = base.CheckDragHit(position); + + // If no hit from base (start and end points), check for elbow point hit. + if (mode == DragMode.None && _validElbow && math.distancesq(position, _elbowPoint) < (PointRadius * PointRadius)) + { + return DragMode.ElbowPos; + } + + return mode; + } + + /// + /// Handles dragging action. + /// + /// Dragging mode. + /// New position. + internal override void HandleDrag(DragMode dragMode, float3 position) + { + if (dragMode == DragMode.ElbowPos) + { + // Update elbow point. + _elbowPoint = position; + } + else + { + // Other points. + base.HandleDrag(dragMode, position); + } + } + /// /// Applies any active constraints the given current cursor world position. /// diff --git a/Code/Systems/LineToolSystem.cs b/Code/Systems/LineToolSystem.cs index 55fb2da..258248b 100644 --- a/Code/Systems/LineToolSystem.cs +++ b/Code/Systems/LineToolSystem.cs @@ -65,8 +65,9 @@ public sealed partial class LineToolSystem : ObjectToolBaseSystem private InputAction _keepBuildingAction; // Mode. +as private LineBase _mode; private LineMode _currentMode; - private LineBase _mode; + private DragMode _dragMode = DragMode.None; // Tool settings. private SpacingMode _spacingMode = SpacingMode.Manual; @@ -81,6 +82,32 @@ public sealed partial class LineToolSystem : ObjectToolBaseSystem private ToolBaseSystem _treeControllerTool; private PropertyInfo _nextTreeState = null; + /// + /// Point dragging mode. + /// + internal enum DragMode + { + /// + /// No dragging. + /// + None = 0, + + /// + /// Dragging the line's start position. + /// + StartPos, + + /// + /// Dragging the line's end position. + /// + EndPos, + + /// + /// Dragging the line's elbow position. + /// + ElbowPos, + } + /// /// Gets the tool's ID string. /// @@ -411,6 +438,32 @@ protected override JobHandle OnUpdate(JobHandle inputDeps) _terrainHeightData = _terrainSystem.GetHeightData(); position.y = TerrainUtils.SampleHeight(ref _terrainHeightData, position); + // Handle any dragging. + if (_dragMode != DragMode.None) + { + if (_applyAction.WasReleasedThisFrame() || _fixedPreviewAction.WasReleasedThisFrame()) + { + // Cancel dragging. + _dragMode = DragMode.None; + } + else + { + // Drag end point. + if (_dragMode == DragMode.EndPos) + { + position = _raycastPoint.m_HitPosition; + _fixedPos = position; + } + else + { + // Handle dragging for other points via line mode instance. + _mode.HandleDrag(_dragMode, _raycastPoint.m_HitPosition); + } + + _dirty = true; + } + } + // Check for and perform any cancellation. if (_cancelAction.WasPressedThisFrame()) { @@ -424,6 +477,7 @@ protected override JobHandle OnUpdate(JobHandle inputDeps) } _previewEntities.Clear(); + _dragMode = DragMode.None; return inputDeps; } @@ -431,8 +485,23 @@ protected override JobHandle OnUpdate(JobHandle inputDeps) // If no cancellation, handle any fixed preview action if we're ready to place. else if (_fixedPreviewAction.WasPressedThisFrame() && _mode.HasAllPoints) { - _fixedPreview = true; - _fixedPos = position; + // Are we already in fixed preview mode? + if (_fixedPreview) + { + // Already in fixed preview mode - check for dragging hits. + _dragMode = _mode.CheckDragHit(_raycastPoint.m_HitPosition); + if (_dragMode != DragMode.None) + { + // If dragging, has started, then we're done here. + return inputDeps; + } + } + else + { + // Activate fixed preview mode and fix current position. + _fixedPreview = true; + _fixedPos = position; + } } // Handle apply action if no other actions. @@ -441,6 +510,14 @@ protected override JobHandle OnUpdate(JobHandle inputDeps) // Were we in fixed state? if (_fixedPreview) { + // Check for dragging hits. + _dragMode = _mode.CheckDragHit(_raycastPoint.m_HitPosition); + if (_dragMode != DragMode.None) + { + // If dragging, has started, then we're done here. + return inputDeps; + } + // Yes - cancel fixed preview. _fixedPreview = false; } @@ -518,7 +595,13 @@ protected override JobHandle OnUpdate(JobHandle inputDeps) } // Render any overlay. - _mode.DrawOverlay(position, _overlayBuffer, _tooltips); + _mode.DrawOverlay(_overlayBuffer, _tooltips); + + // Overlay control points. + if (_fixedPreview) + { + _mode.DrawPointOverlays(_overlayBuffer); + } // Check for position change or update needed. if (!_dirty && position.x == _previousPos.x && position.z == _previousPos.y) diff --git a/LineToolLite.csproj b/LineToolLite.csproj index 39106f5..6edbc98 100644 --- a/LineToolLite.csproj +++ b/LineToolLite.csproj @@ -6,7 +6,7 @@ algernon Copyright © 2023 algernon (github.com/algernon-A). All rights reserved. $(Title) - 1.0.10 + 1.1.0 9.0 True diff --git a/README.md b/README.md index 8863c1d..87f64b0 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,7 @@ Works on all types of objects - trees, shrubs, props. You can even place simple - **Left-click** where you want the line to begin, and **left-click again** at the desired endpoint to place the objects (curves require three clicks - start, guidepoint, and end). - **Right-click** at any time to cancel placement. - **Shift-click** at the end will start a new line placement at the exact same spot where the previous line ended. -- **Control-click** at the end will leave the line in preview mode; it's not fully placed yet so you can go and adjust the settings and see the results in real time. When finished, **left-click** to place or **right-click** to cancel. +- **Control-click** at the end will leave the line in preview mode; it's not fully placed yet so you can go and adjust the settings and see the results in real time. You can also drag the highlighted control points (blue circles) to adjust the line positioning (**control-clicking** to start dragging will ensure that you don't accidentally trigger placement if you miss the point circles, but regular clicking works as well). When finished, **left-click** to place or **right-click** to cancel. ### Use the tool UI to: - Toggle **fence mode** - objects will be automatically aligned with the line direction and placed continuously end-to-end.