Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

交差点編集でトラックや外形の形状を変更できるようにした #394

Merged
merged 11 commits into from
Jan 14, 2025
8 changes: 8 additions & 0 deletions Editor/RoadNetwork/AddSystem.meta

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

74 changes: 74 additions & 0 deletions Editor/RoadNetwork/AddSystem/RoadNetworkAddSystem.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
using PLATEAU.Editor.RoadNetwork.EditingSystemSubMod;
using PLATEAU.RoadAdjust.RoadNetworkToMesh;
using PLATEAU.RoadAdjust;
using PLATEAU.RoadNetwork.Structure;
using UnityEditor;
using UnityEngine;

namespace PLATEAU.Editor.RoadNetwork
{
internal class RoadNetworkAddSystem
{
public static RoadNetworkAddSystem Active { get; private set; }

public RoadNetworkAddSystemContext Context { get; private set; }
public RnRoadAddSystem RoadAddSystem { get; private set; }

private RoadNetworkAddSystem(PLATEAURnStructureModel structureModel)
{
Context = new RoadNetworkAddSystemContext(structureModel);

RoadAddSystem = new RnRoadAddSystem(Context);
RoadAddSystem.OnRoadAdded = (roadGroup) =>
{
// 道路モデル再生成
bool crosswalkExists = PLATEAUReproducedRoad.Find(ReproducedRoadType.Crosswalk, roadGroup.Roads[0].TargetTrans[0].transform, ReproducedRoadDirection.Next);
new RoadReproducer().Generate(new RrTargetRoadBases(Context.RoadNetwork, roadGroup.Roads), crosswalkExists ? CrosswalkFrequency.All : CrosswalkFrequency.Delete);

// スケルトン更新
Context.SkeletonData.UpdateData(roadGroup);
};
}

public static bool TryInitializeGlobal()
{
Active?.Terminate();

var structureModel = Object.FindObjectOfType<PLATEAURnStructureModel>();
if (structureModel == null)
return false;

Active = new RoadNetworkAddSystem(structureModel);

// SceneViewの更新イベントにフック
SceneView.duringSceneGui += Active.OnSceneGUI;

return true;
}

public static void TerminateGlobal()
{
if (Active == null)
return;

Active.Terminate();

// SceneViewの更新イベントから除外
SceneView.duringSceneGui -= Active.OnSceneGUI;

Active = null;

return;
}

private void Terminate()
{
RoadAddSystem.Deactivate();
}

private void OnSceneGUI(SceneView sceneView)
{
RoadAddSystem.HandleSceneGUI(sceneView);
}
}
}
11 changes: 11 additions & 0 deletions Editor/RoadNetwork/AddSystem/RoadNetworkAddSystem.cs.meta

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

17 changes: 17 additions & 0 deletions Editor/RoadNetwork/AddSystem/RoadNetworkAddSystemContext.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
using PLATEAU.RoadNetwork;
using PLATEAU.RoadNetwork.Structure;

namespace PLATEAU.Editor.RoadNetwork
{
internal class RoadNetworkAddSystemContext
{
public RnModel RoadNetwork { get; private set; }
public RoadNetworkSkeletonData SkeletonData { get; private set; }

public RoadNetworkAddSystemContext(PLATEAURnStructureModel structureModel)
{
SkeletonData = new RoadNetworkSkeletonData(structureModel);
RoadNetwork = structureModel.RoadNetwork;
}
}
}
11 changes: 11 additions & 0 deletions Editor/RoadNetwork/AddSystem/RoadNetworkAddSystemContext.cs.meta

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,20 @@ public class IntersectionEditSceneViewGui
{
private RnIntersection targetIntersection;
private readonly IntersectionTrackEditor trackEditor = new();
private readonly IntersectionLineEditor lineEditor;
private readonly RoadNetworkEditTarget editTarget;

public IntersectionEditSceneViewGui(RoadNetworkEditTarget target)
{
editTarget = target;
lineEditor = new IntersectionLineEditor(target);
}

public void Update()
{
if (targetIntersection == null) return;
trackEditor.Draw(editTarget, targetIntersection);
lineEditor.Draw();
}

public void Terminate()
Expand Down
247 changes: 247 additions & 0 deletions Editor/RoadNetwork/EditingSystem/IntersectionLineEditor.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,247 @@
using PLATEAU.Editor.RoadNetwork.EditingSystemSubMod;
using PLATEAU.RoadAdjust;
using PLATEAU.RoadAdjust.RoadNetworkToMesh;
using PLATEAU.RoadNetwork.Structure;
using PLATEAU.Util.GeoGraph;
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEditor;
using UnityEngine;
using UnityEngine.Splines;

namespace PLATEAU.Editor.RoadNetwork.EditingSystem
{
/// <summary>
/// 交差点の線の形状を編集します。
/// </summary>
internal class IntersectionLineEditor : ICreatedSplineReceiver
{
private RoadNetworkEditTarget target;
private IEditTargetLine mouseHoveredLine;
private IEditTargetLine selectedLine;
private LineEditState state = LineEditState.LineNotSelected;
private SplineEditorCore splineCore;

private enum LineEditState
{
LineNotSelected, LineSelected
}

public IntersectionLineEditor(RoadNetworkEditTarget target)
{
this.target = target;
}

/// <summary> 毎フレームのシーンビューへの描画 </summary>
public void Draw()
{
var intersectionEdit = target.SelectedRoadNetworkElement as EditorData<RnIntersection>;
var intersection = intersectionEdit?.Ref;
if (intersection == null) return;


switch (state)
{
case LineEditState.LineNotSelected:
var lines = IEditTargetLine.ComposeFrom(intersection);
DrawIntersectionShapes(lines);
bool isNewLineClicked = CheckLineClicked();
if (isNewLineClicked)
{
state = LineEditState.LineSelected;
StartEditingLine();
}
break;
case LineEditState.LineSelected:
DrawPreviewLine();
SplineEditorHandles.HandleSceneGUI(splineCore);
if (Event.current.keyCode == KeyCode.Return)
{
OnSplineCreated(splineCore.Spline);
}
break;
default:
throw new ArgumentOutOfRangeException();
}


}

/// <summary> 線の編集を開始します </summary>
private void StartEditingLine()
{
var spline = new Spline();
foreach (var point in selectedLine.Line)
{
spline.Add(new BezierKnot(point));
}

splineCore = new SplineEditorCore(spline);
}

/// <summary> 線の編集を確定したとき、それを道路ネットワークに適用します。 </summary>
public void OnSplineCreated(Spline createdSpline)
{
state = LineEditState.LineNotSelected;

var targetIntersection = (target.SelectedRoadNetworkElement as EditorData<RnIntersection>)?.Ref;

selectedLine.Apply(targetIntersection, createdSpline);

var reproduceTarget = new RrTargetRoadBases(target.RoadNetwork, new[]{targetIntersection});
new RoadReproducer().Generate(reproduceTarget, CrosswalkFrequency.All);

selectedLine = null;
}


private void DrawIntersectionShapes(IEditTargetLine[] lines)
{
mouseHoveredLine = GetMouseHoveredLine(lines);
foreach (var line in lines)
{
bool isMouseHovered = line == mouseHoveredLine;
if (!isMouseHovered && line is EditTargetTrack)
{
// マウスホバーでないトラックの線は、ここではなくIntersectionTrackEditorで描画します。
continue;
}
var color = isMouseHovered ? Color.red : Color.cyan;

// マウスホバーの線が他の線と埋もれないように少し位置を上げます。
var drawingLine = isMouseHovered ? line.Line.Select(l => l + Vector3.up * 0.01f) : line.Line;
DrawLine(drawingLine.ToArray(), color);
}
}

private void DrawLine(Vector3[] line, Color color)
{
var drawer = new LaneLineDrawerSolid(line.ToList(), color, LaneLineDrawMethod.Handles);
drawer.Draw();
}

private void DrawPreviewLine()
{
var line = splineCore.Spline.Knots.Select(k => k.Position).Select(f => new Vector3(f.x, f.y, f.z)).ToList();
var drawer = new LaneLineDrawerSolid(line, new Color(1f, 0.4f, 0.3f), LaneLineDrawMethod.Handles);
drawer.Draw();
}


private bool CheckLineClicked()
{
if (mouseHoveredLine == null) return false;
if (!LineUtil.IsMouseDown()) return false;
bool isNewLineClicked = selectedLine != mouseHoveredLine;
selectedLine = mouseHoveredLine;
return isNewLineClicked;
}

private IEditTargetLine GetMouseHoveredLine(IEditTargetLine[] lines)
{
var mouseScreenPos = Event.current.mousePosition;
var ray = HandleUtility.GUIPointToWorldRay(mouseScreenPos);
float minDist = float.MaxValue;
IEditTargetLine nearestLine = null;
foreach (var line in lines)
{
var positions = line.Line;
for (int i = 0; i < positions.Length - 1; i++)
{
var p1 = positions[i];
var p2 = positions[i + 1];
var distance = LineUtil.CheckHit(new LineUtil.Line(p1, p2), 2f, ray, out var closestPoint,
out var closestPoint2);
if (distance >= 0f && distance < minDist)
{
minDist = distance;
nearestLine = line;
}
}
}

return nearestLine;
}

private interface IEditTargetLine
{
public void Apply(RnIntersection intersection, Spline createdSpline);

public Vector3[] Line { get;}

public static IEditTargetLine[] ComposeFrom(RnIntersection intersection)
{
var shapes = intersection
.Edges
.Where(e => e.Road == null)
.Select(n => n.Border)
.Select(b => new EditTargetShape(b));
var tracks = intersection
.Tracks
.Select(t => new EditTargetTrack(t));
return shapes.Cast<IEditTargetLine>().Concat(tracks).ToArray();
}
}

private class EditTargetTrack : IEditTargetLine
{
public RnTrack Track { get; set; }

public EditTargetTrack(RnTrack track)
{
Track = track;
}

public void Apply(RnIntersection intersection, Spline createdSpline)
{
Track.Spline = createdSpline;
}

public Vector3[] Line
{
get => Track.Spline.Knots.Select(k => k.Position).Select(f => new Vector3(f.x, f.y, f.z)).ToArray();
}
}

/// <summary> 交差点で編集対象となる線 </summary>
private class EditTargetShape : IEditTargetLine
{
private RnWay Way { get; set; }

public EditTargetShape(RnWay way)
{
Way = way;
}


/// <summary> 編集した線を道路ネットワークに適用します。 </summary>
public void Apply(RnIntersection intersection, Spline createdSpline)
{
// 交差点のEdgeの線に対して適用するのと同じく、対応するSidewalkを探して同じように適用します。
// Sidewalkへ適用しないと、白線は変わるのに歩道の形は変わらなくなります。
var correspondSidewalkInside =
intersection
?.SideWalks
.Select(sw => sw.InsideWay)
.FirstOrDefault(way => way.IsSameLineSequence(way));

var positions = createdSpline.Knots.Select(k => new RnPoint(k.Position)).ToArray();
var line = new RnLineString(positions);

// 交差点のEdgeに線を適用します。
Way.LineString = line;

// 歩道に線に適用します。
if (correspondSidewalkInside != null)
{
correspondSidewalkInside.LineString = line;
}
}

public Vector3[] Line{
get => Way.LineString.Points.Select(p => p.Vertex).Select(v => new Vector3(v.x, v.y, v.z)).ToArray();
}
}
}
}

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading