2016-06-06 2 views
7

У меня возникли проблемы с моей головой вокруг системы цветов и материалов проектов C# WPF, в настоящее время я обновляю цвет всей системы точек на каждом обновление модели, когда я хотел бы просто обновить цвет одной точки (как она добавлена).Обновить цвет единственной точки GeometryModel3D Материал, а не целая система точек

AggregateSystem Класс

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); 
    } 

} 

, где AggregateParticle класс СТРУЧОК, состоящий из Point3D position, Color color и double size полей, которые самоочевидны.

Есть ли простой и эффективный способ обновления цвета одиночной частицы, поскольку она добавлена ​​в метод Update, а не всей системы частиц? Или мне нужно создать List (или подобную структуру данных) экземпляров DiffuseMaterial для каждой частицы в системе и применить кисти для нужного цвета для каждого?

[Последнее - это то, чего я хочу избежать любой ценой, отчасти из-за того, что это потребует больших структурных изменений для моего кода, и я уверен, что есть лучший способ приблизиться к этому, то есть там ДОЛЖЕН быть каким-то простым способом применить цвет к набору текстурных координат, конечно ?!.]

Дальнейшие подробности

  • AggregateModel является одной Model3D экземпляр, соответствующий полю particle_model, который добавляется к Model3DGroup из MainWindow.

  • Я должен отметить, что то, чего я пытаюсь достичь, в частности, является «градиентом» цветов для каждой частицы в агрегатной структуре, где частица имеет Color в «температурном градиенте» (рассчитанном в другом месте в программа), которая зависит от порядка, в котором она была сгенерирована, т.е. частицы имеют более холодный цвет, если они сгенерированы ранее, и более теплый цвет, если он создан позже. Этот список цветов предварительно вычисляется и передается каждой частице в методе Update, как это видно выше.

  • В одном решении я попытался создать отдельный экземпляр AggregateComponent для каждой частицы, где каждый из этих объектов имеет ассоциированный Model3D и, следовательно, соответствующую кисть. Затем был создан класс AggregateComponentManager, который содержал List каждого AggregateComponent. Это решение работает, однако это ужасно медленно, так как каждый компонент должен обновляться каждый раз, когда добавляется частица, так что потребление памяти взрывается - есть ли способ адаптировать это, когда я могу кэшировать уже сделанные AggregateComponent с без необходимости их метода Update время добавления частицы?

Полный исходный код (C# код в каталоге DLAProject) можно найти на GitHub: https://github.com/SJR276/DLAProject

+0

@EvilTak Но тогда как этот массив 'Material' может быть применен к одному экземпляру' particle_model' 'AggregateSystem'? Насколько мне известно, «GeometryModel3D» принимает только один экземпляр «Material». – ArchbishopOfBanterbury

+0

Извините, это было недоразумение с моей стороны. Читайте дальше: Я не понимаю, что вы пытаетесь сделать здесь. Вы внедряете систему частиц? Если это так, я бы рекомендовал использовать отдельные модели (технически только треугольник или квадрат) для каждого «Particle» и обработки рендеринга для каждой частицы отдельно. – EvilTak

+0

@EvilTak Да, это, по сути, система частиц (со специальными свойствами), которая реализуется. Понимаю, мне придется взглянуть на этот тип решения. Я добавил ссылку на полный исходный код github, если вы хотите увидеть остальную часть структуры проекта, здесь слишком много кода для публикации. – ArchbishopOfBanterbury

ответ

7

Мы создаем WPF 3D модели для низковат облака точек (+/- 100 к точек), где каждая точка добавлен в качестве октаэдра (8 треугольников) в MeshGeometry3D.

Чтобы обладать разными цветами для разных точек (мы используем это для выбора одного или подмножества точек) в таком облаке точек, мы назначаем координаты текстуры из небольшого растрового изображения.

На высоком уровне у нас есть некоторый код, как это:

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) }; 

и теперь координаты текстуры просто

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

... и т.д.

Цвета Bitmap происходит от :

// 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; 
} 

Мы также прилагаем некоторые усилия для кэширования и повторного использования внутренней структуры геометрии при обновлении облака точек.

И наконец, мы показываем это с удивительным Helix Toolkit.

+0

Отлично, мне нужно некоторое время, чтобы переварить все это, но оно выглядит полезным – ArchbishopOfBanterbury

+0

Несколько вопросов, что здесь обозначают 'BaseColor' и' SelectedColor', и как бы применить цвета к этому аргументу 'List' во время' Update' чтобы потянуть цвет каждой добавленной частицы и применить этот «цвет» к соответствующему положению в растровом изображении? Кроме того, есть ли причина использования Helix Toolkit над стандартным WPF Media3D? – ArchbishopOfBanterbury

+0

Я думаю, вы бы создали битмап со всем списком цветов, который хотите использовать заранее. BaseColor и SelectedColor - это фиксированные цвета, связанные со всем PointCloud. Мы просто используем два, но это будет работать до 256. Затем текстуркоординаты вашей геометрии - это всего лишь точки в битовой карте, соответствующие цвету, который вы хотите дать вершинам. Мы просто используем видовое окно Helix Toolkit для перемещения, масштабирования и т. Д. – Govert