We hebben bijna alle elementen op hun plaats staan voor onze Box class. Het enige wat we nu nog moeten doen is het daadwerkelijke tekenen. Hier is de code
private void DrawIt()
{
// Maak een kopie
var positions = _mesh.Positions;
var indices = _mesh.TriangleIndices;
var normals = _mesh.Normals;
var textures = _mesh.TextureCoordinates;
_mesh.Positions = null;
_mesh.TriangleIndices = null;
_mesh.Normals = null;
_mesh.TextureCoordinates = null;
positions.Clear();
indices.Clear();
normals.Clear();
textures.Clear();
// Definieer de punten
Point3D pointA = LeftBottomCorner;
Point3D pointB = new Point3D(LeftBottomCorner.X + Width, LeftBottomCorner.Y, LeftBottomCorner.Z);
Point3D pointC = new Point3D(LeftBottomCorner.X + Width, LeftBottomCorner.Y, LeftBottomCorner.Z - Depth);
Point3D pointD = new Point3D(LeftBottomCorner.X, LeftBottomCorner.Y, LeftBottomCorner.Z - Depth);
Point3D pointE = new Point3D(LeftBottomCorner.X, LeftBottomCorner.Y + Height, LeftBottomCorner.Z);
Point3D pointF = new Point3D(LeftBottomCorner.X + Width, LeftBottomCorner.Y + Height, LeftBottomCorner.Z);
Point3D pointG = new Point3D(LeftBottomCorner.X + Width, LeftBottomCorner.Y + Height, LeftBottomCorner.Z - Depth);
Point3D pointH = new Point3D(LeftBottomCorner.X, LeftBottomCorner.Y + Height, LeftBottomCorner.Z - Depth);
// Voeg de punten toe
// Grond vlak
positions.Add(pointD);
positions.Add(pointC);
positions.Add(pointB);
positions.Add(pointA);
// Voor vlak
positions.Add(pointA);
positions.Add(pointB);
positions.Add(pointF);
positions.Add(pointE);
// Rechtervlak
positions.Add(pointB);
positions.Add(pointC);
positions.Add(pointG);
positions.Add(pointF);
// Achter vlak
positions.Add(pointC);
positions.Add(pointD);
positions.Add(pointH);
positions.Add(pointG);
// Linker vlak
positions.Add(pointD);
positions.Add(pointA);
positions.Add(pointE);
positions.Add(pointH);
// Boven vlak
positions.Add(pointE);
positions.Add(pointF);
positions.Add(pointG);
positions.Add(pointH);
// Voeg de TriangleIndices toe.
for (int i = 0; i
{
indices.Add((i * 4) + 0);
indices.Add((i * 4) + 1);
indices.Add((i * 4) + 2);
indices.Add((i * 4) + 0);
indices.Add((i * 4) + 2);
indices.Add((i * 4) + 3);
}
// Zet de collections terug in de mesh
_mesh.TextureCoordinates = textures;
_mesh.Normals = normals;
_mesh.TriangleIndices = indices;
_mesh.Positions = positions;
}
Het lijkt veel maar echt ingewikkeld is het niet. Er is echter een klein dingetje dat wel erg belangrijk is: de properties Positions, TriangleIndices, Normals en TextureCoordinates (die laaste is nieuw, daar kom ik op terug als we het over materialen gaan hebben) zijn DependencyProperties. Als we iets wijzigigen in deze collections, zal er een PropertyChanged even afgevuurd worden in onze _mesh member (van het type MeshGeometry3D) en wordt de boel opnieuw getekend. Voor ieder punt dat we toevoegen zal hij opnieuw berekend worden. Ook voor iedere TriangleIndex die we toevoegen gaan WPF opnieuw rekenen. Hetzelfde geldt voor de Normals en TextureCoordinates collections. Aangezien we een hoop punten en zo toevoegen zal er een hoop nodeloze berekeningen plaats gaan vinden!
Om dat te voorkomen maken we eerst een kopie van de collections. Vervolgens zetten we de properties van de _mesh op null. Als we nu iets doen met de kopie van de collections heeft dat geen enkele invloed op de _mesh. Aan het einde van de method plaatsen we de gewijzigde collecties weer terug in de _mesh waarna WPF alles opnieuw gaat berekenen. Dit scheelt enorm veel tijd!
Nu we een kopie hebben van de collecties, wissen we de inhoud. Immers: we gaan alles opnieuw maken!
Ik definieer eerst de 8 hoekpunten. Dat hoef ik maar een maal te doen, we hergebruiken de gemaakte punten later bij het toevoegen aan de positions collectie. Je ziet dat de punten gelijk op de goede plaats staan: ik tel de linkeronder hoek er bij op en zorg ervoor dat de Width, Height en Depth gelijk meegenomen worden.
Nu definieren we de zes vlakken. Let er op dat je het wel in de goede volgorde doet, alle punten worden tegen de klok in gedefinieerd.
Als we alle punten toegevoegd hebben, wordt het tijd om de TriangleIndices berekent. Dit is hetzelfde als wat je gezien hebt in mijn vorige post, nu alleen voor alle vlakken. Als dat gebeurdt is, plaatsen we de collecties terug en we zijn klaar!
Nou ja.. we moeten nog een aanroep naar DrawIt() plaatsen in de constructor (aan het einde) en belangrijker: in de PropertyChanged:
///
/// Initializes a new instance of the class.
///
public Box()
{
_model = new GeometryModel3D();
_mesh = new MeshGeometry3D();
_model.Geometry = _mesh;
this.Content = _model;
DrawIt();
}
en...
///
/// Handles the Property Changed event (static)
///
/// The
/// instance containing the event data.
private void PropertyChanged(DependencyPropertyChangedEventArgs args)
{
// opnieuw tekenen!
DrawIt();
}
Om dit nu zichtbaar te maken in onze XAML file, moeten we een referentie naar deze code in de header van de file maken.
De XAML ziet er nu als volgt uit:
<Window x:Class="WpfApplication11.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:detrio="clr-namespace:WpfApplication11"
Title="Window1" Height="300" Width="300">
<Viewport3D>
<detrio:Box LeftBottomCorner="0 0 0" Width="1" Height="1" Depth="1">
<detrio:Box.Material>
<DiffuseMaterial Brush="Blue"/>
</detrio:Box.Material>
</detrio:Box>
<!-- Lampjes! -->
<ModelVisual3D>
<ModelVisual3D.Content>
<Model3DGroup>
<AmbientLight Color="#404040"/>
<DirectionalLight Color="#BFBFBF" Direction="1 -1 -1"/>
</Model3DGroup>
</ModelVisual3D.Content>
</ModelVisual3D>
<!-- Camera -->
<Viewport3D.Camera>
<PerspectiveCamera Position="2.1 2 2"
LookDirection="-0.5 -0.7 -1"
UpDirection="0 1 0"
FieldOfView="60.0"
/>
</Viewport3D.Camera>
</Viewport3D>
</Window>
En meer hebben we niet nodig!
Ik weet niet hoe het met jou zit, maar ik ben dat blauwe blokje een beetje zat. Laten we de volgende keer eens kijken of we die niet mooier kunnen maken, laten we het eens over Materials gaan hebben!