// UpdateViewportCanvas will render the 3D scene with 2D polygons with transforming all 3D points to 2D coordinates // It will render all children of ObjectsRootVisual3D (to render all children of Viewport3D, use MainViewport.Children) // The method is static so it can be easily copied to some other location for some other testing private static void UpdateViewportCanvas(Visual3DCollection visuals, BaseCamera camera, Canvas viewportCanvas, bool fillPolygon) { // Get camera's view and projection matrixes Matrix3D viewMatrix, projectionMatrix; camera.GetCameraMatrixes(out viewMatrix, out projectionMatrix); // Calculate combined viewProjection matrix Matrix3D viewProjectionMatrix3D = viewMatrix * projectionMatrix; // Read target canvas size and its center point (half the size) double viewportCanvasWidth = viewportCanvas.ActualWidth; double viewportCanvasHeight = viewportCanvas.ActualHeight; double viewportCanvasCenterX = viewportCanvasWidth * 0.5; double viewportCanvasCenterY = viewportCanvasHeight * 0.5; // Clear all existing Polygons viewportCanvas.Children.Clear(); // IterateGeometryModel3DObjects will traverse the Visual3D and Model3DGroup objects // and will call the callback delegate for each GeometryModel3D. // The callback delegate also receives the transformation that were applied to the parents of the GeometryModel3D. // If you want to be called for each ModelVisual3D, use the IterateModelVisualsObjects method Ab3d.Utilities.ModelIterator.IterateGeometryModel3DObjects(visuals : visuals, parentTransform3D : null, // optionally we can specify the initial transformation callback : delegate(GeometryModel3D geometryModel3D, Transform3D parentTransform3D) { // This is called for each GeometryModel3D in the hierarchy var meshGeometry3D = geometryModel3D.Geometry as MeshGeometry3D; if (meshGeometry3D == null) { return; } var positions = meshGeometry3D.Positions; var triangleIndices = meshGeometry3D.TriangleIndices; if (positions == null || positions.Count == 0 || triangleIndices == null || triangleIndices.Count == 0) { return; } // Get fill brush from material var materialBrush = GetMaterialBrush(geometryModel3D.Material); // If this geometryModel3D has any transformation, combine it with the parentTransform3D - this will be the world transformation for this geometryModel3D var worldTransform = CombineTransform(parentTransform3D, geometryModel3D.Transform); Matrix3D worldViewProjectionMatrix3D; // Get final worldViewProjectionMatrix3D if (worldTransform == null || worldTransform.Value.IsIdentity) { worldViewProjectionMatrix3D = viewProjectionMatrix3D; } else { worldViewProjectionMatrix3D = worldTransform.Value * viewProjectionMatrix3D; } var triangleIndicesCount = triangleIndices.Count; // Go through all triangles for (int i = 0; i < triangleIndicesCount; i += 3) { // Read positions for one triangle Point3D p1 = positions[triangleIndices[i]]; Point3D p2 = positions[triangleIndices[i + 1]]; Point3D p3 = positions[triangleIndices[i + 2]]; // Transform to homogeneous coordinates (to get final x and y we need to divide them by w) Point4D h1 = worldViewProjectionMatrix3D.Transform(new Point4D(p1.X, p1.Y, p1.Z, 1)); Point4D h2 = worldViewProjectionMatrix3D.Transform(new Point4D(p2.X, p2.Y, p2.Z, 1)); Point4D h3 = worldViewProjectionMatrix3D.Transform(new Point4D(p3.X, p3.Y, p3.Z, 1)); // Do a simple clip test - if any position is behind the camera we will not render this triangle if (h1.Z < 0 || h2.Z < 0 || h3.Z < 0) { return; } // Convert to screen coordinates by dividing by w and then adjust to the size of canvas (we also invert y) Point canvas1 = new Point((h1.X * viewportCanvasWidth) / (2 * h1.W) + viewportCanvasCenterX, viewportCanvasHeight - ((h1.Y * viewportCanvasHeight) / (2 * h1.W) + viewportCanvasCenterY)); // invert y Point canvas2 = new Point((h2.X * viewportCanvasWidth) / (2 * h2.W) + viewportCanvasCenterX, viewportCanvasHeight - ((h2.Y * viewportCanvasHeight) / (2 * h2.W) + viewportCanvasCenterY)); Point canvas3 = new Point((h3.X * viewportCanvasWidth) / (2 * h3.W) + viewportCanvasCenterX, viewportCanvasHeight - ((h3.Y * viewportCanvasHeight) / (2 * h3.W) + viewportCanvasCenterY)); // Create triangle // TODO: This creates many Polyline objects on each change of camera - to improve performance it would be possible to reuse the existing Polylines and just change the position's coordinates var points = new PointCollection(3); points.Add(canvas1); points.Add(canvas2); points.Add(canvas3); var polyline = new Polygon() { Points = points, StrokeThickness = 1, StrokeMiterLimit = 1 }; if (fillPolygon) { polyline.Fill = materialBrush; polyline.Stroke = Brushes.Black; } else { polyline.Fill = null; polyline.Stroke = materialBrush; } viewportCanvas.Children.Add(polyline); } }); }