From 53457b7df5c8293300021d7c3cac1eb3621f029c Mon Sep 17 00:00:00 2001 From: IrishBruse Date: Sun, 24 Dec 2023 02:16:03 +0000 Subject: [PATCH 01/19] Fix layer deselection when using the mouse --- Fushigi/ui/widgets/CourseScene.cs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/Fushigi/ui/widgets/CourseScene.cs b/Fushigi/ui/widgets/CourseScene.cs index a4b580ca..2ec324c0 100644 --- a/Fushigi/ui/widgets/CourseScene.cs +++ b/Fushigi/ui/widgets/CourseScene.cs @@ -300,9 +300,12 @@ void SelectPalette(string name, string palette) if (ImGui.IsWindowFocused()) { - selectedArea = area; + if (selectedArea != area) + { + selectedArea = area; + mHasFilledLayers = false; + } activeViewport = viewport; - mHasFilledLayers = false; } var topLeft = ImGui.GetCursorScreenPos(); From 4d46cf261a6f15b9abec7ece8149ad44261a2409 Mon Sep 17 00:00:00 2001 From: Donavin Draws <51259260+DonavinDraws@users.noreply.github.com> Date: Tue, 2 Jan 2024 17:13:40 -0600 Subject: [PATCH 02/19] Render, Minimap, and Link Tweeks -Fixed the Render glitching when loading into levels -Allows holding shift to link multiple destinations to a source link -Minimap now draws with depth and differentiates solids and semi-solids --- Fushigi/actor_pack/components/GamePhysics.cs | 3 + Fushigi/gl/Bfres/BfresRender.cs | 3 + Fushigi/ui/widgets/CourseScene.cs | 218 ++++++++++++------- Fushigi/ui/widgets/LevelViewport.cs | 2 +- 4 files changed, 145 insertions(+), 81 deletions(-) diff --git a/Fushigi/actor_pack/components/GamePhysics.cs b/Fushigi/actor_pack/components/GamePhysics.cs index dafb0b75..fea9e567 100644 --- a/Fushigi/actor_pack/components/GamePhysics.cs +++ b/Fushigi/actor_pack/components/GamePhysics.cs @@ -11,6 +11,9 @@ namespace Fushigi.actor_pack.components [Serializable] public class GamePhysics { + [BymlProperty("$parent")] + public string parent { get; set; } + [BymlProperty("ControllerSetPath", DefaultValue = "")] public string mPath { get; set; } } diff --git a/Fushigi/gl/Bfres/BfresRender.cs b/Fushigi/gl/Bfres/BfresRender.cs index ba1c4065..cc34949a 100644 --- a/Fushigi/gl/Bfres/BfresRender.cs +++ b/Fushigi/gl/Bfres/BfresRender.cs @@ -92,7 +92,10 @@ public BfresModel(BfresModel bfresModel) internal void Render(GL gl, BfresRender render, Matrix4x4 transform, Camera camera) { foreach (var mesh in Meshes) + { + mesh.LodMeshes[0].BoundingBox.Transform(transform); BoundingBox.Include(mesh.LodMeshes[0].BoundingBox); + } if (!IsVisible || !camera.InFrustum(BoundingBox)) return; diff --git a/Fushigi/ui/widgets/CourseScene.cs b/Fushigi/ui/widgets/CourseScene.cs index a4b580ca..bbc28b16 100644 --- a/Fushigi/ui/widgets/CourseScene.cs +++ b/Fushigi/ui/widgets/CourseScene.cs @@ -17,6 +17,7 @@ using System.Runtime.InteropServices; using Fushigi.rstb; using Fushigi.ui.helpers; +using Fasterflect; namespace Fushigi.ui.widgets { @@ -702,19 +703,23 @@ private void SelectionParameterPanel() if (ImGui.Selectable(linkType)) { + KeyboardModifier modifier; ImGui.SetWindowFocus(selectedArea.GetName()); Task.Run(async () => { - var pickedDest = await PickLinkDestInViewportFor(mSelectedActor); - if (pickedDest is null) - return; - - var link = new CourseLink(linkType) + do { - mSource = mSelectedActor.mHash, - mDest = pickedDest.mHash - }; - editContext.AddLink(link); + (var pickedDest, modifier) = await PickLinkDestInViewportFor(mSelectedActor); + if (pickedDest is null) + return; + + var link = new CourseLink(linkType) + { + mSource = mSelectedActor.mHash, + mDest = pickedDest.mHash + }; + editContext.AddLink(link); + } while ((modifier & KeyboardModifier.Shift) > 0); }); } } @@ -786,7 +791,7 @@ private void SelectionParameterPanel() ImGui.SetWindowFocus(selectedArea.GetName()); Task.Run(async () => { - var pickedDest = await PickLinkDestInViewportFor(mSelectedActor); + var (pickedDest, _) = await PickLinkDestInViewportFor(mSelectedActor); if (pickedDest is null) return; @@ -1440,92 +1445,104 @@ private void AreaLocalLinksView(CourseArea area) var wcMin = ImGui.GetCursorScreenPos() + new Vector2(0, ImGui.GetScrollY()); var wcMax = wcMin + ImGui.GetContentRegionAvail(); - RecursiveLinkFind(area, links, editContext, em); + var topLinks = area.GetActors() + .Where(x => links.GetDestHashesFromSrc(x.mHash).Count > 0); + + RecursiveLinkFind(area, links, editContext, em, topLinks); ImGui.PopClipRect(); ImGui.EndChild(); } - private void RecursiveLinkFind(CourseArea area, CourseLinkHolder links, CourseAreaEditContext editContext, float em) + private void RecursiveLinkFind(CourseArea area, CourseLinkHolder links, CourseAreaEditContext editContext, float em, IEnumerable linkList) { - foreach (CourseActor actor in area.GetActors().Where(x => !links.GetSrcHashesFromDest(x.mHash).Any() && links.GetDestHashesFromSrc(x.mHash).Any())) + foreach (CourseActor actor in linkList) { ImGuiTreeNodeFlags node_flags = ImGuiTreeNodeFlags.FramePadding | ImGuiTreeNodeFlags.OpenOnArrow; - ImGui.PushID(actor.mHash.ToString()); + ImGui.PushID($"##{actor.mHash}"); bool expanded = false; bool isVisible = true; float margin = 1.5f * em; float headerHeight = 1.4f * em; Vector2 cp = ImGui.GetCursorScreenPos(); - expanded = ImGui.TreeNodeEx(actor.mHash.ToString(), node_flags, actor.mPackName); - - if (ImGui.IsItemFocused()) + if(links.GetDestHashesFromSrc(actor.mHash).Count > 0) { - activeViewport.SelectedActor(actor); - } + expanded = ImGui.TreeNodeEx($"{actor.mHash}", node_flags, actor.mPackName); - if (ImGui.IsItemHovered() && ImGui.IsMouseDoubleClicked(0)) - { - activeViewport.FrameSelectedActor(actor); - } + if (ImGui.IsItemFocused()) + { + activeViewport.SelectedActor(actor); + } - if (!isVisible) - ImGui.BeginDisabled(); + if (ImGui.IsItemHovered() && ImGui.IsMouseDoubleClicked(0)) + { + activeViewport.FrameSelectedActor(actor); + } - if (expanded) - { - foreach (var link in links.GetDestHashesFromSrc(actor.mHash)) + if (!isVisible) + ImGui.BeginDisabled(); + + if (expanded) { - if(ImGui.TreeNodeEx(actor.mHash.ToString() + link.Key, ImGuiTreeNodeFlags.FramePadding, link.Key)) + foreach (var link in links.GetDestHashesFromSrc(actor.mHash)) { - foreach (CourseActor linkActor in area.GetActors().Where(x => link.Value.Contains(x.mHash))) + ImGui.PushID($"##{link.Key}"); + if(ImGui.TreeNodeEx($"##{link.Key}", ImGuiTreeNodeFlags.FramePadding, link.Key)) { - var act = linkActor; - string actorName = act.mPackName; - string name = act.mName; - ulong actorHash = act.mHash; - string actorLink = link.Key; - //Check if the node is within the necessary search filter requirements if search is used - bool HasText = act.mName.IndexOf(mActorSearchText, StringComparison.OrdinalIgnoreCase) >= 0 || - act.mPackName.IndexOf(mActorSearchText, StringComparison.OrdinalIgnoreCase) >= 0 || - act.ToString().Equals(mActorSearchText); - - if (!HasText) - continue; - - bool isSelected = editContext.IsSelected(act); - - ImGui.PushID(actorHash.ToString()); - ImGui.Columns(2); - - if (ImGui.Selectable(actorName, isSelected, ImGuiSelectableFlags.SpanAllColumns)) - { - activeViewport.SelectedActor(act); - } - else if (ImGui.IsItemFocused()) - { - activeViewport.SelectedActor(act); - } - - if (ImGui.IsItemHovered() && ImGui.IsMouseDoubleClicked(0)) - { - activeViewport.FrameSelectedActor(act); - } - - - ImGui.NextColumn(); - ImGui.BeginDisabled(); - ImGui.Text(name); - ImGui.EndDisabled(); - ImGui.Columns(1); - - ImGui.PopID(); + var reLinks = area.GetActors().Where(x => link.Value.Contains(x.mHash)); + RecursiveLinkFind(area, links, editContext, em, reLinks); + // foreach (CourseActor linkActor in ) + // { + // + // } ImGui.TreePop(); } + ImGui.PopID(); } + ImGui.TreePop(); } - ImGui.TreePop(); + } + else + { + string actorName = actor.mPackName; + string name = actor.mName; + ulong actorHash = actor.mHash; + //Check if the node is within the necessary search filter requirements if search is used + bool HasText = actor.mName.IndexOf(mActorSearchText, StringComparison.OrdinalIgnoreCase) >= 0 || + actor.mPackName.IndexOf(mActorSearchText, StringComparison.OrdinalIgnoreCase) >= 0 || + actor.ToString().Equals(mActorSearchText); + + if (!HasText) + continue; + + bool isSelected = editContext.IsSelected(actor); + + ImGui.PushID($"##{actorName}"); + ImGui.Columns(2); + + if (ImGui.Selectable(actorName, isSelected, ImGuiSelectableFlags.SpanAllColumns)) + { + activeViewport.SelectedActor(actor); + } + else if (ImGui.IsItemFocused()) + { + activeViewport.SelectedActor(actor); + } + + if (ImGui.IsItemHovered() && ImGui.IsMouseDoubleClicked(0)) + { + activeViewport.FrameSelectedActor(actor); + } + + + ImGui.NextColumn(); + ImGui.BeginDisabled(); + ImGui.Text(name); + ImGui.EndDisabled(); + ImGui.Columns(1); + + ImGui.PopID(); } if (!isVisible) @@ -1819,21 +1836,56 @@ private void CourseMiniView() } } - var foregroundSubUnits = area.mUnitHolder.mUnits - .Where(x => x.mModelType != CourseUnit.ModelType.NoCollision) - .SelectMany(x => x.mTileSubUnits); + var foregroundTileUnits = area.mUnitHolder.mUnits + .Where(x => x.mModelType != CourseUnit.ModelType.NoCollision); + + var foregroundSubUnits = foregroundTileUnits + .SelectMany(x => x.mTileSubUnits) + .OrderBy(x => x.mOrigin.Z); + var t = 0; foreach (var subUnit in foregroundSubUnits) { - var origin2D = new Vector2(subUnit.mOrigin.X, subUnit.mOrigin.Y); + var type = foregroundTileUnits.First(x => x.mTileSubUnits.Contains(subUnit)).mModelType; + var unitColor = 0xFF999999; + var edgeColor = 0xFFEEEEEE; + + switch (type) + { + case CourseUnit.ModelType.Solid: + unitColor = 0xFFBB9999; + edgeColor = 0xFFFFEEEE; + break; + case CourseUnit.ModelType.SemiSolid: + unitColor = 0xFF99BB99; + edgeColor = 0xFFEEFFEE; + break; + } + var origin2D = new Vector2(subUnit.mOrigin.X, subUnit.mOrigin.Y); foreach (var tile in subUnit.GetTiles(bb.Min - origin2D, bb.Max - origin2D)) { var pos = tile.pos + origin2D; dl.AddRectFilled( MapPointPixelAligned(pos), MapPointPixelAligned(pos + Vector2.One), - 0xFF999999); + unitColor); + } + if (subUnit == foregroundSubUnits.Last(x => x.mOrigin.Z == subUnit.mOrigin.Z)) + { + foreach(var wall in foregroundTileUnits + .SelectMany(x => x.Walls) + .Where(x => x.ExternalRail.Points[0].Position.Z == subUnit.mOrigin.Z)) + { + var rail = wall.ExternalRail; + + var pos = rail.Points.Select(x => MapPointPixelAligned(new(x.Position.X, x.Position.Y))).ToArray(); + dl.AddPolyline(ref pos[0], + rail.Points.Count, + edgeColor, + rail.IsClosed ? ImDrawFlags.Closed:ImDrawFlags.None, + 1.5f); + } } } @@ -2051,12 +2103,12 @@ public Course GetCourse() return course; } - private async Task PickLinkDestInViewportFor(CourseActor source) + private async Task<(CourseActor?, KeyboardModifier modifiers)> PickLinkDestInViewportFor(CourseActor source) { - var (picked, _) = await activeViewport.PickObject( - "Select the destination actor you wish to link to.", + var (picked, modifier) = await activeViewport.PickObject( + "Select the destination actor you wish to link to. -- Hold SHIFT to link multiple", x => x is CourseActor && x != source); - return picked as CourseActor; + return (picked as CourseActor, modifier); } private async Task DeleteObjectsWithWarningPrompt(IReadOnlyList objectsToDelete, @@ -2162,6 +2214,12 @@ private async Task AddActorsWithSelectActorAndLayerWindow() posVec.Y = MathF.Round(posVec.Y * 2, MidpointRounding.AwayFromZero) / 2; posVec.Z = 0.0f; actor.mTranslation = posVec; + var i = 0; + do + { + i++; + } while (area.GetActors().Any(x => x.mName == $"{actor.mPackName}{i}")); + actor.mName = $"{actor.mPackName}{i}"; ctx.AddActor(actor); diff --git a/Fushigi/ui/widgets/LevelViewport.cs b/Fushigi/ui/widgets/LevelViewport.cs index a5d01212..4e23a37c 100644 --- a/Fushigi/ui/widgets/LevelViewport.cs +++ b/Fushigi/ui/widgets/LevelViewport.cs @@ -1070,7 +1070,7 @@ Vector2[] GetPoints() Vector3 center = new(0f); var drawing = "box"; - if (actor.mActorPack.ShapeParams != null) + if (actor.mActorPack?.ShapeParams != null) { var shapes = actor.mActorPack.ShapeParams; var calc = shapes.mCalc; From 1b34239633195cfe9d11f54550f4bf5d750fb925 Mon Sep 17 00:00:00 2001 From: jupahe64 Date: Thu, 4 Jan 2024 04:19:50 +0100 Subject: [PATCH 03/19] BeltRail editing support --- Fushigi/ui/CourseAreaEditContext.cs | 14 ++ .../SceneObjects/bgunit/BGUnitRailSceneObj.cs | 46 +++-- .../ui/SceneObjects/bgunit/BGUnitSceneObj.cs | 14 +- Fushigi/ui/widgets/CourseScene.cs | 169 +++++++++++++++--- Fushigi/ui/widgets/LevelViewport.cs | 61 +------ Fushigi/util/MathUtil.cs | 60 +++++++ 6 files changed, 260 insertions(+), 104 deletions(-) diff --git a/Fushigi/ui/CourseAreaEditContext.cs b/Fushigi/ui/CourseAreaEditContext.cs index 18669421..6ce74850 100644 --- a/Fushigi/ui/CourseAreaEditContext.cs +++ b/Fushigi/ui/CourseAreaEditContext.cs @@ -215,6 +215,20 @@ public void DeleteWall(CourseUnit unit, Wall wall) $"{IconUtil.ICON_TRASH} Delete Wall")); } + public void AddBeltRail(CourseUnit unit, BGUnitRail rail) + { + LogAdding(); + CommitAction(unit.mBeltRails.RevertableAdd(rail, + $"{IconUtil.ICON_PLUS_CIRCLE} Add Belt")); + } + + public void DeleteBeltRail(CourseUnit unit, BGUnitRail rail) + { + LogDeleting(); + CommitAction(unit.mBeltRails.RevertableRemove(rail, + $"{IconUtil.ICON_TRASH} Delete Belt")); + } + private void LogAdding(ulong hash) => LogAdding($"[{hash}]"); diff --git a/Fushigi/ui/SceneObjects/bgunit/BGUnitRailSceneObj.cs b/Fushigi/ui/SceneObjects/bgunit/BGUnitRailSceneObj.cs index f3a8bf5e..19edfa29 100644 --- a/Fushigi/ui/SceneObjects/bgunit/BGUnitRailSceneObj.cs +++ b/Fushigi/ui/SceneObjects/bgunit/BGUnitRailSceneObj.cs @@ -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 ChildPoints; public List GetSelected(CourseAreaEditContext ctx) => rail.Points.Where(ctx.IsSelected).ToList(); @@ -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()); } @@ -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); @@ -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) { @@ -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) diff --git a/Fushigi/ui/SceneObjects/bgunit/BGUnitSceneObj.cs b/Fushigi/ui/SceneObjects/bgunit/BGUnitSceneObj.cs index f3323b73..f22e6b5c 100644 --- a/Fushigi/ui/SceneObjects/bgunit/BGUnitSceneObj.cs +++ b/Fushigi/ui/SceneObjects/bgunit/BGUnitSceneObj.cs @@ -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) @@ -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); } } } diff --git a/Fushigi/ui/widgets/CourseScene.cs b/Fushigi/ui/widgets/CourseScene.cs index aff30fb8..27da7171 100644 --- a/Fushigi/ui/widgets/CourseScene.cs +++ b/Fushigi/ui/widgets/CourseScene.cs @@ -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)) { @@ -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(); @@ -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; diff --git a/Fushigi/ui/widgets/LevelViewport.cs b/Fushigi/ui/widgets/LevelViewport.cs index 4e23a37c..0c6fcc3d 100644 --- a/Fushigi/ui/widgets/LevelViewport.cs +++ b/Fushigi/ui/widgets/LevelViewport.cs @@ -929,7 +929,7 @@ Vector2[] GetPoints() } bool isSelected = mEditContext.IsSelected(rail); - bool hovered = LevelViewport.HitTestLineLoopPoint(GetPoints(), 10f, ImGui.GetMousePos()); + bool hovered = MathUtil.HitTestLineLoopPoint(GetPoints(), 10f, ImGui.GetMousePos()); CourseRail.CourseRailPoint selectedPoint = null; @@ -1172,11 +1172,11 @@ Vector2[] GetPoints() string name = actor.mPackName; - isHovered = HitTestConvexPolygonPoint(s_actorRectPolygon, ImGui.GetMousePos()); + isHovered = MathUtil.HitTestConvexPolygonPoint(s_actorRectPolygon, ImGui.GetMousePos()); if (name.Contains("Area")) { - isHovered = HitTestLineLoopPoint(s_actorRectPolygon, 4f, + isHovered = MathUtil.HitTestLineLoopPoint(s_actorRectPolygon, 4f, ImGui.GetMousePos()); } @@ -1189,61 +1189,6 @@ Vector2[] GetPoints() mHoveredObject = newHoveredObject; } - - /// - /// Does a collision check between a convex polygon and a point - /// - /// Points of Polygon a in Clockwise orientation (in screen coordinates) - /// Point - /// - public static bool HitTestConvexPolygonPoint(ReadOnlySpan polygon, Vector2 point) - { - // separating axis theorem (lite) - // we can view the point as a polygon with 0 sides and 1 point - for (int i = 0; i < polygon.Length; i++) - { - var p1 = polygon[i]; - var p2 = polygon[(i + 1) % polygon.Length]; - var vec = (p2 - p1); - var normal = new Vector2(vec.Y, -vec.X); - - (Vector2 origin, Vector2 normal) edge = (p1, normal); - - if (Vector2.Dot(point - edge.origin, edge.normal) >= 0) - return false; - } - - //no separating axis found -> collision - return true; - } - - /// - /// Does a collision check between a LineLoop and a point - /// - /// Points of a LineLoop - /// Point - /// - public static bool HitTestLineLoopPoint(ReadOnlySpan points, float thickness, Vector2 point) - { - for (int i = 0; i < points.Length; i++) - { - var p1 = points[i]; - var p2 = points[(i + 1) % points.Length]; - if (HitTestPointLine(point, - p1, p2, thickness)) - return true; - } - - return false; - } - - static bool HitTestPointLine(Vector2 p, Vector2 a, Vector2 b, float thickness) - { - Vector2 pa = p - a, ba = b - a; - float h = Math.Clamp(Vector2.Dot(pa, ba) / - Vector2.Dot(ba, ba), 0, 1); - return (pa - ba * h).Length() < thickness / 2; - } } static class ColorExtensions diff --git a/Fushigi/util/MathUtil.cs b/Fushigi/util/MathUtil.cs index 8f5f4561..93bb9151 100644 --- a/Fushigi/util/MathUtil.cs +++ b/Fushigi/util/MathUtil.cs @@ -60,6 +60,66 @@ static float isLeft(Vector2 p0, Vector2 p1, Vector2 point) => } return wn; } + + /// + /// Does a collision check between a convex polygon and a point + /// + /// Points of Polygon a in Clockwise orientation (in screen coordinates) + /// Point + /// + public static bool HitTestConvexPolygonPoint(ReadOnlySpan polygon, Vector2 point) + { + // separating axis theorem (lite) + // we can view the point as a polygon with 0 sides and 1 point + for (int i = 0; i < polygon.Length; i++) + { + var p1 = polygon[i]; + var p2 = polygon[(i + 1) % polygon.Length]; + var vec = (p2 - p1); + var normal = new Vector2(vec.Y, -vec.X); + + (Vector2 origin, Vector2 normal) edge = (p1, normal); + + if (Vector2.Dot(point - edge.origin, edge.normal) >= 0) + return false; + } + + //no separating axis found -> collision + return true; + } + + public static bool HitTestConvexQuad(Vector2 p1, Vector2 p2, Vector2 p3, Vector2 p4, Vector2 point) + { + return HitTestConvexPolygonPoint([p1, p2, p3, p4], point); + } + + /// + /// Does a collision check between a LineLoop and a point + /// + /// Points of a LineLoop + /// Point + /// + public static bool HitTestLineLoopPoint(ReadOnlySpan points, float thickness, Vector2 point) + { + for (int i = 0; i < points.Length; i++) + { + var p1 = points[i]; + var p2 = points[(i + 1) % points.Length]; + if (HitTestPointLine(point, + p1, p2, thickness)) + return true; + } + + return false; + } + + static bool HitTestPointLine(Vector2 p, Vector2 a, Vector2 b, float thickness) + { + Vector2 pa = p - a, ba = b - a; + float h = Math.Clamp(Vector2.Dot(pa, ba) / + Vector2.Dot(ba, ba), 0, 1); + return (pa - ba * h).Length() < thickness / 2; + } } struct BoundingBox2D(Vector2 min, Vector2 max) From d7f4181be6d1494bdf963770d323a52f4b9cfd90 Mon Sep 17 00:00:00 2001 From: jupahe64 Date: Thu, 4 Jan 2024 15:12:02 +0100 Subject: [PATCH 04/19] properly update newly created Tile Units --- Fushigi/gl/Bfres/TileBfresRenderer.cs | 2 +- .../SceneObjects/bgunit/BGUnitRailSceneObj.cs | 4 --- Fushigi/ui/widgets/LevelViewport.cs | 32 +++++++++++++------ 3 files changed, 23 insertions(+), 15 deletions(-) diff --git a/Fushigi/gl/Bfres/TileBfresRenderer.cs b/Fushigi/gl/Bfres/TileBfresRenderer.cs index 46c0fa19..1034b351 100644 --- a/Fushigi/gl/Bfres/TileBfresRenderer.cs +++ b/Fushigi/gl/Bfres/TileBfresRenderer.cs @@ -77,7 +77,7 @@ private static Model CreateModel(GL gl, BgUnitInfo bgUnitInfo) )); } - public void Load(CourseUnitHolder unitHolder, Camera camera) + public void Load(CourseUnitHolder unitHolder) { SolidModel.TileManager.Clear(); SemisolidModel.TileManager.Clear(); diff --git a/Fushigi/ui/SceneObjects/bgunit/BGUnitRailSceneObj.cs b/Fushigi/ui/SceneObjects/bgunit/BGUnitRailSceneObj.cs index 19edfa29..a84673af 100644 --- a/Fushigi/ui/SceneObjects/bgunit/BGUnitRailSceneObj.cs +++ b/Fushigi/ui/SceneObjects/bgunit/BGUnitRailSceneObj.cs @@ -71,7 +71,6 @@ public void InsertPoint(CourseAreaEditContext ctx, BGUnitRail.RailPoint point, i ctx.CommitAction(revertible); ctx.Select(point); - CourseUnit.GenerateTileSubUnits(); } public void AddPoint(CourseAreaEditContext ctx, BGUnitRail.RailPoint point) @@ -81,7 +80,6 @@ public void AddPoint(CourseAreaEditContext ctx, BGUnitRail.RailPoint point) ctx.CommitAction(revertible); ctx.Select(point); - CourseUnit.GenerateTileSubUnits(); } public void RemoveSelected(CourseAreaEditContext ctx, LevelViewport viewport) @@ -99,8 +97,6 @@ public void RemoveSelected(CourseAreaEditContext ctx, LevelViewport viewport) } batchAction.Commit($"{IconUtil.ICON_TRASH} Delete Rail Points"); - - CourseUnit.GenerateTileSubUnits(); } public void OnKeyDown(CourseAreaEditContext ctx, LevelViewport viewport) diff --git a/Fushigi/ui/widgets/LevelViewport.cs b/Fushigi/ui/widgets/LevelViewport.cs index 0c6fcc3d..71a3b946 100644 --- a/Fushigi/ui/widgets/LevelViewport.cs +++ b/Fushigi/ui/widgets/LevelViewport.cs @@ -98,6 +98,8 @@ internal class LevelViewport(CourseArea area, GL gl, CourseAreaScene areaScene) public TileBfresRender TileBfresRenderFieldA; public TileBfresRender TileBfresRenderFieldB; public AreaResourceManager EnvironmentData = new AreaResourceManager(gl, area.mInitEnvPalette); + + private readonly HashSet mRegisteredUnits = []; DistantViewManager DistantViewScrollManager = new DistantViewManager(area); @@ -318,15 +320,7 @@ TileBfresRender CreateTileRendererForSkin(SkinDivision division, string skinName NoHit: table.GetPackName(skinName, "NoHit"), Bridge: table.GetPackName(skinName, "Bridge") ), division); - render.Load(this.mArea.mUnitHolder, this.Camera); - - foreach (var courseUnit in this.mArea.mUnitHolder.mUnits.Where(x => x.mSkinDivision == division)) - { - courseUnit.TilesUpdated += delegate - { - render.Load(this.mArea.mUnitHolder, this.Camera); - }; - } + render.Load(this.mArea.mUnitHolder); return render; } @@ -339,6 +333,24 @@ TileBfresRender CreateTileRendererForSkin(SkinDivision division, string skinName if (TileBfresRenderFieldB == null && !string.IsNullOrEmpty(fieldBSkin)) TileBfresRenderFieldB = CreateTileRendererForSkin(SkinDivision.FieldB, fieldBSkin); + //continuously register all course units that haven't been registered yet + foreach (var courseUnit in mArea.mUnitHolder.mUnits) + { + if (mRegisteredUnits.Contains(courseUnit)) + continue; + + if(courseUnit.mSkinDivision == SkinDivision.FieldA && TileBfresRenderFieldA is not null) + { + courseUnit.TilesUpdated += () => TileBfresRenderFieldA.Load(this.mArea.mUnitHolder); + } + else if (courseUnit.mSkinDivision == SkinDivision.FieldB && TileBfresRenderFieldB is not null) + { + courseUnit.TilesUpdated += () => TileBfresRenderFieldB.Load(this.mArea.mUnitHolder); + } + + mRegisteredUnits.Add(courseUnit); + } + TileBfresRenderFieldA?.Render(gl, this.Camera); TileBfresRenderFieldB?.Render(gl, this.Camera); @@ -894,7 +906,7 @@ void DrawGridLines(bool is_vertical, } private static Vector2[] s_actorRectPolygon = new Vector2[4]; - + void DrawAreaContent() { const float pointSize = 3.0f; From 57a0f51acf4e80f8a98a75723d1e9664fe6249b0 Mon Sep 17 00:00:00 2001 From: jupahe64 Date: Thu, 4 Jan 2024 22:26:07 +0100 Subject: [PATCH 05/19] small bugfix --- Fushigi/ui/widgets/OperationWarningDialog.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Fushigi/ui/widgets/OperationWarningDialog.cs b/Fushigi/ui/widgets/OperationWarningDialog.cs index f52820ef..9940fad8 100644 --- a/Fushigi/ui/widgets/OperationWarningDialog.cs +++ b/Fushigi/ui/widgets/OperationWarningDialog.cs @@ -19,7 +19,7 @@ public static async Task ShowDialog(IPopupModalHost modalHost, { var result = await modalHost.ShowPopUp( new OperationWarningDialog(warning, categorizedWarnings), - title, ImGuiWindowFlags.AlwaysAutoResize); + title, ImGuiWindowFlags.AlwaysAutoResize | ImGuiWindowFlags.NoBringToFrontOnFocus); if (result.wasClosed) return DialogResult.Cancel; From a69e3a629d77ad48aa0bcba656d0aa662e37c690 Mon Sep 17 00:00:00 2001 From: jupahe64 Date: Fri, 5 Jan 2024 22:29:22 +0100 Subject: [PATCH 06/19] fix the bugfix --- Fushigi/ui/widgets/OperationWarningDialog.cs | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/Fushigi/ui/widgets/OperationWarningDialog.cs b/Fushigi/ui/widgets/OperationWarningDialog.cs index 9940fad8..a48f31de 100644 --- a/Fushigi/ui/widgets/OperationWarningDialog.cs +++ b/Fushigi/ui/widgets/OperationWarningDialog.cs @@ -19,7 +19,7 @@ public static async Task ShowDialog(IPopupModalHost modalHost, { var result = await modalHost.ShowPopUp( new OperationWarningDialog(warning, categorizedWarnings), - title, ImGuiWindowFlags.AlwaysAutoResize | ImGuiWindowFlags.NoBringToFrontOnFocus); + title, ImGuiWindowFlags.AlwaysAutoResize); if (result.wasClosed) return DialogResult.Cancel; @@ -43,12 +43,8 @@ public void DrawModalContent(Promise promise) float width = ImGui.GetContentRegionAvail().X; ImGui.SetNextWindowSizeConstraints(new Vector2(width, 0), new Vector2(width, ImGui.GetWindowViewport().WorkSize.Y / 3f)); - ImGui.SetNextWindowPos(ImGui.GetCursorScreenPos()); - ImGui.PushStyleVar(ImGuiStyleVar.WindowBorderSize, 0); - ImGui.PushStyleVar(ImGuiStyleVar.WindowPadding, new Vector2(0)); var cursorPos = ImGui.GetCursorPos(); - if (ImGui.Begin("CategorizedWarnings", - ImGuiWindowFlags.AlwaysAutoResize | ImGuiWindowFlags.NoTitleBar | ImGuiWindowFlags.NoNavFocus)) + if(ImGui.BeginChild("Categorized", Vector2.Zero, ImGuiChildFlags.AutoResizeY)) { var headerStickPosY = 0f; foreach (var (category, warnings) in mCategorizedWarnings) @@ -72,7 +68,7 @@ public void DrawModalContent(Promise promise) if(!expanded) continue; - ImGui.PushClipRect(ImGui.GetCursorScreenPos(), + ImGui.PushClipRect(ImGui.GetCursorScreenPos(), new Vector2(float.PositiveInfinity), true); headerStickPosY = ImGui.GetCursorPosY() - ImGui.GetScrollY(); @@ -87,13 +83,9 @@ public void DrawModalContent(Promise promise) } ImGui.Unindent(); ImGui.PopClipRect(); - ImGui.SetWindowSize(new Vector2(100, 100)); } - cursorPos += ImGui.GetWindowSize() with { X = 0 }; - ImGui.End(); + ImGui.EndChild(); } - ImGui.PopStyleVar(2); - ImGui.SetCursorPos(cursorPos); #endregion From 7e59134cf7e38995fcdd2e070088fe85bf06871e Mon Sep 17 00:00:00 2001 From: Donavin Draws <51259260+DonavinDraws@users.noreply.github.com> Date: Sun, 7 Jan 2024 19:35:04 -0600 Subject: [PATCH 07/19] Fixes, MiniMap, & Wonder View Rendering & Link Fixes, MiniMap Color Tweaks Wonder View Modes Added --- Fushigi/course/CourseActor.cs | 9 +++- Fushigi/env/EnvPalette.cs | 2 +- Fushigi/gl/Bfres/BfresRender.cs | 3 +- Fushigi/gl/Bfres/TileBfresRenderer.cs | 2 +- Fushigi/ui/CourseAreaEditContext.cs | 10 ++-- Fushigi/ui/widgets/CourseScene.cs | 69 ++++++++++++++++++++------- Fushigi/ui/widgets/LevelViewport.cs | 45 ++++++++++------- 7 files changed, 98 insertions(+), 42 deletions(-) diff --git a/Fushigi/course/CourseActor.cs b/Fushigi/course/CourseActor.cs index 69779402..9d55bffd 100644 --- a/Fushigi/course/CourseActor.cs +++ b/Fushigi/course/CourseActor.cs @@ -13,6 +13,11 @@ namespace Fushigi.course { + public enum WonderViewType{ + Normal, + WonderOff, + WonderOnly + } public class CourseActor { public CourseActor(BymlHashTable actorNode) @@ -222,6 +227,8 @@ public BymlHashTable BuildNode(CourseLinkHolder linkHolder) public string mPackName; public string mName; public string mLayer; + public WonderViewType mWonderView = WonderViewType.Normal; + public bool wonderVisible = true; public System.Numerics.Vector3 mStartingTrans; public System.Numerics.Vector3 mTranslation; public System.Numerics.Vector3 mRotation; @@ -260,7 +267,7 @@ public CourseActor this[ulong hash] get { bool exists = TryGetActor(hash, out CourseActor? actor); - Debug.Assert(exists); + //Debug.Assert(exists); return actor!; } } diff --git a/Fushigi/env/EnvPalette.cs b/Fushigi/env/EnvPalette.cs index 00c6b1e8..3e4d43c7 100644 --- a/Fushigi/env/EnvPalette.cs +++ b/Fushigi/env/EnvPalette.cs @@ -58,7 +58,7 @@ public void Load(string name) string file_path = FileUtil.FindContentPath(local_path); if (!File.Exists(file_path)) { - Debug.Fail(null); + //Debug.Fail(null); return; } diff --git a/Fushigi/gl/Bfres/BfresRender.cs b/Fushigi/gl/Bfres/BfresRender.cs index cc34949a..dc5c5d92 100644 --- a/Fushigi/gl/Bfres/BfresRender.cs +++ b/Fushigi/gl/Bfres/BfresRender.cs @@ -93,7 +93,8 @@ internal void Render(GL gl, BfresRender render, Matrix4x4 transform, Camera came { foreach (var mesh in Meshes) { - mesh.LodMeshes[0].BoundingBox.Transform(transform); + var m = mesh.LodMeshes[0].BoundingBox; + m.Transform(transform); BoundingBox.Include(mesh.LodMeshes[0].BoundingBox); } diff --git a/Fushigi/gl/Bfres/TileBfresRenderer.cs b/Fushigi/gl/Bfres/TileBfresRenderer.cs index 46c0fa19..3f45ddf0 100644 --- a/Fushigi/gl/Bfres/TileBfresRenderer.cs +++ b/Fushigi/gl/Bfres/TileBfresRenderer.cs @@ -153,9 +153,9 @@ public void Load(CourseUnitHolder unitHolder, Camera camera) public void Render(GL gl, Camera camera) { + NoCollisionModel.Render(gl, camera); SolidModel.Render(gl, camera); SemisolidModel.Render(gl, camera); - NoCollisionModel.Render(gl, camera); BridgeModel.Render(gl, camera); } diff --git a/Fushigi/ui/CourseAreaEditContext.cs b/Fushigi/ui/CourseAreaEditContext.cs index 18669421..b1f73d9c 100644 --- a/Fushigi/ui/CourseAreaEditContext.cs +++ b/Fushigi/ui/CourseAreaEditContext.cs @@ -68,15 +68,17 @@ public void AddLink(CourseLink link) LogAdding($": {link.mSource} -{link.mLinkName}-> {link.mDest}"); //Checks if the the source actor already has links - if(linkList.Any(x => x.mSource == link.mSource)){ + if (linkList.Any(x => x.mSource == link.mSource)){ //Looks through the source actor's links //Then looks through it's links of the same type (If it has any) //Placing the new link in the right spot + var index = linkList.FindLastIndex(x => x.mSource == link.mSource && + (!linkList.Any(y => x.mLinkName == link.mLinkName) || + x.mLinkName == link.mLinkName)); + CommitAction( - area.mLinkHolder.mLinks.RevertableInsert(link, - linkList.FindLastIndex(x => x.mSource == link.mSource - && ((!linkList.Any(y => y.mLinkName == link.mLinkName)) || x.mLinkName == link.mLinkName))+1, + area.mLinkHolder.mLinks.RevertableInsert(link, index+1, $"{IconUtil.ICON_PLUS_CIRCLE} Add {link.mLinkName} Link") ); return; diff --git a/Fushigi/ui/widgets/CourseScene.cs b/Fushigi/ui/widgets/CourseScene.cs index aff30fb8..23c378b4 100644 --- a/Fushigi/ui/widgets/CourseScene.cs +++ b/Fushigi/ui/widgets/CourseScene.cs @@ -48,6 +48,11 @@ class CourseScene CourseLink? mSelectedGlobalLink = null; + string[] viewMode = [ + "View All Actors", + "View Normal Actors", + "View Wonder Actors"]; + string[] linkTypes = [ "BasicSignal", "Create", @@ -242,8 +247,6 @@ public void DrawUI(GL gl, double deltaSeconds) ImGui.SameLine(); - ImGui.SameLine(); - string current_palette = area.mInitEnvPalette == null ? "" : area.mInitEnvPalette.Name; void SelectPalette(string name, string palette) @@ -262,8 +265,8 @@ void SelectPalette(string name, string palette) ImGui.SetItemDefaultFocus(); } - ImGui.PushItemWidth(30); - if (ImGui.BeginCombo($"##EnvPalette", $"{IconUtil.ICON_PALETTE}", ImGuiComboFlags.NoArrowButton)) + var flags = ImGuiComboFlags.NoArrowButton | ImGuiComboFlags.WidthFitPreview; + if (ImGui.BeginCombo($"##EnvPalette", $"{IconUtil.ICON_PALETTE}", flags)) { SelectPalette($"Default Palette", area.mAreaParams.EnvPaletteSetting.InitPaletteBaseName); @@ -284,7 +287,6 @@ void SelectPalette(string name, string palette) } ImGui.EndCombo(); } - ImGui.PopItemWidth(); ImGui.SameLine(); @@ -294,6 +296,18 @@ void SelectPalette(string name, string palette) UserSettings.SetGameShaders(useGameShaders); } + ImGui.SameLine(); + + if (ImGui.BeginCombo("Wonder View", viewMode[(int)activeViewport.WonderViewMode], ImGuiComboFlags.WidthFitPreview)) + { + for (int n = 0; n < 3; n++) + { + if (ImGui.Selectable(viewMode[n])) + viewport.WonderViewMode = (WonderViewType)n; + } + ImGui.EndCombo(); + } + ImGui.PopStyleColor(1); ImGui.EndChild(); @@ -498,8 +512,6 @@ private void LocalLinksPanel() { ImGui.Begin("Local Links"); - ImGui.Checkbox("Wonder View", ref activeViewport.IsWonderView); - ImGui.Separator(); AreaLocalLinksView(selectedArea); @@ -1253,7 +1265,7 @@ BGUnitRailSceneObj GetRailSceneObj(object courseObject) if (ImGui.Selectable(name, editContext.IsSelected(unit))) { - editContext.DeselectAllOfType(); + editContext.DeselectAll(); editContext.Select(unit); } if (expanded) @@ -1458,7 +1470,8 @@ private void AreaLocalLinksView(CourseArea area) ImGui.EndChild(); } - private void RecursiveLinkFind(CourseArea area, CourseLinkHolder links, CourseAreaEditContext editContext, float em, IEnumerable linkList) + private void RecursiveLinkFind(CourseArea area, CourseLinkHolder links, + CourseAreaEditContext editContext, float em, IEnumerable linkList) { foreach (CourseActor actor in linkList) { @@ -1486,6 +1499,8 @@ private void RecursiveLinkFind(CourseArea area, CourseLinkHolder links, CourseAr if (!isVisible) ImGui.BeginDisabled(); + UpdateWonderVisibility(actor, links, area); + if (expanded) { foreach (var link in links.GetDestHashesFromSrc(actor.mHash)) @@ -1495,10 +1510,6 @@ private void RecursiveLinkFind(CourseArea area, CourseLinkHolder links, CourseAr { var reLinks = area.GetActors().Where(x => link.Value.Contains(x.mHash)); RecursiveLinkFind(area, links, editContext, em, reLinks); - // foreach (CourseActor linkActor in ) - // { - // - // } ImGui.TreePop(); } ImGui.PopID(); @@ -1555,6 +1566,34 @@ private void RecursiveLinkFind(CourseArea area, CourseLinkHolder links, CourseAr } } + private void UpdateWonderVisibility(CourseActor actor, CourseLinkHolder links, CourseArea area) + { + foreach (var link in links.GetDestHashesFromSrc(actor.mHash)) + { + var reLinks = area.GetActors().Where(x => link.Value.Contains(x.mHash)); + if (!link.Key.Contains("CreateRelative") && + (link.Key.Contains("Create") || + link.Key.Contains("PopUp") || + link.Key.Contains("Delete") || + link.Key.Contains("BasicSignal"))) + { + foreach (CourseActor linkActor in reLinks) + { + if ((actor.mPackName == "ObjectWonderTag" || actor.mWonderView == WonderViewType.WonderOnly) && + (!link.Key.Contains("BasicSignal") || (linkActor.mActorPack?.Category.Contains("Tag") ?? false))) + { + if (link.Key.Contains("Delete")) + linkActor.mWonderView = WonderViewType.WonderOff; + else + linkActor.mWonderView = WonderViewType.WonderOnly; + } + else + linkActor.mWonderView = WonderViewType.Normal; + } + } + } + } + private void UpdateAllLayerVisiblity() { foreach (string layer in mLayersVisibility.Keys) @@ -1835,7 +1874,7 @@ private void CourseMiniView() dl.AddRectFilled( MapPointPixelAligned(pos), MapPointPixelAligned(pos + Vector2.One), - 0xFF444444); + 0xFF666688); } } @@ -1846,7 +1885,6 @@ private void CourseMiniView() .SelectMany(x => x.mTileSubUnits) .OrderBy(x => x.mOrigin.Z); - var t = 0; foreach (var subUnit in foregroundSubUnits) { var type = foregroundTileUnits.First(x => x.mTileSubUnits.Contains(subUnit)).mModelType; @@ -1892,7 +1930,6 @@ private void CourseMiniView() } } - dl.AddRect(lvlRectTopLeft, lvlRectTopLeft + lvlRectSize, ImGui.ColorConvertFloat4ToU32(ImGui.GetStyle().Colors[(int)ImGuiCol.Text]),6,0,3); diff --git a/Fushigi/ui/widgets/LevelViewport.cs b/Fushigi/ui/widgets/LevelViewport.cs index 4e23a37c..b61fb2e5 100644 --- a/Fushigi/ui/widgets/LevelViewport.cs +++ b/Fushigi/ui/widgets/LevelViewport.cs @@ -36,7 +36,7 @@ public static void DefaultSelect(CourseAreaEditContext ctx, object selectable) { ctx.Select(selectable); } - else if(!ctx.IsSelected(selectable)) + else if(!ctx.IsSelected(selectable)) { ctx.WithSuspendUpdateDo(() => { @@ -82,13 +82,14 @@ internal class LevelViewport(CourseArea area, GL gl, CourseAreaScene areaScene) public bool IsViewportHovered; public bool IsViewportActive; - public bool IsWonderView; + public WonderViewType WonderViewMode = WonderViewType.Normal; public bool PlayAnimations = false; public bool ShowGrid = true; Vector2 mSize = Vector2.Zero; public ulong prevSelectVersion { get; private set; } = 0; + public bool dragRelease; private IDictionary? mLayersVisibility; Vector2 mTopLeft = Vector2.Zero; @@ -347,9 +348,14 @@ TileBfresRender CreateTileRendererForSkin(SkinDivision division, string skinName foreach (var actor in this.mArea.GetActors()) { - if (actor.mActorPack == null || mLayersVisibility.ContainsKey(actor.mLayer) && !mLayersVisibility[actor.mLayer]) - continue; + actor.wonderVisible = WonderViewMode == actor.mWonderView || + WonderViewMode == WonderViewType.Normal || + actor.mWonderView == WonderViewType.Normal; + if (actor.mActorPack == null || (mLayersVisibility.ContainsKey(actor.mLayer) && !mLayersVisibility[actor.mLayer]) || + !actor.wonderVisible) + continue; + RenderActor(actor, actor.mActorPack.ModelInfoRef); RenderActor(actor, actor.mActorPack.DrawArrayModelInfoRef); } @@ -796,22 +802,25 @@ void InteractionWithFocus(KeyboardModifier modifiers) } } - if(mHoveredObject != null && mHoveredObject is CourseActor && - ImGui.IsMouseReleased(ImGuiMouseButton.Left)) + if (ImGui.IsMouseDown(0)) + dragRelease = ImGui.IsMouseDragging(0); + + if(mHoveredObject != null && + mHoveredObject is CourseActor && + !dragRelease && + ImGui.IsMouseReleased(0)) { - if (ImGui.GetIO().MouseDragMaxDistanceSqr[0] <= ImGui.GetIO().MouseDragThreshold) + if(ImGui.IsKeyDown(ImGuiKey.LeftShift) && + prevSelectVersion == mEditContext.SelectionVersion) { - if(ImGui.IsKeyDown(ImGuiKey.LeftShift) - && prevSelectVersion == mEditContext.SelectionVersion) - { - mEditContext.Deselect(mHoveredObject!); - } - else if(!ImGui.IsKeyDown(ImGuiKey.LeftShift)) - { - mEditContext.DeselectAll(); - IViewportSelectable.DefaultSelect(mEditContext, mHoveredObject); - } + mEditContext.Deselect(mHoveredObject!); + } + else if(!ImGui.IsKeyDown(ImGuiKey.LeftShift)) + { + mEditContext.DeselectAll(); + IViewportSelectable.DefaultSelect(mEditContext, mHoveredObject); } + dragRelease = false; } if (ImGui.IsKeyPressed(ImGuiKey.Delete)) @@ -1095,7 +1104,7 @@ Vector2[] GetPoints() string layer = actor.mLayer; - if (mLayersVisibility!.TryGetValue(layer, out bool isVisible) && isVisible) + if (mLayersVisibility!.TryGetValue(layer, out bool isVisible) && isVisible && actor.wonderVisible) { Matrix4x4 transform = Matrix4x4.CreateScale(actor.mScale.X, actor.mScale.Y, actor.mScale.Z From b6f3678e636df9c8bc3e3cceb4846fc66a06a492 Mon Sep 17 00:00:00 2001 From: Donavin Draws <51259260+DonavinDraws@users.noreply.github.com> Date: Fri, 12 Jan 2024 12:05:21 -0600 Subject: [PATCH 08/19] Old Code Revamp, Curved Rails, & ActorToRails Fix Legacy ImGui Column code replaced with the newer API's ImGui Table code. Curved Rails can be created and their curve control point can be moved in the viewport when a point is selected. Fixed how ActorToRail Links are saved, as the previous method caused bugs with objects that moved along them automatically. --- Fushigi/actor_pack/ActorPack.cs | 5 +- Fushigi/course/CourseRail.cs | 31 +- Fushigi/ui/widgets/CourseScene.cs | 1101 ++++++++++++++------------- Fushigi/ui/widgets/LevelViewport.cs | 77 +- Fushigi/ui/widgets/PathSelector.cs | 73 +- 5 files changed, 703 insertions(+), 584 deletions(-) diff --git a/Fushigi/actor_pack/ActorPack.cs b/Fushigi/actor_pack/ActorPack.cs index 46ec92ff..c1a4a1b6 100644 --- a/Fushigi/actor_pack/ActorPack.cs +++ b/Fushigi/actor_pack/ActorPack.cs @@ -206,10 +206,9 @@ private void LoadComponents(SARC.SARC sarc, ActorParam param) } } - private ShapeParamList GetActorShape(SARC.SARC sarc) + private ShapeParamList? GetActorShape(SARC.SARC sarc) { - - var file = GetPathGyml(GamePhysicsRef.mPath); + var file = GetPathGyml(this.GamePhysicsRef.mPath); var dat = sarc.OpenFile(file); this.ControllerPath = BymlSerialize.Deserialize(dat); diff --git a/Fushigi/course/CourseRail.cs b/Fushigi/course/CourseRail.cs index 84697f11..b988cfe3 100644 --- a/Fushigi/course/CourseRail.cs +++ b/Fushigi/course/CourseRail.cs @@ -139,7 +139,7 @@ public CourseRailPoint(CourseRailPoint point) { this.mHash = RandomUtil.GetRandom(); this.mTranslate = point.mTranslate; - this.mControl = point.mControl; + this.mControl = new(this, point.mControl.mTranslate); foreach (var param in point.mParameters) this.mParameters.Add(param.Key, param.Value); } @@ -148,6 +148,7 @@ public CourseRailPoint(BymlHashTable node, string pointParam) { mHash = BymlUtil.GetNodeData(node["Hash"]); mTranslate = BymlUtil.GetVector3FromArray(node["Translate"] as BymlArrayNode); + mControl = new(this); IDictionary comp; if (ParamDB.TryGetRailPointComponent(pointParam, out var componentName)) @@ -169,7 +170,8 @@ public CourseRailPoint(BymlHashTable node, string pointParam) if (node.ContainsKey("Control1")) { - mControl = BymlUtil.GetVector3FromArray(node["Control1"] as BymlArrayNode); + mControl.mTranslate = BymlUtil.GetVector3FromArray(node["Control1"] as BymlArrayNode); + mIsCurve = true; } var dynamicNode = node["Dynamic"] as BymlHashTable; @@ -204,12 +206,12 @@ public BymlHashTable BuildNode() tbl.AddNode(BymlNodeId.Hash, dynamicNode, "Dynamic"); - if (mControl != null) + if (mIsCurve) { BymlArrayNode controlNode = new(3); - controlNode.AddNodeToArray(BymlUtil.CreateNode("X", mControl.Value.X)); - controlNode.AddNodeToArray(BymlUtil.CreateNode("Y", mControl.Value.Y)); - controlNode.AddNodeToArray(BymlUtil.CreateNode("Z", mControl.Value.Z)); + controlNode.AddNodeToArray(BymlUtil.CreateNode("X", mControl.mTranslate.X)); + controlNode.AddNodeToArray(BymlUtil.CreateNode("Y", mControl.mTranslate.Y)); + controlNode.AddNodeToArray(BymlUtil.CreateNode("Z", mControl.mTranslate.Z)); tbl.AddNode(BymlNodeId.Array, controlNode, "Control1"); } @@ -227,10 +229,20 @@ public BymlHashTable BuildNode() public ulong mHash; public Dictionary mParameters = new(); public System.Numerics.Vector3 mTranslate; - public System.Numerics.Vector3? mControl = null; + public CourseRailPointControl mControl; + public bool mIsCurve; + } + public class CourseRailPointControl + { + public CourseRailPointControl(CourseRail.CourseRailPoint point, System.Numerics.Vector3 pos = default) + { + this.point = point; + this.mTranslate = pos; + } + public CourseRail.CourseRailPoint point; + public System.Numerics.Vector3 mTranslate; } } - public class CourseRailHolder { public CourseRailHolder() @@ -291,6 +303,7 @@ public CourseActorToRailLink(BymlHashTable table) { mSourceActor = BymlUtil.GetNodeData(table["Src"]); mDestRail = BymlUtil.GetNodeData(table["Dst"]); + mDestPoint = BymlUtil.GetNodeData(table["Point"]); mLinkName = BymlUtil.GetNodeData(table["Name"]); } @@ -299,7 +312,7 @@ public BymlHashTable BuildNode() BymlHashTable tbl = new(); tbl.AddNode(BymlNodeId.UInt64, BymlUtil.CreateNode(mDestRail), "Dst"); tbl.AddNode(BymlNodeId.String, BymlUtil.CreateNode(mLinkName), "Name"); - tbl.AddNode(BymlNodeId.UInt64, BymlUtil.CreateNode(mSourceActor), "Point"); + tbl.AddNode(BymlNodeId.UInt64, BymlUtil.CreateNode(mDestPoint), "Point"); tbl.AddNode(BymlNodeId.UInt64, BymlUtil.CreateNode(mSourceActor), "Src"); return tbl; } diff --git a/Fushigi/ui/widgets/CourseScene.cs b/Fushigi/ui/widgets/CourseScene.cs index 59c702eb..a1a00fa3 100644 --- a/Fushigi/ui/widgets/CourseScene.cs +++ b/Fushigi/ui/widgets/CourseScene.cs @@ -523,81 +523,81 @@ private void RailLinksPanel() { ImGui.Begin("Actor to Rail Links"); - ImGui.Columns(4); - ImGui.Text("Actor-Hash"); - ImGui.NextColumn(); - ImGui.Text("Rail"); - ImGui.NextColumn(); - ImGui.Text("Point"); - ImGui.NextColumn(); - ImGui.NextColumn(); - var ctx = areaScenes[selectedArea].EditContext; var rails = selectedArea.mRailHolder.mRails; var actors = selectedArea.mActorHolder.mActors; var railLinks = selectedArea.mRailLinksHolder.mLinks; - for (int i = 0; i < railLinks.Count; i++) + if (ImGui.BeginTable("actorRails", 4, ImGuiTableFlags.BordersInnerV | ImGuiTableFlags.Resizable)) { - ImGui.PushID(i); - CourseActorToRailLink link = railLinks[i]; - - string hash = link.mSourceActor.ToString(); - int actorIndex = actors.FindIndex(x => x.mHash == link.mSourceActor); - if (ImGui.InputText("##actor", ref hash, 100) && - ulong.TryParse(hash, out ulong hashInt)) - link.mSourceActor = hashInt; - if(actorIndex == -1) - { - ImGui.SameLine(); - ImGui.TextDisabled("Invalid"); - } + ImGui.TableSetupColumn("Actor-Hash"); + ImGui.TableSetupColumn("Rail"); + ImGui.TableSetupColumn("Point"); + ImGui.TableHeadersRow(); + + for (int i = 0; i < railLinks.Count; i++) + { + ImGui.TableNextRow(); + ImGui.TableSetColumnIndex(0); + ImGui.PushID(i); + CourseActorToRailLink link = railLinks[i]; + + string hash = link.mSourceActor.ToString(); + int actorIndex = actors.FindIndex(x => x.mHash == link.mSourceActor); + if (ImGui.InputText("##actor", ref hash, 100) && + ulong.TryParse(hash, out ulong hashInt)) + link.mSourceActor = hashInt; + if(actorIndex == -1) + { + ImGui.SameLine(); + ImGui.TextDisabled("Invalid"); + } - ImGui.NextColumn(); - int railIndex = rails.FindIndex(x => x.mHash == link.mDestRail); - if (ImGui.BeginCombo("##rail", railIndex >= 0 ? ("rail " + railIndex) : "None")) - { - for (int iRail = 0; iRail < rails.Count; iRail++) + ImGui.TableNextColumn(); + int railIndex = rails.FindIndex(x => x.mHash == link.mDestRail); + if (ImGui.BeginCombo("##rail", railIndex >= 0 ? ("rail " + railIndex) : "None")) { - if (ImGui.Selectable("Rail " + iRail, railIndex == iRail)) - link.mDestRail = rails[iRail].mHash; + for (int iRail = 0; iRail < rails.Count; iRail++) + { + if (ImGui.Selectable("Rail " + iRail, railIndex == iRail)) + link.mDestRail = rails[iRail].mHash; + } + ImGui.EndCombo(); } - ImGui.EndCombo(); - } - if (railIndex == -1) - { - ImGui.SameLine(); - ImGui.TextDisabled("Invalid"); - } - ImGui.NextColumn(); - if (railIndex >= 0) - { - int pointIndex = rails[railIndex].mPoints.FindIndex(x => x.mHash == link.mDestPoint); + if (railIndex == -1) + { + ImGui.SameLine(); + ImGui.TextDisabled("Invalid"); + } + ImGui.TableNextColumn(); + if (railIndex >= 0) + { + int pointIndex = rails[railIndex].mPoints.FindIndex(x => x.mHash == link.mDestPoint); - if (pointIndex == -1) - pointIndex = 0; + if (pointIndex == -1) + pointIndex = 0; - if (ImGui.InputInt("##railpoint", ref pointIndex)) - pointIndex = Math.Clamp(pointIndex, 0, rails[railIndex].mPoints.Count - 1); + if (ImGui.InputInt("##railpoint", ref pointIndex)) + pointIndex = Math.Clamp(pointIndex, 0, rails[railIndex].mPoints.Count - 1); - link.mDestPoint = rails[railIndex].mPoints[pointIndex].mHash; - } + link.mDestPoint = rails[railIndex].mPoints[pointIndex].mHash; + } - ImGui.NextColumn(); - if (ImGui.Button("Delete", new Vector2(ImGui.GetContentRegionAvail().X * 0.65f, 0))) - { - ctx.DeleteRailLink(link); - i--; - } + ImGui.TableNextColumn(); + if (ImGui.Button("Delete", new Vector2(ImGui.GetContentRegionAvail().X * 0.65f, 0))) + { + ctx.DeleteRailLink(link); + i--; + } + ImGui.PopID(); + } - ImGui.NextColumn(); - ImGui.PopID(); + ImGui.EndTable(); } float width = ImGui.GetItemRectMax().X - ImGui.GetCursorScreenPos().X; - ImGui.Columns(1); ImGui.Dummy(new Vector2(0, ImGui.GetFrameHeight() * 0.5f)); if (ImGui.Button("Add", new Vector2(width, ImGui.GetFrameHeight() * 1.5f))) @@ -626,74 +626,77 @@ private void SelectionParameterPanel() string actorName = mSelectedActor.mPackName; string name = mSelectedActor.mName; - ImGui.Columns(2); - ImGui.AlignTextToFramePadding(); - string packName = mSelectedActor.mPackName; - - ImGui.Text("Actor Name"); - ImGui.NextColumn(); - ImGui.PushItemWidth(ImGui.GetColumnWidth() - ImGui.GetStyle().ScrollbarSize); - if (ImGui.InputText("##Actor Name", ref packName, 256, ImGuiInputTextFlags.EnterReturnsTrue)) + if (ImGui.BeginTable("Props", 2, ImGuiTableFlags.BordersInnerV | ImGuiTableFlags.Resizable)) { - if (ParamDB.GetActors().Contains(packName)) - { - mSelectedActor.mPackName = packName; - mSelectedActor.InitializeDefaultDynamicParams(); - } - } - ImGui.PopItemWidth(); - ImGui.NextColumn(); + ImGui.TableNextRow(); + ImGui.TableSetColumnIndex(0); + ImGui.AlignTextToFramePadding(); + string packName = mSelectedActor.mPackName; - ImGui.Text("Actor Hash"); - ImGui.NextColumn(); - string hash = mSelectedActor.mHash.ToString(); - ImGui.PushItemWidth(ImGui.GetColumnWidth() - ImGui.GetStyle().ScrollbarSize); - ImGui.InputText("##Actor Hash", ref hash, 256, ImGuiInputTextFlags.ReadOnly); - ImGui.PopItemWidth(); - ImGui.NextColumn(); - - ImGui.Separator(); - - ImGui.Columns(2); - - ImGui.AlignTextToFramePadding(); - ImGui.Text("Name"); + ImGui.Text("Actor Name"); + ImGui.TableNextColumn(); + ImGui.PushItemWidth(ImGui.GetColumnWidth() - ImGui.GetStyle().ScrollbarSize); + if (ImGui.InputText("##Actor Name", ref packName, 256, ImGuiInputTextFlags.EnterReturnsTrue)) + { + if (ParamDB.GetActors().Contains(packName)) + { + mSelectedActor.mPackName = packName; + mSelectedActor.InitializeDefaultDynamicParams(); + } + } + ImGui.PopItemWidth(); - ImGui.NextColumn(); - ImGui.PushItemWidth(ImGui.GetColumnWidth() - ImGui.GetStyle().ScrollbarSize); - if (ImGui.InputText($"##{name}", ref name, 512, ImGuiInputTextFlags.EnterReturnsTrue)) - { - mSelectedActor.mName = name; - } + ImGui.TableNextRow(); + ImGui.TableSetColumnIndex(0); + ImGui.Text("Actor Hash"); + ImGui.TableNextColumn(); + string hash = mSelectedActor.mHash.ToString(); + ImGui.PushItemWidth(ImGui.GetColumnWidth() - ImGui.GetStyle().ScrollbarSize); + ImGui.InputText("##Actor Hash", ref hash, 256, ImGuiInputTextFlags.ReadOnly); + ImGui.PopItemWidth(); - ImGui.PopItemWidth(); - ImGui.NextColumn(); + ImGui.TableNextRow(); + ImGui.TableSetColumnIndex(0); - ImGui.Text("Layer"); + ImGui.Separator(); - ImGui.NextColumn(); + ImGui.TableNextRow(); + ImGui.TableSetColumnIndex(0); + ImGui.AlignTextToFramePadding(); + ImGui.Text("Name"); - if (ImGui.BeginCombo("##Dropdown", mSelectedActor.mLayer)) - { - foreach (var layer in mLayersVisibility.Keys.ToArray().ToImmutableList()) - { - if (ImGui.Selectable(layer)) + ImGui.TableNextColumn(); + ImGui.PushItemWidth(ImGui.GetColumnWidth() - ImGui.GetStyle().ScrollbarSize); + if (ImGui.InputText($"##{name}", ref name, 512, ImGuiInputTextFlags.EnterReturnsTrue)) { - //item is selected - Console.WriteLine("Changing " + mSelectedActor.mName + "'s layer from " + mSelectedActor.mLayer + " to " + layer + "."); - mSelectedActor.mLayer = layer; + mSelectedActor.mName = name; } - } - - ImGui.EndCombo(); - } - + ImGui.PopItemWidth(); + + ImGui.TableNextRow(); + ImGui.TableSetColumnIndex(0); + ImGui.Text("Layer"); + ImGui.TableNextColumn(); + if (ImGui.BeginCombo("##Dropdown", mSelectedActor.mLayer)) + { + foreach (var layer in mLayersVisibility.Keys.ToArray().ToImmutableList()) + { + if (ImGui.Selectable(layer)) + { + //item is selected + Console.WriteLine("Changing " + mSelectedActor.mName + "'s layer from " + mSelectedActor.mLayer + " to " + layer + "."); + mSelectedActor.mLayer = layer; + } + } - ImGui.PopItemWidth(); + ImGui.EndCombo(); + } + ImGui.PopItemWidth(); - ImGui.Columns(1); + ImGui.EndTable(); + } PlacementNode(mSelectedActor); @@ -748,101 +751,102 @@ private void SelectionParameterPanel() { ImGui.Text(linkName); - ImGui.Columns(3); - - for (int i = 0; i < hashArray.Count; i++) + if (ImGui.BeginTable("##Links", 3, ImGuiTableFlags.BordersInnerV | ImGuiTableFlags.Resizable)) { - ImGui.PushID($"{hashArray[i].ToString()}_{i}"); - ImGui.SetCursorPosX(ImGui.GetCursorPosX() + ImGui.GetStyle().FramePadding.X); - ImGui.Text("Destination"); - ImGui.NextColumn(); + for (int i = 0; i < hashArray.Count; i++) + { + ImGui.TableNextRow(); + ImGui.TableSetColumnIndex(0); + ImGui.PushID($"{hashArray[i].ToString()}_{i}"); + ImGui.SetCursorPosX(ImGui.GetCursorPosX() + ImGui.GetStyle().FramePadding.X); + ImGui.Text("Destination"); + ImGui.TableNextColumn(); - CourseActor? destActor = selectedArea.mActorHolder[hashArray[i]]; + CourseActor? destActor = selectedArea.mActorHolder[hashArray[i]]; - if (destActor != null) - { - if (ImGui.Button(destActor.mName, new Vector2(ImGui.GetContentRegionAvail().X, 0))) + if (destActor != null) { - mSelectedActor = destActor; - activeViewport.SelectedActor(destActor); - activeViewport.Camera.Target.X = destActor.mTranslation.X; - activeViewport.Camera.Target.Y = destActor.mTranslation.Y; + if (ImGui.Button(destActor.mName, new Vector2(ImGui.GetContentRegionAvail().X, 0))) + { + mSelectedActor = destActor; + activeViewport.SelectedActor(destActor); + activeViewport.Camera.Target.X = destActor.mTranslation.X; + activeViewport.Camera.Target.Y = destActor.mTranslation.Y; + } } - } - else - { - if (ImGui.Button("Actor Not Found")) + else { + if (ImGui.Button("Actor Not Found")) + { + } } - } - ImGui.NextColumn(); + ImGui.TableNextColumn(); - var cursorSP = ImGui.GetCursorScreenPos(); - var padding = ImGui.GetStyle().FramePadding; + var cursorSP = ImGui.GetCursorScreenPos(); + var padding = ImGui.GetStyle().FramePadding; - uint WithAlphaFactor(uint color, float factor) => color & 0xFFFFFF | ((uint)((color >> 24) * factor) << 24); + uint WithAlphaFactor(uint color, float factor) => color & 0xFFFFFF | ((uint)((color >> 24) * factor) << 24); - float deleteButtonWidth = ImGui.GetFrameHeight() * 1.6f; + float deleteButtonWidth = ImGui.GetFrameHeight() * 1.6f; - float columnWidth = ImGui.GetContentRegionAvail().X; + float columnWidth = ImGui.GetContentRegionAvail().X; - ImGui.PushClipRect(cursorSP, - cursorSP + new Vector2(columnWidth - deleteButtonWidth, ImGui.GetFrameHeight()), true); + ImGui.PushClipRect(cursorSP, + cursorSP + new Vector2(columnWidth - deleteButtonWidth, ImGui.GetFrameHeight()), true); - var cursor = ImGui.GetCursorPos(); - ImGui.BeginDisabled(); - if (ImGui.Button("Replace")) - { + var cursor = ImGui.GetCursorPos(); + ImGui.BeginDisabled(); + if (ImGui.Button("Replace")) + { - } - ImGui.EndDisabled(); - cursor.X += ImGui.GetItemRectSize().X + 2; + } + ImGui.EndDisabled(); + cursor.X += ImGui.GetItemRectSize().X + 2; - ImGui.SetCursorPos(cursor); - if (ImGui.Button(IconUtil.ICON_EYE_DROPPER)) - { - ImGui.SetWindowFocus(selectedArea.GetName()); - Task.Run(async () => + ImGui.SetCursorPos(cursor); + if (ImGui.Button(IconUtil.ICON_EYE_DROPPER)) { - var (pickedDest, _) = await PickLinkDestInViewportFor(mSelectedActor); - if (pickedDest is null) - return; - - //TODO rework GetDestHashesFromSrc to return the actual link objects or do it in another way - var link = selectedArea.mLinkHolder.mLinks.Find( - x => x.mSource == mSelectedActor.mHash && - x.mLinkName == linkName && - x.mDest == destActor!.mHash); + ImGui.SetWindowFocus(selectedArea.GetName()); + Task.Run(async () => + { + var (pickedDest, _) = await PickLinkDestInViewportFor(mSelectedActor); + if (pickedDest is null) + return; - link.mDest = pickedDest.mHash; - }); - } + //TODO rework GetDestHashesFromSrc to return the actual link objects or do it in another way + var link = selectedArea.mLinkHolder.mLinks.Find( + x => x.mSource == mSelectedActor.mHash && + x.mLinkName == linkName && + x.mDest == destActor!.mHash); - ImGui.PopClipRect(); - cursorSP.X += columnWidth - deleteButtonWidth; - ImGui.SetCursorScreenPos(cursorSP); + link.mDest = pickedDest.mHash; + }); + } - bool clicked = ImGui.InvisibleButton("##Delete Link", new Vector2(deleteButtonWidth, ImGui.GetFrameHeight())); - string deleteIcon = IconUtil.ICON_TRASH_ALT; - ImGui.GetWindowDrawList().AddText(cursorSP + new Vector2((deleteButtonWidth - ImGui.CalcTextSize(deleteIcon).X) / 2, padding.Y), - WithAlphaFactor(ImGui.GetColorU32(ImGuiCol.Text), ImGui.IsItemHovered() ? 1 : 0.5f), - deleteIcon); + ImGui.PopClipRect(); + cursorSP.X += columnWidth - deleteButtonWidth; + ImGui.SetCursorScreenPos(cursorSP); - if (ImGui.IsItemHovered()) - ImGui.SetTooltip("Delete Link"); + bool clicked = ImGui.InvisibleButton("##Delete Link", new Vector2(deleteButtonWidth, ImGui.GetFrameHeight())); + string deleteIcon = IconUtil.ICON_TRASH_ALT; + ImGui.GetWindowDrawList().AddText(cursorSP + new Vector2((deleteButtonWidth - ImGui.CalcTextSize(deleteIcon).X) / 2, padding.Y), + WithAlphaFactor(ImGui.GetColorU32(ImGuiCol.Text), ImGui.IsItemHovered() ? 1 : 0.5f), + deleteIcon); - if (clicked) - editContext.DeleteLink(linkName, mSelectedActor.mHash, hashArray[i]); + if (ImGui.IsItemHovered()) + ImGui.SetTooltip("Delete Link"); - ImGui.NextColumn(); + if (clicked) + editContext.DeleteLink(linkName, mSelectedActor.mHash, hashArray[i]); - ImGui.PopID(); + ImGui.PopID(); + } + ImGui.EndTable(); } ImGui.Separator(); - } #endregion @@ -876,18 +880,24 @@ private void SelectionParameterPanel() if (ImGui.CollapsingHeader("Properties", ImGuiTreeNodeFlags.DefaultOpen)) { - ImGui.Columns(2); - ImGui.Text("Model Type"); ImGui.NextColumn(); + if (ImGui.BeginTable("Props", 2, ImGuiTableFlags.BordersInnerV | ImGuiTableFlags.Resizable)) + { + ImGui.TableNextRow(); + ImGui.TableSetColumnIndex(0); + ImGui.Text("Model Type"); ImGui.TableNextColumn(); - ImGui.Combo("##mModelType", ref Unsafe.As(ref mSelectedUnit.mModelType), - CourseUnit.ModelTypeNames, CourseUnit.ModelTypeNames.Length); - ImGui.NextColumn(); + ImGui.Combo("##mModelType", ref Unsafe.As(ref mSelectedUnit.mModelType), + CourseUnit.ModelTypeNames, CourseUnit.ModelTypeNames.Length); - ImGui.Text("Skin Division"); ImGui.NextColumn(); - ImGui.Combo("##SkinDivision", ref Unsafe.As(ref mSelectedUnit.mSkinDivision), - CourseUnit.SkinDivisionNames, CourseUnit.SkinDivisionNames.Length); + ImGui.TableNextRow(); + ImGui.TableSetColumnIndex(0); - ImGui.Columns(1); + ImGui.Text("Skin Division"); ImGui.TableNextColumn(); + ImGui.Combo("##SkinDivision", ref Unsafe.As(ref mSelectedUnit.mSkinDivision), + CourseUnit.SkinDivisionNames, CourseUnit.SkinDivisionNames.Length); + + ImGui.EndTable(); + } } if(mSelectedUnit.mModelType is CourseUnit.ModelType.SemiSolid) @@ -983,27 +993,30 @@ void ProcessRail(BGUnitRail rail) if (ImGui.CollapsingHeader("Properties", ImGuiTreeNodeFlags.DefaultOpen)) { - ImGui.Columns(2); - ImGui.Text("IsClosed"); ImGui.NextColumn(); - if (ImGui.Checkbox("##IsClosed", ref mSelectedUnitRail.IsClosed)) - mSelectedUnitRail.mCourseUnit.GenerateTileSubUnits(); - - ImGui.NextColumn(); - - //Depth editing for bg unit. All points share the same depth, so batch edit the Z point - float depth = mSelectedUnitRail.Points.Count == 0 ? 0 : mSelectedUnitRail.Points[0].Position.Z; - - ImGui.Text("Z Depth"); ImGui.NextColumn(); - if (ImGui.DragFloat("##Depth", ref depth, 0.1f)) + if (ImGui.BeginTable("Props", 2, ImGuiTableFlags.BordersInnerV | ImGuiTableFlags.Resizable)) { - //Update depth to all points - foreach (var p in mSelectedUnitRail.Points) - p.Position = new System.Numerics.Vector3(p.Position.X, p.Position.Y, depth); - mSelectedUnitRail.mCourseUnit.GenerateTileSubUnits(); + ImGui.TableNextRow(); + ImGui.TableSetColumnIndex(0); + ImGui.Text("IsClosed"); ImGui.TableNextColumn(); + if (ImGui.Checkbox("##IsClosed", ref mSelectedUnitRail.IsClosed)) + mSelectedUnitRail.mCourseUnit.GenerateTileSubUnits(); + + ImGui.TableNextRow(); + ImGui.TableSetColumnIndex(0); + //Depth editing for bg unit. All points share the same depth, so batch edit the Z point + float depth = mSelectedUnitRail.Points.Count == 0 ? 0 : mSelectedUnitRail.Points[0].Position.Z; + + ImGui.Text("Z Depth"); ImGui.TableNextColumn(); + if (ImGui.DragFloat("##Depth", ref depth, 0.1f)) + { + //Update depth to all points + foreach (var p in mSelectedUnitRail.Points) + p.Position = new System.Numerics.Vector3(p.Position.X, p.Position.Y, depth); + mSelectedUnitRail.mCourseUnit.GenerateTileSubUnits(); + } + + ImGui.EndTable(); } - ImGui.NextColumn(); - - ImGui.Columns(1); } } else if (mSelectedGlobalLink != null) @@ -1023,36 +1036,40 @@ void ProcessRail(BGUnitRail rail) if (ImGui.CollapsingHeader("Properties", ImGuiTreeNodeFlags.DefaultOpen)) { - ImGui.Columns(2); - ImGui.Text("Source Hash"); ImGui.NextColumn(); - string srcHash = mSelectedGlobalLink.mSource.ToString(); - if (ImGui.InputText("##Source Hash", ref srcHash, 256, ImGuiInputTextFlags.CharsDecimal | ImGuiInputTextFlags.EnterReturnsTrue)) + if (ImGui.BeginTable("Props", 2, ImGuiTableFlags.BordersInnerV | ImGuiTableFlags.Resizable)) { - mSelectedGlobalLink.mSource = Convert.ToUInt64(srcHash); - } - - ImGui.NextColumn(); + ImGui.TableNextRow(); + ImGui.TableSetColumnIndex(0); + ImGui.Text("Source Hash"); ImGui.TableNextColumn(); + string srcHash = mSelectedGlobalLink.mSource.ToString(); + if (ImGui.InputText("##Source Hash", ref srcHash, 256, ImGuiInputTextFlags.CharsDecimal | ImGuiInputTextFlags.EnterReturnsTrue)) + { + mSelectedGlobalLink.mSource = Convert.ToUInt64(srcHash); + } - ImGui.Text("Destination Hash"); ImGui.NextColumn(); - string destHash = mSelectedGlobalLink.mDest.ToString(); - if (ImGui.InputText("##Dest Hash", ref destHash, 256, ImGuiInputTextFlags.CharsDecimal | ImGuiInputTextFlags.EnterReturnsTrue)) - { - mSelectedGlobalLink.mDest = Convert.ToUInt64(destHash); - } + ImGui.TableNextRow(); + ImGui.TableSetColumnIndex(0); + ImGui.Text("Destination Hash"); ImGui.TableNextColumn(); + string destHash = mSelectedGlobalLink.mDest.ToString(); + if (ImGui.InputText("##Dest Hash", ref destHash, 256, ImGuiInputTextFlags.CharsDecimal | ImGuiInputTextFlags.EnterReturnsTrue)) + { + mSelectedGlobalLink.mDest = Convert.ToUInt64(destHash); + } - ImGui.NextColumn(); + ImGui.TableNextRow(); + ImGui.TableSetColumnIndex(0); + ImGui.Text("Link Type"); ImGui.TableNextColumn(); - ImGui.Text("Link Type"); ImGui.NextColumn(); + List types = linkTypes.ToList(); + int idx = types.IndexOf(mSelectedGlobalLink.mLinkName); + ImGui.SetNextItemWidth(ImGui.GetContentRegionAvail().X); + if (ImGui.Combo("##Link Type", ref idx, linkTypes, linkTypes.Length)) + { + mSelectedGlobalLink.mLinkName = linkTypes[idx]; + } - List types = linkTypes.ToList(); - int idx = types.IndexOf(mSelectedGlobalLink.mLinkName); - ImGui.SetNextItemWidth(ImGui.GetContentRegionAvail().X); - if (ImGui.Combo("##Link Type", ref idx, linkTypes, linkTypes.Length)) - { - mSelectedGlobalLink.mLinkName = linkTypes[idx]; + ImGui.EndTable(); } - - ImGui.Columns(1); } } else if (editContext.IsSingleObjectSelected(out CourseRail? mSelectedRail)) @@ -1064,127 +1081,159 @@ void ProcessRail(BGUnitRail rail) if (ImGui.CollapsingHeader("Properties", ImGuiTreeNodeFlags.DefaultOpen)) { - ImGui.Columns(2); - ImGui.Text("Hash"); ImGui.NextColumn(); - string hash = mSelectedRail.mHash.ToString(); - if (ImGui.InputText("##Hash", ref hash, 256, ImGuiInputTextFlags.CharsDecimal | ImGuiInputTextFlags.EnterReturnsTrue)) + if (ImGui.BeginTable("DynamProps", 2, ImGuiTableFlags.BordersInnerV | ImGuiTableFlags.Resizable)) { - mSelectedRail.mHash = Convert.ToUInt64(hash); - } + ImGui.TableNextRow(); + ImGui.TableSetColumnIndex(0); + ImGui.Text("Hash"); ImGui.TableNextColumn(); + string hash = mSelectedRail.mHash.ToString(); + if (ImGui.InputText("##Hash", ref hash, 256, ImGuiInputTextFlags.CharsDecimal | ImGuiInputTextFlags.EnterReturnsTrue)) + { + mSelectedRail.mHash = Convert.ToUInt64(hash); + } - ImGui.NextColumn(); - ImGui.Text("IsClosed"); - ImGui.NextColumn(); - ImGui.Checkbox("##IsClosed", ref mSelectedRail.mIsClosed); + ImGui.TableNextRow(); + ImGui.TableSetColumnIndex(0); + ImGui.Text("IsClosed"); + ImGui.TableNextColumn(); + ImGui.Checkbox("##IsClosed", ref mSelectedRail.mIsClosed); - ImGui.Columns(1); + ImGui.EndTable(); + } } if (ImGui.CollapsingHeader("Dynamic Properties", ImGuiTreeNodeFlags.DefaultOpen)) { - ImGui.Columns(2); - - foreach (KeyValuePair param in mSelectedRail.mParameters) + if (ImGui.BeginTable("DynamProps", 2, ImGuiTableFlags.BordersInnerV | ImGuiTableFlags.Resizable)) { - string type = param.Value.GetType().ToString(); - ImGui.Text(param.Key); - ImGui.NextColumn(); - - switch (type) + foreach (KeyValuePair param in mSelectedRail.mParameters) { - case "System.Int32": - int int_val = (int)param.Value; - if (ImGui.InputInt($"##{param.Key}", ref int_val)) - { - mSelectedRail.mParameters[param.Key] = int_val; - } - break; - case "System.Boolean": - bool bool_val = (bool)param.Value; - if (ImGui.Checkbox($"##{param.Key}", ref bool_val)) - { - mSelectedRail.mParameters[param.Key] = bool_val; - } - break; - } + ImGui.TableNextRow(); + ImGui.TableSetColumnIndex(0); + string type = param.Value.GetType().ToString(); + ImGui.Text(param.Key); + ImGui.TableNextColumn(); - ImGui.NextColumn(); + switch (type) + { + case "System.Int32": + int int_val = (int)param.Value; + if (ImGui.InputInt($"##{param.Key}", ref int_val)) + { + mSelectedRail.mParameters[param.Key] = int_val; + } + break; + case "System.Boolean": + bool bool_val = (bool)param.Value; + if (ImGui.Checkbox($"##{param.Key}", ref bool_val)) + { + mSelectedRail.mParameters[param.Key] = bool_val; + } + break; + } + } + ImGui.EndTable(); } } } - else if (editContext.IsSingleObjectSelected(out CourseRail.CourseRailPoint? mSelectedRailPoint)) + else if (editContext.IsSingleObjectSelected(out CourseRail.CourseRailPoint? mSelectedRailPoint) || + editContext.IsSingleObjectSelected(out CourseRail.CourseRailPointControl? mSelectedRailPointCont)) { + if(editContext.IsSingleObjectSelected(out CourseRail.CourseRailPointControl? cont)) + mSelectedRailPoint ??= cont.point; ImGui.AlignTextToFramePadding(); ImGui.Text($"Selected Rail Point"); ImGui.NewLine(); ImGui.Separator(); - + if (ImGui.CollapsingHeader("Properties", ImGuiTreeNodeFlags.DefaultOpen)) { - ImGui.Columns(2); - ImGui.Text("Hash"); ImGui.NextColumn(); - string hash = mSelectedRailPoint.mHash.ToString(); - if (ImGui.InputText("##Hash", ref hash, 256, ImGuiInputTextFlags.CharsDecimal | ImGuiInputTextFlags.EnterReturnsTrue)) + if (ImGui.BeginTable("Props", 2, ImGuiTableFlags.BordersInnerV | ImGuiTableFlags.Resizable)) { - mSelectedRailPoint.mHash = Convert.ToUInt64(hash); - } - ImGui.NextColumn(); + ImGui.TableNextRow(); + ImGui.TableSetColumnIndex(0); + ImGui.Text("Hash"); ImGui.TableNextColumn(); + string hash = mSelectedRailPoint.mHash.ToString(); + if (ImGui.InputText("##Hash", ref hash, 256, ImGuiInputTextFlags.CharsDecimal | ImGuiInputTextFlags.EnterReturnsTrue)) + { + mSelectedRailPoint.mHash = Convert.ToUInt64(hash); + } - ImGui.AlignTextToFramePadding(); - ImGui.Text("Translation"); - ImGui.NextColumn(); + ImGui.TableNextRow(); + ImGui.TableSetColumnIndex(0); + ImGui.AlignTextToFramePadding(); + ImGui.Text("Translation"); + + ImGui.TableNextColumn(); + + ImGui.DragFloat3("##Translation", ref mSelectedRailPoint.mTranslate, 0.25f); - ImGui.PushItemWidth(ImGui.GetColumnWidth() - ImGui.GetStyle().ScrollbarSize); + ImGui.TableNextRow(); + ImGui.TableSetColumnIndex(0); + ImGui.AlignTextToFramePadding(); + ImGui.Text("Curve Control"); + + ImGui.TableNextColumn(); + ImGui.Checkbox("##Curved", ref mSelectedRailPoint.mIsCurve); + ImGui.SameLine(); + if (!mSelectedRailPoint.mIsCurve) + ImGui.BeginDisabled(true); + ImGui.PushItemWidth(ImGui.GetColumnWidth() - ImGui.GetStyle().ScrollbarSize); + ImGui.DragFloat3("##Control", ref mSelectedRailPoint.mControl.mTranslate, 0.25f); + ImGui.PopItemWidth(); - ImGui.DragFloat3("##Translation", ref mSelectedRailPoint.mTranslate, 0.25f); - ImGui.PopItemWidth(); + if (!mSelectedRailPoint.mIsCurve) + ImGui.EndDisabled(); - ImGui.Columns(1); + ImGui.EndTable(); + } } if (ImGui.CollapsingHeader("Dynamic Properties", ImGuiTreeNodeFlags.DefaultOpen)) { - ImGui.Columns(2); - - foreach (KeyValuePair param in mSelectedRailPoint.mParameters) + if (ImGui.BeginTable("DynamProps", 2, ImGuiTableFlags.BordersInnerV | ImGuiTableFlags.Resizable)) { - string type = param.Value.GetType().ToString(); - ImGui.Text(param.Key); - ImGui.NextColumn(); - - switch (type) + foreach (KeyValuePair param in mSelectedRailPoint.mParameters) { - case "System.UInt32": - int uint_val = Convert.ToInt32(param.Value); - if (ImGui.InputInt($"##{param.Key}", ref uint_val)) - { - mSelectedRailPoint.mParameters[param.Key] = Convert.ToUInt32(uint_val); - } - break; - case "System.Int32": - int int_val = (int)param.Value; - if (ImGui.InputInt($"##{param.Key}", ref int_val)) - { - mSelectedRailPoint.mParameters[param.Key] = int_val; - } - break; - case "System.Single": - float float_val = (float)param.Value; - if (ImGui.InputFloat($"##{param.Key}", ref float_val)) - { - mSelectedRailPoint.mParameters[param.Key] = float_val; - } - break; - case "System.Boolean": - bool bool_val = (bool)param.Value; - if (ImGui.Checkbox($"##{param.Key}", ref bool_val)) - { - mSelectedRailPoint.mParameters[param.Key] = bool_val; - } - break; - } + ImGui.TableNextRow(); + ImGui.TableSetColumnIndex(0); + string type = param.Value.GetType().ToString(); + ImGui.Text(param.Key); + ImGui.TableNextColumn(); - ImGui.NextColumn(); + switch (type) + { + case "System.UInt32": + int uint_val = Convert.ToInt32(param.Value); + if (ImGui.InputInt($"##{param.Key}", ref uint_val)) + { + mSelectedRailPoint.mParameters[param.Key] = Convert.ToUInt32(uint_val); + } + break; + case "System.Int32": + int int_val = (int)param.Value; + if (ImGui.InputInt($"##{param.Key}", ref int_val)) + { + mSelectedRailPoint.mParameters[param.Key] = int_val; + } + break; + case "System.Single": + float float_val = (float)param.Value; + if (ImGui.InputFloat($"##{param.Key}", ref float_val)) + { + mSelectedRailPoint.mParameters[param.Key] = float_val; + } + break; + case "System.Boolean": + bool bool_val = (bool)param.Value; + if (ImGui.Checkbox($"##{param.Key}", ref bool_val)) + { + mSelectedRailPoint.mParameters[param.Key] = bool_val; + } + break; + } + } + ImGui.EndTable(); } } } @@ -1223,71 +1272,74 @@ private static void AreaParameters(AreaParam area) if (ImGui.BeginPopup($"AreaParams", ImGuiWindowFlags.NoMove)) { ImGui.SeparatorText("Area Parameters"); - ImGui.Columns(2); - foreach (string key in areaParams.Keys) + if (ImGui.BeginTable("AreaParms", 2, ImGuiTableFlags.BordersInnerV | ImGuiTableFlags.Resizable)) { - string paramType = areaParams[key]; + foreach (string key in areaParams.Keys) + { + ImGui.TableNextRow(); + ImGui.TableSetColumnIndex(0); + string paramType = areaParams[key]; - //if (!area.ContainsParam(key)) - //{ - // continue; - //} + //if (!area.ContainsParam(key)) + //{ + // continue; + //} - ImGui.Text(key); - ImGui.NextColumn(); + ImGui.Text(key); + ImGui.TableNextColumn(); - ImGui.PushItemWidth(ImGui.GetColumnWidth() - 5); + ImGui.PushItemWidth(ImGui.GetColumnWidth() - 5); - switch (paramType) - { - case "String": - { - string value = ""; - if (area.ContainsParam(key)) + switch (paramType) + { + case "String": { - value = (string)area.GetParam(area.GetRoot(), key, paramType); + string value = ""; + if (area.ContainsParam(key)) + { + value = (string)area.GetParam(area.GetRoot(), key, paramType); + } + ImGui.InputText($"##{key}", ref value, 1024); + break; } - ImGui.InputText($"##{key}", ref value, 1024); - break; - } - case "Bool": - { - bool value = false; - if (area.ContainsParam(key)) + case "Bool": { - value = (bool)area.GetParam(area.GetRoot(), key, paramType); + bool value = false; + if (area.ContainsParam(key)) + { + value = (bool)area.GetParam(area.GetRoot(), key, paramType); + } + ImGui.Checkbox($"##{key}", ref value); + break; } - ImGui.Checkbox($"##{key}", ref value); - break; - } - case "Int": - { - int value = 0; - if (area.ContainsParam(key)) + case "Int": { - //value = (int)area.GetParam(area.GetRoot(), key, paramType); + int value = 0; + if (area.ContainsParam(key)) + { + //value = (int)area.GetParam(area.GetRoot(), key, paramType); + } + ImGui.InputInt($"##{key}", ref value); + break; } - ImGui.InputInt($"##{key}", ref value); - break; - } - case "Float": - { - float value = 0.0f; - if (area.ContainsParam(key)) + case "Float": { - value = (float)area.GetParam(area.GetRoot(), key, paramType); + float value = 0.0f; + if (area.ContainsParam(key)) + { + value = (float)area.GetParam(area.GetRoot(), key, paramType); + } + ImGui.InputFloat($"##{key}", ref value); + break; } - ImGui.InputFloat($"##{key}", ref value); + default: + Console.WriteLine(key); break; - } - default: - Console.WriteLine(key); - break; + } + ImGui.PopItemWidth(); } - ImGui.PopItemWidth(); - - ImGui.NextColumn(); + ImGui.EndTable(); } ImGui.EndPopup(); } @@ -1367,34 +1419,38 @@ void RailListItem(string type, BGUnitRail rail, int id) } ImGui.SameLine(); - ImGui.Columns(2); - - void SelectRail() + if (ImGui.BeginTable("Rails", 2, ImGuiTableFlags.BordersInnerV | ImGuiTableFlags.Resizable)) { - editContext.DeselectAllOfType(); - editContext.Select(rail); - } + ImGui.TableNextRow(); + ImGui.TableSetColumnIndex(0); - if (ImGui.Selectable($"##{name}{wallname}", isSelected, ImGuiSelectableFlags.SpanAllColumns)) - { - SelectRail(); - } - if (ImGui.IsItemHovered() && ImGui.IsMouseClicked(ImGuiMouseButton.Right)) - { - SelectRail(); - } + void SelectRail() + { + editContext.DeselectAllOfType(); + editContext.Select(rail); + } - ImGui.SameLine(); + if (ImGui.Selectable($"##{name}{wallname}", isSelected, ImGuiSelectableFlags.SpanAllColumns)) + { + SelectRail(); + } + if (ImGui.IsItemHovered() && ImGui.IsMouseClicked(ImGuiMouseButton.Right)) + { + SelectRail(); + } - //Shift text from selection - ImGui.SetCursorPosX(ImGui.GetCursorPosX() + 22); - ImGui.Text(wallname); + ImGui.SameLine(); - ImGui.NextColumn(); + //Shift text from selection + ImGui.SetCursorPosX(ImGui.GetCursorPosX() + 22); + ImGui.Text(wallname); - ImGui.TextDisabled($"(Num Points: {rail.Points.Count})"); + ImGui.TableNextColumn(); - ImGui.Columns(1); + ImGui.TextDisabled($"(Num Points: {rail.Points.Count})"); + + ImGui.EndTable(); + } ImGui.Unindent(); } @@ -1648,28 +1704,32 @@ private void RecursiveLinkFind(CourseArea area, CourseLinkHolder links, bool isSelected = editContext.IsSelected(actor); ImGui.PushID($"##{actorName}"); - ImGui.Columns(2); - - if (ImGui.Selectable(actorName, isSelected, ImGuiSelectableFlags.SpanAllColumns)) + if (ImGui.BeginTable("##Links", 2, ImGuiTableFlags.BordersInnerV | ImGuiTableFlags.Resizable)) { - activeViewport.SelectedActor(actor); - } - else if (ImGui.IsItemFocused()) - { - activeViewport.SelectedActor(actor); - } + ImGui.TableNextRow(); + ImGui.TableSetColumnIndex(0); + + if (ImGui.Selectable(actorName, isSelected, ImGuiSelectableFlags.SpanAllColumns)) + { + activeViewport.SelectedActor(actor); + } + else if (ImGui.IsItemFocused()) + { + activeViewport.SelectedActor(actor); + } - if (ImGui.IsItemHovered() && ImGui.IsMouseDoubleClicked(0)) - { - activeViewport.FrameSelectedActor(actor); - } + if (ImGui.IsItemHovered() && ImGui.IsMouseDoubleClicked(0)) + { + activeViewport.FrameSelectedActor(actor); + } - ImGui.NextColumn(); - ImGui.BeginDisabled(); - ImGui.Text(name); - ImGui.EndDisabled(); - ImGui.Columns(1); + ImGui.TableNextColumn(); + ImGui.BeginDisabled(); + ImGui.Text(name); + ImGui.EndDisabled(); + ImGui.EndTable(); + } ImGui.PopID(); } @@ -1844,27 +1904,32 @@ private void CourseActorsLayerView(CourseActorHolder actorArray) bool isSelected = editContext.IsSelected(actor); ImGui.PushID(actorHash.ToString()); - ImGui.Columns(2); - if (ImGui.Selectable(actorName, isSelected, ImGuiSelectableFlags.SpanAllColumns)) + if (ImGui.BeginTable("##Links", 2, ImGuiTableFlags.BordersInnerV | ImGuiTableFlags.Resizable)) { - activeViewport.SelectedActor(actor); - } - else if (ImGui.IsItemFocused()) - { - activeViewport.SelectedActor(actor); - } + ImGui.TableNextRow(); + ImGui.TableSetColumnIndex(0); + + if (ImGui.Selectable(actorName, isSelected, ImGuiSelectableFlags.SpanAllColumns)) + { + activeViewport.SelectedActor(actor); + } + else if (ImGui.IsItemFocused()) + { + activeViewport.SelectedActor(actor); + } - if (ImGui.IsItemHovered() && ImGui.IsMouseDoubleClicked(0)) - { - activeViewport.FrameSelectedActor(actor); - } + if (ImGui.IsItemHovered() && ImGui.IsMouseDoubleClicked(0)) + { + activeViewport.FrameSelectedActor(actor); + } - ImGui.NextColumn(); - ImGui.BeginDisabled(); - ImGui.Text(name); - ImGui.EndDisabled(); - ImGui.Columns(1); + ImGui.TableNextColumn(); + ImGui.BeginDisabled(); + ImGui.Text(name); + ImGui.EndDisabled(); + ImGui.EndTable(); + } ImGui.PopID(); } @@ -2076,7 +2141,7 @@ float DegToRad(float deg) ImGui.AlignTextToFramePadding(); ImGui.Text(label); - ImGui.NextColumn(); + ImGui.TableNextColumn(); ImGui.PushItemWidth(ImGui.GetColumnWidth() - ImGui.GetStyle().ScrollbarSize); @@ -2090,44 +2155,43 @@ float DegToRad(float deg) } ImGui.PopItemWidth(); - - ImGui.NextColumn(); } if (ImGui.CollapsingHeader("Transform", ImGuiTreeNodeFlags.DefaultOpen)) { ImGui.Indent(); - ImGui.Columns(2); - - ImGui.AlignTextToFramePadding(); - ImGui.Text("Scale"); - ImGui.NextColumn(); - - ImGui.PushItemWidth(ImGui.GetColumnWidth() - ImGui.GetStyle().ScrollbarSize); + if (ImGui.BeginTable("Trans", 2, ImGuiTableFlags.BordersInnerV | ImGuiTableFlags.Resizable)) + { + ImGui.TableNextRow(); + ImGui.TableSetColumnIndex(0); + ImGui.AlignTextToFramePadding(); + ImGui.Text("Scale"); + ImGui.TableNextColumn(); - ImGui.DragFloat3("##Scale", ref actor.mScale, 0.25f, 0, float.MaxValue); - ImGui.PopItemWidth(); + ImGui.PushItemWidth(ImGui.GetColumnWidth() - ImGui.GetStyle().ScrollbarSize); - ImGui.NextColumn(); + ImGui.DragFloat3("##Scale", ref actor.mScale, 0.25f, 0, float.MaxValue); + ImGui.PopItemWidth(); - ImGui.Columns(1); - ImGui.Unindent(); + ImGui.TableNextRow(); + ImGui.TableSetColumnIndex(0); - ImGui.Indent(); - ImGui.Columns(2); + EditFloat3RadAsDeg("Rotation", ref actor.mRotation, 0.25f); - EditFloat3RadAsDeg("Rotation", ref actor.mRotation, 0.25f); + ImGui.TableNextRow(); + ImGui.TableSetColumnIndex(0); - ImGui.AlignTextToFramePadding(); - ImGui.Text("Translation"); - ImGui.NextColumn(); + ImGui.AlignTextToFramePadding(); + ImGui.Text("Translation"); + ImGui.TableNextColumn(); - ImGui.PushItemWidth(ImGui.GetColumnWidth() - ImGui.GetStyle().ScrollbarSize); + ImGui.PushItemWidth(ImGui.GetColumnWidth() - ImGui.GetStyle().ScrollbarSize); - ImGui.DragFloat3("##Translation", ref actor.mTranslation, 0.25f); - ImGui.PopItemWidth(); + ImGui.DragFloat3("##Translation", ref actor.mTranslation, 0.25f); + ImGui.PopItemWidth(); - ImGui.Columns(1); + ImGui.EndTable(); + } ImGui.Unindent(); } } @@ -2154,101 +2218,102 @@ private void DynamicParamNode(CourseActor actor) ImGui.Indent(); - ImGui.Columns(2); - - if (param == "ChildActorSelectName" && ChildActorParam.ActorHasChildParam(actor.mPackName)) + if (ImGui.BeginTable("DynamProps", 2, ImGuiTableFlags.BordersInnerV | ImGuiTableFlags.Resizable)) { - string id = $"##{param}"; - List list = ChildActorParam.GetActorParams(actor.mPackName); - int selected = list.IndexOf(actor.mActorParameters["ChildActorSelectName"].ToString()); - ImGui.Text("ChildParameters"); - ImGui.NextColumn(); - ImGui.PushItemWidth(ImGui.GetColumnWidth() - ImGui.GetStyle().ScrollbarSize); - - if (ImGui.Combo("##Parameters", ref selected, list.ToArray(), list.Count)) + if (param == "ChildActorSelectName" && ChildActorParam.ActorHasChildParam(actor.mPackName)) { - actor.mActorParameters["ChildActorSelectName"] = list[selected]; + ImGui.TableNextRow(); + ImGui.TableSetColumnIndex(0); + string id = $"##{param}"; + List list = ChildActorParam.GetActorParams(actor.mPackName); + int selected = list.IndexOf(actor.mActorParameters["ChildActorSelectName"].ToString()); + ImGui.Text("ChildParameters"); + ImGui.TableNextColumn(); + ImGui.PushItemWidth(ImGui.GetColumnWidth() - ImGui.GetStyle().ScrollbarSize); + + if (ImGui.Combo("##Parameters", ref selected, list.ToArray(), list.Count)) + { + actor.mActorParameters["ChildActorSelectName"] = list[selected]; + } } - } - else - { - foreach (KeyValuePair pair in ParamDB.GetComponentParams(param)) + else { - string id = $"##{pair.Key}"; - - ImGui.AlignTextToFramePadding(); - ImGui.Text(pair.Key); - ImGui.NextColumn(); + foreach (KeyValuePair pair in ParamDB.GetComponentParams(param)) + { + ImGui.TableNextRow(); + ImGui.TableSetColumnIndex(0); + string id = $"##{pair.Key}"; - ImGui.PushItemWidth(ImGui.GetColumnWidth() - ImGui.GetStyle().ScrollbarSize); + ImGui.AlignTextToFramePadding(); + ImGui.Text(pair.Key); + ImGui.TableNextColumn(); - if (actor.mActorParameters.ContainsKey(pair.Key)) - { - var actorParam = actor.mActorParameters[pair.Key]; + ImGui.PushItemWidth(ImGui.GetColumnWidth() - ImGui.GetStyle().ScrollbarSize); - if(pair.Value.IsSignedInt(out int minValue, out int maxValue)) + if (actor.mActorParameters.ContainsKey(pair.Key)) { - int val_int = (int)actorParam; - if (ImGui.InputInt(id, ref val_int)) + var actorParam = actor.mActorParameters[pair.Key]; + + if(pair.Value.IsSignedInt(out int minValue, out int maxValue)) { - actor.mActorParameters[pair.Key] = Math.Clamp(val_int, minValue, maxValue); + int val_int = (int)actorParam; + if (ImGui.InputInt(id, ref val_int)) + { + actor.mActorParameters[pair.Key] = Math.Clamp(val_int, minValue, maxValue); + } } - } - else if (pair.Value.IsUnsignedInt(out minValue, out maxValue)) - { - uint val_uint = (uint)actorParam; - int val_int = unchecked((int)val_uint); - if (ImGui.InputInt(id, ref val_int)) + else if (pair.Value.IsUnsignedInt(out minValue, out maxValue)) { - actor.mActorParameters[pair.Key] = unchecked((uint)Math.Clamp(val_int, minValue, maxValue)); + uint val_uint = (uint)actorParam; + int val_int = unchecked((int)val_uint); + if (ImGui.InputInt(id, ref val_int)) + { + actor.mActorParameters[pair.Key] = unchecked((uint)Math.Clamp(val_int, minValue, maxValue)); + } } - } - else if (pair.Value.IsBool()) - { - bool val_bool = (bool)actorParam; - if (ImGui.Checkbox(id, ref val_bool)) + else if (pair.Value.IsBool()) { - actor.mActorParameters[pair.Key] = val_bool; - } + bool val_bool = (bool)actorParam; + if (ImGui.Checkbox(id, ref val_bool)) + { + actor.mActorParameters[pair.Key] = val_bool; + } - } - else if (pair.Value.IsFloat()) - { - float val_float = (float)actorParam; - if (ImGui.InputFloat(id, ref val_float)) + } + else if (pair.Value.IsFloat()) { - actor.mActorParameters[pair.Key] = val_float; + float val_float = (float)actorParam; + if (ImGui.InputFloat(id, ref val_float)) + { + actor.mActorParameters[pair.Key] = val_float; + } } - } - else if (pair.Value.IsString()) - { - string val_string = (string)actorParam; - if (ImGui.InputText(id, ref val_string, 1024)) + else if (pair.Value.IsString()) { - actor.mActorParameters[pair.Key] = val_string; + string val_string = (string)actorParam; + if (ImGui.InputText(id, ref val_string, 1024)) + { + actor.mActorParameters[pair.Key] = val_string; + } } - } - else if (pair.Value.IsDouble()) - { - double val = (double)actorParam; - if (ImGui.InputDouble(id, ref val)) + else if (pair.Value.IsDouble()) { - actor.mActorParameters[pair.Key] = val; + double val = (double)actorParam; + if (ImGui.InputDouble(id, ref val)) + { + actor.mActorParameters[pair.Key] = val; + } } } - } - - ImGui.PopItemWidth(); - ImGui.NextColumn(); + ImGui.PopItemWidth(); + } } - } - - ImGui.Columns(1); + ImGui.EndTable(); + } ImGui.Unindent(); ImGui.Unindent(); - } } } diff --git a/Fushigi/ui/widgets/LevelViewport.cs b/Fushigi/ui/widgets/LevelViewport.cs index af80ad5b..051c9e8d 100644 --- a/Fushigi/ui/widgets/LevelViewport.cs +++ b/Fushigi/ui/widgets/LevelViewport.cs @@ -82,6 +82,7 @@ internal class LevelViewport(CourseArea area, GL gl, CourseAreaScene areaScene) public bool IsViewportHovered; public bool IsViewportActive; + public bool isPanGesture; public WonderViewType WonderViewMode = WonderViewType.Normal; public bool PlayAnimations = false; public bool ShowGrid = true; @@ -219,8 +220,9 @@ public void SelectedActor(CourseActor actor) public void HandleCameraControls(double deltaSeconds) { - bool isPanGesture = ImGui.IsMouseDragging(ImGuiMouseButton.Middle) || - (ImGui.IsMouseDragging(ImGuiMouseButton.Left) && ImGui.GetIO().KeyShift && !mEditContext.IsAnySelected()); + isPanGesture = ImGui.IsMouseDragging(ImGuiMouseButton.Middle) || + (ImGui.IsMouseDragging(ImGuiMouseButton.Left) && ImGui.GetIO().KeyShift && + mHoveredObject == null && !mEditContext.IsSelected(mHoveredObject) && !dragRelease); if (IsViewportActive && isPanGesture) { @@ -751,7 +753,7 @@ void InteractionWithFocus(KeyboardModifier modifiers) return; } - if (ImGui.IsMouseDragging(ImGuiMouseButton.Left)) + if (ImGui.IsMouseDragging(ImGuiMouseButton.Left) && !isPanGesture) { if (mEditContext.IsAnySelected()) { @@ -789,6 +791,22 @@ void InteractionWithFocus(KeyboardModifier modifiers) rail.mTranslate = posVec; } } + if (mEditContext.IsSingleObjectSelected(out CourseRail.CourseRailPointControl? railCont)) + { + Vector3 posVec = ScreenToWorld(ImGui.GetMousePos()); + + if (ImGui.GetIO().KeyShift) + { + railCont.mTranslate = posVec; + } + else + { + posVec.X = MathF.Round(posVec.X * 2, MidpointRounding.AwayFromZero) / 2; + posVec.Y = MathF.Round(posVec.Y * 2, MidpointRounding.AwayFromZero) / 2; + posVec.Z = railCont.mTranslate.Z; + railCont.mTranslate = posVec; + } + } } @@ -814,7 +832,7 @@ void InteractionWithFocus(KeyboardModifier modifiers) } } - if (ImGui.IsMouseDown(0)) + if (ImGui.IsMouseDown(0) && !isPanGesture) dragRelease = ImGui.IsMouseDragging(0); if(mHoveredObject != null && @@ -838,6 +856,13 @@ mHoveredObject is CourseActor && if (ImGui.IsKeyPressed(ImGuiKey.Delete)) ObjectDeletionRequested?.Invoke(mEditContext.GetSelectedObjects().ToList()); + if (mEditContext.IsSingleObjectSelected(out CourseRail.CourseRailPoint? point) && + mHoveredObject == point && + ImGui.IsMouseDoubleClicked(0)) + { + point.mIsCurve = !point.mIsCurve; + } + if (ImGui.IsKeyPressed(ImGuiKey.Escape)) mEditContext.DeselectAll(); } @@ -957,6 +982,7 @@ Vector2[] GetPoints() foreach (var point in rail.mPoints) { var pos2D = this.WorldToScreen(new(point.mTranslate.X, point.mTranslate.Y, point.mTranslate.Z)); + var contPos2D = this.WorldToScreen(point.mControl.mTranslate); Vector2 pnt = new(pos2D.X, pos2D.Y); bool isHovered = (ImGui.GetMousePos() - pnt).Length() < 6.0f; if (isHovered) @@ -964,7 +990,11 @@ Vector2[] GetPoints() bool selected = mEditContext.IsSelected(point); if (selected) + { selectedPoint = point; + if ((ImGui.GetMousePos() - contPos2D).Length() < 6.0f) + newHoveredObject = point.mControl; + } } //Delete selected @@ -1034,16 +1064,6 @@ Vector2[] GetPoints() var rail_color = selected ? ImGui.ColorConvertFloat4ToU32(new(1, 1, 0, 1)) : color; List pointsList = []; - foreach (CourseRail.CourseRailPoint pnt in rail.mPoints) - { - bool point_selected = mEditContext.IsSelected(pnt); - var rail_point_color = point_selected ? ImGui.ColorConvertFloat4ToU32(new(1, 1, 0, 1)) : color; - var size = newHoveredObject == pnt ? pointSize * 1.5f : pointSize; - - var pos2D = WorldToScreen(pnt.mTranslate); - mDrawList.AddCircleFilled(pos2D, size, rail_point_color); - pointsList.Add(pos2D); - } var segmentCount = rail.mPoints.Count; if (!rail.mIsClosed) @@ -1061,12 +1081,12 @@ Vector2[] GetPoints() Vector2 cpOutA2D = posA2D; Vector2 cpInB2D = posB2D; - if (pointA.mControl.TryGetValue(out Vector3 control)) - cpOutA2D = WorldToScreen(control); + if (pointA.mIsCurve) + cpOutA2D = WorldToScreen(pointA.mControl.mTranslate); - if (pointB.mControl.TryGetValue(out control)) + if (pointB.mIsCurve) //invert control point - cpInB2D = WorldToScreen(pointB.mTranslate - (control - pointB.mTranslate)); + cpInB2D = WorldToScreen(pointB.mTranslate - (pointB.mControl.mTranslate - pointB.mTranslate)); if (cpOutA2D == posA2D && cpInB2D == posB2D) { @@ -1080,6 +1100,24 @@ Vector2[] GetPoints() float thickness = newHoveredObject == rail ? 3f : 2.5f; mDrawList.PathStroke(rail_color, ImDrawFlags.None, thickness); + + foreach (CourseRail.CourseRailPoint pnt in rail.mPoints) + { + bool point_selected = mEditContext.IsSelected(pnt) || mEditContext.IsSelected(pnt.mControl); + var rail_point_color = point_selected ? ImGui.ColorConvertFloat4ToU32(new(1, 1, 0, 1)) : color; + var size = (newHoveredObject == pnt || newHoveredObject == pnt.mControl) ? pointSize * 1.5f : pointSize; + + var pos2D = WorldToScreen(pnt.mTranslate); + mDrawList.AddCircleFilled(pos2D, size, rail_point_color, pnt.mIsCurve ? 0:4); + pointsList.Add(pos2D); + + if (point_selected && pnt.mIsCurve) + { + var contPos2D = WorldToScreen(pnt.mControl.mTranslate); + mDrawList.AddLine(pos2D, contPos2D, rail_point_color, thickness); + mDrawList.AddCircleFilled(contPos2D, size, rail_point_color, 4); + } + } } } @@ -1132,7 +1170,8 @@ Vector2[] GetPoints() uint color = ImGui.ColorConvertFloat4ToU32(new(0.5f, 1, 0, 1)); - if (actor.mPackName.Contains("CameraArea") || actor.mActorPack?.Category == "AreaObj") + if (actor.mPackName.Contains("CameraArea") || + (actor.mActorPack?.Category == "AreaObj" && actor.mActorPack?.ShapeParams == null)) { if (actor.mPackName.Contains("CameraArea")) color = ImGui.ColorConvertFloat4ToU32(new(1, 0, 0, 1)); diff --git a/Fushigi/ui/widgets/PathSelector.cs b/Fushigi/ui/widgets/PathSelector.cs index 2c7b9ea9..b7ff2556 100644 --- a/Fushigi/ui/widgets/PathSelector.cs +++ b/Fushigi/ui/widgets/PathSelector.cs @@ -10,6 +10,8 @@ internal class PathSelector { public static bool Show(string label, ref string path, bool isValid = true) { + bool edited; + bool clicked; //Ensure path isn't null for imgui if (path == null) path = ""; @@ -18,49 +20,50 @@ public static bool Show(string label, ref string path, bool isValid = true) if (!System.IO.Directory.Exists(path)) isValid = false; - ImGui.Columns(2); + ImGui.BeginTable("path", 2, ImGuiTableFlags.BordersInnerV | ImGuiTableFlags.Resizable); + { + ImGui.TableSetupColumn("one", ImGuiTableColumnFlags.WidthFixed, 150.0f); + + ImGui.TableNextRow(); + ImGui.TableSetColumnIndex(0); + edited = false; - ImGui.SetColumnWidth(0, 150); + ImGui.Text(label); - bool edited = false; + ImGui.TableNextColumn(); - ImGui.Text(label); + ImGui.PushItemWidth(ImGui.GetColumnWidth() - 100); - ImGui.NextColumn(); + if (!isValid) + { + ImGui.PushStyleColor(ImGuiCol.FrameBg, new Vector4(0.5f, 0, 0, 1)); + ImGui.InputText($"##{label}", ref path, 500, ImGuiInputTextFlags.ReadOnly); + ImGui.PopStyleColor(); + } + else + { + ImGui.PushStyleColor(ImGuiCol.FrameBg, new Vector4(0, 0.5f, 0, 1)); + ImGui.InputText($"##{label}", ref path, 500, ImGuiInputTextFlags.ReadOnly); + ImGui.PopStyleColor(); + } - ImGui.PushItemWidth(ImGui.GetColumnWidth() - 100); + if (ImGui.BeginPopupContextItem($"{label}_clear", ImGuiPopupFlags.MouseButtonRight)) + { + if (ImGui.MenuItem("Clear")) + { + path = ""; + edited = true; + } + ImGui.EndPopup(); + } - if (!isValid) - { - ImGui.PushStyleColor(ImGuiCol.FrameBg, new Vector4(0.5f, 0, 0, 1)); - ImGui.InputText($"##{label}", ref path, 500, ImGuiInputTextFlags.ReadOnly); - ImGui.PopStyleColor(); - } - else - { - ImGui.PushStyleColor(ImGuiCol.FrameBg, new Vector4(0, 0.5f, 0, 1)); - ImGui.InputText($"##{label}", ref path, 500, ImGuiInputTextFlags.ReadOnly); - ImGui.PopStyleColor(); - } + ImGui.PopItemWidth(); - if (ImGui.BeginPopupContextItem($"{label}_clear", ImGuiPopupFlags.MouseButtonRight)) - { - if (ImGui.MenuItem("Clear")) - { - path = ""; - edited = true; - } - ImGui.EndPopup(); - } - - ImGui.PopItemWidth(); + ImGui.SameLine(); + clicked = ImGui.Button($"Select##{label}"); - ImGui.SameLine(); - bool clicked = ImGui.Button($"Select##{label}"); - - ImGui.NextColumn(); - - ImGui.Columns(1); + ImGui.EndTable(); + } if (clicked) { From 9d730cc4e2d7971881919d76e931bc5112a78610 Mon Sep 17 00:00:00 2001 From: Donavin Draws <51259260+DonavinDraws@users.noreply.github.com> Date: Fri, 12 Jan 2024 14:20:35 -0600 Subject: [PATCH 09/19] Control Point Small Fix Forgot a Declaration --- Fushigi/course/CourseRail.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/Fushigi/course/CourseRail.cs b/Fushigi/course/CourseRail.cs index b988cfe3..cd7aca63 100644 --- a/Fushigi/course/CourseRail.cs +++ b/Fushigi/course/CourseRail.cs @@ -132,6 +132,7 @@ public CourseRailPoint() { this.mHash = RandomUtil.GetRandom(); this.mTranslate = new System.Numerics.Vector3(); + this.mControl = new(this); } From 7538146d6eacb2083bf1d940f1cf9dbe6809c2a1 Mon Sep 17 00:00:00 2001 From: Donavin Draws <51259260+DonavinDraws@users.noreply.github.com> Date: Fri, 12 Jan 2024 15:22:45 -0600 Subject: [PATCH 10/19] Rail Control Tweak The Rail's control point now defaults to the point's position instead of (0, 0, 0). --- Fushigi/course/CourseRail.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Fushigi/course/CourseRail.cs b/Fushigi/course/CourseRail.cs index cd7aca63..7092a503 100644 --- a/Fushigi/course/CourseRail.cs +++ b/Fushigi/course/CourseRail.cs @@ -132,7 +132,7 @@ public CourseRailPoint() { this.mHash = RandomUtil.GetRandom(); this.mTranslate = new System.Numerics.Vector3(); - this.mControl = new(this); + this.mControl = new(this, mTranslate); } @@ -149,7 +149,7 @@ public CourseRailPoint(BymlHashTable node, string pointParam) { mHash = BymlUtil.GetNodeData(node["Hash"]); mTranslate = BymlUtil.GetVector3FromArray(node["Translate"] as BymlArrayNode); - mControl = new(this); + mControl = new(this, mTranslate); IDictionary comp; if (ParamDB.TryGetRailPointComponent(pointParam, out var componentName)) @@ -235,7 +235,7 @@ public BymlHashTable BuildNode() } public class CourseRailPointControl { - public CourseRailPointControl(CourseRail.CourseRailPoint point, System.Numerics.Vector3 pos = default) + public CourseRailPointControl(CourseRail.CourseRailPoint point, System.Numerics.Vector3 pos) { this.point = point; this.mTranslate = pos; From 17ac2ae8f20e28c07c1b1a33e8858f1c0af911a4 Mon Sep 17 00:00:00 2001 From: Donavin Draws <51259260+DonavinDraws@users.noreply.github.com> Date: Thu, 15 Feb 2024 21:10:25 -0600 Subject: [PATCH 11/19] UI Fixes, Undo Fix, & Layer Add * Tweaked the UI of Actor to Rail Link and Local Links window * Fixed selecting Tile Units, Rails, and Global Links via the UI windows * Fixed batch undo when deleting multiple objects * (Experimental WIP) Added button to add new layers --- Fushigi/course/Course.cs | 2 +- Fushigi/course/CourseRail.cs | 12 +- .../distance_view/DistantViewManager.cs | 10 +- Fushigi/ui/EditContextBase.cs | 2 +- Fushigi/ui/widgets/CourseScene.cs | 330 +++++++++++------- Fushigi/ui/widgets/LevelViewport.cs | 51 ++- 6 files changed, 254 insertions(+), 153 deletions(-) diff --git a/Fushigi/course/Course.cs b/Fushigi/course/Course.cs index 63516255..d612e19e 100644 --- a/Fushigi/course/Course.cs +++ b/Fushigi/course/Course.cs @@ -124,7 +124,7 @@ public void Save() foreach (CourseArea area in mAreas) { - refArr.AddNodeToArray(BymlUtil.CreateNode("", $"Work/Stage/StageParam/{area.GetName()}.game__stage__StageParam.gyml")); + refArr.AddNodeToArray(BymlUtil.CreateNode($"Work/Stage/StageParam/{area.GetName()}.game__stage__StageParam.gyml")); } stageParamRoot.AddNode(BymlNodeId.Array, refArr, "RefStages"); diff --git a/Fushigi/course/CourseRail.cs b/Fushigi/course/CourseRail.cs index 7092a503..072769b4 100644 --- a/Fushigi/course/CourseRail.cs +++ b/Fushigi/course/CourseRail.cs @@ -210,17 +210,17 @@ public BymlHashTable BuildNode() if (mIsCurve) { BymlArrayNode controlNode = new(3); - controlNode.AddNodeToArray(BymlUtil.CreateNode("X", mControl.mTranslate.X)); - controlNode.AddNodeToArray(BymlUtil.CreateNode("Y", mControl.mTranslate.Y)); - controlNode.AddNodeToArray(BymlUtil.CreateNode("Z", mControl.mTranslate.Z)); + controlNode.AddNodeToArray(BymlUtil.CreateNode(mControl.mTranslate.X)); + controlNode.AddNodeToArray(BymlUtil.CreateNode( mControl.mTranslate.Y)); + controlNode.AddNodeToArray(BymlUtil.CreateNode(mControl.mTranslate.Z)); tbl.AddNode(BymlNodeId.Array, controlNode, "Control1"); } BymlArrayNode translateNode = new(3); - translateNode.AddNodeToArray(BymlUtil.CreateNode("X", mTranslate.X)); - translateNode.AddNodeToArray(BymlUtil.CreateNode("Y", mTranslate.Y)); - translateNode.AddNodeToArray(BymlUtil.CreateNode("Z", mTranslate.Z)); + translateNode.AddNodeToArray(BymlUtil.CreateNode(mTranslate.X)); + translateNode.AddNodeToArray(BymlUtil.CreateNode(mTranslate.Y)); + translateNode.AddNodeToArray(BymlUtil.CreateNode(mTranslate.Z)); tbl.AddNode(BymlNodeId.Array, translateNode, "Translate"); diff --git a/Fushigi/course/distance_view/DistantViewManager.cs b/Fushigi/course/distance_view/DistantViewManager.cs index 80936af4..293cdde3 100644 --- a/Fushigi/course/distance_view/DistantViewManager.cs +++ b/Fushigi/course/distance_view/DistantViewManager.cs @@ -13,7 +13,8 @@ public class DistantViewManager { private Dictionary LayerMatrices = new Dictionary(); - private DVLayerParamTable ParamTable = new DVLayerParamTable(); + private DVLayerParamTable LvlParamTable = new DVLayerParamTable(); + public static DVLayerParamTable ParamTable = new DVLayerParamTable(); private CourseActor DVLocator; @@ -28,6 +29,7 @@ public DistantViewManager(CourseArea area) public void PrepareDVLocator(CourseArea area) { ParamTable.LoadDefault(); + LvlParamTable = ParamTable; foreach (var actor in area.GetActors()) { @@ -43,13 +45,13 @@ public void PrepareDVLocator(CourseArea area) { string layer_param = (string)DVLocator.mActorParameters["DVLayerParamName"]; if (!string.IsNullOrEmpty(layer_param)) - ParamTable.Load(layer_param); + LvlParamTable.Load(layer_param); } } } LayerMatrices.Clear(); - foreach (var layer in this.ParamTable.Layers) + foreach (var layer in this.LvlParamTable.Layers) LayerMatrices.Add(layer.Key, Matrix4x4.Identity); } @@ -61,7 +63,7 @@ public void UpdateMatrix(string layer, ref Matrix4x4 matrix) public void Calc(Vector3 camera_pos) { - foreach (var layer in this.ParamTable.Layers.Keys) + foreach (var layer in this.LvlParamTable.Layers.Keys) { var scroll_config = ParamTable.Layers[layer]; var locator_pos = DVLocator != null ? DVLocator.mTranslation : Vector3.Zero; diff --git a/Fushigi/ui/EditContextBase.cs b/Fushigi/ui/EditContextBase.cs index 00945477..4586e1db 100644 --- a/Fushigi/ui/EditContextBase.cs +++ b/Fushigi/ui/EditContextBase.cs @@ -36,7 +36,7 @@ public void Commit(string name) public ICommittable BeginBatchAction() { - mCurrentActionBatch = []; + mCurrentActionBatch ??= []; var batchAction = new BatchAction(this); mNestedBatchActions.Push(batchAction); return batchAction; diff --git a/Fushigi/ui/widgets/CourseScene.cs b/Fushigi/ui/widgets/CourseScene.cs index a1a00fa3..be715696 100644 --- a/Fushigi/ui/widgets/CourseScene.cs +++ b/Fushigi/ui/widgets/CourseScene.cs @@ -16,6 +16,7 @@ using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using Fushigi.rstb; +using Fushigi.course.distance_view; using Fushigi.ui.helpers; using Fasterflect; @@ -46,7 +47,7 @@ class CourseScene string mActorSearchText = ""; - CourseLink? mSelectedGlobalLink = null; + //CourseLink? mSelectedGlobalLink = null; string[] viewMode = [ "View All Actors", @@ -195,6 +196,7 @@ public void DrawUI(GL gl, double deltaSeconds) RailsPanel(); GlobalLinksPanel(); + RailLinksPanel(); LocalLinksPanel(); @@ -457,6 +459,13 @@ private void ActorsPanel() ctx, "Delete actors"); } + ImGui.SameLine(); + + if (ImGui.Button("Add Layer")) + { + _ = AddLayerWithLayerWindow(); + } + ImGui.AlignTextToFramePadding(); ImGui.Text(IconUtil.ICON_SEARCH.ToString()); ImGui.SameLine(); @@ -537,24 +546,25 @@ private void RailLinksPanel() for (int i = 0; i < railLinks.Count; i++) { + ImGui.PushID(i); ImGui.TableNextRow(); ImGui.TableSetColumnIndex(0); - ImGui.PushID(i); CourseActorToRailLink link = railLinks[i]; string hash = link.mSourceActor.ToString(); int actorIndex = actors.FindIndex(x => x.mHash == link.mSourceActor); + ImGui.SetNextItemWidth(ImGui.GetContentRegionAvail().X); if (ImGui.InputText("##actor", ref hash, 100) && ulong.TryParse(hash, out ulong hashInt)) - link.mSourceActor = hashInt; + link.mSourceActor = hashInt; if(actorIndex == -1) { ImGui.SameLine(); ImGui.TextDisabled("Invalid"); } - ImGui.TableNextColumn(); int railIndex = rails.FindIndex(x => x.mHash == link.mDestRail); + ImGui.SetNextItemWidth(ImGui.GetContentRegionAvail().X); if (ImGui.BeginCombo("##rail", railIndex >= 0 ? ("rail " + railIndex) : "None")) { for (int iRail = 0; iRail < rails.Count; iRail++) @@ -577,6 +587,7 @@ private void RailLinksPanel() if (pointIndex == -1) pointIndex = 0; + ImGui.SetNextItemWidth(ImGui.GetContentRegionAvail().X); if (ImGui.InputInt("##railpoint", ref pointIndex)) pointIndex = Math.Clamp(pointIndex, 0, rails[railIndex].mPoints.Count - 1); @@ -584,7 +595,7 @@ private void RailLinksPanel() } ImGui.TableNextColumn(); - if (ImGui.Button("Delete", new Vector2(ImGui.GetContentRegionAvail().X * 0.65f, 0))) + if (ImGui.Button("Delete", new Vector2(ImGui.GetContentRegionAvail().X - ImGui.GetStyle().ScrollbarSize, 0))) { ctx.DeleteRailLink(link); i--; @@ -646,8 +657,7 @@ private void SelectionParameterPanel() } ImGui.PopItemWidth(); - ImGui.TableNextRow(); - ImGui.TableSetColumnIndex(0); + ImGui.TableNextColumn(); ImGui.Text("Actor Hash"); ImGui.TableNextColumn(); string hash = mSelectedActor.mHash.ToString(); @@ -655,13 +665,12 @@ private void SelectionParameterPanel() ImGui.InputText("##Actor Hash", ref hash, 256, ImGuiInputTextFlags.ReadOnly); ImGui.PopItemWidth(); - ImGui.TableNextRow(); - ImGui.TableSetColumnIndex(0); - + ImGui.TableNextColumn(); + ImGui.Separator(); + ImGui.TableNextColumn(); ImGui.Separator(); - ImGui.TableNextRow(); - ImGui.TableSetColumnIndex(0); + ImGui.TableNextColumn(); ImGui.AlignTextToFramePadding(); ImGui.Text("Name"); @@ -674,10 +683,10 @@ private void SelectionParameterPanel() ImGui.PopItemWidth(); - ImGui.TableNextRow(); - ImGui.TableSetColumnIndex(0); + ImGui.TableNextColumn(); ImGui.Text("Layer"); ImGui.TableNextColumn(); + ImGui.PushItemWidth(ImGui.GetColumnWidth() - ImGui.GetStyle().ScrollbarSize); if (ImGui.BeginCombo("##Dropdown", mSelectedActor.mLayer)) { @@ -753,10 +762,10 @@ private void SelectionParameterPanel() if (ImGui.BeginTable("##Links", 3, ImGuiTableFlags.BordersInnerV | ImGuiTableFlags.Resizable)) { + ImGui.TableNextRow(); + ImGui.TableSetColumnIndex(0); for (int i = 0; i < hashArray.Count; i++) { - ImGui.TableNextRow(); - ImGui.TableSetColumnIndex(0); ImGui.PushID($"{hashArray[i].ToString()}_{i}"); ImGui.SetCursorPosX(ImGui.GetCursorPosX() + ImGui.GetStyle().FramePadding.X); ImGui.Text("Destination"); @@ -842,6 +851,7 @@ private void SelectionParameterPanel() editContext.DeleteLink(linkName, mSelectedActor.mHash, hashArray[i]); ImGui.PopID(); + ImGui.TableNextColumn(); } ImGui.EndTable(); } @@ -889,8 +899,7 @@ private void SelectionParameterPanel() ImGui.Combo("##mModelType", ref Unsafe.As(ref mSelectedUnit.mModelType), CourseUnit.ModelTypeNames, CourseUnit.ModelTypeNames.Length); - ImGui.TableNextRow(); - ImGui.TableSetColumnIndex(0); + ImGui.TableNextColumn(); ImGui.Text("Skin Division"); ImGui.TableNextColumn(); ImGui.Combo("##SkinDivision", ref Unsafe.As(ref mSelectedUnit.mSkinDivision), @@ -1001,8 +1010,7 @@ void ProcessRail(BGUnitRail rail) if (ImGui.Checkbox("##IsClosed", ref mSelectedUnitRail.IsClosed)) mSelectedUnitRail.mCourseUnit.GenerateTileSubUnits(); - ImGui.TableNextRow(); - ImGui.TableSetColumnIndex(0); + ImGui.TableNextColumn(); //Depth editing for bg unit. All points share the same depth, so batch edit the Z point float depth = mSelectedUnitRail.Points.Count == 0 ? 0 : mSelectedUnitRail.Points[0].Position.Z; @@ -1019,7 +1027,7 @@ void ProcessRail(BGUnitRail rail) } } } - else if (mSelectedGlobalLink != null) + else if (editContext.IsSingleObjectSelected(out CourseLink? mSelectedGlobalLink)) { ImGui.AlignTextToFramePadding(); ImGui.Text($"Selected Global Link"); @@ -1047,8 +1055,7 @@ void ProcessRail(BGUnitRail rail) mSelectedGlobalLink.mSource = Convert.ToUInt64(srcHash); } - ImGui.TableNextRow(); - ImGui.TableSetColumnIndex(0); + ImGui.TableNextColumn(); ImGui.Text("Destination Hash"); ImGui.TableNextColumn(); string destHash = mSelectedGlobalLink.mDest.ToString(); if (ImGui.InputText("##Dest Hash", ref destHash, 256, ImGuiInputTextFlags.CharsDecimal | ImGuiInputTextFlags.EnterReturnsTrue)) @@ -1056,8 +1063,7 @@ void ProcessRail(BGUnitRail rail) mSelectedGlobalLink.mDest = Convert.ToUInt64(destHash); } - ImGui.TableNextRow(); - ImGui.TableSetColumnIndex(0); + ImGui.TableNextColumn(); ImGui.Text("Link Type"); ImGui.TableNextColumn(); List types = linkTypes.ToList(); @@ -1092,8 +1098,7 @@ void ProcessRail(BGUnitRail rail) mSelectedRail.mHash = Convert.ToUInt64(hash); } - ImGui.TableNextRow(); - ImGui.TableSetColumnIndex(0); + ImGui.TableNextColumn(); ImGui.Text("IsClosed"); ImGui.TableNextColumn(); ImGui.Checkbox("##IsClosed", ref mSelectedRail.mIsClosed); @@ -1106,10 +1111,10 @@ void ProcessRail(BGUnitRail rail) { if (ImGui.BeginTable("DynamProps", 2, ImGuiTableFlags.BordersInnerV | ImGuiTableFlags.Resizable)) { + ImGui.TableNextRow(); + ImGui.TableSetColumnIndex(0); foreach (KeyValuePair param in mSelectedRail.mParameters) { - ImGui.TableNextRow(); - ImGui.TableSetColumnIndex(0); string type = param.Value.GetType().ToString(); ImGui.Text(param.Key); ImGui.TableNextColumn(); @@ -1131,6 +1136,7 @@ void ProcessRail(BGUnitRail rail) } break; } + ImGui.TableNextColumn(); } ImGui.EndTable(); } @@ -1159,8 +1165,7 @@ void ProcessRail(BGUnitRail rail) mSelectedRailPoint.mHash = Convert.ToUInt64(hash); } - ImGui.TableNextRow(); - ImGui.TableSetColumnIndex(0); + ImGui.TableNextColumn(); ImGui.AlignTextToFramePadding(); ImGui.Text("Translation"); @@ -1168,8 +1173,7 @@ void ProcessRail(BGUnitRail rail) ImGui.DragFloat3("##Translation", ref mSelectedRailPoint.mTranslate, 0.25f); - ImGui.TableNextRow(); - ImGui.TableSetColumnIndex(0); + ImGui.TableNextColumn(); ImGui.AlignTextToFramePadding(); ImGui.Text("Curve Control"); @@ -1193,10 +1197,10 @@ void ProcessRail(BGUnitRail rail) { if (ImGui.BeginTable("DynamProps", 2, ImGuiTableFlags.BordersInnerV | ImGuiTableFlags.Resizable)) { + ImGui.TableNextRow(); + ImGui.TableSetColumnIndex(0); foreach (KeyValuePair param in mSelectedRailPoint.mParameters) { - ImGui.TableNextRow(); - ImGui.TableSetColumnIndex(0); string type = param.Value.GetType().ToString(); ImGui.Text(param.Key); ImGui.TableNextColumn(); @@ -1232,6 +1236,7 @@ void ProcessRail(BGUnitRail rail) } break; } + ImGui.TableNextColumn(); } ImGui.EndTable(); } @@ -1275,10 +1280,11 @@ private static void AreaParameters(AreaParam area) if (ImGui.BeginTable("AreaParms", 2, ImGuiTableFlags.BordersInnerV | ImGuiTableFlags.Resizable)) { + ImGui.TableNextRow(); + ImGui.TableSetColumnIndex(0); foreach (string key in areaParams.Keys) { - ImGui.TableNextRow(); - ImGui.TableSetColumnIndex(0); + string paramType = areaParams[key]; //if (!area.ContainsParam(key)) @@ -1338,6 +1344,7 @@ private static void AreaParameters(AreaParam area) break; } ImGui.PopItemWidth(); + ImGui.TableNextColumn(); } ImGui.EndTable(); } @@ -1426,7 +1433,7 @@ void RailListItem(string type, BGUnitRail rail, int id) void SelectRail() { - editContext.DeselectAllOfType(); + editContext.DeselectAll(); editContext.Select(rail); } @@ -1571,7 +1578,7 @@ private void CourseRailsView(CourseRailHolder railHolder) foreach (CourseRail rail in railHolder.mRails) { - var rail_node_flags = ImGuiTreeNodeFlags.None; + var rail_node_flags = ImGuiTreeNodeFlags.OpenOnArrow; if (editContext.IsSelected(rail) && !editContext.IsAnySelected()) { @@ -1610,18 +1617,20 @@ private void CourseRailsView(CourseRailHolder railHolder) private void CourseGlobalLinksView(CourseLinkHolder linkHolder) { + var editContext = areaScenes[selectedArea].EditContext; for (int i = 0; i < linkHolder.mLinks.Count; i++) { CourseLink link = linkHolder.mLinks[i]; - if (ImGui.Selectable($"Link {i}")) + if (ImGui.Selectable($"Link {i}", editContext.IsSelected(link))) { - mSelectedGlobalLink = link; + editContext.DeselectAll(); + editContext.Select(link); } } } //VERY ROUGH BASE - //Still need to implement recursion on getting links, currently just displays the top most links + //TODO, optomize recursion private void AreaLocalLinksView(CourseArea area) { var links = area.mLinkHolder; @@ -1633,8 +1642,11 @@ private void AreaLocalLinksView(CourseArea area) var topLinks = area.GetActors() .Where(x => links.GetDestHashesFromSrc(x.mHash).Count > 0); - - RecursiveLinkFind(area, links, editContext, em, topLinks); + if (ImGui.BeginTable("##Links", 2, ImGuiTableFlags.BordersInnerV | ImGuiTableFlags.Resizable)) + { + RecursiveLinkFind(area, links, editContext, em, topLinks); + ImGui.EndTable(); + } ImGui.PopClipRect(); @@ -1646,92 +1658,68 @@ private void RecursiveLinkFind(CourseArea area, CourseLinkHolder links, { foreach (CourseActor actor in linkList) { + ImGui.TableNextRow(); + ImGuiTreeNodeFlags node_flags = ImGuiTreeNodeFlags.FramePadding | ImGuiTreeNodeFlags.OpenOnArrow; ImGui.PushID($"##{actor.mHash}"); + + string actorName = actor.mPackName; + string name = actor.mName; + ulong actorHash = actor.mHash; + bool isSelected = editContext.IsSelected(actor); + + ImGui.TableSetColumnIndex(1); + ImGui.BeginDisabled(); + ImGui.Text(name); + ImGui.EndDisabled(); + bool expanded = false; bool isVisible = true; float margin = 1.5f * em; float headerHeight = 1.4f * em; Vector2 cp = ImGui.GetCursorScreenPos(); + ImGui.TableSetColumnIndex(0); + if(links.GetDestHashesFromSrc(actor.mHash).Count > 0) { + if (isSelected) + node_flags |= ImGuiTreeNodeFlags.Selected; expanded = ImGui.TreeNodeEx($"{actor.mHash}", node_flags, actor.mPackName); - - if (ImGui.IsItemFocused()) - { - activeViewport.SelectedActor(actor); - } - - if (ImGui.IsItemHovered() && ImGui.IsMouseDoubleClicked(0)) - { - activeViewport.FrameSelectedActor(actor); - } - - if (!isVisible) - ImGui.BeginDisabled(); - - UpdateWonderVisibility(actor, links, area); - - if (expanded) - { - foreach (var link in links.GetDestHashesFromSrc(actor.mHash)) - { - ImGui.PushID($"##{link.Key}"); - if(ImGui.TreeNodeEx($"##{link.Key}", ImGuiTreeNodeFlags.FramePadding, link.Key)) - { - var reLinks = area.GetActors().Where(x => link.Value.Contains(x.mHash)); - RecursiveLinkFind(area, links, editContext, em, reLinks); - ImGui.TreePop(); - } - ImGui.PopID(); - } - ImGui.TreePop(); - } } else { - string actorName = actor.mPackName; - string name = actor.mName; - ulong actorHash = actor.mHash; - //Check if the node is within the necessary search filter requirements if search is used - bool HasText = actor.mName.IndexOf(mActorSearchText, StringComparison.OrdinalIgnoreCase) >= 0 || - actor.mPackName.IndexOf(mActorSearchText, StringComparison.OrdinalIgnoreCase) >= 0 || - actor.ToString().Equals(mActorSearchText); + expanded = ImGui.Selectable(actorName, isSelected, ImGuiSelectableFlags.SpanAllColumns); + } - if (!HasText) - continue; + if (ImGui.IsItemFocused()) + { + activeViewport.SelectedActor(actor); + } - bool isSelected = editContext.IsSelected(actor); + if (ImGui.IsItemHovered() && ImGui.IsMouseDoubleClicked(0)) + { + activeViewport.FrameSelectedActor(actor); + } - ImGui.PushID($"##{actorName}"); - if (ImGui.BeginTable("##Links", 2, ImGuiTableFlags.BordersInnerV | ImGuiTableFlags.Resizable)) - { - ImGui.TableNextRow(); - ImGui.TableSetColumnIndex(0); - - if (ImGui.Selectable(actorName, isSelected, ImGuiSelectableFlags.SpanAllColumns)) - { - activeViewport.SelectedActor(actor); - } - else if (ImGui.IsItemFocused()) - { - activeViewport.SelectedActor(actor); - } + if (!isVisible) + ImGui.BeginDisabled(); - if (ImGui.IsItemHovered() && ImGui.IsMouseDoubleClicked(0)) + UpdateWonderVisibility(actor, links, area); + + if (expanded && (links.GetDestHashesFromSrc(actor.mHash).Count > 0)) + { + foreach (var link in links.GetDestHashesFromSrc(actor.mHash)) + { + ImGui.PushID($"##{link.Key}"); + if(ImGui.TreeNodeEx($"##{link.Key}", ImGuiTreeNodeFlags.FramePadding, link.Key)) { - activeViewport.FrameSelectedActor(actor); + var reLinks = area.GetActors().Where(x => link.Value.Contains(x.mHash)); + RecursiveLinkFind(area, links, editContext, em, reLinks); + ImGui.TreePop(); } - - - ImGui.TableNextColumn(); - ImGui.BeginDisabled(); - ImGui.Text(name); - ImGui.EndDisabled(); - ImGui.EndTable(); + ImGui.PopID(); } - - ImGui.PopID(); + ImGui.TreePop(); } if (!isVisible) @@ -2173,13 +2161,11 @@ float DegToRad(float deg) ImGui.DragFloat3("##Scale", ref actor.mScale, 0.25f, 0, float.MaxValue); ImGui.PopItemWidth(); - ImGui.TableNextRow(); - ImGui.TableSetColumnIndex(0); + ImGui.TableNextColumn(); EditFloat3RadAsDeg("Rotation", ref actor.mRotation, 0.25f); - ImGui.TableNextRow(); - ImGui.TableSetColumnIndex(0); + ImGui.TableNextColumn(); ImGui.AlignTextToFramePadding(); ImGui.Text("Translation"); @@ -2220,10 +2206,11 @@ private void DynamicParamNode(CourseActor actor) if (ImGui.BeginTable("DynamProps", 2, ImGuiTableFlags.BordersInnerV | ImGuiTableFlags.Resizable)) { + ImGui.TableNextRow(); + ImGui.TableSetColumnIndex(0); + if (param == "ChildActorSelectName" && ChildActorParam.ActorHasChildParam(actor.mPackName)) { - ImGui.TableNextRow(); - ImGui.TableSetColumnIndex(0); string id = $"##{param}"; List list = ChildActorParam.GetActorParams(actor.mPackName); int selected = list.IndexOf(actor.mActorParameters["ChildActorSelectName"].ToString()); @@ -2235,13 +2222,12 @@ private void DynamicParamNode(CourseActor actor) { actor.mActorParameters["ChildActorSelectName"] = list[selected]; } + ImGui.PopItemWidth(); } else { foreach (KeyValuePair pair in ParamDB.GetComponentParams(param)) { - ImGui.TableNextRow(); - ImGui.TableSetColumnIndex(0); string id = $"##{pair.Key}"; ImGui.AlignTextToFramePadding(); @@ -2307,6 +2293,7 @@ private void DynamicParamNode(CourseActor actor) } ImGui.PopItemWidth(); + ImGui.TableNextColumn(); } } @@ -2335,6 +2322,8 @@ private async Task DeleteObjectsWithWarningPrompt(IReadOnlyList objectsT CourseAreaEditContext ctx, string actionName) { var actors = objectsToDelete.OfType(); + if (actors.Count() == 1) + actionName = "Delete "+actors.ElementAt(0).mPackName; List dstMsgStrs = []; List srcMsgStrs = []; @@ -2446,33 +2435,79 @@ private async Task AddActorsWithSelectActorAndLayerWindow() } while ((modifier & KeyboardModifier.Shift) > 0); } + private async Task AddLayerWithLayerWindow() + { + var viewport = activeViewport; + var area = selectedArea; + var ctx = areaScenes[selectedArea].EditContext; + + if(mOpenToolWindows.Any(x=>x is SelectActorAndLayerWindow)) + return; + + var window = new SelectActorAndLayerWindow(mLayersVisibility, false); + mOpenToolWindows.Add(window); + + var result = await window.Result(); + if (!result.TryGetValue(out var resultVal)) + return; + + var layer = result.Value.layer; + if(layer == "PlayArea" || layer == "DecoArea") + { + var i = layer == "DecoArea" ? 0:1; + do + { + i++; + } while (mLayersVisibility.Keys.Any(x => x == $"{layer}{i}")); + layer += i; + } + + mLayersVisibility[layer] = true; + } interface IToolWindow { void Draw(ref bool windowOpen); } - class SelectActorAndLayerWindow(IReadOnlyDictionary mLayersVisibility) : IToolWindow + class SelectActorAndLayerWindow(IReadOnlyDictionary mLayersVisibility, bool addActors = true) : IToolWindow { public void Draw(ref bool windowOpen) { bool status; - if (mSelectedActor == null) - { - status = ImGui.Begin("Add Actor###SelectActorLayer", ref windowOpen); - SelectActorToAdd(); - } - else if(mSelectedLayer == null) + if(addActors) { - status = ImGui.Begin("Select Layer###SelectActorLayer", ref windowOpen); - SelectActorToAddLayer(); + if (mSelectedActor == null) + { + status = ImGui.Begin("Add Actor###SelectActorLayer", ref windowOpen); + SelectActorToAdd(); + } + else if(mSelectedLayer == null) + { + status = ImGui.Begin("Select Layer###SelectActorLayer", ref windowOpen); + SelectActorToAddLayer(); + } + else + { + mPromise.TrySetResult((addActors ? mSelectedActor:"", mSelectedLayer)); + windowOpen = false; + return; + } } else { - mPromise.TrySetResult((mSelectedActor, mSelectedLayer)); - windowOpen = false; - return; + if(mSelectedLayer == null) + { + status = ImGui.Begin("Select Layer###SelectActorLayer", ref windowOpen); + SelectLayerToAdd(); + } + else + { + mPromise.TrySetResult(("", mSelectedLayer)); + windowOpen = false; + return; + } } if (ImGui.IsKeyDown(ImGuiKey.Escape)) @@ -2525,7 +2560,7 @@ private void SelectActorToAddLayer() ImGui.InputText("Search", ref mAddLayerSearchQuery, 256); var fileteredLayers = mLayersVisibility.Keys.ToArray().ToImmutableList(); - + if (mAddLayerSearchQuery != "") { fileteredLayers = FuzzySharp.Process.ExtractAll(mAddLayerSearchQuery, mLayersVisibility.Keys.ToArray(), cutoff: 65) @@ -2548,6 +2583,39 @@ private void SelectActorToAddLayer() } } + // TODO, maybe find a way to combine with SelectActorToAddLayer(), if that's needed + private void SelectLayerToAdd() + { + ImGui.InputText("Search", ref mAddLayerSearchQuery, 256); + + string[] Layers = ["PlayArea", "DecoArea", "DecoAreaFront"]; + Layers = Layers.Concat(DistantViewManager.ParamTable.Layers.Keys) + .Except(mLayersVisibility.Keys) + .ToArray(); + var fileteredLayers = Layers.ToImmutableList(); + + if (mAddLayerSearchQuery != "") + { + fileteredLayers = FuzzySharp.Process.ExtractAll(mAddLayerSearchQuery, Layers, cutoff: 65) + .OrderByDescending(result => result.Score) + .Select(result => result.Value) + .ToImmutableList(); + } + + if (ImGui.BeginListBox("Select the layer you want to add the actor to.", ImGui.GetContentRegionAvail())) + { + foreach (string layer in fileteredLayers) + { + ImGui.Selectable(layer); + + if (ImGui.IsItemHovered() && ImGui.IsMouseDoubleClicked(0)) + mSelectedLayer = layer; + } + + ImGui.EndListBox(); + } + } + public Task<(string actor, string layer)?> Result() => mPromise.Task; private string? mSelectedActor; diff --git a/Fushigi/ui/widgets/LevelViewport.cs b/Fushigi/ui/widgets/LevelViewport.cs index 051c9e8d..d8090986 100644 --- a/Fushigi/ui/widgets/LevelViewport.cs +++ b/Fushigi/ui/widgets/LevelViewport.cs @@ -18,7 +18,9 @@ using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using static Fushigi.course.CourseUnit; +using Fushigi.ui.undo; using Vector3 = System.Numerics.Vector3; +using Fasterflect; namespace Fushigi.ui.widgets { @@ -835,21 +837,50 @@ void InteractionWithFocus(KeyboardModifier modifiers) if (ImGui.IsMouseDown(0) && !isPanGesture) dragRelease = ImGui.IsMouseDragging(0); - if(mHoveredObject != null && - mHoveredObject is CourseActor && - !dragRelease && - ImGui.IsMouseReleased(0)) + if(ImGui.IsMouseReleased(0)) { - if(ImGui.IsKeyDown(ImGuiKey.LeftShift) && - prevSelectVersion == mEditContext.SelectionVersion) + if(mHoveredObject != null && + mHoveredObject is CourseActor && + !dragRelease) { - mEditContext.Deselect(mHoveredObject!); + if(ImGui.IsKeyDown(ImGuiKey.LeftShift) && + prevSelectVersion == mEditContext.SelectionVersion) + { + mEditContext.Deselect(mHoveredObject!); + } + else if(!ImGui.IsKeyDown(ImGuiKey.LeftShift)) + { + mEditContext.DeselectAll(); + IViewportSelectable.DefaultSelect(mEditContext, mHoveredObject); + } } - else if(!ImGui.IsKeyDown(ImGuiKey.LeftShift)) + + var actors = mEditContext.GetSelectedObjects().Where(x => x.mTranslation != x.mStartingTrans) ?? []; + + if (actors.Count() > 0) { - mEditContext.DeselectAll(); - IViewportSelectable.DefaultSelect(mEditContext, mHoveredObject); + if (mEditContext.IsSingleObjectSelected(out CourseActor? act)) + { + mEditContext.CommitAction(new PropertyFieldsSetUndo( + act, + [("mTranslation", act.GetFieldValue("mStartingTrans"))], + $"{IconUtil.ICON_ARROWS_ALT} Move {string.Join(", ", act.mPackName)}")); + } + else + { + var batchAction = mEditContext.BeginBatchAction(); + foreach(CourseActor actor in mEditContext.GetSelectedObjects().Where(x => x.mTranslation != x.mStartingTrans)) + { + mEditContext.CommitAction(new PropertyFieldsSetUndo( + actor, + [("mTranslation", actor.GetFieldValue("mStartingTrans"))], + $"{IconUtil.ICON_ARROWS_ALT} Move {string.Join(", ", actor.mName)}")); + } + batchAction.Commit($"{IconUtil.ICON_ARROWS_ALT} Move {string.Join(", ", actors.Count())} Actors"); + } } + + dragRelease = false; } From 23280fd90d101ca7f1a8d2a8f388cb74943ec626 Mon Sep 17 00:00:00 2001 From: Donavin Draws <51259260+DonavinDraws@users.noreply.github.com> Date: Thu, 15 Feb 2024 21:34:23 -0600 Subject: [PATCH 12/19] Update CourseRail.cs removed a single space --- Fushigi/course/CourseRail.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Fushigi/course/CourseRail.cs b/Fushigi/course/CourseRail.cs index 072769b4..21c3f3d3 100644 --- a/Fushigi/course/CourseRail.cs +++ b/Fushigi/course/CourseRail.cs @@ -211,7 +211,7 @@ public BymlHashTable BuildNode() { BymlArrayNode controlNode = new(3); controlNode.AddNodeToArray(BymlUtil.CreateNode(mControl.mTranslate.X)); - controlNode.AddNodeToArray(BymlUtil.CreateNode( mControl.mTranslate.Y)); + controlNode.AddNodeToArray(BymlUtil.CreateNode(mControl.mTranslate.Y)); controlNode.AddNodeToArray(BymlUtil.CreateNode(mControl.mTranslate.Z)); tbl.AddNode(BymlNodeId.Array, controlNode, "Control1"); From 2304d870b03ba636370a5cf18da9a375318c213e Mon Sep 17 00:00:00 2001 From: Donavin Draws <51259260+DonavinDraws@users.noreply.github.com> Date: Fri, 16 Feb 2024 16:11:49 -0600 Subject: [PATCH 13/19] More Rail Fixes Overlapping points are possible now --- Fushigi/ui/widgets/LevelViewport.cs | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/Fushigi/ui/widgets/LevelViewport.cs b/Fushigi/ui/widgets/LevelViewport.cs index d8090986..fdb453de 100644 --- a/Fushigi/ui/widgets/LevelViewport.cs +++ b/Fushigi/ui/widgets/LevelViewport.cs @@ -880,7 +880,6 @@ mHoveredObject is CourseActor && } } - dragRelease = false; } @@ -1008,6 +1007,11 @@ Vector2[] GetPoints() bool isSelected = mEditContext.IsSelected(rail); bool hovered = MathUtil.HitTestLineLoopPoint(GetPoints(), 10f, ImGui.GetMousePos()); + //Rail selection disabled for now as it conflicts with point selection + //Putting it at the top of the for loop should make it prioritize points -Donavin + if (hovered) + newHoveredObject = rail; + CourseRail.CourseRailPoint selectedPoint = null; foreach (var point in rail.mPoints) @@ -1036,9 +1040,11 @@ Vector2[] GetPoints() if (selectedPoint != null && ImGui.IsMouseReleased(0)) { //Check if point matches an existing point, remove if intersected - var matching = rail.mPoints.Where(x => x.mTranslate == selectedPoint.mTranslate).ToList(); - if (matching.Count > 1) - rail.mPoints.Remove(selectedPoint); + //The base game has overlapping points, this breaks things + + // var matching = rail.mPoints.Where(x => x.mTranslate == selectedPoint.mTranslate).ToList(); + // if (matching.Count > 1) + // rail.mPoints.Remove(selectedPoint); } bool add_point = ImGui.IsMouseClicked(0) && ImGui.IsMouseDown(0) && ImGui.GetIO().KeyAlt; @@ -1080,10 +1086,6 @@ Vector2[] GetPoints() this.mEditContext.Select(newPoint); newHoveredObject = newPoint; } - - //Rail selection disabled for now as it conflicts with point selection - // if (hovered) - // newHoveredObject = rail; } foreach (CourseRail rail in mArea.mRailHolder.mRails) From 1ac8cc840a2171b48e077d07390ba643dcc64f3b Mon Sep 17 00:00:00 2001 From: Donavin Draws <51259260+DonavinDraws@users.noreply.github.com> Date: Sat, 17 Feb 2024 23:29:51 -0600 Subject: [PATCH 14/19] Layer Limits and Deletion * Added limits to the amount of Play and Deco Areas * WIP Deleting Layers * Fixed crash with loading bone params --- Fushigi/ui/widgets/CourseScene.cs | 62 +++++++++++++++++++++++++++-- Fushigi/ui/widgets/LevelViewport.cs | 4 +- 2 files changed, 61 insertions(+), 5 deletions(-) diff --git a/Fushigi/ui/widgets/CourseScene.cs b/Fushigi/ui/widgets/CourseScene.cs index be715696..99576e20 100644 --- a/Fushigi/ui/widgets/CourseScene.cs +++ b/Fushigi/ui/widgets/CourseScene.cs @@ -19,6 +19,7 @@ using Fushigi.course.distance_view; using Fushigi.ui.helpers; using Fasterflect; +using System.Text.RegularExpressions; namespace Fushigi.ui.widgets { @@ -1851,7 +1852,7 @@ private void CourseActorsLayerView(CourseActorHolder actorArray) expanded = ImGui.TreeNodeEx("TreeNode", ImGuiTreeNodeFlags.FramePadding, layer); ImGui.PushClipRect(wcMin, wcMax, false); - ImGui.SetCursorScreenPos(new Vector2(wcMax.X - (margin + em) / 2, cp.Y)); + ImGui.SetCursorScreenPos(new Vector2(wcMax.X - (margin + em*4) / 2, cp.Y)); isVisible = mLayersVisibility[layer]; if (ToggleButton($"VisibleCheckbox", IconUtil.ICON_EYE, IconUtil.ICON_EYE_SLASH, ref isVisible, new Vector2(em))) @@ -1863,6 +1864,13 @@ private void CourseActorsLayerView(CourseActorHolder actorArray) ImGui.AlignTextToFramePadding(); ImGui.Text(layer); } + var dummy = false; + ImGui.PushClipRect(wcMin, wcMax, false); + ImGui.SetCursorScreenPos(new Vector2(wcMax.X - (margin + em) / 2, cp.Y)); + if (ToggleButton($"Delete Layer", IconUtil.ICON_TRASH, IconUtil.ICON_TRASH, + ref dummy, new Vector2(em))) + _ = DeleteLayerWithWarningPrompt(layer, actorArray, editContext); + ImGui.PopClipRect(); if (!isVisible) ImGui.BeginDisabled(); @@ -2389,6 +2397,42 @@ private async Task DeleteObjectsWithWarningPrompt(IReadOnlyList objectsT batchAction.Commit($"{IconUtil.ICON_TRASH} {actionName}"); } + //TODO making this undoable + private async Task DeleteLayerWithWarningPrompt(string layer, + CourseActorHolder actorArray, CourseAreaEditContext ctx) + { + var actors = actorArray.mActors.FindAll(x => x.mLayer == layer); + var confirm = await OperationWarningDialog.ShowDialog(mPopupModalHost, + "Deletion warning", + "Are you sure you want to delete " + + $"{layer}"); + + if (confirm == OperationWarningDialog.DialogResult.Cancel) + return; + + bool noWarnings = !actors.Any(); + + if (!noWarnings) + { + List warningActors = []; + foreach (var actor in actors) + { + if (selectedArea.mActorHolder.TryGetActor(actor.mHash, out _)) + { + warningActors.Add($"{selectedArea.mActorHolder[actor.mHash].mPackName} [{selectedArea.mActorHolder[actor.mHash].mName}]\n"); + } + } + + var result = await OperationWarningDialog.ShowDialog(mPopupModalHost, + "Deletion warning", + "The following actors will be deleted", + ("Actors", warningActors)); + + if (result == OperationWarningDialog.DialogResult.Cancel) + return; + } + } + private async Task AddActorsWithSelectActorAndLayerWindow() { var viewport = activeViewport; @@ -2604,12 +2648,24 @@ private void SelectLayerToAdd() if (ImGui.BeginListBox("Select the layer you want to add the actor to.", ImGui.GetContentRegionAvail())) { - foreach (string layer in fileteredLayers) + for (var i = 0; i < fileteredLayers.Count; i++) { + var layer = i < 2 ? + fileteredLayers[i]+$" ({mLayersVisibility.Count(x => Regex.Replace(x.Key, @"\\d+", "").Contains(fileteredLayers[i]))}/10)": + fileteredLayers[i]; + + if(layer.Contains("(10/10)")) + ImGui.BeginDisabled(true); + ImGui.Selectable(layer); if (ImGui.IsItemHovered() && ImGui.IsMouseDoubleClicked(0)) - mSelectedLayer = layer; + { + mSelectedLayer = Layers[i]; + } + + if(layer.Contains("(10/10)")) + ImGui.EndDisabled(); } ImGui.EndListBox(); diff --git a/Fushigi/ui/widgets/LevelViewport.cs b/Fushigi/ui/widgets/LevelViewport.cs index fdb453de..791c3771 100644 --- a/Fushigi/ui/widgets/LevelViewport.cs +++ b/Fushigi/ui/widgets/LevelViewport.cs @@ -560,7 +560,7 @@ private void ActorModelExpand(CourseActor actor, BfresRender.BfresModel model, s Dictionary boneScaleLookup = []; - foreach (var boneParam in setting.mBoneSetting.BoneInfoList) + foreach (var boneParam in setting.mBoneSetting?.BoneInfoList ?? []) { Vector2 boneScale; if (boneParam.mIsCustomCalc) @@ -857,7 +857,7 @@ mHoveredObject is CourseActor && var actors = mEditContext.GetSelectedObjects().Where(x => x.mTranslation != x.mStartingTrans) ?? []; - if (actors.Count() > 0) + if (actors.Any()) { if (mEditContext.IsSingleObjectSelected(out CourseActor? act)) { From 78c847210a770d196079bd61d2867953fd58bae7 Mon Sep 17 00:00:00 2001 From: Donavin Draws <51259260+DonavinDraws@users.noreply.github.com> Date: Mon, 26 Feb 2024 16:19:04 -0600 Subject: [PATCH 15/19] Rail Fixes Deluxe * Rails now load and save their parameters properly * Fixed crash when assigning a a rail with no points to an actor to rail link --- Fushigi/course/CourseRail.cs | 9 +++++++++ Fushigi/param/ParamDB.cs | 10 +++++++--- Fushigi/ui/widgets/CourseScene.cs | 2 +- Fushigi/ui/widgets/LevelViewport.cs | 7 ++++--- Fushigi/util/BymlUtil.cs | 8 ++++++++ 5 files changed, 29 insertions(+), 7 deletions(-) diff --git a/Fushigi/course/CourseRail.cs b/Fushigi/course/CourseRail.cs index 21c3f3d3..936c5fa2 100644 --- a/Fushigi/course/CourseRail.cs +++ b/Fushigi/course/CourseRail.cs @@ -35,7 +35,16 @@ public CourseRail(BymlHashTable node) string pointParam = Path.GetFileNameWithoutExtension(BymlUtil.GetNodeData(node["Gyaml"])).Split(".game")[0]; var railParams = ParamDB.GetRailComponent(pointParam); + var railParent = ParamDB.GetRailComponentParent(railParams); var comp = ParamDB.GetRailComponentParams(railParams); + if (railParent != "null") + { + var parentComp = ParamDB.GetRailComponentParams(railParent); + foreach (var component in parentComp) + { + comp.TryAdd(component.Key, component.Value); + } + } if (!node.ContainsKey("Dynamic")) { diff --git a/Fushigi/param/ParamDB.cs b/Fushigi/param/ParamDB.cs index deec670b..1d588ef5 100644 --- a/Fushigi/param/ParamDB.cs +++ b/Fushigi/param/ParamDB.cs @@ -114,6 +114,7 @@ public static void Init() public static List GetActorComponents(string actor) => sActors[actor].Components; public static Dictionary GetComponentParams(string componentName) => sComponents[componentName].Parameters; + public static string GetComponentParent(string componentName) => sComponents[componentName].Parent; public static string GetRailComponent(string railName) => sRailParamList[railName].Components[0]; public static bool TryGetRailPointComponent(string railName, [NotNullWhen(true)] out string? componentName) { @@ -125,6 +126,7 @@ public static bool TryGetRailPointComponent(string railName, [NotNullWhen(true)] return componentName is not null; } + public static string GetRailComponentParent(string componentName) => sRails[componentName].Parent; public static Dictionary GetRailComponentParams(string componentName) => sRails[componentName].Parameters; public static string[] GetActors() => sActors.Keys.ToArray(); @@ -207,7 +209,7 @@ public static void Load(IProgress<(string operationName, float? progress)> progr { var byml = new Byml.Byml(new MemoryStream(File.ReadAllBytes(railComp))); string name = Path.GetFileNameWithoutExtension(railComp).Split(".engine")[0]; - Component component = ReadByml(byml); + Component component = ReadByml(byml, true); sRails.Add(name, component); } @@ -268,7 +270,7 @@ public static void Reload(IProgress<(string operationName, float? progress)> pro Load(progress); } - static Component ReadByml(Byml.Byml byml) + static Component ReadByml(Byml.Byml byml, bool isRailParam = false) { var root = (BymlHashTable)byml.Root; @@ -340,10 +342,12 @@ static Component ReadByml(Byml.Byml byml) /* if the IsInstanceParam value is False, it means that the parameter is not used in a course context * so, if it is False, we ignore it and move on to the next parameter as we will only read what matters + + * No, this causes some rail types to save without default parameters. -Donavin */ bool isInstParam = ((BymlNode)(ht["IsInstanceParam"])).Data; - if (!isInstParam) + if (!isInstParam && !isRailParam) { continue; } diff --git a/Fushigi/ui/widgets/CourseScene.cs b/Fushigi/ui/widgets/CourseScene.cs index 99576e20..7d001709 100644 --- a/Fushigi/ui/widgets/CourseScene.cs +++ b/Fushigi/ui/widgets/CourseScene.cs @@ -581,7 +581,7 @@ private void RailLinksPanel() ImGui.TextDisabled("Invalid"); } ImGui.TableNextColumn(); - if (railIndex >= 0) + if (railIndex >= 0 && rails[railIndex].mPoints.Count > 0) { int pointIndex = rails[railIndex].mPoints.FindIndex(x => x.mHash == link.mDestPoint); diff --git a/Fushigi/ui/widgets/LevelViewport.cs b/Fushigi/ui/widgets/LevelViewport.cs index 791c3771..6454efb9 100644 --- a/Fushigi/ui/widgets/LevelViewport.cs +++ b/Fushigi/ui/widgets/LevelViewport.cs @@ -1008,9 +1008,10 @@ Vector2[] GetPoints() bool hovered = MathUtil.HitTestLineLoopPoint(GetPoints(), 10f, ImGui.GetMousePos()); //Rail selection disabled for now as it conflicts with point selection - //Putting it at the top of the for loop should make it prioritize points -Donavin - if (hovered) - newHoveredObject = rail; + //Putting it at the top of the for loop should make it prioritize points + //TODO curved rails still need work-Donavin + // if (hovered) + // newHoveredObject = rail; CourseRail.CourseRailPoint selectedPoint = null; diff --git a/Fushigi/util/BymlUtil.cs b/Fushigi/util/BymlUtil.cs index 2d2a0d56..a4656bca 100644 --- a/Fushigi/util/BymlUtil.cs +++ b/Fushigi/util/BymlUtil.cs @@ -62,6 +62,14 @@ public static System.Numerics.Vector3 GetVector3FromArray(BymlArrayNode? array) vec.Z = GetNodeFromArray(array, 2); return vec; } + public static System.Numerics.Vector3 GetVector3FromHashTable(BymlHashTable? table) + { + System.Numerics.Vector3 vec = new System.Numerics.Vector3(); + vec.X = GetNodeData(table["X"]); + vec.Y = GetNodeData(table["Y"]); + vec.Z = GetNodeData(table["Z"]); + return vec; + } public static object GetValueFromDynamicNode(IBymlNode node, ParamDB.ComponentParam param) { From a06c5f8d0be90ceab193f60addd6cc3ddf8f6ffc Mon Sep 17 00:00:00 2001 From: Donavin Draws <51259260+DonavinDraws@users.noreply.github.com> Date: Thu, 29 Feb 2024 16:15:40 -0600 Subject: [PATCH 16/19] Local Links Optimization Some Local Link optimizations and preventing infinite recursion --- Fushigi/ui/widgets/CourseScene.cs | 82 +++++++++++++++++-------------- 1 file changed, 45 insertions(+), 37 deletions(-) diff --git a/Fushigi/ui/widgets/CourseScene.cs b/Fushigi/ui/widgets/CourseScene.cs index 7d001709..5e9f9f21 100644 --- a/Fushigi/ui/widgets/CourseScene.cs +++ b/Fushigi/ui/widgets/CourseScene.cs @@ -20,6 +20,7 @@ using Fushigi.ui.helpers; using Fasterflect; using System.Text.RegularExpressions; +using System.Collections; namespace Fushigi.ui.widgets { @@ -1182,7 +1183,7 @@ void ProcessRail(BGUnitRail rail) ImGui.Checkbox("##Curved", ref mSelectedRailPoint.mIsCurve); ImGui.SameLine(); if (!mSelectedRailPoint.mIsCurve) - ImGui.BeginDisabled(true); + ImGui.BeginDisabled(); ImGui.PushItemWidth(ImGui.GetColumnWidth() - ImGui.GetStyle().ScrollbarSize); ImGui.DragFloat3("##Control", ref mSelectedRailPoint.mControl.mTranslate, 0.25f); ImGui.PopItemWidth(); @@ -1632,6 +1633,8 @@ private void CourseGlobalLinksView(CourseLinkHolder linkHolder) //VERY ROUGH BASE //TODO, optomize recursion + List topLinks; + CourseActor? selected; private void AreaLocalLinksView(CourseArea area) { var links = area.mLinkHolder; @@ -1641,11 +1644,12 @@ private void AreaLocalLinksView(CourseArea area) var wcMin = ImGui.GetCursorScreenPos() + new Vector2(0, ImGui.GetScrollY()); var wcMax = wcMin + ImGui.GetContentRegionAvail(); - var topLinks = area.GetActors() - .Where(x => links.GetDestHashesFromSrc(x.mHash).Count > 0); + topLinks = area.GetActors() + .Where(x => links.mLinks.Any(y => y.mSource == x.mHash)).ToList(); if (ImGui.BeginTable("##Links", 2, ImGuiTableFlags.BordersInnerV | ImGuiTableFlags.Resizable)) { - RecursiveLinkFind(area, links, editContext, em, topLinks); + CourseActor? selected = null; + RecursiveLinkFind(area, links, editContext, em, topLinks, []); ImGui.EndTable(); } @@ -1655,14 +1659,13 @@ private void AreaLocalLinksView(CourseArea area) } private void RecursiveLinkFind(CourseArea area, CourseLinkHolder links, - CourseAreaEditContext editContext, float em, IEnumerable linkList) + CourseAreaEditContext editContext, float em, IEnumerable linkList, + Hashtable parentActors) { foreach (CourseActor actor in linkList) { + var destLinks = links.GetDestHashesFromSrc(actor.mHash); ImGui.TableNextRow(); - - ImGuiTreeNodeFlags node_flags = ImGuiTreeNodeFlags.FramePadding | ImGuiTreeNodeFlags.OpenOnArrow; - ImGui.PushID($"##{actor.mHash}"); string actorName = actor.mPackName; string name = actor.mName; @@ -1670,9 +1673,7 @@ private void RecursiveLinkFind(CourseArea area, CourseLinkHolder links, bool isSelected = editContext.IsSelected(actor); ImGui.TableSetColumnIndex(1); - ImGui.BeginDisabled(); - ImGui.Text(name); - ImGui.EndDisabled(); + ImGui.TextDisabled(name); bool expanded = false; bool isVisible = true; @@ -1681,18 +1682,17 @@ private void RecursiveLinkFind(CourseArea area, CourseLinkHolder links, Vector2 cp = ImGui.GetCursorScreenPos(); ImGui.TableSetColumnIndex(0); - if(links.GetDestHashesFromSrc(actor.mHash).Count > 0) - { - if (isSelected) - node_flags |= ImGuiTreeNodeFlags.Selected; - expanded = ImGui.TreeNodeEx($"{actor.mHash}", node_flags, actor.mPackName); - } + ImGuiTreeNodeFlags node_flags = ImGuiTreeNodeFlags.FramePadding | ImGuiTreeNodeFlags.OpenOnArrow; + + if (isSelected) + node_flags |= ImGuiTreeNodeFlags.Selected; + + if (!parentActors.ContainsValue(actor) && destLinks.Count > 0) + expanded = ImGui.TreeNodeEx($"{actorHash}", node_flags, actorName); else - { - expanded = ImGui.Selectable(actorName, isSelected, ImGuiSelectableFlags.SpanAllColumns); - } + expanded = ImGui.Selectable(actorName, isSelected); - if (ImGui.IsItemFocused()) + if (ImGui.IsItemClicked()) { activeViewport.SelectedActor(actor); } @@ -1700,39 +1700,47 @@ private void RecursiveLinkFind(CourseArea area, CourseLinkHolder links, if (ImGui.IsItemHovered() && ImGui.IsMouseDoubleClicked(0)) { activeViewport.FrameSelectedActor(actor); + selected ??= actor; + } + if (parentActors.Count == 0 && selected == actor) + { + ImGui.SetScrollHereY(); + selected = null; } if (!isVisible) ImGui.BeginDisabled(); - UpdateWonderVisibility(actor, links, area); + UpdateWonderVisibility(actor, destLinks, area); - if (expanded && (links.GetDestHashesFromSrc(actor.mHash).Count > 0)) + if (expanded) { - foreach (var link in links.GetDestHashesFromSrc(actor.mHash)) + if(!parentActors.ContainsValue(actor) && destLinks.Count > 0) { - ImGui.PushID($"##{link.Key}"); - if(ImGui.TreeNodeEx($"##{link.Key}", ImGuiTreeNodeFlags.FramePadding, link.Key)) + foreach (var link in destLinks) { - var reLinks = area.GetActors().Where(x => link.Value.Contains(x.mHash)); - RecursiveLinkFind(area, links, editContext, em, reLinks); - ImGui.TreePop(); + if(ImGui.TreeNodeEx($"{link.Key}##{actorHash}", ImGuiTreeNodeFlags.FramePadding, link.Key)) + { + var parents = new Hashtable(parentActors); + parents[actorHash] = actor; + var reLinks = area.GetActors().Where(x => link.Value.Contains(x.mHash)); + RecursiveLinkFind(area, links, editContext, em, reLinks, parents); + ImGui.TreePop(); + } } - ImGui.PopID(); - } - ImGui.TreePop(); + ImGui.TreePop(); + } } if (!isVisible) ImGui.EndDisabled(); - - ImGui.PopID(); } + parentActors.Clear(); } - private void UpdateWonderVisibility(CourseActor actor, CourseLinkHolder links, CourseArea area) + static void UpdateWonderVisibility(CourseActor actor, Dictionary> links, CourseArea area) { - foreach (var link in links.GetDestHashesFromSrc(actor.mHash)) + foreach (var link in links) { var reLinks = area.GetActors().Where(x => link.Value.Contains(x.mHash)); if (!link.Key.Contains("CreateRelative") && @@ -2655,7 +2663,7 @@ private void SelectLayerToAdd() fileteredLayers[i]; if(layer.Contains("(10/10)")) - ImGui.BeginDisabled(true); + ImGui.BeginDisabled(); ImGui.Selectable(layer); From a71d1029894b0d3000510eef6231032d3d229f98 Mon Sep 17 00:00:00 2001 From: Donavin Draws <51259260+DonavinDraws@users.noreply.github.com> Date: Sat, 2 Mar 2024 01:37:33 -0600 Subject: [PATCH 17/19] Feedback Optimizations & Undoable Layer Actions --- .../distance_view/DistantViewManager.cs | 11 +- Fushigi/ui/widgets/CourseScene.cs | 167 +++++++++++++----- Fushigi/ui/widgets/LevelViewport.cs | 2 +- 3 files changed, 132 insertions(+), 48 deletions(-) diff --git a/Fushigi/course/distance_view/DistantViewManager.cs b/Fushigi/course/distance_view/DistantViewManager.cs index 293cdde3..2fbba395 100644 --- a/Fushigi/course/distance_view/DistantViewManager.cs +++ b/Fushigi/course/distance_view/DistantViewManager.cs @@ -12,9 +12,7 @@ namespace Fushigi.course.distance_view public class DistantViewManager { private Dictionary LayerMatrices = new Dictionary(); - - private DVLayerParamTable LvlParamTable = new DVLayerParamTable(); - public static DVLayerParamTable ParamTable = new DVLayerParamTable(); + public DVLayerParamTable ParamTable = new DVLayerParamTable(); private CourseActor DVLocator; @@ -29,7 +27,6 @@ public DistantViewManager(CourseArea area) public void PrepareDVLocator(CourseArea area) { ParamTable.LoadDefault(); - LvlParamTable = ParamTable; foreach (var actor in area.GetActors()) { @@ -45,13 +42,13 @@ public void PrepareDVLocator(CourseArea area) { string layer_param = (string)DVLocator.mActorParameters["DVLayerParamName"]; if (!string.IsNullOrEmpty(layer_param)) - LvlParamTable.Load(layer_param); + ParamTable.Load(layer_param); } } } LayerMatrices.Clear(); - foreach (var layer in this.LvlParamTable.Layers) + foreach (var layer in this.ParamTable.Layers) LayerMatrices.Add(layer.Key, Matrix4x4.Identity); } @@ -63,7 +60,7 @@ public void UpdateMatrix(string layer, ref Matrix4x4 matrix) public void Calc(Vector3 camera_pos) { - foreach (var layer in this.LvlParamTable.Layers.Keys) + foreach (var layer in this.ParamTable.Layers.Keys) { var scroll_config = ParamTable.Layers[layer]; var locator_pos = DVLocator != null ? DVLocator.mTranslation : Vector3.Zero; diff --git a/Fushigi/ui/widgets/CourseScene.cs b/Fushigi/ui/widgets/CourseScene.cs index 5e9f9f21..fc51db4c 100644 --- a/Fushigi/ui/widgets/CourseScene.cs +++ b/Fushigi/ui/widgets/CourseScene.cs @@ -85,6 +85,69 @@ class CourseScene "EventGuest_11", ]; + public static string[] layerTypes = [ + "DvScreen", + "DvNear2", + "DvNear1", + "DecoAreaFront", + "PlayArea", + "DecoArea", + "DvMiddle1", + "DvMiddle2", + "DvFar1", + "DvFar2", + "DvFar3", + "DvFar4", + "DvFar5", + "DvFar6", + "DvFar7", + "DvFar8", + "DvFar9", + "DvFar10" + ]; + public static Regex RxLetters = new(@"\d+"); + + // This code sorts the layer order on the layer panel. + // You can look through it before deciding if it's optimized enough to include. + // Just uncomment all of this if it is. + // public static List layerSortTypes = [ + // "DvScreen", + // "DvNear", + // "DecoAreaFront", + // "PlayArea", + // "DvPlayArea", + // "DecoArea", + // "DvMiddle", + // "DvFar" + // ]; + + // public class LayerSorter : IComparer + // { + // public int Compare(string x, string y) + // { + // var idX = layerSortTypes.IndexOf(RxLetters.Replace(x, "")); + // var idY = layerSortTypes.IndexOf(RxLetters.Replace(y, "")); + // if(idX != -1) + // { + // int result = idY == -1 ? 1:idX.CompareTo(idY); + // if (result != 0) + // { + // return result; + // } + // else + // { + // result = x.Length.CompareTo(x.Length); + // return result != 0 ? result:x.CompareTo(y); + // } + // } + // else + // { + // return idY != -1 ? -1:0; + // } + // } + // } + // readonly LayerSorter layerSort = new(); + public static async Task Create(Course course, GLTaskScheduler glScheduler, IPopupModalHost popupModalHost, @@ -1182,14 +1245,14 @@ void ProcessRail(BGUnitRail rail) ImGui.TableNextColumn(); ImGui.Checkbox("##Curved", ref mSelectedRailPoint.mIsCurve); ImGui.SameLine(); - if (!mSelectedRailPoint.mIsCurve) - ImGui.BeginDisabled(); + + ImGui.BeginDisabled(!mSelectedRailPoint.mIsCurve); + ImGui.PushItemWidth(ImGui.GetColumnWidth() - ImGui.GetStyle().ScrollbarSize); ImGui.DragFloat3("##Control", ref mSelectedRailPoint.mControl.mTranslate, 0.25f); ImGui.PopItemWidth(); - if (!mSelectedRailPoint.mIsCurve) - ImGui.EndDisabled(); + ImGui.EndDisabled(); ImGui.EndTable(); } @@ -1708,8 +1771,7 @@ private void RecursiveLinkFind(CourseArea area, CourseLinkHolder links, selected = null; } - if (!isVisible) - ImGui.BeginDisabled(); + ImGui.BeginDisabled(!isVisible); UpdateWonderVisibility(actor, destLinks, area); @@ -1732,8 +1794,7 @@ private void RecursiveLinkFind(CourseArea area, CourseLinkHolder links, } } - if (!isVisible) - ImGui.EndDisabled(); + ImGui.EndDisabled(); } parentActors.Clear(); } @@ -1846,9 +1907,11 @@ private void CourseActorsLayerView(CourseActorHolder actorArray) ImGui.PushClipRect(wcMin, wcMax - new Vector2(margin, 0), true); bool isSearch = !string.IsNullOrWhiteSpace(mActorSearchText); + //var sortedLayers = mLayersVisibility.Keys.ToList(); + //sortedLayers.Sort(layerSort); ImGui.Spacing(); - foreach (string layer in mLayersVisibility.Keys) + foreach (string layer in mLayersVisibility.Keys) //Use sortedLayers if you think the sorting code is good { ImGui.PushID(layer); cp = ImGui.GetCursorScreenPos(); @@ -1880,8 +1943,7 @@ private void CourseActorsLayerView(CourseActorHolder actorArray) _ = DeleteLayerWithWarningPrompt(layer, actorArray, editContext); ImGui.PopClipRect(); - if (!isVisible) - ImGui.BeginDisabled(); + ImGui.BeginDisabled(!isVisible); if (expanded || isSearch) { @@ -1942,8 +2004,7 @@ private void CourseActorsLayerView(CourseActorHolder actorArray) ImGui.TreePop(); } - if (!isVisible) - ImGui.EndDisabled(); + ImGui.EndDisabled(); ImGui.PopID(); } @@ -2410,15 +2471,7 @@ private async Task DeleteLayerWithWarningPrompt(string layer, CourseActorHolder actorArray, CourseAreaEditContext ctx) { var actors = actorArray.mActors.FindAll(x => x.mLayer == layer); - var confirm = await OperationWarningDialog.ShowDialog(mPopupModalHost, - "Deletion warning", - "Are you sure you want to delete " + - $"{layer}"); - - if (confirm == OperationWarningDialog.DialogResult.Cancel) - return; - - bool noWarnings = !actors.Any(); + bool noWarnings = !(actors.Count > 0); if (!noWarnings) { @@ -2433,12 +2486,39 @@ private async Task DeleteLayerWithWarningPrompt(string layer, var result = await OperationWarningDialog.ShowDialog(mPopupModalHost, "Deletion warning", - "The following actors will be deleted", + "Deleting " + layer + + " will delete the following actors", ("Actors", warningActors)); if (result == OperationWarningDialog.DialogResult.Cancel) return; } + else + { + var result = await OperationWarningDialog.ShowDialog(mPopupModalHost, + "Deletion warning", + "Are you sure you want to delete " + + layer+"?"); + + if (result == OperationWarningDialog.DialogResult.Cancel) + return; + } + + var batchAction = ctx.BeginBatchAction(); + + foreach (var actor in actors) + { + ctx.DeleteActor(actor); + } + ctx.CommitAction(new PropertyFieldsSetUndo( + this, + [("mLayersVisibility", new Dictionary(mLayersVisibility))], + $"{IconUtil.ICON_TRASH} Delete {layer}" + ) + ); + mLayersVisibility.Remove(layer); + + batchAction.Commit($"{IconUtil.ICON_TRASH} Delete Layer: {layer}"); } private async Task AddActorsWithSelectActorAndLayerWindow() @@ -2507,14 +2587,22 @@ private async Task AddLayerWithLayerWindow() if(layer == "PlayArea" || layer == "DecoArea") { - var i = layer == "DecoArea" ? 0:1; - do - { - i++; - } while (mLayersVisibility.Keys.Any(x => x == $"{layer}{i}")); - layer += i; + int startIdx = layer == "DecoArea" ? 0:1; + for (int i = startIdx; /*no condition*/; i++) + { + if (!mLayersVisibility.ContainsKey($"{layer}{i}")) + { + layer += i; + break; + } + } } - + ctx.CommitAction(new PropertyFieldsSetUndo( + this, + [("mLayersVisibility", new Dictionary(mLayersVisibility))], + $"{IconUtil.ICON_LAYER_GROUP} Added Layer: {layer}" + ) + ); mLayersVisibility[layer] = true; } @@ -2638,10 +2726,12 @@ private void SelectActorToAddLayer() // TODO, maybe find a way to combine with SelectActorToAddLayer(), if that's needed private void SelectLayerToAdd() { + const int MaxLayerCount = 10; + int layerCount = 0; + ImGui.InputText("Search", ref mAddLayerSearchQuery, 256); - string[] Layers = ["PlayArea", "DecoArea", "DecoAreaFront"]; - Layers = Layers.Concat(DistantViewManager.ParamTable.Layers.Keys) + string[] Layers = layerTypes .Except(mLayersVisibility.Keys) .ToArray(); var fileteredLayers = Layers.ToImmutableList(); @@ -2658,22 +2748,19 @@ private void SelectLayerToAdd() { for (var i = 0; i < fileteredLayers.Count; i++) { - var layer = i < 2 ? - fileteredLayers[i]+$" ({mLayersVisibility.Count(x => Regex.Replace(x.Key, @"\\d+", "").Contains(fileteredLayers[i]))}/10)": - fileteredLayers[i]; + layerCount = mLayersVisibility.Count(x => RxLetters.Replace(x.Key, "") == fileteredLayers[i]); + var layer = i < 2 ? $"{fileteredLayers[i]} ({layerCount}/{MaxLayerCount})" : fileteredLayers[i]; - if(layer.Contains("(10/10)")) - ImGui.BeginDisabled(); + ImGui.BeginDisabled(layerCount == MaxLayerCount); ImGui.Selectable(layer); if (ImGui.IsItemHovered() && ImGui.IsMouseDoubleClicked(0)) { - mSelectedLayer = Layers[i]; + mSelectedLayer = fileteredLayers[i]; } - if(layer.Contains("(10/10)")) - ImGui.EndDisabled(); + ImGui.EndDisabled(); } ImGui.EndListBox(); diff --git a/Fushigi/ui/widgets/LevelViewport.cs b/Fushigi/ui/widgets/LevelViewport.cs index 6454efb9..d6fa756c 100644 --- a/Fushigi/ui/widgets/LevelViewport.cs +++ b/Fushigi/ui/widgets/LevelViewport.cs @@ -362,7 +362,7 @@ TileBfresRender CreateTileRendererForSkin(SkinDivision division, string skinName //Display skybox EnvironmentData.RenderSky(gl, this.Camera); - foreach (var actor in this.mArea.GetActors()) + foreach (var actor in this.mArea.GetActors().OrderBy(x => x.mTranslation.Z)) { actor.wonderVisible = WonderViewMode == actor.mWonderView || WonderViewMode == WonderViewType.Normal || From 8284aafc55034d034a0be1dffc28087ca337d3b1 Mon Sep 17 00:00:00 2001 From: Donavin Draws <51259260+DonavinDraws@users.noreply.github.com> Date: Sat, 2 Mar 2024 12:17:46 -0600 Subject: [PATCH 18/19] Layer Adding Fix and Optimization * Further improved the code of the add layer window Thank you JuPaHe64 for helping with this. --- Fushigi/ui/widgets/CourseScene.cs | 13 ++++++++----- Fushigi/ui/widgets/LevelViewport.cs | 2 ++ 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/Fushigi/ui/widgets/CourseScene.cs b/Fushigi/ui/widgets/CourseScene.cs index fc51db4c..59c22c10 100644 --- a/Fushigi/ui/widgets/CourseScene.cs +++ b/Fushigi/ui/widgets/CourseScene.cs @@ -105,7 +105,7 @@ class CourseScene "DvFar9", "DvFar10" ]; - public static Regex RxLetters = new(@"\d+"); + public static Regex NumberRegex = new(@"\d+"); // This code sorts the layer order on the layer panel. // You can look through it before deciding if it's optimized enough to include. @@ -125,8 +125,8 @@ class CourseScene // { // public int Compare(string x, string y) // { - // var idX = layerSortTypes.IndexOf(RxLetters.Replace(x, "")); - // var idY = layerSortTypes.IndexOf(RxLetters.Replace(y, "")); + // var idX = layerSortTypes.IndexOf(NumberRegex.Replace(x, "")); + // var idY = layerSortTypes.IndexOf(NumberRegex.Replace(y, "")); // if(idX != -1) // { // int result = idY == -1 ? 1:idX.CompareTo(idY); @@ -2748,8 +2748,11 @@ private void SelectLayerToAdd() { for (var i = 0; i < fileteredLayers.Count; i++) { - layerCount = mLayersVisibility.Count(x => RxLetters.Replace(x.Key, "") == fileteredLayers[i]); - var layer = i < 2 ? $"{fileteredLayers[i]} ({layerCount}/{MaxLayerCount})" : fileteredLayers[i]; + var layer = fileteredLayers[i]; + layerCount = mLayersVisibility.Keys + .Count(x => x.StartsWith(layer) && NumberRegex.IsMatch(x.AsSpan(layer.Length..))); + if (layer == "PlayArea" || layer == "DecoArea") + layer += $" ({layerCount}/{MaxLayerCount})"; ImGui.BeginDisabled(layerCount == MaxLayerCount); diff --git a/Fushigi/ui/widgets/LevelViewport.cs b/Fushigi/ui/widgets/LevelViewport.cs index d6fa756c..1b7d01a6 100644 --- a/Fushigi/ui/widgets/LevelViewport.cs +++ b/Fushigi/ui/widgets/LevelViewport.cs @@ -362,6 +362,8 @@ TileBfresRender CreateTileRendererForSkin(SkinDivision division, string skinName //Display skybox EnvironmentData.RenderSky(gl, this.Camera); + // Actors are listed in the order they were pulled from the yaml. + // So they are ordered by depth for rendering. foreach (var actor in this.mArea.GetActors().OrderBy(x => x.mTranslation.Z)) { actor.wonderVisible = WonderViewMode == actor.mWonderView || From 64b11c969b676f8d4a00c8cfc96f5de11dc9cd38 Mon Sep 17 00:00:00 2001 From: jupahe64 Date: Sat, 9 Mar 2024 14:42:06 +0100 Subject: [PATCH 19/19] support inheritance for ModelExpandParam and fix GoalPole visuals --- Fushigi/actor_pack/ActorPack.cs | 5 +++++ .../actor_pack/components/ModelExpandParam.cs | 15 +++++++++++++++ Fushigi/ui/widgets/LevelViewport.cs | 17 ++++++++++------- 3 files changed, 30 insertions(+), 7 deletions(-) diff --git a/Fushigi/actor_pack/ActorPack.cs b/Fushigi/actor_pack/ActorPack.cs index c1a4a1b6..374786c0 100644 --- a/Fushigi/actor_pack/ActorPack.cs +++ b/Fushigi/actor_pack/ActorPack.cs @@ -190,6 +190,11 @@ private void LoadComponents(SARC.SARC sarc, ActorParam param) break; case "ModelExpandRef": this.ModelExpandParamRef ??= BymlSerialize.Deserialize(data); + this.ModelExpandParamRef.LoadParentIfExists(filePath => + { + filePath = GetPathGyml(filePath); + return BymlSerialize.Deserialize(sarc.OpenFile(filePath)); + }); break; case "DrainPipeRef": this.DrainPipeRef ??= BymlSerialize.Deserialize(data); diff --git a/Fushigi/actor_pack/components/ModelExpandParam.cs b/Fushigi/actor_pack/components/ModelExpandParam.cs index d0a3d194..f5be8bae 100644 --- a/Fushigi/actor_pack/components/ModelExpandParam.cs +++ b/Fushigi/actor_pack/components/ModelExpandParam.cs @@ -1,4 +1,5 @@ using Fushigi.Byml.Serializer; +using Fushigi.SARC; using System; using System.Collections.Generic; using System.Linq; @@ -11,6 +12,20 @@ namespace Fushigi.actor_pack.components [Serializable] public class ModelExpandParam { + public void LoadParentIfExists(Func fileLoader) + { + if (ParentRef == null) + return; + + Parent = fileLoader(ParentRef); + Parent.LoadParentIfExists(fileLoader); + } + + [BymlProperty(Key = "$parent")] + public string? ParentRef { get; set; } + + public ModelExpandParam? Parent { get; set; } + public List Settings { get; set; } } diff --git a/Fushigi/ui/widgets/LevelViewport.cs b/Fushigi/ui/widgets/LevelViewport.cs index 1b7d01a6..5007e681 100644 --- a/Fushigi/ui/widgets/LevelViewport.cs +++ b/Fushigi/ui/widgets/LevelViewport.cs @@ -431,6 +431,7 @@ private void RenderActor(CourseActor actor, ModelInfo modelInfo) if(actor.mActorPack.ModelExpandParamRef != null) { ActorModelExpand(actor, model); + ActorModelExpand(actor, model, "Main"); //yeah idk either //TODO SubModels } @@ -510,15 +511,17 @@ private void ActorModelExpand(CourseActor actor, BfresRender.BfresModel model, s { //Model Expand Param - Debug.Assert(actor.mActorPack.ModelExpandParamRef.Settings.Count > 0); - - if (actor.mActorPack.ModelExpandParamRef.Settings.Count == 0) - return; - //TODO is that actually how the game does it? - var setting = actor.mActorPack.ModelExpandParamRef.Settings.FindLast(x=>x.mModelKeyName == modelKeyName); + var param = actor.mActorPack.ModelExpandParamRef; + ModelExpandParamSettings? setting = null; + do + { + if (param.Settings != null) + setting = param.Settings.FindLast(x => x.mModelKeyName == modelKeyName); + + param = param.Parent; + } while (setting == null && param != null); - //Debug.Assert(setting != null); if (setting == null) return;