Skip to content

Commit

Permalink
BeltRail editing support
Browse files Browse the repository at this point in the history
  • Loading branch information
jupahe64 committed Jan 4, 2024
1 parent 701f280 commit 1b34239
Show file tree
Hide file tree
Showing 6 changed files with 260 additions and 104 deletions.
14 changes: 14 additions & 0 deletions Fushigi/ui/CourseAreaEditContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,20 @@ public void DeleteWall(CourseUnit unit, Wall wall)
$"{IconUtil.ICON_TRASH} Delete Wall"));
}

public void AddBeltRail(CourseUnit unit, BGUnitRail rail)
{
LogAdding<BGUnitRail>();
CommitAction(unit.mBeltRails.RevertableAdd(rail,
$"{IconUtil.ICON_PLUS_CIRCLE} Add Belt"));
}

public void DeleteBeltRail(CourseUnit unit, BGUnitRail rail)
{
LogDeleting<BGUnitRail>();
CommitAction(unit.mBeltRails.RevertableRemove(rail,
$"{IconUtil.ICON_TRASH} Delete Belt"));
}

private void LogAdding<T>(ulong hash) =>
LogAdding<T>($"[{hash}]");

Expand Down
46 changes: 33 additions & 13 deletions Fushigi/ui/SceneObjects/bgunit/BGUnitRailSceneObj.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

namespace Fushigi.ui.SceneObjects.bgunit
{
internal class BGUnitRailSceneObj(CourseUnit unit, BGUnitRail rail) : ISceneObject, IViewportDrawable, IViewportSelectable
internal class BGUnitRailSceneObj(CourseUnit unit, BGUnitRail rail, bool isBelt) : ISceneObject, IViewportDrawable, IViewportSelectable
{
public IReadOnlyDictionary<BGUnitRail.RailPoint, RailPoint> ChildPoints;
public List<BGUnitRail.RailPoint> GetSelected(CourseAreaEditContext ctx) => rail.Points.Where(ctx.IsSelected).ToList();
Expand Down Expand Up @@ -114,7 +114,7 @@ public void OnKeyDown(CourseAreaEditContext ctx, LevelViewport viewport)

private bool HitTest(LevelViewport viewport)
{
return LevelViewport.HitTestLineLoopPoint(GetPoints(viewport), 10f,
return MathUtil.HitTestLineLoopPoint(GetPoints(viewport), 10f,
ImGui.GetMousePos());
}

Expand Down Expand Up @@ -300,7 +300,7 @@ void IViewportDrawable.Draw2D(CourseAreaEditContext ctx, LevelViewport viewport,

addPointPos = EvaluateAddPointPos(ctx, viewport);

if ((addPointPos.HasValue && ctx.IsSelected(rail)) || HitTest(viewport))
if ((addPointPos.HasValue && ctx.IsSelected(rail)) || (!isBelt && HitTest(viewport)))
isNewHoveredObj = true;

bool isSelected = IsSelected(ctx);
Expand All @@ -318,31 +318,49 @@ void IViewportDrawable.Draw2D(CourseAreaEditContext ctx, LevelViewport viewport,

var lineThickness = viewport.IsHovered(this) ? 3.5f : 2.5f;



for (int i = 0; i < rail.Points.Count; i++)
{
Vector3 point = rail.Points[i].Position;
var pos2D = viewport.WorldToScreen(new(point.X, point.Y, point.Z));
Vector3 pos = rail.Points[i].Position;

//Next pos 2D
Vector2 nextPos2D = Vector2.Zero;
Vector3 nextPos;
if (i < rail.Points.Count - 1) //is not last point
{
nextPos2D = viewport.WorldToScreen(
rail.Points[i + 1].Position);
nextPos = rail.Points[i + 1].Position;
}
else if (rail.IsClosed) //last point to first if closed
{
nextPos2D = viewport.WorldToScreen(
rail.Points[0].Position);
nextPos = rail.Points[0].Position;
}
else //last point but not closed, draw no line
continue;

var pos2D = viewport.WorldToScreen(pos);
var nextPos2D = viewport.WorldToScreen(nextPos);

uint line_color = IsValidAngle(pos2D, nextPos2D) ? Color_Default : Color_SlopeError;
if (isSelected && line_color != Color_SlopeError)
line_color = Color_SelectionEdit;

dl.AddLine(pos2D, nextPos2D, line_color, lineThickness);
if (isBelt)
{
var bottomPos2D = viewport.WorldToScreen(pos - Vector3.UnitY * 0.5f);
var bottomNextPos2D = viewport.WorldToScreen(nextPos - Vector3.UnitY * 0.5f);

dl.AddQuadFilled(pos2D, nextPos2D, bottomNextPos2D, bottomPos2D, line_color & 0x00FFFFFF | 0x55000000);
dl.AddQuad(pos2D, nextPos2D, bottomNextPos2D, bottomPos2D, line_color, lineThickness - 1);

if (MathUtil.HitTestConvexQuad(pos2D, nextPos2D, bottomNextPos2D, bottomPos2D,
ImGui.GetMousePos()))
{
isNewHoveredObj = true;
}
}
else
{
dl.AddLine(pos2D, nextPos2D, line_color, lineThickness);
}

if (isSelected)
{
Expand Down Expand Up @@ -379,7 +397,9 @@ void IViewportDrawable.Draw2D(CourseAreaEditContext ctx, LevelViewport viewport,
var pointB = viewport.WorldToScreen(rail.Points.GetWrapped(index).Position);
var pointC = pos2D;

dl.AddTriangleFilled(pointA, pointB, pointC, 0x99FFFFFF);
if(!isBelt)
dl.AddTriangleFilled(pointA, pointB, pointC, 0x99FFFFFF);

if(rail.IsClosed || index > 0)
dl.AddLine(pointA, pointC, 0xFFFFFFFF, 2.5f);
if(rail.IsClosed || index < rail.Points.Count)
Expand Down
14 changes: 8 additions & 6 deletions Fushigi/ui/SceneObjects/bgunit/BGUnitSceneObj.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,15 @@ public void Update(ISceneUpdateContext ctx, bool isSelected)
{
unit.GenerateTileSubUnits();

void CreateOrUpdateRail(BGUnitRail rail)
void CreateOrUpdateRail(BGUnitRail rail, bool isBelt = false)
{
ctx.UpdateOrCreateObjFor(rail, () => new BGUnitRailSceneObj(unit, rail));
ctx.UpdateOrCreateObjFor(rail, () => new BGUnitRailSceneObj(unit, rail, isBelt));
}

if (unit.mModelType is CourseUnit.ModelType.SemiSolid or CourseUnit.ModelType.Bridge)
{
foreach (var rail in unit.mBeltRails)
CreateOrUpdateRail(rail, true);
}

foreach (var wall in unit.Walls)
Expand All @@ -21,10 +27,6 @@ void CreateOrUpdateRail(BGUnitRail rail)
CreateOrUpdateRail(rail);
}
}

//Don't include belt for now. TODO how should this be handled?
//foreach (var rail in unit.mBeltRails)
// CreateOrUpdateRail(rail);
}
}
}
169 changes: 142 additions & 27 deletions Fushigi/ui/widgets/CourseScene.cs
Original file line number Diff line number Diff line change
Expand Up @@ -877,6 +877,90 @@ private void SelectionParameterPanel()

ImGui.Columns(1);
}

if(mSelectedUnit.mModelType is CourseUnit.ModelType.SemiSolid)
{
ImGui.Spacing();
ImGui.Separator();
ImGui.Spacing();

if(ImGui.Button("Remove all Belts"))
{
var batchAction = editContext.BeginBatchAction();

for (int i = mSelectedUnit.mBeltRails.Count - 1; i >= 0; i--)
editContext.DeleteBeltRail(mSelectedUnit, mSelectedUnit.mBeltRails[i]);

batchAction.Commit("Remove all Belts from TileUnit");
}

ImGui.SameLine();

if (ImGui.Button("Generate Belts"))
{
var batchAction = editContext.BeginBatchAction();

void ProcessRail(BGUnitRail rail)
{
if (rail.Points.Count <= 1)
return;

BGUnitRail? firstBeltRail = null;
BGUnitRail? currentBeltRail = null;

var lastPoint = new Vector3(float.NaN);

for (int i = 0; i < rail.Points.Count; i++)
{
var point0 = rail.Points[i].Position;
var point1 = rail.Points.GetWrapped(i + 1).Position;

if (point0.X >= point1.X)
continue;

if (point0 != lastPoint)
{
if (currentBeltRail is not null)
editContext.AddBeltRail(mSelectedUnit, currentBeltRail);

currentBeltRail = new BGUnitRail(mSelectedUnit);
currentBeltRail.Points.Add(new BGUnitRail.RailPoint(currentBeltRail, point0));
firstBeltRail ??= currentBeltRail;
}

currentBeltRail!.Points.Add(new BGUnitRail.RailPoint(currentBeltRail, point1));
lastPoint = point1;
}

var lastBeltRail = currentBeltRail;

if(firstBeltRail is not null && lastBeltRail is not null &&
firstBeltRail != lastBeltRail &&
lastBeltRail.Points[^1].Position == firstBeltRail.Points[0].Position)
{
//connect first and last rail

for (int i = 0; i < lastBeltRail.Points.Count-1; i++)
{
var position = lastBeltRail.Points[i].Position;
firstBeltRail.Points.Insert(i, new BGUnitRail.RailPoint(firstBeltRail, position));
}
}
else if (lastBeltRail is not null)
editContext.AddBeltRail(mSelectedUnit, lastBeltRail);
}

foreach (var wall in mSelectedUnit.Walls)
{
ProcessRail(wall.ExternalRail);

foreach (var internalRail in wall.InternalRails)
ProcessRail(internalRail);
}

batchAction.Commit("Add Belts");
}
}
}
else if (editContext.IsSingleObjectSelected(out BGUnitRail? mSelectedUnitRail))
{
Expand Down Expand Up @@ -1317,45 +1401,76 @@ void SelectRail()
}
}

if (ImGui.Button("Add Wall"))
editContext.AddWall(unit, new Wall(unit));
ImGui.SameLine();
if (ImGui.Button("Remove Wall"))
if (unit.mModelType is not CourseUnit.ModelType.Bridge)
{
editContext.WithSuspendUpdateDo(() =>
if (ImGui.Button("Add Wall"))
editContext.AddWall(unit, new Wall(unit));
ImGui.SameLine();
if (ImGui.Button("Remove Wall"))
{
for (int i = unit.Walls.Count - 1; i >= 0; i--)
editContext.WithSuspendUpdateDo(() =>
{
//TODO is that REALLY how we want to do this?
if (editContext.IsSelected(unit.Walls[i].ExternalRail))
editContext.DeleteWall(unit, unit.Walls[i]);
}
});
}
for (int i = unit.Walls.Count - 1; i >= 0; i--)
{
//TODO is that REALLY how we want to do this?
if (editContext.IsSelected(unit.Walls[i].ExternalRail))
editContext.DeleteWall(unit, unit.Walls[i]);
}
});
}

foreach (var wall in unit.Walls)
{
if (wall.InternalRails.Count > 0)
for (int iWall = 0; iWall < unit.Walls.Count; iWall++)
{
bool ex = ImGui.TreeNodeEx($"##{name}Wall{unit.Walls.IndexOf(wall)}", ImGuiTreeNodeFlags.DefaultOpen);
ImGui.SameLine();
Wall wall = unit.Walls[iWall];
if (wall.InternalRails.Count > 0)
{
ImGui.Unindent();
bool ex = ImGui.TreeNodeEx($"##{name}Wall{iWall}", ImGuiTreeNodeFlags.DefaultOpen);
ImGui.SameLine();

RailListItem("Wall", wall.ExternalRail, unit.Walls.IndexOf(wall));
RailListItem("Wall", wall.ExternalRail, unit.Walls.IndexOf(wall));

ImGui.Indent();
ImGui.Indent();

if (ex)
if (ex)
{
for (int iInternal = 0; iInternal < wall.InternalRails.Count; iInternal++)
{
BGUnitRail? rail = wall.InternalRails[iInternal];
RailListItem("Internal Rail", rail, iInternal);
}
}

ImGui.TreePop();
}
else
{
foreach (var rail in wall.InternalRails)
RailListItem("Internal Rail", rail, wall.InternalRails.IndexOf(rail));
RailListItem("Wall", wall.ExternalRail, iWall);
}
ImGui.Unindent();
}
}

ImGui.TreePop();
if (unit.mModelType is CourseUnit.ModelType.SemiSolid or CourseUnit.ModelType.Bridge)
{
if (ImGui.Button("Add Belt"))
editContext.AddBeltRail(unit, new BGUnitRail(unit));
ImGui.SameLine();
if (ImGui.Button("Remove Belt"))
{
editContext.WithSuspendUpdateDo(() =>
{
for (int i = unit.mBeltRails.Count - 1; i >= 0; i--)
{
if (editContext.IsSelected(unit.mBeltRails[i]))
editContext.DeleteBeltRail(unit, unit.mBeltRails[i]);
}
});
}
else

for (int iBeltRail = 0; iBeltRail < unit.mBeltRails.Count; iBeltRail++)
{
RailListItem("Wall", wall.ExternalRail, unit.Walls.IndexOf(wall));
BGUnitRail beltRail = unit.mBeltRails[iBeltRail];
RailListItem("Belt", beltRail, iBeltRail);
}
}
ImGui.TreePop();
Expand Down Expand Up @@ -1878,7 +1993,7 @@ private void CourseMiniView()
{
foreach(var wall in foregroundTileUnits
.SelectMany(x => x.Walls)
.Where(x => x.ExternalRail.Points[0].Position.Z == subUnit.mOrigin.Z))
.Where(x => x.ExternalRail.Points.FirstOrDefault()?.Position.Z == subUnit.mOrigin.Z))
{
var rail = wall.ExternalRail;

Expand Down
Loading

0 comments on commit 1b34239

Please sign in to comment.