From 38ca9005c48290491cd7ecf165cd3f1da6b7ab5d Mon Sep 17 00:00:00 2001 From: Andy McFadden Date: Sun, 1 Mar 2020 18:18:31 -0800 Subject: [PATCH] Progress on wireframe visualization Added some more plumbing. Updated visualization set edit dialog, which now does word-wrapping correctly in the buttons. Added Alt+V as the hotkey for Create/Edit Visualization Set, which allows you to double-tap it to leap into the visualization editor. Experimented with Path drawing, which looks like it could do just what we need. Also, show the file size in KB in the code/data/junk breakdown at the bottom of the window. (Technically it's KiB, but that looked funny.) --- PluginCommon/VisWireframe.cs | 39 ++++++++--- SourceGen/MainController.cs | 2 +- SourceGen/Res/Strings.xaml | 2 +- .../Visualization/VisWireframeTest.cs | 6 ++ .../Visualization/wireframe-test.dis65 | 30 ++++++++- SourceGen/Visualization.cs | 67 ++++++++++++++++++- SourceGen/WpfGui/EditVisualization.xaml | 9 ++- SourceGen/WpfGui/EditVisualization.xaml.cs | 34 +++++++--- SourceGen/WpfGui/EditVisualizationSet.xaml | 17 +++-- SourceGen/WpfGui/EditVisualizationSet.xaml.cs | 6 +- SourceGen/WpfGui/MainWindow.xaml | 8 ++- 11 files changed, 185 insertions(+), 35 deletions(-) diff --git a/PluginCommon/VisWireframe.cs b/PluginCommon/VisWireframe.cs index cebc7b9..d0fde4c 100644 --- a/PluginCommon/VisWireframe.cs +++ b/PluginCommon/VisWireframe.cs @@ -20,9 +20,27 @@ using System.Diagnostics; namespace PluginCommon { /// /// Wireframe mesh with optional backface normals, for use with visualization generators. + /// Call the various functions to add data, then call Validate() to check for broken + /// references. /// [Serializable] public class VisWireframe : IVisualizationWireframe { + public static VisParamDescr Param_IsPerspective(string uiLabel, bool defaultVal) { + return new VisParamDescr(uiLabel, "_isPerspective", typeof(bool), 0, 0, 0, defaultVal); + } + public static VisParamDescr Param_IsBackfaceRemoved(string uiLabel, bool defaultVal) { + return new VisParamDescr(uiLabel, "_isBackfaceRemoved", typeof(bool), 0, 0, 0, defaultVal); + } + public static VisParamDescr Param_EulerX(string uiLabel, int defaultVal) { + return new VisParamDescr(uiLabel, "_eulerRotX", typeof(int), 0, 359, 0, defaultVal); + } + public static VisParamDescr Param_EulerY(string uiLabel, int defaultVal) { + return new VisParamDescr(uiLabel, "_eulerRotY", typeof(int), 0, 359, 0, defaultVal); + } + public static VisParamDescr Param_EulerZ(string uiLabel, int defaultVal) { + return new VisParamDescr(uiLabel, "_eulerRotZ", typeof(int), 0, 359, 0, defaultVal); + } + private List mVerticesX = new List(); private List mVerticesY = new List(); private List mVerticesZ = new List(); @@ -56,14 +74,15 @@ namespace PluginCommon { } /// - /// Adds the edge to the list. + /// Adds an edge to the list. The referenced vertices do not need to be defined + /// before calling. /// /// Index of first vertex. /// Index of second vertex. /// Edge index. Indices start at zero and count up. public int AddEdge(int index0, int index1) { - Debug.Assert(index0 >= 0 && index0 < mVerticesX.Count); - Debug.Assert(index1 >= 0 && index1 < mVerticesX.Count); + Debug.Assert(index0 >= 0); + Debug.Assert(index1 >= 0); mEdges.Add(new IntPair(index0, index1)); return mEdges.Count - 1; } @@ -84,24 +103,26 @@ namespace PluginCommon { } /// - /// Marks a vertex's visibility as being tied to the specified face. The face does - /// not need to be in the list yet. + /// Marks a vertex's visibility as being tied to the specified face. The vertices and + /// faces being referenced do not need to exist yet. /// /// Index of vertex. /// Index of face. public void AddVertexFace(int vertexIndex, int faceIndex) { - Debug.Assert(vertexIndex >= 0 && vertexIndex < mVerticesX.Count); + Debug.Assert(vertexIndex >= 0); + Debug.Assert(faceIndex >= 0); mVertexFaces.Add(new IntPair(vertexIndex, faceIndex)); } /// - /// Marks an edge's visibility as being tied to the specified face. The face does - /// not need to be in the list yet. + /// Marks an edge's visibility as being tied to the specified face. The edges and + /// faces being referenced do not need to exist yet. /// /// Index of edge. /// Index of face. public void AddEdgeFace(int edgeIndex, int faceIndex) { - Debug.Assert(edgeIndex >= 0 && edgeIndex < mEdges.Count); + Debug.Assert(edgeIndex >= 0); + Debug.Assert(faceIndex >= 0); mEdgeFaces.Add(new IntPair(edgeIndex, faceIndex)); } diff --git a/SourceGen/MainController.cs b/SourceGen/MainController.cs index 04c853a..3897a76 100644 --- a/SourceGen/MainController.cs +++ b/SourceGen/MainController.cs @@ -697,7 +697,7 @@ namespace SourceGen { float dataPerc = (mProject.ByteCounts.DataByteCount * 100.0f) / total; float junkPerc = (mProject.ByteCounts.JunkByteCount * 100.0f) / total; mMainWin.ByteCountText = string.Format(Res.Strings.STATUS_BYTE_COUNT_FMT, - codePerc, dataPerc, junkPerc); + total / 1024.0f, codePerc, dataPerc, junkPerc); } #endregion Init and settings diff --git a/SourceGen/Res/Strings.xaml b/SourceGen/Res/Strings.xaml index a4c2d5d..c0d3718 100644 --- a/SourceGen/Res/Strings.xaml +++ b/SourceGen/Res/Strings.xaml @@ -161,7 +161,7 @@ limitations under the License. C64 Screen Code {1} CPU @ {2} MHz Show - {0:F1}% code, {1:F1}% data, {2:F1}% junk + {0:F1}KB: {1:F1}% code, {2:F1}% data, {3:F1}% junk Ready DCI string has mixed data DCI string not terminated diff --git a/SourceGen/SGTestData/Visualization/VisWireframeTest.cs b/SourceGen/SGTestData/Visualization/VisWireframeTest.cs index 12cb916..6baba6d 100644 --- a/SourceGen/SGTestData/Visualization/VisWireframeTest.cs +++ b/SourceGen/SGTestData/Visualization/VisWireframeTest.cs @@ -41,6 +41,12 @@ namespace WireframeTest { new VisParamDescr[] { new VisParamDescr("File offset (hex)", P_OFFSET, typeof(int), 0, 0x00ffffff, VisParamDescr.SpecialMode.Offset, 0), + + // These are interpreted by the main app. + VisWireframe.Param_EulerX("Euler rotation X", 0), + VisWireframe.Param_EulerY("Euler rotation Y", 0), + VisWireframe.Param_EulerZ("Euler rotation Z", 0), + VisWireframe.Param_IsPerspective("Perspective projection", true), }), }; diff --git a/SourceGen/SGTestData/Visualization/wireframe-test.dis65 b/SourceGen/SGTestData/Visualization/wireframe-test.dis65 index 56c868a..bf0eebf 100644 --- a/SourceGen/SGTestData/Visualization/wireframe-test.dis65 +++ b/SourceGen/SGTestData/Visualization/wireframe-test.dis65 @@ -17,7 +17,8 @@ "SmartPlpHandling":true}, "PlatformSymbolFileIdentifiers":[], -"ExtensionScriptFileIdentifiers":["PROJ:VisWireframeTest.cs"], +"ExtensionScriptFileIdentifiers":["PROJ:VisWireframeTest.cs", +"RT:Apple/VisHiRes.cs"], "ProjectSyms":{ }}, @@ -239,7 +240,30 @@ "LvTables":{ }, -"Visualizations":[], +"Visualizations":[{ +"Tag":"wf_data", +"VisGenIdent":"wireframe-test", +"VisGenParams":{ +"offset":10, +"_eulerRotX":0, +"_eulerRotY":0, +"_eulerRotZ":0, +"_isPerspective":true}}, + +{ +"Tag":"bmp_data", +"VisGenIdent":"apple2-hi-res-bitmap", +"VisGenParams":{ +"offset":10, +"byteWidth":3, +"height":11, +"colStride":0, +"rowStride":0, +"isColor":true, +"isFirstOdd":false, +"isHighBitFlipped":false}}], "VisualizationAnimations":[], "VisualizationSets":{ -}} +"10":{ +"Tags":["wf_data", +"bmp_data"]}}} diff --git a/SourceGen/Visualization.cs b/SourceGen/Visualization.cs index c9515d9..32e76be 100644 --- a/SourceGen/Visualization.cs +++ b/SourceGen/Visualization.cs @@ -18,6 +18,7 @@ using System.Collections.Generic; using System.Collections.ObjectModel; using System.Diagnostics; using System.Text; +using System.Windows; using System.Windows.Media; using System.Windows.Media.Imaging; @@ -99,6 +100,7 @@ namespace SourceGen { VisualizationAnimation.GenerateAnimOverlayImage(); internal static readonly BitmapSource BLANK_IMAGE = GenerateBlankImage(); + internal static readonly BitmapSource BLACK_IMAGE = GenerateBlackImage(); /// /// Serial number, for reference from other Visualization objects. Not serialized. @@ -106,9 +108,10 @@ namespace SourceGen { /// /// This value is only valid in the current session. It exists because animations /// need to refer to other Visualization objects, and doing so by Tag gets sticky - /// if a tag gets renamed. We need a way to uniquely identify a reference to a + /// if a Tag gets renamed. We need a way to uniquely identify a reference to a /// Visualization that persists across Tag renames and other edits. When the objects - /// are serialized to the project file we just output the tags. + /// are serialized to the project file we don't include the serial, and just reference + /// by Tag. /// public int SerialNumber { get; private set; } @@ -191,7 +194,8 @@ namespace SourceGen { } /// - /// Converts an IVisualization2d to a BitmapSource for display. + /// Converts an IVisualization2d to a BitmapSource for display. The bitmap will be + /// the same size as the original content. /// public static BitmapSource ConvertToBitmapSource(IVisualization2d vis2d) { // Create indexed color palette. @@ -218,12 +222,69 @@ namespace SourceGen { return image; } + /// + /// Generates a BitmapSource from IVisualizationWireframe data. Useful for thumbnails + /// and GIF exports. + /// + /// Visualization data. + /// Parameter set, for rotations and render options. + /// Output bitmap width. + /// Output bitmap height. + /// + public static BitmapSource GenerateWireframeImage(IVisualizationWireframe visWire, + ReadOnlyDictionary parms, double width, double height) { + // TODO(xyzzy): need to get path into a bitmap for thumbnails / GIFs...; call + // GenerateWireframePath and then render the path + // https://stackoverflow.com/a/23582564/294248 + Debug.WriteLine("Render " + visWire + " at " + width + "x" + height); + return null; + } + + /// + /// Generates a WPF path from IVisualizationWireframe data. + /// + public static GeometryGroup GenerateWireframePath(IVisualizationWireframe visWire, + ReadOnlyDictionary parms, double scale) { + GeometryGroup geo = new GeometryGroup(); + // This establishes the geometry bounds. It's a zero-length line segment, so + // nothing is actually drawn. + Debug.WriteLine("using scale=" + scale); + Point corner = new Point(scale + 1, scale + 1); + geo.Children.Add(new LineGeometry(corner, corner)); + + // TODO(xyzzy): render + geo.Children.Add(new LineGeometry(new Point(6, 6), new Point(197, 197))); + geo.Children.Add(new LineGeometry(new Point(6, 197), new Point(197, 6))); + return geo; + } + + /// + /// Returns a bitmap with a single transparent pixel. + /// private static BitmapSource GenerateBlankImage() { RenderTargetBitmap bmp = new RenderTargetBitmap(1, 1, 96.0, 96.0, PixelFormats.Pbgra32); return bmp; } + /// + /// Returns a bitmap with a single black pixel. + /// + private static BitmapSource GenerateBlackImage() { + BitmapPalette palette = new BitmapPalette(new List { Colors.Black }); + BitmapSource image = BitmapSource.Create( + 1, + 1, + 96.0, + 96.0, + PixelFormats.Indexed8, + palette, + new byte[] { 0 }, + 1); + + return image; + } + public override string ToString() { return "[Vis: " + Tag + " (" + VisGenIdent + ") count=" + VisGenParams.Count + "]"; diff --git a/SourceGen/WpfGui/EditVisualization.xaml b/SourceGen/WpfGui/EditVisualization.xaml index d7ecef6..3de5ab2 100644 --- a/SourceGen/WpfGui/EditVisualization.xaml +++ b/SourceGen/WpfGui/EditVisualization.xaml @@ -134,8 +134,13 @@ limitations under the License. - + + + + + + diff --git a/SourceGen/WpfGui/EditVisualization.xaml.cs b/SourceGen/WpfGui/EditVisualization.xaml.cs index 4b4ee60..370ae27 100644 --- a/SourceGen/WpfGui/EditVisualization.xaml.cs +++ b/SourceGen/WpfGui/EditVisualization.xaml.cs @@ -302,8 +302,8 @@ namespace SourceGen.WpfGui { ClearValue(SizeToContentProperty); SetValue(MinWidthProperty, this.Width); SetValue(MinHeightProperty, this.Height); - previewImage.ClearValue(WidthProperty); - previewImage.ClearValue(HeightProperty); + previewGrid.ClearValue(WidthProperty); + previewGrid.ClearValue(HeightProperty); tagTextBox.SelectAll(); tagTextBox.Focus(); @@ -450,13 +450,25 @@ namespace SourceGen.WpfGui { // Invoke the plugin. PluginErrMessage = string.Empty; - IVisualization2d vis2d; + IVisualization2d vis2d = null; + IVisualizationWireframe visWire = null; + ReadOnlyDictionary parms = CreateVisGenParams(); try { IPlugin_Visualizer plugin = (IPlugin_Visualizer)mProject.GetPlugin(item.ScriptIdent); - vis2d = plugin.Generate2d(item.VisDescriptor, CreateVisGenParams()); - if (vis2d == null) { - Debug.WriteLine("Vis generator returned null"); + if (item.VisDescriptor.VisualizationType == VisDescr.VisType.Bitmap) { + vis2d = plugin.Generate2d(item.VisDescriptor, parms); + if (vis2d == null) { + Debug.WriteLine("Vis2d generator returned null"); + } + } else if (item.VisDescriptor.VisualizationType == VisDescr.VisType.Wireframe) { + IPlugin_Visualizer_v2 plugin2 = (IPlugin_Visualizer_v2)plugin; + visWire = plugin2.GenerateWireframe(item.VisDescriptor, parms); + if (visWire == null) { + Debug.WriteLine("VisWire generator returned null"); + } + } else { + Debug.Assert(false); } } catch (Exception ex) { Debug.WriteLine("Vis generation failed: " + ex); @@ -465,7 +477,7 @@ namespace SourceGen.WpfGui { LastPluginMessage = ex.Message; } } - if (vis2d == null) { + if (vis2d == null && visWire == null) { previewImage.Source = sBadParamsImage; if (!string.IsNullOrEmpty(LastPluginMessage)) { // Report the last message we got as an error. @@ -474,10 +486,16 @@ namespace SourceGen.WpfGui { PluginErrMessage = (string)FindResource("str_VisGenFailed"); } IsValid = false; - } else { + } else if (vis2d != null) { previewImage.Source = Visualization.ConvertToBitmapSource(vis2d); + wireframePath.Data = new GeometryGroup(); BitmapDimensions = string.Format("{0}x{1}", previewImage.Source.Width, previewImage.Source.Height); + } else { + previewImage.Source = Visualization.BLACK_IMAGE; + wireframePath.Data = Visualization.GenerateWireframePath(visWire, parms, + previewImage.ActualWidth / 2); + BitmapDimensions = "n/a"; } } diff --git a/SourceGen/WpfGui/EditVisualizationSet.xaml b/SourceGen/WpfGui/EditVisualizationSet.xaml index 64f1bc8..bc2322e 100644 --- a/SourceGen/WpfGui/EditVisualizationSet.xaml +++ b/SourceGen/WpfGui/EditVisualizationSet.xaml @@ -85,15 +85,22 @@ limitations under the License. - + - + +