03-08-2013, 11:53 AM
Creating many BoxVisual3D objects can be slow because for each of the object its own MeshGeometry3D is created.
It is much faster to create one box MeshGeometry3D at position (0, 0, 0) and size (1, 1, 1) and than transform that mesh with ScaleTransorm3D and TranslateTransform3D - or even better combine those two transformations into one MatrixTransform3D.
So you create one MeshGeometry3D and than create many GeometryModel3D objects with it - each GeometryModel3D can have its own material and has its Transform that scales and translates it.
It also helps to Freeze (call Freeze method) on each object that will not be changed.
The following code can be used to test the object creation in such manner (on my machine it takes less than half a second to create 10000 boxes):
XAML:
cs:
Because the GeometryModel3D is frozen, this code can be optimized even more with creating the for loop multithreaded - in this case you would need to create Model3DGroup for each thread and than add GeometryModel3D to them.
It is much faster to create one box MeshGeometry3D at position (0, 0, 0) and size (1, 1, 1) and than transform that mesh with ScaleTransorm3D and TranslateTransform3D - or even better combine those two transformations into one MatrixTransform3D.
So you create one MeshGeometry3D and than create many GeometryModel3D objects with it - each GeometryModel3D can have its own material and has its Transform that scales and translates it.
It also helps to Freeze (call Freeze method) on each object that will not be changed.
The following code can be used to test the object creation in such manner (on my machine it takes less than half a second to create 10000 boxes):
XAML:
Code:
<UserControl x:Class="Ab3d.PowerToys.MyTests.Tests.Performance.BoxesTest"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:ab3d="clr-namespace:Ab3d.Controls;assembly=Ab3d.PowerToys"
xmlns:cameras="clr-namespace:Ab3d.Cameras;assembly=Ab3d.PowerToys"
xmlns:visuals="clr-namespace:Ab3d.Visuals;assembly=Ab3d.PowerToys"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<Grid>
<Viewport3D Name="MainViewport">
</Viewport3D>
<cameras:TargetPositionCamera Name="Camera1"
Heading="30" Attitude="-20" Bank="0"
Distance="5000" ShowCameraLight="Always"/>
<ab3d:MouseCameraController Name="MouseCameraController1" UsedMouseButton="Left" EventsSourceElement="{Binding ElementName=MainGrid}"/>
<StackPanel HorizontalAlignment="Left" VerticalAlignment="Top" Orientation="Horizontal">
<Label>Number of boxes:</Label>
<TextBox Name="NumberTextBox" Width="50" Margin="0 0 10 0" Text="5000"/>
<Label>Size:</Label>
<TextBox Name="SizeTextBox" Width="50" Margin="0 0 10 0" Text="50"/>
<Button Name="CreateButton" Content="Create" Click="CreateButton_OnClick"/>
</StackPanel>
</Grid>
</UserControl>cs:
Code:
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Media.Media3D;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace Ab3d.PowerToys.MyTests.Tests.Performance
{
/// <summary>
/// Interaction logic for BoxesTest.xaml
/// </summary>
public partial class BoxesTest : UserControl
{
// Based on emial form Semih (semih@solon.com.tr) on 7. 3. 2013
private MeshGeometry3D _boxMesh;
private List<Vector3D> _randomPositions;
private static Random _rnd;
public BoxesTest()
{
InitializeComponent();
_randomPositions = new List<Vector3D>(10000);
if (_rnd == null)
_rnd = new Random();
for (int i = 0; i < 10000; i++)
{
_randomPositions.Add(new Vector3D(_rnd.NextDouble() * 1000 - 500,
_rnd.NextDouble() * 1000 - 500,
_rnd.NextDouble() * 1000 - 500));
}
_boxMesh = new Ab3d.Meshes.BoxMesh3D(new Point3D(0, 0, 0), new Size3D(1, 1, 1), 1, 1, 1).Geometry;
_boxMesh.Freeze();
}
private void CreateButton_OnClick(object sender, RoutedEventArgs e)
{
int count = Int32.Parse(NumberTextBox.Text);
double size = double.Parse(SizeTextBox.Text);
var stopwatch = new Stopwatch();
stopwatch.Start();
var group = new Model3DGroup();
var material = new DiffuseMaterial(Brushes.Green);
material.Freeze();
for (int i = 0; i < count; i++)
{
var geometryModel3D = new GeometryModel3D(_boxMesh, material);
geometryModel3D.Transform = new MatrixTransform3D(new Matrix3D(size, 0, 0, 0,
0, size, 0, 0,
0, 0, size, 0,
_randomPositions[i].X, _randomPositions[i].Y, _randomPositions[i].Z, 1));
geometryModel3D.Freeze();
group.Children.Add(geometryModel3D);
}
group.Freeze();
var visual3D = new ModelVisual3D();
visual3D.Content = group;
MainViewport.Children.Add(visual3D);
stopwatch.Stop();
MessageBox.Show(string.Format("Time: {0:#,##0.00} ms", stopwatch.Elapsed.TotalMilliseconds));
}
}
}Because the GeometryModel3D is frozen, this code can be optimized even more with creating the for loop multithreaded - in this case you would need to create Model3DGroup for each thread and than add GeometryModel3D to them.
Andrej Benedik

