2016-06-06 5 views
7

Ich habe Probleme, meinen Kopf um das Farb-/Materialsystem von C# -WPF-Projekten zu bekommen, derzeit aktualisiere ich die Farbe eines ganzen Punktsystems auf jedem Update des Modells, wenn ich stattdessen nur die Farbe eines einzelnen Punktes aktualisieren möchte (wie es hinzugefügt wird).Aktualisiere Farbe eines einzelnen Punktes von GeometryModel3D Material statt eines ganzen Punktsystems

AggregateSystem Klasse

public class AggregateSystem { 
    // stack to store each particle in aggregate 
    private readonly Stack<AggregateParticle> particle_stack; 
    private readonly GeometryModel3D particle_model; 
    // positions, indices and texture co-ordinates for particles 
    private readonly Point3DCollection particle_positions; 
    private readonly Int32Collection triangle_indices; 
    private readonly PointCollection text_coords; 
    // brush to apply to particle_model.Material 
    private RadialGradientBrush rad_brush; 
    // ellipse for rendering 
    private Ellipse ellipse; 
    private RenderTargetBitmap render_bitmap; 

    public AggregateSystem() { 
     particle_stack = new Stack<AggregateParticle>(); 
     particle_model = new GeometryModel3D { Geometry = new MeshGeometry3D() }; 
     ellipse = new Ellipse { 
      Width = 32.0, 
      Height = 32.0 
     }; 
     rad_brush = new RadialGradientBrush(); 
     // fill ellipse interior using rad_brush 
     ellipse.Fill = rad_brush; 
     ellipse.Measure(new Size(32,32)); 
     ellipse.Arrange(new Rect(0,0,32,32)); 
     render_bitmap = new RenderTargetBitmap(32,32,96,96,PixelFormats.Pbgra32)); 
     ImageBrush img_brush = new ImageBrush(render_bitmap); 
     DiffuseMaterial diff_mat = new DiffuseMaterial(img_brush); 
     particle_model.Material = diff_mat; 
     particle_positions = new Point3DCollection(); 
     triangle_indices = new Int32Collection(); 
     tex_coords = new PointCollection(); 
    } 

    public Model3D AggregateModel => particle_model; 

    public void Update() { 
     // get the most recently added particle 
     AggregateParticle p = particle_stack.Peek(); 
     // compute position index for triangle index generation 
     int position_index = particle_stack.Count * 4; 
     // create points associated with particle for circle generation 
     Point3D p1 = new Point3D(p.position.X, p.position.Y, p.position.Z); 
     Point3D p2 = new Point3D(p.position.X, p.position.Y + p.size, p.position.Z); 
     Point3D p3 = new Point3D(p.position.X + p.size, p.position.Y + p.size, p.position.Z); 
     Point3D p4 = new Point3D(p.position.X + p.size, p.position.Y, p.position.Z); 
     // add points to particle positions collection 
     particle_positions.Add(p1); 
     particle_positions.Add(p2); 
     particle_positions.Add(p3); 
     particle_positions.Add(p4); 
     // create points for texture co-ords 
     Point t1 = new Point(0.0, 0.0); 
     Point t2 = new Point(0.0, 1.0); 
     Point t3 = new Point(1.0, 1.0); 
     Point t4 = new Point(1.0, 0.0); 
     // add texture co-ords points to texcoords collection 
     tex_coords.Add(t1); 
     tex_coords.Add(t2); 
     tex_coords.Add(t3); 
     tex_coords.Add(t4); 
     // add position indices to indices collection 
     triangle_indices.Add(position_index); 
     triangle_indices.Add(position_index + 2); 
     triangle_indices.Add(position_index + 1); 
     triangle_indices.Add(position_index); 
     triangle_indices.Add(position_index + 3); 
     triangle_indices.Add(position_index + 2); 
     // update colour of points - **NOTE: UPDATES ENTIRE POINT SYSTEM** 
     // -> want to just apply colour to single particles added 
     rad_brush.GradientStops.Add(new GradientStop(p.colour, 0.0)); 
     render_bitmap.Render(ellipse); 
     // set particle_model Geometry model properties 
     ((MeshGeometry3D)particle_model.Geometry).Positions = particle_positions; 
     ((MeshGeometry3D)particle_model.Geometry).TriangleIndices = triangle_indices; 
     ((MeshGeometry3D)particle_model.Geometry).TextureCoordinates = tex_coords; 
    } 

    public void SpawnParticle(Point3D _pos, Color _col, double _size) { 
     AggregateParticle agg_particle = new AggregateParticle { 
      position = _pos, colour = _col, size = _size; 
     } 
     // push most-recently-added particle to stack 
     particle_stack.Push(agg_particle); 
    } 

} 

wo AggregateParticle ein POD-Klasse ist, bestehend aus Point3D position, Color color und double size Felder, die selbsterklärend sind.

Gibt es eine einfache und effiziente Methode, um die Farbe des einzelnen Partikels zu aktualisieren, wie es in der Update Methode und nicht das gesamte System der Partikel hinzugefügt wird? Oder muss ich eine List (oder eine ähnliche Datenstruktur) von DiffuseMaterial Instanzen für jedes Partikel im System erstellen und Pinsel für die jeweils erforderliche Farbe auftragen?

[Letzteres ist etwas, was ich um jeden Preis vermeiden möchte, teilweise aufgrund der Tatsache, dass es große strukturelle Änderungen an meinem Code erfordern würde, und ich bin mir sicher, dass es einen besseren Weg gibt, das zu erreichen - also dort MUSS eine einfache Möglichkeit sein, Farbe auf eine Reihe von Texturkoordinaten anzuwenden, sicher ?!.]

Weitere Einzelheiten

  • AggregateModel ist eine einzelne Instanz Model3D Bereich particle_model entspricht, die zu einer Model3DGroup des MainWindow hinzugefügt wird.

  • soll ich beachten Sie, dass, was ich speziell zu erreichen versuchen, hier ein „Gefälle“ von Farben für jedes Teilchen in einer Gesamtstruktur ist, wo ein Teilchen mit einem Color in einem „Temperatur-Gradient“ hat (an anderer Stelle berechnet in das Programm), das von der Reihenfolge abhängig ist, in der es erzeugt wurde - dh Partikel haben eine kältere Farbe, wenn sie früher erzeugt wurde, und eine wärmere Farbe, wenn sie später erzeugt wird. Diese Farbliste wird vorberechnet und an jedes Partikel in der Update-Methode übergeben, wie oben zu sehen ist.

  • Eine Lösung Ich versuchte, eine separate AggregateComponent Instanz für jedes Partikel zu erstellen, wo jedes dieser Objekte Model3D und damit eine entsprechende Bürste zugeordnet ist. Dann wurde eine AggregateComponentManager Klasse erstellt, die die List von jedem AggregateComponent enthielt. Diese Lösung funktioniert, aber es ist schrecklich langsam, da jede Komponente jedes Mal aktualisiert werden muss, wenn ein Partikel hinzugefügt wird, so Speicherverbrauch explodiert - gibt es eine Möglichkeit, dies anzupassen, wo ich bereits gerenderten AggregateComponent s cache ohne ihre Update Methode jeweils aufrufen müssen Zeit ein Partikel hinzugefügt wird?

Der vollständige Quellcode (C# -Code in dem DLAProject Verzeichnis) auf GitHub zu finden: https://github.com/SJR276/DLAProject

+0

@EvilTak Aber wie kann dann dieses Array von 'Material' auf die einzelne' particle_model' Instanz von 'AggregateSystem' angewendet werden? Soweit ich weiß, benötigt ein 'GeometryModel3D' nur eine einzige' Material'-Instanz. – ArchbishopOfBanterbury

+0

Entschuldigung, das war ein Missverständnis meinerseits. Lesen Sie weiter: Ich sehe nicht, was Sie hier versuchen. Implementieren Sie ein Partikelsystem? Wenn dem so ist, würde ich empfehlen, separate Modelle (technisch nur ein Dreieck oder Quad) für jedes "Partikel" zu verwenden und das Rendering für jedes Partikel separat zu behandeln. – EvilTak

+0

@EvilTak Ja, es ist im Wesentlichen ein Partikelsystem (mit speziellen Eigenschaften), das implementiert wird. Ich sehe, ich werde einen Blick auf diese Art von Lösung werfen müssen. Ich habe einen Link zum vollständigen Quellcode auf github hinzugefügt, wenn Sie den Rest der Projektstruktur sehen möchten, es gibt zu viel Code, um hier zu posten. – ArchbishopOfBanterbury

Antwort

7

Wir schaffen WPF 3D-Modelle für kleinere Punktwolken (+/- 100 k Punkte), wobei jeder Punkt als ein Oktaeder (8 Dreiecke) zu einem MeshGeometry3D hinzugefügt.

Um in einer solchen Punktwolke verschiedene Farben für verschiedene Punkte zuzulassen (wir verwenden dies zur Auswahl eines oder einer Teilmenge von Punkten), ordnen wir Texturkoordinaten aus einer kleinen Bitmap zu.

Auf einem hohen Niveau haben wir einige Code wie folgt aus:

BitmapSource bm = GetColorsBitmap(new List<Color> { BaseColor, SelectedColor }); 
ImageBrush ib = new ImageBrush(bm) 
{ 
    ViewportUnits = BrushMappingMode.Absolute, 
    Viewport = new Rect(0, 0, 1, 1) // Matches the pixels in the bitmap. 
}; 
GeometryModel3D model = new GeometryModel3D { Material = new DiffuseMaterial(ib) }; 

und nun die Texturkoordinaten sind nur

new Point(0, 0); 
new Point(1, 0); 

... etc.

Die Farben Bitmap stammt aus :

// Creates a bitmap that has a single row containing single pixels with the given colors. 
// At most 256 colors. 
public static BitmapSource GetColorsBitmap(IList<Color> colors) 
{ 
    if (colors == null) throw new ArgumentNullException("colors"); 
    if (colors.Count > 256) throw new ArgumentOutOfRangeException("colors", "More than 256 colors"); 

    int size = colors.Count; 
    for (int j = colors.Count; j < 256; j++) 
    { 
     colors.Add(Colors.White); 
    } 

    var palette = new BitmapPalette(colors); 
    byte[] pixels = new byte[size]; 
    for (int i = 0; i < size; i++) 
    { 
     pixels[i] = (byte)i; 
    } 

    var bm = BitmapSource.Create(size, 1, 96, 96, PixelFormats.Indexed8, palette, pixels, 1 * size); 
    bm.Freeze(); 
    return bm; 
} 

Wir bemühen uns auch, beim Aktualisieren der Punktewolke die interne Geometriestruktur zwischenzuspeichern und wiederzuverwenden.

Endlich zeigen wir dies mit dem tollen Helix Toolkit.

+0

Ausgezeichnet, ich brauche etwas Zeit, um all das zu verdauen, aber es sieht nützlich aus – ArchbishopOfBanterbury

+0

Ein paar Fragen, was 'BaseColor' und' SelectedColor' hier darstellen und wie würde ich Farben auf dieses 'List' Argument während des'Updates' anwenden Methode, um die Farbe jedes hinzugefügten Partikels zu ziehen und diese 'Farbe' auf die jeweilige Position in der Bitmap anzuwenden? Gibt es auch einen Grund, Helix Toolkit über Standard WPF Media3D zu verwenden? – ArchbishopOfBanterbury

+0

Ich denke, Sie würden die Bitmap mit der ganzen Liste von Farben erstellen, die Sie vorher verwenden möchten. BaseColor und SelectedColor sind feste Farben, die der gesamten PointCloud zugeordnet sind. Wir verwenden nur zwei, aber es würde mit bis zu 256 funktionieren. Dann sind die Texturkoordinaten Ihrer Geometrie nur Punkte in der Bitmap, die der Farbe entsprechen, die Sie den Scheitelpunkten geben möchten. Wir verwenden nur das Helix Toolkit Ansichtsfenster zum Verschieben, Zoomen usw. – Govert

Verwandte Themen