From 5c13cbb011980da01c4511314cba21a1aff83b19 Mon Sep 17 00:00:00 2001 From: spherallic Date: Mon, 1 May 2023 20:08:25 +0200 Subject: [PATCH] Implement polyobject previews, clean up zoom tube & NiGHTS rendering --- Source/Core/Builder.csproj | 3 + Source/Core/Config/ProgramConfiguration.cs | 16 +- Source/Core/Properties/Resources.Designer.cs | 20 ++ Source/Core/Properties/Resources.resx | 6 + Source/Core/Rendering/IRenderer2D.cs | 2 +- Source/Core/Rendering/Renderer2D.cs | 290 +++++++++++++----- Source/Core/Resources/Actions.cfg | 25 +- Source/Core/Resources/polycenter.png | Bin 0 -> 399 bytes Source/Core/Resources/zoomtube.bmp | Bin 0 -> 3382 bytes Source/Core/Resources/zoomtube.png | Bin 0 -> 314 bytes Source/Core/Windows/MainForm.Designer.cs | 92 +++++- Source/Core/Windows/MainForm.cs | 42 ++- .../BuilderModes/ClassicModes/BridgeMode.cs | 2 +- .../ClassicModes/CurveLinedefsMode.cs | 2 +- .../ClassicModes/DragLinedefsMode.cs | 4 +- .../ClassicModes/DragSectorsMode.cs | 4 +- .../ClassicModes/DragThingsMode.cs | 2 +- .../ClassicModes/DragVerticesMode.cs | 4 +- .../ClassicModes/DrawGeometryMode.cs | 2 +- .../ClassicModes/EditSelectionMode.cs | 2 +- .../ClassicModes/ErrorCheckMode.cs | 2 +- .../ClassicModes/FindReplaceMode.cs | 2 +- .../ClassicModes/FlatAlignMode.cs | 2 +- .../ClassicModes/InsertThingsRadiallyMode.cs | 2 +- .../BuilderModes/ClassicModes/LinedefsMode.cs | 2 +- .../ClassicModes/MakeSectorMode.cs | 2 +- .../ClassicModes/ParallelLinedefMode.cs | 2 +- .../ClassicModes/PerpendicularLinedefMode.cs | 2 +- .../ClassicModes/PerpendicularVertexMode.cs | 2 +- .../BuilderModes/ClassicModes/SectorsMode.cs | 2 +- .../BuilderModes/ClassicModes/ThingsMode.cs | 2 +- .../ClassicModes/VertexIntoShapeMode.cs | 2 +- .../ClassicModes/VertexSlopeAssistMode.cs | 2 +- .../BuilderModes/ClassicModes/VerticesMode.cs | 2 +- .../SoundEnvironmentMode.cs | 2 +- .../SoundPropagationMode.cs | 2 +- 36 files changed, 416 insertions(+), 134 deletions(-) create mode 100644 Source/Core/Resources/polycenter.png create mode 100644 Source/Core/Resources/zoomtube.bmp create mode 100644 Source/Core/Resources/zoomtube.png diff --git a/Source/Core/Builder.csproj b/Source/Core/Builder.csproj index f88e2fa..e432131 100644 --- a/Source/Core/Builder.csproj +++ b/Source/Core/Builder.csproj @@ -727,7 +727,10 @@ + + + diff --git a/Source/Core/Config/ProgramConfiguration.cs b/Source/Core/Config/ProgramConfiguration.cs index 6aa3463..61391bd 100644 --- a/Source/Core/Config/ProgramConfiguration.cs +++ b/Source/Core/Config/ProgramConfiguration.cs @@ -148,6 +148,8 @@ namespace CodeImp.DoomBuilder.Config private bool fixedthingsscale; //mxd private bool rendergrid; private bool rendernightspath; + private bool renderzoomtubes; + private bool renderpolypreview; private bool dynamicgridsize; private int ignoredremoterevision; private bool drawscreenshotinfo; @@ -283,7 +285,9 @@ namespace CodeImp.DoomBuilder.Config public bool FixedThingsScale { get { return fixedthingsscale; } internal set { fixedthingsscale = value; } } //mxd public bool RenderGrid { get { return rendergrid; } internal set { rendergrid = value; } } //mxd public bool RenderNiGHTSPath { get { return rendernightspath; } internal set { rendernightspath = value; } } - public bool DynamicGridSize { get { return dynamicgridsize; } internal set { dynamicgridsize = value; } } //mxd + public bool RenderZoomtubes { get { return renderzoomtubes; } internal set { renderzoomtubes = value; } } + public bool RenderPolyPreview { get { return renderpolypreview; } internal set { renderpolypreview = value; } } + public bool DynamicGridSize { get { return dynamicgridsize; } internal set { dynamicgridsize = value; } } //mxd internal int IgnoredRemoteRevision { get { return ignoredremoterevision; } set { ignoredremoterevision = value; } } //mxd public bool DrawScreenshotInfo { get { return drawscreenshotinfo; } set { drawscreenshotinfo = value; } } public bool CompressScreenshots { get { return compressscreenshots; } set { compressscreenshots = value; } } @@ -426,7 +430,9 @@ namespace CodeImp.DoomBuilder.Config fixedthingsscale = cfg.ReadSetting("fixedthingsscale", false); //mxd rendergrid = cfg.ReadSetting("rendergrid", true); //mxd rendernightspath = cfg.ReadSetting("rendernightspath", true); - dynamicgridsize = cfg.ReadSetting("dynamicgridsize", true); //mxd + renderzoomtubes = cfg.ReadSetting("renderzoomtubes", true); + renderpolypreview = cfg.ReadSetting("renderpolypreview", true); + dynamicgridsize = cfg.ReadSetting("dynamicgridsize", true); //mxd ignoredremoterevision = cfg.ReadSetting("ignoredremoterevision", 0); //mxd drawscreenshotinfo = cfg.ReadSetting("drawscreenshotinfo", true); compressscreenshots = cfg.ReadSetting("compressscreenshots", true); @@ -552,8 +558,10 @@ namespace CodeImp.DoomBuilder.Config cfg.WriteSetting("rendercomments", rendercomments); //mxd cfg.WriteSetting("fixedthingsscale", fixedthingsscale); //mxd cfg.WriteSetting("rendergrid", rendergrid); //mxd - cfg.WriteSetting("rendernightspath", rendernightspath); //mxd - cfg.WriteSetting("dynamicgridsize", dynamicgridsize); //mxd + cfg.WriteSetting("rendernightspath", rendernightspath); + cfg.WriteSetting("renderzoomtubes", renderzoomtubes); + cfg.WriteSetting("renderpolypreview", renderpolypreview); + cfg.WriteSetting("dynamicgridsize", dynamicgridsize); //mxd cfg.WriteSetting("ignoredremoterevision", ignoredremoterevision); //mxd cfg.WriteSetting("drawscreenshotinfo", drawscreenshotinfo); cfg.WriteSetting("compressscreenshots", compressscreenshots); diff --git a/Source/Core/Properties/Resources.Designer.cs b/Source/Core/Properties/Resources.Designer.cs index 8fe19f6..7ba22cf 100644 --- a/Source/Core/Properties/Resources.Designer.cs +++ b/Source/Core/Properties/Resources.Designer.cs @@ -980,6 +980,16 @@ namespace CodeImp.DoomBuilder.Properties { } } + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap polycenter { + get { + object obj = ResourceManager.GetObject("polycenter", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + /// /// Looks up a localized resource of type System.Drawing.Bitmap. /// @@ -1689,5 +1699,15 @@ namespace CodeImp.DoomBuilder.Properties { return ((System.Drawing.Bitmap)(obj)); } } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap zoomtube { + get { + object obj = ResourceManager.GetObject("zoomtube", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } } } diff --git a/Source/Core/Properties/Resources.resx b/Source/Core/Properties/Resources.resx index 03f3cd6..d76da34 100644 --- a/Source/Core/Properties/Resources.resx +++ b/Source/Core/Properties/Resources.resx @@ -607,4 +607,10 @@ ..\Resources\Snap1mp.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + ..\Resources\polycenter.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\zoomtube.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + \ No newline at end of file diff --git a/Source/Core/Rendering/IRenderer2D.cs b/Source/Core/Rendering/IRenderer2D.cs index 60f1f88..cf73eef 100644 --- a/Source/Core/Rendering/IRenderer2D.cs +++ b/Source/Core/Rendering/IRenderer2D.cs @@ -68,7 +68,7 @@ namespace CodeImp.DoomBuilder.Rendering void PlotVerticesSet(ICollection vertices); void RenderThing(Thing t, PixelColor c, float alpha); void RenderThingSet(ICollection things, float alpha); - void RenderNiGHTSPath(); + void RenderSRB2Extras(); void RenderRectangle(RectangleF rect, float bordersize, PixelColor c, bool transformrect); void RenderRectangleFilled(RectangleF rect, PixelColor c, bool transformrect); void RenderRectangleFilled(RectangleF rect, PixelColor c, bool transformrect, ImageData texture); diff --git a/Source/Core/Rendering/Renderer2D.cs b/Source/Core/Rendering/Renderer2D.cs index f51d82f..992b562 100644 --- a/Source/Core/Rendering/Renderer2D.cs +++ b/Source/Core/Rendering/Renderer2D.cs @@ -28,6 +28,8 @@ using CodeImp.DoomBuilder.Editing; using CodeImp.DoomBuilder.GZBuilder.Data; //mxd using CodeImp.DoomBuilder.GZBuilder.Geometry; //mxd using CodeImp.DoomBuilder.Config; //mxd +using static System.Windows.Forms.VisualStyles.VisualStyleElement; + #endregion @@ -113,7 +115,18 @@ namespace CodeImp.DoomBuilder.Rendering // Presentation private Presentation present; - + + // SRB2 stuff + private List axes; + private List axistransferlines; + private List waypoints; + private List polyanchors; + private List polyspawns; + private List firstlines; + + private TextLabel spawnlabel; + private TextLabel anchorlabel; + #endregion #region ================== Properties @@ -140,7 +153,27 @@ namespace CodeImp.DoomBuilder.Rendering // Create rendertargets CreateRendertargets(); - + + anchorlabel = new TextLabel() // create sequence ID label + { + Text = "0", + AlignX = TextAlignmentX.Center, + AlignY = TextAlignmentY.Middle, + Color = General.Colors.GetNiGHTSColor(8), + BackColor = General.Colors.Background, + TransformCoords = true + }; + + spawnlabel = new TextLabel() // create sequence ID label + { + Text = "0", + AlignX = TextAlignmentX.Center, + AlignY = TextAlignmentY.Middle, + Color = General.Colors.GetNiGHTSColor(7), + BackColor = General.Colors.Background, + TransformCoords = true + }; + // We have no destructor GC.SuppressFinalize(this); } @@ -1495,102 +1528,199 @@ namespace CodeImp.DoomBuilder.Rendering } - public void RenderNiGHTSPath() + public void RenderSRB2Extras() { - if (!General.Settings.RenderNiGHTSPath) return; + if (!(General.Settings.RenderNiGHTSPath || General.Settings.RenderZoomtubes || General.Settings.RenderPolyPreview)) return; ICollection things = General.Map.Map.Things; - List axes = new List(); - List axistransferlines = new List(); - List waypoints = new List(); + axes = new List(); + axistransferlines = new List(); + waypoints = new List(); + polyanchors = new List(); + polyspawns = new List(); + firstlines = new List(); + + // Collect relevant things foreach (Thing t in things) { int type = t.Type; - if (type == General.Map.FormatInterface.AxisType) axes.Add(t); - if (type == General.Map.FormatInterface.AxisTransferLineType) axistransferlines.Add(t); - if (type == General.Map.FormatInterface.WaypointType) waypoints.Add(t); + if (General.Settings.RenderNiGHTSPath && type == General.Map.FormatInterface.AxisType) + axes.Add(t); + if (General.Settings.RenderNiGHTSPath && type == General.Map.FormatInterface.AxisTransferLineType) + axistransferlines.Add(t); + if (General.Settings.RenderZoomtubes && type == General.Map.FormatInterface.WaypointType) + waypoints.Add(t); + if (General.Settings.RenderPolyPreview && type == 760) + polyanchors.Add(t); + if (General.Settings.RenderPolyPreview && (type == 761 || type == 762)) + polyspawns.Add(t); } + //Sort waypoints by angle - waypoints.Sort((x,y) => (x.AngleDoom.CompareTo(y.AngleDoom))); - //Sort by axis number and mare number. - axistransferlines.Sort((x, y) => (x.GetFlagsValue() | (x.Parameter)<<16).CompareTo((y.GetFlagsValue() | (y.Parameter) << 16))); + waypoints.Sort((x, y) => (x.AngleDoom.CompareTo(y.AngleDoom))); + + // Sort polyobject stuff by "angle"/tag + polyanchors.Sort((x, y) => (x.AngleDoom.CompareTo(y.AngleDoom))); + polyspawns.Sort((x, y) => (x.AngleDoom.CompareTo(y.AngleDoom))); + + //Sort by axis number and mare number. + axistransferlines.Sort((x, y) => (x.GetFlagsValue() | (x.Parameter) << 16).CompareTo((y.GetFlagsValue() | (y.Parameter) << 16))); + + // Collect relevant lines + if (General.Settings.RenderPolyPreview) + { + foreach (Linedef l in General.Map.Map.Linedefs) + { + if (l.Action == 20) firstlines.Add(l); + } + + //Sort polyobject first lines by tag + firstlines.Sort((x, y) => (x.Tag.CompareTo(y.Tag))); + } //Render (zoom tube) waypoint sequences. - int i = 0; - int size = waypoints.Count; - int seqStart = 0; - ITextLabel[] sequencelabels = new ITextLabel[256]; - while (i < size) + if (General.Settings.RenderZoomtubes) { - int iNext = i + 1; - if (waypoints[i].AngleDoom % 256 == 0) // start of a new sequence? + int i = 0; + int size = waypoints.Count; + int seqStart = 0; + ITextLabel[] sequencelabels = new ITextLabel[256]; + while (i < size) { - seqStart = i; - sequencelabels[waypoints[i].AngleDoom / 256] = new TextLabel() // create sequence ID label + int iNext = i + 1; + if (waypoints[i].AngleDoom % 256 == 0) // start of a new sequence? { - Text = (waypoints[i].AngleDoom / 256).ToString(), - AlignX = TextAlignmentX.Center, - AlignY = TextAlignmentY.Middle, - Color = General.Colors.WaypointColor, - TransformCoords = true, - Location = waypoints[i].Position - }; + seqStart = i; + sequencelabels[waypoints[i].AngleDoom / 256] = new TextLabel() // create sequence ID label + { + Text = (waypoints[i].AngleDoom / 256).ToString(), + AlignX = TextAlignmentX.Center, + AlignY = TextAlignmentY.Middle, + Color = General.Colors.WaypointColor, + TransformCoords = true, + Location = waypoints[i].Position + }; + } + if (iNext < size && waypoints[iNext].AngleDoom == waypoints[i].AngleDoom + 1) + { + // draw line between this waypoint and the next + RenderLine(waypoints[i].Position, waypoints[iNext].Position, 1.5f, General.Colors.WaypointColor, true); + } + else if (iNext < size && waypoints[iNext].AngleDoom == waypoints[i].AngleDoom) + { + // mark duplicate waypoints + RenderCircle(waypoints[i].Position, 32f, 1f, PixelColor.FromColor(Color.Red), true); + RenderCircle(waypoints[iNext].Position, 32f, 1f, PixelColor.FromColor(Color.Red), true); + } + else if (iNext < size && i > 0 && waypoints[i].AngleDoom - waypoints[i - 1].AngleDoom > 1 && waypoints[iNext].AngleDoom > waypoints[seqStart].AngleDoom + 255) + { + // mark inaccessible waypoints + RenderCircle(waypoints[i].Position, 32f, 1f, PixelColor.FromColor(Color.Red), true); + } + else + { + // draw different line between last and first waypoint of this sequence + RenderLine(waypoints[i].Position, waypoints[seqStart].Position, 0.75f, General.Colors.WaypointLoopColor, true); + } + i = iNext; } - if (iNext < size && waypoints[iNext].AngleDoom == waypoints[i].AngleDoom + 1) - { - // draw line between this waypoint and the next - RenderLine(waypoints[i].Position, waypoints[iNext].Position, 1.5f, General.Colors.WaypointColor, true); - } - else if (iNext < size && waypoints[iNext].AngleDoom == waypoints[i].AngleDoom) - { - // mark duplicate waypoints - RenderCircle(waypoints[i].Position, 32f, 1f, PixelColor.FromColor(Color.Red), true); - RenderCircle(waypoints[iNext].Position, 32f, 1f, PixelColor.FromColor(Color.Red), true); - } - else if (iNext < size && i > 0 && waypoints[i].AngleDoom - waypoints[i - 1].AngleDoom > 1 && waypoints[iNext].AngleDoom > waypoints[seqStart].AngleDoom + 255) - { - // mark inaccessible waypoints - RenderCircle(waypoints[i].Position, 32f, 1f, PixelColor.FromColor(Color.Red), true); - } - else - { - // draw different line between last and first waypoint of this sequence - RenderLine(waypoints[i].Position, waypoints[seqStart].Position, 0.75f, General.Colors.WaypointLoopColor, true); - } - i = iNext; + + for (i = 0; i < 256; i++) + if (sequencelabels[i] != null) RenderText(sequencelabels[i]); } - for (i = 0; i < 256; i++) - if (sequencelabels[i] != null) RenderText(sequencelabels[i]); - //Render axis transfer lines. - i = 0; - size = axistransferlines.Count; - while (i < size - 1) - { - int iNext = i; - while (iNext < size - 1 && axistransferlines[++iNext].GetFlagsValue() <= axistransferlines[i].GetFlagsValue()) ; + if (General.Settings.RenderNiGHTSPath) + { + int i = 0; + int size = axistransferlines.Count; + while (i < size - 1) + { + int iNext = i; + while (iNext < size - 1 && axistransferlines[++iNext].GetFlagsValue() <= axistransferlines[i].GetFlagsValue()) ; + + if (iNext < size && axistransferlines[iNext].GetFlagsValue() == axistransferlines[i].GetFlagsValue() + 1) + { + int mare = axistransferlines[i].Parameter; + RenderLine(axistransferlines[i].Position, axistransferlines[iNext].Position, 1f, General.Colors.GetNiGHTSColor(mare), true); + /* Start looking for partners for the one beyond iNext. */ + i = iNext + 1; + } + else + { + /* No partner, so start looking for partners for iNext. */ + i = iNext; + } + } + //Render axes. + foreach (Thing axis in axes) + { + int mare = axis.Parameter; + RenderCircle(axis.Position, (float)(axis.AngleDoom & 0x3FFF), 1f, General.Colors.GetNiGHTSColor(mare), true); + } + } + + if (General.Settings.RenderPolyPreview) + { + int i = 0, j = 0, k = 0; + Sector s = null; + while (i < polyanchors.Count && j < polyspawns.Count && k < firstlines.Count) + { + while (j + 1 < polyspawns.Count && polyanchors[i].AngleDoom > polyspawns[j].AngleDoom) j++; + while (k + 1 < firstlines.Count && polyanchors[i].AngleDoom > firstlines[k].Tag) k++; + + if (polyanchors[i].AngleDoom == firstlines[k].Tag) + s = firstlines[k].Back.Sector; + else + s = null; + + if (polyanchors[i].AngleDoom == polyspawns[j].AngleDoom && s != null) + { + while (j + 1 < polyspawns.Count && polyspawns[j].AngleDoom == polyspawns[j + 1].AngleDoom) + { + // Mark redundant spawnpoints + spawnlabel.Text = polyspawns[j].AngleDoom.ToString(); + spawnlabel.Location = polyspawns[j].Position; + spawnlabel.Color = PixelColor.FromColor(Color.Red); + RenderText(spawnlabel); + j++; + } + + float xdiff = polyanchors[i].Position.x - polyspawns[j].Position.x; + float ydiff = polyanchors[i].Position.y - polyspawns[j].Position.y; + + foreach (Sidedef side in s.Sidedefs) + { + Vector2D start = side.Line.Start.Position; + Vector2D end = side.Line.End.Position; + start.x -= xdiff; + start.y -= ydiff; + end.x -= xdiff; + end.y -= ydiff; + RenderLine(start, end, 1.0f, General.Colors.GetNiGHTSColor(7), true); + } + anchorlabel.Color = General.Colors.GetNiGHTSColor(8); + spawnlabel.Color = General.Colors.GetNiGHTSColor(7); + + spawnlabel.Text = polyspawns[j].AngleDoom.ToString(); + spawnlabel.Location = polyspawns[j].Position; + RenderText(spawnlabel); + } + else + { + // Mark unused polyobject anchors + anchorlabel.Color = PixelColor.FromColor(Color.Red); + } + + anchorlabel.Text = polyanchors[i].AngleDoom.ToString(); + anchorlabel.Location = polyanchors[i].Position; + RenderText(anchorlabel); + + i++; + } + } + } - if (iNext < size && axistransferlines[iNext].GetFlagsValue() == axistransferlines[i].GetFlagsValue() + 1) - { - int mare = axistransferlines[i].Parameter; - RenderLine(axistransferlines[i].Position, axistransferlines[iNext].Position, 1f, General.Colors.GetNiGHTSColor(mare), true); - /* Start looking for partners for the one beyond iNext. */ - i = iNext + 1; - } - else - { - /* No partner, so start looking for partners for iNext. */ - i = iNext; - } - } - //Render axes. - foreach (Thing axis in axes) - { - int mare = axis.Parameter; - RenderCircle(axis.Position, (float)(axis.AngleDoom & 0x3FFF), 1f, General.Colors.GetNiGHTSColor(mare), true); - } - } #endregion #region ================== Surface diff --git a/Source/Core/Resources/Actions.cfg b/Source/Core/Resources/Actions.cfg index 436be87..b4fff5b 100644 --- a/Source/Core/Resources/Actions.cfg +++ b/Source/Core/Resources/Actions.cfg @@ -405,12 +405,31 @@ togglegrid default = 262215; //Alt-G } -//mxd togglenightspath { - title = "Toggle Waypoint and NiGHTS Paths"; + title = "Toggle NiGHTS Paths"; category = "classic"; - description = "Toggles (zoom tube) waypoint and NiGHTS path rendering in classic modes."; + description = "Toggles NiGHTS path rendering in classic modes."; + allowkeys = true; + allowmouse = false; + allowscroll = false; +} + +togglezoomtubes +{ + title = "Toggle Zoom Tube Waypoint Paths"; + category = "classic"; + description = "Toggles zoom tube waypoint path rendering in classic modes."; + allowkeys = true; + allowmouse = false; + allowscroll = false; +} + +togglepolypreview +{ + title = "Toggle Polyobject Preview"; + category = "classic"; + description = "Toggles polyobject previews in classic modes."; allowkeys = true; allowmouse = false; allowscroll = false; diff --git a/Source/Core/Resources/polycenter.png b/Source/Core/Resources/polycenter.png new file mode 100644 index 0000000000000000000000000000000000000000..0efebf628f55f9814ede7a1430b25498fdfb8993 GIT binary patch literal 399 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE3?yBabR7dyjKx9jP7LeL$-D$|SkfJR9T^xl z_H+M9WCik70(?STfwZ)AiLUM%dyg}F>}3DftN)+Q|NkW4|4$506IK4N)Bq}2FlFlp zAjMM>=PdAuEM{O3QwCwilYt4@K*2knE{-7<{=JvDg$^n3uw2-( z@XO)9?_V!V^lo@1;qJZ3|5eD6FRGUp#O-yNma4FEb44usrE7BCJT2PCw!P|zkA4%Y z@a^XDFa>YbPeReQEc#E9b4rETSFbu}ov=eF;;~CWk7%^O6~~D!N$KAe9+Y3&_H>TT zF&(B!e-msN-|UZEF)LX1OalAKF?_l_oDP*TW0r{p6? e&z072O?km7l94+y#2*0t$>8bg=d#Wzp$Pz!U!N)f literal 0 HcmV?d00001 diff --git a/Source/Core/Resources/zoomtube.bmp b/Source/Core/Resources/zoomtube.bmp new file mode 100644 index 0000000000000000000000000000000000000000..55516c7aaa3591604865d4cd18379f8f650c3104 GIT binary patch literal 3382 zcmeIwA#&YN425B3CLjUbgJ57$uqM1+gO+lZkut%6E<(#F6wORPc)-i=r?4!c_Vwp; zZ3Fx2>gm_x54#?Zw`LE_etx;JyM1xL@%<_@PLnpcGPUluKQDjn-|L&12NJySLSxLr z;9wF7q0kt!G&lm>q)=$g*LR%^B!og^%$Qm^0tune7=F^=2qc6;W6U^h9D#&TXpDvN zo8~x1LMSw5m*!3e5<;OdyXkPr%u*^;D_frLmI06Zw(3st}a59h(3XRzqWFR3F8nf*xCj$wg(3oxIax#z*3XRz|Zzlr@q3r$RV$Z*g8j2NO iuBTlt#|(_)(Q$tsX9dm*oE11La8}@~z*&L+tOB3FDm>c& literal 0 HcmV?d00001 diff --git a/Source/Core/Resources/zoomtube.png b/Source/Core/Resources/zoomtube.png new file mode 100644 index 0000000000000000000000000000000000000000..6283111b2e976ffc2e6464f1af9e4339eca440b8 GIT binary patch literal 314 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE3?yBabR7dyjKx9jP7LeL$-D$|SkfJR9T^xl z_H+M9WCijS1AIbUf%G0bS$mH&|LfKNPv`%ClJEZ~23_4XPZL%Cuham_I#(y|15(^2 zL4Lvi;ef%*pYan=n6tnmvY3HEOa+7)XNw*a0Sb0|x;Tbd_`jWSkne~Bhs(wlq5uCk zmp2?VxtlX<@Auf2hmt*;